티스토리 뷰

반응형

최근 프로젝트를 하면서 직접 배포를 하게 되었는데, 생각보다 복잡하고 명령어들이 낯설어서 나중에 헷갈리지 않기 위해 정리하려고 한다.환경은 Spring boot(JAVA) + MySQL + Redis이며, Dockerfile + docker-compose.yml + .github/workflow/app-backend-deploy.yml방식을 사용하였다.

글의 순서는 인스턴스 생성 => ECR 생성 => GitHub 설정 => Spring boot 설정 => 배포하기이다

 

✏️ 인스턴스 설정

인스턴스 생성

인스턴스 유형 tr.micro는 너무 용량이 작아서 배포가 잘 안 될수도 있다.

 

pem 파일 생성

ec2에 원격으로 접속하기 위해서는 로컬 컴퓨터에서 pem파일(키)이 필요하다 사진과 같이 자신이 원하는 키 페어 이름, RSA를 선택 후 키 페어 생성을 클릭한다.

 

보안그룹 생성 및 적용

20번 포트(SSH): 내 로컬 PC에서 EC2에 원격 접속하기 위해 필요한 프로토콜이다. 

8080번 포트: 스프링부트 서버가 동작할 포트번호이다.

6379번 포트 :  Redis에 접속하기 위한 포트번호이다.

탄력적 IP 할당

인스턴스 자세히 보기를 클릭하면 퍼블릭 IPv4 주소라는 것이 있다. 이 주소는 EC2를 배포할 시에 할당되는 주소인데, 이 값을 따로 지정하지 않으면 인스턴스를 시작할 때마다 새롭게 할당된다. 이를 막기 위해서 탄력적 IP를 생성한 뒤 할당해준다.

EC2 ⇒ 인스턴스 ⇒ 네트워크 및 보안 ⇒ 탄력적 IP 클릭 ⇒ 생성하기 생성된 탄력적 IP 클릭한 뒤 탄력적 IP 주소 할당을 클릭한 뒤 아래 사진과 같이 설정한다.

 

생성된 탄력적 IP 주소를 클릭한 뒤 작업 ⇒ 탄력적 IP 주소 연결을 클릭한다. 두 번째 사진과 같이 리소스 유형 = 인스턴스, 인스턴스 = 본인의 인스턴스, 프라이빗 IP주소를 선택 후 연결하기 인스턴스로 돌아와서 본인의 인스턴스를 클릭하면 퍼블릭 IPv4주소에 탄력적 IP가 할당된 것을 볼 수 있다.

 

✏️ 인스턴스 접속하기

인스턴스를 생성하였으니 로컬에서 원격으로 EC2에 접속해보자 자신의 IPV4 도메인을 잘 모르겠다면 자신의 인스턴스 클릭 ⇒ 연결 ⇒ SSH 클라이언트를 클릭한다.

chmod 600 ssuketing.pem
ssh -i pem파일명 ubuntu or ec2-user @ + 본인 IPV4 도메인
ssh -i ssuketing.pem ubuntu@(우분투인 경우)
ssh -i ssuketing.pem ec2-user@(일반적)

 

간편하게 접속하기

먼저 자신의 pem파일을 ~/.ssh/로 위치를 변경한다

cp ssuketing.pem ~/.ssh/
cd ~/.ssh
ll => ssuketing.pem존재 확인 가능

 

~/.ssh에 pem파일이 존재한다면 아래와 같은 명령어를 입력한다. 모두 차례대로 입력하면 EC2 접속에 성공한다.

chmod 600 ~/.ssh/ssuketing.pem ## 소유자에게 읽고쓰기의 권한을 할당하고, 나머지에게는 아무런 권한을 주지 않는 명령어
vi ~/.ssh/config

# 슈케팅 서버
Host ssuketing-server(원하는 서버 명)
    HostName 탄력적 IP 주소
    User ubuntu
    IdentityFile ~/.ssh/ssuketing.pem(pem키 주소)

chmod 700 ~/.ssh/config ## chmod 700 ⇒ 사용자만 읽기,쓰기,실행 모든권한이 있다.
cd ~/.ssh ## ssh로 이동
ssh ssuketing-server ## ssh config에 등록한 서비스명

 

 

✏️ ECR 설정

ECR 생성

먼저 콘솔창에서 ECS를 검색한 후 리포지토리 생성을 클릭한다

 

역할 생성

ECR로 접속하기 위해서는 권한이 필요하다

AWS 콘솔창에서 I AM 검색 ⇒ 역할 ⇒ 역할 생성

 

 

