zaki work log

作業ログやら生活ログやらなんやら

オフライン環境へのK3sインストール

いろんなものを業務でオフライン動作させてきた身としては一度はやっておきたかったインターネット接続の無いオフライン環境へのインストールお試し。
K3sは軽量Kubernetesとして様々な環境で動作するのが特徴の一つで、インターネット接続の無い環境でもAir-Gap Installとしてドキュメントに手順が載っているので、比較的(過去に実施したOCP3.x/4.xとかに比べれば…)容易に構築可能。

docs.k3s.io

構成

ホストOSのネットワーク設定として、デフォルトルートの設定がされている必要がある。これはデフォルトルートが設定されているNICIPアドレスに対してクラスターが構成されるため。

$ ip route
default via 172.29.0.1 dev eth0 proto static
172.29.0.0/24 dev eth0 proto kernel scope link src 172.29.0.77

また、(RHELディストリビューションで)SELinuxが有効の場合は、k3s-selinuxパッケージも追加で必要なので別途インストールしておく。(本エントリでは未確認)

イメージの準備

K3sがデフォルトで使用するイメージは、K3sのバージョンによって固定されておりリリースページで確認可能。今回のv1.29.2で使用するイメージもGitHubのリリースページに一覧が記載されているのでそこから取得。
これはオンライン環境でDLし、プライベートのコンテナレジストリpushする。

$ curl -LO https://github.com/k3s-io/k3s/releases/download/v1.29.2%2Bk3s1/k3s-airgap-images-amd64.tar.zst
$ podman image load -i k3s-airgap-images-amd64.tar.zst

[...]

Loaded image: localhost/rancher/mirrored-library-traefik:2.10.5
Loaded image: localhost/rancher/mirrored-metrics-server:v0.6.3
Loaded image: localhost/rancher/mirrored-pause:3.6
Loaded image: localhost/rancher/klipper-helm:v0.8.2-build20230815
Loaded image: localhost/rancher/klipper-lb:v0.4.5
Loaded image: localhost/rancher/local-path-provisioner:v0.0.26
Loaded image: localhost/rancher/mirrored-coredns-coredns:1.10.1
Loaded image: localhost/rancher/mirrored-library-busybox:1.36.1

展開されたこれらのイメージをすべてプライベートレジストリへpushする。 (コンテナ名・タグ名は変更しない)

skopeo copy containers-storage:localhost/rancher/local-path-provisioner:v0.0.26 docker://gitlab.example.jp:25000/zaki/images/local-path-provisioner:v0.0.26
...

まとめるとこんな感じだったけど、、8個だしコマンドラインを生成して実行した方が楽かな?

