Skip to main content

· 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로 내용을 채우도록 구성.

· 5 min read

지난 글에 이어서 이번엔 약간 다른 내용을 정리.

 

1. Slave 추가

이전 그림을 다시 꺼내보면 slave는 2개인데 지난번에는 slave를 하나만 만들어두고 replication 하도록 해두었었다.

Slave를 추가하려면 어떻게 해야할까? 처음 slave#1을 구성할 때에는 snapshot 적용을 위해서 master를 일시적으로 중단하거나 lock을 걸고 dump를 떠야했는데 이미 존재하는 slave가 있으니 master는 건드릴 필요는 없다. 아래의 순서대로 진행.

(1) Slave #1의 mysql에 접속 후 아래의 쿼리 실행하고 mysql 중지

STOP SLAVE;

Replication 하고 있던 slave #1에 존재하는 data의 snapshot을 만들기 위해 필요하다.

(2) DB datafile 복사

DB file이 모여있는 디렉토리에 보면 master.info 파일이 있는데 master 접속 정보나 마지막 replication 했던 position 정보가 포함되어 있다. 모든 파일들을 slave #2의 db file 경로로 복사한다.

(3) Slave #1의 mysql을 시작하고 아래의 쿼리 실행

START SLAVE;

(4) Slave #2에서 my.cnf 파일의 server-id 변경

Master의 server-id를 1, slave #1은 2로 했기 때문에 slave #2는 3으로 지정했다.

(5) Slave #2에서 (3)과 동일하게 slave 시작 쿼리를 실행

 

정리하면 snapshot을 slave #1에서 가져오는데 master.info에 position 정보까지 들어있으니 단순히 start slave만 해주면 마지막 position 이후부터 replication을 진행하게 된다. Slave #2가 정상적으로 replication 하는지 여부는 지난 글에 기술했던 방법으로 확인할 수 있다.

 

2. Slave => master 변경을 위한 준비

Master에 문제가 발생한 경우 replication 하고 있던 slave를 master로 변경해야 할 필요가 있을 수 있다. 이 때를 위해 slave #1에 log-slave-updates 옵션을 적용해두었다. 공식문서를 보면 slave가 바이너리 로그를 생성하라고 알려주는 옵션이고 log-bin과 함께 사용해야 한다고 되어있는데 slave가 SQL thread가 실행될 때의 변경사항들을 기록하는 역할을 하도록 한다. 이제 log-slave-updates, log-bin 옵션이 적용된 slave #1은 master이자 동시에 slave로 동작한다.

 

3. 더 생각해 볼 문제들

(1) Master 변경시엔 어떻게?

아직 slave 상단에 로드밸런서를 두는 내용을 적지는 않았지만 slave 두 개를 로드밸런싱 하고 있는 상태에서 master에 문제가 생겨 slave #1을 master로 변경할 경우 어떻게 처리를 할지 고민해 봐야 할 것 같다. 상단에 Read와 CUD 요청을 분기하는 방법에 대해서도 살펴봐야 할 것 같다.

(2) Replication lag

Replication 과정이 지연되는 경우들이 발생하는 것 같은데 master와 slave 사이의 간격이 넓어질수록 좋지 않을 것 같다. 프로덕션 레벨에서 경험할 수 있는 내용이 아닌가 싶기도 하지만 어떤 상황에서 발생할 수 있는지 어떻게 줄일 수 있는지 정도를 알아두면 좋을 것 같다.

(3) 자동화 관점에서

위 그림대로 세 개의 mysql instance 구축은 ansible로 간단하게 가능했는데 replication을 위한 dump나 설정 변경, 또 master 변경시에 조치되어야 하는 내용들을 자동화 할 수 있는지 살펴봐야겠다.

· 8 min read

DBA는 아니지만 나중에 필요할 것 같기도 하고 실무 관점에서 직접 구성해보는게 좋을 것 같다는 생각에 DB 이중화를 해보기로 했다. 연습용 DB를 대상으로 하다보니 이중화 구성이 생각보다 간단했고 실제 운영을 하면서 마주칠 수 있는 복잡한 사례와 트러블슈팅을 통해 얻을 수 있는 노하우가 중요할 것 같다. 구성하고자 하는 최종 형태는 아래 그림과 같은 모습인데

 

우선 DB만 master과 slave로 분리하려면 작업을 크게 네가지 정도로 나눌 수 있을 것 같다.

(설정과 관련된 내용은 MySQL 5.5 공식문서에서 참고함)

 

1. Master / slave 설정 변경

설정파일인 my.cnf에서 공통으로 변경해야 하는 내용은 server-id이다. Replication 구성할 master와 slave 들간의 server-id가 중복되어서는 안된다. 추가로 master에서는 log-bin 설정이 필요한데 바이너리 로그파일의 경로나 파일명을 지정하는 설정이다. 바이너리 로그는 DB에 가해지는 변경사항(CUD) 기록이 별도로 저장되는 파일을 의미하는데 master에서만 생성하도록 하는게 일반적이고 slave는 바이너리 로그를 받아와서 릴레이 로그로 저장하게 된다. Slave 입장에서 replication은 두 개의 thread로 동작되는데 하나는 바이너리 로그를 받아와서 릴레이 로그로 저장만 하는 I/O thread이고, 두번째는 릴레이 로그를 DB에 직접 반영하는 작업을 수행하는 SQL thread 이다. my.cnf 파일에서 추가된 내용은 아래와 같다.

# Master
[mysqld]
server-id = 1
log-bin = mysql-bin

# Slave1
[mysqld]
server-id = 2

바이너리 로그로 mysql-bin을 설정하면서 경로를 지정하지 않았기 때문에 실제 DB 파일이 저장되는 경로 (Ubuntu 14.04의 경우 /var/lib/mysql)에 mysql-bin.00000x 형태의 파일로 저장되게 된다.

 

2. Replication 권한 추가

