상세 컨텐츠

본문 제목

Docker와 Kubernetes를 이용한 GKE 환경에서의 CI/CD 구현

프로젝트/click-me

by seungpang 2023. 12. 5. 16:03

본문

반응형

사이드 프로젝트를 하면서 CI/CD를 적용해야 할 필요성을 느꼈고 어떤 방식으로 할지 고민했다.

이전 프로젝트에서는 github action으로 CI를 하고 jenkins로 CD를 진행했었는데 이번에는 github action과 도커와 쿠버네티스를 적용해볼 생각이였다.

도커와 쿠버네티스를 이용하는 이유는 이식성과 자가 치유, 오토스케일링, 로드밸런싱을 손쉽게 적용할 수 있기 때문이다.

  • 이식성: 도커는 애플리케이션과 그 환경을 코드로 작성하여 '컨테이너'라는 단위로 패키징한다. 이 컨테이너는 어디에서나 동일하게 동작하므로, 개발 환경과 운영 환경간의 차이를 최소화한다.
  • 자가 치유: 쿠버네티스는 애플리케이션을 항상 원하는 상태로 유지하기 위해 지속적으로 모니터링하고 관리한다. 예를 들어, 컨테이너가 실패하면 다른 컨테이너로 대체하고, 노드가 다운되면 그 노드에 실행되는 컨테이너를 다른 노드로 이동시킨다.
  • 오토스케일링: 트래픽이 많아질 경우, 쿠버네티스는 자동으로 더 많은 컨테이너를 생성하여 트래픽을 처리할 수 있게한다. 반대로, 트래픽이 줄어들면 불필요한 컨테이너를 자동으로 제거하여 자원을 절약한다.
  • 로드밸런싱: 쿠버네티스는 서비스를 외부에 노출시키는 기능과 함께 로드밸런싱 기능을 제공한다. 이를 통해 들어오는 트래픽을 자동으로 분산시키고, 애플리케이션의 가용성을 높인다.

Cloud 환경은 gcp를 택했다. aws 프리 티어에서는 할 수 있는 것이 많지 않아 gcp에서 주는 300달러 무료 크레딧을 가지고 활용하기로 했다.

사전 설정


서비스 계정에는 Docker image를 Artifact Registry에 푸시하고, GKE에서 Deployment를 만들고,

파드 컨테이너 내에서 이미지를 가져오는 데 필요한 모든 권한이 있다.

새 서비스 계정을 생성하려면 앞서 언급했던 작업을 수행할 수 있도록 역할을 할당해야 한다.

일단은 이 서비스 계정에 대한 JSON 키를 생성하는 것이다. 이 키는 github action에서 gcp에 대한 인증을 위해 사용된다.

IAM 및 관리자 -> 서비스 계정 -> 키 관리로 이동한다.

아래의 이미지와 같이 새키 만들기를 눌러준다.

Json을 선택하고 만들기를 누른다. 해당 파일을 안전하게 저장

위에 언급했듯이 이 키는 github action에서 gcp에 대한 인증을 위해 사용된다.

키에 대한 준비가 끝났다면 이제는 쿠버네티스 클러스터를 생성해준다.

클러스터가 생성되었다면 1차적인 준비는 끝이 났다.

github action cd


github action cd과정에 대한 yml파일은 아래와 같다.

name: cd for click-service
on:
  push:
    branches: [ main ]
    paths:
      - click-service/**
      - .github/**

env:
  PROJECT_ID: ${{ secrets.PROJECT_ID }}
  DOCKER_REPO: ${{ secrets.DOCKER_REPO }} #Artifact registry
  REGION: ${{ secrets.REGION }}
  WORKSTATION_IMAGE: click-service
  VERSION: '1.0.1'
  IMAGE: ${{ secrets.REGION }}-docker.pkg.dev/${{ secrets.PROJECT_ID }}/${{ secrets.DOCKER_REPO }}/click-service
  REDIS_HOST: ${{secrets.REDIS_HOST}}
  REDIS_PORT: ${{secrets.REDIS_PORT}}
  DATASOURCE_URL: ${{secrets.DB_URL}}
  DATASOURCE_USERNAME: ${{secrets.DB_USERNAME}}
  DATASOURCE_PASSWORD: ${{secrets.DB_PASSWORD}}

jobs:
  build:
    runs-on: ubuntu-22.04

    steps:
      - name: Check out Repository
        uses: actions/checkout@v3

      - name: Cache Gradle caches
        uses: actions/cache@v3
        with:
          path: |
            ~/.gradle/caches
            ~/.gradle/wrapper
          key: ${{ runner.os }}-gradle-${{ github.job}}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
          restore-keys: ${{ runner.os }}-gradle-${{ github.job }}

      - name: Cache Jib layers
        uses: actions/cache@v3
        with:
          path: ./click-service/build/jib-cache
          key: ${{ runner.os }}-jib-${{ github.job }}-${{ hashFiles('**/build.gradle') }}
          restore-keys: ${{ runner.os }}-jib-${{ github.job }}

      - name: Set up google auth
        id: 'auth'
        uses: google-github-actions/auth@v1
        with:
          credentials_json: ${{ secrets.GCE_SA_KEY }}

      - id: 'get-credentials'
        uses: google-github-actions/get-gke-credentials@v1
        with:
          cluster_name: autopilot-cluster-2
          location: ${{ env.REGION }}

      - name: Docker auth
        run: |-
          gcloud auth configure-docker ${{ env.REGION }}-docker.pkg.dev --quiet

      - name: set up JDK 17
        uses: actions/setup-java@v3
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Docker Build
        run: ./gradlew :click-service:jib

      - name: Deploy
        run: |
          sed "s,\${image},${{ env.IMAGE }},g" ./click-service/resources.yaml > deployment.yaml
          kubectl apply -f ./deployment.yaml
          kubectl apply -f ./click-service/hpa.yaml

