Architecting Kubernetes Deployments with Python
Architecting Kubernetes Deployments with Python
使用 Python 构建 Kubernetes 部署架构
Python is an excellent language for automating cloud infrastructure, but the official Kubernetes Python client leaves developers with an important architectural decision: Where should Kubernetes manifests live? Should they be constructed directly with Python objects? Embedded as multiline strings? Or stored as external files and rendered at runtime? Each approach works, but they have very different implications for readability, maintainability, and long-term operational cost.
Python 是一门用于自动化云基础设施的优秀语言,但官方的 Kubernetes Python 客户端给开发者留下了一个重要的架构决策:Kubernetes 清单(manifests)应该存放在哪里?是应该直接用 Python 对象构建?以多行字符串的形式嵌入?还是存储为外部文件并在运行时渲染?每种方法都可行,但它们在可读性、可维护性和长期运营成本方面有着截然不同的影响。
The key is recognizing that deployment logic and platform configuration evolve on different lifecycles. Your deployment code, the part that authenticates to Kubernetes, renders templates, and applies resources, may remain unchanged for months. Your manifests, however, often change weekly as applications evolve, resource limits are tuned, cloud-provider annotations are added, or networking requirements change. When those two concerns are tightly coupled, even a configuration-only change forces you to modify, test, and redeploy the delivery or application code itself. Over time, this increases maintenance costs, slows platform changes, and makes configuration drift and production mistakes more likely.
关键在于认识到部署逻辑和平台配置的生命周期是不同的。你的部署代码(即负责向 Kubernetes 进行身份验证、渲染模板并应用资源的部分)可能几个月都不会改变。然而,随着应用程序的演进、资源限制的调整、云服务商注解的添加或网络需求的变化,你的清单往往每周都在变。当这两个关注点紧密耦合时,即使只是配置变更,也会迫使你修改、测试并重新部署交付代码或应用程序本身。久而久之,这会增加维护成本,拖慢平台变更速度,并更容易导致配置漂移和生产事故。
This is a familiar software engineering principle: separate concerns that evolve independently. The same thinking that keeps application configuration separate from executable code also applies to Kubernetes manifests. Treating manifests as first-class configuration artifacts allows them to evolve independently from the Python code that delivers them. In this article we’ll compare three ways of deploying Kubernetes resources with the official kubernetes-python-client, ranging from tightly coupled implementations to a design that cleanly separates deployment logic from platform configuration.
这是一个熟悉的软件工程原则:分离独立演进的关注点。将应用程序配置与可执行代码分离的思路,同样适用于 Kubernetes 清单。将清单视为一等配置工件,可以使它们独立于交付它们的 Python 代码进行演进。在本文中,我们将比较使用官方 kubernetes-python-client 部署 Kubernetes 资源的三种方式,涵盖从紧耦合实现到将部署逻辑与平台配置清晰分离的设计。
The Landscape at a Glance
概览
The comparison below assumes a common application deployment scenario, where the desired state is largely known ahead of time. Controllers and Operators have fundamentally different requirements where the desired state is determined dynamically at runtime.
下表比较基于常见的应用程序部署场景,即预期状态在很大程度上是预先已知的。而控制器(Controllers)和操作器(Operators)有着根本不同的需求,其预期状态是在运行时动态确定的。
| Method¹ | Maintainability² | Readability³ | Flexibility⁴ | Best Used For |
|---|---|---|---|---|
| Direct API Object Calls | ⭐⭐⭐ Medium | ⭐ Low | ⭐⭐⭐⭐⭐ High | Dynamic, complex operators, custom controllers |
| Embedded Strings | ⭐⭐ Low | ⭐⭐⭐ Medium | ⭐⭐ Low | Small utilities, remediation scripts, one-off automation |
| External YAML Templates (Jinja2) | ⭐⭐⭐⭐⭐ High | ⭐⭐⭐⭐⭐ High | ⭐⭐⭐ Medium | Declarative, Standardized application deployments, CI/CD pipelines |
| 方法¹ | 可维护性² | 可读性³ | 灵活性⁴ | 最佳适用场景 |
|---|---|---|---|---|
| 直接 API 对象调用 | ⭐⭐⭐ 中等 | ⭐ 低 | ⭐⭐⭐⭐⭐ 高 | 动态、复杂的 Operator,自定义控制器 |
| 嵌入式字符串 | ⭐⭐ 低 | ⭐⭐⭐ 中等 | ⭐⭐ 低 | 小型工具、修复脚本、一次性自动化 |
| 外部 YAML 模板 (Jinja2) | ⭐⭐⭐⭐⭐ 高 | ⭐⭐⭐⭐⭐ 高 | ⭐⭐⭐ 中等 | 声明式、标准化的应用部署,CI/CD 流水线 |
Method refers to the overall approach used to define and deploy Kubernetes resources, not just features of the official kubernetes-python-client. Maintainability measures the cost to safely modify the solution over time. Readability measures ability of the engineer quickly determine the desired state and confidently modify it. Flexibility measures the ability to determine or modify the desired state dynamically at runtime.
方法指用于定义和部署 Kubernetes 资源的整体方案,而不仅仅是官方 kubernetes-python-client 的功能。
可维护性衡量随时间推移安全修改方案的成本。
可读性衡量工程师快速确定预期状态并自信地进行修改的能力。
灵活性衡量在运行时动态确定或修改预期状态的能力。
1. The Most Verbose: Direct API Object Calls
1. 最冗长的方式:直接 API 对象调用
The official client library (kubernetes-python-client) exposes the Kubernetes API as native Python objects. This provides complete programmatic control over every resource, but that flexibility comes at the cost of verbose, deeply nested object construction. Instead of expressing the desired state directly as a Kubernetes manifest, it is represented through a hierarchy of Python objects that are ultimately translated back into the Kubernetes API. This additional abstraction makes the desired state less obvious during code reviews and collaboration, since engineers must mentally reconstruct the resulting Kubernetes resource instead of reading it directly as a manifest.
官方客户端库 (kubernetes-python-client) 将 Kubernetes API 暴露为原生的 Python 对象。这提供了对每个资源的完全编程控制,但这种灵活性是以冗长、深度嵌套的对象构建为代价的。它不是直接将预期状态表示为 Kubernetes 清单,而是通过 Python 对象层级来表示,最终再转换回 Kubernetes API。这种额外的抽象使得在代码审查和协作过程中,预期状态变得不那么直观,因为工程师必须在脑海中重构最终的 Kubernetes 资源,而不是直接阅读清单。
The example below shows simple web application deployment and a service (type: LoadBalancer) with some annotations for AWS NLB.
下面的示例展示了一个简单的 Web 应用部署,以及一个带有 AWS NLB 注解的 Service(类型为 LoadBalancer)。
from kubernetes import client, config
config.load_kube_config()
apps_v1 = client.AppsV1Api()
core_v1 = client.CoreV1Api()
# Define the Deployment Object
deployment = client.V1Deployment(
metadata=client.V1ObjectMeta(name="demo-nlb-app-deployment"),
spec=client.V1DeploymentSpec(
replicas=3,
selector=client.V1LabelSelector(match_labels={"app": "demo-nlb-app"}),
template=client.V1PodTemplateSpec(
metadata=client.V1ObjectMeta(labels={"app": "demo-nlb-app"}),
spec=client.V1PodSpec(
containers=[client.V1Container(name="web", image="nginx:alpine")]
)
)
)
)
# Define the Service Object with AWS NLB Annotations
service = client.V1Service(
metadata=client.V1ObjectMeta(
name="demo-nlb-app-service",
annotations={
"service.beta.kubernetes.io/aws-load-balancer-type": "external",
"service.beta.kubernetes.io/aws-load-balancer-scheme": "internet-facing",
"service.beta.kubernetes.io/aws-load-balancer-nlb-target-type": "instance"
}
),
spec=client.V1ServiceSpec(
type="LoadBalancer",
selector={"app": "demo-nlb-app"},
ports=[client.V1ServicePort(port=80, target_port=80)]
)
)
# Apply to Cluster
apps_v1.create_namespaced_deployment(namespace="default", body=deployment)
core_v1.create_namespaced_service(namespace="default", body=service)
Notice that the desired state is no longer represented as a Kubernetes manifest. Instead, it is distributed across a hierarchy of Python object constructors. Understanding the resulting Deployment requires understanding both the Kubernetes resource model and the Python API simultaneously. This makes the deployed infrastructure less transparent during reviews, since the desired state must first be reconstructed from the implementation.
请注意,预期状态不再以 Kubernetes 清单的形式呈现,而是分布在 Python 对象构造函数的层级结构中。理解最终的 Deployment 需要同时理解 Kubernetes 资源模型和 Python API。这使得部署的基础设施在审查时透明度降低,因为必须先从实现代码中重构出预期状态。
The Verdict: This approach excels when the desired state cannot be known until runtime, such as Kubernetes Controllers, Operators, or other applications that continuously reconcile resources based on external events. For conventional application deployments, however, the additional flexibility rarely outweighs the increased verbosity and maintenance cost compared to declarative manifests.
结论:这种方法在预期状态无法预先确定时表现出色,例如 Kubernetes 控制器、Operator 或其他根据外部事件持续协调资源的应用程序。然而,对于常规的应用程序部署,相比声明式清单,这种额外灵活性带来的收益往往无法抵消其增加的冗长性和维护成本。