Knativeをminikubeを利用してローカルマシン1台で動かすまで

# 概要

Google Cloud Next '18において、[「Knative」](https://cloud.google.com/knative/)が発表されました。
Knativeは、コンテナのオーケストレーションツールである[Kubernetes](https://kubernetes.io)上でアプリを動かす際の手順(ビルド、デプロイ、サービスの管理)を簡略化してくれるツールとのことです。

このエントリでは、ローカルマシン(mac)1台で、Kubernetes環境構築用のツール[「Minikube」](https://github.com/kubernetes/minikube)を利用してKubernetes環境を作り、その上でKnativeを動かすまでについての手順を紹介します。

<img width="512" alt="minikube_dashboard.png" src="https://qiita-image-store.s3.amazonaws.com/0/65002/5e8bb6d9-13bd-040b-b644-5aa032da4d64.png">
(図：「$ minikube dashboard」を実行して、デプロイ後のサービスを確認したものです)

## 対象読者

- Knativeの動かし方を確認したい方
- Kubernetesが何をしてくれるものかの知識がある方

## 前提

このエントリでは、2018/7/25時点で利用可能な以下のバージョンを利用しています。

- [Minikube](https://github.com/kubernetes/minikube) : 0.28.2
- [Knative](https://cloud.google.com/knative/) : (2018/7/25時点の最新版)
- [macOS](https://www.apple.com/jp/macos/high-sierra/) : 10.13.2
- [VirtualBox](https://www.virtualbox.org) : 5.2.16
- Macbook Pro(15-inch, Mid 2014)

※Minikube上でサービスを動作させるにはそれなりにメモリが必要です。メインメモリが8GBのマシンの場合、Minikube用VMへの割り当てを変更するなどしてお試しください。（筆者はメモリ16GBのマシンでVMに8GBを割り当てて実行しています。）

# Minikubeを動かすまで(Kubernetes環境準備)

まずは、Kubernetes環境をローカルマシン内に準備します。
基本的には、[Minikubeのサイトに記載されている導入手順](https://kubernetes.io/docs/tasks/tools/install-minikube/)通りなのですが、このエントリで利用している方法について簡単に説明します。

## ハイパーバイザ

このエントリでは、[VirtualBox](https://www.virtualbox.org)を利用します。通常のインストールをした上で、「拡張機能パック」もインストールしています。

## Kubernetes-cli

macOSで[brew](https://brew.sh/index_ja)を使ったインストールです。(brewの導入自体は、brewのタイト上にある通り、上で1行で終了します。)

```
$ brew install kubernetes-cli
```

<details><summary>こんな感じでKubernetes-cli 1.11.1が導入されます。</summary><div>
```
==> Downloading https://homebrew.bintray.com/bottles/kubernetes-cli-1.11.1.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring kubernetes-cli-1.11.1.high_sierra.bottle.tar.gz
==> Caveats
Bash completion has been installed to:
  /usr/local/etc/bash_completion.d

zsh completions have been installed to:
  /usr/local/share/zsh/site-functions
==> Summary
🍺  /usr/local/Cellar/kubernetes-cli/1.11.1: 196 files, 53.7MB
```
</div>

## Minikube

リリースページから最新版(エントリ作成時点ではv0.28.2)を取得します。
macOSに対応した、「minikube-darwin-amd64」をダウンロードします。

Knativeのサイトに記載されたオプションでMinikubeを起動します。上と同じく、--vm-driverオプションにはvirtualboxを指定しています。
(うっかり、「minikube」コマンドを単独で実行すると、出来上がったVMがデフォルト設定でメモリが2GBytes程度となり、あとでサービスが増えた時にハマります。)

なお、コマンドが少し長いので筆者はスクリプトにして実行しました。

```bash
#!/bin/sh
minikube start --memory=8192 --cpus=4 \
  --kubernetes-version=v1.10.5 \
  --vm-driver=virtualbox \
  --bootstrapper=kubeadm \
  --extra-config=controller-manager.cluster-signing-cert-file="/var/lib/localkube/certs/ca.crt" \
  --extra-config=controller-manager.cluster-signing-key-file="/var/lib/localkube/certs/ca.key" \
  --extra-config=apiserver.admission-control="LimitRanger,NamespaceExists,NamespaceLifecycle,ResourceQuota,ServiceAccount,DefaultStorageClass,MutatingAdmissionWebhook"
```

## Istio

サイトにあるとおりの手順でサービスメッシュの[Istio](https://istio.io)を開始します。

```
curl -L https://storage.googleapis.com/knative-releases/serving/latest/istio.yaml \
  | sed 's/LoadBalancer/NodePort/' \
  | kubectl apply -f -
```

サービスが上がるまで待ちます。

<details><summary>状態を確認して、最初はこんなかんじで「ContainerCreating」状態のサービスを含んでいたものが
</summary><div>


```
$ kubectl get pods -n istio-system
NAME                                       READY     STATUS              RESTARTS   AGE
istio-citadel-7bdc7775c7-2nqq6             0/1       ContainerCreating   0          1m
istio-cleanup-old-ca-vgg5g                 0/1       ContainerCreating   0          1m
istio-egressgateway-795fc9b47-fkpw9        1/1       Running             0          1m
istio-ingress-84659cf44c-46rmd             0/1       ContainerCreating   0          1m
istio-ingressgateway-7d89dbf85f-vkgcw      0/1       ContainerCreating   0          1m
istio-mixer-post-install-4jgcb             0/1       Completed           0          1m
istio-pilot-66f4dd866c-f4kpt               0/2       ContainerCreating   0          1m
istio-policy-76c8896799-2lggq              0/2       ContainerCreating   0          1m
istio-sidecar-injector-645c89bc64-hgcbq    0/1       ContainerCreating   0          1m
istio-statsd-prom-bridge-949999c4c-9dzd7   1/1       Running             0          1m
istio-telemetry-6554768879-sz59r           0/2       ContainerCreating   0          1m
```
</div>

<details><summary>状態を確認して、全て「Running」か「Completed」となればOKです。
</summary><div>
```
$ kubectl get pods -n istio-system
NAME                                       READY     STATUS      RESTARTS   AGE
istio-citadel-7bdc7775c7-2nqq6             1/1       Running     0          3m
istio-cleanup-old-ca-vgg5g                 0/1       Completed   0          3m
istio-egressgateway-795fc9b47-fkpw9        1/1       Running     0          3m
istio-ingress-84659cf44c-46rmd             1/1       Running     0          3m
istio-ingressgateway-7d89dbf85f-vkgcw      1/1       Running     0          3m
istio-mixer-post-install-4jgcb             0/1       Completed   0          3m
istio-pilot-66f4dd866c-f4kpt               1/2       Running     0          3m
istio-policy-76c8896799-2lggq              2/2       Running     0          3m
istio-sidecar-injector-645c89bc64-hgcbq    1/1       Running     0          3m
istio-statsd-prom-bridge-949999c4c-9dzd7   1/1       Running     0          3m
istio-telemetry-6554768879-sz59r           2/2       Running     0          3m
```
</div>


## Knative Serving

[Knative Serving](https://github.com/knative/serving)を導入します。


サイトの通りに開始します。

```bash
curl -L https://storage.googleapis.com/knative-releases/serving/latest/release-lite.yaml \
  | sed 's/LoadBalancer/NodePort/' \
  | kubectl apply -f -
```


<details><summary>ログはこんな感じ。10秒程度。
</summary><div>

```
$ time curl -L https://storage.googleapis.com/knative-releases/serving/latest/release-lite.yaml \
>   | sed 's/LoadBalancer/NodePort/' \
>   | kubectl apply -f -
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0namespace/knative-build created
clusterrole.rbac.authorization.k8s.io/knative-build-admin created
serviceaccount/build-controller created
clusterrolebinding.rbac.authorization.k8s.io/build-controller-admin created
customresourcedefinition.apiextensions.k8s.io/builds.build.knative.dev created
customresourcedefinition.apiextensions.k8s.io/buildtemplates.build.knative.dev created
service/build-controller created
service/build-webhook created
configmap/config-logging created
deployment.apps/build-controller created
deployment.apps/build-webhook created
namespace/knative-serving created
clusterrole.rbac.authorization.k8s.io/knative-serving-admin created
clusterrole.rbac.authorization.k8s.io/knative-serving-write created
serviceaccount/controller created
serviceaccount/autoscaler created
clusterrolebinding.rbac.authorization.k8s.io/knative-serving-controller-admin created
clusterrolebinding.rbac.authorization.k8s.io/knative-serving-autoscaler-write created
gateway.networking.istio.io/knative-shared-gateway created
service/knative-ingressgateway created
deployment.extensions/knative-ingressgateway created
horizontalpodautoscaler.autoscaling/knative-ingressgateway created
customresourcedefinition.apiextensions.k8s.io/configurations.serving.knative.dev created
customresourcedefinition.apiextensions.k8s.io/revisions.serving.knative.dev created
customresourcedefinition.apiextensions.k8s.io/routes.serving.knative.dev created
customresourcedefinition.apiextensions.k8s.io/services.serving.knative.dev created
service/activator-service created
service/controller created
service/webhook created
deployment.apps/activator created
service/autoscaler created
deployment.apps/autoscaler created
configmap/config-autoscaler created
configmap/config-controller created
configmap/config-domain created
configmap/config-logging created
configmap/config-network created
configmap/config-observability created
deployment.apps/controller created
deployment.apps/webhook created
namespace/monitoring created
configmap/grafana-dashboard-definition-istio created
configmap/grafana-dashboard-definition-mixer created
configmap/grafana-dashboard-definition-pilot created
serviceaccount/kube-state-metrics created
role.rbac.authorization.k8s.io/kube-state-metrics-resizer created
rolebinding.rbac.authorization.k8s.io/kube-state-metrics created
clusterrole.rbac.authorization.k8s.io/kube-state-metrics created
clusterrolebinding.rbac.authorization.k8s.io/kube-state-metrics created
deployment.extensions/kube-state-metrics created
service/kube-state-metrics created
configmap/grafana-dashboard-definition-kubernetes-deployment created
configmap/grafana-dashboard-definition-kubernetes-capacity-planning created
 83  485k   83  403k    0     0   167k      0  0:00:02  0:00:02 --:--:--  167kconfigmap/grafana-dashboard-definition-kubernetes-cluster-health created
configmap/grafana-dashboard-definition-kubernetes-cluster-status created
configmap/grafana-dashboard-definition-kubernetes-control-plane-status created
configmap/grafana-dashboard-definition-kubernetes-resource-requests created
100  485k  100  485k    0     0   160k      0  0:00:03  0:00:03 --:--:--  160k
configmap/grafana-dashboard-definition-kubernetes-nodes created
configmap/grafana-dashboard-definition-kubernetes-pods created
configmap/grafana-dashboard-definition-kubernetes-statefulset created
serviceaccount/node-exporter created
clusterrole.rbac.authorization.k8s.io/node-exporter created
clusterrolebinding.rbac.authorization.k8s.io/node-exporter created
daemonset.extensions/node-exporter created
service/node-exporter created
configmap/scaling-config created
service/fluentd-ds created
configmap/grafana-dashboard-definition-knative-efficiency created
configmap/grafana-dashboard-definition-knative created
configmap/grafana-datasources created
configmap/grafana-dashboards created
service/grafana created
deployment.apps/grafana created
logentry.config.istio.io/requestlog created
fluentd.config.istio.io/requestloghandler created
rule.config.istio.io/requestlogtofluentd created
metric.config.istio.io/revisionrequestcount created
metric.config.istio.io/revisionrequestduration created
metric.config.istio.io/revisionrequestsize created
metric.config.istio.io/revisionresponsesize created
prometheus.config.istio.io/revisionpromhandler created
rule.config.istio.io/revisionpromhttp created
service/kube-controller-manager created
service/prometheus-system-discovery created
configmap/prometheus-scrape-config created
serviceaccount/prometheus-system created
role.rbac.authorization.k8s.io/prometheus-system created
role.rbac.authorization.k8s.io/prometheus-system created
role.rbac.authorization.k8s.io/prometheus-system created
role.rbac.authorization.k8s.io/prometheus-system created
clusterrole.rbac.authorization.k8s.io/prometheus-system created
rolebinding.rbac.authorization.k8s.io/prometheus-system created
rolebinding.rbac.authorization.k8s.io/prometheus-system created
rolebinding.rbac.authorization.k8s.io/prometheus-system created
rolebinding.rbac.authorization.k8s.io/prometheus-system created
clusterrolebinding.rbac.authorization.k8s.io/prometheus-system created
service/prometheus-system-np created
statefulset.apps/prometheus-system created
logentry.config.istio.io/requestlog unchanged
fluentd.config.istio.io/requestloghandler unchanged
rule.config.istio.io/requestlogtofluentd unchanged
metric.config.istio.io/revisionrequestcount unchanged
metric.config.istio.io/revisionrequestduration unchanged
metric.config.istio.io/revisionrequestsize unchanged
metric.config.istio.io/revisionresponsesize unchanged
prometheus.config.istio.io/revisionpromhandler unchanged
rule.config.istio.io/revisionpromhttp unchanged

real	0m7.227s
user	0m0.604s
sys	0m0.154s
```
</div>

動作を確認します。

```
$ kubectl get pods -n knative-serving
NAME                          READY     STATUS    RESTARTS   AGE
activator-5f78d674df-b7f2v    2/2       Running   4          17m
autoscaler-5787568ff8-5mg79   2/2       Running   4          17m
controller-c8dd5d467-8hnhx    1/1       Running   2          17m
webhook-78f58769cb-m5s7z      1/1       Running   2          17m
```

# Knative上でサンプルを動かす

Knativeのサイト上のガイド[「Getting Started with Knative App Deployment」](https://github.com/knative/docs/blob/master/install/getting-started-knative-app.md)の例を動かしてみます。

## services.yamlの準備

このようなファイルを準備します。ガイドに示された、「gcr.io/knative-samples/helloworld-go」のイメージを使っています。

```yaml
apiVersion: serving.knative.dev/v1alpha1 # Current version of Knative
kind: Service
metadata:
  name: helloworld-go # The name of the app
  namespace: default # The namespace the app will use
spec:
  runLatest:
    configuration:
      revisionTemplate:
        spec:
          container:
            image: gcr.io/knative-samples/helloworld-go # The URL to the image of the app
            env:
            - name: TARGET # The environment variable printed out by the sample app
              value: "Go Sample v1"
```

動かします。(ここの簡単さがKnativeのいいところ)

```
$ kubectl apply -f service.yaml
service.serving.knative.dev/helloworld-go created
```

この1行で、Knativeは下記を担当してくれます。
- このアプリの普遍なリビジョンを作る
- route, ingress, load balancerを作る
- podのスケールアップ/ダウン

動作状況を確認します。

```
$ 
NAME                     TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE
knative-ingressgateway   NodePort   10.104.55.123   <none>        80:32380/TCP,443:32390/TCP,32400:32400/TCP   25m
```

サービスのURLを調べて、

```
$ kubectl get services.serving.knative.dev helloworld-go  -o=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain
NAME            DOMAIN
helloworld-go   helloworld-go.default.example.com
```

Knativeのサイトにある方法で接続先IPアドレスを調べて、

```
$ echo $(minikube ip):$(kubectl get svc knative-ingressgateway -n istio-system -o 'jsonpath={.spec.ports[?(@.port==80)].nodePort}')
```

```
192.168.99.101:32380
```

このアドレスを変数にセットして、

```bash
export IP_ADDRESS=192.168.99.100:32380
```

接続してみます。

```bash
$ curl -H "Host: ${HOST_URL}" http://${IP_ADDRESS}
Hello World: Go Sample v1!
```

Goプログラムからの出力が確認できました。

services.yamlの内容を少し書き換えて

```yaml
              value: "Go Sample version2"
```

サービスを登録し直すと、

```bash
$ kubectl apply -f service.yaml
service.serving.knative.dev/helloworld-go configured
```

少し経つと、サービスの環境変数が新しい内容となり、コンソールの出力変わることが確認できます。

```
$ curl -H "Host: ${HOST_URL}" http://${IP_ADDRESS}
Hello World: Go Sample version2
```

```bash
$ minikube dashboard
```

を実行すると、サービスのデプロイの様子をWebのコンソールで確認できます(本エントリ冒頭の図)。

# まとめ

本エントリでは、Knativeをローカルマシン1台で試す方法について説明しました。
Minikubeを使うと、マシン一台でお手軽に試すことができるのでおすすめです。