TheKoguryo's 기술 블로그

 Version 2023.11.20

1.5 OCIR 이미지 사용하기

OCIR에 이미지 등록하기

Oracle Cloud Infrastructure Registry(OCIR)는 오라클이 제공하는 관리형 컨테이너 레지스트리로 Docker V2 API를 지원하며, Open Container Initiate 호환 컨테이너 레지스트리입니다. docker cli를 통해 이미지를 Push, Pull 해서 사용할 수 있으며, Kubernetes 클러스터에서도 사용할 수 있습니다.

OCIR에 이미지를 사용하기 위해서는 먼저 등록 작업이 필요하며, 앞서 예제에서 사용한 nginx 이미지를 아래 절차에 따라 등록해 봅니다.

OCIR Repository 만들기
  1. OCI 콘솔에 로그인합니다.

  2. 좌측 상단 햄버거 메뉴에서 Developer Services > Containers & Artifacts > Container Registry로 이동합니다.

  3. List Scope에서 대상 Compartment를 선택합니다.

  4. 이미지를 Push하기 전에 먼저 OCIR에 repository를 생성이 필요합니다.

    Create repository를 클릭하여 아래와 같이 nginx repository를 생성합니다. Push, Pull 모두 인증 테스트를 위해 Access 모드를 Private으로 선택합니다.

    image-20230516184351566

  5. 생성 완료

    image-20230516184612998

  6. Repository 화면에서 Namespace를 복사해 둡니다.

OCI Auth Token 만들기

docker cli로 docker hub에 이미지를 등록하거나, 가져올때 username/password로 docker login을 통해 로그인을 합니다. OCIR에도 마찬가지로 로그인이 필요하며, password 대신 보안을 위해 Auth Token을 사용합니다.

  1. 우측 상단 사용자의 Profile 아이콘을 클릭하여 My profile로 이동합니다.

    • 아래 그림상의 유저의 username은 Identity Domain 이름인 Default를 제외한 kildong@example.com입니다.

    image-20230516185456736

  2. 왼쪽 아래 Resources > Auth Token으로 이동합니다.

  3. Auth Token 생성을 위해 Generate Token을 클릭합니다.

  4. 설명을 입력하고 생성합니다. Auth Token은 생성시에만 볼수 있으므로 복사해 둡니다.

    image-20230516185740732

    image-20230516185854037

OCIR 로그인 및 이미지 Push
  1. 앞서 생성한 Auth Token을 통해 Cloud Shell 또는 접속 환경에서 docker cli로 로그인 합니다.

    • OCIR 주소: <region-key>.ocir.io
      • region-key: 예, yny
      • region-identifier: 예, ap-chuncheon-1
      • 전체 Region별 OCIR 주소: Availability by Region
    • Username:
      • <tenancy-namespace>/<username> 형식
      • Username: OCI 콘솔에서 유저 Profile에서 보이는 유저명을 사용합니다.
        1. <tenancy-namespace>/<username>
      • tenancy-namespace: 앞서 Repository 생성시 확인한 tenancy-namespace 또는 Cloud Shell에서 oci os ns get으로 확인 가능
    • Password: 앞서 생성한 로그인할 유저의 Auth Token
    kildong@cloudshell:~ (ap-chuncheon-1)$ oci os ns get
    {
      "data": "cnxxxxxxxxgq"  
    }
    kildong@cloudshell:~ (ap-chuncheon-1)$ docker login yny.ocir.io
    Username: cnxxxxxxxxgq/kildong@example.com
    Password: 
    WARNING! Your password will be stored unencrypted in /home/kildong/.docker/config.json.
    Configure a credential helper to remove this warning. See
    https://docs.docker.com/engine/reference/commandline/login/#credentials-store
    
    Login Succeeded
    
  2. 이미지 Push

    • OCIR에 생성한 Repository로 Push 하기 위해 아래 형식으로 태그를 한 후 Push 하면 됩니다.
      • <region-key>.ocir.io/<tenancy-namespace>/<repo-name>:<tag>
    • nginx:latest 예시
    docker pull nginx:latest
    docker tag nginx:latest yny.ocir.io/cnxxxxxxxxgq/nginx:latest
    docker push yny.ocir.io/cnxxxxxxxxgq/nginx:latest
    
  3. OCIR 확인

    OCI 서비스 콘솔로 다시 돌아가서 대상 Compartment 기준으로 Push한 이미지가 정상적으로 등록된 것을 알 수 있습니다.

    image-20230516190919554

