Skip to main content

8 posts tagged with "ansible"

View All Tags

· 9 min read

서비스를 구성하면서 살펴보다 보면 어떤 서비스, 어떤 페이지에 request가 많은지 그 요청들은 어느 곳에서 많이 발생하는지 궁금할 때가 있다. 글로벌을 타겟으로 하는 서비스인 경우 특히 그럴 것이고 혹은 의도치 않은 곳에서 이상한 형태의 요청이 빈번할 경우 차단할지 판단하기 위해 필요할 수도 있다. Google Analytics 같은 서비스의 경우 이러한 요구사항을 간편한 방법으로 어느정도 해소할 수 있지만 어차피 여러가지 분석 용도로 ELK를 사용하고 있으니 packetbeat를 이용해보기로 했다.

Packetbeat?

ELK stack에서 로그 수집용으로 filebeat를 많이 사용하는데 filebeat는 특정 로그파일을 수집해서 logstash나 elasticsearch 등으로 전송하는 역할을 수행한다. Packetbeat는 로그파일 대신 network packet 데이터를 수집해서 전송할 수 있으며, 각종 프로토콜 별로 데이터를 분류해서 수집할 수 있게 되어있다.

테스트용 ELK 구성

우선 packetbeat를 사용해서 어느 지역에서 요청이 들어오는지를 보고 싶은 게 1차 목표였는데 실제 운영중인 ELK를 건드리고 싶지는 않아서 새로운 VM에 Ansible로 다시 구성했다. 미리 만들어둔 5개의 role을 clone해서 아래와 비슷한 형태로 playbook을 만들고 실행하면 된다. ELK가 동시에 여러 종류의 beat에서 전달되는 데이터를 받아야하니 다른 문제가 있는지를 보기 위해 filebeat도 같이 구성했다.

1. Roles

https://github.com/blurblah/ansible-role-elasticsearch

https://github.com/blurblah/ansible-role-logstash

https://github.com/blurblah/ansible-role-kibana

https://github.com/blurblah/ansible-role-docker (Dependency)

https://github.com/blurblah/ansible-role-filebeat

2. Playbook

- hosts: elk_test
become: yes
roles:
- role: elasticsearch
cluster:
name: test-es
- role: logstash
logstash_loglevel: debug
- role: kibana
server_name: test-kibana

- hosts: elk_test
become: yes
roles:
- role: filebeat
logstash_host: '{{ ansible_default_ipv4.address }}'
logstash_port: 5044
enable_kibana_dashboard: true
kibana_host: '{{ ansible_default_ipv4.address }}'
kibana_port: 5601
prospectors:
- { type: syslog, files: ['/var/log/auth.log'] }

Packetbeat 구성

Packetbeat도 역시 Ansible role로 만들어서 ELK 구성과 비슷한 형태로 간단히 완료. Role은 github에서 가져오면 되고 요청을 분석하고 싶은 서버를 대상으로 아래와 같은 형태로 playbook을 만들어서 실행했다.

- hosts: test_proxy
become: yes
roles:
- role: packetbeat
packetbeat_name: test_packetbeat
packetbeat_protocols:
- { type: tls, ports: [443] }
packetbeat_loglevel: debug
out_logstash:
host: '{{ hostvars["elk_test"].ansible_host }}'
port: 5044

내가 분석하고자 했던 서버는 nginx로 되어있는 reverse proxy 였는데 인증서가 적용되어 있었기 때문에 type을 tls로 port는 443 하나만 넣어서 지정했다.

Geoip filter

Packetbeat 까지 구성해서 데이터가 수집되는 것을 확인해보면 (Kibana에서 index pattern 생성까지 한 경우) 요청 ip, url, protocol 등은 표시되지만 위치 정보에 대한 내용은 없다. Logstash의 geoip filter는 수집된 정보 중 ip와 내부 DB에 매핑된 정보를 가지고 geoip라는 field를 생성해주는 역할을 수행한다고 해서 아래처럼 logstash pipeline 경로에 filter를 설정해주었다.

