LoginSignup
7
13

More than 3 years have passed since last update.

【#Kubernetes】Google Kubernetes Engine(GKE)を使ってKubernetesに入門してみる(Ingress、Deployment、StatefulSet編) #GKE #GCP #k8sjp #gcpug

Last updated at Posted at 2019-08-10

"Google Kubernetes Engine(GKE)"を使って"Kubernetes"について勉強した際の内容を自分用のメモとしてまとめました

"Kubernetesクラスタ"内のノードについて

"Kubernetesクラスタ"は大きく2種類のノードで構成されます

  • Control Planeとなるマスターノード
  • コンテナ実行環境となるワーカーノード

"Google Kubernetes Engine(GKE)"では、マスターノード側はフルマネージド提供され課金対象にもなりません。そのためワーカーノードのみ作成すれば、すぐに"Kubernetesクラスタ"環境を利用することが可能です。

"Kubernetesクラスタ"内のオブジェクトについて

"Kubernetesクラスタ"上のオブジェクトは、IngressServicePodの3つのレイヤーに別けることができます

1_KIVa4hUVZxg-8Ncabo8pdg.png
出典: Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what?
https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0

種類 説明
Ingress ”Kubernetesクラスタの外部からのトラフィック”を制御する"L7アプリケーションデリバリーコントローラー(ADC)"です。”Ingress”は”ワーカーノード”とは別の場所に、"Google Kubernetes Engine(GKE)"環境においては、"Google Cloud Load Balancing"ベースのソフトウェア定義として作成されます。
Service ”Ingressからのトラフィック”もしくは”同一Kubernetesクラスタ内部からのトラフィック”を制御する”L4ロードバランサー”です。
Pod ”ワーカーノード”上に作成する”コンテナ””ボリューム”です。”Pod”のデプロイ方式を管理するためのオブジェクト(ワークロード)として別途”Deployment”、”StatefulSet”、”DaemonSet”、”CronJob”といったオブジェクトがありますが、これらの説明については後述します。”Pod”内の永続的ボリュームを管理するオブジェクトとして”PersistentVolumeClaim”がありますが、こちらの説明についても後述します。

"GKE"を使って次の3つを実施し"Kubernetesクラスタ"上にWordpressのシステムを構築することを通じて"Kubernetesクラスタ"の仕組みを理解することを目指します

  • スタティックなグローバルIPアドレスを1つ割り当てたIngressの作成
  • HorizontalPodAutoscaler(HPA)を使ったオートスケールするWebサーバーのPodServiceの作成
  • StatefulSetを使った永続的なDBMS(MySQL)のPodServiceの作成

1. "Google Kubernetes Engine(GKE)"上にワーカーノードのクラスタを作成する

まず、ワーカーノードのクラスタの作成を"Google Cloud Platform(GCP)"のWebコンソールから行います。

□手順1-1: "Google Cloud Platform(GCP)"のWebコンソールにログインします

capture_001_04082019_131138.jpg

□手順1-2: ナビゲーションメニューからGoogle Kubernetes Engine>クラスタを選択します

capture_002_04082019_131224.jpg

□手順1-3: 画面右上のCloud Shellアイコンをクリックします

capture_001_08082019_161012.jpg

□手順1-4: 画面下にCloud Shellが表示されるまでしばらく待ちます

capture_001_09082019_161452.jpg

□手順1-5: gcloud config setコマンドでregionの設定を行います

asia-northeast1(東京リージョン)を指定します

CloudShell
$ gcloud config set compute/region "asia-northeast1"
Updated property [compute/region].

□手順1-6: 続けてgcloud config setコマンドでzoneの設定を行います

CloudShell
$ gcloud config set compute/zone "asia-northeast1-a"
Updated property [compute/zone].

□手順1-6: cloud container clusters createコマンドでワーカーノードのクラスタを作成します

my-worker-node-cluseterという名前で作成します

CloudShell
$ gcloud container clusters create "my-worker-node-cluseter"

□手順1-7: ワーカーノードのクラスタが作成されるまでしばく待ちます

capture_002_09082019_161936.jpg

□手順1-8: kubectl get allコマンドを実行してワーカーノードのクラスタが作成されたことを確認します

$ kubectl get all
NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.15.240.1   <none>        443/TCP   91s

上記のように表示されれば、ワーカーノードのクラスタ作成は完了です

参考: gcloud compute instances listコマンドで個別のワーカーノードの情報を確認することができます

CloudShell
$ gcloud compute instances list
NAME                                                 ZONE               MACHINE_TYPE   PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP     STATUS
gke-my-worker-node-cluse-default-pool-924284ce-bm6s  asia-northeast1-a  n1-standard-1               10.146.0.15  35.243.112.157  RUNNING
gke-my-worker-node-cluse-default-pool-924284ce-kpcs  asia-northeast1-a  n1-standard-1               10.146.0.16  104.198.83.208  RUNNING
gke-my-worker-node-cluse-default-pool-924284ce-td1m  asia-northeast1-a  n1-standard-1               10.146.0.17  104.198.120.68  RUNNING

