여행가는개발자

Github Action을 활용한 branch 배포 본문

개발 스터디

Github Action을 활용한 branch 배포

kimsoonil 2024. 11. 28. 13:49
728x90
반응형

워크플로우

  1. 특정 branch 에 코드가 push 됨
  2. push 이벤트를 트리거 하여 작성된 워크플로우 실행
  3. checkout github repository
  4. docker build and ACR push(auzre 도커 이미지 저장소)
  5. github 환경 변수에 image tag 와 acr login server 설정
  6. 설정된 변수로 k8s 매니페스트 임시 파일 생성
  7. Azure 로그인
  8. Azure AKS 클러스터와 연결
  9. 생성된 k8s 임시 파일을 이용하여 애플리케이션 배포

Github Action 구조

  • develop-workflow.yml (Github Action 파일)
name: Build and deploy - develop myApp Front

on:
  pull_request:
    types: [ closed ]
    branches:
      - develop # 트리거 대상 브랜치 설정
  workflow_dispatch:

env:
  SLACK_WEBHOOK_URL: ${{ vars.SLACK_WEBHOOK_URL }}
  STEP: "Staging"

jobs:
  prepare_build:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: 'Send Slack PR Notification'
        run: |
          curl -X POST --data-urlencode "payload={\"channel\": \"C0566RX1U9H\", \"username\": \"myApp Front - $STEP\", \"text\": \"Merged PR, Start build\", \"icon_emoji\": \":red_car:\"}" $SLACK_WEBHOOK_URL

  build:
    needs: prepare_build
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout repository'
        uses: actions/checkout@v4

      - name: 'Login to ACR'
        uses: azure/docker-login@v2
        with:
        # 환경에 따라 적절한 ACR 서버 설정
          login-server: ${{ secrets.DEV_ACR_LOGIN_SERVER }}
          username: ${{ secrets.DEV_ACR_USERNAME }}
          password: ${{ secrets.DEV_ACR_PASSWORD }}

      - name: 'set up docker buildx'
        uses: docker/setup-buildx-action@v3

      - name: build and push
        uses: docker/build-push-action@v6
        with:
          context: .
          dockerfile: Dockerfile
          push: true
          # Kubernetes 매니페스트와 일치하도록 이미지 이름 설정
          tags: ${{ secrets.DEV_ACR_LOGIN_SERVER }}/myApp-front:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            PROFILE=staging

  deploy:
    needs: [ prepare_build, build ]
    runs-on: ubuntu-latest
    steps:
      - name: 'Checkout repository'
        uses: actions/checkout@v4

      - name: 'set environment variable'
        run: |
          echo "IMAGE_TAG=${{ github.sha }}" >> $GITHUB_ENV
          echo "ACR_LOGIN_SERVER=${{ secrets.DEV_ACR_LOGIN_SERVER }}" >> $GITHUB_ENV

      - name: 'generate k8s manifest'
        run: |
        # Kubernetes 매니페스트 파일 생성 {실제 파일명} > {임시 파일명}
          envsubst < ${{ github.base_ref }}-k8s.yml > ${{ github.base_ref }}-tmp-k8s.yml
        shell: bash

      - name: 'Azure login'
        uses: azure/login@v1.6.1
        with:
        # Azure 인증 정보 설정
          creds: ${{ secrets.DEV_AZURE_CREDENTIALS }}

      - name: 'Set AKS context STAGING'
        uses: azure/aks-set-context@v3
        with:
        # Kubernetes 클러스터 정보 설정
          cluster-name: ${{ secrets.DEV_CLUSTER_NAME }}
          resource-group: ${{ secrets.DEV_CLUSTER_RESOURCE_GROUP }}

      - name: 'Setup kubectl STAGING'
        uses: azure/setup-kubectl@v3

      - name: 'Deploy to AKS front STAGING'
        uses: azure/k8s-deploy@v5
        with:
          namespace: 'default'
          manifests: |
          # 생성된 Kubernetes 매니페스트 임시 파일 경로 지정
            ${{ github.base_ref }}-tmp-k8s.yml

      - name: 'Send Slack Notification'
        if: success() || failure()
        run: |
          STATUS="완료"
          if [ "${{ job.status }}" == "failure" ]; then
            STATUS="실패"
          fi
          curl -X POST --data-urlencode "payload={\"channel\": \"C0566RX1U9H\", \"username\": \"myApp Front - $STEP\", \"text\": \"*$STEP* 배포 *$STATUS! $EMOJI*\", \"icon_emoji\": \":red_car:\"}" $SLACK_WEBHOOK_URL
