一、Docker 的组成和定义

Docker 官网 : http://www.docker.com
帮助文档链接 : https://docs.docker.com/
Docker 镜像 : https://hub.docker.com/
Docker 中文网站 : http://www.docker.org.cn/

Docker 是一个开源的容器化平台,它允许开发者将应用程序及其依赖项打包到一个标准化的单元(称为 “容器”)中,从而确保应用在任何支持 Docker 的环境中都能以相同的方式运行。

简单来说,Docker 的核心思想是 “一次构建,到处运行”,解决了传统开发中 “在我电脑上能运行,到你电脑上就出错” 的环境一致性问题。

核心概念:

  1. 容器(Container):一个独立运行的应用单元,包含应用程序及其所有依赖(代码、运行时、库、环境变量等)。容器是轻量级的,因为它们共享主机的操作系统内核,不需要像虚拟机那样单独安装操作系统。

  2. 镜像(Image):容器的 “模板”,是一个只读的文件系统,包含运行应用所需的所有内容。可以把镜像理解为容器的源代码,容器是镜像的运行实例。

  3. Dockerfile:用于构建镜像的文本文件,包含一系列指令(如安装软件、配置环境等),通过 docker build 命令可生成镜像。

  4. 仓库(Repository):用于存储和分发 Docker 镜像的地方,类似代码仓库(如 GitHub),最常用的公共仓库是 Docker Hub。官方仓库: https://hub.docker.com/ 可搭建私有仓库harbor

  5. Docker 主机(Host):  一个物理机或虚拟机,用于运行 Docker 服务进程和容器,也称为宿主机, node节点
  6. Docker 服务端(Server):Docker守护进程,运行 docker 容器 docker engine
  7. Docker 客户端(Client): 客户端使用 docker 命令或其他工具调用 docker API

主要优势:

  • 环境一致:消除开发、测试、生产环境的差异,减少 “环境问题”。
  • 轻量高效:容器比虚拟机更节省资源,启动速度快(秒级)。
  • 隔离性:不同容器之间相互隔离,避免依赖冲突。
  • 可移植性:容器可以在任何支持 Docker 的平台(Linux、Windows、Mac 等)上运行。

常见用途:

  • 应用程序的打包和分发
  • 微服务架构的部署
  • 持续集成 / 持续部署(CI/CD)流程
  • 开发环境的快速搭建和隔离

例如,开发者可以在本地用 Docker 构建一个包含 Python 环境和特定库的容器,然后直接将这个容器部署到服务器,无需担心服务器上的环境配置问题。

1.Docker 优化配置

https://docs.docker.com/reference/cli/dockerd/#daemon-configuration-file
注意:这种方式只对新建的容器有效的,之前的容器不生效
范例 : 支持官方仓库和私有仓库镜像下载
就是一些加速镜像 略
范例:通过 ssh 协议远程连接
[root@ubuntu2204 ~] #ssh-keygen
[root@ubuntu2204 ~] #ssh-copy-id 10.0.0.100
[root@ubuntu2204 ~] #docker -H ssh://root@10.0.0.100 version
[root@ubuntu2204 ~] #ssh 10.0.0.100 docker images

2.容器镜像管理

[root@ubuntu1804 ~] #docker image history nginx      # 查看镜像分层历史
[root@ubuntu1804 ~] #docker inspect nginx   #获取 镜像的详细信息
[root@ubuntu1804 ~] #docker save nginx -o nginx.tar    #将 Docker 镜像保存为一个 tar 归档文件
[root@ubuntu1804 ~] #docker search centos
[root@ubuntu1804 ~] #docker search -s 100 centos    # 搜索点赞 100 个以上的镜像(旧)
[root@ubuntu1804 ~] #docker search --filter=stars=100 centos  # 搜索点赞 100个以上的镜像(新)

3.Alpine 介绍

Alpine 官网 : https://www.alpinelinux.org/
Alpine 官方仓库 : https://github.com/alpinelinux
Alpine 官方镜像 : https://hub.docker.com/_/alpine/
Alpine 官方镜像仓库 : https://github.com/gliderlabs/docker-alpine
Alpine 阿里云的镜像仓库 : https://mirrors.aliyun.com/alpine/
要理解  Alpine,首先需要明确它的核心身份 —— Alpine Linux,这是一款基于 Linux 内核的轻量级、安全优先的开源操作系统。
Alpine Docker 镜像也继承了 Alpine Linux 发行版的这些优势。相比于其他 Docker 镜像,它的容量非常小,仅仅只有 5 MB 左右(对比 Ubuntu 系列镜像接近 200 MB ),且拥有非常友好的包管理机制。 官方镜像来自 docker-alpine 项目。 在保持瘦身的同时, Alpine 还提供了自己的包管理工具 apk ,可以通过 ht tps://pkgs.alpinelinux.org/packages 网站上查询包信息,也可以直接通过 apk 命令直接查询和安装各种软件。
范例:alpine 管理软件
/ # cat /etc/apk/repositories
https://dl-cdn.alpinelinux.org/alpine/v3.15/main
https://dl-cdn.alpinelinux.org/alpine/v3.15/community
[root@ubuntu2204 ~] #sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/' /etc/apk/repositories
# 修改源替换成阿里源,将里面 dl-cdn.alpinelinux.org 的 改成 mirrors.aliyun.com
vi /etc/apk/repositories
https://mirrors.ustc.edu.cn/alpine/v3.16/main
https://mirrors.ustc.edu.cn/alpine/v3.16/community http://mirrors.aliyun.com/alpine/v3.8/main/
http://mirrors.aliyun.com/alpine/v3.8/community/
[root@ubuntu2204 ~] #sed -i 's/mirrors.aliyun.com/mirrors.ustc.edu.cn/' repositories
[root@ubuntu2204 ~] #docker cp repositories alpine-image-3.16.2:/etc/apk/repositories
将 宿主机当前目录下名为  repositories 的文件,复制到名为  alpine-image-3.16.2的容器内的  /etc/apk/repositories 路径。
# 更新源
apk update = apt update
# 安装软件
apk add vim = apt install vim
# 删除软件
apk del openssh openntp vim = apt remove openssh
# 安装常见软件
apk update && apk --no-cache add bash curl telnet   wget net-tools pstree zip
unzip
# 安装常用编译工具
apk update && apk --no-cache add gcc make curl zip unzip net-tools pstree wget
libgcc libc-dev libcurl libc-utils pcre-dev zlib-dev libnfs pcre pcre2 libevent
libevent-dev iproute2
# 修改时区
apk update && apk --no-cache add tzdata && ln -s  /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone
方式1:docker run -it --rm alpine:3.22.1  进入到容器里面进行操作
方式2:
docker run -it --name haha alpine:3.22.1  #创立一个名为haha的容器
docker exec -it haha /bin/sh    #进入到haha容器,(Alpine 用  /bin/sh,其他系统可能用  /bin/bash)
[root@ubuntu2204 ~] #docker images -f dangling=true   #查看dangling镜像  下面是出现此镜像的原因。
  • 镜像重建:当重新构建一个镜像时,Docker 会为新的镜像分配一个新的镜像 ID,而旧的镜像 ID 如果没有被其他标签引用,就会变成悬空镜像。例如:
    # dockerfile文件第一次构建
    docker build -t myimage:v1 .
    
    # 第二次构建,使用相同标签
    docker build -t myimage:v1 . 
    
    第一次构建的旧镜像失去标签,成为悬空镜像(<none>:<none>)。
  • 删除标签:如果手动删除了一个镜像的标签,且这个镜像没有被其他标签引用,那么它也会成为悬空镜像。例如:
    docker pull nginx:latest
    docker tag nginx:latest mynginx:temp  # 新增一个临时标签
    
    docker rmi mynginx:temp  # 仅删除标签,不删除镜像本身
    如果被删除的 mynginx:temp 是该镜像唯一的标签,原镜像会变成悬空镜像。
    

