Docker Swarm Cluster 환경에서 Fast-API & MongoDB 연결 - Docker Compose & Secret 사용

Docker Swarm Cluster 인프라 환경에서 CI/CD Pipelines 구축을 연습하기 위해, 간단한 2-Tier Web 서비스를 제작하는 과정에서 FastAPI와 MongoDB를 사용했다.

 

Cluster 상에서 FastAPI와 MongoDB를 연결하기 위해서 꽤나 고생을 했어서 이에 대한 기록을 남겨보려고 한다.

Fast-API와 MongoDB의 관계를 보여주는 Diagram


개요 및 목표

Docker Swarm Cluster 상에서 Fast-API와 MongoDB 서비스를 배포시켜 Fast-API에서 데이터를 MongoDB 시스템에 CRUD를 실시할 수 있도록 Docker Compose를 통해 구성하는 방법을 확인한다.

 

docker-compose.yml의 내용에 앞서 연결을 위해 구성해야하는 요소를 정리하면 아래와 같다.

  • MongoDB 서비스의 설정
    • Root 계정명
    • Root 계정 PassWord
  • MongoDB에 접속하기 위한 Fast-API 설정

추가적으로 MongoDB의 Root PassWord 또는 Fast-API가 사용할 사용자와 비밀번호 등에 대한 정보를 Docker Secret 객체를 이용하여 암호화하는 것이 가능하다.

 

본인은 Docker Secret 객체를 이용하여 MongoDB가 사용할 Root 계정 명과 비밀번호를 암호화하고, Fast-API에서도 해당 Secret 객체를 사용하여 MongoDB 접속 정보를 선언했다.


1. Docker Secret 객체 배포

Secret 객체를 생성하여 사용하는 목적은 비밀번호, 토큰 등을 암호화하고, 컨테이너 내부에서 사용할 수 있도록 하는 것이다.

 

따라서, docker-compose.yml 내부에서 Secret 객체와 관련된 부분은 해당 Secret 객체를 사용하겠다고 지정하는 것 뿐이다.

 

이를 사용하기 위해 Swarm Cluster의 Manager Node에서 아래와 같이 Secret 객체를 생성한다.

echo "root" | docker secret create mongodb_root_username -
echo "test123" | docker secret create mongodb_root_password -

생성된 Secret 객체를 확인할 수 있다.


2. docker-compose.yml 파일 작성

다른 부분은 제외하고, Secret 객체를 MongoDB에서 사용하는 부분을 먼저 확인한다.

Secret 객체 사용 선언

secrets:
  mongodb_root_username: # 내부에서 사용하는 시크릿 명
    external: true
    name: mongodb_root_username # 외부에 선언했던 시크릿 명
  mongodb_root_password:
    external: true
    name: mongodb_root_password

networks:
  web_network:
    driver: overlay

Secret을 별도로 선언하지 않고 바로 사용하는 것 또한 가능하지만, 확장성을 위해 docker-compose.yml로 별도의 Secret을 생성하는 방식을 사용했다.

 

위의 구성을 살펴보면,

  • 사용할 Secret 명 선언
    • 해당 Secret은 docker-compose.yml에서 선언하는 것이 아닌 외부의 Secret에서 제공된다.
    • 해당 외부 Secret 명

현재는 외부 Secret 명과 내부 Secret 명이 같아 좀 헷갈린다.

 

추가로, 사용할 네트워크를 Cluster 내에서 사용하기 위해 overlay 방식으로 선언한다.


MongoDB 서비스 선언

services:
  mongodb:
    image: mongo
    ports:
      - 27017:27017
    volumes:
      - nfs_data:/data/db
    environment:
      - MONGO_INITDB_ROOT_USERNAME_FILE=/run/secrets/mongodb_root_username
      - MONGO_INITDB_ROOT_PASSWORD_FILE=/run/secrets/mongodb_root_password
      - MONGO_INITDB_DATABASE=webdb
    secrets:
      - source: mongodb_root_username
        target: /run/secrets/mongodb_root_username
      - source: mongodb_root_password
        target: /run/secrets/mongodb_root_password
    deploy:
      placement:
        constraints: [node.role == manager]
      replicas: 1
    networks:
      - web_network

MongoDB에서 사용할 Root 계정 명과 비밀번호를 선언하기 위해 Secret 객체를 사용하는데, 이를 위해서 Secret 객체를 컨테이너 내부로 전달해주는 작업을 선언했다.

 

이후 해당 파일을 통해 Root 계정 명과 비밀번호를 환경변수로 선언하였다.

 

다른 내용은 생략한다.


Fast-API 서비스 선언

fastapi:
    image: 192.168.1.99:5000/web:latest
    ports:
      - "8888:8888"
    environment:
      - DATABASE_URL=mongodb://dummy_url
    secrets:
      - source: mongodb_root_username
        target: /run/secrets/mongodb_root_username
       - source: mongodb_root_password
        target: /run/secrets/mongodb_root_password
    deploy:
      placement:
        constraints: [node.role == worker]
      mode: global
    networks:
      - web_network
    depends_on:
      - mongodb

Fast-API 서비스는 사설 이미지 저장소에 존재하는 별도의 이미지를 사용했고, Fast-API 동작을 위해 추가적인 설정을 진행했다.

 

Fast-API의 경우, 환경변수로 사용하는 것이 가능하지만, 별도 로직을 통해 MongoDB Root 사용자 비밀번호를 처리하도록 Secret 객체를 컨테이너 내부에 전달했다.

 

이 부분을 환경변수로 선언시켜 사용하는 것 또한 가능하다고 생각한다. 불필요한 로직


3. Fast-API에서 Secret 객체를 사용해 MongoDB와 연결

from models.otas import OTA
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseSettings
import os

MONGODB_ROOT_USERNAME = open("/run/secrets/mongodb_root_username", "r").read().strip()
MONGODB_ROOT_PASSWORD = open("/run/secrets/mongodb_root_password", "r").read().strip()

class Settings(BaseSettings):
    DATABASE_URL: Optional[str] = None
    DATABASE_NAME: Optional[str] = "webdb"

    async def initialize_database(self):
        self.DATABASE_URL = f"mongodb://{MONGODB_ROOT_USERNAME}:{MONGODB_ROOT_PASSWORD}@mongodb:27017/?authSource=admin"
        client = AsyncIOMotorClient(self.DATABASE_URL)
        await init_beanie(database=client[self.DATABASE_NAME], document_models=[OTA])

위와 같은 로직을 사용하여 Fast-API 객체가 시작할 때, Secret에서부터 전달받은 파일을 통해 비밀번호를 제공받을 수 있다.

 

파일을 통해 제공받은 MongoDB Root 사용자 명과 비밀번호를 통해 MongoDB에 CRUD가 가능해진다.


결론

Docker Swarm Cluster 환경에서 Secret 객체를 사용하여 MongoDB와 Fast-API 환경구성을 암호화하고, 연결하는 방법을 확인해봤다.

 

이를 통해 얻을 수 있는 장점은 노출되면 안될 정보(비밀번호, 토큰)를 암호화하여 사용하는 것이 가능하다는 것이다.

 

K8s 환경에서도 Secret 객체를 이용하여 환경설정을 해줘야겠다.

 

아래는 위 과정이 포함된 Gitlab-Runner를 사용한 CI/CD Pipelines을 구성한 깃허브 내용이다.

 

GitHub - Choiwonwong/CW2-KCS-EC-DP: 카카오클라우드스쿨 엔지니어 클래스 도커 프로젝트용 웹서버

카카오클라우드스쿨 엔지니어 클래스 도커 프로젝트용 웹서버. Contribute to Choiwonwong/CW2-KCS-EC-DP development by creating an account on GitHub.

github.com

 

728x90
반응형