一個比 Nginx 功能更強大的 Web 服務器
原文:https://mritd.com/2021/01/07/lets-start-using-caddy2/
Caddy 簡介
Caddy 是一個 Go 編寫的 Web 服務器,類似于 Nginx,Caddy 提供了更加強大的功能,隨著 v2 版本發(fā)布 Caddy 已經(jīng)可以作為中小型站點 Web 服務器的另一個選擇;相較于 Nginx 來說使用 Caddy 的優(yōu)勢如下:
自動的 HTTPS 證書申請(ACME HTTP/DNS 挑戰(zhàn)) 自動證書續(xù)期以及 OCSP stapling 等 更高的安全性包括但不限于 TLS 配置以及內(nèi)存安全等 友好且強大的配置文件支持 支持 API 動態(tài)調(diào)整配置(有木有人可以搞個 Dashboard) 支持 HTTP3(QUIC) 支持動態(tài)后端,例如連接 Consul、作為 k8s ingress 等 后端多種負載策略以及健康檢測等 本身 Go 編寫,高度模塊化的系統(tǒng)方便擴展(CoreDNS 基于 Caddy1 開發(fā)) ……
就目前來說,Caddy 對于我個人印象唯一的缺點就是性能沒有 Nginx 高,但是這是個仁者見仁智者見智的問題;相較于提供的這些便利性,在性能可接受的情況下完全有理由切換到 Caddy。
編譯 Caddy2
注意: 在 Caddy1 時代,Caddy 官方發(fā)布的預編譯二進制文件是不允許進行商業(yè)使用的,Caddy2 以后已經(jīng)全部切換到 Apache 2.0 License。
在默認情況下 Caddy2 官方提供了預編譯的二進制文件,以及自定義 build 下載頁面,不過對于需要集成一些第三方插件時,我們?nèi)孕璨捎霉俜教峁┑?xcaddy 來進行自行編譯;以下為具體的編譯過程:
Golang 環(huán)境安裝
本部分編譯環(huán)境默認為 Ubuntu 20.04 系統(tǒng),同時使用 root 用戶,其他環(huán)境請自行調(diào)整相關(guān)目錄以及配置;編譯時自行處理好科學上網(wǎng)相關(guān)配置,也可以直接用國外 VPS 服務器編譯。
首先下載 go 語言的 SDK 壓縮包,其他平臺可以從 https://golang.org/dl/ 下載對應的壓縮包:
wget?https://golang.org/dl/go1.15.6.linux-amd64.tar.gz
下載完成后解壓并配置相關(guān)變量:
#?解壓
tar?-zxvf?go1.15.6.linux-amd64.tar.gz
#?移動到任意目錄
mkdir?-p?/opt/devtools
mv?go?/opt/devtools/go
#?創(chuàng)建?go?相關(guān)目錄
mkdir?-p?${HOME}/gopath/{src,bin,pkg}
#?調(diào)整變量配置,將以下變量加入到?shell?初始化配置中
#?bash?用戶請編輯?~/.bashrc
#?zsh?用戶請編輯?~/.zshrc
export?GOROOT='/opt/devtools/go'
export?GOPATH="${HOME}/gopath"
export?GOPROXY='https://goproxy.cn'?#?如果已經(jīng)解決了科學上網(wǎng)問題,GOPROXY?變量可以刪除,否則可能會起反作用
export?PATH="${GOROOT}/bin:${GOPATH}/bin:${PATH}"
#?讓配置生效
#?bash?用戶替換成?~/.basrc
#?重新退出登錄也可以
source?~/.zshrc
配置完成后,應該在命令行執(zhí)行 go version 并有以下成功返回:
bleem???~?go?version
go?version?go1.15.6?linux/amd64
安裝 xcaddy
按照官方文檔直接命令行執(zhí)行 go get -u github.com/caddyserver/xcaddy/cmd/xcaddy 安裝即可:
bleem???~?go?get?-u?github.com/caddyserver/xcaddy/cmd/xcaddy
go:?downloading?github.com/caddyserver/xcaddy?v0.1.7
go:?found?github.com/caddyserver/xcaddy/cmd/xcaddy?in?github.com/caddyserver/xcaddy?v0.1.7
go:?downloading?github.com/Masterminds/semver/v3?v3.1.0
go:?github.com/Masterminds/semver/v3?upgrade?=>?v3.1.1
go:?downloading?github.com/Masterminds/semver/v3?v3.1.1
.....
安裝完成后應當在命令行可以直接執(zhí)行 xcaddy 命令:
#?xcaddy?并沒有提供完善的命令行支持,所以?`--help`?報錯很正常
bleem????~?xcaddy?--help
go:?cannot?match?"all":?working?directory?is?not?part?of?a?module
2021/01/07?12:15:56?[ERROR]?exec?[go?list?-m?-f={{if?.Replace}}{{.Path}}?=>?{{.Replace}}{{end}}?all]:?exit?status?1:
編譯 Caddy2
編譯之前系統(tǒng)需要安裝 jq、curl、git 命令,沒有的請使用
apt?install?-y?curl?git?jq?
命令安裝;
自行編譯的目的是增加第三方插件方便使用,其中官方列出的插件可以從 Download 頁面獲取到:
其他插件可以從 GitHub 上尋找或者自行編寫,整理好這些插件列表以后只需要使用 xcaddy 編譯即可:
#?獲取最新版本號,其實直接去?GitHub?realse?頁復制一下就行
#?這里轉(zhuǎn)化為腳本是為了方便自動化
export?version=$(curl?-s?"https://api.github.com/repos/caddyserver/caddy/releases/latest"?|?jq?-r?.tag_name)
#?使用?xcaddy?編譯
xcaddy?build?${version}?--output?./caddy_${version}?\
????????--with?github.com/abiosoft/caddy-exec?\
????????--with?github.com/caddy-dns/cloudflare?\
????????--with?github.com/caddy-dns/dnspod?\
????????--with?github.com/caddy-dns/duckdns?\
????????--with?github.com/caddy-dns/gandi?\
????????--with?github.com/caddy-dns/route53?\
????????--with?github.com/greenpau/caddy-auth-jwt?\
????????--with?github.com/greenpau/caddy-auth-portal?\
????????--with?github.com/greenpau/caddy-trace?\
????????--with?github.com/hairyhenderson/caddy-teapot-module?\
????????--with?github.com/kirsch33/realip?\
????????--with?github.com/porech/caddy-maxmind-geolocation?\
????????--with?github.com/caddyserver/format-encoder?\
????????--with?github.com/mholt/caddy-webdav
編譯過程日志如下所示,稍等片刻后將會生成編譯好的二進制文件:

