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 不用背太多命令,记住这几个日常操作就够用了:
| 操作 | 命令 | 说明 |
|---|---|---|
| 拉取镜像 | 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 就是用“镜像”这个标准包装,通过“仓库”分发,以“容器”形式运行的轻量级应用隔离技术。