スキップしてメイン コンテンツに移動

【Day 03】「私のPCでは動くのに」問題の根本原因とDockerによる解決

Day 03 アイキャッチ
▲ Day 03: 「私のPCでは動くのに」問題の根本原因とDockerによる解決
🐳
皆さん、こんにちは! Docker/コンテナ実践講座の専任講師です。
これまでに30年以上の開発現場を経験された方なら、誰もが一度は「開発者のPCでは完璧に動いていたのに、本番サーバーにデプロイした瞬間に謎の例外で落ちる」という胃の痛むトラブルに直面したことがあるはずです。
第3回となる本日は、この開発現場永遠の課題である「私のPCでは動くのに問題」の真の原因と、Dockerの「イミュータブル(不変)性」による根本的解決策を徹底解説します!

① 導入・本日の達成目標

「ビルドしたDLLは同じはずなのに、なぜ本番環境だけで動かないのか?」
「環境構築手順書通りにセットアップしたのに、なぜ新人のPCだけエラーになるのか?」

これらのトラブルは、開発者のスキル不足ではなく、従来のサーバー運用(可変インフラ)が抱える構造的欠陥によって引き起こされていました。 本講義をマスターすれば、以下の3つの目標を完全に達成できます。

  • 開発・ステージング・本番で環境が乖離していく「環境ドリフト(Environment Drift)」のメカニズムを論理的に解明する。
  • サーバー上でパッチを当てず、同一イメージを丸ごと置き換える「イミュータブル・インフラストラクチャ」の設計思想を掴む。
  • C#/.NETエンジニアを長年苦しめてきた「DLL地獄(GAC競合)」からの完全解放と、Gitコミットとイメージの完全再現性を実践する。

② なぜ「私のPCでは動くのに」が起きるのか?(環境ドリフトの正体)

従来の開発スタイルでは、開発者のPC、テスト用のステージングサーバー、そして本番サーバーはそれぞれ別々にOSがインストールされ、個別にセットアップされていました。

環境ドリフトの根本原因
▲ 【図解①】なぜ起きる?開発機・検証機・本番サーバーでの「環境ドリフト」の構造

一見同じように見える環境でも、時間の経過とともに以下のような「微細な差分(環境ドリフト)」が無数に蓄積していきます。

  • OSパッチやランタイムのバージョン差: 開発者のWindows 11には最新の .NET 8.0.4 SDKが入っているが、本番のWindows Serverには .NET 8.0.0 がインストールされていた。
  • 外部ライブラリ・依存パッケージの競合: 検証サーバーには他プロジェクトがインストールした特定のVisual C++再頒布可能パッケージやGACアセンブリが存在していた。
  • 環境変数やレジストリ設定の不一致: 開発者のマシンには暗黙のシステム環境変数やパスが通っていたが、本番機では未定義だった。

どれほど綿密なExcel手順書を作っても、人間の手作業で複数のサーバーを完全に同一状態に保つことは物理的に不可能だったのです。

③ イミュータブル(不変)インフラストラクチャの衝撃

この環境差異トラブルを根本から撲滅したのが、Dockerがもたらした「イミュータブル・インフラストラクチャ(Immutable Infrastructure: 不変インフラ)」の思想です。

イミュータブルインフラの思想
▲ 【図解②】ミュータブル(サーバー上で直接変更)vs イミュータブル(同一イメージ置換)の対比

従来のサーバー運用(ミュータブル / 可変)では、稼働中のサーバーにSSHやリモートデスクトップでログインし、パッチを当てたり設定ファイルを書き換えたりしていました。その結果、サーバーは時間が経つほど誰にも全容がわからない「秘伝のタレ」状態になっていました。

一方、Dockerのイミュータブル運用では、「一度ビルドしたDockerイメージの中身は一切変更しない」という鉄則を守ります。

💡 イミュータブル運用の3大原則
  1. 全環境に「同一イメージ」を配布: 開発者のローカルでビルド・テストしたDockerイメージ(金型)を、そのまま検証環境・本番環境へデプロイする。
  2. 変更時は「丸ごと破棄して再作成」: アプリの修正や設定変更が必要な場合は、サーバー上で直接いじるのではなく、新しいイメージをビルドしてコンテナを丸ごと入れ替える。
  3. ロールバックはタグを切り替えるだけ: 万一バグが混入しても、前回のイメージタグを指定してコンテナを起動し直すだけで、1秒で元の安定状態に戻せる。

📊 従来運用(可変)とDocker運用(不変)の対比

比較項目 従来のサーバー運用(ミュータブル) Docker運用(イミュータブル)
変更の手法 サーバーにログインして直接手動更新 不変イメージをビルドしてコンテナ置換
環境の一致度 環境ごとに徐々に乖離(環境ドリフト) 全環境で100%完全一致(同一ビット)
障害復旧速度 手動での切り戻し手順実行(数時間) 旧イメージタグの起動(わずか数秒)
Gitとの親和性 手順書Wikiの更新(コードと不整合) Gitコミットとイメージが1対1完全連動

