Closed5

自動でDockerコンテナをアップデートする「watchtower」を試す

kun432kun432

GitHubレポジトリ

https://github.com/containrrr/watchtower

Watchtower

Dockerコンテナのベースイメージ更新を自動化するプロセス。

Quick Start

watchtower を使うと、Docker Hub や 独自のイメージレジストリに新しいイメージをプッシュするだけで、コンテナ化されたアプリの実行中バージョンを更新できます。

Watchtower は新しいイメージをプルし、既存のコンテナを優雅にシャットダウンし、最初にデプロイした際と同じオプションで再起動します。以下のコマンドで watchtower コンテナを実行してください。

$ docker run --detach \
   --name watchtower \
   --volume /var/run/docker.sock:/var/run/docker.sock \
   containrrr/watchtower

Watchtower はホームラボ、メディアセンター、ローカル開発環境などでの使用を想定しています。商用または本番環境での使用は推奨していません。その場合は Kubernetes の利用をご検討ください。Kubernetes が大きすぎると感じる場合は、MicroK8sk3s のような、Kubernetes クラスター運用の手間を大幅に軽減するソリューションをご覧ください。

公式ドキュメント

https://containrrr.dev/watchtower/

kun432kun432

事前準備(自作コンテナの作成)

なにはともあれ、まずFastAPIを使った自作APIサーバのDockerイメージを作ってDockerHubにpushする。

uvで仮想環境作成

uv init -p 3.12 hello-time && cd $_

パッケージ追加

uv add fastapi uvicorn[standard]

ファイルを用意

version.py
__version__ = "0.0.1"
main.py
from fastapi import FastAPI
import datetime
from version import __version__ as VERSION

app = FastAPI()

@app.get("/")
async def root():
    now = datetime.datetime.now().isoformat()
    return {
        "message": "hello world",
        "version": VERSION,
        "time": now
    }
Dockerfile
FROM python:3.12-slim

COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

COPY . /app

WORKDIR /app
RUN uv sync --frozen --nocache

EXPOSE 8080
CMD ["/app/.venv/bin/uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8080"]

こんな感じになっているはず。

tree
出力
.
├── Dockerfile
├── README.md
├── main.py
├── pyproject.toml
├── uv.lock
└── version.py

1 directory, 6 files

ビルド

docker build -t hello-time:0.0.1 .

軽く起動しておく

docker run -rm --name hello-time -p 8080:8080 hello-time:0.0.1

確認

curl -s http://localhost:8080| jq -r .
出力
{
  "message": "hello world",
  "version": "0.0.1",
  "time": "2025-07-07T11:33:30.207601"
}

ではこれをDockerHubにpush。latestタグも付けている。

docker tag hello-time:0.0.1 kun432/hello-time:0.0.1
docker tag hello-time:0.0.1 kun432/hello-time:latest
docker push kun432/hello-time:0.0.1
docker push kun432/hello-time:latest

pushされた

コンテナの起動とwatchtowerのインストール

これをpullして起動する。ややこしいので、ビルドした端末とは別のDockerサーバを使う。

docker run -d --name hello-time -p 8080:8080 kun432/hello-time:latest
docker ps
出力
CONTAINER ID   IMAGE                     COMMAND                  CREATED          STATUS         PORTS                                         NAMES
0b66c9c3b196   kun432/hello-time:0.0.1   "/app/.venv/bin/uvic…"   17 seconds ago   Up 3 seconds   0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp   hello-time

動作確認もOK

curl -s http://localhost:8080| jq -r .
出力
{
  "message": "hello world",
  "version": "0.0.1",
  "time": "2025-07-07T11:47:06.138905"
}

ではwatchtowerのコンテナを起動する。デフォルトだと24時間間隔でチェックするみたい。今回はテストとして1分に1回チェックするようにしてみる。

docker run -d \
  --name watchtower \
  -v /var/run/docker.sock:/var/run/docker.sock \
  containrrr/watchtower --interval 60
docker ps
出力
CONTAINER ID   IMAGE                      COMMAND                  CREATED          STATUS                             PORTS                                         NAMES
4318c4775813   containrrr/watchtower      "/watchtower --inter…"   16 seconds ago   Up 15 seconds (health: starting)   8080/tcp                                      watchtower
3b35f9e8b545   kun432/hello-time:latest   "/app/.venv/bin/uvic…"   6 minutes ago    Up 5 minutes                       0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp   hello-time

ログを見てみると、1分に1回起動しているのがわかる。

