把迁不走的服务器接进网格——WorkloadGroup 与 WorkloadEntry
目标
用 WorkloadGroup 为网格之外的一组服务器搭好模板,用 WorkloadEntry 逐台登记服务器,再用 ServiceEntry 把它们绑定到网格内的服务名称上。最后用 istioctl x workload entry configure 实际生成那台服务器要取走的配置包,并留下统计选择器选中了什么的报告。
为什么重要
即使引入了网格,仍然会有无法迁到 Kubernetes 的服务器。把它们放在外面,网格提供的东西就会在那里中断——身份、加密、访问策略,尤其是一张完整的服务地图。网格扩展,就是把那台服务器当作成员而不是客人接纳进来。核心是身份。在 Kubernetes 内,Pod 通过 ServiceAccount 获得身份,而 VM 上没有这样的东西。所以要在 WorkloadGroup 中写明使用哪个 ServiceAccount,并预先把集群签发的令牌和根证书放到那台服务器上。这三样都对上了,VM 上的代理才能连上控制平面并证明自己的身份。这三个对象的关系也容易混淆,可以这样梳理:一组服务器的模板是 WorkloadGroup,一台服务器是 WorkloadEntry,供调用的名称是 ServiceEntry。
步骤
- 创建
/root/ist-expansion和命名空间expansion,并在其中创建 ServiceAccountbilling-sa。然后把使用该 ServiceAccount 的工作负载在网格中将获得的身份字符串,用一行写入/root/ist-expansion/identity.txt——信任域为默认值cluster.local。 - 在
/root/ist-expansion/wg.yaml中写入 WorkloadGrouplegacy-billing并应用到expansion——spec.metadata.labels为app: billing,spec.template为serviceAccount: billing-sa、network: onprem、ports.http: 8080,spec.probe为periodSeconds: 5,并通过httpGet检查端口 8080 的/healthz。 - 在
/root/ist-expansion/wg-bad-probe.yaml中写入 WorkloadGroupbad-probe,但要在spec.probe中同时放入httpGet和tcpSocket。在/root/ist-expansion/wg-no-template.yaml中写入no-template,但要完全去掉spec.template。两者都只做服务端试运行,并把拒绝的句子按顺序收集到/root/ist-expansion/wg-reject.txt。 - 在
/root/ist-expansion/we.yaml中写入 WorkloadEntrybilling-1并应用——元数据标签app: billing,address为10.20.30.41,network为onprem,serviceAccount为billing-sa,locality为dc1/rack2,ports.http为 8080。然后在/root/ist-expansion/we-typo.yaml中写入billing-typo,把地址写成10.20.30 .41这样带空格的形式并应用,把istioctl analyze -n expansion -o json的输出保存到/root/ist-expansion/analyze-address.json。标签设为app: billing-broken。 - 在
/root/ist-expansion/se.yaml中写入 ServiceEntrybilling-svc并应用——hosts只有一个billing.expansion.internal,location为MESH_INTERNAL,resolution为STATIC,端口编号为 8080、名称为http、协议为HTTP,workloadSelector.labels为app: billing。 - 创建三个 ServiceEntry 文件,并通过服务端试运行确认规则——
/root/ist-expansion/se-none-endpoints.yaml(resolution: NONE却有endpoints)、/root/ist-expansion/se-roundrobin-two.yaml(resolution: DNS_ROUND_ROBIN,有两个endpoints)、/root/ist-expansion/se-roundrobin-one.yaml(resolution: DNS_ROUND_ROBIN,有一个endpoints)。把结果按上述顺序,以<파일이름>\t<accepted|rejected>(占位符依次为文件名和判定结果)的格式写入/root/ist-expansion/resolution-rules.tsv,并把拒绝的句子收集到/root/ist-expansion/resolution-reject.txt。这三个文件都不要实际应用。 - 先创建
istio-system命名空间,从istioctl manifest generate --set profile=minimal的渲染结果中只取出 ConfigMapistio,保存到/root/ist-expansion/istio-cm.yaml后再应用。然后在expansion中创建 ConfigMapistio-ca-root-cert——键名为root-cert.pem,值为用openssl生成的自签名根证书(/root/ist-expansion/root-cert.pem)。最后运行istioctl x workload entry configure -f /root/ist-expansion/wg.yaml -o /root/ist-expansion/vmcfg --clusterID lab-cluster生成配置包。 - 在
/root/ist-expansion/we2.yaml中写入第二台服务器billing-2并应用——标签app: billing,地址10.20.30.42,network: onprem,serviceAccount: billing-sa,locality: dc1/rack3,ports.http为 8080。然后创建/root/ist-expansion/se-match.sh,让它针对expansion中每个带有workloadSelector的 ServiceEntry,按名称顺序只向标准输出打印<ServiceEntry 이름>\t<잡힌 WorkloadEntry 수>\t<이름들 쉼표>(占位符依次为 ServiceEntry 名称、被选中的 WorkloadEntry 数量,以及用逗号连接的名称),并把该输出保存到/root/ist-expansion/match-result.txt。
参考
- 身份格式是
spiffe://<신뢰도메인>/ns/<네임스페이스>/sa/<서비스어카운트>(占位符依次为信任域、命名空间和 ServiceAccount)。 kubectl apply --dry-run=server不会创建对象,只是询问 API 服务器。istioctl x workload entry configure会读取集群中的两个 ConfigMap 和 ServiceAccount。- 常见错误:只创建了 WorkloadEntry 而漏掉了 ServiceEntry。没有可供调用的名称,就没有人能调用它。
- 常见错误:把标签写在 WorkloadEntry 的
spec中。选择器看的是metadata.labels。 - 参考:https://istio.io/v1.24/docs/reference/config/networking/workload-group/
- 参考:https://istio.io/v1.24/docs/reference/config/networking/workload-entry/
- 参考:https://istio.io/v1.24/docs/ops/deployment/vm-architecture/
先确定网格如何称呼那台服务器
创建 /root/ist-expansion 和命名空间 expansion,并在其中创建 ServiceAccount billing-sa。然后把使用该 ServiceAccount 的工作负载在网格中将获得的身份字符串,用一行写入 /root/ist-expansion/identity.txt——信任域为默认值 cluster.local。
网格的身份不是来自 IP 或主机名,而是来自 ServiceAccount。格式为 spiffe://<신뢰도메인>/ns/<네임스페이스>/sa/<서비스어카운트>(占位符依次为信任域、命名空间和 ServiceAccount)。网格之外的服务器也必须拿到这张名牌,才能进入网格。
创建的不是一台 VM,而是一组 VM 的模板
在 /root/ist-expansion/wg.yaml 中写入 WorkloadGroup legacy-billing 并应用到 expansion——spec.metadata.labels 为 app: billing,spec.template 为 serviceAccount: billing-sa、network: onprem、ports.http: 8080,spec.probe 为 periodSeconds: 5,并通过 httpGet 检查端口 8080 的 /healthz。
WorkloadGroup 相当于 Pod 的 Deployment——写的不是某台具体的服务器,而是这一组服务器共有的标签、身份、网络、端口和健康检查方式。network 的值指明这台服务器位于哪个网络,之后代理会根据这个值选择路径。
亲自体验 schema 会拒绝的两种情况
在 /root/ist-expansion/wg-bad-probe.yaml 中写入 WorkloadGroup bad-probe,但要在 spec.probe 中同时放入 httpGet 和 tcpSocket。在 /root/ist-expansion/wg-no-template.yaml 中写入 no-template,但要完全去掉 spec.template。两者都只做服务端试运行,并把拒绝的句子按顺序收集到 /root/ist-expansion/wg-reject.txt。
probe 被设定为只能在检查方式中选择一种——这是 schema 的 oneOf 约束。template 如果缺失,就不知道这一组的默认值,所以是必填项。请阅读拒绝的句子指向了哪个字段。
实际登记一台服务器,并看看笔误在哪里被抓到
在 /root/ist-expansion/we.yaml 中写入 WorkloadEntry billing-1 并应用——元数据标签 app: billing,address 为 10.20.30.41,network 为 onprem,serviceAccount 为 billing-sa,locality 为 dc1/rack2,ports.http 为 8080。然后在 /root/ist-expansion/we-typo.yaml 中写入 billing-typo,把地址写成 10.20.30 .41 这样带空格的形式并应用,把 istioctl analyze -n expansion -o json 的输出保存到 /root/ist-expansion/analyze-address.json。标签设为 app: billing-broken。
WorkloadEntry 是组内的一台服务器。地址格式不由 CRD schema 检查,所以奇怪的值也会被创建——抓到这种问题的是分析器。请确认它以哪个代码给出。
把登记的服务器绑定到网格的服务名称上
在 /root/ist-expansion/se.yaml 中写入 ServiceEntry billing-svc 并应用——hosts 只有一个 billing.expansion.internal,location 为 MESH_INTERNAL,resolution 为 STATIC,端口编号为 8080、名称为 http、协议为 HTTP,workloadSelector.labels 为 app: billing。
只创建了 WorkloadEntry 的话,没有可供调用的名称。ServiceEntry 创建网格内的服务名称,并通过 workloadSelector 选出排在该名称之后的工作负载。location 为 MESH_INTERNAL 时,它被视为网格成员,成为应用 mTLS 和策略的对象。
每种 resolution 的 endpoint 规则不同
创建三个 ServiceEntry 文件,并通过服务端试运行确认规则——/root/ist-expansion/se-none-endpoints.yaml(resolution: NONE 却有 endpoints)、/root/ist-expansion/se-roundrobin-two.yaml(resolution: DNS_ROUND_ROBIN,有两个 endpoints)、/root/ist-expansion/se-roundrobin-one.yaml(resolution: DNS_ROUND_ROBIN,有一个 endpoints)。把结果按上述顺序,以 <파일이름>\t<accepted|rejected>(占位符依次为文件名和判定结果)的格式写入 /root/ist-expansion/resolution-rules.tsv,并把拒绝的句子收集到 /root/ist-expansion/resolution-reject.txt。这三个文件都不要实际应用。
resolution 决定如何找到目标——NONE 直接使用原始目标 IP,STATIC 使用写明的 endpoint,DNS 则解析名称后使用。因此每种方式允许写几个 endpoint 各不相同。这条规则不是由 istioctl,而是由 CRD 的验证规则强制执行的。
实际生成那台服务器要取走的配置包
先创建 istio-system 命名空间,从 istioctl manifest generate --set profile=minimal 的渲染结果中只取出 ConfigMap istio,保存到 /root/ist-expansion/istio-cm.yaml 后再应用。然后在 expansion 中创建 ConfigMap istio-ca-root-cert——键名为 root-cert.pem,值为用 openssl 生成的自签名根证书(/root/ist-expansion/root-cert.pem)。最后运行 istioctl x workload entry configure -f /root/ist-expansion/wg.yaml -o /root/ist-expansion/vmcfg --clusterID lab-cluster 生成配置包。
VM 一侧的代理需要三样东西——网格配置(连接到哪里)、根证书(信任谁)、令牌(自己是谁)。前两者来自集群的 ConfigMap,令牌则由该命令用 ServiceAccount 直接签发。签发的令牌有效期很短,请不要打印到屏幕上。
统计选择器实际选中了什么,并留下报告
在 /root/ist-expansion/we2.yaml 中写入第二台服务器 billing-2 并应用——标签 app: billing,地址 10.20.30.42,network: onprem,serviceAccount: billing-sa,locality: dc1/rack3,ports.http 为 8080。然后创建 /root/ist-expansion/se-match.sh,让它针对 expansion 中每个带有 workloadSelector 的 ServiceEntry,按名称顺序只向标准输出打印 <ServiceEntry 이름>\t<잡힌 WorkloadEntry 수>\t<이름들 쉼표>(占位符依次为 ServiceEntry 名称、被选中的 WorkloadEntry 数量,以及用逗号连接的名称),并把该输出保存到 /root/ist-expansion/match-result.txt。
标签选择器可以像 kubectl get workloadentry -l app=billing 这样直接查询。选择器包含多个标签时,用逗号连接。如果让它在没有选中任何对象时打印 0 和连字符,那么将来标签不一致的那天就能立刻看出来。