Kubernetesのチュートリアルをたどる

kubernetes

Kubernetesとは

Kubernetes(発音はkoo-ber-nay’-tace。 ギリシャ語で操舵手。)はGoogleによって開発が始められた、アプリケーションコンテナにおける自動デプロイ、スケーリング、操作を 自動化するOSS。K8sと略される。

Minikube

K8sをローカルで試すために、MinikubeというVMの中で単一ノードのK8sクラスターを動かすツールを入れる。

v0.6.0

curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.6.0/minikube-darwin-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
$ minikube start
Starting local Kubernetes cluster...

...

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"2", GitVersion:"v1.2.4", GitCommit:"3eed1e3be6848b877ff80a93da3785d9034d0a4f", GitTreeState:"clean"}
Server Version: version.Info{Major:"1", Minor:"3", GitVersion:"v1.3.0", GitCommit:"283137936a498aed572ee22af6774b6fb6e9fd94", GitTreeState:"clean"}

Pods

K8sではコンテナのグループをpodと呼ぶ。pod中のコンテナは共にデプロイされ、起動し、停止する。 また、グループとして複製される。

Podの定義は次のようにyamlで書かれる。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
    ports:
    - containerPort: 80

Podの定義に望ましい状態を記述すると、Kubernatesはそれを見て現在の状態が一致しているかどうか確認する。 例えば、Podが作られたときに、コンテナがその中で動いている状態が望ましい状態だとすると、 コンテナが動かなくなったときに、Kubernatesは新しいものを再作成することで望ましい状態にする。

podの作成とリストの取得のコマンドは次の通り。

$ kubectl create -f nginx.yaml
$ kubectl get pods
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          4s

kubectl execでpsコマンドを実行し、 podが動いていることを確認しようとしたがコマンドがなかった。

$ kubectl exec -it nginx ps
rpc error: code = 2 desc = "oci runtime error: exec failed: exec: \"ps\": executable file not found in $PATH"error: error executing remote command: error executing command in container: Error executing in Docker Container: 126

そこでリクエストを投げるためにkubectl runで単一コンテナのDeploymentを作成する。 Deploymentについては後で説明がある。 --restart=Neverでpodが存在しなくなったときに再起動しないようにし、--envで環境変数としてnginxのpodのIPを渡している。

$ kubectl run busybox --image=busybox --restart=Never --tty -i --env "POD_IP=$(kubectl get pod nginx -o go-template={{.status.podIP}})"
busybox$ wget -qO- http://$POD_IP
busybox$ exit # Exit the busybox container

$ kubectl get deployments

$ kubectl get pods
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          54m

--restart=Neverなしで実行するとこうなる。

$ kubectl run busybox --image=busybox --tty -i --env "POD_IP=$(kubectl get pod nginx -o go-template={{.status.podIP}})"
busybox$ exit
Session ended, resume using 'kubectl attach busybox-985443498-9elny -c busybox -i -t' command when the pod is running

$ kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
busybox   1         1         1            1           3m

$ kubectl get pods
NAME                      READY     STATUS    RESTARTS   AGE
busybox-985443498-9elny   1/1       Running   1          3m
nginx                     1/1       Running   0          59m

$ kubectl delete pods busybox-985443498-9elny
pod "busybox-985443498-9elny" deleted

$ kubectl get pods # Podsだけ削除しても新しいものが立ち上がってくる
NAME                      READY     STATUS              RESTARTS   AGE
busybox-985443498-4iyvx   0/1       ContainerCreating   0          1s
busybox-985443498-9elny   1/1       Terminating         1          13m
nginx                     1/1       Running             0          1h

$ kubectl delete deployment busybox

$ kubectl get pods # Deploymentを削除するとPodsも消える
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          1h

Volumes

コンテナのファイルシステムはコンテナが生存しているときに限り有効なので、 永続化したいデータはコンテナの外に保存する必要がある。

