기록

도커 명령어의 동작 원리 : docker ↔ docker.sock ↔ dockerd 구조 이해하기 본문

DevOps

도커 명령어의 동작 원리 : docker ↔ docker.sock ↔ dockerd 구조 이해하기

zyin 2025. 6. 23. 00:00

도커는 클라이언트-서버 구조로 동작한다

겉으로 보기에는 docker라는 명령어 하나로 모든 게 처리되는 것처럼 보이지만, 사실 도커는 내부적으로 두 가지 컴포넌트로 나뉘어 작동하는 클라이언트-서버 아키텍처를 따른다.
이 구조를 구성하는 핵심 요소는 다음과 같다:

구성 요소 설명
docker 도커 CLI. 사용자의 명령을 입력받아 서버에 전달하는 클라이언트 도구
dockerd 도커 데몬. 실제 컨테이너 실행, 이미지 다운로드, 네트워크 연결 등을 담당하는 서버 프로세스
/var/run/docker.sock 클라이언트와 데몬 사이를 연결하는 유닉스 도메인 소켓. 요청과 응답이 오가는 통신 경로

 

이 구조에서 docker는 사용자와 상호작용하는 클라이언트에 해당하며, dockerd는 실제로 모든 무거운 일을 처리하는 서버라고 할 수 있다. 두 요소는 별도로 실행되며, 서로 직접 통신하지 않고 운영체제 내부의 소켓 파일을 통해 메시지를 주고받는다.


docker.sock은 무엇이며 왜 중요한가?

/var/run/docker.sock은 도커 구조의 핵심이자, 모든 통신이 오가는 실질적인 경로다. 이 파일은 리눅스에서 제공하는 **유닉스 도메인 소켓(Unix Domain Socket)**이라는 특수한 파일로, 서로 다른 프로세스가 네트워크를 거치지 않고도 데이터를 주고받을 수 있게 해주는 통신 채널 역할을 한다.

  • 로컬 소켓 기반 HTTP API 구조

웹 브라우저의 경우, API 서버에 요청을 보낼 때는 보통 http://api.example.com 같은 네트워크 주소를 사용한다.

하지만 도커의 경우, 클라이언트와 서버가 같은 시스템 안에 존재하기 때문에 굳이 외부 네트워크를 거칠 필요가 없다. 대신, 로컬 시스템 안에서 docker.sock이라는 파일을 매개로 하여 HTTP 요청을 주고받는다. 이 방식은 속도 면에서 훨씬 빠르고, 보안 측면에서도 네트워크 설정 없이 안전하게 동작할 수 있다는 장점이 있다.


docker 명령어 실행 시 내부 흐름

이제 이 구조가 실제로 어떻게 작동하는지를 단계적으로 살펴보자.
예를 들어, 다음과 같이 docker ps 명령어를 실행한다고 가정해보자:

docker ps

겉으로는 단순히 실행 중인 컨테이너 목록을 보여주는 명령이다.
하지만 내부적으로는 다음과 같은 순서를 따라 실행된다:

  1. docker CLI는 GET /containers/json이라는 HTTP API 요청을 생성한다.
  2. 이 요청은 TCP/IP가 아닌, /var/run/docker.sock이라는 유닉스 도메인 소켓 파일을 통해 dockerd로 전달된다.
  3. dockerd는 이 요청을 받아 실행 중인 컨테이너 목록을 조회하고, JSON 형식의 응답을 생성한다.
  4. 이 응답은 다시 docker CLI로 전달되며, 사람이 읽기 쉬운 형태로 터미널에 출력된다.

이 흐름은 웹 API 서버와 클라이언트가 JSON 데이터를 주고받는 구조와 매우 유사하다.
단지, 그 통신 경로가 인터넷이 아닌 파일 기반 소켓이라는 점이 다를 뿐이다.

다음은 이 과정을 간략히 도식화한 것이다:

docker CLI
   ↓
HTTP API 요청 (ex. GET /containers/json)
   ↓
/var/run/docker.sock (로컬 유닉스 소켓)
   ↓
dockerd (HTTP 서버 역할 수행)
   ↓
응답 (JSON 형식)
   ↓
CLI에 출력

직접 확인해보기: curl로 docker.sock 호출하기

이 구조는 실습을 통해 직접 확인할 수도 있다.
도커 CLI를 사용하지 않고도, docker.sock에 직접 HTTP 요청을 보내면 dockerd가 응답을 처리한다.

sudo curl --unix-socket /var/run/docker.sock http://localhost/containers/json

 

이 명령은 docker ps와 같은 요청을 수동으로 보내는 것이다. 실행하면 JSON 포맷의 컨테이너 목록이 출력되며, 도커가 실제로 HTTP API 서버처럼 동작하고 있다는 점을 확인할 수 있다.


docker.sock이 없을 때 나타나는 문제

도커를 설치한 후 명령어를 실행했을 때 다음과 같은 오류를 본 적이 있을 것이다:

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

이 오류는 도커 CLI가 소켓 파일에 연결하지 못할 때 발생하며, 대표적인 원인은 다음과 같다:

  • dockerd 데몬이 실행되지 않아 소켓 파일 자체가 존재하지 않는 경우
  • 현재 사용자가 /var/run/docker.sock에 접근할 권한이 없는 경우 (예: docker 그룹 미포함)
  • 클라이언트가 엉뚱한 컨텍스트를 참조하고 있는 경우 (docker context 오용)

소켓 파일은 보통 dockerd가 실행될 때 자동으로 생성된다. 정상적으로 생성되었는지 확인하려면 아래 명령을 입력하면 된다:

ls -l /var/run/docker.sock

 

만약 파일이 없거나, 접근 권한이 부족하면 도커 명령어는 동작하지 않는다. 이때는 sudo dockerd 명령으로 데몬을 수동으로 실행하거나, 사용자에게 적절한 권한을 부여해야 한다.


왜 도커는 이렇게 동작하도록 설계되었을까?

1. 확장성

dockerd는 여러 클라이언트 요청을 동시에 처리할 수 있다. 이를 통해 GUI 도구, 자동화 스크립트, CI/CD 파이프라인 같은 다양한 외부 시스템이 도커 API를 활용할 수 있다.

2. 보안

도커 CLI는 일반 사용자 권한으로 실행되지만, 실제로 컨테이너를 제어하는 dockerd는 root 권한으로 동작한다. 두 프로세스를 분리하고, docker.sock에 대한 접근 권한만 조절하면 보안성이 크게 향상된다.

3. 유연성

필요하다면 도커는 유닉스 소켓 대신 TCP 포트를 열어 원격 제어도 가능하게 만들 수 있다. 예를 들어, 다음과 같이 특정 IP의 도커 데몬에 접근할 수도 있다:

docker -H tcp://192.168.1.100:2375 ps
Comments