diff --git a/deploy/README.md b/deploy/README.md index 0d33c16..3c5cf31 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -39,19 +39,20 @@ sudo bash /home/ec2-user/pangolin/scripts/cutover.sh docker-compose -p pangolin stop edge && sudo systemctl start nginx ``` -## 证书续期(切换后) +## 证书续期(自动,无需宿主 cron) + +`pangolin-edge` 是自定义镜像(官方 nginx + certbot),**容器内每 12h 自动 `certbot renew`(DNS-01/Cloudflare),续期后在本容器内 `nginx -s reload`**。见 `edge/pangolin-entrypoint.sh`。无需宿主 cron、无需 docker socket。 ```bash -# 一次性:把 blog/jiu/vpn 三证书改为 DNS-01(Cloudflare) -cd ~/pangolin +# 手动验证续期可用(staging,不动正式证书) +docker exec pangolin-edge certbot renew --dry-run +# 首次签发/重配(仅手动,用独立 certbot 服务) docker-compose -p pangolin run --rm certbot certonly --dns-cloudflare \ - --dns-cloudflare-credentials /etc/cloudflare.ini \ - -d blog.51yanmei.com -d jiu.51yanmei.com -d vpn.51yanmei.com --keep-until-expiring -# 续期 + 重载(建议加进宿主 cron,每日一次) -docker-compose -p pangolin run --rm certbot renew -q -docker exec pangolin-edge nginx -s reload + --dns-cloudflare-credentials /etc/cloudflare.ini -d <域名> --cert-name <域名> ``` +> 证书文件在 `/etc/letsencrypt`(rw 挂载进 edge);CF 凭据 `secrets/cloudflare.ini` 挂到 `/etc/cloudflare.ini`。 + ## 取客户端导入串(Hy2,含口令) ```bash diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 1308ffb..bda7b56 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -4,9 +4,12 @@ # project 名由命令行 `-p pangolin` 指定(兼容宿主 docker-compose 版本)。 services: - # 分发层:接管 80/443,移植现网全部路由(host 网络才能绑端口并回连 127.0.0.1 后端) + # 分发层:接管 80/443,移植现网全部路由(host 网络才能绑端口并回连 127.0.0.1 后端)。 + # 自定义镜像 = 官方 nginx + certbot,容器自己续签自己用的证书(无需宿主 cron / docker socket)。 edge: - image: nginx:1.27-alpine + # 镜像由脚本用 `docker build -t pangolin-edge:local ./edge` 预先构建 + # (宿主 docker-compose 版本不支持 compose build)。 + image: pangolin-edge:local container_name: pangolin-edge restart: unless-stopped network_mode: host @@ -14,9 +17,10 @@ services: - ./edge/nginx.conf:/etc/nginx/nginx.conf:ro - ./edge/stream.conf:/etc/nginx/stream.conf:ro - ./edge/conf.d:/etc/nginx/conf.d:ro - - /etc/letsencrypt:/etc/letsencrypt:ro # 证书(只读) - - /opt/jiu:/opt/jiu:ro # jiu 静态(images/web/marketing) - - /var/www/pay:/var/www/pay:ro # 收款页静态 + - /etc/letsencrypt:/etc/letsencrypt # rw:certbot 续期写入,nginx 读取 + - ./secrets/cloudflare.ini:/etc/cloudflare.ini:ro # DNS-01 续期凭据 + - /opt/jiu:/opt/jiu:ro # jiu 静态(images/web/marketing) + - /var/www/pay:/var/www/pay:ro # 收款页静态 healthcheck: test: ["CMD", "nginx", "-t"] interval: 1m @@ -34,8 +38,9 @@ services: - ./secrets/hy2:/etc/sing-box/cert:ro command: ["run", "-c", "/etc/sing-box/config.json"] - # 证书续期(DNS-01 / Cloudflare)—— 按需运行:`docker compose run --rm certbot renew` - # 由宿主 cron 每日调用,renew 后宿主再 `docker exec pangolin-edge nginx -s reload`。 + # 证书工具(DNS-01 / Cloudflare)—— 仅手动用于首次签发/重配: + # docker-compose -p pangolin run --rm certbot certonly --dns-cloudflare ... + # 日常【自动续期由 edge 容器自身完成】(见 edge/pangolin-entrypoint.sh),无需本服务。 certbot: image: certbot/dns-cloudflare:latest container_name: pangolin-certbot diff --git a/deploy/edge/Dockerfile b/deploy/edge/Dockerfile new file mode 100644 index 0000000..6e48a0f --- /dev/null +++ b/deploy/edge/Dockerfile @@ -0,0 +1,10 @@ +# pangolin-edge:官方 nginx + certbot(DNS-01/Cloudflare),让分发层容器自己续签自己用的证书。 +FROM nginx:1.27-alpine + +# certbot + Cloudflare DNS 插件(EC2 实测:certbot 3.0.1) +RUN apk add --no-cache certbot certbot-dns-cloudflare + +COPY pangolin-entrypoint.sh /usr/local/bin/pangolin-entrypoint.sh +RUN chmod +x /usr/local/bin/pangolin-entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/pangolin-entrypoint.sh"] diff --git a/deploy/edge/pangolin-entrypoint.sh b/deploy/edge/pangolin-entrypoint.sh new file mode 100644 index 0000000..20f7e67 --- /dev/null +++ b/deploy/edge/pangolin-entrypoint.sh @@ -0,0 +1,21 @@ +#!/bin/sh +# pangolin-edge 入口:前台跑 nginx,后台跑证书续期(续期后在【本容器内】热重载 nginx)。 +# 续期 = certbot renew(DNS-01/Cloudflare),无需 docker socket / 宿主 cron / 跨容器信号。 +set -e + +# 后台续期循环:每 12h 检查一次;有证书续期则热重载,无则什么都不做。 +( + # 启动后先等一会,避免与 nginx 启动抢资源 + sleep 60 + while true; do + if certbot renew --quiet --deploy-hook "nginx -s reload"; then + : + else + echo "[pangolin] certbot renew 失败,下个周期重试" >&2 + fi + sleep 12h + done +) & + +# 前台:沿用官方入口脚本(会执行 /docker-entrypoint.d/*)再启动 nginx +exec /docker-entrypoint.sh nginx -g 'daemon off;' diff --git a/deploy/scripts/cutover.sh b/deploy/scripts/cutover.sh index 57ca79e..9a22593 100755 --- a/deploy/scripts/cutover.sh +++ b/deploy/scripts/cutover.sh @@ -35,6 +35,7 @@ rollback() { echo "==> [2/4] 停用宿主 nginx 并起 edge 容器(交接窗口,秒级)" systemctl stop nginx systemctl disable nginx 2>/dev/null || true +docker build -t pangolin-edge:local ./edge || { echo " edge 镜像构建失败" >&2; rollback; } if ! docker-compose -p pangolin up -d edge singbox; then echo " edge 启动失败" >&2; rollback fi diff --git a/deploy/scripts/deploy.sh b/deploy/scripts/deploy.sh index f94fddf..f298d5d 100755 --- a/deploy/scripts/deploy.sh +++ b/deploy/scripts/deploy.sh @@ -36,8 +36,9 @@ if systemctl is-active --quiet nginx 2>/dev/null; then echo " 宿主 nginx 仍在运行 → 仅校验 edge 配置,不启动(切换请跑 cutover.sh)" edge_validate && echo " edge 配置校验通过 ✅" else - echo " 宿主 nginx 未运行 → 拉起 edge" + echo " 宿主 nginx 未运行 → 构建并拉起 edge" edge_validate + docker build -t pangolin-edge:local ./edge docker-compose -p pangolin up -d edge docker-compose -p pangolin ps edge fi