글 목록으로 돌아가기
최오키 개발블로그 8분 읽기

도커 기초부터 실무까지: 컨테이너 기술 완벽 가이드

도커(Docker)는 현대 개발 환경에서 거의 필수적인 기술이 되었어요.


도커(Docker)는 현대 개발 환경에서 거의 필수적인 기술이 되었어요. 개발 환경과 운영 환경의 차이로 인한 “내 컴퓨터에서는 잘 되는데?”라는 문제를 한 번에 해결할 수 있기 때문입니다. 하지만 도커를 처음 배울 때는 개념이 복잡하게 느껴질 수 있어요. 이 글에서는 도커의 기본 개념부터 실제 사용법, 그리고 실무에서 자주 마주치는 상황들까지 차근차근 설명해드리겠습니다.

특히 이 글은 이미 기본적인 개발 경험이 있는 중급 개발자를 위해 작성되었어요. 단순한 명령어 나열이 아니라, 왜 이 기술이 필요한지, 어떻게 작동하는지, 그리고 실제 프로젝트에서 어떻게 활용할 수 있는지를 중심으로 설명합니다.


📌 이 글에서 배울 내용

✅ 도커의 작동 원리와 핵심 개념

✅ 도커 이미지와 컨테이너의 차이

✅ 실제 프로젝트에 필요한 도커 명령어

✅ 도커 네트워크 구조와 파일 공유

✅ 데이터 보존을 위한 스토리지 전략

✅ 실무에서 자주 하는 실수와 해결 방법


1️⃣ 도커란 무엇인가? 기본 개념 이해하기

도커 구조: 클라이언트, 호스트, 레지스트리

도커를 이해하려면 먼저 도커의 세 가지 핵심 구성 요소를 알아야 해요. 마치 우리가 온라인 쇼핑을 할 때 클라이언트(우리), 배송 시스템(호스트), 창고(레지스트리)가 필요한 것처럼, 도커도 이 세 가지가 함께 작동합니다.

도커 클라이언트 는 우리가 도커에 명령을 내릴 수 있는 CLI(Command Line Interface) 도구예요. 터미널에서 docker run, docker build 같은 명령어를 입력할 때 바로 이 클라이언트를 사용하는 거죠. 클라이언트를 통해 컨테이너, 이미지, 볼륨 등 도커의 모든 리소스를 관리할 수 있습니다.

도커 호스트 는 도커를 설치한 서버 또는 가상머신입니다. 물리 서버가 될 수도 있고, 클라우드 인스턴스가 될 수도 있어요. 중요한 건 도커 호스트 위에서 실제로 컨테이너들이 실행된다는 점입니다. 호스트 내부에는 도커 데몬(daemon)이라는 백그라운드 프로세스가 있는데, 이게 클라이언트의 명령을 받아서 실제 작업을 수행합니다.

도커 레지스트리 는 도커 이미지를 저장하고 배포하는 시스템이에요. 가장 유명한 공개 레지스트리는 Docker Hub인데, 여기에는 수백만 개의 공개 이미지가 있습니다. 회사에서는 보안을 위해 개인 레지스트리를 운영하기도 합니다.

이 세 가지가 어떻게 함께 작동하는지 생각해보세요. 개발자가 클라이언트에서 “Ubuntu 이미지를 다운로드하고 실행해줘”라는 명령을 입력하면, 도커 호스트의 데몬이 그 명령을 받습니다. 호스트에 해당 이미지가 없으면 레지스트리(Docker Hub)에서 자동으로 다운로드하고, 그 이미지를 기반으로 컨테이너를 생성해서 실행하는 거죠.

도커 이미지 vs 도커 컨테이너: 설계도와 건물

도커를 배울 때 가장 헷갈리는 부분이 바로 이미지와 컨테이너의 차이예요. 쉽게 생각하면, 이미지는 설계도이고 컨테이너는 그 설계도로 만든 건물 이라고 보면 됩니다.

