2017년 8월 1일 화요일

Consul에 대해서

Consul에 대해서 대략적으로 설명하고자 한다. 
HashiCorp에서 만든 OpenSource로 기존의 Zookeeper의 역활을 하는 Application으로 보면 된다. 

아래는 해당 consul 홈페이지에 기제된 내용으로 기본적으로 4가지으로 항목으로 구분하여 설명하고 있다.

Service Discovery/Failure Detection/Multi Datacenter/KV Storage

Service Discovery
DNS 또는 HTTP 인터페이스를 통해 서비스를 스스로 등록하고 다른 서비스를 발견 할 수 있도록합니다. SaaS(Software as a Service) 제공되는 외부 서비스에 또한 등록할 수 있다 

Failure Detection
각 노드에 대해서 health check를 통하여 서비스 정상 여부를 체크하고 Service Discovery를 하거나 서비스가 가능한 노드로 routing을 제공한다. 

Multi Datacenter
여러개의 Datacenter를 구축하고 Datacenter간 WAN GOSSIP 프로토콜을 이용하여 정보를 조회 할 수 있도록 기능 제공한다. 

KV Storage
Key/Value를 저장할 수 있는 저장소를 제공하여 서비스 별 Config 정보나 특정 데이타를 저장하여 instance간 config 정보 수정이나 데이타 공유를 할 수 있도록 지원한다. 


아래의 이미지는 Stackshare에서 consul vs Zookeeper vs Eureka를 비교한 화면이다. 
선호도나 사용 횟수면에서 Consul이 우월 하다는 것이 보여집니다. 

저도 stackshare의 데이타를 신용하여 해당 Consul을 이용하여 Monitoring 시스템을 구현하는 것으로 결정하였습니다. 



공식 사이트https://www.consul.io

Consul Architecture
consul 공식 사이트에 등록된 Architecture 이미지이다.

Consul의 Check 기능을 이용하여 해당 서버의 Resource 현황과 health 체크를 할 수 있도록 Consul 서버 및 클라이언트를 설치하고 설정을 진행 하도록 하겠습니다. 

Consul Agent 서버 노드 설치
 - Master 서버에 consul web-ui를 같이 설치


압축 해제

/data/consul
/data/consul-ui  (디렉토리 이름 주의)
두개의 디렉토리를 생성 후  web_ui관련 파일을 다운받아 압축을 푼다.
  
  cd  /data/consul-ui
  unzip  consul_0.8.5_web_ui.zip

  consul keygen
해시값을 생성성
이 해시값으로 모든 서버의 환경설정(encrypt) 값을 설정해야하니 꼭 저장해둘것!

  vim  /etc/consul.d/server/config.json
config 파일 생성

 config.json 내용은 아래와 같이 설정한다.

{
  "bootstrap": true,
  "server": true,
  "datacenter": "test",
  "ui_dir": "/data/consul-ui",  // <- 디렉토리 경로 확인
  "data_dir": "/data/consul",
  "encrypt": "위에서얻은해시값",
  "addresses": {
      "http": "외부망IP"
  }, 
  "bind_addr": "내부망 IP",
  "advertise_addr": "외부망 IP",
}

consul 서버 시작
nohup consul agent --server -config-dir=/etc/consul.d/server -bind=서버IP &

config-dir 옵션으로 config 파일 위치 설정 

- 위와같이 설정하고 위의 명령어로 실행한뒤에 localhost:8500으로 접속하면 404에러가 남
  이때 sudo reboot를하면 위에서 만들어둔 /data/consul-ui 디렉토리가 삭제되어 있는데
  디렉토리를 다시 만들고 1번 명령어로 실행하면 정상 작동함



Consul Agent 설치 방법 

압축 해제한 consul 아래의 경로로 복사/이동 시킨다.
  cp  ./consul   /usr/local/bin
  mv  ./consul  /usr/bin
설치 확인을 위해 consul version 명령 실행

- 아래의 디렉토리를 만든다.
  /data/consul

- 아래의 경로에 config.json 파일을 만든다
  vim  /etc/consul.d/agent/config.json

 config.json 내용은 아래와 같이 설정한다.

