创建一个机密计算Kubernetes集群

这篇文档展示了如何基于 Inclavare-Containers 运行时创建一个单节点的 Kubernetes 集群,Enclave Runtime 用的是 Occlum。

想了解 Inclavare-Containers 的工作原理,请参考 《Inclavare Containers:业界首个面向机密计算场景的开源容器运行时》

准备工作

  • 准备一台硬件支持 Intel SGX 的裸金属服务器
  • 确保操作系统是下面列表的一种:
    • CentOS 8.1 64位
    • Ubuntu 18.04 server 64位
  • 根据操作系统,从官方提供的版本页中合适的 Inclavare-Containers 的安装包
组件名 CentOS Ubuntu
occlum-pal occlum-pal-{version}.el8.x86_64.rpm occlum-pal_{version}_amd64.deb
shim-rune shim-rune-{version}.el8.x86_64.rpm shim-rune_{version}_amd64.deb
rune rune-{version}.el8.x86_64.rpm rune_{version}_amd64.deb

目标

  • 安装 Intel SGX 软件栈,包括 Intel SGX 驱动,SGX SDK 和 SGX PSW;
  • 安装 Occlum 软件栈,包括内核模块 enable-rdfsbase 和实现了 Enclave Runtime PAL API 的动态库 occlum-pal;
  • 创建单节点的 Kubernetes 集群,Pod 配置 RuntimeClass 可创建 runc 容器或 Enclave 容器。

安装步骤

1. 安装 Intel SGX 软件栈

参考官方文档 Intel SGX Installation Guide 安装 Intel SGX 驱动,Intel SDK 和 Intel PSW。建议安装的版本是 2.9.1。

注意:请安装 OOT SGX 驱动,不支持安装支持 ECDSA 认证的驱动。

2. 安装 Occlum 软件栈

目前 Occlum 是 Inclavare-Containers 唯一支持的一种 Enclave Runtime,Occlum 软件栈包括 enable-rdfsdbaseocclum-palenable-rdfsdbase 是 Occlum 所需的内核模块,该模块会开启 RDFSBASE, WRFSBASE, RDGSBASE, WRGSBASE 指令。 occlum-pal 实现了 Enclave Runtime APL API v2,用于 rune 和 Occlum 之间的通信。

  • 步骤1:安装内核模块 enable-redfsbase

参考官方文档 https://github.com/occlum/enable_rdfsbase 安装 enable-rdfsdbase

  • 步骤2:安装 libsgx-uae-service
    • CentOS

切换到步骤 1 中的 SGX RPM 本地库目录,执行如下命令安装 libsgx-uae-service:

  sudo rpm -ivh libsgx-uae-service-2.9.101.2-1.el8.x86_64.rpm
  • Ubuntu
  wget https://download.01.org/intel-sgx/sgx-linux/2.9.1/distro/ubuntu18.04-server/debian_pkgs/libs/libsgx-uae-service/libsgx-uae-service_2.9.101.2-xenial1_amd64.deb -O libsgx-uae-service_2.9.101.2-xenial1_amd64.deb
  sudo dpkg -i libsgx-uae-service_2.9.101.2-xenial1_amd64.deb
  • 步骤3:安装 occlum-pal

    • CentOS
  version=0.15.1-1
  sudo rpm -ivh occlum-pal-${version}.el8.x86_64.rpm
  • Ubuntu
  version=0.15.1-1
  sudo dpkg -i occlum-pal_${version}_amd64.deb

3. 安装容器运行时 runc 和 rune

runcrune 都是符合 OCI Runtime 规范的命令行工具,可用来创建和运行容器。 rune 是在 runc 代码的基础上开发的,所以 rune 也能够运行 runc 容器。他们的区别在于 rune 能够在容器里运行一个可信的执行环境 Enclave,用以保护敏感的数据和组织不可信实体对数据的访问。

  • 步骤1:下载二进制包 runc 并安装到目录 /usr/bin/runc 下
wget https://github.com/opencontainers/runc/releases/download/v1.0.0-rc90/runc.amd64 -O /usr/bin/runc
chmod +x /usr/bin/runc
  • 步骤2:安装 rune

    • CentOS
  version=0.4.0-1
  sudo yum install -y libseccomp
  sudo rpm -ivh rune-${version}.el7.x86_64.rpm
  • Ubuntu
  version=0.4.0-1
  sudo dpkg -i rune_${version}_amd64.deb

4. 安装 shim-rune

  • CentOS
version=0.4.0-1
sudo rpm -ivh shim-rune-${version}.el8.x86_64.rpm
  • Ubuntu
