Foreverly

メモ帳

aws-workshop-for-kubernetes(101〜103)

101-start-here

Kubernetes - Cloud9開発環境の設定

aws-workshop-for-kubernetesの101-start-here

Cloud9を使うためにバージニアリージョンを選び、CloudFormationで既存のVPCを選び サブネットを選択して、次へを押せば、 k8s-workshopというスタックができ、出力タブにCloud9のURLが表示され Cloud9に接続できるようになった。

Cloud9IDEで以下のスクリプトを実行。

aws s3 cp s3://aws-kubernetes-artifacts/lab-ide-build.sh . && \
chmod +x lab-ide-build.sh && \
. ./lab-ide-build.sh

これでkubectlコマンドなどがインストールされました。

$ kubectl get nodes
The connection to the server localhost:8080 was refused - did you specify the right host or port?

Cloud9 IDEが割り当てられたIAMインスタンスプロファイルを使用するためには、 「AWS Cloud9」メニューを開き、「Preferences」に移動し、「AWS Settings」に移動し、「AWS managed temporary credentials」を無効にする。

これで、ワークショップを続ける準備が整いました!

102-your-first-cluster

kopsを使ってKubernetesクラスタを作成する

複数のマスターノードとワーカーノードを複数の可用性ゾーンに分散して、可用性の高いクラスタを作成する。 クラスタ内のマスタノードとワーカーノードは、名前解決にDNSまたはWeave Meshの ゴシッププロトコルを使用できる。 ゴシッププロトコルベースの クラスタはセットアップが簡単で簡単で、ドメインサブドメイン、またはRoute53ホストゾーンを登録する必要がない。

ゴシッププロトコルを使用してクラスタを作成するには、接尾辞が .k8s.local になります。

シングルマスタークラスタとマルチマスタークラスタのどちらを作成するかを選択できるが、 ローリングアップデートを実演するなどもあるので、マルチマスタークラスタを作成するほうがよい。

マルチマスタークラスタ

次のコマンドでマルチマスター、マルチノード、およびmulti-az構成でクラスターを作成します。

$ kops create cluster \
  --name example.cluster.k8s.local \
  --master-count 3 \
  --node-count 5 \
  --zones $AWS_AVAILABILITY_ZONES \
  --yes

(中略)
kops has set your kubectl context to example.cluster.k8s.local

Cluster is starting.  It should be ready in a few minutes.

Suggestions:
 * validate cluster: kops validate cluster
 * list nodes: kubectl get nodes --show-labels
 * ssh to the master: ssh -i ~/.ssh/id_rsa admin@api.example.cluster.k8s.local
 * the admin user is specific to Debian. If not using Debian please use the appropriate user based on

--master-count オプションを使用し、マスターノードの数を指定することにより、マルチマスタークラスタを作成できます。奇数値を推奨します。 デフォルトでは、マスターノードは --zones オプションを使用して指定されたAZに分散されます。 --master-zones オプションを使用して、マスターノードのゾーンを明示的に指定することもできます。 --zones オプションは、ワーカーノードの配布にも使用されます。 --node-count オプションを使用して、ワーカーの数が指定されます。

クラスタkops validate cluster でします。

$ kops validate cluster
(中略)

Validation Failed

クラスタの作成には時間がかかりますね。ワーカーnodeがなく、Validation Failed になってました。 すべてのマスターが異なるAZにまたがっていることは確認できました。

20分ぐらいでワーカーノードもすべて起動しました。

Your cluster example.cluster.k8s.local is ready

Kubernetes Cluster Context

kubectl(Kubernetes CLI)を使用して、複数のKubernetesクラスタを管理できます。 各クラスタの構成は、「kubeconfigファイル」と呼ばれる構成ファイルに格納されています。 デフォルトでは、kubectl configはディレクトリ内で指定されたファイルを探します ~/.kube。 kubectl CLIは、kubeconfigファイルを使用して、クラスタを選択し、クラスタAPIサーバと通信するために必要な情報を検索します。

これでコンテキストを変更するだけで、さまざまな環境にアプリケーションを展開できる。 アプリケーション開発の典型的なフローは次のようになります。 

  1. 開発環境を使用してアプリケーションを構築する(おそらくラップトップにローカルに)
  2. コンテキストをAWS上に作成されたテストクラスタに変更する
  3. 同じコマンドを使用してテスト環境にデプロイする
  4. 一度満足すれば、コンテキストをAWSの実動クラスタに再度変更してください
  5. もう一度、同じコマンドを使用して運用環境に展開します

利用可能なコンテキストの概要を取得

$ kubectl config get-contexts
kubectl config get-contexts
CURRENT   NAME                        CLUSTER                     AUTHINFO                    NAMESPACE
*         example.cluster.k8s.local   example.cluster.k8s.local   example.cluster.k8s.local   

出力には、クラスタごとに1つずつ、kubectlで利用可能な別のコンテキストが表示されます。 NAME列にはコンテキスト名が表示され、* は現在のコンテキストを示す。

現在のコンテキストを表示

$ kubectl config current-context
example.cluster.k8s.local

複数のクラスタが存在する場合は、コンテキストを変更できます。

$ kubectl config use-context <config-name>

103-kubernetes-concepts

Kubernetesの概念

前提

クラスタが稼動しているので、Kubernetes CLIをkubectl(「キューブコントロール」)コマンドで探索することができる。 kubectl はクラスタ内のマスターノード上で動作するKubernetes API Serverと対話します。

プラットフォームとしてのKubernetesには、APIオブジェクトにマップされる多数の抽象的な要素があります。 これらのKubernetes APIオブジェクトは、実行中のアプリケーションやワークロード、コンテナイメージ、ネットワークリソースなどの情報を含む、 クラスタの望ましい状態を記述するために使用できます。 このセクションでは、最もよく使用されるKubernetes APIの概念と、それを介して相互作用する方法について説明する。

前提条件

さきほど作成した、3つのマスターノードと5つのワーカーノードを持つクラスタを使用します。 01-path-basics/103-kubernetes-concepts/templates に必要な設定ファイルが置いてあります。

$ cd 01-path-basics/103-kubernetes-concepts/templates

ノードを表示

$ kubectl get nodes

以下の様にクラスタが出力されます。

NAME                             STATUS    ROLES     AGE       VERSION
ip-172-20-109-34.ec2.internal    Ready     master    1h        v1.9.3
ip-172-20-122-230.ec2.internal   Ready     node      1h        v1.9.3
ip-172-20-136-202.ec2.internal   Ready     node      1h        v1.9.3
ip-172-20-165-37.ec2.internal    Ready     node      1h        v1.9.3
ip-172-20-41-45.ec2.internal     Ready     node      1h        v1.9.3
ip-172-20-61-47.ec2.internal     Ready     master    1h        v1.9.3
ip-172-20-79-150.ec2.internal    Ready     node      1h        v1.9.3
ip-172-20-86-242.ec2.internal    Ready     master    1h        v1.9.3
$ kubectl run nginx --image=nginx
deployment "nginx" created

Podの作成

Nginxコンテナをクラスタに作成する

$ kubectl run nginx --image=nginx
deployment "nginx" created

deploymentsのリストをみる

$ kubectl get deployments
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     1         1         1            1           14s

Runnningのpodsを表示

$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
nginx-8586cf59-bjsgp   1/1       Running   0          53s

以上でわかったpodの名前から、次のようにポッドの追加情報を取得します。

$ kubectl describe pod/nginx-8586cf59-bjsgp
Name:           nginx-8586cf59-bjsgp
Namespace:      default
Node:           ip-172-20-165-37.ec2.internal/172.20.165.37
Start Time:     Mon, 30 Apr 2018 14:52:42 +0000
Labels:         pod-template-hash=41427915
                run=nginx
Annotations:    kubernetes.io/limit-ranger=LimitRanger plugin set: cpu request for container nginx
Status:         Running
IP:             100.96.4.2
Controlled By:  ReplicaSet/nginx-8586cf59
Containers:
  nginx:
    Container ID:   docker://85d3dec6833b185416a9e45cc07f87437ff7ab21064a531a14a71b1f60b89a7c
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:0edf702c890e9518b95b2da01286509cd437eb994b8d22460e40d72f6b79be49
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Mon, 30 Apr 2018 14:52:47 +0000
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:        100m
    Environment:  <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-nqfr5 (ro)
Conditions:
  Type           Status
  Initialized    True 
  Ready          True 
  PodScheduled   True 
Volumes:
  default-token-nqfr5:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-nqfr5
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason                 Age   From                                    Message
  ----    ------                 ----  ----                                    -------
  Normal  Scheduled              2m    default-scheduler                       Successfully assigned nginx-8586cf59-bjsgp to ip-172-20-165-37.ec2.internal
  Normal  SuccessfulMountVolume  2m    kubelet, ip-172-20-165-37.ec2.internal  MountVolume.SetUp succeeded for volume "default-token-nqfr5"
  Normal  Pulling                2m    kubelet, ip-172-20-165-37.ec2.internal  pulling image "nginx"
  Normal  Pulled                 2m    kubelet, ip-172-20-165-37.ec2.internal  Successfully pulled image "nginx"
  Normal  Created                2m    kubelet, ip-172-20-165-37.ec2.internal  Created container
  Normal  Started                2m    kubelet, ip-172-20-165-37.ec2.internal  Started container
  ```

  デフォルトでは、ポッドはdefaultというnamespaceに作成されます。さらに、kube-systemというnamespaceはKubernetesのシステムのポッド用に予約されています。
  kube-systemという名前空間内のすべてのポッドのリストは、次のように表示できます。

  ```
  $ kubectl get pods --namespace kube-system
NAME                                                    READY     STATUS    RESTARTS   AGE
dns-controller-769b5f68b6-mcg59                         1/1       Running   0          1h
etcd-server-events-ip-172-20-109-34.ec2.internal        1/1       Running   1          1h
etcd-server-events-ip-172-20-61-47.ec2.internal         1/1       Running   0          1h
etcd-server-events-ip-172-20-86-242.ec2.internal        1/1       Running   0          1h
etcd-server-ip-172-20-109-34.ec2.internal               1/1       Running   0          1h
etcd-server-ip-172-20-61-47.ec2.internal                1/1       Running   0          1h
etcd-server-ip-172-20-86-242.ec2.internal               1/1       Running   0          1h
kube-apiserver-ip-172-20-109-34.ec2.internal            1/1       Running   0          1h
kube-apiserver-ip-172-20-61-47.ec2.internal             1/1       Running   0          1h
kube-apiserver-ip-172-20-86-242.ec2.internal            1/1       Running   1          1h
kube-controller-manager-ip-172-20-109-34.ec2.internal   1/1       Running   0          1h
kube-controller-manager-ip-172-20-61-47.ec2.internal    1/1       Running   0          1h
kube-controller-manager-ip-172-20-86-242.ec2.internal   1/1       Running   0          1h
kube-dns-7785f4d7dc-4vg2m                               3/3       Running   0          1h
kube-dns-7785f4d7dc-zt74c                               3/3       Running   0          1h
kube-dns-autoscaler-787d59df8f-mvt9d                    1/1       Running   0          1h
kube-proxy-ip-172-20-109-34.ec2.internal                1/1       Running   0          1h
kube-proxy-ip-172-20-122-230.ec2.internal               1/1       Running   0          1h
kube-proxy-ip-172-20-136-202.ec2.internal               1/1       Running   0          1h
kube-proxy-ip-172-20-165-37.ec2.internal                1/1       Running   0          1h
kube-proxy-ip-172-20-41-45.ec2.internal                 1/1       Running   0          1h
kube-proxy-ip-172-20-61-47.ec2.internal                 1/1       Running   0          1h
kube-proxy-ip-172-20-79-150.ec2.internal                1/1       Running   0          1h
kube-proxy-ip-172-20-86-242.ec2.internal                1/1       Running   0          1h
kube-scheduler-ip-172-20-109-34.ec2.internal            1/1       Running   0          1h
kube-scheduler-ip-172-20-61-47.ec2.internal             1/1       Running   0          1h
kube-scheduler-ip-172-20-86-242.ec2.internal            1/1       Running   0          1h

ポッドからログを取得

ポッドからのログを取得することができます(新しいnginxにはログがありません - サービスにアクセスした後にもう一度確認)

kubectl logs <pod-name> --namespace <namespace-name>

kubectl logs nginx-8586cf59-bjsgp --namespace default

@# 実行中のポッドでシェルを実行

ポッド内のシェルへのTTYを開く

$ kubectl get pods
$ kubectl exec -it <pod-name> /bin/bash
$ kubectl get pods
NAME                   READY     STATUS    RESTARTS   AGE
nginx-8586cf59-bjsgp   1/1       Running   0          11m
$ kubectl exec -it nginx-8586cf59-bjsgp /bin/bash
root@nginx-8586cf59-bjsgp:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

これでbashシェルが開き、コンテナのファイルシステムを見ることができる

掃除

これまでに作成されたKubernetesリソースをすべて削除

$ kubectl delete deployment/nginx
deployment.extensions "nginx" deleted

ポッド

ポッドは、作成、スケジュール設定、および管理が可能な最小の展開可能なユニットです。 これは、アプリケーションに属するコンテナの論理的な集合です。 ポッドは名前空間に作成されます。 ポッド内のすべてのコンテナは、名前空間、ボリューム、およびネットワーキングスタックを共有します。 これにより、localhostを使ってポッド内のコンテナ同士が互いを「見つける」ことができる。

ポッドの作成

Kubernetesの各リソースは、構成ファイルを使用して定義できます。たとえば、Nginxポッドは次のような設定ファイルで定義できます。

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:

次のようにポッドを作成します。

$ kubectl apply -f pod.yaml
pod "nginx-pod" created

ポッドのリストを取得

$ kubectl get pods
NAME        READY     STATUS    RESTARTS   AGE
nginx-pod   1/1       Running   0          22s

ポッドが正常に起動したことを確認します(ポート8080で実行中のものがないことを確認してください)。

