这是本节的多页打印视图。 点击此处打印.

返回本页常规视图.

示例

CoCo 部署示例

1 - AWS

在 AWS 上使用 Cloud API Adaptor (CAA) 的 Peer Pods Helm Chart

说明: 本文为英文文档的中文译版,英文原版请参见 AWS 示例(英文版)

本文将介绍如何在 AWS Elastic Kubernetes Service (EKS) 上配置 CAA(即 Peer Pods),具体包括:

  • 一个基于 Elastic Kubernetes Service (EKS) 的单工作节点 Kubernetes 集群
  • 运行在该 Kubernetes 集群上的 CAA
  • 一个由 CAA PodVM 支撑的 Nginx Pod

前提条件

安装所需工具:

AWS 准备工作

  • 为 AWS CLI 访问设置 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY(或 AWS_PROFILE)以及 AWS_REGION

说明: 除了静态凭证外,也可以在 EKS 上使用 IRSA(IAM Roles for Service Accounts)。使用 IRSA 时,CAA Pod 通过 OIDC 完成认证,无需在 Kubernetes Secret 中保存静态 AWS 密钥。集群初始化步骤中仍然需要 AWS_REGION 和临时凭证。

  • 设置区域:
export AWS_REGION="us-east-2"

说明: 选择 us-east-2 区域,是因为这里既提供 AMD SEV-SNP 实例,也提供可直接使用的预构建 PodVM 镜像。

export AWS_REGION="us-east-2"

说明: 选择 us-east-2 区域,是因为这里提供可直接使用的预构建 PodVM 镜像。

使用 EKS 部署 Kubernetes

按需修改以下环境变量:

export CLUSTER_NAME="caa-$(date '+%Y%m%b%d%H%M%S')"
export CLUSTER_NODE_TYPE="m5.xlarge"
export CLUSTER_NODE_FAMILY_TYPE="Ubuntu2204"
export SSH_KEY=~/.ssh/id_rsa.pub

以下示例使用默认的 AWS VPC-CNI 创建 EKS 集群:

eksctl create cluster --name "$CLUSTER_NAME" \
    --node-type "$CLUSTER_NODE_TYPE" \
    --node-ami-family "$CLUSTER_NODE_FAMILY_TYPE" \
    --nodes 1 \
    --nodes-min 0 \
    --nodes-max 2 \
    --node-private-networking \
    --kubeconfig "$CLUSTER_NAME"-kubeconfig

等待集群创建完成。

为集群节点添加 node.kubernetes.io/worker= 标签:

for NODE_NAME in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
  kubectl label node $NODE_NAME node.kubernetes.io/worker=
done

放通所需网络端口

EKS_VPC_ID=$(aws eks describe-cluster --name "$CLUSTER_NAME" \
--query "cluster.resourcesVpcConfig.vpcId" \
--output text)
echo $EKS_VPC_ID

EKS_CLUSTER_SG=$(aws eks describe-cluster --name "$CLUSTER_NAME" \
  --query "cluster.resourcesVpcConfig.clusterSecurityGroupId" \
  --output text)
echo $EKS_CLUSTER_SG

EKS_VPC_CIDR=$(aws ec2 describe-vpcs --vpc-ids "$EKS_VPC_ID" \
--query 'Vpcs[0].CidrBlock' --output text)
echo $EKS_VPC_CIDR

# agent-protocol-forwarder 端口
aws ec2 authorize-security-group-ingress --group-id "$EKS_CLUSTER_SG" --protocol tcp --port 15150 --cidr "$EKS_VPC_CIDR"

# vxlan 端口
aws ec2 authorize-security-group-ingress --group-id "$EKS_CLUSTER_SG" --protocol tcp --port 9000 --cidr "$EKS_VPC_CIDR"
aws ec2 authorize-security-group-ingress --group-id "$EKS_CLUSTER_SG" --protocol udp --port 9000 --cidr "$EKS_VPC_CIDR"

说明:

  • 端口 15150 是 CAA 连接 PodVM 内 agent-protocol-forwarder 时使用的默认端口。
  • 端口 9000 是 CAA 使用的 VXLAN 端口。请确保它与 Kubernetes CNI 使用的 VXLAN 端口不冲突。

配置认证

选择 CAA 与 AWS 交互时的认证方式。静态凭证方式通过 Kubernetes Secret 保存访问密钥;IRSA 则通过 OIDC 联邦让 Pod 直接承担 IAM 角色,无需静态密钥。

确保环境中已设置 AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY(或 AWS_PROFILE)。这些值会存入 Kubernetes Secret,供 CAA Pod 使用。

此处无需额外操作,Secret 将在后续的 在 Kubernetes 集群中部署 Helm Chart 步骤中创建。

IRSA 不再需要把长期 AWS 访问密钥保存在 Kubernetes Secret 中,是 EKS 上推荐使用的认证方式。

启用 OIDC Provider

检查 IAM OIDC provider 是否已经注册:

OIDC_ID=$(aws eks describe-cluster \
  --name ${CLUSTER_NAME} \
  --region ${AWS_REGION} \
  --query "cluster.identity.oidc.issuer" \
  --output text | awk -F'/' '{print $NF}')

aws iam list-open-id-connect-providers | grep ${OIDC_ID}

如果命令没有返回结果,则创建 OIDC provider:

eksctl utils associate-iam-oidc-provider \
  --cluster ${CLUSTER_NAME} \
  --region ${AWS_REGION} \
  --approve

导出账户 ID 和 OIDC provider,供后续步骤使用:

export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

export OIDC_PROVIDER=$(aws eks describe-cluster \
  --name ${CLUSTER_NAME} \
  --region ${AWS_REGION} \
  --query "cluster.identity.oidc.issuer" \
  --output text | sed 's|https://||')

为 cloud-api-adaptor 创建 IAM Role

export NAMESPACE="confidential-containers-system"
export CAA_SERVICE_ACCOUNT="cloud-api-adaptor"
export CAA_ROLE_NAME="CAA-IRSA-Role"

创建信任策略:

cat > /tmp/caa-trust-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "${OIDC_PROVIDER}:sub": "system:serviceaccount:${NAMESPACE}:${CAA_SERVICE_ACCOUNT}",
          "${OIDC_PROVIDER}:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
EOF

创建 IAM 角色,并附加 AmazonEC2FullAccess 托管策略:

aws iam create-role \
  --role-name ${CAA_ROLE_NAME} \
  --assume-role-policy-document file:///tmp/caa-trust-policy.json \
  --description "IRSA role for Cloud API Adaptor on EKS"

aws iam attach-role-policy \
  --role-name ${CAA_ROLE_NAME} \
  --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess

说明: AmazonEC2FullAccess 会授予较宽泛的 EC2 权限。用于生产环境时,强烈建议改用更小权限范围的自定义策略,仅授予 CAA 所需的 EC2 操作权限。更多配置方式请参考 AWS IRSA 文档

导出 role ARN,供后续使用:

export CAA_ROLE_ARN=$(aws iam get-role \
  --role-name ${CAA_ROLE_NAME} \
  --query 'Role.Arn' \
  --output text)

echo "CAA Role ARN: ${CAA_ROLE_ARN}"

为 Peerpod-ctrl 创建 IAM Role(可选)

只有在部署 Peerpod-ctrl 时才需要这一步。

export CTRL_SERVICE_ACCOUNT="peerpodctrl-controller-manager"
export CTRL_ROLE_NAME="PeerpodCtrl-IRSA-Role"

创建信任策略:

cat > /tmp/peerpod-ctrl-trust-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/${OIDC_PROVIDER}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "${OIDC_PROVIDER}:sub": "system:serviceaccount:${NAMESPACE}:${CTRL_SERVICE_ACCOUNT}",
          "${OIDC_PROVIDER}:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
EOF

创建角色并附加权限:

aws iam create-role \
  --role-name ${CTRL_ROLE_NAME} \
  --assume-role-policy-document file:///tmp/peerpod-ctrl-trust-policy.json \
  --description "IRSA role for PeerPod Controller on EKS"

aws iam attach-role-policy \
  --role-name ${CTRL_ROLE_NAME} \
  --policy-arn arn:aws:iam::aws:policy/AmazonEC2FullAccess

说明: AmazonEC2FullAccess 会授予较宽泛的 EC2 权限。用于生产环境时,强烈建议改用更小权限范围的自定义策略,仅授予 CAA 所需的 EC2 操作权限。更多配置方式请参考 AWS IRSA 文档

