2017年8月2日水曜日

オライリーのDocker本を読んだり色々な記事を読んだりしたので自分なりにまとめる(Dockerfileについて)

Docker経験値0だった私も何冊か本を読んで実際にDockerfileを見たりいじったりしてみてECSに試行錯誤でデプロイしてみたりして、ようやく少しずつ慣れてきた感が出てきました。
しかしまだまだ「バッチリできます!」というには力不足間は否めなく…いやそういう話じゃない。
折角本を読んだりネット上の先輩たちの記事を読んだりして色々覚えたのだから、それをまとめておきたいと思います。
実はもうすでに日本語での公式なDockerfileベストプラクティスが存在するので、意味がないといえばたぶん意味はないのですが、アウトプットをして学習にいったんの区切りをつけるという意味で自己満足も兼ねて書きたいと思います。

Dockerfileについての基本的なこと

  • 1コンテナ1プロセスを起動するつもりでDockerfileを書く
    • 1プロセスとはつまりnginxとmod-phpによるPHPアプリケーションなら2つのコンテナを用意するということ
  • コマンドは大文字で書く( RUN, COPY, EXPOSE 等)
  • #によるコメントアウトが使える
  • FROM 文は先頭に1行だけ書く
  • CMD 文は末尾に1行だけ書く

Dockerfile についてやるべきこと・意識すべきこと

  • DockerHub のイメージは「公式」とわかるもの以外むやみやたらに使わずできるだけ Dockerfile を読んでから利用を判断する
    DockerHub は Docker イメージの共有サイトだが、有象無象のコンテナイメージが登録されており、(その意図はなくても)セキュリティ的に危険なコンテナが存在する可能性は十分にある。 DockerHubが公式に提供しているNginx など、ソフトウェアベンダ(団体)の公式イメージ以外は基本的に利用しないことで基本的なセキュリティある程度確保することができる。
  • 構文にはshell形式とexec形式が利用できるものがあるが基本的にexec形式を利用する
    例えばCMDコマンドはshell形式とexec形式を利用することができる。
    shell形式は指定された文字列を /bin/sh -c に渡すことでコマンドを実行する。
    その場合に、exec形式だと/bin/shに対する攻撃のような文字列の実行を回避することができる。
  • FROM 文で引用するイメージのタグに latest は利用しない
    Docker はタグと呼ばれるバージョン情報で Docker イメージのバージョンを管理、選別することができる。
    latest は文字のごとく最新のバージョンのイメージに使われるタグのように見せかけて、実は Docker の予約語であり、特別にタグを指定していない時のデフォルト値として勝手に付与される。つまりそれは latest =! 最新という可能性を否定できず、意図しない動作を引き起こす原因になるかもしれない。
  • 作成したイメージには一意なタグを設定し latest は利用しない
    上述の「FROM 文で引用するイメージのタグに latest は利用しない」と同じ理由。
    もしも「これは最新のイメージです」という意味の任意のタグが必要であれば、「newest」といったタグを自分で設定するのがよい。
  • Docker はイメージのミルフィーユであることを意識する
    Docker は自身の軽量化のため、イメージ生成の各段階でスナップショットとしての中間イメージを作成する。
    そしてそれらの中間イメージは他の Docker イメージを作る際などでも流用できるものはキャッシュとして流用される。
    そして、その中間イメージは Dockerfile の1行ごとに作成される。
    なので Dockerfile の行数を減らすことによりイメージの軽量化に貢献し、開発プロセスをより高速に回すことができる。
  • MAINTAINER 文はオライリーの Docker 本でも必須などとは書かれていなかったけれどもできる限り書いた方がよい
    個人のプロジェクトで DockerHub にイメージを登録する場合は改善要望などをもらえるかもしれない。
    仕事で Dockerfile を書く場合にたとえプライベートリポジトリを用意されていたとしても、プロジェクトの共通メールアドレスなどを記載しておくと出生が分かって他のプロジェクトからの利用しやすくなる可能性など考えられる。
  • CMD 文は上書きされる 最後の1つのみが適用される
    また、ENTRYPOINT 文を書いたときは、そちらが最優先となって適用される。
    ただし、その場合CMD文を ENTRYPOINT 文の引数部分として利用することができる。
    CMD文と同等のコマンドは、docker run 実行時のオプションとしても与えることができる。
    つまりそのオプションで ENTRYPOINT 文の引数を与えることができる。
  • 明示的に圧縮ファイルの展開が必要な場合以外は基本的に COPY 文を使う
    ADD と COPY はどちらもホスト(開発端末)上のファイルをコンテナに転送するが、 ADD は圧縮ファイルの展開なども行う。
    ただのファイル転送であれば COPY 文を利用することで不要な誤解や不慮の不具合を防ぐことができる。
  • yum や apt-get で複数のパッケージをインストールする際は1行のRUN文内でできるかぎりすべてのパッケージをインストールする
    RUN 文を記述するたびに Docker は中間イメージを生成する。
    そのため、パッケージマネージャで各種プログラムをインストールする際は、必要なものをできる限り1回でインストールすることが Dockerfile としては望ましい。
  • yum や apt-get で複数のパッケージをインストールする際はパッケージ名ごとに改行し、かつ、パッケージ名をアルファベット順に記載する
    上述の中間イメージ節約を考慮しつつ、可読性を向上させるためのTipsとして推奨されている。
  • RUN 文で cd は使わずに WORKDIR 文 を利用する
    WORKDIR 文はコンテナ内での作業ディレクトリを定義する。
    用意されている WORKDIR 文を利用することで可読性が向上し、メンテナンスがしやすくなる。 また、同様の理由でパスには絶対パスを利用する。
  • RUN 文 と USER 文を利用してコンテナのプロセス実行ユーザを定義した方がよい Dockerは何もな考えずにイメージを起動するとrootユーザで実行されることが多い。
    VMと違って、ホストマシン(開発端末・本番環境のDockerサーバ)のrootと同じユーザとして振舞う。
  • root権限奪取の危険性を減らすためにコンテナ内に非rootなユーザを作成してそいつにプロセスを起動させる
    日本語のエントリでこれについて言及している人は何故か少ないが、オライリーの Docker 本には明確に書かれてあるし、 How secure are Docker containers? (Ben Hall) - Full Stack Fest 2016 などでも言及されている。
    利用しているイメージのDockerfileが非rootユーザを作成していない場合はできる限り非rootユーザを作成した方がよいのでは。
    そういう意味でも利用するイメージのDockerfileには一度目を通しておいた方がよいのではと考えられる。