Docker 的部署方式
in 技术 with 0 comment

Docker 的部署方式

in 技术 with 0 comment

在使用 docker run 命令启动 Docker 容器时,如果需要进行端口映射、目录挂载、网络信息等配置,整条命令将变得非常长,并且由于是一条 shell 命令,修改和复用也不方便。我们在大规模部署容器的时候不可能手动去输入众多的命令,所以需要一些工具来辅助我们实现 docker run 命令的编写,同时实现简单快捷的大规模部署。

docker-compose 部署

docker-compose 是一个读取特定格式的 yaml 文件并将其转换为 docker run 命令的工具,它有效的规避了上述的问题,并且它也是 docker swarmdocker stack 等技术的基石。docker-compose 需要一份 yaml 格式的脚本,如果在使用命令时不想指定具体的脚本名称,那就需要将脚本命名为 docker-compose.yml。下面是一份启动 MySQL 容器的 docker-compose 脚本。

version: "3"
services:
  mysql:
    container_name: login_db
    image: mysql:5.7
    ports:
      - "3306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=123456
    volumes:
      - "mysql-data:/var/lib/mysql"
    networks:
      - my-bridge

volumes:
  mysql-data:

networks:
  my-bridge:
    driver: bridge

全局配置

version 指定的是 docker-compose 的版本,由于 v2v3 在语法上存在一些不同,所以需要明确告诉 docker-compose 当前脚本所使用的语法版本是多少。

services 表示服务定义。一份脚本中可以定义多个服务,docker-compose 会一并启动。

volumes 下的名称列表就是服务启动时要创建的所有数据卷的名称,只有先创建了数据卷才能在下面的服务定义中的 volumes 配置中进行挂载使用。注意区分全局的 volumes 和服务定义下的 volumes 配置

networks 下配置的是服务启动时要创建的网络及其驱动类型。这里创建了一个驱动为 bridge、名称为 my-bridge 的桥接网络。只有先创建了网络才能在下面的服务定义中的 networks 配置中进行注册使用。注意区分全局的 networks 和服务定义下的 networks 配置

服务配置

mysql 表示定义一个名叫 mysql 的服务。

container_name 表示服务要启动的容器的名称,如果这个服务要动态扩展多个容器,则不可以指定容器名称,否则由于容器名称冲突将导致无法扩展。

image 表示服务要使用的镜像。

ports 表示容器与宿主机的端口映射关系。凡是指定了端口映射关系的服务都不能动态扩展容器数量,因为会导致端口冲突。

environment 表示要设置到容器中的环境变量。这里设置的环境变量将被放置到容器的全局环境变量中,你可以在容器中读取并操作。

volumes 表示要挂载的目录或者数据卷。这里和 docker run 命令中的 -v 参数作用是一致的,即可以挂载数据卷,也可以挂载目录。这里使用到的数据卷必须在全局配置中先指定,如果是挂载目录则需要先手动创建。

networks 表示这个服务要注册到哪些网络上去,这里使用到的网络必须在全局配置中先指定。注册到同一个网络上的容器之间可以使用服务名进行通信。

使用 docker-compose

通过 docker-compose 可以对服务进行启动、停止、删除、扩容等操作。docker-compose 的操作必须依赖脚本,如果脚本存在于当前目录下且名为 docker-compose.yml 则不需要额外指定,否则需要使用 -f 参数进行指定。

启动服务

启动服务使用命令 docker-compose -f /path/to/script.yml up -dup 表示启动服务,-d 表示后台运行。这个命令会读取脚本文件并首先创建申明的数据卷和网络,然后再启动服务。

停止服务

停止服务使用命令 docker-compose -f /path/to/script.yml stop。这个命令会将脚本中的所有服务的所有容器都停止,但不会删除容器。

删除服务

删除服务使用命令 docker-compose -f /path/to/script.yml rm。这个命令会要求二次确认删除,并且无法删除未停止的服务。删除服务时不会关联删除服务启动时创建的网络和数据卷。

强行删除服务

强行删除服务使用命令 docker-compose -f /path/to/script.yml down。这个命令会将所有服务的所有容器都停止并删除,同时删除服务启动时创建的网络,但不会删除数据卷

扩容服务

随着业务量的上升,我们可能需要将服务从一个容器扩展到多个容器以提高服务能力,这时候就可以使用 docker-compose 来直接扩容服务。

docker-compose -f /path/to/script.yml up --scale orderService=3 -d

这个命令表示将名为 orderService 的服务扩容至三个容器并后台启动。如果原本的容器数量大于 3 个,那么这个命令就是缩减容器数量操作。需要注意的是,支持服务扩容的要求是非常苛刻的,需要满足以下三点要求:

  1. 不能够指定 container_name,即容器名;
  2. 不能够指定端口映射关系;
  3. 不能够指定挂载数据卷或目录。

