refactor: 更新项目结构和文档,移除不再使用的配置文件

- 修改README.md,更新项目特性和目录结构说明
- 重命名基础设施代码目录为tofu,并添加Docker Swarm配置目录
- 移除不再使用的Docker Compose和Traefik配置文件
- 更新Terraform配置,专注于Oracle Cloud支持,移除华为云相关配置
- 清理开发环境变量和示例文件
This commit is contained in:
2025-09-20 16:49:32 +00:00
parent 7eb4a33523
commit 377f176501
45 changed files with 3278 additions and 69 deletions

View File

@@ -0,0 +1,138 @@
version: '3.8'
services:
# Traefik 负载均衡器
traefik:
image: traefik:v3.0
container_name: traefik
restart: unless-stopped
ports:
- "80:80"
- "443:443"
- "8080:8080" # Traefik Dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./certs:/certs:ro
environment:
- CONSUL_ENDPOINTS=consul1:8500,consul2:8500,consul3:8500
depends_on:
- consul1
- consul2
- consul3
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.dashboard.rule=Host(`traefik.local`)"
- "traefik.http.routers.dashboard.service=api@internal"
# Consul 集群节点 1
consul1:
image: consul:1.16.1
container_name: consul1
hostname: consul1
restart: unless-stopped
ports:
- "8500:8500"
volumes:
- consul1_data:/consul/data
command: >
consul agent -server -bootstrap-expect=3
-datacenter=dc1 -data-dir=/consul/data
-node=consul1 -bind=0.0.0.0 -client=0.0.0.0
-retry-join=consul2 -retry-join=consul3
-ui-config='{"enabled": true}'
-log-level=INFO
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.consul.rule=Host(`consul.local`)"
- "traefik.http.services.consul.loadbalancer.server.port=8500"
# Consul 集群节点 2
consul2:
image: consul:1.16.1
container_name: consul2
hostname: consul2
restart: unless-stopped
volumes:
- consul2_data:/consul/data
command: >
consul agent -server -bootstrap-expect=3
-datacenter=dc1 -data-dir=/consul/data
-node=consul2 -bind=0.0.0.0 -client=0.0.0.0
-retry-join=consul1 -retry-join=consul3
-log-level=INFO
networks:
- traefik-net
# Consul 集群节点 3
consul3:
image: consul:1.16.1
container_name: consul3
hostname: consul3
restart: unless-stopped
volumes:
- consul3_data:/consul/data
command: >
consul agent -server -bootstrap-expect=3
-datacenter=dc1 -data-dir=/consul/data
-node=consul3 -bind=0.0.0.0 -client=0.0.0.0
-retry-join=consul1 -retry-join=consul2
-log-level=INFO
networks:
- traefik-net
# 示例 Web 应用
web-app:
image: nginx:alpine
container_name: web-app
restart: unless-stopped
volumes:
- ./web-content:/usr/share/nginx/html:ro
environment:
- CONSUL_URL=http://consul1:8500
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`app.local`)"
- "traefik.http.services.web.loadbalancer.server.port=80"
- "traefik.http.routers.web.middlewares=web-auth"
- "traefik.http.middlewares.web-auth.basicauth.users=admin:$$2y$$10$$..."
# 示例 API 应用
api-app:
image: node:18-alpine
container_name: api-app
restart: unless-stopped
working_dir: /app
volumes:
- ./api:/app
command: ["node", "server.js"]
environment:
- CONSUL_URL=http://consul1:8500
- NODE_ENV=production
networks:
- traefik-net
labels:
- "traefik.enable=true"
- "traefik.http.routers.api.rule=Host(`api.local`) && PathPrefix(`/api`)"
- "traefik.http.services.api.loadbalancer.server.port=3000"
- "traefik.http.routers.api.middlewares=api-cors"
- "traefik.http.middlewares.api-cors.headers.accesscontrolallowmethods=GET,POST,PUT,DELETE"
- "traefik.http.middlewares.api-cors.headers.accesscontrolalloworigin=*"
volumes:
consul1_data:
consul2_data:
consul3_data:
networks:
traefik-net:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16

60
swarm/configs/traefik.yml Normal file
View File

