Python×Claudeで実現!Docker上のLinuxを自動構築するAIエージェントの第一歩

「ローカル環境にテスト用のLinuxサーバを立ち上げて、Webサーバを設定したい。でも毎回コマンドを調べて打つのは面倒……」

スポンサーリンク

この記事では、その定型作業をAI(Claude)に代わりにやってもらう「AIエージェント」を、Windows上のDocker Desktopで作ります。

いきなりAIを動かすのではなく、前半でまず自分の手で同じ作業をやってみて、後半でそれを自動化するという順番で進めます。「何を自動化するのか」が分かったうえで作れるので、AI初心者の方でも迷いにくい構成です。

この記事で作るもの
Pythonのプログラムから「nginxをインストールして、Webサーバとして動かして」とClaudeに頼むと、ClaudeがDockerコンテナ内のコマンドを自分で考えて実行し、最後にブラウザで「Welcome to nginx!」が表示されるところまで進めるエージェントです。

先に知っておきたい:費用が発生する場面

この記事の中でお金がかかるのは「Claude APIの利用」だけです。あとから驚かないよう、最初に整理しておきます。

項目費用補足
Python、VS Code無料
Docker Desktop個人利用などは無料一定規模以上の組織での業務利用は有償になる場合があります。会社のPCで使う場合は、公式のライセンス条件を確認してください
第2章の手動作業無料AIを使わないので、Claudeの契約も不要です
Claude APIキーの発行発行そのものは無料ただし、実際に使うには事前にクレジット(前払いの残高)の購入が必要です
第5〜6章のエージェント実行従量課金実行のたびにクレジットが減ります。試行回数が多いほど増えます
Claude Pro(月額)別料金APIの代わりにはなりません。チャット用の契約とAPIは別の商品で、請求も別です

第1章:準備するもの

準備物役割確認方法
Python 3.10以上エージェントのプログラムを動かすpython --version
Docker DesktopLinuxサーバ(コンテナ)の置き場所docker run hello-world
VS Code(任意)コードを書くエディタメモ帳でも代用できます
Claude APIキーAIを呼び出すための鍵第4章で準備します(ここから有料)

1-1. Pythonをインストールする

Python公式サイト(python.org)の「Downloads」からWindows用インストーラをダウンロードして起動します。

ここだけ重要:最初の画面の下にある「Add python.exe to PATH」に必ずチェックを入れてから「Install Now」を押します。「Use admin privileges when installing py.exe」は、チェックなしのままで構いません。

インストール後は、開いているコマンドプロンプトをすべて閉じて、新しく開き直してから確認します。

python --version

Python 3.x.x と表示されれば成功です。

インストーラーに「このインストーラーは将来提供終了」という趣旨の注意書きが出ることがありますが、今回の作業には影響しません。

「Python」としか表示されない場合

Windowsには、Pythonが入っていない状態で python と打つとMicrosoft Storeへ誘導する「ダミーの実行ファイル」が最初から入っています。本物をインストールしたのに直らない場合は、「設定」→「アプリ」→「詳細設定」→「アプリ実行エイリアス」で、python.exe と python3.exe(App Installerと書かれたもの)をオフにしてください。

1-2. 必要なライブラリを入れる

pip install anthropic docker

anthropic はClaudeを呼び出すための公式ライブラリ、docker はPythonからDockerを操作するためのライブラリです。最後に Successfully installed ... と出れば成功です。

1-3. Docker Desktopの動作確認

Docker Desktopを起動し(画面左下が緑色の「Engine running」になっている状態)、次を実行します。

docker run hello-world

Hello from Docker! と表示されれば準備完了です。

1-4. VS Code(任意)

コードを書きやすくなるので、VS Codeを入れておくのがおすすめです。インストール後、拡張機能の検索で「Python」(Microsoft提供)を入れておきます。ただし、メモ帳などでも agent.py は作れるので、必須ではありません。

第2章:まず手動でWebサーバを作ってみる

AIに任せる前に、同じ作業を自分の手で一度やってみます。ここで打つコマンドが、後でAIが代わりに打つコマンドです。この章は無料で、Claudeの契約もAPIキーも不要です。

