Docker概述

Docker 是一个开源的容器化平台,旨在简化应用程序的开发、部署和运行过程。它通过将应用程序及其所有依赖项打包进一个可移植的容器中,实现了应用程序与基础架构的解耦,从而加快了软件交付的速度并增强了其可移植性。百度百科 Docker 详细介绍。

🏷️关键特性包括:

  • 轻量级: Docker容器相比于传统的虚拟机更加轻量,因为它们不需要额外的操作系统层,而是直接在主机的内核上运行。这使得容器启动迅速,资源利用率高。
  • 隔离性: 尽管轻量,Docker容器仍然提供了良好的隔离性。每个容器都在自己的进程中运行,拥有独立的文件系统、CPU、内存、网络等资源,确保应用之间互不干扰。
  • 可移植性: Docker容器能够在任何支持Docker的环境中一致地运行,不论是在开发者的笔记本电脑、测试服务器还是生产环境,消除了“在我机器上能跑”的问题。
  • 快速交付: Docker通过镜像(Images)来定义和封装应用及环境配置,使得从开发到测试再到生产的整个流程变得高效且一致。
  • DevOps友好: Docker整合了开发和运维的工作流,使得持续集成、持续部署(CI/CD)更加顺畅。Dockerfile的使用让构建过程可脚本化、可重复。
  • 生态系统丰富: Docker拥有庞大的社区支持和丰富的工具链,如Docker Compose用于定义和运行多容器应用,Docker Swarm和Kubernetes用于容器编排,以及Docker Hub作为官方的镜像仓库,便于镜像的分享和管理。

💗💗综上所述,Docker通过提供标准化的打包和运行环境,极大地提高了软件开发和运维的效率,是现代云原生应用架构中的关键技术之一。

Docker安装

安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 旧版本的Docker被称为docker或docker-engine。在尝试安装新版本之前,请卸载任何此类旧版本以及相关依赖项。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

# 安装 yum 扩展命令行工具
sudo yum install -y yum-utils

# 设置 yum 镜像源地址
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 更新 yum 软件包索引
yum makecache fast

# 安装 docker
yum install docker-ce docker-ce-cli containerd.io

# 启动 docker
systemctl start docker

# docker 版本查看
docker -version

# 通过运行 hello-world 验证 Docker 引擎是否安装成功
# 要是 hello-world 镜像拉取不下来的话,配置一下镜像加速器
docker run hello-world

⭐⭐组件介绍

docker-ce, docker-ce-cli, 和 containerd.io 是构成 Docker 容器运行环境的关键组件,它们在 Docker 的安装和运行中扮演不同的角色:

  • docker-ce (Docker Community Edition):这是 Docker 的社区版,包含了完整的容器化平台。它提供了 Docker 守护进程 (dockerd),该进程负责管理容器的创建、运行、暂停、恢复和删除等操作。此外,Docker CE 也包括了 Docker 命令行界面 (CLI),使得用户能够通过命令行与 Docker 守护进程交互,执行诸如构建镜像、运行容器、管理网络和数据卷等任务。Docker CE 是面向开发者、系统管理员和希望使用最新 Docker 功能的用户的首选版本。
  • docker-ce-cli: 功能: 这是 Docker 的命令行客户端工具。尽管它通常随 Docker CE 一起安装,但也可以单独安装或升级。它允许用户通过命令行执行 Docker 相关操作,比如构建镜像 (docker build)、运行容器 (docker run)、查看容器状态 (docker ps) 等。当你只需要命令行工具与远程 Docker 守护进程交互,而不需要在本地主机上运行容器时,单独安装此 CLI 就非常有用。
  • containerd.io: Containerd 是一个低级别的容器运行时(启动容器通常需要运行一个专门的工具来配置内核来运行容器,这些工具也被称为“容器运行时”。),它负责容器和镜像的生命周期管理,包括镜像的传输、存储、容器的执行和监控等底层操作。它是 Docker 守护进程 (dockerd) 之下的一个组件,为 Docker 提供了与操作系统进行交互的能力。Containerd 设计为高度模块化和可嵌入式的,不仅服务于 Docker,还可以被其他容器管理系统使用,比如 Kubernetes。它是 Docker 实例化和管理容器的核心依赖。

💗💗综上所述,docker-ce 提供了用户与 Docker 交互的主要界面和后台服务,docker-ce-cli 是与这些服务进行命令行交互的工具,而 containerd.io 则是负责底层容器运行时的管理和协调。这三个组件共同构成了 Docker 在 Linux 系统上的运行环境。

配置

⭐⭐配置阿里云镜像加速

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1. 注册一个阿里云账号
# 2. 进入容器镜像服务 --> 镜像工具 --> 镜像加速器 --> 获取加速器地址(https://xxxxxxxx.mirror.aliyuncs.com)
# 3. 通过修改daemon配置文件/etc/docker/daemon.json来使用加速器(如果没有这个文件的话就新建一个,有的话直接修改)
vi /etc/docker/daemon.json
# 要添加的内容:地址换成你的地址,保存并退出
{
"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"]
}
# 重启 docker
sudo systemctl daemon-reload
sudo systemctl restart docker
# 查看 docker 信息,看是否配置成功
# 存在 Registry Mirrors: https://xxxxxxxx.mirror.aliyuncs.com 说明配置完成
docker info

卸载

1
2
3
4
5
# 卸载docker
sudo yum remove docker-ce docker-ce-cli containerd.io
# 删除目录
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

Docker镜像

镜像的概念

Docker镜像(Image)是Docker技术中的核心组件,它是创建和运行Docker容器的前提。镜像可以被理解为轻量级、可执行的独立软件包。它包含运行某个应用所需的所有内容,包括代码、运行时、库、环境变量和配置文件。形象地说,Docker镜像相当于是一个面向应用的、预配置的操作系统环境模板。

⭐⭐镜像的特性:

  • 只读性:Docker镜像是只读的,这意味着一旦创建,就不能被修改。当你运行一个容器时,Docker会在镜像的顶部添加一个可写层,这个可写层用来保存容器运行时产生的变化,而底下的镜像层保持不变。
  • 分层存储:Docker镜像是由多层组成的,每一层代表了文件系统的一系列变更。这种分层结构使得镜像的构建高效且灵活,因为它可以重用已有的层,避免重复下载相同的内容。当从Docker Hub或其他仓库下载镜像时,实际上是在下载构成该镜像的所有层。
  • 可移植性:因为镜像包含了运行应用所需的一切,所以它可以在任何支持Docker的环境中一致地运行,无论是开发者的笔记本、测试服务器还是生产环境。
  • 版本控制:镜像可以通过标签(tag)来标记不同的版本,便于管理和识别。例如,你可以有ubuntu:latest、nginx:1.18这样的标签来分别指代最新版的Ubuntu镜像或特定版本的Nginx镜像。

⭐⭐构建和管理镜像:

  • 构建:镜像通常是通过DockerFile来构建的,DockerFile是一个文本文件,其中包含了逐条指令来定义如何从基础镜像开始,一步步添加文件、安装软件、设置环境变量等,最终形成新的镜像。
  • 拉取:使用docker pull命令可以从Docker Hub或其他注册中心下载镜像到本地。
  • 查看:使用docker images命令可以列出本地的所有镜像及其相关信息,如镜像ID、创建时间、大小和标签。
  • 运行:通过docker run命令可以基于镜像创建并启动一个新的容器,此时Docker会在镜像的顶部添加一个可写层作为容器的运行环境。
  • 推送:使用docker push命令可以将自己的镜像推送到Docker Hub或其他私有仓库,以便他人或自己在其他地方使用。

⭐⭐Docker镜像的设计理念极大地简化了应用的打包、分发和部署过程,使得应用程序能够在几乎任何基础设施上以一致的方式运行,这是容器化技术的一大优势。

联合文件系统

Docker的联合文件系统(Union File System,简称UnionFS)是一种特殊的文件系统技术,它允许将多个文件系统层叠加在一起,形成一个单一的可读写的文件系统视图。

这种分层的文件系统设计是Docker的核心技术之一,它极大地促进了Docker镜像的高效创建、管理和容器的快速启动。

在Docker中,每个镜像由一系列只读层组成,最顶层通常是一个可写层(当容器运行时生成)。这些层是相互依赖的,下层的更改不会影响上层,而上层可以添加、修改或删除文件,看起来就像是一个统一的文件系统。

⭐⭐关键优势:

  • 镜像复用:因为层是可以共享的,所以多个容器可以共用相同的底层镜像,节省了存储空间。
  • 快速部署:新容器可以从现有镜像的层快速启动,只需添加一个可写层用于保存状态和差异。
  • 高效的镜像传输:当推送或拉取镜像时,只需要传输层间差异,而非整个文件系统。
  • 易于理解的分层结构:每个操作(如安装软件包)都可以视为一个新的层,便于追踪和回滚。