실제 replication을 수행할 계정이 필요한데 기존에 존재하는 계정을 이용한다면 권한만 추가해도 무방하고 신규 계정을 만들어서 처리해도 되겠다. 나의 경우엔 repl이라는 계정을 생성하기로 했다.

GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.100.0/ 255.255.255.0' IDENTIFIED BY 'test';

MySQL master에 접속해서 위와 같이 입력하면 192.168.100.0/24 대역에서 repl이라는 계정이 접근해 replication을 수행할 수 있다. 이미 있는 계정을 이용한다면 REPLICATION SLAVE 권한만 부여하면 되겠다.

 

3. DB dump / 적용

Replication을 시작하기 전에 master와 slave를 동일한 상태로 만들어줘야 하기 때문에 master의 dump를 떠서 slave에 적용해야 한다. 실제 DB 파일을 그대로 복사하는 방법도 있는데 별도로 dump 파일을 만들기로 했다. 이 때 중요한게 dump 파일을 만든 이후에 master에 변경사항이 생기면 안되기 때문에 변경이 발생하지 않도록 조치해두어야 한다. MySQL 서비스를 중단하는 방법도 있지만 테이블에 lock을 거는 방법도 있길래 시도해봤다.

FLUSH TABLES WITH READ LOCK;

MySQL master에 접속해서 위의 쿼리를 실행하면 lock이 걸리는데 이후에 다른 세션에서 데이터를 변경하고 commit을 수행하면 pending 상태로 유지되기 때문에 실제 DB에는 변경이 발생하지 않는다. (DB 엔진 종류에 따라 다르다는 얘기도 있는데 InnoDB에서는 가능) 위의 쿼리를 실행한 세션을 종료하면 lock이 풀린다고 공식문서에 나와있어서 저 세션은 그대로 둔 상태로 dump를 뜬다. (root 계정을 사용했음)

mysqldump -u root -p --all-databases > dump.db

--master-data 옵션을 사용하면 slave가 연결할 master 정보까지 dump에 반영해서 적용시 자동으로 지정가능하다고는 하는데 직접 master 지정까지 해보기 위해 사용하지는 않았다. 생성된 dump가 어느 지점까지의 데이터인지 알아야하기 때문에 master에서 추가로 아래의 쿼리를 수행한다.

SHOW MASTER STATUS;

이 쿼리를 실행하면 사용중인 바이너리 로그와 마지막 position 값을 얻을 수 있다. (아래 그림 참조)

Slave에서 사용해야 하므로 별도로 기록해두고 lock을 푼다.

UNLOCK TABLES;

이제 master는 동작하던대로 계속 작업을 수행하게 될테니 dump를 slave에 복사하고 dump를 적용하면 세번째 과정은 완료된다.

mysql -u root -p < dump.db

 

4. Master 지정과 replication 시작

Master와 slave가 동일한 상태가 되었으므로 (master에 변경사항이 발생하고 있더라도 어느 지점까지 가져왔는지 이미 기록해두었기 때문에 문제가 없다) slave에서 replication 작업을 수행할 master를 지정하고 replication 작업을 시작하는 일만 남았다. Slave에 접속해서 아래의 쿼리를 수행하면 master가 지정된다.

CHANGE MASTER TO
master_host = '192.168.100.52',
master_user = 'repl',
master_password = 'test',
master_log_file = 'mysql-bin.000001',
master_log_pos = 730;

Master 접근에 대한 기본 정보와 과정3에서 별도로 기록해둔 바이너리 로그 파일을 master_log_file에 position을 master_log_pos 항목에 지정하면 되겠다. 이 과정까지 문제없이 진행되었다면 아래의 쿼리로 replication을 시작할 수 있다.

START SLAVE;

Slave가 정상적으로 실행되었는지 여부는 slave status에서 Slave_IO_Running과 Slave_SQL_Running 항목이 Yes인지 Last_Errno 등의 오류 정보 필드에 유효한 내용이 있는지를 보고 판단할 수 있다.

· 6 min read

오전에 marathon을 열어봤더니 이상한 application 들이 여러 개 올라가 있었다. 별다른 인증절차 없이 사용하고 있었으니 올게 온거다. (왜 이제 오셨나. 항상 이런 건 뭔가 처리할 작업이 많을 때 찾아오신다. 신기하다.) 다행히 실제 동작하는 application은 아니었는지 모두 deploying 혹은 waiting 상태로 대기하고 있어서 서둘러 모두 제거했다. Marathon 사용하던 초기에(0.8? 0.9? 버전) 보안 관련 옵션을 문서에서 찾지 못해 습관처럼 그대로 사용하고 있었는데 또 다시 이런 일을 겪을 수는 없으니 뭔가 조치를 취해야했다.

 

1. http_credentials

찾아보니 문서가 있다. Marathon 실행시 http_credentials 옵션을 주는 방법. 링크된 문서에는 SSL 적용 방법도 기술되어 있지만 간단히 사용자 인증만 추가해보기로 했다. Marathon 실행파일을 열어보면 확인할 수 있지만 대부분의 옵션은 /etc/marathon/conf 경로(Ubuntu 기준)에 옵션 이름으로 파일명을 만들고 파일 안에 옵션값을 집어넣으면 실행시에 파일을 읽어서 옵션으로 추가하도록 되어있다. 그러므로 /etc/marathon/conf/http_credentials 라는 파일을 만들고 파일 안에 id:password 형태로 원하는 내용을 집어넣고 marathon을 restart 하면 끝.

옵션이 제대로 지정되었는지 프로세스를 확인해보고 marathon UI에 접근하면 id, password를 묻는 팝업이 뜬다. Marathon restart만으로 제대로 된다고 생각했는데 이후에 더 살펴보니 mesos-master도 restart 해줘야 하는게 아닌가 싶다. (mesos master, marathon 사이의 통신에 문제가 생김)

 

2. REST API 호출은 어떻게?

