Webサーバー
が話しかけてくれるスクリプト20行。
Webはを待ってを送るだけのです。それを証明するために、bashととだけでサーバーを作ります — 一行残らず読めます。そのあと、実際のサイトを込みで動かすCaddyへ進みます。
Webサーバーは答えるプログラムです
ラックとのロゴを取り払えば、Webサーバーは一つのことを繰り返すプログラム一つです。誰かがつなぐのを待ち、何が欲しいかを読み、文章を返し、また待ちます。
「サーバー」という語は二つのものを指し、人はいつも混ぜて使います。データセンターに置かれた機械もサーバー、で耳を澄ませるプログラムもサーバーです。ここで言うのはプログラムのほうで、それは目の前のノートパソコンでも同じように動きます。
訪問者一人、四つの歩み
- 耳を澄ますプログラムがポートを一つ占めて待ちます — 例では8084です。ポートは機械に付いた番号付きの扉なので、二つのプログラムが同じ扉を握ることはできません。
- リクエストを読むブラウザーがつないで数行の文章を送ります。一行目に、どの方法でどのパスが欲しいかが入ります。たとえばパス / をしてくれ、という具合です。
- 判断する何を送るかを決めます。ディスクからファイルを読む、を走らせる、に問う。例はいつも同じファイルを送るだけなので20行に収まります。
- 答えて切るといくつかの、空行を一つ、そして中身を書いて送り、接続を切って次の訪問者のために最初へ戻ります。
あなたが名前を聞いたことのあるサーバーは — Apache・Nginx・Caddy、このサイトを動かしているPHPプログラム(FrankenPHP)まで — 上の四歩をしています。違うのは、それを何千人にも同時に、安全に、HTTPSで行うという点だけです。形は同じです。
シェルスクリプト一枚で作るWebサーバー
下がbashで書いた完全なWebサーバーです。ポートを開き、リクエスト行を読み、記録を残し、index.htmlをきちんとしたHTTPレスポンスとして返します。二十行、なし、そしてブラウザーは違いに気付きません。
重い仕事は二つのコマンドが担います。netcat(nc)がネットワークを話し、が名前付きパイプを作って、書いた答えをncが握っている接続へ戻します。
#!/bin/bash
PORT=8084
PIPE=simple-web-server-pipe
rm -f "$PIPE"
mkfifo "$PIPE"
response() {
read -r method path _
echo "[$(date)] $method $path" >&2
index=$(cat index.html)
printf 'HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\nConnection: close\r\n\r\n%s' \
"$index"
}
while true; do
response < "$PIPE" | nc -l "$PORT" > "$PIPE"
done
一行ずつ読む
PORT=8084耳を澄ますポートです。1024より大きい番号は管理者権限なしで使え、8084なら既に動かしているものとぶつかりにくいでしょう。PIPE=simple-web-server-pipeパイプファイルの名前です。フォルダーにはふつうのファイルのように現れますが、中に保存されるものはありません — 二つのコマンドの間をデータが通り抜けるだけです。mkfifo "$PIPE"名前付きパイプ(FIFO)を作ります。すぐ上の行が前回の残りを消しているのは、mkfifoが既にあるファイルへの上書きを拒むからです。read -r method path _リクエストの一行目を読んで、メソッド・パス・残りに分けます。その後に続くブラウザーのヘッダーは、このサーバーは読み飛ばします。echo "[$(date)] $method $path" >&2記録を一行残します。標準エラーへ向きを変えているおかげで、この文がブラウザーへ行かずターミナルにだけ現れます。index=$(cat index.html)今いるフォルダーのindex.htmlを読んで変数に入れます。サーバーをそのファイルのあるフォルダーで起動しなければならない理由がこれです。printf 'HTTP/1.1 200 OK\r\n…'レスポンスを書きます。ステータス行、中身の種類、ここで接続を終えるというヘッダー、そして空行とページ。ヘッダーと中身を分けるのがその空行で、HTTPはすべての行末にキャリッジリターンと改行を求めます。response < "$PIPE" | nc -l "$PORT" > "$PIPE"これが動く仕掛けです。ncは|の前のresponseが書いたレスポンスを受け取ってブラウザーへ送り、ブラウザーのリクエストはFIFOファイル($PIPE)に書き込みます。responseはそのFIFOからリクエストを読みます。両側が互いに餌を与え合う形です。while true; do … done繰り返しです。ncは接続を一つ処理すると終わってしまうので、この行がなければサーバーは訪問者を一人受けて止まります。
リクエストが関数に届いて初めて、関数の答えがncに届きます — 輪の形です。ところがシェルのパイプラインは一方向にしか流れません。名前付きパイプがその輪を閉じます。レスポンスはパイプラインを通ってncへ、リクエストはFIFOを通って戻ってきます。二つのコマンドを双方向で会話させる、いちばん小さな方法です。
自分で動かす
スクリプトをserver.shとして保存し、隣にindex.htmlを置いて起動します。そのは開いたままにして、二つ目のターミナルからリクエストを送ってみてください。
# 1. create an index.html in the same folder
echo '<h1>Hello</h1>' > index.html
# 2. save the script above as server.sh and start it
bash server.sh
# 3. open it in a browser (or check from a second terminal)
open http://localhost:8084
curl http://localhost:8084
ブラウザーを再読み込みしながら一つ目のターミナルを見てください。リクエスト一件ごとに記録が一行増えます。Control-Cで止め、パイプファイルが残っていたら消します。のncはポートのまえにさらにPオプションが要ることがあり、macOSに標準で入っている版は書いたとおりに動きます。
ここで使ったシェルコマンドを学ぶ線の上を実際に流れるもの
接続を開いてみると、自分でも打てたはずのふつうの文字が流れています。シェルWebサーバーが成り立つ理由がこれであり、Webの不具合を当て推量ではなく読んで直せる理由もこれです。
ブラウザーが送るリクエスト
GET / HTTP/1.1
Host: localhost:8084
User-Agent: Mozilla/5.0
Accept: text/html
- 一行目が、このサーバーが読む唯一の部分です。メソッド(GETは取ってくる、POSTは送る)、パス、そしてプロトコルの版。
- Hostはどのサイトが欲しいかを伝えます。一つのサーバーが一つの住所で何百ものを抱えられ、それを見分けるのがこのヘッダーです。
- 残りはブラウザーの自己紹介です。自分がどんなソフトか、どんな形式を読めるか、どの言語が好みか。サーバーは使ってもよいし、無視してもかまいません。
サーバーが送るレスポンス
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Connection: close
<h1>Hello</h1>
- ステータス行です。プロトコルの版と、どうなったかを告げる数字。200は成功という意味です。
- Content-Typeは、続くものをどう読むべきかをブラウザーに教えます。を間違った種類で送ると、ブラウザーはをページではなく文字のまま見せます。
- charsetの部分は実際には省けません。これがないと日本語は文字化けして届きがちです。
- そして空行が一つ。その後ろはすべて中身です。ヘッダーとページを分ける境目は、その空行だけです。
覚えておきたいステータスコード
| コード | 意味 |
|---|---|
200 OK | 成功です。求めたものがレスポンスに入っています。200番台はどれもこの種の成功を意味します。 |
301 Moved Permanently | 住所が恒久的に移りました。新しい住所はLocationヘッダーにあり、ブラウザーが自動でついていきます。サイトがhttpの訪問者をhttpsへ送るときの手口で、同じ意味の308を使うこともあります(Caddyがそうです)。 |
404 Not Found | サーバーは元気ですが、そのパスがありません。400番台は問題がリクエスト側にあるという意味です。 |
500 Internal Server Error | まっとうなリクエストを処理する途中でサーバーが倒れました。500番台は直すべき人があなただという意味で、詳しい話はサーバーの記録にあります。 |
このサーバーにできないこと
この例は正直な教材であり、ひどいサービスです。どこが足りないかを正確に知ることが、本物のサーバーが代わりに解いてくれる問題を知ることです。
- 一度に一人だけ。ncは接続を一つしか扱わないので、それがふさがっている間ほかの全員が待ちます。
- パスを無視します。何を求めてもindex.htmlが返るだけ — 道案内もなく、二枚目のページもありません。
- すべてHTMLとして送ります。画像もスタイルシートもダウンロードもそれぞれの種類が要るのに、このサーバーは一種類しか知りません。
- HTTPSがありません。すべてが裸で流れるので、途中のどのネットワークでも読めますし書き換えられます。
- 安全装置が一切ありません。リクエストの大きさを測らず、時間切れもなく、うかつに広げれば他人が決めたファイルパスをそのまま読んでしまいます。
自分の機械で動かしてHTTPを目で見ることに使い、他人が訪ねてくるものには本物のサーバーを使います。次の節がその本物で、始めるのはこれより大して難しくありません。
Caddy — 読めるまま残った本物のWebサーバー
Caddyは実行ファイル一つでできた現代的なWebサーバーです。シェルの例のすぐあとに学ぶ価値があるのは、読める数行で動くという感覚をそのままに、シェル版にできなかったこと(大勢の同時接続・HTTPS・住所ごとの道分け)を片づけるからです。
いちばんの売りは自動HTTPSです。本物のドメイン名を与えるだけでを取り、入れ、期限が来る前に更新し、httpの訪問者をhttpsへ回します。ほかのサーバーでは数か月ごとに繰り返す雑用です。
インストール
# macOS
brew install caddy
# Ubuntu / Debian — add Caddy's own package list first (official steps)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
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
# with Docker (covered in the Docker tab): serve this folder on port 8080
docker run -p 8080:80 -v "$PWD":/usr/share/caddy caddy:2
コマンド一行でフォルダーを差し出す
設定ファイルは要りません。index.htmlのあるフォルダーへ行って一行打つだけ — 同じネットワークの誰かにページを見せるいちばん速い方法です。
# serve this folder's index.html on port 8080
caddy file-server --listen :8080
# add a directory listing for folders without an index.html
caddy file-server --listen :8080 --browse
Caddyfile
長く置くものならCaddyfileを書きます。ただの文字ファイルで、サイト一つが四行で終わります。
example.com {
root * /var/www/html
file_server
encode gzip zstd
}
example.com {サイトの住所です。ここに本物のドメインを書けばCaddyがHTTPS証明書を自分で用意します。localhostと書いても、Caddyは自分で作った証明書でHTTPSを使います。ただのhttpだけにしたいなら、http://localhostや:80のように書きます。root * /var/www/htmlファイルがディスクのどこにあるかです。星印はこのサイトのすべてのパスに当てはめるという意味です。file_serverそのファイルを実際に差し出せ、という行です。この行がないとCaddyはリクエストに何も返しません。encode gzip zstd送る前にレスポンスを圧縮します。一語ですが、遅い回線ではページが目に見えて速く届きます。
CaddyはLet's Encryptに無料の証明書を求めます。そのためには、ドメインのが自分の機械を指している必要があります。また、80番と443番のポートに外から届く必要があります。前にいる別のプロキシがそのポートを代わりに受けていると、確認に失敗し、証明書は届きません。最初のCaddy設定で鍵が緑にならない、いちばんよくある理由です。
リバースプロキシ — リクエストを渡す
たいていのサイトはディスク上のファイルではなく、どこかのローカルポートで耳を澄ませているアプリケーションです。は外からのリクエストを受けてそのアプリへ渡し、答えを返すサーバーです。一行で済みます。
example.com {
reverse_proxy 127.0.0.1:8080
}
files.example.com {
root * /srv/files
file_server browse
}
一台の機械が複数のサイトを抱える方法もこれです。443番ポートは一つのプログラムしか持てないのでCaddyがそれを握り、訪問者がどのドメインを求めたかを読んで、それぞれ別のアプリへ回します。
必要なコマンド
| コマンド | 働き |
|---|---|
caddy run | 前面で動かし、記録をターミナルに出します。設定をまだ整えている間はこちらが欲しいはずです。 |
caddy start | 背後へ回してプロンプトを返します。ちょっと見せるには十分ですが、きちんとしたサーバーはシステムサービスとして動かします。 |
caddy reload | 接続を一つも切らずに、変えた設定を読み直します。Caddyfileを直したからといって再起動は要りません。 |
caddy fmt --overwrite | ファイルの書式をその場で標準の形に整えます。Caddyfileは字下げが違っても動きますが、書式がずれているとCaddyが警告を出します。 |
caddy validate | 何も起動せずに設定の誤りを調べます。動いているサーバーをreloadする前に、まずこれを回します。 |
SONUBのサーバーでは前段のCaddy一つが80番と443番を握り、ドメイン名で道を分けます。本サイトはLaravelアプリのへ、ダウンロード用のサブドメインはファイルを出す小さなCaddyコンテナへ渡します。上の例のようにサイトごとにブロックを一つ足せばよく、ポートを奪い合う二つ目のプログラムもありません。
Docker — 箱に入れたCaddyで、本物のサイトをインターネットへ
は、プログラムと、それが動くのに必要なものすべてを、一つのに詰め込みます。ライブラリ、設定、小さなオペレーティングシステムまで入ります。そして、そのイメージをコンテナとして動かします。コンテナは自分のノートパソコンでも借りたサーバーでも同じように動きます。だから「自分のパソコンでは動くのに」という昔ながらの言い訳がなくなります。
Caddyはもともとファイル一つなのに、なぜわざわざDockerを使うのでしょう。本物のサーバーでは、すぐに複数のものが一緒に動くようになるからです — サイト、アプリ、データベース。Dockerはその一つひとつに専用の箱を与え、同じ数個のコマンドで起動・停止・更新・削除できるようにします。サーバーにほかのものを入れる必要はなく、隣の箱を邪魔することもありません。
最初に知っておきたい四つの言葉
- イメージプログラムと、それに必要なものすべてを凍らせた包みです。ダウンロードする(caddy:2)か、Dockerfileというレシピのファイルから自分で作ります。動いている間に変わることはありません。
- コンテナ動いているイメージです。一つのイメージから多くのコンテナを起動でき、コンテナを消してもイメージには手が触れません — 次に起動すると新品の状態から始まります。
- ポートマッピングコンテナには、外からは見えない自分だけのポートがあります。-p 8080:80は自分のパソコンの8080番ポートをコンテナの中の80番ポートにつなぎ、ブラウザーが届くようにします。
- ボリュームコンテナが自分の中に書いたファイルは、コンテナを消すと一緒に消えます。ボリューム、またはコンテナにつないだ自分のフォルダーは、残り続ける保存場所です — ページ、設定、そしてCaddyの証明書がそこに住みます。
Dockerのインストール
MacやWindowsのパソコンでは、docker.comからDocker Desktopをインストールして一度起動しておきます。するとターミナルでdockerコマンドが使えるようになります。Linuxサーバーでは、Docker公式のスクリプトが一行ですべてを入れてくれます。先へ進む前に、確認用のコマンドが二つとも答えを返すことを確かめてください。
# macOS · Windows: install Docker Desktop from docker.com and start it once
# Ubuntu / Debian server: Docker's official install script
curl -fsSL https://get.docker.com | sh
# check that both commands answer
docker --version
docker compose version
コマンド一つでCaddy
index.htmlのあるフォルダーへ行き、下の一行を実行します。初回はDockerがCaddyのイメージをダウンロードし、二回目からは一秒ほどで起動します。http://:8080を開けば、自分のページがそこにあります。
# serve this folder at http://localhost:8080 (Control-C stops it)
docker run --rm -p 8080:80 -v "$PWD":/usr/share/caddy caddy:2
docker runイメージからコンテナを作り、起動します。--rm止まったらすぐにコンテナを消します。止まったコンテナが溜まっていかないようにするためです。イメージは残るので、次の実行も同じく速く始まります。-p 8080:80自分のパソコンの8080番ポートを、Caddyが耳を澄ませているコンテナの中の80番ポートにつなぎます。左の数字は自由に選べ、右の数字はイメージが決めています。-v "$PWD":/usr/share/caddy今のフォルダー($PWD)を、コンテナの中では/usr/share/caddyとして見せます — 公式イメージが標準で差し出すフォルダーです。ここでファイルを直せば、次の再読み込みで変化が見えます。caddy:2実行するイメージ — 公式のCaddyイメージで、2.xのどれかの版です。:2のタグを付けないとDockerはlatestを取ってくるため、ある日突然、足元で大きな版に切り替わるかもしれません。
Control-Cで止めます。ちょっと見るには最高ですが、コマンドが長く、ターミナルを閉じた瞬間に忘れられてしまいます。長く置くものはファイルに書き残します — それが次のプロジェクトです。
プロジェクト1 — HTMLのホームページを、フォルダーからインターネットへ
プロジェクト全体は小さなファイル三つです。index.htmlはページ、CaddyfileはCaddyが何を差し出すか、はDockerがCaddyをどう動かすかを書きます。Docker Composeがそのファイルを読むので、長いdocker runコマンドを打ち直す必要はありません。
my-site/
├── compose.yaml
├── conf/
│ └── Caddyfile
└── site/
└── index.html
services:
caddy:
image: caddy:2
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy
- ./site:/srv
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
compose.yamlを一行ずつ
image: caddy:2実行するイメージ — 先ほどと同じ公式のCaddyイメージです。restart: unless-stoppedコンテナが落ちたりサーバーが再起動したりしても、わざと止めるまではDockerが自動でもう一度起動します。"80:80" · "443:443"機械の80番(http)と443番(https)のポートをCaddyに開きます。Caddyが証明書を受け取り、httpの訪問者をhttpsへ回すには、両方が必要です。"443:443/udp"もう一度443番ポートですが、今度はUDPです。ブラウザーがHTTP/3を使えるようになります。書かなくても害はなく、その場合サイトはHTTP/2で動きます。./conf:/etc/caddy自分のconfフォルダーが/etc/caddyになります。CaddyはここでCaddyfileを探します。Caddyの公式文書は、ファイル一つではなくフォルダーごとつなぎます。多くのエディターは、保存のときにファイルを新しいものと差し替えます。ファイル一つだけをつなぐと、古い中身が見え続けてしまうのです。./site:/srv自分のsiteフォルダーが、Caddyfileが差し出す/srvになります。ディスク上のページを直せば、再起動しなくてもCaddyがすぐ新しい版を差し出します。caddy_data:/dataDockerが預かってくれる、名前付きのボリュームです。CaddyはHTTPS証明書と鍵をここに置くので、コンテナを消して作り直しても生き残ります。隣のcaddy_configも、Caddyの自動設定を同じように守ります。
まず自分のパソコンで試す
Caddyfileの住所の欄に:80と書きます。「どんな名前で尋ねられても80番ポートで答えよ」という意味で、証明書のない素のhttpです。ドメイン名を持たないパソコンには、これがちょうど合います。そのあと起動して、http://localhostを開きます。
:80 {
root * /srv
file_server
}
cd my-site
docker compose up -d # start Caddy in the background
open http://localhost # or type the address into a browser
docker compose logs -f # watch requests arrive (Control-C stops watching)
インターネットに載せる
- サーバーを借りる公開のあるなら何でもかまいません — 小さく安いもので十分です。上のスクリプトでDockerを入れ、ファイアウォールが80番と443番のポートを通すことを確かめます。
- ドメインをサーバーに向けるドメイン名を買ったところで、DNSにAレコードを追加します — 名前はexample.com、値はサーバーのIPアドレスです。変更がインターネット全体に行き渡るまで、数分から数時間かかることがあります。
- 本物の住所を書くCaddyfileの:80を自分のドメイン名に置き換えます。この一か所の変更が自動HTTPSのスイッチを入れます — Caddyが証明書を求め、入れ、更新し、httpの訪問者をhttpsへ回します。
- コピーして起動するrsync(変わったファイルだけを選んでコピーするコマンド)でフォルダーをサーバーにコピーし、ssh(別のコンピューターのターミナルに接続するコマンド)でログインして、docker compose up -dで起動します。記録を見守り、証明書を取得したという行が出れば、サイトは公開されています。
example.com {
root * /srv
file_server
encode gzip zstd
}
下の203.0.113.10は例のために取っておかれた住所なので、自分のサーバーのIPに置き換えてください。あとでページを変えたいときは、index.htmlを直して同じrsyncをもう一度実行します。siteフォルダーはコンテナにつながっているので、何も再起動しなくても次の再読み込みで新しいページが出ます。
# on your computer: copy the project folder to the server
rsync -av my-site/ root@203.0.113.10:~/my-site/
# on the server: start it — Caddy fetches the HTTPS certificate by itself
ssh root@203.0.113.10
cd ~/my-site
docker compose up -d
docker compose logs -f caddy
プロジェクト2 — Reactのホームページ
は、小さく分けて使い回せる部品(コンポーネント)でページを組み立てるライブラリです。公開するうえで大事なのは一つだけ — Reactのプロジェクトはそのままでは差し出しません。の段階でdistフォルダーの中のふつうのHTML・CSS・JavaScriptファイルに変わり、そこから先はプロジェクト1と同じ仕事です。
下のコマンドには、自分のパソコンにが入っている必要があります。Viteは、すぐに動くReactプロジェクトを作ります。 run devは作業中の画面を見せ、npm run buildは完成したサイトを作ります。
# needs Node.js (nodejs.org) — create a React project with Vite
npm create vite@latest my-react -- --template react
cd my-react
npm install
npm run dev # development server at http://localhost:5173
npm run build # the finished site is written to dist/
方法A — 自分のパソコンでビルドし、結果だけをコピーする
いちばん簡単な道は、プロジェクト1をそのまま使い回すことです。ビルドしたら、distフォルダーだけをサーバーのsiteフォルダーにコピーします。--deleteは、今回のビルドにない前回のビルドのファイルを消すので、古い残りかすが溜まりません。
# way A: build here, copy only the result into Project 1's site folder
npm run build
rsync -av --delete dist/ root@203.0.113.10:~/my-site/site/
Reactのアプリは、一つのindex.htmlからすべてのページを自分で描きます。/aboutのような住所はディスク上のファイルではないので、助けがないと、誰かがそのページを再読み込みしたり共有されたリンクから開いたりした瞬間に、Caddyは404と答えます。try_filesはCaddyにこう伝えます。「ファイルがあればそれを、なければindex.htmlを渡して、Reactに正しいページを描かせよ」。
example.com {
root * /srv
encode gzip zstd
try_files {path} /index.html
file_server
}
方法B — 二段階のDockerfileで、Dockerにビルドさせる
より頑丈な道は、Dockerの中でビルドすることです。そうすればサーバーでも同僚のパソコンでもいつも同じようにビルドされ、サーバーにNodeを入れる必要もありません。Dockerfileは二段階です — 一段目はNodeでビルドし、二段目は結果だけを残します。
# stage 1: build the React app with Node
FROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# stage 2: only the finished files go into the Caddy image
FROM caddy:2
COPY Caddyfile /etc/caddy/Caddyfile
COPY --from=build /app/dist /srv
Dockerfileを一行ずつ
FROM node:24-alpine AS build一段目はNode.jsの入ったイメージから始まり、次の段がここからファイルを持っていけるように、この段にbuildという名前を付けます。COPY package*.json ./ · RUN npm ciまずパッケージの一覧だけをコピーし、ロックファイルに書かれたとおりの版を入れます。一覧が変わらない間はDockerがこの段階を使い回すので、次からのビルドは遅いインストールを飛ばせます。COPY . . · RUN npm run build残りのソースをコピーしてビルドします。完成したサイトは、build段階の中の/app/distにできます。FROM caddy:2二段目は、まっさらなCaddyイメージからもう一度始まります。コピーしない限り、一段目のものは何もついてきません。COPY --from=build /app/dist /srvbuild段階からビルドされたファイルだけを受け取り、Caddyfileが差し出す場所に置きます。Nodeとnode_modulesは置いていくので、最終的なイメージは小さいままです。
try_filesを入れたCaddyfileをDockerfileの隣に置き、下のcompose.yamlを使います。build: .は、イメージをダウンロードする代わりに「このフォルダーのDockerfileから作れ」という意味です。サーバーではコードをcloneすればコマンド一つでビルドと起動ができ、変更のたびにpullして同じコマンドをもう一度実行します。
services:
web:
build: .
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
# on your computer, inside my-react: keep node_modules and old builds out of the image
printf 'node_modules\ndist\n' > .dockerignore
git add .dockerignore && git commit -m "Add .dockerignore" && git push
# on the server: get the code, then build and start in one step
git clone https://github.com/you/my-react.git
cd my-react
docker compose up -d --build
# after you push a change: pull it and rebuild
git pull && docker compose up -d --build
よく使うコマンド
| コマンド | 働き |
|---|---|
docker compose up -d | compose.yamlにあるものをすべて背後で起動します。compose.yamlを変えたあとにもう一度実行すると、変わったものだけが作り直されます。 |
docker compose ps | このプロジェクトのコンテナと、それぞれが動いているかどうかを一覧にします。サイトが答えないとき、最初に確かめるものです。 |
docker compose logs -f | 記録をリアルタイムで追いかけます。証明書の問題、間違ったパス、クラッシュは、どれもまずここに現れます。Control-Cで止まるのは見守りだけで、サーバーは止まりません。 |
docker compose exec -w /etc/caddy caddy caddy reload | 動いているコンテナの中で、接続を一つも切らずにCaddyにCaddyfileを読み直させます。Caddyfileを直したあとに使います。 |
docker compose down | コンテナを止めて消します。名前付きのは残るので、証明書も生き残ります。-vを付けるとそれまで消えてしまうので、付けないでください。 |
docker compose pull | 新しいCaddyイメージをダウンロードします。続けてdocker compose up -dを実行すると、新しいほうに切り替わります。 |
証明書はcaddy_dataボリュームに保存されます。これが消えると、Caddyは起動するたびに証明書を新しく求めなければなりません。docker compose down -vを使ったり、compose.yamlにボリュームを書き忘れたりすると消えます。認証局は、一つのドメインが一週間に受け取れる証明書の数を制限しています。だから、うかつな再起動を数回するだけで、サイトが何日もHTTPSなしで過ごすことになりかねません。
自分で記録する欄です。ここで採点はしません。
この段階を理解したものとして記録しますか?
まだ取り消せないので、本当に理解できたときだけ記録してください。
理解したものとして記録しました
自分で記録する欄です。ここで採点はしません。