この記事は、CircleCI 2.0 のリリース後に発表した記事を補足する内容です。2.0 のリリースでは Docker Executor を使用したビルドのサポートが実装されました。しかしリリース後、CircleCI ユーザーにとって Docker の使用経験が少ないことが特に大きな障壁になっていることが判明しました。私も実際に、何人もの開発者とのやり取りや、コミュニティ、各種イベントでそのような意見を耳にしています。多くの開発者が、継続的インテグレーションのパイプラインでコンテナ テクノロジーを利用する方法を十分に把握していないというのです。

そこで今回は前回の記事の延長として、さらに便利な Docker コマンドについて詳しく説明していきます。

Docker とは

前回の記事では Docker について以下のように簡単に説明しました。

Docker は開発者とシステム管理者がコンテナを使用してアプリケーションを開発、デプロイ、実行するためのプラットフォームです。

Docker は、アプリケーションを構成して Docker イメージにパッケージ化するアプリケーション パッケージング ツールでもあります。Docker イメージを基に、アプリケーション インスタンスを実行する Docker コンテナを作成できます。Docker を使用すると、ランタイム環境の隔離、コードの一貫性や移植性の確保など、多くのメリットがあります。Docker コンテナは、Docker Engineをサポートするあらゆるオペレーティングシステムで実行可能です。

Docker の基本用語

ここでは、基本的な Docker コマンドと Docker 関連の用語を紹介します。リンク先のページではさらに詳細を確認できます。このセクションをお読みになれば、Docker についてさらに理解し、Executor を自在に制御できるようになります。コマンドは、Docker Engine がインストールされている任意のコンピューター上でローカルに実行可能です。

Docker イメージのビルド

  • Dockerfile は、イメージをアセンブルするためにコマンドラインで呼び出せるすべてのコマンドが含まれているテキスト ドキュメントです。

Dockerfile は、Docker イメージ作成のブループリントとなるファイルです。Dockerfile テンプレートには、基盤として使用するベース オペレーティングシステム イメージのような要素と、依存関係をインストールまたは構成する実行コマンド、ローカルのソースコードやアーティファクトをターゲットの Docker イメージにプッシュするコピー コマンドが含まれます。以下に、シンプルな Node.js アプリケーションの Dockerfile の例を示します。

FROM node:10

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./

RUN npm install --only=production
# If you are building your code for production
# RUN npm install --only=production

# Bundle app source
COPY . .

EXPOSE 5000
CMD [ "npm", "start" ]
  • docker images は、ローカルで見つかった Docker イメージの一覧を表示します。

このコマンドを使用すると、すべての Docker イメージと、現在ローカル マシン上にある関連データの一覧を確認できます。ターミナルにディレクトリの中身を表示する Linux の ls コマンドや Windows の dir コマンドに相当します。このコマンドは、Docker イメージをローカルで管理、メンテナンス、ビルドする方法を把握するのに非常に有用です。以下に、docker images の実行結果のサンプルを示します。

REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
ariv3ra/nodejs-circleci             latest              f419e4a6b1b8        11 days ago         943MB
node                                10                  01b816051d34        2 weeks ago         911MB
circleci/python                     3.7.6               0d2975896c73        5 weeks ago         1.43GB
ariv3ra/infrastructure-as-code101   latest              cb21a36a2973        8 weeks ago         929MB
python                              3.7.6               879165535a54        2 months ago        919MB
circleci/rust                       1-buster            218329b929cf        2 months ago        1.68GB
rust                                1-buster            f5fde092a1cd        2 months ago        1.19GB
python                              3.7.4               9fa56d0addae        6 months ago        918MB

