티스토리 뷰

반응형

이 글은 Docker 101 시리즈의 첫 번째 글입니다.

Docker를 처음 접하면 대개 이렇게 이해합니다. "개발 환경을 쉽게 맞춰 주는 도구구나." 맞는 말입니다. 하지만 이 설명만으로는 왜 팀들이 Docker를 표준처럼 쓰는지, 왜 컨테이너를 하나의 운영 단위로 보는지까지는 잘 보이지 않습니다. 진짜 핵심은 편의성보다 재현성에 있습니다. 누가 실행하든, 어디서 실행하든, 같은 이미지를 기준으로 같은 동작을 만들 수 있어야 한다는 문제를 Docker가 정면으로 다루기 때문입니다.

현업에서는 이 차이가 생각보다 큽니다. 개발자 노트북, CI, 스테이징, 운영 환경이 서로 조금씩 다르면 문제는 늘 애매하게 터집니다. 코드가 잘못된 것인지, 의존성이 다른 것인지, 운영 서버 설정이 다른 것인지 분간하는 데 시간을 다 써 버리기 쉽습니다. Docker는 바로 그 모호함을 줄이는 도구입니다.

Docker 101 1장 흐름 개요

Docker의 본질은 편의성이 아니라 재현성입니다 — '누가·어디서 실행하든 같은 이미지를 기준으로 같은 동작이 나온다'는 한 약속이, 개발자 노트북·CI·스테이징·운영의 미묘한 차이로 디버깅 시간을 다 써 버리던 문제를 정면으로 줄입니다.

먼저 던지는 질문

  • Docker는 정확히 무엇을 해 주는 도구일까요?
  • 컨테이너와 가상머신은 무엇이 다를까요?
  • image, container, registry는 어떤 관계로 이해해야 할까요?

왜 이 글이 중요한가

환경 차이는 입문자만 힘들게 하는 문제가 아닙니다. 숙련된 팀도 같은 문제로 시간을 잃습니다. 로컬에서는 되는데 CI에서만 실패하고, 어떤 개발자 노트북에서는 되는데 다른 노트북에서는 라이브러리 버전이 달라 오류가 나는 장면은 너무 흔합니다. 이런 문제는 개인 역량보다 시스템 설계의 문제에 가깝습니다.

Docker가 중요한 이유는 단순합니다. 코드만 공유하는 것이 아니라 실행 환경까지 함께 공유하게 만들기 때문입니다. 즉, 팀이 "어떻게 실행해야 하는가"를 위키 문서나 입소문이 아니라 이미지와 명령으로 표준화할 수 있습니다.

핵심 용어

  • Image: 코드, 라이브러리, 런타임을 포함한 실행 가능한 패키지입니다.
  • Container: 이미지를 실제로 실행한 인스턴스입니다.
  • Registry: 이미지를 저장하고 배포하는 저장소입니다. 예를 들어 Docker Hub, GHCR이 있습니다.
  • Daemon: 컨테이너를 생성하고 관리하는 백그라운드 프로세스입니다.
  • Layer: 이미지 내부에서 변경이 쌓이는 단위입니다.

이 용어는 처음엔 비슷해 보여도 역할이 분명히 다릅니다. 특히 image와 container를 분리해서 이해하지 못하면 이후 Dockerfile, volume, 배포 운영까지 전부 흐려집니다. 이미지는 배포 단위이고, 컨테이너는 실행 단위라는 구분부터 정확히 잡는 것이 좋습니다.

전과 후

Before: "제 노트북에서는 돌아갑니다." 새 팀원 환경 구성에 반나절이 걸립니다.

After: docker run myapp 한 줄로 같은 환경을 바로 실행합니다.

이 변화가 중요한 이유는 설치 시간이 짧아져서가 아닙니다. 팀이 같은 문제를 같은 방식으로 재현할 수 있게 되기 때문입니다. 재현성이 생기면 디버깅이 쉬워지고, 디버깅이 쉬워지면 배포 속도와 운영 안정성도 함께 올라갑니다.

실습: 첫 컨테이너를 5단계로 실행해 보기

1단계 — 설치 확인

docker --version
# Docker version 25.x.x
docker run hello-world

가장 먼저 확인할 것은 Docker가 설치되었는가가 아니라, 실제로 이미지를 받아 컨테이너를 실행할 수 있는가입니다. hello-world는 바로 그 확인용으로 가장 적절합니다.

2단계 — 공식 이미지 실행

