KubernetesでWordPress構築をやってみた。運用費は月額4000円ほど。

# WordPressをリーズナブルに使いたい
WordPressでブログサイトを作りたく、リーズナブルに運用できる方法を探していたのですが、[GCEでWordPressがほぼ無料運用できるようになったので改めてまとめる](https://yukari-n.info/posts/gce-wordpress/#tocAnchor-1-3-1)の記事を読んで、`GCP`を使えば安そうなので試してみました。

[公式ドキュメント](https://cloud.google.com/wordpress/?hl=ja)にも
>WordPress ホスティング料金
>WordPress は Compute Engine の無料階層で実行できます（f1-micro、0.2 CPU）

と確かに`Google Compute Engine`を使えば無料枠でWordPressが作れそうですね。

# GCEにWordPressを構築する方法
[公式ドキュメント](https://cloud.google.com/wordpress/?hl=ja)によると、3通りWordPressを構築する方法があるようです。

- `Cloud Launcher` で WordPress の単一インスタンスをデプロイ
 -  トラフィックが少ない場合や中程度の場合に最適
- `Kubernetes Engine`に WordPress をデプロイ
 - トラフィック量が多い場合に、自動スケーリングなどできる 
- `App Engine` に WordPress をデプロイ
 - トラフィック量が変動し、ピーク時の値が高くなる場合にのみおすすめ 

今回は、リーズナブルに済ませたいと思う気持ちがありつつも、
`Kubernetes`触ってみたかったので`Kubernetes`でやりたいと思います。

# Kubernetesとは
Linux コンテナの操作を自動化するオープンソース・プラットフォームのことです。
詳細はこちらの記事に([Kubernetes とは](https://www.redhat.com/ja/topics/containers/what-is-kubernetes))詳しく書かれていますが、今回この記事で使う用語をメモっておきます。

| 用語| 説明 |
|:-----------------|:------------------|
| クラスタマスター |Kubernetes ノードを制御するマシン。すべてのタスクの割り当て元となります。 | 
| ノード |リクエストされ、割り当てられたタスクを実行するマシン。Kubernetes マスターはこれらのノードを制御します。 | 
|  Pod  |1 つのノードにデプロイされた、1 つ以上のコンテナからなるグループ。Pod に入っているすべてのコンテナは、同じ IP アドレス、IPC、ホスト名、その他のリソースを共有します。 | 
| サービス |Pod から作業の定義を分離します。Kubernetes サービスプロキシは適切な Pod に自動的にサービスリクエストを提供します。 | 
| kubectl |Kubernetes のコマンドライン設定ツールです。 | 


# gcloudをインストール
公式ドキュメントによると、`GCP`の`SDK`が必要なので`Google Cloud SDK`を入れます。

参考：[Cloud SDK のインストール](https://cloud.google.com/sdk/downloads?hl=ja)

パッケージをダウンロードしてきてから、インストールを実行。

```shell
$ ./install.sh 
Welcome to the Google Cloud SDK!

・・・

==> Source [/Users/hoge/Downloads/google-cloud-sdk/completion.bash.inc] in your profile to enable shell command completion for gcloud.
==> Source [/Users/hoge/Downloads/google-cloud-sdk/path.bash.inc] in your profile to add the Google Cloud SDK command line tools to your $PATH.

For more information on how to get started, please visit:
  https://cloud.google.com/sdk/docs/quickstarts
```
PATHを通して`gcloud `コマンドが使えるようにします。

```shell
$ vi ~/.bash_profile
#パス追加
export PATH=$PATH:/Users/hoge/src/google-cloud-sdk/completion.bash.inc
export PATH=$PATH:/Users/hoge/src/google-cloud-sdk/path.bash.inc
export PATH=$PATH:/Users/hoge/src/google-cloud-sdk/bin/

$ source ~/.bash_profile
```


また、`gcloud`コマンドが使えるようになったところで、`Kubernetes`のコマンドラインツール[kubectl](https://kubernetes.io/docs/reference/generated/kubectl/kubectl/)もインストールします。

```shell
$ gcloud components install kubectl

```
事前準備が整いました。

# WordPress と MySQL での永続ディスクの使用
以下を参考に行います。
[WordPress と MySQL での永続ディスクの使用](https://cloud.google.com/kubernetes-engine/docs/tutorials/persistent-disk/?hl=ja)

永続ディスクとは、高性能のブロック ストレージです。コンテナは使い捨てがセオリーなので、永続的なファイルシステムの格納には向いていませんが、この永続ディスクを使用するとアプリケーションのデータ(WordPress、MySQL)をコンテナの外部に保存できます。そのためたとえコンテナを削除しても、データは消えないのです。

### Google Kubernetes Engine API を有効にする
まずはGCPのコンソールより、[Kubernetes Engine のページ](https://console.cloud.google.com/project/_/kubernetes/list?hl=ja&_ga=2.202739225.960768144.1515516580-2018042871.1515516580)にアクセスし、プロジェクトを作成します。

<img width="500" alt="スクリーンショット 2018-01-10 1.51.33.png" src="https://qiita-image-store.s3.amazonaws.com/0/159733/eacd69a2-acec-8e90-a017-7566ec172844.png">

プロジェクト作成後、[プロジェクトの課金を有効に](https://cloud.google.com/billing/docs/how-to/modify-project?hl=ja&visit_id=1-636511135971113787-1733532131&rd=1#enable-billing#%E6%97%A2%E5%AD%98%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E8%AA%B2%E9%87%91%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B
)します。

### gcloud コマンドライン ツールのデフォルトの設定
`gcloud`を使う上で、
先ほど作成したプロジェクトのプロジェクトIDと、`Compute Engine ゾーン`のデフォルト設定にします。

```shell
$ gcloud config set project (PROJECT_ID)
Updated property [core/project].
$ gcloud config set compute/zone asia-northeast1-b
Updated property [compute/zone].
```

`Compute Engine ゾーン`によってインスタンスの機能と通信速度が異なります。例えば2018年1月現在、東京リージョンでは、`GPU`搭載のインスタンスが使えません。
ただ日本からアクセスされるブログサイトであれば、東京リージョンの方が通信速度が早く適しています。

参考：[東京 GCP リージョン の正式運用を開始しました](https://cloudplatform-jp.googleblog.com/2016/11/tokyo-region-now-open.html)


### アプリケーション マニフェストのダウンロード
[GitHub レポジトリ](https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/tree/master/wordpress-persistent-disks)より、WordPress環境に必要なマニフェスト ファイルをダウンロードします。

```shell
$ git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
Cloning into 'kubernetes-engine-samples'...
remote: Counting objects: 207, done.
remote: Total 207 (delta 0), reused 0 (delta 0), pack-reused 207
Receiving objects: 100% (207/207), 56.65 KiB | 0 bytes/s, done.
Resolving deltas: 100% (77/77), done.
Checking connectivity... done.
```
ダウンロードしたファイルは、WordPressとMySQLのデプロイ設定ファイル、サービス設定ファイルです。

```shell
$ ls -1
README.md
mysql-service.yaml
mysql.yaml
wordpress-service.yaml
wordpress.yaml
```

## Kubernetes Engine クラスタ マスターを作成する

クラスタ マスターはノードを制御するマシンです。
以下のコマンドで、`blog`という名前で3つのノードを制御するクラスタを作成しています。ノードは、タスクを実行するマシンです。

```shell
$ gcloud container clusters create blog --num-nodes=3 
WARNING: Accessing a Container Engine cluster requires the kubernetes commandline
client [kubectl]. To install, run
  $ gcloud components install kubectl

Creating cluster blog...done.                                                                                                                           
Created [https://container.googleapis.com/v1/projects/wordpress/zones/asia-northeast1-b/clusters/blog].
kubeconfig entry generated for blog.
NAME   ZONE           MASTER_VERSION  MASTER_IP       MACHINE_TYPE   NODE_VERSION  NUM_NODES  STATUS
blog  asia-northeast1-b  1.7.11-gke.1    35.193.159.110  n1-standard-1  1.7.11-gke.1  3          RUNNING
```

## 永続ディスクを作成する
ポッドが再起動されても、WordPress、MySQLのデータが消えないように永続ディスクを使用します。

```shell
$ gcloud compute disks create --size 200GB mysql-disk
Created [https://www.googleapis.com/compute/v1/projects/wordpress-for-beauty/zones/us-central1-b/disks/mysql-disk].
NAME        ZONE           SIZE_GB  TYPE         STATUS
mysql-disk  us-central1-b  200      pd-standard  READY

New disks are unformatted. You must format and mount a disk before it
can be used. You can find instructions on how to do this at:

https://cloud.google.com/compute/docs/disks/add-persistent-disk#formatting

$ gcloud compute disks create --size 200GB wordpress-disk
Created [https://www.googleapis.com/compute/v1/projects/wordpress/zones/asia-northeast1-b/disks/wordpress-disk].
NAME            ZONE           SIZE_GB  TYPE         STATUS
wordpress-disk  asia-northeast1-b  200      pd-standard  READY

New disks are unformatted. You must format and mount a disk before it
can be used. You can find instructions on how to do this at:

https://cloud.google.com/compute/docs/disks/add-persistent-disk#formatting
```
`mysql-disk`と`wordpress-disk`の2つの永続ディスクを用意しました。

※永続ディスクは料金が発生します。詳細は最後に記載。


## MySQL をセットアップする
まずはデータベースのパスワードを格納する Kubernetes シークレットを作成。

```shell
$  kubectl create secret generic mysql --from-literal=password=(YOUR_PASSWORD)
secret "mysql" created
```
mysql.yaml マニフェスト ファイルを使用して、ポート 3306 で動作する単一インスタンスの MySQL アプリケーションをデプロイします。
先ほど作成した永続ディスク`mysql-disk`もこちらで指定します。

```mysql.yaml

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  # レプリカ数の指定
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - image: mysql:5.6
          name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql
                  key: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-persistent-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-persistent-storage
          gcePersistentDisk:
　　　　　　//先ほど作成した永続ディスクを指定
            pdName: mysql-disk
            fsType: ext4
```

### マニフェスト ファイルをデプロイする

```shell
$ kubectl create -f mysql.yaml
deployment "mysql" created
```

### ポッドが実行されているかどうかを確認

```shell
$ kubectl get pod -l app=mysql
NAME                     READY     STATUS    RESTARTS   AGE
mysql-3368603707-327j1   1/1       Running   0          44s
```

### MySQL サービスを作成する
Kubernetesのサービスは、論理的なポッドのセットと、それにアクセスするポリシーを定義する抽象的なものです。

```mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  type: ClusterIP
  ports:
    - port: 3306
  selector:
    app: mysql
```

マニフェスト ファイルをデプロイ

```shell
$ kubectl create -f mysql-service.yaml
service "mysql" created
```

確認

```shell
$ kubectl get service mysql
NAME      TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
mysql     ClusterIP   10.27.248.78   <none>        3306/TCP   10m
```
ちゃんと動いていますね。

## WordPress をセットアップする

```wordpress.yaml 
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: mysql:3306
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-persistent-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-persistent-storage
          gcePersistentDisk:
            pdName: wordpress-disk
            fsType: ext4
```

デプロイする

```shell
$ kubectl create -f wordpress.yaml
deployment "wordpress" created
```
確認

```shell
$ kubectl get pod -l app=wordpress
NAME                         READY     STATUS    RESTARTS   AGE
wordpress-3479901767-gz54d   1/1       Running   0          10m
```
### WordPress サービスを公開する
ここまでで、クラスター内にWordPressを構築しましたが、まだコンテナを外部から見れるようにする必要があります。

公式ドキュメントより
>WordPress コンテナは、外部 IP アドレスを持たないため、現在はクラスタ外からアクセスできません
>ロードバランサ（課金対象）を使用して WordPress アプリケーションをインターネットからのトラフィックに公開するには、type:LoadBalancer のサービスが必要です。

外部に見せるための課金対象のロードバランサを設定する必要がありました。


```wordpress-service.yaml
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wordpress
  name: wordpress
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  selector:
    app: wordpress
```
マニフェストをデプロイすると、ロードバランサが作成されます。

```shell
$ kubectl get svc -l app=wordpress
NAME        TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
wordpress   LoadBalancer   10.27.241.164   35.225.XXX.XXX   80:30370/TCP   2m
```

この`EXTERNAL-IP`がパブリック IP アドレスになります。

### 外部からアクセス
<img width="400" alt="スクリーンショット 2018-01-08 23.31.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/159733/db6a5116-db89-74be-9468-fd950b034e9a.png">

成功です。

# 料金について
WordPressをリーズナブルに使いたいというモチベーションでやってきたのですが、[このチュートリアル](https://cloud.google.com/kubernetes-engine/docs/tutorials/persistent-disk/?hl=ja)を元に作った場合、永続ディスク、ロードバランサ共にお金が発生します。

現在のレート(1USD=112.56JPY)に換算すると

200GBの永続ディスク：一ヶ月あたり約1,172円　
(2個用意しているので、約2,334円)
ロードバランサ：一ヶ月あたり約2000円

合計一ヶ月あたり約4,300円です。

[さくらのレンタルサーバ](https://www.sakura.ne.jp/plans.html)と比較すると高いですね。

# Kubernetesでブログを運用するタイミング
公式ドキュメントにも書いてあるように、そもそも`Kubernetes Engine`は、トラフィック量が多いサイトで、コンテナによる自動スケーリングとパフォーマンスのメリットを活用したい場合に最適なので、ブログのアクセスが増えてパフォーマンスが気になる時に`Kubernetes`を使うのがいいですね。