Docker 命名規則: 上記のコマンドは、有効な Dockerfile を組み合わせることで、Dockerfile に定義されている実行コマンドに基づいて Docker イメージを構築します。イメージを構築し、コンテナを実行するうえで非常に重要なのは、Docker 命名規則を理解することです。このコマンドで Docker イメージを構築するときは、ターゲットとなる Docker イメージの名前を指定します。Docker イメージは、スラッシュで区切られた名前コンポーネントから成る命名規則に従います。アルファベットの小文字、数字、区切り文字も使用できます。区切り文字には、ピリオド、アンダースコア (1 個または 2 個)、ダッシュ (1 個以上) があります。名前コンポーネントの先頭文字または終了文字として区切り文字を使用することはできません。Docker タグの名前は、英数字、アンダースコア、ピリオド、ダッシュといった有効な ASCII 文字で構成されている必要があります。タグ名は最大 128 文字で、先頭文字にはピリオドやダッシュを使用できません。名前とタグを使用してイメージをグループ化することができます。今回はこの命名規則を使用します。

命名規則についてご理解いただけたところで、ここからは上記の例の Dockerfileに基づいて Docker イメージを構築していきます。今回は、punkdata/nodejs-circleci Git リポジトリを使用します。ローカルにコピーし、$ cd でプロジェクト ディレクトリに移動しましょう。

次に以下のコマンドを実行して、プロジェクトのソースコードとサンプルの Dockerfile を基に新しい Docker イメージをビルドします。

docker build -t tutorial/circleci-node:10 .

ビルド コマンドを実行すると、次のような結果が表示されます。

Sending build context to Docker daemon  98.07MB
Step 1/7 : FROM node:10
 ---> 01b816051d34
Step 2/7 : WORKDIR /usr/src/app
 ---> Using cache
 ---> 12b2edc2b97c
Step 3/7 : COPY package*.json ./
 ---> Using cache
 ---> 53b5b8e4e654
Step 4/7 : RUN npm install --only=production
 ---> Using cache
 ---> eefdf560bc4d
Step 5/7 : COPY . .
 ---> aa7d54e955c6
Step 6/7 : EXPOSE 5000
 ---> Running in cc427dbafdcc
Removing intermediate container cc427dbafdcc
 ---> 4ce9084e39eb
Step 7/7 : CMD [ "npm", "start" ]
 ---> Running in f6e854599ddc
Removing intermediate container f6e854599ddc
 ---> 79a8d94cbf42
Successfully built 79a8d94cbf42
Successfully tagged tutorial/circleci-node:10

docker images コマンドを実行すると、結果の一覧に新しく作成された Docker イメージが表示されます。

$ docker images

REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
tutorial/circleci-node              10                  79a8d94cbf42        4 minutes ago       1.03GB

Docker イメージが完成しました。この Docker イメージはすぐに使い始めることができます。次のセクションでは、Docker containers の形式で、この Docker イメージの新しいインスタンスを作成します。

Docker コンテナ

  • Docker コンテナは、アプリケーションを隔離して大規模に実行できるように設計されています。Docker コンテナを使用すると、アプリケーションの管理と実装を合理化することができます。

Docker コンテナの作成と実行に取り掛かる前に、コンテキストを追加しておきましょう。Docker コンテナは Docker イメージを基に作成されたオブジェクトです。Docker イメージはテンプレートであり、クッキー型のようなものです。この型によって、クッキー生地から同じ形のクッキーを簡単に作成することができます。そうして作られる 1 つひとつのクッキーが Docker コンテナです。クッキーは、成形に使用するクッキー型 (Docker イメージ) の種類によって形が変わります。私の比喩にならうなら、Docker イメージはクッキー型であり、そのクッキー型で成形された個々のクッキーは Docker コンテナということになります。

ちょっとおなかが空いてきたところで、いよいよコンテナの実行を開始します。

  • docker run は、新しいコンテナ内でコマンドを実行します。

このコマンドは、Docker コンテナを作成して実行する役目を持つ、Docker ランタイムの最重要コマンドです。

nodetest01 という名前の新しいコンテナを実行します。

docker run -d -p 5000:5000 --name nodetest01 tutorial/circleci-node:10

