0908
This commit is contained in:
@@ -1,27 +1,27 @@
|
||||
# Nginx Docker image
|
||||
# 包含自定义配置的反向代理
|
||||
# 使用官方 Nginx 镜像
|
||||
FROM nginx:09051708
|
||||
|
||||
FROM swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/nginx:alpine
|
||||
# 维护者信息
|
||||
LABEL maintainer="awei"
|
||||
LABEL description="HTTP server with SSL support"
|
||||
|
||||
LABEL maintainer="StoneLan"
|
||||
LABEL description="Nginx reverse proxy container"
|
||||
|
||||
# 复制 nginx 配置
|
||||
# 复制配置文件
|
||||
# conf.d 只放不依赖证书的常驻配置(80 端口:ACME 验证 + HTTPS 跳转)
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
COPY conf.d /etc/nginx/conf.d
|
||||
COPY verification.html /usr/share/nginx/html/verification.html
|
||||
|
||||
# 复制静态文件
|
||||
COPY html /usr/share/nginx/html
|
||||
# 创建日志目录与 HTTPS 配置目录
|
||||
# ssl.d 必须存在:nginx.conf 里的 wildcard include 在目录不存在时会报 emerg
|
||||
RUN mkdir -p /var/log/nginx /etc/nginx/ssl.d
|
||||
|
||||
# 创建必要的目录
|
||||
RUN mkdir -p /var/log/nginx && \
|
||||
mkdir -p /etc/nginx/ssl && \
|
||||
chown -R nginx:nginx /var/log/nginx && \
|
||||
chown -R nginx:nginx /etc/nginx
|
||||
# 暴露端口
|
||||
EXPOSE 80
|
||||
EXPOSE 8443
|
||||
|
||||
EXPOSE 80 443
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost/ || exit 1
|
||||
# 健康检查:/healthz 固定返回 200,不跟随 301,冷启动阶段也不会误判
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --quiet --tries=1 -O /dev/null http://127.0.0.1/healthz || exit 1
|
||||
|
||||
# 启动 Nginx
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
|
||||
30
nginx/conf.d/00-http.conf
Normal file
30
nginx/conf.d/00-http.conf
Normal file
@@ -0,0 +1,30 @@
|
||||
# HTTP 入口:ACME 验证 + HTTPS 跳转
|
||||
#
|
||||
# 【关键】这个文件必须常驻,且不能引用任何证书文件。
|
||||
# 首次部署时证书尚不存在,如果 nginx 加载了带 ssl_certificate 的 server 块,
|
||||
# 会直接 [emerg] cannot load certificate 退出;而申请证书又必须要有 80 端口可用
|
||||
# —— 形成死锁。因此所有 HTTPS server 块统一放到 /etc/nginx/ssl.d/,
|
||||
# 由 nginx/enable-ssl.sh 在证书就绪后再启用。
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name _;
|
||||
|
||||
# 健康检查探针(容器 HEALTHCHECK 使用),不跟随 301
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
return 200 "ok\n";
|
||||
}
|
||||
|
||||
# ACME HTTP-01 验证
|
||||
# certbot 通过 webroot 写入 /var/www/certbot/.well-known/acme-challenge/<token>
|
||||
# 该目录是 certbot-webroot 卷,nginx 以只读方式挂载
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
default_type text/plain;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
@@ -6,14 +6,22 @@ services:
|
||||
- "8081:80"
|
||||
- "8443:8443"
|
||||
volumes:
|
||||
# 证书(只读)
|
||||
- certbot-certs:/etc/letsencrypt:ro
|
||||
# ACME 验证目录(只读):certbot 写入挑战文件,nginx 直接对外提供
|
||||
- certbot-webroot:/var/www/certbot:ro
|
||||
# HTTPS server 块:默认为空,证书就绪后由 enable-ssl.sh 写入
|
||||
- ./ssl.d:/etc/nginx/ssl.d:ro
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- trim-default
|
||||
|
||||
volumes:
|
||||
# 与 certbot/docker-compose.yml 共用,物理卷名由 certbot 侧固定为同名
|
||||
certbot-certs:
|
||||
external: true
|
||||
certbot-webroot:
|
||||
external: true
|
||||
|
||||
networks:
|
||||
trim-default:
|
||||
|
||||
59
nginx/enable-ssl.sh
Normal file
59
nginx/enable-ssl.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# 启用 HTTPS 配置:只把"已经拿到证书"的域名对应的 server 块
|
||||
# 从 ssl.d.available/ 复制到 ssl.d/(nginx 挂载并 include 的目录),
|
||||
# 然后校验配置并优雅重载 nginx。
|
||||
#
|
||||
# 冷启动时 ssl.d/ 为空,nginx 只加载 conf.d/00-http.conf(不引用证书),
|
||||
# 因此可以在没有证书的情况下正常启动并提供 80 端口 —— 这是打破
|
||||
# "先有证书还是先有 nginx" 死锁的关键。
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# cron / 非登录 shell 下 PATH 极简,显式补齐
|
||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin${PATH:+:$PATH}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
AVAILABLE="$SCRIPT_DIR/ssl.d.available"
|
||||
ENABLED="$SCRIPT_DIR/ssl.d"
|
||||
DOMAINS="${DOMAINS:-fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn}"
|
||||
|
||||
mkdir -p "$ENABLED"
|
||||
cd "$SCRIPT_DIR"
|
||||
|
||||
copied=()
|
||||
for d in $DOMAINS; do
|
||||
conf="$AVAILABLE/$d.conf"
|
||||
if [ ! -f "$conf" ]; then
|
||||
echo "跳过 $d:缺少 $conf"
|
||||
continue
|
||||
fi
|
||||
|
||||
# nginx 容器以只读方式挂载了 certbot-certs 卷,用它判断证书是否就绪
|
||||
if ! docker compose exec -T nginx-proxy test -f "/etc/letsencrypt/live/$d/fullchain.pem" 2>/dev/null; then
|
||||
echo "跳过 $d:证书不存在(/etc/letsencrypt/live/$d/fullchain.pem)"
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! cmp -s "$conf" "$ENABLED/$d.conf" 2>/dev/null; then
|
||||
cp "$conf" "$ENABLED/$d.conf"
|
||||
copied+=("$ENABLED/$d.conf")
|
||||
echo "已启用 $d"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#copied[@]}" -eq 0 ]; then
|
||||
echo "没有需要变更的 HTTPS 配置"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if out=$(docker compose exec -T nginx-proxy nginx -t 2>&1); then
|
||||
echo "$out" | sed 's/^/ /'
|
||||
docker kill -s HUP nginx-proxy
|
||||
echo "nginx 已重载,HTTPS 配置生效"
|
||||
else
|
||||
echo "$out" | sed 's/^/ /'
|
||||
echo "nginx 配置校验失败,回滚本次变更" >&2
|
||||
rm -f "${copied[@]}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,29 +1,34 @@
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
|
||||
}
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
|
||||
error_log /var/log/nginx/error.log notice;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
include /etc/nginx/conf.d/*.conf;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
#tcp_nopush on;
|
||||
|
||||
keepalive_timeout 65;
|
||||
|
||||
# HTTPS server 块按需加载:
|
||||
# 该目录默认为空(nginx 的 wildcard include 匹配 0 个文件时不报错),
|
||||
# 证书申请成功后由 enable-ssl.sh 写入配置,从而避免冷启动时
|
||||
# 因证书文件不存在导致 nginx 拒绝启动。
|
||||
include /etc/nginx/ssl.d/*.conf;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,18 +1,6 @@
|
||||
# HTTP -> HTTPS 重定向(ACME 验证由独立 certbot 服务处理)
|
||||
server {
|
||||
listen 80;
|
||||
server_name fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
proxy_pass http://certbot:80;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
# HTTPS 配置
|
||||
# HTTPS 配置 —— fastgpt.stonelan.cn
|
||||
# 仅在 /etc/letsencrypt/live/fastgpt.stonelan.cn/fullchain.pem 存在时,
|
||||
# 由 nginx/enable-ssl.sh 复制到 ssl.d/ 才会被 nginx 加载。
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
server_name fastgpt.stonelan.cn;
|
||||
@@ -1,4 +1,5 @@
|
||||
# HTTPS 配置
|
||||
# HTTPS 配置 —— gitea.stonelan.cn
|
||||
# 仅在证书存在时由 nginx/enable-ssl.sh 复制到 ssl.d/ 才会生效。
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
server_name gitea.stonelan.cn;
|
||||
@@ -1,4 +1,5 @@
|
||||
# HTTPS 配置
|
||||
# HTTPS 配置 —— image.stonelan.cn
|
||||
# 仅在证书存在时由 nginx/enable-ssl.sh 复制到 ssl.d/ 才会生效。
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
server_name image.stonelan.cn;
|
||||
@@ -1,4 +1,5 @@
|
||||
# HTTPS 配置
|
||||
# HTTPS 配置 —— registry.stonelan.cn
|
||||
# 仅在证书存在时由 nginx/enable-ssl.sh 复制到 ssl.d/ 才会生效。
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
server_name registry.stonelan.cn;
|
||||
@@ -1,4 +1,5 @@
|
||||
# HTTPS 配置
|
||||
# HTTPS 配置 —— www.stonelan.cn
|
||||
# 仅在证书存在时由 nginx/enable-ssl.sh 复制到 ssl.d/ 才会生效。
|
||||
server {
|
||||
listen 8443 ssl;
|
||||
server_name www.stonelan.cn;
|
||||
2
nginx/ssl.d/.gitkeep
Normal file
2
nginx/ssl.d/.gitkeep
Normal file
@@ -0,0 +1,2 @@
|
||||
# nginx 从这里加载 HTTPS 配置(wildcard include,目录为空时 nginx 正常启动)。
|
||||
# 首次申请证书前本目录为空,由 ../enable-ssl.sh 按需写入。
|
||||
@@ -1,4 +1,8 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# 把仓库里的 nginx 配置同步到宿主机 nginx(非容器部署路径)。
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
# 检查并创建nginx用户
|
||||
if ! id -u nginx >/dev/null 2>&1; then
|
||||
@@ -6,31 +10,49 @@ if ! id -u nginx >/dev/null 2>&1; then
|
||||
sudo useradd -r -s /sbin/nologin nginx
|
||||
fi
|
||||
|
||||
# 创建 conf.d 目录(如果不存在)
|
||||
sudo mkdir -p /etc/nginx/conf.d
|
||||
# 创建配置目录(ssl.d 必须存在,nginx.conf 里的 wildcard include 在目录缺失时会报 emerg)
|
||||
sudo mkdir -p /etc/nginx/conf.d /etc/nginx/ssl.d
|
||||
|
||||
# 备份并覆盖 nginx.conf 文件
|
||||
sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
|
||||
sudo cp nginx.conf /etc/nginx/
|
||||
|
||||
# 备份并覆盖 conf.d 目录下的所有 .conf 文件
|
||||
# 备份并覆盖 conf.d 目录下的所有 .conf 文件(只放不依赖证书的配置)
|
||||
for conf in conf.d/*.conf; do
|
||||
filename=$(basename "$conf")
|
||||
sudo cp "/etc/nginx/conf.d/$filename" "/etc/nginx/conf.d/${filename}.bak" 2>/dev/null || true
|
||||
sudo cp "$conf" /etc/nginx/conf.d/
|
||||
done
|
||||
|
||||
# HTTPS 配置:只有证书已就绪的域名才启用,避免 nginx 因证书缺失拒绝启动
|
||||
DOMAINS="${DOMAINS:-fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn}"
|
||||
for d in $DOMAINS; do
|
||||
src="ssl.d.available/$d.conf"
|
||||
[ -f "$src" ] || { echo "跳过 $d:缺少 $src"; continue; }
|
||||
|
||||
if [ -f "/etc/letsencrypt/live/$d/fullchain.pem" ]; then
|
||||
sudo cp "$src" "/etc/nginx/ssl.d/$d.conf"
|
||||
echo "已启用 $d"
|
||||
else
|
||||
echo "跳过 $d:证书尚未生成"
|
||||
fi
|
||||
done
|
||||
|
||||
# 测试并重载配置
|
||||
if sudo nginx -t; then
|
||||
sudo systemctl reload nginx
|
||||
echo "配置已生效,HTTPS已启用"
|
||||
if sudo systemctl is-active --quiet nginx; then
|
||||
sudo systemctl reload nginx
|
||||
echo "配置已重载"
|
||||
else
|
||||
sudo systemctl start nginx
|
||||
echo "nginx 已启动"
|
||||
fi
|
||||
else
|
||||
echo "配置测试失败,已恢复备份"
|
||||
# 恢复 nginx.conf 备份
|
||||
sudo cp /etc/nginx/nginx.conf.bak /etc/nginx/nginx.conf
|
||||
# 恢复 conf.d 目录下的所有 .conf 文件备份
|
||||
for conf in conf.d/*.conf; do
|
||||
filename=$(basename "$conf")
|
||||
sudo mv -f "/etc/nginx/conf.d/${filename}.bak" "/etc/nginx/conf.d/$filename" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user