開発日報

窓際エンジニアの開発備忘。日報は嘘です。

執筆中。【連載】Kubernates入門・GKEデプロイと発展的利用 第7回 ~ GKE上にTODOアプリケーションを構築する ~

はじめに

この連載ではコンテナオーケストレーションツールである、 Kubernatesの使い方を学びます。
今回はGoogle Kubernetes Engine上で実際にTODOアプリケーションを動作させてみましょう。

サンプルコード

こちらに順次アップしていきますー。

連載記事一覧

連載記事一覧

構成

こんな感じ。MySQLAPI、Webアプケーションの順でデプロイしていき、最終的にIngressで公開します。

f:id:yuuu1993g:20191201235533p:plain

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