토이 프로젝트/Spring&Java 갖고놀기

Spring Boot Web Chatting : 스프링 부트로 실시간 화상 채팅 만들기(15) GitAction 으로 K8S 배포까지

TerianP 2025. 2. 2. 18:03
728x90

1. 시작하면서

벌써 빠르게 1월이 지나가고 있네요ㅠㅠ올해 목표로 나름 한달에 한개씩은 꼭 쓰자! 라고 시작했는데...결국 1월말이 넘어가고 2월이 다 되어서야 겨우 글 하나를 작성하게 되었네요ㅠㅠ물론 그렇다고 놀고만 있지는 않았습니다! 나름 열심히 했습니다! 아마도...?ㅋㅋㅋㅋ

1월에는 주로 chatforyou.io 프로젝트에 집중했습니다. 그 중에서도 JWT 토큰 발급 및 적용에 대한 부분과 CI/CD 에 대한 부분에 집중했습니다.

 

JWT 도 제가 직접한 프로젝트에서는 처음했던만큼 말할것도 많고 적고 싶은것도 많지만...이번에는 CI/CD , 특히 GitAction 을 활용해서 k8s 에 배포까지에 대한 내용을 작성해볼까합니다. 왜 개발이야기하다가 갑자기 CI/CD 가 튀어나왔냐...하면 스토리가 좀 있습니다. 처음에는 프론트분도 그렇고 저도 그렇고 자연스럽게 그리고 당연하게 로컬에서 개발하고 로컬에서 빌드하고 제 서버에 있는 private docker registry 에 업로드하고 이걸 사용해서 k8s 에 배포했었습니다. 제가 기존에 그렇게 했었기 때문에 사실 이 방법을 자연스럽게 선호했고, 이렇게 하는게 어떨까요? 하고 프론트분께 제시했던 이유도 있었습니다.

 

문제는 여기서 발생합니다. 로컬에서 개발하고 빌드하는게 정말...정말 오래걸렸었고 또 힘들었거든요. 힘들다는게 단순히 어렵다는 것보다  '복잡하다' 였습니다. 먼저 로컬에서 빌드 후 배포까지는 아래 과정이 필요했습니다.

로컬 빌드 명령어 > 빌드 대기 > 업로드 대기 > 업로드 확인 > k8s 접속 > deployment 버전 변경

 

그마나도 이 과정이 한번에 잘 되면 다행인데 그게 아니라 중간에 에러가 나는지 특히 와이파이가 끊어지지는 않는지,  docker registry 에 문제가 없는지 계속 체크해줘야 했습니다. 더욱이 빌드가 20~30 초가 아닌 100 초가 넘어가는 상황에서 한번이라도 삑사리가 나면...정말 눈앞이 캄캄해지곤 했습니다.

두번째로는 "빌드 및 배포 환경에서의 환경변수 값에 관한 문제"가 있었다. 로컬에서 빌드하다보니 프론트에서는 .env 파일을 자연스럽게 포함해서 빌드하였고, 나 역시 application.properties 를 포함하여 빌드하고 배포했습니다. 그러나 이렇게하다보니까 로컬에서는 잘되었는데 왜 배포했을때는 안되지? 라고 할 때가 종종 있었습니다. 후에 그 이유를 알 수 있었는데 로컬에서 넣었던 환경변수가 배포했을때는 동작을 안한다거나 서로 다른 내용을 추가해줘야한다거나 하는 등 문제가 있었습니다. 사실 이 부분은 configmap 을 두고 이걸 가져와서 읽도록 만들면 되는데 로컬에서 하다보니 이 방법을 생각하지 못 했었고, 쉽게 해결될 문제를 오히려 돌아서 가버린 꼴이 되어버렸습니다.

 

이렇게 두가지 문제가 결국 나와 프론트분 모두를 조여왔고, 1월 중순쯤 결단을 내렸습니다...!

 더 이상은 안되겠어요...ㅠ.ㅠ 너무 귀찮아요ㅠㅠ 제가 CI/CD 할 수 있게 만들겠습니다

 

라고 선언해버렸고, 프론트 선생님께서 너무나도 감사하게 GitAction 과 package 를 알아와주셨고, 이를 이용해서 아주 편하게 CI/CD 할 수 있는 docker-image 를 만들 수 있었다! 오늘은 이런 해피엔딩으로 가는 길을 설명해보려 합니다.


2. GitAction 과 GitHub Container Registry 란? : CI/CD 의 혁명!

https://tech.kakao.com/posts/516 // GitAction 과 Git Registry 를 사용한 대표적인 예시

 

 

1) GitAction

