IoT를 위한 고 가용성 MQTT 클러스터 구축하기

2023. 2. 28. 19:20이것저것

728x90

IoT를 위한 고 가용성 MQTT 클러스터 구축하기1

 

IoT를 위한 고 가용성 MQTT 클러스터 구축하기

IoT를 위한 고 가용성 MQTT 클러스터 구축하기1 cake ☺ 배치 단계를 한 번에 해줄 수 있도록 Redis...

blog.naver.com

 

배치 단계를 한 번에 해줄 수 있도록 Redis, HAProxy, Node.js 와 nscale f활용하여 확장 가능한 MQTT 인프라를 생성해보도록 하겠습니다.

 

이 문서에서IoT를 위한 확장가능한MQTT 클러스터를 생성하는 방법을 설명합니다. 여기 모든 내용은 Lelylan에서 만들어진 프로젝트에서 참고 했습니다. 이 문서가 유용했다고 생각하신다면 우리 Github에 스타를 하나 주시면 감사하겠습니다. 그렇게 되면 좀 더 많은 개발자들과 소통 할 수 있을 것 같습니다.

 

https://github.com/lelylan/lelylan/

 

GitHub - lelylan/lelylan: Open Source Lightweight Microservices Architecture for the Internet of Things. For developers.

Open Source Lightweight Microservices Architecture for the Internet of Things. For developers. - GitHub - lelylan/lelylan: Open Source Lightweight Microservices Architecture for the Internet of Thi...

github.com

 

 

무엇을 말하려고 하는 것일까요?

전문적인 IoT 환경에서는 가용성과 확장성이 서비스를 만드는 데 가장 주요한 요소라고 생각합니다. MQTT 환경이란 우리가 만든 브로커가 안정적인 연결성을 보장하고 항상 제 성능을 내고 있으며 제품으로서 실행하는 동안 개인 클라우드 인프라를 업데이트 할 수 있는 능력을 가져야만 합니다. 이 문서에서 Lelylan과같은 IoT 환경을 구성하면서 겪었던 경험치를 단계적으로 공유 하고자 하는 것이 그 목적입니다.

Devices connecting to a load balancer, forwarding all connections to two MQTT servers. Image copyright to HiveMQ.

기기들은 로드 밸런서를 통해서 연결되고 모든 연결들을 두 대의 MQTT 서버로포워딩 할 것입니다. 상위 이미지의 저작권은 HiveMQ에있습니다.

위와 같은 구성의 가장 큰 이득은 우리의 MQTT 서버들 중 하나가 가용하지 않을 때(어떤 이유에서든), 여전히 브로커가 이 연결 트래픽을 조작 할 수 있도록 해 줍니다. 다시말하면 두 개(혹 그 이상) 중 하나의 노드가 작동을 멈추었을경우에 로드 밸런서가 모든 진입 트래픽을 동작하고 있는 다른 클러스터 노드로 재 전송 해 줄 수 있도록 해서 클라이언트에서 어떤 오류 상황을 발생시키지 않도록 만들어 준다는 것입니다.

배치 프로세스를 단순화하는 것도 역시 우리의 목표입니다. Nscale을 사용하는 것은 다음의 두 줄의 명령어를 사용함으로써 2대(혹은 그 이상) MQTT 서버들을 배치 할 수 있도록 해 줄 수 있습니다.

$ nsd cont buildall
$ nsd rev dep head
 

사용한 툴의 리스트

다음은 그 결과치를 내기 위해서 사용되었던 툴의리스트들 입니다.

  • Mosca. Node.js MQTT server/broker 입니다. MQTT 3.1 호환성과 QoS 0과 QoS 1를 지원하며 오프라인 패킷들과 subscription들의저장 옵션을 사용 할 수 있습니다.
  • HAProxy. TCP와 HTTP 기반애플리케이션들의 고가용성, 로드 밸런싱, 및 프록시 기능을제공하는 공짜이며, 빠르고 안정적인 솔루션 입니다.
  • Docker. Docker는 개발과 제품 환경 사이의 충돌을 제거하고, 개발자들과 시스템 담당자가 컴포넌트들로부터 신속하게 조합 될 수 있는 분산 애플리케이션을 실행, 배치 및 빌드 할 수 있게 해주는 오픈 플랫폼입니다.
  • Nscale. 분산된 애플리케이션들을 위한 실행 플랫폼을 구성하기위한 연결된 컨테이너 군을 배치하고 빌드하고 환경 설정을 간소화 해 줄 수 있는 오픈소스 프로젝트 입니다.(nscale을사용하면 마이크로 서비스 기반 시스템을 배치 하는 프로세스들은 쉽게 정형화 시킬 수 있습니다)/
  • Redis. BSD 라이선스, advanced key-value 캐시및 저장소를 가진 오픈소스 입니다.

 

경량IoT를 빌드 하는 개발자들을 위한 마이크로서비스 아키텍처 입니다.

 

 

그래서, 이제부터 뭘 해야 하나요?

 