{
  "server": false,
  "datacenter": "test",
  "data_dir": "/data/consul",
  "encrypt": "7mASsjRU1X0ORmQyymo9Hw==",
  "start_join": ["192.168.1.155"]   // <- Array형식으로 입력해야됨
}

- Agent 실행
  (1) consul agent  -config-dir=/etc/consul.d/agent
  (2) nohup consul agent  -config-dir=/etc/consul.d/agent -bind=AgentIP &
  정상적으로 실행되었다면 서버쪽 웹화면의 [node]탭에 Agent가 추가된다.



# consul을 서버(개발) 모드로 실행하는 방법
  consul  agent  -dev

# 터미널을 하나 떠 띄워서 아래와 같은 명령어를 치면 Cluster Members를 볼 수 있다
  consul members  [-detailed]


# consul에서 사용하는 포트 번호

  SERVER RPC : 8300/tcp
  Serf LAN : 8301/tcp&udp
  Serf WAN : 8302/tcp&udp
  CLI RPC : 8400/tcp
  HTTP API : 8500/tcp (UI접속용)
  DNS Interface : 8600/tcp&udp

 ufw  allow  8300/tcp 
 ufw  allow  8301/tcp 
 ufw  allow  8500/tcp 
 ufw  allow  8600/tcp


# # 서비스 정의 (Defining a Service)
  서비스는 json을 통해 정의하는 방법과 HTTP API를 이용해 정의하는 두가지 방법이 있다.

- /etc/consul.d/server 폴더를 만든다
- 다음의 명령으로 [80번 포트를 사용하고, rails라는 태그를 가진 "web"이라는 서비스]를 만든다.

  echo '{"service": {"name": "web", "tags": ["rails"], "port": 80}}' | sudo tee /etc/consul.d/server/web.json

- 서비스를 등록하기 위해 다음의 명령어로 Consul을 재시작한다.
  consul agent -dev -config-dir=/etc/consul.d/server
  "Synced service 'web'"라는 출력을 통해 웹 서비스를 "동기화"한 것을 알 수 있다.
  이는 에이전트가 위에서 만든 json 파일에서 서비스 정의를 로드했으며
  서비스 카탈로그에 성공적으로 등록했음을 의미한다.
  추가 서비스 역시 위와같은 방식으로 만들 수 있다.

- agent가 시작되고 서비스가 동기화되면 DNS HTTP API를 사용해 서비스를 쿼리할 수 있다.

# DNS API를 이용한 쿼리 방법

- dig  @127.0.0.1  -p 8600 web.service.consul
- dig  @127.0.0.1  -p 8600 web.service.consul  SRV

- DNS API를 이용해 태그로 필터링해서 서비스를 요청하는 방법
  dig  @127.0.0.1  -p 8600 rails.web.service.consul

# HTTP API를 이용한 쿼리 방법

   주어진 서비스를 호스팅하는 모든 노드를 출력한다

[
    {
        "ID": "15e1a433-8f0a-2996-cfdd-ed788d9c6a83",
        "Node": "ef06bc916151",
        "Address": "127.0.0.1",
        "Datacenter": "dc1",
        "TaggedAddresses": {
            "lan": "127.0.0.1",
            "wan": "127.0.0.1"
        },
        "NodeMeta": {},
        "ServiceID": "web",
        "ServiceName": "web",
        "ServiceTags": [
            "rails"
        ],
        "ServiceAddress": "",
        "ServicePort": 80,
        "ServiceEnableTagOverride": false,
        "CreateIndex": 6,
        "ModifyIndex": 6
    }
]

   노드 중에 healthy한것만 출력 (문제없이 작동하는것만 출력?)   





----------------------------------------------------------------------------------

# AWS에서 설치하는 방법

---------------------------------------------------------------------------------

### Health HTTP Request ===================================


# List Checks for Node
  node별로 모든 Check를 요청한다.
      "Client" 노드의 모든 Check json 형식으로 출력한다.
# List Checks for Service
  Service별로 모든 Check를 요청한다.
# List Nodes for Service
  지정한 서비스에 대한 결과 출력
  http://192.168.1.155:8500/v1/health/service/consul
# List Checks in State
 모든 Server/Agent에 대해 상태(state) passing인것을 출력

### SERVICE ==========================================


