28
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Kubernetes勉強 その3(DeploymentとService)

Last updated at Posted at 2018-10-07

引き続き「Kubernetes完全ガイド」読んでお勉強。

Pod と ReplicaSet と Deployment

Pod はリソースの最小単位であり、ReplicaSetPod を管理するリソース。 ReplicaSetPod が停止すると自動的に再起動したりする。
Deployment は、 Pod に含まれるコンテナをバージョンアップする際に、古いコンテナを徐々に減らし、新しいコンテナを徐々に増やすなどの管理をする。

Deployment

  • Deploymentを使うと、ワーカーのバージョンアップを行う場合に旧バージョンを徐々に減らし、新バージョンを徐々に増やすローリングアップデートが行える。

Deploymentの作成

以下のようなファイルを作る。

sample-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.12
          ports:
            - containerPort: 80

コンテナを作成する。

$ kubectl apply -f sample-deployment.yml 

deployment.apps "sample-deployment" created

状況を確認

$ kubectl get pods

NAME                                 READY     STATUS    RESTARTS   AGE
sample-deployment-86b68b4c5b-8v4dg   1/1       Running   0          41s
sample-deployment-86b68b4c5b-cnj45   1/1       Running   0          41s
sample-deployment-86b68b4c5b-pjpln   1/1       Running   0          41s

ポッドが3つできています。試しに一個落としてみます。

$ kubectl delete pod sample-deployment-86b68b4c5b-8v4dg

pod "sample-deployment-86b68b4c5b-8v4dg" deleted

状況を確認してみましょう。

$ kubectl get pods

NAME                                 READY     STATUS    RESTARTS   AGE
sample-deployment-86b68b4c5b-cnj45   1/1       Running   0          2m
sample-deployment-86b68b4c5b-pjpln   1/1       Running   0          2m
sample-deployment-86b68b4c5b-vvcpj   1/1       Running   0          5s

おお、新しく sample-deployment-86b68b4c5b-vvcpj が起動しましたね。これによって、 DeploymentReplicaSet と同等の再起動機能を持っていることがわかりました。

では、nginxを1.12から1.13にアップデートしてみます。

$ kubectl set image deployment sample-deployment nginx-container=nginx:1.13

deployment.apps "sample-deployment" image updated

様子を見てみましょう。

$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-cnj45   1/1       Running       0          7m
sample-deployment-86b68b4c5b-pjpln   1/1       Running       0          7m
sample-deployment-86b68b4c5b-vvcpj   1/1       Terminating   0          5m
sample-deployment-d5b55f699-jhstp    0/1       Pending       0          1s
sample-deployment-d5b55f699-pxpfq    1/1       Running       0          5s

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-cnj45   1/1       Running             0          7m
sample-deployment-86b68b4c5b-pjpln   1/1       Running             0          7m
sample-deployment-86b68b4c5b-vvcpj   0/1       Terminating         0          5m
sample-deployment-d5b55f699-jhstp    0/1       ContainerCreating   0          3s
sample-deployment-d5b55f699-pxpfq    1/1       Running             0          7s

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-cnj45   1/1       Terminating         0          7m
sample-deployment-86b68b4c5b-pjpln   1/1       Running             0          7m
sample-deployment-86b68b4c5b-vvcpj   0/1       Terminating         0          5m
sample-deployment-d5b55f699-jhstp    1/1       Running             0          5s
sample-deployment-d5b55f699-k8fvd    0/1       ContainerCreating   0          2s
sample-deployment-d5b55f699-pxpfq    1/1       Running             0          9s

$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-cnj45   0/1       Terminating   0          7m
sample-deployment-86b68b4c5b-pjpln   1/1       Terminating   0          7m
sample-deployment-86b68b4c5b-vvcpj   0/1       Terminating   0          5m
sample-deployment-d5b55f699-jhstp    1/1       Running       0          7s
sample-deployment-d5b55f699-k8fvd    1/1       Running       0          4s
sample-deployment-d5b55f699-pxpfq    1/1       Running       0          11s