여기 IoT를위해서 고가용성 MQTT 클러스터를 만들기 위한 단계 단계를 하나씩 밟아 가도록 하겠습니다.


  1. MQTT 서버 세팅
  2. MQTT 서버의Docker화
  3. 로드 밸런서로서 HAProxy 추가.
  4. SSL을 통한 MQTT 보안화.
  5. 배치 워크플로우 자동화를 위한 nscale 환경설정
  6. 마지막 고려사항들

1. MQTT 브로커 세팅

MQTT 는 machine-to-machine (M2M) / “사물인터넷” 연결 프로토콜입니다.

이 프로토콜을 초경량 publish/subscribe 메시징 프로토콜을 사용하도록 디자인 되었으며, 메모리를줄이기 위한 소형 코드가 필요한 원격 위치들의 연결성을 보장하거나 네트워크 대역폭은 중요한 결정요인이 되는 곳에 유용합니다.

이 MQTT 솔루션을처음 접한 때는 2년 전이었습니다.우리는 안정성(인증기반)이 보장되고, 커스터마이징이가능하며(REST API를 사용해서 통신가능하고) 그리고쉽게 사용 할 수 있는 솔루션(이미 알고 있던Node.js 같은)을 찾고 있었습니다. Mosca 에서 맞는 솔루션을 찾았고, 2년 지난 후 우리는 우리의 선택이 맞았다는 것을 알게 되었습니다. 지금이 MQTT 서버의 선택 이유가 우리가 생각한 방향과 다른 방향으로 생각 할 수 있을 것입니다. 만약 그렇다면, 각 MQTT서버들의리스트와 그것들의 특징들을 다음의 this list 에서 확인 해 볼수 있을 것입니다.

 

코드로 응답해 봅시다.

각 라인마다 설명을 달 것이지만, 두 개의 주요 섹션들을 보여주고, MQTT 서버의 세팅이 얼마나 쉬운지도 보여 줄 겁니다. Lelylan에서 사용한 MQTT 서버의 실행에 사용한 코드들은 Github 에서 사용 가능합니다.

 

MQTT 서버 세팅.

아래의 코드는 MQTT 서버를 실행하는 데 사용한 코드 입니다. 첫번째, Redis를 사용해서pub/sub 세팅 환경 설정을 하고 이 환경 설정 오브젝트를 서버로 전달하면 끝이 납니다. 간단한 MQTT 서버를 실행하기 위해서는 Node.js 코드가 필요 합니다

var server = require('./lib/server')
  , debug  = require('debug')('lelylan');

var ascoltatore = {
      type: 'redis',
      redis: require('redis'),
      db: 12,
      port: 6379,
      host: process.env.REDIS_HOST }
  , settings = {
      port: process.env.NODE_PORT || 1883,
      backend: ascoltatore };

var app = new server.start(settings);

app.on('published', function(packet, client) {
  debug('ON PUBLISHED', packet.payload.toString(), 'on topic', packet.topic);
});

app.on('ready', function() {
  debug('MQTT Server listening on port', process.env.NODE_PORT)
});
 

pub/sub 솔루션의일환으로 Redis를 사용 하는 것에 대해서 자문 해 본다면, FAQ의 Q1을읽어 보시기 바랍니다. 짧게 말하자면 Lelylan 에 통합되어있는 MQTT 서버와 microservices 들 사이의 채널 통신이 가능하게 하기 위해서 필요하다.

 

물리적인 오브젝트들의 인증

Mosca를 사용해서 클라이언트에 3가지 메서드를 정의하면 인증을 받을 수 있는 데, 각 메서드들은 특정한 클라이언트들을 위한 접근 가능한 토픽들을 제한 하는 데 사용 됩니다.

#authenticate
#authorizePublish
#authorizeSubscribe
 

Lelylan 내에서 클라이언트의 사용자이름과 패스워드를 확인 할 수 있는 인증 방법을 사용합니다.( authenticate method ). 만약 인증이 성공한다면 device_id가 클라이언트 오브젝트에 저장되고, 나중에 publish subscribe 기능 사용에 대한 인증을 받기 위해 사용됩니다.

var mongoose = require('mongoose')
  , debug    = require('debug')('lelylan')
  , Device   = require('../app/models/devices/device');

module.exports.authenticate = function(client, username, password, callback) {
  Device.findOne({ _id: username, secret: password }, function (err, doc) {
    if (doc) client.device_id = doc.id
    callback(null, doc);
  });
}

module.exports.authorizePublish = function(client, topic, payload, callback) {
  callback(null, client.device_id == topic.split('/')[1]);
}

module.exports.authorizeSubscribe = function(client, topic, callback) {
  callback(null, client.device_id == topic.split('/')[1]);
}
 

MQTT와Lelylan에 대해서 좀 더 많은 내용을 알고 싶다면 dev center를 확인해 보시기 바랍니다.

 

2. MQTT 서버의 Docker 화

Docker는 배포 시스템을 배치하기에는 최적의 도구 입니다. 이 프레임워크는 시스템 환경을 초기화 하는 데 사용 되는 설치 “레시피” 인 Dockerfile을 정의 함으로써, 클린 시스템 환경에서 코드를 독립 시킬 수 있도록 해줍니다.

 