⭐⭐Docker支持多种联合文件系统的实现,包括但不限于:

  • AUFS(Advanced Multi-Layered Unification Filesystem):早期Docker默认使用的实现,支持精细的权限控制和高效的层管理。
  • OverlayFS:Linux内核原生支持的轻量级联合文件系统,逐渐成为许多现代Docker部署的首选。
  • Btrfs:一种具有高级特性的文件系统,支持快照和子卷,也可用于Docker存储。
  • VFS(Virtual File System):Docker的一个基本实现,兼容性好但性能较低。
  • ZFS:一个功能丰富的文件系统,支持高级存储功能,如数据校验和自动修复。
  • Device Mapper:Linux内核中用于创建逻辑设备的框架,也用于Docker存储驱动。

分层存储

Docker镜像的分层存储是基于联合文件系统(Union File System)实现,每一个镜像由多个只读层(read-only layers)组成,这些层叠加在一起形成了一个完整的文件系统。

Docker镜像的最底层是 bootfs(boot file system),它主要包含操作系统的 bootloader(引导加载器)和 kernel(内核)。bootloader主要是引导加载kernel。它提供了系统启动时所需的最基本的文件和程序,包括能够加载和初始化 Linux 内核的组件。

Docker镜像的最底层 bootfs 之上就是 rootfs (root file system),包含了系统运行所需的最基本目录结构和文件。

Docker镜像的最上面的一层通常是可写层(writable layer)这一层就是我们通常说的容器层,容器之下的都叫镜像层,运行 docker run 命令时创建该层,主要用于保存容器运行时产生的数据和改动。

这种分层的设计使得镜像的构建、存储和传输都极为高效。

⭐⭐层的特性:

  • 不可变性:一旦创建,层的内容就不能被修改。这种设计保证了镜像的可重现性和一致性。
  • 复用性:多个镜像可以共享相同的底层层,减少了存储空间的需求。
  • 轻量级:当提交一个新的层时,实际上只是记录了相对于前一层所做的更改,而不是整个文件系统的拷贝。
  • 写时复制(COW, Copy-On-Write):当需要修改文件时,该文件首先会被复制到一个新的层中,然后在新层上进行修改。这样的话,原始层保持不变,确保了高效性和资源利用率。

Docker命令

镜像命令

镜像列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
docker images
docker image ls
docker image list

# 列表项含义
# REPOSITORY 镜像的名称
# TAG 镜像的标签
# IMAGE ID 镜像的id
# CREATED 镜像的创建时间
# SIZE 镜像的大小

# 可选项
-a, --all # 列出所有镜像
-f, --filter filter # 根据所提供的条件过滤输出
--format string # 使用自定义模板格式化输出:
# 'table': 以带有列标题的表格式打印输出(默认值)
# 'table TEMPLATE': 使用给定的Go模板以表格格式打印输出
# 'json': 以json格式打印
# 'TEMPLATE': 使用给定的Go模板打印输出
--no-trunc # 不截断输出
-q, --quiet # 仅显示镜像ID

# 举例
docker images -aq # 镜像列表只显示ID
docker images --digests # 查看镜像摘要
docker images -f "dangling=true" # 查看标签为 <none> 的镜像
docker images -f "reference=latest" # 查看所有标签为 latest 的镜像
docker images --format "{{json .}}" # 输出 JSON 格式的信息
# 以表格形式显示镜像的仓库名、标签和大小
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"

搜索镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
docker search image_name

# 列表项含义
# NAME 镜像名称
# DESCRIPTION 镜像描述
# STARS 镜像点赞数量
# OFFICIAL 镜像是否为官方镜像

# 可选项
-f, --filter filter # 根据提供的条件过滤输出
--format string # 使用Go模板进行好看的打印搜索
--limit int # 搜索结果的最大数量
--no-trunc # 不截断输出

# 举例
docker search mysql --filter=STARS=3000 # 搜索点赞数量大于3000的 mysql 镜像
docker search -f is-official=true zookeeper # 查询官方镜像
docker search --limit=5 zookeeper # 展示5条

下载镜像

1
2
3
4
5
6
7
8
9
10
11
12
# [:tag] 镜像的版本号,如果不写,则下载最新版
docker pull image_name[:tag]

# 可选项
-a, --all-tags # 下载存储库中所有已标记的图像
--disable-content-trust # 跳过镜像验证(默认为true)
--platform string # 如果服务器支持多平台,则设置平台
-q, --quiet # 禁止详细输出

# 举例
docker pull mysql # 根据镜像名称下载镜像
docker pull docker.io/library/mysql:latest # 根据镜像详细地址下载镜像

删除镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
docker rmi image_id     # 根据镜像ID删除镜像
docker rmi image_name # 根据镜像名称删除镜像

# 可选项
-f, --force # 解除占用,强制删除镜像
--no-prune # 不删除未标记的父对象

# 举例
docker rmi -f e73346bdf465 # 删除镜像ID为e73346bdf465的镜像
docker rmi -f mysql # 删除镜像名称为 mysql 的镜像
docker rmi -f $(docker images -aq) # 复合删除操作:查询出所有的镜像ID,然后删除
docker image prune # 删除所有未被任何容器使用的镜像以及它们的未被其他镜像共享的层
docker system prune # 删除所有未运行的容器、网络、玄虚镜像、数据卷

镜像提交

1
2
3
4
5
6
7
8
9
10
11
12
# 提交容器成为一个新的镜像
# 提交到本地,可以通过 docker images 命令查看镜像列表
docker commit CONTAINER_ID image_name:[tag]

# 可选项
-a, --author string # 作者名称
-c, --change list # 对创建的镜像应用Dockerfile指令
-m, --message string # 提交消息
-p, --pause # 提交时暂停容器(默认为true)

# 举例
docker commit -a="作者名称" -m="提交的描述信息" d1e5e331a66a test-image-name:1.0.0

镜像推送

1
2
3
4
5
6
7
8
9
10
# 推送到阿里云
# 首先需要注册一个阿里云账号
# 开通阿里云容器镜像服务
# 创建一个镜像仓库
# 使用Docker登录镜像仓库
docker login --username=阿里云登录用户名 registry.cn-beijing.aliyuncs.com
# 为推送的镜像打标签
docker tag [ImageId] registry.cn-beijing.aliyuncs.com/命名空间/仓库名称:[镜像版本号]
# 推送
docker push registry.cn-beijing.aliyuncs.com/命名空间/仓库名称:[镜像版本号]

镜像打包

⭐⭐可以把镜像打成一个压缩包,自己保存或者发送给别人。

1
2
3
4
5
6
7
8
9
docker save image_id ...

# 可选项
-o, --output string # 打包后压缩包存放路径

# 举例:多个镜像打包为一个压缩包,压缩包的名字为 test,存放到 home 的 docker 目录下
docker save -o /home/docker/test.jar a3e63372bd98 605c77e624dd
# 第二种写法
docker save a3e63372bd98 605c77e624dd > /home/docker/test.jar

镜像加载

⭐⭐可以把镜像打成一个压缩包,自己保存或者发送给别人。

1
2
3
4
5
6
7
8
9
10
docker save image_id ...

# 可选项
-i, --input string # 指定要加载的 tar 文件的路径
-q, --quiet # 精简输出信息,只显示必要的进度信息

# 举例
docker load -i /home/docker/test.jar
# 或者
docker load < /home/docker/test.jar

容器命令

运行容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
docker run [可选项] image_id
docker run [可选项] image_name

# 可选项太多,可使用 help 命令查看
docker run --help

# 常用选项
--name="container_name" # 自定义容器名字 tomcat01、tomcat02,用来区分容器
-d # 后台方式运行
-it # 使用交互方式运行
-P # 为容器内部所有端口随机分配宿主机的端口号
-p # 指定容器的端口
-p 容器端口
-p 主机端口:容器端口
-p ip:主机端口:容器端口

# 举例
docker run -it centos /bin/bash # 使用交互方式启动容器
# 后台方式运行 nginx 容器,将容器命名为 nginx01,并且让主机的3344端口映射到容器的80端口
docker run -d --name nginx01 -p 3344:80 nginx
curl localhost:3344 # 测试是否成功

# 注意
# docker 容器使用后台运行,就必须要有要一个应用,docker发现没有应用,就会自动停止
docker run -d centos

创建容器

⭐⭐ 创建容器命令与运行容器命令基本一致。创建容器命令:只创建不运行。运行容器命令:创建并运行。

1
docker create [可选项] image_id

查看列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
docker ps       # 查看正在运行的容器列表

# 列表项含义
# CONTAINER ID 容器的唯一标识符
# IMAGE 创建该容器所使用的镜像名称
# COMMAND 启动容器时执行的命令,默认情况下显示的是容器的主进程。
# CREATED 容器的创建时间
# STATUS 容器的当前状态、容器启动或停止的具体时间信息
# PORTS 容器公开的端口映射信息
# NAMES 容器的名称。Docker 自动为每个容器分配一个唯一的名称,用户也可以在运行容器时自定义名称。

