3、容器技术基础:Docker核心概念与实战
好,咱们进入第三章。容器技术,说白了就是PaaS平台的基石。你想想看,没有容器,谈什么弹性伸缩、环境一致性?我当年刚接触Docker时,第一反应是「这不就是个轻量级虚拟机吗?」后来踩了坑才明白——完全不是一回事。
3.1 Docker核心三要素:镜像、容器、仓库
这三个概念,我建议你当成「类、实例、代码仓库」来理解。
- 镜像(Image):一个只读的模板,包含运行应用所需的一切——代码、运行时、库、环境变量、配置文件。说白了就是「打包好的环境快照」。
- 容器(Container):镜像的运行实例。你可以启动、停止、删除它。每个容器之间相互隔离,就像一个个独立的「小服务器」。
- 仓库(Registry):存放镜像的地方。Docker Hub是公共仓库,企业一般用Harbor或阿里云镜像仓库。
核心关系:镜像 → 容器(1对N)。一个镜像可以启动无数个容器,每个容器都是独立的。
我在项目中遇到过一个问题:团队里有人直接修改容器内部的文件,然后commit成新镜像。嗯,这做法其实很危险。正确的做法是写Dockerfile,保证镜像构建可重复、可追溯。
3.2 Dockerfile编写最佳实践
Dockerfile就是镜像的「配方」。我见过太多人把Dockerfile写成「大杂烩」——所有命令堆在一起,镜像体积动辄1个G。其实有更好的写法。
3.2.1 基础结构
FROM openjdk:11-jre-slim
LABEL maintainer="yourname@company.com"
WORKDIR /app
COPY target/app.jar app.jar
EXPOSE 8080
CMD ["java", "-jar", "app.jar"]
这里有几个关键点:
- FROM:尽量选alpine或slim版本,体积小很多。我习惯用
openjdk:11-jre-slim而不是openjdk:11,体积能差300MB。 - WORKDIR:设置工作目录,避免路径混乱。
- COPY vs ADD:能用COPY就别用ADD。ADD会自动解压tar包,有时候会带来意想不到的问题。
3.2.2 层优化技巧
Dockerfile的每一条指令都会生成一个「层」。层越多,镜像越大,构建越慢。我曾经把一个项目的Dockerfile从20层优化到6层,构建时间从5分钟降到40秒。
我的习惯:把变化频率低的指令放前面,变化频率高的放后面。比如先装系统依赖,再装Python包,最后COPY代码。这样能最大化利用缓存。
# 不好的写法
FROM python:3.9
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# 好的写法
FROM python:3.9-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
避坑指南:我曾经在Dockerfile里用RUN apt-get upgrade,结果每次构建都重新下载所有包,缓存完全失效。后来改成只安装必要的依赖,配合--no-install-recommends,镜像体积直接砍半。
3.3 Docker Compose多容器编排
单机环境下,Docker Compose是管理多容器应用的神器。你想想看,一个Web应用通常需要Nginx + 后端 + MySQL + Redis,手动启动四个容器多麻烦?Compose用YAML文件搞定一切。
3.3.1 基础示例
version: '3.8'
services:
web:
build: .
ports:
- "8080:8080"
depends_on:
- db
environment:
- DB_HOST=db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root123
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
这里要注意depends_on——它只控制启动顺序,不等待服务就绪。我遇到过好几次MySQL还没启动完,后端就开始连数据库,结果报错。解决方案是加个wait-for-it脚本。
3.3.2 常用命令
| 命令 | 作用 | 我的备注 |
|---|---|---|
docker-compose up -d |
后台启动所有服务 | 开发时用up不带-d,方便看日志 |
docker-compose down |
停止并删除容器、网络 | 加-v会删卷,小心数据丢失 |
docker-compose logs -f |
实时查看日志 | 调试时必用 |
docker-compose exec web bash |
进入容器内部 | 比docker exec少打几个字 |
3.4 Docker网络与存储
这两个东西,说白了就是「容器怎么互相通信」和「数据怎么持久化」。
3.4.1 网络模式
Docker有几种网络模式,我挑最常用的说:
- bridge(默认):每个容器有自己的IP,通过端口映射对外暴露。适合单机场景。
- host:容器直接使用宿主机网络,性能好但隔离性差。我一般只在性能敏感的场景用。
- overlay:跨主机的容器通信,Swarm模式必备。
实战经验:在Compose里,所有服务默认在同一个bridge网络里,可以通过服务名互相访问。比如web服务里连接db,直接用jdbc:mysql://db:3306就行,不用记IP。
3.4.2 数据持久化
容器删除后数据就没了,这谁受得了?Docker提供了三种存储方式:
| 方式 | 说明 | 适用场景 |
|---|---|---|
| Volume(卷) | 由Docker管理,存在宿主机特定目录 | 数据库数据、配置文件 |
| Bind Mount(绑定挂载) | 直接映射宿主机目录 | 开发时热更新代码 |
| tmpfs | 存在内存中,容器停止即消失 | 敏感数据、临时缓存 |
避坑指南:我曾经在生产环境用Bind Mount挂载配置文件,结果宿主机文件权限不对,容器启动失败。后来统一改用Volume,配合docker volume create管理,再没出过问题。
嗯,到这里Docker的核心概念就讲完了。你想想看,镜像、容器、仓库是基础,Dockerfile是构建工具,Compose是编排利器,网络和存储是基础设施。把这几个点吃透,PaaS平台的容器化基础就算打牢了。
下一章咱们聊聊Kubernetes——容器编排的工业标准。到时候你会发现,Docker学好了,K8s上手会快很多。