fedctl 完全指南:FCP 控制平面的构建、使用与运维

fedctl 是 eBay Tess 平台用于管理联邦控制平面(FCP, Federation Control Plane)的核心工具。本文从仓库结构出发,详细介绍 fedctl 的构建方式、使用方法、资源配置、权限要求以及控制平面的退役流程,帮助团队成员系统性地掌握 FCP 的运维体系。


一、仓库整体框架与结构

fed-release 仓库是 fedctl 工具的配置输入仓库,不包含 fedctl 的源代码(源码在 github.corp.ebay.com/ebaytess/fedctl)。仓库的作用是存放各个 FCP 实例的配置文件以及 fedctl 生成的 Kubernetes 对象 YAML 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
fed-release/
├── BUILD.bazel # Bazel 构建入口(顶层)
├── WORKSPACE # Bazel workspace 定义
├── README.md # 仓库说明文档
├── pub.asc # GPG 公钥(用于 git-crypt)
├── hack/ # 运维脚本集合
│ ├── fedctl-run.sh # 下载并执行 fedctl 的入口脚本
│ ├── fedctl-bump.sh # 更新 FEDCTL_VERSION 文件
│ ├── fedctl-handle-merge.sh # PR 合并后的流水线触发脚本
│ ├── fedctl-apply-with-lease.sh
│ ├── fedctl-global-join-with-lease.sh
│ ├── lib.sh # 公共 shell 函数库
│ ├── drift/ # 镜像漂移检测脚本
│ └── join/ # join 相关辅助脚本
├── instances/ # 各控制平面实例配置目录
│ ├── fcp/ # FCP(主联邦控制平面)实例
│ │ ├── README.md
│ │ ├── common/ # 跨实例共享配置(k8sclusters.json、证书等)
│ │ ├── dev/ # fcp-dev 环境
│ │ │ ├── cfg.toml # 核心配置文件
│ │ │ ├── FEDCTL_VERSION # 当前使用的 fedctl 版本号
│ │ │ ├── secrets/ # 加密的 kubeconfig / 证书等(git-crypt)
│ │ │ ├── cm/ # ConfigMap 配置
│ │ │ ├── health/ # 健康检查对象
│ │ │ ├── flowschema/ # APF FlowSchema 配置
│ │ │ ├── pdi/ # PDI assertion 对象
│ │ │ ├── fcp-dev/ # 聚合器(aggregator)集群的生成产物 YAML
│ │ │ ├── 130/ # host cluster 130 的生成产物 YAML
│ │ │ └── 140/ # host cluster 140 的生成产物 YAML
│ │ ├── preprod/
│ │ ├── prod/
│ │ └── soak-dev/
│ ├── fcp-sddz/ # SDDZ 环境的 FCP 实例
│ ├── apps/ # Apps 控制平面实例
│ ├── tnet/ # TNet 控制平面实例
│ └── tm/ # TM 控制平面实例
└── runbooks/ # 运维手册
├── design.md # FCP 架构设计文档
├── dev.md # 开发环境搭建指南
├── release.md # 发布流程
├── rbac.md # 权限说明
├── wrap-up.md # 发布收尾步骤
└── ...(其他专项 runbook)

FCP 组件架构概览

FCP 是一个无节点、无 kubelet 的纯 API 控制平面,所有 FCP 组件以普通 Deployment 的形式运行在现有 Tess host cluster 中,并跨多个 host cluster 分布以实现 HA。核心组件包括:

组件 说明
etcd 由定制化 etcd-operator 管理,支持多 host cluster 跨 AZ 部署
kube-apiserver 标准 K8s apiserver,禁用部分 API,启用聚合扩展
ebay-apiserver 自定义 Tess 扩展 apiserver,服务 Account/Group 等自定义 API
admission-apiserver 准入 apiserver,负责所有 webhook 校验与变更
webhook 认证/授权 webhook(Keystone token + TessRBAC)
kube-controller-manager 标准 controller-manager,大量 controller 已禁用
FCM(ebay-controller-manager) Federation Controller Manager,包含 sync controller、account controller 等
FCM-AP Access Point 相关 controller
FCM-ATC 流量控制相关 controller(Admin Traffic Control)
FCM-NSVC NameService controller
audit-transformer 审计日志转换器
PSCP controller 安全策略同步 controller

