code-server にリバースプロキシを組み合わせてマルチユーザー対応の Web IDE 環境にしてみた
はじめに
プログラミングやバイオインフォマティクスを学ぶ講義では、実習環境の準備が欠かせません。しかし、受講者ごとに環境を準備するのは大変な作業です。そこで、複数ユーザーが同時に利用できるブラウザベースの開発環境として、code-server の活用を検討しました。
以前検証した単一 EC2 上でのマルチユーザー向け code-server 環境を受講者視点で使いやすいように改善しました。具体的にはリバースプロキシの追加と HTTPS 対応しました。
検証環境
今回の検証では以下の構成で code-server 環境を導入しました。
項目 | 内容 |
---|---|
EC2 インスタンス | t3.small |
EBS(gp3) | 12GB |
OS | Ubuntu 24.04 |
code-server | v4.98.2 |
SSL 証明書 | Let's Encrypt |
構成図
今回の構成はシンプルです。1 台の EC2 インスタンス上に code-server とリバースプロキシ(Caddy)を配置します。
証明書はLet's Encrypt
Let's Encrypt は無料で SSL 証明書を提供する認証局です。自動化されたプロセスにより、証明書の取得と更新が簡単に行えます。今回は Let's Encrypt を使用して、セキュアな HTTPS 接続を実現しています。
環境構築手順
Route 53でドメイン設定
まず、Route 53 でドメインを設定します。今回はcode.ohmura.example.com
というドメインを使用しました。EC2 にアタッチした EIP のパブリック IP アドレスを A レコードとして登録します。
セキュリティグループの設定
EC2 インスタンスのセキュリティグループで、HTTP と HTTPS アクセスを許可します。HTTP ポート(80 番)は Let's Encrypt の認証に必要なため、事前に開放しておきます。
code-serverのインストールと設定
code-server の設定と Caddy の設定用の自動化スクリプトを用意しました。このスクリプトでは以下の処理を行います。
- code-server と Caddy のインストール
- 指定した数のユーザーアカウント(Linux のユーザー)の作成
- 各ユーザー用の code-server 設定
- Caddy によるリバースプロキシ設定
- ユーザーアクセス情報の生成
#!/bin/bash
set -e
DOMAIN_NAME="code.ohmura.example.com
USER_COUNT=20
BASE_PORT=50443
CSV_FILE="/home/ubuntu/code-server-users.csv"
# 必要なパッケージのインストール
echo "必要なパッケージをインストールしています..."
sudo apt update
sudo apt install -y pwgen jq curl
# code-serverのインストール
CODER_VERSION=$(curl -s https://api.github.com/repos/coder/code-server/releases/latest | jq -r .tag_name | sed 's/v//')
DOWNLOAD_URL="https://github.com/coder/code-server/releases/download/v${CODER_VERSION}/code-server_${CODER_VERSION}_amd64.deb"
if [ ! -f "/usr/bin/code-server" ]; then
echo "code-server ${CODER_VERSION} をダウンロードしています..."
curl -fOL ${DOWNLOAD_URL}
echo "code-server をインストールしています..."
apt install -y ./code-server_${CODER_VERSION}_amd64.deb
rm -f code-server_${CODER_VERSION}_amd64.deb
else
echo "code-server はすでにインストールされています"
fi
# Caddyのインストール
echo "Caddyをインストールしています..."
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
# ユーザー情報CSVファイルの作成
echo "username,cs_password,access_url" > $CSV_FILE
# Webページ用ディレクトリの作成
sudo mkdir -p /var/www/code-server
# インデックスHTMLの作成
cat > /tmp/index.html << EOF
<!DOCTYPE html>
<html>
<head>
<title>Code Server Instances</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #333; }
ul { list-style-type: none; padding: 0; }
li { margin: 10px 0; }
a { color: #0066cc; text-decoration: none; padding: 10px; display: inline-block; border: 1px solid #ddd; border-radius: 4px; }
a:hover { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>Code Server Instances</h1>
<ul>
EOF
# Caddyfile の作成開始
cat > /tmp/Caddyfile << EOF
${DOMAIN_NAME} {
handle / {
root * /var/www/code-server
file_server
}
EOF
# ユーザーごとの設定
for i in $(seq 1 $USER_COUNT); do
USERNAME="guest-$i"
USER_HOME="/home/$USERNAME"
PORT=$((BASE_PORT + i - 1))
echo "ユーザー $USERNAME を設定しています..."
# ユーザーが存在しない場合は作成
if ! id -u "$USERNAME" &>/dev/null; then
sudo useradd -m -s /bin/bash "$USERNAME"
fi
# code-server の設定ディレクトリを作成
sudo mkdir -p "$USER_HOME/.config/code-server/"
# code-server 用パスワードを生成
CS_PASSWORD=$(pwgen -s -n -c 32 1)
# 設定ファイルを作成
sudo bash -c "cat > $USER_HOME/.config/code-server/config.yaml << EOF
bind-addr: localhost:$PORT
auth: password
password: $CS_PASSWORD
cert: false
EOF"
# 所有権を設定
sudo chown -R "$USERNAME:$USERNAME" "$USER_HOME/.config"
# systemd サービスファイルを作成
sudo bash -c "cat > /etc/systemd/system/code-server@$USERNAME.service << EOF
[Unit]
Description=code-server for $USERNAME
After=network.target
[Service]
Type=simple
User=$USERNAME
ExecStart=/usr/bin/code-server
Restart=always
WorkingDirectory=$USER_HOME
[Install]
WantedBy=multi-user.target
EOF"
# サービスを有効化して開始
sudo systemctl daemon-reload
sudo systemctl enable --now "code-server@$USERNAME"
# アクセスURLを作成
ACCESS_URL="https://${DOMAIN_NAME}/${USERNAME}/"
# CSVファイルに情報を追加
echo "$USERNAME,$CS_PASSWORD,$ACCESS_URL" >> $CSV_FILE
# インデックスHTMLにユーザーを追加
echo " <li><a href=\"/${USERNAME}/\">${USERNAME}</a></li>" >> /tmp/index.html
# Caddyfileにユーザー設定を追加
cat >> /tmp/Caddyfile << EOF
handle /${USERNAME}/* {
uri strip_prefix /${USERNAME}
reverse_proxy http://localhost:$PORT
}
redir /${USERNAME} /${USERNAME}/ 301
EOF
done
# インデックスHTMLの終了タグ
cat >> /tmp/index.html << EOF
</ul>
</body>
</html>
EOF
# Caddyfileの終了
cat >> /tmp/Caddyfile << EOF
log {
output file /var/log/caddy/code-server.log
}
}
EOF
# 設定ファイルを適用
sudo mv /tmp/index.html /var/www/code-server/index.html
sudo mv /tmp/Caddyfile /etc/caddy/Caddyfile
# 権限設定
sudo chown -R caddy:caddy /var/www/code-server
sudo chmod -R 755 /var/www/code-server
sudo chown ubuntu:ubuntu $CSV_FILE
sudo chmod 600 $CSV_FILE
# Caddyサービスを再起動
sudo systemctl restart caddy
# サマリーを表示
echo "--- セットアップ完了 ---"
echo "各ユーザーのアクセス情報は以下のファイルに保存されています:"
echo "$CSV_FILE"
echo ""
echo "トップページ: https://${DOMAIN_NAME}"
スクリプトを実行すると、以下のような出力が表示されます。
--- セットアップ完了 ---
各ユーザーのアクセス情報は以下のファイルに保存されています:
/home/ubuntu/code-server-users.csv
トップページ: https://code.ohmura.example.com
管理者向け情報
各ユーザーの code-server パスワードは自動生成され、CSV ファイルに保存されます。
username,cs_password,access_url
guest-1,qgPwwppzzrkEEmWGgv9nQqL10cpPkg6s,https://code.ohmura.example.com/guest-1/
guest-2,xKsLmXWKRrUhIH9qw7x2oyCxHoVVCxbW,https://code.ohmura.example.com/guest-2/
guest-3,VVKWB5GnCvtgZoMOrx6qIIG2izweVLvw,https://code.ohmura.example.com/guest-3/
guest-4,VQunlw4g2YeSS1nfNDJWh7oND9KsEb9i,https://code.ohmura.example.com/guest-4/
guest-5,HkX80de45ADZyMlVNdvf5qfaJYLmFJIy,https://code.ohmura.example.com/guest-5/
Caddy の設定
リバースプロキシの設定は以下のファイルが作成されます。各ユーザー名に対応したパスへのアクセスを想定しています。
折りたたみ
code.ohmura.example.com {
handle / {
root * /var/www/code-server
file_server
}
handle /guest-1/* {
uri strip_prefix /guest-1
reverse_proxy http://localhost:50443
}
redir /guest-1 /guest-1/ 301
handle /guest-2/* {
uri strip_prefix /guest-2
reverse_proxy http://localhost:50444
}
redir /guest-2 /guest-2/ 301
handle /guest-3/* {
uri strip_prefix /guest-3
reverse_proxy http://localhost:50445
}
redir /guest-3 /guest-3/ 301
handle /guest-4/* {
uri strip_prefix /guest-4
reverse_proxy http://localhost:50446
}
redir /guest-4 /guest-4/ 301
handle /guest-5/* {
uri strip_prefix /guest-5
reverse_proxy http://localhost:50447
}
redir /guest-5 /guest-5/ 301
handle /guest-6/* {
uri strip_prefix /guest-6
reverse_proxy http://localhost:50448
}
redir /guest-6 /guest-6/ 301
handle /guest-7/* {
uri strip_prefix /guest-7
reverse_proxy http://localhost:50449
}
redir /guest-7 /guest-7/ 301
handle /guest-8/* {
uri strip_prefix /guest-8
reverse_proxy http://localhost:50450
}
redir /guest-8 /guest-8/ 301
handle /guest-9/* {
uri strip_prefix /guest-9
reverse_proxy http://localhost:50451
}
redir /guest-9 /guest-9/ 301
handle /guest-10/* {
uri strip_prefix /guest-10
reverse_proxy http://localhost:50452
}
redir /guest-10 /guest-10/ 301
handle /guest-11/* {
uri strip_prefix /guest-11
reverse_proxy http://localhost:50453
}
redir /guest-11 /guest-11/ 301
handle /guest-12/* {
uri strip_prefix /guest-12
reverse_proxy http://localhost:50454
}
redir /guest-12 /guest-12/ 301
handle /guest-13/* {
uri strip_prefix /guest-13
reverse_proxy http://localhost:50455
}
redir /guest-13 /guest-13/ 301
handle /guest-14/* {
uri strip_prefix /guest-14
reverse_proxy http://localhost:50456
}
redir /guest-14 /guest-14/ 301
handle /guest-15/* {
uri strip_prefix /guest-15
reverse_proxy http://localhost:50457
}
redir /guest-15 /guest-15/ 301
handle /guest-16/* {
uri strip_prefix /guest-16
reverse_proxy http://localhost:50458
}
redir /guest-16 /guest-16/ 301
handle /guest-17/* {
uri strip_prefix /guest-17
reverse_proxy http://localhost:50459
}
redir /guest-17 /guest-17/ 301
handle /guest-18/* {
uri strip_prefix /guest-18
reverse_proxy http://localhost:50460
}
redir /guest-18 /guest-18/ 301
handle /guest-19/* {
uri strip_prefix /guest-19
reverse_proxy http://localhost:50461
}
redir /guest-19 /guest-19/ 301
handle /guest-20/* {
uri strip_prefix /guest-20
reverse_proxy http://localhost:50462
}
redir /guest-20 /guest-20/ 301
log {
output file /var/log/caddy/code-server.log
}
}
トップページはこちらです。各ユーザーのパスへのリンクを作成しています。
/var/www/code-server/index.html
折りたたみ
<!DOCTYPE html>
<html>
<head>
<title>Code Server Instances</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
h1 { color: #333; }
ul { list-style-type: none; padding: 0; }
li { margin: 10px 0; }
a { color: #0066cc; text-decoration: none; padding: 10px; display: inline-block; border: 1px solid #ddd; border-radius: 4px; }
a:hover { background-color: #f0f0f0; }
</style>
</head>
<body>
<h1>Code Server Instances</h1>
<ul>
<li><a href="/guest-1/">guest-1</a></li>
<li><a href="/guest-2/">guest-2</a></li>
<li><a href="/guest-3/">guest-3</a></li>
<li><a href="/guest-4/">guest-4</a></li>
<li><a href="/guest-5/">guest-5</a></li>
<li><a href="/guest-6/">guest-6</a></li>
<li><a href="/guest-7/">guest-7</a></li>
<li><a href="/guest-8/">guest-8</a></li>
<li><a href="/guest-9/">guest-9</a></li>
<li><a href="/guest-10/">guest-10</a></li>
<li><a href="/guest-11/">guest-11</a></li>
<li><a href="/guest-12/">guest-12</a></li>
<li><a href="/guest-13/">guest-13</a></li>
<li><a href="/guest-14/">guest-14</a></li>
<li><a href="/guest-15/">guest-15</a></li>
<li><a href="/guest-16/">guest-16</a></li>
<li><a href="/guest-17/">guest-17</a></li>
<li><a href="/guest-18/">guest-18</a></li>
<li><a href="/guest-19/">guest-19</a></li>
<li><a href="/guest-20/">guest-20</a></li>
</ul>
</body>
</html>
各ユーザーの code-server の設定
~/.config/code-server/config.yaml
配下に各ユーザーのホームディレクトリ配下にポート別の code-server の設定ファイルが作成されます。
bind-addr: localhost:50443
auth: password
password: qgPwwppzzrkEEmWGgv9nQqL10cpPkg6s
cert: false
動作確認
セットアップが完了したら、ブラウザからアクセスして動作確認を行います。
まず、トップページにアクセスすると、各ユーザーの code-server インスタンスへのリンクが表示されます。
各ユーザーのリンクをクリックすると、各ユーザーに対応した code-server のログイン画面が表示されます。CSV ファイルに記録されたパスワードを入力してログインします。
ログイン後は通常の code-server の環境が利用できます。各ユーザーは独立した環境で作業できます。
code-server の基本的な機能は以下の記事で検証しましたので参考にしてください。
まとめ
今回は、EC2 インスタンス 1 台でマルチユーザー対応の code-server 環境を構築しました。Caddy をリバースプロキシとして利用することで、各ユーザー専用の URL を提供し、Let's Encrypt による HTTPS 対応も実現できました。
この構成の主なメリットは以下の通りです。
- 単一サーバーで複数ユーザーに開発環境を提供できる
- ユーザーごとに独立した環境を提供できる
- HTTPS 対応でセキュアな接続が可能
小規模向けの Linux の演習環境として、手軽に導入できて良いのではないでしょうか。
おわりに
code-server はシングルユーザー向けのプロダクトなこともあり、そのままではマルチユーザーで使うには難しいことは以前検証して把握していました。
EC2 1 台で複数ユーザーが利用可能な Web IDE 環境を code-server で実現できるのか試してみた | DevelopersIO
その後、HTTPS 対応のためにリバースプロキシを経由する方法を検証しました。
Caddy で簡単構築!code-server の HTTPS 環境セットアップ | DevelopersIO
リバースプロキシを配置してアクセスできるならやりようがあるではないかということで今回の検証に至りました。複数ユーザーアクセス時のメモリ消費量が気がかりですので、負荷かけて試したいところです。
code-server の代わりに JupyterHub の活用も検討しました。