-
컨테이너
-
VM vs Container
-
가상 머신 (Virtual Machine)
-
컨테이너 (Container)
-
LXC (LinuX Contatiner)
-
컨테이너 기술의 장점
-
도커
-
Docker 이미지와 컨테이너
-
Docker Image
-
Docker Container
-
Image vs Container
-
Docker 컨테이너 다루기
-
컨테이너 애플리케이션 구축
-
컨테이너와 오케스트레이션 솔루션
-
Docker 이미지
-
Docker 이미지 생성
-
도커 이미지 생성 방법
-
도커 이미지 생성 방법 1
-
도커 이미지 생성 방법 2
-
도커 이미지 레이어
-
도커 이미지 배포
-
도커 이미지 배포 방법
-
도커 이미지 배포 1 - 파일 배포
-
도커 이미지 배포 2 - Public Registry
-
도커 이미지 배포 3 - Private Registry
-
도커 볼륨
-
도커 컨테이너의 구조 (레이어 구조)
-
볼륨(Volume)에 저장하기
-
Stateful vs Stateless 컨테이너
-
Docker 볼륨을 사용하는 세 가지 방법
-
볼륨 사용 1 - Host의 폴더를 직접 공유
-
볼륨 사용 2 - 볼륨 컨테이너 공유 (Legacy 방식)
-
볼륨 사용 3 - 도커가 지원하는 Volume을 사용 (docker volume create)
-
도커 볼륨을 사용하는 이유
-
도커 파일 (Dockerfile)
-
Infra as Code
-
IaC의 장점
-
IaC vs IaaS (Infra as a Service)
-
Dockerfile
-
필수 명령어
-
Docker 기본 구조
-
Dockerfile을 이용한 이미지가 만들어지는 과정
-
Image Layer를 줄이면 좋은 점
-
Dockerfile의 빌드 by Cache
-
추가적인 명령어
-
COPY vs ADD
-
EntryPoint vs CMD
-
Docker Container에 설정을 더 추가하고 싶다면?
-
docker attach vs docker exec
-
Multistage docker build : 최적화
-
기본적인 Dockerfile 예시
-
빌드/ 운영 분리 - Multi Stage
-
멀티 스테이지 빌드의 핵심
-
Docker Compose
-
Docker Compose의 필요성
-
Docker Compose의 주요 개념
-
설치 / 실행 / 종료
-
설치하기
-
실행하기
-
종료하기
-
Nginx + Spring Boot + MySQL + Redis 예제
-
폴더 구조
-
docker-compose.yml 파일 작성
-
backend/Dockerfile (Spring Boot용)
-
nginx/default.conf (Nginx 설정)
-
실행
컨테이너
- 리눅스 컨테이너 + 추가 기능 (오픈 소스)
- 애플리케이션을 컨테이너로써 사용할 수 있도록
- 가상 머신과 달리 성능 손실이 거의 없는 차세대 클라우드 솔루션
VM vs Container
가상 머신 (Virtual Machine)
- Hypervisor를 통한 가상화로 성능 손실이 발생한다.
- 완벽한 독립적인 공간을 생성하나, 이미지 용량이 매우 크고, 가상머신 배포에는 부담이 있다.
컨테이너 (Container)
- 컨테이너에 소스 배포하고, 이미지화한다.
- 리눅스 Chroot, 네임스페이스 등을 사용한 프로세스 단위 격리 환경 구성
- 애플리케이션 구동을 위한 라이브러리만 포함한 이미지 생성, 용량이 작음
- 컨테이너는 OS 자체를 가상화 하는게 아니라 Host OS 위에서 가벼운 프로세스 형태로 돌아가기 때문에 훨씬 빠르고 가볍다.
LXC (LinuX Contatiner)
- [컨테이너는 원래 리눅스의 네임스페이스(namespaces)와 cgroups(control groups)라는 기술을 기반으로 프로세스를 격리하는 개념]
- 리눅스 커널에 구현되어 있는 기술
- chroot(change root)
- Cgroup(Control group)
- Namespace
컨테이너 기술의 장점
- 경량화 : 하이퍼바이저와 게스트 OS가 없기 때문
- 게스트 OS의 부팅이 필요없다.
- 한정된 하드웨어 자원 안에서 VM보다 더 많은 컨테이너를 효율적으로 실행 가능
도커
Docker 이미지와 컨테이너
Docker Image
- 가상 머신 생성 시에 사용되는 ISO와 비슷한 개념의 이미지
- 여러 개의 층으로 된 바이너리 파일로 존재한다. : DockerFile
- Docker Pull, Docker Run과 같은 명령어로 이미지를 다운받고 실행한다.
- Docker가 설치된 Host OS에 이미지를 다운로드하는 명령어

