Docker 이미지 빌드에 Docker 사용

Tier: Free, Premium, Ultimate Offering: GitLab.com, Self-managed, GitLab Dedicated

GitLab CI/CD를 사용하여 Docker로 Docker 이미지를 만들 수 있습니다. 예를 들어, 애플리케이션의 Docker 이미지를 만들고 테스트한 다음 컨테이너 레지스트리에 푸시할 수 있습니다.

CI/CD 작업에서 Docker 명령을 실행하려면 GitLab Runner를 docker 명령을 지원하도록 구성해야 합니다. 이 방법에는 privileged 모드가 필요합니다.

러너에서 privileged 모드를 활성화하지 않고 Docker 이미지를 빌드하려면 Docker 대안을 사용할 수 있습니다.

CI/CD 작업에서 Docker 명령 활성화

CI/CD 작업에 Docker 명령을 활성화하려면 다음을 사용할 수 있습니다:

Shell Executor 사용

CI/CD 작업에서 Docker 명령을 포함하려면 러너를 shell executor로 구성할 수 있습니다. 이 구성에서 gitlab-runner 사용자가 Docker 명령을 실행하지만 이를 위한 권한이 필요합니다.

  1. GitLab Runner를 설치합니다.
  2. 러너를 등록합니다. shell executor를 선택합니다. 예:

    sudo gitlab-runner register -n \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor shell \
      --description "My Runner"
    
  3. GitLab Runner가 설치된 서버에서 Docker Engine을 설치합니다. 지원되는 플랫폼 디렉터리을 확인합니다.

  4. gitlab-runner 사용자를 docker 그룹에 추가합니다:

    sudo usermod -aG docker gitlab-runner
    
  5. gitlab-runner가 Docker에 액세스할 수 있는지 확인합니다:

    sudo -u gitlab-runner -H docker info
    
  6. GitLab에서 .gitlab-ci.ymldocker info를 추가하여 Docker가 작동하는지 확인합니다:

    default:
      before_script:
        - docker info
       
    build_image:
     script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

이제 Docker 명령을 사용하고 Docker Compose를 필요에 따라 설치할 수 있습니다.

gitlab-runnerdocker 그룹에 추가하면 사실상 gitlab-runner에게 전체 루트 권한을 부여하게 됩니다. 자세한 내용은 docker 그룹의 보안을 참조하십시오.

Docker-in-Docker 사용

“Docker-in-Docker”(dind)란 다음을 의미합니다:

Docker 이미지에는 모든 docker 도구가 포함되어 있으며 작업 스크립트를 권한이 필요한 모드로 실행할 수 있습니다.

TLS를 사용하도록 활성화된 Docker-in-Docker를 사용해야 합니다. 이는 GitLab.com 인스턴스 러너에서 지원됩니다.

항상 docker:24.0.5와 같이 특정 버전의 이미지를 사용해야 합니다. docker:latest와 같은 태그를 사용하면 사용되는 버전을 제어할 수 없습니다. 새 버전이 출시될 때 호환성 문제를 일으킬 수 있습니다.

Docker executor와 TLS가 활성화된 Docker-in-Docker 사용

  • GitLab Runner 11.11에서 소개되었습니다.

Docker 데몬은 TLS 연결을 지원합니다. TLS는 Docker 19.03.12 이후에 기본으로 사용됩니다.

caution
이 작업은 --docker-privileged를 활성화하여 컨테이너의 보안 메커니즘을 비활성화하고 호스트를 특권 상승에 노출시키는 것을 의미합니다. 이 작업은 컨테이너 탈출을 일으킬 수 있습니다. 자세한 내용은 runtime privilege and Linux capabilities를 참조하십시오.

