Docker를 이용한 컨테이너화와 CI/CD 파이프라인 구축

Docker 소개

Docker는 개발, 출시 및 실행을 위한 개방형 플랫폼입니다. Docker를 사용하면 인프라를 걱정하지 않고 바로 코드를 실행하는데 집중할 수 있습니다. Docker는 소프트웨어를 빠르게 배포하고 테스트하며 크고 복잡한 애플리케이션을 쉽게 관리할 수 있습니다.

Docker Hello World

가장 간단한 Docker 예는 hello-world 프로그램을 실행하는 것입니다. Docker가 제대로 작동하고 있는지 테스트하기 위한 용도로 사용되는 이 예제는 Docker 명령어를 이용하여 실행할 수 있습니다.


$ docker run hello-world

위 Docker 명령은 Docker 클라이언트에게 hello-world 이미지를 Docker Hub에서 찾아 Docker 컨테이너를 생성하고 실행하라고 지시합니다. 만약 로컬에서 이미지를 찾지 못한다면, Docker는 원격 레지스트리에서 이미지를 pull 합니다.


2. Docker 설치 가이드

2.1 Windows에서 Docker 설치

Docker Desktop을 설치하면 Windows에서 Docker를 이용할 수 있습니다. 아래 설치 과정을 참고하세요.

1. Docker Desktop for Windows를 다운로드하고 설치합니다.
2. Docker 패키지를 실행하면 설치가 시작됩니다.
3. 설치가 완료되면 Docker Desktop 앱을 실행합니다.

Docker가 정상적으로 설치되었는지 확인하려면 다음의 명령어를 이용합니다.


$ docker --version

2.2 Linux에서 Docker 설치

Ubuntu를 예로 들어 Docker를 설치하는 방법을 설명하겠습니다.

1. 터미널을 열고 아래의 명령어를 사용하여 Docker의 최신 버전을 설치합니다.
2. 리포지토리 업데이트를 위해 다음 명령어를 입력합니다.


$ sudo apt-get update

3. Docker 다운로드와 설치는 다음의 단계로 실행합니다.


$ sudo apt-get install docker-ce

2.3 MacOS에서 Docker 설치

Docker Desktop을 설치하면 MacOS에서 Docker를 이용할 수 있습니다. 다음 단계를 따르세요.

1. Docker Desktop for Mac을 다운로드하고 설치하시오.
2. Docker 패키지를 열어 설치를 시작합니다.
3. 설치 완료 후 Docker Desktop 앱을 실행합니다.


3. Docker 기본 사용법

3.1 Docker 이미지

Docker 이미지는 가벼운, standalone, 실행 가능한 소프트웨어 패키지로, 런타임에 필요한 모든 것을 포함하고 있습니다. 즉, 코드, 런타임, 시스템 도구, 시스템 라이브러리 및 설정 등입니다. 이미지를 다운로드하는 코드는 아래와 같습니다.


$ docker pull {IMAGE_NAME}

3.2 Docker 컨테이너

Docker 컨테이너는 Docker 이미지의 실행 인스턴스입니다. Docker 이미지를 실행하면 하나의 컨테이너 혹은 여러 컨테이너를 생성할 수 있습니다. 컨테이너를 실행하는 코드는 다음과 같습니다.


$ docker run {IMAGE_NAME}

컨테이너의 상태를 확인하는 코드는 아래와 같습니다.


$ docker ps

3.3 Docker 네트워크

Docker의 네트워킹 기능은 서로 다른 Docker 컨테이너가 통신할 수 있도록합니다. 컨테이너 간의 네트워크를 생성하는 코드는 다음과 같습니다.


$ docker network create {NETWORK_NAME}

4. Docker를 이용한 컨테이너화 이해

Docker를 이용한 컨테이너화는 개발자가 소프트웨어의 실행 환경을 완벽하게 제어할 수 있다는 장점이 있습니다. 따라서, “나의 컴퓨터에서는 잘 돌아가는데, 회사 서버에서는 왜 안돌아가지?”와 같은 문제를 피할 수 있습니다.

이를 이해하기 위해 간단한 Python 애플리케이션을 컨테이너화 해보겠습니다.

먼저, 우리의 애플리케이션과 Dockerfile을 포함하고 있는 디렉토리를 생성합니다.


