Published on

Jenkins Docker agent 권한 denied 10분 해결법

Authors

서버에서 Jenkins 파이프라인을 Docker agent로 돌리려는 순간, 로그에 permission denied가 뜨면 대부분 원인은 3가지로 수렴합니다.

  1. Jenkins 컨테이너(또는 에이전트 컨테이너)가 Docker 데몬에 접근할 권한이 없다
  2. docker.sock의 소유자/그룹과 컨테이너 내부 사용자 UID/GID가 맞지 않는다
  3. Docker-in-Docker(DinD) 구성에서 TLS/권한/privileged 설정이 어긋났다

이 글은 “10분 내 해결”을 목표로, 가장 빠르게 원인을 특정하고 재발을 막는 방식으로 정리합니다.

증상 패턴 먼저 확인하기

아래 메시지 중 어떤 형태인지 먼저 분류하면 해결 경로가 빨라집니다.

패턴 A: docker CLI는 있는데 소켓 접근이 막힘

  • docker ps 실행 시
  • Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

즉, 컨테이너 내부에서 Docker CLI는 실행되지만 /var/run/docker.sock 접근 권한이 없습니다.

패턴 B: 파일 시스템/워크스페이스 권한 문제로 오인

  • permission deniedgit checkout, npm ci, gradle 캐시 디렉터리에서 발생

이 경우는 Docker 데몬 권한이 아니라, Jenkins workspace 마운트와 사용자 권한 불일치일 수 있습니다. 하지만 Docker agent에서 같이 터지는 경우가 많아 함께 점검합니다.

패턴 C: DinD에서 데몬 자체가 안 뜸

  • Cannot connect to the Docker daemon at tcp://docker:2375
  • 혹은 TLS 관련 오류

이 경우는 소켓 마운트 방식이 아니라 DinD 방식 점검으로 바로 가는 게 빠릅니다.

10분 해결 체크리스트 (가장 흔한 순서)

1) Jenkins가 “어떤 방식”으로 Docker를 쓰는지 확정

Jenkins Docker agent 구성은 크게 2가지입니다.

  • 소켓 마운트 방식: 호스트의 /var/run/docker.sock을 컨테이너에 마운트해 호스트 Docker를 조종
  • DinD 방식: 별도 docker:dind 컨테이너를 띄워 그 안의 Docker 데몬을 사용

먼저 현재 구성을 확인하세요.

  • Jenkins 에이전트 컨테이너에 -v /var/run/docker.sock:/var/run/docker.sock가 있는지
  • 혹은 docker:dind 서비스를 같이 띄우고 DOCKER_HOST를 쓰는지

소켓 마운트 방식이면 아래 2~4번이 정답인 경우가 대부분입니다.

2) 호스트의 docker.sock 권한과 그룹 ID 확인

호스트에서 다음을 확인합니다.

ls -l /var/run/docker.sock
getent group docker
stat -c '%g %G %a' /var/run/docker.sock

대개 출력은 이런 형태입니다.

  • 소유자: root
  • 그룹: docker
  • 권한: 660

핵심은 그룹이 docker이고 권한이 rw여야 하며, 컨테이너 내부 Jenkins 사용자도 그 그룹 권한을 가져야 한다는 점입니다.

3) 컨테이너 내부 사용자 UID/GID와 docker 그룹 매핑 맞추기

가장 빠른 해결은 “컨테이너 내부에서 docker 그룹 GID를 호스트 docker 그룹 GID로 맞추고, jenkins 사용자를 그 그룹에 넣는 것”입니다.

방법 A: 에이전트 이미지에서 그룹을 맞춰 빌드 (권장)

호스트의 docker 그룹 GID를 먼저 확인합니다.

getent group docker | cut -d: -f3

예를 들어 GID가 998이라면, 에이전트 이미지 Dockerfile을 이렇게 구성합니다.

FROM jenkins/inbound-agent:latest

USER root

ARG DOCKER_GID=998

RUN groupadd -for -g ${DOCKER_GID} docker \
  && usermod -aG docker jenkins