导出 role ARN:

export CTRL_ROLE_ARN=$(aws iam get-role \
  --role-name ${CTRL_ROLE_NAME} \
  --query 'Role.Arn' \
  --output text)

echo "Controller Role ARN: ${CTRL_ROLE_ARN}"

部署 CAA Helm Chart

下载 CAA Helm 部署资源

export CAA_VERSION="0.22.0"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/tags/v${CAA_VERSION}.tar.gz"
tar -xvzf "v${CAA_VERSION}.tar.gz"
cd "cloud-api-adaptor-${CAA_VERSION}/src/cloud-api-adaptor/install/charts/peerpods"
export CAA_BRANCH="main"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/heads/${CAA_BRANCH}.tar.gz"
tar -xvzf "${CAA_BRANCH}.tar.gz"
cd "cloud-api-adaptor-${CAA_BRANCH}/src/cloud-api-adaptor/install/charts/peerpods"

假如你已在本地准备好代码,请在终端中切换到 Cloud API Adaptor 的代码目录。

导出 PodVM 镜像版本

导出 peer pods 所用的 PodVM 镜像 ID。该变量告诉部署工具在 AWS 中创建 peer pod 虚拟机时应使用哪个 PodVM 镜像版本。

镜像来自 CoCo 社区镜像库(或由你手动构建),并且必须与当前 CAA 发布版本匹配。

us-east-2 区域中,我们提供了一个可用于 PoC 的预构建调试版 PodVM 镜像。可通过以下命令查询对应发布版本的 AMI ID:

export PODVM_AMI_ID=$(aws ec2 describe-images \
    --filters Name=name,Values="podvm-ubuntu-amd64-${CAA_VERSION//./-}" \
    --query 'Images[*].[ImageId]' --output text)

echo $PODVM_AMI_ID

最新构建没有预构建的 PodVM AMI。你需要自行构建 PodVM 镜像,然后按照这里的说明创建 AMI。

为 AWS 构建 PodVM 镜像前,请记得设置 TEE_PLATFORM=amd

镜像构建完成后,将镜像 ID 导出到环境变量 PODVM_AMI_ID

你可以按照这里的说明构建自定义 PodVM 镜像。

为 AWS 构建 PodVM 镜像前,请记得设置 TEE_PLATFORM=amd

镜像构建完成后,将镜像 ID 导出到环境变量 PODVM_AMI_ID

导出 CAA 容器镜像路径

定义要部署的 Cloud API Adaptor(CAA)容器镜像。 这些变量指定了部署工具所要拉取和运行的 CAA 镜像及其架构专属 tag。 tag 与 CAA 发布版本对应,以确保与所选 PodVM 镜像和配置兼容。

导出以下环境变量以使用 CAA 最新发布镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"
export CAA_TAG="v${CAA_VERSION}-amd64"

导出以下环境变量,以使用每次合并到 main 后由 CAA CI 构建的镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"

你可以在这里找到适合需求的预构建镜像 tag。

export CAA_TAG=""

注意: 你也可以使用 latest tag,但不推荐这样做,因为它缺少版本控制,可能引入不可预期的更新,影响部署稳定性和可复现性。

如果你修改了 CAA 代码并希望部署这些改动,请按这些说明构建容器镜像。镜像构建完成后,导出环境变量 CAA_IMAGECAA_TAG

选择 peer-pods 机型

export PODVM_INSTANCE_TYPE="m6a.large"
export DISABLECVM="false"

更多 AMD SEV-SNP 机型可参考这份 AWS 文档。

export PODVM_INSTANCE_TYPE="t3.large"
export DISABLECVM="true"

填充 providers/aws.yaml 文件

全部可用配置项可在以下两个位置找到:

运行以下命令更新 providers/aws.yaml 文件:

cat <<EOF > providers/aws.yaml
provider: aws
image:
  name: "${CAA_IMAGE}"
  tag: "${CAA_TAG}"
providerConfigs:
   aws:
      DISABLECVM: ${DISABLECVM}
      PODVM_AMI_ID: "${PODVM_AMI_ID}"
      PODVM_INSTANCE_TYPE: "${PODVM_INSTANCE_TYPE}"
      VXLAN_PORT: 9000
EOF

安装 cert-manager

CAA 依赖 cert-manager。除非环境中已经安装,否则请使用以下方式部署:

helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true \
  --wait \
  --timeout 5m

在 Kubernetes 集群中部署 Helm Chart

  1. 创建由 Helm 管理的命名空间:

    kubectl apply -f - << EOF
    apiVersion: v1
    kind: Namespace
    metadata:
      name: confidential-containers-system
      labels:
        app.kubernetes.io/managed-by: Helm
      annotations:
        meta.helm.sh/release-name: peerpods
        meta.helm.sh/release-namespace: confidential-containers-system
    EOF
    
  2. 创建凭证并安装 Helm Chart:

    下面命令使用了 -f--set 这两个自定义选项,其含义可参考这里

使用 kubectl 创建 Secret。所需 key 请参见 providers/aws-secrets.yaml.template

kubectl create secret generic my-provider-creds \
  -n confidential-containers-system \
  --from-literal=AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} \
  --from-literal=AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} \
  --from-file=id_rsa.pub=${SSH_KEY}

说明: --from-file=id_rsa.pub=${SSH_KEY} 是可选项。它允许用户为排障目的通过 SSH 登录 PodVM。 该选项只对启用了调试功能的自定义 PodVM 镜像有效。预构建的 PodVM 镜像默认不启用 SSH 连接。

安装 Helm Chart:

helm install peerpods . \
  -f providers/aws.yaml \
  --set secrets.mode=reference \
  --set secrets.existingSecretName=my-provider-creds \
  --dependency-update \
  -n confidential-containers-system

使用 IRSA 时,无需创建 AWS 凭证 Secret。CAA Pod 会通过配置认证章节中配置的 IAM 角色完成认证。

使用 IRSA 注解安装 Helm Chart:

helm install peerpods . \
  -f providers/aws.yaml \
  --set "daemonset.serviceAccount.annotations.eks\.amazonaws\.com/role-arn=${CAA_ROLE_ARN}" \
  --set "resourceCtrl.serviceAccount.annotations.eks\.amazonaws\.com/role-arn=${CTRL_ROLE_ARN}" \
  --dependency-update \
  -n confidential-containers-system

说明: resourceCtrl.serviceAccount.annotations 这一行只有在部署 Peerpod-ctrl 时才需要。 如果不部署它,可以省略这个 --set 参数,并跳过前文中的 为 Peerpod-ctrl 创建 IAM Role(可选) 步骤。

验证 IRSA 是否生效,可检查服务账号注解和 Pod 环境变量:

kubectl get serviceaccount cloud-api-adaptor \
  -n confidential-containers-system \
  -o jsonpath='{.metadata.annotations.eks\.amazonaws\.com/role-arn}'
CAA_POD=$(kubectl get pods -n confidential-containers-system \
  -l app=cloud-api-adaptor \
  -o jsonpath='{.items[0].metadata.name}')

kubectl exec -n confidential-containers-system ${CAA_POD} -- env | grep AWS

输出中应包含 AWS_WEB_IDENTITY_TOKEN_FILEAWS_ROLE_ARN 变量。

通用的 Peer Pods Helm Chart 部署说明也可参考这里

运行示例应用

确认 RuntimeClass 已创建

部署 CAA 后,请确认已创建 RuntimeClass

kubectl get runtimeclass

当你看到名为 kata-remoteRuntimeClass 时,就说明部署成功。成功输出类似如下:

$ kubectl get runtimeclass
NAME          HANDLER       AGE
kata-remote   kata-remote   7m18s

部署工作负载

创建一个 nginx deployment:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      runtimeClassName: kata-remote
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        imagePullPolicy: Always
EOF

确认 pod 已成功启动:

kubectl get pods -n default

你可以通过运行以下命令确认 peer pod VM 是否已经创建:

aws ec2 describe-instances --filters "Name=tag:Name,Values=podvm*" \
   --query 'Reservations[*].Instances[*].[InstanceId, Tags[?Key==`Name`].Value | [0]]' --output table

此时你应该能看到与 pod nginx 对应的虚拟机。

说明: 如果遇到问题,请查看故障排查指南

清理

删除所有使用 kata-remote runtimeclass 运行的 Pod。可以使用以下命令:

kubectl get pods -A -o custom-columns='NAME:.metadata.name,NAMESPACE:.metadata.namespace,RUNTIMECLASS:.spec.runtimeClassName' | grep kata-remote | awk '{print $1, $2}'

确认所有 peer-pod VM 都已删除。你可以使用以下命令列出所有 peer-pod VM(名称前缀为 podvm)及其状态:

aws ec2 describe-instances --filters "Name=tag:Name,Values=podvm*" \
--query 'Reservations[*].Instances[*].[InstanceId, Tags[?Key==`Name`].Value | [0], State.Name]' --output table

运行以下命令删除 EKS 集群:

eksctl delete cluster --name=$CLUSTER_NAME

2 - Azure

在 Azure 上使用 Cloud API Adaptor (CAA) 的 Peer Pods Helm Chart

说明: 本文为英文文档的中文译版,英文原版请参见 Azure 示例(英文版)

本文将介绍如何在 Azure Kubernetes Service (AKS) 上配置 CAA(即 Peer Pods),具体包括:

  • 一个基于 Azure Kubernetes Service (AKS) 的单工作节点 Kubernetes 集群
  • 运行在该 Kubernetes 集群上的 CAA
  • 一个由 CAA PodVM 支撑的 Nginx Pod

Confidential Containers 也支持将 Azure Key Vault 用作 Trustee 的资源后端。 更多信息

前提条件

安装所需工具:

  • 安装 kubectl
  • 安装 Helm
  • 安装 az CLI 工具
  • 确保已安装 curlgitjqsipcalc

Azure 准备工作

登录 Azure

以下许多步骤均需先登录 Azure 账号:

az login

获取你的订阅 ID:

export AZURE_SUBSCRIPTION_ID=$(az account show --query id --output tsv)

设置区域:

export AZURE_REGION="eastus"

说明: 选择 eastus 区域,是因为这里既提供 AMD SEV-SNP 实例,也提供可直接使用的预构建 PodVM 镜像。

export AZURE_REGION="eastus2"

说明: 选择 eastus2 区域,是因为这里既提供 Intel TDX 实例,也提供可直接使用的预构建 PodVM 镜像。

export AZURE_REGION="eastus"

说明: 选择 eastus 区域,是因为这里提供可直接使用的预构建 PodVM 镜像。

资源组

说明: 如果你已经有可用的资源组,可以跳过这一步。请将资源组名称导出到环境变量 AZURE_RESOURCE_GROUP

运行以下命令创建 Azure 资源组:

export AZURE_RESOURCE_GROUP="caa-rg-$(date '+%Y%m%b%d%H%M%S')"

az group create \
  --name "${AZURE_RESOURCE_GROUP}" \
  --location "${AZURE_REGION}"

使用 AKS 部署 Kubernetes

按需修改以下环境变量:

export CLUSTER_NAME="caa-$(date '+%Y%m%b%d%H%M%S')"
export AKS_WORKER_USER_NAME="azuser"
export AKS_RG="${AZURE_RESOURCE_GROUP}-aks"
export SSH_KEY=~/.ssh/id_rsa.pub

说明: 你也可以通过增加参数 --vnet-subnet-id $MY_SUBNET_ID,将工作节点部署到现有的 Azure Virtual Network (VNet) 和子网中。

将一个单工作节点的 AKS 集群部署到你刚刚创建的资源组中:

az aks create \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --node-resource-group "${AKS_RG}" \
  --name "${CLUSTER_NAME}" \
  --enable-oidc-issuer \
  --enable-workload-identity \
  --location "${AZURE_REGION}" \
  --node-count 1 \
  --node-vm-size Standard_F4s_v2 \
  --nodepool-labels node.kubernetes.io/worker= \
  --ssh-access disabled \
  --admin-username "${AKS_WORKER_USER_NAME}" \
  --os-sku Ubuntu

将 kubeconfig 下载到本地,以便用 kubectl 访问集群:

az aks get-credentials \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --name "${CLUSTER_NAME}"

用户分配身份与联合凭证

CAA 需要具备访问 Azure API 的权限。做法是将工作负载身份(workload identity)关联到 CAA 的服务账号上。该工作负载身份(即用户分配身份)将在下一步被授予创建虚拟机、获取镜像和访问网络的权限。

说明: 如果你使用的是现有 AKS 集群,可能需要先配置工作负载身份(workload identity)和 OpenID Connect (OIDC)。可参考指南

先为 CAA 创建一个身份:

export AZURE_WORKLOAD_IDENTITY_NAME="${CLUSTER_NAME}-identity"

az identity create \
  --name "${AZURE_WORKLOAD_IDENTITY_NAME}" \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --location "${AZURE_REGION}"
export USER_ASSIGNED_CLIENT_ID="$(az identity show \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --name "${AZURE_WORKLOAD_IDENTITY_NAME}" \
  --query 'clientId' \
  -otsv)"

网络

承载 Pod 的虚拟机通常需要访问互联网服务,例如从公共 OCI 镜像仓库拉取镜像。你可以在 AKS 集群所在 VNet 中,为 AKS 子网旁边新建一个独立子网,再为该子网绑定带公网 IP 的 NAT 网关:

export AZURE_VNET_NAME="$(az network vnet list -g ${AKS_RG} --query '[].name' -o tsv)"
export AKS_CIDR="$(az network vnet show -n $AZURE_VNET_NAME -g $AKS_RG --query "subnets[?name == 'aks-subnet'].addressPrefix" -o tsv)"
# 10.224.0.0/16
export MASK="${AKS_CIDR#*/}"
# 16
PEERPOD_CIDR="$(sipcalc $AKS_CIDR -n 2 | grep ^Network | grep -v current | cut -d' ' -f2)/${MASK}"
# 10.225.0.0/16
az network public-ip create -g "$AKS_RG" -n peerpod
az network nat gateway create -g "$AKS_RG" -l "$AZURE_REGION" --public-ip-addresses peerpod -n peerpod
az network vnet subnet create -g "$AKS_RG" --vnet-name "$AZURE_VNET_NAME" --nat-gateway peerpod --address-prefixes "$PEERPOD_CIDR" -n peerpod
export AZURE_SUBNET_ID="$(az network vnet subnet show -g "$AKS_RG" --vnet-name "$AZURE_VNET_NAME" -n peerpod --query id -o tsv)"

AKS 资源组权限

为了让 CAA 能管理虚拟机,需为该身份授予虚拟机和网络相关权限,使其能够在 $AZURE_RESOURCE_GROUP 中创建虚拟机,并连接 $AKS_RG 中的 VNet。

az role assignment create \
  --role "Virtual Machine Contributor" \
  --assignee "$USER_ASSIGNED_CLIENT_ID" \
  --scope "/subscriptions/${AZURE_SUBSCRIPTION_ID}/resourcegroups/${AZURE_RESOURCE_GROUP}"
az role assignment create \
  --role "Reader" \
  --assignee "$USER_ASSIGNED_CLIENT_ID" \
  --scope "/subscriptions/${AZURE_SUBSCRIPTION_ID}/resourcegroups/${AZURE_RESOURCE_GROUP}"
az role assignment create \
  --role "Network Contributor" \
  --assignee "$USER_ASSIGNED_CLIENT_ID" \
  --scope "/subscriptions/${AZURE_SUBSCRIPTION_ID}/resourcegroups/${AKS_RG}"

使用 AKS 集群的 OIDC endpoint 为 CAA ServiceAccount 创建联合凭证:

export AKS_OIDC_ISSUER="$(az aks show \
  --name "${CLUSTER_NAME}" \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --query "oidcIssuerProfile.issuerUrl" \
  -otsv)"
az identity federated-credential create \
  --name "${CLUSTER_NAME}-federated" \
  --identity-name "${AZURE_WORKLOAD_IDENTITY_NAME}" \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --issuer "${AKS_OIDC_ISSUER}" \
  --subject system:serviceaccount:confidential-containers-system:cloud-api-adaptor \
  --audience api://AzureADTokenExchange

部署 CAA Helm Chart

说明: 如果你的 Kubernetes 集群使用 Calico Container Network Interface (CNI),请先按说明为所有工作负载间流量配置 Virtual Extensible LAN (VXLAN) 封装。

下载 CAA Helm 部署资源