과정을 살펴보면 cd과정을 빠르게 하려고 gradle cache와 jib cache가 있다. jib에 대한 부분은 이 게시글을 살펴보는게 좋다.

Set up google auth는 gcp에 대한 권한을 가져오는 부분이고

Docker auth는 docker에 대한 권한을 설정하는 부분이다.

그 이후에는 docker 이미지를 빌드한 후 쿠버네티스에 배포하는 과정이다.

deployment.yaml


apiVersion: apps/v1
kind: Deployment
metadata:
  name: click-service # 이 Deployment의 이름을 지정
spec: # Deployment의 동작을 제어하는 설정
  selector: # 이 Deployment가 관리할 파드를 선택하는 레이블 셀렉터를 지정
    matchLabels:
      app: click-service # app 레이블의 값이 'click-service'인 파드를 선택
  strategy:
    rollingUpdate: # 롤링 업데이트 전략을 설정
      maxSurge: 25% # 업데이트 도중에 추가로 생성할 수 있는 파드의 최대 비율을 지정
      maxUnavailable: 25% # 업데이트 도중에 사용할 수 없게 될 수 있는 파드의 최대 비율
  replicas: 1 # 이 Deployment에서 유지할 파드의 개수를 지정
  template:
    metadata:
      labels:
        app: click-service
    spec:
      containers:
        - name: click-service # 컨테이너의 이름을 지정
          image: ${image}:latest # 사용할 도커 이미지를 지정,  ${image}는 실제 이미지 이름으로 대체해야함
          imagePullPolicy: Always # 항상 최신 이미지를 가져오도록 설정
          ports:
            - containerPort: 8080 # 컨테이너가 리스닝할 포트를 지정
              protocol: TCP # 통신 프로토콜을 TCP로 설정
          resources: 
            requests:
              cpu: 500m
              memory: 1000Mi

해당 파일에 대한 설명은 주석으로 달아놨으니 그 부분을 살펴보면 좋을 것 같다.

hpa.yaml


apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: click-service-hpa # 이 HPA의 이름을 지정
spec:
  minReplicas: 1 # 파드의 최소 수를 1개로 설정
  maxReplicas: 10 # 파드의 최대 수를 10개로 설정
  scaleTargetRef: # HPA가 적용될 대상을 지정
    apiVersion: apps/v1
    kind: Deployment
    name: click-service # click-service라는 이름의 Deployment에 이 HPA 설정을 적용
  metrics: # HPA가 파드 수를 조정하는 기준이 되는 메트릭을 지정
    - type: Resource #메트릭 유형을 리소스로 설정
      resource:
        name: cpu #CPU 사용률을 파드 조정의 기준으로 사용
        target: 
          averageUtilization: 60 #평균 CPU 사용률이 60%가 넘으면 파드를 늘리고, 미달하면 줄인다.
          type: Utilization # 메트릭의 타입을 사용률로 설정

이 설정은 'click-service' Deployment의 평균 CPU 사용률이 60%를 넘어가면 파드를 늘리고, 미달하면 줄이도록 한다.

그리고 파드의 수는 최소 1개에서 최대 10개까지 조절이 가능합니다.

적용하고 느낀 점


이번 프로젝트를 통해 Docker와 Kubernetes를 활용한 CI/CD의 효율성과 강력함을 직접 경험하게 되었다.

이전에 팀 프로젝트를 진행할 때는 Docker와 Kubernetes를 사용하지 않았다. 그 때는 빌드한 파일을 일일이 VM에 옮겨 실행하는 과정을 Jenkins로 진행했다.

이 과정은 서버가 늘어나면 또 일일히 추가해주는 과정을 필요해 번거로울뿐만 아니라 문제가 발생할 때 롤백하는 스크립트도 작업해줘야 했다.

하지만 이번에 Docker와 Kubernetes를 통해 CI/CD를 구현하면서 많은 편리함을 느꼈다.

먼저, 빌드에 대한 설정파일을 yaml로 관리할 수 있고 실패한 경우 이전에 성공적으로 배포된 이미지로 롤백하는 기능은 서비스의 안정성을 크게 향상시켰습니다.

그리고 가장 인상 깊었던 것은 Kubernetes의 오토스케일링 기능이다.

이 기능 덕분에 서비스의 트래픽 변화에 따라 자동으로 스케일 아웃하거나 스케일 인할 수 있다. 이를 통해 자원을 효율적으로 활용하면서도 서비스의 안정성을 유지할 수 있었다.

구현한 내용들은 여기에서 확인이 가능하다.

관련글 더보기