version=0.4.0-1
sudo dpkg -i shim-rune_${version}_amd64.deb

5. 安装和配置 containerd

containerd 一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性。可以在宿主机中管理完整的容器生命周期:容器镜像的传输和存储、容器的执行和管理、存储和网络等; 可从 containerd 下载页面 选择合适的版本来安装。

  • 步骤1:执行下面命令安装 containerd-1.3.4
curl -LO https://github.com/containerd/containerd/releases/download/v1.3.4/containerd-1.3.4.linux-amd64.tar.gz
tar -xvf containerd-1.3.4.linux-amd64.tar.gz
cp bin/* /usr/local/bin
  • 步骤2:配置 containerd.service
cat << EOF >/etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
EOF
  • 步骤3:配置 containerd 配置文件 config.toml
mkdir /etc/containerd
cat << EOF >/etc/containerd/config.toml
[plugins]
  [plugins.cri]
    sandbox_image = "registry.cn-hangzhou.aliyuncs.com/acs/pause-amd64:3.1"
    [plugins.cri.containerd]
      snapshotter = "overlayfs"
      [plugins.cri.containerd.default_runtime]
        runtime_type = "io.containerd.runtime.v1.linux"
        runtime_engine = "/usr/bin/runc"
        runtime_root = ""
      [plugins.cri.containerd.runtimes.rune]
        runtime_type = "io.containerd.rune.v2"
EOF
  • 步骤4:启用并启动 containerd 服务
sudo systemctl enable containerd.service
sudo systemctl restart containerd.service
  • 步骤5:提前下载 Occlum SDK 镜像(可选)

建议提前下载 Occlum SDK 镜像,shim-rune 会用该镜像启动容器并在容器内实现镜像转换。首次下载会比较耗时,提前下载好可以缩短 Enclave 容器的创建时间。 执行下面命令下载 Occlum SDK 镜像:

ctr image pull docker.io/occlum/occlum:0.15.1-ubuntu18.04

6. 创建单节点 Kubernetes 集群

  • 步骤1:设置 Kubernetes 所需的内核参数

加载内核模块 br_netfilter ,设置内核参数 net.bridge.bridge-nf-call-iptablesnet.ipv4.ip_forward 为 1 。

sudo modprobe br_netfilter
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sudo sysctl --system
  • 步骤2:配置安装包仓库,用来下载 kubelet,kubeadm 和 kubectl

    • CentOS
  cat << EOF >/etc/yum.repos.d/kubernetes.repo
  [kubernetes]
  name=Kubernetes
  baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
  enabled=1
  gpgcheck=1
  repo_gpgcheck=1
  gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
  EOF
  • Ubuntu
  sudo apt update && sudo apt install -y apt-transport-https curl
  curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
  echo "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main" >>/etc/apt/sources.list.d/kubernetes.list
  • 步骤3:安装 kubelet,kubeadm 和 kubectl

这里我们安装 v1.16.9 的 kubelet,kubeadm 和 kubectl。你也可以选择其他版本,但建议安装大于等于 v1.16 的版本。

  • CentOS
  sudo setenforce 0
  sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
  kubernetes_version=1.16.9
  sudo yum install -y --setopt=obsoletes=0 kubelet-${kubernetes_version} \
   kubeadm-${kubernetes_version} kubectl-${kubernetes_version} \
   --disableexcludes=kubernetes
  • Ubuntu
  sudo setenforce 0
  sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
  kubernetes_version=1.16.9
  sudo apt update && apt install -y kubelet=${kubernetes_version}-00 \
   kubeadm=${kubernetes_version}-00 kubectl=${kubernetes_version}-00 
  • 步骤4:配置 kubelet 配置文件

    • CentOS
  cat << EOF >/usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf
  # Note: This dropin only works with kubeadm and kubelet v1.11+
  [Service]
  Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
  Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
  Environment="KUBELET_SYSTEM_PODS_ARGS=--max-pods 64 --pod-manifest-path=/etc/kubernetes/manifests"
  Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
  Environment="KUBELET_DNS_ARGS=--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/acs/pause-amd64:3.0 --cluster-domain=cluster.local --cloud-provider=external"
  Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=/run/containerd/containerd.sock"
  ExecStart=
  ExecStart=/usr/bin/kubelet \$KUBELET_KUBECONFIG_ARGS \$KUBELET_CONFIG_ARGS \$KUBELET_SYSTEM_PODS_ARGS \$KUBELET_NETWORK_ARGS \$KUBELET_DNS_ARGS \$KUBELET_EXTRA_ARGS
  EOF
  • Ubuntu
  cat << EOF >/etc/resolv.conf.kubernetes
  nameserver 8.8.8.8
  options timeout:2 attempts:3 rotate single-request-reopen
  EOF
  
  cat << EOF >/etc/systemd/system/kubelet.service.d/10-kubeadm.conf
  # Note: This dropin only works with kubeadm and kubelet v1.11+
  [Service]
  Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf"
  Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
  Environment="KUBELET_SYSTEM_PODS_ARGS=--max-pods 64 --pod-manifest-path=/etc/kubernetes/manifests"
  Environment="KUBELET_NETWORK_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin"
  Environment="KUBELET_DNS_ARGS=--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/acs/pause-amd64:3.0 --cluster-domain=cluster.local --cloud-provider=external --resolv-conf=/etc/resolv.conf.kubernetes"
  Environment="KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=/run/containerd/containerd.sock"
  ExecStart=
  ExecStart=/usr/bin/kubelet \$KUBELET_KUBECONFIG_ARGS \$KUBELET_CONFIG_ARGS \$KUBELET_SYSTEM_PODS_ARGS \$KUBELET_NETWORK_ARGS \$KUBELET_DNS_ARGS \$KUBELET_EXTRA_ARGS
  EOF
  • 步骤5:启用 kubelet 服务
sudo systemctl enable kubelet.service
  • 步骤6:用 kubeadm 初始化集群

kubeadm 可通过参数 --kubernetes-version 设置集群的版本,Kubernetes 的集群版本一定要与 kubectl 的版本一致。可通过参数 --pod-network-cidr--service-cidr 分别设置 Kubernetes 的 Pod 和 Service 的 CIDR, 并确保这些 CIDR 不与 Host IP 有冲突。比如 Host IP 是 192.168.1.100, 可执行如下命令初始化集群:

kubeadm init --image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers \
 --kubernetes-version=v1.16.9 \
 --pod-network-cidr="172.21.0.0/20" --service-cidr="172.20.0.0/20"
  • 步骤7:配置 kubeconfig

为了能使 kubectl 工作,可执行如下命令配置 kubeconfig。以下命令也是 kubeadmin init 输出结果的一部分:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
  • 步骤8:安装网络插件

在没装网络插件前,集群 Node 状态会是 NotReady。执行如下命令安装网络插件 flannel,并确保 Node 状态变为 Ready

kubectl taint nodes $(hostname | tr 'A-Z' 'a-z') node.cloudprovider.kubernetes.io/uninitialized-
kubectl taint nodes $(hostname | tr 'A-Z' 'a-z') node-role.kubernetes.io/master-
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/2140ac876ef134e0ed5af15c65e414cf26827915/Documentation/kube-flannel.yml
  • 步骤9:检查 Pod 状态

执行命令 kubectl get pod -A 确保所有 Pod 状态变为 Ready ,输出结果如下所示:

$ kubectl get pod -A
NAMESPACE     NAME                                              READY   STATUS    RESTARTS   AGE
kube-system   coredns-67c766df46-bzmwx                          1/1     Running   0          74s
kube-system   coredns-67c766df46-l6blz                          1/1     Running   0          74s
kube-system   etcd-izuf68q2tx28s7tel52vb0z                      1/1     Running   0          20s
kube-system   kube-apiserver-izuf68q2tx28s7tel52vb0z            1/1     Running   0          12s
kube-system   kube-controller-manager-izuf68q2tx28s7tel52vb0z   1/1     Running   0          28s
kube-system   kube-flannel-ds-amd64-s542d                       1/1     Running   0          56s
kube-system   kube-proxy-fpwnh                                  1/1     Running   0          74s
kube-system   kube-scheduler-izuf68q2tx28s7tel52vb0z            1/1     Running   0          20s

7. 配置 RuntimeClass

  • 步骤1:创建 runc 和 rune RuntimeClass 对象
cat << EOF | kubectl apply -f -
apiVersion: node.k8s.io/v1beta1
handler: runc
kind: RuntimeClass
metadata:
  name: runc
EOF
cat << EOF | kubectl apply -f -
apiVersion: node.k8s.io/v1beta1
handler: rune
kind: RuntimeClass
metadata:
  name: rune
EOF
  • 步骤2:确保 RuntimeClass 对象创建成功

执行命令 kubectl get runtimeclass 可以列出 runcrune RuntimeClass 对象,输出结果如下:

$ kubectl get runtimeclass
NAME   CREATED AT
runc   2020-05-06T06:57:51Z
rune   2020-05-06T06:57:48Z