④ C#エンジニアを苦しめた「DLL地獄(GAC)」からの完全解放

C#/.NETの長い歴史の中で、多くのベテランエンジニアを悩ませてきたのが「DLL地獄(DLL Hell)」「GAC(Global Assembly Cache)」です。

DLL地獄とGACからの解放
▲ 【図解③】かつてのGAC・共有DLL競合から、コンテナ内自己完結への劇的進化

従来の .NET Framework 時代は、共通ライブラリを C:\Windows\Assembly(GAC)に登録し、IIS上の複数のWebアプリケーションで共有していました。 しかし、アプリAのためにGAC内のDLLをバージョンアップした途端、同じサーバー上で動いていたアプリBが突然死するという悲劇が頻発しました。

Docker × .NET 8 / 9 では、アプリケーションが必要とするすべてのDLL、NuGetパッケージ、そして .NET ランタイム本体までもがコンテナ内部の /app ディレクトリ内に完全に自己完結(カプセル化)されます。

  • ホストOS側のファイルやレジストリを一切汚染しない。
  • 同じサーバー上で、.NET 6 のアプリと .NET 8 のアプリ、さらには異なるバージョンの同一ライブラリが完全に共存可能。
  • テストが終わってコンテナを削除(docker rm)すれば、ホストOSに一切のゴミを残さない。

⑤ GitコミットとDockerイメージの1対1対応による完全再現性

Dockerがもたらすもう一つの絶大なメリットが、「Gitコミットハッシュ」と「Dockerイメージタグ」の完全な1対1紐付けです。

Gitコミットとイメージの完全再現性
▲ 【図解④】GitコミットハッシュとDockerタグの1対1対応による100%再現性サイクル

GitHub ActionsなどのCI/CDパイプラインにおいて、Gitにコードがプッシュされた瞬間にDockerイメージをビルドし、イメージ名にGitのコミットハッシュをタグとして付与します(例: myapi:sha-9f8a3c2)。

これにより、「本番環境で動いているコンテナが、Gitリポジトリのどのコミットのコードで生成されたのか」が100%追跡可能になります。 万が一不具合が報告された場合でも、そのイメージタグをローカルで指定して docker run するだけで、バグが発生した当時の環境を1秒で手元に完全再現して調査できるのです。

⑥ C# / .NET 現場での実践アプローチ

環境差異を完全に排除するためのマルチステージ Dockerfile と、Gitコミットと連動したビルド・実行コマンドを見ていきましょう。

[A] 環境差異を撲滅する高精度 Dockerfile

# 1. 実行環境ベース(バージョンを 8.0-alpine のようにピンポイント指定)
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS base
WORKDIR /app
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
ENV DOTNET_EnableDiagnostics=0

# 2. ビルド環境(SDKバージョンを厳密に固定)
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src

# 先にプロジェクトファイルだけをコピーして restore をキャッシュ化
COPY ["CustomerApi.csproj", "./"]
RUN dotnet restore "CustomerApi.csproj"

# ソースコード全体をコピーしてビルド&発行
COPY . .
RUN dotnet publish "CustomerApi.csproj" -c Release -o /app/publish /p:UseAppHost=false

# 3. 最終成果物(不要なSDKやソースコードを排除し、DLL群のみ配置)
FROM base AS final
WORKDIR /app
COPY --from=build /app/publish .
USER $APP_UID
ENTRYPOINT ["dotnet", "CustomerApi.dll"]

[B] Gitハッシュを埋め込むビルド&実行コマンド(PowerShell)

# 1. 現在のGitコミットハッシュ(短縮形7桁)を取得
$GIT_SHA = (git rev-parse --short HEAD)

# 2. Gitハッシュをタグに含めてDockerイメージをビルド
docker build -t customerapi:sha-$GIT_SHA -t customerapi:latest .

# 3. ビルドされたイメージを確認
docker images customerapi

# 4. コンテナを起動(開発機・ステージング・本番すべてで同じタグを実行)
docker run -d -p 8080:8080 --name customer_service customerapi:sha-$GIT_SHA

# 5. コンテナ内の /app フォルダを確認(DLLが自己完結していることを確認)
docker exec -it customer_service ls -la /app

[C] 4大AIエージェントへの指示プロンプト例

🤖 Antigravity / Claude Code への指示プロンプト:

「このASP.NET Coreプロジェクトに対して、GitHub ActionsでGitコミットハッシュ付きのDockerイメージを自動ビルドし、GitHub Packages(GHCR)へプッシュするワークフローYAML(.github/workflows/docker-build.yml)を作成してください。イミュータブルインフラの原則に基づき、タグの重複上書きを防止する構成にしてください。」