TLS가 활성화된 Docker-in-Docker를 사용하려면:

  1. GitLab Runner를 설치합니다.
  2. 명령줄에서 GitLab Runner를 등록합니다. dockerprivileged 모드를 사용합니다:

    sudo gitlab-runner register -n \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor docker \
      --description "My Docker Runner" \
      --docker-image "docker:24.0.5" \
      --docker-privileged \
      --docker-volumes "/certs/client"
    
    • 이 명령은 새 러너를 docker:24.0.5 이미지를 사용하도록 등록합니다 (작업 수준에서 지정되지 않은 경우). 빌드 및 서비스 컨테이너를 시작하려면 privileged 모드를 사용합니다. Docker-in-Docker를 사용하려면 Docker 컨테이너에서 항상 privileged = true를 사용해야 합니다.
    • 이 명령은 Docker 클라이언트가 해당 디렉터리의 인증서를 사용하도록 하는 서비스 및 빌드 컨테이너용으로 /certs/client을 마운트합니다. 자세한 내용은 도커 이미지 설명서를 참조하십시오.

    이전 명령은 다음과 같은 config.toml 항목을 생성합니다:

    [[runners]]
      url = "https://gitlab.com/"
      token = TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:24.0.5"
        privileged = true
        disable_cache = false
        volumes = ["/certs/client", "/cache"]
      [runners.cache]
        [runners.cache.s3]
        [runners.cache.gcs]
    
  3. 이제 작업 스크립트에서 docker를 사용할 수 있습니다. docker:24.0.5-dind 서비스를 포함해야 합니다:

    default:
      image: docker:24.0.5
      services:
        - docker:24.0.5-dind
      before_script:
        - docker info
       
    variables:
      # dind 서비스를 사용하는 경우 Docker에 서비스 내부에서 시작된 데몬과 통신하도록 지시해야 합니다.
      # 기본값인 /var/run/docker.sock 소켓 대신 네트워크 연결을 통해 데몬을 사용할 수 있습니다. Docker 19.03에서 이 작업은
      # https://github.com/docker-library/docker/blob/d45051476babc297257df490d22cbd806f1b11e4/19.03/docker-entrypoint.sh#L23-L29 에서
      # 동일하게 작동합니다.
      #
      # 'docker' 호스트명은
      # https://docs.gitlab.com/ee/ci/services/#accessing-the-services에서 설명한 서비스 컨테이너의 별칭입니다.
      #
      # 인증서를 작성할 위치를 Docker에 지정합니다. Docker
      #은 부팅시 자동으로 이를 생성하고
      # 메인과 작업 사이에서 공유하기 위해 config.toml로부터 볼륨 마운트를 이용해
      # /certs/client을 생성합니다.
      DOCKER_TLS_CERTDIR: "/certs"
       
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    
TLS가 비활성화된 Docker 실행기에서 Docker-in-Docker

가끔 TLS를 비활성화해야 하는 합당한 이유가 있습니다. 예를 들어, 사용 중인 GitLab Runner 구성을 제어할 수 없는 경우입니다.

러너의 config.toml이 다음과 유사한 경우를 가정해 봅니다:

[[runners]]
  url = "https://gitlab.com/"
  token = TOKEN
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "docker:24.0.5"
    privileged = true
    disable_cache = false
    volumes = ["/cache"]
  [runners.cache]
    [runners.cache.s3]
    [runners.cache.gcs]

이제 작업 스크립트에서 docker를 사용할 수 있습니다. docker:24.0.5-dind 서비스를 포함해야 합니다:

default:
  image: docker:24.0.5
  services:
    - docker:24.0.5-dind
  before_script:
    - docker info

variables:
  # dind 서비스를 사용하는 경우 Docker에 서비스 내에서 시작된 데몬과 통신하도록 지시해야 합니다.
  # 기본 /var/run/docker.sock 소켓 대신 네트워크 연결로 이용 가능한 데몬입니다.
  #
  # 'docker' 호스트명은 https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services 에서 설명된 서비스 컨테이너 별칭입니다.
  #
  # Kubernetes 실행기 및 Kubernetes 1.6 이전과 함께 GitLab Runner 12.7 이전을 사용하는 경우,
  # 작업 컨테이너에 서비스를 연결하는 방식에 따라 변수를 tcp://localhost:2375로 설정해야 합니다.
  # DOCKER_HOST: tcp://localhost:2375
  #
  DOCKER_HOST: tcp://docker:2375
  #
  # 이는 Docker가 TLS를 시작하지 않도록 지시합니다.
  DOCKER_TLS_CERTDIR: ""

