This commit is contained in:
vipg
2025-10-10 16:43:40 +08:00
parent 8bdab52c30
commit 7c93222bd3
8 changed files with 71 additions and 147 deletions

View File

@@ -1,93 +1,34 @@
import os import os
import yaml import yaml
import subprocess import shutil
from typing import Dict, Any from typing import Dict, Any
def run_shell_command(command: str, cwd: str = None) -> bool:
"""执行shell命令并返回执行结果"""
try:
print(f"执行命令: {command} (工作目录: {cwd or os.getcwd()})")
result = subprocess.run(
command,
cwd=cwd,
shell=True,
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
print(f"命令输出: {result.stdout}")
return True
except subprocess.CalledProcessError as e:
print(f"命令执行失败: {e.stderr}")
return False
def cleanup_dangling_images() -> bool:
"""清除docker虚悬镜像dangling images"""
print("开始清除虚悬镜像...")
# 清除所有<none>:<none>的虚悬镜像xargs -r确保无镜像时不执行删除命令
return run_shell_command('sudo docker images -f "dangling=true" -q | xargs -r sudo docker rmi')
def execute_release_scripts(root_dir: str) -> bool:
"""遍历所有api_目录并执行release.sh脚本排除api_template"""
print("开始执行所有api_目录下的release.sh脚本...")
for dir_name in os.listdir(root_dir):
# 排除api_template文件夹
if dir_name == "api_template":
print(f"跳过模板目录: {dir_name}")
continue
dir_path = os.path.join(root_dir, dir_name)
if os.path.isdir(dir_path) and dir_name.startswith('api_'):
release_script = os.path.join(dir_path, 'release.sh')
if os.path.exists(release_script):
print(f"处理目录: {dir_path}")
# 添加执行权限
if not run_shell_command(f"chmod +x {release_script}", dir_path):
print(f"{release_script} 添加权限失败,跳过执行")
continue
# 执行release.sh
if not run_shell_command(f"./release.sh", dir_path):
print(f"{release_script} 执行失败,跳过后续步骤")
return False
else:
print(f"{dir_path} 中未找到release.sh跳过")
print("所有release.sh脚本执行完成")
return True
def merge_yaml_files(root_dir: str) -> Dict[str, Any]: def merge_yaml_files(root_dir: str) -> Dict[str, Any]:
"""合并所有 docker-compose 相关 YAML 文件内容排除api_template""" """合并所有 docker-compose 相关 YAML 文件内容"""
# 初始化合并后的结构
merged = { merged = {
'services': {}, 'services': {},
'networks': {}, 'networks': {},
'volumes': {} 'volumes': {}
} }
# 查找所有 docker-compose 相关 YAML 文件
yaml_files = [] yaml_files = []
# 1. 根目录下的 docker-compose.*.yaml # 1. 收集根目录下的 docker-compose.*.yaml(排除目标输出文件)
for file in os.listdir(root_dir): for file in os.listdir(root_dir):
if file.startswith('docker-compose.') and file.endswith('.yaml'): if (file.startswith('docker-compose.') and
if file == 'docker-compose.yaml': # 跳过目标文件本身 file.endswith('.yaml') and
continue file != 'docker-compose.yaml'):
yaml_files.append(os.path.join(root_dir, file)) yaml_files.append(os.path.join(root_dir, file))
# 2. api_ 目录下的 docker-compose.*.yaml排除api_template # 2. 收集 deploy/api 目录下的所有 docker-compose.*.yaml
for dir_name in os.listdir(root_dir): api_root = os.path.join(root_dir, 'deploy', 'api')
# 排除api_template文件夹 if os.path.exists(api_root):
if dir_name == "api_template": for dir_name in os.listdir(api_root):
print(f"跳过模板目录的yaml文件: {dir_name}") dir_path = os.path.join(api_root, dir_name)
continue if os.path.isdir(dir_path):
for file in os.listdir(dir_path):
dir_path = os.path.join(root_dir, dir_name) if file.startswith('docker-compose.') and file.endswith('.yaml'):
if os.path.isdir(dir_path) and dir_name.startswith('api_'): yaml_files.append(os.path.join(dir_path, file))
for file in os.listdir(dir_path):
if file.startswith('docker-compose.') and file.endswith('.yaml'):
yaml_files.append(os.path.join(dir_path, file))
# 合并所有 YAML 文件内容 # 合并所有 YAML 文件内容
for file_path in yaml_files: for file_path in yaml_files:
@@ -98,15 +39,15 @@ def merge_yaml_files(root_dir: str) -> Dict[str, Any]:
if not data: if not data:
continue continue
# 合并 services # 合并 services 配置
if 'services' in data: if 'services' in data:
merged['services'].update(data['services']) merged['services'].update(data['services'])
# 合并 networks # 合并 networks 配置
if 'networks' in data: if 'networks' in data:
merged['networks'].update(data['networks']) merged['networks'].update(data['networks'])
# 合并 volumes # 合并 volumes 配置
if 'volumes' in data: if 'volumes' in data:
merged['volumes'].update(data['volumes']) merged['volumes'].update(data['volumes'])
@@ -116,71 +57,30 @@ def merge_yaml_files(root_dir: str) -> Dict[str, Any]:
return merged return merged
def stop_docker_compose(root_dir: str) -> bool: def process_yaml(root_dir: str):
"""停止docker-compose""" """处理 YAML 文件的主函数:合并并输出到目标路径"""
print("开始停止docker-compose...") # 合并 YAML 文件
compose_file = os.path.join(root_dir, 'docker-compose.yaml')
if not os.path.exists(compose_file):
print(f"未找到docker-compose文件: {compose_file}")
return False
return run_shell_command(f"sudo docker-compose -f {compose_file} down", root_dir)
def start_docker_compose(root_dir: str) -> bool:
"""启动docker-compose"""
print("开始启动docker-compose...")
compose_file = os.path.join(root_dir, 'docker-compose.yaml')
if not os.path.exists(compose_file):
print(f"未找到docker-compose文件: {compose_file}")
return False
return run_shell_command(f"sudo docker-compose -f {compose_file} up -d", root_dir)
def main():
# 先执行清除虚悬镜像操作
print("部署流程开始,先执行清除虚悬镜像操作...")
if not cleanup_dangling_images():
print("清除虚悬镜像失败(非致命错误,继续部署流程)")
else:
print("虚悬镜像清除完成")
# 获取项目根目录deploy.py 所在目录)
root_dir = os.path.dirname(os.path.abspath(__file__))
print(f"项目根目录: {root_dir}")
# 1. 执行所有api_目录下的release.sh脚本
if not execute_release_scripts(root_dir):
print("执行release脚本失败终止部署流程")
return
# 2. 合并所有YAML文件
merged_data = merge_yaml_files(root_dir) merged_data = merge_yaml_files(root_dir)
# 输出到根目录的docker-compose.yaml # 输出到临时文件
output_path = os.path.join(root_dir, 'docker-compose.yaml') temp_output = os.path.join(root_dir, 'docker-compose.temp.yaml')
with open(output_path, 'w', encoding='utf-8') as f: with open(temp_output, 'w', encoding='utf-8') as f:
yaml.dump( yaml.dump(
merged_data, merged_data,
f, f,
sort_keys=False, # 保持键的顺序 sort_keys=False, # 保持键的顺序
allow_unicode=True, # 支持中文 allow_unicode=True, # 支持中文
default_flow_style=False # 使用块样式 default_flow_style=False # 使用块样式而非流式样式
) )
print(f"已生成合并后的 docker-compose.yaml: {output_path}") print(f"已生成合并后的临时文件: {temp_output}")
# 3. 启动docker-compose # 复制到部署目录并替换目标文件
stop_docker_compose(root_dir) deploy_dir = os.path.join(root_dir, 'deploy')
if not start_docker_compose(root_dir): target_output = os.path.join(deploy_dir, 'docker-compose.yaml')
print("启动docker-compose失败") shutil.move(temp_output, target_output)
return print(f"已将合并后的文件复制到: {target_output}")
print("部署流程完成")
print("\n===== 清理虚悬镜像 =====")
if not cleanup_dangling_images():
print("清除虚悬镜像失败(非致命错误,继续部署流程)")
else:
print("虚悬镜像清除完成")
if __name__ == "__main__": if __name__ == "__main__":
# 需要安装 pyyaml 库: pip install pyyaml # 示例:从当前脚本所在目录作为根目录处理
main() root_dir = os.path.dirname(os.path.abspath(__file__))
process_yaml(root_dir)

View File

@@ -13,5 +13,4 @@ services:
DB_USER: ${DB_USER} # 引用.env变量 DB_USER: ${DB_USER} # 引用.env变量
DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量 DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} # 引用.env变量 TZ: ${TZ} # 引用.env变量
command: ["/bin/sh", "-c", "until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...'; sleep 2; done; 启动你的应用命令"]

