๐Ÿณ ์ธํ”„๋ผ/Cloud & CI-CD

๋น„์ „๊ณต์ž๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” CI/CD ์ž…๋ฌธ/์‹ค์ „(2)

๊ณ„๋ž€์†Œ๋…„ 2024. 9. 24. 22:13


3. Docker + ๋ฐฑ์—”๋“œ(Spring Boot) ํ”„๋กœ์ ํŠธ์— CI/CD ์ ์šฉํ•˜๊ธฐ

 

  • 2๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์กด์žฌ
    • Docker: ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ํ”„๋กœ์ ํŠธ
    • Docker, CodeDeploy: ์ปจํ…Œ์ด๋„ˆ + ํ™•์žฅ์„ฑ ๊ณ ๋ คํ•œ ํ”„๋กœ์ ํŠธ

 

1. Docker: ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ํ”„๋กœ์ ํŠธ

  • ์ „์ฒด ํ๋ฆ„

  • ์žฅ์ 
    • Docker ๊ธฐ๋ฐ˜ ์„œ๋น„์Šค ์šด์˜ ์‹œ, ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ์ธํ”„๋ผ ๊ตฌ์กฐ
  • ๋‹จ์ 
    • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ๊ตฌํ˜„ or ์—ฌ๋Ÿฌ EC2์— ๋ฐฐํฌํ•ด์•ผ ํ•œ๋‹ค๋ฉด, ์ง์ ‘ Github Actions์— ์Šคํฌ๋ฆฝํŠธ ์ž‘์„ฑ, ๊ตฌํ˜„ ํ•„์š” -> ๋ณต์žก
  • ์‚ฌ์šฉ
    • ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ๊ตฌ์„ฑ ์‹œ
    • ์†Œ๊ทœ ๋ชจํ”„๋กœ์ ํŠธ


์‹ค์Šต 1

  • EC2์— Docker ์„ค์น˜, ECR ์„ธํŒ…
  •  Ubuntu์—์„œ Docker, Docker Compose ์„ค์น˜
$ sudo apt-get update && \
	sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common && \
	curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
	sudo apt-key fingerprint 0EBFCD88 && \
	sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" && \
	sudo apt-get update && \
	sudo apt-get install -y docker-ce && \
	sudo usermod -aG docker ubuntu && \
	sudo curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose && \
	sudo chmod +x /usr/local/bin/docker-compose && \
	sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
	
# ์ž˜ ์„ค์น˜๋๋Š” ์ง€ ํ™•์ธ
$ docker -v # Docker ๋ฒ„์ „ ํ™•์ธ
$ docker compose version # Docker Compose ๋ฒ„์ „ ํ™•์ธ
  • Github Actions์˜ IAM์— ๊ถŒํ•œ ์ถ”๊ฐ€
    • AmazonEC2ContainerRegistryFullAccess ๊ถŒํ•œ ์ถ”๊ฐ€
  • ECR ์ƒ์„ฑ

 

์‹ค์Šต 2

  • Docker ๊ธฐ๋ฐ˜์œผ๋กœ ํ”„๋กœ์ ํŠธ ์ˆ˜์ •
    • Dockerfile ์ž‘์„ฑ
    • .dockerignore ํŒŒ์ผ ์ƒ์„ฑ
#Dockerfile
FROM node:alpine

WORKDIR /usr/src/app

COPY . .

RUN npm install

RUN npm run build

EXPOSE 3000

CMD [ "node", "dist/main.js" ]

#.dockerignore ํŒŒ์ผ
node_modules
  •  EC2๊ฐ€ private ECR์— ์ ‘๊ทผํ•˜๋„๋ก ์…‹ํŒ…
    • ECR Docker Credential Helper ์„ค์น˜ 
    • Configuration ์„ค์ •
    • IAM Role ํ™œ์šฉํ•˜์—ฌ EC2์˜ ECR ์ ‘๊ทผ ๊ถŒํ•œ ๋ถ€์—ฌ
      • EC2์— ์—ฐ๊ฒฐ๋œ IAM Role์—AmazonEC2ContainerRegistryFullAccess ์ •์ฑ… ์ถ”๊ฐ€
# ECR Docker Credential Helper ์„ค์น˜ 
# Ubuntu์ผ ๊ฒฝ์šฐ
$ sudo apt update
$ sudo apt install amazon-ecr-credential-helper

