
AIに書かせたDockerfile|イメージ肥大と脆弱性の確認
「コンテナ化しておいて」と言われて、AIにDockerfileを書いてもらう。 貼り付けて docker build。通った。動いた。よし、これで一件落着——。
その流れ、すごく分かります。Dockerfileは書く機会がまばらで、毎回ちょっとずつ思い出しながら書く種類のファイルですよね。AIはそこをきれいに埋めてくれるので、正直ありがたい。
ただ、あとから「イメージがなぜか重い」「セキュリティ担当から指摘が来た」「ビルド時に渡した鍵がイメージに残っていた」と気づくことがあります。ビルドが通ることと、本番に置いていいことは、別の話なんですよね。
この記事は、AIに書いてもらったDockerfileを、そのまま本番に出す前に見ておきたい場所の話です。Dockerに詳しくなろう、という話ではありません。「重さ」と「安全」の2つの軸で、決まった場所を順に見る——それだけで、あとから効いてくる指摘のかなりの部分は先に潰せます。
結論:AIが書いたDockerfileは、「重さ」と「安全」の2軸で見ます。重さは、①ベースイメージが用途に対して大きすぎないか、②ビルド用の道具(コンパイラ・開発パッケージ)が最終イメージに残っていないか、③.dockerignoreが無くて.gitやnode_modulesごとコピーしていないか。安全は、④rootのまま動いていないか(USERの指定)、⑤ENV/ARGに鍵やパスワードを書いていないか、⑥ベースイメージのタグがlatestなどの動くタグのままになっていないか。仕上げに、イメージスキャン(docker scout cveや Trivy など)を1回かける。ここまでで、Dockerfileレビューで指摘されやすい所はだいたい拾えます。全部を今日やらなくて大丈夫です。まずはUSERと.dockerignoreの2つから見てみましょう。
何が起きているのか——AIは「通るDockerfile」を書くのが得意
AIが手を抜いている、という話ではありません。頼まれたこと(ビルドが通って動くこと)にきちんと応えている結果として、こうなりやすい、という話です。
- 「動く」ことが最優先になる:一番確実に動くのは、必要そうなものを全部入れた大きめのイメージです。だから
apt-get installに、実行には要らないビルド用パッケージやエディタまで並ぶことがあります。動作は問題ありません。ただ、本番に運ぶ荷物としては重い。 - 学習した時点の書き方が出てくる:Dockerfileは書き方の流行が変わってきた分野です。すでに非推奨になった命令(
MAINTAINERなど)や、いまは別のやり方が用意されている手順が、そのまま出てくることがあります。AIの知識が少し前で止まっている件は、AIの知識は少し前で止まる|古い情報で最新仕様を扱う注意点にもまとめています。 - 本番の前提までは知らない:どのユーザーで動かすのか、鍵をどう渡すのか、社内のスキャンで何が引っかかるのか——ここはこちらが伝えないと分からない情報です。伝えていなければ、AIは無難な既定値を選びます。既定値では、コンテナの中の処理はroot(管理者)として動きます。
- ビルドは通るので、気づく機会がない:重い・rootで動いている・鍵が残っている。どれも、ビルドもテストも普通に通ります。エラーで教えてくれないぶん、見に行かないと気づけません。
つまり、受け取ったあとに見る場所さえ決めておけばいい話です。次から、その場所を順にいきます。
①〜③ 重さを見る——「本番に運ぶ荷物」として見直す