4.镜像导出

常见用法

#导出为tar格式
docker save -o /path/file.tar IMAGE1 IMAGE2 ...
docker save IMAGE1 IMAGE2 ... > /path/file.tar

#导出为压缩格式
docker save IMAGE1 IMAGE2 ... | gzip > /path/file.tar.gz
案例:导出所有镜像到一个打包文件
使用 image ID 导出镜像 , 在导入后的镜像没有 REPOSITORY 和 TAG, 显示为 <none>
[root@ubuntu1804 ~] #docker save `docker images -qa` -o all.tar
案例:导出所有镜像至不同的文件中
[root@centos8 ~] #docker images | awk 'NR!=1{print $1,$2}' | while read repo tag;do docker save   $repo:$tag -o /opt/$repo-$tag.tar ;done
范例:导出镜像并压缩
[root@ubuntu2204 ~] #docker save rockylinux:9.1-minimal | gzip - > rockylinux-9.1-minimal.tar.gz
范例 : 导出指定镜像
[root@ubuntu1804 ~] #docker save mysql:5.7.30 alpine:3.11.3 -o /data/myimages.tar
# 或者
[root@ubuntu1804 ~] #docker save mysql:5.7.30 alpine:3.11.3 > /data/myimages.tar

5.镜像导入

常见用法
docker load -i /path/file.tar
docker load < /path/file.tar.gz

6.删除镜像

格式
docker rmi [OPTIONS] IMAGE [IMAGE...]
docker image rm [OPTIONS] IMAGE [IMAGE...]
# 选项 :
-f , --force     Force removal of the image    
--no-prune   Do not delete untagged parents
下面两条命令达到的效果是一样的
docker image rm nginx:1.27
docker rmi nginx:1.27
[root@centos7 ~] #docker rmi -f alpine:3.11.3   #删除alpine:3.11.3镜像  -f 可强 删除正在使用的镜像
[root@ubuntu1804 ~] #docker rmi nginx tomcat  #删除多个镜像
[root@ubuntu2204 ~] #docker rmi -f `docker images -q -f dangling=true`   删除 dangling 状态的镜像
[root@ubuntu1804 ~] #docker image prune -a -f  # 清除 dangling 和不再使用的镜像

7. 镜像打标签

[root@ubuntu1804 ~]#docker tag alpine alpine:3.11  (之前存在alpine:latest镜像,重新打标签原标签文件不会被删除)

8. 拉取不可描述的镜像

利用下面网站做代理,可以实现拉取原本从国内可能无法直接访问的镜像

https://docker.aityp.com/
https://dockerproxy.com/
https://github.com/DaoCloud/public-image-mirror
https://github.com/kubesre/docker-registry-mirrors/
docker pull registry.k8s.io/ingress-nginx/controller:v1.7.1  #无法拉取成功
docker pull k8s.dockerproxy.com/ingress-nginx/controller:v1.7.1  #可拉取成功

9. 总结: 企业使用镜像及常见操作

搜索、下载、导出、导入、删除

docker search centos
docker pull alpine
docker images
docker save > /opt/centos.tar #centos # 导出镜像
docker load -i /opt/centos.tar   # 导入本地镜像
docker rmi 镜像 ID/ 镜像名称   # 删除指定 ID 的镜像,此镜像对应容器正启动镜像不能被删除,除非将容器全部关闭

二、容器的操作基础命令

1. docker container相关命令如下