filter {
if [@metadata][beat] == "packetbeat" and [client_ip] and [client_ip] !~ /^10\./ {
geoip {
source => "client_ip"
}
}
}

Filebeat 등 다른 beat들은 geoip filter를 적용하지 않을 것이기 때문에 metadata를 이용해 거르게 했다. Private ip의 경우 위치 정보에 대해서 매핑된 정보가 없기 (알 수도 없고) 때문에 내 환경에서 요청이 발생하는 10.0.0.0/16 대역에 대해서도 적용하지 않기로 했다. Private ip에 대해서 처리를 하게 되면 아래와 같은 오류가 발생한다.

[2018-04-18T04:52:51,124][DEBUG][logstash.filters.geoip   ] IP 10.x.x.x was not found in the database {:event=>#<LogStash::Event:0x65726e3c>}

Filter 설정은 좀 더 살펴보고 더 효율적으로 수정할 필요는 있을 것 같다.

Geoip field 인식 문제

Filter 설정 후 logstash를 restart 하고 나서 kibana 쪽에서 살펴보니 없던 geoip field 들이 새로 만들어지는건 확인이 되는데 아래 그림처럼 warning이 발생하는 걸 볼 수 있었다.

이전에 데이터 들어오는걸 보겠다고 index pattern을 생성했는데 그 시점의 index에는 없던 field 들이기 때문에 warning이 발생하는 것이라서 index pattern을 제거하고 다시 생성해주는 걸로 문제를 해결

geo_point type

Warning이 없어졌으니 kibana의 visualize 메뉴에 가서 map을 추가해 그려볼까 하고 설정을 해보니 아래와 같은 오류가 뜨면서 그려지질 않는다.

무엇이 문제인가 싶어서 index pattern에서 field들의 type을 살펴보니 geo_point type으로 된 field가 보이질 않는다. 이런 부분은 좀 개선이 필요해 보이는데 field 들이 filter에 의해 자동으로 생성되는 것이라 알아서 처리되면 편할 것 같다.

이런 경우 elasticsearch에서 생성하는 index에 대한 변경이 필요한데 이미 존재하는 index를 일괄 변경하는 방법도 있을 것 같고 새로 생성되는 index에 대해서 적용할 template을 추가하는 방법도 있을 수 있다. 나의 경우엔 이미 만들어진 index와 데이터가 거의 없는 상태이기 때문에 존재하는 index는 모두 제거하고 template을 추가해서 이후 생성되는 index에 대해서만 field type이 변경되도록 했다. Template을 적용하기 위해서는 아래와 같은 json 파일을 생성해서 elasticsearch에 밀어넣어야 한다. (이전에 index, index pattern 제거하고 진행함)

{
"template": "packetbeat-*",
"mappings": {
"_default_": {
"properties": {
"geoip": {
"properties": {
"location": {"type": "geo_point"}
}
}
}
}
}
}

Json 생성 후 elasticsearch에게 보내주자.

curl -XPUT -H "Content-Type: application/json" localhost:9200/_template/packetbeat_geoip -d@packetbeat_geoip.json

다시 packetbeat 로부터 데이터가 들어오기 시작하면 template이 적용된 index가 생성되고 kibana에서 index pattern을 다시 생성해 확인해보면 아래처럼 geoip.location field가 geo_point type으로 되어있는 것을 확인할 수 있다.

이제 지도를 그리자

오류가 발생하던 부분을 해결했으니 다시 visualize 메뉴에서 지도를 추가해 설정하면 아래처럼 요청 횟수에 따라 색깔별로 표시되는 걸 볼 수 있다.

VPN에 연결해서 다른 나라로부터의 요청도 제대로 표시되는지 확인해보니 별 문제없이 된다.

 

Packetbeat로부터 들어오는 데이터들을 간단히 살펴보니 사실 지역에 대한 정보 이외에도 쓸만한 내용들이 많다. 요청 url 별로 분석할 때도 유용할 것 같고 protocol type (service) 별로 구분해서 확인하는 것도 쉽게 가능할 것 같다. 그러나 이런 도구들로 분석할만한 서비스를 만들어내는 건 어려운 문제 :)

