はじめに
こんにちは!ITスクールRareTECHにてCS(Customer Support)を担当している池村です。
今回の記事は、前回書いた自作のDockerfile問題(2~10)の解答記事になります。
まずは以下にある問題を、git cloneして解いてみてください。
解き方の解説は👇の記事をご確認ください。
問題1については上記の記事で解説をしているので、そちらをご確認ください。
問題2の解答
さて、早速問題2を見ていきましょう。
問題1ではAlpine Linuxを使っていましたが、今回はUbuntuを使う問題です。
なので、FROM句を変更すれば良さそうですね。
ではディレクトリに移動して、Dockerfileを編集しましょう。
今回はバージョンの指定があります。22.04ですね。
最新版はlatestにすれば良いですが、細かなバージョンを指定したい場合はタグにバージョンを書き込む必要があります。
FROM ubuntu:22.04
CMD ["echo", "Hello Docker!!"]
上記が解答になります。
書き込みが終わったら、ビルドして実行、正誤チェックしてみましょう。
docker build -t q02 .
docker run --rm q02
bash check.sh
正解です!が出たら完了です。次の問題にいきましょう。
問題3の解答
次は問題3ですね。CMD句のちょっと違う使い方です。
まずCMDにはexec形式とshell形式があります。
exec形式とは
シェルを使わず、コマンドの実行ができる書き方。
ただ、パイプ、環境変数、リダイレクトなどが使えません。
CMD ["echo", "Hello"]
これはDockerがjson形式で解釈してくれています。
1つ目がコマンド、それ以降が引数として扱われます。
DockerのReferenceでは以下のように書かれています。
The exec form is a JSON array.
The shell is not invoked.
- exec形式は JSON 配列でなければならない
- シェルは起動しない
これについて詳しくお話しすると、1記事分に相当するので、ここでは詳しくお話しする気がありません。PID1の存在が関係してきますが、別記事で詳しく解説する予定です。
基本的に推奨されている書き方はexec形式になります。
shell形式とは
shell形式は、シェルを介してコマンドを実行できる書き方です。
json形式ではないので、直接コマンドを書くことが可能です。
CMD echo "Hello"
こうすると、Docker内部的には/bin/sh -c "echo Hello"と解釈します。
※Alpineの場合
環境変数や、パイプ、リダイレクトも使用可能です。
ENV NAME=World
CMD echo "Hello $NAME"
※exec形式だと変数の中身は展開されません。
CMD echo "Hello World" | grep World
のような書き方ができる。上記に意味はないですが。
CMD echo "Hello" > /tmp/hello.txt
CMD echo "start" && echo "next"
これらのシェルに備わった機能が様々使えます。
初心者のレベルでは、まず二つの書き方があるんだな。といった理解で十分です。
システムコール周りはややこしいですからね。
さて、ここまできたら解答を見ていきましょう。
FROM alpine:latest
CMD echo "Hello Docker!!"
これだけです。
これを書いてファイルを保存したら、ビルド・実行・正誤チェックをしましょう。
docker build -t q03 .
docker run --rm q03
bash check.sh
正解です!と出たらOKです。
問題4の解答
では次に問題4です。
ここからRUN句が出てきますね。
核となるのはmessage.txtファイルを作成して、中に文字列Hello Docker!!を書き込む必要があるというところです。
まずRUN句ですが、これはビルド時に行われるコマンドを書くところです。
ファイルを作るわけですが、今回はコンテナ内のどこに作るのかの指定がないので、適当に作ってしまいますが、以下のように書いてみましょう。
RUN echo "Hello Docker!!" > /message.txt
これはshell形式で書いていますが、基本的に複雑なコマンドを書く傾向にあるため、基本的にshell形式で書くことになります。
一応exec形式でも書けるのですが
RUN ["/bin/sh", "-c", "echo 'Hello Docker!!' > /message.txt"]
面倒ですよね。
結局リダイレクトとか、パイプとか、連結等の複雑な処理を書くとすごい量になってしまいます。
まず、RUNとCMDでは根本的に違うところがあります。
イメージをビルドする時に実行されるか、コンテナが起動時に実行されるかになります。
RUNはイメージを作る際の作業なので、shell形式の方が便利。CMDは本番のコンテナでのプロセスなので、exec形式でより安全に。
安全な理由は、シグナルの無視やゾンビプロセスが関連してきますが、ここでは詳しく解説しません。問題3の時と同様です。
解答はこちらになります。
FROM alpine:latest
RUN echo "Hello Docker!!" > /message.txt
CMD ["cat", "/message.txt"]
- RUN句で
messeage.txtを/に作成します - CMDで、そのファイルを
catコマンドで読み込んでいます
これで問題4は完了です。
ビルド・実行・正誤チェックしましょう。
docker build -t q04 .
docker run --rm q04
bash check.sh
正解です!と出たら、次の問題にいきましょう。
問題5の解答
では問題5に入ります。
今回はWORKDIR句を使っていきます。
WORKDIR句は、コンテナの中でどのフォルダを基本の作業場所にするかを指定する命令になります。
前回までの問題で行われてきた、RUN句・CMD句のコマンドが実行される際のカレントディレクトリを設定することができます。
RUN echo "Hello" > /app/message.txt
CMD ["cat", "/app/message.txt"]
と言った書き方になり、少々面倒ですよね。
これをしっかり/app以下に作りたいと決まっている際に使っていきます。
なので解答は
FROM alpine:latest
WORKDIR /app
RUN echo "Hello from /app" > message.txt
CMD ["cat", "message.txt"]
このWORKDIR句で設定したディレクトリのおかげで、その後の作業が/app以下で行われることが確定しています。
これで問題5は終了です。
ビルド・実行・正誤チェックしましょう。
docker build -t q05 .
docker run --rm q05
bash check.sh
正解です!と出たら、次の問題にいきましょう。
問題6の解答
続いて問題6です。
今回は環境変数を利用する問題です。
コンテナの中で自由に使えるようにする変数のことです。
ただ、環境変数を設定した場合、CMDでは2通りの書き方があります。
では解答1です。
FROM alpine:latest
ENV GREETING="Hello from ENV"
CMD ["/bin/sh", "-c", "echo $GREETING"]
CMDのexec形式では、シェルを指定してあげることで、環境変数を扱うことができます。
シェルを使います!と宣言した上で、変数を展開する形です。
解答2です。
FROM alpine:latest
ENV GREETING="Hello from ENV"
CMD echo $GREETING
shell形式だと、そのまま変数を使えますので、execに比べると短く書くことができます。
※CMDではexec形式が推奨されています。
ビルド・実行・正誤チェックしましょう。
docker build -t q06 .
docker run --rm q06
bash check.sh
正解です!と出たら、次の問題にいきましょう。
問題7の解答
問題7に入りましょう。
ここではDockerfileでよく使われるCOPY句を使います。
問題を読むと、message.txtを作成する必要があるようなので、問題が格納されているディレクトリの中で作ってしまいましょう。
echo "Hello from COPY" > message.txt
これでファイルはOKなので、次にDockerfileを作成します。
FROM alpine:latest
COPY message.txt /message.txt
CMD ["cat", "/message.txt"]
これでOKです。
COPY句の場合、左が現在いるローカルのファイル、右がコンテナの中のCOPY先になります。ビルドは.を指定するので、今いるディレクトリにあるファイルを見ます。ローカルの方はそのままmessage.txtでOK。
右は、コンテナの中の話なので、/ディレクトリにそのまま作成しています。
ビルド・実行・正誤チェックしましょう。
docker build -t q07 .
docker run --rm q07
bash check.sh
正解です!と出たら、次の問題にいきましょう。
問題8の解答
この問題ではRUN句を二つ使ってみます。
指定の場所にmessage.txtを作成して、その後のRUNで場所を変えます。
移動はmvコマンドを使いましょう。
FROM alpine:latest
RUN echo "Hello from multi RUN" > /tmp/message.txt
RUN mv /tmp/message.txt /message.txt
CMD ["cat", "/message.txt"]
これでOKです。
ビルド・実行・正誤チェックしましょう。
docker build -t q08 .
docker run --rm q08
bash check.sh
正解です!と出たら、次の問題にいきましょう。
このRUN句のコマンドに大した意味はありません。
問題9の解答
ここが一番難しいかもしれません。
シェルスクリプトを学習していない人も多いと思いますが、その内容自体は決して難しくありませんので頑張ってほしいです。
ではまず2パターンありますので、1つ目から。
FROM alpine:latest
RUN echo '#!/bin/sh' > /hello.sh \
&& echo 'echo "Hello from script"' >> /hello.sh \
&& chmod +x /hello.sh
CMD ["/hello.sh"]
解答1では、RUN句で、シェルスクリプトの内容を1行ずつ&&を使って連結させながら入力していっています。
そして最後にchmodコマンドで実行権限をつけています。
FROM alpine:latest
COPY hello.sh /hello.sh
RUN chmod +x /hello.sh
CMD ["/hello.sh"]
解答2ではローカルでhello.shファイルを作成して。COPYしている形です。
これはこれで、chmodを行う必要があります。
ローカルのhello.shには以下の内容を書いておきます。
#!/bin/sh
echo "Hello from script"
ビルド・実行・正誤チェックしましょう。
docker build -t q09 .
docker run --rm q09
bash check.sh
正解です!と出たら、次の問題にいきましょう。
問題10の解答
この問題はENTRYPOINT句を使います。
ENTRYPOINT句は、コンテナ起動時に行うべきコマンドを固定することができます。
おそらく何を言っているかわからない人もいると思うので解説すると、CMD句に書かれたコマンドは、コンテナを実行する人によって書き換え可能なんです。なので以下のような場合を想定して説明します。
CMD ["echo", "Hello"]
上記のような内容が最後に書かれたDockerfileをビルドして、コンテナを起動したとします。その際に以下のようなコンテナ起動のコマンドを使うと。。
docker run イメージ名 echo Docker
# 実行結果
Docker
このdockerコマンドの実行の際に渡される引数は、CMDを上書きしちゃうんですね。
ENTRYPOINTはユーザーに変えてほしくないコマンドを設定できます。
なので、以下のように書けば正解です。
FROM alpine:latest
ENTRYPOINT ["echo"]
CMD ["Hello from ENTRYPOINT"]
今回は指定として、ENTRYPOINTにechoを書きましょうとあります。
最終的な出力結果はHello from ENTRYPOINTなので、CMDに書き込みます。これで、ENTRYPOINTへの引数としてCMDの値が渡される形になります。
使い分けは以下です。
-
ENTRYPOINT
- 変えられたくないコマンド本体
- 必ず実行してほしいアプリケーション
- コンテナのメインプロセス
-
CMD
- ENTRYPOINT のためのデフォルト引数
- あってもなくても良い
- ユーザーが run で変更可能
こちらもCMD同様、exec形式で書くことが推奨されています。
ビルド・実行・正誤チェックしましょう。
docker build -t q10 .
docker run --rm q10
bash check.sh
CLEARと出たら、全問終了です。お疲れ様でした。
おわりに
今回は前回の記事の解答・解説を行いました。
まずは書いて覚える。これが大事です。初歩の初歩から始めることは、遠回りのようで近道です。この後は11~20問目の作成と記事を書きますので、今しばらくお待ちください。