build:
  stage: build
  script:
    - docker build -t my-docker-image .
    - docker run my-docker-image /script/to/run/tests

Docker-in-Docker를 사용하여 Kubernetes 실행기 사용

Kubernetes 실행기를 사용하여 Docker 컨테이너에서 작업을 실행할 수 있습니다.

Kubernetes에서 TLS가 활성화된 Docker-in-Docker
  • GitLab Runner Helm Chart 0.23.0에서 소개됨.

Kubernetes에서 TLS가 활성화된 Docker-in-Docker를 사용하려면 다음을 수행합니다:

  1. Helm 차트를 사용하여, values.yml 파일을 업데이트하여 볼륨 마운트를 지정합니다.

    runners:
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
          [[runners.kubernetes.volumes.empty_dir]]
            name = "docker-certs"
            mount_path = "/certs/client"
            medium = "Memory"
    
  2. 이제 작업 스크립트에서 docker를 사용할 수 있습니다. docker:24.0.5-dind 서비스를 포함해야 합니다:

    default:
      image: docker:24.0.5
      services:
        - docker:24.0.5-dind
      before_script:
        - docker info
       
    variables:
      # dind 서비스를 사용하는 경우 Docker에 서비스 내에서 시작된 데몬과 통신하도록 지시해야 합니다.
      # 기본 /var/run/docker.sock 소켓 대신 네트워크 연결로 이용 가능한 데몬입니다.
      DOCKER_HOST: tcp://docker:2376
      #
      # 'docker' 호스트명은 https://docs.gitlab.com/ee/ci/services/#accessing-the-services 에서 설명된 서비스 컨테이너 별칭입니다.
      # Kubernetes 실행기 및 Kubernetes 1.6 이전과 함께 GitLab Runner 12.7 이전을 사용하는 경우,
      # 작업 컨테이너에 서비스를 연결하는 방식에 따라 변수를 tcp://localhost:2376로 설정해야 합니다.
      # DOCKER_HOST: tcp://localhost:2376
      #
      # Docker에 인증서를 생성할 위치를 지정합니다. Docker는 부팅 시 자동으로 생성하고
      # 볼륨 마운트로 서비스와 작업 컨테이너 간에 공유할 수 있도록 `/certs/client`를 생성합니다.
      DOCKER_TLS_CERTDIR: "/certs"
      # 이러한 값들은 일반적으로 엔트리포인트에서 지정하지만,
      ## Kubernetes 실행기에서는 엔트리포인트가 실행되지 않습니다.
      # https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4125
      DOCKER_TLS_VERIFY: 1
      DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
       
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    
Kubernetes에서 TLS가 비활성화된 Docker-in-Docker

Kubernetes에서 TLS가 비활성화된 Docker-in-Docker를 사용하려면 위의 예제를 다음과 같이 수정해야 합니다.

  • values.yml 파일에서 [[runners.kubernetes.volumes.empty_dir]] 섹션을 제거합니다.
  • DOCKER_HOST: tcp://docker:2375로 포트를 2376에서 2375로 변경합니다.
  • DOCKER_TLS_CERTDIR: ""로 TLS 비활성화 상태로 Docker를 시작하도록 지시합니다.

예시:

  1. Helm 차트를 사용하여 values.yml 파일을 업데이트합니다.

    runners:
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
    
  2. 이제 작업 스크립트에서 docker:24.0.5-dind 서비스를 사용할 수 있습니다.

    default:
      image: docker:24.0.5
      services:
        - docker:24.0.5-dind
      before_script:
        - docker info
       
    variables:
      # dind 서비스를 사용할 때는 Docker가 기본 /var/run/docker.sock 소켓 대신 네트워크 연결로 접근할 수 있도록 해야 합니다.
      DOCKER_HOST: tcp://docker:2375
      #
      # 'docker' 호스트명은 https://docs.gitlab.com/ee/ci/services/#accessing-the-services에 설명된 서비스 컨테이너의 별칭입니다.
      # Kubernetes 실행기와 Kubernetes 1.6 이전 버전을 사용하는 경우 GitLab Runner 12.7 이전 버전의 경우 tcp://localhost:2376로 변수를 설정해야 하며,
      # 이는 Kubernetes 실행기가 서비스를 작업 컨테이너에 연결하는 방식 때문입니다.
      # DOCKER_HOST: tcp://localhost:2376
      #
      # Docker가 TLS를 사용하지 않도록 지시합니다.
      DOCKER_TLS_CERTDIR: ""
       
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Docker-in-Docker와 관련된 알려진 문제

