はじめに
【内容】
社内で「AIツールを実体験してもらう」為の勉強会(講師+10名規模)を開催することに。
Azure上で一時的に仮想マシンを設定し、その中にClaudeCodeをはじめとした環境を事前に作成してみた。
その手順をスクリプト付きでまとめる。
【想定読者】
社内へのAI啓蒙を考えている方。
時間がない方向けの説明
- ClaudeCodeはコーディングだけではなく、手順書作成においても高い効果がある
- AIツールは「はじめてのこと」に関する諸々のハードルを取っ払う
- 「すごい」体験を皆で共有することが、今後重要になってきそう
前段
ぐだぐだと経緯を書いています。
手順だけ知りたい人はこの項を飛ばしてください――
勉強会の経緯
頃合い
AIツールによる生産性の向上が著しい。
実際に社内業務で1年程度体験し、その進歩に驚かされてきた。
AIチャット、AIブラウザから始まり、指示に従ってフロントエンドを自動生成してくれる「Bolt.new」は、2倍以上の生産性を達成しながら、
デザインセンスのなかった私にでも、見栄えの良いWebサイトを作ることに成功した。
また、5月下旬に登場したAIエージェント「ClaudeCode」は圧倒的な評価に恥じない指示解釈・コンテンツ生成能力を持ち、導入からひと月経たずして残留していたタスクを一掃した。
これは必ず、今後のスタンダードになる。そう確信した。
是非とも広めたい。いや、広めなければならない。
ちょうど他部署から「AIに関する勉強会」を依頼されており、渡りに船の状態であった。
どう伝えるか
勉強会を計画するとなった時に、一番に決めたのは「参加者全員にAIツールを実際に使ってもらう」ということだった。
言葉や映像で伝えても、おそらく伝わらない。
という感覚があったため、元より依頼されていた勉強会の中で、実際にAIエージェントを使ってもらうことにした。
ClaudeCodeにJavaの初歩的な買い物システムを作成してもらって、その内容をGitHubにアップロードした(指示一つでサラッと出来る時代に驚かされる……)。
勉強会ではこのシステムにAIツールを適用して、様々な機能追加や、設計書作成を実施してもらうことにしよう――
問題は、参加者に極力負荷をかけずに、AIツールを反映させた環境を用意するか、である。
参加人数は10名と聞いている。
貸し出し用の社用端末にそれぞれセットアップするか?
半日の勉強会のために、端末ごとに様々なライブラリやソフトウェアを入れ、APIキーなどを紐づける?
終わったら、一人で全部消す?
……流石にやってられない。
GitHub Codespaces
AIチャットの力を借りた結果、候補として出てきたのは「GitHub Codespaces」であった。
イメージとしては「Githubリポジトリを参照できる仮想マシンをブラウザ上に生成し、純正VSCodeで操作が出来る」といった具合である。
セットアップが非常に簡単で、無料の範囲でも十分過ぎる時間(120時間のコア時間)使うことが出来、純正のVSCode基準であるため、GitHub Copilot(Agent Mode)も利用可能。
.devcontainer.jsonを作り込むことで、様々な拡張機能を生成時に取り込むことが出来る。
一般的にはこちらが推奨されている……のだが、今回は見送ることになった。
その理由は単純で
参加者各々がGitHubアカウントを作らないといけない
という点にある。
当日「作ってませんでした」と言われた場合、ロスは必至。
なるべく負担をかけない。不確定要素を最低限に抑える。
すべてはAI啓蒙のため……!
Azureを使う
Microsoft Azureは、新規登録から30日間、200ドル分のクレジットが無料で使える――
私が乗らない理由はなかった。使ったことないけど。
サクッと登録(無料期間中であっても、クレジットカード情報の紐づけが必要な点に注意)を行い、毎度の如くAIチャットに訊いてみると、
「Azureで勉強会やるなら、Azure Lab Servicesが良いですよ」とのこと。
嬉々として紹介ページに飛んでみると、まさにやりたいことがそのまま書いてあるではないか!
完璧だ……!
ただ一つ、「終了予定サービスなので、新規登録者は受け付けてない」ということに目をつぶればだが……!
しかし、AIチャットを責めるのは間違っている。最先端であろうと、時点が古い情報を引っ張ってくるなんてことはザラにある……
「やっぱ、GitHub Codespacesにしましょうよ~」と返してくるチャット君。
色々言い合っているうちにこちらも疲れてしまい、ため息をひとつ。
この調子では先が思いやられる。
今でも上手くいってないのに、実践に環境を作る段階になったら、会話が通じない苛立ちに加えて、
実際にやってみてダメで手戻り、別の提案を取り込んでダメで手戻り……なんてことになりかねない。
その挙句に「本当にごめんなさい! やっぱ無理だよぅ、GitHub Codespacesで!」なんて言われたら、理性を保っている自信がない。
はあ、なんかうまいこと問題を整理して、ついでに手順書まで作ってくれないもんか……?
「ClaudeCode君、コーディングや設計書づくりが凄いのは分かったけど、こういうの出来るのかなあ……?」
……出来ちゃった。
手順(目安時間:1~2時間)
以下手順はClaudeCodeが90%以上自動で作成したものとなります。
環境で動作確認はしておりますが、もし想定外の挙動、問題のある操作などがありましたらお知らせいただけますと幸いです。
環境概要
- Azure上に仮想マシンを立てる
- VSCodeライクなエディタcode-serverを仮想マシン上でインストールして適用
- ログイン時パスワード機能あり
- 注意事項:あくまでVSCodeライクなものであり、GitHub Copilotをはじめ、純正VSCodeが想定された拡張機能を入れることは出来ない。事前にどの拡張機能が入れられるのか把握しておく必要がある
- GitHubにある勉強会用のリポジトリをCloneする
- JavaやPythonといった言語の環境をインストールする
- ClaudeCodeを組み込む(勉強会ではClaude4 Sonnetを利用)
- インスタンスを参加者数だけ複製(今回では講師+10名分)して起動する
- Https化対応をする
準備・コスト
- Azureにアカウントを作成する
- AnthropicでClaudeCodeを利用するための手続きを行う
- ターミナル上でログイン出来ることを確認
- 定額プランもあるが、今回は従量課金制を採用している
- 利用料金に注意!
- リハーサルの結果、講師+10名規模での2時間演習だと、以下程度のコストとなる
- Azureの事前環境セットアップ:500円程度(※無料クレジットで支払)
- 勉強会中のAzure仮想マシン(B4s)稼働(2時間):500円程度(※無料クレジットで支払)
- ClaudeCode(Claude 4 Sonnet)の利用費(2時間) :勉強会で取り扱うソース・ドキュメント類の規模に比例。初歩を教える程度の小規模であれば 5 * 11 = 55ドル(≒8300円)程度あれば十分
1. Azure仮想マシンの作成
1.1 Azure Portalでの作成
- Azure Portal にログイン
- 「仮想マシン」を選択(「仮想マシンスケールセット」ではない)
- 「作成」→ 「Azure仮想マシン」をクリック
1.2 基本設定
- サブスクリプション: デフォルト
-
リソースグループ: 「新規作成」→ 名前例:
study-group-rg
-
仮想マシン名:
ai-study-vm
-
地域:
(Asia Pacific) Japan East
- 自分の最寄のリージョン- 【20250710追記】 一般ユーザだとリージョンごとのvCPUが最大10しか利用できないことを確認(クォータ引き上げ申請も却下される)、大人数での開催が必要になる場合、「Japan West」など他のリージョンも併せて作成していく必要があります
- 可用性オプション: 可用性インフラストラクチャは必要ありません
- セキュリティの種類: Standard
-
イメージ:
Ubuntu Server 24.04 LTS - x64 Gen2
- VMアーキテクチャ: x64
-
サイズ:
- 10人以下:
Standard_B4ms
(4vcpu, 16GB RAM) - 20人対応:
Standard_B8ms
(8vcpu, 32GB RAM)
- 10人以下:
- 認証の種類: SSH公開キー
-
ユーザー名:
azureuser
- SSH公開キーのソース: 新しいキーの組の生成(RSA SSH形式) - OpenSSH形式を選択
-
キーの組名:
ai-study-vm-key
- 受信ポートの規則: 選択したポート許可する、SSH(22)
- 添付画像ではStandard_B4msを選択したケース。165.21ドルと出ているが、これは1ヶ月ずっと起動しっぱなしの場合の値段であり、勉強会で数時間使用する程度であれば、仮想マシンのコストは数ドル以下になる
- 設定するインスタンスの性能にも依存するが、そこまで大きな出費にはならない
1.3 ディスク設定
- OSディスクの種類: Standard SSD
- VMの削除時にディスクを削除する: ✅ チェック
1.4 ネットワーク設定
- 仮想ネットワーク: 新規作成(既定)⇒名前例:ai-study-vm-vnet
- サブネット: default(10.0.0.0/24)
-
パブリックIP:
ai-study-vm-ip
新規作成 - NICネットワークセキュリティグループ: Basic
- パブリック受信ポート: 選択したポートを許可する
- 受信ポートを選択: SSH (22)
- VMが削除されたときパブリックIPとNICを削除する: ✅ チェック
- 負荷分散オプション: なし
1.5 管理設定
-
勉強会直後にリソース削除する場合は対応不要だが、この設定をしておくことで仮に停止しそびれた場合でも、指定した時刻に停止することが出来る(削除は別途実行)
-
自動シャットダウンの有効化: ✅ チェック
- 時刻: 18:00(停止したい時刻を記入)
- タイムゾーン: (UTC+09:00) 大阪、札幌、東京
- シャットダウン前の通知、電子メール:任意。設定すると30分前に電子メールの宛先へ送信される
-
バックアップの有効化: ❌ チェックなし
1.6 確認および作成
「監視」についてはデフォルトのままでよい
- 「確認および作成」をクリック
- 検証に成功したら「作成」をクリック
- SSH警告について: 「SSH ポートを開くのはテストにのみ推奨」警告が表示されますが、勉強会用途では問題ないため無視してOK
1.7 秘密キーのダウンロード
- 「秘密キーのダウンロードとリソースの作成」をクリック
-
重要:
.pem
ファイルがダウンロード先に存在していることを確認 - デプロイ完了まで3-5分程度待機
1.8 パブリックIPアドレスの確認
- デプロイ完了後、「リソースに移動」をクリック
- 「概要」ページでパブリックIPアドレスをメモする
2. code-server用ポートの追加設定
2.1 ネットワークセキュリティグループの設定
- Azure Portal → 作成したVM → 「ネットワーク」タブ
- 「受信ポートの規則を追加」をクリック
- 以下の設定で追加:
- ソース、宛先: Any
-
ソースポート範囲:
*
-
サービス:
Custom
-
送信先ポート範囲:
8080-8090
- プロトコル: TCP
- アクション: 許可
- 優先度: 1010(例。優先度に応じて自由に設定)
-
名前:
Allow-CodeServer
- 「追加」をクリック
重要: この設定により、講師+各参加者のcode-serverインスタンス(ポート8080~8090)にインターネットからアクセス可能になります。
2.2 【注意】HTTPS化について
HTTPのままだとMarkdownプレビューが動作しないため、後続セクション4.4でHTTPS化を実行する必要があります。
重要な前提条件(今のうちに準備):
- Azure NSGでポート80と443を開放
- 2.1の作業について、サービスをそれぞれ「HTTP(80)」「HTTPS(443)」で指定すればよい
- 有効なメールアドレス(Let's Encrypt用)の準備をする(Gmailなど)
3. VMへの接続とセットアップ
3.1 SSH接続の準備
- 保存したファイルを「.ssh」フォルダ(C:\Users\(ユーザ名)\.ssh)配下に配置する
3.2 SSH接続
ssh -i ai-study-vm-key.pem azureuser@YOUR_VM_IP
注意: YOUR_VM_IP
は実際のパブリックIPアドレスに置き換えてください。
- 初回ログイン時は信頼してもよいか確認されるので、Yesを選択
- 仮想マシンに入れることを確認する
3.3 システム更新
sudo apt update && sudo apt upgrade -y
3.4 必要なパッケージのインストール
# 基本パッケージ
sudo apt install -y curl wget unzip git nodejs npm python3 python3-pip
# Java 21 + Maven/Gradle(Javaの例。講習に必要な定義を適宜追加してください)
sudo apt install -y openjdk-21-jdk maven gradle
# Java環境確認
java -version
mvn -version
gradle -version
# code-serverインストール
curl -fsSL https://code-server.dev/install.sh | sh
3.5 ClaudeCodeを入れる
インストール
- 素直に
npm install
で入れようとすると権限エラーになってしまうため、以下の手順を行う
# npm global権限の設定(sudoは使用しない)
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
# 再度インストール
npm install -g @anthropic-ai/claude-code
アカウント情報紐付け
4. 複数code-serverインスタンスのセットアップ
4.1 セットアップスクリプト作成
nano setup-multiple-codeserver.sh
スクリプト内容(setup-multiple-codeserver.sh):
#!/bin/bash
# setup-multiple-codeserver.sh (改良版)
echo "=== Code-server Multiple Instance Setup ==="
read -p "参加者数を入力してください: " USER_COUNT
# 既存環境のクリーンアップ確認
if [ -d "$HOME/workspace/user1" ]; then
echo "既存の環境が見つかりました。"
read -p "既存環境を削除して新規作成しますか? (y/N): " CLEANUP
if [[ $CLEANUP =~ ^[Yy]$ ]]; then
echo "既存環境をクリーンアップ中..."
# 既存のcode-serverプロセス停止
pkill -f "code-server.*user[0-9]"
# 既存ディレクトリ削除
rm -rf ~/workspace/user*
rm -rf ~/.config/code-server-user*
rm -rf ~/.local/share/code-server-user*
echo "クリーンアップ完了!"
else
echo "既存環境を保持して新規追加します。"
fi
fi
echo "=== $USER_COUNT 名用の環境を作成中 ==="
for i in $(seq 1 $USER_COUNT); do
PORT=$((8079 + i)) # 8080, 8081, 8082...
PASSWORD="study$(printf "%02d" $i)" # PWは自由に書き換える。例ではユーザごとにstudy01, study02...
# 各ユーザー用ディレクトリ作成
mkdir -p ~/workspace/user$i
# 設定ファイル作成
mkdir -p ~/.config/code-server-user$i
cat > ~/.config/code-server-user$i/config.yaml << EOF
bind-addr: 0.0.0.0:$PORT
auth: password
password: $PASSWORD
cert: false
user-data-dir: /home/azureuser/.local/share/code-server-user$i
EOF
echo "✅ User$i: http://[VM-IP]:$PORT (password: $PASSWORD)"
done
echo ""
echo "=== セットアップ完了 ==="
echo "次に start-all-codeserver.sh を実行してください。"
4.2 起動スクリプト作成
nano start-all-codeserver.sh
スクリプト内容(start-all-codeserver.sh):
#!/bin/bash
# start-all-codeserver.sh (改良版)
echo "=== Code-server 全インスタンス起動 ==="
# 既存のuser*ディレクトリから参加者数を自動判定
USER_COUNT=$(ls -d ~/workspace/user* 2>/dev/null | wc -l)
if [ $USER_COUNT -eq 0 ]; then
echo "❌ エラー: user環境が見つかりません。"
echo "先に setup-multiple-codeserver.sh を実行してください。"
exit 1
fi
echo "🔍 検出された参加者数: $USER_COUNT 名"
read -p "この設定で起動しますか? (Y/n): " CONFIRM
if [[ $CONFIRM =~ ^[Nn]$ ]]; then
echo "キャンセルしました。"
exit 0
fi
echo "🚀 $USER_COUNT 個のcode-serverインスタンスを起動中..."
for i in $(seq 1 $USER_COUNT); do
PORT=$((8079 + i))
WORKSPACE="/home/azureuser/workspace/user$i"
CONFIG="/home/azureuser/.config/code-server-user$i/config.yaml"
# 設定ファイルが存在するかチェック
if [ ! -f "$CONFIG" ]; then
echo "⚠️ 警告: $CONFIG が見つかりません。user$i をスキップします。"
continue
fi
# 既存プロセスがあるかチェック
if pgrep -f "code-server.*user$i" > /dev/null; then
echo "⚠️ user$i は既に起動中です。"
continue
fi
# バックグラウンドで起動
mkdir -p ~/.local/share/code-server-user$i # この行を追加
nohup code-server --config $CONFIG $WORKSPACE > ~/.local/share/code-server-user$i/startup.log 2>&1 &
echo "✅ Started code-server for user$i on port $PORT"
# 少し待機(同時起動負荷軽減)
sleep 1
done
echo ""
echo "🎉 起動完了!"
echo "📝 アクセス情報を確認するには: cat ~/access_info.txt"
4.3 実行手順
# 実行権限付与
chmod +x setup-multiple-codeserver.sh start-all-codeserver.sh
# セットアップ実行
./setup-multiple-codeserver.sh
# 参加者数入力(例:11)
# 起動実行
./start-all-codeserver.sh
4.4 HTTPS化の実装
複数code-serverの起動が完了したら、HTTPS化を実行してください。
HTTPのままだとMarkdownプレビューが動作しないなど、様々な支障が発生します。
実装方法: VM内でNginx + Let's Encrypt
- ドメイン設定不要(nip.ioを自動使用)
- 正式なSSL証明書で完全動作
- 1つのスクリプトで全自動化
実行手順
nano setup-https-parameterized.sh
スクリプト内容(setup-https-parameterized.sh):
#!/bin/bash
# setup-https-parameterized.sh - 参加者数可変対応版
echo "=== Azure code-server環境のHTTPS化(参加者数可変版) ==="
echo ""
# 参加者数の入力
echo "📊 参加者数設定"
echo "=============="
read -p "参加者数を入力してください(1-50): " USER_COUNT
# 入力値チェック
if ! [[ "$USER_COUNT" =~ ^[0-9]+$ ]] || [ "$USER_COUNT" -lt 1 ] || [ "$USER_COUNT" -gt 50 ]; then
echo "❌ 無効な参加者数です(1-50の範囲で入力してください)"
exit 1
fi
echo "参加者数: $USER_COUNT 名"
# VMサイズ推奨の表示
if [ "$USER_COUNT" -le 5 ]; then
echo "💡 推奨VMサイズ: Standard_B2ms (2vcpu, 8GB)"
elif [ "$USER_COUNT" -le 10 ]; then
echo "💡 推奨VMサイズ: Standard_B4ms (4vcpu, 16GB)"
elif [ "$USER_COUNT" -le 20 ]; then
echo "💡 推奨VMサイズ: Standard_B8ms (8vcpu, 32GB)"
else
echo "⚠️ $USER_COUNT 名は大規模です。複数VM構成を検討してください"
echo "💡 推奨: 複数VM構成(詳細はscalability-solutions.mdを参照)"
fi
echo ""
# 前提条件チェック
echo "📋 前提条件チェック:"
echo "- Azure VM(Ubuntu)が起動済み ✅"
echo "- code-serverが基本設定済み ✅"
echo "- NSGでポート80, 443, 8080-$((8079 + USER_COUNT))が開放済み"
echo ""
# Azure NSG設定の確認
END_PORT=$((8079 + USER_COUNT))
echo "⚠️ 重要: Azure NSGで以下ポートが開放されていることを確認してください"
echo "Azure Portal → VM → ネットワーク → 受信ポートの規則"
echo "- ポート80: HTTP (Let's Encrypt用)"
echo "- ポート443: HTTPS (本番用)"
echo "- ポート8080-$END_PORT: code-server用"
echo ""
read -p "NSGの設定が完了している場合はEnterを押してください..."
# メールアドレス入力
echo ""
echo "📧 Let's Encrypt証明書取得用のメールアドレスを入力してください"
echo "(証明書の有効期限通知などに使用されます)"
read -p "メールアドレス: " EMAIL
# メールアドレス形式チェック
if [[ ! "$EMAIL" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then
echo "❌ 無効なメールアドレス形式です"
exit 1
fi
echo "使用するメールアドレス: $EMAIL"
echo ""
# ===== ステップ1: 基本パッケージインストール =====
echo "🔧 ステップ1: 基本パッケージインストール"
echo "=================================="
sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx
echo "✅ ステップ1完了"
echo ""
# ===== ステップ2: Let's Encrypt証明書取得 =====
echo "🔐 ステップ2: Let's Encrypt証明書取得"
echo "================================="
# 1. VMのIPアドレス取得
VM_IP=$(curl -s ifconfig.me)
DOMAIN="$VM_IP.nip.io"
echo "VM IP: $VM_IP"
echo "使用するドメイン: $DOMAIN"
# 2. 一時的なHTTP設定
echo "一時的なHTTP設定を作成中..."
sudo tee /etc/nginx/sites-available/temp-http << EOF
server {
listen 80;
server_name $DOMAIN;
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://\$server_name\$request_uri;
}
}
EOF
sudo rm -f /etc/nginx/sites-enabled/*
sudo ln -s /etc/nginx/sites-available/temp-http /etc/nginx/sites-enabled/
sudo mkdir -p /var/www/html
sudo systemctl restart nginx
# 3. 証明書取得
echo "Let's Encrypt証明書を取得中..."
sudo certbot certonly --webroot -w /var/www/html -d $DOMAIN --non-interactive --agree-tos --email "$EMAIL"
if [ $? -ne 0 ]; then
echo "❌ 証明書取得に失敗しました"
echo "Azure NSGでポート80が正しく開放されているか確認してください"
exit 1
fi
echo "✅ ステップ2完了"
echo ""
# ===== ステップ3: 最終設定 =====
echo "⚙️ ステップ3: 最終設定"
echo "=================="
# 1. 動的Nginx設定生成
echo "動的Nginx設定を生成中($USER_COUNT ユーザー対応)..."
# Nginx設定のヘッダー部分
sudo tee /etc/nginx/sites-available/final-config << 'EOF'
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name _;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name _;
ssl_certificate /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/DOMAIN_PLACEHOLDER/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
EOF
# 各ユーザーのlocationブロックを動的生成
for i in $(seq 1 $USER_COUNT); do
PORT=$((8079 + i))
sudo tee -a /etc/nginx/sites-available/final-config << EOF
# user$i → port $PORT
location /user$i/ {
proxy_pass http://127.0.0.1:$PORT/;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection \$connection_upgrade;
proxy_set_header Host \$host;
proxy_set_header X-Forwarded-Proto \$scheme;
}
EOF
done
# ランディングページのHTML生成
LANDING_HTML='<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>🎓 生成AI勉強会</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
.container { max-width: 1000px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { color: #333; text-align: center; }
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 15px; margin-top: 30px; }
.user-card { background: #007acc; color: white; padding: 15px; border-radius: 8px; text-decoration: none; text-align: center; transition: background 0.3s; }
.user-card:hover { background: #005a9a; text-decoration: none; color: white; }
.status { text-align: center; margin-top: 20px; padding: 10px; background: #e8f5e8; border-radius: 5px; color: #2d5f2d; }
.info { background: #e3f2fd; padding: 15px; border-radius: 5px; margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container">
<h1>🎓 生成AI勉強会 - 参加者アクセス</h1>
<div class="info">
<strong>参加者数:</strong> '$USER_COUNT'名<br>
<strong>アクセス方法:</strong> 各自の番号をクリックしてログインしてください
</div>
<div class="grid">'
# 各ユーザーのリンクを動的生成
for i in $(seq 1 $USER_COUNT); do
USER_NUM=$(printf "%02d" $i)
LANDING_HTML="$LANDING_HTML
<a href=\"/user$i/\" class=\"user-card\">👤 参加者$i<br><small>study$USER_NUM</small></a>"
done
LANDING_HTML="$LANDING_HTML
</div>
<div class=\"status\">
✅ HTTPS対応済み | ✅ Markdownプレビュー利用可能 | ✅ WebSocket通信対応
</div>
</div>
</body>
</html>"
# ランディングページを設定に追加
sudo tee -a /etc/nginx/sites-available/final-config << EOF
# ランディングページ
location / {
return 200 '$LANDING_HTML';
add_header Content-Type text/html;
}
}
EOF
# 2. ドメイン名を実際の値に置換
CERT_DOMAIN=$(sudo find /etc/letsencrypt/live/ -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | head -n1)
if [ -z "$CERT_DOMAIN" ]; then
echo "❌ 証明書ディレクトリが見つかりません"
echo "デバッグ情報:"
sudo ls -la /etc/letsencrypt/live/ 2>/dev/null || echo "letsencryptディレクトリなし"
exit 1
fi
echo "証明書ドメイン: $CERT_DOMAIN"
sudo sed -i "s/DOMAIN_PLACEHOLDER/$CERT_DOMAIN/g" /etc/nginx/sites-available/final-config
# 3. 設定適用
echo "Nginx設定を適用中..."
sudo rm -f /etc/nginx/sites-enabled/*
sudo ln -s /etc/nginx/sites-available/final-config /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl restart nginx
# 4. code-server設定・起動
echo "code-server設定・起動中($USER_COUNT ユーザー)..."
# 既存プロセス停止
pkill -f "code-server" 2>/dev/null || true
for i in $(seq 1 $USER_COUNT); do
PORT=$((8079 + i))
USER_NUM=$(printf "%02d" $i)
mkdir -p ~/.config/code-server-user$i ~/.local/share/code-server-user$i ~/workspace/user$i
cat > ~/.config/code-server-user$i/config.yaml << EOF
bind-addr: 127.0.0.1:$PORT
auth: password
password: study$USER_NUM
cert: false
user-data-dir: /home/azureuser/.local/share/code-server-user$i
EOF
nohup code-server --config ~/.config/code-server-user$i/config.yaml ~/workspace/user$i > ~/.local/share/code-server-user$i/startup.log 2>&1 &
sleep 0.5
done
echo "✅ ステップ3完了"
echo ""
# ===== 完了 =====
echo "🎉 HTTPS化完了!"
echo "================"
echo ""
echo "📋 アクセス情報:"
echo "ランディングページ: https://$DOMAIN/"
echo ""
echo "👥 参加者別アクセス:"
for i in $(seq 1 $USER_COUNT); do
USER_NUM=$(printf "%02d" $i)
echo "参加者$i: https://$DOMAIN/user$i/ (パスワード: study$USER_NUM)"
done
echo ""
echo "✅ 機能確認:"
echo "- HTTPS通信: 正式なSSL証明書"
echo "- Markdownプレビュー: 正常動作"
echo "- WebSocket通信: 対応済み"
echo "- AI拡張機能: 利用可能"
echo ""
echo "🔧 動作確認:"
echo "1. ランディングページにアクセス"
echo "2. 任意の参加者番号をクリック"
echo "3. パスワードでログイン"
echo "4. Markdownファイルを作成してプレビュー機能をテスト"
echo ""
echo "🎓 勉強会の準備完了です!($USER_COUNT 名対応)"
実行
chmod +x setup-https-parameterized.sh
./setup-https-parameterized.sh
実行時の入力事項
- メールアドレス: Let's Encrypt証明書用(例: your@gmail.com)
- NSG確認: ポート80/443が開放済みであることを確認
実行結果
=== Azure code-server環境のHTTPS化(参加者数可変版) ===
📊 参加者数設定
==============
参加者数を入力してください(1-50): 11
参加者数: 11 名
💡 推奨VMサイズ: Standard_B8ms (8vcpu, 32GB)
📋 前提条件チェック:
- Azure VM(Ubuntu)が起動済み ✅
- code-serverが基本設定済み ✅
- NSGでポート80, 443, 8080-8090が開放済み
⚠️ 重要: Azure NSGで以下ポートが開放されていることを確認してください
Azure Portal → VM → ネットワーク → 受信ポートの規則
- ポート80: HTTP (Let's Encrypt用)
- ポート443: HTTPS (本番用)
- ポート8080-8090: code-server用
NSGの設定が完了している場合はEnterを押してください...
📧 Let's Encrypt証明書取得用のメールアドレスを入力してください
(証明書の有効期限通知などに使用されます)
メールアドレス: test@test.com
使用するメールアドレス: test@test.com
🔧 ステップ1: 基本パッケージインストール
==================================
Hit:1 http://azure.archive.ubuntu.com/ubuntu noble InRelease
Hit:2 http://azure.archive.ubuntu.com/ubuntu noble-updates InRelease
/////////////////
/ (中略) /
/////////////////
Nginx設定を適用中...
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
code-server設定・起動中(11 ユーザー)...
✅ ステップ3完了
🎉 HTTPS化完了!
================
📋 アクセス情報:
ランディングページ: https://[VM-IP].nip.io/
👥 参加者別アクセス:
参加者1: https://[VM-IP].nip.io/user1/ (パスワード: study01)
参加者2: https://[VM-IP].nip.io/user2/ (パスワード: study02)
参加者3: https://[VM-IP].nip.io/user3/ (パスワード: study03)
参加者4: https://[VM-IP].nip.io/user4/ (パスワード: study04)
参加者5: https://[VM-IP].nip.io/user5/ (パスワード: study05)
参加者6: https://[VM-IP].nip.io/user6/ (パスワード: study06)
参加者7: https://[VM-IP].nip.io/user7/ (パスワード: study07)
参加者8: https://[VM-IP].nip.io/user8/ (パスワード: study08)
参加者9: https://[VM-IP].nip.io/user9/ (パスワード: study09)
参加者10: https://[VM-IP].nip.io/user10/ (パスワード: study10)
参加者11: https://[VM-IP].nip.io/user11/ (パスワード: study11)
✅ 機能確認:
- HTTPS通信: 正式なSSL証明書
- Markdownプレビュー: 正常動作
- WebSocket通信: 対応済み
- AI拡張機能: 利用可能
🔧 動作確認:
1. ランディングページにアクセス
2. 任意の参加者番号をクリック
3. パスワードでログイン
4. Markdownファイルを作成してプレビュー機能をテスト
🎓 勉強会の準備完了です!(11 名対応)
-
アクセス先:
https://[VM-IP].nip.io/user1/
~https://[VM-IP].nip.io/user11/
-
ランディングページ:
https://[VM-IP].nip.io/
(全参加者のリンク集) - 機能: Markdownプレビュー、WebSocket通信すべて正常動作
注意: このスクリプトは既存のcode-serverプロセスを再起動する。
4.5 【参考】環境クリア用スクリプト
- 必要に応じて、以下のスクリプトを作成して全環境をクリーンアップする
nano cleanup-codeserver.sh
#!/bin/bash
# cleanup-codeserver.sh - 完全クリーンアップ用
echo "=== Code-server 環境のクリーンアップ ==="
read -p "本当に全環境を削除しますか? (y/N): " CONFIRM
if [[ $CONFIRM =~ ^[Yy]$ ]]; then
echo "全環境をクリーンアップ中..."
# プロセス停止
pkill -f "code-server.*user[0-9]"
# ディレクトリ削除
rm -rf ~/workspace/user*
rm -rf ~/.config/code-server-user*
rm -rf ~/.local/share/code-server-user*
echo "✅ クリーンアップ完了!"
else
echo "キャンセルしました。"
fi
- 実行する場合
chmod +x cleanup-codeserver.sh
./cleanup-codeserver.sh
5. 勉強会プロジェクト配置
5.1 勉強会プロジェクトの取得
PATトークンを使ってGitHubから勉強会プロジェクトを取得。
ここでは仮に「javaProject」という名前のリポジトリを取るとする。
# 作業ディレクトリ作成・移動
mkdir -p /tmp/targetRepo
cd /tmp/targetRepo
# プロジェクトクローン
git clone https://[PATtoken]@github.com/(リポジトリパス)/javaProject.git
# 元のディレクトリに戻る
cd ~
# 確認
ls -la /tmp/targetRepo
total 12
drwxrwxr-x 3 azureuser azureuser 4096 Jun 30 07:04 .
drwxrwxrwt 16 root root 4096 Jun 30 05:58 ..
drwxrwxr-x 9 azureuser azureuser 4096 Jun 30 05:59 javaProject
5.2 プロジェクト準備スクリプト
- javaProjectとなっている個所は適宜自分のリポジトリ名に変更してください
nano setup-user-workspaces-parameterized.sh
スクリプト内容(setup-user-workspaces-parameterized.sh):
#!/bin/bash
# setup-user-workspaces-parameterized.sh - 各ユーザーワークスペース準備(可変版)
echo "=== ユーザーワークスペース準備開始(可変版) ==="
# 参加者数の入力
read -p "参加者数を入力してください(1-50): " USER_COUNT
# 入力値チェック
if ! [[ "$USER_COUNT" =~ ^[0-9]+$ ]] || [ "$USER_COUNT" -lt 1 ] || [ "$USER_COUNT" -gt 50 ]; then
echo "❌ 無効な参加者数です(1-50の範囲で入力してください)"
exit 1
fi
echo "参加者数: $USER_COUNT 名"
echo ""
# プロジェクト準備ディレクトリ作成
mkdir -p /tmp/targetRepo
# 勉強会プロジェクトの取得確認
if [ ! -d "/tmp/targetRepo/javaProject" ]; then
echo "❌ 勉強会プロジェクトが見つかりません"
echo "手動で /tmp/targetRepo/javaProject に配置してください"
exit 1
fi
echo "✅ 勉強会プロジェクト確認完了"
echo ""
# 各ユーザーワークスペース準備
for i in $(seq 1 $USER_COUNT); do
USER_DIR="$HOME/workspace/user$i"
echo "📁 user$i 環境準備中..."
# 勉強会プロジェクト配置
if [ ! -d "$USER_DIR" ]; then
echo " ⚠️ user$i のワークスペースが存在しません。先にcode-server設定を実行してください。"
continue
fi
# プロジェクトコピー
cp -r /tmp/targetRepo/javaProject "$USER_DIR/"
echo " ✅ user$i 完了"
done
echo ""
echo "🎉 全ユーザーワークスペース準備完了!($USER_COUNT 名)"
echo ""
echo "📋 配置内容:"
echo "- javaProjectリポジトリ"
5.3 実行手順
chmod +x setup-user-workspaces-parameterized.sh
./setup-user-workspaces-parameterized.sh
実行結果例:
📥 勉強会プロジェクト取得中...
📁 user1 環境準備中...
✅ user1 完了
📁 user2 環境準備中...
✅ user2 完了
...
📁 user11 環境準備中...
✅ user11 完了
🎉 全ユーザーワークスペース準備完了!
余談:
- targetRepo内に複数のリポジトリをクローンし、まとめてcode-serverに配置することも出来ます
- 仮に別のリポジトリ「pythonProject」を入れたい場合は同様にプロジェクトクローンし、上記スクリプトのコピー処理に以下のように追記する(事前チェックやコメント文なども必要に応じて追記)
# リポジトリを複数あれば以下のように設定する # cp -r /tmp/targetRepo/pythonProject "$USER_DIR/"
- 仮に別のリポジトリ「pythonProject」を入れたい場合は同様にプロジェクトクローンし、上記スクリプトのコピー処理に以下のように追記する(事前チェックやコメント文なども必要に応じて追記)
6. 参加者向けアクセス情報の準備
6.1 接続情報リスト作成
nano create-access-info-parameterized.sh
スクリプト内容(create-access-info-parameterized.sh):
#!/bin/bash
# create-access-info-parameterized.sh - アクセス情報作成(可変版)
echo "=== 参加者アクセス情報作成(可変版) ==="
# 参加者数の自動検出
USER_COUNT=$(ls -d ~/workspace/user* 2>/dev/null | wc -l)
if [ $USER_COUNT -eq 0 ]; then
echo "⚠️ ユーザーワークスペースが見つかりません"
read -p "参加者数を手動入力してください(1-50): " USER_COUNT
# 入力値チェック
if ! [[ "$USER_COUNT" =~ ^[0-9]+$ ]] || [ "$USER_COUNT" -lt 1 ] || [ "$USER_COUNT" -gt 50 ]; then
echo "❌ 無効な参加者数です"
exit 1
fi
else
echo "🔍 検出された参加者数: $USER_COUNT 名"
fi
# VMのパブリックIP取得
VM_IP=$(curl -s ifconfig.me)
# HTTPS対応確認
if [ -d "/etc/letsencrypt/live" ] && [ -n "$(sudo ls -A /etc/letsencrypt/live/ 2>/dev/null)" ]; then
PROTOCOL="https"
DOMAIN="$VM_IP.nip.io"
ACCESS_BASE="https://$DOMAIN"
SECURITY_STATUS="✅ HTTPS対応済み"
else
PROTOCOL="http"
ACCESS_BASE="http://$VM_IP"
SECURITY_STATUS="⚠️ HTTP接続(HTTPS化推奨)"
fi
echo "アクセス方式: $PROTOCOL"
echo "ベースURL: $ACCESS_BASE"
echo ""
# アクセス情報ファイル作成
cat > ~/access_info.txt << EOF
# 生成AI勉強会 - 参加者アクセス情報
# 生成日時: $(date '+%Y年%m月%d日 %H:%M:%S')
# 参加者数: $USER_COUNT 名
## 接続方法
1. ブラウザを開く
2. 以下のURLにアクセス
3. パスワードを入力してログイン
## ランディングページ
$ACCESS_BASE/
(全参加者のリンク集)
## 参加者別アクセス情報
EOF
# 各参加者のアクセス情報を追加
for i in $(seq 1 $USER_COUNT); do
USER_NUM=$(printf "%02d" $i)
cat >> ~/access_info.txt << EOF
### 参加者 $i
- URL: $ACCESS_BASE/user$i/
- パスワード: study$USER_NUM
EOF
done
# 環境情報を追加
cat >> ~/access_info.txt << EOF
## 利用環境について
- ブラウザベースのVS Code環境
- Java 21 + Maven/Gradle
- javaProjectリポジトリ
- AI拡張機能:
- ClaudeCode:ターミナルベースで動作するAIエージェント
## セキュリティ状況
$SECURITY_STATUS
## 注意事項
- 作業データは勉強会終了後に削除されます
- 重要なコードは各自でローカル保存してください
- パスワードは第三者に共有しないでください
## トラブル時の対処
1. ブラウザのキャッシュ・Cookie削除
2. プライベートモードでの接続試行
3. 異なるブラウザでの接続試行
4. 講師に連絡
EOF
echo "✅ アクセス情報を ~/access_info.txt に作成しました"
echo ""
echo "📋 作成内容:"
echo "- 参加者数: $USER_COUNT 名"
echo "- アクセス方式: $PROTOCOL"
echo "- ランディングページ: $ACCESS_BASE/"
echo ""
echo "📄 ファイル内容の確認:"
cat ~/access_info.txt
6.2 実行手順
chmod +x create-access-info-parameterized.sh
./create-access-info-parameterized.sh
以下の出力になっていることを確認する
=== 参加者アクセス情報作成(可変版) ===
🔍 検出された参加者数: 11 名
アクセス方式: https
ベースURL: https://[VM-IP].nip.io
✅ アクセス情報を ~/access_info.txt に作成しました
📋 作成内容:
- 参加者数: 11 名
- アクセス方式: https
- ランディングページ: https://[VM-IP].nip.io/
📄 ファイル内容の確認:
# 生成AI勉強会 - 参加者アクセス情報
(中略)
### 参加者 1
- URL: https://[VM-IP].nip.io/user1/
- パスワード: study01
### 参加者 2
- URL: https://[VM-IP].nip.io/user2/
- パスワード: study02
(中略)
### 参加者 11
- URL: https://[VM-IP].nip.io/user11/
- パスワード: study11
## 利用環境について
- ブラウザベースのVS Code環境
- Java 21 + Maven/Gradle
- javaProjectリポジトリ
- AI拡張機能:
- ClaudeCode:ターミナルベースで動作するAIエージェント
## セキュリティ状況
✅ HTTPS対応済み
## 注意事項
- 作業データは勉強会終了後に削除されます
- 重要なコードは各自でローカル保存してください
- パスワードは第三者に共有しないでください
## トラブル時の対処
1. ブラウザのキャッシュ・Cookie削除
2. プライベートモードでの接続試行
3. 異なるブラウザでの接続試行
4. 講師に連絡
6.3 環境確認
- 各参加者の勉強会画面(code-server)を確認する
- ここまでの作業を前日までに終えたら、仮想マシンを一時停止する
- Azureの仮想マシンから「停止」を押す、確認画面が出るので「はい」を押す
7. 【勉強会当日】講師の運用手順
7.1 開始前の確認
-
仮想マシンを開始する
-
11人の場合を記載しています。仮に可変の場合はseq部分の数値を変更してください
# 全code-serverの状態確認
ps aux | grep code-server
# 全ポートの確認
for i in $(seq 1 11); do
PORT=$((8079 + i))
curl -s http://localhost:$PORT > /dev/null && echo "Port $PORT: OK" || echo "Port $PORT: NG"
done
# 全インスタンス起動(必要に応じて)
./start-all-codeserver.sh
# アクセス情報確認
cat ~/access_info.txt
7.2 参加者への案内手順
-
アクセス情報配布
- 各参加者に個別のURL・パスワードを配布
- ブラウザでのアクセス方法を説明
-
初期設定サポート
- VS Code環境の基本操作説明
- 拡張機能などが必要であればここで設定させる
- Mermaid表示用の拡張機能(Markdown Preview Mermaid Supportなど)は取り込めることを確認
- 拡張機能などが必要であればここで設定させる
- 勉強会プロジェクトの説明
- VS Code環境の基本操作説明
-
トラブルシューティング
- 接続できない場合の対処法
8. 勉強会後のクリーンアップ
8.1 Let's Encrypt証明書の取り消し
VMを削除する前に、Let's Encrypt証明書を取り消ししてください。
# SSH接続してVM内で実行
# 1. 証明書の存在確認
sudo certbot certificates
# 存在していたら2.へ
# 2. 証明書が存在するならRevokeする
sudo certbot revoke --cert-path /etc/letsencrypt/live/[VM-IP].nip.io/cert.pem
# 削除確認に対してYESと答える
# 3. 再度証明書を確認
sudo certbot certificates
# No certificates found. と出ればOK
8.2 code-serverプロセスの停止
# 全code-serverプロセスを停止
pkill -f code-server
# 確認
ps aux | grep code-server
# /home/azureuser/workspace/userXX に対応するプロセスが消えていることを確認
8.3 VMの停止
# 割り当て解除で課金停止
sudo shutdown -h now
8.4 リソースの削除
仮想マシンは停止した場合でも、維持費がわずかにかかります。
勉強会に関係するリソースの削除を行います。
Azure Portalから:
- リソースグループ
study-group-rg
を選択 - 「リソースグループの削除」をクリック
- 確認画面後に削除実行
- 仮想ネットワーク生成時に自動的に該当リージョンで Network Watcher が自動的に有効になるようです。(
NetworkWatcherRG
として生成)
こちらに関しては勉強会以前の作業でネットワークを生成した場合、慎重に削除してください。
(本検証では勉強会用の環境作成以外は使ってないので削除)
9. よくある落とし穴と対策
9.1 よくある問題
ポートにアクセスできない
- NSGで該当ポート(8080-8090)が開放されているか確認
- ファイアウォール設定確認
sudo ufw status
sudo ufw allow 8080:8090/tcp
code-server起動失敗
- 設定ファイル確認
cat ~/.config/code-server-user1/config.yaml
- ログ確認
tail -f ~/.local/share/code-server-user1/startup.log
メモリ不足でVMが重い
- VMサイズを上げる(B8ms等)
- 参加者数を制限
- 不要なプロセスの停止
参加者の接続エラー
- ブラウザのキャッシュクリア
- プライベートモードでの接続試行
- 異なるブラウザでの接続試行
9.2 ログ確認
# 特定のcode-serverログ確認
tail -f ~/.local/share/code-server-user1/startup.log
# システムログ
sudo dmesg | tail
# 全プロセス確認
top -p $(pgrep -d',' code-server)
10. 参加者数の変更について
重要: 現在の手順は講師+10人対応として作成されています。参加者数を変更する場合は、以下の調整が必要です:
10.1 変更箇所
-
ポート設定範囲 (セクション2.1)
- 講師+20人の場合:
8080-8100
に変更
- 講師+20人の場合:
-
VMサイズ (セクション1.2)
- 20人の場合:
Standard_B8ms
を選択
- 20人の場合:
10.2 推奨参加者数とVMサイズ
- 5人以下: Standard_B2ms(2vcpu, 8GB)
- 10人以下: Standard_B4ms(4vcpu, 16GB)
- 20人以下: Standard_B8ms(8vcpu, 32GB)
-
21人以上: 高性能インスタンス適用 or 複数VMで構築
- 例:50名体制の場合、B8msを2つ、B4msを1つ作成して対応する。それぞれIPアドレスが異なるため、上記のスクリプトを利用することが可能
- ただし、集中アクセスによる想定外の挙動、またはコスト面の負荷から、あまりに大人数の場合は各員にGitHubアカウントを作ってもらい、Github Codespacesを使った方がよさそう
感想
- この四苦八苦、手順書の作成、内容検証までがわずか2日で完結している
- 今まで「やりたい気持ちはあるが時間がないから諦めた」というタスクが、どんどん消滅していく予感を覚える。なくそう、負債
- AIツールも数々登場してきたが、ClaudeCodeの解釈能力の高さは頭一つ抜けていると痛感した
- とにかくノーストレス。認識齟齬でカチンと来ることが皆無
- ダメな場合でも、様々な手段による検証を試みる機転の良さも見せる
- 次々と権限を委ねたくなってくる……が、集団での開発だと予想外の挙動をしかねないので、事前のルール決めは必須事項になってきそう
- 参考:Claude Code に壊されないための denyルール完全ガイド