docker run -it --rm python:3.12-slim python -c "print('hi')"

이 명령은 Python이 로컬에 설치되어 있지 않아도, 이미지 안의 런타임으로 바로 명령을 실행할 수 있음을 보여 줍니다. 여기서 중요한 포인트는 "내 컴퓨터에 Python을 맞춰 깔았다"가 아니라 "필요한 런타임을 이미지가 이미 포함한다"는 점입니다.

3단계 — 백그라운드 실행

docker run -d --name web -p 8080:80 nginx
curl http://localhost:8080

웹 서버처럼 계속 살아 있어야 하는 프로세스는 보통 백그라운드로 실행합니다. 이 단계에서 함께 익혀야 할 것은 포트 매핑입니다. 컨테이너 안의 80 포트를 호스트의 8080 포트로 연결해야 브라우저나 curl로 접근할 수 있습니다.

4단계 — 상태 확인

docker ps              # running
docker logs web        # logs
docker stop web && docker rm web

컨테이너를 실행하는 것만큼 중요한 것이 관찰과 정리입니다. 어떤 컨테이너가 떠 있는지, 로그는 무엇인지, 다 쓴 컨테이너를 어떻게 내릴지를 일찍부터 익혀 두는 편이 좋습니다. 운영 습관은 이런 기본 명령에서 시작됩니다.

5단계 — 이미지 검색과 다운로드

docker pull redis:7-alpine
docker images

이미지는 보통 직접 만들기도 하지만, 공식 이미지를 가져와 출발점으로 쓰는 경우도 많습니다. 따라서 pull과 images는 이후 모든 실습의 기본 명령이 됩니다.

실행 뒤 바로 확인할 것

  • docker run hello-world 뒤에는 Docker가 이미지를 내려받고 테스트 컨테이너를 실행했다는 성공 메시지가 보여야 합니다.
  • curl http://localhost:8080은 nginx 기본 HTML을 반환해야 합니다. 빈 응답이나 연결 거부가 나오면 포트 매핑부터 다시 확인합니다.

잘 안 될 때 먼저 볼 것

  • Docker Desktop이나 Docker daemon이 실제로 떠 있는지 먼저 확인합니다. 설치만 끝나고 엔진이 내려가 있는 경우가 흔합니다.
  • docker ps에 컨테이너가 떠 있는데 접속이 안 되면 -p 8080:80처럼 호스트 포트와 컨테이너 포트를 뒤집지 않았는지 확인합니다.

이 코드에서 먼저 봐야 할 점

  • image는 실행 직전의 정적 산출물이고, container는 실제로 동작하는 프로세스입니다.
  • -p 8080:80은 호스트 포트와 컨테이너 포트를 연결합니다.
  • --rm은 일회성 실행 뒤 흔적을 남기지 않도록 정리해 줍니다.

입문 단계에서 이 세 가지를 정확히 이해하면 뒤에서 나오는 Dockerfile, Compose, 운영 주제들이 훨씬 잘 이어집니다. 반대로 처음부터 Docker를 "가상머신 비슷한 것"으로 이해하면 파일시스템, 프로세스, 네트워크 개념이 모두 어긋나기 시작합니다.

자주 하는 실수 다섯 가지

  1. Docker를 가상머신처럼 생각합니다. 컨테이너는 호스트 커널을 공유합니다.
  2. 프로덕션에서 latest 태그를 그대로 씁니다. 어느 날 조용히 다른 버전으로 바뀔 수 있습니다.
  3. 컨테이너를 정리하지 않고 계속 쌓아 둡니다. 디스크와 상태 확인이 금방 지저분해집니다.
  4. 포트 매핑 없이 실행하고 접속이 안 된다고 판단합니다. 서비스가 떠 있어도 외부에서는 보이지 않습니다.
  5. root 실행을 당연하게 여긴 채 운영까지 갑니다. 초기에 방치한 습관이 나중에 보안 문제로 이어집니다.

이 다섯 가지는 전부 작은 오해에서 시작합니다. 하지만 컨테이너 수가 늘고 팀이 커질수록 이런 오해는 디버깅 비용, 보안 위험, 운영 혼선으로 바로 이어집니다. 처음부터 멘탈 모델을 바르게 잡는 이유가 여기에 있습니다.

실무에서는 이렇게 이어집니다

현업에서는 서비스 하나를 하나의 컨테이너로 패키징하고, 같은 이미지를 로컬 개발, CI, 스테이징, 운영 환경에 반복해서 사용합니다. 즉, Docker는 단순한 로컬 개발 도구라기보다 "배포 가능한 실행 단위"를 표준화하는 기반입니다.

