프로토콜 버퍼
장점
- type safety 보장
- 스키마 위반 방지
- 보일러플레이트 코드 감소: 인코딩/디코딩 메소드가 자동으로 만들어진다.
- 빠른 직렬화: JSON보다 직렬화가 6배나 빠르다.
- 하위 호환성 제공
→ 마이크로서비스 같은 두 시스템 사이에서 통신하기 좋다.
로그
- 추가만 할 수 있는 레코드의 연속
- 순서가 있는 데이터를 저장, 공유, 처리할 때 사용
- 데이터베이스 복제, 분산 서비스 조율, 애플리케이션 상태 관리 등에 사용한다.
원리
- 여러 개의 세그먼트로 나눔: 무한한 용량의 디스크는 없기 때문
- 세그먼트: 저장 파일(store file) + 인덱스 파일(index file)
- 저장 파일: 레코드 데이터 저장
- 인덱스 파일: 레코드들의 인덱스를 메모리 맵 파일로 만들어 메모리 데이터를 다루는 속도로 빠르게 만들 수 있다.
- 메모리 맵 파일은 생성한 다음 크기를 바꿀 수 없기에 미리 필요한 크기로 만들어야 한다.
- 세그먼트 리스트는 항상 하나의 활성 세그먼트를 포함
- 활성 세그먼트: 유일하게 레코드를 쓸 수 있는 세그먼트
gRPC
과거 분산 서비스의 문제
- 호환성 유지
- API 버저닝으로 해결
- 서버-클라이언트 사이의 성능 관리
- 데이터베이스 쿼리 최적화
- 알고리즘 최적화
- 직렬화/역직렬화 빠르게
- 요청마다 연결을 만들지 말고 하나의 연결을 오래 사용
☝️internal 패키지는 마법과 같은 패키지로, 가까운 코드에서만 임포트 가능!
☝️ex) /a/b/c/internal/d/e/f의 코드는 /a/b/c 디렉터리와 그 아래에서만 임포트 가능
서비스 보안
- 주고받은 데이터 암호화 → 중간자 공격 대비
- TLS 핸드셰이크
- 클라이언트 인증(authentication)
- 일반적으로 서버 인증에만 TLS 사용, 클라이언트는 사용자명-비밀번호와 토큰으로 구현
- 내부 분산 서비스의 경우 TLS 상호 인증 사용
- 클라이언트 권한 결정(authorization)
- 리소스의 접근을 공유하고, 소유권의 레벨이 다양할 때 필요
- 가장 간단하게는 Access List를 이용해서 구현
시스템 관측
메트릭
시간의 경과에 따른 데이터 수치 측정
→ 저장공간과 쿼리 시간을 줄이기 위해 해상도를 줄일 수 있다.
종류
- 카운터: 이벤트 발생 횟수나 시스템 처리한 전체 바이트와 같은 숫자 추적
- 히스토그램: 요청의 기간과 크기 같은 데이터 분포
- 게이지: 현재 값을 추적
중요 메트릭
- 레이턴시
- 트래픽
- 에러
- 포화도: 서비스 용량
로그
시스템에 발생한 이벤트를 기록
문제 해결, 감사, 프로파일링에 도움
너무 적으면 디버깅이 어렵고, 많으면 무엇이 중요한지 찾기 어렵다.
→ 많이 로깅해 보고, 로그를 줄여나가는 방식 추천
트레이스
요청의 라이프사이클을 수집해서 요청이 시스템 내부로 흘러가는 것을 추적
하나 이상의 스팬으로 구성
- 스팬: 요청 처리의 일부
- 스팬은 부모/자식 또는 형제 관계를 가진다.
서비스 디스커버리
서비스에 연결하는 방법을 알아내는 과정
레지스트리 목록을 항상 최신으로 유지
- 레지스트리: 서비스, 서비스의 위치, 서비스의 이상 유무를 가짐
- 다운스트림 서비스가 레지스트리에 질의하여 업스트림 서비스의 위치를 알아내고 연결
문제점
- 클러스터 내 서버들이 어떻게 서로를 찾아낼 것인가?
- 클라이언트는 어떻게 서버를 찾아낼 것인가?
서프(Serf) 라이브러리
- 주키퍼나 콘술 등과 달리 중앙 레지스트리 아키텍처 X
- 각각의 서비스 인스턴스가 서프 노드로 작동
- 새로운 노드를 만들어 클러스터에 추가한다면, 새 노드는 이미 클러스터에 있는 노드 중 하나 이상을 가리켜야 한다.
- 각각의 서프 노드가 레지스트리를 관리
- 노드가 클러스터에 조인하거나 떠나면 서프는 모든 노드에 이벤트를 보낸다.
Consensus
합의 알고리즘은 분산 서비스가 몇몇 실패를 겪더라도 상태에 대해 동의하는 도구
→ 서버를 리더와 팔로워 관계로 나누어 팔로워가 리더의 데이터만을 복제하도록 해야한다.
래프트
- 리더 선출
- 팔로워들이 일정 시간 이상 리더의 하트비트 요청을 받지 못하면, 팔로워들은 candidate가 되고 선출을 시작
- 후보는 자신에게 한 표를 행사하고, 다른 팔로워에게 투표를 요청
- 다수의 득표를 받은 후보가 리더가 되고, 하트비트 요청을 보내면서 지위를 얻는다.
- term
- 논리 시계
- 후보가 선거를 시작하면 자신의 term을 하나 증가시킨다.
- 후보가 리더로 선출되면 팔로워들은 term을 똑같이 만들고, 다음 선거까지 변하지 않는다.
- 로그 복제
- 클라이언트가 요청을 보내면 리더가 받는다.
- 리더는 각 요청을 로그에 추가하고, 팔로워들에게 전달한다.
- 리더가 명령이 실행되었다고 생각하면 리더는 finite-state machine을 이용해 명령을 실행 후 클라이언트에 결과 회신
- 적절한 서버 수
- 3개 혹은 5개가 적당
- 래프트는 (N-1)/2 실패에 대처 가능 → 홀수가 유리
스냅샷
- 전체 로그를 복제하는 것에 비해 로그를 최소한으로 가질 수 있다.
- 새 서버를 부트스트랩할 때 효율적
다중화
- 하나의 포트에서 여러 서비스 제공 가능
- 첫 바이트에서 서비스 넘버를 전달한다.
로드 밸런싱
로드 밸런싱 전략
- 서버 프록시: 로드 밸런서가 자체적으로 서비스 레지스트리에 질의하여 해당 요청을 백엔드 서비스로 전달
- 외부 로드 밸런싱
- 클라이언트 로드 밸런싱: 클라리언트가 서비스 레지스트리에 질의한 후, 어디에 요청을 보낼지 결정하여 바로 서버로 전달
라운드 로빈 로드 밸런서
- 첫 번째 호출을 1번 서버에게, 두 번째 호출을 2번 서버에게 전달하는 방식으로 작동한다.
- 각각의 요청, 클라이언트, 서버에 대한 정보를 고려하지 않는다.
- 읽는 것은 replicas, 쓰기는 primary에게 요청해야 하는 경우
- 지리적으로 가까운 서버와 통신하는 경우
- 레이턴시에 민감한 경우
쿠버네티스
오픈소스 오케스트레이션 시스템으로 컨테이너에서 자동 배포, 확장, 서비스 운영에 사용
- Pod
- 쿠버네티스에서 배포할 수 있는 가장 작은 단위
- 모든 컨테이너는 파드 안에서 실행하고, 네트워크 네임스페이스, IPC 네임스페이스, 볼륨 등을 공유
- Controller
- 제어 루프로, 리소스의 상태를 지켜보고 필요하면 수정
- Statefulset
- 다음 경우에 필요(ex: PostgresSQL)
- 안정적이고 고유한 네트워크 ID
- 안정적으로 유지되는 저장소
- 순서가 있는 배포와 확장
- 순서가 있는 자동 무중단 업데이트
- 다음 경우에 필요(ex: PostgresSQL)
- Probe: 서비스의 신뢰성을 향상하고자 컨테이너에 작업해도 되는지 여부를 판단
- liveness probe
- 컨테이너가 살아있는지 확인
- 컨테이너 생애 내내 호출
- readiness probe
- 트래픽을 받을 준비가 되었는지 확인
- 컨테이너 생애 내내 호출
- startup probe
- 컨테이너 애플리케이션이 시작됐는지, liveness와 readiness를 호출해도 되는지 확인
- 스타트업이 되고 나면 다시 호출하지 않는다.
- liveness probe
- 서비스: 애플리케이션을 네트워크 서비스로 노출
- ClusterIP: 클러스터 내부 IP로 노출
- NodePort: 노드의 정적 포트로 노출
- LoadBalancer: 클라우드 프로바이더의 로드밸런서를 이용해서 노출
- ExternalName: DNS명을 별칭으로 제공하는 특별한 서비스
'golang' 카테고리의 다른 글
goroutine 파헤치기 (0) | 2023.09.23 |
---|---|
Formatting in golang (0) | 2023.09.03 |
Internal package in golang (0) | 2023.09.03 |
실무에 바로 쓰는 Go 언어 핸즈온 가이드 (0) | 2023.07.12 |
[후기] entgo 두달 사용 후기 (0) | 2022.07.23 |