Recommanded Free YOUTUBE Lecture: <% selectedImage[1] %>

Contents

Consul

Consule는 서비스 디스커버리(Service discovery)와 설정을 관리하는 툴이다. Consule는 분산&클라우드 환경에 적응하기 위한 고가용성, 유연한 스케일링, 분산시스템의 특징을 가진다.

Consul의 핵심 기능은 아래와 같다.
  • 서비스 디스커버리 : DNS 나 HTTP 인터페이스를 통해서 서비스를 찾을 수 있게 한다. 외부의 SaaS 서비스업체도 등록할 수 있다.
  • Health Checking : 클러스터의 건강 상태를 모니터링하며 문제가 생길 경우 신속하게 전파한다. 헬스체크는 서비스 디스커버리와 함께 작동하며, 문제가 생긴 호스트로 서비스 요청이 흐르는 걸 막는다. 이를 이용해서 서비스레벨에서의 서킷 브레이커(circuit breaker)를 구현 할 수 있다.
  • KV(Key/Value) 저장소(Store) : Consul은 계층적으로 구성 할 수 있는 KV 저장소를 제공한다. 애플리케이션은 설정, 플래그, 리더 선출 등 다양한 목적을 위해서 이 저장소를 사용 할 수 있다. 이 저장소는 HTTP API를 이용해서 간단하게 사용 할 수 있다.
  • 멀티 데이터센터 대응 : 데이터센터 규모에서 사용 할 수 있으며, 복잡한 구성없이 여러 리전(region)을 지원 할 수 있다.
  • Service Segmentation : Consule Connect는 TLS를 이용 서비스와 서비스사이에 안전한 통신이 가능하게 한다.
이 문서는 아래의 순서로 진행한다.
  1. 로컬에 설치해서 기본적인 사용방법을 익힌다.
  2. 분산 환경(여러 노드에) 설치한다.
  3. 분산 환경에서 응용을 고민한다.

Consul 설치

환경

우분투 리눅스 19.04, 커널 5.0.0
# cat /etc/issue
Ubuntu 19.04 \n \l

#u name -sr
Linux 5.0.0-26-generic

도커 버전 18.06
# docker version
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.4
 Git commit:        e68fc7a
 Built:             Tue May  7 17:57:34 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          18.09.6
  API version:      1.39 (minimum version 1.12)
  Go version:       go1.10.8
  Git commit:       481bc77
  Built:            Sat May  4 01:59:36 2019
  OS/Arch:          linux/amd64
  Experimental:     false
도커는 consul 클러스터를 구성하기 위해서 사용한다.

Consul 다운로드

Download Consul에서 다운로드 할 수 있다. 2019년 8월 현재 consul 버전은 1.5.3 이다.
# wget https://releases.hashicorp.com/consul/1.5.3/consul_1.5.3_linux_amd64.zip
# unzip consul_1.5.3_linux_amd64.zip 
Archive:  consul_1.5.3_linux_amd64.zip
  inflating: consul                  
# sudo cp consul /usr/local/bin 
# consul version
Consul v1.5.3
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)

Dockerizing

나는 consul 클러스터를 구성해서 테스트하고 싶었다. VM(KVM/QEMU) 기반으로 수행 할 수 있었지만 시간이 많이 걸릴 것 같아서 컨테이너 기반으로 테스트하기로 했다. 아래와 같이 Dockerfile을 만들었다. consul 실행파일 외에, consul을 테스트하기 위한 기본툴들도 함께 설치했다.
FROM ubuntu
MAINTAINER yundream

RUN apt-get update 
RUN apt-get -y install vim
RUN apt-get -y install curl
RUN apt-get -y install dnsutils
ADD ./consul /usr/local/bin

CMD /bin/bash

이미지를 빌드한다.
# docker build -t joinc/consul:1.0.0 -t joinc/consul:latest .

consul 이미지를 실행했다.
# docker run --hostname=consul01 -it --name consul01 joinc/consul /bin/bash
root@consul01:/# 

Agent 실행

일단 개발 모드로 실행해봤다.
root@consul01:/# consul agent -dev
==> Starting Consul agent...
           Version: 'v1.5.3'
           Node ID: '80115d27-e681-da61-1672-c994af0b0f2b'
         Node name: 'consul01'
        Datacenter: 'dc1' (Segment: '<all>')
            Server: true (Bootstrap: false)
       Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
      Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
           Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false

