Docker-Compose와 Dockerfile을 활용한 Flask-MySQL 연동 예제
Page content
개요
- Docker-Compose와 Dockerfile의 주요 기능을 이해한다.
- 각 파일의 위치와 주요 기능을 이해한다.
전체 프로젝트 파일 디렉터리
- 본 프로젝트의 전체 코드는 다음과 같다.
- 실제 코드 작성을 해야하는 곳은 다음과 같다.
- app.py
- requirements.txt
- init.sql
- docker-compose.yml
- Dockerfile
docker_kubernetes_flask/
├── app/
│ ├── __init__.py
│ ├── app.py
│ └── requirements.txt
├── db/
│ ├── init.sql
│ └── data/ (This will be created by Docker)
├── docker-compose.yml
└── Dockerfile
사전준비
- 사전에
Docker
는 Desktop 설치가 되어 있다고 가정한다. - 코드 편집을 위해서는
Visual Studio Code
를 활용한다.
Docker가 익숙하지 않은 사람들을 위한 1줄 요약
- MySQL 설치하고, Python 설치하고, 두개 또 연동해야 하고, CLI 명령어 또 각각 입력하는거 다 자동화 해줄게요!!
- 즉, 자동화에 익숙해지자!
docker-compose와 Dockerfile 간단 비교
- docker-compose.yml : python 컨테이너와 mysql 컨테이너를 각각 한꺼번에 구성하도록 스크립트를 작성함
- Dockerfile : 여기에서는 python 개발환경을 구성함
- docker-compose.yml에서 Dockerfile을 호출하여 개발환경을 만들도록 지시할 수 있음
전체 코드 흐름 1줄 요약
- From MySQL to Python Flask
init.sql과 app.py 간단 설명
- 각 두개의 파일은 사전에 미리 작성을 해둔다.
MySQL : init.sql
- 이
SQL
코드는 데이터베이스와 테이블을 생성하고, 테이블에 데이터를 삽입하는 작업을 수행.test_db
데이터베이스 생성users
테이블 생성- 간단하게 이름 생성
CREATE DATABASE IF NOT EXISTS test_db;
USE test_db;
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
INSERT INTO users (name) VALUES ('Evan');
INSERT INTO users (name) VALUES ('Sara');
INSERT INTO users (name) VALUES ('Lotto');
Python : app.py
- 이
Python
코드는Flask
웹 애플리케이션을 설정하여MySQL
데이터베이스에 연결하고, 사용자 데이터를JSON
형식으로 반환하는 작업을 수행.
from flask import Flask, jsonify
import mysql.connector
import os
app = Flask(__name__)
def get_db_connection():
connection = mysql.connector.connect(
host='mysql',
user='root',
password='example',
database='test_db'
)
return connection
@app.route('/')
def index():
connection = get_db_connection()
cursor = connection.cursor()
cursor.execute('SELECT * FROM users')
users = cursor.fetchall()
cursor.close()
connection.close()
users_list = [{"id": user[0], "name": user[1]} for user in users]
return jsonify(users_list)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
- 좀더 구체적으로 살펴본다.
step 01 - 라이브러리 불러오기
Flask
: Flask 웹 애플리케이션 프레임워크를 불러오기jsonify
: 데이터를 JSON 형식으로 변환하여 HTTP 응답으로 반환하는 데 사용mysql.connector
: MySQL 데이터베이스에 연결하기 위해 사용
from flask import Flask, jsonify
import mysql.connector
import os
step 02 - Flask Application Setup
- Flask 애플리케이션 인스턴스를 생성.
__name__
은 현재 모듈의 이름을 전달하여 Flask 애플리케이션을 생성하는 데 사용.
app = Flask(__name__)
step 03 - Database Connection Function
get_db_connection
함수는 MySQL 데이터베이스에 연결하고, 연결 객체를 반환.host
,user
,password
,database
매개변수는 데이터베이스에 연결하기 위한 정보
def get_db_connection():
connection = mysql.connector.connect(
host='mysql',
user='root',
password='example',
database='test_db'
)
return connection
step 04 - Index Route
@app.route('/')
: 해당 Decorator는 URL (’/’)에 대한 요청을 처리하는index
함수를 정의.- index 함수의 내용은 다음과 같이 구성됨
get_db_connection
을 호출하여 데이터베이스에 연결.- 연결 객체에서 커서를 생성하고,
SELECT * FROM users
쿼리를 실행하여users
테이블의 모든 데이터를 가져오기 - 데이터를 가져온 후 커서와 연결을 닫기
users
데이터를 List Comprehension을 사용하여 딕셔너리 형태로 변환합니다. 각 사용자에 대해id
와name
키를 가지는 딕셔너리를 생성.- 이 부분은 별도의 HTML 소스코드를 넣지 않기 위해서 진행한 것이니, 해당 자세한 내용을 보기를 원한다면 Flask 웹개발로 더 공부할 것 권장
- 변환된 리스트를
jsonify
를 사용하여 JSON 형식으로 반환.
@app.route('/')
def index():
connection = get_db_connection()
cursor = connection.cursor()
cursor.execute('SELECT * FROM users')
users = cursor.fetchall()
cursor.close()
connection.close()
users_list = [{"id": user[0], "name": user[1]} for user in users]
return jsonify(users_list)
step 05 - Running the Application
- 모듈이 직접 실행될 때만 Flask 애플리케이션을 실행
app.run
메소드를 호출하여 애플리케이션을 시작.host='0.0.0.0'
: 애플리케이션이 모든 네트워크 인터페이스에서 접근 가능하도록 설정.port=5000
: 애플리케이션이 5000번 포트에서 실행되도록 설정.
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Python : requirements.txt
- 주요 라이브러리 설치 위해 필요한 라이브러리 2개만 설치
- 추후에 독자가 라이브러리 추가 가능
Flask
mysql-connector-python
Docker: Dockerfile
- Dockerfile은 Python 애플리케이션을 컨테이너화하기 위한 스크립트
- 다른 파일과 달리 확장자명이 없다는 것에 주의
- 다양한 옵션에 대해 설명하도록 한다.
FROM python:3.10-slim
WORKDIR /app
COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app/ .
EXPOSE 5000
CMD ["python", "app.py"]
step 01 - FROM
FROM python:3.10-slim
- Base Image를 선택한다. 해당 이미지를 선택하려면 Docker Hub 검색창에서 확인 (Login 필수)
- 각 Base Image를 선택하면 관련 Tag가 존재하며 여기에서 Tags 확인해서 입력하도록 한다.
- 꼭
Docker Official Image
를 선택한다.
Step 02 - WORKDIR
WORKDIR /app
- 생성될 컨테이너의 작업공간을 지정하는 것이다. (독자의 PC 폴더가 아님)
WORKDIR
는 컨테이너 내부의 작업 디렉토리를 설정하는 명령어.- 즉,
WORKDIR /app
는 컨테이너 내부에/app
디렉토리를 생성하고 그 디렉터리를 작업 디렉터리로 설정하는 것.
- 즉,
- 이 디렉터리 내에서 이후의
COPY
,RUN
,CMD
명령들이 실행됨.
Step 03 - COPY
COPY app/requirements.txt .
- Local-PC의
app/requirements.txt
파일을 컨테이너의 현재 작업 디렉토리 (/app
)로 복사함.- 이 때
app
폴더는Dockerfile
과 같은 경로에 있는 것을 의미
- 이 때
Step 04 - RUN
RUN pip install --no-cache-dir -r requirements.txt
requirements.txt
파일에 명시된 Python 패키지들을 설치-no-cache-dir
옵션은 pip가 패키지 캐시를 저장하지 않도록 함
Step 05 - COPY
COPY app/ .
- Local-PC 프로젝트 폴더 내
app/
디렉터리의 모든 파일을 컨테이너의 작업 디렉터리 (/app
)로 복사. .
은 모든 파일을 의미.
Step 06 - EXPOSE
EXPOSE 5000
- EXPOSE는
Dockerfile
내에서 컨테이너가 지정된 네트워크 포트를 통해 접속할 것임을Docker
에 알리는 역할. 컨테이너가5000
포트를 외부에 노출하도록 설정. 보통 Flask 같은 웹 애플리케이션에서 사용. EXPOSE
는 실제로 포트를 공개하거나 외부와의 연결을 설정하지 않는다. 단지, 그 포트가 컨테이너 내에서 사용된다는 것을 알리는 역할을 한다.
Step 07 - CMD
CMD ["python", "app.py"]
CMD ["python", "app.py"]
는 Dockerfile에서 컨테이너가 시작될 때 실행할 명령을 정의.- 이를 통해 컨테이너가 시작되면
python app.py
명령이 실행되어 Python 애플리케이션이 실행됨. CMD
명령어는 Dockerfile 내에서 단 하나만 사용될 수 있음.
Docker: docker-compose.yml
- 이 Docker Compose 파일은 Flask 애플리케이션과 MySQL 데이터베이스를 함께 구성하여, 서로 독립적인 컨테이너로 실행되지만 네트워크를 통해 상호작용할 수 있게 함.
- Flask 애플리케이션은 MySQL 데이터베이스에 연동.
- MySQL 데이터베이스는 초기화 SQL 스크립트를 사용하여 설정.
- 호스트의 포트 5000은 Flask 애플리케이션으로 포워딩되어 외부 접근이 가능.
version: '3.8'
services:
flask:
build: .
volumes:
- ./app:/app
networks:
- flask-mysql-network
ports:
- "5000:5000"
depends_on:
- mysql
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- flask-mysql-network
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: test_db
volumes:
mysql-data:
networks:
flask-mysql-network:
Step 00 - version
- 각 버전은 서로 다른 기능을 지원하며, 일부 오래된 버전은 최신 버전에서 제공하는 특정 기능을 지원하지 않을 수 있음.
- 버전 사용 예시
- version 1 : Docker Compose의 초기 버전, 최상위
version
orservices
키가 없는 평면 구조 사용 - version 2.x :
version
키와services
키와 같은 새로운 구문과 기능 도입- 서비스 간의 의존성, 네트워크 및 볼륨과 같은 더 복잡한 구성 허용
- version 3.x : 스웜 모드를 목표로 하는 더 많은 기능을 추가하여 확장 및 분산 애플리케이션을 더 잘 지원. 일반적으로 배포를 관리하기 위한 배포 구성 개념 도입
- version 3.8 :
3.x
시리즈의 최신 버전
- version 1 : Docker Compose의 초기 버전, 최상위
Step 01 - services
services
는 여러 개의 컨테이너를 정의하고 구성하는 기본 단위임.- 각 서비스는
Docker
컨테이너를 정의하며, 해당 컨테이너의 이미지, 실행 명령, 네트워크 설정, 볼륨 마운트 등의 구성을 포함할 수 있습니다. services
의 주요 개념과 구성은 다음과 같음.- 서비스 정의 : 각 서비스는 특정한 역할을 하는 하나의 컨테이너 정의
- 컨테이너 설정 : 서비스 내에서 컨테이너의 이미지, 환경 변수, 포트 매핑, 볼륨 마운트 ,네트워크 설정 등 지정
- 의존성 설정 :
depends_on
옵션 사용하여 서비스 간의 시작 순서 정의할 수 있음. 일반적으로 웹 서비스가 실행되기 전에 DB 서비스가 시작된 후에 실행되도록 설정 가능 - 네트워크 설정 : 서비스 간의 네트워크 정의하여 서로 통신할 수 있도록 설정
- 볼륨 설정 : 각 서비스에 필요한 데이터를 영구적으로 저장하기 위해 볼륨을 정의하고 마운트
- 위 기본 개념을 통해서 다음 코드를 설명해보도록 한다.
services:
flask:
build: .
volumes:
- ./app:/app
networks:
- flask-mysql-network
ports:
- "5000:5000"
depends_on:
- mysql
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- flask-mysql-network
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: test_db
- 위 코드는 크게 2개의 서비스로 구성되어 있다.
- flask 컨테이너
- mysql 컨테이너
- 먼저
flask
컨테이너에 대해 설명하면 다음과 같다.build: .
: 현재 디렉토리에서 Dockerfile을 사용하여 이미지를 빌드.volumes: - ./app:/app
: 호스트(=Local PC)의./app
디렉토리를 컨테이너의/app
디렉토리에 마운트합니다. 이를 통해 호스트 파일 시스템의 변경 사항이 즉시 컨테이너에 반영됨.networks: - flask-mysql-network
:flask-mysql-network
라는 네트워크를 사용하여 MySQL 서비스와 통신.ports: - "5000:5000"
: 호스트의 포트 5000을 컨테이너의 포트 5000에 매핑. 이를 통해 호스트의 5000 포트로 접근하면 Flask 애플리케이션에 접근할 수 있음.depends_on: - mysql
: Flask 서비스는mysql
서비스가 먼저 시작된 후에 시작됨.
services:
flask:
build: .
volumes:
- ./app:/app
networks:
- flask-mysql-network
ports:
- "5000:5000"
depends_on:
- mysql
- 이번에는 MySQL 컨테이너에 대해 설명하면 다음과 같다.
image: mysql:8.0
: MySQL 8.0 이미지를 사용하여 컨테이너를 생성.command: --default-authentication-plugin=mysql_native_password
: MySQL 서버를 시작할 때 기본 인증 플러그인을mysql_native_password
로 설정.volumes
:mysql-data:/var/lib/mysql
:mysql-data
볼륨을 MySQL 데이터베이스 파일이 저장될 컨테이너의/var/lib/mysql
디렉토리에 마운트합니다../db/init.sql:/docker-entrypoint-initdb.d/init.sql
: 호스트의./db/init.sql
파일을 컨테이너의/docker-entrypoint-initdb.d/init.sql
에 마운트하여 초기화 SQL 스크립트를 실행.
services:
flask:
...
mysql:
image: mysql:8.0
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql-data:/var/lib/mysql
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- flask-mysql-network
environment:
MYSQL_ROOT_PASSWORD: example
MYSQL_DATABASE: test_db
Step 03 - Volume
mysql-data
라는 이름의 Docker 볼륨을 정의. 이 볼륨은 MySQL 데이터베이스 데이터를 영구적으로 저장하는 데 사용됨.volume
은 컨테이너의 파일 시스템과 호스트의 파일 시스템 또는 Docker가 관리하는 외부 스토리지 사이에 데이터를 공유하는 데 사용.- 이는 데이터의 영구 저장을 가능하게 하고, 컨테이너가 재시작되거나 삭제되어도 데이터가 유지되도록 함.
- 주요 기능은 다음과 같다.
- 데이터 지속성 : 컨테이너가 삭제되거나 재시작되더라도 데이터는 유지. 데이터베이스나 로그 파일과 같은 중요한 데이터를 영구적으로 저장
- 개발 편의성 : 소스 코드를 호스트에서 컨테이너로 마운트하여, 개발 중에 소스 코드 변경 사항이 즉시 반영
- 데이터 공유 : 여러 컨테이너 간에 데이터를 공유, 동일한 볼륨을 사용하는 웹 서버와 데이터베이스 서버가 데이터를 공유
volumes:
mysql-data:
Step 04 - Networks
networks
는 여러 컨테이너 간의 네트워크를 정의하고 관리하는 데 사용.- 네트워크는 컨테이너 간의 통신을 가능하게 하며, 격리된 네트워크 환경을 제공하여 보안과 네트워크 구성의 유연성을 향상시킴.
- 주요 기능은 다음과 같다.
- 컨테이너 간 통신 : 같은 네트워크에 있는 컨테이너들은 서로의 서비스 이름을 호스트 이름으로 사용하여 통신할 수 있음 . (예시
web
서비스는db
서비스를db
라는 호스트 이름으로 접근) - 네트워크 격리 : 각 서비스에 대해 별도의 네트워크를 정의하여 서비스 간의 통신을 제한하고 보안을 강화. 여러 네트워크를 사용하여 서비스 그룹 간의 트래픽을 분리.
- 유연한 네트워크 설정 : 사용자 정의 네트워크를 생성하고, 네트워크 드라이버를 지정하여 다양한 네트워크 토폴로지를 구성할 수 있음. 브리지 네트워크, 오버레이 네트워크 등 다양한 네트워크 드라이버를 지원.
- 컨테이너 간 통신 : 같은 네트워크에 있는 컨테이너들은 서로의 서비스 이름을 호스트 이름으로 사용하여 통신할 수 있음 . (예시
flask-mysql-network
라는 사용자 정의 네트워크를 정의하여 Flask와 MySQL 서비스가 서로 통신할 수 있도록 함.
networks:
flask-mysql-network:
Test
- 테스트 확인
docker-compose up
-
만약 Mac 사용자 중 Error가 발생이 되면 AirPlay Receiver를 해제 한다.