# Configuration ์„ค์ •
# ~ ๊ฒฝ๋กœ์—์„œ .dockerf ํด๋” ๋งŒ๋“ค๊ณ , config.json ํŒŒ์ผ ์ƒ์„ฑ
# ~/.docker/config.json
{
	"credsStore": "ecr-login"
}
  • Docker ๊ธฐ๋ฐ˜์œผ๋กœ CI/CD ๊ตฌ์ถ•
    • Github Actions ํŒŒ์ผ ์ž‘์„ฑ
# .github/workflows/deploy.yml
name: Deploy To EC2

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Github Repository ํŒŒ์ผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
        uses: actions/checkout@v4

      - name: Node ์„ค์น˜
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: ์˜์กด์„ฑ(๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ) ์„ค์น˜
        run: npm ci

      - name: .env ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ
        run: |
          touch .env
          echo '${{ secrets.ENV }}' >> .env

      - name: ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์‹คํ–‰
        run: npm run test

      - name: AWS Resource์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ AWS credentials ์„ค์ •
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-2
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: ECR์— ๋กœ๊ทธ์ธํ•˜๊ธฐ
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2

      - name: Docker ์ด๋ฏธ์ง€ ์ƒ์„ฑ
        run: docker build -t instagram-server .

      - name: Docker ์ด๋ฏธ์ง€์— Tag ๋ถ™์ด๊ธฐ
        run: docker tag instagram-server ${{ steps.login-ecr.outputs.registry }}/instagram-server:latest

      - name: ECR์— Docker ์ด๋ฏธ์ง€ Pushํ•˜๊ธฐ
        run: docker push ${{ steps.login-ecr.outputs.registry }}/instagram-server:latest

      - name: SSH๋กœ EC2์— ์ ‘์†ํ•˜๊ธฐ
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USERNAME }}
          key: ${{ secrets.EC2_PRIVATE_KEY }}
          script_stop: true
          script: |
            docker stop instagram-server || true
            docker rm instagram-server || true
            docker pull ${{ steps.login-ecr.outputs.registry }}/instagram-server:latest
            docker run -d --name instagram-server -p 3000:3000 ${{ steps.login-ecr.outputs.registry }}/instagram-server:latest

 

2. Docker, CodeDeploy: ์ปจํ…Œ์ด๋„ˆ + ํ™•์žฅ์„ฑ ๊ณ ๋ คํ•œ ํ”„๋กœ์ ํŠธ

  • ์ „์ฒด ํ๋ฆ„

  • ์žฅ์ 
    • ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ์„œ๋ฒ„๊ฐ€ ์—ฌ๋Ÿฌ ๋Œ€์ด๋”๋ผ๋„ ์ž๋™ ๋ฐฐํฌ ์šฉ์ด
    • ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ ์ ์šฉ ์šฉ์ด
  • ๋‹จ์ 
    • CodeDeploy ์‚ฌ์šฉ -> ์ธํ”„๋ผ ๊ตฌ์กฐ ๋ณต์žก
  • ์‚ฌ์šฉ
    • ์ปจํ…Œ์ด๋„ˆ ๊ธฐ๋ฐ˜ ์ธํ”„๋ผ ๊ตฌ์„ฑ + ์—ฌ๋Ÿฌ ๋Œ€ + ๋ฌด์ค‘๋‹จ ๋ฐฐํฌ


์‹ค์Šต 

  • Github Actions ์ฝ”๋“œ ์ˆ˜์ •ํ•˜๊ธฐ