上述所有 apiserver 组件(kube-apiserver、ebay-apiserver、admission-apiserver、webhook)共享同一个 Pod,通过 loopback 地址(127.x.x.x)互相通信,以减少网络开销。


二、如何构建 fedctl

fedctl 的源代码位于 github.corp.ebay.com/ebaytess/fedctlfed-release 仓库本身不包含 fedctl 的构建过程。

方式一:直接下载预编译二进制

这是 CI/CD 流水线(Tekton)使用的方式,由 hack/fedctl-run.sh 自动完成:

1
2
3
4
5
6
7
# fedctl-run.sh 的核心逻辑
fedctl_version=$(cat "${artifact_dir}/FEDCTL_VERSION")
bin="hack/cache/fedctl-${fedctl_version}"

wget -O "$bin" \
"https://dl.tess.io/bin/fedctl?version=${fedctl_version}&platform=linux-amd64"
chmod +x "$bin"

每个实例目录下的 FEDCTL_VERSION 文件记录该实例使用的 fedctl 版本,例如:

1
instances/fcp/dev/FEDCTL_VERSION

方式二:从源码构建(开发调试)

fedctl 源码仓库中直接用 go run 运行:

1
2
3
4
5
6
7
8
9
10
git clone git@github.corp.ebay.com:ebaytess/fedctl.git
cd fedctl
git checkout <fedctl_tag>

# 直接运行(无需编译)
go run cmd/fedctl/main.go <subcommand> [flags]

# 或者编译后运行
go build -o fedctl cmd/fedctl/main.go
./fedctl <subcommand> [flags]

方式三:通过 Docker 容器运行(本地测试)

1
2
3
4
5
docker run -v $(pwd):/scratch/fed-release --rm \
hub.tess.io/sig-core-cicd/fedctl-base:v0.2 \
bash -c "OFFLINE=false \
INSTANCE_CFG=/scratch/fed-release/instances/fcp/dev/cfg.toml \
/scratch/fed-release/hack/fedctl-run.sh generate"

更新 fedctl 版本

1
2
3
# 更新特定实例的 fedctl 版本
INSTANCE_CFG=instances/fcp/dev/cfg.toml \
hack/fedctl-bump.sh <new_version>

三、fedctl 的使用方式

fedctl 的所有操作都通过子命令(subcommand)+ 配置文件(cfg.toml)驱动。

核心使用模式

1
2
3
4
5
6
# 推荐方式:通过 hack/fedctl-run.sh 调用
INSTANCE_CFG=instances/fcp/dev/cfg.toml \
hack/fedctl-run.sh <subcommand> [extra flags]

# 等价的直接调用
fedctl <subcommand> --config=instances/fcp/dev/cfg.toml [flags]

主要子命令

子命令 功能
kubeconfig-builder 为 FCP 生成 kubeconfig 文件
etcd-init 初始化/更新 etcd 集群(通过 etcd-operator)
apiserver-init 初始化/更新 apiserver、kube-controller-manager 等
fcm-init 初始化/更新 Federation Controller Manager
nsvc-init 初始化/更新 NameService controller
join 将单个 member cluster 加入 FCP
join-all 将所有 sync candidate clusters 加入 FCP
assert-init 初始化 assertion(端到端测试)框架
health-init 初始化健康检查 monitor
install-pscp-controller 部署 PSCP 安全策略 controller
pdi-init 初始化 PDI(Pre-Delivery Inspection)assertion
generate 仅生成 YAML 文件,不 apply(用于 offline/dry-run)
azkubeconfig-init 为 AZ 控制平面生成 kubeconfig

