容器服务Docker&Kubernetes + 关注
手机版

在阿里云上体验Docker 1.12的路由能力和容器应用分发部署

  1. 云栖社区>
  2. 容器服务Docker&Kubernetes>
  3. 博客>
  4. 正文

在阿里云上体验Docker 1.12的路由能力和容器应用分发部署

易立 2016-07-09 22:35:55 浏览10955 评论7

摘要: Docker 1.12除了提供内置的编排能力,也提供了服务路由支持 routing mesh,和新的多容器应用分发和部署机制, Docker Stack和Distributed Application Bundle

14680725458431

阿里云容器服务团队将为大家奉献一系列深入学习的文章来帮助大家了解Docker 1.12的最新动态。

简述

Docker 1.12除了提供内置的编排能力,也提供了服务路由支持 routing mesh,和新的多容器应用分发和部署机制。

这里我们先介绍两个概念

  • 分布式应用打包 (Distributed Application Bundle,简称DAB):DAB 是一个用于应用分发的文件格式,其中可以包含多个容器服务定义。
  • Stack(应用栈):可以从一个DAB文件创建一个应用栈实例,一个应用栈中包含多个服务实例。

如果你熟悉Docker Compose,这些概念听起来是不是很熟悉?一个Docker Compose模板中可以包含多个服务的描述,而利用Compose模板可以创建一个完整的应用。但是Docker Compose本身被设计成为面向单机的开发工具,并不能对分布式应用进行部署、管理。而DAB提供了一个标准化的分发格式解决了这个问题。另外Docker提供了工具,可以将一个已有Docker Compose模板转换成DAB格式,并部署为Docker Stack实例。

14680556259171

本文中,我们会部署一个WordPress DAB应用到阿里云上的Swarm模式Docker集群中,其中包含两个服务wordpress和mysql。我们还会利用阿里云 SLB实现wordpress任务容器的路由和负载均衡。

集群创建

首先我们先构建一个Docker Swarm模式集群,有两种方法一种是利用ROS资源编排模板一键创建一个Swarm集群,另一种方法则是我们前文介绍的利用Docker Machine手工创建完整的环境。为了加深理解,我们这次采用分解动作,使用第二种方法进行集群创建。

在这个环境中我们将创建一个三个ECS实例构成的Swarm集群,其中一个是manager。另外我们会创建一个SLB实例来实现服务的路由和负载均衡,集群中三个ECS实例都会作为SLB的后端服务器。我们会在集群上部署应用,并配置服务路由。大概的拓扑示意图如下:

14694976178970

首先我们创建一个SLB实例

14679813399994

SLB创建成功之后,我们利用docker-machine来创建三个ECS节点来组成Docker集群执行环境。

我们首先在命令行上配置一些共用的环境变量

export DEBUG=true 
export ECS_ACCESS_KEY_ID=<您的Access Key ID>
export ECS_ACCESS_KEY_SECRET=<您的Access Key Secret>
export ECS_REGION=cn-beijing
export ECS_ZONE=cn-beijing-b
export ECS_SSH_PASSWORD=<ECS实例SSH密码>
export ECS_UPGRADE_KERNEL=true
export MACHINE_DOCKER_INSTALL_URL= http://acs-public-mirror.oss-cn-hangzhou.aliyuncs.com/docker-engine/experimental/intranet
export ENGINE_REGISTRY_MIRROR=https://6udu7vtl.mirror.aliyuncs.com
export ECS_SLB_ID=<您的SLB实例ID>

然后我们可以通过如下命令在阿里云上创建3个ECS实例,它还会自动升级操作系统内核,安装配置最新的测试版Docker引擎,并将新创建的SLB实例添加为指定SLB的后端服务器。

docker-machine create -d aliyunecs node-1
docker-machine create -d aliyunecs node-2
docker-machine create -d aliyunecs node-3

等待三个ECS实例会创建完成,我们可以通过如下命令登录到 node-1,初始化Docker Swarm集群,显示master节点的ECS内网"eth0"地址

docker-machine ssh node-1 ip addr show eth0
docker-machine ssh node-1 docker swarm init --advertise-addr <NODE1_IP>

上面的命令会返回如下内容

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

To add a worker to this swarm, run the following command:
    docker swarm join \
    --token SWMTKN-1-1xxxxxxx-xxxxxxx \
    10.xx.xx.xx:2377

To add a manager to this swarm, run the following command:
    docker swarm join \
    --token SWMTKN-1-1xxxxxxx-xxxxxxx \
    10.xx.xx.xx:2377

