19 如何使用 Docker Compoe 解决开发环境的依赖?

前两个模块,我们从 Docker 的基本操作到 Docker 的实现原理,为你一步一步揭开了 Docker 神秘的面纱。然而目前为止,我们所有的操作都是围绕单个容器进行的,但当我们的业务越来越复杂时,需要多个容器相互配合,甚至需要多个主机组成容器集群才能满足我们的业务需求,这个时候就需要用到容器的编排工具了。因为容器编排工具可以帮助我们批量地创建、调度和管理容器,帮助我们解决规模化容器的部署问题。

从这一课时开始,我将向你介绍 Docker 三种常用的编排工具:Docker Compose、Docker Swarm 和 Kubernetes。了解这些编排工具,可以让你在不同的环境中选择最优的编排框架。

本课时我们先来学习一个在开发时经常用到的编排工具——Docker Compose。合理地使用 Docker Compose 可以极大地帮助我们提升开发效率。那么 Docker Compose 究竟是什么呢?

Docker Compose 的前世今生

Docker Compose 的前身是 Orchard 公司开发的 Fig,2014 年 Docker 收购了 Orchard 公司,然后将 Fig 重命名为 Docker Compose。现阶段 Docker Compose 是 Docker 官方的单机多容器管理系统,它本质是一个 Python 脚本,它通过解析用户编写的 yaml 文件,调用 Docker API 实现动态的创建和管理多个容器。

要想使用 Docker Compose,需要我们先安装一个 Docker Compose。

安装 Docker Compose

Docker Compose 可以安装在 macOS、 Windows 和 Linux 系统中,其中在 macOS 和 Windows 系统下 ,Docker Compose 都是随着 Docker 的安装一起安装好的,这里就不再详细介绍。 下面我重点介绍下如何在 Linux 系统下安装 Docker Compose。

Linux 系统下安装 Docker Compose

在安装 Docker Compose 之前,请确保你的机器已经正确运行了 Docker,如果你的机器还没有安装 Docker,请参考官方网站安装 Docker。

要在 Linux 平台上安装 Docker Compose,我们需要到 Compose 的 Github 页面下载对应版本的安装包。这里我以 1.27.3 版本为例,带你安装一个 Docker Compose。

(1)使用 curl 命令(一种发送 http 请求的命令行工具)下载 Docker Compose 的安装包:

$ sudo curl -L "https://github.com/docker/compose/releases/download/1.27.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

如果你想要安装其他版本的 Docker Compose,将 1.27.3 替换为你想要安装的版本即可。

(2)修改 Docker Compose 执行权限:

$ sudo chmod +x /usr/local/bin/docker-compose

(3)检查 Docker Compose 是否安装成功:

$ docker-compose --version
docker-compose version 1.27.3, build 1110ad01

当我们执行完上述命令后,如果 Docker Compose 输出了当前版本号,就表示我们的 Docker Compose 已经安装成功。 Docker Compose 安装成功后,我们就可以很方便地使用它了。

在使用 Docker Compose 之前,我们首先需要先编写 Docker Compose 模板文件,因为 Docker Compose 运行的时候是根据 Docker Compose 模板文件中的定义来运行的。

下面我们首先来学习一下如何编写一个 Docker Compose 模板文件。

编写 Docker Compose 模板文件

在使用 Docker Compose 启动容器时, Docker Compose 会默认使用 docker-compose.yml 文件, docker-compose.yml 文件的格式为 yaml(类似于 json,一种标记语言)。

Docker Compose 模板文件一共有三个版本: v1、v2 和 v3。目前最新的版本为 v3,也是功能最全面的一个版本,下面我主要围绕 v3 版本介绍一下如何编写 Docker Compose 文件。

Docker Compose 文件主要分为三部分: services(服务)、networks(网络) 和 volumes(数据卷)。

  • services(服务):服务定义了容器启动的各项配置,就像我们执行docker run命令时传递的容器启动的参数一样,指定了容器应该如何启动,例如容器的启动参数,容器的镜像和环境变量等。

  • networks(网络):网络定义了容器的网络配置,就像我们执行docker network create命令创建网络配置一样。

  • volumes(数据卷):数据卷定义了容器的卷配置,就像我们执行docker volume create命令创建数据卷一样。

一个典型的 Docker Compose 文件结构如下:

version: "3"
services:nginx:## ... 省略部分配置
networks:frontend:backend:
volumes:db-data:

下面我们首先来学习一下如何编写 services 部分的配置。

编写 Service 配置

services 下,首先需要定义服务名称,例如你这个服务是 nginx 服务,你可以定义 service 名称为 nginx,格式如下:

version: "3.8"
services:nginx:

服务名称定义完毕后,我们需要在服务名称的下一级定义当前服务的各项配置,使得我们的服务可以按照配置正常启动。常用的 16 种 service 配置如下。如果你比较了解,可以直接跳过看 Volume 配置和后续实操即可。

build: 用于构建 Docker 镜像,类似于docker build命令,build 可以指定 Dockerfile 文件路径,然后根据 Dockerfile 命令来构建文件。使用方法如下:

build:## 构建执行的上下文目录context: .## Dockerfile 名称dockerfile: Dockerfile-name

cap_add、cap_drop: 指定容器可以使用到哪些内核能力(capabilities)。使用格式如下:

cap_add:- NET_ADMIN
cap_drop:- SYS_ADMIN

command: 用于覆盖容器默认的启动命令,它和 Dockerfile 中的 CMD 用法类似,也有两种使用方式:

command: sleep 3000
command: ["sleep", "3000"]

container_name: 用于指定容器启动时容器的名称。使用格式如下:

container_name: nginx

depends_on: 用于指定服务间的依赖关系,这样可以先启动被依赖的服务。例如,我们的服务依赖数据库服务 db,可以指定 depends_on 为 db。使用格式如下:

version: "3.8"
services:my-web:build: .depends_on:- dbdb:image: mysql

devices: 挂载主机的设备到容器中。使用格式如下:

devices:- "/dev/sba:/dev/sda"

dns: 自定义容器中的 dns 配置。

dns:- 8.8.8.8- 114.114.114.114

dns_search: 配置 dns 的搜索域。

dns_search:- svc.cluster.com- svc1.cluster.com

entrypoint: 覆盖容器的 entrypoint 命令。

entrypoint: sleep 3000

entrypoint: ["sleep", "3000"]

env_file: 指定容器的环境变量文件,启动时会把该文件中的环境变量值注入容器中。

env_file:- ./dbs.env

env 文件的内容格式如下:

KEY_ENV=values

environment: 指定容器启动时的环境变量。

environment:- KEY_ENV=values

image: 指定容器镜像的地址。

image: busybox:latest

pid: 共享主机的进程命名空间,像在主机上直接启动进程一样,可以看到主机的进程信息。

pid: "host"

ports: 暴露端口信息,使用格式为 HOST:CONTAINER,前面填写要映射到主机上的端口,后面填写对应的容器内的端口。

ports:- "1000"- "1000-1005"- "8080:8080"- "8888-8890:8888-8890"- "2222:22"- "127.0.0.1:9999:9999"- "127.0.0.1:3000-3005:3000-3005"- "6789:6789/udp"

networks: 这是服务要使用的网络名称,对应顶级的 networks 中的配置。

services:my-service:networks:- hello-network- hello1-network

volumes: 不仅可以挂载主机数据卷到容器中,也可以直接挂载主机的目录到容器中,使用方式类似于使用docker run启动容器时添加 -v 参数。

version: "3"
services:db:image: mysql:5.6volumes:- type: volumesource: /var/lib/mysqltarget: /var/lib/mysql

volumes 除了上面介绍的长语法外,还支持短语法的书写方式,例如上面的写法可以精简为:

version: "3"
services:db:image: mysql:5.6volumes:- /var/lib/mysql:/var/lib/mysql

编写 Volume 配置

如果你想在多个容器间共享数据卷,则需要在外部声明数据卷,然后在容器里声明使用数据卷。例如我想在两个服务间共享日志目录,则使用以下配置:

version: "3"
services:my-service1:image: service:v1volumes:- type: volumesource: logdatatarget: /var/log/mylogmy-service2:image: service:v2volumes:- type: volumesource: logdatatarget: /var/log/mylog
volumes:logdata:

编写 Network 配置

Docker Compose 文件顶级声明的 networks 允许你创建自定义的网络,类似于docker network create命令。

例如你想声明一个自定义 bridge 网络配置,并且在服务中使用它,使用格式如下:

version: "3"
services:web:networks:mybridge: ipv4_address: 172.16.1.11
networks:mybridge:driver: bridgeipam: driver: defaultconfig:subnet: 172.16.1.0/24

编写完 Docker Compose 模板文件后,需要使用 docker-compose 命令来运行这些文件。下面我们来学习下 docker-compose 都有哪些操作命令。

Docker Compose 操作命令

我们可以使用docker-compose -h命令来查看 docker-compose 的用法,docker-compose 的基本使用格式如下:

docker-compose [-f <arg>...] [options] [--] [COMMAND] [ARGS...]

其中 options 是 docker-compose 的参数,支持的参数和功能说明如下:

  -f, --file FILE             指定 docker-compose 文件,默认为 docker-compose.yml-p, --project-name NAME     指定项目名称,默认使用当前目录名称作为项目名称--verbose                   输出调试信息--log-level LEVEL           日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)-v, --version               输出当前版本并退出-H, --host HOST             指定要连接的 Docker 地址--tls                       启用 TLS 认证--tlscacert CA_PATH         TLS CA 证书路径--tlscert CLIENT_CERT_PATH  TLS 公钥证书问价--tlskey TLS_KEY_PATH       TLS 私钥证书文件--tlsverify                 使用 TLS 校验对端--skip-hostname-check       不校验主机名--project-directory PATH    指定工作目录,默认是 Compose 文件所在路径。

COMMAND 为 docker-compose 支持的命令。支持的命令如下:

  build              构建服务config             校验和查看 Compose 文件create             创建服务down               停止服务,并且删除相关资源events             实时监控容器的时间信息exec               在一个运行的容器中运行指定命令help               获取帮助images             列出镜像kill               杀死容器logs               查看容器输出pause              暂停容器port               打印容器端口所映射出的公共端口ps                 列出项目中的容器列表pull               拉取服务中的所有镜像push               推送服务中的所有镜像restart            重启服务rm                 删除项目中已经停止的容器run                在指定服务上运行一个命令scale              设置服务运行的容器个数start              启动服务stop               停止服务top                限制服务中正在运行中的进程信息unpause            恢复暂停的容器up                 创建并且启动服务version            打印版本信息并退出

好了,学习完 Docker Compose 模板的编写和 docker-compose 命令的使用方法,下面我们编写一个 Docker Compose 模板文件,实现一键启动 WordPress 服务(一种博客系统),来搭建一个属于我们自己的博客系统。

使用 Docker Compose 管理 WordPress

启动 WordPress

第一步,创建项目目录。首先我们在 /tmp 目录下创建一个 WordPress 的目录,这个目录将作为我们的工作目录。

$ mkdir /tmp/wordpress

第二步,进入工作目录。

$ cd /tmp/wordpress

第三步,创建 docker-compose.yml 文件。

$ touch docker-compose.yml

然后写入以下内容:

version: '3'
services:mysql:image: mysql:5.7volumes:- mysql_data:/var/lib/mysqlrestart: alwaysenvironment:MYSQL_ROOT_PASSWORD: rootMYSQL_DATABASE: mywordpressMYSQL_USER: mywordpressMYSQL_PASSWORD: mywordpresswordpress:depends_on:- mysqlimage: wordpress:php7.4ports:- "8080:80"restart: alwaysenvironment:WORDPRESS_DB_HOST: mysql:3306WORDPRESS_DB_USER: mywordpressWORDPRESS_DB_PASSWORD: mywordpressWORDPRESS_DB_NAME: mywordpress
volumes:mysql_data: {}