2. Manifestの作成と適用

"Kubernetesクラスタ"上に作成する各種オブジェクトについては、コマンドで個別に作成することも可能ですがManifestと呼ばれるYAML形式のファイルを作成することにより一括した管理が可能になります

このManifestの作成単位についてはいろいろ考え方があると思いますが、ここでは、ワーカーノードの外に作成されるIngressについては独立して1つ作成し、ワーカーノード上に作成されるその他のオブジェクトについてはServiceの単位で作成したいと思います

□手順2-1: gcloud compute address createコマンドでGCP上にIngressに割り当てるためのスタティックなグローバルIPアドレスを1つ作成します

CloudShell
$ gcloud compute addresses create "my-ingress-address" --global
Created [https://www.googleapis.com/compute/v1/projects/proud-command-246805/global/addresses/my-ingress-address].

□手順2-2: gcloud compute address listコマンドで割り当てられたスタティックなグローバルIPアドレスを確認します

CloudShell
$ gcloud compute addresses list
NAME                ADDRESS/RANGE  TYPE      PURPOSE  NETWORK  REGION  SUBNET  STATUS
my-ingress-address  XXX.XXX.XXX.XXX  EXTERNAL                                    RESERVED

□手順2-3: Manifestを配置するためのディレクトリを作成してカレントディレクトリを移動します

CloudShell
$ export MANIFESTS_PATH=$HOME/my-manifests
$ mkdir -p $MANIFESTS_PATH
$ cd $MANIFESTS_PATH
$ pwd

□手順2-4: kubectl create namespaceコマンドでnamespaceを作成します

my-wordpressという名前でnamespaceを作成します

CloudShell
$ kubectl create namespace "my-wordpress"
namespace/my-wordpress created
CloudShell
$ kubectl get namespace
NAME           STATUS   AGE
default        Active   24m
kube-public    Active   24m
kube-system    Active   24m
my-wordpress   Active   22s

□手順2-5: この後のコマンドの実行を簡略化するためにkubectl config set-contextコマンドでデフォルトで使用するnamespaceを設定します

CloudShell
$ kubectl config set-context $(kubectl config current-context) --namespace="my-wordpress"
Context "gke_proud-command-246805_asia-northeast1-a_my-worker-node-cluseter" modified.

□手順2-6: まずnamespace名のディレクトリを作成します

CloudShell
$ mkdir -p $MANIFESTS_PATH/my-wordpress

□手順2-7: 作成したディレクトリ下にIngressManifest(my-ingress.yaml)を作成します

このManifestでは、Ingressのオブジェクトのみを定義しています

CloudShell
$ vi $MANIFESTS_PATH/my-wordpress/my-ingress.yaml
$MANIFESTS_PATH/my-wordpress/my-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress  # このIngressの名前を定義します
  namespace: my-wordpress
  annotations:
    kubernetes.io/ingress.global-static-ip-name: my-ingress-address  # 割り当てるスタティックIPアドレスを定義します
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:  # バックエンドのServiceとポート番号を定義します
          serviceName: web-service
          servicePort: 80

□手順2-8: 続いて、Webサーバー側のServiceManifest(web-service.yaml)を作成します

このManifestでは、ServiceDeploymentHorizontalPodAutoscalerの3種類のオブジェクトを定義しています

Deploymentはステートレス(非永続的)なPodを管理するためのオブジェクトです

HorizontalPodAutoscalerPodのオートスケールを定義するためのオブジェクトです

HorizontalPodAutoscalerは指定したメトリックを30秒間隔で監視し、targetAverageValueで指定した値未満になるようPod数をminReplicasおよびmaxReplicasの間で自動調整します

CloudShell
$ vi $MANIFESTS_PATH/my-wordpress/web-service.yaml
$MANIFESTS_PATH/my-wordpress/web-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: web-service  # このServiceの名前を定義します
  namespace: my-wordpress
  labels:  # このServiceに付与するラベルを定義します
    ServiceName: web-service
    Environment: development
spec:
  type: NodePort  # Kubernetesクラスタ外部からのトラフィックも許可する場合はNodePortを設定します
  ports:  # Serviceが使用するポート番号(port)と紐づけを行うPod側のポート番号(targetPort)、Node側のポート番号(nodePort)を定義します
  - port: 80
    targetPort: 80
    nodePort: 30080
    protocol: TCP
  selector:  # このServiceに紐づけるPodをPodに付与したラベルで定義します
    ServiceName: web-service
    Environment: development
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deployment  # このDeploymentの名前を定義します
  namespace: my-wordpress
  labels:  # このDeploymentに付与するラベルを定義します
    ServiceName: web-service
    Environment: development
spec:
  replicas: 3  # 作成するPod数を定義します
  selector:
    matchLabels:  # 作成するPodに付与するラベルを定義します
      ServiceName: web-service
      Environment: development
  template:
    metadata:
      labels:  # spec: -> selector: -> matchLabels: と同じ内容を記載する必要があります
        ServiceName: web-service
        Environment: development
    spec:
      containers:
      - name: web-container  # 作成するPodのコンテナ名を定義します
        ports:  # 作成するPodのコンテナで開放するポート情報を定義します
        - name: http
          containerPort: 80
          protocol: TCP
        resources:
          requests:  # コンテナデプロイ時に必要とするリソースを定義します(リソースが足りない場合デプロイはエラーになります)
            cpu: 100m  # 1vCPU = 1,000m を意味します
            memory: 256Mi
          limits: # コンテナリソースの上限を定義します(ノードのメモリが枯渇しないようにrequestsと同値を設定します)
            memory: 256Mi
        image: wordpress:5.2-apache  # 作成するPodのコンテナ作成時に使用するDockerイメージを定義します
        env:  # 作成するPodのコンテナに設定する環境変数を定義します
        - name: WORDPRESS_DB_HOST
          value: "db-service:3306"
        - name: WORDPRESS_DB_USER
          value: "wordpress"
        - name: WORDPRESS_DB_PASSWORD
          value: "wordpress!"
        - name: WORDPRESS_DB_NAME
          value: "wordpress"
        livenessProbe:  # ヘルスチェックに使用する情報を定義します
          httpGet:
            path: /readme.html
            port: 80
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 3
        readinessProbe:  # ヘルスチェックに使用する情報を定義します
          httpGet:
            path: /readme.html
            port: 80
            scheme: HTTP
          initialDelaySeconds: 10
          periodSeconds: 3
---
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
  name: web-deployment-horizontal-pod-autoscaler
  namespace: my-wordpress
spec:
  scaleTargetRef:  # オートスケールの対象とするDeploymentの名前を定義します
    apiVersion: apps/v1
    kind: Deployment
    name: web-deployment
  minReplicas: 3
  maxReplicas: 10
  metrics:  # オートスケールのトリガーとするメトリックを定義します
  - type: Pods
    pods:  # Pod 1台あたり 100 HTTP Requests per Second 以下になるよう調整します
      metricName: http_requests
      targetAverageValue: 100

参考: HorizontalPodAutoscalerで監視対象として設定するメトリックについて

監視対象として指定するメトリックの種類については、"HTTP Requests per Second"などRED Methodに基づいたものの方が良いと個人的に考えています

コンテナの性質上、CPU使用率/メモリ使用率などUSE Methodに基づいた監視対象メトリックのリソースが不足する場合は、ワーカーノードをオートスケールするように設定した方が良いと考えます

The RED Method: How to Instrument Your Services
https://grafana.com/blog/2018/08/02/the-red-method-how-to-instrument-your-services/

The USE Method
http://www.brendangregg.com/usemethod.html

□手順2-9: DB(MySQL)側のServiceManifest(db-service.yaml)を作成します

このManifestでは、`ServiceStatefulSetPersistentVolumeClaimの3種類のオブジェクトを定義します

StatefulSetDeploymentとは異なりステートフル(永続的)なPodを管理するためのオブジェクトです

永続化を行う際に使用するストレージ領域についてはPersistentVolumeClaimというオブジェクトで定義します

CloudShell
$ vi $MANIFESTS_PATH/my-wordpress/db-service.yaml
$MANIFESTS_PATH/my-wordpress/db-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: db-service  # このServiceの名前を定義します
  namespace: my-wordpress
  labels:  # このServiceに付与するラベルを定義します
    ServiceName: db-service
    Environment: development
spec:
  type: ClusterIP  # Kubernetesクラスタ内のトラフィックのみ許可する場合はClusterIPを設定します
  ports:  # Serviceが使用するポート番号(port)と紐づけを行うPod側のポート番号(targetPort)を定義します
  - port: 3306
    targetPort: 3306
    protocol: TCP
  selector:  # このServiceに紐づけるPodをPodに付与したラベルで定義します
    ServiceName: db-service
    Environment: development
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: db-statefulset  # このStatefulSetの名前を定義します
  namespace: my-wordpress
  labels:  # このStatefulSetに付与するラベルを定義します
    ServiceName: db-service
    Environment: development
spec:
  replicas: 1  # 作成するPod数を定義します
  serviceName: db-service  # このStatefulSetに紐付けるServiceを定義します
  selector:
    matchLabels:  # 作成するPodに付与するラベルを定義します
      ServiceName: db-service
      Environment: development
  template:
    metadata:
      labels:  # spec: -> selector: -> matchLabels: に記載した内容と同じ内容を記載する必要があります
        ServiceName: db-service
        Environment: development
    spec:
      volumes:
      - name: db-persistent-volume  # このPodのボリューム名を定義します
        persistentVolumeClaim:
          claimName: db-persistent-volume-claim  # このPodのボリュームに紐づけるPersistentVolumeClaimを定義します
      containers:
      - name: db-container  # 作成するPodのコンテナ名を定義します
        ports:  # 作成するPodのコンテナで開放するポート情報を定義します
        - name: mysql
          containerPort: 3306
          protocol: TCP
        resources:
          requests:  # コンテナデプロイ時に必要とするリソースを定義します(リソースが足りない場合デプロイはエラーになります)
            cpu: 100m  # 1vCPU = 1,000m を意味します
            memory: 512Mi
          limits: # コンテナリソースの上限を定義します(ノードのメモリが枯渇しないようにrequestsと同値を設定します)
            memory: 512Mi
        image: mysql:5.6  # 作成するPodのコンテナ作成時に使用するDockerイメージを定義します
        env:  # 作成するPodのコンテナに設定する環境変数を定義します
        - name: MYSQL_ROOT_PASSWORD
          value: "somewordpress"
        - name: MYSQL_DATABASE
          value: "wordpress"
        - name: MYSQL_USER
          value: "wordpress"
        - name: MYSQL_PASSWORD
          value: "wordpress!"
        volumeMounts:  # このPodのコンテナが使用するボリューム名とマウントポイントを定義します
        - name: db-persistent-volume
          mountPath: /var/lib/mysql
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: db-persistent-volume-claim  # このPersistentVolumeClaimの名前を定義します
  namespace: my-wordpress
  labels:  # このPersistentVolumeClaimに付与するラベルを定義します
    ServiceName: db-service
    Environment: development
spec:
  accessModes:
    - ReadWriteOnce  # Read/Writeは単一Podからのみ許可することを意味します
  resources:
    requests:
      storage: 20Gi  # 作成するボリュームのサイズを定義します

□手順2-10: kubectl applyコマンドで、db-service関連のオブジェクトから作成します

CloudShell
$ kubectl apply -f $MANIFESTS_PATH/my-wordpress/db-service.yaml

□手順2-11: kubectl get allコマンドでオブジェクトが作成されたことを確認します

CloudShell
$ kubectl get all
NAME                   READY   STATUS    RESTARTS   AGE
pod/db-statefulset-0   1/1     Running   0          46s
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/db-service   ClusterIP   10.15.241.248   <none>        3306/TCP   47s
NAME                              DESIRED   CURRENT   AGE
statefulset.apps/db-statefulset   1         1         47s

参考: StatefulSetは、ナビゲーションメニューGoogle Kubernetes Engine>ワークロードから確認することができます

capture_003_09082019_171435.jpg

参考: Serviceは、ナビゲーションメニューGoogle Kubernetes Engine>ServiceとIngressから確認することができます

capture_006_09082019_171823.jpg

参考: PersistentVolumeClaimは、ナビゲーションメニューGoogle Kubernetes Engine>ストレージから確認することができます

capture_007_09082019_171854.jpg

□手順2-12: kubectl runコマンドを使ってテンポラリのPodを作成しServiceとの接続確認を行います

Serviceに割り当てられているClusterIPへは"Kubernetesクラスタ"内部からのみ疎通可能です

kubectl runコマンドを使えばテンポラリのPodを簡単に作成できるので、これを用いて接続確認を行います

CloudShell
$ kubectl run "mysql-client-pod" --image=mysql:5.6 --rm --restart=Never -it -- mysql -h "db-service" -u "wordpress" -p'wordpress!'
If you don't see a command prompt, try pressing enter.
mysql> quit
Bye
pod "mysql-client-pod" deleted

□手順2-13: 続いて、web-service関連のオブジェクトを作成します

CloudShell
$ kubectl apply -f $MANIFESTS_PATH/my-wordpress/web-service.yaml

□手順2-14: kubectl get allコマンドでオブジェクトが作成されたことを確認します

CloudShell
$ kubectl get all
NAME                                  READY   STATUS    RESTARTS   AGE
pod/db-statefulset-0                  1/1     Running   0          29m
pod/web-deployment-5f4f68bf87-4vvnx   1/1     Running   0          16m
pod/web-deployment-5f4f68bf87-h2pr4   1/1     Running   0          16m
pod/web-deployment-5f4f68bf87-r5kjt   1/1     Running   0          16m
NAME                  TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
service/db-service    ClusterIP   10.15.241.248   <none>        3306/TCP       29m
service/web-service   NodePort    10.15.249.35    <none>        80:30080/TCP   16m
NAME                             DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/web-deployment   3         3         3            3           16m
NAME                                        DESIRED   CURRENT   READY   AGE
replicaset.apps/web-deployment-5f4f68bf87   3         3         3       16m
NAME                              DESIRED   CURRENT   AGE
statefulset.apps/db-statefulset   1         1         29m
NAME                                                                           REFERENCE                   TARGETS        MINPODS   MAXPODS   REPLICAS   AGE
horizontalpodautoscaler.autoscaling/web-deployment-horizontal-pod-autoscaler   Deployment/web-deployment   <unknown>/10   3         10        3          43s

参考: Deploymentは、ナビゲーションメニューGoogle Kubernetes Engine>ワークロードからが確認することができます

capture_008_09082019_174222.jpg

参考: Serviceは、ナビゲーションメニューGoogle Kubernetes Engine>ServiceとIngressから確認することができます

capture_011_09082019_174940.jpg

□手順2-15: kubectl runコマンドを使ってテンポラリのPodを作成しServiceとの接続確認を行います

Webサーバー側についても同様にkubectl runコマンドを使ってテンポラリのPodを作成して接続確認を行います

CloudShell
$ kubectl run "curl-pod" --image=amazonlinux:2 --rm --restart=Never -it -- curl -I -Ss "http://web-service.my-wordpress.svc.cluster.local/readme.html"
HTTP/1.1 200 OK
Date: Fri, 09 Aug 2019 08:50:50 GMT
Server: Apache/2.4.38 (Debian)
Last-Modified: Mon, 08 Apr 2019 22:59:56 GMT
ETag: "1d17-5860ccb887300"
Accept-Ranges: bytes
Content-Length: 7447
Vary: Accept-Encoding
Content-Type: text/html

pod "curl-pod" deleted

□手順2-16: kubectl applyコマンドでIngress(my-ingress)を作成します

CloudShell
$ kubectl apply -f $MANIFESTS_PATH/my-wordpress/my-ingress.yaml
ingress.extensions/my-ingress created

□手順2-17: kubectl get ingressコマンドでIngressが作成されたことを確認します

"Google Kubernetes Engine(GKE)"環境において、Ingressの実体は、"Google Cloud Load Balancing"ベースのソフトウェア定義です

そのため、Ingressの作成には5分ほど要します

次に示すようにIPアドレスが割りてられるまで待つ必要があります

CloudShell
$ kubectl get ingress
NAME         HOSTS   ADDRESS         PORTS   AGE
my-ingress   *       35.244.156.32   80      4m39s

参考: Ingressは、ナビゲーションメニューGoogle Kubernetes Engine>ServiceとIngressから確認することができます

capture_013_09082019_194119.jpg

□手順2-18: ナビゲーションメニューからKubernetes Engine>Service と Ingressを選択します

capture_016_09082019_194255.jpg

□手順2-19: my-ingress列のエンドポイント欄部分をクリックして"Wordpress"の初期設定画面が表示されれば成功です

capture_019_09082019_194529.jpg

capture_018_09082019_194455.jpg

以上、Google Kubernetes Engine(GKE)上を使って "Wordpress" のシステムを構築するまでの手順でした

ここまでの手順が上手くいかなかった場合のトラブルシューティングの方法をいくつか記載しておきますので、参考にしてください

参考: kubectl port-forwardコマンドを使ったトラブルシューティング

Serviceに割り当てられたClusterIPへは"Kubernetesクラスタ"内部からのみしか接続できませんので、Ingress側の問題との切り分けを行う際はポートフォワーディングの設定が有効です

kubectl port-forwardコマンドでServiceに対してポートフォワーディングを設定します

CloudShell
$ kubectl port-forward service/web-service 8080:80

Cloud Shellの右上にあるウェブでプレビューアイコンをクリック後プレビューのポート:8080を選択します

capture_022_09082019_194742.jpg

"ポートフォワーディング"によりWebブラウザから直接ServiceClusterIPに対してアクセスが可能になりました

capture_023_09082019_194815.jpg

参考: Podのトラブルシューティング

kubectl logsコマンドを使用することでPod個別のログメッセージを確認できます

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE    IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          159m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4vvnx   1/1     Running   0          146m   10.12.2.15   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-h2pr4   1/1     Running   0          146m   10.12.2.14   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-r5kjt   1/1     Running   0          146m   10.12.1.5    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>

$ kubectl logs <Pod Name>
 :
中略
 :

より詳細な調査が必要な場合は、kubectl excecコマンドを使用することでPodに直接bashでログインすることができます

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE    IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          159m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4vvnx   1/1     Running   0          146m   10.12.2.15   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-h2pr4   1/1     Running   0          146m   10.12.2.14   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-r5kjt   1/1     Running   0          146m   10.12.1.5    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>

$ kubectl exec <Pod Name> -it -- bash
root@<Pod Name>:/#h

3. Podのアップデートとロールバック

"Kubernetes"では、各Podの"Dockerイメージ"を簡単にアップデートしたりロールバックしたりすることが可能です

□手順3-1: sedコマンドを使ってManifest上のDockerイメージの値をwordpress:5.2-apacheからwordpress:5.2.2-apacheに書き換えます

CloudShell
sed -i -e "s/wordpress:5.2-apache/wordpress:5.2.2-apache/g" $MANIFESTS_PATH/my-wordpress/web-service.yaml

□手順3-2: kubectl applyコマンドで修正したManifestを上書き適用します

CloudShell
$ kubectl apply -f $MANIFESTS_PATH/my-wordpress/web-service.yaml
service/web-service unchanged
deployment.apps/web-deployment configured
horizontalpodautoscaler.autoscaling/web-deployment-horizontal-pod-autoscaler configured

□手順3-3: kubectl get podsコマンドでPodの状態を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS              RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running             0          3h24m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5b7f866c47-c4b6w   0/1     ContainerCreating   0          24s     <none>       gke-my-worker-node-cluse-default-pool-924284ce-bm6s   <none>
web-deployment-5b7f866c47-swvvb   1/1     Running             0          41s     10.12.2.24   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-h2pr4   1/1     Running             0          3h11m   10.12.2.14   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-r5kjt   1/1     Running             0          3h11m   10.12.1.5    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>

元々あった3つのPodはそのままで4つめのPodが作成されています

□手順3-4: しばらく待って、もう一度kubectl get podsコマンドでPodの状態を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          3h26m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5b7f866c47-c4b6w   1/1     Running   0          2m29s   10.12.0.7    gke-my-worker-node-cluse-default-pool-924284ce-bm6s   <none>
web-deployment-5b7f866c47-qjg84   1/1     Running   0          112s    10.12.2.25   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5b7f866c47-swvvb   1/1     Running   0          2m46s   10.12.2.24   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>

Podの名前を確認するとすべてのPodが差し替えられた事がわかります

□手順3-5: kubectl rollout historyコマンドを実行します

CloudShell
$ kubectl rollout history deployment web-deployment
deployment.extensions/web-deployment
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

□手順3-5: kubectl rollout historyコマンドで1つ目のRevisionの情報を確認します

CloudShell
$ kubectl rollout history deployment web-deployment --revision 1
deployment.extensions/web-deployment with revision #1
Pod Template:
  Labels:       Environment=development
        ServiceName=web-service
        pod-template-hash=5f4f68bf87
  Containers:
   web-container:
    Image:      wordpress:5.2-apache
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      memory:   256Mi
    Requests:
      cpu:      100m
      memory:   256Mi
    Liveness:   http-get http://:80/readme.html delay=10s timeout=1s period=3s #success=1 #failure=3
    Readiness:  http-get http://:80/readme.html delay=10s timeout=1s period=3s #success=1 #failure=3
    Environment:
      WORDPRESS_DB_HOST:        db-service:3306
      WORDPRESS_DB_USER:        wordpress
      WORDPRESS_DB_PASSWORD:    wordpress!
      WORDPRESS_DB_NAME:        wordpress
    Mounts:     <none>
  Volumes:      <none>

Dockerイメージとしてwordpress:5.2-apacheを使用していたことが確認できます

□手順3-6: 同様に2つ目のRevisionの詳細を確認します

CloudShell
$ kubectl rollout history deployment web-deployment --revision 2
deployment.extensions/web-deployment with revision #2
Pod Template:
  Labels:       Environment=development
        ServiceName=web-service
        pod-template-hash=5b7f866c47
  Containers:
   web-container:
    Image:      wordpress:5.2.2-apache
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      memory:   256Mi
    Requests:
      cpu:      100m
      memory:   256Mi
    Liveness:   http-get http://:80/readme.html delay=10s timeout=1s period=3s #success=1 #failure=3
    Readiness:  http-get http://:80/readme.html delay=10s timeout=1s period=3s #success=1 #failure=3
    Environment:
      WORDPRESS_DB_HOST:        db-service:3306
      WORDPRESS_DB_USER:        wordpress
      WORDPRESS_DB_PASSWORD:    wordpress!
      WORDPRESS_DB_NAME:        wordpress
    Mounts:     <none>
  Volumes:      <none>

使用しているDockerイメージがwordpress:5.2.2-apacheに変更されていることが確認できます

□手順3-7: kubectl rollout undoコマンドを実行しロールバックを行います

CloudShell
$ kubectl rollout undo deployment web-deployment --to-revision 1
deployment.extensions/web-deployment

□手順3-8: kubectl rollout historyコマンドを実行します

CloudShell
$ kubectl rollout history deployment web-deployment
deployment.extensions/web-deployment
REVISION  CHANGE-CAUSE
2         <none>
3         <none>

Revision 1が無くなってRevision 3が追加されています

□手順3-9: kubectl get podsコマンドでPodの状態を確認します

CloudShell
 kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          3h40m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4ztjn   1/1     Running   0          5m39s   10.12.2.27   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-m8wtz   1/1     Running   0          5m50s   10.12.2.26   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-nfwbz   1/1     Running   0          6m4s    10.12.1.6    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>

Podの名前を確認するとすべてのPodがまた差し替えられた事がわかります

□手順3‐10: kubectl rollout historyコマンドで3つ目のRevisionの情報を確認します

CloudShell
$ kubectl rollout history deployment web-deployment --revision 3
deployment.extensions/web-deployment with revision #3
Pod Template:
  Labels:       Environment=development
        ServiceName=web-service
        pod-template-hash=5f4f68bf87
  Containers:
   web-container:
    Image:      wordpress:5.2-apache
    Port:       80/TCP
    Host Port:  0/TCP
    Limits:
      memory:   256Mi
    Requests:
      cpu:      100m
      memory:   256Mi
    Liveness:   http-get http://:80/readme.html delay=10s timeout=1s period=3s #success=1 #failure=3
    Readiness:  http-get http://:80/readme.html delay=10s timeout=1s period=3s #success=1 #failure=3
    Environment:
      WORDPRESS_DB_HOST:        db-service:3306
      WORDPRESS_DB_USER:        wordpress
      WORDPRESS_DB_PASSWORD:    wordpress!
      WORDPRESS_DB_NAME:        wordpress
    Mounts:     <none>
  Volumes:      <none>

使用しているDockerイメージがwordpress:5.2-apacheに戻っていることを確認できます

Podの"Dockerイメージ"を簡単にアップデートしたりロールバックしたりできることを確認できました

4. Pod障害時の挙動確認

Pod障害時の挙動について確認してみます。まずDeploymentによって作成された非永続的なPodが障害になったときの挙動について確認します

□手順4‐1: kubectl get podsコマンドで現在稼働しているPodの情報を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          3h44m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4ztjn   1/1     Running   0          9m41s   10.12.2.27   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-m8wtz   1/1     Running   0          9m52s   10.12.2.26   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-nfwbz   1/1     Running   0          10m     10.12.1.6    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>

□手順4‐2: kubectl delete pod <Pod Name>コマンドでリストの一番下のPodを削除してみます

CloudShell
$ kubectl delete pod "web-deployment-5f4f68bf87-nfwbz"
pod "web-deployment-5f4f68bf87-nfwbz" deleted

□手順4‐3: kubectl get podsコマンドで直後のPodの状態を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          3h47m   10.12.2.7    gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4ztjn   1/1     Running   0          11m     10.12.2.27   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-m8wtz   1/1     Running   0          12m     10.12.2.26   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-sst4v   1/1     Running   0          41s     10.12.2.28   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>

すぐに新しいPodが別の名前で再作成されたことが確認できました。続いてStatefulSetによって作成された永続的なPodが障害になった場合の挙動について確認します

□手順4‐4: kubectl delete podコマンドでdb-statefulset-0を削除してみます

CloudShell
$ kubectl delete pod "db-statefulset-0"
pod "db-statefulset-0" deleted

□手順4‐5: kubectl get podsコマンドでPodの状態を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          59s     10.12.2.29   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4ztjn   1/1     Running   0          14m     10.12.2.27   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-m8wtz   1/1     Running   0          15m     10.12.2.26   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-sst4v   1/1     Running   0          3m41s   10.12.2.28   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>

StatefulSetによって作成されたPodについては、Deploymentの時とは異なり同じ名前のPodが再作成されることが確認できました

5. ワーカーノード障害時の挙動確認

最後に、ワーカーノード障害時の挙動について確認してみます

□手順5‐1: kubectl get podsコマンドで現在稼働しているPodの情報を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE     IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   0          6m51s   10.12.2.29   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4ztjn   1/1     Running   0          20m     10.12.2.27   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-cddpk   1/1     Running   0          2m      10.12.1.7    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>
web-deployment-5f4f68bf87-dvfns   1/1     Running   0          51s     10.12.2.31   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>

□手順5‐2: StatefulSetによって作成されたPodが稼働しているワーカーノードにログインします

CloudShell
$ gcloud compute ssh "gke-my-worker-node-cluse-default-pool-924284ce-kpcs"
WARNING: The public SSH key file for gcloud does not exist.
WARNING: The private SSH key file for gcloud does not exist.
WARNING: You do not have an SSH key for gcloud.
WARNING: SSH keygen will be executed to generate a key.
This tool needs to create the directory [/home/kukita_keisuke/.ssh]
before being able to generate SSH keys.
Do you want to continue (Y/n)?  Y
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kukita_keisuke/.ssh/google_compute_engine.
Your public key has been saved in /home/kukita_keisuke/.ssh/google_compute_engine.pub.
The key fingerprint is:
SHA256:FUxUU0YJTt5ObFMLIacFwiDU1kKz0ZAhqHeRiid6Eo8 kukita_keisuke@cs-6000-devshell-vm-e9163938-594a-4670-8c13-1d9ca474820d
The key's randomart image is:
+---[RSA 2048]----+
|    o++BO=++*O=..|
|   . oo=++o===o..|
|  o . o.. ..o *. |
|.+ + .   .   + . |
|.++ .   S     .  |
|E o              |
| o               |
|                 |
|                 |
+----[SHA256]-----+
Did you mean zone [asia-east1-b] for instance:
gke-my-worker-node-cluse-default-pool-924284ce-kpcs] (Y/n)?  n
No zone specified. Using zone [asia-northeast1-a] for instance: [gke-my-worker-node-cluse-default-pool-924284ce-kpcs].
Updating project ssh metadata...⠶Updated [https://www.googleapis.com/compute/v1/projects/proud-command-246805].
Updating project ssh metadata...done.
Waiting for SSH key to propagate.
Warning: Permanently added 'compute.147868497783857976' (ED25519) to the list of known hosts.
Welcome to Kubernetes v1.12.8-gke.10!
You can find documentation for Kubernetes at:
  http://docs.kubernetes.io/
The source for this release can be found at:
  /home/kubernetes/kubernetes-src.tar.gz
Or you can download it at:
  https://storage.googleapis.com/kubernetes-release-gke/release/v1.12.8-gke.10/kubernetes-src.tar.gz
It is based on the Kubernetes source at:
  https://github.com/kubernetes/kubernetes/tree/v1.12.8-gke.10
For Kubernetes copyright and licensing information, see:
  /home/kubernetes/LICENSES

□手順5‐3: ワーカーノードを再起動します

WorkerNode
$ sudo /sbin/shutdown -r now
Connection to 104.198.83.208 closed by remote host.
Connection to 104.198.83.208 closed.
ERROR: (gcloud.compute.ssh) [/usr/bin/ssh] exited with return code [255].

□手順5‐4: kubectl get podsコマンドでPodの状態を確認します

CloudShell
$ kubectl get pods -o wide
NAME                              READY   STATUS    RESTARTS   AGE   IP           NODE                                                  NOMINATED NODE
db-statefulset-0                  1/1     Running   1          18m   10.12.2.36   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-4ztjn   1/1     Running   2          32m   10.12.2.35   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>
web-deployment-5f4f68bf87-cddpk   1/1     Running   0          13m   10.12.1.7    gke-my-worker-node-cluse-default-pool-924284ce-td1m   <none>
web-deployment-5f4f68bf87-dvfns   1/1     Running   2          12m   10.12.2.32   gke-my-worker-node-cluse-default-pool-924284ce-kpcs   <none>

ワーカーノード障害時にも各Podは自動的に復旧されることが確認できます


おまけ: "Google Kubernetes Engine(GKE)" では1つの "Ingress" で "namespace" が異なる "Service" を登録できない?

"1つのグローバルIPアドレス"(≒1つのIngress)で複数のServiceへのトラフィックを管理することが可能だというのがIngressのメリットでもあります

この場合、namespaceは分けて管理したくなるのですが、"Google Kubernetes Engine(GKE)"側の制約として NG のようです(Kubernetes側の仕様としては問題ないです)

当初は以下のようなManifestを作成し、IngressServiceとは異なるnamespaceを使って管理することを考えていました

CloudShell
$ vi $MANIFESTS_PATH/default/my-ingress.yaml
$MANIFESTS_PATH/default/my-ingress.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingress  # このIngressの名前を定義します
  namespace: default  # Ingressのnamespaceはdefaultにします
  annotations:
    kubernetes.io/ingress.global-static-ip-name: my-ingress-address  # 割り当てるスタティックIPアドレスを定義します
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:  # バックエンドのServiceとポート番号を定義します
          serviceName: web-service
          servicePort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-service
  namespace: default  # Ingressは同じ名前空間のServiceのみバックエンドとして登録可能です
  labels:  # このServiceに付与するラベルを定義します
    ServiceName: web-service
    Environment: development
spec:
  type: ExternalName  # ExternalName(外部名)は異なるnamespace上のServiceとリンクするために使用します
  externalName: web-service.my-wordpress.svc.cluster.local # リンク先のServiceを指定します
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP

ところがこの方法でIngressを作成すると下記のようなエラーメッセージが出力されてしまいました

error while evaluating the ingress spec: service "default/web-service" is type "ExternalName", expected "NodePort" or "LoadBalancer"

capture_012_09082019_192314.jpg

どうやらこれは、現時点での"Google Kubernetes Engine(GKE)"の制約↓のようですので、改善を期待したいところです

Cross-namespace Ingress #17088
https://github.com/kubernetes/kubernetes/issues/17088


以上、Google Kubernetes Engine(GKE)"を使って"Kubernetes"について勉強した際の内容でした

今回は、DeploymentStatefulSetを中心に動きを確認してみましたが、Podを管理するためのオブジェクトとしては、他にも DaemonSetCronJobといったものがあるため、別の記事としてまとめていきたいと思います

7
13
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
7
13