도커 이미지 는 애플리케이션을 실행하기 위해 필요한 모든 것을 포함한 패키지입니다. 코드, 라이브러리, 설정 파일, 환경 변수, 심지어 운영체제의 일부까지 모두 포함되어 있어요. 이미지는 불변(immutable)이에요. 한 번 만들어지면 변하지 않습니다. 또한 특정 시점의 애플리케이션 상태를 스냅샷처럼 담고 있기 때문에, 같은 이미지로 만든 컨테이너들은 모두 동일한 환경을 가집니다.

도커 컨테이너 는 이미지를 실행한 인스턴스입니다. 같은 이미지로 여러 개의 다른 컨테이너를 만들 수 있어요. 각 컨테이너는 독립적인 파일 시스템을 가지고 있고, 격리된 환경에서 실행됩니다. 컨테이너는 가볍습니다. 왜냐하면 호스트의 커널을 공유하기 때문이에요. 가상머신처럼 전체 운영체제를 포함하지 않아서 빠르고 효율적입니다.

이 차이를 이해하는 게 중요한 이유는 실무에서의 워크플로우 때문입니다. 개발할 때는 이미지를 만들고(빌드), 배포할 때는 그 이미지를 여러 서버에서 실행(컨테이너 생성)하는 방식으로 작동하거든요.


2️⃣ 도커 실전: 필수 명령어와 실행 흐름

이미지 다운로드부터 시작하기

도커를 처음 사용할 때는 이미지를 다운로드하는 것부터 시작합니다. 가장 기본적인 명령어는 다음과 같습니다:

이 명령어를 실행하면 도커 데몬은 먼저 호스트에 해당 이미지가 있는지 확인합니다. 없으면 Docker Hub에서 자동으로 다운로드해요. 태그(:22.04)를 지정하지 않으면 기본적으로 latest 태그가 적용됩니다. 하지만 실무에서는 항상 구체적인 버전을 지정하는 게 좋습니다. 나중에 latest 이미지가 업데이트되면서 예상치 못한 문제가 생길 수 있거든요.

이미지 다운로드가 완료되면 터미널에 여러 줄의 해시값이 출력됩니다. 이것들은 각각 다른 레이어를 나타내요. 도커 이미지는 여러 개의 레이어로 구성되어 있는데, 각 레이어는 이전 레이어 위에 추가되는 방식으로 쌓입니다. 이 레이어 구조 덕분에 이미지 재사용이 효율적이고, 업데이트할 때도 변경된 부분만 다시 다운로드할 수 있어요.

다운로드한 이미지 확인하기

다운로드한 이미지들을 확인하려면:

이 명령어를 실행하면 다음과 같은 정보가 표시됩니다:

REPOSITORY 는 이미지의 이름, TAG 는 버전, IMAGE ID 는 이미지의 고유 식별자, SIZE 는 이미지 크기를 나타냅니다. 실무에서는 이미지 크기도 중요해요. 크기가 크면 배포 시간이 오래 걸리고, 저장소 용량도 많이 차지하거든요.

컨테이너 실행하기

이제 이미지를 실행해서 컨테이너를 만들어봅시다:

이 명령어를 실행하면… 아무것도 출력되지 않습니다. 왜일까요? 이미지에 기본 명령어가 없기 때문이에요. 컨테이너는 실행할 명령어가 없으면 즉시 종료됩니다. 실제로 컨테이너가 실행되었는지 확인하려면:

-a 옵션을 붙이면 실행 중인 컨테이너뿐만 아니라 종료된 컨테이너도 모두 보여줍니다. 이렇게 보면 방금 실행한 컨테이너가 이미 종료된 상태로 있는 걸 확인할 수 있어요.

컨테이너 내부에 접속하기

컨테이너 내부에서 명령어를 실행하고 싶다면 -it 옵션을 사용해야 합니다:

-i는 표준 입력을 열어두는 옵션이고, -t는 터미널을 할당하는 옵션입니다. /bin/bash는 실행할 명령어로, 배시 셸을 실행하라는 뜻이에요. 이 명령어를 실행하면 컨테이너 내부의 셸에 접속하게 됩니다. 프롬프트가 root@<컨테이너ID>:/# 형태로 바뀌는 걸 볼 수 있어요.