次の例のようにvolumesでvolumeを定義し、volumeMountsでどこにマウントするか指定することができる。 volumesには、そのPodがノードで実行されている間存在するディレクトリを作成して使うEmptyDirか、 ノードのファイルシステムに既に存在するディレクトリを使うHostPathを指定する。

apiVersion: v1
kind: Pod
metadata:
  name: redis
spec:
  containers:
  - name: redis
    image: redis
    volumeMounts:
    - name: redis-persistent-storage
      mountPath: /data/redis
  volumes:
  - name: redis-persistent-storage
    emptyDir: {}

Labels

metadataとしてkey-valueのラベルを付けることができる。

metadata:
  name: nginx
  labels:
    app: nginx
kubectl get pods -l app=nginx
NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          2m

Deployments

実行しているPodの維持と更新を管理するのが上でも出てきたDeployment。 Deploymentの定義にはPodを作るためのテンプレートと維持するPod数を記述する。 pod名はDeployment名から生成されるのでtemplateには含めない。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
$ kubectl create -f deployment.yaml
$ kubectl get deployment
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   2         2         2            2           3m

$ kubectl get pods -l app=nginx
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1159050644-ar3i7   1/1       Running   0          3m
nginx-deployment-1159050644-kdjly   1/1       Running   0          3m

kubectl applyでDeploymentの変更を実行中のPodに適用する。 次の例ではnginxのバージョンを上げている。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.8
        ports:
        - containerPort: 80
$ kubectl apply -f deployment-update.yaml
$ kubectl get pods
NAME                                READY     STATUS              RESTARTS   AGE
nginx-deployment-1159050644-kdjly   1/1       Running             0          30m
nginx-deployment-1771418926-pi3vy   0/1       ContainerCreating   0          7s
nginx-deployment-1771418926-ygoxl   0/1       ContainerCreating   0          7s

$ kubectl get pods
NAME                                READY     STATUS              RESTARTS   AGE
nginx-deployment-1771418926-pi3vy   0/1       ContainerCreating   0          12s
nginx-deployment-1771418926-ygoxl   1/1       Running             0          12s

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-1771418926-pi3vy   1/1       Running   0          15s
nginx-deployment-1771418926-ygoxl   1/1       Running   0          15s

Services

アプリケーションのレイヤー間(例えばフロントエンドとバックエンド)の接続で使われるのがService。 次のServiceは、8080番ポートで待ち、app: nginxのラベルが付いているPodの80番ポートに向けるロードバランサーとして働く。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 8000
    targetPort: 80
    protocol: TCP
  selector:
    app: nginx
$ kubectl create -f service.yaml
$ kubectl get services
NAME            CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
kubernetes      10.0.0.1     <none>        443/TCP    19h
nginx-service   10.0.0.90    <none>        8000/TCP   36s

$ export SERVICE_IP=$(kubectl get service nginx-service -o go-template='{{.spec.clusterIP}}')
$ export SERVICE_PORT=$(kubectl get service nginx-service -o go-template='{{(index .spec.ports 0).port}}')
$ echo "$SERVICE_IP:$SERVICE_PORT"
10.0.0.90:8000
$ kubectl run busybox --image=busybox --restart=Never --tty -i --env "SERVICE_IP=$SERVICE_IP,SERVICE_PORT=$SERVICE_PORT"
busybox$ wget -qO- http://$SERVICE_IP:$SERVICE_PORT

Health Checking

livenessProbeにヘルスチェックの設定を含めることができる。 次の例では、初期化のために30秒待った後、/_status/healthzへのHTTP GETリクエストに対して 200~399以外のステータスコードが返るか、1秒以上かかった場合、コンテナを再起動する。

apiVersion: v1
kind: Pod
metadata:
  name: pod-with-healthcheck
spec:
  containers:
  - name: nginx
    image: nginx
    livenessProbe:
      httpGet:
        path: /_status/healthz
        port: 80
      initialDelaySeconds: 30
      timeoutSeconds: 1
    ports:
    - containerPort: 80