๐Ÿณ ์ธํ”„๋ผ/Cloud & CI-CD

EC2, ECR, Docker Compose, Github Actions ์‚ฌ์šฉํ•˜์—ฌ SpringBoot, MongoDB ๋ฐฐํฌํ•˜๊ธฐ

๊ณ„๋ž€์†Œ๋…„ 2024. 10. 1. 23:30
  • ์บก์Šคํ†ค์—์„œ SpringBoot, MongoDB๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋ฐฑ์•ค๋“œ ์„œ๋ฒ„ ๊ฐœ๋ฐœ์„ ํ•ด์•ผํ•œ๋‹ค.
  • ๋‚ด๊ฐ€ ์ธํ”„๋ผ๋ฅผ ๋งก๊ธฐ๋กœ ํ•ด์„œ CI/CD๋ฐ ์ธํ”„๋ผ๋ฅผ ์™„๋ฃŒํ•˜์˜€๋‹ค.
  • ๊ธฐ๋กํ• ๊ฒธ ๋ธ”๋กœ๊ทธ์— ์ •๋ฆฌํ•ด๋ณด์•˜๋‹ค.

EC2๋Š” MongoDB,  SpringBoot ์‚ฌ์šฉํ•  ๊ฒƒ์ด๊ธฐ์—, ์ถ”๊ฐ€๋กœ 8080ํฌํŠธ์™€ 27017 ํฌํŠธ๋ฅผ ์—ด์–ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋ฆฌ๊ณ  t2.micro๋ง๊ณ  t3a.small์„ ์‚ฌ์šฉํ•˜์˜€๋‹ค. t2.micro๋Š” ๋„ˆ๋ฌด ์ž‘๋‹ค...

https://start.spring.io/

  • ์‹คํ–‰ ์ž˜ ๋œ๋‹ค.
  • ์ด์ œ ๊นƒํ—ˆ๋ธŒ์— ์—ฐ๊ฒฐํ•ด์ฃผ์ž

๋ ˆํฌ์ง€ํ† ๋ฆฌ ์ƒ์„ฑํ•˜๊ณ 

git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:seongjju/seongjju-web-server.git
git push -u origin main

ec2์— ์ ‘์†ํ•ด์„œ

$ sudo apt update && /
sudo apt install openjdk-17-jdk -y
$ java -version # ์ž˜ ์„ค์น˜๋๋Š” ์ง€ ํ™•์ธ
$ git clone {git repository clone ์ฃผ์†Œ}

ํ† ๊ทผ ๋ฐœ๊ธ‰ ์ฐธ๊ณ 

https://velog.io/@nara7875/github-%ED%86%A0%ED%81%B0-%EB%B0%9C%EA%B8%89%ED%95%98%EA%B8%B0

 

github ํ† ํฐ ๋ฐœ๊ธ‰ํ•˜๊ธฐ

์œ„์™€ ๊ฐ™์ด private์œผ๋กœ ๋œ repository๋ฅผ ๋‹ค์šด๋ฐ›์œผ๋ ค๊ณ  ํ•˜๋ฉด github์˜ ์•„์ด๋””์™€ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๋ผ๊ณ  ํ•œ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ด๊ฑธ ๋งจ๋‚  private์—์„œ public์œผ๋กœ ๋ฐ”๊ฟ”์„œ ํ•˜๊ธฐ์—” ๋„ˆ๋ฌด ๊ท€์ฐฎ๋‹ค. ์‹ค์ œ๋กœ๋„ ๊ทธ๋ ‡๊ฒŒ ์–ด๋ ต์ง€

velog.io

 

  • ๋งค๋ฒˆ Github ๊ณ„์ •๊ณผ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์น˜๋Š” ๊ณผ์ • ์—†์• ๊ธฐ
