14 Docker Tips for the Payara Platform (Japanese language edition)

by Kenji Hasunuma

Dockerイメージの作成はそれほど難しくありません。Dockerfileスクリプトには、エンジンによってコンテナとして実行されるイメージを定義し準備するいくつかのコマンドが含まれています。

しかし、効率的なビルド・プロセスを持つためには様々な見地が重要であり、その結果として軽量でセキュアなコンテナになるのです。

このブログでは、これから構築するDockerイメージで考慮すべき14のTipsについてご説明します。

  1. イメージをタグ付けする

各Dockerイメージはタグ値があり、明示しない場合にはlatestという値を持ちます。このlatestと言う値はいくつかの理由で好ましくないため、常にセマンティック・バージョニングに基づく値を定義してください。

同じタグ値latestを持つ別のイメージを作成すると、以前のバージョンが上書きされます。これはDockerリポジトリ上で利用可能ですが、Dockerエンジンは引き続き同じタグ値を持つ古いイメージを使用します。エンジンはローカルで利用可能なものを見るだけで、リポジトリに新しいイメージがあるかどうかのチェックまでは行いません。最新のバージョンを使用したい場合には、毎回 docker pull を実行する必要があります。

しかし、同じタグ値(セマンティック・バージョニングに従った値も該当します)を使用すると、別の問題が発生します。すべてのテストを実行したにもかかわらず、新しいバージョンのアプリケーションまたはDockerイメージに問題があると、以前のバージョンに戻す必要があります。しかし、上書きしてしまうと、そのバージョンはもはや存在しません。そのため、常に以前のバージョンにアクセスする必要があり、故に全体の履歴を持つために異なるタグが必要になります。

  1. 一時的なイメージを作成する

Dockerコンテナの実行を停止すると、コンテナに含まれていた情報はすべて失われます。実行中のコンテナ自体にデータやログ・ファイルを保存していた場合は、その情報にアクセスできなくなります。

Payara Server Dockerイメージの場合、以下のコマンドを使用して、ホスト・マシン上のログ・ディレクトリを保存することができます。

docker run -d -v ~/temp/payara-log:/opt/payara/appserver/glassfish/domains/production/logs payara/server-full:5.2020.5

ログ専用のボリュームを割り当て、それをコンテナに割り当てるDockerのボリューム・コマンドとマウント・コマンドに注目してください。

  1. イメージは環境依存であってはならない

アプリケーションとランタイム環境は、おそらく環境固有の設定値が必要になるでしょう。最も一般的なシナリオは、本番環境で接続する必要のあるデータベースとは異なるデータベースにテスト環境で接続する必要があることです。

環境ごとに異なる設定値を含む特定のイメージを作成しないでください。イメージは、一度テストして承認されれば、変更を受けることなく本番環境へ移されます。本番環境用に別のイメージが必要な場合、ある日間違えて間違った「バージョン」を本番環境に入れてしまう可能性があります。本番環境に移行するときアプリケーションを再コンパイルして再パッケージ化してはいけないように、Dockerイメージも同様にしてはいけません。

Payara Platformは、環境変数やその他の設定ソースから設定値を取得するための様々なオプションをサポートしています。これらにより、どのような環境でも変更せずに使用できる単一のイメージを作成することができます。詳細については製品ドキュメントを参照してください。

  1. レイヤー構造

Dockerイメージは一連のレイヤーによって構成されており、コンテナ起動時に実際のものに組み立てられるファイルシステムの一部を含んでいます。

コンテナをより速く起動するためレイヤーの数を制限する必要があります。レイヤーの内容は不変であり、上位で定義されたレイヤーが内容を変更したり、削除するなどしてファイルを変更したりすると、他のレイヤーの情報を上書きします。各ファイルは、実際のファイルシステムを組み立てるために、起動時に結果の内容を計算する必要があります。さらにレイヤーは実際のファイルシステムが最終的にどうなるのかを明らかにするためより多くの作業をすることになります。

しかし、これらのレイヤーにはDockerイメージに冗長なデータが含まれている可能性があります。上位のレイヤーで削除または上書きされたファイルは引き続き存在し、ネットワーク経由で転送しリポジトリに保存する必要があるデータのサイズを増加させます。

Dockerfile内のコマンドのほとんどは、結果として得られるイメージのレイヤーに対応しています。そのため、コマンドを可能な限り組み合わせるべきでしょう。それによりレイヤーの数を制限することが可能で、コマンドを実行した結果として生じる一時ファイルを削除することにつながります。

これがPayaraのDockerfileで似たようなコマンドが現れる理由です (何が起こっているのかを分かりやすくするため簡略化しています)