그래서 많은 팀이 애플리케이션 코드를 검토할 때만큼이나 이미지 태그, 베이스 이미지, 실행 사용자, 포트, 헬스체크를 함께 검토합니다. Docker를 도입한다는 말은 개발 환경만 편해진다는 뜻이 아니라, 실행 방식 자체를 코드로 관리한다는 뜻에 가깝습니다.

시니어 엔지니어는 이렇게 생각합니다

  • 환경은 문서가 아니라 코드와 이미지로 관리해야 합니다.
  • 이미지는 불변 산출물이고, 컨테이너는 언제든 버릴 수 있어야 합니다.
  • latest는 데모에는 편하지만 운영 기본값이 되어서는 안 됩니다.
  • 컨테이너는 결국 하나의 프로세스라는 감각을 놓치면 안 됩니다.
  • 호스트 커널을 공유한다는 사실이 성능과 보안의 출발점입니다.

이 관점은 앞으로 시리즈 전반을 읽는 기준이 됩니다. Dockerfile은 이미지를 만드는 방법을 다루고, volume은 상태를 어떻게 분리할지 다루며, production 주제는 이 불변성과 격리 모델을 어떻게 안전하게 운영할지로 이어집니다.

체크리스트

  • docker run hello-world가 정상 동작합니다.
  • image와 container의 차이를 설명할 수 있습니다.
  • 포트 매핑의 의미를 이해했습니다.
  • 실행한 컨테이너를 정리할 수 있습니다.

연습 문제

  1. nginx를 실행하고 호스트 8080 포트로 접속해 보세요.
  2. python:3.12-slim으로 대화형 셸을 열어 보세요.
  3. 실행 중인 컨테이너의 로그와 상태를 각각 확인해 보세요.

정리 및 다음 단계

Docker는 환경 차이를 없애는 가장 빠른 출발점입니다. 이 글에서 가장 먼저 가져가야 할 핵심은 세 가지입니다. 첫째, Docker는 실행 환경을 이미지라는 산출물로 묶습니다. 둘째, 컨테이너는 그 이미지를 실제로 실행한 프로세스입니다. 셋째, 재현성이 생기면 디버깅과 배포가 모두 쉬워집니다.

다음 글에서는 image와 container를 더 깊게 분리해서 봅니다. 어디까지가 불변이고, 어디서 상태가 생기며, 왜 컨테이너를 언제든 버릴 수 있게 설계해야 하는지를 본격적으로 다룹니다.

처음 질문으로 돌아가기

  • Docker는 정확히 무엇을 해 주는 도구일까요?
    • 본문에서 강조했듯이 Docker는 애플리케이션과 그 실행 환경을 하나의 이미지로 묶어, "내 PC에서는 되는데" 문제를 줄이는 도구입니다. 빌드·배포·실행을 같은 단위(이미지·컨테이너)로 통일하기 때문에 환경 차이로 인한 디버깅 비용이 크게 줄어듭니다.
  • 컨테이너와 가상머신은 무엇이 다를까요?
    • 본문 비교 그림처럼 VM은 OS 커널까지 통째로 가상화해 무겁고 시작이 느리지만, 컨테이너는 호스트 커널을 공유하고 프로세스 격리만 추가하므로 수백 MB 대신 수십 MB, 초 단위 부팅 대신 밀리초 단위 시작이 가능합니다. 격리 강도 대신 밀도·속도를 택한 결과입니다.
  • image, container, registry는 어떤 관계로 이해해야 할까요?
    • 본문에서 본 것처럼 image는 빌드된 정적 스냅샷, container는 그 image를 실제로 띄운 실행 인스턴스, registry는 image를 저장·배포하는 저장소입니다. docker build → registry push → 다른 곳에서 pullrun의 흐름이 바로 이 세 객체의 협력입니다.

시리즈 목차

  • Docker란 무엇인가? (현재 글)
  • Image와 Container (예정)
  • Dockerfile 작성하기 (예정)
  • Volume과 Network (예정)
  • Docker Compose (예정)
  • 환경변수와 설정 (예정)
  • Python 앱 컨테이너화 (예정)
  • 데이터베이스와 함께 실행하기 (예정)
  • Image 최적화 (예정)
  • 배포용 Docker 구성 (예정)

참고 자료

공식 문서

검증과 트러블슈팅

반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함