# 可选项
-a, --all # 显示所有容器(正在运行的和以往运行过的容器)
-f, --filter filter # 根据提供的条件过滤输出
--format string # 使用自定义模板格式化输出:
# 'table': 以带有列标题的表格式打印输出(默认值)
# 'table TEMPLATE': 使用给定的Go模板以表格格式打印输出
# 'json': 以json格式打印
# 'TEMPLATE': 使用给定的Go模板打印输出
-n, --last int # 显示n个上次创建的容器(包括所有状态)(默认值-1)
-l, --latest # 显示最新创建的容器(包括所有状态)
--no-trunc # 不截断输出
-q, --quiet # 仅显示容器ID
-s, --size # 显示文件总大小

# 举例
docker ps -a # 查看所有容器列表

删除容器

1
2
3
4
5
6
7
8
9
10
11
12
docker rm CONTAINER_ID     # 根据容器ID删除容器
docker rm NAMES # 根据容器名称删除容器

# 可选项
-f, --force # 强制删除容器
-l, --link # 删除指定的链接
-v, --volumes # 删除与容器关联的匿名卷

# 举例
docker rm -f c3180317543b # 删除容器ID为e73346bdf465的镜像
docker rm -f $(docker ps -aq) # 复合删除操作:查询出所有的容器ID,然后删除
docker ps -aq | xargs docker rm -f # 也可以通过 Linux 管道符操作,删除全部容器

查看日志

1
2
3
4
5
6
7
8
9
10
11
12
docker logs CONTAINER_ID        # 根据容器ID查看日志

# 可选项
--details # 显示提供给日志的额外详细信息
-f, --follow # 跟踪日志输出
--since string # 显示时间戳 (e.g. "2013-01-02T13:23:37Z") 或相对时间戳 (e.g. "42m" for 42 minutes) 以来的日志
-n, --tail string # 从日志末尾开始显示的行数(默认为“all”)
-t, --timestamps # 显示时间戳
--until string # 显示时间戳 (e.g. "2013-01-02T13:23:37Z") 或相对时间戳 (e.g. "42m" for 42 minutes)以来的日志

# 举例
docker logs -tf --tail 10 d1e5e331a66a # 显示10条带有时间戳的日志,并跟踪日志输出

拷贝文件

1
2
3
4
## 从容器拷贝文件到宿主机
docker cp d1e5e331a66a:/home/test.java /home
## 从宿主机拷贝文件到容器
docker cp my.tar d1e5e331a66a:/root

容器启动与停止

1
2
3
4
docker start CONTAINER_ID       # 根据容器ID启动容器
docker restart CONTAINER_ID # 根据容器ID重启容器
docker stop CONTAINER_ID # 根据容器ID停止正在运行的容器
docker kill CONTAINER_ID # 根据容器ID杀死正在运行的容器

进入正在运行的容器

1
2
3
4
5
6
7
8
# 进入容器后开启一个新的终端
docker exec -it CONTAINER_ID 命令行
# 进入容器正在执行的终端
docker attach CONTAINER_ID

# 举例
docker exec -it d1e5e331a66a /bin/bash # 进入正在运行的 centos 容器
docker exec -it -w /root d1e5e331a66a /bin/bash # 进入正在运行的 centos 容器,并到root目录下

退出正在运行的容器

1
2
exit                                # 停止运行容器并退出
Ctrl + P + Q # 不停止容器退出

查看容器内部的进程

1
docker top CONTAINER_ID

查看容器的元数据

1
docker inspect CONTAINER_ID

导出/导入容器

1
2
3
4
# 导出容器
docker export /home/tomcat-export.tar d1e5e331a66a
# 导入容器
docker import /home/tomcat-export.tar tomcat-export:1.0

暂停容器服务

⭐⭐ 暂停容器对外提供服务,并不是暂停容器

1
2
3
4
# 暂停
docker pause d1e5e331a66a
# 取消暂停
docker unpause d1e5e331a66a

容器数据卷

数据卷的概念

Docker容器数据卷是一种由Docker管理的、用于存储容器数据的独立于容器的持久化存储机制。它是宿主机上的一个特定目录,可以被挂载到一个或多个Docker容器中。在宿主机中的这个文件/目录就称为数据卷,而容器中的这个关联文件/目录则称为该数据卷在该容器中的挂载点。

数据卷的设计旨在解决容器数据的持久化问题,确保数据不因容器的创建、启动、停止或删除等操作而丢失。使用数据卷是实现 Docker 应用数据持久化和容器间数据共享的有效方式,它增强了容器化应用的数据管理灵活性和可靠性。

⭐⭐数据卷的关键特性:

  • 持久性:数据卷中的数据会在容器被删除后继续存在,因为数据卷拥有自己的生命周期,独立于任何单个容器。
  • 可共享性:多个容器可以同时挂载同一个数据卷,实现数据的实时共享。
  • 宿主机集成:数据卷直接在宿主机的文件系统中存在,可以轻松地从宿主机备份、恢复或直接访问数据卷内容。
  • 绕过UnionFS:数据卷不使用Docker的联合文件系统(UnionFS),这意味着对数据卷内数据的修改是直接的,不会受到容器读写层的影响,提高了I/O性能。
  • 命名卷与匿名卷:
    • 命名卷:由用户显式创建并命名,便于在多个容器间共享和管理。
    • 匿名卷:在容器创建时自动创建且未命名,主要用于临时存储或一次性使用的场景。
  • 数据卷容器:一种特殊的容器,其主要作用是提供数据卷供其他容器挂载,这样可以更容易地管理和迁移数据。

指定宿主机路径挂载卷

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 交互方式启动 centos 镜像
# /home/ceshi 宿主机数据路径
# /home 容器的数据路径
docker run -it -v /home/ceshi:/home centos /bin/bash

# 启动 mysql 镜像
docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
# 命令解析
docker run -d # 以后台运行的方式启动
-p 3306:3306 # 端口映射:宿主机3306端口映射到mysql容器的3306端口
-v /home/mysql/conf:/etc/mysql/conf.d # 数据卷挂载:宿主机mysql文件的储存路径 : mysql容器的配置文件路径
-v /home/mysql/data:/var/lib/mysql # 数据卷挂载:宿主机mysql数据的储存路径 : mysql容器的数据储存路径
-e MYSQL_ROOT_PASSWORD=123456 # 容器的配置信息:配置mysql容器的登录密码
--name mysql01 # 给容器命名
mysql:5.7 # 启动容器:版本号

不指定宿主机路径挂载卷

⭐⭐不指定路径挂载卷默认保存到 –> /var/lib/docker/volumes 目录下

匿名卷

1
2
3
4
5
6
# -d 后台启动镜像
# -P 随机分配宿主机的端口号
# 匿名挂载卷:/etc/nginx 对应的是容器中要挂载的路径
# --name 将容器名称命名为 nginx01
# nginx:latest 启动最新版本的 nginx 镜像
docker run -d -P -v /etc/nginx --name nginx01 nginx:latest

命名卷

1
2
3
4
5
6
7
8
# -d 后台启动镜像
# -P 随机分配宿主机的端口号
# 匿名挂载卷:zdy-name:/etc/nginx
# zdy-name-nginx:自定义卷的名称
# /etc/nginx:对应的是容器中要挂载的路径
# --name 将容器名称命名为 nginx02
# nginx:latest 启动最新版本的 nginx 镜像
docker run -d -P -v zdy-name-nginx:/etc/nginx --name nginx02 nginx:latest

卷的操作

1
2
3
4
5
6
7
8
9
10
11
docker volume [指令]

# 指令
create # 创建卷
inspect # 显示一个或多个卷的详细信息
ls # 列出卷
prune # 删除未使用的本地卷
rm # 删除一个或多个卷

# 举例
docker volume ls # 查看卷的列表

数据卷共享

当一个容器与另一个容器使用相同的数据卷时,就称这两个容器实现了“数据卷共享”。

当一个 容器C 启动运行时创建并挂载了数据卷,若其它容器也需要共享 容器C 挂载的数据卷,这些容器只需在 docker run 启动时通过 –volumes-from[容器C] 选项即可实现数据卷共享。此时 容器C 就称为数据卷容器。

1
docker run --name myubuntu --volumes-from 容器C -it ubuntu /bin/bash

数据卷的权限

1
2
3
4
# 容器对于卷(ro):只读(在容器内部不能修改数据)
docker run -d -P -v zdy-name-nginx:/etc/nginx:ro nginx
# 容器对于卷(rw):可读写(在容器内部可读写)
docker run -d -P -v zdy-name-nginx:/etc/nginx:rw nginx

💗💗一旦设置了容器的权限则不可修改,默认是可读写权限。

DockerFile

DockerFile的概念

