|
本文档采用自动化机器翻译技术翻译。 尽管我们力求提供准确的译文,但不对翻译内容的完整性、准确性或可靠性作出任何保证。 若出现任何内容不一致情况,请以原始 英文 版本为准,且原始英文版本为权威文本。 |
为Kubernetes开发
Rancher Desktop – SUSE应用程序集合 – Tilt
从开发环境到部署 – 内循环、可观测性、GitOps
1.概述:为什么要使用本指南?
本指南涵盖Kubernetes开发的*完整价值链*,从初始环境设置到持续部署。目标:使开发人员能够快速使用流行工具、本地群集(Rancher Desktop)和可信镜像(SUSE应用程序集合)提高生产力。
它分为两个部分:首先是一个*动手演示*,让您在几分钟内开始运行。然后是一个*深入了解*部分,涵盖更广泛的生态系统(开发容器、Testcontainers、mirrord、Helm、安全性、GitOps),以便您准备好深化工作流程时使用。
2.整体架构
2.2 堆栈层
| 层 | 工具 | 角色 |
|---|---|---|
IDE |
VS Code + 扩展 |
编辑器、调试、集成终端 |
本地群集 |
Rancher Desktop (k3s) |
本地Kubernetes + 容器运行时 |
图像 |
SUSE 应用程序集合 |
基础镜像、语言、中间件、工具 |
内循环 |
Tilt |
自动构建、自动部署、热重载 |
身份验证 |
Keycloak |
OAuth2 / OpenID Connect身份提供者 |
可观测性 |
Prometheus + Grafana |
应用程序指标,实时仪表板 |
打包 |
Helm / Kustomize |
K8s 清单模板化 |
安全性 |
Trivy / Cosign |
漏洞扫描,镜像签名 |
GitOps |
Argo CD |
从 Git 进行声明式部署 |
3.设置 Rancher Desktop
3.1 什么是 Rancher Desktop?
Rancher Desktop 是一个开源桌面应用程序,提供一个 本地群集 (k3s) 和一个 容器运行时 (dockerd 或 containerd),所有这些都在一个自动管理的虚拟机中。无需安装 Docker Desktop。
3.2 安装和配置
-
从 下载 rancherdesktop.io。
-
选择运行时: 选择 dockerd (moby) (而不是 containerd)。这对后续内容至关重要。
-
启用 Kubernetes (默认启用)。
-
校验:
docker info # shows "Server Version: ..." kubectl get nodes # shows "Ready" -
PATH – 在 macOS 上,验证
~/.rd/bin/是否在您的$PATH中(由安装程序自动添加):which docker kubectl helm # should point to ~/.rd/bin/
*Rancher Desktop 不是 Docker Desktop。*您不需要 Docker Desktop。Rancher Desktop 提供自己的 Docker 守护程序 (moby/dockerd)。同时安装两者可能会导致套接字冲突。如果您有 Docker Desktop,请禁用它。
3.3 当生产环境运行 containerd 时,为什么选择 dockerd (moby)?
在生产中,Kubernetes 使用 containerd 作为运行时。关键见解是:*Rancher Desktop 也如此,即使在 moby 模式下。*k3s 集群始终在 containerd 上运行,无论设置如何。选择 “dockerd (moby)” 并不会替代 containerd——它会在其旁边添加 Docker 守护程序,两者共享相同的镜像存储。
这使我们能够兼得两全其美:与生产相同的运行时,加上 Docker 友好的开发工具。
Rancher Desktop VM (moby mode): +---------------------------------------------+ | | | dockerd (moby) k3s (containerd) | | +-- image store <----> image store --+ | | (shared) (same!) | | | | | docker build -> image appears in both | | NO PUSH NEEDED! | +---------------------------------------------+ Rancher Desktop VM (containerd mode): +---------------------------------------------+ | | | nerdctl (containerd) k3s (containerd) | | +-- image store image store --+ | | (separate!) (separate!) | | | | | nerdctl build -> push -> pull -> k3s | | 3 steps instead of 1 = slower | +---------------------------------------------+
Docker 构建的镜像对 k3s 立即可见,因为它们共享相同的存储。没有注册表,没有推送,没有拉取。这就是内循环如此快速的原因。
*在一致性上毫不妥协。*在这两种情况下,您的应用程序都在 containerd 上运行。唯一的区别是用于构建镜像的 CLI:
docker(moby 模式) 与nerdctl(containerd 模式)。在运行时,k3s 的行为是完全相同的。
这也是为什么演示 K8s 部署使用 imagePullPolicy: IfNotPresent (或 Never):我们告诉 k3s “use the local image, do not look in a registry”。
4.SUSE 应用程序集合
4.1 什么是 SUSE 应用程序集合?
SUSE 应用程序集合 是一组以容器镜像和 Helm 图表形式存在的应用程序,由 SUSE 构建、打包、加固和维护——具有 SLSA L3 级别的构建和所有保持操作平稳所需的元数据。这是构建 Kubernetes 应用程序的可信来源。
注册表是 dp.apps.rancher.io。您将会找到:
-
基础镜像 (BCI) – SUSE Linux Enterprise Base Container Images:最小、安全的基础。
-
语言镜像 – Node.js、Go、Rust、Java、Ruby、Clojure……配备完整的工具链。
-
中间件 – PostgreSQL、Redis、Kafka、MariaDB、Nats、NGINX、Apache ActiveMQ、Apache Apisix、Apache Tomcat……
-
工具 – Helm、Trivy、Cosign、kubectl、ArgoCD、Prometheus、Grafana……
以单个容器的形式提供,或在相关情况下,提供带有 helm-charts 的完整应用程序以供部署。
Rancher Desktop 中的 SUSE 应用程序集合扩展 在用户界面中添加了一个专用标签。您可以浏览目录,配置值,并一键安装 – Helm 的复杂性被隐藏。
4.2 为什么选择应用程序集合而不是公共注册表?
| 公共注册表 | SUSE 应用程序集合 | |
|---|---|---|
维护 |
社区,变量 |
SUSE,企业 SLA |
基础操作系统 |
Alpine、Debian、Ubuntu…… |
SLE BCI (SUSE Linux Enterprise) |
安全增补程序 |
当维护者需要时 |
SUSE 持续的 CVE 跟踪 |
签名 |
可选 |
内置 Cosign |
供应链 |
变量 |
SBOM、溯源、证明、SLSA L3 |
4.3 身份验证
应用程序集合注册表的身份验证由 Rancher Desktop 中的 SUSE 应用程序集合扩展自动配置。
验证它是否有效:
docker pull dp.apps.rancher.io/containers/bci-base:latest
如果未配置身份验证,请手动添加:
# Log in to the registry (SUSE Customer Center credentials)
docker login dp.apps.rancher.io
# Verify
docker pull dp.apps.rancher.io/containers/bci-base:latest
对于 Kubernetes(helm 安装,pods) – 如果未拉取镜像,则需要一个拉取密钥。Rancher Desktop 通过扩展自动处理此操作。如果出现问题:
kubectl create secret docker-registry application-collection \
--docker-server=dp.apps.rancher.io \
--docker-username=<USERNAME> \
--docker-password=<PASSWORD>
然后在您的 Helm 值中添加 imagePullSecrets: [{name: application-collection}]。
5.安装 Tilt
Tilt 是一个开源工具,自动化内循环的每个步骤,从代码更改到重新部署。它监视您的文件,重建镜像,更新集群,并在实时仪表板中显示所有内容。请参见 tilt.dev。
5.2 Linux(SUSE 和其他)
curl -fsSL https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.sh | bash
该脚本检测您的架构并将二进制文件放置在您的 $PATH(~/.local/bin、/usr/local/bin 或 ~/bin)中。验证:
tilt version
5.3 Windows
在 PowerShell 中:
iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/tilt-dev/tilt/master/scripts/install.ps1'))
如果您已安装 Scoop,脚本将自动使用它。否则,您可能需要将安装目录添加到您的 $PATH。验证:
tilt version
6.演示:带有可观测性的消息墙
本节将逐步演示完整流程。它展示了内部循环工作流程:一个 Node.js “message wall” 应用程序连接到 PostgreSQL,使用 Keycloak 进行身份验证,使用 Prometheus 进行监控,并在 Grafana 中可视化。所有内容均从 SUSE 应用程序集合中安装。
完整的源代码可在 GitHub 上获取: fxHouard/Rancher-Developer-Access-Demo。
6.1 项目结构
Rancher-Developer-Access-Demo/ +-- src/ | +-- server.js Application (API + UI + Prometheus metrics) +-- k8s/ | +-- appco/ | | +-- deployment.yaml Pod spec with Prometheus annotations | | +-- service.yaml ClusterIP service | | +-- keycloak.yaml Keycloak Deployment + Service (Application Collection image) | +-- shared/ | +-- grafana-dashboard.yaml 8-panel dashboard (auto-provisioned via sidecar) | +-- keycloak-realm.json Realm config (demo user + OAuth client) +-- scripts/ | +-- setup-keycloak-realm.sh Keycloak realm import via Admin REST API +-- values_yaml/ | +-- postgresql.yaml Helm values for PostgreSQL | +-- prometheus.yaml Helm values for Prometheus | +-- grafana.yaml Helm values for Grafana +-- Dockerfile Container image (Application Collection base) +-- Tiltfile Inner loop config (build, deploy, sync, monitoring) +-- package.json
6.2 package.json
{
"name": "message-wall",
"version": "1.0.0",
"description": "SUSE Rancher Developer Access + Tilt: Demo",
"main": "src/server.js",
"scripts": {
"start": "node src/server.js"
},
"dependencies": {
"pg": "^8.13.0",
"prom-client": "^15.1.0"
}
}
仅有两个依赖项:pg 用于 PostgreSQL,prom-client 用于暴露 Prometheus 指标。
6.3 应用程序 (src/server.js)
该应用程序是一个带有内置 Web UI 的互动消息墙。它暴露 Prometheus 指标以便于可观测性。以下是关键部分——完整文件在仓库中。
配置和 Prometheus 指标:
const http = require('http');
const { Client } = require('pg');
const promClient = require('prom-client');
const PORT = 3000;
// Change this color, save, see it update!
const ACCENT_COLOR = "#747dcd";
// --- Prometheus metrics ---
// collectDefaultMetrics() automatically exposes Node.js
// metrics: CPU, heap memory, event loop lag, GC...
promClient.collectDefaultMetrics();
// Custom metrics -- prefixed "app_" for easy discovery
const httpDuration = new promClient.Histogram({
name: 'app_http_request_duration_seconds',
help: 'Duration of HTTP requests in seconds',
labelNames: ['method', 'path', 'status'],
buckets: [0.005, 0.01, 0.05, 0.1, 0.5, 1],
});
const messagesPosted = new promClient.Counter({
name: 'app_messages_posted_total',
help: 'Total number of messages posted',
});
const messagesDeleted = new promClient.Counter({
name: 'app_messages_deleted_total',
help: 'Total number of bulk deletes',
});
const messagesCurrent = new promClient.Gauge({
name: 'app_messages_count',
help: 'Current number of messages in the database',
});
为什么使用
app_前缀? – Prometheus 收集数百个指标(Node.js、k8s、系统……)。app_前缀让您可以立即找到应用程序指标:在 Grafana 中输入app_,自动完成功能会处理其余部分。
Prometheus 指标类型:
| 类型 | 用法 | 演示示例 |
|---|---|---|
计数器 |
只会增加的值 |
|
仪表 |
上下波动的值 |
|
柱状图 |
值的分布(延迟) |
|
API 路由:
该应用程序暴露了 6 个路由:GET / 提供 HTML 页面,GET /api/messages 列出最近 50 条消息,POST /api/messages 创建一条消息(限制 280 个字符),DELETE /api/messages 删除所有消息,GET /health 作为 K8s 探针(活跃性 + 就绪),GET /metrics 以文本格式暴露 Prometheus 指标。
-
/metrics端点:*
if (req.method === 'GET' && req.url === '/metrics') {
const metrics = await promClient.register.metrics();
res.writeHead(200, { 'Content-Type': promClient.register.contentType });
res.end(metrics);
// Do not record /metrics in the histogram (noise)
return;
}
这是 Prometheus 定期抓取的端点。它以 OpenMetrics 文本格式返回所有指标。请注意,/metrics 本身并不被直方图测量——那将是噪声。
测量中间件:
每个 HTTP 请求会自动计时:
const end = httpDuration.startTimer();
// ... request processing ...
end({ method: req.method, path: routePath, status: statusCode });
直方图记录持续时间、方法、路径和返回代码。Grafana 然后可以计算每个路由的百分位数(p50、p95、p99)。
HTML 页面:
该应用程序直接从 Node.js 提供交互式消息墙(内联 HTML 在 server.js 中)。用户界面包括一个输入字段、一个显示 pod 名称和运行时间的信息栏,以及每 3 秒自动轮询并进行智能差异处理(如果消息没有变化,则仅更新时间戳,不会闪烁)。
实时更新演示 – 更改第 9 行的
ACCENT_COLOR常量,保存。大约 2 秒后,墙的颜色会改变,消息不会丢失。这就是 Tilt 的实时更新功能在实际运行中的表现。
6.4 Dockerfile
FROM dp.apps.rancher.io/containers/nodejs:24-dev
WORKDIR /app
COPY package.json ./
RUN npm install --no-package-lock
COPY . .
EXPOSE 3000
CMD ["node", "src/server.js"]
在生产环境中,您将使用多阶段构建将
npm install步骤(带有 npm 的开发镜像)与最终镜像(最小的nodejs:24镜像,不带 npm 或构建工具)分开。在这里,我们为演示保留一个简单的 Dockerfile。请参见 进一步了解:最小镜像。
6.5 Kubernetes 清单
k8s/deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: message-wall
spec:
replicas: 1
selector:
matchLabels:
app: message-wall
template:
metadata:
labels:
app: message-wall
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "3000"
prometheus.io/path: "/metrics"
spec:
containers:
- name: message-wall
image: message-wall # Tilt replaces with local image
ports:
- containerPort: 3000
env:
- name: DB_HOST
value: "demo-db-postgresql"
- name: DB_PORT
value: "5432"
- name: DB_USER
value: "demo"
- name: DB_PASSWORD
value: "demo"
- name: DB_NAME
value: "demo"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 5
Prometheus 注释 –
template.metadata.annotations下的三个注释告诉 Prometheus:“抓取此 pod,端口 3000,路径/metrics”。Prometheus 服务器默认配置为 Kubernetes 自动发现,自动检测带注释的 pod。无需额外的 Prometheus 配置。
注意缩进 – 注释必须在
template.metadata(pod 模板)下,而不是在 Deployment 的metadata或spec级别下。这是一个常见错误:如果注释位于错误的级别,Prometheus 将无法找到您的 pod。
k8s/service.yaml:
apiVersion: v1
kind: Service
metadata:
name: message-wall
spec:
selector:
app: message-wall
ports:
- port: 3000
targetPort: 3000
6.6 安装 PostgreSQL、Prometheus 和 Grafana
这三者都是基础设施服务 – 通过 Rancher Desktop 一次性安装,而不是在每个 tilt up 上安装。它们在开发会话之间保持持久。
该仓库在 values_yaml/ 中为每个服务包含 Helm 值文件。在 Rancher Desktop 应用程序的集合选项卡中,搜索每个图表,切换到 YAML 模式,并粘贴相应的文件。
values_yaml/postgresql.yaml:
auth:
database: demo
postgresPassword: demo
postgresUsername: demo
username: demo
global:
imagePullSecrets:
- application-collection
values_yaml/prometheus.yaml:
alertmanager:
service:
type: NodePort
global:
imagePullSecrets:
- application-collection
values_yaml/grafana.yaml:
adminPassword: admin
global:
imagePullSecrets:
- application-collection
sidecar:
dashboards:
enabled: true
datasources:
enabled: true
imagePullSecrets– 每个值文件引用application-collection秘密,以便 Pod 可以从dp.apps.rancher.io拉取镜像。该秘密由 Rancher Desktop 扩展自动创建。
Grafana 边车 –
sidecar.dashboards.enabled和sidecar.datasources.enabled设置至关重要。它们在 Grafana 旁边启动小型容器,监视具有特定标签的 Kubernetes ConfigMaps,并自动将其内容加载到 Grafana 中。无需手动配置数据源或导入仪表板。
| 边车 | 监视标签 | 效果 |
|---|---|---|
|
|
自动加载仪表板 JSON 文件 |
|
|
自动配置数据源 |
验证边车是否处于活动状态:
kubectl get pods -l app.kubernetes.io/name=grafana \
-o jsonpath='{.items[0].spec.containers[*].name}'
# Expected: grafana grafana-sc-dashboard grafana-sc-datasources
如果您只看到 grafana,请返回 Rancher Desktop UI,验证 sidecar.dashboards.enabled 和 sidecar.datasources.enabled 是否为 true,然后 升级 图表。
PostgreSQL 的 Helm 图表会自动创建用户、数据库和名为
<release-name>-postgresql的服务。Tiltfile 通过app.kubernetes.io/name=postgresql标签自动检测此服务 – 无需记住发布名称。
6.7 Keycloak:没有 Helm 图表的身份验证
Keycloak 为消息墙提供 OAuth2 / OpenID Connect 身份验证。用户登录,应用程序在允许他们发布或删除消息之前验证他们的身份词元。
为什么没有 Helm 安装? – 与 PostgreSQL、Prometheus 和 Grafana 不同,SUSE 应用程序集合中没有 Keycloak 的 Helm 图表。它仅作为容器镜像可用 (dp.apps.rancher.io/containers/keycloak)。这是一个现实场景:并不是每个应用程序都提供 Helm 图表,开发人员需要知道如何部署原始 Kubernetes 清单。
k8s/appco/keycloak.yaml (simplified):
apiVersion: apps/v1
kind: Deployment
metadata:
name: keycloak-appco
spec:
replicas: 1
selector:
matchLabels:
app: keycloak
template:
metadata:
labels:
app: keycloak
spec:
volumes:
- name: realm-config
configMap:
name: keycloak-realm
containers:
- name: keycloak
image: dp.apps.rancher.io/containers/keycloak:26.5.4
args: ["start-dev", "--health-enabled=true", "--import-realm"]
volumeMounts:
- name: realm-config
mountPath: /opt/keycloak/data/import
env:
- name: KC_DB
value: postgres
- name: KC_DB_URL
value: jdbc:postgresql://PLACEHOLDER_PG_SVC:5432/keycloak
# ... KC_DB_USERNAME, KC_DB_PASSWORD, KEYCLOAK_ADMIN, etc.
readinessProbe:
httpGet:
path: /health/ready
port: 9000
initialDelaySeconds: 30
imagePullSecrets:
- name: application-collection
要点:
start-dev+--import-realm– Keycloak 以开发模式启动(HTTP,无证书),并自动导入在/opt/keycloak/data/import中找到的任何 JSON 领域文件。领域 ConfigMap – 一个包含领域 JSON 的 ConfigMap (
keycloak-realm) 被挂载为一个卷。此 ConfigMap 由k8s/shared/keycloak-realm.json中的 Tiltfile 创建。
PLACEHOLDER_PG_SVC– Tiltfile 在部署时用通过标签选择器发现的实际 PostgreSQL 服务名称替换此项。单独的数据库 – Keycloak 在同一 PostgreSQL 实例中使用专用
keycloak数据库。如果不存在,Tiltfile 会自动创建它。
领域设置脚本 (scripts/setup-keycloak-realm.sh):
Tiltfile 还通过 Admin REST API 运行设置脚本作为后备。该脚本是幂等的:它检查领域是否已存在,获取管理员词元,并在需要时创建领域。这处理了 ConfigMap 导入未被识别的情况(例如,Keycloak 在 ConfigMap 创建之前已经在运行)。
6.8 Grafana 仪表板(自动配置的 ConfigMap)
Grafana 仪表板在 Kubernetes ConfigMap 中定义。由于在上一步中启用了边车,它会自动加载 – 无需手动导入。
k8s/grafana-dashboard.yaml(结构 – 完整文件在仓库中):
apiVersion: v1
kind: ConfigMap
metadata:
name: message-wall-dashboard
labels:
grafana_dashboard: "1" # the sidecar detects this label
data:
message-wall.json: |
{
"uid": "message-wall",
"title": "Message Wall",
"panels": [ ... ]
}
uid: "prometheus"– 每个面板通过"uid": "prometheus"引用数据源。此 uid 必须 完全匹配 Tiltfile 生成的数据源 ConfigMap 中声明的 uid(见下一节)。如果 JSON 使用${DS_PROMETHEUS}(Grafana UI 导入语法),边车 将 不 解析该变量 – 您必须使用硬编码的 uid。
仪表板包含8个面板:
| 面板 | 类型 | 度量 | 它显示的内容 |
|---|---|---|---|
数据库中的消息 |
统计 |
|
带有阈值的仪表:绿色 < 50 < 黄色 < 100 < 红色 |
发布的消息 |
统计 |
|
发布的消息总计计数器 |
批量删除 |
统计 |
|
批量删除总计计数器 |
请求/秒 |
时间序列 |
|
每个 HTTP 路由的吞吐量 |
每分钟帖子数 |
时间序列 |
|
发布率 |
响应时间(p95) |
时间序列 |
|
每个路由的第95百分位延迟 |
内存用量 |
时间序列 |
|
RSS + Node.js 堆 |
事件循环延迟(p99) |
时间序列 |
|
事件循环健康状况 |
为什么`rate(…[5m])
而不是[1m]`? – `rate()`函数需要至少2个数据点在窗口内。如果普罗米修斯每60秒抓取一次,那么1分钟的窗口只有一个点,什么也不返回。经验法则:将`rate()`窗口设置为至少*2倍抓取间隔*。5分钟是一个安全的默认值。
声明式配置 – 仪表板在Git中,和代码一起版本控制。如果您在Grafana中修改它(添加面板,改变查询),请导出并更新ConfigMap,以便在下次重新部署时不会丢失更改。这是应用于可观测性的*基础架构即代码*方法。
6.9 完整的Tiltfile
# Demo Tiltfile
load('ext://restart_process', 'docker_build_with_restart')
allow_k8s_contexts('rancher-desktop')
# --- Helpers ---------------------------------------------------------
def find_service(label_selector, required=False, name='Service'):
# Discover a Kubernetes service by label selector.
#
# Helm charts installed via Rancher Desktop get random release
# names (e.g. postgresql-1772033328). Searching by label is
# robust regardless of the release name.
svc = str(local(
"kubectl get svc -l " + label_selector +
" -o jsonpath='{.items[0].metadata.name}'",
quiet=True,
)).strip()
if required and svc == '':
fail(name + ' not found. Install it via Rancher Desktop ' +
'(Application Collection).')
return svc
# --- Service discovery -----------------------------------------------
pg_svc = find_service(
'app.kubernetes.io/name=postgresql',
required=True,
name='PostgreSQL',
)
grafana_svc = find_service('app.kubernetes.io/name=grafana')
prometheus_svc = find_service(
'app.kubernetes.io/name=prometheus,app.kubernetes.io/component=server',
)
# --- Application -----------------------------------------------------
docker_build_with_restart(
'message-wall',
'.',
entrypoint=['node', 'src/server.js'],
only=['src/', 'package.json'],
live_update=[
sync('./src', '/app/src'),
run('cd /app && npm install --no-package-lock',
trigger=['package.json']),
],
)
deployment = str(read_file('k8s/appco/deployment.yaml')).replace(
'PLACEHOLDER_PG_SVC', pg_svc)
service = str(read_file('k8s/appco/service.yaml'))
k8s_yaml([blob(deployment), blob(service), 'k8s/shared/grafana-dashboard.yaml'])
k8s_resource(
'message-wall-appco',
port_forwards='3000:3000',
labels=['app'],
)
# --- Keycloak (no Helm chart — deployed as raw K8s manifest) ---------
# Ensure keycloak DB exists in PostgreSQL
pg_pod = str(local(
"kubectl get pods -l app.kubernetes.io/name=postgresql "
"-o jsonpath='{.items[0].metadata.name}'", quiet=True)).strip()
local("kubectl exec " + pg_pod +
" -- env PGPASSWORD=demo psql -U demo -tc "
"\"SELECT 1 FROM pg_database WHERE datname='keycloak'\""
" | grep -q 1 || kubectl exec " + pg_pod +
" -- env PGPASSWORD=demo psql -U demo -c 'CREATE DATABASE keycloak'",
quiet=True)
# Realm ConfigMap (auto-imports realm with demo user + OAuth client)
local('kubectl create configmap keycloak-realm '
'--from-file=message-wall.json=k8s/shared/keycloak-realm.json '
'--dry-run=client -o yaml | kubectl apply -f -', quiet=True)
# Deploy Keycloak using the Application Collection container image
keycloak_yaml = str(read_file('k8s/appco/keycloak.yaml')).replace(
'PLACEHOLDER_PG_SVC', pg_svc)
k8s_yaml(blob(keycloak_yaml))
k8s_resource('keycloak-appco', port_forwards='8080:8080', labels=['app'])
# Realm setup via Admin REST API (idempotent fallback)
local_resource(
'keycloak-realm-setup',
cmd='bash scripts/setup-keycloak-realm.sh http://localhost:8080 '
'k8s/shared/keycloak-realm.json',
labels=['app'],
resource_deps=['keycloak-appco'],
)
# --- Monitoring (optional) -------------------------------------------
if grafana_svc:
local_resource(
'grafana',
serve_cmd='kubectl port-forward svc/' + grafana_svc + ' 3001:80',
labels=['monitoring'],
allow_parallel=True,
links=['http://localhost:3001',
'http://localhost:3001/d/message-wall/'],
)
if prometheus_svc:
local_resource(
'prometheus',
serve_cmd='kubectl port-forward svc/' + prometheus_svc
+ ' 9090:80',
labels=['monitoring'],
allow_parallel=True,
links=['http://localhost:9090'],
)
datasource_cm = """apiVersion: v1
kind: ConfigMap
metadata:
name: grafana-datasource-prometheus
labels:
grafana_datasource: "1"
data:
prometheus.yaml: |
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
uid: prometheus
url: http://{svc}:80
access: proxy
isDefault: true
editable: false
""".format(svc=prometheus_svc)
k8s_yaml(blob(datasource_cm))
k8s_resource(
objects=['message-wall-dashboard:configmap',
'grafana-datasource-prometheus:configmap'],
new_name='grafana-config',
labels=['monitoring'],
links=['http://localhost:3001/d/message-wall/'],
)
Tiltfile的关键点:
find_service()`助手 – 通过Rancher Desktop UI安装的服务具有随机的发布名称(例如,`grafana-1772033328)。助手通过Helm图表设置的Kubernetes标签发现这些名称,而不是硬编码这些名称。这是一个稳健的模式:Tiltfile无论发布名称如何都能正常工作。
docker_build_with_restart – `restart_process`扩展解决了特定于解释性语言的问题。当`live_update`将文件同步到容器中时,Node.js并没有看到它——代码已经加载到内存中。`docker_build_with_restart`包装入口点,以便在每次同步后自动重启进程。对于编译语言(Go,Rust),标准的`docker_build`在`run()`中有一个编译步骤是常见的方法。
将Prometheus数据源作为ConfigMap – 与其在Grafana UI中配置Prometheus(手动配置在重新部署时丢失),Tiltfile生成一个带有`grafana_datasource: "1"`标签的ConfigMap。Grafana边车检测到它并自动配置连接。Prometheus URL是动态注入的:http://<detected-service-name>:80。
links – 每个`local_resource`和`k8s_resource`可以在Tilt仪表板中声明可点击的链接。您可以直接在Tilt UI中看到Grafana、Prometheus和特定仪表板的URL。
objects + new_name – ConfigMaps不是工作负载(Deployment,StatefulSet…),因此Tilt默认将它们归档到"`uncategorized`"。objects 指令将它们归类为一个明确的名称 (grafana-config),并带有 monitoring 标签。
Keycloak 部署 – 由于在 SUSE 应用程序集合中没有 Keycloak 的 Helm 图表,Tiltfile 将其作为原始 K8s 清单进行部署。它首先确保 PostgreSQL 中存在一个 keycloak 数据库,创建一个包含领域 JSON 的 ConfigMap,部署 Keycloak 部署(将 PLACEHOLDER_PG_SVC 替换为发现的服务名称),并通过 Admin REST API 运行一个设置脚本,作为幂等的后备方案。
条件性 – 监控块是有条件的 (if grafana_svc / if prometheus_svc)。如果没有安装 Prometheus 和 Grafana,Tiltfile 仍然可以工作 – 只需要 APP 和 PostgreSQL。可观测性是一个可选的附加功能。
6.10 运行演示
# 1. Clone the repo
git clone https://github.com/fxHouard/Rancher-Developer-Access-Demo.git
cd Rancher-Developer-Access-Demo
# 2. Install services via Rancher Desktop (one time only):
#
# PostgreSQL:
# Application Collection tab -> search PostgreSQL -> Install
# Switch to YAML mode, paste values_yaml/postgresql.yaml, Install
#
# Prometheus:
# Application Collection tab -> search Prometheus -> Install
# Switch to YAML mode, paste values_yaml/prometheus.yaml, Install
#
# Grafana:
# Application Collection tab -> search Grafana -> Install
# Switch to YAML mode, paste values_yaml/grafana.yaml, Install
# 3. Start the inner loop:
tilt up
# 4. Press Space to open the Tilt dashboard in your browser
# 5. From the Tilt dashboard, click the links to:
# -> http://localhost:3000 -- The Message Wall app
# -> http://localhost:8080 -- Keycloak (admin / admin)
# -> http://localhost:9090 -- Prometheus (check targets)
# -> http://localhost:3001 -- Grafana (admin / admin)
# 6. In Grafana: go to Dashboards -> "Message Wall" is already there
# (or click the direct link in Tilt)
# 7. Post messages on localhost:3000 and watch the metrics
# update in real time in Grafana
# 8. Change ACCENT_COLOR in src/server.js (line 9)
# -> save -> ~2 sec -> the color changes
6.11 背后的工作原理
1. `tilt up` on the host: +-- Auto-detects PostgreSQL (label app.kubernetes.io/name=postgresql) +-- Auto-detects Prometheus and Grafana (labels app.kubernetes.io/name=...) +-- Creates keycloak DB in PostgreSQL if needed +-- docker build message-wall -> image in dockerd local store | +-- k3s sees the image because same store -> imagePullPolicy: IfNotPresent +-- kubectl apply deployment + service -> k3s creates the app pod | +-- Pod connects to detected PG service (K8s internal DNS) | +-- Prometheus scrapes the pod (prometheus.io/* annotations) +-- Deploys Keycloak (Application Collection image, raw K8s manifest) | +-- Imports realm via ConfigMap volume mount | +-- Runs setup script via Admin REST API (idempotent fallback) +-- kubectl apply ConfigMaps (datasource + dashboard) | +-- Grafana sidecars detect and load them +-- Port-forward 3000 -> localhost:3000 (app) +-- Port-forward 8080 -> Keycloak +-- Port-forward 3001 -> Grafana +-- Port-forward 9090 -> Prometheus 2. Dev modifies src/server.js: +-- Tilt (on host) detects the change (file watcher) +-- live_update syncs ./src -> /app/src in the pod (kubectl cp) +-- restart_process relaunches `node src/server.js` in the container +-- ~2 seconds later: change visible on localhost:3000 3. Observability loop (continuous): +-- Prometheus scrapes localhost:3000/metrics every 15-60s +-- Grafana queries Prometheus to display dashboards +-- Dev sees the impact of their changes in real time
6.12 端口转发解释
Tilt 通过 port_forwards 在 k8s_resource() 中自动管理端口转发。它相当于 kubectl port-forward,但集成在 Tilt 的生命周期中(如果 pod 被重新创建,则自动重启)。
完整的端口转发链:
Browser (localhost:3000)
-> Tilt port-forward
-> K8s Service (ClusterIP)
-> Your app pod (:3000)
-> Connects to PostgreSQL Service (:5432)
-> PostgreSQL pod
您的 APP 在 pod 中使用 Kubernetes 内部 DNS 访问 PostgreSQL:
postgresql://user:pass@demo-db-postgresql.default.svc.cluster.local:5432/mydb
从您的本地机器(例如 SQL 客户端):kubectl port-forward svc/demo-db-postgresql 5432:5432.
7.完整的工作流程
| # | 步骤 | 工具 | 细节 |
|---|---|---|---|
1 |
编写代码 |
VS Code + 扩展 |
编辑器、自动补全、代码检查、Git |
2 |
迭代(内部循环) |
Tilt |
自动构建 + 实时同步 + 仪表板 |
3 |
鉴定 |
Keycloak |
OAuth2 登录,领域由 Tilt 自动配置 |
4 |
观察 |
Prometheus + Grafana |
实时指标,自动配置的仪表板 |
5 |
提交 + 推送 |
Git |
GitOps 的真实来源 |
6 |
构建 + 扫描(外部循环) |
CI 管道 + Trivy + Cosign |
漏洞 + 镜像签名 |
7 |
部署 |
Argo CD |
自动同步 Git → 集群 |
8 |
渐进式发布 |
Argo Rollouts |
金丝雀 / 蓝绿部署与分析 |
8.进一步发展
上面的演示涵盖了内部循环的基本要素。本节介绍了随着项目增长而补充工作流程的额外工具和实践。
8.1 开发容器:可重现的环境
原则:您的开发环境在 代码中 定义(.devcontainer/)。每个打开项目的开发者都获得完全相同的环境,使用相同的工具、相同的版本、相同的 VS Code 扩展。
开发容器是一个开发容器——它仅用于编码。它不需要 Docker、kubectl 或 Helm.Tilt 在主机上运行。
.devcontainer/Dockerfile:
FROM dp.apps.rancher.io/containers/nodejs:24-dev
# System tools (available in the SLE_BCI repo)
# gawk: required by VS Code Server (check-requirements.sh)
RUN zypper --non-interactive install -y git openssh make gawk \
&& zypper clean -a
.devcontainer/devcontainer.json:
{
"name": "Message Wall - Node.js",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"extensions": [
"ms-kubernetes-tools.vscode-kubernetes-tools"
],
"settings": {
"vs-kubernetes.disable-linters": true
}
}
},
"postCreateCommand": "if [ -f package.json ]; then npm install --no-package-lock; fi"
}
开发容器中没有
docker、kubectl、helm或tilt。Tilt 在主机上运行。Kubernetes 扩展为 K8s 清单编辑提供 IntelliSense。kubeconfig not found警告是预期的且无害的——集群浏览器在容器内无法工作,但自动补全可以。
当 VS Code 连接到开发容器时,所有集成终端都在 容器内 打开。但 Tilt 必须在主机上运行(docker、kubectl 和 helm 所在的地方)。使用单独的终端(Terminal.app、iTerm2、Warp、Windows Terminal…)或在同一文件夹中打开的第二个 VS Code 窗口 不 “Reopen in Container”。
8.2 资源请求和限制
在演示中,为了简单起见,部署没有 resources 块。在生产环境(或共享集群)中,您应该始终定义它们:
resources:
requests:
cpu: 100m # 0.1 vCPU -- scheduler reserves this
memory: 128Mi # 128 MB -- guaranteed minimum
limits:
cpu: 500m # 0.5 vCPU -- ceiling, throttled beyond
memory: 256Mi # 256 MB -- OOMKill beyond
没有 requests 或 limits,一个 pod 可能会消耗所有节点资源并影响其他工作负载——“noisy neighbor” 问题。requests 是为调度器(智能放置)准备的,limits 保护节点(处理器 限流,如果内存超出则 OOMKill)。
8.3 Testcontainers:集成测试
*概念:*Testcontainers 是一个库,可以在您的集成测试中启动短暂的 Docker 容器。需要一个 PostgreSQL 来测试您的 SQL 查询吗?Testcontainers 启动一个,运行您的测试,并在完成后销毁它。
为什么重要:
-
可重复性: 每个测试都启动一个新的数据库——测试之间没有污染。
-
没有模拟: 您是针对真实数据库进行测试,而不是可能偏离的模拟。
-
CI 友好: 在 GitHub Actions、GitLab CI 等中工作(只需 Docker)。
Testcontainers 使用主机的 Docker 守护进程。使用 Rancher Desktop (dockerd),它可以直接工作 - 无需特殊配置。
配置:
# macOS / Linux -- already configured if ~/.rd/bin is in PATH
export DOCKER_HOST=unix://$HOME/.rd/run/docker.sock
示例 (Node.js):
const { GenericContainer } = require('testcontainers');
const { Client } = require('pg');
describe('Database integration', () => {
let container, client;
beforeAll(async () => {
container = await new GenericContainer('dp.apps.rancher.io/containers/postgresql:17')
.withExposedPorts(5432)
.withEnvironment({ POSTGRES_USER: 'test', POSTGRES_PASSWORD: 'test', POSTGRES_DB: 'test' })
.start();
client = new Client({
host: container.getHost(),
port: container.getMappedPort(5432),
user: 'test', password: 'test', database: 'test',
});
await client.connect();
});
afterAll(async () => {
await client.end();
await container.stop();
});
test('should insert and retrieve data', async () => {
await client.query('CREATE TABLE test (id SERIAL, name TEXT)');
await client.query("INSERT INTO test (name) VALUES ('hello')");
const result = await client.query('SELECT * FROM test');
expect(result.rows).toHaveLength(1);
});
});
Testcontainers 与 Tilt+Helm:
| Testcontainers | Tilt + Helm | |
|---|---|---|
生命周期 |
临时(1 次测试运行) |
持久(开发会话持续时间) |
数据 |
每次运行都是全新的 |
持久(除非被清除) |
用法 |
CI 中的集成测试 |
每日本地开发 |
8.4 mirrord:在远程集群上进行本地调试
mirrord 允许您在连接到远程 Kubernetes 集群中的 pod 的网络和文件系统时运行本地进程。代码在本地运行,但 “sees” 集群环境。
No need for mirrord if 您有一个包含所有依赖项的本地集群,并且您的 APP 有 1 到 5 个微服务,则无需使用 mirrord.
mirrord becomes interesting when 应用程序有 20 个以上微服务(无法在本地运行所有内容),您需要本地不可用的托管服务,或者您想要在真实集群的上下文中进行本地调试(在 VS Code 中设置断点)时,mirrord 变得有趣。
制胜组合 - Tilt 用于每日内部循环(本地集群),mirrord 用于偶尔在预发布环境中调试。这两个工具是互补的。
8.5 Helm 和 Kustomize
Helm 让您为 Kubernetes 清单创建模板并将其分发为 “charts”。它相当于 Kubernetes 的包管理器(npm、zypper…)。 values.yaml 根据环境(开发、预发布、生产)自定义部署。Helm 跟踪已部署的版本并支持回滚。
Kustomize 通过 叠加补丁 在基础 YAML 清单上工作。没有模板语言:您应用声明式转换。原生集成到 kubectl 中 (kubectl apply -k)。
Helm 还是 Kustomize? – 这两者并不互斥。一种常见的方法:使用 Helm 处理外部图表(数据库、监控),使用 Kustomize 根据环境自定义自己的清单。
8.6 安全性:Trivy 和 Cosign
Trivy(在应用程序集合中可用)扫描您的容器镜像、Kubernetes 文件、依赖项以及基础架构即代码,以检测已知漏洞(CVE)、错误配置和暴露的秘密。
trivy image dp.apps.rancher.io/containers/nodejs:24
trivy k8s --report summary cluster
Cosign(在应用程序集合中可用)允许您对容器镜像进行加密签名。它是 供应链安全 的支柱。
cosign sign --key cosign.key myregistry/myapp:v1.0
cosign verify --key cosign.pub myregistry/myapp:v1.0
8.7 GitOps:Argo CD 和 Argo Rollouts
GitOps 是这样一个原则:Git 是您基础设施和应用程序所需状态的唯一真实来源。一个部署工具监视 Git 储存库,并自动将集群状态与 Git 中声明的状态进行协调。
Argo CD(在应用程序集合中可用)是一个声明式的 Kubernetes 持续部署工具:在 Git 中声明状态,自动同步并检测漂移,支持多集群,并提供可视化的 Web 界面。
Argo Rollouts(同样在应用程序集合中)为 Kubernetes 添加了 蓝绿 和 金丝雀 部署策略,并通过自动指标分析来决定是否提升或回滚部署。
8.8 “12 Factor App” 的 Kubernetes
-
通过环境配置: 使用 ConfigMaps 和 Secrets,绝不要在镜像中硬编码配置。
-
无状态处理: 您的应用程序的每个实例必须是相同的且可替换。
-
端口绑定: 您的应用程序暴露一个端口,Kubernetes 通过 Services 处理路由。
-
日志输出到 stdout: 绝不要写入日志文件。让 Kubernetes / Fluent Bit 收集 stdout。
-
健康检查: 始终实现活跃性和就绪性探针。
-
指标: 暴露一个
/metricsPrometheus 端点。可观测性不是奢侈品,而是标准。
9.词汇表
| 术语 | 定义 |
|---|---|
内部循环 |
快速本地开发周期:编码、构建、部署、测试 |
外部循环 |
自动化提交后周期:CI/CD、测试、部署 |
Tilt |
具有实时更新和网页仪表板的内部循环工具 |
Tiltfile |
Tilt 配置文件,使用 Starlark(类似 Python 的 DSL) |
live_update |
Tilt 特性:在不重建的情况下将文件同步到容器中 |
restart_process |
Tilt 扩展:在实时更新后重启应用程序进程 |
开发容器 |
用于在容器中定义开发环境的开放规范 |
mirrord |
将本地进程连接到远程 K8s 集群上下文 |
Testcontainers |
用于测试中的容器化依赖库 |
Helm |
Kubernetes 的包管理器(charts) |
Kustomize |
通过补丁/覆盖自定义 K8s 清单 |
Trivy |
用于镜像、代码和基础架构即代码的漏洞扫描器 |
Cosign |
容器镜像签名/验证工具 |
Argo CD |
Kubernetes 的 GitOps 持续部署工具 |
Argo Rollouts |
Kubernetes 的金丝雀和蓝绿部署 |
GitOps |
范式:Git = 集群状态的真实来源 |
Keycloak |
开源身份和访问管理(OAuth2 / OpenID Connect) |
Prometheus |
通过HTTP抓取收集指标的监控系统 |
Grafana |
指标可视化平台(仪表板) |
prom-client |
用于暴露Prometheus指标的Node.js库 |
Sidecar |
在一个Pod中运行补充任务的辅助容器 |
ConfigMap |
用于存储配置的 K8s 资源(这里指:仪表板、数据源) |
k3s |
Rancher Desktop使用的轻量级Kubernetes发行版 |
BCI |
来自 SUSE 的基础容器镜像,是应用程序集合镜像的基础。 |
OCI |
开放容器倡议:容器镜像的标准 |
imagePullPolicy |
K8s策略: |
10.更多资料
-
Rancher Desktop: rancherdesktop.io
-
SUSE Application Collection: apps.rancher.io/
-
Demo source code: github.com/fxHouard/Rancher-Developer-Access-Demo
-
Tilt: tilt.dev – 以及博客文章 Tilt + Rancher Desktop
-
mirrord: mirrord.dev
-
Testcontainers: testcontainers.com
-
Dev Containers Spec: containers.dev
-
Argo CD: argoproj.github.io/cd
-
Helm: helm.sh
-
Trivy: aquasecurity.github.io/trivy
-
Cosign: docs.sigstore.dev/cosign
-
Keycloak: keycloak.org
-
Prometheus: prometheus.io
-
Grafana: grafana.com
-
prom-client (Node.js): github.com/siimon/prom-client
祝您在 Kubernetes 开发中愉快!