본문 바로가기

Cloud/Oracle Cloud Infrastructure

OCI를 이용한 쿠버네티스, Wercker 쉬운 샘플 - 5. OCI 레지스트리에 이미지 업로드 & 다운로드 설정

도커 이미지를 보관은 앞서 살펴본 Docker Hub, 또는 Red Hat Quay.io 같은 퍼블릭 레지스트리 서비스를 이용하는 것이 편리하다. 그러나 네트웍 지연, 보안 등의 이슈로 인해 퍼블릭 레지스트리를 사용하지 못하는 경우도 있다. 한번 내려받은 도커 이미지는 로컬 호스트에 저장되지만, 클라우드 환경과 같이 서버의 생성과 파기가 자주 일어나는 환경에서는 이미지를 새로 내려받는 상황이 흔하게 발생할 있으며, 이는 배포 시간에 영향을 있는 상황이다. 또한 Docker Hub, Quay.io 비공개 레지스트리 기능이 있기는 하지만, 퍼블릭 레지스트리에 운영환경에 배포할 어플리케이션 이미지를 저장하는 것도 보안 측면에서는 불안한 요소가 있는 것도 사실이다.

이러한 이유로 별도의 도커 레지스트리를 구축하게 되는데, 인하우스 도커 레지스트리를 구성하는 방법은 도커를 설치할 레지스트리 컨테이너를 서비스로 등록해서 사용할 있다. 도커 레지스트리는 Docker Distribution 함께 제공된다.

OCI 환경에서는 OCI Registry(OCIR)라는 이름으로 도커 레지스트리를 서비스 형태로 제공한다.

OCI 레지스트리는 컨테이너 이미지를 저장하고 공유하기 위한 개방형 표준 기반의 오라클 관리 도커 레지스트리 서비스로서 도커 CLI API 사용하여 도커 이미지를 푸시하고 가져올 있다. OCI Container Engine for Kubernetes(OKE), Identity and Access Management(IAM), Visual Builder Studio, 타사 개발자 DevOps 도구와 함께 작동한다.

이번 테스트에서는 OCI 레지스트리에 연결하여 도커 이미지를 업로드 (Push) 것이다.

먼저 지금까지 테스트했던 kubectl 실행 호스트에 도커 명령을 실행할 도커 CLI 환경을 구성한다. 아래와 같이 OCI 콘솔의 Cloud Shell 환경을 이용해도 되지만, 여기서는 별도 도커 CLI 환경을 구성했다.

 

root 유저로 도커 엔진을 설치한다.

[opc@inst-public ~]$ sudo -s
[root@inst-public opc]# whoami
root
[root@inst-public opc]# yum install docker-engine -y
Loaded plugins: langpacks, ulninfo

Dependency Installed:
  container-selinux.noarch 2:2.107-3.el7                containerd.x86_64 0:1.3.9-2.el7         criu.x86_64 0:3.12-2.el7
  docker-cli.x86_64 0:19.03.11.ol-8.el7                 libnet.x86_64 0:1.1.6-7.el7             protobuf-c.x86_64 0:1.0.2-3.el7
  runc.x86_64 0:1.0.0-19.rc5.git4bb1fe4.0.4.el7
 
Complete!

 

도커 서비스 Enable Start.

[root@inst-public opc]# systemctl enable docker
Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
[root@inst-public opc]# systemctl start docker
[root@inst-public opc]# systemctl status docker.service
docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2021-03-02 12:10:08 GMT; 6s ago
     Docs: https://docs.docker.com
 Main PID: 8615 (dockerd)
    Tasks: 10
   Memory: 42.1M
   CGroup: /system.slice/docker.service
           └─8615 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
 
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.419518115Z" level=warning msg="Your kernel does not supp...weight"
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.419604169Z" level=warning msg="Your kernel does not supp...device"
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.419738433Z" level=info msg="Loading containers: start."
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.697883636Z" level=info msg="Default bridge (docker0) is ...ddress"
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.856326656Z" level=info msg="Loading containers: done."
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.867109885Z" level=warning msg="Not using native diff for...verlay2
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.867274498Z" level=info msg="Docker daemon" commit=f0aae7...3.11-ol
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.867384686Z" level=info msg="Daemon has completed initialization"
Mar 02 12:10:08 inst-public dockerd[8615]: time="2021-03-02T12:10:08.896632432Z" level=info msg="API listen on /var/run/docker.sock"
Mar 02 12:10:08 inst-public systemd[1]: Started Docker Application Container Engine.
Hint: Some lines were ellipsized, use -l to show in full.

 

