python(64) : python服务部署镜像封装和调试及导出

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

1.前言

本文依赖docker环境, Windows安装docekr可参考如下文章:

docker(25) : Windows10在wsl2上面安装docker_windows docker podman-优快云博客

2.说明

基于python:3.10-slim镜像封装创建容器并挂载目录, install.sh脚本安装环境, 挂载目录便于调试python代码, 调试完成后导出容器为镜像传输到服务器部署即可.

创建文件夹base_env, 将 Dockerfile install.sh requirements.txt main.sh startup.sh 放到该文件内, base_env文件放到python项目根目录(容器挂载为base_env的上级目录也就是python项目根目录) 

main.sh exec 进入容器后就是python项目根目录, install.sh根据各自项目所需依赖安装即可

功能概述

此脚本提供了一个统一的管理界面,用于管理 xx项目的 Docker 开发环境和发布镜像。

配置说明

  • 项目名称xx
  • 基础镜像xx_env:0.1
  • 发布镜像xx_release:0.1
  • 容器名称xx_env
  • 工作目录: 脚本所在目录的父目录

使用方法

./main.sh <command> [options]

可用命令

一键执行

./main.sh deploy

一键完成构建+创建+安装+保存+导出的完整流程。

构建基础镜像

./main.sh build

构建基础 Docker 镜像。如果存在旧镜像或容器,会先删除它们。

创建开发环境容器

./main.sh create

创建开发环境容器,并挂载代码目录和工作目录。

进入开发容器

./main.sh exec

进入开发容器的交互式 bash 终端。如果容器未运行,会自动启动。

安装环境依赖

./main.sh install

在容器中安装项目依赖。需要在主机环境下运行,不能在容器内执行。

保存容器为发布镜像

./main.sh save

将当前容器状态提交为发布镜像。如果容器未运行,会自动启动。

导出发布镜像

./main.sh export

将发布镜像导出为 tar 文件(xx_release_0.1.tar)。

删除容器

./main.sh rm

删除开发环境容器。

删除基础镜像

./main.sh rmi

删除基础镜像。

删除发布镜像

./main.sh rmir

删除发布镜像。

显示当前状态

./main.sh status

显示基础镜像、发布镜像和容器的当前状态。

显示帮助信息

./main.sh help
# 或
./main.sh --help
# 或
./main.sh -h

典型工作流程

快速部署

适用于首次部署或完整重建环境:

./main.sh deploy

这将执行以下步骤:

  1. 构建基础镜像
  2. 创建开发容器
  3. 安装环境依赖(非交互式)
  4. 保存容器为发布镜像
  5. 导出镜像文件

分步操作

适用于需要分步控制或调试的场景:

# 1. 构建基础镜像
./main.sh build

# 2. 创建开发容器
./main.sh create

# 3. 安装依赖
./main.sh install

# 4. 进入容器进行开发
./main.sh exec

# 5. 保存为发布镜像(开发完成后)
./main.sh save

# 6. 导出镜像文件
./main.sh export

容器管理

启动已停止的容器

容器在创建后会自动运行。如果容器被停止,脚本在执行需要容器运行的命令时会自动启动它。

查看容器状态

./main.sh status

删除并重新创建

如果需要重置环境:

./main.sh rm    # 删除容器
./main.sh rmi   # 删除基础镜像(可选)
./main.sh build # 重新构建
./main.sh create # 重新创建

注意事项

  1. 权限要求: 确保有执行脚本的权限,如果没有可以使用 chmod +x main.sh
  2. Docker 环境: 确保 Docker 已安装并正在运行
  3. 容器内执行限制install 和 deploy 命令不能在容器内执行,需要在主机环境下运行
  4. 镜像导出: 导出的 tar 文件会保存在脚本所在目录
  5. 容器挂载: 容器会自动挂载项目根目录到 /app,便于代码开发和调试

错误处理

脚本使用 set -e,遇到错误会自动退出。所有操作都包含错误检查,失败时会显示明确的错误信息。

日志输出

脚本提供彩色输出以区分不同级别的信息:

  • 蓝色 [INFO]: 一般信息
  • 绿色 [SUCCESS]: 成功操作
  • 黄色 [WARNING]: 警告信息
  • 红色 [ERROR]: 错误信息