이 상태에서 apt update && apt install net-tools 같은 명령어를 실행해서 패키지를 설치할 수 있습니다. 하지만 여기서 중요한 점이 있어요. 컨테이너 내부에서 한 모든 변경사항은 컨테이너가 종료되면 사라집니다. 만약 이 변경사항을 유지하고 싶다면, 컨테이너를 이미지로 저장해야 합니다.

컨테이너를 이미지로 저장하기

컨테이너에서 한 변경사항을 새로운 이미지로 만들려면 docker container commit 명령어를 사용합니다:

이 명령어를 실행하면 현재 컨테이너의 상태를 새로운 이미지로 저장합니다. 이제 docker image ls를 실행하면 새로운 이미지가 생겨 있을 거예요. 하지만 실무에서는 이 방법보다 Dockerfile을 사용해서 이미지를 빌드하는 게 훨씬 낫습니다. 왜냐하면 Dockerfile은 버전 관리가 가능하고, 재현 가능하고, 자동화할 수 있거든요.


3️⃣ 도커 네트워크: 컨테이너 간 통신의 비결

도커 네트워크 구조 이해하기

도커 컨테이너가 어떻게 서로 통신하는지 이해하려면 네트워크 구조를 알아야 합니다. 컨테이너를 실행하면 내부에 eth0이라는 네트워크 인터페이스가 생기고, 호스트에는 docker0이라는 가상 브릿지가 있습니다. 또한 각 컨테이너마다 veth(virtual ethernet)라는 가상 인터페이스가 생성되어 컨테이너의 eth0과 호스트의 docker0을 연결합니다.

호스트에서 ifconfig를 실행하면 여러 네트워크 인터페이스를 볼 수 있어요:

  • docker0 : 도커 설치 시 자동으로 생성되는 브릿지. 호스트와 컨테이너를 연결하는 다리 역할
  • veth… : 각 컨테이너마다 생성되는 가상 인터페이스
  • enp0s3 (또는 eth0): 호스트 자체의 네트워크 인터페이스

기본적으로 컨테이너는 172.17.0.0/16 범위의 IP 주소를 할당받습니다. 첫 번째 컨테이너는 172.17.0.2, 두 번째는 172.17.0.3 이런 식으로요. 172.17.0.1은 호스트의 docker0 인터페이스 IP입니다.

도커 네트워크 드라이버

도커는 기본적으로 세 가지 네트워크 드라이버를 제공합니다:

1. Bridge 드라이버 (기본값)

컨테이너 생성 시 기본으로 사용되는 드라이버입니다. 호스트의 docker0 브릿지와 연결되어 있어요. 이 드라이버를 사용하는 컨테이너들은 서로 통신할 수 있고, 호스트를 통해 외부와도 통신할 수 있습니다. 포트 매핑을 통해 외부에서 컨테이너로 접근할 수 있죠.

2. Host 드라이버

이 드라이버를 사용하면 컨테이너가 호스트의 네트워크 인터페이스를 직접 공유합니다. 컨테이너가 호스트와 같은 IP 주소를 가지게 되어요. 성능이 좋지만, 포트 충돌 위험이 있어서 신중하게 사용해야 합니다.

3. None 드라이버

이 드라이버를 사용하는 컨테이너는 네트워크 인터페이스를 가지지 않습니다. 외부와의 통신이 불가능해요. 보안이 중요한 작업이나 테스트 목적으로 사용됩니다.

호스트와 컨테이너 간 파일 전송

실무에서 자주 필요한 작업이 호스트와 컨테이너 간 파일 전송입니다. docker container cp 명령어를 사용하면 됩니다:

이 명령어는 호스트의 test.txt 파일을 컨테이너의 /home/ 디렉토리로 복사합니다. 반대 방향도 가능해요:

이건 컨테이너의 파일을 호스트로 복사하는 명령어입니다. 이 기능은 컨테이너에서 생성된 로그나 데이터를 호스트로 가져올 때 유용해요.


4️⃣ 데이터 보존 전략: Volume, Bind Mount, Tmpfs

왜 스토리지가 필요한가?