GitHub Actions(일명 GitAction)는 GitHub에서 제공하는 CI/CD(지속적 통합/지속적 배포) 도구로 GitHub 리포지토리 내에서 코드 빌드, 테스트, 배포 등 다양한 작업을 자동화할 수 있게 해주며, 워크플로우를 통해 이벤트에 반응하여 자동으로 작업을 실행한다.

 

주요 특징

1. YAML 기반 워크플로우 정의사용자는 .github/workflows 디렉터리에 YAML 파일을 작성하여 빌드, 테스트, 배포 등 자동화할 과정을 정의할 수 있다. 이 방식은 워크플로우를 읽기 쉽고 관리하기 쉽게 만든다.

2. 이벤트 기반 트리거커밋, 풀 리퀘스트, 이슈 생성 등 GitHub 내 다양한 이벤트를 기반으로 워크플로우가 자동 실행된다. 이로 인해 코드 변경 사항에 따라 자동으로 작업을 수행할 수 있게 된다. Job과 Step의 구조각 워크플로우는 하나 이상의 Job으로 구성되며, 각 Job은 독립적인 환경(가상 머신 또는 컨테이너)에서 실행된다. Job 내부에서는 여러 Step이 순서대로 실행되어 구체적인 작업(예: 코드 체크아웃, 의존성 설치, 테스트 실행 등)을 수행한다.

3. 재사용 가능한 액션GitHub Marketplace에서 미리 작성되어 공유되는 다양한 액션을 활용하여 반복되는 작업을 최소화하고 워크플로우 구성을 간소화할 수 있다. 필요 시 사용자가 직접 커스텀 액션을 작성할 수도 있다.

4. 다양한 실행 환경 지원GitHub는 Ubuntu, Windows, macOS 등의 호스트 머신에서 워크플로우를 실행할 수 있는 GitHub-hosted runner를 제공하며, 사용자가 자체적으로 runner를 호스팅할 수도 있다. 이를 통해 다양한 플랫폼에서 동시다발적으로 테스트 및 배포 작업을 수행할 수 있다.

5. 보안 및 비밀 관리GitHub Actions는 암호, 토큰, 기타 민감한 정보를 안전하게 저장하고 관리할 수 있도록 Secrets 기능을 지원한다. 이를 통해 민감 정보를 코드에 노출하지 않고 자동화 프로세스에서 안전하게 활용할 수 있다.

6. 확장성과 유연성단순한 작업 자동화부터 복잡한 다단계 CI/CD 파이프라인까지 다양한 규모의 워크플로우를 구축할 수 있으며, 의존성 설정이나 조건문(if)을 이용해 세밀하게 제어할 수 있다.

 

 

2) GitHub Container Registry 

GitHub Container Registry는 GitHub에서 제공하는 컨테이너 이미지 전용 저장소입니다. 기존의 Docker 패키지 레지스트리를 대체하며, Docker 이미지를 비롯한 OCI 이미지들을 안전하게 저장하고 관리할 수 있도록 설계되었습니다. GitHub Container Registry는 섬세한 접근 제어, 최적화된 스토리지 및 익명 접근(공개 이미지의 경우) 등 다양한 이점을 제공한다.

주요 특징

1. 세분화된 접근 제어각 이미지에 대해 세밀하게 권한을 설정할 수 있어, 팀이나 조직 단위의 보안 정책을 따르기 좋다. 통합 스토리지 및 관리GitHub 계정 혹은 조직 단위로 이미지를 관리할 수 있어, 여러 리포지토리와 연동이 쉽다.

2. 익명 접근 지원공개 이미지의 경우, 인증 없이도 접근이 가능하여 외부 사용자와의 공유가 용의하다. GitHub Actions과의 통합GitHub Container Registry는 GitHub Actions와 자연스럽게 연동된다.

3. GitHub Actions 워크플로우 내에서 Docker 이미지를 빌드한 후, ghcr.io에 이미지를 푸시할 수 있도록 docker/login-action 및 docker/build-push-action 같은 액션을 사용할 수 있다. 예를 들어, 워크플로우 스크립트 내에서 GITHUB_TOKEN을 사용하여 ghcr.io에 로그인하고 이미지를 자동으로 배포할 수 있다.

 


3. docker-image.yml : CI/CD 를 위한 pipline 작성하기

사실 맨 처음에 CI/CD 를 생각했을때는 당연하게 Jenkins 를 고려했었다. 이는 현재 회사에서도 젠킨스를 사용하고 있기 때문이고, 예전에도 젠킨스를 한번 맛봤었기에 금방 하지 않을까...? 하는 생각도 있었기 때문이다. 뭣보다 GitAction 이 엄청 어려울 것이라고 생각했었다.

