탈중앙화 전쟁(1) – 이더리움 킬러와의 공성전

중앙화된 시스템은 때로는 예고 없이 유저를 실망시킨다. 2010년도, 블리자드 사의 MMORPG인 World of Warcraft 의 흑마법사 생명력 흡수 스킬 너프는 이 게임을 즐기는 한 청년을 꼬접하게 만들었다. 청년에게 이 사건은 중앙화된 시스템이 사용자 동의 없이 임의대로 서비스 내용을 변경시키는 것의 위험성을 인지하는 계기가 되었으며, 훗날 이더리움이라는 탈중앙화 생태계를 만드는 동력이 되었다고 한다. (링크)

비탈릭 부테린의 WOW 꼬접(?)으로 탄생한 이더리움은 2021년 현재, 전체 암호화폐 시장에서 두번째로 규모가 큰 대형 블록체인 네트워크다. 이더리움의 스마트 컨트랙트는 블록체인 네트워크 생태계를 완전히 새로운 영역으로 확장시킨 놀라운 기술이었다. 스마트 컨트랙트란 블록체인 네트워크 상에서 동작하는 프로그램으로, 스마트 컨트랙트를 사용하면 중개자 없이 안전한 재화의 이동이나 서비스 제공이 가능하다. 예를 들어 AWS에서 소프트웨어를 작동시키면 그 대가를 아마존에 지불하게 된다. 허나 블록체인 네트워크 상에 구현된 소프트웨어를 작동시키면 그 대가는 불특정 블록체인 노드 제공자에게 돌아간다. 이렇게 스마트 컨트랙트 기반으로 동작하는 어플리케이션을 DApp이라 한다.

다음은 비탈릭이 19세에 작성한 백서 원문 및 한글 번역본의 링크다.
 비탈릭 부테린 블로그 archive
이더리움 백서(en)
이더리움 백서(ko)

이더리움은 DApp의 성장으로 암호화폐 시장에서 크게 성공하였지만 현재는 쉽게 해결이 어려운 몇 가지 기술적 문제에 봉착해 있다. 블록체인 트릴레마 가설, 높은 가스비, DAO 해킹 등의 문제들이 지적되는 동안 3세대 블록체인들이 이더리움 킬러를 자처하며 강력한 도전자로 등장하였다.

이더리움의 문제들

블록체인 트릴레마(blockchain trilemma)문제란 확장성(scalability), 탈중앙화(decentralization), 보안(security) 이 세가지 가치를 동시에 만족하기 매우 어렵다는 것이다.

확장성(scalability) : 사용자나 트랜잭션이 늘어나더라도 처리비용이나 시간이 증가하지 않아야 한다.
탈중앙화(decentralization) : 블록체인 네트워크는 하나의 중앙화된 엔티티가 아닌 다수의 참여자가 관리하여야 한다.
보안(security) : 블록체인 네트워크를 탈취하려는 악의적인 공격을 막아낼 수 있어야 한다.

비탈릭 부테린이 블록체인 네트워크 구성의 어려움을 토로하면서 언급한 가설로, 해결이 불가능하다는 의미는 아니다. 실제로 비탈릭은 기술 발전으로 트릴레마 문제를 극복 가능하다고 여러 번 언급하였다.

트릴레마 외에도 이더리움의 과도하게 높은 가스비는 큰 골칫덩이이다. 이더리움에서 스마트 컨트랙트를 실행하면 수수료 명목으로 비용이 청구되는데, 이 비용을 가스비라 한다. 가스비는 탈중앙화된 환경에서 소프트웨어를 동작시키는 노드의 운영비용으로 노드 제공자에게 전달된다. 헌데 이더리움 토큰의 가격이 올라가고, 스마트 컨트랙트 사용자가 많아지면서 가스비가 과도하게 비싸졌다.

간단한 송금이나 결제를 수행하려 해도 한화로 대략 몇만원 정도 소요되는데, 소액결제라도 할라치면 배보다 배꼽이 더 큰 상황이 발생하게 된다. 이더리움의 가스비는 특성상 사용자 간에 경쟁이 발생하면 급등하는 성격이 있기 때문에, 어떤 서비스가 인기를 끌면 끌 수록 가스비가 높아지게 될 것이고, 가스비가 높아지면 사용자 부담이 발생하면서 서비스의 인기를 하락시키는 주요 원인이 될 수 있다.

업데이트를 통해 가스비를 낮췄음에도 최근 이더리움 기반 NFT 발행이 붐을 이루면서 경쟁이 치열해지다보니 가스비가 과도하게 올랐다. 지금이야 높은 가스비를 지불하고라도 얻은 NFT의 가격 상승분이 더 높기 때문에 괜찮은 것 처럼 보이지만 사용자 입장에서 가스비는 결국 이용 수수료이다. 수만원 ~ 수십만원 어치의 수수료를 지불하는 네트워크는 어느 시점이 되면 외연 확장의 한계에 도달할 것이다.

이더리움의 이러한 문제들을 해결하기 위한 다양한 시도가 있는데, 고쳐쓰거나, 이더리움 위에 덧대거나, 새로 만들거나 이렇게 3가지 접근이 있다.

이더리움 2.0 : 이더리움 자체를 고쳐쓰는 방법이다. 이더리움 2.0 업그레이드를 통해 기존의 문제들을 해결하겠다는 것이다.
레이어2 : 이더리움 네트워크 위에서 동작하는 레이어를 잘 만들어서 기존 문제를 해결하겠다는 접근이다.
3세대 블록체인 : 이더리움 네트워크를 대신하는 새로운 블록체인 네트워크를 구성하겠다는, 일명 이더리움 킬러들의 접근법이다.

3세대 블록체인 – 이더리움 킬러들


이더리움 2.0이나 레이어2 솔루션은 이더리움 생태계를 확장하는 방법이지만, 3세대 블록체인은 이더리움을 대체하는 새로운 플랫폼이므로 필연적으로 이더리움과 경쟁관계가 발생할 수 밖에 없다. 특히 이더리움의 초기 설계 헛점이라고도 할 수 있는 느린 트랜잭션과 높은 가스비, 이 두 가지를 개선한 프로젝트들의 도전이 거세다.

카르다노

