本文發表於一年多前。舊文章可能包含過時內容。請檢查頁面中的資訊自發布以來是否已變得不正確。

使用 Kubeadm 引導一個氣隙叢集

有沒有想過軟體是如何部署到一個與網際網路和其他網路完全斷開的系統上的?由於其敏感性,這些系統通常是斷開的。這裡的敏感指的是公用事業(電力/水)、銀行、醫療保健、武器系統以及其他政府用例等。如果你在水下船隻上執行 Kubernetes,那麼這在技術上可能是一個“水隙”(water gap)。儘管如此,這些環境仍然需要軟體來執行。這種在斷開連線狀態下部署的概念,就是部署到 “氣隙”(air gap)另一側的含義。

再說一次,儘管存在這種狀況,軟體仍然需要在這些環境中執行。傳統上,軟體工件(artifacts)是透過硬碟、U 盤、CD 或軟盤(對於古代系統,這種情況仍然存在)物理地跨越“氣隙”傳輸的。Kubernetes 特別適合在“氣隙”後執行軟體,原因有幾個,主要是由於其宣告式的特性。

在這篇部落格文章中,我將使用 Fedora Linux 和 kubeadm,引導一個離線實驗環境中的 Kubernetes 叢集,並詳細介紹整個過程。

離線虛擬機器設定

建立一個真正的離線網路可能需要一些努力,所以在這篇文章中,我將使用筆記型電腦上的一個示例虛擬機器,並進行一些網路修改。下面是拓撲結構:

Topology on the host/laptop which shows that connectivity to the internet from the air gap VM is not possible. However, connectivity between the host/laptop and the VM is possible

本地拓撲

這個虛擬機器的網路連線將被停用,但方式不會關閉虛擬機器的虛擬網絡卡。相反,它的網路將透過注入一條預設路由到一個虛擬介面來使其下線,從而使任何網際網路託管的服務都無法訪問。然而,該虛擬機器仍然有一條連線到主機上橋接介面的路由,這意味著與主機的網路連線仍然正常。這種狀態意味著資料可以透過 `scp` 從主機/筆記型電腦傳輸到虛擬機器,即使虛擬機器上的預設路由將所有非目標為本地橋接子網的流量都“黑洞”了。這種傳輸方式類似於跨越“氣隙”傳輸資料,並將在本文中貫穿使用。

關於實驗環境的其他細節

虛擬機器作業系統:Fedora 37
Kubernetes 版本:v1.27.3
CNI 外掛版本:v1.3.0
CNI 提供商和版本:Flannel v0.22.0

雖然這個單一虛擬機器的實驗室是一個簡化的例子,但下面的圖表更近似地展示了一個真實的離線環境可能是什麼樣子:

Example production topology which shows 3 control plane Kubernetes nodes and 'n' worker nodes along with a Docker registry in an air-gapped environment.  Additionally shows two workstations, one on each side of the air gap and an IT admin which physically carries the artifacts across.

請注意,環境與網際網路之間仍然存在有意的隔離。為了保持圖表的簡潔,有些東西沒有顯示,例如在“氣隙”安全側的惡意軟體掃描。

回到單一虛擬機器的實驗環境。

識別所需的軟體工件

