007、Docker初探:容器化技术解决了什么问题?
环境不一致开发者的噩梦我们团队曾经有个经典段子新来的实习生提交代码前信誓旦旦说“在我电脑上没问题”结果CI流水线直接报错。问题出在哪他本地装的是Python 3.8服务器跑的是Python 3.6某个语法特性不兼容。这还只是语言版本要是算上系统库、依赖包、配置文件……“在我这儿能跑”简直成了玄学。传统虚拟机能解决部分问题但代价太大了。我有个项目用VMware跑测试环境启动一个干净的系统要两分钟内存吃掉2个G磁盘占20G。团队六个人每人开两三个虚拟机服务器内存就直接告警了。更麻烦的是镜像管理——那个“基础镜像”被不同人修改过无数次早就没人知道里面到底装了什么。Docker的解法集装箱思维想象一下货运码头。以前各种形状的货物散装运输现在全放进标准集装箱里。Docker做的就是这件事把你的应用和它所有的依赖运行时、系统工具、系统库打包成一个标准化的“集装箱”。# 以前要写一长串安装文档现在几行Dockerfile搞定 FROM python:3.8-slim # 指定基础镜像版本锁死 WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 依赖固定版本 COPY . . CMD [python, app.py] # 启动命令写死 # 这样打出来的镜像在任何装了Docker的机器上跑起来都一样最妙的是这个“集装箱”是分层的。基础镜像比如Ubuntu大家共用一层你的应用代码是上面薄薄一层。这意味着传输快、存储省——我笔记本上能跑几十个容器换虚拟机早卡死了。真实场景微服务依赖地狱去年我们拆微服务十几个服务相互调用。A服务需要Redis 4.0B服务要Redis 6.0C服务用MySQL 5.7D服务必须用MySQL 8.0。要是全装一台物理机上光解决依赖冲突就能耗掉一周。Docker让每个服务带着自己的数据库版本跑# 服务A的compose文件片段redis_a: image: redis:4.0-alpine ports: -6379:6379# 服务B的compose文件片段redis_b: image: redis:6.0-alpine ports: -6380:6379# 换个端口就行互不干扰两个Redis实例完全隔离升级其中一个不会影响另一个。这种“依赖隔离”在微服务架构里简直是救命稻草。持续交付的流水线革命我们以前的发布流程开发提交代码 - 打包成tar.gz - 写部署脚本 - 登录服务器 - 停服务 - 备份 - 解压 - 改配置 - 启动。中间任何一步出错都得回滚经常搞到凌晨三点。现在GitLab CI里这么配# .gitlab-ci.yml 关键部分deploy:stage:deployscript:-docker build-t myapp:$CI_COMMIT_SHA .# 用提交哈希打标签-docker push my-registry/myapp:$CI_COMMIT_SHA-ssh prod-server docker pull my-registry/myapp:$CI_COMMIT_SHA-ssh prod-server docker stop old-containerdocker run--name new-container myapp:$CI_COMMIT_SHA# 回滚直接拉上一个版本的镜像重启就行从代码提交到生产环境运行全自动化。出问题秒级回滚——因为旧版本的镜像还在仓库里躺着呢。资源利用的降本增效我们有个数据分析服务每天凌晨跑三小时。用物理机部署时那台服务器其他时间基本闲置。用Docker后同一个集群白天跑Web服务晚上跑分析任务CPU利用率从15%提到60%以上。对于创业公司来说这种节省是真金白银。踩坑心得当然Docker不是银弹。我交过的学费包括容器内日志没配置轮转把磁盘写满了容器时间不同步导致订单时间错乱还有网络模式配错服务间调用超时。但这些坑踩过一次就能固化到镜像或编排文件里不会再犯。个人建议如果你刚开始接触容器化别想着一步到位。先从边缘服务开始把那个总出环境问题的Python脚本Docker化或者把测试用的MySQL搬到容器里。感受一下“一次构建到处运行”的爽快感。关键是要转变思维以前我们关注“机器上装了什么”现在要关注“镜像里包含了什么”。基础设施变成了代码可以版本控制、可以Review、可以复用。这种确定性在复杂的分布式系统里尤其珍贵。下次再有人说“在我电脑上没问题”你可以笑着递给他一个Dockerfile。TOC