Skip to content

Commit

Permalink
Merge pull request pulumi#749 from pulumi/gke-sa-example
Browse files Browse the repository at this point in the history
add a gke + service account example
  • Loading branch information
jaxxstorm committed Jul 24, 2020
2 parents 9efa662 + d6d76f3 commit a555402
Show file tree
Hide file tree
Showing 5 changed files with 313 additions and 0 deletions.
12 changes: 12 additions & 0 deletions gcp-ts-gke-serviceaccount/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
name: gcp-ts-gke-serviceaccount
description: A Google Kubernetes Engine (GKE) + Service account example
runtime: nodejs
template:
config:
gcp:project:
description: The Google Cloud project to deploy into
gcp:zone:
description: The Google Cloud zone
gcp:credentials:
description: Your GCP Service Account key contents for GKE Administration
secret: true
119 changes: 119 additions & 0 deletions gcp-ts-gke-serviceaccount/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)

# Google Kubernetes Engine (GKE) Cluster with Service Account

This example deploys an Google Cloud Platform (GCP) [Google Kubernetes Engine (GKE)](https://cloud.google.com/kubernetes-engine/) cluster, and deploys an example application that consumes a PubSub topic. The cluster has a secret which contains [Google Cloud Service Account Credentials](https://cloud.google.com/iam/docs/service-accounts)

## Deploying the App

To deploy your infrastructure, follow the below steps.

### Prerequisites

1. [Install Pulumi](https://www.pulumi.com/docs/get-started/install/)
1. [Install Node.js](https://nodejs.org/en/download/)
1. Install a package manager for Node.js, such as [npm](https://www.npmjs.com/get-npm) or [Yarn](https://yarnpkg.com/en/docs/install).
1. [Install Google Cloud SDK (`gcloud`)](https://cloud.google.com/sdk/docs/downloads-interactive)
1. Configure GCP Auth

* Login using `gcloud`

```bash
$ gcloud auth login
$ gcloud config set project <YOUR_GCP_PROJECT_HERE>
$ gcloud auth application-default login
```
> Note: This auth mechanism is meant for inner loop developer
> workflows. If you want to run this example in an unattended service
> account setting, such as in CI/CD, please [follow instructions to
> configure your service account](https://www.pulumi.com/docs/intro/cloud-providers/gcp/setup/). The
> service account must have the role `Kubernetes Engine Admin` / `container.admin`.
### Steps

After cloning this repo, from this working directory, run these commands:

1. Install the required Node.js packages:

This installs the dependent packages [needed](https://www.pulumi.com/docs/intro/concepts/how-pulumi-works/) for our Pulumi program.

```bash
$ npm install
```

1. Create a new Pulumi stack, which is an isolated deployment target for this example:

This will initialize the Pulumi program in TypeScript.

```bash
$ pulumi stack init
```

1. Set the required GCP configuration variables:

This sets configuration options and default values for our cluster.

```bash
$ pulumi config set gcp:project <YOUR_GCP_PROJECT_HERE>
$ pulumi config set gcp:zone us-west1-a // any valid GCP Zone here
```

1. Set some optional configuration variables (note, these values are optional and have defaults set):

```bash
$ pulumi config set name <NAME>
$ pulumi config set machineType n1-standard-1
```

1. Stand up the GKE cluster:

To preview and deploy changes, run `pulumi update` and select "yes."

The `update` sub-command shows a preview of the resources that will be created
and prompts on whether to proceed with the deployment. Note that the stack
itself is counted as a resource, though it does not correspond
to a physical cloud resource.

You can also run `pulumi up --diff` to see and inspect the diffs of the
overall changes expected to take place.

Running `pulumi up` will deploy the GKE cluster. Note, provisioning a
new GKE cluster takes between 3-5 minutes.

```bash

```

1. After 3-5 minutes, your cluster will be ready, and the kubeconfig YAML you'll use to connect to the cluster will
be available as an output.

1. Access the Kubernetes Cluster using `kubectl`

To access your new Kubernetes cluster using `kubectl`, we need to setup the
`kubeconfig` file and download `kubectl`. We can leverage the Pulumi
stack output in the CLI, as Pulumi facilitates exporting these objects for us.

```bash
$ pulumi stack output kubeconfig > kubeconfig
$ export KUBECONFIG=$PWD/kubeconfig
$ kubectl version
$ kubectl cluster-info
$ kubectl get nodes
```

1. Verify the pubsub example is working

The pubsub deployment should be running, you can check it by examining the logs:

```bash
k logs -n pubsub -l appClass=pubsub
Pulling messages from Pub/Sub subscription...
```

1. Once you've finished, tear down your stack's resources by destroying and removing it:

```bash
$ pulumi destroy --yes
$ pulumi stack rm --yes
```

153 changes: 153 additions & 0 deletions gcp-ts-gke-serviceaccount/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright 2016-2019, Pulumi Corporation. All rights reserved.
import * as gcp from "@pulumi/gcp";
import * as k8s from "@pulumi/kubernetes";
import * as pulumi from "@pulumi/pulumi";

const config = new pulumi.Config();

const name = config.get("name") ||
"gke-serviceaccount-example";
export const masterVersion = config.get("masterVersion") ||
gcp.container.getEngineVersions().then(it => it.latestMasterVersion);
const machineType = "n1-standard-1" || config.get("machineType");

// Create a service account
const serviceAccount = new gcp.serviceAccount.Account("serviceAccount", {
accountId: name,
displayName: "A service account for a GKE application",
});

const serviceAccountIAM = new gcp.projects.IAMBinding("serviceAccount-pub", {
role: "roles/pubsub.subscriber",
members: [pulumi.interpolate`serviceAccount:${serviceAccount.email}`],
}, {parent: serviceAccount});

const serviceAccountKey = new gcp.serviceAccount.Key("serviceAccount-key", {
serviceAccountId: serviceAccount.name,
publicKeyType: "TYPE_X509_PEM_FILE",
}, {parent: serviceAccount, additionalSecretOutputs: ["privateKey"]});

// Create a GKE cluster
const cluster = new gcp.container.Cluster(name, {
initialNodeCount: 2,
minMasterVersion: masterVersion,
nodeVersion: masterVersion,
nodeConfig: {
machineType: machineType,
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",
],
},
});

// Export the Cluster name
export const clusterName = cluster.name;

// 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 kubeconfig = pulumi.all([cluster.name, cluster.endpoint, cluster.masterAuth]).apply(([name, endpoint, masterAuth]) => {
const context = `${gcp.config.project}_${gcp.config.zone}_${name}`;
return `apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ${masterAuth.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
`;
});

// Create a Kubernetes provider instance that uses our cluster from above.
const clusterProvider = new k8s.Provider(name, {
kubeconfig: kubeconfig,
});

const appLabels = {appClass: "pubsub"};

// Create a Kubernetes Namespace
const ns = new k8s.core.v1.Namespace("pubsub-ns", {
metadata: {
name: "pubsub",
labels: appLabels,
},
}, {provider: clusterProvider});


const gcpCredentials = new k8s.core.v1.Secret("gcp-credentials", {
metadata: {
namespace: ns.metadata.name,
labels: appLabels,
},
type: "Opaque",
stringData: {
"gcp-credentials.json": serviceAccountKey.privateKey.apply((x) => Buffer.from(x, "base64").toString("utf8")),
},
}, {provider: clusterProvider, parent: ns});

const deployment = new k8s.apps.v1.Deployment("pubsub",
{
metadata: {
namespace: ns.metadata.name,
labels: appLabels,
},
spec: {
replicas: 1,
selector: {matchLabels: appLabels},
template: {
metadata: {
labels: appLabels,
},
spec: {
volumes: [
{
name: "google-cloud-key",
secret: {
secretName: gcpCredentials.metadata.name,
},
},
],
containers: [
{
name: "pubsub-example",
image: "gcr.io/google-samples/pubsub-sample:v1",
volumeMounts: [
{
name: "google-cloud-key",
mountPath: "/var/secrets/google",
},
],
env: [{
name: "GOOGLE_APPLICATION_CREDENTIALS",
value: "/var/secrets/google/gcp-credentials.json",
}],
},
],
},
},
},
},
{
provider: clusterProvider, parent: ns,
},
);
11 changes: 11 additions & 0 deletions gcp-ts-gke-serviceaccount/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "gke.typescript",
"devDependencies": {
"@types/node": "^10.0.0"
},
"dependencies": {
"@pulumi/gcp": "^3.0.0",
"@pulumi/kubernetes": "^2.4.0",
"@pulumi/pulumi": "^2.0.0"
}
}
18 changes: 18 additions & 0 deletions gcp-ts-gke-serviceaccount/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"strict": true,
"outDir": "bin",
"target": "es2016",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.ts"
]
}

0 comments on commit a555402

Please sign in to comment.