최근 부진에 빠지긴 했지만, 카르다노는 가장 유력한 이더리움 킬러 후보다. 이더리움 재단의 전 CEO였던 찰스 호스킨슨의 주도로 만들어졌으며, 검증 단계가 엄격하고 단단한 아키텍처와 컨센서스 알고리즘을 보유하고 있기 때문에 다른 경쟁자에 비해 개발속도가 1~2년 정도 느리다는 단점에도 불구하고 많은 지지를 얻고 있다. 2021년 9월, 알론조 하드포크 업그레이드를 통해 스마트 컨트랙트가 가능해졌기에 생태계에 다양한 DApp(탈중앙화 앱, Decentralized Application)이 출시를 준비하고 있다. 2021년 12월에는 선대스왑이라는 Dex 의 런칭을 앞두고 있어, 솔라나에게 뺏긴 과거의 영광을 되찾을 수 있을지 기대를 모으고 있다.

아발란체

아발란체는 ‘로켓단’이라는 의문(?)의 단체가 작성한 논문, ‘Snowflake to Avalanche: A Novel Metastable Consensus Protocol Family for Cryptocurrencies‘이 ipfs.io에 올라오면서 시작되었다. 코넬 대학의 에밀 군 시러 교수는 이 논문에 영감을 받아 아발란체 프로젝트를 만들었으며, 높은 기술력과 안정성을 바탕으로 빠르게 구성된 DApp 생태계 덕에 강력한 3세대 블록체인 플랫폼 지위를 획득하였다.

솔라나

솔라나는 2021년에 가장 급성장한 3세대 블록체인으로, 카르다노를 제치고 차세대 이더리움 킬러로 각광받는 프로젝트이다. 아나톨리 야코벤코에 의해 만들어졌으며, 아직 메인넷 베타 버전을 운영중임에도 불구하고 생태계가 폭발적으로 성장하고 있다. 역사증명(Proof Of History)라는 창의적면서 과감한 방식의 컨센서스 알고리즘을 통해 트랜잭션 속도를 극적으로 높였으며(65k tps), 개발자 친화적인 툴체인 구성으로 DApp 생태계를 빠르게 확장하였다.

DApp 트렌드 – Dex , DeFi , P2E , NFT

사용자들에게 인기있는 DApp 트렌드를 살펴보면 블록체인 플랫폼 경쟁이 어떤 양상으로 흘러가고 있는지 대략적으로 유추할 수 있을 것이다. 다음은 블록체인 네트워크 상에서 동작하는 DApp들의 최근 30일(2021-11-03 ~ 2021-12-03) 사용자 순위 Top5를 주요 플랫폼 별로 정렬한 리스트이다. (Axie Infinity는 주요 플랫폼은 아니지만 최근 큰 인기를 얻고 있기 때문에 비교를 위해 추가하였다. 카르다노는 아직 DApp이 없다.)