간단한 실행옵션으로 UI 접근시 사용자 인증은 가능해졌는데 이걸로 모든게 끝난건 아니다. http_credentials를 적용하면서 UI 뿐만 아니라 marathon이 제공하는 REST API 호출도 인증 정보를 넘겨야만 하는데 이전처럼 그대로 호출하면 401 Unauthorized 오류가 넘어오게 된다.

그럼 인증 정보를 어떻게 넘겨줘야 하는가? 이 부분은 친절하게 문서로 기술되어 있지 않다. 사용자 인증 적용방법을 기술해놓은 문서에 basic authentication이라고 명시되어 있었으니 일반적인 basic authentication 방법대로 header에 Authorization을 넣어서 호출해봤는데 다행히 된다. Header에 Authorization에 대한 값으로는 "Basic base64_encoded_data"를 넣어줘야 하는데 base64_encoded_data 부분은 1번 과정에서 http_credentials 파일에 넣어줬던 id:password 내용을 그대로 base64로 인코딩해서 사용하면 되겠다.

Marathon client를 별도로 사용하는게 있다면 client에서 http 호출시 header에 인증 정보를 추가할 수 있도록 수정하는 작업도 당연히 해야만 한다.

 

3. marathon-lb도 변경하자

REST API 까지 제대로 확인했는데 역시 뭔가 동작이 이상하다. 할 게 하나 더 남아있다. marathon-lb (load balancer용 python script. Marathon에서 container로 올라가 있는데 lb에 대해서도 더 작업을 진행해보고 정리할 필요가 있겠다)를 사용중이라면 아래 문서를 참고해서 실행옵션을 추가해야 한다. marathon-lb도 내부적으로는 marathon의 REST API로 event callback을 등록해 사용중이므로 marathon-lb가 접근할 때 사용할 인증정보를 추가할 필요가 있다. marathon-lb 저장소의 README.md를 읽어보면 auth-credentials 옵션을 넣어줘야 한다는 걸 알 수 있다. 아래와 유사한 형태로 marathon-lb container를 수정하거나 지우고 다시 만들면 되겠다.

{
"id": "/marathon-lb",
"args": [
"sse",
"-m",
"http://marathon_leader_host:marathon_port",
"--group",
"external",
"--auth-credentials",
"id:password"
],
"env": {
"PORTS": "9090"
},
"portDefinitions": [
{
"port": 9090,
"protocol": "tcp"
}
],
"instances": 6,
"cpus": 0.5,
"mem": 256.0,
"container": {
"type": "DOCKER",
"volumes": [
{
"containerPath": "/dev/log",
"hostPath": "/dev/log",
"mode": "RW"
}
],
"docker": {
"image": "mesosphere/marathon-lb",
"network": "HOST",
"privileged": true
}
},
"constraints": [
[
"hostname",
"UNIQUE"
]
]
}

· 7 min read

이전 글에서 docker monitoring을 하기 위해서 cAdvisor를 사용해봤는데 몇가지 단점들 때문에 바로 monitoring에 사용하기에는 문제가 있었다. 그래서 많이 사용한다는 prometheus를 사용해보기로 했다.

 

1. cAdvisor 정리

Prometheus를 사용하기 전에 먼저 cAdvisor를 정리해보면 아래와 같다.

(1) Container가 동작중인 host 각각에 설치 (클러스터링 안됨)

(2) Host와 container의 지표 모두 수집

(3) 수집된 지표들은 /metrics 경로에서 확인 가능

 

2. Prometheus

Prometheus는 docker container, VM 등을 모니터링할 수 있는 툴로 수집되는 지표들을 내부에 DB로 보관하는데 수집된 지표들을 확인하기 위해 PromQL이라는 쿼리를 제공하며 대략적인 구조는 아래 그림과 같다. Alert도 가능하다.

prom_architecture <출처 : https://www.ctl.io/developers/blog/post/monitoring-docker-services-with-prometheus>

Prometheus에서 중요하게 본 것은 그림에서의 "scrape metrics"를 어떻게 할 것이냐였는데 다행히 cAdvisor와의 연동은 prometheus 설정 파일에 cAdvisor의 metrics 경로를 지정해 주는 것으로 간단히 가능하고 별도로 container-exporter도 제공되고 있다. Container exporter를 사용할 경우 cAdvisor처럼 container로 올려줘야 하는데 Mesos, Marathon을 사용중이라면 Marathon으로 간단히 올려주면 되겠다. Container exporter는 cAdvisor와 유사하게 측정 지표들을 수집해서 /metrics 경로로 export 할 수 있는데 cAdvisor처럼 자체적으로 monitoring UI를 제공하지는 않는 것 같다. cAdvisor와는 수집하는 지표들이 차이가 있다. Prometheus 입장에서는 cAdvisor, container exporter 둘 중에 무엇이든 /metrics 경로에 접근해서 수집된 데이터들을 내부에 보관한다.

 

3. Prometheus 설치와 설정

(1) Install

Installing 페이지에 기술된 내용을 보면 release 된 파일을 다운로드해서 압축을 풀거나 source를 빌드해서 사용하거나 docker로 올리면 된다. 나의 경우엔 별도의 VM에 파일을 다운로드해서 사용했는데 linux 배포판 별로 버젼이 따로 없으므로 그냥 linux 용으로 되어있는 파일을 사용하면 된다. (macOS는 darwin)

(2) Configuration

문서들을 보면 설정파일(prometheus.yml)에서 다양한 설정을 지원하고 있는데 가볍게 cAdvisor와의 연동만을 해보고 싶다면 아래처럼 scape_configs section의 static_configs > targets 부분만 설정해도 가능하다. targets에는 cAdvisor가 동작중인 host와 port 정보만 넣어주면 prometheus는 yml 파일에 명시된대로 기본으로 /metrics 경로에 접근해서 측정된 지표를 가져온다. cAdvisor가 아니라 container exporter를 사용하고자 한다면 targets 부분만 변경해주면 되고 두가지 모두 사용해도 무방하지만 중복되는 지표가 많고 cAdvisor가 더 다양한 지표를 수집한다.

