|
| 1 | +--- |
| 2 | +title: "Chapter 9: CI user guide" |
| 3 | +linkTitle: "Chapter 9: CI user guide" |
| 4 | +description: |
| 5 | + This chapter provides a practical guide for using kpt in CI/CD workflows, |
| 6 | + including rendering (with validators) and gated apply steps. |
| 7 | +toc: true |
| 8 | +menu: |
| 9 | + main: |
| 10 | + parent: "Book" |
| 11 | + weight: 90 |
| 12 | +--- |
| 13 | + |
| 14 | +## Overview: Using kpt in CI/CD |
| 15 | + |
| 16 | +Continuous integration (CI) is the practice of running automated checks on every change so that teams can validate |
| 17 | +configuration early and consistently. In a CI/CD pipeline, kpt fits naturally because it operates on local files and |
| 18 | +produces deterministic output. This makes it easy to run in ephemeral build environments and validate changes before |
| 19 | +they reach a cluster. |
| 20 | + |
| 21 | +In CI, kpt is typically used to render packages (including validators), and in some workflows to apply configuration in a |
| 22 | +gated step. CI does not author or mutate packages; it consumes versioned packages from source control and verifies that they |
| 23 | +render correctly and meet policy requirements. |
| 24 | + |
| 25 | +## CI Responsibilities vs. Developer Responsibilities |
| 26 | + |
| 27 | +kpt follows the configuration-as-data model: Git is the source of truth, and packages represent declared intent. The |
| 28 | +division of responsibilities exists to preserve that intent and to keep automation predictable. Developers author and |
| 29 | +declare intent in the repository, while CI renders (including validators) and optionally applies that intent in controlled |
| 30 | +environments. |
| 31 | + |
| 32 | +At developer time (outside CI), teams create and evolve packages. This includes writing the `Kptfile`, declaring |
| 33 | +pipelines, and version-controlling configuration. Changes are reviewed and merged in Git so the repository remains the |
| 34 | +authoritative record. |
| 35 | + |
| 36 | +At CI time, the pipeline consumes the versioned package, runs the declared pipeline with `kpt fn render`, fails fast when |
| 37 | +validators fail, and optionally applies the rendered configuration using `kpt live apply` when explicit gates are satisfied. |
| 38 | +CI should never mutate the package source as part of the build; doing so breaks the source-of-truth model and makes |
| 39 | +changes harder to audit and reproduce. |
| 40 | + |
| 41 | +| Area | Developer responsibilities | CI responsibilities | |
| 42 | +| ------------------ | ------------------------------------------------------ | ----------------------------------------------------------- | |
| 43 | +| Source of truth | Author configuration in Git and manage version history | Treat Git as authoritative input | |
| 44 | +| Package definition | Create packages and write the `Kptfile` | Consume packages as-is | |
| 45 | +| Pipelines | Declare `pipeline` steps in the `Kptfile` | Execute declared pipelines with `kpt fn render` | |
| 46 | +| Validation | Choose validators and policies | Fail fast on validation results | |
| 47 | +| Apply | Decide when packages are ready to deploy | Optionally apply with `kpt live apply` under explicit gates | |
| 48 | +| Mutations | Make intentional edits in Git | Do not mutate package sources in CI (anti-pattern) | |
| 49 | + |
| 50 | +## Typical kpt CI workflow (conceptual) |
| 51 | + |
| 52 | +This section provides a system-agnostic mental model for how kpt is typically used in CI. The flow is intentionally |
| 53 | +simple and does not assume any specific CI platform or YAML configuration. The same steps should behave the same way |
| 54 | +when run locally or in CI, which helps keep automation deterministic and easy to debug. |
| 55 | + |
| 56 | +At a high level, a CI run follows this sequence: |
| 57 | + |
| 58 | +1. Check out the repository that contains the kpt package. |
| 59 | +2. Install kpt in the build environment. |
| 60 | +3. Run `kpt fn render` to execute the declared pipeline. |
| 61 | +4. Observe results and fail fast if any checks fail. |
| 62 | +5. Optionally apply the rendered resources when explicit deployment gates are satisfied. |
| 63 | + |
| 64 | + |
| 65 | + |
| 66 | +This flow emphasizes determinism and no hidden state: the repository is the source of truth, the rendered output is |
| 67 | +derived entirely from the checked-out files, and the results should be consistent across developer machines and CI |
| 68 | +runners. |
| 69 | + |
| 70 | +## Rendering in CI |
| 71 | + |
| 72 | +Rendering is the most important CI step in a kpt workflow. The `kpt fn render` command executes the package pipeline |
| 73 | +declared in the `Kptfile`, running mutators and validators in a predictable order. The output is the fully hydrated |
| 74 | +configuration that CI can use for downstream steps. |
| 75 | + |
| 76 | +### Prerequisites |
| 77 | + |
| 78 | +Since kpt functions run as containers, your CI environment must have access to a container runtime (for example, |
| 79 | +Podman). |
| 80 | + |
| 81 | +Podman is preferred in CI because it supports rootless operation and does not require a daemon. |
| 82 | + |
| 83 | +- Podman socket: Ensure your CI step can access the Podman socket (for example, `/run/podman/podman.sock` or rootless |
| 84 | + `$XDG_RUNTIME_DIR/podman/podman.sock`). |
| 85 | +- Privileges: The CI runner requires permissions to pull images and run containers. |
| 86 | + |
| 87 | +### Why render (including validation) |
| 88 | + |
| 89 | +Render is the default CI action because it catches configuration errors early. Validators in the pipeline fail the build |
| 90 | +when schemas, policies, or constraints are not met. This makes CI a reliable safety net before any configuration is |
| 91 | +applied to a cluster. |
| 92 | + |
| 93 | +Run `kpt fn render` on every change so CI always tests the exact package state stored in Git. This keeps results |
| 94 | +deterministic and avoids hidden state between runs. |
| 95 | + |
| 96 | +### `kpt fn render` vs `kpt fn eval` |
| 97 | + |
| 98 | +It is important to distinguish `kpt fn render` from `kpt fn eval`: |
| 99 | + |
| 100 | +- `kpt fn render`: Runs the declared pipeline from the `Kptfile`. Use this in CI to ensure the rendered output matches |
| 101 | + the intent declared in Git. |
| 102 | +- `kpt fn eval`: Runs an ad-hoc function. In CI, this is useful for extra validation (like a separate linter step), but |
| 103 | + it does not represent the package's definition. |
| 104 | + |
| 105 | +## Applying configuration in CI (optional and gated) |
| 106 | + |
| 107 | +Applying configuration from CI should be treated as optional and tightly controlled to prevent accidental cluster |
| 108 | +changes. In most pipelines, [kpt live apply](/reference/cli/live/apply/) runs only when a deployment is explicitly |
| 109 | +authorized. |
| 110 | + |
| 111 | +Run apply only under these conditions: |
| 112 | + |
| 113 | +- The pipeline is executing on the main branch. |
| 114 | +- The pipeline is part of a release workflow. |
| 115 | +- A manual approval gate has been satisfied. |
| 116 | + |
| 117 | +Avoid running apply on pull requests. PRs are for review and validation, not for changing live clusters. Applying from |
| 118 | +unmerged changes makes it difficult to audit what was deployed and can introduce drift between Git and the cluster. |
| 119 | + |
| 120 | +Scope cluster credentials to the minimum permissions needed for the target environment. Use separate credentials for |
| 121 | +different environments and avoid sharing production access with non-deployment jobs. |
| 122 | + |
| 123 | +Always render before apply. The `kpt fn render` step produces the exact, validated output that should be deployed, and |
| 124 | +it ensures the apply step reflects the intent stored in Git. |
| 125 | + |
| 126 | +Pruning & inventory (critical safety note): `kpt live apply` tracks an inventory of deployed resources and will |
| 127 | +prune resources that exist in the cluster but are missing from the current package. Unlike `kubectl apply`, it will |
| 128 | +delete resources that are absent from the rendered input. If a CI pipeline accidentally renders an empty directory and |
| 129 | +runs apply, it can delete the application. Guard apply with explicit gates and verify rendered output before |
| 130 | +deployment. |
| 131 | + |
| 132 | +## Handling secrets in CI |
| 133 | + |
| 134 | +Handling secrets is often the most challenging part of automation. The core principle for kpt is simple: secrets are |
| 135 | +runtime inputs, not configuration data. Git is the source of truth for declared configuration, but secrets should never |
| 136 | +be stored in the repository. |
| 137 | + |
| 138 | +### The golden rules |
| 139 | + |
| 140 | +- Never in Git: Secrets must not appear in YAML files, the `Kptfile`, or `functionConfig`. |
| 141 | +- Runtime only: Inject secrets only at render or apply time, and keep them in memory or a temporary filesystem. |
| 142 | + |
| 143 | +### Where to store secrets |
| 144 | + |
| 145 | +Do not rely on the kpt package to store sensitive data. Use one of the following: |
| 146 | + |
| 147 | +- CI native stores (for example, GitHub Secrets or GitLab CI/CD Variables). |
| 148 | +- External vaults (for example, HashiCorp Vault, Google Secret Manager, AWS Secrets Manager, or Azure Key Vault). |
| 149 | + |
| 150 | +### How to inject secrets |
| 151 | + |
| 152 | +Because kpt separates configuration from execution, secrets must be supplied to kpt commands at runtime. |
| 153 | + |
| 154 | +#### Environment variables (for functions) |
| 155 | + |
| 156 | +If a function requires credentials (for example, a validator that calls an external API), pass them as environment |
| 157 | +variables. Fetch the secret in a setup step and export it so the container runtime can pass it to the function. |
| 158 | + |
| 159 | +```shell |
| 160 | +$ export API_TOKEN=$(vault read -field=token secret/my-api) |
| 161 | +``` |
| 162 | + |
| 163 | +```shell |
| 164 | +$ kpt fn render |
| 165 | +``` |
| 166 | + |
| 167 | +#### File mounts (for `kpt live apply`) |
| 168 | + |
| 169 | +Applying to a cluster requires credentials such as a kubeconfig file or service account token. Mount the credential |
| 170 | +file from your secret store into the CI runner's filesystem and point `kpt live apply` to it. |
| 171 | + |
| 172 | +```shell |
| 173 | +$ echo "$KUBECONFIG_CONTENT" > /tmp/kubeconfig |
| 174 | +``` |
| 175 | + |
| 176 | +```shell |
| 177 | +$ KUBECONFIG=/tmp/kubeconfig kpt live apply |
| 178 | +``` |
| 179 | + |
| 180 | +### Integration with external vaults |
| 181 | + |
| 182 | +When using an external vault, the standard pattern is fetch-then-run: |
| 183 | + |
| 184 | +1. Authenticate: The CI job authenticates to the vault (OIDC, AppRole, or similar). |
| 185 | +2. Fetch: The job retrieves only the secrets required for this pipeline. |
| 186 | +3. Inject: Secrets are exported as environment variables or written to a tmpfs volume. |
| 187 | +4. Execute: kpt runs using the injected credentials. |
| 188 | +5. Cleanup: The CI runner is destroyed, wiping the secrets. |
| 189 | + |
| 190 | +## Example: Using kpt in a Cloud Build pipeline |
| 191 | + |
| 192 | +Cloud Build is a concise way to demonstrate the CI pattern, but the same structure applies to any CI system. The |
| 193 | +examples below are intentionally small and focus on the critical steps: install, render (including validators), and an |
| 194 | +optional gated apply. |
| 195 | + |
| 196 | +To keep the example concrete, we use the WordPress package. You can fetch it locally with: |
| 197 | + |
| 198 | +```shell |
| 199 | +$ kpt pkg get https://github.com/kptdev/kpt/package-examples/wordpress@v1.0.0-beta.59 |
| 200 | +``` |
| 201 | + |
| 202 | +### Render-only build |
| 203 | + |
| 204 | +This build renders configuration on every change and runs validators as part of the pipeline. It does not deploy. |
| 205 | + |
| 206 | +```yaml |
| 207 | +steps: |
| 208 | + # Install step: install kpt into the Docker builder |
| 209 | + - name: gcr.io/cloud-builders/docker |
| 210 | + entrypoint: bash |
| 211 | + args: |
| 212 | + - -c |
| 213 | + - | |
| 214 | + curl -L https://github.com/kptdev/kpt/releases/download/${_KPT_VERSION}/kpt_linux_amd64 -o /usr/local/bin/kpt |
| 215 | + chmod +x /usr/local/bin/kpt |
| 216 | +
|
| 217 | + # Render step: execute declared pipeline |
| 218 | + # Validators run as part of the pipeline and fail the build when checks fail. |
| 219 | + - name: gcr.io/cloud-builders/docker |
| 220 | + entrypoint: bash |
| 221 | + args: |
| 222 | + - -c |
| 223 | + - | |
| 224 | + kpt fn render ${_PACKAGE_DIR} |
| 225 | +
|
| 226 | +substitutions: |
| 227 | + _KPT_VERSION: v1.0.0-beta.59 |
| 228 | + _PACKAGE_DIR: wordpress |
| 229 | +``` |
| 230 | +
|
| 231 | +### Deployment build (gated) |
| 232 | +
|
| 233 | +This build is intended for main or release workflows and assumes a manual approval gate. It renders first, then |
| 234 | +applies only after credentials are injected. The example uses the `package-examples/wordpress` package. |
| 235 | + |
| 236 | +```yaml |
| 237 | +steps: |
| 238 | + # Install step |
| 239 | + - name: gcr.io/cloud-builders/docker |
| 240 | + entrypoint: bash |
| 241 | + args: |
| 242 | + - -c |
| 243 | + - | |
| 244 | + curl -L https://github.com/kptdev/kpt/releases/download/${_KPT_VERSION}/kpt_linux_amd64 -o /usr/local/bin/kpt |
| 245 | + chmod +x /usr/local/bin/kpt |
| 246 | +
|
| 247 | + # Render step |
| 248 | + - name: gcr.io/cloud-builders/docker |
| 249 | + entrypoint: bash |
| 250 | + args: |
| 251 | + - -c |
| 252 | + - | |
| 253 | + kpt fn render ${_PACKAGE_DIR} |
| 254 | +
|
| 255 | + # Apply step: gated deployment with secrets |
| 256 | + - name: gcr.io/cloud-builders/docker |
| 257 | + entrypoint: bash |
| 258 | + secretEnv: ["KUBECONFIG_CONTENT"] |
| 259 | + args: |
| 260 | + - -c |
| 261 | + - | |
| 262 | + # Write secret to a file for use |
| 263 | + echo "$$KUBECONFIG_CONTENT" > /workspace/kubeconfig |
| 264 | +
|
| 265 | + # Apply with the kubeconfig |
| 266 | + KUBECONFIG=/workspace/kubeconfig kpt live apply ${_PACKAGE_DIR} |
| 267 | +
|
| 268 | +substitutions: |
| 269 | + _KPT_VERSION: v1.0.0-beta.59 |
| 270 | + _PACKAGE_DIR: wordpress |
| 271 | +
|
| 272 | +# Define where the secret comes from |
| 273 | +availableSecrets: |
| 274 | + secretManager: |
| 275 | + - versionName: projects/$PROJECT_ID/secrets/my-kubeconfig/versions/latest |
| 276 | + env: "KUBECONFIG_CONTENT" |
| 277 | +``` |
| 278 | + |
| 279 | +## Common mistakes and anti-patterns |
| 280 | + |
| 281 | +This section highlights practices that commonly lead to CI failures, drift, or unintended cluster changes. Avoid the |
| 282 | +following: |
| 283 | + |
| 284 | +- Running `kpt pkg init` in CI. Packages and `Kptfile` metadata should be authored by developers, not created during |
| 285 | + CI runs. |
| 286 | +- Mutating packages in CI. CI should validate and render the declared intent, not change the source of truth. |
| 287 | +- Storing secrets in configuration. Secrets must not appear in YAML files, the `Kptfile`, or `functionConfig`. |
| 288 | +- Applying on pull requests. PRs should validate only; deployment belongs in gated, mainline workflows. |
| 289 | +- Committing rendered output back to the source package. Rendered results are derived artifacts and should not be |
| 290 | + committed to the source repository in CI. |
0 commit comments