개념

Edit This Page

Images

사용자 Docker 이미지를 생성하고 레지스트리에 푸시(push)하여 쿠버네티스 파드에서 참조되기 이전에 대비한다.

컨테이너의 image 속성은 docker 커맨드에서 지원하는 문법과 같은 문법을 지원한다. 이는 프라이빗 레지스트리와 태그를 포함한다.

이미지 업데이트

기본 풀(pull) 정책은 IfNotPresent이며, 이것은 Kubelet이 이미 존재하는 이미지에 대한 풀을 생략하게 한다. 만약 항상 풀을 강제하고 싶다면, 다음 중 하나를 수행하면 된다.

:latest 태그 사용은 피해야 한다는 것을 참고하고, 자세한 정보는 구성을 위한 모범 사례를 참고한다.

매니페스트로 멀티-아키텍처 이미지 빌드

Docker CLI는 현재 docker manifest 커맨드와 create, annotate, push와 같은 서브 커맨드를 함께 지원한다. 이 커맨드는 매니페스트를 빌드하고 푸시하는데 사용할 수 있다. 매니페스트를 보기 위해서는 docker manifest inspect를 사용하면 된다.

다음에서 docker 문서를 확인하기 바란다. https://docs.docker.com/edge/engine/reference/commandline/manifest/

이것을 사용하는 방법에 대한 예제는 빌드 하니스(harness)에서 참조한다. https://cs.k8s.io/?q=docker%20manifest%20(create%7Cpush%7Cannotate)&i=nope&files=&repos=

이 커맨드는 Docker CLI에 의존하며 그에 전적으로 구현된다. $HOME/.docker/config.json 편집 및 experimental 키를 enabled로 설정하거나, CLI 커맨드 호출 시 간단히 DOCKER_CLI_EXPERIMENTAL 환경 변수를 enabled로만 설정해도 된다.

참고: Docker 18.06 또는 그 이상 을 사용하길 바란다. 더 낮은 버전은 버그가 있거나 실험적인 명령줄 옵션을 지원하지 않는다. 예를 들어 https://github.com/docker/cli/issues/1135 는 containerd에서 문제를 일으킨다.

오래된 매니페스트 업로드를 실행하는 데 어려움을 겪는다면, $HOME/.docker/manifests에서 오래된 매니페스트를 정리하여 새롭게 시작하면 된다.

쿠버네티스의 경우, 일반적으로 접미사 -$(ARCH)가 있는 이미지를 사용해 왔다. 하위 호환성을 위해, 접미사가 있는 구형 이미지를 생성하길 바란다. 접미사에 대한 아이디어는 모든 아키텍처를 위한 매니페스트를 가졌다는 의미가 내포된 pause 이미지를 생성하고, 접미사가 붙은 이미지가 하드 코드되어 있을 오래된 구성 또는 YAML 파일에 대해 하위 호환된다는 의미가 내포되어 있는 pause-amd64를 생성하기 위한 것이다.

프라이빗 레지스트리 사용

프라이빗 레지스트리는 해당 레지스트리에서 이미지를 읽을 수 있는 키를 요구할 것이다. 자격 증명(credential)은 여러 가지 방법으로 제공될 수 있다.

각 옵션은 아래에서 더 자세히 설명한다.

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

쿠버네티스는 Google 컴퓨트 엔진(GCE)에서 동작할 때, Google 컨테이너 레지스트리(GCR)를 자연스럽게 지원한다. 사용자의 클러스터가 GCE 또는 Google 쿠버네티스 엔진에서 동작 중이라면, 간단히 이미지의 전체 이름(예: gcr.io/my_project/image:tag)을 사용하면 된다.

클러스터 내에서 모든 파드는 해당 레지스트리에 있는 이미지에 읽기 접근 권한을 가질 것이다.

Kubelet은 해당 인스턴스의 Google 서비스 계정을 이용하여 GCR을 인증할 것이다. 인스턴스의 서비스 계정은 https://www.googleapis.com/auth/devstorage.read_only라서, 프로젝트의 GCR로부터 풀은 할 수 있지만 푸시는 할 수 없다.

AWS EC2 컨테이너 레지스트리 사용

쿠버네티스는 노드가 AWS EC2 인스턴스일 때, AWS EC2 컨테이너 레지스트리를 자연스럽게 지원한다.

간단히 이미지의 전체 이름(예: ACCOUNT.dkr.ecr.REGION.amazonaws.com/imagename:tag)을 파드 정의에 사용하면 된다.

파드를 생성할 수 있는 클러스터의 모든 사용자는 ECR 레지스트리에 있는 어떠한 이미지든지 파드를 실행하는데 사용할 수 있다.

kubelet은 ECR 자격 증명을 가져오고 주기적으로 갱신할 것이다. 이것을 위해서는 다음에 대한 권한이 필요하다.