Docker-in-Docker는 권장 구성이지만 다음과 같은 문제를 알아두어야 합니다:

  • docker-compose 명령어: 이 구성에서는 기본적으로 이 명령어를 사용할 수 없습니다. 작업 스크립트에서 docker-compose를 사용하려면 Docker Compose의 설치 지침을 따르세요.
  • 캐시: 각 작업은 새로운 환경에서 실행됩니다. Docker 엔진의 경우 빌드마다 독립된 인스턴스가 생성되므로 동시 작업은 충돌을 일으키지 않습니다. 그러나 레이어를 캐싱하지 않기 때문에 작업이 느릴 수 있습니다. Docker 레이어 캐싱을 참조하세요.
  • 스토리지 드라이버: Docker의 이전 버전은 기본적으로 vfs 스토리지 드라이버를 사용하여 각 작업마다 파일 시스템을 복사합니다. Docker 17.09 이후 버전은 --storage-driver overlay2를 사용하도록 권장합니다. 자세한 내용은 OverlayFS 드라이버 사용를 참조하세요.
  • 루트 파일 시스템: docker:24.0.5-dind 컨테이너와 러너 컨테이너가 서로의 루트 파일 시스템을 공유하지 않기 때문에 작업 디렉터리를 자식 컨테이너의 마운트 지점으로 사용할 수 있습니다. 예를 들어, 자식 컨테이너와 공유할 파일이 있다면 /builds/$CI_PROJECT_PATH 아래에 하위 디렉터리를 생성하고 해당 디렉터리를 마운트 지점으로 사용할 수 있습니다. 자세한 내용은 이슈 #41227을 참조하세요.

    variables:
      MOUNT_POINT: /builds/$CI_PROJECT_PATH/mnt
    script:
      - mkdir -p "$MOUNT_POINT"
      - docker run -v "$MOUNT_POINT:/mnt" my-docker-image
    

Docker 소켓 바인딩을 사용한 Docker 실행기 사용

CI/CD 작업에서 Docker 명령어를 사용하려면 /var/run/docker.sock를 컨테이너에 bind-mount하여 사용할 수 있습니다. 이렇게 하면 이미지의 컨텍스트 내에서 Docker를 사용할 수 있습니다.

Docker 소켓을 바인딩하고 GitLab Runner 11.11 이상을 사용하는 경우 docker:24.0.5-dind 서비스를 더 이상 사용할 수 없습니다. 볼륨 바인딩은 서비스에도 영향을 미치므로 호환되지 않습니다.

Docker를 이미지의 컨텍스트 내에서 사용할 수 있도록 하려면 Docker 실행기에 /var/run/docker.sock을 mount하세요. Docker 실행기로 이를 수행하려면 Volumes in the [runners.docker] section"/var/run/docker.sock:/var/run/docker.sock"을 추가하세요.

다음 예와 유사한 구성을 해야 합니다:

[[runners]]
  url = "https://gitlab.com/"
  token = RUNNER_TOKEN
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "docker:24.0.5"
    privileged = false
    disable_cache = false
    volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
  [runners.cache]
    Insecure = false

러너(runner)를 등록할 때 /var/run/docker.sock을 mount하려면 다음 옵션을 포함하세요:

sudo gitlab-runner register -n \
  --url "https://gitlab.com/" \
  --registration-token REGISTRATION_TOKEN \
  --executor docker \
  --description "My Docker Runner" \
  --docker-image "docker:24.0.5" \
  --docker-volumes /var/run/docker.sock:/var/run/docker.sock