第四步,启动 MySQL 数据库和 WordPress 服务。

$ docker-compose up -d
Starting wordpress_mysql_1 ... done
Starting wordpress_wordpress_1 ... done

执行完以上命令后,Docker Compose 首先会为我们启动一个 MySQL 数据库,按照 MySQL 服务中声明的环境变量来设置 MySQL 数据库的用户名和密码。然后等待 MySQL 数据库启动后,再启动 WordPress 服务。WordPress 服务启动后,我们就可以通过 http://localhost:8080 访问它了,访问成功后,我们就可以看到以下界面,然后按照提示一步一步设置就可以拥有属于自己的专属博客系统了。

图 1 WordPress 启动界面

停止 WordPress

如果你不再需要 WordPress 服务了,可以使用docker-compose stop命令来停止已启动的服务。

$ docker-compose stop
Stopping wordpress_wordpress_1 ... done
Stopping wordpress_mysql_1     ... done

结语

Docker Compose 是一个用来定义复杂应用的单机编排工具,通常用于服务依赖关系复杂的开发和测试环境,如果你还在为配置复杂的开发环境而烦恼,Docker Compose 可以轻松帮你搞定复杂的开发环境。你只需要把复杂的开发环境使用 Docker Compose 模板文件描述出来,之后无论你在哪里可以轻松的一键启动开发和测试环境,极大地提高了我们的开发效率,同时也避免了污染我们开发机器的配置。

那么,学完本课时的课程,你可以试着使用 Docker Compose 一键启动一个 LNMP 开发环境吗?

下一课时,我将为你讲解容器的另一个编排系统 Docker Swarm。


20 如何在生产环境中使用 Docker Swarm 调度容器?

上一课时,我介绍了 Docker 的单节点引擎工具 Docker Compose,它能够在单一节点上管理和编排多个容器,当我们的服务和容器数量较小时可以使用 Docker Compose 来管理容器。

然而随着我们的业务规模越来越大,我们的容器规模也逐渐增大时,数量庞大的容器管理将给我们带来许多挑战。Docker 官方为了解决多容器管理的问题推出了 Docker Swarm ,我们可以用它来管理规模更大的容器集群。

Swarm 的前生今世

2014 年 Docker 在容器界越来越火,这时容器的编排工具 Mesos 和 Kubernetes 也开始崭露头角。此时,Docker 公司也开始筹划容器的编排和集群管理工具,推出了自己的通信协议项目 Beam。后来,通过改进 Beam,Beam 成为一个允许使用 Docker API 来控制的一种分布式系统,之后项目被重命名为 libswarm。然而在 2014 年 11 月,Docker 公司又对 libswarm 进行了重新设计,支持了远程调用 API,并且被重新命名为 Swarm。到此我们称之为 Swarm V1。

在 2016 年,为了解决中央服务可扩展性的问题,Docker 团队重新设计了 Swarm,并称之为 Swarm V2。此时的 Docker Swarm 已经可以支持超过 1000 多个节点的集群规模,并且 Docker 团队在发布 Docker 1.12 版本时,将 Docker Swarm 默认集成到了 Docker 引擎中。

由于 Swarm 是 Docker 官方推出的容器集群管理工具,因此 Swarm 最大的优势之一就是原生支持 Docker API,给用户带来了极大的便利,原来的 Docker 用户可以很方便地将服务迁移到 Swarm 中来。

与此同时,Swarm 还内置了对 Docker 网络插件的支持,因此用户可以很方便地部署需要跨主机通信的容器集群。其实 Swarm 的优点远远不止这些,还有很多,例如以下优点。

  • 分布式: Swarm 使用Raft(一种分布式一致性协议)协议来做集群间数据一致性保障,使用多个容器节点组成管理集群,从而避免单点故障。

  • 安全: Swarm 使用 TLS 双向认证来确保节点之间通信的安全,它可以利用双向 TLS 进行节点之间的身份认证,角色授权和加密传输,并且可以自动执行证书的颁发和更换。

  • 简单: Swarm 的操作非常简单,并且除 Docker 外基本无其他外部依赖,而且从 Docker 1.12 版本后, Swarm 直接被内置到了 Docker 中,可以说真正做到了开箱即用。

Swarm 的这些优点得益于它优美的架构设计,下面我们来了解一下 Swarm 的架构。

Swarm 的架构

Swarm 的架构整体分为管理节点(Manager Nodes)和工作节点(Worker Nodes),整体架构如下图:

图 1 Swarm 架构图

管理节点: 管理节点负责接受用户的请求,用户的请求中包含用户定义的容器运行状态描述,然后 Swarm 负责调度和管理容器,并且努力达到用户所期望的状态。

工作节点: 工作节点运行执行器(Executor)负责执行具体的容器管理任务(Task),例如容器的启动、停止、删除等操作。

管理节点和工作节点的角色并不是一成不变的,你可以手动将工作节点转换为管理节点,也可以将管理节点转换为工作节点。

Swarm 核心概念

在真正使用 Swarm 之前,我们需要了解几个 Swarm 的核心概念,这些核心概念可以帮助我们更好地学习和理解 Swarm 的设计理念。

Swarm 集群

Swarm 集群是一组被 Swarm 统一管理和调度的节点,被 Swarm纳管的节点可以是物理机或者虚拟机。其中一部分节点作为管理节点,负责集群状态的管理和协调,另一部分作为工作节点,负责执行具体的任务来管理容器,实现用户服务的启停等功能。

节点

Swarm 集群中的每一台物理机或者虚拟机称为节点。节点按照工作职责分为管理节点工作节点,管理节点由于需要使用 Raft 协议来协商节点状态,生产环境中通常建议将管理节点的数量设置为奇数个,一般为 3 个、5 个或 7 个。

服务

