[TIL] Github Action으로 아주 간단한 서비스 CI/CD 구축하기

박창조·2025년 7월 29일

TIL

목록 보기
4/6

이 글에서는 작은 서비스를 Github Action, GHCR을 사용한 간단한 CI/CD 를 구성하는 내용을 담고 있습니다.

📌 Intro

요즘 계속 인프라에 대한 간단한 작업들을 하는 것에 관심이 가고 있다. 그래서 기존에 만들어두고 거의 사용하지 않고 있던 Oracle 서버를 통해 무엇을 할 수 있을까? 고민을 하게 되었다.

그래서 먼저 내가 지금 가지고 있는 불편함이 무엇인지 먼저 찾아보기로 했다.

⚠️ 문제 : 포트폴리오에 첨부할 들어갈 링크가 너무 길다..

요즘 가장 큰 관심사는 당연 포트폴리오를 만드는 것이다. 프로젝트 관련 내용을 정리하면서 Github 나 Figma 등 링크들을 첨부하려고 했다.

포트폴리오를 만들면서 가장 많이 고민이 되었던 부분은 “가독성” 이었다. 사용성과 사용자 경험에 대해 항상 고민을 했기 때문에 “어떻게하면 내용을 잘 전달할 수 있을까? 가 큰 고민이었다.

첨부하는 링크들 중 많은 구현 화면을 보여주기 위해 Figma 링크를 첨부하려고 했는데, 다음과 같이 첨부 링크가 너무 길게 들어간다는 문제가 있었다.

🚩 목표 : URL을 나의 정체성을 담은 도메인 주소로 만들어보자

그래서 이런 고민을 시작했다.

“링크를 어떻게 하면 짧게 줄일 수 있을까? ”

bit.ly 와 같은 변환해주는 사이트(부분 무료)를 이용하는 방법도 있었지만, Domain에 “나의 정체성” 을 담는 것도 면접관님께 어필 할 수 있는 하나의 방법이라는 생각이 들었다.

그래서 내가 가지고 있는 Oracle 서버를 통해 “포트폴리오 링크들을 Redirect 하는 간단한 서비스”를 만들어보기로 했다.


📌 구현 : Nginx 하나로 원하는 기능을 딸깍!

링크를 Redirect 하는 방법을 어떻게 구현하지 정해야했다. 이 기능을 구현하는 생각하는 순간부터 정말 많은 구현 방법이 떠올랐다. React 나 Next.js, 간단한 Node.js, Spring Boot 등 사용할 수 있는 도구는 아주 아주 많이 있었다. 그래서 어떤 방식을 사용할지 딱 정할 기준이 필요했다.

보유하고 있는 서버의 한계

Oracle 서버에서 가지고 있는 인스턴스는 평생 무료 라는 이야기에 넘어가 만들어놓은 상태였다.

A1 코어 24GB 메모리 서버를 사용…했으면 아주 행복했겠지만… 😢 아쉽게도 1GB 메모리 서버를 이용하고 있었다.

가능하다면 해당 서버로 나의 블로그 같은 서비스 함께 올리는 것으로 확장하여 사용하고 싶었지만, 물리적인 한계로 인해 그러지는 못할 것 같았다.

그러기 때문에 “링크 변환” 이라는 작은 기능을 돌리기에는 무거운 프레임워크까지 사용하는 것은 과하다고 판단했다.

내가 결정한 방법 “서버에서 단순 301 Redirect “

가장 비용이 적게 드는 방법으로 “단순 Redirect” 를 선택했다. 서버에서 “딸깍” 처리만 해주면 되기 때문에 아주 쉬운 방법이었다.

사용할 서버로는 가장 많이 다뤄보아서 익숙한 Nginx 를 사용하기로 했다.

구현 내용

  1. YAML 파일로 Redirect 링크들을 관리
  2. Node.js 를 통해 YAML 파일을 읽어와 Nginx에 적용할 conf 파일 만들기
  3. 만든 conf 파일을 사용해 Nginx 실행하기.

*구현 내용을 모두 작성하기에는 블로그의 공간이 부족해 적지 못했다… - 박 페르마(?) -

아주 간단하기에 구현 내용은 아래 Github 링크에 들어가서 확인바랍니다. 😉

https://link.scent-jo.me/portfolio-redirects/github


📌 CI/CD 를 구성해보자

모든 구현을 마쳤으니 이제 CI/CD 를 구성해보자. 이것의 목표를 간단하게 한문장으로 정의하면 다음과 같다.

“링크 데이터 (YAML 파일)을 수정할 때마다 서버에 자동으로 반영되도록 한다.”


CI (Continuous Integration)

CIContinuous Integration 의 약자로 직역하면 “지속적인 통합” 이라고 할 수 있다.

여기서 말하는 “지속적인 통합” 은 서비스에 새로운 변경 사항이 생겼을 때, 정기적으로 빌드 및 테스트 되어 Repository에 통합하는 것을 의미한다.

