Skip to content

Docker 学习指南 ​

导航目录 ​

一、Docker 初体验 ​

1.1 Docker 简介 ​

问题:为什么会有 Docker 出现?

一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发+运维之间的协作,我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的迭代之后,不同版本环境的兼容,对运维人员都是考验。这个时候 Docker 横空出世,是因为它对此给出了一个标准化的解决方案。

环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。开发人员利用 Docker 可以消除协作编码时"在我的机器上可正常工作"的问题。

  • Docker 官网地址:https://www.docker.com/

  • Docker 是一个开源的应用容器引擎,基于 Go 语言开发。Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

  • Docker 的主要目标是"Build,Ship and Run Any App,Anywhere",也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的 APP(可以是一个 WEB 应用或数据库应用等等)及其运行环境能够做到"一次封装,到处运行"。

  • 总之一句话:只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。

1.2 Docker 的优势 ​

Docker 的优势包括:

  1. 可移植性:Docker 容器可以在任何支持 Docker 的环境中运行,包括本地开发环境、测试环境和生产环境,从而提高了应用程序的可移植性。

  2. 可伸缩性:Docker 容器可以根据负载的变化进行快速扩展和收缩,从而更好地满足应用程序的需求。

  3. 隔离性:Docker 容器提供了隔离的运行环境,从而使得不同容器中运行的应用程序互相隔离,避免了应用程序之间的干扰。

  4. 轻量级:Docker 容器共享宿主机的操作系统内核,不需要模拟整个操作系统,因此比虚拟机更加轻量级,启动速度更快。

  5. 版本控制:Docker 镜像可以进行版本控制,方便回滚和管理。

1.3 Docker 和虚拟机的区别 ​

隔离性:在于隔离性上面,由于 VM 对操作系统也进行了虚拟化,隔离的更加彻底。而 Docker 共享宿主机的操作系统,隔离性较差。

运行效率:由于 VM 的隔离操作,导致生成虚拟机的速率大大低于容器 Docker 生成的速度,因为 Docker 直接利用宿主机的系统内核。它们的启动速度是在数量级上的差距。

资源利用率:在资源利用率上虚拟机由于隔离更彻底,因此利用率也会相对较低。

对比表格:

特性Docker 容器虚拟机(VM)
启动速度秒级分钟级
硬盘占用MB 级别GB 级别
性能接近原生有损耗
系统支持量单机支持上千个容器单机支持几十个虚拟机
隔离性进程级别隔离操作系统级别隔离
操作系统共享宿主机内核独立内核

经典名句:虚拟机已死,容器才是未来

1.4 Docker 架构 ​

  • Docker 是一个客户端-服务器(C/S)架构程序。Docker 客户端只需要向 Docker 服务器或者守护进程发出请求,服务器或者守护进程将完成所有工作并返回结果。Docker 提供了一个命令行工具 docker 以及一整套 RESTful API。你可以在同一台宿主机上运行 Docker 守护进程和客户端,也可以从本地的 Docker 客户端连接到运行在另一台宿主机上的远程 Docker 守护进程。

Docker 核心组件:

  1. Docker Client(客户端):Docker 客户端是用户与 Docker 交互的主要方式,通过命令行或 API 发送请求给 Docker 守护进程。

  2. Docker Daemon(守护进程):Docker 守护进程是 Docker 的服务器端组件,负责构建、运行和分发 Docker 容器。

  3. Docker Image(镜像):Docker 镜像是一个只读模板,包含创建 Docker 容器的指令。

  4. Docker Container(容器):Docker 容器是镜像的运行实例,可以被启动、停止、删除等。

  5. Docker Registry(仓库):Docker 仓库用于存储和分发 Docker 镜像。

1.5 Docker 镜像与容器 ​

镜像:类似虚拟机镜像,是一个特殊的文件系统。

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

镜像分层结构:

  • Docker 镜像采用分层存储的架构
  • 镜像在构建时会一层层构建,前一层是后一层的基础
  • 每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层

容器:类似 Linux 系统环境,运行和隔离应用。是镜像运行时的实体。

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

仓库:集中存放镜像文件的地方。

镜像构建完成后,可以很容易的在当前宿主上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中存储、分发镜像的地方,比如后面我们要学的,Docker Registry 就是这样的服务。

1.6 Docker Hub ​

  • Docker 用 Registry 来保存用户构建的镜像。Registry 分为公共和私有两种。Docker 公司运营公共的 Registry 叫做Docker Hub。用户可以在 Docker Hub 注册账号,分享并保存自己的镜像(说明:在 Docker Hub 下载镜像很慢,可以自己构建私有的 Registry)。

  • 访问 Docker Hub 搜索 Redis 镜像:https://hub.docker.com/

  • 一般都会选择官方的子镜像仓库,在子镜像仓库中会存在很多版本的镜像。

国内镜像加速: 由于国内访问 Docker Hub 速度较慢,建议配置国内镜像加速器,如阿里云、DaoCloud 等。

1.7 Docker 的安装与卸载 ​

  • Docker 从 17.03 版本之后分为 CE(Community Edition: 社区版)和 EE(Enterprise Edition: 企业版),CE 版本是免费的,EE 版本是收费的。本次我们使用社区版。

  • Docker 的安装和卸载可以参考官方文档:https://docs.docker.com/engine/install/centos/

1.7.1 卸载 Docker ​

shell
# 卸载Docker相关组件
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

1.7.2 安装 Docker ​

  • 第一步 检查系统版本 注意:这里建议安装在 CentOS7.x 以上的版本,在 CentOS6.x 的版本中,安装前需要安装其他很多的环境,而且 Docker 很多补丁不支持更新。
shell
# 确定是CentOS7.x及其以上版本
cat /etc/redhat-release
  • 第二步 检查环境,安装 gcc 和 g++
shell
# 安装gcc编译器
yum -y install gcc
# 安装g++编译器
yum -y install gcc-c++
  • 第三步 安装必要的一些系统工具
shell
# 安装必要的系统工具
yum install -y yum-utils device-mapper-persistent-data lvm2
  • 第四步 添加软件源信息(设置阿里云镜像地址,提高下载速度)
shell
# 添加阿里云Docker镜像源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  • 第五步 更新 yum 软件包索引并安装 Docker-CE
shell
# 更新yum软件包索引
yum makecache fast

# 安装Docker-CE及其相关组件
yum install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
  • 第六步 启动 Docker 并设置开机自启
shell
# 启动Docker服务
systemctl start docker

# 设置Docker开机自启
systemctl enable docker

# 查看Docker版本
docker version

1.8 Docker 服务相关命令 ​

Docker 服务操作的相关命令如下所示:

shell
# 查看Docker服务的运行状态
systemctl status docker

# 启动Docker服务
systemctl start docker

# 关闭Docker服务
systemctl stop docker

# 重启Docker服务
systemctl restart docker

# 设置Docker开机自启
systemctl enable docker

# 取消Docker开机自启
systemctl disable docker

# 查看Docker服务信息
docker info

1.9 配置镜像加速器 ​

Docker 的使用过程中,需要从远程仓库下载镜像,但是默认为国外网站,所以在下载时可能会出现下载连接超时导致下载失败,因此需要为其配置镜像加速器,以提高下载速度。

1.9.1 国内配置的镜像地址 ​

受目前网络环境影响,其它加速器可能暂时不能使用,参考下面网站推荐的加速器地址:

https://www.coderjia.cn/archives/dba3f94c-a021-468a-8ac6-e840f85867ea

1.9.2 配置过程 ​

  • 创建文件 daemon.json
shell
# 编辑Docker守护进程配置文件
vim /etc/docker/daemon.json
  • 文件中添加如下内容
json
{
  "registry-mirrors": [
    "https://docker.m.daocloud.io",
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me",
    "https://ccr.ccs.tencentyun.com"
  ]
}
  • 重启 Docker 生效
shell
# 重新加载配置文件
systemctl daemon-reload

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

# 验证加速器是否生效
docker info | grep "Registry Mirrors" -A 5

二、Docker 镜像操作 ​

2.1 搜索远程镜像 ​

相关命令如下所示:

shell
# 命令:
docker search

# 格式:
docker search 镜像关键字

# 示例:搜索镜像名称中包含redis关键字的镜像
docker search redis

列介绍:

  1. name:镜像仓库源名称
  2. description:镜像的描述
  3. official:是否 Docker 官方发布
  4. stars:镜像的收藏数,收藏数越多表示此镜像的受欢迎程度越高
  5. automated:是否自动构建

