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

The shell executor 사용하기

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

  1. GitLab Runner 설치.
  2. Runner 등록. 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에 docker info.gitlab-ci.yml에 추가하여 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에 전체 root 권한이 부여됩니다. 더 많은 정보는 docker 그룹의 보안을 참조하십시오.

Docker-in-Docker 사용하기

“Docker-in-Docker”(dind)은:

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

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

항상 docker:latest와 같은 태그 대신 특정 버전을 사용해야 합니다. 새로운 버전이 출시되면 호환성 문제가 발생할 수 있기 때문입니다.

Docker executor와 Docker-in-Docker 사용하기

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

Docker executor에서 TLS가 활성화된 Docker-in-Docker

Docker 데몬은 TLS 연결을 지원합니다. TLS는 Docker 19.03.12부터 기본값입니다.

경고: 이 작업은 --docker-privileged을 활성화하여 컨테이너의 보안 메커니즘을 비활성화하고 호스트를 특권 승격에 노출시키는 것을 의미합니다. 이 작업은 컨테이너 탈출을 유발할 수 있습니다. 자세한 내용은 런타임 특권 및 Linux 기능을 참조하십시오.

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를 마운트합니다. 자세한 내용은 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를 서비스 내부에서 실행되는 데몬과 통신하도록 Docker에 지시해야 합니다.
      # 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 구성을 제어할 수 없는 경우입니다.

가정하에 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에 설명된 서비스 컨테이너의 별칭입니다.
  #
  # GitLab Runner 12.7 이하 버전을 사용하여 Kubernetes 실행기와 Kubernetes 1.6 이하 버전을 사용하는 경우,
  # Kubernetes 실행기가 서비스를 작업 컨테이너에 연결하는 방식 때문에 변수를 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 실행기에서 Docker-in-Docker 사용

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

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

Kubernetes에서 TLS가 활성화된 Docker-in-Docker를 사용하려면 다음을 따르세요:

  1. 헬름 차트를 사용하여 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에 설명된 서비스 컨테이너의 별칭입니다.
      # GitLab Runner 12.7 이하 버전을 사용하여 Kubernetes 실행기와 Kubernetes 1.6 이하 버전을 사용하는 경우,
      # 변수를 tcp://localhost:2376로 설정해야 합니다.
      # DOCKER_HOST: tcp://localhost:2376
      #
      # Docker에게 인증서를 생성할 위치를 지정합니다. Docker는 부팅 시 자동으로 이를 생성하고 config.toml에서의 볼륨 마운트를 통해 서비스와 작업 컨테이너 간에 공유하도록 합니다.
      DOCKER_TLS_CERTDIR: "/certs"
      # 일반적으로 이것들은 entrypoint에 지정하지만 Kubernetes 실행기는 entrypoint를 실행하지 않습니다.
      # 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를 사용할 수 있습니다. 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 실행기와 Kubernetes 1.6 이하 버전을 사용하는 경우,
      # 변수를 tcp://localhost:2376로 설정해야 합니다.
      # 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 아래에 하위 디렉터리를 만들고 마운트 지점으로 사용할 수 있습니다. 더 자세한 설명은 issue #41227을 확인하세요.

    변수:
      MOUNT_POINT: /builds/$CI_PROJECT_PATH/mnt
    스크립트:
      - 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 소켓을 바인딩하면 docker:24.0.5-dind을 서비스로 사용할 수 없습니다. 볼륨 바인딩은 서비스에도 영향을 미치므로 호환되지 않습니다.

이미지의 컨텍스트에서 Docker를 사용하려면 러너의 컨텍스트에 /var/run/docker.sock을 마운트해야 합니다. Docker 실행자로 사용하려면 [runners.docker] 섹션에 "/var/run/docker.sock:/var/run/docker.sock"을 추가하세요. (Volumes in the [runners.docker] section을 참조하세요).

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

[[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 Quality 검사와 같은 복잡한 Docker-in-Docker 설정의 경우, 올바른 실행을 위해 호스트와 컨테이너 경로를 일치시켜야 합니다. 자세한 내용은 프라이빗 러너로 Code Quality 성능 향상을 참조하세요.

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

서비스 컨테이너 내에서 Docker 데몬이 시작되면 기본 구성을 사용합니다. 성능 향상을 위해 레지스트리 미러를 구성할 수 있습니다.

.gitlab-ci.yml 파일의 서비스에 추가 CLI 플래그를 붙여서 레지스트리 미러를 설정할 수 있습니다:

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

GitLab 러너 구성 파일에서도 Docker 데몬을 위한 레지스트리 미러를 설정할 수 있습니다. 이를 위해 dind 서비스가 Docker 또는 Kubernetes 실행자로 정의되어야 합니다.

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 러너 구성 파일에서 Docker 실행자를 사용하고 있다면 매 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 Runner 구성 파일의 Kubernetes executor

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

참고: 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 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-in-Docker 빌드를 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.yml 파일의 DOCKER_DRIVER CI/CD 변수를 사용하여 각 프로젝트에 대해 드라이버를 활성화할 수 있습니다:

variables:
  DOCKER_DRIVER: overlay2

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

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

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:
    # vfs를 buildah와 함께 사용합니다. Docker는 오버레이FS를 기본으로 제공하지만, Buildah는 다른 오버레이FS 파일 시스템 위에 다른 오버레이FS를 스택할 수 없습니다.
    STORAGE_DRIVER: vfs
    # 모든 이미지 메타데이터를 표준 OCI 형식이 아니라 Docker 형식으로 작성합니다. 새로운 버전의 Docker는 OCI 형식을 처리할 수 있지만, Fedora 30과 같이 오래된 버전은 처리할 수 없을 수 있습니다.
    BUILDAH_FORMAT: docker
    FQ_IMAGE_NAME: "$CI_REGISTRY_IMAGE/test"
  before_script:
    # GitLab 컨테이너 레지스트리 자격 증명은
    # [사전 정의 CI/CD 변수](../variables/index.md#predefined-cicd-variables)에서 가져와 레지스트리에 인증합니다.
    - 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 컨테이너 레지스트리 사용

도커 이미지를 빌드한 후 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라는 오류가 발생할 수 있습니다.

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

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

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에서 설명한대로, 이 오류는 다음 조건을 충족하는 경우 발생할 수 있습니다.

  • 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로 변경합니다.

connect 중 오류 발생: 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 러너가 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 업그레이드가 작업에 대해 투명해집니다.