LoginSignup
8
5

More than 3 years have passed since last update.

CircleCI上でDockerとファイルをうまくやりとりする

Last updated at Posted at 2019-12-24

この記事はミクシィグループ advent calendar 2019の12月25日分にあたります。

TL;DR

  • CircleCI remote-docker上でvolumeが使えないので、コンテナの内と外でファイルを共用できず、つらい
  • シェルコマンドで解決する
  • machine executor ubuntuを用いて解決する

CircleCI上でDockerとファイルをうまくやりとりする

先日登壇発表したNuxt.js SSR E2Eテストの実装のこぼれ話です。
スライドの内容をかいつまむと、CI上でうまくDockerを走らせてスクリーンショットテストを実行するというものですが、実装中に頭を悩ませたのがCircleCIでDocker Volumeが使えないという問題でした。そのため、ヘッドレスブラウザで生成した画像をうまく外部にアップロードできないトラブルが発生していました。この記事ではその現象をうまく切り抜けるためのテクニックを紹介します。

CircleCI 上でvolumeが使えない

さっそく具体例を見ていきます。

/blog
├── Dockerfile
├── asset
│   ├── a.txt
│   └── b.txt
└── docker-compose.yml

というフォルダがあったとしましょう。
このとき、docker-compose.ymlとDockerfileは次のように設定されています:

docker-compose.yml

version: '3.7'

services:
  blog:
    build: .
    tty: true
    volumes:
      - ./asset:/asset-in-docker
    command: ls /asset-in-docker
Dockerfile

FROM node:10-slim

Docker volumeとしてassetディレクトリをマウントし、その内部をlsするだけのシンプルな構造と仮定します。

ローカルで実行すると:

$ docker-compose up

Starting blog_blog_1 ... done
Attaching to blog_blog_1
blog_1  | a.txt  b.txt
blog_blog_1 exited with code 0

確かにa.txt および b.txtがあるのが見えます。
このまますんなりいってくれればよいのですが……。

CIで実行すると:

.circleci/config.yml

jobs:
  ls_asset:
    parallelism: 1
    docker:
      - image: circleci/node:12.13
    working_directory: ~/repo
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: docker-compose up
          command: docker-compose up
          working_directory: ~/repo/blog

docker-compose up を走らせると、

[circleci]$ docker-compose up

Starting blog_blog_1 ... done
Attaching to blog_blog_1
blog_blog_1 exited with code 0

a.txtb.txtを見つけられませんでした。

原因特定

CircleCIに直接ssh接続してみると、Dockerコンテナ自体がassetディレクトリをマウントできていないことが分かります。ここでこの情報をもとに調べてみると、
Running Docker Commands - CircleCI
という公式ドキュメントが引っかかります。

It is not possible to mount a volume from your job space into a container in Remote Docker (and vice versa). You may use the docker cp command to transfer files between these two environments.

曰く、remote docker環境ではボリュームをマウントできません。代わりにdocker cpコマンドを使って二つの環境間でファイルを交換してくださいとあります。そもそもremote dockerとはdockerを安全にビルドするための隔絶された空間なので、二環境間でディレクトリのマウントができないのには納得が行きます。

シェルコマンドで解決する

dockerfileに追記

COPY asset asset-in-docker
$ mkdir dir-in-ci
$ docker-compose restart blog && docker cp $(docker-compose ps -q blog):/repo/blog/asset ./dir-in-ci/
$ ls ./dir-in-ci

コンテナのビルド時にassetを含めるようにします。
その後ドキュメントの指示通りにdocker cpコマンドを使い、ファイルをコンテナから取り出します。ここでもう一つちょっとした落とし穴があり、少なくとも2019年12月20日現在、ドキュメントに書いてあるサンプルコマンドでは失敗するはずです。docker cpを使うためにはそのコンテナが起動状態になければならないからです。ここではrestartを用いてコンテナを起動し、docker-compose経由でコンテナidを取得してdocker cpします。各自の環境に合わせてうまく書き換えてください。

machine executor ubuntuを用いて解決する

しかしこれでは完全な解決になったとはいえません。本来この仕事ではdocker volumeを使いたかったはず。CIのためにCOPYを使い、ローカル実行時にvolumeが使えない。帳尻を合わせるようにローカル専用コマンドを作るのでは何のためにdockerコンテナを書いたのか分からなくなってしまいます。どこでも同じように・誰もが見通し良くコンテナを使えるようになっていてほしいですね。
ここは是が非でもdocker volumeを貫き通したいと思い調べ続けていたところ、次の短いページを発見しました!

How can I mount volumes to docker containers? - CircleCI

It's not possible to use volume mounting with the docker executor, but using the machine executor it's possible to mount local directories to your running Docker containers.

:cool:
machine executorすなわち実行マシンの設定を変えればいい!
ということでubuntu-1604:201903-01を使ってみます。

circleci/config.yml

jobs:
  ls_asset:
    parallelism: 1
    machine:
      image: ubuntu-1604:201903-01

volumeを使用するため、Dockerfileとdocker-compose.ymlも最初の状態に戻しておきます。

すると...


[circleci]$ docker-compose up

Starting blog_blog_1 ... done
Attaching to blog_blog_1
blog_1  | a.txt  b.txt
blog_blog_1 exited with code 0

確かにローカルと同じように動作させることができました!


ただし公式ドキュメント曰く、machine-executorは新しいコンテナビルドの方式しかサポートしないないこと、今後のアップデートによっては課金要素になり追加料金を請求されるかもしれないという二点に注意が必要です。

8
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5