Compare commits

...

12 Commits

Author SHA1 Message Date
awei
3332c6c940 清理二进制文件,修复 Dockerfile conf.d 残留问题 2026-09-09 07:08:24 +00:00
张威33321
a9ec40a289 0908 2026-09-09 15:04:54 +08:00
张威33321
fb87be680d 0908 2026-09-08 20:36:17 +08:00
awei
58d65188a1 feat: 添加 Let's Encrypt SSL 证书管理
- 新增独立 certbot 服务项目
- nginx 配置添加 HTTPS 8443 端口监听
- frpc.toml HTTPS 代理指向 nginx:8443
- 支持自动续签并重载 nginx
- 共享 Docker volume 传递证书
2026-09-08 11:43:34 +00:00
Your Name
b028e6dd73 0908 2026-09-08 11:26:41 +00:00
张威33321
b93e85b428 0908 2026-09-08 19:10:25 +08:00
张威33321
8fa08f4b98 0908 2026-09-08 17:45:49 +08:00
张威33321
42d14a19e6 0908 2026-09-08 17:43:18 +08:00
张威33321
9a2defc0ee 0908 2026-09-08 17:39:29 +08:00
张威33321
538dc1339d 0908 2026-09-08 17:33:06 +08:00
12600k-rog-d4
774cae3c02 0907 2026-09-07 10:29:54 +08:00
12600k-rog-d4
1e5c48ffdd chore: update nginx config, add oneapi, verification pages and codebuddy skills 2026-08-20 02:01:32 +08:00
36 changed files with 642 additions and 266231 deletions

View File

@@ -0,0 +1,37 @@
---
name: docker-build
description: 根据dockerfile构建镜像
---
# Docker Build
## Overview
此技能用于协助完成 Docker 镜像的构建
## When to Use
根据docker file 构建镜像
## Core Workflow
### 1. 清理node1 服务器 ~/dockerbuild/ 目录下,对应项目的文件
### 2. 同步代码到node1 服务器 ~/dockerbuild/ 目录下,对应项目的文件
### 3. 执行docker build 构建镜像
### 4. 导出镜像,并下载到当前项目对应的把目录下
## node1服务器信息
ip 10.0.3.5
用户名 awei
已做ssh 互信
## 镜像命名
- 名字保持和对应目录名称一致
- 版本采用月日时分格式,如 041923344月19日23时34分

2
.gitignore vendored
View File

