docker consul_使用Docker和Consul进行服务发现:第1部分

docker consul

在过去的一年中,我非常喜欢使用Consul处理与服务发现有关的所有事情。 如果您正在执行微服务,那么您可能会遇到这样的问题:当您创建的服务数量增加时,管理所有这些服务之间的通信变得越来越困难。 领事非常适合此问题。 它为服务发现提供了一种易于使用的,基于开放标准的(有针对性的)方法(此外,它还提供了大量其他功能)。 我最近做了一个关于如何使用Consul在微服务体系结构中进行服务发现的演示,并收到了一些请求以对它进行更多的解释。 因此,在这篇文章中,以及一些后续操作中,我将进一步解释如何使用Consul。 我不仅会只关注Consul提供的服务发现部分,而且还会向您展示Consul或围绕它的工具之一提供的其他一些功能。 请注意,可以在以下存储库中找到所有示例,docker文件等: https : //github.com/josdirksen/next-build-consul 。 因此,无需克隆本文中的“复制和粘贴”,只需克隆存储库即可。

入门

在第一篇文章中,我们将创建一个基于docker的简单体系结构,其中包含许多服务,这些服务将使用简单的HTTP调用相互通信,并使用Consul相互发现。 我们最初的目标架构看起来像这样:

演示1

为了完成所有这些工作,我们首先需要采取以下步骤来设置环境,我们可以在其中运行服务:

注意:我正在Mac上使用docker-machine完成所有操作。 如果运行Windows或Linux,则命令可能会略有不同。 我们希望适用于Mac(和Windows)的DockerSwift脱离beta( https://blog.docker.com/2016/03/docker-for-mac-windows-beta/) ,因此我们不再需要它了…

  1. 创建四个docker-machine:将运行Consul服务器,三台将运行我们的服务和Consul代理。
  2. 启动主领事服务器:我们将使用一台领事服务器(和多个领事代理,稍后再介绍)来跟踪正在运行的服务和一些与docker相关的东西。
  3. 设置docker swarm:为避免必须单独部署我们的服务,我们将使用Docker Swarm来管理我们将在其上运行服务的三个节点。 在本文的其余部分中,我们将使用docker-compose来启动和停止单个服务。
  4. 设置docker覆盖网络:如果我们希望我们的服务以简单的方式相互通信,我们将创建一个覆盖网络。 这将使我们部署到docker的组件能够轻松地彼此通信(因为它们将共享同一子网)
  5. 启动Consul代理:每个节点将拥有自己的Consul代理,该代理将监视该节点上服务的运行状况并与Consul服务器通信。
创建docker-machines

因此,我们要做的第一件事是创建一些docker-machines。 首先,我们将创建用于存放领事服务器的docker-machine。 我们之所以首先运行它,是因为我们可以将其他docker-machine指向在该容器内运行的领事,并使用它来管理docker-swarm和我们要使用的覆盖网络。

docker-machine create nb-consul --driver virtualbox

在启动Consul服务器之前,让我们快速查看Consul背后的体系结构。

领事馆

在此图中,您可以看到Consul可以在两种模式下运行。它可以在Server模式或Agent模式下运行。 所有服务器互相交谈,并确定谁是领导者。 代理仅与其中一台服务器对话,通常在也运行服务的节点上运行。 请注意,群集中所有服务器和代理之间的状态是共享的。 因此,当一项服务向其中一个代理注册时,该信息可用于彼此连接的所有服务器和代理。

对于这组文章,我们将不会设置服务器集群,而只使用其中一个。 现在我们已经运行了docker-machine,我们可以启动领事服务器。 在开始之前,让我首先向您展示一个简单的脚本,该脚本使在不同的docker-machines之间切换以及我们用来避免键入“ docker-machine”的别名更加容易。

# quickly switch environments e.g: . dm-env nb-consul
$ cat ~/bin/dm-env
eval `docker-machine env $2 $1`
 
# avoid typing too much
$ alias dm
dm=docker-machine

因此,有了这些别名,首先我们执行“ dm-env nb-consul”以选择正确的docker-machine。

启动主领事机

接下来,我们获得该服务器的IP地址,然后我们可以像这样启动Consul服务器。

# get the ip address
$
192.168.99.106
 
# use this ip address in the advertise
docker run -d --restart always -p 8300:8300 -p 8301:8301 -p 8301:8301/udp -p 8302:8302/udp \ 
           -p 8302:8302 -p 8400:8400 -p 8500:8500 -p 53:53/udp -h server1 progrium/consul \
           -server -bootstrap -ui-dir /ui -advertise $(dm ip nb-consul)

至此,我们的docker consul服务器正在运行。 现在,让我们创建其他三台将在其上运行服务的服务器。

设置docker swarm

如您在以下命令中看到的,我们还同时创建了一个docker swarm集群,并且“ nb1”节点是swarm主机。

docker-machine create -d virtualbox --swarm --swarm-master \ 
           --swarm-discovery="consul://$(docker-machine ip nb-consul):8500" \
           --engine-opt="cluster-store=consul://$(docker-machine ip nb-consul):8500" \
           --engine-opt="cluster-advertise=eth1:2376" nb1
 
docker-machine create -d virtualbox --swarm  \
            --swarm-discovery="consul://$(docker-machine ip nb-consul):8500" \
            --engine-opt="cluster-store=consul://$(docker-machine ip nb-consul):8500" \
           --engine-opt="cluster-advertise=eth1:2376" nb2
 
docker-machine create -d virtualbox --swarm \
              --swarm-discovery="consul://$(docker-machine ip nb-consul):8500"  \
              --engine-opt="cluster-store=consul://$(docker-machine ip nb-consul):8500" \
              --engine-opt="cluster-advertise=eth1:2376" nb3

至此,我们已经启动并运行了四台docker机器。 一个正在运行领事主,而其他人还没有做太多。

$ dm ls
NAME        ACTIVE   DRIVER       STATE     URL                         SWARM
nb1         -        virtualbox   Running   tcp://192.168.99.110:2376   nb1 (master)
nb2         -        virtualbox   Running   tcp://192.168.99.111:2376   nb1
nb3         -        virtualbox   Running   tcp://192.168.99.112:2376   nb1
nb-consul   *        virtualbox   Running   tcp://192.168.99.106:2376

在继续配置从属服务器之前,可能需要使用另外一个实用程序脚本:

$ cat addToHost
#!/usr/bin/env bash
 
$ cat addToHost
#!/usr/bin/env bash
 
update-docker-host(){
	# clear existing docker.local entry from /etc/hosts
	sudo sed -i "/"${1}"\.local$/d" /etc/hosts
 
	# get ip of running machine
	export DOCKER_IP="$(docker-machine ip $1)"
 
	# update /etc/hosts with docker machine ip
	 && sudo /bin/bash -c "echo \"${DOCKER_IP} $1.local\" >> /etc/hosts"
}
 
update-docker-host nb1
update-docker-host nb2
update-docker-host nb3
update-docker-host nb-consul

该脚本将docker-machines的ip地址添加到本地“主机”文件中。 这意味着我们只需访问“ http://nb-consul.local:8500 ”即可简单地访问Docker主机。

设置Docker网络

在我们的方案中,我们希望我们所有的服务都能够相互通信。 我们有多个Docker主机,因此我们需要找到一种简单的方法来使服务在节点“ nb1”中运行,以便能够与“ nb2”进行通信。 实现此目的的最简单方法是创建一个由Docker容器中运行的所有服务使用的单一网络。 为此,我们创建一个简单的“覆盖”网络,如下所示:

# select the swarm master
$dm-env nb1 --swarm
# create an overlay network the the name my-net

并且由于我们是在群集主服务器上创建的,因此该网络将在群集的所有成员中可用。 稍后创建服务时,我们会将其连接到该网络,以便它们都共享相同的子网。

启动领事代理

要启动领事代理,我们将使用docker-compose。 docker-compose文件非常简单,并且是避免键入所有启动命令的简单方法(尤其是在进行实时演示时)

version: '2'
 
services:
  agent-1:
    image: progrium/consul
    container_name: consul_agent_1
    ports:
      - 8300:8300
      - 8301:8301
      - 8301:8301/udp
      - 8302:8302
      - 8302:8302/udp
      - 8400:8400
      - 8500:8500
      - 53:53/udp
    environment:
      - "constraint:node==nb1"
    command: -ui-dir /ui -join 192.168.99.106 -advertise 192.168.99.110
    networks:
      default:
        aliases:
          - agent-1
 
  agent-2:
    image: progrium/consul
    container_name: consul_agent_2
    ports:
      - 8300:8300
      - 8301:8301
      - 8301:8301/udp
      - 8302:8302
      - 8302:8302/udp
      - 8400:8400
      - 8500:8500
      - 53:53/udp
    environment:
      - "constraint:node==nb2"
    command: -ui-dir /ui -join 192.168.99.106 -advertise 192.168.99.111
    networks:
      default:
        aliases:
          - agent-2
 
  agent-3:
    image: progrium/consul
    container_name: consul_agent_3
    ports:
      - 8300:8300
      - 8301:8301
      - 8301:8301/udp
      - 8302:8302
      - 8302:8302/udp
      - 8400:8400
      - 8500:8500
      - 53:53/udp
    environment:
      - "constraint:node==nb3"
    command: -ui-dir /ui -join 192.168.99.106 -advertise 192.168.99.112
    networks:
      default:
        aliases:
          - agent-3
 
networks:
  default:
    external:
      name: my-net

此文件没有什么特别的。 您可能会注意到的唯一一件事是,我们在命令中使用显式IP地址来启动Consul代理。 我们可以轻松地为此使用一个环境变量,它是通过一个简单的bash脚本设置的。 但是对于本文,我们仅指定相关码头工人机器的IP地址。 确保您的“ DOCKER_HOST”指向docker swarm master并启动代理,如下所示:

# start the agents
$ docker-compose -f docker-compose-agents.yml up -d
Creating consul_agent_3
Creating consul_agent_2
Creating consul_agent_1
 
# check what is running
$ docker ps  --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
 
bf2000882dcc	progrium/consul	"/bin/start -ui-dir /"	nb1/consul_agent_1
a1bc26eef516	progrium/consul	"/bin/start -ui-dir /"	nb2/consul_agent_2
eb0d1c0cc075	progrium/consul	"/bin/start -ui-dir /"	nb3/consul_agent_3

此时,我们有一个在Consul机器“ nb-consul”中运行的Consul服务器,并且在节点上运行了三个代理。 为了验证我们的设置,我们打开Consul服务器的界面: http://nb-consul.local:8500

consul_1

而且,正如您所看到的,我们有1台服务器(我们的Consul Server)正在运行,并且有3个代理。 因此,在这一点上,我们可以开始添加我们的服务,以使用此架构:

演示1(1)

添加服务

在这种情况下,服务只是简单的golang应用程序。 我创建了一个可以在前端或后端模式下运行的简单应用程序。 在前端模式下,它提供带有按钮的最小UI,以调用后端服务;在后端模式下,它提供一个简单的API,该API将一些信息返回给主叫方,并且提供一个简单的UI,显示一些统计信息。 为了方便起见,我已将此映像推送到Docker中心( https://hub.docker.com/r/josdirksen/demo-service/ ),因此您可以轻松使用它,而无需从源github存储库中构建。

如您在以前的体系结构概述中所看到的,我们希望在每个节点上启动一个前端和一个后端服务。 我们可以手动执行此操作,但是由于有了docker-swarm,我们可以轻松地通过单个docker-compose文件执行此操作。 如果要查看此文件的外观,可以在此处查看源代码( https://github.com/josdirksen/next-build-consul)

首先启动服务,然后看一下它们如何在Consul中注册自己:

# make sure you select the swarm master
$ . dm-env nb1 --swarm
 
# now use docker-compose to run the backend services
$ docker-compose -f docker-compose-backend.yml up -d
Creating Backend2
Creating Backend3
Creating Backend1
 
# and use docker-compose to run the frontend services
$ docker-compose -f docker-compose-frontend.yml up -d
Creating Frontend1
Creating Frontend3
Creating Frontend2
 
# check in docker if everything is running
$ docker ps --format '{{ .ID }}\t{{ .Image }}\t{{ .Command }}\t{{ .Names}}'
 
65846be2e367    josdirksen/demo-service "/entrypoint.sh --typ"  nb2/Frontend2
aedd80ab0889    josdirksen/demo-service "/entrypoint.sh --typ"  nb3/Frontend3
d9c3b1d83b5e    josdirksen/demo-service "/entrypoint.sh --typ"  nb1/Frontend1
7c860403b257    josdirksen/demo-service "/entrypoint.sh --typ"  nb1/Backend1
80632e910d33    josdirksen/demo-service "/entrypoint.sh --typ"  nb3/Backend3
534da0670e13    josdirksen/demo-service "/entrypoint.sh --typ"  nb2/Backend2
bf2000882dcc    progrium/consul "/bin/start -ui-dir /"  nb1/consul_agent_1
a1bc26eef516    progrium/consul "/bin/start -ui-dir /"  nb2/consul_agent_2
eb0d1c0cc075    progrium/consul "/bin/start -ui-dir /"  nb3/consul_agent_3

如您在“ docker ps”的最后输出中所见,我们有三个前端,三个后端和三个领事代理正在运行。 这几乎就是我们的目标。 当我们打开领事时,我们也可以看到以下内容:

领事_2

如您所见,我们在Consul中注册了三个前端服务和三个后端服务。 如果我们打开一个后端,我们将看到一些常规信息:

后端_1

我们可以使用前端UI来调用我们的后端之一:

frontend_1

但是,我们需要回答几个问题:

  1. 服务注册 :启动后端或前端服务时,我们会在Consul中看到它。 我们如何做到这一点?
  2. 服务发现 :当我们单击前端服务上的按钮时,就会调用其中一个后端服务。 前端如何知道要调用哪个服务?

在接下来的部分中,我们将进一步探讨这些问题。

服务注册

首先,服务注册。 要向Consul注册服务,我们必须对本地consul-agent进行非常简单的REST调用,如下所示:

{
  "Name": "service1",
  "address": "10.0.0.12",
  "port": 8080,
  "Check": {
     "http": "http://10.0.0.12:8080/health",
     "interval": "5s"
  }
}

如您所见,我们指定可以找到服务的名称,地址和端口,并添加其他运行状况检查。 当healtcheck返回200范围内的值时,该服务将标记为运行状况良好,并且可以被其他服务发现。 那么我们如何为我们的服务做到这一点。 如果查看此示例的源代码,则可以找到“ script / entrypoint.sh”文件,如下所示:

#!/usr/bin/env bash
 
IP=`ip addr | grep -E 'eth0.*state UP' -A2 | tail -n 1 | awk '{print $2}' | cut -f1 -d '/'`
NAME="$2-service"
 
read -r -d '' MSG << EOM
{
  "Name": "$NAME",
  "address": "$IP",
  "port": $PORT,
  "Check": {
     "http": "http://$IP:$PORT",
     "interval": "5s"
  }
}
EOM
 
curl -v -XPUT -d "$MSG" http://consul_agent_$SERVER_ID:8500/v1/agent/service/register && /app/main "$@"

该脚本的作用是,它创建要发送到领事代理的JSON,并在启动主应用程序之前使用“ curl”发送它。 因此,当服务启动时,它会自动将其注册到本地领事代理(请注意,例如,您也可以使用Consul Registrator来更自动地进行此操作。之所以可行,是因为我们可以通过其名称引用本地代理,因为它位于如果您仔细观察,可能会发现我们在这里使用了两个环境变量,这些变量是通过docker-compose文件传入的:

...
frontend-1:
    image: josdirksen/demo-service
    container_name: Frontend1
    ports:
      - 8090:8090
    environment:
      - "constraint:node==nb1"
      - SERVER_ID=1
      - SERVERNAME=Server1
      - PORT=8090
    command: /entrypoint.sh --type frontend
    dns: 192.168.99.106
    dns_search: service.consul
...

有趣的部分是DNS条目。 您可能还记得192.168.99.106是我们的领事服务器的地址。 这意味着我们针对Consul进行DNS查找(我们也可能指向Consul代理)。

服务发现

通过此设置,我们可以仅按名称引用服务,然后使用DNS来解析它。 下面显示了它是如何工作的。

# check which IPs are registered for the backend-service
# called from outside the container
$ dig @nb-consul.local backend-service.service.consul +short
10.0.9.7
10.0.9.8
10.0.9.6
 
# If we do this from a container, we can do just this
docker exec -ti nb2/Frontend2 ping backend-service
PING backend-service.service.consul (10.0.9.8): 56 data bytes
64 bytes from 10.0.9.8: icmp_seq=0 ttl=64 time=0.809 ms
64 bytes from 10.0.9.8: icmp_seq=1 ttl=64 time=0.636 ms

酷吧? 我们只需使用DNS即可发现服务。 这也意味着将其集成到我们现有的应用程序中非常容易,因为我们只能依靠基本的DNS解析。 例如,在前端服务中,我们使用以下代码调用后端:

resp, err := http.Get("http://backend-service:8081")
if err != nil {
	// handle error
	fmt.Println(err)
} else {
	defer resp.Body.Close()
	body, _ := ioutil.ReadAll(resp.Body)
	w.Header().Set("Content-Type",resp.Header.Get("Content-Type"))
	w.Write(body)
}

这将使用DNS调用后端服务之一。 由于Consul的DNS timetolive设置为0,我们现在也有了一些简单的故障转移。应用程序可能仍会进行一些缓存,但这意味着我们已经有了一些基本的故障转移:

$ curl -s backend-service:8081
 
        {"result" : {
          "servername" : "Server1",
          "querycount" : 778
          }
        }
 
# shutdown server 1 and do again, curl has a DNS cache of
# 1 minute, so you might need to wait a bit
$ curl -s backend-service:8081
 
        {"result" : {
          "servername" : "Server2",
          "querycount" : 770
          }
        }
 
$ curl -s backend-service:8081
 
        {"result" : {
          "servername" : "Server2",
          "querycount" : 771
          }
        }

当然,哪种方法也适用于我们的前端/ golang应用程序:

consul_3

在本文的后续文章中,我们还将通过引入HAProxy作为更高级的故障转移技术的中介,来展示一些更高级的故障转移。

结论

这几乎涵盖了第一篇文章。 总而言之,我们做了什么:

  1. 我们使用4个docker节点设置了一个简单的架构。 1个用于领事服务器,三个用于我们的服务。
  2. 服务在服务启动时向Consul注册。
  3. 我们无需显式执行某些操作即可启用服务发现。 我们可以使用标准DNS查找服务。
  4. Consul对DNS使用TTL为0,并使用轮询机制返回可用的服务。 如您所见,当DNS查找失败时,您已经可以将其用于基本故障转移。

在接下来的几周内,请继续关注后续文章。

翻译自: https://www.javacodegeeks.com/2016/04/service-discovery-docker-consul-part-1.html

docker consul

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值