· 3 min read

Bitbucket의 경우 저장소가 기본적으로 private이고 clone을 하려면 key를 통해 ssh로 진행하거나 http의 경우 password 입력 필요
이 때 사용할 수 있는게 prompt인데, playbook 실행시 prompt에 기술된 문자열이 뜨면서 입력을 받고 입력된 문자열은 name으로 지정한 변수로 저장됨
Built-in으로 보이는 vars_prompt와 함께 사용하고 private 항목을 yes로 하면 입력하는 문자열이 화면에 표시되지 않고 가려짐

- hosts: test_host
vars:
bitbucket_username: test_user
vars_prompt:
- name: bitbucket_password
prompt: Password for bitbucket?
private: yes
tasks:
- name: Clone the repository
git:
repo: 'http://{{ bitbucket_username }}:{{ bitbucket_password }}@bitbucket.org/test_group/test_repo.git'
dest: '/tmp/test_repo'

http로 clone하면서 username과 password를 넘겨주려면 위의 예시처럼 username:password 형태로 colon으로 묶어주면 가능

Slack notification

Ansible에 slack module이 있는데 slack의 webhook을 사용하도록 되어있음
우선 Slack에서 incoming webhook을 하나 추가하거나 존재하는 webhook에 configuration을 추가해야 함
Configuration 추가시 메세지를 전송할 channel을 지정해야 하는데 slack module에도 channel key가 존재함
Slack module의 channel을 지정하면 Slackbot으로 메세지가 들어옴
Module에서 channel을 지정하지 않고 webhook 설정을 그대로 따라가게 두면 webhook 설정대로 지정한 channel에 메세지가 전달됨
이 때, channel 별로 token이 달라지므로 playbook에서는 해당 token만 교체하면서 사용

Docker for the node apps

Node 기반의 application을 dockerization 할 때 아래와 유사한 방식을 사용(Angular.js 환경에서 사용해 봄)

  1. Base image 지정 (node official image)
  2. package.json 복사
  3. npm install
  4. Copy source files (이 때 node_modules는 복사되지 않도록 .dockerignore 파일에 등록)
  5. Expose ports and something...
  6. Run application (CMD를 이용 npm start)

· 3 min read

Ansible에서 다른 파일에 기술된 task를 불러오기 위해서 기존에는 include 만으로 가능
include가 deprecated 되면서 import_tasks or include_tasks를 사용해야 함 (2.4 이후)
import_tasks와 include_tasks 모두 불러올 yml 파일만 지정하면 됨
import_tasks는 static, include_tasks는 dynamic이라고 기술되어 있음
불러올 yml 파일에 variable이 사용된 경우 import_tasks를 이용하면 오류 발생
Variable이 사용된 경우 동적으로 변경되기 때문에 include_tasks를 사용해야만 한다.

Nodebb 구조별 terms

게시판은 category
게시물은 topic
게시물을 구성하는 글(답글? 포함) post
category > topic > post와 같은 구조

Nodebb APIs

Nodebb는 URL이 resource를 표현하고 있기 때문에 api endpoint만 지정하고 나머지 url을 붙이면 가능함
예를 들어, 메인 화면에 진입하면 category list가 보이는데 url이 http://nodebb_host/ 라면, 아래처럼 호출.

# category list sample
curl http://nodebb_host/api

