Caddy部署与使用
官方文档
https://github.com/caddyserver/caddy
Caddy是什么
相信大家都知道apache、nginx,那么caddy也一样,它是一个Web服务器,可以帮你托管你的Web服务,让其他人可以通过互联网访问。
比如你想搭建一个博客,可以在互联网上被其他人访问,那么就可以使用caddy,相比nginx来说,它配置更简单。
对于caddy,官方是这么定义的:
Caddy 2 is a powerful, enterprise-ready, open source web server with automatic HTTPS written in Go
Caddy是一个强大的、企业级的、开放源代码的web服务器,使用Go编写,并且可以自动HTTPS加密(其实就是自动配置TLS证书并管理)。
安装Caddy
得益于Go语言,caddy是一个独立的二进制包,所以它没有任何依赖,你可以直接从官网下载并安装它。
docker-compose部署caddy并开启https请求
创建目录(在root目录下)
$ mkdir -p caddy/{conf,logs,www}
$ cd caddy
配置Caddyfile
$ vi conf/Caddyfile
www.?????.net {
gzip
root /opt/www
timeouts 30s
log /opt/logs/access.log
proxy / localhost:8080 #根url 直接代理转发
proxy /more localhost:8081 localhost:8082 #负载均衡
}
我自己的配置
我想直接访问 www.???.net 就帮我代理转发到 localhost:8080,可以!我想直接访问 www.???.net 就帮我代理转发到 localhost:8080,可以!
以上是在配置公网域名后的配置,还没有配置域名?那直接用 IP 就行,如下
192.168.1.2:80 192.168.1.2:443 {
gzip
root /opt/www
tls self_signed #自动签名,很重要
timeouts 30s
log /opt/logs/access.log
proxy /other localhost:8080
}
自己看着修改
配置docker-compose.yml
$ vi docker-compose.yml
version: '3.7'
services:
# http/2 server
caddy:
image: abiosoft/caddy
container_name: caddy
hostname: caddy
domainname: caddy
restart: always
environment:
- ACME_AGREE=true
- TZ=Asia/Shanghai
- agree
volumes:
- "/caddy/conf/Caddyfile:/etc/Caddyfile"
- "/caddy/.caddy:/root/.caddy"
- "/caddy/logs:/opt/logs"
- "/caddy/www:/opt/www"
ports:
- "80:80"
- "443:443"
- "443:443/udp"
其中三个环境变量(environment)非常重要
1、配置上海时区
2、同意caddy自动配置ssl证书
3、caddy需要的反馈邮箱
启动
$ docker-compose up -d
$ docker logs -f caddy # 查看日志
测试
随便复制个文件到 www 目录下,我复制了个图片bg.jpg,
然后输入 https://192.168.1.2/bg.jpg
Caddyfile配置详解
Caddyfile 主要包含一些通用的配置,并将其抽到配置片段中,类似于 nginx 的 nginx.conf
主配置;在最后部分通过 import
关键字引入其他具体站点配置,类似 nginx 的 vhost
配置。
# 授权认证服务
http://auth-server.jianpeicn.com {
reverse_proxy http://172.16.95.146:5000
}
# 基础服务
http://management-server.jianpeicn.com {
reverse_proxy /api/identity/* http://172.16.95.146:5001
reverse_proxy /api/multi-tenancy/* http://172.16.95.146:5003
reverse_proxy /api/identity-server/* http://172.16.95.146:5002
reverse_proxy /api/permission-management/* http://172.16.95.146:5004
reverse_proxy /api/storage/* http://120.26.112.192:7020
reverse_proxy /* http://172.16.95.146:7000
}
https://management-server.jianpeicn.com {
tls internal
reverse_proxy /api/identity/* http://172.16.95.146:5001
reverse_proxy /api/multi-tenancy/* http://172.16.95.146:5003
reverse_proxy /api/identity-server/* http://172.16.95.146:5002
reverse_proxy /api/permission-management/* http://172.16.95.146:5004
reverse_proxy /api/storage/* http://120.26.112.192:7020
reverse_proxy /* http://172.16.95.146:7000
}
- tls: 是否使用htpps证书(内网使用internal,外网使用邮箱)
- reverse_proxy: 代理地址
配置片段
Caddyfile 支持类似代码中 function 一样的配置片段,这些配置片段可以在任意位置被 import
,同时可以接受参数,以下为配置片断示例:
# 括号内为片段名称,可以自行定义
(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 比较自由化,其格式如下:
地址 {
站点配置
}
关于这个 “地址” 接受多种格式,以下都为合法的地址格式:
localhost
example.com
:443
http://example.com
localhost:8080
127.0.0.1
[::1]:2015
example.com/foo/*
*.example.com
http://
配置多个Web服务
既然caddy是一个Web服务器,那么就不止可以托管一个Web站点,如果需要托管多个,怎么做呢?
localhost {
respond "Hello, world!"
}
localhost:2016 {
respond "Goodbye, world!"
}
配置反向代理
localhost
reverse_proxy 127.0.0.1:9000
以上示例,都是把https://localhost/ 的请求代理到127.0.0.1:9000 。
以上http默认的是80端口,https默认的是443端口。当然,我们也可以换个端口,比如2016.
:2016
reverse_proxy 127.0.0.1:9000
代理匹配的请求
caddy作为一个强大的web服务器,其反向打理功能肯定不止以上这么简单。比如,我们不想代理全部的网络请求,而是只想代理匹配的怎么做呢?比如代理 /api
这个Path下的。
reverse_proxy /api/* {
to 127.0.0.1:9000
}
从以上示例可以看到,只需要在 reverse_proxy
指令后,加 /api/*
这个match即可,这和Nginx的 location
是非常相似的。
代理到多个上游服务
为了高可用,上游服务我们会部署多少,这样当一个有问题的时候,不会影响产品功能。下面我们看下Caddy是如何实现这一能力的。
reverse_proxy node1:80 node2:80 node3:80
这样就会所有的请求,随机的反向代理到这三个node节点上了。
当然我们还可以这样配置:
reverse_proxy /api/* {
to node1:80 node2:80 node3:80
}
当同时配置多个反向代理服务的时候,就有了负载均衡了。以上默认的情况下,是随机的,也就是caddy会随机的选择一个上游服务使用。
Caddy的负载均衡
只要定义了多个上游(upstream),Caddy的反向代理就会使用负载均衡的能力,,就是用了负载均衡,代码如下所示:
reverse_proxy /api/* {
to node1:80 node2:80 node3:80
}
以上这个配置,它的负载均衡策略(调度方式)默认是随机的,我们可以通过 lb_policy
来指定不同的负载均衡策略,比如
reverse_proxy /api/* {
to node1:80 node2:80 node3:80
lb_policy first
}
以上代码指定的是 first
的负责均衡策略,也就是选择第一个可用的上游。
负载均衡策略
除了 first 外,还有很多负载均衡策略以供我们选择,下面就为你分别介绍他们。
1- first:选取第一个可用的上游
2- random:随机选取一个可用的上游
3- least_conn:选取当前请求数最少的上游,这个比较适合长连接的场景
4- ip_hash:根据IP的Hash值选取一个固定的上游
5- random_choose
:随机选取2个或者更多个上游,然后再从中选择负载最小的,n通常为2
6- header:这个是根据请求头的Hash选取一个固定的上游,和 ip_hash 很像,只不过它是根据指定的请求头的值进行Hash,然后选取上游的。所以这里的用法是 header ,要指定一个请求头。
7- uri_hash:这个也和 ip_hash 很像,只不过它是根据请求的URI进行Hash,然后选取一个上游。
8- round_robin:这个策略是循环迭代,挨个使用一个个上游,每个上游都可以被用到,轮着来。
9- cookie
重试等待时间
当你设置了多个上游主机,启用了负载均衡后,不可避免的会遇到上游服务不可用的情况,默认情况下,Caddy的处理是:当上游服务器不可用的时候,客户端的请求就没有可用的服务来相应,客户端就会报错。
当然除了立即响应服务不可用,Caddy还提供了负责均衡的重试功能,这就是 lb_try_duration ,他可以设置一个重试的时间,比如1000毫秒。
这是什么意思呢?如果你设置了lb_try_duration 为1000毫秒,那么Caddy的负载均衡在处理该客户端请求时,如果选用了一个不可用的上游服务,就会继续重试,一直到找到一个可用的上游或者到了设置的1000毫秒时间为止。
也就是,这个时间,就是Caddy的负载平衡器尝试查找可用的上游主机时,客户端将等待最长时间
重试时间间隔
有了重试等待时间,肯定也有重试时间间隔,因为正常的逻辑上来讲,不能马上去重试,因为马上重试的话,很大概率拿到的也是个不可用的上游主机,所以需要一个重试时间间隔,来控制重试的节奏,在Caddy中,就是 lb_try_interval ,默认是重试间隔是250ms,250毫秒。
所以你可以看到, lb_try_interval 是结合着lb_try_duration 来使用的。lb_try_duration 是重试等待的总时间,而lb_try_interval 是在这段时间内,每隔多久重试一次。
负载均衡示例非常简单,按照配置写即可
# 按照ip hash 负载均衡
reverse_proxy /api/* {
to node1:80 node2:80 node3:80
lb_policy ip_hash
}
设置等待时间的话,可以这样:
# 按照ip hash 负载均衡,重试等待时间为5秒
reverse_proxy /api/* {
to node1:80 node2:80 node3:80
lb_policy ip_hash
lb_try_duration 5s
}
环境变量
Caddyfile 支持直接引用系统环境变量,通过此功能可以将一些敏感信息从配置文件中剔除:
# 引用环境变量 GANDI_API_TOKEN
dns gandi {$GANDI_API_TOKEN}
配置片段参数支持
针对于配置片段,Caddyfile 还支持类似于函数代码的参数支持,通过参数支持可以让外部引用时动态修改配置信息:
(LOG) {
log {
format json {
time_format "iso8601"
}
# "{args.0}" 引用传入的第一个参数,此处用于动态传入日志文件名称
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
)已经解析到了本机,那么 Caddy2 启动后会尝试自动通过 ACME HTTP 挑战申请证书;如果期望使用 DNS 的方式申请证书则需要其他 DNS 插件支持,比如上面编译的 --with github.com/caddy-dns/gandi
为 gandi 服务商的 DNS 插件;关于使用 DNS 挑战的配置编写方式需要具体去看其插件文档,目前 gandi 的配置如下:
tls {
dns gandi {env.GANDI_API_TOKEN}
}
配置完成后 Caddy2 会通过 ACME DNS 挑战申请证书,值得注意的是即使通过 DNS 申请证书默认也不会申请泛域名证书,如果想要调整这种细节配置请使用 json 配置或管理 API。
(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) {
# 从环境变量获取 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
为主站点配置,主站点配置内主要编写一些路由规则,TLS 等都从配置片段引入,这样可以保持统一。
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" 支持三种参数
# 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
}
采用以下大佬文章理解caddy这个服务
www.mritd.me {
# 重定向到 mritd.com
# 最后的 "code" 支持三种参数
# 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
}