InfoGrab DocsInfoGrab Docs

Docker를 사용하여 Docker 이미지 빌드

요약

GitLab CI/CD와 Docker를 함께 사용하여 컨테이너 이미지를 빌드, 테스트, 푸시할 수 있습니다. 선택하는 방식은 인프라, executor 유형, 보안 요구 사항에 따라 달라집니다. CI/CD job에 Docker 명령을 포함하려면, shell executor를 사용하도록 러너를 구성할 수 있습니다.

GitLab CI/CD와 Docker를 함께 사용하여 컨테이너 이미지를 빌드, 테스트, 푸시할 수 있습니다. CI/CD job에서 Docker 명령을 실행하려면 docker 명령을 지원하도록 GitLab Runner를 구성해야 합니다.

선택하는 방식은 인프라, executor 유형, 보안 요구 사항에 따라 달라집니다. 일부 방식은 러너에서 privileged 모드가 필요합니다. privileged 모드를 활성화할 수 없는 경우 Docker 대안을 사용하세요.

방식 Executor Privileged 모드 OS
Shell executor Shell 아니요 Linux
Docker-in-Docker Docker, Kubernetes Linux
Docker 소켓 바인딩 Docker, Kubernetes 아니요 Linux
Docker 파이프 바인딩 Docker, Kubernetes 아니요 Windows

shell executor 사용#

CI/CD job에 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에서 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에게 실질적으로 전체 root 권한을 부여하게 됩니다. 자세한 내용은 docker 그룹의 보안을 참조하세요.

Docker-in-Docker 사용#

Docker-in-Docker(dind)는 등록된 러너가 Docker executor 또는 Kubernetes executor를 사용하고, executor가 Docker의 컨테이너 이미지를 사용하여 CI/CD job을 실행하는 것을 의미합니다.

각 job은 자체 격리된 Docker 데몬을 가지므로 동시 job이 충돌하지 않습니다. 이는 러너가 privileged 모드를 지원할 때 권장되는 방식입니다.

설정 지침은 Docker-in-Docker 사용을 참조하세요.

Docker 소켓 바인딩 사용#

CI/CD job에서 Docker 명령을 사용하려면 /var/run/docker.sock을 빌드 컨테이너에 바인드 마운트할 수 있습니다. 그러면 Docker를 이미지 컨텍스트에서 사용할 수 있습니다.

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

Docker executor와 Docker 소켓 바인딩 사용#

Docker executor로 Docker 소켓을 마운트하려면 "/var/run/docker.sock:/var/run/docker.sock"[runners.docker] 섹션의 볼륨에 추가합니다.

  1. 러너 등록 시 /var/run/docker.sock을 마운트하려면 다음 옵션을 포함합니다:

    sudo gitlab-runner register \
      --non-interactive \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor "docker" \
      --description "docker-runner" \
      --tag-list "socket-binding-docker-runner" \
      --docker-image "docker:24.0.5-cli" \
      --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"
    

    이전 명령은 다음 예와 유사한 config.toml 항목을 생성합니다:

    [[runners]]
      url = "https://gitlab.com/"
      token = RUNNER_TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:24.0.5-cli"
        privileged = false
        disable_cache = false
        volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
      [runners.cache]
        Insecure = false
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:24.0.5-cli
      before_script:
        - docker info
    
    build:
      stage: build
      tags:
        - socket-binding-docker-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Kubernetes executor와 Docker 소켓 바인딩 사용#

Kubernetes executor로 Docker 소켓을 마운트하려면 "/var/run/docker.sock"[[runners.kubernetes.volumes.host_path]] 섹션의 볼륨에 추가합니다.

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

    runners:
      tags: "socket-binding-kubernetes-runner"
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = false
          [runners.kubernetes]
            [[runners.kubernetes.volumes.host_path]]
              host_path = '/var/run/docker.sock'
              mount_path = '/var/run/docker.sock'
              name = 'docker-sock'
              read_only = true
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:24.0.5-cli
      before_script:
        - docker info
    build:
      stage: build
      tags:
        - socket-binding-kubernetes-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Docker 소켓 바인딩의 알려진 문제#