Endpoint가 /api 인데, 다른 화면에서도 표시되는 url들을 endpoint와 조합하면 json 형태의 정보 획득 가능
/api 호출시 나타나는 category list에서 특정 category의 게시물들을 보고 싶다면 해당 category의 slug값을 이용한다.
예를 들어, Announcements category의 slug key의 값이 1/announcements 인 경우 해당 category의 게시물 리스트를 보고싶다면, 아래처럼 조합한다.
API endpoint + /category/ + category slug

# post list of the category numbered 5 as cid
# category slug printed 5/posts got from above command
curl http://nodebb_host/api/category/5/posts

게시물 리스트를 얻어오고 나서 특정 게시물의 정보를 얻고싶다면 게시물 리스트의 item 별로 할당되어 있는 slug를 역시 이용하고 아래처럼 호출.
API endpoint + /topics/ + topic slug

# topic information
# topic slug from above is 10/test-article-09
curl http://nodebb_host/api/topic/10/test-article-09

· 2 min read

when 구문에서 변수는 따옴표를 포함해 이중 중괄호로 묶을 필요가 없음
묶어서 사용할 경우 warning 발생

# Warning
- name: check distribution
fail:
msg: "Not supported {{ ansible_distribution }}"
when: "{{ ansible_distribution }} != 'Ubuntu'"

# Fixed
when: ansible_distribution != 'Ubuntu'

register & set_fact

Playbook에 정의된 variable을 task에서 사용할 때 register로 재정의가 안됨
set_fact module을 사용하는 경우 재정의 가능

Warning in loop

apt module 사용시 with_items로 반복 설치하도록 해두었는데 외부에서 가져온 변수는 사용하지 말라는 warning 발생함(deprecate 예정)
이해가 잘 되지는 않지만 아래처럼 변경했더니 warning 사라짐

# Warning
- name: Install packages
apt: "{{ item }}"
with_items:
- { name: apt-transport-https }
- { name: ca-certificates }
- { name: curl }
- { name: software-properties-common }
- { name: "{{ docker_package }}" }

# Fixed
- name: Install packages
apt: name={{ item }}
with_items:
- apt-transport-https
- ca-certificates
- curl
- software-properties-common
- "{{ docker_package }}"

· One min read

Ansible issues 확인함
그 중에서 undefined variable에 대한 bug 확인
2.3.2에서는 inventory_file이라는 변수에 할당된 값이 없어도 오류 발생하지 않음
2.4.0부터는 undefined variable 오류
실제로 문서에는 magic variables 중에 inventory_file 존재
Ansible 실행 버전을 switch 하기 위한 편리한 방법 필요함

Apply ansible devel branch

devel branch에서 ansible을 적용하기 위한 방법

cd ansible
source hacking/env-setup

코드 수정 후 make를 해서 실행파일들이 bin/ 에 반영되게 만들어야할 것으로 보임

· 2 min read

Dev guide 참조
compile, sanity test 모두 2.6, 2.7, 3.5, 3.6 버전에서 테스트하도록 되어있음
특정 버전 대상으로 테스트하고 싶다면 --python VERSION option 사용

Multiple python version

pyenv를 설치하면 여러개의 python version을 설치하고 관리할 수 있음
아래는 간단한 사용법

# list to install
pyenv install --list
# install a specific version
pyenv install 3.6.3
# show installed versions
pyenv versions
# change to a specific version
pyenv shell 3.6.3

shell subcommand 실행시 오류가 발생하기 때문에 아래와 같은 구문을 .profile에 넣어줘야 함

export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"

if command -v pyenv 1>/dev/null 2>&1; then
eval "$(pyenv init -)"
fi

pyenv로 특정 python version 설치시 ssl 부분에서 오류가 발생한다면 아래처럼 설치
Common build problem 참조

CFLAGS="-I$(brew --prefix openssl)/include" \
LDFLAGS="-L$(brew --prefix openssl)/lib" \
pyenv install -v 3.4.3

· 8 min read