요구 사항:

문제 해결:

Azure 컨테이너 레지스트리(ACR) 사용

Azure 컨테이너 레지스트리를 사용하는 경우 관리자 역할의 사용자나 서비스 주체(principal) 중 하나를 사용하여 인증할 수 있다. 어느 경우라도, 인증은 표준 Docker 인증을 통해서 수행된다. 이러한 지침은 azure-cli 명령줄 도구 사용을 가정한다.

우선 레지스트리를 생성하고 자격 증명을 만들어야한다. 이에 대한 전체 문서는 Azure 컨테이너 레지스트리 문서에서 찾을 수 있다.

컨테이너 레지스트리를 생성하고 나면, 다음의 자격 증명을 사용하여 로그인한다.

해당 변수에 대한 값을 채우고 나면 쿠버네티스 시크릿을 구성하고 그것을 파드 디플로이를 위해서 사용할 수 있다.

IBM 클라우드 컨테이너 레지스트리 사용

IBM 클라우드 컨테이너 레지스트리는 멀티-테넌트 프라이빗 이미지 레지스트리를 제공하여 사용자가 Docker 이미지를 안전하게 저장하고 공유할 수 있도록 한다. 기본적으로, 프라이빗 레지스트리의 이미지는 통합된 취약점 조언기(Vulnerability Advisor)를 통해 조사되어 보안 이슈와 잠재적 취약성을 검출한다. IBM 클라우드 계정의 모든 사용자가 이미지에 접근할 수 있도록 하거나, 레지스트리 네임스페이스에 접근을 승인하는 토큰을 생성할 수 있다.

IBM 클라우드 컨테이너 레지스트리 CLI 플러그인을 설치하고 사용자 이미지를 위한 네임스페이스를 생성하기 위해서는, IBM 클라우드 컨테이너 레지스트리 시작하기를 참고한다.

IBM 클라우드 퍼블릭 이미지 및 사용자의 프라이빗 이미지로부터 컨테이너를 사용자의 IBM 클라우드 쿠버네티스 서비스 클러스터의 default 네임스페이스에 디플로이하기 위해서 IBM 클라우드 컨테이너 레지스트리를 사용하면 된다. 컨테이너를 다른 네임스페이스에 디플로이하거나, 다른 IBM 클라우드 컨테이너 레지스트리 지역 또는 IBM 클라우드 계정을 사용하기 위해서는, 쿠버네티스 imagePullSecret를 생성한다. 더 자세한 정보는, 이미지로부터 컨테이너 빌드하기를 참고한다.

프라이빗 레지스트리에 대한 인증을 위한 노드 구성

참고: Google 쿠버네티스 엔진에서 동작 중이라면, 이미 각 노드에 Google 컨테이너 레지스트리에 대한 자격 증명과 함께 .dockercfg가 있을 것이다. 그렇다면 이 방법은 쓸 수 없다.
참고: AWS EC2에서 동작 중이고 EC2 컨테이너 레지스트리(ECR)을 사용 중이라면, 각 노드의 kubelet은 ECR 로그인 자격 증명을 관리하고 업데이트할 것이다. 그렇다면 이 방법은 쓸 수 없다.
참고: 이 방법은 노드의 구성을 제어할 수 있는 경우에만 적합하다. 이 방법은 GCE 및 자동 노드 교체를 수행하는 다른 클라우드 제공자에 대해서는 신뢰성 있게 작동하지 않을 것이다.
참고: 현재 쿠버네티스는 docker 설정의 authsHttpHeaders 섹션만 지원한다. 이는 자격증명 도우미(credHelpers 또는 credStore)가 지원되지 않는다는 뜻이다.

Docker는 프라이빗 레지스트리를 위한 키를 $HOME/.dockercfg 또는 $HOME/.docker/config.json 파일에 저장한다. 만약 동일한 파일을 아래의 검색 경로 리스트에 넣으면, kubelete은 이미지를 풀 할 때 해당 파일을 자격 증명 공급자로 사용한다.

참고: 아마도 kubelet을 위한 사용자의 환경 파일에 HOME=/root을 명시적으로 설정해야 할 것이다.

프라이빗 레지스트리를 사용도록 사용자의 노드를 구성하기 위해서 권장되는 단계는 다음과 같다. 이 예제의 경우, 사용자의 데스크탑/랩탑에서 아래 내용을 실행한다.

  1. 사용하고 싶은 각 자격 증명 세트에 대해서 docker login [서버]를 실행한다. 이것은 $HOME/.docker/config.json를 업데이트한다.
  2. 편집기에서 $HOME/.docker/config.json를 보고 사용하고 싶은 자격 증명만 포함하고 있는지 확인한다.
  3. 노드의 리스트를 구한다. 예를 들면 다음과 같다.
    • 이름을 원하는 경우: nodes=$(kubectl get nodes -o jsonpath='{range.items[*].metadata}{.name} {end}')
    • IP를 원하는 경우: nodes=$(kubectl get nodes -o jsonpath='{range .items[*].status.addresses[?(@.type=="ExternalIP")]}{.address} {end}')
  4. 로컬의 .docker/config.json를 위의 검색 경로 리스트 중 하나에 복사한다.
    • 예: for n in $nodes; do scp ~/.docker/config.json root@$n:/var/lib/kubelet/config.json; done

