Sep 1, 2019
Jan 1, 1970
N/A Views
MD
warning
この記事は2年以上前に更新されたものです。情報が古くなっている可能性があります。

Kubernetesにアプリケーションをデプロイする上で最も基本的なPod, ReplicaSet, Deploymentについて説明します。

Podはアプリケーションを動かすための最小の単位で、1つ以上のコンテナから構成されます。
ReplicaSetはPodを複製し、指定されたレプリカ数を維持するようにPodを管理します。
DeploymentはReplicaSetを生成、管理しローリングアップデートやロールバックを実現します。

image

通常、アプリケーションをデプロイする際はDeploymentを作成し、ReplicaSetやPodはそれぞれDeployment、ReplicaSetに管理させます。

目次

Podのデプロイ

まずはPod単体でデプロイすることから試しましょう。

hello-pks-pod.ymlを作成して、次の内容を記述してください。

apiVersion: v1
kind: Pod
metadata:
  name: hello-pks
spec:
  containers:
  - image: making/hello-pks:0.0.1
    name: hello-pks
    ports:
    - containerPort: 8080

このmanifestファイルを使い、次のコマンドでPodをデプロイします。

kubectl apply -f hello-pks-pod.yml

出力結果

pod/hello-pks created

kubectl get podでPodを一覧表示します。

kubectl get pod

出力結果

NAME        READY   STATUS    RESTARTS   AGE
hello-pks   1/1     Running   0          25s

kubectl describe pod <Pod名>でPodの詳細情報を確認できます。

kubectl describe pod hello-pks

出力結果