도커 컨테이너는 일시적입니다. 컨테이너를 삭제하면 그 안의 모든 데이터도 함께 사라져요. 데이터베이스를 컨테이너에서 실행하고 있다면, 컨테이너가 종료되면 모든 데이터가 손실됩니다. 이 문제를 해결하기 위해 도커는 세 가지 스토리지 옵션을 제공합니다.

1. Volume: 도커가 관리하는 저장소

Volume 은 도커가 관리하는 저장소입니다. 호스트의 특정 디렉토리에 데이터가 저장되지만, 도커가 그 위치를 관리하기 때문에 개발자는 신경 쓸 필요가 없어요.

첫 번째 명령어로 myvolume이라는 볼륨을 생성합니다. 두 번째 명령어로 컨테이너를 실행할 때 -v 옵션으로 이 볼륨을 컨테이너의 /data 디렉토리에 마운트합니다. 이제 컨테이너 내부의 /data 디렉토리에 저장된 모든 파일은 호스트의 볼륨에 저장되고, 컨테이너를 삭제해도 데이터는 유지됩니다.

Volume의 장점은 도커가 자동으로 관리한다는 점입니다. 호스트의 정확한 경로를 알 필요가 없어요. 또한 여러 컨테이너가 같은 볼륨을 공유할 수 있어서 데이터 공유가 쉽습니다. 데이터베이스 같은 중요한 데이터를 저장할 때 주로 사용됩니다.

2. Bind Mount: 호스트 디렉토리 직접 공유

Bind Mount 는 호스트의 특정 디렉토리를 컨테이너에 직접 마운트합니다. 개발자가 호스트의 정확한 경로를 지정하기 때문에 더 유연해요.

이 명령어는 호스트의 /home/user/mydata 디렉토리를 컨테이너의 /data 디렉토리로 마운트합니다. 호스트와 컨테이너가 같은 파일을 공유하기 때문에, 호스트에서 파일을 수정하면 컨테이너에서도 즉시 반영되고, 그 반대도 마찬가지입니다.

Bind Mount는 개발할 때 매우 유용합니다. 호스트의 IDE에서 코드를 수정하면 컨테이너에서 즉시 반영되거든요. 하지만 호스트 경로에 직접 의존하기 때문에 이식성이 떨어질 수 있습니다. 또한 호스트와 컨테이너의 파일 권한 문제가 발생할 수 있어요.

3. Tmpfs: 메모리 기반 임시 저장소

Tmpfs 는 호스트의 메모리에 파일을 저장합니다. 매우 빠르지만, 컨테이너가 종료되면 모든 데이터가 사라집니다.

Tmpfs는 임시 데이터, 캐시, 세션 정보 같은 중요하지 않은 데이터를 저장할 때 사용합니다. 컨테이너 간 데이터 공유도 지원하지 않습니다.

세 가지 스토리지 옵션 비교:

Volume은 도커 관리, 컨테이너 간 공유 가능, 영구 저장에 최적

Bind Mount는 호스트 경로 지정, 개발 시 유용, 이식성 낮음

Tmpfs는 메모리 기반, 매우 빠름, 임시 데이터만 저장


5️⃣ 실무에서 자주 하는 실수와 해결 방법

실수 1: 이미지 태그를 지정하지 않기

많은 개발자들이 docker image pull ubuntu처럼 태그를 지정하지 않습니다. 이러면 자동으로 latest 태그가 적용되는데, 나중에 latest 이미지가 업데이트되면 예상과 다른 버전이 다운로드될 수 있어요. 항상 구체적인 버전을 지정하세요: docker image pull ubuntu:22.04

실수 2: 컨테이너 내부에서 직접 수정하기

컨테이너 내부에서 패키지를 설치하거나 설정을 변경한 후 그대로 사용하는 경우가 있습니다. 하지만 이 방식은 재현 불가능하고 관리하기 어려워요. 항상 Dockerfile을 사용해서 이미지를 빌드하세요. 그러면 누구나 같은 명령어로 같은 이미지를 만들 수 있습니다.

실수 3: 중요한 데이터를 컨테이너에만 저장하기

데이터베이스나 사용자 업로드 파일 같은 중요한 데이터를 컨테이너 내부에만 저장하면, 컨테이너를 삭제할 때 모든 데이터가 사라집니다. 항상 Volume이나 Bind Mount를 사용해서 호스트에 데이터를 저장하세요.

