【AWS】CodeBuildでPHP Unitを流すためのDockerfile

Posted on
AWS Dockerfile PHP Unit

CodeBuildでユニットテストを流したい

半年前くらいにAWSのCodeBuildで「Gitから引っ張ってきたソースでPHPUnitを動かしたいんだけど」と言われたので作成したdockerfileを一部修正して紹介する。

前提

前提とする構成は下記の通り。

(created with cloudcraft.io)

  • 開発者がCodeCommitにホスティングしているGitリポジトリにpush
  • CodeBuildが走る(UnitTest, デプロイ資材の作成)
  • CodeDeployからEC2にデプロイ 上記の流れをCodePipelineで組んでいる。

で、CodeBuildでUnitTestを走らせるために今回のdockerfileを作成した・・・というのが背景。

テストについては、単体テスト(DB関連含む)になるので、テスト用のDBや使用するPHP extensionとかも準備することになった。

dockerfile作成

CodeBuildは、実行時にECRからdocker imageをpullしてくる。そのため、CodeBuild設定前の

  • dockerfileの作成
  • docker imageのbuild及びpush(to AWS ECR)

がキモになってくる。というかそれが全て。

で、色々試行錯誤して作成したdockerfileは下記の通り。

# dockerfile
FROM php:7

ARG composer_checksum=48e3236262b34d30969dca3c37281b3b4bbe3221bda826ac6a9a62d6444cdb0dcd0615698a5cbe587c3f0fe57a54d8f5
ARG composer_url=https://raw.githubusercontent.com/composer/getcomposer.org/ba0141a67b9bd1733409b71c28973f7901db201d/web/installer

ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PATH=$PATH:vendor/bin
ENV DEBIAN_FRONTEND noninteractive

RUN apt-get update

RUN apt-get install -y --no-install-recommends 
      apt-utils 
      locales 
      mariadb-server-10.3 
      wget 
  && echo ja_JP.UTF-8 UTF-8 > /etc/locale.gen 
  && locale-gen 
  && update-locale LANG=ja_JP.UTF-8

RUN mkdir -p /etc/sysconfig
RUN echo "NETWORKING=yes" > /etc/sysconfig/network

RUN apt-get install -y --no-install-recommends 
      curl 
      git 
      python3 
      python3-dev 
      python3-pip 
      libzip-dev 
      zlib1g-dev 
      libxslt1-dev 
      nodejs 
      npm 
      zip 
      unzip 
  && pip3 install --upgrade setuptools 
  && pip3 install awscli --upgrade 
  && pip3 install --upgrade pip 
  && export PATH=$PATH:$HOME/.local/bin 
  && php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 
        && php -r "if (hash_file('sha384', 'composer-setup.php') === 'a5c698ffe4b8e849a443b120cd5ba38043260d5c4023dbf93e1558871f1f07f58274fc6f4c93bcfd858c6bd0775cd8d1') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" 
  && php composer-setup.php 
  && php -r "unlink('composer-setup.php');" 
  && mv composer.phar /usr/local/bin/composer 
  && cd /tmp 
  && export PATH=$PATH:$HOME/.local/bin 
  && composer create-project --prefer-dist laravel/laravel codebuild

ADD ./setup.sql ./setup.sql
# ADD php.ini /usr/local/etc/php/php.ini

RUN docker-php-ext-configure pdo_mysql --with-pdo-mysql=mysqlnd 
  && docker-php-ext-configure mysqli --with-mysqli=mysqlnd 
  && docker-php-ext-install pdo_mysql

RUN /usr/bin/mysqld_safe & 
      sleep 10s && 
      cat setup.sql | mysql

CMD ["/usr/bin/mysqld_safe"]

ENV LC_ALL ja_JP.UTF-8

上記のdockerfile内では、PHPUnit用のデータベースを作成している。その際に実行されるSQLは下記の通り。

-- setup.sql
CREATE DATABASE IF NOT EXISTS test_database;
USE test_database;
GRANT ALL PRIVILEGES ON *.* TO test_user@localhost IDENTIFIED BY 'password';

このdockerfileにおいて実行される内容をざっくりと説明すると以下のようになる。

  • MySQLのインストール
  • PHPのインストール
  • composerのインストール
  • 空のLaravelプロジェクトの作成
  • AWS CLIに必要なpython3のインストール
  • AWS CLIのインストール
  • PHP Extension(主にテストDB接続用)のインストール
  • mysqlサーバーの起動

空のlaravelプロジェクト作成はいらないかもしれない。結局CodeBuildで実行されるshell(buildspec.yaml)内部にて、インプットソース丸ごとcomposer installしちゃうので。

docker

imageのbuild&ECRへのpush ここで、dockerイメージのビルドとAWS ECRへのpushを行うが、AWS CLIの設定は既に済んでいるものとする。

また、あらかじめECRに任意のリポジトリを作成しておいて、ARNをメモっとくこと。

準備が整ったら、先述のdockerfileとsetup.sqlの配置ディレクトリにおいて、下記のコマンドを実行する。

$ docker build -t codebuild .
$ docker tag codebuild:latest <作成したECRのARN>
$ docker push <作成したECRのARN>/codebuild:latest

こうすると、AWSのECRに作成したリポジトリに、dockerイメージがpushされる。

これでCodeBuildでPHP、MySQL、Composer、PHPUnitが動かせる環境が整ったので、buildspecを作成し、CodeBuildのプロジェクトを作成してインプット・アウトプットをCodePipelineで指定してやればOK。

サンプルのbuildspec.yaml(抜粋)も記載しておく。

# buildspec.yaml
version: 0.2

phases:
  build:
    commands:
      - service mysql restart
      - cd ${CODEBUILD_SRC_DIR}/src
      - composer install
      - composer dump-autoload
      - vendor/bin/phpunit
      - composer install --no-dev
      - php artisan config:cache
artifacts:
  secondary-artifacts:
    BuildArtifact:
      files:
        - '**/*'