• Homebridge로 미지원 IoT 기기 HomeKit 제어하기

    AliExpress에서 몇개월 전 할인에 혹해 충동구매한 Yeelight의 스마트 컬러 LED 전구가 해를 넘어서도 책장 위에서 먼지만 쌓이고 있는 것을 보고, 드디어 사용할 마음을 먹었다. IKEA에서 싸고 이쁜 플로어스탠드 하나를 주문하고 잠깐 생각해보니, 조명의 제어가 걱정되기 시작했다.

    물론 Yeelight 측에서 기본적으로 제공하는 앱을 통해 제어가 가능하지만, 조금 더 힙하고 맛깔나는 방법을 원했다. 마치 Apple의 유튜브 광고에 나오는 이런 것을 말이다. 아쉽게도, 내가 구매한 이 조명은 Apple에게 어떠한 HomeKit 인증도 받지 못했지만, 지금이 어떤 세상인가. Homebridge라는 HomeKit 에뮬레이션 서버가 필자를 구재할 것이니.

    나는 편법이 좋아요

    Homebridge는 Node.js 기반으로 만들어진 홈킷 에뮬레이션 서버다. 다시 말해, Apple HomeKit 액세서리 인증을 받지 못한 IoT 기기라도 이 Homebridge를 통해 HomeKit으로 제어할 수 있다는 소리다. IoT를 좋아하는 친구에게 이전에 잠깐 말은 들어본 적 있지만, 직접 검색해보니 생각보다 정말 다양한 플러그인들이 존재했다. 물론 필자가 사용하려 하는 Yeelight 컬러 전구를 위한 플러그인도 있다. 망설일 게 뭔가. 당장 실천에 옮기자.

    이 글에서는 Yeelight만 예시로 다루지만, 플러그인만 존재한다면 비슷한 과정을 거쳐 얼마든지 다양한 기기를 연결할 수 있다. 필자가 다른 기기들도 가지고 있었다면 다뤄봤을 수도 있겠지만, 안타깝게도 필자의 주머니는 얇다 :(

    필자는 추가적인 자원 소모를 막기 위해, 이미 로컬넷에서 작동하고 있던 라즈베리 파이에 Docker 컨테이너 형태로 Homebridge를 구동했다. 미리 빌드된 이미지가 존재하니, 아래 명령어를 통해 바로 컨테이너를 띄워보자. 네이티브 구동을 원할 경우 위 Homebridge GitHub 링크를 타고 들어가 README를 참고하면 된다.

    라즈베리 파이가 아닌 일반 x86 기기에서 구동 시, 이미지 이름 뒤의 :raspberry-pi만 지우면 된다. 알아서 x86용 :latest 이미지를 받아올 것이다.

    docker run \
      --net=host \
      --name=homebridge \
      -e PUID=1001 -e PGID=1001 \
      -e TZ=Asia/Seoul \
      -v </호스트/연결/경로>:/homebridge \
      oznu/homebridge:raspberry-pi
    

    홈 앱과의 연결을 위해 컨테이너는 호스트 네트워크에 직접 연결시켜 줬다. PUID와 PGID는 기본 Docker 유저, 그룹을 뜻하는 1001, 타임존은 서울로 설정하면 된다. 편리한 Homebridge 설정을 위해 원하는 호스트의 특정 경로를 컨테이너의 /homebridge와 마운트하자.

    컨테이너가 정상적으로 켜졌다면 아래와 같은 QR 코드가 콘솔창에 나타날 것인데, 애플 기기에서 홈 앱을 실행해 액세서리 추가 메뉴에서 이 비슷한 QR코드를 스캔하자.


    Homebridge가 홈 앱에 정상적으로 추가되었다면, 다시 콘솔로 돌아와 Yeelight 플러그인을 설치해야 한다. 다행히 복잡하지는 않고, docker exec homebridge yarn add homebridge-yeelight 한 줄로 직접 컨테이너에 명령을 보내 플러그인을 간단히 설치할 수 있다.

    플러그인도 설치했으니 이제 Yeelight를 정상적으로 인식할 수 있도록, 장비 목록에 추가만 해주면 된다. /homebridge 경로와 마운트했던 호스트 위치로 이동해보자. 이 경로에는 Homebridge 앱의 모든 설정 파일들이 동기화된다. config.json 파일을 열어, 아래와 같이 편집하자. 상단의 브릿지와 관련된 설정은 무시하고, 하단 platforms만 건들면 된다.

        "platforms": [
            {
                "platform" : "yeelight",
                "name" : "yeelight"
            }
        ]
    

    여기까지 하면 다 된 것 같은데, 귀찮은 과정이 하나 더 남아있다. Yeelight가 Homebridge와 통신할 수 있게, Yeelight의 개발자 모드를 켜줘야 한다. 하다하다 이젠 전구의 개발자 모드라니. 직접 타이핑하면서도 뭔가 어색하다.

    Yeelight 앱을 통해 전구와 연결된 스마트폰에서, 하단 설정 메뉴를 누르면 ‘긱 모드’라는 버튼이 하나 보일 것이다. 살짝 밀어 켜주면 된다. 이전 버전에서는 개발자 모드라고 제대로 출력이 되었는데, 나름 센스를 부려 Geek 모드로 개명을 시킨 것 같다. 맞는 말이긴 하다. 전구에서까지 개발자 모드를 키고 있는 사람들이 Geek이 아니고 무엇이겠는가.

    필자의 경우 처음부터 손에 바로 잡히는 거리에 Android 기기가 있어 Android용 Yeelight 앱에서 진행했는데, 해외 포럼에서 iOS용 Yeelight 앱은 긱 모드 버튼이 보이지 않는다는 말도 오가는 것 같다. 진행에 참고하길 바란다.


    여기까지 따라왔으면, 정말 다 끝났다. 전구를 켜둔 상태로 아까 설정을 마친 Homebridge 컨테이너를 재시작하면, 자동으로 Yeelight를 감지해 서로 연결될 것이다. 못 미더우면 콘솔 로그를 들여다보자. 애플 홈 앱에서도 알아서 새로 추가한 전구가 보일 건데, 혹시라도 계속 팝업이 안될 경우 Homebridge를 홈 앱에서 지웠다 다시 추가해보자. 다시 잘 보일 것이다.

    홈에서 지운 후 다시 추가 시도 시 ‘이미 추가된 액세서리입니다.’ 와 같은 메시지가 뜨며 진행이 막힐 수도 있다. 이럴 경우 /homebridge 경로의 accessories 폴더와 persist 폴더를 삭제한 후 다시 Homebridge를 시작해보자. 필자도 이렇게 오류를 해결했다.

    시리야, 불 꺼.



    Yeelight 앱에서와 같이, 홈 앱과 시리를 통해 전원부터 빛 색깔, 밝기까지 모두 컨트롤 가능한 것을 확인할 수 있다. 여기서 혹시 외부 네트워크에서의 기기 원격 제어는 어떻게 하냐 물어본다면, iPad나 Apple TV를 구매하시라고 미리 답을 드린다.

    농담이 아니라, 이 두 기기는 Apple이 직접 권장하는 (그리고 유일한) HomeKit 중계 서버로 동작하며, 여러가지 자동화나 외부 연결도 이 녀석들을 통해야만 가능하다. 배보다 배꼽이 더 커질 것 같다. 아직 필자는 스탠드 조명 하나만 연결되어 있기에 로컬넷 한정 제어로도 만족한다. 여차하면 구형 iPad를 가지고 있으니 활용해볼 수도 있을 것 같고 말이다.

    무엇보다 이제 당당히 외칠 수 있다.

    시리야, 불 꺼.

  • Docker Swarm으로 프로덕션 아키텍처 세우기

    2010년대 중반 백엔드 업계는 일종의 ‘클러스터 붐’을 맞았다. Docker를 비롯해 비교적 사용이 간편하며 쉽게 규모를 확장할 수 있는 도구들이 등장하고, AWS나 GCP 등 대표적 클라우드 서비스 제공자들도 비슷한 기능들을 자체적으로 제공하기 시작하면서 전 세계 수많은 기업들이 자신들의 서비스를 클러스터화 시키기 시작했다. 당장 필자도 이전에 Docker가 기본으로 제공하는 Swarm 기능을 통해 간단히 컨테이너를 클러스터화 하는 방법에 대해 다뤄본 적이 있지 않은가.

    그럼, 왜 그럴까? 모바일 기기와 초고속 인터넷의 폭발적인 보급으로 온라인 서비스 접속 수요도 그만큼 높아지고 있는데, 이를 쉽게 해결해줄 도구들이 잔뜩 등장하고 있으니 다들 물 만난 물고기가 된게 가장 표면적인 이유가 아닐까 싶다.

    그런 흐름에 맞춰, 이번 글에서는 점점 더 높은 수요를 안정적으로 감당해야 하는 프로덕션 상황을 위해 로컬 Swarm 클러스터와 외부 퍼블릭 클라우드가 함께 동작하는 하이브리드 아키텍처를 직접 세워보려 한다.

    알맞은 도구의 선택?


    혹시 이 글을 읽기 전 Docker를 이용한 클러스터화, 더 나아가 서비스 인프라의 오케스트레이션을 위해 사용할 수 있는 도구들을 미리 알아본 적 있다면 백이면 백 그 중 어떤 것을 골라야 잘 골랐다고 소문이 날지 고민하고 있을 것이다.

    대표적으로 Google의 Kubernetes와 Docker에서 자체적으로 제공하는 Swarm 두 개가 도마 위에 오를 것 같은데, Swarm은 Docker에서 아무것도 추가 설치하지 않아도 기본적으로 제공한다는 누구도 이길 수 없는 막강한 장점이 있다. 이는 관리 명령어나 서비스 구조 등도 단일 Docker 머신과 크게 다르지 않다는 뜻이 되기도 한다.

    사실 Kubernetes가 이것저것 지원하는 것도 많고, 실제 사용하고 있는 기업도 더 많지만 한정된 시간과 자원 안에 비슷한 결과물을 만들기에는 Swarm이 최고라 생각한다. 당연히 Swarm도 나름 프로덕션급 오케스트레이션 도구임을 표방하고 있고, 점점 사용자도 늘어나고 있어 세팅의 간편함을 제외하더라도 딱히 어딘가 떨어지거나 하지 않는다. 우리 모두의 행복한 삽질을 위해, Swarm으로 밀고 나가보자.

    Swarm 클러스터 구성

    위에서 침이 마르도록 칭찬했지만, Swarm 클러스터 구성은 입에서 Easy Peasy Lemon Squeezy가 튀어나올 정도로 쉽고 편하다.

    아래가 필자의 테스트 셋업인데, 본인의 상황에 맞게 적당히 응용하면 된다.

    빠르고 편한 세팅을 위해 Subicura 님의 미리 빌드된 Vagrant 이미지를 활용했다. Swarm 클러스터 구성 방법 자체는 필자의 이전 글을 참조 바란다.


    core-01 은 Manager, 나머지 core-02core-03은 Worker 노드다. 프로덕션 상황을 감안하여 웹 서버와 DB가 함께 구동되는 WordPress를 테스트 애플리케이션으로 사용하고, 추가로 원활한 서비스를 위한 traefik 리버스 프록시와 관리 편의를 고려해 Portainer까지 같이 구동한다. 만약 본인의 팀 / 회사에서 구동 예정인 애플리케이션이 따로 존재할 경우, 중간 과정을 대체하거나 따로 수정해도 무방하다.

    사전 작업

    원하는 애플리케이션을 구동하기 전에, 위에 잠깐 언급한 리버스 프록시와 편의장치 먼저 세팅하는 편이 좋다. 특히 리버스 프록시의 경우 미리 설정하지 않으면 애플리케이션을 구동해도 외부와 통신할 통로 마련이 안되니, 당연하기도 하지만 말이다.


    Swarm 환경이 제대로 구축되었다면, Manager 노드에서 아래 명령을 통해 Portainer를 구동하자.

    Portainer에 관련된 자세한 정보는 공식 홈페이지와 필자의 이전 글을 참고하면 된다.

    docker service create \
    --name portainer \
    --publish 9000:9000 \
    --replicas=1 \
    --constraint 'node.role == manager' \
    --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
    --mount type=bind,src=//opt/portainer,dst=/data \
    portainer/portainer \
    -H unix:///var/run/docker.sock
    

    여기서 주의할 점이, Portainer는 무조껀 Manager 노드에서만 구동해야 한다는 점이다. 명령어를 읽어보면 알겠지만, 작동에 Docker 데몬과의 연결이 필요하다. Swarm에서의 Worker 노드는 말 그대로의 Slave일 뿐, 서비스나 클러스터의 제어 작업 등은 불가하니 이런 서비스의 경우 애초에 실행 자체가 막혀버린다.

    이후 웹 브라우저를 통해 9000 포트로 접속하면 Portainer의 웹 콘솔 화면이 나온다. 물론 애플리케이션 구동에 필수는 아니지만, 전체 클러스터 구성 상태 확인이나 실행중인 서비스 제어 등 CLI보다 편리한 인프라 관리가 가능해지니 애용하면 삶이 조금 더 편해지지 않을까.


    다음은 traefik이다. 물론 더 유명하고 당장 사용할 수 있는 리버스 프록시들은 많이 존재하지만, traefik은 가볍고 컨테이너 환경에 최적화된 힙스터같은 녀석이라 이번 기회에 손을 벌려봤다. 실제로 프로덕션 환경에서도 잘 사용되는 소프트웨어니, 크게 걱정하지는 않아도 된다. 물론 모험 없이 nginx 등을 사용해도 무방할 것 같다.

    아래 명령을 통해 traefik을 구동해보자. 마찬가지로 traefik도 Docker 데몬과의 연결을 필요로 하기에, Manager 노드에서만 구동된다.

     docker service create \
        --name traefik \
        --constraint=node.role==manager \
        --publish 80:80 --publish 8080:8080 \
        --mount type=bind,source=/var/run/docker.sock,target=/var/run/docker.sock \
        --network traefik-net \
        traefik \
        --docker \
        --docker.swarmmode \
        --docker.domain=traefik \
        --docker.watch \
        --web
    

    명령어를 자세히 들여다보면 traefik-net 이라는 새로운 네트워크를 생성하는 것을 알 수 있다. 이는 기존의 ingress 네트워크와는 별도로, 구동할 애플리케이션이 리버스 프록시를 거쳐 외부와 통신하게 해줄 독립 네트워크다. 추후 애플리케이션 구동 시 따로 네트워크를 traefik-net으로 잡아줄 것이니, 미리 알고 넘어가자.

    각설하고, 웹 브라우저의 8080포트로 접속하면 아래와 같은 웹 콘솔에 접근할 수 있다. 애플리케이션을 구동하면 Providers 메뉴에 관련 정보가 표시되고, Health 메뉴에서 HTTP 상태 코드나 평균 응답 시간이 예쁜 그래프를 입고 나타날 것이다.


    잠깐 다음으로 넘어가기 전에, 우리 traefik이 미친 쥐인지, 애완용 햄스터인지 확인 한번 하고 넘어가면 정신건강에 좋을 것 같은데 말이다. traefik 메뉴얼에서 직접 권장하는 테스트용 웹 서비스인 whoami를 통해 접속 테스트를 진행하자. 아래 명령어를 사용하면 된다.

    docker service create \
        --name whoami0 \
        --label traefik.port=80 \
        --label traefik.frontend.rule="Host:<Manager_노드의_IP주소>"
        --network traefik-net \
        emilevauge/whoami"
    


    다시 traefik 콘솔을 살펴보자. Providers 메뉴에 방금 실행한 whoami0 서비스가 나타났을 것이다. 웹 브라우저로 80 포트에 접속할 경우 정상적으로 whoami0 서비스까지 닿을 수 있다.


    다행히 애완용 햄스터였다 :)

    데이터 보존에 관하여

    이제야 원하는 애플리케이션을 구동하는 차례인가! 하면 아쉽게도 아직 아니다. 중요한 일이 조금 더 남았다. 많은 애플리케이션은 데이터를 저장할 DB, 그리고 기타 쌓이는 자료를 위한 별도의 저장공간을 요구한다. 이런 녀석들을 Stateful 애플리케이션 이라고도 하는 것 같던데, 컨테이너 기술은 별도 기록 / 의존 데이터가 필요없는 Stateless 애플리케이션을 우선적으로 고려해 디자인되었다. 물론 Docker가 제공하는 로컬 볼륨 기능이나, 컨테이너 내부 RW 공간이 존재는 하지만, 이 방법으로는 Swarm 상태에서 다른 컨테이너와 데이터 공유가 불가하고, 특히 내부 RW 공간을 사용했을 경우 기존 컨테이너를 삭제할 시 데이터까지 그대로 소멸되어 버린다는 치명적인 단점이 존재한다.

    이러한 문제는 Swarm이 등장한 초창기부터 지금까지 끊임없이 논의되고 있고, 사용자가 Swarm의 여러 장점들을 모두 포기하더라도 Kubernetes로 넘어가는 가장 큰 이유가 되기도 하는데(Kubernetes의 경우 자체적인 저장공간 오케스트레이션을 지원한다), 다행히 CephGluster같은 분산 파일 시스템을 마운트해 사용하거나, Flocker, REX-Ray같은 Docker 볼륨 드라이버 / 플러그인이 개발되는 등 다양한 해결책이 마련되어 있다.

    이번과 같은 테스트, 또는 소규모 환경에서 Ceph 등을 사용하는 것은 배보다 배꼽이 더 큰 행위라 생각해 제외하고, Docker 플러그인 형태로 제공되는 REX-Ray와 AWS의 S3, RDS 서비스를 이용해 어느 컨테이너에서나 접근할 수 있는 저장 공간, 그리고 DB를 구현하려 한다.

    사실 Flocker의 경우 공유 볼륨을 위한 별도 서버가 필요 없다는 장점 때문에 가장 먼저 사용해보려 했으나, 이를 개발한 ClusterHQ 팀이 최근에 셧다운되어 대부분의 자료가 사라지는 바람에 더 접근이 용의한 REX-Ray를 선택했다. 다행히 팀은 셧다운됬어도 Flocker의 개발은 계속 이어간다는 것 같으니 조만간 따로 사용해본 후 글을 남기도록 하겠다.

    가장 먼저, 우리의 Swarm 시스템이 AWS 자원과 통신할 수 있도록 API 키를 발급받아야 한다. AWS는 IAM이라는 접속인증 관리 서비스를 제공한다. 당장 우리가 필요한 것은 S3 저장소의 접근이니, 별도 사용자 추가를 눌러 AmazonS3FullAccess 권한을 먹여주면 된다. 물론 모든 서비스 접근이 가능한 root 키도 존재하지만, 보안을 생각해 사용하지 않는다.

    계정 생성 후 나오는 엑세스 키는 잘 모셔두고, 이제 Swarm 시스템에 플러그인을 설치해 보자. REX-Ray는 여러 퍼블릭 파일 시스템에 연결할 수 있게 해주는 다양한 플러그인들을 제공하는데, 그 중 우리는 s3fs 플러그인을 사용할 것이다. 이름 그대로, AWS가 제공하는 가장 간단한 형태의 저장공간 S3를 서버에서 파일 시스템(fs)으로도 사용할 수 있게 해주는 녀석이다.

    Master 노드에 접속하고, 아래 명령어로 플러그인을 설치하자.

    docker plugin install rexray/s3fs \
    S3FS_ACCESSKEY=<엑세스_키> \
    S3FS_SECRETKEY=<시크릿_키> \
    --grant-all-permissions=true
    


    이제 AWS S3와 내 Swarm 시스템이 연결되었다. 만약 이전에 사용하던 S3 버킷이 있을 경우, docker volume ls 명령어를 통해 내 모든 버킷을 확인할 수 있을 것이다. 설령 그렇다고 해도 이미 사용중인 버킷을 덮어쓸 수는 없으니, 우리 Swarm만을 위한 새로운 버킷을 하나 만들어 보자.

    AWS의 S3 웹 콘솔에 들어가면, 클릭 몇 번으로 간단히 버킷을 생성할 수 있다. 버킷 이름이나 기타 속성 등을 지정해준 후, 다시 Master 노드로 돌아오자. docker volume ls 명령어를 입력해 방금 생성한 버킷이 제대로 보이는지 확인했다면, 이제 DB를 생성할 차례다.


    마찬가지로 AWS 웹 콘솔의 RDS 메뉴로 들어가면, MySQL을 필두로 하는 다양한 관계형 DB 인스턴스를 생성할 수 있다. MariaDB 또는 MySQL 중 취향에 맞는 것을 선택하고, DB 식별 이름과 계정 등을 설정해주면 된다. AWS에서 처음 DB를 생성하는 경우 약간의 보안 정책과 한글 인코딩 정책을 손봐줘야 하는데, 깔끔하게 정리된 이 포스트를 참조하면 좋을 것 같다. 필자도 도움을 많이 받았다. 이후 생성된 DB의 세부정보를 확인하면 엔드포인트 URI가 적혀있는데, 모니터 한쪽에 잘 띄워두도록 하자. 곧 쓸 꺼다.

    이제 모든 준비가 끝났다. 정말로 애플리케이션을 실행해 보자. 아래 명령어를 적당히 수정해 콘솔에 넣으면 된다.

    docker service create \
    --name wordpress \
    --mount type=volume,source=<S3_버킷_이름>,target=/var/www/html \
    -e WORDPRESS_DB_HOST=<RDS_엔드포인트_URI>:3306 \
    -e WORDPRESS_DB_NAME=<RDS_식별_이름> \
    -e WORDPRESS_DB_USER=<RDS_DB_유저명> \
    -e WORDPRESS_DB_PASSWORD=<RDS_DB_비밀번호> \
    -e WORDPRESS_TABLE_PREFIX=wp_ \
    --label traefik.frontend.rule="Host:<접속에_사용할_URL>" \
    --label traefik.port=80 \
    --network traefik-net \
    --constraint=node.role==worker \
    --replicas 2 \
    wordpress
    

    하나하나 천천히 살펴보자. S3 버킷을 WordPress 서비스의 기본 데이터 경로인 /var/www/html과 연결하고, DB 호스트와 이름을 조금 전 RDS에서 생성한 그것으로 설정했다. --label 태그를 통해 traefik을 거쳐 우리 웹 서비스에 접속에 사용할 URL을 설정하고(테스트 환경에서는 Master 노드의 IP주소로 설정해도 무방), 80 포트를 허용했다. 통신할 네트워크도 물론 traefik-net으로 고정해줬다. 컨테이너가 Worker 노드에서만 구동될 수 있도록 규칙을 잡고, 기본적으로 2개의 컨테이너가 작동하도록 해두었다. 이미지는 최신 버전의 공식 wordpress 이미지를 사용한다.

    더 이상 기다릴 건 없다. 웹 브라우저를 키고 80 포트로 접속하자. WordPress 설치 화면이 나온다면, 우린 모든 역경을 이겨냈다는 뜻이다. 이제 당신의 애플리케이션도 훌륭한 퍼포먼스와 높은 안정성을 자랑하게 되었다.

    마지막으로 /var/www/html의 데이터가 정상적으로 S3 버킷에 저장되는지, DB의 접속은 제대로 되는지, 일부 노드가 다운되도 정상적인 서비스 접속이 가능한지 등 필수 기능 체크는 꼭 거치길 바란다.

    뭔가 아쉬운데…

    필자의 다른 글을 살펴봤다면 알겠지만, 휴대전화 기지국도 직접 만들 정도로 외부 의존성을 가능하면 줄이려고 하는 편인데, 이번에는 투자하는 시간과 노력 대비 결과물을 생각해 사용이 간편한 AWS로 데이터 저장 파트를 대체했다. 의도하지는 않았지만 독자가 원할 경우 컴퓨팅까지 AWS EC2로 대체하는 과정이 좀 더 편해졌을 수도 있겠다.

    그래도 뭔가 아쉬운 느낌이 든다. 아직 보충할 일거리가 조금 더 남았지 않을까. 애플리케이션의 SSL 통신이나 외부 클라우드 의존 없는 100% 로컬 아키텍처 대체법 등이 될 것 같은데, 추후 별도의 글로 작성할 생각이다. 글이 업로드되면 이곳에도 링크를 달아 두겠다.

  • 느리게 가는 도시의 빨리 사는 사람

    필자는 도시의 탈을 쓴 시골에 산다. 간신히 한 곳 있는 영화관에서 새로 나온 영화를 챙겨보고, 학생들의 문제집 판매량이 없다면 당장이라도 망할 것 같은 서점에서 한달에 한번 바뀌는 베스트셀러 책장을 들여다보며, 사용하는 노트북의 수리라도 받으려면 옆 도시까지 움직여야 하는. 그런 겨우겨우 도시라고 불릴 수 있는 곳에서 살고 있다.

    그래서 그런지, 언제나 북적거리고 바쁘게 움직이는 대도시를 동경한다. 높게 뻗은 건물들과 살아있는 듯 자연스럽게 연결되는 대중교통, 어디로 눈을 돌려도 밝게 빛나는 환한 조명은 지금도 내 심장을 쿵쿵 뛰게 만든다. 좋아하면 닮는다더니, 웃기게도 필자는 아무 급할 일 없는 이 작은 도시에서 언제부턴가 빠르게 움직이기 시작했다. 거리의 풍경을 눈에 담지 않았고, 움직이는 도중에도 자주 스마트폰을 만지작 거렸다. 항상 끼는 이어폰에서는 싫어도 몸을 움직이게 만드는 EDM이 흘러나왔다.

    느리게 가는 도시의 빨리 사는 사람이 되어버린 것이다.

    아무도 닥달하는 사람이 없는데, 스스로 생산적인 일을 하고 있지 않으면 어딘가 불안했다. 내가 가치없는 사람이 되는 것 같았다. 한동안 이런 상태를 별로 문제삼지 않았었는데, 최근에 들어서 ‘왜?’ 라는 질문이 자꾸 들었다. 한참을 곰곰히 생각해봤는데, 대답 대신 웃음이 피식 나왔다. 남들은 ‘타임 푸어’니 뭐니 하면서 일에서 벗어나고 싶어하는데, 나는 누구보다 시간이 풍족한데도 자꾸 스스로 족쇄를 채우려 하는 상황이 너무 아이러니했다. 더 이상 고민할 건 없었다.

    컴퓨터를 끄고 밖으로 나갔다. 항상 듣던 Dance Mix 대신 Chill Mix를 틀었다. 메시지가 온 스마트폰을 알고도 가만히 냅뒀다. 횡단보도의 신호등이 깜빡거려도 마음을 졸이지 않았다.

    나는 느리게 가는 도시의 느리게 사는 사람이다.

  • Portainer를 통한 Docker 초점잡기

    자, 2017년 마지막 날까지 까만 바탕에 하얀 글씨만 들여다보고 있는 당신을 위해 Portainer라는 가볍고 빠른 웹 기반 Docker 환경 관리 도구를 들고왔다.

    필자도 평소 꽤 CLI 환경을 애용하는 편이지만, 가끔은 눈으로 보이는 인터페이스가 미친듯이 끌린다. 마치 할리우드 해커가 된 것처럼 이쁘고 깔끔한 그래픽을 바라보며 폼 잡고 싶어진다. 그래서 로컬에서 구동하는 Docker를 사용할 때는 종종 Kitematic이라는 GUI 도구도 같이 사용했었는데, 원격 서버에서 구동되는 Docker를 사용할 때는 마땅한 도구를 찾지 못해 선택권 없이 CLI만 주구장창 사용했다. 사실 어지간한 도구들은 사용 전 이것저것 신경써야 할 것들도 많고 그 결과물도 썩 마음에 들지 않아 더 CLI를 선호했기도 한데, Portainer는 사전 설정도 간단하고 사용도 편해 상당히 만족하며 사용하고 있다.

    속살

    날씨도 추우니 빠르게 속살을 까고 내려가자. 아래는 Portainer 공식 웹페이지에서 제공하는 설치 과정이다. 일반적인 단일 노드 환경에서 Docker를 사용한다면, 아래 두 명령어면 충분하다. Portainer가 사용하는 데이터 볼륨을 만들고, 호스트의 /var/run/docker.sock 을 컨테이너와 직접 연결한 후 9000포트를 통해 외부와 커뮤니케이션 시켜준다.

    $ docker volume create portainer_data
    $ docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
    

    만약 다중 노드로 구성된 Docker Swarm 환경에서 Portainer를 사용하고 싶다면, 아래를 참고하면 된다.

    $ docker service create \
    --name portainer \
    --publish 9000:9000 \
    --replicas=1 \
    --constraint 'node.role == manager' \
    --mount type=bind,src=//var/run/docker.sock,dst=/var/run/docker.sock \
    portainer/portainer \
    -H unix:///var/run/docker.sock
    

    Easy peasy lemon squeezy는 바로 이럴 때 사용하는 말이다. 컨테이너 구동이 끝났다면, 브라우저에서 해당 노드의 IP + 9000포트로 접속하면 된다.


    간단한 계정 생성 과정을 거친 후, 위와 같이 Local과 Remote를 선택하는 창이 나오는데, 이름에서 유추할 수 있듯 Local은 Portainer가 설치된 시스템의 Docker 그 자체에 접근하는 것이고 (이를 위해 docker run-v 옵션을 통해 시스템의 /var/run/docker.sock을 마운트해 줬다.), Remote는 다른 곳에 위치한 Docker 머신을 관리할 수 있는 옵션이다.

    대부분의 경우 Docker가 돌아가는 시스템 그 자체에 Portainer를 설치했을 것이니, Local을 선택하자. 원격지 Docker 머신을 사용하고 싶은 경우, Docker 데몬의 API 주소 (원격 서버 주소 + 2375 혹은 2376)로 Endpoint를 설정하면 된다.


    모든 필요 설정을 마치면, 드디어 무언가 할 수 있는 콘솔창이 마중나온다. 이제 더 이상 설명할 건 없고, 하나하나 눌러보며 이전에 Docker CLI나 기타 GUI 도구로 진행했던 작업을 이어가면 된다. 혹시 단일 컨테이너로 실행했더라도 해당 시스템이 Docker Swarm의 Manager 노드일 경우 그 Swarm 클러스터의 전반적인 상태와 구조까지 시각화해서 보여주니 상당히 편하다.

    한가지 아쉬운 점이라면, 각 컨테이너의 리소스 사용량을 그래프화시켜 실시간으로 보여주는 기능도 있으면서 정작 상태 이상 알림 기능은 없어 본격적인 모니터링 용도로는 사용하기 애매하다는 것인데, 추후 업데이트에서 대응할 계획이 있다고 운영진이 공식 입장을 내놓았으니 이를 기다려봐도 좋을 것 같다.

    마치며

    사실 해가 가기 전 캐노니컬의 MAAS 솔루션 관련 포스트를 마무리하고 개고생한 2017년 넋두리 글이라도 쓸까 했는데, 생각보다 MAAS의 잔버그가 많아 모두 내년으로 미뤄버렸다. 그래도 어찌저찌 다른 글로 공백은 매웠으니 뭐 만족한다.

    몇시간 남지 않은 2017년 모두 잘 마무리 할 수 있길 바란다.

  • YateBTS를 사용하는 홈메이드 GSM 네트워크 구축하기

    이전 프로젝트 포스트 에서 온갖 폼은 다 잡아 놨으니, 이제 회수할 차례다. 지난 한 주간 틈날 때마다 방에서 전자파를 뿜어내며 시간을 보냈다. 네트워크 구축에 필요한 하드웨어도 배송 받았고, 어느 정도의 테스트도 끝마친 상태다. “모두를 위한 모바일 네트워크” 라는 프로젝트 취지에 맞게, 나 스스로가 국토를 뒤덮을 수 없으니 인터넷의 불특정 다수에게 과정을 공유하는 것이 맞다고 생각한다. 특히 이와 관련된 한국어 자료는 전무하다 싶을 정도니, 한국어 독자들에게는 분명 도움이 될 것이다.

    주의! 이 프로젝트와 관련 포스트를 참고해 발생한 모든 종류의 불이익은 독자에게 있습니다. 적합한 법적 절차를 거치지 않았을 경우, 외부와 격리된 테스트 환경에서만 운용하고 전파가 외부로 새어나가지 않도록 주의하여야 합니다.

    하드웨어 준비물

    • 라즈베리 파이 3 모델 B.

      국내 인터넷 쇼핑몰 등지에서 쉽게 구할 수 있는 최신 라즈베리 파이 모델이다. 원활한 작업을 위해 16GB 이상의 마이크로SD와 적절한 전력을 공급해줄 수 있는 충전 케이블 / 전원 공급원을 같이 준비하자.

      휴대성과 적은 전력 소모를 걱정하지 않는다면 기존에 가지고 있던 x86 데스크탑이나 노트북 등을 사용해도 좋다. 오히려 퍼포먼스 부분에서는 더 권장한다. 라즈베리 파이는 기본 성능 자체도 낮을 뿐더러 USB 3.0을 지원하지 않아 추후 네트워크 구축 후 데이터 패킷 통신 시 (GPRS) 간혈적인 병목 현상이 발생할 수 있다.

    • BladeRF x40

      필자는 몇해 전 킥스타터에서 성공적으로 모금을 마친 Nuand 사의 소프트웨어 정의 라디오 (SDR) 플랫폼 BladeRF x40를 구축에 사용했다. 비슷한 기능을 제공하는 장비 중 가장 저렴한 가격대와 안정적인 품질을 보여주니 개인적으로는 가장 추천한다. 한국에서 리셀링하는 업체는 프리미엄을 잔뜩 붙였으니 공식 홈페이지 에서 주문할 것. 한국 직배송도 지원한다.

      추가로, 수요층이 한정된 물건이라 서드파티 케이스를 구하기 힘드니 구매할 때 공식 홈페이지에서 케이스까지 같이 구매하는 편이 좋다. 물론 DIY 능력자라면 스스로 만들어 써도 된다.

    • SMA 규격을 준수하는 안테나

      본인의 용도에 맞게 적절한 출력을 보장하는 SMA 규격 안테나를 준비해두자. BladeRF는 전이중 통신 플랫폼이니 당연하지만 안테나 2개가 필요하다. Nuand 공식 홈페이지에서 BladeRF와 함께 판매하는 2dBi 출력의 안테나가 있으니 귀찮으면 같이 사도 무방하다. 다만, 눈물 나오게 빈약한 전파 커버리지는 감안해야 한다.

    기타 라즈베리 파이에 연결할 모니터나 키보드 등 작업에 도움을 줄 수 있는 물건들은 알아서 구비하길 바란다.

    가입자 식별에 사용하는 SIM 카드는 이번 포스트에서는 사용하지 않는다. 가입자 인증 기능을 구현하고 싶은 독자는 Amazon이나 AliExpress 등에서 공 SIM 카드를 별도로 구매하면 된다. 이런 제품이런 리더기 조합으로 맞추면 될 것이다.

    소프트웨어 작업

    라즈베리 파이의 OS는 라즈비안 Jessie Lite 마지막 릴리즈 를 사용한다. 사용할 YateBTS 소프트웨어가 Ubuntu 14.04 / Debian 8을 기반으로 하기 때문. 현재 업로드되는 최신 OS인 라즈비안 Stretch를 설치하면 소프트웨어 설치 과정 중간에 오류가 발생한다. YateBTS 프로젝트 자체도 정상적으로 유지되고 있고, 포럼도 활발한 상태인데 최신 OS 기반 대응은 해줄 생각을 안 하고 있어 아쉬울 따름이다. 그래도 아쉬운 사람이 발품 팔아야 하지 않겠나. 일단 그대로 따라가자.

    위에서 언급한 YateBTS는 적합한 트랜시버와 컴퓨터만 있다면 누구나 모바일 네트워크를 구축할 수 있게 해주는 오픈소스 프로젝트다. 우리는 적합한 트랜시버 (BladeRF) 와 덜 적합하지만 적당히 굴러는 가는 컴퓨터 (라즈베리 파이)를 이미 가지고 있으니 YateBTS를 사용해 당장 셀룰러 신호를 뿜어낼 수 있다.

    그리고 희소식 하나, BladeRF x40의 펌웨어와 YateBTS, 그리고 각종 필요한 유틸리티들을 자동으로 설치해주는 자동화 스크립트를 가져왔다. 혹시라도 이후 과정에 기다리고 있을 험난한 소프트웨어 설치 작업을 걱정하고 계셨던 분들이 있으시다면 안심하셔도 된다. 여러분이 할 일은 스크립트를 다운로드 받아 실행만 하면 된다.

    아래 과정을 통해 라즈베리 파이에서 스크립트를 실행해 보자.

    아래 과정에서 사용될 스크립트는 Matthew May와 Brendan Harlow님이 챔플레인 대학의 SEC-440(System Security) 학위 프로그램에 사용한 자동화 스크립트로 최신 YateBTS를 구동할 수 있도록 필자가 직접 커스터마이즈 하였습니다.

    //필자의 GitHub에서 스크립트 다운로드.
    wget https://raw.githubusercontent.com/kycfeel/dokupe/master/auto_yatebts_deploy.sh
    
    //다운받은 스크립트에 실행 권한 추가.
    chmod +x ./auto_yatebts_deploy.sh
    
    //sudo 권한으로 스크립트 실행.
    sudo ./auto_yatebts_deploy.sh
    

    이제 커피 한잔 마시며 기다리면 된다! 첫 실행 시 내 네트워크의 이름을 물어보는데 알아서 잘 입력해주고, 중간 BladeRF 펌웨어 설치 시 BladeRF가 연결되어 있지 않다면 USB로 연결해달라는 안내문만 하나 뜰 뿐, 완전히 자동으로 YateBTS가 설치되니 걱정 푹 놓고 있으면 된다. 성능이 떨어지는 라즈베리 파이 특성 상 소프트웨어 make 과정 등에서 상당한 시간을 소요하니, 인내심을 가지자.

    설치가 완료되었다면 라즈베리 파이 내 계정의 홈 폴더에 StartYateBTS.sh 라는 스크립트가 생성됬을 것이다. sudo ./StartYateBTS 명령어로 YateBTS를 실행해 보자. 자동으로 소프트웨어가 가동되고, BladeRF의 신호 송수신 LED가 깜빡거릴 것이다. 콘솔창의 화면에 MBTS Ready 라는 메시지가 나오면 가동 완료.

    고맙게도 YateBTS는 대부분의 네트워크 설정 과정을 GUI로 진행할 수 있도록 웹 인터페이스를 제공한다. 왜 생 Yate 엔진만 사용하지 않고 YateBTS를 같이 사용하는지 지금부터 피부로 느끼게 될 것이다. 생각보다 상당히 편하다. 같은 네트워크에 연결된 컴퓨터에서 브라우저를 키고 라즈베리_파이의_IP/nib 주소로 접속하면 된다. 아마 별 문제 없이 웹 인터페이스가 마중을 나올 거다.


    바로 웹 인터페이스의 BTS Configuration -> GSM 메뉴로 들어가서 가장 중요한 GSM 네트워크의 설정을 진행해보자.


    위 이미지는 필자의 세팅값인데, EGSM900 - #1000 (930.2 MHz) 규격으로 네트워크를 구축하고 있고, MCC와 MNC 코드는 각각 테스트 네트워크를 의미하는 001과 01로 설정되어 있다. 하단의 전파 최소 / 최대 강도는 모두 중간 수준인 35, 네트워크를 식별명은 프로젝트 이름을 그대로 따 dokupe로 설정했다.

    원할 경우, 바로 옆의 GPRS 메뉴로 넘어가 Enable 값에 체크만 해 주면 GSM 음성 통화 / SMS와 더불어 GPRS 데이터 통신도 가능해진다. 외부 인터넷 연결까지 생각한다면 GGSN 메뉴의 Firewall.Enable 값을 no firewall로 변경해주자.

    일반적인 GSM 네트워크는 우리에게도 익숙한 USIM (SIM) 카드를 통해 가입자를 식별한다. 혹시 설치 스크립트를 유심히 살펴봤다면 항목 중 pySIM 이라는 소프트웨어가 있었을 건데, 이게 바로 라즈베리 파이를 통해 SIM 카드를 프로그래밍 할 수 있게 해주는 도구다. 웹 인터페이스의 Subscribers -> Manage SIMs 메뉴를 통해 간단히 사용할 수 있지만, 이번에는 SIM 카드 없이 주변의 모든 GSM 대응 휴대전화가 네트워크에 연결할 수 있는 방법을 소개하려 한다. SIM 카드에 투자하는 추가 비용이 없는 것은 좋지만 출력이 높아질 경우 제 3자의 휴대전화가 내 네트워크에 자동 연결되어 버릴 수도 있으니 언제나 주의해야 한다. 테스트 환경에서만 사용할 것. 사실 지금 사용 가능한 공 SIM 카드와 리더기가 없다 읍읍

    그렇다고 복잡한 건 없고, 단순히 Subscribers -> List Subscribers 메뉴의 Regexp 값을 .* 으로만 고정하면 SIM 카드가 없어도 누구나 네트워크에 연결이 가능해진다. 이 정도면 만지면 이제 최소한의 네트워크 설정이 완료되었다.

    사용하기

    이제 휴대전화를 꺼내 수동 네트워크 검색 (또는 비슷한 기능을 하는 메뉴) 에 들어가 스캔을 돌려보면 KTKOR SK Telecom 등의 익숙한 통신사와 함께 Test PLMN 1-1 (혹은 비슷한 이름의) 처음 보는 통신사가 같이 검색될 것인데, 이게 우리가 만든 모바일 네트워크다.


    처음 스크립트를 돌릴 때 설정했던 네트워크 이름은 왜 안나오나 궁금해 할 수도 있는데, YateBTS의 커뮤니티 버전은 커스텀 네트워크 이름의 출력을 지원하지 않는 것 같다. 해외 포럼 등에서도 계속 오고가는 이야기이며, 틈날 때마다 리서치 중이니 혹시 우회 방법이나 대체 해결책이 보이면 글을 업데이트 하겠다. Test PLMN 1-1는 우리가 설정한 MNC, MCC 코드에 지정되어 있는 기본 네트워크 이름이다.

    정상적으로 휴대전화가 네트워크에 연결되었다면, 아래와 같이 몇초 지나지 않아 YateBTS 시스템에서 자동으로 보내는 문자메시지가 수신될 것이다. 웰컴 메시지는 Yate 시스템의 버전에 따라 얼마든지 형태가 달라질 수 있다. 혹시 내가 수신한 메시지의 양식이 다르더라도, 그 내용은 아주 비슷할 것이니 별 걱정하지 않아도 된다.


    상단의 웰컴 메시지는 Yate 6 기준 /usr/local/share/yate/scripts/nipc.js 파일 안의 sms_registration 변수 안에 담겨있다. 자유롭게 커스터마이즈 해보자!

    수신된 문자 메시지에는 내 모바일 네트워크에서의 고유 전화번호도 같이 적혀 있다. 이제 이 번호로 다른 기기들과의 전화 / 문자메시지를 주고받을 수 있다. 남는 휴대전화가 더 있다면, 네트워크에 연결해 서로 전화를 걸어보고 문자를 발송해 보자. 별 문제 없이 잘 진행될 것이다.

    IP를 할당받고 인터넷에 연결하는 일도 전혀 어려울 건 없다. 평소 휴대전화에서 데이터 네트워크에 접속하는 것처럼, 설정의 셀룰러 데이터 메뉴에서 데이터 통신을 허용해주기만 하면 된다. 평소에 보던 LTE나 3G 마크 대신 GPRS라는 안 익숙한 친구가 불쑥 튀어나올 것이다. 물론 기본 수십 ~ 수백 mbps 속도를 뽑아내는 LTE 네트워크를 사용하던 우리한테는 이 GPRS라는 친구는 영 미덥지 않다. 무려 이론상 최고 속도 114kbps를 보장해주는 2000년대 2.5G 기술이니까 말이다. 그래도 뭐, 일단 인터넷에 연결할 수 있다는 것 만으로도 감사하자. ‘불가능한 것’ 과 ‘불편한 것’ 의 차이는 어마어마하게 크다.

    결론

    독자분들 중 시대가 어느 시대인데, 왜 GSM 네트워크를 선택했냐고 궁금해하시는 분들도 분명 있을 것이다. 필자도 이왕 만들거 속도 빵빵한 LTE 네트워크를 사용할 수 있었으면 좋았겠지만 현실적으로 봤을 때, 그리고 우리 네트워크의 취지를 생각할 때 GSM과 GPRS 조합이 가장 적당하다고 판단했다.

    일단, GSM 이상의 모바일 네트워크 (UMTS 등)는 더 높은 하드웨어 사양을 요구한다. 다시말해, 라즈베리 파이로 구축하는 휴대용 네트워크는 어림도 없다는 소리다. 노트북이라는 대안이 있기는 하지만, 비용 측면이나 전력 소모량, 공간 사용 측면에서 비교가 되지 못한다. 하나 더 이유를 꼽자면, 오직 GSM 네트워크만 SIM을 통한 가입자 인증 없이 주변 모든 휴대기기를 네트워크에 연결시킬 수 있다. UMTS 이상 모바일 네트워크들은 필수적으로 사전에 사용자 정보를 SIM에 프로그래밍 해둬야 한다. 유사시 네트워크가 제 기능을 하기 위해서는 최대한 간단하고 편하게 사람들 사이에 퍼져야 한다. 평시에도 국내에서는 공 SIM 카드 자체를 구할 수 없을 뿐더러, 일일이 그 많고 많은 사람들을 위한 SIM 카드를 프로그래밍 하고, 또 나눠준다는 것 자체가 비현실적이다. 사용할 유저가 없는 네트워크는 유령 전파가 될 뿐이다.

    물론, 여유가 있다면 OpenBTS-UMTS 프로젝트나 openLTE 프로젝트 등을 참고해 GSM보다 빠르고 현대적인 네트워크를 얼마든지 구축할 수 있다. 관심이 있다면 해당 사이트를 잘 열람해보자. 필자도 시간과 자원이 남는다면, 이런 프로젝트들을 활용한 네트워크를 구축해보고, 글을 남길 의향이 있다.

    여기까지 따라오신 분들, 혹은 단순 흥미로 읽어내려가신 분들, 모두 수고하셨다. 이제 우리 손에 모바일 네트워크 하나쯤은 들고 집에 갈 수 있게 되었다. 각종 재난 상황에서, 또는 혹시라도 세상에 ‘네트워크는 공공재’ 라는 중요한 사실을 다시 각인시켜줄 필요가 있을 때, 오늘의 삽질이 결실을 맺을 수 있길 바란다.