12단계 앱 으로 알려진 가이드라인은 10년 전에 처음 발표되었습니다. 그 이후로 거의 모든 의무적 관행이 웹앱을 작성하고 배포하는 사실상의 표준 방식이 되었습니다. 앱이 구성되고 배포되는 방식이 변경된 상황에서도 이러한 원칙은 여전히 적용 가능하지만, 어떤 경우에는 앱을 개발하고 배포하기 위한 마이크로서비스 패턴에 이러한 원칙이 어떻게 적용되는지 이해하기 위해 추가적인 이해가 필요합니다.
이 블로그는 환경에서 구성 저장의 요소 3에 초점을 맞추고 있으며 다음과 같이 설명합니다.
마이크로서비스로 이동하면서 여전히 이러한 지침을 준수할 수 있지만, 항상 12팩터 앱의 문자 그대로의 해석에 정확히 매핑되는 방식은 아닙니다. 환경 변수로 구성 데이터를 제공하는 것과 같은 일부 지침은 훌륭하게 이어집니다. 12단계 앱의 핵심 원칙을 존중하는 반면, 다른 일반적인 마이크로서비스 관행은 이를 확장한 형태에 더 가깝습니다. 이 게시물에서는 Factor 3의 관점에서 마이크로서비스를 위한 구성 관리의 세 가지 핵심 개념을 살펴보겠습니다.
마이크로서비스에 맞게 Factor 3를 적용하는 것에 대한 논의에 들어가기 전에 몇 가지 핵심 용어와 개념을 이해하는 것이 도움이 됩니다.
모노리식 애플리케이션을 사용하면 조직의 모든 팀이 동일한 애플리케이션과 관련 인프라에서 작업합니다. 모놀리식 앱은 일반적으로 문서상으로는 마이크로서비스보다 간단해 보이지만 조직이 마이크로서비스로 전환하기로 결정하는 데에는 몇 가지 일반적인 이유가 있습니다.
물론 마이크로서비스에는 복잡성 증가, 관찰성 감소, 새로운 보안 모델 필요성 등 고유한 과제가 따릅니다. 하지만 많은 조직, 특히 대규모 또는 빠르게 성장하는 조직은 고객에게 제공하는 경험에 대한 안정적이고 신뢰할 수 있는 기반을 구축하기 위해 팀에 더 많은 자율성과 유연성을 제공하기 위해 이러한 과제를 감수할 가치가 있다고 결정합니다.
모놀리식 앱을 마이크로서비스로 리팩토링하는 경우 서비스는 다음을 수행해야 합니다.
단일 앱의 경우 프로세스 상의 불일치나 공유된 가정에 대한 종속성은 그다지 중요하지 않습니다. 그러나 개별 마이크로서비스가 많으면 이러한 불일치와 가정으로 인해 많은 어려움과 혼란이 생길 수 있습니다. 마이크로서비스를 변경하는 데 필요한 변경 사항 중 많은 부분은 기술적으로 필요하지만, 놀랍게도 그 중 상당수는 팀이 내부적으로 작업하고 다른 팀과 상호 작용하는 방식과 관련이 있습니다.
마이크로서비스 아키텍처를 적용한 주요 조직 변화는 다음과 같습니다.
마이크로서비스 아키텍처의 한 영역에서는 요소 3을 확장해야 하는데, 여기에는 구성을 포함한 서비스에 대한 특정 중요 정보를 명확하게 정의하고 다른 서비스와 최소한의 공유 컨텍스트를 가정해야 할 필요성이 있습니다. 요소 3은 이를 직접적으로 다루지는 않지만, 애플리케이션 기능에 기여하는 개별 마이크로서비스의 수가 많은 경우 특히 중요합니다.
마이크로서비스 아키텍처의 서비스 소유자로서 귀하의 팀은 시스템 전체에서 특정 역할을 수행하는 서비스를 소유합니다. 귀하의 서비스와 상호 작용하는 다른 팀이 코드와 문서를 읽고 기여하기 위해 귀하의 서비스 저장소에 액세스해야 합니다.
또한 소프트웨어 개발 분야에서 팀 구성원은 개발자가 회사에 합류하거나 떠나는 경우뿐만 아니라 내부 조직 개편으로 인해 자주 바뀐다는 것은 불행한 현실입니다. 또한, 주어진 서비스에 대한 책임도 종종 팀 간에 이전됩니다.
이러한 현실에 비추어 볼 때, 귀하의 코드베이스와 문서는 매우 명확하고 일관성이 있어야 하며, 이는 다음을 통해 달성됩니다.
많은 애플리케이션 프레임워크는 필요한 구성을 정의하는 수단을 제공합니다. 예를 들어, Node.js 애플리케이션용 convict
NPM 패키지는 단일 파일에 저장된 완전한 구성 "스키마"를 사용합니다. 이는 Node.js 앱을 실행하는 데 필요한 모든 구성에 대한 진실의 원천 역할을 합니다.
강력하고 쉽게 발견할 수 있는 스키마를 통해 팀원과 다른 사람들이 서비스를 자신 있게 사용할 수 있습니다.
애플리케이션에 필요한 구성 값을 명확하게 정의했다면 배포된 마이크로서비스 애플리케이션이 구성을 가져오는 두 가지 기본 소스 간의 중요한 구별도 존중해야 합니다.
배포 스크립트는 마이크로서비스 아키텍처에서 일반적인 코드 구성 패턴입니다. 이러한 기능은 12단계 앱이 처음 출시된 이후 새롭게 출시되었기 때문에 필연적으로 12단계 앱의 확장 버전입니다.
최근 몇 년 사이에 애플리케이션 코드와 동일한 저장소에 infrastructure (또는 이와 비슷한 이름의 폴더)라는 이름의 폴더를 갖는 것이 일반적이 되었습니다. 일반적으로 다음이 포함됩니다.
언뜻 보기에 이는 구성과 코드를 엄격하게 분리해야 한다는 Factor 3의 규정을 위반하는 것처럼 보일 수 있습니다.
사실, 애플리케이션 옆 에 배치하면 인프라 폴더가 실제로 규칙을 준수하는 동시에 마이크로서비스 환경에서 작업하는 팀에 중요한 귀중한 프로세스 개선을 가능하게 한다는 것을 의미합니다.
이 패턴의 장점은 다음과 같습니다.
이 패턴이 제공하는 이점은 개별 팀의 자율성을 강화하는 동시에 배포 및 구성 프로세스에 추가적인 엄격성이 적용된다는 것입니다.
실제로 인프라 폴더에 저장된 배포 스크립트를 사용하여 스크립트 자체에 명시적으로 정의된 구성과 배포 시점에 외부 소스에서 구성을 검색하는 작업을 모두 관리합니다. 이를 위해 서비스에 대한 배포 스크립트를 다음과 같이 사용합니다.
서비스의 특정 배포에 특화되어 있고 팀에서 완전히 제어할 수 있는 구성 값은 infrastructure 폴더의 파일에 직접 지정할 수 있습니다. 예를 들어 앱에서 시작한 데이터베이스 쿼리를 실행할 수 있는 시간을 제한하는 것과 같은 것이 있습니다. 이 값은 배포 파일을 수정하고 애플리케이션을 다시 배포하면 변경될 수 있습니다.
이 방식의 장점 중 하나는 구성을 변경하면 반드시 코드 검토와 자동 테스트를 거쳐야 하므로 잘못 구성된 값으로 인해 중단이 발생할 가능성이 줄어든다는 것입니다. 코드 검토를 거친 값의 변경 사항과 특정 시점의 구성 키 값은 소스 제어 도구의 기록에서 확인할 수 있습니다.
애플리케이션을 실행하는 데 필요하지만 팀에서 제어할 수 없는 값은 애플리케이션이 배포된 환경에서 제공해야 합니다. 예를 들어, 서비스가 종속된 다른 마이크로서비스에 연결하는 호스트 이름과 포트가 있습니다.
해당 서비스는 귀하의 팀이 소유하지 않으므로 포트 번호와 같은 값에 대해 가정할 수 없습니다. 이러한 값은 언제든지 변경될 수 있으며 변경 시 중앙 구성 저장소에 등록되어야 합니다. 해당 변경이 수동으로 이루어지든 자동 프로세스를 통해 이루어지든 상관없습니다. 그런 다음 이를 사용하는 애플리케이션에서 쿼리를 실행할 수 있습니다.
이러한 지침은 마이크로서비스 구성을 위한 두 가지 모범 사례로 요약할 수 있습니다.
배포 스크립트에 특정 값(예: 서비스가 상호 작용하는 서비스의 위치)을 하드코딩하는 것이 가장 간단한 것처럼 보일 수 있습니다. 실제로 이런 유형의 구성을 하드코딩하는 것은 위험합니다. 특히 서비스 위치가 자주 변경되는 현대 환경에서는 더욱 그렇습니다. 특히 두 번째 서비스를 소유하지 않은 경우 더욱 위험합니다.
스크립트에서 서비스 위치를 업데이트하기 위해 자신의 부지런함에 의지할 수 있다고 생각할 수도 있고, 더 나쁜 경우 위치가 변경될 때 소유 팀에 의지하여 알려줄 수 있다고 생각할 수도 있습니다. 스트레스가 많은 시기에는 근면함이 흐트러지는 경우가 많고, 인간의 엄격함에 의존하다 보면 시스템이 아무런 경고 없이 고장날 위험이 있습니다.
위치 정보가 하드코딩되어 있든 없든, 애플리케이션은 중요 인프라가 특정 위치에 있어야 한다는 전제 하에 동작해서는 안 됩니다. 대신, 새로 배포된 서비스는 "내 데이터베이스는 어디에 있나요?"와 같은 시스템 내의 일반적인 소스에 질문을 하고 해당 외부 리소스의 현재 위치에 대한 정확한 답변을 받아야 합니다. 각 서비스가 배포되는 대로 시스템에 등록되면 작업이 훨씬 간단해집니다.
시스템이 "내 데이터베이스는 어디에 있는가?"와 "내가 의존하고 있는 '서비스 X'는 어디에 있는가?"라는 질문에 대한 답변을 제공해야 하는 것처럼, 서비스는 배포 방법에 대해 아무것도 알지 못해도 다른 서비스가 쉽게 찾아서 통신할 수 있는 방식으로 시스템에 노출되어야 합니다.
마이크로서비스 아키텍처의 주요 구성 관행은 서비스 검색입니다. 즉, 새로운 서비스 정보를 등록하고 다른 서비스에서 액세스할 때 해당 정보를 동적으로 업데이트하는 것입니다. 마이크로서비스에 서비스 검색이 필요한 이유를 설명한 후 NGINX 오픈 소스와 Consul을 사용하여 이를 달성하는 방법의 예를 살펴보겠습니다.
한 번에 여러 서비스 인스턴스(배포)를 실행하는 것은 일반적인 관행입니다. 이를 통해 추가 트래픽을 처리할 수 있을 뿐만 아니라 새로운 배포를 실행하여 다운타임 없이 서비스를 업데이트할 수도 있습니다. NGINX와 같은 도구는 역방향 프록시 및 부하 분산 장치 역할을 하며, 들어오는 트래픽을 처리하고 가장 적합한 인스턴스로 라우팅합니다. 이것은 좋은 패턴입니다. 왜냐하면 귀하의 서비스에 의존하는 서비스는 NGINX에만 요청을 보내고 귀하의 배포에 대해 아무것도 알 필요가 없기 때문입니다.
예를 들어, NGINX 뒤에서 역방향 프록시 역할을 하는 messenger 라는 서비스의 단일 인스턴스가 있다고 가정해 보겠습니다.
이제 여러분의 앱이 인기를 얻는다면 어떨까요? 이는 좋은 소식으로 여겨지지만, 트래픽이 증가했기 때문에 메신저 인스턴스가 많은 CPU를 사용하고 요청을 처리하는 데 시간이 더 오래 걸리는 반면 데이터베이스는 아무 문제없이 작동하는 것처럼 보입니다. 이는 메신저 서비스의 다른 인스턴스를 배포하면 문제를 해결할 수 있음을 나타냅니다.
메신저 서비스의 두 번째 인스턴스를 배포할 때 NGINX는 해당 인스턴스가 라이브 상태인지 어떻게 알고 해당 인스턴스로 트래픽을 전송합니까? NGINX 구성에 수동으로 새로운 인스턴스를 추가하는 것도 한 가지 방법이지만, 더 많은 서비스가 확장되거나 축소됨에 따라 빠르게 관리하기 어려워집니다.
일반적인 솔루션은 Consul 과 같은 가용성이 높은 서비스 레지스트리를 갖춘 시스템에서 서비스를 추적하는 것입니다. 새로운 서비스 인스턴스는 배포됨에 따라 Consul에 등록됩니다. Consul은 주기적으로 인스턴스에 상태 검사를 보내 인스턴스의 상태를 모니터링합니다. 인스턴스가 상태 검사에 실패하면 사용 가능한 서비스 목록에서 제거됩니다.
NGINX는 다양한 방법을 사용하여 Consul과 같은 레지스트리에 쿼리를 보내고 이에 따라 라우팅을 조정할 수 있습니다. 역방향 프록시 또는 로드 밸런서 역할을 할 때 NGINX는 트래픽을 "업스트림" 서버로 라우팅한다는 점을 기억하세요. 다음과 같은 간단한 구성을 고려해 보세요.
# "messenger_service"라는 업스트림 그룹을 정의합니다.
upstream messenger_service {
server 172.18.0.7:4000;
server 172.18.0.8:4000;
}
server {
listen 80;
location /api {
# '/api'로 시작하는 경로로 HTTP 트래픽을 프록시하여
# 위의 'upstream' 블록으로 보냅니다. 기본 로드 밸런싱 알고리즘인
# 라운드 로빈은 블록에서 두 서버 간의 요청을 번갈아 가며 수행합니다.
# proxy_pass http://messenger_service;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
기본적으로 NGINX는 트래픽을 라우팅하기 위해 각 메신저 인스턴스의 정확한 IP 주소와 포트를 알아야 합니다. 이 경우에는 172.18.0.7과 172.18.0.8 모두에서 포트 4000입니다.
여기서 Consul과 Consul 템플릿이 등장합니다. Consul 템플릿은 NGINX와 동일한 컨테이너에서 실행되며 서비스 레지스트리를 유지 관리하는 Consul 클라이언트와 통신합니다.
레지스트리 정보가 변경되면 Consul 템플릿은 올바른 IP 주소와 포트를 포함하는 NGINX 구성 파일의 새 버전을 생성하고, 이를 NGINX 구성 디렉토리에 쓰고, NGINX에 구성을 다시 로드하라고 지시합니다. NGINX가 구성을 다시 로드할 때는 다운타임이 발생하지 않으며, 다시 로드가 완료되자마자 새 인스턴스가 트래픽을 수신하기 시작합니다.
이런 상황에서 NGINX와 같은 역방향 프록시를 사용하면 다른 서비스가 액세스할 수 있는 장소로 시스템에 등록할 수 있는 단일 터치 포인트가 있습니다. 귀하의 팀은 다른 서비스가 서비스 전체에 대한 액세스를 잃을까봐 걱정하지 않고 개별 서비스 인스턴스를 관리할 수 있는 유연성을 확보할 수 있습니다.
마이크로서비스는 서비스의 기술적 측면과 다른 팀과의 관계에 대한 조직적 측면에서 모두 복잡성을 증가시킨다는 점을 인정합니다. 마이크로서비스 아키텍처의 이점을 누리려면 모노리스를 위해 설계된 관행을 비판적으로 재검토하여 매우 다른 환경에 적용했을 때에도 여전히 동일한 이점을 제공하는지 확인하는 것이 중요합니다. 이 블로그에서는 12가지 요소 앱 중 요소 3이 마이크로서비스 컨텍스트에서 여전히 가치를 제공하지만 구체적으로 적용되는 방식을 약간 변경하면 이점을 얻을 수 있는지 살펴보았습니다.
12팩터 앱을 마이크로서비스 아키텍처에 적용하는 방법에 대해 자세히 알아보려면 2023년 3월 마이크로서비스 단원 1을 확인하세요( 곧 블로그에 게재 ). 무료로 등록하세요 이 주제에 대한 웨비나와 실습 랩에 참여하세요.
"이 블로그 게시물에는 더 이상 사용할 수 없거나 더 이상 지원되지 않는 제품이 참조될 수 있습니다. 사용 가능한 F5 NGINX 제품과 솔루션에 대한 최신 정보를 보려면 NGINX 제품군을 살펴보세요. NGINX는 이제 F5의 일부가 되었습니다. 이전의 모든 NGINX.com 링크는 F5.com의 유사한 NGINX 콘텐츠로 리디렉션됩니다."