コンテナイメージを実際に作成してみよう !

スペシャリストから学ぶコンテナ技術 第 3 回

2021-08-03
デベロッパーのためのクラウド活用方法

Author : 原田 和則 / 林 政利

こんにちは、テクニカルソリューションアーキテクトの原田です。

スペシャリストから学ぶコンテナ技術 第 1 回」では、環境差分をなくすための手段の 1 つとしてコンテナならびにコンテナ技術について紹介しました。

スペシャリストから学ぶコンテナ技術 第 2 回」では、コンテナ実装のソフトウェアとしてデファクトスタンダードになっている Docker が生まれた背景について紹介しました。

今回は、コンテナイメージの作成を実際に行って、Docker の使い方の基礎を紹介します。
本記事は、原田とコンテナスペシャリスト ソリューションアーキテクトの 林 (@literalice) の共同執筆となります。


(動作環境について)
私の環境は Mac で、Docker for Desktop を利用しています。
この記事はハンズオンでありませんが、Windows や Linux 上でも動きますので、一緒にお試しいただくことも可能です。その場合は、Docker for Desktop を こちらの Docker 社の Web サイト からダウンロード & インストールを行ってください。

この連載記事のその他の記事はこちら

選択
  • 選択
  • スペシャリストから学ぶコンテナ技術 第 1 回
  • Docker が生まれるまでの課題と背景とは ?
  • コンテナイメージを実際に作成してみよう !

何はともあれコンテナを起動する

Docker の場合、コンテナはコンテナイメージから起動します。

「コンテナ」と「コンテナイメージ」の違いが分からないというのは Docker 入門あるあるの 1 つで、詳しい説明は Docker Docs に任せますが、ここでは、「コンテナイメージ = コンテナのテンプレートファイル」、「コンテナ = イメージ ( テンプレート ) から起動した実体 」という理解で十分です。

既に AWS をご利用中の方であれば、 Amazon マシンイメージ (AMI) と EC2 インスタンスの関係と似ていると言えばピンとくるかもしれません。
このコンテナイメージは、誰かがあらかじめ作ったものを利用することも、自分で作成することも可能です。

それではさっそく、具体的に Ubuntu 18.04 のコンテナを起動してみます。
先ほど説明したとおり、Ubuntu 18.04 のコンテナを起動するには、Ubuntu 18.04 のコンテナイメージが必要となるため、まずはそれをローカルにダウンロードします。

コンテナイメージのダウンロード

コンテナイメージをダウンロードする方法ですが、docker pull コマンドでダウンロードできます。コンテナイメージの中身が Ubuntu であっても、Amazon Linux 2 であっても、つまり、中身がどんなイメージであっても、docker pull でダウンロードできます。すごく便利ですね。

これでコンテナイメージをダウンロードする方法は分かりましたが、肝心の「どこ」からダウンロードすればいいかがまだ謎ですね。つまり、どこかにコンテナイメージの保存場所があるわけです。この保存場所のことを一般的にコンテナレジストリ (Container Registry) と言います。

コンテナイメージの取得 (ダウンロード)

このコンテナレジストリを提供しているサービスはいくつかあり、その中の 1 つが Docker Hub で、名前を聞いたことある読者の方も多いのではないでしょうか。AWS でも、コンテナレジストリとして、Amazon Elastic Container Registry (ECR) というサービスを提供しており、プライベート用/パブリック用のコンテナレジストリとしてご利用いただけます。

今回は ECR 上に公開されている Ubuntu 18.04 のコンテナイメージ をダウンロードします。

$ docker pull public.ecr.aws/ubuntu/ubuntu:18.04   # docker pull <コンテナイメージの公開パス:タグ>  
 
18.04: Pulling from ubuntu/ubuntu
030309cad0ba: Downloading [================>                                  ]  21.44MB/65.62MB
1e77dd81f9fa: Download complete 
6f15325cc380: Download complete 
Status: Downloaded newer image for public.ecr.aws/ubuntu/ubuntu:18.04

ダウンロードが終わりましたね。念の為、確認します。
ローカルに保存されているコンテナイメージの確認には、docker images コマンドを利用します。

$ docker images
REPOSITORY                                                                                 TAG                 IMAGE ID            CREATED             SIZE   
public.ecr.aws/ubuntu/ubuntu                                                               18.04               6e5f35703668        3 months ago        63.3MB

