본문 바로가기
AWS Cloud School 8기/CICD

[CI/CD] Github-action을 통한 CI/CD

by YUNZEE 2025. 5. 11.
728x90

🔧 CI/CD가 뭐야?

  • CI (지속적 통합, Continuous Integration)
    → 개발자가 코드를 푸시(push)할 때마다 자동으로 테스트하거나 빌드하는 것.
    • 어떤 테스트? 코드가 잘 작동하는지 자동으로 검증하는 것
    • 어떤 빌드? 소스 코드를 실제 실행 가능한 파일로 변환하는 것
    • 즉, 코드가 정상 동작한다고 확인
  • CD (지속적 배포, Continuous Deployment)
    → 코드가 문제 없다면 자동으로 서버나 클라우드에 배포하는 것.
    • 예를 들어 깃허브 액션을 사용해서 문제가 없다면 웹페이지에 실제 서비스에 반영되는 것을 배포라고 함
    • 즉, 확인된 코드를 웹서버, 클라우드, AWS EC2 등에 자동 반영하는 것을 말함

즉, 코드를 바꾸면 -> 자동으로 테스트하고 -> 자동으로 서버에 반영되는 시스템

🔗 GitHub Actions가 뭐야?

GitHub 자체에서 제공하는 자동화 시스템
우리는 이를 이용해서 아래처럼 설정할 수 있음:

 

GitHub Actions = CI + CD

CI: 코드 테스트와 빌드를 자동으로 진행하는 역할을 함. 

즉, 코드가 푸시되면 자동으로 테스트를 실행하고, 빌드를 돌려 문제가 없는지 확인함

 

CD: CI가 완료되면 자동으로 배포도 가능함. 예를 들어, 코드가 정상이라면, 자동으로 서버에 배포하여 실시간 서비스에 반영할 수 있음

 

즉, CI와 CD를 깃허브 액션으로 하나의 파이프라인에서 자동화할 수 있음.

하지만 여기서는 깃허브 액션을 CI 위주로 두고 실습을 할 예정임

 

누가 main 브랜치에 푸시하면 → 테스트하고 → 빌드하고 → 서버에 배포해 줘!

 

이걸 하기 위해서는 .github/workflows 폴더에 main.yml 같은 설정 파일을 만들면 됨!

여기서 중요한 건 파일을 저장할 때 꼭 yml이나 yaml파일로 저장해야 된다는 점!!

📁 폴더/파일 구조

/my-project
 ├── .github
 │    └── workflows
 │         └── main.yml   ← 여기에 CI/CD 설정!
 ├── src/
 └── Dockerfile

✅ 공통점 (GitHub Actions vs Jenkins)

🎯 자동화 도구 둘 다 "코드 변경 → 테스트/빌드/배포 자동 실행"을 해주는 CI/CD 도구
🛠️ 작업 구성 방식 둘 다 YAML 또는 스크립트 파일로 어떤 작업을 할지 정의함
⚙️ 플러그인/액션 존재 외부 도구(Git, Docker, AWS 등)와 연동할 수 있는 플러그인 또는 액션이 많음
⛓️ 빌드 파이프라인 구성 여러 작업(Job)을 순서대로 실행하는 파이프라인을 구성할 수 있음

✅ 차이점 (GitHub Actions vs Jenkins)

📦 설치 GitHub에 내장되어 있어, 따로 설치할 필요 없음 직접 서버에 설치해야 해 (EC2, PC 등)
🌐 연동 GitHub에서 코드 변경 감지가 자동 GitHub 연동은 별도 설정 필요 (Webhook 등)
🛠️ 유지관리 GitHub가 관리해줌 (서버 운영 불필요) 서버 직접 관리해야 함 (버전 업데이트, 백업 등)
🔌 확장성 GitHub Marketplace에 있는 Action 사용 Jenkins는 Plugin 사용 (더 다양하고 강력)
💰 비용 개인/소규모는 무료 (분당 제한 있음) Jenkins는 설치는 무료지만 서버 운영비가 듦
UI GitHub 웹 UI에서 쉽게 사용 가능 Jenkins는 전용 대시보드에서 관리함

 

플러그인(Plugin)이란?

- 기본 기능에 새로운 기능을 덧붙일 수 있는 확장 프로그램임

- 컴퓨터나 핸드폰 앱에 추가 기능을 붙이는 도구라고 보면 됨

 

GitHub Actions에서의 “Action”이란?

- CI/CD 자동화 작업을 구성하는 작은 명령 블록임

- 자동화 파이프라인 안에서 할 일 하나하나를 대신 수행해 주는 코드 조각이라고 보면 됨