docker logs -f watchtower
出力
time="2025-07-07T12:33:08Z" level=info msg="Watchtower 1.7.1"
time="2025-07-07T12:33:08Z" level=info msg="Using no notifications"
time="2025-07-07T12:33:08Z" level=info msg="Checking all containers (except explicitly disabled with label)"
time="2025-07-07T12:33:08Z" level=info msg="Scheduling first run: 2025-07-07 12:34:08 +0000 UTC"
time="2025-07-07T12:33:08Z" level=info msg="Note that the first check will be performed in 59 seconds"
time="2025-07-07T12:34:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
time="2025-07-07T12:35:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no

コンテナのバージョンアップ

自作のコンテナをバージョンアップする。

ファイルを修正

version.py
__version__ = "0.0.2"

0.0.2としてビルド

docker build -t hello-time:0.0.2 .

DockerHubにpush

docker tag hello-time:0.0.2 kun432/hello-time:0.0.2
docker tag hello-time:0.0.2 kun432/hello-time:latest
docker push kun432/hello-time:0.0.2
docker push kun432/hello-time:latest

で、定期的にdocker psを見ていると・・・

これはまだ何も起きていない

出力
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS                   PORTS                                         NAMES
4318c4775813   containrrr/watchtower   "/watchtower --inter…"   4 minutes ago    Up 4 minutes (healthy)   8080/tcp                                      watchtower
3b35f9e8b545   d7d344783013            "/app/.venv/bin/uvic…"   10 minutes ago   Up 9 minutes             0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp   hello-time

hello-timeのコンテナがいなくなった!

出力
CONTAINER ID   IMAGE                   COMMAND                  CREATED         STATUS                   PORTS      NAMES
4318c4775813   containrrr/watchtower   "/watchtower --inter…"   4 minutes ago   Up 4 minutes (healthy)   8080/tcp   watchtower

新しいコンテナが立ち上がっている

出力
CONTAINER ID   IMAGE                      COMMAND                  CREATED         STATUS                   PORTS                                         NAMES
3607c4790eed   kun432/hello-time:latest   "/app/.venv/bin/uvic…"   4 seconds ago   Up 2 seconds             0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp   hello-time
4318c4775813   containrrr/watchtower      "/watchtower --inter…"   4 minutes ago   Up 4 minutes (healthy)   8080/tcp                                      watchtower

curlで確認すると、新しいバージョンになっているのがわかる。

curl -s http://localhost:8080| jq -r .
出力
{
  "message": "hello world",
  "version": "0.0.2",
  "time": "2025-07-07T12:39:17.107434"
}

watchtowerのログを見ると、途中でコンテナが再作成されているのがわかる。

出力
time="2025-07-07T12:34:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
time="2025-07-07T12:35:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
time="2025-07-07T12:36:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
time="2025-07-07T12:37:23Z" level=info msg="Found new kun432/hello-time:latest image (9c36a2ee109c)"
time="2025-07-07T12:37:23Z" level=info msg="Stopping /hello-time (3b35f9e8b545) with SIGTERM"
time="2025-07-07T12:37:27Z" level=info msg="Creating /hello-time"
time="2025-07-07T12:37:28Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=1 notify=no
time="2025-07-07T12:38:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
time="2025-07-07T12:39:11Z" level=info msg="Session done" Failed=0 Scanned=2 Updated=0 notify=no
kun432kun432

で、いちいちlatestタグを付けていたのは、watchtowerのアップデートの判断は任意のタグでイメージのハッシュが変更されたら、、、、という感じになっているみたいで、残念ながらセマンティックバージョニングに対応しているわけではないみたい。

https://github.com/containrrr/watchtower/issues/111

そこが使いにくくて、アップデートするツール自作してる人もいたりして、まあ確かにちょっと微妙かもなあ。

READMEにもあるけど、

Watchtower はホームラボ、メディアセンター、ローカル開発環境などでの使用を想定しています。商用または本番環境での使用は推奨していません。その場合は Kubernetes の利用をご検討ください。Kubernetes が大きすぎると感じる場合は、MicroK8s や k3s のような、Kubernetes クラスター運用の手間を大幅に軽減するソリューションをご覧ください。

このあたりも含めて、ちょっと商用できちんと管理したいみたいなニーズを考えて作られているいるわけではなさそう。まあだからといってK8Sを入れようとは個人的には思わないけど。

kun432kun432

いつもにも増して今回は内容薄めなので、ドキュメント参照を推奨

kun432kun432

似たようなものでこういうのもある・・・・

https://github.com/pyouroboros/ouroboros

のだが、メンテ終了してる様子・・・

⚠️⚠️⚠️ ouroboros は開発が終了しました。その役割は(ほぼ)果たしており、開発者は現実の生活に屈服しました!フォークして自由にメンテナンスしていただいて構いません。過去1年間のご支援に感謝申し上げます :)。コミュニティからのサポートを受けて、依存関係との整合性を保つため、自動バージョンアップは継続されます。⚠️⚠️⚠️

このスクラップは2ヶ月前にクローズされました