$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-cnj45   0/1       Terminating   0          7m
sample-deployment-86b68b4c5b-pjpln   0/1       Terminating   0          7m
sample-deployment-86b68b4c5b-vvcpj   0/1       Terminating   0          5m
sample-deployment-d5b55f699-jhstp    1/1       Running       0          9s
sample-deployment-d5b55f699-k8fvd    1/1       Running       0          6s
sample-deployment-d5b55f699-pxpfq    1/1       Running       0          13s


$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-pjpln   0/1       Terminating   0          7m
sample-deployment-d5b55f699-jhstp    1/1       Running       0          15s
sample-deployment-d5b55f699-k8fvd    1/1       Running       0          12s
sample-deployment-d5b55f699-pxpfq    1/1       Running       0          19s

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
sample-deployment-d5b55f699-jhstp   1/1       Running   0          21s
sample-deployment-d5b55f699-k8fvd   1/1       Running   0          18s
sample-deployment-d5b55f699-pxpfq   1/1       Running   0          25s

おお、新しいコンテナが立ち上がると同時に、古いコンテナが徐々に消えていきます。想定していた通りの動きとはいえ、なかなか感動ものですね。

さて、いまはコマンドでコンテナのバージョンを指定しましたが、ファイルでバージョンを上げた場合の動きを見てみましょう。

ファイルを書き換えます。書き換えるのはnginxのバージョンのみ。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.13
          ports:
            - containerPort: 80

続いて、変更を反映します。
まずは、一旦全てのコンテナを削除して、 nginx:1.12 で立ち上げ直しましょう。

$ kubectl delete -f sample-deployment.yml 
deployment.apps "sample-deployment" deleted

$ kubectl apply -f sample-deployment.yml 
deployment.apps "sample-deployment" created

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-drg4l   0/1       ContainerCreating   0          3s
sample-deployment-86b68b4c5b-dwb28   0/1       ContainerCreating   0          3s
sample-deployment-86b68b4c5b-k6vkt   0/1       ContainerCreating   0          3s
$ 
$ 
$ vi sample-deployment.yml 
$ kubectl get pods
NAME                                 READY     STATUS    RESTARTS   AGE
sample-deployment-86b68b4c5b-drg4l   1/1       Running   0          27s
sample-deployment-86b68b4c5b-dwb28   1/1       Running   0          27s
sample-deployment-86b68b4c5b-k6vkt   1/1       Running   0          27s

ここで、設定ファイルを修正して nginx:1.13 にアップデートします。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.13
          ports:
            - containerPort: 80
$ kubectl apply -f sample-deployment.yml 
deployment.apps "sample-deployment" configured

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-drg4l   1/1       Running             0          1m
sample-deployment-86b68b4c5b-dwb28   1/1       Running             0          1m
sample-deployment-86b68b4c5b-k6vkt   1/1       Running             0          1m
sample-deployment-d5b55f699-rbftp    0/1       ContainerCreating   0          5s

$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-drg4l   1/1       Running       0          1m
sample-deployment-86b68b4c5b-dwb28   1/1       Terminating   0          1m
sample-deployment-86b68b4c5b-k6vkt   1/1       Running       0          1m
sample-deployment-d5b55f699-rbftp    1/1       Running       0          13s
sample-deployment-d5b55f699-xvm78    0/1       Pending       0          2s

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-drg4l   0/1       Terminating         0          2m
sample-deployment-86b68b4c5b-k6vkt   1/1       Running             0          2m
sample-deployment-d5b55f699-rbftp    1/1       Running             0          49s
sample-deployment-d5b55f699-rwm49    0/1       ContainerCreating   0          13s
sample-deployment-d5b55f699-xvm78    1/1       Running             0          38s