Code Quality를 사용한 Code Climate과 같은 복잡한 Docker-in-Docker 설정의 경우 호스트 및 컨테이너 경로를 일치시켜야 합니다. 자세한 내용은 개인 러너를 사용하여 Code Quality 성능 향상을 참조하세요.

docker:dind 서비스용 레지스트리 미러 활성화

도커 데몬이 서비스 컨테이너 내에서 시작되면 기본 구성을 사용합니다. 성능 향상 및 Docker Hub 속도 제한을 초과하지 않도록 하기 위해 레지스트리 미러를 구성할 수 있습니다.

.gitlab-ci.yml 파일의 서비스

추가 CLI 플래그를 사용하여 dind 서비스에 레지스트리 미러를 설정할 수 있습니다:

services:
  - name: docker:24.0.5-dind
    command: ["--registry-mirror", "https://registry-mirror.example.com"]  # 사용할 레지스트리 미러 지정
GitLab 러너 구성 파일의 서비스

GitLab 러너 관리자인 경우 도커 데몬을 위한 레지스트리 미러를 구성하기 위해 command를 지정할 수 있습니다. dind 서비스는 Docker 또는 Kubernetes executor에 대해 정의되어야 합니다.

도커:

[[runners]]
  ...
  executor = "docker"
  [runners.docker]
    ...
    privileged = true
    [[runners.docker.services]]
      name = "docker:24.0.5-dind"
      command = ["--registry-mirror", "https://registry-mirror.example.com"]

쿠버네티스:

[[runners]]
  ...
  name = "kubernetes"
  [runners.kubernetes]
    ...
    privileged = true
    [[runners.kubernetes.services]]
      name = "docker:24.0.5-dind"
      command = ["--registry-mirror", "https://registry-mirror.example.com"]
GitLab 러너 구성 파일의 Docker executor

GitLab 러너 관리자인 경우 모든 dind 서비스에 대해 미러를 사용할 수 있습니다. 구성을 업데이트하여 볼륨을 마운트하도록 지정합니다.

예를 들어, 다음 내용을 포함하는 /opt/docker/daemon.json 파일이 있는 경우:

{
  "registry-mirrors": [
    "https://registry-mirror.example.com"
  ]
}

config.toml 파일을 업데이트하여 파일을 /etc/docker/daemon.json로 마운트합니다. 이렇게 하면 GitLab 러너에 의해 생성된 모든 컨테이너에 파일이 마운트됩니다. dind 서비스에서 이 구성이 감지됩니다.

[[runners]]
  ...
  executor = "docker"
  [runners.docker]
    image = "alpine:3.12"
    privileged = true
    volumes = ["/opt/docker/daemon.json:/etc/docker/daemon.json:ro"]
GitLab 러너 구성 파일의 Kubernetes executor

GitLab 러너 관리자인 경우 모든 dind 서비스에 대해 미러를 사용할 수 있습니다. 구성을 업데이트하여 ConfigMap 볼륨을 마운트하도록 지정합니다.

예를 들어, 다음 내용을 포함하는 /tmp/daemon.json 파일이 있는 경우:

{
  "registry-mirrors": [
    "https://registry-mirror.example.com"
  ]
}

다음과 같이 명령을 사용하여 이 파일의 내용으로 ConfigMap을 생성할 수 있습니다:

kubectl create configmap docker-daemon --namespace gitlab-runner --from-file /tmp/daemon.json
note
GitLab 러너의 작업 pod을 생성하기 위해 사용하는 네임스페이스를 사용해야 합니다.

ConfigMap이 생성된 후, config.toml 파일을 업데이트하여 파일을 /etc/docker/daemon.json로 마운트합니다. 이 업데이트는 GitLab 러너에 의해 생성된 모든 컨테이너에 파일이 마운트됩니다. dind 서비스가 이 구성을 감지합니다.

[[runners]]
  ...
  executor = "kubernetes"
  [runners.kubernetes]
    image = "alpine:3.12"
    privileged = true
    [[runners.kubernetes.volumes.config_map]]
      name = "docker-daemon"
      mount_path = "/etc/docker/daemon.json"
      sub_path = "daemon.json"

