第一章:Docker 基础——镜像、容器、仓库
说实话,刚接触 Docker 那会儿,我总觉得它就是个“轻量级虚拟机”。直到在项目里踩了几次坑,才真正明白——它跟虚拟机完全是两码事。这一章,我们先把最核心的三个概念掰扯清楚:镜像、容器、仓库。
1.1 为什么需要 Docker?
先聊聊场景。你在本地开发环境跑得好好的,代码一提交,测试同事就报“跑不起来”。查了半天,环境不一致——他 Ubuntu 20.04,你 Mac,依赖库版本差了一个小号。这种“在我机器上没问题”的梗,我遇到不下十次。
Docker 的思路很简单:把应用和它需要的所有依赖打包成一个“镜像”。这个镜像到哪都能跑,只要目标机器装了 Docker 引擎。说白了,就是环境一致性问题被彻底解决了。
核心价值:开发、测试、生产环境完全一致,告别“环境差异”带来的 bug。
1.2 三个核心概念
来,我们一个一个说。
镜像(Image)
镜像是一个只读的模板,里面包含了操作系统、运行环境、应用代码和配置。你可以把它理解成“光盘”或者“快照”。我习惯把镜像叫做“构建的产物”——你写 Dockerfile,然后 build,出来的就是镜像。
镜像采用分层结构。每一层都是只读的,层与层之间有缓存。为什么 build 第二次会快很多?因为没改的层直接复用缓存。我刚开始不懂,每次 build 都清缓存重来,浪费了不少时间。
小技巧:写 Dockerfile 时,把不常变的依赖层放在前面,常变的代码层放在后面。这样改代码不会破坏依赖层缓存。
容器(Container)
容器是镜像的运行实例。镜像跑起来,就有了一个容器。容器有自己独立的文件系统、网络、进程空间。你可以 start、stop、rm 它。它就像一个轻量级的“进程沙箱”。
记住一个点:容器是临时的。你删掉容器,里面的数据就没了。所以重要数据要用卷(volume)挂载到宿主机。我在项目里吃过这个亏——容器重启后日志全丢了,后来才加上持久化。
仓库(Repository)
仓库用来存储和分发镜像。你可以把镜像推送(push)到仓库,别人拉取(pull)下来用。Docker Hub 是官方的公共仓库,企业里常用 Harbor、Nexus 等私有仓库。
仓库名通常以 registry/image:tag 格式出现。比如 nginx:1.21,1.21 就是标签(tag)。没有标签时默认取 latest——但注意,latest 只是个名字,不是版本号。我曾经在 CI 里用了 latest,结果某天镜像更新后构建直接挂了。所以,生产环境务必指定具体版本标签。
1.3 安装与配置
安装 Docker 其实很简单。我假设你用的是 Ubuntu/Debian 系统。如果是 CentOS 或 Mac,步骤类似,但命令略有不同。下面给一份常见的安装脚本:
# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc
# 更新包索引
sudo apt-get update
# 安装依赖
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加 Docker 官方 GPG 密钥
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /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
# 安装 Docker 引擎
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
# 验证安装
sudo docker run hello-world
注意:安装后默认需要用 sudo 执行 Docker 命令。如果想免 sudo,把当前用户加入 docker 组:sudo usermod -aG docker $USER。然后退出重登。不过——加入 docker 组等同于有了 root 权限,生产环境要谨慎。
1.4 快速上手:常用命令
安装好了,我们跑几个命令试试。这些命令你以后几乎天天用:
# 查看 Docker 版本
docker version
# 查看系统信息
docker info
# 拉取镜像
docker pull nginx:1.21
# 列出本地镜像
docker images
# 运行容器(交互模式)
docker run -it --rm nginx:1.21 /bin/bash
# 后台运行容器
docker run -d --name my-nginx -p 8080:80 nginx:1.21
# 查看运行中的容器
docker ps
# 停止容器
docker stop my-nginx
# 删除容器
docker rm my-nginx
# 删除镜像
docker rmi nginx:1.21
提示:--rm 参数让容器在退出后自动删除。适合临时测试。加 -d 后台运行后,容器会一直跑,直到你 stop 它。
1.5 核心概念关系图
为了让你更直观地理解镜像、容器、仓库之间的关系,我画了一张图——这也算是我自己学习时的脑补。你可以看看:
1.6 镜像与容器的关系表格
很多人容易把镜像和容器搞混。我整理了一个对比表,看一眼就明白:
| 特性 | 镜像 | 容器 |
|---|---|---|
| 本质 | 只读模板 | 运行实例 |
| 生命周期 | 持久存在(除非删除) | 可创建、停止、删除 |
| 可写性 | 不可以写 | 可以写(有可写层) |
| 分层结构 | 多层只读层 | 镜像层 + 容器可写层 |
| 常见操作 | pull, push, build, tag | run, exec, start, stop, rm |
| 大小 | 固定(镜像大小) | 镜像大小 + 可写层增量 |
| 标识 | 镜像名:标签 | 容器ID或名称 |
1.7 基础操作小实验
我们来亲自拉一个镜像,跑个容器,玩一下。打开终端跟着做:
# 拉取一个非常小的镜像:alpine(只有5MB左右)
docker pull alpine:3.18
# 查看镜像列表
docker images
# 运行一个交互式容器,执行 shell
docker run -it --rm alpine:3.18 /bin/sh
# 进入容器后,你可以试试看:
# / # echo "Hello Docker"
# / # cat /etc/os-release
# 退出容器
# / # exit
我曾经遇到的坑:alpine 镜像默认没有 bash,只有 sh。如果你写成 /bin/bash,容器会报错。所以看镜像里有什么就用什么,或者用 ubuntu 镜像更舒服。
1.8 本章小结
我们把这章的核心点再过一遍:
- Docker 是什么:解决环境一致性问题的容器化引擎。
- 镜像:只读模板,分层构建,
docker build生成。 - 容器:镜像的运行实例,有可写层,生命周期独立。
- 仓库:镜像分发中心,
docker pull/push操作。 - 安装配置:20.04/22.04 环境下几步搞定,别忘了考虑用户权限。