ASP.NET Core アプリケーションを実行するために、アプリケーションをビルドし、コンテナー内のソース コードをコピーする dockerfile を生成しました。このソース コードは、Jenkins を使用して Git によって取得されます。そのため、ワークスペースでは、dockerfile で次の操作を実行します。
WORKDIR /app
COPY src src
Jenkins は Git を使用してホスト上のファイルを正しく更新しますが、Docker はこれをイメージに適用しません。
構築のための基本的なスクリプト:
#!/bin/bash
imageName=xx:my-image
containerName=my-container
docker build -t $imageName -f Dockerfile .
containerRunning=$(docker inspect --format="{{ .State.Running }}" $containerName 2> /dev/null)
if [ "$containerRunning" == "true" ]; then
docker stop $containerName
docker start $containerName
else
docker run -d -p 5000:5000 --name $containerName $imageName
fi
パラメータ--rm
やコンテナの停止/削除など、さまざまなことを試しました--no-cache
docker run
前に新しいのはビルドです。ここで何が間違っているのかわかりません。 の呼び出しCOPY src src
によりレイヤー ID が生成され、キャッシュ呼び出しが行われないため、docker はイメージを正しく更新しているようです。
Step 6 : COPY src src
---> 382ef210d8fd
コンテナを更新する推奨方法は何ですか?
私の典型的なシナリオは次のようになります。アプリケーションは、Docker コンテナ内のサーバー上で実行されています。次に、ファイルの変更などにより、アプリの一部が更新されます。これで、コンテナは新しいバージョンを実行するはずです。Docker は、既存のコンテナを変更するのではなく、新しいイメージを構築することを推奨しているようなので、私が行っているような再構築の一般的な方法は正しいと思いますが、実装の詳細の一部を改善する必要があります。
ベストアンサー1
視覚的な説明付きビデオ(2022年から)
たくさんの肯定的なフィードバックをいただいたので前回の最初の視覚的説明グラフィカルなビデオで視覚化できるものがいくつかあるため、この質問と回答について別のビデオを作成することにしました。このビデオでは、複数のシステム (および K8) で Docker を使用して過去数年間に得た知識と経験に基づいて、この回答を視覚化し、更新しています。
この質問は ASP.NET Core のコンテキストで尋ねられましたが、実際にはこのフレームワークとは関係ありません。問題は Docker の概念に関する基本的な理解が不足していたため、ほぼすべてのアプリケーションとフレームワークで発生する可能性があります。そのため、ここではシンプルな Nginx Web サーバーを使用しました。Web サーバーについては多くの方がご存知だと思いますが、ASP.NET Core などの特定のフレームワークがどのように機能するかを誰もが知っているわけではないからです。
根本的な問題は、コンテナとイメージの違いと、それらのライフサイクルの違いを理解することであり、これがこのビデオの基本的なトピックです。
テキスト回答(2016年版)
調査とテストを行った結果、Dockerコンテナの寿命について誤解していたことが判明しました。コンテナを再起動するだけでは、その間にイメージが再構築された場合、Dockerは新しいイメージを使用するわけではありません。代わりに、Dockerはイメージをフェッチします。前にコンテナを作成します。そのため、コンテナを実行した後の状態は永続的です。
削除が必要な理由
したがって、再構築と再起動だけでは不十分です。コンテナはサービスのように機能すると思っていました。つまり、サービスを停止し、変更を加えて再起動すれば、変更が適用されます。これが私の最大の間違いでした。
コンテナは永続的であるため、まず を使用して削除する必要がありますdocker rm <ContainerName>
。コンテナが削除された後は、 で単純に起動することはできませんdocker start
。これは を使用して実行する必要があります。docker run
自体は、新しいコンテナ インスタンスを作成するために最新のイメージを使用します。
コンテナは可能な限り独立している必要がある
この知識があれば、コンテナにデータを保存することがなぜ重要なのか理解できます。悪い習慣とみなされるそしてDockerは推奨するデータボリューム/ホストディレクトリのマウント代わりに、アプリケーションを更新するにはコンテナを破棄する必要があるため、内部に保存されているデータも失われます。これにより、サービスのシャットダウン、データのバックアップなどの余分な作業が発生します。
したがって、これらのデータをコンテナから完全に除外することは賢明な解決策です。データがホスト上に安全に保存され、コンテナにはアプリケーション自体のみが保持されるため、データについて心配する必要はありません。
なぜ-rf
本当に役に立たないのか
このdocker run
コマンドは、掃除というスイッチを使用します-rf
。これにより、Docker コンテナを永続的に保持する動作が停止します。 を使用すると-rf
、Docker は終了した後にコンテナを破棄します。ただし、このスイッチには問題があります。Docker はコンテナに関連付けられた名前のないボリュームも削除するため、データが破壊される可能性があります。
スイッチは、開発中に作業を節約して簡単なテストを行うのに適したオプションですが-rf
、本番環境ではあまり適していません。特に、ほとんどの場合必要となる、バックグラウンドでコンテナーを実行するオプションがないため、その傾向が顕著です。
コンテナを削除する方法
コンテナを削除するだけでこれらの制限を回避できます。
docker rm --force <ContainerName>
--force
実行中のコンテナで SIGKILL を使用する(または)スイッチ-f
。代わりに、次の前にコンテナを停止することもできます。
docker stop <ContainerName>
docker rm <ContainerName>
どちらも同じです。docker stop
また、シグナルタームただし、--force
スイッチを使用すると、特に CI サーバーを使用している場合は、スクリプトが短縮されますdocker stop
。コンテナーが実行されていない場合はエラーがスローされます。これにより、Jenkins や他の多くの CI サーバーは、ビルドが誤って失敗したと見なします。これを修正するには、質問で行ったように、最初にコンテナーが実行されているかどうかを確認する必要があります (containerRunning
変数を参照)。
もっと良い方法があります(2016年追加)
などの単純な docker コマンドはdocker build
、docker run
初心者が基本的な概念を理解するのに適していますが、すでに Docker に慣れていて生産性を高めたい場合には煩わしくなります。より良い方法は、Docker-Compose を使用することです。これは複数のコンテナー環境向けに設計されていますが、単一のコンテナーでスタンドアロンで使用する場合にも利点があります。ただし、複数のコンテナー環境はそれほど珍しいものではありません。ほぼすべてのアプリケーションには、少なくともアプリケーション サーバーとデータベースがあります。キャッシュ サーバー、cron コンテナーなどのものもあります。
version: "2.4"
services:
my-container:
build: .
ports:
- "5000:5000"
これで、 を使用するだけdocker-compose up --build
で、手動で実行したすべての手順が compose によって処理されます。2016 年の回答として追加した単純な Docker コマンドを使用したスクリプトよりも、こちらの方が好みです。それでも動作しますが、より複雑で、特定の状況を docker-compose ほどうまく処理できません。たとえば、compose はすべてが最新かどうかを確認し、変更のために再構築が必要なものだけを再構築します。
特に複数のコンテナを使用している場合、Compose にはさらに多くの利点があります。たとえば、コンテナをリンクすると、手動でネットワークを作成/維持する必要がなくなります。また、依存関係を指定して、起動時に DB に依存するデータベース コンテナをアプリケーション サーバーの前に起動することもできます。
以前、Docker-Compose 1.x で、特にキャッシュに関する問題に気づきました。このため、何かが変更されてもコンテナーが更新されません。しばらくの間、Compose v2 をテストしましたが、これらの問題は再び発生していないため、現在は修正されているようです。
Docker コンテナを再構築するための完全なスクリプト (2016 年の元の回答)
この新しい知識に従って、私はスクリプトを次のように修正しました。
#!/bin/bash
imageName=xx:my-image
containerName=my-container
docker build -t $imageName -f Dockerfile .
echo Delete old container...
docker rm -f $containerName
echo Run new container...
docker run -d -p 5000:5000 --name $containerName $imageName
これは完璧に動作します:)