「’latest’を使うと便利だけど…なぜ実務では使うなと言われるのだろう?」今日はその理由を徹底的に掘り下げていきます。
>
🎯 この記事で扱うこと
- コンテナイメージタグがmutable(可変)であると何が起こるか
- Immutableタグとは何か、なぜそれが標準とされるのか
- 実務でよく使われるタグ戦略パターン(SemVer、Git SHA、日付ベース)
- ECR・GCR・Harborでタグの不変性を強制設定する方法
- CI/CDパイプラインで不変タグを自動化する例
📌 導入 / 背景
コンテナを初めて学ぶとき、誰もが docker pull nginx:latest の一行から始めます。手軽で、常に最新バージョンが手に入るように見え、直感的で分かりやすいです。
しかし、実際の運用環境でlatestタグをそのまま使用して失敗した事例は数えきれません。
昨日までは確かに動いていたのに、今日デプロイしたら突然アプリが落ちました。
この言葉の原因の多くは、まさにmutableタグにあります。同じタグ名なのにイメージの内容が変わってしまうこと、これが核心的な問題です。
コンテナエコシステムが成熟するにつれて、現在ではイメージタグをimmutable(不変)に管理することが事実上の業界標準となっています。Kubernetes、GitOps、DevSecOps、どの分野を深掘りしても、最終的にこの原則にたどり着きます。
🔍 MutableタグとImmutableタグ、正確には何が違うのか?
Mutableタグとは?
同じタグ名で異なるイメージを上書きできる状態です。
# 午前10時のビルド
docker build -t myapp:latest .
docker push myapp:latest
# → latest = コミットabc123イメージ
# 午後3時のビルド(コード変更後)
docker build -t myapp:latest .
docker push myapp:latest
# → latest = コミットdef456イメージ(午前のイメージは消滅)
latest以外にも、stable、production、v1のようなタグも同じ方法で上書きするとmutableになります。
Immutableタグとは?
一度pushしたタグは、決して別のイメージで上書きできないポリシーです。
# 特定のGit SHAまたはバージョンでタグ付け
docker build -t myapp:1.4.2 .
docker push myapp:1.4.2
# → 1.4.2はこのイメージに永久固定
# 後で同じタグでpushを試みた場合
docker push myapp:1.4.2
# ❌ ERROR: タグの不変性が有効です。既存のタグを上書きできません。
核心は、「タグ = 特定イメージの固有識別子」となる点です。
⚡ Mutableタグが引き起こす現実的な問題
1️⃣ 再現不可能なビルド/デプロイ環境
latestタグを使用すると、今日デプロイしたイメージと1週間後にロールバックを試みた際のイメージが異なる可能性があります。その間にdocker pull myapp:latestが上書きされていれば、ロールバック自体が無意味になります。
2️⃣ セキュリティ監査の困難さ
「このイメージにはどのようなライブラリが含まれていたか?」を追跡する必要がある場合、mutableタグは根拠を提供できません。CVEが発生した際に、「その時点でデプロイされたイメージが正確に何であったか」を証明することができません。
3️⃣ Kubernetesにおけるキャッシュ問題
Kubernetesは同じタグのイメージをノードでキャッシュします。imagePullPolicy: IfNotPresent 設定時、タグが同じであれば、新しいイメージに更新されたことに気づかず、既存のキャッシュを使い続けます。
# 危険な設定例
spec:
containers:
- name: myapp
image: myapp:latest # ← mutableタグ
imagePullPolicy: IfNotPresent # ← キャッシュ優先、常にpullしない
4️⃣ 同時デプロイ時の競合状態(Race Condition)
マルチインスタンス環境でlatestを同時にpullすると、インスタンスごとに異なるイメージを実行する状況が発生する可能性があります。これは障害原因の特定が非常に困難になります。
🏗️ 実務でよく使われるImmutableタグ戦略
戦略1: Git SHAベース(最も強力な追跡性)
# CIパイプラインの例(GitHub Actions)
IMAGE_TAG=$(git rev-parse --short HEAD) # 例: a3f5c12
docker build -t myapp:${IMAGE_TAG} .
docker push myapp:${IMAGE_TAG}
| 長所 | 短所 |
| コミットとイメージの1:1マッピング | 人間が読みにくい |
| 完璧な追跡性 | バージョンの意味を把握しにくい |
### 戦略2: SemVer(セマンティックバージョン)ベース
# v1.4.2形式
docker build -t myapp:1.4.2 .
docker push myapp:1.4.2
MAJOR.MINOR.PATCH体系で変更規模を直感的に表現できるため、チームコミュニケーションに有利です。
戦略3: Git SHA + SemVer併用(ベストプラクティス)
VERSION="1.4.2"
GIT_SHA=$(git rev-parse --short HEAD)
# SemVerタグ: リリース用
docker push myapp:${VERSION}
# SHAタグ: 追跡用
docker push myapp:${GIT_SHA}
# latestは参照用のみ(開発/テスト環境)
docker push myapp:latest
こうすることで、「1.4.2が正確にどのコミットなのか」をいつでも逆追跡できます。
戦略4: 日付 + ビルド番号ベース
# 20260414-001形式
TAG=$(date +%Y%m%d)-${BUILD_NUMBER}
docker push myapp:${TAG}
リリースサイクルが日付中心のチームに適しています。
💻 レジストリ別タグ不変性強制設定
AWS ECR
aws ecr put-image-tag-mutability
--repository-name myapp
--image-tag-mutability IMMUTABLE
またはTerraformで:
resource "aws_ecr_repository" "myapp" {
name = "myapp"
image_tag_mutability = "IMMUTABLE" # MUTABLEがデフォルトなので、必ず明示的に指定
image_scanning_configuration {
scan_on_push = true
}
}
Harbor(自社構築レジストリ)
Harbor UI → プロジェクト設定 → 「Immutable Tags」ルールを追加:
# タグパターン: v* で始まるタグのみimmutableを適用
Tag Pattern: v*
Repository Pattern: **
Google Artifact Registry
gcloud artifacts repositories update myapp-repo
--location=us-central1
--update-immutable-tags=true # タグ不変性ポリシーを有効化
🤖 GitHub Actions CI/CDでの不変タグ自動化
name: Build and Push
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set image tag
id: tag
run: |
# 短縮SHA + 日付の組み合わせ
echo "IMAGE_TAG=$(date +%Y%m%d)-$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- name: Build image
run: docker build -t ${{ secrets.ECR_REGISTRY }}/myapp:${{ env.IMAGE_TAG }} .
- name: Push image
run: docker push ${{ secrets.ECR_REGISTRY }}/myapp:${{ env.IMAGE_TAG }}
- name: Update Kubernetes manifest
run: |
# GitOps方式: マニフェストのタグを新しいイメージタグに置き換え
sed -i "s|image: myapp:.*|image: myapp:${{ env.IMAGE_TAG }}|g" k8s/deployment.yaml
git commit -am "chore: update image tag to ${{ env.IMAGE_TAG }}"
git push
こうすることで、デプロイごとに固有のタグが自動生成され、Kubernetesマニフェストに記録が残ります。後で「その時デプロイされたイメージが何だったか」をGit履歴で確認できます。
⚠️ 注意事項 / よくある間違い
🚫 latestを運用環境で使用しないこと 開発・テストで便宜上使うのは構いませんが、stagingとproductionでは絶対に禁止です。
🚫 Immutable設定後のタグ衝突処理の不備 同じタグを再pushするとレジストリがエラーを返します。CIパイプラインでタグ生成ロジックを必ずユニークに設計する必要があります。(同じコミットを2回ビルドすると? → SHAは同じ → エラー発生 → ビルド番号の組み合わせを推奨)
🚫 Digestをタグと混同するケース イメージダイジェスト(sha256:abc…)はイメージ内容のハッシュであり、元々不変です。タグが不変であるべきというのは、タグ名が常に同じダイジェストを指すべきであるという意味です。
🚫 レジストリポリシーなしにコンベンションだけで管理 「私たちはlatestを使わないと約束しました」だけでは不十分です。ECR Immutabilityのようなレジストリレベルの強制ポリシーがあって初めて、ミスを根本的に防ぐことができます。
✅ まとめ / 終わりに
| 区分 | Mutableタグ | Immutableタグ |
| 再現性 | ❌ 同一タグ ≠ 同一イメージ | ✅ 同一タグ = 同一イメージ |
| セキュリティ監査 | ❌ 履歴追跡不可 | ✅ デプロイイメージ完全追跡 |
| ロールバック | ❌ イメージが消失する可能性あり | ✅ 常に正確なバージョンに復帰 |
| コラボレーション | ❌ 混乱の可能性あり | ✅ 明確なコミュニケーション |
| 運用リスク | 🔴 高い | 🟢 低い |
コンテナイメージタグの不変性は、単なる慣習ではなく、GitOps・DevSecOps・再現可能なビルドの基盤となる核心的な原則です。今すぐレジストリに不変性ポリシーを有効にし、CIパイプラインでGit SHAベースのタグ自動化を適用してみてください。
次のステップとして、Image Signing(Cosign)やSBOM(Software Bill of Materials)によるイメージ整合性検証まで拡張することで、セキュリティレベルがさらに向上します。

コメントを残す