一、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、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