Translate

2015年3月12日木曜日

Dockerfile リファレンスを翻訳してみた

Dockerをはじめた人は自分でイメージを作りたくなると思います。
ユーザガイドによれば、作成には2つの選択肢、シェルをrunしてインストール手順を手動で実行して最後commitするか、Dockerfileでドカっとつくるかのどちらかになります。

で近年偉い人がおっしゃる継続的デプロイっていうやつを実現するには、Dockerfileの書き方を勉強しなくてはなりません。

でも..日本語情報が少ない..

..ので本家サイトにある以下のページを翻訳してみました。

 Docker: Dockerfile Reference
 https://docs.docker.com/reference/builder/


翻訳してわかったことは幾つもありました。

  • キャッシュの動きを理解しないとyum update -y/apt-get dist-upgrade -yが..
  • ENV指定とOS側の環境変数が混同しやすい
  • どうもビルドコンテキストというのはADDやCOPYする元ディレクトリ/ファイル群
  • ビルドコンテキストの指定でのWORkdirの取り扱いに注意
  • VOLUMEでホスト側ファイルシステムをマウントする
  • ビルド時、コンテナとして実行時に実行するコマンドの使い分けを理解してないとやばい
  • shellフォームとexecフォームは書式の違いだけでないこと
  • ADDやCOPY時のUID/GIDのパーミッションがどうなるか
  • CMD/ENTRYPOINTで実行されるプロセスのPIDがどうなるか
  • そしてCMD/ENTRYPOINTにスクリプトをかませるとき、Unixのシグナルをプロセスに認識させるためexecやgosuを使わないとダメ
  • ベースイメージを作成するDockerfileをつくるにはONBUILDも駆使しないといけないかも





っていうか、VMwareやXen、kvmなんかで作成する仮想マシンのまるまる代替に
Dockerコンテナがそのままなるわけないことを改めて理解させられた..


以下、翻訳した日本語です。
長文だったので、結構疲れている時間の翻訳が超微妙です。
誤りなどがあればコメントにて指摘してください。

あと、参考にする場合は At your own risk でおねがいします。

なお、Dockerユーザガイドの勝手翻訳も有ります。

-----

Dockerfile リファレンス


DockerはDockerfileからインストラクションを読み込んでイメージ度自動ビルドすることができます。Dockerfileはテキストファイルで、Dockerイメージを通常の手動ビルドするすべてのコマンドが含まれています。ターミナルから docker buildコマンドを呼び出すことで、連続でインストラクションを実行し段階的にイメージをビルドすることができます。

このページではDockerfileに記述可能なすべてのインストラクションについて説明します。
明確に、読みやすい、メンテナンスしやすいDockerfileを書くための更なるヘルプのために、Dockerfileベストプラクティスガイドも執筆しました。最後に、Dockerfileチュートリアルを使って、あなたのDockerfileに関する知識をテストすることができます。

訳者注:
上記リンクの「Dockerfileチュートリアル」は
本当にテスト問題が出題されます。


使い方


ソースリポジトリからイメージをビルドするために、あなたのリポジトリのルートにDockerfileと呼ばれる記述ファイルを作成します。このファイルにはイメージを組み立てるための手順を記述します。

そして、docker buildコマンドの引数としてソースリポジトリへのパスを指定して呼び出します(例: .):

 $ sudo docker build .

ソースリポジトリへのパスはビルドのコンテキストがどこにあるかを定義します。ビルドは、CLIではなく、Dockerデーモンにより実行されます。このためすべてのコンテキストはデーモンへ転送されるべきです。Docker CLI はコンテキストをデーモンへ送信される際に"Sending build context to Docker daemon(ビルドコンテキストをDockerデーモンへ送信中)"とレポートします。

 警告:
 ソースリポジトリとしてルートディレクトリ「/」の使用は避けてください。
 docker build コマンドは、ビルドコンテキストとしてのDockerfileを含む
 どんなディレクトリでも(すべてのサブディレクトリを含む)使用します。
 ビルドコンテキストはイメージをビルドする前にDockerデーモンへ送信します。
 そこで/をソースリポジトリとして使う場合、あなたのハードドライブのすべての
 コンテンツををデーモン(デーモンが実行中のマシン)へ送信します。
 それはおそらくあなたが望まないことでしょう。

一般的には、空のディレクトリ上にそれぞれのDockerfileを置くことがもっともよい方法です、そしてDockerfileをおいたディレクトリにビルドに必要なファイル群(ADD元のファイルなど)を配置するだけです。将来のビルドのスピード向上のために、同じディレクトリに .dockerignore ファイルを追加することでファイルやディレクトリを除くことが可能です。

ビルドが成功した場合新規イメージを保存するリポジトリやタグを指定することができます:

 $ sudo docker build -t shykes/myapp .

Dockerデーモンは一つづつステップを実行します、そして最後に新規イメージのIDを出力する前に、必要な場合は結果を新規イメージとしてコミットします。Dockerデーモンは送信したコンテキストを自動でクリーンナップします。

