서비스
고친 사람 github-actions[bot]
서비스는 다른 프로그램이 부르면 일을 해 주고 그동안 계속 떠 있는 제공 단위입니다. 회원 번호를 받아 그 회원의 정보를 돌려주는 프로그램이 그런 것입니다. 한 번 돌고 끝나는 프로그램과 달리 서비스는 요청을 기다리며 머무릅니다. 백엔드에서 이 말이 가리키는 물건은 맥락마다 달라서 상세에서 하나씩 가릅니다.
쉽고 빠른 이해
서비스는 부르면 일을 해 주는 제공 단위입니다. 회원 정보를 물으면 돌려주는 프로그램 하나가 떠 있습니다. 여러 곳에서 그것을 부릅니다.
같은 일을 하는 코드를 부르는 쪽마다 복사해 두면 고칠 때 전부 고쳐야 합니다. 한 군데 두고 부르게 하면 고칠 곳이 하나입니다.
어떻게 도나:
- 제공하는 쪽이 프로그램을 띄우고 부를 창구를 엽니다
- 부르는 쪽은 서비스 이름으로 그 창구를 찾습니다
- 요청을 보내고 응답을 받습니다
대가는 호출이 프로그램 밖으로 나간다는 것입니다. 상대가 죽거나 응답이 늦어지는 일을 부르는 쪽이 감당해야 합니다.
부르는 쪽이 하나뿐이고 같이 배포한다면 떼지 않고 같은 프로그램 안의 함수로 둡니다.
상세
카드를 잃어버리면 새벽에라도 카드 뒷면에 적힌 번호로 전화를 겁니다. 전화를 받은 사람이 그 카드를 막아 줍니다. 누가 언제 걸지 모르니 그 번호는 밤새 전화를 받습니다.
서비스는 요청을 받아 처리하고 결과를 돌려주는 제공 단위입니다. 회원 번호를 받아 그 회원의 정보를 돌려주는 프로그램이 그런 예입니다. 요청이 없는 동안에도 꺼지지 않고 다음 요청을 기다립니다.
같은 처리를 여러 곳에서 해야 할 때 코드를 곳곳에 복사해 두면 고칠 일이 생길 때마다 전부 찾아 고쳐야 합니다. 한 군데만 띄워 두고 나머지가 그것을 부르게 하면 고칠 곳이 하나로 줄어듭니다. 그 부분만 따로 늘리거나 배포할 수도 있습니다.
서비스를 이루는 조건
어떤 프로그램을 서비스라고 부르려면 셋이 갖춰져야 합니다.
첫째는 계속 떠 있는 것입니다. 요청이 언제 올지 모르므로 꺼져 있으면 부를 수 없습니다. 프로그램을 띄워 두고 요청을 기다리게 만드는 것이 서비스의 출발입니다.
둘째는 부를 창구입니다. 창구는 대개 네트워크 포트 하나입니다. 부르는 쪽은 그 포트로 요청을 보냅니다. 프로그램은 그 포트에 들어오는 요청을 계속 받습니다.
셋째는 이름입니다. 부르는 쪽이 상대의 주소를 코드에 박아 두면 상대가 옮겨 갈 때마다 코드를 고쳐야 합니다. 이름만 적어 두고 주소는 부를 때 찾으면 뒤가 바뀌어도 부르는 쪽은 그대로입니다. 이름으로 주소를 찾아 주는 장치를 서비스 디스커버리라고 부릅니다.
셋이 한 번의 호출에서 이렇게 맞물립니다.
sequenceDiagram
participant C as 부르는 쪽
participant D as 서비스 디스커버리
participant S as 서비스
C->>D: 이 이름의 주소를 알려 달라
D-->>C: 주소와 포트
C->>S: 그 포트로 요청을 보낸다
S-->>C: 응답
부르는 쪽이 코드에 적어 둔 것은 이름 하나입니다. 주소와 포트는 부를 때마다 찾습니다.
맥락마다 서비스가 가리키는 물건
「서비스」는 백엔드에서 제일 자주 쓰이는 말입니다. 가리키는 물건은 맥락마다 다릅니다. 공통점은 「부르면 해 주는 제공 단위」라는 뜻입니다. 갈리는 것은 그 이름이 붙은 물건의 종류입니다.
일곱 줄을 다 외울 필요는 없습니다. 이 글이 이어서 다루는 것은 셋째 줄, 배포 단위로서의 서비스입니다.
| 맥락 | 「서비스」가 가리키는 것 | 물건의 종류 |
|---|---|---|
| 운영·조직 | 사용자에게 내놓는 시스템 전체 | 제품 |
| 서버 운영체제 | 부팅 때 떠서 계속 도는 프로그램 | 프로세스 |
| 시스템 구성 | 따로 배포하고 따로 늘리는 한 덩이 | 배포 단위 |
| 원격 호출 선언 | 원격으로 부를 수 있는 메서드 묶음 | 선언 |
| 오케스트레이션 도구(프로그램을 여러 서버에 나눠 띄워 주는 도구) | 같은 프로그램 여러 벌 앞에 서는 고정 이름과 주소 | 이름 |
| 애플리케이션 코드 | 화면도 저장소도 아닌 처리를 모아 둔 계층 | 클래스 |
| 모바일 앱 | 화면 없이 뒤에서 도는 구성 요소 | 앱 구성 요소 |
표에서 헷갈리기 쉬운 것이 원격 호출 선언입니다. 원격 호출을 쓰는 곳에서는 도는 프로그램이 아니라 무엇을 부를 수 있는지 적어 둔 선언을 서비스라고 부릅니다. 아래가 그런 선언 하나입니다.
service UserService {
rpc GetUser (GetUserRequest) returns (User);
}
이 글에는 도는 프로그램이 없습니다. 「이 이름으로 이런 요청을 보내면 이런 응답이 온다」는 약속만 적혀 있습니다. 부르는 쪽은 이 선언을 보고 상대를 대신 불러 주는 스텁을 만들어 씁니다.
오케스트레이션 도구에서도 이 말을 씁니다. 다만 그 안에서 서비스는 프로그램이 아니라 같은 프로그램 여러 벌 앞에 붙여 둔 고정된 이름입니다. 뒤의 프로그램은 늘거나 줄거나 갈립니다. 이름은 바뀌지 않습니다.
이름 하나 뒤에 서는 복제본들
여기서부터 「서비스」는 표 셋째 줄의 뜻으로 씁니다. 따로 배포하고 따로 늘리는 한 덩이를 말합니다.
요청이 많아지면 같은 프로그램을 여러 벌 띄웁니다. 이렇게 띄운 것 하나하나를 복제본이라고 부릅니다. 이때 부르는 쪽이 복제본의 개수와 주소를 알아야 한다면 복제본이 하나 늘 때마다 부르는 쪽을 전부 고쳐야 합니다. 그래서 앞에 이름 하나를 세우고 그 뒤로 요청을 나눠 보냅니다.
flowchart TD
C["부르는 쪽"] --> N["서비스 이름 · 고정된 주소"]
N --> LB["로드 밸런서 · 어느 복제본에 넘길지 고른다"]
LB --> P1["복제본 1"]
LB --> P2["복제본 2"]
LB --> P3["복제본 3"]
P1 --> ST["바깥 저장소"]
P2 --> ST
P3 --> ST
부르는 쪽은 이름 하나만 압니다. 복제본이 셋에서 열로 늘어도 부르는 코드는 바뀌지 않습니다. 이 고정된 이름 뒤에서 들어온 요청을 어느 복제본에 넘길지 고르는 일은 로드 밸런서가 맡습니다.
이 구조 덕분에 복제본 하나가 죽어도 나머지가 요청을 받습니다. 대신 요청마다 다른 복제본에 갈 수 있으므로 앞 요청에서 알게 된 내용을 프로그램 안에 들고 있으면 안 됩니다. 들고 있어야 하는 것은 데이터베이스나 캐싱 같은 바깥 저장소로 내보냅니다. 그림 아래쪽의 저장소를 복제본 셋이 함께 보는 것이 그 모양입니다.
서비스로 떼는 대가
이 절에서도 서비스는 앞 절과 같은 뜻입니다. 떼어낸 한 덩이를 말합니다. 한 덩이를 떼어내면 같은 프로그램 안의 함수 호출이 서비스 호출로 바뀌고 호출이 프로그램 밖으로 나갑니다. 밖으로 나간 호출은 안에서는 겪지 않던 일을 겪습니다.
함수 호출의 결과는 성공 아니면 실패입니다. 서비스 호출에는 셋째가 있습니다. 응답이 오지 않는 것입니다. 상대가 요청을 받기는 했는지도 알 수 없습니다. 부르는 쪽은 얼마나 기다릴지를 미리 정해 둡니다(타임아웃). 다시 부를지도 정해야 합니다(재시도).
주고받는 값도 그대로 건너가지 않습니다. 객체를 바이트로 바꿔 보내고 받은 쪽이 다시 객체로 되돌립니다. 이 왕복에 직렬화 비용과 네트워크 지연이 붙습니다.
고장이 번지는 모양도 달라집니다. 한 서비스의 응답이 늦어지면 그것을 부르던 서비스에 요청이 쌓입니다. 그 서비스를 부르던 쪽도 같이 밀립니다. 이렇게 번지는 것을 중간에서 끊으려고 서킷 브레이커 같은 장치를 둡니다.
flowchart TD
A["앞단 서비스"] --> B["가운데 서비스"]
B --> BR["서킷 브레이커 · 여기서 호출을 끊는다"]
BR --> C["느려진 서비스 · 응답이 안 온다"]
C -->|요청이 쌓인다| B
B -->|요청이 쌓인다| A
호출은 위에서 아래로 갑니다. 밀림은 아래에서 위로 되돌아옵니다.
한 프로그램 안에 두는 경우
떼는 값이 늘 남는 것은 아닙니다. 부르는 쪽이 하나뿐이고 같은 사람들이 같은 때에 배포한다면 떼어도 얻는 것이 없습니다. 함수 호출로 끝날 일에 네트워크와 모니터링이 따라붙습니다.
떼는 판단은 대개 셋 중 하나가 걸릴 때 섭니다. 이 부분만 따로 늘려야 할 때, 다른 팀이 따로 배포해야 할 때, 이 부분이 멈춰도 나머지는 돌아야 할 때입니다.
셋 다 아니면 같은 프로그램 안의 모듈로 두고 경계만 분명히 그어 둡니다. 모듈을 나중에 떼어내는 일보다 떼어 놓은 서비스를 도로 합치는 일이 품이 더 듭니다.
관련 항목
서비스를 이루고 띄우는 단위
프로세스 · 데몬 · 포트 · 컨테이너 이미지 · 파드 · 디플로이먼트 · 레플리카셋 · 노드
서비스를 부르는 방식과 창구
API · RPC · gRPC · REST · HTTP · 스텁 · 엔드포인트 · 메시지 큐
서비스를 찾고 요청을 나누는 장치
서비스 디스커버리 · DNS · 로드 밸런서 · 리버스 프록시 · 게이트웨이 · 서비스 메시 · 사이드카
서비스를 여러 덩이로 나누는 구성
마이크로서비스 · 모놀리스 · 서비스 지향 아키텍처 · 소프트웨어 아키텍처 · 분산 시스템 · 무상태
서비스가 멈추거나 밀릴 때 쓰는 장치
타임아웃 · 재시도 · 서킷 브레이커 · 헬스 체크 · 고가용성 · 폴백 · 벌크헤드
서비스의 품질을 재고 약속하는 지표
서비스 수준 계약 · 서비스 수준 목표 · 서비스 수준 지표 · 골든 시그널 · 관측성 · 오류 예산
서비스를 굴리며 하는 작업
배포 · 롤링 업데이트 · 오토스케일링 · 모니터링 · 온콜 · 용량 계획 · 카나리 배포
같은 이름으로 불리는 플랫폼별 구성 요소
서비스 계층 · 도메인 서비스 · 윈도 서비스 · 액티비티 · 브로드캐스트 리시버
다른 이름: service · services