イメージはコンテナを作るためのファイルですが、そのイメージをベースイメージをもとにカスタマイズして新しい独自のイメージを生成するための仕様書がDockerfileです。
Docker Hubなどにあるイメージには最低限の機能しか入っていないので、カスタマイズすることを前提としています。
Dockerを使う場合、「コンテナ内に自分好みの開発環境や公開環境を構築」したい場合は、ベースイメージのコンテナを生成し、中に入ってapt
系のコマンドを自分で打つという、いつものOS上でやるような環境構築の方法ではなく、すでにツールや設定が終わっているイメージを作成するアプローチが一般的
コンテナは自分が使っているパソコン上でしか動かないので、コンテナ自体をカスタマイズしてしまうとほかの人に配布したい場合に無駄な設定やコピーしたくないデータまで入ってしまう可能性があるので、イメージの段階で配布可能な状態にするのが理想です。
Imageはファイル容量が大きくなりがちなので、それぞれの手元でDockerfileからイメージを生成してコンテナを作ってもらうほうがより早く環境をコピーできるという利点もあります。
ハンズオン形式で進めていくのでまずは、フォルダの生成とDockerfile
の作成を以下のような構造になるように作成します。
ターミナルのカレントディレクトリはDockerTest
フォルダにします。
DockerTest/ <- カレントディレクトリ
- Dockerfile
Dockerfileでは、まず「ベースイメージ」というもとになるイメージを指定する必要があります。
今回は、試しにubuntuバージョン20.04
を使用するので、以下の1行をDockerfile
に記述します。
FROM ubuntu:20.04
- FROM : 使用するベースのイメージを指定
- : 20.04 : そのイメージのタグ(バージョン)を明示的に指定(指定しないと:latestが対象になります)
そして作成したDockerfile
から独自のイメージの生成を以下のコマンドを使用して行います。
docker image build {ディレクトリパス}
ディレクトリパスは基本はDockerfileがあるパスを指定します。
今回は、カレントディレクトリの直下にDockerfile
を作成しているので、以下のような.
だけでディレクトリを指定しています。
docker image build .
buildコマンド
で指定するディレクトリは、特別な意味のあるディレクトリになるので、基本はDocker作業用のを作成、移動してから.
でカレントディレクトリを指定してあげるのがいいかと思います。
特別な意味のあるディレクトリの詳細な説明は以下の記事で解説しています。
詳しく知りたい方はご覧ください。
TODO 次の記事のURL
無事に自作イメージが生成できれば以下のようなログが流れると思います。
[+] Building 0.2s (5/5) FINISHED docker:desktop-linux
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 54B 0.0s
=> [internal] load metadata for docker.io/library/ubuntu:20.04 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> CACHED [1/1] FROM docker.io/library/ubuntu:20.04@sha256:fa17826afb526a9fc7250e0fbcbfd18d03fe7a54849472f86879d 0.0s
=> => resolve docker.io/library/ubuntu:20.04@sha256:fa17826afb526a9fc7250e0fbcbfd18d03fe7a54849472f86879d8bf562c 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => exporting manifest sha256:61dabcc03fce0d2215eb9b36f0e2dbae6e69a67b8b053ffed0df332ad9724e30 0.0s
=> => exporting config sha256:ca50bf172a18927296f8d57164b9a553970ac2aaa70d79c7383bf717e085d43e 0.0s
=> => exporting attestation manifest sha256:814df59e26e5454091cd545d2059370ddf5ba829e85af7b6a9bd644650cbd6ab 0.0s
=> => exporting manifest list sha256:4ea81cedea5b6d3bbc7ec1e7a7511b7666002d62d85a068579e918441e8c8ef0 0.0s
=> => naming to moby-dangling@sha256:4ea81cedea5b6d3bbc7ec1e7a7511b7666002d62d85a068579e918441e8c8ef0 0.0s
=> => unpacking to moby-dangling@sha256:4ea81cedea5b6d3bbc7ec1e7a7511b7666002d62d85a068579e918441e8c8ef0 0.0s
イメージが正常に生成できたか確認するためにdocker image ls
コマンドを実行すると、ログの「最後の行に出力されているsha256
の先頭文字列と同じ内容のIDを持ったコンテナ」が生成した自作イメージになります。
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 4ea81cedea5b 4 weeks ago 109MB
今回のDockerfile
では自作イメージに対して特別コマンドは実行していないので、中身はベースイメージと同じものが生成されています。
このイメージをよく見ると、REPOSITORY
とTAG
が<none>
になっているのが確認できると思います。
これは、イメージ生成時に名前をつけていないためこのような表示になっています。
イメージ生成時に名前をつけるオプション : -t {名前:タグ}
イメージ生成時に名前をつけないと、<none>
という名前とタグが付いてしまいます。
これの厄介なところは、すべての独自イメージに同じ<none>
がついてしまうところです。
違いを判別できるのは、IMAGE ID
のみになってしまいます。
イメージ生成時に名前をつけるには、docker image build -t {名前:タグ}
を使います:タグ
はなくても大丈夫ですが、バージョンが分かりやすくなるように使うのはいいのではないの
docker image build -t test:v1 .
実行後、docker image ls
コマンドを実行して状態を確認すると、以下のような表示になり期待通りの命名ができていることが確認できます
REPOSITORY TAG IMAGE ID CREATED SIZE
test v1 460e105405c9 8 minutes ago 129MB
RUN命令を使用することでイメージをビルドする際にイメージ内で実行したいコマンドを実行させる事ができます。
つまり、イメージ内で使いたいツールがある場合、RUN apt install 使いたいツール
のような記述をすると、「イメージ」の生成時にすでにそのツールをインストールしたイメージを生成できるということです。
例 : Ubuntuにスピートテストツールをインストールする
以下のspeedtest(ネットのスピードを測るツール)をデフォルトでインストールしたイメージを生成したます。
このツールには、curl
が必要なので合わせてインストールします。
Dockerfileは以下のように3つのRUN
コマンドが入ります。
FROM ubuntu:20.04
RUN apt update
RUN apt install -y curl
RUN curl -s https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | bash
RUN apt install -y speedtest
最初のコマンドはapt
の更新をするためのコマンドです。
-y
オプションは、apt installコマンド実行時のインストールしていいですか?プロンプトをyesで答えると事前に指定できるオプションです。
RUN実行時はこちらから入力ができないので、デフォルト動作を設定できないとbuildがそこで止まってしまうか、正常にインストールできない結果になってしまいます。
では、このDockerfileを元にイメージをビルドします。
今回は、イメージ名をspeed
としてタグをv1
にしました
docker image build -t speed:v1 .
イメージ生成後、このコンテナを使ってスピードテスを実行してみます。
docker container run -it speed:v1
speedtest
というコマンドで実行できます。
実行後ライセンスに同意するとテストが実行されます。
root@6a3b852f58f6:/# speedtest
==============================================================================
You may only use this Speedtest software and information generated
from it for personal, non-commercial use, through a command line
interface on a personal computer. Your use of this software is subject
to the End User License Agreement, Terms of Use and Privacy Policy at
these URLs:
https://www.speedtest.net/about/eula
https://www.speedtest.net/about/terms
https://www.speedtest.net/about/privacy
==============================================================================
Do you accept the license? [type YES to accept]: YES
License acceptance recorded. Continuing.
Speedtest by Ookla
Server: i3D.net - Tokyo (id: 21569)
ISP: Internet Multifeed Co.
Idle Latency: 7.63 ms (jitter: 0.46ms, low: 6.92ms, high: 8.30ms)
Download: 178.16 Mbps (data used: 236.9 MB)
12.03 ms (jitter: 4.35ms, low: 6.91ms, high: 71.27ms)
Upload: 349.27 Mbps (data used: 485.7 MB)
19.39 ms (jitter: 6.90ms, low: 7.10ms, high: 88.79ms)
Packet Loss: 2.4%
Result URL: https://www.speedtest.net/result/c/76b35e46-ecf4-4162-a1c1-7e3bc4244cc9
私は最初この説明を聞いて、「コンテナ実行時にコマンドを実行する」のではなく、「イメージ生成時にコマンドを実行する」ことに、違和感を少し覚えました。
コマンドの実行は「コンテナになってから行う」のかと思ったからです。
イメージ生成時にコマンドを実行するとファイルが増えたりしてイメージサイズが大きくなるのではないかと思いました。
イメージの時点でそのイメージをコンテナとして使う際に必要なツールや情報は入れておくのがDockerの思想らしいので、こうなっているらしい。
つまりは、「コンテナはすぐに実行できる状態」で生成されないと行けないということだと思う
私の疑問でも結局はイメージかコンテナかどちらかの生成時にツールや情報は入れないといけないとは思っていたので、そしたらコンテナを作るときに毎回ツールをインストールするよりそれをインストールしてあるイメージから作り上げたほうが効率的なのはなんとなく理解できる。(イメージの時点で情報を入れておけば一度イメージを作れれば、ツールのダウンロードリンク切れとかは、コンテナ生成時には発生しなさそうだし)
今回はDockerとDockerfileの基本をまとめました。
Dockerの思想をきちんと理解することでより正しく効率的な使い方をできるようになると思うので、ツールの使い方としてだけではなく、そのツールの設計思想を理解することを心掛けていく必要もあると考えるようになりました。