コンテナイメージが 1 つ表示されており、Ubuntu 18.04 のコンテナイメージがローカルに保存されていることが分かります。

それでは、次に、このコンテナイメージから Ubuntu 18.04 コンテナを起動します。

コンテナイメージからコンテナの起動

先ほど、どんなコンテナイメージでも docker pull コマンドでダウンロードできるというお話をしましたが、コンテナの起動に関しても、同様なコマンドが用意されています。それが docker run コマンドです。

つまり、中身がどんなコンテナでも docker run というコマンドで、コンテナイメージからコンテナを起動できます。すごく便利ですね。

それではコンテナを起動してみます。

コンテナイメージからのコンテナの起動 

$ docker run -it public.ecr.aws/ubuntu/ubuntu:18.04 bash   ## docker run <起動オプション> <コンテナイメージ> <コンテナ起動時に実行するコマンド>
root@a603c316cdb8:/#  ###コンテナ内のbash###

Ubuntu 18.04 コンテナイメージからコンテナが起動して、その中で root ユーザーとして bash が起動しています。

やや見にくいのですが、以下の部分は、Ubuntu コンテナ内の bash を表示しています。

root@a603c316cdb8:/#

試しに、OS やユーザーを確認してみると、OS が Ubuntu 18.04 であること、そして、root ユーザーとして bash を起動していることが分かります。

root@a603c316cdb8:/# whoami
root

root@a603c316cdb8:/# cat /etc/os-release 
NAME="Ubuntu"
VERSION="18.04.5 LTS (Bionic Beaver)"
~ 省略 ~

次に、このコンテナ中で、Node.js で書いた簡単なアプリケーションを動かしてみたいと思います。


コンテナ内で簡単なアプリケーションを動かす

コンテナに Node.js をインストールする

まず、このコンテナに Node.js をインストールします。
コンテナとして動いてはいますが、中身は Ubuntu 18.04 ですので、apt コマンドでインストールします。

root@a603c316cdb8:/# apt update
~ 省略 ~
root@a603c316cdb8:/# apt install -y nodejs   ## Node.js のインストール
~ 省略 ~
root@a603c316cdb8:/# which node   ## Node.js がインストールされたことを確認
/usr/bin/node

アプリケーションコードを用意して、コンテナ内で実行する

それでは、次に、簡単なアプリケーションコードを 1 つ用意して、実行してみます。
具体的には、標準出力に Hello, world と表示するだけの JavaScript コード (index.js) をコンテナ内で作成し、実行します。

root@a603c316cdb8:/# echo "console.log('Hello, world')" > index.js   # JavaScript コードの作成
root@a603c316cdb8:/#
root@a603c316cdb8:/# node index.js  # 作成したコードを実行
Hello, world

Hello, world と表示されているので、問題なくアプリケーションが動いていることが確認できました。
かなりシンプルなのであまり実感はないかもしれませんが、これでアプリケーションがインストールされたコンテナの完成です。

次に、このアプリケーション入りコンテナを再利用 (例えば、別の環境でも同じコンテナを起動) できるように、コンテナイメージを作成します。

コンテナ内作業でのアプリケーション入りコンテナの作成


コンテナからコンテナイメージを作成する

コンテナから抜けて、コンテナの状態を確認する

コンテナ内での作業は終わっているので、コンテナから抜け、Mac の bash に戻ります。

root@a603c316cdb8:/# exit   # コンテナから抜けます
$  
$  # Mac の bash に戻る

次に docker ps -a というコンテナ一覧を表示するコマンドを実行して、コンテナの状態を確認してみます。

$ docker ps -a   # コンテナ一覧を表示するコマンド
CONTAINER ID        IMAGE                                 COMMAND    CREATED             STATUS                      PORTS                               NAMES
a603c316cdb8        public.ecr.aws/ubuntu/ubuntu:18.04    "bash"     About an hour ago   Exited (0) 43 seconds ago                                       boring_kalam

このコンテナは、a603c316cdb8 という CONTAINER ID を持っており、STATUS が Exited (0) 43 seconds ago となっているため、43 秒前に正常終了しているといったことが分かります。

コンテナイメージの作成