하지만 늘 그렇듯 내 예상은 손쉽게 빗나갔고, 이번에 작업하면서 GitAction 이 말도 안되게 쉽고, 편하다는 것을 알 수 있었다. 이 부분은 정말 놀라운 부분 중 하나였는데 내 서버에 젠킨스를 설치 + 관리 + GitHub 와 연결 등등 하는 모든 과정보다 GitAction 과 Git Registry 를 사용해서 빌드/배포하는게 훨씬 쉬웠기 때문이다.

 

1) docker-image.yml

GitAction 을 사용하기 위해서는 프로젝트 내 .docker > workflows 디렉토리 내  {workflow_name}.yml 파일이 필요하다. 이 파일에는 GitAction 에서 '어떤' 동작을 '어떻게' '어디로' 수행할 것인지 모두 적혀있다.

name: CI/CD Pipeline with GitHub Actions for K8S

on:
  push:
    branches: [ "master-webrtc-catchmind"]


jobs:
  deploy:
    name: Build And Deploy K8S
    environment: chatforyou-back-env # 환경 지정
    runs-on: ubuntu-latest
    permissions:
      packages: write
      contents: read
    env:
      KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
      K8S_NAMESPACE: chatforyou # Kubernetes 네임스페이스
      DEPLOYMENT_NAME: chatforyou-v03    # Deployment 이름

    steps:
      # 1. 코드 체크아웃
      - name: Checkout code
        uses: actions/checkout@v2

      # 2. Kubeconfig 설정
      - name: Configure Kubeconfig
        run: |
          mkdir -p ${HOME}/.kube
          echo "${KUBE_CONFIG}" | base64 --decode > ${HOME}/.kube/config
          export KUBECONFIG=${HOME}/.kube/config

      # 3. TIMESTAMP 생성 (한국 시간으로) && IMAGE_URI 로 이미지명:태크명 공통화
      # IMAGE_URI = chatforyou-io/chatforyou-io-backend:시간
      - name: Generate TIMESTAMP in KST
        id: timestamp
        run: |
          export TZ=Asia/Seoul
          TIMESTAMP=$(date '+%Y%m%d%H%M%S')
          echo "TIMESTAMP=$TIMESTAMP" >> $GITHUB_ENV
          echo "IMAGE_URI=ghcr.io/sejonj/chatforyou:$TIMESTAMP" >> $GITHUB_ENV

      # 5. Docker 이미지 빌드 및 태그
      - name: Build Docker Image
        run: |
          docker build --file Dockerfile -t $IMAGE_URI .

      # 6. Docker 이미지 푸시 (GitHub Container Registry)
      - name: Push Docker Image to GitHub Packages
        run: |
          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker push $IMAGE_URI

      # 7. Kubernetes 배포
      - name: Deploy to Kubernetes
        run: |
          kubectl set image deployment/$DEPLOYMENT_NAME \
            chatforyou-container=$IMAGE_URI \
            -n $K8S_NAMESPACE
          kubectl rollout status deployment/$DEPLOYMENT_NAME -n $K8S_NAMESPACE

 

 

2) 전체 파이프라인 개요 및 각 단계별 설명

이제 해당 yml 에 파일에서 대체 뭔 짓을 하는지 확인해보자

1. 트리거

  • 파이프라인은 master-webrtc-catchmind 브랜치에 코드가 푸시될 때 실행된다.

2. Job 정의

  • Job 이름은 "Build And Deploy K8S"다.
  • 실행 환경은 ubuntu-latest를 사용하며, 필요한 권한(packages: write, contents: read)을 부여한다.
  • 환경 변수로 KUBE_CONFIG, K8S_NAMESPACE, DEPLOYMENT_NAME을 설정하여 배포 대상 Kubernetes 환경을 지정한다.

3. 코드 체크아웃

  • actions/checkout@v2 액션을 사용해 리포지토리의 코드를 체크아웃한다.
  • 이 단계는 이후 빌드, 테스트, 배포에 사용될 소스 코드를 작업 환경에 복사한다.

4. Kubeconfig 설정

  • 작업 환경에 .kube 디렉터리를 생성한다.
  • KUBE_CONFIG 변수가 base64로 인코딩되어 있으므로 base64 --decode 명령어로 디코딩 후 ${HOME}/.kube/config 파일에 저장한다.
  • 환경 변수 KUBECONFIG에 해당 파일 경로를 지정해 kubectl이 접근할 수 있도록 설정한다.
    • 여기서 중요한 것은 KUBE_CONFIG 이다. 해당 값은 추후에 설명하겠지만 단순히 아무값이나 넣는 것이 아니라 k8s 에서 특정값을 가져와서 세팅해야한다.

