一、当Shell脚本遇上Docker:天生一对的好搭档
想象一下,你每天都要重复执行几十次"docker build"和"docker run"命令,还要手动管理容器网络和存储卷。这种日子是不是让你抓狂?别担心,Shell脚本就是来拯救你的超级英雄!它和Docker的结合,就像咖啡遇上奶泡,能调出效率的美味拿铁。
Shell脚本可以帮我们把繁琐的Docker操作封装成简单的命令。比如下面这个启动MySQL服务的例子:
#!/bin/bash
# 定义变量
CONTAINER_NAME="mysql_dev"
MYSQL_PASSWORD="safe_password123"
DATA_DIR="/opt/mysql_data"
# 检查数据目录是否存在
if [ ! -d "$DATA_DIR" ]; then
mkdir -p $DATA_DIR
echo "创建数据目录: $DATA_DIR"
fi
# 运行MySQL容器
docker run -d \
--name $CONTAINER_NAME \
-e MYSQL_ROOT_PASSWORD=$MYSQL_PASSWORD \
-v $DATA_DIR:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0 \
--default-authentication-plugin=mysql_native_password
# 检查容器状态
if [ $(docker inspect -f '{{.State.Running}}' $CONTAINER_NAME) = "true" ]; then
echo "MySQL容器启动成功!"
else
echo "警告:MySQL容器启动异常!"
fi
这个脚本做了几件很酷的事:
- 自动创建数据存储目录
- 用安全的方式设置MySQL密码
- 挂载数据卷实现持久化
- 自动检查容器是否正常运行
二、进阶玩法:多容器编排与管理
当项目变得复杂时,单个容器往往不够用。我们需要同时管理多个容器,并让它们协同工作。下面这个示例展示了如何用Shell脚本编排一个简单的Web应用栈(Nginx + Node.js + Redis):
#!/bin/bash
# 定义网络
NETWORK_NAME="myapp_network"
docker network create $NETWORK_NAME
# 启动Redis服务
docker run -d \
--name redis \
--network $NETWORK_NAME \
redis:6
# 启动Node.js应用
docker run -d \
--name node_app \
--network $NETWORK_NAME \
-v $(pwd)/app:/usr/src/app \
-w /usr/src/app \
node:16 \
sh -c "npm install && npm start"
# 启动Nginx反向代理
docker run -d \
--name nginx \
--network $NETWORK_NAME \
-p 80:80 \
-v $(pwd)/nginx.conf:/etc/nginx/nginx.conf \
nginx:latest
echo "应用栈部署完成!访问 http://localhost"
这个脚本的亮点在于:
- 创建了专用Docker网络让容器间可以互相通信
- 使用数据卷挂载应用代码和配置
- 自动构建Node.js依赖
- 通过Nginx暴露服务
三、自动化构建与部署:让CI/CD更简单
在持续集成环境中,我们经常需要自动构建Docker镜像并推送到仓库。下面这个脚本可以集成到你的Jenkins或GitLab CI中:
#!/bin/bash
# 参数检查
if [ $# -lt 2 ]; then
echo "用法: $0 <项目目录> <镜像标签>"
exit 1
fi
PROJECT_DIR=$1
IMAGE_TAG=$2
REGISTRY="registry.mycompany.com:5000"
# 进入项目目录
cd $PROJECT_DIR || exit 1
# 构建Docker镜像
echo "开始构建镜像..."
docker build -t $IMAGE_TAG .
# 标记并推送镜像
echo "标记镜像..."
docker tag $IMAGE_TAG $REGISTRY/$IMAGE_TAG
echo "推送镜像到仓库..."
docker push $REGISTRY/$IMAGE_TAG
# 清理本地镜像
echo "清理..."
docker rmi $IMAGE_TAG $REGISTRY/$IMAGE_TAG
echo "构建流程完成!镜像地址: $REGISTRY/$IMAGE_TAG"
这个脚本展示了:
- 参数化输入提高复用性
- 完整的构建-标记-推送流程
- 自动清理避免占用磁盘空间
- 详细的执行日志输出
四、实用技巧与避坑指南
在实际使用中,我总结了一些非常有用的技巧:
- 容器健康检查:在脚本中添加健康检查逻辑
# 等待容器健康状态
wait_for_healthy() {
local container=$1
local timeout=${2:-60}
echo "等待 $container 变为健康状态..."
for i in $(seq 1 $timeout); do
status=$(docker inspect --format='{{.State.Health.Status}}' $container)
if [ "$status" = "healthy" ]; then
echo "$container 已健康运行!"
return 0
fi
sleep 1
done
echo "超时:$container 未达到健康状态"
return 1
}
# 使用示例
wait_for_healthy "mysql_dev" 120
- 日志收集:自动收集容器日志到文件
# 收集最近1小时的日志
collect_logs() {
local container=$1
local log_file="logs/${container}_$(date +%Y%m%d%H%M).log"
mkdir -p logs
echo "收集 $container 日志到 $log_file..."
docker logs --since 1h $container > $log_file 2>&1
}
- 安全提示:不要在脚本中硬编码密码!应该使用:
# 从环境变量读取密码
if [ -z "$DB_PASSWORD" ]; then
echo "错误:DB_PASSWORD环境变量未设置"
exit 1
fi
docker run -e MYSQL_PASSWORD=$DB_PASSWORD ...
五、应用场景与技术选型
这种技术组合特别适合以下场景:
- 开发环境快速搭建
- 自动化测试环境准备
- 小型项目的轻量级部署
- 日常运维任务自动化
技术优点:
- 极低的学习曲线(只要会基础Shell和Docker命令)
- 无需额外依赖,所有Linux系统原生支持
- 灵活性强,可以处理各种定制化需求
- 调试方便,可以逐步执行查看效果
技术局限:
- 不适合超大规模容器编排(这种情况下应该用Kubernetes)
- 脚本可能变得难以维护(需要良好的代码组织)
- 缺乏原生的事务支持(需要自己实现回滚逻辑)
注意事项:
- 始终处理错误情况(使用set -euo pipefail)
- 为脚本添加详细的日志输出
- 考虑添加参数校验和帮助信息
- 重要操作前添加确认提示
六、总结与展望
通过Shell脚本与Docker的集成,我们实现了从单一容器管理到复杂应用栈编排的自动化。这种轻量级方案特别适合中小型项目,能够在保持简单性的同时提供强大的自动化能力。
未来可以进一步考虑:
- 与Ansible等配置管理工具集成
- 添加更完善的错误恢复机制
- 实现蓝绿部署等高级部署策略
- 集成监控告警功能
记住,自动化不是为了炫技,而是为了让生活更轻松。从今天开始,把你重复的Docker操作变成脚本吧!
评论