지돌이의 블로그 입니다!

Kubernetes Offline 사용

개요

Kubernetes(이하 K8s)를 인터넷과 연결되지 않은 상태(이하 Offline 상태)에서 정상적으로 동작시킬 수 있도록 한다. K8s는 기본적으로 Docker Image를 인터넷 상에 있는 Docker Registry에서 받아오게 된다. 잘 알려진 Docker Registry는 docker.io, quay.io, k8s.gcr.io 등이 있다. Offline 상태에서는 Docker Image를 Online에 있는 Docker Registry에서 받아올 수 없기 때문에 필요한 이미지를 Offline상에 가지고 있어야 한다. 이를 위해 필요한 사항들을 정리한다.

고려해야 할 부분들

1. K8s 설치

여기서는 K8s 설치를 microk8s를 통한 설치를 사용한다. Ubuntu에서 microk8s는 snap으로 설치할 수 있다. snap은 어플리케이션을 패키징화 하는 것으로 추가적인 dependency없이 즉시 설치 가능하게 한다. microk8s을 오프라인 설치하기 위해 snap을 미리 다운받아야 한다.

2. K8s 내부 동작

K8s는 Pod의 Container를 동작시키기 위한 Docker Image뿐만 아니라 sandbox image 가 필요하다. 이에 대해서는 159. [Kubernetes] Pause 컨테이너의 역할과 원리 (원문: The Almighty Pause Container) 을 참고하길 바란다. 간단히 설명하면 sandbox container는 Pod의 부모 컨테이너로써 하나의 Pod의 Container들을 관리하고 동일한 namespace를 공유할 수 있게 해 주는 K8s의 필수적인 컨테이너이다.

sandbox image 의 위치는 /var/snap/microk8s/current/args/containerd.toml/var/snap/microk8s/current/args/containerd-template.toml 에서 plugins -> plugins.cri -> sandbox_image 에서 위치를 지정할 수 있다. 기본 위치는 k8s.gcr.io/pause:3.1 이다.

(해당 containerd argument는 clustered 서버 구성에서도 자기 자신의 node에만 적용된다.)

Offline 상태에서 K8s을 동작시키기 위해서는 해당 이미지 또한 Offline 상에 저장되어 있어야 한다.

3. Helm

$ microk8s.enable helm 명령을 통해 helm을 쉽게 설치할 수 있다. microk8s.enable 명령은 내부적으로 /snap/microk8s/current/actions/enable.helm.sh을 실행시킨다. 해당 파일은 https://get.helm.sh/helm-$HELM_VERSION-linux-$(arch).tar.gz 을 다운받아 실행한다. 하지만 Offline인 경우에 해당 파일을 다운받을 수 없을 것이다. 따라서 microk8s에 helm을 설치하려면 이를 미리 다운받아 놓아야 한다.

뿐만 아니라 helm을 설치하는 과정에서 _tiller_이미지를 Online에서 다운받게 된다.

4. 사용자가 구성하는 Docker Image

microk8s에서는 microk8s.enable 명령을 통해 local registry를 설치할 수 있다. 이렇게 설치한 local registry의 주소에서 Docker Image를 가져오게 하면 문제되지 않는다.

고려해야 할 것들 총정리...

  1. microk8s 설치
  2. sandbox image (pause 이미지)
  3. microk8s.enable시 사용되는 shell script & helm & tiller
  4. 사용자 docker image들

오프라인 설치 방법

1. microk8s 설치

snap을 오프라인으로 설치하는 방법은 아래와 같다.

Online 상태의 Ubuntu Machine에서 패키지 다운로드

$ snap download microk8s
# 이후 microk8s_xxxx.snap, microk8s_xxxx.assert 두개의 파일이 생성된다.

Offline 상태의 Ubuntu Machine에서 패키지 설치

$ sudo snap ack microk8s_xxxx.assert
$ sudo snap install microk8s_xxxx.snap --classic

2.1. Docker Image