典型发布流程

cfg.toml 中通过 subcommands 字段定义按序执行的子命令列表:

1
2
3
4
5
6
7
8
9
10
11
subcommands = [
"etcd-init",
"apiserver-init",
"fcm-init",
"nsvc-init",
"join-all",
"assert-init",
"health-init",
"install-pscp-controller",
"pdi-init",
]

标准发布步骤(每个子命令先 dry-run,再正式 apply):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Step 1: 生成 kubeconfig
go run cmd/fedctl/main.go kubeconfig-builder \
--config=${config_toml} \
--kubeconfig=/tmp/fcp \
--artifact-dir=${artifact_dir}

# Step 2~N: 每个子命令先 generate-only,再正式执行
for cmd in etcd-init apiserver-init fcm-init join-all; do
go run cmd/fedctl/main.go ${cmd} \
--config=${config_toml} \
--artifact-dir=${artifact_dir} \
--kubeconfig=/tmp/fcp \
--generate-only # 先 dry-run

go run cmd/fedctl/main.go ${cmd} \
--config=${config_toml} \
--artifact-dir=${artifact_dir} \
--kubeconfig=/tmp/fcp # 正式 apply
done

--generate-only 标志只生成 YAML 文件到 artifact_dir,不实际 apply 到集群,用于审查变更内容。


四、fedctl 是否包含 Namespace 创建与容量分配

Namespace 创建

fedctl 本身负责在 host cluster 创建 FCP 相关的 Namespace(如 fcp-devmember-fcp-dev),这些 Namespace 通过 cfg.toml 中的以下参数控制:

1
2
3
4
federation-name = "fcp-dev"
namespace-annotations = "application.tess.io/name=fcp,environment.tess.io/name=dev,identity.alpha.tess.io/default-posixuser=true"
namespace-labels = "account.tess.io/name=fcp"
cluster-namespace = "member-fcp-dev" # member cluster 上的 namespace

FCP 作为 API 层使用的上游 Namespace(即 FCP 在父 FCP 中注册自身所需的 Account/Group/Namespace)需要手动提前创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 在父 FCP(host cluster 所在的 FCP)中手动创建
kubectl apply -f - <<EOF
apiVersion: identity.tess.io/v1alpha1
kind: Group
metadata:
name: ${tess_group}
---
apiVersion: identity.tess.io/v1alpha1
kind: Account
metadata:
name: ${tess_acct}
---
apiVersion: v1
kind: Namespace
metadata:
name: ${federation_name}
annotations:
application.tess.io/name: ${tess_app}
environment.tess.io/name: ${real_tess_env}
labels:
account.tess.io/name: ${tess_acct}
EOF

容量分配(Quota)

fedctl 不负责配额分配,配额需要在运行 fedctl 之前通过 http://go/arq 手动申请。

需要为以下 Namespace 申请配额:

<fcp-name> namespace(运行 apiserver/etcd 的主 namespace):

Quota 类型 资源 参考值
best-effort-pods pods 7
object-counts configmaps 1
object-counts secrets 18
object-counts services 3
object-counts loadbalancer services 1
object-counts nodeport services 1

<fcp-name>-fcm namespace(运行 FCM 的 namespace):

Quota 类型 资源 参考值
best-effort-pods pods 2
object-counts configmaps 1
object-counts secrets 4

实际生产所需配额需根据副本数计算:etcd × 副本数 + apiserver × 副本数 + kube-controller-manager + fcm + fcm-ap + ...


五、组件资源配置:CPU/Memory、Label Selector 与 Tolerations

所有组件的资源配置均在各实例的 cfg.toml 中统一管理,fedctl 将这些参数渲染为 Deployment YAML 后 apply 到集群。

CPU/Memory 资源配置