export CAA_VERSION="0.17.0"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/tags/v${CAA_VERSION}.tar.gz"
tar -xvzf "v${CAA_VERSION}.tar.gz"
cd "cloud-api-adaptor-${CAA_VERSION}/src/cloud-api-adaptor/install/charts/peerpods"
export CAA_BRANCH="main"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/heads/${CAA_BRANCH}.tar.gz"
tar -xvzf "${CAA_BRANCH}.tar.gz"
cd "cloud-api-adaptor-${CAA_BRANCH}/src/cloud-api-adaptor/install/charts/peerpods"

假如你已在本地准备好代码,请在终端中切换到 Cloud API Adaptor 的代码目录。

导出 PodVM 镜像版本

导出 peer pods 所用的 PodVM 镜像 ID。该变量告诉部署工具在 Azure 中创建 peer pod 虚拟机时应使用哪个 PodVM 镜像版本。

镜像来自 CoCo 社区镜像库(或由你手动构建),并且必须与当前 CAA 发布版本匹配。

导出以下环境变量,作为 peer pod VM 使用的镜像:

export AZURE_IMAGE_ID="/CommunityGalleries/cococommunity-42d8482d-92cd-415b-b332-7648bd978eff/Images/peerpod-podvm-fedora/Versions/${CAA_VERSION}"

自动化任务会在每天 00:00 UTC 构建一次 PodVM 镜像。你可以导出以下环境变量来使用该镜像:

SUCCESS_TIME=$(curl -s \
  -H "Accept: application/vnd.github+json" \
  "https://api.github.com/repos/confidential-containers/cloud-api-adaptor/actions/workflows/azure-nightly-build.yml/runs?status=success" \
  | jq -r '.workflow_runs[0].updated_at')

export AZURE_IMAGE_ID="/CommunityGalleries/cocopodvm-d0e4f35f-5530-4b9c-8596-112487cdea85/Images/podvm_image0/Versions/$(date -u -jf "%Y-%m-%dT%H:%M:%SZ" "$SUCCESS_TIME" "+%Y.%m.%d" 2>/dev/null || date -d "$SUCCESS_TIME" +%Y.%m.%d)"

版本号格式为 YYYY.MM.DD,最新镜像对应今天或前一天的日期。

如果你修改了会影响 PodVM 镜像的 CAA 代码,并希望部署这些改动,请按这些说明构建 PodVM 镜像。 镜像构建完成后,将镜像 ID 导出到环境变量 AZURE_IMAGE_ID

导出 CAA 容器镜像路径

定义要部署的 Cloud API Adaptor(CAA)容器镜像。 这些变量指定了部署工具所要拉取和运行的 CAA 镜像及其架构专属 tag。 tag 与 CAA 发布版本对应,以确保与所选 PodVM 镜像和配置兼容。

导出以下环境变量以使用 CAA 最新发布镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"
export CAA_TAG="v${CAA_VERSION}-amd64"

导出以下环境变量,以使用每次合并到 main 后由 CAA CI 构建的镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"

你可以在这里找到适合需求的预构建镜像 tag。

export CAA_TAG=""

注意: 你也可以使用 latest tag,但不推荐这样做,因为它缺少版本控制,可能引入不可预期的更新,影响部署稳定性和可复现性。

如果你修改了 CAA 代码并希望部署这些改动,请按说明构建容器镜像。镜像构建完成后,导出环境变量 CAA_IMAGECAA_TAG

选择 peer-pods 机型

export AZURE_INSTANCE_SIZE="Standard_DC2as_v5"
export DISABLECVM="false"

更多 AMD SEV-SNP 机型可参考Azure 文档

export AZURE_INSTANCE_SIZE="Standard_DC2es_v6"
export DISABLECVM="false"

更多 Intel TDX 机型可参考Azure 文档

export AZURE_INSTANCE_SIZE="Standard_D2as_v5"
export DISABLECVM="true"

填充 providers/azure.yaml 文件

全部可用配置项可在以下两个位置找到:

运行以下命令更新 providers/azure.yaml 文件:

cat <<EOF > providers/azure.yaml
provider: azure
image:
  name: "${CAA_IMAGE}"
  tag: "${CAA_TAG}"
providerConfigs:
   azure:
      AZURE_IMAGE_ID: "${AZURE_IMAGE_ID}"
      AZURE_REGION: "${AZURE_REGION}"
      AZURE_RESOURCE_GROUP: "${AZURE_RESOURCE_GROUP}"
      AZURE_SUBNET_ID: "${AZURE_SUBNET_ID}"
      AZURE_SUBSCRIPTION_ID: "${AZURE_SUBSCRIPTION_ID}"
      AZURE_INSTANCE_SIZE: "${AZURE_INSTANCE_SIZE}"
      DISABLECVM: ${DISABLECVM}
EOF

在 Kubernetes 集群中部署 Helm Chart

  1. 创建由 Helm 管理的命名空间:

    kubectl apply -f - << EOF
    apiVersion: v1
    kind: Namespace
    metadata:
      name: confidential-containers-system
      labels:
        app.kubernetes.io/managed-by: Helm
      annotations:
        meta.helm.sh/release-name: peerpods
        meta.helm.sh/release-namespace: confidential-containers-system
    EOF
    
  2. 使用 kubectl 创建 Secret:

    所需 key 请参见 providers/azure-secrets.yaml.template

说明: 以下示例假定你使用工作负载身份(workload identity)进行认证,因此不需要提供 AZURE_CLIENT_SECRETAZURE_TENANT_ID

kubectl create secret generic my-provider-creds \
 -n confidential-containers-system \
 --from-literal=AZURE_CLIENT_ID="${USER_ASSIGNED_CLIENT_ID}" \
 --from-file=id_rsa.pub=${SSH_KEY}

说明: --from-file=id_rsa.pub=${SSH_KEY} 是可选项。它允许用户为排障目的通过 SSH 登录 PodVM。 该选项只对启用了调试功能的自定义 PodVM 镜像有效。预构建的 PodVM 镜像默认不启用 SSH 连接。

  1. 安装 Helm Chart:

    下面命令使用了 -f--set 这两个自定义选项,其含义可参考这里

    helm install peerpods . \
      -f providers/azure.yaml \
      --set secrets.mode=reference \
      --set secrets.existingSecretName=my-provider-creds \
      --set-json daemonset.podLabels='{"azure.workload.identity/use":"true"}' \
      --dependency-update \
      -n confidential-containers-system
    

说明: 上述示例假定你使用工作负载身份(workload identity)进行认证。
--set-json daemonset.podLabels='{{"azure.workload.identity/use":"true"}}' 参数在使用工作负载身份时才需要。

通用的 Peer Pods Helm Chart 部署说明也可参考这里

运行示例应用

确认 RuntimeClass 已创建

部署 Peer Pods Helm Chart 后,请确认已创建 runtimeclass

kubectl get runtimeclass

当你看到名为 kata-remoteruntimeclass 时,就说明部署成功。 成功输出类似如下:

$ kubectl get runtimeclass
NAME          HANDLER       AGE
kata-remote   kata-remote   7m18s

部署工作负载

创建一个 nginx deployment:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      runtimeClassName: kata-remote
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        imagePullPolicy: Always
EOF

确认 pod 已成功启动:

kubectl get pods -n default

你可以通过运行以下命令确认 peer pod VM 是否已经创建:

az vm list \
  --resource-group "${AZURE_RESOURCE_GROUP}" \
  --output table

此时你应该能看到与 pod nginx 对应的虚拟机。

说明: 如果遇到问题,请查看故障排查指南

PodVM 参考值

PodVM 镜像在构建过程中,会把预期的 PCR 测量值发布到 OCI 镜像仓库中。

前提条件

安装 ORAS 工具,以便从 OCI 镜像仓库拉取参考值;安装 GitHub CLI,以便验证构建来源。

验证构建来源

确认这些测量值是由官方仓库中可信的构建流程生成的。指定 --format=json 可以看到更详细的构建信息。

CAA_REPO="confidential-containers/cloud-api-adaptor"
OCI_REGISTRY="ghcr.io/${CAA_REPO}/measurements/azure/podvm:${CAA_VERSION}"
gh attestation verify -R "$CAA_REPO" "oci://${OCI_REGISTRY}"

获取参考值

这些 PCR 值可用于远程证明策略中,以校验 PodVM 镜像的完整性。

oras pull "$OCI_REGISTRY"
jq -r .measurements.sha256.pcr11 < measurements.json
0x58e8afdf5b105fc6b202eb8e537a9f1512a4b33cd5921171b518645a86ca5a75

清理

