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 명령을 활성화하려면 다음을 사용할 수 있습니다:

쉘 실행기 사용하기

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

  1. GitLab Runner 설치하기.
  2. 러너 등록하기
    shell 실행기를 선택합니다. 예를 들면:

    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에서 Docker가 작동하는지 확인하기 위해 .gitlab-ci.ymldocker info를 추가합니다:

    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-in-Docker를 사용한 Docker 실행기

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

Docker 실행기에서 TLS가 활성화된 Docker-in-Docker

Docker 데몬은 TLS를 통한 연결을 지원합니다. TLS는 Docker 19.03.12 이상에서 기본값입니다.

경고:
이 작업은 --docker-privileged를 활성화하여 컨테이너의 보안 메커니즘을 사실상 비활성화하고 호스트를 권한 상승에 노출시킵니다. 이 작업은 컨테이너 탈출을 초래할 수 있습니다. 더 많은 정보는 런타임 권한과 리눅스 기능을 참조하세요.

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를 사용해야 합니다.
    • 이 명령은 서비스 및 빌드 컨테이너에 대해 /certs/client를 마운트합니다, 해당 디렉토리의 인증서를 사용하기 위해 Docker 클라이언트에 필요합니다. 더 많은 정보는 Docker 이미지 문서를 참조하세요.

    이전 명령은 다음과 유사한 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은 DOCKER_HOST를 설정하여 이를 자동으로 수행합니다
      # 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는 
      # 부팅 시 자동으로 인증서를 생성하고 
      # `/certs/client`를 서비스와 작업 
      # 컨테이너 간에 공유하도록 생성합니다. 이는 config.toml의 볼륨 마운트 덕분입니다.
      DOCKER_TLS_CERTDIR: "/certs"
    
    build:
      stage: build
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    
Docker-in-Docker에서 TLS 비활성화하기 (Docker executor 사용)

때때로 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 executor와 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

Kubernetes executor에서 Docker-in-Docker 사용하기

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

Kubernetes에서 TLS 활성화된 Docker-in-Docker 사용하기

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 executor와 Kubernetes 1.6 이하의 GitLab Runner 12.7 이하를 사용하는 경우,
      # 변수는 tcp://localhost:2376으로 설정해야 합니다.
      # DOCKER_HOST: tcp://localhost:2376
      #
      # Docker에 인증서를 생성할 위치를 지정합니다. Docker는
      # 부팅 시 자동으로 인증서를 생성하며, 서비스와 작업
      # 컨테이너 간에 공유하기 위해 `/certs/client`를 생성합니다.
      # config.toml에서의 볼륨 마운트 덕분입니다.
      DOCKER_TLS_CERTDIR: "/certs"
      # 일반적으로 엔트리포인트에 의해 지정되지만,
      # Kubernetes executor는 엔트리포인트를 실행하지 않습니다.
      # 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]] 섹션을 제거합니다.
  • 포트를 2376에서 2375로 변경하고 DOCKER_HOST: tcp://docker:2375로 설정합니다.
  • Docker가 TLS가 비활성화된 상태로 시작하도록 DOCKER_TLS_CERTDIR: ""를 지시합니다.

예를 들어:

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

    runners:
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
    
  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:2375
      #
      # 'docker' 호스트 이름은 서비스 컨테이너의 별칭입니다
      # https://docs.gitlab.com/ee/ci/services/#accessing-the-services. 
      # GitLab Runner 12.7 이하 버전과 Kubernetes executor 및 Kubernetes 1.6 이하를 사용하는 경우,
      # 이 변수는 tcp://localhost:2376로 설정해야 합니다. 이는 
      # Kubernetes executor가 서비스와 작업 컨테이너를 연결하는 방식 때문입니다.
      # 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을 컨테이너에 바인드 마운트할 수 있습니다. 그러면 이미지의 컨텍스트 내에서 Docker를 사용할 수 있습니다.

Docker 소켓을 바인드하면 docker:24.0.5-dind를 서비스로 사용할 수 없습니다. 볼륨 바인딩은 서비스에도 영향을 미쳐 호환되지 않습니다.

Docker를 이미지의 컨텍스트에서 사용 가능하게 하려면, 실행되는 컨테이너에 /var/run/docker.sock을 마운트해야 합니다. Docker 실행기와 함께 이 작업을 수행하려면, [runners.docker] 섹션의 Volumes"/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

러너를 등록할 때 /var/run/docker.sock을 마운트하려면 다음 옵션을 포함하세요:

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 Climate를 사용한 코드 품질 체크와 같은 복잡한 Docker-in-Docker 설정의 경우, 적절한 실행을 위해 호스트와 컨테이너 경로를 일치시켜야 합니다. 자세한 내용은 개인 러너로 코드 품질 성능 개선하기를 참조하세요.

docker:dind 서비스에 대한 레지스트리 미러 활성화

서비스 컨테이너 내에서 Docker 데몬이 시작될 때 기본 구성으로 실행됩니다. 성능 개선을 위해 레지스트리 미러를 설정하고 Docker Hub 속도 제한을 초과하지 않도록 구성할 수 있습니다.

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

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

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

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