LB 이중화에 대해서 정리하는 것을 미루고 있다가 아예 이중화 구축을 자동화하는 것까지 해보기로 했다. 지난번에는 HAProxy를 하나만 두었었는데 다른 VM (192.168.100.54)에 동일한 설정으로 HAProxy를 올리고 VRRP를 위해 keepalived를 사용했고 keepalived 설치에 대해서는 '믿고 보는' Digital Ocean의 문서를 참조했음. 아래의 그림처럼 만들어봤다.

(두 개의 VM에 공통으로 설정할 내용이 있고 각각 다르게 해야 하는 부분이 있어서 아래 소제목에 공통으로 적용해야 하는 내용들만 별도 표기함)

 

1. haproxy_check 계정 수정

지난번에는 하나의 HAProxy에서 로드밸런싱을 했기 때문에 MySQL에 연결되는지 확인할 계정 (haproxy_check)을 MySQL에 추가할 때 host로 192.168.100.53을 지정했는데 192.168.100.54에서도 접근해야 하므로 미리 다른 host에 대한 계정을 추가하거나 host를 192.168.100.0/24 255.255.255.0 수정하는 등의 작업을 해두어야 한다.

 

2. keepalived 설치 (공통)

keepalived는 최신 버전(현재 1.3.4)을 다운받아서 빌드해 설치했다. 빌드에 필요한 package 들을 사전에 설치해 두어야 한다.

sudo apt-get install build-essential libssl-dev

아래 경로로 최신 버전을 다운받아 압축을 풀고 아래의 명령으로 빌드해서 설치하면 완료된다.

http://keepalived.org/software/keepalived-1.3.4.tar.gz

./configure
make
sudo make install

 

3. keepalived upstart script (공통)

빌드해서 설치한 경우 ubuntu에 service로 등록되어있지 않기 때문에 upstart script를 만들어줘야만 한다. /etc/init/keepalived.conf 파일을 생성해서 아래의 내용으로 채워준다. 어떤 경우에 시작할지, 서비스 시작은 어떻게 할지 등을 결정하는 내용이다.

description "load-balancing and high-availability service"

start on runlevel [2345]
stop on runlevel [!2345]

respawn

exec /usr/local/sbin/keepalived --dont-fork

 

4. keepalived 설정

이제 중요한 keepalived 설정을 할 순서인데 무엇을 어떻게 감시할 것인지 failover시 backup instance가 인계하거나 master가 가지고 있을 VIP 설정을 해야 한다. Master 역할을 할 첫번째 LB (지난번에 설치한)가 있는 VM에 가서 우선은 /etc/keepalived 디렉토리가 없으므로 생성해둔다. 그리고 /etc/keepalived/keepalived.conf 파일을 아래와 같은 형태로 만든다.

vrrp_script chk_haproxy {
script "pidof haproxy"
interval 2
}

vrrp_instance VI {
state MASTER
interface eth0
virtual_router_id 100
priority 200

virtual_ipaddress {
192.168.100.60
}

track_script {
chk_haproxy
}
}

vrrp_script에는 감시할 내용을 기술하면 되는데 HAProxy가 제대로 떠있는지를 확인하면 되기 때문에 단순하게 pidof를 이용해 pid만 확인하기로 한다. 상황에 따라 다른 방식으로 기술될 수도 있을 것 같고 다양한 방법이 있을 것 같다. vrrp_instance는 VRRP에 대한 설정이라고 보면 되는데 keepalived를 어떻게 동작시킬 것인지에 대한 내용. Master로 사용할 내용이므로 state는 MASTER, eth0로 통신하니 interface는 eth0로 지정하고 virtual_router_id는 임의로 100을 지정했다. virtual_router_id는 다른 keepalived와 중복되지만 않으면 되니 임의의 수를 입력한다. priority는 여러개의 keepalived가 있는 상태에서 failover를 할 때 어떤 instance가 인계할 것인지를 결정하는 수치인데 높은 값일수록 인계할 가능성이 높다. virtual_ipaddress에는 보유하고 있는 VIP를 지정해준다.

