作为一个开发者,有时候需要一个集群环境,之前的做法要么就是使用多个虚拟机,要么就是采用不同的端口来模拟,但是虚拟机比较占内存,而且启动慢,采用不同的端口来模拟,管理起来比较麻烦一些,程序隔离性差一些。
docker的出现让我们可以在一台虚拟机上模拟构建出来一个几乎完全隔离的集群,本文提供一种快速构建consul集群的方法。
首先我们需要consul的镜像,这个可以从dockerhub上获取:
上面第一个就是consul官方的镜像
# 搜索镜像 sudo docker search consul # 拉取镜像 sudo docker pull consul
获取到镜像之后,可以使用docker run运行,consul镜像运行的相关参数可以参考官方的文档
consul镜像官方说明:https://registry.hub.docker.com/_/consul
consul的官方文档:https://www.consul.io/docs
直接使用docker run来启动一个consul节点的集群,如:
# 不启用acl时,可直接执行下面的命令,创建一个只有一个节点的consul集群 sudo docker run -id -expose=[8300,8301,8302,8500,8600] --restart always -p 18300:8300 -p 18301:8301 -p 18302:8302 -p 18500:8500 -p 18600:8600 --name server1 -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' consul agent -server -bootstrap-expect=1 -node=server1 -bind=0.0.0.0 -client=0.0.0.0 -ui -datacenter dc1 # 启用acl是,只需要修改上面-e参数,如下面的命令,其中token_value可自行设置 sudo docker run -id -expose=[8300,8301,8302,8500,8600] --restart always -p 18300:8300 -p 18301:8301 -p 18302:8302 -p 18500:8500 -p 18600:8600 --name server1 -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true,"acl": {"enabled": true,"default_policy": "deny","down_policy": "extend-cache","tokens": {"master": "token_value"}}}' consul agent -server -bootstrap-expect=1 -node=server1 -bind=0.0.0.0 -client=0.0.0.0 -ui -datacenter dc1 各参数说明: -expose:暴露出出来的端口,即consul启动所需的端口:8300,8301,8302,8500,8600 --restart:always表示容器挂了就自动重启 -p:建立宿主机与容器的端口映射 --name:容器名称 -e:环境变量,这里用于对consul进行配置 consul:这是consul镜像名,不是consul命令 agent:容器中执行的命令,各参数含义: -server:表示节点是server类型 -bootstrap-expect:表示集群中有几个server节点后开始选举leader,既然是单节点集群,那自然就是1了 -node:节点名称 -bind:集群内部通信地址,默认是0.0.0.0 -client:客户端地址,默认是127.0.0.1 -ui:启用consul的web页面管理 -datacenter:数据中心 更多配置参考:https://www.consul.io/docs/agent/options
命令执行后,可以在浏览器上访问宿主机上的18500端口(映射到容器的8500),就可以访问到consul的web管理页面了,如果启动了acl,那么需要点击右上角的登录进行授权,授权值即上面自己设置的token_value,之后页面大概是这样子的:
如果是启动一个多节点的consul集群,docker run管理起来就会非常繁琐,所以分享两种简单的管理方式:
使用shell命令来管理
自己可以写一个shell文件来时间集群的管理,比如:
#!/bin/bash servers=3 #server节点数 prefix_server="server" #server节点名称前缀 clients=3 #client节点数 prefix_client="client" #client节点名称前缀 datacenter="dc1" #数据中心 port_rpc=18100 #rpc端口(起始)(不包含) port_lan=18200 #lan端口(起始)(不包含) port_wan=18300 #wan端口(起始)(不包含) port_http=18400 #http端口(起始)(不包含) port_dns=18500 #dns端口(起始)(不包含) network_name="net-consul" #docker网络名称 token= #认证token,可以使用guid,之后连接需要携带token # 检查网络是否存在 network_create(){ if [ -z "$network_name" ];then echo network not exists exit 1 fi if [ $(sudo docker network ls | grep -c "$network_name") -gt 0 ];then echo network [$network_name] exists return fi echo create network [$network_name] sudo docker network create --driver bridge $network_name > /dev/null } # 停止并删除docker容器 node_down(){ for i in $(seq 1 $2); do node_name="$1$i" sudo docker stop $node_name > /dev/null && sudo docker rm $node_name > /dev/null echo node [$node_name] removed done } # 停止运行docker容器 node_stop(){ for i in $(seq 1 $2); do node_name="$1$i" sudo docker stop $node_name > /dev/null echo node [$node_name] stoped done } # 生成一组可用的端口 generate_ports(){ port_rpc=`expr 1 + $port_rpc` port_lan=`expr 1 + $port_lan` port_wan=`expr 1 + $port_wan` port_http=`expr 1 + $port_http` port_dns=`expr 1 + $port_dns` p_rpc="$port_rpc:8300" p_lan="$port_lan:8301" p_wan="$port_wan:8302" p_http="$port_http:8500" p_dns="$port_dns:8600" bind_ports="-p $p_rpc -p $p_lan -p $p_wan -p $p_http -p $p_dns" } # 创建并运行集群 consul_run(){ network_create if [ $servers -eq 0 ];then echo no servers exit 0 fi common_agent="-bind=0.0.0.0 -client=0.0.0.0" common_docker="--network $network_name -expose=[8300,8301,8302,8500,8600] --restart always" #设置通用的环境变量 if [ -z "$token" ];then e_server='CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true}' # 不使用token else e_server='CONSUL_LOCAL_CONFIG= { "skip_leave_on_interrupt": true, "acl": { "enabled": true, "default_policy": "deny", "down_policy": "extend-cache", "tokens": { "master": "'$token'" } } }' echo server token:$token fi e_client='CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}' # 创建并启动server节点 for i in $(seq 1 $servers); do node_name="$prefix_server$i" #节点名称 generate_ports if [ $(sudo docker ps -a | grep -c "$node_name") -gt 0 ];then echo node [$node_name] exists continue fi node_join= if [ $i -ne 1 ];then node_join="-join ${prefix_server}1" fi sudo docker run -d $common_docker -e "$e_server" $bind_ports --name $node_name consul agent -server -bootstrap-expect=$servers -node=$node_name $common_agent -ui -datacenter $datacenter $node_join > /dev/null echo "server node:[$node_name] is running => $p_rpc $p_lan $p_wan $p_http $p_dns" done if [ $clients -gt 0 ];then sleep 3 fi # 创建并启动client节点 for i in $(seq 0 `expr $clients - 1`); do node_name="$prefix_client`expr $i + 1`" if [ $(sudo docker ps -a | grep -c "$node_name") -gt 0 ];then echo node [$node_name] exists continue fi generate_ports server_node="$prefix_server`expr $i % $servers + 1`" sudo docker run -d $common_docker -e "$e_client" $bind_ports --name $node_name consul agent -node=$node_name $common_agent -retry-join=$server_node > /dev/null echo "client node:[$node_name] is running => $p_rpc $p_lan $p_wan $p_http $p_dns" done } consul_down(){ node_down $prefix_client $clients node_down $prefix_server $servers } consul_start(){ if [ $servers -le 0 ];then echo invalid servers exit 2 fi for i in $(seq 1 $servers); do node_name="$prefix_server$i" sudo docker start $node_name > /dev/null echo node [$node_name] started done if [ $clients -gt 0 ];then sleep 3 fi for i in $(seq 1 $clients); do node_name="$prefix_client$i" server_node_name="$prefix_server`expr $i % $servers + 1`" sudo docker start $node_name > /dev/null echo node [$node_name] started done } consul_stop(){ node_stop $prefix_client $clients node_stop $prefix_server $servers } consul_up(){ consul_run sleep 3 consul_status } consul_restart(){ consul_stop sleep 3 consul_start } consul_logs(){ sudo docker logs $1 } consul_status(){ para_token= if [ -n "$token" ];then para_token="-token $token" fi # 检测并等待leader选举 consul_exec consul operator raft list-peers $para_token > /dev/null consul_exec consul members $para_token } consul_exec(){ sudo docker exec -i ${prefix_server}1 $@ } if [ ! -z "$1" ];then _args=$@ _index=`expr index "$_args" " "` _args=`echo ${_args:$_index}` consul_$1 $_args exit 0 fi echo " Usage: $0 COMMAND 可用命令: start 启动集群 stop 停止集群服务 up 创建并启动集群 run 创建并启动集群 down 停止并删除集群 status 查看集群容器节点信息 logs 输出指定节点的日志 exec 在集群中的首个节点执行指定的命令 restart 重新启动集群"
赋值上面的代码到一个sh文件,比如叫consul.sh,其中,文件开头是几个配置,可以修改:
servers=3 #server节点数 prefix_server="server" #server节点名称前缀 clients=3 #client节点数 prefix_client="client" #client节点名称前缀 datacenter="dc1" #数据中心 port_rpc=18100 #rpc端口(起始)(不包含) port_lan=18200 #lan端口(起始)(不包含) port_wan=18300 #wan端口(起始)(不包含) port_http=18400 #http端口(起始)(不包含) port_dns=18500 #dns端口(起始)(不包含) network_name="net-consul" #docker网络名称 token= #认证token,可以使用guid,之后连接需要携带token
然后进行下面的操作:
# 赋予可执行的权限,脚本需要使用/bin/bash来执行,如果使用sh执行可能会报错,sh默认使用/bin/dash执行 sudo chmod +x consul.sh # 启动 ./consul.sh up # 查看状态 ./consul.sh status # 停止并删除 ./consul.sh down
如,启动后:
然后可以在浏览器上访问数组机上对应server节点的8500端口的端口,比如这里的18401,18402,18403,就是打开consul的web管理页面了,如果需要token,就是文件中配置的token参数
使用docker-compose来管理
使用shell命令来管理可以很灵活的拓展,以及实现自己的需求,但是需要自行编码比较繁琐,如果需要拓展自己的一些功能,可以使用docker-compose来管理,如下面的内容复制到一个docker-compose.yml文件中:
# yaml 配置 version: '2' services: server1: image: consul restart: always container_name: server1 ports: - 18101:8300 - 18201:8301 - 18301:8302 - 18401:8500 - 18501:8600 expose: - 8300 - 8301 - 8302 - 8500 - 8600 command: agent -server -bootstrap-expect=3 -node=server1 -bind=0.0.0.0 -client=0.0.0.0 -ui -datacenter dc1 environment: # 不启用acl #CONSUL_LOCAL_CONFIG: '{"skip_leave_on_interrupt": true}' # 启用acl,下面的token_value是token值,可自行设置 CONSUL_LOCAL_CONFIG: '{"skip_leave_on_interrupt": true,"acl": {"enabled": true,"default_policy": "deny","down_policy": "extend-cache","tokens": {"master": "token_value"}}}' server2: image: consul restart: always container_name: server2 ports: - 18102:8300 - 18202:8301 - 18302:8302 - 18402:8500 - 18502:8600 expose: - 8300 - 8301 - 8302 - 8500 - 8600 depends_on: - server1 command: agent -server -bootstrap-expect=3 -node=server2 -bind=0.0.0.0 -client=0.0.0.0 -ui -datacenter dc1 -join server1 environment: # 不启用acl #CONSUL_LOCAL_CONFIG: '{"skip_leave_on_interrupt": true}' # 启用acl,下面的token_value是token值,可自行设置 CONSUL_LOCAL_CONFIG: '{"skip_leave_on_interrupt": true,"acl": {"enabled": true,"default_policy": "deny","down_policy": "extend-cache","tokens": {"master": "token_value"}}}' server3: image: consul restart: always container_name: server3 ports: - 18103:8300 - 18203:8301 - 18303:8302 - 18403:8500 - 18503:8600 expose: - 8300 - 8301 - 8302 - 8500 - 8600 depends_on: - server1 command: agent -server -bootstrap-expect=3 -node=server3 -bind=0.0.0.0 -client=0.0.0.0 -ui -datacenter dc1 -join server1 environment: # 不启用acl #CONSUL_LOCAL_CONFIG: '{"skip_leave_on_interrupt": true}' # 启用acl,下面的token_value是token值,可自行设置 CONSUL_LOCAL_CONFIG: '{"skip_leave_on_interrupt": true,"acl": {"enabled": true,"default_policy": "deny","down_policy": "extend-cache","tokens": {"master": "token_value"}}}' client1: image: consul restart: always container_name: client1 ports: - 18104:8300 - 18204:8301 - 18304:8302 - 18404:8500 - 18504:8600 expose: - 8300 - 8301 - 8302 - 8500 - 8600 depends_on: - server1 command: agent -node=client1 -bind=0.0.0.0 -client=0.0.0.0 -retry-join=server1 environment: CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}' client2: image: consul restart: always container_name: client2 ports: - 18105:8300 - 18205:8301 - 18305:8302 - 18405:8500 - 18505:8600 expose: - 8300 - 8301 - 8302 - 8500 - 8600 depends_on: - server2 command: agent -node=client2 -bind=0.0.0.0 -client=0.0.0.0 -retry-join=server2 environment: CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}' client3: image: consul restart: always container_name: client3 ports: - 18106:8300 - 18206:8301 - 18306:8302 - 18406:8500 - 18506:8600 expose: - 8300 - 8301 - 8302 - 8500 - 8600 depends_on: - server3 command: agent -node=client3 -bind=0.0.0.0 -client=0.0.0.0 -retry-join=server3 environment: CONSUL_LOCAL_CONFIG: '{"leave_on_terminate": true}'
然后可以使用docker-compose来启动和停止
# 启动 sudo docker-compose up -d # 停止 sudo docker-compose down
启动后,如:
同样的,这时可以在浏览器上访问数组机上对应server节点的8500端口的端口,比如这里的18401,18402,18403,就是打开consul的web管理页面了,如果需要token,就是文件中配置的token参数