이더리움 DApp Top5 (최근 30일 기준, 출처: dappradar.com

솔라나 DApp Top5 (최근 30일 기준, 출처: dappradar.com

아발란체 DApp Top5 (최근 30일 기준, 출처: dappradar.com

폴리곤 DApp Top5 (최근 30일 기준, 출처: dappradar.com

로닌(액시인피니티) DApp Top2 (최근 30일 기준, 출처: dappradar.com

DApp 사용자 수는 이더리움이 1위이기는 하지만 솔라나, 아발란체, 폴리곤의 사용자와 격차가 크지 않음을 알 수 있다. (참고로 액시인피니티는 이더리움 진영의 1위인 유니스왑을 압도하기도 한다!) 특히 솔라나의 Raydium이나 아발란체의 Trader Joe, 폴리곤의 QuickSwap 같은 경우 사용자 증가율이 각각 33%, 191%, 59% 로 가파른 성장세이다.

DApp의 종류는 Dex가 3개, NFT 거래소가 3개, DeFi 11개, 게임이 4개로, 단연 DeFi DApp의 수가 많고, 나머지는 대동소이함을 알 수 있다. NFT 거래소의 경우 전반적으로 사용자가 감소하는 추세이나, 아발란체의 NFTrade 같은 경우 예외적으로 사용자가 증가하고 있다.

또한 중요한 사건이 하나 있는데, 이더리움 DApp 3위인 Uniswap v3는 이더리움의 높은 가스비를 감당하지 못해 폴리곤으로 옮겨갈 예정이다. 폴리곤의 경우 레이어2 솔루션이기 때문에 이더리움 생태계 내에서의 이동이라 볼 수도 있겠으나, 전반적으로는 이더리움의 견고한 DApp 생태계가 높은 가스비와 낮은 트랜잭션 처리량이라는 구조적 문제로 도전자들의 위협을 받고 있는 것으로 보인다.

이더리움의 반격, 2.0

이더리움 진영도 도전자들의 위협을 보고만 있지는 않는다. 컨센서스 방식을 PoW에서 PoS로 개선하고 샤딩을 지원하는 이더리움 2.0 네트워크가 개발중이다. 이더리움 2.0 업그레이드는 비콘체인, 머지, 샤드체인 3단계로 나뉘는데 이중에서 비콘체인은 2021년 12월 1일에 적용되었다. 머지와 샤드체인은 2022년에 완료 예정이다.

이더리움2.0 업그레이드가 완료되면 낮은 tps와 높은 가스비 문제는 구조적으로 사라지게 된다. 또한 스마트 컨트랙트 언어로 WASM(웹 어셈블리)를 지원하게 되므로 javascript 언어로 DApp을 만들 수 있다. javascript는 개발자 커뮤니티가 매우 큰 언어이므로 현재보다도 많은 DApp의 출시를 기대할 수 있을 것이다.

elasticsearch 구성 참고사항

elasticsearch 는 워낙 유명하고 안정적으로 성장한 오픈소스라 공식 문서, 커뮤니티, 블로그, 소스코드 등 참고할 자료들이 넘쳐난다. 굳이 그 넘쳐나는 자료에 한 숟가락을 더 얹을 필요가 있겠냐마는, 프로덕션 환경에 elasticsearch 를 처음 도입하려는 사람에게 찾는 수고를 덜 수 있는 best practice 를 제공하는 것이 이 문서의 목적이므로 누군가에게는 도움이 될 것으로 생각한다. elk 스택이나 관리용 플러그인, 키바나 사용법 등은 필자가 잘 모르기도 하고 워낙 좋은 문서가 많기 때문에 다른 문서를 참조하는 편이 낫다. 이 문서는 elasticsearch cluster 를 구성할 때 참고할 만한 사항들을 정리하였다.

ECK(Elastic Cloud on k8s)

ECK는 k8s 에 거부감이 없다면 elasticsearch 운영환경으로 선택할 수 있는 좋은 옵션으로, elasticsearch 개발사인 Elastic 에서 공식 배포하는 k8s operator pattern 이다. k8s 환경에 빠르게 적용할 수 있으며, elasticsearch 특성을 고려한 리소스 / lifecycle 관리가 가능하다.

Operator pattern이란?

k8s에서 기본 제공하는 방식으로 서비스 운영하기엔 도메인 특성이 적합하지 않아 반복적인 설정/변경작업이 잦을 경우 직접 custom resource 를 작성할 수 있는데, 이 custom resource 를 관리하는 확장 인터페이스가 operator 이며, 이렇게 custom resource 와 opertor를 제작하여 k8s 를 사용하는 패턴을 operator pattern 이라 한다.

elasticsearch 적정 메모리

elasticsearch 는 메모리의 크기가 매우 중요하다. 다큐먼트로부터 인덱스를 생성할 때에도 메모리를 사용하고, 검색 속도 향상을 위한 캐시에도 메모리를 사용하기 때문에 elasticsearch 가 빠른 성능을 내기 위해선 메모리 리소스가 넉넉해야 한다. 그럼 그 넉넉한 메모리의 적정 규모는 얼마일까?

jvm compressed oop

대부분 elasticsearch 와 적정 메모리 관련 문서를 찾아보면 jvm heap 에 32Gbytes 이상 할당하지 말라는 내용이 많을 것이다. compressed oop 라는 기능으로 메모리를 효율적으로 쓸 수 있는 최대치가 32Gbytes 이기 때문이다. 절반은 맞고 절반은 틀렸다.

Compressed OOPs in the JVM | Baeldung
compressed oop란 0으로 padding 되어 실질적으로 사용하지 않는 주소 영역 3bit 도 addressing 에 활용하는 기술이다. 2^3 만큼 이득이므로 32bit(=4G) x 8 = 32Gbytes 까지 addressing 이 가능하다.

64bit 주소를 사용하면 메모리 공간 내에서 데이터 저장영역이 그만큼 줄어들기 때문에 효율성 측면에서 heap size는 32Gbytes를 넘지 않는 편이 좋았다. 과거에는 말이다.

zgc

zgc는 java11 부터 사용할 수 있는 신규 gc 이다. 벤치마크를 확인해 보면 성능이 매우 뛰어남을 알 수 있다. 허나 zgc를 사용하려면 compressed oop 를 쓰면 안된다. 0으로 채워지는 padding 영역을 zgc에서 사용하기 때문이다.

64bit 주소체계에서 18bit 는 사용되지 않는다. zgc는 저 사용되지 않는 공간을 활용하여 gc 의 성능을 높였다. 기회가 되면 zgc에 대해 따로 다뤄보겠다.
gc 벤치마크이다. zgc의 성능이 매우 높음을 알 수 있다. 또한 gc 에서 발생하는 latency 가 낮기 때문에 체감 성능은 기존 mark&sweep 이나 g1gc보다 월등히 높을 것으로 보인다.

compressed oop 는 32Gbytes 제약이 있으나 32bit 주소체계이기 때문에 메모리 공간을 효율적으로 사용할 수 있다. 반면 zgc는 64bit 주소체계이기 때문에 메모리 공간 사용률이 효율적이지 않지만 gc 성능이 빠르다. 그럼 둘 중에 어느 것이 효율적일까? 정해진 답은 없으나 heap size 가 32Gbytes 이하라면 compressed oop, 그 이상으로 heap을 크게 써야 한다면 zgc를 사용하는 편이 이득일 것으로 보인다.

lucene mmaped io

elasticsearch에 메모리를 할당할 때 주의할 점이 하나 더 있는데, 서버의 모든 메모리 리소스를 jvm에 할당하면 오히려 성능상 불리해질 수 있다. lucene은 memory mapped io 를 사용하는데, virtual memory 의 원활한 paging 을 위해 시스템 영역에 충분한 메모리를 남겨두어야 한다.

The Generics Policeman Blog: Use Lucene's MMapDirectory on 64bit ...
lucene 의 MMapDirectory 는 virtual memory를 사용하는데, virtual memory 의 성능을 위해서 시스템 영역에 충분히 메모리를 남겨둘 필요가 있다.

Maximum shards in node

elasticsearch 를 default 설정으로 사용하였을 때, index 하나 당 shard 갯수는 5개, node 당 maximum shard 갯수는 1000개이다. 필자는 이걸 모르고 index를 많이 만들었다가 shard 갯수가 1000 개를 초과하여 더이상 index를 생성하지 못하는 문제를 겪었다. node 당 shard 갯수는 설정으로 조절이 가능하므로 shard 를 늘리고 싶다면 클러스터 설정을 바꾸면 된다.

그렇다면 node 하나 당 적절한 shard 갯수는 얼마일까? elasticsearch 공식 블로그에 따르면 적절한 heap 1Gbyte 당 shard 20개가 적절하다고 하니 참고하길 바란다. (https://www.elastic.co/kr/blog/how-many-shards-should-i-have-in-my-elasticsearch-cluster)

Hot-Warm architecture

elasticsearch 는 저장 비용이 큰 편이다. 주어진 리소스가 데이터 양에 비해 충분히 넉넉하다면 별 문제가 없겠으나, 리소스를 효율적으로 사용하려면 아키텍처를 적절히 구성하여야 할 것이다. 일반적으로 최근 데이터는 검색을 자주하는 반면 오래된 데이터는 그렇지 않은 경우가 많은데, hot-warm 모델은 index 를 고성능 data node(hot) -> 저성능 data node(warm) -> 삭제 의 순서로 이동시키며 lifecycle을 관리하는 아키텍처이다.

hot-warm_cluster.png
Hot node 는 고성능의 저장장치와 높은 메모리로 구성, 빈번한 읽기/쓰기 작업을 감당할 수 있도록 한다. 최초에 Hot node에서 생성한 index는 일정 시간이 경과하면 Warm node 로 이동시킨다. Warm node 는 고용량의 저가 저장장치로 구성, 참조 빈도가 낮은 데이터를 보관하는 용도로 사용한다.

opendistro ISM

opendistro의 ISM(Index State Management) 기능을 사용하면 Hot-Warm architecture 의 index lifecycle 을 쉽게 관리할 수 있다. opendistro에는 다른 여러 부가기능들이 있으므로 관심있으면 공식 문서를 참조 바란다.

OSX 환경에서 CI/CD 자동화

현 시점에선 이 구성을 사용 안하지만 조사해 둔 내용이 아까워서 osx 환경에서 CI / CD 자동화 관련 참고할만한 문서 링크들을 정리해 둔다.

결론부터…

  • 여러 버전의 ios sdk 관리는 xcode-select / xserver 사용
  • unreal 이나 unity 같이 gpu / 하드웨어 특성을 타는 빌드 환경이 아니라면 vagrant
  • 하드웨어 종속성 탓에 물리적인 빌드환경이 필요하다면 ansible

ios sdk 빌드 관련 문서

원칙적으로 sdk는 하위 호환성을 보장하여야 한다. 허나 현실에서 반드시 최신 버전의 빌드도구가 하위 버전을 지원한다는 보장은 없다. 여러 프로젝트를 하나의 머신에서 빌드해야 하는데 sdk나 서드파티 라이브러리 버전이 서로 배타적일 때, 모든 프로젝트의 빌드환경을 만족하는 구성을 관리하는건 손이 많이 가는 일이다. 특히 ios sdk 의 패키지 / 종속성 관리는 애플에서 제공하는 대로 따르는 수 밖에 없기 때문에 더욱 귀찮다.애플에서 제공하는 문서를 잘 찾아보고 읽어야 한다.

인프라 자동화 관련 문서

osx가 리눅스나 윈도우처럼 docker 환경을 지원한다면 빌드환경을 구성하는게 편했을텐데 아쉽게도 osx 는 docker를 지원하지 않는다. vm 환경 또한 구성하는게 편하지는 않다. 공식적으로 os image 다운로드 링크를 제공해주지도 않을 뿐더러, osx 이외에 다른 host os 에서 osx vm을 실행하는건 불법이기 때문이다. 하지만 어떻게든 vm 환경을 구성할 수 있으며, vagrant 같은 vm 관리도구로 인프라를 자동화할 수 있다.

하지만 unreal 이나 unity 처럼 gpu 하드웨어에 종속적인 개발환경을 병행하여야 한다면 vm으로 빌드 인프라를 구성할 수는 없다. 이런 경우에 쓸 수 있는 좋은 솔루션으로 ansible을 추천한다. ansible galaxy 에는 ios 개발환경을 쉽게 자동화할 수 있는 양질의 레시피들이 제공되므로 참고해 보길 바란다.

겜섭알못의 게임서버 아키텍처 101 #1

서론

별다른 언급 없이 서버라 하면 당연히 웹서버를 떠올릴만큼, 웹기술은 서버-클라이언트 모델의 대세다. 웹 서버 기술은 상당히 많은 연구와 응용이 수십년간 진행되었기에 표준, 프레임워크, 아키텍처 등이 정형화되었고, 따라서 서버 개발자는 거의 도메인 모델 구현만 신경쓰면 되는 수준에 이르렀다. 허나 게임 서버는 웹과는 많은 것들이 다르다. 게임 장르와 대상, 목적에 따라 기술이나 아키텍처가 달라지기 때문에 정형화할 만한 영역이 많지 않다. 또한 웹과는 다르게 게임 진행중 서버-클라이언트는 연결성을 유지하고 있어야 한다. 이러한 요구사항, 기술적 차이 외에도 규모의 차이가 있다. 시장 규모, 매출을 기준으로 하면 게임 서버 시장이 결코 작지 않으나 개발자 인력 풀 관점에서 게임 개발자는 다른 도메인에 비해 소수다. 웹서버 개발 10년차 베테랑이라 할지라도 멀티쓰레드 환경에서 발생하는 다양한 동시성 문제나 소켓 통신에 따른 네크워크 문제, 위상 변화에 따른 위치문제 등을 현업에서 겪었을 가능성은 높지 않다. 때문에 게임 업계는 다른 업계 종사자가 전직하기엔 진입장벽이 높은 고인물들의 닫힌 시장인 경향이 있다.

본인의 커리어에 게임서버 백엔드 경험이 없기 때문에 이쪽 분야에 대한 기술적 특징이나 아키텍처를 짚고 넘어가야 할 필요를 느낀다. 다행스럽게도(?) 본인의 커리어가 워낙 잡스러웠던 탓에 다른 사람들 보다 게임 서버 기술을 이해하는데 조금은 낫지 않나 싶다. 이 글에선 게임 서버 기술 및 아키텍처의 일반론을 서술하고, 필요한 경우 웹 기술과 비교하면서 그 특징의 이해도를 높이도록 하겠다. 본인도 겜섭알못이기 때문에 글에 깊이가 없을 수 있음은 감안해 달라. 게임 개발에 흥미가 있는 주니어나 전업을 고민하는 개발자에게 도움이 되었으면 좋겠다.

Stateless vs Stateful

일반적으로 웹 컨텐츠는 모두에게 동일한 결과를 제공해준다.

웹은 기본적으로 stateless 하다.  100명의 사용자에게 하나의 동일한 페이지를 제공하는 것이다. 반면 게임은 기본적으로 이전의 상태를 저장하고 있어야 한다. 같은 아이템을 사용해도 사용자 레벨이나 능력치에 따라 다르게 적용되는 경우도 있고, PvP 상황에선 누가 먼저 공격을 했는지에 따라 결과가 달라질 수 있다.

물론 현재의 웹은 로그인도 필요하고 장바구니도 필요하고 결제, 배송 내역을 확인할 필요도 있기 때문에 이전의 상태값을 저장해야 한다. 비즈니스 모델의 요구사항이 사용자 개개인의 상태를 저장하도록 발전해왔기 때문에 웹에도 다양한 방법으로 이전 상태를 저장하는 기술이 추가되었다.

허나 어떤 특정 상황에서 웹기술로 이전 상태를 저장하는 데에는 근본적인 한계가 있다. 1) 아주 짧은 시간 동안, 2) 상태가 계속 바뀌는데, 3) 상태변화의 순서가 나와 타인에게 영향을 주는, mmorpg에선 일반적으로 발생하는 그런 상황은 웹기술로 처리하기가 곤란하다. 웹프로토콜이 http이기 때문이다.

가장 흔한 http 클라이언트인 웹브라우저로 예를 들어보자. 웹브라우저는 사용자가 브라우저를 사용하는 대부분의 시간동안 서버와의 연결이 끊어져 있다. 어떤 페이지에 접근을 하기 위해 요청을 하면 그때 서버와 연결을 하고, 응답을 받으면 바로 서버와 연결을 끊는다. 간단하게 원하는 리소스를 가져올 수 있는 장점이 있지만 리소스를 가져올 때 마다 매번 새로 연결 / 연결 해제하므로 아무리 응답이 빨라도 연결 수립을 위한 시간이 소요된다. 요즘 흔한 웹기반 모바일 게임 장르에 fps 나 mmorpg가 없거나, PvP 관련 컨텐츠가 존재하지 않는 이유가 웹기술의 이러한 한계 때문이다.

Realtime event

반면 게임서버는 빠르게 요청되는 유저 간의 이벤트를 순서대로 처리하기 위해 클라이언트/서버 간에 연결을 유지한다.  위치의 이동, 스킬 또는 아이템의 사용, 공격과 회피에 따른 상태 변경 등을 게임월드에 요청하면 게임서버는 이를 순서대로 적용하여 결과를 사용자에게 돌려주는데 이때 소요되는 시간은 게임 진행에 무리가 없도록 충분히 짧아야 한다.

Key pain points of web application development
웹페이지 컨텐츠를 기다려 주는 시간은 일반적으로 3초 정도라고 한다.

일반적으로 웹 컨텐츠의 응답시간은 3초 이내로 상정한다. 반면 게임 서버는 100ms 이내에 응답이 와야 원활한 플레이가 가능하다. 네트워크에서 가장 코스트가 큰 작업이 연결이므로 연결 비용을 줄이려면 게임 클라이언트/서버는 연결을 유지하여야 한다.

게임에서 요구되는 빠른 응답성과 연결성, 이 두 가지 특징은 서버 운영 및 아키텍처에 큰 도전 과제다. 후술할 게임 서버의 다른 특징들은 상기 두 가지 특징으로부터 파생되었다 하여도 과언이 아니다.

Logical / physical topology – Load balancing

다수의 사용자를 처리하기 위한 서버 운영 기술 중 여러 개의 서버를 로드밸런싱하는 방법이 있는데, 게임 서버에서는 이런 구성을 하기 전에 한 번 더 고민하여야 한다. 사용자 간에 데이터 교환이 일어나기 때문이다. 쿠팡이나 11번가 같은 쇼핑몰 사용자들은 서로 통신할 일이 없기 때문에 다른 사용자가 어느 서버에 접속하던 전혀 상관할 필요가 없다. 반면 게임 서버는 사용자 간 논리적 거리가 가까우면 물리적으로도 가까워야 한다. 최소한 서버간 데이터 교환에 걸리는 시간이 응답성에 영향을 주지 않을 정도로 짧아야 한다. 때문에 게임서버는 같이 게임을 진행하는 유저들을 물리적으로도 같은 서버에 위치시키도록 유도한다.

오버워치 서버 탐색기 A to Z : 이런 것도 가능해?! : 네이버 포스트
어떤 게임은 함께 플레이하는 유저들을 물리적으로 같은 서버에 위치시키도록 대기실에 입장하는 단계를 거치기도 한다.

이러한 토폴로지적 특징(특정 유저군을 물리적/논리적으로 동일한 위치에 모아야만 하는) 때문에 게임 서버는 로드밸런싱이 어렵다. 웹 서버의 로드밸런서 – 백엔드 구성을 떠올려보자. 웹의 특징 – connectionless / stateless – 에 따라 모든 서버는 독립적이다. 사용자는 어느 서버에 접속하여도 동일한 리소스를 제공받는다. 따라서 로드밸런서는 부하 분산의 역할에 충실하여 접속하는 사용자들을 골고루 분산시키기만 하면 된다. 허나 게임서버의 부하분산은 기술적 도전과제다.

Long connection – Scalability

컴퓨터 네트워크에서 가장 코스트가 높은 작업은 커넥션이다. 바꿔말하면 최초로 커넥션을 생성할 때 걸리는 시간이 가장 길다는 뜻이기도 하다. 물론 최근의 웹소켓이나 스트리밍 컨텐츠 같은 예외들이 있지만, 일반적으로 웹은 요청 시 마다 새로 연결을 생성하고 응답을 받으면 연결을 종료한다. 이러한 특성 때문에 웹서비스는 서버의 사용량에 맞추어 수평적 스케일링을 하기 용이하다. 모든 요청 시 마다 새로 커넥션을 생성하는 구조이기 때문에 기존 유저가 신규 서버에 접속하여 리소스를 요청하여도 문제가 없다.

반면 게임 서버 같은 경우는 앞서 말한 빠른 응답성을 보장해야 하기 때문에 커넥션을 매번 새로 맺는 비용을 감당할 수 없다. 따라서 게임 서버는 일반적으로 커넥션을 유지한 상태로 데이터 교환을 한다. 이러한 연결성을 유지해야 하는 제약조건 때문에 게임 서버는 스케일링이 웹서비스처럼 용이하지가 않다. 높은 연결성과 관련한 문제들은 물리적인 한계이기 때문에 게임 디자인 / 서버 아키텍처를 잘 구성하여 게임 서비스 중에도 스케일링을 하기 좋은 형태로 처음부터 설계하여야 한다.

게임의 성격에 따라 서버-클라이언트 네트워킹 외에 서버-서버 간 통신이 이루어져야 하는 경우도 많다. 다수의 월드 서버로 구성된 MMORPG 게임이 있다고 가정하자. 월드 내에서 일어나는 이벤트 중에선 모든 서버에 공유되어야만 하는 사건들도 존재한다. 이러한 니즈 때문에 게임 서버들은 각각의 게임 서버들 끼리 거미줄처럼 서로 통신하게 된다.

game_server_arch
좌측의 웹서버는 서버간 연결이 불필요한 반면, 우측의 게임서버는 거미줄처럼 n대n 통신을 하여야 한다. – 출처 NetEase/pomelo

하지만 이러한 n대n 연결 구조에서 큰 고민 없이 아키텍처를 구성하면 가용성 향상을 위해 서버를 늘렸을 때 오히려 커뮤니케이션 비용이 올라가버린다. 서버를 추가하였으나 도리어 성능이 저하되는 원치 않는 결과를 초래하기 때문에 주의하여야 한다.

다음 글에선 일반적인 게임 아키텍처 패턴, 그리고 게임서버와 관련된 기술들을 열거해 보도록 하겠다.

아이에게 프로그래밍 가르치기

예전부터 아이들에게 프로그래밍을 가르치는 일에 흥미가 있었다. 특히 올해는 우리집 멍멍이가 초등학교에 입학할 나이가 되었기 때문에 개발자 아빠로서 뭐라도 알려주어야겠다는 생각이 든다. 이참에 아이에게 프로그래밍을 가르칠 때 무엇을 염두에 두어야 하는지 글로 정리해 보려고 한다.

 

코딩 교육 의무화

2019년도부터 초등학생에게 코딩교육을 의무화하였다 한다. 사실 코딩 교육 의무화는 2015년부터 계획되어 있던 것이라 놀랄 일은 아니다. 다만 코딩이 공교육과 입시의 영역으로 들어오게 되었을 때 생길지 모를 부작용 때문에 각계에서 우려가 많은 것으로 안다.

본인은 현업 교사도 아니고 입시 전문가도 아니기 때문에 현 코딩 교육 실태에 대한 평가가 맞지 않을 수도 있음을 감안하고 들어달라. 코딩 교육은 2019년부터 의무화가 되었지만 현장 교육은 학교마다 차이가 있다. 어떤 곳은 저학년부터, 어떤 곳은 5/6학년부터 코딩 교육을 실시한다. 왜 이렇게 차이가 나는가 하면 아직 공교육의 영역에 들어올 만큼 커리큘럼이 자리를 잡지 못한 상태에서 의무화를 서둘렀기 때문이라고 생각한다. 이런 경우 예상할 수 있는건 코딩 교육의 난이도가 많이 낮을 것이라는거다. 실제로 교재들을 찾아보면 부교재의 품질(로봇을 움직인다던지, 악기를 연주한다던지)에 차이가 있을 뿐 코딩의 난이도가 높은 과정은 찾아볼 수 없었다. 허면, 앞으로도 그럴 것인가 하면 그건 알 수 없다. 향후 코딩 교육 난이도의 향방은 입시 포함 여부가 결정할 듯 하다. 입시에 코딩이 포함되려면 변별력을 두어야 할텐데, 사실 코딩문제를 어렵게 내려면 그 난이도는 끝도 없이 높일 수도 있다.

개인적인 의견을 말하자면, 본인은 그다지 주입식 교육을 좋아하지는 않는다. 코딩 교육도 주입식 교육과 그다지 어울리지는 않는 영역으로 보인다. 허나 만약 코딩을 현 입시제도 하에서 주입식으로 가르친다 가정한다면 다른 어떤 과목보다도 실용적이기는 할 것이라 본다. 12년 입시 공교육 체제에서 배운 지식 중 실용적인 학문이 얼마나 될까? 기껏해야 영어 / 제2외국어 / 한문 정도? 본인은 수학도 좋아했고 과학도 좋아했고 철학 / 문학 / 역사도 즐겁게 배웠다. 공교육 체제 아래 배운 이러한 지식들이 본인의 관점과 지평을 넓혀주었다는 점을 부인할 수는 없으나, 그 지식들은 실용성과는 정말 거리가 멀었다. 심지어 변별력을 위해 무의미하게 난이도를 엄청나게 올린 입시 스킬은 정말 쓰잘데기 없는 죽은 지식들이다.  80평생 동안 ‘반데르발스 힘’ 같은걸 써먹을 일이 단 한 번이라도 생길까?

반면 코딩은 조금 상황이 다르다. 예를 들어 대입 시험에 임의의 숫자 배열을 merge sort하는 방법과 그때의 시간복잡도를 증명하는 문제가 출제된다면 이것도 죽은 지식일까? 물론 실용성 측면이라면 좀 전에 예로 들은 ‘반데르발스 힘’과 큰 차이가 없을 지도 모른다. 허나 merge sort는 컴퓨터나 핸드폰을 사용하는 사람이라면 알던 모르던 매 순간 마주하는 알고리즘일만큼 우리 일상에 가까운 것이다. 심지어 입시 공부로 merge sort를 배운 학생이 어느 날 갑자기 영감이 떠올라 개선된 탐색 알고리즘을 제안할 수 있을지도 모른다. 왜냐하면 생각은 누구나 할 수 있고, 그 생각의 결과물을 제안하는 방법도 누구에게나 열려 있기 때문이다.

기존의 입시과목의 실용성이 0이라면 단언컨대 코딩은 살면서 반드시 쓰일 일이 생긴다. 이 글을 읽는 독자가 설령 비개발자여도 엑셀 함수나 받은 편지함 분류 따위의 간단한 자동화는 직장에서 필수적이다. 현재에도 이런데 미래에 12년간 코딩 의무교육을 이수한 사람이라면 얼마나 많은 일들을 자동화 해낼 수 있을까.

코딩 교육 의무화는 본인이 다루고자 하는 주제의 본질과는 살짝 거리가 있으므로 ‘코딩 교육은 본질적으로 주입식과는 맞지 않으나, 만약 주입식으로 가르친다면 그나마 다른 학문에 비해 실용적일 것이다.’ 정도로 정리해본다.

 

Computational thinking

입시 코딩은 일단 제껴두자. 입시를 제외한다면 코딩 교육은 무엇을 목적으로 하여야 할까? 필자는 computational thinking 이어야 한다고 생각한다. 다른 말로 하면 ‘문제 해결 능력’인데, 그냥 문제를 해결하는 것이 아니라 문제 해결의 절차를 실행 가능한 작은 단위로 나누어 서술하는 능력을 말한다. 문제해결의 절차는 논리적으로 서술할 수만 있으면 도구는 무엇이던 크게 상관이 없다. 아래 링크한 두 영상은 컴퓨터 없이 코딩 교육을 진행하는 좋은 예제다. 영상에서 작성한 문제 해결의 절차를 컴퓨터가 제공하는 명령어로 치환하면 바로 코딩이 된다.

EBS 소프트웨어야 놀자 – 쓱싹쓱싹 로봇 청소기

스크린샷 2020-02-16 오후 9.10.29

[한글] 샌드위치 만들기

[원본] Exact Instructions Challenge PB&J Classroom Friendly

스크린샷 2020-02-16 오후 9.26.02

코딩학원이나 학교의 교재를 살펴보면 엔트리나 스크래치 류의 블록코딩 언어부터 시작하는 경향이 있다. 이후에 라즈베리파이나 아두이노, 마이크로비트 같은 IoT 하드웨어를 제어하는 방법을 배우고, 파이썬이나 c언어 등의 프로그래밍 언어를 배우는 식으로 커리큘럼이 만들어져 있다.

블록코딩이나 IoT 기기들로 코딩을 시작하는 것이 나쁘다는건 아니다. 아이들은 쉽게 접할 수 있으면서 결과가 눈에 보이는 것들에 쉽게 흥미를 보인다. 허나 이런 것들은 본질이 아니므로 아이가 충분히 우수하다면 굳이 흥미 본위의 교육에 시간과 비용을 뺏길 필요는 없다. 가끔 블록코딩으로 엄청나게 복잡한 제어로직을 작성하거나 IoT 교재에 많은 비용을 들이는 경우가 있는데 굳이 그럴 필요가 없다는거다.

 

Structured programming

현대의 거의 대부분의 프로그래밍 언어는 구조적이다. 왜냐하면 프로그래밍 언어가 구조적이지 않을 경우, 그 복잡도가 인간이 제어할 수 없는 수준으로 높아지기 때문이다. 아래 링크한 아티클을 굳이 읽어볼 필요는 없으나 업계에서 전설처럼 회자되는 ‘~considered harmful’ 의 시조이자 비구조적 언어의 한계를 명백히 하여 현대의 프로그램 언어가 구조적으로 자리잡는 분기점이 된 사건이기에 링크해 보았다.

GOTO considered harmful(전설의 레전드의 시작…)

각설하고, 아이들에게는 구조적 프로그래밍의 구성단위만을 알려주면 된다. 컴퓨터에 특별한 소질을 보이면서 진짜 내부 동작원리를 궁금해 하는 아이라면 실제 컴퓨터의 명령어 수행은 비구조적이라는 사실을 알려줄 수도 있겠으나, 보통의 아이들에게 필요한 교육은 1) 순차 2) 분기 3) 반복 4) 함수 의 4가지 구성단위이다. 구조적 프로그래밍의 자세한 내용은 기회가 되면 다뤄보도록 하자.

 

