1、Docker 入门:Docker 发展史、核心概念、安装与配置

聊到容器技术,我总会想起2014年刚接触 Docker 的场景。那时公司要把 Java 应用部署到几十台服务器上,每次上线都像在拆弹。一个环境配置不对,整个集群就炸了。直到 Docker 出现,嗯,事情才变得简单。

说白了,Docker 就是一个“打包工具”。它把你的应用、依赖、配置、运行环境全部塞进一个标准化的“箱子”里。这个箱子在任何机器上打开,行为完全一致。你想想看,这解决了多大的问题?

1.1 发展史:从 LXC 到云原生标配

Docker 不是凭空冒出来的。2013 年,dotCloud 公司内部有个叫 Solomon Hykes 的工程师,受不了当时 PaaS 平台的部署痛苦,就基于 LXC(Linux Container)搞了个上层工具,叫 Docker。我早期在项目中试过 LXC 裸用,那体验确实不友好——配置复杂,隔离性也差。Docker 把这些糟心事全封装了。

几个关键时间点:

  • 2013 年 3 月:Docker 首次公开亮相,PyCon 上的演示引爆了社区。
  • 2014 年 6 月:Docker 1.0 发布,开始有商用版本。
  • 2015 年:OCI(Open Container Initiative)成立,定义容器运行时和镜像标准。Docker 把核心组件 runC 捐给了社区。
  • 2017 年:Docker 拆分出 Moby 项目,社区版正式叫 Docker CE,企业版叫 Docker EE。
  • 2020 年至今:Kubernetes 成为容器编排事实标准,Docker 转向以 containerd 为核心的底层运行时。

为什么 Docker 能赢? 不是因为它技术最牛,而是它让“打包-分发-运行”这条链路变得极简。我见过很多技术产品,功能堆得很强,但用户根本用不起来。Docker 恰恰相反——它把复杂性藏了起来。

我记得 2015 年帮客户做微服务迁移,传统虚拟机方案要半小时启动一台,Docker 容器只需 2 秒。这个差异直接让客户拍板换了技术栈。

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

这三个概念可以说是 Docker 的“三驾马车”。理解它们,你就掌握了 Docker 的 80%。

镜像 (Image)

镜像是只读的模板。它里面包含了操作系统基础层、运行时、库、代码、配置等一切。你可以把镜像理解成“虚拟机快照”的轻量版。我经常跟新人说:“镜像是一个打包好的文件系统,加上一些启动参数。”

镜像采用分层存储。每一层都是只读的,构建时通过 Dockerfile 的指令一层层叠加。这样做的好处是——相同层可以复用,拉取快,磁盘占用低。

容器 (Container)

容器是镜像的运行实例。它在镜像层之上加了一层可写层,所有对文件系统的修改都写在这里。容器可以被启动、停止、删除、暂停。每个容器都运行在独立的命名空间里,进程、网络、文件系统全隔离。

你想想看,这就像从同一个安装包(镜像)装出来的多个沙箱。每个沙箱随便玩,玩坏了删掉重来,不影响原包。

仓库 (Registry)

仓库用来存储和分发镜像。Docker Hub 是最大的公共仓库,但生产环境中多数企业都搭建私有仓库,比如 Harbor 或者直接用云厂商的镜像服务。

举个最简单的例子:

docker pull nginx:latest       # 从 Docker Hub 拉取 nginx 镜像
docker run -d -p 80:80 nginx   # 基于镜像启动一个容器

一个小技巧: 我习惯把镜像名写成 registry/namespace/repo:tag 的格式。比如 mycompany.com/microservice/user-service:v1.2。这样一眼就能看出镜像来源、项目归属和版本。

为了帮你一目了然,我画了张图,把三者的关系捋清楚:

Docker 核心概念关系图 Docker 引擎 (守护进程 + API + CLI) 镜像仓库 Registry 镜像 只读模板 容器 运行中的实例 推送/拉取 pull run 管理 引擎负责与仓库通信、拉取镜像、管理容器的整个生命周期。

1.3 安装与配置

安装方式有很多种,这里我只推荐最稳妥的——使用官方仓库安装。曾经有个同事从第三方源装了个 Docker,结果版本不对,折腾两天才发现是源的问题。所以,直接用官方的。

在 Ubuntu/Debian 上安装

# 卸载旧版本
sudo apt-get remove docker docker-engine docker.io containerd runc

# 安装依赖
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg lsb-release

# 添加 Docker 官方 GPG 密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

# 添加稳定版仓库
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 安装 Docker CE
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io

# 将当前用户加入 docker 组,避免每次都 sudo
sudo usermod -aG docker $USER

注意: 加入 docker 组后,必须重新登录才能生效。我曾经见人在这步卡了一下午,一直报权限错误,其实是忘记切会话了。

验证安装

docker --version
# 输出类似:Docker version 24.0.7, build 24.0.7-0ubuntu1

docker run hello-world
# 如果看到 "Hello from Docker!" 就说明成功了

docker run hello-world 这个镜像极小,它只是验证守护进程工作正常。我第一次跑通的时候,感觉像给新电脑装好了系统,踏实。

配置镜像加速器

国内拉取 Docker Hub 镜像经常超时。我建议配置镜像加速器。编辑或创建 /etc/docker/daemon.json:

{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "https://hub-mirror.c.163.com"
  ],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

个人习惯: 我会把 max-size 设小一点,默认的日志文件容易撑爆磁盘。曾经在生产环境遇到过容器的日志写满根分区,导致整个宿主机挂掉。从那以后,每个节点我都强制配置日志轮转。

配置好后重启 Docker:

sudo systemctl daemon-reload
sudo systemctl restart docker

验证是否生效:

docker info | grep -A 10 "Registry Mirrors"
# 应该能看到你配置的镜像地址

1.4 快速上手:拉取镜像并运行容器

来个完整的 Demo。假设你要跑一个 Nginx 服务:

# 拉取镜像
docker pull nginx:alpine

# 查看本地镜像
docker images

# 启动容器,将宿主机 8080 端口映射到容器 80 端口
docker run -d --name my-nginx -p 8080:80 nginx:alpine

# 查看运行中的容器
docker ps

# 在容器内执行命令
docker exec -it my-nginx /bin/sh

# 停止并删除容器
docker stop my-nginx
docker rm my-nginx

打开浏览器访问 http://localhost:8080,就能看到 Nginx 欢迎页。整个过程不到 30 秒。如果是传统方式,你得装 Nginx、调配置、开端口、加守护进程……嗯,差距就是这么大。

容器 vs 虚拟机
特性 Docker 容器 传统虚拟机
启动速度 秒级 分钟级
占用空间 MB 级 GB 级
隔离级别 进程级命名空间 完整操作系统
性能损失 接近原生 有一定损耗

一句话总结: Docker 通过镜像保证环境一致性,通过容器提供轻量隔离,通过仓库实现标准化分发。这套组合拳,让它成为现代软件交付的基石。我见过的所有大型互联网公司,没有一家不在用容器技术。