从 Docker 1.11 开始,Docker 容器运行已经不是简单的通过 Docker Daemon 启动,而是集成了 containerd、runC 等多个组件,在 Docker 服务启动之后,可以看到系统上启动了 dockerd、containerd 等进程。
这里简单介绍一些与 Docker 相关的基本概念。
简介
在安装了 Docker 之后,比较重要的组件主要有如下几种:
docker
一个客户端工具,用来把用户的请求发送给 dockerd;dockerd
一般也会被称为 docker engine,在后台运行;docker-containerd
管理 Docker 的核心组件;docker-containerd-shim
容器的运行时载体;runc
真正运行的容器。
各个模块的组件如下。
安装
这里包含了 CentOS、Debain 原生的安装,以及直接通过二进制包安装。
CentOS
可以通过安装 YUM 安装。
----- 如有需要,删除老版本
# yum remove docker docker-client docker-client-latest docker-common docker-latest \
docker-latest-logrotate docker-logrotate docker-engine
----- 配置阿里云的源
# yum install -y yum-utils device-mapper-persistent-data lvm2
# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
----- 安装包
# yum install docker-ce docker-ce-cli containerd.io
在 CentOS 8 中,如果使用 yum-config-manager
需要安装 dnf-utils
包。
Debain
通过 apt 进行安装,使用的是网易的镜像源。
----- 添加源
# curl -fsSL http://mirrors.163.com/docker-ce/linux/debian/gpg | sudo apt-key add -
# add-apt-repository "deb http://mirrors.163.com/linux/debian $(lsb_release -cs) stable"
----- 更新源数据并安装
# apt update
# apt install docker-ce
二进制
或者下载 静态二进制文件,直接复制到 /usr/bin
目录下,并将 docker.service docker.socket containerd.service 几个服务添加到 /usr/lib/systemd/system/
目录下。
正常来说,可以将二进制文件放到 /opt/docker
目录下,但是在执行 run
命令的时候会报 shim
命令在 PATH
中不存在,应该是查找路径没有配置好,暂时没有仔细研究怎么配置。
镜像地址
默认 Docker 会使用 index.docker.io
作为镜像源,国内使用会比较慢,可以使用一些国内镜像替换,直接修改 /etc/docker/daemon.json
配置文件。
{
"registry-mirrors": [
"https://registry.hub.docker.com",
"http://hub-mirror.c.163.com",
"https://docker.mirrors.ustc.edu.cn",
"https://registry.docker-cn.com",
"https://dockerpull.com", // 2024.10 新增
"https://dockerproxy.cn" // 2024.10 新增
]
}
修改后重启,然后通过 docker info
查看 Registry Mirrors
配置项即可。
配置使用
安装完后可以通过 systemctl start docker
启动,通过 docker run hello-world
进行测试。
仓库地址
可以通过 docker info
中的 Registry 字段查看当前使用的仓库地址,如果没有则使用的是默认 https://hub.docker.com/
地址,在国内的仓库 registry.docker-cn.com
中包含了流行的公有镜像,私有镜像仍需要从 Docker Hub 镜像库中拉取。
----- 通过命令行拉取
$ docker pull registry.docker-cn.com/library/ubuntu:16.04
也可以在启动的时候通过 --registry-mirror
参数指定,然后直接使用 library/ubuntu
获取。还可以在配置文件 /etc/docker/daemon.json
文件并添加上 registry-mirrors
配置。
{
"registry-mirrors": ["https://registry.docker-cn.com"]
}
修改存储目录
容器的镜像等数据会保存在 Docker 的根目录下,可以通过 docker info | grep 'Docker Root Dir'
查看当前的配置,默认保存在 /var/lib/docker
目录下,可以通过如下方式修改。
# vim /etc/docker/daemon.json
{
"data-root": "/opt/docker"
}
然后重启 docker 服务即可,另外,在 19 版本之前可以使用 graph
参数,不过其之后建议使用 data-root
参数。
启动参数
可以在启动时通过参数定制,例如.
--storage-driver=overlay2
使用存储驱动;--registry-mirror=https://foobar.com
镜像地址;--insecure-registry=http://foobar.com
指定非安全的镜像仓库地址。
常用命令
在通过 run
命令启动时,常用的参数如下。
-i, --interactive=true|false 保持STDIN打开,也就是交互方式
-t, --tty=true|false 是否分配一个tty终端
-d, --detach=true|false 在后台运行
-p, --publish=[] 端口映射,可以查看帮助
-v, --volume=[] 文件映射,可以查看帮助
--name 指定运行时的容器名称
详细可通过 man docker
查看帮助,利用 man docker-run
查看子命令帮助文档。
----- 查找镜像
# docker search centos
----- 下载镜像,可以通过 :NAME.TAG 指定版本号以及Tag信息
# docker pull docker.io/hello-world
# docker pull docker.io/centos
----- 查看镜像,及其详细信息,也可以查看DIGEST
# docker images --digests
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/centos latest 67fa590cfc1c 4 weeks ago 202 MB
# docker inspect 67fa590cfc1c
----- 镜像的构建信息,包括了含有几层,可以通过 --no-trunc 查看完整命令
# docker history busybox
----- 删除镜像,可以通过REPO或者ID指定
# docker image rm 67fa590cfc1c
----- 重命名镜像,通过ID指定,同样可以删除Tag
# docker tag 67fa590cfc1c centos
# docker rmi centos:7.7.1908
----- 运行容器,指定名称为REPOSITORY:TAG
# docker run hello-world
# docker run -it docker.io/centos /bin/bash
----- 后台运行,并attach到后台容器
# docker run -itd --name centos centos /bin/bash
# docker exec -it centos /bin/bash
----- 查看正在运行的容器,可以通过-a参数查看所有
# docker ps
------ 可以从容器和本地之间相互复制文件
# docker cp YourContainerID:/opt/file.txt /opt/file.txt
# docker cp /opt/file.txt YourContainerID:/opt/file.txt
----- 链接到运行的容器,需要指定CONTAINER ID
# docker start 5328dfa90416
# docker stop 5328dfa90416
# docker attach 5328dfa90416
# docker container rm 5328dfa90416
----- 如果要推送镜像是需要登录的,默认会明文保存在~/.docker/config.json文件中,也可以logout
# docker login registry.example.com
----- 重命名镜像,通过ID指定,也可以增加自己的仓库地址,然后直接推送的自己的仓库中
# docker tag 67fa590cfc1c centos:7.9.2009
# docker tag 67fa590cfc1c registry.example.com/centos:latest
# docker push registry.example.com/centos:latest
----- 将正在运行的镜像保存
# docker commit -a "AuthorName" -m "SomeCommitMessage" RunningContainerID Name:Tag
----- 将已有的镜像保存,然后本地加载镜像
$ docker save -o busybox.tar busybox:latest
$ docker load -i busybox.tar
----- 清理已经停止的容器
# docker ps -a | grep "Exited" | awk '{print $1 }' | xargs docker rm
注意,在拉取镜像时还可以指定指纹信息,例如如下命令。
# docker pull kindest/node:v1.27.3@sha256:3966ac761ae0136263ffdb6cfd4db23ef8a83cb
# docker images --digests
其中指纹信息可以保存在 /var/lib/docker/image/overlay2/repositories.json
文件中。
端口映射
容器在启动的时候如果不指定端口映射参数,在容器外部是无法通过网络来访问容器内的应用的,可以在 Dockerfile
中通过 EXPOSE 80
类似的指令将其导出,或者启动时通过参数指定。
参数格式如下。
ip:hostport:containerport # 全部指定
ip::containerport # 主机Port随机
hostport:containerport # 未指定ip、指定宿主机port、指定容器port
如下是常见的示例。
----- 容器所有端口都随机映射到宿主机上
# docker run -P -it ubuntu /bin/bash
----- 将容器的80端口随机映射到宿主机的一个端口上
# docker run -p 80 -it ubuntu /bin/bash
----- 容器的80端口映射到8000端口上
# docker run -p 8000:80 -it ubuntu /bin/bash
----- 容器的192.168.0.100和80端口随机映射到宿主机的一个端口上
# docker run -p 192.168.0.100::80 -it ubuntu /bin/bash
----- 容器的192.168.0.100和80端口映射到宿主机的8000端口上
# docker run -p 192.168.0.100:8000:80 -it ubuntu /bin/bash
然后可以通过 docker port CONTAINER-ID
命令查看。
资源清理
随着 Docker 的运行,会占用越来越多的资源,比较常见的是磁盘资源,对于一些无用的镜像、容器、网络、数据卷实际可以直接删除,可以通过如下命令查看当前使用的资源。
----- 所有的容器信息,不带-a参数之列举出运行的容器
# docker container ls -a
----- 镜像信息,通过-a会列举出中间的镜像层
# docker image ls -a
----- 列出数据卷、网络信息、系统信息
# docker volume ls
# docker network ls
# docker info
默认提供了 docker system prune
命令来删除已经停止的容器、dangling 镜像、为被容器引用的 network 以及构建过程中的 cache 。
注意,为了安全,默认不会删除未被任何容器引用的数据卷,如果需要则应该显示指定 --volumes
参数。
# docker system prune --all --force --volumes
dangling images
所谓的 dangling images ,可以被理解为未被任何镜像引用的镜像,当通过 docker image ls
查看时,会在 TAG
中显示 <none>
,例如,重新构建镜像之后,之前依赖的镜像就变成了 dangling images 。
另外,还有 REPOSITOY
和 TAG
都是 <none>
的镜像,一般是其它镜像的依赖层。
镜像介绍
Docker 镜像由多个只读文件系统叠加而成,起始于一个基础镜像层,在修改后就会在当前镜像层之上创建新的镜像层,而且是只读的,例如如下。
启动容器后,会在顶层增加一个读写层,容器修改存在文件时,实际是将只读层文件复制到读写层,然后进行修改,原文件则会被隐藏,这也是 Union File System 的作用,在重启之后则原文件保持不变,也就是对应的数据会丢失。
目录结构
这种镜像采用的分层设计,每层被可以称之为 layer,其中 Docker 的镜像默认会保存在 /var/lib/docker
目录下,当前环境可以通过 docker info | grep 'Docker Root Dir'
查看,各个目录的含义介绍如下。
另外 podman
默认会保存在 /var/lib/containers/storage/overlay
目录下,这里仍然以 Docker 为例。
builder/
buildkit/
containers/
image/ 存储镜像管理数据的目录
|-overlay2/ 子目录对应了存储驱动
|-distribution/ 远端拉到本地的镜像相关元数据
|-imagedb/ 镜像数据库
| |-content/
| |-sha256/ 镜像的ID信息,images命令的前12字节来自这里
|-leveldb/ 镜像每个Layer的元数据
| |-sha256/ Layer的ChainID
|-repositories.json 通过JSON保存的相关元数据
network/
overlay2/
plugins/
runtimes/
swarm/
tmp/
trust/
volumes/
其中 image 由多个 layer 组合而成的,那么有可能多个 image 会指向同一个 layer ,一个 image 指向了那些 layer 可以通过 imagedb 查看。
存储驱动
也就是 GraphDriver ,默认支持多种,包括了 VFS、DeviceMapper、aufs、Overlay 等,开始最常用的是 aufs ,不过随着 Linux 3.18 将 Overlay 纳入内核,默认的驱动一般就是 Overlay 。
当前的驱动可以通过 docker info | grep 'Storage Driver'
查看,最新的一般是 overlay2
,这里只需要关心 root 目录下的 image 和 overlay2 目录即可。
OverlayFS 是一种堆叠文件系统,不会直接操作磁盘,而是建立在其它的文件系统之上,仅仅将原来底层文件系统中不同的目录进行合并。在 Linux 内核中包含了 overlay 和 overlay2 ,后者是一种改进,相比前者 inode 使用率更加高效。
Overlay2
Overlay 也是一种 Union FS,不过只有两层:A) Upper 文件系统;B) Lower 文件系统;分别代表 Docker 的镜像层和容器层,当需要修改一个文件时,使用 CoW 将文件从只读的 Lower 复制到可写的 Upper 进行修改,结果也保存在 Upper 层。
在挂载完成之后,可以通过 mount | grep overlay
查看其挂载的参数,如下是一个简单的示例。
mkdir -p /tmp/test/{lower1,lower2,upper,worker,merged}
echo "from lower 1" > /tmp/test/lower1/data.txt
echo "hello world" > /tmp/test/lower1/hi.txt
echo "from lower 2" > /tmp/test/lower2/data.txt
echo "from upper" > /tmp/test/upper/data.txt
echo "from worker" > /tmp/test/worker/data.txt
mount -t overlay overlay -o lowerdir=lower1:lower2,upperdir=upper,workdir=worker merged
cat /tmp/test/merged/hi.txt
cat /tmp/test/merged/data.txt
挂载时包含了三个目录:
- Lower 可以是多个,一般对应了磁盘镜像,通常只读;
- Upper 通常只有一个,作为容器运行操作目录;
- Worker 基础目录,挂在后会被清空,使用时对用户不可见。
最后会统一呈现的视图在 merged 目录。
镜像管理
所有镜像最终以 tar.gz
方式静态存储在服务器端,这种存储适用于对象存储而不是块存储。
常用镜像
Docker 提供了一个 Hello-World 镜像可以用来测试基本环境安装是否成功,另外,还要一些常用的较小镜像,例如 BusyBox(1M+)、Alpine Linux (使用 musl libc 和 busybox 减小体积,只有 5M+ ) 。
Alpine
一个面向安全的轻型 Linux 发行版,采用了 musl libc 和 busybox 以减小系统的体积和运行时资源消耗,但功能上比 busybox 又完善的多,通过 apk 管理包,也可以从 pkgs 上查看。
可以通过 docker run alpine echo '123'
或者 docker run -it alpine sh
简单运行。
私有镜像
默认的私有镜像通过可以通过 docker-registry
包进行安装,详细的安装步骤可以参考 Deploy a registry server 中的详细介绍。
----- 下载仓库镜像
# docker pull registry
----- 运行镜像,并将本机的/opt/docker/registry映射到容器中
# docker run -d -p 5000:5000 --restart=always --name=registry \
-v /opt/docker/registry:/var/lib/registry registry
----- 验证是否创建成功,正常应该返回{}
# curl http://127.0.0.1:5000/v2/
Docker 在与 Docker Registry 进行交互时默认使用 https,但是当前的镜像仓库仅提供了 http ,需要将本地仓库添加为不安全仓库。
# cat /etc/docker/daemon.json
{
"registry-mirrors": [
"https://registry.docker-cn.com"
],
"insecure-registries": [
"127.0.0.1:5000"
]
}
然后将本地的 busybox 添加到新搭建的仓库。
----- 查看当前的镜像
# docker images
----- 修改某个镜像的tag,例如busybox,并将其添加到本地
# docker tag busybox:latest 127.0.0.1:5000/busybox:latest
# docker push 127.0.0.1:5000/busybox:latest
然后调用 REST API 查看镜像信息。
----- 查询私有仓库镜像列表
# curl http://127.0.0.1:5000/v2/_catalog
----- 查询busybox镜像的标签列表
# curl http://127.0.0.1:5000/v2/busybox/tags/list
----- 从私有仓库拉取镜像
# docker pull 127.0.0.1:5000/busybox:latest
常见问题
容器已经在运行
详细报错为 The container name "XXX" is already in use by container
。
----- 查看所有的容器信息,并根据ID删除
# docker ps -a
# docker rm 36bceeae4b22
证书验证报错
详细报错为 Error response from daemon: Get "https://registry.exmaple.com/v2/": tls: failed to verify certificate: x509: certificate relies on legacy Common Name field, use SANs instead
。
从 Go 1.15
版本开始废弃 X.509 CommonName 推荐使用 SAN 证书,也可以将其添加到非安全的仓库配置中,此时将不再检查证书,常见有如下两种方式。
----- 通过systemd管理docker服务进程,可以通过如下方式增加启动选项
# vim /etc/sysconfig/docker
OPTIONS='--insecure-registry registry.example.com'
----- 或者修改如下的配置项
# vim /etc/docker/daemon.json
{
"insecure-registries" : ["registry.example.com"]
}
然后通过 systemctl restart docker
命令重启服务。
拉取镜像报错
详细报错为 Error response from daemon: manifest for your/image:latest not found: manifest unknown: manifest unknown
。
通常是因为没有这个镜像,多数原因是 tag 错误或者是默认没有设置 latest,可以从对应的 repository 中搜索或者,可以通过如下命令查看当前支持的 tag 信息。
$ curl -XGET http://hub-mirror.c.163.com/v2/your/image/tags/list
这里使用的是网易的镜像进行测试。
下载 ARM 镜像
有时需要在 x86 平台上下载 arm 镜像,例如,在 x86 机器上只是做下载,然后在其它平台上运行,可以通过如下方式修改。
依赖 manifest 特性,需要同时配置服务端和客户端。
----- 开启服务端配置,然后重启
# vim /etc/docker/daemon.json
{
"experimental": true
}
# systemctl restart docker
----- 配置客户端,可以设置环境变量或者修改配置文件
# export DOCKER_CLI_EXPERIMENTAL=enabled
# vim ~/.docker/config.json
{
"experimental": "enabled"
}
----- 下载时指定平台
# docker pull docker.io/hello-world --platform=linux/arm64
----- 查看某个镜像的当前架构信息
$ docker inspect 638e1285665f | grep -Ew 'Architecture|Os'