Kubernetes sample-apiserver 实战:在 Minikube 上构建并部署一个扩展 API Server
本文以 Kubernetes 仓库中 staging/src/k8s.io/sample-apiserver 自带的官方指南 minikube-walkthrough.md 为主线,完整还原“从零开始把示例 API Server 跑起来”的全过程:构建二进制、制作并推送容器镜像、修改 Deployment、创建 RBAC 与 APIService 对象,最终用 kubectl 操作 Flunder 这一自定义资源。读完并动手之后,你将掌握 Kubernetes API 聚合(API Aggregation)模式下扩展 API Server 的完整部署链路,并能对照仓库源码理解每个部署对象存在的意义。
前置条件
指南列出的前置条件如下,全部满足后即可开始:
- 已安装并配置好 Go(文档要求 Go 1.7.x 或更高版本,参考 Go 官方安装文档即可);
- 拥有 Docker Hub 账号,用于推送构建出来的镜像;
- 已安装 Minikube——一个运行在本地机器上的单节点 Kubernetes 集群,按其官方安装指南对号入座即可;
- 已安装
kubectl(部署阶段会用到,参见 Kubernetes 官方工具安装文档)。
关于代码获取方式的补充:当前仓库就是 Kubernetes 单体仓库,
sample-apiserver的完整源码已经位于 staging/src/k8s.io/sample-apiserver。其 README.md 明确指出该目录是独立仓库kubernetes/sample-apiserver的 staging 同步源头——独立仓库由这里的代码自动发布,因此直接在本地仓库中操作即可,无需额外克隆。
这个 Demo 到底在演示什么
在动手前先用一句话界定范围:sample-apiserver 演示了如何使用 k8s.io/apiserver 库构建一个功能完整的 Kubernetes 风格 API Server,既可以作为 API 聚合(Aggregation)下的扩展 API Server 挂载到主 kube-apiserver,也可以独立运行。其 README.md 同时给出了选型建议:如果只是往集群里加一种资源,优先考虑 CRD(代码量和维护成本更低);只有确实需要自定义鉴权、准入、存储行为时才需要写扩展 API Server。
本 Demo 注册的 API 组是 wardle.example.com,版本为 v1alpha1,提供 Flunder(以及从生成的 clientset/informers 目录结构看,还有 Fischer)等资源类型。部署成功后,kubectl 就可以像操作内置资源一样操作 flunder。
第 1 步:构建 sample-apiserver 二进制
要构建镜像,先得有二进制。在仓库根目录(即 main.go 所在目录,本仓库中为 staging/src/k8s.io/sample-apiserver)执行:
export GOOS=linux; go build .
如果一切顺利,当前目录下会出现一个名为 sample-apiserver 的二进制文件(Go 的模块名为 k8s.io/sample-apiserver,见 go.mod,go build 默认以模块名作为输出二进制名)。
从源码看这个二进制的入口非常薄:main.go 仅做三件事——
ctx := genericapiserver.SetupSignalContext()
options := server.NewWardleServerOptions(os.Stdout, os.Stderr)
cmd := server.NewCommandStartWardleServer(ctx, options, false)
code := cli.Run(cmd)
它依赖 k8s.io/apiserver/pkg/server(generic apiserver 框架)创建信号上下文,然后由 pkg/cmd/server/start.go 构建 WardleServerOptions 与启动命令,真正的服务端装配逻辑位于 pkg/apiserver/apiserver.go。也就是说,一个“API Server”的核心就是:generic apiserver 框架 + 自己声明的 API 类型 + 存储/注册逻辑。
构建细节提示:README.md 给出的更严格的构建命令是
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o artifacts/simple-image/kube-sample-apiserver,直接产出 Dockerfile 所需的二进制。另外,如果你修改了pkg/apis/.../types.go中的类型定义,需要运行 hack/update-codegen.sh 重新生成代码。
第 2 步:构建并推送容器镜像
指南使用 artifacts/simple-image/Dockerfile 来打镜像。这个 Dockerfile 极其精简,只有三行有效指令:
FROM fedora
ADD kube-sample-apiserver /
ENTRYPOINT ["/kube-sample-apiserver"]
即以 fedora 为基底,把上一步构建的二进制重命名为 kube-sample-apiserver 放进镜像根目录并作为入口。因此构建镜像前需要先把二进制拷贝到位,然后从仓库根目录执行:
cp ./sample-apiserver ./artifacts/simple-image/kube-sample-apiserver
docker build -t <YOUR_DOCKERHUB_USER>/kube-sample-apiserver:latest ./artifacts/simple-image
docker push <YOUR_DOCKERHUB_USER>/kube-sample-apiserver
其中 <YOUR_DOCKERHUB_USER> 替换为你的 Docker Hub 用户名,镜像会被推送到你的个人仓库,供集群拉取。
替代方案:deployment.yaml 的注释中给出了现成测试镜像
registry.k8s.io/e2e-test-images/sample-apiserver:1.17.4,可以docker pull后docker tag为kube-sample-apiserver:latest,跳过自行构建的步骤(该方式依赖集群节点可访问该镜像仓库,Minikube 场景下配合imagePullPolicy: Never使用)。
第 3 步:修改 Deployment 的镜像与拉取策略
原文指南的这一步标题仍写作 “Modify the replication controller”,但当前仓库中 artifacts/example/deployment.yaml 实际是一个 apps/v1 的 Deployment(名为 wardle-server)。需要修改两处:
- 把
imagePullPolicy从Never改为Always或IfNotPresent; - 把
image从kube-sample-apiserver:latest改为你推送的<YOUR_DOCKERHUB_USER>/kube-sample-apiserver:latest。
修改后的关键片段:
...
containers:
- name: wardle-server
image: <YOUR_DOCKERHUB_USER>/kube-sample-apiserver:latest
imagePullPolicy: Always
...
理解这个 Pod 的完整结构有助于排障,deployment.yaml 中每个容器/参数都各司其职:
spec:
serviceAccountName: apiserver
containers:
- name: wardle-server
image: kube-sample-apiserver:latest
imagePullPolicy: Never
args: [ "--etcd-servers=http://localhost:2379" ]
- name: etcd
image: registry.k8s.io/etcd:v3.7.0
serviceAccountName: apiserver:绑定后面创建的 ServiceAccount,用于委托鉴权;--etcd-servers=http://localhost:2379:apiserver 的存储指向localhost:2379,而etcd作为同 Pod 内的 sidecar 容器一起运行——这就是整个 Demo 不需要外部 etcd 集群的原因,也是它能在单节点 Minikube 上跑通的关键设计;- 注意
imagePullPolicy: Never是仓库默认值(面向预加载镜像的 CI 场景),在 Minikube 上使用自建镜像时必须按上文修改。
第 4 步:部署到 Minikube
指南给出了一组完整的 kubectl 命令,这里完整保留,并逐条说明每个对象的用途(所有 YAML 均位于 artifacts/example/ 目录):
# create the namespace to run the apiserver in
kubectl create ns wardle
# create the service account used to run the server
kubectl create -f artifacts/example/sa.yaml -n wardle
# create the rolebindings that allow the service account user to delegate authz back to the kubernetes master for incoming requests to the apiserver
kubectl create -f artifacts/example/auth-delegator.yaml -n kube-system
kubectl create -f artifacts/example/auth-reader.yaml -n kube-system
# create rbac roles and clusterrolebinding that allow the service account user to use admission webhooks
kubectl create -f artifacts/example/rbac.yaml
kubectl create -f artifacts/example/rbac-bind.yaml
# create the service and replication controller
kubectl create -f artifacts/example/deployment.yaml -n wardle
kubectl create -f artifacts/example/service.yaml -n wardle
# create the apiservice object that tells kubernetes about your api extension and where in the cluster the server is located
kubectl create -f artifacts/example/apiservice.yaml
逐个对象拆解:
命名空间与 ServiceAccount
ns.yaml 定义 wardle 命名空间(上面用 kubectl create ns 等价创建);sa.yaml 在其中创建名为 apiserver 的 ServiceAccount,Deployment 通过 serviceAccountName: apiserver 引用它。
委托鉴权(Delegated Authn/Authz)
扩展 API Server 并不自己做用户身份识别,而是把请求“委托”回主 kube-apiserver。这由两个绑定文件实现:
- auth-delegator.yaml:把内置的
system:auth-delegatorClusterRole 绑定给wardle/apiserverSA,允许它调用主 API Server 的 TokenReview/SubjectAccessReview 机制完成委托鉴权与鉴权; - auth-reader.yaml:在
kube-system命名空间把extension-apiserver-authentication-readerRole 绑定给该 SA,允许其读取extension-apiserver-authenticationConfigMap(包含主 API Server 的 requestheader CA 等委托所需配置)。
这正是指南中“allow the service account user to delegate authz back to the kubernetes master for incoming requests”的含义。
聚合 API Server 自身需要的 RBAC
- rbac.yaml 定义 ClusterRole
aggregated-apiserver-clusterrole,授予三类权限:对核心组namespaces的get/watch/list、对admissionregistration.k8s.io下 mutating/validating webhook 配置及 ValidatingAdmissionPolicy 的get/watch/list、对flowcontrol.apiserver.k8s.io下prioritylevelconfigurations与flowschemas的list/watch; - rbac-bind.yaml 通过 ClusterRoleBinding
sample-apiserver-clusterrolebinding将该角色授予wardle/apiserverSA。
这些权限是 generic apiserver 框架在运行期拉取 admission webhook 配置、APF 流控配置和 namespace 信息所必需的(见指南注释 “allow the service account user to use admission webhooks”)。
Service 与 APIService——聚合接入的最后一块拼图
- service.yaml:创建名为
api的 Service(wardle命名空间,443 端口,selector 为apiserver: "true",与 Deployment 的标签对应),作为集群内访问扩展 API Server 的入口; - apiservice.yaml:创建
apiregistration.k8s.io/v1的 APIService 对象,告诉主 API Server “wardle.example.com组v1alpha1版本的路由转发到wardle/api这个 Service”:
apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
name: v1alpha1.wardle.example.com
spec:
insecureSkipTLSVerify: true
group: wardle.example.com
groupPriorityMinimum: 1000
versionPriority: 15
service:
name: api
namespace: wardle
version: v1alpha1
其中 groupPriorityMinimum/versionPriority 用于多版本共存时的优先级仲裁,insecureSkipTLSVerify: true 是因为 Demo 使用自签证书。
批量部署的等价写法:README.md 的 “Deploy into a Kubernetes Cluster” 一节给出了更省事的方式——修改好
deployment.yaml的镜像后直接kubectl apply -f artifacts/example,一次性应用整个目录下的示例对象。
第 5 步:验证部署
APIService 就绪后,主 API Server 就会把 /apis/wardle.example.com/v1alpha1/... 下的请求转发到你的 wardle-server Pod。按指南验证——创建资源:
kubectl create -f artifacts/flunders/01-flunder.yaml
# outputs flunder "my-first-flunder" created
01-flunder.yaml 的内容:
apiVersion: wardle.example.com/v1alpha1
kind: Flunder
metadata:
name: my-first-flunder
labels:
sample-label: "true"
然后查询它:
kubectl get flunder my-first-flunder
#outputs
# NAME KIND
# my-first-flunder Flunder.v1alpha1.wardle.example.com
如果能看到 my-first-flunder 与 Flunder.v1alpha1.wardle.example.com,说明从 kubectl → kube-apiserver(发现 APIService)→ Service wardle/api → wardle-server 容器 → 同 Pod etcd 存储的整条链路已经打通。此时 kubectl get 的 KIND 列也直观体现了版本化的 API 命名规则:<Kind>.<version>.<group>。
延伸阅读:仓库内可以继续深挖的位置
- 独立运行(不经过聚合、用本地 X.509 证书鉴权)的完整步骤,包括签发 CA/客户端证书与启动参数
--secure-port、--etcd-servers、--client-ca-file等,见 README.md 的 “Running it stand-alone” 一节; - 服务端装配与 generic apiserver 配置:pkg/apiserver/apiserver.go、pkg/cmd/server/start.go;
- 资源类型与存储策略:pkg/apis/wardle/v1alpha1/types.go 定义类型,pkg/registry/wardle/flunder/ 展示 etcd 存储与注册表策略的实现;
- 两个内置的准入插件示例(BanFlunder、WardleInitializer)位于 pkg/admission/plugin/,展示了扩展 API Server 如何注册 mutating/validating 逻辑;
- 修改类型后重新生成代码:hack/update-codegen.sh 与 hack/verify-codegen.sh。
按 minikube-walkthrough.md 的五个步骤顺序执行,配合上述源码路径逐层对照,即可完整理解 Kubernetes 扩展 API Server 从构建到聚合接入的每个环节。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00