그밖에 주제들

다음 주제들을 아이 가르칠 때 염두에 두어야 할 듯 하다. 허나 저학년이 이해하기에는 내용이 쉽지 않으므로 염두에만 두도록 한다. 혹시라도 고학년이나 우수한 학생들을 가르칠 기회가 생긴다면 아래 주제들을 다뤄보도록 하자.

  • 기계의 한계
  • 알고리즘
  • NAND to Tetris
  • No silver bullet – programming paradigm

OSX 의 exception handling 순서

오랜만에 올리는 블로그 글의 주제는 OSX 의 exception handling 순서이다.

OSX kernel 구조

애플 개발자 문서 링크 – Kernel Architecture Overview

Signal handling 순서를 설명하기 전에 알아둘 사전 지식, OSX 의 커널 구조를 알아보자.

C436D54C-9BDD-4FC0-B225-DAFF54156938

OSX 커널은 BSD와 Mach 의 디자인을 둘 다 따르고 있다.

맥 os 의 발전사를 깊이 알지 못하는 필자로선 이런 디자인이 캘베로스나 히드라의 ‘한 몸 – 두 머리’처럼 꽤나 기괴한 설계로 보인다. 운전석과 조수석에 각각 핸들이 달려있는 자동차를 상상해보자. 서로 핸들을 반대 방향으로 꺾을 경우, 이 자동차는 어디로 가야하는가?