Docker 소켓 바인딩을 사용하면 Docker를 privileged 모드로 실행하는 것을 방지할 수 있습니다. 그러나 이 방법의 영향은 다음과 같습니다:

  • Docker 데몬을 공유하면 실질적으로 컨테이너의 보안 메커니즘을 비활성화하고 호스트를 권한 상승에 노출시킵니다. 이는 컨테이너 탈출을 일으킬 수 있습니다. 예를 들어, 프로젝트에서 docker rm -f $(docker ps -a -q)를 실행하면 GitLab Runner 컨테이너가 제거됩니다.

  • 동시 job이 작동하지 않을 수 있습니다. 테스트에서 특정 이름의 컨테이너를 생성하면 서로 충돌할 수 있습니다.

  • 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-cli
  before_script:
    - docker info

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

CodeClimate를 사용한 코드 품질 스캐닝과 같은 복잡한 Docker-in-Docker 설정의 경우, 적절한 실행을 위해 호스트와 컨테이너 경로를 일치시켜야 합니다. 자세한 내용은 CodeClimate 기반 스캐닝에 개인 러너 사용을 참조하세요.

Docker 파이프 바인딩 사용#

Windows 컨테이너는 Docker-in-Docker 방식을 지원하지 않으므로, 컨테이너 내에서 중첩된 Docker Engine을 실행할 수 없습니다. Windows 컨테이너 내에서 Docker 이미지를 빌드하거나 관리하려면 Docker 파이프 바인딩(Docker-outside-of-Docker 또는 DooD라고도 함)을 사용합니다.

Warning

Docker 파이프 바인딩은 보안적 영향이 있습니다. \\\\.\\pipe\\docker_engine을 바인드 마운트하면 컨테이너가 호스트의 Docker 데몬에 대한 전체 관리자 액세스 권한을 갖게 됩니다. 컨테이너 내의 프로세스는 다른 컨테이너를 시작하거나 중지하고, 이미지를 관리하며, 호스트 시스템에서 권한을 상승시킬 수 있습니다.

Docker 파이프 바인딩을 사용하려면 호스트 Windows Server 운영 체제에 Docker Engine을 설치하고 실행해야 합니다. 자세한 내용은 Windows Server에 Docker Community Edition (CE) 설치를 참조하세요.

Windows 기반 컨테이너 CI/CD job에서 Docker 명령을 사용하려면 \\\\.\\pipe\\docker_engine을 실행된 executor 컨테이너에 바인드 마운트할 수 있습니다. 그러면 Docker를 이미지 컨텍스트에서 사용할 수 있습니다.

Windows에서의 Docker 파이프 바인딩Linux에서의 Docker 소켓 바인딩과 유사하며 알려진 문제Docker 소켓 바인딩의 알려진 문제와 유사합니다.

Docker 파이프 바인딩 사용의 필수 전제 조건은 호스트 Windows Server 운영 체제에 Docker Engine이 설치되어 실행 중인 것입니다.

Docker executor와 Docker 파이프 바인딩 사용#

Docker executor를 사용하여 Windows 기반 컨테이너에서 job을 실행할 수 있습니다.

Docker executor로 Docker 파이프를 마운트하려면 "\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"[runners.docker] 섹션의 볼륨에 추가합니다.

  1. 러너 등록 시 \\\\.\\pipe\\docker_engine을 마운트하려면 다음 옵션을 포함합니다:

    .\gitlab-runner.exe register \
      --non-interactive \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor "docker-windows" \
      --description "docker-windows-runner"
      --tag-list "docker-windows-runner" \
      --docker-image "docker:25-windowsservercore-ltsc2022" \
      --docker-volumes "\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"
    

    이전 명령은 다음 예와 유사한 config.toml 항목을 생성합니다:

    [[runners]]
      url = "https://gitlab.com/"
      token = RUNNER_TOKEN
      executor = "docker-windows"
      [runners.docker]
        tls_verify = false
        image = "docker:25-windowsservercore-ltsc2022"
        privileged = false
        disable_cache = false
        volumes = ["\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"]
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:25-windowsservercore-ltsc2022
      before_script:
        - docker version
        - docker info
    
    build:
      stage: build
      tags:
        - docker-windows-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Kubernetes executor와 Docker 파이프 바인딩 사용#