웹 자격 증명을 누르게 되면 공급자 유형을 선택해야 한다. 공급자 유형 생성 방법은 다음과 같다.

권한 추가에는 AmazonEC2ContainerRegistryReadOnly을 추가해준다

역할 클릭 → 신뢰 관계를 클릭하면 아래와 같은 JSON형식을 볼 수 있다.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
                    "token.actions.githubusercontent.com:sub": "repo:"
                }
            }
        }
    ]
}

 

✏️ Github 설정

 

Secrets and variables 생성

Github repository → settings → Secrets and variables 이동

  • EC2_HOST: EC2 인스턴스의 호스트명 또는 IP 주소를 지정한다.
  • EC2_USERNAME: EC2 인스턴스의 사용자명을 지정한다(일반적으로 ec2-user).
  • EC2_SSH_KEY: SSH 접속을 위한 개인 키를 지정한다
  • SSUKETING_ENV: .env파일에 저장된 값이다.

 

deploy.yml 생성

프로젝트 루트 디렉토리 → github → workflows → app-backend-deploy.yml 생성 각각의 코드에 대한 설명은 주석을 참고하자

# 워크플로우 이름
name: Build and Push Image to ECR

# 트리거 조건
on:
  push:
    branches: [ "main" ] # main 브랜치 기준 자동 배포
    paths: ["app-backend/**"] # app-backend 하위 경로의 변동을 감지

# 전역 변수 설정
env:
  AWS_REGION: "ap-northeast-2" # AWS REGION
  ECR_REPOSITORY: "ssuketing/app-backend" # ECR

# 권한 설정
permissions:
  id-token: write # OIDC 방식의 AWS 로그인
  contents: write

# 할 일 정의
jobs:
  deploy:
    name: Deploy
    runs-on: ubuntu-latest # VM?
    environment: production # GitHub Actions 환경

    steps:
      - name: Checkout
        uses: actions/checkout@v4 # 현재 저장소 코드 가져오기

      - name: Configure AWS credentials # AWS 자격 증명 구성
        uses: aws-actions/configure-aws-credentials@v3
        with:
          role-to-assume: ${{ secrets.ARN_ECR_PUSH_ROLE }}
          role-session-name: ecrPrivatePushRole
          aws-region: ${{ env.AWS_REGION }}
          audience: sts.amazonaws.com

      - name: Login to Amazon ECR # ECR 로그인
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Push image to Amazon ECR (api-server) # Docker 이미지 빌드 및 푸시
        id: build-image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          IMAGE_TAG: latest
        run: |
          # Build a docker container and
          # push it to ECR so that it can
          # be deployed to ECS.
           cd app-backend
           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: Update Helm Chart Version
        run: |
          cd app-backend
          # 파일 이름 지정
          FILE="Chart.yaml"
          
          # 변경된 버전을 저장
          NEW_VERSION=$(sed -i '18s/\(version: 0.0.\)\([0-9]\+\)/echo "\1$((\2 + 1))"/ge' "$FILE" && grep -oP '(?<=version: )0\.0\.[0-9]+' "$FILE")
          
          git config --global user.email "pooreumjung02@naver.com"
          git config --global user.name "pooreumjung"
          
          git add .
          git commit -m "chore(api-server): bump version to $NEW_VERSION"
          git push

      - name: Create .env on EC2
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ubuntu
          key: ${{ secrets.EC2_KEY }}
          script: |
            echo "${{ secrets.SSUKETING_ENV }}" > ~/app-backend/app-backend/.env

      - name: Deploy to EC2 via SSH
        uses: appleboy/ssh-action@v0.1.10 
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_KEY }}
          script: |
            cd ~/app-backend/app-backend  
            git pull origin main
            docker-compose down
            docker-compose up --build -d

 

 

docker-compose.yml 생성

redis 정보를 등록 + 환경 변수를 등록하는 파일이다. 또한 ports를 통해 외부에서 접속이 가능하도록 설정할 수 있다.