Docker는 상품레벨의 시스템들을 배치해주는 굉장한 도구입니다.

Docker. An awesome tool to deploy production systems

좋습니다! 시작해보죠.

 

컨테이너 정의

우리의 애플리케이션의 컨테이너를 빌드하기 위해서는, 첫째로 Dockerfile 이라는 파일을 생성할 필요가 있습니다. 원하는 환경을 초기화 하기위해서 Docker를 사용하는 데 필요한 명령을 여기에 다 열거 해보기로 합니다.

# node image
FROM node:0.10-onbuild

# docker mantainer
MAINTAINER reggie

# add the files to load
ADD ./ .

# install all needed packages
RUN npm install

# expose port
EXPOSE 1883

# execute app.js
ENTRYPOINT ["node", "app.js"]
 

특정Node.js 버전(node:0.10-onbuild 부터)에서요청하는 MQTT 서버의 컨테이너를 생성하기 위한 Dockerfile내에 repo(ADD ./.)의모든 파일을 추가하고, 노드 패키지들을 인스톨하고(npminstall 명령), 1883 포트를 개방하고(EXPOSE1883), 마지막으로 노드 앱을 실행한다(ENTRYPOINT[“node”, “app.js”]). 그러면 설정이 끝난다.

 

실행하기

Dockerfile을 만들었다면, docker 컨테이너를 빌드 할 수 있습니다(만약 Docker 를 인스톨 하지 못했다면, 이렇게 해보자 – 현재 모든 플랫폼을 지원하고 있으며, 윈도우의 경우에도 마찬가지 입니다 ☺).

# 컨테이너 빌드하기
$ docker build -t lelylan/mqtt
 

다음과 같은 출력결과가 나오면 됩니다.

“Successfully built lelylan/mqtt”
 

컨테이너를 빌드 했다면, 실행 이미지를 사용해서 실제 실행을 다음과 같이 합니다.

docker run -p 1883:1883 -d lelylan/mqtt
 

환경 설정이 끝난 것 같군요!, 그럼 MQTT 서버로 요청을 날려보는 것으로 시작 해보기로 하겠습니다. Docker 를 사용하는 초기에는 컨테이너들과 이미지들 사이에서 약간 혼란이 있을 수 있습니다. 이 용어 들에 대해서 정확히 알고 싶다면,  단어 의 의미가 무엇인지 읽어 보시기 바랍니다.

# OSX
$ http://$(boot2docker ip):1883
# Linux
$ http://localhost:1883
 

OS X 를 사용하고 있다면, 실제Linux VM 으로 사용하는 boot2docker를 사용합니다. VM의 localhost를 접근하기 위해서 $DOCKER_HOST 환경 변수를 사용 할 필요가 있습니다만 그러고 싶지 않고 리눅스를 localhost로 사용 할 수 있습니다.

 

빈번히 사용하는 다른 명령어들.

Docker 사용법을 배우는 동안 사용했던 일련의 일반적인 명령어 리스트를 만들었습니다. 이 기본명령어이지만, 필요할 때마다 참조 하는 것이 좋을 듯 생각이 되네요.

 

컨테이너 관련 명령어.
# Tag 없는 컨테이너를 빌드하고 실행하는 명령어.
$ docker build .
$ docker run -p 80:1883 <CONTAINER_ID>
# Tag를 사용하는 컨테이너를 빌드하고 실행 하는 명령어.
$ docker build -t <USERNAME>/<PROJECT_NAME>:<V1>
$ docker run -p 80:1883 -d <USERNAME>/<PROJECT_NAME>:<V1>

Image 관련 명령어.
# Run interactively into the image
$ docker run -i <IMAGE_ID> /bin/bash
# Run image with environment variables (place at the beginning)
$ docker run -e "VAR=VAL" -p 80:1883 <IMAGE_ID>

# list all running images
$ docker ps

# List all running and not running images
# (useful to see also images that exited because of an error).
$ docker ps -a
Kill images

# Kill all images
docker ps -a -q | xargs docker rm -f

로그 관련 명령어
# See logs for a specific image
docker logs <IMAGE_ID>
# See logs using the tail mode
docker logs -f <IMAGE_ID>
 

 

3. 로드 밸런서로서 HAProxy 추가하기.

Docker 화 된 MQTT 서버는 물리적인 오브젝트(클라이언트)로부터 연결을 수신 할 준비가 되었다. 그 다음 부족한 부분은 아직확장성이 고려 되지 않았다는 것이다 ☺.

여기 HAProxy라고 알려진 유명한 TCP/HTTP 로드 밸런서이자, 성능향상사용하기도 하며, 서버 환경의 안정성과 다중 서버 사이의 사용량 분배에 사용되는 프록시 솔루션입니다. C로 작성되었으며, 빠르고 효과적인 서버라고 널리 알려 졌습니다.

 

용어

