카테고리 보관물: Personal projects

AWS Lightsail에 둥지 틀기

여러 국내 호스팅 업체들을 거쳐서 여러 곳에서 WordPress 호스팅 서비스로 추천되는 Siteground에 한동안 정착하는 듯 했다. 준수한 속도에 비해 합리적인 가격($3.95/월) 때문에 당분간 정착할 거라고 생각하고 있었는데 일년이 지나서 서비스 갱신기간이 되자 가격이 세배 넘게 올라서 1년에 $143을 달라고 한다.($11.95/월).
“이건 너무 한 거 아니냐고!”

WordPress hosting은 더 비싸다

대안으로 생각한 것은 1) wordpress.com hosting과 2) AWS Lightsail이었는데, 발견한 한 가지 놀.라.운. 사실은 WordPress hosting에서 플러그인을 설차하려면 한달에 $25 짜리 business plan 이상을 계약해야 한다는 것이다. 한달에 $25라니!!! 멋 모르고 카드 결재를 하고 이리저리 플러그인 설치를 시도하다가 아래의 표를 발견하고는 미련 없이 ‘refund’ 버튼을 눌렀다.

AWS Lightsail은 한 달간 무료 시험기간을 주기 때문에 Siteground 계정이 종료하기 전에 미리 설치를 마쳤다. 인스턴스 리전을 한국으로 할까 하다가 이전 Siteground와 같은 위치인 싱가포르로 선택했고 WordPress의 export/import 기능 덕에 마이그레이션은 수월하게 진행되었다. Domain name은 호스팅 업체와 같은 곳으로 이전하는게 좋다고 해서 AWS Route 53으로 이전 및 1년 연기 등록을 했는데 상위 도메인에 따라 다르지만 .com의 경우 $13.2(세금포함)가 소요된다. 어라? 그러고 보니 Siteground에서 도메인을 연장하는 서비스는 $15.95였는데? 이눔 시키들…

도메인 이전과 새로운 IP 연결

도메인 이전을 요청하니 이전 관리자였던 Siteground에서 확인 메일이 왔고, 수락한 이후에는 하루 후에 AWS로 도메인이 이전되었다. 그 후에는 Lightsail의 네트워킹 탭에서 고정 IP와 DNS 영역을 생성해서 A 레코드를 추가하고 IP와 도메인 이름을 연결 시켜 주었다.

그리고 나서 한참을 DNS가 업데이트 되기를 기다렸다. nslookup은 여전히 예전 IP를 보여줬지만 48시간 까지 걸릴 수 있다는 말에 이틀정도를 기다렸는데도 IP가 업데이트 되지 않았다. AWS Route 53에 가보니 ‘registered domains > [domain name]’ 아래에 Name Servers 항목이 있는데 혹시 이것 때문인가 싶어서 고정 IP를 만들때 나왔던 네임서버 목록으로 업데이트를 해주었다. 그리고 나서 잠시 후 드디어 새로운 IP로 접속되었다!

계속 예전 IP로 돌아가는 도메인 문제와 해결

기쁜 마음으로 꿀잠에 들었는데 다음날 아침에 보니 Jetpack에서 서버연결 붙었다가 끊어졌다가 했다는 연락이 와 있었다. 혹시나 싶어서 서버를 재부팅 해봤더니 재부팅하는 순간 잠깐 IP가 새 것으로 붙었다가 다시 예전 IP로 돌아가는게 보였다. 그니까 누가 AWS 알고 있는 것과 다른 예전 정보를 자꾸 준다는 거지…” 혹시나 하는 마음에 Siteground의 cPannel > Advanced DNS zone editor에 들어가 봤더니 역시나 얘가 예전 주소를 꼭 쥐고 있었다. 예전 사업자의 도메인 서버를 AWS 고정 IP로 수정해 주고 났더니 Jetpack이 사이트 살아났다고 축하한댄다. 다행히 그 후로는 다시 끊어지는 일은 없었다.

도메인 관리자를 변경하고 새로운 IP를 연결 했으니 이전 네임 서버에서는 자동으로 놔 주겠거니 하고 잘 못 생각하고 있었던게 문제 였던듯 싶다.

Travis CI 설정과 docker image 사용