Docker 소켓 바인딩에 대한 알려진 문제점

Docker 소켓 바인딩을 사용하면 Docker를 특권 모드로 실행하지 않을 수 있습니다. 그러나, 이 방법의 함의는 다음과 같습니다:

  • Docker 데몬을 공유함으로써 컨테이너의 모든 보안 메커니즘을 비활성화하고 호스트를 특권 승격에 노출시킵니다. 이는 컨테이너 탈출을 유발할 수 있습니다. 예를 들어, 프로젝트에서 docker rm -f $(docker ps -a -q)를 실행하면 GitLab Runner 컨테이너가 제거될 수 있습니다.
  • 병렬 작업이 수행되지 않을 수 있습니다. 테스트 중에 특정 이름의 컨테이너를 생성하는 경우 서로 충돌할 수 있습니다.
  • Docker 명령으로 생성된 모든 컨테이너는 러너의 자식이 아니라 동등한 위치의 형제 컨테이너가 됩니다. 이는 워크플로에 복잡성을 유발할 수 있습니다.
  • 파일 및 디렉터리를 소스 리포지터리에서 컨테이너로 공유할 수 있지만 예상대로 동작하지 않을 수 있습니다. 볼륨 마운트는 호스트 머신의 컨텍스트에서 수행되며 빌드 컨테이너에서 수행되지 않습니다. 예를 들어:

     docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
    

Docker-in-Docker executor를 사용할 때와 같이 docker:24.0.5-dind 서비스를 포함할 필요가 없습니다.

default:
  image: docker:24.0.5
  before_script:
    - docker info

build:
  stage: build
  script:
    - docker build -t my-docker-image .
    - docker run my-docker-image /script/to/run/tests

Docker-in-Docker에서 레지스트리로 인증

Docker-in-Docker를 사용하는 경우, 새로운 Docker 데몬이 서비스로 시작되기 때문에 표준 인증 방법은 작동하지 않습니다. 따라서 레지스트리로 인증해야 합니다.

Docker 레이어 캐싱을 사용하여 Docker-in-Docker 빌드 가속화

Docker-in-Docker를 사용할 때 모든 이미지 레이어를 매번 빌드할 필요가 없습니다. Docker 레이어 캐싱을 사용하여 빌드 속도를 높일 수 있습니다.

OverlayFS 드라이버 사용

note
GitLab.com의 인스턴스 러너는 기본적으로 overlay2 드라이버를 사용합니다.

기본적으로 docker:dind를 사용할 때 Docker는 vfs 스토리지 드라이버를 사용하며 모든 실행마다 파일 시스템을 복사합니다. 이 디스크 집약적인 작업을 회피하려면 다른 드라이버(예: overlay2)를 사용할 수 있습니다.

요구 사항

  1. 최신 커널(선호되는 버전: >= 4.2)을 사용합니다.
  2. overlay 모듈이 로드되어 있는지 확인합니다:

    sudo lsmod | grep overlay
    

    결과가 없는 경우 모듈이 로드되지 않았습니다. 모듈을 로드하려면 다음을 사용합니다:

    sudo modprobe overlay
    

    모듈이 로드된 경우 부팅 시에도 모듈이 로드되도록 확인해야 합니다. Ubuntu 시스템에서는 /etc/modules에 다음 줄을 추가하여 확인할 수 있습니다:

    overlay
    

프로젝트별로 OverlayFS 드라이버 사용

.gitlab-ci.yml에서 DOCKER_DRIVER CI/CD 변수를 사용하여 각 프로젝트마다 드라이버를 활성화할 수 있습니다:

variables:
  DOCKER_DRIVER: overlay2

모든 프로젝트에 OverlayFS 드라이버 사용

자체 러너를 사용하는 경우 config.toml 파일의 [[runners]] 섹션에서 DOCKER_DRIVER 환경 변수를 설정하여 각 프로젝트에 대해 드라이버를 활성화할 수 있습니다](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section):