@@ -0,0 +1,60 @@
# Traefik 配置文件
api:
dashboard: true
insecure: true # 仅开发环境,生产环境请使用 HTTPS
# 入口点配置
entryPoints:
web:
address: ":80"
websecure:
address: ":443"
# 提供者配置
providers:
# Docker 标签发现
docker:
endpoint: "unix:///var/run/docker.sock"
exposedByDefault: false
watch: true
# Consul 服务发现
consul:
endpoints:
- "consul1:8500"
- "consul2:8500"
- "consul3:8500"
watch: true
# 文件配置提供者
file:
filename: /etc/traefik/dynamic.yml
watch: true
# 证书解析器Let's Encrypt
certificatesResolvers:
letsencrypt:
acme:
email: admin@example.com
storage: /certs/acme.json
httpChallenge:
entryPoint: web
# 日志配置
log:
level: INFO
filePath: "/var/log/traefik.log"
accessLog:
filePath: "/var/log/access.log"
# 指标配置
metrics:
prometheus:
addEntryPointsLabels: true
addServicesLabels: true
# 全局配置
global:
checkNewVersion: false
sendAnonymousUsage: false

184
swarm/scripts/swarm-manager.sh Executable file
View File

@@ -0,0 +1,184 @@
#!/bin/bash
# Docker Swarm 管理脚本
set -euo pipefail
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查是否在 Swarm 模式
check_swarm_mode() {
if docker info --format '{{.Swarm.LocalNodeState}}' | grep -q "active"; then
log_success "Docker Swarm 模式已激活"
return 0
else
log_error "Docker Swarm 模式未激活"
return 1
fi
}
# 初始化 Swarm
init_swarm() {
log_info "初始化 Docker Swarm..."
if docker swarm init; then
log_success "Docker Swarm 初始化成功"
log_info "要添加工作节点,请在其他主机上运行:"
docker swarm join-token worker
else
log_error "Docker Swarm 初始化失败"
return 1
fi
}
# 部署堆栈
deploy_stack() {
local stack_name="$1"
local compose_file="$2"
log_info "部署堆栈: $stack_name"
if [[ ! -f "$compose_file" ]]; then
log_error "Compose 文件不存在: $compose_file"
return 1
fi
if docker stack deploy -c "$compose_file" "$stack_name"; then
log_success "堆栈 $stack_name 部署成功"
else
log_error "堆栈 $stack_name 部署失败"
return 1
fi
}
# 列出堆栈
list_stacks() {
log_info "当前部署的堆栈:"
docker stack ls
}
# 查看堆栈服务
show_stack_services() {
local stack_name="$1"
log_info "堆栈 $stack_name 的服务:"
docker stack services "$stack_name"
}
# 删除堆栈
remove_stack() {
local stack_name="$1"
log_info "删除堆栈: $stack_name"
if docker stack rm "$stack_name"; then
log_success "堆栈 $stack_name 删除成功"
else
log_error "堆栈 $stack_name 删除失败"
return 1
fi
}
# 显示节点信息
show_nodes() {
log_info "Swarm 节点信息:"
docker node ls
}
# 显示帮助信息
show_help() {
echo "Docker Swarm 管理脚本"
echo ""
echo "用法: $0 [命令] [参数]"
echo ""
echo "命令:"
echo " init - 初始化 Docker Swarm"
echo " deploy <stack> <compose-file> - 部署堆栈"
echo " list - 列出所有堆栈"
echo " services <stack> - 查看堆栈服务"
echo " remove <stack> - 删除堆栈"
echo " nodes - 显示节点信息"
echo " check - 检查 Swarm 状态"
echo " help - 显示此帮助信息"
echo ""
echo "示例:"
echo " $0 init # 初始化 Swarm"
echo " $0 deploy traefik stacks/traefik-swarm-stack.yml"
echo " $0 deploy demo stacks/demo-services-stack.yml"
echo " $0 list # 列出堆栈"
echo " $0 services traefik # 查看 traefik 堆栈服务"
}
# 主函数
main() {
case "${1:-help}" in
"init")
init_swarm
;;
"deploy")
if [[ $# -lt 3 ]]; then
log_error "部署命令需要堆栈名称和 compose 文件"
echo "用法: $0 deploy <stack-name> <compose-file>"
exit 1
fi
check_swarm_mode || exit 1
deploy_stack "$2" "$3"
;;
"list")
check_swarm_mode || exit 1
list_stacks
;;
"services")
if [[ $# -lt 2 ]]; then
log_error "需要指定堆栈名称"
echo "用法: $0 services <stack-name>"
exit 1
fi
check_swarm_mode || exit 1
show_stack_services "$2"
;;
"remove")
if [[ $# -lt 2 ]]; then
log_error "需要指定堆栈名称"
echo "用法: $0 remove <stack-name>"
exit 1
fi
check_swarm_mode || exit 1
remove_stack "$2"
;;
"nodes")
check_swarm_mode || exit 1
show_nodes
;;
"check")
check_swarm_mode
;;
"help"|*)
show_help
;;
esac
}
# 运行主函数
main "$@"

View File