范例 : 设置容器内的主机名  a1.wang.org
[root@ubuntu1804 ~] #docker run -it --name a1 -h a1.wang.org alpine
范例 : 一次性运行容器,退出后立即删除,用于测试
[root@ubuntu1804 ~] #docker run --rm alpine cat /etc/issue
Welcome to Alpine Linux 3 .11
Kernel \r on an \m (\l)
范例 : 创建容器后直接进入并退出
退出两种方式 : exit 容器也停止,按ctrl+p+q 容器不停止
[root@ubuntu1804 ~] #docker run -it --name alpine3 alpine
# 同时按 ctrl+p+q 三个键退出后 ,容器不停 止,exit 会停止。
范例 : 启动前台守护式容器
[root@ubuntu1804 ~] #docker run nginx
[root@ubuntu1804 ~] #docker run --rm --name b1 busybox wget -qO - 172.17.0.3
范例 : 启动后台守护式容器
[root@ubuntu1804 ~] #docker run -d nginx
范例 : 开机自动运行容器
[root@ubuntu1804 ~]
#docker run -d --name nginx -p 80:80 nginx   #reboot后也自动关闭容器
[root@ubuntu1804 ~] #docker run -d --name nginx --restart=always -p 80:80 nginx # 容器总是运行

2. --privileged 选项

大约在 0.6 版, --privileged 选项被引入 docker 。使用该参数, container 内的 root 拥有真正的 root 权限。 否则,container 内的 root 只是外部的一个普通用户权限。 privileged 启动的容器,可以看到很多 host 上的设备,并且可以执行mount 。甚至允许你在 docker 容器中启动 docker 容器。
范例:使用 --privileged 让容器获取 root 权限
[root@ubuntu2204 ~] #docker run --name pod-test -d xiaoming/pod-test:v0.1
[root@ubuntu2204 ~] #docker exec -it pod-test sh
[root@08cfefa34a87 /] # iptables -vnL   #提示权限不足
# 加 --privileged 选项再创建容器
[root@ubuntu2204 ~] #docker run --name pod-test --privileged -d wangxiaochun/pod-test:v0.1
范例 : 使用 --privileged 让容器获取 root 权限
[root@centos8 ~] #podman run -it --privileged   centos
[root@centos8 ~] #echo host data >> /data/containter.txt  #可以看到能正常创建文件

3.查看容器信息

显示当前存在容器:https://docs.docker.com/engine/reference/commandline/ps/
[root@ubuntu1804 ~] #docker ps   # 显示运行的容器
[root@ubuntu1804 ~] #docker ps -a  # 显示全部容器,包括退出状态的容器
[root@ubuntu1804 ~] #docker ps -a -q  # 只显示容器 ID
[root@ubuntu1804 ~] #docker ps -a -s  # 显示容器大小
[root@ubuntu1804 ~] #docker ps -l  # 显示最新创建的容器 ( 停止的容器也能显示 )
[root@ubuntu1804 ~] #docker ps -f 'status=exited'  # 查看退出状态的容器

4.查看容器内的进程

[root@ubuntu1804 ~] #docker run -d httpd
[root@ubuntu1804 ~] #docker top db144f19    #db144f19是httpd生成容器的ID

5.各种命令

容易搞混淆的命令

  • docker run 是新建容器的命令,无论是否已有同名镜像的容器,都会创建新实例。
  • docker exec 是进入已有容器的命令,用于复用已存在的容器。