搜索过滤:

shell
# 只显示官方镜像
docker search --filter "is-official=true" redis

# 只显示收藏数大于100的镜像
docker search --filter "stars=100" redis

2.2 拉取镜像 ​

相关命令如下所示:

shell
# 命令:
docker pull

# 格式: tag表示的镜像的标签,也可以理解为就是镜像的版本
docker pull 镜像名称[:tag]

# 示例1: 默认拉取的是最新的redis镜像
docker pull redis

# 示例2: 拉取redis7.0.10镜像,一个镜像到底存在哪些标签,需要上docker hub中进行查看
docker pull redis:7.0.10

# 示例3: 拉取指定平台的镜像
docker pull --platform linux/amd64 redis:7.0.10

2.3 查看本地镜像 ​

相关命令如下所示:

shell
# 命令:
docker images

# 示例:
docker images

# 只显示镜像ID
docker images -q

# 显示所有镜像(包括中间层镜像)
docker images -a

# 只显示指定镜像
docker images redis

列介绍:

  1. repository:镜像来源仓库名称
  2. tag:镜像标签
  3. image id:镜像 id
  4. created:创建时间
  5. size:镜像的大小

2.4 删除本地镜像 ​

相关命令如下所示:

shell
# 命令:
docker rmi

# 删除单个镜像(-f 强制删除):
docker rmi -f 镜像ID

# 删除多个镜像:
docker rmi -f 镜像名1:TAG 镜像名2:TAG
# 根据镜像的id或者镜像的名称进行删除,如果不添加镜像的标签删除的就是最新的镜像

# 示例:
docker rmi redis:7.0.10				# 删除redis:7.0.10镜像
  • 注意:如果一个镜像存在对应的容器,此时这个镜像是无法进行删除的。

  • 删除所有镜像:

shell
# 慎用此命令,会删除所有本地镜像
docker rmi $(docker images -q)

2.5 镜像标签管理 ​

shell
# 为镜像添加标签
docker tag 原镜像名:标签 新镜像名:标签

# 示例:为redis:7.0.10添加一个新标签
docker tag redis:7.0.10 myredis:v1

# 查看镜像详细信息
docker inspect redis:7.0.10

# 查看镜像历史
docker history redis:7.0.10

2.6 镜像导入导出 ​

shell
# 导出镜像为tar文件
docker save -o redis.tar redis:7.0.10

# 从tar文件导入镜像
docker load -i redis.tar

# 导出镜像并压缩
docker save redis:7.0.10 | gzip > redis.tar.gz

# 从压缩文件导入镜像
docker load < redis.tar.gz

三、Docker 容器操作 ​

3.1 查询容器 ​

相关命令如下所示:

shell
# 命令:docker ps
# 格式:docker ps [options]	# 可以添加一些参数选项,如果不添加表示查询本地所有正在运行的容器
# 示例: docker ps 			# 查看本地正在运行的容器

列介绍:

  1. container id:容器 ID
  2. image:镜像名称
  3. command:容器启动时所执行的命令
  4. created:创建时间
  5. status:容器状态
  6. ports:端口映射情况
  7. names:容器的名称

常见参数选项:

shell
-a,--all												# 查询所有的镜像,包含未运行的容器
-q,--quiet												# 查询容器的id

# 示例1:查询所有的容器包含未运行的容器
docker ps -a
# 示例2:查询容器的id
docker ps -q

# 示例3:显示容器大小
docker ps -s

# 示例4:显示最近创建的容器
docker ps -l

3.2 创建容器 ​

  • 容器分类:
  1. 交互型容器:具有和用户交互的输入和输出终端,容器创建后自动进入容器中,退出容器后,容器自动关闭。

  2. 守护型容器:没有和用户交互终端,需要使用 docker exec 进入容器,退出后,容器不会关闭。

  • 命令介绍:
shell
# 命令:  docker run
# 格式: docker run [OPTIONS] 镜像的名称:镜像标签/镜像id [COMMAND] [ARG...]

# 类型参数选项:
# -i:表示运行容器
# -t:表示容器启动后会进入其命令行。加入这两个参数后,容器创建就能登录进去。即分配一个伪终端。
# --name :为创建的容器命名。
# -v:表示目录映射关系(前者是宿主机目录,后者是映射到宿主机上的目录),可以使用多个-v做多个目录或文件映射。注意:最好做目录映射,在宿主机上做修改,然后共享到容器上。
# -d:在run后面加上-d参数,则会创建一个守护式容器在后台运行(这样创建容器后不会自动登录容器,如果只加-i -t两个参数,创建后就会自动进去容器)。
# -p:表示端口映射,前者是宿主机端口,后者是容器内的映射端口。可以使用多个-p做多个端口映射
# -e:表示为容器设置环境变量,相当于在容器内部的操作系统中定义 KEY=VALUE 格式的环境变量,容器内的应用程序可以读取这些变量来调整运行行为
# --restart:设置容器重启策略
# --network:指定网络模式
# -m:设置容器内存限制
# --cpus:设置容器CPU限制
  • 交互式方式创建容器

以交互式方式创建并启动容器,启动完成后,直接进入当前容器。使用 exit 命令退出容器。需要注意的是以此种方式启动容器,如果退出容器,则容器会进入停止状态,可以理解成交互式容器是前台容器。

shell
# 交互式创建容器命令
docker run -it --name=容器名称 镜像名称:标签 /bin/bash

# 示例:创建一个名为mycentos的交互式容器
docker run -it --name=mycentos centos:7 /bin/bash

# 命令解释:
# docker run:表示创建容器
# -it:表示运行容器并进入它的命令行
# --name=mycentos:给当前的容器命名
# centos:7:使用该镜像创建
# /bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash
  • 守护式方式创建容器
shell
# 守护式容器和交互式容器的创建方式区别:
# -it 换成 -di
# 去掉后面的 /bin/bash
docker run -di --name=容器名称 镜像名称:标签

# 示例:创建一个名为mycentos10的守护式容器
docker run -di --name=mycentos10 centos:7
  • 创建容器常用示例
shell
# 创建Redis容器
docker run -d --name=redis01 -p 6379:6379 redis:7.0.10

# 创建MySQL容器
docker run -d --name=mysql01 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=1234 mysql:8.0.30

# 创建Nginx容器
docker run -d --name=nginx01 -p 80:80 nginx:latest

# 创建容器并设置重启策略
docker run -d --name=redis02 --restart=always -p 6380:6379 redis:7.0.10

# 创建容器并设置资源限制
docker run -d --name=redis03 -m 512m --cpus=1 -p 6381:6379 redis:7.0.10

3.3 容器服务管理 ​

容器管理的相关命令如下所示:

shell
# 关闭容器
docker stop 容器名称/容器id

# 启动容器
docker start 容器名称/容器id

# 重启容器
docker restart 容器名称/容器id

# 暂停容器
docker pause 容器名称/容器id

# 恢复容器
docker unpause 容器名称/容器id

# 杀死容器
docker kill 容器名称/容器id

3.4 删除容器 ​

删除容器的常见命令如下所示:

shell
# 命令: docker rm
# 格式:docker rm 容器名称/容器的id							#  删除容器
# 示例:删除mycentos10容器
docker rm mycentos10
  • 注意:上述的命令只能删除已经关闭的容器,如果想删除正在运行的容器,可以通过添加 -f 参数进行实现。

  • 删除所有的容器:

shell
# 删除所有容器(包括运行中的)
docker rm $(docker ps -aq)

# 删除所有已停止的容器
docker container prune

3.5 进入容器 ​

进入容器命令如下所示:

shell
# 命令:docker exec
# 格式:docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
# 常见的参数选项:
# -t, --tty														# 分配一个虚拟终端,通常和-i参数一起使用
# -i,--interactive												# 把交互界面一直保留,通常和-t参数一起使用

# 示例:进入到mycentos10容器中并打开一个shell窗口
docker exec -it mycentos10 /bin/bash

# 示例:在容器中执行命令(不进入容器)
docker exec mycentos10 ls /root

docker attach 与 docker exec 的区别:

shell
# docker attach:进入容器正在执行的终端,退出会导致容器停止
docker attach 容器名称/容器id

# docker exec:在容器中打开新的终端,退出不会导致容器停止(推荐使用)
docker exec -it 容器名称/容器id /bin/bash

3.6 其他命令 ​

如下所示:

shell
# 查询容器内进程日志,-f参数表示实时监控日志信息
docker logs -f 容器名称/容器的id

