juju:lxcで立てたkubernetesにhelmでwordpressをデプロイする


# 動機
最近飼い始めたハムの日記をつけよう、ということでhelm使ってwordpress立ててみたら、意外に苦戦したのでメモしておく。

# 前提条件
* kubernetesとhelmが利用できる環境

```terminal
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.1", GitCommit:"b1b29978270dc22fecc592ac55d903350454310a", GitTreeState:"clean", BuildDate:"2018-07-17T18:53:20Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.5", GitCommit:"32ac1c9073b132b8ba18aa830f46b77dcceb0723", GitTreeState:"clean", BuildDate:"2018-06-21T11:34:22Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
```
```terminal
$ helm version
Client: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.9.1", GitCommit:"20adb27c7c5868466912eebdf6664e7390ebe710", GitTreeState:"clean"}
```
* helmのChartはstable/wordpressで`appVersion: 4.9.7`
* 筆者の環境はconjure-up => Juju:lxcで構築したkubernetes(1node)

# Persistent Volumeを作成する
インストールするwordpressのhelmチャートは2つのPersistentVolume(PV)が必要なので、作成する。

nfsでのPVの作成方法はこちらを参考にさせてもらった。
[Kubernetes: PersistentVolume概要とminikubeでの検証](https://qiita.com/ysakashita/items/924786aa24ef3bb864ef)

まず、各PV用のディレクトリを作成。

```bash
install --directory --mode=777 /mnt/datahdd01/kube/rwo20g-wordpress01-wordpress
install --directory --mode=777 /mnt/datahdd01/kube/rwo8g-wordpress01-mariadb
```

次に、上記で作成したディレクトリをめがけたPVのyamlを作成する。

画像やら保存すると足りなくなりそうなのでサイズは多めに設定

```yaml:./volumes/nfs/pv-nfs-rwo20g-wordpress01-wordpress.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-rwo20g-wordpress01-wordpress
spec:
  capacity:
    storage: 20Gi
  accessModes:
    - ReadWriteOnce
  # PersistentVolumeClaim を削除した時の動作
  persistentVolumeReclaimPolicy: Recycle
  mountOptions:
    - hard
  ## マウント先のNFS Serverの情報を記載
  nfs:
    path: /mnt/datahdd01/kube/rwo20g-wordpress01-wordpress
    server: 10.174.70.1
```

どのぐらい使うかわからないのでhelmチャートデフォルトの8Gに設定

```yaml:./volumes/nfs/pv-nfs-rwo8g-wordpress01-mariadb.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nfs-rwo8g-wordpress01-mariadb
spec:
  capacity:
    storage: 8Gi
  accessModes:
    - ReadWriteOnce
  # PersistentVolumeClaim を削除した時の動作
  persistentVolumeReclaimPolicy: Recycle
  mountOptions:
    - hard
  ## マウント先のNFS Serverの情報を記載
  nfs:
    path: /mnt/datahdd01/kube/rwo8g-wordpress01-mariadb
    server: 10.174.70.1
```

作成したPVのyamlをkubernetesに適用し、実際にPVを作成する。

```bash
kubectl apply -f ./volumes/nfs/pv-nfs-rwo20g-wordpress01-wordpress.yaml
kubectl apply -f ./volumes/nfs/pv-nfs-rwo8g-wordpress01-mariadb.yaml
```

# helm install
helm installでwordpressをインストールする。

```bash
helm install \
--name wordpress01 \
--namespace faru \
--set serviceType=NodePort \
--set mariadb.rootUser.password=***** \
--set mariadb.db.password=***** \
--set persistence.size=20Gi \
--set livenessProbe.initialDelaySeconds=1200 \
stable/wordpress
```
オプションの意味

| オプション | 役割 | 備考 |
|-----------|------------|------------|
|--namespace faru|リリースするkubernetes上のnamespaceを指定する | 
| --set serviceType=NodePort | 作成するServiceのType | うちのKubernetesは1NodeしかないのでNodePortを使っている |
| --set mariadb.rootUser.password=***** | MariaDBのルートユーザパスワード | *****は置き換える |
| --set mariadb.db.password=***** | MariaDBのWordpress用ユーザパスワード | *****は置き換える |
| --set persistence.size=20Gi | WordPress用PVCのサイズ |
| --set livenessProbe.initialDelaySeconds=1200 | 生存ヘルスチェックの初期猶予時間 | 詳細説明します |

## livenessProbe.initialDelaySeconds
ここが今回詰まったところ。
これを設定せずに起動するとPodsのログが下記のところまで進んだところで、コンテナ再起動してしまい、いつまで待ってもwordpressが起動しなかった。

```terminal
Welcome to the Bitnami wordpress container
Subscribe to project updates by watching https://github.com/bitnami/bitnami-docker-wordpress
Submit issues and feature requests at https://github.com/bitnami/bitnami-docker-wordpress/issues
 WARN  ==&gt; You set the environment variable ALLOW_EMPTY_PASSWORD=yes. For safety reasons, do not use this flag in a production environment.
nami    INFO  Initializing apache
apache  INFO  ==&gt; Reconfiguring PID file location...
nami    INFO  apache successfully initialized
nami    INFO  Initializing php
nami    INFO  php successfully initialized
nami    INFO  Initializing mysql-client
nami    INFO  mysql-client successfully initialized
nami    INFO  Initializing wordpress
wordpre INFO  WordPress has been already initialized, restoring...
mysql-c INFO  Trying to connect to MySQL server
mysql-c INFO  Found MySQL server listening at wordpress01-mariadb:3306
mysql-c INFO  MySQL server listening and working at wordpress01-mariadb:3306
wordpre INFO  Upgrading WordPress Database ...
```

調べてみると公式のissueで同じ症状を見つけた。
参考：[kubernetes chart hangs after external DB connection #136](https://github.com/bitnami/bitnami-docker-wordpress/issues/136)

つまり、コンテナ初期化に異様に時間がかかることが原因となり、起動が終わる前にヘルスチェック(livenessProbe)が走ることで、
kubernetesはコンテナが壊れたと認識として再起動をかけてしまうようだ。
この対策として、issueにもあるようにinitialDelaySecondsを大幅に引き上げた。
1200秒は適当。


# iptablesでポートフォワード
ローカルでlxc上にkubernetesノードを立てているので、外部公開用にServiceをポートフォワードをする。

kubectlのport-forwardはlocalhostしか使えないみたいなので、iptablesをいじってポートフォワードするシェルスクリプトを作ってみた。

```bash:./commands/setup_port_forward_for_kubernetes.sh
#!/bin/bash

# exit code not 0 error
set -e

### Usage
usage_exit() {
  echo "Usage: $0 [-m ADD/DEL] [-i private_if_name] [-p local_port] [-n kube_namespace] [ -s kube_service_name]"
  echo "ex) $0 -m ADD -i lxdbr0 -p 8081 -n faru -s metabase01-metabase"
  exit 1
}

### opts
while getopts m:i:p:n:s: OPT
do
  case $OPT in
    m)  MODE=$OPTARG
        ;;
    i)  PRIVATE_IF_NAME=$OPTARG
        ;;
    p)  LOCAL_PORT=$OPTARG
        ;;
    n)  NAMESPACE=$OPTARG
        ;;
    s)  SERVICE_NAME=$OPTARG
        ;;
    \?) usage_exit
        ;;
  esac
done

if [ $OPTIND -lt 11 ]; then
  echo "options are insufficient."
  usage_exit
fi

### opts check
case $MODE in
  ADD)  APPEND_OR_DELETE='I'
        APPEND_NUMBER='1'
        ;;
  DEL)  APPEND_OR_DELETE='D'
        APPEND_NUMBER=''
        ;;
  \?)   echo "option m is ADD or DEL"
        usage_exit
        ;;
esac

### private_if_namebash
echo "getting private IP"
PRIVATE_IP=$(ip -f inet address show dev ${PRIVATE_IF_NAME} | grep -o 'inet [0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+' | grep -o [0-9].* | head -n 1)
echo ">> ${PRIVATE_IP}"

### get info from kubernetes
echo "getting kubernetes service port and IP"
NODE_PORT=$(kubectl get --namespace ${NAMESPACE} -o jsonpath="{.spec.ports[0].nodePort}" services ${SERVICE_NAME})
NODE_IP=$(kubectl get nodes --namespace ${NAMESPACE} -o jsonpath="{.items[0].status.addresses[0].address}")
echo ">> ${NODE_IP}:${NODE_PORT}(${SERVICE_NAME})"

### setting iptables
COMMENT="port forward setting :: port ${LOCAL_PORT} => ${NODE_IP}:${NODE_PORT}(${SERVICE_NAME})"

echo "setting iptables"
# nat:PREROUTING
echo ">> nat:PREROUTING"
iptables -t nat -${APPEND_OR_DELETE} PREROUTING ${APPEND_NUMBER} -p tcp \
  --dport ${LOCAL_PORT} -j DNAT --to-destination ${NODE_IP}:${NODE_PORT} \
  -m comment --comment "port ${COMMENT}"

# nat:OUTPUT  for local packet
echo ">> nat:OUTPUT"
iptables -t nat -${APPEND_OR_DELETE} OUTPUT ${APPEND_NUMBER} -p tcp \
  --dport ${LOCAL_PORT} -j DNAT --to-destination ${NODE_IP}:${NODE_PORT} \
  -m comment --comment "port ${COMMENT}"

sysctl net.ipv4.conf.${PRIVATE_IF_NAME}.route_localnet=1

# nat:POSTROUTING
echo ">> nat:POSTROUTING"
iptables -t nat -${APPEND_OR_DELETE} POSTROUTING ${APPEND_NUMBER} -p tcp \
  --dst ${NODE_IP} --dport ${NODE_PORT} -j SNAT --to-source ${PRIVATE_IP} \
  -m comment --comment "${COMMENT}"

echo ">> ${MODE} ${COMMENT}"
echo "Finished!!"

exit 0
```

ローカルの8083から今回作成したwordpressのサービス(wordrpess01-wordpress)にポートフォワードする。

```bash
sudo ./commands/setup_port_forward_for_kubernetes.sh \
-m ADD -i lxdbr0 -p 8083 -n faru \
-s wordpress01-wordpress
```
ここまでくると、ブラウザからhttp://localhost:8083/ or http://\<IPアドレス\>:8083/にアクセスするとwordpress初期ページが表示される。

# まとめ
helmでwordpressをデプロイする方法を紹介した。

* kubernetesでコンテナの初期化に時間を要する場合はlivenessProbeのinitialDelaySecondsを伸ばす
* lxcで構築したkubernetesでサービスを公開するためにiptablesを使ってポートフォワードするスクリプトを作成した

# 感想
* helmの簡単インストールもまだまだこんな感じで動かないことが多い
* helmに関する日本語の記事がほとんどない
* 記事の中でおかしいところ、もっと簡単にできるってところがあればコメント頂ければ幸いである