5 min read

배포 GitHub Actions κ°œμ„ ν•˜κΈ°

Table of Contents

ν˜„μž¬ νšŒμ‚¬μ—μ„œλŠ” GitHub Actionsλ₯Ό 톡해 Docker 이미지λ₯Ό λ§Œλ“€κ³  λ°°ν¬ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.
ν•˜μ§€λ§Œ μ œκ°€ ν•©λ₯˜ν•˜κΈ° 이전에 μ‚¬μš©ν•˜κ³  μžˆμ—ˆλ˜ 배포 μ›Œν¬ν”Œλ‘œμš°μ—λŠ” μ•½κ°„μ˜ λ¬Έμ œκ°€ μžˆμ—ˆλŠ”λ°, 이λ₯Ό κ°œμ„ ν•œ ν›„κΈ°λ₯Ό 적어 보렀 ν•©λ‹ˆλ‹€.

λ°°κ²½

ν˜„μž¬ 저희 λ°±μ—”λ“œ μ†ŒμŠ€ μ½”λ“œλŠ” 단일 μ €μž₯μ†Œμ— μ—¬λŸ¬ 개의 폴더가 있고, 각각의 폴더가 Kubernetes μƒμ—μ„œ λ…λ¦½λœ μ„œλ²„λ‘œ κ΅¬λ™λ˜λ„λ‘ λ‚˜λ‰˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€. 이듀을 λ…λ¦½μ μœΌλ‘œ λ°°ν¬ν•˜κΈ° μœ„ν•΄, κΈ°μ‘΄μ—λŠ” μ½”λ“œ 변경이 일어났을 λ•Œ 경둜λ₯Ό κ°μ§€ν•΄μ„œ κ΄€λ ¨ 파일이 λ³€κ²½λœ κ²½μš°μ—λ§Œ 각각의 μ›Œν¬ν”Œλ‘œμš°κ°€ μž‘λ™λ˜λ„λ‘ μ„€μ •λ˜μ–΄ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

예λ₯Ό λ“€λ©΄, μ΄λŸ¬ν•œ 파일이 μ—¬λŸ¬ 개 μžˆλŠ” 것이죠.

name: foo app Actions
on:
  push:
    branches:
      # ...
    paths:
      - 'apps/foo/**/*'
      - 'common/**/*'

jobs:
  build-and-push-image:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        # ...

      (Build... Push...)

      - name: Checkout manifest repository
        # ...

      - name: Change deployment.yaml
        # ...

      - name: Pushes to the repository
        # ...

μ΄λ ‡κ²Œ μ‚¬μš©ν•  경우 μ½”λ“œ 변경에 영ν–₯을 λ°›λŠ” μ„œλ²„λ§Œ μ„ νƒμ μœΌλ‘œ λΉŒλ“œκ°€ κ°€λŠ₯ν•˜κ³ , 각각의 과정이 병렬 싀행이 λ˜λŠ” μž₯점이 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μ›Œν¬ν”Œλ‘œμš°μ˜ λ§ˆμ§€λ§‰ 뢀뢄에 이미지 νƒœκ·Έ μ—…λ°μ΄νŠΈλ₯Ό μœ„ν•΄ Git Pushλ₯Ό ν•˜λŠ”λ°, 이 μ‹œμ μ΄ 겹치면 좩돌둜 인해 μ—λŸ¬κ°€ λ°œμƒν•˜κ³ , μ‹€νŒ¨ν•œ μ›Œν¬ν”Œλ‘œμš°λ₯Ό λ‹€μ‹œ μˆ˜λ™μœΌλ‘œ λŒλ €μ•Ό ν•˜λŠ” λ¬Έμ œκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

κΈ°μ‘΄ μ›Œν¬ν”Œλ‘œμš°

κ·Έλž˜μ„œ κ°œμ„ λœ μ›Œν¬ν”Œλ‘œμš°μ—μ„œλŠ” 선택적 λΉŒλ“œμ™€ λ³‘λ ¬μ„±μ΄λΌλŠ” μž₯점은 κ°€μ Έκ°€λ©΄μ„œ, Git Pushμ—μ„œμ˜ μ—λŸ¬λ₯Ό μ—†μ• λŠ” 것이 주된 λͺ©μ μ΄μ—ˆμŠ΅λ‹ˆλ‹€.

μƒˆλ‘œμš΄ GitHub Actions λ§Œλ“€κΈ°

μž¬μ‚¬μš© μ›Œν¬ν”Œλ‘œμš°

