Docker & Containers Explained: Docker 작동 방식에 대한 초보자 친화적인 가이드
Source: Dev.to
현대 소프트웨어 개발은 더 이상 단순히 코드를 작성하는 것만을 의미하지 않습니다. 그 코드를 어디서든 안정적으로 실행하는 것—노트북, 테스트 환경, 그리고 실제 운영 환경—이 또한 중요합니다. 여기서 컨테이너화와 Docker가 등장합니다.
이번 블로그에서는 다음 내용을 쉽게 이해할 수 있도록 풀어보겠습니다:
- 컨테이너가 무엇인지
- Docker가 왜 만들어졌는지
- Docker가 내부적으로 어떻게 동작하는지
…완전히 처음 접하는 사람도 이해하기 쉬운 방식으로 설명합니다.
왜 우리는 컨테이너가 필요했는가
컨테이너가 등장하기 전에는 애플리케이션을 보통 서버나 가상 머신에 직접 배포했습니다. 이로 인해 여러 문제가 발생했습니다:
| 문제 | 설명 |
|---|---|
| 환경 드리프트 | 다양한 환경(개발, 테스트, 프로덕션)에서 동작이 달라짐 |
| 의존성 충돌 | 애플리케이션이 라이브러리, 런타임 등을 두고 충돌함 |
| 배포 및 롤백 어려움 | 수동 작업, 긴 릴리스 기간, 다운타임 |
| 확장성 부족 | 용량을 늘리려면 전체 서버/VM을 복제해야 함 |
개발자들은 애플리케이션과 실행에 필요한 모든 것을 함께 패키징하고, 어디서든 동일하게 실행할 수 있는 방법이 필요했습니다.
컨테이너란 무엇인가
컨테이너는 가볍고 휴대 가능한 단위로 다음을 패키징합니다:
- 애플리케이션 코드
- 런타임 (Node, Python, Java 등)
- 라이브러리 및 종속성
- 설정
컨테이너는 호스트 운영 체제 커널을 공유하지만 격리된 사용자 공간에서 실행되어 빠르고 효율적입니다.
선박 컨테이너 비유
- 내부 내용물은 선박에 중요하지 않다.
- 컨테이너는 선박, 트럭, 항구 사이를 자유롭게 이동할 수 있다.
- 내부에 있는 모든 것은 그대로 유지된다.
Virtual Machines vs. Containers
| Feature | Virtual Machines | Containers |
|---|---|---|
| OS | VM당 전체 게스트 OS | 호스트 OS 커널 공유 |
| Startup time | 분 | 초 또는 밀리초 |
| Resource usage | 무겁다 | 가볍다 |
| Isolation | 강력함 | 프로세스 수준 격리 |
| Portability | 제한적 | 매우 높음 |
Analogy:
VM은 각 손님에게 전체 집을 임대하는 것과 같습니다.
컨테이너는 같은 건물 안의 방을 임대하는 것과 같습니다.
컨테이너 이전의 문제점
- Dependency hell – 하나의 앱은 Node 16이 필요하고, 다른 앱은 Node 18이 필요 → 하나를 업그레이드하면 다른 앱이 깨짐.
- “Works on my laptop” 증후군 – 개발자 노트북에서는 동작하지만 QA에서는 실패하고, 운영 환경에서는 OS 버전, 라이브러리, 설정, 런타임 차이 때문에 깨짐.
- Scaling headaches – 트래픽 증가에 대비해 서버/VM을 복제하고, 모든 설정을 다시 구성하고, 기다려야 함.
- Manual deployments – 긴 릴리스 기간, 오류가 발생하기 쉬운 롤백, 빈번한 다운타임.
컨테이너가 해결하는 문제
- Same container runs everywhere – 환경 차이가 사라짐.
- Scale by running more containers – 빠르고 저렴하며 자동화됨.
- Rollback by switching container versions – 간단하고 신뢰성 있음.
Docker 소개
Docker는 다음을 도와주는 도구입니다:
- 애플리케이션 패키징 – 실행에 필요한 모든 것을 포함합니다.
- 어떤 머신에서도 동일하게 실행 – “앱을 한 번 빌드하면 어디서든 실행할 수 있습니다.”
환경을 수동으로 설정하는 대신, Docker는 컨테이너를 사용해 이를 자동화합니다.
Docker 이전
- 컨테이너는 존재했지만 사용하기 어려웠습니다.
- 각 회사마다 자체 도구가 있었습니다.
- 개발자들은 설정 및 일관성 문제에 어려움을 겪었습니다.
Docker의 해답
- 표준 포맷 –
Dockerfile및 이미지. - 간단한 명령 –
docker build,docker run. - 쉬운 이미지 공유 – 레지스트리(Docker Hub, GHCR, 사설 레지스트리).
Docker는 우리가 논의한 문제들을 직접 해결합니다:
| 문제 | Docker 해결책 |
|---|---|
| 의존성 충돌 | 각 앱마다 별도의 컨테이너를 사용 |
| 환경 불일치 | 동일한 이미지가 어디서든 실행 |
| 배포 지연 | 컨테이너를 몇 초 안에 시작 |
| 롤백 어려움 | 이미지 버전을 쉽게 전환 |
Docker가 복잡성을 없애는 것은 아닙니다—오히려 깔끔하게 패키징합니다.
현대 워크플로우에서 Docker의 위치
- Local development – 모든 개발자가 동일한 설정을 사용합니다.
- CI/CD pipelines – 예측 가능한 빌드와 테스트.
- Microservices – 각 서비스가 자체 컨테이너에서 실행됩니다.
- Cloud & Kubernetes – Docker 이미지가 표준 단위입니다.
Docker는 종종 first step toward:
- Kubernetes
- DevOps
- Cloud‑native architectures
Source: …
Docker 빌딩 블록
| Component | What it does |
|---|---|
| Docker Client | 사용자가 입력하는 (docker build, docker run) 명령이나 GUI가 호출하는 부분. 요청을 daemon에 보냅니다. |
Docker Daemon (dockerd) | 실제로 이미지를 빌드하고 컨테이너를 실행하는 백그라운드 서비스. |
containerd & runc | daemon이 이미지를 실행 중인 프로세스로 전환할 때 사용하는 저수준 도구들. 외우기보다는 Docker이 실제 프로세스 생성을 이 도구들에 위임한다는 점만 기억하면 됩니다. |
| Image | 레시피나 설계도와 같은 읽기 전용(frozen) 스냅샷. |
| Container | 이미지의 실행 인스턴스. 얇은 쓰기 가능한 레이어를 추가해 애플리케이션이 실행 중에 파일을 변경할 수 있게 합니다. |
| Dockerfile | 이미지를 빌드하기 위한 명령들을 담은 간단한 텍스트 파일(베이스 이미지, 복사할 파일, 실행할 명령 등). docker build가 이를 읽어 이미지를 생성합니다. |
| Registry | 이미지 저장소(Docker Hub, GitHub Container Registry, 사설 레지스트리 등). docker push와 docker pull을 통해 어디서든 이미지를 주고받을 수 있게 합니다. |
Docker가 의존하는 커널 기능
- Namespaces – 컨테이너마다 프로세스, 네트워크, 파일시스템 등을 독립적인 시각으로 제공합니다. 마치 각각의 방이 별도의 시야를 갖는 것과 같습니다.
- cgroups (control groups) – 컨테이너가 사용할 CPU, 메모리, 디스크 등을 제한합니다. 방의 전력 제한기와 같은 역할을 합니다.
이 두 기능이 결합돼 전체 OS를 구동하는 오버헤드 없이 격리된 환경을 제공합니다.
이미지 레이어
각 Dockerfile 단계는 레이어를 생성합니다. 레이어가 변경되지 않았다면 캐시가 활용되어, Dockerfile 순서를 적절히 잡으면 빌드 속도가 빨라집니다.
네트워킹
Docker는 각 컨테이너에 네트워크 인터페이스를 할당하고, 호스트 포트(-p host:container)를 매핑해 서비스에 접근할 수 있게 합니다.
볼륨
컨테이너 재시작 후에도 유지되어야 하는 데이터는 볼륨을 사용합니다. 볼륨은 컨테이너의 일시적인 쓰기 레이어 밖에 데이터를 보관합니다.
Example Dockerfile
# Use an official Node runtime as a parent image
FROM node:18-alpine
# Set working directory inside the container
WORKDIR /app
# Copy package.json and package-lock.json first (for caching)
COPY package*.json ./
# Install app dependencies
RUN npm ci --only=production
# Copy the rest of the application source code
COPY . .
# Expose the port the app runs on
EXPOSE 3000
# Define the command to run the app
CMD ["node", "index.js"]
이미지 빌드:
docker build -t my-node-app:1.0 .
컨테이너 실행:
docker run -d -p 8080:3000 --name my-app my-node-app:1.0
TL;DR
Docker는 앱을 패키징하고 어디서든 동일하게 실행할 수 있게 도와주는 작은 시스템입니다. 위에서 설명한 컨테이너, 이미지, Dockerfile, 레지스트리, 그리고 기본 커널 기능을 이해하면 Docker를 워크플로에 적용하고 현대적인 클라우드‑네이티브 아키텍처로 원활히 전환할 준비가 됩니다.
컨테이너화된 Node.js 앱 실행
이 섹션에서는 명령을 복사‑붙여넣기하여 실제로 컨테이너화된 앱을 실행합니다. 사전 Docker 경험은 필요 없습니다. 매우 간단한 Node.js 웹 서버를 컨테이너화합니다.
사전 요구사항
- Docker가 설치되어 있음(
docker --version명령이 작동해야 함) - 운영체제는 Windows / macOS / Linux 중 어느 것이든 상관없음
1. 프로젝트 폴더 설정
mkdir simple-docker-app
cd simple-docker-app
2. 애플리케이션 소스 생성
index.js
const http = require('http');
const PORT = 3000;
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello from Docker Container!');
});
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
package.json
{
"name": "simple-docker-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
}
}
3. Dockerfile 작성
다음 내용을 가진 Dockerfile(확장자 없음) 파일을 생성합니다:
# Use an official Node.js runtime
FROM node:18
# Set working directory inside container
WORKDIR /app
# Copy package files first (for caching)
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy application source code
COPY . .
# Expose application port
EXPOSE 3000
# Start the application
CMD ["npm", "start"]
4. Docker 이미지 빌드
Dockerfile과 같은 폴더에서 다음 명령을 실행합니다:
docker build -t simple-web-app .
This creates a Docker image called
simple-web-app.
5. 컨테이너 실행
docker run -p 3000:3000 simple-web-app
- 컨테이너 내부 포트 3000이 호스트의 포트 3000에 매핑됩니다.
6. 앱 확인
브라우저를 열고 다음 주소에 접속합니다:
http://localhost:3000
다음과 같은 화면이 표시됩니다:
Hello from Docker Container!
방금 무슨 일이 일어났나요?
- Docker는 애플리케이션 + Node.js + 설정을 하나의 이미지로 패키징합니다.
- 해당 이미지로부터 컨테이너가 생성됩니다.
- 컨테이너 내부 포트 3000이 여러분의 머신에 매핑되었습니다.
- 애플리케이션은 어디서든 동일하게 실행됩니다.
왜 중요한가
- 모든 개발자 노트북에서 앱을 실행할 수 있습니다.
- CI/CD 파이프라인에서 사용할 수 있습니다.
- 추가 설정 없이 클라우드 서버에서도 동작합니다.
Docker는 단순한 도구가 아니라 소프트웨어를 구축하고 배포하는 방식에 대한 근본적인 변화입니다.
컨테이너의 장점
- 환경 문제를 제거합니다.
- 배포를 단순화합니다.
- 자신 있게 확장할 수 있습니다.
이해했다면
- 컨테이너가 무엇인지.
- Docker가 존재하는 이유.
- Docker가 내부적으로 어떻게 동작하는지.
…실제 프로젝트에 Docker를 사용할 준비가 된 것입니다!