いろんなものを業務でオフライン動作させてきた身としては一度はやっておきたかったインターネット接続の無いオフライン環境へのインストールお試し。
K3sは軽量Kubernetesとして様々な環境で動作するのが特徴の一つで、インターネット接続の無い環境でもAir-Gap Installとしてドキュメントに手順が載っているので、比較的(過去に実施したOCP3.x/4.xとかに比べれば…)容易に構築可能。
構成
- ホストOS: Ubuntu 22.04.03 LTS
- K3s: v1.29.2
- プライベートコンテナレジストリ: ローカルのGitLab使用
ホストOSのネットワーク設定として、デフォルトルートの設定がされている必要がある。これはデフォルトルートが設定されているNICのIPアドレスに対してクラスターが構成されるため。
$ 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のコンテナレジストリ機能を使って、ユーザーzaki
のimages
プロジェクトのリポジトリを使う場合で、K3sで使用するイメージ(rancher/*
)を全てここへpushしている構成。
ちなみにendpoint
はhttps
から書けるので、非SSLのコンテナレジストリでもhttp://
から記述すれば使用可能。
docker.io
のrancher
以外のリポジトリを追加するなら以下のように加えていく。
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"
リダイレクト設定はすべてのノードに必要。
インストールスクリプトの配置
通常インストール時に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