$ mkdir my_python_app && cd my_python_app

그리고 `app.py`라는 간단한 Python 애플리케이션을 생성합니다.


print("Hello, Docker!")

다음으로, Dockerfile을 작성합니다. Dockerfile은 Docker 콘테이너 이미지를 만드는 방법을 정의합니다.


# 이 이미지는 공식 Python 런타임을 기반으로 합니다.
FROM python:3

# 작업 디렉토리를 /app으로 설정합니다.
WORKDIR /app

# 현재 디렉토리의 파일들을 컨테이너의 /app에 추가합니다.
ADD . /app

# 명령을 실행합니다.
CMD ["python", "app.py"]

마지막으로, 위의 Dockerfile을 이용하여 Docker 이미지를 빌드하고 컨테이너를 실행합니다.


$ docker build -t my_python_app .
$ docker run -it --rm --name my-running-app my_python_app

컨테이너를 실행하면, Python 애플리케이션의 출력인 “Hello, Docker!”를 볼 수 있습니다.

이처럼, Docker를 이용하면 어떤 환경에서든 동일하게 애플리케이션을 실행할 수 있습니다. 이는 개발-테스트-운영 환경 간 불일치 문제를 해결하고, 소프트웨어 배포 및 확장을 간소화합니다.


5. CI/CD 간략 소개

CI/CD는 Continuous Integration (지속적 통합) / Continuous Delivery (지속적 전달)의 약자이며, 소프트웨어 개발에서 사용되는 방법론입니다.

Continuous Integration (CI)

Continuous Integration은 개발자가 작업한 코드를 공유 저장소에 지속적으로 병합하는 프로세스를 말합니다. 이를 통해 여러 개발자가 동시에 작업하더라도 서로의 코드 변경사항을 빠르게 발견하고 이를 해결할 수 있습니다.


# 코드를 commit 하고 push 합니다.
$ git add .
$ git commit -m "Add new feature"
$ git push origin master

Continuous Delivery (CD)

Continuous Delivery는 개발된 코드를 사용자가 사용할 수 있는 환경에 지속적으로 안전하게 배포하는 프로세스를 말합니다. 이를 통해 새로운 기능이나 버그 수정이 빠르게 사용자에게 전달될 수 있습니다.


# 새로운 버전을 배포합니다.
$ git tag v1.0.0
$ git push origin v1.0.0

그러나, 실제로는 이러한 과정이 자동화 도구 (예: Jenkins, Travis CI, CircleCI 등)를 통해 자동적으로 실행되며, 코드의 테스트와 빌드, 배포까지 이루어집니다.

CI/CD는 개발 및 배포 속도를 높이고, 개발 팀의 협업을 향상시키며, 고품질의 소프트웨어를 빠르게 제공할 수 있도록 도와줍니다.


6. Docker와 CI/CD의 연결점 이해

Docker와 CI/CD는 잘 연결될 수 있으며, 이를 통해 개발 및 배포 과정을 더욱 효율적으로 만들 수 있습니다.

이를 이해하기 위해서는 먼저, Docker는 개발 환경을 표준화하고, 해당 환경을 어디서든 빠르게 재현할 수 있다는 점을 이해해야 합니다. 이렇게 되면 개발자는 어떤 환경에서든 동일하게 애플리케이션을 실행할 수 있고, 이는 배포 및 확장을 간소화합니다.

다음으로, CI/CD는 코드 변경 사항을 자동으로 테스트하고, 빌드하며, 프로덕션 환경에 배포하는 과정을 자동화합니다. 이렇게 되면 개발 및 배포 과정이 효율화되고, 따라서 생산성이 향상됩니다.

이 두 가지를 연결하면, Docker를 통해 만들어진 환경에서 CI/CD 파이프라인을 실행할 수 있습니다. 즉, 코드 변경 사항은 Docker 컨테이너 내에서 테스트되고 빌드되며, 그 결과는 동일한 Docker 컨테이너를 통해 프로덕션 환경에 배포될 수 있습니다.

다음은 Docker와 CI/CD를 연결하는 예시 중 일부를 보여줍니다.


# Docker 이미지를 빌드합니다.
$ docker build -t my_python_app .