Kubernetes executor를 사용하여 Windows 기반 컨테이너에서 job을 실행할 수 있습니다.

Windows 기반 컨테이너에 Kubernetes executor를 사용하려면 Kubernetes 클러스터에 Windows 노드를 포함해야 합니다. 자세한 내용은 Kubernetes의 Windows 컨테이너를 참조하세요.

Linux 환경에서 실행 중이지만 Windows 노드를 대상으로 하는 러너를 사용할 수 있습니다.

Kubernetes executor로 Docker 파이프를 마운트하려면 "\\\\.\\pipe\\docker_engine"[[runners.kubernetes.volumes.host_path]] 섹션의 볼륨에 추가합니다.

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

    runners:
      tags: "kubernetes-windows-runner"
      config: |
        [[runners]]
          executor = "kubernetes"
    
          # FF_USE_POWERSHELL_PATH_RESOLVER 기능 플래그는 러너가 Linux 환경에서
          # 실행 중이지만 Windows 노드를 대상으로 할 때
          # PowerShell이 Windows 경로를 올바르게 확인하도록 활성화해야 합니다.
          [runners.feature_flags]
            FF_USE_POWERSHELL_PATH_RESOLVER = true
    
          [runners.kubernetes]
            [[runners.kubernetes.volumes.host_path]]
              host_path = '\\\\.\\pipe\\docker_engine'
              mount_path = '\\\\.\\pipe\\docker_engine'
              name = 'docker-pipe'
              read_only = true
    
            [runners.kubernetes.node_selector]
              "kubernetes.io/arch" = "amd64"
              "kubernetes.io/os" = "windows"
              "node.kubernetes.io/windows-build" = "10.0.20348"
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:25-windowsservercore-ltsc2022
      before_script:
        - docker version
        - docker info
    
    build:
      stage: build
      tags:
        - kubernetes-windows-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

AWS EKS Kubernetes 클러스터의 알려진 문제#

dockerd에서 containerd로 마이그레이션할 때, AWS EKS 부트스트래핑 스크립트 Start-EKSBootstrap.ps1이 Docker 서비스를 중지하고 비활성화합니다. 이 문제를 해결하려면 Windows Server에 Docker Community Edition (CE) 설치 후 이 스크립트로 Docker 서비스 이름을 바꿉니다:

Write-Output "Rename the just installed Docker Engine Service from docker to dockerd"
Write-Output "because the Start-EKSBootstrap.ps1 stops and disables the docker Service as part of migration from dockerd to containerd"
Stop-Service -Name docker
dockerd --register-service --service-name dockerd
Start-Service -Name dockerd
Write-Output "Ready to do Docker pipe binding on Windows EKS Node! :-)"

Docker 파이프 바인딩의 알려진 문제#

Docker 파이프 바인딩은 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 Runner 구성 파일의 서비스#

GitLab Runner 관리자라면 command를 지정하여 Docker 데몬의 레지스트리 미러를 구성할 수 있습니다. 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 executor#

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 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
Note

GitLab Runner의 Kubernetes executor가 job 파드를 생성하는 데 사용하는 네임스페이스를 사용해야 합니다.

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-in-Docker에서 레지스트리 인증#

Docker-in-Docker를 사용할 때, 표준 인증 방법은 작동하지 않습니다. 서비스와 함께 새로운 Docker 데몬이 시작되기 때문입니다. 레지스트리 인증을 참조하세요.

