Google Kubernetes Engineについて少し調べたので備忘録として残しておきます。
Code
概要
Google Kubernetes Engineとは
Kubernetesの機能に加えて下記をサポートしてるGCPのフルマネージドサービスです。Kubernetesの既存機能に加え、次の機能があります。
- Kubernetesのバージョン管理:常に最新の状態になるようにします
- GCPの他のサービスとの連携:GCPの各種サービスとの連携・統合が容易
- オートスケーリング:CPU使用率やカスタム指標に基づいて、Podやクラスタを自動でスケーリング可能
Kubernetesについては次の記事でまとめました。
www.case-k.jp
GKEについては次の記事を参考にさせていただきました。
Kubernetes Engine | Google Cloud
[Cloud OnAir] Dive to Google Kubernetes Engine 2018年8月2日 放送
DevOps 編(全 6 回) | はじめてみよう Google Cloud Platform Online Handson
[Cloud OnAir] Google Cloud で実践するマイクロサービスアーキテクチャ 2019年2月21日 放送
実践編
次の書籍の構成を参考にさせていただきました。書籍に沿って行い、フロントにNuxt.jsバックエンドはGO、プロキシサーバにNginx、データストアにMySQLといった構成です。
gihyo.jp
Cloud SDKをインストール
ローカルホストにCloud SDKをインストールしgcloudコマンドを使えるようにし、upddateします
$ gcloud components update
認証しGCPプロジェクトを操作できるようにします
$ gcloud auth login
プロジェクトを設定
$ gcloud config set project [project id ]
リーージョンは東京リージョンを選びます
$ gcloud config set compute/zone asia-northeast1-a Updated property [compute/zone].
GKEクラスタ構築
GKEのクラスタを作ります。マシンタイプはコストが低い「n1-standard-1」でクラスタ内のノード数は3とします。
$ gcloud container clusters create casek --machine-type=n1-standard-1 --num-nodes=3
gcloudコマンドで作成したクラスタを制御できるようにするために、kubectlコマンドに認証情報を設定します。
$ gcloud container clusters get-credentials casek
Fetching cluster endpoint and auth data.
kubeconfig entry generated for casek.
kubectlコマンドでGCP上に構築したクラスタを制御できることを確認します。
$ kubectl get nodes NAME STATUS ROLES AGE VERSION gke-casek-default-pool-cfeda864-7mgk Ready <none> 10m v1.14.10-gke.17 gke-casek-default-pool-cfeda864-gqv5 Ready <none> 10m v1.14.10-gke.17 gke-casek-default-pool-cfeda864-pxjx Ready <none> 10m v1.14.10-gke.17
Kubernetesでストレージを確保するためのリソース
Kubernetesでストレージを確保するためのリソースとしてPersistentVolumeとPersistentVolumeClaimがあります。
PersistentVolumeとPersistentVolumeClaim
PersistentVolumeはストレージの実体で、PersistentVolumeClaimはPersistentVolumeに対して必要な容量を動的に確保するためのものです。
PersistentVolumeClaimのマニフェストファイルは次のようになります。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-example spec: accessModes: - ReadWriteOnce storageClassName resources: requests: storage: 4Gi
- accessModes:Podからストレージへのマウントポリシーのこと。「ReadWriteOnceは」どこか1つのノードからR/Wマウントのみ許可されている。
- storageClassName:StorageClassの名前。利用するストレージの種類を定義。resources.requests.storageでボリュームが必要な容量を指定する。
StorageClass
StorageClassはPersistentVolumeが確保するストレージの種類を定義するリソースとなります。GCPのストレージには「標準」と「SSD」が存在しています。次のマニフェストにはSSDを利用してストレージを作成しています。
kind: StorageClass apiVersion: storage.k8s.io/v1 metadata: name: ssd annotations: storageclass.kubernetes.io/is-default-class: "false" labels: kubernetes.io/cluster-service: "true" provisioner: kubernetes.io/gce-pd parameters: type: pd-ssd
$ kubectl apply -f storage-class-ssd.yaml
storageclass.storage.k8s.io/ssd created
StatefulSet
StatefulSetはデータストアのように継続的にデータを永続化するステートフルなアプリケーションの管理に向いているリソースです。類似するものでDeploymentは永続的なデータをもつ必要がないステートレスなアプリケーションをデプロイするのに向いているリソースです。
MySQL
まずはMySQLをデプロイします。
Masterをデプロイ
# mysql-master.yaml apiVersion: v1 kind: Service metadata: name: mysql-master labels: app: mysql-master spec: ports: - port: 3306 name: mysql clusterIP: None selector: app: mysql-master --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql-master labels: app: mysql-master spec: serviceName: "mysql-master" selector: matchLabels: app: mysql-master replicas: 1 template: metadata: labels: app: mysql-master spec: terminationGracePeriodSeconds: 60 containers: - name: mysql image: gihyodocker/tododb:latest imagePullPolicy: Always args: - "--ignore-db-dir=lost+found" ports: - containerPort: 3306 env: - name: MYSQL_ROOT_PASSWORD value: "gihyo" - name: MYSQL_DATABASE value: "tododb" - name: MYSQL_USER value: "gihyo" - name: MYSQL_PASSWORD value: "gihyo" - name: MYSQL_MASTER value: "true" volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: ssd resources: requests: storage: 4Gi
- volumeClaimTemplates:PersistentVolumeClaimをPodごとに自動生成するテンプレート
Serviceはクラスタ内で「mysql-master」で名前解決できるよう定義しておきます。
マニフェストができたらデプロイします。
$ kubectl apply -f mysql-master.yaml
service/mysql-master unchanged
statefulset.apps/mysql-master created
Slaveをデプロイ
次にSlaveのStatefuleSetのマニフェストファイルを定義していきます。マニフェストはMasterとほとんど同じです。Serviceはクラスタ内で「mysql-slave」で名前解決できるよう定義しておきます。レプリケーションは2つとします。
apiVersion: v1 kind: Service metadata: name: mysql-slave labels: app: mysql-slave spec: ports: - port: 3306 name: mysql clusterIP: None selector: app: mysql-slave --- apiVersion: apps/v1 kind: StatefulSet metadata: name: mysql-slave labels: app: mysql-slave spec: serviceName: "mysql-slave" selector: matchLabels: app: mysql-slave replicas: 2 # master1 slave 2 updateStrategy: type: OnDelete template: metadata: labels: app: mysql-slave spec: terminationGracePeriodSeconds: 60 containers: - name: mysql image: gihyodocker/tododb:latest imagePullPolicy: Always args: - "--ignore-db-dir=lost+found" ports: - containerPort: 3306 env: # 変更 - name: MySQL_MASTER_HOST value: "mysql-master" - name: MYSQL_ROOT_PASSWORD value: "gihyo" - name: MYSQL_DATABASE value: "tododb" - name: MYSQL_REPL_USER value: "gihyo" - name: MYSQL_REPL_PASSWORD value: "gihyo" volumeMounts: - name: mysql-data mountPath: /var/lib/mysql volumeClaimTemplates: - metadata: name: mysql-data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: ssd resources: requests: storage: 4Gi
- updateStrategy:StatefulSetの更新戦略
RollingUpdate:Podの自動更新
updateStrategy:
type: RollingUpdate
partition: 3
OnDelete:更新するには手動でPodを削除。削除すると手動で新しいPodを作成する。
Kubernetes道場 13日目 - StatefulSet / DaemonSetについて - Toku's Blog
マニフェストファイルをデプロイし、動作を確認します。
$ kubectl apply -f mysql-slave.yaml service/mysql-slave created statefulset.apps/mysql-slave created $ kubectl get pod NAME READY STATUS RESTARTS AGE mysql-master-0 1/1 Running 0 38m mysql-slave-0 1/1 Running 1 9m5s mysql-slave-1 1/1 Running 1 8m20s $ kubectl exec -it mysql-slave-0 bash mysql -u root -pgihyo tododb -e "SHOW TABLES;" root@mysql-slave-0:/# mysql -u root -pgihyo tododb
バックエンド
MySQLにデータを取りにいくAPIを作ります。プロキシサーバとしてNginxを介してAPIを実行します。ServiceではAPIで名前解決できるように「todoapi」を選択します。DeploimentではNginxコンテナとAPIコンテナを1つのPodとしてデプロイします。
apiVersion: v1 kind: Service metadata: name: todoapi labels: app: todoapi spec: selector: app: todoapi ports: - name: http port: 80 --- apiVersion: apps/v1 kind: Deployment metadata: name: todoapi labels: app: todoapi spec: replicas: 2 selector: matchLabels: app: todoapi template: metadata: labels: app: todoapi spec: containers: - name: nginx image: gihyodocker/nginx:latest imagePullPolicy: Always ports: - containerPort: 80 env: - name: WORKER_PROCESSES value: "2" - name: WORKER_CONNECTIONS value: "1024" - name: LOG_STDOUT value: "true" - name: BACKEND_HOST value: "localhost:8080" - name: api image: gihyodocker/todoapi:latest imagePullPolicy: Always ports: - containerPort: 8080 env: - name: TODO_BIND value: ":8080" - name: TODO_MASTER_URL value: "gihyo:gihyo@tcp(mysql-master:3306)/tododb?parseTime=true" - name: TODO_SLAVE_URL value: "gihyo:gihyo@tcp(mysql-slave:3306)/tododb?parseTime=true"
マニフェストファイルをデプロイします、
$ kubectl apply -f todo-api.yaml
service/todoapi unchanged
deployment.apps/todoapi created
フロントエンド
NginxコンテナとWEBコンテナを1つのPodとしてデプロイします。Serviceで疎通できるコンテナ(todoweb)を指定します。Deployment対象はNginxコンテナとWEBコンテナとなります。Nginxコンテナの環境変数「BACKEND_HOST」は同じPod内のWEBコンテナとなるので「localhost:3000」を選びます。WEBコンテナの「TODO_API_URL」にはtodoapiのService名をURLにします。
# todo-web.yaml apiVersion: v1 kind: Service metadata: name: todoweb labels: app: todoweb spec: selector: app: todoweb ports: - name: http port: 80 type: NodePort --- apiVersion: apps/v1 kind: Deployment metadata: name: todoweb labels: name: todoweb spec: replicas: 2 selector: matchLabels: app: todoweb template: metadata: labels: app: todoweb spec: volumes: - name: assets emptyDir: {} containers: - name: nginx image: gihyodocker/nginx-nuxt:latest imagePullPolicy: Always ports: - containerPort: 80 env: - name: WORKER_PROCESSES value: "2" - name: WORKER_CONNECTIONS value: "1024" - name: LOG_STDOUT value: "true" - name: BCKEND_HOST value: "localhost:3000" volumeMounts: - mountPath: /var/www/_nuxt name: assets - name: web image: gihyodocker/todoweb:latest imagePullPolicy: Always lifecycle: postStart: exec: command: - cp - -R - /todoweb/.nuxt/dist - / ports: - containerPort: 3000 env: - name: TODO_API_URL value: http://todoapi volumeMounts: - mountPath: /dist name: assets
マニフェストファイルをデプロイします。
$ kubectl apply -f todo-web.yaml
service/todoweb unchanged
deployment.apps/todoweb created
Ingressでインターネットに公開
Ingressを利用してWEBアプリケーションをインターネットに公開します。GCPを利用しているため、Cloud Load Balancingが利用されます。GCPを使う場合、Serviceを外部公開する上でLoadBalancer Serviceを利用する方法もありますが、HTTPレベルでServiceの制御をするようなケースではあまり向いていないようです。こちらのハンズオンではLoadBalancer Serviceを利用する方法で行っていました。
DevOps 編(全 6 回) | はじめてみよう Google Cloud Platform Online Handson
# ingress.yaml apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress spec: rules: - http: paths: - path: /* backend: serviceName: todoweb servicePort: 80
$ kubectl apply -f ingress.yaml
ingress.extensions/ingress created
$ kubectl get pod NAME READY STATUS RESTARTS AGE mysql-master-0 1/1 Running 0 25h mysql-slave-0 1/1 Running 1 25h mysql-slave-1 1/1 Running 1 24h todoapi-5467bf5d79-79vgl 2/2 Running 0 24h todoapi-5467bf5d79-9pjrl 2/2 Running 0 24h todoweb-6df88ccc69-887b6 2/2 Running 0 32s todoweb-6df88ccc69-qk7sz 2/2 Running 0 24s
所感
運用面などはまだまだですが基礎的な部分を少しは理解できてきたので、以前作った株価のアプリケーションをベースにイメージを作り、GKE上にあげてみたいと思います。