kubectl -n default port-forward $(kubectl -n default get pod -l name=nginx-pod -o jsonpath='{.items[0].metadata.name}') 8080:80

Cloud9 IDEでプレビューと実行中のアプリケーションのプレビューをクリック。プレビュータブが開き、NGINXのメインページが表示されるのを確認。

ポッド内のコンテナがログを生成する場合は、次のコマンドを使用して表示できます。

$kubectl logs nginx-pod
127.0.0.1 - - [30/Apr/2018:15:14:10 +0000] "GET / HTTP/1.1" 200 612 "https://us-east-1.console.aws.amazon.com/cloud9/ide/a776f598fd54453fb848b088e8bce324?region=us-east-1" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" "49.129.246.233"
ec2-user:~/environment/aws-workshop-for-kubernetes/01-path-basics/103-kubernetes-concepts/templates (master) $ 

もしくは

$ kubectl logs nginx-pod  --namespace default                                                                                                                                                               
127.0.0.1 - - [30/Apr/2018:15:14:10 +0000] "GET / HTTP/1.1" 200 612 "https://us-east-1.console.aws.amazon.com/cloud9/ide/a776f598fd54453fb848b088e8bce324?region=us-east-1" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36" "49.129.246.233"
ec2-user:~/environment/aws-workshop-for-kubernetes/01-path-basics/103-kubernetes-concepts/templates (master) $ 

メモリおよびCPUリソースリクエス

ポッド内のコンテナには、メモリとCPUのリクエストと制限を割り当てることができる。 リクエストは、Kubernetesがコンテナに与えるメモリ/CPUの最小量で、 制限は、コンテナが使用できるメモリ/CPUの最大量。 Podのメモリ/CPUリクエストおよび制限は、Pod内のすべてのコンテナのメモリ/CPUリクエストおよび制限の合計。 要求が指定されていない場合、デフォルトに制限されます。制限のデフォルト値はノードの容量。

PodのメモリとCPUリクエストが満たされている場合、ノード上でPodをスケジュールすることができる スケジューリングにはメモリとCPUの制限は考慮されていない。

ポッド内のコンテナがメモリ要求を超えていない場合、ノードはノード上で操作を継続できる。 ポッド内のコンテナがメモリ要求を超えると、ノードがメモリ不足になるたびに追い出し対象になる。 ポッド内のコンテナがメモリの制限を超えると、それらは終了する。 Podを再起動できる場合、他のタイプのランタイムエラーと同様に、kubeletはそれを再起動する。 コンテナは、長時間にわたってCPU制限を超えてもしなくてもよい。 しかし、過度の使用のために殺されることはない

メモリとCPUのリクエスト/制限は、以下を使用して指定可能。

タイプ フィールド
メモリリクエス spec.containers.resources.requests.memory
メモリ制限 spec.containers.resources.limits.memory
CPU要求 spec.containers.resources.requests.cpu
CPU制限 spec.containers.resources.limits.cpu

メモリリソースはバイト単位で要求される。 接尾辞の一つで整数または小数でそれらを指定することができ E,P,T,G,M,K。 また、電源の-2当量で表すことができEi,Pi,Ti,Gi,Mi,Ki

CPUはCPU単位で要求できます。1 cpuユニットは同等の1 AWS vCPU。それはまた、0.5やる500メートルなどのmillicpuを分数や小数単位で要求することができる。

デフォルトメモリとCPU

デフォルトでは、ポッド内のコンテナにはメモリ要求/制限と100m CPU要求が割り当てられず、制限はない。これは、以前に開始したポッドを使用して確認できる。

$ kubectl get pod/nginx-pod -o jsonpath={.spec.containers[].resources}
map[requests:map[cpu:100m]]

メモリとCPUを割り当てる

以下の設定ファイルを使用して、Podにメモリ要求と制限を割り当てる。

$ cat pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod2
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      limits:
        memory: "200Mi"
        cpu: 2
      requests:
        memory: "100Mi"
        cpu: 1
    ports:
    - containerPort: 80

この設定ファイルの唯一の変更はspec.containers[].resourcesセクションの追加です。 制限はlimitsセクションで指定され、要求はrequestsセクションで指定される。

Podを作成

kubectl apply -f pod-resources.yaml
pod "nginx-pod2" created

リクエストと制限の詳細を取得。

$ kubectl get pod/nginx-pod2 -o jsonpath={.spec.containers[].resources}
map[limits:map[cpu:2 memory:200Mi] requests:map[cpu:1 memory:100Mi]]

NGINXコンテナは、メモリとCPUがかなり少なくて済みます。 したがって、これらの要求数と制限数は正常に機能し、ポッドは正しく開始されます。今 度は、同様の番号を使ってWildFlyポッドを開始しようとしましょう。 同じ設定ファイルが表示されます。

$ cat pod-resources1.yaml
apiVersion: v1
kind: Pod
metadata:
  name: wildfly-pod
  labels:
    name: wildfly-pod
spec:
  containers:
  - name: wildfly
    image: jboss/wildfly:11.0.0.Final
    resources:
      limits:
        memory: "200Mi"
        cpu: 2
      requests:
        memory: "100Mi"
        cpu: 1
    ports:
    - containerPort: 8080

このポッド内のWildFlyコンテナに割り当てられるメモリの最大量は200MBに制限されています。 このポッドを作成してみよう。

$ kubectl apply -f pod-resources1.yaml
pod "wildfly-pod" created

ポッドの状態を確認。

$ kubectl get pods -w
NAME          READY     STATUS              RESTARTS   AGE
wildfly-pod   0/1       ContainerCreating   0          5s
wildfly-pod   1/1       Running   0         26s
wildfly-pod   0/1       OOMKilled   0         29s
wildfly-pod   1/1       Running   1         31s
wildfly-pod   0/1       OOMKilled   1         34s
wildfly-pod   0/1       CrashLoopBackOff   1         45s
wildfly-pod   1/1       Running   2         46s
wildfly-pod   0/1       OOMKilled   2         49s
wildfly-pod   0/1       CrashLoopBackOff   2         1m
wildfly-pod   1/1       Running   3         1m
wildfly-pod   0/1       OOMKilled   3         1m

OOMKilledでコンテナがメモリ不足で終了したことを示します。 pod-resources2.yamlspec.containers[].resources.limits.memoryの値を300Miに変更します。 既存のPodを削除し、新しいPodを作成します。

$ kubectl delete -f pod-resources1.yaml
pod "wildfly-pod" deleted
$ kubectl apply -f pod-resources2.yaml
pod "wildfly-pod" created
$ kubectl get -w pod/wildfly-pod
NAME          READY     STATUS              RESTARTS   AGE
wildfly-pod   0/1       ContainerCreating   0          3s
wildfly-pod   1/1       Running   0         25s

これで、Podが正常に開始されます。 Podに割り当てられたリソースの詳細を取得。