# .github/workflows/deploy.yml
name: Deploy To EC2

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Github Repository ํŒŒ์ผ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
        uses: actions/checkout@v4

      - name: JDK 17๋ฒ„์ „ ์„ค์น˜
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 17

      - name: application.yml ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ
        run: echo "${{ secrets.APPLICATION_PROPERTIES }}" > ./src/main/resources/application.yml

      - name: ํ…Œ์ŠคํŠธ ๋ฐ ๋นŒ๋“œํ•˜๊ธฐ
        run: ./gradlew clean build

      - name: AWS Resource์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ AWS credentials ์„ค์ •
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ap-northeast-2
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

      - name: ECR์— ๋กœ๊ทธ์ธํ•˜๊ธฐ
        id: login-ecr
        uses: aws-actions/amazon-ecr-login@v2
      - name: Docker ์ด๋ฏธ์ง€ ์ƒ์„ฑ
        run: docker build -t instagram-server .

      - name: Docker ์ด๋ฏธ์ง€์— Tag ๋ถ™์ด๊ธฐ
        run: docker tag instagram-server ${{ steps.login-ecr.outputs.registry }}/instagram-server:latest

      - name: ECR์— Docker ์ด๋ฏธ์ง€ Pushํ•˜๊ธฐ
        run: docker push ${{ steps.login-ecr.outputs.registry }}/instagram-server:latest

      - name: ์••์ถ•ํ•˜๊ธฐ
        run: tar -czvf $GITHUB_SHA.tar.gz appspec.yml scripts

      - name: S3์— ํ”„๋กœ์ ํŠธ ํด๋” ์—…๋กœ๋“œํ•˜๊ธฐ
        run: aws s3 cp --region ap-northeast-2 ./$GITHUB_SHA.tar.gz s3://instagram-server/$GITHUB_SHA.tar.gz

      - name: Code Deploy๋ฅผ ํ™œ์šฉํ•ด EC2์— ํ”„๋กœ์ ํŠธ ์ฝ”๋“œ ๋ฐฐํฌ
        run: aws deploy create-deployment
          --application-name instagram-server
          --deployment-config-name CodeDeployDefault.AllAtOnce
          --deployment-group-name Production
          --s3-location bucket=instagram-server,bundleType=tgz,key=$GITHUB_SHA.tar.gz
  • appspec.yml, ์Šคํฌ๋ฆฝํŠธ ํŒŒ์ผ ์ˆ˜์ •
    • appspec.yml -> ์ฝ”๋“œ ์œ ์ง€
    • scripts/start-server.sh
# appspec.yml -> ์ฝ”๋“œ ์œ ์ง€
version: 0.0
os: linux

files:
  # CodeDeploy๊ฐ€ S3๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ ํŒŒ์ผ ์ค‘ destination์œผ๋กœ ์ด๋™์‹œํ‚ฌ ๋Œ€์ƒ์„ ์ง€์ •ํ•œ๋‹ค.
  # / ์ด๋ผ๊ณ  ์ง€์ •ํ•˜๋ฉด S3๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ ์ „์ฒด ํŒŒ์ผ์„ ๋œปํ•œ๋‹ค.
  - source: /
    # CodeDeploy๊ฐ€ S3๋กœ๋ถ€ํ„ฐ ๊ฐ€์ ธ์˜จ ํŒŒ์ผ์„ EC2์˜ ์–ด๋–ค ๊ฒฝ๋กœ์— ์ €์žฅํ•  ์ง€ ์ง€์ •ํ•œ๋‹ค.
    destination: /home/ubuntu/instagram-server

permissions:
  - object: /
    owner: ubuntu
    group: ubuntu

hooks:
  ApplicationStart:
    - location: scripts/start-server.sh
      timeout: 60
      runas: ubuntu

# scripts/start-server.sh
#!/bin/bash

echo "--------------- ์„œ๋ฒ„ ๋ฐฐํฌ ์‹œ์ž‘ -----------------"
docker stop instagram-server || true
docker rm instagram-server || true
docker pull {ECR Repository ์ฃผ์†Œ}/instagram-server:latest
docker run -d --name instagram-server -p 8080:8080 {ECR Repository ์ฃผ์†Œ}/instagram-server:latest
echo "--------------- ์„œ๋ฒ„ ๋ฐฐํฌ ๋ -----------------"

 

4. ๋ฐฑ์—”๋“œ(Nest.js) ํ”„๋กœ์ ํŠธ์— CI/CD ์ ์šฉํ•˜๊ธฐ

 

5. Docker + ๋ฐฑ์—”๋“œ(Nest.js) ํ”„๋กœ์ ํŠธ์— CI/CD ์ ์šฉํ•˜๊ธฐ

 

6. ํ”„๋ก ํŠธ์—”๋“œ CI/CD ๊ตฌ์„ฑ์„ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ธฐ๋ณธ AWS ์ง€์‹ (๋ณด์ถฉ ๊ฐ•์˜)

 

7.์›น ํ”„๋ก ํŠธ์—”๋“œ ํ”„๋กœ์ ํŠธ์— CI/CD ์ ์šฉํ•˜๊ธฐ

 