[root@ubuntu1804 ~] #docker stats 251c7c7cf2aa   查看容器资源使用情况
#限制内存使用的大小
[root@ubuntu1804 ~] #docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx128m" elasticsearch:7.6.2 
docker inspect 可以查看 docker 各种对象的详细信息 , 包括 : 镜像 , 容器 , 网络等
[root@ubuntu1804 ~] #docker inspect 9997  
[root@ubuntu1804 ~] #docker inspect -f "{{.Metadata}}" test:v1.0 # 选择性查看,最新版本不支持
查看容器的日志
[root@ubuntu1804 ~] #docker run -d --name alpine01 alpine /bin/sh -c 'i=1;while
true;do echo hello$i;let i++;sleep 2;done'
512622b006c05673630eb04f081f8475400b1cda786b0a8a5d1c1c2fd6dc56a7
[root@ubuntu1804 ~] #docker logs 5126
[root@ubuntu1804 ~] #docker logs --tail 3 5126   #查看倒数三行的日志
[root@ubuntu1804 ~] #docker logs -f 5126  #持续跟踪
[root@ubuntu1804 ~] #docker logs --tail 0 -t 5126  #显示时间
容器的启动和停止   批量正常启动或关闭所有容器
docker start $(docker ps -a -q)  
docker stop $(docker ps -a -q)
[root@ubuntu1804 ~] #docker kill `docker ps -a -q`    # 强制关闭所有运行中的容器
进入正在运行的容器
格式: docker attach [OPTIONS] CONTAINER    
[root@ubuntu1804 ~] #docker run -it centos
[root@94a5c5c69b14 /] # cat /etc/redhat-release
CentOS Linux release 8 .1.1911 (Core) #ctrl+p+q 退出(exit的话容器也会停止运行
)
[root@94a5c5c69b14 /] # [root@ubuntu1804 ~]#docker ps   可以看到centos对应的容器在运行
[root@ubuntu1804 ~] #docker attach 94a5    #进入容器
[root@94a5c5c69b14 /] #cat /etc/redhat-release
使用 exec 命令
测试环境使用此方式,使用 exit 退出 , 但容器还在运行, 此为推荐方式
[root@ubuntu1804 ~] #docker exec 2478 cat /etc/redhat-release  # 执行一次性命令
[root@ubuntu1804 ~] #docker exec -it 2478 bash  # 进入容器,执行命令, exit 退出但容器不停止
传递环境变量
范例:指定环境变量给 wordpress 和 MySQL
[root@ubuntu2404 ~] #docker run -d -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 -d --restart=always registry.cn-beijing.aliyuncs.com/xiaoming/mysql:8.0.29-oracle
[root@ubuntu2404 ~] #docker run -d -p 80:80 --name wordpress -e WORDPRESS_DB_HOST=10.0.0.200 -e WORDPRESS_DB_USER=wordpress -e
WORDPRESS_DB_PASSWORD=123456 -e WORDPRESS_DB_NAME=wordpress --restart=always registry.cn-beijing.aliyuncs.com/xiaoming/wordpress:php8.2-apache
[root@ubuntu2404 ~] #docker exec -it mysql sh   # 确认变量生效
暴露所有容器端口
docker port 可以查看容器的端口映射关系
格式:docker port CONTAINER [PRIVATE_PORT[/PROTO]]
[root@centos7 ~] #docker run -P nginx     # 前台启动的会话窗口无法进行其他操作,除非退出,但是退出后容器也会退出
端口映射的本质就是利用 NAT 技术实现的
范例 : 端口映射和 iptables
# 端口映射前的 iptables 规则
[root@ubuntu1804 ~] #iptables -S
[root@ubuntu1804 ~] #iptables -S -t nat
[root@ubuntu1804 ~] #iptables -S > pre.filter
[root@ubuntu1804 ~] #iptables -S -t nat > pre.nat
# 实现端口映射
[root@ubuntu1804 ~] #docker run -d -P --name nginx1 nginx
[root@ubuntu1804 ~] #docker exec -it nginx1 hostname -i
[root@ubuntu1804 ~] #docker port nginx1
# 对比端口映射前后的变化
[root@ubuntu1804 ~] #iptables -S > post.filter
[root@ubuntu1804 ~] #iptables -S -t nat > post.nat
[root@ubuntu1804 ~] #diff pre.filter post.filter
[root@ubuntu1804 ~] #diff pre.nat post.nat
指定端口映射
docker run -p 可以将容器的预定义的指定端口映射到宿主机的相应端口
docker run   -p 81 :80 --name nginx-test-port2 nginx   容器 80 端口映射到宿主机本地端口 81 其他略
传递运行命令
容器需要有一个前台运行的进程才能保持容器的运行,可以在构建镜像的时候指定容器启动时运行的前台命令,也可以通过启动容器时传递运行参数实现
容器里的 PID 为 1 的守护进程的实现方式
服务类 : 如 : Nginx , Tomcat , Apache ,但服务不能停
命令类 : 如 : tail -f /etc/hosts ,主要用于测试环境,注意 : 不要 tail -f < 服务访问日志 > 会产生不
必要的磁盘 IO
[root@ubuntu1804 ~] #docker run -d alpine          #docker ps 可以看到该容器没运行
[root@ubuntu1804 ~] #docker run -d alpine tail -f /etc/hosts  #docker ps 可以看到该容器在运行
容器内部的host文件
[root@ubuntu1804 ~] #docker run -it centos /bin/bash
[root@598262a87c46 /] # cat /etc/hosts
范例 : 修改容器的 hosts 文件
[root@ubuntu1804 ~] #docker run -it --rm --add-host www.xiaoming.com:6.6.6.6
--add-host www.wang.org:8.8.8.8   busybox
例如下图
指定容器 DNS
容器内和宿主机之间复制文件
范例: 复制容器的文件至宿主机
[root@ubuntu2004 ~] #docker run -it --name b1 busybox sh
[root@ubuntu2004 ~] #docker cp b1:/bin/busybox /usr/local/bin/
同理:复制宿主机文件到容器
[root@ubuntu2004 ~] #docker cp /usr/local/bin/ b1:/bin/busybox
[root@ubuntu1804 ~] #docker system prune     清除不再使用的数据
[root@ubuntu1804 ~] #docker system prune -f -a   # 清除不再使用的镜像
导出和导出容器文件系统
docker export 是用来导出容器的文件系统,而 docker save 是用来导出镜像的全部内容。
例如:

三、实战案例

1.实战案例: 利用 Docker 快速部署自动化运维平台

[root@ubuntu1804 ~] # docker pull registry.aliyuncs.com/openspug/spug  #拉取镜像
[root@ubuntu1804 ~] # docker pull openspug/spug-service  #官方镜像,不推荐 无法重置用户密码
[root@ubuntu1804 ~] #docker run -d --restart=always --name=spug -p 80:80 registry.aliyuncs.com/openspug/spug
或者可选
# 持久化存储启动命令:
# mydata 指的是本地磁盘路径,也可以是其他目录,但需要保证映射的本地磁盘路径已经存在, /data 是容器内代码和数据初始化存储的路径
$ docker run -d --restart = always --name = spug -p 80 :80 -v /mydata/:/data registry.aliyuncs.com/openspug/spug
以下操作会创建一个用户名为 admin 密码为 123456 的管理员账户,可自行替换管理员账户。
[root@ubuntu1804 ~] #docker exec spug init_spug admin 123456
在浏览器中输入 http://宿主机ip地址:80 访问输入账号密码得到下图:

2.实战案例:两个容器运行Wordpress博客系统

#方法1
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -e
MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 
registry.cn-beijing.aliyuncs.com/wangxiaochun/mysql:8.0.29-oracle
#连接数据库的信息通过环境变量指定,MySQL容器重启IP可能会发生变化
docker run -d --name wordpress -p 80:80 -e WORDPRESS_DB_HOST=`docker inspect 
mysql --format "{{.NetworkSettings.Networks.bridge.IPAddress}}"` -e
WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=123456 -e
WORDPRESS_DB_NAME=wordpress registry.cn-beijing.aliyuncs.com/wangxiaochun/wordpress:php8.2-apache

#方法2:需要初始化人为输入连接数据库信息
docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e
MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 
registry.cn-beijing.aliyuncs.com/wangxiaochun/mysql:8.0.29-oracle
#连接数据库的主机:宿主机的IP
docker run -d -p 80:80 --name wordpress -e WORDPRESS_DB_HOST=<宿主机的IP>  -e
WORDPRESS_DB_USER=wordpress -e WORDPRESS_DB_PASSWORD=123456 -e
WORDPRESS_DB_NAME=wordpress registry.cn-beijing.aliyuncs.com/wangxiaochun/wordpress:php8.2-apache
docker run -d --name wordpress -p 80:80 registry.cn-beijing.aliyuncs.com/wangxiaochun/wordpress:php8.2-apache