도커 클라이언트, 서버가 정상적으로 설치되었는지 확인한다.

[root@inst-public opc]# docker -v
Docker version 19.03.11-ol, build f0aae77

 

root 다른 유저 (여기서는 opc) 도커를 사용할 있게 하려면, 아래와 같이 docker 그룹에 해당 유저를 추가한다. 로그 아웃 다시 로그인 하면 opc 유저로도 도커 명령이 수행되는 것을 확인할 있다.

[root@inst-public opc]# usermod -aG docker opc
[root@inst-public opc]# systemctl restart docker
[root@inst-public opc]# exit
exit
[opc@inst-public ~]$ exit
logout

[opc@inst-public ~]$ docker -v
Docker version 19.03.11-ol, build f0aae77

 

참고 사이트: 10. Log in to Oracle Cloud Infrastructure Registry

설치한 도커 CLI OCI 레지스트리에 접근하려면 OCI 레지스트리 접속 패스워드 기능을 하는 인증 토큰 (Auth Token) 필요하다. 인증 토큰은 OCI  콘솔에서 생성할  있다. OCI  콘솔 오른쪽 상단의 사용자 메뉴를 클릭하면 나오는 User Settings 화면으로 이동한다.

 

Auth Tokens 화면에서 “Generate Token” 클릭하면 나오는 팝업창에서 인증 토큰에 대한 설명을 입력하고 “Generate Token” 클릭한다. 여기서는 “demo auth token”이라고 입력했다.

 

인증 토큰의 값은 OCI 웹 콘솔에서 다시 조회가 되지 않기 때문에 생성된 인증 토큰을 즉시 복사하여 따로 저장해 둔다. 이후 OCI 레지스트리에 접속할  사용된다. 인증 토큰 복사  “Close” 클릭하여 팝업창을 닫는다.

 

먼저 테스트를 위해 로컬에 샘플 이미지를 다운로드(Pull)하고, 이를 확인한다. 아래 “docker (image) pull 리포지토리명(:태그)” 명령을 실행하면 원격 리포지토리로 부터 이미지를 다운로드한다.

[opc@inst-public ~]$ docker image pull nginx
Using default tag: latest
Trying to pull repository docker.io/library/nginx ...
latest: Pulling from docker.io/library/nginx
45b42c59be33: Pull complete
d0d9e9ea897e: Pull complete
66e650438339: Pull complete
76a3dfe4406b: Pull complete
410ff9d97480: Pull complete
Digest: sha256:8e10956422503824ebb599f37c26a90fe70541942687f70bbdb744530fc9eba4
Status: Downloaded newer image for nginx:latest
nginx:latest
[opc@inst-public ~]$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              298ec0e28760        5 days ago          133MB

 

이제 도커 CLI 명령 “docker login <region-key>.ocir.co”으로 OCI 레지스트리에 로그인한다. 또는 “docker login -u apackrsct01/oracleidentitycloudservice/young.kyun.jung@sampletest.com iad.ocir.io”  같이 -u 옵션으로 유저명을 명시해서 로그인할 수도 있다. OCI region-key 아래 문서를 참조한다. 여기서는 Ashburn Region 사용했다.

Availability by Region

- Username: <tenancy-namespace>/<username> 형식을 사용한다. 여기서 <tenancy-namespace> 자동으로 생성된 오브젝트 스토리지 네임스페이스로 OCI Registy 화면의 홈에 있는 이름이다.

- Password: 앞서 생성한 인증 토큰 값이다.

[opc@inst-public ~]$ docker login -u apackrsct01/oracleidentitycloudservice/young.kyun.jung@sampletest.com iad.ocir.io
Password: auth_token_value
WARNING! Your password will be stored unencrypted in /home/opc/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
 
Login Succeeded
[opc@inst-public ~]$

 