GitHub project에 CI를 붙이고 싶은데 Jenkins server가 회사 firewall 안에 들어 있어서 GitHub에서 직접 webhook을 붙일 수 없는 문제가 있다. Jenkins의 GitHub plugin으로 tunneling을 설정하는 방법 등 있기는 하지만 다른 CI 옵션들을 살펴 보던중 Open source project에 대해서는 무료라는 Travis CI가 있다는 것을 알게 되었다. Travis CI는 기본으로 Ubuntu를 지원하고 그 외의 경우는 docker를 사용해서 환경을 설정할 수도 있다. 이 포스팅은 Travis CI에서 ClearLinux docker를 사용한 설정에 대한 기록이다.

 

삽질1: Travis CI의 Ubuntu이용

빌드와 Google test를 이용한 unittest만 할 것이니까 OS를 크게 타지 않을테니 기본으로 제공되는 Ubuntu 환경에 필요한 도구들만 설치 하면 가장 빠르지 않을까?

일견 타당해 보이기는 하지만 문제는 의존성이다. Pre-compile된 Google test를 download 받는다 해도, 2019년 1월 현재 아직 Travis CI에서 제공하는 Ubuntu의 가장 최신 버전은 Xenial이다. CMake version이 안맞아서 최신버전으로 설치하고 Intel LibVA, Intel MediaSDK등의 의존 package들을 컴파일한 후 빌드를 하고 unittest를 하도록 하는데 14분이 넘게 걸렸다. 다음은 사용한 .travis.yml file이다.

 

삽질2: Clear Linux docker image 사용

시간만 오래 안 걸렸어도 기본 Ubuntu OS로 어떻게든 해보는 건데, 14분이면 시간이 너무 오래 걸린다. 이왕 시간이 오래 걸리는 거라면 Clear Linux docker docker image를 사용해보자.

Clear Linux docker image를 생성하기 위한 dockerfile을 다음과 같이 작성해준 다음

.travis.yml file을 다음과 같이 선언해 준다.

총 소요된 시간은 17분 41초 그 중에 docker 설정하는데 걸린 시간만 16분이 넘는다. 나머지 시간은 unittest… 즉 대부분의 시간이 docker를 빌드 하고 설정하는데 사용 되고 있었다.

 

삽질3: Docker image download

빌드하는데 시간이 오래 걸린다면 이미 만들어 둔 docker image를 저장소에 넣어두고 pull해서 사용하면 좀 빠르지 않을까? Docker 빌드 vs Docker 다운로드.

이미 빌드 한 docker image를 공개 저장소인 docker hub에 넣어두고 Travis CI에서 pull하도록 변경하면 시간은 8분정도로 줄어든다.

흠.. 8분이면 그나마 그럭저럭 쓸만 하군.

 

결론

Travis CI는 머리는 나쁘고 손발은 빠르다. Docker를 이용해서 테스트 하려면 매번 새롭게 빌드하기 보다 Docker Hub에 올려두고 pull 하는 방법을 고려해 볼만 하다.

Heap tree 만들기

Heap tree는 완전이진트리 형태로 모든 부모 노드의 값이 자식 노드 보다 크거나 (max heap) 모든 부모 노드의 값이 자식 노드 보다 작은 (min heap) tree를 말한다. 이 글에서는 max heap tree를 만드는 방법을 알아본다.

Heap tree를 생성하는 buildHeapTree()이 동작하는 절차는 다음과 같다. N은 원소의 갯수.

재귀로 구현된 heapify()가 등장하는데, 이 함수가 하는 역할은 주어진 node를 부모로 시작해서 단말 node로 내려가면서 max heap 조건에 맞도록 변경해 주는 역할을 한다. heapify()는 큰 자식과 부모를 swap() 하는 경우 재귀하므로 시간 복잡도는 O(트리높이)가 된다.

Heap tree를 생성하는 방법을 고민해 본 적이 있다면, 부모 node들을 순회 할 때 역순으로 순회하는게 얼마나 영리한 방법인지 무릎을 쳤을 것이다. 내 얘기다;;

 

예제

임의의 정렬되지 않은 배열 { 2, 7, 5, 4, 1, 6, 10, 3, 9, 8}을 예로들어 보자. 10개의 node를 가지는 tree를 배열로 구현 할 때, 부모 node들의 index는 소숫점_버림(N/2) – 1로 계산할 수 있다. 따라서 10개의 node를 가지는 이 tree의 부모 node들의 위치는 [0], [1], [2], [3], [4]이다. (C/C++로 구현 할때는 N/2한 값을 그냥 int형 변수에 넣으면 된다)