fcp-devcfg.toml 为例,各组件配置键的命名规律为 <component>-cpu-request<component>-memory-limit 等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# etcd
etcd-cpu-limit = "12"
etcd-cpu-request = "4"
etcd-memory-limit = "60Gi"
etcd-memory-request = "10Gi"

# etcd(events 专用集群)
etcd-events-cpu-limit = "3"
etcd-events-cpu-request = "1"
etcd-events-memory-limit = "10Gi"
etcd-events-memory-request = "3Gi"

# apiserver(kube-apiserver 容器)
apiserver-cpu-limit = "16"
apiserver-cpu-request = "2"
apiserver-memory-limit = "150G"
apiserver-memory-request = "80G"

# ebay-apiserver 容器
ebay-cpu-limit = "12"
ebay-cpu-request = "1"
ebay-memory-limit = "120G"
ebay-memory-request = "60G"

# webhook 容器
webhook-cpu-limit = "1"
webhook-cpu-request = "100m"
webhook-memory-limit = "20G"
webhook-memory-request = "4G"

# admission-apiserver 容器
admission-cpu-limit = "4"
admission-cpu-request = "50m"
admission-memory-limit = "80G"
admission-memory-request = "20G"

# proxy sidecar
proxy-sidecar-cpu-limit = "0.5"
proxy-sidecar-cpu-request = "10m"
proxy-sidecar-memory-limit = "200M"
proxy-sidecar-memory-request = "50M"

# FCM(TFC sync controller 主控)
tfc-cpu-request = 3
tfc-memory-request = "12Gi"
tfc-cpu-limit = 8
tfc-memory-limit = "115Gi"

# FCM-AP(AccessPoint controller)— 有 fed、az、cluster 三类部署
fed-ap-cpu-request = 4
fed-ap-memory-request = "4Gi"
fed-ap-cpu-limit = 20
fed-ap-memory-limit = "16Gi"

az-ap-cpu-request = 1
az-ap-memory-request = "4Gi"
az-ap-cpu-limit = 8
az-ap-memory-limit = "16Gi"

cluster-ap-cpu-request = 4
cluster-ap-memory-request = "8Gi"
cluster-ap-cpu-limit = 20
cluster-ap-memory-limit = "48Gi"

# FCM-ATC(Admin Traffic Control)
fcm-atc-cpu-request = 2
fcm-atc-memory-request = "3Gi"
fcm-atc-cpu-limit = 6
fcm-atc-memory-limit = "9Gi"

# FCM-NSVC(NameService)
fcm-nsvc-cpu-request = 8
fcm-nsvc-memory-request = "4Gi"
fcm-nsvc-cpu-limit = 20
fcm-nsvc-memory-limit = "100Gi"

# audit-transformer
audit-transformer-cpu-limit = "3"
audit-transformer-cpu-request = "50m"
audit-transformer-memory-limit = "8Gi"
audit-transformer-memory-request = "50Mi"

# in-cluster TFC(在 member cluster 内运行)
tfc-in-cluster-cpu-request = "500m"
tfc-in-cluster-memory-request = "1Gi"
tfc-in-cluster-cpu-limit = 1
tfc-in-cluster-memory-limit = "4Gi"

# AIM(Application Instance Manager)
aim-cpu-request = 1
aim-cpu-limit = 2
aim-memory-request = "3Gi"
aim-memory-limit = "6Gi"

副本数配置

副本数按 host cluster 维度分别指定,格式为 <cluster_id>=<count>

1
2
3
4
5
6
7
8
9
10
11
# apiserver 在两个 host cluster 各跑 4 个副本
apiserver-replicas = "130=4,140=4"

# etcd 的 host cluster 副本分布
host-cluster-replicas = "130=3,140=4"

# FCM 副本
fcm-replicas = "130=1,140=1"

# controller-manager
controller-manager-replicas = "130=1,140=1"

Template Label Selector

fedctl 通过 deployment-labelspod-labels 参数为各组件的 Deployment 和 Pod 注入 label,用于 health monitor 和其他控制器的选择器:

1
2
3
4
5
6
deployment-labels = "fcm=applicationinstance.tess.io/name=fcp-ebay-controller-manager-dev,\
controller-manager=applicationinstance.tess.io/name=fcp-controller-manager-dev,\
apiserver=application.tess.io/name=fcp-apiserver\\,applicationinstance.tess.io/name=fcp-apiserver-dev,\
etcd-operator=application.tess.io/name=fcp-etcd\\,applicationinstance.tess.io/name=fcp-etcd-dev"

pod-labels = "fcm=applicationinstance.tess.io/name=fcp-ebay-controller-manager-dev,..."

格式为 <component>=<label-key>=<label-value>,多个 label 间用 \\, 分隔(转义逗号)。

健康检查 label selector 通过独立参数配置:

1
2
3
4
5
6
7
8
# apiserver health monitor 标签
apiserver-healthmonitor-labels = "apiserver-release=controlplane"

# AP 健康检查标签
ap-healthmonitor-labels = "ap-fed-release=canary"

# 整体健康检查(pre/post check 均使用)
health-monitor-labels = "master@fcp-dev:apiserver-release=controlplane,ap-fed-release=canary"

Tolerations 配置

Tolerations 在 cfg.toml 中统一配置,应用于所有 FCP 组件的 Pod:

1
2
# 允许调度到 highmem 节点(但不强制)
tolerations = "dedicated=highmem:NoSchedule"

格式为 <key>=<value>:<effect>,如需强制调度到特定节点,配合 node-selector 使用:

1
2
# 可选:强制调度到 highmem 节点
# node-selector = "dedicated=highmem"

六、如何退役(Decomm)和销毁一个 FCP Apiserver

目前 fed-release 仓库中没有专用的”一键退役”命令,FCP apiserver 的退役是一个手动分步骤的过程。

步骤一:确认健康状态与备份

1
2
3
4
# 确认 etcd 数据已有最新备份
# 检查 etcd 备份配置(如 S3)
etcd-operator-backup-s3-bucket = "fcp-dev-2"
etcd-operator-backup-max-backups = "288"

步骤二:从 cfg.toml 中缩容组件

将 apiserver 副本数归零,然后重新运行 apiserver-init

1
2
3
# 在 cfg.toml 中将所有副本数设为 0
apiserver-replicas = "130=0,140=0"
controller-manager-replicas = "130=0,140=0"
1
2
3
4
go run cmd/fedctl/main.go apiserver-init \
--config=${config_toml} \
--artifact-dir=${artifact_dir} \
--kubeconfig=/tmp/fcp

步骤三:清理 fedctl 管理的生成物(Pruning)

fedctl 使用 kubectl apply 语义,不会自动删除旧对象。需要手动找出并删除过期对象:

1
2
3
4
5
6
7
8
9
10
11
# 找出超过发布时间窗口的旧文件(以 120 分钟为例)
cd ${artifact_dir}
find . -type f -mmin +120 -ls

# 根据文件名构造 kubectl delete 命令
# 文件名格式:./<cluster>/[<apigroup>-]<apiversion>-<kind>-<namespace>-<name>.yaml
kubectl --kubeconfig=${kubeconfig} --context=<context> \
[-n <namespace>] delete <kind> <name>

# 确认后删除旧文件
find . -type f -mmin +120 -delete

步骤四:清理 member cluster 中的资源

对于每个已 join 的 member cluster,需要删除 fedctl 在其中创建的资源(ServiceAccount、ClusterRoleBinding、Deployment 等):

1
2
3
4
5
# 对每个 member cluster 执行
kubectl --kubeconfig=${kubeconfig} --context=<cluster_id> \
-n member-fcp-dev delete deploy fcm fcm-ap fcm-ap-canary
kubectl --kubeconfig=${kubeconfig} --context=<cluster_id> \
delete clusterrolebinding fed-member-fcp-dev:fcm