OCI 레지스트리에 업로드할 이미지에 태그를 붙인다. 여기서는 앞서 샘플로 다운로드한 Nginx 이미지에 태그를 붙였으며, 아래와 같이 이미지ID 이용했다. 태그 사용 문법은 다음과 같다.

- docker tag 이미지(:태그) <region-key>.ocir.io/<tenancy-namespace>/<repo-name>/<image-name>:<tag>

- repo-name: 리토티토리 이름. 리포지토리 이름은 선택사항으로 여기서처럼 생략한 경우, 이미지의 이름 ”ocir-sample-nginx” 리포지토리 이름으로 사용된다.

태깅 , docker images 명령을 실행해 보면 기존 Nginx 이미지와 동일한 이미지ID 이미지가 OCI 레지스트리에 생성되어 있음을   있다.

[opc@inst-public ~]$ docker tag nginx iad.ocir.io/apackrsct01/ocir-sample-nginx:1.0.demo
[opc@inst-public ~]$ docker images
REPOSITORY                                   TAG                 IMAGE ID            CREATED             SIZE
nginx                                       latest              35c43ace9216        13 days ago         133MB
iad.ocir.io/apackrsct01/ocir-sample-nginx   1.0.demo            35c43ace9216        13 days ago         133MB

 

이제 위에서 태그를 붙인 이미지를 “docker push” 명령으로 OCI 레지스트리에 업로드(Push) 한다.

[opc@inst-public ~]$ docker push iad.ocir.io/apackrsct01/ocir-sample-nginx:1.0.demo
The push refers to repository [iad.ocir.io/apackrsct01/ocir-sample-nginx]
2acf82036f38: Pushed
9f65d1d4c869: Pushed
0f804d36244d: Pushed
9b23c8e1e6f9: Pushed
ffd3d6313c9b: Pushed
9eb82f04c782: Pushed
1.0.demo: digest: sha256:b08ecc9f7997452ef24358f3e43b9c66888fadb31f3e5de22fec922975caa75a size: 1570

 

OCI  콘솔에서 Developer Services > Container Registry 화면으로 이동해 보면, root Compartment 새로운 리포지토리와 이미지가 생성되어 있음을 확인할  있다.

생성된 리포지토리에 대해 Readme 만들  있다. 리포지토리를 선택한 다음 나타나는 Readme 섹션 오른쪽의 “Edit this readme” 클릭하면 업로드한 이미지에 대한 Readme 작성할  있다. “Edit this readme” 클릭한다.

 

Edit Readme 팝업 창의 Edit탭에서 포맷을 Markdown으로 선택하고 Content 아래 내용을 붙여 넣는다.

# OCIR에 업로드한 샘플 Nginx 이미지입니다.
##  OCIR에 업로드한 샘플 Nginx 이미지입니다.
###  OCIR에 업로드한 샘플 Nginx 이미지입니다.
####  OCIR에 업로드한 샘플 Nginx 이미지입니다.
#####  OCIR에 업로드한 샘플 Nginx 이미지입니다.
######  OCIR에 업로드한 샘플 Nginx 이미지입니다.
 
이미지 파일도 링크할 수 있습니다.

 

Preview 탭에서는 미리보기도   있다. 확인 , 하단의 “Save” 클릭한다.

 

리포지토리의 이미지를 클릭하면 이미지 크기  이미지에 대한 상세 내용도 확인할  있다.

 

OCI 레지스트리에 업로드한 이미지에 대한 다운로드(Pull) 아래와 같이 docker pull 명령어로 수행하면 된다.

docker pull iad.ocir.io/apackrsct01/ocir-sample-nginx:1.0.demo

 

앞선 예제에서는 OCI 레지스트리에 도커 CLI 명령으로 로그인하여 이미지에 태그를 붙이고, 업로드  다운로드를 수행해 보았다.

이번에는 OCI 레지스트리에 도커 CLI 명령으로 수동으로 로그인하는 대신, 로그인 상세 정보를 담은 쿠버네티스 시크릿을 만들고 시크릿과 매니페스트 파일을 이용하여 선언 방식으로 Nginx 배포해본다.

 

먼저 쿠버네티스 시크릿 파일을 생성한다. 시크릿 생성 kubectl 명령 문법은 다음과 같다.

