Secure Kubernetes Application Secrets with Pulumi ESC
In this tutorial, you'll learn:
- How to securely manage secrets and configurations with Pulumi ESC
- How to create, manage, and import Pulumi environments for Kubernetes apps
- How to securely deploy a Kubernetes application using Minikube and Pulumi
Prerequisites:
- Completion of the Kubernetes Getting Started guide or familiarity with the basics of the Pulumi workflow
- A Pulumi Cloud account
- Install and configure Kubectl CLI
- Install and configure Minikube
Pulumi ESC helps you manage different environments (like development, staging, and production) while securely handling secrets and configurations. In this tutorial, you’ll create two environments: one for managing the Kubernetes infrastructure and one for application-specific configurations.
Initialize your Pulumi project
Start by creating a new Pulumi project named esc-k8-tutorial
. Open your terminal and use the following command to initialize your new Pulumi project:
pulumi new kubernetes-typescript
Follow the prompts, and use esc-k8-tutorial
as the project name.
Install the Pulumi ESC CLI
Now, you will install the Pulumi ESC CLI. Use an option below to install based on your operating system.
$ brew update && brew install pulumi/tap/esc
$ curl -fsSL https://get.pulumi.com/esc/install.sh | sh
Windows Binary Download
For more installation options, visit the ESC installation docs.
Create your first environment
Next, use the Pulumi ESC CLI to create the first environment, which will manage the Kubernetes infrastructure. To create an environment named base, run the following command, adding [
esc env init esc-k8-tutorial/base
esc env init esc-k8-tutorial/db
These environments will handle Kubernetes cluster configuration and database specific secrets and configuration.
Add secrets and configuration to the db environment
Now that your base environment is created, you’ll securely store the necessary secrets and configurations for the voting app through Pulumi Cloud UI. Follow these steps to add secrets and configuration values using the Pulumi Cloud.
- Open your browser and log in to Pulumi Cloud.
- Select Environments from the ESC section of the console.
- Find and select the
db
environment under theproject: esc-k8-tutorial
.
Using the Document view, remove the example environment definition add the following:
values:
labels:
component: db
postgres:
replicas: 1
image:
name: postgres
tag: 15-alpine
port: 5432
user: postgres
pulumiConfig:
POSTGRES_USER: ${postgres.user}
POSTGRES_PORT: ${postgres.port}
LABELS: ${labels}
POSTGRES_IMAGE: ${postgres.image.name}:${postgres.image.tag}
POSTGRES_REPLICA: ${postgres.replicas}
Click Save.
Now, you can add your postgres.password
as a secret by:
- Selecting the Table view button
- Click on +Secret/config
- Add
postgres.password
as the Path, enter your desired password as the Value, then change from Plaintext, to Secret in the drop-down.
Click Done and then click Save.
Your password is now stored securely in Pulumi Cloud as a secret.
Composing and importing environments
Pulumi ESC enables you to compose different environments for modularity and reusability via importing. In this scenario, your base
environment might define Kubernetes cluster details across multiple environments. For example in a real-world application, environments can be set by a central DevOps team to maintain constancy, security and cost-control.
To import settings from the base
environment into the app
environment select the base
environment and using the Document view, remove the example environment definition and copy in the following:
values:
labels:
app: voting
Click Save.
Return to your db
environment and add the following to the top of your environment definition:
imports:
- esc-k8-tutorial/base
Click Save.
Verify the environment import
To confirm that the base
environment has been imported successfully, you can see this reflected in the see an Environment preview of your db
environment:
Deploying the voting app to Kubernetes
With your Pulumi ESC environments set up and secrets configured, you’re ready to add and deploy the voting app to a Kubernetes cluster using Minikube.
Add the following code to the index.ts
file located in your Pulumi project directory:
import * as k8s from "@pulumi/kubernetes";
// Define the voting app deployment
const appLabels = { app: "voting-app" };
const deployment = new k8s.apps.v1.Deployment("voting-app", {
spec: {
selector: { matchLabels: appLabels },
replicas: 1,
template: {
metadata: { labels: appLabels },
spec: {
containers: [
{
name: "voting-app",
image: "dockersamples/examplevotingapp_vote:latest",
ports: [{ containerPort: 80 }],
},
],
},
},
},
});
// Define a Kubernetes service to expose the app
const service = new k8s.core.v1.Service("voting-app-service", {
metadata: {
labels: appLabels,
},
spec: {
type: "ClusterIP",
ports: [{ port: 80, targetPort: 80 }],
selector: appLabels,
},
});
This code defines and deploys a simple TypeScript voting application on a Kubernetes cluster using Pulumi that creates and defines:
Deployment Definition: The code creates a Kubernetes
Deployment
. It specifies that the app should use thedockersamples/examplevotingapp_vote:latest
Docker image. The deployment has:- One replica (or instance) of the voting app running.
- The app listens on port 80 inside the container.
Service Definition: The code also defines a Kubernetes
Service
of typeClusterIP
. This service exposes the voting app inside the cluster and forwards traffic to the app’s container, which listens on port 80.
Deploy the app with Pulumi
Once the Kubernetes resources are defined, deploy the voting app with Pulumi by running:
pulumi up
Pulumi will ask for confirmation before deploying the voting app to your Kubernetes cluster.
Access the application
After the app is deployed, access it locally by forwarding the Kubernetes service to a local port:
Run the following command to see all services across namespaces:
kubectl get svc --all-namespaces
Note your voting app name in the output.
❯ kubectl get svc --all-namespaces
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default db ClusterIP 10.97.4.236 <none> 5432/TCP 18h
default kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 363d
default redis ClusterIP 10.100.7.66 <none> 6379/TCP 23h
default result NodePort 10.106.91.25 <none> 5001:31001/TCP 23h
default vote NodePort 10.103.1.134 <none> 5000:31002/TCP 23h
default voting-app-service-c7f2a549 ClusterIP 10.104.116.64 <none> 80/TCP 2m15s
kube-system kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 363d
Run kubectl port-forward svc/voting-app-service-<id> 8080:80
adding your unique voting-app-service-<id>
.
kubectl port-forward svc/voting-app-service-c7f2a549 8080:80
Open your browser and go to http://localhost:8080
to view the voting app.
Cleanup
To remove the resources and clean up your local environment, follow these steps:
Destroy the Pulumi stack by running:
pulumi destroy
If you no longer need the project, remove it from your local machine by deleting the project folder:
rm -rf esc-k8-tutorial
This ensures that all resources are properly cleaned up, and your local environment remains organized.
Next steps
In this tutorial, you secured Kubernetes application secrets using Pulumi Environments, Secrets, and Configuration (ESC). You set up environments, securely stored sensitive data, and deployed a sample voting app to a Kubernetes cluster.
To dive deeper into using Pulumi ESC for advanced scenarios, check out the following resources:
Environment Composition: Learn more about to effectively compose multiple environments to manage configurations across your infrastructure. Explore the Pulumi documentation on environment imports.
Managing Secrets: Learn how to securely manage and adopt dynamic, short-lived secrets on demand using Pulumi ESC, ensuring sensitive information is protected across different environments. Read more in the Pulumi ESC documentation.