View File

@@ -8,5 +8,4 @@ services:
networks: networks:
- user-network - user-network
environment: environment:
GATEWAY_PORT: ${GATEWAY_PORT} # 引用.env变量 GATEWAY_PORT: ${GATEWAY_PORT} # 引用.env变量
command: ["/bin/sh", "-c", "until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...'; sleep 2; done; 启动你的应用命令"]

View File

@@ -13,5 +13,4 @@ services:
DB_USER: ${DB_USER} # 引用.env变量 DB_USER: ${DB_USER} # 引用.env变量
DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量 DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} # 引用.env变量 TZ: ${TZ} # 引用.env变量
command: ["/bin/sh", "-c", "until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...'; sleep 2; done; 启动你的应用命令"]

View File

@@ -13,5 +13,4 @@ services:
DB_USER: ${DB_USER} # 引用.env变量 DB_USER: ${DB_USER} # 引用.env变量
DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量 DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} # 引用.env变量 TZ: ${TZ} # 引用.env变量
command: ["/bin/sh", "-c", "until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...'; sleep 2; done; 启动你的应用命令"]

View File

@@ -13,5 +13,4 @@ services:
DB_USER: ${DB_USER} # 引用.env变量 DB_USER: ${DB_USER} # 引用.env变量
DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量 DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} # 引用.env变量 TZ: ${TZ} # 引用.env变量
command: ["/bin/sh", "-c", "until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...'; sleep 2; done; 启动你的应用命令"]