다행히(?) OSX 에서 BSD와 Mach 사이에 이런 문제는 발생하지 않는다. 두 커널은 담당하는 레이어가 분리되어 있다. 아래 발췌해 온 문서를 참고해 보자.

Mach

Mach manages processor resources such as CPU usage and memory, handles scheduling, provides memory protection, and provides a messaging-centered infrastructure to the rest of the operating-system layers. The Mach component provides

  • untyped interprocess communication (IPC)
  • remote procedure calls (RPC)
  • scheduler support for symmetric multiprocessing (SMP)
  • support for real-time services
  • virtual memory support
  • support for pagers
  • modular architecture

General information about Mach may be found in the chapter Mach Overview. Information about scheduling can be found in the chapter Mach Scheduling and Thread Interfaces. Information about the VM system can be found in Memory and Virtual Memory.

BSD

Above the Mach layer, the BSD layer provides “OS personality” APIs and services. The BSD layer is based on the BSD kernel, primarily FreeBSD. The BSD component provides

  • file systems
  • networking (except for the hardware device level)
  • UNIX security model
  • syscall support
  • the BSD process model, including process IDs and signals
  • FreeBSD kernel APIs
  • many of the POSIX APIs
  • kernel support for pthreads (POSIX threads)

The BSD component is described in more detail in the chapter BSD Overview.