프라이빗 이미지를 사용하는 파드를 생성하여 검증한다. 예를 들면 다음과 같다.

kubectl create -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: private-image-test-1
spec:
  containers:
    - name: uses-private-image
      image: $PRIVATE_IMAGE_NAME
      imagePullPolicy: Always
      command: [ "echo", "SUCCESS" ]
EOF
pod/private-image-test-1 created

만약 모든 것이 잘 작동한다면, 잠시 후에, 다음 메시지를 볼 것이다.

kubectl logs private-image-test-1
SUCCESS

만약 실패했다면, 다음 메시지를 볼 것이다.

kubectl describe pods/private-image-test-1 | grep "Failed"
  Fri, 26 Jun 2015 15:36:13 -0700    Fri, 26 Jun 2015 15:39:13 -0700    19    {kubelet node-i2hq}    spec.containers{uses-private-image}    failed        Failed to pull image "user/privaterepo:v1": Error: image user/privaterepo:v1 not found

클러스터의 모든 노드가 반드시 동일한 .docker/config.json를 가져야 한다. 그렇지 않으면, 파드가 일부 노드에서만 실행되고 다른 노드에서는 실패할 것이다. 예를 들어, 노드 오토스케일링을 사용한다면, 각 인스턴스 템플릿은 .docker/config.json을 포함하거나 그것을 포함한 드라이브를 마운트해야 한다.

프라이빗 레지스트리 키가 .docker/config.json에 추가되고 나면 모든 파드는 프라이빗 레지스트리의 이미지에 읽기 접근 권한을 가지게 될 것이다.

미리 풀링(pre-pulling)된 이미지

참고: Google 쿠버네티스 엔진에서 동작 중이라면, 이미 각 노드에 Google 컨테이너 레지스트리에 대한 자격 증명과 함께 .dockercfg가 있을 것이다. 그렇다면 이 방법은 쓸 수 없다.
참고: 이 방법은 노드의 구성을 제어할 수 있는 경우에만 적합하다. 이 방법은 GCE 및 자동 노드 교체를 수행하는 다른 클라우드 제공자에 대해서는 신뢰성 있게 작동하지 않을 것이다.

기본적으로, kubelet은 지정된 레지스트리에서 각 이미지를 풀 하려고 할 것이다. 그러나, 컨테이너의 imagePullPolicy 속성이 IfNotPresent 또는 Never으로 설정되어 있다면, 로컬 이미지가 사용된다(우선적으로 또는 배타적으로).

레지스트리 인증의 대안으로 미리 풀 된 이미지에 의존하고 싶다면, 클러스터의 모든 노드가 동일한 미리 풀 된 이미지를 가지고 있는지 확인해야 한다.

이것은 특정 이미지를 속도를 위해 미리 로드하거나 프라이빗 레지스트리에 대한 인증의 대안으로 사용될 수 있다.

모든 파드는 미리 풀 된 이미지에 대해 읽기 접근 권한을 가질 것이다.

파드에 ImagePullSecrets 명시

참고: 이 방법은 현재 Google 쿠버네티스 엔진, GCE 및 노드 생성이 자동화된 모든 클라우드 제공자에게 권장된다.

쿠버네티스는 파드에 레지스트리 키를 명시하는 것을 지원한다.

Docker 구성으로 시크릿 생성

대문자 값을 적절히 대체하여, 다음 커맨드를 실행한다.

kubectl create secret docker-registry myregistrykey --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret/myregistrykey created.

만약 다중 레지스트리에 접근이 필요하다면, 각 레지스트리에 대한 하나의 시크릿을 생성할 수 있다. Kubelet은 파드를 위한 이미지를 풀링할 때 imagePullSecrets를 단일의 가상 .docker/config.json 에 병합할 것이다.

파드는 이미지 풀 시크릿을 자신의 네임스페이스에서만 참조할 수 있다. 따라서 이 과정은 네임스페이스 당 한 번만 수행될 필요가 있다.

kubectl create secrets 우회

어떤 이유에서 단일 .docker/config.json에 여러 항목이 필요하거나 위의 커맨드를 통해서는 주어지지 않는 제어가 필요한 경우, json 또는 yaml로 시크릿 생성을 수행할 수 있다.

다음 사항을 준수해야 한다.

예:

apiVersion: v1
kind: Secret
metadata:
  name: myregistrykey
  namespace: awesomeapps