서비스는 아래와 같은 json 형식으로 정의할 수 있다.
agent 실행시 -config-file의 옵션으로 지정하거나 -config-dir 명령줄 옵션으로 
서비스 파일의 경로를설정해서 읽어들일 수 있다.
HTTP API를 사용해서 동적으로 등록 가능하다.

# 개별 서비스 설정 예제
{
  "service": {
    "name": "redis",
    "tags": ["primary"],
    "address": "",
    "port": 8000,
    "enableTagOverride": false,
    "checks": [
      {
        "script": "/usr/local/bin/check_redis.py",
        "interval": "10s"
      }
    ]
  }
}
... or ...
{  
   "service":{  
      "name":"web",
      "tags":[ "rails" ],
      "port":80
   }
}

# 멀티 서비스 설정 예제
{
  "services": [              // services처럼 s가 붙고, Array형식([])이다.
    {
      "id": "red0",
      "name": "redis",
      "tags": [
        "primary"
      ],
      "address": "",
      "port": 6000,
      "checks": [
        {
          "script": "/bin/check_redis -p 6000",
          "interval": "5s",
          "ttl": "20s"
        }
      ]
    },
    {
      "id": "red1",
      "name": "redis",
      "tags": [
        "delayed",
        "secondary"
      ],
      "address": "",
      "port": 7000,
      "checks": [
        {
          "script": "/bin/check_redis -p 7000",
          "interval": "30s",
          "ttl": "60s"
        }
      ]
    },
    ...
  ]
}

name은 필수고, 이외의 값은 선택적으로 사용 가능하다.
id는 지정하지 않을경우 name값으로 설정되지만, 노드마다 고유값을 가져야하기 때문에
가급적 고유한 이름으로 직접 지정해주는게 좋다.
tags는 노드나 서비스에서 필터링 조건으로 사용된다.
서비스마다 IP port번호를 지정할 수 있는데, address/port에서 지정해주면되지만,
기본적으로 agent ip port번호가 지정되기 때문에 별도로 지정할 필요는 없다.
check/checks에 가능한 속성은 script, HTTP, TCP, TTL이고, script, http, tcp 속성이
사용될경우 꼭 interval 속성을 사용해 시간간격을 설정해줘야한다. (10s = 10)
ttl ttl만 제공하면된다.
check/checks 이름은 service:<service-id> 형식으로 자동 생성된다
enableTagoverride는 이 서비스의 anti-entropy기능을 비활성화 시키기 위해 선택적으로 사용한다.


CHECK 관련


노드들에 대한 확인이 필요한 항목을 정의해서 주기적으로 체크를 진행할 수 있다. 
서비스처럼 json형식으로 설정하거나 HTTP API를 이용해 등록할 수 있다.

체크는 크게 5가지 형식이 있다.

1)script + Interval 
  스크립트 파일을 읽어서 Interval로 지정한 시간마다 실행시켜준다
  스크립트 파일 크기는 최대 4k이고, 그 이상은 잘린다
  스크립트 파일의 최대 실행 시간은 기본값이 30초이고, 별도의 속성값으로 증감이 가능하다.

{
  "check": {
    "id": "mem-util",
    "name": "Memory utilization",
    "script": "/usr/local/bin/check_mem.py",
    "interval": "10s",
    "timeout": "1s"
  }
}


2)HTTP + Interval
  Interval로 지정한 시간마다 HTTP GET 요청을한다
  HTTP 응답 코드가 2XX일때는 통과로 처리되고, 429(너무 많은 요청)는 경고
  그 외에는 모두 실패로 처리된다
  HTTP 요청시 TIMEOUT 10초이고, 별도의 속성값으로 증감이 가능하다
  HTTP 응답 코드는 최대 4K만 받고, 나머지는 잘린다
  SSL 검사시 SSL인증서를 설치하고, tls_skip_verify : true로 설정한다

{
  "check": {
    "id": "api",
    "name": "HTTP API on port 5000",
    "tls_skip_verify": false,
    "method": "POST",
    "header": {"x-foo":["bar", "baz"]},
    "interval": "10s",
    "timeout": "1s"
  }
}

