Caddy实战(十一)| Caddyfile 设计之美

点击上方蓝色“飞雪无情”关注我,设个星标,第一时间看文章

Caddyfile是Caddy的核心配置文件,它的设计,关乎着我们使用,开发者的解析以及扩展,所以本篇着重的介绍Caddy是如何设计一个Caddyfile的,我们也可以从中学到如何设计一个配置文件,并且让它更好的通用,更好的解析。

其实设计如此复杂的一个配置文件,已经和设计一门编程语言,很接近了。

结构

我前面的系统文章中,你也看到了如何使用Caddyfile的指令等功能,来满足我们的需求的。在我们写Caddyfile的时候,是遵循一定的规范的,哪些地方要怎么写,谁可以包含谁,这些规范就构成了Caddyfile的结构。

这张图是了解Caddyfile的神器,它定了Caddy的规范以及结构,让我们可以很方面的使用Caddyfile。现在,我来介绍下里面的一些关键点:

看到最顶部的红色框圈出来的这部分了吗?这是一个全局配置,它在Caddyfile的最顶部,用于配置一些通用的全局信息。当然它并不是必须的,你也可以不用配置它。

第二部分的 snippet 是一个可以复用的片段,你可以在其他地方通过 import 来引入它,这非常适用于你的Caddyfile中有很多重复配置的情况。它和全局配置的差别在于 { 前面有一对小括号,用于定义可复用片段的名字,这样你才可以在其他地方通过这个名字引用。下面我通过一个例子来说明它的使用,如下所示:

(static_file){
  root * /var/www/mysite
  file_server 
}

www.example.com{
  import static_file
}
www.example.com{
  import static_file
}

接下来就是 Site Block 了,也就是定义你的站点的块,在Apache中叫虚拟主机。写到这里你可以看到,Caddyfile只有这三个顶级的定义块,一个全局配置、一个可复用的片段、一个就是站点配置,其他所有的配置,都要放在这三个顶级的配置中。

你可以通过站点块定义多个站点,但是他们之间没什么关系。如果你只有一个站点,你可以省略站点后面的大括号,比如下面两种定义是等价的:

localhost

reverse_proxy /api/* localhost:9001
file_server

等价于:

localhost {
    reverse_proxy /api/* localhost:9001
    file_server
}

因为整个Caddyfile中只有这么一个站点,所以大括号是可选的。

在一个Caddyfile中,你可以至少得定义一个站点,也可以定义多个,并且定义站点的时候,大括号前面的部分必须是 Site Address ,比如示例中的 localhost ,一个站点可以有一个站点地址,也可以有多个。

Block ,也就是大括号 {} 内的这部分。左大括号 { 必须在行的末尾,而右大括号 } 则必须自己单独占一行,这和我们Go语言编程很像,这样可以保持美观。

... {
    ...
}

我们前面讲过,只有一个站点的时候,大括号是可选的,但是当有多个站点的时候,必须得用大括号把他们分开。

example1.com {
    root * /www/example.com
    file_server
}
example2.com {
    reverse_proxy localhost:9000
}

如果一个网络请求,匹配多个站点,那么Caddy只会选择地址最匹配的那个,不会同时匹配多个站点,这保障了站点匹配的唯一性,不会级联。

指令

指令只能属于某个站点,它是定义站点服务的关键字,位于一行中的第一个单词。比如我们示例中的 file_server 就是一个定义静态文件服务的指令。

指令也可以有子指令,子指令位于指令块中,用于进一步的配置,比如我们在反向代理文章中用到的负载均衡策略子指令。

localhost
reverse_proxy localhost:9000 localhost:9001 {
    lb_policy first
}

lb_policy 就是 reverse_proxy 的子指令, first 是子指令lb_policy 的参数。

Caddyfile解析

Caddyfile是一个普通的文本文件,只不过它具备一定的格式规范,所以它也要被解析成特定标记(Token)才能使用,这就和编译器的词法分析器一样。

在Caddyfile中,空格是非常重要的,因为Caddy使用它来分隔不同的标记。同样情况下,指令都需要一定的数量的参数,如果参数是有空格的,这可能会有问题,因为Caddy会把它们当成两个单独的标记进行词法分析,比如:

directive abc def

以上可能会返回异常,或者其他不可预知的行为。如果 abc def 是一个单独参数的话,最安全的做法就是使用引号,这样Caddy的词法分析器,就不会把他们当成两个标记(Token)。

directive "abc def"

这时候,你可能会有疑问,如果我参数中就需要引号怎么办呢?答案其实很简单,使用转义符号即可。

directive "\"abc def\""

不止是引号,其他空格、制表符、换行符等也可以使用转义,

这里还有一个办法,就是使用反引号:

directive `"foo bar"`

效果是等价的,反引号尤其是包含引号的文本中使用非常方便,比如JSON字符串等。

地址

地址就是站点块的顶部那部分,通常也是Caddyfile的第一个内容。Caddy基本上支持所有的地址样式,如下常用示例:

  1. localhost

  2. example.com

  3. :443

  4. http://example.com

  5. localhost:8080

  6. 127.0.0.1

  7. example.com/foo/*

  8. *.example.com

  9. http://

根据地址,Caddy可以推断出站点的Scheme、Host、Port和Path。

如果指定主机名,则只接受具有匹配主机头的请求。换句话说,如果站点地址是 localhost ,那么Caddy将不会将请求与 127.0.0.1 匹配,因为 127.0.0.1 的请求主机头不是localhost ,没法匹配。

Caddy允许在地址中使用通配符(*),但是它也有严格的限制:它只用来匹配主机名。比如 *.example.com 可以匹配 foo.example.com ,但不匹配 foo.bar.example.com 。

你也可以让多个站点地址共享同一个定义,只需要使用逗号分隔这些地址即可。

localhost:8080, example.com, www.example.com

最后,地址必须唯一,不能多次指定同一个地址。

请求匹配

一个客户端请求过来,Caddy是怎么处理的呢?比如用哪个指令来处理,这就需要设置匹配器了,通过匹配器,你可以精确的设置某个指令用于哪些请求。

如果不设置,默认情况下,该指令适用于所有请求。

指令后的第一个参数是匹配器,比如:

root *           /var/www  # matcher token: *
root /index.html /var/www  # matcher token: /index.html
root @post       /var/www  # matcher token: @post

* 表示匹配所有,这里的 @post 是一个定义的匹配器,可以被引用、复用。匹配器的定义,详见我们结构那部分的截图。

以上示例其实代表了三种匹配器:通配符匹配器、路径匹配器和命名匹配器,更多的关于请求匹配器的说明可以详见 https://caddyserver.com/docs/caddyfile/matchers ,这里不再赘述。

占位符

使用Nginx的时候,我们会看到有 $ 开头的变量,它就是占位符,是一种将动态值注入静态配置的方法,通过它可以让我们更灵活的配置Nginx。同样的,Caddy也有占位符的功能,便于我们配置Caddyfile。

在Caddyfile中,占位符的两边用大括号{}限定,并在其中包含变量名,例如: {foo.bar} 。占位符大括号可以转义, \{like so\} 。变量名通常用点命名,以避免模块之间的冲突。

你可以在Caddyfile中使用任何Caddy占位符,但为了方便起见,您也可以使用一些等效的速记占位符:

速记占位符用于取代的占位符(等价)
{dir}{http.request.uri.path.dir}
{file}{http.request.uri.path.file}
{header.*}{http.request.header.*}
{host}{http.request.host}
{labels.*}{http.request.host.labels.*}
{hostport}{http.request.hostport}
{port}{http.request.port}
{method}{http.request.method}
{path}{http.request.uri.path}
{path.*}{http.request.uri.path.*}
{query}{http.request.uri.query}
{query.*}{http.request.uri.query.*}
{re..}{http.regexp..}
{remote}{http.request.remote}
{remote_host}{http.request.remote.host}
{remote_port}{http.request.remote.port}
{scheme}{http.request.scheme}
{uri}{http.request.uri}
{tls_cipher}{http.request.tls.cipher_suite}
{tls_version}{http.request.tls.version}
{tls_client_fingerprint}{http.request.tls.client.fingerprint}
{tls_client_issuer}{http.request.tls.client.issuer}
{tls_client_serial}{http.request.tls.client.serial}
{tls_client_subject}{http.request.tls.client.subject}
{tls_client_certificate_pem}{http.request.tls.client.certificate_pem}
{upstream_hostport}{http.reverse_proxy.upstream.hostport}

并非所有占位符在配置的所有部分都可用,哪些占位符可用取决于上下文。例如,HTTP应用程序设置的占位符仅在与处理HTTP请求相关的配置区域中可用。

片段

在结构部分我简单的介绍过片段,它是一个可以复用的配置,使用 import 导入实现复用。

(redirect) {
    @http {
        protocol http
    }
    redir @http https://{host}{uri}
}

片段是顶级配置,片段定义的开头是片段的名称,使用小括号 () 括起来。定义好一个片段后,就可以通过 import 使用给它了。

import redirect

除了复用之外,片段的另一个强大之处在于可以传参给片段,实现动态化配置。

(snippet) {
  respond "Yahaha! You found {args.0}!"
}
a.example.com {
    import snippet "Example A"
}
b.example.com {
    import snippet "Example B"
}

使用非常简单,通过 {args.0} 可以获得传递过来的第一个参数。

环境变量

在Caddyfile,你也可以使用环境变量,这样可以让你的配置更灵活。

使用环境变量也非常简单,和占位符差不多,也是一个大括号包裹,但是多一个 $ 符号。

{$SITE_ADDRESS}

这种形式的环境变量在解析开始之前被替换,因此它们可以扩展为空值、部分标记、完整标记,甚至多个标记和行。和C语言的 define 一样,是不是很强大。
在具体的代码实现上,Caddy是使用Go语言的 os.LookupEnv 方法获取环境变量的。

那么,如果忘记配置环境变量怎么办呢?别急,这点Caddy肯定考虑到了,我们在使用环境变量的时候,可以设置一个默认值,如果找不到环境变量的时候,会使用这个默认值。

{$DOMAIN:localhost}

Caddyfile是使用 : 来分隔环境变量名称和默认值的,以上示例中,默认值是 localhost 。

注释

Caddyfile是支持注释的,这样我们就可以增加点注释,便于多人协作和理解。在Caddyfile中,注释以 # 开头,直到行的末尾。

# Comments can start a line
directive  # or go at the end

小结

这篇主要介绍了Caddyfile的规范以及设计,这也是一个比较完整的配置文件的设计,看了这篇相信你不光可以很好的理解Caddyfile并使用它,也可以很好的理解Nginx的conf配置,因为都差不多。

一个配置文件的设计,牵涉到规范、可以扩展性、模块化、可复用、变量,还得需要加载、替换、词法分析等,这俨然已经是在定义一门脚本言语了,所以如果你有编译器的功底,也能更好的理解Caddyfile的设计和解析。

本文为原创文章,转载注明出处,欢迎扫码关注公众号flysnow_org或者网站 https://www.flysnow.org/ ,第一时间看后续精彩文章。觉得好的话,请猛击文章右下角「在看」,感谢支持。

——  精彩推荐  ——

Caddy实战(十)| 一分钟搭建PHP服务器

Caddy实战(九)| 设置头信息实现跨域

Caddy实战(八)| 利用缓冲提升反向代理的性能

Caddy实战(七)| 反向代理中的健康检查

Caddy实战(六)| 反向代理中的负载均衡

Caddy实战(五)| 配置反向代理

Caddy实战(四)| 使用API管理Caddy

Caddy实战(三)| Caddyfile 快速入门

Caddy实战(二)| Caddy命令行参数最全教程

Caddy实战(一)| 托管你的网站,只需一行命令

扫码关注

分享、点赞、在看就是最大的支持

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值