도커 이미지를 보관은 앞서 살펴본 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을 사용했다.
- 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. |
글 순서
5. OCI 레지스트리에 이미지 업로드 & 다운로드 설정
여기에 정리한 내용은 오라클 제품을 다루고 있지만, 이는 개인적인 정리 및 테스트 결과일 뿐입니다. 오라클의 공식 문서는 오라클이 제공하는 매뉴얼과 기타 기술문서를 참조하셔야 합니다.
'Cloud > Oracle Cloud Infrastructure' 카테고리의 다른 글
OCI를 이용한 쿠버네티스, Wercker 쉬운 샘플 - 7. Wercker를 이용한 어플리케이션 배포 (0) | 2021.03.22 |
---|---|
OCI를 이용한 쿠버네티스, Wercker 쉬운 샘플 - 6. Wercker를 이용한 어플리케이션 빌드 (0) | 2021.03.22 |
OCI를 이용한 쿠버네티스, Wercker 쉬운 샘플 - 4. OKE 클러스터에 샘플 웹서버 배포 (0) | 2021.03.17 |
OCI를 이용한 쿠버네티스, Wercker 쉬운 샘플 - 3. OCI 쿠버네티스 배포, 사용 환경 설정 (0) | 2021.03.17 |
OCI를 이용한 쿠버네티스, Wercker 쉬운 샘플 - 2.쿠버네티스 및 OCI 쿠버네티스(OKE) 개요 (0) | 2021.03.17 |