flowchart TB
classDef blue fill:blue, stroke:black, stroke-width:2px, color:white
classDef yellow fill:yellow, stroke:black, stroke-width:2px, color:black
node(MyBranch)
node --> r1
r1(Github):::yellow
r1-->|action|r2(job)
    r2-->r5(deploy)
    r2-->r4(build)
    r2-->r3(prepare_build)

subgraph deploy
    direction LR
        r5--->r5.1(Azure login)
        r5--->r5.2(kubectl)
        r5--->r5.3(Azure Deploy)
        r5--->r5.4(Slack PR)
    end

subgraph build
    direction LR
        r4--->r4.1(repository)
        r4--->r4.2(Login ACR)
        r4--->r4.3(docker)
    end

    subgraph prepare_build
    direction LR
    r3--->r3.1(Slack PR)
    end

prepare_build

  • Slack PR - 슬랙에 ‘Merged PR, Start build’ 문구와 함께 배포를 시작했다는 메세지를 날립니다.

build

  • repository - 브랜치에 있는 코드를 체크아웃합니다.
  • Login ACR - ACR에 로그인을 합니다.
  • docker - 도커 파일을 빌드/푸시합니다
FROM node:20.11.1-slim as base

FROM base as deps
WORKDIR /app

COPY package.json .

RUN yarn install

FROM base as builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules

RUN yarn config set depth 0 && yarn cache clean --force
RUN yarn global add env-cmd
ARG PROFILE=staging
COPY . .
RUN env-cmd -f .env.${PROFILE} $(yarn bin)/next build


FROM base as runner
WORKDIR /app
ARG PROFILE=staging
ENV NODE_ENV ${PROFILE}
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static

EXPOSE 3000

CMD ["node", "server.js"]

 

deploy

  • Azure login - 애저에 로그인합니다.
  • kubectl - k8s 파일을 푸시/배포합니다.
---
# front deploy
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myApp-front
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myApp
      tier: front
  template:
    metadata:
      labels:
        app: myApp
        tier: front
    spec:
      containers:
        - name: myApp-front
          image: ${ACR_LOGIN_SERVER}/myApp-front:${IMAGE_TAG}
          imagePullPolicy: Always
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
            successThreshold: 1
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 30
            periodSeconds: 15
          ports:
            - containerPort: 3000
              name: myApp-front
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"
          securityContext:
            runAsNonRoot: true
            runAsUser: 1000
            allowPrivilegeEscalation: false

---
# customer ilb svc
apiVersion: v1
kind: Service
metadata:
  name: myApp-svc-front
  labels:
    app: myApp
spec:
  ports:
    - port: 80
      targetPort: 3000
  selector:
    app: myApp
    tier: front

---
  • Azure Deploy - 애저에 배포합니다.
  • Slack PR - 슬랙에 빌드 성공 / 실패를 메세지를 남깁니다.

health check 코드 추가

import { NextResponse } from 'next/server';

import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  const url = request.nextUrl.clone();

  if (url.pathname === '/health') {
    return new NextResponse(
      JSON.stringify({
        status: 'ok',
        timestamp: new Date().toISOString()
      }), 
      { 
        status: 200,
        headers: {
          'Content-Type': 'application/json'
        }
      }
    );
  }

  return NextResponse.next();
}

export const config = {
  matcher: '/health',
};
728x90
반응형