# 빌드된 Docker 이미지를 Docker Hub에 푸시합니다.
$ docker push my_python_app

# CI/CD 파이프라인에서 Docker 이미지를 가져와 테스트를 실행합니다.
$ docker pull my_python_app
$ docker run -it --rm --name my-running-app my_python_app

이처럼, Docker와 CI/CD를 연결함으로써, 팀은 동일한 개발 환경을 공유할 수 있고, 코드 변경 사항은 빠르게 테스트되고 배포될 수 있습니다.


7. Docker를 활용한 CI/CD 파이프라인 구축

Docker를 활용하면 개발 환경을 테스트 환경, 스테이징 환경, 제품 환경에서 이식성 있는 형태로 관리할 수 있습니다. 이는 도커 이미지를 통해 수행되며, 이 이미지를 CI/CD 파이프라인에 통합함으로써 코드의 테스트, 빌드, 배포 과정을 자동화할 수 있습니다.

7.1 Docker 이미지 빌드 자동화

개발자는 Dockerfile을 작성하여 애플리케이션과 그 실행 환경을 자동화할 수 있습니다. 이 Dockerfile은 CI/CD 파이프라인의 한 단계에서 `docker build` 명령어를 사용해 자동화할 수 있습니다.


# Dockerfile 베이스 이미지
FROM python:3.7

# 작업 디렉터리 설정
WORKDIR /app

# 현재 Dockerfile의 경로를 /app에 복사
ADD . /app

# 파이썬 종속성 설치
RUN pip install -r requirements.txt

# 컨테이너 시작 시 실행 될 명령어 
CMD python main.py

여기서 Dockerfile을 실행하여 Docker 이미지를 빌드하고 이름을 붙이는 bash script는 다음과 같습니다.


# Docker 이미지를 빌드합니다.
$ docker build -t my_python_app:1.0 .

# Docker 이미지를 Docker Hub에 푸시합니다.
$ docker push my_python_app:1.0

7.2 Docker 컨테이너 배포 자동화

CI/CD 파이프라인에서 Docker 이미지가 성공적으로 빌드되면, 이 이미지는 프로덕션 환경에 배포될 준비가 됩니다. 이를 위한 Docker 컨테이너 배포를 자동화하는 bash script는 다음과 같습니다.


# Docker 이미지를 빌드합니다.
$ docker pull my_python_app:1.0

# Docker 이미지를 기반으로 컨테이너를 실행합니다.
$ docker run -d -p 80:5000 my_python_app:1.0 

이처럼, Docker와 CI/CD 파이프라인을 결합하면, 개발 환경을 표준화하고 코드 변경 사항을 빠르게 테스트하고 배포하는 과정을 자동화할 수 있습니다.


8. CI/CD 도구 소개

CI/CD(Continuous Integration/Continuous Deployment)의 주요 목표는 코드 변경 사항을 프로덕션 환경으로 안정적으로 빠르게 전달하는 것입니다. 여러 CI/CD 도구를 사용해 개발 팀이 이 목표를 달성할 수 있습니다.

8.1 Jenkins

Jenkins는 가장 널리 사용되는 오픈 소스 CI/CD 도구 중 하나입니다. Jenkins는 수천 개의 플러그인과 함께 제공되어, 거의 모든 유형의 빌드, 테스트 및 배포 작업을 지원합니다.

Jenkinsfile은 Jenkins에서 CI/CD 파이프라인을 정의하는 데 사용되는 Groovy 스크립트입니다.


pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
        stage('Test'){
            steps{
                echo 'Testing...'
            }
        }
        stage('Deploy'){
            steps{
                echo 'Deploying...'
            }
        }
    }
}

8.2 Travis CI

Travis CI는 클라우드 기반 CI/CD 서비스로, GitHub와 통합되어 코드 변경 사항을 자동으로 빌드하고 테스트하며 배포합니다. 설정은 프로젝트 루트에 있는 .travis.yml 파일에 작성됩니다.


language: python
python: 
- '3.6'  
install: 
- pip install -r requirements.txt
script: 
- python test.py

이처럼, Jenkins와 Travis CI 같은 CI/CD 도구를 통해 개발 팀은 코드 변경 사항을 빠르고 안정적으로 프로덕션에 배포하는 과정을 자동화할 수 있습니다.