scrape_configs:
# The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
- job_name: 'mesosphere'

# Override the global default and scrape targets from this job every 5 seconds.
scrape_interval: 5s

# metrics_path defaults to '/metrics'
# scheme defaults to 'http'.

static_configs:
- targets: [
'cAdvisor_host1:port1',
'cAdvisor_host2:port2',
'...',
'...'
]

 

4. Prometheus UI

Prometheus 설정을 마치고 prometheus 파일을 실행하면 브라우저에서 UI를 확인할 수 있는데 cAdvisor에서 제공하는 지표들을 선택하고 PromQL을 사용해서 적절히 쿼리해야 한다.

prom_ui

시간 간격을 조절할 수 있고 원하는 그래프를 계속 추가해서 확인할 수는 있지만 그래프가 단순하고 cAdvisor처럼 바로 사용하기에는 뭔가 부족하다. 위 그림에는 제대로 표현되지 않았지만 cAdvisor가 수집하는 데이터 중 CPU usage의 경우 CPU core 별로 (cpu00, cpu01 등) 사용량을 수집하고 있어서 데이터를 가공할 필요가 있는데 이것은 PromQL로 해결해야 한다.

 

5. Grafana

Grafana는 단순히 dashboard를 편하게 만들고 관리할 수 있는 툴인데 data source 중 하나로 prometheus를 지원하고 있다. Prometheus에서 제공하는 grafana 설치 문서를 참고해서 설치한 후에 실행하고, 아래 그림처럼 data source 추가하는 부분에서 prometheus의 경로를 추가하면 된다.

grafana_datasource

Dashboard를 생성하면 빈 페이지가 나오는데 다양한 graph를 추가하고 설정할 때 graph를 그리기 위한 data는 prometheus에서 가져오게 되므로 PromQL을 적절히 사용해줘야 한다. (PromQL 문서 참조)

 

6. Grafana dashboard

Dashboard를 만들고 수정해보니 cAdvisor와 prometheus의 모니터링 측면에서의 부족한 부분들을 grafana가 모두 보완할 수 있다는 생각이 든다. Host와 container의 상태를 비교해보기도 좋고 시간 간격을 다양하게 변경할 수 있으며 훨씬 다양한 그래프를 사용하거나 아래 그림처럼 숫자만으로 혹은 테이블로 표시할 수도 있다.

granfana

· 16 min read

Docker container의 부하 분산과 auto scaling을 고민해야 하는 상황인데 부하 분산을 하기 위해서는 부하 테스트를 어떻게 할지부터 생각해봐야한다. 또 어느 정도의 stress에서 성능이 얼마나 떨어지는지 알아야 하기 때문에 모니터링도.

 

1. Environments

며칠 고민하고 검토해서 현재는 아래와 같은 상태로 만들어둔 상태인데 서비스 오케스트레이션을 위해 Apache Mesos와 Marathon framework을 사용하다보니 편한 것도 있는데 고민해야할 요소들도 많아진 것 같다. 단지 부하를 가하고 모니터링이 되는지 정도만 확인할거라 테스트 대상이 되는 web container는 python으로 hello world만 뿌려주는 간단한 웹앱을 사용했고 container 자체에는 cpu 0.5 core, memory 256MB만 할당. Load balancer는 marathon에서 제공하는 marathon-lb인데 0.5 core / 128MB만 할당해 두었다.

container-load-environment

 

2. Stress는 ngrinder로

Stress를 어떻게 줄까 고민하다가 Apache JMeter를 처음에 고려했는데 사용에 번거로운 요소들이 조금 있고 맥에서는 UI가 별로라 ngrinder를 시도해보게 되었다. 눈에 띄는 버그가 있긴 했지만 단순히 부하만 가하고 싶을 때 간편하게 할 수 있고 확장이 쉬워보이는게 장점인 것 같다.

(1) Agent, controller 사이의 통신 문제

ngrinder에서는 agent 하나에서 생성하는 process의 thread 하나를 가상 유저로 취급하는 것으로 보이는데 agent가 동작하는 환경을 고려했을 때 process와 thread의 수가 높으면 controller와의 통신에 문제가 생긴다. 찾아보니 이 문제는 이미 보고된 내용인 것 같고 현재는 적당히 수치를 조절하면서 사용해야 할 것 같다. 실제로 문제가 발생하는 process, thread 수보다 적게 설정해서 테스트를 해보니 agent가 돌아가는 vm의 cpu usage가 거의 full이었는데 agent가 돌아가는 환경에 대한 문제로 인식해야 할 것 같음.

(2) Agent 추가를 통한 stress 범위 확장과 클러스터링

더 많은 stress를 가하고 싶다면 agent를 확장하는 방법도 있는데, 비현실적이지만 단순히 부하만 가하기 위해서 처음에 vm 10대에서 agent를 돌리다가 현재는 5대로 줄여놓은 상태. 나중에 script를 추가하거나 변경하게 되었을 때 필요하면 다시 늘리면 된다. 아직 해보지는 않았지만 문서 상으로는 ngrinder controller의 경우 clustering이 가능한 것 같다.

(3) Test script로 다양한 시뮬레이션 가능

비현실적인 가상의 stress를 테스트하는 거지만 script 추가, 수정으로 조금 더 현실적인 테스트가 가능하게 되어있다. 예를 들어 가상 유저의 think time을 고려하고 싶다면 script에 sleep을 넣는 식으로 구현이 가능.

(4) Monitor의 한계

Target이 되는 시스템 모니터 용으로 사용할 수 있는 모듈이 controller에 들어있어서 다운로드 후에 target에 넣고 돌릴 수 있게 되어있는데 target의 ip와 port를 설정파일에 넣고 바인딩하도록 되어있다. 이 방식은 container에서 사용하기 어렵거나 가능하더라도 번거로워질 것 같다. Marathon framework에서는 container 별로 가상 ip를 부여하는 방식을 지원하려고 하는 것 같은데 아직 완전하지 않고 ip를 굳이 할당하고 싶다면 별도로 vip를 부여할 수 있도록 구성해줘야 한다.

 

