本文目录导读:

使用Shell脚本编排容器化应用部署是一个常见的实践,特别是在小型项目或需要快速自动化的场景中,以下是一个完整、实用的Shell脚本编排方案。
基础部署脚本结构
#!/bin/bash
set -e # 遇到错误立即退出
# ========== 配置区域 ==========
APP_NAME="myapp"
IMAGE_NAME="myapp:latest"
CONTAINER_NAME="myapp-container"
HOST_PORT=8080
CONTAINER_PORT=3000
NETWORK_NAME="app-network"
VOLUME_PATH="/data/myapp"
RESTART_POLICY="unless-stopped"
LOG_DIR="/var/log/myapp"
# =============================
# 颜色输出
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
# 日志函数
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# 检查命令是否存在
check_command() {
if ! command -v $1 &> /dev/null; then
log_error "$1 未安装,请先安装"
exit 1
fi
}
# 环境准备
prepare_environment() {
log_info "准备运行环境..."
# 检查必要工具
check_command docker
check_command docker-compose # 可选
# 创建必要的目录
mkdir -p $VOLUME_PATH $LOG_DIR
# 检查Docker服务
if ! docker info &>/dev/null; then
log_error "Docker服务未运行"
exit 1
fi
}
# 清理旧容器
cleanup_old() {
log_info "清理旧容器和镜像..."
# 停止并删除同名容器
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
docker stop $CONTAINER_NAME 2>/dev/null || true
docker rm $CONTAINER_NAME 2>/dev/null || true
log_info "已移除旧容器: $CONTAINER_NAME"
fi
# 清理旧镜像(保留最近2个版本)
docker images $IMAGE_NAME --format "{{.Repository}}:{{.Tag}}" | \
tail -n +3 | xargs -r docker rmi 2>/dev/null || true
}
# 构建镜像
build_image() {
local tag=$1
local dockerfile=${2:-Dockerfile}
log_info "构建Docker镜像: ${IMAGE_NAME}:${tag}"
docker build \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VERSION=$tag \
-t "${IMAGE_NAME}:${tag}" \
-f $dockerfile .
# 标记为latest
docker tag "${IMAGE_NAME}:${tag}" "${IMAGE_NAME}:latest"
log_info "镜像构建完成"
}
# 启动容器
start_container() {
local tag=${1:-latest}
log_info "启动容器: $CONTAINER_NAME"
# 确保网络存在
docker network create $NETWORK_NAME 2>/dev/null || true
# 运行容器
docker run -d \
--name $CONTAINER_NAME \
--network $NETWORK_NAME \
-p ${HOST_PORT}:${CONTAINER_PORT} \
-v ${VOLUME_PATH}:/app/data \
-v ${LOG_DIR}:/app/logs \
-e NODE_ENV=production \
-e APP_VERSION=$tag \
--restart $RESTART_POLICY \
--memory="512m" \
--cpus="0.5" \
--health-cmd="curl -f http://localhost:${CONTAINER_PORT}/health || exit 1" \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
${IMAGE_NAME}:${tag}
log_info "容器已启动,映射端口: ${HOST_PORT}->${CONTAINER_PORT}"
}
# 健康检查
check_health() {
local retries=12
local wait_time=5
log_info "等待服务就绪..."
for i in $(seq 1 $retries); do
if curl -sf http://localhost:${HOST_PORT}/health >/dev/null 2>&1; then
log_info "服务健康检查通过"
return 0
fi
sleep $wait_time
done
log_error "服务启动失败,查看日志: docker logs $CONTAINER_NAME"
return 1
}
# 主部署流程
deploy() {
local version=${1:-$(date +%Y%m%d%H%M%S)}
log_info "开始部署 ${APP_NAME} v${version}"
prepare_environment
cleanup_old
build_image $version
start_container $version
check_health
log_info "部署完成: http://localhost:${HOST_PORT}"
}
# 回滚到上一版本
rollback() {
log_info "执行回滚操作..."
# 获取上一个镜像版本
local prev_tag=$(docker images $IMAGE_NAME --format "{{.Tag}}" | \
grep -v "latest" | sort -r | sed -n '2p')
if [ -z "$prev_tag" ]; then
log_error "没有可回滚的版本"
exit 1
fi
log_info "回滚到版本: $prev_tag"
cleanup_old
start_container $prev_tag
check_health
log_info "回滚完成"
}
# 查看状态
status() {
echo "========== 容器状态 =========="
docker ps -a --filter "name=${CONTAINER_NAME}" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "========== 资源使用 =========="
docker stats $CONTAINER_NAME --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
echo ""
echo "========== 健康检查 =========="
docker inspect $CONTAINER_NAME --format '{{.State.Health.Status}}' 2>/dev/null || echo "未配置健康检查"
}
# 日志查看
logs() {
docker logs --tail 100 -f $CONTAINER_NAME
}
# 停止服务
stop() {
log_info "停止服务..."
docker stop $CONTAINER_NAME 2>/dev/null || true
log_info "服务已停止"
}
# 显示帮助信息
show_help() {
cat << EOF
用法: $0 {deploy|rollback|status|logs|stop|help}
命令:
deploy [version] 部署新版本,默认使用时间戳
rollback 回滚到上一版本
status 查看服务状态
logs 查看实时日志
stop 停止服务
help 显示此帮助信息
示例:
$0 deploy # 部署最新代码
$0 deploy v1.2.3 # 部署指定版本
$0 rollback # 回滚
$0 status # 查看状态
EOF
}
# 主函数
main() {
case "${1:-help}" in
deploy)
deploy "${2:-$(date +%Y%m%d%H%M%S)}"
;;
rollback)
rollback
;;
status)
status
;;
logs)
logs
;;
stop)
stop
;;
help|*)
show_help
;;
esac
}
# 执行主函数
main "$@"
多容器应用编排脚本
对于微服务架构,可以使用以下脚本编排多个服务:
#!/bin/bash
set -e
# 服务配置
SERVICE_LIST=(
"gateway:8080:3000"
"user-service:8081:3001"
"order-service:8082:3002"
"notification-service:8083:3003"
)
# 启动所有服务
start_all_services() {
log_info "启动所有微服务..."
for service in "${SERVICE_LIST[@]}"; do
IFS=':' read -r name host_port container_port <<< "$service"
log_info "启动 ${name}..."
# 检查并创建目录
mkdir -p "/data/${name}" "/var/log/${name}"
# 启动容器
docker run -d \
--name "${name}" \
--network "microservice-network" \
-p "${host_port}:${container_port}" \
-v "/data/${name}:/app/data" \
-v "/var/log/${name}:/app/logs" \
--label "service=${name}" \
--restart unless-stopped \
"${name}:latest"
done
}
# 滚动更新
rolling_update() {
local service=$1
local new_image=$2
log_info "滚动更新 ${service}..."
# 启动新容器
docker run -d \
--name "${service}-new" \
--network "microservice-network" \
-P \
--label "service=${service}" \
"${new_image}"
sleep 5
# 检查新容器健康状态
if docker inspect "${service}-new" --format '{{.State.Status}}' | grep -q "running"; then
# 停止旧容器
docker stop "${service}" 2>/dev/null || true
docker rm "${service}" 2>/dev/null || true
# 重命名新容器
docker rename "${service}-new" "${service}"
log_info "${service} 更新完成"
else
log_error "${service} 更新失败,回滚"
docker stop "${service}-new" 2>/dev/null || true
docker rm "${service}-new" 2>/dev/null || true
fi
}
# 批量启动
batch_start() {
# 并行启动服务
for service in "${SERVICE_LIST[@]}"; do
IFS=':' read -r name _ <<< "$service"
(
log_info "启动 ${name}..."
docker start "${name}" 2>/dev/null || \
log_warn "${name} 未创建或启动失败"
) &
done
wait # 等待所有后台任务完成
}
# 数据库迁移
run_migrations() {
log_info "执行数据库迁移..."
docker run --rm \
--network "microservice-network" \
-v "$(pwd)/migrations:/migrations" \
migrate/migrate \
-path /migrations \
-database "mysql://user:password@tcp(mysql:3306)/dbname" \
up
}
CI/CD集成脚本
#!/bin/bash
set -e
# Git操作
git_operations() {
log_info "拉取最新代码..."
git fetch --all
git checkout $1 # 分支名
git pull origin $1
export GIT_COMMIT=$(git rev-parse --short HEAD)
export GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
}
# 环境变量管理
manage_env() {
local env=$1 # dev, staging, production
# 加载对应环境配置
if [ -f ".env.${env}" ]; then
set -a
source ".env.${env}"
set +a
log_info "已加载 ${env} 环境配置"
fi
# 替换模板变量
envsubst < docker-compose.template.yml > docker-compose.yml
}
# 自动化部署
auto_deploy() {
local env=$1
local branch=${2:-main}
log_info "开始自动化部署到 ${env} 环境"
# 步骤1: 代码准备
git_operations $branch
# 步骤2: 环境配置
manage_env $env
# 步骤3: 运行测试
if [ "$env" != "development" ]; then
docker-compose -f docker-compose.test.yml run --rm tests
log_info "测试通过"
fi
# 步骤4: 构建部署
docker-compose build
docker-compose up -d
# 步骤5: 健康检查
sleep 10
if curl -sf http://localhost/health; then
log_info "部署成功"
# 发送通知
send_notification "部署成功: ${env} - ${GIT_COMMIT}"
else
log_error "部署失败,执行回滚"
docker-compose down
docker-compose -f docker-compose.rollback.yml up -d
fi
}
# 通知函数
send_notification() {
local message=$1
local webhook_url=${SLACK_WEBHOOK_URL:-""}
if [ -n "$webhook_url" ]; then
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"${message}\"}" \
$webhook_url
fi
}
使用示例
# 1. 基本部署 ./deploy.sh deploy # 或指定版本 ./deploy.sh deploy v1.2.3 # 2. 查看状态 ./deploy.sh status # 3. 实时日志 ./deploy.sh logs # 4. 回滚 ./deploy.sh rollback # 5. 停止服务 ./deploy.sh stop # 6. CI/CD自动化 ./ci-deploy.sh auto_deploy staging develop
最佳实践建议
- 错误处理:使用
set -e确保脚本在错误时停止 - 日志记录:详细记录每一步操作结果
- 版本管理:为每个部署打标签,支持回滚
- 健康检查:部署后验证服务可用性
- 资源限制:设置内存和CPU限制
- 环境分离:使用环境变量区分不同环境
- 并行操作:对无依赖的服务并行部署
- 通知机制:部署完成后发送通知
这个脚本框架可以根据实际需求进行扩展,添加更多功能如灰度发布、金丝雀部署、服务发现集成等。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播