주저리 주저리 설명되어 있지만 거칠게 요약하면 Mach 커널은 리소스에 대한 보다 추상화된 영역을 관리하는 반면, POSIX 커널은 상대적으로 하드웨어에 밀접한 영역을 관리한다고 볼 수 있다.

 

Exception?

Exception 이란 ‘프로그램에서 정의하지 않은 동작을 처리하기 위한 추상화’ 이다. 텍스트 뷰어 프로그램을 예로 들면, 파일을 열고 안의 내용을 볼 수 있게 화면에 띄워주는 기능은 프로그램에서 정의한 동작이다. 허나 파일을 열려고 시도했는데 해당 파일이 지워져서 찾을 수 없다면? 더 이상 프로그램은 정의된 동작을 수행할 수가 없다. 이러한 ‘파일을 찾을 수 없는 상태’를 추상화하여 이를 제어하는 메커니즘을 제공하는 것이 Exception handling 이다.

 

Signal?

Signal 이란 OS에서 발생한 이벤트를 프로세스에게 전달하기 위한 software interrupt 이다. Signal은 경찰관이 부는 호루라기 소리와 같다. 운전자가 술을 먹고 중앙선을 침범하였을 때, 운전자를 멈춰세우고 면허를 취소시키기 위해 호루라기를 부는 것이다. 이때 보통의 운전자들은 경찰의 지시에 따라 모든 것을 체념하고 갓길에 차를 댄 후 면허증을 반납한다. 헌데 가끔씩 있다. 경찰의 부름에 제멋대로 GTA 를 찍는 돌+I 들이 말이다. 이와 유사하게, Signal이 발생했을 때 프로세스는 default action(종료, 덤프 생성 등)을 수행한다. 허나 default action을 따르지 않고 Signal handler 를 등록하여 동작을 바꿀 수도 있다.

 