environment = ["DOCKER_DRIVER=overlay2"]

여러 러너를 실행 중인 경우 모든 구성 파일을 수정해야 합니다.

러너 구성에 대해 자세히 알아보고 OverlayFS 스토리지 드라이버 사용를 참조하세요.

Docker 대체 옵션

러너에서 특권 모드를 활성화하지 않고 Docker 이미지를 빌드하기 위해 다음과 같은 대체 옵션을 사용할 수 있습니다:

Buildah 예시

GitLab CI/CD에서 Buildah를 사용하려면 러너와 다음 중 하나의 엑스큐터가 필요합니다:

이 예시에서는 Buildah를 사용하여 다음 작업을 수행합니다:

  1. Docker 이미지를 빌드합니다.
  2. GitLab 컨테이너 레지스트리에 푸시합니다.

마지막 단계에서 Buildah는 프로젝트의 컨테이너 레지스트리로 이미지를 빌드하는 데 사용됩니다:

build:
  stage: build
  image: quay.io/buildah/stable
  variables:
    # Buildah에서 vfs 사용. Docker는 overlayfs를 기본으로 제공하지만 Buildah는 다른 overlayfs 파일 시스템에 overlayfs를 스택할 수 없습니다.
    STORAGE_DRIVER: vfs
    # 모든 이미지 메타데이터를 표준 OCI 형식이 아닌 docker 형식으로 작성합니다. 새로운 버전의 Docker는 OCI 형식을 처리할 수 있지만 Fedora 30과 같은 예전 버전은 처리할 수 없습니다.
    BUILDAH_FORMAT: docker
    FQ_IMAGE_NAME: "$CI_REGISTRY_IMAGE/test"
  before_script:
    # [사전 정의된 CI/CD 변수](../variables/index.md#predefined-cicd-variables)에서 가져온 GitLab 컨테이너 레지스트리 자격 증명을 사용하여 레지스트리에 인증합니다.
    - echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
  script:
    - buildah images
    - buildah build -t $FQ_IMAGE_NAME
    - buildah images
    - buildah push $FQ_IMAGE_NAME

OpenShift 클러스터에 배포된 GitLab 러너 오퍼레이터를 사용하는 경우 루트리스 컨테이너에서 이미지를 빌드하는 Buildah 사용 튜토리얼을 참조해 보세요.

GitLab 컨테이너 레지스트리 사용하기

도커 이미지를 빌드한 후 GitLab 컨테이너 레지스트리에 푸시할 수 있습니다.

문제 해결

docker: Cannot connect to the Docker daemon at tcp://docker:2375. Is the docker daemon running?

이 오류는 Docker-in-Docker v19.03 이후에 발생하는 일반적인 오류이다.

이 오류는 Docker가 TLS로 자동으로 시작되기 때문에 발생합니다.

이 오류는 또한 Kubernetes 실행기에서 Docker-in-Docker 서비스에 완전히 시작하기 전에 접근을 시도할 때 발생할 수 있습니다. 자세한 설명은 이슈 27215를 참조하십시오.

Docker no such host 오류

다음과 같은 오류가 발생할 수 있습니다. docker: error during connect: Post https://docker:2376/v1.40/containers/create: dial tcp: lookup docker on x.x.x.x:53: no such host.

이 문제는 서비스 이미지 이름이 레지스트리 호스트 이름을 포함할 때 발생할 수 있습니다. 예를 들어:

default:
  image: docker:24.0.5
  services:
    - registry.hub.docker.com/library/docker:24.0.5-dind

서비스의 호스트명은 전체 이미지 이름에서 파생됩니다. 그러나 더 짧은 서비스 호스트명 docker가 예상됩니다. 서비스 해상도 및 액세스를 허용하려면 서비스 이름 docker에 명시적 별칭을 추가하십시오.

default:
  image: docker:24.0.5
  services:
    - name: registry.hub.docker.com/library/docker:24.0.5-dind
      alias: docker

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

docker 명령을 실행하여 dind 서비스에 액세스하려고 할 때 다음과 같은 오류가 발생할 수 있습니다.

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

