Kubernetes 速習会

  • 67
    Like
  • 0
    Comment

この速習会の目標

Developer がさくっと kubernetes を理解して、実際にリリース出来る

  • kubectl が叩ける用になる
  • Kubernetes の manifest ファイルの書き方
  • Kubernetes への Release / Deploy 方法
  • Kubernetes 上にあるApplication の maintenance
  • Kubernetes に上げたApplicationのMonitoring

kubectl を install

kubectl の install してなかった場合は、こちらの実行ファイルを直接ダウンロードして叩けるようにしましょう

The linux kubectl binary can be fetched with a command like:

curl -O https://storage.googleapis.com/kubernetes-release/release/v1.3.4/bin/linux/amd64/kubectl

On an OS X workstation, replace linux in the URL above with darwin:

curl -O https://storage.googleapis.com/kubernetes-release/release/v1.3.4/bin/darwin/amd64/kubectl

chmod +x kubectl
mv kubectl /usr/local/bin/kubectl

ref. https://coreos.com/kubernetes/docs/latest/configure-kubectl.html

config ファイルを設定する

期限付きURL から設定ファイルをダウンロード(リンクは削除してあります)してみよう

  • 既に k8s を使っている方

既存の config と混ざってしまうと大変のため、kubectl command の引数に --kubeconfig= を付けて絶対他のcluster に実行させないようにしましょう

ex:

kubectl get po --kubeconfig=k8s-sokusyu.yaml
  • 初めて使う方

いちいち --kubeconfig=k8s-sokusyu.yaml 打つのが面倒なので、 ~/.kube/config にリネームしておきましょう。
kubectl は default で ~/.kube/config の読み込んで実行してくれます。

mkdir ~/.kube
cp ~/Downloads/k8s-sokusyu.yaml ~/.kube/config

kubectl を実行してみよう

kubectl get pod
kubectl get po --kubeconfig=k8s-sokusyu.yaml

一覧が見えましたでしょうか?

$ kubectl get pod
NAME                          READY     STATUS    RESTARTS   AGE
hello-world-855027487-25n7g   1/1       Running   0          49m

このような画面が返ってきたら設定は完了です!

hello world

hello world を git clone して下さい。
ここでは、シンプルな sinatra で作ったアプリケーションを例に進めて行きます。

まずは自分のスペースを作ってみよう

Kubernetes では、たくさんのサーバーに対して、一つのクラスタリングを作り、一つの大きなリソース(サーバー)に見せます。
そのため、複数のチームやたくさんの web application を実行する際にそのまま default で上げ続けるとすべてのアプリケーションが同じところに表示されて、すごく使いづらくなります。

namespace について

ref. https://github.com/kubernetes/kubernetes/blob/master/docs/design/namespaces.md

Motivation
A single cluster should be able to satisfy the needs of multiple user communities.
Each user community wants to be able to work in isolation from other communities.

複数のコミュニティ毎に分けて利用することで

  • すべてのresource (pod rc rs deployment secret etc...)を分けて利用できる
  • 他のapplication を誤って操作出来てしまうのを防ぐ
  • namespace を default のまま利用した場合、自分の見たいものが探しにくくなる(常にcommandでlabel を指定する必要がある)
  • namespace は初期値として、既に defaultkube-system でわけられており、kubernetes の service が誤って操作されないようにわけられている

そこで、速習会では、個々人で namespace を作って自分のスペースで kubernetes に取り組んで行こうと思います。

名前は自由ですが、githubアカウント名を使うと周りも理解しやすいのでオススメです。

それぞれの namespace を作ってみよう

まずは、ファイルを編集します。

diff --git a/kubernetes/namespace.yaml b/kubernetes/namespace.yaml
index 5a96da9..57c89f7 100644
--- a/kubernetes/namespace.yaml
+++ b/kubernetes/namespace.yaml
@@ -1,4 +1,4 @@
 apiVersion: v1
 kind: Namespace
 metadata:
-  name: #GitHub Account Name
+  name: koudaiii

その後、実行してみましょう

$ kubectl create -f kubernetes/namespace.yaml 
namespace "koudaiii" created

無事作成できました。実際に出来たか確認しみよう!
すべての namespace の一覧を表示させてみます。

$ kubectl get namespace
NAME          STATUS    AGE
default       Active    86d
koudaiii      Active    5m
kube-system   Active    86d
  • 他の人の namespace を見てみよう

--namespace= で指定すると、その namespace で作られた pod が見れます。
ここでは kubernetes 上で作られる pod の一覧を表示しています。

$ kubectl get po --namespace=kube-system
NAME                                                                    READY     STATUS    RESTARTS   AGE
elasticsearch-logging-v1-0nyp4                                          1/1       Running   0          69d
elasticsearch-logging-v1-ixfyy                                          1/1       Running   0          58d
・・・・・・・・・・・・・・・

※複数のクラスタがあってめんどくさい人への設定があります。デフォルトでは、 namespace=default が指定されて表示されますが、変更することが出来ます。

実際に叩いてみよう

  • 自分の space で実際に動かしてみよう

kubectl create -f kubernetes/production.yaml --namespace=koudaiii

大まかな manifest file の理解

  • kind
    • Pod
    • Deployment
    • Service

  • meta data
    • label
    • name
$ cat kubernetes/pod.yaml 
apiVersion: v1
kind: Service
metadata:
  labels:
    name: hello-world
    role: web
  name: hello-world
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    name: hello-world
    role: web
  type: LoadBalancer
---
apiVersion: v1
kind: Pod
metadata:
  name: "hello-world"
  labels:
    name: hello-world
    role: web
spec:
  containers:
    - image: koudaiii/hello-world:latest
      name: hello-world
      ports:
        - containerPort: 8080
      env:
        - name: MESSAGE
          value: Hello Wantedly

Kobito.IM03fJ.png

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: hello-world
  labels:
    name: hello-world
    role: web
spec:
  minReadySeconds: 30
  strategy:
    type: RollingUpdate
  replicas: 1
  template:
    metadata:
      name: hello-world
      labels:
        name: hello-world
        role: web
    spec:
      containers:
      - image: koudaiii/hello-world:latest
        name: hello-world
        ports:
          - containerPort: 8080
        env:
          - name: MESSAGE
            value: Hello Wantedly

golang を出してみよう

https://github.com/koudaiii/go_server を git clone して実行してこう

golang の application を立てる

kubectl create -f kubernetes/blue.yaml --namespace=koudaiii
kubectl create -f kubernetes/green.yaml --namespace=koudaiii
kubectl create -f kubernetes/service-go.yaml --namespace=koudaiii

front になる nginx を立てる

kubectl get po -w --namespace=koudaiii
kubectl create -f kubernetes/deployment-nginx.yaml --namespace=koudaiii
kubectl create -f kubernetes/service-nginx.yaml --namespace=koudaiii

確認してみよう

kubectl get svc --namespace=koudaiii
kubectl get deployment --namespace=koudaiii
kubectl describe svc --namespace=koudaiii

blue-green deployent

ref. https://speakerdeck.com/koudaiii/kubernetes-woshi-tutesabisuwojia-su-saseruqu-rizu-mi

Kobito.nhowIs.png

  • Color を知ろう!

Kobito.dki04l.png

$ kubectl describe svc go --namespace=koudaiii
Name:           go
Namespace:      koudaiii
Labels:         app=go,name=app
Selector:       app=go,color=blue,name=app
Type:           NodePort
IP:         172.16.174.98
Port:           go  8080/TCP
NodePort:       go  32067/TCP
Endpoints:      172.18.2.5:8080
Session Affinity:   None
No events.
  • Rolling Update してみよう!

Kobito.LqcmDd.png

$ kubectl edit deployment green --namespace=koudaiii
 21 spec:
 22   minReadySeconds: 30
 23   replicas: 10  # ここを10にしてみました                                                                                                                                                                                               
 24   selector:
 25     matchLabels:
 26       app: go