$ podman image list localhost/rancher/* -n | while read line; do src=$(echo $line | awk '{print $1":"$2}'); echo $src; dst=$(echo $src | sed 's#localhost/rancher#gitlab.example.jp:25000/zaki/images#'); skopeo copy containers-storage:$src docker://$dst --dest-tls-verify=false; done
localhost/rancher/mirrored-library-busybox:1.36.1

skopeo copy tarball:/path/to/k3s-airgap-images-amd64.tar.zstだと複数リポジトリにpushしてくれず、うまいやり方わからなかったので1つずつやってる…

プライベートレジストリのリダイレクト設定

通常通りのインストールを行うと、K3sのデプロイ時に使用するイメージはdocker.ioからpullしようとして失敗する。そこでdocker.ioへのアクセスをプライベートレジストリへリダイレクトする設定を事前に行う。

インストールするノードに/etc/rancher/k3s/registries.yamlを作成する。

mirrors:
  docker.io:
    endpoint:
      - "https://gitlab.example.jp:25000"
    rewrite:
      "^rancher/(.*)": "zaki/images/$1"
configs:
  "gitlab.example.jp:25000":
    tls:
      insecure_skip_verify: true

これはプライベートレジストリのホストがオレオレ証明書設定のhttps://gitlab.example.jp:25000にあり、イメージをpushするリポジトリzaki/images/*を使用している構成の場合。
もう少し具体的に言うと、GitLabのコンテナレジストリ機能を使って、ユーザーzakiimagesプロジェクトのリポジトリを使う場合で、K3sで使用するイメージ(rancher/*)を全てここへpushしている構成。

ちなみにendpointhttpsから書けるので、非SSLのコンテナレジストリでもhttp://から記述すれば使用可能。

docker.iorancher以外のリポジトリを追加するなら以下のように加えていく。

mirrors:
  docker.io:
    endpoint:
      - "https://gitlab.example.jp:25000"
    rewrite:
      "^rancher/(.*)": "zaki/images/$1"
      "^library/(.*)": "zaki/images/$1"

docker.io以外のレジストリのリダイレクト設定は追加する場合は以下の通り。

mirrors:
  docker.io:
    endpoint:
      - "https://gitlab.example.jp:25000"
    rewrite:
      "^rancher/(.*)": "zaki/images/$1"
      "^library/(.*)": "zaki/images/$1"
  gcr.io:
    endpoint:
      - "https://gitlab.example.jp:25000"
    rewrite:
      "^kubebuilder/(.*)": "zaki/images/$1"

リダイレクト設定はすべてのノードに必要。

インストールスクリプトの配置

https://get.k3s.io

通常インストール時にcurl -sfL https://get.k3s.io | sh -するときに使用するhttps://get.k3s.ioスクリプトをローカルに配置する。

online$ curl -L https://get.k3s.io -o install.sh
online$ scp install.sh  k3s-offline1:~

実行バイナリの配置

対象バージョンのK3s実行バイナリを取得。

online$ curl -LO https://github.com/k3s-io/k3s/releases/download/v1.29.2%2Bk3s1/k3s
online$ scp k3s k3s-offline1:~

インストール対象ノードで/usr/local/binへ配置。

$ chmod 755 k3s
$ sudo cp k3s /usr/local/bin/
$ k3s --version
k3s version v1.29.2+k3s1 (86f10213)
go version go1.21.7

インストール

イメージ・バイナリ・インストールスクリプトの準備ができたら、あとはインストールスクリプトINSTALL_K3S_SKIP_DOWNLOAD=trueを付与して実行する。

$ INSTALL_K3S_SKIP_DOWNLOAD=true ./install.sh 
[INFO]  Skipping k3s download and verify
[INFO]  Skipping installation of SELinux RPM
[INFO]  Creating /usr/local/bin/kubectl symlink to k3s
[INFO]  Creating /usr/local/bin/crictl symlink to k3s
[INFO]  Creating /usr/local/bin/ctr symlink to k3s
[INFO]  Creating killall script /usr/local/bin/k3s-killall.sh
[INFO]  Creating uninstall script /usr/local/bin/k3s-uninstall.sh
[INFO]  env: Creating environment file /etc/systemd/system/k3s.service.env
[INFO]  systemd: Creating service file /etc/systemd/system/k3s.service
[INFO]  systemd: Enabling k3s unit
Created symlink /etc/systemd/system/multi-user.target.wants/k3s.service → /etc/systemd/system/k3s.service.
[INFO]  systemd: Starting k3s
$ sudo kubectl get node
NAME           STATUS   ROLES                  AGE   VERSION
k3s-offline1   Ready    control-plane,master   18s   v1.29.2+k3s1
$ sudo kubectl get pod -A
NAMESPACE     NAME                                      READY   STATUS              RESTARTS   AGE
kube-system   helm-install-traefik-gvq48                0/1     ContainerCreating   0          97s
kube-system   helm-install-traefik-crd-xsblz            0/1     ContainerCreating   0          97s
kube-system   metrics-server-67c658944b-r655h           0/1     ContainerCreating   0          97s
kube-system   local-path-provisioner-6c86858495-qznr7   0/1     ContainerCreating   0          97s
kube-system   coredns-6799fbcd5-rwg9x                   0/1     ContainerCreating   0          97s

余談だけどこのときいつまでもRunningにならないと思ったらプライベートネットワークはDNSが無かったので/etc/hosts追加(笑)

$ sudo kubectl get pod -A
NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
kube-system   local-path-provisioner-6c86858495-qznr7   1/1     Running     0          2m29s
kube-system   coredns-6799fbcd5-rwg9x                   1/1     Running     0          2m29s
kube-system   helm-install-traefik-crd-xsblz            0/1     Completed   0          2m29s
kube-system   svclb-traefik-10a1b448-6h6c9              2/2     Running     0          35s
kube-system   helm-install-traefik-gvq48                0/1     Completed   1          2m29s
kube-system   traefik-f4564c4f4-gg6mv                   1/1     Running     0          35s
kube-system   metrics-server-67c658944b-r655h           1/1     Running     0          2m29s

動いた。

httpdのデプロイお試し

$ sudo kubectl apply -f http.yaml 
namespace/sample-app created
deployment.apps/sample-http created
service/sample-http created
$ sudo kubectl get pod,svc -n sample-app
NAME                               READY   STATUS    RESTARTS   AGE
pod/sample-http-5cd4944c69-l7xnb   1/1     Running   0          11s
pod/sample-http-5cd4944c69-6knfd   1/1     Running   0          11s

NAME                  TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/sample-http   LoadBalancer   10.43.134.90   172.29.0.77   8080:31629/TCP   11s

別ホストからアクセス

online$ curl 172.29.0.77:8080
<html><body><h1>It works!</h1></body></html>

大丈夫。

(追記) http.yamlの中身は以下の通り。

apiVersion: v1
kind: Namespace
metadata:
  name: sample-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sample-http
  name: sample-http
  namespace: sample-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: sample-http
  template:
    metadata:
      labels:
        app: sample-http
    spec:
      containers:
      - image: gitlab.example.jp:25000/zaki/images/httpd:latest
        name: httpd
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: sample-http
  name: sample-http
  namespace: sample-app
spec:
  ports:
  - port: 8080
    protocol: TCP
    targetPort: 80
    name: http
  selector:
    app: sample-http
  type: LoadBalancer

オフラインの仲間たち

qiita.com

zaki-hmkc.hatenablog.com

zaki-hmkc.hatenablog.com

zaki-hmkc.hatenablog.com

zaki-hmkc.hatenablog.com