k8s 노드가 한개일 경우에는 야매로 containerd에 이미지를 저장시켜 놓을 수 있다. 그런데 containerd v1.2.x 버전(v1.3.x 이전)에서는 image export/import에 버그(#2862 참고)가 있다... 일단 기본적으로..

$ sudo microk8s.ctr image pull k8s.gcr.io/pause:3.1
k8s.gcr.io/pause:3.1:                                                             resolved
...
elapsed: 2.2 s                                                                    total:  3.7 Ki (1.7 KiB/s)
unpacking linux/amd64 sha256:f78411e19d84a252e53bff71a4407a5686c46983a2c2eeed83929b888179acea...
done

$ sudo microk8s.ctr image export pause.tar k8s.gcr.io/pause:3.1
ctr: export failed: content digest sha256:c84b0a3a07b628bc4d62e5047d0f8dff80f7c00979e1e28a821a033ecda8fe53: not found (헐?)

이렇게 오류가 나는데 이를 해결하기 위해서는 image pull 에 --all-platforms 옵션을 줘서 모든 platform (i386/arm64/arm/arm64/ppc... * linux/windows)의 이미지를 한번에 받아야 하는데 그러면 이미지 크기가 어머어마 하다... 여러 Platform에서 k8s을 동작시킨다면 --all-platforms이 필요하겠지만 그렇지 않다면 특정 platform만 export할 수 있어야 하는데 그럴려면 containerd 1.3.x 을 사용해야 한다.

$ sudo apt install containerd

ubuntu 18.04에서는 기본적으로 apt-get으로 containerd를 설치하면 1.3.3 버전이 설치된다.

$ sudo ctr -a /var/snap/microk8s/common/run/containerd.sock image pull k8s.gcr.io/pause:3.1
$ sudo ctr -a /var/snap/microk8s/common/run/containerd.sock image export --platform linux/amd64 pause.tar k8s.gcr.io/pause:3.1

이렇게 하면 linux/amd64용으로 pause이미지가 export된다.

$ sudo ctr -a /var/snap/microk8s/common/run/containerd.sock image import --base-name k8s.gcr.io/pause pause.tar

이렇게 임포트!

2.2. Docker Registry 사용하기

하지만 위 방법은 노드가 2개 이상이면 골치아파 진다. 모든 노드에 이미지를 저장시켜놓아야 하기 때문이다. 따라서 registry를 구축해놓고 해당 registry에 필요한 이미지들을 모아놓는 것이 좋다.

microk8s에는 local registry을 간편하게 설치할 수 있다. microk8s.enable registry 하면 끝이다! 하지만 여기에는 큰 문제가 하나 있다...

  1. 닭이 먼저냐 달걀이 먼저냐...

    local registry를 돌릴려면 pause 이미지와 registry 이미지가 필요하다.. 근데 이건 어디서 받아오냐...? 클러스터링해서 ceph을 쓰려면 ceph이미지도 필요한데 ceph이미지는 또 어디서 받아오고...?
    물론.. hostPath를 이용한다면 pause이미지와 registry이미지만 먼저 containerd에다가 저장시켜놓고 해당 노드 주소를 이용해서 registry를 이용하면 된다.

  2. 그런데 해당 노드가 죽으면...? 클러스터의 중요한 장점인 HA(고가용성)이 떨어진다..

  3. 근데 또... ctr에 버그가 있어...ㅠㅠ 이어서 설명함..

  4. 그러고 보니 docker 리포 주소를 다 바꾸는 수고가 엄청난데...? 이것도 이어서...

(3. ctr에 버그가 있어... - 1)

일단 registry만 Kubernetes밖에 저장한다 치고, 위에서 export한 도커 이미지를 registry에 다시 부어야 한다.

한가지 예시이다.

$ ctr -a /var/snap/microk8s/common/run/containerd.sock image push 127.0.0.1:32000/quay.io/cephcsi/cephcsi:v1.2.2 quay.io/cephcsi/cephcsi:v1.2.2
...
elapsed: 2.3 s                                                                    total:  302.8  (131.6 MiB/s)
ctr: failed commit on ref "manifest-sha256:fd16e128b5831a4c73e57181b890445c4eb73726f5210ebc8e408855f99912dd": unexpected status: 400 Bad Request

이건 뭘까... (모든 이미지가 위 오류가 나진 않는다. 잘 되는 이미지만 테스트 해보고 offline지역으로 이동해서 이런 이미지를 push 하려고 하면 대략난감... => 물론 나는 이러한 대략난감을 경험했다.. 화가난다.. containerd가 미워진다.. 근데 containerd만큼 잘 만든것도 없고 containerd를 안쓰면 k8s못쓴다..ㅎㅎ)

위 tar 파일을 풀어보면 index.json이 나오는데 manifest파일의 mediaType이 application/vnd.oci.image.manifest.v1+json 인것들이 그렇다. 일단... v2에 맞게 일일히 수정해주면 되긴 하는데.. 노가다이다. (난 nodejs로 짜서 변환했는데... 그래도 이건 아니다 싶었다.. v2.list로 바꾸고 리스트 파일 만들고 blobs에 hash이름으로 추가하고 이렇고 저렇고... ㅠㅠv2.list로 바꾸고 리스트 파일 만들고 blobs에 hash이름으로 추가하고 이렇고 저렇고...ㅠㅠ)

근데 나중에 알아서 이 방법을 썼는데...

$ sudo docker image load < cephcsi_v1.2.0.tar
$ sudo docker tag quay.io/cephcsi/cephcsi:v1.2.0 127.0.0.1:32000/cephcsi/cephcsi:v1.2.0
$ sudo docker push 127.0.0.1:32000/cephcsi/cephcsi:v1.2.0

이렇게 하면 된다... (모든 이미지가 이 방법이 먹히는건 아니다.. 휴...)

(3. ctr에 버그가 있어... - 2)

multi-platform 이미지를 자기 platform 것만 pull받은 경우 push가 되지 않는다. (export -> import 또는 pull) -> push 과정이 안되는 것이다.

#3915 PR에서 해결된거 같은데.. master에 merge되고 아직 release는 안된듯...ㅠㅠ

이건 우째야 할지 모르겠다... 하지만 위 방법처럼 그냥 docker를 통해서는 되는거 같다.

(4. 그러고 보니 docker 리포 주소를 다 바꾸는 수고가 엄청난데...?)

그렇다... 일단 다시 containerd.toml 파일을 보면 중간이 이런 부분이 있다.

    [plugins.cri.registry]
      [plugins.cri.registry.mirrors]
        [plugins.cri.registry.mirrors."docker.io"]
          endpoint = ["https://registry-1.docker.io"]
        [plugins.cri.registry.mirrors."localhost:32000"]
          endpoint = ["http://localhost:32000"]

registry 주소를 override할 수 있는 부분인데 유용하게 사용할 수 있다.

예를 들어 docker.io(docker hub)에 저장되는 이미지들을 로컬에서 받아오고 싶으면

    [plugins.cri.registry]
      [plugins.cri.registry.mirrors]
        [plugins.cri.registry.mirrors."docker.io"]
          endpoint = ["http://10.1.2.3:32000"]

이런식으로 하면 alpine:1.23.4 => 10.1.2.3:32000/library/alpine:1.23.4 에서 받아오게 된다. 이런식으로 override하면 image 를 일일히 바꿔주는 수고는 안해도 된다. (물론 node가 여러개면 여러개 각각 수정해줘야 한다...ㅠㅠ k8s전체 적용하는 방법 있음 알려주오...ㅠㅠ)

위에서 보았던 _sandbox_image_또한 local registry주소로 바꾸는걸 잊지 말길!

2.3. 더 좋은 방법 없을까?

thirdparty들을 쓰다 보면 quay.io, docker.io, k8s.gcr.io 등... 여러 registry를 쓰게 되며 이름이 중복될 가능성도 있고.. registry를 로컬에다 설치해야 하는데 registry들이 다 docker용으로 만들어져 있다...

기쁜 소식은 containerd.toml에서 [plugins.cri.registry.mirrors."*"] 이러한 문법이 동작한다는 것이다!

근데 그럼 뭐해.. 필요한 이미지를 로컬에다 저장하고 이미지를 검색하는 기능을 가진 서버가 없는데...

따라서 그건 내가 만들테니... 조금만 기다리길...ㅠㅠ

Comment +1