더보기

EKS 클러스터 구성

LB controller도 설치

 

사용자이름 : user

암호 : test1234

암호화 x

초기 DB이름 : db

퍼블릭액세스 : 예

되도록이면 같은 VPC에 서브넷은 퍼블릭서브넷

 

보안그룹 source 0.0.0.0/0 포트 3306

 

pri에 있는 db를  강제로 pub으로 바꿔주기

https://github.com/oolralra/svelte-fast

- svelte - fastapi로 만든 앱.

- 이걸 fork 해서 가져오기

git clone <본인레포>/svelte-fast

- frontend 빼고 나머지 다 백엔드 관련 파일

 

apt install mysql-client-core-8.0

- mysql 명령어 설치

 

mysql -u user -ptest1234 -h <본인 RDS주소> 

- 본인 RDS로 수정

그 앞에 user는 아이디

test1234는 비번임

- 접속되는지 확인

 

docker build -t backend:1 .

# 백엔드 앱 이미지빌드

 

docker run -dp 8000:8000 --name back backend:1

# 컨테이너 실행

- 필요하다면 자기 vm ip 넣어주기 

root@aws-cli:~/svel-fast/frontend# docker build -t frontend:1 .

- 프론트엔드 앱 이미지빌드

 

root@aws-cli:~/svel-fast/frontend# docker run -dp 80:5173 --name front frontend:1

실습) ECR을 사용하여  이런 형태로 만들어서 NLB로 접속 가능하게 만들어보세요!

더보기

ECR 리포지토리 생성

aws ecr create-repository --repository-name web-front
aws ecr create-repository --repository-name web-back

 

로그인 (Amazon ECR 인증)

aws ecr get-login-password | docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com

 태그 변경

docker tag frontend:1 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/web-front:1
docker tag backend:1 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/web-back:1

 푸시

docker push 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/web-front:1
docker push 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/web-back:1

 

frontend Deployment/Service (ECR 이미지 사용) (type: LoadBalancer → NLB 생성)

apiVersion: v1
kind: Service
metadata:
  name: svc-front
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
spec:
  type: LoadBalancer
  selector:
    app: front
  ports:
  - port: 80
    targetPort: 5173
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-front
spec:
  replicas: 1
  selector:
    matchLabels:
      app: front
  template:
    metadata:
      labels:
        app: front
    spec:
      containers:
      - name: front
        image: 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/web-front:1
        ports:
        - containerPort: 5173

 

backend Deployment/Service (ECR 이미지 사용) (type: LoadBalancer → NLB 생성)

apiVersion: v1
kind: Service
metadata:
  name: svc-backend
spec:
  selector:
    app: back
  ports:
  - port: 8000
    targetPort: 8000
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-backend
spec:
  replicas: 1
  selector:
    matchLabels:
      app: back
  template:
    metadata:
      labels:
       app: back
    spec:
      containers:
      - name: back
        image: 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/web-back:1
        ports:
        - containerPort: 8000

- 근데 앞에 실습을 하면 백엔드 연결이 안 되는 걸 알 수 있음

- 백엔드는 프라이빗한 곳에 있기 때문에 브라우저 입장에서는 찾아갈 수 없었다.

=> 해결법은 두 가지

 

1. 프론트를 띄운 nodejs앱에서 리버스프록시 코드를 추가

2. npm build를 통해 정적파일을 생성 후 nginx를 통해 리버스프록시.

앱 업데이트(CI 관점)

CI

1. 프로그래밍 언어로 짜인 코드를 수정

2. 새로 앱빌드

3. 새로 컨테이너이미지 빌드 = 이미지의 태그가 변경이 됐다는 뜻함.

4. 그렇다면 이미지의 태그가 변경이 됐다는 사실을 어디에 명시? => 매니페스트 파일에 명시

 

CD

5. argoCD가 이를 인지해서 자동으로 apply를 해준다.

더보기

root@aws-cli:~/svel-fast# cp -r frontend/ ~/frontend

# frontend 앱이 보이는 경로에서 디렉토리를 복사.

root@aws-cli:~/frontend# git init

 - 여기는 깃허브의 로컬이 됨

git remote add origin https://github.com/YUMZII/src

 - 아직 원격레포를 생성하진 않았지만 앞으로 만들 예정.

- 동기화된 상태는 아님

 

깃허브로 가서 레포지토리 만들기

- 깃허브에 레포만 생성하기

 

root@aws-cli:~/frontend# git add .

# 모든 변경사항 추가.

 

root@aws-cli:~/frontend# git commit -m "first"