# docker CLI가 필요하면 설치 (배포판에 맞게 조정)
RUN apt-get update \
  && apt-get install -y docker.io \
  && rm -rf /var/lib/apt/lists/*

USER jenkins

파이프라인에서 해당 에이전트 이미지를 사용하고, 소켓을 마운트합니다.

pipeline {
  agent {
    docker {
      image 'my-jenkins-agent:latest'
      args '-v /var/run/docker.sock:/var/run/docker.sock'
    }
  }
  stages {
    stage('docker') {
      steps {
        sh 'docker ps'
      }
    }
  }
}

이 조합이 가장 재현 가능하고, 운영에서 덜 흔들립니다.

방법 B: 실행 시점에 --group-add로 우회 (빠른 임시처방)

컨테이너 실행 옵션에 호스트 docker GID를 추가합니다.

DOCKER_GID=$(getent group docker | cut -d: -f3)

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --group-add ${DOCKER_GID} \
  my-jenkins-agent:latest \
  docker ps

Jenkins Docker plugin에서도 args로 주입할 수 있습니다.

args "-v /var/run/docker.sock:/var/run/docker.sock --group-add ${DOCKER_GID}"

다만 Jenkins에서 동적으로 GID를 주입하기가 애매할 수 있어, 장기적으로는 방법 A가 낫습니다.

4) “컨테이너 안에서 root로 실행”은 최후의 수단

급하면 USER root로 돌리면 해결되는 경우가 많지만, 보안상 리스크가 큽니다. 특히 소켓 마운트 방식은 사실상 호스트 root 권한에 준하므로, root 실행과 결합하면 위험이 더 커집니다.

그래도 장애 복구가 급하면 아래처럼 단계적으로 접근하세요.

  • 우선 root로 성공 여부 확인
  • 성공하면 즉시 UID/GID 매핑 방식으로 되돌리기

workspace 권한 문제도 함께 잡기 (패턴 B)

Docker agent에서 흔히 겪는 2차 문제는 Jenkins workspace가 호스트 볼륨으로 마운트되면서 UID가 어긋나는 케이스입니다.

증상 예:

  • fatal: could not create work tree dir ... Permission denied
  • EACCES: permission denied, mkdir ...

해결 원칙은 “마운트된 디렉터리 소유자 UID/GID를 컨테이너 사용자와 맞추는 것”입니다.

가장 단순한 진단 명령

컨테이너 내부에서:

id
ls -ld .
ls -ld $WORKSPACE

호스트에서:

ls -ld /path/to/jenkins/workspace

UID/GID가 다르면, 다음 중 하나로 정리합니다.

  • 호스트 디렉터리 소유권을 Jenkins가 쓰는 UID로 변경
  • 컨테이너 사용자를 호스트 디렉터리 UID로 맞춘 이미지 사용
  • Kubernetes라면 securityContext.fsGroup 또는 runAsUser로 정렬

Kubernetes에서 Pod가 계속 재시작되는 상황이라면, 권한 문제로 프로세스가 비정상 종료되며 재시작되는 경우도 많습니다. 이런 경우는 systemd 서비스가 계속 재시작될 때 원인 9가지처럼 “왜 재시작되는가”를 로그/권한/의존성 관점에서 분해하는 방식이 도움이 됩니다.

DinD 방식이라면 이렇게 점검 (패턴 C)

DinD는 소켓 마운트보다 격리가 낫지만, 설정이 조금 더 필요합니다.

docker:dind 서비스 예시 (Docker Compose)

services:
  jenkins-agent:
    image: my-jenkins-agent:latest
    environment:
      DOCKER_HOST: tcp://dind:2375
    depends_on:
      - dind

  dind:
    image: docker:24-dind
    privileged: true
    environment:
      DOCKER_TLS_CERTDIR: ""
    ports:
      - "2375:2375"

포인트:

  • privileged: true 없으면 데몬이 제대로 못 뜨는 경우가 많습니다.
  • TLS를 끄고 2375를 쓰는 구성은 내부 네트워크에서만 제한적으로 쓰세요.
  • TLS를 쓸 거면 인증서 마운트와 환경변수 구성이 추가로 필요합니다.

DinD에서 permission denied가 나는 경우

DinD에서도 결국 “데몬 접근 권한” 문제로 귀결됩니다.

  • DOCKER_HOST가 올바른지
  • dind 컨테이너가 살아있는지
  • 방화벽/네트워크 정책으로 포트가 막히지 않는지

Kubernetes에서 DinD를 쓰는 경우, Pod가 Pending 또는 네트워크 이슈로 지연되면 진단 흐름이 완전히 달라집니다. 클러스터 레벨에서 IP 고갈/할당 이슈가 의심되면 EKS Pod Pending - CNI IP 고갈 원인과 해결 가이드도 함께 확인하는 게 좋습니다.

재발 방지: 운영에서 안전한 권한 모델

정리하면, Jenkins Docker agent의 permission denied는 “컨테이너 내부 사용자”와 “호스트 docker.sock 그룹”의 불일치가 대부분입니다. 재발 방지는 아래 원칙을 추천합니다.

  1. 가능하면 전용 에이전트 이미지를 만들고, 호스트 docker 그룹 GID를 명시적으로 매핑
  2. 파이프라인에서 임시로 root 실행하는 패턴을 금지
  3. 소켓 마운트 방식은 편하지만 강력한 권한을 부여하므로, 민감 환경에서는 DinD 또는 원격 빌더(예: BuildKit)로 분리

10분 안에 끝내는 최소 실행 플로우

아래 순서대로 하면 보통 빠르게 끝납니다.

  1. 호스트에서 ls -l /var/run/docker.sock로 그룹과 권한 확인
  2. getent group docker로 docker 그룹 GID 확인
  3. 에이전트 이미지에서 docker 그룹을 해당 GID로 생성하고 jenkins를 그룹에 추가
  4. 에이전트 실행 시 /var/run/docker.sock 마운트
  5. 컨테이너 내부에서 iddocker ps로 검증

여기까지 했는데도 안 되면, 그때는 “Docker 소켓이 다른 경로로 생성됨”, “SELinux/AppArmor 정책”, “Kubernetes securityContext” 같은 2차 원인을 의심하는 게 효율적입니다.


부록: 빠른 검증용 스크립트

호스트에서 아래를 실행해 현재 docker 그룹 GID를 출력하고, 컨테이너에서 접근 테스트까지 한 번에 확인할 수 있습니다.

DOCKER_GID=$(getent group docker | cut -d: -f3)
echo "docker group gid: ${DOCKER_GID}"

docker run --rm \
  -v /var/run/docker.sock:/var/run/docker.sock \
  --group-add ${DOCKER_GID} \
  docker:24 \
  sh -lc 'id && docker ps'

이 테스트가 성공하면, Jenkins 에이전트 쪽도 거의 동일한 원리로 해결됩니다.