Dockerfile 是一个用于自动化构建 Docker 镜像的文本文件,其中包含了一系列命令和说明。这些命令用于定制基础镜像、安装软件、设置环境变量、暴露端口、指定运行时的工作目录和启动命令等,创建一个全新的 Docker 镜像。Dockerfile 的命令类似于 Linux 的 shell 命令,使得开发人员能够以简洁的方式表述镜像的构建过程。

⭐⭐Dockerfile 的基本结构通常包括以下几个部分:

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 容器启动时执行指令

💗💗Docker 引擎会读取 Dockerfile 中的指令,依次执行它们,最终生成一个新的 Docker 镜像。这种方式标准化了应用的打包和部署流程,提高了可移植性和重复性。

基本结构与指令

基础镜像信息

基础镜像信息在 Dockerfile 中通过 FROM 指令指定,这是 Dockerfile 中的第一条也是必备的指令。

基础镜像实际上是构建新镜像的起始点,它可以是一个预定义的操作系统镜像,如 Ubuntu、Alpine Linux、CentOS,或者是一个包含了特定运行环境的镜像,比如带有 Java 运行时的 OpenJDK 镜像、Python 的官方镜像等。

也可以用一个特殊的 Docker 镜像:scratch 镜像。scratch 镜像表示一个完全空白的基础镜像。它不包含任何操作系统层或文件系统,实质上是一个空的、无父镜像的状态。当你使用 FROM scratch 开始一个 Dockerfile 时,意味着你正在从零开始创建一个镜像,你需要手动添加所有需要的文件和二进制执行文件。

⭐⭐ FROM 指令:添加基础镜像。

1
2
3
4
5
# 格式: FROM <image>:<tag>
# 举例一
FROM scratch
# 举例二
FROM centos:7.9.0

维护者信息

⭐⭐ MAINTAINER 指令:添加维护者信息。

1
2
3
4
5
6
# 举例一
MAINTAINER name
# 举例二
MAINTAINER name@example.com
# 举例三
MAINTAINER 名字<name@example.com>

⭐⭐ LABEL 指令:为生成的镜像添加元数据标签。随着 Docker 的发展,推荐的做法是使用 LABEL 指令来添加。

1
2
3
4
5
# 格式: LABEL <key>=<value> <key>=<value> ...
# 举例
LABEL author.name="your-name" \
author.email="your-name@example.com" \
description="A custom Docker image maintained by your-name."

镜像操作指令

⭐⭐ WORKDIR 指令:设置镜像的工作目录,后续的RUN、CMD、ENTRYPOINT指令将在这个目录下执行。

1
2
# 格式: WORKDIR <dir>
WORKDIR /app

⭐⭐ VOLUME 指令:创建一个挂载点,用于持久化数据。

1
2
3
4
5
# 格式: VOLUME ["/data"]
VOLUME ["/var/log/nginx", "/etc/nginx/conf.d"]

# 创建好后的使用方式
docker run -d -v /host/path/to/data:/data my_image

⭐⭐ RUN 指令:在构建镜像的过程中执行命令,可以是shell命令或执行器命令。

1
2
# 格式: RUN <command> 或 RUN ["executable", "param1", "param2"]
RUN apt-get update && apt-get install -y nginx

⭐⭐ COPY 指令:在构建镜像的过程中执行命令,可以是shell命令或执行器命令。

1
2
# 格式: COPY <src>... <dest>
COPY package.json /app/

⭐⭐ ADD 指令:类似COPY,但能处理URL和自动解压tar文件。

1
2
3
# 格式: ADD <src>... <dest>
ADD file.tar.gz /app/
ADD https://example.com/archive.tar.gz /app/

⭐⭐ ENV 指令:设置环境变量,在构建过程及容器运行时生效。

1
2
# 格式: ENV <key>=<value> <key>=<value> ...
ENV MYSQL_ROOT_PASSWORD=123456

⭐⭐ EXPOSE 指令:声明容器运行时监听的端口,供宿主机或其他服务访问。

1
2
3
4
5
# 格式: EXPOSE <port> [<port>...]
EXPOSE 8080 80 443

# 创建好后的使用方式
docker run -p 10000:8080 -p 80:80 -p 443:443 nginx

⭐⭐ ARG 指令:定义构建时变量,可以在构建过程中使用这些变量。

1
2
3
4
5
6
7
8
9
# 格式: ARG <name>[=<default value>]
ARG VERSION=1.0

# 使用构建参数
LABEL version=$VERSION
# 如果需要在 RUN 指令中使用并且变量名包含特殊字符或空格,推荐使用 JSON 格式
RUN ["echo", "Building version", "${VERSION}"]
# 当你使用 docker build 命令构建镜像时,可以通过 --build-arg 选项覆盖 Dockerfile 中定义的 ARG 变量的默认值
docker build --build-arg VERSION=2.0 -t my-image-name .

⭐⭐ SHELL 指令:设置默认的shell。

1
2
# 格式: SHELL ["executable", "param1", "param2"]
SHELL ["/bin/bash", "-c"]

⭐⭐ ONBUILD 指令:触发指令,当基于当前镜像构建新镜像时,这些指令会被执行。

1
2
# 格式: ONBUILD <instruction>
ONBUILD RUN npm install

⭐⭐ USER 指令:指定运行容器时的用户和用户组。

1
2
# 格式: USER <user>[:<group>]
USER nobody:nogroup

⭐⭐ STOPSIGNAL 指令:设置容器停止时发送的系统信号,默认是 SIGTERM。

1
2
# 格式: STOPSIGNAL signal
STOPSIGNAL SIGINT

⭐⭐ HEALTHCHECK 指令:配置健康检查命令,用于判断容器服务是否健康运行。

1
2
# 格式: HEALTHCHECK [OPTIONS] CMD command
HEALTHCHECK --interval=5s --timeout=3s CMD curl -f http://localhost/ || exit 1

容器启动时执行指令

⭐⭐ CMD 指令:容器启动时默认执行的命令,可以被docker run命令行参数覆盖。

1
2
# 格式: CMD ["executable", "param1", "param2"] 或 CMD command param1 param2
CMD ["npm", "start"]

⭐⭐ ENTRYPOINT 指令:容器启动时默认执行的命令,不会被docker run的命令行参数覆盖,命令行参数会追加到 ENTRYPOINT 指令后边。

1
2
# 格式: ENTRYPOINT ["executable", "param1", "param2"] 或 ENTRYPOINT command param1 param2(直接执行不会拼接)
ENTRYPOINT ls -a

构建Docker镜像

编写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
cd /home
mkdir -p docker/tomcat/bulid
touch Dockerfile
vi Dockerfile
# ---------------- vi 编辑器 --------------------
FROM centos
LABEL author.name="szxck" author.url="szxck.top"

ARG MYPATH=/usr/local

WORKDIR ${MYPATH}