HAProxy를 알아보기 전에, 로드 밸런싱을 사용하려면 알아야 할 필요가 있는 개념들이 있습니다. 관심이 있으시면, Mitchell Anicas 이쓴 이 문서에서 많은 유용한 정보를 찾을 수 있습니다.

 

Access Control List (ACL)

 

ACL은 테스트 결과를 기반으로 어떤 조건을 테스트하고 액션(서버를 선택하거나, 요청을 블록 시키는 것 같은)을 실행 하는 것을 말합니다. ACL의 사용은 패턴 매칭이나 백엔드로의 연결 개수 같은 서로 다른 요소들을 기반으로 유연한 네트워크 트래픽을사용 하도록 만들어 줍니다.

# 만약 이 ACL이 사용자 요청을 /blog로 매칭 했다면,
# (http://example.org/blog/entry-1 의 요청에 대한 매칭은 다음과 같이 설정 할 것입니다)
acl url_blog path_beg /blog
 

Backend 백엔드

 

백엔드는 포워딩된 요청들을 수신하는 일련의 서버군을 말합니다. 일반적으로 말하자면, 백엔드 서버를 좀 더많이 추가하는 것은 기본 로딩 능력과 서버 상의 로드 분산에 의한 안정성을 증가 시킬 것입니다. 다음예는 80 포트에서 리스닝 되고 있는 두 대의 웹서버의 백엔드 환경 설정 입니다.

backend web-backend
   balance roundrobin
   server web1 web1.example.org:80 check
   server web2 web2.example.org:80 check
 

프론트 엔드

프론트 엔드는 요청을 어떻게 백엔드들로 포워딩 할것인지를 정의합니다. 프론트 엔드들은 HAProxy 환경설정의 frontend 섹션에 정의하며,IP 주소들과, ACL들과 백엔드들을 함께 설정 하도록 되어 있습니다. 다음 예제는, example.com/blog로 사용자 요청이 있을 때, blog 애플리케이션을 실행하는 서버 군들의 blog 백엔드로 포워딩 합니다. 다른 요청들은 그 다른 요청 애플리케이션을 실행 하고 있는 web-backend 로 포워딩 합니다.

frontend http
  bind *:80
  mode http
  acl url_blog path_beg /blog
  use_backend blog-backend if url_blog
  default_backend web-backend
 

 

이론은 그만! HAProxy 환경 설정을 해봅시다..☺

 

여기서 사용한 코드들은 Dockerfile로 정의한 Lelylan 상에서 HAProxy 서버를 실행하는 데 쓰이며, 요청들을 어떻게 조작 할것인가에 대한 환경 설정 파일입니다.

HAProxy를 실행하기 위해서 사용한 코드는 다음 Github 사이트에서 만나 볼 수 있습니다.

 

컨테이너 가져오기

공용 Docker Hub Registry에서부터 HAProxy컨테이너 다운로드를 시작 하려면 다음과 같이 합니다.

$ docker pull dockerfile/haproxy
 

여기서 HAProxy컨테이너는 실행됩니다.

$ docker run -d -p 80:80 dockerfile/haproxy
 

haproxy.cfg (커스텀 환경 파일)과 errors/ (커스텀 에러 응답)파일을 포함하고 있는 <override-dir> 디렉토리의 절대 경로에서 데이터 볼륨 옵션(아래 예제에서 보듯이) 환경 파일을 HAProxy 컨테이너가 적용합니다.

# Run HAProxy image with a custom configuration file
$ docker run -d -p 1883:1883 \
-v <override-dir>:/haproxy-override dockerfile/haproxy
 

HAProxy 환경 설정

다음은 1883 포트로들어오는 모든 요청에 대해서 leastconn balance 모드를 사용해서(서버최소 연결 수를 가진 서버를 선택) 두개의 MQTT 서버로(mosca_1 와 mosca_2) 포워딩 하도록 HAProxy가 리슨하고 있는 MQTT 서버들의 환경 설정입니다.

# Listen to all MQTT requests (port 1883)
listen mqtt
  # MQTT binding to port 1883
  bind *:1883
  # communication mode (MQTT works on top of TCP)
  mode tcp
  option tcplog
  # balance mode (to choose which MQTT server to use)
  balance leastconn
  # MQTT server 1
  server mosca_1 178.62.122.204:1883 check
  # MQTT server 2
  server mosca_2 178.62.104.172:1883 check
 

2. Github 에서Lelylan이 사용하는 최종 환경 설정 파일인 haproxy.cfg를 checkout 합니다. 1. ACL, HAProxy 소개 동안 백엔드와 프론트 엔드에 대해 설명 하였습니다. 이 솔루션은 백엔드와 프론트엔드를 사용하는 데 어떤 문제 때문에 사용하게 되었습니다. 만약 이 두 개념으로도 실행 되는 환경 설정을 찾게 되면, 저희에게도연락 부탁드립니다.

출처 입력

새로운 환경설정(개발중에 유용한)을 사용해보려면, 데이터 볼륨 옵션을 사용해서디폴트 환경설정을 오버라이드 합니다. 다음 예에서 /root/haproxy-override/에 정의되어 있는 환경 설정 파일, haproxy-override 를 오버라이드 했습니다.


