Dockerコンテナの永続性とストレージの扱い方
Dockerは、アプリを独自の環境でどのシステムでも実行するために設計されたコンテナ化サービスです。プラットフォームに依存しないことを意図していますが、ディスクにデータを保存する必要がある場合は、ボリュームやバインドマウントを使用して行うことができます。
外部データベースまたはオブジェクトストアを使用する
これはほとんどの人が推奨する方法です。状態をディスク上のファイルとして保存することはDockerのモデルとは一致せず、実行可能であっても、本当にそれが必要なのかを考えることが常に最良です。
例えば、データベースにデータを保存する必要があるDockerでウェブアプリケーションを実行しているとしましょう。MySQLをDockerコンテナで実行するのはあまり意味がないので、代わりにMySQLをRDSまたはEC2にデプロイし、Dockerコンテナがそれに直接接続できるようにするべきです。Dockerコンテナは意図された通りに完全にステートレスであり、停止、開始、またはハンマーで叩かれることができ、その代わりに新しいものを立ち上げることができ、すべてデータ損失なしに行えます。IAMの権限を使用すると、これを安全に、完全にあなたのVPC内で実現することができます。
もし本当にユーザーがアップロードした写真やビデオなどのファイルを保存する必要があるなら、AWSのSimple Storage Service(S3)を使用すべきです。EBSベースのストレージよりもずっと安価で、ECSコンテナの共有ファイルシステムの主要な選択肢であるEFSストレージに比べても遥かに安いです。ディスクにファイルを保存する代わりに、直接S3にアップロードします。この方法では、アップロードしたコンテンツに対してLambda関数を使用して追加の処理を実行することも可能で、画像やビデオを圧縮するなどの操作を行うことで、帯域幅コストを大幅に節約できます。
簡単な解決策:コンテナにドライブをマウントする
Dockerは永続性を実現するための2つの方法を提供しています:ボリュームマウントとバインドマウントです。バインドマウントでは、サーバーのファイルシステム上の特定の場所をDockerコンテナ内の場所にマウントできます。このリンクは読み取り専用のものもあれば、読み書き可能なものもあり、Dockerコンテナによって書き込まれたファイルはディスク上に永続化されます。
Dockerコンテナ内のターゲットディレクトリに個々のホストディレクトリをバインドすることができますが、これは便利ですが、推奨される方法はDockerが管理する新しい「ボリューム」を作成することです。これにより、バックアップ、転送、および異なるコンテナインスタンス間でのボリュームの共有が容易になります。
注意事項: Dockerを実行しているサーバーに直接アクセスできない場合、AWSのElastic Container Service (ECS)やKubernetesのような管理されたデプロイメントでは、注意が必要です。これはサーバー自身のディスクスペースに関連しており、通常は一時的です。ECSで本当の永続性を達成するためには、EFSのような外部ファイルストレージを使用することをお勧めします(詳細については後で説明します)。
ただし、バインドとボリュームマウントは、サーバー上でアプリの簡単なインストールを実行するためにDockerを使用している場合や、テスト目的での迅速な永続性を求めているだけの場合には、うまく機能します。どちらにしても、ボリュームの作成方法は、保存している場所に関係なく同じになります。
コマンドラインから新しいボリュームを作成できます:
docker volume create nginx-configそれから、Dockerコンテナを実行するときに、--mount フラグを使ってコンテナ内のターゲットにリンクします:
docker run -d
--name devtest
--mount source=nginx-config,target=/etc/nginx
nginx:latest
もしdocker inspect <name>を実行すると、Mountsセクションの下にボリュームがリストされているのが見えます。
Docker Composeを使用している場合、セットアップは非常に簡単です。各コンテナサービスのためにvolumesエントリを追加し、ボリューム名をゲストの位置にマッピングします。また、Composeがプロビジョニングできるように、最上位のvolumesキーにボリュームのリストを提供する必要があります。
version: "3.0"
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- nginx-config:/etc/nginx/
volumes:
nginx-config:
これにより、このコンポーズのためにボリュームが自動的に作成されます。コンポーズの外部からプレメイドのボリュームを使用したい場合は、ボリューム設定でexternal: trueを指定してください。
volumes:
cms-content:
external: true
ボリュームを定義するのを気にせずに、代わりにバインドマウントを行いたい場合は、ボリューム名の代わりにパス名を入力するだけです。
version: "3.0"
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- /docker/nginx-config/:/etc/nginx/
特定のケースが必要な場合は、DockerのComposeでボリュームを使用するための完全なドキュメントを読むことができます。
管理されたデプロイメントには、共有ファイルシステム(AWS EFS)を使用してください。
AWS ECS にデプロイしている場合、通常のバインドやボリュームマウントを使用することはできません。なぜなら、コンテナをシャットダウンすると、次回起動する際に同じマシン上で実行されることはおそらくないため、永続性の目的が達成されないからです。
ただし、別のAWSサービスであるElastic File System(EFS)を使用することで、永続性を達成することも可能です。EFSは共有ネットワークファイルシステムです。複数のEC2サーバーにマウントでき、アクセスされたデータはすべてのサーバーで同期されます。たとえば、これを使用してウェブサイトの静的コンテンツやコードをホストし、ECS上でワーカーノードを実行して実際のコンテンツの提供を処理することができます。これは、ボリュームマウントがECSデプロイメントを超えて持続する外部ドライブにバインドされているため、ディスクにデータを保存しないという制限を回避します。
これを設定するには、EFSファイルシステムを作成する必要があります。これは比較的簡単で、EFS管理コンソールから行うことができますが、ボリュームを操作するためにボリュームIDをメモしておくことをお勧めします。
EFSボリュームに手動でファイルを追加または変更する必要がある場合は、任意のEC2インスタンスにマウントできます。amazon-efs-utilsをインストールする必要があります:
sudo yum install -y amazon-efs-utils次に、以下のコマンドを使用してIDを指定してマウントします:
sudo mount -t efs fs-12345678:/ /mnt/efsこの方法では、EFSボリュームの内容をサーバー上の別のHDDのように直接表示および編集することができます。すべてが正しく機能するように、nfs-utilsがインストールされていることを確認してください。
次に、このボリュームにECSを接続する必要があります。ECS管理コンソールで新しいタスク定義を作成します。下にスクロールし、「JSONで設定」を選択します。その後、空の「volumes」キーを以下のJSONに置き換え、「family」キーを末尾に追加します:
"volumes": [
{
"name": "efs-demo",
"host": null,
"dockerVolumeConfiguration": {
"autoprovision": true,
"labels": null,
"scope": "shared",
"driver": "local",
"driverOpts": {
"type": "nfs",
"device": ":/",
"o": "addr=fs-XXXXXX.efs.us-east-1.amazonaws.com,nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2,noresvport"
}
}
}
],
"family":"nginx",
fs-XXXXXX.efs.us-east-1.amazonaws.com をあなたのEFSボリュームの実際のアドレスに置き換えてください。新しいボリュームが表示されるはずです:
これはコンテナ定義にマウントポイントとして使用できます。「コンテナを追加」を選択するか(既存のものを編集して)、 「ストレージとログ」の下で新しく作成したボリュームを選択し、コンテナパスを指定してください。
タスク定義を保存し、この新しい定義でクラスターを起動すると、すべてのコンテナが共有ファイルシステムにアクセスできるようになります。