之后,我们把node-2和node-3作为worker加入Docker Swarm集群,执行docker-machine ssh <NODE> 之后拷贝上文返回的第一个命令来添加一个worker节点

docker-machine ssh node-2 docker swarm join --token <WORKER_TOKEN> <NODE1_IP>:2377
docker-machine ssh node-3 docker swarm join --token <WORKER_TOKEN> <NODE1_IP>:2377

这样整个Docker集群就已经创建成功了,现在我们来部署一个WordPress应用。

生成Distributed Application Bundle

注:这一节是为了介绍DAB的创建过程,心急的同学可以忽略。

本文使用Wordpress作为实例,其Docker Compose模板如下

version: '2'
services:
  wordpress:
    image: wordpress:4.5.3
    environment:
      - WORDPRESS_DB_PASSWORD=password
      - WORDPRESS_AUTH_KEY=changeme
      - WORDPRESS_SECURE_AUTH_KEY=changeme
      - WORDPRESS_LOGGED_IN_KEY=changeme
      - WORDPRESS_NONCE_KEY=changeme
      - WORDPRESS_AUTH_SALT=changeme
      - WORDPRESS_SECURE_AUTH_SALT=changeme
      - WORDPRESS_LOGGED_IN_SALT=changeme
      - WORDPRESS_NONCE_SALT=changeme
      - WORDPRESS_NONCE_AA=changeme
    ports:
      - 80:80
    restart: always
  mysql:
    image: mysql:5.7.13
    environment:
      - MYSQL_ROOT_PASSWORD=password
    restart: always

这里我们可以看到它包含两个服务"wordpress"和"mysql", 我们可以利用 docker-compose bundle 命令生成DAB格式文件。

yili@yili-mbp:~/work/docker-experimental/wordpress$ docker-compose bundle
WARNING: Unsupported key 'restart' in services.wordpress - ignoring
WARNING: Unsupported key 'restart' in services.mysql - ignoring
Wrote bundle to wordpress.dab

注:目前DAB/Docker Service/Stack的能力还不完备,有很多Docker Compose声明还不能被正确处理,比如volume支持等等。

产生的"wordpress.dab"文件内容如下

{
  "Services": {
    "mysql": {
      "Env": [
        "MYSQL_ROOT_PASSWORD=password"
      ], 
      "Image": "mysql@sha256:a9a5b559f8821fe73d58c3606c812d1c044868d42c63817fa5125fd9d8b7b539", 
      "Networks": [
        "default"
      ]
    }, 
    "wordpress": {
      "Env": [
        "WORDPRESS_AUTH_SALT=changeme", 
        "WORDPRESS_LOGGED_IN_KEY=changeme", 
        "WORDPRESS_AUTH_KEY=changeme", 
        "WORDPRESS_NONCE_AA=changeme", 
        "WORDPRESS_SECURE_AUTH_SALT=changeme", 
        "WORDPRESS_NONCE_KEY=changeme", 
        "WORDPRESS_DB_PASSWORD=password", 
        "WORDPRESS_LOGGED_IN_SALT=changeme", 
        "WORDPRESS_NONCE_SALT=changeme", 
        "WORDPRESS_SECURE_AUTH_KEY=changeme"
      ], 
      "Image": "wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b", 
      "Networks": [
        "default"
      ], 
      "Ports": [
        {
          "Port": 80, 
          "Protocol": "tcp"
        }
      ]
    }
  }, 
  "Version": "0.1"
}

关于DAB格式的详细定义请参见官方文档注意:目前DAB依然是一个实验性质的特性,其格式可能会在未来进行调整。

创建Wordpress Stack

我们要登录到集群的master节点来部署应用,首先我们从Github上获取相应的示例文件

git clone https://github.com/denverdino/docker-experimental
cd docker-experimental/wordpress/

利用 docker deploy命令来部署"wordpress" stack,Docker命令缺省会在本目录寻找同名的.dab文件作为Stack部署描述。

root@node-1:~/docker-experimental/wordpress# docker deploy wordpress
Loading bundle from wordpress.dab
Creating network wordpress_default
Creating service wordpress_mysql
Creating service wordpress_wordpress

之后,我们可以利用docker service lsdocker stack tasks命令来查看相应service/task状态

