先日、クラウド環境の勉強と開発環境のセットアップを兼ねてKubernetesのセットアップを行いました。
クラウド環境の勉強と開発環境のセットアップを兼ねてKubernetesをインストール&セットアップします。 dockerをコンテナに使った情報ばかり出回ってますが、この記事では次世代を担うと思われる(個人的に)CRI-Oをコンテナ:Kubernetes + CRI-O インストール
docker.ioや、gcr.ioにあるイメージで遊ぶのも楽しいのですが、サービスを開発するためにイメージのローカルレジストリが欲しくなりました。
ローカルレジストリにociイメージ(open container initiative image)を保存していつでも引き出せるようにしてみます。
僕の環境はdockerが入ってないdockerless環境なので、dockerを入れずになんとかしたいと思います。
- 現在の環境
- OCIイメージ作成ツール:buildah
- buildahによるイメージ作成
- kubernetes上にローカルレジストリサービス作成
- lxd上のfedoraでbuildahによるscrachからのイメージ作成
- おまけ: ローカルレジストリのイメージの削除
- まとめ
現在の環境
Kubernetes: v1.18.3
コンテナランタイム: crio v1.17.4
cni: calico v3.14
os: ubuntu 20.04
また、開発環境向けコンテナとしてlxdも使えるようになっています。
(この記事の後半のfedora環境でのイメージ作成に使います。)
lxd環境構築に関しては以下を参照してください。
コンテナ環境というと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でのコンテナの立ち上げは前の記事を参照してください。
コンテナ環境というと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!