基于Docker的应用负载均衡与服务发现
概述
现在微服务架构越来越流行,阿里云容器服务对于微服务架构提供了很好的支撑,平台提供了便利的服务注册与发现机制,内置的服务负载均衡与路由功能,以及灵活的模板编排、全生命周期管理。
对于Web应用,下图是阿里云容器服务对弹性高可用Web应用负载均衡及路由支持的一种形式:
阿里云容器路由服务
假如你有一个Web应用需要运行多个实例,并对外提供服务。可以配置docker compose模板如下(以Java应用的Tomcat+Mysql为例), 这里通过Docker的link方式支持服务的依赖,部署后mysql的IP地址,端口,环境变量等信息会自动配置到Tomcat容器的环境变量中。
tomcat-default:
environment:
- CATALINA_HOME=/usr/local/tomcat
- 'TOMCAT_TGZ_URL=https://www.apache.org/dist/tomcat/tomcat-8/v8.0.30/bin/apache-tomcat-8.0.30.tar.gz'
expose:
- 8080/tcp
image: 'tomcat:latest'
labels:
aliyun.routing.port_8080: 'http://tomcat-sample'
aliyun.scale: '3'
restart: always
links:
- 'db:mysql'
db:
image: registry.aliyuncs.com/acs-sample/mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: password
restart: always
labels:
aliyun.logs: /var/log/mysql
- aliyun.scale: '3' 表示部署3个tomcat容器
- aliyun.routing.port_8080: 'http://tomcat-sample' 配置路由服务(路由到3个容器的8080端口),并配置阿里云容器服务提供的测试域名。
或者在阿里云容器服务中通过界面部署应用:
选择镜像
配置域名
服务详情
访问域名

