Docker & System Architecture 가이드·약 10분 내외

Docker Compose 환경 분리와 Systemd 아키텍처 딥다이브 가이드

서버 재시작 시 도커 컨테이너가 유실되는 원인을 분석하고, Dev/Prod 환경의 완벽한 분리 방법부터 Systemd 데몬과 Docker의 계층 구조까지 심층적으로 다룹니다.

아키텍처 구조도

다이어그램 렌더링 중...

Q1. 서버 재시작 후 특정 컨테이너들만 유실되거나 Created 상태에 멈춰있는 이유는 무엇인가요?

이는 `docker-compose.yml` 파일 하나를 로컬 개발(Dev)과 서버 운영(Prod)에서 동시에 공유하며 발생한 프로젝트 이름 충돌(Project Name Conflict) 때문입니다.

충돌 발생 원리

•도커 데몬이 기존에 띄워져 있던 로컬 개발용 DB 컨테이너(`quizzer-db`)를 살려놓은 상태에서, 서버 데몬(`systemd`)이 `quizzer-prod`라는 프로젝트 이름으로 전체 컨테이너를 다시 띄우려 시도합니다.
•이때 DB 컨테이너 이름(`quizzer-db`)이 중복되어 충돌(Conflict) 에러가 발생합니다.
•이 에러로 인해 전체 실행 프로세스가 중간에 중단(Abort)되어 나머지 앱 컨테이너들이 생성조차 되지 않거나 껍데기(Created)만 남게 된 것입니다.

Q2. 로컬 스크립트(package.json)의 프로젝트 이름을 서버와 똑같이 맞추면, 알아서 충돌이 해결되나요?

아닙니다! 스크립트 수정은 미래의 충돌을 예방하는 조치일 뿐입니다.

이미 도커 데몬에는 서로 다른 프로젝트 소속으로 꼬여버린 컨테이너 찌꺼기들이 남아있습니다. 이들은 스크립트를 수정한다고 자동으로 삭제되지 않습니다.

반드시 1회 수동으로 `docker compose down`을 호출하여 꼬인 기존 컨테이너 잔여물들을 깨끗하게 청소한 뒤 서비스를 재시작해야만 정상 구동됩니다.

Q3. 왜 서버(Prod) 도커를 실행할 때 DB(Postgres, Redis)를 따로 실행해야 했나요? IJM은 어떻게 되어 있나요?

서버(Prod)를 실행할 때 DB를 따로 띄워야 한다는 것은 흔한 오해입니다! 실제 운영 서버는 `docker compose up -d` 한 번으로 앱과 DB 등 모든 컨테이너를 한꺼번에 띄웁니다.

따로 띄우는 스크립트(`pnpm infra`)는 오직 로컬에서 편하게 코딩하기 위해 앱은 직접 돌리고 DB만 도커로 띄우려는 목적으로 만들어진 개발자 편의용 스크립트입니다.

IJM 프로젝트의 모범 사례

IJM 프로젝트는 애초에 `docker-compose.dev.yml`(개발용)과 `docker-compose.prod.yml`(서버용) 파일을 분리했습니다. 컨테이너 이름도 `dev-postgres`처럼 다르게 지어 애초에 두 환경이 충돌할 일이 없도록 안전하게 설계되어 있습니다.

Q4. 이런 충돌을 근본적으로 해결하려면 구조를 어떻게 바꿔야 하나요?

IJM의 모범 사례처럼 목적에 따라 설정 파일을 물리적으로 쪼개야 합니다.

실천 방안

1. 기존 `docker-compose.yml`을 `docker-compose.prod.yml`로 이름 변경하여 프로덕션 전용으로 만듭니다.

2. DB만 들어있는 `docker-compose.dev.yml`을 새로 생성하고, 컨테이너 이름(예: `dev-db`)을 차별화합니다.

3. 무중단 배포 스크립트(`deploy.sh`)와 Systemd 서비스 파일이 명시적으로 `-f docker-compose.prod.yml`을 바라보도록 옵션을 추가합니다.

Q5. docker compose 명령어에서 -p 옵션은 무엇이고 왜 쓰나요?

`-p` 옵션은 컨테이너 그룹을 묶는 프로젝트 이름(Project Name)을 명시적으로 지정하는 옵션입니다.

기본적으로 도커 컴포즈는 명령어를 실행하는 현재 디렉터리(폴더)의 이름을 프로젝트 이름으로 사용합니다. 하지만 `-p quizzer`와 같이 강제 지정하면 디렉터리 위치와 상관없이 독립적인 환경을 구축할 수 있습니다.

-p 옵션 없이 실행

  • •폴더 이름(예: src)이 그대로 프로젝트 이름이 됨
  • •서로 다른 프로젝트라도 폴더명이 같으면 충돌 위험

-p quizzer로 명시 지정

추천
  • ✓중복 방지: 정확히 타겟팅하여 관리 가능
  • ✓식별 용이성: 모든 컨테이너와 네트워크 이름 앞에 quizzer_ 접두사가 붙어 관리가 편해짐

Q6. 도커 프로젝트 = 네트워크 = 서비스, 비슷한 계층의 개념인가요?

아닙니다. 세 가지는 명확한 포함 관계를 가진 상하 계층 구조입니다. 쉽게 비유하자면 프로젝트는 회사, 네트워크는 사내 전화망, 서비스는 각 부서에 해당합니다.

도커 컴포즈의 계층 구조

1. Project (프로젝트): 최상위 개념. 애플리케이션을 구동하기 위한 전체 테두리입니다. (-p 옵션 지정 영역)

2. Network (네트워크): 프로젝트 구성원들끼리 소통할 수 있는 인프라입니다. 프로젝트 생성 시 default 네트워크가 자동 생성됩니다.

3. Service (서비스): docker-compose.yml에 정의하는 개별 기능 단위(web, db 등)입니다.

즉, 하나의 프로젝트 안에 네트워크가 깔리고, 그 위에서 여러 개의 서비스가 돌아가는 수직적인 구조입니다.

Q7. 그렇다면 리눅스의 quizzer.service와 도커 서비스(Service)는 같은 건가요?

이름만 우연히 같을 뿐, 작동하는 세계(계층)가 완전히 다릅니다! 상단의 다이어그램을 참고해 보세요.

`quizzer.service`는 리눅스 운영체제(OS) 레벨의 파일입니다. 서버가 켜질 때 도커 컴포즈를 대신 실행해주거나 서버가 꺼질 때 안전하게 종료해주는 리눅스 전용 관리 껍데기입니다.

반면 도커의 서비스는 그 리눅스 껍데기가 실행시킨 도커 내부 레벨의 개별 앱 단위(Nginx, DB 등)입니다. 리눅스의 .service 파일이 더 거시적인 관점에서 도커 프로젝트 전체를 관리하는 것입니다.

Q8. 데몬(Daemon)이 무엇이고, sudo systemctl daemon-reload는 왜 필요한가요?

데몬(Daemon)은 눈에 보이지 않는 백그라운드에서 컴퓨터가 켜져 있는 내내 묵묵히 일하는 수호신 같은 프로그램입니다. (예: 시스템을 총괄하는 systemd, 도커를 관리하는 dockerd)

사용자가 리눅스 서비스 파일(`quizzer.service`)의 내부 코드를 수정하더라도, 총괄 데몬 관리자인 `systemd`는 그 사실을 즉각 알지 못하고 옛날 설정(메모리)을 그대로 고집합니다.

daemon-reload 명령어의 의미

이 명령어는 리눅스에게 "설정 파일을 새로 고쳤으니, 메모리를 지금 바로 최신 상태로 동기화(새로고침)해라"라고 지시하는 필수 작업입니다. 윈도우의 F5(새로고침)나 브라우저 강제 새로고침과 완벽히 같은 역할을 합니다.

가이드 모음

다른 가이드도 확인해보세요

이정마 프로젝트의 다양한 가이드와 꿀팁들을 확인하실 수 있습니다.