31 KiB
title_tag, meta_desc, title, h1, meta_image, menu, aliases
title_tag | meta_desc | title | h1 | meta_image | menu | aliases | |||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Deploy Kubernetes Apps | Crosswalk | This page gives you an overview on how to deploy Kubernetes applications to different cloud providers. | Apps | Kubernetes Apps | /images/docs/meta-images/docs-clouds-kubernetes-meta-image.png |
|
|
The following are examples of how to create and use various types of Kubernetes resources, and typical apps and workloads.
{{< chooser cloud "aws,azure,gcp" / >}}
{{% choosable cloud aws %}}
The full code for the AWS apps stack is on GitHub.
{{% /choosable %}}
{{% choosable cloud azure %}}
The full code for the Azure apps stack is on GitHub.
{{% /choosable %}}
{{% choosable cloud gcp %}}
The full code for the Google Cloud apps stack is on GitHub.
{{% /choosable %}}
The full code for the apps is on GitHub.
Overview
Check out how to:
- Overview
- Build and Deploy a Container
- Deploy a Pod with a Sidecar
- Deploy a Helm Chart
- Deploy Wordpress
- Create a Deployment with a Secret
- Perform a ConfigMap Rollout on a Deployment
- Deploy a Job
- Deploy a DaemonSet
- Deploy a CronJob
- Deploy a StatefulSet
- Learn More
Build and Deploy a Container
Build a Docker container image, push it to the registry, and deploy it to Kubernetes.
{{% choosable cloud aws %}}
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{< choosable k8s-language typescript >}}
import * as awsx from "@pulumi/awsx";
import * as k8s from "@pulumi/kubernetes";
// Create a repository.
const repo = new awsx.ecr.Repository("my-repo", {
forceDelete: true,
});
// Build a Docker image from a local Dockerfile context in the
// './node-app' directory, and push it to the registry.
const customImage = "node-app";
const appImage = new awsx.ecr.Image("image", {
repositoryUrl: repo.url,
path: `./${customImage}`,
});
// Create a k8s provider.
const provider = new k8s.Provider("provider", {
kubeconfig: config.kubeconfig,
namespace: config.appsNamespaceName,
});
// Create a Deployment of the built container.
const appLabels = { app: customImage };
const appDeployment = new k8s.apps.v1.Deployment("app", {
spec: {
selector: { matchLabels: appLabels },
replicas: 1,
template: {
metadata: { labels: appLabels },
spec: {
containers: [{
name: customImage,
image: appImage.imageUri,
ports: [{name: "http", containerPort: 80}],
}],
}
},
}
}, { provider: provider });
{{< /choosable >}}
{{< choosable k8s-language typescript-kx >}}
import * as awsx from "@pulumi/awsx";
import * as k8s from "@pulumi/kubernetes";
import * as kx from "@pulumi/kubernetesx";
// Create a repository.
const repo = new awsx.ecr.Repository("my-repo", {
forceDelete: true,
});
// Build a Docker image from a local Dockerfile context in the
// './node-app' directory, and push it to the registry.
const customImage = "node-app";
const appImage = repo.buildAndPushImage(`./${customImage}`);
// Create a k8s provider.
const provider = new k8s.Provider("provider", {
kubeconfig: config.kubeconfig,
namespace: config.appsNamespaceName,
});
// Define the Pod for the Deployment.
const pb = new kx.PodBuilder({
containers: [{
image: appImage.imageUri,
ports: { "http": 80 },
}],
});
// Create a Deployment of the Pod defined by the PodBuilder.
const appDeploymentKx = new kx.Deployment("app-kx", {
spec: pb.asDeploymentSpec(),
}, { provider: provider });
{{< /choosable >}}
{{< /chooser >}}
{{% /choosable %}}
{{% choosable cloud azure %}}
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{< choosable k8s-language typescript >}}
import * as azure from "@pulumi/azure";
import * as docker from "@pulumi/docker";
import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";
// Create an Azure Resource Group
const resourceGroup = new azure.core.ResourceGroup("samples");
// Create a registry in ACR.
const registry = new azure.containerservice.Registry("myregistry", {
resourceGroupName: resourceGroup.name,
sku: "Basic",
adminEnabled: true,
});
// Build a Docker image from a local Dockerfile context in the
// './node-app' directory, and push it to the registry.
const customImage = "node-app";
const appImage = new docker.Image(customImage, {
imageName: pulumi.interpolate`${registry.loginServer}/${customImage}:v1.0.0`,
build: {
context: `./${customImage}`,
},
registry: {
server: registry.loginServer,
username: registry.adminUsername,
password: registry.adminPassword,
},
});
// Create a k8s provider.
const provider = new k8s.Provider("provider", {
kubeconfig: config.kubeconfig,
namespace: config.appsNamespaceName,
});
// Create a Deployment of the built container.
const appLabels = { app: customImage };
const appDeployment = new k8s.apps.v1.Deployment("app", {
spec: {
selector: { matchLabels: appLabels },
replicas: 1,
template: {
metadata: { labels: appLabels },
spec: {
containers: [{
name: customImage,
image: appImage.imageUri,
ports: [{name: "http", containerPort: 80}],
}],
}
},
}
}, { provider: provider });
{{< /choosable >}}
{{< choosable k8s-language typescript-kx >}}
import * as azure from "@pulumi/azure";
import * as k8s from "@pulumi/kubernetes";
import * as kx from "@pulumi/kubernetesx";
import * as docker from "@pulumi/docker";
import * as pulumi from "@pulumi/pulumi";
// Create an Azure Resource Group
const resourceGroup = new azure.core.ResourceGroup("samples");
// Create a registry in ACR.
const registry = new azure.containerservice.Registry("myregistry", {
resourceGroupName: resourceGroup.name,
sku: "Basic",
adminEnabled: true,
});
// Build a Docker image from a local Dockerfile context in the
// './node-app' directory, and push it to the registry.
const customImage = "node-app";
const appImage = new docker.Image(customImage, {
imageName: pulumi.interpolate`${registry.loginServer}/${customImage}:v1.0.0`,
build: {
context: `./${customImage}`,
},
registry: {
server: registry.loginServer,
username: registry.adminUsername,
password: registry.adminPassword,
},
});
// Create a k8s provider.
const provider = new k8s.Provider("provider", {
kubeconfig: config.kubeconfig,
namespace: config.appsNamespaceName,
});
// Define the Pod for the Deployment.
const pb = new kx.PodBuilder({
containers: [{
image: appImage.imageUri,
ports: { "http": 80 },
}],
});
// Create a Deployment of the Pod defined by the PodBuilder.
const appDeploymentKx = new kx.Deployment("app-kx", {
spec: pb.asDeploymentSpec(),
}, { provider: provider });
{{< /choosable >}}
{{< /chooser >}}
{{% /choosable %}}
{{% choosable cloud gcp %}}
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{< choosable k8s-language typescript >}}
import * as docker from "@pulumi/docker";
import * as gcp from "@pulumi/gcp";
import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";
// Get the Google Cloud project registry repository.
const registry = gcp.container.getRegistryRepositoryOutput();
// Get the repository URL
const repositoryUrl = registry.repositoryUrl;
// Build a Docker image from a local Dockerfile context in the
// './node-app' directory, and push it to the registry.
const customImage = "node-app";
const appImage = new docker.Image(customImage, {
imageName: pulumi.interpolate`${repositoryUrl}/${customImage}:v1.0.0`,
build: {
context: `./${customImage}`,
},
});
// Create a k8s provider.
const provider = new k8s.Provider("provider", {
kubeconfig: config.kubeconfig,
namespace: config.appsNamespaceName,
});
// Create a Deployment of the built container.
const appLabels = { app: customImage };
const appDeployment = new k8s.apps.v1.Deployment("app", {
spec: {
selector: { matchLabels: appLabels },
replicas: 1,
template: {
metadata: { labels: appLabels },
spec: {
containers: [{
name: customImage,
image: appImage.imageUri,
ports: [{name: "http", containerPort: 80}],
}],
}
},
}
}, { provider: provider });
{{< /choosable >}}
{{< choosable k8s-language typescript-kx >}}
import * as docker from "@pulumi/docker";
import * as gcp from "@pulumi/gcp";
import * as k8s from "@pulumi/kubernetes";
import * as kx from "@pulumi/kubernetesx";
import * as pulumi from "@pulumi/pulumi";
// Get the Google Cloud project registry repository.
const registry = gcp.container.getRegistryRepositoryOutput();
// Get the repository URL
const repositoryUrl = registry.repositoryUrl;
// Build a Docker image from a local Dockerfile context in the
// './node-app' directory, and push it to the registry.
const customImage = "node-app";
const appImage = new docker.Image(customImage, {
imageName: pulumi.interpolate`${repositoryUrl}/${customImage}:v1.0.0`,
build: {
context: `./${customImage}`,
},
});
// Create a k8s provider.
const provider = new k8s.Provider("provider", {
kubeconfig: config.kubeconfig,
namespace: config.appsNamespaceName,
});
// Define the Pod for the Deployment.
const pb = new kx.PodBuilder({
containers: [{
image: appImage.imageUri,
ports: { "http": 80 },
}],
});
// Create a Deployment of the Pod defined by the PodBuilder.
const appDeploymentKx = new kx.Deployment("app-kx", {
spec: pb.asDeploymentSpec(),
}, { provider: provider });
{{< /choosable >}}
{{< /chooser >}}
{{% /choosable %}}
Deploy a Pod with a Sidecar
The full code for this app stack is on GitHub.
Create a NGINX Pod with a Debian sidecar that prints to a file in a shared volume of the Pod.
import * as k8s from "@pulumi/kubernetes";
// Create an example Pod with a Sidecar.
const pod = new k8s.core.v1.Pod("example", {
spec: {
restartPolicy: "Never",
volumes: [
{name: "shared-data", emptyDir: {}},
],
containers: [
{
name: "nginx",
image: "nginx",
resources: {requests: {cpu: "50m", memory: "50Mi"}},
volumeMounts: [
{ name: "shared-data", mountPath: "/usr/share/nginx/html"},
],
},
{
name: "debian-container",
image: "debian",
resources: {requests: {cpu: "50m", memory: "50Mi"}},
volumeMounts: [
{ name: "shared-data", mountPath: "/pod-data"},
],
command: [ "/bin/bash"],
args: ["-c", "echo Hello from the Debian container > /pod-data/index.html ; sleep infinity"],
}
],
}
}, { provider: provider });
Print out the contents of the shared file from the nginx
container in the Pod.
$ kubectl exec -it example-<SUFFIX> -n `pulumi output stack appsNamespaceName` -c nginx -- cat /usr/share/nginx/html/index.html
Deploy a Helm Chart
Deploy the Helm chart into the app-svcs
namespace created in Configure
Cluster Defaults, and publicly expose it to the
Internet using a load balanced Service.
Note: NGINX requires a privileged PSP given its use of
allowPrivilegeEscalation: true
.
import * as k8s from "@pulumi/kubernetes";
// Deploy the NGINX ingress controller using the Helm chart.
const nginx = new k8s.helm.v3.Chart("nginx",
{
namespace: config.appSvcsNamespaceName,
chart: "nginx-ingress",
version: "1.24.4",
fetchOpts: {repo: "https://charts.helm.sh/stable/"},
values: {controller: {publishService: {enabled: true}}},
transformations: [
(obj: any) => {
// Do transformations on the YAML to set the namespace
if (obj.metadata) {
obj.metadata.namespace = config.appSvcsNamespaceName;
}
},
],
},
{providers: {kubernetes: provider}},
);
Deploy Wordpress
Create a Deployment of Wordpress.
The full code for this app stack is on GitHub.
import * as k8s from "@pulumi/kubernetes";
const wordpress = new k8s.apps.v1.Deployment("wordpress", {
spec: {
selector: { matchLabels: { app: "wordpress", release: "example" } },
strategy: { type: "RollingUpdate" },
replicas: 1,
template: {
metadata: { labels: { app: "wordpress", release: "example" } },
spec: {
hostAliases: [ { ip: "127.0.0.1", hostnames: [ "status.localhost"] } ],
containers: [
{
name: "wordpress",
image: "docker.io/bitnami/wordpress:5.2.4-debian-9-r0",
imagePullPolicy: "IfNotPresent",
env: [
{ name: "MARIADB_HOST", value: "mariadb" },
{ name: "WORDPRESS_DATABASE_NAME", value: "bitnami_wordpress" },
{ name: "WORDPRESS_DATABASE_USER", value: "bn_wordpress" },
{
name: "WORDPRESS_DATABASE_PASSWORD",
valueFrom: {
secretKeyRef: {
name: mariadbSecret.metadata.name,
key: "mariadb-password"
}
}
},
...
],
ports: [
{ name: "http", containerPort: 80 },
{ name: "https", containerPort: 443 }
],
volumeMounts: [
{
mountPath: "/bitnami/wordpress",
name: "wordpress-data",
subPath: "wordpress"
}
],
resources: { requests: { cpu: "300m", memory: "512Mi" } }
...
}
],
...
}
}
}
}, { provider: provider });
Create a Deployment with a Secret
Create a Deployment NGINX that uses a Secret.
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{% choosable k8s-language typescript %}}
import * as k8s from "@pulumi/kubernetes";
// Create a Secret with the database credentials.
const databaseSecret = new k8s.core.v1.Secret("db-secret", {
stringData: {
"database-username": config.databaseUsername,
"database-password": config.databasePassword,
}
}, { provider: provider });
// Create a Deployment that uses the database credentials as environment variables.
const appName = "nginx";
const appLabels = { app: appName };
const nginx = new k8s.apps.v1.Deployment(appName, {
metadata: { labels: appLabels },
spec: {
selector: {
matchLabels: appLabels,
},
replicas: 1,
template: {
metadata: { labels: appLabels },
spec: {
containers: [
{
image: "nginx",
name: "nginx",
env: [
{
name: "DATABASE_USERNAME",
valueFrom: {
secretKeyRef: {
name: databaseSecret.metadata.name,
key: "database-username"
}
}
},
{
name: "DATABASE_PASSWORD",
valueFrom: {
secretKeyRef: {
name: databaseSecret.metadata.name,
key: "database-password"
}
}
}
]
},
],
},
},
},
}, { provider: provider });
{{% /choosable %}}
{{% choosable k8s-language typescript-kx %}}
import * as kx from "@pulumi/kubernetesx";
// Create a KX Secret with the database credentials.
const databaseSecretKx = new kx.Secret("db-secret", {
stringData: {
"database-username": config.databaseUsername,
"database-password": config.databasePassword,
}
}, { provider: provider });
// Create a KX PodBuilder for the demo app.
const nginxPB = new kx.PodBuilder({
containers: [{
image: "nginx",
env: {
"DATABASE_USERNAME": databaseSecretKx.asEnvValue("database-username"),
"DATABASE_PASSWORD": databaseSecretKx.asEnvValue("database-password"),
}
}]
});
// Create a KX Deployment from the KX PodBuilder by transforming it into a DeploymentSpec.
// The deployment use database credentials as environment variables.
const nginxDeployment = new kx.Deployment(appName, {
spec: nginxPB.asDeploymentSpec({replicas: 1})
}, { provider: provider });
{{% /choosable %}}
{{< /chooser >}}
Perform a ConfigMap Rollout on a Deployment
For a complete example, check out our Kubernetes Graceful App Rollout tutorial for more details on how to update a Deployment automatically when it's ConfigMap changes.
Deploy a Job
Deploy a Job of a Perl program.
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{% choosable k8s-language typescript %}}
import * as k8s from "@pulumi/kubernetes";
// Create an example Job.
const exampleJob = new k8s.batch.v1.Job("example-job", {
spec: {
template: {
spec: {
containers: [
{
name: "pi",
image: "perl",
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"],
}
],
restartPolicy: "Never"
}
},
}
}, { provider: provider });
{{% /choosable %}}
{{% choosable k8s-language typescript-kx %}}
import * as kx from "@pulumi/kubernetesx";
// Create the PodBuilder for the Job.
const pb = new kx.PodBuilder({
containers: [{
name: "pi",
image: "perl",
command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"],
}],
restartPolicy: "Never",
});
// Create a Job using the Pod defined by the PodBuilder.
const exampleJobKx = new kx.Job("example-job-kx", {
spec: pb.asJobSpec(),
}, { provider: provider });
{{% /choosable %}}
{{< /chooser >}}
Deploy a DaemonSet
Deploy a DaemonSet of NGINX across all nodes in the cluster.
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{% choosable k8s-language typescript %}}
import * as k8s from "@pulumi/kubernetes";
// Create a DaemonSet that deploys nginx to each worker node.
const appName = "nginx";
const appLabels = { app: appName };
const nginx = new k8s.apps.v1.DaemonSet(appName, {
metadata: { labels: appLabels },
spec: {
selector: {
matchLabels: appLabels,
},
template: {
metadata: { labels: appLabels },
spec: {
containers: [
{
image: "nginx",
name: "nginx",
},
],
},
},
},
}, { provider: provider });
{{% /choosable %}}
{{% choosable k8s-language typescript-kx %}}
Coming Soon.
{{% /choosable %}}
{{< /chooser >}}
Deploy a CronJob
Deploy a CronJob of a command that runs every minute.
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{% choosable k8s-language typescript %}}
import * as k8s from "@pulumi/kubernetes";
// Create an example CronJob.
const exampleCronJob = new k8s.batch.v1beta1.CronJob("example-cronjob", {
spec: {
schedule: "*/1 * * * *",
jobTemplate: {
spec: {
template: {
spec: {
containers: [
{
name: "hello",
image: "busybox",
args: ["/bin/sh", "-c", "date; echo Hello from the Kubernetes cluster"],
}
],
restartPolicy: "OnFailure"
}
}
}
},
}
}, { provider: provider });
{{% /choosable %}}
{{% choosable k8s-language typescript-kx %}}
Coming soon.
{{% /choosable %}}
{{< /chooser >}}
Deploy a StatefulSet
Deploy a StatefulSet of MariaDB.
The full code for this app stack is on GitHub.
{{< chooser k8s-language "typescript,typescript-kx" >}}
{{% choosable k8s-language typescript %}}
import * as k8s from "@pulumi/kubernetes";
// Deploy MariaDB as a StatefulSet.
const mariadb = new k8s.apps.v1.StatefulSet("mariadb", {
spec: {
selector: {
matchLabels: {
app: "mariadb",
release: "example",
component: "master"
}
},
serviceName: "mariadb",
replicas: 1,
updateStrategy: {
type: "RollingUpdate"
},
template: {
metadata: {
labels: {
app: "mariadb",
release: "example",
component: "master"
}
},
spec: {
serviceAccountName: "default",
securityContext: {
fsGroup: 1001,
runAsUser: 1001
},
affinity: {
podAntiAffinity: {
preferredDuringSchedulingIgnoredDuringExecution: [
{
weight: 1,
podAffinityTerm: {
topologyKey: "kubernetes.io/hostname",
labelSelector: {
matchLabels: {
app: "mariadb",
release: "example"
}
}
}
}
]
}
},
containers: [
{
name: "mariadb",
image: "docker.io/bitnami/mariadb:10.3.18-debian-9-r36",
imagePullPolicy: "IfNotPresent",
env: [
{
name: "MARIADB_ROOT_PASSWORD",
valueFrom: {
secretKeyRef: {
name: mariadbSecret.metadata.name,
key: "mariadb-root-password"
}
}
},
{ name: "MARIADB_USER", value: "bn_wordpress" },
{
name: "MARIADB_PASSWORD",
valueFrom: {
secretKeyRef: {
name: mariadbSecret.metadata.name,
key: "mariadb-password"
}
}
},
{ name: "MARIADB_DATABASE", value: "bitnami_wordpress" }
],
ports: [
{ name: "mysql", containerPort: 3306 }
],
livenessProbe: {
exec: {
command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"],
},
initialDelaySeconds: 120,
periodSeconds: 10,
timeoutSeconds: 1,
successThreshold: 1,
failureThreshold: 3
},
readinessProbe: {
exec: {
command: ["sh", "-c", "exec mysqladmin status -uroot -p$MARIADB_ROOT_PASSWORD"]
},
initialDelaySeconds: 30,
periodSeconds: 10,
timeoutSeconds: 1,
successThreshold: 1,
failureThreshold: 3
},
volumeMounts: [
{
name: "data",
mountPath: "/bitnami/mariadb"
},
{
name: "config",
mountPath: "/opt/bitnami/mariadb/conf/my.cnf",
subPath: "my.cnf"
}
]
}
],
volumes: [
{
name: "config",
configMap: {
name: mariadbCM.metadata.name
}
}
]
},
},
volumeClaimTemplates: [
{
metadata: {
name: "data",
labels: {
app: "mariadb",
component: "master",
release: "example",
}
},
spec: {
accessModes: [
"ReadWriteOnce"
],
resources: {
requests: {
storage: "8Gi"
}
}
}
}
]
}
}, { provider: provider });
{{% /choosable %}}
{{% choosable k8s-language typescript-kx %}}
Coming soon.
{{% /choosable %}}
{{< /chooser >}}
Learn More
To learn more about how to work with Kubernetes and Pulumi, check out the Kubernetes Tutorials for details.