Handling 순서

Exception 과 Signal 모두 하드웨어 리소스에 대한 프로세스의 잘못된 접근에 의해 발생할 수 있다. OSX의 경우, Mach exception handler 와 POSIX signal handler 를 둘 다 제공한다. OSX 프로그램에서 Exception handler 와 Signal handler 를 둘 다 정의한 경우 프로그램은 아래의 순서로 동작한다.

  1. 제일 먼저 Mach exception 이 발생한다.
  2. 발생한 Mach exception handler 가 있는지 찾아본다. 있으면 handler 수행 후 종료.
  3. exception handler 가 없으면 해당하는 POSIX signal 이 발생한다.
  4. 발생한 POSIX signal handler 가 있는지 찾아본다. 있으면 handler 수행 후 종료.
  5. Signal handler 가 없으면 EXC_CRASH exception 이 발생한다.
  6. Apple crash report server 로 관련 crash 를 전송한다.

 

한국에서는 시스템 프로그래밍을 할 기회가 많지 않은데, 하물며 OSX 계열의 시스템 api 라면 더더욱 관련 경험을 얻기가 쉽지 않다. 필자 역시도 일이 아니었더라면 OSX의 커널 아키텍처는 아마도 평생 찾아볼 일이 없었을 것이다. 혹시라도 누군가 OSX 의 하드웨어 exception을 처리하면서 어려움을 겪고 있다면 이 글이 도움이 되길 바란다. 참고로 iOS 도 구조가 유사하다.

