3、Docker基础入门:Docker架构、镜像与容器概念、Docker安装与配置、Hello World容器

好,咱们进入正题。这一章是Docker的入门课,说白了就是带你认识Docker到底是个什么东西,怎么装,怎么跑起来。我当年第一次接触Docker时,也被那一堆概念绕得头晕——镜像、容器、仓库,听着都差不多。但等你真正用上手,就会发现这些设计其实非常巧妙。

3.1 Docker架构:到底谁在干活?

Docker不是单打独斗的。它背后有一套完整的架构。我个人习惯把它拆成三块来看:

  • Docker客户端:就是你敲命令的那个终端。比如 docker rundocker build,都是客户端在干活。
  • Docker守护进程(daemon):这是真正干重活的后台服务。它负责管理镜像、容器、网络、存储卷等。
  • Docker镜像仓库(Registry):存放镜像的地方。默认是Docker Hub,你也可以搭私有的。

你想想看,客户端发一条命令,守护进程收到后就去拉镜像、启动容器,整个过程就像点外卖——你下单,厨房做菜,骑手送到。嗯,这个比喻挺贴切的。

核心关系图(文字版)

客户端(docker CLI) → 守护进程(dockerd) → 镜像仓库(Registry)

守护进程负责:拉取镜像 → 创建容器 → 管理容器生命周期

我在项目中遇到过一个问题:有同事直接在嵌入式开发板上跑 docker run,结果卡死了。后来发现是开发板内存只有256MB,而守护进程本身就要占不少资源。所以,嵌入式环境用Docker,硬件配置得心里有数。

3.2 镜像与容器:别再傻傻分不清

这两个概念,我见过太多人搞混了。其实一句话就能说清楚:

  • 镜像(Image):是一个只读的模板,包含操作系统、应用代码、依赖库等。你可以把它理解成一个“光盘”或者“安装包”。
  • 容器(Container):是镜像运行起来后的实例。它有自己的文件系统、网络、进程空间。说白了,镜像就是“菜谱”,容器就是“做出来的菜”。

为什么会这样设计?因为镜像可以复用。你拉一个Ubuntu镜像,可以启动10个不同的容器,每个容器里跑不同的应用,互不干扰。这在嵌入式构建环境里特别有用——你可以在一个镜像里装好交叉编译工具链,然后启动多个容器同时编译不同的目标平台。

我的经验:在嵌入式项目中,我习惯把基础镜像做得尽量小。比如用 alpine 而不是 ubuntu,能省下几十MB空间。别小看这点,在资源受限的板子上,每一KB都珍贵。

特性 镜像 容器
是否可变 只读 可写(运行时)
生命周期 持久存在 启动→运行→停止→删除
类比 光盘/安装包 运行中的程序
嵌入式场景 存放工具链、库文件 执行编译、测试任务

3.3 Docker安装与配置:别踩这些坑

安装Docker其实不难,但嵌入式环境往往不是标准的x86架构。我以最常见的ARM平台为例,说说步骤。

3.3.1 在Ubuntu(x86/ARM)上安装

# 更新包索引
sudo apt update

# 安装依赖
sudo apt 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引擎
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io

# 启动并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

# 验证安装
sudo docker --version

我曾经踩过的坑:在ARM开发板上安装时,如果直接用 apt install docker.io,装的是旧版本,很多新特性不支持。一定要用官方仓库。另外,记得检查内核版本——Docker需要Linux内核3.10以上,且开启cgroups和命名空间支持。嵌入式板子的内核经常被裁剪,这点要特别留意。

3.3.2 配置非root用户运行Docker

每次敲命令都要加 sudo,烦不烦?我建议你配一下用户组:

# 创建docker组(如果不存在)
sudo groupadd docker

# 将当前用户加入docker组
sudo usermod -aG docker $USER

# 重新登录或执行以下命令使生效
newgrp docker

# 测试
docker run hello-world

嗯,这里要注意:加入docker组的用户权限等同于root。在多人使用的嵌入式服务器上,要谨慎分配。

3.4 Hello World容器:跑起来看看

装好了,总得试试能不能用。Docker官方提供了一个最小的测试镜像——hello-world。它只有几KB,专门用来验证安装是否正确。

docker run hello-world

如果一切正常,你会看到类似这样的输出:

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

这个过程背后发生了什么?我拆解给你看:

  1. 客户端发命令:你敲了 docker run,客户端告诉守护进程:“我要跑一个叫hello-world的容器”。
  2. 守护进程检查本地镜像:如果本地没有这个镜像,就去Docker Hub拉取。
  3. 创建容器:拉下来后,守护进程基于这个镜像创建一个容器,并启动里面的程序。
  4. 输出结果:程序运行完,输出信息到终端,然后容器退出。

你看,整个过程就几秒钟。我第一次跑通时,心里想的是:“就这?”但后来才意识到,这背后是镜像分层、联合文件系统、命名空间隔离等一系列技术的结晶。在嵌入式构建环境里,这个流程意味着:你可以在服务器上配好一个包含完整工具链的镜像,然后任何开发机只要装个Docker,就能拉下来直接用,再也不用折腾环境配置了。

小技巧:如果你在嵌入式板子上跑 docker run hello-world 很慢,别急着怀疑网络。先检查一下存储——很多板子用的是SD卡或eMMC,IO性能差,拉取镜像时会很慢。我建议用SSD或者高性能存储卡。

好了,这一章的内容就到这里。Docker的架构、镜像与容器的区别、安装配置、以及第一个Hello World容器,你都亲手试过了。下一章我们会深入镜像的构建,教你如何制作一个专属于嵌入式开发的工具链镜像。到时候,你会真正感受到Docker在构建环境中的威力。