$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-k6vkt   1/1       Terminating   0          2m
sample-deployment-d5b55f699-rbftp    1/1       Running       0          1m
sample-deployment-d5b55f699-rwm49    1/1       Running       0          30s
sample-deployment-d5b55f699-xvm78    1/1       Running       0          55s

$ kubectl get pods
NAME                                READY     STATUS    RESTARTS   AGE
sample-deployment-d5b55f699-rbftp   1/1       Running   0          1m
sample-deployment-d5b55f699-rwm49   1/1       Running   0          1m
sample-deployment-d5b55f699-xvm78   1/1       Running   0          1m
$ 

ロールバック

なんらかの理由で、古いバージョンに戻さなくてはならなくなったとします。まあよくある話です。

$ kubectl rollout undo deployment sample-deployment --to-revision 0

deployment.apps "sample-deployment" 

--to-revision0 を指定した場合、一つ前に戻ります。

状況を確認します。

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-zlnnf   0/1       ContainerCreating   0          3s
sample-deployment-d5b55f699-rbftp    1/1       Running             0          12m
sample-deployment-d5b55f699-rwm49    1/1       Running             0          12m
sample-deployment-d5b55f699-xvm78    1/1       Running             0          12m

$ kubectl get pods
NAME                                 READY     STATUS              RESTARTS   AGE
sample-deployment-86b68b4c5b-zlnnf   0/1       ContainerCreating   0          7s
sample-deployment-d5b55f699-rbftp    1/1       Running             0          12m
sample-deployment-d5b55f699-rwm49    1/1       Running             0          12m
sample-deployment-d5b55f699-xvm78    1/1       Running             0          12m

$ kubectl get pods
NAME                                 READY     STATUS        RESTARTS   AGE
sample-deployment-86b68b4c5b-4skwv   1/1       Running       0          6s
sample-deployment-86b68b4c5b-cfmkh   1/1       Running       0          12s
sample-deployment-86b68b4c5b-zlnnf   1/1       Running       0          20s
sample-deployment-d5b55f699-rbftp    1/1       Terminating   0          12m

$ kubectl get pods
NAME                                 READY     STATUS    RESTARTS   AGE
sample-deployment-86b68b4c5b-4skwv   1/1       Running   0          13s
sample-deployment-86b68b4c5b-cfmkh   1/1       Running   0          19s
sample-deployment-86b68b4c5b-zlnnf   1/1       Running   0          27s

新しいコンテナが立ち上がりました。

Serviceによる内部IPアドレス

先ほどのdeploymentに加えて、以下のServiceを定義して起動する。

sample-clusterip.yml
apiVersion: v1
kind: Service
metadata:
  name: sample-clusterip
spec:
  type: ClusterIP
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

ちなみに、おさらいしておくと、deploymentのほうはこんな定義になってるはず。

sample-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: sample-app
  template:
    metadata:
      labels:
        app: sample-app
    spec:
      containers:
        - name: nginx-container
          image: nginx:1.13
          ports:
            - containerPort: 80

ここで、deplymentのほうで app: sample-app としておいて、serviceの方で、 selector: app: sample-app としているので、serviceは自身の8080番ポートへ届いたパケットを、deploymentの3つのレプリカへ転送することになる。

まず、念のためdeploymentをapply

$ kubectl apply -f sample-deployment.yml

続いて、serviceをapply

$ kubectl apply -f sample-clusterip.yml

これでServiceとDeploymentか起動してるはず。

$ kubectl get pods,services

NAME                                    READY     STATUS    RESTARTS   AGE
pod/sample-deployment-d5b55f699-dpdmz   1/1       Running   0          3m
pod/sample-deployment-d5b55f699-sh452   1/1       Running   0          3m
pod/sample-deployment-d5b55f699-x557b   1/1       Running   0          3m

NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/kubernetes         ClusterIP   10.96.0.1        <none>        443/TCP    10h
service/sample-clusterip   ClusterIP   10.111.247.243   <none>        8080/TCP   8s