查看 docker run 启动参数命令
忘记之前启动一个容器的启动命令是什么,现在需要找回来 docker run 的运行参数,可以使用 runlike工具实现  https://github.com/lavie/runlike
安装runlike
# 安装方式 1: pip
apt install -y python3-pip
pip3 install runlike
# 安装方法 2: by docker
alias runlike = "docker run --rm -v /var/run/docker.sock:/var/run/docker.sock
assaflavie/runlike"
# 包安装
[root@ubuntu2404 ~] #apt install -y python3-pip
[root@ubuntu2404 ~] #pip3 install runlike --break-system-packages
[root@ubuntu2204 ~] #apt install -y python3-pip
[root@ubuntu2204 ~] #pip3 install runlike
启动一个测试容器并验证

四、基于CRIU实现容器热迁移(了解)

容器热迁移 https://docs.docker.com/reference/cli/docker/checkpoint/
容器热迁移案例
1.  环境准备
安装 criu
安装 docker
配置 docker 支持 CRIU
#Ubuntu24.04 默认没有 criu 包 , 需要添加安装源
[root@ubuntu2404 ~] #add-apt-repository ppa:criu/ppa -y && apt update && apt -y install criu docker.io
[root@ubuntu2404 ~] #criu check
[root@ubuntu2404 ~] #criu -V
[root@ubuntu2404 ~] #criu check     显示: Looks good.  再执行
[root@ubuntu2404 ~] #criu -V
[root@ubuntu2404 ~] #docker checkpoint   # 默认没有开启 checkpoint 的实验性功能
# 启动 CRIU 功能(需要配置 Docker 守护进程启用实验性特性)
cat > /etc/docker/daemon.json <<EOF
{
    "registry-mirrors": [
        "https://docker.1ms.run",
        "https://docker.xuanyuan.me"
    ],
    "experimental": true
}
EOF

# 重启 Docker 服务使配置生效
systemctl restart docker

# 验证 checkpoint 功能(查看命令帮助确认是否启用成功)
docker checkpoint
启动运行一个需要热迁移的容器
# 创建测试容器
[root@rocky9 ~] #docker run --restart always --name cr -d registry.cn
beijing.aliyuncs.com/wangxiaochun/busybox:1.32.0 /bin/sh -c 'i=0; while true; do
echo $i `date +%F_%T` ; i=$(expr $i + 1); sleep 1; done'
[root@rocky9 ~] #docker logs -f cr
0 2025 -06-25_02 :38:10
1 2025 -06-25_02 :38:11
2 2025 -06-25_02 :38:12
3 2025 -06-25_02 :38:13
4 2025 -06-25_02 :38:14
5 2025 -06-25_02 :38:15
6 2025 -06-25_02 :38:16
# 方法 1:checkpoint 数据指定生成文件所在目录
[root@ubuntu2404 ~] #docker checkpoint create --checkpoint-dir /srv/ cr checkpoint1
# 创建 checkpoint 后 , 容器会自动退出
[root@ubuntu2404 ~] #docker ps -l
[root@ubuntu2404 ~] #docker checkpoint ls cr --checkpoint-dir /srv/
还原
[root@ubuntu2404 ~] #docker create --restart always --name cr registry.cn
beijing.aliyuncs.com/wangxiaochun/busybox:1.32.0 /bin/sh -c 'i=0; while true; do
echo $i `date +%F_%T` ; i=$(expr $i + 1); sleep 1; done'
[root@ubuntu2404 ~] #docker ps -l
# 当前没有 checkpoint
[root@ubuntu2404 ~] #docker checkpoint ls cr
# 查看容器 ID
[root@ubuntu2404 ~] #docker inspect --format "{{.ID}}" cr
# 查看当前容器目录为空
[root@ubuntu2404 ~] #ls
/var/lib/docker/containers/e765a315dfd4b11b2ce9affd4cf80263054a3de2b4baaac1492ffa5c378a5a84/checkpoints/
# 将第一台主机的 checkpoint 文件复制到需要恢复的目标主机相应的目录下
[root@ubuntu2404 ~] #cd
/var/lib/docker/containers/e765a315dfd4b11b2ce9affd4cf80263054a3de2b4baaac1492ff
a5c378a5a84/checkpoints/
[root@ubuntu2404 checkpoints] #rsync -av 10.0.0.101:/srv/checkpoint1/ .
# 目标主机上确认文件复制完成
[root@ubuntu2404 ~] #ls
/var/lib/docker/containers/e765a315dfd4b11b2ce9affd4cf80263054a3de2b4baaac1492ff
a5c378a5a84/checkpoints/
checkpoint1
# 查看 checkpoint 出现
[root@ubuntu2404 ~] #docker checkpoint ls cr
CHECKPOINT NAME
checkpoint1
# 恢复容器
[root@ubuntu2404 ~] #docker start --checkpoint checkpoint1 cr
# 观察日志说明容器是接着上前面的继续执行
[root@ubuntu2404 ~] #docker logs -f cr

五、Docker镜像的制作

1.实战案例: 基于 busybox 制作 httpd 镜像