각 부모 node들과 자식 node들을 subtree라고 하면 이 예제는 다음과 같이 5개의 subtree들로 구분될 수 있다.

 

Heapify – Subtree A

부모인 4번째 index의 값과 하나 밖에 없는 자식인 9번째 index의 값을 비교한다. 자식의 값이 더 크므로 부모와 swap()한다.

 

Heapify – Subtree B

부모인 3번째 index와 왼쪽 자식인 7번째, 오른쪽 자식인 8번째를 비교해서 가장 큰 오른쪽 자식과 부모 node를 swap()한다.

 

Heapify – Subtree C

오른쪽 자식인 index 6번과 부모인 index 2번을 교체한다.

 

Heapify – Subtree D

1번 index의 왼쪽 자식이 더 크므로 부모와 swap()한다.

 

 

Heapify – Subtree E

2번 index의 값이 더 커서 부모인 0번 index와 swap()하고 보니 2번 index를 부모로 하는 subtree가 더이상 max heap이 아니게 된다.

그래서 다시 한번 heapify()를 호출해서 max heap을 만든다.

 

Code

 

 

Heap sort (힙 정렬)

Heap sort는 root node에 항상 제일 작은 값이나 제일 큰 값이 오도록 heap tree를 구성해서 정렬하는 방법이다. Heap tree의 root에 최소 값이 오도록 구현하면 min heap tree, 최대 값이 오도록 하면 max heap tree라 한다. Heap tree를 구성하는 방법에 대해서는 별도의 포스트로 다루고 여기서는 max heap으로 오름차순 정렬하는 과정을 설명한다.

Heap sort는 한번 heap tree를 구성한 다음에는, 어떤 값이 제자리를 찾는데 소요되는 시간이 짧다는 점을 이용한다. 부모와 자식노드의 값에 따라 완전 이진트리로 구성하므로, 처음 한 번 heap tree를 구성할 때 O(N/2)만큼 소요된 이후에는, heap tree가 구성된 상태에서는 어떤 노드의 제자리를 찾는데 O(log N) 만큼이 소요된다.

Heap sort 과정을 정리하면 다음과 같다.

언뜻 잘 이해가 안 갈수도 있는데, 모종의 과정을 거치고 나면 root (배열의 0번 index)에 최대값이 오게되는 마법의 tree가 있고 이것의 root를 싹둑싹둑 자를때마다 남아있는 것들 중 제일 큰값이 root에 올라오는 것을 상상해보면 도움이 될지도 모르겠다. Tree라고 하면 일반적으로 왼쪽과 오른쪽 자식노드에 대한 pointer를 가지는 형태를 떠올리게 되니까 배열로 표현된 tree가 혼동스러울 수도 있겠는데, 완전 이진 트리인 경우에는 배열안에 원소들이 차례대로 들어 있다고 가정할 때 왼쪽/오른쪽 자식 node의 위치를 다음의 계산식으로 구할 수 있다.

그리고 하나라도 자식이 있는 부모 node들의 index도 구할 수 있다.

 

어떤 배열이 다음의 순서일 때 이를 heap sort하는 과정의 예를 들어 살펴보자.

 

1. “Heap tree를 만든다.”

Max heap을 가정하므로, 위의 배열로 max heap tree를 만들면 다음과 같다. 대괄호([ ]) 안에 있는 숫자는 배열상의  index를 의미한다.

예제의 경우, 이미 “모든 서브트리의 부모가 가진 값이 자식보다 크다”는 max heap tree 조건을 만족하기 때문에 배열 안에서의 순서가 변경되지 않는다. 다른 경우에 heap tree를 생성하는 절차는 나중에 설명하도록 하고 지금은 꼼수 같겠지만 일단 그냥 넘어가자. 위 그림에서 색칠된 node들은 자식들을 가지는 부모 node들인데, 앞서 설명한 부모 node들의 index를 구하는 공식에 의해서 얻을 수 있다. 이 예제에서는 잘 보이지 않지만, 부모 node들의 index를 구하는 것은 heap tree를 구성할 때 중요하다. 이 부분도 별도의 포스팅으로 따로 설명한다.

 

2. “제일 마지막 node를 root와 바꿔서 heap tree를 망가뜨린다.”

“아니 왜?” 하는 생각이 들 수도 있을 텐데, root에는 최대값이 들어 있으므로 제일 마지막 node와 swap하면 root에 있던 값은 오름 차순 정렬일 때의 자기 자리를 찾아가는 샘이 된다.

 

