forked from pulumi/examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request pulumi#124 from pulumi/joeduffy/gke
Add a Google Kubernetes Engine (GKE) example
- Loading branch information
Showing
8 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
/bin/ | ||
/node_modules/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
name: gcp-ts-gke | ||
description: A Google Kubernetes Engine (GKE) cluster, with canary deployment | ||
runtime: nodejs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
# Google Kubernetes Engine (GKE) with a Canary Deployment | ||
|
||
This example provisions a [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine/) cluster, using | ||
infrastructure-as-code, and then deploys a Kubernetes Deployment into it, to test that the cluster is working. This | ||
demonstrates that you can manage both the Kubernetes objects themselves, in addition to underlying cloud infrastructure, | ||
using a single configuration language (in this case, TypeScript), tool, and workflow. | ||
|
||
# Prerequisites | ||
|
||
Ensure you have [downloaded and installed the Pulumi CLI](https://pulumi.io/install). | ||
|
||
We will be deploying to Google Cloud Platform (GCP), so you will need an account. If you don't have an account, | ||
[sign up for free here](https://cloud.google.com/free/). In either case, | ||
[follow the instructions here](https://pulumi.io/quickstart/gcp/setup.html) to connect Pulumi to your GCP account. | ||
|
||
This example assumes that you have GCP's `gcloud` CLI on your path. This is installed as part of the | ||
[GCP SDK](https://cloud.google.com/sdk/). | ||
|
||
# Running the Example | ||
|
||
After cloning this repo, `cd` into it and run these commands. A GKE Kubernetes cluster will appear! | ||
|
||
1. Create a new stack, which is an isolated deployment target for this example: | ||
|
||
```bash | ||
$ pulumi stack init gcp-ts-gke-dev | ||
``` | ||
|
||
2. Set the required configuration variables for this program: | ||
|
||
```bash | ||
$ pulumi config set gcp:project [your-gcp-project-here] | ||
$ pulumi config set gcp:zone us-west-1a # any valid GCP zone here | ||
$ pulumi config set password --secret [your-cluster-password-here] | ||
``` | ||
|
||
By default, your cluster will have 3 nodes of type `n1-standard-1`. This is configurable, however; for instance | ||
if we'd like to choose 5 nodes of type `n1-standard-2` instead, we can run these commands: | ||
```bash | ||
$ pulumi config set nodeCount 5 | ||
$ pulumi config set nodeMachineType n1-standard-2 | ||
``` | ||
This shows how stacks can be configurable in useful ways. You can even change these after provisioning. | ||
3. Deploy everything with the `pulumi up` command. This provisions all the GCP resources necessary, including | ||
your GKE cluster itself, and then deploys a Kubernetes Deployment running nginx, all in a single gesture: | ||
```bash | ||
$ pulumi up | ||
``` | ||
This will show you a preview, ask for confirmation, and then chug away at provisioning your cluster: | ||
``` | ||
Updating stack 'gcp-ts-gke-dev' | ||
Performing changes: | ||
Type Name Plan Info | ||
+ pulumi:pulumi:Stack gcp-ts-gke-gcp-ts-gke-dev create | ||
+ ├─ gcp:container:Cluster gke-cluster create | ||
+ ├─ pulumi:providers:kubernetes gkeK8s create | ||
+ └─ kubernetes:apps:Deployment canary create | ||
---outputs:--- | ||
kubeConfig: "apiVersion: v1\n..." | ||
info: 4 changes updated: | ||
+ 4 resources created | ||
Update duration: 2m07.424737735s | ||
``` | ||
After about two minutes, your cluster will be ready, and its config will be printed. | ||
4. From here, you may take this config and use it either in your `~/.kube/config` file, or just by saving it | ||
locally and plugging it into the `KUBECONFIG` envvar. All of your usual `gcloud` commands will work too, of course. | ||
For instance: | ||
```bash | ||
$ pulumi stack output kubeConfig > kubeconfig.yaml | ||
$ KUBECONFIG=./kubeconfig.yaml kubectl get po | ||
NAME READY STATUS RESTARTS AGE | ||
canary-n7wfhtrp-fdbfd897b-lrm58 1/1 Running 0 58s | ||
``` | ||
5. At this point, you have a running cluster. Feel free to modify your program, and run `pulumi up` to redeploy changes. | ||
The Pulumi CLI automatically detects what has changed and makes the minimal edits necessary to accomplish these | ||
changes. This could be altering the existing chart, adding new GCP or Kubernetes resources, or anything, really. | ||
6. Once you are done, you can destroy all of the resources, and the stack: | ||
```bash | ||
$ pulumi destroy | ||
$ pulumi stack rm | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved. | ||
|
||
import * as gcp from "@pulumi/gcp"; | ||
import * as k8s from "@pulumi/kubernetes"; | ||
import * as pulumi from "@pulumi/pulumi"; | ||
import { nodeCount, nodeMachineType, password, username } from "./config"; | ||
|
||
// Create the GKE cluster and export it. | ||
export const k8sCluster = new gcp.container.Cluster("gke-cluster", { | ||
initialNodeCount: nodeCount, | ||
nodeVersion: "latest", | ||
minMasterVersion: "latest", | ||
masterAuth: { username, password }, | ||
nodeConfig: { | ||
machineType: nodeMachineType, | ||
oauthScopes: [ | ||
"https://www.googleapis.com/auth/compute", | ||
"https://www.googleapis.com/auth/devstorage.read_only", | ||
"https://www.googleapis.com/auth/logging.write", | ||
"https://www.googleapis.com/auth/monitoring" | ||
], | ||
}, | ||
}); | ||
|
||
// Manufacture a GKE-style Kubeconfig. Note that this is slightly "different" because of the way GKE requires | ||
// gcloud to be in the picture for cluster authentication (rather than using the client cert/key directly). | ||
export const k8sConfig = pulumi. | ||
all([ k8sCluster.name, k8sCluster.endpoint, k8sCluster.masterAuth ]). | ||
apply(([ name, endpoint, auth ]) => { | ||
const context = `${gcp.config.project}_${gcp.config.zone}_${name}`; | ||
return `apiVersion: v1 | ||
clusters: | ||
- cluster: | ||
certificate-authority-data: ${auth.clusterCaCertificate} | ||
server: https://${endpoint} | ||
name: ${context} | ||
contexts: | ||
- context: | ||
cluster: ${context} | ||
user: ${context} | ||
name: ${context} | ||
current-context: ${context} | ||
kind: Config | ||
preferences: {} | ||
users: | ||
- name: ${context} | ||
user: | ||
auth-provider: | ||
config: | ||
cmd-args: config config-helper --format=json | ||
cmd-path: gcloud | ||
expiry-key: '{.credential.token_expiry}' | ||
token-key: '{.credential.access_token}' | ||
name: gcp | ||
`; | ||
}); | ||
|
||
// Export a Kubernetes provider instance that uses our cluster from above. | ||
export const k8sProvider = new k8s.Provider("gkeK8s", { | ||
kubeconfig: k8sConfig, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved. | ||
|
||
import { Config } from "@pulumi/pulumi"; | ||
|
||
const config = new Config(); | ||
|
||
// nodeCount is the number of cluster nodes to provision. Defaults to 3 if unspecified. | ||
export const nodeCount = config.getNumber("nodeCount") || 3; | ||
|
||
// nodeMachineType is the machine type to use for cluster nodes. Defaults to n1-standard-1 if unspecified. | ||
// See https://cloud.google.com/compute/docs/machine-types for more details on available machine types. | ||
export const nodeMachineType = config.get("nodeMachineType") || "n1-standard-1"; | ||
|
||
// username is the admin username for the cluster. | ||
export const username = config.get("username") || "admin"; | ||
|
||
// password is the password for the admin user in the cluster. | ||
export const password = config.require("password"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// Copyright 2016-2018, Pulumi Corporation. All rights reserved. | ||
|
||
import * as k8s from "@pulumi/kubernetes"; | ||
import * as pulumi from "@pulumi/pulumi"; | ||
import { k8sProvider, k8sConfig } from "./cluster"; | ||
|
||
// Create a canary deployment to test that this cluster works. | ||
const name = `${pulumi.getProject()}-${pulumi.getStack()}`; | ||
const canaryLabels = { app: `canary-${name}` }; | ||
const canary = new k8s.apps.v1beta1.Deployment("canary", { | ||
spec: { | ||
selector: { matchLabels: canaryLabels }, | ||
replicas: 1, | ||
template: { | ||
metadata: { labels: canaryLabels }, | ||
spec: { containers: [{ name, image: "nginx" }] }, | ||
}, | ||
}, | ||
}, { provider: k8sProvider }); | ||
|
||
// Export the Kubeconfig so that clients can easily access our cluster. | ||
export let kubeConfig = k8sConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"name": "gcp-ts-gke", | ||
"devDependencies": { | ||
"@types/node": "latest" | ||
}, | ||
"dependencies": { | ||
"@pulumi/gcp": "latest", | ||
"@pulumi/kubernetes": "latest", | ||
"@pulumi/pulumi": "latest" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"compilerOptions": { | ||
"outDir": "bin", | ||
"target": "es6", | ||
"lib": [ | ||
"es6" | ||
], | ||
"module": "commonjs", | ||
"moduleResolution": "node", | ||
"sourceMap": true, | ||
"experimentalDecorators": true, | ||
"pretty": true, | ||
"noFallthroughCasesInSwitch": true, | ||
"noImplicitAny": true, | ||
"noImplicitReturns": true, | ||
"forceConsistentCasingInFileNames": true, | ||
"strictNullChecks": true | ||
}, | ||
"files": [ | ||
"index.ts", | ||
"config.ts", | ||
"cluster.ts" | ||
] | ||
} |