5. TIMESTAMP 생성 및 IMAGE_URI 설정

  • export TZ=Asia/Seoul을 통해 시간대를 한국 시간으로 설정한다.
  • 현재 시간을 "YYYYMMDDHHMMSS" 포맷으로 생성하여 TIMESTAMP 변수에 저장한다.
  • 생성된 TIMESTAMP를 사용해 IMAGE_URI 변수를 설정한다. IMAGE_URI는 ghcr.io 레지스트리의 이미지 태그에 TIMESTAMP를 포함하는 형태로 구성된다.
  • 두 변수 모두 $GITHUB_ENV에 추가되어 이후 단계에서 참조할 수 있다.

6. Docker 이미지 빌드 및 태그

  • 지정된 Dockerfile을 이용해 Docker 이미지를 빌드한다.
  • 빌드된 이미지는 IMAGE_URI 태그를 붙여 관리한다.

7. Docker 이미지 푸시

  • GitHub Container Registry(ghcr.io)에 로그인하기 위해 GitHub Token을 사용한다.
  • 로그인 후, 빌드된 이미지를 IMAGE_URI로 GitHub Packages에 푸시한다.
    • 재미있는 점은 여기서 빌드된 이미지를 push 할 때 GitHub Packages 에 해당 이미지명으로 된 Registry 가 없다면 자연스럽게 생성된다는 점이다.

8. Kubernetes 배포

  • kubectl set image 명령어를 사용해 지정된 Deployment의 컨테이너 이미지를 업데이트한다.
  • 새로운 이미지로 변경된 후, kubectl rollout status 명령어를 통해 배포의 상태를 확인한다.

4. K8S 에 배포하기 : kubeconfig 사용하기

GitHub Container Registry 는 GitHub 에서 제공하는 dockerHub 의 개념이라고 생각하면 편하다. 우리는 GitAction 을 통해서 docker image 로 빌드된 후 해당 이미지를 어딘가에 저장해야한다. 이때 '어딘가' 에 해당하는 장소가 바로 GitHub Container Registry 이다. 사실 빌드와 저장하는 것까지는 3번 글에서 모두 끝이 난다. 하지만 우리는 빌드와 이미지 저장에서 끝을 내는 것이 아닌 k8s 에 배포하는 것이다. 그렇다면 어떻게 k8s 에 배포까지 가능할까?

 

답은 바로 kubeconfig 파일을 활용하는 것이다. kubeconfig 파일은 Kubernetes 클러스터에 접근하고 인증하기 위한 중요한 구성 파일이다. 즉 GitAction에서 Kubernetes 클러스터에 배포할 때 이 파일을 사용하여 클러스터와 안전하게 통신할 수 있도록 한다. 조금 사설을 덧붙이자면, 사실 이 부분은 관련된 레퍼런스를 찾기가 정말 어려웠던 부분 중 하나이다. 이는 보통 aws 나 azure 등 공식 클라우드에서 지원하는 k8s 에 배포하기 때문에 직접 k8s 를 구축하고 배포하는, 내가 원하는 환경에 맞는 내용이 거의 없었기 때문이다. 이 부분은 검색도 많이했지만 특히 ChatGPT 와 perplexity 의 도움을 많이 받았다.

1) kubeconfig 파일 찾기

kubeconfig 파일은 보통 아래 디렉토리에 위치한다. 마스터 노드의 root 의 home 디렉토리 > .kube > config 파일을 찾는다.

root 의 HOME 디렉토리의 > .kube 디렉토리 > config 파일이다

이 파일의 내용은 대략 아래처럼 생겼다.

살짝만 봐도 아주아주 중요한 cert 정보가 포함되어 있다.

 

2) Base64 로 인코딩하기

이제 이 내용을 모두 복사해서 아래 사이드에서 base64 값으로 인코딩한다. 단 인코딩할때 아주아주 주의할 점이 있다!

 

base64 인코딩시에 server 라고 되어있는 부분을 실제 k8s 클러스터와 통신할 수 있는 공인 IP 와 포트를 넣어줘야 한다는 점이다. 즉 192.168.X.X:6443 이 아닌 공인 IP:특정포트 로 넣어줘야 정상적으로 통신이 가능하다.

https://www.base64encode.org/

 

Base64 Encode and Decode - Online

Encode to Base64 format or decode from it with various advanced options. Our site has an easy to use online tool to convert your data.

www.base64encode.org

 

 