[root@ubuntu1804 ~] #docker run -it --name b1 busybox
/ # mkdir /data/html -p
/ # echo httpd website in busybox > /data/html/index.html
/ # httpd --help
/ # exit
# 格式 1  (推荐,易懂)
[root@ubuntu1804 ~] #docker commit -a "wang<root@lili.com>" -c 'CMD /bin/httpd -fv -h /data/html' -c "EXPOSE 80" b1 httpd-busybox:v1.0
# 格式 2
[root@ubuntu1804 ~] #docker commit -a "wang<root@lili.com>" -c 'CMD ["/bin/httpd", "-f", "-v","-h", "/data/html"]' -c "EXPOSE 80" b1 httpd-busybox:v1.0
[root@ubuntu1804 ~] #docker run -d -P --name httpd01 httpd-busybox:v1.0
[root@ubuntu1804 ~] #docker port httpd01
[root@ubuntu1804 ~] #docker inspect -f " {{.NetworkSettings.Networks.bridge.IPAddress}}" httpd01
# 对应格式 1
[root@ubuntu1804 ~] #docker inspect -f "{{.Config.Cmd}}" httpd01
[/bin/sh -c /bin/httpd -f -h /data/html]
# 对应格式 2
[root@ubuntu1804 ~] #docker inspect -f "{{.Config.Cmd}}" httpd01
[/bin/httpd -f -h /data/html]
# 再次制作镜像 v2.0 版
[root@ubuntu1804 ~] #docker commit -a "wang<root@wangxiaochun.com>" b1 httpd
busybox:v2.0

2.实战案例: 基于官方镜像生成的容器制作 tomcat 镜像

[root@ubuntu1804 ~] #docker run -d -p 8080:8080 tomcat
#修改容器
[root@ubuntu1804 ~] #docker exec -it 6fcc5f7c1c7c bash
root@6fcc5f7c1c7c:/usr/local/tomcat # cp -a webapps.dist/* webapps/
root@6fcc5f7c1c7c:/usr/local/tomcat # exit
[root@ubuntu1804 ~] #curl -I 127.0.0.1:8080
提交新镜像
[root@ubuntu1804 ~] #docker commit -m "add webapps app" -a "xiaoming" 6fcc5f7c1c7c tomcat:9.0.37-v1
[root@ubuntu1804 ~] #docker history tomcat:9.0.37-v1
[root@ubuntu1804 ~] #docker inspect tomcat:9.0.37-v1 |tail -n20    查看镜像的详细元数据信息

3.实战案例: 基于Ubuntu的基础镜像用 apt 安装手动制作 nginx 的镜像

[root@ubuntu1804 ~] #docker run -it -p 80 --name nginx_ubuntu ubuntu bash
# 修改仓库源
# 方法 1
root@705148273eac:/ # sed -i 's/cn.archive.ubuntu.com/mirrors.ustc.edu.cn/' /etc/apt/sources.list
# 方法 2
root@705148273eac:/ # cat > /etc/apt/sources.list
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe 
multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe 
multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe 
multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe 
multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe 
multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe 
multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe 
multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe 
multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted 
universe multiverse
root@705148273eac:/ # apt update
root@705148273eac:/ # apt -y install nginx
root@705148273eac:/ # echo Nginx Website in Docker > /var/www/html/index.html
root@705148273eac:/ # exit
提交为镜像
[root@ubuntu1804 ~] #docker commit -a 'wangxiaochun' -m 'nginx-ubuntu:20.04' nginx_ubuntu nginx_ubuntu20.04:v1.18.0
从制作的新镜像启动容器并测试访问
[root@ubuntu1804 ~] #docker run -d -p 80 --name nginx-web nginx_ubuntu20.04:v1.18.0 nginx -g 'daemon off;'
[root@ubuntu1804 ~] #curl http://127.0.0.1:32771
Nginx Website in Docker

4.范例: 基于 alpine 定制新的镜像(练习)

[root@ubuntu2404 ~] #docker run -it --name test01 registry.cn-beijing.aliyuncs.com/xiaoming/alpine:3.20.0 sh
/# sed -i 's@dl-cdn.alpinelinux.org@mirrors.tuna.tsinghua.edu.cn@' /etc/apk/repositories  更新源
/# apk update && apk --no-cache add tzdata curl && ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo "Asia/Shanghai" > /etc/timezone  更改成本地时间
[root@ubuntu2404 ~] #docker commit test01 alpine:3.20.0-`date +%F`   # 手动制作镜像
[root@ubuntu2404 ~] #docker run -it --name test02 alpine:3.20.0-xxxxxx sh # 基于新镜像启动容器
/ # curl --version   #可以看到有了curl命令
/ # date  #时间也是正常的

6.利用 DockerFile 文件执行 docker build 自动构建镜像

Dockerfile 使用详解
dockerfile 官方说明 : https://docs.docker.com/engine/reference/builder/
Dockerfile 相关指令
https://docs.docker.com/reference/dockerfile/
dockerfile 文件中的有 18 个指令 , 但最常见指令:
FROM
LABEL
RUN
ADD
COPY
CMD
ENTRYPOINT
ENV
EXPOSE
USER
VOLUME
WORKDIR
简单记忆理解如下图
之前有写过dockerfile文件的命令和实践,可参看网页
案例:基于 Openjdk 的微服务的 JAVA 应用镜像
https://gitee.com/lbtooth/RuoYiCloud/blob/master/docker/ruoyi/gateway/dockerfile

# 基础镜像
FROM openjdk:8-jre
# author
MAINTAINER ruoyi
# 挂载目录
VOLUME /home/ruoyi
# 创建目录
RUN mkdir -p /home/ruoyi
# 指定路径
WORKDIR /home/ruoyi
# 复制jar文件到路径
COPY ./jar/ruoyi-gateway.jar /home/ruoyi/ruoyi-gateway.jar
# 启动网关服务
ENTRYPOINT ["java","-jar","ruoyi-gateway.jar"]
多阶段构建:https://docs.docker.com/build/building/multi-stage/
范例1:Java应用多阶段构建
# 第一阶段:构建阶段
FROM maven:3.8.6 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src/ /app/src/
RUN mvn package
 
# 第二阶段:运行阶段
FROM openjdk:17-jdk-slim
COPY --from=build /app/target/myapp.jar /app/myapp.jar
ENTRYPOINT ["java", "-jar", "/app/myapp.jar"]

范例2:前端应用多阶段构建

# 第一阶段:构建阶段
FROM node:16 AS build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
 