실수를 막기 위한 참고 사항
다음과 같은 상황에서는 docker push하면 어떻게 될까요?
  • Push 전에 OCIR Repository를 사전에 만들지 않은 경우

    => 사전에 OCIR Repository를 만들지 않으면, 기본 설정에 의해 root compartment 쪽에 push 됩니다.

  • dev/nginx:latest(또는 bitnami/nginx:latest)와 같이 이미지 이름 앞에 추가 적인 경로가 있는 경우에 OCIR Repository를 dev 로만 만든 경우

    => dev/nginx 까지를 Repository 이름으로 생각합니다. 그래서 OCIR에서 dev/nginx를 Repository Name으로 입력하여 생성합니다. 그렇게 만들지 않는 경우 동일하게 root compartment 쪽에 push 됩니다.

Container Registry 우측 상단에 Settings를 클릭하여 설정정보를 보면 아래와 같이 대상 repository가 없는 경우 root compartment에 private repository를 자동으로 새로 만들고 push 하는 것이 기본 값으로 체크되어 있습니다.

image-20230516191459987

OCIR 이미지로 OKE 클러스터에 배포

OCIR 이미지 배포 테스트
  1. 가장 흔한 형태인 Public Container Registry에 이미지를 가져와서 OKE 클러스터에 배포를 해봅니다.

    kubectl create deployment nginx-ocir --image=yny.ocir.io/cnxxxxxxxxgq/nginx:latest
    
  2. 배포 결과 아래와 같이 private repository로 인증문제로 이미지를 가져오는 오류가 발생한 것을 알수 있습니다.

    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl create deployment nginx-ocir --image=yny.ocir.io/cnxxxxxxxxgq/nginx:latest
    deployment.apps/nginx-ocir created
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl get pod
    NAME                                READY   STATUS             RESTARTS   AGE
    nginx-ocir-56d7b8d55c-t667j         0/1     ImagePullBackOff   0          15s
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl describe pod nginx-ocir-56d7b8d55c-t667j
    Name:         nginx-ocir-56d7b8d55c-t667j
    ...
    Events:
      Type     Reason     Age                From               Message
      ----     ------     ----               ----               -------
      ...
      Warning  Failed     15s (x2 over 27s)  kubelet            Failed to pull image "yny.ocir.io/cnxxxxxxxxgq/nginx:latest": rpc error: code = Unknown desc = reading manifest latest in yny.ocir.io/cnxxxxxxxxgq/nginx: denied: Anonymous users are only allowed read access on public repos
      Warning  Failed     15s (x2 over 27s)  kubelet            Error: ErrImagePull
      Normal   BackOff    1s (x2 over 26s)   kubelet            Back-off pulling image "yny.ocir.io/cnxxxxxxxxgq/nginx:latest"
      Warning  Failed     1s (x2 over 26s)   kubelet            Error: ImagePullBackOff
    
OCIR Private Repository 이미지 배포 테스트 - imagepullsecret

