2. • 유은총
• 2015년부터 Spoqa에서 근무
• 2011년부터 Amazon Web Services 사용
• 4년째 Amazon ECS 사용
• 전업 Python 프로그래머
• 2017년부터 TypeScript 사용
• 2015년부터 React 사용
• 2014년부터 Rust 프로그래밍 시작
• 잡역부 풀스택 프로그래머
2
발표자
3. 동종업계 1위의 오프라인 매장 포인트 멤버십 및 고객 관리 솔루션
누적 이용자 수 1,700만명, 가맹 매장 수 10,000점 (한국˙일본 합계)
위치 정보 기반의 오프라인 타겟 광고 서비스
핵심 파트너
3
Spoqa
4. • 2014년까지 도도 포인트는 커다란 모놀리틱 시스템으로 돌아가고 있었음
• 마이크로서비스로 가자
• 각 팀에서 제각각 서비스를 만들기 시작하면 답이 없겠다
• 적어도 배포 시스템은 일원화해야 한다
4
마이크로서비스 아키텍쳐로의 여행
5. • 괜찮은 패키징 솔루션
• 언어˙라이브러리와 무관하게 사용 가능
• 로컬에서도 이미지 빌드 및 실행이 가능하다
• EC2 + AMI보다 가벼운 배포 절차를 구성할 수 있음
• 클러스터링은 어떻게 하지……
• Docker Swarm이 갓 등장한 시기
• CoreOS? DC/OS?? Kubernetes???
5
Docker
6. • Geofront의 전례를 따라……
• https://spoqa.github.io/2014/07/09/geofront.html
• 스포카 마이크로서비스를 위한 플랫폼 구축이 목표
• 최종적으로 private subnet 안에 ECS 클러스터를 구성하는 것으로 마무리
• 원대한 목표가 있었지만 사이드잡이 다 그렇듯이 용두사미로 끝남
• 당초 계획했던 통합 배포 시스템이나 서비스 디스커버리 같은 건 온데간데없고……
6
Project Blackmoon
8. • 결국 다른 팀에 계시던 분께서 만들어냄
• Stateless: 별도의 DB 없이 동작
• Docker Hub + Slack Button API
• AWS Lambda 위에서 도는 가벼운 서비스
• Python + Zappa
• 일단 설정하고 나면 Slack에서 간단히 배포 가능
8
Longinus
9. • 셋업이 귀찮음
• 새 서비스를 추가할 때마다 손으로 웹훅 주소를 Docker Hub 콘솔에다 붙여 넣어야
• Stateless의 한계
• 필요한 정보를 모조리 웹훅 URL에 때려넣으니 URL 길이 제한에 걸린다
• 결국 몇몇 서비스는 하드코딩하게 됨
• 로컬 테스트는……?
• 개발용 Lambda를 따로 띄워서……??
9
그렇게 한동안 버텼지만……
10. • 갑자기 일반인 코스프레
• Longinus를 계승
• 최대한 설정 없이 쓸 수 있는 방향으로
• Docker Hub 대신 Amazon ECR을 쓰자
• 로컬 테스트를 하기 쉽게 만들자
• AWS Lambda + Python ➡ Docker + Rust
10
Crane
12. • 시스템 프로그래밍 언어
• 조합 가능한 멀티코어 프로그래밍
• 패턴 매칭 + 제네릭
• 강한 타입 + 타입 추론
• Cargo 패키지 시스템
• 멀티플랫폼
• 메모리 안전
• 쓰레기 수집기(GC) 없음
• 매크로
12
Rust?
13. • 예전 프로젝트에서 그럴싸하게 써먹어 본 적이 있음
• https://www.rust-lang.org/ko-KR/friends.html
• 통상적으로는 C++ 또는 Java와 쓰이는 분야가 겹치는 언어
• 이걸로 웹 서비스도 만들 수 있을까?
• C++ 같았으면 가성비가 안 나오는 일이지만 Rust라면……?
13
Why Rust?
15. • Slack API 및 웹훅
• 웹훅 요청을 받을 웹 서비스가 필요
• 일반 웹훅 API로는 안 됩니다 Slack App을 만들어야 함
• ECR에 이미지가 올라오는 시점을 알아내기
• CloudTrail ➡ CloudWatch Events ➡ SQS
• 주기적으로 ReceiveMessage에 롱 폴링
• 보통 15초 정도 이내에 반응함
• 새 이미지로 ECS 서비스 배포
• 새 이미지와 같은 이름을 쓰는 작업 정의를 죄다 찾고 업데이트
• 업데이트된 작업 정의를 쓰는 서비스를 죄다 찾아 배포
• ECR을 사용한다면 별도의 설정이 필요없음
15
시작
16. • 모르겠고 일단 쓰레드 따서 돌린다!
• SQS로부터 메시지를 받을 쓰레드를 웹 서비스가 도는 쓰레드와 별도로 실행
• 내친김에 워커 쓰레드도 하나 만들자
• 쓰레드 사이의 통신은 MPSC 채널을 사용
• 하다 보니까 대책없이 쓰레드를 쓰고 있는데 이거 괜찮을까?
16
Fearless Concurrency!
17. • 트레이트(trait): 일종의 인터페이스
• 동시성 프로그래밍의 안전을 지키는 두 트레이트
• Send: 쓰레드 경계를 넘을 수 있음 (예: 참조를 포함하지 않는 대부분의 타입)
• Sync: 여러 쓰레드가 참조해도 괜찮음 (예: Mutex<T>, RwLock<T>, AtomicI32 등)
• 위의 규칙을 만족하지 않는 코드는 컴파일 에러
• 언어적으로 참조 공유와 값 변경이 극도로 제약되어 있음
• 뮤텍스가 보호하는 값은 반드시 보호될 거라는 확신이 생김
• 전례없이 편안한 마음으로 멀티쓰레드 프로그래밍을 할 수 있습니다
• 동적 타입 언어 쓰다가 정적 타입 언어로 돌아올 때의 그 느낌처럼
17
괜찮아, 문제없다
18. • 직업 프로그래머로 일하기 시작할 적에 새겨두었던 교훈
• 멀티쓰레드를 써야 할 것 같다면, 일단 다시 생각하라
• 그래도 써야 할 것 같다면, 멀티프로세싱이나 여하간 다른 방법을 찾아라
• Python GIL이 제 프로그래머로서의 가치관에 큰 영향을 끼쳤습니다 😂
• 결과적으로 제대로 된 멀티쓰레딩 경험이 전무한 상태였음
• 왠지는 모르겠지만 봇이 자꾸 먹통이 된다
• 메모리 누수 같은 리소스 누수나 논리적인 동시성 오류까지 Rust 컴파일러가 잡아주지는 못하더라
• 쓰레드를 잘 죽이려면 어떻게 해야 하지?
18
그러나
19. • 컴파일러와의 끝없는 투쟁
• 경험자들의 증언: 잘못될 가능성이 있는 코드는 다른 언어보다 Rust로 쓰기가 더 어렵더라
• 생각보다 그런 코드가 많습니다
• 가끔 논리적으로는 맞지만 타입 검사를 통과 못하는 경우를 밟기도 함
• 다행히 지속적으로 개선되고 있습니다 #
• 함수형 프로그래밍만큼은 아니더라도 어느 정도 코딩 습관을 바꾸게 됩니다
19
안전의 대가
22. • Rust용 AWS SDK (like boto)
• https://www.rusoto.org/
• 대부분의 AWS 서비스를 지원함
• SQS, ECS, ECR 모두 사용 가능했음
• SO HUGE!
• 모든 API 요청˙응답˙에러에 대한 구조체와 열거형이 각각 따로 만들어져 있음
• 컴파일 시간 증가에 혁혁한 공로
22
Rusoto
23. • CloudWatch Events로 ECS 클러스터 상태 이벤트를 수신할 수 있음 #
• 컨테이너 인스턴스나 서비스 상태를 모니터링한다거나 하는 데 쓸 수 있겠다
• 또는 배포 기록을 보존하거나
23
데이타베이스도 좀 써 봅시다
24. • Rust용 ORM 중에 가장 멀쩡한 라이브러리
• http://diesel.rs/
• 마이그레이션, 쿼리 DSL, 타입과 스키마 간 매핑 등등 기본적인 기능은 갖추고 있음
• PostgreSQL, MySQL, SQLite 지원
• 유의점
• Rust는 객체 지향 언어가 아닙니다
• 그래서 엄밀히 말해 이건 ORM은 아닙니다
• Java나 Python 같은 OO 언어에서와는 조금 다른 설계가 필요합니다
24
Diesel
26. • Rocket
• Flask처럼 생김
• Rust여서 가능할 법한 기능들: request guards
• Flask처럼 보이기 위해 일부 nightly compiler 기능에 의존
• 비동기 지원 없음
• Rouille
• Rust스러운 웹 프레임워크 디자인을 찾기 위한 시도
• 하나의 커다란 request ➡ response 루프와 같은 구조
• 사실상 표준인 Hyper, Tokio 등과 아무 상관 없는 스택
• 역시나 비동기 지원 없음
26
웹 프레임워크
27. • https://actix.rs/
• 적당한 절충안
• 그럭저럭 익숙한 생김새
• 모든 것이 비동기!
• Actix 액터 프레임워크 기반
• 내친 김에 쓰레드 쓰던 코드들을 전부 액터 기반으로 갈아버림
27
Actix-web
28. • 그래도 멀티쓰레드보단 많은 경험이 있었음
• Gevent/Eventlet, asyncio, Promise 등등...
• Futures
• Promise랑 비슷합니다
• 프로덕션용으로는 0.1이 널리 쓰임. 0.3을 준비 중인 상태
• Tokio
• Futures 기반의 비동기 I/O 프레임워크
• Work-stealing task executor가 딸린 기본 런타임 제공
28
비동기 프로그래밍
29.
30. • 소유권 및 lifetime 검사와 안 좋은 방식으로 상호작용
• 사실상 참조를 쓸 수 없는 수준
• async fn + Futures 0.3이 미래다
• 그래서 제가 미래에 살고 있는데요 nightly compiler의 세계는 험난하네요
• sync ➡ tokio-core ➡ tokio로 가는 과도기를 직격으로 겪음
• Rusoto가 꽤 늦게 트렌드를 따라 온 편이라 섞어 쓰느라 고생했었습니다
• Actix vs Tokio
• Actix 자체 런타임은 전체 애플리케이션 수준에서 단일 쓰레드를 강제 (tokio-core의 잔재)
• 일부 액터에 한해 쓰레드 풀링으로 동작
30
헬게이트 오픈
32. • 예상했던 대로 rapid prototyping은 좀 어려움
• 메모리는 정말 조금 먹습니다 64MiB 이하
• Crane 자체도 ECS 서비스라 자기 자신을 배포하는 것도 되고 나름 만족하고 있습니다
• 비동기 프로그래밍을 하고 싶으시면 일단 기다리세요
• 올해 안에 async fn이 포함된 Rust 2018 edition이 나옵니다
• 웹 프레임워크는 일단 Actix-web 쓰세요 😂
32
그래서 할만하던가요
33. • 서비스 롤백
• 클러스터 유지보수 자동화
• 웹 콘솔
• 신규 서비스 등록
• 회사 내 다른 서비스도 Rust로?
• CI와 연동 및 이미지 자체 빌드 (AWS CodeBuild!)
• 여러 리전에 걸친 클러스터 관리하기
• 오픈소스?
• Slack App으로 공개?
33
앞으로 하고 싶은 것
35. 서울 본사
서울특별시 강남구 테헤란로 420
메이플타워 9층
TEL 02-544-6463 FAX 02-544-6460
부산 지사
부산광역시 부산진구 부전동 426-7
신동아오피스텔 1007호
TEL 02-544-6463 FAX 02-544-6460
주식회사스포카
SEOUL·BUSAN·TOKYO
도쿄 지사
〒151-0053 東京都渋谷区代々木1-21-12ヤマ
ノ26ビル 3F
TEL 03-6869-3610
Question?
36. • a = b는 기본적으로 복사가 아닌 이동
• 언제나 하나의 값에 하나의 변수만 존재함. 구조체 필드에 넣거나 함수 인자로 전달해도 마찬가지.
• 마지막으로 값을 담았던 변수가 사라지는 지점이 그 값이 메모리에서 해제되는 지점
• 이동 의미론은 바꿀 수 없음. 언제나 C memcpy/memmove와 동등한 동작
• 복사는 a = b.clone()으로 명시적으로 하는 것이 기본
• 복사가 안전한 타입에 한해서 a = b로 복사가 가능
• 참조(&T)는 C 또는 C++의 포인터처럼 동작
• 어떤 참조도 자기가 참조한 변수보다 오래 남아있을 수 없음
• mut를 붙이지 않은 모든 변수와 참조는 불변(immutable)
• 동시에 하나의 &mut만 존재할 수 있음 (iterator invalidation 같은 걸 방지)
• 위에서 설명한 규칙은 모두 컴파일 시간에 검사됨
36
부록: Rust 주요 기능 요약
37. • enum은 tagged union
• match 식으로 패턴 매칭을 해서 원하는 값을 꺼낸다
• 널 포인터가 없고 명시적으로 None을 나타낼 수 있는 Option<T>가 존재
• 에러 처리에 쓰이는 Result<T, E>도 enum 타입
• 트레이트(trait)는 일종의 인터페이스. 제네릭 타입을 한정짓거나 동적 디스패치를 하는 데에 쓰임.
• 다른 라이브러리의 타입이나 i32 같은 기본 타입에 내가 정의한 트레이트를 구현하는 게 가능함
• Haskell의 타입 클래스 같은 겁니다
• Send나 Sync 같은 일부 트레이트는 자동으로 구현됨
• 클래스 같은 건 없어요
37
부록: Rust 주요 기능 요약 (2)