$ git config --global credential.helper store
$ git pull origin main
# Github ๊ณ„์ • ๋ฐ ๋น„๋ฐ€๋ฒˆํ˜ธ ์ž…๋ ฅ
$ git pull origin main # ๋” ์ด์ƒ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์•ˆ ๋ฌป๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

  • ๊นƒํ—ˆ๋ธŒ์— ์‹œํฌ๋ฆฟ ๊ฐ’ ๋„ฃ์–ด์ฃผ๊ธฐ

  • Settings -> Secrets and variables -> Actions
  • EC2_USERNAME: ubuntu
  • EC2_HOST:์›๊ฒฉ ip ๋‚˜๋Š” 52.79.159.70
  • EC2_PRIVATE_KEY: ์ด๊ฑฐ๋Š” ์•„๊นŒ ec2๋งŒ๋“ค๋•Œ pem๊ฐ’์ด๋‹ค. ๋‹ค์šด๋ฐ›์€ pemํ‚ค๊ฐ€ ์žˆ๋Š” ํด๋”๋กœ ์ด๋™ํ•ด์„œ cat์œผ๋กœ ํ™•์ธํ•˜์ž

  • ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ-----END RSA PRIVATE KEY----- ๊นŒ์ง€ ๋„ฃ์œผ๋ฉด ๋œ๋‹ค. ๋งˆ์ง€๋ง‰ “%”๋Š” ๋„ฃ์œผ๋ฉด ์•ˆ๋œ๋‹ค.

 

  • APPLICATION_PROPERTIES ์ด๊ฑฐ๋Š” ymlํŒŒ์ผ๊ฐ’์„ ๋„ฃ์„๊ฒƒ์ด๋‹ค.
  • ์™œ๋ƒํ•˜๋ฉด ๋ณด์•ˆ์„ ์œ„ํ•ด์„œ ๊นƒํ—ˆ๋ธŒ์—๋Š” ymlํŒŒ์ผ ๊ฐ’์ด ์˜ฌ๋ผ๊ฐ€๋ฉด ์•ˆ๋œ๋‹ค.
  • application.yml์„ .gitignore์— ์ถ”๊ฐ€ํ•˜๊ณ  ์˜ฌ๋ ค์•ผํ•œ๋‹ค.

 

  • ํ˜น์‹œ ์ด๋ฏธ ๊นƒํ—ˆ๋ธŒ์— ymlํŒŒ์ผ์„ ์˜ฌ๋ ธ๋‹ค๋ฉด
  • git rm --cached src/main/resources/application.yml๋กœ ์บ์‹œ๋ฅผ ์ง€์šฐ๊ณ  ์˜ฌ๋ฆฌ์ž
spring:
  data:
    mongodb:
      uri: mongodb://mongo:27017/seongjju-web-server-database

 

  • ์ด์ œ ๋„์ปค ๋ถ€๋ถ„ํ•ด๋ณด์ž

ec2์—์„œ

sudo apt-get update && \ sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common && \ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \ sudo apt-key fingerprint 0EBFCD88 && \ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \ sudo apt-get update && \ sudo apt-get install -y docker-ce && \ sudo usermod -aG docker ubuntu && \ sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \ sudo chmod +x /usr/local/bin/docker-compose && \ sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

๋„์ปค ์„ค์น˜ํ•˜๊ณ  ์—”ํ„ฐํ•œ๋ฒˆ๋ˆ„๋ฅด๊ณ 

# ์ž˜ ์„ค์น˜๋๋Š” ์ง€ ํ™•์ธ 
$ docker -v # Docker ๋ฒ„์ „ ํ™•์ธ 
$ docker compose version # Docker Compose ๋ฒ„์ „ ํ™•์ธ

์ž˜ ์„ค์น˜ ๋˜์—ˆ๋‹ค.

  • ECR์ƒ์„ฑ

๋‹ค๋ฅธ๊ฑด ๊ฑด๋“ค๊ฒŒ ์—†๋‹ค.

  • EC2๊ฐ€ Private ECR์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ์…‹ํŒ…ํ•˜๊ธฐ
# Ubuntu์ผ ๊ฒฝ์šฐ
$ sudo apt update
$ sudo apt install amazon-ecr-credential-helper

~ ๊ฒฝ๋กœ์—์„œ .docker๋ผ๋Š” ํด๋” ๋งŒ๋“ค๊ณ , config.json ํŒŒ์ผ ๋งŒ๋“ค์–ด์„œ ์œ„์™€ ๊ฐ™์ด ์ž‘์„ฑํ•ด๋ผ.
~/.docker/config.json

{
	"credsStore": "ecr-login"
}

 

  • Github Actions์˜ IAM์— ๊ถŒํ•œ ์ถ”๊ฐ€
  • iam ์‚ฌ์šฉ์ž์™€ ์—ญํ•  ๋ชจ๋‘์— ์•„๋ž˜์˜ ์ •์ฑ…์„ ์—ฐ๊ฒฐํ•ด์•ผํ•œ๋‹ค.
  • AmazonEC2ContainerRegistryFullAccess

  • ์ด ํ‚ค๋ฅผ ์•„๊นŒ ์‹œํฌ๋ฆฟ ๊ฐ’ ๋„ฃ์€๊ฒƒ์ฒ˜๋Ÿผ ๊นƒํ—ˆ๋ธŒ ์‹œํฌ๋ฆฟ ๊ฐ’์— ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.
  • AWS_ACCESS_KEY_ID: ์•ก์„ธ์Šค ํ‚ค
  • AWS_SECRET_ACCESS_KEY: ๋น„๋ฐ€ ์•ก์„ธ์Šค ํ‚ค

 

  • IAM ์—ญํ• 

  • ์ƒ์„ฑ์™„๋ฃŒ
  • ์ด์ œ ec2์™€ iam ์—ฐ๊ฒฐ -> ์ด๊ฑฐ ๊นŒ๋จน์–ด์„œ ๋ช‡๋ฒˆ์ด๋‚˜ credentials ์˜ค๋ฅ˜๋‚ฌ๋‹ค
  • EC2 -> ์ž‘์—… -> ๋ณด์•ˆ ->  Iam ์—ญํ•  ์ˆ˜์ • 
  • ๋ฐฉ๊ธˆ ๋งŒ๋“  ์—ญํ•  ์„ ํƒํ•˜๋ฉด ๋œ๋‹ค.

 

