💻 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
 

 
마이크로서비스 아키텍처에 대해 알아보며, 간단한 프로젝트 예시로 AWS EKS 배포를 살펴보겠습니다 🙂
AWS EKS, Java 11, Spring Boot 2.6.2, Gradle
 

 

마이크로서비스 아키텍처(Micro Service Architecture, MSA)

마이크로서비스 아키텍처는 하나의 큰 애플리케이션을 작고, 독립적인 서비스들로 분리하여 개발하고 운영하는 디자인 패턴입니다. 이러한 서비스들은 각각 자신의 프로세스에서 실행되며, 보통 HTTP API를 통해 통신합니다.
마이크로서비스 아키텍처의 핵심원칙은 각 서비스가 독립적으로 배포될 수 있어야 한다는 것입니다. 이는 각 서비스를 독립적으로 확장하고 업데이트할 수 있게 하여, 빠른 개발과 배포를 가능하게 합니다.
 

마이크로서비스 아키텍처의 장점

  • 독립적인 배포 및 확장
    • 각 마이크로서비스는 독립적으로 배포하고 확장할 수 있습니다. 이를 통해 특정 서비스에 대한 변경이나 확장이 다른 서비스에 영향을 미치지 않습니다.
  • 분리된 데이터 저장소
    • 각 마이크로서비스는 자신만의 데이터베이스를 가질 수 있습니다. 이로 인해 서비스 간 데이터 형식의 종속성이 제거되며, 각 서비스가 필요에 따라 데이터베이스 유형을 자유롭게 선택할 수 있게 됩니다.
  • 변경에 유연한 구조
    • 각 마이크로서비스는 독립적이므로, 한 서비스의 변경이나 업데이트가 다른 서비스에 영향을 미치지 않습니다. 따라서 개발팀은 변경 사항을 빠르게 적용하고, 실험적인 기능을 안전하게 테스트할 수 있습니다.
  • 분산 개발 효율성
    • 마이크로서비스는 각 서비스를 독립적으로 개발할 수 있기 때문에, 다양한 기술 스택을 활용하거나, 분산된 팀 구조를 사용할 수 있습니다.
 

마이크로서비스 아키텍처의 단점

마이크로서비스 아키텍처는 모든 상황에 적합한 것은 아닙니다. 서비스 간의 통신과 데이터 일관성을 유지하면서, 독립적인 서비스를 관리하는 것은 복잡성을 증가시킬 수 있습니다. 또한, 이러한 복잡성을 관리하기 위한 툴과 전략이 필요합니다.
 

마이크로서비스 아키텍처를 지원하는 플랫폼

  1. 쿠버네티스(Kubernetes)
      • 현재 가장 널리 사용되는 컨테이너 오케스트레이션 플랫폼입니다. 컨테이너를 효과적으로 관리하고, 애플리케이션을 확장하며, 네트워킹, 서비스 디스커버리, 보안, 로깅, 모니터링 등을 관리하는 데 사용됩니다.
  1. Docker Swarm
      • Docker에서 직접 관리하는 컨테이너 오케스트레이션 플랫폼으로, Docker와의 긴밀한 통합이 특징입니다. 쿠버네티스보다 사용법이 간단하지만, 기능적으로는 쿠버네티스에 비해 다소 제한적입니다.
  1. Apache Mesos
      • 빅데이터 처리와 컨테이너 오케스트레이션을 모두 처리할 수 있는 강력한 플랫폼입니다. 하지만 배포와 관리의 복잡성 때문에 일반적으로 대규모 시스템에서 사용됩니다.
  1. Amazon ECS (Elastic Container Service)
      • AWS에서 제공하는 컨테이너 오케스트레이션 서비스입니다. AWS 서비스와의 통합이 잘 되어 있지만, 다른 클라우드 플랫폼으로 이동하는 데 제약이 있을 수 있습니다.
  1. AWS EKS (Elastic Kubernetes Service)
      • AWS에서 제공하는 관리형 쿠버네티스 서비스입니다. 쿠버네티스의 강력한 기능을 AWS 환경에서 사용할 수 있게 해줍니다.
  1. Google Kubernetes Engine (GKE)
      • Google Cloud에서 제공하는 관리형 쿠버네티스 서비스입니다. 쿠버네티스를 처음 개발한 Google에서 관리하므로, 쿠버네티스에 대한 깊은 이해와 최신 기능을 제공합니다.
  1. 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.yamlservice.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) 애플리케이션을 생성합니다.
  1. 의존성 추가: 먼저, 데이터베이스와 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' ... }
  1. 환경 변수 설정: 애플리케이션의 설정은 환경에 따라 달라질 수 있으므로, 이를 관리하기 위해 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을 사용합니다.
  1. 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 ... }
  1. Repository 생성: User 엔티티를 사용하여 CRUD 연산을 수행하는 UserRepository 인터페이스를 생성합니다.