Name:               hello-pks
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               ip-10-0-8-6.ap-northeast-1.compute.internal/10.0.8.6
Start Time:         Mon, 02 Sep 2019 01:56:51 +0900
Labels:             <none>
Annotations:        kubectl.kubernetes.io/last-applied-configuration:
                      {"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"hello-pks","namespace":"default"},"spec":{"containers":[{"image":"mak...
Status:             Running
IP:                 10.200.74.52
Containers:
  hello-pks:
    Container ID:   docker://3e6c04ce22a7ec591a5f485bded17a83ecbd89eb8e0e100a7836765acf3498a9
    Image:          making/hello-pks:0.0.1
    Image ID:       docker-pullable://making/hello-pks@sha256:cdceb399a5b4a11216792d662dbc23ecbdef04d6e49b9af48fd730a0173384cc
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Mon, 02 Sep 2019 01:57:12 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-hdrng (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-hdrng:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-hdrng
    Optional:    false
QoS Class:       BestEffort
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  38s   default-scheduler                                     Successfully assigned default/hello-pks to ip-10-0-8-6.ap-northeast-1.compute.internal
  Normal  Pulling    37s   kubelet, ip-10-0-8-6.ap-northeast-1.compute.internal  Pulling image "making/hello-pks:0.0.1"
  Normal  Pulled     18s   kubelet, ip-10-0-8-6.ap-northeast-1.compute.internal  Successfully pulled image "making/hello-pks:0.0.1"
  Normal  Created    17s   kubelet, ip-10-0-8-6.ap-northeast-1.compute.internal  Created container hello-pks
  Normal  Started    17s   kubelet, ip-10-0-8-6.ap-northeast-1.compute.internal  Started container hello-pks

PodにアクセスするためPodの8080番ポートをlocalhostの8080番ポートにフォワードします。

kubectl port-forward hello-pks 8080:8080

出力結果

Forwarding from 127.0.0.1:8080 -> 8080
Forwarding from [::1]:8080 -> 8080

既存のプロセスが8080番ポートを使用している場合はエラーになります。kill $(lsof -i:8080 | awk 'NR>1 { print $2}')等でプロセスを終了してください。

ターミナルからhttp://localhost:8080/actuator/healthにアクセスして下さい。

curl http://localhost:8080/actuator/health

出力結果

{
  "status" : "UP"
}

kubectl deleteで作成したリソースを削除してください。

kubectl delete -f hello-pks-pod.yml 

出力結果

pod "hello-pks" deleted

Ctrl+Cでkubectl port-forwardを止めてください。

ReplicaSetのデプロイ

ReplicaSetはPodを複製し、複数インスタンスで起動したり、指定したレプリカ数を維持するように管理します。

hello-pks-rs.ymlを作成して、次の内容を記述してください。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: hello-pks
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.1
        name: hello-pks
        ports:
        - containerPort: 8080

spec.template以下が複製するPodの情報です。レプリカ数(spec.replicas)が1なので、この場合は1Podのみ作成されます。
spec.template.metadata.labelsに設定したラベルをspec.selector.matchLabelsに設定して結びつける必要があります。

kubectl applyでデプロイします。

kubectl apply -f hello-pks-rs.yml

出力結果

replicaset.apps/hello-pks created

kubectl get pod,rsでPodとReplicaSetを一覧表示します。

kubectl get pod,rs

出力結果

NAME                  READY   STATUS    RESTARTS   AGE
pod/hello-pks-gvgh6   1/1     Running   0          9s

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.extensions/hello-pks   1         1         1       9s

1Pod作成されていることがわかります。Pod名は"ReplicaSet名-一意な文字列"になっています。

次にレプリカ数を2に変更します。

# ...
spec:
  replicas: 2
# ...

再度、kubectl applyを実行してください。

kubectl apply -f hello-pks-rs.yml

出力結果

replicaset.apps/hello-pks configured

kubectl get pod,rsでPodとReplicaSet一覧を確認できます。

kubectl get pod,rs

出力結果

NAME                 READY     STATUS    RESTARTS   AGE
po/hello-pks-dp9tb   1/1       Running   0          15s
po/hello-pks-x9gdm   1/1       Running   0          3s

NAME           DESIRED   CURRENT   READY     AGE
rs/hello-pks   2         2         2         23m

hello-pksから始まる名前を持つPodが2つ作成されていることがわかります。

次は-o wideをつけて実行してください。

$ kubectl get pod -o wide

出力結果

NAME              READY   STATUS    RESTARTS   AGE   IP             NODE                                          NOMINATED NODE   READINESS GATES
hello-pks-gvgh6   1/1     Running   0          93s   10.200.74.53   ip-10-0-8-6.ap-northeast-1.compute.internal   <none>           <none>
hello-pks-r7qx6   1/1     Running   0          52s   10.200.6.17    ip-10-0-9-4.ap-northeast-1.compute.internal   <none>           <none>

NODE列にPodが起動しているWorker Nodeが表示されています。Nodeが複数台ある場合は、NODE列に別々の値が表示されるかもしれません。

ここでPodを一つ削除して見ます。

kubectl delete pod hello-pks-gvgh6

出力結果

pod "hello-pks-gvgh6" deleted

この状態で、Pod一覧を表示してください。

kubectl get pod,rs

出力結果

NAME                  READY     STATUS        RESTARTS   AGE
pod/hello-pks-gvgh6   0/1       Terminating   0          29s
pod/hello-pks-r7qx6   1/1       Running       0          17s
pod/hello-pks-tml22   1/1       Running       0          1s

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.extensions/hello-pks   2         2         2       29s

1つPodが削除されるとほぼ同時に新しいPodが起動しています。しばらくすると再びPodが2つ起動している状態になります。

kubectl get pod,rs

出力結果

NAME                  READY   STATUS    RESTARTS   AGE
pod/hello-pks-r7qx6   1/1     Running   0          4m19s
pod/hello-pks-tml22   1/1     Running   0          21s

NAME                              DESIRED   CURRENT   READY   AGE
replicaset.extensions/hello-pks   2         2         2       5m

Podのインスタンス数を管理しているのはReplicaSetであるため、kubectlコマンドでPodを意図的に削除してもReplicaSetにより、元々指定していたインスタンス数になるように復元されます。

kubectl describe rs <ReplicaSet名>でReplicaSetの詳細情報を確認できます。

kubectl describe rs hello-pks

出力結果

Name:         hello-pks
Namespace:    default
Selector:     app=hello-pks
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"apps/v1","kind":"ReplicaSet","metadata":{"annotations":{},"name":"hello-pks","namespace":"default"},"spec":{"replicas":2,"s...
Replicas:     2 current / 2 desired
Pods Status:  2 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=hello-pks
  Containers:
   hello-pks:
    Image:        making/hello-pks:0.0.1
    Port:         8080/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age    From                   Message
  ----    ------            ----   ----                   -------
  Normal  SuccessfulCreate  8m20s  replicaset-controller  Created pod: hello-pks-gvgh6
  Normal  SuccessfulCreate  7m39s  replicaset-controller  Created pod: hello-pks-r7qx6
  Normal  SuccessfulCreate  3m41s  replicaset-controller  Created pod: hello-pks-tml22

Events:にReplicaSetが作成したPodの情報が記載されています。

作成したReplicaSetは削除してください。

kubectl delete -f hello-pks-rs.yml

出力結果

replicaset.apps "hello-pks" deleted

Deploymentのデプロイ

DeploymentはReplicaSetを管理し、ローリングアップデートできるようにします。

hello-pks.ymlを作成して、次の内容を記述してください。(hello-pks-rs.ymlとの違いはkindReplicaSetからDeploymentになっただけです。)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-pks
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.1
        name: hello-pks
        ports:
        - containerPort: 8080

kubectl applyでデプロイします。

kubectl apply -f hello-pks.yml

出力結果

deployment.apps/hello-pks created

kubectl get pod,rs,deployでPodとReplicaSetおよびDeployment一覧を確認できます。

kubectl get pod,rs,deploy

出力結果

NAME                             READY   STATUS    RESTARTS   AGE
pod/hello-pks-8658768855-526jn   1/1     Running   0          10s
pod/hello-pks-8658768855-6h7sp   1/1     Running   0          10s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.extensions/hello-pks-8658768855   2         2         2       11s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/hello-pks   2/2     2            2           11s

名前がDeployment名-一意な文字列であるReplicaSetができていることがわかります。そしてこのReplicaSetによって2つのPodができていることがわかります。

kubectl describe <Deployment名>でDeploymentの詳細情報を確認できます。

kubectl describe deploy hello-pks

出力結果

Name:                   hello-pks
Namespace:              default
CreationTimestamp:      Mon, 02 Sep 2019 02:09:59 +0900
Labels:                 <none>
Annotations:            deployment.kubernetes.io/revision: 1
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"hello-pks","namespace":"default"},"spec":{"replicas":2,"s...
Selector:               app=hello-pks
Replicas:               2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=hello-pks
  Containers:
   hello-pks:
    Image:        making/hello-pks:0.0.1
    Port:         8080/TCP
    Host Port:    0/TCP
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   hello-pks-8658768855 (2/2 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  35s   deployment-controller  Scaled up replica set hello-pks-8658768855 to 2

Serviceのデプロイ

ここまでPodへのアクセスはPort Forwardingを利用して行いましたが、ReplicaSetによって複製されたPodへロードバランスすることはできていません。
PodをKubernetesクラスタ内外に公開し、ロードバランシングの機能も提供するのがServiceです。
ServiceのTypeはいくつか用意されており、次の3つが代表的です。

  • ClusterIP: クラスタ内にのみ公開される。デフォルトのService Type。
  • NodePort: Nodeのポートを経由して公開される。クラスタ外からアクセス可能。
  • LoadBalancer: クラウドプロバイダ等が提供する外部ロードバランサを経由して公開される。ロードバランサは動的に作成される。

ClusterIPNodePortは全てのクラスタで利用可能ですが、全てのk8s環境でLoadBalancerが対応されているわけではありません。
本資料が想定としている環境のLoadBalancer対応状況は次の表の通りです。

環境 NSX-Tなし NSX-Tあり
PKS on vSphere
PKS on GCP -
PKS on AWS -
PKS on Azure -

ここではNodePortのServiceを作成する例を説明します。

Type=NodePort

LoadBalancerに対応していない環境ではNodePortのServiceを作成します。

hello-pks.ymlを次の内容を変更してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-pks
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.1
        name: hello-pks
        ports:
        - containerPort: 8080

# ここから追記
---
kind: Service
apiVersion: v1
metadata:
  name: hello-pks
spec:
  type: NodePort
  selector:
    app: hello-pks
  ports:
  - protocol: TCP
    port: 8080

spec.selectorにはPodに設定したラベルを、spec.ports[].portには公開対象のPodのポートを指定します。

kubectl applyでデプロイしてください。

kubectl apply -f hello-pks.yml

出力結果

deployment.apps/hello-pks unchanged
service/hello-pks created

kubectl get serviceでService一覧を確認できます。

kubectl get pod,rs,deploy,service

出力結果

NAME                             READY   STATUS    RESTARTS   AGE
pod/hello-pks-8658768855-526jn   1/1     Running   0          4m27s
pod/hello-pks-8658768855-6h7sp   1/1     Running   0          4m27s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.extensions/hello-pks-8658768855   2         2         2       4m28s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/hello-pks   2/2     2            2           4m28s

NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/hello-pks    NodePort    10.100.200.190   <none>        8080:32569/TCP   15s
service/kubernetes   ClusterIP   10.100.200.1     <none>        443/TCP          230d

svc/hello-pks-servicePORT(S)列の8080:32569に注目してください。左がPodのIPで右がNode VMでlistenしているポートです。
この場合は、Node VMのIPの32569番ポートにアクセスするとhello-pksのPodにルーティングされます。ポートはデフォルトでは30000から32767で空いているポートがランダムで選択されます。
Node上のポートを固定したい場合は、次のように指定可能です。

spec:
  type: NodePort
  selector:
    app: hello-pks
  ports:
  - protocol: TCP
    port: 8080
    nodePort: 32569

Node VMのIPはkubectl get node -o wideの結果のEXTERNAL-IP列から取得できます。
Node VMが複数ある場合は、どのNode VMにアクセスしても指定のPodへルーティングされます。

PKS on GCPやGKEでNode VMにインターネットからアクセスする場合は、Firewallの設定を変更してポートをポートを開放する必要があります。
gcloud compute instances list --format=jsonでNode VMに設定されているNetwork Tagを取得し、
gcloud compute firewall-rules create k8s-allow-node-port --allow tcp:30000-32767 --target-tags <Node VMのNetwork Tag> --network <Node VMのNetwork Name>

Node VMの32569ポートにアクセスします。Nodeが複数ある場合はどれか一つのIPアドレスを選んでください。

curl http://<Worker NodeのIP>:32569/actuator/health

出力結果

{
  "status" : "UP"
}

Type=LoadBalancer

LoadBalancerに対応している環境ではLoadBalancerのServiceを作成します。

hello-pks.ymlを次の内容を変更してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-pks
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.1
        name: hello-pks
        ports:
        - containerPort: 8080

# ここから追記
---
kind: Service
apiVersion: v1
metadata:
  name: hello-pks
spec:
  type: LoadBalancer
  selector:
    app: hello-pks
  ports:
  - protocol: TCP
    port: 8080

spec.selectorにはPodに設定したラベルを、spec.ports[].portには公開対象のPodのポートを指定します。

kubectl applyでデプロイしてください。

kubectl apply -f hello-pks.yml

出力結果

deployment.apps/hello-pks unchanged
service/hello-pks configured

kubectl get serviceでService一覧を確認できます。

kubectl get pod,rs,deploy,service

出力結果

NAME                             READY   STATUS    RESTARTS   AGE
pod/hello-pks-8658768855-526jn   1/1     Running   0          8m43s
pod/hello-pks-8658768855-6h7sp   1/1     Running   0          8m43s

NAME                                         DESIRED   CURRENT   READY   AGE
replicaset.extensions/hello-pks-8658768855   2         2         2       8m44s

NAME                              READY   UP-TO-DATE   AVAILABLE   AGE
deployment.extensions/hello-pks   2/2     2            2           8m44s

NAME                 TYPE           CLUSTER-IP       EXTERNAL-IP                                                                    PORT(S)          AGE
service/hello-pks    LoadBalancer   10.100.200.190   aeb43003accdb11e99cb7066f53a5a1a-2093361350.ap-northeast-1.elb.amazonaws.com   8080:32569/TCP   4m31s
service/kubernetes   ClusterIP      10.100.200.1     <none>  

service/hello-pksEXTERNAL-IP列にアクセス可能なLoad BalancerのIPアドレスまたはホスト名が表示されます。
このIPアドレスがアタッチされた外部のLoadBalancerを経由してServiceにアクセス可能になりました。
<pending>になっている場合はLoad Balancer作成中またはエラーが起きてLoad Balancerの作成に失敗しています。kubectl describe service hello-pkdで状態を確認してください。

curlhttp://<EXTERNAL-IP>:8080/actuator/healthにアクセスしてください。

curl http://aeb43003accdb11e99cb7066f53a5a1a-2093361350.ap-northeast-1.elb.amazonaws.com:8080/actuator/health

出力結果

{
  "status" : "UP"
}

Node/Pod情報の表示

このままではロードバランスがされていることがわからないため、アクセスしているNodeとPodの情報をレスポンスボディに含めるようにします。
making/hello-pksアプリは環境変数がNODEから始まるものとPODから始まるものがレスポンスボディに含まれるように作られています。

Podの環境変数にNodeとPodの情報を埋め込みます。NodeやPodの情報はDownload APIを使って取得できます。

hello-pks.ymlを次のように修正してください。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-pks
spec:
  replicas: 2
  selector:
    matchLabels:
      app: hello-pks
  template:
    metadata:
      labels:
        app: hello-pks
    spec:
      containers:
      - image: making/hello-pks:0.0.1
        name: hello-pks
        ports:
        - containerPort: 8080
        # ここから追加
        env:
        - name: NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: NODE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        # ここまで
---
kind: Service
apiVersion: v1
metadata:
  name: hello-pks
spec:
  type: LoadBalancer
  selector:
    app: hello-pks
  ports:
  - protocol: TCP
    port: 8080

kubectl applyで変更内容を適用します。

kubectl apply -f hello-pks.yml

出力結果

deployment.apps/hello-pks configured
service/hello-pks unchanged

kubectl get podでPodのSTATUSRunningになっていることを確認したら、curlで、

  • NodePortの場合http://<Node IP>:<hello-pksのNode Port>
  • LoadBalancerの場合http://<EXTERNAL IP>:8080

にアクセスしてください。

curl http://aeb43003accdb11e99cb7066f53a5a1a-2093361350.ap-northeast-1.elb.amazonaws.com:8080

出力結果

{
  "node" : {
    "NODE_IP" : "10.0.8.6",
    "NODE_NAME" : "ip-10-0-8-6.ap-northeast-1.compute.internal"
  },
  "pod" : {
    "POD_IP" : "10.200.74.57",
    "POD_NAME" : "hello-pks-795466b574-lfr2z",
    "POD_NAMESPACE" : "default"
  },
  "container" : { }
}

NodeとPodの情報が返ります。
何回かアクセスして、異なるPodの情報が表示されることを確認してください。

{
  "node" : {
    "NODE_IP" : "10.0.8.6",
    "NODE_NAME" : "ip-10-0-8-6.ap-northeast-1.compute.internal"
  },
  "pod" : {
    "POD_IP" : "10.200.74.56",
    "POD_NAME" : "hello-pks-795466b574-75dcz",
    "POD_NAMESPACE" : "default"
  },
  "container" : { }
}

WebブラウザでアクセスするとHTMLで表示されます。

image

"SHUTDOWN"ボタンを押すとアプリケーションが終了します。ボタンを押したあとはReplicaSetによってPodが再作成されますが、
それまでは1 Podだけでリクエストを捌きます。"SHUTDOWN"ボタンを押した後、別のPodの情報が表示されることを確認してください。

kubectl deleteでdeplymentとserviceを削除してください。

kubectl delete -f hello-pks.yml 

出力結果

deployment.apps "hello-pks" deleted
service "hello-pks" deleted
Found a mistake? Update the entry.
Share this article: