Quickstart
In this guide, you'll experience how Holos makes the process of operating a Platform safer, easier, and more consistent. We'll use Holos to manage a vendor-provided Helm chart as a Component. Next, we'll mix in our own custom resources to manage the Component with GitOps. Finally, you'll see how Holos makes it safer and easier to maintain software over time by surfacing the exact changes that will be applied when upgrading the vendor's chart to a new version, before they are actually made.
The Concepts page defines capitalized terms such as Platform and Component.
What you'll need
You'll need the following tools installed to complete this guide.
Optionally, if you'd like to apply the rendered manifests to a real Cluster, first complete the Local Cluster Guide.
Install Holos
Install Holos with the following command or other methods listed on the Installation page.
go install github.com/holos-run/holos/cmd/holos@latest
Create a Git Repository
Start by initializing an empty Git repository. Holos operates on local files stored in a Git repository.
- Command
- Output
mkdir holos-quickstart
cd holos-quickstart
git init
Initialized empty Git repository in /holos-quickstart/.git/
This guide assumes you will run commands from the root directory of the Git repository unless stated otherwise.
Generate the Platform
Generate the Platform code in the repository root. A Platform refers to the entire set of software holistically integrated to provide a software development platform for your organization. In this guide, the Platform will include a single Component to demonstrate how the concepts fit together.
holos generate platform quickstart
Commit the generated platform config to the repository.
- Command
- Output
git add .
git commit -m "holos generate platform quickstart - $(holos --version)"
[main (root-commit) 0b17b7f] holos generate platform quickstart
213 files changed, 72349 insertions(+)
...
Generate a Component
The platform you generated is currently empty. Run the following command to generate the CUE code that defines a Helm Component.
- Command
- Output
holos generate component podinfo --component-version 6.6.1
generated component
The --component-version 6.6.1 flag intentionally installs an older release. You'll see how Holos assists with software upgrades later in this guide.
The generate component command creates two files: a leaf file,
components/podinfo/podinfo.gen.cue
, and a root file, podinfo.gen.cue
. Holos
leverages the fact that order is
irrelevant in CUE to
register the component with the Platform by adding a file to the root of the Git
repository. The second file defines the component in the leaf component
directory.
- Leaf
- Root
components/podinfo/podinfo.gen.cue
package holos
// Produce a helm chart build plan.
(#Helm & Chart).Output
let Chart = {
Name: "podinfo"
Version: "6.6.1"
Namespace: "default"
Repo: name: "podinfo"
Repo: url: "https://stefanprodan.github.io/podinfo"
Values: {}
}
podinfo.gen.cue
package holos
// Manage podinfo on workload clusters only
for Cluster in #Fleets.workload.clusters {
#Platform: Components: "\(Cluster.name)/podinfo": {
path: "components/podinfo"
cluster: Cluster.name
}
}
In this example, we provide the minimal information needed to manage the Helm chart: the name, version, Kubernetes namespace for deployment, and the chart repository location.
This chart deploys cleanly without any values provided, but we include an empty Values struct to show how Holos improves consistency and safety in Helm by leveraging the strong type-checking in CUE. You can safely pass shared values, such as the organization’s domain name, to all Components across all clusters in the Platform by defining them at the root of the configuration.
Commit the generated component config to the repository.
- Command
- Output
git add .
git commit -m "holos generate component podinfo - $(holos --version)"
[main cc0e90c] holos generate component podinfo
2 files changed, 24 insertions(+)
create mode 100644 components/podinfo/podinfo.gen.cue
create mode 100644 podinfo.gen.cue
Render the Component
You can render individual components without adding them to a Platform, which is helpful when developing a new component.
- Command
- Output
holos render component ./components/podinfo --cluster-name=default
cached
rendered podinfo
First, the command caches the Helm chart locally to speed up subsequent renderings. Then, the command runs Helm to produce the output and writes it into the deploy directory.
- Command
- Output
tree deploy
deploy
└── clusters
└── default
└── components
└── podinfo
└── podinfo.gen.yaml
5 directories, 1 file
The component deploys to one cluster named default
. In practice, the same
component is often deployed to multiple clusters, such as east
and west
to
provide redundancy and increase availability.
This example is equivalent to running helm template
on the chart and saving
the output to a file. Holos simplifies this task, making it safer and more
consistent when managing many charts.
Mix in an ArgoCD Application
We've seen how Holos works with Helm, but we haven't yet explored how Holos makes it easier to consistently and safely manage all of the software in a Platform.
Holos allows you to easily mix in resources that differentiate your Platform. We'll use this feature to mix in an ArgoCD Application to manage the podinfo Component with GitOps. We'll define this configuration in a way that can be automatically and consistently reused across all future Components added to the Platform.
Create a new file named argocd.cue
in the root of your Git repository with the
following contents:
- argocd.cue
package holos
#ArgoConfig: {
Enabled: true
RepoURL: "https://example.com/holos-quickstart.git"
}
If you plan to apply the rendered output to a real cluster, change the
example.com
RepoURL to the URL of the Git repository you created in this
guide. You don't need to change the example if you're just exploring Holos by
inspecting the rendered output without applying it to a live cluster.
With this file in place, render the component again.
- Command
- Output
holos render component ./components/podinfo --cluster-name=default
wrote deploy file
rendered gitops/podinfo
rendered podinfo
Holos uses the locally cached chart to improve performance and reliability. It then renders the Helm template output along with an ArgoCD Application resource for GitOps.
By defining the ArgoCD configuration at the root, we again take advantage of the fact that order is irrelevant in CUE.
Defining the configuration at the root ensures all future leaf Components take the ArgoCD configuration and render an Application manifest for GitOps management.
- Command
- Output
tree deploy
deploy
└── clusters
└── default
├── components
│ └── podinfo
│ └── podinfo.gen.yaml
└── gitops
└── podinfo.application.gen.yaml
6 directories, 2 files
Notice the new podinfo.application.gen.yaml
file created by enabling ArgoCD in
the Helm component. The Application resource in the file looks like this:
- podinfo.application.gen.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: podinfo
namespace: argocd
spec:
destination:
server: https://kubernetes.default.svc
project: default
source:
path: ./deploy/clusters/default/components/podinfo
repoURL: https://example.com/holos-quickstart.git
targetRevision: main
Holos generates a similar Application resource for every additional Component added to your Platform.
Finally, add and commit the results to your Platform's Git repository.
- Command
- Output
git add .
git commit -m "holos render component ./components/podinfo --cluster-name=default"
[main f95cef1] holos render component ./components/podinfo --cluster-name=default
3 files changed, 134 insertions(+)
create mode 100644 argocd.cue
create mode 100644 deploy/clusters/default/components/podinfo/podinfo.gen.yaml
create mode 100644 deploy/clusters/default/gitops/podinfo.application.gen.yaml
In this section, we learned how Holos simplifies mixing resources into
Components, like an ArgoCD Application. Holos ensures consistency by managing an
Application resource for every Component added to the Platform through the
configuration you define in argocd.cue
at the root of the repository.
Define Workload Clusters
We've generated a Component to manage podinfo and integrated it with our Platform, but rendering the Platform doesn't render podinfo. Podinfo isn't rendered because we haven't assigned any Clusters to the workload Fleet.
Define two new clusters, east
and west
, and assign them to the workload
Fleet. Create a new file named clusters.cue
in the root of your Git repository
with the following contents:
- clusters.cue
package holos
// Define two workload clusters for disaster recovery.
#Fleets: workload: clusters: {
// In CUE _ indicates values are defined elsewhere.
east: _
west: _
}
This example shows how Holos simplifies configuring multiple clusters with similar configuration by grouping them into a Fleet.
Fleets help segment a group of Clusters into one leader and multiple followers by designating one cluster as the primary. Holos makes it safer, easier, and more consistent to reconfigure which cluster is the primary. The primary can be set to automatically restore persistent data from backups, while non-primary clusters can be configured to automatically replicate from the primary.
Automatic database backup, restore, and streaming replication is an advanced topic enabled by Cloud Native PG and CUE. Check back for a guide on this and other Day 2 operations topics.
Render the Platform
Render the Platform to render the podinfo Component for each of the workload clusters.
- Command
- Output
holos render platform ./platform
rendered components/podinfo for cluster west in 99.480792ms
rendered components/podinfo for cluster east in 99.882667ms
The render platform command iterates over every Cluster in the Fleet and renders
each Component assigned to the Fleet. Notice the two additional subdirectories
created under the deploy directory, one for each cluster: east
and west
.
- Command
- Output
tree deploy
deploy
└── clusters
├── default
│ ├── components
│ │ └── podinfo
│ │ └── podinfo.gen.yaml
│ └── gitops
│ └── podinfo.application.gen.yaml
├── east
│ ├── components
│ │ └── podinfo
│ │ └── podinfo.gen.yaml
│ └── gitops
│ └── podinfo.application.gen.yaml
└── west
├── components
│ └── podinfo
│ └── podinfo.gen.yaml
└── gitops
└── podinfo.application.gen.yaml
14 directories, 6 files
Holos ensures consistency and safety by defining the ArgoCD Application once, with strong type checking, at the configuration root.
New Application resources are automatically generated for the east
and west
workload Clusters.
- east
- west
- default
deploy/clusters/east/gitops/podinfo.application.gen.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: podinfo
namespace: argocd
spec:
destination:
server: https://kubernetes.default.svc
project: default
source:
path: ./deploy/clusters/east/components/podinfo
repoURL: https://example.com/holos-quickstart.git
targetRevision: main
deploy/clusters/west/gitops/podinfo.application.gen.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: podinfo
namespace: argocd
spec:
destination:
server: https://kubernetes.default.svc
project: default
source:
path: ./deploy/clusters/west/components/podinfo
repoURL: https://example.com/holos-quickstart.git
targetRevision: main
deploy/clusters/default/gitops/podinfo.application.gen.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: podinfo
namespace: argocd
spec:
destination:
server: https://kubernetes.default.svc
project: default
source:
path: ./deploy/clusters/default/components/podinfo
repoURL: https://example.com/holos-quickstart.git
targetRevision: main
Add and commit the rendered Platform and workload Clusters.
- Command
- Output
git add .
git commit -m "holos render platform ./platform - $(holos --version)"
[main 5aebcf5] holos render platform ./platform - 0.93.2
5 files changed, 263 insertions(+)
create mode 100644 clusters.cue
create mode 100644 deploy/clusters/east/components/podinfo/podinfo.gen.yaml
create mode 100644 deploy/clusters/east/gitops/podinfo.application.gen.yaml
create mode 100644 deploy/clusters/west/components/podinfo/podinfo.gen.yaml
create mode 100644 deploy/clusters/west/gitops/podinfo.application.gen.yaml
Upgrade a Helm Chart
Holos is designed to ease the burden of Day 2 operations. With Holos, upgrading software, integrating new software, and making safe platform-wide configuration changes become easier.
Let's upgrade the podinfo Component to see how this works in practice. First, update the Component version field to the latest upstream Helm chart version.
- Command
- Output
holos generate component podinfo --component-version 6.6.2
generated component
Remove the cached chart version.
- Command
rm -rf components/podinfo/vendor
Now re-render the Platform.
- Command
- Output
holos render platform ./platform
rendered components/podinfo for cluster east in 327.10475ms
rendered components/podinfo for cluster west in 327.796541ms
Notice we're still using the upstream chart without modifying it. The Holos component wraps around the chart to mix in additional resources and integrate the component with the broader Platform.
Visualize the Changes
Holos makes it easier to see exactly what changes are made and which resources
will be applied to the API server. By design, Holos operates on local files,
leaving the task of applying them to ecosystem tools like kubectl
and ArgoCD.
This allows platform operators to inspect changes during code review, or before
committing the change at all.
For example, using git diff
, we see that the only functional change when
upgrading this Helm chart is the deployment of a new container image tag to each
cluster. Additionally, we can roll out this change gradually by applying it to
the east cluster first, then to the west cluster, limiting the potential blast
radius of a problematic change.
- Command
- Output
git diff deploy/clusters/east
diff --git a/deploy/clusters/east/components/podinfo/podinfo.gen.yaml b/deploy/clusters/east/components/podinfo/podinfo.gen.yaml
index 7cc3332..8c1647d 100644
--- a/deploy/clusters/east/components/podinfo/podinfo.gen.yaml
+++ b/deploy/clusters/east/components/podinfo/podinfo.gen.yaml
@@ -5,9 +5,9 @@ kind: Service
metadata:
name: podinfo
labels:
- helm.sh/chart: podinfo-6.6.1
+ helm.sh/chart: podinfo-6.6.2
app.kubernetes.io/name: podinfo
- app.kubernetes.io/version: "6.6.1"
+ app.kubernetes.io/version: "6.6.2"
app.kubernetes.io/managed-by: Helm
spec:
type: ClusterIP
@@ -29,9 +29,9 @@ kind: Deployment
metadata:
name: podinfo
labels:
- helm.sh/chart: podinfo-6.6.1
+ helm.sh/chart: podinfo-6.6.2
app.kubernetes.io/name: podinfo
- app.kubernetes.io/version: "6.6.1"
+ app.kubernetes.io/version: "6.6.2"
app.kubernetes.io/managed-by: Helm
spec:
replicas: 1
@@ -53,7 +53,7 @@ spec:
terminationGracePeriodSeconds: 30
containers:
- name: podinfo
- image: "ghcr.io/stefanprodan/podinfo:6.6.1"
+ image: "ghcr.io/stefanprodan/podinfo:6.6.2"
imagePullPolicy: IfNotPresent
command:
- ./podinfo
Holos is designed to surface the fully rendered manifests intended for the Kubernetes API server, making it easier to see and reason about platform-wide configuration changes.
Recap
In this quickstart guide, we learned how Holos makes it easier, safer, and more consistent to manage a Platform composed of multiple Clusters and upstream Helm charts.
We covered how to:
- Generate a Git repository for the Platform config.
- Wrap the unmodified upstream podinfo Helm chart into a Component.
- Render an individual Component.
- Mix-in your Platform's unique resources to all Components. For example, ArgoCD Application resources.
- Define multiple similar, but not identical, workload clusters.
- Render the manifests for the entire Platform with the
holos render platform
command. - Upgrade a Helm chart to the latest version as an important Day 2 task.
- Visualize and surface the details of planned changes Platform wide.
Dive Deeper
If you'd like to dive deeper, check out the Schema API and Core
API reference docs. The main difference between the schema and core
packages is that the schema is used by users to write refined CUE, while the
core package is what the schema produces for holos
to execute. Users rarely
need to interact with the Core API when on the happy path, but can use the core
package as an escape hatch when the happy path doesn't go where you want.