S3

  • ๊ธฐ๋Šฅ
    • ํŒŒ์ผ ์ €์žฅ
    • ์ •์  ์›น ์‚ฌ์ดํŠธ ํ˜ธ์ŠคํŒ… = ์›น ์„œ๋น„์Šค๋ฅผ ์ธํ„ฐ๋„ท์— ๋ฐฐํฌ
    • ๋ฒ„ํ‚ท: ๊นƒํ—ˆ๋ธŒ์˜ Repository์ฒ˜๋Ÿผ S3์˜ ์ €์žฅ์†Œ
      • ์ •์ฑ…: ๊ถŒํ•œ ์ •์˜ JSON ๋ฌธ์„œ
      • ex) ๋ฒ„ํ‚ท์—์„œ ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋Š” ์ •์ฑ…์„ ์ถ”๊ฐ€ํ•œ๋‹ค.
    • ๊ฐ์ฒด: S3์— ์—…๋กœ๋“œํ•œ ํŒŒ์ผ

CloudFront

  • CDN(Content Delivery Network)
  • ๊ธฐ๋Šฅ: ํŒŒ์ผ, ๋™์˜์ƒ ๋“ฑ์˜ ์ปจํ…์ธ ๋ฅผ ๋น ๋ฅด๊ฒŒ ์ „์†ก ํ•ด์ฃผ๋Š” ์„œ๋น„์Šค
  • ๊ฐ€๊นŒ์šด ์œ„์น˜์— ์ปจํ…์ธ ์˜ ๋ณต์‚ฌ๋ณธ์„ ์ €์žฅํ•˜์—ฌ ์ž„์‹œ ์ €์žฅ์†Œ๋กœ ์‚ฌ์šฉ
  • ๋ฌด์—‡๋ณด๋‹ค HTTPS๋ฅผ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” CloudFront๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

 

S3 ๋ฒ„ํ‚ท ์„ธํŒ…

S3 ๋ฒ„ํ‚ท ์ƒ์„ฑ

  • S3 -> ๋ฒ„ํ‚ท ๋งŒ๋“ค๊ธฐ -> ๋ฒ„ํ‚ท ์ด๋ฆ„ ์ง€์ •ํ•˜๊ณ  -> ์ด ๋ฒ„ํ‚ท์˜ ํผ๋ธ”๋ฆญ ์•ก์„ธ์Šค ์ฐจ๋‹จ์„ ๋ชจ๋‘ ๋น„ํ™œ์„ฑํ™”ํ•ด์•ผํ•œ๋‹ค(์ด ํŽ˜์ด์ง€ ์ ‘์†ํ•ด์„œ ๋‹ค์šด๋ฐ›๋Š” ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค์šด๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—) -> ๋ฒ„ํ‚ท ๋งŒ๋“ค๊ธฐ