- kubectl create secret docker-registry <secretname> --docker-server=<region-key>.ocir.io --docker-username='<tenancy-namespace>/<oci-username>' --docker-password='<oci-auth-token>' --docker-email='<email-address>'

- secretname: 시크릿 이름으로 이후 매니페스트 파일에서 참조하게 된다. 여기서는 “ocirsecret” 이라는 이름을 사용했다.

- --docker-server: “<region-key>.ocir.io” OCI region-key Availability by Region 참조한다. 여기서는 Seoul Region 사용했다.

- --docker-username: <tenancy-namespace>/<username> 형식을 사용한다. 여기서 <tenancy-namespace> 자동으로 생성된 오브젝트 스토리지 네임스페이스로 OCI Registy 화면의 홈에 있는 이름이다.

- --docker-password: 앞서 생성한 해당 유저의 인증 토큰

- --docker-email: 이메일 주소. 어떤 이메일 주소를 사용해도 된다.

[opc@inst-public ~]$ kubectl create secret docker-registry ocirsecret --docker-server=iad.ocir.io --docker-username='apackrsct01/oracleidentitycloudservice/young.kyun.jung@sampletest.com' --docker-password='Q;:tK8nw1To;kjZZ(+Oo' --docker-email='young.kyun.jung@sampletest.com'
secret/ocirsecret created

 

kubectl get secrets 명령으로 생성된 시크릿을 확인할  있다.

[opc@inst-public ~]$ kubectl get secrets
NAME                  TYPE                                  DATA   AGE
default-token-ngvzw   kubernetes.io/service-account-token   3      25h
ocirsecret            kubernetes.io/dockerconfigjson        1      62s

 

참고로 시크릿을 생성할  올바른 정보로 만들지 않았을 경우, 향후 해당 시크릿을 이용하여 이미지/어플리케이션을 배포할  아래와 같이 OCI 레지스트리에서 이미지를  가져오는 에러를 만날  있다.

[opc@inst-public ~]$ kubectl get pods
NAME                               READY   STATUS             RESTARTS   AGE
NAME                               READY   STATUS             RESTARTS   AGE
nginx-deployment-786c69578-2vzdp   0/1     ImagePullBackOff   0          2m31s
nginx-deployment-786c69578-746st   0/1     ImagePullBackOff   0          2m32s
nginx-deployment-786c69578-brcd9   0/1     ImagePullBackOff   0          2m31s
 
[opc@inst-public ~]$ kubectl describe pods nginx-deployment-786c69578-2vzdp
...
Normal   Pulling    2m5s (x4 over 3m45s)  kubelet            Pulling image "icn.ocir.io/cndcymqut2ej/ocir-sample-nginx:1.0.demo"
  Warning  Failed     2m2s (x4 over 3m43s)  kubelet            Failed to pull image "icn.ocir.io/cndcymqut2ej/ocir-sample-nginx:1.0.demo": rpc error: code = Unknown desc = pull access denied for icn.ocir.io/cndcymqut2ej/ocir-sample-nginx, repository does not exist or may require 'docker login': denied: Anonymous users are only allowed read access on public repos
  Warning  Failed     2m2s (x4 over 3m43s)  kubelet            Error: ErrImagePull
  Normal   BackOff    109s (x6 over 3m43s)  kubelet            Back-off pulling image "icn.ocir.io/cndcymqut2ej/ocir-sample-nginx:1.0.demo"
  Warning  Failed     94s (x7 over 3m43s)   kubelet            Error: ImagePullBackOff

 

이제 클러스터에 어플리케이션을 배포할  쿠버네티스가 사용할 매니페스트 파일을 만들고, 매니페스트 파일 내에 시크릿 이름을 포함시켜 연결시킬 차례이다. 매니페스트 파일에는 OCI 레지스트리의 도커 이미지 경로도 포함시킬 것이다.

앞선 디플로이먼트 테스트에서는 디플로이를 먼저 하고, “kubectl explose deployment …” 명령으로 서비스를 생성했었다. 여기서는 디플로이먼트와 서비스가 아래 매니페스트 파일에 함께 정의되어 있다. YAML파일 하단의 서비스를 정의한 부분의 spec selector 속성값과 디플로이먼트 부분의 template metadata.label 속성값이 “app: nginx” 연결되어 있어서 디플로이가 이루어질  연결된 로드밸런서 타입의 서비스도 생성될 것이다.

