Windmill 实例配置即代码IaC完整指南sync-config 与 Kubernetes Operator【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmillWindmill 官方在examples/deploy/instance-config-iac/目录中提供了完整的实例配置即代码Infrastructure-as-Code示例允许通过声明式 YAML 文件统一管理实例级配置全局设置 global settings 与 worker group 配置并自动同步到数据库。本文以该目录下的 README.md 为主体结合 docker-compose.yml、windmill-config.yaml、k8s-windmill-instance.yaml 等真实文件以及 backend 源码详细讲解sync-configCLI 与 Kubernetes Operator 两种部署模型的配置方法、敏感字段引用机制、Replace 同步语义与底层实现原理。为什么需要实例配置即代码Windmill 实例的配置项License Key、SMTP、OAuth、保留策略、Worker 标签等默认是通过管理界面或数据库直接维护的这带来两个问题配置变更不可追溯、无法在多个环境开发/预发/生产间复用。实例配置即代码把整份配置收敛为一个版本可控的 YAML 文件随部署流程自动应用实现配置版本化所有变更通过 git 留痕、可 review、可回滚环境可复现同一份 YAML 在不同环境使用不同 secret 即可拉起等效实例声明式收敛数据库状态与文件声明的状态保持完全一致Replace 语义。Windmill 为此提供了两种部署模型它们共用同一份 YAML 模式InstanceConfig与同一套 secret 引用机制方案适用场景前置要求sync-configCLIDocker Compose、虚拟机、CI/CD 流水线数据库访问权限Kubernetes OperatorKubernetes 集群operatorfeature flag、RBAC 权限配置文件结构InstanceConfig模式一份 Windmill 实例配置文件包含两个顶层键对应windmill_common::instance_config::InstanceConfig结构体的两个字段见 backend/windmill-common/src/instance_config.rsglobal_settings: # 实例级全局设置存储在 global_settings 数据表中 base_url: https://windmill.example.com retention_period_secs: 2592000 # ... worker_configs: # Worker group 配置存储在 config 表中、键名为 worker__name default: worker_tags: [deno, python3, bun, go, bash] gpu: dedicated_worker: ws:f/gpu_inference # ...所有字段均为可选只同步你在文件中显式声明的字段。global_settings会被扁平化为name → value键值对写入global_settings表而worker_configs中每个组名对应 config 表中一条worker__name记录例如worker__default、worker__nativeWorker 启动时按WORKER_GROUP环境变量查找对应配置。从源码结构看InstanceConfig还实现了to_settings_map()/from_db()等序列化与数据库读取方法instance_config.rs支持将数据库当前状态反向导出为 YAML便于审计或迁移。敏感字段引用三种格式凡是包含敏感信息的字段License Key、OAuth secret、SMTP 密码等都支持三种写法# 1. 明文直写生产环境不推荐 license_key: my-license-key # 2. 环境变量引用所有环境通用 license_key: envRef: WM_LICENSE_KEY # 3. Kubernetes Secret 引用仅 K8s 场景 license_key: secretKeyRef: name: windmill-secrets # Secret 资源名 key: license-key # Secret 内的键名在源码中这两种引用分别由EnvRef与SecretRef两个结构体承载并通过serde的untagged反序列化自动识别字符串值按明文处理{ envRef: VAR }反序列化为EnvRef{ secretKeyRef: { name, key } }反序列化为SecretRefinstance_config.rs。支持envRef与secretKeyRef的字段清单license_keyhub_api_secretscim_tokensmtp_settings.smtp_passwordoauths.provider.secret每个 OAuth 客户端各自的 secretcustom_instance_pg_databases.user_pwd方案一Docker Compose 中使用sync-configsync-config是 Windmill 二进制提供的一个子命令读取 YAML 配置文件从进程环境解析envRef引用然后将结果同步到数据库。底层工作原理sync-config的完整执行链位于 backend/src/main.rs大致分五步读取并解析 YAML 文件serde_yml反序列化为InstanceConfig调用resolve_env_refs将envRef字段从容器环境变量解析为实际值环境变量缺失会直接报错environment variable VAR not found建立数据库连接initial_connection读取数据库当前状态计算 diff使用Replace模式文件中未声明的设置会被删除ducklake_settings、custom_instance_pg_databases等受保护设置除外将变更写回数据库输出Synced instance config from path。ApplyMode::Replace与受保护设置的过滤逻辑在diff_global_settings/diff_worker_configs中实现instance_config.rs受保护键列表位于 instance_config.rs包括ducklake_user_pg_pwd、ducklake_settings、custom_instance_pg_databases——这些是运行时/凭据相关设置配置同步永远不会将其清空。worker_configs也作为保留键被过滤避免历史遗留的单 blob 记录干扰新式分组存储。搭建步骤仓库的 docker-compose.yml 提供了完整可运行的 Compose 示例关键部分如下。第一步编写配置文件windmill-config.yaml可直接参考仓库中的 windmill-config.yamlglobal_settings: base_url: https://windmill.example.com license_key: envRef: WM_LICENSE_KEY retention_period_secs: 2592000 # 30 天 job_default_timeout: 900 # 15 分钟 expose_metrics: false smtp_settings: smtp_host: smtp.example.com smtp_port: 587 smtp_from: windmillexample.com smtp_tls_implicit: false smtp_password: envRef: SMTP_PASSWORD oauths: google: id: google-client-id secret: envRef: GOOGLE_OAUTH_SECRET login_config: auth_url: https://accounts.google.com/o/oauth2/v2/auth token_url: https://oauth2.googleapis.com/token userinfo_url: https://openidconnect.googleapis.com/v1/userinfo scopes: [openid, profile, email] custom_tags: - gpu - high-mem worker_configs: default: worker_tags: [deno, python3, bun, go, bash, powershell] init_bash: echo Worker starting native: worker_tags: [nativets]注意worker_tags中的值deno、python3、bun、go、bash、powershell、nativets等是 Windmill 内置的脚本运行语言标签Worker 会按标签认领对应语言的任务native组对应 nativets 运行时可与docker-compose.yml中WORKER_GROUPnative的 worker 服务一一对应。第二步在docker-compose.yml中添加一次性 init 容器services: windmill_config_sync: image: ${WM_IMAGE} # 启动时运行一次后退出 restart: no command: [windmill, sync-config, /config/windmill-config.yaml] environment: - DATABASE_URL${DATABASE_URL} - WM_LICENSE_KEY${WM_LICENSE_KEY} - SMTP_PASSWORD${SMTP_PASSWORD} - GOOGLE_OAUTH_SECRET${GOOGLE_OAUTH_SECRET} volumes: - ./windmill-config.yaml:/config/windmill-config.yaml:ro depends_on: db: condition: service_healthy仓库示例中windmill_server通过depends_on: windmill_config_sync: condition: service_completed_successfully保证「先同步配置、后启动服务」的先后顺序数据库服务带健康检查pg_isready -U postgres。生产环境可将该服务替换为 Kubernetes InitContainer 或 CI 流水线中的一步。第三步在.env中设置密钥不要提交到版本控制DATABASE_URLpostgres://postgres:changemedb/windmill WM_IMAGEghcr.io/windmill-labs/windmill-ee:main WM_LICENSE_KEYyour-license-key-here SMTP_PASSWORDyour-smtp-password GOOGLE_OAUTH_SECRETyour-google-oauth-secret启动docker compose up -d。配置变更后重新同步sync-config容器运行一次即退出。编辑 YAML 后重新应用docker compose run --rm windmill_config_sync在 CI/CD 流水线中可以直接以二进制方式运行windmill sync-config ./windmill-config.yamlReplace 语义数据库状态与文件严格一致sync-config采用Replace替换模式数据库中存在但 YAML 中未声明的任何全局设置都会被删除ducklake_settings、custom_instance_pg_databases等受保护设置除外。这保证数据库状态与文件声明完全一致但也意味着如果你只想管理一部分设置就必须把希望保留的所有设置都写进 YAML 文件否则未声明的设置会被清理掉。对应测试用例见 backend/tests/instance_config.rs其中覆盖了受保护设置不被误删、worker_configs禁止以 global setting 形式写入等回归场景。方案二Kubernetes OperatorWindmill Kubernetes operator 持续 watch 一个 ConfigMap并不断调和reconcile数据库以匹配声明状态同时原生支持通过secretKeyRef从 Kubernetes Secret 拉取值。前置条件以operatorfeature flag 构建的 WindmillOSS 构建中该能力不可用windmill_operator::run在非private特性下会直接报错 K8s operator is not available in this build见 backend/windmill-operator/src/operator_oss.rsoperator Pod 具备读取 ConfigMap / Secret 与创建 Event 的 RBAC 权限见下文一个名为windmill-instance的 ConfigMap可通过OPERATOR_CONFIGMAP环境变量自定义名称。搭建步骤第一步为敏感值创建 Kubernetes Secret仓库中的 k8s-secrets.yamlapiVersion: v1 kind: Secret metadata: name: windmill-secrets namespace: windmill type: Opaque stringData: license-key: your-license-key-here smtp-password: your-smtp-password-here google-oauth-secret: your-google-oauth-secret-here生产环境建议用 sealed-secrets、external-secrets 或你偏好的密钥管理方案管理该 Secret。第二步创建 ConfigMap仓库中的 k8s-windmill-instance.yamlapiVersion: v1 kind: ConfigMap metadata: name: windmill-instance namespace: windmill data: spec: | global_settings: base_url: https://windmill.example.com license_key: secretKeyRef: name: windmill-secrets key: license-key retention_period_secs: 2592000 job_default_timeout: 900 expose_metrics: true smtp_settings: smtp_host: smtp.example.com smtp_port: 587 smtp_from: windmillexample.com smtp_tls_implicit: false smtp_password: secretKeyRef: name: windmill-secrets key: smtp-password oauths: google: id: your-google-client-id secret: secretKeyRef: name: windmill-secrets key: google-oauth-secret login_config: auth_url: https://accounts.google.com/o/oauth2/v2/auth token_url: https://oauth2.googleapis.com/token userinfo_url: https://openidconnect.googleapis.com/v1/userinfo scopes: [openid, profile, email] custom_tags: - gpu - high-mem worker_configs: default: worker_tags: [deno, python3, bun, go, bash, powershell] init_bash: echo Worker starting native: worker_tags: [nativets]配置放在data.spec下、作为一段 YAML 字符串与sync-config使用完全相同的模式。第三步应用kubectl apply -f windmill-instance.yaml第四步通过 Event 查看同步状态kubectl get events --field-selector involvedObject.namewindmill-instanceLicense Key 的幂等处理如果 ConfigMap 中license_key缺失或为空、但数据库中已存在operator 会保留数据库中的现有值。这意味着你可以把 License Key 交给 UI 单独管理而不用担心 operator 的周期性调和把它覆盖掉——调和逻辑天然对 license_key 做了保护。Operator 环境变量变量默认值说明OPERATOR_NAMESPACEPod 自身所在 namespaceConfigMap 所在的 namespaceOPERATOR_CONFIGMAPwindmill-instance要 watch 的 ConfigMap 名称在 Kubernetes 中使用envRefenvRef在 operator 场景下同样可用值从operator Pod 的环境变量解析。当 secret 通过 Pod 环境变量注入例如来自 vault sidecar 或 external-secrets 的 env 注入时非常有用data: spec: | global_settings: license_key: envRef: WM_LICENSE_KEY # 从 operator Pod 环境读取operator Pod 的 Deployment 中对应env: - name: WM_LICENSE_KEY valueFrom: secretKeyRef: name: windmill-secrets key: license-key这与在 ConfigMap 中直接使用secretKeyRef功能等价区别在于它不要求 operator 具备 Secret 读取权限且可以接入集群支持的任意 secret 注入机制external-secrets、vault-agent 等。RBAC 最小权限operator Pod 需要读取 ConfigMap、Secret 并创建 Event。最小 Role 如下注意因为没有 CRD 需要管理所以是namespace 级 Role而非 ClusterRoleapiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: windmill-operator namespace: windmill rules: - apiGroups: [] resources: [configmaps] verbs: [get, list, watch] - apiGroups: [] resources: [secrets] verbs: [get, list, watch] - apiGroups: [] resources: [events] verbs: [create, patch]运行 operator# 作为独立进程运行适合开发调试 DATABASE_URLpostgres://... windmill operator # 生产环境以 Kubernetes Deployment 形式部署从源码看windmill operator子命令由operatorfeature 编译backend/src/main.rs建立独立的 operator 数据库连接后调用windmill_operator::run(db)启动 ConfigMap watcher在 enterprise private 构建下还会注册 Ctrl-C 信号以优雅退出。如何选择envRef还是secretKeyRef特性envRefsecretKeyRefDocker Compose 可用是否Kubernetes 可用是是vault sidecar 注入是否改用envRef数据来源进程环境变量K8s Secrets API是否需要 Secret 读取 RBAC否是建议追求跨部署目标的可移植性时优先使用envRef希望不经过中间环境变量、直接绑定 Kubernetes 原生 Secret 时使用secretKeyRef。总结与最佳实践两种方案共用InstanceConfigYAML 模式与 secret 引用机制sync-config适合 Docker Compose / VM / CI 的一次性同步operator 适合 K8s 的持续调和敏感字段一律使用envRef或secretKeyRef明文仅用于本地调试理解 Replace 语义文件即真相source of truth未声明的设置会被删除受保护设置ducklake_settings、custom_instance_pg_databases等除外License Key 可脱离配置文件单独管理operator 不会覆盖数据库已有值所有配置变更走版本控制与代码 review配合 CI 中的windmill sync-config即可实现从「手工改库」到「声明式交付」的完整升级。【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考