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 모드를 필요로 합니다.

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

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

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

The 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"
    
    • 이 명령은 빌드 및 서비스 컨테이너 시작에 privileged 모드를 사용하여 docker:24.0.5 이미지를 사용하는 새 러너를 등록합니다. 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에 지시해야 합니다. 데몬은 기본 설정인 /var/run/docker.sock 소켓 대신 네트워크 연결로 사용할 수 있습니다.
      # Docker 19.03은 이를 자동으로 수행합니다.
      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 컨테이너에서 작업을 실행할 수 있습니다.

TLS가 활성화된 Kubernetes에서 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 실행기 및 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"
      # 일반적으로 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
    
TLS가 비활성화된 Kubernetes에서 Docker-in-Docker 사용

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

  • values.yml 파일에서 [[runners.kubernetes.volumes.empty_dir]] 섹션을 삭제합니다.
  • DOCKER_HOST: tcp://docker: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에서 설명된 서비스 컨테이너의 별칭입니다.
      # Kubernetes 실행기 및 Kubernetes 1.6 이전을 사용하는 GitLab Runner 12.7 이전의 경우
      # 작업 컨테이너에 서비스를 연결하는 방식으로 인해 변수는 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 계층 캐싱을 참조하세요(#make-docker-in-docker-builds-faster-with-docker-layer-caching).
  • 저장 드라이버: 기본적으로, 이전 버전의 Docker는 각 작업에 대해 파일 시스템을 복사하는 vfs 저장 드라이버를 사용합니다. Docker 17.09 이후로는 권장하는 저장 드라이버인 --storage-driver overlay2를 사용합니다. 자세한 내용은 OverlayFS 드라이버 사용를 참조하십시오.
  • 루트 파일 시스템: docker:24.0.5-dind 컨테이너와 러너 컨테이너가 루트 파일 시스템을 공유하지 않기 때문에, 작업 디렉터리를 하위 컨테이너의 마운트 지점으로 사용할 수 있습니다. 예를 들어, 하위 컨테이너와 공유하려는 파일이 있다면, /builds/$CI_PROJECT_PATH 아래에 하위 디렉터리를 생성하고 이를 마운트 지점으로 사용할 수 있습니다. 더 자세한 설명은 이슈 #41227를 참조하세요.

    이슈 #41227.

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

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

CI/CD 작업에서 도커 명령어를 사용하려면 /var/run/docker.sock을 컨테이너에 바인딩하면 됩니다. 그러면 도커가 이미지의 컨텍스트에서 사용할 수 있습니다.

도커 소켓을 바인딩하고 있고 GitLab Runner 11.11 이상을 사용하면 docker:24.0.5-dind를 서비스로 더 이상 사용할 수 없습니다. 볼륨 바인딩은 서비스에도 영향을 주어, 호환되지 않게 만듭니다.

이미지의 컨텍스트에서 도커를 사용하려면, 실행된 컨테이너에 /var/run/docker.sock을 마운트해야 합니다. 도커 실행기로 이 작업을 하려면 [runners.docker] 섹션에 "/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

코드 품질 체크와 같은 복잡한 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 실행자를 위해 정의되어 있어야 합니다.

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 실행자

GitLab 러너 관리자라면 모든 dind 서비스에 대해 미러를 사용할 수 있습니다. 구성을 업데이트하여 볼륨 마운트를 지정할 수 있습니다. json { "registry-mirrors": [ "https://registry-mirror.example.com" ] }

/etc/docker/daemon.json 파일에 아래 내용으로 config.toml 파일을 수정합니다. 이렇게 하면 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 executor

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

예를 들어, /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 Runner용 Kubernetes executor가 작업 pod을 생성하는 데 사용하는 네임스페이스를 사용해야 합니다.

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 드라이버 사용

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.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 형식이 아니라 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

GitLab Runner Operator가 OpenShift 클러스터에 배포된 경우 루트리스 컨테이너에서 이미지를 빌드하는 데 Buildah 사용하는 튜토리얼을 확인해보세요.

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

Docker 이미지를 빌드한 후 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 서비스에 완전히 시작하기 전에 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 명령을 실행하려고 할 때 다음 오류가 발생할 수 있습니다:

$ 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에 설명되어 있듯이, 이 오류는 작업에서 이전에 사용되었던 Deprecated Docker --link 매개변수에서 유도된 환경 변수(예: DOCKER_PORT_2375_TCP)로 인해 발생할 수 있습니다. 다음 조건에서 작업이 이 오류로 실패합니다.

오류: unauthorized: incorrect username or password

이 오류는 Deprecated 변수 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?)가 나타나는지 확인하세요. 예를 들어:

서비스 컨테이너 로그:
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.tomlprivileged = 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 Engine 19.06.x 또는 이전 버전이 있는 경우 docker:24.0.5-dind 서비스)는 문제없이 작동합니다. 최상의 전략은 자주 테스트하여 환경 버전을 최신 상태로 업데이트하는 것입니다. 이로써 새로운 기능이나 향상된 보안을 제공하며, 특히 이 경우에는 러너 호스트의 기반이 되는 Docker Engine을 업그레이드할 때 투명하게 작업하게 됩니다.