CloudRunでWordPressのプロダクション環境を構築する

# はじめに
CloudRunでWordPressのプロダクション環境を構築する機会があったので、構築した際の方法を公開します。

# ゴール
・手元の環境でWordPressを開発することができる
・CI/CDを回すことができる
・WordPressのコンテンツデータを保存できる

# 利用したサービス類
・Apache
・PHP
・WordPress
・CloudRun
・CloudSQL for MySQL
・CloudStorage
・CircleCI
・GitHub

# 構築した環境
<img width="733" alt="スクリーンショット 2020-07-27 20.59.57.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93452/c6aa06d5-0a1f-f935-6007-0634fd43e290.png">

# ディレクトリ構成
今回は下記のディレクトリ構成で環境を構築しました。
|-+ .circleci/　　　　　　　- circleciのconfigを保存するディレクトリ
　+ credential/　　　　　　- GCPのcredentialキーを保存するディレクトリ
　+ src/　　　　　　　　　- WordPressのソースコードを保存するディレクトリ
　+ dockerfile
　+ docker-compose.yaml

# 構築手順

## 権限設定
次に、GCPにアクセスするためにいくつか必要な権限があるため、権限を設定していきます。

### GCPの権限を設定する
CloudRunからCloudSQLに接続するために、CloudRunのサービスエージェントにCloudSQLクライアントの権限を追加しておきます。
また、docker-composeで起動したWordPressからCloudSQLに接続するために、CloudSQLクライアントの権限を持ったサービスアカウントを1個準備し、json形式のキーをcredential.jsonという名前で事前にダウンロードしておきます。

### CircleCIの権限を設定する
CircleCIではContainer RegistryとCloud Runを操作するため、ストレージ管理とCloud Run 管理者の権限を持ったサービスアカウントを1個準備し、こちらもjson形式のキーを事前にダウンロードしておきます。
次にCircleCIの環境変数にGCLOUD_SERVICE_KEYという名前で先程ダウンロードしたjsonキーを設定します。