3)TCP + Interval
  Interval로 지정한 시간마다 지정한 IP/Port로 연결을 시도합니다
  호스트 이름이 없을경우 기본값으로 "localhost"가 설정됩니다
  연결 시도는 IPv4 IPv6(가능할경우) 둘 다 수행되고, 하나라도 성공하면 
  성공을 리턴하고, 실패할 경우, 응답 코드를 확인해야한다.
  이 방식의 timeout값은 10초이고, 별도의 속성값으로 증감이 가능하다

{
  "check": {
    "id": "ssh",
    "name": "SSH TCP on port 22",
    "tcp": "localhost:22",
    "interval": "10s",
    "timeout": "1s"
  }
}

4)TTL (Time to Live)
  Agent의 상태를 HTTP를 통해 주기적으로 갱신하면서 상태를 체크한다

{
  "check": {
    "id": "web-app",
    "name": "Web App Status",
    "notes": "Web app does a curl internally every 10 seconds",
    "ttl": "30s"
  }
}

5)Docker + Interval
  이 검사는 Docker 컨테이너 내에 패키지 된 외부 응용 프로그램을 호출하는 
  방법에 따라 달라집니다
  애플리케이션은 Docker Exec API를 통해 실행중인 컨테이너 내에서 트리거됩니다
  Consul 에이전트 사용자는 Docker HTTP API 또는 Unix 소켓에 액세스 할 수 
  있어야합니다. Consul $ DOCKER_HOST를 사용하여 Docker API 끝점을 결정합니다.
  응용 프로그램이 실행되고 컨테이너 내부에서 실행중인 서비스의 상태 검사를 수행 
  한 다음 적절한 종료 코드로 종료해야합니다
  점검은 호출 간격과 쌍을 이루어야합니다
  검사를 수행해야하는 쉘은 구성 가능하며 동일한 호스트에서 다른 쉘을 가진 
  컨테이너를 실행할 수 있습니다
  Docker에 대한 응답 결과는 최대 4K이며, 이보다 클 경우 잘립니다.

{
"check": {
    "id": "mem-util",
    "name": "Memory utilization",
    "docker_container_id": "f972c95ebf0e",
    "shell": "/bin/bash",
    "script": "/usr/local/bin/check_mem.py",
    "interval": "10s"
  }
}


# 체크 관련 주의사항
- name은 필수이며, 다른 속성들은 선택적으로 필요한것만 사용 가능하다.
- id 속성값이 없을경우 name으로 자동 설정되지만, id 충돌을 방지하기 위해 직접
  id 속성값을 설정해주는게 좋다.
- notes 속성값은 사용자를 위한 설명이다
- script, TCP, Docker, HTTP 속성을 사용할때 interval 속성값은 필수로 설정해줘야하고,
  시간 설정은 10진수를 이용해 아래와 같은 방식으로 설정 가능하다.
  "300ms", "-1.5h", "2h45m15s"
- deregister_critical_service_after 속성값은 특정 "체크"가 이 속성으로 지정된
  시간을 초과할 경우 자동으로 등록을 취소시키게된다
  기본값은 1분이지만, 30초마다 실행되기 때문에 최대 130정도의 텀이 생길 수 있다
- "체크"를 설정하기 위해 -config-file 옵션으로 설정하거나, 에이전트 실행시
  -config-dir로 설정할 수 있다.
- 새로운 "체크"가 등록되면 기본적으로 "critical"로 설정되는데, 이것은 서비스 풀이
  정상이 되기전에 서비스풀에 등록되는것을 막기 위한것이다
  , 경우에 따라 "체크"의 초기 status값을 직접 설정하는게 좋을 수 있다.

{
  "check": {
    "id": "mem",
    "script": "/bin/check_mem",
    "interval": "10s",
    "status": "passing"     // <- 초기값 설정
  }
}
  
- 멀티 체크
{
  "checks": [            // <- checks처럼 s가 붙고, Array 형식([])이다
    {
      "id": "chk1",
      "name": "mem",
      "script": "/bin/check_mem",
      "interval": "5s"
    },
    {
      "id": "chk2",
      "name": "/health",
      "http": "http://localhost:5000/health",
      "interval": "15s"
    },
    {
      "id": "chk3",
      "name": "cpu",
      "script": "/bin/check_cpu",
      "interval": "10s"
    },
    ...
  ]

}

댓글 없음:

댓글 쓰기