イメージが重いと、配布も起動も遅くなり、余計なものが入っているぶん気にすべき脆弱性の数も増えます。見る場所は3つです。
① ベースイメージが用途に対して大きすぎないか FROM の行を見ます。フルセットのOSイメージが指定されていて、実際にはアプリを動かすだけ、ということがよくあります。多くの公式イメージには、必要最小限に絞った軽い版(-slim が付いたものなど)が用意されています。
- ただし「軽ければ軽いほど良い」ではありません。たとえば Alpine ベースは小さい一方、標準Cライブラリが一般的なものと違うため、ネイティブ拡張を使うライブラリで動かない・挙動が変わることがあります。シェルすら入らない最小構成(distroless系)は安全側に振れますが、コンテナの中に入って調べるのが難しくなります。
- 軽さと、いざという時の調べやすさはトレードオフです。「一番小さいものにする」ではなく、「このチームが運用できる範囲で、いちばん小さいもの」を選べば十分です。
② ビルド用の道具が最終イメージに残っていないか ここがいちばん効きます。コンパイラ、開発用ヘッダ、パッケージマネージャのキャッシュ——これらはビルドの間だけ必要で、動かすときには要りません。それでも、1段構成のDockerfileだと全部そのまま荷物に入ります。
対処はマルチステージビルドです。難しい仕組みではなく、「作業する部屋」と「出荷する箱」を分けるだけの話です。前半のステージでビルドし、後半のステージには成果物だけをコピーする。図の右側がこれにあたります。全部入りのまま運ばない、というだけで、荷物はかなり軽くなります。
- あわせて、
RUNの中で入れたものを同じRUNの中で片付けているかを見ます。ここは誤解しやすい所で、あとの行でrmしても、前の層に残ったファイルのぶんは軽くなりません。消すなら、入れたのと同じRUNの中で消す。apt-getなら、更新・インストール・キャッシュ削除を1つのRUNにまとめる形です。 - 「推奨パッケージまで一緒に入れない」指定(
--no-install-recommendsなど)も、地味ですが効きます。
③ .dockerignore があるか COPY . . の1行は、書いてあるとおりカレントディレクトリを丸ごと持っていきます。.git、node_modules、ローカルのログ、そして .env。重くなるだけでなく、入れるつもりのなかった秘密情報が同梱される入り口にもなります。
.dockerignore は、Dockerfileと同じ場所に置く除外リストです。AIにDockerfileを頼むと、Dockerfile単体は書いてくれても .dockerignore は付いてこないことがよくあります。無ければ、「このプロジェクト用の .dockerignore も書いて」と一言足せば出してくれます。設定ファイルまわりで秘密が混ざる話は、AIに設定ファイルを生成させる|秘密情報の混入を防ぐ手順にも整理しています。
④〜⑥ 安全を見る——実行ユーザー・秘密・タグ
④ rootのまま動いていないか 何も指定しなければ、コンテナの中の処理はroot として動きます。動かすぶんには一番楽なので、AIの出力にも USER の行が無いことが多いです。
- アプリを動かすだけなら、専用のユーザーを作って
USERで切り替えるのが基本形です。公式イメージによっては、最初から一般ユーザーが用意されているものもあります(Node.jsの公式イメージのnodeユーザーなど)。 - 切り替えると、書き込み先のディレクトリの持ち主や、1024番未満のポートを開けないといった制約に当たることがあります。そこで初めて「このアプリはどこに書き込んでいるのか」がはっきりするので、面倒に見えて、実は把握が進む作業です。
- ここは「必ず全部やる」ではなく、本番に出すイメージから順にで十分です。
⑤ ENV や ARG に鍵・パスワードを書いていないか 「ビルド時にトークンが要る」場面で、AIは素直に ARG TOKEN=... と書いてくれることがあります。動きます。ただ、ENV に書いた値はイメージの中に残り、ARG で渡した値もビルド履歴に記録されます。あとから消したつもりでも、履歴をたどれば見えてしまう形です。
- 実行時に必要な値は、イメージに焼き込まず、起動時の環境変数やシークレット管理の仕組みから渡します。
- ビルド時だけ必要な値(プライベートリポジトリのトークンなど)は、いまのDockerに用意されているビルド時シークレットの仕組み(
RUN --mount=type=secret)を使うと、イメージにも履歴にも残さずに渡せます。2026年8月時点では、標準のビルド機能(BuildKit)で使える書き方です。 - 見つけたときは、そのまま消して終わりにしないでください。すでにビルド・push 済みなら、その鍵は「外に出たもの」として扱い、鍵の再発行まで進めるのが安全です。責める話ではなく、単純に手順の問題です。
⑥ ベースイメージのタグが動くタグのままになっていないか FROM node:latest のような指定は、ビルドする日によって中身が変わります。「昨日は通ったのに今日は落ちる」の原因になりますし、セキュリティ担当から見ても「何が入っているか説明できない」状態になります。
- 最低限、具体的なバージョンを指定する(
node:22-slimのように)。 - 再現性をきっちり求めるなら、ダイジェスト(イメージの中身に対する固定の識別子)まで指定する手もあります。ただし、固定したままだと更新が止まるので、定期的に上げる運用とセットにしないと、逆に古いまま放置されます。ここは「固定すれば安心」ではなく、更新の担当と頻度まで決めて初めて意味がある部分です。
そのほか、レビューでよく引っかかる小さな所
大きな2軸から外れますが、指摘されやすいわりに直すのが一瞬の所をまとめておきます。
CMDの書き方:CMD npm startのような書き方(シェル形式)だと、アプリがプロセスの一番上に立たず、停止の合図(SIGTERM)が届かないことがあります。デプロイのたびに終了処理が走らない・強制終了になる、という形で出ます。CMD ["node", "server.js"]のような配列形式にしておくのが無難です。- 依存インストールとソースコピーの順番:
package.json(やrequirements.txt)を先にコピーして依存を入れ、そのあとでソース全体をコピーする順にすると、ソースを1文字直しただけで依存の再インストールが走る、という待ち時間を減らせます。速さの話なので急ぎませんが、毎日効きます。 - 開発用の設定が混ざっていないか:開発向けの環境変数、デバッグ用ポート、テスト用の認証スキップ。ローカル用のDockerfileをそのまま本番に転用すると起きやすい所です。
- 不要なパッケージが入っていないか:
curl、エディタ、ビルドツール。デバッグ用に入れたものが、そのまま本番の荷物になっていないか。
スキャンを1回かける——目視の取りこぼしを拾う
ここまで目で見たら、最後に機械にも1回見てもらいます。ベースイメージやライブラリに既知の脆弱性が報告されていないかは、手で追うより道具のほうが確実です。
- Docker に付属の Docker Scout(
docker scout cve <イメージ名>)や、広く使われている Trivy(trivy image <イメージ名>)などで、ローカルからすぐ試せます。 - ※ 昔よく使われた
docker scanは提供が終わっており、現在は Docker Scout がその位置にあります。AIがdocker scanを案内してくることがあるので、動かないときはコマンドが古い可能性を疑ってみてください(2026年8月時点)。 - 結果を見て、全部をゼロにしようとしなくて大丈夫です。ベースイメージを新しめの版に上げるだけで一気に減ることが多いので、まずはそこから。残ったものは「いつ・誰が見るか」を決めて、次に回して構いません。
- CI に組み込めるとさらに楽ですが、最初は手元で1回かけてみるだけでも十分に価値があります。
道具が拾ってくれるのは「既知の脆弱性」までで、設計や設定の穴は拾えません。両方が要るという意味で、AI生成コードのセキュリティ確認|見落としやすい穴の型も、必要なときにのぞいてみてください。
明日やること(3つだけ)
一気に全部は要りません。まずこの3つから。
USERの行があるか見る:無ければ、AIに「非rootユーザーで動くようにこのDockerfileを直して。書き込み先の権限も一緒に見て」と頼む。動かなくなった所が、そのアプリの書き込み先です。.dockerignoreがあるか見る:無ければ作る。最低限.git、node_modules(や同等のもの)、.env、ログを外す。これだけで荷物が軽くなり、秘密の同梱も減ります。docker scout cve(または Trivy)を1回かける:結果は眺めるだけでOK。ベースイメージのタグを具体的な版に上げると、たいてい素直に減ります。
この3つは、慣れれば合わせて30分ほどの作業です。あとから指摘されて直すことを思えば、ずっと軽い。 そして、直したDockerfileの意図をコメント1行で残しておくと、次に触る人(数か月後の自分を含む)が助かります。
AI生成Dockerfileのチェックリスト
本番用のイメージを作る前に、上から順に。全部に○が要るわけではなく、そのプロジェクトで気になる所だけで十分です。
重さ
-
FROMのベースイメージが、用途に対して大きすぎないか(軽い版を選べないか) - 軽さを取った結果、調査やネイティブ拡張で困らないか(トレードオフを分かって選んでいるか)
- ビルド用の道具(コンパイラ・開発パッケージ)が最終イメージに残っていないか
- マルチステージにして、最後のイメージには成果物だけを入れられないか
- 入れたものを、同じ
RUNの中で片付けているか(あとの行のrmは効かない) -
.dockerignoreがあり、.git・依存ディレクトリ・.env・ログを外しているか
安全
-
USERで非rootに切り替えているか -
ENV/ARGに鍵・トークン・パスワードを書いていないか - ビルド時だけ必要な秘密を、ビルド時シークレットの仕組みで渡せているか
- (書かれていた場合)その鍵の再発行まで手配したか
-
FROMのタグがlatestなどの動くタグになっていないか - ダイジェスト固定にした場合、更新の担当と頻度を決めてあるか
- イメージスキャンを1回かけ、結果に目を通したか
その他
-
CMD/ENTRYPOINTが配列形式で、停止の合図が届く形になっているか - 依存インストールとソースコピーの順番が、キャッシュの効く並びになっているか
- 開発用の設定(デバッグポート・認証スキップ等)が残っていないか
- 非推奨の命令や、いまは別のやり方がある手順が混ざっていないか
この記事のまとめ
AIが書くDockerfileは、出発点としてはとても優秀です。ゼロから思い出しながら書くより、ずっと速い。 そのうえで、受け取ったあとに見る場所を決めておく。重さは「ベースイメージ・ビルド道具・.dockerignore」、安全は「USER・秘密・タグ」。最後にスキャンを1回。それだけで、あとから来る指摘のかなりの部分は先回りできます。
全部を今日やらなくて大丈夫です。USER と .dockerignore の2つだけでも、荷物はずいぶんまともになります。

Dockerfileは、書く機会が少ないぶん「これで合っているのか」が分かりにくいファイルです。 それでも今日、USER の1行を足せたなら、あるいは .dockerignore を1つ置けたなら、それは半年後の自分と、次に触る誰かを確実に助けます。急いで全部を整える必要はありません。触るときに、ひとつずつで十分です。
よければ、こちらも
- AI生成コードのセキュリティ確認|見落としやすい穴の型
- AIに設定ファイルを生成させる|秘密情報の混入を防ぐ手順
- AIにコマンドを書かせて事故らない|破壊的コマンドを弾く確認の型
- AI生成コードのレビュー・検証チェックリスト|使う前に見る所