では、このコンテナからコンテナイメージを作ります。
コンテナからコンテナイメージを作るには、docker commit コマンドを利用します。

以下のコマンドで、hello-node-app という名前でコンテナイメージを作成し、そのコンテナイメージに v1 というタグをつけています。ここで、先ほど確認した CONTAINER ID である a603c316cdb8 を使用します。

$ docker commit a603c316cdb8 hello-node-app:v1   # docker commit <コンテナID> <コンテナイメージ名:タグ>
sha256:82502034c657ef02b185c35e77e4ec277e9e23100133b7a65cf1279ea062c5f9

これで、hello-node-app というコンテナイメージが作成されたはずですが、念の為、確認しましょう。

コンテナからのコンテナイメージの作成

作成したコンテナイメージの確認

コンテナイメージの確認には、docker images コマンドを利用します。先ほども使いましたね。

$ docker images
REPOSITORY                            TAG                 IMAGE ID            CREATED             SIZE                                                                                TAG                 IMAGE ID            CREATED             SIZE
hello-node-app                        v1                  82502034c657        8 minutes ago       161MB
public.ecr.aws/ubuntu/ubuntu          18.04               6e5f35703668        3 months ago        63.3MB

もともとあった Ubuntu 18.04 のコンテナイメージに加えて、先ほど作成した hello-node-app コンテナイメージが増えています。これで、コンテナイメージが作成されていることが確認できました。

それでは、 この hello-node-app コンテナイメージを使ってコンテナを起動してみます。


作成したコンテナイメージからコンテナを起動する

コンテナの起動コマンドは docker run でしたね。さっそく、起動してみます。
今回は、コンテナ内で bash を起動せずに、コンテナ起動時に アプリケーションを実行 (node index.js) してみます。

$ docker run hello-node-app:v1 node index.js
Hello, world
$ 

Hello, world と表示されているので、アプリケーションは問題なく動いているようです。

動作確認もできたので、これで、アプリケーション入りのコンテナイメージ作成は完了です。

今までの流れを少し振り返ってみましょう。


docker commit でのコンテナイメージの作成振り返り

今回は大きく分けて以下の 3 つの手順でアプリケーション入りのコンテナイメージ作成を行いました。

  1. docker run コマンドで、Ubuntu 18.04 のコンテナを起動
  2. コンテナ内作業で、Node.js のインストールとアプリケーションコードの用意
  3. docker commit コマンドで、アプリケーション入りコンテナからコンテナイメージの作成

コンテナ内作業と docker commit コマンドによりコンテナイメージ作成

これらの手順は、使用する OS や動作させるアプリケーションによって多少変わってくると思いますが、基本的には手作業で行います。特に、2 つ目のコンテナ内作業は、今回は簡単なアプリケーションだったため手作業でも特に問題はないかもしれませんが、ビジネスアプリケーションの場合は、フレームワークや依存ライブラリをインストールしたりなど複雑なセットアップ手順が必要なことが多いです。

そして、手順が複雑であればあるほど、どうしても作業ミスが発生しやすくなります。
つまり、環境差分をなくすために Docker を採用したにも関わらず、コンテナイメージの作成が手作業だったために、作成されたコンテナイメージ自体を信用できないといった本末転倒なことになりかねません。

そのため、実運用だと、コンテナイメージを作成する手段として docker commit を使うことはあまりなく、その代わりに Dockerfile という定義ファイルと docker build というコマンドを使って、コンテナイメージを作成することがほとんどです。


Dockerfile と docker build でのコンテナイメージの作成

Dockerfile とは

アプリケーションを作るとき、アプリケーションコード以外にも設定ファイルなどいろいろ書くと思いますが、Docker にも設定ファイルがいくつかあります、その 1 つが Dockerfile です。ただ、データベースの接続情報やログの設定といったパラメーターを記載するのではなく、Dockerfile にはこういうコンテナイメージをつくりたいという定義を書きます。

先ほどのコンテナイメージ作成の手順をステップに分けると、以下の 4 つのステップでコンテナを作り、その後、コンテナイメージを作りました。

  1. *Ubuntu 18.04 コンテナの中で
  2. apt updateapt install -y nodejs を実行し、Node.js をインストール
  3. echo "console.log('Hello, world')" > index.js を実行し、アプリケーションコードを用意
  4. node index.js を実行し、アプリケーションを実行このステップを Dockerfile に書いてみます。