deployment "green" edited
  • Switch してみよう!

Kobito.iQJLvH.png

$ kubectl edit svc go --namespace=koudaiii

 25   selector:
 26     app: go
 27     color: green       ## blue -> green  へ変更                                                                                                                                                                                       
 28     name: app
 29   sessionAffinity: None
service "go" edited
  • Endpoint が増えたか確認してみよう
$ kubectl describe svc go --namespace=koudaiii
Name:           go
Namespace:      koudaiii
Labels:         app=go,name=app
Selector:       app=go,color=green,name=app
Type:           NodePort
IP:         172.16.174.98
Port:           go  8080/TCP
NodePort:       go  32067/TCP
Endpoints:      172.18.0.5:8080,172.18.1.6:8080,172.18.10.6:8080 + 7 more...
Session Affinity:   None
No events.

もう少し踏み込んだ manifest file の理解

ref. https://speakerdeck.com/koudaiii/kubernetes-woshi-tutesabisuwojia-su-saseruqu-rizu-mi

  • healthcheck

http://kubernetes.io/docs/user-guide/production-pods/
http://kubernetes.io/docs/user-guide/pod-states/#container-probes

  • readinessProbe

初回起動時に data や config をたくさん読むこむような大きいアプリケーションの場合、readinessProbe を使う。
Success になった後、 実際に traffic を受付ける。

  • livenessProbe

コンテナが生きているどうか見るもの。
LivenessProbe が Failure の場合、 RestartPolicy の対象となり kubelet から コンテナを kill する。

Process Health Checking

http://kubernetes.io/docs/user-guide/walkthrough/k8s201/#health-checking

kubernetes 上では default でコンテナのプロセスをチェックしており、もし失敗していれば restart するようになっている。

Application Health Checking

http://kubernetes.io/docs/user-guide/liveness/

  • httpGet を使ったヘルスチェック
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        livenessProbe:
          httpGet:
            # Path to probe; should be cheap, but representative of typical behavior
            path: /index.html
            port: 80
          initialDelaySeconds: 30
          timeoutSeconds: 1
  • exec を使ったヘルスチェック
apiVersion: v1
kind: Pod
metadata:
  labels:
    test: liveness
  name: liveness-exec
spec:
  containers:
  - args:
    - /bin/sh
    - -c
    - echo ok > /tmp/health; sleep 10; rm -rf /tmp/health; sleep 600
    image: gcr.io/google_containers/busybox
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/health
      initialDelaySeconds: 15
      timeoutSeconds: 1
    name: liveness
  • 確認

Unhealthyで何度も restart されているのがわかる

$ kubectl describe pods liveness-exec
・・・・・・・・・・・・・・
Events:
  FirstSeen LastSeen    Count   From                                SubobjectPath           Type        Reason      Message
  --------- --------    -----   ----                                -------------           --------    ------      -------
  4m        4m      1   {default-scheduler }                                        Normal      Scheduled   Successfully assigned liveness-exec to ip-172-20-0-171.ap-northeast-1.compute.internal
  4m        4m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Created     Created container with docker id 027dc93671c0
  4m        4m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Started     Started container with docker id 027dc93671c0
  3m        3m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Killing     Killing container with docker id 027dc93671c0: pod "liveness-exec_default(af352909-1d93-11e6-9d4c-06164e6ae803)" container "liveness" is unhealthy, it will be killed and re-created.
  3m        3m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Started     Started container with docker id 6c79b57f8d03
  3m        3m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Created     Created container with docker id 6c79b57f8d03
  2m        2m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Killing     Killing container with docker id 6c79b57f8d03: pod "liveness-exec_default(af352909-1d93-11e6-9d4c-06164e6ae803)" container "liveness" is unhealthy, it will be killed and re-created.
  2m        2m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Created     Created container with docker id 6037f2406959
  2m        2m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Started     Started container with docker id 6037f2406959
  1m        1m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Killing     Killing container with docker id 6037f2406959: pod "liveness-exec_default(af352909-1d93-11e6-9d4c-06164e6ae803)" container "liveness" is unhealthy, it will be killed and re-created.
  1m        1m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Started     Started container with docker id 3eb2af8cfef1
  1m        1m      1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Created     Created container with docker id 3eb2af8cfef1
  48s       48s     1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Killing     Killing container with docker id 3eb2af8cfef1: pod "liveness-exec_default(af352909-1d93-11e6-9d4c-06164e6ae803)" container "liveness" is unhealthy, it will be killed and re-created.
  4m        48s     5   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Pulling     pulling image "gcr.io/google_containers/busybox"
  46s       46s     1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Started     Started container with docker id 862d07529857
  4m        46s     5   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Pulled      Successfully pulled image "gcr.io/google_containers/busybox"
  46s       46s     1   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Normal      Created     Created container with docker id 862d07529857
  4m        28s     7   {kubelet ip-172-20-0-171.ap-northeast-1.compute.internal}   spec.containers{liveness}   Warning     Unhealthy   Liveness probe failed: cat: can't open '/tmp/health': No such file or directory

