【服务器与部署 27】Shell脚本自动化部署:Python应用一键部署让运维效率提升10倍
关键词:Shell脚本、自动化部署、Python应用、CI/CD、运维自动化、Bash编程、部署脚本、服务器管理
摘要:本文深入探讨Shell脚本在Python应用自动化部署中的核心作用,从基础概念到高级实践,通过实际案例展示如何编写高效、可靠的部署脚本,实现一键部署、回滚、监控等功能,大幅提升运维效率和系统稳定性。
文章目录
- 【服务器与部署 27】Shell脚本自动化部署:Python应用一键部署让运维效率提升10倍
-
- 引言:为什么需要自动化部署?
- 一、Shell脚本基础:理解自动化部署的基石
-
- 1.1 什么是Shell脚本?
- 1.2 Shell脚本的核心优势
- 二、Python应用部署脚本设计模式
-
- 2.1 基础部署脚本模板
- 2.2 高级部署脚本:支持回滚和版本管理
- 三、环境配置与依赖管理
-
- 3.1 环境检测脚本
- 3.2 依赖安装脚本
- 四、监控与日志管理
-
- 4.1 应用监控脚本
- 4.2 日志轮转脚本
- 五、CI/CD集成脚本
-
- 5.1 GitHub Actions集成
- 5.2 Jenkins集成脚本
- 六、实战案例:完整的Python Web应用部署
-
- 6.1 Django应用部署脚本
- 6.2 Flask应用部署脚本
- 七、最佳实践与安全考虑
-
- 7.1 脚本安全最佳实践
- 7.2 错误处理与恢复
- 八、性能优化与监控
-
- 8.1 部署性能优化
- 总结
-
- 关键要点回顾:
- 实践建议:
- 参考资料
引言:为什么需要自动化部署?
想象一下这样的场景:每次发布新版本时,你需要手动执行几十个步骤——上传代码、安装依赖、重启服务、检查状态…不仅耗时费力,还容易出错。而一个优秀的Shell脚本可以让你只需一个命令就完成整个部署流程。
这就是自动化部署的魅力所在。本文将带你从零开始,掌握Shell脚本自动化部署的核心技能,让你的Python应用部署变得简单、快速、可靠。
一、Shell脚本基础:理解自动化部署的基石
1.1 什么是Shell脚本?
Shell脚本就像是给计算机的"菜谱",告诉它按什么顺序执行哪些操作。在部署场景中,这个"菜谱"包含了从代码部署到服务启动的完整流程。
#!/bin/bash
# 这是一个简单的部署脚本示例
echo "开始部署Python应用…"
# 进入项目目录
cd /opt/myapp
# 拉取最新代码
git pull origin main
# 安装依赖
pip install -r requirements.txt
# 重启服务
sudo systemctl restart myapp
echo "部署完成!"
1.2 Shell脚本的核心优势
- 可重复性:每次执行结果一致,避免人为错误
- 可追溯性:记录每个步骤的执行情况
- 可扩展性:易于添加新功能和优化
- 跨平台性:在Linux/Unix系统上通用
二、Python应用部署脚本设计模式
2.1 基础部署脚本模板
让我们从一个完整的部署脚本开始:
#!/bin/bash
# 配置变量
APP_NAME="myapp"
APP_DIR="/opt/$APP_NAME"
BACKUP_DIR="/opt/backups"
LOG_FILE="/var/log/deploy.log"
PYTHON_VERSION="3.9"
# 颜色输出函数
RED='\\033[0;31m'
GREEN='\\033[0;32m'
YELLOW='\\033[1;33m'
NC='\\033[0m' # No Color
log() {
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a $LOG_FILE
}
error() {
echo -e "${RED}[ERROR] $1${NC}" | tee -a $LOG_FILE
exit 1
}
warning() {
echo -e "${YELLOW}[WARNING] $1${NC}" | tee -a $LOG_FILE
}
# 检查依赖
check_dependencies() {
log "检查系统依赖…"
# 检查Python版本
if ! command -v python$PYTHON_VERSION &> /dev/null; then
error "Python $PYTHON_VERSION 未安装"
fi
# 检查git
if ! command -v git &> /dev/null; then
error "Git 未安装"
fi
# 检查pip
if ! command -v pip &> /dev/null; then
error "Pip 未安装"
fi
log "依赖检查完成"
}
# 备份当前版本
backup_current_version() {
log "备份当前版本…"
if [ -d "$APP_DIR" ]; then
BACKUP_NAME="${APP_NAME}_$(date +%Y%m%d_%H%M%S)"
cp -r "$APP_DIR" "$BACKUP_DIR/$BACKUP_NAME"
log "备份完成: $BACKUP_DIR/$BACKUP_NAME"
else
warning "应用目录不存在,跳过备份"
fi
}
# 部署新版本
deploy_new_version() {
log "开始部署新版本…"
# 创建应用目录
mkdir -p "$APP_DIR"
cd "$APP_DIR"
# 拉取最新代码
if [ -d ".git" ]; then
git pull origin main
else
git clone https://github.com/your-repo/$APP_NAME.git .
fi
# 激活虚拟环境
if [ ! -d "venv" ]; then
python$PYTHON_VERSION -m venv venv
fi
source venv/bin/activate
# 安装依赖
pip install –upgrade pip
pip install -r requirements.txt
log "新版本部署完成"
}
# 重启服务
restart_service() {
log "重启应用服务…"
# 检查服务是否存在
if systemctl list-unit-files | grep -q "$APP_NAME.service"; then
sudo systemctl restart $APP_NAME
sudo systemctl enable $APP_NAME
# 等待服务启动
sleep 5
# 检查服务状态
if systemctl is-active –quiet $APP_NAME; then
log "服务启动成功"
else
error "服务启动失败"
fi
else
warning "服务文件不存在,请手动配置systemd服务"
fi
}
# 健康检查
health_check() {
log "执行健康检查…"
# 检查端口是否监听
if netstat -tlnp | grep -q ":8000"; then
log "应用端口监听正常"
else
error "应用端口未监听"
fi
# 检查HTTP响应
if curl -f http://localhost:8000/health > /dev/null 2>&1; then
log "应用健康检查通过"
else
error "应用健康检查失败"
fi
}
# 主函数
main() {
log "=== 开始部署 $APP_NAME ==="
check_dependencies
backup_current_version
deploy_new_version
restart_service
health_check
log "=== 部署完成 ==="
}
# 执行主函数
main "$@"
2.2 高级部署脚本:支持回滚和版本管理
#!/bin/bash
# 高级部署脚本 – 支持回滚和版本管理
APP_NAME="myapp"
APP_DIR="/opt/$APP_NAME"
VERSIONS_DIR="/opt/versions"
CURRENT_VERSION_FILE="/opt/current_version"
MAX_VERSIONS=5
# 版本管理函数
create_version() {
local version=$(date +%Y%m%d_%H%M%S)
local version_dir="$VERSIONS_DIR/$version"
log "创建版本: $version"
mkdir -p "$version_dir"
# 复制应用文件
cp -r "$APP_DIR"/* "$version_dir/"
# 记录版本信息
echo "$version" > "$CURRENT_VERSION_FILE"
# 清理旧版本
cleanup_old_versions
echo "$version"
}
rollback_version() {
local target_version=$1
if [ -z "$target_version" ]; then
error "请指定要回滚的版本"
fi
local version_dir="$VERSIONS_DIR/$target_version"
if [ ! -d "$version_dir" ]; then
error "版本 $target_version 不存在"
fi
log "回滚到版本: $target_version"
# 停止服务
sudo systemctl stop $APP_NAME
# 恢复文件
rm -rf "$APP_DIR"/*
cp -r "$version_dir"/* "$APP_DIR/"
# 更新当前版本记录
echo "$target_version" > "$CURRENT_VERSION_FILE"
# 重启服务
restart_service
health_check
log "回滚完成"
}
cleanup_old_versions() {
log "清理旧版本…"
# 获取所有版本,按时间排序
local versions=($(ls -t "$VERSIONS_DIR" 2>/dev/null))
# 保留最新的MAX_VERSIONS个版本
if [ ${#versions[@]} -gt $MAX_VERSIONS ]; then
for ((i=$MAX_VERSIONS; i<${#versions[@]}; i++)); do
local old_version="${versions[$i]}"
log "删除旧版本: $old_version"
rm -rf "$VERSIONS_DIR/$old_version"
done
fi
}
list_versions() {
log "可用版本列表:"
if [ -d "$VERSIONS_DIR" ]; then
local current_version=$(cat "$CURRENT_VERSION_FILE" 2>/dev/null)
for version in $(ls -t "$VERSIONS_DIR"); do
if [ "$version" = "$current_version" ]; then
echo " * $version (当前版本)"
else
echo " $version"
fi
done
else
echo " 暂无版本记录"
fi
}
三、环境配置与依赖管理
3.1 环境检测脚本
#!/bin/bash
# 环境检测脚本
check_environment() {
log "检测部署环境…"
# 系统信息
OS=$(uname -s)
ARCH=$(uname -m)
KERNEL=$(uname -r)
log "操作系统: $OS"
log "架构: $ARCH"
log "内核版本: $KERNEL"
# 内存检查
MEMORY=$(free -m | awk 'NR==2{printf "%.1f%%", $3*100/$2}')
log "内存使用率: $MEMORY"
if [ $(echo "$MEMORY > 90" | bc) -eq 1 ]; then
warning "内存使用率过高: $MEMORY%"
fi
# 磁盘空间检查
DISK_USAGE=$(df -h / | awk 'NR==2{print $5}' | sed 's/%//')
log "磁盘使用率: $DISK_USAGE%"
if [ "$DISK_USAGE" -gt 90 ]; then
error "磁盘空间不足: $DISK_USAGE%"
fi
# 网络连接检查
if ping -c 1 8.8.8.8 > /dev/null 2>&1; then
log "网络连接正常"
else
error "网络连接异常"
fi
}
3.2 依赖安装脚本
#!/bin/bash
# 依赖安装脚本
install_dependencies() {
log "安装系统依赖…"
# 检测包管理器
if command -v apt-get &> /dev/null; then
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y python3 python3-pip python3-venv git curl wget
elif command -v yum &> /dev/null; then
# CentOS/RHEL
sudo yum update -y
sudo yum install -y python3 python3-pip git curl wget
elif command -v dnf &> /dev/null; then
# Fedora
sudo dnf update -y
sudo dnf install -y python3 python3-pip git curl wget
else
error "不支持的包管理器"
fi
# 安装Python依赖
log "安装Python依赖…"
pip3 install –upgrade pip setuptools wheel
# 安装常用Python包
pip3 install virtualenv supervisor gunicorn uwsgi
log "依赖安装完成"
}
四、监控与日志管理
4.1 应用监控脚本
#!/bin/bash
# 应用监控脚本
monitor_application() {
local app_name=$1
local check_interval=${2:-60} # 默认60秒检查一次
log "开始监控应用: $app_name"
while true; do
# 检查进程状态
if ! pgrep -f "$app_name" > /dev/null; then
error "应用进程不存在,尝试重启…"
restart_service
fi
# 检查内存使用
local memory_usage=$(ps aux | grep "$app_name" | grep -v grep | awk '{sum+=$6} END {print sum/1024}')
if [ ! -z "$memory_usage" ] && [ $(echo "$memory_usage > 1000" | bc) -eq 1 ]; then
warning "应用内存使用过高: ${memory_usage}MB"
fi
# 检查CPU使用
local cpu_usage=$(ps aux | grep "$app_name" | grep -v grep | awk '{sum+=$3} END {print sum}')
if [ ! -z "$cpu_usage" ] && [ $(echo "$cpu_usage > 80" | bc) -eq 1 ]; then
warning "应用CPU使用过高: ${cpu_usage}%"
fi
# 健康检查
if ! curl -f http://localhost:8000/health > /dev/null 2>&1; then
error "应用健康检查失败,尝试重启…"
restart_service
fi
sleep $check_interval
done
}
4.2 日志轮转脚本
#!/bin/bash
# 日志轮转脚本
rotate_logs() {
local log_dir="/var/log/$APP_NAME"
local max_size="100M"
local max_files=10
log "执行日志轮转…"
# 检查日志文件大小
for log_file in "$log_dir"/*.log; do
if [ -f "$log_file" ]; then
local file_size=$(stat -c%s "$log_file")
local max_size_bytes=$(numfmt –from=iec $max_size)
if [ $file_size -gt $max_size_bytes ]; then
# 轮转日志文件
local timestamp=$(date +%Y%m%d_%H%M%S)
mv "$log_file" "${log_file}.${timestamp}"
gzip "${log_file}.${timestamp}"
# 清理旧日志文件
local old_logs=($(ls -t "${log_file}".*.gz 2>/dev/null | tail -n +$((max_files + 1))))
for old_log in "${old_logs[@]}"; do
rm -f "$old_log"
done
log "轮转日志文件: $log_file"
fi
fi
done
}
五、CI/CD集成脚本
5.1 GitHub Actions集成
# .github/workflows/deploy.yml
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu–latest
steps:
– uses: actions/checkout@v2
– name: Deploy to server
uses: appleboy/ssh–action@v0.1.4
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.KEY }}
script: |
cd /opt/deploy-scripts
./deploy.sh
5.2 Jenkins集成脚本
#!/bin/bash
# Jenkins部署脚本
jenkins_deploy() {
local build_number=$BUILD_NUMBER
local workspace=$WORKSPACE
log "Jenkins构建号: $build_number"
log "工作空间: $workspace"
# 复制构建产物
cp -r "$workspace/dist"/* "$APP_DIR/"
# 执行部署
deploy_new_version
# 发送通知
send_notification "部署完成 – 构建号: $build_number"
}
# 通知函数
send_notification() {
local message=$1
# 发送邮件通知
if command -v mail &> /dev/null; then
echo "$message" | mail -s "部署通知" admin@example.com
fi
# 发送钉钉通知
if [ ! -z "$DINGTALK_WEBHOOK" ]; then
curl -X POST "$DINGTALK_WEBHOOK" \\
-H 'Content-Type: application/json' \\
-d "{\\"text\\":\\"$message\\"}"
fi
# 发送企业微信通知
if [ ! -z "$WECHAT_WEBHOOK" ]; then
curl -X POST "$WECHAT_WEBHOOK" \\
-H 'Content-Type: application/json' \\
-d "{\\"text\\":\\"$message\\"}"
fi
}
六、实战案例:完整的Python Web应用部署
6.1 Django应用部署脚本
#!/bin/bash
# Django应用完整部署脚本
APP_NAME="django_blog"
APP_DIR="/opt/$APP_NAME"
STATIC_DIR="/var/www/static"
MEDIA_DIR="/var/www/media"
DB_NAME="blog_db"
DB_USER="blog_user"
deploy_django_app() {
log "部署Django应用…"
# 进入应用目录
cd "$APP_DIR"
# 激活虚拟环境
source venv/bin/activate
# 安装依赖
pip install -r requirements.txt
# 数据库迁移
python manage.py migrate
# 收集静态文件
python manage.py collectstatic –noinput
# 创建超级用户(如果不存在)
echo "from django.contrib.auth.models import User; User.objects.filter(username='admin').exists() or User.objects.create_superuser('admin', 'admin@example.com', 'admin123')" | python manage.py shell
# 重启服务
restart_service
log "Django应用部署完成"
}
# 数据库备份
backup_database() {
log "备份数据库…"
local backup_file="/opt/backups/db_$(date +%Y%m%d_%H%M%S).sql"
pg_dump -U "$DB_USER" -h localhost "$DB_NAME" > "$backup_file"
gzip "$backup_file"
log "数据库备份完成: ${backup_file}.gz"
}
# 数据库恢复
restore_database() {
local backup_file=$1
if [ -z "$backup_file" ]; then
error "请指定备份文件"
fi
log "恢复数据库: $backup_file"
# 停止应用
sudo systemctl stop $APP_NAME
# 恢复数据库
gunzip -c "$backup_file" | psql -U "$DB_USER" -h localhost "$DB_NAME"
# 重启应用
restart_service
log "数据库恢复完成"
}
6.2 Flask应用部署脚本
#!/bin/bash
# Flask应用部署脚本
APP_NAME="flask_api"
APP_DIR="/opt/$APP_NAME"
deploy_flask_app() {
log "部署Flask应用…"
cd "$APP_DIR"
source venv/bin/activate
# 安装依赖
pip install -r requirements.txt
# 设置环境变量
export FLASK_ENV=production
export FLASK_APP=app.py
# 重启Gunicorn服务
sudo systemctl restart $APP_NAME
log "Flask应用部署完成"
}
# 创建systemd服务文件
create_systemd_service() {
local service_file="/etc/systemd/system/$APP_NAME.service"
cat > "$service_file" << EOF
[Unit]
Description=$APP_NAME
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=$APP_DIR
Environment=PATH=$APP_DIR/venv/bin
ExecStart=$APP_DIR/venv/bin/gunicorn -w 4 -b 0.0.0.0:8000 app:app
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable $APP_NAME
log "Systemd服务文件创建完成"
}
七、最佳实践与安全考虑
7.1 脚本安全最佳实践
#!/bin/bash
# 安全最佳实践示例
# 1. 设置严格模式
set -euo pipefail
# 2. 使用函数封装敏感操作
secure_deploy() {
# 验证用户权限
if [ "$EUID" -ne 0 ]; then
error "需要root权限执行此操作"
fi
# 验证文件完整性
if [ ! -f "$APP_DIR/requirements.txt" ]; then
error "requirements.txt文件不存在"
fi
# 使用临时文件
local temp_file=$(mktemp)
trap "rm -f $temp_file" EXIT
# 执行部署
deploy_new_version
}
# 3. 输入验证
validate_input() {
local input=$1
# 检查是否包含危险字符
if [[ "$input" =~ [;&|`$] ]]; then
error "输入包含危险字符"
fi
# 检查路径遍历
if [[ "$input" =~ \\.\\. ]]; then
error "检测到路径遍历攻击"
fi
}
# 4. 日志审计
audit_log() {
local action=$1
local user=$(whoami)
local timestamp=$(date +'%Y-%m-%d %H:%M:%S')
local ip=$(who am i | awk '{print $5}' | sed 's/[()]//g')
echo "$timestamp | $user@$ip | $action" >> /var/log/deploy_audit.log
}
7.2 错误处理与恢复
#!/bin/bash
# 错误处理与恢复机制
# 设置错误处理
set -e
trap 'error_handler $? $LINENO $BASH_LINENO "$BASH_COMMAND" $(printf "::%s" ${FUNCNAME[@]:-})' ERR
error_handler() {
local exit_code=$1
local line_no=$2
local bash_lineno=$3
local last_command=$4
local func_stack=$5
error "脚本执行失败:"
error " 退出码: $exit_code"
error " 行号: $line_no"
error " 命令: $last_command"
error " 函数栈: $func_stack"
# 执行清理操作
cleanup_on_error
exit $exit_code
}
cleanup_on_error() {
log "执行错误清理…"
# 清理临时文件
find /tmp -name "deploy_*" -mtime +1 -delete
# 恢复备份(如果存在)
if [ -f "$CURRENT_VERSION_FILE" ]; then
local current_version=$(cat "$CURRENT_VERSION_FILE")
if [ -d "$VERSIONS_DIR/$current_version" ]; then
log "尝试恢复到上一个版本: $current_version"
rollback_version "$current_version"
fi
fi
}
八、性能优化与监控
8.1 部署性能优化
#!/bin/bash
# 部署性能优化脚本
optimize_deployment() {
log "优化部署性能…"
# 并行安装依赖
install_dependencies_parallel() {
local requirements_file="$APP_DIR/requirements.txt"
local max_jobs=4
# 分割requirements文件
split -l $(( $(wc –l < "$requirements_file") / $max_jobs + 1 )) "$requirements_file" /tmp/req_part_
# 并行安装
for part in /tmp/req_part_*; do
pip install -r "$part" &
done
wait
# 清理临时文件
rm -f /tmp/req_part_*
}
# 使用缓存
if [ -d "$HOME/.cache/pip" ]; then
export PIP_CACHE_DIR="$HOME/.cache/pip"
fi
# 预编译Python包
pip install –compile -r requirements.txt
log "部署性能优化完成"
}
# 部署时间统计
measure_deployment_time() {
local start_time=$(date +%s)
# 执行部署
main "$@"
local end_time=$(date +%s)
local duration=$((end_time – start_time))
log "部署耗时: ${duration}秒"
# 记录性能指标
echo "$(date +%Y-%m-%d),$duration" >> /var/log/deploy_performance.csv
}
总结
通过本文的学习,你已经掌握了Shell脚本自动化部署的核心技能。从基础的脚本编写到高级的CI/CD集成,从简单的部署流程到复杂的环境管理,这些技能将大大提升你的运维效率。
关键要点回顾:
实践建议:
- 从简单的部署脚本开始,逐步添加复杂功能
- 重视错误处理和日志记录,便于问题排查
- 定期测试和优化脚本,确保可靠性
- 建立完善的文档和备份机制
记住,优秀的部署脚本不仅要功能完整,更要稳定可靠。通过持续的学习和实践,你将能够构建出企业级的自动化部署系统。
评论前必须登录!
注册