Skip to content

Commit 5bfd4ed

Browse files
authored
Add a Python version of our GKE example (#272)
1 parent 97a4edf commit 5bfd4ed

File tree

5 files changed

+190
-0
lines changed

5 files changed

+190
-0
lines changed

gcp-py-gke/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/bin/

gcp-py-gke/Pulumi.yaml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name: gcp-py-gke
2+
description: A Google Kubernetes Engine (GKE) cluster, with canary deployment
3+
runtime: python

gcp-py-gke/README.md

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)
2+
3+
# Google Kubernetes Engine (GKE) with a Canary Deployment
4+
5+
This example provisions a [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine/) cluster, using
6+
infrastructure-as-code, and then deploys a Kubernetes Deployment into it, to test that the cluster is working. This
7+
demonstrates that you can manage both the Kubernetes objects themselves, in addition to underlying cloud infrastructure,
8+
using a single configuration language (in this case, Python), tool, and workflow.
9+
10+
# Prerequisites
11+
12+
Ensure you have [Python 3](https://www.python.org/downloads/) and [the Pulumi CLI](https://pulumi.io/install).
13+
14+
We will be deploying to Google Cloud Platform (GCP), so you will need an account. If you don't have an account,
15+
[sign up for free here](https://cloud.google.com/free/). In either case,
16+
[follow the instructions here](https://pulumi.io/quickstart/gcp/setup.html) to connect Pulumi to your GCP account.
17+
18+
This example assumes that you have GCP's `gcloud` CLI on your path. This is installed as part of the
19+
[GCP SDK](https://cloud.google.com/sdk/).
20+
21+
# Running the Example
22+
23+
After cloning this repo, `cd` into it and run these commands. A GKE Kubernetes cluster will appear!
24+
25+
1. Create a new stack, which is an isolated deployment target for this example:
26+
27+
```bash
28+
$ pulumi stack init dev
29+
```
30+
31+
2. Set the required configuration variables for this program:
32+
33+
```bash
34+
$ pulumi config set gcp:project [your-gcp-project-here]
35+
$ pulumi config set gcp:zone us-west1-a # any valid GCP zone here
36+
$ pulumi config set password --secret [your-cluster-password-here]
37+
```
38+
39+
By default, your cluster will have 3 nodes of type `n1-standard-1`. This is configurable, however; for instance
40+
if we'd like to choose 5 nodes of type `n1-standard-2` instead, we can run these commands:
41+
42+
```bash
43+
$ pulumi config set node_count 5
44+
$ pulumi config set node_machine_type n1-standard-2
45+
```
46+
47+
This shows how stacks can be configurable in useful ways. You can even change these after provisioning.
48+
49+
3. Deploy everything with the `pulumi up` command. This provisions all the GCP resources necessary, including
50+
your GKE cluster itself, and then deploys a Kubernetes Deployment running nginx, all in a single gesture:
51+
52+
```bash
53+
$ pulumi up
54+
```
55+
56+
This will show you a preview, ask for confirmation, and then chug away at provisioning your cluster:
57+
58+
```
59+
Updating stack 'gcp-ts-gke-dev'
60+
Performing changes:
61+
62+
Type Name Plan
63+
+ pulumi:pulumi:Stack gcp-py-dev create
64+
+ ├─ gcp:container:Cluster gke-cluster create
65+
+ ├─ pulumi:providers:kubernetes gkeK8s create
66+
+ └─ kubernetes:apps:Deployment canary create
67+
68+
---outputs:---
69+
kubeConfig: "apiVersion: v1\n..."
70+
71+
info: 4 changes updated:
72+
+ 4 resources created
73+
Update duration: 2m07.424737735s
74+
```
75+
76+
After about two minutes, your cluster will be ready, and its config will be printed.
77+
78+
4. From here, you may take this config and use it either in your `~/.kube/config` file, or just by saving it
79+
locally and plugging it into the `KUBECONFIG` envvar. All of your usual `gcloud` commands will work too, of course.
80+
81+
For instance:
82+
83+
```bash
84+
$ pulumi stack output kubeconfig > kubeconfig.yaml
85+
$ KUBECONFIG=./kubeconfig.yaml kubectl get po
86+
NAME READY STATUS RESTARTS AGE
87+
canary-n7wfhtrp-fdbfd897b-lrm58 1/1 Running 0 58s
88+
```
89+
90+
5. At this point, you have a running cluster. Feel free to modify your program, and run `pulumi up` to redeploy changes.
91+
The Pulumi CLI automatically detects what has changed and makes the minimal edits necessary to accomplish these
92+
changes. This could be altering the existing chart, adding new GCP or Kubernetes resources, or anything, really.
93+
94+
6. Once you are done, you can destroy all of the resources, and the stack:
95+
96+
```bash
97+
$ pulumi destroy
98+
$ pulumi stack rm
99+
```

gcp-py-gke/__main__.py

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
from pulumi import Config, export, get_project, get_stack, Output, ResourceOptions
2+
from pulumi_gcp.config import project, zone
3+
from pulumi_gcp.container import Cluster
4+
from pulumi_kubernetes import Provider
5+
from pulumi_kubernetes.apps.v1 import Deployment
6+
7+
# Read in some configurable settings for our cluster:
8+
config = Config(None)
9+
10+
# nodeCount is the number of cluster nodes to provision. Defaults to 3 if unspecified.
11+
NODE_COUNT = config.get('node_count') or 3
12+
# nodeMachineType is the machine type to use for cluster nodes. Defaults to n1-standard-1 if unspecified.
13+
# See https://cloud.google.com/compute/docs/machine-types for more details on available machine types.
14+
NODE_MACHINE_TYPE = config.get('node_machine_type') or 'n1-standard-1'
15+
# username is the admin username for the cluster.
16+
USERNAME = config.get('username') or 'admin'
17+
# password is the password for the admin user in the cluster.
18+
PASSWORD = config.require('password')
19+
20+
# Now, actually create the GKE cluster.
21+
k8s_cluster = Cluster('gke-cluster',
22+
initial_node_count=NODE_COUNT,
23+
node_version='latest',
24+
min_master_version='latest',
25+
master_auth={ 'username': USERNAME, 'password': PASSWORD },
26+
node_config={
27+
'machine_type': NODE_MACHINE_TYPE,
28+
'oauth_scopes': [
29+
'https://www.googleapis.com/auth/compute',
30+
'https://www.googleapis.com/auth/devstorage.read_only',
31+
'https://www.googleapis.com/auth/logging.write',
32+
'https://www.googleapis.com/auth/monitoring'
33+
],
34+
},
35+
)
36+
37+
# Manufacture a GKE-style Kubeconfig. Note that this is slightly "different" because of the way GKE requires
38+
# gcloud to be in the picture for cluster authentication (rather than using the client cert/key directly).
39+
k8s_info = Output.all(k8s_cluster.name, k8s_cluster.endpoint, k8s_cluster.master_auth)
40+
k8s_config = k8s_info.apply(
41+
lambda info: """apiVersion: v1
42+
clusters:
43+
- cluster:
44+
certificate-authority-data: {0}
45+
server: https://{1}
46+
name: {2}
47+
contexts:
48+
- context:
49+
cluster: {2}
50+
user: {2}
51+
name: {2}
52+
current-context: {2}
53+
kind: Config
54+
preferences: {{}}
55+
users:
56+
- name: {2}
57+
user:
58+
auth-provider:
59+
config:
60+
cmd-args: config config-helper --format=json
61+
cmd-path: gcloud
62+
expiry-key: '{{.credential.token_expiry}}'
63+
token-key: '{{.credential.access_token}}'
64+
name: gcp
65+
""".format(info[2]['clusterCaCertificate'], info[1], '{0}_{1}_{2}'.format(project, zone, info[0])))
66+
67+
# Make a Kubernetes provider instance that uses our cluster from above.
68+
k8s_provider = Provider('gke_k8s', kubeconfig=k8s_config)
69+
70+
# Create a canary deployment to test that this cluster works.
71+
labels = { 'app': 'canary-{0}-{1}'.format(get_project(), get_stack()) }
72+
canary = Deployment('canary',
73+
spec={
74+
'selector': { 'matchLabels': labels },
75+
'replicas': 1,
76+
'template': {
77+
'metadata': { 'labels': labels },
78+
'spec': { 'containers': [{ 'name': 'nginx', 'image': 'nginx' }] },
79+
},
80+
}, __opts__=ResourceOptions(provider=k8s_provider)
81+
)
82+
83+
# Finally, export the kubeconfig so that the client can easily access the cluster.
84+
export('kubeconfig', k8s_config)

gcp-py-gke/requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pulumi>=0.17.4
2+
pulumi_gcp>=0.18.2
3+
pulumi_kubernetes>=0.22.0

0 commit comments

Comments
 (0)