3. 모니터링을 위해 cAdvisor를 사용해보자

ngrinder의 monitor도 사용하기 어렵고 일반적인 모니터링 도구인 zabbix, cacti, nagios 등도 container에는 적합하지 않다는 생각을 했는데 이유는 아래와 같다.

(1) Target에 binding 되는 형태

모니터링 지표들을 agent 형태의 모듈들이 직접 중앙으로 쏴주는 형태라면 괜찮을거라고 생각하는데 거꾸로 중앙에서 각 container에 접근해야 하는 형태라면 혹은 그렇지 않더라도 위에서처럼 target 각각의 ip나 hostname으로 바인딩해야 한다면 번거로워질 것 같다.

(2) Host와 container를 함께 관리하기 어렵다

(1)의 문제를 갖고 있지 않더라도 일반적인 tool로는 container와 host 모두를 함께 확인하기 어렵다. 각각을 다른 모니터링 대상으로 보고 따로 관리하면 가능할텐데 한 host에 어떤 container들이 올라가 있고 각각의 container가 어떤 상태인지, 부하가 가해졌을 때 host는 어떻게 되는지를 함께 확인할 수 있어야 더 유용하다고 본다.

(3) Container 이중화의 문제

부하를 분산하기 위해, 또는 다른 이유로 하나의 container를 이중화 하거나 여러개로 분산하는 경우 분산된 container들 각각의 상태와 또 하나의 서비스 관점에서 통합된 지표를 확인할 필요가 있을 것 같다.

이 내용들을 정리하자면 일반적인 모니터링 도구들은 물리적인 서버나 vm에 초점을 맞추고 있다는 생각이고 그 위에서 동작하는 container의 모니터링은 조금 다른 관점에서 접근할 필요가 있다고 본다. 그래서 docker 모니터링 도구들이 있는지 확인해보게 되었는데 아래의 글을 보고 우선은 cAdvisor를 검토해보기로 했다.

http://rancher.com/comparing-monitoring-options-for-docker-deployments/

 

우선은 Marathon framework으로 mesos slave node 각각에 deploy를 했다. 아래의 json을 curl로 날리면 끝. 아니면 직접 marathon ui에서 생성해도 된다. 최근의 marathon은 constraints 설정으로 각 node에 container가 하나씩만 배포할 수 있는 기능을 제공하고 있다. 또, network을 HOST 모드로 지정하면 BRIDGE 모드에서처럼 container와 host의 port를 각각 매핑하지 않아도 되고 marathon의 service port가 임의로 붙지 않아서 편하다.

{
"id": "cadvisor",
"cmd": null,
"cpus": 0.5,
"mem": 256,
"disk": 0,
"instances": 4,
"container": {
"docker": {
"image": "google/cadvisor",
"network": "HOST",
"privileged": true,
"parameters": []
},
"type": "DOCKER",
"volumes": [
{
"hostPath": "/",
"containerPath": "/rootfs",
"mode": "RO"
},
{
"hostPath": "/var/run",
"containerPath": "/var/run",
"mode": "RW"
},
{
"hostPath": "/sys",
"containerPath": "/sys",
"mode": "RO"
},
{
"hostPath": "/var/lib/docker",
"containerPath": "/var/lib/docker",
"mode": "RO"
}
]
},
"portDefinitions": [
{
"port": 8080,
"protocol": "tcp",
"name": null,
"labels": null
}
],
"env": {},
"labels": {},
"healthChecks": [],
"constraints": [
[
"hostname",
"UNIQUE"
]
]
}

cAdvisor까지 배포된 후 ngrinder로 간단하게 부하를 발생시켜봤다. Script 등은 그대로 두고 agent는 하나만 사용하는 것으로 해서 테스트를 해보니 아래 그림처럼 cAdvisor의 그래프가 변화한다. 우선은 CPU만 확인해봤는데 container 내부에서는 대략 80% 이하로 cpu를 점유하는 것 같고 동작중인 host 전체에서는 50% 정도의 사용량을 찍었다. 여기서 host의 50% 점유율은 테스트 대상이 되는 container가 단독으로 만들어낸 사용량은 아니지만 (LB 등도 돌아가고 있으므로) container의 사용량이 올라갈수록 container가 동작중인 host에도 많은 부하가 걸림을 알 수 있다.

edited-ngrinder-normal

edited-cpu-normal

 

4. Container와 host에 걸리는 부하

간단한 부하테스트와 모니터링이 가능한 것은 확인했는데 auto scaling을 구현하는게 어찌보면 최종 목표이기 때문에 단일 container가 감당하지 못할 정도의 부하를 줄 수 있는지 확인해보기로 했다. 그래서 ngrinder agent를 5개로 늘려 가상 사용자를 2500으로 만들어서 다시 테스트.

edited-ngrinder-high

위 그림처럼 테스트 도중에 TPS가 0으로 떨어지고 이미지는 첨부하지 않았지만 그 시점부터 error가 발생한다. 이 때 container와 host의 상태는 어떨까? 아래 그림처럼 container는 본인에게 할당된 core 이상을 점유하는 경우가 발생하고, host (2core 짜리 VM)도 거의 100%에 가까운 사용량을 보이다가 줄어든다.

edited-cpu-high

테스트 후에 웹앱에 접근해보니 안되길래 container 내부의 로그를 확인해보니 아래 이미지처럼 SocketServer에서 오류가 발생해서 더 이상 서비스가 안된다. 오류 발생 이후에 접근이 안되는데도 200이 찍히는건 이해가 안되지만 일단 서비스가 죽을 정도의 부하를 발생시킬 수 있다는 걸 알았고 그걸 모니터링 할 수 있으니 성공.

container-error

5. Load balancing

