💻 Backend
마이크로 서비스 아키텍처 맛보기: 간단한 프로젝트 배포하기(AWS EKS, Java, Spring Boot, Gradle)
date
Jun 6, 2023
slug
msa-springboot
author
status
Public
tags
Blog
Java
AWS
SpringBoot
DevOps
summary
Java/Spring Boot 프로젝트로 MSA 맛보기
type
Post
thumbnail
category
💻 Backend
updatedAt
Dec 10, 2023 04:21 AM
마이크로서비스 아키텍처(Micro Service Architecture, MSA)마이크로서비스 아키텍처의 장점마이크로서비스 아키텍처의 단점마이크로서비스 아키텍처를 지원하는 플랫폼쿠버네티스(Kubernetes, k8s)쿠버네티스의 핵심개념쿠버네티스의 장점AWS EKS(Elastic Kubernetes Service)AWS EKS 특징프로젝트 생성 및 배포하기REST APICRUD 포함 프로젝트 예시AWS CLI 설치 및 배포프로젝트 관리애플리케이션 모니터링 및 로깅CI/CD 파이프라인 설정서비스 메쉬 적용보안 고려배포 전략 설정
마이크로서비스 아키텍처에 대해 알아보며, 간단한 프로젝트 예시로 AWS EKS 배포를 살펴보겠습니다 🙂
AWS EKS, Java 11, Spring Boot 2.6.2, Gradle
마이크로서비스 아키텍처(Micro Service Architecture, MSA)
마이크로서비스 아키텍처는 하나의 큰 애플리케이션을 작고, 독립적인 서비스들로 분리하여 개발하고 운영하는 디자인 패턴입니다.
이러한 서비스들은 각각 자신의 프로세스에서 실행되며, 보통 HTTP API를 통해 통신합니다.
마이크로서비스 아키텍처의 핵심원칙은 각 서비스가 독립적으로 배포될 수 있어야 한다는 것입니다.
이는 각 서비스를 독립적으로 확장하고 업데이트할 수 있게 하여, 빠른 개발과 배포를 가능하게 합니다.
마이크로서비스 아키텍처의 장점
- 독립적인 배포 및 확장
- 각 마이크로서비스는 독립적으로 배포하고 확장할 수 있습니다. 이를 통해 특정 서비스에 대한 변경이나 확장이 다른 서비스에 영향을 미치지 않습니다.
- 분리된 데이터 저장소
- 각 마이크로서비스는 자신만의 데이터베이스를 가질 수 있습니다. 이로 인해 서비스 간 데이터 형식의 종속성이 제거되며, 각 서비스가 필요에 따라 데이터베이스 유형을 자유롭게 선택할 수 있게 됩니다.
- 변경에 유연한 구조
- 각 마이크로서비스는 독립적이므로, 한 서비스의 변경이나 업데이트가 다른 서비스에 영향을 미치지 않습니다. 따라서 개발팀은 변경 사항을 빠르게 적용하고, 실험적인 기능을 안전하게 테스트할 수 있습니다.
- 분산 개발 효율성
- 마이크로서비스는 각 서비스를 독립적으로 개발할 수 있기 때문에, 다양한 기술 스택을 활용하거나, 분산된 팀 구조를 사용할 수 있습니다.
마이크로서비스 아키텍처의 단점
마이크로서비스 아키텍처는 모든 상황에 적합한 것은 아닙니다.
서비스 간의 통신과 데이터 일관성을 유지하면서, 독립적인 서비스를 관리하는 것은 복잡성을 증가시킬 수 있습니다. 또한, 이러한 복잡성을 관리하기 위한 툴과 전략이 필요합니다.
마이크로서비스 아키텍처를 지원하는 플랫폼
- 쿠버네티스(Kubernetes)
- 현재 가장 널리 사용되는 컨테이너 오케스트레이션 플랫폼입니다. 컨테이너를 효과적으로 관리하고, 애플리케이션을 확장하며, 네트워킹, 서비스 디스커버리, 보안, 로깅, 모니터링 등을 관리하는 데 사용됩니다.
- Docker Swarm
- Docker에서 직접 관리하는 컨테이너 오케스트레이션 플랫폼으로, Docker와의 긴밀한 통합이 특징입니다. 쿠버네티스보다 사용법이 간단하지만, 기능적으로는 쿠버네티스에 비해 다소 제한적입니다.
- Apache Mesos
- 빅데이터 처리와 컨테이너 오케스트레이션을 모두 처리할 수 있는 강력한 플랫폼입니다. 하지만 배포와 관리의 복잡성 때문에 일반적으로 대규모 시스템에서 사용됩니다.
- Amazon ECS (Elastic Container Service)
- AWS에서 제공하는 컨테이너 오케스트레이션 서비스입니다. AWS 서비스와의 통합이 잘 되어 있지만, 다른 클라우드 플랫폼으로 이동하는 데 제약이 있을 수 있습니다.
- AWS EKS (Elastic Kubernetes Service)
- AWS에서 제공하는 관리형 쿠버네티스 서비스입니다. 쿠버네티스의 강력한 기능을 AWS 환경에서 사용할 수 있게 해줍니다.
- Google Kubernetes Engine (GKE)
- Google Cloud에서 제공하는 관리형 쿠버네티스 서비스입니다. 쿠버네티스를 처음 개발한 Google에서 관리하므로, 쿠버네티스에 대한 깊은 이해와 최신 기능을 제공합니다.
- Azure Kubernetes Service (AKS)
- Microsoft Azure에서 제공하는 관리형 쿠버네티스 서비스입니다. Azure 서비스와의 통합이 잘 되어 있습니다.
이 포스팅에서는 쿠버네티스를 다룹니다.
쿠버네티스(Kubernetes, k8s)
마이크로서비스 아키텍처를 지원하는 플랫폼 중 하나인 쿠버네티스(Kubernetes)는 서비스를 확장하고, 빠르게 배포하고, 높은 가용성을 유지하는 데 도움이 되는 강력한 도구입니다.
쿠버네티스는 컨테이너화된 애플리케이션을 자동으로 배포, 확장, 그리고 관리해주는 오픈소스 플랫폼입니다.
이는 서비스의 확장성과 빠른 업데이트를 지원하며, 다양한 배포 패턴(예: 카나리아 배포, 블루/그린 배포)을 제공합니다.
쿠버네티스의 핵심개념
쿠버네티스는 여러 개념을 기반으로 합니다. 이 중 몇 가지를 간략히 살펴보겠습니다.
- 파드(Pod): 파드는 쿠버네티스의 가장 작은 배포 단위입니다. 각 파드는 하나 이상의 컨테이너를 포함하며, 같은 파드 안의 컨테이너들은 같은 네트워크와 저장 공간을 공유합니다.
- 서비스(Service): 서비스는 네트워크에 쿠버네티스의 파드를 노출하는 방법입니다. 서비스를 통해 파드는 네트워크를 통해 다른 파드나 애플리케이션에 연결될 수 있습니다.
- 디플로이먼트(Deployment): 디플로이먼트는 파드의 복제본(replica)을 관리하는 쿠버네티스 리소스입니다. 디플로이먼트는 파드의 스케일링과 롤아웃을 관리하며, 파드에 문제가 발생하면 자동으로 재시작합니다.
쿠버네티스의 장점
- 자동 복구: 쿠버네티스는 장애가 발생하면 자동으로 파드를 재시작하며, 노드에 문제가 있는 경우 파드를 다른 노드로 이동시킵니다.
- 스케일링과 로드 밸런싱: 쿠버네티스는 애플리케이션의 트래픽을 자동으로 분산시키고, 요청에 따라 동적으로 스케일링합니다.
- 선언적 구성: 쿠버네티스는 애플리케이션의 상태를 선언하는 방식으로 작동하므로, 시스템 관리자는 쿠버네티스에 원하는 상태를 선언하면 쿠버네티스가 그 상태를 유지하는 데 필요한 작업을 수행합니다.
AWS EKS(Elastic Kubernetes Service)
AWS EKS는 Amazon Web Services(AWS)에서 제공하는 관리형 쿠버네티스 서비스입니다.
이는 사용자가 쿠버네티스 클러스터를 쉽게 설정하고, 관리하며, 확장할 수 있도록 도와줍니다.
이를 통해 개발자는 클러스터 설정 및 관리에 대한 고민 없이 애플리케이션 개발에 집중할 수 있습니다.
AWS EKS 특징
- 표준 호환성
- AWS EKS는 오픈 소스 쿠버네티스를 그대로 사용하기 때문에, 기존 쿠버네티스 애플리케이션을 그대로 이식할 수 있습니다.
- AWS 서비스와의 통합
- AWS EKS는 Amazon RDS, Amazon S3, Amazon DynamoDB와 같은 AWS 서비스와 자연스럽게 연동됩니다. 이는 마이크로서비스의 다양한 요구사항을 쉽게 처리할 수 있게 해줍니다.
- 확장성 및 가용성
- AWS EKS는 클러스터를 여러 AWS 가용 영역에 걸쳐 분산시킬 수 있습니다. 이는 마이크로서비스의 고가용성과 확장성을 보장해줍니다.
- 보안 및 규정 준수
- AWS EKS는 AWS의 다양한 보안 및 규정 준수 도구와 통합되어, 애플리케이션의 보안 요구사항을 쉽게 만족시킬 수 있습니다.
프로젝트 생성 및 배포하기
Spring Initializr를 사용하여 기본 프로젝트를 설정하고, 이에 필요한 의존성을 추가할 수 있습니다. 이 예시에서는 Java 11과 Gradle를 사용하겠습니다.
REST API
기본적으로 간단한 "Hello, World!"를 반환하는 REST API를 만들어 보겠습니다.
아래는 기본적인
HelloController.java
파일의 예시입니다:package com.example.demo; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @GetMapping("/") public String index() { return "Hello, World!"; } }
그리고 애플리케이션의 시작점인
DemoApplication.java
파일의 예시는 다음과 같습니다:package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
다음으로,
build.gradle
파일을 이용하여 Gradle 프로젝트를 구성하고 필요한 의존성을 추가합니다:plugins { id 'org.springframework.boot' version '2.6.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' testImplementation 'org.springframework.boot:spring-boot-starter-test' } test { useJUnitPlatform() }
이제 Docker를 사용하여 이 애플리케이션을 컨테이너화할 수 있습니다. 이를 위해
Dockerfile
을 작성해야 합니다:FROM openjdk:11 VOLUME /tmp COPY build/libs/demo-0.0.1-SNAPSHOT.jar app.jar ENTRYPOINT ["java","-jar","/app.jar"]
마지막으로, 이를 Kubernetes에서 배포하기 위한
deployment.yaml
과 service.yaml
파일을 만들어야 합니다:deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: demo-deployment spec: replicas: 1 selector: matchLabels: app: demo template: metadata: labels: app: demo spec: containers: - name: demo image: <docker-image-url> ports: - containerPort: 8080
service.yaml
apiVersion: v1 kind: Service metadata: name: demo-service spec: selector: app: demo ports: - protocol: TCP port: 80 targetPort: 8080 type: LoadBalancer
여기서
<docker-image-url>
는 해당 Docker 이미지의 URL로 대체해야 합니다.이제 이 Java Spring Boot 애플리케이션을 AWS EKS에서 배포할 준비가 되었습니다. 이는 아주 기본적인 예시이며, 실제 환경에서는 데이터베이스 연결, 환경 변수, 로깅, 에러 핸들링 등을 고려해야 할 것입니다.
또한, AWS CLI 및
kubectl
명령어를 사용하여 이 애플리케이션을 EKS 클러스터에 배포해야 합니다.이제 이 Java/Spring Boot 애플리케이션을 AWS EKS에서 배포할 준비가 되었습니다.
이는 아주 기본적인 예시이며, 실제 환경에서는 데이터베이스 연결, 환경 변수, 로깅, 에러 핸들링 등을 고려해야 할 것입니다.
CRUD 포함 프로젝트 예시
MySQL 데이터베이스와 Spring Data JPA를 사용하여 간단한 CRUD(Create, Read, Update, Delete) 애플리케이션을 생성합니다.
- 의존성 추가: 먼저, 데이터베이스와 JPA를 사용하기 위해 필요한 의존성을
build.gradle
에 추가합니다.
dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'mysql:mysql-connector-java' ... }
- 환경 변수 설정: 애플리케이션의 설정은 환경에 따라 달라질 수 있으므로, 이를 관리하기 위해
application.properties
파일을 사용합니다. 이 파일에 데이터베이스 연결 정보를 추가합니다.
spring.datasource.url=jdbc:mysql://${MYSQL_HOST:localhost}:3306/db_example spring.datasource.username=${MYSQL_USER:example} spring.datasource.password=${MYSQL_PASSWORD:example} spring.jpa.hibernate.ddl-auto=update
이 설정에서는 환경 변수
MYSQL_HOST
, MYSQL_USER
, MYSQL_PASSWORD
를 사용하여 데이터베이스의 호스트, 사용자 이름, 비밀번호를 설정합니다. 환경 변수가 설정되지 않은 경우에는 기본값으로 localhost
, example
, example
을 사용합니다.- Entity 생성: JPA를 사용하여 데이터베이스의 테이블을 표현하는
User
엔티티를 생성합니다.
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class User { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Integer id; private String name; // getters and setters ... }
- Repository 생성:
User
엔티티를 사용하여 CRUD 연산을 수행하는UserRepository
인터페이스를 생성합니다.
import org.springframework.data.repository.CrudRepository; public interface UserRepository extends CrudRepository<User, Integer> { }
- Controller 생성:
UserRepository
를 사용하여 HTTP 요청을 처리하는UserController
클래스를 생성합니다.
import org.springframework.web.bind.annotation.*; @RestController public class UserController { private final UserRepository repository; UserController(UserRepository repository) { this.repository = repository; } @GetMapping("/users") Iterable<User> all() { return repository.findAll(); } @PostMapping("/users") User newUser(@RequestBody User newUser) { return repository.save(newUser); } // Additional CRUD methods ... }
- 로깅 및 에러 핸들링: 로깅은
logback
라이브러리를 사용하며, 기본 설정을 사용하면 애플리케이션의 로그를 콘솔에 출력합니다. 에러 핸들링은 Spring MVC의 예외 핸들러를 사용하여 처리할 수 있습니다.
import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @RestController public class UserController { private static final Logger log = LoggerFactory.getLogger(UserController.class); // ... @ExceptionHandler @ResponseStatus(HttpStatus.NOT_FOUND) private void userNotFoundHandler(UserNotFoundException ex) { log.error("User not found", ex); } }
이렇게 생성한 애플리케이션은 HTTP API를 통해
User
데이터를 생성하고 조회할 수 있으며, 애플리케이션의 로그를 확인하고, 에러를 처리할 수 있습니다.실제 애플리케이션에서는 보안, 트랜잭션 관리, 데이터 유효성 검사 등 추가적인 고려사항이 있을 수 있습니다.
AWS CLI 설치 및 배포
AWS CLI와 kubectl을 설치하고, AWS EKS에 애플리케이션을 배포하는 단계입니다.
AWS CLI 설치: AWS CLI는 AWS 서비스를 관리하기 위한 명령줄 인터페이스입니다. 다음 명령어를 사용하여 AWS CLI를 설치할 수 있습니다. 아래의 명령어는 Unix/Linux 기반 시스템을 위한 것이며, Windows를 사용하신다면 AWS 공식 문서를 참조하시기 바랍니다.
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip" unzip awscliv2.zip sudo ./aws/install
설치를 완료한 후,
aws configure
명령어를 실행하여 AWS 계정의 Access Key ID와 Secret Access Key, 기본 리전 등을 설정합니다.kubectl 설치: kubectl은 Kubernetes 클러스터를 관리하는데 사용되는 명령줄 도구입니다. 다음 명령어를 사용하여 kubectl을 설치합니다.
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" chmod +x kubectl sudo mv kubectl /usr/local/bin/
EKS 클러스터에 연결: AWS CLI와 kubectl을 설치한 후,
aws eks update-kubeconfig
명령어를 사용하여 Kubeconfig 파일을 업데이트하고 EKS 클러스터에 연결합니다. 이 명령어는 AWS CLI가 설치된 곳에서 실행해야 합니다.aws eks update-kubeconfig --region region-code --name cluster-name
여기서 region-code는 클러스터가 위치한 AWS 리전의 코드이고, cluster-name은 EKS 클러스터의 이름입니다.
애플리케이션 배포: kubectl을 사용하여 애플리케이션을 EKS 클러스터에 배포할 수 있습니다.
kubectl apply
명령어를 사용하여 이전에 생성한 Kubernetes 매니페스트 파일들을 적용합니다.kubectl apply -f deployment.yaml kubectl apply -f service.yaml
이제 클러스터를 확인하고, 애플리케이션 서비스가 제대로 작동하는지 테스트하면 됩니다.
kubectl get services
명령어를 사용하여 서비스의 상태를 확인할 수 있습니다.bashCopy code kubectl get services
모든 것이 제대로 작동한다면, 이 명령어는 LoadBalancer의 IP 주소 또는 DNS 이름을 반환해야 합니다.
이 주소를 웹 브라우저에 입력하면 애플리케이션의 "Hello, World!" 메시지를 볼 수 있어야 합니다.
대략적인 방법이며, 실제 환경에서는 클러스터의 보안, 네트워크 설정 등에 대한 고려사항이 있을 것입니다.
프로젝트 관리
배포 된 프로젝트를 관리하기 위한 관리 항목들이 어떤 것들이 있는 지 알아보겠습니다.
이 포스팅에서는 깊게 들여다보지는 않고, 항목들에 대해 간략하게만 살펴보겠습니다.
애플리케이션 모니터링 및 로깅
애플리케이션과 인프라의 상태를 지속적으로 모니터링하고 로그를 수집해야 합니다.
이를 통해 문제가 발생할 때 신속히 대응하고, 성능 개선을 위한 인사이트를 얻을 수 있습니다.
- AWS CloudWatch: AWS 리소스와 애플리케이션을 모니터링하고, 로그를 수집하고, 알람을 설정할 수 있는 서비스입니다.
- Prometheus and Grafana: Kubernetes의 상태를 모니터링하고 시각화하는데 널리 사용되는 오픈소스 도구입니다.
- Fluentd or Fluent Bit: 로그 수집을 위한 오픈소스 도구입니다.
CI/CD 파이프라인 설정
배포는 일회성 작업이 아닌, 지속적인 개발과 테스트, 배포의 사이클을 반복하는 과정입니다.
따라서 Continuous Integration/Continuous Delivery (CI/CD) 파이프라인을 구성하여 코드의 변경 사항을 자동으로 테스트하고, 안전하게 배포하는 과정을 자동화해야 합니다.
- AWS CodePipeline and AWS CodeBuild: AWS에서 제공하는 CI/CD 서비스로, 소스 코드의 변경을 감지하고, 빌드하고, 테스트하고, 배포하는 과정을 자동화할 수 있습니다.
- Jenkins, CircleCI, GitLab CI/CD 등: 널리 사용되는 오픈소스 또는 상용 CI/CD 도구입니다.
서비스 메쉬 적용
마이크로서비스 아키텍처에서는 서비스 간의 통신을 관리하고 보안, 로드 밸런싱, 오류 복구, 메트릭 수집 등의 기능을 제공하는 서비스 메쉬를 사용하는 것이 일반적입니다.
- Istio, Linkerd, AWS App Mesh 등: 서비스 메쉬를 제공하는 오픈소스 또는 상용 솔루션입니다.
보안 고려
클러스터의 보안을 유지하고, 애플리케이션의 보안 취약점을 최소화하기 위한 전략을 구성해야 합니다.
- AWS IAM Roles for Service Accounts (IRSA): EKS 클러스터에서 서비스 계정을 사용하여 AWS 리소스에 접근 권한을 제공하는 방법입니다.
- AWS Secrets Manager or Vault: 비밀 키, 암호 등의 민감한 정보를 안전하게 관리하고 사용하는 서비스입니다.
- Container security tools: 컨테이너의 보안을 강화하고, 보안 취약점을 감지하고 관리하는 도구입니다.
배포 전략 설정
롤아웃, 롤백, 카나리 배포 등 다양한 배포 전략을 설정하여 애플리케이션의 업데이트를 안전하게 진행해야 합니다.
- 롤링 업데이트(Rolling Update): 이 전략은 기존 파드를 새 파드로 순차적으로 교체하는 방식입니다. 이는 쿠버네티스 디플로이먼트의 기본 배포 전략입니다. 롤링 업데이트를 사용하면 서비스 중단 시간 없이 애플리케이션을 업데이트할 수 있지만, 업데이트 중에는 일부 트래픽이 이전 버전과 새 버전의 애플리케이션에 동시에 도달하게 됩니다.
maxUnavailable
: 이 파라미터는 업데이트 도중에 사용 불가능한 파드의 최대 비율 또는 수를 정의합니다. 이는 기존 파드가 새 파드로 교체되는 속도를 제어하며, 이 값이 클수록 업데이트가 빨라집니다.maxSurge
: 이 파라미터는 디플로이먼트에서 허용하는 최대 초과 파드의 비율 또는 수를 정의합니다. 이 값이 클수록 업데이트가 빨라집니다.
롤링 업데이트 파라미터
예를 들어,
maxUnavailable
을 10%, maxSurge
를 20%로 설정하면, 롤링 업데이트가 시작될 때 먼저 새 파드를 20%만큼 추가로 생성합니다. 그런 다음 이전 파드의 10%를 중단하고, 새 파드로 교체합니다. 이 과정을 계속 반복하여 모든 이전 파드를 새 파드로 교체합니다.따라서
maxUnavailable
과 maxSurge
의 값을 조정함으로써, 새로운 파드의 몇 퍼센티지가 배포되면 이전 파드를 중단하도록 설정할 수 있습니다. 이 값들을 조정하여 롤링 업데이트의 속도와 서비스의 가용성을 적절히 조절해야 합니다.- 블루/그린 배포(Blue/Green Deployment): 블루/그린 배포는 테스트와 배포를 분리하기 위해 두 개의 동일한 환경을 준비하는 전략입니다. '블루' 환경에서는 현재 운영 중인 버전을 계속 실행하고, '그린' 환경에서는 새 버전을 배포하고 테스트합니다. 테스트가 완료되면, 트래픽을 그린 환경으로 전환(switch)하여 실제로 배포하게 됩니다. 이 전략은 롤링 업데이트와 달리 서비스의 버전이 동시에 혼재하는 상황을 방지할 수 있습니다.
- 카나리 배포(Canary Deployment): 카나리 배포는 새 버전을 점진적으로 배포하는 전략입니다. 처음에는 일부 트래픽만 새 버전으로 라우팅하고, 새 버전의 안정성을 확인한 후에 나머지 트래픽도 새 버전으로 전환합니다. 이는 새 버전의 문제가 전체 사용자에게 영향을 미치는 것을 방지할 수 있습니다.
이러한 배포 전략은 쿠버네티스 리소스, 특히 디플로이먼트, 서비스, 그리고 인그레스를 사용하여 구현할 수 있습니다.
구체적인 설정 방법은 선택한 배포 전략과 쿠버네티스 매니페스트(YAML 파일)의 작성 방법에 따라 다릅니다. AWS EKS에서는 또한 AWS App Mesh와 같은 서비스 메시를 활용하여 더욱 고급스러운 배포 전략을 구현할 수도 있습니다.