Windows上で長年Visual StudioやC#を使い倒し、堅牢なエンタープライズシステムを構築してきたベテランの皆様、心より歓迎いたします!
Windows開発者がDockerコンテナ(Linux世界)へ踏み出したとき、「ほぼ100%の確率で最初に遭遇し、半日〜数日を溶かす最大の落とし穴」が存在します。それが「改行コード(CRLF / LF)問題」と「ファイル実行権限(パーミッション)」の壁です。
「ファイルは間違いなく存在するのに、なぜか
./entrypoint.sh: not found と言われてコンテナが起動しない……」という不可解な現象を、本日はGitの仕組みと合わせて根本から完全撲滅しましょう!
① 導入・本日の達成目標
WindowsとLinuxは、根本的なファイルシステムの思想や改行の表現方式が異なります。
従来のように「Windowsマシンだけで完結するC#開発」を行っている間は、この違いを意識する必要はほとんどありませんでした。
しかし、「WindowsでソースコードやDockerfileを編集し、Linuxコンテナ内で実行する」というハイブリッド環境においては、不可視の1文字や1ビットの差異が致命的な停止障害を引き起こします。
本日の講義を終える頃には、以下の3つの目標を完全にクリアできるようになります。
- CRLFとLFのバイナリ差異を理解し、Linuxコンテナでスクリプトが落ちる根本原因(シバンの誤認識)を完全特定できる。
- 個人のPC設定(
core.autocrlf)に頼らず、リポジトリ共通ルールとして改行コードを自動統制する.gitattributesを正しく設計できる。 - Windows環境からでもLinuxの実行権限(
chmod +x)をGitインデックスに記録し、コンテナ内での「Permission denied」を根絶できる。
② 改行コードの罠:CRLF vs LF の正体とエラーの真相
テキストファイルの「改行」は、画面上ではただの空白行に見えますが、内部的には特定の制御文字バイトが記録されています。
OSごとの改行コードの仕様は以下の通りです。
- Windows(CRLF):
(0x0D 0x0A の2バイト)。タイプライターの「復帰(Carriage Return)」と「改行(Line Feed)」に由来。 - Linux / macOS(LF):
(0x0A の1バイト)。
なぜ「./entrypoint.sh: not found」という謎エラーが起きるのか?
Windowsのエディタで作成したシェルスクリプトの先頭行には、通常以下のようなシバン(Shebang)を書きます。
#!/bin/sh
しかし改行コードがCRLFの場合、末尾に不可視の
(キャリッジリターン)が付着しています。
Linuxカーネルはこれを行末ではなく「コマンド名の一部」として解釈するため、システム上から /bin/sh
という名前の実行ファイルを探そうとします。
当然、Linux上に /bin/sh
などというファイルは存在しないため、Linuxは「そんなファイルやディレクトリはありません(No such file or directory / not found)」というエラーを吐き出して即座に異常終了します。
開発者は「./entrypoint.sh は目の前にあるのに、なぜ『not found』なんだ!?」と大混乱に陥るわけです。
③ 旧来のファイル共有(VM/Samba)と現代のGit+Docker管理の対比
かつて物理サーバーやVMware時代にも、Windows共有フォルダ(SMB)経由でLinuxにスクリプトをコピーして改行コードが壊れるトラブルが多発していました。
④ .gitattributes によるリポジトリレベルの完全自動統制
「Git for Windowsをインストールした時に core.autocrlf = true にしたから大丈夫」と考えるのは非常に危険です。
個人のグローバル設定(~/.gitconfig)は、新メンバーの参加時やCI/CD環境(GitHub Actions / GitLab CI)において設定漏れが発生しやすく、再現性が担保できません。
チーム開発において唯一にして最強の解決策は、「リポジトリ直下に .gitattributes を配置し、Git自身にファイル種別ごとの改行ルールを強制させること」です。
【決定版】C# + Docker向け .gitattributes 設定ファイル
# ----------------------------------------------------------------------------- # 1. デフォルト設定:すべてのテキストファイルを自動判別し、Git保管時はLFへ正規化 # ----------------------------------------------------------------------------- * text=auto # ----------------------------------------------------------------------------- # 2. Linux / Docker 実行スクリプト:Windows上であっても【絶対にLF固定】 # ----------------------------------------------------------------------------- *.sh text eol=lf *.bash text eol=lf entrypoint.sh text eol=lf Dockerfile text eol=lf docker-compose*.yml text eol=lf *.env text eol=lf # ----------------------------------------------------------------------------- # 3. C# / .NET 関連コード:差分アルゴリズムの最適化と柔軟な管理 # ----------------------------------------------------------------------------- *.cs text diff=csharp *.csx text diff=csharp *.csproj text *.sln text eol=crlf *.props text *.targets text *.json text *.xml text *.config text # ----------------------------------------------------------------------------- # 4. バイナリファイル:改行コード変換の完全抑止 # ----------------------------------------------------------------------------- *.dll binary *.exe binary *.pdb binary *.so binary *.png binary *.jpg binary *.ico binary
この設定ファイルをリポジトリのルートに置いてコミットするだけで、Windows上のエディタ(Visual Studio / VS Code)がCRLFで保存したとしても、Gitにコミット(ステージング)される瞬間に自動的にLFへ変換され、Linuxコンテナ内には常に純粋なLFとして渡されるようになります。
⑤ ファイル実行権限(パーミッション)の落とし穴と解決法
改行コード問題をクリアしても、次に立ちはだかるのが「実行権限(Executable Permission)」の壁です。
Windowsの標準ファイルシステム(NTFS)には、LinuxのようなPOSIX実行権限ビット(rwxr-xr-x)の概念がありません。
そのため、Windows上で新規作成した entrypoint.sh を通常通り git add してコミットすると、ファイルモードは 100644(通常の読み書きファイル / 実行権限なし) として記録されます。
これをDockerビルドでコンテナ内に COPY して実行しようとすると、以下の非情なエラーに直面します。
/bin/sh: ./entrypoint.sh: Permission denied
Windowsから一発解決する「git update-index」コマンド
Linuxマシンを用意したりWSL2側で chmod +x を叩き直さなくても、Git for Windowsのコマンド1行でGitインデックス上のパーミッションを直接変更できます。
# Windows PowerShell または コマンドプロンプトから実行可能 git update-index --chmod=+x scripts/entrypoint.sh # 変更を確認(ファイルモードが 100644 ➡ 100755 に昇格していることを確認) git ls-files --stage scripts/entrypoint.sh # 出力例: 100755 3b18e512... 0 scripts/entrypoint.sh # 変更をコミット git commit -m "chore: add executable permission to entrypoint.sh"
ファイルモード 100755 でコミットされたスクリプトは、Linuxコンテナ内へ COPY された瞬間に自動的に実行可能ビットが有効になり、RUN chmod +x をDockerfileに書かなくてもそのまま安全に起動します!
⑥ C# / .NET 現場での実践:理想のGitリポジトリ構造
C#ソースコードとDockerインフラ資産、そしてGit統制設定が調和した理想的なリポジトリ構成を確認しましょう。
推奨ディレクトリツリー構成
MyEnterpriseApp/
├── .git/
├── .gitattributes # 【最重要】改行コード&diff統制ルール
├── .gitignore # bin/, obj/, .vs/ の除外
├── .dockerignore # Dockerビルドコンテキストの最適化
├── Dockerfile # マルチステージビルド定義
├── docker-compose.yml # ローカル開発用スタック定義
├── scripts/
│ └── entrypoint.sh # LF固定 & 権限100755(DB待機やマイグレーション処理)
└── src/
├── MyEnterpriseApp.sln
└── OrderService/
├── Program.cs
├── Controllers/
└── OrderService.csproj
⑦ 実践コマンド集:既存リポジトリの改行コード一括再正規化
「途中で .gitattributes を追加したけれど、既にCRLFでコミットされてしまったファイルがある……」という場合の安全な一括修復手順です。
# 1. 念のため現在の作業変更をコミット git commit -am "WIP: save before renormalize" # 2. .gitattributesの定義に従ってインデックス全体を一括再評価・再正規化 git add --renormalize . # 3. 差分を確認(改行コードのみがLFに修正されたファイルがステージされる) git status # 4. コミットしてリモートへプッシュ git commit -m "chore: renormalize line endings using .gitattributes" git push origin main
⑧ 4大AIエージェントへの指示プロンプト例
「Windows環境でC#(.NET 8)Web APIを開発しており、Dockerコンテナ(Linux)との間で改行コードCRLF事故やスクリプト実行権限エラー(Permission denied)を根本防止したいです。リポジトリルートに配置すべき最適な.gitattributesファイルを作成し、既存のscriptsフォルダ内の*.shファイルに対してgit update-index --chmod=+x を適用して一括コミットする手順を提示してください。」
「VS Code右下のステータスバーで『CRLF ➡ LF』に変更して満足してしまう罠」
VS Codeの画面下部で個別ファイルを手動でLFに切り替えても、他の開発メンバーがVisual Studioで開いて保存したり、Gitのブランチを切り替えた瞬間にCRLFへ戻ってしまうことがよくあります。
個人のエディタ操作という「努力目標」で改行コードを管理するのは絶対にやめましょう。「.gitattributes でGitリポジトリそのものにルールを埋め込む」ことが、チーム全員の時間を守る唯一のプロの流儀です!
- 改行コードCRLF(
)が混入すると、Linuxシバンが
/bin/shを探して「not found」で即死する。 .gitattributesをリポジトリルートに配置し、*.sh text eol=lfを指定することで、OSに関わらずLFを強制統制する。- Windowsからでも
git update-index --chmod=+xを実行することで、Linux実行権限(100755)をGit上で安全に管理できる。 - 既存リポジトリで改行コードが散乱している場合は
git add --renormalize .で一括クリーニングを行う。
いよいよ実践編突入!『最初のコンテナ起動(docker run hello-world から nginx まで)』〜Docker Hubからのイメージ取得とポートマッピングの仕組み〜
コメント
コメントを投稿