작업이 다음과 같은 환경 변수를 정의했는지 확인하십시오:

  • DOCKER_HOST
  • DOCKER_TLS_CERTDIR (옵션)
  • DOCKER_TLS_VERIFY (옵션)

또한 Docker 클라이언트를 제공하는 이미지를 업데이트하는 것이 좋습니다. 예를 들어, docker/compose 이미지는 사용이 중지되었으므로 docker로 대체해야 합니다.

러너(runner) 이슈 30944에 설명된대로, 이 오류는 다음 조건이 모두 충족된 경우 발생할 수 있습니다.

오류: Error response from daemon: Get "https://registry-1.docker.io/v2/": unauthorized: incorrect username or password

이 오류는 사용이 중지된 변수 CI_BUILD_TOKEN을 사용할 때 발생합니다. 사용자가 이 오류를 받지 않도록하려면 다음을 해야 합니다:

  • CI_JOB_TOKEN을 대신 사용하십시오.
  • gitlab-ci-token/CI_BUILD_TOKEN에서 $CI_REGISTRY_USER/$CI_REGISTRY_PASSWORD로 변경하십시오.

오류: error during connect: Post "https://docker:2376/v1.24/auth": dial tcp: lookup docker on 127.0.0.11:53: no such host

이 오류는 dind 서비스가 시작하지 못했을 때 발생합니다. 작업 로그를 확인하여 mount: permission denied (are you root?)이 나타나는지 확인하십시오. 예를 들어:

Service container logs:
2023-08-01T16:04:09.541703572Z Certificate request self-signature ok
2023-08-01T16:04:09.541770852Z subject=CN = docker:dind server
2023-08-01T16:04:09.556183222Z /certs/server/cert.pem: OK
2023-08-01T16:04:10.641128729Z Certificate request self-signature ok
2023-08-01T16:04:10.641173149Z subject=CN = docker:dind client
2023-08-01T16:04:10.656089908Z /certs/client/cert.pem: OK
2023-08-01T16:04:10.659571093Z ip: can't find device 'ip_tables'
2023-08-01T16:04:10.660872131Z modprobe: can't change directory to '/lib/modules': No such file or directory
2023-08-01T16:04:10.664620455Z mount: permission denied (are you root?)
2023-08-01T16:04:10.664692175Z Could not mount /sys/kernel/security.
2023-08-01T16:04:10.664703615Z AppArmor detection and --privileged mode might break.
2023-08-01T16:04:10.665952353Z mount: permission denied (are you root?)

이는 GitLab 러너가 dind 서비스를 시작할 권한이 없음을 나타냅니다:

  1. config.toml에서 privileged = true가 설정되어 있는지 확인하십시오.
  2. CI 작업이 이러한 권한이 부여된 러너 태그를 사용하는지 확인하십시오.

오류: cgroups: cgroup mountpoint does not exist: unknown

Docker Engine 20.10에 의해 도입된 알려진 호환성 문제가 있습니다.

호스트가 Docker Engine 20.10 이상을 사용하는 경우, 20.10 이전 버전의 docker:dind 서비스는 예상대로 작동하지 않습니다.

서비스 자체는 문제없이 시작되지만, 컨테이너 이미지를 빌드하려고 하면 다음과 같은 오류가 발생합니다.

cgroups: cgroup mountpoint does not exist: unknown

이 문제를 해결하려면 docker:dind 컨테이너를 적어도 20.10.x 버전으로 업데이트해야 합니다. 예를 들어 docker:24.0.5-dind와 같이 업데이트할 수 있습니다.

반대의 구성(docker:24.0.5-dind 서비스 및 호스트의 Docker Engine이 19.06.x 또는 그 이전 버전)은 문제없이 작동합니다. 최선의 전략으로는 작업 환경 버전을 자주 테스트하고 업데이트하는 것이 좋습니다. 이렇게 하면 새로운 기능과 향상된 보안이 제공되며, 특정한 경우인 이 업그레이드를 호스트의 Docker Engine에서 프로그램을 실행하는 작업에 투명하게 만들어줍니다.