それぞれのインストラクションは独立して実行していることに注意してください、そしてこのことは作成された新規イメージで発生します-このため「RUN cd /tmp」は次のインストラクションに反映しません。

Dockerは可能な限り中間イメージを再利用します、これにより docker build コマンドはかなり高速化します(ビルド中に「Using cache」と表示されます-詳細はDockerfile ベストプラクティスガイドを参照のこと):

 $ sudo docker build -t SvenDowideit/ambassador .
 Uploading context 10.24 kB
 Uploading context
 Step 1 : FROM docker-ut
  ---> cbba202fe96b
 Step 2 : MAINTAINER SvenDowideit@home.org.au
  ---> Using cache
  ---> 51182097be13
 Step 3 : CMD env | grep _TCP= | sed 's/.*_PORT_\([0-9]*\)_TCP=tcp:\/\/\(.*\):\(.*\)/socat TCP4-LISTEN:\1,fork,reuseaddr TCP4:\2:\3 \&/'  | sh && top
  ---> Using cache
  ---> 1a5ffc17324d
 Successfully built 1a5ffc17324d

ビルド終了したら、リポジトリからレジストリへの push を調べる準備ができます。

フォーマット


Dockerfileのフォーマットは以下のとおりです:

 # Comment
 INSTRUCTION arguments

インストラクションには大文字小文字の区別はありませんが、より簡単に項目(arguments)と区別するために大文字での記述が慣例となっています。

DockerはDockerfileを上から順番に実行します。最初のインストラクションはベースイメージの指定のための"FROM"であるべきです。

Dockerは#で始まる行をコメントとして扱います。#は行内のどこでも項目(arguments)とみなされます。次のような文章も許可されます:

 # Comment
 RUN echo 'we are running some # of cool things'

イメージのビルドのためにDockerfileで使うことのできるインストラクション群を紹介します。

環境変数の置換



 注意:
 バージョン1.3より前では、Dockerfile環境変数は、
 後述のように置換される点では、
 同様に取り扱われていました。
 しかしインストラクションとしての環境変数の
 置換の正式な定義がありませんでした。
 1.3より後のバージョンでは、
 この振る舞いは維持されており、正式なものです。

(ENV文で定義された)環境変数は、Dockerfileにより解釈される変数として特定のインストラクションも使用できます。エスケープ故事もまた文章内の変数のような構文を含めるために取り扱われます。


環境変数は、Dockerfile内では$variable_name もしくは ${variable_name}のどちらか一方で記述されます。それらは同等に扱われます。そして中括弧構文は一般的に問題に対処するために、${foo}_barのように、空白無しの変数名で使用されます。


エスケープ処理は変数の前に\を追加することで可能です:例として、\$foo もしくは \${foo}$foo${foo} というリテラルに各々置き換えられます。

