
6개월만에 끝나는 간단한 토이 프로젝트지만, 하고 싶어서 제대로 해보려고 한다.
처음에는 단순히 웹 서비스를 만들고 배포하는 게 목표였지만, CI/CD 환경까지 직접 구성해보면 더 많은 걸 배울 수 있을 것 같아서 시작했다.
이번 글에서는 프론트엔드와 백엔드를 각각 도커 컨테이너로 패키징하여 실행하는 과정을 다룬다.
프론트엔드는 React (포트: 3000), 백엔드는 FastAPI (포트: 8000)로 구성되어 있으며, 이를 Docker를 이용해 배포 가능한 환경으로 만든다.
도커 컨테이너를 만들기 전에 서버로 사용하는 우분투 서버 24.04.01 LTS에서 도커를 설치하는 작업 먼저 진행해보도록 하겠다.
sudo apt_get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt update
sudo apt-get install docker-ce docker-ce-cli containerd.io
위의 명령어를 차례로 입력하여 도커를 설치하도록한다.
도커 설치가 완료되었으면
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker $USER
sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
위의 과정을 마치면 docker --version, docker-compose --version 명령어를 입력했을 때 정상적으로 도커가 설치되었다면 Docker, Docker Compose 버전이 나오게 된다.
먼저 프론트엔드 먼저 도커 이미지로 빌드한 후 배포하는 과정까지 해보겠다.
프론트엔드는 리액트로 구성되어있다.
배포할 프로젝트의 루트 디렉토리로 가서 Dockerfile을 만들어준다.
파일에 들어가서
FROM --platform=linux/amd64 node:20-alpine
WORKDIR /src
COPY /package.json .
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "preview"]
이렇게 입력해준다.
나는 맥 환경에서 작업하는데 배포는 리눅스에서 하게 될꺼라 FROM에 --platform=linux/amd64을 추가했다.
제대로 되는지 테스트 하려면(현재 나는 서버 컴퓨터가 아닌 맥에서 작업함)
docker buildx build --platform linux/amd64 -t 계정명/이미지명:latest --push .
로 이미지 빌드를 하고, ssh 접속을 통해 서버PC 터미널에 접속했다.
docker pull 계정명/이미지명:latest
docker run -d --name 컨테이너명 -p 내부포트:외부포트 계정명/이미지명:latest
위의 명령어를 서버PC에 입력하면 우리가 빌드한 이미지가 서버PC에서 실행되게 된다.
docker ps -a : 정상적으로 컨테이너가 실행되는지 확인docker logs 컨테이너명 : 컨테이너의 로그 확인docker exec -it 컨테이너명 sh : 해당 도커 컨테이너 터미널에 접근테스트를 마쳤으면
docker stop 컨테이너명
docker rm 컨테이너명
을 입력해 컨테이너를 종료 및 삭제해준다.
다시 프로젝트 루트 디렉토리로 이동해서 docker-compose.yml 파일을 만들어준다.
version: "3.8"
services:
web:
build:
context: .
dockerfile: Dockerfile
container_name: web
ports:
- "3000:3000"
restart: always
networks:
- app-network
networks:
app-network:
나는 container_name을 web으로 지정했고, 외부포트 3000번 내부포트 3000번을 사용했다.
.env파일은 현재 아무것도 없지만 나중에 젠킨스를 하면서 Jenkinsfile에서 추가할 예정이다.
docker-compose -f docker-compose.yml up --build -d을 통해 컨테이너를 만들 수 있다.
docker-compose를 쓰는 좋은 이유는 원래 dockerfile만 만들면 이미지 빌드하고 원래 있던 컨테이너를 종료하고 삭제하고 빌드한 이미지를 실행시키는데 이를 한번에 처리해준다.
이제 프론트엔드 구성이 끝났으니 백엔드 도커 구성으로 넘어가 보겠다.
백엔드는 FastAPI로 구성되어 있다.
마찬가지로 백엔드 루트 디렉토리로 가서 Dockerfile을 만들어준다.
FROM --platform=linux/amd64 python:3.9-slim-bullseye
WORKDIR /src
RUN apt update && apt install -y \
libpq-dev gcc \
&& apt clean && rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "src.app:app", "--host", "0.0.0.0", "--port", "8000", "--reload", "--reload-dir", "src"]
그리고 docker-compose.yml도 만들어준다.
version: "3.8"
services:
api:
build:
context: .
dockerfile: Dockerfile
container_name: api
ports:
- "8000:8000"
restart: always
env_file:
- .env
networks:
- app-network
networks:
app-network:
이렇게 도커 컨테이너 구성을 마치게 되었다.
도커로 구성했을 시 장점은 내가 지금 맥을 쓰는데 이걸 리눅스 환경으로 맞춰서 도커 배포해버리면 플랫폼 상관없이 무조건 리눅스 환경에만 맞추면 되니깐 더 편리했던 것 같다.
또한 서버를 닫으려면 프론트엔드와 백엔드 각자 명령어나 백그라운드에 실행되어있는게 다른데 이를 그냥 docker stop 컨테이너명으로 쉽게 관리하고, 지금 실행 되어있는 서버도 docker ps -a로 쉽게 볼 수 있다는게 편리했었다.
물론 초기 세팅에 귀찮다는 점이 있지만, 익숙해지면 별거 아닌 정도로 간단해진다.