Skip to content

Docker (컨테이너 · 이미지 · 배포) ​

컨테이너와 이미지가 무엇인지부터, 이미지를 직접 빌드해 Docker Hub에 배포하는 과정, 실행 중인 컨테이너에 접속하는 exec와 attach의 차이까지 Docker의 기본을 한 페이지에 모았습니다.

컨테이너와 이미지란? ​

컨테이너는 애플리케이션 실행에 필요한 것들만 격리된 상자 안에 담아서 돌리는 방식입니다. 같은 컴퓨터 안에서 여러 프로그램을 동시에 돌리다 보면 서로 필요한 라이브러리 버전이 다르거나 포트가 겹치는 등 충돌이 생기기 쉬운데, 컨테이너는 이런 충돌을 막아줍니다.

왜 격리가 필요할까 ​

비유하자면 원룸에서 여러 명이 냉장고 하나를 같이 쓰는 상황과 비슷합니다. 누가 뭘 넣었는지, 유통기한이 언제인지 서로 섞이면 곤란하겠죠. 컨테이너는 각자에게 자기만의 냉장고(파일시스템, 네트워크, 프로세스 공간)를 하나씩 나눠주는 셈입니다.

가상머신(VM)과 뭐가 다를까 ​

VM은 컴퓨터 한 대 안에 컴퓨터를 통째로 하나 더 만드는 방식이라 자신만의 OS 커널을 가집니다. 반면 컨테이너는 호스트의 커널을 그대로 빌려 쓰고, 프로세스 수준에서만 격리합니다. 그래서 훨씬 가볍고 빠릅니다.

왼쪽 VM은 앱마다 Guest OS를 따로 올리고, 오른쪽 컨테이너는 Docker Engine 위에서 OS 하나를 함께 쓰는 구조 비교

그림에서 VM 쪽은 앱마다 Guest OS가 통째로 하나씩 붙어 있지만, 컨테이너 쪽은 앱과 라이브러리(Bins/Libs)만 따로 갖고 아래의 OS는 다 같이 씁니다. 이 차이가 용량과 시작 속도 차이로 이어집니다.

구분가상머신(VM)컨테이너
격리 단위OS 전체프로세스
용량수 GB수십~수백 MB
시작 속도분 단위초 단위
커널자기 커널 따로 가짐호스트 커널 공유

이미지란? ​

이미지는 컨테이너를 실행하는 데 필요한 모든 걸 담아둔 파일입니다. 붕어빵 틀과 붕어빵 사이의 관계로 생각하면 됩니다. 이미지가 틀이고, 그 틀로 찍어낸 결과물이 컨테이너입니다.

ubuntu:15.04 이미지 하나 위에 컨테이너 4개가 각자 얇은 읽기/쓰기 레이어만 얹어서 함께 쓰는 모습그림 출처: Docker Docs - Storage drivers (Apache-2.0)

틀(이미지) 하나로 붕어빵(컨테이너)을 여러 개 찍어내도 틀은 그대로입니다. 컨테이너마다 자기 변경 사항만 담는 얇은 쓰기 공간(Thin R/W layer)을 따로 갖고, 아래 이미지는 모두가 읽기 전용으로 같이 씁니다.

Next.js 앱을 돌리려면 node 환경이, Django 앱을 돌리려면 Python 환경이 필요한데, 이런 기본 환경이 이미 다 준비된 이미지를 Docker Hub에서 그냥 내려받아 쓸 수 있습니다. 바닥부터 환경을 만들 필요가 없습니다.

이미지는 레이어로 쌓여있다 ​

이미지 하나는 사실 여러 개의 "레이어"가 겹겹이 쌓인 형태입니다. 그래서 이미지를 받을 때 여러 파일이 순서대로 다운로드되는 걸 볼 수 있고, 이미지를 여러 개 받다 보면 저장공간이 금방 부족해질 수 있습니다.

ubuntu:15.04 이미지의 읽기 전용 레이어 4개 위에 컨테이너용 얇은 읽기/쓰기 레이어가 올라간 구조그림 출처: Docker Docs - Storage drivers (Apache-2.0)

자물쇠가 걸린 아래 칸들이 이미지 레이어(읽기 전용)이고, 맨 위 점선 칸이 컨테이너가 실행되면서 생기는 쓰기 레이어입니다. 컨테이너 안에서 파일을 고치거나 새로 만들면 이 맨 위 칸에만 기록되고, 컨테이너를 지우면 이 칸도 같이 사라집니다.

App 1과 App 2가 각자의 소스·의존성 레이어를 갖지만 Python and pip, Debian base 레이어는 함께 쓰는 구조그림 출처: Docker Docs - Understanding the image layers (Apache-2.0)

레이어는 이미지끼리도 재사용됩니다. 두 앱이 같은 베이스(Debian + Python)를 쓴다면 그 부분은 한 번만 내려받고, 위에 쌓인 앱별 레이어만 따로 받습니다.

docker image ls              # 받아둔 이미지 목록 확인
docker image rm <이미지명>    # 안 쓰는 이미지 삭제

이미지 빌드와 Docker Hub 배포 ​

내가 만든 애플리케이션을 컨테이너로 돌리려면, 먼저 그 애플리케이션을 담은 이미지를 직접 만들어야 합니다. 이 과정을 **빌드(Build)**라고 부릅니다.