3) GitHub Environments Secret 에 저장하기

이제 KubeConfig 값을 저장할 차례이다. GitHub 의 해당 레포지토리의 Settings 으로 넘어간다. 그리고 Environments 로 간다. 이후 New Environments 를 눌러 나만의 env 를 생성한다. 단 이때 생성되는 env 명은 yml 파일의 jobs > deploy > environment 에 있는 환경명과 일치해야한다.

jobs:
  deploy:
    name: Build And Deploy K8S
    environment: chatforyou-back-env ##### 환경 지정 <<< 여기와 일치 #####
    runs-on: ubuntu-latest

잊지말자 정확히 일치해야한다

그리고 Add environment secret 를 눌러 secret 를 생성한다. 당연하게도 yml 에 env 에 작성한 내용안에 있는 ${{secrets.변수명}} 일치해야한다.

    env:
      KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}

${{secrets.변수명}} 과 secrets 안에 변수명은 일치!!


5. GitAction 동작 및 업로드된 docker Image 확인하기

GitAction 의 동작을 확인하는건 아주 간단하다. 작업하는 git repo 에서 Action 탭으로 넘어간다

git action 이 정상적으로 성공한 모습!

만약 자세한 내용을 확인하고 싶다면 각 workflow 를 클릭하면 어떻게 동작했는지 상세한 로그도 확인가능하다.

결과, 시간, job 에 대한 로그 등 모두 확인가능!

또한 내 docker image 가 잘 업로드 되었는지 어떤 이미지명으로 업로드 되었는지 확인하기 위해서는 git repo 에서 오른쪽에 있는 Packages 메뉴를 클릭하면 아주 상세하게 확인할 수 있다. 이미지 pull 도 가능하다!

Packages 를 클릭해보면...
요렇게 내가 원하던 docker image 가 등장한다!

 

마지막으로 K8S 에 정말 배포되었는지도 확인해보자! 디플로이먼트와 pod 이미지를 잘 확인해보면 위에서 보았던 20250202150722 와 동일한 것을 확인할 수 있다!

package 에 보이는 이미지명과 똑같다!


6. 빌드에서 배포까지 최종 결과 : 38.7% 성능 향상

단계 도입 전 소요시간 도입 후 소요시간 시간 절감량 성능 향상률
빌드 105.2초 66초 39.2초 37.3%
이미지 업로드 25초* 9초 16초 64.0%
배포 자동화 15초(수동 작업)* 14초(자동 처리) 1초 6.7%
전체 프로세스 145.2초 89초 56.2초 38.7%

- 이미지 업로드의 경우 카페에서 핸드폰 핫스팟을 잡고, 업로드할 때 20~30 초 정도가 걸렸다. 이에 평균 25초로 잡았다

- 배포 자동화의 경우 업로드 확인 > k8s 대시보드 로그인 > chatforyou 디플로이먼트에서 버전 변경까지 전 과정으로 빨라야 10초 남짓, 조금 머뭇거리면 15초가 넘어가는 경우도 많았다.

 


- Reference

https://elephant-dev.tistory.com/43

 

[GIT] github-action으로 빌드, 쿠버네티스 클러스터에 배포하기 (with Naver Cloud Platform)

github action을 통해 소스코드 커밋 혹은 머지 시 빌드와 테스트 하고, NCP Container Registry로 이미지 push 후 다시 pull 받아 쿠버네티스 클러스터에 Rolling Update 배포 전략을 사용하여 배포하는 방법에

elephant-dev.tistory.com

https://devtron.ai/blog/create-ci-cd-pipelines-with-github-actions-for-kubernetes-the-definitive-guide/

 

Kubernetes CI/CD Pipelines with GitHub Actions

Tl:DR: Create a robust CI/CD pipeline for Kubernetes using GitHub Actions, covering everything from code security scanning to deployment. Learn how Devtron simplifies post-deployment tasks and Day 2 operations for a seamless Kubernetes experience.

devtron.ai

https://somaz.tistory.com/213

 

1. Github Action이란?

Overview 오늘은 Github Action에 대해서 공부해보려고 한다. Github Action이란? GitHub Actions는 빌드, 테스트 및 배포 파이프라인을 자동화할 수 있는 지속적 통합 및 지속적 배포(CI/CD) 플랫폼이다. 리포지

somaz.tistory.com

https://gist.github.com/alo9507/1d1538d3767d856162209cbca25a81a4

 

Use GitHub Actions and Kubeconfig to deploy to Kubernetes

Use GitHub Actions and Kubeconfig to deploy to Kubernetes - deploy.js.yml

gist.github.com