├── .gitignore ├── docker-compose ├── slackin │ └── Dockerfile ├── docker-compose.yml └── README.md ├── terraform ├── terraform.tfstate.backup ├── variables.tf ├── dnsimple.tf ├── README.md ├── docker.tf ├── digitalocean.tf └── terraform.tfstate └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore sensitve variable files 2 | *.env 3 | *.tfvars 4 | *.tfplan -------------------------------------------------------------------------------- /docker-compose/slackin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:4.2 2 | 3 | RUN mkdir /home/slackin 4 | WORKDIR /home/slackin 5 | RUN npm install -g slackin 6 | CMD slackin --silent --port $PORT $SLACK_ORG $SLACK_TOKEN -------------------------------------------------------------------------------- /terraform/terraform.tfstate.backup: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "serial": 6, 4 | "modules": [ 5 | { 6 | "path": [ 7 | "root" 8 | ], 9 | "outputs": {}, 10 | "resources": {} 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "digitalocean_token" {} 2 | variable "digitalocean_ssh_fingerprint" {} 3 | variable "digitalocean_region" { 4 | default = "tor1" 5 | } 6 | variable "dnsimple_token" {} 7 | variable "dnsimple_email" {} 8 | variable "dnsimple_domain" { 9 | default = "feathersjs.com" 10 | } 11 | variable "slack_token" {} -------------------------------------------------------------------------------- /docker-compose/docker-compose.yml: -------------------------------------------------------------------------------- 1 | nginx: 2 | image: jwilder/nginx-proxy 3 | container_name: nginx-proxy 4 | ports: 5 | - "80:80" 6 | - "443:443" 7 | volumes: 8 | - /var/run/docker.sock:/tmp/docker.sock:ro 9 | 10 | slackin: 11 | build: ./slackin 12 | container_name: feathers-slackin 13 | env_file: .env 14 | environment: 15 | - VIRTUAL_HOST=slack.feathersjs.com 16 | - PORT=3000 17 | ports: 18 | - "3000:3000" 19 | 20 | demo: 21 | image: ekryski/feathers-demo 22 | container_name: feathers-demo 23 | environment: 24 | - VIRTUAL_HOST=demo.feathersjs.com,todos.feathersjs.com 25 | - PORT=8080 26 | ports: 27 | - "8080:8080" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # feathers-devops-playground 2 | 3 | > A playground for Feathers infrastructure configurations 4 | 5 | This repos contains test/example configurations for deploying our Feathers infrastructure. It's not really for public consumption but we open source all the things in case someone finds this useful. It is **not** published to npm as a module. 6 | 7 | 8 | ## Consul 9 | 10 | If you run into this error: 11 | 12 | ```sh 13 | Error starting agent: Failed to get advertise address: Multiple private IPs found. Please configure one. 14 | ``` 15 | 16 | You probably have a VM running. In my case I had my docker-machine instance running on OS X so I need to be explicit that I want to bind to my OS X host. 17 | 18 | `consul agent -server -bind 127.0.0.1 -bootstrap-expect 1 -node ek-macbook-pro -data-dir /tmp/consul` 19 | -------------------------------------------------------------------------------- /terraform/dnsimple.tf: -------------------------------------------------------------------------------- 1 | # Configure the DNSimple provider 2 | provider "dnsimple" { 3 | token = "${var.dnsimple_token}" 4 | email = "${var.dnsimple_email}" 5 | } 6 | 7 | # Create A records 8 | resource "dnsimple_record" "feathers-demo" { 9 | domain = "${var.dnsimple_domain}" 10 | name = "demo" 11 | value = "${digitalocean_droplet.feathers.ipv4_address}" 12 | type = "A" 13 | ttl = 3600 14 | } 15 | 16 | resource "dnsimple_record" "feathers-todo" { 17 | domain = "${var.dnsimple_domain}" 18 | name = "todos" 19 | value = "${digitalocean_droplet.feathers.ipv4_address}" 20 | type = "A" 21 | ttl = 3600 22 | } 23 | 24 | resource "dnsimple_record" "feathers-slack" { 25 | domain = "${var.dnsimple_domain}" 26 | name = "slack" 27 | value = "${digitalocean_droplet.feathers.ipv4_address}" 28 | type = "A" 29 | ttl = 3600 30 | } -------------------------------------------------------------------------------- /docker-compose/README.md: -------------------------------------------------------------------------------- 1 | # Docker Compose Playground 2 | 3 | This provisions our infrastructure for Feathers using docker compose. This can be deployed locally or on a remote instance. Currently it sets up: 4 | 5 | - [Slackin](https://github.com/rauchg/slackin) 6 | - Our [Todo demo app](https://github.com/feathersjs/feathers-demo) 7 | - [An Nginx proxy](https://github.com/jwilder/nginx-proxy/) 8 | 9 | ## Getting Started 10 | 11 | ### On OSX 12 | 13 | 1. Make sure you have [docker installed](https://docs.docker.com/engine/installation/mac/) 14 | 2. Run `docker-machine create --driver virtualbox default` 15 | 3. Run `docker-machine env default` 16 | 4. Run `eval "$(docker-machine env default)"` 17 | 5. Get the `.env` file that contains the secrets 18 | 6. Run `docker-compose up -d` 19 | 20 | ## Provisioning 21 | 22 | Simply run `docker-compose up -d`. This will start all the instances in **detached** mode. You can use `docker-compose ps` to list the running containers. 23 | 24 | ## Changing 25 | 26 | Alter your `docker-compose.yml` file and go through the provisioning process. 27 | 28 | ## Destroying 29 | 30 | Running this sequence will stop all containers and then remove all of them. 31 | 32 | 1. `docker-compose kill` 33 | 2. `docker-compose rm` -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | # Terraform Playground 2 | 3 | This provisions our infrastructure on Digital Ocean for Feathers and sets up the DNS records. Currently it sets up: 4 | 5 | - [Slackin](https://github.com/rauchg/slackin) 6 | - Our [Todo demo app](https://github.com/feathersjs/feathers-demo) 7 | - [An Nginx proxy](https://github.com/jwilder/nginx-proxy/) 8 | 9 | ## Getting Started 10 | 11 | 1. run `brew install terraform` 12 | 2. Get the `terraform.tfvars` file that contains the secrets 13 | 3. Run `terraform apply` 14 | 15 | ## Provisioning 16 | 17 | To see a preview of what will happen run `terraform plan`. 18 | 19 | To apply our plan run `terraform apply`. 20 | 21 | ## Changing 22 | 23 | Alter your file and go through the provisioning process. 24 | 25 | ### Getting SSH Key Ids 26 | 27 | You need to get your SSH ID from Digital Ocean in order to use Terraform to provision machines. You can do this by running: 28 | 29 | `curl -X GET -H "Content-Type: application/json" -H "Authorization: Bearer your_api_token" "https://api.digitalocean.com/v2/account/keys"` 30 | 31 | > Alternatively you can [upload a new key](https://terraform.io/docs/providers/do/r/ssh_key.html) to Digital Ocean and use that. 32 | 33 | ### Digital Ocean Regions 34 | 35 | You need to specify the digital ocean region you want to deploy. Currently these are the short forms: 36 | 37 | - `nyc1` 38 | - `nyc2` 39 | - `nyc3` 40 | - `ams1` 41 | - `ams2` 42 | - `ams3` 43 | - `sfo1` 44 | - `sgp1` 45 | - `lon1` 46 | - `fra1` 47 | - `tor1` 48 | 49 | ## Destroying 50 | 51 | `terraform destroy` -------------------------------------------------------------------------------- /terraform/docker.tf: -------------------------------------------------------------------------------- 1 | ############################### 2 | # This is currently not used! 3 | ############################### 4 | 5 | # Configure the Docker provider 6 | #provider "docker" { 7 | # host = "https://hub.docker.com/" 8 | #} 9 | # 10 | ## Configure the Docker images. 11 | #resource "docker_image" "nginx-proxy" { 12 | # name = "jwilder/nginx-proxy:latest" 13 | #} 14 | # 15 | #resource "docker_image" "slackin" { 16 | # name = "chk1/slackin:latest" 17 | #} 18 | # 19 | #resource "docker_image" "feathers-demo" { 20 | # name = "ekryski/feathers-demo:latest" 21 | #} 22 | # 23 | ## Configure the Docker containers 24 | #resource "docker_container" "nginx-proxy" { 25 | # name = "nginx-proxy" 26 | # image = "${docker_image.nginx-proxy.latest}" 27 | # ports { 28 | # internal = 80 29 | # external = 80 30 | # } 31 | # volumes { 32 | # host_path = "/var/run/docker.sock" 33 | # container_path = "/tmp/docker.sock" 34 | # read_only = true 35 | # } 36 | #} 37 | # 38 | #resource "docker_container" "slackin" { 39 | # name = "slackin" 40 | # image = "${docker_image.slackin.latest}" 41 | # ports { 42 | # internal = 3000 43 | # external = 3000 44 | # } 45 | # env = ["VIRTUAL_HOST=slack.feathersjs.com", "SLACK_ORG=feathersjs", #"SLACK_TOKEN="] 46 | #} 47 | # 48 | #resource "docker_container" "feathers-demo" { 49 | # name = "feathers-demo" 50 | # image = "${docker_image.feathers-demo.latest}" 51 | # ports { 52 | # internal = 8080 53 | # external = 8080 54 | # } 55 | # env = ["VIRTUAL_HOST=demo.feathersjs.com", "PORT=8080"] 56 | #} -------------------------------------------------------------------------------- /terraform/digitalocean.tf: -------------------------------------------------------------------------------- 1 | # Configure the Digital Ocean provider 2 | provider "digitalocean" { 3 | token = "${var.digitalocean_token}" 4 | } 5 | 6 | # Create a single droplet 7 | resource "digitalocean_droplet" "feathers" { 8 | image = "docker" 9 | name = "feathers" 10 | region = "${var.digitalocean_region}" 11 | size = "512mb" 12 | # size = "1gb" 13 | ssh_keys = ["${var.digitalocean_ssh_fingerprint}"] 14 | 15 | connection { 16 | user = "root" 17 | private_key = "${file("/Users/eric/.ssh/id_rsa")}" 18 | } 19 | 20 | # Configure Docker containers 21 | provisioner "remote-exec" { 22 | inline = [ 23 | "docker run --name nginx-proxy -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy", 24 | "docker run --name slackin -d -p 3000:3000 -e VIRTUAL_HOST=slack.feathersjs.com -e SLACK_ORG=feathersjs -e SLACK_TOKEN=${var.slack_token} chk1/slackin", 25 | "docker run --name feathers-demo -d -p 8080:8080 -e PORT=8080 -e VIRTUAL_HOST=demo.feathersjs.com ekryski/feathers-demo" 26 | ] 27 | } 28 | } 29 | 30 | # Define our outputs to be shown once provisioning is complete 31 | output "ipv4_address" { 32 | value = "${digitalocean_droplet.feathers.ipv4_address}" 33 | } 34 | 35 | output "ipv4_address_private" { 36 | value = "${digitalocean_droplet.feathers.ipv4_address_private}" 37 | } 38 | 39 | output "ipv6_address" { 40 | value = "${digitalocean_droplet.feathers.ipv6_address}" 41 | } 42 | 43 | output "ipv6_address_private" { 44 | value = "${digitalocean_droplet.feathers.ipv6_address_private}" 45 | } 46 | 47 | output "locked" { 48 | value = "${digitalocean_droplet.feathers.locked}" 49 | } 50 | 51 | output "status" { 52 | value = "${digitalocean_droplet.feathers.status}" 53 | } -------------------------------------------------------------------------------- /terraform/terraform.tfstate: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "serial": 7, 4 | "modules": [ 5 | { 6 | "path": [ 7 | "root" 8 | ], 9 | "outputs": { 10 | "ipv4_address": "159.203.43.160", 11 | "locked": "false", 12 | "status": "active" 13 | }, 14 | "resources": { 15 | "digitalocean_droplet.feathers": { 16 | "type": "digitalocean_droplet", 17 | "primary": { 18 | "id": "9918648", 19 | "attributes": { 20 | "id": "9918648", 21 | "image": "docker", 22 | "ipv4_address": "159.203.43.160", 23 | "locked": "false", 24 | "name": "feathers", 25 | "region": "tor1", 26 | "size": "512mb", 27 | "ssh_keys.#": "1", 28 | "ssh_keys.0": "430556", 29 | "status": "active" 30 | } 31 | } 32 | }, 33 | "dnsimple_record.feathers-demo": { 34 | "type": "dnsimple_record", 35 | "depends_on": [ 36 | "digitalocean_droplet.feathers" 37 | ], 38 | "primary": { 39 | "id": "5264678", 40 | "attributes": { 41 | "domain": "feathersjs.com", 42 | "domain_id": "110384", 43 | "hostname": "demo.feathersjs.com", 44 | "id": "5264678", 45 | "name": "demo", 46 | "priority": "0", 47 | "ttl": "3600", 48 | "type": "A", 49 | "value": "159.203.43.160" 50 | } 51 | } 52 | }, 53 | "dnsimple_record.feathers-slack": { 54 | "type": "dnsimple_record", 55 | "depends_on": [ 56 | "digitalocean_droplet.feathers" 57 | ], 58 | "primary": { 59 | "id": "5264677", 60 | "attributes": { 61 | "domain": "feathersjs.com", 62 | "domain_id": "110384", 63 | "hostname": "slack.feathersjs.com", 64 | "id": "5264677", 65 | "name": "slack", 66 | "priority": "0", 67 | "ttl": "3600", 68 | "type": "A", 69 | "value": "159.203.43.160" 70 | } 71 | } 72 | }, 73 | "dnsimple_record.feathers-todo": { 74 | "type": "dnsimple_record", 75 | "depends_on": [ 76 | "digitalocean_droplet.feathers" 77 | ], 78 | "primary": { 79 | "id": "5264679", 80 | "attributes": { 81 | "domain": "feathersjs.com", 82 | "domain_id": "110384", 83 | "hostname": "todos.feathersjs.com", 84 | "id": "5264679", 85 | "name": "todos", 86 | "priority": "0", 87 | "ttl": "3600", 88 | "type": "A", 89 | "value": "159.203.43.160" 90 | } 91 | } 92 | } 93 | } 94 | } 95 | ] 96 | } 97 | --------------------------------------------------------------------------------