Backup instance로 동작할 VM (192.168.100.54)에도 동일한 경로에 아래와 같은 형태로 설정 파일을 만들어준다.

vrrp_script chk_haproxy {
script "pidof haproxy"
interval 2
}

vrrp_instance VI {
state BACKUP
interface eth0
virtual_router_id 200
priority 100

virtual_ipaddress {
192.168.100.60
}

track_script {
chk_haproxy
}
}

문제가 발생했는지 여부를 검사할 내용이나 VIP 설정 등은 동일한데 state와 virtual_router_id, priority 가 다르다. state는 BACKUP으로 지정하고 앞서 기술했듯이 virtual_router_id는 master와 다르게 지정하고 priority는 election에 관여하는 내용이니 master보다 낮은 값을 입력해준다.

 

5. 서비스 시작과 확인

Master, backup instance 모두에서 keepalived service를 시작하고 문제없이 시작되었다면 예전과 비슷한 방식으로 이중화가 제대로 되는지 확인해본다.

mysql -h 192.168.100.60 -u tester -p -e "show variables like 'server_id'"

지난번에는 HAProxy 하나에서 round robin으로 로드밸런싱을 했기 때문에 HAProxy가 있는 VM의 ip (192.168.100.53)로 연결해서 server_id가 변경되는지 확인했었는데 이번에는 HAProxy도 이중화를 했기 때문에 위에서 설정한 VIP (192.168.100.60)를 통해 server_id를 확인할 수 있다. Master가 정상동작중인 상태이므로 master instance가 VIP에 연결된 상태일텐데 이 상태에서 이중화가 제대로 되고 있는지 확인하려면 master의 haproxy service를 중단시키고 위와 동일한 command로 server_id를 확인해보면 되겠다. VRRP에 의해 backup instance가 인계하는 내용은 syslog에서도 확인 가능하다.

 

6. 자동화

Ansible을 사용해서 HAProxy로 mysql을 로드밸런싱 하는 내용은 만들어둔 상태였는데 keepalived 설치와 설정하는 내용도 추가해봤다. 또 여러개의 instance를 한 번에 설정할 수 있도록 수정했는데 실제 OS만 올려져 있는 VM 몇 개를 두고 playbook 실행만으로 귀찮은 반복작업이 줄어들어 너무 편해졌음. 상세 내용은 아래 저장소 참조.

https://github.com/blurblah/ansible-role-mysql-lb

· 6 min read

지난번에 이어서 구축한 MySQL slave 2개를 로드밸런싱 해보기로 했다. 로드밸런서로 가용한게 몇가지 있지만 그 중 HAProxy를 이용해서 아래 그림과 같은 모양으로 만들 생각이다. LB의 IP는 192.168.100.53 이고 로드밸런싱 대상은 각각 192.168.100.51, 192.168.100.50

 

1. Status check를 위한 user 추가

HAProxy가 mysql 상태 확인을 위해 접근하는 사용자 계정이 추가되어야 한다. 다른 방식으로 status check 하는게 가능하다면 사용자 계정이 필요없을 수도 있으나 option mysql-check을 사용한다면 mysql에 사용자가 추가되어 있어야 한다. Master (위 그림에서는 192.168.100.52)에서 아래의 쿼리로 사용자를 추가한다.

INSERT INTO mysql.user(host, user) VALUES('192.168.100.53', 'haproxy_check');
FLUSH PRIVILEGES;

나중에는 변경되겠지만 우선 haproxy가 설치될 vm의 ip (192.168.100.53)와 임의의 사용자 계정 (여기서는 haproxy_check)을 넣어두었다. Slave에만 접근할 거지만 slave에 직접 insert를 하면 데이터들이 달라지게 되므로 master에만 집어넣는다. 그렇게해도 이미 검토한대로 slave에 그대로 replication 될 것이다.

 