Docker Container
- 도커 이미지로부터 생성
- 독립적인 공간을 생성한다.
- 일반적으로 도커 이미지 목적에 맞게 컨테이너를 새로 생성한다.
- 이미지는 읽기 전용으로 사용한다.
- 개별 컨테이너의 애플리케이션 설치/삭제는 다른 컨테이너에 영향이 없다.
- 도커 엔진 버전 확인
- 컨테이너 내부에서 나오기
- 이미지 내려받기
- 컨테이너 생성
- 컨테이너 목록 확인
- 컨테이너 이름 변경
- 컨테이너 삭제
Image vs Container
- 먼저 한 줄로 정리하면,
- 이미지와 컨테이너는 클래스와 인스턴스의 관계와 비슷하다.
Docker 컨테이너 다루기
- 만약, port를 안 열고 Container를 실행 시켰다면?
- port도 겹치면 안된다.
컨테이너 애플리케이션 구축
컨테이너와 오케스트레이션 솔루션
- 컨테이너 : 도커
- 오케스트레이션 솔루션 : k8s k3s EKS AKS ECS = 컨테이너를 관리하는 툴
Docker 이미지
- 애플리케이션과 의존성을 포함한 애플리케이션이 실행되기 위한 모든 것이 포함되어있는 파일
- 계층화 되었다.
- 도커 허브(중앙 저장소)에서 다운로드 가능하다.
- create, run, pull 로 이미지를 다운로드할 수 있다.
- 이미지 저장소 (레지스트리)를 직접 구축해서 사용 가능하다.
- 이미지와 컨테이너는 클래스와 인스턴스의 관계와 비슷하다.
Docker 이미지 생성
도커 이미지 생성 방법
- 현재 구동중인 Conatiner에 있는 그대로 image로 만든다. (긴급상황 시에)
- Dockerfile을 이용하여 생성을 선언 (기본적으로)
도커 이미지 생성 방법 1
- Container로 Image 생성
- 설치, 환경구성 변경 등을 직접 수작업
- 이미지 동작을 보장함
도커 이미지 생성 방법 2
- DockerFile 사용
도커 이미지 레이어
- commit 실행으로 새로운 이미지 생성 시 마다 계층화 되어 레이어 값이 쌓인다.
도커 이미지 배포
도커 이미지 배포 방법
- 파일 배포
- Public Registry
- Private Registry
도커 이미지 배포 1 - 파일 배포
- docker save 를 통해서 추출
- 도커 이미지 로드
- docker exprot / import 를 통해서 추출하기
- Load : Image에 container 정보들이 들어있음. Image의 Layer가 그대로 들어간다.
- Import : Image에 container 정보들이 없다. 기존 Layer가 없고, 새로운 Layer로 존재한다.
- 이미지를 파일로 추출하면 개수 만큼 디스크 공간을 차지한다.
도커 이미지 배포 2 - Public Registry

- 도커 허브 or AWS ECR에 올리기
- 도커 허브
도커 이미지 배포 3 - Private Registry
- 기업 내부에서 직접 도커 이미지 저장소를 구축하는 방법으로 많이 쓰인다.
- 사설 레지스트리 저장소 생성
- /etc/docker/daemon.json 파일에서 "insecure-registries" 설정을 추가
- 사설 레지스트리가 HTTPS 인증서를 사용하지 않을 때 필수
- 기본적으로 도커는 HTTPS를 요구하지만, 사설 레지스트리를 HTTP로 운영하면 보안 경고 발생
- "insecure-registries"를 추가해서 예외 처리 가능
도커 볼륨
- 도커 컨테이너는 읽기-쓰기가 가능한 레이어(컨테이너 레이어)를 가진다.
- 이미지 자체는 읽기 전용 (Read-Only)
- 컨테이너 변경 정보는 컨테이너 레이어에 저장된다.
도커 컨테이너의 구조 (레이어 구조)
- Docker Container는 베이스 이미지 + 여러 레이어로 구성됨
- 각 레이어는 변경될 수 있으며, 애플리케이션 업데이트 시 전체 VM을 교체할 필요 없음
볼륨(Volume)에 저장하기
- 컨테이너 데이터를 영구적으로 저장하는 방법
Stateful vs Stateless 컨테이너
- Stateless 컨테이너
- 데이터를 컨테이너 내부가 아닌 외부 볼륨에 저장하는 방식
- 컨테이너가 삭제되더라도 데이터는 유지된다.
- 예: 웹 서버(Nginx, Apache)
- Stateful 컨테이너
- 데이터를 컨테이너 내부에 저장하는 방식
- 컨테이너 삭제 시 데이터도 함께 삭제된다.
- 예: 데이터베이스(MySQL, PostgreSQL, MongoDB)
Docker 볼륨을 사용하는 세 가지 방법
- 다른 말로 “Docker에서 데이터를 영구적으로 저장하는 방법”
볼륨 사용 1 - Host의 폴더를 직접 공유

- 호스트 OS의 특정 디렉토리를 컨테이너 내부 경로와 연결하는 방식
- 호스트의 파일 시스템을 그대로 사용하므로, 컨테이너 삭제 후에도 데이터 유지될 수 있는 것
- 단점: 컨테이너가 다른 호스트에서 실행될 경우 데이터 공유가 어려움
📌 예제
docker run -d --name my-nginx \
-v /var/log/nginx:/var/log/nginx \
nginx
- 호스트의 nginx 디렉토리를 컨테이너 내부 nginx와 연결
- 컨테이너가 생성/삭제되더라도 호스트의 파일은 유지되는 것
볼륨 사용 2 - 볼륨 컨테이너 공유 (Legacy 방식)