Docker:

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

Kubernetes:

[[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 Runner 구성 파일의 Docker 실행기

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

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

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

config.toml 파일을 업데이트하여 해당 파일을
/etc/docker/daemon.json에 마운트하세요. 이는 GitLab Runner가 생성하는 모든 컨테이너에 대해 파일을 마운트합니다.
구성은 dind 서비스에 의해 감지됩니다.

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

GitLab Runner 관리자인 경우 모든 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
Kubernetes 실행기가 GitLab Runner용으로 작업 포드를 생성하는 네임스페이스를 사용해야 합니다.

ConfigMap이 생성된 후, config.toml 파일을 업데이트하여 파일을
/etc/docker/daemon.json에 마운트하세요. 이 업데이트는 GitLab Runner가 생성하는 모든 컨테이너에 대해 파일을 마운트합니다.
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 실행기를 사용할 때처럼 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는 빌드를 생성할 때마다 이미지의 모든 레이어를 다운로드합니다. Docker 레이어 캐싱으로 빌드를 더 빠르게 만들 수 있습니다.

OverlayFS 드라이버 사용하기

주의: 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.ymlDOCKER_DRIVER CI/CD 변수를 사용하여 각 프로젝트에 대해 드라이버를 개별적으로 활성화할 수 있습니다:

variables:
  DOCKER_DRIVER: overlay2

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

자신의 러너를 사용하는 경우, config.toml 파일의 [[runners]] 섹션에서 DOCKER_DRIVER 환경 변수를 설정하여 모든 프로젝트에 대해 드라이버를 활성화할 수 있습니다:

environment = ["DOCKER_DRIVER=overlay2"]

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

러너 구성OverlayFS 스토리지 드라이버 사용하기에 대해 더 읽어보세요.

Docker 대안

러너에서 특권 모드를 활성화하지 않고 Docker 이미지를 빌드하려면 다음 중 하나의 대안을 사용할 수 있습니다:

Buildah 예제

GitLab CI/CD에서 Buildah를 사용하려면 다음 중 하나의 실행자가 있는 러너가 필요합니다:

이 예제에서 Buildah를 사용하여:

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

마지막 단계에서 Buildah는 프로젝트의 루트 디렉토리에 있는 Dockerfile을 사용하여 Docker 이미지를 빌드합니다. 마지막으로 이미지를 프로젝트의 컨테이너 레지스트리에 푸시합니다:

build:
  stage: build
  image: quay.io/buildah/stable
  variables:
    # Buildah와 함께 vfs를 사용합니다. Docker는 기본적으로 overlayfs를 제공하지만 Buildah
    #는 다른 overlayfs 파일 시스템 위에 overlayfs를 쌓을 수 없습니다.
    STORAGE_DRIVER: vfs
    # 모든 이미지 메타데이터를 표준 OCI 형식이 아닌 도커 형식으로 작성합니다.
    # 새로운 버전의 도커는 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 Runner Operator를 사용하는 경우, 비루트 컨테이너에서 이미지를 빌드하기 위해 Buildah를 사용하는 튜토리얼을 참조하세요.

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

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

문제 해결

오류: docker: Cannot connect to the Docker daemon at tcp://docker:2375

이 오류는 Docker-in-Docker v19.03 이상을 사용할 때 흔히 발생합니다:

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

이 오류는 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.

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

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

dind 서비스에 접근하기 위해 docker 명령을 실행할 때 다음과 같은 오류가 발생할 수 있습니다:

$ 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로 교체해야 합니다.

러너 이슈 30944에서 설명한 바와 같이, 이 오류는 작업이 이전에 더 이상 지원되지 않는 Docker --link 매개변수에서 파생된 환경 변수에 의존하고 있었던 경우에 발생할 수 있습니다, 예를 들어 DOCKER_PORT_2375_TCP와 같은 환경 변수입니다. 작업이 다음과 같은 이유로 이 오류로 실패합니다:

  • CI/CD 이미지가 DOCKER_PORT_2375_TCP와 같은 레거시 변수에 의존하는 경우.
  • 러너 기능 플래그 FF_NETWORK_PER_BUILDtrue로 설정된 경우.
  • DOCKER_HOST가 명시적으로 설정되지 않은 경우.

오류: unauthorized: incorrect username or password

이 오류는 더 이상 사용되지 않는 변수, CI_BUILD_TOKEN을 사용할 때 발생합니다:

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

사용자가 이 오류를 받지 않도록 하려면 다음과 같이 해야 합니다:

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

오류 발생: no such host

이 오류는 dind 서비스가 시작되지 못했을 때 발생합니다:

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

작업 로그를 확인하여 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 Runner가 dind 서비스를 시작할 권한이 없음을 나타냅니다:

  1. config.toml에서 privileged = true가 설정되어 있는지 확인하세요.

  2. CI 작업에 이러한 특권 러너를 사용할 수 있는 적절한 Runner 태그가 있는지 확인하세요.

오류: 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 업그레이드가 작업에 투명하게 진행됩니다.