随着近几年微服务与云计算的飞速发展,机器由物理机逐步变为了虚拟机,应用服务由庞大的单体应用逐渐变为了若干个微服务联合组成的应用集群,更新迭代的速度成倍上涨,传统的部署模式已无法满足开发日常更新需求,需要一套适合微服务的管理架构。

技术栈及文档

资源调度框架 MESOS

应用编排平台 Marathon

nginx 动态修改 upstream dyups

nginx 动态修改 upstream upsync

使用Mesos 进行机器资源管理

首先,是机器资源的管理。在微服务的架构中,原有的单体服务被拆分成了一个个独立单元的应用程序,这些服务体量较小,可以独立运行在配置较小的机器上。为了故障隔离,我们会尽可能的把这些服务部署在不同的虚拟机上,这样机器的数量会成倍增加。对于运维来说,每个新服务部署时,需要先查看现有机器的剩余资源是否满足新服务的需求,有时可能因为评估不准确造成来回扩容、迁移,或者资源浪费。

开始时,我们的架构可能时这样的

1.png

为了解决上面的问题,可以使用 MESOS ( 布式资源管理框架),它可以 让我们像用一台电脑(一个资源池)一样使用整个数据中心。
mesos 部署时分为 master 和 agent 两个角色,当然,你可以在同一台机器启动它们。

安装Mesos 前需要安装zookeeper,mesos 使用zk 实现高可用和选举,包括一个 master leader 和 几个备份 master 避免宕机。

Mesos master  负责管理各个Framework和Slave,并将Slave上的资源非配给各个Framework。
Mesos agent   负责管理本节点上的各个Mesos Task,为各个Executor分配资源 (低版本为 mesos-slave)。

$ cat > /tmp/bintray-mesos-el.repo <<EOF
#bintray-mesos-el - packages by mesos from Bintray
[bintray-mesos-el]
name=bintray-mesos-el
baseurl=https://dl.bintray.com/apache/mesos/el7/x86_64
gpgcheck=0
repo_gpgcheck=0
enabled=1
EOF
 
$ sudo mv /tmp/bintray-mesos-el.repo /etc/yum.repos.d/bintray-mesos-el.repo
 
$ sudo yum update
 
$ sudo yum install mesos
 
$ tree /etc/mesos-master
/etc/mesos-master/
|-- hostname
|-- ip
|-- log_dir
|-- quorum   # quorum > (number of masters)/2
`-- work_dir
 
$ tree /etc/mesos-agent
/etc/mesos-agent/
|-- containerizers  # 容器类型,默认 mesos,可以添加 docker,如: mesos,docker
|-- hostname
|-- ip
|-- log_dir
|-- master           # master 地址,格式为 host:port 或 
zk://host1:port1,host2:port2,.../path 或 file:///path/to/file
|-- resources       # 设置总资源大小,可以设置小些来预留更多机器资源
`-- work_dir
 
$ cat /etc/mesos/zk  # 设置 mesos 在zk 中的存储目录
zk://192.168.100.9:2181,192.168.100.110:2181,192.168.100.234:2181/mesos
 
$ systemctl start mesos-master
$ systemctl start mesos-slave

当mesos服务启动后,agent 会向master 节点汇报机器资源,包括CPU、内存、磁盘等。 当我们要发布一个服务时,只需要设置这个服务的CPU、内存、磁盘参数, mesos master 会自动帮我们选择有足够资源的机器去运行,如下图

2.png

我们将微服务的启动都交给 Mesos 管理,这样我们只需要关注整体资源即可。MESOS 提 供了UI界面,可以直接访问 mesos master 的5050 端口,查看集群资源使用情况。 总体使用情况 及 Agent 节点使用情况

3.png


4.png

完成以上后,我们的架构变成了这样

5.png

使用Marathon 进行微服务管理 

Marathon 是建立在 Mesos 上的私有 PaaS平台。它能自动处理硬件或者软件故障,并确 保每个应用程序都"永远在线"。 我们使用 Marathon 管理微服务有以下优势 1. 支持容器和非容器,不受限于服务启动类型,操作系统版本等。 2. 漂亮而强大的用户界面,可以在UI 上进行快捷方便的应用程序配置 3. 支持约束条件,例如允许一个mesos agent 节点只运行一个应用程序。 4. 支持健康检查。可以配置 http、https、tcp、command 类型的监控检查。 5. 完整的REST API,易于集成和编写脚本。这个对于后期集成来说至关重要。

# Add the repository
$ sudo rpm -Uvh http://repos.mesosphere.com/el/7/noarch/RPMS/mesosphere-el-repo-7-2.noarch.rpm
 
