Docker를 활용해 개발 환경을 컨테이너화하고, 로컬 구축부터 서비스 배포까지 단계별로 실습하는 가이드입니다. 일관된 개발 환경을 경험하고 효율을 높여보세요.
📑 목차
Image by Olga_Fil on Pixabay
멈추지 않는 개발 환경 불일치 문제, 이제는 안녕!
혹시 이런 경험 있으신가요? "제 컴퓨터에서는 잘 되는데요..." 또는 "개발 환경 세팅에만 하루 종일 걸렸어요!" 프로젝트에 새로운 팀원이 합류하거나, 다른 컴퓨터에서 개발을 시작할 때마다 똑같은 문제에 부딪히곤 하죠. OS 버전, 라이브러리 의존성, 데이터베이스 설정까지... 신경 쓸 게 한두 가지가 아니잖아요? 이런 문제들 때문에 소중한 개발 시간을 낭비하는 경우가 정말 많습니다.
하지만 걱정 마세요! 오늘부터는 이런 불필요한 고민을 덜어줄 아주 훌륭한 도구를 소개해 드릴 거예요. 바로 Docker입니다. Docker를 활용하면 개발 환경을 컨테이너화해서, 누구든, 어디서든, 똑같은 환경에서 개발할 수 있게 된답니다. 마치 가상 머신처럼 환경을 격리하지만, 훨씬 가볍고 빠르다는 장점이 있죠.
이 가이드에서는 Docker를 사용해서 로컬 개발 환경을 구축하고, 나아가 실제 서비스 배포까지 어떻게 활용할 수 있는지 단계별로 자세히 알려드릴 거예요. 개발 환경 컨테이너화의 매력을 직접 경험해보고, 더 효율적인 개발 라이프를 시작해봅시다!
왜 Docker여야 할까요? 개발 환경의 혁신
전통적인 개발 환경에서는 다양한 문제에 직면하곤 했습니다. 예를 들어, 어떤 프로젝트는 Python 3.8을 쓰고 다른 프로젝트는 Python 3.10을 써야 한다고 가정해볼까요? 이 두 가지 환경을 하나의 컴퓨터에서 동시에 관리하는 건 정말 번거로운 일입니다. 가상 환경(Virtual Environment) 같은 도구를 사용하기도 하지만, 데이터베이스나 캐시 서버 같은 외부 서비스까지 관리하기엔 역부족이죠. 이런 문제들을 흔히 "의존성 지옥(Dependency Hell)"이라고 부르기도 해요.
이때 Docker가 등장합니다. Docker는 애플리케이션과 그 애플리케이션을 실행하는 데 필요한 모든 것을 컨테이너라는 독립적인 패키지로 묶어주는 기술이에요. 이 컨테이너는 OS 수준에서 격리되기 때문에, 어떤 환경에서 실행하더라도 항상 동일하게 작동합니다. 로컬 컴퓨터든, 테스트 서버든, 실제 운영 서버든 말이죠.
Docker 컨테이너 환경이 전통적인 개발 환경과 어떻게 다른지 아래 표를 통해 비교해볼까요?
| 항목 | 전통적인 개발 환경 | Docker 컨테이너 환경 |
|---|---|---|
| 환경 일관성 | OS, 라이브러리 버전에 따라 다를 수 있어 "내 컴퓨터에서는 되는데..." 문제 발생 | Dockerfile에 정의된 대로 항상 동일한 환경 제공, 불일치 문제 해소 |
| 의존성 관리 | 다수의 프로젝트에서 각기 다른 의존성 충돌 가능성 높음 | 각 컨테이너가 독립적으로 의존성을 관리하여 충돌 없음 |
| 환경 설정 시간 | 새로운 프로젝트나 팀원 합류 시 오랜 시간 소요 | Docker 이미지만 받으면 바로 실행 가능, 몇 분 내로 준비 완료 |
| 이식성 | 환경 종속적이라 다른 시스템으로 옮기기 어려움 | 컨테이너로 패키징되어 어떤 OS에서도 쉽게 실행 가능 |
| 격리 | 프로세스 간 간섭 가능성, 시스템 전체에 영향 줄 수 있음 | 각 컨테이너는 독립된 공간에서 실행되어 완벽한 격리 제공 |
어때요? Docker를 사용하면 개발 환경 설정과 관리의 복잡성이 현저히 줄어드는 것을 알 수 있죠? 이제 Docker를 직접 설치하고 간단하게 사용해보면서 그 매력을 더 깊이 느껴볼 시간입니다.
Docker 로컬 개발 환경 구축의 시작
Docker를 사용하기 위한 첫 단계는 로컬 컴퓨터에 Docker Desktop을 설치하는 것입니다. Docker Desktop은 Windows, macOS, Linux 등 다양한 운영체제를 지원하며, Docker 엔진과 Docker CLI, Docker Compose 등 Docker를 편리하게 사용할 수 있는 모든 도구를 한 번에 설치해줍니다. 공식 Docker 웹사이트에서 OS에 맞는 버전을 다운로드하여 설치 과정을 따라주시면 됩니다.
첫 Docker 이미지 실행하기
설치가 완료되었다면, 터미널(또는 명령 프롬프트)을 열고 Docker가 제대로 설치되었는지 확인해볼까요?
docker --version
docker run hello-world
docker --version 명령은 Docker 클라이언트와 서버의 버전을 보여줄 거예요. 그리고 docker run hello-world 명령을 실행하면, Docker가 hello-world라는 이미지를 Docker Hub에서 다운로드(없을 경우)하여 컨테이너로 실행하고, 터미널에 환영 메시지를 출력한 다음 종료될 거예요. 이 과정을 통해 Docker가 정상적으로 작동하는 것을 확인할 수 있습니다.
이제 조금 더 유용한 컨테이너를 실행해볼까요? 예를 들어, Ubuntu 운영체제 컨테이너를 실행하고 그 안으로 들어가보는 거예요.
docker run -it ubuntu bash
-it: 컨테이너와 상호작용할 수 있도록 터미널을 연결하고(-i), 가상 TTY를 할당(-t)하는 옵션입니다.ubuntu: Docker Hub에 있는 Ubuntu 이미지 이름입니다.bash: Ubuntu 컨테이너 안에서 실행할 명령어로, bash 셸을 시작하겠다는 의미예요.
이 명령을 실행하면, 여러분은 마치 새로운 Ubuntu 컴퓨터에 접속한 것처럼 bash 셸 프롬프트가 나타나는 것을 볼 수 있을 거예요. 여기서 ls -al 같은 리눅스 명령어를 실행해보세요. 컨테이너 안에서만 존재하는 파일 시스템을 확인할 수 있을 겁니다. exit를 입력하면 컨테이너에서 나올 수 있습니다.
자, 이제 몇 가지 핵심 Docker 명령어들을 알아볼까요?
docker pull [이미지 이름]: Docker Hub 등에서 이미지를 로컬로 다운로드합니다.docker images: 로컬에 다운로드된 모든 이미지 목록을 보여줍니다.docker ps: 현재 실행 중인 컨테이너 목록을 보여줍니다.-a옵션을 붙이면 모든 컨테이너(실행 중이 아닌 것도 포함)를 보여줘요.docker stop [컨테이너 ID 또는 이름]: 실행 중인 컨테이너를 중지합니다.docker rm [컨테이너 ID 또는 이름]: 컨테이너를 삭제합니다. (실행 중인 컨테이너는 중지해야 삭제 가능해요)docker rmi [이미지 ID 또는 이름]: 로컬 이미지를 삭제합니다.
이 기본적인 명령어들만 잘 알아두어도 Docker를 활용하는 데 큰 어려움이 없을 거예요.
Image by 2427999 on Pixabay
애플리케이션 컨테이너화 실습: 웹 앱과 데이터베이스
이제 이론은 충분히 다졌으니, 실제 애플리케이션을 컨테이너화하는 실습을 해볼까요? 간단한 Python Flask 웹 애플리케이션과 PostgreSQL 데이터베이스를 Docker 컨테이너로 묶어 로컬 개발 환경을 구축해볼 거예요.
프로젝트 폴더를 하나 만들고, 그 안에 다음 파일들을 생성해봅시다.
my-flask-app/
├── app.py
├── requirements.txt
├── Dockerfile
└── docker-compose.yml
Dockerfile 작성하기
먼저 Python Flask 웹 앱을 위한 Dockerfile을 작성해볼게요. Dockerfile은 Docker 이미지를 만드는 레시피라고 생각하시면 편합니다.
my-flask-app/app.py
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
return f"Hello from Docker! Running on port {os.environ.get('PORT', '5000')}"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=os.environ.get('PORT', 5000))
my-flask-app/requirements.txt
Flask==2.3.2
psycopg2-binary==2.9.9
my-flask-app/Dockerfile
# 베이스 이미지로 파이썬 공식 이미지를 사용합니다.
FROM python:3.9-slim-buster
# 작업 디렉토리를 /app으로 설정합니다.
WORKDIR /app
# 현재 디렉토리의 모든 파일들을 컨테이너의 /app 디렉토리로 복사합니다.
COPY . /app
# requirements.txt에 명시된 파이썬 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# Flask 앱이 사용할 포트를 외부에 노출합니다. (문서화 목적)
EXPOSE 5000
# 컨테이너가 시작될 때 실행할 명령어를 정의합니다.
CMD ["python", "app.py"]
위 Dockerfile은 다음과 같은 역할을 합니다.
FROM: 어떤 베이스 이미지를 사용할지 정의합니다. 여기서는 Python 3.9 버전을 사용하는 Debian 기반의 경량 이미지를 선택했죠.WORKDIR: 컨테이너 내부에서 작업할 디렉토리를 설정합니다.COPY: 로컬 파일(현재 디렉토리의 모든 내용)을 컨테이너 내부로 복사합니다.RUN: 이미지 빌드 시 실행될 명령어를 지정합니다. 여기서는 필요한 파이썬 라이브러리들을 설치하고 있어요.EXPOSE: 컨테이너가 특정 포트(여기서는 5000번)를 리스닝하고 있음을 알립니다.CMD: 컨테이너가 시작될 때 실행될 기본 명령어입니다.
Docker Compose로 다중 서비스 관리하기
우리 애플리케이션은 웹 앱과 데이터베이스, 두 개의 서비스로 구성되어 있죠? 이렇게 여러 컨테이너를 한 번에 정의하고 관리할 때는 Docker Compose를 사용하는 것이 아주 편리합니다. docker-compose.yml 파일을 작성해볼까요?
my-flask-app/docker-compose.yml
version: '3.8' # Docker Compose 파일 형식 버전
services:
web: # 웹 서비스 정의
build: . # 현재 디렉토리의 Dockerfile을 사용하여 이미지를 빌드합니다.
ports:
- "5000:5000" # 로컬 5000번 포트를 컨테이너의 5000번 포트에 연결합니다.
environment: # 환경 변수 설정
PYTHONUNBUFFERED: 1 # 파이썬 로그 버퍼링 방지
POSTGRES_HOST: db # 데이터베이스 호스트명 (db 서비스 이름)
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydatabase
depends_on: # db 서비스가 먼저 시작되도록 의존성 설정
- db
volumes: # 로컬 코드 변경사항을 컨테이너에 반영하기 위한 볼륨 마운트
- .:/app
db: # 데이터베이스 서비스 정의
image: postgres:14 # PostgreSQL 14 이미지를 사용합니다.
environment: # 환경 변수 설정 (PostgreSQL 초기 설정)
POSTGRES_DB: mydatabase
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes: # 데이터베이스 데이터를 로컬에 영구 저장하기 위한 볼륨 마운트
- db_data:/var/lib/postgresql/data
volumes: # Docker 볼륨 정의 (db_data)
db_data:
이 docker-compose.yml 파일은 웹 서비스(web)와 데이터베이스 서비스(db)를 정의하고 있습니다. 특히 주목할 부분은 다음과 같아요.
build: .:web서비스는 현재 디렉토리의 Dockerfile을 사용해서 이미지를 직접 빌드합니다.image: postgres:14:db서비스는 이미 존재하는 PostgreSQL 14 공식 이미지를 사용합니다.ports: "5000:5000": 로컬 컴퓨터의 5000번 포트로 들어오는 요청을web컨테이너의 5000번 포트로 전달합니다.depends_on: - db:web서비스가 시작되기 전에db서비스가 먼저 시작되도록 순서를 지정합니다.volumes: - .:/app: 로컬 개발 환경에서 코드 변경 시 컨테이너 내부의 코드도 자동으로 업데이트되도록 현재 디렉토리를 컨테이너의/app디렉토리에 마운트합니다.db_data:/var/lib/postgresql/data는 데이터베이스 데이터를 컨테이너가 삭제되어도 유지되도록 로컬 볼륨에 저장하는 역할을 합니다.
이제 이 모든 서비스를 한 번에 실행해볼까요? my-flask-app 디렉토리에서 다음 명령어를 실행해주세요.
docker compose up -d
-d 옵션은 컨테이너들을 백그라운드에서 실행하라는 의미입니다. 잠시 기다리면 Docker Compose가 Dockerfile을 빌드하고, PostgreSQL 이미지를 다운로드하고, 두 컨테이너를 모두 실행할 거예요. 웹 브라우저를 열고 http://localhost:5000으로 접속해보세요. "Hello from Docker!" 메시지를 볼 수 있을 겁니다. 이렇게 하면 웹 앱과 데이터베이스가 모두 Docker 컨테이너 안에서 성공적으로 실행된 것이죠!
작업을 마친 후에는 다음 명령어로 모든 컨테이너를 중지하고 삭제할 수 있습니다.
docker compose down
Docker Compose를 사용하면 복잡한 다중 서비스 애플리케이션도 이렇게 간단하게 관리할 수 있답니다. 정말 편리하죠?
CI/CD와 Docker: 개발부터 배포까지의 흐름
로컬 개발 환경에서 Docker의 강력함을 경험하셨다면, 이제 이 컨테이너화된 애플리케이션을 어떻게 실제 서비스에 배포할지에 대한 고민으로 이어지실 거예요. 이때 CI/CD (지속적 통합/지속적 배포) 파이프라인과 Docker의 조합이 빛을 발합니다.
CI/CD 파이프라인은 코드 변경이 발생했을 때, 자동으로 빌드, 테스트, 그리고 배포까지 진행하는 일련의 자동화된 과정을 의미합니다. Docker는 이 과정에서 일관된 실행 환경을 제공함으로써 CI/CD의 효율성과 안정성을 극대화합니다.
일반적인 CI/CD 흐름에서 Docker는 다음과 같이 활용될 수 있어요.
- 코드 변경 및 푸시: 개발자가 코드를 변경하고 Git 저장소(예: GitHub, GitLab)에 푸시합니다.
- CI 트리거: 코드 푸시 이벤트가 CI 툴(예: Jenkins, GitHub Actions, GitLab CI/CD)을 트리거합니다.
- Docker 이미지 빌드: CI 툴은 프로젝트의 Dockerfile을 사용하여 새로운 Docker 이미지를 빌드합니다. 이때, Dockerfile은 애플리케이션 코드와 모든 의존성을 포함하고 있죠.
- 테스트 실행: 새로 빌드된 Docker 이미지를 컨테이너로 실행하여 단위 테스트, 통합 테스트 등을 수행합니다. 컨테이너 환경 덕분에 테스트 환경이 개발 환경과 완벽하게 일치하므로, "내 컴퓨터에서는 잘 되는데..." 같은 문제가 발생하지 않습니다.
- Docker Registry로 푸시: 테스트를 통과한 Docker 이미지는 Docker Registry(예: Docker Hub, Amazon ECR, Google Container Registry 또는 사설 레지스트리)에 저장됩니다. Registry는 Docker 이미지들을 저장하고 관리하는 중앙 저장소 역할을 합니다.
- CD (지속적 배포): Registry에 저장된 이미지를 운영 서버로 가져와 새로운 컨테이너를 배포합니다. 이 과정 또한 자동화되어 있어서, 개발자는 코드만 푸시하면 나머지는 CI/CD 파이프라인이 알아서 처리하게 되는 거죠.
이렇게 Docker와 CI/CD를 결합하면, 개발 속도를 높이고 배포 오류를 줄이며, 팀 전체의 생산성을 크게 향상시킬 수 있습니다. 개발자는 코드 작성에만 집중하고, 배포는 자동화된 시스템에 맡길 수 있으니 얼마나 효율적인가요?
Image by yeiferr on Pixabay
Docker 컨테이너 서비스 배포 전략
이제 우리가 만든 Docker 컨테이너를 실제 사용자들이 접근할 수 있는 서비스로 배포하는 방법에 대해 알아볼 시간입니다. Docker를 활용한 배포는 크게 단일 서버 배포와 대규모 서비스 배포로 나눌 수 있어요.
단일 서버 배포 (Docker Swarm 또는 직접 관리)
작은 규모의 서비스나 MVP(Minimum Viable Product) 단계에서는 단일 서버에 Docker 컨테이너를 배포하는 것만으로도 충분할 수 있습니다. 이미 로컬에서 Docker Compose를 사용해본 경험이 있으니, 서버에서도 비슷하게 적용할 수 있어요.
- 서버 준비: 클라우드 서비스(AWS EC2, Google Cloud Compute Engine, Azure VM 등)나 온프레미스 서버에 Docker를 설치합니다.
- 코드 및 Docker Compose 파일 전송: Git을 통해 서버에 코드와 docker-compose.yml 파일을 클론하거나 SCP 등으로 전송합니다.
- 서비스 실행: 서버에서 해당 디렉토리로 이동하여 로컬에서와 동일하게
docker compose up -d명령어를 실행합니다.
이렇게 하면 서버에 여러 컨테이너가 동시에 실행되어 서비스를 제공하게 됩니다. 만약 여러 대의 서버에 컨테이너를 분산해서 배포하고 싶다면, Docker Swarm과 같은 컨테이너 오케스트레이션 도구를 사용할 수 있습니다. Docker Swarm은 Docker 자체에 내장된 기능으로, 여러 Docker 호스트를 하나의 가상 Docker 엔진처럼 관리하여 컨테이너를 분산 배포하고 관리할 수 있게 해줍니다.
대규모 서비스 배포 (Kubernetes)
서비스 규모가 커지고, 수십, 수백 개의 컨테이너를 관리해야 하며, 높은 가용성과 자동 확장이 필요하다면 Kubernetes(쿠버네티스)와 같은 전문적인 컨테이너 오케스트레이션 플랫폼이 필수적입니다. Kubernetes는 Docker 컨테이너를 대규모로 배포, 관리, 스케일링하는 데 최적화된 도구입니다.
Kubernetes는 다음과 같은 강력한 기능을 제공합니다.
- 자동 스케일링: 트래픽 증가에 따라 자동으로 컨테이너 수를 늘리고, 감소에 따라 줄여줍니다.
- 자가 복구: 컨테이너에 문제가 발생하면 자동으로 재시작하거나, 다른 노드에서 다시 실행하여 서비스 중단을 최소화합니다.
- 롤링 업데이트: 서비스 중단 없이 새로운 버전의 애플리케이션을 배포하고, 문제가 발생하면 이전 버전으로 쉽게 롤백할 수 있습니다.
- 로드 밸런싱: 여러 컨테이너로 들어오는 트래픽을 효율적으로 분산하여 안정적인 서비스를 제공합니다.
Kubernetes는 Docker 이미지를 기반으로 작동합니다. 즉, Dockerfile로 애플리케이션 이미지를 만들고, 이 이미지를 Registry에 푸시한 다음, Kubernetes에게 이 이미지를 사용하여 컨테이너를 배포하도록 지시하는 방식입니다. Kubernetes는 학습 곡선이 다소 높지만, 대규모 서비스를 운영하는 데 있어 사실상의 표준으로 자리 잡고 있는 만큼, 장기적으로는 반드시 익혀두면 좋은 기술이랍니다.
어떤 배포 전략을 선택하든, Docker를 통해 애플리케이션이 컨테이너화되어 있다는 것은 큰 장점입니다. 로컬 개발 환경에서부터 배포 환경까지, 모든 단계에서 "환경 불일치" 걱정 없이 일관된 방식으로 애플리케이션을 다룰 수 있으니까요.
Docker와 함께라면 개발이 즐겁죠!
오늘 우리는 Docker를 활용하여 개발 환경을 컨테이너화하는 여정을 함께 해봤습니다. Docker가 왜 필요한지부터 시작해서, 로컬에 Docker Desktop을 설치하고, 간단한 명령어를 사용해보며, 나아가 Python Flask 웹 앱과 PostgreSQL 데이터베이스를 Docker Compose로 묶어 실행하는 실습까지 진행했죠. 마지막으로는 CI/CD 파이프라인에서 Docker의 역할과 실제 서비스 배포 전략까지 폭넓게 다뤄봤습니다.
Docker는 "내 컴퓨터에서는 잘 되는데요..."라는 개발자들의 오랜 숙원을 해결해주는 강력한 도구입니다. 컨테이너라는 개념을 통해 개발 환경의 일관성, 효율성, 그리고 이식성을 극대화할 수 있다는 점이 가장 큰 매력이죠. 이제 더 이상 환경 설정 때문에 골머리 썩지 않고, 오로지 코드 작성과 기능 구현에만 집중할 수 있게 될 거예요.
이 가이드가 여러분의 Docker 학습에 큰 도움이 되었기를 바랍니다. 처음에는 조금 낯설게 느껴질 수도 있지만, 꾸준히 사용하다 보면 Docker 없는 개발 환경은 상상하기 어려울 정도로 편리함을 느끼실 거예요. 혹시 궁금한 점이나 여러분만의 Docker 꿀팁이 있다면 주저하지 말고 댓글로 공유해주세요! 함께 배우고 성장하는 개발 문화, 정말 멋지잖아요?
📌 함께 읽으면 좋은 글
- [튜토리얼] AWS Lambda와 API Gateway 활용 서버리스 REST API 구축: Python 기반 실전 배포 가이드
- [커리어 취업] 개발자 합격률 높이는 기술 이력서 포트폴리오 작성 전략: 실전 가이드
- [튜토리얼] Docker Compose를 활용한 다중 서비스 로컬 개발 환경 구축: 빠르고 효율적인 컨테이너 기반 워크플로우 가이드
이 글이 도움이 되셨다면 공감(♥)과 댓글로 응원해 주세요!
궁금한 점이나 다루었으면 하는 주제가 있다면 댓글로 남겨주세요.
'튜토리얼' 카테고리의 다른 글
| Next.js Prisma 풀스택 웹 서비스 개발 환경 구축: 타입스크립트 API 서버와 데이터베이스 연동 완벽 가이드 (0) | 2026.03.31 |
|---|---|
| WebRTC 실시간 화상 채팅 애플리케이션 구축: Signaling 서버와 P2P 연결 완벽 가이드 (0) | 2026.03.30 |
| AWS Lambda와 API Gateway 활용 서버리스 REST API 구축: Python 기반 실전 배포 가이드 (0) | 2026.03.28 |
| Prometheus Grafana 모니터링 시스템 구축: 지표 수집부터 시각화까지 완벽 가이드 (0) | 2026.03.28 |
| Minikube를 활용한 로컬 쿠버네티스 환경 구축: 웹 애플리케이션 배포 완벽 가이드 (0) | 2026.03.27 |