如果你希望清理整个环境,可以运行以下命令删除资源组:

az group delete \
  --name "${AZURE_RESOURCE_GROUP}" \
  --yes --no-wait

3 - GCP

在 GCP 上使用 Cloud API Adaptor (CAA) 的 Peer Pods Helm Chart

说明: 本文为英文文档的中文译版,英文原版请参见 GCP 示例(英文版)

本文将介绍如何在 Google Kubernetes Engine (GKE) 上配置 CAA(即 Peer Pods),具体包括:

  • 一个基于 GKE 的单工作节点 Kubernetes 集群
  • 运行在该 Kubernetes 集群上的 CAA
  • 一个由 CAA PodVM 支持的示例应用

前提条件

安装所需工具:

Google Cloud 项目:

  • 确保你已经创建了一个 Google Cloud 项目
  • 记录项目 ID(导出为 GCP_PROJECT_ID

GCP 准备工作

先完成 Google 认证,并选择要使用的项目:

export GCP_PROJECT_ID="YOUR_PROJECT_ID"
gcloud auth login
gcloud config set project ${GCP_PROJECT_ID}

启用所需 API:

gcloud services enable container.googleapis.com --project=${GCP_PROJECT_ID}

创建一个具备所需权限的服务账号:

gcloud iam service-accounts create peerpods \
  --description="Peerpods Service Account" \
  --display-name="Peerpods Service Account"

gcloud projects add-iam-policy-binding ${GCP_PROJECT_ID} \
  --member="serviceAccount:peerpods@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/compute.instanceAdmin.v1"

gcloud projects add-iam-policy-binding ${GCP_PROJECT_ID} \
  --member="serviceAccount:peerpods@${GCP_PROJECT_ID}.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountUser"

生成并保存凭证文件:

gcloud iam service-accounts keys create \
  ~/.config/gcloud/peerpods_application_key.json \
  --iam-account=peerpods@${GCP_PROJECT_ID}.iam.gserviceaccount.com
export GOOGLE_APPLICATION_CREDENTIALS=~/.config/gcloud/peerpods_application_key.json

配置后续要使用的其他环境变量。

设置区域:

export GCP_REGION="us-central1"

设置 PodVM 实例类型:

export PODVM_INSTANCE_TYPE="n2d-standard-4"
export DISABLECVM=false
export GCP_CONFIDENTIAL_TYPE="SEV" # SEV or SEV_SNP
export GCP_DISK_TYPE="pd-standard"
export PODVM_INSTANCE_TYPE="c3-standard-4"
export DISABLECVM=false
export GCP_CONFIDENTIAL_TYPE="TDX"
export GCP_DISK_TYPE="pd-balanced"
export PODVM_INSTANCE_TYPE="e2-medium"
export DISABLECVM=true
export GCP_CONFIDENTIAL_TYPE=""
export GCP_DISK_TYPE="pd-standard"

使用 GKE 部署 Kubernetes

使用 GKE 部署一个单节点 Kubernetes 集群:

gcloud container clusters create my-cluster \
  --zone ${GCP_REGION}-a \
  --machine-type "e2-standard-4" \
  --image-type UBUNTU_CONTAINERD \
  --num-nodes 1

为工作节点添加标签:

kubectl get nodes --selector='!node-role.kubernetes.io/master' -o name | \
xargs -I{} kubectl label {} node.kubernetes.io/worker=

配置 VPC 网络

我们需要确保默认 VPC 网络中已经放通 15150 端口:

gcloud compute firewall-rules create allow-port-15150 \
    --project=${GCP_PROJECT_ID} \
    --network=default \
    --allow=tcp:15150

在生产场景中,建议限制来源 IP 范围,以降低安全风险。例如,可以把来源范围限制为特定 IP 地址或 CIDR 段:

gcloud compute firewall-rules create allow-port-15150-restricted \
   --project=${GCP_PROJECT_ID} \
   --network=default \
   --allow=tcp:15150 \
   --source-ranges=[YOUR_EXTERNAL_IP]

部署 CAA Helm Chart

下载 CAA Helm 部署资源

export CAA_VERSION="0.17.0"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/tags/v${CAA_VERSION}.tar.gz"
tar -xvzf "v${CAA_VERSION}.tar.gz"
cd "cloud-api-adaptor-${CAA_VERSION}/src/cloud-api-adaptor/install/charts/peerpods"
export CAA_BRANCH="main"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/heads/${CAA_BRANCH}.tar.gz"
tar -xvzf "${CAA_BRANCH}.tar.gz"
cd "cloud-api-adaptor-${CAA_BRANCH}/src/cloud-api-adaptor/install/charts/peerpods"

此方式假定你已在本地准备好代码,请在终端中切换到 Cloud API Adaptor 的代码目录。

导出 PodVM 镜像版本

导出 peer pods 所用的 PodVM 镜像 ID。该变量告诉部署工具在 Google Cloud 中创建 peer pod 虚拟机时应使用哪个 PodVM 镜像版本。

镜像来自 CoCo 社区镜像库(或由你手动构建),并且必须与当前 CAA 发布版本匹配。

导出以下环境变量,作为 PodVM 使用的镜像:

export PODVM_IMAGE_ID="/projects/it-cloud-gcp-prod-osc-devel/global/images/fedora-mkosi-tee-amd-1-11-0"

最新构建没有预构建的 PodVM 镜像。你需要按说明构建 PodVM 镜像。镜像构建完成后,将镜像 ID 导出到环境变量 PODVM_IMAGE_ID

如果你修改了会影响 PodVM 镜像的 CAA 代码,并希望部署这些改动,请按说明构建 PodVM 镜像。镜像构建完成后,将镜像 ID 导出到环境变量 PODVM_IMAGE_ID

导出 CAA 容器镜像路径

定义要部署的 Cloud API Adaptor(CAA)容器镜像。 这些变量指定了部署工具所要拉取和运行的 CAA 镜像及其架构专属 tag。 tag 与 CAA 发布版本对应,以确保与所选 PodVM 镜像和配置兼容。

导出以下环境变量以使用 CAA 最新发布镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"
export CAA_TAG="v${CAA_VERSION}-amd64"

导出以下环境变量,以使用每次合并到 main 后由 CAA CI 构建的镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"

你可以在这里找到适合需求的预构建镜像 tag。

export CAA_TAG=""

注意: 你也可以使用 latest tag,但不推荐这样做,因为它缺少版本控制,可能引入不可预期的更新,影响部署稳定性和可复现性。

如果你修改了 CAA 代码并希望部署这些改动,请按这些说明构建容器镜像。镜像构建完成后,导出环境变量 CAA_IMAGECAA_TAG

填充 providers/gcp.yaml 文件

全部可用配置项可在以下两个位置找到:

运行以下命令更新 providers/gcp.yaml 文件:

cat <<EOF > providers/gcp.yaml
provider: gcp
image:
  name: "${CAA_IMAGE}"
  tag: "${CAA_TAG}"
providerConfigs:
  gcp:
    GCP_NETWORK: "global/networks/default"
    GCP_PROJECT_ID: "${GCP_PROJECT_ID}"
    GCP_ZONE: "${GCP_REGION}-a"
    GCP_MACHINE_TYPE: "${PODVM_INSTANCE_TYPE}"
    GCP_DISK_TYPE: "${GCP_DISK_TYPE}"
    PODVM_IMAGE_NAME: "${PODVM_IMAGE_ID}"
    GCP_CONFIDENTIAL_TYPE: "${GCP_CONFIDENTIAL_TYPE}"
    DISABLECVM: ${DISABLECVM}
EOF

在 Kubernetes 集群中部署 Helm Chart

  1. 创建由 Helm 管理的命名空间:

    kubectl apply -f - << EOF
    apiVersion: v1
    kind: Namespace
    metadata:
      name: confidential-containers-system
      labels:
        app.kubernetes.io/managed-by: Helm
      annotations:
        meta.helm.sh/release-name: peerpods
        meta.helm.sh/release-namespace: confidential-containers-system
    EOF
    
  2. 使用 kubectl 创建 Secret:

    所需 key 请参见 providers/gcp-secrets.yaml.template

    kubectl create secret generic my-provider-creds \
     -n confidential-containers-system \
     --from-file=GCP_CREDENTIALS="${GOOGLE_APPLICATION_CREDENTIALS}"
    
  3. 安装 Helm Chart:

    下面命令使用了 -f--set 这两个自定义选项,其含义可参考这里

    helm install peerpods . \
      -f providers/gcp.yaml \
      --set secrets.mode=reference \
      --set secrets.existingSecretName=my-provider-creds \
      --dependency-update \
      -n confidential-containers-system
    

通用的 Peer Pods Helm Chart 部署说明也可参考这里

运行示例应用

确认 RuntimeClass 已创建

部署 Peer Pods Helm Chart 后,请确认已创建 runtimeclass

kubectl get runtimeclass

当你看到名为 kata-remoteruntimeclass 时,就说明部署成功。 成功输出类似如下:

$ kubectl get runtimeclass
NAME          HANDLER       AGE
kata-remote   kata-remote   7m18s

部署工作负载

本示例展示了一个更完整的部署方式:结合 TEE、机密虚拟机以及 kata-remote RuntimeClass,演示如何部署示例 Pod 并在机密计算环境中安全获取机密。

准备 init data 配置

Peer Pods 现已支持 init data。你可以通过注解 io.katacontainers.config.hypervisor.cc_init_data 传入所需配置文件(aa.tomlcdh.tomlpolicy.rego)。下面给出配置和使用示例。

# initdata.toml
algorithm = "sha384"
version = "0.1.0"

[data]
"aa.toml" = '''
[token_configs]
[token_configs.coco_as]
url = 'http://127.0.0.1:8080'

[token_configs.kbs]
url = 'http://127.0.0.1:8080'
cert = """
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIUR/UNh13GFam4emgludtype/S9BIwDQYJKoZIhvcNAQEL
BQAwdTELMAkGA1UEBhMCQ04xETAPBgNVBAgMCFpoZWppYW5nMREwDwYDVQQHDAhI
YW5nemhvdTERMA8GA1UECgwIQUFTLVRFU1QxFDASBgNVBAsMC0RldmVsb3BtZW50
MRcwFQYDVQQDDA5BQVMtVEVTVC1IVFRQUzAeFw0yNDAzMTgwNzAzNTNaFw0yNTAz
MTgwNzAzNTNaMHUxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhaaGVqaWFuZzERMA8G
A1UEBwwISGFuZ3pob3UxETAPBgNVBAoMCEFBUy1URVNUMRQwEgYDVQQLDAtEZXZl
bG9wbWVudDEXMBUGA1UEAwwOQUFTLVRFU1QtSFRUUFMwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDfp1aBr6LiNRBlJUcDGcAbcUCPG6UzywtVIc8+comS
ay//gwz2AkDmFVvqwI4bdp/NUCwSC6ShHzxsrCEiagRKtA3af/ckM7hOkb4S6u/5
ewHHFcL6YOUp+NOH5/dSLrFHLjet0dt4LkyNBPe7mKAyCJXfiX3wb25wIBB0Tfa0
p5VoKzwWeDQBx7aX8TKbG6/FZIiOXGZdl24DGARiqE3XifX7DH9iVZ2V2RL9+3WY
05GETNFPKtcrNwTy8St8/HsWVxjAzGFzf75Lbys9Ff3JMDsg9zQzgcJJzYWisxlY
g3CmnbENP0eoHS4WjQlTUyY0mtnOwodo4Vdf8ZOkU4wJAgMBAAGjHjAcMBoGA1Ud
EQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAKW32spii
t2JB7C1IvYpJw5mQ5bhIlldE0iB5rwWvNbuDgPrgfTI4xiX5sumdHw+P2+GU9KXF
nWkFRZ9W/26xFrVgGIS/a07aI7xrlp0Oj+1uO91UhCL3HhME/0tPC6z1iaFeZp8Y
T1tLnafqiGiThFUgvg6PKt86enX60vGaTY7sslRlgbDr9sAi/NDSS7U1PviuC6yo
yJi7BDiRSx7KrMGLscQ+AKKo2RF1MLzlJMa1kIZfvKDBXFzRd61K5IjDRQ4HQhwX
DYEbQvoZIkUTc1gBUWDcAUS5ztbJg9LCb9WVtvUTqTP2lGuNymOvdsuXq+sAZh9b
M9QaC1mzQ/OStg==
-----END CERTIFICATE-----
"""
'''

"cdh.toml"  = '''
socket = 'unix:///run/confidential-containers/cdh.sock'
credentials = []

[kbc]
name = 'cc_kbc'
url = 'http://1.2.3.4:8080'
kbs_cert = """
-----BEGIN CERTIFICATE-----
MIIFTDCCAvugAwIBAgIBADBGBgkqhkiG9w0BAQowOaAPMA0GCWCGSAFlAwQCAgUA
oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATCjAwIBATB7MRQwEgYD
VQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDASBgNVBAcMC1NhbnRhIENs
YXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5jZWQgTWljcm8gRGV2aWNl
czESMBAGA1UEAwwJU0VWLU1pbGFuMB4XDTIzMDEyNDE3NTgyNloXDTMwMDEyNDE3
NTgyNlowejEUMBIGA1UECwwLRW5naW5lZXJpbmcxCzAJBgNVBAYTAlVTMRQwEgYD
VQQHDAtTYW50YSBDbGFyYTELMAkGA1UECAwCQ0ExHzAdBgNVBAoMFkFkdmFuY2Vk
IE1pY3JvIERldmljZXMxETAPBgNVBAMMCFNFVi1WQ0VLMHYwEAYHKoZIzj0CAQYF
K4EEACIDYgAExmG1ZbuoAQK93USRyZQcsyobfbaAEoKEELf/jK39cOVJt1t4s83W
XM3rqIbS7qHUHQw/FGyOvdaEUs5+wwxpCWfDnmJMAQ+ctgZqgDEKh1NqlOuuKcKq
2YAWE5cTH7sHo4IBFjCCARIwEAYJKwYBBAGceAEBBAMCAQAwFwYJKwYBBAGceAEC
BAoWCE1pbGFuLUIwMBEGCisGAQQBnHgBAwEEAwIBAzARBgorBgEEAZx4AQMCBAMC
AQAwEQYKKwYBBAGceAEDBAQDAgEAMBEGCisGAQQBnHgBAwUEAwIBADARBgorBgEE
AZx4AQMGBAMCAQAwEQYKKwYBBAGceAEDBwQDAgEAMBEGCisGAQQBnHgBAwMEAwIB
CDARBgorBgEEAZx4AQMIBAMCAXMwTQYJKwYBBAGceAEEBEDDhCejDUx6+dlvehW5
cmmCWmTLdqI1L/1dGBFdia1HP46MC82aXZKGYSutSq37RCYgWjueT+qCMBE1oXDk
d1JOMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0B
AQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBA4ICAQACgCai9x8DAWzX/2IelNWm
ituEBSiq9C9eDnBEckQYikAhPasfagnoWFAtKu/ZWTKHi+BMbhKwswBS8W0G1ywi
cUWGlzigI4tdxxf1YBJyCoTSNssSbKmIh5jemBfrvIBo1yEd+e56ZJMdhN8e+xWU
bvovUC2/7Dl76fzAaACLSorZUv5XPJwKXwEOHo7FIcREjoZn+fKjJTnmdXce0LD6
9RHr+r+ceyE79gmK31bI9DYiJoL4LeGdXZ3gMOVDR1OnDos5lOBcV+quJ6JujpgH
d9g3Sa7Du7pusD9Fdap98ocZslRfFjFi//2YdVM4MKbq6IwpYNB+2PCEKNC7SfbO
NgZYJuPZnM/wViES/cP7MZNJ1KUKBI9yh6TmlSsZZOclGJvrOsBZimTXpATjdNMt
cluKwqAUUzYQmU7bf2TMdOXyA9iH5wIpj1kWGE1VuFADTKILkTc6LzLzOWCofLxf
onhTtSDtzIv/uel547GZqq+rVRvmIieEuEvDETwuookfV6qu3D/9KuSr9xiznmEg
xynud/f525jppJMcD/ofbQxUZuGKvb3f3zy+aLxqidoX7gca2Xd9jyUy5Y/83+ZN
bz4PZx81UJzXVI9ABEh8/xilATh1ZxOePTBJjN7lgr0lXtKYjV/43yyxgUYrXNZS
oLSG2dLCK9mjjraPjau34Q==
-----END CERTIFICATE-----
"""
'''

"policy.rego" = '''
package agent_policy

import future.keywords.in
import future.keywords.every

import input

# Default values, returned by OPA when rules cannot be evaluated to true.
default CopyFileRequest := true
default CreateContainerRequest := true
default CreateSandboxRequest := true
default DestroySandboxRequest := true
default ExecProcessRequest := false
default GetOOMEventRequest := true
default GuestDetailsRequest := true
default OnlineCPUMemRequest := true
default PullImageRequest := true
default ReadStreamRequest := false
default RemoveContainerRequest := true
default RemoveStaleVirtiofsShareMountsRequest := true
default SignalProcessRequest := true
default StartContainerRequest := true
default StatsContainerRequest := true
default TtyWinResizeRequest := true
default UpdateEphemeralMountsRequest := true
default UpdateInterfaceRequest := true
default UpdateRoutesRequest := true
default WaitProcessRequest := true
default WriteStreamRequest := false
'''

请确认策略正确,并且 KBC URL 已指向你的 Key Broker Service。

然后对 initdata.toml 进行编码并保存为变量:

INITDATA=$(cat initdata.toml | gzip | base64 -w0)

使用以下命令部署 Pod:

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: example-pod
  annotations:
    io.katacontainers.config.hypervisor.cc_init_data: "$INITDATA"
spec:
  runtimeClassName: kata-remote
  containers:
    - name: example-container
      image: alpine:latest
      command:
        - sleep
        - "3600"
      securityContext:
        privileged: false
        seccompProfile:
          type: RuntimeDefault
EOF

从 Trustee 获取机密

Pod 成功携带 initdata 部署后,你可以在 Pod 内部从 Trustee 服务获取机密。使用以下命令获取指定机密:

kubectl exec -it example-pod -- curl http://127.0.0.1:8006/cdh/resource/default/kbsres1/key1

本示例是最基础的部署验证,用于确认 Helm Chart 是否已在云厂商侧成功启动 PodVM。

创建一个 nginx deployment:

cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 1
  template:
    metadata:
      labels:
        app: nginx
    spec:
      runtimeClassName: kata-remote
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
        imagePullPolicy: Always
EOF

确认 pod 已成功启动:

kubectl get pods -n default

你可以通过运行以下命令确认 PodVM 是否已经创建:

gcloud compute instances list

此时你应该能看到与上述示例 Pod 对应的虚拟机。

清理

删除所有使用 kata-remote RuntimeClass 运行的 Pod。可以使用以下命令:

kubectl get pods -A -o custom-columns='NAME:.metadata.name,NAMESPACE:.metadata.namespace,RUNTIMECLASS:.spec.runtimeClassName' | grep kata-remote | awk '{print $1, $2}'

确认所有 peer-pod VM 都已删除。你可以使用以下命令列出所有 peer-pod VM(名称前缀为 podvm)及其状态:

gcloud compute instances list \
  --filter="name~'podvm.*'" \
  --format="table(name,zone,status)"

运行以下命令删除 GKE 集群:

gcloud container clusters delete my-cluster --zone ${GCP_REGION}-a

故障排查

说明: 如果你的问题不在下面的范围内,请查看这里的故障排查指南。

虚拟机未启动

从 GKE 1.27 开始,GCP 会为 containerd 配置 discard_unpacked_layers=true,以节省磁盘空间(移除解包后的压缩镜像层)。但这可能会导致 PeerPods 出现问题,因为工作负载可能找不到所需镜像层。 为避免这个问题,请在 containerd 配置中禁用 discard_unpacked_layers

大多数情况下,你会看到类似下面的通用报错:

Error: failed to create containerd container: error unpacking image: failed to extract layer sha256:<SHA>: failed to get reader from content store: content digest sha256:<SHA>: not found

要在 Google Kubernetes Engine (GKE) 1.27 及以上版本中禁用 containerd 配置里的 discard_unpacked_layers,请按以下步骤操作:

  1. Google Console 中 SSH 登录工作节点
  2. 运行命令 sudo sed -i 's/discard_unpacked_layers = true/discard_unpacked_layers = false/' /etc/containerd/config.toml
  3. 运行 sudo cat /etc/containerd/config.toml | grep discard_unpacked_layers 验证修改结果
  4. 重启 containerd:sudo systemctl restart containerd

4 - 阿里云

在阿里云上部署 Cloud API Adaptor (CAA)

说明: 本文为英文文档的中文译版,英文原版请参见阿里云示例(英文版)

本文将介绍如何在阿里云容器服务 Kubernetes 版(ACK)和阿里云弹性计算服务(ECS)上部署 CAA(即 Peer Pods),具体包括:

  • ACK 托管集群中的一个工作节点
  • 运行在该 Kubernetes 集群上的 CAA
  • 一个由运行在 ECS 上 CAA PodVM 支撑的 Nginx Pod

说明: 请在目录 src/cloud-api-adaptor 下运行以下命令。

说明: 当前机密计算实例已在部分地域提供。

说明: 阿里云官方文档可参考这里

前提条件

安装所需工具:

创建 PodVM 镜像

说明:cn-hongkong 地域中,版本 0.22.0 已提供一个预构建的社区镜像(id: m-j6c3kfz2vhu6ze04wacb),可直接用于测试。 也可以使用下方导出 PodVM 镜像版本中列出的各地域镜像 ID。

如果你希望自行构建 PodVM 镜像,请按以下步骤操作。当前 PodVM 构建使用 mkosi,目标系统为 Ubuntu 26.04resolute)。前提条件和自定义选项请参见 PodVM README

  1. 构建 Ubuntu 26.04 PodVM 镜像。

    cd podvm
    make
    

    此命令将构建 PodVM 二进制文件和操作系统镜像,生成的 qcow2 文件路径为:

    podvm/build/podvm-ubuntu-amd64.qcow2
    

    若二进制文件已存在,仅重新构建操作系统镜像:

    cd podvm
    make image
    
  2. 上传到 OSS,并创建 ECS 镜像。

    src/cloud-api-adaptor 目录下,将 qcow2 文件上传到 OSS(对象存储服务):

    cd ..
    export REGION_ID=<region-id>
    export IMAGE_FILE=podvm/build/podvm-ubuntu-amd64.qcow2
    export BUCKET=<OSS-bucket-name>
    export OBJECT=<object-name>
    
    aliyun oss cp ${IMAGE_FILE} oss://${BUCKET}/${OBJECT}
    

    然后将该镜像文件导入为 ECS 镜像:

    export IMAGE_NAME=$(basename ${IMAGE_FILE%.*})
    aliyun ecs ImportImage --ImageName ${IMAGE_NAME} \
        --region ${REGION_ID} --RegionId ${REGION_ID} \
        --BootMode UEFI \
        --DiskDeviceMapping.1.OSSBucket ${BUCKET} --DiskDeviceMapping.1.OSSObject ${OBJECT} \
        --Features.NvmeSupport supported \
        --method POST --force
    
    export POD_IMAGE_ID=<ImageId>
    