3. “Heap tree를 구성하는 배열의 크기를 하나 줄인다.”

이전의 root였던 10은 제자리를 찾았으니 이제 배열의 크기를 하나 줄여서 index 0 ~ 8까지가 배열이라고 가정한다. 배열 그림을 보면 정렬되어가는 모습이 보일 것이다.

 

4. “다시 heap tree가 되도록 새로운 root의 자리를 찾아 준다.”

얼떨결에 새로운 root가 된 node는 이제 자기 있을 곳을 찾아가야 한다. 자식 node들과의 크기를 비교해서 둘 중 큰 자식과 swap하면서 원래 있어야 할 곳을 찾아간다. 완전 이진 트리의 장점을 살려서 O(log N)의 비교 연산만에 자기 자리를 찾을 수 있다. 이 과정에서 노드들간의 swap이 일어나고 root에는 다시 최소 혹은 최대값이 오게된다.

Root였던 ‘1’은 자식들 중 더 큰 왼쪽 자식인 ‘9’와 자리를 바꾼다.

여전히 max heap tree를 만족하지 못하므로 ‘7’, ‘6’ 중 큰 자식인 ‘7’과 자리를 바꾼다.

마지막으로 ‘3’, ‘2’ 중 큰 자식인 ‘3’과 자리를 바꾸면 모든 서브트리가 max heap tree의 조건을 만족하는 상태가 된다.

 

5. “위의 2, 3, 4번 과정을 배열의 크기가 1이 될때 까지 반복한다.”

이제 다시 새로뽑힌 root와 마지막 node인 2를 swap하고 배열 크기를 하나 줄인 다음 새로운 node의 자리를 찾아준다. 즉, node를 swap하고 index를 하나 줄이는 과정을 O(N)번 반복 하고 각 반복 마다 O(log N)의 –정확히는 매 반복 마다 배열의 크기가 하나씩 줄어듬– 비교 연산이 필요하다.  한바퀴 더 돌아 보면.

바꾸고

크기줄이고

자리 찾아주고

이렇게 원소를 하나씩 줄여 가면서 1개가 남을때까지 반복하면 정렬된 배열을 얻게 된다.

Code로 나타내면 다음과 같다. (충분히 시험되지 않았음)

 

 

WPU version 1.0 release

WPU는 WordPress에서 PlantUML 문법을 사용하기 위한 plugin입니다. WPU에 대한 자세한 내용은 지난 포스팅 (WPU – WordPress에서 PlantUML 문법을 사용하기 위한 plugin)를 참고 하세요.

첫번째 버전 release이후, 다른 plugin과의 충돌이나 WordPress에서 제공하는 기본 기능을 사용하기 위해 <div> tag에 class를 지정하는 방식보다 shortcode를 사용하는 편이 더 낫다는 것을 알게 되었다. 그리고 UML 편집중에 preview를 볼 수 있도록 visual editor plugin도 추가 했다.

WPU를 활성하면 Visual Editor에 ‘UML’이라고 표시된 못생긴 버튼이 하나 추가되는데 이것을 눌러서 preview를 보면서 편집할 수도 있고, 간단한 것이라면 [[wpu] ~ [/wpu]] shortcode 사이에 PlantUML 문법을 직접 기술해서 작성할 수도 있다.

wpu_visual_editor_button

wpu_visual_editor_preview

다음은 shortcode로 변경한 PlantUML 예제이다.

  • Class diagram

    [wpu] scale 900 width
    Class01 <|– Class02
    Class03 *– Class04
    Class05 o– Class06
    Class07 .. Class08
    Class09 — Class10
    Class11 <|.. Class12
    Class13 –> Class14
    Class15 ..> Class16
    Class17 ..|> Class18
    Class19 <–* Class20[/wpu]
  • Sequence diagram

    [wpu]
    Alice -> Bob: Authentication Request
    Bob –> Alice: Authentication Response
    Alice -> Bob: Another authentication Request
    Alice <– Bob: another authentication Response
    [/wpu]

  • State diagram

    [wpu]
    [*] –> State1
    State1 –> [*]
    State1 : this is a string
    State1 : this is another string
    State1 -> State2
    State2 –> [*]
    [/wpu]

 

Plugin download URL :  https://bitbucket.org/litcoder/wpu/downloads/wpu_1.0.zip