- 아마 정보가 등록이 안되어 있을 것임

- 깃허브 아이디랑 이메일을 넣으면 됨

 

깃허브 토큰 넣어주기

aws ecr create-repository --repository-name frontend --region ap-northeast-2

- ECR 레포 생성

 

export ACCOUNT_ID= 922805825674
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin $ACCOUNT_ID.dkr.ecr.ap-northeast-2.amazonaws.com

 - ECR 로그인

 

export FRONT=$ACCOUNT_ID.dkr.ecr.ap-northeast-2.amazonaws.com/frontend​

- FRONT라는 이름으로 환경변수 처리.

 

docker build -t $FRONT:3 .

- 이렇게 사용가능

 kubectl delete deploy,svc --all

- 기존 리소스 삭제 

 

앱 배포를 위한 매니페스트

새로운 세션 복제해서

root@aws-cli:~# pwd

/root

root@aws-cli:~# mkdir deploy

root@aws-cli:~# cd deploy

 

root@aws-cli:~/deploy# vi deploy.yml

apiVersion: v1
kind: Service
metadata:
  name: svc-front
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
  type: LoadBalancer
  selector:
    app: front
  ports:
  - port: 80
    targetPort: 5173
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-front
spec:
  replicas: 1
  selector:
    matchLabels:
      app: front
  template:
    metadata:
      name: pod-front
      labels:
        app: front
    spec:
      containers:
      - name: con-front
        image: <내어카운트ID>.dkr.ecr.ap-northeast-2.amazonaws.com/frontend:1

 

root@aws-cli:~/frontend# echo $FRONT
922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/frontend

 - frontend 경로로 돌아와서

 

root@aws-cli:~/frontend# docker build -t $FRONT:1 .

root@aws-cli:~/frontend# docker push $FRONT:1

 

- 프론트만 잘 뜨는지 확인

 

그리고 깃허브에 src레포에 잘 생성된 걸 확인할 수 있음

root@aws-cli:~/deploy# cat deploy.yml 

- 여기 경로에 있는 내용 복사해서

- 깃허브에 있던 dep디렉토리에 있던 deployment.yml파일 수정해주기

- 나중에 argoCD가 모니터링할 레포

apiVersion: v1
kind: Service
metadata:
  name: svc-front
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
spec:
  type: LoadBalancer
  selector:
    app: front
  ports:
  - port: 80
    targetPort: 5173
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dep-front
spec:
  replicas: 1
  selector:
    matchLabels:
      app: front
  template:
    metadata:
      name: pod-front
      labels:
        app: front
    spec:
      containers:
      - name: con-front
        image: 922805825674.dkr.ecr.ap-northeast-2.amazonaws.com/frontend:1

Github-Action

- 위 과정(CI, Continuous Integration)을 깃허브 액션을 통해 정의할 예정

더보기

- 패키징하고 통합하는 느낌

 - 내가 직접 스크립트를 정의하겠다는 의미

name: fast CI/CD #이름

on: # on 언제 action을 동작시킬지
  push: # push가 됐을때 동작시키겠다.
    branches: [main] # 대상 브랜치.
    paths-ignore: # push 이벤트가 발생해도 무시할 파일.
    - '.gitignore'
    - '.dockerignore'

- 메인브랜치에 push가 됐을 때 트리거가 될 예정

- 예외 할 디렉토리나 파일도 존재함

jobs:
  ci:
    runs-on: ubuntu-latest  #깃액션이 동작하는 환경
    steps:
      - uses: actions/checkout@v4 #깃액션을 할 때 어떤걸 사용할지 모듈 형태로 만들어둔 걸 가져와서 사용
        with:
          fetch-depth: 1 #가장 최근 commit의 히스토리만 가져옴.

- checkout: 클론 한다는 의미임

- 다음 절차는 ECR에 push를 하기 위한 계정을 등록할 건데, ECR에 접근할 수 있는 최소한의 권한만 갖고 있는 계정을 생성해서 등록하는 게 좋음

- 절대 administrator 권한은 주면 안 됨

이 친구 추가해 주기

Access key ID,Secret access key
AKIA.... AWS_ACCESS_KEY_ID
GhNg3......AWS_SECRET_ACCESS_KEY

- 깃허브 토큰 것도 만들어주기 

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2

- aws configure와 비슷함

- 위에서 구성한 시크릿들은 secrets에 저장이 되어있음

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