在不支持颜色的终端中,颜色代码会自动禁用。

文件结构

base_env/
├── main.sh          # 主管理脚本(本文件)
├── Dockerfile       # Docker 镜像构建文件
├── install.sh       # 容器内依赖安装脚本
├── startup.sh       # 容器启动脚本
└── README.md        # 本说明文件

3.shell脚本(main.sh)

#!/bin/bash
# ============================================================================
# Docker 环境管理脚本
# 功能:统一管理 Docker 镜像和容器的构建、创建、操作等
# ============================================================================

set -e

# ============================================================================
# 配置变量
# ============================================================================
# 获取脚本所在目录的绝对路径
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# 工作目录为脚本目录的父目录
WORK_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
# 切换到脚本所在目录(Dockerfile 在此目录)
cd "${SCRIPT_DIR}"

NAME="xx"
BASE_IMAGE_NAME="${NAME}_env:0.1"
CONTAINER_NAME="${NAME}_env"
RELEASE_IMAGE_NAME="${NAME}_release:0.1"

# ============================================================================
# 颜色输出
# ============================================================================
# 检测终端是否支持颜色
if [ -t 1 ] && command -v tput > /dev/null 2>&1; then
    # 终端支持颜色
    RED=$(tput setaf 1)
    GREEN=$(tput setaf 2)
    YELLOW=$(tput setaf 3)
    BLUE=$(tput setaf 4)
    BOLD=$(tput bold)
    NC=$(tput sgr0) # No Color
else
    # 终端不支持颜色,使用空字符串
    RED=''
    GREEN=''
    YELLOW=''
    BLUE=''
    BOLD=''
    NC=''
fi

# ============================================================================
# 工具函数
# ============================================================================

# 打印信息
info() {
    printf "${BLUE}[INFO]${NC} %s\n" "$1"
}

# 打印成功
success() {
    printf "${GREEN}[SUCCESS]${NC} %s\n" "$1"
}

# 打印警告
warning() {
    printf "${YELLOW}[WARNING]${NC} %s\n" "$1"
}

# 打印错误
error() {
    printf "${RED}[ERROR]${NC} %s\n" "$1"
}

# 检查镜像是否存在
check_image_exists() {
    docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${BASE_IMAGE_NAME}$"
}

# 检查发布镜像是否存在
check_release_image_exists() {
    docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${RELEASE_IMAGE_NAME}$"
}

# 检查容器是否存在
check_container_exists() {
    docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"
}

# 检查容器是否运行中
check_container_running() {
    docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"
}

# ============================================================================
# 功能函数
# ============================================================================

# 1. 构建基础镜像
build_image() {
    info "开始构建基础镜像 ${BASE_IMAGE_NAME}..."
    
    # 删除旧容器(如果存在)
    if check_container_exists; then
        warning "删除旧容器 ${CONTAINER_NAME}..."
        docker rm -f ${CONTAINER_NAME} || true
    fi
    
    # 删除旧镜像(如果存在)
    if check_image_exists; then
        warning "删除旧镜像 ${BASE_IMAGE_NAME}..."
        docker rmi ${BASE_IMAGE_NAME} || true
    fi
    
    # 构建新镜像
    info "正在构建基础镜像..."
    if docker build -t ${BASE_IMAGE_NAME} .; then
        success "基础镜像 ${BASE_IMAGE_NAME} 构建成功"
        docker images | grep ${CONTAINER_NAME}
    else
        error "基础镜像构建失败"
        exit 1
    fi
}

# 2. 创建开发环境容器
create_container() {
    info "正在创建开发容器 ${CONTAINER_NAME}..."
    
    # 检查基础镜像是否存在
    if ! check_image_exists; then
        error "基础镜像 ${BASE_IMAGE_NAME} 不存在,请先运行: $0 build"
        exit 1
    fi
    
    # 删除旧容器(如果存在)
    if check_container_exists; then
        warning "删除旧容器 ${CONTAINER_NAME}..."
        docker rm -f ${CONTAINER_NAME} || true
    fi
    
    # 创建新容器
    info "创建容器并挂载代码目录..."
    if docker run -d \
        --restart=unless-stopped \
        --name ${CONTAINER_NAME} \
        -v "${WORK_DIR}":/app \
        -v "${SCRIPT_DIR}/startup.sh":/app/startup.sh \
        -w /app \
        ${BASE_IMAGE_NAME}; then
        success "容器 ${CONTAINER_NAME} 创建成功"
        echo ""
        info "可以使用以下命令:"
        echo "  安装依赖: $0 install"
    else
        error "容器创建失败"
        exit 1
    fi
}

