最近把自己和公司的 Web 服务器都从 Nginx 切换成了 Caddy,主要原因是 HTTPS 证书申请太麻烦。之前的 Nginx 如果使用 Let’s Encrypt 的证书,代理程序会自动修改网站的配置文件,不太方便通过 git 来管理这些配置。刚好更换 Caddy 一起解决了这些问题。几年前就看过 Caddy 的相关介绍,但是没有真正使用。现在最新的 Caddy 已经到 2.0 版本,这次实际使用体验下来,Caddy 吸引我的几个功能点如下:
- Caddyfile,比 JSON 和 Nginx 的默认配置方式有好玩
- 自动 HTTPS,默认 DV 证书,可以支持到通配符证书
- Go 开发,写自己的模块比 Nginx 的 Lua 模块方便
- 不停机热更新
Caddyfile
Caddyfile 是 Caddy 的配置文件,官方文档定义是人类方便使用的一种配置文件(这句话让我想到以前的 Toml 😊),我赞同这个点。Caddyfile 确实比 JSON 有意思多了!官方有个简单的对比表格 JSON vs. Caddyfile,我复制到下面:
JSON | Caddyfile |
---|---|
Easy to generate | Easy to craft by hand |
Easily programmable | Awkward to automate |
Extremely expressive | Moderately expressive |
Full range of Caddy functionality | Most of Caddy functionality |
Allows config traversal | Cannot traverse within Caddyfile |
Partial config changes | Whole config changes only |
Can be exported | Cannot be exported |
Compatible with all API endpoints | Compatible with some API endpoints |
Documentation generated automatically | Documentation is hand-written |
Ubiquitous | Niche |
More efficient | More computational |
Kind of boring | Kind of fun |
自己配置过程中,主要按照官网的图例(如下)进行修改。各个部分参照着进行修改,可以很快地完成配置。
不同于 Nginx 需要拆分很多个文件,使用 Caddy 将一个站点的全部配置放在一个 Caddyfile 内。得益于 Caddyfile 精简的结构和强大的表达能力,Caddyfile 可以由很少的内容表示复杂的配置!一个 PR 将成百上千行的 Nginx 配置替换成几十行 Caddyfile,感觉超级棒!
这个站点的配置如下:
1 | ldsink.com { |
Caddy 也可以很方便的代理 WebSocket 的流量,在某些时候很有用,如下:
1 | @websockets { |
可能因为 Caddy 是使用 Go 开发,Caddyfile 保留了一些 Go 的特点,他的缩进使用 Tab,不是我们常用 Python 的 4 个空格。修改完配置文件以后,可以使用 Caddy 自带的 fmt 命令加上 --overwrite
格式化并覆盖配置文件(写这个文章前几天还是 -overwrite
,然后更新了,此时就是 --
了)。进入配置文件所在目录,执行如下命令:
caddy fmt –overwrite
自动 HTTPS,支持通配符证书
Caddy 的一大特点就是自动 HTTPS,默认的配置都是 HTTPS 的,只需要使用 80 和 443 端口就可以自动在 Let’s Encrypt 或者 ZeroSSL 签发证书并正确配置。Caddy 除了自动搞定开始的签发和配置,还可以在证书过期前自动进行续签。对于我们这种小站而言,只要 DNS 设置正确,就再也不用担心各种证书问题,Caddy 都能帮忙自动搞定。
Caddy 还支持修改 ACME 设置更改 SSL 证书的提供商,不过这个属于高级用法,我没用过,有需要的可以自己翻文档。这里介绍一下怎么通过插件把 Caddy 和 DNSPod 结合,自动签发通配符证书。
首先需要安装一个第三方模块,让 Caddy 可以调用 DNSPod 的接口。
caddy add-package github.com/caddy-dns/dnspod
然后需要去 DNSPod 申请一个 Token 供 Caddy 使用,链接如下:
https://console.dnspod.cn/account/token/token
申请好 Token 后,需要添加到 Caddyfile 内。可以结合模块的介绍,填入对应的配置。
1 | # 这里我做成了一个 snippet,方便重复使用 |
最后重新加载配置,自动的通配符证书应该就生效了。
caddy reload
这里特别夸一下,Caddy 支持不停机热更新。如果新的配置文件有错误,Caddy 会自动回滚到上一个有效的配置,保证服务持续可用。
目前的问题
前面夸了很多,Caddy 确实很好,但也不是十全十美,他也有一些小问题,这里也说明一下。使用过程中注意和规避掉这些缺点,Caddy 就可以在生产环境中替换 Nginx 使用。
首先明确一点,Caddy 本体就是个独立的可执行二进制文件。想要集成任何模块,做的事情其实是 Caddy 加上模块的源代码一起重新编译出一个新的二进制文件。这种方式让 Caddy 变得简单和轻量,某种意义上也带来了一些麻烦。
我最开始使用 Ubuntu 的包管理方式安装 Caddy,这种方式有个严重的问题:之后所有的更新(apt update && apt upgrade -y
),只会更新成不带第三方插件版本的最新的 Caddy。我们使用 caddy add-package
命令安装的任何第三方扩展,更新后都会丢失,例如上面的 DNSPod 模块。
Tips: 查看安装的模块可以使用命令
caddy list-modules
想要在更新成携带第三方模块版本的 Caddy,需要使用自带的升级命令:
caddy upgrade
我现在的做法是,通过 apt 安装完 Caddy 后,马上去 /etc/apt/sources.list.d
内删除源。只通过 apt 安装,后续的更新都走自带的升级命令。这样做,可以保持第三方模块存在。
不过这样做也有一个坑,需要注意:caddy 自带的更新,会将现在的可执行文件直接备份,然后下载新的文件到对面的目录。如果这个时候网络中断导致更新没有完成,那么 caddy 文件会损坏,需要手动把备份文件恢复。否则如果此时遇到 caddy 进程重启,新的 caddy 将无法启动。
更新时候的日志信息如下,遇到问题恢复也简单,把 .tmp
后缀的备份文件重新恢复成原始文件就好。
mv /usr/bin/caddy.tmp /usr/bin/caddy
1 | { |