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. 构建基础镜像
./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 # 重新创建
注意事项
- 权限要求: 确保有执行脚本的权限,如果没有可以使用
chmod +x main.sh - Docker 环境: 确保 Docker 已安装并正在运行
- 容器内执行限制:
install和deploy命令不能在容器内执行,需要在主机环境下运行 - 镜像导出: 导出的 tar 文件会保存在脚本所在目录
- 容器挂载: 容器会自动挂载项目根目录到
/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

6444

被折叠的 条评论
为什么被折叠?