如果在脚本中指定了要挂在的目录或者数据卷,那么扩容后多个容器将共用一个数据卷和目录,这样就会导致数据出现混乱。解决方法是不指定任何数据卷和目录进行挂载,由 Docker 自行创建随机名称的数据卷。

Docker Swarm 部署

上文讲到的 docker-compose 部署方式还停留在单机部署上,但在实际生产环境中不同系统的 Docker 容器都是垮宿主机部署,这时候就需要将这些宿主机形成集群统一管理,Docker Swarm 就是 Docker 原生提供的一种集群管理模式。除了 Docker Swarm 外,还有 Mesos、Kubernates 等不同的集群管理模式。

在 Docker Swarm 集群模式下,当多个宿主机形成集群后,我们就可以在管理节点(Manager Node)上通过管理命令将不同服务的容器部署到集群内不同的宿主机上,同一个服务的多个容器也可以分布到集群内不同的宿主机上以实现负载均衡。

创建 Swarm 集群

创建集群之前需要先规划好集群内的节点角色,选择其中一台宿主机作为管理节点开始创建集群,执行如下命令:

docker swarm init --advertise-addr=本机IP

--advertise-addr 用于指定管理节点所在宿主机的 eth0 网卡的 IP 地址,如果存在多个网卡,这个参数一定要指定,否则可能造成管理节点和工作节点之间无法通信。命令执行完毕后会输出如下的提示信息:

Swarm initialized: current node (gmdscjfdlubanwl7i75z5cc85) is now a manager.

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

    docker swarm join \
    --token SWMTKN-1-6djatxtetutac68xd1u8v1icnyv6t0pcplhaph2irqqxqo1m2b-8w6lq2kpw6j1chqpu4vlf2cx3 \
    管理节点IP:2377

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

根据输出提示,我们需要在其他宿主机上执行 docker swarm join … 操作以使其加入到集群作为工作节点。集群节点加入完成后在管理节点执行 docker node ls 可以看到所有的节点状态信息。

ID                           HOSTNAME  STATUS  AVAILABILITY  MANAGER STATUS
gmdscjfdlubanwl7i75z5cc85 *  docker-1  Ready   Active        Leader
xna7a0h6a0xhct95kh7v6p9pl    docker-3  Ready   Active

使用标签标记节点

Docker Swarm 会根据自己的负载均衡算法将服务分散部署到不同的集群节点上,但有时候我们也希望能够指定服务部署到指定的集群节点上,这时候就需要通过标签来指定具体的节点了。下面的名称尝试为 HOSTNAME 是 docker-3 的节点增加一个标签:

docker node update --label-add mytag=db xna7a0h6a0xhct95kh7v6p9pl

这条命令为 ID 为 xna7a0h6a0xhct95kh7v6p9pl 的节点增加了一个标签,标签名称为 mytag,标签值为 db。标签的使用会在下文 Docker Stack 部署服务时讲解。

在 Swarm 集群中部署服务

有了集群就可以进行服务部署,下面尝试在集群中部署一个 MySQL 服务,在管理节点执行命令如下:

docker service create --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

命令的参数和 docker run 命令的参数是基本一致的,只是命令换成了 docker service create。命令 docker service ls 用于查看所有已部署的服务;命令 docker service ps 服务名称 用于查看该服务容器的部署节点和状态:

ID            NAME     IMAGE      NODE      ESIRED STATE  CURRENT STATE          ERROR  PORTS
0m5erytxi6sa  mysql.1  mysql:5.7  docker-1  Running        Running 3 minutes ago

上面的信息表示 MySQL 服务下共启动了 1 个容器,其部署在名为 docker-1 的节点上,容器ID 为 0m5erytxi6sa,当前状态是正在运行,并且与期望的状态一致。

扩容服务

与使用 docker-compose 部署服务类似,docker service 命令也可以让指定的服务进行容量增减,下面的命令尝试将 MySQL 服务扩容至 3 个容器:

docker service scale mysql=3

再次执行 docker service ps mysql 查看容器部署情况:

ID            NAME     IMAGE      NODE      DESIRED STATE  CURRENT STATE           ERROR  PORTS
0m5erytxi6sa  mysql.1  mysql:5.7  docker-1  Running        Running 13 minutes ago
idgvqymekwam  mysql.2  mysql:5.7  docker-3  Running        Running 3 minutes ago
fldlrega7p40  mysql.3  mysql:5.7  docker-3  Running        Running 3 minutes ago

可以看到容器数量已经扩大至 3 个,其中两个部署在 docker-3 节点上。

更新服务

在服务使用多容器部署的情况下,可以使用服务更新来发布新的版本或调整服务部署参数,这样可以避免先删除服务再启动服务造成的服务中断。如下命令尝试使用新版本镜像来更新服务:

docker service update --image mysql:5.8 mysql

这个命令表示对名为 mysql 的服务使用使用 mysql:5.8 版本的镜像进行更新。需要注意的是,如果服务只部署了一个容器,那么更新过程中,对外服务是肯定会中断的。

删除服务

使用命令 docker service rm 服务名称 可以将已部署的服务删除。删除服务时服务下所有的容器都会被删除且不可恢复。

使用 Docker Stack 管理多个服务

Stack

在 Swarm 模式下,使用 docker service create 命令可以创建服务进行部署,当多个服务组合到一起时,我们就把它们看作是一个 Stack。简单来说,**Stack 就是多个 Service 的组合。在 Swarm 模式下可以使用 Docker Stack 可以实现服务的批量部署。

Docker Stack 脚本

Docker Stack 并不是新的技术点,它同样是使用 docker-compose 脚本来管理服务创建脚本,并且语法没有任何区别。下面的示例展示了怎么使用 Docker Stack 来部署一个 WordPress 和 MySQL 服务,其中 WordPress 服务需要引用 MySQL 服务进行数据读写:

version: '3'
services:
  wordpress:
    image: wordpress
    ports:
      - 80:80
    environment:
      - WORDPRESS_DB_HOST=mysql
      - WORDPRESS_DB_PASSWORD=123456
    networks:
      - my-network
    depends_on:
      - mysql
    deploy:
      mode: replicated
      replicas: 3
      restart_policy:
        condition: on-failure
        delay: 5s
        max_attempts: 3
      update_config:
        parallelism: 1
        delay: 10s

  mysql:
    image: mysql:5.7
    environment:
      - MYSQL_ROOT_PASSWORD=123456
      - MYSQL_DATABASE=wordpress
    volumes:
      - mysql-data:/var/lib/mysql
    networks:
      - my-network
    deploy:
      mode: global
      placement:
        constraints: [node.labels.mytag == db]

volumes:
  mysql-data:

networks:
  my-network:
    driver: overlay

wordpress 服务的 environment 中配置的环境变量 WORDPRESS_DB_HOST 的值是 mysql,这个 mysql 表示的是下面的 mysql 服务。由于 wordpress 服务和 mysql 服务都注册到了 my-network 网络上,所以他们彼此可以通过服务名称进行通信,而不需要指定容器 IP。

depends_on 用于配置依赖关系。这里的配置表示 wordpress 服务必须等 mysql 服务启动后才开始启动,通过这个指令可以组织服务的启动顺序。

deploy 指令下配置的是部署相关的策略信息。

placement 用于配置节点的部署位置。constraints 用于指定部署位置的判断条件,上文中的 node.labels.mytag == db 表示将 mysql 服务部署到标签 mytag 的值为 db 的节点上。

部署使用的网络 my-network 的驱动是 overlay,这是一种跨主机通信的网络,在 Swarm 模式下为了让分布在集群内不同节点上的容器能够互相通信,他们就都必须注册到 overlay 驱动的网络上。我们也可以预先手动创建一个 overlay 驱动的网络:

docker network create -d overlay my-network

这样我们在 docker-compose 脚本中声明网络时就需要进行如下变更:

networks:
  my-network:
    external: true

external: true 这里的 my-network 网络引用的是预先创建好的同名网络。

部署 Stack

编写好 docker-compose 脚本后就可以使用 docker stack 命令进行服务部署了,假设上文的脚本命名为 myweb.yml, 下面的命令使用上文的脚本进行服务部署:

docker stack deploy -c myweb.yml myweb

-c 参数是 --compose-file 的缩写,用于指定部署脚本的位置,最后的 myweb 是这个 Stack 的名字,可以任意设置,通常建议和脚本名称保持一致,便于管理。命令 docker stack ls 可以查看部署的所有 Stack;命令 docker stack ps Stack名称 可以查看该 Stack 下所有服务的容器部署情况;命令 docker stack services Stack 名称 可以查看该 Stack 下所有服务的部署情况,如部署模式、副本数量等信息。

删除 Stack

命令 docker stack rm Stack名称 可以删除一个指定的 Stack。这个命令会删除该 Stack 下所有的服务及其所有的容器,无法恢复。

规划 Stack

上文提到删除 Stack 时,其下所有服务都会被删除,那么规划 Stack 就变得尤为重要。如果在规划时将所有服务都放到一个 Stack 下,那么当需要重新部署某个服务时就不得不将其他所有服务也全部重新部署。通常建议将高耦合的服务放到一个 Stack中,每个中间件集群作为一个 Stack,这样可以避免重新部署时发生雪崩。总之,规划 Stack 与编写代码类似,都要减少相互的耦合,尽量避免出现雪崩。

Responses