- 여러 컨테이너가 하나의 데이터 컨테이너를 공유하도록 설정
- 데이터 컨테이너를 이용하면, 다른 컨테이너에서도 동일한 볼륨을 공유 가능
📌 예제
docker create -v /data --name data-container busybox
docker run --rm --volumes-from data-container my-app
- 단점: 공유 컨테이너가 삭제되면 볼륨도 사라질 수 있음
볼륨 사용 3 - 도커가 지원하는 Volume을 사용 (docker volume create)

- 도커의 내장된 볼륨 기능을 사용하는 방식
- 호스트 OS에 종속되지 않고, 여러 컨테이너에서 쉽게 공유 가능
- 가장 권장되는 방식의 볼륨이다.
📌 예제
docker volume create my-volume
docker run -d --name my-container -v my-volume:/app/data my-app
도커 볼륨을 사용하는 이유
- Host OS에 독립적인 볼륨을 생성
- 컨테이너가 삭제 후에도 볼륨은 유지된다.
- Container끼리 종속적이지 않다.
- 도커 볼륨은 호스트 파일 시스템의 특정 경로를 직접 노출하지 않기 때문에 보안적으로 더 안전하다.
도커 파일 (Dockerfile)
Infra as Code
- IaC, 인프라스트럭처 코드화
- 코드로써 관리되는 인프라
- 인프라 설치를 사람이 직접 수행하는 것이 아닌, 설치 시에 할일을 코드로써 작성하는 것
IaC의 장점
- Image 생성의 자동화
- 빠른 배포 & 스케일링
- 변경 이력 관리
- 내부 내용 명확하고 손쉽게 파악 가능
- 협업 & 재사용성
IaC vs IaaS (Infra as a Service)
- IaaS는 클라우드에서 인프라를 제공하는 개념이고
- IaC는 그 인프라를 코드로 자동화하는 방법임.
Dockerfile
- 컨테이너 빌드에 필요한 작업 명령이 저장된 파일이다.
- Working Directory의 “Dockerfile”이라는 이름의 파일을 인식하여 image를 생성한다.
필수 명령어
명령어
|
설명
|
FROM
|
어떤 기본 이미지를 사용할지 지정
|
WORKDIR
|
작업 디렉토리 설정 (cd처럼 생각하면 됨)
|
COPY
|
로컬 파일을 컨테이너 내부로 복사
|
RUN
|
컨테이너 내부에서 실행할 명령어 (ex. 패키지 설치)
|
CMD
|
컨테이너가 실행될 때 실행할 명령어 (기본 실행 프로세스)
|
Docker 기본 구조
# 1. 사용할 기본 이미지 선택
FROM python:3.9
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. 필요한 파일 복사
COPY . /app
# 4. 의존성 설치
RUN pip install -r requirements.txt
# 5. 컨테이너 실행 시 실행할 명령어
CMD ["python", "app.py"]
→ CI/CD의 경우 “.github/workflows/deploy.yml”와 연동해서 사용함.
- main 브랜치에 push가 되면 실행됨.
- 도커 이미지 빌드
- Docker Hub등에 업로드
- AWS EC2에 SSH로 접속 후 컨테이너 실행
Dockerfile을 이용한 이미지가 만들어지는 과정
- Dockerfile을 기반으로 이미지를 만들 때, 각 명령어 (ADD, RUN, COPY 등) 가 실행될 때마다 새로운 레이어(layer)가 생성된다.
Image Layer를 줄이면 좋은 점
- Docker 이미지에서 한 번 생성된 레이어는 그대로 유지된다.
- 즉, 프로그램을 RUN apt install로 설치했다가 RUN apt remove로 삭제해도, 이미 만들어진 레이어에는 여전히 삭제되기 전 상태가 남아 있음.
- SIZE : 설치, 삭제 따로 한 경우 <<< 설치, 삭제 같이 한 경우
Dockerfile의 빌드 by Cache
- Docker는 이미 실행된 명령어의 결과를 캐시에 저장하여 빠르게 빌드할 수 있음
- 의존성 파일을 먼저 COPY하고, 나중에 소스 코드를 복사하면 캐시를 더 잘 활용할 수 있음
- RUN 명령어를 여러 개 쓰지 말고 하나로 합쳐서 캐시 활용도를 높이자.
- 따로 설정할 것은 없지만,
- ⇒ 명령어 순서를 신경 써야 하고, 변경이 자주 발생하는 파일은 뒤쪽에 배치하는 게 좋다.
추가적인 명령어
- ENV (환경 변수 설정)
- Volume (데이터 저장 - 영속성 유지)
- ARG (빌드 시 사용되는 변수)
- User
- Onbuild
- Healthcheck
COPY vs ADD
- 둘 다 파일이나 디렉토리를 컨테이너 내부로 복사하는 명령어
- Copy : 단순히 파일/디렉토리을 이미지에 복사 (파일/디렉토리만 가능하다.)
- Add : 로컬파일, URL, tar 파일 등도 복사가 가능하다.
EntryPoint vs CMD
- 둘 다 컨테이너가 실행될 때 실행할 기본 명령어를 정의하는 명령어
- Entrypoint : 절대적으로 실행시킨다.
- CMD : 기본적으로 실행된다. (덮어쓰면 덮어씌여짐)
✔ ENTRYPOINT는 항상 실행되기 때문에 컨테이너 실행 시 필수적인 실행 파일을 보장함
✔ 커맨드라인에서 추가 입력하는 값들을 자동으로 실행 파일의 인자로 전달할 수 있음
✔ CMD와 달리 ENTRYPOINT는 덮어쓰기되지 않고, 기본 실행 프로그램으로 고정됨!
Docker Container에 설정을 더 추가하고 싶다면?
- 이미 실행중인 container에 Port나 volume 설정을 더 추가할 수는 없다.
- → 해결책
docker attach vs docker exec
- 둘 다 실행중인 컨테이너와 상호작용하는 명령어
- docker attach [ 컨테이너 id]
- Docker exet -it [컨테이너 id] /bin/bash
Multistage docker build : 최적화
- 여러 개의 빌드 단계를 거친 후, 최종 이미지를 생성하는 방식이야.
- 즉, 목적에 맞춰서 image를 분리하고, 필요한 것을 활용할 수 있다.
- 빌드 / 운영 분리
기본적인 Dockerfile 예시
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
- pip install 과정에서 빌드 시 필요한 패키지들이 최종 이미지에도 포함됨
- gcc, make 같은 컴파일러도 포함될 가능성이 있어 불필요한 용량 증가
빌드/ 운영 분리 - Multi Stage
# 1️⃣ 빌드 스테이지 (build 단계)
FROM python:3.9 AS builder
WORKDIR /app
# 필요한 파일만 복사 후, 패키지 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 2️⃣ 실행 스테이지 (최종 이미지)
FROM python:3.9
WORKDIR /app
# 빌드된 패키지만 복사 (불필요한 파일 제외)
COPY --from=builder /app /app
# 실행
CMD ["python", "app.py"]
멀티 스테이지 빌드의 핵심
- 빌드용 이미지(builder)에서 패키지를 설치하고, 최종 이미지에는 빌드 결과만 복사!
- 최종 이미지에는 불필요한 빌드 도구가 포함되지 않음 → 이미지 크기 최소화!
- 보안성 향상 (빌드 과정에서만 필요한 정보는 최종 이미지에서 사라짐)
참고:
GitHub awesome-compose/spring-postgres/backend/Dockerfile at master · docker/awesome-compose
Docker Compose
- 여러 개의 컨테이너가 하나의 애플리케이션으로 동작할 때, 한 번에 관리할 수 있도록 도와주는 도구
- docker run 명령어 없이 설정 파일(docker-compose.yml) 하나로 여러 컨테이너를 쉽게 실행, 관리 가능하다.
- 애플리케이션이 여러 개의 서비스(Nginx + DB + Redis 등)로 구성될 때 쉽고 일관되게 실행할 수 있음
- 한 줄로 모든 컨테이너를 실행(docker-compose up)하고, 중지(docker-compose down) 가능!
Docker Compose의 필요성
- 일일이 docker run을 해주려면…너무너무 길다…….
- 여러 개의 컨테이너를 한 번에 실행하고 관리하고 싶을 때
Docker Compose의 주요 개념
서비스(services)
|
하나의 컨테이너 역할 (예: nginx, springboot, mysql, redis)
|
볼륨(volumes)
|
컨테이너가 삭제돼도 데이터가 유지되도록 저장소 설정
|
네트워크(networks)
|
여러 컨테이너 간 통신을 가능하게 하는 네트워크
|
환경 변수(environment)
|
컨테이너 실행 시 필요한 환경 변수를 설정
|
설치 / 실행 / 종료
설치하기
- Linux/macOS (Docker가 이미 설치되어 있어야 함)
sudo apt-get install docker-compose -y # Ubuntu
brew install docker-compose # macOS (Homebrew 사용)
- 설치 확인
sudo apt-get install docker-compose -y # Ubuntu
brew install docker-compose # macOS (Homebrew 사용)
실행하기
docker-compose up -d # -d 옵션: 백그라운드 실행
종료하기
docker-compose down
Nginx + Spring Boot + MySQL + Redis 예제
폴더 구조
📌 폴더 구조
project/
│── backend/ # Spring Boot 프로젝트
│ ├── Dockerfile
│ ├── src/ ...
│── nginx/ # Nginx 설정 파일
│ ├── default.conf
│── docker-compose.yml
docker-compose.yml 파일 작성
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- "8080:80"
depends_on:
- backend
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
- app-network
backend:
build: ./backend
ports:
- "8081:8081"
depends_on:
- db
- redis
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/mydb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=rootpass
- REDIS_HOST=redis
- REDIS_PORT=6379
networks:
- app-network
db:
image: mysql:latest
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- app-network
redis:
image: redis:latest
ports:
- "6379:6379"
networks:
- app-network
volumes:
mysql_data:
networks:
app-network:
✅ 설명:
- nginx: 8080포트로 Spring Boot 서비스로 트래픽을 라우팅
- backend: Spring Boot 애플리케이션 실행
- db: MySQL 데이터베이스 실행
- redis: Redis 캐시 서버 실행
- volumes: MySQL 데이터 저장소 유지
- networks: 컨테이너 간 통신을 위한 네트워크 설정
backend/Dockerfile (Spring Boot용)
FROM openjdk:17
WORKDIR /app
COPY target/myapp.jar app.jar
CMD ["java", "-jar", "app.jar"]
nginx/default.conf (Nginx 설정)
server {
listen 80;
location / {
proxy_pass http://backend:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
실행
bocker-compose up -d
✅ Nginx, Spring Boot, MySQL, Redis 컨테이너가 한 번에 실행됨! 🚀
📌 웹 브라우저에서 확인
- http://localhost:8080 → Spring Boot 앱이 Nginx를 통해 연결됨
- http://localhost:8081 → Spring Boot API 직접 호출 가능
- mysql -h 127.0.0.1 -u root -p → MySQL 접속 가능
컨테이너
- 리눅스 컨테이너 + 추가 기능 (오픈 소스)
- 애플리케이션을 컨테이너로써 사용할 수 있도록
- 가상 머신과 달리 성능 손실이 거의 없는 차세대 클라우드 솔루션
VM vs Container
가상 머신 (Virtual Machine)
- Hypervisor를 통한 가상화로 성능 손실이 발생한다.
- 완벽한 독립적인 공간을 생성하나, 이미지 용량이 매우 크고, 가상머신 배포에는 부담이 있다.
컨테이너 (Container)
- 컨테이너에 소스 배포하고, 이미지화한다.
- 리눅스 Chroot, 네임스페이스 등을 사용한 프로세스 단위 격리 환경 구성
- 애플리케이션 구동을 위한 라이브러리만 포함한 이미지 생성, 용량이 작음
- 컨테이너는 OS 자체를 가상화 하는게 아니라 Host OS 위에서 가벼운 프로세스 형태로 돌아가기 때문에 훨씬 빠르고 가볍다.
LXC (LinuX Contatiner)
- [컨테이너는 원래 리눅스의 네임스페이스(namespaces)와 cgroups(control groups)라는 기술을 기반으로 프로세스를 격리하는 개념]
- 리눅스 커널에 구현되어 있는 기술
- chroot(change root)
- Cgroup(Control group)
- Namespace
컨테이너 기술의 장점
- 경량화 : 하이퍼바이저와 게스트 OS가 없기 때문
- 게스트 OS의 부팅이 필요없다.
- 한정된 하드웨어 자원 안에서 VM보다 더 많은 컨테이너를 효율적으로 실행 가능
도커
Docker 이미지와 컨테이너
Docker Image
- 가상 머신 생성 시에 사용되는 ISO와 비슷한 개념의 이미지
- 여러 개의 층으로 된 바이너리 파일로 존재한다. : DockerFile
- Docker Pull, Docker Run과 같은 명령어로 이미지를 다운받고 실행한다.
- Docker가 설치된 Host OS에 이미지를 다운로드하는 명령어

Docker Container
- 도커 이미지로부터 생성
- 독립적인 공간을 생성한다.
- 일반적으로 도커 이미지 목적에 맞게 컨테이너를 새로 생성한다.
- 이미지는 읽기 전용으로 사용한다.
- 개별 컨테이너의 애플리케이션 설치/삭제는 다른 컨테이너에 영향이 없다.
- 도커 엔진 버전 확인
- 컨테이너 내부에서 나오기
- 이미지 내려받기
- 컨테이너 생성
- 컨테이너 목록 확인
- 컨테이너 이름 변경
- 컨테이너 삭제
Image vs Container
- 먼저 한 줄로 정리하면,
- 이미지와 컨테이너는 클래스와 인스턴스의 관계와 비슷하다.
Docker 컨테이너 다루기
- 만약, port를 안 열고 Container를 실행 시켰다면?
- port도 겹치면 안된다.
컨테이너 애플리케이션 구축
컨테이너와 오케스트레이션 솔루션
- 컨테이너 : 도커
- 오케스트레이션 솔루션 : k8s k3s EKS AKS ECS = 컨테이너를 관리하는 툴
Docker 이미지
- 애플리케이션과 의존성을 포함한 애플리케이션이 실행되기 위한 모든 것이 포함되어있는 파일
- 계층화 되었다.
- 도커 허브(중앙 저장소)에서 다운로드 가능하다.
- create, run, pull 로 이미지를 다운로드할 수 있다.
- 이미지 저장소 (레지스트리)를 직접 구축해서 사용 가능하다.
- 이미지와 컨테이너는 클래스와 인스턴스의 관계와 비슷하다.
Docker 이미지 생성
도커 이미지 생성 방법
- 현재 구동중인 Conatiner에 있는 그대로 image로 만든다. (긴급상황 시에)
- Dockerfile을 이용하여 생성을 선언 (기본적으로)
도커 이미지 생성 방법 1
- Container로 Image 생성
- 설치, 환경구성 변경 등을 직접 수작업
- 이미지 동작을 보장함
도커 이미지 생성 방법 2
- DockerFile 사용
도커 이미지 레이어
- commit 실행으로 새로운 이미지 생성 시 마다 계층화 되어 레이어 값이 쌓인다.
도커 이미지 배포
도커 이미지 배포 방법
- 파일 배포
- Public Registry
- Private Registry
도커 이미지 배포 1 - 파일 배포
- docker save 를 통해서 추출
- 도커 이미지 로드
- docker exprot / import 를 통해서 추출하기
- Load : Image에 container 정보들이 들어있음. Image의 Layer가 그대로 들어간다.
- Import : Image에 container 정보들이 없다. 기존 Layer가 없고, 새로운 Layer로 존재한다.
- 이미지를 파일로 추출하면 개수 만큼 디스크 공간을 차지한다.
도커 이미지 배포 2 - Public Registry

- 도커 허브 or AWS ECR에 올리기
- 도커 허브
도커 이미지 배포 3 - Private Registry
- 기업 내부에서 직접 도커 이미지 저장소를 구축하는 방법으로 많이 쓰인다.
- 사설 레지스트리 저장소 생성
- /etc/docker/daemon.json 파일에서 "insecure-registries" 설정을 추가
- 사설 레지스트리가 HTTPS 인증서를 사용하지 않을 때 필수
- 기본적으로 도커는 HTTPS를 요구하지만, 사설 레지스트리를 HTTP로 운영하면 보안 경고 발생
- "insecure-registries"를 추가해서 예외 처리 가능
도커 볼륨
- 도커 컨테이너는 읽기-쓰기가 가능한 레이어(컨테이너 레이어)를 가진다.
- 이미지 자체는 읽기 전용 (Read-Only)
- 컨테이너 변경 정보는 컨테이너 레이어에 저장된다.
도커 컨테이너의 구조 (레이어 구조)
- Docker Container는 베이스 이미지 + 여러 레이어로 구성됨
- 각 레이어는 변경될 수 있으며, 애플리케이션 업데이트 시 전체 VM을 교체할 필요 없음
볼륨(Volume)에 저장하기
- 컨테이너 데이터를 영구적으로 저장하는 방법
Stateful vs Stateless 컨테이너
- Stateless 컨테이너
- 데이터를 컨테이너 내부가 아닌 외부 볼륨에 저장하는 방식
- 컨테이너가 삭제되더라도 데이터는 유지된다.
- 예: 웹 서버(Nginx, Apache)
- Stateful 컨테이너
- 데이터를 컨테이너 내부에 저장하는 방식
- 컨테이너 삭제 시 데이터도 함께 삭제된다.
- 예: 데이터베이스(MySQL, PostgreSQL, MongoDB)
Docker 볼륨을 사용하는 세 가지 방법
- 다른 말로 “Docker에서 데이터를 영구적으로 저장하는 방법”
볼륨 사용 1 - Host의 폴더를 직접 공유

- 호스트 OS의 특정 디렉토리를 컨테이너 내부 경로와 연결하는 방식
- 호스트의 파일 시스템을 그대로 사용하므로, 컨테이너 삭제 후에도 데이터 유지될 수 있는 것
- 단점: 컨테이너가 다른 호스트에서 실행될 경우 데이터 공유가 어려움
📌 예제
docker run -d --name my-nginx \
-v /var/log/nginx:/var/log/nginx \
nginx
- 호스트의 nginx 디렉토리를 컨테이너 내부 nginx와 연결
- 컨테이너가 생성/삭제되더라도 호스트의 파일은 유지되는 것
볼륨 사용 2 - 볼륨 컨테이너 공유 (Legacy 방식)

- 여러 컨테이너가 하나의 데이터 컨테이너를 공유하도록 설정
- 데이터 컨테이너를 이용하면, 다른 컨테이너에서도 동일한 볼륨을 공유 가능
📌 예제
docker create -v /data --name data-container busybox
docker run --rm --volumes-from data-container my-app
- 단점: 공유 컨테이너가 삭제되면 볼륨도 사라질 수 있음
볼륨 사용 3 - 도커가 지원하는 Volume을 사용 (docker volume create)

- 도커의 내장된 볼륨 기능을 사용하는 방식
- 호스트 OS에 종속되지 않고, 여러 컨테이너에서 쉽게 공유 가능
- 가장 권장되는 방식의 볼륨이다.
📌 예제
docker volume create my-volume
docker run -d --name my-container -v my-volume:/app/data my-app
도커 볼륨을 사용하는 이유
- Host OS에 독립적인 볼륨을 생성
- 컨테이너가 삭제 후에도 볼륨은 유지된다.
- Container끼리 종속적이지 않다.
- 도커 볼륨은 호스트 파일 시스템의 특정 경로를 직접 노출하지 않기 때문에 보안적으로 더 안전하다.
도커 파일 (Dockerfile)
Infra as Code
- IaC, 인프라스트럭처 코드화
- 코드로써 관리되는 인프라
- 인프라 설치를 사람이 직접 수행하는 것이 아닌, 설치 시에 할일을 코드로써 작성하는 것
IaC의 장점
- Image 생성의 자동화
- 빠른 배포 & 스케일링
- 변경 이력 관리
- 내부 내용 명확하고 손쉽게 파악 가능
- 협업 & 재사용성
IaC vs IaaS (Infra as a Service)
- IaaS는 클라우드에서 인프라를 제공하는 개념이고
- IaC는 그 인프라를 코드로 자동화하는 방법임.
Dockerfile
- 컨테이너 빌드에 필요한 작업 명령이 저장된 파일이다.
- Working Directory의 “Dockerfile”이라는 이름의 파일을 인식하여 image를 생성한다.
필수 명령어
명령어
|
설명
|
FROM
|
어떤 기본 이미지를 사용할지 지정
|
WORKDIR
|
작업 디렉토리 설정 (cd처럼 생각하면 됨)
|
COPY
|
로컬 파일을 컨테이너 내부로 복사
|
RUN
|
컨테이너 내부에서 실행할 명령어 (ex. 패키지 설치)
|
CMD
|
컨테이너가 실행될 때 실행할 명령어 (기본 실행 프로세스)
|
Docker 기본 구조
# 1. 사용할 기본 이미지 선택
FROM python:3.9
# 2. 작업 디렉토리 설정
WORKDIR /app
# 3. 필요한 파일 복사
COPY . /app
# 4. 의존성 설치
RUN pip install -r requirements.txt
# 5. 컨테이너 실행 시 실행할 명령어
CMD ["python", "app.py"]
→ CI/CD의 경우 “.github/workflows/deploy.yml”와 연동해서 사용함.
- main 브랜치에 push가 되면 실행됨.
- 도커 이미지 빌드
- Docker Hub등에 업로드
- AWS EC2에 SSH로 접속 후 컨테이너 실행
Dockerfile을 이용한 이미지가 만들어지는 과정
- Dockerfile을 기반으로 이미지를 만들 때, 각 명령어 (ADD, RUN, COPY 등) 가 실행될 때마다 새로운 레이어(layer)가 생성된다.
Image Layer를 줄이면 좋은 점
- Docker 이미지에서 한 번 생성된 레이어는 그대로 유지된다.
- 즉, 프로그램을 RUN apt install로 설치했다가 RUN apt remove로 삭제해도, 이미 만들어진 레이어에는 여전히 삭제되기 전 상태가 남아 있음.
- SIZE : 설치, 삭제 따로 한 경우 <<< 설치, 삭제 같이 한 경우
Dockerfile의 빌드 by Cache
- Docker는 이미 실행된 명령어의 결과를 캐시에 저장하여 빠르게 빌드할 수 있음
- 의존성 파일을 먼저 COPY하고, 나중에 소스 코드를 복사하면 캐시를 더 잘 활용할 수 있음
- RUN 명령어를 여러 개 쓰지 말고 하나로 합쳐서 캐시 활용도를 높이자.
- 따로 설정할 것은 없지만,
- ⇒ 명령어 순서를 신경 써야 하고, 변경이 자주 발생하는 파일은 뒤쪽에 배치하는 게 좋다.
추가적인 명령어
- ENV (환경 변수 설정)
- Volume (데이터 저장 - 영속성 유지)
- ARG (빌드 시 사용되는 변수)
- User
- Onbuild
- Healthcheck
COPY vs ADD
- 둘 다 파일이나 디렉토리를 컨테이너 내부로 복사하는 명령어
- Copy : 단순히 파일/디렉토리을 이미지에 복사 (파일/디렉토리만 가능하다.)
- Add : 로컬파일, URL, tar 파일 등도 복사가 가능하다.
EntryPoint vs CMD
- 둘 다 컨테이너가 실행될 때 실행할 기본 명령어를 정의하는 명령어
- Entrypoint : 절대적으로 실행시킨다.
- CMD : 기본적으로 실행된다. (덮어쓰면 덮어씌여짐)
✔ ENTRYPOINT는 항상 실행되기 때문에 컨테이너 실행 시 필수적인 실행 파일을 보장함
✔ 커맨드라인에서 추가 입력하는 값들을 자동으로 실행 파일의 인자로 전달할 수 있음
✔ CMD와 달리 ENTRYPOINT는 덮어쓰기되지 않고, 기본 실행 프로그램으로 고정됨!
Docker Container에 설정을 더 추가하고 싶다면?
- 이미 실행중인 container에 Port나 volume 설정을 더 추가할 수는 없다.
- → 해결책
docker attach vs docker exec
- 둘 다 실행중인 컨테이너와 상호작용하는 명령어
- docker attach [ 컨테이너 id]
- Docker exet -it [컨테이너 id] /bin/bash
Multistage docker build : 최적화
- 여러 개의 빌드 단계를 거친 후, 최종 이미지를 생성하는 방식이야.
- 즉, 목적에 맞춰서 image를 분리하고, 필요한 것을 활용할 수 있다.
- 빌드 / 운영 분리
기본적인 Dockerfile 예시
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
- pip install 과정에서 빌드 시 필요한 패키지들이 최종 이미지에도 포함됨
- gcc, make 같은 컴파일러도 포함될 가능성이 있어 불필요한 용량 증가
빌드/ 운영 분리 - Multi Stage
# 1️⃣ 빌드 스테이지 (build 단계)
FROM python:3.9 AS builder
WORKDIR /app
# 필요한 파일만 복사 후, 패키지 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 2️⃣ 실행 스테이지 (최종 이미지)
FROM python:3.9
WORKDIR /app
# 빌드된 패키지만 복사 (불필요한 파일 제외)
COPY --from=builder /app /app
# 실행
CMD ["python", "app.py"]
멀티 스테이지 빌드의 핵심
- 빌드용 이미지(builder)에서 패키지를 설치하고, 최종 이미지에는 빌드 결과만 복사!
- 최종 이미지에는 불필요한 빌드 도구가 포함되지 않음 → 이미지 크기 최소화!
- 보안성 향상 (빌드 과정에서만 필요한 정보는 최종 이미지에서 사라짐)
참고:
GitHub awesome-compose/spring-postgres/backend/Dockerfile at master · docker/awesome-compose
Docker Compose
- 여러 개의 컨테이너가 하나의 애플리케이션으로 동작할 때, 한 번에 관리할 수 있도록 도와주는 도구
- docker run 명령어 없이 설정 파일(docker-compose.yml) 하나로 여러 컨테이너를 쉽게 실행, 관리 가능하다.
- 애플리케이션이 여러 개의 서비스(Nginx + DB + Redis 등)로 구성될 때 쉽고 일관되게 실행할 수 있음
- 한 줄로 모든 컨테이너를 실행(docker-compose up)하고, 중지(docker-compose down) 가능!
Docker Compose의 필요성
- 일일이 docker run을 해주려면…너무너무 길다…….
- 여러 개의 컨테이너를 한 번에 실행하고 관리하고 싶을 때
Docker Compose의 주요 개념
서비스(services)
|
하나의 컨테이너 역할 (예: nginx, springboot, mysql, redis)
|
볼륨(volumes)
|
컨테이너가 삭제돼도 데이터가 유지되도록 저장소 설정
|
네트워크(networks)
|
여러 컨테이너 간 통신을 가능하게 하는 네트워크
|
환경 변수(environment)
|
컨테이너 실행 시 필요한 환경 변수를 설정
|
설치 / 실행 / 종료
설치하기
- Linux/macOS (Docker가 이미 설치되어 있어야 함)
sudo apt-get install docker-compose -y # Ubuntu
brew install docker-compose # macOS (Homebrew 사용)
- 설치 확인
sudo apt-get install docker-compose -y # Ubuntu
brew install docker-compose # macOS (Homebrew 사용)
실행하기
docker-compose up -d # -d 옵션: 백그라운드 실행
종료하기
docker-compose down
Nginx + Spring Boot + MySQL + Redis 예제
폴더 구조
📌 폴더 구조
project/
│── backend/ # Spring Boot 프로젝트
│ ├── Dockerfile
│ ├── src/ ...
│── nginx/ # Nginx 설정 파일
│ ├── default.conf
│── docker-compose.yml
docker-compose.yml 파일 작성
version: '3.8'
services:
nginx:
image: nginx:latest
ports:
- "8080:80"
depends_on:
- backend
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
networks:
- app-network
backend:
build: ./backend
ports:
- "8081:8081"
depends_on:
- db
- redis
environment:
- SPRING_DATASOURCE_URL=jdbc:mysql://db:3306/mydb
- SPRING_DATASOURCE_USERNAME=root
- SPRING_DATASOURCE_PASSWORD=rootpass
- REDIS_HOST=redis
- REDIS_PORT=6379
networks:
- app-network
db:
image: mysql:latest
restart: always
environment:
MYSQL_ROOT_PASSWORD: rootpass
MYSQL_DATABASE: mydb
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
networks:
- app-network
redis:
image: redis:latest
ports:
- "6379:6379"
networks:
- app-network
volumes:
mysql_data:
networks:
app-network:
✅ 설명:
- nginx: 8080포트로 Spring Boot 서비스로 트래픽을 라우팅
- backend: Spring Boot 애플리케이션 실행
- db: MySQL 데이터베이스 실행
- redis: Redis 캐시 서버 실행
- volumes: MySQL 데이터 저장소 유지
- networks: 컨테이너 간 통신을 위한 네트워크 설정
backend/Dockerfile (Spring Boot용)
FROM openjdk:17
WORKDIR /app
COPY target/myapp.jar app.jar
CMD ["java", "-jar", "app.jar"]
nginx/default.conf (Nginx 설정)
server {
listen 80;
location / {
proxy_pass http://backend:8081;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
실행
bocker-compose up -d
✅ Nginx, Spring Boot, MySQL, Redis 컨테이너가 한 번에 실행됨! 🚀
📌 웹 브라우저에서 확인
- http://localhost:8080 → Spring Boot 앱이 Nginx를 통해 연결됨
- http://localhost:8081 → Spring Boot API 직접 호출 가능
- mysql -h 127.0.0.1 -u root -p → MySQL 접속 가능