๊ฒฝ๋กœ ์ฐธ๊ณ  

 

.github/workflows/deploy.yml

name: Deploy To EC2

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Github Repository ํŒŒ์ผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
        uses: actions/checkout@v4

      - name: JDK 17๋ฒ„์ „ ์„ค์น˜
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 17

      - name: application.yml ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ
        run: echo "${{ secrets.APPLICATION_PROPERTIES }}" > ./src/main/resources/application.yml

      - name: ํ…Œ์ŠคํŠธ ๋ฐ ๋นŒ๋“œํ•˜๊ธฐ
        run: ./gradlew clean build

      - name: AWS Resource์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ AWS credentials ์„ค์ •
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-2
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: ECR์— ๋กœ๊ทธ์ธํ•˜๊ธฐ
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Docker ์ด๋ฏธ์ง€ ์ƒ์„ฑ
        run: docker build -t seongjju-web-server .

      - name: Docker ์ด๋ฏธ์ง€์— Tag ๋ถ™์ด๊ธฐ
        run: docker tag seongjju-web-server ${{ steps.login-ecr.outputs.registry }}/seongjju-web-server:latest

      - name: ECR์— Docker ์ด๋ฏธ์ง€ Pushํ•˜๊ธฐ
        run: docker push ${{ steps.login-ecr.outputs.registry }}/seongjju-web-server:latest

      - name: SCP๋กœ EC2์— docker-compose.yml ํŒŒ์ผ ์ „์†ก
        run: |
          echo "${{ secrets.EC2_PRIVATE_KEY }}" > private_key.pem
          chmod 600 private_key.pem
          scp -o StrictHostKeyChecking=no -i private_key.pem ./docker-compose.yml ${{ secrets.EC2_USERNAME }}@${{ secrets.EC2_HOST }}:/home/ubuntu/seongjju-web-server/

      - name: SSH๋กœ EC2์— ์ ‘์†ํ•˜๊ธฐ
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_PRIVATE_KEY }}
          script_stop: true
          script: |
            cd /home/ubuntu/seongjju-web-server  # ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ฒฝ๋กœ๋กœ ์ด๋™
            export ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }}  # ECR_REGISTRY ํ™˜๊ฒฝ ๋ณ€์ˆ˜ 
            docker-compose down  # ๊ธฐ์กด ์ปจํ…Œ์ด๋„ˆ ์ค‘์ง€
            # 'latest' ํƒœ๊ทธ๊ฐ€ ๋ถ™์€ ์ด๋ฏธ์ง€๋ฅผ ์ตœ์‹  ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ณ , ์ฒซ ๋‘ ๊ฐœ๋ฅผ ์ œ์™ธํ•œ ๋‚˜๋จธ์ง€ ์‚ญ์ œ
            docker images --filter "reference=*:*latest" --format '{{.Repository}}:{{.Tag}}' | sort -r | awk 'NR > 2' | xargs -r docker rmi
            docker-compose pull  # ECR์—์„œ ์ตœ์‹  ์ด๋ฏธ์ง€ ๊ฐ€์ ธ์˜ค๊ธฐ
            docker-compose up -d  # Docker Compose ์‹คํ–‰

 

Dockerfile

