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上手会快很多。