$ kubectl get pod/wildfly-pod -o jsonpath={.spec.containers[].resources}
map[limits:map[cpu:2 memory:300Mi] requests:map[cpu:1 memory:100Mi]

Quality of service

Kubernetesは、コンテナによって使用されていない場合、要求と制限の差を便宜的に排除します。 これにより、Kubernetesがノードをオーバーサブスクライブできるようになり、使用率が向上すると同時に、保証が必要なコンテナのリソース保証が維持されます。

Kubernetesは、QoSクラスの1つをPodに割り当てます。

  1. Guaranteed
  2. Burstable
  3. BestEffort

QoSクラスは、KubernetesによってPodのスケジュール設定と撤回に使用されます。

ポッド内のすべてのコンテナにメモリとCPUの制限が与えられ、オプションで非ゼロの要求が与えられ、それらが完全に一致すると、PodはGuaranteedQoS でスケジュールされます。これが最優先事項です。

ポッドがBurstableQoSを満たさずGuaranteed、少なくとも1つのコンテナにメモリまたはCPU要求がある場合、ポッドにはQoSクラスが与えられます。これは中間的な優先事項です。

ポッド内の任意のコンテナにメモリとCPUの要求または制限が割り当てられていない場合、ポッドはBestEffortQoS でスケジュールされます。これが最も低く、デフォルトの優先順位です。

起床が必要なポッドは、GuaranteedQoS を要求できます。あまり厳しくない要件のポッドは、より弱いQoSを使用することも、QoSを使用しないこともあります。

Guaranteed

GuaranteedQoS を備えたPodの例。

$ cat pod-guaranteed.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod-guaranteed
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      limits:
        memory: "200Mi"
        cpu: 1
    ports:
    - containerPort: 80

ここでは要求値は指定されておらず、デフォルト値はlimitになる。

このポッドを作成

$ kubectl apply -f pod-guaranteed.yaml
pod "nginx-pod-guaranteed" created

リソースを確認

$ kubectl get pod/nginx-pod-guaranteed -o jsonpath={.spec.containers[].resources}
map[limits:map[cpu:1 memory:200Mi] requests:map[cpu:1 memory:200Mi]]

QoSを確認

$ kubectl get pod/nginx-pod-guaranteed -o jsonpath={.status.qosClass}
Guaranteed

制限と要求の明示的な値を持つ別のポッドが表示さ

$ cat pod-guaranteed2.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod-guaranteed2
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      limits:
        memory: "200Mi"
        cpu: 1
      requests:
        memory: "200Mi"
        cpu: 1
    ports:
    - containerPort: 80

このポッドを作成

$ kubectl apply -f pod-guaranteed2.yaml
pod "nginx-pod-guaranteed2" created

リソースを確認

$ kubectl get pod/nginx-pod-guaranteed2 -o jsonpath={.spec.containers[].resources}
map[limits:map[cpu:1 memory:200Mi] requests:map[cpu:1 memory:200Mi]]

QoSを確認

$ kubectl get pod/nginx-pod-guaranteed2 -o jsonpath={.status.qosClass}
Guaranteed

Burstable

次は、Burstable QoS を備えたPodの例

$ cat pod-burstable.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod-burstable
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      limits:
        memory: "200Mi"
        cpu: 1
      requests:
        memory: "100Mi"
        cpu: 1
    ports:
    - containerPort: 80

ここでは、リクエスト値と制限値の両方を指定しました。

このポッドを作成。

$ kubectl apply -f pod-burstable.yaml
pod "nginx-pod-burstable" created

リソースを確認

$ kubectl get pod/nginx-pod-burstable -o jsonpath={.spec.containers[].resources}
map[limits:map[cpu:1 memory:200Mi] requests:map[cpu:1 memory:100Mi]]

QoSを確認

$ kubectl get pod/nginx-pod-burstable -o jsonpath={.status.qosClass}
Burstable

BestEffort

リソースを確認

$ kubectl get pod/nginx-pod -o jsonpath={.spec.containers[].resources}
map[requests:map[cpu:100m]]

QoSを確認

$ kubectl get pod/nginx-pod -o jsonpath={.status.qosClass}
Burstable

これは BestEffort になるべき kubernetes#55278

ポッドの削除

実行中のすべてのポッドを取得

$ kubectl get pods
NAME                    READY     STATUS    RESTARTS   AGE
nginx-pod               1/1       Running   0          6m
nginx-pod-burstable     1/1       Running   0          9m
nginx-pod-guaranteed    1/1       Running   0          23m
nginx-pod-guaranteed2   1/1       Running   0          12m
nginx-pod2              1/1       Running   0          6m
wildfly-pod             1/1       Running   0          6m

ポッドを削除

$ kubectl delete $(kubectl get pods -o=name)
pod "nginx-pod" deleted
pod "nginx-pod-burstable" deleted
pod "nginx-pod-guaranteed" deleted
pod "nginx-pod-guaranteed2" deleted
pod "nginx-pod2" deleted
pod "wildfly-pod" deleted
$ kubectl get pods
No resources found.

Deployment

ポッドの4つのレプリカなどの「望ましい状態」は、Deploymentオブジェクトに記述することができます。 Kubernetesクラスタ内のDeploymentコントローラは、望まれた状態と実際の状態が一致していることを確認します。 ワーカーノードに障害が発生したり再起動したりすると、Deploymentによってポッドが再作成されます。 ポッドが死ぬと、新しいポッドが開始されて、望ましい状態とと実際の状態のマッチが確実に行われます。 また、レプリカ数のアップスケーリングとダウンスケーリングが可能です。 これは、ReplicaSetを使用して実現されます。 Deploymentはレプリカセットを管理し、それらのポッドに更新を提供します。

Deploymentの作成

次の例では、NGINXベースイメージの3つのレプリカでDeploymentを作成する。 以下のテンプレートを確認して、試してみる。

 $ cat deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment # kubernetes object type
metadata:
  name: nginx-deployment # deployment name
spec:
  replicas: 3 # number of replicas
  template:
    metadata:
      labels:
        app: nginx # pod labels
    spec:
      containers:
      - name: nginx # container name
        image: nginx:1.12.1 # nginx image
        imagePullPolicy: IfNotPresent # if exists, will not pull new image
        ports: # container and host port assignments
        - containerPort: 80
        - containerPort: 443

このDeploymentでは、NGINXイメージのインスタンスが3つ作成されます。 次のコマンドを実行してDeploymentを作成

$ kubectl create -f deployment.yaml --record
deployment "nginx-deployment" created

この--recordフラグは、各リビジョンの変更を追跡します。 deploymentのdeployment状況を監視するには:

$ kubectl rollout status deployment/nginx-deployment
deployment "nginx-deployment" successfully rolled out

デプロイメントはレプリカの数を管理するレプリカセットを作成します。 既存のデプロイメントとレプリカセットを見てみましょう。 デプロイメント情報を確認

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   3         3         3            3           25s

deployment用のレプリカセットを取得

$ kubectl get replicaset
NAME                          DESIRED   CURRENT   READY     AGE
nginx-deployment-3441592026   3         3         3         1m

実行中のポッドのリストを取得

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-3441592026-ddpf0   1/1       Running   0          2m
nginx-deployment-3441592026-kkp8h   1/1       Running   0          2m
nginx-deployment-3441592026-lx304   1/1       Running   0          2m

デプロイメントのスケーリング

Deploymentのレプリカ数は、次のコマンドを使用してスケーリングできる。

$ kubectl scale --replicas=5 deployment/nginx-deployment
deployment "nginx-deployment" scaled

deploymentを確認

$ kubectl get deployments
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   5         5         5            5           2m

deployment内のポッドを確認

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
nginx-deployment-3441592026-36957   1/1       Running   0          44s
nginx-deployment-3441592026-8wch5   1/1       Running   0          44s
nginx-deployment-3441592026-ddpf0   1/1       Running   0          3m
nginx-deployment-3441592026-kkp8h   1/1       Running   0          3m
nginx-deployment-3441592026-lx304   1/1       Running   0          3m

デプロイメントのアップデート

Podの仕様を編集することで、Deploymentのより一般的なアップデートを行うことができます。 この例では、最新のnginxイメージに変更しましょう。

最初に、次のように入力してテキストエディタを開きます。

$ kubectl edit deployment/nginx-deployment

次に、imageをnginx:1.12.1からnginx:latestに変更します。

これにより、展開のロール更新が実行されます。 リビジョン、イメージのバージョン、ポートなどの展開の詳細を追跡するには、次のように入力します。

$ kubectl describe deployments
Name:                   nginx-deployment
Namespace:              default
CreationTimestamp:      Mon, 23 Oct 2017 09:14:36 -0400
Labels:                 app=nginx
Annotations:            deployment.kubernetes.io/revision=2
                        kubernetes.io/change-cause=kubectl edit deployment/nginx-deployment
Selector:               app=nginx
Replicas:               5 desired | 5 updated | 5 total | 5 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:  app=nginx
  Containers:
   nginx:
    Image:        nginx:latest
    Ports:        80/TCP, 443/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-deployment-886641336 (5/5 replicas created)
Events:
  Type    Reason             Age                From                   Message
  ----    ------             ----               ----                   -------
  Normal  ScalingReplicaSet  4m                 deployment-controller  Scaled up replica set nginx-deployment-3441592026 to 3
  Normal  ScalingReplicaSet  1m                 deployment-controller  Scaled up replica set nginx-deployment-3441592026 to 5
  Normal  ScalingReplicaSet  32s                deployment-controller  Scaled up replica set nginx-deployment-886641336 to 1
  Normal  ScalingReplicaSet  32s                deployment-controller  Scaled down replica set nginx-deployment-3441592026 to 4
  Normal  ScalingReplicaSet  32s                deployment-controller  Scaled up replica set nginx-deployment-886641336 to 2
  Normal  ScalingReplicaSet  29s                deployment-controller  Scaled down replica set nginx-deployment-3441592026 to 3
  Normal  ScalingReplicaSet  29s                deployment-controller  Scaled up replica set nginx-deployment-886641336 to 3
  Normal  ScalingReplicaSet  28s                deployment-controller  Scaled down replica set nginx-deployment-3441592026 to 2
  Normal  ScalingReplicaSet  28s                deployment-controller  Scaled up replica set nginx-deployment-886641336 to 4
  Normal  ScalingReplicaSet  25s (x3 over 26s)  deployment-controller  (combined from similar events): Scaled down replica set nginx-deployment-3441592026 to 0

デプロイメントのロールバック

以前のバージョンにロールバックするには、まず更新履歴を確認します。

$ kubectl rollout history deployment/nginx-deployment
deployments "nginx-deployment"
REVISION  CHANGE-CAUSE
1         kubectl scale deployment/nginx-deployment --replicas=5
2         kubectl edit deployment/nginx-deployment

前のリビジョンにのみロールバックする場合は、次のコマンドを入力します。

$ kubectl rollout undo deployment/nginx-deployment
deployment "nginx-deployment" rolled back

Deploymentをロールバックしてnginx:1.12.1イメージを使用します。イメージ名を確認してください

$ kubectl describe deployments | grep Image
   Image:        nginx:1.12.1

特定のリビジョンにロールバックする場合は、次のように入力します。

$ kubectl rollout undo deployment/nginx-deployment --to-revision=<version>

デプロイメントの削除

$ kubectl delete -f deployment.yaml
deployment "nginx-deployment" deleted

Service

ポッドは一時的です。各ポッドには一意のIPアドレスが割り当てられます。 複製コントローラに属するポッドが消滅した場合は、再作成され、異なるIPアドレスが与えられます。 さらに、DeploymentまたはReplica Setを使用して追加のポッドを作成することもできます。 これにより、WildFlyなどのアプリケーションサーバーが、そのIPアドレスを使用してMySQLなどのデータベースにアクセスすることが困難になります。

サービスとは、論理的なポッドセットとそのポッドにアクセスするためのポリシーを定義する抽象化です。 サービスに割り当てられたIPアドレスは時間の経過とともに変化しないため、他のポッドに依存することができます。 通常、サービスに属するポッドは、ラベルセレクタによって定義されます。 これは、ポッドがレプリカセットにどのように属しているかと同様のメカニズムです。

ラベルを使用してポッドを選択するというこの抽象化は、疎結合を可能にする。 デプロイメント内のポッドの数は拡大または縮小されますが、アプリケーションサーバーはそのサービスを使用して引き続きデータベースにアクセスできます。

Kubernetesサービスは、論理的なポッドセットを定義し、マイクロサービスを介してそれらにアクセスできるようにします。

ServiceのDeploymentを作成

Podは、ラベルがPodに接続されている疎結合モデルを使用してServiceに属し、Serviceはそれらのラベルを使用してPodを選択します。

最初にDeploymentを作成して、ポッドの3つのレプリカを作成しましょう。

 $ cat echo-deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: echo-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: echo-pod
    spec:
      containers:
      - name: echoheaders
        image: gcr.io/google_containers/echoserver:1.4
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 8080

この例では、Elastic Load BalancerのHTTPヘッダーで応答するechoアプリケーションを作成します。 デプロイメントを作成するには、次のように入力します。

$ kubectl create -f echo-deployment.yaml --record

kubectl describe deployment コマンドを使って echo-appがデプロイされたことを確認する

  $ kubectl describe deployment
Name:                   echo-deployment
Namespace:              default
CreationTimestamp:      Mon, 23 Oct 2017 10:07:47 -0400
Labels:                 app=echo-pod
Annotations:            deployment.kubernetes.io/revision=1
                        kubernetes.io/change-cause=kubectl create --filename=templates/echo-deployment.yaml --record=true
Selector:               app=echo-pod
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:  app=echo-pod
  Containers:
   echoheaders:
    Image:        gcr.io/google_containers/echoserver:1.4
    Port:         8080/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
OldReplicaSets:  <none>
NewReplicaSet:   echo-deployment-3396249933 (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  10s   deployment-controller  Scaled up replica set echo-deployment-3396249933 to 3

Podのリストを取得

$ kubectl get pods
NAME                               READY     STATUS    RESTARTS   AGE
echo-deployment-3396249933-8slzp   1/1       Running   0          1m
echo-deployment-3396249933-bjwqj   1/1       Running   0          1m
echo-deployment-3396249933-r05nr   1/1       Running   0          1m

Podのラベルを確認

$ kubectl describe pods/echo-deployment-3396249933-8slzp | grep Label
Labels:         app=echo-pod

Serviceの作成

次の例では、echo-serviceサービスを作成

$ cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo-service
spec:
  selector:
    app: echo-pod
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer

サービスの対象となるポッドのセットは、サービスにapp: echo-podと添付されているラベルによって決定されます。 また、コンテナ上の8080のターゲットポートへのインバウンドポート80を定義します。

Kubernetesは、TCPプロトコルUDPプロトコルの両方をサポートしています。

Serviceを公開する

type属性を使用して外部IPにサービスを公開することができます。この属性は、次のいずれかの値をとります。

  1. ClusterIP: クラスタ内のIPアドレスで公開されるサービス。これがデフォルト動作
  2. NodePort: 定義されたポートで各ノードのIPアドレスに公開されるサービス。
  3. LoadBalancer: クラウドにデプロイされている場合は、クラウド固有のロードバランサを使用して外部に公開される。
  4. ExternalName: externalNameフィールドにサービスが添付されています。値を持つCNAMEにマップされます。

ロードバランサのserviceを公開してサービスを公開し、LoadBalancerのtypeフィールドを追加してみましょう

このテンプレートはElastic Load Balancer(ELB)上にecho-appサービスを公​​開します。

$ cat service.yaml
apiVersion: v1
kind: Service
metadata:
  name: echo-service
spec:
  selector:
    app: echo-pod
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: 8080
  type: LoadBalancer

次のコマンドを実行してサービスを作成します。

$ kubectl create -f service.yaml --record

サービスの詳細を取得

$ kubectl get service
NAME           TYPE           CLUSTER-IP       EXTERNAL-IP        PORT(S)        AGE
echo-service   LoadBalancer   100.66.161.199   ad0b47976b7fe...   80:30125/TCP   40s
kubernetes     ClusterIP      100.64.0.1       <none>             443/TCP        1h
$ kubectl describe service echo-service
Name:                     echo-service
Namespace:                default
Labels:                   <none>
Annotations:              kubernetes.io/change-cause=kubectl create --filename=templates/service.yaml --record=true
Selector:                 app=echo-pod
Type:                     LoadBalancer
IP:                       100.66.161.199
LoadBalancer Ingress:     ad0b47976b7fe11e7a8870e55a29a6a9-1770422890.us-east-1.elb.amazonaws.com
Port:                     http  80/TCP
TargetPort:               8080/TCP
NodePort:                 http  30125/TCP
Endpoints:                100.96.3.8:8080,100.96.4.9:8080,100.96.5.9:8080
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age   From                Message
  ----    ------                ----  ----                -------
  Normal  CreatingLoadBalancer  58s   service-controller  Creating load balancer
  Normal  CreatedLoadBalancer   56s   service-controller  Created load balancer

出力はLoadBalancer IngressをElastic Load Balancer(ELB)のアドレスとして表示されます。 ELBがプロビジョニングされ、利用可能になるまでには約2〜3分かかるので数分待ってからサービスにアクセスするとよい。

$ curl http://ad0b47976b7fe11e7a8870e55a29a6a9-1770422890.us-east-1.elb.amazonaws.com
CLIENT VALUES:
client_address=172.20.45.253
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://ad0b47976b7fe11e7a8870e55a29a6a9-1770422890.us-east-1.elb.amazonaws.com:8080/

SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001

HEADERS RECEIVED:
accept=*/*
host=ad0b47976b7fe11e7a8870e55a29a6a9-1770422890.us-east-1.elb.amazonaws.com
user-agent=curl/7.51.0
BODY:
-no body in request-

出力に示されているclient_addressの値に注意してください。 これは、リクエストを処理するポッドのIPアドレスです。 このコマンドを複数回呼び出すと、この属性に異なる値が表示されます。

これで、deploymentのポッドの数を増減できます。 または、ポッドが終了して別のホストで再起動することがあります。 しかし、Serviceは、ポッドに付いているラベルがServiceによって使用されているため、それらのポッドを対象とすることができます。

サービスの削除

サービスを削除するには、次のコマンドを実行します。

$ kubectl delete -f service.yaml

バックエンドの展開も明示的に削除する必要があります。

$ kubectl delete -f echo-deployment.yaml

デーモンセット

デーモンセットは、Podのコピーが選択されたノードセットで実行されるようにします。 デフォルトでは、クラスタ内のすべてのノードが選択されます。 選択基準を指定して、限られた数のノードを選択することができる。

新しいノードがクラスタに追加されると、ポッドがクラスタ上で開始されます。 ノードが削除されると、ガベージコレクションによってポッドが削除されます。

デーモンセットを作成する

次は、Prometheusコンテナを実行するDaemonSetの例です。テンプレートから始めましょう。

$ cat daemonset.yaml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: prometheus-daemonset
spec:
  template:
    metadata:
      labels:
        tier: monitoring
        name: prometheus-exporter
    spec:
      containers:
      - name: prometheus
        image: prom/node-exporter
        ports:
        - containerPort: 80

次のコマンドを実行することでReplicaSetとpodsが作成されます。

$ kubectl create -f daemonset.yaml --record

--recordフラグは各リビジョンの変更を追跡します。

$ kubectl get daemonsets/prometheus-daemonset
NAME                   DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
prometheus-daemonset   5         5         5         5            5           <none>          7s

DaemonSetの詳細を取得

$ kubectl describe daemonset/prometheus-daemonset
Name:           prometheus-daemonset
Selector:       name=prometheus-exporter,tier=monitoring
Node-Selector:  <none>
Labels:         name=prometheus-exporter
                tier=monitoring
Annotations:    kubernetes.io/change-cause=kubectl create --filename=templates/daemonset.yaml --record=true
Desired Number of Nodes Scheduled: 5
Current Number of Nodes Scheduled: 5
Number of Nodes Scheduled with Up-to-date Pods: 5
Number of Nodes Scheduled with Available Pods: 5
Number of Nodes Misscheduled: 0
Pods Status:  5 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  name=prometheus-exporter
           tier=monitoring
  Containers:
   prometheus:
    Image:        prom/node-exporter
    Port:         80/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From        Message
  ----    ------            ----  ----        -------
  Normal  SuccessfulCreate  28s   daemon-set  Created pod: prometheus-daemonset-pzfl8
  Normal  SuccessfulCreate  28s   daemon-set  Created pod: prometheus-daemonset-sjcgh
  Normal  SuccessfulCreate  28s   daemon-set  Created pod: prometheus-daemonset-ctrg4
  Normal  SuccessfulCreate  28s   daemon-set  Created pod: prometheus-daemonset-rxg79
  Normal  SuccessfulCreate  28s   daemon-set  Created pod: prometheus-daemonset-cnbkh

DaemonSetでポッドを取得

$ kubectl get pods -lname=prometheus-exporter
NAME                         READY     STATUS    RESTARTS   AGE
prometheus-daemonset-cnbkh   1/1       Running   0          57s
prometheus-daemonset-ctrg4   1/1       Running   0          57s
prometheus-daemonset-pzfl8   1/1       Running   0          57s
prometheus-daemonset-rxg79   1/1       Running   0          57s
prometheus-daemonset-sjcgh   1/1       Running   0          57s

特定のノードにDaemonSetsを制限する

Prometheusポッドがクラスタノードに正常に展開されたことを確認します。

$ kubectl get pods -o wide
NAME                         READY     STATUS    RESTARTS   AGE       IP           NODE
prometheus-daemonset-drqj8   1/1       Running   0          3m        100.96.3.9   ip-172-20-99-142.ec2.internal
prometheus-daemonset-l747p   1/1       Running   0          3m        100.96.4.7   ip-172-20-74-231.ec2.internal
prometheus-daemonset-vbd85   1/1       Running   0          3m        100.96.5.8   ip-172-20-185-78.ec2.internal
prometheus-daemonset-xr67t   1/1       Running   0          3m        100.96.6.8   ip-172-20-150-149.ec2.internal
prometheus-daemonset-xsdlp   1/1       Running   0          3m        100.96.7.7   ip-172-20-37-172.ec2.internal

ノードラベルの1つを次のように変更します。

$ kubectl label node ip-172-20-74-231.ec2.internal app=prometheus-node
node "ip-172-20-74-231.ec2.internal" labeled

次に、次のコマンドでDaemonSetテンプレートを編集します。

$ kubectl edit ds/prometheus-daemonset

spec.template.spec の中に変更したラベルに一致するnodeSelectorを含めるように変更。

      nodeSelector:
        app: prometheus-node

更新が実行された後、Prometheusを特定のノードで実行するように設定しました。

$ kubectl get ds/prometheus-daemonset
NAME                   DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR         AGE
prometheus-daemonset   1         1         1         0            1           app=prometheus-node   2m

デーモンセットを削除する

DaemonSetを削除するには、次のコマンドを実行します。

$ kubectl delete -f daemonset.yaml

ジョブ

ジョブは1つまたは複数のポッドを作成し、指定された数のポッドが正常に完了するようにします。ジョブは、ポッドの正常終了を追跡します。 指定した数のポッドが正常に完了すると、ジョブ自体は完了です。 ハードウェア障害のためにポッドに障害が発生したり削除されたりすると、ジョブは新しいポッドを開始します。 指定した数のポッドが正常に完了すると、ジョブが完了したことを意味します。

これは、特定の数のポッドが常に実行されていることを保証するレプリカセットまたはデプロイメントとは異なります。 したがって、レプリカセットまたは配備内のポッドが終了すると、そのポッドは再び再開されます。これにより、レプリカセットまたはデプロイメントは長期実行プロセスとなります。 これは、NGINXなどのWebサーバーに適しています。ただし、指定した数のポッドが正常に完了すると、ジョブは完了します。 これは、一度だけ実行する必要のあるタスクに適しています。例えば、ジョブは画像フォーマットを別のものに変換することができる。 レプリケーションコントローラでこのポッドを再起動すると、重複した作業が発生するだけでなく、場合によっては有害な可能性があります。

ジョブはレプリカセットを補完します。 レプリカセットは、終了する予定のないポッド(Webサーバーなど)を管理し、ジョブは終了すると予想されるポッド(バッチジョブなど)を管理します。

仕事を持つポッドにのみ適しているRestartPolicyに等しいですOnFailureかNever。

非並列ジョブ

ポッドに障害が発生しない限り、1ジョブにつき1つのポッドしか開始されません。 ポッドが正常に終了するとすぐにジョブが完了します。

これがジョブの仕様です。

 $ cat job.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: wait
spec:
  template:
    metadata:
      name: wait
    spec:
      containers:
      - name: wait
        image: ubuntu
        command: ["sleep",  "20"]
      restartPolicy: Never

Ubuntuのコンテナを作成し、20秒間スリープ状態になります。 次のコマンドを使用してジョブを作成します。

$ kubectl apply -f job.yaml
job "wait" created

ジョブを見てみます。

$ kubectl get jobs
NAME      DESIRED   SUCCESSFUL   AGE
wait      1         0            0s

出力はジョブがまだ成功していないことを示しています。 ポッドの状態を確認して確認します。

$ kubectl get -w pods
NAME         READY     STATUS    RESTARTS   AGE
wait-lk49x   1/1       Running   0          7s
wait-lk49x   0/1       Completed   0         24s

まず、ジョブのポッドが実行中であることを示します。 ポッドは数秒後に正常に終了し、Completedステータスが表示されます。

ジョブの状態をもう一度見てみましょう。

$ kubectl get jobs
NAME      DESIRED   SUCCESSFUL   AGE
wait      1         1            1m

出力はジョブが正常に実行されたことを示します。

完了したポッドはkubectl get podsコマンドに表示されません。 代わりに、次のように追加のオプションを渡すことで表示できます。

$ kubectl get pods --show-all
NAME         READY     STATUS      RESTARTS   AGE
wait-lk49x   0/1       Completed   0          1m

ジョブを削除するには、このコマンドを実行します

$ kubectl delete -f job.yaml

パラレルジョブ

非並列ジョブはジョブごとに1つのポッドのみを実行します。 このAPIは、ジョブに対して複数のポッドを並行して実行するために使用されます。 完了させるポッドの数は設定ファイルの.spec.completions属性によって定義されます。 並行して実行するポッドの数は、構成ファイルの.spec.parallelism属性によって定義されます。 これらの属性の両方のデフォルト値は1です。

範囲 1〜.spec.completionsの各値に対して成功したポッドが1つある場合、ジョブは完了です。 そのため、固定完了カウントジョブとも呼ばれます。

ジョブの仕様は以下。

 $ cat job-parallel.yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: wait
spec:
  completions: 6
  parallelism: 2
  template:
    metadata:
      name: wait
    spec:
      containers:
      - name: wait
        image: ubuntu
        command: ["sleep",  "20"]
      restartPolicy: Never

このジョブ仕様は、非並行ジョブ仕様に似ています。 これは、2つの新しい属性が追加されました .spec.completions.spec.parallelism。 つまり、6つのポッドが正常に完了すると、ジョブは完了します。 同時に最大2つのポッドが並行して実行されます。

次のコマンドを使用して並列ジョブを作成します。

$ kubectl apply -f job-parallel.yaml

次のようにジョブのステータスを確認します。

$ kubectl get -w jobs
NAME      DESIRED   SUCCESSFUL   AGE
wait      6         0            2s
wait      6         1         22s
wait      6         2         22s
wait      6         3         43s
wait      6         4         43s
wait      6         5         1m
wait      6         6         1m

出力は約20秒ごとに2つのポッドが作成されることを示しています。 別の端末ウィンドウで、作成されたポッドの状態を確認します。

$ kubectl get -w pods -l job-name=wait
NAME         READY     STATUS    RESTARTS   AGE
wait-f7kgb   1/1       Running   0          5s
wait-smp4t   1/1       Running   0          5s
wait-smp4t   0/1       Completed   0         22s
wait-jbdp7   0/1       Pending   0         0s
wait-jbdp7   0/1       Pending   0         0s
wait-jbdp7   0/1       ContainerCreating   0         0s
wait-f7kgb   0/1       Completed   0         22s
wait-r5v8n   0/1       Pending   0         0s
wait-r5v8n   0/1       Pending   0         0s
wait-r5v8n   0/1       ContainerCreating   0         0s
wait-r5v8n   1/1       Running   0         1s
wait-jbdp7   1/1       Running   0         1s
wait-r5v8n   0/1       Completed   0         21s
wait-ngrgl   0/1       Pending   0         0s
wait-ngrgl   0/1       Pending   0         0s
wait-ngrgl   0/1       ContainerCreating   0         0s
wait-jbdp7   0/1       Completed   0         21s
wait-6l22s   0/1       Pending   0         0s
wait-6l22s   0/1       Pending   0         0s
wait-6l22s   0/1       ContainerCreating   0         0s
wait-ngrgl   1/1       Running   0         1s
wait-6l22s   1/1       Running   0         1s
wait-ngrgl   0/1       Completed   0         21s
wait-6l22s   0/1       Completed   0         21s

すべてのポッドが完成したら、kubectl get podsは完成したポッドのリストは表示されません。 ポッドの一覧を表示するコマンドを以下に示します。

$ kubectl get pods -a
NAME         READY     STATUS      RESTARTS   AGE
wait-6l22s   0/1       Completed   0          1m
wait-f7kgb   0/1       Completed   0          2m
wait-jbdp7   0/1       Completed   0          2m
wait-ngrgl   0/1       Completed   0          1m
wait-r5v8n   0/1       Completed   0          2m
wait-smp4t   0/1       Completed   0          2m

同様に、kubectl get jobs完了後のジョブのステータスを示します。

$ kubectl get jobs
NAME      DESIRED   SUCCESSFUL   AGE
wait      6         6            3m

ジョブを削除すると、すべてのポッドも削除されます。ジョブを次のように削除します。

$ kubectl delete -f job-parallel.yaml
job.batch "wait" deleted
$ kubectl get jobs
No resources found.
$ kubectl get pods -a
Flag --show-all has been deprecated, will be removed in an upcoming release
No resources found.

Cron Job

前提条件

Kubernetesクラスタバージョン<1.8の場合、Cron JobはAPIバージョンbatch/v2alpha1で作成できます。 クラスタのバージョンを確認しましょう。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.2", GitCommit:"81753b10df112992bf51bbc2c2f85208aad78335", GitTreeState:"clean", BuildDate:"2018-04-27T09:22:21Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.3", GitCommit:"d2835416544f298c919e2ead3be3d0864b52323b", GitTreeState:"clean", BuildDate:"2018-02-07T11:55:20Z", GoVersion:"go1.9.2", Compiler:"gc", Platform:"linux/amd64"}

Cronジョブを作成する

Cronジョブは、Cron形式で書かれたスケジュールに従って実行されるジョブです。 主な使用例は2つあります。

  1. 指定した時点でジョブを1回実行する
  2. 特定の時点で繰り返し

ジョブの仕様です。

 $ cat cronjob.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        metadata:
          labels:
            app: hello-cronpod
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello World!
          restartPolicy: OnFailure

このジョブは、現在のタイムスタンプと "Hello World"というメッセージを毎分出力します。 次のコマンドのように、Cron Jobを作成します。

$ kubectl create -f cronjob.yaml 
cronjob.batch "hello" created

--validate=false はkubectlのCLIバージョンが1.8などだった場合は必須です。このオプションを指定しないと、エラーが表示されます。

$ kubectl create -f cronjob.yaml --validate=false
cronjob.batch "hello" created

次のようにジョブのステータスを確認します。

$ kubectl get -w cronjobs
NAME      SCHEDULE      SUSPEND   ACTIVE    LAST SCHEDULE   AGE
hello     */1 * * * *   False     1         4s              1m

別の端末ウィンドウで、作成されたポッドの状態を確認します。

$ kubectl get -w pods -l app=hello-cronpod
NAME                     READY     STATUS      RESTARTS   AGE
hello-1525775460-z24xb   0/1       Completed   0          1m
hello-1525775520-2lvvg   0/1       Completed   0          24s

いずれかのポッドからログを取得

 $ kubectl logs hello-1525775460-z24x

Hello World!

Cronジョブを削除

次のコマンドのように、Cron Jobを削除します。

$ kubectl delete -f cronjob.yaml
cronjob.batch "hello" deleted

Namespace

Namespaceを使用すると、物理クラスタを複数のチームで共有することができます。 Namespaceは、作成されたリソースを論理的に名前のついたグループに分割することを可能にします。 各ネNamespaceは以下の機能を提供します。

  1. 名前の衝突を回避するためのリソースのためにユニークなスコープ
  2. 信頼できるユーザーへの適切な権限を確保するポリシー
  3. リソース消費の制約を指定する機能

これにより、Kubernetesクラスタは複数のグループによってリソースを共有し、各グループごとに異なるレベルのQoSを提供できます。 ある名前空間で作成されたリソースは、他の名前空間から隠されています。 潜在的にそれぞれ異なる制約を持つ複数の名前空間を作成できます。

デフォルトNamespace

名前空間のリストは、次のコマンドを使用して表示できます。

$ kubectl get namespace
NAME          STATUS    AGE
default       Active    2m
kube-public   Active    2m
kube-system   Active    2m

デフォルトでは、Kubernetesクラスタ内のすべてのリソースはdefaultNamespaceに作成されます。 kube-publicはすべてのユーザーが読み取ることができる名前空間で、認証されていないユーザーも含まれます。 kubeadmで起動されたクラスタは、cluster-infoConfigMapがあります。 このクラスタはkopsを使用して作成されるため、このConfigMapは存在しません。

kube-system Kubernetesシステムによって作成されたオブジェクトの名前空間です。

Deploymentを作成しましょう

$ kubectl apply -f deployment.yaml
deployment "nginx-deployment" created

namespaceを確認

$ kubectl get deployment -o jsonpath={.items[].metadata.namespace}
default

カスタムNamespace

新しい名前空間は、設定ファイルまたはkubectlを使用して作成できます

  1. 次の設定ファイルを使用して名前空間を作成できます。
$ cat namespace.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: dev
  labels:
    name: dev
  1. 新しい名前空間を作成
$ kubectl apply -f namespace.yaml
namespace "dev" created
  1. 名前空間のリストを取得する
$ kubectl get ns
NAME          STATUS    AGE
default       Active    3h
dev           Active    12s
kube-public   Active    3h
kube-system   Active    3h
  1. ネームスペースの詳細を取得
$ kubectl describe ns/dev
Name:         dev
Labels:       name=dev
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Namespace","metadata":{"annotations":{},"labels":{"name":"dev"},"name":"dev","namespace":""}}

Status:  Active

No resource quota.

No resource limits.
  1. 構成ファイルを使用して、この新しい名前空間に配置を作成
 $ cat deployment-namespace.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment-ns
  namespace: dev
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.12.1
        ports:
        - containerPort: 80
        - containerPort: 443

主な変更はnamespace: devの追加です

  1. デプロイメントの作成
$ kubectl apply -f deployment-namespace.yaml
deployment "nginx-deployment-ns" created
  1. 次の-nように追加のスイッチを用意することによって、ネームスペース内の配置を照会することができます。
$ kubectl get deployments -n dev
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment-ns   3         3         3            3           1m
  1. この展開のNamespaceを照会
$ kubectl get deployments/nginx-deployment-ns -n dev -o jsonpath={.metadata.namespace}
dev

別の方法として、名前空間kubectlを使っても作成することができます。

  1. Namespaceを作成
$ kubectl create ns dev2
namespace "dev2" created
  1. Deploymentの作成
$ kubectl -n dev2 apply -f deployment.yaml
deployment "nginx-deployment-ns" created
  1. 新しく作成されたNamespaceにDeploymentを取得
$ kubectl get deployments -n dev2
NAME               DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment-ns   3         3         3            3           1m
  1. すべてのNamespaceでのdeploymentを取得
$ kubectl get deployments --all-namespaces
NAMESPACE     NAME                  DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
default       nginx-deployment      3         3         3            3           1h
dev           nginx-deployment-ns   3         3         3            3           1h
dev2          nginx-deployment-ns   3         3         3            3           1m
kube-system   dns-controller        1         1         1            1           5h
kube-system   kube-dns              2         2         2            2           5h
kube-system   kube-dns-autoscaler   1         1         1            1           5h

クォータと制限

各ネームスペースにはリソースクォータを割り当てることができます。クォータを指定すると、ネームスペース内のすべてのリソースで消費できるクラスタリソースの量を制限できます。 リソースクォータは、ResourceQuotaオブジェクトで定義できます。名前空間にResourceQuotaオブジェクトが存在すると、リソースクォータが強制されます。 名前空間内には最大でも1つのResourceQuotaオブジェクトが存在します。現在、複数のResourceQuotaオブジェクトが許可されています。 これはkubernetes#55430として提出されています。

クォータは、CPUやメモリなどの計算リソース、PersistentVolumeやPersistentVolumeClaimなどのストレージリソース、および指定されたタイプのオブジェクトの数に指定できます。 ResourceQuotaを使用して制限できるリソースの完全なリストは、https://kubernetes.io/docs/concepts/policy/resource-quotas/ にリストされています。

ResourceQuotaを作成する

ResourceQuotaは、構成ファイルまたはkubectlを使用して作成できます。

  1. 次の設定ファイルを使用してResourceQuotaを作成できます。
$ cat resource-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota
spec:
  hard:
    cpu: "4"
    memory: 6G
    pods: "10"
    replicationcontrollers: "3"
    services: "5"
    configmaps: "5"

この設定ファイルは、ネームスペースに次の要件を設定します。

  1. 作成されるすべての新しいコンテナには、メモリとCPUの制限が必要です ii. この名前空間内のポッドの総数は10を超えることはできません iii. この名前空間内のReplicationControllerの総数は3を超えることはできません iv. この名前空間内のサービスの総数は5を超えることはできません
  2. この名前空間内のConfigMapの総数は5を超えることはできません

  3. 新しいResourceQuotaを作成します。

$ kubectl apply -f resource-quota.yaml
resourcequota "quota" created

あるいは、kubectlCLI を使用してResourceQuotaを作成することもできます。 どちらの場合も、この場合、これらの制限はdefaultネームスペースに置かれます。 別の名前空間は、構成ファイルで指定するか、またはCLIkubectl--namespaceオプションを使用して指定することができます。

kubectl create -f ./resource-quota.yaml --namespace=myspace
  1. ResourceQuotaのリストを取得
$ kubectl get quota
NAME      AGE
quota     5m
  1. ResourceQuotaの詳細については、次を参照してください。
$ kubectl describe quota/quota
Name:                   quota
Namespace:              default
Resource                Used  Hard
--------                ----  ----
configmaps              0     5
cpu                     300m  4
memory                  0     6G
pods                    3     12
replicationcontrollers  0     3
services                1     5

出力には、default名前空間に3つのPodと1つのサービスが既に存在していることが示されています。

ResourceQuotaを使用してリソースを拡大

ResourceQuotaが作成されたので、これが作成された新しいリソースや拡張された既存のリソースにどのように影響するのかを見てみましょう。

私たちは既にデプロイメントを持っていますnginx-deployment。割り当てられたクォータを超えるようにレプリカの数をスケーリングし、何が起こるかを見てみましょう。

  1. Deploymentのレプリカ数を調整します。
$ kubectl scale --replicas=12 deployment/nginx-deployment
deployment "nginx-deployment" scaled

コマンド出力には、展開がスケーリングされていることが示されます。

  1. すべてのレプリカが利用可能かどうかを確認してみる
$ kubectl get deployment/nginx-deployment -o jsonpath={.status.availableReplicas}
3

3つのレプリカしか利用できないことを示しています。

  1. 詳細は次を参照してください。
$ kubectl get deployment/nginx-deployment -o jsonpath={.status.conditions[].message}
Deployment does not have minimum availability.

現在の理由が出力に表示されます。

ResourceQuotaでリソースを作成する

次の設定ファイルを使ってPodを作成しましょう

$ cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80

このポッドを作成する前に、以前実行していたポッドまたはデプロイメントを削除する必要があります。

$ kubectl apply -f pod.yaml
Error from server (Forbidden): error when creating "pod.yaml": pods "nginx-pod" is forbidden: failed quota: quota: must specify memory

エラーメッセージは、ResourceQuotaが有効であり、Podがメモリリソースを明示的に指定しなければならないことを示します。

設定ファイルを次のように更新します。

$ cat pod-memory.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    name: nginx-pod
spec:
  containers:
  - name: nginx
    image: nginx:latest
    resources:
      requests:
        memory: "100m"
    ports:
    - containerPort: 80

ここに定義された明示的メモリリソースがあります。今度は、ポッドを作成してみてください:

$ kubectl apply -f pod-memory.yaml
pod "nginx-pod" created

ポッドは正常に作成されます。

Podの詳細を取得

$ kubectl get pod/nginx-pod -o jsonpath={.spec.containers[].resources}
map[requests:map[cpu:100m memory:100m]]

ResourceQuotaの詳細については、次を参照してください。

$ kubectl describe quota/quota
Name:                   quota
Namespace:              default
Resource                Used  Hard
--------                ----  ----
configmaps              0     5
cpu                     400m  4
memory                  100m  6G
pods                    4     12
replicationcontrollers  0     3
services                1     5

CPUとメモリリソースがどのように増分された値になっているかに注意してください。

kubernetes#55433 ResourceQuotaでポッドを作成するために明示的なCPUリソースが必要ない方法の詳細を提供します。

$ $ kubectl delete quota/quota
resourcequota "quota" deleted

これで、ワークショップを続ける準備が整いました!

次回は201-cluster-monitoringからです。

minikubeでWordPress環境を構築

こちらKubernetes ハンズオンをやりました。 適宜コマンドや説明を修正、補足した作業メモです。

ゴール

VirtualBox環境の1VM上にk8s環境をつくりwordpressを構築していきます。

事前知識

Pod

Podはk8sでアプリケーションを動かす最小単位のリソースです。 同じプロセス空間、ネットワーク、ストレージを共有する1つ以上のコンテナから構成されます。 Podは必ずNode上で実行されます。

Node

Nodeはk8sのワーカーマシンで、仮想マシンか物理マシンです。 今回はvirtualbox上にminikubeというNodeが作られることを確認できます。

k8s環境の準備

minikubeとkubectlをMacにインストールします

brew cask install minikube brew install kubectl

minikubeでローカルにk8s環境作成

minikube start でk8sv1.10.0のシングルクラスタが作成できました。

minikube start
Starting local Kubernetes v1.10.0 cluster...
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.

minikube statusminikubecluster がRunningになっていることを確認

minikube status
minikube: Running
cluster: Running
kubectl: Correctly Configured: pointing to minikube-vm at 192.168.99.102

冒頭でもいいましたが、Nodeとしてminikubeが確認できます。

kubectl get nodes
NAME       STATUS    ROLES     AGE       VERSION
minikube   Ready     master    24s       v1.10.0

k8s上にWordpressを構築

k8s上にwordpressmysqlのコンテナを立ち上げていきます。 DockerStoreにあるものを使っていきます。

DBのパスワードの秘密情報をk8sに保存

k8sの昨日でSecretsというものがあります。 これは秘密情報を Secrets というデータベースで管理して、 パスワードなどを必要とする Pod のみに送ります。 Secrets の中身は Key-Value ペアのリストでパスワードを保存し、対象のpodに送ります。 今回は password.txt というファイルを作成し、 Secrets に登録します。

vim password.txt

kubectl ceate secret コマンドで登録

kubectl create secret generic mysql-pass --from-file=password.txt
secret "mysql-pass" created

kubectl get secrets コマンドで、Secretsの一覧を取得できます。

kubectl get secrets
NAME                  TYPE                                  DATA      AGE
default-token-g46vb   kubernetes.io/service-account-token   3         3h
mysql-pass            Opaque                                1         8s

MySQLを起動

MySQLのPodをつくりMySQLを起動させます。 以下の内容で ./manifests/mysql-pod.yaml を作成します。

apiVersion: v1
kind: Pod
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
    tier: mysql
spec:
  containers:
  - image: mysql:5.6
    name: mysql
    env:
      - name: MYSQL_ROOT_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysql-pass
            key: password.txt
    ports:
      - containerPort: 3306
        name: mysql
  • apiVersion
  • kind
    • リソースの種類
  • metadata
    • Podに設定するメタデータ
    • Podの名前とラベルを設定することで同じアプリケーションが動作するPodが複数存在するときに絞り込みを行うことが可能
  • spec
    • Podの仕様についての定義
    • 起動時に渡す環境変数や、公開するポートなどをここで設定
    • 環境変数として MYSQL_ROOT_PASSWORD を定義し、Secretsに登録したパスワードを参照させる

kubectl apply コマンドでPodを作成

kubectl apply -f ./manifests/mysql-pod.yaml
pod "wordpress-mysql" created

kubectl get pods コマンドで、mysqlが起動していることを確認

kubectl get pods
NAME              READY     STATUS    RESTARTS   AGE
wordpress-mysql   0/1       Pending   0          1m

-l でラベルを使って絞り込みもできます。

kubectl get pods -l app=wordpress -l tier=mysql
NAME              READY     STATUS    RESTARTS   AGE
wordpress-mysql   0/1       Pending   0          59s

kubectl logs コマンドでMySQLのログも確認できます

kubectl logs wordpress-mysql | tail -n 5
2018-05-05 08:04:52 1 [Warning] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory
.
2018-05-05 08:04:52 1 [Warning] 'proxies_priv' entry '@ root@wordpress-mysql' ignored in --skip-name-resolve mode.
2018-05-05 08:04:52 1 [Note] Event Scheduler: Loaded 0 events
2018-05-05 08:04:52 1 [Note] mysqld: ready for connections.
Version: '5.6.40'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)

mysqld: ready for connections. と出力されていることを確認できます。

MySQLに接続

kubectl port-forward コマンドで、Podのポートをフォワードさせます。

kubectl port-forward wordpress-mysql 13306:3306
Forwarding from 127.0.0.1:13306 -> 3306
Forwarding from [::1]:13306 -> 3306

Handling connection for 13306
Handling connection for 13306
Handling connection for 13306

ポート 13306 をしていしてMySQLクライアントでMySQLに接続できることを確認できました。

mysql -s -uroot -p -h127.0.0.1 --port=13306
Enter password:
mysql> show databases;
Database
information_schema
mysql
performance_schema

DBのデータを永続的に保存させる

MySQLのデータはPod上にあるので、Podが消えたらDBのデータも消えてしまいます。 なのでDBのデータを PersistentVolume に保存するようにします。

kubernetesでPersistent Volumesを使ってみる

./manifests/mysql-volume.yaml でPersistentVolumeを定義します。 local-volume-1というPersistentVolumeを作成します。

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-volume-1
  labels:
    type: local
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /tmp/data/lv-1
  persistentVolumeReclaimPolicy: Recycle

適用すると、PersistentVolumeが作成されます。

kubectl apply -f ./manifests/mysql-volume.yaml
persistentvolume "local-volume-1" created

PersistentVolume一覧からlocal-volume-1が作成されていることを確認できました。

kubectl get persistentvolume
NAME             CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM     STORAGECLASS   REASON    AGE
local-volume-1   20Gi       RWO            Recycle          Available

次に、PodがPersistentVolumeを取得できるようにします。 PersistentVolumeClaim というリソースを使いますので ./manifests/mysql-persistent-volume-claim.yaml で定義します。 PV(PersistentVolume)からPodに紐付けるための領域がPVC(PersistentVolumeClaim)です。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-lv-claim
  labels:
    app: wordpress
    tier: mysql
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi
  • Access Modes
    • Claimsは特定のアクセスモードでストレージを要求するときにボリュームと同じ規則を使用します。
  • Resources
    • Claimsはポッドのように、特定の量のリソースを要求することができます。この場合、要求はストレージ用です。同じリソース・モデルがボリュームと要求の両方に適用されます。

適用してPVC(PersistentVolumeClaim)を作成します。 PVCをつくることでPodにストレージを追加することができます。

kubectl apply -f ./manifests/mysql-persistent-volume-claim.yaml
persistentvolumeclaim "mysql-lv-claim" created

それではmysql PodがPVを使うようにマウント設定を行います。Deploymentsにもしておきます。 Deployment については以下もこちらも確認しました。

Kubernetes Deploymentを理解する Kubernetes: Deployment の仕組み

DeploymentReplicaSet を生成・管理し、 ReplicaSetPod を生成・管理します。 Rolling Back やローリングアップデート、Scaling でセルフヒーリングといった仕組みを提供してくれます。

./manifests/mysql-deployment-with-volume.yaml で定義します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
    tier: mysql
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: mysql
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
          - name: MYSQL_ROOT_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql-pass
                key: password.txt
        ports:
          - containerPort: 3306
            name: mysql
        volumeMounts:
          - name: mysql-local-storage
            mountPath: /var/lib/mysql
      volumes:
      - name: mysql-local-storage
        persistentVolumeClaim:
          claimName: mysql-lv-claim

kubectl apply して、mysql Deploymentを作成

kubectl apply -f  manifests/mysql-deployment-with-volume.yaml
deployment.apps "wordpress-mysql" created

Deploymentを作ったので、Deploymentによって作られたPodと、最初に作成したPodがいる状態です。 wordpress-mysql-7584cf4c49-xx9t4 がDeploymentによって作られたPodです。

kubectl get pod -l app=wordpress -l tier=mysql
NAME                               READY     STATUS    RESTARTS   AGE
wordpress-mysql                    1/1       Running   0          48m
wordpress-mysql-7584cf4c49-xx9t4   1/1       Running   0          14s

古いwordpress-mysql Podは消します。

kubectl delete pod wordpress-mysql
pod "wordpress-mysql" deleted

Deploymentが作ったPodを見ると、 ReplicaSet でPodが管理されており、 Volumesmysql-lv-claimが登録されているのがわかります。

kubectl describe pod wordpress-mysql-7584cf4c49-xx9t4
Name:           wordpress-mysql-7584cf4c49-xx9t4
(略)
Controlled By:  ReplicaSet/wordpress-mysql-7584cf4c49
(略)
Volumes:
  mysql-local-storage:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  mysql-lv-claim
    ReadOnly:   false
  default-token-cwnn6:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-cwnn6
    Optional:    false
(略)

WordPressの起動

次は ./manifests/wordpress-pod.yaml に定義し、WordPressのPodを作成します。

apiVersion: v1
kind: Pod
metadata:
  name: wordpress
  labels:
    app: wordpress
    tier: front-end
spec:
  containers:
  - image: wordpress
    name: wordpress
    env:
    - name: WORDPRESS_DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysql-pass
          key: password.txt
    ports:
    - containerPort: 80
      name: wordpress

kubectl apply をします。

kubectl apply -f ./manifests/wordpress-pod.yaml
pod "wordpress" created

Podが立ち上がりませんでした。

kubectl get pods -l app=wordpress -l tier=front-end
NAME        READY     STATUS             RESTARTS   AGE
wordpress   0/1       CrashLoopBackOff   7          17m

ログを確認

WordPressMySQLの接続ができていなそう。

kubectl logs wordpress | tail -n 5
MySQL Connection Error: (2002) php_network_getaddresses: getaddrinfo failed: Name or service not known

Warning: mysqli::__construct(): php_network_getaddresses: getaddrinfo failed: Name or service not known in Standard input code on line 22

Warning: mysqli::__construct(): (HY000/2002): php_network_getaddresses: getaddrinfo failed: Name or service not known in Standard input code on line 22

WordPressのPod定義で、MySQLのホストを指定していないからので、 環境変数WORDPRESS_DB_HOST として渡してあげましょう。

WordPressMySQLの接続

Service というリソースを作ります。 サービスの転送先のポッドのセットは、ラベルとセレクターによって判別されます。 Serviceのspecとしては、ポート情報とどのPodにつなぐかを決めるselectorとclusterIPです。

Services Kubernetes "サービス"の概要についての自習ノート

./manifests/mysql-service.yamlMySQLの接続情報を定義します。

apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
    tier: mysql
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
  clusterIP: None

今回はまずService wordpress-mysql という名前の新しいオブジェクトを作成しました。 このオブジェクトは、TCPポート 3306 をラベルのある任意のPod、ここでは wordpress で使用できるようにします。

wordpress-mysqlサービスを作成します。

kubectl apply -f manifests/mysql-service.yaml
service "wordpress-mysql" created

Service一覧で確認

kubectl get service -l app=wordpress -l tier=mysql
NAME              TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
wordpress-mysql   ClusterIP   None         <none>        3306/TCP   8s

Serviceを作成したら、wordpress Podを一度消して、 DB接続できるwordpress Podを作り直します。

kubectl delete pod wordpress
pod "wordpress" deleted
kubectl get pods -l app=wordpress -l tier=frontend
No resources found.

mysqlのホストとしてmysql Serviceを使うように定義を追加します。 spec/containers/envに、WORDPRESS_DB_HOSTを追加して、値に、wordpress-mysql Serviceの3306を指定しています。 ./manifests/wordpress-pod-with-mysql-host.yaml という名前で以下のを記述します。

apiVersion: v1
kind: Pod
metadata:
  name: wordpress
  labels:
    app: wordpress
    tier: frontend
spec:
  containers:
  - image: wordpress
    name: wordpress
    env:
    - name: WORDPRESS_DB_HOST
      value: wordpress-mysql:3306
    - name: WORDPRESS_DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysql-pass
          key: password.txt
    ports:
    - containerPort: 80
      name: wordpress

kubectl applyして、再度Podを作成。

kubectl apply -f ./manifests/wordpress-pod-with-mysql-host.yaml
pod "wordpress" created

ログをみてみます。 WordPressが無事起動されています。 wordpressからwordpress-mysqlに繋がりました。

kubectl logs wordpress | tail -n 5
Complete! WordPress has been successfully copied to /var/www/html
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppres
s this message
AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.4. Set the 'ServerName' directive globally to suppress this message
[Sat May 05 09:32:32.910938 2018] [mpm_prefork:notice] [pid 1] AH00163: Apache/2.4.25 (Debian) PHP/7.2.5 configured -- resuming normal operations
[Sat May 05 09:32:32.911000 2018] [core:notice] [pid 1] AH00094: Command line: 'apache2 -D FOREGROUND'

しかし、k8sの外からwordpressに繋ぐことができないので、wordpress用のサービスも作ります。 mysqlとは違い、外部から接続したいので、typeとしてNodePortというものを選択します。 これは、PodのポートとNodeのポートを接続するものです。

./manifests/wordpress-service.yaml で以下のyamlを作成します。

apiVersion: v1
kind: Service
metadata:
  labels:
    app: wordpress
    tier: frontend
  name: wordpress
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30180
  selector:
    app: wordpress
    tier: frontend

kubectl apply コマンドで、wordpress Serviceを作成。 applyすればブラウザからアクセス可能になります。

kubectl apply -f ./manifests/wordpress-service.yaml
service "wordpress" created

Service一覧から確認

kubectl get service -l app=wordpress -l tier=frontend
NAME        TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
wordpress   NodePort   10.101.18.94   <none>        80:30180/TCP   9s

minikubeでは、以下のコマンドでブラウザで開くことができます。 Wordpressのセットアップ画面が表示されることができます。

minikube service wordpress
Opening kubernetes service default/wordpress in default browser...

セルフヒーリング

wordpressコンテナがクラッシュした時に繋がらなくなってしまいます。 対応したくないものです。運用はラクにしたいですね。 Podはコンテナとストレージボリュームの集合というだけで、自分自身を管理するということをしませんでした。 DeploymentのリソースをつかうことでPodの管理が可能になります。 DeploymentReplicaSet を生成・管理し、 ReplicaSetPod を生成・管理でしたね。

./manifests/wordpress-deployment.yaml という名前で、以下のyamlを作成。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
    tier: frontend
spec:
  selector:
    matchLabels:
      app: wordpress
      tier: frontend
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
      - image: wordpress
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql:3306
        - name: WORDPRESS_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-pass
              key: password.txt
        ports:
        - containerPort: 80
          name: wordpress

applyしてDeploymentによって自動的にwodpress Podが作成されることが確認できます。 古いPod(wordpress)は削除します。

kubectl apply -f ./manifests/wordpress-deployment.yaml
deployment.apps "wordpress" created

kubectl get deployment コマンドで、wordpress Deploymentが作成されていることを確認

kubectl get deployment -l app=wordpress -l tier=frontend
NAME        DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
wordpress   1         1         1            0           7s

Deployment によってPodが自動作成されていることを確認。 wordpressはPodとして作成したもので、wordpress-5644c5ff4-fgbgbは Deployment によって自動作成されたものです。 また、最初に作成したwordpress Podは消しておきましょう。

kubectl get pods -l app=wordpress -l tier=frontend
NAME                        READY     STATUS    RESTARTS   AGE
wordpress                   1/1       Running   0          8m
wordpress-5644c5ff4-fgbgb   1/1       Running   0          23s
kubectl delete po/wordpress
pod "wordpress" deleted

Deploymentは定義した状態を維持しようとします。 例えば、Podを削除しても新しいPodが自動的に作成され、Podが1台動いているという状態が維持されるます。

kubectl delete pod wordpress-5644c5ff4-fgbgb
pod "wordpress-5644c5ff4-fgbgb" deleted
kubectl get pods -l app=wordpress -l tier=frontend
NAME                        READY     STATUS        RESTARTS   AGE
wordpress-5644c5ff4-fgbgb   0/1       Terminating   0          54s
wordpress-5644c5ff4-w7h9z   1/1       Running       0          6s

このように wordpress-5644c5ff4-fgbgb が削除されると、 wordpress-5644c5ff4-w7h9z というPodがDeploymentにより作られます。

これはk8sの特徴で、状態を維持しようとする機能があります。 これにより、アプリケーションがクラッシュしたりしても自動的に復旧され、 サービスの運用の負担を下げることができます。

Deploymentを使うように変えてもPodは1つのままですがPodの数を変更することも可能です。 次は kubectl scale コマンドでレプリカ数を変更して、Podの数を変えてみましょう。

kubectl scale deployment wordpress --replicas 5
deployment.extensions "wordpress" scaled

kubectl get pods でPodが4台追加されていることが確認できました。

kubectl get pods -l app=wordpress -l tier=frontend

NAME                        READY     STATUS              RESTARTS   AGE
wordpress-5644c5ff4-9kt5l   0/1       ContainerCreating   0          12s
wordpress-5644c5ff4-kvb4q   1/1       Running             0          12s
wordpress-5644c5ff4-vhkhq   0/1       ContainerCreating   0          12s
wordpress-5644c5ff4-w7h9z   1/1       Running             0          33s
wordpress-5644c5ff4-zq6sz   0/1       ContainerCreating   0          12s

これで、スケールアウトできる、wordpress環境の完成です。

# KVM仮想化ホストサーバ間でのゲストサーバ移動

KVM仮想化環境でゲストイメージを別のホストサーバに移動させたので、 手順をまとめておきます。

ゲストサーバの起動状況確認

virsh list --all などで対象のゲストサーバが存在するか確認しましょう。 またイメージファイルや転送先のホストのディスク容量なども確認しておきましょう。 virsh nodeinfovirsh dominfo <ドメイン名> でCPUやメモリなどの割当状況も確認しておきます。

移動するゲストイメージの停止

移動するゲストイメージを停止します。

virsh dominfo <ドメイン名>
virsh shutdown <ドメイン名>
virsh destroy <ドメイン名> #shutdownで停止しなかったら  
virsh dominfo <ドメイン名>

ゲストイメージのファイルコピー

定義ファイルとimageファイルをコピーします。 rsyncは転送の効率化のために、暗号方式や転送速度制限を行っています。

virsh dumpxml <ドメイン名> > /tmp/定義ファイル名.xml
scp /tmp/定義ファイル名.xml user@対象サーバのIP:~/
rsync -avS --progress -e "ssh -c arcfour" --bwlimit=250000 /var/lib/libvirt/images/イメージファイル名.img user@対象サーバのIP:~/
ionice -c3 nice -n15 md5sum /var/lib/libvirt/images/イメージファイル名.img

転送したファイルのチェック

imageファイルのチェック

ionice -c3 nice -n15 md5sum /path/to/イメージファイル名.img

定義ファイルの配置

権限等は他と揃えておきましょう、

mv /path/to/定義ファイル名.xml /etc/libvirt/qemu/

ハードリンク作成

imageファイルが消失してもいいようにハードリンク作成しておきます。

ln /var/lib/libvirt/images/イメージファイル名.img /var/lib/libvirt/hardlink/イメージファイル名.img

ゲストサーバをlibvirt 管理下に登録、起動

定義ファイルを定義して、起動

virsh define /etc/libvirt/qemu/定義ファイル名.xml
virsh dominfo <ドメイン名>
virsh start <ドメイン名>

設定の削除

移設元の設定は不要なので削除します。

virsh undefine <ドメイン名>

JAPAN CONTAINER DAYS V18.04に行ってきた

https://medium.com/@yukotan/japan-container-days-v18-04-%E3%81%AE%E8%B3%87%E6%96%99-4f380fb7b696

サイバーエージェントにおけるプライベートコンテナ基盤AKEを支える技術

https://speakerdeck.com/masayaaoyama/saibaezientoniokerupuraibetokontenaji-pan-akewozhi-eruji-shu

アドテクで求められる性能要件

  • Low Latency
  • High Traffic
  • High Computing

環境

AKEとよばれるプライベート基盤(オンプレ)

AKEとは

コンテナが流行り始めた2016年ごろにスタート。 ソースを読んで理解を深め、2017年04月ごろにリリース。 2017年07付きにk8s1.7がプロダクションでサポート。 type loadbalanceをサポート。 プロダクション環境にAKEをリリース。 コンテナ環境に合わせたCICD環境を作り上げると効率が上がる。

コマンドでクラスタが作れる。 Heatとういう機能をつかって構築している。

一連の流れ

パッチを当てたK8sをbuildしOSimageをつくり、Opencluster Heatで自動構築して、E2E Testを実行する。 テストで使用しているもの https://github.com/heptio/sonobuoy

Key Features

  • K8sとSwarms support
  • openstackと統合
    • Heatで構築、CinderをPV、Designateで名前解決、Keystoneで認証する。
    • MagnumはL3ネットワークを使わなければいけないので、開発が遅い。細かい設定ができない。
    • Rancher 2.0はGAが5月だった。細かい設定ができない。
    • Tectonicは知名度が低くかった。細かい設定ができない。
  • L4/L7ロードバランサーを用意
    • NodePort + 手動LBだとノードのスケールしたり、LoadBalancerの操作が必要で大変。
    • type: LoadBalancerはCloud Provider Integrationで実装、OpenStackではOctaviaが使える。
    • Octaviaは性能不足なのでBaremetal LBと連携するようなCloudProviderを実装した。
    • ingressは面倒
  • monitoring log
    • addonを追加することで後から利便性を高めることができる。
    • addonとしてEFKstack、Datadog、helmなどがある。
  • Tuning
    • アドテクのシステムに合わせてチューニング
    • Network,Kernel,K8s,Hypervisor
  • Multi Countainer の実行サポート
    • k8sだけではなく、Docker Swamも対応

利点

自分たちでコンテナ基盤をつくれば、なんでも作れて触れて最高 ハイブリットクラウド構成 マルチコンテナランタイム対応

デメリット

実装コスト、運用コスト

マイクロサービスアプリケーションとしての機械学習

機械学習人工知能分野のブームがきている。 この分野では人材不足、獲得合戦だが、 今の学生は優秀なので数年後には解消されていそうとのこと。

機械学習をアプリケーションで利用する

処理時間、サーバコストも高い コードと学習モデルの整合性が難しい 学習環境の用意も必要 機械学習エンジニアの担当領域が不明 機械学習エンジニアはモデリングなどが得意だけど、システム設計とかAPI提供などはメインじゃない

コストの高い要求をした時のメルカリのSREチームの解凍

メルカリのSREチーム「Dockerfile用意してください。なんとかします」

はじめてのDockerfile

ビルド時にキャッシュがある。 キャッシュを意識した順序で書く。

一週間後、S3に画像が入っているので、GCPのk8sという構成。 Datadogでのモニタリング、Spinnakerでのデプロイリリース管理

http://techlife.cookpad.com/entry/2015/09/16/182917

はじめてのSpinnaker

GUIで操作できる。 自動deploy,immutableである。 http://tech.mercari.com/entry/2017/08/21/092743 http://tech.mercari.com/entry/2017/12/17/205719 http://tech.mercari.com/entry/2017/12/02/093000

1週間でできた理由は,モノリシックアーキテクチャからマイクロサービスに変わっていたことで,疎結合な作りになっていたから. これにとり,異なる機能を影響なく短期間でリリースできるようになった。 基本機能は Monolithic のまま、新機能や大幅な変更が伴う機能は Microservice可。

機械学習では汎用的につくるのは難しい。

キモになる機械学習の更新は,手動更新を都度SREに依頼していた.後から気づいたことだが, コードとモデルは密結合なため,整合性が保たれていないと動かない.複数のモデルをサポートし始めたら,自分の運用が破綻するのは目に見え始めていた。 マイクロサービスにしたメリットは機械学習モデルのサービスの組み込みが早い。 影響範囲が明確,改善の見込みがあれば早期のモデルデプロイ→気モデルの軽量化チューニングという投機的デプロイもできる。 機械学習エンジニアからすると,最大限の成果を上げるためにはモデリングへの注力が大事。 とはいえ、運用は無視できないので、モデリングと運用を分けたチームで運用する予定。 サービス関連系のオーバーヘッドも実は無視できない。

マイクロサービス化、依存パッケージとかを自由に使えるようになるのでいいけど、汎用イメージをつくるのはよくない。

PersistentVolumeでデータをBlueGreenデプロイする。

機械学習のモデルを頻繁に更新するのでコンテナーにしてマイクロサービスにすると相性がよい。

まとめ

機械学習エンジニアの立場からマイクロサービスは相性がいい。

  • 影響範囲がわかりやすい
  • アルゴリズム選択の自由度が高い
  • サービスの組み込みが早い

機械学習とMicroservicesとの親和性は高いがMicroservicesの雛形や指針があり、機械学習のシステムの専属のチームがあるとよい。

Yahoo!JapanのK8s as a Serviceで加速するアプリケーション開発

https://www.slideshare.net/techblogyahoo/yahoo-japan-kubernetesasaservice

Yahoo!ズバトクonk8s

サービス内容

くじが当たるキャンペーンプラットフォーム

技術スタックIaaS上のVMに社内独自パケージシステムとPHPから構成. キャンペーン中の数10倍に跳ね上がるトラフィックは,VMで捌くのが大変.CI/CD周りが自動化されていないので, リリースが遅い.パフォーマンステスト環境の整備が難しいなどの問題があった.

Yahoo ズバトクというサービスの基盤をkubernetesにした。 Zlabと協力してスタックをモダン化.OpenStackの上にk8s,Docker,ConcourseCIが,アプリもJavaで作り変えた

開発フローもモダン化

dev/stg/proの3環境にデプロイするまで,GitHubgのPR,Jenkins・独自ツールでビルド・構築していたのが、 GithubとConcurseが全てテスト/ビルド/デプロイが走る統合型に変わって便利になった。 リリースに掛かる時間も数時間から10分程度に変わった。

障害発生時の対応も変わった。 IaaSのHyperVisorが落ちたら、その上で動くVMを退避→サーバ稼働再開という流れ k8sを入れるとHyperVisorダウンをトリガにオートヒーリングで自動的にサービスが継続できる。 障害調査も分散環境のログ取得から,ログは1箇所で見れば良くなった。 今まではサーバーログを集めて調査してたが次はk8s側の機能でログを集約、splunkを使っている。

移行コストの話

内製プラットフォーム側の追加対応・CI/CD、言語のスイッチ、考え方や設計方針もk8s化する必要があった。 具体的にはクラウドネイティブ化。 これはZlabの協力を得て,考え方を変えた。 まだ移行の課題はあって。既存の設計をもう少しクラウドネイティブ化するための設計変更などもある。 今後はリリースまでの時間をもっと短くしたい。

Yahoo!JapanのK8s as a Service

Zlabは株式会社.ヤフージャパンの100%子会社.インフラ基盤技術の調査・研究開発。

Kaas

k8sクラスタの作成、削除、アップグレードを簡単値行える

  • セルフサービス
  • マネージド
  • スケーラブル

障害や問題のあるノードの修復(セルフヒーリング)

ノードAが壊れたら、削除して、新規のノードを自動作成し、クラスタを一定に保つ。

ゼロダウンタイムのアップグレード

利用可能なノード数を一定に保つことでサービス断なしに達成・ 全ノードの更新が必要な脆弱性対応にも即座に自動で対応できる。

クラスタアドオン

クラスタでサポートするアドオン:Ingres Controller、Ingressホストの自動登録。 Prometeus/Grafanaとダッシュボードで分かりやすく

Kaasの価値

煩雑なk8sのオペレーションから運用者を解放

  • クラスタの作成、削除、設定変更
  • ノード(VN)の追加、削除

これらは人間のやる仕事じゃないのでソフトウェア+Kaasにやらせる。 AUTOMATE ALL THE THINGS!!

KaaSの要件

数万台でも動くスケーラビリティ、非同期で処理できるモデル、処理が失敗しても再開できる・一部の破壊が全体の障害に影響を与えない堅牢性。

  • スケーラブル
  • 非同期モデル
  • 堅牢性
    • 処理が失敗しても再開できる

複雑な分散システムとして実装する必要があるが、近くに優れた分散システムの基盤があることに気づく。

KaaSは何を元に作っているのか.分散システム基盤としてのk8sに対して拡張機能を追加することで対応。 もともとのk8s自体が分散処理で動くように作り込まれているから k8sの拡張機能とすることで、付加価値が生まれる箇所のロジックに集中して開発ができている

分散システム基盤としてのk8s

kaas on k8s

母体のk8sで便利なもの.CustomResourceDeginitions。k8sAPIを拡張して,任意のリソースを追加できる。(エンドポイント,Watch APIなど) CRDを書いただけではなにもおきないのでコントローラを書く。 callbackとworkerを書けばOK。 カスタムコントローラの実装ができる。 これはControllerという形でフレームワークが用意されていて、実装者はコールバックだけをフレームワークに登録すれば良い。 カスタムリソースとカスタムコントローラのパターンで開発している

K8sのセキュリティのベストプラクティス

https://speakerdeck.com/ianlewis/kubernetesfalsesekiyuriteifalsebesutopurakuteisu

Ian Lwwis

k8sはインフラの提供をしてくれる

Guestbookアプリで説明 - WebFronted - web app - Message - メッセージを保存閲覧 - NGWord - NGワードを検出

k8s API

  1. Frontend Podからトークン取得
  2. トークン取得し、APIサーバを攻撃
  3. シークレットなどを取得し、さらにサービスを攻撃

Mitigate 1 & 2:RBAC

RBAC(Role Based Access Control)をきちんと設定しよう!

RBACは1.6から標準

Role Based Access Control ユーザやサーボすアカウントへロールを付与 ロールが権限を持つ get secrets tipdate configmap etc RBACはネームスペース展開 GKEではIAMと連携

Mitigate 2:API Sercer Firewall

API Server FirewallでAPIサーバーへのアクセスにIP制限かけよう!(Backend network側しかアクセスさせないようにする)

APIサーバへのアクセスをIPアドレスに制限 GKE なら1コマンドでできる

Mitigate 3:Network Policy

Network Policyで、DBやRedisなどKVSへのアクセスは必要なPodだけに制限しよう! telnet redis port番号 とかで接続できてしまう。 データベースへのアクセスを必要のあるPodに制限 ラブルセレクターでPodを洗濯 ネットワークプラグインで実装されている Calico, Weave,etc

ingressで設定したものしかportにアクセスできない。

ホストへアクセス

  1. コンテナ外へ突破
  2. kubeletを攻撃(権限や情報にアクセスされるなど)
  3. 同じホストに実行中のコンテナを攻撃

Mitigate1 :non-rootユーザで実行

コンテナをrootで実行すると色々できちゃうからroot意外のユーザーで実行する コンテナで別ユーザを実行すると、ホストのユーザがとれていない状態になる。 spec: securityContext: runAsUserでユーザを指定できる

runAsUser:1000

Mitigate1 :読み込みせんようファイルシステム

読み込み専用ファイルシステムもtrueにしておくと良い spec: securityContext: readOnlyRootFilesystem: true readOnlyRootFilesystem: true eadonlyfilesystemの使い所は大事

Mitigate1 :no_nre_privs

allowPrivilegeEscalation: false

自分が持っている権限委譲は付与できないようにする。 AllowPriviledgeEscalationはk8s 1.9での挙動では注意 https://qiita.com/inajob/items/943a634a1941030e5075

Mitigate 1: seccpmp/AppArmor/SELinux

seccomp + AppArmor + SELinuxで多段で守る eccomp + AppArmor/SELinuxで壁を増やす

SECCOMP

seccomp: security.alpha.kubernetes.io/pod: docker/default

metadata: annotations:seccomp.security.alpha.kubernetes.io/pod : docker/defaultとするとseccompが有効になってお勧め(ただしalpha)

seccompはv1.10でもまだalpha版 https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp

unshare -U でネームスペースから突破できてしまう。`

AppArmor

container .apparmor .security.beta.kubernates.io/hello: runtime/default

SELinux

SELinuxRedhat系、AppArmorはDebian系、

seLinuxはラベルで設定可能 https://kubernetes.io/docs/tasks/configure-pod-container/security-context/

Mitigate 2&3: kubeletの権限を制限する

RBAC for kubelet

--authorization-mode=RBAC,Node --admission-control=...,NodeReaatriction

Rotate lubelet certs

--rotate-certificates

Mitigate: PodSecurityPolicy

トラフィックを傍受

  1. ネットワーク上の通信を傍受

Istio

  1. サービス間のプロキシー
  2. 暗号化
  3. 証明証の自動更新
  4. ポリシーがセントラルサーバで集中して管理する

CNCF Cloud Native Interactive Landscape

https://landscape.cncf.io/

Cloud Native Apps 入門

https://speakerdeck.com/tnir/cloud-native-apps

What is Cloud Natic

クラウドネイティブなシステム

https://www.cncf.io/about/charter/

CNCF

2015年にk8sプロジェクトの寄贈先としてLinux Foudationのもとでスタート 20プロジェクト(2018年4月現在) メンバーシップ(スポンサー)~180社(2018/4) Technical Oversight Communite

What is Cloud Natic Application

cloud application maturity https://www.nirmata.com/2015/03/09/cloud-native-application-maturity-model/

CI/CD基盤

イメージビルドの省力化・自動化・標準化は重要 Gitlab Runner + Gitlab Container Registry

Gitlab CI評価

CNCF CIにも採用済 Cloud Native対応 GitHub/GHE対応 企業ユースには適する UIの洗練感はない

アーキテクチャ

積極的なマイクロサービスは行わない。 

マイクロサービス化に拘らない

  • 既存コード資産
  • 慣れたフレームワーク
  • I/Oを熟考した「サービス」を実装していく

KubeCon tips and k8s at Github

https://speakerdeck.com/tnir/kubecon-tips-and-kubernetes-at-github

The Twelve-Factor Appsに従う

コミュニティとの関わり

コミュニティに支えられた。

まとめ

k8sで実行できるようにアプリをcloud Native化しよう。k8sで廃れても対応できそう。 cloud Native化の仕組みが大事。 microservicesにこだわらないことも大事 コミュニティに支えられた。

Spinnakerを利用したk8sへの継続的デリバリ

k8sで安全にappをデプロイする仕組みについて ※安全とは作業ミスをなくすなど

導入するメリット

なぜコンテナを使うのか? コンテナを利用するメリットは ポータビリティ、軽量、実行環境の隔り

開発と本番環境の環境差分をなくす 設定漏れやパッケージの差分など

k8sの役割

複数のDockerホストの管理 コンテナの死活監視 障害時のセルフヒーリング ロードバランサーの組み込み

k8sにはCI/CDの機能がないので別途用意する必要がある

CI/CD

高品質なプロダクトを素早くユーザに届ける

CI:継続的インテグレーション DEVELOPE→DEPLOY→TESTを自動で回す仕組み a.g. Jenkins

CD:継続的デリバリ CIで回したものをstg,本番環境にデプロイする仕組み

Spinnakerにより、k8s上でCDを実現できる CIは別途用意

Spinnakerとは

Netflix社が開発したOSS マルチクラウド対応CDツール アプリケーションの自動デプロイに必要な機能が実装 パイプラインやNlue/Greenデプロイなど

機能紹介

k8sにデプロイする機能 GUIでパイプラインを作成できる パイプラインとはワークフローみたいなもの 複数のデプロイメント方法をサポート Red /Black Deploy(Blue/Green) Rollying Red/Black Deploy Canary Deploy

Red/Black Deploy

切り替え切り戻しを一瞬で行い時

Rollying Red/Black Deploy

断続的に切り替えたいとき

Canary Deploy

テスト的検査 最小構成だけ切り替えて様子見したいとき 問題なければ、切り替える

切り戻しもGUIやパイプラインで簡単に可能

パイプラインにカスタムスクリプトの実行が可能

serverspec,selemiumなどで工程ごとに試験ができる

CIツールは別途必要。 連携できるのは、TravisとJenkins

進捗状況を通知できる

パイプラインの成功、失敗をslackなどで通知など

パイプラインで承認フローを組み込める

stg,testは自動、本番は管理者の承認で先にすすめる。

承認(manual judgment)

その他機能

  • white-listed execution windows
  • chaos monkey integrarion
  • enable monitoring
    • datadog
    • prometheus
    • stackdrive
  • triggering on webhock
  • authentication

spinnaker と k8sのマッピング

instance, pod server group, replicaset clustrt, deployment load bakancers, service security group, Ingress

Spinnakerのこれから

2ヶ月ごとにバージョンアップ k8sのmanifestをデプロイ kayentaと連携

k8sの運用設計ガイド

細かい機能はあとで理解し、k8sはなにをするのかをまずは理解した方がいい。 明確な目的を持てば、自然とどう使えばいいかわかる。

テーマ

自律的なチームとシステムを作るためのk8sの利用/運用設計

why自律的?

独立的に動けて。自由がある。 moving fast, innovating

moving fast

速さというのは急ぐこと空ではなく、何かをなくすことから生まれる チューニングも思いクエリをなくす。承認リレーをなくす。

k8s自体の狙いとズレていないか?

3GB.4GBのイメージを使ってしまうとか k8s design architecture オーケストレーションを排除して、セルフオペレーションのためのもの。

チームの設計

技術と組織は表裏一体 どういうチームだったらk8sを使える?

コンウェイの法則を逆手に取る

自分が作りたいシステムを設計したチームをつくればいい

アプリケーション系、インフラ共通基盤系 これらは密結合せず、チームを分けた方がいい プロダクトチームとクラスタアドミンチーム

責任の設計

システムごとに必要なエンジニアリング作業がある

疎結合になる責任教会を決める

ソースコード、Container、ノード、クラスタ コンテナとノードが教会

プロダクトチームの責任

顧客の課題を解決

クラスタチームの責任

プロダクトチームのパフォーマンスを最大化すること。

You build it,you run it!!

クラスタの設計

開発環境、本番環境毎にクラスタをつくるのはよくない 環境が増えるたびにクラスタが増えて管理コストが上がる。 開発と本番環境が一致していることが保証しづらい。 ステージング環境が必要になっtラ、またクラスタをつくるのか。

リージョン毎に1つだけクラスタをつくる。

  1. 開発と本番環境が一致
  2. プロダクトがクラスタを意識しない

環境を特別視しない

開発と本番環境をわけても、serviceA がserviceBに影響を与えないようにしといけない結局、

リージョンごとに1つだけくらつたをつくる。

  1. aws,gcp,herokuに開発環境せんよう窓口はない
  2. ユーザが開発環境用として認識すればいい

プロダクト、サービス毎にクラスタをわける

メンテされないクラスタがでてくる。 他のアービスと熊津するばあいはどうする

クラスタ感通信yほり、暮らした無い通信のお方がネットワークの制御がしやすい。Neteork PolivyやIstopがあるので。

同じノードにのっているとセキュアじゃない

クラスタは1つ専用のノードを用意する

クラスタの粒度

Namespaceの設計

Namespaceでバーチャルクラスタを作る

環境毎にNamespaceを分ける

環境だけでなく差=ビス感もわけたい

サービス名+環境にNamespaceを分ける

Network Pokivyの設計

Namespaceレベルで制御する

基本はAll Denyにしてホワイト絵リストで通信可能なNamespaceを設計する

RBACの設計

RBCSCで権限委譲する admin Roleとcluster admin Roleでプロダクトチームと、クラスタアドミンチームでわける。

Borg-cluster adminチームはGmail adminチームの権限はみれない、 権限は強すぎるなら削った方外い。

アプリケーションContainerの設計

Disposable

  1. ステートレスに
  2. ログは標準出力に

k8sはあるべき状態に治す。いまの状態から。control move 死んでも立ち上がるので、大丈夫というアプリケーションをつくるのがよい。

IMuutable

  1. Latest tagは使わない
  2. 開発と本番環境で同じイメージを使う

Resilient

自然に復旧する動きにする

  1. Liveness Proveを使う(生きてるけど、バックエンドにつながらない時は通さない。Containerは生きているけど、プロセスがゾンビの時はkillしてもらう。)
  2. Crash only
  3. PDBを使う

Observable

  1. Liveness Prove,Readiness Probe
  2. ログは標準出力に
  3. メトリクス、トレース

Single Concern

  1. 1Container 1プロセス

Loosely COupled

!. Labelで引っ掛ける 2. 順番はないほうがいい 3. Affinityも極力避ける(疎結合にしたい) 4. (Externaml)Serviceで固定IPも避ける

12 Factoro Appをみるのがいい

オペレーションの設計

必ず宣言的なアプローチをロツ

  1. バージョン管理
  2. Control Loop
  3. 1リソース1ファイル

バージョン管理では GUIのデプロイツールはあんまりよくない。 yamlのパラメータを変えた時、どこ変えたのかわかんない どうあるべきかを書けばいい。

WHY k8sがYAMKベースなのか

YAMLCLIcurlrest APIが自然と使える。

モニタリングの設計

メトリクス イベント トレース どうはねているのか どのクエリ、どの関数がエラーなのか イベント、ログ なぜを把握するためのもの。 メトリクスやトレースにはきづけないもの 4つのレベルでモニタリングする ソースコード -> コンテナ -> ノード -> クラスタ

『コンテナ疲れ』と戦う、k8s - PaaS -ServerLessの活用法!

正しいテクノロジースタックの洗濯ができる知識

コンテナつらくないですか? コンテナ具術は抽象度が低い エンジニアのかばーしないといけない責任は似が広い k8sはエンジニアスキルが高いことを前提では SREも日本の企業にあっているだろうか

Containerの次はなんだろう

10年前はクラウド黎明期。 EC2のEUリージョン解説

5年前はクラウドは定着 DevOpsがもてはやされた。IaaSやCI/CDが中心。

テクノロジーの流れは 抽象度が高く、自動化の繰り返し 自動化の好循環

次はより抽象化、より自動化。 PaasやServerless

PaaS

開発者がアプリケーション開発に専念 アプリケーションのライフサイクルを支援するプラットフォーム PaaSの内部はContainerを使っている Containerが廃れてもPaaSは進化し続けている

Serverless

サーバ管理をせずともアプリケーションの構築と実行を行う仕組み

ServerlessとContainerの関係

Slervelessプラットフォームは込んでナでFucctionえお実行

CNCF serverless whitepaper http://gs2.hatenablog.com/entry/2018/02/16/114739

Fluentd and Distributed Logging in Container Era

ログにもプロダクションのログ。ビジネスや、サービスのためのログ。 サービスログ、システムログなどがある。 コンテナは生まれて消えるので、ログ管理は大変。マイクロサービスが流行り、いろんなコンテナに、アプリがある。

ソースレイヤーでパースする。先に統一すると後が楽。統一された型を持ったレイヤーにするのが大事。 Fluentdでは基本はjson型に変換する。 aggregatorはfluentdからfluentdに送ること。

logging driverはDockerコンテナのログを取れる。 fluent-loggerはfluentdはコンテナのアドレスを知らなくていい。

コネクションが増えるとパフォーマンスが落ちるので、コンテナのアプリケーションのログの送り先に直接redisに送るとかではなく、fuentdを送る。 バッファリングや、ロードバランシングを考えてくれる。分離して管理する。

agregation serverとしてfluentdをさらに置く。 コネクションを一つにまとめて、ロギングだけのコンテナを置くと、ソースサイドの負荷も下げられる。

destinationapiコールが多いか、少ないかで変わる。 Bigqueryとかは課金があるので、前段にアグリッションサーバを置くことがある。 ネットワークを分散して、障害で落ちてもいいように、ロードバランシングしたほうがいい。 ログは飛ばし先の分散が重要。

ログのフォーマットは統一しよう。アプリケーションレイヤーとシステムレイヤーが分断されるとツライ。 Fluentdで飛ばすログが1ファイルに全部入り(スタックトレースアクセスログなど)つらい。

macからvagrantへrake spec実行ユーザ変更

macのユーザでSSH接続しようとするので、vagrant ユーザに変えたかった。 documentには以下のように記載があった。

Serverspec with SSH backend logs in to target servers as a user configured in ~/.ssh/config or a current user. If you’d like to change the user, please edit the below line in spec/spec_helper.rb.

options[:user] ||= Etc.getlogin

~/.ssh/configssh_config に記載したがどうしても上の options[:user] ||= Etc.getlogin が先に読み込まれて 現在のログインユーザーで実行されてしまった。 なので以下のように強引に書き換えて通った。

-options[:user] ||= 'vagrant'
+options[:user] ||= Etc.getlogin

spec_helper.rb をいい感じに書き換えて ssh_config を読ませるようにしたい。

"msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).\r\n"の対応

環境

  • コントローラー:Mac
  • ターゲット:CentOS6.9(vagrant)

ansible -i inventory/inventory.ini playbook -m ping

pingで疎通確認をしたところ以下のメッセージが出た。 SSH接続失敗しているようだった。

"msg": "Failed to connect to the host via ssh: Permission denied (publickey,password).\r\n"

公開鍵の権限など確認したけど問題なく原因がわからなかったのですが、 - vvv オプションで確認したところ以下のようにSSH接続するuserが指定されていないのが原因だった。

ESTABLISH SSH CONNECTION FOR USER: None

なので、 以下のようにインベントリにユーザを指定すれば解決しました。

192.168.33.27 ansible_user=vagrant

SSHが通らない時に原因はいくつかあると思いますが、 -vvv-vvvv を指定すれば原因が掴めそうです。

ReactでHello World!

Node.jsのインストール

Homebrewでインストール

brew -v
brew update
brew install nodejs
node -v

インストール用プロジェクトの作成

JSのプログラムをプロジェクト単位で管理。 npm init -y でパッケージの管理ファイル(package.json)を作成する。

mkdir hello_react
cd hello_react
npm init -y

package.jsonの変更

  1. desctiption(説明文)を記入
  2. Githubで公開しないprivateなプロジェクトと指定
  3. 開発ツールの起動スクリプトstartを定義
  4. webpack実行用スクリプトwebpackを定義
{
  "name": "hello_react",
  "version": "1.0.0",
  "description": "Hello React", #1
  "private": true,              #2
  "main": "index.js",
  "scripts": {
    "start": "webpack-dev-server", #3
    "webpack": "webpack -d"        #4
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

npmパッケージをインストール

npm install react react-dom
npm install webpack webpack-cli webpack-dev-server --save-dev
npm install babel-cli babel-loader babel-preset-env babel-preset-react --save-dev
npm install eslint eslint-loader eslint-plugin-react --save-dev
npm install css-loader style-loader --save-dev

インストール結果の確認用Reackコード

  1. ディレクトリー作成
mkdir src
mkdir public
  1. .babelrc作成
vim .babelrc
cat .babelrc
{
  "presets": ["env", "react"]
}
  1. .eslintrc.json作成
vim .eslintrc.json
cat .eslintrc.json
{
  "env": {
    "browser": true,
    "es6": true
  },
  "parserOptions": {
    "sourceType": "module",
    "ecmaFeatures": {
      "experimentalObjectRestSpread": true,
      "jsx": true
    }
  },
  "extends": ["eslint:recommended", "plugin:react/recommended"],
  "plugins": ["react"],
  "rules": {
    "no-console": "off"
  }
}
  1. webpack.config.js作成
vim webpack.config.js
cat webpack.config.js
module.exports = {
  entry: {
    app: "./src/index.js"
  },
  output: {
    path: __dirname + '/public/js',
    filename: "[name].js"
  },
    devServer: {
    contentBase: __dirname + '/public',
    port: 8080,
    publicPath: '/js/'
  },
  devtool: "eval-source-map",
  mode: 'development',
  module: {
    rules: [{
      test: /\.js$/,
      enforce: "pre",
      exclude: /node_modules/,
      loader: "eslint-loader"
    }, {
      test: /\.css$/,
      loader: ["style-loader","css-loader"]
    }, {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: 'babel-loader'
     }]
  }
};
  1. public/index.html作成
vim public/index.html
cat public/index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta http-equiv="X-UA-Compatible" content="IE=Edge, chrome=1" />
  <title>React App</title>
</head>
<body>
  <div id="root"></div>
  <script type="text/javascript" src="js/app.js" charset="utf-8"></script>
</body>
</html>
  1. src/index.js
vim src/index.js
cat src/index.js
import React from 'react'
import ReactDOM from 'react-dom'

ReactDOM.render(
  <h1>Hello, world!!</h1>,
  document.getElementById('root') 
)
  1. 確認
npm start

ターミナルに webpack: Compiled successfully. が表示されたら ブラウザーhttp://localhost:8080 をアクセスしてHello World! と表示されたらOK.