YAML 파일에서 spec.containers image 대한 값은 OCI 레지스트리에 이미지를 업로드했을  지정한 경로를 다음 포맷으로 입력한다. 여기서는 “iad.ocir.io/apackrsct01/ocir-sample-nginx:1.0.demo” 으로 입력했다.

- image: <region-key>.ocir.io/<tenancy-namespace>/<repo-name>/<image-name>:<tag>

- repo-name: 리토티토리 이름. 리포지토리 이름은 선택사항으로 여기서처럼 생략한 경우, 이미지의 이름이 리포지토리 이름으로 사용된다.

YAML 파일에서 spec.imagePullSecrets name 대한 값은 앞서 만든 시크릿 이름을 입력한다.

여기서는 “nginx-lb.yaml” 이라는 이름으로 매니페스트 파일을 만들었다.

[opc@inst-public ~]$ vi ~/nginx-lb.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: iad.ocir.io/apackrsct01/ocir-sample-nginx:1.0.demo
        ports:
        - containerPort: 80
      imagePullSecrets:
      - name: ocirsecret
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  type: LoadBalancer
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx

[opc@inst-public ~]$

 

이제 이미지를 배포한다. 여기서는 kubectl create 명령으로 배포를 한다. 만일 동일한 이름의 오브젝트가 이미 생성되어 있으면 때문에 kubectl apply 명령을 실행했을 때와는 달리 에러를 리턴할 것이다.

[opc@inst-public ~]$ kubectl create -f ~/nginx-lb.yaml
deployment.apps/nginx-deployment created
service/nginx-service created

 

디플로이먼트와 파드를 확인한다. 디플로이가 되어 세개의 레플리카가 만들어저 있음을   있다. 서비스도 생성되어 있으며, 로드밸런서 타입으로 EXTERNAL-IP 할당되어 있음을   있다. 여기서 EXTERNAL-IP OCI 로드밸런서의 퍼블릭IP 주소이다.

[opc@inst-public ~]$ kubectl get deploy,po,svc
NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-deployment   3/3     3            3           75s
 
NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-5db6dd996d-9472s   1/1     Running   0          74s
pod/nginx-deployment-5db6dd996d-pbtd6   1/1     Running   0          74s
pod/nginx-deployment-5db6dd996d-xxkb5   1/1     Running   0          75s
 
NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)          AGE
service/kubernetes      ClusterIP      10.96.0.1      <none>            443/TCP          25h
service/nginx-service   LoadBalancer   10.96.228.77   193.122.195.128   8080:32250/TCP   75s

 

OCI  콘솔에  보면 퍼블릭 로드밸런서가 생성되어 있고, 위에서 조회된 퍼블릭IP 할당되어 있음을   있다.

 

 브라우저를 열어서 “http://로드밸런서_퍼블릭IP:호스트포트 접속해보면 Nginx 웰컴페이지를   있다.

 

다음 테스트를 위해 앞서 만든 서비스와 디플로이먼트를 삭제한다. 서비스를 삭제하면 OCI 로드밸런서도 함께 삭제된다.

[opc@inst-public ~]$ kubectl delete service nginx-service
service "nginx-service" deleted
[opc@inst-public ~]$ kubectl delete deployments nginx-deployment
deployment.apps "nginx-deployment" deleted
[opc@inst-public ~]$ kubectl delete secret ocirsecret
secret "ocirsecret" deleted
[opc@inst-public ~]$ kubectl get deploy,po
No resources found in default namespace.

 

글 순서

1. Introduction

2.쿠버네티스  OCI 쿠버네티스(OKE) 개요

3. OCI 쿠버네티스 배포, 사용 환경 설정

4. OKE 클러스터에 샘플 웹서버 배포

5. OCI 레지스트리에 이미지 업로드 & 다운로드 설정

6. Wercker 이용한 어플리케이션 빌드

7. Wercker 이용한 어플리케이션 배포

8. OKE 모니터링

여기에 정리한 내용은 오라클 제품을 다루고 있지만, 이는 개인적인 정리 및 테스트 결과일 뿐입니다. 오라클의 공식 문서는 오라클이 제공하는 매뉴얼과 기타 기술문서를 참조하셔야 합니다.