例(解釈後の環境変数置換は #の後に表示):

 FROM busybox
 ENV foo /bar
 WORKDIR ${foo}   # WORKDIR /bar
 ADD . $foo       # ADD . /bar
 COPY \$foo /quux # COPY $foo /quux

Dockerfile内で環境変数を取り扱うインストラクションは、次の通り:

  • ENV
  • ADD
  • COPY
  • WORKDIR
  • EXPOSE
  • VOLUME
  • USER

ONBUILDインストラクションは(上記のインストラクションでさえ)環境変数の置換をサポートしていません。

.dockerignore ファイル


.dockerignore と名付けられたファイルがソースリポジトリ上に存在するならば、改行でセパレートされた除外パターンのリストと解釈されます。除外パターンとマッチしたソースディレクトリのファイルやディレクトリはコンテキストから外されます。その際にはGoのfilepath.Matchルールが使用されます。

 注意:
 .dockerignore ファイルは、
 Dockerfileや.dockerignoreを無視のためでも使用可能です。
 これは、ビルドコンテキストのルートからファイルを
 新規コンテナへコピーする際に、Dockerfile.dockerignore
 含めたくない場合(例: ADD . /someDir/)に役に立ちます。

次の例では、.dockerignoreファイルにて.gitディレクトリをコンテキストから外しています。これはコンテキストのアップロードのサイズ変更に効果が有ります。

 $ sudo docker build .
 Uploading context 18.829 MB
 Uploading context
 Step 0 : FROM busybox
  ---> 769b9341d937
 Step 1 : CMD echo Hello World
  ---> Using cache
  ---> 99cc1ad10469
 Successfully built 99cc1ad10469
 $ echo ".git" > .dockerignore
 $ sudo docker build .
 Uploading context  6.76 MB
 Uploading context
 Step 0 : FROM busybox
  ---> 769b9341d937
 Step 1 : CMD echo Hello World
  ---> Using cache
  ---> 99cc1ad10469
 Successfully built 99cc1ad10469


FROM



 FROM

もしくは

 FROM :

FROMインストラクションは後続のインストラクション軍に対してベースイメージをセットします。このように、正しいDockerfileは、最初のインストラクションとしてFROMを指定しなくてはなりません。イメージは有効であればどんなものでもかまいません-最も簡単な方法は公式リポジトリからイメージをpullして始めることです。

FROMは、Dockerfile 内で最初の非コメントのインストラクションでなければなりません。

複数のイメージを構築する場合は単一のDockerfileに複数回FROMが登場することが有ります。それぞれのFROMコマンドの前のコミット処理による最後のイメージIDアウトプットを書き留めておいてください。

もしFROMインストラクションにタグをしてしなければ、latestを指定したことになります。また指定したタグが存在しないものであった場合、エラーとなります。

MAINTAINER



 MAINTAINER <氏名>

MAINTAINERインストラクションはイメージを作成した著者名フィールドのセットを許可します。

RUN


RUN には2つのフォームがあります:

RUN <コマンド> (コマンドはシェル上 /bin/sh -c で実行される:shellフォーム)
RUN ["executable", "param1", "param2"] (execフォーム)

RUNインストラクションは現在のイメージのトップ上の新規レイヤでコマンドを実行し、結果をコミットします。結果がコミットされたイメージはDockerfileの次のステップで使用されます。

ソースコントロールのように、Dockerのコアコンセプトに従ったレイヤ化RUNインストラクションやコミット生成は低コストで、コンテナはイメージヒストリのどのポイントからも生成できます。

execフォームは、シェルストリングを台無しにすることの回避を可能にします、また /bin/sh を含まないベースイメージを使ってコマンドをRUNすることを可能にします。

 注意:
 (/bin/sh以外の)異なるシェルを使うために、
 希望するシェルを指定したexecフォームを使います。
 例: RUN ["/bin/bash", "-c", "echo hello"]


 注意:
 exec フォームはJSON配列として構文解釈されます、
 文字の前後にシングルクォート(')ではなく
 ダブルクォート(")を必ず使用してください。


 注意:
 execフォームは、shellフォームとは異なり、
 コマンドシェルを呼び出しません。
 これは通常のシェル実行が発生しないことを意味します。
 例えば、RUN [ "echo", "$HOME" ] における $HOME は変数値に置換されません。
 もしシェル実行シたいのであればshellフォームか、シェルの直接実行
 (例:RUN [ "sh", "-c", "echo", "$HOME" ])のどちらかを使いましょう。

RUNインストラクションのキャッシュは次のビルドの間自動的に無効ではなくなります。「RUN apt-get dist-upgrade -y」のようなインストラクションのキャッシュは次のビルドでも再利用されます。RUNインストラクションのキャッシュは、例えば「docker build --no-cache」のように、「--no-cache」フラグを使うことで無効にすることができます。

詳細は Dockerfile ベストプラクティスを参照してください。

RUN インストラクションのキャッシュは、ADD インストラクションにより無効にすることができます。詳細はADDセクションを確認して下さい。

既知の問題(RUN)


Issue 783は、AUFS ファイルシステム使用の際に発生するファイルパーミッションに関する問題についてです。たとえば、rmコマンドによるファイル削除中に気づくかもしれません。Issue783 には回避策の記述があります。

CMD


CMDインストラクションは3つのフォームがあります。

CMD ["executable","param1","param2"] (exec フォーム、おすすめ)
CMD ["param1","param2"] (ENTRYPOINTへのデフォルトパラメータとして)
CMD command param1 param2 (shell フォーム)

Dockerfileには1つだけCMDインストラクションが存在できます。もし1つ以上のCMDを記述した場合、最後のCMDインストラクションが有効になります。

CMDの主な目的は、実行コンテナのためのデフォルトを提供するためです。これらのデフォルトは実行可能なものを含みます、もしくは実行可能なものを省略可能です、そしてその場合には同様にENTRYPOINTインストラクションを指定しなくてはなりません。

 注意:
 もしENTRYPOINTインストラクションのデフォルト引数を
 提供するためにCMDを使用したならば、
 CMDENTRYPOINT 両方のインストラクションは
 JSON配列フォーマットで指定すべきです。


 注意:
 execフォームはJSON配列として構文解析されます。
 そしてそれはシングルクォート(')ではなく
 ダブルクォート(")を使用しなければならないことを意味しています。


 注意:
 shellフォームとは異なり、execフォームはコマンドシェルを呼び出しません。
 これは通常のシェル実行が発せしないことを意味しています。
 例えば、CMD [ "echo", "$HOME" ] における$HOMEは変数値に置換されません。
 もしシェル実行したいのであれば、shellフォームか、
 CMD [ "sh", "-c", "echo", "$HOME" ] のようにシェルを直接実行するか
 どちらかを使用してください。

shell もしくは exec フォーマットで使用された場合、CMD インストラクションはイメージ動作時に実行されるコマンドをセットします。

CMDのshellフォームを使うのであれば、コマンド(CMDの次の項目)は「/bin/sh -c」上で実行します:

 FROM ubuntu
 CMD echo "This is a test." | wc -

もしシェルなしでコマンドを実行したのであれば、JSON 配列形式として表現し、実行可能ファイルをフルパスで指定しなくてはなりません。この配列形式はCMDの推奨フォーマットです。どんな追加のパラメータも配列内の文字列として独立して表現すべきです:

 FROM ubuntu
 CMD ["/usr/bin/wc","--help"]

もしコンテナで毎回同じ実行可能ファイルを実行するのであれば、CMD と組み合わせて ENTRYPOINT を使うことを考慮すべきです。

もし docker run の引数として指定した場合、CMD内に指定したデフォルトをオーバライドします。

 注意:
 RUNCMD を混同しないでください。
 RUNは実際にコマンドを実行し、結果をコミットします;
 CMD はビルド時には何も実行しませんが、
 イメージのために意図されたコマンドを指定します。


EXPOSE



 EXPOSE [...]

EXPOSE インストラクションは、Dockerに実行中コンテナがlistenしているネットワークポートを知らせます。Dockerはこの情報を使ってリンク(Dockerユーザガイド参照)を使ってコンテナと内部連結します。そして「-P」フラグ使用の際にどのポートをホストへ露出させるかを定義します。

 注意:
 EXPOSEは、ホストへ露出されるポートがどれか、
 もしくはデフォルトでホストからアクセス可能なポートについては、
 定義しません。


ENV



 ENV
 ENV = ...

ENV インストラクションは環境変数とその値をセットします。この値はすべての"子孫"のDockerfileコマンドの環境変数となります、そしてそれらの多くはインラインで置き換え可能です。

ENV インストラクションには2つのフォームがあります。最初のフォーム、「ENV 」は、単一の環境変数に1つの値をセットします。最初の空白の後のすべての(空白やクォートのようなキャラクタを含む)文字列はとして扱われます。

2番めのフォーム、「ENV =...」は一度に複数の値をセットできます。2番めのフォームは等号(=)を構文中に使用します、この点が最初のフォームと異なります。コマンドラインにおける構文解析のように、値の中に空白を含めるためにクォートやバックスラッシュが使用できます。

 例:
 ENV myName="John Doe" myDog=Rex\ The\ Dog \
     myCat=fluffy

および

 例:
 ENV myName John Doe
 ENV myDog Rex The Dog
 ENV myCat fluffy

は最終的に同じ結果となりますが、最初のフォームは1つのレイヤ内ですべて処理します。

ENV を使った環境変数セットは、イメージが生じてコンテナが実行されている間永続化されます。docker inspectコマンドを使って値を確認することができます。また「docker run --env =」を使用して変更することができます。

 注意:
 環境変数の永続性は予想外の影響を起こすことがあります。
 例えば、「ENV DEBIAN_FRONTEND noninteractive」は
 Debianベースのイメージの apt-get ユーザを
 混乱させることがあります。
 単一のコマンドに対する環境変数をセットするには、
 「RUN = 」を使用してください。


ADD


ADD には2つのフォームがあります:

 ADD ...
 ADD [""... ""] (このフォームの場合は、空白を含むパスも指定可能)

ADD インストラクションは から新規ファイル、ディレクトリ、リモートファイルのURLフォームを、コンテナ上のファイルシステムのパス へコピーします。

複数の リソースを指定することができますが、もしそれらがファイルもしくはディレクトリであれば、ビルドされた(ビルドのコンテキスト)ソースディレクトリへ関連付けされるべきです。

それぞれの はワイルドカードを含めることができ、Go の filepath.Match ルールを使ったマッチングしたものに対して実行します。多くのコマンドラインの用途のために、これは予想どおりに振る舞うべきです。例えば:

 ADD hom* /mydir/        # "hom"で始まるすべてのファイルを追加する
 ADD hom?.txt /mydir/    # ?は適当な1文字に置換される

は絶対パス、もしくは WORKDIR と関連したパスで、ソースが対象のコンテナ内へコピーされます。

 ADD test aDir/          # "test" を `WORKDIR`/aDir/ へ追加する

すべての新規のファイルおよびディレクトリは、UIDおよびGIDが0(スーパーユーザ)で作成されます。

がリモートファイルURLである場合、追加先はパーミッションが600(ユーザのみ読み書き可能)となります。もし取得したリモートファイルがHTTP Last-Modified ヘッダを保有していたら、ヘッダのタイムスタンプは追加先のファイル上のmtimeの値として使用されます。そして、ADD 中に処理される他のどんなファイルも、mtime はいつファイルが変更されたかどうかやキャッシュが更新されたかの決定に使用されます。

 注意:
 もし標準入力慶友でDockfileを指定してビルド
 (docker build - < somefile)した場合、
 ビルドコンテキストがありません。
 そして DockerfileADD インストラクションベースの
 URL でのみ構成されます。
 また標準入力経由で圧縮アーカイブも指定でき
 (docker build - < archive.tar,gz)、
 アーカイブのルートの Dockerfile やアーカイブの残りは
 ビルドのコンテキストとして使用されます。


 注意:
 URL ファイルが認証によりプロテクトされているのであれば、
 ADD インストラクションは認証をサポートしていないため、
 RUN wgetRUN curl もしくはそのほかのコンテナ上のツールを
 使う必要があります。


 注意:
 もし コンテンツが変更されているならば、
 一番最初に ADD インストラクションに遭遇するとすべての後続の
 Dockerfile上のインストラクションに対してキャッシュを無効化します。
 これはRUNインストラクションのキャッシュ無効化を含みます。
 詳細はDockerfileベストプラクティスガイドを参照してください。

コピーは、以下の規則に従います:

  • パスはビルドコンテキストの内部を指定すべきです:「ADD ../something /something」は実行できません、これはdocker buildの最初のステップが、コンテキストディレクトリ(およびサブディレクトリ)をdockerデーモンへ送信するためです。


  • もし URL であり の最後がスラッシュで終わっていないならば、ファイルはURLからダウンロードされ へコピーします。


  • もし URL であり の最後がスラッシュで終わっているならば、filenameURLから推断され、ファイルは / へダウンロードされます。例えば、「ADD http://example.com/foobar /」はファイル「/foobar」を作成します。この場合(http://example.com では動作しません)、適切な filename が発見できるように、URLは浅薄ではないパスを指定しなくてはなりません。


  • もし がディレクトリであれば、ファイルシステムのメタデータを含む、ディレクトリのコンテンツすべてがコピーされます。


 注意:
 ディレクトリ自身はコピーされません、コンテンツだけです。


  • もし がローカルの認められた圧縮フォーマット(identity、gzip、bzip2、もしくはxz)tarアーカイブであるならば、ディレクトリとして解凍されます。リモートURLからのリソースは解凍されません。ディレクトリがコピーもしくは回答されたら、同じ振る舞い(tar -x)を実行します:結果は以下の集合になります:

  1. 追加先パスに存在するなんでも、そして
  2. ソースツリーのコンテンツ(ファイル対ファイルの基本に則って競合を解決する)


  • もし が他の種類のファイルであるならば、メタデータとともに個別にコピーされます。この場合、もし がスラッシュ(/)で終わっているならば、ディレクトリとして扱われます、そして のコンテンツは/ベース()へ書き込まれます。


  • もしリソースが、ディレクトもしくはワイルドカードを使って、複数指定されているならば、 はディレクトリであるべきです、そしてスラッシュ(/)で終わっていなければなりません。


  • もし がスラッシュで終わっていなければ、通常のファイルとして扱われます、そして のコンテンツが へ書き込まれます。


  • もし が存在しないならば、パス内のすべての存在しないディレクトリとともに作成されます。


COPY


COPY には以下の2つのフォームがあります:

 COPY ...
 COPY [""... ""] (このフォームはパスに空白文字を含めることができます)

COPY インストラクションはから新たにファイルやディレクトリをコピーし、コンテナ上のファイルシステムの パスに追加します。

複数の リソースを指定することもできます、がビルドされたソースディレクトリ(ビルドコンテキスト)に関連付けしなければなりません。

それぞれの にはワイルドカードを含めることができます、そしてそれらはGoのfilepath.Matchルールに従ってマッチングします。多くのコマンドラインの使い方のように予想された動作をします、例えば:

 COPY hom* /mydir/        # "hom"で始まるすべてのファイルをコピーする
 COPY hom?.txt /mydir/    # ? は任意の1文字に置換される

は絶対パス、もしくはWORKDIRに関連づいたパスです。ソースはコピー先のコンテナ内部にコピーされます。

 COPY test aDir/          # `WORKDIR`/aDir/ に "test"を追加する

すべての新規のファイルやディレクトリはUIDおよびGIDが0(スーパーユーザ)として作成されます。

 注意:
 もし標準出力を使ってビルドする(docker build - < somefile)ならば、
 ビルドコンテキストが存在しないので、COPY が使用できません。

コピーは次のルールに従います:

  • パスはビルドコンテキスト内部を指定しなければなりません:「COPY ../something /something」と記述することはできません、なぜならdocker buildコマンドの最初のステップはコンテキストの(サブディレクトリを含む)ディレクトリをdockerデーモンへ送信するためです。


  • もし がディレクトリであれば、ディレクトリのコンテンツすべてがコピーされます。


 注意:
 ディレクトリ自身はコピーされません、コンテンツだけです。


  • もし その他の種類のファイルであるならば、メタデータと一緒に個別にコピーされます。このケースで、 がスラッシュ(/)で終わっているのであれば、ディレクトリとして取り扱い、 のコンテンツが /ベース()へ書き込まれます。


  • ディレクトリもしくはワイルドカード使用のどちらかで複数の リソースを指定した場合は、 はディレクトリであるべきです、そして指定の最後はスラッシュ(/)で終わっているべきです。


  • もし の指定がスラッシュで終わっていない場合は、通常のファイルとして取り扱い のコンテンツが へ書き込まれます。


  • もし が存在しない場合は、パス内のすべての存在しないディレクトリが作成されます。


ENTRYPOINT


ENTRYPOINT には2つのフォームがあります:

 ENTRYPOINT ["executable", "param1", "param2"] (推奨: exec フォーム)
 ENTRYPOINT command param1 param2 (shell フォーム)

ENTRYPOINT は実行可能なイメージとしてコンテナ実行の設定を許可します。

次の例は、デフォルトで nginx を開始し、ポート80番で待ち受けます:

 docker run -i -t --rm -p 80:80 nginx

docker run 」のコマンドライン引数は、ENTRYPOINT execフォームのすべての要素の後に追加されます、そしてCMDを使って指定したすべての要素をオーバライドします。引数をエントリーポイントに追加することを許可します。例:「docker run -d」は、「-d」の引数をエントリーポイントへ追加します。「docker run --entrypoint」フラグを使うとENTRYPOINTインストラクションをオーバライドすることができます。

shell フォームは、CMD やコマンドラインの引数が使用されることを防ぎますが、ENTRYPOINTを「/bin/sh -c」のサブコマンドとして開始(シグナルをパスしない)する欠点があります。これは、コンテナのPID1にならない-そして、UNIXシグナルを受け取れない-ことを意味します。このため、SIGTERMを「docker stop <コンテナ>」から受け取れません。

Dockerfile の最後の ENTRYPOINT インストラクションだけ有効です。

exec フォーム ENTRYPOINT サンプル


ほぼ安定したデフォルトコマンドやパラメータを指定する際には、exec フォームで ENTRYPOINTを指定します。CMDのどちらかのフォームを使って変更のありそうなデフォルト値を設定します。

 FROM ubuntu
 ENTRYPOINT ["top", "-b"]
 CMD ["-c"]

コンテナ実行した際、topではプロセスだけ参照することができます:

 $ docker run -it --rm --name test  top -H
 top - 08:25:00 up  7:27,  0 users,  load average: 0.00, 0.01, 0.05
 Threads:   1 total,   1 running,   0 sleeping,   0 stopped,   0 zombie
 %Cpu(s):  0.1 us,  0.1 sy,  0.0 ni, 99.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
 KiB Mem:   2056668 total,  1616832 used,   439836 free,    99352 buffers
 KiB Swap:  1441840 total,        0 used,  1441840 free.  1324440 cached Mem

   PID USER      PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
     1 root      20   0   19744   2336   2080 R  0.0  0.1   0:00.04 top

結果について更に調べるには、「docker exec」を使用します:


 $ docker exec -it test ps aux
 USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
 root         1  2.6  0.1  19752  2352 ?        Ss+  08:24   0:00 top -b -H
 root         7  0.0  0.1  15572  2164 ?        R+   08:25   0:00 ps aux

そして、topに対して「docker stop test」を使ってエレガントにシャットダウンを求めることができます。

次のDockerfileでは、ENTRYPOINTを使ってフォアグラウンドでApacheを実行しています(PID 1として):

 FROM debian:stable
 RUN apt-get update && apt-get install -y --force-yes apache2
 EXPOSE 80 443
 VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
 ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

もし単一の実行可能イメージのためにスタータースクリプトが必要であれば、「exec」と「gosu」コマンドを使えばUnixシグナルを最終実行対象が受け取ることを確実にすることができます:

 #!/bin/bash
 set -e

 if [ "$1" = 'postgres' ]; then
     chown -R postgres "$PGDATA"

     if [ -z "$(ls -A "$PGDATA")" ]; then
         gosu postgres initdb
     fi

     exec gosu postgres "$@"
 fi

 exec "$@"


最後に、もしシャットダウン時にいくつかの追加のクリンナップ(もしくは、他のコンテナとの通信)が必要であれば、もしくは1つ以上の実行コンテナと同期しているのであれば、ENTRYPOINT スクリプトはUnixシグナルを受け取りを確実にしなくてはなりません、シグナルを渡して、次にいくつかの仕事をします:

 #!/bin/sh
 # 注意:shを使って書かれています、このためbusyboxコンテナでも動作します

 # サービス停止後マニュアルクリンナップが必要であれば、
 #、もしくは1つのコンテナで複数のサービスを開始する必要があれば
 # trapを使ってください。
 trap "echo TRAPed signal" HUP INT QUIT KILL TERM

 # ここでサービスをバックグラウンドで開始します
 /usr/sbin/apachectl start

 echo "[hit enter key to exit] or run 'docker stop '"
 read

 # サービスを停止してクリンナップをここで実施してます
 echo "stopping apache"
 /usr/sbin/apachectl stop

 echo "exited $0"

このイメージを「docker run -it --rm -p 80:80 --name test apache」で実行するならば、「docker exec」もしくは「docker top」コマンドでコンテナのプロセスを解決することができます、そしてApache停止のスクリプトへ問い合わせてください:

 $ docker exec -it test ps aux
 USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
 root         1  0.1  0.0   4448   692 ?        Ss+  00:42   0:00 /bin/sh /run.sh 123 cmd cmd2
 root        19  0.0  0.2  71304  4440 ?        Ss   00:42   0:00 /usr/sbin/apache2 -k start
 www-data    20  0.2  0.2 360468  6004 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
 www-data    21  0.2  0.2 360468  6000 ?        Sl   00:42   0:00 /usr/sbin/apache2 -k start
 root        81  0.0  0.1  15572  2140 ?        R+   00:44   0:00 ps aux
 $ docker top test
 PID                 USER                COMMAND
 10035               root                {run.sh} /bin/sh /run.sh 123 cmd cmd2
 10054               root                /usr/sbin/apache2 -k start
 10055               33                  /usr/sbin/apache2 -k start
 10056               33                  /usr/sbin/apache2 -k start
 $ /usr/bin/time docker stop test
 test
 real    0m 0.27s
 user    0m 0.03s
 sys 0m 0.03s


 注意:
 「--entrypoint」でENTRYPOINT設定をオーバライド可能ですが、
 これはバイナリに対してexec(sh -cを使ってではなく)する場合のみ可能です。


 注意:
 execフォームはJSONフォームとして解釈されます。
 そしてそれは文字の前後にシングルクォート(')ではなく、
 ダブルクォート(")を使わなければならないことを意味します。

 注意:
 shellフォームとは異なり、execフォームはコマンドシェルを起動しません。
 これは通常のシェル実行が発生しないことを意味しています。
 例えば「ENTRYPOINT [ "echo", "$HOME" ] 」では、$HOMEの環境変数値へ
 置換しないことを意味しています。
 もしshell実行を望むのであれば、shellフォームを使用するか、
 直接shellを実行するかのどちらかです
 (例: ENTRYPOINT [ "sh", "-c", "echo", "$HOME" ])。
 ENVを使っているDockerfileで定義した変数は、
 Dockerfileパーサーにより置換されます

Shell フォーム ENTRYPOINT サンプル


シェル実行を使用してシェル環境変数を置換するために、ENTRYPOINT に明確な文字列を指定して「/bin/sh -c」を実行します。そして、CMD や「docker run」のコマンドライン引数を無視します。
docker stop」がどんなに長く実行している ENTRYPOINT の実行対象に対しても正確なシグナル送信を確実にするために、「exec」を使って開始することを忘れないで下さい:

 FROM ubuntu
 ENTRYPOINT exec top -b


このイメージを実行する場合は、PID 1のプロセスを確認します:

 $ docker run -it --rm --name test top
 Mem: 1704520K used, 352148K free, 0K shrd, 0K buff, 140368121167873K cached
 CPU:   5% usr   0% sys   0% nic  94% idle   0% io   0% irq   0% sirq
 Load average: 0.08 0.03 0.05 2/98 6
   PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
     1     0 root     R     3164   0%   0% top -b

docker stop」上でとちらがきれいにexitしたか:

 $ /usr/bin/time docker stop test
 test
 real    0m 0.20s
 user    0m 0.02s
 sys 0m 0.04s

もしENTRYPOINTのはじめに「exec」追加を忘れたら:

 FROM ubuntu
 ENTRYPOINT top -b
 CMD --ignored-param1

次のように動作します(次のステップで名前を与えます):

 $ docker run -it --name test top --ignored-param2
 Mem: 1704184K used, 352484K free, 0K shrd, 0K buff, 140621524238337K cached
 CPU:   9% usr   2% sys   0% nic  88% idle   0% io   0% irq   0% sirq
 Load average: 0.01 0.02 0.05 2/101 7
   PID  PPID USER     STAT   VSZ %VSZ %CPU COMMAND
     1     0 root     S     3168   0%   0% /bin/sh -c top -b cmd cmd2
     7     1 root     R     3164   0%   0% top -b

topの出力結果からENTRYPOINTPID1ではないことがわかります。

もし「docker stop test」を実行したら、コンテナはきれいに終了しません-「stop」コマンドによりタイムアウト後にSIGKILL送信を強制されます:

 $ docker exec -it test ps aux
 PID   USER     COMMAND
     1 root     /bin/sh -c top -b cmd cmd2
     7 root     top -b
     8 root     ps aux
 $ /usr/bin/time docker stop test
 test
 real    0m 10.19s
 user    0m 0.04s
 sys 0m 0.03s


VOLUME



 VOLUME ["/data"]

VOLUME インストラクションは、指定した名前のマウントポイントを作成し、そしてホストやその他のコンテナからボリュームの外部マウントを維持することを示しています。設定値は、「VOLUME ["/var/log/"]」のようなJSON配列、もしくは「VOLUME /var/log」や「VOLUME /var/log /var/db」のような複数の引数の文字です。より詳しい情報/例や、Dockerクライアント経由のマウントインストラクションはドキュメント「Share Directories via Volumes(ボリューム経由のディレクトリ共有)」を参照してください。

 注意:
 リストはJSON配列として解釈されます、
 そしてそれは単語の前後にはシングルクォート(')ではなく
 ダブルクォート(")を使わなくてはならないことを意味します。


USER



 USER daemon

USER インストラクションは、イメージ実行や Dockerfile RUN CMDENTRYPOINT インストラクションの実行時使用するユーザ名もしくは UID を指定します。

WORKDIR



 WORKDIR /path/to/workdir

WORKDIR インストラクションは、DockerfileRUNCMDENTRYPOINT COPYADD インストラクションのためのワーキングディレクトリを指定します。

1つのDockerfile 内で複数回使用できます。相対パスであれば、前のWORKDIR インストラクションのパスに対する相対パスとなります。例えば:

 WORKDIR /a
 WORKDIR b
 WORKDIR c
 RUN pwd

この Dockerfile の最後の「pwd」コマンドは「/a/b/c」と出力します。

WORKDIR インストラクションはその前にENVで指定した環境変数を解決することができます。Dockerfile内で明確に環境変数だけを使うことができます。例えば:

 ENV DIRPATH /path
 WORKDIR $DIRPATH/$DIRNAME
 RUN pwd

この Dockerfile の最後の「pwd」コマンドは「/path/$DIRNAME」と出力します。

ONBUILD



 ONBUILD [INSTRUCTION]

その他のビルドのためにベースとして使使用されるイメージの場合、ONBUILD インストラクションはイメージへあとで実行するためのトリガインストラクションを追加します。トリガは、下流のDockerfileFROMインストラクションの直後に挿入されたかのように、下流のビルドのコンテキスト内で実行されます。

どんなビルドコンストラクションもトリガとして登録されます。

これは、例えばアプリケーションビルド環境やユーザ個別に設定するデーモンなど、ほかのイメージをビルドするためのベースとして使用されるイメージをビルドする場合、とても便利です。

例えば、特定のディレクトリへアプリケーションソースコードの追加して後で呼び出されるビルドスクリプトが必要となる、再利用可能なPythonアプリケーションビルダである場合のイメージです。ADDRUNを今すぐに実行できません、なぜならまだアプリケーションソースコードにアクセスしません、そして各アプリケーションビルドによって異なるからです。単純にアプリケーションをコピー&ペーストするためのボイれープレートDockerfileを使ってアプリケーション開発者に提供することはできました、しかしそれは能率が悪く、エラーを起こしやすく、更新が難しいです。なぜなら、アプリケーション固有のコードがミックスされるからです。


解決策として、次のビルドステージの間に、後で実行するために事前にインストラクションを登録するためにONBUILDを使います。

どのように動作するかについてはこちら:

  1. ONBUILD インストラクションに遭遇すると、ビルダは構築されたイメージのメタデータにトリガを追加します。インストラクションはそのほかには現在のビルドに影響を及ぼしません。
  2. ビルドの終わりに、すべてのトリガのリストは、キーOnBuildの下の、イメージマニフェスト内に格納されます。それらは「docker inspect」コマンドで調べることができます。
  3. FROMインストラクションを使うことで、後で新規のビルドのためのベースとしてイメージを作成することができます。FROMインストラクション処理の一部として、下流のビルダはONBUILDトリガを探し、登録されたものと同じ命令を処理します。もしいくつかのトリガが失敗したら、FROMインストラクションを、ビルドが失敗する原因が順番に中止されます。すべてのトリガが成功したら、FROM インストラクションを完了し、通常通りビルドを継続します。
  4. トリガは、実行後最終イメージからクリアされます。別の言い方をすると、"grand-children(孫)"のビルドには継承されません。

たとえば、次のように追加します:

 [...]
 ONBUILD ADD . /app/src
 ONBUILD RUN /usr/local/bin/python-build --dir /app/src
 [...]

 警告:
 「ONBUILD ONBUILD」を使ったONBUILDインストラクションの
 チェーンは許可されません。


 警告:
 ONBUILD インストラクションは FROM
 MAINTAINER インストラクションのトリガになりません。


Dockerfile の例



 # Nginx
 #
 # VERSION               0.0.1

 FROM      ubuntu
 MAINTAINER Victor Vieux

 RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server

 # Firefox over VNC
 #
 # VERSION               0.3

 FROM ubuntu

 # 'ニセ'のディスプレイおよびfirefoxの生成のためにvnc, xvfbをインストール
 RUN apt-get update && apt-get install -y x11vnc xvfb firefox
 RUN mkdir ~/.vnc
 # パスワードのセットアップ
 RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
 # firefoxを自動起動(最良の方法ではありませんが、あえてトリックを使っています)
 RUN bash -c 'echo "firefox" >> /.bashrc'

 EXPOSE 5900
 CMD    ["x11vnc", "-forever", "-usepw", "-create"]

 # 複数イメージサンプル
 #
 # VERSION               0.1

 FROM ubuntu
 RUN echo foo > bar
 # 907ad6c2736fのようなものが出力される

 FROM ubuntu
 RUN echo moo > oink
 # 695d7793cbe4 のようなものが出力される

 # 2つのイメージ、/bar の 907ad6c2736f と /oink の 695d7793cbe4

-----

...~つーか、gosuって、何?
とおもって検索したら、みつかった、gosu

もう翻訳飽きたのでgosuのサイトは翻訳する気無いけど..
dockerがgoベースだからたぶんgosuも使えるようになってるみたい..


0 件のコメント:

既存アプリケーションをK8s上でコンテナ化して動かす場合の設計注意事項メモ

既存アプリをK8sなどのコンテナにして動かすには、どこを注意すればいいか..ちょっと調べたときの注意事項をメモにした。   1. The Twelve Factors (日本語訳からの転記) コードベース   バージョン管理されている1つのコードベースと複数のデプロイ 依存関係 ...