[{"data":1,"prerenderedAt":1339},["ShallowReactive",2],{"blog-ja-dockerfile-best-practices-2025":3,"blog-ja-dockerfile-best-practices-2025-alt":225},{"id":4,"title":5,"author":6,"body":7,"date":1322,"description":1323,"extension":1324,"image":197,"locale":1325,"meta":1326,"navigation":225,"path":1327,"seo":1328,"stem":1329,"tags":1330,"__hash__":1338},"blog\u002Fblog\u002Fja\u002Fdockerfile-best-practices-2025.md","Dockerfile ベストプラクティス 2025: 軽量・安全・高速なイメージ構築","Kubo Team",{"type":8,"value":9,"toc":1295},"minimark",[10,14,24,29,38,42,183,191,256,263,306,314,318,321,325,424,437,441,500,504,512,516,564,567,570,614,617,621,698,710,716,720,723,727,788,791,794,881,884,963,967,971,1000,1010,1013,1058,1061,1130,1138,1141,1165,1169,1184,1190,1194,1197,1272,1282,1291],[11,12,13],"p",{},"Dockerfile の書き方ひとつで、コンテナイメージのサイズは 10 倍、ビルド時間は 20 倍、脆弱性の数は 5 倍の差がつきます。「動けばいい」から「本番で使える」Dockerfile への進化は、2025 年の DevOps エンジニアにとって必須スキルです。",[11,15,16,23],{},[17,18,22],"a",{"href":19,"rel":20},"https:\u002F\u002Fkubo.hexabase.io\u002F",[21],"nofollow","Kubo"," では、Kubernetes 上で稼働する多数のコンテナワークロードを管理しており、Dockerfile の品質がクラスタ全体のパフォーマンスとセキュリティに直接影響します。本記事では、2025 年の最新知見に基づいた Dockerfile ベストプラクティスを体系的に解説します。",[25,26,28],"h2",{"id":27},"_1-ベースイメージの選択-軽量化の第一歩","1. ベースイメージの選択: 軽量化の第一歩",[11,30,31,32,37],{},"ベースイメージの選択は、最終イメージのサイズとセキュリティに最も大きく影響します。",[17,33,36],{"href":34,"rel":35},"https:\u002F\u002Fdocs.docker.com\u002Fbuild\u002Fbuilding\u002Fbest-practices\u002F",[21],"Docker 公式ドキュメント","では、信頼できるソースからのイメージを推奨しています。",[39,40,41],"h3",{"id":41},"ベースイメージ比較",[43,44,45,67],"table",{},[46,47,48],"thead",{},[49,50,51,55,58,61,64],"tr",{},[52,53,54],"th",{},"ベースイメージ",[52,56,57],{},"サイズ",[52,59,60],{},"CVE 数（目安）",[52,62,63],{},"シェル",[52,65,66],{},"用途",[68,69,70,91,109,128,147,165],"tbody",{},[49,71,72,79,82,85,88],{},[73,74,75],"td",{},[76,77,78],"code",{},"ubuntu:24.04",[73,80,81],{},"~77MB",[73,83,84],{},"30-50",[73,86,87],{},"bash",[73,89,90],{},"汎用（非推奨）",[49,92,93,98,101,104,106],{},[73,94,95],{},[76,96,97],{},"debian:bookworm-slim",[73,99,100],{},"~52MB",[73,102,103],{},"20-40",[73,105,87],{},[73,107,108],{},"Debian 系の軽量版",[49,110,111,116,119,122,125],{},[73,112,113],{},[76,114,115],{},"alpine:3.21",[73,117,118],{},"~5MB",[73,120,121],{},"5-10",[73,123,124],{},"sh (busybox)",[73,126,127],{},"軽量 + デバッグ可能",[49,129,130,135,138,141,144],{},[73,131,132],{},[76,133,134],{},"distroless\u002Fstatic",[73,136,137],{},"~2MB",[73,139,140],{},"0-2",[73,142,143],{},"なし",[73,145,146],{},"Go\u002FRust 静的バイナリ",[49,148,149,154,157,160,162],{},[73,150,151],{},[76,152,153],{},"distroless\u002Fbase",[73,155,156],{},"~20MB",[73,158,159],{},"2-5",[73,161,143],{},[73,163,164],{},"glibc 依存あり",[49,166,167,172,175,178,180],{},[73,168,169],{},[76,170,171],{},"scratch",[73,173,174],{},"0MB",[73,176,177],{},"0",[73,179,143],{},[73,181,182],{},"完全カスタム",[11,184,185,190],{},[17,186,189],{"href":187,"rel":188},"https:\u002F\u002Fgithub.com\u002FGoogleContainerTools\u002Fdistroless",[21],"Google の distroless イメージ","は、アプリケーションとそのランタイム依存のみを含み、シェルやパッケージマネージャが存在しないため攻撃対象領域を最小化します。",[192,193,198],"pre",{"className":194,"code":195,"language":196,"meta":197,"style":197},"language-dockerfile shiki shiki-themes tokyo-night","# 推奨: 用途に応じた最小イメージを選択\n# Go アプリケーション\nFROM gcr.io\u002Fdistroless\u002Fstatic-debian12\n\n# Node.js アプリケーション\nFROM gcr.io\u002Fdistroless\u002Fnodejs20-debian12\n\n# Python アプリケーション\nFROM python:3.12-slim\n","dockerfile","",[76,199,200,208,214,220,227,233,239,244,250],{"__ignoreMap":197},[201,202,205],"span",{"class":203,"line":204},"line",1,[201,206,207],{},"# 推奨: 用途に応じた最小イメージを選択\n",[201,209,211],{"class":203,"line":210},2,[201,212,213],{},"# Go アプリケーション\n",[201,215,217],{"class":203,"line":216},3,[201,218,219],{},"FROM gcr.io\u002Fdistroless\u002Fstatic-debian12\n",[201,221,223],{"class":203,"line":222},4,[201,224,226],{"emptyLinePlaceholder":225},true,"\n",[201,228,230],{"class":203,"line":229},5,[201,231,232],{},"# Node.js アプリケーション\n",[201,234,236],{"class":203,"line":235},6,[201,237,238],{},"FROM gcr.io\u002Fdistroless\u002Fnodejs20-debian12\n",[201,240,242],{"class":203,"line":241},7,[201,243,226],{"emptyLinePlaceholder":225},[201,245,247],{"class":203,"line":246},8,[201,248,249],{},"# Python アプリケーション\n",[201,251,253],{"class":203,"line":252},9,[201,254,255],{},"FROM python:3.12-slim\n",[11,257,258,262],{},[259,260,261],"strong",{},"バージョンピニング","は再現性のために必須です:",[192,264,266],{"className":194,"code":265,"language":196,"meta":197,"style":197},"# 悪い例: タグが変わる可能性\nFROM node:20\n\n# 良い例: 具体的なバージョン指定\nFROM node:20.18-alpine3.21\n\n# 最良: ダイジェストで完全固定\nFROM node:20.18-alpine3.21@sha256:abc123...\n",[76,267,268,273,278,282,287,292,296,301],{"__ignoreMap":197},[201,269,270],{"class":203,"line":204},[201,271,272],{},"# 悪い例: タグが変わる可能性\n",[201,274,275],{"class":203,"line":210},[201,276,277],{},"FROM node:20\n",[201,279,280],{"class":203,"line":216},[201,281,226],{"emptyLinePlaceholder":225},[201,283,284],{"class":203,"line":222},[201,285,286],{},"# 良い例: 具体的なバージョン指定\n",[201,288,289],{"class":203,"line":229},[201,290,291],{},"FROM node:20.18-alpine3.21\n",[201,293,294],{"class":203,"line":235},[201,295,226],{"emptyLinePlaceholder":225},[201,297,298],{"class":203,"line":241},[201,299,300],{},"# 最良: ダイジェストで完全固定\n",[201,302,303],{"class":203,"line":246},[201,304,305],{},"FROM node:20.18-alpine3.21@sha256:abc123...\n",[11,307,308,313],{},[17,309,312],{"href":310,"rel":311},"https:\u002F\u002Fwww.hexabase.com\u002Fproduct\u002Fcaptain-ai\u002F",[21],"Captain.AI"," は、プロジェクトの言語とフレームワークを分析し、最適なベースイメージを自動推薦します。",[25,315,317],{"id":316},"_2-レイヤー構造の最適化","2. レイヤー構造の最適化",[11,319,320],{},"Docker はレイヤーキャッシュを活用してビルドを高速化します。命令の順序を最適化することで、キャッシュヒット率を最大化できます。",[39,322,324],{"id":323},"命令の順序-変更頻度の低い順に","命令の順序: 変更頻度の低い順に",[192,326,328],{"className":194,"code":327,"language":196,"meta":197,"style":197},"# 1. ベースイメージ（ほぼ変更なし）\nFROM node:20-alpine AS builder\n\n# 2. システム依存のインストール（まれに変更）\nRUN apk add --no-cache python3 make g++\n\n# 3. 依存定義のコピー（パッケージ追加時のみ変更）\nWORKDIR \u002Fapp\nCOPY package.json package-lock.json .\u002F\n\n# 4. 依存のインストール（パッケージ追加時のみ再実行）\nRUN npm ci\n\n# 5. ソースコードのコピー（頻繁に変更）\nCOPY . .\n\n# 6. ビルド（ソースコード変更のたびに再実行）\nRUN npm run build\n",[76,329,330,335,340,344,349,354,358,363,368,373,378,384,390,395,401,407,412,418],{"__ignoreMap":197},[201,331,332],{"class":203,"line":204},[201,333,334],{},"# 1. ベースイメージ（ほぼ変更なし）\n",[201,336,337],{"class":203,"line":210},[201,338,339],{},"FROM node:20-alpine AS builder\n",[201,341,342],{"class":203,"line":216},[201,343,226],{"emptyLinePlaceholder":225},[201,345,346],{"class":203,"line":222},[201,347,348],{},"# 2. システム依存のインストール（まれに変更）\n",[201,350,351],{"class":203,"line":229},[201,352,353],{},"RUN apk add --no-cache python3 make g++\n",[201,355,356],{"class":203,"line":235},[201,357,226],{"emptyLinePlaceholder":225},[201,359,360],{"class":203,"line":241},[201,361,362],{},"# 3. 依存定義のコピー（パッケージ追加時のみ変更）\n",[201,364,365],{"class":203,"line":246},[201,366,367],{},"WORKDIR \u002Fapp\n",[201,369,370],{"class":203,"line":252},[201,371,372],{},"COPY package.json package-lock.json .\u002F\n",[201,374,376],{"class":203,"line":375},10,[201,377,226],{"emptyLinePlaceholder":225},[201,379,381],{"class":203,"line":380},11,[201,382,383],{},"# 4. 依存のインストール（パッケージ追加時のみ再実行）\n",[201,385,387],{"class":203,"line":386},12,[201,388,389],{},"RUN npm ci\n",[201,391,393],{"class":203,"line":392},13,[201,394,226],{"emptyLinePlaceholder":225},[201,396,398],{"class":203,"line":397},14,[201,399,400],{},"# 5. ソースコードのコピー（頻繁に変更）\n",[201,402,404],{"class":203,"line":403},15,[201,405,406],{},"COPY . .\n",[201,408,410],{"class":203,"line":409},16,[201,411,226],{"emptyLinePlaceholder":225},[201,413,415],{"class":203,"line":414},17,[201,416,417],{},"# 6. ビルド（ソースコード変更のたびに再実行）\n",[201,419,421],{"class":203,"line":420},18,[201,422,423],{},"RUN npm run build\n",[11,425,426,427,430,431,436],{},"この順序にすることで、ソースコード変更時の再ビルドは",[259,428,429],{},"ステップ 5-6 のみ","で完了し、依存のインストール（数分かかる場合がある）がキャッシュされます。",[17,432,435],{"href":433,"rel":434},"https:\u002F\u002Fblog.bytescrum.com\u002Fdockerfile-best-practices-2025-secure-fast-and-modern",[21],"ByteScrum のベストプラクティス記事","では「間違った順序 = 20 分のリビルド、正しい順序 = 20 秒のリビルド」と表現されています。",[39,438,440],{"id":439},"run-命令の結合","RUN 命令の結合",[192,442,444],{"className":194,"code":443,"language":196,"meta":197,"style":197},"# 悪い例: 3 つのレイヤーが作成され、中間ファイルが残る\nRUN apt-get update\nRUN apt-get install -y curl wget\nRUN rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n\n# 良い例: 1 つのレイヤーに統合、キャッシュも削除\nRUN apt-get update && \\\n    apt-get install -y --no-install-recommends \\\n      curl \\\n      wget \\\n    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[76,445,446,451,456,461,466,470,475,480,485,490,495],{"__ignoreMap":197},[201,447,448],{"class":203,"line":204},[201,449,450],{},"# 悪い例: 3 つのレイヤーが作成され、中間ファイルが残る\n",[201,452,453],{"class":203,"line":210},[201,454,455],{},"RUN apt-get update\n",[201,457,458],{"class":203,"line":216},[201,459,460],{},"RUN apt-get install -y curl wget\n",[201,462,463],{"class":203,"line":222},[201,464,465],{},"RUN rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[201,467,468],{"class":203,"line":229},[201,469,226],{"emptyLinePlaceholder":225},[201,471,472],{"class":203,"line":235},[201,473,474],{},"# 良い例: 1 つのレイヤーに統合、キャッシュも削除\n",[201,476,477],{"class":203,"line":241},[201,478,479],{},"RUN apt-get update && \\\n",[201,481,482],{"class":203,"line":246},[201,483,484],{},"    apt-get install -y --no-install-recommends \\\n",[201,486,487],{"class":203,"line":252},[201,488,489],{},"      curl \\\n",[201,491,492],{"class":203,"line":375},[201,493,494],{},"      wget \\\n",[201,496,497],{"class":203,"line":380},[201,498,499],{},"    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n",[25,501,503],{"id":502},"_3-セキュリティ強化の必須対策","3. セキュリティ強化の必須対策",[11,505,506,511],{},[17,507,510],{"href":508,"rel":509},"https:\u002F\u002Fwww.sysdig.com\u002Flearn-cloud-native\u002Fdockerfile-best-practices",[21],"Sysdig の調査","によると、本番コンテナの 76% が root で実行されており、これは重大なセキュリティリスクです。",[39,513,515],{"id":514},"非-root-ユーザーで実行","非 root ユーザーで実行",[192,517,519],{"className":194,"code":518,"language":196,"meta":197,"style":197},"# アプリケーション用ユーザーを作成\nRUN addgroup -g 10001 -S appgroup && \\\n    adduser -u 10001 -S appuser -G appgroup\n\n# 必要なディレクトリの所有権を設定\nCOPY --chown=appuser:appgroup . \u002Fapp\n\n# 非 root ユーザーに切り替え\nUSER appuser\n",[76,520,521,526,531,536,540,545,550,554,559],{"__ignoreMap":197},[201,522,523],{"class":203,"line":204},[201,524,525],{},"# アプリケーション用ユーザーを作成\n",[201,527,528],{"class":203,"line":210},[201,529,530],{},"RUN addgroup -g 10001 -S appgroup && \\\n",[201,532,533],{"class":203,"line":216},[201,534,535],{},"    adduser -u 10001 -S appuser -G appgroup\n",[201,537,538],{"class":203,"line":222},[201,539,226],{"emptyLinePlaceholder":225},[201,541,542],{"class":203,"line":229},[201,543,544],{},"# 必要なディレクトリの所有権を設定\n",[201,546,547],{"class":203,"line":235},[201,548,549],{},"COPY --chown=appuser:appgroup . \u002Fapp\n",[201,551,552],{"class":203,"line":241},[201,553,226],{"emptyLinePlaceholder":225},[201,555,556],{"class":203,"line":246},[201,557,558],{},"# 非 root ユーザーに切り替え\n",[201,560,561],{"class":203,"line":252},[201,562,563],{},"USER appuser\n",[11,565,566],{},"UID 10000 以上を使用することで、ホスト OS のシステムユーザーとの ID 衝突を回避します。",[39,568,569],{"id":569},"シークレット管理",[192,571,573],{"className":194,"code":572,"language":196,"meta":197,"style":197},"# 絶対にやってはいけない: イメージレイヤーにシークレットが残る\nCOPY .env \u002Fapp\u002F.env\nENV API_KEY=sk-secret-key\n\n# 正しい方法: BuildKit シークレットマウント\nRUN --mount=type=secret,id=api_key \\\n    export API_KEY=$(cat \u002Frun\u002Fsecrets\u002Fapi_key) && \\\n    .\u002Fsetup.sh\n",[76,574,575,580,585,590,594,599,604,609],{"__ignoreMap":197},[201,576,577],{"class":203,"line":204},[201,578,579],{},"# 絶対にやってはいけない: イメージレイヤーにシークレットが残る\n",[201,581,582],{"class":203,"line":210},[201,583,584],{},"COPY .env \u002Fapp\u002F.env\n",[201,586,587],{"class":203,"line":216},[201,588,589],{},"ENV API_KEY=sk-secret-key\n",[201,591,592],{"class":203,"line":222},[201,593,226],{"emptyLinePlaceholder":225},[201,595,596],{"class":203,"line":229},[201,597,598],{},"# 正しい方法: BuildKit シークレットマウント\n",[201,600,601],{"class":203,"line":235},[201,602,603],{},"RUN --mount=type=secret,id=api_key \\\n",[201,605,606],{"class":203,"line":241},[201,607,608],{},"    export API_KEY=$(cat \u002Frun\u002Fsecrets\u002Fapi_key) && \\\n",[201,610,611],{"class":203,"line":246},[201,612,613],{},"    .\u002Fsetup.sh\n",[11,615,616],{},"BuildKit のシークレットマウントは、シークレットをイメージレイヤーに残さずにビルド時のみ利用可能にします。",[39,618,620],{"id":619},"dockerignore-の活用",".dockerignore の活用",[192,622,626],{"className":623,"code":624,"language":625,"meta":197,"style":197},"language-gitignore shiki shiki-themes tokyo-night","# .dockerignore\n.git\n.gitignore\nnode_modules\n.env\n.env.*\n*.md\ndocker-compose*.yml\nDockerfile*\n.DS_Store\n__pycache__\n*.pyc\ncoverage\u002F\n.pytest_cache\u002F\n","gitignore",[76,627,628,633,638,643,648,653,658,663,668,673,678,683,688,693],{"__ignoreMap":197},[201,629,630],{"class":203,"line":204},[201,631,632],{},"# .dockerignore\n",[201,634,635],{"class":203,"line":210},[201,636,637],{},".git\n",[201,639,640],{"class":203,"line":216},[201,641,642],{},".gitignore\n",[201,644,645],{"class":203,"line":222},[201,646,647],{},"node_modules\n",[201,649,650],{"class":203,"line":229},[201,651,652],{},".env\n",[201,654,655],{"class":203,"line":235},[201,656,657],{},".env.*\n",[201,659,660],{"class":203,"line":241},[201,661,662],{},"*.md\n",[201,664,665],{"class":203,"line":246},[201,666,667],{},"docker-compose*.yml\n",[201,669,670],{"class":203,"line":252},[201,671,672],{},"Dockerfile*\n",[201,674,675],{"class":203,"line":375},[201,676,677],{},".DS_Store\n",[201,679,680],{"class":203,"line":380},[201,681,682],{},"__pycache__\n",[201,684,685],{"class":203,"line":386},[201,686,687],{},"*.pyc\n",[201,689,690],{"class":203,"line":392},[201,691,692],{},"coverage\u002F\n",[201,694,695],{"class":203,"line":397},[201,696,697],{},".pytest_cache\u002F\n",[11,699,700,703,704,709],{},[76,701,702],{},".dockerignore"," はビルドコンテキストのサイズを削減し、意図しないファイル（特にシークレット）のイメージへの混入を防ぎます。",[17,705,708],{"href":706,"rel":707},"https:\u002F\u002Fwww.geeksforgeeks.org\u002Fdevops\u002Fhow-to-use-dockerfile-best-practices-for-efficient-image-building\u002F",[21],"GeeksforGeeks の Dockerfile ガイド","でも必須対策として紹介されています。",[11,711,712,715],{},[17,713,22],{"href":19,"rel":714},[21]," のセキュリティポリシーでは、非 root 実行と .dockerignore の使用を標準として推奨しています。",[25,717,719],{"id":718},"_4-buildkit-の活用","4. BuildKit の活用",[11,721,722],{},"BuildKit は Docker の次世代ビルドエンジンで、並列ビルド、改善されたキャッシュ、セキュリティ機能を提供します。",[39,724,726],{"id":725},"buildkit-の有効化","BuildKit の有効化",[192,728,731],{"className":729,"code":730,"language":87,"meta":197,"style":197},"language-bash shiki shiki-themes tokyo-night","# 環境変数で有効化\nexport DOCKER_BUILDKIT=1\n\n# docker buildx を使用（推奨）\ndocker buildx build -t myapp:latest .\n",[76,732,733,739,757,761,766],{"__ignoreMap":197},[201,734,735],{"class":203,"line":204},[201,736,738],{"class":737},"sbD-w","# 環境変数で有効化\n",[201,740,741,745,749,753],{"class":203,"line":210},[201,742,744],{"class":743},"sN7LL","export",[201,746,748],{"class":747},"sE3pS"," DOCKER_BUILDKIT",[201,750,752],{"class":751},"sAklC","=",[201,754,756],{"class":755},"sOJ5S","1\n",[201,758,759],{"class":203,"line":216},[201,760,226],{"emptyLinePlaceholder":225},[201,762,763],{"class":203,"line":222},[201,764,765],{"class":737},"# docker buildx を使用（推奨）\n",[201,767,768,771,775,778,782,785],{"class":203,"line":229},[201,769,770],{"class":747},"docker",[201,772,774],{"class":773},"sPY7s"," buildx",[201,776,777],{"class":773}," build",[201,779,781],{"class":780},"sT800"," -t",[201,783,784],{"class":773}," myapp:latest",[201,786,787],{"class":773}," .\n",[39,789,790],{"id":790},"キャッシュマウント",[11,792,793],{},"依存パッケージのキャッシュをビルド間で共有し、再インストールを回避します:",[192,795,797],{"className":194,"code":796,"language":196,"meta":197,"style":197},"# pip のキャッシュを保持\nRUN --mount=type=cache,target=\u002Froot\u002F.cache\u002Fpip \\\n    pip install -r requirements.txt\n\n# npm のキャッシュを保持\nRUN --mount=type=cache,target=\u002Froot\u002F.npm \\\n    npm ci\n\n# Go モジュールのキャッシュを保持\nRUN --mount=type=cache,target=\u002Fgo\u002Fpkg\u002Fmod \\\n    --mount=type=cache,target=\u002Froot\u002F.cache\u002Fgo-build \\\n    go build -o \u002Fapp\u002Fserver .\n\n# apt のキャッシュを保持\nRUN --mount=type=cache,target=\u002Fvar\u002Fcache\u002Fapt \\\n    --mount=type=cache,target=\u002Fvar\u002Flib\u002Fapt \\\n    apt-get update && apt-get install -y curl\n",[76,798,799,804,809,814,818,823,828,833,837,842,847,852,857,861,866,871,876],{"__ignoreMap":197},[201,800,801],{"class":203,"line":204},[201,802,803],{},"# pip のキャッシュを保持\n",[201,805,806],{"class":203,"line":210},[201,807,808],{},"RUN --mount=type=cache,target=\u002Froot\u002F.cache\u002Fpip \\\n",[201,810,811],{"class":203,"line":216},[201,812,813],{},"    pip install -r requirements.txt\n",[201,815,816],{"class":203,"line":222},[201,817,226],{"emptyLinePlaceholder":225},[201,819,820],{"class":203,"line":229},[201,821,822],{},"# npm のキャッシュを保持\n",[201,824,825],{"class":203,"line":235},[201,826,827],{},"RUN --mount=type=cache,target=\u002Froot\u002F.npm \\\n",[201,829,830],{"class":203,"line":241},[201,831,832],{},"    npm ci\n",[201,834,835],{"class":203,"line":246},[201,836,226],{"emptyLinePlaceholder":225},[201,838,839],{"class":203,"line":252},[201,840,841],{},"# Go モジュールのキャッシュを保持\n",[201,843,844],{"class":203,"line":375},[201,845,846],{},"RUN --mount=type=cache,target=\u002Fgo\u002Fpkg\u002Fmod \\\n",[201,848,849],{"class":203,"line":380},[201,850,851],{},"    --mount=type=cache,target=\u002Froot\u002F.cache\u002Fgo-build \\\n",[201,853,854],{"class":203,"line":386},[201,855,856],{},"    go build -o \u002Fapp\u002Fserver .\n",[201,858,859],{"class":203,"line":392},[201,860,226],{"emptyLinePlaceholder":225},[201,862,863],{"class":203,"line":397},[201,864,865],{},"# apt のキャッシュを保持\n",[201,867,868],{"class":203,"line":403},[201,869,870],{},"RUN --mount=type=cache,target=\u002Fvar\u002Fcache\u002Fapt \\\n",[201,872,873],{"class":203,"line":409},[201,874,875],{},"    --mount=type=cache,target=\u002Fvar\u002Flib\u002Fapt \\\n",[201,877,878],{"class":203,"line":414},[201,879,880],{},"    apt-get update && apt-get install -y curl\n",[39,882,883],{"id":883},"ヒアドキュメント構文",[192,885,887],{"className":194,"code":886,"language":196,"meta":197,"style":197},"# 従来の方法（エスケープが煩雑）\nRUN echo \"server {\" > \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n    echo \"  listen 80;\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n    echo \"  location \u002F { proxy_pass http:\u002F\u002Fapp:8080; }\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n    echo \"}\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n\n# BuildKit のヒアドキュメント（読みやすい）\nCOPY \u003C\u003CEOF \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\nserver {\n    listen 80;\n    location \u002F {\n        proxy_pass http:\u002F\u002Fapp:8080;\n    }\n}\nEOF\n",[76,888,889,894,899,904,909,914,918,923,928,933,938,943,948,953,958],{"__ignoreMap":197},[201,890,891],{"class":203,"line":204},[201,892,893],{},"# 従来の方法（エスケープが煩雑）\n",[201,895,896],{"class":203,"line":210},[201,897,898],{},"RUN echo \"server {\" > \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n",[201,900,901],{"class":203,"line":216},[201,902,903],{},"    echo \"  listen 80;\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n",[201,905,906],{"class":203,"line":222},[201,907,908],{},"    echo \"  location \u002F { proxy_pass http:\u002F\u002Fapp:8080; }\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf && \\\n",[201,910,911],{"class":203,"line":229},[201,912,913],{},"    echo \"}\" >> \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n",[201,915,916],{"class":203,"line":235},[201,917,226],{"emptyLinePlaceholder":225},[201,919,920],{"class":203,"line":241},[201,921,922],{},"# BuildKit のヒアドキュメント（読みやすい）\n",[201,924,925],{"class":203,"line":246},[201,926,927],{},"COPY \u003C\u003CEOF \u002Fetc\u002Fnginx\u002Fconf.d\u002Fdefault.conf\n",[201,929,930],{"class":203,"line":252},[201,931,932],{},"server {\n",[201,934,935],{"class":203,"line":375},[201,936,937],{},"    listen 80;\n",[201,939,940],{"class":203,"line":380},[201,941,942],{},"    location \u002F {\n",[201,944,945],{"class":203,"line":386},[201,946,947],{},"        proxy_pass http:\u002F\u002Fapp:8080;\n",[201,949,950],{"class":203,"line":392},[201,951,952],{},"    }\n",[201,954,955],{"class":203,"line":397},[201,956,957],{},"}\n",[201,959,960],{"class":203,"line":403},[201,961,962],{},"EOF\n",[25,964,966],{"id":965},"_5-イメージサイズ削減のテクニック集","5. イメージサイズ削減のテクニック集",[39,968,970],{"id":969},"copy-を-add-より優先","COPY を ADD より優先",[192,972,974],{"className":194,"code":973,"language":196,"meta":197,"style":197},"# 避ける: ADD はリモート URL ダウンロードと tar 自動展開を行う\nADD https:\u002F\u002Fexample.com\u002Fapp.tar.gz \u002Fapp\u002F\n\n# 推奨: COPY は明示的なローカルファイルコピーのみ\nCOPY app\u002F \u002Fapp\u002F\n",[76,975,976,981,986,990,995],{"__ignoreMap":197},[201,977,978],{"class":203,"line":204},[201,979,980],{},"# 避ける: ADD はリモート URL ダウンロードと tar 自動展開を行う\n",[201,982,983],{"class":203,"line":210},[201,984,985],{},"ADD https:\u002F\u002Fexample.com\u002Fapp.tar.gz \u002Fapp\u002F\n",[201,987,988],{"class":203,"line":216},[201,989,226],{"emptyLinePlaceholder":225},[201,991,992],{"class":203,"line":222},[201,993,994],{},"# 推奨: COPY は明示的なローカルファイルコピーのみ\n",[201,996,997],{"class":203,"line":229},[201,998,999],{},"COPY app\u002F \u002Fapp\u002F\n",[11,1001,1002,1005,1006,1009],{},[76,1003,1004],{},"ADD"," は予期しない動作を引き起こす可能性があるため、ローカルファイルのコピーには常に ",[76,1007,1008],{},"COPY"," を使用します。",[39,1011,1012],{"id":1012},"不要パッケージの排除",[192,1014,1016],{"className":194,"code":1015,"language":196,"meta":197,"style":197},"# --no-install-recommends で推奨パッケージを除外\nRUN apt-get update && \\\n    apt-get install -y --no-install-recommends \\\n      ca-certificates \\\n      curl \\\n    && rm -rf \u002Fvar\u002Flib\u002Fapt\u002Flists\u002F*\n\n# Alpine では --no-cache で apk キャッシュを無効化\nRUN apk add --no-cache curl\n",[76,1017,1018,1023,1027,1031,1036,1040,1044,1048,1053],{"__ignoreMap":197},[201,1019,1020],{"class":203,"line":204},[201,1021,1022],{},"# --no-install-recommends で推奨パッケージを除外\n",[201,1024,1025],{"class":203,"line":210},[201,1026,479],{},[201,1028,1029],{"class":203,"line":216},[201,1030,484],{},[201,1032,1033],{"class":203,"line":222},[201,1034,1035],{},"      ca-certificates \\\n",[201,1037,1038],{"class":203,"line":229},[201,1039,489],{},[201,1041,1042],{"class":203,"line":235},[201,1043,499],{},[201,1045,1046],{"class":203,"line":241},[201,1047,226],{"emptyLinePlaceholder":225},[201,1049,1050],{"class":203,"line":246},[201,1051,1052],{},"# Alpine では --no-cache で apk キャッシュを無効化\n",[201,1054,1055],{"class":203,"line":252},[201,1056,1057],{},"RUN apk add --no-cache curl\n",[39,1059,1060],{"id":1060},"マルチステージビルドの活用",[192,1062,1064],{"className":194,"code":1063,"language":196,"meta":197,"style":197},"FROM node:20-alpine AS builder\nWORKDIR \u002Fapp\nCOPY package*.json .\u002F\nRUN npm ci\nCOPY . .\nRUN npm run build && npm prune --production\n\nFROM node:20-alpine\nWORKDIR \u002Fapp\nCOPY --from=builder \u002Fapp\u002Fdist .\u002Fdist\nCOPY --from=builder \u002Fapp\u002Fnode_modules .\u002Fnode_modules\nCOPY --from=builder \u002Fapp\u002Fpackage.json .\u002F\nUSER node\nCMD [\"node\", \"dist\u002Findex.js\"]\n",[76,1065,1066,1070,1074,1079,1083,1087,1092,1096,1101,1105,1110,1115,1120,1125],{"__ignoreMap":197},[201,1067,1068],{"class":203,"line":204},[201,1069,339],{},[201,1071,1072],{"class":203,"line":210},[201,1073,367],{},[201,1075,1076],{"class":203,"line":216},[201,1077,1078],{},"COPY package*.json .\u002F\n",[201,1080,1081],{"class":203,"line":222},[201,1082,389],{},[201,1084,1085],{"class":203,"line":229},[201,1086,406],{},[201,1088,1089],{"class":203,"line":235},[201,1090,1091],{},"RUN npm run build && npm prune --production\n",[201,1093,1094],{"class":203,"line":241},[201,1095,226],{"emptyLinePlaceholder":225},[201,1097,1098],{"class":203,"line":246},[201,1099,1100],{},"FROM node:20-alpine\n",[201,1102,1103],{"class":203,"line":252},[201,1104,367],{},[201,1106,1107],{"class":203,"line":375},[201,1108,1109],{},"COPY --from=builder \u002Fapp\u002Fdist .\u002Fdist\n",[201,1111,1112],{"class":203,"line":380},[201,1113,1114],{},"COPY --from=builder \u002Fapp\u002Fnode_modules .\u002Fnode_modules\n",[201,1116,1117],{"class":203,"line":386},[201,1118,1119],{},"COPY --from=builder \u002Fapp\u002Fpackage.json .\u002F\n",[201,1121,1122],{"class":203,"line":392},[201,1123,1124],{},"USER node\n",[201,1126,1127],{"class":203,"line":397},[201,1128,1129],{},"CMD [\"node\", \"dist\u002Findex.js\"]\n",[11,1131,1132,1137],{},[17,1133,1136],{"href":1134,"rel":1135},"https:\u002F\u002Fwww.devopstraininginstitute.com\u002Fblog\u002F10-best-practices-for-docker-image-optimization",[21],"DevOps Training Institute の最適化ガイド","では、これらのテクニックを組み合わせることで 80% 以上のサイズ削減が達成可能と報告されています。",[39,1139,1140],{"id":1140},"定期的なイメージ再ビルド",[11,1142,1143,1144,1147,1148,1153,1154,1159,1160,1164],{},"本番イメージは",[259,1145,1146],{},"少なくとも月次","で再ビルドし、ベースイメージの最新セキュリティパッチを適用します。",[17,1149,1152],{"href":1150,"rel":1151},"https:\u002F\u002Fgithub.com\u002Fdependabot",[21],"Dependabot"," や ",[17,1155,1158],{"href":1156,"rel":1157},"https:\u002F\u002Fwww.mend.io\u002Frenovate\u002F",[21],"Renovate"," で自動化するのが ",[17,1161,1163],{"href":34,"rel":1162},[21],"Docker 公式","の推奨です。",[25,1166,1168],{"id":1167},"_6-ヘルスチェックの定義","6. ヘルスチェックの定義",[192,1170,1172],{"className":194,"code":1171,"language":196,"meta":197,"style":197},"HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n  CMD curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1\n",[76,1173,1174,1179],{"__ignoreMap":197},[201,1175,1176],{"class":203,"line":204},[201,1177,1178],{},"HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \\\n",[201,1180,1181],{"class":203,"line":210},[201,1182,1183],{},"  CMD curl -f http:\u002F\u002Flocalhost:8080\u002Fhealthz || exit 1\n",[11,1185,1186,1189],{},[76,1187,1188],{},"HEALTHCHECK"," 命令は Docker がコンテナの健全性を判断するために使用します。Kubernetes では liveness\u002Freadiness Probe に置き換えられますが、Docker Compose 環境では依然として有効です。",[25,1191,1193],{"id":1192},"まとめ-dockerfile-チェックリスト-2025","まとめ: Dockerfile チェックリスト 2025",[11,1195,1196],{},"本番品質の Dockerfile を書くための最終チェックリスト:",[1198,1199,1202,1212,1218,1224,1230,1236,1242,1248,1254,1260,1266],"ul",{"className":1200},[1201],"contains-task-list",[1203,1204,1207,1211],"li",{"className":1205},[1206],"task-list-item",[1208,1209],"input",{"disabled":225,"type":1210},"checkbox"," 最小限のベースイメージを選択（alpine - distroless - scratch）",[1203,1213,1215,1217],{"className":1214},[1206],[1208,1216],{"disabled":225,"type":1210}," バージョンピニングで再現性を確保",[1203,1219,1221,1223],{"className":1220},[1206],[1208,1222],{"disabled":225,"type":1210}," 変更頻度の低い命令を先に配置（キャッシュ最適化）",[1203,1225,1227,1229],{"className":1226},[1206],[1208,1228],{"disabled":225,"type":1210}," RUN 命令を結合し、キャッシュを削除",[1203,1231,1233,1235],{"className":1232},[1206],[1208,1234],{"disabled":225,"type":1210}," 非 root ユーザーで実行（UID 10000+）",[1203,1237,1239,1241],{"className":1238},[1206],[1208,1240],{"disabled":225,"type":1210}," .dockerignore でビルドコンテキストを最小化",[1203,1243,1245,1247],{"className":1244},[1206],[1208,1246],{"disabled":225,"type":1210}," シークレットを BuildKit マウントで管理",[1203,1249,1251,1253],{"className":1250},[1206],[1208,1252],{"disabled":225,"type":1210}," マルチステージビルドで本番と開発を分離",[1203,1255,1257,1259],{"className":1256},[1206],[1208,1258],{"disabled":225,"type":1210}," COPY を ADD より優先",[1203,1261,1263,1265],{"className":1262},[1206],[1208,1264],{"disabled":225,"type":1210}," HEALTHCHECK を定義",[1203,1267,1269,1271],{"className":1268},[1206],[1208,1270],{"disabled":225,"type":1210}," 定期的な再ビルドスケジュールを設定",[11,1273,1274,1277,1278,1281],{},[17,1275,22],{"href":19,"rel":1276},[21]," では、これらのベストプラクティスに準拠したコンテナイメージを Kubernetes クラスタ上で効率的に運用しています。",[17,1279,312],{"href":310,"rel":1280},[21]," を活用すれば、Dockerfile の自動レビューと最適化提案により、チーム全体の Dockerfile 品質を底上げできます。",[11,1283,1284,1285,1290],{},"Dockerfile の最適化やコンテナ運用のご相談は、",[17,1286,1289],{"href":1287,"rel":1288},"https:\u002F\u002Fwww.hexabase.com\u002Fcontact-us\u002F",[21],"お問い合わせ","よりお気軽にどうぞ。",[1292,1293,1294],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sbD-w, html code.shiki .sbD-w{--shiki-default:#51597D;--shiki-default-font-style:italic}html pre.shiki code .sN7LL, html code.shiki .sN7LL{--shiki-default:#9D7CD8;--shiki-default-font-style:italic}html pre.shiki code .sE3pS, html code.shiki .sE3pS{--shiki-default:#C0CAF5}html pre.shiki code .sAklC, html code.shiki .sAklC{--shiki-default:#89DDFF}html pre.shiki code .sOJ5S, html code.shiki .sOJ5S{--shiki-default:#FF9E64}html pre.shiki code .sPY7s, html code.shiki .sPY7s{--shiki-default:#9ECE6A}html pre.shiki code .sT800, html code.shiki .sT800{--shiki-default:#E0AF68}",{"title":197,"searchDepth":210,"depth":210,"links":1296},[1297,1300,1304,1309,1314,1320,1321],{"id":27,"depth":210,"text":28,"children":1298},[1299],{"id":41,"depth":216,"text":41},{"id":316,"depth":210,"text":317,"children":1301},[1302,1303],{"id":323,"depth":216,"text":324},{"id":439,"depth":216,"text":440},{"id":502,"depth":210,"text":503,"children":1305},[1306,1307,1308],{"id":514,"depth":216,"text":515},{"id":569,"depth":216,"text":569},{"id":619,"depth":216,"text":620},{"id":718,"depth":210,"text":719,"children":1310},[1311,1312,1313],{"id":725,"depth":216,"text":726},{"id":790,"depth":216,"text":790},{"id":883,"depth":216,"text":883},{"id":965,"depth":210,"text":966,"children":1315},[1316,1317,1318,1319],{"id":969,"depth":216,"text":970},{"id":1012,"depth":216,"text":1012},{"id":1060,"depth":216,"text":1060},{"id":1140,"depth":216,"text":1140},{"id":1167,"depth":210,"text":1168},{"id":1192,"depth":210,"text":1193},"2026-05-27","2025年最新の Dockerfile ベストプラクティスを徹底解説。軽量ベースイメージ選択、レイヤー最適化、セキュリティ対策、BuildKit 活用で本番品質のコンテナイメージを構築する方法。","md","ja",{},"\u002Fblog\u002Fja\u002Fdockerfile-best-practices-2025",{"title":5,"description":1323},"blog\u002Fja\u002Fdockerfile-best-practices-2025",[1331,1332,1333,1334,1335,1336,1337],"Dockerfile","Docker","ベストプラクティス","コンテナ","セキュリティ","BuildKit","最適化","lVkAwNQvsqF-_miHVG7Pf7Rr70Kj_kA_AKegeWDiu-Q",1779964617053]