このステップを Dockerfile に書いてみます。

Dockerfile を作る

それでは、テキストエディタで、Dockerfile というファイルを作成し、以下の内容を記載します。

FROM public.ecr.aws/ubuntu/ubuntu:18.04

RUN apt update && apt install -y nodejs

RUN echo "console.log('Hello, world')" > index.js

CMD ["node", "index.js"]

いかがでしょうか ?
FROM、RUN や CMD の正確な意味は分からなかったとしても、前述の 4 つステップと 1 つ 1 つ比較すると何をやっているのかなんとくなる分かるかと思います。

正確な意味やその他の使い方は こちらの Dockerfile リファレンス に任せますが、今の時点では、以下のような理解で大丈夫です。

FROM

これから作成するコンテナイメージのベースになるコンテナイメージを指定します。これをベースイメージと言ったりします。
アプリケーション開発に限らず、モノを作る際、なにかベースとなるものがあって、それに修正やアレンジを加えて、新しいモノを作るっていうことをよくやると思うんですが、Docker でもそれを行います。

RUN

ベースイメージから起動したコンテナ内で実行するコマンドを記載します。
複数行書くことも可能で、起動したコンテナの中で、上から順番に記載したコマンドを実行してくれます。
今回だと、Node.js のインストールやアプリケーションコードの用意を行っています。

CMD

作成したコンテナイメージからコンテナを起動した際に、デフォルトで実行してくれるコマンドを指定します。ここには、アプリケーションの実行コマンドを書くことが多いです。
コンテナを起動 (docker run) するだけで、コンテナ内のアプリケーションが起動してくれるのですごく便利です。
CMD の 代わりに ENTRYPOINT を使うこともあります。若干違いはありますが、詳細な説明はここでは割愛します。

Docker 入門者あるある ~ RUN と CMD の違いに悩む ~

Docker 入門者あるあるの 1 つですが、RUN と CMD の用途がごちゃ混ぜになってしまうことがあります。
例えば、以下のように、CMD とすべきところが RUN となっていたりすることがあります。

*# Dockerfile 間違い例*
FROM public.ecr.aws/ubuntu/ubuntu:18.04

RUN apt update && apt install -y nodejs

RUN echo "console.log('Hello, world')" > index.js

RUN node index.js   # 正しくは CMD ["node", "index.js"]

現時点では以下の理解で十分です。

  • RUN は、アプリケーション動作環境のセットアップコマンド (コンテナイメージの作成の際に実行するコマンド)を書く
  • CMD は、アプリケーションの起動コマンド (コンテナ起動時に自動実行されるコマンド) を書く

(node index.js は、アプリケーションを実行するコマンドなので、RUN ではなくて CMD を使うということですね)

以上で、Dockerfile の完成です。

次に、この Dockerfile を使って、コンテナイメージの作成 (ビルド) を行います。

Dockerfile の作成

docker build コマンドを使ったコンテナイメージの作成

Dockefile に記載した内容に沿って、コンテナイメージを作成するには docker build というコマンドを利用します。どんなアプリケーションを利用しているか問わず Dockerfile といった必要なファイルを用意しておけば、docker build コマンドで、コンテナイメージを作成することができます。すごく便利ですね。

さっそく コンテナイメージの作成 (ビルド) を行います。
hello-node-app という同じコンテナイメージ名で、今度は v2 というタグ をつけて、コンテナイメージを作成します。

$ docker build -t hello-node-app:v2 .   # docker build -t <コンテナイメージ名:タグ> <Dockerfileが存在しているディレクトリ>

Sending build context to Docker daemon  2.048kB
### Step1: ベースイメージの指定
Step 1/4 : FROM public.ecr.aws/ubuntu/ubuntu:18.04
 ---> 6e5f35703668

### Step2: アップデートと Node.js のインストール
Step 2/4 : RUN apt update && apt install -y nodejs
 ---> Running in d0d0490b2526
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
~ 省略 ~

### Step3: アプリケーションコードの用意
Step 3/4 : RUN echo "console.log('Hello, world')" > index.js
 ---> Running in ef04fd44bde1

### Step4: コンテナ起動時に、アプリケーションが自動実行されるように設定
Step 4/4 : CMD ["node", "index.js"]
 ---> Running in 2781f17e96aa