즉, 테스트와 빌드를 통해 서비스가 잘 동작하는지를 확인 후 병합하는 과정의 자동화라고 생각하면 된다.

이 과정은 혼자 개발할 때보다 다수의 개발자가 함께 개발할 때 큰 의미를 가지게 된다.

CD (Continuous Delivery | Continuous Deployment )

CD 는 Continuous Delivery 혹은 Continuous Deployment 용어의 축약어 입니다. 해석하면 “지속적인 서비스 제공, “지속적인 배포” 라고 한다.

CI를 통해 서비스가 Repository 에 잘 통합 되도록 하였다면, CD 는 Repository를 넘어서 Production 환경까지 Release 되는 것을 의미한다.

간단하게 말해, “실제로 운영되는 환경에 자동으로 반영되게 한다!” 라고 생각하면 좋다.

이 글에서는 CI/CD에 대해 심플하게만 설명하고 넘어간다.


📌 CI/CD 를 설계해보자.

예전 SSAFY 에서 경험해본 방식은 Jenkins를 통해 CI/CD 파이프라인을 작성하는 것이었다.

하지만, Jenkins를 구동할 서버가 필요하다는 점에서 과하다는 생각이 들어, 더 쉬운 선택지인 Github Action을 사용하기로 했다.

사용한 기술

  • Github Action
  • Docker
  • Github Container Registry(GHCR)

✅ CI/CD 구성의 흐름

————CI—————

  1. YAML 파일 등 내용에 수정사항을 Github 에 Push
  2. Github Action을 통해 스크립트 실행 → Nginx conf 파일 생성
  3. 작성해둔 Dockerfile 을 통해 Nginx 이미지 빌드
  4. GHCR 에 생성한 Docker 이미지 Push → Docker image 저장

————CD————

  1. Oracle 서버에 SSH 를 통해 접속
  2. GHCR에 저장한 Docker Image 가져오기
  3. 가져온 Docker Image 실행 (Nginx 실행)
  4. 실행한 Docker Container 동작 확인

초기 CI/CD을 기본적인 내용을 빠르게 작성하고 싶어서 아주 간단하게 구성해보았다.

사실 여기에 빠진 테스트, Health Check, 로그 확인, 롤백 등 이 들어가야만 진정한 CI/CD 라고 할 수 있다.

해당 내용들은 추후 천천히 추가하여 업그레이드 해보겠다.

📌 GitHub Actions 워크플로우 작성하기

위에서 설계한 내용을 바탕으로 CI / CD 를 한번에 구성하는 Github Action 워크 플로우를 작성해보자.

내가 작성한 Repository에 .github/workflows/ci-cd.yml 을 작성해주면 된다.

0️⃣ 기본 설정 (이름, 트리거, 권한 설정)

  • 워크플로우의 이름 지정 : Action 탭에서 표시되는 내용
  • main 브랜치에 Push 할 때 실행
  • 수동 실행도 가능하도록 설정 (workflow_dispatch)
  • 접근 권한 설정 : 추후 GHCR 에 올릴 때 해당 GitHub 저장소 리소스에 접근 할 수 있는 권한

name: 🚀 간단한 배포

on:
  push:
    branches: main
  workflow_dispatch:

permissions:
  contents: read
  packages: write

1️⃣ 환경 설정

  • 현재 GitHub 리포지토리의 코드를 runner에 가져옴
  • pnpm 패키지 매니저 설치
  • Node.js 22 버전을 설치 및 cache: "pnpm"으로 의존성 캐시를 저장해 빌드 속도 향상
jobs:
  deploy:
    name: 빌드 및 배포
    runs-on: ubuntu-latest
    steps:
      - name: 1. 체크아웃
        uses: actions/checkout@v4

      - name: 2. pnpm 설치
        uses: pnpm/action-setup@v4
        with:
          version: latest

      - name: 3. Node.js 설정
        uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "pnpm"

2️⃣ 의존성 설치 및 nginx.conf 생성

  • 의존성 설치 후 nginx.conf를 스크립트로 생성
    • --frozen-lockfile → lockfile 에 정의된 버전 그대로 의존성 설치
  • 생성 실패 시 워크플로우 를 중단

- name<: 4. 의존성 설치 및 nginx.conf 생성
  run: |
    pnpm install --frozen-lockfile
    pnpm generate

    if [ ! -f "nginx.conf" ]; then
      echo "❌ nginx.conf 파일이 생성되지 않았습니다!"
      exit 1
    fi
    echo "✅ nginx.conf 파일이 성공적으로 생성되었습니다."

3️⃣ GHCR 로그인

  • Docker 에서 공식 제공해주는 Action 사용하여 간단하게 구성
  • Docker CLI 로 GHCR 에 로그인
  • ${{ github.actor }}는 워크플로 실행자의 GitHub ID
  • ${{ secrets.GITHUB_TOKEN }}은 GitHub에서 자동 생성된 인증 토큰
