12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Docker] Dockerfileを書く際の知見まとめ

Posted at

はじめに

こんにちは,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からイメージを作成するとします.

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命令を使ってみる

Dockerfile
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で変更してみる

Dockerfile
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の最後でCMDENTRYPOINTでシェルを指定しているのをよく見かけます.その方法を用います.(CMDENTRYPOINTについてまだ詳しくないので使いたくなかったのですが...)

Dockerfile
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にかけます.

Dockerfile
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に則って真面目に直します.(他のエラーは今回は無視します.無視するオプションもありますが,今回は付けていません.)

Dockerfile
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ラインで済ますなどを挙げています.

Dockerfile
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するようにするか,イメージのサイズが気にならなければ,無視することも挙げています.(理解ミスで間違っていたらご指摘お願いします...)

終わりに

今考えると,そりゃそうだと思う内容ばっかりなのですが,自分が通ってきた道ということで記事にしました.そりゃそうだと思えることが,成長した証というものでしょうか.今後も何かあれば,追記します.
また,何か気になる点や直すべき点があれば,コメントお願いします.

参考サイト

12
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?