很早之前就听说过caddy这个开源的web服务器,但一直也没尝试过。最近刚好使用caddy配置了一个站点,发现真香!
一、Caddy简介
Caddy是使用Go语言编写的一款开源Web服务器和反向代理服务器,设计目标是提供易于使用且高效的性能。它不仅支持常见的HTTP/HTTPS协议,还可以作为反向代理服务器、负载均衡器、WebSocket支持等。它的灵活性和模块化的架构,使其能够根据不同需求扩展功能,特别适合用于容器化环境和微服务架构。
个人体验下来Caddy的有几个比较大的特点
第一点、默认启用HTTPS,Caddy集成了Let’s Encrypt,可以自动为你的网站申请、更新和管理SSL证书,无需任何额外操作,免去繁琐的配置和证书管理流程。
第二点、配置简洁,与传统的Web服务器相比,Caddy的配置文件极为简洁,使用简易的配置文件(Caddyfile),极大降低了新手的学习成本。
第三点,除了传统的Caddyfile和JSON配置文件外,Caddy还提供了通过REST API动态管理和变更其配置的能力。这个API使得我们能够在运行时更改Caddy的配置,而无需重新启动服务器或手动编辑配置文件。
第四点,现代化,通过默认启用HTTPS和REST API动态变更配置也能看出来,除此之外caddy还支持Prometheus metrics、默认使用结构化的json作为access日志。
对比传统的web服务器Nginx对比更能看出caddy的一系列特点
特性 | Caddy | Nginx |
---|---|---|
配置方式 | Caddyfile, JSON, REST API | Nginx配置文件(nginx.conf) |
自动HTTPS支持 | 是,默认启用自动TLS证书管理 | 否,需手动配置SSL证书 |
适用范围 | 7层(应用层),反向代理和Web服务,内置负载均衡 | 支持4层(传输层)和7层(应用层)反向代理、负载均衡等 |
扩展性 | 插件化架构,支持扩展 | 模块化架构,支持静态编译的模块 |
性能 | 较高(适合轻量应用) | 非常高(适合高并发应用) |
配置简洁性 | Caddyfile格式简洁,易于上手 | 配置相对复杂,灵活但不够直观 |
系统资源占用 | 较低 | 较低,适合高并发处理 |
编写语言 | Go语言 | C语言 |
Access日志格式 | 结构化,默认JSON格式,支持自定义 | 非结构化,默认标准日志格式,支持自定义 |
二、Caddy的基本用法
Caddy的安装和配置非常简便,下面是一些常见的配置示例。
1. Caddy的安装
Caddy可以通过多种方式进行安装,除了传统的安装方法,还可以通过Docker Compose来进行快速部署。
方法一:二进制安装
安装方式除了可以使用发行版提供的仓库之外
因为caddy使用Go编写,编译完成后只有一个二进制文件,所以也可以直接在官网或者github release页面进行下载,下载完成后把caddy移动到PATH下即可
直接启动Caddy
可以直接在命令行中手动启动 Caddy。运行以下命令:
caddy start
systemctl启动
官方也提供了systemd unit files,配置之后就可以使用systemd来启动了(限于篇幅这里就不介绍了)
也可以使用以下命令启动Caddy:
# 启动Caddy服务
sudo systemctl start caddy
# 设置Caddy开机自启
sudo systemctl enable caddy
这样,Caddy服务会在后台启动,并且会随系统开机自动启动。
如果想查看Caddy的运行状态,可以使用:
# 查看Caddy服务的状态
sudo systemctl status caddy
如果需要停止Caddy服务,可以执行:
# 停止Caddy服务
sudo systemctl stop caddy
方法二:使用Docker Compose安装
如果你希望通过Docker容器来运行Caddy,可以使用Docker Compose来进行安装和启动。首先,在项目目录下创建一个docker-compose.yml
文件,内容如下:
version: "3.8"
services:
caddy:
image: caddy:latest
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
environment:
- ACME_AGREE=true
volumes:
caddy_data:
caddy_config:
在上述配置中,
./Caddyfile
是你的Caddy配置文件,Docker容器将其挂载到Caddy的配置目录中。- 此外,
caddy_data
和caddy_config
用于持久化存储Caddy的TLS证书和配置文件。
启动Caddy服务:
docker-compose up -d
通过此方式,你可以轻松地将Caddy部署到Docker容器中,并且无需关心手动管理TLS证书,Caddy会自动处理。
2. 配置方式
Caddy的配置可以通过两种方式来管理:
- 配置文件方式:通过将配置写入Caddyfile或者JSON文件中,Caddy会自动加载配置,官方也提供了一系列的adapter来支持其他格式的配置文件
- API方式:通过Caddy的API接口动态修改配置,适用于更复杂的环境和自动化场景。
在实际应用中,但大多数人会选择 JSON + API 或 Caddyfile + CLI 的组合方式,不会混合使用两者,避免出现配置不一致。
通过Caddyfile文件配置Caddy
Caddyfile是Caddy最常用的配置文件格式,以简洁明了著称。大多数用户和大部分文档推荐使用这种格式来配置Caddy。
Caddyfile是一种基于块结构的配置格式,语法非常简洁且易于理解。每个配置项通常以站点名称(通常是域名)作为起始,然后是需要的配置项。Caddyfile的基本结构如下:
example.com {
reverse_proxy 127.0.0.1:3000
log {
output file /var/log/caddy/access.log {
mode 644
}
format json
}
}
在这个示例中,example.com
是配置的站点名称,后续的内容是针对该站点的配置项。
Caddyfile的配置项可以包括但不限于:
- 反向代理:将请求转发到后台服务。
- TLS/SSL配置:启用HTTPS并管理证书。
- 路径匹配和重定向:根据请求路径来定义不同的处理方式。
Caddyfile的每一行都代表一个配置项,它非常易于编写和阅读,且支持丰富的功能。
通过JSON文件配置Caddy
虽然Caddyfile格式更加简洁,但是在一些高级使用场景中,JSON格式的配置文件更加灵活和强大。特别是在需要动态配置或者通过API接口修改配置时,JSON格式是更常见的选择。一个Caddy JSON配置文件的例子如下:
{
"apps": {
"http": {
"servers": {
"example": {
"listen": [":80"],
"routes": [
{
"match": [
{
"host": ["example.com"]
}
],
"handle": [
{
"handler": "static_response",
"body": "Hello, world!"
}
]
}
]
}
}
}
}
}
尽管JSON格式更为复杂,但它支持更多的高级功能,如动态配置、分布式管理等。通常,开发者在需要与其他系统进行集成时会选择这种格式。
通过API配置Caddy
除了传统的Caddyfile和JSON配置文件外,Caddy还提供了通过REST API动态管理和变更其配置的能力
Caddy的REST API允许你通过HTTP请求来控制Caddy的配置、状态和TLS证书管理等,无需重新启动服务器或手动编辑配置文件
API默认情况下监听在Caddy的管理端口(默认为 localhost:2019
)。通过API,你可以对Caddy进行以下操作
1. 获取当前配置
你可以通过API请求来获取当前Caddy的配置。默认情况下,Caddy配置是以JSON格式返回的。
curl -X GET http://localhost:2019/config/
返回的JSON数据将展示当前Caddy的所有配置,类似于Caddyfile的配置内容。
2. 添加配置
如果你需要动态修改配置,可以通过PUT
请求来添加Caddy的配置。
举个例子,假设当前caddy没有加载任何配置文件,通过动态加载配置,创建一个server hello
,它监听2015端口,并且返回"Hello, world!"
curl localhost:2019/load \
-H "Content-Type: application/json" \
-d @- << EOF
{
"apps": {
"http": {
"servers": {
"hello": {
"listen": [":2015"],
"routes": [
{
"handle": [{
"handler": "static_response",
"body": "Hello, world!"
}]
}
]
}
}
}
}
}
EOF
curl localhost:2015
Hello, world!
此时你想动态的修改server hello
curl -X PATCH http://localhost:2019/config/apps/http/servers/hello/routes \
-H "Content-Type: application/json" \
-d '[
{
"handle": [
{
"handler": "static_response",
"body": "Hello from Caddy API!"
}
]
}
]'
curl localhost:2015
Hello from Caddy API!
这个请求会将一个修改静态响应,返回 Hello from Caddy API!
3. 删除站点配置
通过API,你也可以删除某个站点或相关配置。例如,删除一个指定的站点配置:
curl -X DELETE http://localhost:2019/config/apps/http/servers/hello
这将删除server hello
的配置
三、 常见配置示例
为了简化配置过程和提升可读性,我们将在后续的示例中使用Caddyfile格式,以下是几个常见的配置示例。
直接回复
localhost:2017 { # 要server的站点名,不写端口则默认443(https)或者80(http)
respond "Hello, world!" # 直接响应内容
}
如果配置只有一行,{}
在caddyfile中是可以省略的。但我还是习惯用{}
包裹
localhost:2017
respond "Hello, world!"
配置静态文件
localhost:2016 { # 要server的站点名,不写端口则默认443(https)或者80(http)
root * /var/www/mysite # 静态文件的根路径
file_server { # 静态文件处理
browse # 如果没有index文件,则展示目录浏览模式
hide .git # 隐藏 .git
precompressed zstd br gzip # 启用压缩
}
}
如果只有localhost:2016并且上面的如果file_server不需要配置其他选项的时候
localhost:2016
root * /var/www/mysite
file_server browse
配置反向代理
这个配置将所有访问example.com
的请求反向代理到本地的8080
端口。
example.com {
reverse_proxy localhost:8000
}
还可以针对不同的path进行反向代理
example.com {
reverse_proxy /bar localhost:8000 # example.com/bar的内容会被转发到localhost:8000/bar
reverse_proxy /foo localhost:8001 # example.com/foo的内容会被转发到localhost:8000/foo
}
还可以针对反向代理配置更复杂的策略,如改写请求与响应等
example.com {
reverse_proxy /bar localhost:8000 # example.com/bar的内容会被转发到localhost:8000/bar
reverse_proxy /foo { # 针对example.com/foo配置更复杂的策略
to localhost:8001 # 转发到localhost:8001
rewrite / # 改写path,example.com/foo会被转发成localhost:8001/
header_up X-Forwarded-For {remote} # 增加新的header:X-Forwarded-For,内容为client ip
}
}
配置负载均衡
example.com {
reverse_proxy / backend1.example.com backend2.example.com
}
此配置将请求负载均衡地分发到backend1.example.com
和backend2.example.com
。
负载均衡也类似,有很多参数可以设置
一个复杂的DEMO
# 要server的站点名,不写端口则默认443(https)或者80(http)
# 使用http://代表不启用https
http://localhost:8000 {
respond "Hello, world!"
}
http://localhost:8001 {
respond "{path}"
}
localhost:8002 {
# 记录所有路径的访问日志
log {
# 访问日志写入/path/to/access.log
output file /path/to/access.log {
# 设置日志文件的权限
mode 644
}
# 日志格式为json
format json
}
# 使用handle来匹配路径
# 它和下面等价
# reverse_proxy /lb/* localhost:8000 localhost:8001
handle /lb/* {
reverse_proxy localhost:8000 localhost:8001
}
handle /proxy/* {
reverse_proxy {
to localhost:8001 # 转发到localhost:8001
rewrite / # 改写path,/proxy/*会被转发到/*
header_up X-Forwarded-For {remote} # 增加新的header:X-Forwarded-For,内容为client ip
}
}
handle /static/* {
uri strip_prefix /static # 移除/static前缀
root * /var/www/mysite # 静态文件的根路径
file_server { # 静态文件处理
browse # 如果没有index文件,则展示目录浏览模式
hide .git # 隐藏 .git
precompressed zstd br gzip # 启用压缩
}
}
}
四. Caddy的重要持久化存储
在Caddy的配置中,有几个重要的持久化存储目录,它们用于存储TLS证书、配置文件和其他关键数据。理解这些存储路径的作用可以帮助你更好地管理和迁移Caddy的部署。
1. 自定义的配置文件
这个就不多说了,就是上文一直提到配置文件,你需要放置到合理的位置
对于容器内,默认配置文件位置在/etc/caddy/Caddyfile
。因此可以挂载这个文件来提供自定义的配置文件
# ...
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
2. Data Directory(数据目录)
Caddy会自动为每个网站生成并管理SSL/TLS证书,这些证书存储在Caddy的数据目录中。
默认情况下,如果设置了 XDG_DATA_HOME
环境变量,那么位置就是 $XDG_DATA_HOME/caddy/
,它是一个目录
没设置的话则取决于系统
OS | Data directory path |
---|---|
Linux, BSD | $HOME/.local/share/caddy |
Windows | %AppData%\Caddy |
macOS | $HOME/Library/Application Support/Caddy |
Plan 9 | $HOME/lib/caddy |
Android | $HOME/caddy (or /sdcard/caddy ) |
docker | /data |
这个目录用于存储所有与Caddy运行相关的数据,例如:
- TLS证书和私钥:Caddy会自动申请和续订证书,并将这些证书文件保存在数据目录中。
- 证书缓存:为了提高性能,Caddy会缓存证书验证和其他相关数据。
- ACME(自动证书管理环境)缓存:Caddy使用ACME协议与Let's Encrypt等证书颁发机构通信,该缓存存储了所有的ACME响应数据。
所以数据目录不能被视为缓存,其内容并非临时的,也不仅仅是为了性能。因此,在不了解其影响的情况下,不应清除数据目录的内容。
因此在容器中我们也需要挂载对应目录,不然重启之后数据就没了
# ...
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data # 数据目录
volumes:
caddy_data: # 这里使用了docker volume来存储
3. Configuration Directory(配置目录)
caddy会把最后一次有效的配置保存到该目录中,也就是说如果你通过API设置的配置也会被持久化到这里。
当 caddy run --resume
命令启动的时候,就可以继续使用之前的配置,这时候你的自定义配置文件是不生效的
如果设置了 XDG_CONFIG_HOME
环境变量, 位置在 $XDG_CONFIG_HOME/caddy
.
没设置的话则取决于系统
OS | Config directory path |
---|---|
Linux, BSD | $HOME/.config/caddy |
Windows | %AppData%\Caddy |
macOS | $HOME/Library/Application Support/Caddy |
Plan 9 | $HOME/lib/caddy |
docker | /config |
因此在容器中我们也需要挂载对应目录,不然重启之后数据就没了
# ...
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data # 数据目录
- caddy_config:/config # 配置目录
volumes:
caddy_data:
caddy_config: # 这里使用了docker volume来存储
总结
Caddy是一款易于使用、功能强大的现代Web服务器,适合快速部署,尤其是自动申请和续期Let’s Encrypt的HTTPS证书,真香!其与Nginx相比,最大的优势在于配置简便、内置HTTPS支持及开箱即用的功能,尤其适合中小型网站和开发环境。
✨ 微信公众号【凉凉的知识库】同步更新,欢迎关注获取最新最有用的知识 ✨