ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Github Action์œผ๋กœ EC2 ๋ฐฐํฌ ์ž๋™ํ™”ํ•˜๊ธฐ
    Develop/DevOps 2022. 12. 16. 23:33

    ๐Ÿ’ก Github Actions์ด๋ž€?

    Github Action์€ build, test, deployment์™€ ๊ฐ™์€ workflow๋ฅผ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ๋Š” CI/CD ํ”Œ๋žซํผ์œผ๋กœ,
    github repository์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ชจ๋“  ์ด๋ฒคํŠธ(push, pull request, merge ๋“ฑ)์— ๋Œ€ํ•˜์—ฌ ์ •ํ•ด์ง„ ๋™์ž‘์„ ์‹คํ–‰์‹œํ‚ค๋„๋ก ํ•  ์ˆ˜ ์žˆ๋‹ค.

    ์ง„ํ–‰์ค‘์ธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” docker compose๋ฅผ ์ด์šฉํ•ด ์„œ๋น„์Šค ์ปจํ…Œ์ด๋„ˆ๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.
    ์†Œ์Šค์ฝ”๋“œ๊ฐ€ ์ˆ˜์ •๋  ๋•Œ๋งˆ๋‹ค ์ˆ˜๋™์œผ๋กœ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ญ์ œํ•˜๊ณ  ๋นŒ๋“œํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ…Œ์ŠคํŠธ ์„œ๋ฒ„๋ฅผ ์šด์˜ํ•˜๋‹ค๊ฐ€ Github action์„ ์‚ฌ์šฉํ•˜์—ฌ CI/CD๋ฅผ ์ž๋™ํ™”ํ•ด๋ณด๊ธฐ๋กœ ํ–ˆ๋‹ค.

     

    ๐Ÿค”  CI/CD ํ”„๋กœ์„ธ์Šค ์„ค๊ณ„ํ•ด๋ณด๊ธฐ

    ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค๋“ค์„ ์ž๋™ํ™”์‹œํ‚ค๋ ค ํ•˜์˜€๋‹ค.

    .github/workflow์— YAMLํŒŒ์ผ ์ƒ์„ฑ

    1. ์ƒ์„ฑ๋œ pull request์— ๋Œ€ํ•˜์—ฌ ์ž๋™์œผ๋กœ build ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ → ์‹คํŒจ์‹œ merge ๋ถˆ๊ฐ€

    2. main branch์— pull request๊ฐ€ merge๋œ ๊ฒฝ์šฐ, 
       a) production ์„œ๋ฒ„์— ๋™์ž‘ํ•˜๋˜ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ค‘์ง€ / dangling image๋ฅผ ์‚ญ์ œํ•˜์—ฌ ์„œ๋ฒ„ ์—ฌ์œ ๊ณต๊ฐ„ ํ™•๋ณด
       b) production ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด container๋“ค์„ ๋นŒ๋“œ
       c) container๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, rollback

    3. pull request๋ฅผ ํ™œ์šฉํ•œ ์†Œ์Šค์ฝ”๋“œ release, tag ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ ์ž๋™ํ™”

     

    ๐Ÿ”‘  Github๋ฅผ ํ™œ์šฉํ•œ CI/CD ํ™˜๊ฒฝ ๊ด€๋ฆฌ 

    Repository > Settings > Secrets๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์„ธํŒ…ํ•œ๋‹ค.
    workflow์—์„œ env ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๊ณ  secrets๊ฐ’์„ ๋„˜๊ฒจ์ฃผ๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ด€๋ฆฌํ–ˆ๋‹ค.

    Self-Hosted Runner๋ฅผ ๋“ฑ๋กํ•˜์—ฌ ๊ด€๋ฆฌ ์ค‘์ธ ์„œ๋ฒ„์—์„œ ์ž๋™์œผ๋กœ ๋นŒ๋“œ์™€ ๋ฐฐํฌ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋„๋ก ํ•˜์˜€๋‹ค.
    runner๋Š” ํ”„๋กœ์ ํŠธ์˜ EC2 instance์—์„œ daemon์œผ๋กœ ๋™์ž‘ํ•˜๊ณ  ์žˆ๋‹ค.

     

    โš™๏ธ  Workflow ๊ตฌํ˜„ํ•˜๊ธฐ

    1. build.yml
          - ์ƒ์„ฑ๋œ pull request์— ๋Œ€ํ•˜์—ฌ ์ž๋™์œผ๋กœ build ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” workflow
          - ubuntu ํ™˜๊ฒฝ ๊ตฌ์„ฑ / container์˜ ๋™์ž‘์„ ๊ด€๋ฆฌํ•˜๋Š” shell script๋ฅผ ์ˆ˜ํ–‰ํ•˜๋Š” step์„ ์‹คํ–‰ํ•˜๋„๋ก ๊ตฌํ˜„

    name: Build Test
    
    # pull request event์— ๋Œ€ํ•˜์—ฌ trigger๋˜๋Š” workflow
    on: [pull_request]
    
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          # ํ”„๋กœ์ ํŠธ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ์— checkoutํ•˜๊ณ  ์‹คํ–‰ํ•˜๋„๋ก ๋ช…์‹œ
          - name: Checkout source code
            uses: actions/checkout@v3
            
          # ํ•ด๋‹น workflow๋ฅผ python3.8 ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•˜๊ฒ ๋‹ค๊ณ  ๋ช…์‹œ
          - name: Setup python
            uses: actions/setup-python@v3
            with:
              python-version: "3.8"
        
          # ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ์— docker๋ฅผ ์„ธํŒ…ํ•˜๋Š” shell script๋ฅผ ์‹คํ–‰
          - name: Setup Docker
            run: sh scripts/setup-docker.sh
    
          # ${{ secrets.* }} ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Settings > Secrets > Actions์— ์ •์˜ํ•œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ณ 
          # .envํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ devํ™˜๊ฒฝ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ด€๋ฆฌ
          - name: Create env file
            run: |
              touch ${{ secrets.ENV_PATH }}
              {
                echo SECRET_KEY="\"${{ secrets.SECRET_KEY }}"\"
                echo RUN_ENV="\"${{ secrets.RUN_ENV }}"\"
                echo DOMAIN="\"${{ secrets.DOMAIN }}"\"
                echo PROD_ALLOWED_HOSTS='${{ secrets.PROD_ALLOWED_HOSTS }}'
                echo CORS_ORIGIN_WHITELIST='${{ secrets.CORS_ORIGIN_WHITELIST }}'
                ...
              } >> ${{ secrets.ENV_PATH }}
          
          # containter๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” shell script๋ฅผ ์ˆ˜ํ–‰
          - name: Build Docker containers
            run: sudo kill `sudo lsof -t -i:8084` && sh scripts/build-docker-compose.sh
    
          # container์˜ ๋™์ž‘ ์ƒํƒœ๋ฅผ ํ™•์ธํ•œ ๋’ค job์„ ์ข…๋ฃŒ
          - name: Check container running state
            run: |
              if [ $(docker ps --format "{{.Names}} {{.Status}}" | grep "Up" | wc -l) -ne 3 ]
              then
                echo "Build error while running docker-compose"
                exit 1
              else
                echo "Deploy Complete"
              fi

    2. schedule-deploy.yml
          - production ์„œ๋ฒ„์— ๋™์ž‘ํ•˜๋˜ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์ค‘์ง€ / dangling image๋ฅผ ์‚ญ์ œํ•˜์—ฌ ์„œ๋ฒ„ ๊ณต๊ฐ„์„ ํ™•๋ณดํ•˜๋Š” ๋™์ž‘์„ ๋ณ„๋„์˜ shell script๋กœ ์ž‘์„ฑํ•˜์—ฌ workflow์˜ step์—์„œ ์‹คํ–‰
          - production ์„œ๋ฒ„์— ์ƒˆ๋กœ์šด container๋“ค์„ ๋นŒ๋“œ ๋ฐ ๋ฐฐํฌํ•œ๋’ค ๋™์ž‘ ์ƒํƒœ๋ฅผ ํ™•์ธ
          - ์ •์ƒ์ ์œผ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ์ด์ „ ๋ฒ„์ „์˜ release๋กœ rollback

    name: Schedule Deployment
    
    # main branch๋ฅผ target์œผ๋กœ ํ•˜๋Š” pull request๊ฐ€ closed๋˜์—ˆ์„ ๊ฒฝ์šฐ์—๋งŒ ๋™์ž‘ํ•˜๋Š” workflow
    on:
      pull_request:
        branches:
          - main
        types:
          - closed
    
    jobs:
      checkout:
        # ํ”„๋กœ์ ํŠธ๋ฅผ buildํ•  machine(runner)๋ฅผ ๋ช…์‹œ
        runs-on: [ self-hosted, label-go ]
        # pull request๊ฐ€ merge๋œ ๊ฒฝ์šฐ์—๋งŒ checkout job์„ ์ˆ˜ํ–‰
        if: ${{ github.event.pull_request.merged == true }}
        steps:
          # runner๊ฐ€ workflow๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก workspace์— permission ๋ถ€์—ฌ
          - name: Set Permissions
            run: sudo chown -R $USER:$USER ${{ github.workspace }}
    
          # ํ”„๋กœ์ ํŠธ ์†Œ์Šค์ฝ”๋“œ๋ฅผ ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ์— checkoutํ•˜๊ณ  ์‹คํ–‰ํ•˜๋„๋ก ๋ช…์‹œ
          - name: Checkout source code
            uses: actions/checkout@v3
            
          # ํ•ด๋‹น workflow๋ฅผ python3.8 ํ™˜๊ฒฝ์—์„œ ์‹คํ–‰ํ•˜๊ฒ ๋‹ค๊ณ  ๋ช…์‹œ
          - name: Setup python
            uses: actions/setup-python@v3
            with:
              python-version: "3.8"
    
          # ๋ฆฌ๋ˆ…์Šค ํ™˜๊ฒฝ์— docker๋ฅผ ์„ธํŒ…ํ•˜๋Š” shell script๋ฅผ ์‹คํ–‰
          - name: Setup Docker
            run: sh scripts/setup-docker.sh
    
      deploy:
        runs-on: [ self-hosted, label-go ]
        # checkout job์ด ์™„๋ฃŒ๋˜์—ˆ์„ ๊ฒฝ์šฐ ๋™์ž‘ํ•˜๋„๋ก ์˜์กด๊ด€๊ณ„๋ฅผ ์„ค์ • 
        needs: checkout
        if: ${{ github.event.pull_request.merged == true }}
        steps:
          # ${{ secrets.* }} ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Settings > Secrets > Actions์— ์ •์˜ํ•œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๊ณ 
          # .envํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ devํ™˜๊ฒฝ์˜ ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ๊ด€๋ฆฌ
          - name: Create env file
            run: |
              touch ${{ secrets.ENV_PATH }}
              {
                echo SECRET_KEY="\"${{ secrets.SECRET_KEY }}"\"
                echo RUN_ENV="\"${{ secrets.RUN_ENV }}"\"
                echo DOMAIN="\"${{ secrets.DOMAIN }}"\"
                echo PROD_ALLOWED_HOSTS='${{ secrets.PROD_ALLOWED_HOSTS }}'
                echo CORS_ORIGIN_WHITELIST='${{ secrets.CORS_ORIGIN_WHITELIST }}'
                ...
              } >> ${{ secrets.ENV_PATH }}
    
          # container๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” shell script๋ฅผ ์ˆ˜ํ–‰
          - name: Build Docker containers
            run: sh scripts/build-docker-compose.sh
     
          # container์˜ ๋™์ž‘ ์ƒํƒœ๋ฅผ ํ™•์ธํ•œ ๋’ค job์„ ์ข…๋ฃŒ
          - name: Check container running state
            run: |
              if [ $(docker ps --format "{{.Names}} {{.Status}}" | grep "Up" | wc -l) -ne 3 ]
              then
                echo "Build error while running docker-compose"
                exit 1
              else
                echo "Deploy Complete"
              fi
    
      rollback:
        runs-on: [ self-hosted, label-go ]
        # checkout, deploy job์ด ๋ชจ๋‘ ์™„๋ฃŒ๋˜์—ˆ์„ ๊ฒฝ์šฐ ๋™์ž‘ํ•˜๋„๋ก ์˜์กด๊ด€๊ณ„๋ฅผ ์„ค์ • 
        needs: [ checkout, deploy ]
        # ์˜์กด๊ด€๊ณ„๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ๋Š” job๋“ค ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜์˜€์„ ๊ฒฝ์šฐ ๋™์ž‘ํ•˜๋„๋ก ์กฐ๊ฑด ์„ค์ •
        if: ${{ always() && contains(needs.*.result, 'failure') }}
        steps:
          # runner๊ฐ€ workflow๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋„๋ก workspace์— permission ๋ถ€์—ฌ
          - name: Set Action Runner Permissions
            run: sudo chown -R $USER:$USER ${{ github.workspace }}
    
          # rollback job์„ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•˜์—ฌ ๋งˆ์ง€๋ง‰์œผ๋กœ release๋œ ๋ฒ„์ „์˜ ์†Œ์Šค์ฝ”๋“œ๋ฅผ fetch
          - name: Fetch Latest Release
            id: fetch-latest
            uses: thebritican/fetch-latest-release@v1.0.3
            with:
              github_token: ${{ secrets.GITHUB_TOKEN }}
    
          # fetch-latest step์—์„œ fetchํ•œ ๋ฒ„์ „์˜ ์†Œ์Šค์ฝ”๋“œ๋กœ checkout
          - name: Checkout Latest Release source code
            uses: actions/checkout@v3
            with:
              ref: ${{ steps.fetch-latest.outputs.tag_name }}
    
          # container๋ฅผ ๋นŒ๋“œํ•˜๊ณ  ๋ฐฐํฌํ•˜๋Š” shell script๋ฅผ ์ˆ˜ํ–‰
          - name: Build Docker containers
            run: sh scripts/build-docker-compose.sh
    
          # container์˜ ๋™์ž‘ ์ƒํƒœ๋ฅผ ํ™•์ธํ•œ ๋’ค job์„ ์ข…๋ฃŒ
          - name: Check container running state
            run: |
              if [ $(docker ps --format "{{.Names}} {{.Status}}" | grep "Up" | wc -l) -ne 3 ]
              then
                echo "Build error while running docker-compose"
                exit 1
              else
                echo "Deploy Complete"
              fi

     

    3. auto-release.yml
          - pull request๋ฅผ ํ™œ์šฉํ•œ ์†Œ์Šค์ฝ”๋“œ release, tag ์ƒ์„ฑ ๋ฐ ๊ด€๋ฆฌ ์ž๋™ํ™”

    name: Auto Release
    
    # pull request๊ฐ€ opened / reopened / synchronize / edited / closed๋˜์—ˆ์„ ๋•Œ 
    # ๋™์ž‘ํ•˜๋Š” workflow
    on:
      pull_request:
        types: [opened, reopened, synchronize, edited, closed]
    
    jobs:
      release:
        runs-on: ubuntu-latest
        # pull request๊ฐ€ target์œผ๋กœ ํ•˜๋Š” branch๊ฐ€ main์ผ ๊ฒฝ์šฐ์—๋งŒ ์ˆ˜ํ–‰๋˜๋„๋ก ์กฐ๊ฑด์„ ์„ค์ •
        if: ${{ contains(github.base_ref, 'main') || contains(github.ref, 'main') }}
        steps:
          # workflow๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ์ฝ”๋“œ์— ๋Œ€ํ•ด์„œ๋งŒ sparse checkout ์ˆ˜ํ–‰
          - name: Sparse-checkout
            uses: lablup/sparse-checkout@v1
            with:
              patterns: |
                scripts
          # releaseํ•  version๋ช…์„ output์œผ๋กœ ์ €์žฅํ•˜๋Š” step ์ˆ˜ํ–‰
          - name: Extract version
            id: extract-version
            run: |
              # release๋ช…์€ pull request์˜ title์— ๋ช…์‹œ๋œ '์ˆซ์ž.์ˆซ์ž.์ˆซ์ž' ํฌ๋งท์œผ๋กœ ์„ค์ •
              # ํ•ด๋‹น ๊ทœ์น™์„ ์ค€์ˆ˜ํ•œ release๋ช…์ด ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ์ž˜๋ชป๋œ ๊ฐ’์ด ์ž…๋ ฅ๋˜์–ด ์žˆ๋Š” ๊ฒฝ์šฐ์—๋Š” ์ดํ›„์˜ step์„ ๋ชจ๋‘ skip
              version=$(echo '${{ github.event.pull_request.title }}' | egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')
              echo "::set-output name=version::$version"
    
          # ํ˜„์žฌ branch์™€ ์ด์ „ ๋ฒ„์ „ release ์‚ฌ์ด์˜ commit log๋“ค์„ CHANGELOG_RELEASE.md์— ์ €์žฅํ•˜๊ณ 
          # ์กด์žฌํ•˜๋˜ CHANGELOG.md๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” python script๋ฅผ ์‹คํ–‰
          - name: Auto Generate Changelog
            id: changelog
            if: ${{ steps.extract-version.outputs.version }}
            run: |
              python3 ./scripts/generate_changelog.py --version "${{ steps.extract-version.outputs.version }}" --tag "${{ github.head_ref }}"
    
         # CHANGELOG_RELEASE.md์— ๋ณ€๊ฒฝ๋‚ด์šฉ์ด ์กด์žฌํ•˜๋Š”์ง€, ์ฆ‰ ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ release๋ฅผ ์ƒ์„ฑํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ
         # ํ•ด๋‹น workflow์—์„œ ์„ค์ •ํ•œ github action(pull request)๊ฐ€ close๋˜๊ณ ,
         # extract-version step์—์„œ ์˜ฌ๋ฐ”๋ฅธ ํ˜•ํƒœ์˜ ๋ฒ„์ „๋ช…์„ ์–ป์–ด๋‚ธ ๊ฒฝ์šฐ์—๋งŒ ์ˆ˜ํ–‰ํ•˜๋„๋ก ์กฐ๊ฑด์„ ์„ค์ •
          - name: Get Changed Files
            id: changed-files
            if: ${{ github.event.action != 'closed' && steps.extract-version.outputs.version }}
            uses: tj-actions/changed-files@v31
    
          # ์ด์ „ step์ธ changed-files์—์„œ ๋ณ€๊ฒฝ๋œ ํŒŒ์ผ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ํŒŒ์ผ์„ staged ์ƒํƒœ๋กœ ์ „ํ™˜ํ•œ๋’ค ์ž๋™์œผ๋กœ ์ปค๋ฐ‹
          # ์ด ๋•Œ, ์ปค๋ฐ‹ํ•  ํŒŒ์ผ(CHANGELOG.md)๊ณผ ์ปค๋ฐ‹ ๋ฉ”์„ธ์ง€๋Š” with ๊ตฌ๋ฌธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ง€์ • 
          - name: Auto Commit Updated Changelog
            id: auto-commit-push
            if: ${{ github.event.action != 'closed' && steps.changed-files.outputs.any_changed == true && steps.extract-version.outputs.version }}
            uses: stefanzweifel/git-auto-commit-action@v4
            with:
              commit_message: "update: CHANGELOG.md"
              file_pattern: "CHANGELOG.md"
    
          # ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š” ๊ฒฝ์šฐ, ์ƒˆ๋กœ์šด ๋ฒ„์ „์˜ release์™€ tag๋ฅผ ์ƒ์„ฑ
          - name: Create Release with Tag
            if: ${{ github.event.action == 'closed' && github.event.pull_request.merged == true && steps.extract-version.outputs.version == true && contains(github.event.pull_request.title, 'release') }}
            uses: ncipollo/release-action@v1
            with:
              tag: ${{ steps.extract-version.outputs.version }}
              name: ${{ steps.extract-version.outputs.version }}
              bodyFile: "./CHANGELOG_RELEASE.md"
              skipIfReleaseExists: true

     

    ๐Ÿ“š  ๋งˆ๋ฌด๋ฆฌ

    schedule deploy workflow ์ˆ˜ํ–‰๊ฒฐ๊ณผ

    ์ด๋ ‡๊ฒŒ Github Action์„ ์‚ฌ์šฉํ•˜์—ฌ ๋นŒ๋“œ ๋ฐ ๋ฐฐํฌ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ฒ„์ „๊ด€๋ฆฌ๊นŒ์ง€ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์—ˆ๋‹ค.
    YAML ๋ฌธ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ workflow๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ณ  github ๊ณต์‹ ๋„ํ๋จผํŠธ๊ฐ€ ๋งค์šฐ ์ž˜ ์ •๋ฆฌ๋˜์–ด ์žˆ์–ด์„œ ํ™œ์šฉ์ด ๋งค์šฐ ํŽธ๋ฆฌํ–ˆ๋‹ค.
    ๋‹ค์Œ์—๋Š” ์ž๋™ํ™”ํ•˜๊ณ  ์‹ถ์€ action์„ ์ง์ ‘ github marketplace์— ๋ฐฐํฌํ•ด๋ณด๋Š” ๊ฒƒ์ด ๋ชฉํ‘œ๋‹ค!ใ…Žใ…Ž

     

    'Develop > DevOps' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

    Vagrant๋กœ VM ๊ฐœ๋ฐœํ™˜๊ฒฝ ๊ตฌ์„ฑ  (0) 2022.09.18
    ๋ณด์•ˆ์„ ์œ„ํ•œ AWS network ๊ตฌ์„ฑํ•˜๊ธฐ  (0) 2021.10.23

    ๋Œ“๊ธ€

Designed by Tistory.