我已經費心識別了所有需要跨越“氣隙”傳輸的必需軟體元件,以便這個叢集能夠建立起來:

  • Docker(用於託管一個內部容器映象倉庫)
  • Containerd
  • libcgroup
  • socat
  • conntrack-tools
  • CNI 外掛
  • crictl
  • kubeadm
  • kubelet
  • kubectl 和 k9s(嚴格來說,這些不是引導叢集所必需的,但它們是與叢集互動的便捷工具)
  • kubelet.service systemd 檔案
  • kubeadm 配置檔案
  • Docker 倉庫容器映象
  • Kubernetes 元件容器映象
  • CNI 網路外掛容器映象(本實驗將使用 Flannel
  • CNI 網路外掛清單(manifest)
  • CNI 工具容器映象

我識別這些依賴項的方法是,嘗試進行安裝,然後解決所有因為需要額外依賴而丟擲的錯誤。在真實的離線場景中,每次跨“氣隙”傳輸工件可能代表安裝人員花費 20 分鐘到幾周不等的時間。也就是說,目標系統可能位於與你辦公桌同一樓層的資料中心,也可能位於某個偏遠地區的衛星下行設施,或者在海上航行的潛艇上。在任何給定時間瞭解該系統上有什麼是很重要的,這樣你才知道需要帶什麼東西。

為 K8s 準備節點

在下載並移動工件到虛擬機器之前,讓我們先準備好該虛擬機器以執行 Kubernetes。

虛擬機器準備

以普通使用者身份執行這些步驟

為軟體工件建立目標目錄

mkdir ~/tmp

以超級使用者(`root`)身份執行以下步驟

寫入 `/etc/sysctl.d/99-k8s-cri.conf`

cat > /etc/sysctl.d/99-k8s-cri.conf << EOF
net.bridge.bridge-nf-call-iptables=1
net.ipv4.ip_forward=1
net.bridge.bridge-nf-call-ip6tables=1
EOF

寫入 `/etc/modules-load.d/k8s.conf`(啟用 `overlay` 和 `nbr_netfilter`)

echo -e overlay\\nbr_netfilter > /etc/modules-load.d/k8s.conf

安裝 iptables

dnf -y install iptables-legacy

將 iptables 設定為使用 legacy 模式(而不是 `nft` 模擬 `iptables`)

update-alternatives --set iptables /usr/sbin/iptables-legacy

關閉 swap

touch /etc/systemd/zram-generator.conf
systemctl mask systemd-zram-setup@.service
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab

停用 `firewalld`(在演示環境中這樣做是可以的)

systemctl disable --now firewalld

停用 `systemd-resolved`

systemctl disable --now systemd-resolved

為 NetworkManager 配置 DNS 預設值

sed -i '/\[main\]/a dns=default' /etc/NetworkManager/NetworkManager.conf

清空系統級 DNS 解析器配置

unlink /etc/resolv.conf || true
touch /etc/resolv.conf

停用 SELinux (僅用於演示——在生產環境中執行此操作前請檢查!)

setenforce 0

確保所有更改在重啟後依然生效

reboot

下載所有工件

在筆記型電腦/主機上,下載上一節中列舉的所有工件。由於離線虛擬機器執行的是 Fedora 37,本部分顯示的所有依賴項都是針對 Fedora 37 的。請注意,此過程僅適用於 AArch64 或 AMD64 CPU 架構,因為它們是最流行和廣泛可用的。你可以在任何有寫許可權的地方執行此過程;你的主目錄是一個非常合適的選擇。

請注意,需要跨越“氣隙”傳輸的 Kubernetes 工件的作業系統軟體包現在可以在 pkgs.k8s.io 找到。這篇博文將結合使用 Fedora 倉庫和 GitHub 來下載所有必需的工件。當你在自己的叢集上執行此操作時,你應該決定是使用官方的 Kubernetes 軟體包,還是使用你的作業系統發行版的官方軟體包——兩者都是有效的選擇。

# Set architecture variables
UARCH=$(uname -m)

if [["$UARCH" == "arm64" || "$UARCH" == "aarch64"]]; then

    ARCH="aarch64"
    K8s_ARCH="arm64"

else

    ARCH="x86_64"
    K8s_ARCH="amd64"

fi

為要使用的軟體版本設定環境變數

CNI_PLUGINS_VERSION="v1.3.0"
CRICTL_VERSION="v1.27.0"
KUBE_RELEASE="v1.27.3"
RELEASE_VERSION="v0.15.1"
K9S_VERSION="v0.27.4"

建立一個 `download` 目錄,進入該目錄,並下載所有的 RPM 和配置檔案

mkdir download && cd download

curl -O https://download.docker.com/linux/fedora/37/${ARCH}/stable/Packages/docker-ce-cli-23.0.2-1.fc37.${ARCH}.rpm

curl -O https://download.docker.com/linux/fedora/37/${ARCH}/stable/Packages/containerd.io-1.6.19-3.1.fc37.${ARCH}.rpm

curl -O https://download.docker.com/linux/fedora/37/${ARCH}/stable/Packages/docker-compose-plugin-2.17.2-1.fc37.${ARCH}.rpm

curl -O https://download.docker.com/linux/fedora/37/${ARCH}/stable/Packages/docker-ce-rootless-extras-23.0.2-1.fc37.${ARCH}.rpm

curl -O https://download.docker.com/linux/fedora/37/${ARCH}/stable/Packages/docker-ce-23.0.2-1.fc37.${ARCH}.rpm

curl -O https://download-ib01.fedoraproject.org/pub/fedora/linux/releases/37/Everything/${ARCH}/os/Packages/l/libcgroup-3.0-1.fc37.${ARCH}.rpm

echo -e "\nDownload Kubernetes Binaries"

curl -L -O "https://github.com/containernetworking/plugins/releases/download/${CNI_PLUGINS_VERSION}/cni-plugins-linux-${K8s_ARCH}-${CNI_PLUGINS_VERSION}.tgz"

curl -L -O "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${K8s_ARCH}.tar.gz"

curl -L --remote-name-all https://dl.k8s.io/release/${KUBE_RELEASE}/bin/linux/${K8s_ARCH}/{kubeadm,kubelet}

curl -L -O "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/kubepkg/templates/latest/deb/kubelet/lib/systemd/system/kubelet.service"

curl -L -O "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/kubepkg/templates/latest/deb/kubeadm/10-kubeadm.conf"

curl -L -O "https://dl.k8s.io/release/${KUBE_RELEASE}/bin/linux/${K8s_ARCH}/kubectl"

echo -e "\nDownload dependencies"

curl -O "https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Everything/${ARCH}/os/Packages/s/socat-1.7.4.2-3.fc37.${ARCH}.rpm"

curl -O "https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Everything/${ARCH}/os/Packages/l/libcgroup-3.0-1.fc37.${ARCH}.rpm"

curl -O "https://dl.fedoraproject.org/pub/fedora/linux/releases/37/Everything/${ARCH}/os/Packages/c/conntrack-tools-1.4.6-4.fc37.${ARCH}.rpm"

curl -LO "https://github.com/derailed/k9s/releases/download/${K9S_VERSION}/k9s_Linux_${K8s_ARCH}.tar.gz"

curl -LO "https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml"

下載所有必需的容器映象

images=(
    "registry.k8s.io/kube-apiserver:${KUBE_RELEASE}"
    "registry.k8s.io/kube-controller-manager:${KUBE_RELEASE}"
    "registry.k8s.io/kube-scheduler:${KUBE_RELEASE}"
    "registry.k8s.io/kube-proxy:${KUBE_RELEASE}"
    "registry.k8s.io/pause:3.9"
    "registry.k8s.io/etcd:3.5.7-0"
    "registry.k8s.io/coredns/coredns:v1.10.1"
    "registry:2.8.2"
    "flannel/flannel:v0.22.0"
    "flannel/flannel-cni-plugin:v1.1.2"
)

for image in "${images[@]}"; do
    # Pull the image from the registry
    docker pull "$image"

    # Save the image to a tar file on the local disk
    image_name=$(echo "$image" | sed 's|/|_|g' | sed 's/:/_/g')
    docker save -o "${image_name}.tar" "$image"

done

上述命令將檢視當前主機/筆記型電腦的 CPU 架構,建立並進入一個名為 `download` 的目錄,最後下載所有依賴項。這些檔案中的每一個都必須透過 `scp` 傳輸到離線環境中。命令的確切語法會根據虛擬機器上的使用者、你是否建立了 SSH 金鑰以及你的離線虛擬機器的 IP 地址而有所不同。大致語法是:

scp -i <<SSH_KEY>> <<FILE>> <<AIRGAP_VM_USER>>@<<AIRGAP_VM_IP>>:~/tmp/

一旦所有檔案都傳輸到離線虛擬機器,部落格文章的其餘部分將在虛擬機器上進行。開啟到該系統的終端會話。

將工件放置到位

為了引導一個 Kubernetes 叢集所需的一切現在都存在於離線虛擬機器上了。這一部分要複雜得多,因為現在各種型別的工件都在離線虛擬機器的磁碟上。在離線虛擬機器上獲取一個 root shell,因為本節的其餘部分將從那裡執行。讓我們先設定與主機/筆記型電腦上相同的架構變數和環境變數,然後安裝所有的 RPM 軟體包。

UARCH=$(uname -m)
# Set architecture variables

if [["$UARCH" == "arm64" || "$UARCH" == "aarch64"]]; then

    ARCH="aarch64"
    K8s_ARCH="arm64"

else

    ARCH="x86_64"
    K8s_ARCH="amd64"

fi

# Set environment variables
CNI_PLUGINS_VERSION="v1.3.0"
CRICTL_VERSION="v1.27.0"
KUBE_RELEASE="v1.27.3"
RELEASE_VERSION="v0.15.1"
K9S_VERSION="v0.27.4"

cd ~/tmp/

dnf -y install ./*.rpm

接下來,安裝 CNI 外掛和 `crictl`

mkdir -p /opt/cni/bin
tar -C /opt/cni/bin -xz -f "cni-plugins-linux-${K8s_ARCH}-v1.3.0.tgz"
tar -C /usr/local/bin-xz -f "crictl-v1.27.0-linux-${K8s_ARCH}.tar.gz"

使 kubeadm、kubelet 和 kubectl 可執行,並將它們從 `/tmp` 目錄移動到 `/usr/local/bin`

chmod +x kubeadm kubelet kubectl
mv kubeadm kubelet kubectl /usr/local/bin

為 systemd 的 kubelet 服務檔案定義一個覆蓋,並將其移動到適當的位置

mkdir -p /etc/systemd/system/kubelet.service.d

sed "s:/usr/bin:/usr/local/bin:g" 10-kubeadm.conf > /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

containerd 的 CRI 外掛預設是停用的;啟用它。

sed -i 's/^disabled_plugins = \["cri"\]/#&/' /etc/containerd/config.toml

放置一個自定義的 `/etc/docker/daemon.json` 檔案

echo '{

"exec-opts": ["native.cgroupdriver=systemd"],

"insecure-registries" : ["localhost:5000"],

"allow-nondistributable-artifacts": ["localhost:5000"],

"log-driver": "json-file",

"log-opts": {

"max-size": "100m"

},

"group": "rnd",

"storage-driver": "overlay2",

"storage-opts": [

"overlay2.override_kernel_check=true"

]

}' > /etc/docker/daemon.json

在 Docker `daemon.json` 配置檔案中有兩項重要的內容需要強調。`insecure-registries` 這一行意味著括號中的倉庫不支援 TLS。即使在離線環境中,這也不是一個好的做法,但對於本實驗的目的是可以的。`allow-nondistributable-artifacts` 這一行告訴 Docker 允許將不可分發的工件推送到這個倉庫。Docker 預設不推送這些層,以避免圍繞許可或分發權的潛在問題。一個很好的例子是 Windows 基礎容器映象。這一行將允許 Docker 標記為“foreign”的層被推送到倉庫。雖然對於本文來說不是什麼大問題,但在某些離線環境中可能需要這一行。所有層都必須在本地存在,因為離線環境內的任何東西都無法訪問公共容器映象倉庫來獲取所需的東西。

(重新)啟動 Docker 並啟用它,使其在系統啟動時啟動

systemctl restart docker
systemctl enable docker

啟動並啟用 containerd 和 kubelet

systemctl enable --now containerd
systemctl enable --now kubelet

在 Docker 中執行的容器映象倉庫僅用於任何 CNI 相關的容器和後續的工作負載容器。該倉庫**不**用於存放 Kubernetes 元件容器。注意,這裡也可以使用 nerdctl 作為 Docker 的替代品,它允許直接與 containerd 互動。選擇 Docker 是因為它的熟悉度。

在 Docker 內啟動一個容器映象倉庫

docker load -i registry_2.8.2.tar
docker run -d -p 5000:5000 --restart=always --name registry registry:2.8.2

將 Flannel 容器載入到 Docker 倉庫中

注意本實驗選擇 Flannel 是因為熟悉。請選擇在你的環境中效果最好的 CNI。

docker load -i flannel_flannel_v0.22.0.tar
docker load -i flannel_flannel-cni-plugin_v1.1.2.tar
docker tag flannel/flannel:v0.22.0 localhost:5000/flannel/flannel:v0.22.0
docker tag flannel/flannel-cni-plugin:v1.1.1 localhost:5000/flannel/flannel-cni-plugin:v1.1.1
docker push localhost:5000/flannel/flannel:v0.22.0
docker push localhost:5000/flannel/flannel-cni-plugin:v1.1.1

透過 `ctr` 載入 Kubernetes 元件的容器映象

images_files=(
    "registry.k8s.io/kube-apiserver:${KUBE_RELEASE}"
    "registry.k8s.io/kube-controller-manager:${KUBE_RELEASE}"
    "registry.k8s.io/kube-scheduler:${KUBE_RELEASE}"
    "registry.k8s.io/kube-proxy:${KUBE_RELEASE}"
    "registry.k8s.io/pause:3.9"
    "registry.k8s.io/etcd:3.5.7-0"
    "registry.k8s.io/coredns/coredns:v1.10.1"
    
)


for index in "${!image_files[@]}"; do

    if [[-f "${image_files[$index]}" ]]; then

        # The below line loads the images where they need to be on the VM
        ctr -n k8s.io images import ${image_files[$index]}

    else

        echo "File ${image_files[$index]} not found!" 1>&2

    fi

done

這裡一個完全合理的問題可能是:“為什麼不使用剛剛建立的 Docker 倉庫來存放 K8s 元件映象?”即使對傳遞給 kubeadm 的配置檔案進行了適當的修改,這樣做也行不通。

啟動 Kubernetes 叢集

檢查叢集是否已在執行,如果是則拆除它

if systemctl is-active --quiet kubelet; then

    # Reset the Kubernetes cluster

    echo "A Kubernetes cluster is already running. Resetting the cluster..."

    kubeadm reset -f

fi

從離線虛擬機器內部登入到 Docker 倉庫

# OK for a demo; use secure credentials in production!

DOCKER_USER=user
DOCKER_PASS=pass
echo ${DOCKER_PASS} | docker login --username=${DOCKER_USER} --password-stdin localhost:5000

建立一個叢集配置檔案並初始化叢集

echo "---

apiVersion: kubeadm.k8s.io/v1beta3
kind: ClusterConfiguration
clusterName: kubernetes
kubernetesVersion: v1.27.3
networking:
    dnsDomain: cluster.local
    podSubnet: 10.244.0.0/16 # --pod-network-cidr
    serviceSubnet: 10.96.0.0/12
---
apiVersion: kubeadm.k8s.io/v1beta3
kind: InitConfiguration
localAPIEndpoint:
    advertiseAddress: 10.10.10.10 # Update to the IP address of the air gap VM
    bindPort: 6443
nodeRegistration:
    criSocket: unix:///run/containerd/containerd.sock # or rely on autodetection
    name: airgap # this must match the hostname of the air gap VM
# Since this is a single node cluster, this taint has to be commented out,
# otherwise the coredns pods will not come up.
# taints:
# - effect: NoSchedule
# key: node-role.kubernetes.io/master" > kubeadm_cluster.yaml

kubeadm init --config kubeadm_config.yaml

設定 `$KUBECONFIG` 並使用 `kubectl` 等待 API 伺服器健康

export KUBECONFIG=/etc/kubernetes/admin.conf

until kubectl get nodes; do
    echo -e "\nWaiting for API server to respond..." 1>&2
    sleep 5

done

設定網路

更新 Flannel 清單中的 Flannel 映象位置,並應用它

sed -i 's/image: docker\.io/image: localhost:5000/g' kube-flannel.yaml
kubectl apply -f kube-flannel.yaml

執行 `kubectl get pods -A --watch` 直到所有 Pod 都啟動並執行。

執行一個示例 Pod

叢集執行後,下一步是部署一個工作負載。對於這個簡單的演示,將部署 Podinfo 應用程式。

安裝 Helm

這個過程的第一部分必須在主機/筆記型電腦上執行。如果尚未安裝 Helm,請按照安裝 Helm 的說明進行安裝。

接下來,下載適用於 Linux 的 helm 二進位制檔案

UARCH=$(uname -m)
# Reset the architecture variables if needed
if [["$UARCH" == "arm64" || "$UARCH" == "aarch64"]]; then

    ARCH="aarch64"
    K8s_ARCH="arm64"

else

    ARCH="x86_64"
    K8s_ARCH="amd64"

fi

curl -LO https://get.helm.sh/helm-v3.12.2-linux-${K8s_ARCH}.tar.gz

新增 Podinfo Helm 倉庫,下載 Podinfo Helm Chart,下載 Podinfo 容器映象,然後最後將其儲存到本地磁碟

helm repo add https://stefanprodan.github.io/podinfo
helm fetch podinfo/podinfo --version 6.4.0
docker pull ghcr.io/stefanprodan/podinfo:6.4.0

將 podinfo 映象儲存到本地磁碟上的一個 tar 檔案中

docker save -o podinfo_podinfo-6.4.0.tar ghcr.io/stefanprodan/podinfo

### Transfer the image across the air gap

Reuse the `~/tmp` directory created on the air gapped VM to transport these artifacts across the air gap:

```bash
scp -i <<SSH_KEY>> <<FILE>> <<AIRGAP_VM_USER>>@<<AIRGAP_VM_IP>>:~/tmp/

在隔離側繼續

現在切換到離線虛擬機器進行其餘的安裝過程。

切換到 `~/tmp`

cd ~/tmp

解壓並移動 `helm` 二進位制檔案

tar -zxvf helm-v3.0.0-linux-amd64.tar.gz
mv linux-amd64/helm /usr/local/bin/helm

將 Podinfo 容器映象載入到本地 Docker 倉庫

docker load -i podinfo_podinfo-6.4.0.tar
docker tag podinfo/podinfo:6.4.0 localhost:5000/podinfo/podinfo:6.4.0
docker push localhost:5000/podinfo/podinfo:6.4.0

確保 `KUBECONFIG` 設定正確,然後安裝 Podinfo Helm Chart

# Outside of a demo or lab environment, use lower (or even least) privilege
# credentials to manage your workloads.
export KUBECONFIG=/etc/kubernetes/admin.conf
helm install podinfo ./podinfo-6.4.0.tgz --set image.repository=localhost:5000/podinfo/podinfo

驗證 Podinfo 應用程式是否啟動成功

kubectl get pods -n default

或者執行 k9s(一個 Kubernetes 的終端使用者介面)

k9s

Zarf

Zarf 是一個開源工具,它採用宣告式方法進行軟體打包和交付,包括離線環境。本節將使用 Zarf 將同一個 podinfo 應用程式安裝到離線虛擬機器上。第一步是在主機/筆記型電腦上安裝 Zarf

或者,可以從 GitHub 為各種作業系統/CPU 架構下載預構建的二進位制檔案到主機/筆記型電腦上。

在虛擬機器上也需要一個二進位制檔案來跨越“氣隙”

UARCH=$(uname -m)
# Set the architecture variables if needed
if [["$UARCH" == "arm64" || "$UARCH" == "aarch64"]]; then

    ARCH="aarch64"
    K8s_ARCH="arm64"

else

    ARCH="x86_64"
    K8s_ARCH="amd64"

fi

export ZARF_VERSION=v0.28.3

curl -LO "https://github.com/defenseunicorns/zarf/releases/download/${ZARF_VERSION}/zarf_${ZARF_VERSION}_Linux_${K8s_ARCH}"

Zarf 需要透過使用一個 init 包來引導自身進入 Kubernetes 叢集。這個包也需要跨越“氣隙”傳輸,所以讓我們在主機/筆記型電腦上下載它。

curl -LO "https://github.com/defenseunicorns/zarf/releases/download/${ZARF_VERSION}/zarf-init-${K8s_ARCH}-${ZARF_VERSION}.tar.zst"

Zarf 實現宣告式的方式是透過使用一個 `zarf.yaml` 檔案。這是將用於此 Podinfo 安裝的 `zarf.yaml` 檔案。將它寫入你主機/筆記型電腦上有寫許可權的任何目錄;你的主目錄就可以。

echo 'kind: ZarfPackageConfig
metadata:
    name: podinfo
    description: "Deploy helm chart for the podinfo application in K8s via zarf"
components:
    - name: podinfo
        required: true
        charts:
            - name: podinfo
              version: 6.4.0
              namespace: podinfo-helm-namespace
              releaseName: podinfo
              url: https://stefanprodan.github.io/podinfo
        images:
        - ghcr.io/stefanprodan/podinfo:6.4.0' > zarf.yaml

下一步是構建 Podinfo 包。這必須在 `zarf.yaml` 檔案所在的同一目錄位置執行。

zarf package create --confirm

該命令將下載定義的 Helm Chart 和映象,並將它們放入一個寫入磁碟的單個檔案中。這個單一檔案就是所有需要跨越“氣隙”傳輸的東西。

ls zarf-package-*

示例輸出

zarf-package-podinfo-arm64.tar.zst

將 Linux 平臺的 Zarf 二進位制檔案、Zarf init 包和 Podinfo 包傳輸到離線虛擬機器

scp -i <<SSH_KEY>> <<FILE>> <<AIRGAP_VM_USER>>@<<AIRGAP_VM_IP>>:~/tmp/

在離線虛擬機器上,切換到存放所有工件的 `~/tmp` 目錄

cd ~/tmp

將 `$KUBECONFIG` 設定為包含本地叢集憑據的檔案;同時設定 Zarf 版本

export KUBECONFIG=/etc/kubernetes/admin.conf

export ZARF_VERSION=$(zarf version)

使 `zarf` 二進位制檔案可執行,並(以 `root` 身份)將其移動到 `/usr/bin`

chmod +x zarf && sudo mv zarf /usr/bin

同樣,將 Zarf init 包移動到 `/usr/bin`

mv zarf-init-arm64-${ZARF_VERSION}.tar.zst /usr/bin

將 Zarf 初始化到叢集中

zarf init --confirm --components=git-server

當此命令完成時,一個 Zarf 包就準備好部署了。

zarf package deploy

此命令將在當前目錄中搜索 Zarf 包。選擇 podinfo 包 (`zarf-package-podinfo-${K8s_ARCH}.tar.zst`) 並繼續。一旦包部署完成,執行 `zarf tools monitor` 以啟動 k9s 來檢視叢集。

結論

這是一種可以用來啟動一個離線叢集的方法,以及兩種部署任務應用的方法。在不同的作業系統上,需要跨越“氣隙”傳輸的確切軟體工件可能會有所不同,但從概念上講,這個過程仍然是有效的。

這個演示還建立了一個人工的離線環境。在現實世界中,每一個遺漏的依賴都可能代表著數小時,甚至數天或數週的時間損失,才能在離線環境中執行軟體。這個人工的“氣隙”也掩蓋了一些常見的離線軟體交付方法,例如使用**資料二極體**。根據環境的不同,使用二極體的成本可能非常高。此外,在跨越“氣隙”之前,沒有任何工件被掃描過。通常,“氣隙”的存在意味著在那裡執行的工作負載更敏感,除非已知是安全的,否則不應攜帶任何東西過去。