lifecycle

Kubernetes では delete した際に、 SIGTERM が送られる。
terminationGracePeriodSeconds(default 30s)より長い場合は、 SIGKILL が送られる。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx
spec:
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        lifecycle:
          preStop:
            exec:
              # SIGTERM triggers a quick exit; gracefully terminate instead
              command: ["/usr/sbin/nginx","-s","quit"]
  • termination message

deploy や debugger 目的で terminate されたかどうかを Message を設定することで確認できる。
また log に吐き出されるため、 logentries などで確認することも可能である。

apiVersion: v1
kind: Pod
metadata:
  name: pod-w-message
spec:
  containers:
  - name: messager
    image: "ubuntu:14.04"
    command: ["/bin/sh","-c"]
    args: ["sleep 60 && /bin/echo Sleep expired > /dev/termination-log"]
  • メッセージとStatusを確認する
$ kubectl create -f examples/lifecycle/termination-message.yaml

$ kubectl get pods/pod-w-message -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.message}}{{end}}"
Sleep expired
$ kubectl get pods/pod-w-message -o go-template="{{range .status.containerStatuses}}{{.lastState.terminated.exitCode}}{{end}}"

環境変数

Kubernetes - kubectl create secret generic

# Create a new secret named my-secret with key1=supersecret and key2=topsecret
$ kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret

http://kubernetes.io/docs/user-guide/secrets/#using-secrets-as-environment-variables

changelog: https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG.md#changes-since-v111

     env:
        - name: SECRET_USERNAME
          valueFrom:
            secretKeyRef:
              name: my-secret
              key: key1
        - name: SECRET_PASSWORD
          valueFrom:
            secretKeyRef:
              name: my-secret
              key: key2

namespace を越えての通信と名前解決について

ref. https://github.com/kubernetes/kubernetes/blob/master/build/kube-dns/README.md

namespace で区切ったとしても kubernetes では同じ cluster にいる場合は、ネットワーク的にはつながっています。
また、 skydns という kubernetes の addon が用意されており、名前解決も出来ます。

  • service my-svc.my-namespace.svc.cluster.local
$ kubectl run -i --tty --rm busybox --image=busybox --restart=Never -- sh
/ # nslookup nginx.koudaiii.svc.cluster.local
Server:    100.64.0.10
Address 1: 100.64.0.10

Name:      nginx.koudaiii.svc.cluster.local
Address 1: 100.64.17.97
  • pod pod-ip-address.my-namespace.pod.cluster.local

describe pod で ip を確認して、- で繋いで上げると名前解決できます。

