포스트

[이제와서 시작하는 Docker 마스터하기 - 고급편 #6] Docker와 CI/CD

[이제와서 시작하는 Docker 마스터하기 - 고급편 #6] Docker와 CI/CD

“이제와서 시작하는 Docker 마스터하기” CI/CD(지속적 통합/지속적 배포)는 현대 소프트웨어 개발의 핵심입니다. Docker를 CI/CD 파이프라인에 통합하면 일관된 빌드와 배포가 가능해집니다. 이번 편에서는 주요 CI/CD 플랫폼에서 Docker를 활용하는 방법을 알아보겠습니다.

CI/CD와 Docker의 시너지

Docker가 CI/CD에 주는 이점

  1. 환경 일관성: 개발, 테스트, 프로덕션 환경 통일
  2. 재현 가능한 빌드: 동일한 이미지로 어디서나 같은 결과
  3. 빠른 배포: 이미지 기반 배포로 속도 향상
  4. 롤백 용이: 이전 버전 이미지로 즉시 롤백

CI/CD 파이프라인 플로우

flowchart LR
    subgraph 개발
        Dev[개발자] --> Code[Code Push]
        Code --> SCM[Git Repository]
    end
    
    subgraph CI[지속적 통합]
        SCM --> Build[Docker Build]
        Build --> Test[테스트 실행]
        Test --> Scan[보안 스캔]
        Scan --> Push[Registry Push]
    end
    
    subgraph CD[지속적 배포]
        Push --> Stage[Staging 배포]
        Stage --> Verify[검증]
        Verify --> Prod[Production 배포]
        Prod --> Monitor[모니터링]
    end
    
    Test -->|실패| Dev
    Scan -->|취약점| Dev
    Verify -->|문제| Stage
    Monitor -->|알림| Dev

GitHub Actions

처음에는 GitHub Actions 하나만 골라 테스트 → 이미지 빌드·게시 → 서버 배포의 연결을 따라가세요. 뒤의 GitLab, Jenkins, CircleCI는 같은 목적을 다른 도구로 구현하는 대안입니다. 모든 설정을 한 프로젝트에 동시에 적용하는 순서가 아닙니다.

1. 기본 워크플로우

파일 안의 테스트 명령, Docker 빌드 경로, SSH 대상 서버와 secrets는 프로젝트에 맞게 준비해야 합니다. 특히 마지막 원격 명령은 기존 myapp 컨테이너를 멈추고 새로 띄우는 예제입니다. 아래 Blue-Green 예제와 구분하여 읽고, 먼저 각 job이 어떤 산출물을 다음 단계로 넘기는지 확인하세요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# .github/workflows/docker.yml
name: Docker CI/CD

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build test image
        uses: docker/build-push-action@v6
        with:
          context: .
          target: test
          load: true
          tags: ${{ env.IMAGE_NAME }}:test
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Run tests
        run: |
          docker run --rm ${{ env.IMAGE_NAME }}:test npm test

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.event_name != 'pull_request'
    
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=ref,event=pr
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix={{branch}}-

      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          platforms: linux/amd64,linux/arm64

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
      - name: Deploy to production
        uses: appleboy/ssh-action@v0.1.5
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          key: ${{ secrets.DEPLOY_KEY }}
          script: |
            docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main
            docker stop myapp || true
            docker rm myapp || true
            docker run -d \
              --name myapp \
              --restart unless-stopped \
              -p 80:3000 \
              ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:main

2. 멀티 스테이지 Dockerfile (CI/CD용)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 테스트 스테이지
FROM node:24-alpine AS test
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run lint
CMD ["npm", "test"]

# 빌드 스테이지
FROM node:24-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 프로덕션 스테이지
FROM node:24-alpine AS production
RUN apk add --no-cache dumb-init
RUN addgroup -g 1001 -S nodejs && \
    adduser -S nodejs -u 1001
WORKDIR /app
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --chown=nodejs:nodejs package*.json ./
USER nodejs
EXPOSE 3000
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/index.js"]

GitLab CI/CD

1. .gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
stages:
  - test
  - build
  - deploy

variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: "/certs"
  IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

before_script:
  - docker info

test:
  stage: test
  image: docker:latest
  services:
    - docker:dind
  script:
    - docker build --target test -t test-image .
    - docker run --rm test-image
  only:
    - merge_requests
    - branches

build:
  stage: build
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker build -t $IMAGE_TAG .
    - docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
    - docker push $IMAGE_TAG
    - docker push $CI_REGISTRY_IMAGE:latest
  only:
    - main
    - develop

deploy-staging:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - |
      curl -X POST \
        -H "Authorization: Bearer $DEPLOY_TOKEN" \
        -H "Content-Type: application/json" \
        -d "{\"image\": \"$IMAGE_TAG\"}" \
        https://staging.example.com/deploy
  environment:
    name: staging
    url: https://staging.example.com
  only:
    - develop

deploy-production:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
    - mkdir -p ~/.ssh
    - ssh-keyscan -H $DEPLOY_HOST >> ~/.ssh/known_hosts
  script:
    - |
      ssh $DEPLOY_USER@$DEPLOY_HOST << EOF
        docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
        docker pull $IMAGE_TAG
        docker stop myapp || true
        docker rm myapp || true
        docker run -d \
          --name myapp \
          --restart unless-stopped \
          -p 80:3000 \
          -e NODE_ENV=production \
          $IMAGE_TAG
      EOF
  environment:
    name: production
    url: https://example.com
  when: manual
  only:
    - main

Jenkins Pipeline

1. Jenkinsfile

Jenkins를 선택한 경우: 빌드·테스트·게시·정리 파이프라인
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
pipeline {
    agent any
    
    environment {
        DOCKER_REGISTRY = 'registry.example.com'
        DOCKER_IMAGE = 'myapp'
        DOCKER_CREDENTIALS = 'docker-registry-credentials'
    }
    
    stages {
        stage('Checkout') {
            steps {
                checkout scm
            }
        }
        
        stage('Test') {
            steps {
                script {
                    docker.build("${DOCKER_IMAGE}:test", "--target test .")
                    docker.image("${DOCKER_IMAGE}:test").inside {
                        sh 'npm test'
                    }
                }
            }
        }
        
        stage('Build') {
            steps {
                script {
                    dockerImage = docker.build("${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${env.BUILD_NUMBER}")
                }
            }
        }
        
        stage('Push') {
            when {
                branch 'main'
            }
            steps {
                script {
                    docker.withRegistry("https://${DOCKER_REGISTRY}", DOCKER_CREDENTIALS) {
                        dockerImage.push()
                        dockerImage.push('latest')
                    }
                }
            }
        }
        
        stage('Deploy to Staging') {
            when {
                branch 'develop'
            }
            steps {
                sshagent(['staging-ssh-key']) {
                    sh '''
                        ssh user@staging.example.com << EOF
                        docker pull ${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${BUILD_NUMBER}
                        docker stop myapp || true
                        docker rm myapp || true
                        docker run -d \
                            --name myapp \
                            --restart unless-stopped \
                            -p 80:3000 \
                            ${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${BUILD_NUMBER}
                        EOF
                    '''
                }
            }
        }
        
        stage('Deploy to Production') {
            when {
                branch 'main'
            }
            input {
                message "Deploy to production?"
                ok "Deploy"
            }
            steps {
                sshagent(['production-ssh-key']) {
                    sh '''
                        ssh user@production.example.com << EOF
                        docker pull ${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${BUILD_NUMBER}
                        docker stop myapp || true
                        docker rm myapp || true
                        docker run -d \
                            --name myapp \
                            --restart unless-stopped \
                            -p 80:3000 \
                            -e NODE_ENV=production \
                            ${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${BUILD_NUMBER}
                        EOF
                    '''
                }
            }
        }
    }
    
    post {
        always {
            cleanWs()
            sh 'docker system prune -f'
        }
    }
}

2. Jenkins Docker 설정

1
2
3
4
5
6
7
8
9
10
// Jenkins 설정 스크립트
pipeline {
    agent {
        docker {
            image 'docker:dind'
            args '--privileged -v /var/run/docker.sock:/var/run/docker.sock'
        }
    }
    // ...
}

CircleCI

1. .circleci/config.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
version: 2.1

orbs:
  docker: circleci/docker@2.2.0

jobs:
  test:
    executor: docker/docker
    steps:
      - setup_remote_docker
      - checkout
      - docker/check
      - run:
          name: Build test image
          command: |
            docker build --target test -t myapp:test .
      - run:
          name: Run tests
          command: |
            docker run --rm myapp:test

  build-and-push:
    executor: docker/docker
    steps:
      - setup_remote_docker
      - checkout
      - docker/check
      - docker/build:
          image: $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
          tag: ${CIRCLE_SHA1:0:7},latest
      - docker/push:
          image: $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
          tag: ${CIRCLE_SHA1:0:7},latest

  deploy:
    docker:
      - image: cimg/base:stable
    steps:
      - add_ssh_keys:
          fingerprints:
            - "SO:ME:FI:NG:ER:PR:IN:T"
      - run:
          name: Deploy to server
          command: |
            ssh -o StrictHostKeyChecking=no user@server.com << EOF
              docker pull $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:latest
              docker stop myapp || true
              docker rm myapp || true
              docker run -d \
                --name myapp \
                --restart unless-stopped \
                -p 80:3000 \
                $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME:latest
            EOF

workflows:
  version: 2
  test-build-deploy:
    jobs:
      - test
      - build-and-push:
          requires:
            - test
          filters:
            branches:
              only:
                - main
                - develop
      - deploy:
          requires:
            - build-and-push
          filters:
            branches:
              only: main

ArgoCD와 GitOps

1. Kubernetes 배포 매니페스트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  labels:
    app: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: ghcr.io/username/myapp:latest
        ports:
        - containerPort: 3000
        env:
        - name: NODE_ENV
          value: production
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 5
          periodSeconds: 5

2. ArgoCD Application

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# argocd/application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: myapp
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/username/myapp-k8s-config
    targetRevision: HEAD
    path: k8s
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
    - CreateNamespace=true

보안을 위한 CI/CD

1. 이미지 스캔 통합

1
2
3
4
5
6
7
8
9
10
11
12
13
# GitHub Actions 예시
- name: Run Trivy vulnerability scanner
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
    format: 'sarif'
    output: 'trivy-results.sarif'

- name: Upload Trivy scan results to GitHub Security tab
  uses: github/codeql-action/upload-sarif@v2
  if: always()
  with:
    sarif_file: 'trivy-results.sarif'

2. 시크릿 관리

1
2
3
4
5
6
7
8
9
10
11
12
# GitHub Actions에서 시크릿 사용
- name: Build with secrets
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: ${{ steps.meta.outputs.tags }}
    secrets: |
      "npm_token=${{ secrets.NPM_TOKEN }}"
      "api_key=${{ secrets.API_KEY }}"
    build-args: |
      BUILDKIT_INLINE_CACHE=1

배포 전략

배포 전략 비교

전략 특징 장점 단점 적합한 경우
Blue-Green 두 환경 교체 빠른 롤백 리소스 2배 필요 빠른 롤백이 필수인 경우
카나리 점진적 배포 위험 최소화 복잡한 설정 새 기능 테스트
롤링 업데이트 순차적 교체 간단한 구현 일시적 비일관성 일반적인 업데이트
A/B 테스팅 트래픽 분할 실험 가능 복잡한 모니터링 기능 비교 테스트

1. Blue-Green 배포

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash
# blue-green-deploy.sh

NEW_COLOR="green"
OLD_COLOR="blue"

if docker ps | grep -q "myapp-green"; then
    NEW_COLOR="blue"
    OLD_COLOR="green"
fi

echo "Deploying to $NEW_COLOR environment"

# 새 버전 배포
docker run -d \
    --name myapp-$NEW_COLOR \
    --network myapp-network \
    -e COLOR=$NEW_COLOR \
    $IMAGE_TAG

# 헬스체크
sleep 10
if ! curl -f http://localhost:8080/health; then
    echo "Health check failed"
    docker stop myapp-$NEW_COLOR
    docker rm myapp-$NEW_COLOR
    exit 1
fi

# 트래픽 전환
docker exec nginx sed -i "s/myapp-$OLD_COLOR/myapp-$NEW_COLOR/g" /etc/nginx/nginx.conf
docker exec nginx nginx -s reload

# 이전 버전 제거
sleep 30
docker stop myapp-$OLD_COLOR
docker rm myapp-$OLD_COLOR

2. 카나리 배포

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Kubernetes에서 Flagger 사용
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
  name: myapp
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp
  progressDeadlineSeconds: 60
  service:
    port: 80
    targetPort: 3000
  analysis:
    interval: 1m
    threshold: 5
    maxWeight: 50
    stepWeight: 10
    metrics:
    - name: request-success-rate
      thresholdRange:
        min: 99
      interval: 1m
    - name: request-duration
      thresholdRange:
        max: 500
      interval: 30s

모니터링과 알림

파이프라인의 성공 여부는 테스트 통과, 레지스트리의 이미지 게시, 대상 서버의 실행 상태를 따로 확인하세요. 배포 명령이 끝난 뒤에는 실제 서비스 주소에서 응답을 받아야 합니다. 연습에서는 어떤 이미지 태그를 빌드했고 어떤 태그를 서버가 실행하는지 한 줄로 기록해보세요.

1. 배포 알림

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Slack 알림 예시
- name: Notify deployment
  uses: 8398a7/action-slack@v3
  with:
    status: ${{ job.status }}
    text: |
      Deployment ${{ job.status }}
      Repository: ${{ github.repository }}
      Branch: ${{ github.ref }}
      Commit: ${{ github.sha }}
      Author: ${{ github.actor }}
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
  if: always()

2. 배포 대시보드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Grafana 대시보드 설정
apiVersion: v1
kind: ConfigMap
metadata:
  name: deployment-dashboard
data:
  dashboard.json: |
    {
      "dashboard": {
        "title": "CI/CD Metrics",
        "panels": [
          {
            "title": "Deployment Frequency",
            "targets": [{
              "expr": "rate(deployments_total[5m])"
            }]
          },
          {
            "title": "Build Success Rate",
            "targets": [{
              "expr": "sum(rate(builds_total{status=\"success\"}[5m])) / sum(rate(builds_total[5m]))"
            }]
          }
        ]
      }
    }

베스트 프랙티스

CI/CD 톴 비교

톴 특징 Docker 지원 설정 복잡도 가격
GitHub Actions GitHub 통합 ★★★★★ 낮음 무료/종량제
GitLab CI GitLab 통합 ★★★★★ 중간 무료/기업
Jenkins 오픈소스 ★★★★☆ 높음 무료
CircleCI 클라우드 ★★★★★ 낮음 무료/종량제
Azure DevOps MS 통합 ★★★★☆ 중간 무료/기업
Travis CI 간단한 설정 ★★★★☆ 낮음 종량제

1. 태그 전략

1
2
3
4
5
6
7
8
# 시맨틱 버저닝
git tag v1.2.3
git push origin v1.2.3

# 자동 태깅
VERSION=$(cat VERSION)
git tag -a v$VERSION -m "Release version $VERSION"
git push origin v$VERSION

2. 캐시 최적화

1
2
3
4
5
6
7
8
9
10
11
12
13
# BuildKit 캐시
- name: Build with cache
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    tags: ${{ steps.meta.outputs.tags }}
    cache-from: |
      type=gha
      type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
    cache-to: |
      type=gha,mode=max
      type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max

빌드 성능 최적화 결과

gantt
    title Docker 빌드 시간 비교
    dateFormat mm:ss
    axisFormat %M:%S
    
    section 기본 빌드
    레이어 다운로드    :02:30, 150s
    소스 복사         :00:20, 20s  
    빌드 실행         :01:00, 60s
    
    section 캐시 사용
    캐시 확인         :00:05, 5s
    변경된 레이어만    :00:30, 30s
    빌드 실행         :00:15, 15s
    
    section 멀티스테이지 + 캐시
    병렬 빌드        :00:05, 5s
    변경 감지         :00:02, 2s
    최종 조립         :00:08, 8s

마무리

Docker와 CI/CD의 결합은 현대적인 소프트웨어 배포의 핵심입니다. 자동화된 테스트, 빌드, 배포를 통해 더 빠르고 안정적인 릴리스가 가능해집니다. 다음 편에서는 Docker Swarm을 통한 오케스트레이션을 알아보겠습니다.

다음 편 예고

  • Docker Swarm 아키텍처
  • 서비스와 스택 관리
  • 롤링 업데이트
  • 고가용성 구성

컨테이너 오케스트레이션의 세계로! 🎯

📚 Docker 마스터하기 시리즈

🐳 기초편 (입문자용 - 5편)

  1. Docker란 무엇인가?
  2. Docker 설치 및 환경 설정
  3. 첫 번째 컨테이너 실행하기
  4. Docker 이미지 이해하기
  5. Dockerfile 작성하기

💼 실전편 (중급자용 - 6편)

  1. Docker 네트워크 기초
  2. Docker 볼륨과 데이터 관리
  3. Docker Compose 입문
  4. 멀티 컨테이너 애플리케이션
  5. Docker Hub 활용하기
  6. Docker 보안 베스트 프랙티스

🚀 고급편 (전문가용 - 9편)

  1. Docker 로그와 모니터링
  2. Docker로 Node.js 애플리케이션 배포
  3. Docker로 Python 애플리케이션 배포
  4. Docker로 데이터베이스 운영
  5. Docker 이미지 최적화
  6. Docker와 CI/CD ← 현재 글
  7. Docker Swarm 기초
  8. 문제 해결과 트러블슈팅
  9. Docker 생태계와 미래
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.