2-1. Linuxコンテナを作る

docker run -d --name testbox -p 8080:80 ubuntu:24.04 sleep infinity
部分意味
-dバックグラウンドで動かす
--name testboxコンテナに「testbox」という名前を付ける
-p 8080:80PCの8080番ポートを、コンテナの80番ポートにつなぐ(ブラウザから見るための設定)
ubuntu:24.04使うLinuxのイメージ(Ubuntu 24.04)
sleep infinityコンテナがすぐ終了しないよう、何もせず待機させる

2-2. コンテナの中に入る

docker exec -it testbox bash

プロンプトが root@(英数字):/# に変われば、Linuxコンテナの中に入れています。以降のコマンドは、この中で実行します。

2-3. nginxをインストールして起動する

apt-get update
apt-get install -y nginx
nginx
  • apt-get update:パッケージ一覧を最新にします
  • apt-get install -y nginx:nginxを入れます。-y は確認の質問を省略するオプションです
  • nginx:nginxを起動します。成功すると何も表示されずにプロンプトに戻ります(これが正常です)
systemctlが使えない理由
通常のLinuxサーバでは systemctl start nginx のようにサービスを起動しますが、Dockerコンテナでは基本的にsystemdが動かないため、nginxを直接起動します。これも後でAIに教えておくポイントです。

2-4. ブラウザで確認する

Windows側のブラウザで http://localhost:8080 を開き、「Welcome to nginx!」が表示されれば成功です。

もし ERR_EMPTY_RESPONSE(このページは動作していません)と表示された場合は、ブラウザからコンテナまでは届いているものの、nginxがまだ起動していない状態です。コンテナ内で nginx を実行してから再読み込みしてください。

2-5. 片付ける

exit
docker rm -f testbox

exit でコンテナから出て、docker rm -f でコンテナを削除します。

第3章:何を自動化するのか

第2章でやった作業は、次の4ステップでした。

  1. コンテナを作る
  2. パッケージを入れる
  3. サービスを起動する
  4. 結果を見て、うまくいっているか確認する

AIエージェントは、この作業を人間の代わりに、結果を見て考えながら進めてくれる仕組みです。

手動(第2章)自動化(第5章以降)
コマンドを考えるあなたClaude
コマンドを実行するあなたが入力Pythonプログラムが実行(あなたの承認つき)
エラーを読んで次の手を考えるあなた(例:ERR_EMPTY_RESPONSEを見て「nginxが未起動」と判断)Claude(出力を読んで原因を判断)
最終確認あなたがブラウザで確認Claudeがcurlで確認+あなたもブラウザで確認

実行するコマンド自体は、手動のときと同じです。違うのは「誰が考えて、誰が打つか」だけです。

エージェントの仕組み

中身は、次の繰り返しです。

目標を伝える
   ↓
Claudeが「次に打つコマンド」を決める
   ↓
Pythonが(あなたの承認後に)コンテナ内で実行する
   ↓
実行結果(出力・エラー)をClaudeに返す
   ↓
Claudeが結果を読んで、次のコマンドを決める … を繰り返す
   ↓
Claudeが「完了」と判断したら終了

必要な部品は3つです。

  • 頭脳:Claude(API経由で呼び出す)
  • 手:コンテナ内でコマンドを実行する関数(これをClaudeに「ツール」として教える)
  • 繰り返し:結果を返して次の指示をもらうループ
AIの「できました」を信じすぎない
最後は必ず自分の目でブラウザを開いて確認します。第2章で手順を体験しておくと、AIの操作が正しいかを自分で判断できます。

第4章:APIキーの準備と課金のしくみ(ここから有料)

4-1. クレジットを購入する

Claude Console(platform.claude.com)にログインし、Billing(請求)の画面からクレジットを購入します。APIは前払いのクレジットが減っていく従量課金です。