==> Log data will now stream in as it occurs:
실행됐다. 127.0.0.1로 바인드 했으니, 로컬에서만 테스트 할 수 있는 정도가 돼겠다. consul member 명령으로 consul 데이터센터의 멤버들을 확인해보자.
root@consul01:/# consul members
Node      Address         Status  Type    Build  Protocol  DC   Segment
consul01  127.0.0.1:8301  alive   server  1.5.3  2         dc1  <all>
지금은 단지 하나의 멤버만 보일 것이다. 멤버 정보로 IP 주소, heath 상태, 데이터센터에서(Datacenter, DC)의 롤을 확인 할 수 있다. --detailed 플래그를 이용하면 추가적인 메타정보를 확인 할 수 있다. HTTP API를 이용해서 consul 서버에 요청을 전달 할 수 있다. consule은 분산환경에서 데이터에 대한 consistent를 보장하는데, HTTP API를 이용하면 consistent가 보장된 정보를 읽을 수 있다.
# curl localhost:8500/v1/catalog/nodes
[
    {
        "ID": "80115d27-e681-da61-1672-c994af0b0f2b",
        "Node": "consul01",
        "Address": "127.0.0.1",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "127.0.0.1",
            "wan": "127.0.0.1"
        },
        "Meta": {
            "consul-network-segment": ""
        },
        "CreateIndex": 9,
        "ModifyIndex": 10
    }
]

DNS 기반 node discovery

HTTP API 외에도 DNS를 이용해서 노드를 검색 할 수 있다.
root@consul01:/# dig @127.0.0.1 -p 8600 consul01.node.consul ANY

; <<>> DiG 9.11.3-1ubuntu1.8-Ubuntu <<>> @127.0.0.1 -p 8600 consul01.node.consul ANY
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 17324
;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;consul01.node.consul.		IN	ANY

;; ANSWER SECTION:
consul01.node.consul.	0	IN	A	127.0.0.1
consul01.node.consul.	0	IN	TXT	"consul-network-segment="

;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Aug 21 13:51:43 UTC 2019
;; MSG SIZE  rcvd: 101

Consul 중지

consul leave 명령으로 중지할 수 있다.
root@consul01:/# consul monitor
2019/08/21 13:24:25 [INFO]  raft: Initial configuration (index=1): [{Suffrage:Voter ID:80115d27-e681-da61-1672-c994af0b0f2b Address:127.0.0.1:8300}]
2019/08/21 13:24:25 [INFO]  raft: Node at 127.0.0.1:8300 [Follower] entering Follower state (Leader: "")
2019/08/21 13:24:25 [INFO] serf: EventMemberJoin: consul01.dc1 127.0.0.1
2019/08/21 13:24:25 [INFO] serf: EventMemberJoin: consul01 127.0.0.1
2019/08/21 13:24:25 [INFO] consul: Adding LAN server consul01 (Addr: tcp/127.0.0.1:8300) (DC: dc1)
2019/08/21 13:24:25 [INFO] consul: Handled member-join event for server "consul01.dc1" in area "wan"
2019/08/21 13:24:25 [WARN] agent/proxy: running as root, will not start managed proxies
2019/08/21 13:24:25 [INFO] agent: Started DNS server 127.0.0.1:8600 (tcp)
2019/08/21 13:24:25 [INFO] agent: Started DNS server 127.0.0.1:8600 (udp)
2019/08/21 13:24:25 [INFO] agent: Started HTTP server on 127.0.0.1:8500 (tcp)
2019/08/21 13:24:25 [INFO] agent: Started gRPC server on 127.0.0.1:8502 (tcp)
2019/08/21 13:24:25 [INFO] agent: started state syncer
2019/08/21 13:24:25 [WARN]  raft: Heartbeat timeout from "" reached, starting election
2019/08/21 13:24:25 [INFO]  raft: Node at 127.0.0.1:8300 [Candidate] entering Candidate state in term 2
2019/08/21 13:24:25 [INFO]  raft: Election won. Tally: 1
2019/08/21 13:24:25 [INFO]  raft: Node at 127.0.0.1:8300 [Leader] entering Leader state
2019/08/21 13:24:25 [INFO] consul: cluster leadership acquired
2019/08/21 13:24:25 [INFO] consul: New leader elected: consul01
2019/08/21 13:24:25 [INFO] connect: initialized primary datacenter CA with provider "consul"
2019/08/21 13:24:25 [INFO] consul: member 'consul01' joined, marking health alive
2019/08/21 13:24:25 [INFO] agent: Synced node info

Service Discovery

