6.1 Caddy插件系统概述
6.1.1 插件系统架构
Caddy采用模块化架构,通过插件系统提供强大的扩展能力。插件系统的特点:
- 模块化设计:每个功能都是独立的模块
- 热插拔支持:可以动态加载和卸载插件
- 类型安全:Go语言的类型系统确保插件安全
- 性能优化:编译时优化,运行时高效
- 丰富生态:社区提供大量第三方插件
6.1.2 插件类型
- HTTP中间件:处理HTTP请求和响应
- DNS提供商:用于DNS验证的ACME挑战
- 存储后端:证书和配置存储
- 日志编写器:自定义日志输出
- 认证提供商:身份验证和授权
- 缓存后端:缓存存储和管理
- 监控插件:指标收集和监控
6.1.3 插件管理
# 查看已安装的插件
caddy list-modules
# 查看特定类型的插件
caddy list-modules --type http.handlers
# 查看插件版本信息
caddy version
# 构建包含特定插件的Caddy
xcaddy build --with github.com/caddyserver/cache-handler
6.2 常用官方插件
6.2.1 文件服务器插件
# 基本文件服务器
example.com {
file_server {
root /var/www/html
index index.html index.htm
browse
hide .git .env
precompressed gzip br
}
}
# 高级文件服务器配置
example.com {
file_server {
root /var/www/html
# 自定义错误页面
status 404 /404.html
status 403 /403.html
# 文件过滤
hide *.log *.tmp .DS_Store
# 目录浏览模板
browse {
template browse.html
}
}
}
6.2.2 反向代理插件
# 基本反向代理
example.com {
reverse_proxy localhost:8080 {
# 负载均衡
lb_policy round_robin
# 健康检查
health_uri /health
health_interval 30s
health_timeout 5s
# 请求修改
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
# 响应修改
header_down -Server
header_down X-Powered-By "Caddy"
}
}
# 高级代理配置
example.com {
reverse_proxy localhost:8080 localhost:8081 {
# 会话保持
lb_policy ip_hash
# 故障转移
fail_duration 30s
max_fails 3
# 传输配置
transport http {
dial_timeout 10s
response_header_timeout 30s
keep_alive 30s
compression off
}
# 重试配置
@retry {
status 502 503 504
}
handle_response @retry {
reverse_proxy localhost:8082
}
}
}
6.2.3 重定向插件
# 基本重定向
example.com {
# 简单重定向
redir /old-page /new-page 301
# 模式重定向
redir /blog/* /posts/{uri} 302
# 条件重定向
@mobile {
header User-Agent *Mobile*
}
redir @mobile /mobile{uri}
file_server
}
# 高级重定向
example.com {
# 基于查询参数的重定向
@old_search {
query q=*
}
redir @old_search /search?query={query.q}
# 基于主机的重定向
@www {
host www.example.com
}
redir @www https://example.com{uri} 301
# 正则表达式重定向
redir /user/([0-9]+) /users/{re.1} 301
file_server
}
6.2.4 重写插件
# URL重写
example.com {
# 简单重写
rewrite /api/v1/* /api/v2/*
# 条件重写
@api {
path /api/*
method GET
}
rewrite @api /rest{uri}
# 正则表达式重写
rewrite ^/user/([0-9]+)$ /users.php?id=$1
# 查询参数重写
rewrite /search /search.php?q={query}
file_server
}
# 高级重写
example.com {
# 多条件重写
@rewrite {
path /old-api/*
header Accept application/json
}
rewrite @rewrite /new-api{path_regexp ^/old-api(.*)$ $1}
# 基于时间的重写
@maintenance {
expression {time.now.hour} >= 2 && {time.now.hour} <= 4
}
rewrite @maintenance /maintenance.html
file_server
}
6.3 第三方插件
6.3.1 缓存插件
# 安装缓存插件
xcaddy build --with github.com/caddyserver/cache-handler
# 基本缓存配置
example.com {
cache {
# 缓存键
key {method} {host} {path} {query}
# 缓存时间
ttl 1h
stale_ttl 24h
# 缓存条件
match {
status 200
header Content-Type text/html
}
# 缓存后端
redis {
host localhost:6379
password {env.REDIS_PASSWORD}
db 0
}
}
reverse_proxy localhost:8080
}
# 高级缓存配置
example.com {
# 不同内容的缓存策略
@static {
path *.css *.js *.png *.jpg *.gif
}
cache @static {
key {method} {host} {path}
ttl 24h
stale_ttl 168h
}
@api {
path /api/*
method GET
}
cache @api {
key {method} {host} {path} {query} {header.Authorization}
ttl 5m
stale_ttl 1h
vary Accept-Encoding Authorization
}
# 缓存清除
@purge {
method PURGE
}
handle @purge {
cache_purge {path}
respond "Cache purged" 200
}
reverse_proxy localhost:8080
}
6.3.2 限流插件
# 安装限流插件
xcaddy build --with github.com/mholt/caddy-ratelimit
# 基本限流
example.com {
rate_limit {
zone general
key {remote_host}
rate 100r/m
window 1m
}
file_server
}
# 高级限流配置
example.com {
# 基于用户的限流
@authenticated {
header Authorization *
}
rate_limit @authenticated {
zone users
key {http.request.header.X-User-ID}
rate 1000r/h
window 1h
response_code 429
response_body "Rate limit exceeded for user"
}
# 基于IP的限流
rate_limit not @authenticated {
zone anonymous
key {remote_host}
rate 100r/h
window 1h
}
# API端点的特殊限流
@api {
path /api/*
}
rate_limit @api {
zone api
key {remote_host}
rate 300r/h
window 1h
burst 50
}
# 登录端点的严格限流
@login {
path /auth/login
method POST
}
rate_limit @login {
zone login
key {remote_host}
rate 5r/m
window 5m
response_code 429
response_body `{"error": "Too many login attempts"}`
}
reverse_proxy localhost:8080
}
6.3.3 JWT认证插件
# 安装JWT插件
xcaddy build --with github.com/greenpau/caddy-security
# JWT认证配置
example.com {
# JWT中间件
jwt {
primary yes
# JWT密钥
trusted_tokens {
static_secret {env.JWT_SECRET}
}
# 或使用RSA公钥
# trusted_tokens {
# rsa_file /path/to/public.pem
# }
# 认证URL
auth_url /auth/login
forbidden_url /auth/forbidden
# 令牌位置
token_sources {
header Authorization
query_string access_token
cookie jwt_token
}
# 用户声明
user_claims {
username sub
roles groups
email email
}
}
# 受保护的路径
@protected {
path /admin/* /api/private/*
}
# 需要特定角色
@admin_only {
path /admin/*
expression {http.jwt.claims.roles} contains "admin"
}
handle @admin_only {
file_server {
root /var/www/admin
}
}
handle @protected {
reverse_proxy localhost:8080 {
header_up X-User-ID {http.jwt.claims.sub}
header_up X-User-Roles {http.jwt.claims.roles}
}
}
# 公开路径
file_server
}
6.3.4 Prometheus监控插件
# 安装Prometheus插件
xcaddy build --with github.com/caddyserver/caddy/v2/modules/caddyhttp/metrics
# Prometheus监控
example.com {
# 启用指标收集
metrics /metrics {
# 基本认证保护
basicauth {
prometheus $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
}
}
# 自定义指标
@api_requests {
path /api/*
}
handle @api_requests {
# 增加API请求计数
metrics_increment api_requests_total {
labels {
method {method}
endpoint {path}
}
}
reverse_proxy localhost:8080
}
# 错误指标
@errors {
status 4xx 5xx
}
handle_response @errors {
metrics_increment http_errors_total {
labels {
status {status}
method {method}
}
}
}
file_server
}
# 全局指标配置
{
servers {
metrics
}
}
6.3.5 日志插件
# 安装高级日志插件
xcaddy build --with github.com/caddyserver/caddy/v2/modules/logging
# 高级日志配置
example.com {
# 结构化日志
log {
output file /var/log/caddy/access.log {
roll_size 100mb
roll_keep 30
roll_keep_for 720h
}
format json {
time_format "2006-01-02T15:04:05.000Z07:00"
message_key "msg"
level_key "level"
time_key "ts"
}
level INFO
# 自定义字段
include {
http.request.method
http.request.uri
http.request.headers.User-Agent
http.request.headers.X-Forwarded-For
http.response.status
http.response.size
http.response.duration
}
# 排除字段
exclude {
http.request.headers.Authorization
http.request.headers.Cookie
}
}
# 错误日志
log error {
output file /var/log/caddy/error.log
level ERROR
format console
}
# 安全日志
@security_events {
status 403 429
}
log @security_events {
output file /var/log/caddy/security.log
format json
level WARN
}
file_server
}
6.4 自定义插件开发
6.4.1 插件开发基础
// hello.go - 简单的Hello World插件
package hello
import (
"fmt"
"net/http"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
)
func init() {
caddy.RegisterModule(Hello{})
}
// Hello 插件结构
type Hello struct {
Message string `json:"message,omitempty"`
}
// CaddyModule 返回模块信息
func (Hello) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.hello",
New: func() caddy.Module { return new(Hello) },
}
}
// Provision 初始化插件
func (h *Hello) Provision(ctx caddy.Context) error {
if h.Message == "" {
h.Message = "Hello, World!"
}
return nil
}
// ServeHTTP 处理HTTP请求
func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
w.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(w, "%s\n", h.Message)
return nil
}
// UnmarshalCaddyfile 解析Caddyfile配置
func (h *Hello) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
if d.NextArg() {
h.Message = d.Val()
}
}
return nil
}
// 接口验证
var (
_ caddy.Provisioner = (*Hello)(nil)
_ caddyhttp.MiddlewareHandler = (*Hello)(nil)
_ caddyfile.Unmarshaler = (*Hello)(nil)
)
6.4.2 中间件插件
// middleware.go - 自定义中间件插件
package middleware
import (
"context"
"net/http"
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"go.uber.org/zap"
)
func init() {
caddy.RegisterModule(RequestLogger{})
}
// RequestLogger 请求日志中间件
type RequestLogger struct {
IncludeHeaders bool `json:"include_headers,omitempty"`
ExcludePaths []string `json:"exclude_paths,omitempty"`
logger *zap.Logger
}
// CaddyModule 返回模块信息
func (RequestLogger) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "http.handlers.request_logger",
New: func() caddy.Module { return new(RequestLogger) },
}
}
// Provision 初始化插件
func (rl *RequestLogger) Provision(ctx caddy.Context) error {
rl.logger = ctx.Logger(rl)
return nil
}
// ServeHTTP 处理HTTP请求
func (rl RequestLogger) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// 检查是否需要排除此路径
for _, path := range rl.ExcludePaths {
if r.URL.Path == path {
return next.ServeHTTP(w, r)
}
}
start := time.Now()
// 创建响应记录器
rec := caddyhttp.NewResponseRecorder(w, nil, nil)
// 处理请求
err := next.ServeHTTP(rec, r)
// 记录请求信息
duration := time.Since(start)
fields := []zap.Field{
zap.String("method", r.Method),
zap.String("path", r.URL.Path),
zap.String("query", r.URL.RawQuery),
zap.String("remote_addr", r.RemoteAddr),
zap.String("user_agent", r.UserAgent()),
zap.Int("status", rec.Status()),
zap.Int64("size", rec.Size()),
zap.Duration("duration", duration),
}
if rl.IncludeHeaders {
headers := make(map[string][]string)
for k, v := range r.Header {
headers[k] = v
}
fields = append(fields, zap.Any("headers", headers))
}
rl.logger.Info("HTTP request", fields...)
return err
}
// UnmarshalCaddyfile 解析Caddyfile配置
func (rl *RequestLogger) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextBlock(0) {
switch d.Val() {
case "include_headers":
rl.IncludeHeaders = true
case "exclude_paths":
rl.ExcludePaths = d.RemainingArgs()
}
}
}
return nil
}
// 接口验证
var (
_ caddy.Provisioner = (*RequestLogger)(nil)
_ caddyhttp.MiddlewareHandler = (*RequestLogger)(nil)
_ caddyfile.Unmarshaler = (*RequestLogger)(nil)
)
6.4.3 DNS提供商插件
// dnsprovider.go - 自定义DNS提供商插件
package dnsprovider
import (
"context"
"fmt"
"time"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
"github.com/libdns/libdns"
)
func init() {
caddy.RegisterModule(Provider{})
}
// Provider 自定义DNS提供商
type Provider struct {
APIKey string `json:"api_key,omitempty"`
APISecret string `json:"api_secret,omitempty"`
BaseURL string `json:"base_url,omitempty"`
client *APIClient
}
// CaddyModule 返回模块信息
func (Provider) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "dns.providers.custom",
New: func() caddy.Module { return new(Provider) },
}
}
// Provision 初始化提供商
func (p *Provider) Provision(ctx caddy.Context) error {
if p.APIKey == "" {
return fmt.Errorf("API key is required")
}
if p.BaseURL == "" {
p.BaseURL = "https://api.example.com"
}
p.client = NewAPIClient(p.APIKey, p.APISecret, p.BaseURL)
return nil
}
// AppendRecords 添加DNS记录
func (p *Provider) AppendRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
var created []libdns.Record
for _, record := range records {
createdRecord, err := p.client.CreateRecord(ctx, zone, record)
if err != nil {
return created, fmt.Errorf("failed to create record %s: %w", record.Name, err)
}
created = append(created, createdRecord)
}
return created, nil
}
// DeleteRecords 删除DNS记录
func (p *Provider) DeleteRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
var deleted []libdns.Record
for _, record := range records {
err := p.client.DeleteRecord(ctx, zone, record.ID)
if err != nil {
return deleted, fmt.Errorf("failed to delete record %s: %w", record.ID, err)
}
deleted = append(deleted, record)
}
return deleted, nil
}
// GetRecords 获取DNS记录
func (p *Provider) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) {
return p.client.GetRecords(ctx, zone)
}
// SetRecords 设置DNS记录
func (p *Provider) SetRecords(ctx context.Context, zone string, records []libdns.Record) ([]libdns.Record, error) {
// 先删除现有记录,再创建新记录
existing, err := p.GetRecords(ctx, zone)
if err != nil {
return nil, err
}
_, err = p.DeleteRecords(ctx, zone, existing)
if err != nil {
return nil, err
}
return p.AppendRecords(ctx, zone, records)
}
// UnmarshalCaddyfile 解析Caddyfile配置
func (p *Provider) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
for d.Next() {
for d.NextBlock(0) {
switch d.Val() {
case "api_key":
if !d.NextArg() {
return d.ArgErr()
}
p.APIKey = d.Val()
case "api_secret":
if !d.NextArg() {
return d.ArgErr()
}
p.APISecret = d.Val()
case "base_url":
if !d.NextArg() {
return d.ArgErr()
}
p.BaseURL = d.Val()
}
}
}
return nil
}
// APIClient DNS API客户端
type APIClient struct {
apiKey string
apiSecret string
baseURL string
}
// NewAPIClient 创建新的API客户端
func NewAPIClient(apiKey, apiSecret, baseURL string) *APIClient {
return &APIClient{
apiKey: apiKey,
apiSecret: apiSecret,
baseURL: baseURL,
}
}
// CreateRecord 创建DNS记录
func (c *APIClient) CreateRecord(ctx context.Context, zone string, record libdns.Record) (libdns.Record, error) {
// 实现API调用逻辑
// ...
return record, nil
}
// DeleteRecord 删除DNS记录
func (c *APIClient) DeleteRecord(ctx context.Context, zone, recordID string) error {
// 实现API调用逻辑
// ...
return nil
}
// GetRecords 获取DNS记录
func (c *APIClient) GetRecords(ctx context.Context, zone string) ([]libdns.Record, error) {
// 实现API调用逻辑
// ...
return nil, nil
}
// 接口验证
var (
_ caddy.Provisioner = (*Provider)(nil)
_ libdns.RecordAppender = (*Provider)(nil)
_ libdns.RecordDeleter = (*Provider)(nil)
_ libdns.RecordGetter = (*Provider)(nil)
_ libdns.RecordSetter = (*Provider)(nil)
_ caddyfile.Unmarshaler = (*Provider)(nil)
)
6.4.4 插件构建和部署
# 创建插件模块
go mod init github.com/username/caddy-plugin
# 添加依赖
go get github.com/caddyserver/caddy/v2
go get github.com/caddyserver/caddy/v2/modules/caddyhttp
# 构建包含插件的Caddy
xcaddy build --with github.com/username/caddy-plugin
# 或者本地开发
xcaddy build --with github.com/username/caddy-plugin=./
# Dockerfile - 构建包含自定义插件的Caddy镜像
FROM caddy:builder AS builder
# 添加自定义插件
RUN xcaddy build \
--with github.com/username/caddy-plugin \
--with github.com/caddyserver/cache-handler \
--with github.com/mholt/caddy-ratelimit
FROM caddy:latest
# 复制自定义构建的Caddy
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
# 复制配置文件
COPY Caddyfile /etc/caddy/Caddyfile
# 暴露端口
EXPOSE 80 443
# 启动Caddy
CMD ["caddy", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]
6.5 插件配置和使用
6.5.1 插件配置语法
# 基本插件使用
example.com {
# 使用自定义插件
hello "Welcome to my site!"
# 使用中间件插件
request_logger {
include_headers
exclude_paths /health /metrics
}
file_server
}
# 复杂插件配置
example.com {
# 缓存插件
cache {
key {method} {host} {path} {query}
ttl 1h
stale_ttl 24h
match {
status 200
header Content-Type text/html application/json
}
redis {
host localhost:6379
password {env.REDIS_PASSWORD}
db 0
pool_size 10
}
}
# 限流插件
rate_limit {
zone api
key {remote_host}
rate 100r/m
window 1m
burst 20
response_code 429
response_body "Rate limit exceeded"
}
reverse_proxy localhost:8080
}
6.5.2 插件组合使用
# 多插件组合
example.com {
# 请求日志
request_logger {
include_headers
}
# 限流
rate_limit {
zone general
key {remote_host}
rate 300r/m
}
# JWT认证
jwt {
trusted_tokens {
static_secret {env.JWT_SECRET}
}
}
# 缓存
cache {
ttl 10m
match {
method GET
status 200
}
}
# 反向代理
reverse_proxy localhost:8080 {
header_up X-User-ID {http.jwt.claims.sub}
}
# 指标收集
metrics /metrics {
basicauth {
admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
}
}
}
6.5.3 条件插件使用
example.com {
# 基于路径的插件使用
@api {
path /api/*
}
@static {
path /static/* /assets/*
}
@admin {
path /admin/*
}
# API路径使用JWT和限流
handle @api {
jwt {
trusted_tokens {
static_secret {env.JWT_SECRET}
}
}
rate_limit {
zone api
key {http.jwt.claims.sub}
rate 1000r/h
}
reverse_proxy localhost:8080
}
# 静态资源使用缓存
handle @static {
cache {
ttl 24h
key {method} {host} {path}
}
file_server
}
# 管理区域使用基本认证和严格限流
handle @admin {
basicauth {
admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
}
rate_limit {
zone admin
key {remote_host}
rate 10r/m
}
file_server {
root /var/www/admin
}
}
# 默认处理
file_server
}
6.6 插件性能优化
6.6.1 插件性能监控
example.com {
# 性能监控插件
performance_monitor {
# 监控阈值
slow_request_threshold 1s
memory_threshold 100MB
# 监控指标
metrics {
request_duration
memory_usage
goroutine_count
gc_duration
}
# 告警配置
alerts {
webhook https://alerts.example.com/webhook
email admin@example.com
}
}
# 其他插件
cache {
ttl 1h
}
reverse_proxy localhost:8080
}
6.6.2 插件缓存优化
example.com {
# 多层缓存
# 内存缓存(L1)
cache_memory {
max_size 100MB
ttl 5m
key {method} {host} {path}
}
# Redis缓存(L2)
cache_redis {
host localhost:6379
ttl 1h
key {method} {host} {path} {query}
# 连接池优化
pool_size 20
max_idle 10
idle_timeout 5m
}
# CDN缓存(L3)
cache_cdn {
provider cloudflare
api_key {env.CLOUDFLARE_API_KEY}
zone_id {env.CLOUDFLARE_ZONE_ID}
ttl 24h
}
reverse_proxy localhost:8080
}
6.6.3 插件资源管理
{
# 全局资源限制
servers {
max_header_size 16KB
read_timeout 30s
write_timeout 30s
idle_timeout 120s
# 连接限制
max_connections 10000
# 内存限制
max_memory 1GB
}
}
example.com {
# 请求大小限制
request_body {
max_size 10MB
}
# 连接限制
connection_limit {
max_connections_per_ip 100
max_connections_total 5000
}
# 内存使用监控
memory_monitor {
max_usage 500MB
check_interval 30s
# 超出限制时的动作
on_exceed {
log_warning
reject_new_connections
# gc_force
}
}
reverse_proxy localhost:8080
}
6.7 实战案例
6.7.1 企业级API网关
# 企业级API网关配置
api.company.com {
# 请求日志
request_logger {
include_headers
exclude_paths /health /metrics
format json
output file /var/log/caddy/api-requests.log
}
# 全局限流
rate_limit {
zone global
key {remote_host}
rate 1000r/m
window 1m
burst 100
}
# CORS处理
@cors_preflight {
method OPTIONS
}
handle @cors_preflight {
header {
Access-Control-Allow-Origin "*"
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization, X-API-Key"
Access-Control-Max-Age "86400"
}
respond "" 204
}
# API密钥验证
@api_key_required {
not path /public/*
not path /health
}
handle @api_key_required {
api_key_auth {
header X-API-Key
redis_store {
host localhost:6379
key_prefix "api_keys:"
}
# 密钥限流
rate_limit {
zone api_keys
key {http.request.header.X-API-Key}
rate 10000r/h
window 1h
}
}
}
# JWT认证(高级API)
@jwt_required {
path /v2/* /premium/*
}
handle @jwt_required {
jwt {
trusted_tokens {
rsa_file /etc/caddy/jwt-public.pem
}
user_claims {
user_id sub
plan subscription.plan
permissions permissions
}
}
# 基于订阅计划的限流
@premium_users {
expression {http.jwt.claims.plan} == "premium"
}
rate_limit @premium_users {
zone premium
key {http.jwt.claims.sub}
rate 100000r/h
}
rate_limit not @premium_users {
zone standard
key {http.jwt.claims.sub}
rate 10000r/h
}
}
# 缓存层
@cacheable {
method GET
not path /user/* /account/*
}
cache @cacheable {
key {method} {host} {path} {query} {header.X-API-Key}
ttl 5m
stale_ttl 1h
redis {
host redis-cluster:6379
password {env.REDIS_PASSWORD}
db 1
}
# 缓存预热
warmup {
urls /api/popular /api/trending
interval 10m
}
}
# 服务路由
handle /v1/users/* {
reverse_proxy user-service:8080 {
lb_policy least_conn
health_uri /health
header_up X-User-ID {http.jwt.claims.sub}
header_up X-API-Key {http.request.header.X-API-Key}
}
}
handle /v1/orders/* {
reverse_proxy order-service:8080 {
lb_policy ip_hash
health_uri /health
}
}
handle /v1/payments/* {
# 支付服务需要额外安全
security_headers {
strict_transport_security "max-age=31536000"
content_security_policy "default-src 'none'"
}
reverse_proxy payment-service:8080 {
transport http {
tls
tls_client_auth /etc/caddy/client.pem /etc/caddy/client.key
}
}
}
# WebSocket支持
handle /ws/* {
reverse_proxy websocket-service:8080 {
lb_policy ip_hash
}
}
# 监控端点
handle /metrics {
basicauth {
monitor $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNqq9qB6FY9gZKOOdOoKw6Uw.
}
metrics {
prometheus
include_labels method path status
}
}
# 健康检查
handle /health {
health_check {
upstream_checks {
user-service:8080
order-service:8080
payment-service:8080
}
response_format json
}
}
# 默认处理
respond "API Gateway" 200
}
6.7.2 多租户SaaS平台
# 多租户SaaS平台
*.saas.example.com {
# 租户识别
tenant_resolver {
source subdomain
redis_store {
host localhost:6379
key_prefix "tenants:"
}
# 租户不存在时的处理
fallback_url https://www.example.com/signup
}
# 租户级别的限流
rate_limit {
zone tenants
key {http.tenant.id}
rate {http.tenant.rate_limit}r/h
window 1h
}
# 租户级别的缓存
cache {
key {http.tenant.id} {method} {path} {query}
ttl {http.tenant.cache_ttl}
redis {
host localhost:6379
db {http.tenant.cache_db}
}
}
# 租户特定的安全策略
@enterprise_tenants {
expression {http.tenant.plan} == "enterprise"
}
handle @enterprise_tenants {
# 企业级安全
security_headers {
strict_transport_security "max-age=31536000; includeSubDomains"
content_security_policy {http.tenant.csp_policy}
}
# 客户端证书认证
tls {
client_auth {
mode require_and_verify
trusted_ca_cert_file /etc/caddy/tenant-ca/{http.tenant.id}.pem
}
}
}
# 租户数据隔离
reverse_proxy app-backend:8080 {
header_up X-Tenant-ID {http.tenant.id}
header_up X-Tenant-Plan {http.tenant.plan}
header_up X-Tenant-Features {http.tenant.features}
# 基于租户的路由
@premium_tenants {
expression {http.tenant.plan} in ["premium", "enterprise"]
}
# 高级租户使用专用后端
to @premium_tenants premium-backend:8080
}
# 租户级别的监控
metrics {
labels {
tenant_id {http.tenant.id}
tenant_plan {http.tenant.plan}
}
}
# 租户日志
log {
output file /var/log/caddy/tenants/{http.tenant.id}.log
format json
include http.tenant.id http.tenant.plan
}
}
本章总结
本章我们全面学习了Caddy的插件系统和扩展功能:
- 插件系统架构:理解了Caddy的模块化设计和插件类型
- 官方插件:掌握了常用官方插件的配置和使用
- 第三方插件:学习了热门第三方插件的安装和配置
- 自定义插件开发:了解了插件开发的基础知识和最佳实践
- 插件配置:掌握了插件的配置语法和组合使用
- 性能优化:学习了插件的性能监控和优化技巧
- 实战案例:通过企业级案例学习了插件的综合应用
通过本章的学习,你应该能够: - 选择和配置适合的插件 - 开发自定义插件满足特殊需求 - 优化插件性能和资源使用 - 构建复杂的企业级应用架构 - 扩展Caddy的功能以满足业务需求
练习题
基础练习
插件安装和配置
- 安装和配置缓存插件
- 配置限流插件
- 使用JWT认证插件
插件组合
- 组合使用多个插件
- 配置条件性插件使用
- 实现插件间的数据传递
插件监控
- 配置Prometheus监控
- 实现插件性能监控
- 设置插件告警
进阶练习
自定义插件开发
- 开发简单的中间件插件
- 创建DNS提供商插件
- 实现存储后端插件
插件优化
- 优化插件性能
- 实现插件缓存
- 管理插件资源使用
企业级配置
- 构建API网关
- 实现多租户架构
- 配置企业级安全
实战练习
插件生态系统
- 评估和选择插件
- 构建插件组合方案
- 实现插件版本管理
性能调优
- 分析插件性能瓶颈
- 优化插件配置
- 实现插件负载均衡
运维管理
- 建立插件监控体系
- 实现插件自动化部署
- 配置插件故障恢复
下一章我们将学习Caddy的部署和运维,这将帮助你在生产环境中成功运行Caddy。