오토에버 클라우드 2기 37일차
Docker Compose를 활용한 멀티 컨테이너 애플리케이션 구축
여러 개의 컨테이너로 구성된 애플리케이션을 효율적으로 관리하고 배포하기 위해 Docker Compose를 사용하는 방법을 단계별로 설명한다
개별 docker 명령어를 이용한 환경 구축의 한계
여러 컨테이너를 연동하여 애플리케이션을 구성할 때, docker run 명령어를 각각 실행하는 방식은 직관적이지만 여러 단점이 존재한다
- 명령어가 길고 복잡하여 오타가 발생하기 쉽다
- 컨테이너 간의 의존 관계나 네트워크 설정을 수동으로 관리해야 한다
- 프로젝트 설정을 공유하거나 다른 환경에 동일하게 재현하기 어렵다
Docker Compose를 이용한 컨테이너 연동
Docker Compose는 다중 컨테이너 Docker 애플리케이션을 정의하고 실행하기 위한 도구 docker-compose.yml 이라는 YAML 파일을 사용하여 애플리케이션의 서비스를 구성하고, 단 한 번의 명령어로 모든 서비스를 생성하고 시작할 수 있다
Python(Flask) + Redis 스택 구축
웹 페이지 방문 횟수를 Redis에 저장하고 화면에 출력하는 간단한 Flask 웹 애플리케이션 생성
1. 프로젝트 구조
1
2
3
4
5
.
├── app.py
├── Dockerfile
├── requirements.txt
└── docker-compose.yml <-- 최종적으로 만들 파일
2. Python 애플리케이션 및 의존성 파일 생성
#requirements.txt
flask
redis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# app.py
import time
import redis
from flask import Flask
app = Flask(__name__)
def web_hit_cnt():
# Docker Compose 네트워크 내에서는 서비스 이름('redis')으로 다른 컨테이너에 접근합니다.
with redis.StrictRedis(host='redis', port=6379) as conn:
return conn.incr("hits")
@app.route("/")
def hello():
cnt = web_hit_cnt()
return "<p>Web Access Count: {} times</p>".format(cnt)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9000, debug=True)
1
2
3
4
5
6
7
8
9
10
11
# Dockerfile
FROM python:3.10-slim
WORKDIR /py_app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 9000
CMD ["python", "app.py"]
3. Docker-compose.yml 파일 작성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# docker-compose.yml
version: '3.8'
services:
# Redis 서비스 정의
redis:
image: redis:latest
restart: always
# 포트는 외부 노출이 필요 없으므로 주석 처리 가능
# ports:
# - "6379:6379"
# Flask 웹 애플리케이션 서비스 정의
flask:
# 현재 디렉토리의 Dockerfile을 사용하여 이미지 빌드
build: .
ports:
- "9000:9000"
restart: always
# 'redis' 서비스가 먼저 시작되도록 의존성 설정
depends_on:
- redis
4. Docker Compose 실행
1
2
# -d 옵션으로 백그라운드에서 실행
docker-compose up -d --build
이제 웹 브라우저에서 http://localhost:9000에 접속하면, 페이지를 새로고침할 때마다 카운트가 증가하는 것을 확인할 수 있다

app.py에서 Redis 호스트를 localhost나 IP 주소가 아닌 서비스 이름 redis로 지정할 수 있었던 이유는 Docker Compose가 자동으로 생성한 네트워크 내에서 서비스 이름으로 DNS 조회를 지원하기 때문이다
Nginx를 이용한 로드 밸런싱 구축
서비스에 대한 요청이 많아지면 서버 하나만으로는 부하를 감당하기 어렵다 이때 로드 밸런서를 사용하여 여러 서버에 요청을 분산시킬 수 있다 Docker Compose를 사용하면 Nginx를 로드 밸런서로 구성하는 것도 간단하게 구현 가능하다
로드 밸런싱 개념 및 알고리즘
- 개요: 서버에 가해지는 부하(Load)를 여러 대의 서버로 분산(Balancing)시켜주는 기술 이를 통해 시스템의 가용성과 처리량을 높일 수 있다
- 주요 알고리즘:
- Round Robin (기본): 요청을 서버에 순서대로 분배
- Least Connection: 연결 수가 가장 적은 서버에 요청을 보낸다
- IP Hash: 클라이언트의 IP 주소를 해싱하여 특정 서버로만 요청을 보내, 세션 유지를 돕는다
Docker Compose를 활용한 Nginx 로드 밸런서 구현
1. 프로젝트 구조
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.
├── pyfla_app1/
│ ├── app.py # "Web Application [1]" 출력
│ ├── Dockerfile
│ └── requirements.txt
├── pyfla_app2/
│ ├── app.py # "Web Application [2]" 출력
│ └── ...
├── pyfla_app3/
│ ├── app.py # "Web Application [3]" 출력
│ └── ...
├── nginx_alb/
│ ├── nginx.conf # Nginx 로드 밸런싱 설정
│ └── Dockerfile
└── docker-compose.yml # 전체 서비스 통합 파일
2. Nginx 설정 파일 (nginx_alb/nginx.conf)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# upstream 블록에 요청을 분산할 서버 그룹을 정의합니다.
# Docker Compose 네트워크에서는 서비스 이름으로 서버를 지정합니다.
upstream web-alb {
server pyfla_app1:9000;
server pyfla_app2:9001;
server pyfla_app3:9002;
}
server {
listen 80;
location / {
# 모든 요청을 'web-alb' 그룹으로 전달합니다.
proxy_pass http://web-alb;
}
}
3. Nginx Dockerfile (nginx_alb/Dockerfile)
1
2
3
4
FROM nginx
# 기본 설정을 삭제하고 우리가 만든 설정 파일을 복사합니다.
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
4. 전체 서비스를 통합하는 docker-compose.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
version: "3.8"
services:
pyfla_app1:
build: ./pyfla_app1
# 각 앱의 포트는 외부로 노출할 필요 없음 (Nginx를 통해서만 접근)
pyfla_app2:
build: ./pyfla_app2
pyfla_app3:
build: ./pyfla_app3
nginx:
build: ./nginx_alb
ports:
- "8080:80" # 외부에서 8080 포트로 접근하면 Nginx의 80포트로 연결
depends_on:
- pyfla_app1
- pyfla_app2
- pyfla_app3
그리고 이전에 만든 플라스크 프로젝트를 약간 수정함
- 플라스크에 1 web, 2 web, 3 web만 출력 그리고 출력 포트 9000, 9001, 9002로 설정
- redis를 삭제
5. 실행 및 테스트
1
docker-compose up --build -d

새로고침시 위에서 부터 차례대로 1, 2, 3이 실행되는것을 확인할 수 있다