Update
This commit is contained in:
159
README.md
159
README.md
@@ -1 +1,158 @@
|
||||
# REAMDME
|
||||
# 用户服务部署使用手册
|
||||
|
||||
## 一、概述
|
||||
|
||||
本文档介绍用户服务系统的创建、构建和部署流程,涉及`create_api.py`、`build.py`和`deploy/deploy.sh`三个核心脚本的使用方法,帮助使用者快速搭建和部署用户服务相关的API模块。
|
||||
|
||||
## 二、环境准备
|
||||
|
||||
1. 确保系统安装以下工具:
|
||||
- Python 3.x
|
||||
- Docker
|
||||
- Docker Compose
|
||||
|
||||
2. 克隆代码库后,进入项目根目录:
|
||||
```bash
|
||||
cd user_service
|
||||
```
|
||||
|
||||
## 三、核心脚本功能说明
|
||||
|
||||
### 1. 创建新API服务(create_api.py)
|
||||
|
||||
用于基于模板快速创建新的API服务模块,自动生成目录结构和配置文件。
|
||||
|
||||
#### 使用方法:
|
||||
1. 编辑`create_api.py`文件,设置`a`(功能名)和`b`(次功能名)参数:
|
||||
```python
|
||||
a = "aaaa" # 主功能名
|
||||
b = "bbbb" # 次功能名(可选,如果不需要,留空)
|
||||
```
|
||||
|
||||
2. 执行脚本:
|
||||
```bash
|
||||
sudo python3 create_api.py
|
||||
```
|
||||
|
||||
#### 功能说明:
|
||||
- 自动在`deploy/api`目录下创建新服务目录(格式:`api_{a}_{b}` 或 `api_{a}`)
|
||||
- 复制`api_template`模板文件并替换其中的占位符
|
||||
- 生成对应的`docker-compose.{a}.{b}.yaml`或`docker-compose.{a}.yaml`文件
|
||||
- 自动修改`release.sh`、`init.py`和`README.md`中的服务标识
|
||||
|
||||
### 2. 构建API服务镜像(build.py)
|
||||
|
||||
用于批量构建所有API服务的Docker镜像,并将构建结果整理到指定目录。
|
||||
|
||||
#### 使用方法:
|
||||
```bash
|
||||
python3 build.py
|
||||
```
|
||||
|
||||
#### 功能说明:
|
||||
1. 遍历所有以`api_`开头的目录(排除`api_template`)
|
||||
2. 执行每个目录下的`release.sh`脚本:
|
||||
- 为脚本添加执行权限
|
||||
- 运行脚本删除旧镜像并构建新镜像
|
||||
3. 收集构建生成的`.tar`镜像文件:
|
||||
- 复制到`deploy/images`目录
|
||||
- 自动处理文件权限问题
|
||||
- 清理原始目录中的`.tar`文件
|
||||
|
||||
#### 注意事项:
|
||||
- 若遇到权限问题,可尝试使用`sudo`运行:
|
||||
```bash
|
||||
sudo python3 build.py
|
||||
```
|
||||
- 脚本会自动跳过不存在`release.sh`的目录
|
||||
|
||||
### 3. 部署服务(deploy/deploy.sh)
|
||||
|
||||
用于停止现有服务、构建API并启动新的Docker Compose编排。
|
||||
|
||||
#### 使用方法:
|
||||
```bash
|
||||
cd deploy
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
#### 功能说明:
|
||||
1. **步骤1:停止现有服务**
|
||||
- 停止`docker-compose.yaml`定义的`user_service`项目
|
||||
|
||||
2. **步骤2:构建所有API服务**
|
||||
- 遍历`./api`目录下的所有服务
|
||||
- 执行每个服务的`release.sh`脚本
|
||||
- 若构建失败则终止部署流程
|
||||
|
||||
3. **步骤3:启动新服务**
|
||||
- 使用`docker-compose.yaml`启动服务
|
||||
- 项目名称为`user_service`
|
||||
|
||||
#### 配置说明:
|
||||
脚本开头可修改以下配置参数:
|
||||
```bash
|
||||
COMPOSE_PROJECT_NAME="user_service" # Docker Compose项目名称
|
||||
DOCKER_COMPOSE_FILE="./docker-compose.yaml" # 配置文件路径
|
||||
API_DIR="./api" # API服务所在目录
|
||||
```
|
||||
|
||||
## 四、完整工作流程
|
||||
|
||||
1. **创建新API服务**:
|
||||
```bash
|
||||
# 编辑create_api.py设置功能名
|
||||
python3 create_api.py
|
||||
```
|
||||
|
||||
2. **开发API功能**:
|
||||
- 在生成的`deploy/api/api_{a}_{b}`目录中编写代码(如`main.go`)
|
||||
- 根据需要修改`init.py`中的初始化逻辑
|
||||
|
||||
3. **构建服务镜像**:
|
||||
```bash
|
||||
python3 build.py
|
||||
```
|
||||
|
||||
4. **部署服务**:
|
||||
```bash
|
||||
cd deploy
|
||||
./deploy.sh
|
||||
```
|
||||
|
||||
## 五、常见问题处理
|
||||
|
||||
1. **权限错误**:
|
||||
- 执行脚本时若出现`Permission denied`,尝试添加执行权限:
|
||||
```bash
|
||||
chmod +x deploy/deploy.sh
|
||||
```
|
||||
- 或使用`sudo`运行相关命令
|
||||
|
||||
2. **镜像构建失败**:
|
||||
- 检查`release.sh`中的镜像名称和构建命令是否正确
|
||||
- 确保Docker服务正常运行:`systemctl status docker`
|
||||
|
||||
3. **服务启动失败**:
|
||||
- 检查`docker-compose.yaml`配置是否正确
|
||||
- 查看容器日志排查问题:`docker logs <容器名>`
|
||||
|
||||
4. **端口冲突**:
|
||||
- 若启动时提示端口被占用,修改`init.py`中的端口映射配置
|
||||
|
||||
## 六、服务目录结构说明
|
||||
|
||||
成功创建并部署后,项目主要目录结构如下:
|
||||
```
|
||||
user_service_副本/
|
||||
├── deploy/
|
||||
│ ├── api/ # 所有API服务目录
|
||||
│ │ ├── api_login/ # 登录服务
|
||||
│ │ ├── api_register/ # 注册服务
|
||||
│ │ └── ... # 其他API服务
|
||||
│ ├── images/ # 构建好的镜像文件
|
||||
│ ├── docker-compose.yaml # 服务编排配置
|
||||
│ └── deploy.sh # 部署脚本
|
||||
├── create_api.py # 创建新API的脚本
|
||||
└── build.py # 构建镜像的脚本
|
||||
```
|
253
build.py
253
build.py
@@ -1,253 +0,0 @@
|
||||
import os
|
||||
import shutil
|
||||
import yaml
|
||||
import subprocess
|
||||
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("开始清除虚悬镜像...")
|
||||
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脚本...")
|
||||
|
||||
# 定义目标镜像目录
|
||||
images_dir = os.path.join(root_dir, 'deploy', 'images')
|
||||
# 确保目标目录存在并设置权限
|
||||
os.makedirs(images_dir, exist_ok=True)
|
||||
# 尝试设置目录可写权限(解决目标目录权限问题)
|
||||
try:
|
||||
os.chmod(images_dir, 0o755)
|
||||
except PermissionError:
|
||||
print(f"警告:无法修改目录权限 {images_dir},可能导致文件复制失败")
|
||||
|
||||
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
|
||||
|
||||
# 复制目录下的.tar文件到根目录的deploy/images下(替换方式)
|
||||
for file in os.listdir(dir_path):
|
||||
if file.endswith('.tar'):
|
||||
src_path = os.path.join(dir_path, file)
|
||||
dest_path = os.path.join(images_dir, file)
|
||||
|
||||
# 检查源文件是否存在且可读
|
||||
if not os.path.exists(src_path):
|
||||
print(f"警告:源文件不存在 {src_path},跳过")
|
||||
continue
|
||||
|
||||
if not os.access(src_path, os.R_OK):
|
||||
print(f"警告:无权限读取 {src_path},尝试修复权限...")
|
||||
try:
|
||||
# 尝试赋予源文件读权限
|
||||
os.chmod(src_path, 0o644)
|
||||
# 再次检查权限
|
||||
if not os.access(src_path, os.R_OK):
|
||||
print(f"错误:仍无法读取 {src_path},请手动检查权限")
|
||||
continue
|
||||
except PermissionError:
|
||||
print(f"错误:无法修复 {src_path} 权限,跳过该文件")
|
||||
continue
|
||||
|
||||
# 处理目标文件
|
||||
if os.path.exists(dest_path):
|
||||
try:
|
||||
os.remove(dest_path)
|
||||
print(f"已删除旧文件: {dest_path}")
|
||||
except PermissionError:
|
||||
print(f"警告:无权限删除旧文件 {dest_path},尝试用sudo运行脚本")
|
||||
continue
|
||||
|
||||
# 复制文件(带权限处理)
|
||||
try:
|
||||
shutil.copy2(src_path, dest_path)
|
||||
# 复制后确保目标文件可读写
|
||||
os.chmod(dest_path, 0o644)
|
||||
print(f"已将 {src_path} 替换复制到 {dest_path}")
|
||||
|
||||
# 复制成功后删除原文件
|
||||
try:
|
||||
os.remove(src_path)
|
||||
print(f"已删除原文件: {src_path}")
|
||||
except PermissionError:
|
||||
print(f"警告:无权限删除原文件 {src_path},请手动清理")
|
||||
except Exception as e:
|
||||
print(f"删除原文件 {src_path} 时发生错误: {str(e)}")
|
||||
|
||||
except PermissionError:
|
||||
print(f"错误:复制 {src_path} 失败(权限不足)")
|
||||
print("建议:使用 sudo 权限重新运行脚本(sudo python3 build.py)")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"复制文件时发生错误: {str(e)},跳过该文件")
|
||||
continue
|
||||
else:
|
||||
print(f"{dir_path} 中未找到release.sh,跳过")
|
||||
|
||||
print("所有release.sh脚本执行完成")
|
||||
return True
|
||||
|
||||
def merge_yaml_files(root_dir: str) -> Dict[str, Any]:
|
||||
"""合并所有 docker-compose 相关 YAML 文件内容(排除api_template)"""
|
||||
merged = {
|
||||
'services': {},
|
||||
'networks': {},
|
||||
'volumes': {}
|
||||
}
|
||||
|
||||
yaml_files = []
|
||||
# 根目录下的 docker-compose.*.yaml
|
||||
for file in os.listdir(root_dir):
|
||||
if file.startswith('docker-compose.') and file.endswith('.yaml') and file != 'docker-compose.yaml':
|
||||
yaml_files.append(os.path.join(root_dir, file))
|
||||
|
||||
# api_ 目录下的 docker-compose.*.yaml(排除api_template)
|
||||
for dir_name in os.listdir(root_dir):
|
||||
if dir_name == "api_template":
|
||||
print(f"跳过模板目录的yaml文件: {dir_name}")
|
||||
continue
|
||||
|
||||
dir_path = os.path.join(root_dir, dir_name)
|
||||
if os.path.isdir(dir_path) and dir_name.startswith('api_'):
|
||||
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 文件内容
|
||||
for file_path in yaml_files:
|
||||
print(f"合并文件: {file_path}")
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
data = yaml.safe_load(f)
|
||||
if not data:
|
||||
continue
|
||||
|
||||
if 'services' in data:
|
||||
merged['services'].update(data['services'])
|
||||
if 'networks' in data:
|
||||
merged['networks'].update(data['networks'])
|
||||
if 'volumes' in data:
|
||||
merged['volumes'].update(data['volumes'])
|
||||
|
||||
except yaml.YAMLError as e:
|
||||
print(f"解析 {file_path} 失败: {e}")
|
||||
continue
|
||||
except PermissionError:
|
||||
print(f"错误:无权限读取 {file_path},跳过该文件")
|
||||
continue
|
||||
|
||||
return merged
|
||||
|
||||
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)
|
||||
|
||||
# 输出到根目录的docker-compose.yaml
|
||||
output_path = os.path.join(root_dir, 'docker-compose.yaml')
|
||||
try:
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
yaml.dump(
|
||||
merged_data,
|
||||
f,
|
||||
sort_keys=False,
|
||||
allow_unicode=True,
|
||||
default_flow_style=False
|
||||
)
|
||||
print(f"已生成合并后的 docker-compose.yaml: {output_path}")
|
||||
|
||||
# 将新的docker-compose.yaml以替换方式复制到根目录的deploy/下
|
||||
deploy_dir = os.path.join(root_dir, 'deploy')
|
||||
os.makedirs(deploy_dir, exist_ok=True)
|
||||
# 确保deploy目录可写
|
||||
try:
|
||||
os.chmod(deploy_dir, 0o755)
|
||||
except PermissionError:
|
||||
print(f"警告:无法修改目录权限 {deploy_dir}")
|
||||
|
||||
dest_compose_path = os.path.join(deploy_dir, 'docker-compose.yaml')
|
||||
# 替换方式复制
|
||||
if os.path.exists(dest_compose_path):
|
||||
try:
|
||||
os.remove(dest_compose_path)
|
||||
except PermissionError:
|
||||
print(f"警告:无权限删除旧文件 {dest_compose_path}")
|
||||
return False
|
||||
|
||||
shutil.copy2(output_path, dest_compose_path)
|
||||
# 设置目标文件权限
|
||||
os.chmod(dest_compose_path, 0o644)
|
||||
print(f"已将 {output_path} 替换复制到 {dest_compose_path}")
|
||||
|
||||
except PermissionError as e:
|
||||
print(f"错误:无权限操作文件 {output_path} 或 {dest_compose_path}")
|
||||
print(f"详细错误: {str(e)}")
|
||||
print("建议:使用 sudo 权限重新运行脚本(sudo python3 build.py)")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"生成或复制docker-compose.yaml时出错: {str(e)}")
|
||||
return False
|
||||
|
||||
print("\n===== 清理虚悬镜像 =====")
|
||||
if not cleanup_dangling_images():
|
||||
print("清除虚悬镜像失败(非致命错误,继续部署流程)")
|
||||
else:
|
||||
print("虚悬镜像清除完成")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 需要安装 pyyaml 库: pip install pyyaml
|
||||
main()
|
Reference in New Issue
Block a user