도서 리뷰 – 알고리즘 산책: 수학에서 제네릭 프로그래밍까지

알고리즘 산책 수학에서 제네릭 프로그래밍까지에 대한 이미지 검색결과

길벗 출판사 도서 소개

프로그래밍은 추상화(abstraction)의 연속이다. 데이터를 추상화하고 흐름을 추상화하고 모듈과 빌딩블록을 추상화하며 심지어는 서비스와 어플리케이션도 추상화한다.

헌데 추상화라는 단어만큼 추상적인 것도 없다. 누군가는 추상화라는 단어를 듣고 타입 추상화를 떠올릴 것이며 누군가는 자료구조나 객체를 떠올릴 것이다. 컴퓨터 과학에서 누군가가 추상화라는 단어를 사용한다면 이 단어가 다양한 계층에서 서로 다른 의미로 쓰인다는 것을 미리 알고 있어야 이해에 도움이 될 것이다.

알고리즘 산책에서 다루는 추상화는 크게 두가지이다. 수학적 추상화와 제네릭 프로그래밍이다.  이 책은 제네릭 프로그래밍이 어떻게 데이터 타입을 추상화 하는지에 대하여 여러 수학적 사례를 들어 설명한다. 이를테면, 고대 이집트인들의 곱셈하는 방식으로부터 곱셈 알고리즘을 도출한 후 이를 점진적으로 개선한다. 이후 이를 일반화하여 덧셈 연산으로부터 닫혀있는 타입에서 동작하는 템플릿으로 알고리즘을 재구현한다. 이 과정은 매우 흥미롭다. 알고리즘을 개선하는 시도와 이를 어떻게 일반화하는지를 보여주면서 제네릭 프로그래밍의 목적이 무엇인지 독자에게 자연스럽게 알려준다.

꽤 많은 수식과 증명이 나온다. 때문에 수학과 친하지 않다면 쉽게 읽히는 책은 아니다. 허나 저자의 친절한 설명과 함께 코드 구현이 수반하기 때문에 고교 수학 과정을 성실히 이수하였다면 이해하는데 큰 무리는 없을 것이다. 틈틈이 수학과 수학자의 역사를 읽을거리로 제공하는 것도 이 책의 가치를 높인다.

c++처럼 type define이 자유로운 강타입 언어에서 제네릭 프로그래밍이 갖는 의미는 매우 크다. 문제 해결하는 방법을 일반화하고, 이 일반화된 문제해결 방법이 어떤 타입에 제한되는지(또는 어떤 타입에 적용 가능한지) 정의하는 방법이 어떻게 수학의 문제해결 방식과 맞닿아 있는지에 대한 설명이 탁월하다. 누군가가 제네릭 프로그래밍을 주제로 책을 추천해 달라 한다면 알고리즘 산책은 단연 선순위이다.

주개발 언어가 c++ 가 아닌 프로그래머라면 type에 조건을 주는 concepts 부분이 잘 이해가 가지 않을 수도 있겠다. 이번 기회에 이 책을 읽으면서 인식의 지평을 넓히는 계기가 되었으면 한다.

concepts 소개

마지막으로 이런 훌륭한 책을 리뷰할 수 있도록 출간해 주신 길벗 출판사에게 감사드린다.

kubernetes multi-node cluster 구성하기

kubernetes 공식 문서에서는 다양한 kubernetes cluster 구성 전략 을 제공하고 있다.

그 중 docker를 이용한 cluster 구성 방식(portable multi-node)으로 multi-node cluster 를 구성하는 방법을 기술한다.

설치 후 알게 된 사실

portable multi-node 는 kubernetes developer community 에서 업데이트가 뜸하다.  대신 kubeadm 활동이 활발하다.(관련링크) 아직 alpha release 이므로 좀 더 안정화될 때까지 기다렸다가(얼마 안남은 듯) kubeadm 으로 cluster 구성하는 것을 추천한다.

prerequisite
docker 만 있으면 됨
필자는 centos 7 에 설치하였다.

docker 설치
docker 설치는 이 사이트에서 하란대로 하면 쉽게 설치가 가능하다.

환경에 따라 docker daemon 이 올라오지 않는 문제가 있다.
필자의 경우에는 docker0 라는 가상 bridge 가 생성되지 않아서 강제로 docker0 브릿지를 생성하였다.

brctl addbr docker0
ip addr add 192.168.5.1/24 dev docker0
ip link set dev docker0 up
iptables -t nat -L -n

gnome gvfs 에서 io 를 엄청나게 점유해 버리는 문제가 있다.
관련 app / service 를 모조리 uninstall 하고 reboot 하자.

kubernetes docker image 설치

이 글을 쓰는 도중 kubernetes documentation 에서 docker multi-node 구성이 지워졌다.

앞으로는 kubeadm 으로 multi node 구성하면 된다. (링크 : https://kubernetes.io/docs/getting-started-guides/kubeadm/)

내가 이 글을 쓴 이유가 공홈 다큐멘테이션 대로 docker multi node 구성하면 설치가 안되기 때문이었는데 글 쓸 이유가 사라졌다.(ㅠㅠ)

글은 여기서 줄인다. 아마도 kubeadm 으로 설치하면 한방에 잘 설치 될거다.