- name: 5. GHCR 로그인
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

→ 여기서 사용되는 actor, GITHUB_TOKEN 변수들은 따로 설정하지 않아도 Github에서 알아서 설정해준다.

4️⃣ Docker Image 빌드 및 GHCR 올리기

  • Docker의 BuildKit 기반 빌드 도구buildx를 설정 (Docker 공식 Action 사용)
  • Dockerfile 을 기반으로 현재 위치에서 이미지 빌드 (Docker 공식 Action 사용)
  • 이미지의 tag 설정
    • 커밋 해시 기준 태그 (:sha)
    • 최신 버전 태그 (:latest)
  • cache-from / cache-to: GitHub Actions 캐시 사용으로 빌드 속도 개선

- name: 5.1 Docker Buildx 설정
  uses: docker/setup-buildx-action@v3

- name: 6. 이미지 빌드 및 푸쉬
  uses: docker/build-push-action@v6
  id: build
  with:
    context: .
    push: true
    tags: |
      ghcr.io/${{ github.repository }}:${{ github.sha }}
      ghcr.io/${{ github.repository }}:latest
    cache-from: type=gha
    cache-to: type=gha,mode=max

4️⃣ 변수 설정 및 서버 배포

  • Docker image, Container 이름이 길어지니까 변수로 만들어 재사용하도록 하기
  • appleboy/ssh-action@v1.2.2 Action을 사용하여 Oracle 서버에 SSH 로 접속하기
    • 여기서 보안을 위해 작성한 secrets 변수는 Github 저장소 → settings → Actions → Secrets 에 설정해줘야 한다.

  • Docker 스크립트 명령어 실행
    1. 기존 컨테이너 정리
    2. 최신 이미지 가져온 후 컨테이너 실행
    3. 실행 상태와 로그 확인

- name: 6.1 변수 설정
  run: |
    echo "IMAGE_NAME=ghcr.io/${{ github.repository }}:${{ github.sha }}" >> $GITHUB_ENV
    echo "CONTAINER_NAME=nginx-redirect" >> $GITHUB_ENV

- name: 7. 서버 배포
  uses: appleboy/ssh-action@v1.2.2
  with:
    host: ${{ secrets.ORACLE_SERVER_IP }}
    username: ${{ secrets.ORACLE_SERVER_USERNAME }}
    key: ${{ secrets.ORACLE_SERVER_KEY }}
    script: |
    
	    set -e # 오류 발생 시 바로 종료
	    
	    # GHCR 로그인
	    echo "${{ secrets.GITHUB_TOKEN }}" | sudo docker login ghcr.io -u ${{ github.actor }} --password-stdin
    
      # 이전 컨테이너 정리
      sudo docker stop ${{ env.CONTAINER_NAME }} || true
      sudo docker rm ${{ env.CONTAINER_NAME }} || true
      sudo docker image prune -f

      # 새 이미지 배포
      sudo docker pull ${{ env.IMAGE_NAME }}
      sudo docker run -d --name ${{ env.CONTAINER_NAME }} --network nginx-proxy-manager_default -p 8080:80 ${{ env.IMAGE_NAME }}

      # 배포 확인
      sleep 3
      sudo docker ps | grep ${{ env.CONTAINER_NAME }}
      sudo docker logs --tail 30 ${{ env.CONTAINER_NAME }} || true
      
      # GHCR 로그아웃
      docker logout ghcr.io || true

위 내용 작성 후 Github 저장소 에 반영 하고 나면 다음과 같이 Action이 활성화가 되고, 동작하는 것을 확인할 수 있다.

Redirect 데이터에 이번 프로젝트에 대한 링크도 넣어놓았다.
아래 링크를 통해 잘 동작하는지 확인할 수 있다.

https://link.scent-jo.me/portfolio-redirects/github

📌 마무리하며…

이번 프로젝트는 아주 간단하지만, 서버를 다루다보니 재미가 쏠쏠한 시간이었다. 현재 나에게 필요한 것을 구현한 것이다보니 더 몰입해서 할 수 있었던 것 같다.

과정에서 정말 많은 “딸깍” 이 될 정도로 많은 부분에서 오픈소스화 되어있는 것이 Github Action의 크고 큰 장점이 아닌가 싶다.

나도 언젠가는 저런 자동화 서비스를 오픈소스로 만들어보고 싶다는 소망을 품어본다. 😁😁

대부분의 회사에서 CI/CD 를 많이 구축해 놓아 사용하고 있다고 알고 있다. 이 경험이 아주 멋진 자산이 되길 기대한다!!

끝!!

“ 아 CI/CD 전략 고도화는 잊지 않고 꼭 해볼거다! “

Reference

GitHub Actions의 소개와 핵심 개념

CI/CD 5분 개념 정리 (현업에서 쓰는 개발 프로세스)

profile
사랑을 꿈꾸는 냄새나는 개발자 입니다 :)

0개의 댓글