はじめに
こんにちは,Docker使用歴一週間のものです.最近Dockerについてばかり調べています.
Dockerは,コンテナ型の軽量な仮想環境を構築するソフトウェアです.
個人的なポイントは,
- 綺麗な環境がすぐに構築できる.
- 環境が汚くなれば,捨てれば良い.
- 環境の保存,再現が容易.
この辺りでしょうか.
詳しい基本的な説明は別の方にお任せして,この記事では,Dockerfileを書いているうちに気になったことを備忘録がてらにまとめていきます.(詳しい方には当たり前のことかと思うかもしれませんが,ご容赦ください...)
今回は,Ubuntuのイメージを利用して,Dockerfileから新しいイメージを作ることを想定しています.
環境
$ sw_vers
ProductName: Mac OS X
ProductVersion: 10.14.4
BuildVersion: 18E226
$ docker -v
Docker version 18.09.2, build 6247962
sudo
は使えない
以下のようなDockerfileからイメージを作成するとします.
FROM ubuntu:latest
ENV USER hoge
# Add User and Change User
RUN useradd -m ${USER}
RUN gpasswd -a ${USER} sudo
RUN echo "${USER}:pass" | chpasswd
USER ${USER}
RUN sudo apt-get update
当たり前かもしれませんが,buildする際,一番最後のRUN
でエラーが起きます.
というのも,一般ユーザーからsudoする時,パスワードの入力が求められますが,build中はパスワードの入力は不可能であるからです.
どうしてもsudo
の必要なコマンドを実行する際は,ユーザーを変更する前にrootで行うか,最後にシェルスクリプトを実行するようにCMD ["./startup.sh"]
を付け足すなどが考えられます.(ただし,シェルスクリプトは,イメージではなく,コンテナ作成時に実行されます.)
コンテナのログインシェルの変更(SHELL
などについて)
まず,公式のUbuntuのイメージのログインシェルを確認してみます.
$ docker run --rm -it ubuntu
root@e406d43e91vr:/# echo $SHELL
/bin/bash
root@e406d43e91vr:/# grep $(whoami) /etc/passwd
root:x:0:0:root:/root:/bin/bash
ログインシェルはbashのようです.では,ここからzshに変更したいと思います.
SHELL
命令を使ってみる
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends zsh \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
SHELL ["/bin/zsh", "-c"]
DockerにはSHELL
命令があるので,これを使えば変更できるでしょう.
と思っていました...
では,上記のDockerfileからイメージを作成します.
$ docker build -t test .
...
$ docker run --rm -it test
root@406d41e91vbe:/# echo $SHELL
/bin/bash
root@406d41e91vbe:/# grep $(whoami) /etc/passwd
root:x:0:0:root:/root:/bin/bash
...変わっていません...
どうやらSHELL
コマンドはビルド時のシェルを変更するもので,コンテナ内のログインシェルを変更するものではないっぽい?
なので,普通のコマンドで変更します.
chsh
で変更してみる
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends zsh \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# SHELL ["/bin/zsh", "-c"]
RUN chsh -s /bin/zsh
$ docker build -t test2 .
...
$ docker run --rm -it test2
root@06ve40d193e9:/# echo $SHELL
/bin/zsh
root@06ve40d193e9:/# grep $(whoami) /etc/passwd
root:x:0:0:root:/root:/bin/zsh
変更できたようですが,プロンプトの形式が先ほどと変わっていません.
色々調べてみると,$SHELL
はログインシェルらしく,現在のシェルは$0
だそうです.今まで現在のシェルだと勘違いしてました...
そこで,$0
を見てみます.
root@06ve40d193e9:/# echo $0
/bin/bash
変更されてない!!!
ログインシェルとはその名の通り,ログインした際に起動されるシェルということだと思うのですが,コンテナを作成するということは,ログインするということではないということでしょうか.この点は,階層の話にも繋がってきそうですが,まだまだ勉強不足です.
とりあえず,この節の結論は,「chsh
でも”コンテナ起動時のシェル”は変更できない」です.
しかし,ログインシェルの変更はできているので,ビルド時にログインし直すような処理があれば,できそうな気がしなくもないのですが,どうすればよいかはまだわかりません...
CMD
もしくはENTRYPOINT
を使ってみる
GitHubなんかでDockerfileと検索すると,Dockerfileの最後でCMD
やENTRYPOINT
でシェルを指定しているのをよく見かけます.その方法を用います.(CMD
とENTRYPOINT
についてまだ詳しくないので使いたくなかったのですが...)
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends zsh \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
CMD ["/bin/zsh"]
$ docker build -t test3 .
...
$ docker run --rm -it test3
e40d10e406ve# echo $SHELL
e40d10e406ve# echo $0
/bin/zsh
e40d10e406ve# grep $(whoami) /etc/passwd
root:x:0:0:root:/root:/bin/bash
e40d10e406ve# exit
シェルが変更されました!確かに$0
がzshになっていますし,プロンプトも先程と異なります.
でも,ログインシェルの変更はうまくいってませんね.この点は,先程のchsh
で変更することができます.(ログインシェルを使わないので,設定する意味は定かではありませんが.)
ちなみにDockerfileのCMD
の部分をENTRYPOINT
に変更しても同様に動きます.
ただし,両者で役割が異なりますし,厳密に扱いたい場合は詳しく調べることをお勧めします.
hadolintを使っているとapt-get
で何か言われるが...
hadolintは,Haskellで実装された静的解析ツールです.
hadolintのルールに従って,エラーとなる書き方や非推奨な書き方をビルド前に教えてくれます.
$ brew install hadolint
$ hadolint Dockerfile
さて,このhadolintのルールの中にこういうものがあります.
DL3009: Delete the apt-get lists after installing something.
apt-get
で何かインストールした後はリストを消せという感じの意味でしょうか.
下記のDockerfileをhadolintにかけます.
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y --no-install-recommends zsh
$ hadolint Dockerfile
Dockerfile:1 DL3007 Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag
Dockerfile:3 DL3009 Delete the apt-get lists after installing something
Dockerfile:4 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
Dockerfileの3行目で先程述べたルールが出てきていますね.では,これをWikiに則って真面目に直します.(他のエラーは今回は無視します.無視するオプションもありますが,今回は付けていません.)
FROM ubuntu:latest
RUN apt-get update \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get install -y --no-install-recommends zsh
$ hadolint Dockerfile
Dockerfile:1 DL3007 Using latest is prone to errors if the image will ever update. Pin the version explicitly to a release tag
Dockerfile:6 DL3008 Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
DL3009
は消えました.では,ビルドしてみましょう.
$ docker build -t test4 .
...
Step 3/3 : RUN apt-get install -y --no-install-recommends zsh
---> Running in 006ve4db0ae7
Reading package lists...
Building dependency tree...
Reading state information...
E: Unable to locate package zsh
The command '/bin/sh -c apt-get install -y --no-install-recommends zsh' returned a non-zero code: 100
エラーが生じてしまいました.最後で/var/lib/apt/lists/*
を消してしまったのが悪かったのでしょうか.
また,このDL3009
については,似たような問題かはわかりませんが,issueもありました.
これを解決する方法としては,apt-get
を1ラインで済ますなどを挙げています.
FROM ubuntu:latest
RUN apt-get update && apt-get install -y --no-install-recommends zsh \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
もしくは,エラーは無視して最後にrm
するようにするか,イメージのサイズが気にならなければ,無視することも挙げています.(理解ミスで間違っていたらご指摘お願いします...)
終わりに
今考えると,そりゃそうだと思う内容ばっかりなのですが,自分が通ってきた道ということで記事にしました.そりゃそうだと思えることが,成長した証というものでしょうか.今後も何かあれば,追記します.
また,何か気になる点や直すべき点があれば,コメントお願いします.
参考サイト
- Enterprise Application Container Platform | Docker
- Docker Documentation
- Dockerfileでビルド時にbashを使う - Qiita
- 現在使っているシェルの名前を知る方法 - 揮発性のメモ2
- hadolint/hadolint: Dockerfile linter, validate inline bash, written in Haskell
- DockerfileをLintする - Qiita
- DL3009 Warning when clean done as a later statement? · Issue #311 · hadolint/hadolint