# 3. 进入开发容器
exec_container() {
    if ! check_container_exists; then
        error "容器 ${CONTAINER_NAME} 不存在,请先运行: $0 create"
        exit 1
    fi
    
    if ! check_container_running; then
        warning "容器未运行,正在启动..."
        docker start ${CONTAINER_NAME}
        sleep 2
    fi
    
    info "进入容器 ${CONTAINER_NAME}..."
    docker exec -it ${CONTAINER_NAME} /bin/bash
}

# 4. 安装环境依赖
install_dependencies() {
    # 判断是否在容器中,如果在容器中提示退出
    if [ -f "/.dockerenv" ]; then
        error "当前正在容器中,请在主机环境下运行该命令。"
        exit 1
    fi
    if ! check_container_exists; then
        error "容器 ${CONTAINER_NAME} 不存在,请先运行: $0 create"
        exit 1
    fi
    
    if ! check_container_running; then
        warning "容器未运行,正在启动..."
        docker start ${CONTAINER_NAME}
        sleep 2
    fi
    
    info "在容器中安装依赖..."
    docker exec -it ${CONTAINER_NAME} sh /app/base_env/install.sh
}

# 5. 保存容器为发布镜像
save_release_image() {
    if ! check_container_exists; then
        error "容器 ${CONTAINER_NAME} 不存在"
        exit 1
    fi
    
    # 检查容器是否在运行
    if ! check_container_running; then
        warning "容器未运行,正在启动..."
        docker start ${CONTAINER_NAME}
        sleep 2
    fi
    
    # 删除旧镜像(如果存在)
    if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${RELEASE_IMAGE_NAME}$"; then
        warning "删除旧镜像 ${RELEASE_IMAGE_NAME}..."
        docker rmi ${RELEASE_IMAGE_NAME} || true
    fi
    
    # 提交容器为新镜像
    info "正在提交容器 ${CONTAINER_NAME} 为镜像 ${RELEASE_IMAGE_NAME}..."
    if docker commit ${CONTAINER_NAME} ${RELEASE_IMAGE_NAME}; then
        success "镜像 ${RELEASE_IMAGE_NAME} 创建成功"
        docker images | grep ${RELEASE_IMAGE_NAME}
    else
        error "镜像创建失败"
        exit 1
    fi
}

# 6. 导出发布镜像
export_image() {
    if ! docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${RELEASE_IMAGE_NAME}$"; then
        error "发布镜像 ${RELEASE_IMAGE_NAME} 不存在,请先运行: $0 save"
        exit 1
    fi
    
    EXPORT_FILE="${NAME}_release_0.1.tar"
    info "正在导出镜像 ${RELEASE_IMAGE_NAME} 到 ${EXPORT_FILE}..."
    
    if docker save -o ${EXPORT_FILE} ${RELEASE_IMAGE_NAME}; then
        success "镜像导出成功: ${EXPORT_FILE}"
        ls -lh ${EXPORT_FILE}
    else
        error "镜像导出失败"
        exit 1
    fi
}

# 7. 删除容器
remove_container() {
    if ! check_container_exists; then
        warning "容器 ${CONTAINER_NAME} 不存在"
        return 0
    fi
    
    info "删除容器 ${CONTAINER_NAME}..."
    if docker rm -f ${CONTAINER_NAME}; then
        success "容器删除成功"
    else
        error "容器删除失败"
        exit 1
    fi
}

# 8. 删除基础镜像
remove_image() {
    if ! check_image_exists; then
        warning "镜像 ${BASE_IMAGE_NAME} 不存在"
        return 0
    fi
    
    info "删除镜像 ${BASE_IMAGE_NAME}..."
    if docker rmi ${BASE_IMAGE_NAME}; then
        success "镜像删除成功"
    else
        error "镜像删除失败"
        exit 1
    fi
}

