도커 컨테이너와 서비스: 햄버거로 이해하는 도커의 세계관

“햄버거 세트 하나 주세요”의 의미

지난 시간, 우리는 Dockerfile을 이용해 나만의 ‘도커 이미지(Image)’를 만들었다. 이것은 마치 빵 위에 패티를 올리고, 양상추를 얹는 순서가 적힌 ‘절대 변하지 않는 레시피’와 같다.

이제 이 레시피를 가지고 실제 장사(서비스)를 시작할 차례다. 그런데 도커를 쓰다 보면 헷갈리는 용어들이 튀어나온다. Container, Service, Stack… 다 비슷비슷해 보이는데 도대체 뭐가 다른 걸까?

이 개념을 잡지 못하면, 나중에 배포 스크립트를 짤 때 “왜 컨테이너를 켰는데 서비스가 안 올라가지?”라며 헤매게 된다. 오늘은 이 족보를 정리하기 위해 전 세계 공통어인 ‘햄버거 가게’ 비유를 가져와 봤다.

레시피(이미지)대로 햄버거(컨테이너)를 만들고, 같은 메뉴를 관리하며(서비스), 세트로 묶어 파는 것(스택)이다.

1. 컨테이너(Container): 주문받은 햄버거

  • 정의: 이미지를 실행해서 메모리에 올린 ‘실체’다.
  • 특징: 손님에게 레시피 종이(이미지)를 줄 순 없다. 레시피대로 조리된 따끈따끈한 ‘햄버거(컨테이너)’를 줘야 한다.
  • 명령어: docker run my-burger

내가 서버 한 대를 띄웠다면, 그건 ‘컨테이너 하나’를 실행한 것이다.

2. 서비스(Service): 같은 메뉴의 재고 관리

점심시간이 되어 손님이 몰려왔다. 햄버거 하나 굽는 속도로는 감당이 안 된다. 주방장을 늘려서 ‘치즈버거’를 미리 3개씩 만들어 두기로 했다. 이제 나는 “치즈버거 1개”가 아니라, “치즈버거 라인(Service)” 전체를 관리해야 한다.

  • 정의: 같은 이미지로 만든 ‘컨테이너들의 집합’.
  • 특징: “이 메뉴는 항상 3개의 재고를 유지해라(Replicas=3)” 같은 ‘정책’을 부여할 수 있다. 햄버거 하나를 떨어뜨리면(에러), 알아서 새 걸 굽는다.
  • 목적: ‘스케일 아웃(Scale-out)’. 트래픽 분산과 안정성.

3. 스택(Stack): 세트 메뉴 (풀 패키지)

햄버거만 먹으면 목이 멘다. 감자튀김(DB)도 먹어야 하고, 콜라(Redis)도 마셔야 한다. 이 모든 것들이 모여야 비로소 ‘완벽한 한 끼(애플리케이션)’가 된다.

  • 정의: 서로 다른 서비스들(웹, DB, 캐시)을 묶어놓은 ‘최상위 그룹’.
  • 특징: 햄버거와 콜라는 한 쟁반에 있어야 먹기 편하다(네트워크 연결). 스택으로 묶이면 이들끼리 서로 통신할 수 있는 전용 공간(Network)이 생긴다.

문제점: 주문하다 숨넘어가겠다

개념은 알겠는데, 이걸 실제로 운영하려면 머리가 아프다. 우리가 만들려는 웹 서비스(스택)에는 보통 다음과 같은 구성요소가 필요하다.

  1. Spring Boot (Burger): 메인 메뉴 2개 (이중화)
  2. PostgreSQL (Fries): 사이드 메뉴 1개
  3. Redis (Coke): 음료 1개

이걸 쌩 도커 명령어(docker run)로 띄우는 건, 마치 카운터에서 이렇게 주문하는 것과 같다.

“저기요, ‘치즈버거(Spring)’ 주시는데 패티는 미디엄 웰던으로 구워주시고요, 안에 피클은 빼주시고 소스는 많이 뿌려주세요. 아, 그리고 ‘감자튀김(DB)’도 주시는데 소금은 치지 마시고 케첩은 3개 주시고요. 마지막으로 ‘콜라(Redis)’는 제로 콜라로 주시는데 얼음은 반만 채워주세요. 그리고 이 3개를 ‘하나의 쟁반(Network)’에 담아주세요.”