Docker 레이어 캐싱#

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 환경 변수를 설정하여 모든 프로젝트에 대해 드라이버를 활성화할 수 있습니다:

environment = ["DOCKER_DRIVER=overlay2"]

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

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

Docker 대안#

러너에서 privileged 모드를 활성화하지 않고 컨테이너 이미지를 빌드할 수 있습니다:

  • BuildKit: Docker 데몬 의존성을 제거하는 rootless BuildKit 옵션 포함.
  • Buildah: Docker 데몬 없이 OCI 호환 이미지 빌드.

Buildah 예시#

GitLab CI/CD와 함께 Buildah를 사용하려면 다음 executor 중 하나를 가진 러너가 필요합니다:

이 예시에서 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

OpenShift 클러스터에 배포된 GitLab Runner Operator를 사용하는 경우, rootless 컨테이너에서 Buildah를 사용하여 이미지를 빌드하는 튜토리얼을 참조하세요.

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

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

트러블슈팅#

open //./pipe/docker_engine: The system cannot find the file specified#

PowerShell 스크립트에서 마운트된 Docker 파이프에 접근하기 위해 docker 명령을 실행할 때 다음 오류가 발생할 수 있습니다:

PS C:\> docker version
Client:
 Version:           25.0.5
 API version:       1.44
 Go version:        go1.21.8
 Git commit:        5dc9bcc
 Built:             Tue Mar 19 15:06:12 2024
 OS/Arch:           windows/amd64
 Context:           default
error during connect: this error may indicate that the docker daemon is not running: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.44/version": open //./pipe/docker_engine: The system cannot find the file specified.

오류는 Docker Engine이 Windows EKS 노드에서 실행되지 않고 있으며 Windows 기반 Executor 컨테이너에서 Docker 파이프 바인딩을 사용할 수 없음을 나타냅니다.

문제를 해결하려면 Kubernetes executor와 Docker 파이프 바인딩 사용에 설명된 해결 방법을 사용하세요.

Docker를 사용하여 Docker 이미지 빌드

GitLab v19.2
Tier: Free, Premium, Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
원문 보기
요약

GitLab CI/CD와 Docker를 함께 사용하여 컨테이너 이미지를 빌드, 테스트, 푸시할 수 있습니다. 선택하는 방식은 인프라, executor 유형, 보안 요구 사항에 따라 달라집니다. CI/CD job에 Docker 명령을 포함하려면, shell executor를 사용하도록 러너를 구성할 수 있습니다.

GitLab CI/CD와 Docker를 함께 사용하여 컨테이너 이미지를 빌드, 테스트, 푸시할 수 있습니다. CI/CD job에서 Docker 명령을 실행하려면 docker 명령을 지원하도록 GitLab Runner를 구성해야 합니다.

선택하는 방식은 인프라, executor 유형, 보안 요구 사항에 따라 달라집니다. 일부 방식은 러너에서 privileged 모드가 필요합니다. privileged 모드를 활성화할 수 없는 경우 Docker 대안을 사용하세요.

방식 Executor Privileged 모드 OS
Shell executor Shell 아니요 Linux
Docker-in-Docker Docker, Kubernetes Linux
Docker 소켓 바인딩 Docker, Kubernetes 아니요 Linux
Docker 파이프 바인딩 Docker, Kubernetes 아니요 Windows

shell executor 사용#

CI/CD job에 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에서 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에게 실질적으로 전체 root 권한을 부여하게 됩니다. 자세한 내용은 docker 그룹의 보안을 참조하세요.

Docker-in-Docker 사용#

Docker-in-Docker(dind)는 등록된 러너가 Docker executor 또는 Kubernetes executor를 사용하고, executor가 Docker의 컨테이너 이미지를 사용하여 CI/CD job을 실행하는 것을 의미합니다.

각 job은 자체 격리된 Docker 데몬을 가지므로 동시 job이 충돌하지 않습니다. 이는 러너가 privileged 모드를 지원할 때 권장되는 방식입니다.