Successfully built b978ea3f75e2
Successfully tagged hello-node-app:v2

Dockerfile の中身が、上から順番に Step1, Step2, Step3, Step4 と実行されて、最終的に hello-node-app:v2 というコンテナイメージが作成されていることが分かります。

docker build コマンドでのコンテナイメージの作成

作成したコンテナメージの動作確認

docker images コマンドで、コンテナイメージ一覧を確認すると、v2 というタグがついた hello-node-app というコンテナイメージが確認できます。

$ docker images
REPOSITORY                                                                                 TAG                 IMAGE ID            CREATED             SIZE                                                                                TAG                 IMAGE ID            CREATED             SIZE
hello-node-app                                                                             v1                  82502034c657        58 minutes ago      161MB
hello-node-app                                                                             v2                  b978ea3f75e2        26 minutes ago      161MB
public.ecr.aws/ubuntu/ubuntu                                                               18.04               6e5f35703668        3 months ago        63.3MB

作成したコンテナイメージからコンテナを起動し、アプリケーションの動作確認をしてみます。

Dockerfile 内の CMD の記述で、コンテナ起動時に自動で node index.js を実行するはずなので、 docker run だけ実行すれば、勝手にアプリケーションが実行されるはずですね。
それではやってみましょう。

$ docker run -t hello-node-app:v2
Hello, world

Hello, world と出力されていますね。確かに docker run だけで、アプリケーションが起動したことが確認できました。

コンテナの起動とアプリケーションの自動実行

動作確認もできたので、これで Dockerfile と docker build コマンドを使ったコンテナイメージ作成は完了です。

まとめ

今回は Docker を使って、アプリケーション入りのコンテナイメージを実際に作成しました。コンテナイメージの作成方法は大きく分けて 2 通り(docker commitdocker build) あり、それぞれ実際に行ってみました。

まず、コンテナ内作業でアプリケーションの動作確認をセットアップしたあと docker commit でコンテナイメージを作成する方法です。次に、コンテナイメージのセットアップやアプリケーション起動手順を記載した Dockerfile を用意して docker build でコンテナイメージ作成する方法です。

どちらの方法でも、最終的にアプリケーション入りのコンテナイメージが作成されるという結果は変わりませんが、途中で述べたように、手作業を極力減らすことで作業ミスを減らすことができたり、自動化の仕組みに載せやすいといった観点などで、実ワークロードだと、後者の「 Dockerfile と docker build を使いコンテナイメージを作成する」ことがほとんどです。

Dockerfile と docker build コマンドを利用したコンテナイメージ作成フロー

今回の第 3 回は、コンテナイメージの作成にフォーカスして、Docker の基本的な使い方を紹介しました。

次回は、作成したコンテナイメージをコンテナレジストリにアップロードして、別の環境でコンテナを動かしてみながら、少し発展的な内容にも触れてみたいと思います。

この連載記事のその他の記事はこちら

選択
  • 選択
  • スペシャリストから学ぶコンテナ技術 第 1 回
  • Docker が生まれるまでの課題と背景とは ?
  • コンテナイメージを実際に作成してみよう !

builders.flash メールメンバーへ登録することで
AWS のベストプラクティスを毎月無料でお試しいただけます

筆者プロフィール

原田 和則
アマゾン ウェブ サービス ジャパン合同会社
テクニカルソリューションアーキテクト

インフラエンジニアとして経験を積む中で、クラウドに携わりたい想いで AWS へ。
業種業態、技術領域を問わず、様々なお客様の AWS 活用支援を行っています。

林 政利 (@literalice)
アマゾン ウェブ サービス ジャパン合同会社
コンテナスペシャリスト ソリューションアーキテクト

フリーランスや Web 系企業で業務システムや Web サービスの開発、インフラ運用に従事。近年はベンダーでコンテナ技術の普及に努めており、現在、AWS Japan で Amazon ECS や Amazon EKS でのコンテナ運用や開発プロセス構築を中心にソリューションアーキテクトとして活動中。

AWS を無料でお試しいただけます

AWS 無料利用枠の詳細はこちら ≫
5 ステップでアカウント作成できます
無料サインアップ ≫
ご不明な点がおありですか?
日本担当チームへ相談する