Varnish Cache 运维教程 / 第12章:Docker 容器化部署
第12章:Docker 容器化部署
12.1 Docker 部署概述
Docker 容器化部署 Varnish 可以实现环境一致性、快速部署和易于扩展。
12.1.1 容器化优势
| 优势 | 说明 |
|---|---|
| 环境一致性 | 开发、测试、生产环境完全一致 |
| 快速部署 | 秒级启动,无需手动配置 |
| 易于扩展 | 容器编排实现水平扩展 |
| 版本管理 | 镜像标签实现版本管理 |
| 资源隔离 | CPU/内存资源限制 |
12.2 基本 Dockerfile
12.2.1 标准 Dockerfile
# Dockerfile
FROM ubuntu:22.04
# 安装 Varnish
RUN apt-get update && \
apt-get install -y \
curl \
gnupg \
apt-transport-https && \
curl -fsSL https://packagecloud.io/varnishcache/varnish70/gpgkey | \
gpg --dearmor -o /usr/share/keyrings/varnish-archive-keyring.gpg && \
echo "deb [signed-by=/usr/share/keyrings/varnish-archive-keyring.gpg] \
https://packagecloud.io/varnishcache/varnish70/ubuntu/ jammy main" | \
tee /etc/apt/sources.list.d/varnish.list && \
apt-get update && \
apt-get install -y varnish && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*
# 复制配置文件
COPY default.vcl /etc/varnish/default.vcl
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# 默认参数
ENV VARNISH_SIZE=256m
ENV VARNISH_PORT=6081
ENV VARNISH_ADMIN_PORT=6082
ENV BACKEND_HOST=backend
ENV BACKEND_PORT=80
# 暴露端口
EXPOSE ${VARNISH_PORT}
EXPOSE ${VARNISH_ADMIN_PORT}
# 健康检查
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost:${VARNISH_PORT}/ || exit 1
# 启动命令
ENTRYPOINT ["/entrypoint.sh"]
12.2.2 启动脚本
#!/bin/bash
# entrypoint.sh
# 替换环境变量
envsubst < /etc/varnish/default.vcl > /tmp/default.vcl
mv /tmp/default.vcl /etc/varnish/default.vcl
# 验证 VCL 配置
varnishd -C -f /etc/varnish/default.vcl || {
echo "VCL configuration error"
exit 1
}
# 启动 Varnish
exec varnishd \
-F \
-a :${VARNISH_PORT} \
-T localhost:${VARNISH_ADMIN_PORT} \
-f /etc/varnish/default.vcl \
-s malloc,${VARNISH_SIZE} \
-p thread_pool_min=5 \
-p thread_pool_max=500 \
-p thread_pool_timeout=120
12.2.3 VCL 配置文件
# default.vcl
vcl 4.1;
backend default {
.host = "${BACKEND_HOST}";
.port = "${BACKEND_PORT}";
.connect_timeout = 5s;
.first_byte_timeout = 30s;
.between_bytes_timeout = 10s;
.probe = {
.url = "/health";
.timeout = 3s;
.interval = 5s;
.window = 5;
.threshold = 3;
}
}
sub vcl_recv {
if (req.method != "GET" && req.method != "HEAD") {
return (pass);
}
if (req.url ~ "^/admin") {
return (pass);
}
return (hash);
}
sub vcl_backend_response {
if (beresp.http.Cache-Control ~ "no-cache|no-store|private") {
set beresp.uncacheable = true;
set beresp.ttl = 0s;
return (deliver);
}
if (bereq.url ~ "\.(css|js|jpg|png|gif|webp)$") {
set beresp.ttl = 1h;
} else {
set beresp.ttl = 5m;
}
set beresp.grace = 1h;
}
sub vcl_deliver {
if (obj.hits > 0) {
set resp.http.X-Cache = "HIT (" + obj.hits + ")";
} else {
set resp.http.X-Cache = "MISS";
}
}
12.3 Docker Compose 部署
12.3.1 基本 Compose 配置
# docker-compose.yml
version: '3.8'
services:
varnish:
build: .
image: varnish:7.5
container_name: varnish
ports:
- "6081:6081"
- "6082:6082"
environment:
- VARNISH_SIZE=256m
- BACKEND_HOST=backend
- BACKEND_PORT=80
volumes:
- ./default.vcl:/etc/varnish/default.vcl:ro
depends_on:
backend:
condition: service_healthy
networks:
- web
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M
backend:
image: nginx:alpine
container_name: backend
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- web
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 10s
timeout: 3s
retries: 3
networks:
web:
driver: bridge
12.3.2 生产环境 Compose
# docker-compose.prod.yml
version: '3.8'
services:
varnish:
build:
context: .
dockerfile: Dockerfile
image: myapp-varnish:latest
ports:
- "80:6081"
environment:
- VARNISH_SIZE=2G
- BACKEND_HOST=backend
- BACKEND_PORT=80
volumes:
- ./config/default.vcl:/etc/varnish/default.vcl:ro
- ./config/secret:/etc/varnish/secret:ro
networks:
- web
- backend-net
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
deploy:
mode: replicated
replicas: 2
resources:
limits:
cpus: '4'
memory: 4G
reservations:
cpus: '1'
memory: 512M
update_config:
parallelism: 1
delay: 10s
order: start-first
restart_policy:
condition: any
delay: 5s
max_attempts: 3
window: 120s
hitch:
image: hitch:latest
ports:
- "443:443"
volumes:
- ./certs:/etc/hitch/certs:ro
- ./config/hitch.conf:/etc/hitch/hitch.conf:ro
networks:
- web
depends_on:
- varnish
backend:
image: myapp-backend:latest
networks:
- backend-net
deploy:
mode: replicated
replicas: 3
resources:
limits:
cpus: '2'
memory: 2G
networks:
web:
driver: overlay
backend-net:
driver: overlay
internal: true
12.4 配置管理
12.4.1 使用配置文件挂载
# docker-compose.yml
services:
varnish:
volumes:
# 挂载 VCL 配置
- ./config/default.vcl:/etc/varnish/default.vcl:ro
# 挂载 secret 文件
- ./config/secret:/etc/varnish/secret:ro
# 挂载自定义 VMOD
- ./vmods:/usr/lib/varnish/vmods:ro
12.4.2 使用环境变量
# 使用环境变量的 VCL
vcl 4.1;
backend default {
.host = "${BACKEND_HOST}";
.port = "${BACKEND_PORT}";
}
sub vcl_recv {
# 使用环境变量控制行为
if (req.http.Host ~ "${ALLOWED_DOMAINS}") {
return (hash);
}
return (synth(403, "Forbidden"));
}
# 启动时传入环境变量
docker run -e BACKEND_HOST=10.0.0.1 \
-e BACKEND_PORT=8080 \
-e ALLOWED_DOMAINS="example\.com" \
varnish:latest
12.4.3 使用 Docker Secrets
# docker-compose.yml
version: '3.8'
services:
varnish:
image: varnish:latest
secrets:
- varnish_secret
- backend_password
environment:
- VARNISH_SECRET_FILE=/run/secrets/varnish_secret
secrets:
varnish_secret:
file: ./secrets/varnish_secret
backend_password:
file: ./secrets/backend_password
12.5 健康检查
12.5.1 Dockerfile 健康检查
# 简单健康检查
HEALTHCHECK --interval=10s --timeout=3s --retries=3 \
CMD curl -f http://localhost:6081/ || exit 1
# 详细健康检查
HEALTHCHECK --interval=10s --timeout=5s --retries=3 --start-period=30s \
CMD varnishadm status || exit 1
12.5.2 Compose 健康检查
services:
varnish:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:6081/health"]
interval: 10s
timeout: 5s
retries: 3
start_period: 30s
12.5.3 自定义健康检查端点
# VCL 中添加健康检查端点
sub vcl_recv {
if (req.url == "/health") {
# 检查后端健康状态
if (std.healthy(req.backend_hint)) {
return (synth(200, "OK"));
} else {
return (synth(503, "Backend Unhealthy"));
}
}
if (req.url == "/health/detailed") {
# 返回详细状态
return (synth(200, "Detailed OK"));
}
}
sub vcl_synth {
if (resp.status == 200 && resp.reason == "OK") {
set resp.http.Content-Type = "application/json";
synthetic({"{"status":"healthy","backend":""} + req.backend_hint + {"","cache_hit":""} + obj.hits + {"","}"});
return (deliver);
}
}
12.5.4 Docker 健康检查脚本
#!/bin/bash
# healthcheck.sh
# 检查 Varnish 进程
if ! pgrep -x "varnishd" > /dev/null; then
echo "Varnish process not running"
exit 1
fi
# 检查端口响应
if ! curl -sf http://localhost:6081/ > /dev/null 2>&1; then
echo "HTTP check failed"
exit 1
fi
# 检查管理端口
if ! varnishadm status > /dev/null 2>&1; then
echo "Admin check failed"
exit 1
fi
# 检查缓存命中率
HIT=$(varnishstat -1 -f MAIN.cache_hit | awk '{print $2}')
MISS=$(varnishstat -1 -f MAIN.cache_miss | awk '{print $2}')
TOTAL=$((HIT + MISS))
if [ $TOTAL -gt 100 ]; then
HITRATE=$((HIT * 100 / TOTAL))
if [ $HITRATE -lt 50 ]; then
echo "Low cache hit rate: ${HITRATE}%"
exit 1
fi
fi
echo "All checks passed"
exit 0
12.6 Nginx 集成
12.6.1 Nginx 作为 TLS 终结代理
# nginx.conf
upstream varnish {
server varnish:6081;
keepalive 32;
}
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location / {
proxy_pass http://varnish;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 启用 keepalive
proxy_http_version 1.1;
proxy_set_header Connection "";
# 超时设置
proxy_connect_timeout 5s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
12.6.2 Docker Compose 完整配置
# docker-compose.nginx.yml
version: '3.8'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- varnish
networks:
- web
varnish:
build: .
volumes:
- ./default.vcl:/etc/varnish/default.vcl:ro
environment:
- VARNISH_SIZE=256m
- BACKEND_HOST=backend
- BACKEND_PORT=80
depends_on:
- backend
networks:
- web
- backend-net
backend:
image: myapp-backend:latest
networks:
- backend-net
networks:
web:
driver: bridge
backend-net:
driver: bridge
internal: true
12.6.3 VCL 处理代理头部
# 处理来自 Nginx 的代理头
sub vcl_recv {
# 获取真实客户端 IP
if (req.http.X-Forwarded-For) {
set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
# 获取原始协议
if (req.http.X-Forwarded-Proto) {
set req.http.X-Proto = req.http.X-Forwarded-Proto;
} else {
set req.http.X-Proto = "http";
}
# 重定向 HTTP 到 HTTPS
if (req.http.X-Proto != "https") {
return (synth(750, "https://" + req.http.Host + req.url));
}
}
12.7 容器编排
12.7.1 Kubernetes 部署
# k8s-varnish.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: varnish
spec:
replicas: 3
selector:
matchLabels:
app: varnish
template:
metadata:
labels:
app: varnish
spec:
containers:
- name: varnish
image: varnish:7.5
ports:
- containerPort: 6081
name: http
- containerPort: 6082
name: admin
env:
- name: VARNISH_SIZE
value: "512m"
- name: BACKEND_HOST
valueFrom:
configMapKeyRef:
name: varnish-config
key: backend-host
volumeMounts:
- name: vcl-config
mountPath: /etc/varnish/default.vcl
subPath: default.vcl
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "2"
memory: "2Gi"
livenessProbe:
httpGet:
path: /health
port: 6081
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 6081
initialDelaySeconds: 10
periodSeconds: 5
volumes:
- name: vcl-config
configMap:
name: varnish-vcl
---
apiVersion: v1
kind: Service
metadata:
name: varnish
spec:
selector:
app: varnish
ports:
- name: http
port: 80
targetPort: 6081
type: ClusterIP
---
apiVersion: v1
kind: ConfigMap
metadata:
name: varnish-vcl
data:
default.vcl: |
vcl 4.1;
backend default {
.host = "backend";
.port = "80";
}
sub vcl_recv {
return (hash);
}
12.7.2 ConfigMap 管理
# varnish-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: varnish-config
data:
backend-host: "backend-service"
backend-port: "80"
varnish-size: "512m"
default.vcl: |
vcl 4.1;
backend default {
.host = "BACKEND_HOST_PLACEHOLDER";
.port = "BACKEND_PORT_PLACEHOLDER";
}
12.8 监控与日志
12.8.1 Docker 日志配置
services:
varnish:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "5"
tag: "{{.Name}}/{{.ID}}"
12.8.2 指标导出
# Prometheus 指标导出
services:
varnish-exporter:
image: prometheus/varnish-exporter:latest
command:
- "--varnish.addr=localhost:6082"
- "--web.listen-address=:9131"
ports:
- "9131:9131"
network_mode: "service:varnish"
12.9 注意事项
重要
- Docker 容器中 Varnish 的内存限制要与
-s malloc大小匹配- 使用
init进程或dumb-init处理信号,确保 Varnish 正确关闭- 生产环境使用多副本部署,配合负载均衡器
- 配置文件使用只读挂载(
:ro),避免意外修改- 日志输出到 stdout/stderr,便于 Docker 日志收集
- 定期更新基础镜像,修复安全漏洞
- 健康检查要覆盖关键功能,不仅仅是进程存在