빌드에 필요한 두 가지 ​

빌드에는 Dockerfile과 빌드 컨텍스트가 필요합니다. Dockerfile은 "이미지를 어떻게 만들지" 적어둔 설계도이고, 빌드 컨텍스트는 그 설계도대로 재료(소스코드 등)를 가져올 시작 위치입니다.

빌드 명령어 ​

공식적으로는 기존의 docker image build 대신 **docker buildx build**를 표준으로 사용합니다.

docker buildx build -t username/이미지명:태그 .
옵션의미
-t이미지 이름과 태그 지정
--build-arg빌드 시점에 넘겨줄 변수
-fDockerfile 경로 지정 (기본값은 Dockerfile)
--pull베이스 이미지를 항상 최신으로 새로 받아옴

빌드 컨텍스트는 왜 보통 .일까 ​

빌드 컨텍스트는 대부분 소스코드 최상위 경로로 지정합니다. 그 위치에서 빌드 명령어를 실행해야 경로가 꼬이지 않고 헷갈릴 일이 없습니다.

빌드 로그 읽는 법 ​

빌드를 실행하면 로그에 1/4, 2/4처럼 진행 상황이 찍히는데, 이는 Dockerfile의 각 명령어가 레이어 하나씩 만들어지는 과정을 보여주는 겁니다.

만든 이미지 확인하고 배포하기 ​

빌드가 끝나면 docker image ls로 내 컴퓨터에 잘 만들어졌는지 확인합니다. 배포는 Docker Hub에 로그인한 뒤 진행하며, 태그를 여러 개 한 번에 올리고 싶다면 docker image push -a username/이미지명을 사용하면 됩니다. 업로드 후에는 Docker Hub 사이트에서 이미지와 태그가 정상적으로 올라갔는지 확인할 수 있습니다.

정리 흐름: Dockerfile 작성 → docker buildx build로 이미지 빌드 → 로컬에서 확인 → Docker Hub로 배포

Client의 docker run, build, pull 명령이 Docker Host의 daemon으로 전달되고, daemon이 이미지와 컨테이너를 관리하며 Registry(Docker Hub)와 이미지를 주고받는 전체 구조그림 출처: Docker Docs - What is Docker? (Apache-2.0)

터미널에서 치는 docker 명령(Client)은 직접 일을 하지 않고 Docker daemon에게 부탁만 합니다. build는 내 컴퓨터에 이미지를 만들고, run은 그 이미지로 컨테이너를 띄우고, pull/push는 Registry(Docker Hub)와 이미지를 주고받습니다.

docker exec와 docker attach 차이 ​

이미 실행 중인 컨테이너 안을 들여다보는 방법에는 exec와 attach 두 가지가 있는데, 둘은 비슷해 보여도 동작 방식이 완전히 다릅니다.

exec: 새 프로세스를 하나 더 띄운다 ​

docker exec는 이미 돌아가고 있는 컨테이너 안에 새로운 프로세스를 하나 더 실행하는 방식입니다. 사무실에 이미 사람들이 일하고 있는데, 내가 새 문으로 따로 들어가는 느낌입니다.

docker exec -it <컨테이너명> /bin/bash

이렇게 열어둔 터미널에서 exit을 쳐도 내가 만든 프로세스만 종료될 뿐, 컨테이너 자체와 원래 돌아가던 메인 프로세스는 아무 영향을 받지 않습니다.

attach: 원래 있던 프로세스에 직접 연결한다 ​

docker attach는 컨테이너가 시작될 때 실행된 **메인 프로세스(PID 1)**의 입출력에 지금 내 터미널을 직접 연결하는 방식입니다. 그래서 메인 프로세스가 실시간으로 찍는 로그를 그대로 볼 수 있습니다.

문제는 여기서 exit을 치면 메인 프로세스 자체가 종료돼버려서, 컨테이너까지 같이 꺼져버린다는 점입니다. 컨테이너를 끄지 않고 빠져나오려면 Ctrl+P 다음 Ctrl+Q를 순서대로 눌러야 합니다.

한눈에 비교 ​

execattach
연결 대상새로 띄운 프로세스기존 메인 프로세스(PID 1)
나갈 때 컨테이너영향 없음exit 치면 컨테이너도 종료
안전하게 빠져나오기그냥 exitCtrl+P → Ctrl+Q
주 용도셸 접속, 디버깅실시간 로그 확인

정리 ​

개념핵심
컨테이너실행에 필요한 것만 격리해서 돌림, 호스트 커널을 공유해 VM보다 가볍고 빠름
이미지컨테이너를 찍어내는 틀, 여러 레이어가 쌓인 형태 (docker image ls / rm으로 관리)
빌드Dockerfile + 빌드 컨텍스트(보통 .)로 docker buildx build -t ...
배포로그인 후 Docker Hub로 push, 여러 태그는 docker image push -a
exec새 프로세스로 접속, exit해도 컨테이너 영향 없음 (셸 접속, 디버깅)
attach메인 프로세스(PID 1)에 연결, 빠져나올 땐 Ctrl+P → Ctrl+Q (실시간 로그 확인)

참고: 개인 Notion 학습 노트