費用についての注意
  • 最低購入額や試用クレジットの有無は変わることがあるので、実際の画面で確認してください
  • この記事の小さな実験であれば、少額のクレジットで足ります。まずは最小額から始めるのがおすすめです
  • 自動チャージはオフのままにしておくと安心です
  • Claude Pro(チャットの月額契約)に入っていても、APIのクレジットは付いてきません

4-2. APIキーを発行する

Consoleの「API keys」で「Create Key」を押し、名前を付けて発行します。表示されたキー(sk-ant- で始まる文字列)は、その場でコピーして安全な場所に保管してください。

4-3. Windowsの環境変数に登録する

コマンドプロンプトで、次を実行します。

setx ANTHROPIC_API_KEY "ここにコピーしたキーを貼り付け"

設定後は、コマンドプロンプトとVS Codeを一度すべて閉じて、開き直してください。開いたままの画面には反映されません。開き直したら、次で確認します。

echo %ANTHROPIC_API_KEY%

先頭が sk-ant- と表示されれば成功です。

APIキーの取り扱い
  • キーはパスワードと同じです。コードに直接書かない、SNSやチャットに貼らない、GitHubに上げない
  • キーを貼ったコマンドの画面を、スクリーンショットで共有しない
  • 漏れた可能性があれば、Consoleでそのキーを無効にして作り直す

第5章:エージェントのコードを書く

作業用のフォルダを作り、その中に agent.py というファイルを作成します。

mkdir C:\work\server-agent
cd C:\work\server-agent

次のコードを agent.py に貼り付けて保存してください。

import datetime
import sys

import anthropic
import docker

# Windowsのコンソールで表示できない文字があってもエラーで止まらないようにする
sys.stdout.reconfigure(errors="replace")

# ===== 設定 =====
MODEL = "claude-sonnet-5-5"      # 使用するモデル(廃止されることがあるので公式で最新を確認)
CONTAINER_NAME = "agent-web"     # AIが操作してよい唯一のコンテナ
MAX_STEPS = 20                   # AIの試行回数の上限(費用の暴走防止)
LOG_FILE = "agent.log"           # 実行したコマンドの記録先

GOAL = """\
Ubuntuコンテナ内にnginxをインストールし、80番ポートでWebサーバとして起動してください。
最後にcurlで http://localhost にアクセスして、応答を確認した結果を報告してください。
"""

SYSTEM = """\
あなたはLinuxサーバ構築の担当者です。run_commandツールで、Ubuntuコンテナ内のコマンドを実行できます。
- 対話が出ないよう、apt-get install -y を使い、DEBIAN_FRONTEND=noninteractive を指定する
- このコンテナにはcurlが入っていない。動作確認に使う場合は先にインストールする
- systemctlは使えない。サービスは nginx のように直接起動する
- フォアグラウンドで動き続けるコマンドは実行しない(バックグラウンドで起動するものだけ使う)
- 失敗したらエラーを読み、原因を述べてから再試行する。同じ失敗を3回繰り返したら方針を変える
- 完了したら、確認した結果を日本語で簡潔に報告する
"""

TOOLS = [
    {
        "name": "run_command",
        "description": "Ubuntuコンテナ内でbashコマンドを実行し、終了コードと出力を返す",
        "input_schema": {
            "type": "object",
            "properties": {"cmd": {"type": "string", "description": "実行するbashコマンド"}},
            "required": ["cmd"],
        },
    }
]

client = anthropic.Anthropic()  # 環境変数 ANTHROPIC_API_KEY を自動で読み込む
docker_client = docker.from_env()


def log(text):
    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(f"[{datetime.datetime.now():%Y-%m-%d %H:%M:%S}] {text}\n")


def create_container():
    """作業用コンテナを作る。同名のコンテナがあれば、消さずに止まる。"""
    try:
        docker_client.containers.get(CONTAINER_NAME)
    except docker.errors.NotFound:
        return docker_client.containers.run(
            "ubuntu:24.04",
            "sleep infinity",
            name=CONTAINER_NAME,
            ports={"80/tcp": 8080},  # PCの8080番 → コンテナの80番
            detach=True,
        )
    print(f"コンテナ {CONTAINER_NAME} がすでに存在します。")
    print(f"docker rm -f {CONTAINER_NAME} で削除してから、もう一度実行してください。")
    raise SystemExit(1)