# 查看容器的详情信息(包括目录映射、端口映射、IP地址等)
docker inspect 容器名称/容器的id

# 完成容器和宿主机之间的文件copy
docker cp

# 查看容器中的进程信息
docker top 容器名称/容器的id

# 查看容器资源使用情况
docker stats 容器名称/容器的id

# 查看容器端口映射
docker port 容器名称/容器的id

# 导出容器
docker export 容器名称/容器的id > 容器.tar

# 导入容器
docker import 容器.tar 镜像名:标签

# 示例1: 实时查看redis01容器中的日志信息
docker logs -f redis01

四、Docker 数据卷操作 ​

4.1 数据卷简介 ​

什么是数据卷?

数据卷是一个可供一个或多个容器使用的特殊目录,它绕过 UFS(Union File System),提供很多有用的特性:

  1. 数据卷可以在容器之间共享和重用
  2. 对数据卷的修改会立马生效
  3. 对数据卷的更新不会影响镜像
  4. 数据卷默认会一直存在,即使容器被删除

为什么需要数据卷?

Docker 容器产生的数据,如果不通过 docker commit 生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了。为了能保存数据,Docker 使用了数据卷。

数据卷的特点:

  1. 数据卷可在容器之间共享或重用数据
  2. 卷中的更改可以直接生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为止

4.2 数据卷操作命令 ​

shell
# 创建数据卷
docker volume create 数据卷名称

# 查看所有数据卷
docker volume ls

# 查看数据卷详情
docker volume inspect 数据卷名称

# 删除数据卷
docker volume rm 数据卷名称

# 删除所有未使用的数据卷
docker volume prune

4.3 数据卷挂载 ​

方式一:使用-v 参数挂载数据卷

shell
# 格式
docker run -v 数据卷名称:容器内路径 镜像名称

# 示例:创建容器并挂载数据卷
docker run -di --name=centos1 -v volume1:/root centos:7

# 说明:
# volume1:数据卷名称(如果不存在会自动创建)
# /root:容器内的路径

方式二:直接挂载宿主机目录

shell
# 格式
docker run -v 宿主机目录:容器内目录 镜像名称

# 示例:将宿主机的/root目录挂载到容器的/root目录
docker run -di --name=centos2 -v /root/host_data:/root/container_data centos:7

# 说明:
# /root/host_data:宿主机目录(必须使用绝对路径)
# /root/container_data:容器内目录

方式三:挂载文件

shell
# 挂载单个文件
docker run -di --name=nginx01 -v /root/nginx.conf:/etc/nginx/nginx.conf:ro nginx:latest

# 说明:
# :ro 表示只读(read-only),容器内无法修改该文件
# :rw 表示可读写(默认值)

4.4 数据卷容器 ​

什么是数据卷容器?

命名的容器挂载数据卷,其他容器通过挂载这个容器实现数据共享,挂载数据卷的容器,称之为数据卷容器。

创建数据卷容器

shell
# 创建一个数据卷容器
docker run -di --name=data_container -v /data centos:7

# 其他容器挂载数据卷容器
docker run -di --name=centos3 --volumes-from data_container centos:7
docker run -di --name=centos4 --volumes-from data_container centos:7

# 说明:
# centos3和centos4共享data_container的数据卷
# 即使data_container容器停止,数据卷仍然可以被其他容器访问

4.5 数据卷应用案例 ​

案例 1:MySQL 数据持久化

shell
# 创建MySQL容器并挂载数据卷
docker run -d --name=mysql \
  -p 3306:3306 \
  -v mysql_data:/var/lib/mysql \
  -v mysql_conf:/etc/mysql \
  -e MYSQL_ROOT_PASSWORD=1234 \
  mysql:8.0.30

# 说明:
# mysql_data:存储MySQL数据文件
# mysql_conf:存储MySQL配置文件
# 即使删除容器,数据也不会丢失

案例 2:Nginx 静态资源挂载

shell
# 创建Nginx容器并挂载静态资源目录
docker run -d --name=nginx \
  -p 80:80 \
  -v /root/html:/usr/share/nginx/html \
  -v /root/conf/nginx.conf:/etc/nginx/nginx.conf:ro \
  nginx:latest

# 说明:
# 将宿主机的html目录挂载到Nginx的静态资源目录
# 修改宿主机的html文件,Nginx会立即生效

案例 3:Redis 数据持久化

shell
# 创建Redis容器并开启持久化
docker run -d --name=redis \
  -p 6379:6379 \
  -v redis_data:/data \
  redis:7.0.10 redis-server --appendonly yes

# 说明:
# redis_data:存储Redis持久化数据
# --appendonly yes:开启AOF持久化

4.6 数据卷权限管理 ​

shell
# 挂载时指定读写权限
docker run -v 数据卷:容器路径:ro 镜像名称  # 只读
docker run -v 数据卷:容器路径:rw 镜像名称  # 可读写(默认)

# 挂载时指定用户和组
docker run -v 数据卷:容器路径 --user 1000:1000 镜像名称

# 挂载时设置SELinux标签
docker run -v 数据卷:容器路径:z 镜像名称   # 私有标签
docker run -v 数据卷:容器路径:Z 镜像名称   # 共享标签

4.7 数据卷备份与恢复 ​

备份数据卷

shell
# 备份数据卷到宿主机
docker run --rm \
  -v 数据卷名称:/data \
  -v /root/backup:/backup \
  centos:7 \
  tar cvf /backup/backup.tar /data

# 说明:
# --rm:容器执行完毕后自动删除
# 第一个-v:挂载要备份的数据卷
# 第二个-v:挂载宿主机备份目录
# tar命令:将数据卷内容打包

**恢复数据卷**