View File

@@ -13,5 +13,4 @@ services:
DB_USER: ${DB_USER} # 引用.env变量 DB_USER: ${DB_USER} # 引用.env变量
DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量 DB_PASSWORD: ${DB_PASSWORD} # 引用.env变量
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} # 引用.env变量 TZ: ${TZ} # 引用.env变量
command: ["/bin/sh", "-c", "until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...'; sleep 2; done; 启动你的应用命令"]

View File

@@ -46,6 +46,11 @@ services:
DB_PASSWORD: ${DB_PASSWORD} DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} TZ: ${TZ}
command:
- /bin/sh
- -c
- until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...';
sleep 2; done; 启动你的应用命令
user_update_account: user_update_account:
image: user-update-account-api:1.0.0 image: user-update-account-api:1.0.0
container_name: api_user_update_account container_name: api_user_update_account
@@ -61,6 +66,11 @@ services:
DB_PASSWORD: ${DB_PASSWORD} DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} TZ: ${TZ}
command:
- /bin/sh
- -c
- until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...';
sleep 2; done; 启动你的应用命令
user_gateway: user_gateway:
image: user-gateway-api:1.0.0 image: user-gateway-api:1.0.0
container_name: api_user_gateway container_name: api_user_gateway
@@ -71,6 +81,11 @@ services:
- user-network - user-network
environment: environment:
GATEWAY_PORT: ${GATEWAY_PORT} GATEWAY_PORT: ${GATEWAY_PORT}
command:
- /bin/sh
- -c
- until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...';
sleep 2; done; 启动你的应用命令
user_update_password: user_update_password:
image: user-update-password-api:1.0.0 image: user-update-password-api:1.0.0
container_name: api_user_update_password container_name: api_user_update_password
@@ -86,6 +101,11 @@ services:
DB_PASSWORD: ${DB_PASSWORD} DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} TZ: ${TZ}
command:
- /bin/sh
- -c
- until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...';
sleep 2; done; 启动你的应用命令
user_register: user_register:
image: user-register-api:1.0.0 image: user-register-api:1.0.0
container_name: api_user_register container_name: api_user_register
@@ -101,6 +121,11 @@ services:
DB_PASSWORD: ${DB_PASSWORD} DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} TZ: ${TZ}
command:
- /bin/sh
- -c
- until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...';
sleep 2; done; 启动你的应用命令
user_delete: user_delete:
image: user-delete-api:1.0.0 image: user-delete-api:1.0.0
container_name: api_user_delete container_name: api_user_delete
@@ -116,6 +141,11 @@ services:
DB_PASSWORD: ${DB_PASSWORD} DB_PASSWORD: ${DB_PASSWORD}
DB_NAME: ${DB_NAME} DB_NAME: ${DB_NAME}
TZ: ${TZ} TZ: ${TZ}
command:
- /bin/sh
- -c
- until pg_isready -h $DB_HOST -p $DB_PORT -U $DB_USER; do echo '等待数据库就绪...';
sleep 2; done; 启动你的应用命令
networks: networks:
user-network: user-network:
driver: bridge driver: bridge