부하를 걸어서 서비스를 죽여는 봤으니 load balancing을 하면 어떻게 될지 궁금해졌다. 당연히 개선되겠지만 auto scaling을 구현했을 때 그 기능이 유효할지는 알아야 하니까. 부하는 동일하게, 테스트 대상인 웹앱의 instance를 2개로 늘려서 다시 테스트. 이 때 ngrinder가 가리키는 url은 LB가 된다. (instance가 하나일때도 동일했음) LB가 request를 2개의 container에 적당히 알아서 분배해야할테니 죽더라도 어느 정도 개선된 지표가 확인되어야만 한다.

edited-ngrinder-lb

위 그림처럼 ngrinder에서 테스트 오류가 발생하지 않았고 TPS는 container가 하나일 때에는 최대 1400 정도였던 것이 1860까지 올라갔다. 이 때 부하를 받은 2개의 container와 VM의 cpu 사용량 상태는 아래와 같았다. (container는 각각 다른 VM에 올라가도록하고 해당 VM에는 테스트용 container, cAdvisor 이외에는 아무것도 없는 상태)

edited-cpu-lb

두개의 container 모두 할당된 core 이상의 점유율을 보이긴 했지만 서비스가 죽지는 않았고 그 상태에서 VM의 cpu 사용량은 50%를 넘지는 않았다. 추가로 LB가 올라가있는 VM의 점유율이 상당히 높아진 것도 확인.

 

6. cAdvisor의 장단점

(1) 장점

위에 링크된 비교 페이지에서처럼 cAdvisor는 구축이 무척 간단하다. 또 VM과 container의 상태 확인이 가능하다는 것도 도움이 된다.

(2) 단점

특정 시점의 상태를 다시 확인할 수가 없고 정확한 결과값이 아니라 그래프로만 표시되어서 정확한 분석이 어렵다. REST API를 제공하는 것으로 봐서는 내부에서는 data를 쌓을 것 같기도 한데 UI로는 확인할 수가 없었다. REST API를 호출해야봐야 알겠지만 API로 제공하는 data도 그 순간의 data 뿐일수도 있다. 또, host와 container의 상태를 확인할 수는 있지만 한 화면에서 보는 것은 불가능해서 상당히 불편했는데 측정된 지표들이 시간에 따라 계속 흘러가기 때문에 동시에 확인하는 것이 힘들고 각 host에 설치되어야 하지만 클러스터링이 지원되지 않아 host 별로 따로 접속해서 확인해야 한다. Mesos, marathon과 엮어서 생각해봤을 때에는 mesos 내부에서 관리하는 container의 id와 marathon에서의 application 이름(예를 들면 simple-webapp 같은 사람이 인지할 수 있는 이름)이 매칭이 어려운데 cAdvisor에서 표시하는 container는 marathon에서의 application 이름이 아닌 것도 문제.

 

7. 더 고민할 것들

Auto scaling을 위해서는 scaling 시점을 결정해야 하기 때문에 container와 VM의 상태에 대한 data를 얻을 수 있어야만 한다. 더 확인해봐야 하겠지만 cAdvisor의 data를 InfluxDB나 Redis 등에 전송한 후에 그 곳에서 쌓이는 data를 가지고 처리하는 방법도 있을 것 같고 아예 다른 모니터링 도구를 활용하는 방법도 생각해봐야겠다.

· 6 min read

MAAS 구축하기를 통해 만들어진 node 들을 사용자들이 acquire 해서 각자의 용도에 맞게 사용할 수도 있지만 juju를 이용하면 자동 배포 및 관리도 가능하다. MAAS를 사용하게 된 이유 중 하나가 ceph을 더 살펴보기 위해서인데 (Juju로 Ceph을 deploy 해보자 참조) 어찌보면 배보다 배꼽이 더 큰 경우일 수도 있겠지만 어디까지나 재미로 혹은 공부한다 생각하니 효율적이지 못해도 괜찮다고 본다.

 

1. Prerequisites

Juju를 먼저 설치해야 한다. (getting-started 문서 참조) 나의 경우엔 예전에 설치해 둔 상태라 1.25.3 버전이 설치되어 있는데 현재 최신 버전은 2.0이고 각 버전별로 기능 차이가 있으니(버그도...) 주의가 필요하다. MAAS에 접속해서 사전에 MAAS key를 복사해 두어야 한다.

maas-keys

MAAS에 로그인 후 계정 이름을 클릭해서 Account 메뉴를 선택하면 위 그림과 같은 화면이 표시되는데 기본으로 생성되어 있는 MAAS key를 복사해둔다. 없으면 생성. 또, 이미 MAAS 구축할 때 ssh key 등록을 해놨지만 안해두었다면 본인이 사용하는 host의 ssh public key를 등록해둔다.

 

2. environments.yaml 파일 수정

Juju를 설치하면 ~/.juju 경로가 몇개의 파일과 함께 자동 생성된다. 만약 없다면 juju init 명령을 실행하면 생성된다. Juju는 yaml로 각 환경에 대한 설정을 사전에 정의해서 사용하기 때문에 ~/.juju/environments.yaml 파일을 수정해야 한다. 아래 그림처럼 maas 항목을 찾아서 세부 설정을 해주면 된다.

juju-environment

필수 설정 항목을 정리하면 아래와 같다.

maas-server : MAAS 접근 url

maas-oauth : 과정 1에서 미리 복사해 둔 maas-key를 입력

admin-secret : 문서에 의하면 random pass-phrase라고 하는데 어디에 사용되는지는 모르겠지만 없으면 bootstrap이 진행되지 않으니 임의의 값을 입력해준다.

위의 3개 값은 필수이니 설정하고 저장하면 완료.

 

3. Bootstrap with no-auto-upgrade option

(1) Switch environment

environments.yaml 파일을 대충 봤으면 알겠지만 여러가지 환경에 대한 설정을 한 파일에 모두 정의하도록 되어있는데 상황에 따라 선택적으로 환경을 지정할 수 있게 되어있다. 현재 juju가 사용하는 환경으로 지정된 것은 ~/.juju/current-environment 파일에 저장된다. 만약 maas가 기본으로 지정되어 있지 않다면 아래의 command를 실행해야 한다. (environments.yaml 파일에 maas라는 이름을 변경하지 않은 경우. 만약 변경했다면 변경된 이름으로 switch)