```shell
# 从备份文件恢复数据卷
docker run --rm \
  -v 数据卷名称:/data \
  -v /root/backup:/backup \
  centos:7 \
  tar xvf /backup/backup.tar -C /

# 说明:
# 将备份文件解压到数据卷中

# 示例 2: 查看 redis01 容器的详情信息

docker inspect redis01

# 示例 3: 把宿主机中 a.txt 文件拷贝到 redis01 的 root 目录中

docker cp a.txt redis01:/root

# 示例 4: 把容器中的 root 目录下的 a.txt 文件拷贝到宿主机中当前目录中

docker cp redis01:/root/a.txt .

3.7 备份与迁移 ​

对某一个容器修改完毕以后,我们可以把最新的容器部署到其他的环境中。具体的流程操作如下所示:

涉及的 Docker 命令:

shell
# 把Docker容器保存成一个镜像
docker commit 容器名称/容器的id 镜像名称

# 把镜像保存为tar文件
docker save -o 镜像tar文件名称 镜像名称/镜像id

# 把tar文件恢复成为一个镜像
docker load -i tar文件的名称

示例代码:

shell
# 将mycentos10容器保存为一个镜像
docker commit mycentos10 mycentos

# 将mycentos镜像保存为一个tar文件
docker save -o mycentos.tar mycentos

# 删除之前的mycentos镜像
docker rmi mycentos

# 将mycentos.tar恢复成一个镜像
docker load -i mycentos.tar

commit 命令详解:

shell
# 格式:docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
# -a, --author:作者信息
# -m, --message:提交信息
# -c, --change:应用Dockerfile指令

# 示例:提交容器并添加作者和说明信息
docker commit -a "author" -m "install vim" mycentos10 mycentos:v1

五、Spring Boot 项目部署 ​

本章节主要讲解的就是如何把一个 Spring Boot 项目使用 Docker 进行部署,以减少整个项目的维护成本。

5.1 Dockerfile ​

5.1.1 Dockerfile 简介 ​

前面我们所使用的镜像都是别人构建好的,但是别人构建好的镜像不一定能满足我们的需求。为了满足我们自己的某一些需求,此时我们就需要构建自己的镜像,怎么构建?使用 Dockerfile。

Dockerfile 就是一个文本文件,在这个文本文件中可以使用 Docker 所提供的一些指令来指定我们构建镜像的细节,后期就可以使用这个 Dockerfile 文件来构建自己的镜像。

Dockerfile 文件内容一般分为 4 部分:

  1. 基础镜像信息(必选)
  2. 维护者信息(可选)
  3. 镜像操作指令(可选)
  4. 容器启动时执行的指令(可选)

常用命令:

指令用法作用
FROMFROM image_name:tag指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的 latest 标签,可以出现多次,如果需要在一个 dockerfile 中构建多个镜像。
MAINTAINERMAINTAINER user_name描述镜像的创建者,名称和邮箱
RUNRUN "command" "param1" "param2"用来执行一些命令,可以写多条
ENVENV key value设置容器的环境变量,可以写多条。
ADDADD source_dir/file将宿主机的文件复制到容器内,如果是压缩文件,则复制后自动解压
COPYCOPY source_dir/file将宿主机的文件复制到容器内(不会自动解压)
ENTRYPOINTENTRYPOINT "command" "param1" "param2"用来指定容器启动时所执行的命令
CMDCMD "command" "param1" "param2"指定容器启动时默认执行的命令,可以被 docker run 参数覆盖
WORKDIRWORKDIR path指定工作目录
EXPOSEEXPOSE port声明暴露的端口
VOLUMEVOLUME ["path"]创建挂载点
ARGARG key=value定义构建时的变量
USERUSER username指定运行容器时的用户名或 UID

5.1.2 入门案例 ​

需求:使用 Dockerfile 来构建一个包含 Jdk17 的 centos7 镜像

分析:

  1. 基础的镜像的应该选择 centos:7
  2. 在自己所构建的镜像中需要包含 Jdk17,就需要把 Jdk17 添加到 centos:7 的基础镜像中
  3. 为了方便的去使用自己构建的镜像中的 Jdk17,就需要去配置环境变量
  4. 因为 Jdk17 仅仅是一个开发工具,并不是一个服务进程,因此在启动容器的时候可以不指定任何的执行命令

实现步骤:

  1. 将 Jdk17 的安装包上传到 linux 服务器的指定目录下
  2. 在 Jdk17 所在的目录下创建一个 dockerfile 文件
  3. 使用 docker build 命令构建镜像
  4. 使用 docker images 查看镜像构建情况
  5. 使用自己所构建的镜像创建容器,测试 Jdk17 的安装情况

代码实现:

shell
# 创建目录
mkdir -p /usr/local/dockerfilejdk17
cd /usr/local/dockerfilejdk17

# 下载jdk-17_linux-x64_bin.tar.gz并上传到服务器(虚拟机)中的/usr/local/dockerfilejdk17目录
# 手动操作:从Oracle官网下载JDK17 Linux版本,上传到上面创建的目录中

# vim dockerfile:使用vim编辑器创建并编辑名为 dockerfile 的文件
vim dockerfile

FROM centos:7
# MAINTAINER hl:声明镜像作者(自定义名称,用于标识)
MAINTAINER hl
# RUN mkdir -p /usr/local/java:在镜像内创建存放JDK的目录
RUN mkdir -p /usr/local/java
# ADD jdk-17_linux-x64_bin.tar.gz /usr/local/java/:将宿主机的JDK压缩包复制到镜像内并自动解压
ADD jdk-17_linux-x64_bin.tar.gz /usr/local/java/
ENV JAVA_HOME=/usr/local/java/jdk-17.0.7
ENV PATH=$PATH:$JAVA_HOME/bin

# 4、执行命令构建镜像;不要忘了后面的那个 .
# docker build:Docker构建镜像命令
# -t centos7-jdk17:-t 给镜像打标签(命名),centos7-jdk17 是自定义镜像名称
# . :指定构建上下文为当前目录,让Docker在当前目录找 dockerfile 文件
docker build -t centos7-jdk17 .

# 5、查看镜像是否建立完成
# docker images:查看本地所有已构建完成的Docker镜像
docker images

# 6、创建容器
# docker run:创建并启动Docker容器
# -it:交互式运行容器,分配终端
# --name  hl-cento:给容器自定义名称,方便管理
# centos7-jdk17:指定使用刚才构建的镜像来创建容器
# /bin/bash:容器启动后直接进入bash命令行终端
docker run -it --name hl-centos centos7-jdk17 /bin/bash

Dockerfile 最佳实践:

  1. 使用.dockerignore 文件排除不需要的文件
  2. 使用多阶段构建减小镜像大小
  3. 合并 RUN 指令减少镜像层数
  4. 使用特定版本标签而不是 latest
  5. 尽量使用官方镜像作为基础镜像

5.1.3 Dockerfile 指令详解 ​

1. FROM 指令

FROM 指令用于指定基础镜像,是 Dockerfile 的第一条指令。

dockerfile
# 格式
FROM <image>
FROM <image>:<tag>
FROM <image>@<digest>

# 示例
FROM centos:7                    # 使用centos:7作为基础镜像
FROM nginx:1.25-alpine          # 使用指定版本的nginx
FROM scratch                    # 从零开始构建(用于静态编译的程序)

2. MAINTAINER 指令

MAINTAINER 指令用于指定镜像维护者信息。

dockerfile
# 格式
MAINTAINER <name>

# 示例
MAINTAINER "zhangsan <zhangsan@example.com>"

3. RUN 指令

RUN 指令用于执行命令,有两种格式:

dockerfile
# Shell格式(推荐)
RUN <command>

# Exec格式
RUN ["executable", "param1", "param2"]

# 示例
RUN yum -y install vim           # Shell格式
RUN ["/bin/bash", "-c", "echo hello"]  # Exec格式

# 合并多个RUN指令(减少镜像层数)
RUN yum -y install vim && \
    yum -y install net-tools && \
    yum clean all

4. CMD 指令

CMD 指令指定容器启动时默认执行的命令,只能有一条 CMD 指令。

dockerfile
# Exec格式(推荐)
CMD ["executable", "param1", "param2"]

# Shell格式
CMD command param1 param2

# 作为ENTRYPOINT的默认参数
CMD ["param1", "param2"]

# 示例
CMD ["nginx", "-g", "daemon off;"]    # 启动nginx
CMD ["java", "-jar", "app.jar"]       # 启动Java应用

5. ENTRYPOINT 指令

ENTRYPOINT 指令配置容器启动时执行的命令,不会被 docker run 的参数覆盖。

dockerfile
# Exec格式(推荐)
ENTRYPOINT ["executable", "param1", "param2"]

# Shell格式
ENTRYPOINT command param1 param2

# 示例
ENTRYPOINT ["java", "-jar"]
CMD ["app.jar"]                 # CMD作为ENTRYPOINT的默认参数

# 使用场景:制作一个可执行程序镜像
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["redis-server"]            # 默认启动redis-server

CMD 与 ENTRYPOINT 的区别:

特性CMDENTRYPOINT
作用设置容器默认启动命令设置容器主命令
是否可被覆盖可被 docker run 参数覆盖不会被覆盖,参数会追加
数量限制只能有一条只能有一条
推荐用法作为 ENTRYPOINT 的默认参数定义可执行程序

6. ENV 指令

ENV 指令用于设置环境变量。

dockerfile
# 格式
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

# 示例
ENV JAVA_HOME=/usr/local/java/jdk-17
ENV PATH=$PATH:$JAVA_HOME/bin

# 设置多个环境变量
ENV APP_NAME=myapp \
    APP_VERSION=1.0 \
    APP_ENV=production

7. ADD 指令

ADD 指令用于复制文件,支持自动解压和 URL 下载。

dockerfile
# 格式
ADD <src> <dest>
ADD ["<src>", "<dest>"]

# 示例
ADD app.jar /app/app.jar              # 复制文件
ADD https://example.com/file.txt /tmp/  # 从URL下载
ADD jdk-17.tar.gz /usr/local/java/    # 自动解压tar.gz文件

8. COPY 指令

COPY 指令用于复制文件,不会自动解压。

dockerfile
# 格式
COPY <src> <dest>
COPY ["<src>", "<dest>"]

# 示例
COPY app.jar /app/app.jar
COPY config/ /app/config/
COPY *.sh /scripts/

# ADD与COPY的区别:
# - ADD支持URL下载和自动解压
# - COPY只支持本地文件复制
# - 推荐使用COPY,除非需要解压功能

9. WORKDIR 指令

WORKDIR 指令用于设置工作目录。

dockerfile
# 格式
WORKDIR <path>

# 示例
WORKDIR /app                      # 设置工作目录为/app
RUN pwd                           # 输出/app

# 可以使用多个WORKDIR
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd                           # 输出/a/b/c

10. EXPOSE 指令

EXPOSE 指令声明暴露的端口。

dockerfile
# 格式
EXPOSE <port> [<port>/<protocol>...]

# 示例
EXPOSE 80                         # 暴露80端口
EXPOSE 8080 8081                  # 暴露多个端口
EXPOSE 53/udp                     # 指定协议
EXPOSE 80/tcp 443/tcp             # 暴露HTTP和HTTPS端口

11. VOLUME 指令

VOLUME 指令创建挂载点。

dockerfile
# 格式
VOLUME ["<path>"]
VOLUME <path>

# 示例
VOLUME ["/data"]                  # 创建挂载点
VOLUME ["/data", "/log"]          # 创建多个挂载点
VOLUME /var/lib/mysql             # MySQL数据目录

12. ARG 指令

ARG 指令定义构建时的变量,只在构建时有效。

dockerfile
# 格式
ARG <name>[=<default value>]

# 示例
ARG VERSION=7.0
FROM centos:${VERSION}

# 使用docker build传递参数
# docker build --build-arg VERSION=8.0 -t myimage .

13. USER 指令

USER 指令指定运行容器时的用户。

dockerfile
# 格式
USER <user>[:<group>]
USER <UID>[:<GID>]

# 示例
USER nginx                        # 使用nginx用户运行
USER 1000:1000                    # 使用UID和GID

# 创建用户并切换
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser

14. HEALTHCHECK 指令

HEALTHCHECK 指令用于健康检查。

dockerfile
# 格式
HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK NONE                  # 禁用健康检查

# 示例
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost:8080/health || exit 1

# 参数说明:
# --interval:检查间隔(默认30s)
# --timeout:超时时间(默认30s)
# --start-period:启动等待时间(默认0s)
# --retries:重试次数(默认3次)

15. ONBUILD 指令

ONBUILD 指令定义触发器,在子镜像构建时执行。

dockerfile
# 格式
ONBUILD <INSTRUCTION>

# 示例(创建一个基础镜像)
ONBUILD COPY . /app
ONBUILD RUN npm install
ONBUILD CMD ["npm", "start"]

# 当其他镜像FROM这个镜像时,上述命令会自动执行

16. LABEL 指令

LABEL 指令用于添加元数据。

dockerfile
# 格式
LABEL <key>=<value> <key>=<value>...

# 示例
LABEL version="1.0"
LABEL description="This is my app"
LABEL maintainer="zhangsan@example.com"

17. SHELL 指令

SHELL 指令用于指定默认 Shell。

dockerfile
# 格式
SHELL ["executable", "parameters"]

# 示例
SHELL ["/bin/bash", "-c"]         # 使用bash
SHELL ["powershell", "-command"]  # 在Windows上使用PowerShell

5.1.4 多阶段构建 ​

多阶段构建可以大幅减小镜像大小。

dockerfile
# 第一阶段:构建阶段
FROM maven:3.8-openjdk-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn clean package -DskipTests

# 第二阶段:运行阶段
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY --from=builder /app/target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

# 说明:
# AS builder:给第一阶段命名
# --from=builder:从第一阶段复制文件
# 最终镜像只包含第二阶段的内容,大小大幅减小

多阶段构建的优势:

  1. 减小镜像大小
  2. 分离构建环境和运行环境
  3. 提高安全性(不包含构建工具)

5.2 案例介绍与需求分析 ​

需求:将提供的 Spring Boot 项目使用容器化进行部署

刚才的入门案例中,构建过程是手动镜像构建的过程。现在我们可以使用 Maven 的 Docker 插件实现镜像的自动化部署。

分析:

  1. Spring Boot 项目中使用到了 Mysql 环境,因此需要先使用 Docker 部署 Mysql 环境
  2. 要将 Spring Boot 项目使用 Docker 容器进行部署,就需要将 Spring Boot 项目构建成一个 Docker 镜像

实现步骤:

  1. 使用 Docker 部署 Mysql
  2. 在 pom.xml 文件中添加 Maven 的 Docker 插件
  3. 开启 Docker 服务端的远程访问
  4. 在工程的根目录下创建 Dockerfile 文件
  5. 使用 Maven 的打包命令进行打包
  6. 创建容器并访问

5.3 Docker 部署 Mysql ​

使用 Docker 部署 Mysql 步骤如下所示:

shell
# 创建容器。-e: 设置环境变量 --privileged=true 开启root用户权限
docker run -di --name=mysql -p 3306:3306 -v mysql_data:/var/lib/mysql -v mysql_conf:/etc/mysql --privileged=true -e MYSQL_ROOT_PASSWORD=1234 mysql:8.0.30

# 进入容器
docker exec -it mysql /bin/bash

# 登录mysql
mysql -uroot -p

并创建对应的数据库和数据库表:

创建数据库:docker

创建表:

sql
CREATE TABLE `tb_school` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

添加测试数据:

sql
INSERT INTO `tb_school` VALUES (1, '北京校区', '北京市昌平区宏福科技园2号楼3层');
INSERT INTO `tb_school` VALUES (2, '上海校区', '上海市松江区谷阳北路166号大江商厦3层');
INSERT INTO `tb_school` VALUES (3, '深圳校区', '深圳市宝安区西部硅谷大厦B座C区一层');
INSERT INTO `tb_school` VALUES (4, '西安校区', '西安市雁塔区和发智能大厦B座3层');
INSERT INTO `tb_school` VALUES (5, '成都校区', '成都市成华区北辰星拱青创园综合楼3层');
INSERT INTO `tb_school` VALUES (6, '武汉校区', '武汉市东湖高新区东湖网谷6号楼4层');

5.4 Maven 的 Docker 插件 ​

实操:

  1. 在 pom.xml 文件中添加 Maven 的 Docker 插件
xml
<plugin>
    <groupId>com.spotify</groupId>
    <artifactId>docker-maven-plugin</artifactId>
    <version>1.2.2</version>

    <!--将插件绑定在某个phase执行-->
    <executions>
        <execution>
            <id>build-image</id>
            <!--将插件绑定在package这个phase(阶段)上。也就是说,用户只需执行mvn package,就会自动执行mvn docker:build-->
            <phase>package</phase>
            <goals>
                <goal>build</goal>
            </goals>
        </execution>
    </executions>

    <configuration>
        <!--指定生成的镜像名-->
        <imageName>${project.artifactId}</imageName>

        <!--指定标签,也就是版本号,可以自定义-->
        <imageTags>
            <imageTag>v2.0</imageTag>
        </imageTags>

        <!--指定远程 docker api地址 也就是服务器ip+docker的端口号-->
        <dockerHost>http://192.168.136.142:2375</dockerHost>

        <!-- 指定 dockerfile 路径-->
        <dockerDirectory>${project.basedir}</dockerDirectory>

        <!-- 是否跳过docker构建 -->
        <skipdockerBuild>false</skipdockerBuild>

    </configuration>

</plugin>
  1. Docker 服务端开启远程访问
shell
# 修改Docker服务配置文件
vim /lib/systemd/system/docker.service

# 找到ExecStart行,修改成如下内容
ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H fd:// --containerd=/run/containerd/containerd.sock

# 重启守护进程
systemctl daemon-reload

# 重启Docker服务
systemctl restart docker
  1. 编写 Dockerfile 文件

注意:

  • 该文件的位置必须是和 pom.xml 处于同一个目录
  • 关闭防火墙
shell
FROM centos7-jdk17
MAINTAINER hl
EXPOSE 8081
ADD target/ebuy-docker-1.0-SNAPSHOT.jar /ebuy-docker-1.0-SNAPSHOT.jar

WORKDIR /
ENTRYPOINT ["java" , "-jar" , "ebuy-docker-1.0-SNAPSHOT.jar"]
  1. 执行 Maven 的打包命令
shell
# 打包跳过测试
mvn clean package -DskipTests

# 打包跳过测试的同时提高构建
mvn clean package -DskipTests -DskipdockerBuild
  1. 创建容器并进行访问
shell
# 创建并运行容器,映射端口8082到容器的8081端口
docker run -d --name ebuy-docker -p 8082:8081 ebuy-docker:latest

5.5 推送镜像 ​

推送到阿里云镜像:

  • 开通阿里云镜像仓库(打开阿里云登录页,让学生扫码登录,演示从零到一开通过程)
  • 登录镜像仓库
  • 重命名镜像,按照阿里云规范
  • 推送镜像

推送镜像到 Docker Hub:

shell
# 登录Docker Hub
docker login

# 为镜像打标签
docker tag ebuy-docker:latest username/ebuy-docker:v1.0

# 推送镜像
docker push username/ebuy-docker:v1.0

5.6 测试 ​

访问测试: http://192.168.6.131:8081

六、Docker Compose ​

6.1 Docker Compose 简介 ​

  1. Docker Compose 是一个工具,用于定义和运行多容器应用程序的工具;
  2. Docker Compose 通过 yml 文件定义多容器的 Docker 应用;
  3. Docker Compose 通过一条命令根据 yml 文件的定义去创建或管理多容器;

Docker Compose 是用来做 Docker 的多容器控制,有了 Docker Compose 你可以把所有繁复的 Docker 操作全都一条命令,自动化的完成。

官网地址:https://docs.docker.com/compose/install/linux/

Docker Compose 的优点:

  1. 单主机多容器部署
  2. 快速搭建开发环境
  3. 项目编排管理
  4. 简化多容器应用的管理

6.2 下载与安装 ​

下载与安装:

  • 在安装 Docker 时候已经完成了安装,直接查看版本号,查看是否安装成功
shell
# 创建指定目录存储docker compose
mkdir -p /usr/local/lib/docker/cli-plugins

# 下载并移动
curl -SL https://github.com/docker/compose/releases/download/v2.14.2/docker-compose-linux-x86_64 -o /usr/local/lib/docker/cli-plugins/docker-compose

# 给docker-compose文件赋予可执行权限
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

# 查看docker compose的版本
docker compose version

6.3 入门案例 ​

需求:使用 Docker Compose 部署 Redis

docker-compose.yml 文件的内容如下所示:

yml
services:
  redis:
    image: redis:7.0.10
    container_name: redis
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data
    restart: always
volumes:
  redis-data: {}

Docker Compose 相关命令:

shell
# 启动容器(如果不存在容器就创建、存在则修改)
docker compose -f docker-compose.yml up -d

# 删除所有容器
docker compose -f docker-compose.yml down

# 停止所有容器
docker compose -f docker-compose.yml stop

# 启动所有容器
docker compose -f docker-compose.yml start

# 重启所有容器
docker compose -f docker-compose.yml restart

# 查看容器状态
docker compose -f docker-compose.yml ps

# 查看容器日志
docker compose -f docker-compose.yml logs

# 进入容器
docker compose -f docker-compose.yml exec redis /bin/bash

Docker Compose 文件中其他的常见指令参考官方文档:https://docs.docker.com/compose/compose-file/05-services/

6.4 编排 配置多容器 ​

docker-compose.yml 文件的内容如下所示:

yaml
# 核心功能:一键启动MySQL容器+Python应用容器,实现容器间通信、数据持久化

services:
  # 容器1:MySQL服务容器(命名为mysql-db,容器间可通过该名称访问)
  mysql-db:
    # 基础镜像:指定MySQL 8.0.45版本(稳定性高,适配Python连接)
    image: mysql:8.0.45
    # 重启策略:容器意外关闭/服务器重启时,自动重启(保证服务可用性)
    restart: always
    # 环境变量:配置MySQL核心参数(无需手动进入容器修改)
    environment:
      MYSQL_ROOT_PASSWORD: "123456"  # root用户密码(自定义,Python连接需一致)
      MYSQL_DATABASE: "test_db"      # 自动创建初始数据库(无需手动建库)
      # 字符编码配置:适配MySQL 8.0+,避免中文乱码/连接报错
      MYSQL_INITDB_ARGS: "--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
    # 端口映射:本地9999端口 → 容器内3306端口(避免与本地MySQL端口冲突)
    ports:
      - "9999:3306"
    # 数据卷挂载:Windows本地D盘mysql-data目录 → 容器内MySQL数据目录(持久化数据)
    volumes:
      - D:/mysql-data:/var/lib/mysql
    # 加入自定义网络:与Python容器互通(容器间通信的基础)
    networks:
      - app-network
    # 健康检查:判断MySQL服务是否真正就绪(而非仅容器启动),解决连接拒绝问题
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-uroot", "-p123456"]  # 检测MySQL是否可ping通
      interval: 3s          # 每3秒检查一次
      timeout: 3s           # 单次检查超时时间3秒
      retries: 10           # 最多重试10次(覆盖MySQL启动初始化时间)
      start_period: 5s      # 容器启动后,延迟5秒再开始检查(避免过早检测)

  # 容器2:Python应用容器(命名为python-app,业务代码运行载体)
  python-app:
    # 构建规则:基于当前目录下的Dockerfile,构建自定义Python镜像
    build: .
    # 依赖条件:等待mysql-db容器的健康检查通过后,再启动本容器(核心修复:解决MySQL未就绪问题)
    depends_on:
      mysql-db:
        condition: service_healthy  # 仅当MySQL服务就绪时,才启动Python容器
    # 环境变量:传递MySQL连接信息(Python代码可通过os.getenv获取,无需硬编码)
    environment:
      MYSQL_HOST: "mysql-db"    # 容器间通信地址:直接填MySQL容器名(无需IP)
      MYSQL_PORT: "3306"        # MySQL容器内端口(非本地9999)
      MYSQL_USER: "root"        # 与MySQL容器的用户名一致
      MYSQL_PASSWORD: "123456"  # 与MySQL容器的密码一致(修改需同步)
      MYSQL_DB: "test_db"       # 与MySQL容器自动创建的数据库名一致
    # 加入自定义网络:与MySQL容器在同一网络,才能通信
    networks:
      - app-network

# 自定义网络:桥接模式(Docker默认),保证两个容器相互可见、可访问
networks:
  app-network:
    driver: bridge

6.5 Docker Compose 常用配置 ​

yaml
version: "3.8" # 指定compose版本

services: # 定义服务
  web:
    image: nginx:latest # 指定镜像
    build: . # 构建镜像
    container_name: mynginx # 容器名称
    ports: # 端口映射
      - "80:80"
    volumes: # 数据卷挂载
      - ./html:/usr/share/nginx/html
    environment: # 环境变量
      - ENV=production
    networks: # 网络
      - mynetwork
    depends_on: # 依赖关系
      - db
    restart: always # 重启策略
    command: nginx -g "daemon off;" # 覆盖默认命令

networks: # 定义网络
  mynetwork:
    driver: bridge

volumes: # 定义数据卷
  mydata:

6.6 Docker 私有仓库搭建 ​

为什么需要私有仓库?

  1. 企业内部镜像需要保密
  2. 加速镜像下载
  3. 节省带宽
  4. 统一管理镜像

6.6.1 搭建私有仓库 ​

方式一:使用 Docker Registry

shell
# 1. 拉取registry镜像
docker pull registry:2

# 2. 创建私有仓库容器
docker run -d \
  --name=registry \
  -p 5000:5000 \
  -v /opt/registry:/var/lib/registry \
  --restart=always \
  registry:2

# 说明:
# -p 5000:5000:映射端口
# -v /opt/registry:/var/lib/registry:数据持久化
# --restart=always:容器自动重启

方式二:使用 Harbor(企业级推荐)

Harbor 是 VMware 开源的企业级 Docker Registry 项目,提供了更丰富的功能。

shell
# 1. 下载Harbor安装包
wget https://github.com/goharbor/harbor/releases/download/v2.5.0/harbor-offline-installer-v2.5.0.tgz

# 2. 解压
tar -xzf harbor-offline-installer-v2.5.0.tgz

# 3. 修改配置文件
cd harbor
cp harbor.yml.tmpl harbor.yml
vim harbor.yml

# 修改以下配置:
# hostname: 192.168.136.142  # 修改为本机IP
# http.port: 80
# harbor_admin_password: Harbor12345  # 管理员密码

# 4. 安装Harbor
./install.sh

# 5. 启动Harbor
docker-compose up -d

6.6.2 配置私有仓库地址 ​

shell
# 修改Docker配置文件
vim /etc/docker/daemon.json

# 添加以下内容(将192.168.136.142替换为私有仓库地址)
{
  "registry-mirrors": ["https://docker.m.daocloud.io"],
  "insecure-registries": ["192.168.136.142:5000"]
}

# 重启Docker服务
systemctl daemon-reload
systemctl restart docker

6.6.3 推送镜像到私有仓库 ​

shell
# 1. 标记镜像
docker tag redis:7.0.10 192.168.136.142:5000/redis:7.0.10

# 2. 推送镜像
docker push 192.168.136.142:5000/redis:7.0.10

# 3. 查看私有仓库中的镜像
curl http://192.168.136.142:5000/v2/_catalog

# 4. 查看某个镜像的所有标签
curl http://192.168.136.142:5000/v2/redis/tags/list

6.6.4 从私有仓库拉取镜像 ​

shell
# 从私有仓库拉取镜像
docker pull 192.168.136.142:5000/redis:7.0.10

6.6.5 私有仓库认证 ​

shell
# 1. 创建用户密码文件
docker run --entrypoint htpasswd httpd:2 -Bbn admin 123456 > /opt/registry-auth/htpasswd

# 2. 启动带认证的私有仓库
docker run -d \
  --name=registry-auth \
  -p 5000:5000 \
  -v /opt/registry:/var/lib/registry \
  -v /opt/registry-auth:/auth \
  -e REGISTRY_AUTH=htpasswd \
  -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  --restart=always \
  registry:2

# 3. 登录私有仓库
docker login 192.168.136.142:5000

# 4. 推送/拉取镜像(需要先登录)
docker push 192.168.136.142:5000/redis:7.0.10

# 5. 退出登录
docker logout 192.168.136.142:5000

6.6.6 Harbor 使用指南 ​

shell
# 登录Harbor
docker login 192.168.136.142

# 推送镜像到Harbor
docker tag redis:7.0.10 192.168.136.142/library/redis:7.0.10
docker push 192.168.136.142/library/redis:7.0.10

# 从Harbor拉取镜像
docker pull 192.168.136.142/library/redis:7.0.10

Harbor Web 界面访问:http://192.168.136.142

默认账号:admin 默认密码:Harbor12345

七、Docker 网络管理 ​

7.1 Docker 网络概述 ​

Docker 网络是 Docker 容器之间以及容器与外部世界通信的桥梁。Docker 提供了多种网络模式,满足不同场景的需求。

7.2 Docker 网络模式 ​

Docker 支持以下几种网络模式:

  1. bridge 模式(默认):桥接网络,容器通过网桥与宿主机通信
  2. host 模式:容器使用宿主机的网络命名空间
  3. none 模式:容器没有网络接口
  4. container 模式:容器共享另一个容器的网络命名空间
  5. 自定义网络:用户创建的网络

7.3 网络管理命令 ​

shell
# 查看所有网络
docker network ls

# 创建网络
docker network create 网络名称

# 创建网络并指定驱动类型
docker network create -d bridge mynetwork

# 查看网络详情
docker network inspect 网络名称

# 删除网络
docker network rm 网络名称

# 删除所有未使用的网络
docker network prune

# 将容器连接到网络
docker network connect 网络名称 容器名称

# 将容器从网络断开
docker network disconnect 网络名称 容器名称

7.4 网络使用示例 ​

shell
# 创建自定义网络
docker network create mynetwork

# 创建容器并连接到自定义网络
docker run -d --name=redis01 --network=mynetwork redis:7.0.10
docker run -d --name=redis02 --network=mynetwork redis:7.0.10

# 在同一网络中的容器可以通过容器名称互相访问
docker exec -it redis01 ping redis02

7.5 Docker 资源限制详解 ​

Docker 可以通过设置资源限制来控制容器使用的 CPU、内存等资源,防止单个容器占用过多资源影响其他容器或宿主机。

7.5.1 内存限制 ​

shell
# 设置内存限制
docker run -d --name=redis01 -m 512m redis:7.0.10

# 设置内存和交换内存限制
docker run -d --name=redis02 -m 512m --memory-swap=1g redis:7.0.10

# 设置内存软限制(允许超过,但在内存紧张时优先回收)
docker run -d --name=redis03 --memory-reservation=256m -m 512m redis:7.0.10

# 禁止容器使用交换内存
docker run -d --name=redis04 -m 512m --memory-swappiness=0 redis:7.0.10

# 设置内存限制后容器被杀死的退出状态码
# OOM (Out of Memory) 退出状态码: 137

内存限制参数说明:

参数说明
-m, --memory设置内存使用上限
--memory-swap设置内存+交换内存的总上限
--memory-reservation设置内存软限制
--memory-swappiness设置使用交换内存的倾向(0-100)
--oom-kill-disable禁用 OOM Killer(需要设置-m 参数)
--oom-score-adj设置 OOM 优先级(-1000 到 1000)

7.5.2 CPU 限制 ​

shell
# 设置CPU份额(相对权重,默认1024)
docker run -d --name=redis01 --cpu-shares=512 redis:7.0.10

# 设置CPU核数限制
docker run -d --name=redis02 --cpus=1.5 redis:7.0.10

# 指定使用的CPU核心
docker run -d --name=redis03 --cpuset-cpus="0,1" redis:7.0.10

# 设置CPU配额和周期
docker run -d --name=redis04 --cpu-quota=50000 --cpu-period=100000 redis:7.0.10

# 设置CPU实时运行时间
docker run -d --name=redis05 --cpu-rt-runtime=950000 --cpu-rt-period=1000000 redis:7.0.10

CPU 限制参数说明:

参数说明
--cpus设置可使用的 CPU 核数(如 1.5 表示 1.5 核)
--cpu-shares设置 CPU 份额权重(相对值)
--cpuset-cpus指定使用的 CPU 核心(如 0,1,2)
--cpu-quota设置 CPU 配额(需要配合--cpu-period)
--cpu-period设置 CPU 周期(默认 100000 微秒)

7.5.3 磁盘 IO 限制 ​

shell
# 设置块设备读写速率限制
docker run -d --name=redis01 \
  --device-read-bps=/dev/sda:10mb \
  --device-write-bps=/dev/sda:10mb \
  redis:7.0.10

# 设置块设备读写IOPS限制
docker run -d --name=redis02 \
  --device-read-iops=/dev/sda:1000 \
  --device-write-iops=/dev/sda:1000 \
  redis:7.0.10

# 设置IO权重(仅对CFQ调度器有效)
docker run -d --name=redis03 --blkio-weight=500 redis:7.0.10

磁盘 IO 限制参数说明:

参数说明
--device-read-bps设置设备读取速率上限
--device-write-bps设置设备写入速率上限
--device-read-iops设置设备读取 IOPS 上限
--device-write-iops设置设备写入 IOPS 上限
--blkio-weight设置 IO 权重(10-1000)

7.5.4 综合资源限制示例 ​

shell
# 创建一个资源受限的容器
docker run -d \
  --name=myapp \
  -m 1g \
  --memory-swap=2g \
  --cpus=2 \
  --cpuset-cpus="0,1" \
  --device-read-bps=/dev/sda:50mb \
  --device-write-bps=/dev/sda:50mb \
  --pids-limit=100 \
  nginx:latest

# 说明:
# - 内存限制1GB,内存+交换内存限制2GB
# - CPU限制2核,指定使用第0和第1核
# - 磁盘读写限制50MB/s
# - 进程数限制100个

7.5.5 查看容器资源使用情况 ​

shell
# 查看所有容器的资源使用情况
docker stats

# 查看指定容器的资源使用情况
docker stats myapp

# 不刷新显示(只显示一次)
docker stats --no-stream

# 自定义输出格式
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"

# 查看容器的资源限制配置
docker inspect myapp | grep -A 20 "HostConfig"

7.5.6 使用 Docker Compose 设置资源限制 ​

yaml
services:
  web:
    image: nginx:latest
    deploy:
      resources:
        limits:
          cpus: "0.5"
          memory: 512M
        reservations:
          cpus: "0.25"
          memory: 256M

八、Docker 命令总结 ​

8.1 Docker 命令速查表 ​

分类命令及常用参数核心功能实用示例
镜像管理docker pull [镜像:标签]拉取远程镜像docker pull nginx:1.25
docker build -t 镜像:标签 .基于 Dockerfile 构建镜像docker build -t myapp:1.0 .
docker images [-a]列出本地镜像(-a 显示所有)docker images -a
docker rmi [-f] 镜像 / ID删除镜像(-f 强制删除)docker rmi -f nginx:1.25
docker tag 镜像 新镜像:标签为镜像添加标签docker tag nginx:1.25 mynginx:v1
docker save -o file.tar 镜像导出镜像docker save -o nginx.tar nginx:1.25
docker load -i file.tar导入镜像docker load -i nginx.tar
容器管理docker run [-d -p -name] 镜像创建并启动容器docker run -d -p 80:80 --name web nginx
docker ps [-a]查看容器(-a 显示所有状态)docker ps -a
docker exec -it 容器 bash进入运行中容器docker exec -it web bash
docker logs [-f] 容器查看容器日志(-f 实时跟踪)docker logs -f web
docker rm [-f] 容器删除容器(-f 强制删除运行中)docker rm -f web
docker start/stop/restart 容器启动/停止/重启容器docker start web
docker cp 容器:路径 宿主机路径容器与宿主机间复制文件docker cp web:/app/log.txt ./
存储管理docker volume ls/create列出 / 创建数据卷docker volume create myvol
docker volume inspect 数据卷查看数据卷详情docker volume inspect myvol
docker volume rm 数据卷删除数据卷docker volume rm myvol
网络管理docker network ls/create列出 / 创建网络docker network create mynet
docker network connect 网络名 容器名将容器连接到网络docker network connect mynet web
Composedocker compose up [-d]启动 Compose 服务(-d 后台)docker compose up -d
docker compose down [-v]停止并删除服务(-v 删除卷)docker compose down -v
docker compose logs [-f]查看 Compose 服务日志docker compose logs -f

8.2 Docker 帮助文档 ​

Docker 中提供了很多命令,每一个命令也可以加很多的参数选项。把一个命令以及对应的参数选项都记住很显然是不太现实的。可以通过查看 Docker 帮助文档来学习 Docker 的常用命令以及参数选项的使用。

  • 帮助文档的使用如下所示:
shell
# 查询docker可以使用到的命令
docker --help

# 查询images命令的使用文档
docker images --help

# 查询run命令的使用文档
docker run --help

九、Docker 监控与日志管理 ​

9.1 Docker 日志管理 ​

9.1.1 查看容器日志 ​

shell
# 查看容器日志
docker logs 容器名称

# 实时查看日志(类似tail -f)
docker logs -f 容器名称

# 查看最后100行日志
docker logs --tail 100 容器名称

# 查看指定时间段的日志
docker logs --since 2023-01-01 容器名称
docker logs --since 1h 容器名称          # 最近1小时
docker logs --until 2023-12-31 容器名称

# 查看日志并显示时间戳
docker logs -t 容器名称

9.1.2 日志驱动配置 ​

Docker 支持多种日志驱动:

shell
# 创建容器时指定日志驱动
docker run -d \
  --name=nginx \
  --log-driver json-file \
  --log-opt max-size=10m \
  --log-opt max-file=3 \
  nginx:latest

# 常用日志驱动:
# json-file:默认驱动,日志以JSON格式存储
# syslog:将日志发送到syslog
# journald:将日志发送到systemd journal
# none:禁用日志
# local:本地日志驱动,性能更好

日志驱动配置示例:

shell
# 使用syslog日志驱动
docker run -d \
  --name=nginx \
  --log-driver syslog \
  --log-opt syslog-address=tcp://192.168.1.100:514 \
  nginx:latest

# 使用local日志驱动
docker run -d \
  --name=nginx \
  --log-driver local \
  --log-opt max-size=10m \
  --log-opt max-file=5 \
  nginx:latest

9.1.3 日志配置文件 ​

在 daemon.json 中配置默认日志驱动:

json
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

9.2 Docker 监控工具 ​

9.2.1 Docker 内置监控 ​

shell
# 实时监控容器资源使用
docker stats

# 查看容器进程信息
docker top 容器名称

# 查看容器详细信息
docker inspect 容器名称

# 查看容器事件
docker events

# 查看容器端口映射
docker port 容器名称

# 查看容器文件系统变化
docker diff 容器名称

9.2.2 cAdvisor(容器监控工具) ​

cAdvisor 是 Google 开源的容器监控工具:

shell
# 启动cAdvisor容器
docker run -d \
  --name=cadvisor \
  -p 8080:8080 \
  -v /:/rootfs:ro \
  -v /var/run:/var/run:ro \
  -v /sys:/sys:ro \
  -v /var/lib/docker/:/var/lib/docker:ro \
  google/cadvisor:latest

# 访问Web界面:http://localhost:8080

9.2.3 Prometheus + Grafana 监控方案 ​

yaml
# docker-compose.yml
services:
  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    command:
      - "--config.file=/etc/prometheus/prometheus.yml"

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-data:/var/lib/grafana

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - "--path.procfs=/host/proc"
      - "--path.sysfs=/host/sys"

volumes:
  grafana-data:

9.3 Docker 健康检查 ​

9.3.1 Dockerfile 中配置健康检查 ​

dockerfile
FROM nginx:latest

# 配置健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD curl -f http://localhost/ || exit 1

# 参数说明:
# --interval:检查间隔(默认30s)
# --timeout:超时时间(默认30s)
# --start-period:启动等待时间(默认0s)
# --retries:重试次数(默认3次)

9.3.2 运行时配置健康检查 ​

shell
# 创建容器时配置健康检查
docker run -d \
  --name=nginx \
  --health-cmd="curl -f http://localhost/ || exit 1" \
  --health-interval=30s \
  --health-timeout=3s \
  --health-retries=3 \
  nginx:latest

# 查看容器健康状态
docker inspect --format='{{.State.Health.Status}}' nginx

# 查看健康检查历史
docker inspect --format='{{json .State.Health}}' nginx | jq

9.3.3 Docker Compose 中配置健康检查 ​

yaml
services:
  web:
    image: nginx:latest
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/"]
      interval: 30s
      timeout: 3s
      retries: 3
      start_period: 5s

9.4 Docker 事件监控 ​

shell
# 实时监控Docker事件
docker events

# 过滤特定事件
docker events --filter type=container
docker events --filter event=start
docker events --filter container=nginx

# 监控指定时间范围的事件
docker events --since 1h
docker events --since "2023-01-01" --until "2023-12-31"

# 格式化输出
docker events --format '时间: {{.Time}} 类型: {{.Type}} 动作: {{.Action}}'

9.5 日志聚合方案 ​

9.5.1 ELK Stack(Elasticsearch + Logstash + Kibana) ​

yaml
# docker-compose.yml
services:
  elasticsearch:
    image: elasticsearch:8.8.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - "9200:9200"
    volumes:
      - es-data:/usr/share/elasticsearch/data

  logstash:
    image: logstash:8.8.0
    container_name: logstash
    volumes:
      - ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf
    ports:
      - "5044:5044"
    depends_on:
      - elasticsearch

  kibana:
    image: kibana:8.8.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch

volumes:
  es-data:

9.5.2 使用 Filebeat 收集 Docker 日志 ​

yaml
# filebeat.docker.yml
filebeat.inputs:
  - type: container
    paths:
      - "/var/lib/docker/containers/*/*.log"

output.elasticsearch:
  hosts: ["elasticsearch:9200"]
shell
# 启动Filebeat容器
docker run -d \
  --name=filebeat \
  --user=root \
  -v /var/lib/docker/containers:/var/lib/docker/containers:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  -v ./filebeat.docker.yml:/usr/share/filebeat/filebeat.yml:ro \
  elastic/filebeat:8.8.0

十、常见问题与解决方案 ​

10.1 镜像拉取失败 ​

问题:拉取镜像时出现连接超时或下载速度很慢

解决方案:

  1. 配置国内镜像加速器
  2. 使用代理服务器
  3. 使用私有镜像仓库

10.2 容器无法启动 ​

问题:容器启动后立即退出

解决方案:

  1. 查看容器日志:docker logs 容器名称
  2. 检查容器配置是否正确
  3. 检查镜像是否完整

10.3 端口冲突 ​

问题:启动容器时提示端口已被占用

解决方案:

  1. 查看端口占用情况:netstat -tunlp | grep 端口号
  2. 更换映射端口
  3. 停止占用端口的服务

10.4 磁盘空间不足 ​

问题:Docker 占用磁盘空间过大

解决方案:

shell
# 查看Docker磁盘使用情况
docker system df

# 清理未使用的镜像
docker image prune

# 清理未使用的容器
docker container prune

# 清理未使用的数据卷
docker volume prune

# 清理所有未使用的资源
docker system prune

10.5 容器时间不正确 ​

问题:容器内时间与宿主机时间不一致

解决方案:

shell
# 方案1:挂载宿主机时区文件
docker run -v /etc/localtime:/etc/localtime:ro 镜像名称

# 方案2:设置环境变量
docker run -e TZ=Asia/Shanghai 镜像名称

10.6 容器无法访问外网 ​

问题:容器内部无法访问外部网络

解决方案:

  1. 检查宿主机网络是否正常
  2. 检查 Docker 网络配置
  3. 重启 Docker 服务

10.7 Docker 最佳实践 ​

  1. 镜像优化

    • 使用多阶段构建减小镜像大小
    • 合并 RUN 指令减少镜像层数
    • 使用.dockerignore 排除不需要的文件
  2. 容器管理

    • 使用数据卷持久化数据
    • 设置容器资源限制
    • 使用健康检查
  3. 安全建议

    • 不要使用 root 用户运行容器
    • 定期更新基础镜像
    • 使用私有镜像仓库
  4. 性能优化

    • 合理设置容器资源限制
    • 使用合适的存储驱动
    • 优化 Dockerfile 构建过程