はじめに
この連載ではコンテナオーケストレーションツールである、 Kubernatesの使い方を学びます。
今回はGoogle Kubernetes Engine上で実際にTODOアプリケーションを動作させてみましょう。
サンプルコード
こちらに順次アップしていきますー。
連載記事一覧
構成
こんな感じ。MySQL、API、Webアプケーションの順でデプロイしていき、最終的にIngressで公開します。
MySQLをMaster Slave構成でGKE上に構築する
Dockerで永続化データを扱うコンテナを実行するためには、データボリュームが利用されます。
Kubernetesでは、以下のKubernetesリソースで、外部ストレージをボリュームとして利用できます。
- PersistentVolume
- PersistentVolumeClaim
- StorageClass
- StatefulSet
ここではGKEでのMySQL構築を通して、永続化データを扱うPodの実行について解説します。
PersistentVolumeとPersistentVolumeClaim
PersistentVolumeはストレージの実体です。GCPではGCEPersistentDiskが該当します。
対してPersistentVolumeClaimはストレージを論理的に抽象化したリソースで、PersistentVolumeに対して必要な容量を動的に確保できます。
PersistentVolumeClaimのマニフェストファイルのイメージは次のようなものです。
PodからはPersistentVolumeClaimを直接マウントできます。
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-example spec: accessModes: - ReadWriteOnce storageClassName: ssd resources: requests: storage: 4Gi
accessModesはPodからストレージへのマウントポリシーのことです。ReadWriteOnceであればどこか1つのノードからのR/Wマウントのみが許可されます。
storageClassNameは後述するStorageClassリソースの名前のことで、利用するストレージの種類を定義します。
ボリュームが必要な容量を.resources.requests.storageで指定できます。
StorageClass
StorageClassはPersistentVolumeが確保するストレージの種類を定義できるリソースです。
直前のPersistentVolumeClaimのstorageClassNameで指定していた値の実体です。
GCPのストレージには「標準」と「SSD」が存在します。ここではSSDを利用してストレージを作成できるようにしましょう。
次のようなstorage-class-ssd.yamlというマニフェストファイルを作成します。
# storage-class-ssd.yaml 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
ssdという名前のSSDに対応したStorageClassを定義しています。
provisionerはGCPの永続ストレージであるGCEPersistentDiskに対応したVolumePluginであるgce-pdを指定します。
このプラグインのパラメータのtype属性にSSDに対応したpd-ssdを指定します。
$ kubectl apply -f storage-class-ssd.yaml storageclass.stroage.k8s.io "ssd" created
StatefulSet
Deployment : 定義されたPod仕様に基づきPodを作成するリソース。
- 一意性を持つPodや永続化データを持つ必要のないステートレスなアプリケーションをデプロイするのに向いている。
StatefulSet : データストアのように継続的にデータを永続化するステートフルなアプリケーションの管理に向いたリソース。
- StatefulSetではpod-0、pod-1、pod-2のような連番で一意な識別子でPodを作成。StatefulSetが作るPodの識別子は、Podが再作成されても保たれる。また、スケーリングも識別子の連番が維持されるように行われる。
ここではStatefulSetを使用してMySQLのMasterとSlaveのPodを作成していきます。
Podが安定した識別子を持つことで、Podが再作成されてもストレージを継続して同じPodに紐づけることができます。 そのため、Podが持つデータを再作成前と同じ状態で復元できます。
まず、MasterのStatefulSetを次のmysql-master.yamlというマニフェストファイルに定義して作成します
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
反映します。
$ kubectl apply -f mysql-master.yaml service "mysql-master" created statefulset.apps "mysql-master" created
Slaveの設定
続いてSlaveのStatefulSetを次のmysql-slave.yamlというマニフェストファイルに定義して作成します。
# mysql-slave.yaml 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 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_USER value: "gihyo" - name: MYSQL_PASSWORD value: "gihyo" - name: MYSQL_REPL_USER value: "repl" - 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
$ kubectl apply -f mysql-slave.yaml service "mysql-slave" created statefulset.apps "mysql-slave" created
SlaveもMasterと要領は同じです。レプリカ数と名前、環境変数だけが異なります。SlaveはMasterの場所を知る必要があるため、環境変数MYSQL_MASTER_HOSTでMasterのService名であるmysql-masterを指定しています。
実行内容の確認
StatefulSetで作成されたMasterのPodにinit-data.shで初期データ登録を実行し、SlaveのPodに反映されているかを確認します。
$ kubectl get pod NAME READY STATUS RESTARTS AGE mysql-master-0 1/1 Running 0 48m mysql-slave-0 1/1 Running 0 5m mysql-slave-1 1/1 Running 0 5m $ kubectl exec -it mysql-master-0 init-data.sh $ kubectl exec -it mysql-slave-0 bash root@mysql-slave-0:/# mysql -u root -pgihyo tododb -e "SHOW TABLES;" mysql: [Warning] Using a password on the command line interface can be insecure. +------------------+ | Tables_in_tododb | +------------------+ | todo | +------------------+