@@ -1,8 +1,10 @@
# Docker images
docker-images/*.tar
nginx/*.tar
# frp binary (large file)
frpc-client/frp_*.tar.gz
frpc-client/frp.tar.gz.b64
# IDE
.idea/

View File

@@ -1,55 +0,0 @@
# MEMORY.md
## 服务器信息
### node1
- **IP**: 10.0.3.5
- **用户名**: awei
- **SSH 互信**: 已配置
## Docker 镜像构建流程
### 关键步骤
1. **清理服务端目录**(避免文件残留导致问题)
```bash
ssh awei@10.0.3.5 "rm -rf /tmp/<目录名> && mkdir -p /tmp/<目录名>"
```
2. **上传源码文件**
```bash
scp <本地文件> awei@10.0.3.5:/tmp/<目录名>/
```
3. **构建镜像**(带版本号)
```bash
ssh awei@10.0.3.5 "cd /tmp/<目录名> && docker build -t stonelan/<镜像名>:YYYYMMDD -t stonelan/<镜像名>:latest ."
```
4. **导出镜像**
```bash
ssh awei@10.0.3.5 "docker save -o /tmp/<镜像名>-YYYYMMDD.tar stonelan/<镜像名>:YYYYMMDD"
```
5. **下载到本地**
```bash
scp awei@10.0.3.5:/tmp/<镜像名>-YYYYMMDD.tar <本地路径>/
```
### 现有镜像
| 镜像名称 | 版本号 | 基础镜像 | 说明 |
|---------|--------|---------|------|
| stonelan/nginx | 1.0.1 | nginx:alpine | 反向代理,含自定义配置 |
| stonelan/frpc | 1.0.1 | alpine:3.19 | FRP 客户端 v0.61.1 |
### 注意事项
- **版本号格式**:语义化版本 `X.Y.Z`(如 1.0.0、1.0.1、1.1.0
- **每次构建必须递增版本号**,避免重复
- 构建前先清理服务端临时目录,避免旧文件干扰
- 本地 tar.gz 等二进制文件通过 scp 传输正常,无需特殊处理
### 已知问题
- **scp 传输速度慢**Windows OpenSSH 客户端传输速度约 0.76 MB/s12MB 文件需 2-3 分钟),暂无解决方案

View File

@@ -1,47 +0,0 @@
version: '3.8'
services:
navidrome:
container_name: navidrome
image: swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/deluan/navidrome:pr-4181
ports:
- "4533:4533"
restart: always
environment:
# 扫描音乐库的计划
ND_SCANSCHEDULE: "@every 1h"
# 日志级别
ND_LOGLEVEL: "info"
# 默认语言
ND_DEFAULTLANGUAGE: "zh"
# 会话超时时间
ND_SESSIONTIMEOUT: "24h"
# 启用下载功能
ND_ENABLEDOWNLOADS: "true"
# 启用收藏夹
ND_ENABLEFAVOURITES: "true"
# 启用共享功能
ND_ENABLESHARING: "false"
# 在 UI 中启用5星评级
ND_ENABLESTARRATING: "true"
# 启用转码配置
ND_ENABLETRANSCODINGCONFIG: "true"
# 转码缓存大小
ND_TRANSCODINGCACHESIZE: "2000M"
# 图片缓存大小
ND_IMAGECACHESIZE: "1000M"
# 音乐文件路径
ND_MUSICFOLDER: "/music"
# 数据路径
ND_DATAFOLDER: "/data"
volumes:
# 将宿主机的数据目录挂载到容器的数据目录,用于数据持久化
- "./data:/data"
# 将宿主机的音乐目录挂载到容器的音乐目录,只读模式
- "./music:/music:ro"
networks:
- navidrome-net
networks:
navidrome-net:
driver: bridge

98
acme/README.md Normal file
View File

@@ -0,0 +1,98 @@
# 证书管理服务acme.sh
用宿主机上的 [acme.sh](https://github.com/acmesh-official/acme.sh) 管理 Let's Encrypt 证书:
纯 Shell 实现,不依赖 Python / snap / Docker 容器。
## 为什么换成 acme.sh
之前用 certbot 容器时有个绕不开的别扭点certbot 的 `--deploy-hook` 在**容器内**执行,
而我们要 reload 的 nginx 是**宿主机上的容器**,只能在容器里挂 `docker.sock`(等于把宿主机
root 交给容器),或者退而求其次每天无条件发一次 HUP。
acme.sh 直接跑在宿主机,`--reloadcmd` 天然在宿主机执行,**只在证书真正更新时才 reload**
顺带把 certbot 容器、Dockerfile、docker.sock、compose PATH 问题全部消掉。
## 架构
```
宿主机 容器
───────────────────────────────────── ──────────────────────────
acme.sh~/.acme.sh自带 cron
├─ 写挑战文件 ──▶ /var/www/certbot ──bind mount(ro)──▶ nginx-proxy
├─ 写证书 ──▶ /etc/letsencrypt ──bind mount(ro)──▶ nginx-proxy
└─ 续签后执行 reloadcmd: docker kill -s HUP nginx-proxy
用户 ─▶ frps(80/443) ─▶ frpc ─▶ nginx-proxy(80/8443)
```
## 目录约定
| 宿主机路径 | 用途 |
|---|---|
| `/var/www/certbot` | ACME HTTP-01 挑战文件 |
| `/etc/letsencrypt/live/<CERT_NAME>/` | 证书(默认 CERT_NAME=stonelan.cn |
| `nginx/ssl.d/` | HTTPS server 块,证书就绪后由 `enable-ssl.sh` 写入 |
5 个域名合成**一张 SAN 证书**放在同一个目录nginx 的 5 个 server 块都引用它。
## 使用方法
### 1. 安装
```bash
cd common-server-public/acme
chmod +x install.sh issue.sh renew.sh check-cert.sh
./install.sh
```
### 2. 申请证书(一键引导)
```bash
sudo ./issue.sh
```
依次完成:建目录 → 起 nginx此时无 HTTPS 配置)→ 申请证书 → 安装证书 →
写入 `ssl.d/` 并重载 nginx。
可用环境变量覆盖:
```bash
EMAIL=you@example.com \
CERT_NAME=stonelan.cn \
DOMAINS="fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn" \
sudo -E ./issue.sh
```
### 3. 自动续签
`install.sh` 已让 acme.sh 自己写入 cron**无需额外配置**。
建议再加一条兜底告警(`crontab.example`
```cron
0 8 * * * bash <绝对路径>/check-cert.sh >> /var/log/acme-check.log 2>&1
```
## 故障排查
```bash
acme.sh --list # 已申请的证书
acme.sh --cron --debug 2 # 带调试跑一次续签
./check-cert.sh # 剩余天数
# 确认 nginx 只加载了 HTTP 配置(冷启动时属正常)
docker exec nginx-proxy nginx -T | grep -c "listen 8443"
# ACME 路径可达性
echo probe | sudo tee /var/www/certbot/.well-known/acme-challenge/test
curl -i http://fastgpt.stonelan.cn/.well-known/acme-challenge/test
```
## 注意事项
1. 需要 root或 sudo运行要写 `/etc/letsencrypt``/var/www`
2. nginx 容器 bind mount 的是**宿主机路径**,宿主机上这两个目录必须存在,
否则 docker 会自动创建成 root 所有的空目录
3. 挑战文件需要让容器内 nginx 可读,目录保持 755`issue.sh` 已处理)
4. CA 已显式固定为 Let's Encryptacme.sh 3.x 默认是 ZeroSSL
5. 证书有效期 90 天acme.sh 默认在剩余 30 天内续签

32
acme/check-cert.sh Normal file
View File

@@ -0,0 +1,32 @@
#!/usr/bin/env bash
#
# 检查证书剩余有效期,低于阈值时以非 0 退出,便于接入告警。
# 自动续签有可能静默失败,必须有一个独立的检查点。
#
set -euo pipefail
CERT_NAME="${CERT_NAME:-stonelan.cn}"
CERT="${CERT:-/etc/letsencrypt/live/$CERT_NAME/cert.pem}"
WARN_DAYS="${WARN_DAYS:-20}"
if [ ! -f "$CERT" ]; then
echo "证书不存在:$CERT" >&2
exit 1
fi
end="$(openssl x509 -enddate -noout -in "$CERT" | cut -d= -f2)"
end_ts="$(date -d "$end" +%s)"
days=$(( (end_ts - $(date +%s)) / 86400 ))
echo "证书文件:$CERT"
echo "到期时间:$end(剩余 $days 天)"
openssl x509 -subject -issuer -noout -in "$CERT" | sed 's/^/ /'
openssl x509 -text -noout -in "$CERT" \
| grep -A1 "Subject Alternative Name" | sed 's/^/ /'
if [ "$days" -lt "$WARN_DAYS" ]; then
echo "警告:剩余 $days 天,低于阈值 $WARN_DAYS" >&2
exit 1
fi
echo "证书状态正常"

10
acme/crontab.example Normal file
View File

@@ -0,0 +1,10 @@
# acme.sh 安装时会自动写入一条 cron每天执行 ~/.acme.sh/acme.sh --cron
# 续签成功后自动执行 --reloadcmd所以续签本身不需要再配 cron。
#
# 这里只需要一个"有效期检查"作为兜底告警:
# 自动续签可能静默失败,剩余天数低于 20 天时脚本以非 0 退出。
0 8 * * * bash /home/awei/workspace/common-server-public/acme/check-cert.sh >> /var/log/acme-check.log 2>&1
# 如需强制每天显式跑一次续签(一般不必),可加:
# 0 3 * * * bash /home/awei/workspace/common-server-public/acme/renew.sh >> /var/log/acme-renew.log 2>&1

25
acme/install.sh Normal file
View File

@@ -0,0 +1,25 @@
#!/usr/bin/env bash
#
# 安装 acme.sh纯 Shell ACME 客户端,不依赖 Python / 容器 / snap
#
set -euo pipefail
EMAIL="${EMAIL:-admin@stonelan.cn}"
ACME="${ACME:-$HOME/.acme.sh/acme.sh}"
if [ -s "$ACME" ]; then
echo "acme.sh 已安装:$ACME"
else
echo "==> 安装 acme.sh"
curl https://get.acme.sh | sh -s email="$EMAIL"
fi
# acme.sh 3.x 默认 CA 是 ZeroSSL这里显式固定为 Let's Encrypt
"$ACME" --set-default-ca --server letsencrypt
echo "==> acme.sh 就绪"
"$ACME" --version
# 安装脚本会自动写入一条 cron每天执行 acme.sh --cron无需再手配续签
echo "==> 已注册的 cron"
crontab -l 2>/dev/null | grep acme.sh || echo " (未找到,请手动添加,参考 crontab.example"

61
acme/issue.sh Normal file
View File

@@ -0,0 +1,61 @@
#!/usr/bin/env bash
#
# 首次申请证书并启用 HTTPS完整引导
#
# 1. 准备宿主机目录acme.sh 写挑战文件nginx 容器 bind mount 只读
# 2. 启动 nginx —— 此时 ssl.d/ 为空,只加载 80 端口配置,不依赖证书
# 3. 申请 SAN 证书webroot 验证,挑战文件由 nginx 直接对外提供)
# 4. 把证书安装到 /etc/letsencrypt/live/<CERT_NAME>/
# --reloadcmd 会被 acme.sh 记住,之后每次自动续签都执行
# 5. 启用 HTTPS server 块并重载 nginx
#
# 需要 root 权限(要写 /etc/letsencrypt 与 /var/wwwsudo bash issue.sh
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
NGINX_DIR="$SCRIPT_DIR/../nginx"
EMAIL="${EMAIL:-admin@stonelan.cn}"
CERT_NAME="${CERT_NAME:-stonelan.cn}"
WEBROOT="${WEBROOT:-/var/www/certbot}"
CERT_HOME="${CERT_HOME:-/etc/letsencrypt/live/$CERT_NAME}"
DOMAINS="${DOMAINS:-fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn}"
ACME="${ACME:-$HOME/.acme.sh/acme.sh}"
# acme.sh 的 Main_Domain 就是 -d 列表的第一个,--install-cert 要用它
PRIMARY="${DOMAINS%% *}"
if [ ! -s "$ACME" ]; then
echo "未找到 acme.sh请先执行$SCRIPT_DIR/install.sh" >&2
exit 1
fi
# 1. 目录
mkdir -p "$WEBROOT/.well-known/acme-challenge" "$CERT_HOME"
chmod 755 "$WEBROOT" "$WEBROOT/.well-known" "$WEBROOT/.well-known/acme-challenge"
# 2. nginx无证书也能起来
echo "==> 启动 nginx"
docker compose -f "$NGINX_DIR/docker-compose.yml" up -d
# 3. 申请
args=()
for d in $DOMAINS; do
args+=(-d "$d")
done
echo "==> 申请证书(主域 $PRIMARY,共 $(echo $DOMAINS | wc -w) 个域名):$DOMAINS"
"$ACME" --issue --webroot "$WEBROOT" --server letsencrypt "${args[@]}"
# 4. 安装证书
echo "==> 安装证书到 $CERT_HOME"
"$ACME" --install-cert -d "$PRIMARY" \
--cert-file "$CERT_HOME/cert.pem" \
--key-file "$CERT_HOME/privkey.pem" \
--fullchain-file "$CERT_HOME/fullchain.pem" \
--reloadcmd "docker kill -s HUP nginx-proxy"
# 5. 启用 HTTPS
echo "==> 启用 HTTPS 配置"
exec bash "$NGINX_DIR/enable-ssl.sh"

20
acme/renew.sh Normal file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
#
# 手动触发一次续签检查。
#
# acme.sh 安装时已经自动写入了 cron每天 ~/.acme.sh/acme.sh --cron
# 正常情况下不需要手动执行,这个脚本只是方便排查和立即验证。
# 续签成功时 acme.sh 会自动执行 issue.sh 里登记的 --reloadcmd。
#
set -euo pipefail
ACME="${ACME:-$HOME/.acme.sh/acme.sh}"
if [ ! -s "$ACME" ]; then
echo "未找到 acme.sh请先执行本目录下的 install.sh" >&2
exit 1
fi
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 开始续签检查"
"$ACME" --cron
echo "[$(date '+%Y-%m-%d %H:%M:%S')] 续签检查完成"

View File

@@ -1,39 +0,0 @@
#!/bin/bash
# 定义变量
DOMAIN="registry.deepseak.icu" # 主域名
ADDITIONAL_DOMAINS=("active.deepseak.icu" "bbs.deepseak.icu" "image.deepseak.icu" "gitea.deepseak.icu") # 额外域名
EMAIL="hpuzhagnwei@163.com" # 请替换为你的邮箱地址
# 检查 Certbot 是否安装
if ! command -v certbot &> /dev/null
then
echo "Certbot 未安装,开始安装..."
sudo apt update
sudo apt install -y certbot python3-certbot-nginx
fi
# 申请证书
echo "开始申请证书..."
# 构建域名参数
DOMAIN_ARGS="-d $DOMAIN"
for domain in "${ADDITIONAL_DOMAINS[@]}"; do
DOMAIN_ARGS="$DOMAIN_ARGS -d $domain"
done
sudo certbot certonly --nginx $DOMAIN_ARGS --email $EMAIL --agree-tos --no-eff-email
# 检查证书是否申请成功
if [ $? -eq 0 ]; then
echo "证书申请成功!"
else
echo "证书申请失败,请检查错误信息。"
exit 1
fi
# 安装自动续期任务
# echo "安装自动续期任务..."
# (crontab -l 2>/dev/null; echo "0 12 * * * /usr/bin/certbot renew --quiet") | crontab -
# echo "自动续期任务安装成功!"

3
csp-task.md Normal file
View File

@@ -0,0 +1,3 @@
1. nginx配置有更改构建新的镜像
2. scp部署到100.66.1.5 这个机器上 用户名admin 密码 admin123

View File

@@ -1,151 +0,0 @@
# Dolibarr ERP/CRM Docker部署指南
本指南将帮助您使用Docker Compose快速部署Dolibarr ERP/CRM系统。
## 功能特点
- 使用官方Dolibarr Docker镜像
- 集成MariaDB数据库
- 数据持久化存储
- 可自定义配置
## 系统要求
- Docker 18.06.0+
- Docker Compose 1.22.0+
## 部署步骤
### 1. 克隆或下载配置文件
确保您已经在`dolibarr`目录下有`docker-compose.yml`文件。
### 2. 启动服务
`dolibarr`目录下执行以下命令:
```bash
docker-compose up -d
```
这将启动两个容器:
- `dolibarr`Dolibarr ERP/CRM应用
- `dolibarr_db`MariaDB数据库
### 3. 访问Dolibarr
启动完成后通过以下地址访问Dolibarr
```
http://localhost:8080
```
### 4. 完成安装
首次访问时您需要完成Dolibarr的安装配置
1. 选择您的语言并点击"开始安装"
2. 检查系统需求(应该都是绿色的)
3. 数据库配置:
- 数据库类型MySQL
- 数据库服务器db
- 数据库名称dolibarr
- 数据库用户dolibarr
- 数据库密码dolibarr_password
4. 点击"测试连接"并确认连接成功
5. 完成剩余的安装步骤
## 配置说明
### 环境变量
`docker-compose.yml`文件中,您可以修改以下环境变量:
- `DOLI_ADMIN_LOGIN`Dolibarr管理员用户名默认admin
- `DOLI_ADMIN_PASSWORD`Dolibarr管理员密码默认admin_password
- `DOLI_URL_ROOT`Dolibarr访问URL默认http://localhost:8080
- `MYSQL_ROOT_PASSWORD`MariaDB root用户密码默认root_password
- `MYSQL_DATABASE`数据库名称默认dolibarr
- `MYSQL_USER`数据库用户名默认dolibarr
- `MYSQL_PASSWORD`数据库密码默认dolibarr_password
### 端口映射
默认将Dolibarr的80端口映射到主机的8080端口。您可以通过修改`ports`部分来更改:
```yaml
ports:
- "您选择的端口:80"
```
### 数据持久化
使用Docker卷来持久化存储数据
- `dolibarr_html`存储Dolibarr应用文件
- `dolibarr_documents`存储Dolibarr文档和上传文件
- `dolibarr_db`存储MariaDB数据库数据
## 管理命令
### 查看日志
```bash
# 查看Dolibarr日志
docker-compose logs dolibarr
# 查看数据库日志
docker-compose logs db
# 查看所有日志
docker-compose logs
```
### 停止服务
```bash
docker-compose down
```
### 重启服务
```bash
docker-compose restart
```
### 更新服务
```bash
# 拉取最新镜像
docker-compose pull
# 重新启动服务
docker-compose up -d
```
## 安全建议
1. 生产环境中请修改默认密码
2. 考虑使用HTTPS协议访问
3. 定期备份数据卷
4. 限制容器的网络访问
## 常见问题
### Q: 无法访问Dolibarr
A: 检查容器是否正在运行,端口是否正确映射,以及防火墙设置。
### Q: 忘记管理员密码?
A: 您可以通过修改数据库中的`llx_user`表来重置密码,或者在环境变量中重新设置`DOLI_ADMIN_PASSWORD`并重启容器。
### Q: 如何备份数据?
A: 您可以使用`docker cp`命令将数据卷中的内容复制到本地:
```bash
docker cp dolibarr:/var/www/html ./backup/html
docker cp dolibarr:/var/www/documents ./backup/documents
docker exec dolibarr_db mysqldump -u root -proot_password dolibarr > ./backup/dolibarr.sql
```
## 联系方式
如有问题,请参考[Dolibarr官方文档](https://wiki.dolibarr.org/index.php/Main_Page)或[Docker Hub页面](https://hub.docker.com/r/dolibarr/dolibarr/)。

View File

@@ -1,42 +0,0 @@
version: '3.7'
services:
# Dolibarr ERP/CRM
dolibarr:
image: dolibarr/dolibarr:latest
container_name: dolibarr
restart: unless-stopped
ports:
- "8080:80"
depends_on:
- db
environment:
- DOLI_DB_HOST=db
- DOLI_DB_USER=dolibarr
- DOLI_DB_PASSWORD=dolibarr_password
- DOLI_DB_NAME=dolibarr
- DOLI_ADMIN_LOGIN=admin
- DOLI_ADMIN_PASSWORD=admin_password
- DOLI_URL_ROOT=http://localhost:8080
volumes:
- dolibarr_html:/var/www/html
- dolibarr_documents:/var/www/documents
# MariaDB Database
db:
image: mariadb:10.6
container_name: dolibarr_db
restart: unless-stopped
environment:
- MYSQL_ROOT_PASSWORD=root_password
- MYSQL_DATABASE=dolibarr
- MYSQL_USER=dolibarr
- MYSQL_PASSWORD=dolibarr_password
volumes:
- dolibarr_db:/var/lib/mysql
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
volumes:
dolibarr_html:
dolibarr_documents:
dolibarr_db:

View File

@@ -1,6 +1,6 @@
services:
frpc:
image: frpc-client:latest
image: frpc-client:09051708
container_name: frpc-client
restart: unless-stopped
networks:

File diff suppressed because it is too large Load Diff

View File

@@ -1,47 +1,19 @@
# frpc 基础配置
serverAddr = "47.239.121.24"
serverPort = 7000
auth.method = "token"
auth.token = "8f3c9b7d5e2a4f6h1i0j"
[[proxies]]
name = "http"
type = "tcp"
localIP = "nginx-proxy"
localPort = 80
remotePort = 80
[[proxies]]
name = "https"
type = "tcp"
localIP = "10.0.3.148"
localPort = 4430
remotePort = 443
# frpc 基础配置
serverAddr = "47.113.230.48"
serverPort = 7000
auth.method = "token"
auth.token = "8f3c9b7d5e2a4f6h1i0j"
[[proxies]]
name = "http"
type = "tcp"
localIP = "nginx-proxy"
localPort = 80
remotePort = 80
[[proxies]]
name = "https"
type = "tcp"
localIP = "nginx-proxy"
localPort = 8443
remotePort = 443

View File

@@ -1,24 +1,29 @@
# Nginx Docker image
# 包含自定义配置的反向代理
# 使用官方 Nginx 镜像
FROM nginx:09051708
FROM 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
RUN rm -rf /etc/nginx/conf.d/*
COPY conf.d /etc/nginx/conf.d
COPY verification.html /usr/share/nginx/html/verification.html
# 创建必要的目录
RUN mkdir -p /var/log/nginx && \
mkdir -p /etc/nginx/ssl && \
chown -R nginx:nginx /var/log/nginx && \
chown -R nginx:nginx /etc/nginx
# 创建日志目录与 HTTPS 配置目录
# ssl.d 必须存在nginx.conf 里的 wildcard include 在目录不存在时会报 emerg
RUN mkdir -p /var/log/nginx /etc/nginx/ssl.d
EXPOSE 80 443
# 暴露端口
EXPOSE 80
EXPOSE 8443
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost/health || 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
View 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 验证
# acme.sh 通过 webroot 写入 /var/www/certbot/.well-known/acme-challenge/<token>
# 这是宿主机目录nginx 容器以只读方式 bind mount
location /.well-known/acme-challenge/ {
root /var/www/certbot;
default_type text/plain;
}
location / {
return 301 https://$host$request_uri;
}
}

View File

@@ -1,16 +0,0 @@
server {
listen 80;
server_name gitea.membank.xyz;
client_max_body_size 200m;
location / {
proxy_pass http://gitea:3000;
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
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;
}
}

View File

@@ -1,17 +0,0 @@
server {
listen 80;
server_name image.membank.xyz;
location / {
proxy_pass http://10.0.3.14:3004;
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;
# 添加代理超时配置
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}

View File

@@ -1,29 +0,0 @@
server {
listen 80;
server_name membank.membank.xyz;
location / {
proxy_pass http://10.0.3.148:3000;
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;
# 添加代理超时配置
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
location /bbs-go {
proxy_pass http://10.0.3.148:9000;
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;
# 添加代理超时配置
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}

View File

@@ -1,39 +0,0 @@
server {
listen 80;
server_name navidrome.membank.xyz;
location / {
proxy_pass http://127.0.0.1:4533;
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;
# 添加代理超时配置
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
# 针对Navidrome的特殊配置
# 支持大文件上传和流媒体传输
client_max_body_size 100M;
proxy_buffering off;
# 启用gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_proxied any;
gzip_comp_level 6;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;
}
}

View File

@@ -1,17 +0,0 @@
server {
listen 80;
server_name registry.membank.xyz;
client_max_body_size 500m;
location / {
proxy_pass http://127.0.0.1:3005;
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;
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}

View File

@@ -1,14 +1,21 @@
services:
nginx:
image: nginx:alpine
image: nginx:09051708
container_name: nginx-proxy
ports:
- "8081:80"
- "4430:443"
- "8443:8443"
volumes:
- ./conf.d:/etc/nginx/conf.d:ro
- ./nginx.conf:/etc/nginx/nginx.conf:ro
# 证书:宿主机 acme.sh 写入nginx 只读
- /etc/letsencrypt:/etc/letsencrypt:ro
# ACME HTTP-01 挑战文件:宿主机 acme.sh 写入nginx 只读
- /var/www/certbot:/var/www/certbot:ro
# HTTPS server 块:默认为空,证书就绪后由 enable-ssl.sh 写入
- ./ssl.d:/etc/nginx/ssl.d:ro
restart: unless-stopped
networks:
- trim-default
networks:
trim-default:
external: true

59
nginx/enable-ssl.sh Normal file
View File

@@ -0,0 +1,59 @@
#!/usr/bin/env bash
#
# 启用 HTTPS 配置:证书就绪后,把 ssl.d.available/ 下的 server 块
# 复制进 ssl.d/nginx 容器 bind mount 并 include 的目录),校验后优雅重载 nginx。
#
# 冷启动时 ssl.d/ 为空nginx 只加载 conf.d/00-http.conf不引用证书
# 因此可以在没有证书的情况下启动并提供 80 端口 —— 这是打破
# "先有证书还是先有 nginx" 死锁的关键。
#
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AVAILABLE="$SCRIPT_DIR/ssl.d.available"
ENABLED="$SCRIPT_DIR/ssl.d"
CERT_NAME="${CERT_NAME:-stonelan.cn}"
CERT_FULL="/etc/letsencrypt/live/$CERT_NAME/fullchain.pem"
DOMAINS="${DOMAINS:-fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn}"
# 证书在宿主机上,直接判断即可(不再需要进容器)
if [ ! -f "$CERT_FULL" ]; then
echo "证书尚未生成:$CERT_FULL"
echo "请先执行 acme/issue.sh成功后再运行本脚本"
exit 0
fi
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
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

View File

@@ -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;
}

View File

@@ -1,24 +1,35 @@
# HTTPS 配置 —— fastgpt.stonelan.cn
# 5 个域名共用一张 SAN 证书,统一放在 /etc/letsencrypt/live/stonelan.cn/。
# 由 nginx/enable-ssl.sh 在证书就绪后复制到 ssl.d/ 才会被 nginx 加载。
server {
listen 80;
server_name fastgpt.membank.xyz;
listen 8443 ssl;
server_name fastgpt.stonelan.cn;
ssl_certificate /etc/letsencrypt/live/stonelan.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stonelan.cn/privkey.pem;
# SSL 优化配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
location / {
proxy_pass http://10.0.3.14:3000;
proxy_pass http://10.0.3.173:3000;
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;
# 添加代理超时配置
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
# 支持大文件上传和API请求
client_max_body_size 100M;
proxy_buffering off;
# 启用gzip压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
@@ -35,4 +46,4 @@ server {
application/atom+xml
image/svg+xml;
}
}
}

View File

@@ -0,0 +1,30 @@
# HTTPS 配置 —— gitea.stonelan.cn
# 5 个域名共用一张 SAN 证书,统一放在 /etc/letsencrypt/live/stonelan.cn/。
# 由 nginx/enable-ssl.sh 在证书就绪后复制到 ssl.d/ 才会生效。
server {
listen 8443 ssl;
server_name gitea.stonelan.cn;
client_max_body_size 200m;
ssl_certificate /etc/letsencrypt/live/stonelan.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stonelan.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
location / {
proxy_pass http://gitea:3000;
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
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;
}
}

View File

@@ -0,0 +1,29 @@
# HTTPS 配置 —— image.stonelan.cn
# 5 个域名共用一张 SAN 证书,统一放在 /etc/letsencrypt/live/stonelan.cn/。
# 由 nginx/enable-ssl.sh 在证书就绪后复制到 ssl.d/ 才会生效。
server {
listen 8443 ssl;
server_name image.stonelan.cn;
ssl_certificate /etc/letsencrypt/live/stonelan.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stonelan.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
location / {
proxy_pass http://10.0.3.173:3004;
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;
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}

View File

@@ -0,0 +1,30 @@
# HTTPS 配置 —— registry.stonelan.cn
# 5 个域名共用一张 SAN 证书,统一放在 /etc/letsencrypt/live/stonelan.cn/。
# 由 nginx/enable-ssl.sh 在证书就绪后复制到 ssl.d/ 才会生效。
server {
listen 8443 ssl;
server_name registry.stonelan.cn;
client_max_body_size 500m;
ssl_certificate /etc/letsencrypt/live/stonelan.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stonelan.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
location / {
proxy_pass http://127.0.0.1:3005;
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;
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}

View File

@@ -0,0 +1,29 @@
# HTTPS 配置 —— www.stonelan.cn
# 5 个域名共用一张 SAN 证书,统一放在 /etc/letsencrypt/live/stonelan.cn/。
# 由 nginx/enable-ssl.sh 在证书就绪后复制到 ssl.d/ 才会生效。
server {
listen 8443 ssl;
server_name www.stonelan.cn;
ssl_certificate /etc/letsencrypt/live/stonelan.cn/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stonelan.cn/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
location / {
proxy_pass http://10.0.3.173:8080;
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;
proxy_connect_timeout 60s;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
}

2
nginx/ssl.d/.gitkeep Normal file
View File

@@ -0,0 +1,2 @@
# nginx 从这里加载 HTTPS 配置wildcard include目录为空时 nginx 正常启动)。
# 首次申请证书前本目录为空,由 ../enable-ssl.sh 按需写入。

View File

@@ -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,55 @@ 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 因证书缺失拒绝启动
# 5 个域名共用一张 SAN 证书,统一放在 CERT_NAME 目录下
CERT_NAME="${CERT_NAME:-stonelan.cn}"
CERT_FULL="/etc/letsencrypt/live/$CERT_NAME/fullchain.pem"
DOMAINS="${DOMAINS:-fastgpt.stonelan.cn gitea.stonelan.cn image.stonelan.cn registry.stonelan.cn www.stonelan.cn}"
if [ -f "$CERT_FULL" ]; then
for d in $DOMAINS; do
src="ssl.d.available/$d.conf"
if [ -f "$src" ]; then
sudo cp "$src" "/etc/nginx/ssl.d/$d.conf"
echo "已启用 $d"
else
echo "跳过 $d:缺少 $src"
fi
done
else
echo "证书尚未生成($CERT_FULL),跳过 HTTPS 配置"
fi
# 测试并重载配置
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

1
nginx/verification.html Normal file
View File

@@ -0,0 +1 @@
verify_01965aad87b3aae1c15f4373ae9efcfb