HashiCorp 公司推出的Consul是一款分布式高可用服务治理与服务配置的工具。关于其配置与使用能够参考这篇文章 consul 简介与配置说明。javascript
通常,咱们会在多台主机上安装并启动 consul,在开发时这可能会比较不方便,因此这里介绍如何使用 vagrant 和 docker 来简化开发环境的搭建。java
Vagrant 是 HashiCorp 公司的产品, 用于建立和部署虚拟化开发环境,支持常见的操做系统。因为其安装比较简单,参照官方文档便可,此处再也不赘述。node
安装完成后,咱们建立文件夹consul,在consul文件夹下建立一个文件Vagrantfile,内如以下:python
# -*- mode: ruby -*- # vi: set ft=ruby : $script = <<SCRIPT echo "Installing dependencies ..." # 使用阿里云镜像 sudo sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list sudo apt-get update sudo apt-get install -y unzip curl jq SCRIPT # Specify a custom Vagrant box for the demo DEMO_BOX_NAME = "ubuntu/xenial64" # Vagrantfile API/syntax version. # NB: Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = DEMO_BOX_NAME config.vm.provision "shell", inline: $script config.vm.define "n1" do |n1| n1.vm.hostname = "n1" n1.vm.network "private_network", ip: "172.20.20.10" end config.vm.define "n2" do |n2| n2.vm.hostname = "n2" n2.vm.network "private_network", ip: "172.20.20.11" end config.vm.define "n3" do |n3| n3.vm.hostname = "n3" n3.vm.network "private_network", ip: "172.20.20.12" end end
# -*- mode: ruby -*- # vi: set ft=ruby : $script = <<SCRIPT echo "Installing dependencies ..." # 使用阿里云镜像 sudo sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list sudo apt-get update sudo apt-get install -y unzip curl jq SCRIPT # Specify a custom Vagrant box for the demo DEMO_BOX_NAME = "ubuntu/xenial64" # Vagrantfile API/syntax version. # NB: Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| config.vm.box = DEMO_BOX_NAME config.vm.provision "shell", inline: $script config.vm.define "n1" do |n1| n1.vm.hostname = "n1" n1.vm.network "private_network", ip: "172.20.20.10" end config.vm.define "n2" do |n2| n2.vm.hostname = "n2" n2.vm.network "private_network", ip: "172.20.20.11" end config.vm.define "n3" do |n3| n3.vm.hostname = "n3" n3.vm.network "private_network", ip: "172.20.20.12" end end
这里咱们建立了三个虚拟机,hostname为 n1,n2,n3;ip分别为 172.20.20.10,172.20.20.11,172.20.20.10,使用镜像 ubuntu/xenial64,而且将镜像更新为了阿里云,安装了一些必须的软件。linux
编辑好该文件,在命令行运行 vagrant up
,出去喝杯咖啡,你的机器上就会启动三台对应的虚拟机了。若是你不想使用ubuntu 16.04,还能够更换其余的操做系统。git
在 consul 文件夹下使用 vagrant ssh命令,就能够登陆到对应的虚拟机了:github
➜ consul vagrant ssh n2 Welcome to Ubuntu 16.04.2 LTS (GNU/Linux 4.4.0-66-generic x86_64) 0 packages can be updated. 0 updates are security updates. Last login: Tue Mar 28 04:13:00 2017 from 10.0.2.2 ubuntu@n2:~$
docker的安装请参照这里docker安装。须要注意,鉴于 dockerhub 在国内丧心病狂的访问速度,咱们通常会选择使用国内的镜像,如阿里云、网易蜂巢等。web
安装好了docker后,咱们以 n1 虚拟机为例,说明如何在虚拟机上使用docker容器来运行consul。docker
首先,建立一个consul文件夹,而后编辑 Dockerfile:shell
FROM phusion/baseimage:latest MAINTAINER millions <chenzhesp@gmail.com> RUN DEBIAN_FRONTEND=noninteractive RUN locale-gen en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV LC_CTYPE=UTF-8 ENV LANG=en_US.UTF-8 ENV TERM xterm RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get -qq install curl unzip ADD https://releases.hashicorp.com/consul/0.7.5/consul_0.7.5_linux_amd64.zip /tmp/consul.zip RUN cd /usr/sbin && unzip /tmp/consul.zip && chmod +x /usr/sbin/consul && rm /tmp/consul.zip ADD consul.json /config/ ADD https://releases.hashicorp.com/consul/0.7.5/consul_0.7.5_web_ui.zip /tmp/webui.zip RUN mkdir /webui && unzip /tmp/webui.zip -d /webui/ && rm /tmp/webui.zip EXPOSE 8300 8301 8301/udp 8302 8302/udp 8400 8500 53/udp VOLUME ["/data"] ENTRYPOINT [ "/usr/sbin/consul", "agent", "-config-dir=/config" ] CMD []
FROM phusion/baseimage:latest MAINTAINER millions <chenzhesp@gmail.com> RUN DEBIAN_FRONTEND=noninteractive RUN locale-gen en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV LC_CTYPE=UTF-8 ENV LANG=en_US.UTF-8 ENV TERM xterm RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get -qq install curl unzip ADD https://releases.hashicorp.com/consul/0.7.5/consul_0.7.5_linux_amd64.zip /tmp/consul.zip RUN cd /usr/sbin && unzip /tmp/consul.zip && chmod +x /usr/sbin/consul && rm /tmp/consul.zip ADD consul.json /config/ ADD https://releases.hashicorp.com/consul/0.7.5/consul_0.7.5_web_ui.zip /tmp/webui.zip RUN mkdir /webui && unzip /tmp/webui.zip -d /webui/ && rm /tmp/webui.zip EXPOSE 8300 8301 8301/udp 8302 8302/udp 8400 8500 53/udp VOLUME ["/data"] ENTRYPOINT [ "/usr/sbin/consul", "agent", "-config-dir=/config" ] CMD []
大约解释一下上面的Dockerfile。首先,咱们选择phusion/baseimage为基础构建docker容器。而后设置使用阿里云镜像,安装各类工具,下载 consul 并建立 consul 使用的 ui 文件夹。而后声明暴露8个 consul 须要使用的 tcp 或 udp 端口,并将 /data 设置为一个 VOLUME (经过--volumes-from能够在容器之间共享数据)。 最后,注意 ENTRYPOINT 声明了容器启动时执行的命令,而在启动容器时加上的命令会被添加在 ENTRYPOINT 指定的命令后,好比ENTRYPOINT为 [ "/usr/sbin/consul", "agent", "-config-dir=/config" ]
,启动镜像时的命令为 sudo docker run <镜像名称> XXXX
,则容器启动时执行的命令为 /usr/sbin/consul agent -config-dir=/config XXXX
。
另外,为了使用 consul,咱们也须要编辑其配置文件 consul.json。这里咱们使用一个比较简单的配置,有特殊须要的请参阅 consul 官方文档的配置说明。
{
"data_dir": "/data",
"ui_dir": "/webui",
"client_addr": "0.0.0.0",
"ports": {
"dns": 53
},
"recursor": "8.8.8.8"
}
编辑好这2个文件后,在consul文件夹下执行命令 sudo docker build -t millions/consul .
便可建立镜像 millions/consul,经过 docker images
命令能够查看。
ubuntu@n1:~/consul$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE millions/consul latest 5fc1597b91cf 23 hours ago 372 MB phusion/baseimage latest b6b482650b50 7 days ago 247 MB
ubuntu@n1:~/consul$ sudo docker images REPOSITORY TAG IMAGE ID CREATED SIZE millions/consul latest 5fc1597b91cf 23 hours ago 372 MB phusion/baseimage latest b6b482650b50 7 days ago 247 MB
然后在其余虚拟机使用一样方法建立镜像。为了方便启动 docker 镜像,咱们再在每台虚拟机上建立如下环境变量:
# 在 Vagrantfile 中指定的ip,对于n1为172.20.20.10
export PUBLIC_IP="$(ifconfig enp0s8 | awk -F ' *|:' '/inet addr/{print $4}')"
然后,因为咱们打算用n1作 bootstrap 时的leader,因此在 n二、n3 上建立环境变量:
export JOIN_IP=172.20.20.10
接下来,咱们就能够准备启动 consul 实例们啦~
注意启动时须要使用 -h $HOSTNAME
指定容器的hostname。首先咱们启动 n1,因为用其作 bootstrap, 因此咱们使用了 -bootstrap-expect 3
。其余相关启动参数若是不熟悉的话能够查看 consul 的文档,这里不作详细解释。比较重要的是须要额外设置dns,首先使用本地 docker0 来使用 consul 解析 DNS,而后使用 8.8.8.8解析其余请求的dns,最后为 consul 的查询指定搜索域。
sudo docker run -d -h $HOSTNAME -p 8300:8300 \
-p 8301:8301 -p 8301:8301/udp -p 8302:8302 \
-p 8302:8302/udp -p 8400:8400 -p 8500:8500 \
-p 53:53/udp --name n1 \
--dns 172.17.0.1 --dns 8.8.8.8 \
--dns-search 'service.consul' millions/consul \
-server -advertise $PUBLIC_IP -bootstrap-expect 3
这是查看 n1 的输出发现其已经启动,可是还没法组成集群:
ubuntu@n1:~/consul$ sudo docker logs n1 ==> WARNING: Expect Mode enabled, expecting 3 servers ==> Starting Consul agent... ==> Starting Consul agent RPC... ==> Consul agent running! Version: 'v0.7.5' Node ID: 'cedd93d8-4ff4-4bde-8bb4-391f5a129ed4' Node name: 'n1' Datacenter: 'dc1' Server: true (bootstrap: false) Client Addr: 0.0.0.0 (HTTP: 8500, HTTPS: -1, DNS: 53, RPC: 8400) Cluster Addr: 172.20.20.10 (LAN: 8301, WAN: 8302) Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false Atlas: <disabled>
ubuntu@n1:~/consul$ sudo docker logs n1 ==> WARNING: Expect Mode enabled, expecting 3 servers ==> Starting Consul agent... ==> Starting Consul agent RPC... ==> Consul agent running! Version: 'v0.7.5' Node ID: 'cedd93d8-4ff4-4bde-8bb4-391f5a129ed4' Node name: 'n1' Datacenter: 'dc1' Server: true (bootstrap: false) Client Addr: 0.0.0.0 (HTTP: 8500, HTTPS: -1, DNS: 53, RPC: 8400) Cluster Addr: 172.20.20.10 (LAN: 8301, WAN: 8302) Gossip encrypt: false, RPC-TLS: false, TLS-Incoming: false Atlas: <disabled>
接下来,咱们来启动 n2 还有 n3(注意修改 --name 参数):
sudo docker run -d -h $HOSTNAME \
-p 8300:8300 -p 8301:8301 -p 8301:8301/udp \
-p 8302:8302 -p 8302:8302/udp -p 8400:8400 \
-p 8500:8500 -p 53:53/udp --name n2 \
-dns 172.17.0.1 --dns 8.8.8.8 \
--dns-search 'service.consul' millions/consul \
-server -advertise $PUBLIC_IP -join $JOIN_IP
这里做为后来加入者咱们使用 -join参数而不是 bootstrap-expect。都完成启动后在物理机上访问 http://172.20.20.10:8500 便可看到熟悉的consul web-ui 界面。至此,咱们的 consul 集群开发环境就搭建完成了,咱们可使用其余各类程序将本身做为服务注册到 consul 集群中,而且访问集群获取其余服务地址。
为了测试集群是否能够正常工做,咱们再建立其余2个docker 镜像。
Dockerfile为:
ROM phusion/baseimage:latest MAINTAINER millions <chenzhesp@gmail.com> RUN DEBIAN_FRONTEND=noninteractive RUN locale-gen en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV LC_CTYPE=UTF-8 ENV LANG=en_US.UTF-8 ENV TERM xterm RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get -qq install ruby-dev git libcurl4-openssl-dev curl build-essential python RUN gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/ RUN gem install --no-ri --no-rdoc uwsgi sinatra RUN mkdir -p /opt/distributed_app WORKDIR /opt/distributed_app RUN uwsgi --build-plugin https://github.com/unbit/uwsgi-consul ADD uwsgi-consul.ini /opt/distributed_app/ ADD config.ru /opt/distributed_app/ ENTRYPOINT [ "uwsgi", "--ini", "uwsgi-consul.ini", "--ini", "uwsgi-consul.ini:server1", "--ini", "uwsgi-consul.ini:server2" ] CMD []
ROM phusion/baseimage:latest MAINTAINER millions <chenzhesp@gmail.com> RUN DEBIAN_FRONTEND=noninteractive RUN locale-gen en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV LC_CTYPE=UTF-8 ENV LANG=en_US.UTF-8 ENV TERM xterm RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get -qq install ruby-dev git libcurl4-openssl-dev curl build-essential python RUN gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/ RUN gem install --no-ri --no-rdoc uwsgi sinatra RUN mkdir -p /opt/distributed_app WORKDIR /opt/distributed_app RUN uwsgi --build-plugin https://github.com/unbit/uwsgi-consul ADD uwsgi-consul.ini /opt/distributed_app/ ADD config.ru /opt/distributed_app/ ENTRYPOINT [ "uwsgi", "--ini", "uwsgi-consul.ini", "--ini", "uwsgi-consul.ini:server1", "--ini", "uwsgi-consul.ini:server2" ] CMD []
config.ru 的内容为:
require 'rubygems' require 'sinatra' get '/' do "Hello World!" end run Sinatra::Application
require 'rubygems' require 'sinatra' get '/' do "Hello World!" end run Sinatra::Application
uwsgi-consul.ini 的内容为:
[uwsgi] plugins = consul socket = 127.0.0.1:9999 master = true enable-threads = true [server1] consul-register = url=http://%h.node.consul:8500,name=distributed_app,id=server1,port=2001 mule = config.ru [server2] consul-register = url=http://%h.node.consul:8500,name=distributed_app,id=server2,port=2002 mule = config.ru
[uwsgi] plugins = consul socket = 127.0.0.1:9999 master = true enable-threads = true [server1] consul-register = url=http://%h.node.consul:8500,name=distributed_app,id=server1,port=2001 mule = config.ru [server2] consul-register = url=http://%h.node.consul:8500,name=distributed_app,id=server2,port=2002 mule = config.ru
然后建立镜像:
sudo docker build -t millions/distributed_app .
咱们建立好镜像后,在 n1 和 n2 上分别启动该容器:
sudo docker run -h $HOSTNAME -d --name n1-distributed \
--dns 172.17.0.1 --dns 8.8.8.8 \
--dns-search 'service.consul' millions/distributed_app
完成以后能够在 web-ui 上看到 n1 和 n2 上各有2个名为distributed_app的服务。
咱们使用一个脚原本进行测试,为了再温习一下 docker,咱们仍是将其放在一个 docker 容器中运行,Dockerfile 以下,注意咱们替换了使用了淘宝的 ruby source 来加速:
FROM phusion/baseimage:latest MAINTAINER millions <chenzhesp@gmail.com> RUN DEBIAN_FRONTEND=noninteractive RUN locale-gen en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV LC_CTYPE=UTF-8 ENV LANG=en_US.UTF-8 ENV TERM xterm RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get -qq install ruby ruby-dev build-essential RUN gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/ RUN gem install --no-ri --no-rdoc json RUN mkdir -p /opt/distributed_client ADD client.rb /opt/distributed_client/ WORKDIR /opt/distributed_client ENTRYPOINT [ "ruby", "/opt/distributed_client/client.rb" ] CMD []
FROM phusion/baseimage:latest MAINTAINER millions <chenzhesp@gmail.com> RUN DEBIAN_FRONTEND=noninteractive RUN locale-gen en_US.UTF-8 ENV LANGUAGE=en_US.UTF-8 ENV LC_ALL=en_US.UTF-8 ENV LC_CTYPE=UTF-8 ENV LANG=en_US.UTF-8 ENV TERM xterm RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list RUN apt-get update -y RUN apt-get upgrade -y RUN apt-get -qq install ruby ruby-dev build-essential RUN gem sources --add https://gems.ruby-china.org/ --remove https://rubygems.org/ RUN gem install --no-ri --no-rdoc json RUN mkdir -p /opt/distributed_client ADD client.rb /opt/distributed_client/ WORKDIR /opt/distributed_client ENTRYPOINT [ "ruby", "/opt/distributed_client/client.rb" ] CMD []
ruby 脚本以下,分别使用了http和dns两种方式来获取服务:
require "rubygems" require "json" require "net/http" require "uri" require "resolv" empty = "There are no distributed applications registered in Consul" uri = URI.parse("http://consul.service.consul:8500/v1/catalog/service/distributed_app") http = Net::HTTP.new(uri.host, uri.port) request = Net::HTTP::Get.new(uri.request_uri) while true response = http.request(request) if response.body == "{}" puts empty sleep(1) elsif result = JSON.parse(response.body) result.each do |service| puts "Application #{service['ServiceName']} with element #{service["ServiceID"]} on port #{service["ServicePort"]} found on node #{service["Node"]} (#{service["Address"]})." sleep(1) end end end
require "rubygems" require "json" require "net/http" require "uri" require "resolv" empty = "There are no distributed applications registered in Consul" uri = URI.parse("http://consul.service.consul:8500/v1/catalog/service/distributed_app") http = Net::HTTP.new(uri.host, uri.port) request = Net::HTTP::Get.new(uri.request_uri) while true response = http.request(request) if response.body == "{}" puts empty sleep(1) elsif result = JSON.parse(response.body) result.each do |service| puts "Application #{service['ServiceName']} with element #{service["ServiceID"]} on port #{service["ServicePort"]} found on node #{service["Node"]} (#{service["Address"]})." sleep(1) end end end
建立好镜像:
sudo docker build -t millions/distributed_client .
运行之:
sudo docker run -h $HOSTNAME -d --name n3-distributed \
--dns 172.17.0.1 --dns 8.8.8.8 \
--dns-search 'service.consul' millions/distributed_client
使用 sudo docker logs n3-distributed
命令,能够查看到:
ubuntu@n3:~$ sudo docker logs n3-distributed Application distributed_app with element server1 on port 2001 found on node n1 (172.20.20.10). Application distributed_app with element server2 on port 2002 found on node n1 (172.20.20.10). Application distributed_app with element server1 on port 2001 found on node n2 (172.20.20.11). Application distributed_app with element server2 on port 2002 found on node n2 (172.20.20.11).
ubuntu@n3:~$ sudo docker logs n3-distributed Application distributed_app with element server1 on port 2001 found on node n1 (172.20.20.10). Application distributed_app with element server2 on port 2002 found on node n1 (172.20.20.10). Application distributed_app with element server1 on port 2001 found on node n2 (172.20.20.11). Application distributed_app with element server2 on port 2002 found on node n2 (172.20.20.11).
或者可使用 dns 来向 consul 查询 ip 以及 port,咱们利用 dig 命令查看 dns 的结果,能够看到获取了 port 以及 n1 和 n2 的 ip:
ubuntu@n3:~$ dig @172.17.0.1 distributed_app.service.consul SRV ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @172.17.0.1 distributed_app.service.consul SRV ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30551 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 2 ;; QUESTION SECTION: ;distributed_app.service.consul. IN SRV ;; ANSWER SECTION: distributed_app.service.consul. 0 IN SRV 1 1 2001 n2.node.dc1.consul. distributed_app.service.consul. 0 IN SRV 1 1 2001 n1.node.dc1.consul. distributed_app.service.consul. 0 IN SRV 1 1 2002 n1.node.dc1.consul. ;; ADDITIONAL SECTION: n2.node.dc1.consul. 0 IN A 172.20.20.11 n1.node.dc1.consul. 0 IN A 172.20.20.10 ;; Query time: 0 msec ;; SERVER: 172.17.0.1#53(172.17.0.1) ;; WHEN: Tue Mar 28 14:41:34 UTC 2017 ;; MSG SIZE rcvd: 155
ubuntu@n3:~$ dig @172.17.0.1 distributed_app.service.consul SRV ; <<>> DiG 9.10.3-P4-Ubuntu <<>> @172.17.0.1 distributed_app.service.consul SRV ; (1 server found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30551 ;; flags: qr aa rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 2 ;; QUESTION SECTION: ;distributed_app.service.consul. IN SRV ;; ANSWER SECTION: distributed_app.service.consul. 0 IN SRV 1 1 2001 n2.node.dc1.consul. distributed_app.service.consul. 0 IN SRV 1 1 2001 n1.node.dc1.consul. distributed_app.service.consul. 0 IN SRV 1 1 2002 n1.node.dc1.consul. ;; ADDITIONAL SECTION: n2.node.dc1.consul. 0 IN A 172.20.20.11 n1.node.dc1.consul. 0 IN A 172.20.20.10 ;; Query time: 0 msec ;; SERVER: 172.17.0.1#53(172.17.0.1) ;; WHEN: Tue Mar 28 14:41:34 UTC 2017 ;; MSG SIZE rcvd: 155
至此,咱们的consul 集群开发环境就搭建好了。关机前不要忘记用 vagrant halt
或者 vagrant suspend
关闭或暂停你的虚拟机哦~