@@ -0,0 +1,41 @@
version: '3.8'
services:
consul:
image: consul:latest
hostname: consul-ash3c
command: >
sh -c "
IP=$$(hostname -i | awk '{print $$1}');
consul agent -server -bootstrap-expect=2
-datacenter=dc1 -data-dir=/consul/data
-node=consul-ash3c -bind=$$IP -advertise=100.116.80.94 -client=0.0.0.0
-retry-join=100.117.106.136
-ui
-log-level=INFO
"
ports:
- "8500:8500"
- "8600:8600/udp"
volumes:
- consul_data:/consul/data
networks:
- consul-net
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.hostname == ash3c
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
volumes:
consul_data:
networks:
consul-net:
driver: overlay
attachable: true

View File

@@ -0,0 +1,76 @@
version: '3.8'
services:
consul-master:
image: consul:latest
hostname: consul-master
command: >
sh -c "
IP=$$(hostname -i | awk '{print $$1}');
consul agent -server -bootstrap-expect=2
-datacenter=dc1 -data-dir=/consul/data
-node=consul-master -bind=$$IP -advertise=$$IP -client=0.0.0.0
-ui
-log-level=INFO
"
ports:
- "8500:8500"
- "8600:8600/udp"
volumes:
- consul_master_data:/consul/data
networks:
consul-net:
aliases:
- consul-master
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.hostname == master
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
consul-ash3c:
image: consul:latest
hostname: consul-ash3c
command: >
sh -c "
IP=$$(hostname -i | awk '{print $$1}');
consul agent -server -bootstrap-expect=2
-datacenter=dc1 -data-dir=/consul/data
-node=consul-ash3c -bind=$$IP -advertise=$$IP -client=0.0.0.0
-retry-join=consul-cluster_consul-master
-ui
-log-level=INFO
"
ports:
- "8501:8500"
- "8601:8600/udp"
volumes:
- consul_ash3c_data:/consul/data
networks:
consul-net:
aliases:
- consul-ash3c
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.hostname == ash3c
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
volumes:
consul_master_data:
consul_ash3c_data:
networks:
consul-net:
driver: overlay
attachable: true

View File

@@ -0,0 +1,40 @@
version: '3.8'
services:
consul:
image: consul:latest
hostname: consul-master
command: >
sh -c "
IP=$$(hostname -i | awk '{print $$1}');
consul agent -server -bootstrap-expect=2
-datacenter=dc1 -data-dir=/consul/data
-node=consul-master -bind=$$IP -advertise=100.117.106.136 -client=0.0.0.0
-ui
-log-level=INFO
"
ports:
- "8500:8500"
- "8600:8600/udp"
volumes:
- consul_data:/consul/data
networks:
- consul-net
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.hostname == master
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
volumes:
consul_data:
networks:
consul-net:
driver: overlay
attachable: true

View File

@@ -0,0 +1,39 @@
version: '3.8'
services:
consul:
image: consul:latest
hostname: consul
command: >
consul agent -server -bootstrap-expect=1
-datacenter=dc1 -data-dir=/consul/data
-node=consul -client=0.0.0.0
-ui
-log-level=INFO
ports:
- "8500:8500"
- "8600:8600/udp"
volumes:
- consul_data:/consul/data
networks:
- consul-net
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.consul.rule=Host(`consul.local`)"
- "traefik.http.services.consul.loadbalancer.server.port=8500"
- "traefik.docker.network=consul-net"
volumes:
consul_data:
networks:
consul-net:
driver: overlay
attachable: true

View File