步骤五:清理 FCP 中的 Cluster 对象与配置

1
2
3
4
5
6
7
8
9
# 从 sync-candidate-contexts 中移除该集群
vi instances/fcp/${FCP_INSTANCE}/cfg.toml
# 删除 sync-candidate-contexts 中对应的 cluster id

# 重新运行 join-all 以更新配置
go run cmd/fedctl/main.go join-all \
--config=${config_toml} \
--artifact-dir=${artifact_dir} \
--kubeconfig=/tmp/fcp

步骤六:回滚方案

如果退役过程出现问题,简单回滚方式是还原 cfg.toml 并重新运行发布流程

1
2
3
4
git revert HEAD  # 或手动还原 cfg.toml
go run cmd/fedctl/main.go etcd-init --config=... --kubeconfig=...
go run cmd/fedctl/main.go apiserver-init --config=... --kubeconfig=...
# ...依次恢复

极端情况(数据损坏)可从 etcd 备份恢复,参考 runbooks/etcd-backup-and-restore.md


七、fedctl 使用所需的权限

1. 基本 FCP Admin 权限

运行 fedctl 的用户或 CI 服务账号需要是 FCP admin group 成员:

1
2
# cfg.toml 中定义的 admin 用户/组
admin-users = [ "group:fcp_admin", "l=qa+ou=sig-core-cicd-dev,o=sig-core-cicd,dc=tess,dc=ebay,dc=com" ]

2. Join 命令的特殊权限

join 子命令需要在 member cluster 中拥有 impersonation 权限,这通过 sig-security 仓库的 PR 申请:

参考 sig-security#268 格式提交 PR,合并后等待约 10 分钟生效。

3. git-crypt 解密权限

fed-release 仓库使用 git-crypt 加密 secrets/ 目录下的敏感文件(kubeconfig、证书、云服务凭据等)。需要请现有协作者将你的 GPG 公钥添加到仓库:

1
2
3
4
5
6
7
8
9
10
# 新用户:生成密钥对并导出公钥
gpg --gen-key
gpg --export --armor your@email.com > pub.asc

# 现有协作者:导入并添加新用户
gpg --import pub.asc
git-crypt add-gpg-user --trusted new-user@ebay.com

# 解锁仓库
git-crypt unlock

4. sig-security 权限(sync controller 写入权限)

sync controller 在 member cluster 中变更资源需要特殊授权,通过 sig-security 仓库管理:

参考 sig-security#3284 格式申请。

5. kubeconfig 访问权限

fedctl 通过 secrets/kubeconfig 访问 host cluster 和 member cluster,该 kubeconfig 中的 ServiceAccount token 需要具备以下权限:

  • host cluster:在 FCP namespace 内创建/更新 Deployment、ConfigMap、Secret、Service 等
  • member cluster:通过 cdcontinuous-deployment ServiceAccount 访问(具备 namespace-admin ClusterRole)

总结

主题 关键点
构建 下载预编译二进制(生产)或 go run(开发),版本由 FEDCTL_VERSION 文件管理
使用 子命令 + cfg.toml 驱动,建议先 --generate-only 审查再正式 apply
Namespace fedctl 负责 host/member cluster 的 FCP namespace;父 FCP 中的账号体系需手动创建
容量分配 fedctl 不管理 Quota,需提前通过 go/arq 申请
资源配置 所有 CPU/Memory/副本数均在 cfg.toml 中配置,label selector 通过 deployment-labels 设置
退役 无专用命令,需手动缩容 → 清理生成物 → 清理 member cluster → 提 PR 删除配置
权限 需要 FCP admin group 成员身份 + git-crypt 解密权限 + sig-security 申请(join/sync)

fedctl 将复杂的多集群控制平面编排抽象为声明式配置,通过 cfg.toml 单文件驱动整个 FCP 生命周期管理,配合 Tekton 流水线实现全自动化发布,是 eBay Tess 平台控制平面运维体系的核心。