- ECR에 로그인하기 위한 액션

      - name: Set Variables
        id: set-var
        run: |
          echo "ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }}" >> $GITHUB_ENV
          echo "ECR_REPOSITORY=frontend" >> $GITHUB_ENV #본인 ecr레포에 맞게 수정 frontend
          echo "IMAGE_TAG=${{ github.run_number }}" >> $GITHUB_ENV -> 두번 액션 했을 경우 2로 됨
          echo "GIT_EMAIL=rlarn2003@naver.com" >> $GITHUB_ENV
          echo "GIT_NAME=YUMZII" >> $GITHUB_ENV

- 환경변수 선언 => GITHUB_ENV라는 파일에 저장해 놨다고 생각해도 좋음

-> 나중에 env로 호출 가능

 

- ${{ github.run_number }}= 깃허브액션에서 제공하는 액션넘버. CI/CD의 흐름을 이해하는 데 있어서 제일 중요한 포인트. "그래서 이걸 왜 바꿔줘야 하는데?"

-> ArgoCD에서 매니페스트 파일의 변경사항을 인지해야 하니까

      - name: Docker Image Build
        id: build-image
        run: |
          docker build -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} .

 docker build -t <레지스트리주소>/<레포이름>:<태그>

- name: Docker image Push
        id: push-image
        run: |
          docker push ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}

- ECR에 push

매니페스트 파일을 수정

매니페스트 파일이란?

주로 설정 파일을 말하는데, 프로그램이나 시스템이 실행될 때 필요한 설정과 구체적인 정보를 담고 있는 파일을 의미함.

이를 통해 프로그램이나 서비스가 어떻게 실행되고 동작할지를 정의할 수 있음.

 

깃허브 소스레포 = oolralra/src

깃허브 매니페스트레포 = oolralra/dep

 

변경이 된다는 것만 인지

 

oolralra/dep 에 있는 매니페스트를 clone 한 후, sed로 이미지 태그를 변경 후  push

더보기
     - name: Checkout Deployment Repository
        uses: actions/checkout@v4
        with:
          repository: YUMZII/dep #본인에 맞게 수정
          ref: main  # branch
          token: ${{ secrets.GH_TOKEN }}

- 매니페스트 레포로 변경

      - name: k8s manifest update
        run: |
          sed -i "s@ \
          image: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:.*@ \
          image: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}@g" deploy.yml

- 리눅스 환경에서는 구분자를 '/'로 해서 s/<바뀌기 전>/<바뀐 후>/g  이렇게 썼지만. 컨테이너 레지스트리와 레포를 구분하는 용도로 '/'가 쓰이기 때문에 구분자를 @로 했음

      - name: Commit and Push
        run: |
          git config user.email ${{ env.GIT_EMAIL }}
          git config user.name ${{ env.GIT_NAME }}
          git add deploy.yml
          git commit -m "Update image tag"
          git push origin main

- git push 

name: fast CI/CD #이름

on: # on 언제 action을 동작시킬지
  push: # push가 됐을때 동작시키겠다.
    branches: [main] # 대상 브랜치.
    paths-ignore: # push 이벤트가 발생해도 무시할 파일.
    - '.gitignore'
    - '.dockerignore'

jobs:
  ci:
    runs-on: ubuntu-latest  #깃액션이 동작하는 환경
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 1 #가장 최근 commit의 히스토리만 가져옴.
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-2
      - name: Login to Amazon ECR
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2
      - name: Set Variables
        id: set-var
        run: |
          echo "ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }}" >> $GITHUB_ENV
          echo "ECR_REPOSITORY=frontend" >> $GITHUB_ENV #본인 ecr레포에 맞게 수정
          echo "IMAGE_TAG=${{ github.run_number }}" >> $GITHUB_ENV
          echo "GIT_EMAIL=rlarn2003@naver.com" >> $GITHUB_ENV
          echo "GIT_NAME=YUMZII" >> $GITHUB_ENV          
      - name: Docker Image Build
        id: build-image
        run: |
          docker build -t ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }} .
      - name: Docker image Push
        id: push-image
        run: |
          docker push ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}
      - name: Checkout Deployment Repository
        uses: actions/checkout@v4
        with:
          repository: YUMZII/dep #본인에 맞게 수정
          ref: main  # branch
          token: ${{ secrets.GH_TOKEN }}
      - name: k8s manifest update
        run: |
          sed -i "s@ \
          image: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:.*@ \
          image: ${{ env.ECR_REGISTRY }}/${{ env.ECR_REPOSITORY }}:${{ env.IMAGE_TAG }}@g" deploy.yml
      - name: Commit and Push
        run: |
          git config user.email ${{ env.GIT_EMAIL }}
          git config user.name ${{ env.GIT_NAME }}
          git add deploy.yml
          git commit -m "Update image tag"
          git push origin main

 

728x90