これで、ポート 5000 を使用して、作成したばかりの新しいコンテナ内で Node.js イメージを実行できるようになりました。Web ブラウザーで、http://localhost:5000 を開きます。すると、このアプリケーションにより、Web ページに「Welcome to CI/CD 101 using CircleCI!」という静的なテキストが表示されます。ターミナルで docker ps コマンドを実行して、コンテナが実行されていることを確認します。これについては、次のセクションで説明します。その前に、別のコンテナを実行して、新たにアプリケーション インスタンスを開始しましょう。

先ほどのコンテナには nodetest01 という名前を付けました。コンテナ名は一意である必要があるので、次のコンテナには nodetest02 という名前を付けます。さらに、新しいコンテナではポート番号も変更しなければなりません。複数のアプリケーションが同じネットワーク インターフェイス上の同じポートを使用することはできないので、50005001 に変更します。

ターミナルで次のように docker run コマンドを実行します。

docker run -d -p 5001:5000 --name nodetest02 tutorial/circleci-node:10

完璧です! これで、ローカルマシンで 2 つのコンテナを実行していることになります。ブラウザーのアドレス バーに http://localhost:5001 と入力して、2 つ目の Docker コンテナでアプリが実行されていることを確認します。

docker run コマンドは非常に堅牢性が高く、多くのプロパティ フラグと構成フラグが使用できますが、今回は詳しくは取り上げません。Docker コンテナのさまざまな実行方法についてご自分でドキュメントに目を通し、知識を蓄えることを強くお勧めします。Docker コンテナ コマンドの詳細については、こちらのドキュメントをご覧ください。

  • docker ps を実行すると、実行中の Docker コンテナの一覧が表示されます。このコマンドは docker image コマンドとよく似ていて、実行中のアクティブなコンテナを表示します。-a フラグを指定すると、実行中のコンテナと実行中でないコンテナがすべて表示されます。

ターミナルでこのコマンドを実行すると、ここまでに作成した 2 つの実行中のコンテナを確認できるはずです。

docker ps

node01 コンテナと node02 コンテナはどちらも実行中なので、次のような結果が表示されます。

CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                    NAMES
dfd30181e15c        tutorial/circleci-node:10   "docker-entrypoint.s…"   12 minutes ago      Up 12 minutes       0.0.0.0:5001->5000/tcp   nodetest02
0efaf7f11780        tutorial/circleci-node:10   "docker-entrypoint.s…"   28 minutes ago      Up 28 minutes       0.0.0.0:5000->5000/tcp   nodetest01

このコマンドは、docker run コマンドで作成した既存のコンテナを起動する目的でのみ使用します。docker run コマンドの実行時に --rm=true フラグを指定していない限り、新しく作成されたコンテナは永続化し、docker start コマンドや docker stop コマンドによって再利用できます。

次のコマンドを実行して、実行中のコンテナを停止してみましょう。

docker stop nodetest01 nodetest02

実行中だったコンテナが非アクティブになり、停止します。これらのコンテナは停止しているので、docker ps -a コマンドを実行すると、次のようにコンテナが停止しているという結果が表示されます。

CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS                      PORTS               NAMES
dfd30181e15c        tutorial/circleci-node:10   "docker-entrypoint.s…"   31 minutes ago      Exited (0) 38 seconds ago                       nodetest02
0efaf7f11780        tutorial/circleci-node:10   "docker-entrypoint.s…"   About an hour ago   Exited (0) 38 seconds ago                       nodetest01

これらのコンテナは停止していますが、永続化されているので、docker start コマンドで再起動できます。

nodetest01 コンテナを起動して、docker logs 機能の使い方を紹介する次のセクションへと進みましょう。

docker start nodetest01

ここで docker ps -a コマンドを実行すると、このコンテナのステータスは Up about a 30 seconds のように実行中として表示されます。

  • docker logs はコンテナのログをフェッチします。

このコマンドを実行すると、開発者は実行時のログを表示できます。アプリケーションはコンテナ内で実行されているので、logs コマンドは重要なアプリ出力を確認するのに便利です。アプリの実行状況を把握し、必要に応じてデバッグやトラブルシューティングを行うのに役立ちます。