root@node-1:~/docker-experimental/wordpress# docker service ls
ID            NAME                 REPLICAS  IMAGE                                                                              COMMAND
3p616th5ypgc  wordpress_wordpress  1/1       wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  
dzyof5wz1s5y  wordpress_mysql      1/1       mysql@sha256:a9a5b559f8821fe73d58c3606c812d1c044868d42c63817fa5125fd9d8b7b539      
root@node-1:~/docker-experimental/wordpress# docker stack tasks wordpress
ID                         NAME                   SERVICE              IMAGE                                                                              LAST STATE       DESIRED STATE  NODE
2d9bjq6htn7xbcweocy192ubx  wordpress_wordpress.1  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 1 hours  Running        node-2
b31ol0r0eow2b4a5ut684dofy  wordpress_mysql.1      wordpress_mysql      mysql@sha256:a9a5b559f8821fe73d58c3606c812d1c044868d42c63817fa5125fd9d8b7b539      Running 1 hours  Running        node-3

现在我们通过docker service inspect命令检查"wordpress_wordpress"服务状态

root@node-1:~# docker service inspect wordpress_wordpress
[
    {
        "ID": "3p616th5ypgcrfxfb4a9v44ew",
        "Version": {
            "Index": 498
        },
        "CreatedAt": "2016-07-09T07:43:25.368994849Z",
        "UpdatedAt": "2016-07-09T07:43:25.378275593Z",
        "Spec": {
            "Name": "wordpress_wordpress",
            "Labels": {
                "com.docker.stack.namespace": "wordpress"
            },
            "TaskTemplate": {
                "ContainerSpec": {
                    "Image": "wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b",
                    "Env": [
                        "WORDPRESS_AUTH_SALT=changeme",
                        "WORDPRESS_LOGGED_IN_KEY=changeme",
                        "WORDPRESS_AUTH_KEY=changeme",
                        "WORDPRESS_NONCE_AA=changeme",
                        "WORDPRESS_SECURE_AUTH_SALT=changeme",
                        "WORDPRESS_NONCE_KEY=changeme",
                        "WORDPRESS_DB_PASSWORD=password",
                        "WORDPRESS_LOGGED_IN_SALT=changeme",
                        "WORDPRESS_NONCE_SALT=changeme",
                        "WORDPRESS_SECURE_AUTH_KEY=changeme"
                    ]
                }
            },
            "Mode": {
                "Replicated": {
                    "Replicas": 1
                }
            },
            "Networks": [
                {
                    "Target": "5cb3ojhzk5snap2s3nsjtexsf",
                    "Aliases": [
                        "wordpress"
                    ]
                }
            ],
            "EndpointSpec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80
                    }
                ]
            }
        },
        "Endpoint": {
            "Spec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80
                    }
                ]
            },
            "Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 80,
                    "PublishedPort": 30000
                }
            ],
            "VirtualIPs": [
                {
                    "NetworkID": "6qj71vtgeipenr838hbjt1ash",
                    "Addr": "10.255.0.6/16"
                },
                {
                    "NetworkID": "5cb3ojhzk5snap2s3nsjtexsf",
                    "Addr": "10.0.0.5/24"
                }
            ]
        }
    }
]

我们可以看到这个"wordpress_wordpress"服务发布了一个tcp端口“30000”来对外提供服务。那么我们怎么访问这个服务呢?

利用SLB创建服务路由

阿里云的SLB提供了对多台ECS服务器进行流量分发的负载均衡服务。下面我们将利用SLB服务来配置对Swarm集群中的服务的路由和负载均衡。

首先添加侦听端口,我们选择"http"和"8080"(可以任选)作为前端协议[端口], "http"和"30000"作为后端的协议[端口]。注意 :后端端口必须与服务状态中的"PublishedPort"端口值保持一致。

14680524171808

配置健康检查配置时,可以把“/license.txt”作为检查路径,这样只要wordpress服务的Apache可以被访问就认为服务是健康的

14680521587487

完成配置

14680519323177

随后,我们就会在SLB负载均衡实例上看到相应的侦听端口和健康检查状态

14680666664389

访问SLB实例的公网IP地址,我们就可以看到熟悉的Wordpress设置界面了,

14680523321519

我们还可以利用docker service scale命令对服务进行伸缩,也可以试验一下负载均衡的效果