import org.springframework.data.repository.CrudRepository; public interface UserRepository extends CrudRepository<User, Integer> { }
  1. 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 ... }
  1. 로깅 및 에러 핸들링: 로깅은 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: 컨테이너의 보안을 강화하고, 보안 취약점을 감지하고 관리하는 도구입니다.
 
 

배포 전략 설정

롤아웃, 롤백, 카나리 배포 등 다양한 배포 전략을 설정하여 애플리케이션의 업데이트를 안전하게 진행해야 합니다.
  1. 롤링 업데이트(Rolling Update): 이 전략은 기존 파드를 새 파드로 순차적으로 교체하는 방식입니다. 이는 쿠버네티스 디플로이먼트의 기본 배포 전략입니다. 롤링 업데이트를 사용하면 서비스 중단 시간 없이 애플리케이션을 업데이트할 수 있지만, 업데이트 중에는 일부 트래픽이 이전 버전과 새 버전의 애플리케이션에 동시에 도달하게 됩니다.
    1. 롤링 업데이트 파라미터
      • maxUnavailable: 이 파라미터는 업데이트 도중에 사용 불가능한 파드의 최대 비율 또는 수를 정의합니다. 이는 기존 파드가 새 파드로 교체되는 속도를 제어하며, 이 값이 클수록 업데이트가 빨라집니다.
      • maxSurge: 이 파라미터는 디플로이먼트에서 허용하는 최대 초과 파드의 비율 또는 수를 정의합니다. 이 값이 클수록 업데이트가 빨라집니다.
      예를 들어, maxUnavailable을 10%, maxSurge를 20%로 설정하면, 롤링 업데이트가 시작될 때 먼저 새 파드를 20%만큼 추가로 생성합니다. 그런 다음 이전 파드의 10%를 중단하고, 새 파드로 교체합니다. 이 과정을 계속 반복하여 모든 이전 파드를 새 파드로 교체합니다.
      따라서 maxUnavailablemaxSurge의 값을 조정함으로써, 새로운 파드의 몇 퍼센티지가 배포되면 이전 파드를 중단하도록 설정할 수 있습니다. 이 값들을 조정하여 롤링 업데이트의 속도와 서비스의 가용성을 적절히 조절해야 합니다.
  1. 블루/그린 배포(Blue/Green Deployment): 블루/그린 배포는 테스트와 배포를 분리하기 위해 두 개의 동일한 환경을 준비하는 전략입니다. '블루' 환경에서는 현재 운영 중인 버전을 계속 실행하고, '그린' 환경에서는 새 버전을 배포하고 테스트합니다. 테스트가 완료되면, 트래픽을 그린 환경으로 전환(switch)하여 실제로 배포하게 됩니다. 이 전략은 롤링 업데이트와 달리 서비스의 버전이 동시에 혼재하는 상황을 방지할 수 있습니다.
  1. 카나리 배포(Canary Deployment): 카나리 배포는 새 버전을 점진적으로 배포하는 전략입니다. 처음에는 일부 트래픽만 새 버전으로 라우팅하고, 새 버전의 안정성을 확인한 후에 나머지 트래픽도 새 버전으로 전환합니다. 이는 새 버전의 문제가 전체 사용자에게 영향을 미치는 것을 방지할 수 있습니다.
이러한 배포 전략은 쿠버네티스 리소스, 특히 디플로이먼트, 서비스, 그리고 인그레스를 사용하여 구현할 수 있습니다. 구체적인 설정 방법은 선택한 배포 전략과 쿠버네티스 매니페스트(YAML 파일)의 작성 방법에 따라 다릅니다. AWS EKS에서는 또한 AWS App Mesh와 같은 서비스 메시를 활용하여 더욱 고급스러운 배포 전략을 구현할 수도 있습니다.