Docker容器批次校验流程实践 - 第1节

Docker 到底解决了什么问题?

说实话,我第一次接触 Docker 是在一个混乱的部署现场。那时候项目组还在用传统的物理机部署,每次上线都像拆弹——环境不一致、依赖冲突、配置丢失。一个新同学把 Python 3.7 装成了 3.8,整个系统就崩了。排查了一下午才发现是同一个坑。

后来我找到了 Docker。说白了,它就是个“集装箱”。把你的应用和它所有的依赖(系统库、环境变量、配置)打包成一个标准化的单元。这个单元到处都能跑:你的笔记本、测试服务器、生产环境,结果一模一样。

Docker 的核心理念

我习惯把 Docker 理解为三个维度:

  • Build once, run anywhere —— 构建一次,到处运行
  • 环境隔离 —— 每个应用拥有独立空间,互不干扰
  • 轻量级 —— 共享宿主机内核,比虚拟机快得多

你想想看,以前你搭一套开发环境得装 MySQL、Redis、Nginx,还要处理版本冲突。现在呢?一条命令拉下来就完事。

关键知识点:Docker 不是虚拟机。它共享宿主机的操作系统内核,只隔离进程空间和文件系统。所以启动一个容器只需要毫秒级,而虚拟机要几十秒。

安装 Docker 并不复杂

我个人推荐在 Linux 上使用 Docker,体验最好。Windows 和 macOS 也可以,但底层会跑一个轻量级虚拟机(Docker Desktop 帮你搞定了)。

Ubuntu 下的安装还是很清爽的:

sudo apt-get update
sudo apt-get install ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

装完之后,验证一下:

sudo docker run hello-world

看到 "Hello from Docker!" 就对了。嗯,这里要注意——默认情况下 Docker 需要 root 权限。我一般把当前用户加入 docker 组,省得每次敲 sudo:

sudo usermod -aG docker $USER

然后退出重登就生效了。

避坑提醒:加入 docker 组相当于给了用户 root 级别的权限。生产环境要谨慎控制。我曾经见过有人把普通开发账号加到 docker 组后,误删了宿主机上的数据卷,那个惨啊...

三个核心概念:镜像、容器、仓库

这是 Docker 的“三驾马车”。理解了这三个东西,你就算入门了。

镜像(Image)

镜像是一个只读模板,包含了运行应用所需的一切。你可以把它想象成一个快照,或者一个软件安装包。镜像是分层构建的,每一层记录一次变更。这样做的好处是:拉取时只下载有变化的层,省带宽省时间。

# 查看本地镜像
docker images

# 拉取一个 nginx 镜像
docker pull nginx:1.25

容器(Container)

容器是镜像的运行实例。你可以把它理解成一个轻量级的进程,有自己的文件系统、网络栈和进程空间。同一个镜像可以启动多个容器,彼此隔离。

# 基于 nginx 镜像启动一个容器
docker run -d --name my-nginx -p 8080:80 nginx:1.25

# 查看运行中的容器
docker ps

运行后,浏览器打开 http://localhost:8080 就能看到 Nginx 欢迎页了。一个干净的环境,没有污染你的系统。

仓库(Registry)

仓库用来存放和分发镜像。最著名的公共仓库是 Docker Hub。你也可以搭建私有仓库,比如 Harbor、Nexus,或者直接用云厂商的镜像服务。

实用技巧:拉取镜像时建议指定版本标签,不要用 latest。因为 latest 会变,容易引发环境不一致。我在项目中遇到过因为 latest 被覆盖导致测试和生产环境镜像不同的乌龙,后来就强制要求 tag 必须显式写明。

核心概念关系图

下面我画了一张图,帮你理清这三者之间的关系:

Docker 核心概念关系图 镜像仓库 (Registry) 镜像 (Image) 只读模板 分层构建 容器 (Container) 运行实例 隔离环境 docker pull docker run 典型工作流 从仓库拉取镜像 → 基于镜像创建容器 → 在容器中运行应用 对比虚拟机:Docker 容器共享宿主机内核,无需单独操作系统 启动时间毫秒级,资源消耗远小于虚拟机 一个宿主机可以运行成百上千个容器

常用命令速查表

刚开始用 Docker 不用背太多命令,记住这几个日常操作就够用了:

操作 命令 说明
拉取镜像 docker pull [镜像名]:[标签] 从仓库下载镜像到本地
查看镜像 docker images 列出本地所有镜像
运行容器 docker run [选项] [镜像名] 基于镜像启动一个新容器
查看容器 docker ps [-a] 查看运行中(或所有)容器
停止容器 docker stop [容器ID] 优雅停止容器
删除容器 docker rm [容器ID] 删除已停止的容器
删除镜像 docker rmi [镜像ID] 删除本地镜像

个人小技巧:记不住容器 ID 没关系,容器名称可以自己取。用 --name my-app 命名,后面操作直接指定名字就行,不用敲那一串哈希值。

聊聊我踩过的一个坑

我曾经犯过一个新手错误。当时急着部署一个 Node.js 应用,直接 docker run node:18 就跑起来了。结果一直报错说找不到模块。排查了半天才发现——我拉的是 镜像,但没有把应用代码挂载进去。容器里跑的是一个空的 Node 基础镜像,当然找不到代码。

正确的做法是:

# 把当前目录挂载到容器的 /app 目录
docker run -d --name my-node -v $(pwd):/app -w /app node:18 node index.js

或者更好的做法:写一个 Dockerfile,把应用代码打包进镜像里。后面我们会详细讲。

镜像与容器的差异

这两个概念初学者容易搞混。我列个对比表,一目了然:

特性 镜像 容器
状态 只读 可读写
生命周期 持久存储 可创建、启动、停止、删除
层结构 由多个只读层叠加 在镜像层之上加一个可写层
用途 分发和构建模板 运行应用
命名 镜像名:标签 容器 ID 或自定义名称

你想想看,镜像就像编程里的类,容器就是实例化出来的对象。一个类可以 new 出很多对象,一个镜像也能启动很多容器。

仓库不只是 Docker Hub

Docker Hub 是官方公共仓库,但国内访问速度一般。我一般配置镜像加速器:

# 编辑 /etc/docker/daemon.json
{
  "registry-mirrors": ["https://docker.mirrors.ustc.edu.cn"]
}

# 重启 Docker 生效
sudo systemctl daemon-reload
sudo systemctl restart docker

公司的生产环境,建议用私有仓库。比如 Harbor,支持权限控制、镜像扫描、复制策略,安全可控得多。

一句话总结:Docker 就是用“镜像”这个标准包装,通过“仓库”分发,以“容器”形式运行的轻量级应用隔离技术。