services:
  redis_container:
    image: redis:7
    container_name: ssuketing-redis
    hostname: redis
    ports:
      - 6379:6379
    restart: always
    networks:
      - ssuketing-network

  web-backend:
    build: "./app-backend"
    container_name: "app-backend"
    environment:
      - MYSQL_USERNAME=${MYSQL_USERNAME}
      - MYSQL_PASSWORD=${MYSQL_PASSWORD}
      - MYSQL_URL=${MYSQL_URL}
      - AWS_SECRET_KEY=${AWS_SECRET_KEY}
      - AWS_ACCESS_KEY=${AWS_ACCESS_KEY}
      - AWS_SQS_ENDPOINT=${AWS_SQS_ENDPOINT}
      - AWS_SQS_QUEUE_NAME=${AWS_SQS_QUEUE_NAME}
      - SPRING_REDIS_HOST=${SPRING_REDIS_HOST}
      - SPRING_REDIS_PORT=${SPRING_REDIS_PORT}
      - WEB_SITE_CODE=${WEB_SITE_CODE}
      - WEB_SITE_ID=${WEB_SITE_ID}
      - PRIVATE_KEY=${PRIVATE_KEY}
      - SPL_CERT_PEM=${SPL_CERT_PEM}
      - SPL_PRIKEY_PEM=${SPL_PRIKEY_PEM}
    ports:
      - 8080:8080
    networks:
      - ssuketing-network

# networks 설정
networks:
  ssuketing-network:

 

 

✏️Spring Boot 설정

application-dev.yml 환경변수 설정

spring :
  application :
    name : ssuketing

  # MySQL
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: ${MYSQL_USERNAME}
    password: ${MYSQL_PASSWORD}
    url: ${MYSQL_URL}
    hikari:
      maximum-pool-size: 50

  # JPA
  jpa:
    database: mysql
    database-platform: org.hibernate.dialect.MySQLDialect
    show-sql: false
    hibernate:
      ddl-auto: none
      naming:
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    properties:
      hibernate:
        format_sql: true
        use_sql_comments: true
        auto_quote_keyword: true
    open-in-view: false

  # Spring Security
  security:
    user:
      name: 
      password: 


  # AWS
  cloud:
    aws:
      region:
        static: ap-northeast-2
      credentials:
        secret-key: ${AWS_SECRET_KEY}
        access-key: ${AWS_ACCESS_KEY}
      sqs:
        endpoint: ${AWS_SQS_ENDPOINT}
        queue-name: ${AWS_SQS_QUEUE_NAME}
        
  # redis
  data:
    redis:
      host: ${SPRING_REDIS_HOST}
      port: ${SPRING_REDIS_PORT}

# TOMCAT
server:
  tomcat:
    threads:
      max: 500
      min-spare: 50
    accept-count: 2000
    max-connections: 20000
    connection-timeout: 15000
  servlet:
    encoding:
      charset: UTF-8
      enabled: true
      force: true
      force-response: true

