2021-06-15 15:01:18 +02:00
---
slug: /learn/107-kubernetes
---
# Dagger 107: deploy to Kubernetes
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
This tutorial illustrates how to use Dagger to build, push and deploy Docker images to Kubernetes.
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
2021-06-04 05:34:18 +02:00
2021-06-04 03:18:32 +02:00
## Prerequisites
2021-06-04 05:34:18 +02:00
For this tutorial, you will need a Kubernetes cluster.
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
2021-06-14 18:29:10 +02:00
groupId="provider"
values={[
2021-07-03 00:25:56 +02:00
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
2021-06-14 18:29:10 +02:00
]}>
2021-06-04 05:34:18 +02:00
2021-06-16 18:57:19 +02:00
< TabItem value = "kind" >
2021-06-04 03:18:32 +02:00
2021-06-04 05:34:18 +02:00
[Kind ](https://kind.sigs.k8s.io/docs/user/quick-start ) is a tool for running local Kubernetes clusters using Docker.
2021-06-04 03:18:32 +02:00
1\. Install kind
2021-07-03 00:25:56 +02:00
Follow [these instructions ](https://kind.sigs.k8s.io/docs/user/quick-start ) to install Kind.
2021-06-04 03:18:32 +02:00
Alternatively, on macOS using [homebrew ](https://brew.sh/ ):
```shell
brew install kind
```
2\. Start a local registry
```shell
docker run -d -p 5000:5000 --name registry registry:2
```
3\. Create a cluster with the local registry enabled in containerd
2021-06-16 15:48:49 +02:00
```shell
2021-06-04 03:18:32 +02:00
cat < < EOF | kind create cluster --config = -
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"]
endpoint = ["http://registry:5000"]
EOF
```
4\. Connect the registry to the cluster network
```shell
docker network connect kind registry
```
2021-06-04 05:34:18 +02:00
< / TabItem >
< TabItem value = "gke" >
2021-07-03 00:25:56 +02:00
This tutorial can be run against a [GCP GKE ](https://cloud.google.com/kubernetes-engine ) cluster
and [GCR ](https://cloud.google.com/container-registry ). You can follow
this [GCP documentation ](https://cloud.google.com/kubernetes-engine/docs/quickstart ) to create a GKE cluster. You will
also need to create
a [kubeconfig ](https://cloud.google.com/kubernetes-engine/docs/quickstart#get_authentication_credentials_for_the_cluster )
.
2021-06-04 05:34:18 +02:00
< / TabItem >
< TabItem value = "eks" >
2021-07-03 00:25:56 +02:00
This tutorial can be run against a [AWS EKS ](https://aws.amazon.com/eks/ ) cluster and [ECR ](https://aws.amazon.com/ecr/ )
. You can follow this [AWS documentation ](https://docs.aws.amazon.com/eks/latest/userguide/getting-started-console.html )
to create an EKS cluster. You will also need to create
a [kubeconfig ](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html ).
2021-06-04 05:34:18 +02:00
< / TabItem >
< / Tabs >
2021-06-04 03:18:32 +02:00
## Initialize a Dagger Workspace and Environment
2021-06-16 18:57:19 +02:00
### (optional) Setup example app
2021-07-03 00:25:56 +02:00
You will need the local copy of the [Dagger examples repository ](https://github.com/dagger/examples ) used in previous
guides
2021-06-16 18:57:19 +02:00
2021-06-04 03:18:32 +02:00
```shell
2021-06-16 18:57:19 +02:00
git clone https://github.com/dagger/examples
```
Make sure that all commands are run from the todoapp directory:
```shell
cd examples/todoapp
```
### (optional) Initialize a Cue module
2021-07-03 00:25:56 +02:00
This guide will use the same directory as the root of the Dagger workspace and the root of the Cue module, but you can
create your Cue module anywhere inside the Dagger workspace.
2021-06-16 18:57:19 +02:00
```shell
cue mod init
```
### Organize your package
Let's create a new directory for our Cue package:
```shell
2021-07-07 12:18:56 +02:00
mkdir kube
2021-07-01 11:26:52 +02:00
```
### Deploy using Kubectl
Kubernetes objects are located inside the `k8s` folder:
```shell
tree k8s
# k8s
# ├── deployment.yaml
# └── service.yaml
# 0 directories, 2 files
```
As a starting point, let's deploy them manually with `kubectl` :
```shell
kubectl apply -f k8s/
# deployment.apps/todoapp created
# service/todoapp-service created
```
Verify that the deployment worked:
```shell
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# todoapp 1/1 1 1 10m
kubectl get service
# NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
# todoapp-service NodePort 10.96.225.114 <none> 80:32658/TCP 11m
```
2021-07-05 15:24:48 +02:00
The next step is to transpose it in Cue. Before continuing, clean everything:
2021-07-01 11:26:52 +02:00
```shell
2021-07-03 00:25:56 +02:00
kubectl delete -f k8s/
2021-07-01 11:26:52 +02:00
# deployment.apps "todoapp" deleted
# service "todoapp-service" deleted
2021-06-04 03:18:32 +02:00
```
## Create a basic plan
2021-07-03 00:25:56 +02:00
Create a file named `todoapp.cue` and add the following configuration to it.
2021-06-04 03:18:32 +02:00
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/todoapp.cue"
2021-07-01 11:26:52 +02:00
package main
import (
"alpha.dagger.io/dagger"
"alpha.dagger.io/kubernetes"
)
2021-07-03 00:25:56 +02:00
// input: kubernetes objects directory to deploy to
2021-07-01 11:26:52 +02:00
// set with `dagger input dir manifest ./k8s -e kube`
manifest: dagger.#Artifact & dagger.#Input
2021-07-03 00:25:56 +02:00
// Deploy the manifest to a kubernetes cluster
2021-07-01 11:26:52 +02:00
todoApp: kubernetes.#Resources & {
"kubeconfig": kubeconfig
source: manifest
}
```
2021-07-03 00:25:56 +02:00
This defines a `todoApp` variable containing the Kubernetes objects used to create a todoapp deployment. It also
references a `kubeconfig` value defined above:
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
2021-06-14 18:29:10 +02:00
groupId="provider"
values={[
2021-07-03 00:25:56 +02:00
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
2021-06-14 18:29:10 +02:00
]}>
2021-06-04 05:34:18 +02:00
< TabItem value = "kind" >
2021-07-01 11:26:52 +02:00
The above `config.cue` defines:
2021-06-04 03:18:32 +02:00
2021-07-05 15:24:48 +02:00
- `kubeconfig` a generic value created to embed this string `kubeconfig` value
2021-06-04 03:18:32 +02:00
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/config.cue"
2021-07-01 11:26:52 +02:00
package main
2021-06-04 03:18:32 +02:00
2021-07-01 11:26:52 +02:00
import (
"alpha.dagger.io/dagger"
)
2021-06-04 05:34:18 +02:00
2021-07-03 00:25:56 +02:00
// set with `dagger input text kubeconfig -f "$HOME"/.kube/config -e kube`
2021-07-01 11:26:52 +02:00
kubeconfig: string & dagger.#Input
2021-06-04 05:34:18 +02:00
```
< / TabItem >
< TabItem value = "gke" >
2021-07-01 11:26:52 +02:00
The below `config.cue` defines:
- `kubeconfig` a generic value created to embbed this `gke.#KubeConfig` value
- `gcpConfig` : connection to Google using `alpha.dagger.io/gcp`
2021-07-03 00:25:56 +02:00
- `gkeConfig` : transform a `gcpConfig` to a readable format for `kubernetes.#Resources.kubeconfig`
using `alpha.dagger.io/gcp/gke`
2021-07-01 11:26:52 +02:00
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/config.cue"
2021-07-01 11:26:52 +02:00
package main
import (
"alpha.dagger.io/gcp"
"alpha.dagger.io/gcp/gke"
)
// Value created for generic reference of `kubeconfig` in `todoapp.cue`
2021-06-04 05:34:18 +02:00
kubeconfig: gkeConfig.kubeconfig
2021-07-01 11:26:52 +02:00
// gcpConfig used for Google connection
gcpConfig: gcp.#Config
2021-06-04 05:34:18 +02:00
2021-07-01 11:26:52 +02:00
// gkeConfig used for deployment
gkeConfig: gke.#KubeConfig & {
// config field references `gkeConfig` value to set in once
config: gcpConfig
2021-06-04 05:34:18 +02:00
}
```
< / TabItem >
< TabItem value = "eks" >
2021-07-01 11:26:52 +02:00
The below `config.cue` defines:
2021-06-04 05:34:18 +02:00
2021-07-01 11:26:52 +02:00
- `kubeconfig` , a generic value created to embbed this `eksConfig.kubeconfig` value
- `awsConfig` , connection to Amazon using `alpha.dagger.io/aws`
2021-07-03 00:25:56 +02:00
- `eksConfig` , transform a `awsConfig` to a readable format for `kubernetes.#Resources.kubeconfig`
using `alpha.dagger.io/aws/eks`
2021-06-04 05:34:18 +02:00
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/config.cue"
2021-07-01 11:26:52 +02:00
package main
import (
"alpha.dagger.io/aws"
"alpha.dagger.io/aws/eks"
)
2021-06-04 05:34:18 +02:00
2021-07-01 11:26:52 +02:00
// Value created for generic reference of `kubeconfig` in `todoapp.cue`
2021-06-04 05:34:18 +02:00
kubeconfig: eksConfig.kubeconfig
2021-07-01 11:26:52 +02:00
// awsConfig for Amazon connection
awsConfig: aws.#Config
// eksConfig used for deployment
eksConfig: eks.#KubeConfig & {
// config field references `gkeConfig` value to set in once
config: awsConfig
2021-06-04 03:18:32 +02:00
}
```
2021-06-04 05:34:18 +02:00
< / TabItem >
2021-06-04 03:18:32 +02:00
2021-07-01 11:26:52 +02:00
< / Tabs >
2021-06-04 03:18:32 +02:00
2021-06-16 18:57:19 +02:00
### Setup the environment
#### Create a new environment
Now that your Cue package is ready, let's create an environment to run it:
```shell
2021-07-07 12:18:56 +02:00
dagger new 'kube' -m kube
2021-06-16 18:57:19 +02:00
```
2021-06-04 03:18:32 +02:00
### Configure the environment
2021-07-03 00:25:56 +02:00
Before we can bring up the deployment, we need to provide the `kubeconfig` input declared in the configuration.
Otherwise, Dagger will complain about a missing input:
2021-06-04 03:18:32 +02:00
```shell
2021-07-01 11:26:52 +02:00
dagger up -e kube
# 5:05PM ERR system | required input is missing input=kubeconfig
# 5:05PM ERR system | required input is missing input=manifest
# 5:05PM FTL system | some required inputs are not set, please re-run with `--force` if you think it's a mistake missing=0s
2021-06-04 03:18:32 +02:00
```
You can inspect the list of inputs (both required and optional) using `dagger input list` :
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
2021-06-14 18:29:10 +02:00
groupId="provider"
values={[
2021-07-03 00:25:56 +02:00
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
2021-06-14 18:29:10 +02:00
]}>
2021-06-04 05:34:18 +02:00
< TabItem value = "kind" >
2021-06-04 03:18:32 +02:00
```shell
2021-07-01 11:26:52 +02:00
dagger input list -e kube
# Input Value Set by user Description
2021-07-03 00:25:56 +02:00
# kubeconfig string false set with `dagger input text kubeconfig -f "$HOME"/.kube/config -e kube`
2021-07-01 11:26:52 +02:00
# manifest dagger.#Artifact false input: source code repository, must contain a Dockerfile set with `dagger input dir manifest ./k8s -e kube`
# todoApp.namespace *"default" | string false Kubernetes Namespace to deploy to
# todoApp.version *"v1.19.9" | string false Version of kubectl client
2021-06-04 03:18:32 +02:00
```
2021-06-04 05:34:18 +02:00
< / TabItem >
< TabItem value = "gke" >
```shell
2021-07-01 11:26:52 +02:00
dagger input list -e kube
# Input Value Set by user Description
# gcpConfig.region string false GCP region
# gcpConfig.project string false GCP project
# gcpConfig.serviceKey dagger.#Secret false GCP service key
# manifest dagger.#Artifact false input: source code repository, must contain a Dockerfile set with `dagger input dir manifest ./k8s -e kube`
# gkeConfig.clusterName string false GKE cluster name
# gkeConfig.version *"v1.19.9" | string false Kubectl version
# todoApp.namespace *"default" | string false Kubernetes Namespace to deploy to
# todoApp.version *"v1.19.9" | string false Version of kubectl client
2021-06-04 05:34:18 +02:00
```
< / TabItem >
< TabItem value = "eks" >
```shell
2021-07-01 11:26:52 +02:00
dagger input list -e kube
# Input Value Set by user Description
# awsConfig.region string false AWS region
# awsConfig.accessKey dagger.#Secret false AWS access key
# awsConfig.secretKey dagger.#Secret false AWS secret key
# manifest dagger.#Artifact false input: source code repository, must contain a Dockerfile set with `dagger input dir manifest ./k8s -e kube`
# eksConfig.clusterName string false EKS cluster name
# eksConfig.version *"v1.19.9" | string false Kubectl version
# todoApp.namespace *"default" | string false Kubernetes Namespace to deploy to
# todoApp.version *"v1.19.9" | string false Version of kubectl client
2021-06-04 05:34:18 +02:00
```
< / TabItem >
< / Tabs >
Let's provide the missing inputs:
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
2021-06-14 18:29:10 +02:00
groupId="provider"
values={[
2021-07-03 00:25:56 +02:00
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
2021-06-14 18:29:10 +02:00
]}>
2021-06-04 05:34:18 +02:00
< TabItem value = "kind" >
2021-06-04 03:18:32 +02:00
```shell
2021-07-03 00:25:56 +02:00
# we'll use the "$HOME"/.kube/config created by `kind`
dagger input text kubeconfig -f "$HOME"/.kube/config -e kube
2021-07-01 11:26:52 +02:00
2021-07-05 15:24:48 +02:00
# Add as an artifact the k8s folder
2021-07-01 11:26:52 +02:00
dagger input dir manifest ./k8s -e kube
2021-06-04 03:18:32 +02:00
```
2021-06-04 05:34:18 +02:00
< / TabItem >
< TabItem value = "gke" >
```shell
2021-07-05 15:24:48 +02:00
# Add as an artifact the k8s folder
2021-07-01 11:26:52 +02:00
dagger input dir manifest ./k8s -e kube
# Add Google credentials
dagger input text gcpConfig.project < PROJECT > -e kube
dagger input text gcpConfig.region < REGION > -e kube
dagger input secret gcpConfig.serviceKey -f < PATH TO THE SERVICEKEY . json > -e kube
# Add GKE clusterName
dagger input text gkeConfig.clusterName < GKE CLUSTER NAME > -e kube
2021-06-04 05:34:18 +02:00
```
< / TabItem >
< TabItem value = "eks" >
```shell
2021-07-05 15:24:48 +02:00
# Add as an artifact the k8s folder
2021-07-01 11:26:52 +02:00
dagger input dir manifest ./k8s -e kube
# Add Amazon credentials
dagger input text awsConfig.region < REGION > -e kube
dagger input secret awsConfig.accessKey < ACCESS KEY > -e kube
dagger input secret awsConfig.secretKey < SECRET KEY > -e kube
# Add EKS clustername
dagger input text eksConfig.clusterName < EKS CLUSTER NAME > -e kube
2021-06-04 05:34:18 +02:00
```
< / TabItem >
< / Tabs >
2021-06-04 03:18:32 +02:00
### Deploying
2021-07-01 11:26:52 +02:00
Now is time to deploy to Kubernetes.
2021-06-04 03:18:32 +02:00
```shell
2021-07-01 11:26:52 +02:00
dagger up -e kube
# deploy | computing
2021-07-03 00:25:56 +02:00
# deploy | #26 0.700 deployment.apps/todoapp created
# deploy | #27 0.705 service/todoapp-service created
# deploy | completed duration=1.405s
2021-06-04 03:18:32 +02:00
```
2021-07-01 11:26:52 +02:00
Let's verify if the deployment worked:
2021-06-04 03:18:32 +02:00
```shell
2021-07-01 11:26:52 +02:00
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
2021-07-03 00:25:56 +02:00
# todoapp 1/1 1 1 1m
2021-06-04 03:18:32 +02:00
```
2021-07-03 00:25:56 +02:00
Before continuing, cleanup deployment:
2021-06-16 18:57:19 +02:00
```shell
2021-07-03 00:25:56 +02:00
kubectl delete -f k8s/
# deployment.apps "todoapp" deleted
# service "todoapp-service" deleted
2021-06-04 03:18:32 +02:00
```
2021-07-01 11:26:52 +02:00
## Building, pushing, and deploying Docker images
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
Rather than deploying an existing (`todoapp`) image, we're going to build a Docker image from the source, push it to a
registry, and update the Kubernetes configuration.
2021-06-04 03:18:32 +02:00
### Update the plan
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
2021-07-01 11:26:52 +02:00
groupId="provider"
values={[
2021-07-03 00:25:56 +02:00
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
2021-07-01 11:26:52 +02:00
]}>
< TabItem value = "kind" >
2021-07-05 15:24:48 +02:00
Let's see how to deploy an image locally and push it to the local cluster
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
`kube/todoapp.cue` faces these changes:
2021-06-04 03:18:32 +02:00
2021-07-05 15:24:48 +02:00
- `repository` , source code of the app to build. It needs to have a Dockerfile
2021-07-01 11:26:52 +02:00
- `registry` , URI of the registry to push to
- `image` , build of the image
2021-07-05 15:24:48 +02:00
- `remoteImage` , push an image to the registry
2021-07-01 11:26:52 +02:00
- `kustomization` , apply kustomization to image
```cue title="todoapp/kube/todoapp.cue"
package main
2021-06-04 03:18:32 +02:00
import (
"encoding/yaml"
2021-06-23 16:31:42 +02:00
"alpha.dagger.io/dagger"
"alpha.dagger.io/docker"
2021-07-01 11:26:52 +02:00
"alpha.dagger.io/kubernetes"
"alpha.dagger.io/kubernetes/kustomize"
2021-06-04 03:18:32 +02:00
)
// input: source code repository, must contain a Dockerfile
2021-07-01 11:26:52 +02:00
// set with `dagger input dir repository . -e kube`
repository: dagger.#Artifact & dagger.#Input
2021-07-03 00:25:56 +02:00
// Registry to push images to
2021-07-01 11:26:52 +02:00
registry: string & dagger.#Input
2021-07-07 11:10:54 +02:00
tag: "test-kind"
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
// input: kubernetes objects directory to deploy to
// set with `dagger input dir manifest ./k8s -e kube`
2021-07-01 11:26:52 +02:00
manifest: dagger.#Artifact & dagger.#Input
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
// Todoapp deployment pipeline
2021-07-01 11:26:52 +02:00
todoApp: {
2021-07-03 00:25:56 +02:00
// Build the image from repositoru artifact
2021-07-01 11:26:52 +02:00
image: docker.#Build & {
2021-06-04 03:18:32 +02:00
source: repository
2021-07-01 11:26:52 +02:00
}
2021-07-03 00:25:56 +02:00
// Push image to registry
2021-07-01 11:26:52 +02:00
remoteImage: docker.#Push & {
target: "\(registry):\(tag)"
source: image
}
2021-07-03 00:25:56 +02:00
// Update the image from manifest to use the deployed one
2021-07-01 11:26:52 +02:00
kustomization: kustomize.#Kustomize & {
source: manifest
2021-07-03 00:25:56 +02:00
// Convert CUE to YAML.
2021-07-01 11:26:52 +02:00
kustomization: yaml.Marshal({
resources: ["deployment.yaml", "service.yaml"]
images: [{
2021-07-03 00:25:56 +02:00
name: "public.ecr.aws/j7f8d3t2/todoapp"
2021-07-01 11:26:52 +02:00
newName: remoteImage.ref
}]
})
}
2021-07-03 00:25:56 +02:00
// Deploy the customized manifest to a kubernetes cluster
2021-07-01 11:26:52 +02:00
kubeSrc: kubernetes.#Resources & {
"kubeconfig": kubeconfig
source: kustomization
}
}
```
< / TabItem >
< TabItem value = "gke" >
2021-07-03 00:25:56 +02:00
Let's see how to leverage [GCR ](https://github.com/dagger/dagger/tree/main/stdlib/gcp/gcr )
and [GKE ](https://github.com/dagger/dagger/tree/main/stdlib/gcp/gke ) packages.
2021-07-01 11:26:52 +02:00
2021-07-05 15:24:48 +02:00
The two files have to be edited to do so.
2021-07-01 11:26:52 +02:00
2021-07-05 15:24:48 +02:00
`kube/config.cue` configuration has following change:
2021-07-01 11:26:52 +02:00
2021-07-03 00:25:56 +02:00
- definition of a new `ecrCreds` value that contains ecr credentials for remote image push to GCR
2021-07-01 11:26:52 +02:00
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/config.cue"
2021-07-01 11:26:52 +02:00
package main
import (
"alpha.dagger.io/gcp"
"alpha.dagger.io/gcp/gcr"
"alpha.dagger.io/gcp/gke"
)
2021-07-03 00:25:56 +02:00
// Value created for generic reference of `kubeconfig` in `todoapp.cue`
kubeconfig: gkeConfig.kubeconfig
2021-07-01 11:26:52 +02:00
// gcpConfig used for Google connection
gcpConfig: gcp.#Config
// gkeConfig used for deployment
gkeConfig: gke.#KubeConfig & {
// config field references `gkeConfig` value to set in once
config: gcpConfig
}
// gcrCreds used for remote image push
gcrCreds: gcr.#Credentials & {
// config field references `gcpConfig` value to set in once
config: gcpConfig
2021-06-04 03:18:32 +02:00
}
2021-07-01 11:26:52 +02:00
```
2021-07-03 00:25:56 +02:00
`kube/todoapp.cue` , on the other hand, faces these changes:
2021-07-01 11:26:52 +02:00
2021-07-05 15:24:48 +02:00
- `repository` , source code of the app to build. It needs to have a Dockerfile
2021-07-01 11:26:52 +02:00
- `registry` , URI of the registry to push to
- `image` , build of the image
2021-07-05 15:24:48 +02:00
- `remoteImage` , push an image to the registry
2021-07-01 11:26:52 +02:00
- `kustomization` , apply kustomization to image
```cue title="todoapp/kube/todoapp.cue"
package main
import (
"encoding/yaml"
"alpha.dagger.io/dagger"
"alpha.dagger.io/docker"
"alpha.dagger.io/kubernetes"
"alpha.dagger.io/kubernetes/kustomize"
)
// input: source code repository, must contain a Dockerfile
// set with `dagger input dir repository . -e kube`
repository: dagger.#Artifact & dagger.#Input
2021-07-03 00:25:56 +02:00
// GCR registry to push images to
2021-07-01 11:26:52 +02:00
registry: string & dagger.#Input
2021-07-07 11:10:54 +02:00
tag: "test-gcr"
2021-07-01 11:26:52 +02:00
// source of Kube config file.
// set with `dagger input dir manifest ./k8s -e kube`
manifest: dagger.#Artifact & dagger.#Input
// Declarative name
todoApp: {
2021-07-03 00:25:56 +02:00
// Build an image from the project repository
2021-07-01 11:26:52 +02:00
image: docker.#Build & {
source: repository
}
2021-07-03 00:25:56 +02:00
// Push the image to a remote registry
2021-07-01 11:26:52 +02:00
remoteImage: docker.#Push & {
target: "\(registry):\(tag)"
source: image
auth: {
username: gcrCreds.username
secret: gcrCreds.secret
}
}
2021-07-03 00:25:56 +02:00
// Update the image of the deployment to the deployed image
2021-07-01 11:26:52 +02:00
kustomization: kustomize.#Kustomize & {
source: manifest
2021-07-03 00:25:56 +02:00
// Convert CUE to YAML.
2021-07-01 11:26:52 +02:00
kustomization: yaml.Marshal({
resources: ["deployment.yaml", "service.yaml"]
images: [{
2021-07-03 00:25:56 +02:00
name: "public.ecr.aws/j7f8d3t2/todoapp"
2021-07-01 11:26:52 +02:00
newName: remoteImage.ref
}]
})
}
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
// Value created for generic reference of `kubeconfig` in `todoapp.cue`
2021-07-01 11:26:52 +02:00
kubeSrc: kubernetes.#Resources & {
2021-07-03 00:25:56 +02:00
kubeconfig: kubeconfig
2021-07-01 11:26:52 +02:00
source: kustomization
}
2021-06-04 03:18:32 +02:00
}
2021-07-01 11:26:52 +02:00
```
< / TabItem >
< TabItem value = "eks" >
2021-07-03 00:25:56 +02:00
Let's see how to leverage [ECR ](https://github.com/dagger/dagger/tree/main/stdlib/aws/ecr )
and [EKS ](https://github.com/dagger/dagger/tree/main/stdlib/aws/eks ) packages.
2021-07-01 11:26:52 +02:00
2021-07-05 15:24:48 +02:00
The two files have to be edited to do so.
2021-06-04 03:18:32 +02:00
2021-07-05 15:24:48 +02:00
`kube/config.cue` configuration has following change:
2021-07-03 00:25:56 +02:00
2021-07-01 11:26:52 +02:00
- definition of a new `ecrCreds` value that contains ecr credentials for remote image push to ECR
```cue title="todoapp/kube/config.cue"
package main
import (
"alpha.dagger.io/aws"
"alpha.dagger.io/aws/eks"
"alpha.dagger.io/aws/ecr"
)
2021-07-03 00:25:56 +02:00
// Value created for generic reference of `kubeconfig` in `todoapp.cue`
kubeconfig: eksConfig.kubeconfig
2021-07-01 11:26:52 +02:00
// awsConfig for Amazon connection
awsConfig: aws.#Config
// eksConfig used for deployment
eksConfig: eks.#KubeConfig & {
// config field references `awsConfig` value to set in once
config: awsConfig
2021-06-04 03:18:32 +02:00
}
2021-07-01 11:26:52 +02:00
// ecrCreds used for remote image push
ecrCreds: ecr.#Credentials & {
// config field references `awsConfig` value to set in once
config: awsConfig
}
2021-06-04 03:18:32 +02:00
```
2021-07-03 00:25:56 +02:00
`kube/todoapp.cue` , on the other hand, faces these changes:
2021-06-16 18:57:19 +02:00
2021-07-05 15:24:48 +02:00
- `repository` , source code of the app to build. It needs to have a Dockerfile
2021-07-01 11:26:52 +02:00
- `registry` , URI of the registry to push to
- `image` , build of the image
2021-07-05 15:24:48 +02:00
- `remoteImage` , push an image to the registry
2021-07-01 11:26:52 +02:00
- `kustomization` , apply kustomization to image
```cue title="todoapp/kube/todoapp.cue"
package main
import (
"encoding/yaml"
"alpha.dagger.io/dagger"
"alpha.dagger.io/docker"
"alpha.dagger.io/kubernetes"
"alpha.dagger.io/kubernetes/kustomize"
)
// input: source code repository, must contain a Dockerfile
// set with `dagger input dir repository . -e kube`
repository: dagger.#Artifact & dagger.#Input
// ECR registry to push images to
registry: string & dagger.#Input
2021-07-07 11:10:54 +02:00
tag: "test-ecr"
2021-07-01 11:26:52 +02:00
// source of Kube config file.
// set with `dagger input dir manifest ./k8s -e kube`
manifest: dagger.#Artifact & dagger.#Input
todoApp: {
2021-07-03 00:25:56 +02:00
// Build an image from the project repository
2021-07-01 11:26:52 +02:00
image: docker.#Build & {
source: repository
}
2021-07-03 00:25:56 +02:00
// Push the image to a remote registry
2021-07-01 11:26:52 +02:00
remoteImage: docker.#Push & {
target: "\(registry):\(tag)"
source: image
auth: {
username: ecrCreds.username
secret: ecrCreds.secret
}
}
2021-07-03 00:25:56 +02:00
// Update the image of the deployment to the deployed image
2021-07-01 11:26:52 +02:00
kustomization: kustomize.#Kustomize & {
source: manifest
2021-07-03 00:25:56 +02:00
// Convert CUE to YAML.
2021-07-01 11:26:52 +02:00
kustomization: yaml.Marshal({
resources: ["deployment.yaml", "service.yaml"]
images: [{
2021-07-03 00:25:56 +02:00
name: "public.ecr.aws/j7f8d3t2/todoapp"
2021-07-01 11:26:52 +02:00
newName: remoteImage.ref
}]
})
}
2021-07-03 00:25:56 +02:00
// Value created for generic reference of `kubeconfig` in `todoapp.cue`
2021-07-01 11:26:52 +02:00
kubeSrc: kubernetes.#Resources & {
2021-07-03 00:25:56 +02:00
kubeconfig: kubeconfig
2021-07-01 11:26:52 +02:00
source: kustomization
}
}
2021-06-16 18:57:19 +02:00
```
2021-07-01 11:26:52 +02:00
< / TabItem >
< / Tabs >
2021-06-04 03:18:32 +02:00
### Connect the Inputs
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
2021-07-01 11:26:52 +02:00
groupId="provider"
values={[
2021-07-03 00:25:56 +02:00
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
2021-07-01 11:26:52 +02:00
]}>
2021-06-04 03:18:32 +02:00
2021-07-01 11:26:52 +02:00
< TabItem value = "kind" >
Next, we'll provide the two new inputs, `repository` and `registry` .
2021-06-04 03:18:32 +02:00
```shell
2021-07-05 15:24:48 +02:00
# A name after `localhost:5000/` is required to avoid error on push to the local registry
2021-07-01 11:26:52 +02:00
dagger input text registry "localhost:5000/kind" -e kube
# Add todoapp (current folder) to repository value
dagger input dir repository . -e kube
2021-06-04 03:18:32 +02:00
```
2021-07-01 11:26:52 +02:00
< / TabItem >
2021-07-03 00:25:56 +02:00
< TabItem value = "gke" >
2021-07-01 11:26:52 +02:00
Next, we'll provide the two new inputs, `repository` and `registry` .
2021-06-04 03:18:32 +02:00
```shell
2021-07-01 11:26:52 +02:00
# Add registry to export built image to
dagger input text registry < URI > -e kube
# Add todoapp (current folder) to repository value
dagger input dir repository . -e kube
2021-06-04 03:18:32 +02:00
```
2021-07-01 11:26:52 +02:00
< / TabItem >
2021-07-03 00:25:56 +02:00
< TabItem value = "eks" >
2021-07-01 11:26:52 +02:00
Next, we'll provide the two new inputs, `repository` and `registry` .
2021-06-04 03:18:32 +02:00
```shell
2021-07-01 11:26:52 +02:00
# Add registry to export built image to
dagger input text registry < URI > -e kube
# Add todoapp (current folder) to repository value
dagger input dir repository . -e kube
2021-06-04 03:18:32 +02:00
```
2021-07-01 11:26:52 +02:00
< / TabItem >
< / Tabs >
2021-06-04 03:18:32 +02:00
2021-07-01 11:26:52 +02:00
### Bring up the changes
2021-06-04 03:18:32 +02:00
2021-07-01 11:26:52 +02:00
```shell
dagger up -e kube
# 4:09AM INF manifest | computing
# 4:09AM INF repository | computing
# ...
# 4:09AM INF todoApp.kubeSrc | #37 0.858 service/todoapp-service created
# 4:09AM INF todoApp.kubeSrc | #37 0.879 deployment.apps/todoapp created
# Output Value Description
2021-07-07 11:10:54 +02:00
# todoApp.remoteImage.ref "localhost:5000/kind:test-kind@sha256:cb8d92518b876a3fe15a23f7c071290dfbad50283ad976f3f5b93e9f20cefee6" Image ref
2021-07-01 11:26:52 +02:00
# todoApp.remoteImage.digest "sha256:cb8d92518b876a3fe15a23f7c071290dfbad50283ad976f3f5b93e9f20cefee6" Image digest
```
Let's verify if the deployment worked:
2021-06-04 03:18:32 +02:00
2021-07-01 11:26:52 +02:00
```shell
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# todoapp 1/1 1 1 50s
```
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
Before continuing, cleanup deployment:
```shell
kubectl delete -f k8s/
# deployment.apps "todoapp" deleted
# service "todoapp-service" deleted
```
## CUE Kubernetes manifest
This section will convert Kubernetes YAML manifest from `k8s` directory to [CUE ](https://cuelang.org/ ) to take advantage
of the language features.
> For a more advanced example, see the [official CUE Kubernetes tutorial](https://github.com/cuelang/cue/blob/v0.4.0/doc/tutorial/kubernetes/README.md)
### Convert Kubernetes objects to CUE
2021-07-05 15:24:48 +02:00
First, let's create re-usable definitions for the `deployment` and the `service` to remove a lot of boilerplate
2021-07-03 00:25:56 +02:00
and repetition.
Let's define a re-usable `#Deployment` definition in `kube/deployment.cue` .
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/deployment.cue"
2021-07-03 00:25:56 +02:00
package main
// Deployment template containing all the common boilerplate shared by
// deployments of this application.
#Deployment: {
2021-07-05 15:24:48 +02:00
// Name of the deployment. This will be used to label resources automatically
2021-07-03 00:25:56 +02:00
// and generate selectors.
name: string
// Container image.
image: string
// 80 is the default port.
port: *80 | int
// 1 is the default, but we allow any number.
replicas: *1 | int
// Deployment manifest. Uses the name, image, port and replicas above to
// generate the resource manifest.
manifest: {
apiVersion: "apps/v1"
kind: "Deployment"
metadata: {
"name": name
labels: app: name
}
spec: {
"replicas": replicas
selector: matchLabels: app: name
template: {
metadata: labels: app: name
spec: containers: [{
"name": name
"image": image
ports: [{
containerPort: port
}]
}]
}
}
}
}
```
Indeed, let's also define a re-usable `#Service` definition in `kube/service.cue` .
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/service.cue"
2021-07-03 00:25:56 +02:00
package main
// Service template containing all the common boilerplate shared by
// services of this application.
#Service: {
2021-07-05 15:24:48 +02:00
// Name of the service. This will be used to label resources automatically
2021-07-03 00:25:56 +02:00
// and generate selector.
name: string
// NodePort is the default service type.
type: *"NodePort" | "LoadBalancer" | "ClusterIP" | "ExternalName"
// Ports where the service should listen
ports: [string]: number
// Service manifest. Uses the name, type and ports above to
// generate the resource manifest.
manifest: {
apiVersion: "v1"
kind: "Service"
metadata: {
"name": "\(name)-service"
labels: app: name
}
spec: {
"type": type
"ports": [
for k, v in ports {
"name": k
port: v
},
]
selector: app: name
}
}
}
```
### Generate Kubernetes manifest
2021-07-05 15:24:48 +02:00
Now that you have generic definitions for your Kubernetes objects. You can use them to get back your YAML definition
without having boilerplate nor repetition.
2021-07-03 00:25:56 +02:00
2021-07-05 15:24:48 +02:00
Create a new definition named `#AppManifest` that will generate the YAML in `kube/manifest.cue` .
2021-07-03 00:25:56 +02:00
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/manifest.cue"
2021-07-03 00:25:56 +02:00
package main
import (
"encoding/yaml"
)
// Define and generate kubernetes deployment to deploy to kubernetes cluster
#AppManifest: {
// Name of the application
name: string
// Image to deploy to
image: string
// Define a kubernetes deployment object
deployment: #Deployment & {
"name": name
"image": image
}
// Define a kubernetes service object
service: #Service & {
"name": name
ports: "http": deployment.port
}
// Merge definitions and convert them back from CUE to YAML
manifest: yaml.MarshalStream([deployment.manifest, service.manifest])
}
```
### Update manifest
You can now remove the `manifest` input in `kube/todoapp.cue` and instead use the manifest created by `#AppManifest` .
`kube/todoapp.cue` configuration has following changes:
2021-07-05 15:24:48 +02:00
- removal of unused imported `encoding/yaml` and `kustomize` packages.
2021-07-03 00:25:56 +02:00
- removal of `manifest` input that is doesn't need anymore.
- removal of `kustomization` to replace it with `#AppManifest` definition.
2021-07-05 15:24:48 +02:00
- Update `kubeSrc` to use `manifest` field instead of `source` because we don't send Kubernetes manifest of `dagger.#Artifact` type anymore.
2021-07-03 00:25:56 +02:00
< Tabs defaultValue = "kind"
groupId="provider"
values={[
{label: 'kind', value: 'kind'}, {label: 'GKE', value: 'gke'}, {label: 'EKS', value: 'eks'},
]}>
< TabItem value = "kind" >
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/todoapp.cue"
2021-07-03 00:25:56 +02:00
package main
import (
"alpha.dagger.io/dagger"
"alpha.dagger.io/docker"
"alpha.dagger.io/kubernetes"
)
// input: source code repository, must contain a Dockerfile
// set with `dagger input dir repository . -e kube`
repository: dagger.#Artifact & dagger.#Input
// Registry to push images to
registry: string & dagger.#Input
2021-07-07 11:10:54 +02:00
tag: "test-kind"
2021-07-03 00:25:56 +02:00
// Todoapp deployment pipeline
todoApp: {
// Build the image from repositoru artifact
image: docker.#Build & {
source: repository
}
// Push image to registry
remoteImage: docker.#Push & {
target: "\(registry):\(tag)"
source: image
}
// Generate deployment manifest
deployment: #AppManifest & {
name: "todoapp"
image: remoteImage.ref
}
// Deploy the customized manifest to a kubernetes cluster
kubeSrc: kubernetes.#Resources & {
"kubeconfig": kubeconfig
manifest: deployment.manifest
}
}
```
< / TabItem >
< TabItem value = "gke" >
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/todoapp.cue"
2021-07-03 00:25:56 +02:00
package main
import (
"alpha.dagger.io/dagger"
"alpha.dagger.io/docker"
"alpha.dagger.io/kubernetes"
)
// input: source code repository, must contain a Dockerfile
// set with `dagger input dir repository . -e kube`
repository: dagger.#Artifact & dagger.#Input
// GCR registry to push images to
registry: string & dagger.#Input
2021-07-07 11:10:54 +02:00
tag: "test-gcr"
2021-07-03 00:25:56 +02:00
// Todoapp deployment pipeline
todoApp: {
// Build the image from repositoru artifact
image: docker.#Build & {
source: repository
}
// Push image to registry
remoteImage: docker.#Push & {
target: "\(registry):\(tag)"
source: image
}
// Generate deployment manifest
deployment: #AppManifest & {
name: "todoApp"
image: remoteImage.ref
}
// Deploy the customized manifest to a kubernetes cluster
kubeSrc: kubernetes.#Resources & {
"kubeconfig": kubeconfig
manifest: deployment.manifest
}
}
```
< / TabItem >
< TabItem value = "eks" >
2021-07-07 12:18:56 +02:00
```cue title="todoapp/kube/todoapp.cue"
2021-07-03 00:25:56 +02:00
package main
import (
"alpha.dagger.io/dagger"
"alpha.dagger.io/docker"
"alpha.dagger.io/kubernetes"
)
// input: source code repository, must contain a Dockerfile
// set with `dagger input dir repository . -e kube`
repository: dagger.#Artifact & dagger.#Input
// ECR registry to push images to
registry: string & dagger.#Input
2021-07-07 11:10:54 +02:00
tag: "test-ecr"
2021-07-03 00:25:56 +02:00
// Todoapp deployment pipeline
todoApp: {
// Build the image from repositoru artifact
image: docker.#Build & {
source: repository
}
// Push image to registry
remoteImage: docker.#Push & {
target: "\(registry):\(tag)"
source: image
}
// Generate deployment manifest
deployment: #AppManifest & {
name: "todoApp"
image: remoteImage.ref
}
// Deploy the customized manifest to a kubernetes cluster
kubeSrc: kubernetes.#Resources & {
"kubeconfig": kubeconfig
manifest: deployment.manifest
}
}
```
< / TabItem >
< / Tabs >
### Remove unused input
2021-07-05 15:24:48 +02:00
Now that we manage our Kubernetes manifest in CUE, we don't need `manifest` anymore.
2021-07-03 00:25:56 +02:00
```shell
# Remove `manifest` input
dagger input unset manifest -e kube
```
### Deployment
```shell
dagger up -e kube
# 4:09AM INF manifest | computing
# 4:09AM INF repository | computing
# ...
# 4:09AM INF todoApp.kubeSrc | #37 0.858 service/todoapp-service created
# 4:09AM INF todoApp.kubeSrc | #37 0.879 deployment.apps/todoapp created
# Output Value Description
2021-07-07 11:10:54 +02:00
# todoApp.remoteImage.ref "localhost:5000/kind:test-kind@sha256:cb8d91518b076a3fe15a33f7c171290dfbad50283ad976f3f5b93e9f33cefag7" Image ref
2021-07-03 00:25:56 +02:00
# todoApp.remoteImage.digest "sha256:cb8d91518b076a3fe15a33f7c171290dfbad50283ad976f3f5b93e9f33cefag7" Image digest
```
2021-07-05 15:24:48 +02:00
Let's verify that the deployment worked:
2021-07-03 00:25:56 +02:00
```shell
kubectl get deployments
# NAME READY UP-TO-DATE AVAILABLE AGE
# todoapp 1/1 1 1 37s
```
2021-07-01 11:26:52 +02:00
## Next Steps
2021-06-04 03:18:32 +02:00
2021-07-03 00:25:56 +02:00
Integrate Helm with Dagger:
2021-06-04 03:18:32 +02:00
- [Helm ](https://github.com/dagger/dagger/tree/main/stdlib/kubernetes/helm )