도커를 쓰면서 그냥 편리한 리눅스 서버 구축 가능한 소프트웨어 정도로만 생각하고 있었는데, 앞으로 도커를 많이 쓰게 될 것 같아 정확한 개념을 잡고 넘어가보자한다.
도커를 쓰다보면 compose.yaml이니 backend.Dockerfile이니 여러 파일들이 관련되어 나오는데 코드를 봐보려고 하면 자연어로는 쓰여있는데 image니 container니 상당히 복잡하게 쓰여져있다.
그리고 이걸 통합해서 이해하려면 상당히 파편화되어있어 한번에 설명을 해보고자 한다.
컨테이너와 이미지
난 처음에는 컨테이너라는 작은 리눅스 컴퓨터를 먼저 만들고, 그 안에 이미지를 다운로드하는 방식인 줄 알았다. 그런데 실제 순서는 반대였다. 이미지는 프로그램 실행에 필요한 Linux 파일, 라이브러리, 프로젝트 코드, 기본 명령을 묶어둔 읽기 전용 템플릿이다. 컨테이너는 작은 컴퓨터라기보다 그 이미지를 이용해 실행한 격리된 프로세스 집합 (하나의 메인 프로세스 + 자식 프로세스) 에 가깝다.
1이미지로 파일시스템 준비 2→ 네트워크·볼륨·격리 환경 구성 3→ 그 환경에서 메인 프로세스 실행 4= 실행 중인 컨테이너
라고 할 수 있다.
compose.yaml에서는 프로젝트에 필요한 서비스들을 선언한다.
- image:만 있으면 로컬에서 먼저 찾고, 없으면 Docker Hub 같은 저장소에서 완성된 이미지를 받는다.
- build:가 있으면 지정된 Dockerfile을 읽어서 기반 이미지, 패키지, 프로젝트 코드를 조합해 우리 프로젝트 이미지를 만든다.
그래서 redis나 postgreSQL 등은 이미 업로드 되어있는 만들어진 외부 이미지를 사용하고, 프로젝트에 따라 다른 백엔드나 웹 같은 경우는 Dockerfile을 실제로 우리가 따로 정의해서 사용한다.
Dockerfile?
Dockerfile에 대해서도 정리해보면, Dockerfile은 우리 프로젝트 이미지의 내부 구성을 정의하는 파일이다.
예를 들어 백엔드 이미지는 대략 이런 방식으로 만들어진다.
1외부 Python 이미지 2+ 필요한 Python 패키지 3+ 프로젝트 코드 4+ 기본 실행 명령 5= 프로젝트 백엔드 이미지
Dockerfile의 FROM은 기반으로 사용할 외부 이미지를 지정한다.
FROM python:3.12-slim
Docker는 이 Python 이미지를 로컬에서 먼저 찾고, 없으면 Docker Hub에서 다운로드한다. 그 위에 RUN으로 패키지를 설치하고, COPY로 프로젝트 코드를 추가한다.
COPY app ./app RUN pip install ... CMD ["uvicorn", "app.main:app"]
각 작업 결과는 이미지의 읽기 전용 레이어로 차례대로 쌓인다.
처음에는 Dockerfile 안에 FROM이 여러 개 있어서 이미지 여러 개를 만드는 줄 알았다. 하지만 대부분은 하나의 최종 이미지를 만들기 위한 제작 단계였다.
결론
이미지가 준비되면 Docker Engine이 네트워크와 볼륨을 만들고, API·웹·DB·Redis 등을 각각 별도의 컨테이너 프로세스로 실행한다.
Windows에서는 Linux 컨테이너를 직접 실행할 수 없기 때문에 Docker Desktop이 WSL2 Linux VM을 사용한다. VM 안에는 Linux 커널 하나가 있고, 여러 컨테이너가 그 커널을 공유한다.
1Windows 2└─ WSL2 Linux VM 3 └─ Linux 커널 4 ├─ API 컨테이너 5 ├─ Web 컨테이너 6 ├─ PostgreSQL 컨테이너 7 └─ Redis 컨테이너
이렇게 컨테이너는 프로세스이고, 도커 내부의 vm을 여러 컨테이너가 공유하는 시스템이다.
결국 docker compose up --build의 의미는
Compose 설정을 읽고, 필요한 이미지를 다운로드하거나 빌드한 다음, 네트워크·볼륨·컨테이너를 만들고 모든 프로세스를 실행하라는 뜻이다.
참고로 up은 컨테이너들을 생성하고 실행한다는 명령이고, --build는 실행 전에 프로젝트 이미지를 빌드하라는 옵션이다.