編譯成功后可以通過 list-modules 子命令查看被添加的插件是否成功編譯到了 caddy 中:
bleem????~?./caddy_v2.3.0?list-modules
admin.api.load
admin.api.metrics
caddy.adapters.caddyfile
caddy.listeners.tls
caddy.logging.encoders.console
caddy.logging.encoders.filter
caddy.logging.encoders.filter.delete
caddy.logging.encoders.filter.ip_mask
caddy.logging.encoders.formatted
caddy.logging.encoders.json
caddy.logging.encoders.logfmt
caddy.logging.encoders.single_field
caddy.logging.writers.discard
caddy.logging.writers.file
caddy.logging.writers.net
caddy.logging.writers.stderr
caddy.logging.writers.stdout
caddy.storage.file_system
dns.providers.cloudflare
dns.providers.dnspod
dns.providers.duckdns
dns.providers.gandi
dns.providers.route53
exec
http
http.authentication.hashes.bcrypt
http.authentication.hashes.scrypt
http.authentication.providers.http_basic
http.authentication.providers.jwt
......
安裝 Caddy2
宿主機安裝
宿主機安裝 Caddy2 需要使用 systemd 進行守護,幸運的是 Caddy2 官方提供了各種平臺的安裝包以及 systemd 配置文件倉庫;目前推薦的方式是直接采用包管理器安裝標準版本的 Caddy2,然后替換自編譯的可執(zhí)行文件:
#?安裝標準版本?Caddy2
sudo?apt?install?-y?debian-keyring?debian-archive-keyring?apt-transport-https
curl?-1sLf?'https://dl.cloudsmith.io/public/caddy/stable/cfg/gpg/gpg.155B6D79CA56EA34.key'?|?sudo?apt-key?add?-
curl?-1sLf?'https://dl.cloudsmith.io/public/caddy/stable/cfg/setup/config.deb.txt?distro=debian&version=any-version'?|?sudo?tee?-a?/etc/apt/sources.list.d/caddy-stable.list
sudo?apt?update
sudo?apt?install?caddy
#?替換二進制文件
systemctl?stop?caddy
rm?-f?/usr/bin/caddy
mv?./caddy_v2.3.0?/usr/bin/caddy
Docker 安裝
Docker 用戶可以通過 Dockerfile 自行編譯 image,目前我編寫了一個基于 xcaddy 的 Dockerfile,如果有其他插件需要集成自行修改重新編譯即可;當前 Dockerfile 預編譯的鏡像已經(jīng)推送到了 Docker Hub 中,鏡像名稱為 mritd/caddy。
配置 Caddy2
Caddy2 的配置文件核心采用 json,但是 json 可讀性不強,所以官方維護了一個轉(zhuǎn)換器,抽象出稱之為 Caddyfile 的新配置格式;關(guān)于 Caddyfile 的完整語法請查看官方文檔 https://caddyserver.com/docs/caddyfile,本文僅做一些基本使用的樣例。
配置片段
Caddyfile 支持類似代碼中 function 一樣的配置片段,這些配置片段可以在任意位置被 import,同時可以接受參數(shù),以下為配置片斷示例:
#?括號內(nèi)為片段名稱,可以自行定義
(TLS)?{
????protocols?tls1.2?tls1.3
????ciphers?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
}
#?在任意位置可以引用此片段從而達到配置復用
import?TLS
配置模塊化
import 指令除了支持引用配置片段以外,還支持引用外部文件,同時支持通配符,有了這個命令以后我們就可以方便的將配置文件進行模塊化處理:
#?引用外部的?/etc/caddy/*.caddy
import?/etc/caddy/*.caddy
站點配置
針對于站點域名配置,Caddyfile 比較自由化,其格式如下:
地址?{
????站點配置
}
關(guān)于這個 “地址” 接受多種格式,以下都為合法的地址格式:
localhost
example.com
:443
http://example.com
localhost:8080
127.0.0.1
[::1]:2015
example.com/foo/*
*.example.com
http://
環(huán)境變量
Caddyfile 支持直接引用系統(tǒng)環(huán)境變量,通過此功能可以將一些敏感信息從配置文件中剔除:
#?引用環(huán)境變量?GANDI_API_TOKEN
dns?gandi?{$GANDI_API_TOKEN}
配置片段參數(shù)支持
針對于配置片段,Caddyfile 還支持類似于函數(shù)代碼的參數(shù)支持,通過參數(shù)支持可以讓外部引用時動態(tài)修改配置信息:
(LOG)?{
????log?{
????????format?json??{
????????????time_format?"iso8601"
????????}
????????#?"{args.0}"?引用傳入的第一個參數(shù),此處用于動態(tài)傳入日志文件名稱
????????output?file?"{args.0}"?{
????????????roll_size?100mb
????????????roll_keep?3
????????????roll_keep_for?7d
????????}
????}
}
#?引用片段
import?LOG?"/data/logs/mritd.com.log"
自動證書申請
在啟動 Caddy2 之前,如果目標域名(例如: www.example.com)已經(jīng)解析到了本機,那么 Caddy2 啟動后會嘗試自動通過 ACME HTTP 挑戰(zhàn)申請證書;如果期望使用 DNS 的方式申請證書則需要其他 DNS 插件支持,比如上面編譯的 --with github.com/caddy-dns/gandi 為 gandi 服務商的 DNS 插件;關(guān)于使用 DNS 挑戰(zhàn)的配置編寫方式需要具體去看其插件文檔,目前 gandi 的配置如下:
tls?{
?dns?gandi?{env.GANDI_API_TOKEN}
}
配置完成后 Caddy2 會通過 ACME DNS 挑戰(zhàn)申請證書,值得注意的是即使通過 DNS 申請證書默認也不會申請泛域名證書,如果想要調(diào)整這種細節(jié)配置請使用 json 配置或管理 API。
完整模塊化配置樣例
了解了以上基礎(chǔ)配置信息,我們就可以實際編寫一個站點配置了;以下為本站的 Caddy 配置樣例:
目錄結(jié)構(gòu):
caddy
├──?Caddyfile
├──?mritd.com.caddy
└──?mritd.me.caddy
Caddyfile
Caddyfile 主要包含一些通用的配置,并將其抽到配置片段中,類似于 nginx 的 nginx.conf 主配置;在最后部分通過 import 關(guān)鍵字引入其他具體站點配置,類似 nginx 的 vhost 配置。
(LOG)?{
????log?{
????????#?日志格式參考?https://github.com/caddyserver/format-encoder?插件文檔
????????format?formatted?"[{ts}]?{request>remote_addr}?{request>proto}?{request>method}?<-?{status}?->?{request>host}?{request>uri}?{request>headers>User-Agent>[0]}"??{
????????????time_format?"iso8601"
????????}
????????output?file?"{args.0}"?{
????????????roll_size?100mb
????????????roll_keep?3
????????????roll_keep_for?7d
????????}
????}
}
(TLS)?{
????#?TLS?配置采用?https://mozilla.github.io/server-side-tls/ssl-config-generator/?生成,SSL?Labs?評分?A+
????protocols?tls1.2?tls1.3
????ciphers?TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256?TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256?TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384?TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384?TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256?TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
}
(HSTS)?{
????#?HSTS?(63072000?seconds)
????header?/?Strict-Transport-Security?"max-age=63072000"
}
(ACME_GANDI)?{
????#?從環(huán)境變量獲取?GANDI_API_TOKEN
????dns?gandi?{$GANDI_API_TOKEN}
}
#?聚合上面的配置片段為新的片段
(COMMON_CONFIG)?{
????#?壓縮支持
????encode?zstd?gzip
????#?TLS?配置
????tls?{
????????import?TLS
????????import?ACME_GANDI
????}
????#?HSTS
????import?HSTS
}
#?開啟?HTTP3?實驗性支持
{
????servers?:443?{
????????protocol?{
????????????experimental_http3
????????}
????}
}
#?引入其他具體的站點配置
import?/etc/caddy/*.caddy
mritd.com.caddy
mritd.com.caddy 為主站點配置,主站點配置內(nèi)主要編寫一些路由規(guī)則,TLS 等都從配置片段引入,這樣可以保持統(tǒng)一。
www.mritd.com?{
????#?重定向到?mritd.com(默認?302)
????redir?https://mritd.com{uri}
????#?日志
????import?LOG?"/data/logs/mritd.com.log"
????#?TLS、HSTS、ACME?等通用配置
????import?COMMON_CONFIG
}
mritd.com?{
????#?路由
????route?/*?{
????????reverse_proxy?mritd_com:80
????}
????#?日志
????import?LOG?"/data/logs/mritd.com.log"
????#?TLS、HSTS、ACME?等通用配置
????import?COMMON_CONFIG
}
mritd.me.caddy
mritd.me.caddy 為老站點配置,目前主要將其 301 到新站點即可。
www.mritd.me?{
????#?重定向到?mritd.com
????#?最后的?"code"?支持三種參數(shù)
????#?temporary?=>?302
????#?permanent?=>?301
????#?html?=>?HTML?document?redirect
????redir?https://mritd.com{uri}?permanent
????#?日志
????import?LOG?"/data/logs/mritd.com.log"
????#?TLS、HSTS、ACME?等通用配置
????import?COMMON_CONFIG
}
mritd.me?{
????#?重定向
????redir?https://mritd.com{uri}?permanent
????#?日志
????import?LOG?"/data/logs/mritd.com.log"
????#?TLS、HSTS、ACME?等通用配置
????import?COMMON_CONFIG
}
啟動與重載
配置文件編寫完成后,通過 systemctl start caddy 可啟動 caddy 服務器;每次配置修改后可以通過 systemctl reload caddy 進行配置重載,重載期間 caddy 不會重啟(實際上調(diào)用 caddy reload 命令),當配置文件書寫錯誤時,重載只會失敗,不會影響正在運行的 caddy 服務器。
總結(jié)
本文只是列舉了一些簡單的 Caddy 使用樣例,在強大的插件配合下,Caddy 可以實現(xiàn)各種 “神奇” 的功能,這些功能依賴于復雜的 Caddy 配置,Caddy 配置需要仔細閱讀官方文檔,關(guān)于 Caddyfile 的每個配置段在文檔中都有詳細的描述。
值得一提的是 Caddy 本身內(nèi)置了豐富的插件,例如內(nèi)置 “file_server”、內(nèi)置各種負載均衡策略等,這些插件組合在一起可以實現(xiàn)一些復雜的功能;Caddy 是采用 go 編寫的,官方也給出了詳細的開發(fā)文檔,相較于 Nginx 來說通過 Lua 或者 C 來開發(fā)編寫插件來說,Caddy 的插件開發(fā)上手要容易得多;Caddy 本身針對數(shù)據(jù)存儲、動態(tài)后端、配置文件轉(zhuǎn)換等都內(nèi)置了擴展接口,這為有特定需求的擴展開發(fā)打下了良好基礎(chǔ)。
最終總結(jié),綜合來看目前 Caddy2 的性能損失可接受的情況下,相較于 Nginx 絕對是個絕佳選擇,各種新功能都能夠滿足現(xiàn)代化 Web 站點的需求,真香警告。
推薦閱讀:
5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復「1024」,即可免費獲?。?!