FROM eclipse-temurin:17-jdk-alpine
COPY ./build/libs/*SNAPSHOT.jar project.jar
ENTRYPOINT ["java", "-jar", "project.jar"]

 

docker-compose.yml

version: '3.3'
services:
  spring:
    image: ${ECR_REGISTRY}/seongjju-web-server:latest  # ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉ
    container_name: seongjju-web-server
    ports:
      - "8080:8080"
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - MONGO_URI=mongodb://mongo:27017/seongjju-web-server-database  # MongoDB ์—ฐ๊ฒฐ
    depends_on:
      - mongo  # mongo ์„œ๋น„์Šค๊ฐ€ ๋จผ์ € ์‹œ์ž‘๋จ
    networks:
      - app-network

  mongo:
    image: mongo:latest
    container_name: mongodb
    ports:
      - "27017:27017"
    volumes:
      - mongo-data:/data/db
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  mongo-data:
    driver: local

 

 

์ด์ œ ๊นƒํ—ˆ๋ธŒ์— ์˜ฌ๋ ค๋ณด์ž ์ž˜๋Œ์•„๊ฐ€๋‚˜~

git add .
git commit -m "cicd-test"
git push origin main

  • ๊นƒํ—ˆ๋ธŒ์•ก์…˜์ด ํŠธ๋ฆฌ๊ฑฐ ๋˜์–ด์„œ ๋Œ์•„๊ฐ€๊ณ  ์žˆ๋‹ค. ์ž˜ ๋˜๊ธธ ๊ธฐ๋„ํ•˜์ž
  • ์˜คํƒ€ํ•˜๋‚˜, ์—ฐ๊ฒฐํ•˜๋‚˜ ์•ˆํ•˜๋ฉด ์–ด๋”˜๊ฐ€์— ๋ฐ”๋กœ ๋นจ๊ฐ„ํ‘œ์‹œ๊ฐ€ ๋œฐ๊ฒƒ์ด๋‹ค.

  • ๋‚ด ์ฒซ ์˜ค๋ฅ˜๋Š” ๋‚ด๊ฐ€ iam ์‚ฌ์šฉ์ž์— AmazonEC2ContainerRegistryFullAccess ์ด ๊ถŒํ•œ์„ ๋ˆ„๋ฝํ•ด์„œ ๋ฐœ์ƒํ•œ ์˜ค๋ฅ˜์ด๋‹ค.
  • ์ˆ˜์ •ํ•˜๊ณ  ๋‹ค์‹œ ๊นƒํ—ˆ๋ธŒ์•ก์…˜ ์‹คํ–‰์‹œ์ผœ๋ณด์ž

 

  • AWS CLI ์„ค์น˜
$ sudo apt install unzip
$ curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
$ unzip awscliv2.zip
$ sudo ./aws/install
$ aws --version # ์ž˜ ์ถœ๋ ฅ๋œ๋‹ค๋ฉด ์ •์ƒ ์„ค์น˜๋œ ์ƒํƒœ

  • AWS CLI๋กœ ์•ก์„ธ์Šค ํ‚ค ๋“ฑ๋กํ•˜๊ธฐ
$ aws configure
AWS Access Key ID [None]: <์œ„์—์„œ ๋ฐœ๊ธ‰ํ•œ ์•ก์ƒˆ์Šคํ‚ค>
AWS Secret Access Key [None]: <์œ„์—์„œ ๋ฐœ๊ธ‰ํ•œ ๋น„๋ฐ€์•ก์ƒˆ์Šคํ‚ค>
Default region name [None]: ap-northeast-2
Default output format [None]: ์—”ํ„ฐ์น˜๋ฉด๋œ๋‹ค.
  • ์ค‘๊ฐ„์— ๋ญ”๊ฐ€ ์•ˆ๋œ๋‹ค๋ฉด ec2๋ฅผ ์žฌ๋ถ€ํŒ… ์‹œ์ผœ๋ณด์ž

  • ๋ชจ๋“  job ์„ฑ๊ณต~

 

ํ˜น์‹œ ์•ˆ ๋˜๋ฉด ์ผ์‹œ์  ๋กœ๊ทธ์ธํ•˜๊ณ  ํ‚ค๋“ค์„ ํ™•์ธํ•ด๋ณด์ž

aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 811271461583.dkr.ecr.ap-northeast-2.amazonaws.com

 

๊ทธ๋ฆฌ๊ณ  lsํ•ด์„œ config.json๋งŒ ๋‚จ๋„๋ก ๋‹ค๋ฅธ๊ฑฐ ์ง€์›Œ์ฃผ์ž 

  • User, UserRepository, UserController๋ฅผ ๋งŒ๋“ค๊ณ  
  • ํฌ์ŠคํŠธ๋งจ์œผ๋กœ ๋ฐ์ดํ„ฐ ํ•˜๋‚˜ ๋„ฃ์–ด๋ดค๋‹ค.

์ฒ˜์Œ์—๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์—†์—ˆ๋Š”๋ฐ, ๊นƒํ—ˆ๋ธŒ์•ก์…˜์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ณ  ํ™•์ธํ•˜๋‹ˆ ์ €๋ ‡๊ฒŒ seongjju-web-server-database๊ฐ€ ์ƒ๊ฒผ๊ณ , json์ด ์ •์ƒ์ ์œผ๋กœ ์ถœ๋ ฅ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

์ด๋ ‡๊ฒŒ EC2, ECR, Docker Compose, Github Actions ์‚ฌ์šฉํ•˜์—ฌ SpringBoot, MongoDB ๋ฐฐํฌ๋ฅผ ์™„๋ฃŒํ•˜์˜€๋‹ค.