博客:在 Intuit 运行 Kubeflow:融入服务网格
在现有 Kubernetes 集群中安装 Kubeflow 1.3,包含 Istio 服务网格和 Argo
在企业现有的 Kubernetes 基础设施、服务网格 (Istio) 和 Argo 环境中部署 Kubeflow 1.3 带来了一系列挑战。本文将探讨如何在克服这些挑战的同时,保留组织和 Kubeflow 推荐的最佳实践。
Intuit 的现状
Intuit 在构建强大的 Kubernetes 基础设施方面投入巨大,该基础设施支撑着 Intuit 的所有产品:TurboTax、QuickBooks 和 Mint。有数千个服务运行在一百多个 Kubernetes 集群上。管理这些集群的是 Intuit Kubernetes Service (IKS) 控制平面。IKS 控制平面提供命名空间管理、角色管理、隔离等服务。连接这些服务的是一个先进的、基于 Istio 的服务网格,它补充了 Intuit 的 API Gateway。两者结合提供了强大的身份验证、授权、速率限制和其他路由功能。
Intuit ML 平台建立在此生态系统之上,利用 Intuit Kubernetes 基础设施和 AWS SageMaker 的优势,提供模型训练、推理和特征管理能力。这是我们开始探索 Kubeflow 的背景,旨在提供高级编排、实验和其他服务。
Kubeflow 与 Istio
我们在运行 Kubeflow 时遇到的第一个挑战是 Kubeflow 自带的 Istio 与 Intuit 基于 Istio 构建的现有服务网格之间的兼容性。出现了两个关键问题:版本兼容性和运维维护。
Kubeflow v1.3 默认使用 Istio (v1.9),幸运的是它与 Intuit 正在运行的旧版本 Istio (v1.6) 兼容。运行两个 Istio 版本是不切实际的,因为那会丧失大型、互联的现有服务网格带来的好处。因此,我们希望 Kubeflow 能与 Intuit 运行 Istio v1.6 的服务网格无缝协作。
如果您刚接触 Istio,可能需要了解这些关键的流量管理组件和安全组件的入门知识
- VirtualService
- DestinationRule
- Gateway
- EnvoyFilter
- AuthorizationPolicy
步骤 1:从 Kubeflow 中移除默认的 Istio 配置和 Argo
运行 Kubeflow 的第一步是移除 Kubeflow 捆绑的 Istio 和 Argo,以便将其与 Intuit 服务网格集成。
移除 Kubeflow 的默认 Istio
我们使用 Kustomize 构建了 Kubeflow 安装所需的 Manifest,并使用 ArgoCD 部署 Kubeflow Kubernetes Manifest。
.
├── base # Base folder for the kubeflow out of the box manifests
│ ├── kustomization.yaml
│ ├── pipelines # Folder for Kubeflow Pipelines module
│ │ ├── kustomization.yaml
│ ├── other modules # Similar to the Pipelines module you can bring other modules as well
│ ├── kustomization.yaml
├── envs # Folder for all the Kubeflow environments
│ ├── prod
│ │ ├── kustomization.yaml
│ ├── dev
│ ├── kustomization.yaml
base -> kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- github.com/kubeflow/manifests/common/kubeflow-roles/base?ref=v1.3.0
- github.com/kubeflow/manifests/common/kubeflow-namespace/base?ref=v1.3.0
- github.com/kubeflow/manifests/common/oidc-authservice/base?ref=v1.3.0
- github.com/kubeflow/manifests/apps/admission-webhook/upstream/overlays/cert-manager?ref=v1.3.0
- github.com/kubeflow/manifests/apps/profiles/upstream/overlays/kubeflow?ref=v1.3.0
- github.com/kubeflow/manifests/apps/centraldashboard/upstream/overlays/istio?ref=v1.3.0
- pipelines
base -> pipelines -> kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- github.com/kubeflow/pipelines/manifests/kustomize/base/installs/multi-user?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/base/metadata/base?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/base/metadata/options/istio?ref=1.5.0
# To remove the default Argo from Pipelines module
# - github.com/kubeflow/pipelines/manifests/kustomize/third-party/argo/installs/cluster?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/third-party/mysql/base?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/third-party/mysql/options/istio?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/third-party/minio/base?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/third-party/minio/options/istio?ref=1.5.0
- github.com/kubeflow/pipelines/manifests/kustomize/third-party/metacontroller/base?ref=1.5.0
# Identifier for application manager to apply ownerReference.
# The ownerReference ensures the resources get garbage collected
# when application is deleted.
commonLabels:
application-crd-id: kubeflow-pipelines
# !!! If you want to customize the namespace,
# please also update base/cache-deployer/cluster-scoped/cache-deployer-clusterrolebinding.yaml
namespace: kubeflow
注意:我们不得不为 pipelines 创建一个单独的文件夹,因为我们不想使用 Pipelines 模块附带的 Argo。如果您可以使用默认的 Argo,那么可以直接使用 https://github.com/kubeflow/manifests/apps/pipeline/upstream/env/platform-agnostic-multi-user-pns?ref=v1.3.0
而不是 pipelines 文件夹。
如果您不想使用 ArgoCD,可以使用 kustomize build
命令构建 Manifest,这本质上是 ArgoCD 所做的事情。上述配置已在 Kustomize 3.8.x 和 4.0.x 上测试过,两者均可正常工作。
步骤 2:使用 Kustomize 定制 Kubeflow Manifest
考虑到 Intuit 的托管 Kubernetes 生态系统,服务间通信和命名空间隔离的协议是有特定要求的,我们需要进行以下更改:
- 通过在命名空间规范中添加标签
istio-injection: enabled
,为 Kubeflow 命名空间启用Istio 注入。Istio 然后使用此标签将 Sidecar 添加到命名空间中。 - 通过为 Deployments 和 StatefulSets 添加注解
sidecar.istio.io/inject: "true"
,以及一些 Intuit 特定的自定义标签和注解,为 Kubeflow 中的所有 Deployment 和 StatefulSet 启用 Sidecar 注入。 - Intuit 的安全策略禁止直接使用外部容器注册表。Intuit 内部容器注册表定期运行漏洞扫描,并认证 Docker 镜像可在各种环境中使用。内部容器注册表还有一个允许列表,允许对外部注册表进行代理,并使其符合同样的高安全标准。我们已为所有 Kubeflow 容器启用此功能。
- 修改 VirtualService,将所有流量从一个中心 Gateway 路由,而不是使用 Kubeflow Gateway。
我们使用 Kustomize 修改了 Kubeflow 应用 Manifest。
- 对于添加标签,我们使用了 LabelTransformer
apiVersion: builtin kind: LabelTransformer metadata: name: deployment-labels labels: <Intuit custom labels> istio-injected: "true" fieldSpecs: - path: spec/template/metadata/labels kind: Deployment create: true - path: spec/template/metadata/labels kind: StatefulSet create: true
-
对于添加注解,我们使用了 AnnotationsTransformer
apiVersion: builtin kind: AnnotationsTransformer metadata: name: deployment-annotations annotations: <Intuit custom annotations> sidecar.istio.io/inject: "true" fieldSpecs: - path: spec/template/metadata/annotations kind: Deployment create: true - path: spec/template/metadata/annotations kind: StatefulSet create: true
-
对于替换 Docker 镜像 URL,我们使用了 ImageTagTransformer
apiVersion: builtin kind: ImageTagTransformer metadata: name: image-transformer-1 imageTag: name: gcr.io/ml-pipeline/cache-deployer newName: docker.intuit.com/gcr-rmt/ml-pipeline/cache-deployer
对于任何需要通过代理访问互联网的组织来说,将所有容器镜像克隆到组织内部是最佳方案,因为这样就不需要互联网来访问这些镜像了。
-
对于转换 VirtualServices
- op: remove path: /spec/hosts/0 - op: replace path: /spec/gateways/0 value: <custom gateway> - op: add path: /spec/hosts/0 value: <kubflow host name> - op: add path: /spec/exportTo value: ["."]
-
整合所有配置
envs -> prod/dev -> kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - ../base transformers: - transformers/image-transformers.yaml - transformers/label-transformers.yaml - transformers/annotations-transformers.yaml patchesJson6902: # patch VirtualService with explicit host # add multiple targets like below for all the VirtualServices which you need - path: patches/virtual-service-hosts.yaml target: group: networking.istio.io version: v1alpha3 kind: VirtualService name: centraldashboard
- 您可能会遇到
metadata_envoy
服务的问题,在我们这里遇到了以下错误:[debug][init] [external/envoy/source/common/init/watcher_impl.cc:27] init manager Server destroyed unable to bind domain socket with id=0 (see --base-id option) 2021-01-29T23:32:26.680310Z error Epoch 0 exited with error: exit status 1
经过查找,我们发现当您使用 Istio Sidecar 注入运行此 Docker 镜像时,会出现此问题。原因是,这两个容器本质上都是 envoyproxy 容器,并且两个容器的默认 base-id 都设置为 0。
因此,为了使其工作,我们必须更改此Dockerfile中的 CMD。
CMD ["/etc/envoy.yaml", "--base-id", "1"]
步骤 3:SSO 所需的自定义更改
使用 SSO 进行身份验证主要涉及两个组件:
- Authservice:它是一个 StatefulSet,运行 oidc-auth 服务。它运行在 istio-system 命名空间中,直接与 OIDC 服务通信进行身份验证。
- Authn-filter:这是一个 EnvoyFilter,它过滤到 authservice 的流量,检查 Kubeflow 身份验证头,并在请求未授权时重定向到 authservice。它检查名为
kubeflow-userid
的头的存在。
注意:Intuit SSO 支持 OIDC,因此我们不需要使用 dex 进行集成。如果您的组织 SSO 不支持 OIDC,您可以在中间使用 dex;详细信息请参考此处。
对于我们的安装,我们需要启用 authservice 的服务网格功能,并且将 authservice 移动到 kubeflow
命名空间也更合理,因为该命名空间已经启用了 Istio Sidecar 注入。
在 authservice
上启用 Istio 网格后,默认 Manifest 还需要进行一些更改才能使其工作。authservice
Pod 无法与 Intuit SSO HTTPS URL 通信,因为来自主容器 Pod 的出站流量会被 Istio Sidecar 拦截以强制执行 mtls(默认行为)。因此,我们必须排除 HTTPS 端口 (443) 以禁用 mtls。这可以通过使用注解 traffic.sidecar.istio.io/excludeOutboundPorts: "443"
来完成。
步骤 4:设置 Ingress
我们使用以下机制将 istio-ingressgateway
服务暴露为 LoadBalancer:
- 在 Route 53 中设置公共托管区域,添加您想使用的主机名,例如
example.com
- 为 Kubeflow 安装中要使用的主机名设置 ACM 证书,主机名可以是
kubeflow.example.com
- 通过添加一些注解来更新服务 Manifest
# Note that the backend talks over HTTP. service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http # TODO: Fill in with the ARN of your certificate. service.beta.kubernetes.io/aws-load-balancer-ssl-cert: <cert arn from step 2> service.beta.kubernetes.io/aws-load-balancer-security-groups: <to restrict access within org> # Only run SSL on the port named "https" below. service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "https" external-dns.alpha.kubernetes.io/hostname: kubeflow.example.com
应用新的 Manifest 后,AWS 将自动在您的托管区域 (example.com
) 中添加相应的 A 和 TXT 记录,然后您就可以通过 kubeflow.example.com
访问 Kubeflow 了。
要使用 https 保护Gateway,您还可以更改 Gateway 端口并在 Gateway 中添加密钥和证书。
有关这些注解的更多信息,请参阅在 Amazon EKS 上终止 HTTPS 流量和AWS 上的 SSL 支持博客。
步骤 5:使用外部 Argo 安装
Kubeflow 内部使用 Argo 工作流以工作流方式运行管道。Argo 在工作流步骤完成后生成 Artifact,如果计划使用外部 Argo,我们只需配置 Artifact Store。
- 在您的集群中安装 Argo 工作流,它会被安装在一个名为 argo 的命名空间中。
- 从 Kubeflow 中移除所有与 Argo 相关的 Manifest。
- 要覆盖 Artifact Store,您需要更改随Kubeflow Manifest一起提供的 ConfigMap
workflow-controller-configmap
。它使用 minio 作为存储,但您也可以将其配置为使用 S3。更多详细信息请参阅ArgoWorkflow Controller Configmap GitHub 页面。 - 最新版本的 Argo 也提供了覆盖命名空间 Artifact Store 的选项。
调试技巧
-
检查 EnvoyFilter 是否已应用:您应该拥有 istioctl 命令行工具
istioctl proxy-config listeners <pod name> --port 15001 -o json
查看输出中是否列出了 Envoy Filter。有关 Istio 代理调试的更多信息,请参考此处。
-
检查 istio-ingressgateway
# Port forward to the first istio-ingressgateway pod kubectl -n istio-system port-forward $(kubectl -n istio-system get pods -listio=ingressgateway -o=jsonpath="{.items[0].metadata.name}") 15000 # Get the http routes from the port-forwarded ingressgateway pod (requires jq) curl --silent http://localhost:15000/config_dump | jq '\''.configs.routes.dynamic_route_configs[].route_config.virtual_hosts[]| {name: .name, domains: .domains, route: .routes[].match.prefix}'\'' # Get the logs of the first istio-ingressgateway pod # Shows what happens with incoming requests and possible errors kubectl -n istio-system logs $(kubectl -n istio-system get pods -listio=ingressgateway -o=jsonpath="{.items[0].metadata.name}") --tail=300 # Get the logs of the first istio-pilot pod # Shows issues with configurations or connecting to the Envoy proxies kubectl -n istio-system logs $(kubectl -n istio-system get pods -listio=pilot -o=jsonpath="{.items[0].metadata.name}") discovery --tail=300
-
检查 authservice 连接性:istio-ingressgateway Pod 应该能够访问 authservice。您可以使用以下命令进行检查:
kubectl -n istio-system exec $(kubectl -n istio-system get pods -listio=pilot -o=jsonpath="{.items[0].metadata.name}") -- curl -v http://authservice.istio-system.svc.cluster.local:8080
此外,请确保 authservice 可以访问 dex
在我们的案例中,authservice 位于 kubeflow 命名空间中,因此我们使用以下命令进行了相应的更改:
kubectl -n kubeflow exec authservice-0 -- wget -q -S -O '-' <oidc auth url>/.well-known/openid-configuration
输出应类似于:
{ "issuer": "http://dex.kubeflow.svc.cluster.local:5556/dex", "authorization_endpoint": "http://dex.kubeflow.svc.cluster.local:5556/dex/auth", "token_endpoint": "http://dex.kubeflow.svc.cluster.local:5556/dex/token", "jwks_uri": "http://dex.kubeflow.svc.cluster.local:5556/dex/keys", "userinfo_endpoint": "http://dex.kubeflow.svc.cluster.local:5556/dex/userinfo", "response_types_supported": [ "code" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "scopes_supported": [ "openid", "email", "groups", "profile", "offline_access" ], "token_endpoint_auth_methods_supported": [ "client_secret_basic" ], "claims_supported": [ "aud", "email", "email_verified", "exp", "iat", "iss", "locale", "name", "sub" ] }
-
检查服务之间的连接性:尝试使用 curl 或 wget 从一个服务访问另一个服务。通常其中一个总是可用的,否则,您随时可以使用
apt-get
命令安装。示例用例:从 ml-pipeline 部署 Pod 中,您可以检查管道 API 是否可访问。kubectl -n kubeflow exec $(kubectl -n kubeflow get pods -lapp=ml-pipeline-ui -o=jsonpath="{.items[0].metadata.name}") -- wget -q -S -O '-' ml-pipeline.kubeflow.svc.cluster.local:8888/apis/v1beta1/pipelines
对 Kubeflow 社区的请求
我们在 Intuit 遇到的挑战并非独有,任何希望采用 Kubeflow 的企业都会面临这些挑战。
很高兴看到 Kubeflow 能很好地与企业现有的 Kubernetes 基础设施配合使用,而不是强制要求一套自己的基础设施。这里有一些改进生态系统的建议/错误报告,其中一些 Intuit 将与社区合作进行构建:
- 我们看到 Kubeflow manifest 仓库在 v1.3 中进行了主要的文件夹结构重组,但我们认为仍有改进空间。
- 多集群 / 多区域支持。#5467
- 升级总体上似乎存在问题,应该找到更好的管理方式。#5440
- 支持组的多租户。#4188
- 在任意自定义命名空间中安装 Kubeflow。#5647
- 现有的 Metadata 服务性能不佳,我们尝试了增加资源和水平扩展的一些设置。社区已经在开发KFP v2.0,这可能会解决很多关于 Metadata 服务的问题。
参考资料