Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DevOps - Orion deployment using Kubernetes and Pulumi #3065

Merged
merged 3 commits into from
Jan 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions devops/kubernetes/orion/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/bin/
/node_modules/
kubeconfig*
package-lock.json
Pulumi.*.yaml
25 changes: 25 additions & 0 deletions devops/kubernetes/orion/Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: orion
runtime: nodejs
description: A Pulumi program to deploy Orion service to Kubernetes
template:
config:
aws:profile:
default: joystream-user
aws:region:
default: us-east-1
isMinikube:
description: Whether you are deploying to minikube
default: false
queryNodeEndpoint:
description: Full URL for Query node endpoint
isLoadBalancerReady:
description: Whether the load balancer service is ready and has been assigned an IP
default: false
storage:
description: Amount of storage in gigabytes for ipfs volume
default: 40
orionImage:
description: The Orion image to use for running the orion node
default: joystream/orion:latest
contentSecret:
description: Orion featured content secret
119 changes: 119 additions & 0 deletions devops/kubernetes/orion/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Amazon Kubernetes Cluster: Orion

Deploy Orion to a Kubernetes cluster

## 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. [Configure AWS Credentials](https://www.pulumi.com/docs/intro/cloud-providers/aws/setup/)
1. Optional (for debugging): [Install kubectl](https://kubernetes.io/docs/tasks/tools/)

### 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 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 configuration variables in `Pulumi.<stack>.yaml`

```bash
$ pulumi config set-all --plaintext queryNodeEndpoint='http://host.minikube.internal:8081/graphql' \
--plaintext isMinikube=true --plaintext orionImage='joystream/orion:latest' \
--plaintext contentSecret='password123' \
--plaintext aws:region=us-east-1 --plaintext aws:profile=joystream-user
```

If you want to build the stack on AWS set the `isMinikube` config to `false`

```bash
$ pulumi config set isMinikube false
```

You can also set the `storage` config parameter if required. Check `Pulumi.yaml` file for additional parameters.

1. Stand up the EKS cluster:

Running `pulumi up -y` will deploy the EKS cluster. Note, provisioning a
new EKS cluster takes between 10-15 minutes.

1. Once the stack if up and running, we will modify the Caddy config to get SSL certificate for the load balancer

Modify the config variable `isLoadBalancerReady`

```bash
$ pulumi config set isLoadBalancerReady true
```

Run `pulumi up -y` to update the Caddy config

1. Access the Kubernetes Cluster using `kubectl`

To access your new Kubernetes cluster using `kubectl`, we need to set up 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 --show-secrets > kubeconfig
$ export KUBECONFIG=$PWD/kubeconfig
$ kubectl get nodes
```

We can also use the stack output to query the cluster for our newly created Deployment:

```bash
$ kubectl get deployment $(pulumi stack output deploymentName) --namespace=$(pulumi stack output namespaceName)
$ kubectl get service $(pulumi stack output serviceName) --namespace=$(pulumi stack output namespaceName)
```

To get logs

```bash
$ kubectl config set-context --current --namespace=$(pulumi stack output namespaceName)
$ kubectl get pods
$ kubectl logs <PODNAME> --all-containers
```

To run a command on a pod

```bash
$ kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1}
```

To see complete pulumi stack output

```bash
$ pulumi stack output
```

To execute a command

```bash
$ kubectl exec --stdin --tty <PODNAME> -c colossus -- /bin/bash
```

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

```bash
$ pulumi destroy --yes
$ pulumi stack rm --yes
```
154 changes: 154 additions & 0 deletions devops/kubernetes/orion/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import * as awsx from '@pulumi/awsx'
import * as eks from '@pulumi/eks'
import * as k8s from '@pulumi/kubernetes'
import * as pulumi from '@pulumi/pulumi'
import { CaddyServiceDeployment } from 'pulumi-common'
import { MongoDBServiceDeployment } from './mongo'

const awsConfig = new pulumi.Config('aws')
const config = new pulumi.Config()

const name = 'orion'

const queryNodeHost = config.require('queryNodeEndpoint')
const lbReady = config.get('isLoadBalancerReady') === 'true'
const orionImage = config.get('orionImage') || `joystream/orion:latest`
const contentSecret = config.require('contentSecret')
const storage = parseInt(config.get('storage') || '40')
const isMinikube = config.getBoolean('isMinikube')

export let kubeconfig: pulumi.Output<any>
let provider: k8s.Provider

if (isMinikube) {
provider = new k8s.Provider('local', {})
} else {
// Create a VPC for our cluster.
const vpc = new awsx.ec2.Vpc('orion-vpc', { numberOfAvailabilityZones: 2, numberOfNatGateways: 1 })

// Create an EKS cluster with the default configuration.
const cluster = new eks.Cluster('eksctl-orion-node', {
vpcId: vpc.id,
subnetIds: vpc.publicSubnetIds,
instanceType: 't2.medium',
providerCredentialOpts: {
profileName: awsConfig.get('profile'),
},
})
provider = cluster.provider

// Export the cluster's kubeconfig.
kubeconfig = cluster.kubeconfig
}

const resourceOptions = { provider: provider }

// Create a Kubernetes Namespace
const ns = new k8s.core.v1.Namespace(name, {}, resourceOptions)

// Export the Namespace name
export const namespaceName = ns.metadata.name

const appLabels = { appClass: name }

const mongoDb = new MongoDBServiceDeployment(
'mongo-db',
{
namespaceName: namespaceName,
storage: storage,
},
resourceOptions
)

// Create a Deployment
const deployment = new k8s.apps.v1.Deployment(
name,
{
metadata: {
namespace: namespaceName,
labels: appLabels,
},
spec: {
replicas: 1,
selector: { matchLabels: appLabels },
template: {
metadata: {
labels: appLabels,
},
spec: {
containers: [
{
name: 'orion',
image: orionImage,
imagePullPolicy: 'IfNotPresent',
env: [
{
name: 'ORION_PORT',
value: '6116',
},
{
name: 'ORION_MONGO_HOSTNAME',
value: mongoDb.service.metadata.name,
},
{
name: 'ORION_FEATURED_CONTENT_SECRET',
value: contentSecret,
},
{
name: 'ORION_QUERY_NODE_URL',
value: queryNodeHost,
},
],
ports: [{ containerPort: 6116 }],
},
],
},
},
},
},
resourceOptions
)

// Create a LoadBalancer Service for the Deployment
const service = new k8s.core.v1.Service(
name,
{
metadata: {
labels: appLabels,
namespace: namespaceName,
name: 'orion-node',
},
spec: {
type: isMinikube ? 'NodePort' : 'ClusterIP',
ports: [{ name: 'port-1', port: 6116 }],
selector: appLabels,
},
},
resourceOptions
)

// Export the Service name
export const serviceName = service.metadata.name

// Export the Deployment name
export const deploymentName = deployment.metadata.name

const caddyEndpoints = [
` {
reverse_proxy orion-node:6116
}`,
]

export let endpoint1: pulumi.Output<string> = pulumi.interpolate``
export let endpoint2: pulumi.Output<string> = pulumi.interpolate``

if (!isMinikube) {
const caddy = new CaddyServiceDeployment(
'caddy-proxy',
{ lbReady, namespaceName: namespaceName, caddyEndpoints },
resourceOptions
)

endpoint1 = pulumi.interpolate`${caddy.primaryEndpoint}`
endpoint2 = pulumi.interpolate`${caddy.secondaryEndpoint}`
}
Loading