Caddyfile 语法浅析
发现大部分人在切换 Caddy 时遇到的比较大的困难就是这个 Caddyfile 不知道怎么写,一开始我也是很懵逼的状态,今天决定写写这个 Caddyfile 配置语法,顺便自己也完整的学学。
一、Caddy 配置体系
在 Caddy1 时代,Caddy 自创了一种被称之为 Caddyfile 的配置文件格式,当然可以理解为创造了一种语法,这里面深入的说就涉及到了编译原理相关知识,这里不再展开细谈(因为我也不会);Caddyfile 由内部的语法解析器进行语法、词法分析最后 “序列化” 到 Go 的配置结构体中。
随着 Caddy 壮大,到了 Caddy2 时代人们已经并不满足于单纯的 Caddyfile 配置,因为学习 Caddyfile 是有代价的,负载均衡器选型的切换本身就代价很大,还要去花心思学习 Caddyfile 语法,这无异非常痛苦。所以 Caddy2 在经过取舍过后决定使用 json 作为内部标准配置,然后其他类型的配置通过 Config Adapters
将其转换为 json 再使用,而 Caddyfile 的 Adapter 作为官方支持的内置 Adapter 存在。
最终要说明的是: Caddyfile 里支持哪些指令是由 Caddyfile 的 Adapter 决定的,内部的 json 配置对应的指令名称可能跟 Caddyfile 不同,也可能内部 json 支持一些指令,而 Caddyfile 根本不支持。
二、Caddyfile 基本结构
开局一张图,文章全靠编(下面是官方的语法结构图)
2.1、全局选项
在一个 Caddyfile 内(空白文本文件),如果仅以两个大括号括起来的配置就是全局配置项,例如下面的配置:
1 |
|
那么一共有哪些全局配置项呢?当然是看 官方文档:
1 |
|
这些全局配置具体都什么意思这里就不细说了,请自行查阅文档;当然文档也可能并不一定准确,有些兴趣的可以去查看 Caddy 源码,这些都在源码中定义了 caddyconfig/httpcaddyfile/options.go:28
2.2、代码块
叫代码块可能不太恰当,也可以叫做配置块或配置片段;这是 Caddyfile 比较棒的一个功能,配置片段可以实现类似代码这种引用使用,方便组合配置文件;配置片段的语法如下:
1 |
|
下面是一个配置片段示例(不能运行,只是举例):
1 |
|
这种写法与下面的配置等价,目的就是增加配置的重用和规范化:
1 |
|
2.3、站点配置
站点配置是 Caddyfile 的核心中的核心,从开局的图上也可以看到,能在 “Top Level” 上存在的只有三种配置,其中就包含了这个站点配置块,站点配置块格式如下:
1 |
|
以下是两个合法的站点配置示例:
1 |
|
2.4、自定义匹配器
请求匹配器是 Caddy 内置的一种针对请求的过滤工具,有点类似于 nginx 配置中的 location /api {...}
,只不过 Caddyfile 中的匹配器更加强大;标准的请求匹配器列表如下:
- expression: 表达式匹配(CEL)
- file: 文件匹配
- header: 请求头匹配
- header_regexp: 请求头正则匹配
- host: 域名匹配
- method: HTTP 请求方法匹配
- not: 对其他匹配器取反
- path: 请求路径匹配
- path_regexp: 请求路径正则匹配
- protocol: 请求协议匹配
- query: 请求查询参数匹配
- remote_ip: 请求 IP 匹配
自定义命名匹配器的作用是组合多个标准匹配器,然后实现复用,自定义命名匹配器语法如下:
1 |
|
然后这个自定义的命名匹配器可以在其他位置引用:
1 |
|
三、Caddyfile 语法细节
3.1、Blocks
Caddyfile 中的配置块可以理解为代码中的作用域,其包含两个大括号范围内的所有配置:
1 |
|
当 Caddyfile 中只有一个站点配置,且不需要其他全局配置等信息时,Blocks 可以被省略,例如:
1 |
|
这个配置可以直接简写为:
1 |
|
这么做的目的是方便单站点快速配置,但是一般不常用也不推荐使用。在同一个 Caddyfile 中可以包含多个站点配置,只要地址不同即可:
1 |
|
3.2、Directives
指令是指描述站点配置的一些关键字,例如下面的站点配置文件:
1 |
|
在这个配置文件中 reverse_proxy
就是一个指令,同时指令还可能包含子指令(Subdirectives),下面的配置中 lb_policy
就是 reverse_proxy
的一个子指令:
1 |
|
3.3、Tokens and quotes
在 Caddyfile 被 Caddy 读取后,Caddy 会将配置文件解析为一个个的 Token;Caddyfile 中所有 Token 都认为是空格分割,所以如果某些指令需要传递参数时我们需要通过合理的空格和引号来确保 Token 正确解析:
1 |
|
如果某些参数需要包含空格,那么需要使用双引号包裹:
1 |
|
如果这个参数里需要包含双引号,只需要通过反斜线转义即可,例如 "\"a b\""
;如果有太多的双引号或者空格,可以使用 Go 语言中类似的反引号来定义 “绝对字符串”:
1 |
|
3.4、Addresses
Caddyfile 中的地址其实是一种很宽泛的格式,在上面讲站点配置时其实前面的字符串并不一定是域名,准确的说应该是地址:
1 |
|
在 Caddyfile 中以下格式全部都是合法的地址:
localhost
example.com
:443
http://example.com
localhost:8080
127.0.0.1
[::1]:2015
example.com/foo/*
*.example.com
http://
需要注意的是: 自动 HTTPS 是 Caddy 服务器的一个重要特性,但是自动 HTTPS 会隐式进行,除非在地址中明确的写明 http://example.com
这种格式时 Caddy 才会单纯监听 HTTP 协议,否则域名格式的地址 Caddy 都会进行 HTTPS 证书申请。
如果地址中指定了域名,那么只有匹配到域名的请求才会接受;例如地址为 localhost
的站点不会响应 127.0.0.1
方式的访问请求。同时地址中可以采用 *
作为通配符,通配符作用域仅在域名的英文句号 .
之内,意思就是说 *.example.com
会匹配 test.example.com
但不会匹配 abc.test.example.com
。
如果多个域名/地址共享一个站点配置,可以采用英文逗号分隔的方式写在一起:
1 |
|
3.5、Matchers
匹配器其实在第一部分已经介绍过,这里仅做一下简单说明;匹配器一般紧跟在指令之后,其大致格式分为以下三种:
*
: 匹配所有请求(通配符)/path
: 匹配特定路径@name
: 自定义命名匹配器
匹配器的用法样例如下:
1 |
|
具体更细节的官方匹配器使用限于篇幅这里不再详细说明,请自行阅读 官方文档
3.6、Placeholders
占位符可以理解为 Caddyfile 内部的变量替换符号,占位符同样以大括号包裹,同时支持转义:
1 |
|
Caddyfile 内部可用的占位符有很多,但是并非在所有情况下都可用,比如 HTTP 相关的占位符仅在处理 HTTP 请求相关配置中才可用;同时占位符也支持简写,下面是官方目前支持的占位符列表:
3.7、Snippets
片段上面也介绍过了,这里说一下片段更高级的用法: 支持参数传递;下面是定义一个通用日志格式,然后通过参数引用实现不同站点使用不同日志文件的配置:
1 |
|
3.8、Comments
注释没啥好说的,以 #
作为开头就行了。
3.9、Environment variables
环境变量和占位符类似,不同的是占位符是 Caddyfile 内置的变量,而环境变量是引用系统环境变量;环境变量的使用格式如下(推荐全大写):
1 |
|
上面的配置在 Caddy 启动时会读取 SITE_ADDRESS
作为监听地址,如果 SITE_ADDRESS
读取不到则会报错退出;如果想要为 SITE_ADDRESS
设置默认值,那么只需要使用如下格式即可:
1 |
|
四、其他补充
Caddyfile 并不是万能的,但是 Caddyfile 因为更易于编写和维护所以使用比较广泛;在第一部分介绍 Caddy 的配置文件体系时已经说明了,实际上 Caddy 内部是使用 json 作为配置的;这时就可能出现一些极端情况,比如说真的某个配置只能通过 json 配置,那么这时候可以考虑先通过 json 管理 API 进行动态修改,然后再去向官方发 issue,有能力也可以直接 PR;API 动态修改的流程如下:
首先假设你已经有一个能够正常启动的 Caddyfile,但是某个配置选项不支持,这时候你可以通过 API 获取内部的 json 配置:
1 |
|
得到这个配置以后,你可以通过格式化工具格式化 json,然后添加特定选项,再将其保存到一个配置文件中,然后重新 load 回去即可:
1 |
|