설정 지침은 Docker-in-Docker 사용을 참조하세요.

Docker 소켓 바인딩 사용#

CI/CD job에서 Docker 명령을 사용하려면 /var/run/docker.sock을 빌드 컨테이너에 바인드 마운트할 수 있습니다. 그러면 Docker를 이미지 컨텍스트에서 사용할 수 있습니다.

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

Docker executor와 Docker 소켓 바인딩 사용#

Docker executor로 Docker 소켓을 마운트하려면 "/var/run/docker.sock:/var/run/docker.sock"[runners.docker] 섹션의 볼륨에 추가합니다.

  1. 러너 등록 시 /var/run/docker.sock을 마운트하려면 다음 옵션을 포함합니다:

    sudo gitlab-runner register \
      --non-interactive \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor "docker" \
      --description "docker-runner" \
      --tag-list "socket-binding-docker-runner" \
      --docker-image "docker:24.0.5-cli" \
      --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"
    

    이전 명령은 다음 예와 유사한 config.toml 항목을 생성합니다:

    [[runners]]
      url = "https://gitlab.com/"
      token = RUNNER_TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:24.0.5-cli"
        privileged = false
        disable_cache = false
        volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
      [runners.cache]
        Insecure = false
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:24.0.5-cli
      before_script:
        - docker info
    
    build:
      stage: build
      tags:
        - socket-binding-docker-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Kubernetes executor와 Docker 소켓 바인딩 사용#

Kubernetes executor로 Docker 소켓을 마운트하려면 "/var/run/docker.sock"[[runners.kubernetes.volumes.host_path]] 섹션의 볼륨에 추가합니다.

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

    runners:
      tags: "socket-binding-kubernetes-runner"
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = false
          [runners.kubernetes]
            [[runners.kubernetes.volumes.host_path]]
              host_path = '/var/run/docker.sock'
              mount_path = '/var/run/docker.sock'
              name = 'docker-sock'
              read_only = true
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:24.0.5-cli
      before_script:
        - docker info
    build:
      stage: build
      tags:
        - socket-binding-kubernetes-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Docker 소켓 바인딩의 알려진 문제#

Docker 소켓 바인딩을 사용하면 Docker를 privileged 모드로 실행하는 것을 방지할 수 있습니다. 그러나 이 방법의 영향은 다음과 같습니다:

  • Docker 데몬을 공유하면 실질적으로 컨테이너의 보안 메커니즘을 비활성화하고 호스트를 권한 상승에 노출시킵니다. 이는 컨테이너 탈출을 일으킬 수 있습니다. 예를 들어, 프로젝트에서 docker rm -f $(docker ps -a -q)를 실행하면 GitLab Runner 컨테이너가 제거됩니다.

  • 동시 job이 작동하지 않을 수 있습니다. 테스트에서 특정 이름의 컨테이너를 생성하면 서로 충돌할 수 있습니다.

  • 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-cli
  before_script:
    - docker info

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

CodeClimate를 사용한 코드 품질 스캐닝과 같은 복잡한 Docker-in-Docker 설정의 경우, 적절한 실행을 위해 호스트와 컨테이너 경로를 일치시켜야 합니다. 자세한 내용은 CodeClimate 기반 스캐닝에 개인 러너 사용을 참조하세요.

Docker 파이프 바인딩 사용#

Windows 컨테이너는 Docker-in-Docker 방식을 지원하지 않으므로, 컨테이너 내에서 중첩된 Docker Engine을 실행할 수 없습니다. Windows 컨테이너 내에서 Docker 이미지를 빌드하거나 관리하려면 Docker 파이프 바인딩(Docker-outside-of-Docker 또는 DooD라고도 함)을 사용합니다.

Warning

Docker 파이프 바인딩은 보안적 영향이 있습니다. \\\\.\\pipe\\docker_engine을 바인드 마운트하면 컨테이너가 호스트의 Docker 데몬에 대한 전체 관리자 액세스 권한을 갖게 됩니다. 컨테이너 내의 프로세스는 다른 컨테이너를 시작하거나 중지하고, 이미지를 관리하며, 호스트 시스템에서 권한을 상승시킬 수 있습니다.