[bash]

juju switch maas

[/bash]

(2) Ready node

Juju가 MAAS 대상으로 bootstrap 가능한 node는 ready 상태의 node이다. 당연하겠지만 다른 사용자의 소유로 되어있거나 (allocated) OS가 deploy된 상태인 node에는 이미 다른 용도로 사용중이거나 예정인 것이므로 juju가 관여하지 않고 ready로 남아있는 node 중에 하나를 무작위로 골라서 bootstrap을 하게 되므로 주의해야 한다.

(3) Bootstrap

이제 bootstrap만 하면 되는데 juju 1.25.3에서 진행해보니 거의 마지막 과정에서 Waiting for API to become available이 여러번 반복되며 부분적으로만 bootstrap이 성공한 것으로 완료되는 문제가 있어서 bug report를 찾아봤다. 문서 내용에 의하면 newer version이 필요한 상태로 client에 기록되고 그 정보가 bootstrap machine (juju state server가 될 장비)에 넘어가서 bootstrap machine에서 newer version과의 연결을 계속 시도하는 것 같다. 개선될 bug로 보이는데 어찌되었든 1.25.3에서 그런 문제를 방지하려면 아래처럼 no-auto-upgrade option을 추가해준다. (v option은 verbose.혹시 더 많은 로그를 보고 싶으면 v 대신 debug 옵션을 주면 되겠다.)

[bash]

juju bootstrap -v --no-auto-upgrade

[/bash]

아래 그림과 비슷한 내용이 표시되고 완료된다.

juju-bootstrap

bootstrap이 정상으로 완료되고 juju status 명령을 입력해보면 아래 그림처럼 현재 상태가 표시되고,

 

juju-status

혹시 ssh로 접속하고 싶다면 아래의 command를 입력하면 간단히 접속된다.

[bash]

# connect to juju machine number '0'

juju ssh 0

[/bash]

· 5 min read

"MAAS 구축하기"를 통해 MAAS를 위한 network 설정,  구축 등을 해봤는데 정작 생성된 MAAS node에 바로 접근하는게 불가능하다. MAAS network이 NAT되어 MAAS network 내부에 있는 VM들이 외부 인터넷과 연결되는 것은 가능하지만 MAAS network으로 통하는 경로를 지정하지 않았기 때문인데 간단히 route 설정으로 처리한 방법을 정리해봤다.

 

1. Network 구성

현재 상태의 network 구성을 그려보면 아래 그림과 같다. (뭐든 그려봐야 명확해진다)

maas-vpn-network

 

정리를 해보면 192.168.100.0/24의 정체를 알고 있는 것은 NAT를 구성하고 있는 VyOS  하나 뿐인데, 집에서 사용하고 있는 공유기는 VyOS가 가지고 있는 두 개의 NIC 중 하나만 관리하고 있고 VyOS가 무엇을 하는 놈인지 관심도 없고 모르는 상태. 그렇기 때문에 공유기로부터 ip를 할당받아 내외부와 통신하고 있는 192.168.10.0/24 대역의 모든 장비도 192.168.100.0/24에 접근할 수가 없다. 단, "VyOS를 이용한 NAT network 구성"에서 설정했던 것처럼 192.168.10.0/24 대역에 있는 장비가 maas-network과 연결할 수 있는 NIC을 추가로 달고 있으면 VyOS에 의해 DHCP로 192.168.100.0/24 대역의 ip를 할당받을 수 있으니 접근이 가능하다. 하지만 연결하고자 하는 모든 장비에 NIC을 추가하는 것도 이상하고 더 나아가 VPN으로 연결된 외부에 있는 장비의 경우엔 내부망에 소프트웨어적으로 구성되어 있는 NIC을 다는 것은 불가능한 일이다. 이런 경우 MAAS node에 어떻게 연결되게 할 수 있을까?

 

2. Route to MAAS network

공유기 아래에 있는 192.168.10.0/24 대역의 장비들은 공유기를 gateway로 하고 있으니 공유기 라우팅 테이블에 설정해보기로 했다. Destination은 192.168.100.0, subnet mask는 255.255.255.0  (결국 192.168.100.0/24에 대한 요청)  을 가지는 LAN 내부에서의 트래픽이 VyOS의 eth0가 할당받은 ip를 gateway로 해서 흘러가게 설정했더니 끝. 192.168.10.x를 ip로 갖는 장비들에서 ping을 날려보니 정상 응답들이 넘어온다.

 

3. VPN route

공유기 내부에서의 접근은 공유기 라우팅 테이블에 하나 추가함으로써 문제는 해결이 되었는데 VPN은 약간 상황이 다르다. OpenVPN 서버 기능이 포함된 공유기를 사용하고 있는데, 기본 설정만으로 VPN client가 할당받는 10.8.0.0/24 대역의 ip에서 공유기 내부의 192.168.10.0/24 대역의 장비들이 보이는 상태였으나(원래는 별도로 설정을 추가해야  가능) 10.8.0.0/24에서는 192.168.100.0/24로 가는 길을 모르는 상태. 상황은 2와 약간 다르지만 어차피 경로만 추가하면 될 일이다. OpenVPN 설정에 아래 라우트 설정을 추가해서 해결할 수 있었다.

[bash]

push "route 192.168.100.0 255.255.255.0"

[/bash]

 

4. MAAS node에 연결

MAAS 구축시 ssh key (public)를 등록해두었기 때문에 MAAS node 배포시 등록한 public key도 함께 배포된다. 그렇기 때문에 node에 별도로 설정을 추가하지 않는 이상 이미 배포되어 있는 public key와 쌍을 이루는 private key를 가지고 있어야만 ssh 접속이 가능하다.  Network 대역이 달랐기 때문에 처음엔 접근 자체가 되지 않았지만 3까지의 과정까지 진행한 후 VPN으로 연결된 장비(private key를 가지고 있는)에서 ssh 접근을 시도하니 성공.

