一、容器化基础:Docker核心概念、镜像与容器、Dockerfile编写规范、Docker Compose入门

说实话,我第一次接触Docker的时候,心里是有点抵触的。那时候我还在用虚拟机部署项目,每次环境不一致都搞得焦头烂额。后来真正上手Docker,才发现——这玩意儿,说白了就是给应用打包了一个「随身携带的运行环境」。

咱们做《因子工厂》这个项目,容器化是第一步。你想想看,如果每个开发者的电脑环境都不一样,测试和生产环境又差一截,那部署起来得多痛苦?Docker就是来解决这个问题的。

1.1 Docker核心概念:镜像、容器、仓库

这三个概念,我建议你一开始就记牢。它们就像「菜谱、菜、菜市场」的关系。

  • 镜像(Image):一个只读的模板,包含了运行应用所需的一切——代码、运行时、系统工具、库、配置。说白了就是「菜谱」。
  • 容器(Container):镜像的运行实例。你可以启动、停止、删除它。就像按照菜谱做出来的「一道菜」。
  • 仓库(Registry):存放镜像的地方。Docker Hub是最大的公共仓库,就像「菜市场」。

我个人习惯:把镜像想象成「类」,容器就是「对象」。一个镜像可以启动多个容器,每个容器都是独立的运行环境。

我在项目中遇到过一个问题:有个同事把镜像和容器搞混了,以为删了镜像容器就没了。其实不是——容器是基于镜像创建的,删了镜像,已经运行的容器还能继续跑,只是不能再启动新容器了。

1.2 镜像与容器的基本操作

咱们直接上手。先拉一个Nginx镜像试试:

# 拉取镜像
docker pull nginx:latest

# 查看本地镜像
docker images

# 运行容器
docker run -d --name my-nginx -p 8080:80 nginx

# 查看运行中的容器
docker ps

# 进入容器内部
docker exec -it my-nginx /bin/bash

# 停止容器
docker stop my-nginx

# 删除容器
docker rm my-nginx

# 删除镜像
docker rmi nginx

嗯,这里要注意:docker run 如果本地没有镜像,会自动从仓库拉取。但生产环境我建议你提前拉好,避免部署时网络波动。

避坑指南:我曾经在CI/CD流水线里直接用 docker run 拉取镜像,结果某次网络超时导致部署失败。后来我改成先 docker pull,再 docker run,稳多了。

1.3 Dockerfile编写规范

Dockerfile就是构建镜像的「配方」。写得好,镜像小、构建快、安全;写得不好,镜像几百兆、构建慢、漏洞多。

我总结了几条核心规范:

  1. 选择合适的基础镜像:别上来就 ubuntu:latest,试试 alpineslim 版本。我见过一个项目,基础镜像从1.2GB降到150MB,构建时间缩短了70%。
  2. 减少层数:每个 RUNCOPYADD 都会创建一层。能用 && 连接的,就别拆开。
  3. 利用构建缓存:把不常变动的指令放在前面,比如 apt-get install,把经常变动的代码放在后面。
  4. 使用多阶段构建:编译环境和运行环境分开,最终镜像只保留运行所需。

来看一个《因子工厂》后端服务的Dockerfile示例:

# 第一阶段:编译
FROM golang:1.21-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o factor-factory

# 第二阶段:运行
FROM alpine:3.19

RUN apk add --no-cache ca-certificates tzdata
COPY --from=builder /app/factor-factory /usr/local/bin/

EXPOSE 8080
CMD ["factor-factory"]

你看,最终镜像只有十几MB,而且不包含Go编译器,安全多了。

警告:千万不要在Dockerfile里写 RUN apt-get upgradeRUN pip install --upgrade 这种指令。它们会破坏构建的可重复性——今天构建的镜像和明天构建的可能不一样。

1.4 Docker Compose入门

单容器好办,但《因子工厂》有前端、后端、数据库、缓存等多个服务。一个个 docker run 太累了。Docker Compose就是用来定义和运行多容器应用的。

说白了,你写一个 docker-compose.yml 文件,把服务、网络、卷都定义好,然后一条命令全部启动。

来看一个简化版的《因子工厂》Compose文件:

version: '3.8'

services:
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: factor_factory
      POSTGRES_USER: factory
      POSTGRES_PASSWORD: secret
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "5432:5432"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  backend:
    build: ./backend
    depends_on:
      - db
      - redis
    environment:
      DB_HOST: db
      REDIS_HOST: redis
    ports:
      - "8080:8080"

  frontend:
    build: ./frontend
    depends_on:
      - backend
    ports:
      - "3000:3000"

volumes:
  pgdata:

启动命令就一行:

docker-compose up -d

停止也简单:

docker-compose down

我的经验:开发环境用 docker-compose up(不加-d),可以看到所有服务的日志。生产环境用 docker-compose up -d 后台运行。另外,depends_on 只控制启动顺序,不保证服务就绪——比如数据库可能还在初始化。我一般会在应用层加重试机制。

知识体系总览

下面这张图,是我梳理的本章知识结构。你可以把它当作学习地图:

容器化基础:知识体系 核心概念 镜像(只读模板) 容器(运行实例) 仓库(镜像存储) 镜像与容器操作 docker pull / images docker run / ps / exec docker stop / rm / rmi Dockerfile 基础镜像选择 减少层数 多阶段构建 Docker Compose:多容器编排 docker-compose.yml up / down / logs

这张图把本章的核心内容串起来了。从左到右,从概念到实践,再到多容器编排。你学完这一章,应该能回答这几个问题:

  • 镜像和容器有什么区别?
  • 怎么写一个高效的Dockerfile?
  • 怎么用Docker Compose一键启动所有服务?

嗯,这些就是容器化的基本功。我在《因子工厂》项目里,就是靠这套东西把部署时间从半小时压缩到了两分钟。你掌握了这些,后面的章节会越学越顺。

专注资料整理