$ docker run -d -p 80:80 1883:1883 \
-v /root/haproxy-override:/haproxy-override
dockerfile/haproxy
 

자신만의 HAProxy Docker 컨테이너 생성.

사용자 환경 설정이 끝나면, 이를 사용해서 HAProxy 컨테이너를 생성 할 수 있습니다. 단지 우리가 해야 할 일은 HAProxy 컨테이너를 로딩하기 위한/etc/haproxy/haproxy.cfg에 정의 되어 있는 환경 파일을(haproxy.cfg를 /etc/haproxy/haproxy.cfg 추가) Dockerfile로 교체하여 정의 하는일입니다 ( dockerfile/haproxy 에서).

그런 다음, HAProxy 서버를(CMD [“bash”, “/haproxy-start”]) 재 기동 하고 원하는 포트들을 활성화 합니다.(80/443/1883/8883).

# Container to start from with a working HAProxy definition
FROM dockerfile/haproxy
MAINTAINER Andrea Reginato <andrea.reginato@gmail.com>

# Add personalized configuration
ADD haproxy.cfg /etc/haproxy/haproxy.cfg

# Add restart commands
ADD restart.bash /haproxy-restart

# Define working directory
WORKDIR /etc/haproxy

# Define default command
CMD ["bash", "/haproxy-start"]

# Expose ports
EXPOSE 80
EXPOSE 1883
 

주의사항. HAProxy를 재시작 했다는 말은 단순히 시작했다는 것이 아니라 기본 HAProxy 컨테이너가 로딩될 때, HAProxy 는 이미 실행 되어 있습니다. 이말은 환경 설정 파일을 바꾸면, 이 환경 설정이 적용 되기 위해서 재 시작 해야 할 필요가 있다는 말입니다.

출처 입력

나머지 팁들

HAProxy 설정에 문제가 있다면 로그 파일을 읽어봐야 한다. HAProxy는 rsyslog를 사용하는 데, Ubuntu에서 기본으로 사용되는 로그 프로세싱의 초 스피드를 자랑하는시스템이다.

# HAProxy log configuration file
$ vi /etc/rsyslog.d/haproxy.conf
# Files where you can find the HAProxy logs
$ tail -f /var/lib/haproxy/dev/log
$ tail -f /var/log/haproxy.log
 

 

4. SSL로 MQTT 보안 구성하기

이제 우리는 모든 요청을 두 개 이상의 MQTT 서버들과 연동되어 있는 HAProxy 에서 받게 되는 확장성MQTT인프라를 가지게 되었습니다. 그럼 다음 단계로 SSL을 사용한 보안 연결을 만들어야 할 차례가 되었습니다.

 

2014년 6월에 릴리즈 된 안정화 버전인 HAProxy 1.5.x는 네이티브 SSL 지원을 구현하였습니다.

 

SSL이란?

.SSL(SecureSockets Layer) 은 비밀보장과 통합을 위해서 클라이언트와 서버사이에서 통신 되는 모든 데이터가 암호화 되어통신 될 수 있도록 해 주는 표준이다.

 

PEM SSL을 합친 증명서/키 파일 생성하기

 

첫째로 SSL 증명서가필요합니다. SSL을 사용하는 HAProxy를 구현하기 위해서는, SSL 증명서와 키 쌍이 적정한 형식으로 만들어져야 합니다: PEM.

대부분의 경우 SSL증명서(인증기관으로부터 발급된 .crt 나 .cer 파일)와그에 상응하는 개인키(당신이 생성한 .key 파일)을 조합하면 됩니다. 증명서 파일이 lelylan.com.crt 라고가정하고 개인키는 lelylan.com.key 라고하겠습니다.

여기서 이 두개의 파일을 조합해서 lelylan.com.pem 라는 PEM파일을 생성합니다.

cat lelylan.com.crt lelylan.com.key > lelylan.com.pem
 

항상말하듯이, PEM 파일을(개인키를 포함하고 있음) 포함한, 개인 키 파일의 어떤 복사본이라도 보안을 유지해야 합니다.

 

Docker 볼륨을 이용한 PEM 파일 로딩

 

SSL 증명서를 생성하고 나면, 우리는 공개된 리포지터리에 저장 할 수 없습니다.

아시다시피 보안이슈 때문이죠. 데이터 볼륩들을 통한 Docker 형식으로 접근 할 수 있도록 HAProxy 서버를 생성해야만 하는 것이 최종 목표입니다.

 

데이터 볼륨이란?

 

데이터 볼륨은 특별히 – 데이터를 공유하기 위한 하나 이상의 특징을 공급하기 위한 컨테이너를 가진 지정 디렉토리입니다. 어떤 파일/폴더를 공유하기 위해서는 –v를 사용하여 컨테이너를 데이터 볼륨에 추가하고, -v 옵션은다중 데이터 볼륩들을 마운트 시키기위해서 여러 번 사용 할 수 있습니다.(우리는 HAProxy 컨테이너를 위해서 환경 설정 파일을 로딩하는 데 이미 이를 사용 했습니다)

 