服务是为了支持容器编排所提出的概念,它是一系列复杂容器环境互相协作的统称。一个服务的声明通常包含容器的启动方式、启动的副本数、环境变量、存储、配置、网络等一系列配置,用户通过声明一个服务,将它交给 Swarm,Swarm 负责将用户声明的服务实现。

服务分为全局服务(global services)和副本服务(replicated services)。

  • 全局服务:每个工作节点上都会运行一个任务,类似于 Kubernetes 中的 Daemonset。

  • 副本服务:按照指定的副本数在整个集群中调度运行。

任务

任务是集群中的最小调度单位,它包含一个真正运行中的 Docker 容器。当管理节点根据服务中声明的副本数将任务调度到节点时,任务则开始在该节点启动和运行,当节点出现异常时,任务会运行失败。此时调度器会把失败的任务重新调度到其他正常的节点上正常运行,以确保运行中的容器副本数满足用户所期望的副本数。

服务外部访问

由于容器的 IP 只能在集群内部访问到,而且容器又是用后马上销毁,这样容器的 IP 也会动态变化,因此容器集群内部的服务想要被集群外部的用户访问到,服务必须要映射到主机上的固定端口。Swarm 使用入口负载均衡(ingress load balancing)的模式将服务暴露在主机上,该模式下,每一个服务会被分配一个公开端口(PublishedPort),你可以指定使用某个未被占用的公开端口,也可以让 Swarm 自动分配一个。

Swarm 集群的公开端口可以从集群内的任意节点上访问到,当请求达到集群中的一个节点时,如果该节点没有要请求的服务,则会将请求转发到实际运行该服务的节点上,从而响应用户的请求。公有云的云负载均衡器(cloud load balancers)可以利用这一特性将流量导入到集群中的一个或多个节点,从而实现利用公有云的云负载均衡器将流量导入到集群中的服务。

搭建 Swarm 集群

要想使用 Swarm 集群有如下一些要求:

  • Docker 版本大于 1.12,推荐使用最新稳定版 Docker;

  • 主机需要开放一些端口(TCP:2377 UDP:4789 TCP 和 UDP:7946)。

下面我通过四台机器来搭建一个 Swarm 集群,演示的节点规划如下:

生产环境中推荐使用至少三个 manager 作为管理节点。

  • 第一步:初始化集群

Docker 1.12 版本后, Swarm 已经默认集成到了 Docker 中,因此我们可以直接使用 Docker 命令来初始化 Swarm,集群初始化的命令格式如下:

docker swarm init --advertise-addr <YOUR-IP>

advertise-addr 一般用于主机有多块网卡的情况,如果你的主机只有一块网卡,可以忽略此参数。

在管理节点上,通过以下命令初始化集群:

$ docker swarm init
Swarm initialized: current node (1ehtnlcf3emncktgjzpoux5ga) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-1kal5b1iozbfmnnhx3kjfd3y6yqcjjjpcftrlg69pm2g8hw5vx-8j4l0t2is9ok9jwwc3tovtxbp 192.168.31.100:2377

To add a manager to this swarm, run ‘docker swarm join-token manager’ and follow the instructions.

集群初始化后, Swarm 会提示我们当前节点已经作为一个管理节点了,并且提示了如何把一台主机加入集群成为工作节点。

  • 第二步:加入工作节点

按照第一步集群初始化后输出的提示,只需要复制其中的命令即可,然后在剩余的三台工作节点上分别执行如下命令:

$ docker swarm join --token SWMTKN-1-1kal5b1iozbfmnnhx3kjfd3y6yqcjjjpcftrlg69pm2g8hw5vx-8j4l0t2is9ok9jwwc3tovtxbp 192.168.31.100:2377
This node joined a swarm as a worker.

默认加入的节点为工作节点,如果是生产环境,我们可以使用docker swarm join-token manager命令来查看如何加入管理节点:

$ docker swarm join-to ken manager
To add a manager to this swarm, run the following command:

docker swarm join --token SWMTKN-1-1kal5b1iozbfmnnhx3kjfd3y6yqcjjjpcftrlg69pm2g8hw5vx-8fq89jxo2axwggryvom5a337t 192.168.31.100:2377

复制 Swarm 输出的结果即可加入管理节点到集群中。

注意:管理节点的数量必须为奇数,生产环境推荐使用3个、5个或7个管理节点来管理 Swarm 集群。

  • 第三步:节点查看

节点添加完成后,我们使用以下命令可以查看当前节点的状态:

$ ]# docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
1ehtnlcf3emncktgjzpoux5ga *   swarm-manager       Ready               Active              Leader              19.03.12
pn7gdm847sfzydqhcv3vma97y *   swarm-node1         Ready               Active                                        19.03.12
4dtc9pw5quyjs5yf25ccgr8uh *   swarm-node2         Ready               Active                                        19.03.12
est7ww3gngna4u7td22g9m2k5 *   swarm-node3         Ready               Active                                        19.03.12

到此,一个包含 1 个管理节点,3 个工作节点的 Swarm 集群已经搭建完成。

使用 Swarm

集群搭建完成后,我们就可以在 Swarm 集群中创建服务了,Swarm 集群中常用的服务部署方式有以下两种。

(1)通过 docker service 命令创建服务

使用docker service create命令可以创建服务,创建服务的命令如下:

$ docker service create --replicas 1 --name hello-world nginx
24f9ng83m9sq4ml3e92k4g5by
overall progress: 1 out of 1 tasks
1/1: running   [==================================================>]
verify: Service converged

此时我们已经创建好了一个服务,使用docker service ls命令可以查看已经启动的服务:

$ docker service ls
ID                  NAME                  MODE                REPLICAS            IMAGE               PORTS
24f9ng83m9sq        hello-world           replicated          1/1                 nginx:latest

当我们不再需要这个服务了,可以使用docker service rm命令来删除服务:

$ docker service rm hello-world
hello-world

此时 hello-world 这个服务已经成功地从集群中删除。
想要了解更多的docker service命令的相关操作,可以参考这里。