maas-node-login

· 7 min read

VyOS는 소프트웨어 라우터, VPN, 방화벽 등의 기능을 제공하는 Vyatta의 community fork 버전으로 라우터, 방화벽 등을 소프트웨어로 구현해서 설정 가능한 network 전용 OS라고 보면 된다. 가벼운 서버나 PC 같은 장비에 VyOS를 올리면 공유기를 대체해서 사용하는 것도 가능하다. (물론 LAN port 확장을 위해 랜카드가 추가로 필요하겠지만) 리눅스 기반이기 때문에 ssh 접속부터 일반적인 command 사용이 가능하고 network 전용 OS 답게 기능 설정을 위한 별도의 command set도 제공된다.

어디까지나 MAAS 구축을 위해 필요에 의해서 진행한 내용이고 (MAAS 구축하기 참조) 사실 network 분리, NAT 구성은 여러가지 방법이 있을 수 있지만 공부 혹은 연습을 위한 것이라 비효율적으로 보일 수 있겠다는 생각도 든다.

 

1. vSwitch / Port group 추가를 통한 network 분리 준비

ESXi 기준으로 보면 단순히 하나의 vSwitch에 port group만 추가해서 설정해도 network 분리는 가능할 것 같다. 아래 그림처럼 기본으로 존재하는 vSwitch0에 기존에 사용하는 VM Network 이란 port group이 있는 상태에서 maas-cluster-switch라는 vSwitch를 추가했다. 다시 maas-cluster-switch에는 VM 연결을 위한 maas-network이라는 port group을 추가했는데 vSwitch0와 달리 물리 어댑터에 연결된 상태가 아니기 때문에 maas-network에 붙은 VM들은 기본적으로 외부 인터넷과의 통신이 불가능하다.

vswitch

단절된 maas-network이 외부 인터넷과의 통신을 가능하게 하기 위해 NAT (Network Address Translation) 가 필요하다.

 

2. 설치

VyOS 설치를 위해 VM을 하나 생성하고 VyOS user guide에 기술된 대로 진행하면 설치는 간단하게 끝낼 수 있다. (설치에 필요한 ISO image는 VyOS wiki main에 링크되어 있음) 단, 설치 전이나 후에 VyOS VM에 VM Network과 maas-network과 연결하는 NIC 2개를 붙여주어야 한다.

 

3. NAT 구성

사실 user guide에 다 기술되어 있는 내용이지만 부분별로 나누어서 봤을 때 우선 interface 기본 설정을 해야 한다. Configuration mode로 진입해서 아래의 command를 입력한다. (eth0가 VM Network에 연결된 interface이고 eth1이 maas-network에 연결된 interface인 경우를 가정)

[bash]

set interfaces ethernet eth0 address dhcp

set interfaces ethernet eth0 description 'PUBLIC'

 

set interfaces ethernet eth1 address '192.168.100.1/24'

set interfaces ethernet eth1 description 'PRIVATE'

[/bash]

eth0는 공유기의 DHCP가 할당한 ip를 사용하기 때문에 dhcp를 지정했고 eht1의 경우에는 VyOS에서 dhcp로 할당할 ip 대역(192.168.100.0/24를 사용할 예정)의 gateway가 될 것이기 때문에 위와 같이 지정했다.

다음으로 NAT 구성을 위해 아래의 command를 입력한다.

[bash]

set nat source rule 100 outbound-interface 'eth0'

set nat source rule 100 source address '192.168.100.0/24'

set nat source rule 100 translation address masquerade

[/bash]

매우 간단하다. 여기까지 진행된 후에 commit을 입력하면 수정내용이 반영되고 save 명령을 실행하면 영구 저장된다.

 

4. DHCP 구성

maas-network에 연결될 VM들이 ip를 할당받아야 하기 때문에 dhcp 구성이 필요하다. 역시 아래의 command로 간단하게 처리할 수 있다.

[bash]

set service dhcp-server disabled 'false'

set service dhcp-server shared-network-name LAN subnet 192.168.100.0/24 default-router '192.168.100.1'

set service dhcp-server shared-network-name LAN subnet 192.168.100.0/24 dns-server '192.168.100.1'

set service dhcp-server shared-network-name LAN subnet 192.168.100.0/24 domain-name 'internal-network'

set service dhcp-server shared-network-name LAN subnet 192.168.100.0/24 lease '86400'

set service dhcp-server shared-network-name LAN subnet 192.168.100.0/24 start '192.168.100.10' stop '192.168.100.254'

[/bash]

다음은 DNS forwarder 설정.

[bash]

set service dns forwarding cache-size '0'

set service dns forwarding listen-on 'eth1'

set service dns forwarding name-server '8.8.8.8'

set service dns forwarding name-server '8.8.4.4'

[/bash]

위 command 들은 dhcp를 활성화시킨 후 dns, lease time 등의 기본적인 설정을 진행한 것인데 필요하다면 lease time, dhcp 할당 범위 등을 적당히 조절해서 설정하면 되겠다. 역시 저장을 위해 commit, save 명령을 실행하고 exit 명령으로 configuration mode를 빠져나오면 완료. VyOS user guide에서는 방화벽 등의 추가 설정을 하도록 되어있으니 필요한 경우 별도로 진행하면 될 것 같다.

 

5. 결과

간단하게 VyOS에서 몇가지 설정만 하는 것으로 결과적으로 내가 원하는 이런 그림이 되었는데, VyOS가 192.168.100.0/24 대역을 DHCP로 만들어주고, 물리적으로는 단절된 해당 네트웍 대역이 외부로 나가기 위해 192.168.10.0/24 대역에 연결된 NIC을 NAT로 거치게 됨으로써 이제 외부 인터넷과의 통신이 가능해졌다.

maas-network