Consul의 주요 사용 목적 중 하나는 서비스 디스커버리다. Consul은 다운스트림 서비스(downstream service)가 그들의 업스트림 서비스(downstream service) 종속성을 찾기 위한 IP 주소를 찾는데 사용 할 수 있는 DNS 인터페이스를 제공한다. 서비스 디스커버리 테스트를 위해서 서비스를 등록하기로 했다. 등록 할 서비스는 레일즈(rails)로 만든 웹 애플리케이션 서버다.

먼저 Consul 설정 파일을 저장할 디렉토리 /etc/consul.d 를 만들었다. 여기에 레일즈 웹 애플리케이션 서비스를 위한 설정파일을 만들었다.
root@consul01:/consul.d# cat web.json 
{"service":
  {"name": "web",
   "tags": ["rails"],
   "port": 80
  }
}

설정파일 디렉토리를 읽어서 로딩하도록 consul 에이전트를 다시 실행한다.
# consul agent -dev -enable-script-checks -config-dir=/consul.d/
==> Starting Consul agent...
           Version: 'v1.5.3'
           Node ID: '1fce03c0-f364-20b7-f837-3db917e75d6b'
         Node name: 'consul01'
        Datacenter: 'dc1' (Segment: '<all>')
            Server: true (Bootstrap: false)
       Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
      Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
           Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false

==> Log data will now stream in as it occurs:

DNS 인터페이스를 이용해서 서비스를 찾아보자.
# dig @127.0.0.1 -p 8600 web.service.consul    

; <<>> DiG 9.11.3-1ubuntu1.8-Ubuntu <<>> @127.0.0.1 -p 8600 web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44772
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul.		IN	A

;; ANSWER SECTION:
web.service.consul.	0	IN	A	127.0.0.1

;; ADDITIONAL SECTION:
web.service.consul.	0	IN	TXT	"consul-network-segment="

;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Aug 21 14:53:29 UTC 2019
;; MSG SIZE  rcvd: 99
A 레코드에서 IP 주소를 확인 할 수 있다.

앞서 설정파일에 포트번호를 넣었던 것을 기억 할 것이다. 포트 정보는 DNS SRV 레코드에 저장된다.
# dig @127.0.0.1 -p 8600 web.service.consul SRV

; <<>> DiG 9.11.3-1ubuntu1.8-Ubuntu <<>> @127.0.0.1 -p 8600 web.service.consul SRV
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58287
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 3
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;web.service.consul.		IN	SRV

;; ANSWER SECTION:
web.service.consul.	0	IN	SRV	1 1 80 consul01.node.dc1.consul.

;; ADDITIONAL SECTION:
consul01.node.dc1.consul. 0	IN	A	127.0.0.1
consul01.node.dc1.consul. 0	IN	TXT	"consul-network-segment="

;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Aug 21 15:05:06 UTC 2019
;; MSG SIZE  rcvd: 143
SRV 레코드에 "1 1 80 consul01.node.dc1.consul."를 확인 할 수 있을 것이다. SRV 레코드는 서비스의 위치(호스트이름과 포트번호)를 저장하기 위해서 사용하는 레코드다. A 레코드와 SRV 레코드를 이용해서 web.serviceconsul 서비스가 127.0.0.1:80에서 서비스 한다는 것을 알 수 있다. SRV에 대한 자세한 내용은 DNS SRV 레코드문서를 참고하자.

설정파일의 TAG는 태그이름 기반으로 서비스를 찾을 수 있게 해준다. DNS 질의 형식은 TAG.NAME.service.consul이다. rails.web.service.consul 로 질의해보자.
# dig @127.0.0.1 -p 8600 rails.web.service.consul    

; <<>> DiG 9.11.3-1ubuntu1.8-Ubuntu <<>> @127.0.0.1 -p 8600 rails.web.service.consul
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6954
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
;; WARNING: recursion requested but not available

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;rails.web.service.consul.	IN	A

;; ANSWER SECTION:
rails.web.service.consul. 0	IN	A	127.0.0.1

;; ADDITIONAL SECTION:
rails.web.service.consul. 0	IN	TXT	"consul-network-segment="

;; Query time: 0 msec
;; SERVER: 127.0.0.1#8600(127.0.0.1)
;; WHEN: Wed Aug 21 15:14:40 UTC 2019
;; MSG SIZE  rcvd: 105
인터넷 웹 서비스는 여러 컴포넌트들로 구성이 되므로 태그이름을 잘 정의하면, 서비스를 잘 구조화 할 수 있다. 예컨데 아래와 같은 구성이 가능할 것이다.
  • web.service.consul
  • db.web.service.consul
  • cache.web.service.consul
  • log.web.service.consul