生产环境中,我们推荐使用 docker-compose 模板文件来部署服务,这样服务的管理会更加方便并且可追踪,而且可以同时创建和管理多个服务,更加适合生产环境中依赖关系较复杂的部署模式。

(2)通过 docker stack 命令创建服务

我们在 19 课时中创建了 docker-compose 的模板文件,成功的使用该模板文件创建并启动了 MySQL 服务和 WordPress 两个服务。这里我们将 19 讲中的 docker-compose 模板文件略微改造一下:

version: '3'

services:
   mysql:
     image: mysql:5.7
     volumes:
       - mysql_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: root
       MYSQL_DATABASE: mywordpress
       MYSQL_USER: mywordpress
       MYSQL_PASSWORD: mywordpress

wordpress:
     depends_on:
       - mysql
     image: wordpress:php7.4
     deploy:
       mode: replicated
       replicas: 2
     ports:
       - “8080:80”
     restart: always
     environment:
       WORDPRESS_DB_HOST: mysql:3306
       WORDPRESS_DB_USER: mywordpress
       WORDPRESS_DB_PASSWORD: mywordpress
       WORDPRESS_DB_NAME: mywordpress
volumes:
    mysql_data: {}

我在服务模板文件中添加了 deploy 指令,并且指定使用副本服务(replicated)的方式启动两个 WordPress 实例。

准备好启动 WordPress 服务的配置后,我们在 /tmp 目下新建 docker-compose.yml 文件,并且写入以上的内容,然后我们使用以下命令启动服务:

$ docker stack deploy -c docker-compose.yml wordpress
Ignoring unsupported options: restart

Creating network wordpress_default
Creating service wordpress_mysql
Creating service wordpress_wordpress

执行完以上命令后,我们成功启动了两个服务:

  1. MySQL 服务,默认启动了一个副本。

  2. WordPress 服务,根据我们 docker-compose 模板的定义启动了两个副本。

下面我们用docker service ls命令查看一下当前启动的服务。

$ docker service ls
ID                  NAME                  MODE                REPLICAS            IMAGE               PORTS
v8i0pzb4e3tc        wordpress_mysql       replicated          1/1                 mysql:5.7
96m8xfyeqzr5        wordpress_wordpress   replicated          2/2                 wordpress:php7.4    *:8080->80/tcp

可以看到,Swarm 已经为我们成功启动了一个 MySQL 服务,并且启动了两个 WordPress 实例。WordPress 实例通过 8080 端口暴露在了主机上,我们通过访问集群中的任意节点的 IP 加 8080 端口即可访问到 WordPress 服务。例如,我们访问http://192.168.31.101:8080即可成功访问到我们搭建的 WordPress 服务。

结语

Docker Swarm 是一个用来定义复杂应用的集群编排工具,可以帮我们把多台主机组成一个 Swarm 集群,并且帮助我们管理和调度复杂的容器服务。由于 Swarm 已经被内置于 Docker 中,因此 Swarm 的安装和使用也变得非常简单,只要你有 Docker 的使用经验,就可以很快地将你的应用迁移到 Swarm 集群中。

那么,学完本课时内容,你可以试着构建一个高可用(管理节点扩展为 3 个或 5 个)的 Swarm 集群吗?

下一课时,我将为你讲解目前使用最多的容器编排系统Kubernetes,再会。


21 如何使 Docker 和 Kubernete 结合发挥容器的最大价值?

Docker 虽然在容器领域有着不可撼动的地位,然而在容器的编排领域,却有着另外一个事实标准,那就是 Kubernetes。本课时,我就带你一起来认识下 Kubernetes。

Kubernetes 的前生今世

说起 Kubernetes,这一切还得从云计算这个词说起,云计算这个概念是 2006 年由 Google 提起的,近些年被提及的频率也越来越高。云计算从起初的概念演变为现在的 AWS、阿里云等实实在在的云产品(主要是虚拟机和相关的网络、存储服务),可见已经变得非常成熟和稳定。

正当大家以为云计算领域已经变成了以虚拟机为代表的云平台时,Docker 在 2013 年横空出世,Docker 提出了镜像、仓库等核心概念,规范了服务的交付标准,使得复杂服务的落地变得更加简单,之后 Docker 又定义了 OCI 标准,可以说在容器领域 Docker 已经成了事实的标准。

然而 Docker 诞生只是帮助我们定义了开发和交付标准,如果想要在生产环境中大批量的使用容器,还离不开的容器的编排技术。于是,在 2014 年 6 月 7 日,Kubernetes(Kubernetes 简称为 K8S,8 代表 ubernete 8个字母) 的第一个 commit(提交)拉开了容器编排标准定义的序幕。

Kubernetes 是舵手的意思,我们把 Docker 比喻成一个个集装箱,而 Kubernetes 正是运输这些集装箱的舵手。早期的 Kubernetes 主要参考 Google 内部的 Borg 系统,Kubernetes 刚刚诞生时,提出了 Pod、Sidecar 等概念,这些都是 Google 内部多年实战和沉淀所积累的精华。经过将近一年的沉淀和积累,Kubernetes 于 2015 年 7 月 21 日对外发布了第一个正式版本 v1.0,正式走入了大众的视线。

很荣幸,我也是在 2015 年下半年正式开始了 Kubernetes 和 Docker 的研发之路。时至今日,Kubernetes 经过 6 年的沉淀,已经成为了事实的编排技术标准。

接下来,我们就看来看看,究竟是什么样的架构使得 Kubernetes 在容器编排领域成为了王者?

Kubernetes 架构

Kubernetes 采用声明式 API 来工作,所有组件的运行过程都是异步的,整个工作过程大致为用户声明想要的状态,然后 Kubernetes 各个组件相互配合并且努力达到用户想要的状态。

Kubernetes 采用典型的主从架构,分为 Master 和 Node 两个角色。

  • Mater 是 Kubernetes 集群的控制节点,负责整个集群的管理和控制功能。

  • Node 为工作节点,负责业务容器的生命周期管理。

