CircleCIでDockerfileのあるプロダクトをテストする
既存のアプリケーションをコンテナ化したら、CircleCIで回していたテストがコケていたので、その修正の記録です。
CircleCIの設定を書く
CircleCIの設定をいじるのは久しぶりだったので、以下のようなことも調べつつやっていたのですが、結局ハマってだいぶ時間をとられてしまいました。
- dockerキー配下のimageは、先頭のコンテナイメージが、stepsのコマンド実行に使われる
- コンテナから別コンテナを呼ぶ際の名前を変えたければ、nameキーを使う
- ポートフォワードの指定はconfig.ymlからできない
- dockerhubのdockerはalpineだったので、ソフトウェアのインストールは
apk add
テスト用イメージの作成(ハマったとこ)
何にハマっていたのかと言うと、大きくは2つで
- コンテナから他のコンテナに繋げない
- コンテナの状態を育てていけない
というところでした。
1. コンテナから他のコンテナに繋げない
こういうことをしたかった、という実行環境のイメージは以下のような感じでした。
各種stepを実行するためのコンテナイメージ(docker)がCircleCI上で動作していて、 そのコンテナの中で、アプリケーション(python)用のコンテナがビルドされ、起動後にテストが実行される・・・みたいな。
つまり、テストアプリケーションのコンテナを、Docker in Dockerの状態にしたかったのです。
そして、アプリケーションのコンテナから参照するテスト用のコンテナ(MySQL、Azurite)は、実行用コンテナと同列で起動させれば、ローカルのDocker Composeと同じ状態にできるなって思ったのですが、 結論から言うとこれはできないそうです。
そうとは知らず、色々試してはみたので、結果だけは残しておこうかなって・・・
privilegedオプションでできること・できないこと
まずは、stepsの実行イメージをdockerベースのコンテナにし、そのコンテナでビルドしたイメージをprivilegedオプションを付けて起動させて、やりたいことができるか確認しました。
build: docker: - image: docker:git - image: arafato/azurite:latest name: azurite ... steps: - checkout - setup_remote_docker - run: name: Build image command: docker build -t hoge/hoge-app . - run: name: Run image command: docker run -td --privileged --name hoge-app hoge/hoge-app bash
この状態で、それぞれのコンテナからできたことと、できなかったことは以下になりました。
dockerイメージのコンテナを実行コンテナ、その上でビルドしたイメージをアプリケーションとして書いています。
できた
- 実行コンテナからAzuriteのイメージ名でcurl接続
できなかった
ちなみにAzuriteのホスト名解決を行うためには、dockerキーのvalue値でnameの指定も必要です(image名と同じだとしても)。
2. コンテナの状態を育てていけない
次に、アプリケーションのコンテナでテストを実行できるようにするには、 Dockerfileで実行される処理以外にも処理を実行する必要があったので、コンテナの状態を育てていく必要がありました。
そこで、docker run時にコマンドとしてpipenv sync --dev
を渡していたわけですが、これがなぜか実行されなかったため、
run時はDockerfile上のCMDを動作させないためにbash
を渡し、別のステップでdocker execで実施するようにするしかありませんでした。
また、docker execでpipenv sync --dev
を実行したステップの後、pipenv run lint
などをexecに渡して実行すると
「mypyなんぞない」
と言われたので、、、execにコマンドを渡す際はbash -c
を使って渡すようにしました。
- run: name: Install dependencies command: docker exec hoge-app bash -c 'pipenv sync --dev' - run: name: Lint code command: docker exec hoge-app bash -c 'pipenv run lint 2>&1' | tee lint.log - run: name: Test hoge-app command: docker exec hoge-app bash -c 'pipenv run test 2>&1' | tee test.log - store_artifacts: path: lint.log - store_artifacts: path: test.log
とりあえずこうすると、Docker in DockerでもLintとTestの実行はできたのですが、名前解決がうまく行かないことなどから肝心のテストがコケるので、サポート問い合わせした結果、やりたいような構成はできないという回答をもらったわけです。
以下にも直接通信ができないと書いてあるので、そのとおりだと言う話でもあるのですが。。。
最終的なconfig.yml
というわけで、最終的には実行コンテナをDockerfileのベースイメージにし、Dockerfileに書いている処理をstepsに記載するという、普通のやり方で対応しました。
以下は最終的なconfig.ymlを適当に抜粋したものになります。
version: 2 jobs: build: docker: - image: circleci/python:3.6.6 name: hoge-app environment: DB_HOST: test-db - image: circleci/mysql:5.7-ram name: test-db environment: MYSQL_USER: root MYSQL_ALLOW_EMPTY_PASSWORD: yes MYSQL_TCP_PORT: 3306 - image: redis name: redis - image: arafato/azurite:latest name: azurite working_directory: ~/hoge-app environment: LANG: ja_JP.UTF-8 LC_ALL: ja_JP.UTF-8 LANGUAGE: ja_JP:ja steps: - checkout - setup_remote_docker - run: name: Set locales command: sudo localedef -f UTF-8 -i ja_JP ja_JP.UTF-8 - run: name: Update submodule command: git submodule update --recursive --init - run: name: Install dependencies command: | sudo apt-get update && \ sudo apt-get install -y curl build-essential \ mecab mecab-ipadic libmecab-dev - run: name: Install dependencies command: pipenv sync --dev - run: name: Setup azurite filedata command: curl -v -X PUT -H 'x-ms-version:2017-11-09' http://azurite:10000/azureacount/filedata?restype=container - run: name: Lint code command: pipenv run lint 2>&1 | tee lint.log - run: name: Test hoge-app command: pipenv run test 2>&1 | tee test.log - store_artifacts: path: lint.log - store_artifacts: path: test.log