๋ฒ„ํ‚ท์— ์ •์ฑ… ์ถ”๊ฐ€ํ•˜๊ธฐ

  • ๊ถŒํ•œ -> ๋ฒ„ํ‚ท ์ •์ฑ… -> ํŽธ์ง‘ -> ์ƒˆ ๋ฌธ ์ถ”๊ฐ€ -> S3๊ฒ€์ƒ‰ -> GetObject -> ๋ฆฌ์†Œ์Šค ์ถ”๊ฐ€(์„œ๋น„์Šค:S3, ๋ฆฌ์†Œ์Šค ์œ ํ˜•:object, ๋ฆฌ์†Œ์Šค ARN:arn:aws:s3:::{๋‚ด๊ฐ€ ๋งŒ๋“  ๋ฒ„ํ‚ท๋ช…}/*) -> ์ •์ฑ…์—์„œ Principal์„ "*"๋กœ ๋ณ€๊ฒฝ -> ์ €์žฅ

 

S3์— ์—…๋กœ๋“œ

S3์— ์—…๋กœ๋“œ

  • ํ…Œ์ŠคํŠธ ์œ„ํ•œ ๊ฐ„๋‹จํ•œ ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.
  • index.html
<h1>Title</h1>
  • Amazone S3 -> ๋ฒ„ํ‚ท -> ๋ฒ„ํ‚ท์ด๋ฆ„ -> ํŒŒ์ผ ๋“œ๋ž˜๊ทธํ•ด์„œ ์—…๋กœ๋“œ
  • ๋ฒ„ํ‚ท ๋“ค์–ด๊ฐ€์„œ ์—…๋กœ๋“œ ๋ˆ„๋ฅด๋ฉด ์˜ฌ๋ผ๊ฐ„๊ฒƒ ํ™•์ธ ๊ฐ€๋Šฅ

S3์— ์ •์  ์›น ํ˜ธ์ŠคํŒ… ์„ค์ •

  • ๋ฒ„ํ‚ท -> ์†์„ฑ -> ๋ฐ‘์— ์ •์  ์›น ์‚ฌ์ดํŠธ ํ˜ธ์ŠคํŒ… -> ํŽธ์ง‘ -> ํ™œ์„ฑํ™”, ์ •์  ์›น ์‚ฌ์ดํŠธ ํ˜ธ์ŠคํŒ… ์„ ํƒ -> ์ธ๋ฑ์Šค ๋ฌธ์„œ: index.html -> ์ €์žฅ
  • ๋งจ ๋ฐ‘์— ์ƒˆ๋กœ์šด ๋งํฌ๊ฐ€ ์ƒ๊ธด๊ฒƒ ํ™•์ธ ๊ฐ€๋Šฅ

์ด์ œ ์„ฑ๋Šฅ ๋ฌธ์ œ์™€, https ์ ์šฉ์„ ์œ„ํ•ด์„œ CloudFront๋ฅผ ์ ์šฉํ•ด๋ณด์ž

 

CloudFront ์ƒ์„ฑ

  • CloudFront -> ๋ฐฐํฌ์ƒ์„ฑ -> ์›๋ณธ ๋„๋ฉ”์ธ ์ž…๋ ฅ -> ์ด๋•Œ S3 ๋ฒ„ํ‚ท ์—”๋“œํฌ์ธํŠธ๋ง๊ณ  S3 ์ •์  ํ˜ธ์ŠคํŒ… ์›น ์‚ฌ์ดํŠธ ์—”๋“œํฌ์ธํŠธ ์„ ํƒ-> HTTP,HTTPS์— ๋Œ€ํ•œ ์ •์ฑ… ์„ ํƒ: Redirect HTTP to HTTPS -> WAF ์„ค์ •: ๋ณด์•ˆ ๋ณดํ˜ธ ๋น„ํ™œ์„ฑํ™” -> ์„ค์ • -> ๊ฐ€๊ฒฉ๋ถ„๋ฅ˜ ์„ค์ •: ๋ถ๋ฏธ, ์œ ๋Ÿฝ, ์•„์‹œ์•„, ์ค‘๋™ ๋ฐ ์•„ํ”„๋ฆฌ์นด์—์„œ ์‚ฌ์šฉ -> ๊ธฐ๋ณธ๊ฐ’ ๋ฃจํŠธ ๊ฐ์ฒด: index.html -> CloudFront ๋ฐฐํฌ ์ƒ์„ฑ -> ํ™•์ธ
  • ์šฐ๋ฆฌ๋Š” S3์˜ ์ฃผ์†Œ๊ฐ€ ์•„๋‹ˆ๋ผ CloudFront์˜ ์ฃผ์†Œ๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. ์ด๋•Œ,  ๋ฐฐํฌ ๋„๋ฉ”์ธ ์ด๋ฆ„์ด CloudFront์˜ ์ฃผ์†Œ

์ด์ œ CloudFront์— ๋„๋ฉ”์ธ์„ ์—ฐ๊ฒฐํ•˜๊ณ , HTTPS๋ฅผ ์ ์šฉํ•˜๋ฉด ๋œ๋‹ค.

 

 


์ถœ์ฒ˜

https://www.inflearn.com/course/%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90-ci-cd-%EC%9E%85%EB%AC%B8-%EC%8B%A4%EC%A0%84

 

๋น„์ „๊ณต์ž๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋Š” CI/CD ์ž…๋ฌธ·์‹ค์ „ ๊ฐ•์˜ | JSCODE ๋ฐ•์žฌ์„ฑ - ์ธํ”„๋Ÿฐ

JSCODE ๋ฐ•์žฌ์„ฑ | ๋น„์ „๊ณต์ž ์ž…์žฅ์—์„œ๋„ ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๊ณ , ์‹ค์ „์—์„œ ๋ฐ”๋กœ ์ ์šฉ ๊ฐ€๋Šฅํ•œ CI/CD ์ž…๋ฌธ ๊ฐ•์˜๋ฅผ ๋งŒ๋“ค์–ด๋ดค์Šต๋‹ˆ๋‹ค!, ๐Ÿคฌ ์—๋ผ์ด, ๋ชป ํ•ด๋จน๊ฒ ๋„ค!๋น„์ „๊ณต์ž๋กœ ๊ฐœ๋ฐœ์„ ์‹œ์ž‘ํ•ด ์—ฌ๋Ÿฌ ํšŒ์‚ฌ์—์„œ CTO๋กœ

www.inflearn.com