nodetest01 コンテナに対して docker logs コマンドを実行してみましょう。

docker logs nodetest01

このコマンドを実行すると、次のような結果が表示され、コンテナ内のアプリが実行中であることがわかります。

Node server is running..

> nodejs-circleci@0.0.1 start /usr/src/app
> node app.js

Node server is running..

永続的な Docker コンテナは、ホストのディスク スペースとリソースを消費します。ほとんどの場合、コンテナは使い捨てです。したがって、すべてのコンテナをディスク上に永続化する必要はありません。コンテナが不要になったら、ホストから永久に削除しましょう。docker rm コマンドを実行すれば、ホストからコンテナを永久に削除できます。nodetest02 コンテナはもう不要なので、削除してディスク スペースを節約しましょう。なお、このコマンドでは、停止している非アクティブなコンテナのみを削除できます。実行中のアクティブなコンテナに対して実行するとエラーが発生するので注意してください。

nodetest02 コンテナに対して docker rm コマンドを実行します。

docker rm nodetest02

この後 docker ps -a コマンドを実行すると、nodetest02コンテナがホストから削除されたことが確認できます。このコマンドのその他のプロパティとフラグについては、こちらのドキュメントを参照してください。

  • docker rmi は、ローカルの Docker イメージを削除します。

このコマンドは、ホストから Docker イメージを削除するときに使用します。ホスト上に特定のイメージに基づくコンテナが複数ある場合、同じイメージに基づくコンテナを削除することはできません。まず、対象のコンテナを停止し、削除する必要があります。その後、docker rmi コマンドで Docker イメージを削除できるようになります。docker rmi コマンドの詳細についてはこちらのドキュメントをご覧ください。

Docker イメージのデプロイ

多くの Docker イメージは公開されており、Docker イメージのオンライン セントラル ホスティング ソリューションである Docker Hub Registry でホスティングされています。Docker Hub では、インターネット接続さえあればだれでも、公開されているイメージをレジストリから自身のローカル マシンまたはサーバーにプルすることができます。ユーザーとして登録すれば、作成したコンテナをアップロードして一般にシェアすることも可能です。こうすれば、そのコンテナをだれでも Docker Hub からイメージをプルできるようになります。

ここからは、Docker Hub の関連コマンドをいくつか紹介していきます。

  • docker pull は、レジストリからイメージまたはリポジトリをプルします。

このコマンドを使用すると、Docker Hub から有効な Docker イメージをダウンロードできます。公開されている Docker イメージは認証不要です。レジストリが非公開の場合は、割り当て済みの認証情報 (通常はユーザー名とパスワード) を使用して認証を行う必要があります。

  • docker login は、Docker レジストリにログインします。

このコマンドでは Docker レジストリに対して認証を行うことができます。このログイン コマンドを使用する場合は、Docker Hub アカウントのユーザー名とパスワードを .txt ファイルに入力し、認証情報の漏えいを防止する必要があります。

  • docker push は、イメージまたはレポジトリをレジストリにプッシュします。

このコマンドでは Docker レジストリにイメージをアップロードできます。アップロードには、有効な認証情報が必要です。

まとめ

これですべての手順が完了しました。今回は、Docker の用語とコマンド、コンテナとその使用方法について少し詳しく解説しました。紹介したコマンドは、Docker ランタイムで使用する最も一般的なコマンドです。Docker とコンテナのスキルを磨きたい場合は、これらのコマンドとそのプロパティおよびフラグについてさらに詳しく学習することをお勧めします。

基本的なコマンドについて知識を身につけておけば、CircleCI を使い始めたばかりのユーザーの前に立ちはだかる大きな障壁も、乗り越えられるようになります。

CircleCI についてもっと詳しく知りたいという方は、ドキュメント サイトをご覧ください。どうしても解決できない問題にぶつかったときには、https://discuss.circleci.com/ から CircleCI コミュニティやフォーラム サイトにアクセスしてみてください。