사전에 각각의 μ›Œν¬ν”Œλ‘œμš°λ“€μ„ μ‚΄νŽ΄λ³΄μ•˜μ„ λ•Œ, λ³€μˆ˜ λͺ‡ 개만 μ„€μ •ν•œλ‹€λ©΄ μž¬μ‚¬μš©μ΄ κ°€λŠ₯ν•΄ λ³΄μ˜€μŠ΅λ‹ˆλ‹€. μ›Œν¬ν”Œλ‘œμš°λ₯Ό μž¬μ‚¬μš©ν•˜λŠ” λ²•μ—λŠ” 크게 두 κ°€μ§€κ°€ μžˆλŠ”λ°, ν•˜λ‚˜λŠ” λ³„λ„μ˜ μ €μž₯μ†Œμ— μ›Œν¬ν”Œλ‘œμš°λ₯Ό κ΅¬μ„±ν•˜λŠ” 것이고 λ‹€λ₯Έ ν•˜λ‚˜λŠ” μ €μž₯μ†Œ 내에 νŒŒμΌμ„ 두고 μ°Έμ‘°ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” κ²ƒμž…λ‹ˆλ‹€. μ „μžμ˜ κ²½μš°κ°€ ν™•μž₯μ„± λ©΄μ—μ„œ 더 λ›°μ–΄λ‚˜μ§€λ§Œ, μ—¬κΈ°μ„œλŠ” ν•˜λ‚˜μ˜ μ €μž₯μ†Œμ—μ„œλ§Œ μ‚¬μš©ν•˜λ©΄ 되기 λ•Œλ¬Έμ— ν›„μžλ₯Ό μ„ νƒν–ˆμŠ΅λ‹ˆλ‹€.

κ³΅ν†΅μœΌλ‘œ μ‚¬μš©ν•  파일의 λŒ€λž΅μ μΈ ν˜•νƒœλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

on:
  workflow_call:
    inputs:
      module-name:
        required: true
        type: string
      registry:
        required: true
        type: string
    outputs:
      image-tag:
        description: Docker image tag
      branch:
        description: Branch name
    secrets:
      # ...

jobs:
  build-image:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        # ...

      (Build... Push...)

      - name: Checkout manifest repository
        # ...

      - name: Change deployment.yaml
        # ...

      - run: |
          git config user.name "${{ github.actor }}"
          git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
          git switch -c manifest-${{ inputs.module-name }}
          git add .
          git commit -m "Update ${{ inputs.module-name }} deployment.yaml"
          git push origin manifest-${{ inputs.module-name }}

μ—¬κΈ°μ„œ 뒷뢀뢄이 살짝 λ‹€λ₯Έλ°, κ·Έ μ΄μœ λŠ” μ΄μ–΄μ„œ 적도둝 ν•˜κ² μŠ΅λ‹ˆλ‹€.

메인 μ›Œν¬ν”Œλ‘œμš°

이제 μœ„μ˜ 곡톡 μ›Œν¬ν”Œλ‘œμš°λ₯Ό ν™œμš©ν•΄ 전체 배포 ν”„λ‘œμ„ΈμŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•©λ‹ˆλ‹€.
핡심 μ•„μ΄λ””μ–΄λŠ” 3κ°€μ§€μ˜€μŠ΅λ‹ˆλ‹€.

  1. 선택적 λΉŒλ“œλ₯Ό μœ„ν•œ 필터링

    쑰건이 μ—†λ‹€λ©΄ μ–΄λ–€ νŒŒμΌμ„ 변경해도 전체 λΉŒλ“œκ°€ λ‹€ λŒμ•„κ°ˆ ν…Œλ‹ˆ, 필터링을 ν•΄ 쀄 ν•„μš”κ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
    λ‹€ν–‰νžˆ dorny/paths-filter λΌλŠ” μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ°Ύμ•˜κ³ , μ–΄λ–€ λͺ¨λ“ˆμ΄ λ³€κ²½λ˜μ—ˆλŠ”μ§€ νŒλ‹¨ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

  2. 병렬 처리λ₯Ό μœ„ν•œ Matrix Strategy

    λ™μΌν•œ μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ—¬λŸ¬ λ³€μˆ˜λ₯Ό μž…λ ₯ν•˜μ—¬ μ‹€ν–‰ν•˜κΈ° μœ„ν•΄ Matrix Strategyλ₯Ό μ‚¬μš©ν–ˆμŠ΅λ‹ˆλ‹€. 기본적으둜 λ³‘λ ¬λ‘œ μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ— λͺ©μ μ—λ„ λΆ€ν•©ν–ˆκ³ , 일뢀 섀정을 μ‘°κ±΄λΆ€λ‘œ μ œμ™Έν•˜λŠ” 것도 κ°€λŠ₯ν•΄μ„œ μœ„μ—μ„œ 얻은 ν•„ν„° 값을 톡해 λΉŒλ“œν•  λŒ€μƒμ„ μ§€μ •ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. μ—¬λŸ¬ 개의 λ³€μˆ˜λ₯Ό μ •μ˜ν•˜κ³  이λ₯Ό μ‘°ν•©ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 것도 κ°€λŠ₯ν•˜μ§€λ§Œ, μ €λŠ” 1개의 λ³€μˆ˜λ§Œ ν•„μš”ν•΄μ„œ κ·Έλ ‡κ²Œ ν•˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.

  3. Git Push 좩돌 λ°©μ§€ν•˜κΈ°

    이 뢀뢄이 κ°€μž₯ 고민을 많이 ν–ˆλ˜ λΆ€λΆ„μ΄μ—ˆμŠ΅λ‹ˆλ‹€. 일반적인 λ°©λ²•μœΌλ‘œλŠ” 병렬 ν”„λ‘œμ„ΈμŠ€μ—μ„œ 각각 μ½”λ“œ 변경을 ν–ˆμ„ 경우 μΆ©λŒμ„ ν”Όν•˜κΈ° μ–΄λ €μ›Œ λ³΄μ˜€μŠ΅λ‹ˆλ‹€. κ·Έλž˜μ„œ μƒκ°ν•œ 방법은 각각의 μ›Œν¬ν”Œλ‘œμš°μ—μ„œ 브랜치λ₯Ό ν•˜λ‚˜μ”© λ§Œλ“€μ–΄ 변경사항을 μ €μž₯ν•˜κ³ , λ§ˆμ§€λ§‰μ— 이λ₯Ό λͺ¨λ‘ ν•©μΉ˜λŠ” κ²ƒμ΄μ—ˆμŠ΅λ‹ˆλ‹€. 이 ν•©μΉ˜λŠ” λ‹¨κ³„λŠ” Matrix Strategyκ°€ μ„€μ •λœ 단계가 μ™„λ£Œλœ ν›„ μ§„ν–‰λ˜λ„λ‘ ν•˜μ—¬ ν•„μš”ν•œ 과정이 λͺ¨λ‘ μ‹€ν–‰λ˜μ—ˆμŒμ„ 보μž₯ν•˜λ„λ‘ ν•˜κ³ , μœ„μ—μ„œ κ΅¬ν•œ ν•„ν„° 쑰건을 기반으둜 μƒμ„±λœ 브랜치λ₯Ό ν•©μ³€μŠ΅λ‹ˆλ‹€.