443番ポートにも何かサービスが立ち上がってるけど、これはデフォルトで立ち上がってるものだろうか...?
それ以外はpodもserviceも先ほど定義したものが立ち上がっています。

Serviceの状態を確認します。

$ kubectl describe svc sample-clusterip

Name:              sample-clusterip
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"sample-clusterip","namespace":"default"},"spec":{"ports":[{"name":"http-port",...
Selector:          app=sample-app
Type:              ClusterIP
IP:                10.111.247.243
Port:              http-port  8080/TCP
TargetPort:        80/TCP
Endpoints:         172.17.0.4:80,172.17.0.5:80,172.17.0.6:80
Session Affinity:  None
Events:            <none>

IP がServiceのIPアドレス、 Endpoints がトラフィックを割り振る各コンテナのIPアドレス、 Port はServiceのポート番号、 TargetPort は各コンテナのサービスポートみたい。

各コンテナのhostnameをnginxのwwwrootにコピーして、アクセスしたコンテナがわかるようにします。

$ for PODNAME in `kubectl get pods -l app=sample-app -o jsonpath='{.items[*].metadata.name}'`; do
  kubectl exec -it ${PODNAME} -- cp /etc/hostname /usr/share/nginx/html/index.html;                                           done

ClusterIPは外部からはアクセスできないので、一時コンテナを立ち上げてチェック

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080
sample-deployment-d5b55f699-sh452

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080
sample-deployment-d5b55f699-x557b

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080
sample-deployment-d5b55f699-x557b

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080
sample-deployment-d5b55f699-sh452

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080
sample-deployment-d5b55f699-x557b

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080
sample-deployment-d5b55f699-dpdmz

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://10.111.247.243:8080

アクセスするごとにコンテナ名が変わっているので、複数コンテナに割り振られているらしい。

名前解決

上記はIPアドレスでアクセスしたが、kubernetesでは内部DNSによって自動的に名前付けが行われるらしい。
ためしに今回定義した sample-clusterip という名前でアクセスしてみる。

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://sample-clusterip:8080

sample-deployment-d5b55f699-x557b

おお、何も名前解決のための設定をしていないのに解決できた。アプリケーションはこちらを使って接続先を定義した方が、毎回書き換える必要がなくてよさそうだ。

ただし、先ほどは同じネームスペース内からのアクセスだから sample-clusterip で良かったが、本来はこちらが正確らしい。

$ kubectl run --image=centos:6 --restart=Never --rm -i testpod -- curl -s http://sample-clusterip.default.svc.cluster.local:8080

sample-deployment-d5b55f699-dpdmz

[Serivice名].[Namespace名].svc.cluster.local が正式なFQDNになる。

ExternalIPによるサービス内部公開

ClusterIPでは内部ロードバランサを定義しましたが、これはあくまで内部で使うのものなので、外部からアクセスすることはできない。
けど、Webサーバーなどで外部公開が目的のものは外部に公開できなければならない。この場合はExternalIPを使う。
ExternalIPといっても実態はClusterIPで、ClusterIPに spec.externalIPs にノードのIPアドレスを定義すると使えるようになる。

最初にノードのIPアドレスを調べる。minikubeを使っている場合は、仮想環境のアドレスであってMacのアドレスではないので注意が必要。

$ kubectl cluster-info

Kubernetes master is running at https://192.168.99.100:8443
KubeDNS is running at https://192.168.99.100:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

これをみると、どうやらノードのIPアドレスは 192.168.99.100 のようだ。
このアドレスを新たに作った sample-externalip.yml に設定してやる。

sample-externalip.yml
apiVersion: v1
kind: Service
metadata:
  name: sample-externalip
spec:
  type: ClusterIP
  externalIPs:
    -  192.168.99.100
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
  selector:
    app: sample-app

applyする。

$ kubectl apply -f sample-externalip.yml 

状態を見てみよう。

$ kubectl describe svc sample-externalip

Name:              sample-externalip
Namespace:         default
Labels:            <none>
Annotations:       kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"sample-externalip","namespace":"default"},"spec":{"externalIPs":["192.168.99.1...
Selector:          app=sample-app
Type:              ClusterIP
IP:                10.103.102.209
External IPs:      192.168.99.100
Port:              http-port  8080/TCP
TargetPort:        80/TCP
Endpoints:         172.17.0.4:80,172.17.0.5:80,172.17.0.6:80
Session Affinity:  None
Events:            <none>

External IPs として設定したIPアドレスが割り付けられている。
curlで外部からアクセスしてみよう。

$ curl http://192.168.99.100:8080
sample-deployment-d5b55f699-dpdmz

$ curl http://192.168.99.100:8080
sample-deployment-d5b55f699-sh452

$ curl http://192.168.99.100:8080
sample-deployment-d5b55f699-dpdmz

$ curl http://192.168.99.100:8080
sample-deployment-d5b55f699-x557b

$ curl http://192.168.99.100:8080
sample-deployment-d5b55f699-dpdmz

リクエストが各コンテナに割り振られているのが確認できた。

NodePortによる、全ノードでのListen

ExternalIPを使うと、指定したIPアドレスのノードでListenしますが、NodePortを使うと参加している全てのノードの指定ポートで待機するようになるみたいです。やってみましょう。

sample-nodeport.yml
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport
spec:
  type: NodePort 
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
      nodePort: 30080
  selector:
    app: sample-app

ExternalIPの定義とよく似ているが、違うのは

  • spec.typeNodePort を指定。
  • spec.externalIPs を定義しない。
  • spec.ports.nodePort に公開するポート番号を指定する。

のようです。

さっそくapplyします。

$ kubectl apply -f sample-nodeport.yml
 
service "sample-nodeport" created

curlでアクセスしてみる。

$ curl http://192.168.99.100:30080

sample-deployment-d5b55f699-dpdmz

ふう。
とりえあず動作が確認できたので削除。

$ kubectl delete -f sample-nodeport.yml
service "sample-nodeport" deleted

$ kubectl delete -f sample-cluster.yaml 
service "sample-clusterip" deleted

LoadBalancerによる外部公開

サービスを外部公開する場合、結局これになるんだろうな〜、っていう。いわゆるロードバランサー。
LoadBalancerの実装はKubernetesのノードがどこで動いているかに依存するようです。
まだ試してませんが、AWSであればLoadBalancerを定義するとElasticIPを取って来てくれるんでしょうかね。

設定はこんな感じで

sample-loadbalancer.yml
apiVersion: v1
kind: Service
metadata:
  name: sample-lb
spec:
  type: LoadBalancer
  loadBalancerIP: 192.168.99.100
  ports:
    - name: "http-port"
      protocol: "TCP"
      port: 8080
      targetPort: 80
      nodePort: 30082
  selector:
    app: sample-app

loadbalancerIPは定義しなくてもいいけど、普通は外部公開するサービスであればIPアドレスは固定になるので定義した。
定義しない場合は自動的に割り振られる。
起動します。

$ kubectl apply -f sample-loadbalancer.yml 

service "sample-lb" created

サービスの様子を確認します。

$ kubectl get services

NAME         TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP      10.96.0.1       <none>        443/TCP          14h
sample-lb    LoadBalancer   10.106.15.138   <pending>     8080:30082/TCP   16m

あれ、sample-lbのEXTERNAL-IPがpendingのまま。

このままではどうすればいいかわからないので、minikubeコマンドでURL取得。

$ minikube service sample-lb --url

http://192.168.99.100:30082

とうことで、curlでアクセス。

$ curl http://192.168.99.100:30082
sample-deployment-d5b55f699-sh452

$ curl http://192.168.99.100:30082
sample-deployment-d5b55f699-x557b

ほーん。

28
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?