# 9. 删除发布镜像
remove_release_image() {
    if ! check_release_image_exists; then
        warning "发布镜像 ${RELEASE_IMAGE_NAME} 不存在"
        return 0
    fi
    
    info "删除发布镜像 ${RELEASE_IMAGE_NAME}..."
    if docker rmi ${RELEASE_IMAGE_NAME}; then
        success "发布镜像删除成功"
    else
        error "发布镜像删除失败"
        exit 1
    fi
}

# 10. 显示状态
show_status() {
    echo ""
    info "=== Docker 环境状态 ==="
    echo ""
    
    # 镜像状态
    echo "基础镜像:"
    if check_image_exists; then
        docker images | grep ${CONTAINER_NAME} || true
    else
        warning "基础镜像 ${BASE_IMAGE_NAME} 不存在"
    fi
    
    echo ""
    echo "发布镜像:"
    if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${RELEASE_IMAGE_NAME}$"; then
        docker images | grep ${RELEASE_IMAGE_NAME} || true
    else
        warning "发布镜像 ${RELEASE_IMAGE_NAME} 不存在"
    fi
    
    echo ""
    echo "容器状态:"
    if check_container_exists; then
        docker ps -a | grep ${CONTAINER_NAME} || true
        if check_container_running; then
            success "容器正在运行"
        else
            warning "容器已停止"
        fi
    else
        warning "容器 ${CONTAINER_NAME} 不存在"
    fi
    
    echo ""
}

# 11. 一键部署(构建、创建、安装、保存、导出)
deploy_all() {
    # 判断是否在容器中,如果在容器中提示退出
    if [ -f "/.dockerenv" ]; then
        error "当前正在容器中,请在主机环境下运行该命令。"
        exit 1
    fi
    
    echo ""
    info "=== 开始一键部署流程 ==="
    echo ""
    
    # 步骤 1: 构建基础镜像
    info "[步骤 1/5] 构建基础镜像..."
    echo "----------------------------------------"
    build_image
    echo ""
    
    # 步骤 2: 创建容器
    info "[步骤 2/5] 创建开发容器..."
    echo "----------------------------------------"
    create_container
    echo ""
    
    # 步骤 3: 安装依赖(使用非交互式方式)
    info "[步骤 3/5] 安装环境依赖..."
    echo "----------------------------------------"
    if ! check_container_running; then
        warning "容器未运行,正在启动..."
        docker start ${CONTAINER_NAME}
        sleep 2
    fi
    
    info "在容器中安装依赖(非交互式)..."
    if docker exec ${CONTAINER_NAME} sh /app/base_env/install.sh; then
        success "依赖安装完成"
    else
        error "依赖安装失败"
        exit 1
    fi
    echo ""
    
    # 步骤 4: 保存为发布镜像
    info "[步骤 4/5] 保存容器为发布镜像..."
    echo "----------------------------------------"
    save_release_image
    echo ""
    
    # 步骤 5: 导出镜像
    info "[步骤 5/5] 导出发布镜像..."
    echo "----------------------------------------"
    export_image
    echo ""
    
    success "=== 一键部署完成! ==="
    echo ""
    info "生成的镜像文件: ${NAME}_release_0.1.tar"
    echo ""
}

