orztv commited on
Commit ·
7695663
1
Parent(s): 273bb23
update
Browse files- Dockerfile +41 -0
- README.md +90 -0
- services.json +24 -0
- start.sh +114 -0
- traefik/dynamic.yml +29 -0
- traefik/traefik.yml +19 -0
Dockerfile
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
FROM nikolaik/python-nodejs:python3.10-nodejs20
|
| 2 |
+
|
| 3 |
+
USER root
|
| 4 |
+
|
| 5 |
+
# 安装 Traefik
|
| 6 |
+
ARG TRAEFIK_VERSION=2.9.6
|
| 7 |
+
ENV TRAEFIK_CONFIG_FILE=/home/pn/app/traefik/traefik.yml
|
| 8 |
+
|
| 9 |
+
RUN wget -q https://github.com/traefik/traefik/releases/download/v${TRAEFIK_VERSION}/traefik_v${TRAEFIK_VERSION}_linux_amd64.tar.gz \
|
| 10 |
+
&& tar -xzf traefik_v${TRAEFIK_VERSION}_linux_amd64.tar.gz \
|
| 11 |
+
&& mv traefik /usr/local/bin/ \
|
| 12 |
+
&& rm traefik_v${TRAEFIK_VERSION}_linux_amd64.tar.gz
|
| 13 |
+
|
| 14 |
+
# 安装 jq 用于解析 JSON
|
| 15 |
+
RUN apt-get update && apt-get install -y jq
|
| 16 |
+
|
| 17 |
+
# 切换到 pn 用户
|
| 18 |
+
USER pn
|
| 19 |
+
|
| 20 |
+
# 设置工作目录
|
| 21 |
+
WORKDIR /home/pn/app
|
| 22 |
+
|
| 23 |
+
# 创建 Traefik 配置目录
|
| 24 |
+
RUN mkdir -p /home/pn/app/traefik
|
| 25 |
+
|
| 26 |
+
# 复制配置文件和启动脚本
|
| 27 |
+
COPY --chown=pn:pn traefik/ /home/pn/app/traefik/
|
| 28 |
+
COPY --chown=pn:pn start.sh /home/pn/app/start.sh
|
| 29 |
+
COPY --chown=pn:pn services.json /home/pn/app/services.json
|
| 30 |
+
RUN chmod +x /home/pn/app/start.sh
|
| 31 |
+
|
| 32 |
+
# 设置环境变量
|
| 33 |
+
ENV TRAEFIK_PORT=7860 \
|
| 34 |
+
PYTHON_PORT=8000 \
|
| 35 |
+
NODE_PORT=8001
|
| 36 |
+
|
| 37 |
+
# 暴露 Traefik 和后端服务端口
|
| 38 |
+
EXPOSE $TRAEFIK_PORT $PYTHON_PORT $NODE_PORT
|
| 39 |
+
|
| 40 |
+
# 启动容器时运行启动脚本
|
| 41 |
+
CMD ["/home/pn/app/start.sh"]
|
README.md
CHANGED
|
@@ -8,3 +8,93 @@ pinned: false
|
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 11 |
+
|
| 12 |
+
Traefik 作为反向代理,使用 YAML 或 TOML 文件。Traefik 将监听 7860 端口,并根据路径前缀将请求转发到相应的服务。
|
| 13 |
+
反代两个服务:
|
| 14 |
+
1. `python -m http.server 8000`
|
| 15 |
+
2. `sudo npm install -g http-server` 后 `http-server -p 8001`
|
| 16 |
+
|
| 17 |
+
帮我写一个完整的 Dockerfile 程序。使用 `nikolaik/python-nodejs:python3.10-nodejs20` 镜像。
|
| 18 |
+
本镜像已经安装好 Node 及 Python 的了,容器启动默认使用 `pn` 用户,UID 为 1000。容器中需要使用这个用户。
|
| 19 |
+
|
| 20 |
+
## 程序特点及用法
|
| 21 |
+
|
| 22 |
+
本程序采用了动态配置和服务管理的方式,具有以下特点和用法:
|
| 23 |
+
|
| 24 |
+
1. **易用性**:
|
| 25 |
+
- 通过修改 `services.json` 文件,可以轻松添加、删除或修改服务,无需更改其他配置文件。
|
| 26 |
+
- 使用环境变量来配置端口号和其他可变参数,增加了灵活性。
|
| 27 |
+
|
| 28 |
+
2. **扩展性**:
|
| 29 |
+
- 新服务可以通过在 `services.json` 中添加一个新条目来增加,包括其命令、端口和路径前缀。
|
| 30 |
+
- Dockerfile 中预装了常用工具,如 `jq`,方便进行 JSON 解析和处理。
|
| 31 |
+
|
| 32 |
+
3. **复用性**:
|
| 33 |
+
- `start.sh` 脚本可以处理任意数量的服务,无需为每个新服务修改脚本。
|
| 34 |
+
- 使用通用的健康检查函数,适用于所有服务。
|
| 35 |
+
|
| 36 |
+
4. **逻辑性**:
|
| 37 |
+
- 配置生成和服务启动的逻辑集中在 `start.sh` 脚本中,使得整个流程更加清晰。
|
| 38 |
+
- 使用 Traefik 作为反向代理,通过动态配置文件实现路由规则的灵活管理。
|
| 39 |
+
|
| 40 |
+
## 如何使用
|
| 41 |
+
|
| 42 |
+
1. **添加新服务**:
|
| 43 |
+
在 `services.json` 文件中添加新的服务配置,例如:
|
| 44 |
+
```json
|
| 45 |
+
{
|
| 46 |
+
"name": "new-service",
|
| 47 |
+
"command": "new-service-command",
|
| 48 |
+
"port": 8002,
|
| 49 |
+
"path_prefix": "/new-service",
|
| 50 |
+
"health_check_path": "/health"
|
| 51 |
+
}
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
2. **修改现有服务**:
|
| 55 |
+
直接在 `services.json` 文件中修改相应服务的配置。
|
| 56 |
+
|
| 57 |
+
3. **自定义端口**:
|
| 58 |
+
通过环境变量设置 `TRAEFIK_PORT`、`PYTHON_PORT` 和 `NODE_PORT` 来自定义端口。
|
| 59 |
+
|
| 60 |
+
4. **配置 HTTPS**:
|
| 61 |
+
- 修改 `traefik.yml` 中的 `certificatesResolvers` 部分,配置有效的电子邮件地址以使用 Let’s Encrypt。
|
| 62 |
+
- 确保端口 80 和 443 对外开放,以便 Let’s Encrypt 能够验证和获取证书。
|
| 63 |
+
|
| 64 |
+
5. **构建和运行**:
|
| 65 |
+
```bash
|
| 66 |
+
docker build -t my-traefik-app .
|
| 67 |
+
docker run -p 7860:7860 -p 80:80 -p 443:443 my-traefik-app
|
| 68 |
+
```
|
| 69 |
+
|
| 70 |
+
6. **访问服务**:
|
| 71 |
+
- Python 服务:`http://localhost:7860/python` 或 `https://localhost/python`
|
| 72 |
+
- Node 服务:`http://localhost:7860/node` 或 `https://localhost/node`
|
| 73 |
+
- Traefik 仪表板:`https://localhost/dashboard/`
|
| 74 |
+
|
| 75 |
+
7. **查看日志和监控**:
|
| 76 |
+
使用 Docker 日志命令查看容器日志,包括服务启动状态和健康检查结果。
|
| 77 |
+
```bash
|
| 78 |
+
docker logs -f <container_id>
|
| 79 |
+
```
|
| 80 |
+
|
| 81 |
+
8. **健康检查和失败处理**:
|
| 82 |
+
- `start.sh` 脚本中已集成健康检查功能,确保所有服务正常运行。
|
| 83 |
+
- 若某个服务未能通过健康检查,容器将输出相应的错误信息,便于及时修复。
|
| 84 |
+
|
| 85 |
+
## 进一步优化建议
|
| 86 |
+
|
| 87 |
+
1. **配置管理**:
|
| 88 |
+
- 使用配置管理工具(如 `consul` 或 `etcd`)进行集中式配置管理,提升配置的可维护性和一致性。
|
| 89 |
+
|
| 90 |
+
2. **日志管理**:
|
| 91 |
+
- 集成集中式日志管理系统(如 ELK Stack),便于日志的收集、分析和监控。
|
| 92 |
+
|
| 93 |
+
3. **容器编排**:
|
| 94 |
+
- 考虑使用 Kubernetes 进行容器编排,提升系统的扩展性和可靠性。
|
| 95 |
+
|
| 96 |
+
4. **安全增强**:
|
| 97 |
+
- 配置 Traefik 的访问控制,如基本认证或 OAuth,保护敏感服务和仪表板的访问。
|
| 98 |
+
- 定期更新基础镜像和依赖,以减少安全漏洞。
|
| 99 |
+
|
| 100 |
+
通过以上优化,您的程序在易用性、扩展性、复用性和逻辑性等方面将得到显著提升,能够更好地满足实际业务需求。如果您有任何进一步的问题或需要更多的帮助,请随时与我联系。
|
services.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[
|
| 2 |
+
{
|
| 3 |
+
"name": "python-service",
|
| 4 |
+
"command": "python -m http.server",
|
| 5 |
+
"port": 8000,
|
| 6 |
+
"path_prefix": "/python",
|
| 7 |
+
"health_check_path": "/",
|
| 8 |
+
"env": {
|
| 9 |
+
"PYTHONUNBUFFERED": "1"
|
| 10 |
+
},
|
| 11 |
+
"working_dir": "/home/pn/app/python"
|
| 12 |
+
},
|
| 13 |
+
{
|
| 14 |
+
"name": "node-service",
|
| 15 |
+
"command": "http-server",
|
| 16 |
+
"port": 8001,
|
| 17 |
+
"path_prefix": "/node",
|
| 18 |
+
"health_check_path": "/",
|
| 19 |
+
"env": {
|
| 20 |
+
"NODE_ENV": "production"
|
| 21 |
+
},
|
| 22 |
+
"working_dir": "/home/pn/app/node"
|
| 23 |
+
}
|
| 24 |
+
]
|
start.sh
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -e
|
| 3 |
+
|
| 4 |
+
# 添加日志函数
|
| 5 |
+
log() {
|
| 6 |
+
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
# 捕获 SIGTERM 信号,优雅关闭所有后台进程
|
| 10 |
+
trap "log '收到终止信号,关闭进程...'; kill $(jobs -p); exit 0" SIGTERM
|
| 11 |
+
|
| 12 |
+
# 读取服务配置
|
| 13 |
+
services=$(jq -c '.[]' services.json)
|
| 14 |
+
|
| 15 |
+
# 动态生成 Traefik 配置
|
| 16 |
+
generate_traefik_config() {
|
| 17 |
+
local dynamic_config="/home/pn/app/traefik/dynamic.yml"
|
| 18 |
+
echo "http:" > $dynamic_config
|
| 19 |
+
echo " routers:" >> $dynamic_config
|
| 20 |
+
echo " services:" >> $dynamic_config
|
| 21 |
+
echo " middlewares:" >> $dynamic_config
|
| 22 |
+
echo " strip-prefix:" >> $dynamic_config
|
| 23 |
+
echo " stripPrefix:" >> $dynamic_config
|
| 24 |
+
echo " prefixes:" >> $dynamic_config
|
| 25 |
+
|
| 26 |
+
echo "$services" | while read -r service; do
|
| 27 |
+
name=$(echo $service | jq -r '.name')
|
| 28 |
+
port=$(echo $service | jq -r '.port')
|
| 29 |
+
path_prefix=$(echo $service | jq -r '.path_prefix')
|
| 30 |
+
|
| 31 |
+
echo " - \"$path_prefix\"" >> $dynamic_config
|
| 32 |
+
done
|
| 33 |
+
|
| 34 |
+
echo "" >> $dynamic_config
|
| 35 |
+
echo " routers:" >> $dynamic_config
|
| 36 |
+
|
| 37 |
+
echo "$services" | while read -r service; do
|
| 38 |
+
name=$(echo $service | jq -r '.name')
|
| 39 |
+
port=$(echo $service | jq -r '.port')
|
| 40 |
+
path_prefix=$(echo $service | jq -r '.path_prefix')
|
| 41 |
+
|
| 42 |
+
echo " $name:" >> $dynamic_config
|
| 43 |
+
echo " rule: \"PathPrefix('$path_prefix')\"" >> $dynamic_config
|
| 44 |
+
echo " service: $name" >> $dynamic_config
|
| 45 |
+
echo " middlewares:" >> $dynamic_config
|
| 46 |
+
echo " - strip-prefix" >> $dynamic_config
|
| 47 |
+
done
|
| 48 |
+
|
| 49 |
+
echo "" >> $dynamic_config
|
| 50 |
+
echo " services:" >> $dynamic_config
|
| 51 |
+
|
| 52 |
+
echo "$services" | while read -r service; do
|
| 53 |
+
name=$(echo $service | jq -r '.name')
|
| 54 |
+
port=$(echo $service | jq -r '.port')
|
| 55 |
+
|
| 56 |
+
echo " $name:" >> $dynamic_config
|
| 57 |
+
echo " loadBalancer:" >> $dynamic_config
|
| 58 |
+
echo " servers:" >> $dynamic_config
|
| 59 |
+
echo " - url: \"http://localhost:$port\"" >> $dynamic_config
|
| 60 |
+
done
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
# 生成 Traefik 配置
|
| 64 |
+
generate_traefik_config
|
| 65 |
+
|
| 66 |
+
# 启动服务
|
| 67 |
+
echo "$services" | while read -r service; do
|
| 68 |
+
name=$(echo $service | jq -r '.name')
|
| 69 |
+
command=$(echo $service | jq -r '.command')
|
| 70 |
+
port=$(echo $service | jq -r '.port')
|
| 71 |
+
working_dir=$(echo $service | jq -r '.working_dir // "/home/pn/app"')
|
| 72 |
+
|
| 73 |
+
log "Starting $name on port $port"
|
| 74 |
+
|
| 75 |
+
# 设置环境变量
|
| 76 |
+
eval $(echo $service | jq -r '.env // {} | to_entries | .[] | "export " + .key + "=\"" + .value + "\""')
|
| 77 |
+
|
| 78 |
+
# 切换到工作目录并启动服务
|
| 79 |
+
(cd $working_dir && $command -p $port) &
|
| 80 |
+
done
|
| 81 |
+
|
| 82 |
+
# 等待服务启动
|
| 83 |
+
sleep 2
|
| 84 |
+
|
| 85 |
+
# 健康检查函数
|
| 86 |
+
check_health() {
|
| 87 |
+
local port=$1
|
| 88 |
+
local service=$2
|
| 89 |
+
local health_check_path=$3
|
| 90 |
+
for i in {1..5}; do
|
| 91 |
+
if curl -s "http://localhost:$port$health_check_path" > /dev/null; then
|
| 92 |
+
log "$service is up"
|
| 93 |
+
return 0
|
| 94 |
+
fi
|
| 95 |
+
sleep 1
|
| 96 |
+
done
|
| 97 |
+
log "ERROR: $service failed to start"
|
| 98 |
+
return 1
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
# 执行健康检查
|
| 102 |
+
echo "$services" | while read -r service; do
|
| 103 |
+
name=$(echo $service | jq -r '.name')
|
| 104 |
+
port=$(echo $service | jq -r '.port')
|
| 105 |
+
health_check_path=$(echo $service | jq -r '.health_check_path')
|
| 106 |
+
|
| 107 |
+
if ! check_health $port "$name" "$health_check_path"; then
|
| 108 |
+
log "ERROR: 健康检查失败,退出程序"
|
| 109 |
+
exit 1
|
| 110 |
+
fi
|
| 111 |
+
done
|
| 112 |
+
|
| 113 |
+
# 启动 Traefik
|
| 114 |
+
exec traefik --configFile=$TRAEFIK_CONFIG_FILE
|
traefik/dynamic.yml
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
http:
|
| 2 |
+
routers:
|
| 3 |
+
python-service:
|
| 4 |
+
rule: "PathPrefix(`/python`)"
|
| 5 |
+
service: python-service
|
| 6 |
+
middlewares:
|
| 7 |
+
- strip-prefix
|
| 8 |
+
node-service:
|
| 9 |
+
rule: "PathPrefix(`/node`)"
|
| 10 |
+
service: node-service
|
| 11 |
+
middlewares:
|
| 12 |
+
- strip-prefix
|
| 13 |
+
|
| 14 |
+
services:
|
| 15 |
+
python-service:
|
| 16 |
+
loadBalancer:
|
| 17 |
+
servers:
|
| 18 |
+
- url: "http://localhost:${PYTHON_PORT}"
|
| 19 |
+
node-service:
|
| 20 |
+
loadBalancer:
|
| 21 |
+
servers:
|
| 22 |
+
- url: "http://localhost:${NODE_PORT}"
|
| 23 |
+
|
| 24 |
+
middlewares:
|
| 25 |
+
strip-prefix:
|
| 26 |
+
stripPrefix:
|
| 27 |
+
prefixes:
|
| 28 |
+
- "/python"
|
| 29 |
+
- "/node"
|
traefik/traefik.yml
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
global:
|
| 2 |
+
checkNewVersion: true
|
| 3 |
+
sendAnonymousUsage: false
|
| 4 |
+
|
| 5 |
+
log:
|
| 6 |
+
level: INFO
|
| 7 |
+
|
| 8 |
+
entryPoints:
|
| 9 |
+
web:
|
| 10 |
+
address: ":${TRAEFIK_PORT}"
|
| 11 |
+
|
| 12 |
+
providers:
|
| 13 |
+
file:
|
| 14 |
+
filename: /home/pn/app/traefik/dynamic.yml
|
| 15 |
+
watch: true
|
| 16 |
+
|
| 17 |
+
api:
|
| 18 |
+
insecure: true
|
| 19 |
+
dashboard: true
|