整体架构如下图:

图 1 Kubernetes 架构图(来源:Kubernetes 官网)

Master 节点

Master 节点负责对集群中所有容器的调度,各种资源对象的控制,以及响应集群的所有请求。Master 节点包含三个重要的组件: kube-apiserver、kube-scheduler、kube-controller-manager。下面我对这三个组件逐一介绍。

  • kube-apiserver

kube-apiserver 主要负责提供 Kubernetes 的 API 服务,所有的组件都需要与 kube-apiserver 交互获取或者更新资源信息,它是 Kubernetes Master 中最前端组件。

kube-apiserver 的所有数据都存储在 etcd 中,etcd 是一种采用 Go 语言编写的高可用 Key-Value 数据库,由 CoreOS 开发。etcd 虽然不是 Kubernetes 的组件,但是它在 Kubernetes 中却扮演着至关重要的角色,它是 Kubernetes 的数据大脑。可以说 etcd 的稳定性直接关系着 Kubernetes 集群的稳定性,因此生产环境中 etcd 一定要部署多个实例以确保集群的高可用。

  • kube-scheduler

kube-scheduler 用于监听未被调度的 Pod,然后根据一定调度策略将 Pod 调度到合适的 Node 节点上运行。

  • kube-controller-manager

kube-controller-manager 负责维护整个集群的状态和资源的管理。例如多个副本数量的保证,Pod 的滚动更新等。每种资源的控制器都是一个独立协程。kube-controller-manager 实际上是一系列资源控制器的总称。

为了保证 Kubernetes 集群的高可用,Master 组件需要部署在多个节点上,由于 Kubernetes 所有数据都存在于 etcd 中,Etcd 是基于 Raft 协议实现,因此生产环境中 Master 通常建议至少三个节点(如果你想要更高的可用性,可以使用 5 个或者 7 个节点)。

Node 节点

Node 节点是 Kubernetes 的工作节点,负责运行业务容器。Node 节点主要包含两个组件 :kubelet 和 kube-proxy。

  • kubelet

Kubelet 是在每个工作节点运行的代理,它负责管理容器的生命周期。Kubelet 通过监听分配到自己运行的主机上的 Pod 对象,确保这些 Pod 处于运行状态,并且负责定期检查 Pod 的运行状态,将 Pod 的运行状态更新到 Pod 对象中。

  • kube-proxy

Kube-proxy 是在每个工作节点的网络插件,它实现了 Kubernetes 的 Service 的概念。Kube-proxy 通过维护集群上的网络规则,实现集群内部可以通过负载均衡的方式访问到后端的容器。

Kubernetes 的成功不仅得益于其优秀的架构设计,更加重要的是 Kubernetes 提出了很多核心的概念,这些核心概念构成了容器编排的主要模型。

Kubernetes 核心概念

Kubernetes 这些概念是 Google 多年的技术沉淀和积累,理解 Kubernetes 的核心概念有助于我们更好的理解 Kubernetes 的设计理念。

(1)集群

集群是一组被 Kubernetes 统一管理和调度的节点,被 Kubernetes 纳管的节点可以是物理机或者虚拟机。集群其中一部分节点作为 Master 节点,负责集群状态的管理和协调,另一部分作为 Node 节点,负责执行具体的任务,实现用户服务的启停等功能。

(2)标签(Label)

Label 是一组键值对,每一个资源对象都会拥有此字段。Kubernetes 中使用 Label 对资源进行标记,然后根据 Label 对资源进行分类和筛选。

(3)命名空间(Namespace)

Kubernetes 中通过命名空间来实现资源的虚拟化隔离,将一组相关联的资源放到同一个命名空间内,避免不同租户的资源发生命名冲突,从逻辑上实现了多租户的资源隔离。

(4)容器组(Pod)

Pod 是 Kubernetes 中的最小调度单位,它由一个或多个容器组成,一个 Pod 内的容器共享相同的网络命名空间和存储卷。Pod 是真正的业务进程的载体,在 Pod 运行前,Kubernetes 会先启动一个 Pause 容器开辟一个网络命名空间,完成网络和存储相关资源的初始化,然后再运行业务容器。

(5)部署(Deployment)

Deployment 是一组 Pod 的抽象,通过 Deployment 控制器保障用户指定数量的容器副本正常运行,并且实现了滚动更新等高级功能,当我们需要更新业务版本时,Deployment 会按照我们指定策略自动的杀死旧版本的 Pod 并且启动新版本的 Pod。

(6)状态副本集(StatefulSet)

StatefulSet 和 Deployment 类似,也是一组 Pod 的抽象,但是 StatefulSet 主要用于有状态应用的管理,StatefulSet 生成的 Pod 名称是固定且有序的,确保每个 Pod 独一无二的身份标识。

(7)守护进程集(DaemonSet)

DaemonSet 确保每个 Node 节点上运行一个 Pod,当我们集群有新加入的 Node 节点时,Kubernetes 会自动帮助我们在新的节点上运行一个 Pod。一般用于日志采集,节点监控等场景。

(8)任务(Job)

Job 可以帮助我们创建一个 Pod 并且保证 Pod 的正常退出,如果 Pod 运行过程中出现了错误,Job 控制器可以帮助我们创建新的 Pod,直到 Pod 执行成功或者达到指定重试次数。

(9)服务(Service)

Service 是一组 Pod 访问配置的抽象。由于 Pod 的地址是动态变化的,我们不能直接通过 Pod 的 IP 去访问某个服务,Service 通过在主机上配置一定的网络规则,帮助我们实现通过一个固定的地址访问一组 Pod。

(10)配置集(ConfigMap)

ConfigMap 用于存放我们业务的配置信息,使用 Key-Value 的方式存放于 Kubernetes 中,使用 ConfigMap 可以帮助我们将配置数据和应用程序代码分开。

(11)加密字典(Secret)