def run_command(box, cmd):
    """人間の承認を得てから、コンテナ内でコマンドを実行する。"""
    print(f"\nAIが実行したいコマンド:\n  $ {cmd}")
    if input("実行しますか? [y/N]: ").strip().lower() != "y":
        log(f"REJECTED: {cmd}")
        return "ユーザーが実行を拒否しました。別の方法を提案するか、作業を終了してください。"

    result = box.exec_run(["bash", "-lc", cmd], demux=True)
    out, err = result.output
    out = (out or b"").decode("utf-8", errors="replace")
    err = (err or b"").decode("utf-8", errors="replace")
    text = f"exit_code={result.exit_code}\n[stdout]\n{out[-3000:]}\n[stderr]\n{err[-3000:]}"
    log(f"RUN: {cmd}\n  exit_code={result.exit_code}")
    return text


def main():
    try:
        docker_client.ping()
    except Exception:
        print("Dockerに接続できません。Docker Desktopが起動しているか確認してください。")
        raise SystemExit(1)

    box = create_container()
    print(f"コンテナ {CONTAINER_NAME} を起動しました。")
    messages = [{"role": "user", "content": GOAL}]

    for step in range(1, MAX_STEPS + 1):
        print(f"\n===== ステップ {step}/{MAX_STEPS} =====")
        response = client.messages.create(
            model=MODEL,
            max_tokens=2000,
            system=SYSTEM,
            tools=TOOLS,
            messages=messages,
        )
        messages.append({"role": "assistant", "content": response.content})

        for block in response.content:
            if block.type == "text":
                print(block.text)

        if response.stop_reason != "tool_use":
            break  # AIがコマンドを求めなくなった = 作業終了

        results = []
        for block in response.content:
            if block.type == "tool_use":
                results.append(
                    {
                        "type": "tool_result",
                        "tool_use_id": block.id,
                        "content": run_command(box, block.input["cmd"]),
                    }
                )
        messages.append({"role": "user", "content": results})
    else:
        print(f"\n上限の{MAX_STEPS}ステップに達したため、中断しました。")

    print("\n作業が終わりました。ブラウザで http://localhost:8080 を開いて確認してください。")
    print(f"片付けるときは: docker rm -f {CONTAINER_NAME}")


if __name__ == "__main__":
    main()

コードの読みどころ

部分役割
GOALClaudeへの依頼文。ここを書き換えれば、別の構成も試せます
SYSTEM作業の方針。第2章で気づいた「systemctlが使えない」「確認用にcurlが要る」といった知識を先に教えています
TOOLSClaudeに渡す「手」の説明。run_command という道具が使えることを伝えています
create_container()作業用コンテナをプログラム側で作ります。Claudeがコンテナを作ったり消したりすることはできません
run_command()実行前に [y/N] で承認を求め、実行結果(終了コード・出力・エラー)をClaudeに返します
for step in range(...)エージェントの繰り返し本体。MAX_STEPS で回数の上限を設けています

このコードに入れている安全対策

  • 操作できるコンテナは agent-web の1つだけ:PC上の他のコンテナには触れません
  • 同名のコンテナがあっても勝手に消さない:既存のものがあれば止まって、手動での削除を促します
  • 実行前に必ず人間が承認:y を入力したコマンドだけが実行されます
  • ステップ数の上限(20回):AIが迷走しても、費用が青天井になりません
  • 実行ログの保存:agent.log に、いつ何を実行したかが残ります
  • ホストのフォルダやDockerのソケットをコンテナにつながない:コンテナ内で作業が完結します
この設計は崩さないでください
/var/run/docker.sock をコンテナにつないだり、PCのフォルダを丸ごとマウントしたりすると、AIの操作がPC全体に及ぶ危険があります。

第6章:実行して確認する

Docker Desktopが起動していることを確認し、新しく開いたコマンドプロンプトで実行します。

cd C:\work\server-agent
python agent.py

実行中の流れ

