Updated:

Docker

 Docker는 애플리케이션을 쉽게 만들고, 테스트하고, 배포할 수 있게 도와주는 소프트웨어 플랫폼으로, Docker image는 애플리케이션을 실행하는 데 필요한 모든 것을 포함한다. 주요 특징은 다음과 같다.

  • 컨테이너화: 애플리케이션과 필요한 모든 것을 하나의 패키지로 묶어 어디서든 실행할 수 있음
  • 경량: Docker는 운영 체제의 커널을 공유하므로, 가상 머신보다 훨씬 가볍고 빠르게 실행됨
  • 이식성: Docker 컨테이너는 어디서든 동일하게 실행됨
  • 확장성: Docker를 사용하면 여러 개의 컨테이너를 효율적으로 관리하고 쉽게 확장할 수 있음

 주요 키워드는 다음과 같다.

  • Image: 애플리케이션과 모든 실행에 필요한 파일을 포함한 읽기 전용 템플릿
  • Container: 이미지를 실행하여 동작하는 애플리케이션 인스턴스
  • Dockerfile: 이미지를 생성하기 위한 명령어가 담긴 스크립트 파일
  • Docker Hub: 이미지를 저장하고 공유하는 중앙 저장소
  • Volume: 컨테이너 데이터를 지속적으로 저장하는 메커니즘
  • Network: 컨테이너 간의 통신을 관리하는 방식
    • Bridge Network
      • 기본적으로 Docker가 컨테이너를 실행할 때 사용하는 네트워크
      • 동일한 브리지 네트워크에 연결된 컨테이너들은 서로 통신할 수 있음
      • 외부 네트워크와는 NAT (내부 네트워크의 여러 장치가 하나의 공용 IP 주소를 통해 외부 네트워크와 통신할 수 있도록 IP 주소를 변환하는 기술) 를 통해 통신
      • 일반적으로 단일 호스트에서 여러 컨테이너를 연결할 때 사용
      • 명시하지 않으면 모두 브리지 네트워크에서 실행
    • Host Network
      • 컨테이너가 호스트의 네트워크 스택을 직접 사용
      • 네트워크 격리가 없기 때문에 성능상 이점이 있지만, 보안 및 네트워크 충돌 위험이 있음
      • 일반적으로 성능이 중요한 애플리케이션에 사용
    • Overlay Network
      • 여러 Docker 호스트에 걸쳐 있는 컨테이너를 연결할 때 사용
      • Swarm 모드(Docker 컨테이너의 오케스트레이션과 클러스터링을 지원하여 여러 호스트에서 컨테이너를 관리하고 배포할 수 있는 기능)나 Kubernetes 같은 오케스트레이션 도구와 함께 사용
      • 데이터 센터 또는 클라우드 환경에서 분산 시스템을 구축할 때 유용

 Docker는 다음과 같은 상황에서 사용하면 좋다.

  • 일관된 개발 환경이 필요할 때
  • 애플리케이션을 빠르게 배포하고 싶을 때
  • 마이크로서비스 아키텍처를 도입할 때
  • CI/CD 파이프라인을 구축할 때
  • 리소스 효율성을 높이고 싶을 때
  • 애플리케이션 격리가 필요할 때
  • 쉽게 스케일링하고 싶을 때
  • Kubernetes와 함께 사용하고자 할 때

