Dream Driven Development。夢を形にしよう!

Kubernetesの ローカルイメージレジストリを dockerlessで作る

Kubernetesの
ローカルイメージレジストリを
dockerlessで作る


先日、クラウド環境の勉強と開発環境のセットアップを兼ねてKubernetesのセットアップを行いました。

Kubernetes + CRI-O
インストール-Dream Driven Development(夢駆動開発)
Kubernetes + CRI-O インストール-Dream Driven Development(夢駆動開発)
クラウド環境の勉強と開発環境のセットアップを兼ねてKubernetesをインストール&セットアップします。 dockerをコンテナに使った情報ばかり出回ってますが、この記事では次世代を担うと思われる(個人的に)CRI-Oをコンテナ:Kubernetes + CRI-O インストール

docker.ioや、gcr.ioにあるイメージで遊ぶのも楽しいのですが、サービスを開発するためにイメージのローカルレジストリが欲しくなりました。
ローカルレジストリにociイメージ(open container initiative image)を保存していつでも引き出せるようにしてみます。

僕の環境はdockerが入ってないdockerless環境なので、dockerを入れずになんとかしたいと思います。

現在の環境

Kubernetes: v1.18.3
コンテナランタイム: crio v1.17.4
cni: calico v3.14
os: ubuntu 20.04
また、開発環境向けコンテナとしてlxdも使えるようになっています。
(この記事の後半のfedora環境でのイメージ作成に使います。)
lxd環境構築に関しては以下を参照してください。

LXDコンテナによる
開発環境セットアップ-Dream Driven Development(夢駆動開発)
LXDコンテナによる 開発環境セットアップ-Dream Driven Development(夢駆動開発)
コンテナ環境というとkubernetesやdockerばかりが取り上げられますが、開発環境という意味では向いていません。 LXD/LXCは「システムコンテナ」であり、docker等のような「アプリケーションコンテナ」とは異なります。 :LXDコンテナによる 開発環境セットアップ

OCIイメージ作成ツール:buildah

イメージ作成ツールとしてはdocker以外で考えるとkanikoやbuildahがあります。
kanikoはdockerfile等を使うっぽいので却下。今回はbuildahを使うことにします。
(buildahは普通にビルダーと読むようですが、作者になまりがあるからerじゃなくahになったとか笑)

buildahのインストール

aptでインストール後、動いているか確認します。

$ sudo apt install buildah
$ buildah images
REPOSITORY   TAG   IMAGE ID   CREATED   SIZE
$ buildah containers
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME                       CONTAINER NAME

buildahによるイメージ作成

スクラッチから

空っぽのイメージに対して、自分のアプリを追加してイメージを作成する方法です。

$ buildah from scratch
working-container
$ buildah containers #コンテナが出来上がっていることを確認
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME                       CONTAINER NAME
e32fdcc591b3     *                  scratch                          working-container
$ buildah run working-container bash #=> コンテナのbashを立ち上げようとしてもエラー。

scratchで作成したコンテナはbashすら入っていないのでbuildah runコマンドでbash指定してもエラーで動きません。
そこで、このイメージをホストにmountしてインストールするという方法をとります。
(mountコマンド部分が長いですが、buildah unshareしないと動作しなかったのでこのようになっています)

$ scratchmnt=$(buildah unshare -- sh -c "buildah mount $newcontainer")
$ echo $scratchmnt
/var/lib/containers/storage/overlay/70a7f159a9bf000991f91f266c7e53fb2897a30632ec1e5abb2e1de16e58fed6/merged

マウント先が長いので$scratchmntに値をいれています。
この/var/lib/containers/storage/...配下にいろいろなパッケージをインストールすることになります。
ただ、Ubuntuのaptコマンドはインストール先を指定できないので、ダウンロードだけしてdpkgで場所指定してインストールとかいう面倒なことになります。
そこで、おすすめの方法としてはlxdにfedora環境を作ってdnfコマンドで場所指定インストールする方法です。
(この記事の後ろの方にやり方を書いておきます。)

別のlinuxイメージから

$ buildah from ubuntu
Getting image source signatures
Copying blob 97058a342707 done
:
Writing manifest to image destination
Storing signatures
$ buildah containers
CONTAINER ID  BUILDER  IMAGE ID     IMAGE NAME                       CONTAINER NAME
9782bede2813     *     adafef2e596e docker.io/library/ubuntu:latest  ubuntu-working-container

こちらの方法の場合、通常のlinuxコンテナなので、アプリの追加も楽です。

方法1:コンテナ立ち上げて中に入ってインストール

$ buildah run ubuntu-working-container bash

方法1: buildahコマンド実行でインストール

$ buildah run ubuntu-working-container apt update
$ buildah run ubuntu-working-container apt install -y wget

このやり方でどんどんアプリ追加するとすぐにイメージが肥大化します。
dockerやcri-oなどの「アプリケーション(プロセス)コンテナ」は、「アプリを動かす最低限の環境を作る」ことを意識しましょう。
lxdのような「システムコンテナ」(コンテナにログインして作業することを前提としている)の場合は、好き放題インストールして良いです。

