old/WebProject

[FastAPI] 2. FastAPI - MariaDB(MySQL) Docker Compose 로 연동, Project 구조 세팅

뒷골목프로그래머 2022. 9. 26. 12:02
반응형

 이전 포스트에서 local Docker Container에 띄워져 있는 MariaDB와 FastAPI를 연동하였습니다. 그런데 Project를 Heroku(또는 AWS)에 배포 한다고 생각했을 때 docker-compose로 배포하는 것이 낫다고 생각하였습니다. 문제는 제가 docker-compose를 사용한 적 이 없다는 것이었고 직접 연동해 보았습니다.

 docker-compose 적용을 결정하면서 docker-compose.yml 파일 하나로 DB, Backend, Frontend, nginx 세팅이 가능하도록 프로젝트 구조를 잡았습니다. 또한, Backend 내부적으로 보았을 때 유지보수성을 확보할 수 있는 구조를 고민하였습니다. 토이 프로트 처럼 작은 규모의 프로젝트의 구조를 정하는 작업은 언제나 두 가지 고민이 떠오릅니다.

 

'Project 규모가 작은데 굳이 모듈화(분리) 시킬 필요가 있을까?'

 실제로 규모가 작은 경우에는 FastAPI 공식문서의 예제들 처럼 main.py에서 관리하는 것이 훨씬 편합니다. 굳이 이 파일 저 파일 왔다갔다 하지 않고도 작업 할 수 있기 때문입니다.

 

'언제 규모가 커질 지 모르니 귀찮더라도 유지보수성을 향상 시키자'

 그럼에도 불구하고 저는 프로젝트를 계층화 시켜서 유지보수성을 향상 시키는 쪽을 선택 하였습니다. 경험이 많다고는 말씀드릴 수 없지만 프로젝트 규모는 언제나 확장되는 방향으로 갔었고 계층화 시키지 않은 프로젝트의 경우, 회고를 하면서 항상 후회 했던 기억이 있습니다. 

 

1. Project 구조

       Backend, DB를 docker-compose로 연동하여 배포하기 위해 다음과 같이 프로젝트 구조를 세팅하였습니다.

       1) docker-compose.yml

           docker-compose.yml 파일과 동등한 위치에 backend, db, frontend, nginx 를 배치

project_root
│
├── docker-compose.yml
├── backend
│   └── app
│       ├── main.py
│       ├── Dockerfile
│       ├── requirements.txt
│       │
│       ├── business    # 비즈니스 로직 구현 (여러 Service 통합 지점)
│       ├── config      # db연결
│       ├── controllers # API Router
│       ├── models      # Table Schema
│       ├── schemas     # Request, Response Schema
│       └── services    # CRUD 구현
│
└── db # DB 설정
│   ├── conf.d
│   ├── data
│   └── initdb.d
│
├── frontend # 추후 반영
├── nginx # 추후반영

       2) Backend 구조

            API Collection별로 API router 역할을 하는 Controllers에 작성하고 각각 Business Logic을 담당하는 계층과 연결 됩니다. 이 계층은 DB Data를 불러오는 Services와 연결되어 결과 값을 불러오고 API의 목적에 맞는 output으로 변형되어 Controllers로 전달합니다. (해당 내용은 docker-compose 와는 관련이 없습니다. Project 구조를 고민하시는 분들 참고만 하시기 바랍니다.)

         

Backend 프로젝트 구조

 

2. Dockerfile

     Backend 서버가 될 Docker Image를 만들고 Docker Container 환경을 정의하기 위해 Dockerfile을 생성합니다.  (backend/app/Dockerfile)

   

# backend/Dockerfile


# pull official base image
FROM python:3.10.6-slim-buster

# set working directory
WORKDIR /usr/src/app

# set environment variables
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

# install system dependencies
RUN apt-get update \
  && apt-get -y install netcat gcc \
  && apt-get -y install default-libmysqlclient-dev \
  && apt-get clean

# install python dependencies
RUN pip install --upgrade pip \
  && pip install --upgrade setuptools
COPY requirements.txt .
RUN pip install -r requirements.txt

# add app
COPY app .

 

 

3. docker-compose.yml

version: '3.8'

services:
  db:
    image: mariadb:10
    container_name: db
    ports:
      - 3306:3306
    volumes:
      - ./db/conf.d:/etc/mysql/conf.d
      - ./db/data:/var/lib/mysql
      - ./db/initdb.d:/docker-entrypoint-initdb.d
    env_file: .env
    environment:
      TZ: Asia/Seoul
    networks:
      - backend
    restart: always

  backend:
    build: ./backend
		container_name: backend
    command: uvicorn app.main:app --reload --workers 1 --host 0.0.0.0 --port 8000
    volumes:
      - ./backend:/usr/src/app
    ports:
      - 8004:8000
    environment:
      - ENVIRONMENT=dev
      - TESTING=0
    networks:
      - backend
    depends_on:
      - db
networks:
  backend:

작성한 docker-dompose.yml의 내용을 하나하나 살펴보겠습니다.

  • version: docker-compose format 버전 입니다. '3.8'은 최신 버전입니다. 최신 docker-compose 문법을 사용하지 않은 상태에서 굳이 사용할 필욘은 없습니다. 통상 '3'을 많이 쓴다고 합니다.
  • services: docker container 설정(생성)
    • db
      • image (mariadb:10)
        • Docker Image를 pull, Local에 해당하는 name의 Image가 없는 경우 DockerHub의 Image를 pull
      • container_name
        • 원하는 이름으로 container를 생성하기 위해 사용
        • DB 연동 시 DB_HOST를 ip주소가 아닌 container name으로 사용 가능 (networks 참고)
      • ports
        • Container와 communication을 위한 포트 포워딩
      • volumes
        • Host와 Container의 디렉토리를 연결
        • [Host 저장소]:[Container 저장소]
        • db 디렉토리 생성 후 하위에 3개의 디렉토리(conf.d, data, initdb.d)를 추가 생성함
          • conf.d/my.cnf : mysql 기본 설정
            # conf.d/my.cnf
            
            [client]
            default-character-set = utf8mb4
            
            [mysql]
            default-character-set = utf8mb4
            
            [mysqld]
            character-set-client-handshake = FALSE
            character-set-server           = utf8mb4
            collation-server               = utf8mb4_unicode_ci
          • initdb.d
            • create_table.sql
            • load.data.sql
      • env_file
        • docker-compose는 .env 파일을 그대로 읽어와 사용할 수 있음 (docker-compose.yml과 동등한 위치에 생성)
        • DB 설정과 같은 민감 정보를 .env 파일을 활용해 보호함과 동시에 간편하게 연결 할 수 있음
        • .env
          MYSQL_HOST=<host>
          MYSQL_PORT=<port>
          MYSQL_ROOT_PASSWORD=<root_password>
          MYSQL_DATABASE=<database_name>
          MYSQL_USER=<user_name>
          MYSQL_PASSWORD=<user_password>
      • environment:
        • TZ: 생성된 DB의 Timezone
      • restart (중요)
        • always: Docker 재 시작 등의 상황에서 자동으로 Container를 재 시작하는 option
      • networks : 별도 설명 참고
    • backend
      • command: container 생성 후 실행 할 명령어
      • depens_on
        • 여러 Container를 생성하여 연결하는 docker-compose의 경우, 생성 시점에 따라 문제가 발생하는 경우가 있을 수 있음 (DB보다 Backend Container가 먼저 생성되어 구동될 경우 'db connecetion fail error' 발생)
        • 먼저 생성 되어야하는 service 명을 입력하여 순서를 제어 할 수 있음
  • Networks
    • Container 간 Communication을 위해 custome network를 생성 하여 사용
    • 각각의 service에 생성한 networks 부분에 생성한 custom networks 값으로 묶어줌
    • network 명칭은 [어플리케이션이름]_[정의한 network 명칭] 으로 정해짐
    • 본 프로젝트의 경우, docker-compose.yml 파일 최하단에 'backend' 라는 이름으로 custom network를 새성하였고 각각의 service에 이를 명시하여 사용 중
    • network를 inspect 해보면, Containers에 service에서 정의한 db와 backend가 포함되어 있음을 확인 가능
      docker network inspect [application name]_[custom network name]
      [
          {
              "Name": "life_sports_backend_backend",
              "Id": "216757e7ee77c080a2d871ab149bbb864a0819dd0272836c40522ab4aec237c5",
              "Created": "2022-09-21T05:32:32.016644426Z",
              "Scope": "local",
              "Driver": "bridge",
              "EnableIPv6": false,
              "IPAM": {
                  "Driver": "default",
                  "Options": null,
                  "Config": [
                      {
                          "Subnet": "0.0.0.0/16",
                          "Gateway": "0.0.0.1"
                      }
                  ]
              },
              "Internal": false,
              "Attachable": true,
              "Ingress": false,
              "ConfigFrom": {
                  "Network": ""
              },
              "ConfigOnly": false,
              "Containers": {
                  "2ec77560d21e55c99d8faa5b475d9db169302c46796f27d26c6392c9e001822f": {
                      "Name": "backend",
                      "EndpointID": "15fba2e62138ec6f746d55fb3aeda95377ddde1705f6038bd8c1169bb21fddc0",
                      "MacAddress": "",
                      "IPv4Address": "0.0.0.0/16",
                      "IPv6Address": ""
                  },
                  "ee1803657f04e7bda712f865c17c0b11f29f83a3d2f4f53d4b8abf910f5ed4ea": {
                      "Name": "db",
                      "EndpointID": "b274b7ddb234b7f96e28329ee4584b50e48afd270d1908158accfd2eaf006575",
                      "MacAddress": "",
                      "IPv4Address": "0.0.0.0/16",
                      "IPv6Address": ""
                  }
              },
              "Options": {},
              "Labels": {
                  "com.docker.compose.network": "backend",
                  "com.docker.compose.project": "application_name",
                  "com.docker.compose.version": "1.29.2"
              }
          }
      ]

 

4. 실행

    • FastAPI 내부 DB 연결 정보 변경
        • 기존에 IP 주소로 연결 해놓았던 DB Host 정보를 생성한 Container이름인 'db'로 변경
          # /backend/app/conofig/secrets.json
          
          {
              "DB": {
                  "user": "{ DB-USER-NAME }",
                  "password": "{ DB-PASSWORD }",
                  "host": "db",  # container 명칭
                  "port": { DB-PORT },
                  "database": "{ DB-NAME } "
              }
          }
    • docker-compose 실행
      docker-compose build
      docker-compose up -d

 

5. 맺음말

 docker-compose로 FastAPI와 DB 연동하는 내용만 작성하려다 보니 Docker Networks를 잘 몰라서 에러를 정말 많이 냈던 기억 때문에 docker-compose.yml 파일을 좀 상세히 다뤄보았습니다. 특히, Networks가 가장 어려웠다 보니 이 부분을 docker inspect를 통해 확인 까지 해보았습니다. 모두 작성하고 보니 제목에 비해 내용이 비대해진 것 같습니다. 부족한 글 이오니 궁금하신 점 댓글로 남겨주시면 성심 껏 답변 드리겠습니다. 감사합니다.

반응형

'old > WebProject' 카테고리의 다른 글

[FastAPI] 3. FastAPI - MariaDB(MySQL) Restful API / CRUD  (0) 2022.09.26
[FastAPI] 0. 프로젝트 개요  (0) 2022.09.15