构建 CAA 开发镜像

如果你希望自行构建 CAA DaemonSet 镜像:

export registry=<registry-address>
export RELEASE_BUILD=true
export CLOUD_PROVIDER=alibabacloud
make image

请记录该镜像使用的 tag,后续会用到。

使用 ACK 托管集群部署 Kubernetes

  1. 创建 ACK 托管集群。

    export CONTAINER_CIDR=172.18.0.0/16
    export REGION_ID=cn-beijing
    export ZONES='["cn-beijing-i"]'
    
    aliyun cs CreateCluster --header "Content-Type=application/json" --body "
    {
      \"cluster_type\":\"ManagedKubernetes\",
      \"name\":\"caa\",
      \"region_id\":\"${REGION_ID}\",
      \"zone_ids\":${ZONES},
      \"enable_rrsa\":true,
      \"container_cidr\":\"${CONTAINER_CIDR}\",
      \"addons\":[
        {
          \"name\":\"flannel\"
        }
      ]
    }"
    
    export CLUSTER_ID=<cluster-id>
    export SECURITY_GROUP_ID=$(aliyun cs DescribeClusterDetail --ClusterId ${CLUSTER_ID} | jq -r ".security_group_id")
    

    等待集群创建完成。获取该集群的 vSwitch ID,然后为集群添加一个工作节点。

  2. 为集群所在 VPC 添加公网访问能力。

    export VPC_ID=$(aliyun cs DescribeClusterDetail --ClusterId ${CLUSTER_ID} | jq -r ".vpc_id")
    export VSWITCH_ID=$(echo ${VSWITCH_IDS} | sed 's/[][]//g' | sed 's/"//g')
    aliyun vpc CreateNatGateway \
      --region ${REGION_ID} \
      --RegionId ${REGION_ID} \
      --VpcId ${VPC_ID} \
      --NatType Enhanced \
      --VSwitchId ${VSWITCH_ID} \
      --NetworkType internet
    
    export GATEWAY_ID="<NatGatewayId>"
    export SNAT_TABLE_ID="<SnatTableId>"
    
    # 公网 IP 带宽(Mbps)
    export BAND_WIDTH=5
    aliyun vpc AllocateEipAddress \
      --region ${REGION_ID} \
      --RegionId ${REGION_ID} \
      --Bandwidth ${BAND_WIDTH}
    
    export EIP_ID="<AllocationId>"
    export EIP_ADDRESS="<EipAddress>"
    
    aliyun vpc AssociateEipAddress \
      --region ${REGION_ID} \
      --RegionId ${REGION_ID} \
      --AllocationId ${EIP_ID} \
      --InstanceId ${GATEWAY_ID} \
      --InstanceType Nat
    
    aliyun vpc CreateSnatEntry \
      --region ${REGION_ID} \
      --RegionId ${REGION_ID} \
      --SnatTableId ${SNAT_TABLE_ID} \
      --SourceVSwitchId ${VSWITCH_ID} \
      --SnatIp ${EIP_ADDRESS}
    
  3. 授予角色权限。

    为集群工作节点授予相应角色权限,使其能够创建 ECS 实例。

部署 CAA Helm Chart

下载 CAA Helm 部署资源

export CAA_VERSION="0.22.0"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/tags/v${CAA_VERSION}.tar.gz"
tar -xvzf "v${CAA_VERSION}.tar.gz"
cd "cloud-api-adaptor-${CAA_VERSION}/src/cloud-api-adaptor/install/charts/peerpods"
export CAA_BRANCH="main"
curl -LO "https://github.com/confidential-containers/cloud-api-adaptor/archive/refs/heads/${CAA_BRANCH}.tar.gz"
tar -xvzf "${CAA_BRANCH}.tar.gz"
cd "cloud-api-adaptor-${CAA_BRANCH}/src/cloud-api-adaptor/install/charts/peerpods"

假如你已在本地准备好代码,请在终端中切换到 Cloud API Adaptor 的代码目录。

导出 PodVM 镜像版本

导出 peer pods 所用的 PodVM 镜像 ID。该变量告诉部署工具在阿里云中创建 peer pod 虚拟机时应使用哪个 PodVM 镜像版本。

export IMAGEID="m-j6c3kfz2vhu6ze04wacb"

说明: 阿里云会预先构建这些镜像,不同地域使用不同的镜像 ID。

region IMAGEID
cn-beijing m-2ze2vxvrxsbue3sf8b02
cn-hongkong m-j6c3kfz2vhu6ze04wacb
cn-hangzhou m-bp146ws6x3iyuwjvbdw2
ap-southeast-1 m-t4ng1w8ipua3c0o57hor

导出 CAA 容器镜像路径

定义要部署的 Cloud API Adaptor(CAA)容器镜像。 这些变量指定了部署工具所要拉取和运行的 CAA 镜像及其架构专属 tag。 tag 与 CAA 发布版本对应,以确保与所选 PodVM 镜像和配置兼容。

导出以下环境变量以使用 CAA 最新发布镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"
export CAA_TAG="v${CAA_VERSION}-amd64"

导出以下环境变量,以使用每次合并到 main 后由 CAA CI 构建的镜像:

export CAA_IMAGE="quay.io/confidential-containers/cloud-api-adaptor"

你可以在这里找到适合需求的预构建镜像 tag。

export CAA_TAG=""

注意: 你也可以使用 latest tag,但不推荐这样做,因为它缺少版本控制,可能引入不可预期的更新,影响部署稳定性和可复现性。

如果你修改了 CAA 代码并希望部署这些改动,请按说明构建容器镜像。镜像构建完成后,导出环境变量 CAA_IMAGECAA_TAG

选择 peer-pods 机型

export PODVM_INSTANCE_TYPE="ecs.g8i.xlarge"
export DISABLECVM="false"

说明: 更多支持机密计算的实例规格,请参考官方文档

填充 providers/alibabacloud.yaml 文件

全部可用配置项可在以下两个位置找到:

运行以下命令更新 providers/alibabacloud.yaml 文件:

cat <<EOF > providers/alibabacloud.yaml
provider: alibabacloud
image:
  name: "${CAA_IMAGE}"
  tag: "${CAA_TAG}"
providerConfigs:
   alibabacloud:
      IMAGEID: "${IMAGEID}"
      REGION: "${REGION_ID}"
      SECURITY_GROUP_IDS: "${SECURITY_GROUP_ID}"
      VSWITCH_ID: "${VSWITCH_ID}"
      DISABLECVM: ${DISABLECVM}
alibabacloud:
  rrsa:
    enable: true
EOF

说明: 如果你不使用 RRSA 进行认证,请将 yaml 中的 alibabacloud.rrsa.enable 改为 false

在 Kubernetes 集群中部署 Helm Chart

  1. 创建由 Helm 管理的命名空间:

    kubectl apply -f - << EOF
    apiVersion: v1
    kind: Namespace
    metadata:
      name: confidential-containers-system
      labels:
        app.kubernetes.io/managed-by: Helm
      annotations:
        meta.helm.sh/release-name: peerpods
        meta.helm.sh/release-namespace: confidential-containers-system
    EOF
    
  2. 使用 kubectl 创建 Secret:

    所需 key 请参见 providers/alibabacloud-secrets.yaml.template

    说明: 以下示例假定你使用 RRSA 进行认证,因此不需要提供 ALIBABACLOUD_ACCESS_KEY_IDALIBABACLOUD_ACCESS_KEY_SECRET,而是提供 ALIBABA_CLOUD_ROLE_ARNALIBABA_CLOUD_OIDC_PROVIDER_ARN

    kubectl create secret generic my-provider-creds \
    -n confidential-containers-system \
    --from-literal=ALIBABA_CLOUD_ROLE_ARN=${ALIBABA_CLOUD_ROLE_ARN} \
    --from-literal=ALIBABA_CLOUD_OIDC_PROVIDER_ARN=${ALIBABA_CLOUD_OIDC_PROVIDER_ARN} \
    --from-literal=ALIBABA_CLOUD_OIDC_TOKEN_FILE=/var/run/secrets/ack.alibabacloud.com/rrsa-tokens/token
    
  3. 安装 Helm Chart:

    下面命令使用了 -f--set 这两个自定义选项,其含义可参考这里

    helm install peerpods . \
      -f providers/alibabacloud.yaml \
      --set secrets.mode=reference \
      --set secrets.existingSecretName=my-provider-creds \
      --dependency-update \
      -n confidential-containers-system
    

通用的 Peer Pods Helm Chart 部署说明也可参考这里

运行示例应用

确认 runtimeclass 已创建

部署 CAA 后,请确认 runtimeclass 已创建:

kubectl get runtimeclass

当你看到名为 kata-remoteRuntimeClass 时,就说明部署成功。成功输出类似如下:

$ kubectl get runtimeclass
NAME          HANDLER       AGE
kata-remote   kata-remote   7m18s

部署工作负载

创建一个 nginx deployment:

echo '
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  runtimeClassName: kata-remote
  containers:
  - name: nginx
    image: registry.openanolis.cn/openanolis/nginx:1.14.1-8.6
' | kubectl apply -f -

确认 pod 已成功启动:

kubectl get pods -n default

你可以通过运行以下命令确认 peer-pod VM 是否已经创建:

aliyun ecs DescribeInstances --RegionId ${REGION_ID} --InstanceName 'podvm-*'

此时你应该能看到与 pod nginx 对应的虚拟机。 如果遇到问题,请查看故障排查指南

远程证明

TODO

清理

删除所有使用 runtimeClass kata-remote 运行的 Pod。

确认所有 peer-pod VM 都已删除。你可以使用以下命令列出所有 peer-pod VM(名称前缀为 podvm)及其状态:

aliyun ecs DescribeInstances --RegionId ${REGION_ID} --InstanceName 'podvm-*'

运行以下命令删除 ACK 集群:

aliyun cs DELETE /clusters/${CLUSTER_ID} --region ${REGION_ID} --keep_slb false --retain_all_resources false --header "Content-Type=application/json;" --body "{}"