Private Repository에서 이미지를 가져와서 사용하려면 인증을 위한 secret을 등록해서 사용해야 합니다. 아래 절차에 따라 secret을 만들어 사용합니다.

  1. 앞서 Auth Token을 사용하여 docker login을 하였습니다. 사용자 홈 밑에 .docker/config.json에 인증정보가 저장됩니다.

    kildong@cloudshell:~ (ap-chuncheon-1)$ docker login yny.ocir.io
    Username: cnxxxxxxxxgq/kildong@example.com
    Password: 
    WARNING! Your password will be stored unencrypted in /home/kildong/.docker/config.json.
    Configure a credential helper to remove this warning. See
    https://docs.docker.com/engine/reference/commandline/login/#credentials-store
    
    Login Succeeded
    
  2. 위 인증 정보를 통해 secret을 만듭니다.

    kubectl create secret generic ocir-secret \
        --from-file=.dockerconfigjson=$HOME/.docker/config.json \
        --type=kubernetes.io/dockerconfigjson
    
  3. 또는 docker login 정보 없이 직접 secret을 만들 수도 있습니다.

    kubectl create secret docker-registry <secret-name> --docker-server=<region-key>.ocir.io --docker-username='<tenancy-namespace>/<oci-username>' --docker-password='<oci-auth-token>' --docker-email='<email-address>'
    
  4. 아래와 같이 imagepullsecret을 사용하여 다시 배포합니다.

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: nginx-ocir
      name: nginx-ocir
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: nginx-ocir
      template:
        metadata:
          labels:
            app: nginx-ocir
        spec:
          containers:
          - name: nginx
            image: yny.ocir.io/cnxxxxxxxxgq/nginx:latest
          imagePullSecrets:
          - name: ocir-secret
    
  5. 아래와 같이 Running 상태로 정상 배포되는 것을 볼 수 있습니다.

    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl create secret generic ocir-secret \
    >     --from-file=.dockerconfigjson=$HOME/.docker/config.json \
    >     --type=kubernetes.io/dockerconfigjson
    
    secret/ocir-secret created
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl get secret
    NAME          TYPE                             DATA   AGE
    ocir-secret   kubernetes.io/dockerconfigjson   1      46s
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl apply -f nginx-ocir-deployment.yaml
    deployment.apps/nginx-ocir configured
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl get all
    NAME                                    READY   STATUS    RESTARTS   AGE
    pod/nginx-ocir-86bcf7867c-tk4m5         1/1     Running   0          86s
    
    NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)             AGE
    service/kubernetes             ClusterIP      10.96.0.1       <none>           443/TCP,12250/TCP   152m
    
    NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/nginx-ocir         1/1     1            1           7m21s
    
    NAME                                          DESIRED   CURRENT   READY   AGE
    replicaset.apps/nginx-ocir-86bcf7867c         1         1         1       87s
    
OCIR Private Repository 이미지 배포 테스트 - default imagepullsecret

매번 imagepullsecret을 지정하는 것이 불편한 경우 기본으로 사용할 Container Repository에 대한 인증을 default로 저장하여 사용할 수도 있습니다.

  1. namespace에 default serviceaccount가 있는데, 여기에 아래와 같이 imagepullsecret을 추가합니다.

    kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "ocir-secret"}]}'
    
  2. 그 결과 아래와 같이 default serviceaccount에 기본적으로 사용할 imagesecret이 추가되었습니다

    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "ocir-secret"}]}'
    serviceaccount/default patched
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl get sa default -o yaml
    apiVersion: v1
    imagePullSecrets:
    - name: ocir-secret
    kind: ServiceAccount
    metadata:
      creationTimestamp: "2023-05-16T07:52:12Z"
      name: default
      namespace: default
      resourceVersion: "37675"
      uid: 477e180e-82af-4ff4-a1cd-b9bd12763078
    
  3. 앞서 배포한 yaml을 삭제하고 인증정보가 없어 처음 실패한 명령으로 다시 배포합니다.

    kubectl delete deployment nginx-ocir
    kubectl create deployment nginx-ocir --image=yny.ocir.io/cnxxxxxxxxgq/nginx:latest
    
  4. 결과확인하면 default imagepullsecret을 사용하여 정상 배포됨을 알 수 있습니다.

    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl delete deployment nginx-ocir
    deployment.apps "nginx-ocir" deleted
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl create deployment nginx-ocir --image=yny.ocir.io/cnxxxxxxxxgq/nginx:latest
    deployment.apps/nginx-ocir created
    kildong@cloudshell:~ (ap-chuncheon-1)$ kubectl get all
    NAME                                    READY   STATUS    RESTARTS   AGE
    pod/nginx-ocir-56d7b8d55c-65d22         1/1     Running   0          12s
    
    NAME                           TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)             AGE
    service/kubernetes             ClusterIP      10.96.0.1       <none>           443/TCP,12250/TCP   156m
    
    NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
    deployment.apps/nginx-ocir         1/1     1            1           12s
    
    NAME                                          DESIRED   CURRENT   READY   AGE
    replicaset.apps/nginx-ocir-56d7b8d55c         1         1         1       13s
    


이 글은 개인으로서, 개인의 시간을 할애하여 작성된 글입니다. 글의 내용에 오류가 있을 수 있으며, 글 속의 의견은 개인적인 의견입니다.

Last updated on 8 Nov 2021