
Docker
イベント
該当するコンテンツが見つかりませんでした
マガジン
技術ブログ
Claude Code は認証トークン・設定・会話履歴を、すべて ~/.claude の下(と、すぐ隣の ~/.claude.json )に置きます。Dev Container で使うときは、再ビルドのたびの再ログインを避けるために、このディレクトリをホストから丸ごとマウントするのが定番です。 この構成を複数のプロジェクトで使っていて、気になり始めたことがあります。 全部のコンテナが、ホストにある同じ ~/.claude を書き換え合っている 、ということです。 手元の ~/.claude.json の中身を数えると、こうなっていました。 $ python3 -c "import json,os; d=json.load(open(os.path.expanduser('~/.claude.json'))); \ print('projects:', len(d['projects']), '/ githubRepoPaths:', len(d['githubRepoPaths']), '/ numStartups:', d['numStartups'])" projects: 36 / githubRepoPaths: 13 / numStartups: 1278 36プロジェクト分の状態が1ファイルに入っています。 projects/ 配下の会話履歴も同じで、 いま作業しているリポジトリとは関係のないプロジェクトの履歴が、どのコンテナからも見えます 。逆方向も起きます。自分がいるコンテナの外で走った Claude Code の書き込みが、知らないうちに手元のファイルに反映されます。 先に正直に書いておくと、 この構成で壊れた経験は一度もありません 。直したかった理由は単純で、 コンテナに閉じたはずの作業が、コンテナの外と混ざっているのが気持ち悪い からです。コンテナに入れた意味が薄れます。 本記事では、 ~/.claude の中身を 更新頻度で分類 して、コンテナをまたいで共有し続けるものと、プロジェクトごとに独立させるものの線を引きます。そのうえで後半では、その線引きを Dev Container(docker-compose 版と素の devcontainer.json 版)で実装します。ディレクトリの中身そのものについては、同じブログに Claude Codeが作成する~/.claudeディレクトリの詳細解析 があります。 .credentials.json や projects/ が何をしているファイルなのかはそちらが詳しいので、役割から知りたい場合は先に読むと早いです。 この記事でわかること : ~/.claude の中身を更新頻度で2つに分ける線引き(共有し続けるもの / コンテナごとに分けるもの) .claude.json が既定ではどこにあるのか。なぜそれが「全コンテナの共用ノート」になるのか 履歴と設定をプロジェクトごとに独立させつつ、再ログインは不要に保つ実装(compose ベースと素の devcontainer.json の両方) named volume の所有者問題( chown を 非再帰 にする理由) 「稀更新なら共有してよい」の例外( plugins/ を共有すると壊れる理由) 検証環境 : ホストは Linux / WSL2 です。リポジトリも WSL 上に置きます。macOS は認証情報の保存先が異なり本構成がそのままは成立しません(末尾の「制約・前提」で扱います)。 向き / 不向き : 対象は ホストの ~/.claude.json まで bind している人 です。すでに volume だけで運用しているなら、ここで扱う「混ざる」現象は起きていません。なお切り替えても ホスト側の履歴が消えることはありません 。ホストにはそのまま残り、以降の会話がコンテナ側にだけ増えていきます。見えなくなるだけですが、 --resume でホスト側の過去を頻繁に遡る使い方とは正面からぶつかります(一度きりの持ち込み手順は後述します)。 ~/.claude は全コンテナの共用ノートになっている .claude.json は ~/.claude の中に無い まず位置関係から押さえます。共有すると混ざるファイルの主役は .claude.json ですが、 これは既定では ~/.claude の中にありません 。 ~/.claude.json という、 ~/.claude と並ぶ別のパスに置かれます。 $ ls -la ~/.claude.json ~/.claude/.claude.json -rw-r--r-- 1 vscode vscode 104786 Jul 30 15:27 /home/vscode/.claude.json ls: cannot access '/home/vscode/.claude/.claude.json': No such file or directory だから、以前書いた Claude Code×DevContainer 環境構築ガイド の設定も、この2つを別々に bind していました。 "mounts": [ "source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind,consistency=cached", "source=${localEnv:HOME}/.claude.json,target=/home/vscode/.claude.json,type=bind,consistency=cached" ] つまりプロジェクトの状態が全コンテナで混ざるのは、正確には 2行目のほうを共有しているから です。 ~/.claude だけを bind して ~/.claude.json を共有していない人は、混ざりの主役をまだ共有していません。 中身を更新頻度で並べ直す settings.json や skills/ のような user レベルのものは、そもそもどこで作業していても同じものを使う前提で用意されています。共有したいのは自明で、迷いません。 迷うのは .claude.json です。名前も中身も「設定ファイル」の顔をしていて、実際に user レベルの設定も入っています。役割で並べると共有側に落ちます。ところがこのファイルは起動や操作のたびに書き換わり、中身の大半はプロジェクトごとの状態です。 書き込み頻度で並べると、ここが状態側に落ちます。 中身 役割 書き込み頻度 .credentials.json 認証トークン 稀 (ログイン時・トークン更新時のみ) settings.json user レベル設定(権限の許可リストなど) 稀 (設定を変えたときだけ) CLAUDE.md user レベルのメモリ 稀 (書いたときだけ) skills/ user レベルの skill 稀 .claude.json (既定では ~/.claude.json ) 設定・プロジェクトの信頼状態など 高 (起動・操作のたびに書き換わる) projects/ ・ sessions/ ・ history.jsonl 会話履歴・プロンプト履歴・実行中セッションの検出 高 (操作のたびに書き換わる) この表で上下がきれいに分かれます。 コンテナをまたいで共有して嬉しいのは上の稀更新グループだけ です。認証トークンは共有したいから共有している。user レベルの設定・メモリ・skill も、どのプロジェクトで作業していても同じものを使いたい種類のものです。 混ざって困るのは下の高頻度グループ です。ここには2つの向きの困りかたがあります。 他のプロジェクトの状態が見えてくる : .claude.json の projects は全プロジェクト分の信頼状態を1ファイルに持ちます。 projects/ 配下の会話履歴も、いま開いているリポジトリと無関係なものまで並びます 自分のいるコンテナの外からの書き込みが入ってくる : 別のコンテナ(あるいはホスト)で走った Claude Code が、同じファイルを更新します。手元では何もしていないのに中身が変わります 再ログイン不要のために本当に欲しいのはトークン1つなのに、丸ごと共有はその巻き添えで、混ざって困るものまで全部共有してしまう。これが直したかった状態です。 この下側の分け方は、こちらで発明したものではありません。公式の claude project purge (v2.1.124 以降)は、プロジェクト1つぶんの状態を消すコマンドです。その削除対象が projects/ の会話ログ、 history.jsonl の該当行、そして ~/.claude.json のそのプロジェクトのエントリ—— 本記事が隔離するものとほぼ同じ です。Anthropic 自身も、この3つを「プロジェクト固有の状態」として数えています。ただし purge は溜まったものを後から消すコマンドなので、作業している最中に他プロジェクトの状態が見えることは変わりません。 それでも「丸ごと共有」が広まったのは、そのほうが手軽だから この分類を無視して丸ごと扱う方式が広く使われているのには理由があります。Dev Container で再ビルドのたびに再ログインしたくない、という要望に対して、 ~/.claude をまとめてコンテナに渡すのが一番手数が少ないからです。 以降は bind と volume を、この記事の軸で読み分けてください。 bind はホスト側の実体を指すので共有される 。 named volume はコンテナ側の実体なので共有されない 。この記事がやろうとしているのは、その2つを1つのディレクトリの中で使い分けることです。 定番の方式は、大きく2つに分かれます。 定番方式 何をするか 達成できること 抱える問題 丸ごと named volume(公式リファレンス実装がこれ) ~/.claude 全体を named volume で永続化する 再ビルドを跨いで設定・履歴・認証を保持でき、 コンテナごとに独立する ホストとログインを共有しない= コンテナ側で別途ログインが要る 丸ごと bind(ホスト共有系) ホストの ~/.claude をそのまま bind する ホストのログインをそのまま使え、 再ログインが不要 全コンテナが同じファイルを書き換え合う 前者は Anthropic 自身の リファレンス実装 が採っている形です。 "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume" 後者はさきほどの入門編で採ったものです。表を見ると、2つの方式は「再ログインの手間」と「コンテナごとの独立」をトレードオフにしています。どちらも捨てたくない、というのが本記事の出発点です。 なお公式のこの実装は、 CLAUDE_CONFIG_DIR にも同じ /home/node/.claude を指定しています。後述するとおり本記事も同じことをします。環境変数のリファレンスに項目が無い変数ですが、公式のリファレンス実装自身が使っている、という位置づけのものです。 分離設計:共有し続けるものと、プロジェクトごとに分けるもの ここからが本記事の主張の中心です。方針は 稀更新のものはホストと共有し続け、頻繁更新のものは named volume でコンテナごとに独立させる こと。前述の二択(丸ごと volume / 丸ごと bind)のどちらでもない、 第3の中間設計 です。 先に性格を断っておきます。これは 公式のリファレンス実装が示す形から外れた回避策 です。Docker がマウントを深さ順に並べ替えること、 CLAUDE_CONFIG_DIR が集約先を動かすこと——この2つの挙動に乗って成立しています。どちらも後で実物で確かめますが、公式が保証した組み合わせではないので、 どちらかが変われば追従が要る 前提で採ってください。 対象 扱い なぜ .credentials.json (トークン) ホストと bind 共有 再ログインを不要にしたい。更新は稀 settings.json ・user CLAUDE.md ・ skills/ ホストと bind 共有 稀更新。どのプロジェクトでも同じものを使いたい。分けるとホスト側で育てた許可設定や user 資産がコンテナで使えなくなる .claude.json ・ projects/ ・ sessions/ ・ history.jsonl named volume でコンテナごとに独立 頻繁に更新される。他プロジェクトの状態が見えず、外からの書き込みも入らない。volume は再ビルドで消えず永続する plugins/ 共有しない(コンテナごと) 稀更新だが例外。理由は「ハマり所」で述べます バイナリ( ~/.local/ ) コンテナ固有(共有しない) 再ビルド時に入れ直す。代償として再ビルドまで版が固定される volume 名をプロジェクトごとに変えるのが肝 です。同じ名前を使い回すと、せっかく volume にしてもプロジェクト間で中身を共有してしまいます。リポジトリ名を含めた名前(例: myrepo-claude-config )にしておけば、履歴と .claude.json はプロジェクトごとに完全に独立します。 この設計は次の2段構えで成立します。 1段目: CLAUDE_CONFIG_DIR で集約先を $HOME/.claude に固定する。 ポイントは、これが 既定と同じパスの明示指定 であることです。効果は「外にあった .claude.json が ~/.claude の中に入る」ことです。これで .claude.json が volume の内側に入り、 リビルドを跨いで残せるようになります 。指定しなくてもコンテナごとに独立はしますが、その場合は volume の外=リビルドで消える場所に書かれるので、独立と永続の両方を取るにはこの1段が要ります。認証情報についても、Linux / Windows では CLAUDE_CONFIG_DIR を設定すると .credentials.json がそのディレクトリ配下に置かれることが 認証ドキュメント に明記されています。 公式のリファレンス実装が現に使っている変数で、 .claude ディレクトリの解説 にも「これを設定すれば ~/.claude 配下のパスはそちらの下に移る」と書かれています。それでも環境変数の 公式リファレンス には項目として載っていません。 前者は後者へリンクを張っているのに、飛んだ先にその項目が無い 、という状態です(2026-08-02 時点)。ドキュメント化を求める Issue #33430 は not planned として close されました 。載せてほしいという要望も「この変数が効いていない」というバグ報告も上がっているのに項目は無い—— 動くから使われている 、という位置づけの変数です。 さらに、この公式の記述は ~/.claude 配下 のパスについてのものです。本記事が頼っているのは、その外にある ~/.claude.json を配下へ引き込む挙動のほうで、そちらはどの公式ドキュメントにも書かれていません(後述の「動作確認」で実物を見ます)。機能はしますが、リファレンスに裏付けられた公式仕様ではない点は承知の上で採ってください。 既定と同じパスを指定することには副産物もあります。 CLAUDE_CONFIG_DIR を尊重せず ~/.claude を見にいってしまう既知バグ( #4739 は /ide 連携のロックファイル、後継の #30538 は VS Code 拡張機能。後者は現在も open)がありますが、フォールバック先と指定先が同じディレクトリになるため、この一族のバグは実質的に無効化されます。 なおどちらも IDE 連携側の不具合で、CLI 本体の資格情報の集約には及びません。 2段目: 集約先を named volume にし、その上に稀更新のものだけを深いパスで bind して重ねる。 ディレクトリ全体を指す volume に対して、より深いパスを名指しした bind を重ねます。後者が前者の上に乗るので、ディレクトリ全体は volume(コンテナ固有)のまま、名指しした少数のファイルだけがホスト共有になります。 この重なりは 書く順番に依存しません 。わざと bind 4本を volume より先に書いたコンテナを起動して、中から実際のマウント順を見るとこうなります。 $ grep -i claude /proc/self/mountinfo | awk '{print $5}' /home/vscode/.claude /home/vscode/.claude/.credentials.json /home/vscode/.claude/settings.json /home/vscode/.claude/CLAUDE.md /home/vscode/.claude/skills 指定した順ではなく、 浅いものから順に 並び直っています。volume を先・後・真ん中に置いた3通りで試しましたが、どれも同じ並びになり、共有した4つの中身もホスト側のものが見えました。 並べ替えているのは Docker デーモンです(上の対照は docker run で直接組みましたが、compose も devcontainer.json も最後は同じデーモンを通ります)。moby の daemon/volumes.go に、target のパス区切りの数でマウントを整列する sortMounts があります。コメントは「マウントが他のマウントを覆い隠さないようにするため。たとえば /etc と /etc/resolv.conf をマウントするなら、 /etc/resolv.conf を先にマウントしてはならない」と、まさにこの用途を書いています。 書く順番は気にしなくて構いません。深さの関係さえ作れていれば狙った重なりになります。実際に重なったかどうかは、後述の「動作確認」で自分の環境で目視できます。 つまり「コンテナ固有の volume の中に、共有したいものだけホストへの窓を開ける」構成です。これで「再ログイン不要」と「履歴や状態がプロジェクトごとに独立する」を同時に満たせます。正確には、 導入時の1回だけはログインが必要 で、そこで書かれたトークンがホスト側に残るため2回目以降のリビルドで不要になります(実測は後述の「動作確認」)。 引き換えに、履歴の置き場所が弱くなる 得るものだけ書くのは不誠実なので、先に払うものを1つ出しておきます。 この設計は、会話履歴の耐久性を確実に下げます。 丸ごと bind の構成では、履歴はホストの ~/.claude/projects/ にありました。ホームディレクトリごとバックアップを取っていればその中に入りますし、コンテナを何回作り直しても残ります。本構成に移すと、履歴は named volume の中へ移ります。リビルドでは消えませんが、 ホームのバックアップ対象からは外れます 。そして docker volume prune や docker system prune -a --volumes で消えます。ディスクが逼迫したときに反射で叩くコマンドです。 「再ビルドで消えない」ことと「消えない」ことは別だ、と理解したうえで採ってください。履歴を資産として扱っているなら、volume を対象にしたバックアップを別途組む必要があります(本記事ではそこまで踏み込みません)。混ざらないことと引き換えに何を差し出すのかは、末尾の「制約・前提」にも一覧で置いています。 Dev Container で実装する ここからは、上の線引きを実際の設定ファイルに落とします。読者の環境がどちらでも動くように、compose ベースと素の devcontainer.json の両方の完成コードを載せます。 検証状況 : compose 版は別のリポジトリで運用中の構成です。素の devcontainer.json 版はこの記事を書いているリポジトリに適用し、リビルドを2回跨いだ実測を後述の「動作確認」に載せています(Claude Code v2.1.220 / WSL2)。 compose ベースの場合 workspace サービスに、環境変数・named volume・重ね bind を定義します。 services: workspace: environment: CLAUDE_CONFIG_DIR: /home/vscode/.claude DISABLE_AUTOUPDATER: "1" volumes: # 頻繁更新分(.claude.json / projects / sessions / history.jsonl)はコンテナ隔離 - type: volume source: claude_config target: /home/vscode/.claude # 稀更新分だけ、深いパスの bind を重ねてホスト共有する - type: bind source: ${HOME}/.claude/.credentials.json target: /home/vscode/.claude/.credentials.json - type: bind source: ${HOME}/.claude/settings.json target: /home/vscode/.claude/settings.json volumes: claude_config: CLAUDE_CONFIG_DIR は集約先を固定します。前述のとおり、外にある .claude.json を ~/.claude 配下に引き込むのが狙いです。 DISABLE_AUTOUPDATER: "1" はバージョンを決定的にします。バイナリは ~/.local/ (コンテナ固有)にあるため、更新は再ビルド時に入れ直す形になります。裏を返せば 再ビルドするまで古い版に留まる ということなので、更新を取り込みたいタイミングで再ビルドしてください。 named volume を /home/vscode/.claude にマウントし、設定・履歴・ .claude.json をコンテナに隔離して再ビルドを跨いで永続させます。 稀更新のファイルを同名パスへ重ね bind します。より深いパスの bind が volume の上に重なるため、そのファイルだけがホスト共有になります。 consistency: cached は付けていません。Docker Desktop for Mac(osxfs)時代のオプションで、 Linux では無視される ためです。既存の設定に残っている場合は機能上無害ですが、意味があるように見えるぶん紛らわしいので外しておくのが親切です。 環境変数をもう1つ入れるなら CLAUDE_CODE_DISABLE_AUTO_MEMORY: "1" も候補で、共有と隔離の線引きとは独立した話題になりますが、チームで使うときの考え方は Claude Code Auto Memory をチームでは使わない理由 に書いています。 素の devcontainer.json の場合 compose を使わない構成では、マウントは devcontainer.json の mounts 配列に書きます。Claude Code に関係する部分だけ抜き出すと次の形です。 { "remoteUser": "vscode", // ホストとコンテナで uid が食い違う場合に揃える。重ね bind したファイルの // 権限調整をこれに委ねるため、本構成では明示的に有効化しておく "updateRemoteUserUID": true, "remoteEnv": { // 既定の $HOME/.claude と同一パスを明示指定する(.claude.json を配下に引き込む) "CLAUDE_CONFIG_DIR": "/home/vscode/.claude" }, // bind のソースはコンテナ生成前にホスト側へ実体を作っておく(ホスト上で実行される) "initializeCommand": "mkdir -p ~/.claude/skills && touch -a ~/.claude/.credentials.json ~/.claude/CLAUDE.md && chmod 600 ~/.claude/.credentials.json && { [ -s ~/.claude/settings.json ] || echo '{}' > ~/.claude/settings.json; }", // 先頭の chown に -R を付けてはいけない(理由は後述) "postCreateCommand": "sudo chown vscode:vscode /home/vscode/.claude && curl -fsSL https://claude.ai/install.sh | bash", "mounts": [ // 頻繁更新分はコンテナ隔離。volume なのでリビルドで消えない。 // volume 名にはリポジトリ名を入れる(理由は直後の「3点目」を参照) "source=myrepo-claude-config,target=/home/vscode/.claude,type=volume", // 稀更新分だけ深いパスの bind を重ねる "source=${localEnv:HOME}/.claude/.credentials.json,target=/home/vscode/.claude/.credentials.json,type=bind", "source=${localEnv:HOME}/.claude/settings.json,target=/home/vscode/.claude/settings.json,type=bind", "source=${localEnv:HOME}/.claude/CLAUDE.md,target=/home/vscode/.claude/CLAUDE.md,type=bind", // ディレクトリでも同じように重ねられる "source=${localEnv:HOME}/.claude/skills,target=/home/vscode/.claude/skills,type=bind" ] } compose 版との違いは3点です。 マウントを書く場所 (各サービスの volumes: か、 devcontainer.json の mounts 配列か)、 HOME の変数記法 ( ${HOME} か ${localEnv:HOME} か)、そして volume 名の扱い 。前の2つは書き換えるだけですが、3点目は設計の肝に関わるので単独で説明します。 compose の top-level volumes: に書いた名前は、 compose プロジェクト名でプレフィックスされます 。 Compose Specification が既定動作として定めていて、例外は name: を明示したときだけです。だから claude_config という汎用名のままでも、プロジェクトごとに別の volume になります。 一方 devcontainer.json の mounts は compose を経由せず、Docker CLI のマウント構文へそのまま渡ります。 プレフィックスの仕組みが挟まらないので、書いた名前がそのまま volume 名になります 。これは後述の確認2で実際に見えます。手元の devcontainer.json には source=sios-claude-config と書いてあり、 findmnt が返す実体も /var/lib/docker/volumes/sios-claude-config/_data です。リポジトリ名は付いていません。 つまり汎用名のままだと全プロジェクトが同じ volume を掴み、前述の「プロジェクトごとに独立させる」が成立しなくなります。 ただ、名前を手で付け分けるのは規律に頼る運用です。忘れれば静かに壊れます。ここは変数で自動化できます。 // リポジトリのフォルダ名から導出する(読める名前になる) "source=claude-config-${localWorkspaceFolderBasename},target=/home/vscode/.claude,type=volume" ${localWorkspaceFolderBasename} は devcontainer 仕様の変数で、 json_reference が mounts での利用を認めています。 ただしこれは フォルダ名から導出するので、別の場所にある同名リポジトリとは衝突します 。 ~/work/app と ~/oss/app を両方 Dev Container で開いていれば、どちらも claude-config-app を掴みます。手で命名するよりは安全ですが、衝突が消えるわけではありません。 そこまで潰すなら ${devcontainerId} を使います。Anthropic 自身のリファレンス実装がこれです。上と同じ json_reference が「その dev container に固有で、リビルドを跨いで安定する識別子」と定めているので、同名フォルダでも衝突しません。代わりに名前が不透明になり、 docker volume ls で目視できなくなります。 同名リポジトリを複数開く可能性があるなら ${devcontainerId} 、読める名前を優先するならフォルダ名 、という選び方になります。 initializeCommand について補足します。ファイル bind はソースが存在しないと、Docker がそれを root 所有のディレクトリとして誤生成してしまいます。これを防ぐため、ホスト側に空ファイルを先に作ります。 initializeCommand はコンテナ生成前にホスト上で実行されるので、ここが適切な置き場所です。 3点、細かいが外せない注意があります。 chmod 600 を付ける 。公式ドキュメントは Linux の .credentials.json を “file mode 0600 ” と明記しています。 touch だけだと既定の umask で 644 のトークンファイルができあがるため、明示的に絞ります。 settings.json は空ファイルにしない 。 {} で初期化します。 .credentials.json は空でも “Not logged in” として扱われますが、 settings.json が空だと JSON パースに失敗しうるためです。 mkdir -p / touch -a は冪等 なので、すでにファイルを持っているメンバーの環境では何もしません。 postCreate での所有者修正とインストール コンテナ生成後は、所有者修正 → インストーラの順で実行します。 # 新規 named volume はマウントポイント(~/.claude)が root 所有で初期化される。 # 非再帰で「ルートだけ」を vscode へ。-R は使わない(重ね bind したホスト側の # 実体を巻き込むため。理由は後述) if [ -d "$HOME/.claude" ]; then echo "[postCreate] Fixing ownership of .claude volume root (non-recursive)" sudo chown vscode:vscode "$HOME/.claude" fi # 非ブロッキング: ネットワーク/プロキシ起因の失敗で postCreate 全体を止めない echo "[postCreate] Installing Claude Code (native installer)" if curl -fsSL https://claude.ai/install.sh | bash; then echo "[postCreate] Claude Code installed" else echo "[postCreate][WARN] Claude Code install failed (network/proxy?); continuing" fi echo "[postCreate] Completed" インストーラを if で包んでいるのは、企業プロキシなどで到達できなかったときに postCreate 全体を止めないためです。 既存の履歴を持ち込む この構成に移ると projects/ は空から始まります。ホストに残った過去の会話を引き継ぎたい場合は、手でコピーできます。ただし cp だけでは足りず、 ディレクトリ名の付け替えが要ります 。 projects/ 配下のディレクトリ名は、開いていたワークスペースのパスから機械的に導かれます。ホストで /home/ryu/product/blog/sios-tech-blog-with-claude を開いていたなら -home-ryu-product-blog-sios-tech-blog-with-claude 、コンテナで /workspaces/sios-tech-blog-with-claude を開けば -workspaces-sios-tech-blog-with-claude です。名前が違うので、そのままコピーしてもコンテナ側は「このプロジェクトの会話は無い」と言います。実際に名前を揃えずに入れてみたところ、 claude --resume は空のままでした。 ホスト側で ls ~/.claude/projects/ を見て自分のディレクトリ名を確かめてから、名前を付け替えつつ volume へ流し込みます。 # ホスト側で実行。<volume> は devcontainer.json に書いた volume 名 docker run --rm \ -v <volume>:/dst \ -v ~/.claude/projects/-home-ryu-product-blog-sios-tech-blog-with-claude:/src:ro \ alpine sh -c 'mkdir -p "/dst/projects/-workspaces-sios-tech-blog-with-claude" \ && cp -a /src/. "/dst/projects/-workspaces-sios-tech-blog-with-claude/" \ && chown -R 1000:1000 /dst/projects' chown の 1000 はコンテナ側 vscode の uid です。root 所有のまま置くと Claude Code が書き込めません。 必要なのはここまでで、 .jsonl の中身を書き換える必要はありません 。各行は cwd にホスト側の絶対パス( /home/ryu/... )を持ったままですし、 sessions-index.json の originalPath もホストのパスのままですが、コンテナ内の claude --resume はディレクトリ名だけを見てセッションを拾います。持ち込んだセッションを実際に開くと、リビルド前のやり取りがそのまま復元されて続きから会話できました。 ただし持ち込めるのは その時点の断面だけ です。これ以降ホスト側で増えた会話は入ってきません。あと、リポジトリによっては projects/ 配下が数百MBあるので(手元の1リポジトリで885MB)、流し込む前にサイズは見ておいてください。 ハマり所 ここからは実装上の落とし穴です。本構成を実際に組んだときに踏んだものだけを並べます。 named volume の初期所有者は root なので chown は非再帰で named volume を 新規作成 すると、マウントポイント( ~/.claude )は root(uid 0)所有 で初期化されます。このままだと vscode ユーザーが配下に書き込めず、設定の保存が失敗します。そのため postCreate で ~/.claude の所有者を vscode に直します。volume は再ビルドで消えないため、初回作成後はすでに vscode 所有となり、この処理は冪等です。 ここで重要なのが、 chown に -R (再帰)を付けてはいけない ことです。 この時点の ~/.claude は、新規 volume(空)に重ね bind したファイルが乗っているだけの状態です。root 所有なのはルートディレクトリ自身だけで、サブディレクトリは Claude 初回起動時に vscode 所有として作られます。ルートだけ直せば十分です。 -R を付けると、重ね bind したファイルまで再帰の対象に入ります。この bind 元は ホスト側のファイルそのもの(コンテナとホストで同じ実体を指しています) なので、コンテナ内での再帰 chown が ホスト側の所有権まで書き換えます 。ホストの uid が 1000 でない環境では、書き換えられた結果ホスト側の Claude Code がトークンを読めなくなります。共有するファイルを増やすほど、この事故の影響範囲は広がります。 非再帰なら共有ファイルには一切触れません。権限調整は initializeCommand (ホスト側で実行)と updateRemoteUserUID: true に委ねます。 plugins/ は稀更新でも共有できない 「稀更新なら共有してよい」の例外です。 plugins/ は更新頻度こそ低いのですが、 レジストリの中身がコンテナの絶対パスを持っています 。 known_marketplaces.json の installLocation installed_plugins.json の installPath / projectPath これらに /home/vscode/.claude/plugins/... や /workspaces/... といったコンテナ側のパスが記録されます。ここまでは実物を開いて確認した事実です。 ホストと共有すると、両者が互いに解決できないパスを見ることになります。片方が「存在しないパスを指している=壊れている」と判断してレジストリを自分のパスで上書きし、次はもう片方が同じことをする——というピンポンが起きるはずです。 ただしこれは記録されるパスから導いた推測で、実際に往復させて確かめたわけではありません 。確かめる価値より断つコストのほうが安いので、構成で切っています。 共有をやめた場合の復旧コストは /plugin install を1回やり直すだけです。共有した settings.json に marketplace の情報が残るので、そこから復元できます。 未導入メンバーへの配布では空ファイルを先に作る これはチームへ配布したときに実際に踏んだ罠です。ホストに ~/.claude/.credentials.json が無いと、Docker が bind ソースを root 所有のディレクトリとして誤生成 します。Claude Code をまだ使っていないメンバーの環境では bind ソースが存在せず、root 所有で生成された結果、 vscode から書き込めなくなって壊れました。 対応は前述の initializeCommand です。空の .credentials.json は “Not logged in” として扱われるためクラッシュせず、初回に claude /login するとトークンがこのファイルに書き込まれ、以降は永続します。 壊れてしまった状態からの復旧 すでに root 所有のディレクトリとして誤生成されている場合は、それを消してからやり直します。 # ホスト側で実行(root 所有ディレクトリになっているものを削除) sudo rm -rf ~/.claude/.credentials.json このとき注意したいのが、 initializeCommand の中で権限を直そうとして chown を書く場合です。ホスト上で sudo なしに実行されるため、root 所有のものに対しては失敗します。そして initializeCommand が失敗するとコンテナの作成自体が止まります ( json_reference はライフサイクルスクリプトの失敗について「後続は実行されない」とだけ書いており、ホスト側で走る initializeCommand の失敗時にどうなるかは明記していません。ここは仕様の裏を取れていない挙動です)。「壊れた」状態で検索して来た読者がまず踏むのはここなので、先にホスト側で消しておくのが確実です。 volume 側が壊れた場合は docker volume rm で作り直せますが、 中の履歴と設定も一緒に消えます (後述の制約を参照)。 動作確認 本構成の中核は、 CLAUDE_CONFIG_DIR による集約先の移動と、volume の上への bind の重なりです。どちらも設定ファイルを読んだだけでは効いたかどうか分かりません。組んだあとに確認してください。以下は本記事の素の devcontainer.json 版をこのリポジトリに適用し、リビルドを2回跨いだうえで、さらに3日そのまま使って取った実測です。 4本あります。 どれもコンテナの中でコマンド1つ なので、順に叩けば数分で終わります。1と2で設計の2段が効いたかを見て、3で看板(再ログイン不要)を、4でその結果として何が変わったかを確かめる流れです。 先に、記事の主張を正確な形に直しておきます。 「リビルドしても再ログイン不要」ではありません。導入時に1回だけログインが必要で、そのとき書かれたトークンがホスト側に残るため、2回目以降のリビルドで不要になります。 initializeCommand が作るのは0バイトの空ファイルなので、導入時点でホストにトークンを持っていなければ初回の1回は必ずログインが要ります。実際、1回目のリビルド直後はログインを求められました。ここを飛ばして読むと、導入直後に「効いていない」と誤解します。 1. .claude.json が ~/.claude の中に入ったか まず1段目からです。記事の冒頭で、既定では .claude.json が ~/.claude の 外 にあることを ls で見ました。同じコマンドを新構成のコンテナ内で叩くと、そっくり反転します。 $ ls -la ~/.claude.json ~/.claude/.claude.json ls: cannot access '/home/vscode/.claude.json': No such file or directory -rw------- 1 vscode vscode 42482 Aug 1 12:56 /home/vscode/.claude/.claude.json 外にあったはずのファイルが消え、 ~/.claude の中に現れています。 CLAUDE_CONFIG_DIR が効いた証拠がこれです。ここが反転していなければ、 .claude.json は volume の外に残っています。その場合、コンテナ固有ではあるものの リビルドで消える 場所に書かれているので、隔離できたように見えて永続しません。 2. bind が volume の上に乗っているか 2段目です。 findmnt を階層表示のまま .claude で絞ると、重なり方が1画面で見えます。 $ findmnt -o TARGET,SOURCE | grep -i claude ├─/home/vscode/.claude /dev/sdc[/var/lib/docker/volumes/sios-claude-config/_data] │ ├─/home/vscode/.claude/CLAUDE.md /dev/sdc[/home/ryu/.claude/CLAUDE.md] │ ├─/home/vscode/.claude/skills /dev/sdc[/home/ryu/.claude/skills] │ ├─/home/vscode/.claude/settings.json /dev/sdc[/home/ryu/.claude/settings.json] │ └─/home/vscode/.claude/.credentials.json /dev/sdc[/home/ryu/.claude/.credentials.json] ディレクトリ自体は volume( /var/lib/docker/volumes/... )を指し、その 内側にぶら下がった4つだけ がホストのパス( /home/ryu/.claude/... )を指しています。狙った重なり方です。共有すると決めたものが4つとも子として並んでいることも、ここで同時に確認できます。ぶら下がりが1つも出ないなら bind が効いておらず、以降の確認は通りません。 なお skills はファイルではなくディレクトリですが、扱いは同じです。volume 側の同名ディレクトリを丸ごと覆い隠して、ホストの中身が見えます。 出力はこのリポジトリの実測そのままなので、volume 名がサンプルの myrepo-claude-config ではなく sios-claude-config になっています。前述のとおりリポジトリ名を入れた名前にしているためで、読み替えてください。 ここまで出ていれば、トークンがホストと同じ実体であることも同時に決まります。 source がホストのパスを指している時点で、そのパスでは volume 側のコピーが隠れている からです。コンテナから読める中身は、ホストのファイル以外にありません。逆に .credentials.json の行だけホスト以外を指しているなら、 ${localEnv:HOME} の解決先がずれています(Windows のファイルシステム上でリポジトリを開いた場合に起こりえます。末尾の「制約・前提」も参照)。 3. 再ログインが要らないか claude auth status がログイン状態を JSON で返します。対話セッションを起こす前に確かめられるので、これが一番早い確認です。 $ claude auth status { "loggedIn": true, "authMethod": "claude.ai", "apiProvider": "firstParty", ... } ただし「ログイン画面が出なかった」だけでは、裏で新しくログインし直していないことの証明になりません。判別できるのは .credentials.json の中身です。 リフレッシュトークンの有効期限が変わっていなければ、新規ログインは起きていません 。新規ログインならリフレッシュトークンが再発行され、期限もそれに合わせて先へ動くためです。 この推論は片道でだけ使ってください。「動いていない → ログインしていない」は言えますが、逆の「動いた → ログインした」は言えません。トークン更新のたびにリフレッシュトークン自体を再発行する実装なら、ログインなしでもここは動きうるからです(手元の v2.1.220 では、後述のとおり更新をまたいでも動きませんでした)。 $ python3 -c "import json,os,datetime as dt; \ d=json.load(open(os.path.expanduser('~/.claude/.credentials.json')))['claudeAiOauth']; \ [print(k, dt.datetime.fromtimestamp(d[k]/1000).strftime('%Y-%m-%d %H:%M:%S')) \ for k in ('expiresAt','refreshTokenExpiresAt')]" expiresAt 2026-07-31 00:20:05 refreshTokenExpiresAt 2026-08-27 19:01:56 リビルドの前後と、さらに3日そのまま使ったあとで並べるとこうなりました。3日後の列は挙動を見極めるためにこちらで取った追試です。 読者が3日待つ必要はありません 。 項目 リビルド前(7/30) リビルド後(7/30) 3日後(8/1) inode 1396334 1396334 1396334 サイズ 509 509 509 mtime 07-30 16:20:05.958 07-30 16:20:05.958 08-01 11:21:35.319 expiresAt (アクセストークン) 2026-07-31 00:20:05 2026-07-31 00:20:05 2026-08-01 19:21:35 refreshTokenExpiresAt 2026-08-27 19:01:56 2026-08-27 19:01:56 2026-08-27 19:01:56 refreshTokenExpiresAt が動いていないので、2回目のリビルドで新規ログインは発生していません。 リビルドの直後だけを見ると、アクセストークンの期限(7/31 00:20)がまだ生きていたため mtime すら動いていません。1回目が書いたファイルをそのまま読んだだけです。3日後の列で、その先が見えます。アクセストークンは期限切れを迎えて更新され、 mtime と expiresAt は動きました。 それはログイン不要のまま起こる正常系 です。一方で refreshTokenExpiresAt は動いていません。判定に使うのがこちらでよい理由が、この1列で確かめられます。 同じ列がもう1つ示しているのが inode の不変 です。トークンが書き換わっても番号が変わっていない=Claude Code はこのファイルを その場で上書き しており、一時ファイルを作って rename で差し替えてはいません。単一ファイルの bind が切れる典型的な原因がこの rename なので、ここが動かないことは本構成が長期的に保つかどうかの目安になります(後述の制約も参照)。 4. 他のプロジェクトが見えなくなったか ここまでの3つは「設計が効いたか」を見てきました。最後は、効いた結果として 何が変わったか です。記事の冒頭で .claude.json を数えたのと同じコマンドを、新構成のコンテナ内で叩きます。 $ python3 -c "import json,os; d=json.load(open(os.path.expanduser('~/.claude/.claude.json'))); \ print('projects:', len(d['projects']), '/ githubRepoPaths:', len(d['githubRepoPaths']), '/ numStartups:', d['numStartups']); \ print('project keys:', list(d['projects'].keys()))" projects: 1 / githubRepoPaths: 1 / numStartups: 10 project keys: ['/workspaces/sios-tech-blog-with-claude'] 36プロジェクトが1つになりました 。しかもその1つは、いま開いているリポジトリそのものです。 numStartups も 1278 から 10 に落ちていて、このカウンタがコンテナの中だけで数え直されていることが分かります。他プロジェクトの信頼状態は、もうここからは見えません。 会話履歴も同じです。 $ ls ~/.claude/projects/ -workspaces-sios-tech-blog-with-claude ディレクトリが1つだけ。ここに無関係なリポジトリの名前が並んでいたら、 .claude.json か projects/ のどちらかが volume の外に残っています。逆に 中を覗いてリビルド前のセッションの .jsonl がそのまま読めれば 、隔離した側が消えずに永続していることも同時に確認できます。ここが空なら、意図した named volume ではなく匿名ボリュームやコンテナのレイヤに書いている可能性があります。 制約・前提 本構成を採る前に知っておくべき制約を整理します。 項目 内容 認証トークンの露出 .credentials.json はコンテナから読み書きできます。コンテナ内の任意のコード・MCP・エージェントがトークンを取得しうる、という共有の本質的なトレードオフです。許容できない場合は共有をやめ、初回都度ログインする構成(volume のみ・bind なし)にします volume を消すと設定・履歴も消える docker compose down -v / docker volume rm / docker volume prune を実行すると、隔離した .claude.json と会話履歴は失われます。ホームディレクトリごとバックアップしている場合も、隔離した分はその対象から外れます。「再ビルドで消えない」ことと「消せない」ことは別です 分けたことの代償 プロジェクトの信頼状態・ projects/ の会話履歴・ history.jsonl はコンテナ側で空から始まります。 --resume がホスト側の過去セッションを自動で拾うことはありません(ディレクトリ名を付け替えて volume へコピーすれば、一度きりの持ち込みはできます。「既存の履歴を持ち込む」を参照)。初回にプロジェクトの信頼プロンプトが出ます。**「混ざらない」の裏返しは「手を動かさないと入ってこない」**なので、過去の履歴を頻繁に遡る使い方をしている場合は、この構成は向きません user スコープの MCP サーバが引き継がれない claude mcp add --scope user で登録した MCP サーバは .mcp.json ではなく ~/.claude.json に書き込まれることが 公式ドキュメント に明記されています。このファイルを隔離するので、 user スコープで登録した MCP サーバはコンテナごとに登録し直し になります。リポジトリに置いた .mcp.json (プロジェクトスコープ)は bind の対象外なので影響を受けません。なお手元は user スコープの登録が0件で、この不便自体は踏んでいません(ドキュメントからの指摘です) 断てるのはホスト コンテナ経路だけ 混ざりを断てるのはホストと各コンテナのあいだです。同一コンテナ内で claude を複数起動すれば、それらは同じ volume 上の同じ .claude.json を共有します トークン更新をまたいだ長期挙動 単一ファイルの bind は、書き手が「一時ファイルに書いて rename で差し替える」方式を採ると実体の対応が切れることがあります。v2.1.220 の実測では、初回ログインでコンテナ内から書かれたトークンが inode を変えずにホストへ貫通し、次のリビルドで読み出せました。さらに3日後、アクセストークンの期限切れによる更新が実際に走ったあとも inode は同じままでした(=上書き方式)。ただしこれは実装の観察であって保証された仕様ではないので、版が上がったら上記「動作確認」で確かめ直してください ホスト前提 Linux / WSL2 で検証しています。macOS では認証情報が Keychain に保存され .credentials.json としてファイル化されないことが 認証ドキュメント に明記されているため、トークンの bind 共有は成立しません(コンテナ側で claude /login し、volume で以降永続させる形になります)。保存先はおそらくログインキーチェーン内のアプリケーションパスワード相当の項目だと思われますが、 macOS 実機では未検証 なので、場所の特定は各自で確認してください $HOME の食い違い Windows のファイルシステム上でリポジトリを開くと、 initializeCommand の $HOME と compose の ${HOME} がずれる恐れがあります。リポジトリは WSL 上に置いてください CLAUDE_CONFIG_DIR の位置づけ 機能しますが、環境変数の 公式リファレンス には項目として載っていません( 認証ドキュメント と .claude ディレクトリの解説 で挙動が言及されるのみ。後者はリファレンスへリンクを張っていますが、飛んだ先に項目はありません=2026-08-02 時点)。ドキュメント化を求める Issue #33430 は not planned で close されており、リファレンス化を待つ前提では組まないほうが安全です なお、認証情報をホストに保持してコンテナから参照する考え方そのものは、Claude Code に限った話ではありません。 Azure CLI と gh をチームで統一する構成 や Gemini CLI の認証共有 でも同じ発想を採っています。本記事はそこに「何を共有し、何をプロジェクトごとに分けるか」という線引きを一段持ち込んだ形です。 まとめ ~/.claude を丸ごとマウントすると、 全コンテナがホストの同じファイルを書き換え合う 状態になります。手元の .claude.json には36プロジェクト分の状態が入っていました。他プロジェクトの履歴が見え、コンテナの外からの書き込みが入ってくる。壊れなくても、コンテナに閉じたはずの作業が外と混ざっているのは気持ち悪いです。 ~/.claude の中身は 更新頻度の違うものの寄せ集め で、上下できれいに分かれます。共有したいのは稀にしか更新されないもの(認証トークン・user 設定・user メモリ・user skill)だけ。混ざって困るのは起動や操作のたびに書き換わる .claude.json と履歴です。しかも .claude.json は既定では ~/.claude の外にあるため、「丸ごと」で扱おうとすると取りこぼしやすい位置にいます。 頻繁更新分を named volume でコンテナごとに独立させ、稀更新分だけを bind 共有すれば、 履歴と状態をプロジェクトごとに分けたまま、再ログインの手間を導入時の1回で済ませられます (そこで書かれたトークンがホストに残るため、2回目以降のリビルドでは不要)。実測では、36プロジェクト分あった .claude.json の状態が いま開いているリポジトリ1つだけ になりました。volume 名はプロジェクトごとに変えてください。素の devcontainer.json の volume 名は compose と違ってプレフィックスされないので、ここを汎用名のままにすると分けたつもりで分かれません。 ただし タダではありません 。履歴はホストのホームから named volume へ移るので、ホームのバックアップから外れ、 docker volume prune で消えます。過去のセッションを頻繁に遡る使い方をしているなら、この構成は向きません。「混ざらない」の裏返しは「手を動かさないと入ってこない」です(導入時の持ち込みだけは、ディレクトリ名を付け替えて volume へコピーすれば通せます)。 「稀更新なら共有してよい」にも例外があります。 plugins/ はレジストリがコンテナの絶対パスを持つため、共有するとホストとコンテナで壊し合います。 これは公式のリファレンス実装が採る「丸ごと volume」でも、多くの記事がやる「丸ごと bind」でもない 中間設計 です。更新頻度で線を引くと、再ログインの手間とコンテナごとの独立を両方取れます。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post Claude Codeの~/.claude、どれを共有してどれを分ける? first appeared on SIOS Tech Lab .
はじめに こんにちは、セーフィーに新卒で入社し、サーバーサイドエンジニアをしている古谷です。 入社後、同期の坂上さんと一緒に VApp(Vulnerable Application) という、意図的に脆弱性を仕込んだ学習用Webアプリケーションを作りました。社内のセキュリティ学習を目的に始めたプロジェクトでしたが、作ってみると 「Webフレームワークを使っていても、脆弱な実装をしないように気をつけなければならない」 という、当たり前のようで意外と実感しにくいことを実装ベースで確かめられました。 この記事では、VAppを紹介しつつ、それを通して 自分の中でセキュリティとの向き合い方がど
はじめに! こんにちは!新卒1年目のGokuと竹下です! 新人研修の一環としてニフティ2026年度新卒入社の11名が AWS JumpStart2026に参加しました! この記事では2日間実施されたワークショップの内容と、 グループワークで作成したアーキテクチャ設計図を紹介します! AWS JumpStartとは? AWS初学者のエンジニアを対象とした実践的な研修プログラムです 2日間実施され座学やサービスの学習だけでなく、実際の要件に合わせたアーキテクチャの 検討、設計まで体験できるのが特徴です! プログラムの到達目標 この研修プログラムでは3つの到達目標があります! 1.一般的なリファレンスアーキテクチャの理解 2.AWSコアサービスの概要とその選定基準の理解 3.AWSのアーキテクチャ図を作成するまでの流れを知る 日程 6/4(木)~6/5(金)の2日間 研修内容 1日目 ・AWS体験ハンズオン 簡易的なWebサーバーの構築 ToDo管理アプリの作成 2日目 ・AWSのサービスの機能に関するクイズ ・アーキテクティング演習 1日目 午前 午前中はアーキテクティングのコツに関する解説や、以下の2つの構成で Webサーバーの構築を行いました! ・VPCを作成しその中にEC2インスタンスを構築する方法 ・VPCを用いずにAmplifyを利用する方法 午後 午後はチームでToDo管理アプリを動かすためのアーキテクチャの構築を行いました! 構成は以下の図の通りとなっております。 ユーザーがアクセスした際にApplication Load Balancer(アプリケーションロードバランサー)に通されその後、各プライベートサブネットに配置されたコンテナに振り分けられる構成となっております。いわゆる冗長化構成となっており、いずれかのコンテナがダウンした場合でもシステムを利用できる仕組みです。 2日目 2日目の朝は、クイズから始まりました。 出題されたのは「システムを運用していく上で〜〜という課題があり、それを解決するためにどのような構成が考えられますか?」といった形式の問題が中心で、このあとの課題に向けた、ちょうど良いウォーミングアップになりました。 ここで得た学びは、アーキテクチャの正解は一つとは限らないということです。 もちろん模範解答はあります。ですが、今回のクイズでは「これが絶対に正しい」「これが唯一の正解」といったアーキテクチャは存在しませんでした。コストや冗長性などの観点で、それぞれにメリット・デメリットがあるのだと学びました。 アーキテクチャ図を作成する 課題の内容 まず、このような課題が与えられました。 ECサイトをローンチ予定。 大々的なプロモーションを実施しており、半年後にはリリースする必要があるが、残念ながら既存のチームメンバーが 全員退職 してしまった。 AWSを中心に開発すること以外はまだ何も決まっていないが、時間がないので、皆さんには 本日中 にAWSでのアーキテクチャを考えていただきます! チームメンバーが全員退職……?? 今日中にAWSでのアーキテクチャを考える……??? そんな、なかなか衝撃的な設定からのスタートでした。 検討にあたっては、以下のような前提も補足として提示されました。 現状の構成(自由に変更可能) バックエンド:Java / Spring Boot フロントエンド:TypeScript / React データベース:MySQL ローカルはDockerで開発中だが、コンテナは必須ではない 必須機能 商品一覧・詳細ページ カート機能・購入機能 決済・在庫管理・配送システムは外部のSaaS APIを利用する アカウント管理機能 検討してほしい項目 機能要件 :検討した構成で、ECサイトの各要件を実現できるか? 可用性 :あるコンポーネントに障害が起きても、システムを継続できるか? スケーラビリティとパフォーマンス :今後ユーザーが10倍になったとき、同じような構成で捌けるか? 実践 4人チームで各自がアーキテクチャ図を作成し、途中でチーム内発表を挟みながら、良いと思った案を採用してブラッシュアップしていく流れでした。 最初は、そもそもAWSのどのリソースを使えばよいのか、そしてそれらをどうつなげればよいのかが、まったく見当もつきませんでした。 そこで一度AWSのことは脇に置き、「提示された要件で、ごく普通にシステムを組むとしたら何が必要だろう?」と考えるところから始めました。 Webサーバー、データベース、アプリケーションの実行環境……。こうして洗い出した要素を、AWSのリソースに置き換えていくイメージです。 とはいえ、AWSのリソース、とくにECS on Fargate、VPC、パブリック/プライベートサブネット、ゲートウェイ周辺については理解が浅く、厳密なアーキテクチャ図を1日で描き切るのは、さすがに難しかったというのが本音です。 最終的に、私(竹下)が提案したアーキテクチャが「スケールのしやすさや冗長性の面でメリットが大きい」とチームメンバーに評価してもらい、採用されました。 竹下チームが作成したアーキテクチャ図 主なリソースは以下の3つです。 AWS Amplify インフラを意識せずにWebアプリケーションをデプロイできる、フルマネージドなサービス GitHub等と連携し、Pushするだけで自動的にデプロイしてくれる機能などがあります Amazon ECS on Fargate サーバーの管理や構築をせずに、コンテナだけを動かせるサーバーレスの仕組み Amazon Aurora Serverless v2(MySQL) 0から数十万規模までのスケールアップに対応可能な、フルマネージドなDBサービス バックエンドはマルチAZ構成にして冗長性を確保し、片方のAZで問題が発生してもELBが自動的に振り分けることで、安定的に稼働できるようにしました。 データ分析には Amazon Aurora MySQL zero-ETL integration with Amazon Redshift を活用し、Auroraに負荷をかけずにデータをRedshiftへ反映し、QuickSightで可視化できるようにしました。 全体としては、マネージドサービスやサーバーレスを中心に据え、運用負荷を抑えながら、急激なアクセス増にも対応できるように、スケーラビリティとアベイラビリティの高い構成を意識しました。 感想 Goku 今回のイベントで、初めて本格的にAWSを触りました。 特に感心したのは、EC2インスタンスを起動してWebサーバーを構築するまでが、 10分程度で完了してしまう点です。 オンプレミス環境であれば、機器の準備からOSの導入、各種設定までかなりの時間を要する作業ですが、 それがマネジメントコンソール上の操作だけで完結します。 また、保有している機器を気にすることなく大規模な構成を組んだり、 用途に合わせた小さな構成を画面上だけで組んだりできる点も印象的で、 スピードが求められる現代のプロダクト開発に適したサービスだと感じました。 EC2やS3など、名前は聞いたことがあるものの実態がわからない状態からのスタートでしたが、 主要なサービスについては、どのサービスがどのような機能を持つのかを大まかに把握できるようになりました。 今後は資格試験の学習や実務を通じて、AWSに関する知見をさらに深めていきたいです。 竹下 1日目では、EC2でWebサーバーを構築する方法と、Amplifyで同様のWebサイトを公開する方法を体験するハンズオンを行いました。 EC2もAmplifyも最終的にWebサイトを公開できる点は同じですが、公開までの工数や設定項目が異なり、EC2(IaaS)とAmplify(PaaS)の違いを実際に手を動かして理解できました。Amplifyは数回クリックするだけでWebサイトを公開することができ、あまりの手軽さに拍子抜けしてしまいました。 2日目は、チーム内で「これで合っているのかな?」「要件を満たすにはどうすればいい?」と議論するフェーズ、ドキュメントを読みながら情報を集めるフェーズ、そして実際に図を書くフェーズを行き来しました。10分に一度くらいの頻度で情報収集に立ち返り、試行錯誤を重ねながら何度もブラッシュアップしていきました。 参加時点ではアーキテクチャ図を書いた経験もなく、AWSもほとんどわからない状態でしたが、提出する頃にはFargateがどのようなものかをある程度説明できるようになっていました。 さまざまなAWSリソースを知る大きなきっかけになったと思います。