# 第二阶段:运行阶段
FROM nginx:alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Docker多阶段构建是现代容器化应用开发的重要技术,它通过分离构建环境和运行环境,带来了诸多优势:
显著减小镜像体积:只包含运行所需的文件
增强安全性:减少攻击面,避免敏感信息泄露
简化构建流程:单个Dockerfile管理复杂构建过程
提高可维护性:清晰的阶段划分使构建逻辑更直观

六、容器数据持久保存方式(重点)

Docker 提供了多种方法将宿主机的文件系统挂载到容器内,包括以下几种主要方式:
绑定挂载(Bind Mount)
这种方式可以将指定的宿主机上的任意文件或目录挂载到容器内。与卷不同,绑定挂载依赖于宿主
机的文件系统结构。
因此,如果你在 Docker CLI 或 Docker API 中使用绑定挂载,你需要知道宿主机上的文件或目录的
具体路径。 另外,由于绑定挂载可以访问宿主机上的任意文件和目录,使用这种方式可能会有一定的安全风险。
卷(Volume)
这是 Docker 推荐的挂载方式。卷是完全由 Docker 管理的文件目录,可以在容器之间共享和重
用。在创建卷时, Docker 创建了一个目录在宿主机上,然后将这个目录挂载到容器内。卷的主要
优点是你可以使用 Docker CLI 或 Docker API 来备份、迁移或者恢复卷,而无需关心卷在宿主机上
的具体位置。 卷分为匿名卷和命名卷。
tmpfs 挂载:
此方式并不算一种持久化方案。
tmpfs 挂载不与宿主机上的任何文件或目录相关联,而是将一个内存空间的临时文件系统挂载到容
器的某个目录下。
这种方式的主要优点是它提供了一个高速且安全的挂载方式,因为 tmpfs 挂载通常驻留在宿主机的
内存中,且在容器停止后会被自动删除。
tmpfs 挂载是临时的,只存留在容器宿主机的内存中。当容器停止时, tmpfs 挂载文件路径将被删
除,在那里写入的文件不会被持久化。
范例:tmpfs 挂载  (重启主机后挂载到/data里面的内容会消失)
docker run -d --tmpfs /data --name test registry.cn-beijing.aliyuncs.com/xiaoming/pod-test:v0.1
Docker 的 卷( Volume ) 类型分为两种 :
数据卷(Data Volume): 直接将宿主机目录挂载至容器的指定的目录 ,推荐使用此种方式,此方式
较常用。
数据卷容器(Data Volume Container): 间接使用宿主机空间,数据卷容器是将宿主机的目录挂载至一个专门的数据卷容器,然后让其他容器通过数据卷容器读写宿主机的数据 ,此方式不常用。
数据卷 (data volume)
数据卷分类(重点)
数据卷使用方法
docker run 命令的以下格式可以实现数据卷
-v, --volume=[host-src:]container-dest[:<options>]

<options>
ro 从容器内对此数据卷是只读,不写此项默认为可读可写
rw 从容器内对此数据卷可读可写,此为默认值
host-src 宿主机目录如果不存在,会自动创建
container-dest 容器目录如果不存在,会自动创建
方式 1: 绑定挂载( Bind Mount)
# 指定宿主机目录或文件格式 :
-v < 宿主机绝对路径的目录(文件)或者相对路径的文件 >:< 容器目录或文件 >[:ro]   # 将宿主机目录挂载容器目录,两个目录都可自动创建
# 注意:如果初始容器中有旧数据,将被宿主机目录覆盖
# 注意 : 如果挂载文件 , 必须指定文件的相对(以 . 开头的相对路径)或绝对路径 , 否则会将目录名或文件名当成命名卷


方式2: 匿名卷

# 匿名卷 , 只指定容器内路径 , 没有指定宿主机路径信息 , 宿主机自动生成 /var/lib/docker/volumes/< 卷
ID>/_data 目录 , 并挂载至容器指定路径
# 注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录
-v < 容器内路径 >
# 示例 :
docker run --name nginx -v /etc/nginx nginx
方式 3: 命名卷
# 命名卷将固定的存放在 /var/lib/docker/volumes/< 卷名 >/_data
# 注意:如果初始容器中有旧数据,将被复制到宿主机数据卷目录
-v < 卷名 >:< 容器目录路径 >
# 可以通过以下命令事先创建 , 如可没有事先创建卷名 ,docker run 时也会自动创建卷
docker volume create < 卷名 >
# 示例 :
docker volume create vol1   # 也可以事先不创建
docker run -d   -p 80 :80 --name nginx01 -v vol1:/usr/share/nginx/html nginx

1.实战案例: 目录数据卷

[root@ubuntu1804 ~] #mkdir /data/testdir
[root@ubuntu1804 ~] #echo Test page on host > /data/testdir/index.html
[root@ubuntu1804 ~] #docker run -it --rm nginx-alpine:1.16.1 sh
/ # cat /data/nginx/html/index.html     #nginx 网页存放路径
引用同一个数据卷目录,开启多个容器,实现多个容器共享数据
[root@ubuntu1804 ~] #docker run -d -v /data/testdir:/data/nginx/html/   -p 8001:80 nginx-alpine:1.16.1
[root@ubuntu1804 ~] #docker run -d -v /data/testdir:/data/nginx/html/   -p 8002:80 nginx-alpine:1.16.1
[root@ubuntu1804 ~] #echo Test page v3 on host > /data/testdir/index.html  #在宿主机上修改数据
[root@ubuntu1804 ~] #curl 127.0.0.1:8002     #可以看到容器里面的内容进行了更改
Test page v3 on host  
只读方法挂载数据卷
默认数据卷为可读可写,加 ro 选项,可以实现只读挂载,对于不希望容器修改的数据,比如 : 配置文件,脚本等,可以用此方式挂载
[root@ubuntu1804 ~] #docker run -d -v /data/testdir:/data/nginx/html/:ro   -p 8004:80 nginx-alpine:1.16.1
删除容器
删除容器后,宿主机的数据卷还存在,可继续给新的容器使用