Docker 명령어

 Image와 관련된 명령어는 다음과 같다.

  • 이미지 빌드
    • docker build -t myapp:latest .
    • 현재 디렉토리의 Dockerfile을 기반으로 myapp이라는 이름의 이미지를 생성
    • -t 옵션을 사용하여 이미지의 이름과 태그를 입력 할 수 있음
  • 이미지 가져오기
    • docker pull postgres
    • 도커 허브에서 해당 이미지를 가져옴
  • 이미지 목록 보기
    • docker images
    • 현재 로컬에 저장된 Docker 이미지를 목록으로 표시
  • 이미지 삭제
    • docker rmi myapp:latest
    • myapp:latest 이미지를 로컬 저장소에서 삭제

 Docker Container와 관련된 명령어는 다음과 같다. 참고로 컨테이너 아이디는 모두 작성할 필요없이 식별 가능한 자릿수까지만 입력해도 된다.

  • 컨테이너 실행
    • docker run -d -p 8080:80 myapp:latest
    • myapp:latest 이미지를 사용하여 컨테이너를 실행
      • -d (detached mode) 옵션은 백그라운드에서 실행. 터미널을 컨테이너에 붙잡히지 않고, 컨테이너가 백그라운드에서 계속 실행
      • -p 옵션은 호스트의 8080 포트를 컨테이너의 80 포트에 매핑
  • 컨테이너 내부 접속
    • docker exec -it 컨테이너_아이디 /bin/bash
    • -i (interactive): 컨테이너의 표준 입력(STDIN)을 열어둠
      • 컨테이너 내부에서 사용자 입력을 받을 수 있음
    • -t (tty): 가상 터미널을 할당
      • 컨테이너 내부에서 터미널을 사용할 수 있음
  • 실행 중인 컨테이너 목록 보기
    • docker ps
    • 현재 실행 중인 컨테이너의 목록을 표시
  • 모든 컨테이너 목록 보기
    • docker ps -a
    • docker ps -al: 중지된 컨테이너를 포함한 모든 컨테이너의 목록을 표시
    • 마지막으로 실행된 컨테이너를 가장 먼저 나열
  • 컨테이너 중지
    • docker stop container_id
    • 지정된 container_id를 가진 컨테이너를 중지
  • 컨테이너 시작
    • docker start container_id
    • 중지된 컨테이너를 다시 시작
  • 컨테이너 삭제
    • docker rm 컨테이너_아이디
    • 지정된 container_id를 가진 컨테이너를 삭제

 Docker 네트워크 및 볼륨 관련 명령어는 다음과 같다.

  • 네트워크 생성
    • docker network create mynetwork
    • mynetwork이라는 이름의 네트워크를 생성
  • 네트워크 목록 보기
    • docker network ls
    • 현재 설정된 Docker 네트워크의 목록을 표시
  • 네트워크 삭제
    • docker nework rm mynetwork
    • mynetwork이라는 이름의 네트워크를 삭제
  • 볼륨 생성
    • docker volume create myvolume
    • myvolume이라는 이름의 볼륨을 생성
  • 볼륨 목록 보기
    • docker volume ls
    • 현재 설정된 Docker 볼륨의 목록을 표시
  • 볼륨 삭제
    • docker volume rm myvolume
    • myvolume이라는 이름의 볼륨을 삭제

 도커를 통해 PostgreSQL 컨테이너 2개를 실행해본다(https://hub.docker.com/hardened-images/catalog/dhi/postgres). 먼저 이미지를 받는다.

docker pull postgres

 이제 컨테이너를 실행해본다.

docker run -d --name postgres-sample \
  -p 5433:5432 \
	-e POSTGRES_USER=admin1 \
	-e POSTGRES_PASSWORD=admin2 \
	-e PGDATA=/var/lib/postgresql/data/pgdata \
	-v ${로컬_바인딩_폴더}:/var/lib/postgresql/data:z \
	postgres
  • :z 옵션은 SELinux(Secure Enhanced Linux) 환경에서 사용되는 파일 시스템 옵션
    • Docker 컨테이너가 호스트 파일 시스템의 특정 디렉토리에 접근할 수 있도록 SELinux 컨텍스트를 설정
    • SELinux는 Linux 시스템의 보안을 강화하기 위해 파일 및 프로세스에 대한 권한을 세밀하게 제어
  • :z 옵션은 해당 볼륨이 여러 컨테이너에서 공유될 수 있음을 나타내며, 이를 통해 컨테이너가 해당 디렉토리에 읽기 및 쓰기 권한을 갖도록 함

Docker Compose

 Docker Compose는 다중 컨테이너 Docker 애플리케이션을 정의하고 실행하기 위한 도구로, docker-compose.yml 파일 하나로 애플리케이션의 서비스, 네트워크, 볼륨 등을 정의할 수 있다.
 Docker Compose의 파일 구조는 다음과 같다.

version: '3'
services:
  web:
    image: nginx
    ports:
      - "8080:80"
  app:
    build: .
    ports:
      - "8081:8080"
    depends_on:
      - db
  db:
    image: postgres
    environment:
      POSTGRES_PASSWORD: example
  • version: Docker Compose 파일의 버전 지정
  • services: 애플리케이션의 각 서비스 정의
  • web, app, db: 각각의 서비스 이름
  • image: 서비스를 실행할 Docker 이미지 지정
  • build: Dockerfile이 있는 디렉토리 경로를 지정하여 이미지 빌드
  • ports: 호스트와 컨테이너 간의 포트 매핑
  • depends_on: 다른 서비스가 먼저 실행되어야 하는 순서 지정
  • environment: 컨테이너의 환경 변수 설정

Docker Compose 명령어

  • docker compose up: docker-compose.yml 파일에 정의된 서비스를 빌드하고 시작. 만약 이미 빌드된 이미지가 있다면 이를 사용
    • 백그라운드에서 실행하려면 d 옵션 추가
      docker compose up -d
      docker compose -f /path/to/your/project/docker-compose.yml up
      
  • docker compose down: 실행 중인 모든 서비스를 중지하고 컨테이너, 네트워크, 볼륨 등 정리
  • docker compose build: docker-compose.yml 파일에 정의된 서비스 빌드
  • docker compose ps: 현재 실행 중인 서비스 확인
  • docker compose logs: 실행 중인 서비스의 로그 확인

실습

 2개의 스프링 컨테이너를 생성하고 사용자가 service-a 컨테이너의 컨트롤러를 호출하면, service-b 컨테이너를 호출한다. 최종 사용자에게 노출되는 메시지는 ”service-a: hi ###### service-b: hello” 이다. 메시지 “service-b: hello” 는 service-b 컨테이너의 메시지이다.
 먼저 Lombok, Spring Web, OpenFeign dependencies를 가진 2개의 Spring 프로젝트를 생성하고 service-b부터 작성을 시작한다.

package com.service.b;

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

@RestController
public class BController {

    @GetMapping("hello")
    public String hello() {
        return "Hello";
    }
}
spring.application.name=service-b

server.port=18081

 이제 service-a를 작성한다.

package com.service.a;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
public class AApplication {

    public static void main(String[] args) {
        SpringApplication.run(AApplication.class, args);
    }

}
package com.service.a;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class AController {

    private final BServiceClient bServiceClient;

    @GetMapping("hi")
    public String hi() {
        String hello = bServiceClient.getHello();
        return "service-a : hi ###### service-b: " + hello;
    }
}
package com.service.a;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;

@FeignClient(name = "service-b", url = "${service.b.url}")
public interface BServiceClient { // B Service를 호출하는 Client
    @GetMapping("/hello")
    public String getHello();
}

실습 - Docker 사용

 이번엔 Docker를 사용해서 컨테이너 2개를 실행해본다. 먼저 두 프로젝트의 port를 8080으로 수정한다. 또한 도커 컨테이너끼리는 같은 네트워크에 있으면 도커 컨테이너의 이름으로 호출할 수 있기 때문에 service.b.urlhttp://service-b:8080 으로 수정한다.
 두 프로젝트에 Dockerfile을 작성한다.

FROM openjdk:17-jdk-slim

VOLUME /tmp

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

 Docker 끼리 Container 이름으로 호출하기 위해서는 기본 브리지 네트워크가 아닌 사용자 정의 네트워크에서 진행해야 한다. 그렇기 때문에 도커 네트워크를 생성한다.

$ docker network create my-network
5638c404332de3e8b56d589397f27a637eb377f5b27c998eae3ebd690caf9b79

 이후 프로젝트를 빌드하고, 각 프로젝트의 위치로 들어가 image를 생성한다.

$ ./gradlew clean bootJar  
$ docker build -t img-service-a .
$ ./gradlew clean bootJar
$ docker build -t img-service-a .

 이제 container를 생성한다.

PS C:\Users\dbsau\project\b> docker run -d --name service-b `
>> --network my-network `                                                              
>> -p 18081:8080 `                                                                    
>> img-service-b                                                                                         
300b21a6cb147d22e72b2c2218034bdb520f6c6f694a660d62718619caeed32f
PS C:\Users\dbsau\project\b> docker ps
CONTAINER ID   IMAGE           COMMAND                CREATED          STATUS          PORTS                                           NAMES
300b21a6cb14   img-service-b   "java -jar /app.jar"   16 seconds ago   Up 15 seconds   0.0.0.0:18081->8080/tcp, [::]:18081->8080/tcp   service-b

이제 http://localhost:18081/hello로 접속하면 service-b가 호출되는 것을 확인할 수 있다.

같은 방식으로 service-a image를 만들고 http://localhost:18080/hi로 접속하면 service-a가 호출되는 것을 확인할 수 있다.

실습 - Docker Compose 사용

 기존 Docker Container를 모두 삭제한다.

docker rm -f 서비스_컨테이너_아이디

 이후 service-a 프로젝트와 service-b 프로젝트의 상위 폴더에서 docker-compose.yml 파일을 작성한다.

version: '3.8'

services:
  service-a:
    image: img-service-a
    ports:
      - "18080:8080"
    environment:
      - SERVICE_B_URL=http://service-b:8080
    depends_on:
      - service-b

  service-b:
    image: img-service-b
    ports:
      - "18081:8080"

networks:
  default:
    driver: bridge

 이제 docker compose up을 하면 실행되는 것을 확인할 수 있다. 이제 http://localhost:18080/hi로 접속하면 service-a가 호출되는 것을 확인할 수 있다.
 참고로 Docker Compose를 사용하여 서비스를 실행하면, Docker Compose는 기본적으로 새 브리지 네트워크를 생성하여 각 서비스 컨테이너를 그 네트워크에 연결한다. 이 네트워크는 docker-compose.yml 파일에 정의된 모든 서비스가 서로 통신할 수 있도록 한다.
 Docker Compose는 자동으로 네트워크를 생성하며, 이 네트워크는 docker-compose.yml 파일이 있는 디렉토리의 이름을 기반으로 한다. 네트워크의 이름은 다음과 같은 형식으로 생성된다.

directoryname_default

docker network ls를 사용하여 네트워크가 생성되었는지 확인해본다.

PS C:\Users\dbsau\project> docker network ls
NETWORK ID     NAME                       DRIVER    SCOPE
5c5a4ec9281c   bridge                     bridge    local
31e65c59c6e0   deliverycommerce_default   bridge    local
07555a55daa7   host                       host      local
5638c404332d   my-network                 bridge    local
440f6d75ddc8   none                       null      local
442a7f165d12   project_default            bridge    local

CI/CD

CI/CD

  • CI(Continuous Integration)
    • 개발자가 변경한 코드를 자주 통합하고, 이 코드가 전체 시스템과 잘 어우러지는지 자동으로 테스트하는 프로세스
    • 코드 변경이 발생할 때마다 빌드 및 테스트를 수행하여 코드 품질을 유지하고 문제를 조기에 발견할 수 있음
  • CD(Continuous Delivery)
    • 코드 변경이 통합되고 테스트를 통과하면, 이를 자동으로 스테이징 환경 또는 프로덕션 환경에 배포
    • 지속적인 배포(Continuous Deployment)는 CI/CD의 확장 개념으로, 승인 절차 없이 자동으로 프로덕션 환경에 배포하는 것을 의미

 CI/CD의 장점은 빠른 피드백, 자동화된 프로제스, 일관된 배포, 높은 품질 유지, 개발 속도 향상 등이 있다.
 CI/CD의 도구는 다음과 같다.

  • GitHub Actions: GitHub 저장소에 직접 통합되어 있는 CI/CD 도구로, YAML 파일을 사용하여 워크플로우를 정의할 수 있음
  • Jenkins: 오픈 소스 CI/CD 도구로, 플러그인을 통해 다양한 기능을 확장할 수 있음
  • GitLab CI: GitLab과 통합된 CI/CD 도구로, GitLab 저장소를 기반으로 CI/CD 파이프라인을 설정할 수 있음

Amazon ECS

 AWS Elastic Container Service는 docker 애플리케이션을 쉽게 배포하고 운영 할 수 있도록 지원하는 완전관리형 Container Orchestration 서비스로 Kubernetes와 같은 container Orchestration 서비스이다. serverless로 구성할 수 있는데, 이렇게 하면 인스턴스(가성서버)를 구성하고 관리할 필요도 없다.
 ECS는 크게 ECR, ECS Cluster, ECS Service, ECS Task로 이루어져있다.

  • ECR: Docker image 저장소
  • ECS Cluster: 컨테이너를 실행하기 위한 Cluster로 여러 인스턴스로 이루어짐
    • 이 인스턴스에 Docker container가 분산 실행
    • Serverless로 할 경우에는 인스턴스도 필요 없음
  • ECS Server: Docker 애플리케이션의 실행 그룹
  • ECS Task: ECS Server에 실제로 실행되는 docker container들

 ECS는 로드밸런서와, 모니터링, Auto Scaling 등의 요소도 있다. 따라서 ECS를 이용하면 이런 요소들까지 자동으로 관리할 수 있다.

  • ECS의 최소 실행단위는 Task
  • Service는 Task가 두개 이상 모인것
  • Task는 docker 애플리케이션
  • 어느 포트에서 몇개의 도커 컨테이너를 어떤 이미지로 실행할지 애플리케이션 실행 정보가 필요
  • 이 정보를 저장하고 있는것이 Task definition

실습

 Gitlab을 사용하여 CI/CD를 실습해본다. Gitlab 파이프 라인에서 build를 수행하여 AWS ECR에 도커 이미지를 등록하고 파이프라인의 deploy에서 ECS에 어플리케이션을 배포한다.
 GitLab에 가입한 후, 프로젝트를 생성한 뒤, Intellij에서 Spring 프로젝트와 연동한다.
 프로젝트와 연동한 후 Controller를 작성하고 application.properties를 수정한다.

package com.spring.sample;

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

@RestController
public class SampleController {

    @GetMapping("/sample")
    public String sample() {
        return "Hello World";
    }
}
spring.application.name=sample

server.port=8080

 또한 Dockerfile도 작성한다.

FROM eclipse-temurin:17-jdk-jammy

VOLUME /tmp

ARG JAR_FILE=build/libs/*.jar

COPY ${JAR_FILE} app.jar

ENTRYPOINT ["java","-jar","/app.jar"]

AWS Elastic Container Service

 이후 AWS EC2 페이지에 접속해 보안그룹에서 80 포트와 8080 포트를 추가한다.
 다음으로 Amazon Elastic Container Registry(ECR)에 접속해 리포지토리를 생성한다. 리포지토리 이름은 gitlab url에서 group-xxxxxxxx/xxxxxx와 동일하게 작성하고 생성한다.
 이번에는 Amazon Elastic Container Service(ECS)에 접속해 클러스터를 생성한다. 클러스터 이름은 project-cluster로 생성했다. 이후 태스크 정의에 들어가 새 태스크 정의(project-01-task)를 생성한다. 컨테이너 정보는 이름을 project-01-container로 정의하고, 이미지 URI를 전에 만들었던 ECR 의 주소를 넣는다. 컨테이너 포트는 8080을 매핑 후 생성한다.
 Service를 생성해본다. 클러스터 이름을 클릭해서 들어오면 하단 서비스 영역에서 태스크 정의 패밀리를 project-01-task로 지정하고 서비스 이름은 project-01-service, 로드 밸런싱을 선택하고 로드 밸런서 이름을 project-01-lb로 설정한다.
 마지막으로 IAM에 접속해 엑세스 관리를 해본다. 사용자 탭에서 project-01-user 사용자를 생성한다. AdministratorAccess 권한을 직접 정책 연결을 한다. 사용자 페이지에 들어가 엑세스 키를 기타를 선택해 만들어보고 cvs파일 다운로드를 진행해 로컬에 저장한다.
 이제 다시 Gitlan으로 돌아와 Setting에 CI/CD 탭으로 들어간다. Variables 탭에서 CI/CD variables를 설정한다. 이때 값들은 앞에서 다운받은 cvs 파일에 있는 값들을 넣는다.

 Spring 프로젝트로 넘어와서 프로젝트 루트에 .gitlab-ci.yml 파일을 생성한다.

services:
  - docker:stable-dind
stages:
  - build jar
  - build and push docker image
  - deploy
variables:
  APPLICATION_NAME: "01"
  TAG_NAME: "latest"
  DOCKER_IMAGE: "group-11512812/project-01"
build:
  image: openjdk:17-jdk-slim
  stage: build jar
  script:
    - chmod +x gradlew
    - ./gradlew clean build
  artifacts:
    paths:
      - build/libs/*.jar
docker build:
  image: docker:latest
  stage: build and push docker image
  rules:
    - if: $CI_COMMIT_BRANCH == "main" || $CI_COMMIT_REF_NAME == "main"
      variables:
        TAG_NAME: "latest"
    - if: $CI_COMMIT_BRANCH == "develop" || $CI_COMMIT_REF_NAME == "develop"
      variables:
        TAG_NAME: "develop"

  script:
    - apk add --update --no-cache curl py3-pip py3-virtualenv
    - python3 -m venv /tmp/venv
    - source /tmp/venv/bin/activate
    - pip install awscli
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set region ap-northeast-2
    - docker build -t $DOCKER_IMAGE .
    - docker tag $DOCKER_IMAGE:latest 590183718586.dkr.ecr.ap-northeast-2.amazonaws.com/group-11512812/project-01:$TAG_NAME
    - aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 590183718586.dkr.ecr.ap-northeast-2.amazonaws.com
    - docker push 590183718586.dkr.ecr.ap-northeast-2.amazonaws.com/$DOCKER_IMAGE:$TAG_NAME

deploy:
  image: python:3.9-slim
  stage: deploy
  script:
    - python3 -m venv /tmp/venv
    - source /tmp/venv/bin/activate
    - pip install awscli
    - aws configure set aws_access_key_id $AWS_ACCESS_KEY_ID
    - aws configure set aws_secret_access_key $AWS_SECRET_ACCESS_KEY
    - aws configure set region ap-northeast-2
    - aws ecs update-service --cluster project-cluster --service project-01-service --task-definition project-01-task:1 --force-new-deployment
  • DOCKER_IMAGE: group-11512812/project-01
    • ECR 리포지토리 이름
  • docker tag
    • ECR 리포지토리의 푸시 명령에서 복사
  • aws ecr get-login-password ~
    • ECR 리포지토리의 푸시 명령에서 복사
  • docker push ~
    • ECR 리포지토리의 푸시 명령에서 복사

 이제 gitlab으로 push를 해주면 Pipeline이 진행되는 것을 확인할 수 있다. 이제 aws ecs의 서비스의 로드 밸런서에 들어간다. DNS 이름을 복사하고 접속하면 Whitelabel Error Page가 뜨는 것을 볼 수 있고 /sample 로 요청을 보내면 정상적으로 출력되는 것을 확인할 수 있다.

Github actions

 Gitlab에서 진행한 작업을 Github에서도 진행해본다. Spring 프로젝트에서 github 설정으로가 프로젝트를 하나 만들어본다. 그러면 자동으로 github에 리포지토리가 생기는 것을 확인할 수 있다.
 리포지토리의 Actions 탭에 들어가 ecs를 검색 후 Configure을 선택해준뒤, asw.yml 파일을 생성한다.

# This workflow will build and push a new container image to Amazon ECR,
# and then will deploy a new task definition to Amazon ECS, when there is a push to the "main" branch.
#
# To use this workflow, you will need to complete the following set-up steps:
#
# 1. Create an ECR repository to store your images.
#    For example: `aws ecr create-repository --repository-name my-ecr-repo --region us-east-2`.
#    Replace the value of the `ECR_REPOSITORY` environment variable in the workflow below with your repository's name.
#    Replace the value of the `AWS_REGION` environment variable in the workflow below with your repository's region.
#
# 2. Create an ECS task definition, an ECS cluster, and an ECS service.
#    For example, follow the Getting Started guide on the ECS console:
#      https://us-east-2.console.aws.amazon.com/ecs/home?region=us-east-2#/firstRun
#    Replace the value of the `ECS_SERVICE` environment variable in the workflow below with the name you set for the Amazon ECS service.
#    Replace the value of the `ECS_CLUSTER` environment variable in the workflow below with the name you set for the cluster.
#
# 3. Store your ECS task definition as a JSON file in your repository.
#    The format should follow the output of `aws ecs register-task-definition --generate-cli-skeleton`.
#    Replace the value of the `ECS_TASK_DEFINITION` environment variable in the workflow below with the path to the JSON file.
#    Replace the value of the `CONTAINER_NAME` environment variable in the workflow below with the name of the container
#    in the `containerDefinitions` section of the task definition.
#
# 4. Store an IAM user access key in GitHub Actions secrets named `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`.
#    See the documentation for each action used below for the recommended IAM policies for this IAM user,
#    and best practices on handling the access key credentials.

name: Deploy to Amazon ECS

on:
  push:
    branches: [ "master" ]

env:
  AWS_REGION: ap-northeast-2                  # set this to your preferred AWS region, e.g. us-west-1
  ECR_REPOSITORY: group-11512812/project-01         # set this to your Amazon ECR repository name
  ECS_SERVICE: project-01-service                 # set this to your Amazon ECS service name
  ECS_CLUSTER: project-cluster                # set this to your Amazon ECS cluster name
  ECS_TASK_DEFINITION: .github/workflows/project-01-task-revision1.json # set this to the path to your Amazon ECS task definition
                                               # file, e.g. .aws/task-definition.json
  CONTAINER_NAME: project-01-container           # set this to the name of the container in the
                                               # containerDefinitions section of your task definition

permissions:
  contents: read

jobs:
  build:
    name: Build Jar
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'

    - name: Grant execute permission for gradlew
      run: chmod +x gradlew

    - name: Build with Gradle
      run: ./gradlew clean build

    - name: Upload Build Artifacts
      uses: actions/upload-artifact@v4
      with:
        name: build-libs
        path: build/libs/*.jar

        
  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    needs: build
    environment: production

    steps:
    - name: Checkout
      uses: actions/checkout@v4

    - name: Download Build Artifacts
      uses: actions/download-artifact@v4
      with:
        name: build-libs
        path: build/libs

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: $
        aws-secret-access-key: $
        aws-region: $

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Build, tag, and push image to Amazon ECR
      id: build-image
      env:
        ECR_REGISTRY: $
        IMAGE_TAG: $
      run: |
        # Build a docker container and
        # push it to ECR so that it can
        # be deployed to ECS.
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT

    - name: Fill in the new image ID in the Amazon ECS task definition
      id: task-def
      uses: aws-actions/amazon-ecs-render-task-definition@v1
      with:
        task-definition: $
        container-name: $
        image: $

    - name: Deploy Amazon ECS task definition
      uses: aws-actions/amazon-ecs-deploy-task-definition@v1
      with:
        task-definition: $
        service: $
        cluster: $
        wait-for-service-stability: true

 다음으로 ECS의 태스크 정의에서 JSON을 클릭해 다운로드 하고 github workflows에 추가한다.

 마지막으로 AWS_ACCESS_KEYAWS_SECRET_ACCESS_KEY를 추가해야 한다. Github 리포지토리의 settings에 들어가 Secrets and variables 탭의 Actions에 들어가 New repository secret을 클릭해 Gitlab과 동일한 작업을 진행한다. 그러면 정상적으로 Deploy 되는 것을 확인할 수 있다.

댓글남기기