Docker 파이프 바인딩을 사용하려면 호스트 Windows Server 운영 체제에 Docker Engine을 설치하고 실행해야 합니다. 자세한 내용은 Windows Server에 Docker Community Edition (CE) 설치를 참조하세요.

Windows 기반 컨테이너 CI/CD job에서 Docker 명령을 사용하려면 \\\\.\\pipe\\docker_engine을 실행된 executor 컨테이너에 바인드 마운트할 수 있습니다. 그러면 Docker를 이미지 컨텍스트에서 사용할 수 있습니다.

Windows에서의 Docker 파이프 바인딩Linux에서의 Docker 소켓 바인딩과 유사하며 알려진 문제Docker 소켓 바인딩의 알려진 문제와 유사합니다.

Docker 파이프 바인딩 사용의 필수 전제 조건은 호스트 Windows Server 운영 체제에 Docker Engine이 설치되어 실행 중인 것입니다.

Docker executor와 Docker 파이프 바인딩 사용#

Docker executor를 사용하여 Windows 기반 컨테이너에서 job을 실행할 수 있습니다.

Docker executor로 Docker 파이프를 마운트하려면 "\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"[runners.docker] 섹션의 볼륨에 추가합니다.

  1. 러너 등록 시 \\\\.\\pipe\\docker_engine을 마운트하려면 다음 옵션을 포함합니다:

    .\gitlab-runner.exe register \
      --non-interactive \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor "docker-windows" \
      --description "docker-windows-runner"
      --tag-list "docker-windows-runner" \
      --docker-image "docker:25-windowsservercore-ltsc2022" \
      --docker-volumes "\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"
    

    이전 명령은 다음 예와 유사한 config.toml 항목을 생성합니다:

    [[runners]]
      url = "https://gitlab.com/"
      token = RUNNER_TOKEN
      executor = "docker-windows"
      [runners.docker]
        tls_verify = false
        image = "docker:25-windowsservercore-ltsc2022"
        privileged = false
        disable_cache = false
        volumes = ["\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"]
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:25-windowsservercore-ltsc2022
      before_script:
        - docker version
        - docker info
    
    build:
      stage: build
      tags:
        - docker-windows-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

Kubernetes executor와 Docker 파이프 바인딩 사용#

Kubernetes executor를 사용하여 Windows 기반 컨테이너에서 job을 실행할 수 있습니다.

Windows 기반 컨테이너에 Kubernetes executor를 사용하려면 Kubernetes 클러스터에 Windows 노드를 포함해야 합니다. 자세한 내용은 Kubernetes의 Windows 컨테이너를 참조하세요.

Linux 환경에서 실행 중이지만 Windows 노드를 대상으로 하는 러너를 사용할 수 있습니다.

Kubernetes executor로 Docker 파이프를 마운트하려면 "\\\\.\\pipe\\docker_engine"[[runners.kubernetes.volumes.host_path]] 섹션의 볼륨에 추가합니다.

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

    runners:
      tags: "kubernetes-windows-runner"
      config: |
        [[runners]]
          executor = "kubernetes"
    
          # FF_USE_POWERSHELL_PATH_RESOLVER 기능 플래그는 러너가 Linux 환경에서
          # 실행 중이지만 Windows 노드를 대상으로 할 때
          # PowerShell이 Windows 경로를 올바르게 확인하도록 활성화해야 합니다.
          [runners.feature_flags]
            FF_USE_POWERSHELL_PATH_RESOLVER = true
    
          [runners.kubernetes]
            [[runners.kubernetes.volumes.host_path]]
              host_path = '\\\\.\\pipe\\docker_engine'
              mount_path = '\\\\.\\pipe\\docker_engine'
              name = 'docker-pipe'
              read_only = true
    
            [runners.kubernetes.node_selector]
              "kubernetes.io/arch" = "amd64"
              "kubernetes.io/os" = "windows"
              "node.kubernetes.io/windows-build" = "10.0.20348"
    
  2. job 스크립트에서 Docker를 사용합니다:

    default:
      image: docker:25-windowsservercore-ltsc2022
      before_script:
        - docker version
        - docker info
    
    build:
      stage: build
      tags:
        - kubernetes-windows-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests
    