Secret 用于存放我们业务的敏感配置信息,类似于 ConfigMap,使用 Key-Value 的方式存在于 Kubernetes 中,主要用于存放密码和证书等敏感信息。

了解完 Kubernetes 的架构和核心概念,你是不是已经迫不及待地想要体验下了。下面就让我们动手安装一个 Kubernetes 集群,来体验下 Kubernetes 的强大之处吧。

安装 Kubernetes

Kubernetes 目前已经支持在多种环境下安装,我们可以在公有云,私有云,甚至裸金属中安装 Kubernetes。下面,我们通过 minikube 来演示一下如何快速安装和启动一个 Kubernetes 集群,minikube 是官方提供的一个快速搭建本地 Kubernetes 集群的工具,主要用于本地开发和调试。

下面,我以 Linux 平台为例,演示一下如何使用 minikube 安装一个 Kubernetes 集群。

如果你想要在其他平台使用 minikube 安装 Kubernetes,请参考官网安装教程。
在使用 minikube 安装 Kubernetes 之前,请确保我们的机器已经正确安装并且启动 Docker。

第一步,安装 minikube 和 kubectl。首先执行以下命令安装 minikube。

$ curl -LO https://github.com/kubernetes/minikube/releases/download/v1.13.1/minikube-linux-amd64
$ sudo install minikube-linux-amd64 /usr/local/bin/minikube

Kubectl 是 Kubernetes 官方的命令行工具,可以实现对 Kubernetes 集群的管理和控制。
我们使用以下命令来安装 kubectl:

$ curl -LO https://dl.k8s.io/v1.19.2/kubernetes-client-linux-amd64.tar.gz
$ tar -xvf kubernetes-client-linux-amd64.tar.gz
kubernetes/
kubernetes/client/
kubernetes/client/bin/
kubernetes/client/bin/kubectl
$ sudo install kubernetes/client/bin/kubectl /usr/local/bin/kubectl

第二步,安装 Kubernetes 集群。
执行以下命令使用 minikube 安装 Kubernetes 集群:

$ minikube start

执行完上述命令后,minikube 会自动帮助我们创建并启动一个 Kubernetes 集群。命令输出如下,当命令行输出 Done 时,代表集群已经部署完成。