RUN true \
    && ./asadmin start-domain ${DOMAIN_NAME} \
    # The configuration commands
    && ./asadmin stop-domain ${DOMAIN_NAME} \
    && rm -rf \
       ${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/osgi-cache \
       ${PAYARA_DIR}/glassfish/domains/${DOMAIN_NAME}/logs \
    && true

すべてのOSコマンドをまとめてPayara Serverを停止させると、ログやOSGiキャッシュなどの不要なファイルが削除されます。それらを1つのRUNコマンドにまとめることにより、レイヤーを小さく保ち、設定変更の内容を含むだけで単一のレイヤーとなっています。

  1. 正しいレイヤーの順序

レイヤーは最小限にするだけではなく、その順序もとても重要です。各レイヤーは下のレイヤーへの「リンク」を持っています。そしてあるレイヤーが再構築されると、それに依存するレイヤーもすべて再構築する必要があります。そのため、レイヤーを正しく設計していない場合、小さな変更であってもすべてのレイヤーを再構築しなければならず、そしてすべてのレイヤーをDockerリポジトリへプッシュしなければならなくなります。結果としてビルド時間が長くなり、ネットワーク上に送信する必要のあるデータが増えてしまいます。

docker

レイヤー構造は上掲の画像のようになるはずです。アプリケーションは最上位のレイヤーであり、その他の中間生成物を含んでいません。そのため、アプリケーションを実行可能jarファイルとして実行してはいけません。大きなレイヤーは、小さなコードの変更があるたびに、ランタイムを含め再構築しなければならないからです。

画像を見ると、1つのレイヤーを複数のコンテナで(再)使用できることが分かります。2つ以上のアプリケーションで使用できるように、同じPayara Serverのセットアップを含む同じベース・イメージを使用する場合、レイヤーはDockerエンジンによって一度だけロードされます。

そのため、Dockerfile内のコマンドの順番は重要であり、それらは使用される論理的な順番を必ずしも反映しているとは限りません。CMDおよびENTRYPOINTコマンドは、Dockerfileの最後のコマンドにしないほうがよいでしょう。これらのコマンドは(変更されるとしても)頻繁に変更されることはないため、最上位には位置して、毎回再構築する必要のない最下位に近いレイヤーで作成してください。最後のコマンドは常にアプリケーションの成果物を取り込むCOPYコマンドである必要があります。

  1. 多段ビルドの使用

Dockerイメージの作成が複雑で、多くのステップを踏む場合、多段ビルドの使用を検討してみましょう。先に挙げたTipsで見たように、レイヤーの数と順序が重要です。

状況によっては、冗長なデータを含む複数のレイヤーが出来上がることがあります。そのような場合には、Docker 17.06で導入されたこの新機能を使用することにより、イメージを再び小さくすることができます。

多段ビルドでは、それまでに作成したすべてのレイヤーのファイルシステムにある情報を、新しいレイヤーの出発点として結合します。その結果、変更されたファイルは一度だけ最新の内容で表示され、削除されたファイルは事実上存在しなくなります。

FROM ... AS builder
# Other commands to create the first image


FROM ....
COPY --from=builder <from> <to>
# Contains other commands required for your image
  1. コンテナあたりの単一サービス

コンテナ内でアプリケーションを実行するという考え方は、他のアプリケーションやプロセスからアプリケーションを隔離するというものです。アプリケーションに問題がある場合、そのアプリケーションだけが影響を受け、同じインスタンス上で実行されている他のアプリケーションやプロセスは影響を受けません。

複数のアプリケーションを実行することは、コンテナ化という考え方の本質を破るものであり、実装すべきではありません。

例えば、ログ・ファイルの処理やサービスのプロキシの提供など、コンテナ内で補助的なプロセスを実行したい場合もあることでしょう。しかし、この種のプロセスも別のコンテナに入れて、分離、再利用、およびサイズと設定の最適化を実現できるように、サイドカー・コンテナの原則に従うべきでしょう。

  1. PID 1

シグナルは、プロセスやアプリケーションを適切に終了させたり、強制的にシャットダウンしたりするために使用される、UnixやLinuxの世界では重要なものです。

Dockerコンテナ内では、最初に起動したプロセスである、識別番号1 – PID 1を持つものだけが終了またはシャットダウンの通知を受け取ります。また、この最初のプロセスが停止すると、コンテナも停止します。そのため、例えばアプリケーションが終了した時にコンテナも停止する等、アプリケーションがシグナルに適切に対応できるようにする必要があります。

Tiniプロジェクトは、これらの要件を達成するのに役立ちます。Tiniではすべてのシグナルを定義されたプロセスに委譲し、アプリケーション終了とともに終了します。Tiniは自分でDockerイメージ内にバンドルしなくても、Dockerバージョン1.13からDocker自体の中で利用できるようになっています。Tiniを利用するには、docker runコマンドで --init フラグを指定する必要があります。ただし、アプリケーション内にバンドルしておくことで、ユーザーがイメージを正しく動作させるためにフラグを指定する必要がなかったり、指定し忘れたりすることがないようにすることが推奨されています。

  1. 最小特権ユーザー

ここまでは、実現しうる最高のDockerイメージを作成することだけに集中していましたが、セキュリティ面については触れていませんでした。しかし、セキュリティは重要であり、Dockerのビルド・プロセスの中で対処する必要があります。

重要な点は、アプリケーションを実行するユーザーとそのユーザーが持つ権限です。デフォルトでは、これはrootユーザーとなっており、そのままではセキュリティ上のリスクとなります。

Dockerコンテナは、デフォルトで隔離された状態で実行され、これはプロセス型の実行中のプロセスを見ることができないことを意味しています。しかし、Dockerエンジンを実行しているオペレーティング・システムのカーネルとやりとりをすることはできます。そのためrootユーザーでコンテナ内のアプリケーションを実行することは、セキュリティ上の大きな問題となります。

以上のことから、常に特定のユーザーを作成して、そのユーザーに必要なタスクを実行するのに十分な権限を割り当てるようにしてください。この新しく作成されたユーザーを使用することにより、ハッカーがアプリケーションを介してシステムを乗っ取ろうとすることを確実に阻止できるようになります。

RUN addgroup --gid 1000 payara \
   && adduser --system --uid 1000 --no-create-home --shell /bin/bash --home "${HOME_DIR}" --gecos "" --ingroup payara payara \
   && echo payara:payara | chpasswd

USER payara
  1. 機密データを保持しない

コンテナ自体の中にデータを保存すべきではないという一般的なルールについては既に説明してきました。当然のことながら、機密データを保存してはいけません。

しかし、このTipsは、Dockerfile自体に機密データを保持してはいけないというものです。ファイルの内容は読み取ることができるため、例えばデータベースのパスワードのように、そこに入れた機密データはすべて読み取ることができてしまいます。ビルド・プロセスでこの種の情報を提供する場合には常に引数を使用し、例えばコンテナの実行時にデータベースのパスワードが必要な場合には変数を使用します。

  1. ADDコマンドを避ける

DockerのADDコマンドを使用すると、リモート・ファイルをDockerイメージにコピーすることができます。ADDコマンドには、リソース上でそのままUnzip操作を実行できるという利点もあります。しかし、このコマンドを使用すると、信頼できないソースからプログラムを引き込んだり、転送中に書き換えられてしまったりする可能性もあるため、セキュリティ上のリスクもあります。

ADDコマンドを使用する場合には、必ずチェックサム検証を使用して整合性を確認してください。これにより、悪意のあるコードを含むDockerイメージの作成を回避することができます。

  1. イメージの脆弱性をスキャンする

既知の脆弱性がないか常にイメージをスキャンしてください。Snykのようなツールを使用すると、Dockerfileをスキャンして既知の脆弱性を報告することができます。使用しているアプリケーションや依存関係の既知の脆弱性をスキャンするのと同様に、Dockerイメージについてもこれを行うようにしてください。

既知の脆弱性がないことを知らずにアプリケーションを本番環境に置くことは、認めてよい手順ではありません。

  1. イメージに署名する

前項のTipsにより、適切な手順を使用していることを確認し、Dockerイメージで使用されているリソースをすべてチェックすることができました。セキュアなプロセスを実装して、既知の脆弱性をチェックできるようになりました。

最後に行うべきことは、本番前にDockerイメージが書き換えられないようにすることです。

DockerにはNotaryというツールがあり、これを使用してDockerイメージに署名することができます。DockerエンジンにDockerイメージが読み込まれた時にこの署名を検証することで、ビルド時から内容が変更されていないことを確認することができます。

  1. イメージを作成する

これでDockerイメージを作成するエキスパートになりました。次はボトムアップからDockerイメージを作成していきましょう。インターネット上には数多くの良質なDockerイメージが公開されています。その多くはこれまでのTipsで説明したベストプラクティスをすべて使用しています。

しかし、これらは汎用的なDockerイメージであるため、特定のケースには適していないかしれません。これらのDockerイメージをベースにした場合、ニーズに合わないため前のレイヤーの内容を上書きしてしまうこともあるでしょう。

そのため、完全に最適化された、ご自身のニーズに合わせたバージョンを構築することも選択肢の1つとして検討してみてください。これまでのTipsとHadolintのような構文チェック・ツールを活用して、ベストプラクティスに沿っているかどうかを確認し、ご自身のための最高のイメージを作成してください。

まとめ

Dockerfileビルド・スクリプトを作成することは一見すると簡単で、何かを非常に素早く簡単に動作させることができます。しかし、Dockerのいくつかの概念を知っていると、なぜ良質とされるDockerイメージが存在するのかを理解するのに役立ちます。そして、いつものように、いくつかのセキュリティ面に注意しながら、コンテナ内のアプリケーションが高速で信頼性の高いだけでなく、セキュアに動作するように心がけてください。

Payara Dockerイメージ

詳細についてはこちらをクリックしてください: using the Payara Platform with Docker.

 

 

 Payara Platform  Download Here 

 

Comments