본문 바로가기
카테고리 없음

AWS ECR과 ECS를 이용한 CI/CD 구현(1)

by sum_mit45 2024. 11. 19.
728x90
반응형
11월 15일(금) ~ 11월 18일(월) 수업 중

 

AWS Elastic Container Registry(ECR)Elastic Container Service(ECS)를 활용하면 애플리케이션 배포 프로세스를 자동화하여 안정성과 효율성을 극대화할 수 있다. ECR과 ECS를 활용한 CI/CD 파이프라인을 단계별로 구축하는 방법을 정리하였다.

 

전체 목차는 아래와 같다.

0. ECR과 ECS 소개
1. Spring Boot 프로젝트 생성
2. Docker 이용한 CI/CD 적용
3. Git Ops 설정
4. Docker Hub와 연동하여 이미지 빌드 및 배포 자동화
5. AWS ECR(Elastic Container Registry)
6. GitHub Actions에서 ECR에 접근 설정
7. AWS ECS(Elastic Container Service)
8. Github Actions에서 ECS 적용하기 => CD

0. ECR과 ECS 소개

  • ECR (Elastic Container Registry): Docker 컨테이너 이미지를 저장하고 관리하는 완전 관리형 컨테이너 레지스트리 서비스로, 높은 가용성과 보안을 제공한다. ECR을 사용하면 Docker Hub처럼 이미지를 쉽게 푸시하고 ECS와 연동하여 배포할 수 있다.
  • ECS (Elastic Container Service): 컨테이너 오케스트레이션 서비스로, AWS에서 컨테이너 기반 애플리케이션을 실행, 관리, 확장할 수 있도록 돕는다. ECS는 Fargate(서버리스) 또는 EC2 인스턴스를 기반으로 애플리케이션을 배포할 수 있다.

CI/CD 파이프라인의 주요 단계는 다음과 같다:

  1. 코드 작성 및 푸시: 애플리케이션 소스를 GitHub 등의 소스 관리 툴에 푸시한다. 
  2. 이미지 빌드 및 ECR 푸시: 소스 코드를 기반으로 Github Actions에서 Docker 이미지를 빌드하고 ECR에 업로드한다. 
  3. ECS 배포: ECR에 저장된 최신 Docker 이미지를 ECS로 배포하여 업데이트된 애플리케이션을 서비스한다.
CI(Continuous Integration): 코드 변경 사항을 통합하고 테스트하여 빌드 가능한 상태를 유지하는 과정 => 코드 작성 및 푸시, 이미지 빌드
CD(Continuous Deployment): 빌드된 애플리케이션을 자동으로 배포하는 과정 => ECR푸시 및 ECS 배포

1. Spring Boot 프로젝트 생성

(1) Spring Project 생성

S스프링 프로젝트 시작하기

  • 프로젝트 이름(Name)
  • Type: Gradle-Groovy
  • Group
  • JDK, Java 버전 설정

이렇게 4가지 정도 신경써서 작성하고 [Next] 로 넘어간다.

Dependency 설정

 

정말 최소한의 스프링 부트 프로젝트를 만들기 위해서 아래 3가지 Dependency를 설정했다. 

  • Spring Boot DevTools
  • Spring Web
  • Lombok

(2) Spring Boot 코드 작성

FrontController.java

FrontController.java 파일을 만들고 아래와 같이 작성해준다. 

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class FrontController {
    @RequestMapping("/")
    public Map<String, Object> index(){
        Map<String, Object> model = new HashMap<>();

        model.put("result", "success");
        return model;
    }

}

 

  • @RestController: 이 클래스가 REST API 요청을 처리하는 컨트롤러이며, 반환값은 JSON 형식으로 응답.
  • @RequestMapping("/"): HTTP 요청의 URL 매핑을 지정한다. http://localhost:8080/로 접근했을 때 이 메서드가 실행된다. 
  • index() 메서드: 루트 URL 요청이 들어왔을 때, {"result": "success"}와 같은 JSON 형식의 응답을 생성하여 Map<string, object> 형식의 데이터를 반환한다.
  • HashMap: 데이터를 저장하기 위해 사용한 자료구조로, 키("result")와 값("success")으로 데이터를 저장한다.