コンテナエントリーポイントの作成

アプリのインストールが終わったらコンテナのエントリーポイント(コンテナ起動時に起動するコマンド)を作成します。

$ buildah config --entrypoint "/usr/bin/myapp" ubuntu-working-container

イメージの作成

作成し終わったコンテナをイメージに変換します。

$ buildah commit ubuntu-working-container myapp
$ buildah inspect --type=image myapp  #内容チェック

kubernetes上にローカルレジストリサービス作成

さて、buildahでイメージの作成が終わりましたが、これだけではkubernetesからイメージをpullすることができません。
イメージのリポジトリ管理を行うローカルレジストリを作成する必要があります。
ここでは、少しdockerの力を借りましょう。docker.io/registryイメージを使ってレジストリサービスをkubernetes上に立ち上げます。

Persistent Volume(pv), Persistent Volume Claim(pvc)の作成

レジストリサービスが止まってもイメージデータが残るようにpv,pvcを作成します。
今回は手軽にマスターノードからのnfsを利用してpv,pvcを作成します。

マスターノード(nfs提供側)

$ sudo apt install nfs-kernel-server
$ sudo mkdir -p /var/nfs/exports
$ sudo mkdir /var/nfs/exports/registry # local registry用
$ sudo chown nobody.nogroup /var/nfs/exports/registry
$ echo "/var/nfs/exports <ローカルドメイン 192.168.0.0/24>(rw,sync,no_root_squash) 127.0.0.1/32(rw,sync,no_root_squash)" | sudo tee -a /etc/exports
$ sudo systemctl restart nfs-server rpcbind
$ sudo systemctl enable nfs-server rpcbind

ワーカーノード全部(nfs client側)

$ sudo apt install -y nfs-common;	

kubernetesでのpv,pvc作成

以下のマニフェストファイル(persistentvolume.yaml)を作成してpv,pvcを作成します。

apiVersion: v1
kind: PersistentVolume
metadata:
    name: my-pv
    labels:
        k8s-app: kubernetes-registry
spec:
    capacity:
        storage: 50Gi
    accessModes:
        - ReadWriteOnce
    nfs:
        path: /var/nfs/exports/registry
        server: <master node IP>
    persistentVolumeReclaimPolicy: Retain
    storageClassName: sc-registry

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
    name: pvc-registry
    namespace: kubernetes-registry
    labels:
        k8s-app: kubernetes-registry
spec:
    accessModes:
        - ReadWriteOnce
    resources:
        requests:
            storage: 50Gi
    storageClassName: sc-registry

kubectlで立ち上げます。

$ kubectl create ns kubernetes-registry
$ kubectl apply -f persistentvolume.yaml

確認します。

$ kubectl get pv
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                              STORAGECLASS   REASON   AGE
my-pv   50Gi       RWO            Retain           Bound    kubernetes-registry/pvc-registry   sc-registry             10s
$ kubectl get pvc -n kubernetes-registry
NAME           STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
pvc-registry   Bound    my-pv    50Gi       RWO            sc-registry    31s

ローカルレジストリサービスの立ち上げ

レジストリサービスの立ち上げようのマニフェストを準備します。(registry-service.yaml)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: kubernetes-registry
  namespace: kubernetes-registry
  labels:
    k8s-app: kubernetes-registry
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: kubernetes-registry
  template:
    metadata:
      labels:
        k8s-app: kubernetes-registry
    spec:
      containers:
        - name: kubernetes-registry
          image: docker.io/registry:latest
          env:
            - name: REGISTRY_HTTP_ADDR
              value: ":5000"
            - name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
              value: "/var/lib/registry"
            - name: REGISTRY_STORAGE_DELETE_ENABLED
              value: "true"
          ports:
          - name: http
            containerPort: 5000
          volumeMounts:
          - name: vol-registry
            mountPath: "/var/lib/registry"
      volumes:
        - name: vol-registry
          persistentVolumeClaim:
            claimName: pvc-registry

---

apiVersion: v1
kind: Service
metadata:
  name: kubernetes-registry
  namespace: kubernetes-registry
  labels:
    k8s-app: kubernetes-registry
spec:
  type: NodePort
  ports:
  - name: http
    port: 5000
    targetPort: 5000
  selector:
    k8s-app: kubernetes-registry
  externalIPs:
      - <External IP>

deployment/service pod立ち上げを行います。

$ kubectl apply -f registry-service.yaml

ここで立ち上げたレジストリサービスはhttp(insecure)なサービスです。httpsにするためには証明書などいろいろ面倒なのでlocal環境だということで割り切ってhttpのまま進めます。

buildahからローカルレジストリへのimageのpush

https(tls)では無いことをオプションで示してpushする必要があります。

$ buildah --tls-verify=false push localhost/ubuntu-working-container docker://<loal registry ip>:5000/heisaku/miniubu:latest
Getting image source signatures
Copying blob cf0f3facc4a3 done]
:

ローカルレジストリからkubernetesへのpull

http(insecure)レジストリから持ってくる場合、insecureを示すコンフィグが必要となります。
(正しく設定しないとerrorとしてserver gave HTTP response to HTTPS clientがでます)

以下の両方のファイルへの変更が必要(cri-oなので)(全node(master/worker))
/etc/containers/registries.conf #cri-oじゃない場合はこちらだけ?
 [registries.insecure]
 registries = ['<local registry ip>:5000']
/etc/crio/crio.conf
insecure_registries = [
  "<local registry ip>:5000"
]

作成したイメージをローカルレジストリから読み込んでみます。

 $ kubectl create deployment myapp --image=<local registry ip>:5000/heisaku/miniubu

lxd上のfedoraでbuildahによるscrachからのイメージ作成

lxdでのコンテナの立ち上げは前の記事を参照してください。

LXDコンテナによる
開発環境セットアップ-Dream Driven Development(夢駆動開発)
LXDコンテナによる 開発環境セットアップ-Dream Driven Development(夢駆動開発)
コンテナ環境というとkubernetesやdockerばかりが取り上げられますが、開発環境という意味では向いていません。 LXD/LXCは「システムコンテナ」であり、docker等のような「アプリケーションコンテナ」とは異なります。 :LXDコンテナによる 開発環境セットアップ

今回はimages:fedora/32/cloudを用いました。
コンテナにログインした後からの作業は以下のようになります。

 [fedora ~]$ sudo dnf -y install buildah

buildahのインストールが終わったらubuntuと同様にscratchイメージを作成します。

$ newcontainer=$(buildah from scratch)
$ scratchmnt=$(buildah unshare -- sh -c "buildah mount $newcontainer")

fedoraのdnfはインストール先のrootを変更できるのでパッケージをマウントしたディレクトリ($scratchmnt)にインストールすることができます。

dnf -y install --installroot $mnt --releasever 32 bash coreutils --setopt install_weak_deps=false

後は、エントリポイントの作成など、ubuntuと同じ操作になります。

buildah copy $newcontainer ./myapp.sh /usr/bin
buildah unshare --sh -c "buildah unmount $newcontainer"
buildah config --cmd /usr/bin/myapp.sh $newcontainer
buildah commit $newcontainer myapp
buildah --tls-verify=false push myapp docker://<local registry ip>:5000/heisaku/myapp:latest

おまけ: ローカルレジストリのイメージの削除

ローカルレジストリからイメージを消したい場合に、リポジトリの管理idであるDocker-Content-Digestというものが必要になります。
まずはこのDocker-Content-Digestを取り出すところからです。

$ curl <local registry IP>:5000/v2/_catalog  #入っているリポジトリチェック
$ curl <local registry IP>:5000/v2/<リポジトリ名>/tags/list #タグ名チェック
$ curl -i -H "Accept: application/vnd.oci.image.manifest.v1+json" <local registry IP>:5000/v2/<リポジトリ名>/manifests/<タグ名>
HTTP/1.1 200 OK
Content-Length: 975
Content-Type: application/vnd.oci.image.manifest.v1+json
Docker-Content-Digest: sha256:b9ec7f270714b78bbb6c848af7ace3aa8e61a1b90e69442e9e1ee963a47500f1
Docker-Distribution-Api-Version: registry/2.0
Etag: "sha256:b9ec7f270714b78bbb6c848af7ace3aa8e61a1b90e69442e9e1ee963a47500f1"
X-Content-Type-Options: nosniff
Date: Fri, 24 Jul 2020 13:01:33 GMT
:

この出力のDocker-Content-Digestを使います

$ curl -X DELETE <local repositry IP>:5000/v2/<リポジトリ名>/manifests/sha256:b9ec7f270714b7.....

これでリンクは消えますが実際のファイルの実体はガーベージコレクションされるまで消えません。
無理やり実行させましょう。
レジストリのpodにgarbage-collectを実行させます。

$ kubectl get po -n kubernetes-registry
NAME                                  READY   STATUS    RESTARTS   AGE
kubernetes-registry-868cc4bb4-n58d6   1/1     Running   0          22m
$ kubectl -n kubernetes-registry exec -it kubernetes-registry-868cc4bb4-n58d6 -- bin/registry garbage-collect /etc/docker/registry/config.yml
0 blobs marked, 7 blobs and 0 manifests eligible for deletion
blob eligible for deletion: sha256:041d5e62ac8a9b43b052e5a8aa6b0d349b6bf5d12e0ab0a0c6a2ceca78bf4214
:

まとめ

k8sで最初に立ち上げた自分用のサービスがこのローカルレジストリサービスです。
開発環境のためにサービスを立ち上げるというよくわからない状況ですが、一つ一つこなしていくと理解が深まってくると思います。

dockerlessというこだわりが無ければもっとスムーズだと思いますよ。

enjoy!

|

お気に入りサイト