# Install packages
$ sudo yum -y install mesos marathon
 
# marathon  and mesos zk path
$ cat /etc/default/marathon 
MARATHON_MESOS_USER="root"
MARATHON_MASTER="zk://192.168.100.9:2181,192.168.100.110:2181,192.168.100.234:2181/mesos"
MARATHON_ZK="zk://192.168.200.9:1181,192.168.100.110:2181,192.168.100.234:2181/marathon"
 
systemctl start marathon

启动后,直接访问 marathon 的 8080 端口,就能看到一个漂亮强大的 UI 界面。

6.png

我们以 springboot 应用为例,在 marathon 上创建一个应用程序

7.png

8.png

9.png

10.png

当我们更新应用程序时, marathon 会新建相同实例数量的应用程序,待 health check 通过之后替换老节点,所以不需要担心新的服务没有启动期间老的服务停掉造成线上事 故。 到这里为止,我们已经可以在marathon 上方便快捷的进行日常应用的创建、升级、扩 容、缩容。当服务健康检查失败或者机器宕机后,marathon 会自动在其它节点上启动挂掉的应用程序,大大提升了高可用性。

11.png

使用 nginx upsync/dyups 模块进行平滑变更 

当我们的微服务可以随机分配在不同机器上时,便产生了一个新的令人头疼的问题。 nginx 并不知道后端节点的变更, 也不可能每次都去手动修改 upstream 节点, reload nginx,这样成本就太高了。 我们的解决思路是和微服务的注册中心打通,当服务注册、注销时,都会对注册中心进行 更新,利用 nginx upsync/dyups 模块 可以动态修改 upstream 节点的能力进行同步,做 到平滑变更。 如果使用的注册中心为 consul,建议使用 upsync 模块,这样无需开发,只需要简单的 nginx 配置,就可以实现我们想要的效果, 支持 consul kv, consul_services, consul_health, 同时 upsync 也支持 etcd。建议使用 consul_health 接口。 upsync 模块不是nginx 内置模块,使用时需要重新编译添加此模块。

wget 'http://nginx.org/download/nginx-1.8.0.tar.gz'
tar -xzvf nginx-1.8.0.tar.gz
cd nginx-1.8.0/
 
 
./configure --add-module=/path/to/nginx-upsync-module
make
make install

配置文件示例

http {
    upstream test {
        upsync 127.0.0.1:8500/v1/health/service/test upsync_timeout=6m upsync_interval=500ms upsync_type=consul_health strong_dependency=off;
        upsync_dump_path /usr/local/nginx/conf/servers/servers_test.conf;
 
        include /usr/local/nginx/conf/servers/servers_test.conf;
    }
 
    upstream bar {
        server 127.0.0.1:8090 weight=1 fail_timeout=10 max_fails=3;
    }
 
    server {
        listen 8080;
 
        location = /proxy_test {
            proxy_pass http://test;
        }
 
        location = /bar {
            proxy_pass http://bar;
        }
 
        location = /upstream_show {
            upstream_show;
        }
 
    }
}

当upsync无法满足我们的需求或者注册中心不是 consul、etcd 时,我们可以考虑使用 nginx dyups 模块。dyups 仅对外提供 upstream 的增删查改接口,和注册中心对比、修 改的工作需要我们通过脚本的方式完成。虽然这种方式麻烦一些,但是可定制化程度高, 支持 http, C,lua API,基本上可以满足大部分的场景需求。 

dyups 模块也需要nginx 编译时添加

$ git clone git://github.com/yzprofile/ngx_http_dyups_module.git
 
# to compile as a static module
$ ./configure --add-module=./ngx_http_dyups_module
 
# to compile as a dynamic module
$ ./configure --add-dynamic-module=./ngx_http_dyups_module

示例配置

http {
 
    include conf/upstream.conf;
 
    server {
        listen   8080;
 
        location / {
            # The upstream here must be a nginx variable
            proxy_pass http://$dyups_host;
        }
    }
 
    server {
        listen 8088;
        location / {
            return 200 "8088";
        }
    }
 
    server {
        listen 8089;
        location / {
            return 200 "8089";
        }
    }
 
    server {
        listen 8081;
        location / {
            dyups_interface;
        }
    }
}

特别注意,使用dyups 时, proxy_pass 时的 upstream 必须是 nginx 变量,否则不生 效,切记。

14.png

整体回顾

经过以上调整,我们得到了以下优化

1. 服务器资源自动分配,合理利用

2. 提升微服务的高可用性

3. 减低OPS人工成本,更加便于管理和维护