![在这里插入图片描述](https://img-blog.csdnimg.cn/f5ae3d5b13a442e089a9eccd2ecda5ca.png#pic_center)

第三步,检查集群状态。集群安装成功后,我们可以使用以下命令检查 Kubernetes 集群是否成功启动。

$ kubectl cluster-info
Kubernetes master is running at https://172.17.0.3:8443
KubeDNS is running at https://172.17.0.3:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.

执行kubectl cluster-info命令后,输出 "Kubernetes master is running" 表示我们的集群已经成功运行。

172.17.0.3 为演示环境机器的 IP 地址,这个 IP 会根据你的实际 IP 地址而变化。

创建第一个应用

集群搭建好后,下面我们来试着使用 Kubernetes 来创建我们的第一个应用。

这里我们使用 Deployment 来定义应用的部署信息,使用 Service 暴露我们的应用到集群外部,从而使得我们的应用可以从外部访问到。

第一步,创建 deployment.yaml 文件,并且定义启动的副本数(replicas)为 3。

apiVersion: apps/v1
kind: Deployment
metadata:name: hello-world
spec:replicas: 3selector:matchLabels:app: hello-worldtemplate:metadata:labels:app: hello-worldspec:containers:- name: hello-worldimage: wilhelmguo/nginx-hello:v1ports:- containerPort: 80

第二步,发布部署文件到 Kubernetes 集群中。

$ kubectl create -f deployment.yaml

部署发布完成后,我们可以使用 kubectl 来查看一下 Pod 是否被成功启动。

$ kubectl get pod -o wide
NAME                           READY   STATUS    RESTARTS   AGE     IP           NODE       NOMINATED NODE   READINESS GATES
hello-world-57968f9979-xbmzt   1/1     Running   0          3m19s   172.18.0.7   minikube   <none>           <none>
hello-world-57968f9979-xq5w4   1/1     Running   0          3m18s   172.18.0.5   minikube   <none>           <none>
hello-world-57968f9979-zwvgg   1/1     Running   0          4m14s   172.18.0.6   minikube   <none>           <none>

这里可以看到 Kubernetes 帮助我们创建了 3 个 Pod 实例。

第三步,创建 service.yaml 文件,帮助我们将服务暴露出去,内容如下:

apiVersion: v1
kind: Service
metadata:name: hello-world
spec:type: NodePortports:- port: 80targetPort: 80selector:app: hello-world

然后执行如下命令在 Kubernetes 中创建 Service:

kubectl create -f service.yaml

服务创建完成后,Kubernetes 会随机帮助我们分配一个外部访问端口,可以通过以下命令查看服务信息:

$ kubectl  get service -o wide
NAME          TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE   SELECTOR
hello-world   NodePort    10.101.83.18   <none>        80:32391/TCP   12s   app=hello-world
kubernetes    ClusterIP   10.96.0.1      <none>        443/TCP        40m   <none>

由于我们的集群使用 minikube 安装,要想集群中的服务可以通过外部访问,还需要执行以下命令:

$ minikube service hello-world

输出如下:

可以看到 minikube 将我们的服务暴露在了 32391 端口上,我们通过 http://{YOUR-IP}:32391 可以访问到我们启动的服务,如下图所示。

图 2 服务请求结果

总结下,我们首先使用 Deployment 创建了三个 nginx-hello 的实例,然后使用 Service 的方式随机负载到后端的三个实例,并将服务通过 NodePort 的方式暴露在主机上,使得我们可以直接使用主机的端口访问到容器中的服务。

结语

Kubernetes 从诞生到现在已经经历了 6 个年头,起初由于它的超前理念被世人误认为设计过度复杂,使得 Kubernetes 的入门门槛非常高。然而 6 年后的今天, Kubernetes 已经拥有了非常完善的社区和工具集,它可以帮助我们一键搭建 Kubernetes 集群,并且围绕 Kubernetes 构建的各种应用也是越来越丰富。

Kubernetes 的目标一直很明确,那就是对标 Borg,可以支撑数亿容器的运行。目前来看,要达到这个目标,Kubernetes 还有很长的路要走,但是当我们谈及云原生,谈及容器云时都必然会提到 Kubernetes,显然它已经成为容器编排的标准和标杆,目前大多数公有云也有支持 Kubernetes。容器的未来一定是美好的,而使用 Kubernetes 来调度容器则更是未来云计算的一个重要风向标。

那么,你的朋友中有没有人从事过 Kubernetes 或 Docker 相关的项目研发,现在这些项目发展得怎么样了呢?欢迎留言和我一起讨论容器圈创业那点事。

下一课时,我将为你带来 Docker 的综合实战案例,Docker 下如何实现镜像多阶级构建?


Docker容器化实战第七课 容器编排Docker Compose、Docker Swarm 和 Kubernetes相关推荐

  1. Docker容器化实战第八课 DevOps和CI/CD

    22 多阶段构建:Docker 下如何实现镜像多阶级构建? 通过前面课程的学习,我们知道 Docker 镜像是分层的,并且每一层镜像都会额外占用存储空间,一个 Docker 镜像层数越多,这个镜像占用 ...

  2. Docker容器化实战第三课 dockerfile介绍、容器安全与监控讲解

    06 最佳实践:如何在生产中编写最优 Dockerfile? 在介绍 Dockerfile 最佳实践前,这里再强调一下,生产实践中一定优先使用 Dockerfile 的方式构建镜像. 因为使用 Doc ...

  3. 量化交易 实战第七课 单因子 IC 分析

    量化交易 实战第七课 单因子 IC 分析 概述 代码实现 导包 1. 准备因子数据 2. 准备价格数据 3. 生成通用 Alphalens 结构 4. 计算因子 因子 IC 结果分析 时间序列图和移动 ...

  4. Docker容器化实战第二课 镜像、容器、仓库详解

    03 镜像使用:Docker 环境下如何配置你的镜像? 今天我将围绕 Docker 核心概念镜像展开,首先重点讲解一下镜像的基本操作,然后介绍一下镜像的实现原理.首先说明,咱们本课时的镜像均指 Doc ...

  5. 【云原生】docker+k8微服务容器化实战

    博主昵称:跳楼梯企鹅 博主主页面链接:博主主页传送门 博主专栏页面连接:专栏传送门--网路安全技术 创作初心:本博客的初心为与技术朋友们相互交流,每个人的技术都存在短板,博主也是一样,虚心求教,希望各 ...

  6. 分布式技术与实战第七课 高并发下高可用的熔断、降级、限流和负载均衡、监控以及统一的日志系统

    第39讲:从双十一看高可用的保障方式 从这一课时开始,专栏内容进入最后一个模块,即分布式高可用系列,这部分的内容,我将以电商大促为背景,讲解系统限流.降级熔断.负载均衡.稳定性指标.系统监控和日志系统 ...

  7. 微服务容器化运维:微博容器运维平台DCP

    微服务容器化运维系列的前两期,我给你详细介绍了微服务容器化后如何运维的几个关键问题:镜像仓库.资源调度.容器调度.服务编排,这些问题的产生都是因为微服务部署的节点从一台台物理机或者虚拟机变成了一个个容 ...

  8. Docker最全教程之MySQL容器化 (二十五)

    前言 MySQL是目前最流行的开源的关系型数据库,MySQL的容器化之前有朋友投稿并且写过此块,本篇仅从笔者角度进行总结和编写. 目录 镜像说明  运行MySQL容器镜像  1.运行MySQL容器  ...

  9. Docker最全教程——数据库容器化之持久保存数据(十一)

    Docker最全教程--数据库容器化之持久保存数据(十一) 原文:Docker最全教程--数据库容器化之持久保存数据(十一) 上一节我们讲述了SQL Server容器化实践(注意,SQL Server ...

最新文章

  1. make--变量与函数的综合示例 自动生成依赖关系
  2. SRM 397(1-250pt)
  3. 关于bjam编译自己模块出错的问题
  4. Windows API之WriteFile函数详细解答
  5. java.io.NotSerializableException: org.apache.shiro.util.SimpleByteSource at java.io.ObjectOutputStr
  6. 电信运营商计费模型_商客通:南京电信400电话怎么办理更省心
  7. Nginx upstream (一) 整体流程分析
  8. Leet Code OJ 125. Valid Palindrome [Difficulty: Easy]
  9. vue-cli 相同页面的跳转,但路由参数不同的情况下 组件状态没有更新的问题是为什么 如何解决
  10. Android中让Service被关闭后再重新启动
  11. Object Clustering(POJ-3214)
  12. ModifyStyle ModifyStyleEx修改自定义控件的问题
  13. 怎么进行finebi下载
  14. 计算机学院宣传橱窗,校园橱窗、报栏、展板、宣传标语管理办法
  15. Robot fish: bio-inspired fishlike underwater robots 阅读笔记 1
  16. Mac pro20版本的Endnote20关联word经历
  17. 妻子和鱼----海子
  18. nginx iis 502 错误处理
  19. 2020年感冒人群大幅度减少,不感冒意味着身体变好?
  20. Gdal库计算形心方法。

热门文章

  1. PBC Library Manual(PBC库手册)翻译(一)
  2. python中PIL的安装参考教程
  3. 我与博友们分享我的工作经验
  4. 屏幕录制软件camtasia studio 8序列号激活
  5. Mysql 与ES(Elastic Search)对比
  6. OpencCV制作相框效果
  7. Win10+Cuda10.2的Pytorch安装
  8. Failed to connect to the host via ssh: Control socket connect(/root/.ansible/cp/xxx): Connect
  9. 【Maven】你好,Maven >>> 与Maven的初次见面~
  10. ProGet 22.0 Enterprise Crack by Xacker