Docker Compose로 Nginx + Flask + MySQL

이 글에서는 하나의 서버에서
목차
- Nginx(프록시/정적 파일)
- Flask(Python 웹 애플리케이션)
- MySQL(데이터베이스)
이 세 가지를 모두 도커 컨테이너로 구성하고,
이를 Docker Compose로 한 번에 띄우는 방법을 단계별로 정리한다.
최종 목표는 다음과 같다.
http://localhost:8080으로 접속하면 Nginx → Flask → MySQL 까지 연동된 페이지가 뜬다.- 모든 서비스는 개별 컨테이너이지만, Docker Compose 네트워크로 자연스럽게 연결된다.
- 코드 수정은 로컬에서 하고, 실행은 컨테이너에서 한다.
전체 구조 설계
먼저 디렉터리 구조부터 잡는다.
project-root/
├── docker-compose.yml
├── nginx/
│ └── default.conf
└── app/
├── Dockerfile
├── app.py
└── requirements.txt
각 파일은 다음 역할을 한다.
docker-compose.yml
→ 전체 서비스를 정의하는 Docker Compose 설정 파일nginx/default.conf
→ Nginx가 Flask 컨테이너로 요청을 프록시하는 설정app/Dockerfile
→ Flask 애플리케이션용 커스텀 이미지 정의app/app.py,app/requirements.txt
→ 실제 Flask 코드와 Python 의존성 목록
이렇게 나누어 두면, 이후 Docker Compose를 사용할 때 서비스별 역할이 명확해진다.
1. Flask 애플리케이션 작성
app/app.py:
from flask import Flask
import os
import pymysql
app = Flask(__name__)
DB_HOST = os.getenv("DB_HOST", "db")
DB_USER = os.getenv("DB_USER", "root")
DB_PASSWORD = os.getenv("DB_PASSWORD", "example")
DB_NAME = os.getenv("DB_NAME", "testdb")
def get_connection():
return pymysql.connect(
host=DB_HOST,
user=DB_USER,
password=DB_PASSWORD,
database=DB_NAME,
cursorclass=pymysql.cursors.DictCursor,
)
@app.route("/")
def index():
try:
conn = get_connection()
with conn.cursor() as cursor:
cursor.execute("CREATE TABLE IF NOT EXISTS visitors (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50));")
cursor.execute("INSERT INTO visitors (name) VALUES ('Docker Compose User');")
conn.commit()
cursor.execute("SELECT COUNT(*) AS cnt FROM visitors;")
result = cursor.fetchone()
count = result["cnt"]
conn.close()
return f"<h1>Hello from Flask + MySQL!</h1><p>Total visitors: {count}</p>"
except Exception as e:
return f"<h1>Error</h1><pre>{e}</pre>"
if __name__ == "__main__":
# 개발용 직접 실행 (도커에서는 gunicorn 또는 flask run 대신 이걸 써도 무방)
app.run(host="0.0.0.0", port=5000)
이 코드는 다음을 수행한다.
- 환경변수로 MySQL 접속 정보(DB_HOST, DB_USER 등)를 받는다.
- 첫 요청 시
visitors테이블을 생성하고, 방문자를 하나 추가한 뒤 현재 카운트를 보여준다. - DB 호스트를
db로 지정했는데, 이 이름은 나중에 Docker Compose에서 MySQL 서비스 이름과 일치시키면 된다.
app/requirements.txt:
Flask==3.0.0
PyMySQL==1.1.0
이제 Flask 애플리케이션에 필요한 패키지를 명확하게 정의했으니,
Docker Compose로 이미지 빌드 시 이 파일을 활용할 수 있다.
2. Flask용 Dockerfile 작성
app/Dockerfile:
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
이 Dockerfile은 다음 작업을 한다.
- Python 3.11-slim 이미지를 기반으로 한다.
- 작업 디렉터리를
/app으로 설정한다. requirements.txt를 복사하고, 필요한 패키지를 설치한다.- 나머지 애플리케이션 파일을 모두 복사한다.
- 5000번 포트를 열고, 컨테이너 실행 시
python app.py를 실행한다.
이제 Docker Compose에서 이 Dockerfile을 이용해 Flask 이미지를 빌드하게 만들 것이다.
3. Nginx 설정 파일 작성
nginx/default.conf:
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://flask:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
핵심 포인트:
- Nginx는 80 포트에서 대기한다.
/경로로 들어온 요청을http://flask:5000으로 보낸다.- 여기서
flask는 Docker Compose에서 정의할 Flask 서비스 이름이다.
Docker Compose를 사용하면 서비스 이름이 곧 호스트명이 된다.
이렇게 하면 Docker Compose 네트워크 안에서
Nginx 컨테이너가 Flask 컨테이너로 HTTP 요청을 프록시하게 된다.
4. Docker Compose 설정 작성
이제 모든 것을 묶어줄 docker-compose.yml 을 작성한다.
docker-compose.yml:
version: "3.8"
services:
nginx:
image: nginx:latest
container_name: nginx
ports:
- "8080:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- flask
flask:
build: ./app
container_name: flask
environment:
DB_HOST: db
DB_USER: root
DB_PASSWORD: example
DB_NAME: testdb
expose:
- "5000"
depends_on:
- db
db:
image: mysql:8.0
container_name: db
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: testdb
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:
이 Docker Compose 설정을 하나씩 해석해보면 다음과 같다.
nginx서비스- 공식
nginx:latest이미지를 사용한다. - 호스트의 8080 포트를 컨테이너의 80 포트에 연결한다.
./nginx/default.conf파일을 Nginx 설정 경로에 read-only로 마운트한다.depends_on으로flask이후에 올라오도록 설정한다.
- 공식
flask서비스./app디렉터리를 빌드 컨텍스트로 사용하여 Dockerfile을 빌드한다.- 환경변수로 DB 접속 정보를 전달한다(DB_HOST 등).
expose로 내부 5000포트를 열어두고, Nginx가 이 포트로 접근할 수 있게 한다.depends_on으로db이후에 올라오도록 한다.
db서비스mysql:8.0이미지를 사용한다.- 루트 패스워드와 초기 데이터베이스 이름을 환경변수로 전달한다.
db_data라는 named volume을/var/lib/mysql에 마운트해서 데이터가 컨테이너 밖에 보존되도록 한다.
volumesdb_data를 named volume으로 선언한다.- 컨테이너를 삭제해도 이 볼륨은 남기 때문에 MySQL 데이터가 유지된다.
이렇게 Docker Compose를 사용하면 세 개의 컨테이너를 한 번에 정의하고,
같은 네트워크에서 자연스럽게 통신하게 만들 수 있다.
5. Docker Compose로 한 번에 실행
루트 디렉터리(즉, docker-compose.yml 이 있는 곳)에서 다음을 실행한다.
docker-compose up -d
정상적으로 실행되면:
nginx,flask,db세 컨테이너가 모두 올라온다.- Docker Compose는 자동으로 프로젝트용 네트워크를 하나 생성하고,
세 컨테이너를 모두 그 네트워크에 붙인다. nginx컨테이너는flask:5000으로,flask컨테이너는db:3306으로 접근할 수 있다.
상태 확인:
docker-compose ps
로그 확인:
docker-compose logs -f
6. 브라우저에서 연동 확인
이제 브라우저를 열고 다음 주소에 접속한다.
http://localhost:8080
정상적으로 구성되었다면:
- Nginx 컨테이너가 요청을 받는다.
- Nginx 설정에 따라
flask:5000으로 프록시한다. - Flask 컨테이너가 MySQL 컨테이너
db:3306에 접속해visitors테이블을 만들고 데이터를 넣는다. - 총 방문자 수를 화면에 출력한다.
페이지를 여러 번 새로고침하면 Total visitors 숫자가 증가하는 것을 볼 수 있다.
이것이 바로 Docker Compose로 Nginx + Flask + MySQL 을 한 번에 연동한 결과다.
7. 수정과 재배포
코드를 수정하는 방식은 다음과 같다.
- Flask 코드(
app/app.py)를 수정한 경우
→ 이미지 기반이므로, 보통은docker-compose build flask후docker-compose up -d를 다시 실행한다.
→ 개발 환경에서는 Dockerfile 대신 bind mount를 써서app디렉터리를 볼륨으로 연결하는 방법도 있다. - Nginx 설정(
nginx/default.conf)을 바꾼 경우
→ 설정 파일은 볼륨으로 직접 연결되어 있으므로, 파일을 수정한 다음docker-compose restart nginx정도로 재시작하면 적용된다.
Docker Compose를 사용하면
여러 컨테이너를 한 번에 내렸다 올릴 수 있기 때문에,
개발·테스트 환경을 관리하기가 훨씬 편해진다.
8. 마무리 정리
이 글에서 한 것은 다음과 같다.
- Docker Compose로 세 개의 서비스를 정의했다.
- Nginx: 외부 진입점, 리버스 프록시
- Flask: 애플리케이션 서버
- MySQL: 데이터베이스
- Docker Compose 네트워크 덕분에
nginx → flask는flask:5000flask → db는db:3306으로 이름 기반 통신이 가능해졌다.
- Docker Compose의
volumes기능으로 MySQL 데이터를 영구 저장했다.
이제 여기서 조금만 확장하면,
- Docker Compose로 Redis 추가
- Docker Compose로 여러 Flask 인스턴스와 로드밸런싱
같은 고급 구성도 쉽게 시도할 수 있다.
원하면 이 다음에는
- “개발 모드에서 Flask 코드를 bind mount로 바로 반영하는 Docker Compose 설정”
- “Docker Compose에서 stage별(개발/운영) 설정 분리하는 방법”
같은 주제로 이어서 더 깊게도 들어갈 수 있다.
그 방향으로도 이어서 정리해줄까?