环境说明
- ubuntu16.04.7 LTS xenial
- boxes 下载地址
- 我这里用的ubuntu/xenial64下载地址
- 具体安装细节请参考Ubuntu安装docker准备篇
主要技术以及版本
- vagrant
- vagrant是一个对虚拟环境管理的工具。virtualbox
代码语言:txt复制- VirtualBox 是一款开源虚拟机软件。docker
代码语言:txt复制- version 20.10.7
- docker是一个开源的应用容器引擎。
- 安装[docker](https://www.runoob.com/docker/ubuntu-docker-install.html)
- 具体安装细节以及权限问题解决请参考[Ubuntu安装docker](https://niuzheng.net/archives/1146/)consul
代码语言:txt复制- version 1.11.3
- consul是一个用来实现分布式系统的服务发现与配置的开源工具。
- [官网下载地址](https://www.consul.io/downloads)consul-template
代码语言:txt复制- version 0.19.0
- consul-template基于consul的自动替换配置文件的应用。
- [consul-template 下载地址](https://releases.hashicorp.com/consul-template)nginx
代码语言:txt复制- version 1.21.6
- 地球人都知道的嘛!!!额外说明
代码语言:txt复制- `virtualbox`和`vagrant`版本不兼容会出现很多未知问题(坑),官方网站也没有介绍哪个版本对应兼容,我这里分享一个兼容性不错的版本下载连接放下面,供大家参考。
- [virtualbox5.2.8](https://www.virtualbox.org/wiki/Download_Old_Builds_5_2)
- [vagrant2.1.1](https://www.vagrantup.com/downloads)
简要说明
- 传统负载均衡架构图
- 假如目前服务需要扩容,增加了一台Web Server5,你需要手动修改Nginx配置并且重启Nginx。
- 假如目前Web Server1、Web Server4服务大半夜突然挂了,你岂不是需要爬起来修改Nginx配置,这种情况重复来几次,这可不是什么好的体验,会让人崩溃的呢!!!我们要实现什么呢?下面我们附上图来说明!!
代码语言:txt复制- 因需要或者意外情况而造成增加/减少机器及或者某个服务而动态实现nginx配置自动更新并实现更新重载。
- web server 通过 registrator 注册到consul服务里,然后consul-template订阅consul实现nginx配置更新重载,达到动态扩容(增加/减少),保证服务稳定可靠运行的目的。
实现
- 假设你已经安装好虚拟机(以ubuntu为例)和docker应用容器引擎。
- 这里可以先完整安装一台机器的软件和应用,然后使用vagrant打包剩下的两个机器。
机器说明
- 服务器1: IP: 192.168.56.2
- 服务器2: IP: 192.168.56.3
- 服务器3: IP: 192.168.56.4
consul集群角色分配(我这里电脑配置有限,搞太多虚拟机测试主机windows机器容易卡死,所以我的consul集群和webserver以及nginx转发服务器都用这三个机器实现啦)
- 服务器1: IP: 192.168.56.2 leader
- 服务器2: IP: 192.168.56.3 service
- 服务器3: IP: 192.168.56.4 client nginx转发服务器
应用程序角色分配
- 服务器1: IP: 192.168.56.2 直播服务API1
- 服务器2: IP: 192.168.56.3 直播服务API2
- 服务器3: IP: 192.168.56.4 直播服务API3
部署consul集群
consul常用命令
- server : 定义agent运行在server模式
- bootstrap-expect :在一个datacenter中期望提供的server节点数目,当该值提供的时候,consul一直等到达到指定sever数目的时候才会引导整个集群,该标记不能和bootstrap共用
- bind:该地址用来在集群内部的通讯,集群内的所有节点到地址都必须是可达的,默认是0.0.0.0
- node:节点在集群中的名称,在一个集群中必须是唯一的,默认是该节点的主机名
- ui-dir: 提供存放web ui资源的路径,该目录必须是可读的
- rejoin:使consul忽略先前的离开,在再次启动后仍旧尝试加入集群中。
- config-dir:配置文件目录,里面所有以.json结尾的文件都会被加载
- client:consul服务侦听地址,这个地址提供HTTP、DNS、RPC等服务,默认是127.0.0.1所以不对外提供服务,如果你要对外提供服务改成0.0.0.0
# 拉取镜像
docker pull consul
# 192.168.56.2 (leader server)
docker run --net=host --name consul1 -d consul agent -server -bootstrap-expect 1 -ui -node=s0 -bind=192.168.56.2 -client=0.0.0.0
# 192.168.56.3 (server)
docker run --net=host --name consul3 -d consul agent -server -ui -node=s3 -bind=192.168.56.3 -client=0.0.0.0 -join=192.168.56.2
# 192.168.56.4 (client)
docker run --net=host --name consul4 -d consul agent -ui -node=s4 -bind=192.168.56.4 -client=0.0.0.0 -join 192.168.56.2
# 查看consul cluster中consul节点的信息
docker exec -it consul1 consul members
#查看consul集群已经启动以及选举的leader
docker exec -it consul1 consul operator raft list-peers
浏览器访问UI界面 http://192.168.56.2:8500/
编写直播API服务
我这里直接使用swoole简单模拟搭建一个直播的HTTP服务,前提是你的php安装了swoole扩展(server.php)
代码语言:javascript复制<?php
#192.168.56.3
$http = new SwooleHttpServer('0.0.0.0', 8080);
$http->on('Request', function ($request, $response) {
$response->header('Content-Type', 'text/html; charset=utf-8');
$response->end('<h1>直播服务API2 #IP:192.168.56.3</h1>');
});
$http->start();
代码语言:javascript复制<?php
#192.168.56.3
$http = new SwooleHttpServer('0.0.0.0', 8080);
$http->on('Request', function ($request, $response) {
$response->header('Content-Type', 'text/html; charset=utf-8');
$response->end('<h1>直播服务API2 #IP:192.168.56.3</h1>');
});
$http->start();
代码语言:javascript复制<?php
#192.168.56.4
$http = new SwooleHttpServer('0.0.0.0', 8080);
$http->on('Request', function ($request, $response) {
$response->header('Content-Type', 'text/html; charset=utf-8');
$response->end('<h1>直播服务API3 #IP:192.168.56.4</h1>');
});
$http->start();
注册直播服务API到consul集群
consul api 文档
代码语言:javascript复制//composer require php-curl-class/php-curl-class
require __DIR__ . '/vendor/autoload.php';
use CurlCurl;
function _curl($u = '',$method = 'get', $data = null)
{
$curl = new Curl();
$baseUrl = 'http://192.168.56.4:8500/v1';
$url = $baseUrl. $u;
if (in_array(strtolower($method), ['put','post']) && !empty($data)) {
return $curl->$method($url,$data);
} else {
return $curl->$method($url);
}
}
$res = 'init';
//注册开始
$servers = [
['ip' => '192.168.56.2','port' => 8080,'id' => 'zhibo-1','Name' => 'zhibo_service'],
['ip' => '192.168.56.3','port' => 8080,'id' => 'zhibo-2','Name' => 'zhibo_service'],
['ip' => '192.168.56.4','port' => 8080,'id' => 'zhibo-3','Name' => 'zhibo_service']
];
foreach ($servers as $serve)
{
//注意同一个服务Name必须一样ID可以不同
$data = array(
'ID' => $serve['id'],
'Name' => $serve['Name'],
'Tags' => ["primary", "v2"],
'Address' => $serve['ip'],
'Port' => $serve['port'],
'EnableTagOverride' => false,
'check' => array(
'DeregisterCriticalServiceAfter' => '90m',
'HTTP' => "http://{$serve['ip']}:{$serve['port']}",
'Interval' => '10s',
"Timeout" => "5s"
)
);
$data = json_encode($data,JSON_UNESCAPED_SLASHES);
$res = _curl('/agent/service/register','put',$data);
//$res = _curl('/agent/service/register?replace-existing-checks=true','put',$data);
echo "<pre>";
print_r($res);
}
##其他API大概描述
//注销直播-3服务
//$res = _curl('/agent/service/deregister/zhibo-3','put');
//启用维护模式(enable=true/false)
//$res = _curl('/agent/service/maintenance/zhibo-3?enable=false&reason=维护原因','put');
//检查是否健康 status 为passing的为通过
//$res = _curl('/health/checks/zhibo_service');
//列出服务
//$res = _curl('/agent/services');
//获取服务配置
//$res = _curl('/agent/service/zhibo-3');
//获取本地服务运行状况
//json
//$res = _curl('/agent/health/service/name/zhibo_service');
//text
//$res = _curl('/agent/health/service/name/zhibo_service?format=text');
//通过 ID 获取本地服务运行状况
//$res = _curl('/agent/health/service/id/zhibo-3');
//$res = _curl('/agent/health/service/id/zhibo-3?format=text');
echo '<pre>';
print_r($res);
die;
搭建nginx转发服务,安装consul-template并关联订阅服务consul集群更新nginx转发配置,重启nginx。
- 目录结构
consul-template文档地址
模板文件
代码语言:javascript复制{{range services}} {{$name := .Name}} {{$service := service .Name}}
upstream {{$name}} {
zone upstream-{{$name}} 64k;
{{range $service}}
server {{.Address}}:{{.Port}} max_fails=3 fail_timeout=60 weight=1;
{{else}}
server 127.0.0.1:65535; # force a 502
{{end}}
}
{{end}}
server {
listen 80 zhibo_service.com;
location / {
root /usr/share/nginx/html/;
index index.html;
}
location /stub_status {
stub_status;
}
{{range services}} {{$name := .Name}}
location /{{$name}} {
proxy_pass http://{{$name}};
}
{{end}}
}
Dockerfile 文件
代码语言:javascript复制#指定基础镜像
FROM nginx:latest
#指定维护人
#MAINTAINER zhengniu
LABEL authors="zhengniu <771036148@qq.com>"
#https://releases.hashicorp.com/consul-template
ARG CONSUL_TMEPLATE_VERSION="0.19.0"
RUN rm -rf /etc/nginx/sites-available/
ENV CONSUL_URL 127.0.0.1:8500
#ADD https://releases.hashicorp.com/consul-template/${CONSUL_TMEPLATE_VERSION}/consul-template_${CONSUL_TMEPLATE_VERSION}_linux_amd64.zip /usr/local/bin/
ADD consul-template_0.19.0_linux_amd64.tgz /usr/local/bin/
VOLUME /templates
ADD service.ctmpl /templates/
EXPOSE 80
RUN mkdir -p /etc/service/nginx && rm -rf /etc/service/nginx/*
ADD start.sh /etc/service/nginx/run
CMD ["/etc/service/nginx/run"]
start.sh
代码语言:javascript复制#!/bin/bash
service nginx start &
consul-template -consul-addr=$CONSUL_URL -template="/templates/service.ctmpl:/etc/nginx/conf.d/service.conf:service nginx reload"
构建镜像以及启动镜像
代码语言:javascript复制 docker build -t nginx:consuleTemplate .
docker run --net=host -d --name nginx nginx:consuleTemplate
验证
查看是否生成配置文件
代码语言:javascript复制#查看
docker exec -it nginx sh -c "cat /etc/nginx/conf.d/service.conf"
#结果
upstream consul {
zone upstream-consul 64k;
server 192.168.56.2:8300 max_fails=3 fail_timeout=60 weight=1;
server 192.168.56.3:8300 max_fails=3 fail_timeout=60 weight=1;
}
upstream zhibo_service {
zone upstream-zhibo_service 64k;
server 127.0.0.1:65535; # force a 502
}
server {
listen 80 zhibo_service.com;
location / {
root /usr/share/nginx/html/;
index index.html;
}
location /stub_status {
stub_status;
}
location /consul {
proxy_pass http://consul;
}
location /zhibo_service {
proxy_pass http://zhibo_service;
}
}
分别启动直播-1、直播-2、直播-3服务
代码语言:javascript复制#192.168.56.2
php server.php
#192.168.56.3
php server.php
#192.168.56.4
php server.php
#查看
docker exec -it nginx sh -c "cat /etc/nginx/conf.d/service.conf"
#查看结果动态增加了
upstream consul {
zone upstream-consul 64k;
server 192.168.56.2:8300 max_fails=3 fail_timeout=60 weight=1;
server 192.168.56.3:8300 max_fails=3 fail_timeout=60 weight=1;
}
upstream zhibo_service {
zone upstream-zhibo_service 64k;
server 192.168.56.2:8080 max_fails=3 fail_timeout=60 weight=1;
server 192.168.56.3:8080 max_fails=3 fail_timeout=60 weight=1;
server 192.168.56.4:8080 max_fails=3 fail_timeout=60 weight=1;
}
server {
listen 80 zhibo_service.com;
location / {
root /usr/share/nginx/html/;
index index.html;
}
location /stub_status {
stub_status;
}
location /consul {
proxy_pass http://consul;
}
location /zhibo_service {
proxy_pass http://zhibo_service;
}
}
# 浏览器访问 http://zhibo.com/zhibo_service