## Dockerfileを準備する
権限設定が終わったので、WordPressが稼働するコンテナを準備していきます。
最終的なDockerfileは[こちら](https://gist.github.com/y-hayama/42bf3165bdb3a774a1b8dd6a43d8a150#file-dockerfile)です。

### ベースとなるコンテナを選択する
[Docker Hub](https://hub.docker.com/_/php)で公式のコンテナが提供されているので、適切なものを選択します。
今回はphp:apacheを選択しました。

```Dockerfile
FROM php:apache
```

### Apacheを設定を調整する
CloudRun上ではデフォルトでは8080ポートをListenしておく必要があるため、apacheが8080ポートでListenするように設定します。

```Dockerfile
ARG PORT=8080
RUN sed -i'' -e "s/^Listen 80/Listen ${PORT}/g" /etc/apache2/ports.conf \
    && sed -i'' -e "s/:80/:${PORT}/g" /etc/apache2/sites-enabled/000-default.conf
```

### PHPのMySQLライブラリをインストールする
PHP7からmysql extensionが削除されたため、mysqliとPDOをインストールします。

```Dockerfile
RUN docker-php-ext-install mysqli pdo_mysql
```

### エントリーポイントをカスタマイズする

CloudRun上のWordPressからCloudSQLに接続するために、CloudSQL Proxyを使います。
CloudSQL Proxyのバイナリが提供されているので、コンテナの中にダウンロードしておきます。

```
RUN wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O /usr/local/bin/cloud_sql_proxy \
    && chmod +x /usr/local/bin/cloud_sql_proxy
```

またコンテナ上でCloudSQL Proxyを起動させておく必要があるため、エントリーポイントをカスタマイズしていきます。

```Dockerfile
COPY docker/cloud-run-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["cloud-run-entrypoint.sh","docker-php-entrypoint"]
CMD ["apache2-foreground"]
```

カスタムしたエントリーポイントではcloud_sql_proxyを起動しておきます。
この際に、環境変数でCLOUDSQL_INSTANCEを受け取って接続先を変更できるようにしてます。
また、docker-composeで起動した際にCloudSQLに接続できるようにcredential.jsonを読み込むように設定しておきます。

```shell
#!/usr/bin/env bash

CREDENTIAL_FILE=/credential/credential.json
SQL_OPTION="${CLOUDSQL_INSTANCE}=tcp:3306"
if [ -e ${CREDENTIAL_FILE} ]; then
    SQL_OPTION="${SQL_OPTION} -credential_file=${CREDENTIAL_FILE}"
fi

cloud_sql_proxy -instances=${SQL_OPTION} &

exec "$@"
```

## WordPressを準備する

### WordPressの接続先を設定する
WordPressのソースコードはsrcディレクトリに格納しておきます。
DBの接続先を環境変数で受け取るように、wp-config.phpのを設定します。

```php
define('DB_NAME', getenv('DB_NAME'));
define('DB_USER', getenv('DB_USER'));
define('DB_PASSWORD', getenv('DB_PASSWORD'));
define('DB_HOST', getenv('DB_HOST'));
```

## CircleCIを準備する

CircleCIではsrcディレクトリで開発したソースコードをpushした際にCloudRunに反映するところまでを構築していきます。
最終的なconfig.yamlは[こちら](https://gist.github.com/y-hayama/42bf3165bdb3a774a1b8dd6a43d8a150#file-config-yaml)です。

### デプロイまでの流れ
成果物をGitHubにpush
↓
CircleCIでコンテナを作成し、ContainerRegistryにpush
↓
pushしたコンテナを CloudRun にデプロイ

### デプロイまでの流れ

CircleCIからGCPリソースを操作できるように、GCPにログインするコマンドを準備します。
ここでは事前に登録していた環境変数GCLOUD_SERVICE_KEYに登録されているサービスアカウントのjsonキーを使います。

```yaml
commands:
  authenticate_gcloud:
    steps:
      - run:
          name:  Authenticate gcloud
          command: |
              echo ${GCLOUD_SERVICE_KEY} | gcloud auth activate-service-account --key-file=-
              gcloud auth configure-docker
```

次にビルドからCloudRunにデプロイするまでのコマンドを準備します。
この際に、ブランチごとにCloudRunのDBへの接続先情報を変更できるように、接続先をパラメータ化しておきます。

```yaml
commands:
  build_image:
    steps:
      - run:
          name: Build the image
          command: |
            docker build -t ${IMAGE}:${CIRCLE_BRANCH} .
  push_image:
    steps:
      - run:
          name:  Push the image
          command: |
            docker push ${IMAGE}:${CIRCLE_BRANCH}
  deploy_image:
    parameters:
      CLOUDRUN_INSTANCE:
        type: string
      DB_NAME:
        type: string
      CLOUDSQL_INSTANCE:
        type: string
    steps:
      - deploy:
          name: Deploy the image
          command: |
            gcloud run deploy << parameters.CLOUDRUN_INSTANCE >> \
              --platform=managed \
              --project=${GOOGLE_PROJECT_ID} \
              --region=${REGION} \
              --memory=1Gi \
              --cpu=2 \
              --allow-unauthenticated \
              --image=${IMAGE}:${CIRCLE_BRANCH} \
              --add-cloudsql-instances=<< parameters.CLOUDSQL_INSTANCE >> \
              --set-env-vars CLOUDSQL_INSTANCE=<< parameters.CLOUDSQL_INSTANCE >> \
              --set-env-vars DB_NAME=<< parameters.DB_NAME >> \
              --set-env-vars DB_HOST=127.0.0.1 \
              --set-env-vars DB_USER=*** \
              --set-env-vars DB_PASSWORD=***
```

最後に今まで準備してきたコマンドを使って実際にジョブを構築します。
この際にDBへの接続先と実行したいブランチを指定します。

```yaml
jobs:
  build_and_deploy:
    executor: default
    steps:
      - checkout
      - setup_remote_docker:
          version: 18.06.0-ce
      - build_image
      - authenticate_gcloud
      - push_image
      - deploy_image:
          CLOUDRUN_INSTANCE: cloudrun
          DB_NAME: cloudsql
          CLOUDSQL_INSTANCE: project-id:region:dbname

workflows:
  version: 2
  build_and_deploy:
    jobs:
      - build_and_deploy:
          filters:
            branches:
              only:
                - master
```

これでソースコードをGitHubにPushすることでCloudRunが起動するようになりました。
ブランチごとにCloudRunを起動するようにすれば開発環境など複数の環境を簡単に立ち上げることができます。

### WordPressのコンテンツをCloudStorageに保存する
これでCloudRunでWordPressを動かしつつ、コンテンツを保存できるようになりました。
ただし、画像データについてはまだ永続化されていないです。
ここについてはWP-Statelessなどのプラグインを利用することでデータをCloudStorage上に保存できるようになります。

## docker-composeを準備する

最後にdocker-composeではPC上でWordPressが稼働できるように設定していきます。
最終的なdocker-compose.yamlは[こちら](https://gist.github.com/y-hayama/42bf3165bdb3a774a1b8dd6a43d8a150#file-docker-compose-yaml)です。

### 環境変数の準備
PHPとCircleCIで待ち受けている環境変数にDBの接続先を指定していきます。

```yaml
    environment:
      - DB_NAME=cloudsql
      - DB_USER=***
      - DB_PASSWORD=***
      - DB_HOST=127.0.0.1
      - CLOUDSQL_INSTANCE=project-id:region:dbname
```

### ディレクトリをマウントする
次にcredentialディレクトリとsrcディレクトリをマウントします。

```yaml
    volumes:
      # 事前に準備しておいたcredential.jsonをdocker上から見えるようにする
      - ./credential:/credential
      # srcディレクトリを
      - ./src:/var/www/html
```

### コンテナイメージを起動する
設定が終わったので、docker-composeでコンテナイメージを起動できれば構築完了です。

```shell
docker-compose up -d
```



## 参考記事
https://medium.com/acadevmy/how-to-install-a-wordpress-site-on-google-cloud-run-828bdc0d0e96