RUN rm -f /etc/yum.repos.d/*.repo

RUN echo "[AppStream]" >> CentOS-Linux-AppStream.repo
RUN echo "name=AppStream" >> CentOS-Linux-AppStream.repo
RUN echo "baseurl=https://mirrors.aliyun.com/centos/\$releasever/AppStream/\$basearch/os/" >> CentOS-Linux-AppStream.repo
RUN echo "gpgcheck=1" >> CentOS-Linux-AppStream.repo
RUN echo "enabled=1" >> CentOS-Linux-AppStream.repo
RUN echo "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial" >> CentOS-Linux-AppStream.repo

RUN echo "[BaseOS]" >> CentOS-Linux-BaseOS.repo
RUN echo "name=CentOS-\$releasever - BaseOS" >> CentOS-Linux-BaseOS.repo
RUN echo "baseurl=https://mirrors.aliyun.com/centos/\$releasever/BaseOS/\$basearch/os/" >> CentOS-Linux-BaseOS.repo
RUN echo "gpgcheck=1" >> CentOS-Linux-BaseOS.repo
RUN echo "enabled=1" >> CentOS-Linux-BaseOS.repo
RUN echo "gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial" >> CentOS-Linux-BaseOS.repo

RUN mv CentOS-Linux-AppStream.repo /etc/yum.repos.d/
RUN mv CentOS-Linux-BaseOS.repo /etc/yum.repos.d/

RUN yum clean all
RUN yum makecache
RUN yum install -y vim

ADD jdk-8u11-linux-x64.tar.gz ${MYPATH}/java
ADD apache-tomcat-8.5.100.tar.gz ${MYPATH}/tomcat

ENV JAVA_HOME=${MYPATH}/java/jdk1.8.0_11
ENV CLASSPATH=$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV TMOCAT_HOME=${MYPATH}/tomcat/apache-tomcat-8.5.100
ENV TMOCAT_BASH=${MYPATH}/tomcat/apache-tomcat-8.5.100
ENV PATH=$PATH:$JAVA_HOME/bin:${TMOCAT_HOME}/lib:${TMOCAT_HOME}/bin

EXPOSE 8080

CMD /usr/local/tomcat/apache-tomcat-8.5.100/bin/startup.sh && tail -F /usr/local/tomcat/apache-tomcat-8.5.100/logs/catalina.out
# ----------------------------------------------

构建镜像

1
2
3
4
5
# 命令行最后的 点(表示当前目录) 不要忘记
docker build -f Dockerfile -t tomcat-centos:1.0 .

# 构建成功后可以查看镜像的构建流程
docker history image_id

运行镜像

1
2
3
4
5
# 运行构建好的镜像
docker run -d -p 10000:8080 -v /home/docker/tomcat/test:/usr/local/tomcat/apache-tomcat-8.5.100/webapps/test -v /home/docker/tomcat/logs:/usr/local/tomcat/apache-tomcat-8.5.100/logs --name tomcat-centos tomcat-centos:1.0

# 测试
curl localhost:10000

构建运行Jar包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 首先把jar包上传到Linux

# 编写Dockerfile文件
vi Dockerfile
# ---------------- vi 编辑器 --------------------
FROM openjdk:8u102
LABEL auth=szxck email=suzengxin@foxmail.com
WORKDIR /home
COPY app.jar /home/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# ----------------------------------------------

# 构建镜像
docker build -t hello-docker .

# 运行镜像
docker run -d -p 8080:8080 --name hello-docker hello-docker

# 测试
curl localhost:8080

Docker缓存机制

Docker Build 缓存机制是为了加速镜像构建过程而设计的。当你使用 docker build 命令来创建一个新的镜像时,Docker 会根据 Dockerfile 中的指令逐步执行并构建每个镜像层。为了提高效率,Docker 使用缓存机制避免重新构建那些在之前的构建中没有发生变化的部分。

⭐⭐以下是一些关于 Docker Build 缓存机制的关键点:

  • 缓存键(Cache Key):Docker 计算一个缓存键,它基于 Dockerfile 中每条指令的内容及其所依赖的文件。如果这些内容或文件没有变化,缓存键保持不变,Docker 就可以重用缓存。缓存键考虑到了文件的元数据(如修改时间、权限、inode 等)和文件的实际内容。
  • ADD 和 COPY 指令:ADD 和 COPY 指令会触发缓存键的重新计算。如果在这两个指令中引用的文件发生变化,缓存就会失效,即使 Dockerfile 没有变化。如果文件的修改时间或内容发生变化,即使文件名相同,缓存也会失效。
  • 缓存失效:当 Dockerfile 或其引用的文件发生变化时,从那个点开始的缓存都会失效,后续的指令需要重新执行。例如,如果 Dockerfile 的第一条 RUN 指令之后是一个 COPY 指令,并且 COPY 的源文件被修改,那么所有在 COPY 之后的指令都需要重新执行,即使它们自身没有变化。
  • 禁用缓存:可以通过向 docker build 命令添加 –no-cache 参数来完全禁用缓存,这通常用于确保镜像是根据最新的代码和依赖项构建的。
  • 缓存清理:Docker 会自动管理缓存,但有时可能需要手动清理以释放磁盘空间。可以使用 docker builder prune 命令来清除不再使用的构建缓存。
  • BuildKit:Docker 18.09 引入了 BuildKit,这是一个用于构建镜像的新引擎,它提供了更高级别的缓存管理和其他功能,比如并行化构建步骤。

💗💗Docker Build 缓存机制对于优化构建流程和减少构建时间非常重要。正确地组织 Dockerfile 和使用缓存可以帮助你更快地迭代和部署应用。

Docker网络原理

Docker网络架构基础

📶📶 Docker0网桥:当你在宿主机上安装Docker时,会自动创建一个名为docker0的虚拟网桥。这个网桥本质上是一个软件定义的网络交换机,允许连接到它的网络设备(即容器)相互通信,并可配置与外部网络的连接。

📶📶 网络命名空间:每个Docker容器都运行在自己的网络命名空间中,这意味着每个容器都有独立的网络设备、IP地址、路由表等,实现了容器间的网络隔离。

📶📶 Veth Pair: veth(virtual Ethernet)对是一种虚拟网络设备对,通常用于连接两个网络命名空间。当创建一个新的Docker容器时,会创建一对veth接口,一端位于容器内部(如eth0),另一端则连接到宿主机上的网络栈,通常绑定到docker0网桥上。这样,容器内的网络流量可以通过这对veth接口流向宿主机网络,并通过docker0网桥与其他容器通信。

1
2
3
4
5
6
7
8
# 宿主机查看Veth Pair
ip addr

# 查看所有的Docker网络
docker network ls

# 查看 Docker0网桥为容器分配的IP地址
docker exec -it CONTAINER_ID ip addr

容器间通信原理

📤📤 同一宿主机上的容器通信:同一宿主机上的Docker容器通过共享docker0网桥实现通信。每个容器获得一个与docker0网桥同网段的IP地址,由于它们处于同一子网,可以直接通过IP通信,无需额外配置。

📤📤 跨宿主机容器通信:当容器分布在不同的宿主机上时,需要更复杂的网络配置,如使用Docker的overlay网络或第三方网络插件,实现跨宿主机容器的通信。

1
2
3
4
5
6
7
8
9
# 使用IP地址,可以实现本地容器的相互通信
docker exec -it CONTAINER_ID ping Docker0网桥为容器分配的IP地址

# 启动镜像时,可以配置要连通的镜像,就可以使用容器名称进行通讯
# 原理就是修改hosts文件,把容器名称指向了IP地址
docker run -d -P --link tomcat01 --name tomcat02 tomcat

# 根据容器名称通讯只是单向的,配置好才能使用
docker exec -it tomcat02 ping tomcat01

ARP协议与容器发现

🔎🔎 ARP协议:当容器需要与另一个容器通信但只知道目标容器的IP地址时,会使用ARP(Address Resolution Protocol)协议来解析目标容器的MAC地址。容器会发送ARP广播请求,询问具有特定IP地址的MAC地址。目标容器接收到请求后,会回应其MAC地址,发起请求的容器即可构建完整的以太网帧进行通信。

网络配置与管理

⏩⏪ 端口映射:Docker支持端口映射,允许将容器内部的服务端口映射到宿主机的某个端口,使得外部网络可以通过宿主机的IP和端口访问容器内的服务。

💿💿 Docker网络驱动:Docker提供了多种网络驱动,包括bridge(默认)、host、none、overlay等,每种模式提供了不同的网络隔离和连接策略,满足不同场景下的网络需求。

1
2
# 桥接模式:在启动时默认会加上:--net bridge 
docker run -d -P --net bridge --name tomcat01 tomcat

自定义网络

创建自定义网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 自定义一个Docker网络
# --driver bridge:桥接模式,不写也可以,因为它是默认的
# --subnet 192.168.0.0/16:子网掩码
# --gateway 192.168.0.1:网关地址
# mynet:自定义的网络名称
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet

# 查看创建好的网络
docker network ls

# 查看网络详情
docker network inspect mynet

# 容器使用自定义的网络
docker run -d -P --net mynet --name tomcat-net-01 tomcat
docker run -d -P --net mynet --name tomcat-net-02 tomcat

# 测试连通
docker exec -it tomcat-net-01 ping tomcat-net-02

容器互通

💗💗 在不同的自定义网络中,容器的相互通信

1
2
3
4
# 想让其他网络地址下的 tomcat01 容器,跟 mynet 网络下的容器进行通信
# 只需要把 tomcat01 容器,加入到 mynet 网络下就可以了
# 实际就是给 tomcat01 容器再配置一个 mynet 网段下的IP地址
docker network connect mynet tomcat01

网络共享

1
2
# tomcat02 容器使用 tomcat01 容器的网络
docker run -d -P --network container:tomcat01 --name tomcat02 tomcat

搭建 Redis 集群

💗💗 创建Docker自定义网络

1
docker network create --subnet 192.169.0.0/16 --gateway 192.169.0.1 redis

💗💗 创建Redis容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 新建一个脚本创建6个Redis容器
mkdir -p /home/docker/redis
cd /home/docker/redis
vi createRedis.sh
# ---------------- vi 编辑器 --------------------
for port in $(seq 1 6);
do
# 创建Redis配置文件
mkdir -p /home/docker/redis/node-${port}/conf
touch /home/docker/redis/node-${port}/conf/redis.conf
cat << EOF > /home/docker/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.169.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF

# 创建Redis容器
docker run -p 637${port}:6379 -p 1637${port}:16379 \
-v /home/docker/redis/node-${port}/data:/data \
-v /home/docker/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net redis --ip 192.169.0.1${port} \
--name redis-${port} \
redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
done
# ----------------------------------------------
# 修改脚本执行权限
chmod +x createRedis.sh
# 执行脚本创建Redis
./createRedis.sh

💗💗 配置Redis集群

1
2
3
4
5
6
7
8
9
10
11
# 进入redis-1容器
docker exec -it redis-1 /bin/sh
# 配置集群
redis-cli --cluster create \
192.169.0.11:6379 \
192.169.0.12:6379 \
192.169.0.13:6379 \
192.169.0.14:6379 \
192.169.0.15:6379 \
192.169.0.16:6379 \
--cluster-replicas 1

💗💗 测试Redis集群

1
2
3
4
5
6
7
8
# 进入Redis集群
redis-cli -c

# 查看集群信息
cluster info

# 查看节点信息
cluster nodes

Docker Compose

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。使用 YAML 文件描述整个应用程序的组件,包括每个服务的容器、网络、卷和其他资源。通过这种方式,可以将复杂的应用程序环境封装成可移植且可重复的单元。

💗💗 Docker Compose 的核心概念:

  • 项目(Project):一个项目是指由一组相关服务组成的集合,它们一起构成一个完整的应用程序。所有服务都定义在一个 docker-compose.yml 文件中,并且属于同一个项目。
  • 服务(Service):每个服务定义了运行该服务的一个或多个容器的配置,包括使用的镜像、端口映射、环境变量、卷绑定等。
  • 容器(Container):服务下运行的实际实例。每个服务可以运行一个或多个容器实例。
  • 网络(Network):Docker Compose 支持自定义网络,允许服务之间的容器相互通信。
  • 卷(Volume):用于数据持久化,可以在容器重启或重建后保留数据。

💗💗 Docker Compose 的优势:

  • 简化配置:使用 YAML 文件集中管理所有服务的配置,便于维护和版本控制。
  • 自动化部署:可以通过一个命令启动或停止所有服务,减少手动配置的错误。
  • 环境一致性:确保本地开发环境与生产环境保持一致。
  • 可扩展性:易于添加或删除服务,适应应用程序的变化。

常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
docker compose attach       #将本地标准输入、输出和错误流附加到服务的运行容器
docker compose build #生成或重新生成服务
docker compose config #解析、解析并呈现规范格式的compose文件
docker compose cp #在服务容器和本地文件系统之间复制文件/文件夹
docker compose create #为服务创建容器
docker compose down #停止并移除容器、网络
docker compose events #从容器接收实时事件
docker compose exec #在运行的容器中执行命令
docker compose images #列出创建的容器使用的图像
docker compose kill #强制停止服务容器
docker compose logs #查看容器的输出
docker compose ls #列出正在运行的compose项目
docker compose pause #暂停服务
docker compose port #打印端口绑定的公共端口
docker compose ps #列出容器
docker compose pull #拉取服务镜像
docker compose push #推送服务图片
docker compose restart #重新启动服务容器
docker compose rm #删除已停止的服务容器
docker compose run #在服务上运行一次性命令
docker compose scale #缩放服务
docker compose start #启动服务
docker compose stats #显示容器资源使用统计信息的实时流
docker compose stop #停止服务
docker compose top #显示正在运行的进程
docker compose unpasse #未使用的服务
docker compose up #创建并启动容器
docker compose version #显示docker Compose版本信息
docker compose wait #阻止直到第一个服务容器停止
docker compose watch #监视服务的构建上下文,并在文件更新时重建/刷新容器

项目部署

创建构建文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# 把 jar 文件放入该目录下
cd /home/docker/szxck

# 新建 jar 服务的 Dockerfile 文件
vi Dockerfile
# ---------------- vi 编辑器 --------------------
FROM openjdk:8u102
LABEL auth=szxck email=suzengxin@foxmail.com
WORKDIR /home
COPY app.jar /home/app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# ----------------------------------------------

# 新建 compose 构建文件
vi compose.yml
# ---------------- vi 编辑器 --------------------
networks:
szxck-net:
driver: bridge

services:
szxck-server:
build: ./
image: szxck-server:1.0
container_name: szxck-server
ports:
- 10000:8080
networks:
- szxck-net
volumes:
- ./logs:/home/logs
depends_on:
- szxck-mysql
- szxck-redis

szxck-mysql:
image: mysql:5.7
container_name: szxck-mysql
environment:
MYSQL_ROOT_PASSWORD: 123456
ports:
- 3306:3306
networks:
- szxck-net
volumes:
- /home/docker/mysql/log:/var/log/mysql
- /home/docker/mysql/data:/var/lib/mysql
- /home/docker/mysql/conf:/etc/mysql/conf.d

szxck-redis:
image: redis:latest
container_name: szxck-redis
ports:
- 6379:6379
networks:
- szxck-net
volumes:
- /home/docker/redis/redis.conf:/etc/redis/redis.conf
- /home/docker/redis/data:/data
command: redis-server /etc/redis/redis.conf
# ----------------------------------------------

验证文件

1
docker compose config -q

启动服务

1
docker compose -f compose.yml up -d

Docker Swarm

Docker Swarm 是 Docker 公司提供的一个用于构建和管理容器集群的工具。它允许用户将多个 Docker 主机组织成一个集群,并且能够统一管理和调度集群内的容器。

Docker Swarm 提供了集群级别的特性,如服务发现、负载均衡、以及跨主机的网络连接,使得在集群环境中部署和管理容器化应用变得更为简单。

💗💗Docker Swarm 的组件

  • 管理节点 (Manager nodes):管理节点负责维护集群状态、执行编排决策和协调任务。它们存储集群的状态和配置信息,并处理加入和离开集群的请求。至少需要一个管理节点来启动 Swarm 集群。
  • 工作节点 (Worker nodes):工作节点是执行容器和任务的实际机器。它们接收来自管理节点的任务,并在其上运行容器实例。
  • 服务 (Services):服务定义了容器运行的策略,包括容器的数量、网络设置、更新策略等。Docker Swarm 使用服务的概念来实现容器的编排。
  • 任务 (Tasks):任务是服务的具体实例,代表了运行中的容器。每个服务可以有多个任务,这些任务可以在集群中的不同工作节点上运行。
  • 网络 (Networks):Docker Swarm 支持多种网络模式,包括覆盖网络 (overlay networks),允许容器在不同的主机之间相互通信。
  • 堆栈 (Stacks):堆栈是一个或多个服务的集合,通常由 Docker Compose 文件定义,可以在 Swarm 模式下部署。

💗💗Docker Swarm 的优势

  • 易于使用:Swarm 与 Docker 引擎紧密集成,使用 Docker CLI 或者 API 即可管理集群。
  • 轻量级:相比于 Kubernetes,Swarm 更轻量,适合小型或中型部署。
  • 内置网络:Swarm 提供了内置的覆盖网络,简化了跨主机容器间的通信。
  • 安全性:Swarm 支持加密通信和认证机制,确保集群的安全性。

常用命令

初始化集群

1
docker swarm init

加入节点

1
2
3
docker swarm join
docker swarm join-token manager
docker swarm join-token worker

部署服务

1
2
docker service create
docker stack deploy

管理节点

1
2
3
4
5
docker info
docker node ls
docker node inspect
docker node update
docker node rm

管理服务

1
2
3
4
docker service ls
docker service inspect
docker service scale
docker service rm

集群搭建

集群初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 初始化 Swarm 集群(主机)
docker swarm init --advertise-addr 服务器IP地址
# 获取加入到集群主节点的命令(主机)
docker swarm join-token manager
# 获取加入到集群子节点的命令(主机)
docker swarm join-token worker

# 根据集群需求选择加入主节点还是子节点:一般是三主三从
# 加入到主节点(其他服务器)以下命令是根据上边的命令获取到的
docker swarm join --token SWMTKN-1-24xzkhefjywjnn078e10w5c6z4c33jbp2hft0dfcmdb4l73jri-4p0mjvzk7xxvsyh08fyj6lbtq 172.14.195.136:2377
# 加入到子节点(其他服务器)以下命令是根据上边的命令获取到的
docker swarm join --token SWMTKN-1-24xzkhefjywjnn078e10w5c6z4c33jbp2hft0dfcmdb4l73jri-1sr5j29kqudsoreh3ssp0huou 172.14.195.136:2377

# 查看集群列表
docker node ls

添加备注信息

💗💗给节点添加描述信息,便于浏览、记忆。

1
2
3
4
5
6
7
# 添加描述信息
# 5w16y47qf225m8ac2ggyh6mt9 节点标识
# 可以通过 docker info 命令查看要升级节点的标识
docker node update --label-add auth=李四 --label-add email=lisi@163.com 5w16y47qf225m8ac2ggyh6mt9

# 移除描述信息
docker node update --label-rm email 5w16y47qf225m8ac2ggyh6mt9

节点升级/降级

💗💗升级节点为 manager 节点

1
2
3
4
5
# 5w16y47qf225m8ac2ggyh6mt9 节点标识
# 可以通过 docker info 命令查看要升级节点的标识
docker node promote 5w16y47qf225m8ac2ggyh6mt9
# 第二种方式
docker node update --role manager 5w16y47qf225m8ac2ggyh6mt9

💗💗降级节点为 worker 节点

1
2
3
4
5
# 5w16y47qf225m8ac2ggyh6mt9 节点标识
# 可以通过 docker info 命令查看要升级节点的标识
docker node demote 5w16y47qf225m8ac2ggyh6mt9
# 第二种方式
docker node update --role worker 5w16y47qf225m8ac2ggyh6mt9

节点退出集群

1
2
3
4
5
6
7
8
9
10
11
12
# worker节点退出集群
docker swarm leave

# manager节点退出集群
# 先把manager节点变更为worker节点,然后在退出集群
# 也可以强制离开集群(不推荐)
docker swarm leave --force

# 移除节点
# 5w16y47qf225m8ac2ggyh6mt9 节点标识
# 可以通过 docker info 命令查看要升级节点的标识
docker node rm -f 5w16y47qf225m8ac2ggyh6mt9

集群退出自动锁定

💗💗开启集群退出自动锁定后,节点中需要至少有一个manager节点运行,不然manager节点全部宕机,则启动秘钥丢失(秘钥保存在内存中),集群就不可使用,只能离开原有集群,重新搭建。

1
2
3
4
5
6
7
8
9
10
11
# 开启集群退出自动锁定
docker swarm update --autolock=true

# 关闭集群退出自动锁定
docker swarm update --autolock=false

# 查看启动秘钥
docker swarm unlock-key

# 重新加入集群(运行命令后,输入秘钥)
docker swarm unlock

集群通信安全

Docker Swarm 的通信安全主要围绕着确保集群内部通信以及客户端与集群之间通信的完整性、机密性和不可否认性。Swarm 通过使用 Transport Layer Security (TLS) 加密来实现这一目标,以防止中间人攻击、数据窃听和其他安全威胁。

Docker Swarm 使用 TLS 加密来保护管理节点和工作节点之间的通信,同时也用于保护 Docker 客户端与 Swarm 集群之间的通信。TLS 证书可以由任何证书颁发机构(CA)生成,包括自签名证书或第三方 CA。

💗💗在 Swarm 集群中,TLS 证书通常包括以下几部分

CA 证书:这是集群的根证书,用于签发所有其他证书。


Manager 证书:为每个管理节点生成的证书,用于与其他节点通信。
Worker 证书:为每个工作节点生成的证书,用于与管理节点通信。
客户端证书:为每个需要与 Swarm 集群交互的 Docker 客户端生成的证书。

💗💗安全通信端口

2377/tcp:用于客户端与 Swarm 进行安全通信,以及管理节点之间的通信。


7946/tcp 和 7946/udp:用于节点之间的心跳检测和集群状态的同步。
4789/udp:用于 overlay 网络的 VXLAN 通信。

💗💗Swarm 集群的安全性

定期轮换证书:定期更换 TLS 证书,尤其是当有节点离开集群或怀疑证书已泄露时。


限制对管理节点的访问:只允许经过身份验证和授权的客户端访问管理节点。
使用强密码策略:为 Docker 客户端和节点使用复杂的密码或 SSH 密钥进行身份验证。
监控和审计:实施日志记录和审计措施,监控集群活动,以便及时发现异常行为。

集群服务

创建服务(容器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 跟运行容器命令差不多
# --replicas 3:创建三个副本(运行任务/运行容器),负载均衡:轮询分配到各个节点,
docker service create -p 9000:8080 --replicas 3 --name tomcat tomcat:8.5.49

# --mode global: 全局模式,每个节点分配一个任务
docker service create -p 9000:8080 --mode global --name tomcat tomcat:8.5.49

# 创建服务其他配置:服务回滚
docker service create \
-p 9000:8080 \
--replicas 5 \
--name tomcat \
--update-parallelism 2 \ # 更新并发量
--update-delay 3s \ # 更新间歇时间
--update-max-failure-ratio 0.2 \ # 允许更新最大失败率
--update-failure-action rollback \ # 更新超过失败率:回滚
--rollback-parallelism 2 \ # 回滚一次两个
--rollback-delay 3s \ # 回滚间歇时间
--rollback-max-failure-ratio 0.2 \ # 回滚最大失败率
--rollback-failure-action contiue \ # 回滚超过失败率:继续 | 暂停
tomcat:8.5.39

服务列表查询

1
2
3
docker service ls
docker service ps tomcat
docker node ps

更新服务

1
docker service update --image tomcat:8.5.39 tomcat

服务回滚

1
docker service update --rollback tomcat

服务扩容\缩容

1
2
3
4
5
# 第一种方法
docker service update --replicas 5 --name tomcat

# 第二种方法
docker service scale tomcat=5

节点暂停扩容

1
2
# 集群部署服务时不在给改节点分配任务,但原有的任务还可以访问
docker node update --availability pause 5w16y47qf225m8ac2ggyh6mt9

节点清空任务

1
2
# 清空该节点上集群部署的任务,集群会把任务重新分配到其他节点
docker node update --availability drain 5w16y47qf225m8ac2ggyh6mt9

删除服务(任务)

1
docker service rm tomcat

负载均衡验证

1
2
3
# 使用该容器可以验证负载均衡
docker service create -p 8080:80 --replicas 5 --name whoami containous/whoami
curl http://localhost:8080

集群网络

Swarm 使用一种称为 Overlay 网络的技术来实现跨主机的服务通信,这使得容器可以在集群中的任何节点上运行,同时保持与其他容器的网络连通性。

💗💗Swarm 集群网络主要分为以下几个部分


Swarm Overlay 网络: Swarm 提供了一种特殊的网络类型,即 Overlay 网络,它允许容器跨越不同的物理或虚拟主机进行通信。当在一个 Swarm 集群中创建服务时,Docker 会自动为服务创建一个 Overlay 网络。这个网络类型是基于 VXLAN 的,这意味着它使用封装技术在底层网络上创建了一个逻辑上的二层网络,这样容器就像在同一台主机上一样进行通信,而实际上它们可能分布在集群的不同节点上。
Ingress 网络:当服务被创建时,Swarm 也会创建一个 Ingress 网络。这个网络用于将外部流量路由到服务的容器实例上。Ingress 网络利用了负载均衡器(通常是 Traefik 或 Nginx)来转发流量到正确的容器。Ingress 网络使用一个虚拟 IP (VIP) 来接收外部请求,并将请求路由到集群内服务的实例上。
Node 网络:每个 Swarm 节点都有一个 Node 网络,它负责将容器与本地网络接口连接起来,同时也提供了与 Overlay 网络的连接。Node 网络是每个节点上的默认桥接网络。
端口映射和服务发现:服务在 Swarm 集群中可以定义端口映射,将容器的端口暴露给外部世界。此外,Swarm 支持服务发现,允许容器自动找到同一服务的其他实例,而无需显式配置。
网络策略和安全:Swarm 允许管理员设置网络策略,例如限制容器之间的通信或访问外部网络的权限。这可以通过网络的标签和容器的网络连接规则来实现。
DNS 解析:Swarm 集群中的容器可以自动解析服务名称到其 IP 地址,这得益于内置的 DNS 服务,使得容器间的通信更加便捷。

CI/CD | DevOps

概述

CI/CD 是现代软件开发和运维(DevOps)中非常关键的概念。CI/CD 不仅是一种技术实践,也是一种文化和工作流程的变革,它鼓励团队采用敏捷开发方法,快速迭代,同时保持高度的质量标准。通过自动化和标准化,CI/CD 帮助组织实现了更快的软件交付周期,提高了软件质量和客户满意度。

💗💗 CI

持续集成(Continuous Integration),持续集成是一种软件开发实践,要求开发人员经常(比如每天)将他们的工作集成到共享的主干分支中。每次集成后,会通过自动化构建(包括编译和测试)来验证,以便尽早发现集成错误。

💗💗 CD

  • 持续交付(Continuous Delivery),持续交付扩展了持续集成的概念,它不仅包括自动化构建和测试,还包含了自动化部署至测试和预生产环境。这意味着,软件产品在任何时刻都可以被部署到生产环境,但实际是否部署则由人工决策。
  • 持续部署(Continuous Deployment),持续部署是持续交付的进一步延伸,它指的是每当代码通过了所有预定的测试之后,就自动部署到生产环境中,无需人工干预。

💗💗DevOps

DevOps 是一个结合了软件开发(Development)和 IT 运维(Operations)的术语,旨在促进软件开发、技术运营和质量保证(QA)部门之间的沟通、协作与整合。DevOps 文化和实践的目标是缩短系统开发周期,提供持续的交付和部署能力,同时仍然保持高质量的软件产品。DevOps 是一种综合的方法论,它改变了传统软件开发和运维的分离模式,推动了软件开发的现代化和高效化。

💗💗工具链

  • 源代码管理系统:如 Git 和 SVN。
  • 构建和自动化工具:如 Jenkins、Travis CI、GitLab CI/CD 和 GitHub Actions。
  • 自动化测试框架:如 JUnit、Selenium、Postman 等。
  • 容器化和虚拟化平台:如 Docker 和 Kubernetes。
  • 配置管理工具:如 Ansible、Chef 和 Puppet。
  • 监控和日志工具:如 Prometheus、Grafana 和 ELK Stack。

系统架构

CI/CD简化流程图

graph LR;
A[代码提交] --> B{构建};
B -->|通过| C[测试];
B -->|失败| F[构建失败];
C --> D{代码审查};
D -->|通过| E[制品];
D -->|失败| G[代码审查失败];
E --> H[打包];
H --> I{部署};
  I -->|成功| J[生产环境];
  I -->|失败| K[部署失败];
  J --> L[监控];
  L --> M{问题?};
  M -->|是| N[警报];
  M -->|否| J;

classDef success fill:#0f0,stroke:#333,stroke-width:4px;
classDef fail fill:#f00,stroke:#333,stroke-width:4px;
classDef monitoring fill:#ccc,stroke:#333,stroke-width:4px;

class J success;
class F,G,K,N fail;
class L monitoring;

系统整体架构图

C4Container

Person(User, 用户, "Eclipse | IDEA")

Container(GitLab, "源代码仓库", "Git | GitLab | SVN")

Container_Boundary(Jenkins, "Jenkins") {
  Container(Maven, "mvn package", "源代码打包")
  Container(SonarScanner, "SonarScanner", "SonarQube客户端")
  Container(Docker, "cocker build", "构建镜像")
  Container(JT, "监听")
}

Container_Boundary(SonarQube, "SonarQube") {
  Container(SonarQube, "SonarQube")
}

Container_Boundary(Harbor, "Harbor") {
  Container(Harbor, "Harbor", "镜像仓库")
}

Container_Boundary(TargetServer, "TargetServer") {
  Container(DockerServer, Docker, "docker run")
}

Container(DD, "钉钉")

Rel(User, GitLab, "Uses", "git push")
Rel(GitLab, Maven, "git push")
Rel(Maven, SonarScanner, "")
Rel(SonarScanner, Docker, "")
Rel(SonarScanner, SonarQube, "代码质量检测")
Rel(Docker, Harbor, "docker push")
Rel(Docker, DockerServer, "SSH:cmd")
Rel(Harbor, DockerServer, "docker pull")
Rel(JT, DD, "消息推送")

UpdateRelStyle(User, GitLab, $offsetY="10")
UpdateRelStyle(JT, DD, $offsetY="-100", $offsetX="-30")
UpdateRelStyle(SonarScanner, SonarQube, $offsetY="10", $offsetX="-50")
UpdateRelStyle(Harbor, DockerServer, $offsetY="10", $offsetX="-45")

Docker项目配置运行

安装可视化面板

1
2
3
4
5
6
7
8
9
10
11
12
# 后台运行 portainer 容器
# --restart=always:守护进程在容器退出后总是重启该容器
# --privileged=true:以特权模式运行容器,可以访问主机的底层资源和功能
docker run -d \
-p 9000:9000 \
-p 9443:9443 \
-v /data
-v /var/run/docker.sock:/var/run/docker.sock \
--restart=always \
--privileged=true \
--name portainer \
portainer/portainer-ce

安装 Nginx 容器

1
2
3
4
5
6
7
8
docker run --rm --entrypoint=cat nginx /etc/nginx/conf.d/default.conf > /home/docker/nginx/conf.d/default.conf
docker run --rm --entrypoint=cat nginx /etc/nginx/nginx.conf > /home/docker/nginx/nginx.conf
docker run -d -p 10000:80 \
-v /home/docker/nginx/conf.d:/etc/nginx/conf.d\
-v /home/docker/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /home/docker/nginx/html:/usr/share/nginx/html \
--name nginx \
nginx

安装 MySql 容器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 安装 mysql
docker run -d -p 10000:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /home/docker/mysql/conf:/etc/mysql/conf.d \
-v /home/docker/mysql/data:/var/lib/mysql \
-v /home/docker/mysql/log:/var/log/mysql \
--name mysql \
mysql:5.7

# 中文乱码问题
vim /home/docker/mysql/conf/my.cnf
# ---------------- vi 编辑器 --------------------
[client]
default_character_set=utf8

[mysql]
default_character_set=utf8

[mysqld]
character_set_server=utf8
# ----------------------------------------------

# 重启容器
docker restart mysql

搭建私有镜像中心:registry

安装httpd-tools

1
2
# 需要httpd-tools工具包中的 htpasswd 命令生成密码
yum install -y httpd-tools

创建镜像中心登录用户

1
2
3
4
5
6
7
8
9
# 创建一个目录存放密码文件
mkdir -p /home/docker/auth
cd /home/docker/auth
# 新建文件并且添加用户
htpasswd -Bbc htpasswd.user zhangsan 123
# 添加用户
htpasswd -Bb htpasswd.user lisi 123
# 删除用户
htpasswd -D htpasswd.user lisi

运行容器

1
2
3
4
5
6
7
8
9
10
11
# 运行容器
docker run \
-d -p 5000:5000 \
--restart always \
-v /var/lib/registry:/var/lib/registry \
-v /home/docker/auth:/auth \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd.user \
--name registry \
registry

把请求改为http

1
2
3
4
5
6
7
8
9
10
vi /etc/docker/daemon.json
# ---------------- vi 编辑器 --------------------
# registry-mirrors:镜像加速
# insecure-registries:注册镜像库地址
# 192.168.192.111:5000:registry容器的地址
{
"registry-mirrors": ["https://xxxxxxxx.mirror.aliyuncs.com"],
"insecure-registries": ["192.168.192.111:5000"]
}
# ----------------------------------------------

推送镜像

1
2
3
4
5
6
# 登录
docker login -u zhangsan 192.168.192.111:5000
# 打标签
docker tag hello-world 192.168.192.111:5000/zhangsan/hw:1.0
# 推送
docker push 192.168.192.111:5000/zhangsan/hw:1.0

查看镜像

1
2
3
4
5
curl -u zhangsan:123 -XGET http://192.168.192.111:5000/v2/_catalog

curl -u zhangsan:123 -XGET http://192.168.192.111:5000/v2/zhangsan/hw/tags/list

tree /var/lib/registry

修改配置

1
2
3
4
5
6
7
8
9
10
# 默认是不能删除镜像的,需要手动配置
docker exec -it registry /bin/sh
vi /etc/docker/registry/config.yml
# ---------------- vi 编辑器 --------------------
storage:
delete:
enabled: true
# ----------------------------------------------
# 重启容器
docker restart registry

删除镜像

1
2
3
4
5
# 删除镜像
curl -u zhangsan:123 -I -XDELETE http://192,168.192.111:5000/v2/zhangsan/hw/manifests/sha256:7071451ad41asdada1531as3415

# blobs
docker exec -it registry registry garbage-collect /etc/docker/registry/config.yml

搭建源代码仓库:GitLab

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 拉取镜像
docker pull gitlab/gitlab-ce
# 创建目录
mkdir -p /home/docker/gitlab
# 创建构建文件
vi compose.yml
# ---------------- vi 编辑器 --------------------
services:
gitlab:
image: gitlab/gitlab-ce
container_name: gitlab
restart: always
environment:
GITLAB_OMNIBUS_CONFIG:
external_url 'http://192.168.153.128:9999'
gitlab_rails['initial_root_password']=123456789
gitlab_rails['gitlab_shell_ssh_port']=2222
ports:
- 9999:9999
- 2222:2222
volumes:
- ./config:/etc/gitlab
- ./logs:/var/log/gitlab
- ./data:/var/opt/gitlab
# ----------------------------------------------
# 验证文件是否正确
docker compose config -q
# 运行配置文件
docker compose up -d
# 查看是否启动
docker logs gitlab
# 获取登录密码
docker exec -it gitlab bash
cat /etc/gitlab/initial_root_password
# 登录页面
http://192.168.153.128:9999

学习资源

  • 视频
    • 【狂神说Java】Docker最新超详细版教程通俗易懂:https://www.bilibili.com/video/BV1og4y1q7M4
    • 【Docker视频教程从入门到进阶,docker视频教程详解】https://www.bilibili.com/video/BV1sb411X7oe
    • 尚硅谷 - Jenkins 持续集成工具教程:https://www.bilibili.com/video/BV1GW411w7pn
  • 书籍
    • 《深入浅出 Docker》:https://www.zhihu.com/pub/reader/119632192
  • 文档
    • Docker — 从入门到实践:https://vuepress.mirror.docker-practice.com/
    • 菜鸟教程 Docker:https://www.runoob.com/docker/docker-tutorial.html
    • Jenkins 官方文档:https://www.jenkins.io/zh/
  • 网站
    • Docker 中文社区:https://www.docker.org.cn/
  • 镜像源
    • 阿里云:https://cr.console.aliyun.com/
    • 腾讯云:https://cloud.tencent.com/product/tsr
    • 华为云:https://www.huaweicloud.com/product/swr.html
    • 腾讯软件源:http://mirrors.tencent.com/#/index