$ kubectl describe po/blue-534540973-9svyj --namespace=koudaiii
Name:       blue-534540973-9svyj
Namespace:  koudaiii
Node:       ip-172-20-0-54.ap-northeast-1.compute.internal/172.20.0.54
Start Time: Mon, 22 Aug 2016 11:29:56 +0900
Labels:     app=go,color=blue,name=app,pod-template-hash=534540973
Status:     Running
IP:     100.66.0.12 # ip を確認
・・・・・
$ kubectl run -i --tty --rm busybox --image=busybox --restart=Never -- sh
/ # nslookup 100-66-0-12.koudaiii.pod.cluster.local
Server:    100.64.0.10
Address 1: 100.64.0.10

Name:      100-66-0-12.koudaiii.pod.cluster.local
Address 1: 100.66.0.12

Kubernetes の裏側を知る

ref. https://speakerdeck.com/thockin/kubernetes-extensibility
ref. http://kubernetes.io/docs/hellonode/

  • Google では10年以上前からコンテナを動かしている

Kobito.7kNj5k.png

  • kubernetes がどうなっているのか

Kobito.5GkMhC.png

  • ReplicationController と Deployment

今回立てたやつを Datadog で見てみる

  • Datadog

Monitoring Service です。
Wantedlyでどのように使われてるかは、こちらの記事に書かれています。

  • Dashboardを見てみる

Kobito.eNVlxP.png

  • kubernetes に dd-agent を立てるには??

git に管理したくない api key をcommandから kubernetes 上に登録させる

$ kubectl create secret generic dd-agent --from-literal=api-key=your api key
secret "dd-agent" created

datadog 公式が用意している manifestfile

apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: dd-agent
spec:
  template:
    metadata:
      labels:
        app: dd-agent
      name: dd-agent
    spec:
      containers:
        - image: datadog/docker-dd-agent:kubernetes
          imagePullPolicy: Always
          name: dd-agent
          ports:
            - containerPort: 8125
              name: dogstatsdport
          env:
            - name: API_KEY
              valueFrom:
                secretKeyRef:
                  name: dd-agent
                  key: api-key
          volumeMounts:
            - name: dockersocket
              mountPath: /var/run/docker.sock
            - name: procdir
              mountPath: /host/proc
              readOnly: true
            - name: cgroups
              mountPath: /host/sys/fs/cgroup
              readOnly: true
      volumes:
        - hostPath:
            path: /var/run/docker.sock
          name: dockersocket
        - hostPath:
            path: /proc
          name: procdir
        - hostPath:
            path: /sys/fs/cgroup
          name: cgroups

ここでの kind: DaemonSet と呼ばれるものは、すべての Node にこの pod を配置するという記述になります。
この方法で、log 収集系の pod を合わせて配置すると便利になります。

$ kubectl create -f kubernetes/dd-agent.yaml 
daemonset "dd-agent" created

(演習課題)綺麗なAPI速習会で使ったgo-api をkubernetes に出してみよう

https://github.com/shimastripe/go-api-sokushukai から fork して manifest file を書いてkubernetes に出してよう!!

image は以下の方法で取得できます。

docker pull quay.io/shimastripe/go-api-sokushukai

元に kubernetes 立ててみよう

manifest を書く時にやっていること

  • kubectl create -f file で validate check させる
  • 他の人を見てみる
  • kubectl run でネットワークがつながっているか見てみる
$ kubectl run -i --tty --rm busybox --image=busybox --restart=Never --namespace=koudaiii -- sh
Waiting for pod sync/busybox to be running, status is Pending, pod ready: false


Hit enter for command prompt

/ # nslookup go
Server:    100.64.0.10
Address 1: 100.64.0.10

Name:      go
Address 1: 100.64.166.49
/ # exit
pod "busybox" deleted
  • kubectl exec pod name で実際に中で何が起こっているか見てみる
  • kubectl logs pod name でコンテナのログをみる
  • kubectl describe pod name でEventのログを見てみる

参考回答: https://github.com/dtan4/go-api-sokushukai/tree/master/kubernetes

(演習課題) blue-green deployment が出来るようにしてみよう!