# Swagger
springdoc:
  swagger-ui:
    # 각 API의 그룹 표시 순서
    # path, query, body, response 순으로 출력
    groups-order : DESC

    # 태그 정렬 순서
    # 알파벳 순 정렬
    tags-sorter: alpha

    # 컨트롤러 정렬 순서
    # method는 delete - get - patch - post - put 순으로 정렬
    operations-sorter: method

    # swagger-ui default url인 petstore html의 비활성화 설정
    disable-swagger-default-url: true

    # swagger-ui에서 try 했을 때 request duration을 알려주는 설정
    display-request-duration: true

    # 모델 확장 깊이를 2단계로 설정
    default-models-expand-depth: 2

    # 개별 API 호출의 모델 확장 깊이를 2단계로 설정
    default-model-expand-depth: 2
  api-docs :
    # OpenAPI 명세서의 경로를 /api/docs로 설정
    # 기본값 : /v3/api-docs
    path:  /api-docs

  # Spring Actuator의 엔드포인트를 Swagger에 표시
  show-actuator: true

  # API에서 사용할 기본 미디어 유형을 application/json으로 설정
  default-consumes-media-type: application/json
  default-produces-media-type: application/json

  # Swagger UI에서 출력 포맷을 pretty-print 방식으로 설정
  writer-with-default-pretty-printer: true

  # Swagger API에서 Spring의 ModelAndView 객체를 허용
  model-and-view-allowed: true

  # Swagger 문서에 포함할 경로 패턴을 지정
  paths-to-match: /**

kcp:
  site-code: ${WEB_SITE_CODE}
  up-hash-url: 
  ct-type-has: 
  ct-type-chk: 
  ct-type-dec: 
  order-prefix: 
  test-url : 
  prod-url : 
  website-id : ${WEB_SITE_ID}
  private-key-password: ${PRIVATE_KEY}
  splcert : ${SPL_CERT_PEM}
  splprivate : ${SPL_PRIKEY_PEM}

 

.env 파일 생성

AWS_ACCESS_KEY=YOUR_AWS_ACCESS_KEY
AWS_SECRET_KEY=YOUR_AWS_SECRET_KEY
AWS_SQS_ENDPOINT=YOUR_AWS_SQS_ENDPOINT
AWS_SQS_QUEUE_NAME=YOUR_AWS_SQS_QUEUE_NAME
MYSQL_PASSWORD=YOUR_MYSQL_PASSWORD
MYSQL_URL=YOUR_MYSQL_URL
MYSQL_USERNAME=YOUR_MYSQL_USERNAME
SPL_CERT_PEM=YOUR_SPL_CERT_PEM
SPL_PRIKEY_PEM=YOUR_SPL_PRIKEY_PEM
SPRING_REDIS_HOST=YOUR_SPRING_REDIS_HOST
SPRING_REDIS_PORT=YOUR_SPRING_REDIS_PORT
WEB_SITE_CODE=YOUR_WEB_SITE_CODE
WEB_SITE_ID=YOUR_WEB_SITE_ID

 

DockerFile 생성

여기서 스냅샷의 이름은 setting.gradle의 rootProject.name - build.gradle의 version값 .jar으로 이루어진다

FROM bellsoft/liberica-openjdk-alpine:17 AS builder

COPY gradlew .
COPY gradle gradle
COPY build.gradle .
COPY settings.gradle .
COPY src src
RUN chmod +x ./gradlew
RUN ./gradlew clean bootJar

FROM bellsoft/liberica-openjdk-alpine:17
COPY --from=builder build/libs/.jar app.jar

EXPOSE 8080

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

 

✏️ 배포하기

EC2 설정

해당 글은 Github Actions를 이용한 자동 배포지만, 처음에는 EC2에 접속해서 초기 설정을 해야 한다. 그래서 아래와 같은 명령어를 차례대로 입력해야 한다.

sudo apt update  ## 초기 설정

# 깃 관련
sudo apt-get install git ## 깃 설치

# Docker 관련
sudo apt install docker-compose -y ## docker-compose 설치
docker-compose up -d --build ## docker 실행
sudo usermod -aG docker $USER ##  Docker 권한 설정 (옵션)
newgrp docker

sudo apt install openjdk-17-jdk -y => 자바 설치
./gradlew clean build => gradle로 jar 빌드

 

디렉토리 설정

위의 명령어들을 입력해서 필요한 패키지들을 설치하였다면 app-backend-deploy.yml이 잘 작동할 수 있도록 디렉토리 구조를 맞춰줘야 한다 EC2에서 git clone을 하기 위해서는 깃허브 PAT(Personal Access Tokens)가 필요하다

깃허브 프로필 → settings → Developers settings → Tokens 클릭 후 Generate new token

mkdir ~/your-app
cd ~/your-app

git clone https://pooreumjung:PAT값@github.com/SSUketing/app-backend.git ## 깃 클론

 

 

✏️ 수동 배포 해보기

위의 단계까지 모두 설정 후 로컬에서 깃허브로 푸쉬하면 자동으로 배포를 할 수 있지만 수동 배포하는 방법도 첨부하면 좋을 것 같아서 추가로 작성한다.

# 1. EC2에 SSH 접속
ssh -i ~/.ssh/ssuketing.pem ubuntu@<EC2 퍼블릭 IP 또는 도메인>

# 2. 시스템 업데이트
sudo apt update
sudo apt upgrade -y

# 3. Docker & Docker Compose 설치 (처음 한 번만)
sudo apt install -y docker.io
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" \
  -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose --version

# 4. Docker 권한 설정 (옵션)
sudo usermod -aG docker $USER
newgrp docker

# 5. 배포 디렉토리 생성 및 Git clone
mkdir -p ~/app-backend
cd ~/app-backend
git clone https://github.com/리포지토리.git

# 6. .env 파일 생성
vi .env
# 또는 echo 방식
echo "MYSQL_USERNAME=pooreum" >> .env
# ... 필요한 환경변수 추가

# 7. Docker Compose로 빌드 및 실행
docker-compose down --volumes         # 기존 컨테이너 완전 정리 (선택)
docker image prune -a -f              # 캐시된 이미지 제거 (선택)
docker-compose build --no-cache       # 새로 빌드
docker-compose up -d                  # 백그라운드 실행

# 8. 컨테이너 상태 확인
docker ps

# 9. 로그 확인 (에러 디버깅 시)
docker logs -f <컨테이너 이름>

 

코드 업데이트시

cd ~/app-backend
git pull origin main
docker-compose down
docker-compose up --build -d

 

에러시 초기화

docker-compose down --volumes
docker image prune -a -f
docker-compose build --no-cache
docker-compose up -d
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/07   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함