클라우드 네이티브 설계

  • 클라우드에서 최적화된 애플리케이션을 설계, 배포, 운영하는 방식
  • 마이크로서비스, 컨테이너 오토스케일링, 분산 시스템을 중심으로 하며, 애플리케이션을 독립적으로 배포하고 관리

vs. monolithic

  • 모놀리식은 모든 기능이 하나의 코드베이스로 구성.
  • 모든 기능이 함께 배포되고 관리됨
  • 초기에는 간단하지만 시간이 지나고 기능이 늘면 확장성과 유지보수 측면에서 어려움이 생김

  • 클라우드 네이티브는 각 기능을 독립적인 마이크로서비스로 구성
  • 확장성과 유지보수를 용이하게 한다.
  • The Twelve-Factor App은 확장 가능하고 유지 보수성이 높은 애플리케이션을 개발하기 위한 표준이자 원칙이 된다.
  • 12요소는 각기 독립적인 모듈로, 애플리케이션이 다양한 클라우드 환경과 하이브리드 환경에서 일관되게 동작하게 하고, 관리할 수 있도록 돕는 설계 원칙이다.

12 Factor App

  1. codebase
    • 하나의 코드베이스를 가져야하며, 이 코드베이스는 버전 관리 시스템에 의해서 추적되어야 한다.
    • 하나의 코드베이스를 기반으로 다양한 환경(dev, stage, prod)을 지원할 수 있다.
    • 모든 환경은 동일한 코드베이스에서 파생된다.( 다른 버전의 코드를 사용하지 않는다. )
    • 동일한 코드베이스로부터 빌드된 여러 배포가 존재할 수 있다.
  2. dependencies(Explicitly Declare Dependencies)
    • 애플리케이션이 의존하는 모든 라이브러리, 패키지를 명시적으로 선언해야 한다.
    • 이를 통해 일관된 빌드를 보장하고, 종속성 관련 문제를 최소화한다.
    • 모든 의존성은 완벽히 관리되고 동일한 패키지를 사용하도록 보장된다.
  3. config(Store Config in the Environment)
    • 설정정보는 코드 베이스가 아닌 환경 변수나 외부 설정 파일로 분리해야 한다.
    • 이는 다양한 환경에서 일관되게 동작할 수 있도록 한다.
    • 설정 정보가 코드에 포함되지 않아서 보안 문제도 해결할 수 있다.
  4. backing services(Treat Backing Services as Attached Resources)
    • DB, MessageQueue, Cache 같은 외부 서비스는 백엔드 리소스로 취급된다. (연결된 자원)
    • 애플리케이션은 이들 서비스와 독립적으로 실행되어야 한다.
    • 백엔드 서비스의 위치나 구성 변경 시에도 애플리케이션의 코드에 수정이 필요하지 않도록 해야 한다.
    • 이 접근법은 애플리케이션이 어느 환경에서도 일관되게 동작할 수 있도록 보장한다.
  5. build release run(Strictly Separate Build and Run Stages)
    • 애플리케이션은 build, release, run 단계를 명확히 구분해야 한다.
    • 빌드 단계에서는 코드를 컴파일하고, 릴리즈 단계에서는 빌드된 아티팩트와 설정 정보를 결합하며, 실행 단계에서는 해당 릴리즈 버전을 실행한다.
    • CI/CD를 통해서 빌드하고, 결과물을 환경 설정과 결합하여 릴리즈 버전을 생성한다.
    • 그리고 배포하여 실행한다.
      • 코드와 설정 정보 분리 이유는 동일한 코드베이스가 다양한 환경에서 설정만 변경하여 사용할 수 있도록 하기 위함이다.
  6. processes
    • 애플리케이션은 상태를 가지고 있지 않아야 한다.
    • 만약 상태를 가져야 한다면 db같은 backing service에 저장되어야 한다.
  7. port binding(Export Services via Port Binding)
    • 애플리케이션이 특정 포트를 통해 외부와 통신할 수 있도록 하는 것이다.
    • 외부와의 통신을 위해서 포트 바인딩을 통해서 서비스를 제공해야 한다.
    • 이를 통해서 애플리케이션은 웹 서비스와 같은 외부 프로세스에 의존하지 않고 자체적으로 HTTP 트래픽을 처리할 수 있다.
    • 웹서버를 이용할 수도 있고, 로드밸런서도 쓸 수 있다.
    • 애플리케이션이 포트에 직결해서 서비스 할 수 있다면 더 유연하다.
    • 애플리케이션이 외부에 의존 없이 포트 바인딩으로 자체적으로 서비스 제공하면 애플리케이션의 독립성과 확작성을 높인다.
  8. concurrency(Scale Out via the Process Model)
    • 애플리케이션은 프로세스 기반으로 동시성을 처리해야 한다.
    • 여러 프로세스나 인스턴스를 통해서 확장성을 제공한다.
    • 이는 수평 확장을 통해서 더 많은 트래픽을 처리할 수 있게 한다.
      • 동시성: 여러 작업이 동시에 처리될 수 있도록 프로세스를 병렬로 실행하는 것
    • 애플리케이션은 프로세스 모델을 통해서 동시성을 제공한다.
    • 프로세스 간 상태 공유 없이 독립적으로 동작할 수 있어야 한다.
  9. disposability
    • 프로세스는 언제든 처분될 수 있으며, 이는 언제든지 실행되거나 종료될 수 있음을 의미한다.
    • 프로세스의 시작 시간은 최소화되어야 한다.
    • 짧은 시작시간은 릴리즈, 스케일 업에 유연성을 부여한다.
    • 종료는 우아하게 되어야 한다.
    • 또한 고장 발생시 갑작스런 종료에 견고해야 한다.
  10. dev/prod parity
    • 최대한 dev/stage/prod 간 차이가 크지 않아야 한다.
    • 시간적 차이: 개발 단계에서 배포 전까지 기간이 반드시 존재한다.
    • 인원적 차이: 개발은 개발자가, 배포는 엔지니어가 한다.
    • 도구적 차이: 각 단계에서 사용하는 도구가 다르다. - 즉 이 차이들을 줄이려면 주기적으로 지속적으로 배포해야 한다. - 또한 인원 차이를 문서화등 명시적으로 작성하는 것으로 극복해야 한다. - 마지막으로 개발, 배포 간 차이를 줄여야 한다.
  11. logs(Treat Logs as Event Stream)
    • 애플리케이션에서 발생하는 모든 로그는 이벤트 스트림으로 처리되야 한다.
    • 별도의 로그 처리 시스템에서 중앙화 하여 관리할 수 있다.
    • 로그는 단순 출력이 아니라, 애플리케이션의 상태를 모니터링하고 문제를 디버깅하는 중요한 데이터 소스다.
      • EventStream: 애플리케이션에서 발생하는 이벤트를 시간 순서대로 처리하는 방식이다. 이를 통해서 실시간 데이터 분석, 모니터링이 가능하다.
  12. admin processes(Run Admin/Management Tasks as One-off Process)
    • DB 마이그레이션이나 백업 같은 관리자 작업은 애플리케이션의 코드와 별도로 독립적인 프로세스로 관리되어야 한다.
    • 이러한 작업은 운영 중인 애플리케이션과 분리되며, 애플리케이션에 영향을 미치지 않도록 처리한다.
    • 관리 작업은 독립적 프로세스로 실행되어야 한다.
    • 이 작업은 애플리케이션에 영향을 미치지 않아야 한다.

1. CAP

  • CAP 이론은 Consistency, Availability, Partition Tolerance 중 두 가지를 선택해야 한다는 이론이다.
  • 이 세가지 요소는 서로 상충되기에, 한 시스템에서 세 가지를 동시에 완벽하기 어렵다.
  • 아키텍쳐 설계 단계에서 뭘 더 중점을 둘지를 정해야 한다.
  1. Consistency
    • 일관성은 분산 시스템에서 모든 노드가 동일한 데이터 상태를 유지하는 것을 의미한다.
    • 한 노드에서 데이터를 업데이트하면, 그 업데이트는 다른 모든 노드에 즉시 반영되어야 한다.
    • 장/단점
      • 장점: 데이터 신뢰성, 정확성이 높아진다.
      • 단점: 모든 데이터를 즉시 동기화해야 해서, 네트워크 지연이나 장애 발생 시 가용성이 떨어진다.
  2. Availability
    • 가용성을 장애가 발생하거나 네트워크가 분리되더라도 모든 요청에 대해서 응답할 수 있는 능력을 의미한다.
    • 이는 분산 시스템의 중요한 목표 중 하나다.
    • 사용자 요청을 무조건적으로 처리해주는 것을 보장한다.
    • 장/단점
      • 장점: 사용자 경험이 지속적으로 유지, 시스템 가동 중단을 최소화할 수 있다.
      • 단점: 노드 간에 일관성이 일시적으로 깨질 수 있다.
  3. Partition Tolerance
    • 파티션 허용성은 네트워크 분할이 발생했을 때도 시스템이 정상적으로 동작할 수 있는 능력을 의미한다.
    • 분산 시스템에서 네트워크 연결이 끊기거나, 일부 노드가 다른 노드와 연결되지 못하는 상황을 의미한다.
    • 이런 경우에도 시스템은 가능한 한 정상적으로 작동해야 한다.
    • 장/단점
      • 장점: 네트워크 분리 시에도 서비스가 계속 제공될 수 있다.
      • 단점: 일관성과 가용성 중 하나를 포기해야 한다.

cap.png

  • 일관성 우선: 데이터 정확성이 중요한 경우 선택. 가용성과 파티션 허용성을 어느정도 희생
  • 가용성 우선: 대규모 트래픽을 처리해야 하는 경우 선택.
  • 파티션 우선: 글로벌 서비스를 제공하는 클라우드 환경에서 선택

2. 데이터 복구, 장애 복구

  • 분산 시스템에서 데이터를 여러 노드로 복제하여 시스템의 고가용성을 보장하는 것이 중요하다.
  • 데이터 복제는 한 노드에서 데이터를 변경했을 때, 그 변경 사항이 다른 노드에서도 복사되는 것을 의미한다.
  • 이를 통해서 장애가 발생해도 데이터를 잃지 않고 시스템을 빠르게 복구할 수 있다.
  1. 데이터 복제의 중요성
    • 고가용성 보장: 하나의 노드에 장애가 발생하더라도 다른 노드에 동일 데이터가 있어서 서비스 중단이 없다.
    • 데이터 손실 방지: 실시간으로 데이터를 여러 노드에 복제함으로써, 시스템 장애나 데이터센터의 물리적 손상에서도 데이터 복구가 가능
  2. 복제 방법
    • 동기 복제: 데이터 변경할 떄, 모든 노드가 데이터 변경 사항을 즉시 반영한다. 이는 데이터 일관성을 보장하지만, 지연 시간이 길어질 수 있다.
    • 비동기 복제: 데이터 변경 후, 일정 시간이 지난 후에 데이터를 복제한다. 짧은 시간 일관성이 떨어질 수 있지만, 시스템 성능과 가용성이 높아진다.
  3. 장애 복구 전략
    • 자동 복구(Self-healing): 클라우드 네이티브 시스템은 장애가 발생하면 자동으로 복구될 수 있도록 설계되어야 한다.
    • 백업, 복원: 중요 데이터는 정기적으로 백업하고, 장애 발생 시 이를 통해서 복원해야 한다. 데이터 손실을 최소화 하기 위해서 여러 데이터 센터에서 복제된 백업을 보유하는 것이 좋다.

3. 네트워크 레이턴시

  • 분산 시스템에서 성능을 저하시키는 요소 중 하나
  • 노드 간 이동하는데 걸리는 시간을 의미한다.
  • latency가 길어지면 응답 시간이 길어져 사용자 경험에 악영향을 미친다.
  • 따라서 통신을 최적화해야 한다.
  1. 레이턴시 발생 원인
    • 지리적 거리: 사용자 - 서버 간 물리적 거리가 멀면 느려질 수 있다.
    • 네트워크 혼잡: 네트워크가 많은 트래픽을 처리해야 하면 느려질 수 있다.
    • 잘못된 라우팅: 네트워크 내에서 잘못된 경로로 전송하면 추가적 레이턴시가 발생할 수 있다.
  2. 최적화
    • CDN 사용: 지리적으로 가까운 서버에서 받을 수 있도록 한다.
    • DNS 최적화: 라우팅을 최적화 하여 사용자가 가까운 서버에 연결되도록 한다.
    • 오토스케일링 및 로드 밸런싱: 트래픽이 많으면 자동으로 서버를 추가하고, 트래픽을 분배해서 네트워크 혼잡을 방지한다.
    • 서비스 간 통신 최적화:
      1. 서비스 메시: 서비스 메시를 사용해서 마이크로서비스 간 통신을 효율적으로 관리할 수 있다. 서비스 메시를 통해서 통신 경로를 최적화하고, 성능을 모니터링할 수 있다.
      2. RPC: 원격 노드 간의 통신을 최적화하는 방법이다. 서비스 간 빠른 데이터 전송을 가능하게 한다. gRPC는 RPC를 개선하여, 데이터 전송 속도를 높였다.

4. ServiceMesh/ API gateway

4.1. 서비스 메시

  • 분산 시스템에서 마이크로 서비스 간의 통신을 최적화하는 구조다.
  • 여러 개의 마이크로 서비스가 통신할 때, 서비스 메시가 각 서비스 간의 트래픽을 라우팅하고, 보안 및 모니터링을 담당한다.
  • 주요 기능
    1. 트래픽 관리: 서비스 간의 통신 경로를 최적화 하고, 필요한 경우 트래픽을 제한하거나 우선순위를 설정
    2. 보안: 서비스 메시를 통해서 마이크로서비스 간의 통신을 암호화하고, 인증 및 권한 부여 기능을 통해 보안을 강화
    3. 모니터링 및 로깅: 서비스 메시가 모든 서비스 간의 트래픽을 감시하고, 성능을 모니터링한다. 또한, 로깅 기능을 통해 문제가 발생한 서비스의 통신 기록을 분석할 수 있다.

4.2. API gateway

  • 외부 클라이언트가 여러 마이크로서비스에 접근할 수 있도록 라우팅을 제공하는 진입점 역할을 한다.
  • 클라우드 네이티브에서는 중요한 역할을 하며, 서비스 메시와 사용되기도 한다.
  • 주요 기능
    1. 트래픽 라우팅: API 게이트웨이는 클라이언트 요청을 적절한 마이크로서비스로 라우팅하여 각 서비스가 개별적으로 처리할 수 있도록 한다.
    2. 보안: 보안, 인가, 데이터 검증 등의 기능을 제공하여, 외부 클라이언트 - 내부 마이크로서비스 간의 보안을 강화한다.
    3. 로드밸런싱: 여러 마이크로서비스 간 트래픽을 분산하여, 특정 서비스에 과부화가 걸리지 않도록 조정한다.

API 설계와 버저닝

  • 마이크로 서비스 간 통신을 관리하고 데이터를 주고받는 중요한 요소
  • 서비스 간 연결성을 보장
  • 클라우드 네이티브에서는 여러 마이크로서비스가 독립적으로 배포되고 관리되기에 각 서비스가 다른 서비스와 통신할 수 있도록 API 설계, 버저닝이 중요
  • 잘 설계된 API는 확장성, 유지보수성 그리고 서비스 간 안정적 통신을 보장

a. API 설계 원칙

1. RESTful

  • REST(REpresentational State Transfer)
  • 인터넷 사이 상호 운용성을 제공하는 방법

    1.1. 어떻게 정보를 공유할 것인가?

  • 장점:
    • 표준화되니 방식으로 설계되어, 다양한 클라이언트에서 사용 가능함
    • 무상태성으로 인해 서버 확장성이 높다.
    • 직관적인 설계를 통해 자원에 대한 접근을 쉽게 이해할 수 있다.
  • 단점
    • 대규모 요청에서 불필요한 데이터 전송이 발생할 수 있다
    • 요청한 데이터 외에도 많은 자원 정보를 보내기 때문에 대역폭이 낭비될

      GraphQL

  • facebook이 개발한 쿼리 언어, 클라이언트가 단일 엔드포인트롤 통해서 필요한 데이터만 요청할 수 있다.
  • 불필요한 정보 송수신을 줄일 수 있다.
  • 장점
    • 클라이언트가 필요한 데이터만 요청할 수 있어 효율적이다.
    • 하나의 엔드포인트에서 다양한 자원에 접근이 가능하다.
    • API 변경 시에도 클라이언트와의 호환성을 유지할 수 있다.
  • 단점
    • 복잡한 쿼리 로직이 많다면 서버 부하가 생길 수 있다.
    • GraphQL 스키마를 정의하고 유지관리하는데 추가적인 작업이 필요하다.

      gRPC

  • 구글에서 개발한 RemoteProcedureCall 프레임워크
  • 고성능 통신을 위해서 프로토콜 버퍼를 사용해서 데이터를 직렬화 하고 비동기 통신을 지원한다.
  • 마이크로서비스 간의 저지연, 고성능 통신을 위해서 사용된다.
  • HTTP/2 프로토콜 기반으로 작동한다.
  • 장점
    • 프로토콜 버퍼를 사용한 데이터 직렬화로 매우 빠른 데이터 전송이 가능
    • HTTP/2 기반으로 다중 요청 및 스트리밍을 지원해서 네트워크 효율이 뛰어남
    • 언어에 구애받지 않음
  • 단점
    • RESTful에 비해 상대적으로 복잡
    • 브라우저에서 직접적으로 사용하기 어려움(프록시 서버가 필요)

b. 버전 관리 전략

  • 여러 버전으로 존재해야 할 수도 있다.
  • 기능이 추가되면서 기존 명세는 유지해야할 수도 있다.
  • 이 경우 버전 관리가 중요하다.
  • URI, Header, Parameter 세 가지 종류가 있다.
    1. URI: /v1/users
      • 장점:
        • 클라이언트가 명확하게 버전을 선택할 수 있다.
        • URL에 버전을 포함해서 쉽게 관리할 수 있다.
      • 단점
        • 여러 버전 API를 유지 관리해야해서 복잡할 수 있다.
    2. Header: API-Version: v1
      • 장점:
        • URI를 변경하지 않고도 버전관리를 할 수 있다.
        • 클라이언트가 헤더를 통해서 유연하게 API를 선택할 수 있다.
      • 단점
        • 클라이언트에 요청 헤더를 추가해야한다.
        • URI에 버전 명시가 없어서 가시성이 떨어진다.
    3. Parameter: ~?version=v1 // ~;version=1
      • 장점:
        • 클라이언트가 요청 파라미터로 버전을 쉽게 설정할 수 있다.
        • 버전이 API 경로와는 별개로 관리되기 때문에 URI 설계에 영향을 주지 않는다.
      • 단점:
        • URL이 복잡해질 수 있다. 파라미터를 일일이 관리해야 한다.
        • 쿼리 또는 경로 파라미터가 없는 케이스를 처리해야 한다.