9. Docker와 Jenkins를 이용한 CI/CD 파이프라인 구축 예시

Docker와 Jenkins를 이용하면 개발, 테스트, 배포 과정을 하나의 최종 아티팩트인 Docker 이미지로 통합하여 CI/CD 파이프라인을 구축할 수 있습니다.

먼저 Jenkins 서버가 설치되어 있는 환경에 Docker가 설치되어 있어야 합니다. Docker Plugin을 사용하여 Jenkins 내부에서 Docker 커맨드 명령을 사용할 수 있게 설정해야 합니다.

의존성이 있는 Python 애플리케이션을 예로 들면 아래와 같은 Dockerfile을 작성할 수 있습니다.


# Python 이미지를 기반으로 함
FROM python:3.8

# 작업 디렉토리 설정
WORKDIR /usr/src/app

# 의존성 파일 복사
COPY requirements.txt ./

# 의존성 설치
RUN pip install --no-cache-dir -r requirements.txt

# 소스 코드 복사
COPY . .

# 컨테이너를 실행할 때의 명령어 설정
CMD [ "python", "./main.py" ]

Jenkins에서 이 Dockerfile을 통해 이미지를 빌드하기 위한 Jenkinsfile은 다음과 같습니다.


pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                script { 
                    dockerImage = docker.build "my-python-app"
                }
            }
        }
        stage('Test'){
            steps{
                script {
                    docker.image("my-python-app").inside {
                        sh 'python test.py'
                    }
                }
            }
        }
        stage('Deploy'){
            steps{
                script {
                    docker.withRegistry('https://registry.hub.docker.com', 'docker-hub-credentials'){
                        dockerImage.push("1.0.${env.BUILD_NUMBER}")
                    }
                }
            }
        }
    }
}

Dockerfile로부터 생성된 Docker 이미지는 테스트에서 사용되며, 모든 테스트가 통과되면 해당 이미지는 Docker Hub와 같은 Docker 레지스트리에 push됩니다. 이 과정을 통해 CI/CD 파이프라인은 자동화되고 통합됩니다.


10. Docker와 Travis CI를 이용한 CI/CD 파이프라인 구축 예시

이제 Docker와 Travis CI를 이용하여 간단한 CI/CD 파이프라인을 구축하는 방법을 살펴보겠습니다. Travis CI는 소스 코드가 GitHub에 저장되어 있다는 가정하에 작동합니다.

먼저, GitHub으로부터 가져올 소스 코드와 함께 Dockerfile이 필요합니다. Node.js 애플리케이션을 예로 들면, 아래와 같은 Dockerfile을 작성할 수 있습니다.


# Node.js 이미지를 기반으로 함
FROM node:12

# 작업 디렉토리 생성
WORKDIR /usr/src/app

# 애플리케이션 파일 복사
COPY package*.json ./

# 의존성 설치
RUN npm install

# 소스 코드 복사
COPY . .

# 지정된 포트로 실행
EXPOSE 8080
CMD [ "node", "app.js" ]

다음으로, Travis CI가 이 Dockerfile을 이용해서 이미지를 빌드하고 Docker Hub에 push할 수 있도록 .travis.yml 파일을 루트 디렉토리에 생성합니다.


sudo: required
services:
  - docker

before_install: 
  - docker build -t my-node-app -f Dockerfile .

script:
  - docker run -e CI=true my-node-app npm test

deploy:
  provider: script
  script: bash docker_push
  on:
    branch: master

위의 .travis.yml 파일은 Travis CI가 Docker 이미지를 빌드하고, 테스트를 실행하고(실패 시 Travis CI 빌드 실패), 마지막으로 master 브랜치에 코드가 푸시될 때마다 Docker Hub에 이미지를 푸시하도록 설정합니다.

마지막으로, docker_push 스크립트는 Docker Hub에 로그인하고 master 브랜치에 푸시될 때마다 Travis CI에 의해 이미지를 푸시하는 역할을 합니다.

이렇게 Docker와 Travis CI를 통해 CI/CD 파이프라인을 구축하면, 코드가 변경될 때마다 자동으로 빌드, 테스트, 배포의 과정이 수행되어 소프트웨어 개발의 효율성을 높일 수 있습니다.


Leave a Comment