2. HAProxy 설치와 init script 등록

LB로 사용할 haproxy를 192.168.100.53에 설치한다. Ubuntu라면 아래의 command를 실행했을 때 1.4 버전이 설치된다.

sudo apt-get install haproxy

설치 후에 service 시작을 하면 반응이 없는데 init script로 등록해서 관리할거라면 /etc/default/haproxy 파일을 열어서 수정해줘야 한다. ENABLED=0으로 되어있는 부분의 값을 1로 바꿔주자.

 

3. HAProxy 설정과 확인

HAProxy의 설정파일인 /etc/haproxy/haproxy.cfg가 기본값으로 그대로 있기 때문에 /etc/default/haproxy 파일을 수정했더라도 시작할 수 없다. (listen 항목이 없음) /etc/haproxy/haproxy.cfg 파일 하단에 아래와 같은 형태로 상태 확인할 mysql 정보와 확인 방법 등을 넣어준다.

listen mysql-slaves
bind *:3306
mode tcp
option mysql-check user haproxy_check
balance roundrobin
server mysql1 192.168.100.51:3306 check
server mysql2 192.168.100.50:3306 check

tcp 모드로 haproxy_check라는 계정을 이용해 mysql1, mysql2의 상태를 확인하고 round robin 방식으로 로드밸런싱을 하는데 haproxy는 3306 포트로 바인딩한다는 내용이 되겠다. 이런 형태로 haproxy 설정파일을 수정한 후에 기본으로 들어있는 defaults의 option httplog, option dontlognull 항목은 지워준다. 여기서는 사용하지 않는 내용이라 포함된 상태로 서비스를 시작할 경우 warning 메세지가 뜨기 때문. 여기까지 진행하고 아래의 command로 서비스를 시작하면 끝이다. 간단하다.

sudo service haproxy start

이제 로드밸런싱이 되는지를 확인할 차례인데 mysql master, slave 모두 고유의 server_id를 가지고 있으므로 간단히 server_id를 쿼리해보면 아래의 그림처럼 매번 2와 3으로 번갈아 바뀌는 걸 볼 수 있다.

여기에 weight 같은 것을 주면 slave 각각에 연결되는 빈도가 달라지게 되겠지만 여기까지 확인하고, slave 하나가 죽었을 경우를 가정해보기로 했다. Slave #2에 접근해서 mysql service를 중지시키고나서 다시 쿼리를 날리면 아래 그림처럼 한쪽으로만 연결되는 걸 볼 수 있다. (HAProxy가 상태 검사하는 내용은 haproxy 로그나 syslog에서도 확인 가능)

 

4. 자동화

계속 사용해보고 있는 ansible로 위 과정을 자동화해보기로 했다. Github에 위의 내용으로 간단히 role을 정의한 내용을 넣어두었는데 playbook 하위에 roles를 디렉토리를 만들고 적당한 이름으로 clone한 후에 playbook에서 호출하도록 하면 되겠다. Playbook을 만들 때에는 아래처럼 mysql_instances variable을 정의하면서 slave 정보(name, host, port)를 넣어줘야 한다.

---
- name: Install and configure haproxy for load balancers
hosts: maasnode04
become: yes
roles:
- common
- role: mysql-lb
mysql_instances:
- { name: mysql1, host: 192.168.100.51, port: 3306 }
- { name: mysql2, host: 192.168.100.50, port: 3306 }

Role을 만들기 전에 ansible module 중에 haproxy module이 있어서 검토를 해봤는데 이미 존재하는 haproxy의 backend에 특정 host를 enable, disable 하는 정도의 기능만 수행하도록 되어있는 것 같았다. listen 항목을 추가하거나 초기 설정, 또 상세한 설정은 안되는 걸로 보여서 그냥 template을 만들어서 입력되는 variable로 내용을 채우도록 구성.