第一章: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 核心概念关系图

为了让你更直观地理解镜像、容器、仓库之间的关系,我画了一张图——这也算是我自己学习时的脑补。你可以看看:

Docker 核心概念关系图 仓库 (Registry) 存储与分发镜像 docker pull 镜像 (Image) 只读模板,分层结构 docker run 容器 (Container) 运行实例,可读写层 docker commit 推送镜像 docker push 关键流程 开发者推送镜像到仓库 → 任何人拉取镜像 → 运行容器 容器可被提交为新镜像,形成闭环

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 环境下几步搞定,别忘了考虑用户权限。