orztv commited on
Commit
7695663
·
1 Parent(s): 273bb23
Files changed (6) hide show
  1. Dockerfile +41 -0
  2. README.md +90 -0
  3. services.json +24 -0
  4. start.sh +114 -0
  5. traefik/dynamic.yml +29 -0
  6. 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