SSL 증명서를 공유하기 위해서 데이터 볼륨들을 사용하기

SSL 증명서를 공유하기 위해서, 우리는 Docker 컨테이너가 실행될 때, /certs 폴더를 통해서 접근 가능할 수 있도록 /certs (HAProxy 서버 내에) 폴더에위치 시킵니다.

$ docker run -d -p 80:80 -p 443:443 -p 1883:1883 -p 8883:8883 \
-v /certs:/certs
-v /root/haproxy-override:/haproxy-override
 

포트8883을 개방해 놓는 것을 잊지 말아야 합니다( 기본 포트는보안 MQTT 연결들을 위해서 만들어 논 것입니다)

 

증명서 로딩하기.

Docker 데이터 볼륨을 통해 사용가능한 SSL 증명서를 만들었으면, 우리는 HAProxy 환경 설정 파일을 통해서 이 증명서에 접근가능합니다. 우리가 해야 할 일은 lelylan.pem 이라는 이름의 파일과 /certs 내에 SSL 증명서를위치 시켜 8883 포트로 들어오는 요청을 합치는 코드를 추가 하는 것입니다.

# Listen to all MQTT requests
listen mqtt
  # MQTT binding to port 1883
  bind *:1883
  # MQTT binding to port 8883
  bind *:8883 ssl crt /certs/lelylan.pem
  ...
 

설정 끝!

자 이렇게 되면,우리는 고가용성 및 보안성을 갖춘 IoT 용 MQTT 클러스터를가지게 되었습니다. 아래는 최종 결과물에 대한 이미지입니다.

 

로드 밸런서로 연결된 기기들의 모든 연결(커넥션)들은 두대의 MQTT 서버로 포워딩 되어 집니다. 위의 이미지들은 HiveMQ 의 것입니다.

여기에서의 내용은,아키텍쳐를 완성하는 한가지가 있다는 것입니다: 우리는 이 서버들을 배치하기위한 간단한 방법이필요하다는 것 바로 그것입니다.

 

5. 배치 워크플로우 자동화를 위한 nscale 환경 설정.

이를 가능하게 하기 위해서, 우리는 오픈소스 프로젝트를 연결 된 컨테이너 군을 배치, 빌드하고환경설정 할 수 있도록 nscale를 사용 할 것입니다.

Nscale 에서사용하는 가장 중요한 명령어들을 살펴보겠습니다. Nscale이 어떻게 동작하는 지에 대한 단계적인 가이드문서는 여기 에서 찾을 수 있습니다.

출처 입력

이 모든 것을 어디에 배치 할 수 있을까요?

 

Digital Ocean은 개발자들을 위해서 만들어진 단순 클라우드 호스팅입니다. 우리의 배치 솔루션을 위해서 Ubuntu 기반의 Docker가 인스톨 되어 있는 모든 droplet들을 사용 할 것입니다.

 

계정이 없다면 this link 에서 10$로가능합니다.

 

첫번째로 할 일은5개의 droplet 들을 생성하는 것입니다. 각droplet은 특정 앱 전용입니다: 1 management machine (nscale 로직이 들어가는),1 HAProxy 로드 밸런서, 2 MQTT Mosca 서버들과 1 Redis 서버 입니다.

사진 삭제

사진 설명을 입력하세요.

이 튜토리얼에서 Digital Ocean 내에서 생성한 Droplet 리스트

 

Installing nscale. nscale 설치하기

DigitalOcean 내에서 정의된 managementmachine 으로 nscale을 설치할 준비가 되었습니다. 우리는 여기서 로컬 머신도 사용 할 수도 있지만, 이를 위한 전용서버를 가지고 있다면 모든 팀 구성원들이 새로 변경 분을 배치 하는 것을 보여주기 위해서 단순하게 가져 갈 것입니다.

 

설치

InstallNode.js via nvm (Node Version Manager). nvm (Node Version Manager) 를 통해서 Node.js를 설치합니다.

curl https://raw.githubusercontent.com/creationix/nvm/v0.18.0/install.sh | bash
 

로그오프, 로그인그리고 다음 명령을 실행 합니다.

# install needed dependencies
apt-get update
apt-get install build-essential

# install node and npm
nvm install v0.10.33
nvm alias default v0.10.33
npm install npm@latest -g --unsafe-perm

# install nscale
npm install nscale -g --unsafe-perm
 

설치 시간은 좀 걸려요, 그리고 일반적으로 그래요☺

 

Github 사용자 환경 설정

nscale을 사용하기 위해서는 GIT 환경 설정이 필요합니다.

git config --global user.name "<YOUR_NAME>"
git config --global user.email "<YOUR_EMAIL>"
 

nscale 프로젝트 생성

 

환경설정이 끝나면,nscale에 로그인 합니다.

$ nsd login
 

이 부분에서 name 과 namespace 를 설정을 통해서 우리의 최초 nscale 프로젝트를 생성 할 수 있습니다.(이 두개는 같은 이름으로설정 할 것입니다).