AWS EKS Kubernetes 클러스터의 알려진 문제#

dockerd에서 containerd로 마이그레이션할 때, AWS EKS 부트스트래핑 스크립트 Start-EKSBootstrap.ps1이 Docker 서비스를 중지하고 비활성화합니다. 이 문제를 해결하려면 Windows Server에 Docker Community Edition (CE) 설치 후 이 스크립트로 Docker 서비스 이름을 바꿉니다:

Write-Output "Rename the just installed Docker Engine Service from docker to dockerd"
Write-Output "because the Start-EKSBootstrap.ps1 stops and disables the docker Service as part of migration from dockerd to containerd"
Stop-Service -Name docker
dockerd --register-service --service-name dockerd
Start-Service -Name dockerd
Write-Output "Ready to do Docker pipe binding on Windows EKS Node! :-)"

Docker 파이프 바인딩의 알려진 문제#

Docker 파이프 바인딩은 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 Runner 구성 파일의 서비스#

GitLab Runner 관리자라면 command를 지정하여 Docker 데몬의 레지스트리 미러를 구성할 수 있습니다. 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 executor#

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 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
Note

GitLab Runner의 Kubernetes executor가 job 파드를 생성하는 데 사용하는 네임스페이스를 사용해야 합니다.

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-in-Docker에서 레지스트리 인증#

Docker-in-Docker를 사용할 때, 표준 인증 방법은 작동하지 않습니다. 서비스와 함께 새로운 Docker 데몬이 시작되기 때문입니다. 레지스트리 인증을 참조하세요.

Docker 레이어 캐싱#

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 환경 변수를 설정하여 모든 프로젝트에 대해 드라이버를 활성화할 수 있습니다:

environment = ["DOCKER_DRIVER=overlay2"]

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

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

Docker 대안#

러너에서 privileged 모드를 활성화하지 않고 컨테이너 이미지를 빌드할 수 있습니다:

  • BuildKit: Docker 데몬 의존성을 제거하는 rootless BuildKit 옵션 포함.
  • Buildah: Docker 데몬 없이 OCI 호환 이미지 빌드.

Buildah 예시#

GitLab CI/CD와 함께 Buildah를 사용하려면 다음 executor 중 하나를 가진 러너가 필요합니다:

이 예시에서 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

OpenShift 클러스터에 배포된 GitLab Runner Operator를 사용하는 경우, rootless 컨테이너에서 Buildah를 사용하여 이미지를 빌드하는 튜토리얼을 참조하세요.

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

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

트러블슈팅#

open //./pipe/docker_engine: The system cannot find the file specified#

PowerShell 스크립트에서 마운트된 Docker 파이프에 접근하기 위해 docker 명령을 실행할 때 다음 오류가 발생할 수 있습니다:

PS C:\> docker version
Client:
 Version:           25.0.5
 API version:       1.44
 Go version:        go1.21.8
 Git commit:        5dc9bcc
 Built:             Tue Mar 19 15:06:12 2024
 OS/Arch:           windows/amd64
 Context:           default
error during connect: this error may indicate that the docker daemon is not running: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.44/version": open //./pipe/docker_engine: The system cannot find the file specified.

오류는 Docker Engine이 Windows EKS 노드에서 실행되지 않고 있으며 Windows 기반 Executor 컨테이너에서 Docker 파이프 바인딩을 사용할 수 없음을 나타냅니다.

문제를 해결하려면 Kubernetes executor와 Docker 파이프 바인딩 사용에 설명된 해결 방법을 사용하세요.