前回までのあらすじ
今回はDockerイメージを実際に作成してリポジトリに登録してみます。
ここで使用しているソースコードはこちらに格納してあります。
https://github.com/NewGyu/docker-demo
readme.mdにもある程度のことを書いてあるので参考にしてください。
ビルドできる環境を作る
https://github.com/NewGyu/docker-demo をビルドするには
- JDK...tomcatにデプロイするwarファイルを作るため
JREではなく、javacを含むJDKが必要になります(コンパイルするので) - git...上記のソースコードを取得するため
が必要です。
$ sudo yum install java-1.8.0-openjdk-devel git
勘違いしてほしくないのはこれらはDockerコンテナを動かす上では必要ありません。あくまで私が用意したサンプルをコンパイル・ビルドするために必要だということです。
本来は以下のようにビルド環境と実行環境は別々になるのですが、
今回のハンズオンでは一つのインスタンスでまとめてやってしまいます。
ビルドしてみよう
ソースコードを取得
普通にgithubからソースコードをcloneで取得してください。
$ git clone https://github.com/NewGyu/docker-demo.git
こんなディレクトリ構成です。
docker-demo/
├── app ...下記のinfraのイメージを元にhello.war入りのイメージを作成するもの
│ ├── Dockerfile
│ ├── build.gradle ...hello.warのビルドスクリプト
│ ├── gradlew
│ ├── gradlew.bat
│ └── src ...hellow.warのソースコード
├── infra ...tomcat7+apache2.4の入ったイメージを作成するもの
│ ├── Dockerfile
│ ├── apache.conf
│ │ ├── extra
│ │ └── httpd.conf
│ └── httpd-foreground
└── readme.md
warファイルの生成
warファイルのビルドにはgradleを使用しています。
gradleにはmavenなどの他のビルドツールと違って、gradleをインストールせずにビルドできるgradle wrapperという素敵な仕組みがありますが、本筋ではないので紹介だけして割愛します。ここではとりあえず「ソースに同梱されているgradlew
スクリプトを叩けばいい」ということだけ理解してください。
warファイルのビルド内容についてはbuild.gradle
に記述されていますが、これについても本筋ではないので割愛します。
取得したソースそのままでは代わり映えがしないのでvi src/main/webapp/index.jsp
でjspファイルを編集してみましょう。その後に以下のコマンドを実行してください。
$ cd docker-demo/app
$ ./gradlew war
すると./app/build/libs/hello.war
が生成されます。
イメージのビルド
続いてDockerイメージを作成します。
ビルドするイメージに対する内容は./app/Dockerfile
に書かれています。こちらについては後述します。
$ docker build .
Sending build context to Docker daemon 460.8 kB
Sending build context to Docker daemon
Step 0 : FROM newgyu/tomcat:1.0.0
1.0.0: Pulling from newgyu/tomcat
e2a4fb18da48: Already exists
58016a5acc80: Already exists
:
a48de4915c8d: Already exists
Digest: sha256:473b2575f7a97c904ee1c3735891d85313ff254be617f41f21ca83002e84ba46
Step 1 : RUN rm -rf /usr/local/tomcat/webapps/ROOT
---> Running in 415983e2d95c
---> 851e0dd6ea1e
Removing intermediate container 415983e2d95c
Step 2 : COPY build/libs/hello.war /usr/local/tomcat/webapps/ROOT.war
---> 6ab8fbc5b09d
Removing intermediate container ed79995666c9
Successfully built 6ab8fbc5b09d
ということでイメージID6ab8fbc5b09d
が出来ました。
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
<none> <none> 6ab8fbc5b09d About a minute ago 640.9 MB
Dockerfileとビルド動作
docker build
では引数に指定されたディレクトリにあるDockerfile
を読み込んで動作します。
サンプルアプリケーションのDockerfile
は以下の内容になっています。
FROM newgyu/tomcat:1.0.0
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY build/libs/hello.war /usr/local/tomcat/webapps/ROOT.war
ここから上記Dockerfile
の内容とdocker build
時に出力された結果ログを突き合わせて解説していきます。
Dockerfile
の記法の詳細については公式のリファレンスを参照してください。
FROM
まず注目すべきはFROM
というインストラクションです。
ここにはどのイメージを元にして作成するかが書かれています。(オブジェクト指向でいうところの継承元ですね)自前のDockerイメージを作るときには必ず何かのベースイメージから作ることになるのでFROMは必須になります。
Step 0 : FROM newgyu/tomcat:1.0.0
1.0.0: Pulling from newgyu/tomcat
e2a4fb18da48: Already exists
58016a5acc80: Already exists
:
a48de4915c8d: Already exists
Digest: sha256:473b2575f7a97c904ee1c3735891d85313ff254be617f41f21ca83002e84ba46
part1でdocker run
した時の動作と同じで、イメージファイルからコンテナを起動します。ローカルリポジトリに該当するイメージがなければ暗黙的にpull
する動作も同じです。
RUN
RUNはイメージを作成する上で最も使われるインストラクションです。
見てわかると思いますが、RUNのあとにLinuxのコマンドを書きます。DockerfileはChefやAnsibleと異なり、DSLではなくLinuxコマンドを普通記述します。Linuxに慣れている人にとってはこの方が理解しやすいでしょう。
複数のRUNを書くこともできるし、一つのRUNで&&
で区切って複数のコマンドを書くことも出来ます。
RUN apt-get update \
&& apt-get install -y --no-install-recommends
RUN apt-get update
RUN apt-get install -y --no-install-recommends
今回のサンプルではrm
コマンドを実行する実行しています。
Step 1 : RUN rm -rf /usr/local/tomcat/webapps/ROOT
---> Running in 415983e2d95c
---> 851e0dd6ea1e
Removing intermediate container 415983e2d95c
この2種類の書き方には大差はありませんが、後述するコミットの単位が異ってきます。
COPY
ビルドするマシンのローカルにあるファイルをイメージにコピーします。
COPY build/libs/hello.war /usr/local/tomcat/webapps/ROOT.war
この例ではgradleでビルドしたhello.war
をROOT.war
という名前にしてイメージ内にコピーします。
ローカルファイルのパスはDockerfile
からの相対パスで指定します。
Step 2 : COPY build/libs/hello.war /usr/local/tomcat/webapps/ROOT.war
---> 6ab8fbc5b09d
Removing intermediate container ed79995666c9
docker commit
dockerのコンテナは実は変更をするたびにバージョン管理が出来ます。このバージョン管理の仕組みは先のビルド時の、
Step 1 : RUN rm -rf /usr/local/tomcat/webapps/ROOT
---> Running in 415983e2d95c
---> 851e0dd6ea1e
^^^^^^^^^^^^
このログ出力と密接に関係しています。
少し実験してみましょう。
コンテナでshellコマンドを実行
$ docker run -it newgyu/tomcat:1.0.0 /bin/bash
root@cd7240eac1a2:/usr/local/apache2# cd /usr/local/tomcat/
root@cd7240eac1a2:/usr/local/tomcat# ls -l webapps/
total 20
drwxr-xr-x 3 root root 4096 Oct 14 13:29 ROOT
drwxr-xr-x 14 root root 4096 Oct 14 13:29 docs
drwxr-xr-x 7 root root 4096 Oct 14 13:29 examples
drwxr-xr-x 5 root root 4096 Oct 14 13:29 host-manager
drwxr-xr-x 5 root root 4096 Oct 14 13:29 manager
newgyu/tomcat:1.0.0
というイメージはtomcatをインストールしたばかりの状態なので、webappsディレクトリにtomcatデフォルトの
- ROOT
- docs
- example
- host-manager
- manager
というWEBアプリケーションがインストールされた状態になっています。
そのうちのROOTを消してみます。
root@cd7240eac1a2:/usr/local/tomcat# rm -rf /usr/local/tomcat/webapps/ROOT/
そしてshellを終わりましょう。
root@cd7240eac1a2:/usr/local/tomcat# exit
exit
コンテナの状態をコミットする
さて、掲題のdocker commit
です。
が、その前にdocker ps
で先ほど作業したコンテナのIDを確認しましょう。すでに終了させたコンテナなので-a
オプションをつけます。
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cd7240eac1a2 newgyu/tomcat:1.0.0 "/bin/bash" 21 minutes ago Exited (130) 6 minutes ago lonely_ritchie
cd7240eac1a2
をコミットすると、
$ docker commit cd7240
c426b57d7bc11b89724b574a04b1ea8b49ba0ab5bdaf4b119bed17dae691db13
newgyu@newgyu-UX31E:~/Desktop/dockersample/app$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
<none> <none> c426b57d7bc1 3 seconds ago 640.6 MB
c426b57d7bc1
というイメージが作成されましたね。
docker commit
コマンドを使うことで、コンテナのある状態をローカルリポジトリの中のイメージとして保存することができるというものになります。つまりDockerfile
,docker build
を使わなくても、
とすることでイメージが作れるのです。
ただし、これをやると「どうやって作ったのか不明なVMイメージ」と全く同じことになるので、最近では当然になってきた「Infrastructure As Code」を実践するためにDockerfile
でイメージの作り方をコード化するようにしましょう。(食品でも生産地とか添加物とかを明示する義務があるように、インフラも見える化すべきですね)
さて、ここでdocker buildを振り返る
dockerのコンテナは実は変更をするたびにバージョン管理が出来ます。このバージョン管理の仕組みは先のビルド時の、
Step 1 : RUN rm -rf /usr/local/tomcat/webapps/ROOT
---> Running in 415983e2d95c
---> 851e0dd6ea1e
^^^^^^^^^^^^
このログ出力と密接に関係しています。
ここに話を戻しましょう。もうお分かりだと思いますが、docker build
の実行過程で行われることは上記で実験してみたのと同じでdocker commit
でイメージを作成しているだけです。内部的にDockerfile
を一行ずつコミットしながらイメージを作成しているわけです。
ここが理解できると、前述の、
複数のRUNを書くこともできるし、一つのRUNで
&&
で区切って複数のコマンドを書くことも出来ます。
この意味もわかってくるかと思います。つまりは結果は同じなのだけれど、履歴の残し方が変わってくるわけです。(gitでもどの単位でcommitしていくかを考えますよね?)
docker history
コミットをしているということは当然履歴を追跡することも可能です。先ほどビルドした
Successfully built 6ab8fbc5b09d
このイメージの履歴を見てみましょう。
$ docker history 6ab8
IMAGE CREATED CREATED BY SIZE COMMENT
6ab8fbc5b09d 6 hours ago /bin/sh -c #(nop) COPY file:0a2dfbf69ce2dab06 295.4 kB
851e0dd6ea1e 6 hours ago /bin/sh -c rm -rf /usr/local/tomcat/webapps/R 0 B
a48de4915c8d 8 days ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/usr/l 0 B
8d614329fad3 8 days ago /bin/sh -c #(nop) EXPOSE 80/tcp 0 B
:
このイメージを作るために実行されたコマンドをコミット単位で見ることが出来ます。
DockerHubにイメージを登録してみよう
さて、こうして作成したイメージをリモートリポジトリであるDockerHubに登録してみましょう。
まずはDockerHubにユーザー登録
https://hub.docker.com/ から登録してください。メールアドレスがあればできるいつものやつです。
作ったイメージにタグ名をつける
DockerHubに登録する前に対象のイメージに名前(タグ)をつけてあげる必要があります。これはdocker tag
コマンドを使用します。
Usage: docker tag [OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]
$ docker tag 50ed3 newgyu/hoge:1.0.0
$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
newgyu/hoge 1.0.0 50ed314c079a 11 minutes ago 640.9 MB
引数 | 意味 |
---|---|
IMAGE | イメージのIDを指定します |
REGISTRYHOST | DockerHubにpushする場合は省略します。その他のレジストリにpushする場合はホスト名を指定します |
USERNAME | DockerHubのユーザー名を指定してください |
NAME | イメージ名を何か指定してください |
TAG | 通常は1.0 などのバージョン番号を指定します。省略するとlatest となります |
作ったイメージをpushする
$ docker push newgyu/hoge:1.0.0
The push refers to a repository [newgyu/hoge] (len: 1)
50ed314c079a: Image push failed
Please login prior to push: <---- DockerHubにログインしろと言ってきます
Username: newgyu
Password:
Email: newgyu@mail.com
WARNING: login credentials saved in /home/ec2-user/.docker/config.json
Login Succeeded
The push refers to a repository [newgyu/hoge] (len: 1)
50ed314c079a: Image already exists
2a17d1069a42: Image successfully pushed
a48de4915c8d: Image successfully pushed
これでDockerHubにアップロードされたと思いますので、DockerHubのページを確認してみましょう。
というところで今回は、
- Dockerイメージを作成する
- DockerHubにイメージを登録する
ということをやってみました。