Claudeが実行したいコマンドを提示するたびに、次のように承認を求められます。

AIが実行したいコマンド:
  $ (Claudeが選んだコマンド)
実行しますか? [y/N]:
  • 最初は1つずつ内容を読んで、納得できたら y を入力してください
  • 第2章で自分が打ったコマンド(apt-get update、apt-get install -y nginx、nginx)と、ほぼ同じものが出てくるはずです
  • 怪しいコマンドは、y 以外(そのままEnter)で拒否できます。拒否するとClaudeは別の方法を考えます

Claudeが毎回選ぶコマンドは、実行のたびに少し違うことがあります。何度か試して、コマンドの違いを見比べるのも勉強になります。

完了したら

  1. 画面にClaudeの報告(curlの結果など)が表示されます
  2. 自分の目で http://localhost:8080 をブラウザで開き、「Welcome to nginx!」を確認します
  3. 作業の記録は agent.log で確認できます
  4. 終わったら、片付けます
docker rm -f agent-web

第7章:つまずいたときは

症状原因と対処
APIキーが見つからない旨のエラーsetx のあと、コマンドプロンプトを開き直していない可能性があります。開き直して echo %ANTHROPIC_API_KEY% を確認してください
認証エラーキーのコピー漏れ・余分な空白がないか確認してください。必要ならキーを作り直します
クレジット残高が不足している旨のエラーConsoleの請求画面でクレジットを購入してください(第4章)
モデルが見つからない旨のエラーコード先頭の MODEL が廃止されている可能性があります。モデルは定期的に入れ替わるので、公式ドキュメントで最新のモデル名を確認して書き換えてください
Dockerに接続できないDocker Desktopを起動し、「Engine running」になってから再実行します
agent-web がすでに存在する前回のコンテナが残っています。docker rm -f agent-web で削除してください
8080番ポートが使用中他のアプリが使っています。コードの ports={"80/tcp": 8080} の8080を別の番号(例:8081)に変え、ブラウザのURLも合わせます
ブラウザに表示されない手動のときと同様に、nginxが起動していない可能性があります。docker exec agent-web nginx を試してください
止まらない・固まったキーボードで Ctrl + C を押すと中断できます
Claudeが同じ失敗を繰り返すMAX_STEPS で自動的に止まります。GOAL や SYSTEM に、気づいたことを追記して再実行します

まとめと次のステップ

この記事では、まず手動でnginxのWebサーバを作り、その同じ作業をClaudeに任せるエージェントを作りました。エージェントといっても、仕組みは「Claudeがコマンドを決める → 実行する → 結果を返す」の繰り返しにすぎません。

慣れてきたら、次のように発展させてみてください。

  1. 手順を残させる:GOAL に「最後に今回の手順を Dockerfile にまとめて」と追加すると、次回からはAIなしで同じサーバを再現できます
  2. 構成を増やす:自作のHTMLページを表示する → nginx+PostgreSQL(Docker Composeで2コンテナ)→ リバースプロキシ、といった順に難しくしていきます
  3. 安全装置を強める:危険なコマンド(rm -rf / など)を自動で拒否するフィルタを足す
Claude Proをご契約の方へ
プログラムを自作しなくても、Pro/Maxの契約で使えるコマンドライン型のコーディングエージェント「Claude Code」に、同じ作業を頼む方法もあります。ただし、Claude Codeはあなたのパソコン上で直接コマンドを実行するため、この記事のコードのような「専用コンテナ内に閉じる」設計にはなりません。実行の許可は1つずつ確認しながら、空のフォルダで試してください。また、ANTHROPIC_API_KEY が環境変数に設定されていると、Claude CodeはProの枠ではなくAPI課金で動く点にも注意が必要です。最新の仕様は、Anthropicの公式ドキュメントでご確認ください。

この記事のコードは、自分のPC上のテスト用コンテナだけを操作する前提で作っています。会社のサーバや本番環境に向ける場合は、権限や承認の設計を別途しっかり検討してください。

この記事が気に入ったら
いいね ! しよう

スポンサーリンク

おすすめ

error: Content is protected !!