data:
  .dockerconfigjson: UmVhbGx5IHJlYWxseSByZWVlZWVlZWVlZWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGxsbGx5eXl5eXl5eXl5eXl5eXl5eXl5eSBsbGxsbGxsbGxsbGxsbG9vb29vb29vb29vb29vb29vb29vb29vb29vb25ubm5ubm5ubm5ubm5ubm5ubm5ubm5ubmdnZ2dnZ2dnZ2dnZ2dnZ2dnZ2cgYXV0aCBrZXlzCg==
type: kubernetes.io/dockerconfigjson

error: no objects passed to create라는 에러 메시지가 나오면, 그것은 base64 인코딩된 문자열이 유효하지 않다는 것을 뜻한다. Secret "myregistrykey" is invalid: data[.dockerconfigjson]: invalid value ...와 유사한 에러 메시지가 나오면, 그것은 base64 인코딩 된 데이터가 성공적으로 디코딩되었지만, .docker/config.json 파일로는 파싱될 수 없었음을 의미한다.

파드의 imagePullSecrets 참조

이제, imagePullSecrets 섹션을 파드의 정의에 추가함으로써 해당 시크릿을 참조하는 파드를 생성할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: foo
  namespace: awesomeapps
spec:
  containers:
    - name: foo
      image: janedoe/awesomeapp:v1
  imagePullSecrets:
    - name: myregistrykey

이것은 프라이빗 레지스트리를 사용하는 각 파드에 대해서 수행될 필요가 있다.

그러나, 이 필드의 셋팅은 서비스 어카운트 리소스에 imagePullSecrets을 셋팅하여 자동화할 수 있다. 자세한 지침을 위해서는 서비스 어카운트에 ImagePullSecrets 추가를 확인한다.

이것은 노드 당 .docker/config.json와 함께 사용할 수 있다. 자격 증명은 병합될 것이다. 이 방법은 Google 쿠버네티스 엔진에서 작동될 것이다.

유스케이스

프라이빗 레지스트리를 구성하기 위한 많은 솔루션이 있다. 다음은 여러 가지 일반적인 유스케이스와 제안된 솔루션이다.

  1. 비소유 이미지(예를 들어, 오픈소스)만 실행하는 클러스터의 경우. 이미지를 숨길 필요가 없다.
    • Docker hub의 퍼블릭 이미지를 사용한다.
      • 설정이 필요 없다.
      • GCE 및 Google 쿠버네티스 엔진에서는, 속도와 가용성 향상을 위해서 로컬 미러가 자동적으로 사용된다.
  2. 모든 클러스터 사용자에게는 보이지만, 회사 외부에는 숨겨야하는 일부 독점 이미지를 실행하는 클러스터의 경우.
    • 호스트 된 프라이빗 Docker 레지스트리를 사용한다.
      • 그것은 Docker Hub에 호스트 되어 있거나, 다른 곳에 되어 있을 것이다.
      • 위에 설명된 바와 같이 수동으로 .docker/config.json을 구성한다.
    • 또는, 방화벽 뒤에서 읽기 접근 권한을 가진 내부 프라이빗 레지스트리를 실행한다.
      • 쿠버네티스 구성은 필요 없다.
    • 또는, GCE 및 Google 쿠버네티스 엔진에서는, 프로젝트의 Google 컨테이너 레지스트리를 사용한다.
      • 그것은 수동 노드 구성에 비해서 클러스터 오토스케일링과 더 잘 동작할 것이다.
    • 또는, 노드의 구성 변경이 불편한 클러스터에서는, imagePullSecrets를 사용한다.
  3. 독점 이미지를 가진 클러스터로, 그 중 일부가 더 엄격한 접근 제어를 필요로 하는 경우.
    • AlwaysPullImages 어드미션 컨트롤러가 활성화되어 있는지 확인한다. 그렇지 않으면, 모든 파드가 잠재적으로 모든 이미지에 접근 권한을 가진다.
    • 민감한 데이터는 이미지 안에 포장하는 대신, “시크릿” 리소스로 이동한다.
  4. 멀티-테넌트 클러스터에서 각 테넌트가 자신의 프라이빗 레지스트리를 필요로 하는 경우.
    • AlwaysPullImages 어드미션 컨트롤러가 활성화되어 있는지 확인한다. 그렇지 않으면, 모든 파드가 잠재적으로 모든 이미지에 접근 권한을 가진다.
    • 인가가 요구되도록 프라이빗 레지스트리를 실행한다.
    • 각 테넌트에 대한 레지스트리 자격 증명을 생성하고, 시크릿에 넣고, 각 테넌트 네임스페이스에 시크릿을 채운다.
    • 테넌트는 해당 시크릿을 각 네임스페이스의 imagePullSecrets에 추가한다.

피드백