# 12. 显示帮助信息
show_help() {
    printf "${BLUE}Docker 环境管理脚本${NC}\n\n"
    printf "${GREEN}用法:${NC}\n"
    printf "    %s <command> [options]\n\n" "$0"
    printf "${GREEN}可用命令:${NC}\n"
    printf "    ${BOLD}${YELLOW}deploy${NC}          一键部署(构建+创建+安装+保存+导出)\n"
    printf "    ${BOLD}${YELLOW}build${NC}           构建基础镜像\n"
    printf "    ${BOLD}${YELLOW}create${NC}          创建开发环境容器\n"
    printf "    ${BOLD}${YELLOW}exec${NC}            进入开发容器\n"
    printf "    ${BOLD}${YELLOW}install${NC}         在容器中安装依赖\n"
    printf "    ${BOLD}${YELLOW}save${NC}            保存容器为发布镜像\n"
    printf "    ${BOLD}${YELLOW}export${NC}          导出发布镜像为 tar 文件\n"
    printf "    ${BOLD}${YELLOW}rm${NC}              删除容器\n"
    printf "    ${BOLD}${YELLOW}rmi${NC}             删除基础镜像\n"
    printf "    ${BOLD}${YELLOW}rmir${NC}            删除发布镜像\n"
    printf "    ${BOLD}${YELLOW}status${NC}          显示当前状态\n"
    printf "    ${BOLD}${YELLOW}help${NC}            显示此帮助信息\n\n"
    printf "${GREEN}典型工作流程:${NC}\n"
    printf "    ${BOLD}快速部署:${NC}\n"
    printf "    %s deploy          # 一键完成构建+创建+安装+保存+导出\n\n" "$0"
    printf "    ${BOLD}分步操作:${NC}\n"
    printf "    1. %s build          # 构建基础镜像\n" "$0"
    printf "    2. %s create         # 创建开发容器\n" "$0"
    printf "    3. %s install        # 安装依赖\n" "$0"
    printf "    4. %s exec           # 进入容器开发\n" "$0"
    printf "    5. %s save           # 保存为发布镜像\n" "$0"
    printf "    6. %s export         # 导出镜像文件\n\n" "$0"
    printf "${GREEN}配置:${NC}\n"
    printf "    基础镜像: %s\n" "${BASE_IMAGE_NAME}"
    printf "    发布镜像: %s\n" "${RELEASE_IMAGE_NAME}"
    printf "    容器名称: %s\n" "${CONTAINER_NAME}"
    printf "    工作目录: %s\n" "${WORK_DIR}"
    printf "\n"
}

# ============================================================================
# 主逻辑
# ============================================================================

main() {
    local command="${1:-help}"
    
    case "$command" in
        build)
            build_image
            ;;
        create)
            create_container
            ;;
        exec)
            exec_container
            ;;
        install)
            install_dependencies
            ;;
        save)
            save_release_image
            ;;
        export)
            export_image
            ;;
        rm)
            remove_container
            ;;
        rmi)
            remove_image
            ;;
        rmir)
            remove_release_image
            ;;
        status)
            show_status
            ;;
        deploy)
            deploy_all
            ;;
        help|--help|-h)
            show_help
            ;;
        *)
            error "未知命令: $command"
            echo ""
            show_help
            exit 1
            ;;
    esac
}

# 执行主函数
main "$@"

4.Dockerfile

FROM python:3.10-slim

WORKDIR /app

# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 永久配置 ll 别名(写入 /etc/bash.bashrc,所有 bash 终端生效)
RUN echo "alias ll='ls -l'" >> /etc/bash.bashrc

CMD ["sh","startup.sh"]

5.starup.sh

#!/bin/sh
while true
do
  date 
  sleep 5s
done

6.install.sh

#!/bin/bash
set -e  # 遇到错误立即退出

# 获取脚本所在目录的绝对路径
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# 切换到脚本所在目录
cd "${SCRIPT_DIR}"

# 定义总阶段数(新增阶段时只需修改此变量)
TOTAL_STAGES=3

echo "=========================================="
echo "开始安装环境依赖..."
echo "=========================================="

echo ""
echo "[阶段 1/${TOTAL_STAGES}] 更新系统包并安装基础软件..."
echo "----------------------------------------"
apt-get update && apt-get install -y --no-install-recommends vim-tiny curl && rm -rf /var/lib/apt/lists/*
echo "✓ 阶段 1 完成"

echo ""
echo "[阶段 2/${TOTAL_STAGES}] 升级 pip..."
echo "----------------------------------------"
pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/
echo "✓ 阶段 2 完成"


echo ""
echo "[阶段 3/${TOTAL_STAGES}] 安装项目依赖..."
echo "----------------------------------------"
pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
echo "✓ 阶段 3 完成"


echo ""
echo "=========================================="
echo "✓ 所有阶段安装完成!"
echo "=========================================="

7.requirements.txt

fastapi==0.115.6

您可能感兴趣的与本文相关的镜像

Python3.11

Python3.11

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值