$ nsd sys create
 
  1. Set a name for your project: <NAME>
  2. Set a namespace for your project: <NAMESPACE>

이 명령어는 자동으로 다음 폴더 구조대로 프로젝트를생성 할 것입니다(아래 보는 모든 파일들에 대한 내용은 걱정하지 마십시오; definition/services.js 와 system.js 만 우리가 집중 해야 할 파일 입니다)

|— definitions
| |— machines.js
| `— services.js *
|— deployed.json
|— map.js
|— npm-debug.log
|— README.md
|— sudc-key
|— sudc-key.pub
|— system.js *
|— timeline.json
`— workspace
...
 

이 부분에서, list명령어를 사용해서 새로운 nscale 프로젝가 실행되었는 지 여부를 알아 볼 수 있습니다. 모든 것이 제대로 실행되었다면, 프로젝트 이름과 아이디를 볼 수있을 것입니다.

$ nsd sys list
Name Id
lelylan-mqtt 6b4b4e3f-f22e-4516-bffb-e1a8daafb3ea
 

보안 접근(nscale에서 다른 서버까지의)

nscale 에서 환경 설정 할 모든 서버들에 접근은, 암호가 필요 없는 보안 인증 솔루션을 위한새로운 ssh 키가 필요 합니다.

ssh-keygen -t rsa
 

비밀번호 없이 위와 같이 명령어를 입력하고, 프로젝트 이름으로 저장합니다. 우리의 경우, lelyan 으로 합니다 – key (~/.ssh/ 가 아니라 새로운 ssh 키는 nscale 프로젝트에생성 해야 한다는 것에 주의 해야만 합니다). ssh 키가 생성되었다면, nscale에서 환경 설정이 필요한 모든 서버들내에 public 키를셑업합니다: haproxymosca 1mosca 2 그리고 redis.

DigitalOcean dashboard를 통해서 이 작업을 하거나, 다음 명령어를사용해서 authorized_keys 에 nscale public 키를 추가 할 수 있습니다.

cat lelylan-key.pub | \
ssh <USER>@<IP-SERVER> "cat ~/.ssh/authorized_keys"
 

만약문제가 있다면, 첫번째로 SSH를 통해서 서버로 연결 합니다.

ssh <USER>@<IP-SERVER>
 

SSH Agent 포트 포워딩.

managementserver에 한가지 더 해야할 일은(nscale 프로젝트가정의되어 있는 곳) SSH Agent Forwarding 를 설정 하는 것입니다. 이는 서버에 키들을저장하는 대신에 로컬 SSH 키를 사용 할 수 있도록 도와 줍니다.

# ~/.ssh/config
Host *
  ForwardAgent yes
 

다음은 이 절차에 대한 nscale의 open issue 입니다. 만약 이 절차를 진행하지 않으면 nscale을 사용해서 배치를 하지 못할 것입니다.

 

nscale 환경설정

지금부터 nscale의환경 설정을 할 수 있습니다. 원하는 머신에 접근하는 데 사용되는 인증 정보 세팅을 정해주는 nscale analyzer부터 시작합니다. 특정 오브젝트를 세팅하기위해서 다음과 같이 ~/.nscale/config/config.json 파일을 조절합니다.