@@ -0,0 +1,166 @@
version: '3.8'
services:
# Web 应用示例
webapp:
image: nginx:alpine
networks:
- traefik-public
configs:
- source: webapp-html
target: /usr/share/nginx/html/index.html
deploy:
replicas: 2
labels:
- traefik.enable=true
- traefik.http.routers.webapp.rule=Host(`app.local`)
- traefik.http.routers.webapp.entrypoints=web
- traefik.http.services.webapp.loadbalancer.server.port=80
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
# API 服务示例
api:
image: httpd:alpine
networks:
- traefik-public
configs:
- source: api-html
target: /usr/local/apache2/htdocs/index.html
deploy:
replicas: 2
labels:
- traefik.enable=true
- traefik.http.routers.api.rule=Host(`api.local`)
- traefik.http.routers.api.entrypoints=web
- traefik.http.services.api.loadbalancer.server.port=80
# 添加路径前缀
- traefik.http.routers.api-path.rule=Host(`app.local`) && PathPrefix(`/api`)
- traefik.http.routers.api-path.entrypoints=web
- traefik.http.routers.api-path.service=api
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
# 监控服务示例
monitor:
image: nginx:alpine
networks:
- traefik-public
configs:
- source: monitor-html
target: /usr/share/nginx/html/index.html
deploy:
replicas: 1
labels:
- traefik.enable=true
- traefik.http.routers.monitor.rule=Host(`monitor.local`)
- traefik.http.routers.monitor.entrypoints=web
- traefik.http.services.monitor.loadbalancer.server.port=80
# 添加基本认证 (可选)
- traefik.http.routers.monitor.middlewares=auth
- traefik.http.middlewares.auth.basicauth.users=admin:$$2y$$10$$DLKjKQKQKQKQKQKQKQKQKe
restart_policy:
condition: on-failure
networks:
traefik-public:
external: true
configs:
webapp-html:
content: |
<!DOCTYPE html>
<html>
<head>
<title>Web App - Traefik Swarm Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
.container { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { color: #2c3e50; }
.info { background: #e8f4fd; padding: 15px; border-radius: 4px; margin: 20px 0; }
</style>
</head>
<body>
<div class="container">
<h1>🚀 Web Application</h1>
<div class="info">
<p><strong>服务:</strong> webapp</p>
<p><strong>访问地址:</strong> http://app.local</p>
<p><strong>负载均衡:</strong> Traefik + Docker Swarm</p>
<p><strong>时间:</strong> <span id="time"></span></p>
</div>
<p>这是通过 Traefik 路由的 Web 应用示例。</p>
</div>
<script>
document.getElementById('time').textContent = new Date().toLocaleString();
</script>
</body>
</html>
api-html:
content: |
<!DOCTYPE html>
<html>
<head>
<title>API Service - Traefik Swarm Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
.container { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { color: #27ae60; }
.info { background: #e8f8f5; padding: 15px; border-radius: 4px; margin: 20px 0; }
</style>
</head>
<body>
<div class="container">
<h1>🔌 API Service</h1>
<div class="info">
<p><strong>服务:</strong> api</p>
<p><strong>访问地址:</strong> http://api.local</p>
<p><strong>路径路由:</strong> http://app.local/api</p>
<p><strong>负载均衡:</strong> Traefik + Docker Swarm</p>
<p><strong>时间:</strong> <span id="time"></span></p>
</div>
<p>这是通过 Traefik 路由的 API 服务示例。</p>
</div>
<script>
document.getElementById('time').textContent = new Date().toLocaleString();
</script>
</body>
</html>
monitor-html:
content: |
<!DOCTYPE html>
<html>
<head>
<title>Monitor Service - Traefik Swarm Demo</title>
<style>
body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; }
.container { background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
h1 { color: #e74c3c; }
.info { background: #fdf2e9; padding: 15px; border-radius: 4px; margin: 20px 0; }
</style>
</head>
<body>
<div class="container">
<h1>📊 Monitor Service</h1>
<div class="info">
<p><strong>服务:</strong> monitor</p>
<p><strong>访问地址:</strong> http://monitor.local</p>
<p><strong>认证:</strong> 基本认证保护</p>
<p><strong>负载均衡:</strong> Traefik + Docker Swarm</p>
<p><strong>时间:</strong> <span id="time"></span></p>
</div>
<p>这是通过 Traefik 路由的监控服务示例。</p>
</div>
<script>
document.getElementById('time').textContent = new Date().toLocaleString();
</script>
</body>
</html>

View File

@@ -0,0 +1,70 @@
version: '3.8'
services:
traefik:
image: traefik:v3.0
command:
# API 和 Dashboard
- --api.dashboard=true
- --api.insecure=true
# 入口点
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Docker Swarm Provider
- --providers.swarm=true
- --providers.swarm.endpoint=unix:///var/run/docker.sock
- --providers.swarm.exposedByDefault=false
- --providers.swarm.network=traefik-public
# 日志
- --log.level=INFO
- --accesslog=true
# 指标
- --metrics.prometheus=true
- --metrics.prometheus.addEntryPointsLabels=true
- --metrics.prometheus.addServicesLabels=true
# 证书解析器 (可选)
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
- --certificatesresolvers.letsencrypt.acme.email=admin@example.com
- --certificatesresolvers.letsencrypt.acme.storage=/certificates/acme.json
ports:
- "80:80"
- "443:443"
- "8080:8080" # Dashboard
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-certificates:/certificates
networks:
- traefik-public
deploy:
mode: global
placement:
constraints:
- node.role == manager
labels:
# Traefik Dashboard 路由
- traefik.enable=true
- traefik.http.routers.traefik-dashboard.rule=Host(`traefik.local`)
- traefik.http.routers.traefik-dashboard.service=api@internal
- traefik.http.services.traefik-dashboard.loadbalancer.server.port=8080
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
networks:
traefik-public:
external: true
volumes:
traefik-certificates: