데브옵스 엔지니어에게 마이크로서비스 설계란 양산 이후의 서비스 관리에 관한 설계다.
첫번째, 마이크로서비스는 단일 소프트웨어 산출물을 이용해 여러 서비스 인스턴스를 시작하거나 제거할 수 있도록 자체 완비형이며 독립적으로 배포 가능해야 한다.
두번째, 마이크로서비스는 구성 가능해야 한다. 서비스 인스턴스가 시작될 때 구성에 필요한 데이터를 중앙에서 읽어 들이거나 환경 변수로 전달된 구성 정보를 받아야 한다. 서비스를 구성하는데 사람의 개입은 필요하지 않다.
세번째, 마이크로서비스 인스턴스는 클라이언트가 위치를 알지 못하도록 투명해야 한다. 클라이언트는 서비스의 정확한 위치를 알고 있어서는 안된다. 그 대신에 마이크로서비스 클라이언트는 마이크로서비스 인스턴스의 물리적 위치를 모르더라도 애플리케이션이 알 수 있도록 서비스 디스커버리 에이전트와 통신해야 한다.
네번째, 마이크로서비스는 자신의 상태를 전달해야 한다. 이는 클라우드 아키텍처에서 매우 중요한 부분이다. 마이크로서비스 인스턴스들은 고장 날 수 있으며 클라이언트는 잘못된 서비스 인스턴스를 피해 라우팅해야 한다.
Step1) 서비스 어셈블리 : 동일한 서비스 코드와 런타임을 정확히 같은 방식으로 배포하기 위해 반복성과 일관성을 보장하는 서비스 패키징과 배포 방식은 무엇인가?
Step2) 서비스 부트스트래핑 : 사람이 개입하지 않아도 모든 환경에 마이크로서비스 인스턴스를 신속하게 배포하기 위해 애플리케이션과 환경별 구성 코드를 런타임 코드와 분리하는 방법은 무엇인가?
Step3) 서비스 등록 및 디스커버리 : 새로운 마이크로서비스 인스턴스가 배포될 때 다른 애플리케이션 클라이언트가 발견할 수 있게 만드는 방법은 무엇인가?
Step4) 서비스 모니터링 : 마이크로서비스 환경에서 매우 높은 가용성을 요구하기 때문에 동일 서비스를 여러 인스턴스로 실행하는 것이 일반적이다. 데브옵스 관점에서 마이크로서비스 인스턴스를 모니터링하고 마이크로서비스 고장을 회피하는 라우팅과 비정상 인스턴스를 제거하는지 확인해야 한다.
[성공적인 마이크로 서비스 아키텍처를 위해 강력한 애플리케이션 개발과 데브옵스 실천이 필요함을 깨닫는다. 이러한 실천의 간결한 요약 중 하나가 Heroku's의 Twelve-Factor이다. (12개의 모범지침 제공). ]
1. 코드베이스 : 모든 애플리케이션 코드와 서버 프로비저닝(provisioning) 정보는 버전관리되어야 한다. 각 마이크로 서비스는 소스 제어 시스템 안에 독립적인 코드 저장소를 가져야 한다.
2. 의존성 : 애플리케이션이 사용하는 의존성을 메이븐 같은 빌드 도구를 이용해 명시적으로 선언해야 한다. 제3자의 JAR 의존성은 특정 버전 번호를 붙여 명시해 선언해야 한다. 따라서 동일 버전의 라이브러리를 사용해 항상 마이크로서비스를 빌드할 수 있다.
3. 구성 : 애플리케이션 구성(특히 환경별 구성)을 코드와 독립적으로 저장하자. 애플리케이션 구성은 절대로 소스 코드와 동일한 저장소에 있으면 안된다.
4. 백엔드 서비스 : 마이크로서비스는 대개 네트워크를 거쳐 데이터베이스나 메시징 서비스와 통신한다. 그렇다면 언제든 데이터베이스 구현을 자체 관리형 서비스에서 외부업체 서비스로 교체할 수 있어야 한다.
5. 빌드, 릴리스, 실행(build, release, run) : 배포할 애플리케이션의 빌드, 릴리스, 실행 부분을 철저히 분리하라. 코드가 빌드되면 개발자는 실행 중에 코드를 변경할 수 없다. 모든 변경 사항을 빌드 프로세스로 되돌려 재배포해야 한다. 빌드된 서비스는 불변적이므로 변경할 수 없다.
6. 프로세스 : 마이크로서비스는 항상 무상태 방식을 사용해야 한다. 서비스 인스턴스 손실에 의해 데이터가 손실될 것이라는 우려 없이 언제든 서비스를 강제 종료하거나 교체할 수 있다.
7. 포트 바인딩 : 마이크로서비스는 서비스용 런타임 엔진을 포함한 완전히 자체 완비형이다. 별도의 웹 또는 애플리케이션 서버 없이도 서비스는 실행되어야 한다. 서비스는 명령행에서 단독으로 시작하고 노출한 HTTP 포트를 통해 즉시 엑세스 할 수 있어야 한다.
8. 동시성 : 확장해야 한다면 단일 서비스 안에서 스레드 모델에 의존하지 마라. 마이크로서비스 안에서 스레드 사용을 배제하지는 않지만 확장을 위한 유일한 매커니즘으로 믿지 말라. 수직 대신 수평 확장하라.
9. 폐기 기능 : 마이크로서비스는 폐기 가능하므로 요구에 따라 시작 및 중지할 수 있다. 시작 시간은 최소화하고 운영 체제에서 강제 종료 신호를 받으면 프로세스는 적절히 종료해야 한다.
10. 개발 및 운영 환경 일치 : 서비스가 실행되는 모든 환경 사이의 차이를 최소화하라. 개발자는 서비스 개발을 위해 실제 서비스가 실행되는 동일한 인프라스트럭처를 로컬에 사용해야 한다. 이는 환경 간 서비스 배포가 수 주가 아닌 수 시간 안에 이루어져야 한다는 것을 의미한다. 코드가 커밋되자마자 테스트되고 가능한 신속하게 개발 환경에서 운영환경으로 전파되어야 한다.
11. 로그 : 로그는 이벤트 스트림이다. 로그가 기록될 때 스플렁크 같은 도구로 로그가 스트리밍되어야 한다. 이들 도구는 로그를 수집해 중앙에 기록한다. 마이크로서비스는 이러한 로깅 동작 방식에 신경쓰지 않아야 하며, 개발자는 표준 출력으로 출력된 로그를 시간적으로 확인할 수 있어야 한다.
12. 관리 프로세스 : 개발자는 종종 담당 서비스에 대해 데이터 마이그레이션이나 변환처럼 관리 작업을 수행해야 한다. 이러한 작업은 임의로 수행되면 안 되고 소스 코드 저장소에 유지 및 관리되는 스크립트에 의해 수행되어야 한다.
마이크로서비스의 성공을 위해 아키텍트, 소프트웨어 개발자, 데브옵스 관점을 통합해야 한다.
마이크로 서비스는 강력한 아키텍처 패러다임이지만, 혜택과 장단점이 있다. 모든 애플리케이션이 마이크로서비스 애플리케이션일 필요는 없다.
아키텍트 관점에서 마이크로서비스는 작고, 자체 완비형이며, 분산된 것이다. 마이크로서비스는 좁은 범위와 소규모 데이터를 관리한다.
개발자 관점에서 REST 설계 방식과 서비스의 데이터 송수신을 위한 JSON을 사용해 마이크로서비스를 구축한다.
데브옵스 관점에서 마이크로서비스를 패키징, 배포, 모니터링하는 방법은 매우 중요하다.
스프링 부트를 사용하면 서비스를 하나의 JAR 실행 파일로 전달할 수 있다. JAR 파일에 내장된 톰캣 서버가 서비스를 호스팅한다.
스프링 부트 프레임워크에 포함된 스프링 액추에이터는 서비스 런타임 정보와 함께 서비스의 운영 상태 정보도 제공한다.