μ™„μ„±λœ μ΅œμ’… 메인 μ›Œν¬ν”Œλ‘œμš°μ˜ ν˜•νƒœλŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

name: 전체 Workflow
on:
  push:
    branches:
      # ...
    paths:
      - "apps/**/*"
      - "common/**/*"

jobs:
  get-filter:
    runs-on: ubuntu-latest
    outputs:
      foo: ${{ steps.filter.outputs.foo }}
      bar: ${{ steps.filter.outputs.bar }}
      # ...
    steps:
      - uses: actions/checkout@v2
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          base: ${{ github.ref }}
          filters: |
            ...

  ci-cd:
    permissions:
      contents: read
      packages: write

    needs: [get-filter]

    strategy:
      matrix:
        module:
          - foo
          - bar
          # ...
        exclude:
          - module: ${{ needs.get-filter.outputs.foo != 'true' && 'foo' }}
          - module: ${{ needs.get-filter.outputs.bar != 'true' && 'bar' }}
          # ...

    uses: ./.github/workflows/common_workflow.yml
    with:
      module-name: ${{ matrix.module }}
      registry: ghcr.io
    secrets: inherit

  push-manifest:
    runs-on: ubuntu-latest
    needs: [get-filter, ci-cd]

    steps:
      - name: Checkout manifest repository
        # ...

      - run: |
          git fetch origin
          git branch -a
          git checkout main
          git config user.name "${{ github.actor }}"
          git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
      - name: Merge foo branch
        if: needs.get-filter.outputs.foo == 'true'
      - name: Merge bar branch
        if: needs.get-filter.outputs.bar == 'true'
      # ...
      - name: Push to main branch
        # ...

ν›„κΈ°

μƒˆλ‘œμš΄ μ›Œν¬ν”Œλ‘œμš° νŒ€μ›λ“€μ˜ λ°˜μ‘

λͺ‡ κ°€μ§€ μ‹œν–‰μ°©μ˜€λ₯Ό 더 κ±°μ³μ„œ, ν˜„μž¬λŠ” μΆ©λΆ„νžˆ ν…ŒμŠ€νŠΈκ°€ μ™„λ£Œλ˜μ—ˆκ³  운영 ν™˜κ²½μ—μ„œλ„ μ €μ˜ μ›Œν¬ν”Œλ‘œμš°λ₯Ό μ‚¬μš©ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. νŒ€μ›λ“€μ˜ λ°˜μ‘λ„ κ½€λ‚˜ μ’‹μ•˜μŠ΅λ‹ˆλ‹€.

ν•œλ„ 초과

λ‹€λ§Œ 이번 달 초 GitHub Actions μ‚¬μš©λŸ‰ ν•œλ„λ₯Ό λͺ¨λ‘ μ‚¬μš©ν•΄μ„œ, μž μ‹œ 배포λ₯Ό λ©ˆμΆ”κ²Œ 된 μ†Œμ†Œν•œ μ΄μŠˆκ°€ μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 아무리 생각해도 제 지뢄이 μ’€ μžˆλŠ” 것 κ°™λ„€μš” πŸ˜…

νšŒμ‚¬ 정보가 λ“€μ–΄ 있기 λ•Œλ¬Έμ— λͺ¨λ“  μ½”λ“œλ₯Ό κ³΅κ°œν•  μˆ˜λŠ” μ—†μ§€λ§Œ, κΆκΈˆν•˜μ‹  점은 μ΅œλŒ€ν•œ λ‹΅ν•΄ λ“œλ¦¬κ² μŠ΅λ‹ˆλ‹€ :)

참고 자료