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


はじめに

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

ゴール

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

利用したサービス類

・Apache
・PHP
・WordPress
・CloudRun
・CloudSQL for MySQL
・CloudStorage
・CircleCI
・GitHub

構築した環境

ディレクトリ構成

今回は下記のディレクトリ構成で環境を構築しました。
|-+ .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はこちらです。

ベースとなるコンテナを選択する

Docker Hubで公式のコンテナが提供されているので、適切なものを選択します。
今回はphp:apacheを選択しました。

FROM php:apache

Apacheを設定を調整する

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

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をインストールします。

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を起動させておく必要があるため、エントリーポイントをカスタマイズしていきます。

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を読み込むように設定しておきます。

#!/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のを設定します。

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はこちらです。

デプロイまでの流れ

成果物をGitHubにpush

CircleCIでコンテナを作成し、ContainerRegistryにpush

pushしたコンテナを CloudRun にデプロイ

デプロイまでの流れ

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

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への接続先情報を変更できるように、接続先をパラメータ化しておきます。

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への接続先と実行したいブランチを指定します。

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はこちらです。

環境変数の準備

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

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

ディレクトリをマウントする

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

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

コンテナイメージを起動する

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

docker-compose up -d

参考記事