【Docker使えるようになりたい】【#8 CMD & WORKDIR】Dockerfileの使い方 応用編

はじめに

Dockerfile内でよく使う命令をまとめてみました。

この記事では2つの命令を解説しますが、どちらも、多用することの多い命令になります。

この記事で解説する命令

  • CMD : 実行時のデフォルト実行命令を変更
  • WORKDIR : イメージビルド時の、カレントディレクトリを変更し、最後のディレクトリは-itでコンテナ内のシェルに入った時の最初のディレクトリになる

デフォルト実行する命令を変更する : CMD

CMD命令は、イメージをコンテナとして実行したときのデフォルトの実行命令を指定する命令です。

RUN命令との違いはRUN命令はイメージ生成時に動作するのに対し、CMD命令はイメージ生成時には動作しません。
この影響でCMD命令はイメージのレイヤーを増加させません。

CMD命令は、1つしか有効にできません。
複数記述してある場合は、最後に記述されているCMD命令が有効になります。

また、CMD命令は少し記述方法が特殊になります。

CMD ["命令","引数1","引数2",...]

このように、[]で囲みつつ、命令と引数をすべてダブルクォーテーションで囲んで指定します。
配列のような指定方法と思っていただければいいかと思います

引数には、ls命令であれば-lオプションや-aオプションなどになります。
もちろん、gcc命令のようにmain.cのようなファイルの指定もできます。

例 : ubuntuイメージのデフォルト実行命令をイメージ生成時にコンパイルしたバイナリを実行する

当然バイナリは、実行するOSごとにコンパイルする必要があるので、ホストOSでコンパイルしてイメージ内にCOPYするという方法は使えません。

ホストOSでソースコードを用意して、それをイメージ内にコピー(COPY)してgcc(RUN)して、デフォルト実行するコマンドをa.outにしてバイナリを実行させてみようと思います

まずは全体のフォルダ構成です。
カレントディレクトリの矢印が付いているフォルダにいる状態でdockerコマンドを実行していきます

DockerTest/   <- カレントディレクトリ
    - main.c
    - Dockerfile

main.cファイルを以下のような記述で作成しました。

#include <stdio.h>

int main(){
  printf("Hello CMD");

  return 0;
}

dockerfileのFORM命令で持ってくるベースイメージはgccを指定します。

FROM gcc

# コピー
COPY main.c main.c

# コンパイル実行
RUN gcc main.c

# コンパイルの結果生成されたa.outをデフォルト実行に設定
CMD ["./a.out"]