# 실제로는 이런 명령어를 매번 쳐야 한다
$ docker run -d --name my-web -e PATTY=medium -e PICKLE=no -p 8080:8080 ...
$ docker run -d --name my-db -e SALT=no ...
$ docker run -d --name my-redis -e ICE=half ...

매번 배포할 때마다 이 긴 주문을 오타 없이 외울 수 있을까? 순서가 틀려서 햄버거보다 감자튀김이 늦게 나오면? 손님(유저)은 화를 낼 것이다.

해결사: 도커 컴포즈 (Docker Compose) – “먹던 걸로 주세요!”

그래서 개발자들은 생각했다. “이 복잡한 주문 내용을 ‘메뉴판(문서)’에 미리 적어놓고, 그냥 ‘1번 세트 주세요’라고 할 수는 없을까?”

그렇게 탄생한 것이 ‘도커 컴포즈(Docker Compose)’다. docker-compose.yml이라는 파일에 우리만의 ‘커스텀 세트 메뉴’를 정의해 두면, 도커가 알아서 주방에 오더를 넣고 착착착 쟁반에 담아준다.

복잡한 커스텀 주문도 메뉴판(문서)에 적어두면 “이거 주세요” 한 마디로 끝난다.

[Action] 나만의 세트 메뉴 만들기

말로만 하지 말고, 실제로 내 로컬에 있는 스프링 서버와 DB를 한 방에 띄워보자. 프로젝트 루트 폴더에 docker-compose.yml 파일을 만들고 아래 내용을 붙여넣기만 하면 된다.

version: '3.8'

services:
  # 1. 감자튀김 (DB)
  my-database:
    image: postgres:14
    environment:
      POSTGRES_PASSWORD: mysecretpassword
    ports:
      - "5432:5432"

  # 2. 치즈버거 (Web)
  my-web-server:
    image: my-spring-app:latest
    ports:
      - "8080:8080"
    depends_on:
      - my-database # "감자튀김 먼저 튀겨져야 버거 나갑니다" (순서 보장)
    environment:
      DB_URL: jdbc:postgresql://my-database:5432/mydb

이제 터미널에서 마법의 주문을 외운다.

# "여기 1번 세트(docker-compose.yml) 하나요!"
$ docker-compose up -d

결과: 도커가 이 메뉴판을 읽고 DB를 먼저 띄운 뒤, DB가 준비되면 스프링 서버를 띄운다. 이것이 바로 ‘IaC (Infrastructure as Code)’, 즉 “인프라를 코드로 관리한다”는 개념의 시작이다. 이제 복잡한 서버 구성도 ‘메뉴판 파일’ 하나만 공유하면 끝이다.

마치며: Works on My Machine? Works on ANY Machine!

이 시리즈의 시작이었던 “내 컴퓨터에선 되는데…”라는 변명은 이제 통하지 않는다. 우리는 ‘리눅스(OS)’를 이해했고, ‘도커(Container)’로 환경을 격리했으며, ‘도커 컴포즈(Compose)’로 복잡한 서비스들을 코드 한 장에 정의했다.

이제 내가 만든 docker-compose.yml 파일만 있으면, 내 컴퓨터든, 팀장님 컴퓨터든, AWS 서버든 어디서나 똑같은 햄버거 세트를 1분 만에 차려낼 수 있다.

우리는 이제 ‘그냥 코더’가 아니라, 내가 짠 코드가 돌아갈 세상(환경)까지 설계할 줄 아는 ‘엔지니어’가 되었다. 이것으로 [Works on My Machine] 시리즈를 마친다.

다음 시리즈부터는 이 견고한 인프라 위에서, 귀찮은 배포 과정을 기계에게 맡기는 [The Pipeline: CI/CD 자동화]의 세계로 떠나보자.

“도커 컨테이너와 서비스: 햄버거로 이해하는 도커의 세계관”에 대한 1개의 생각

  1. yml에 들어가는 healthcheck나 devices 같은 추가설정도 나중에 작성해주시면 좋을것같아요
    잘봣습니다

    응답

댓글 남기기