fleet은 systemd를 클러스터 레벨에서 제어하는 클러스터 매니저입니다. 클러스터 내에서 서비스를 실행하기 위해서는 먼저 systemd unit을 submit 해야합니다.
Unit file 작성
Redis 컨테이너를 실행하는 Unit file을 작성합니다. 이 때 docker 명령문에서 -d 옵션을 사용하지 않도록 주의해야 합니다. -d 옵션으로 인해 컨테이너가 detached 되면 unit이 종료되기 때문입니다.
/etc/systemd/system/redis@.service
[Unit]
Description=Redis
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill redis%i
ExecStartPre=-/usr/bin/docker rm redis%i
ExecStart=/usr/bin/docker run --name redis%i -p 6379:6379 redis
ExecStop=/usr/bin/docker stop redis%i
[X-Fleet]
MachineMetadata=role=worker
Redis 컨테이너가 실행된 머신에서 Redis 컨테이너를 대신해서 IP, Port를 리포트해줄 Discovery Unit을 작성합니다. 이와 같이 메인 컨테이너의 보조 역할을 하는 유닛을 Sidekick 컨테이너라고 부릅니다.
/etc/systemd/system/redis-discovery@.service
[Unit]
Description=Announce redis
BindsTo=redis@%i.service
After=redis@%i.service
[Service]
ExecStart=/bin/sh -c "while true; do etcdctl set /services/redis/redis@%i '{ \"host\": \"%H\", \"port\": 6379 }' --ttl 60;sleep 45;done"
ExecStop=/usr/bin/etcdctl rm /services/redis@%i
[X-Fleet]
MachineOf=redis@%i.service
Unit 실행
먼저 현재 클러스터를 구성하고 있는 머신들을 확인해봅니다. 저는 다섯개의 머신이 등록되어 있습니다. 메타데이터에 role=service로 표시된 3개의 머신이 etcd 클러스터를 구성하고 있습니다. role=worker로 표시된 머신은 etcd proxy를 통해 etcd를 사용합니다.
$ fleetctl list-machines
MACHINE IP METADATA
23d8bb5c... 172.31.24.253 role=worker
5a06ff68... 172.31.24.254 role=worker
69bf522c... 172.31.17.24 role=services
a5ba2fc0... 172.31.17.23 role=services
bd460f19... 172.31.17.22 role=services
redis@1 unit을 실행합니다. MachineMetadata=role=worker 옵션에 따라 role=worker 메타데이터를 갖는 두개의 머신 중 하나에서 unit이 실행됩니다.
$ fleetctl start redis@1
Unit redis@1.service inactive
Unit redis@1.service launched on 5a06ff68.../172.31.24.254
redis-discovery@1 unit을 실행합니다. MachineOf=redis@%i.service 옵션에 따라 redis-discovery@1 unit은 redis@1 unit이 실행된 머신에서 실행됩니다. 실제 etcd에 기록된 값도 직접 읽어봅니다.
$ fleetctl start redis-discovery@1
Unit redis-discovery@1.service inactive
Unit redis-discovery@1.service launched on 5a06ff68.../172.31.24.254
$ etcdctl ls /services --recursive
/services/redis/redis@1
$ etcdctl get /services/redis/redis@1
{ "host": "ip-172-31-24-253.ap-northeast-2.compute.internal", "port": 6379 }
아래 명령을 통해 unit file 목록을 확인할 수 있습니다. fleetctl destroy 명령을 사용하면 unit file을 목록에서 제거할 수 있습니다. 단, 현재 실행 중인 unit도 정지되니 주의해야 합니다.
$ fleetctl list-unit-files
UNIT HASH DSTATE STATE TARGET
redis-discovery@1.service 2fe5611 launched launched 5a06ff68.../172.31.24.254
redis@1.service dc3f4f7 launched launched 5a06ff68.../172.31.24.254
현재 실행 중인 unit은 아래 명령으로 확인할 수 있습니다. fleetctl stop 명령을 사용하면 unit을 정지시킬 수 있습니다.
$ fleetctl list-units
UNIT MACHINE ACTIVE SUB
redis-discovery@1.service 5a06ff68.../172.31.24.254 active running
redis@1.service 5a06ff68.../172.31.24.254 active running
머신 장애에 따른 리커버리
클러스터 내의 머신들은 스케쥴링(유닛을 머신에 할당하는 작업)을 수행할 리더를 선출하고 모든 멤버들끼리 지속적으로 통신을 유지합니다. 만약 하나의 머신에 장애가 생겨서 리더에게 heartbeat 패킷을 보내지 못하게 되면, 해당 머신에서 실행중이던 모든 유닛은 리스케쥴링 됩니다. 리더는 각 유닛을 실행하기에 적합한 머신을 찾아내고, 해당 머신이 유닛을 실행하도록 명령합니다. 리스케쥴링이 불가능한 유닛은 적합한 머신을 찾을 때까지 stop 상태로 남게됩니다.
만약 장애가 발생했던 머신이 다시 복구되면, 리더는 해당 머신에게 오래된 유닛들을 정지시키도록 명령합니다. 이로인해 머신은 새로운 작업을 수행할 수 있는 상태가 됩니다. sudo systemctl stop fleet 명령어를 사용하여 머신 하나를 정지시키면 sudo journalctl -u fleet 로그를 통해 무슨 일이 발생하는지 확인할 수 있습니다.
HAProxy를 활용한 서비스 디스커버리 구현
etcd에 기록된 정보를 바탕으로 다른 머신의 어플리캐이션에서 Redis로 연결을 해보려고 합니다. etcd로부터 host, port를 얻어오는 과정을 어플리캐이션에서 직접 구현해도 되지만 저는 별도의 Discovery Agent를 통해 구현하려고 합니다.
어플리캐이션과 Agent를 분리하면 어플리캐이션 개발자는 플랫폼에 의존적이지 않아도 되기 때문에 규모가 큰 프로젝트에서 유리합니다. 또한 etcd를 consul과 같은 다른 서비스로 변경할 일이 생기더라도 소스코드를 수정하지 않아도 되니 플랫폼이 보다 유연해집니다.
Discovery Agent 컨테이너 실행
Discovery Agent는 etcd에 저장된 Redis Unit의 Host, Port 정보를 바탕으로 HAProxy 설정을 업데이트합니다. HAProxy는 80 포트에 바인딩되어 실행되므로 80 포트를 열어줍니다. 컨테이너의 이름은 'discovery-agent'로 지정합니다.
$ git clone https://github.com/behonestar/service-discovery
$ cd discovery-agent
$ docker build -t discovery-agent .
$ docker run -it -p 80:80 --name discovery-agent discovery-agent
/services/redis/redis@1
config changed. reload haproxy
/services/redis/redis@1
/services/redis/redis@1
...
Worker 컨테이너 실행
Worker는 'haproxy'라는 host로 연결을 요청합니다. 따라서 'discovery-agent' 컨테이너를 'haproxy'라는 별칭으로 link합니다. 만약 연결에 실패한다면 Security Group의 6379 포트가 Inbound 설정이 되어 있는지 확인합니다.
$ git clone https://github.com/behonestar/service-discovery
$ cd worker
$ docker build -t worker .
$ docker run -it --link discovery-agent:haproxy worker
bar
bar
bar
bar
...
참고
- https://coreos.com/fleet/docs/latest/launching-containers-fleet.html
- http://jasonwilder.com/blog/2014/07/15/docker-service-discovery/
- http://www.haproxy.org/
'MicroService' 카테고리의 다른 글
Consul & Registrator (0) | 2016.10.05 |
---|---|
CoreOS etcd 클러스터 구축하기 (0) | 2016.08.11 |
CoreOS etcd cluster 사이즈 변경하기 (0) | 2016.08.10 |