まずは、イメージをビルドします。
生成するイメージは`gcc-testとしました。

docker image build -t gcc-test .

実行すると以下のようなログが流れて正常にイメージを生成できるかと思います。

[+] Building 33.9s (8/8) FINISHED                                                       docker:desktop-linux
 => [internal] load build definition from Dockerfile                                                    0.0s
 => => transferring dockerfile: 225B                                                                    0.0s
 => [internal] load metadata for docker.io/library/gcc:latest                                           2.2s
 => [internal] load .dockerignore                                                                       0.0s
 => => transferring context: 2B                                                                         0.0s
 => [1/3] FROM docker.io/library/gcc:latest@sha256:de5eab50ec2a4f64aa63b13cf14406a9266fce4016a9401c40  29.7s
 => => resolve docker.io/library/gcc:latest@sha256:de5eab50ec2a4f64aa63b13cf14406a9266fce4016a9401c403  0.0s
 => => sha256:a8b1354530263850950f06cdaf2fba4d2ee014b2c2ac6bc5a2a8097d89dd77f5 1.80kB / 1.80kB          0.2s
 => => sha256:c02a85f4822d38153c9749e5481a33ec72b748938c199687c674b0772cb7f12d 9.63kB / 9.63kB          0.4s
 => => sha256:5a943f17ed11816067c3d8776686b9c3744c060e4e28641d98f2e063ec313a0d 157.73MB / 157.73MB     19.6s
 => => sha256:a74f2e01d9631f365fbeb32a320522f9ad65c8e3457c5cb73e5fb93857b39561 2.81MB / 2.81MB          1.0s
 => => sha256:1c8ff076d818ad6b8557e03e10c83657cc716ab287c8380054ff91571c8cae81 211.27MB / 211.27MB     22.1s
 => => sha256:2e66a70da0bec13fb3d492fcdef60fd8a5ef0a1a65c4e8a4909e26742852f0f2 64.15MB / 64.15MB       10.6s
 => => sha256:2e6afa3f266c11e8960349e7866203a9df478a50362bb5488c45fe39d99b2707 24.05MB / 24.05MB        4.3s
 => => sha256:8cd46d290033f265db57fd808ac81c444ec5a5b3f189c3d6d85043b647336913 49.56MB / 49.56MB        7.6s
 => => extracting sha256:8cd46d290033f265db57fd808ac81c444ec5a5b3f189c3d6d85043b647336913               3.0s
 => => extracting sha256:2e6afa3f266c11e8960349e7866203a9df478a50362bb5488c45fe39d99b2707               1.0s
 => => extracting sha256:2e66a70da0bec13fb3d492fcdef60fd8a5ef0a1a65c4e8a4909e26742852f0f2               3.5s
 => => extracting sha256:1c8ff076d818ad6b8557e03e10c83657cc716ab287c8380054ff91571c8cae81               4.4s
 => => extracting sha256:a74f2e01d9631f365fbeb32a320522f9ad65c8e3457c5cb73e5fb93857b39561               0.1s
 => => extracting sha256:5a943f17ed11816067c3d8776686b9c3744c060e4e28641d98f2e063ec313a0d               2.8s
 => => extracting sha256:c02a85f4822d38153c9749e5481a33ec72b748938c199687c674b0772cb7f12d               0.0s
 => => extracting sha256:a8b1354530263850950f06cdaf2fba4d2ee014b2c2ac6bc5a2a8097d89dd77f5               0.0s
 => [internal] load build context                                                                       0.0s
 => => transferring context: 108B                                                                       0.0s 
 => [2/3] COPY main.c main.c                                                                            1.2s 
 => [3/3] RUN gcc main.c                                                                                0.4s
 => exporting to image                                                                                  0.2s
 => => exporting layers                                                                                 0.1s 
 => => exporting manifest sha256:f06f3edbbe22191eeb02aa28d791f24530d1943e0bd02d68853fb579b73e88f4       0.0s 
 => => exporting config sha256:b4db4e2d264346fb4450021197c5a14d2dfbb4f0581306d5fef003864517033e         0.0s 
 => => exporting attestation manifest sha256:7024b7972794fd855cb61631602709469fde1809b6670f1b6019beb2c  0.0s 
 => => exporting manifest list sha256:b4e95797eff29aab3767a1b50e17947033571f0c585237547de88c6781c4749f  0.0s 
 => => naming to docker.io/library/gcc-test:latest                                                      0.0s 
 => => unpacking to docker.io/library/gcc-test:latest                  

その後、コンテナを実行して「デフォルトでHello CMD」が実行されるかを確認します
このコンテナでは、特に入力もせずに出力を1行見たいだけなので-itのようなオプションはつけず、--rmで実行後自動で削除されるように設定します

docker container run --rm gcc-test

実行して、以下のような表示がされれば無事にCMDの設定ができていたということになります

Hello CMD

dockerfile内のカレントディレクトリと実行時のカレントディレクトリを変更する: WORKDIR

WORKDIR命令は、イメージビルド時の処理のカレントディレクトリの変更と、コンテナ生成時のデフォルトディレクトリの設定を行います。

WORKDIR命令を使っていない場合は、デフォルトで/がカレントディレクトリになっているはずです。(Ubuntuは)

イメージビルド時のカレントディレクトリの変更ができるので、「特定のディレクトリに移動してRUNでコマンドを実行したい」や「コンテナ起動時のディレクトリをすぐに作業を始められるディレクトリにしたい」などの設定ができるようになっています

例 : 階層の移動が分かりやすいようにtouch命令を実行して痕跡をたどる

今回はシンプルに、Dockerfileだけの以下のフォルダ構成で動作の確認をしていきます。

DockerTest\
    - Dockerfile

Dcokerfileの内部では、WORKDIRを3回、touchを3回行いディレクトリの移動の形跡を残すことで動作の履歴を見ていきます。

FROM ubuntu:20.04

RUN touch 1.txt

WORKDIR /myApp

RUN touch 2.txt

WORKDIR ../

RUN touch 3.txt

WORKDIR /testDir

想定では以下のようにファイルとディレクトリが生成されると思っています。

\
    - 1.txt
    - ...
    - myApp\
        - 2.txt
    - 3.txt
        - testDir <- コンテナ起動時のカレントディレクトリ

では、まずは、イメージをビルドしていきます。

docker image build -t workdir-test .

特にエラーが出なければ、生成したworkdir-testイメージからコンテナを生成して内部のBashを使って想定通りの構造になっているか確認します。

docker container run -it --rm workdir-test

以下が確認したときのログになります。
正常に想定通りのファイル構造とデフォルトディレクトリになっているのが確認できると思います。

root@05c0f42fa214:/testDir# pwd
/testDir
root@05c0f42fa214:/testDir# ls ../
1.txt  bin   dev  home  lib32  libx32  mnt    opt   root  sbin  sys      tmp  var
3.txt  boot  etc  lib   lib64  media   myApp  proc  run   srv   testDir  usr
root@05c0f42fa214:/testDir# ls ../myApp/
2.txt

さいごに

地味に多用するコマンドの紹介でした。

頭の中で想定しているとどうしてもこんがらがってしまうので、Dockerの強みの小回りが利くことを利用して実際に動かしてみて想定通りの動作になっているかをチェックしながらDockerfileを構築していくのがいいかと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA