- 操作系统
- 云原生
- 安全
【免费下载链接】bottlerocket
An operating system designed for hosting containers
Bottlerocket 是一个专为托管容器而设计的开源 Linux 操作系统,其首个发布版本即聚焦于 Kubernetes,尤其是作为 Kubernetes Pod 的宿主操作系统。本文以仓库中的 QUICKSTART-EKS.md 为骨架,完整讲解两种将 Bottlerocket AMI 接入 Amazon EKS 集群的路径:一是借助eksctl完成自动化集群与节点创建,二是完全手动的方式(从查询 AMI、生成 user-data、配置 IAM 角色到最终用run-instances启动实例)。读完本文,你将掌握 Bottlerocket EKS 变体的选择规则、SSM 参数获取 AMI 的方法、TOML 格式用户数据(user data)的编写方式,以及 NVIDIA 与 Neuron 加速实例的启用配置。
背景:为什么用 Bottlerocket 作为 EKS 节点
Bottlerocket 的设计目标是“只包含运行容器所需的最小集”,并把安全与可维护性放在首位。从 README.md 可以看到,其基础系统仅包含运行容器所必需的组件,Bottlerocket 自身的增量工作集中在可靠的更新机制与API 配置模型上:
- 系统配置通过 API 调用完成,而不是手工修改文件;
- 配置项在版本更新时会被自动迁移;
- 更新基于分区翻转(partition flip)机制,快速且可靠。
在 AWS EKS 场景下,Bottlerocket 以 AMI 的形式提供,天然适合作为托管 Kubernetes 集群的工作节点。本文所有示例均以 EKS 为主线,但如文档所述,"There's nothing that limits Bottlerocket to EKS or AWS"——Bottlerocket 并不局限于 EKS 或 AWS,仓库中还提供了 Amazon ECS、VMware 与裸金属(PROVISIONING-METAL.md)等接入路径。
环境依赖
在开始之前,需要准备以下命令行工具:
| 工具 | 用途 | 说明 |
|---|---|---|
eksctl | 创建和管理 EKS 集群 | 从0.15.0-rc.2开始原生支持 Bottlerocket,建议下载最新版本以获得完整支持 |
kubectl | 管理 Kubernetes 集群与运行 Pod | 在集群搭建期间补充eksctl,之后用于运行工作负载 |
aws-cli | 与 AWS 交互 | 需要支持 EKS 的较新版本(v1 系列中的较新发布版) |
大部分流程都是一次性设置,仓库文档也明确说明计划后续进一步自动化。一旦集群创建完成,之后的操作可以直接跳到最后的"Launch!"(启动)步骤。
路径一:使用 eksctl 的自动化设置
只要eksctl版本满足要求,Bottlerocket 在 EKS 中的大部分设置都是自动化的。
集群设置配置文件
eksctl支持通过配置文件简化集群创建。仓库根目录提供了两个可直接使用的示例文件:
- sample-eksctl.yaml:适用于大多数场景(推荐)。
- sample-eksctl-ssh.yaml:适用于确定需要 SSH 访问的测试集群,使用时务必把
publicKeyName改为你在 EC2 中注册的密钥对名称。
以 sample-eksctl.yaml 为例,其核心结构如下:
apiVersion: eksctl.io/v1alpha5 kind: ClusterConfig metadata: name: bottlerocket region: us-west-2 version: '1.24' nodeGroups: - name: ng-bottlerocket instanceType: m5.large desiredCapacity: 4 amiFamily: Bottlerocket disableIMDSv1: true iam: attachPolicyARNs: - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore bottlerocket: settings: motd: "Hello from eksctl!"使用方法:复制一份,例如命名为my-eksctl.yaml,然后按需修改:
- 节点数量:修改
desiredCapacity(示例中为 4 个节点)。 - Bottlerocket 设置:
bottlerocket.settings段可以预先写入任意 Bottlerocket settings(例如示例中的motd)。 - AWS 区域:配置文件中包含 region 字段,若你不在
us-west-2运行,请同步修改。
关于 eksctl 配置文件的完整 schema 与官方示例,可参考 eksctl 官方文档;eksctl create cluster会自动选择与集群 Kubernetes 版本匹配的 Bottlerocket AMI,无需手工指定。
版本提示:仓库示例文件中的 Kubernetes
version为'1.24',而当前仓库支持的 EKS 变体为aws-k8s-1.31至aws-k8s-1.37(详见下文"变体选择"一节),实际使用时建议将version更新为与你的集群规划一致的较新版本,并同步确认eksctl对该版本的支持情况。
创建集群
指向你修改好的配置文件,执行:
eksctl create cluster --config-file ./my-eksctl.yaml该命令会花几分钟时间创建 EKS 集群并拉起 Bottlerocket 工作节点。
可选集群配置
CSI 插件(持久化存储)
如果需要在 Bottlerocket 主机上创建持久卷(Persistent Volume),必须使用 EBS CSI Plugin。原因是 EKS 默认的 EBS 驱动依赖的文件系统工具并未包含在 Bottlerocket 镜像中。AWS EKS 用户指南中有关于使用该驱动创建 StorageClass 的完整演练,可按官方文档操作。
conntrack 配置
默认情况下,kube-proxy会把nf_conntrack_max内核参数设置为一个可能与 Bottlerocket 启动时设定的默认值不同的值。如果你希望保留 Bottlerocket 的默认设置,可以编辑 kube-proxy-config 这个 ConfigMap:
kubectl edit -n kube-system cm/kube-proxy-config将conntrack的maxPerCore与min字段改为 0(0 表示不改变):
conntrack: maxPerCore: 0 min: 0 tcpCloseWaitTimeout: 1h0m0s tcpEstablishedTimeout: 24h0m0s完成
Bottlerocket 实例会被放置在一个自动扩缩组(ASG)中,数量与你配置文件中指定的节点数一致(之后也可以通过 AWS 控制台按常规方式调整该 ASG 的大小)。实例会自动注册到eksctl创建的 EKS 集群中,之后你可以完全使用常规的 Kubernetes 工具(如kubectl)管理集群与 Bottlerocket 节点。例如运行一个简单的 busybox Pod:
kubectl run -i -t busybox --image=busybox --restart=Never路径二:手动设置(完全掌控)
如果你需要eksctl目前尚不能提供的更高控制粒度,或想了解底层到底发生了什么,可以按下面的手动步骤操作。手动路径的完整信息收集流程包括:查询 AMI → 创建集群 → 生成集群信息 → 配置 IAM → 调整 kube-proxy → 最后启动实例。
查找 AMI
官方 AMI ID 存储在 AWS Systems Manager 公共参数(public SSM parameters) 中,参数名形如:
/aws/service/bottlerocket/aws-k8s-1.32/x86_64/latest/image_id只需把其中的变体名(如aws-k8s-1.32)和架构(x86_64)替换为你要使用的值。关于支持哪些变体与架构,见 README.md;对于 SSM 参数而言,合法的架构名为x86_64和arm64(即aarch64)。如果知道具体版本(例如1.11.0),可以把参数名中的latest替换为版本号。
变体选择规则
结合仓库 README.md 与 variants 目录,当前仓库中支持 EKS 的变体包括:
- 常规 K8s 变体:
aws-k8s-1.31、aws-k8s-1.32、aws-k8s-1.33、aws-k8s-1.34、aws-k8s-1.35、aws-k8s-1.36、aws-k8s-1.37; - NVIDIA 变体:上述各版本对应的
aws-k8s-*-nvidia(变体名追加-nvidia后缀,例如aws-k8s-1.32-nvidia)。
文档中同时说明:所有 Bottlerocket EKS 变体(
v1.30.0+)都支持 Neuron 实例类型。以 Kubernetes 1.28 为例,支持 Neuron 的变体即为aws-k8s-1.28(此处为文档示例;当前仓库中最低 K8s 变体为 1.31,旧版本已不再维护)。
以某个变体的 Cargo 清单为例,variants/aws-k8s-1.32/Cargo.toml 展示了该变体实际打包的内容,可帮助你理解"变体"在构建层面的含义——它决定了一组被包含的软件包(included-packages)、镜像特性(image-features)与内核启动参数:
[package.metadata.build-variant.image-features] grub-set-private-var = true uefi-secure-boot = true xfs-data-partition = true erofs-root-partition = true systemd-networkd = true [package.metadata.build-variant] included-packages = [ "release", "kernel-6.1", "whippet", "cni", "cni-plugins", "kubelet-1.32", "aws-iam-authenticator", "soci-snapshotter", ] kernel-parameters = [ "console=tty0", "console=ttyS0,115200n8", "net.ifnames=0", "netdog.default-interface=eth0:dhcp4,dhcp6?", "quiet", ]直接使用 SSM 参数
拿到参数名后,最简单的用法是把它直接传给 EC2(同样适用于 CloudFormation 等代你启动 EC2 实例的服务)——只要在参数名前加resolve:ssm:前缀,EC2 就会替你获取当前值。例如:
resolve:ssm:/aws/service/bottlerocket/aws-k8s-1.32/x86_64/latest/image_id手动查询 SSM
如果你希望自己获取 AMI ID,可以使用 aws-cli。以us-west-2区域查询上文示例参数为例:
aws ssm get-parameter --region us-west-2 --name "/aws/service/bottlerocket/aws-k8s-1.32/x86_64/latest/image_id" --query Parameter.Value --output text如果安装了jq并且想要更多信息,可以同时查询 image_id 与 image_version:
aws ssm get-parameters --region us-west-2 \ --names "/aws/service/bottlerocket/aws-k8s-1.32/x86_64/latest/image_id" \ "/aws/service/bottlerocket/aws-k8s-1.32/x86_64/latest/image_version" \ --output json | jq -r '.Parameters | .[] | "\(.Name): \(.Value) (updated \(.LastModifiedDate | gmtime | strftime("%c")) UTC)"'集群设置
注意:以下大多数命令都带 region 参数,如果你不想在
us-west-2部署,请相应修改。另外,在GovCloud环境中操作时,IAM ARN 需要把arn:aws:iam改为arn:aws-us-gov:iam。例如arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy需改为arn:aws-us-gov:iam::aws:policy/AmazonEKSWorkerNodePolicy。
创建集群:
eksctl create cluster --region us-west-2 --name bottlerocket该命令会自动添加一个 "context",让kubectl知道如何与集群交互,并将其设为默认 context。你可以用kubectl config get-contexts查看所有 context(集群),用kubectl config use-context 'NEW-CONTEXT-HERE'切换当前 context。
集群信息收集
这一节帮助你确定后续实例启动命令所需的一些集群信息。
Kubernetes 集群信息
Bottlerocket 使用TOML 格式的配置文件作为 user data(用户数据),其中可以包含刚创建的 Kubernetes 集群配置。运行下面的命令生成包含 API endpoint 与 base64 编码的证书颁发机构(CA)的配置文件:
eksctl get cluster --region us-west-2 --name bottlerocket -o json \ | jq --raw-output '.[] | "[settings.kubernetes]\napi-server = \"" + .Endpoint + "\"\ncluster-certificate =\"" + .CertificateAuthority.Data + "\"\ncluster-name = \"bottlerocket\""' > user-data.toml这会把 TOML 格式的配置数据保存到名为user-data.toml的文件中,供最后一步的实例启动命令使用。
生成的 TOML 内容实质是向settings.kubernetes写入集群接入信息。这一点与源码中 aws-k8s 变体的设置模型一致——sources/settings-plugins/aws-k8s/src/lib.rs 定义了该变体可配置的kubernetes设置结构,而 sources/settings-defaults/aws-k8s-1.32/defaults.d/50-kubernetes-aws.toml 则给出了该设置的默认值(例如cluster-domain = "cluster.local"、authentication-mode = "aws"、server-tls-bootstrap = true、cloud-provider = "aws")。这说明手动流程写入的api-server、cluster-certificate、cluster-name会与变体自带的默认 Kubernetes 设置共同生效。
子网信息
接下来运行下面的命令获取eksctl创建的子网信息,它会列出各子网并标明是公网还是私网:
aws ec2 describe-subnets \ --subnet-ids $(eksctl get cluster --region us-west-2 --name bottlerocket -o json | jq --raw-output '.[].ResourcesVpcConfig.SubnetIds[]') \ --region us-west-2 \ --query "Subnets[].[SubnetId, Tags[?Key=='aws:cloudformation:logical-id'].Value]" \ | xargs -L2选择一个子网,保存起来供最后的启动命令使用。选择公网还是私网:
- 私网(private):适合生产部署,工作节点隔离性最好。
- 公网(public):更适合调试实例。这些子网带 Internet Gateway,如果给实例加上公网 IP 就能直接访问它(之后也可以手动为私网子网添加 Internet Gateway,因此这个决定是可逆的)。
注意:如果使用公网子网,实例必须拥有可公开访问的 IP——要么在下面的启动命令中加--associate-public-ip-address,要么在启动后绑定一个弹性 IP(Elastic IP)。此外,如果希望在特定可用区(AZ)启动,请选择匹配的子网(AZ 就列在公网/私网状态旁边)。
IAM 角色
要启动的实例必须关联一个允许其与 EKS 和 ECR 通信的 IAM 角色。
eksctl默认已经在创建集群节点组(nodegroup)时创建了这样的角色(以及允许使用该角色的实例配置文件 instance profile)。
用以下命令获取 IAM 角色的 ARN:
eksctl get iamidentitymapping --region us-west-2 --cluster bottlerocket输出应类似:
ARN USERNAME GROUPS arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/INSTANCE_ROLE_NAME system:node:{{EC2PrivateDNSName}} system:bootstrappers,system:nodes记下其中的INSTANCE_ROLE_NAME供后续步骤使用。
启用 SSM
如果为角色添加 SSM 权限,就可以使用 Bottlerocket 默认的 SSM agent 在实例上获得 shell 会话。
为角色附加 SSM 权限的策略:
aws iam attach-role-policy \ --role-name INSTANCE_ROLE_NAME \ --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore如果收到下面的错误,说明INSTANCE_ROLE_NAME需要截断到 64 个字符以内(仓库文档注明正在改进这一限制):
1 validation error detected: Value 'INSTANCE_ROLE_NAME' at 'role Name' failed to satisfy constraint: Member must have length less than or equal to 64接下来,获取用于启动实例的实例配置文件名称:
aws iam list-instance-profiles-for-role --role-name INSTANCE_ROLE_NAME --query "InstanceProfiles[*].InstanceProfileName" --output text应该只有一个,形如:
eksctl-bottlerocket-nodegroup-ng-IDENTIFIER-NodeInstanceProfile-IDENTIFIER把它记为INSTANCE_PROFILE_NAME,用于最后的启动命令。
关联到源码层面,Bottlerocket 的 EKS 变体默认会在用户数据之外自动配置 host container(控制容器)与 SSM agent,参见 sources/settings-defaults/aws-k8s-1.32/defaults.d/20-aws-host-containers.toml:
host-containers.control默认enabled = true(非 superpowered),而host-containers.admin默认enabled = false。也就是说,只要实例 IAM 角色带有 SSM 权限,即可直接通过 Session Manager 进入控制容器。
kube-proxy 设置
与自动化路径同理:默认情况下kube-proxy会把nf_conntrack_max内核参数设置为可能与 Bottlerocket 启动默认值不同的值。如果希望保留 Bottlerocket 的默认设置,可以编辑 kube-proxy 的 DaemonSet:
kubectl edit -n kube-system daemonset kube-proxy在 kube-proxy 的参数中追加--conntrack-max-per-core和--conntrack-min(设为 0 表示不改变):
containers: - command: - kube-proxy - --v=2 - --config=/var/lib/kube-proxy-config/config - --conntrack-max-per-core=0 - --conntrack-min=0最终启动细节
为了让实例能与 EKS 集群控制平面及其它工作节点通信,需要确保实例以正确的安全组(security group)启动。
运行以下命令:
aws ec2 describe-security-groups --region us-west-2 \ --filters 'Name=tag:Name,Values=*bottlerocket*' \ --query "SecurityGroups[*].{Name:GroupName,ID:GroupId}"输出会列出若干安全组名称与 ID,你需要保存其中...ClusterSharedNodeSecurityGroup...与...nodegroup...两项的 ID。示例输出:
[ { "Name": "eksctl-bottlerocket-cluster-ClusterSharedNodeSecurityGroup-IDENTIFIER", "ID": "SECURITY_GROUP_ID_1" }, { "Name": "eksctl-bottlerocket-cluster-ControlPlaneSecurityGroup-IDENTIFIER", "ID": *ignore* }, { "Name": "eksctl-bottlerocket-nodegroup-ng-IDENTIFIER-SG-IDENTIFIER", "ID": "SECURITY_GROUP_ID_2" } ]如果选择了公网子网,并且计划 SSH 到实例(通过 admin container),还需要允许 SSH 流量进入安全组。执行类似下面的命令(替换上一步得到的某个安全组 ID,以及你的源网络 CIDR):
aws ec2 authorize-security-group-ingress --region us-west-2 \ --group-id SECURITY_GROUP_ID_1 --cidr YOUR_NETWORK_CIDR \ --protocol tcp --port 22如果选择了私网子网并想 SSH 进入,可以从同一子网和同一安全组中的另一台实例进行。
Launch!(启动实例)
现在可以在集群中启动 Bottlerocket 实例了。启动命令中有几个值必须替换:
YOUR_KEY_NAME:你在 EC2 中注册的 SSH 密钥对名称;SUBNET_ID:之前选择的子网——若选的是公网子网,请为命令添加--associate-public-ip-address,或在之后绑定弹性 IP;SECURITY_GROUP_ID_1、SECURITY_GROUP_ID_2:之前找到的两个安全组 ID;BOTTLEROCKET_AMI_ID:你注册的 AMI ID,或 AWS 提供的 AMI ID(即通过 SSM 参数解析得到的值,如resolve:ssm:/aws/service/bottlerocket/aws-k8s-1.32/x86_64/latest/image_id);user-data.toml:之前生成的用户数据文件路径;INSTANCE_PROFILE_NAME:eksctl为集群节点组创建的实例配置文件名称。
aws ec2 run-instances --key-name YOUR_KEY_NAME \ --subnet-id SUBNET_ID \ --security-group-ids SECURITY_GROUP_ID_1 SECURITY_GROUP_ID_2 \ --image-id BOTTLEROCKET_AMI_ID \ --instance-type c7.large \ --region us-west-2 \ --tag-specifications 'ResourceType=instance,Tags=[{Key=kubernetes.io/cluster/bottlerocket,Value=owned}]' \ --user-data file://user-data.toml \ --iam-instance-profile Name=INSTANCE_PROFILE_NAME再次提醒:如果使用公网子网,记得加--associate-public-ip-address或启动后绑定弹性 IP。
启动完成后,你就可以用常规的 Kubernetes 工作流在 Bottlerocket 实例上运行 Pod 了,例如:
kubectl run -i -t busybox --image=busybox --restart=Never命令中的关键设计解读
--tag-specifications中的kubernetes.io/cluster/bottlerocket=owned标签让集群自动发现该节点并纳入管理;--user-data file://user-data.toml对应 Bottlerocket 的"user data 即配置"机制:Bottlerocket 的 early-boot-config 组件会在启动时读取 TOML 格式的用户数据并将其发送给本地 API 服务(见 README.md 中关于 early-boot-config 的说明),从而在实例启动阶段就把settings.kubernetes等配置写入系统。
aws-k8s-*-nvidia 变体:启用 NVIDIA GPU
aws-k8s-*-nvidia变体打包了利用 NVIDIA GPU 所需的全部软件包与配置:自带 NVIDIA Tesla 驱动、NVIDIA 容器运行时 所需的库,以及 NVIDIA k8s device plugin。如果你的集群中已有 device plugin 的 DaemonSet,可能需要通过 taints 和 tolerations 避免它在 Bottlerocket 节点上重复运行。
其他 NVIDIA 工具,如 DCGM exporter 和 GPU Feature Discovery,均可正常工作,可按各项目提供的helm install说明安装。GPU Operator 也可用于安装这些工具,但要选出合适的特性子集以避免与变体内置软件冲突比较繁琐,因此文档建议在需要时逐个单独安装这些工具。
在具有多 GPU 的主机(如 EC2g4dn实例)上,可以在容器 spec 中通过资源声明按 GPU 分配(详见 Kubernetes 官方文档):
apiVersion: v1 kind: Pod metadata: name: test spec: restartPolicy: OnFailure containers: - name: test image: amazonlinux:2 resources: limits: nvidia.com/gpu: 1 # requesting 1 GPUNeuron 支持:为推理工作负载配置设备
Bottlerocketv1.30.0+支持 Neuron 实例类型,例如inf1、inf2、trn1和trn2。要启用 Neuron 工作负载,需要在用户数据(user data)中添加如下配置:
[settings] [settings.kubernetes] device-ownership-from-security-context = true该设置允许容器根据 Pod spec 中提供的runAsUser和runAsGroup值获得所挂载 Neuron 设备的所有权(详细原理参见 Kubernetes 关于非 root 容器与设备的官方说明)。配合该设置部署一个请求 Neuron 设备的 Pod 示例:
apiVersion: v1 kind: Pod metadata: name: test spec: hostNetwork: true securityContext: runAsUser: 1001 runAsGroup: 2001 fsGroup: 3001 restartPolicy: OnFailure containers: - name: test image: amazonlinux:2023 resources: limits: aws.amazon.com/neuron: "1"除了device-ownership-from-security-context设置外,还需要部署 neuron-device-plugin,可选部署 neuron-scheduler。仓库的 README.md 也确认了这些变体包含利用 Neuron 加速实例所需的包与配置。
总结
无论选择哪种路径,接入流程的核心都围绕三条主线展开:选择正确的变体(普通 /-nvidia,匹配 Kubernetes 版本与架构)、提供集群接入信息(通过 eksctl 配置文件或手动生成的 TOML user data 写入settings.kubernetes)、保证实例具备正确的 IAM 角色与安全组。自动化路径下,eksctl会替你完成 AMI 选择、节点组、IAM 角色与安全组的全套配置;手动路径则把这些步骤逐一暴露出来,适合需要精细控制的场景。启动完成后,Bottlerocket 节点会自动注册进集群,你可以完全使用标准的 Kubernetes 工作流在其上运行容器负载。
- 操作系统
- 云原生
- 安全
【免费下载链接】bottlerocket
An operating system designed for hosting containers
相关推荐
n8n工作流自动化完全指南:从入门到实战部署
n8n工作流自动化完全指南:从入门到实战部署 想要摆脱重复性工作的束缚?n8n作为一款开源的工作流自动化平台,正帮助全球开发者实现高效自动化。这款工具将代码的灵
工作流自动化人工智能AI Agent后端前端低代码快速上手AWS EKS:使用eksctl的完整集群部署指南
快速上手AWS EKS:使用eksctl的完整集群部署指南 想要在AWS上快速搭建Kubernetes集群吗?Amazon EKS(Elastic Kubern
云原生Bottlerocket 接入 VMware 环境:将 Bottlerocket 作为 Kubernetes Worker 节点部署的完整实战指南
Bottlerocket 接入 VMware 环境:将 Bottlerocket 作为 Kubernetes Worker 节点部署的完整实战指南 Bottle
操作系统云原生安全
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考