application.yaml

기존의 application.properties 파일을 application.yaml 파일로 수정하고, 80 포트로 접근할 수 있도록 수정한다. 

server:
 port: 80

2.  Docker 이용해서 CI/CD 적용

(1) Docker Image 생성 및 도커 허브에 push

터미널에서 빌드히가

./gradlew clean build
  •  Gradle Wrapper를 사용하여 프로젝트를 빌드하는 명령어
    • clean: 이전 빌드 결과를 삭제
    • build: 프로젝트의 컴파일, 테스트, 패키징 단계를 수행해 최종 결과물을 생성하며, 결과물은 build/libs 디렉터리에 생성
  • 빌드 결과물로 build/libs 디렉터리에 2개의 JAR 파일이 생성된다.
  • Jenkins를 이용하는 경우는 이 과정에서 compileJava를 수행하고, 단위 테스트 및 코드 커버리지 측정을 진행하며, 정적 분석 도구를 사용해 코드 품질을 점검할 수 있다. 

(2) 이미지 생성을 위한 Dockerfile 작성

(2-1) Java 17 환경에서 Spring Boot 애플리케이션을 컨테이너화하기 위한 설정을 정의하는 Dockerfile 을 작성

FROM amazoncorretto:17
CMD ["./mvnw", "clean", "package"]
COPY ./build/libs/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]
  • FROM amazoncorretto:17 Amazon Corretto 17(JDK 17 기반)의 Docker 이미지를 기반으로 사용. (Java 실행 환경 제공)
  • CMD ["./mvnw", "clean", "package"] 컨테이너 실행 시 Maven Wrapper(mvnw)를 이용해 프로젝트를 빌드하고, clean으로 이전 빌드 결과를 제거한 후 package로 JAR 파일을 생성.
  • COPY ./build/libs/*.jar app.jar 로컬 시스템의 build/libs 디렉터리에 있는 JAR 파일을 컨테이너 내부로 복사하여 app.jar 이름으로 저장.
  • ENTRYPOINT ["java", "-jar", "app.jar"] 컨테이너가 실행될 때 Java 명령으로 app.jar 애플리케이션을 실행.

(2-2) 이미지 생성을 위한 명령어

현재 디렉터리의 Dockerfile을 기반으로 이미지를 빌드하고 cicd로 태그하는 명령어

docker build -t cicd .

도커를 이용한 이미지 생성 후

(2-3) 생성된 이미지 확인

도커에 저장된 이미지들의 목록을 확인하는 명령어

docker images

 

생성된 이미지 확인

 

(2-4) 이미지를 컨테이너로 만들어서 실행

이름이 cicd(--name)인 컨테이너를 백그라운드에서 실행하고(-d), 호스트의 80포트를 컨테이너의 80번 포트에 연결(-p)하는 명령어

docker run --name cicd -d -p 80:80 cicd

 

(2-5) springboot 실행하지 않아도 localhost:80 에서 작동 중인 것 확인

localhost:80 포트 확인

3. GitOps 설정

(3-1) github에서 레포지토리 생성

(3-2) git 레포지토리와 springboot project와 연동

git init
git add .
git commit -m "init"
git branch -M main
git remote add origin https://github.com/[아이디]/[레포지토리이름].git
git push -u origin main​

 

github에 해당 코드 업로드한 모습

4. Docker Hub와 연동하여 이미지 빌드 및 배포 자동화

(4-1) Docker Hub에 레포지토리 생성

docker hub에 레포지토리 생성

(4-2) Docker Hub에서 Access Token 발급

(4-3) Github Repository에서 Secret Key 생성

 

[레포지토리] > [Settings] > [Security] > [Secrets and variables] > [Actions]

아래 세 항목에 대한 시크릿 키를 생성한다. 

  • DOCKERHUB_TOKEN(필수) - 위에서 발급받은 키
  • DOCKERHUB_USERNAME
  • DOCKERHUB_REPOSITORY

.github/workflows 디렉터리에 yaml 파일 작성

name: Java and AWS ECS CICD
on:
 push:
   branches: [ "main" ]

permissions:
 contents: read

jobs:
 build-docker-image:
   #ubuntu 환경에서 수행
   runs-on: ubuntu-latest
   steps:
   #소스 코드 가져오기
   - uses: actions/checkout@v3
   #JDK 설치
   - name: Set up JDK 17
     uses: actions/setup-java@v3
     with:
       java-version: '17'
       distribution: 'temurin'
   #Spring Boot Application Build
   - name: Build with Gradle
     uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
     with:
       arguments: clean bootJar

   #Docker Hub 로그인
   - name: Login to DockerHub
     uses: docker/login-action@v1
     with:
       username: ${{ secrets.DOCKERHUB_USERNAME }}
       password: ${{ secrets.DOCKERHUB_TOKEN }}

   #이미지 업로드
   - name: build and release
     run: |
       docker build -t ${{ secrets.DOCKERHUB_REPOSITORY }} .
       docker tag ${{ secrets.DOCKERHUB_REPOSITORY }}:latest ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest
       docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest

 

이 워크플로는 Spring Boot 애플리케이션의 빌드 및 Docker 이미지 생성을 자동화하고, Docker Hub에 업로드하는 프로세스를 구현하여 CI/CD를 간소화한다. 코드를 구체적으로 보자면 아래와 같다. 

1. Workflow 설정
name: Java and AWS ECS CICD
on:
 push:
   branches: [ "main" ]
    • name: 워크플로우 이름을 설정. (Java and AWS ECS CICD)
    • on: 특정 이벤트(여기서는 push 이벤트)가 발생했을 때 워크플로를 실행한다.
      • branches: main 브랜치에 코드가 푸시될 때 실행된다.

2. 권한 설정

permissions:
 contents: read
  • contents: read: 워크플로 실행 시 리포지토리 내용을 읽는 권한을 부여한다.

3. Job 정의

jobs:
 build-docker-image:
   runs-on: ubuntu-latest
  • jobs: 작업 단위를 정의.
  • build-docker-image: 작업 이름
  • runs-on: ubuntu-latest: 해당 작업이 실행될 환경으로 최신 Ubuntu 이미지가 사용된다.

4-1. 작업단계(Steps) - 소스 코드 가져오기

- uses: actions/checkout@v3
  • 리포지토리의 소스 코드를 가져온다. 이 단계에서 GitHub Actions Runner에 현재 브랜치의 모든 파일이 복사된다.

4-2. 작업단계(Steps) - JDK 설치

- name: Set up JDK 17
  uses: actions/setup-java@v3
  with:
    java-version: '17'
    distribution: 'temurin'
  • Java 17 환경을 설정
  • distribution: temurin: Eclipse Temurin JDK를 사용

4-3. 작업단계(Steps) - Spring Boot 애플리케이션 빌드

- name: Build with Gradle
  uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1
  with:
    arguments: clean bootJar
  • Gradle을 사용해 애플리케이션을 빌드
  • arguments: clean bootJar: 기존 빌드 결과물을 제거하고 bootJar로 새로 빌드

4-4. 작업단계(Steps) - Docker Hub 로그인

- name: Login to DockerHub
  uses: docker/login-action@v1
  with:
    username: ${{ secrets.DOCKERHUB_USERNAME }}
    password: ${{ secrets.DOCKERHUB_TOKEN }}
  • Docker Hub에 로그인
  • secrets.DOCKERHUB_USERNAMEsecrets.DOCKERHUB_TOKEN: GitHub Secrets에 저장된 Docker Hub 계정 정보

4-5. 작업단계(Steps) - Docker 이미지 빌드 및 업로드

- name: build and release
  run: |
    docker build -t ${{ secrets.DOCKERHUB_REPOSITORY }} .
    docker tag ${{ secrets.DOCKERHUB_REPOSITORY }}:latest ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest
    docker push ${{ secrets.DOCKERHUB_USERNAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest
  • docker build: 현재 디렉터리의 Dockerfile을 사용해 Docker 이미지를 빌드한다.
    • -t ${{ secrets.DOCKERHUB_REPOSITORY }}: 이미지 이름을 설정한다.
  • docker tag: 이미지를 태그로 식별한다.
    • 최신 버전(:latest)으로 태깅한다.
  • docker push: Docker Hub에 이미지를 업로드한다.
728x90
반응형