2.实战案例: MySQL使用的数据卷

root@ubuntu1804 ~] #docker pull registry.cn-beijing.aliyuncs.com/xiaoming/mysql:8.0.29-oracle
[root@ubuntu1804 ~] #mkdir /data/mysql
[root@ubuntu1804 ~] #docker run -d --name mysql -p 3306:3306 -v /data/mysql/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 registry.cn-beijing.aliyuncs.com/xiaoming/mysql:8.0.29-oracle
[root@ubuntu1804 ~] #mysql -uroot -p123456 -h127.0.0.1
[root@ubuntu1804 ~] #docker rm -fv mysql  # 删除容器,数据存放在挂载数据卷中,不会删除
# 重新创建新容器,之前数据还在
[root@ubuntu1804 ~] #docker run -d --name mysql -p 3306:3306 -v /data/mysql/:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.30

3.实战案例: 文件数据卷

[root@ubuntu1804 ~] #mkdir /data/{bin,testapp,logs}
[root@ubuntu1804 ~] #echo testapp v1 > /data/testapp/index.html
同时挂载可读可写方式的目录数据卷和只读方式的文件数据卷,实现三个数据卷的挂载,数据,日志和启动脚本(关于只读方式是:容器里面修改不了,宿主机文件可以修改)
[root@ubuntu1804 ~] #docker run -d -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v
/data/testapp:/data/tomcat/webapps/testapp   -v /data/logs:/apps/tomcat/logs -p 8080:8080 tomcat-web:app1

4.实战案例: 匿名数据卷

#利用匿名数据卷创建容器   -v 后面第一个路径(/usr/share/nginx/html)是容器内的路径 
[root@ubuntu1804 ~] #docker run -d -p 80:80 --name nginx01 -v /usr/share/nginx/html nginx
[root@ubuntu1804 ~] #docker volume ls  #假设得到2a2b的卷名
[root@ubuntu1804 ~] #docker volume inspect 2a2b     # 查看匿名数据卷的详细信息
"Mountpoint" :  后面的文件夹就是匿名卷的位置
删除容器不会删除匿名卷

5.实战案例: 命名数据卷

[root@ubuntu1804 ~] #docker volume create vol1
[root@ubuntu1804 ~] #docker run -d -p 8001:80 --name nginx01 -v vol1:/usr/share/nginx/html nginx
[root@ubuntu1804 ~] #docker inspect vol1  # 查看命名数据卷详解信息
"Mountpoint" : "/var/lib/docker/volumes/vol1/_data" ,
[root@ubuntu1804 ~] #ls  /var/lib/docker/volumes/vol1/_data   #命名卷的位置
创建容器时自动创建命名数据卷
[root@ubuntu1804 ~] #docker run -d -p 8003:80 --name nginx03 -v vol2:/usr/share/nginx/html nginx
[root@ubuntu1804 ~] #docker volume rm vol1     # 删除指定的命名数据卷
[root@ubuntu1804 ~] #docker volume prune -f     # 清理全部不再使用的卷

6.实战案例:实现 wordpress 持久化(练习)

[root@ubuntu2004 ~] #docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e
MYSQL_DATABASE=wordpress -e MYSQL_USER=wordpress -e MYSQL_PASSWORD=123456 --name mysql -d -v /data/mysql:/var/lib/mysql --restart=always registry.cn-beijing.aliyuncs.com/xiaoming/mysql:8.0.29-oracle
[root@ubuntu2004 ~] #docker run -d -p 80:80 --name wordpress -v /data/wordpress:/var/www/html --restart=always registry.cn-beijing.aliyuncs.com/wangxiaochun/wordpress:php8.2-apache
# 注意:数据库服务器的地址要输入宿主机的 IP ,不能是 127.0.0.1 或 localhost

七、数据卷容器(了解)

1.实战案例: 数据卷容器

先创建一个挂载宿主机的数据目录的容器,且可以无需启动
范例: 使用之前的镜像创建数据卷容器
# 数据卷容器一般无需映射端口
[root@ubuntu1804 ~] #docker run -d --name volume-server -v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro -v /data/testapp:/data/tomcat/webapps/testapp   tomcat-web:app1
启动多个数据卷容器 Client
[root@ubuntu1804 ~] #docker run -d --name client1 --volumes-from volume-server -p
8081:8080 tomcat-web:app1
[root@ubuntu1804 ~] #docker run -d --name client2 --volumes-from volume-server -p
8082:8080 tomcat-web:app1
进入容器测试读写    读写权限依赖于源数据卷 Server 容器
[root@ubuntu1804 ~]
#docker exec -it volume-server bash
[root@109a34c5a83b /] # cat /data/tomcat/webapps/testapp/index.html  假定结果是:testapp v3
[root@109a34c5a83b /] # echo testapp v4 > /data/tomcat/webapps/testapp/index.html
[root@ubuntu1804 ~] #curl 127.0.0.1:8081/testapp/   结果是:testapp v4
关闭卷容器 Server ,仍然可以创建新的 client 容器及访问旧的 client 容器
[root@ubuntu1804 ~]
#docker stop volume-server   
[root@ubuntu1804 ~] #docker run -d --name client3 --volumes-from volume-server -p
8083:8080 tomcat-web:app1
删除数据卷容器后,旧的 client 容器仍能访问,但无法再创建新的 client 容器
删除数据卷容器后,旧的 client 容器仍能访问,但无法再创建基于数据卷容器的新的 client 容器 , 但可以 创建基于已创建的 Client 容器的 Client 容器
[root@ubuntu1804 ~] #docker rm -fv volume-server
[root@ubuntu1804 ~] #docker run -d --name client4 --volumes-from volume-server -p
8084:8080 tomcat-web:app1         #出现下面的报错

八、Docker 网络管理

太卡了接下一页 
Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