自定义负载均衡
很多的Web应用有自己的负载均衡Server, Nginx就是一个很流行的负载均衡及反向代理Server. 很多的Java应用也是采用Nginx+Tomcat架构,以提供负载均衡,静态资源代理,流控,ABTest等等服务支持。对于这样一个架构在Docker上我们应该怎样实现呢?下面我们看看几种不同的实现方式:
独立link
Nginx link到分立的多个Tomcat服务。Docker compose模板如下:
nginx:
image: 'nginx:latest'
labels:
aliyun.routing.port_80: 'http://ngtomcat'
aliyun.scale: '2'
ports:
- '80'
links:
- 'tomcat1:tomcat1'
- 'tomcat2:tomcat2'
- 'tomcat3:tomcat3'
restart: always
extra_hosts:
- "tomcat1.if:162.242.195.82"
tomcat1:
environment:
- LANG=C.UTF-8
- CATALINA_HOME=/usr/local/tomcat
- TOMCAT_MAJOR=8
image: 'tomcat:latest'
labels:
aliyun.routing.port_8080: 'http://tomcat1'
ports:
- '8080'
restart: always
tomcat2:
environment:
- LANG=C.UTF-8
- CATALINA_HOME=/usr/local/tomcat
- TOMCAT_MAJOR=8
image: 'tomcat:latest'
labels:
aliyun.routing.port_8080: 'http://tomcat1'
ports:
- '8080'
restart: always
tomcat3:
environment:
- LANG=C.UTF-8
- CATALINA_HOME=/usr/local/tomcat
- TOMCAT_MAJOR=8
image: 'tomcat:latest'
labels:
aliyun.routing.port_8080: 'http://tomcat1'
ports:
- '8080'
restart: always
这里前面是2个Nginx,后面接入3个Tomcat.
Ngnix的upstream配置大致如下:
upstream tomcat {
server tomcat1:8080;
server tomcat2:8080;
server tomcat3:8080;
}
server {
listen 80;
server_name localhost;
index index.html index.htm index.php;
access_log /var/log/nginx/access.log;
location / {
proxy_pass http://tomcat;
}
}
Nginx的配置也可以在模板中通过command自动配置:
command: sh -c "echo 'xxxx' >/etc/nginx/conf.d/default.conf"
这种方式是直接利用docker提供的link机制,link后docker会自动将link容器的ip配置到nginx容器中。阿里云容器服务针对ECS的经典网络及VPC都提供了夸主机通讯的能力,tomcat容器部署到多节点上,通讯也不会有问题。
这种方式是路由到容器的IP+Port,优点是配置简单,跟以前的应用部署比较类似,但不便于弹性扩展,增加tomcat就需要调整配置。
或者也可以只link到一个tomcat服务(多个容器),并配置路由到容器(容器 host为tomcat_n)。
路由到节点
阿里云容器服务在集群的每个ECS节点上都提供了一个路由服务,可以利用这个路由服务提供更弹性的负载均衡服务。Compose模板如下:
nginx:
image: 'nginx:latest'
labels:
aliyun.routing.port_80: 'http://ngtomcat'
aliyun.scale: '2'
ports:
- '80'
links:
- 'tomcat1:tomcat1'
- 'tomcat2:tomcat2'
restart: always
extra_hosts:
- "tomcat1.ir:123.56.80.151"
- "tomcat2.ir:182.92.204.43"
tomcat1:
environment:
- LANG=C.UTF-8
- CATALINA_HOME=/usr/local/tomcat
- TOMCAT_MAJOR=8
image: 'tomcat:latest'
labels:
aliyun.scale: '2'
aliyun.routing.port_8080: 'http://tomcat.ir'
ports:
- '8080'
restart: always
tomcat2:
environment:
- LANG=C.UTF-8
- CATALINA_HOME=/usr/local/tomcat
- TOMCAT_MAJOR=8
image: 'tomcat:latest'
labels:
aliyun.scale: '2'
aliyun.routing.port_8080: 'http://tomcat.ir'
ports:
- '8080'
restart: always
这里配置了2个Nginx,Nginx连接了2个Tomcat服务,每个Tomcat服务运行2个实例。
nginx upstream配置大致如下:
upstream tomcat.ir {
server tomcat.ir.1;
server tomcat.ir.2;
}
server {
listen 80;
server_name tomcat.ir;
index index.html index.htm index.php;
access_log /var/log/nginx/access.log;
location / {
proxy_pass http://tomcat.ir;
}
}
这种方式利用阿里云容器提供的路由服务,能够自动服务发现与路由。当tomcat弹性调整容器数量的时候,并不需要调整模板或者Nginx的配置,会更加方便些。
利用API进行服务发现
阿里云容器也提供了服务发现的API,我们可以利用服务发现API定制自己的负载均衡。
获取某个服务的状态API如下(支持Stream):
API
/services/{serviceId}
样例
请求命令:curl -k --cert agent.pem --key agent-key.pem https://DiscoveryServer:2379/services/ceshi-2_gitlab
结果:
{
"Service": {
"name": "gitlab",
"project": "ceshi-2",
"definition": {
"image": "registry.aliyuncs.com/acs-sample/sameersbn-gitlab:7.14.1",
"labels": {
...
"aliyun.routing.port_80": "gitlab-test"
},
"links": ["redis:redisio", "postgresql:postgresql"],
"ports": ["80", "10022:22"],
"volumes": ["/srv/docker/gitlab/gitlab:/home/git/data"]
},
"extensions": {
"scale": 1,
"routing": [{
"virtual_host": "gitlab-test.xxx.cn-hangzhou.alicontainer.com",
"protocol": "http",
"port": 80
}]
},
...
},
"Containers": {
"13b3ee3e7ca247537756ad4db117d76f52d9a40efd84d561f1b865200597c97d": {
"name": "/ceshi-2_gitlab_1",
"node": "120.55.181.78",
"ip": "172.64.0.8",
"running": true,
"status": "running",
"health": "success"
}
}
}
这里是一个用Python脚本查询更新Nginx的样例:
def serviceSync():
# Get setting
projectName = os.getenv('COMPOSE_PROJECT_NAME')
serviceServer = os.getenv('ETCD_NODES')
...
# Get service data for contianers
serviceData = getServiceData(serviceServe, serviceName )
upStreamIPs = getUpstreamIps( serviceData )
# Generate config from template
upstreamServers = ''
for ip in upStreamIPs:
upstreamServers += "server %s:%s;\n" % (ip, port)
config = getTemplate().substitute(upstreamServers=upstreamServers)
updateConfig( config )
def main():
options, arguments = getOpts()
while True:
serviceSync()
time.sleep(60)
用这个脚本构建一个自动更新配置的Nginx的新镜像:
FROM nginx:1.9.8
MAINTAINER Chang Hai Yan <danielyanch@hotmail.com>
ADD serviceSync.py /usr/local/bin/serviceSync.py
ADD supervisord.conf /etc/supervisord.conf
RUN apt-get update && \
apt-get install --force-yes -y curl python supervisor && \
chmod +x /usr/local/bin/serviceSync.py
CMD supervisord -n
Compose部署模板如下,注意访问API需要证书,需要在Label中添加 aliyun.addon ,有这个Label,部署的时候,集群管理会自动将服务发现Server的地址及证书配置到运行的容器中。
nginx:
image: 'registry.aliyuncs.com/acs-sample/nginx-service-discovery'
labels:
aliyun.routing.port_80: 'http://ngservice'
aliyun.scale: '2'
aliyun.addon: "nginx"
ports:
- '80'
links:
- 'tomcat:tomcat'
restart: always
tomcat:
environment:
- LANG=C.UTF-8
- CATALINA_HOME=/usr/local/tomcat
- TOMCAT_MAJOR=8
image: 'tomcat:latest'
labels:
aliyun.scale: '3'
aliyun.routing.port_8080: 'http://tomcat'
ports:
- '8080'
restart: always
阿里云容器服务也支持通过NAT+SLB的方式来进行负载均衡及更简单的事件机制进行服务的生命周期管理,我们后面再介绍。