Skip to content

Commit

Permalink
Add "triggering a rollout by updating S3" demo
Browse files Browse the repository at this point in the history
  • Loading branch information
hausdorff committed Sep 12, 2018
1 parent 1c96ca5 commit 2aa00c3
Show file tree
Hide file tree
Showing 15 changed files with 641 additions and 0 deletions.
3 changes: 3 additions & 0 deletions kubernetes-ts-s3-rollout/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
name: data-from-s3
description: A simple Deployment obtaining data from S3
runtime: nodejs
132 changes: 132 additions & 0 deletions kubernetes-ts-s3-rollout/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# Kubernetes: Triggering a rollout of an app by changing data in S3

This example is similar in principle to the [`ConfigMap`-based rollout example][rollout], except a
rollout is triggered any time the data in S3 changes.

Like the `ConfigMap`-based example, this one uses nginx to reverse-proxy traffic to
`pulumi.github.io`. The nginx configuration is contained in the file `default.conf` in this
directory; this program reads that file and puts it into an S3 bucket. Hence, changing data in that
file will cause register as a change in the S3 bucket's data, which will trigger a rollout of the
nginx `Deployment`.

![configmapRollout](images/rollout.gif "ConfigMap-induced Rollout")

## Running the App

Follow the steps in [Pulumi Installation and Setup](https://docs.pulumi.com/install/) and
[Configuring Pulumi Kubernetes](https://docs.pulumi.com/reference/kubernetes.html#configuration) to
get setup with Pulumi and Kubernetes.

Install dependencies:

```sh
npm install
```

Create a new stack:

```sh
$ pulumi stack init
Enter a stack name: s3-kube
```

Perform the deployment:

```sh
$ pulumi up
Updating stack 's3-kube'
Performing changes:

Type Name Status Info
+ pulumi:pulumi:Stack data-from-s3-s3-kube created
+ ├─ aws:s3:Bucket nginx-configs created
+ ├─ aws:s3:BucketPolicy bucketPolicy created
+ ├─ aws:s3:BucketObject default.conf created
+ ├─ kubernetes:apps:Deployment nginx created
+ └─ kubernetes:core:Service nginx created

---outputs:---
defaultConfUrl: "nginx-configs-4b9ea08.s3.amazonaws.com/default.conf"
frontendIp : "35.224.120.207"

info: 6 changes performed:
+ 6 resources created
Update duration: 1m21.870672089s

Permalink: https://app.pulumi.com/hausdorff/s3-kube/updates/1
```

We can see here in the `---outputs:---` section that our proxy was allocated a public IP, in this
case `35.224.120.207"`. It is exported with a stack output variable, `frontendIp`. We can use `curl`
and `grep` to retrieve the `<title>` of the site the proxy points at.

```sh
$ curl -sL $(pulumi stack output frontendIp):80 | grep -C 1 "<title>"

<title>Pulumi. Serverless // Containers // Infrastructure // Cloud // DevOps</title>

```

Now, open `default.conf` and change `.node.server` and `.server.location.proxy_set_header` to point
at `google.com`. If you're on macOS you can run `sed -i bak "s/pulumi.github.io/google.com/g"
default.conf`

The result should look like this:

```conf
upstream node {
server google.com;
}
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
location / {
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header Host google.com;
proxy_pass http:https://node;
proxy_redirect off;
port_in_redirect off;
}
}
```

Running `preview` now shows that this change will cause us to replace the S3 bucket with a new one
containing the new data, and subsequently trigger a rollout in the `Deployment`.

> NOTE: This rollout is safe! Pulumi executes this plan with the following steps:
>
> 1. Create a new S3 bucket with a new name and the new data.
> 1. Update the `PodTemplate` of the `Deployment` to point at the new S3 bucket. This update
> triggers the `Deployment` controller to try to roll out a new set of containers with mounts
> that contain this new data.
> 1. Only once that succeeds, delete the old S3 bucket.
```sh
Previewing update of stack 's3-kube'
Type Name Status Info
* pulumi:pulumi:Stack configmap-rollout-configmap-rollout-dev no change
+- ├─ kubernetes:core:ConfigMap nginx replace changes: ~ data,metadata
~ └─ kubernetes:apps:Deployment nginx update changes: ~ spec

info: 2 changes previewed:
~ 1 resource to update
+-1 resource to replace
2 resources unchanged
```

Running `pulumi up` would actually attempt to achieve these results.

Now, if we `curl` the IP address once more, we see that it points at google.com!

> *Note*: minikube does not support type `LoadBalancer`; if you are deploying to minikube, make sure
> to run `kubectl port-forward svc/frontend 8080:80` to forward the cluster port to the local
> machine and access the service via `localhost:8080`.
```sh
$ curl -sL $(pulumi stack output frontendIp) | grep -o "<title>Google</title>"
<title>Google</title>
```

[rollout]: https://github.com/pulumi/examples/tree/master/kubernetes-ts-configmap-rollout
51 changes: 51 additions & 0 deletions kubernetes-ts-s3-rollout/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as fs from "fs";

import * as aws from "@pulumi/aws";
import * as pulumi from "@pulumi/pulumi";
import * as mime from "mime";

// Create a bucket and expose a website index document
let siteBucket = new aws.s3.Bucket("s3-website-bucket", {
website: {
indexDocument: "index.html"
}
});

let siteDir = "www"; // directory for content files

// For each file in the directory, create an S3 object stored in `siteBucket`
for (let item of fs.readdirSync(siteDir)) {
let filePath = require("path").join(siteDir, item);
let object = new aws.s3.BucketObject(item, {
bucket: siteBucket, // reference the s3.Bucket object
source: new pulumi.asset.FileAsset(filePath), // use FileAsset to point to a file
contentType: mime.getType(filePath) || undefined // set the MIME type of the file
});
}

// Create an S3 Bucket Policy to allow public read of all objects in bucket
function publicReadPolicyForBucket(bucketName) {
return JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Principal: "*",
Action: ["s3:GetObject"],
Resource: [
`arn:aws:s3:::${bucketName}/*` // policy refers to bucket name explicitly
]
}
]
});
}

// Set the access policy for the bucket so all objects are readable
let bucketPolicy = new aws.s3.BucketPolicy("bucketPolicy", {
bucket: siteBucket.bucket, // refer to the bucket created earlier
policy: siteBucket.bucket.apply(publicReadPolicyForBucket) // use output property `siteBucket.bucket`
});

// Stack exports
export const bucketName = siteBucket.bucket;
export const websiteUrl = siteBucket.websiteEndpoint;
16 changes: 16 additions & 0 deletions kubernetes-ts-s3-rollout/default.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
upstream node {
server google.com;
}
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
location / {
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header Host google.com;
proxy_pass http:https://node;
proxy_redirect off;
port_in_redirect off;
}
}
Loading

0 comments on commit 2aa00c3

Please sign in to comment.