{
  ...
  "modules": {
    ...
    "analysis": {
      "require": "nscale-local-analyzer",
      "specific": {
      }
    }
    ...
}
 

를 다음으로:

{
  ...
  "modules": {
    ...
    "analysis": {
      "require": "nscale-direct-analyzer",
      "specific": {
        "user": "root",
        "identityFile": "/root/lelylan/lelylan-key"
      }
    }
  }
 

프로젝트이름과 키가 틀리다면 위의 환경설정을 조정하면 됩니다.

우리가 단지 하는 것은 user (root) 와 identity 파일로(ssh key) 특정 오브젝트를 생성 하는 것입니다.(이 단계는 다음 릴리즈를 위해서 필요한 것 처럼 보이지 않을 것입니다)

 

프로세스 정의

nscale 내에서 우리는 서로 다른 프로세스들을 정의 할 수 있습니다. 이때 모든 프로세스들은 이름이아이디인 Docker 컨테이너, Github 리포지터리(컨테이너 소스코드) 그리고 이미지를 실행하기 위해서 사용되는 Docker 아규먼트 입니다.

exports.mqtt = {
  type: 'process',
    specific: {
      repositoryUrl: 'git@github.com:lelylan/mqtt.git',
      execute: {
        args: '-e "DEBUG=lelylan" -e "REDIS_HOST=178.62.31.9" -p 1883:1883 -d'
      }
    }
  };

exports.haproxy = {
  type: 'process',
    specific: {
      repositoryUrl: 'git@github.com:lelylan/haproxy-mqtt.git',
      execute: {
        args: '-p 80:80 -p 1883:1883 -p 8883:8883 -v /certs:/certs/ -d'
      }
    }
  };

exports.redis = {
  type: 'process',
  specific: {
    name: 'redis',
    execute: {
      args: '-d -p 6379:6379'
    }
  }
};
 

redis 가Github 리포지터리를 가지고 있지 않다면, 축하드려요! 이 문서에서는 안된다는 말일 수도 있습니다 ☺. Redis 에서 Docker Hub에 정의된 redis 이미지를 직접 사용하는 Github 리포지터리가 필요 없습니다.

이 경우 우리는3개의 서로 다른 타입의 프로세스를 가집니다: haproxy,mqtt 그리고 redis

 

시스템 정의

지금 우리는 system.js 파일을 정의하는 것으로 nscale 에 Digital Ocean에 생성 되어야만 하는 각 프로세스를정의하고, 실행하기 원하는 프로세스들을 정의 하였습니다.

xports.name = 'lelylan_mqtt';
exports.namespace = 'lelylan_mqtt';
exports.id = '6b4b4e3f-f22e-4516-bffb-e1a8daafb3ea';

exports.topology = {
  local: {
  },

  direct: {
    machine$1: {
      contains: ['redis'],
      specific: {
        user: 'root',
        identityFile: 'lelylan',
        ipAddress: '178.62.31.9'
      }
    },
    machine$2: {
      contains: ['haproxy'],
      specific: {
        user: 'root',
        identityFile: 'lelylan',
        ipAddress: '178.62.108.47'
      }
    },
    machine$3: {
      contains: ['mqtt'],
      specific: {
        user: 'root',
        identityFile: 'lelylan',
        ipAddress: '178.62.122.204'
      }
    },
    machine$4: {
      contains: ['mqtt'],
      specific: {
        user: 'root',
        identityFile: 'lelylan',
        ipAddress: '178.62.104.172'
      }
    },
  }
};
 

보시다시피, system.js 은 모든 머신 셑업 부분을 정의 합니다. 각각의 머신에 우리는 실행 프로세스들을 정의합니다(앞서 services.js 에 정의되어진것들 중에 하나를 사용 할 필요가 있습니다)- 머신 IP 주소, 로그인 사용자와 접근을 위한 인증 ssh 키 이름이 그것입니다.

 

새로운 MQTT 서버 추가를 원한다면?

Nscale의 system.js 정의에 새로운 머신을 추가합니다. 새로운 서버를 HAproxy환경설정으로 등록하면, 사용할 준비가 된 것입니다.

 

이제 배치 할 시간 ☺.

 

우리는 이제 우리의 구조대로 컴파일, 빌드하고 배치 할 수 있습니다.

# Compile the nscale project
nsd sys comp direct
# Build all containers
# (grab a cup of coffee, while nscale build everything)
nsd cont buildall
# Deploy the latest revision on Digital Ocean
nsd rev dep head
 

잘 했어요!

위의 세개의 명령으로 셑업이 끝나면, 우리는 새로운 MQTT 서버들을 추가하고 우리의 시스템 구조를 수분내에확장 할 수 있는 IoT를 위한 고가용성 MQTT 클러스터를배치 할 준비가 되었습니다.

 

결론

이 문서는 Lelylan을만들면서 나온 일련의 작업과정에서 나온 것입니다. Lelylan은IoT를 위한 오픈소스 클라우드 플랫폼입니다. 이 문서가 유용하다고 판단 하신다면 우리Github에 스타를 달아주시면 감사하겠습니다(개발자를 모으는 데 도움이 될 것입니다.)

 

 

이 문서에서 우리는 IoT를 위한 고가용성 MQTT 클러스터를 만드는 방법을 살펴보았습니다. 여기 제품에 사용된 모든 코드는 다음의 오픈소스들과 함께 릴리즈 되고 있습니다.

 

  • LelylanMQTT HAProxy (TCP load balancer)
  • LelylanMQTT Server (Mosca implementation)

 

우리는 또 nscale프로젝트를 조만간 릴리즈 할 것입니다.(지금은 리포지터리에서 제거 해야할 필요가 있는 몇몇민감한 정보를 가지고 있어서 힘듭니다만)

 

nscale 과 MQTT 기반환경에 대한 어떤 질문에 대해서도 답변해주고 도와준nearForm와 Matteo Collina에게 무한한 감사를 보냅니다(이들은 nscale 팀원이며 Mosca개발자입니다)

기반환경 빌딩, 테스팅그리고 보안 사항들에 대한 환경을 만들기 위해서 몇 달의 시간이 필요 했습니다. 우리는 이 내용들을오픈소스 프로젝트로서 릴리즈 함으로써 빠른 시간내에 MQTT 플랫폼을 빌드 할 수 있도록 도와주는 지름길이되기를 희망합니다.

 

더 알고 싶나요?

충분하지 않다고 생각하십니까? 우리가 이야기 했던 몇몇 이야기들에 대해서 좀 더 공부하고 싶다면 다음 문서들을 읽어 보십시오!

 

  • An Internet of Things System — How to Build It Faster
  • Building a two node high availability MQTT cluster withHiveMQ
  • An Introduction to HAProxy and Load Balancing Concepts
  • How To Implement SSL Termination With HAProxy on Ubuntu
  • NscaleWorkshop
  • Playingwith MQTT

 

이상.

 

 

 

728x90