本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

使用 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 来查看集群。

结论

这是一种可以用来启动一个离线集群的方法,以及两种部署任务应用的方法。在不同的操作系统上,需要跨越“气隙”传输的确切软件工件可能会有所不同,但从概念上讲,这个过程仍然是有效的。

这个演示还创建了一个人工的离线环境。在现实世界中,每一个遗漏的依赖都可能代表着数小时,甚至数天或数周的时间损失,才能在离线环境中运行软件。这个人工的“气隙”也掩盖了一些常见的离线软件交付方法,例如使用**数据二极管**。根据环境的不同,使用二极管的成本可能非常高。此外,在跨越“气隙”之前,没有任何工件被扫描过。通常,“气隙”的存在意味着在那里运行的工作负载更敏感,除非已知是安全的,否则不应携带任何东西过去。