⚠️ 30年選手がハマりやすい現場の落とし穴

「コンテナ内に docker exec で入って apt-get や設定変更をしてしまう罠(可変への逆戻り)」
従来のVM運用の癖で、トラブルシューティングの際に「稼働中のコンテナに入って手動で設定ファイルを書き換えたりパッチを当てて解決する」エンジニアが後を絶ちません。
コンテナ内で直接行った変更は、コンテナ再起動や再デプロイ時にすべて消滅します。変更は必ず Dockerfile や Git管理された設定ファイルに反映し、イメージを再ビルドしてデプロイする のがイミュータブル運用の鉄則です!

📌 本日のまとめ & 明日へのステップ
  • 「私のPCでは動くのに」の真因は、環境ごとに微細な差分が生じる「環境ドリフト」である。
  • Dockerは「同一イメージの全環境配布」と「不変(イミュータブル)運用」によって環境差分を完全撲滅する。
  • GitコミットハッシュとDockerタグを1対1で紐付けることで、過去のあらゆる時点の開発環境を100%再現できる。
次回【Day 04】予告:
『Dockerの3大要素(Dockerfile / Image / Container)とライフサイクル』〜作成・起動・停止・破棄の完全図解〜

コメント

このブログの人気の投稿

Perfume、2025年末で“コールドスリープ(活動休止)”へ──東京ドーム2daysの記録と、公式・各界・ファンの声

  1) 公式発表・基本情報 活動休止(コールドスリープ)を正式発表 2025年9月21日、公式サイト「 Perfumeより 」で、2026年から一度コールドスリープに入ると発表。再始動の意思(“新しいPerfume”として戻る)も明記。 Perfume Official Site 今後の運用告知(配信・上映など) 同21日付の「 今後のPerfumeに関して 」で、9/23(火・祝)東京ドーム公演の LIVESHIP全世界生配信 および 全国ディレイビューイング 決定を案内。特設サイトも併記。 Perfume Official Site +1 公式グッズ **「Perfume Calendar 2026」**発売決定(B2壁掛け/表紙+2カ月1枚×6枚+ポスター)。テーマは“フルーツ”。 Perfume Official Site 2) 東京ドーム2days(9/22・23)要点 公演タイトル: ZO/Z5 Anniversary “ネビュラロマンス” Episode TOKYO DOME 。日程・開場/開演・会場・物販等の実務情報は特設で公開。 Perfume Official Site 9/23公演を世界配信(LIVESHIP) /ディレイビューイング実施。 Perfume Official Site 海外向け報道(英語)も「 indefinite hiatus 」として広く配信。 The Times of India 国内英字メディアも速報。 朝日新聞 3) 現地レポ( メリ爺 )から見える空気 あなたの現地記録(9/24公開)を、ファクトと現場情景の両面で反映: 両日参戦 。22日は アリーナ 、23日は 1階席から俯瞰 。前日の発表を受け、開演前のザワつき→MC後に“いつもの一体感”へ。開演は22日 18:45 頃。オープニングは**P³ツアー東京ドーム(2020/2)**と同趣向で“回収”の演出。 メリ爺のゲーム万歳 MC所感 :解散ではなく前向きな休止で不安を和らげる内容。 かしゆか の涙、 のっち の気丈さ、 あ〜ちゃん の思い出語り──3人の結束と「 また戻る 」約束が強く刻まれた。 メリ爺のゲーム万歳 体感時間 : “3時間弱”級のボリューム ながら“過去一...

【Perfume】Perfume 3rd Tour 「JPN」さいたまスーパーアリーナに行って参りました~

Perfume 3rd Tour 「JPN」1月28日、29日の2daysで参戦して参りました。 ファンクラブ会員の選考抽選会に28日、29日の両日で申し込んでいたんですが、両方とも当選していたという嬉しい誤算。 仕事だぁ、なんだかんだと紆余曲折ありまして28日の参戦は危ぶまれていたのですが、なんとかかんとか28日は40分遅刻しながらも参戦、29日はフル参戦する事が出来ました(∩´∀`)∩

ニンテンドースイッチでオンラインゲームを遊ぶには?

モンスターハンターライズが発売になって、家族でモンハン楽しみたい人が増えたみたいですね。 某ブログの検索キーワードを調べると、モンハンを家族でやるにはどうしたらいいのか?を調べている人が沢山いるのがわかります。 ※これでも一部  家族で協力するにはSwitch2台、ソフトも2つ購入する必要があります。 1台のスイッチでは遊べませんし、ソフト1つでも遊べません。 でも、1台のスイッチで別々アカウントがあれば交代で遊ぶこともできますよ。セーブデータもしっかり分かれます。 Switchのオンラインゲームを家族で遊ぶ方法については以下の記事にまとめてますのでよろしければ参考にしてみてくださいね。 家族でモンスターハンターライズDL版のオンラインプレーを楽しむ方法