실수 4: 운영체제 호환성 무시하기

Windows에서 잘 작동하는 코드가 Linux 컨테이너에서는 작동하지 않을 수 있습니다. 특히 파일 경로의 대소문자 구분이 그렇습니다. Windows는 대소문자를 구분하지 않지만, Linux는 구분합니다. 코드를 작성할 때 이런 점을 항상 고려하세요.

실수 5: 컨테이너 리소스 제한 설정 안 하기

컨테이너에 리소스 제한을 설정하지 않으면, 한 컨테이너가 모든 호스트 리소스를 사용해서 다른 컨테이너들에 영향을 줄 수 있습니다. 프로덕션 환경에서는 항상 메모리와 CPU 제한을 설정하세요:

이 명령어는 메모리를 512MB, CPU를 1개로 제한합니다.


6️⃣ 실전 팁: 효율적인 도커 사용법

⚙️ 이미지 크기 최적화하기

이미지 크기가 크면 배포 시간이 오래 걸리고, 저장소 용량도 많이 차지합니다. 불필요한 파일을 제외하려면 .dockerignore 파일을 사용하세요. 이건 .gitignore처럼 작동합니다:

이 파일을 프로젝트 루트에 두면, 이미지 빌드 시 이 파일들이 제외됩니다. 특히 node_modules는 매우 크기 때문에 반드시 제외해야 해요.

🔄 컨테이너 생명주기 관리하기

컨테이너를 종료하는 방법은 두 가지입니다. docker container stop은 우아하게(gracefully) 종료하고, docker container kill은 강제로 종료합니다. 프로덕션 환경에서는 stop을 사용해서 애플리케이션이 정리 작업을 할 수 있도록 해주세요. 기본적으로 10초의 대기 시간이 있습니다.

종료된 컨테이너를 다시 시작하려면 docker container start를 사용합니다. 이건 새로운 컨테이너를 만드는 게 아니라 기존 컨테이너를 다시 실행하는 거예요. 따라서 이전의 파일 시스템 상태가 유지됩니다.

🔍 로그 확인하기

컨테이너에 문제가 있을 때 로그를 확인하는 게 첫 번째 단계입니다:

-f 옵션을 사용하면 실시간으로 로그를 볼 수 있습니다. 이건 tail -f처럼 작동해요.


마치며: 도커 마스터가 되기 위한 다음 단계

이 글에서는 도커의 기본 개념부터 실무에서 필요한 기술들까지 다루었습니다. 하지만 도커는 매우 광범위한 기술이에요. 여기서 배운 내용을 바탕으로 다음 단계로 나아가시기를 권합니다:

1. Dockerfile 마스터하기

지금까지는 기존 이미지를 사용했지만, 실무에서는 자신의 애플리케이션을 위한 이미지를 직접 만들어야 합니다. Dockerfile 문법을 깊이 있게 학습하세요.

2. Docker Compose 배우기

여러 개의 컨테이너를 조합해서 실행해야 할 때 Docker Compose를 사용합니다. 웹 서버, 데이터베이스, 캐시 같은 여러 서비스를 한 번에 관리할 수 있어요.

3. 컨테이너 오케스트레이션 시스템 학습하기

Kubernetes는 수백 개의 컨테이너를 자동으로 관리하는 시스템입니다. 대규모 프로덕션 환경에서는 필수적인 기술입니다.

도커는 현대적인 소프트웨어 개발의 핵심 기술입니다. 이 글의 내용을 충분히 이해하고 실습하면, 도커를 자신감 있게 사용할 수 있을 거예요. 계속 학습하고 실습하면서 도커 마스터가 되어보세요!


#Docker #컨테이너 #DevOps #클라우드 #개발환경 #배포 #이미지 #네트워크 #스토리지

원문 보기

Related posts

Other Git push 시 발생하는 non-fast-forward 오류 해결 방법 Other HTTP 504 Gateway Timeout 오류의 원인과 해결 방법 ElasticSearch Elasticsearch Unassigned Shards 문제 해결 가이드