root@node-1:~# docker service scale wordpress_wordpress=6
wordpress_wordpress scaled to 6
root@node-1:~# docker service ps wordpress_wordpress
ID                         NAME                   SERVICE              IMAGE                                                                              LAST STATE         DESIRED STATE  NODE
2d9bjq6htn7xbcweocy192ubx  wordpress_wordpress.1  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 1 hours    Running        node-2
8w2dsfyyjb9cjiiw3d4j0111h  wordpress_wordpress.2  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 2 minutes  Running        node-1
42ym2i9lic0bvg1763acdudwc  wordpress_wordpress.3  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 2 minutes  Running        node-1
6f33yfwbrpj9g601z2c2gs3qu  wordpress_wordpress.4  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 2 minutes  Running        node-3
6xxkek3t8dgg48zs5mjwrakg8  wordpress_wordpress.5  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 2 minutes  Running        node-2
5idxgssy8t439jgoj65d6g4ln  wordpress_wordpress.6  wordpress_wordpress  wordpress@sha256:7bb9549fb6d80c230bec2da6bd181be8f30e5199687e53e5ad5744a3144eae1b  Running 2 minutes  Running        node-2

Docker的Routing Mesh简析

看完上面的示例,大家一定非常关心相应服务和路由的网络配置。我们就来简单分析一下。在"node-1"节点上,我们执行下面的命令

root@node-1:~# docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
29de61a371e7        bridge              bridge              local               
bdeb358293a3        docker_gwbridge     bridge              local               
78ee3e892931        host                host                local               
6qj71vtgeipe        ingress             overlay             swarm               
70d0aaba61cc        none                null                local               
5cb3ojhzk5sn        wordpress_default   overlay             swarm               

我们可以看到有两个集群范围的overlay网络"ingress"和"wordpress_default"。其中"ingress"是在集群创建之后就自动创建的接入路由网络,而后者是部署"wordpress.dab"的时候自动创建的。

如果没有手工指定,Swarm manager会自动给需要对外访问的服务分配一个在30000-32767之内的PublishedPort。在上文中"wordpress_wordpress"所分配的端口为30000。

在Swarm集群内的每个节点上,都侦听着相同的PublishedPort来为服务提供接入路由。所以我们可以在SLB上配置一个侦听端口对集群中节点进行转发和负载均衡,而不管后端节点是否运行着服务的任务容器。Swarm集群中的所有节点都可以通过ingress网络连接到运行的任务容器,并保证入口流量被引导到正确的容器实例上。

关于Routing mesh的Ingress网络的负载均衡,其设计如下图。我们可以看到每个服务实际上都被分配了一个虚拟IP,而虚拟IP到服务任务容器端口之间的负载均衡是通过IPVS来实现的。

注意:Routing mesh的端口映射机制和bridge网络下容器publish端口的实现完全不同,不要混淆。

14680691310281

我们可以通过下面的命令来看看iptables中ECS节点主机端口到服务虚拟IP的转发配置

root@node-1:~# iptables -L DOCKER-INGRESS -t nat
Chain DOCKER-INGRESS (2 references)
target     prot opt source               destination         
DNAT       tcp  --  anywhere             anywhere             tcp dpt:30000 to:172.18.0.2:30000
RETURN     all  --  anywhere             anywhere            

总结

Docker 1.12带来了新的分布式应用打包格式,可以给在集群上部署多容器应用带来很多便利。但是目前DAB的能力和成熟度都有待提高,和Docker Compose的兼容性也存在一些问题,希望会在未来版本中逐渐解决。

如果你了解阿里云容器服务,会发现很多新Swarm模式下的服务概念和我们实现有很多相似性。我们目前是通过对现有开源编排方案上做增强来提供类似的能力。比如我们扩展了Docker Compose中Service的概念,加强生命周期管理,提供服务伸缩和自动恢复支持等。不远的未来我们也会提供平滑的升级,支持Docker最新的编排技术。

在网络方面,Docker Swarm模式的routing mesh可以和外部的负载均衡技术结合。在阿里云上我们利用SLB实现了Docker服务路由和负载均衡。目前这个工作还需要一些手动工作来完成的,在稍后我们会推出全自动化的方式自动配置SLB监听端口,让整个流程完全自动化。

此外,我们可以看到Docker Swarm模式和阿里云容器服务的路由设计原理上是类似的,只是容器服务中每个节点利用HAProxy为服务提供7层或4层的路由实现,而Docker Engine是每个节点利用IPVS做为4层的路由实现。7层的优势在于可以支持virtual host、session affinity等较复杂的路由策略,而基于kernel空间的4层路由可以有更好的性能表现。我们计划会融合这些技术在阿里云容器服务中给大家带来更好的体验。

想了解更多容器服务内容,请访问 https://www.aliyun.com/product/containerservice

【云栖快讯】一站式开发者服务,海量学习资源免费学  详情请点击

网友评论