ember-cli deployment so even your grandma would understand

In this article, I want to show the one possible way to deploy Ember apps from my own example. I’m gonna use http://ember-cli-deploy.com/ lightning approach: Digital Ocean + extra small node app for hosting and serve static index.html with assets through Amazon S3.

I found that there’s no end to end walkthrough so far.

This tutorial targeting OS X users first, other OS can be adjusted with a small amount of efforts.

Let’s begin.

Setup Digital Ocean

  1. Register account in DigitalOcean — https://www.digitalocean.com/ . They will ask you to put credit card info and you need to be able to buy the cheapest instance which costs $5/mo
  2. Go to “Create a Droplet” page and choose “One click apps”, then find “Redis 3.2.1” in name (or so) and choose a size of $5/mo droplet instance below.
  3. Scroll down to SSH keys section. Hit “New SSH Key” and by following their article on how to add SSH key kindly referenced by DigitalOcean team add your key.
  4. Hit “Create”. Droplet is gonna take a couple of seconds to create and we’re done!
  5. While it’s building or after that, hit on the droplet name and copy the ip address, it should be next to ipv4 in the headline.
  6. Go to your Terminal console and put the copied ip address in the next statement (this is my example of ip address 444.555.89.19):
GrandMa notice: $ used to define that following command should be used in Terminal
$ ssh root@444.555.89.19

7. You might be asked whether you want to connect, type “yes” and hit “Enter”. After short delay you’ll see something like that in your console:

Welcome to DigitalOcean’s Redis Application
Your Redis instance is running on port 6379 and is bound to the local interface.
Comment out the “bind” configuration directive in /etc/redis/redis.conf to make
it available on the public interface. Before doing so, please review:
http://redis.io/topics/security
Log file: /var/log/redis/redis_6379.log
Data dir: /var/lib/redis/6379
You can learn more about using this image here: http://do.co/redisapp
 — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — 
To delete this message of the day: rm -rf /etc/motd.tail
root@redis-512mb-nyc3–01:~#

This means we just entered into the console using SSH key.

Setup Node serving app for assets

Remember, we have installed Redis based instance — this saved us some time and simplified the process. Let’s configure nginx first. Before that, we need to create an app name so we can use the same title everywhere, let’s call our app — “testapp”.

Setup nginx

You can go read yourself how to do that on DigitalOcean blog. However, if you aren’t familiar with DevOps — you can skip this and follow my lead

Shortcut:

$ sudo apt-get update && sudo apt-get install nginx

Hit “Y” and enter when you’ll be asked to use your precious disk space.

Done? Almost.

Use the next command to test it installed well (no errors) and also stop server. Nginx servers starts by default:

$ sudo service nginx stop

Then you can simply start nginx:

$ sudo service nginx start

Anytime you’re changing the config you’d want to restart service:

$ sudo service nginx restart

Now, let’s create the config file:

Notice: VIM or VI is hard to digest for novice. I’m not expert either, however, it’s extremely useful editor once you know a few basic commands
$ vim /etc/nginx/conf.d/testapp.com.conf

We just have attempted to open the file named “testapp.com.conf” that supposed to be present in “/etc/nginx/conf.d/” folder. Since we don’t have such file, VIM created it for us. Important. This editor looks weird at first glance and you can’t even exit easily - use the cheatsheet below so you can use it with ease:

VIM CheatSheet:
VIM has 2 modes: “command” and “edit”. You’re starting from command mode (it’s not working like usual editor) by default. All commands starting from “:” supposed to be used in command mode.
edit mode: type “a”
from edit mode to command mode: “ctrl+c”
exit editor without save: type “:q” and hit enter
exit editor with save: type “:wq” and hit enter
if for some reason you see an error or want to make force action type: add ! in the end of command “:q!” or “:wq!”
you can use arrows in edit mode (suggested option for novice), or “h”,“j”,“k”, “l”

Now you’re ready. Hit “a” and put this config by simply copying and pasting. After hitting “a” when you entered edit mode nothing stops you from pasting anything in the file like in a normal editor.

/etc/nginx/conf.d/testapp.conf

https://gist.github.com/Blackening999/cb5af5a39493664ae3206d10b411fb3e

server {
listen 80;
server_name testapp.com www.testapp.com; #let’s assume you have bare/www domain registered
#SECURE WAY: return 301 https://www.testapp.com$request_uri;#force HTTPS + permanent redirect
}
server {
#SECURE WAY: listen 443 ssl;
#SECURE WAY: ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
#SECURE WAY: ssl_prefer_server_ciphers on;
#SECURE WAY: ssl_session_cache shared:SSL:10m;
#SECURE WAY: ssl_ciphers “ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS”;
#SECURE WAY: ssl_dhparam /etc/nginx/ssl/dhparam.pem;
#SECURE WAY: ssl_certificate /etc/nginx/ssl/testapp.com.com.crt; #put here your crt file
#SECURE WAY: ssl_certificate_key /etc/nginx/ssl/testapp.com.key; #put here your key file
#LOGS: access_log /var/log/nginx/testapp.com.access.log main;
#LOGS: error_log /var/log/nginx/testapp.com.error.log;
server_name testapp.com www.testapp.com;
location / {
# we are going to use proxy to our Node app
proxy_pass http://localhost:8782;#random port, be aware that you cannot use any port
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection ‘upgrade’;
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I have commented rows responsible for SSL secured apps. If you have .crt and .key files generated and registered, simply delete all “#SECURE WAY:” statements (not the whole row :), only this statement).

In the exmple config file we’re assuming we have domain name registered under testapp.com

Nginx part is done! Run “Nginx restart command” and let’s test it.

Put your ip-address in your browser URL, and if everything is fine (no config file errors) — you’ll “Welcome nginx” page.

Configure Redis

Let’s test it’s up and running.

redis-cli

Once you’re in console

redis-cli $> ping

You should see “PONG” in response. To exit Redis console hit “Ctrl + C”

Now let’s secure your Redis instance:
I’m gonna use in this example next password “mysupersecretredispassword:

Let’s open Redis config:

$ vim /etc/redis/redis.conf

Now, for easier search, type: “/” while in Vim and type this search phrase: “requirepass” then hit “Enter” (it enables search mode, navigate to next key found by typing “n” or “N” for previous result).

Go to edit mode, by hitting “a”. rename it so it doesn’t have trailing “#” and requirepass followed by your password.

requirepass mysupersecretredispassword

And while we’re here got to command mode “CTRL + C” and search, using approach we used above for

bind 127.0.0.1

Change it to (where 444.555.89.19 is your DigitalOcean ip address):

bind 444.555.89.19 127.0.0.1

Hit CTRL+C and type “:wq”. Saved!

For system to take the effect:

$ sudo service redis restart

Let’s test!

$ redis-cli
redis-cli $> ping
(error) NOAUTH Authentication required.

As you can see, we’re now secure. Let’s test our password is correct

$ redis-cli auth mysupersecretredispassword
OK

Sweet! Remember your password, it will be our REDIS SECRET we’re going to use later

You can go more secure by reading this article: https://www.digitalocean.com/community/tutorials/how-to-use-the-redis-one-click-application

Setup Node server

Let’s navigate to “/var”

$ cd /var

Now, we’re going to create a folder which will hold our Node app:

$ mkdir www && cd www
$ mkdir testapp && cd testapp

We have created www folder and testapp inside it and we also inside it.

Now let’s install Node itself. We also will need NPM and NVM(for easier version management). Let’s go!

$ sudo apt-get update
$ sudo apt-get install build-essential libssl-dev
$ curl -sL https://raw.githubusercontent.com/creationix/nvm/v0.33.2/install.sh -o install_nvm.sh

Hit “Yes” whenever you’ve been asked to allocate some space.

Last command downloaded the script (latest version is v0.33.2 at the time of writing, you can check last version here: https://github.com/creationix/nvm

Let’s install NVM and also delete script file after:

$ bash install_nvm.sh && rm install_nvm.sh

and then let’s reload our profile so we can use NVM without logging out and logging in:

$ source ~/.profile

Let’s test it:

$ nvm list

If you have any output — you have installed it correctly!

You can check the list of all available version through this command:

$ nvm ls-remote

I’m gonna install Latest LTS (long-term-support)

$ nvm install 6.11.0

And we want to use it + set by default:

$ nvm use 6.0.0 && nvm alias default 6.0.0 && nvm use default

If you want to check what version you’re using now, simply use this command:

$ node -v

Time to write the app!

First let’s create app.json which will contain all configs:

$ vim app.json

Hit “a” and copy/paste next file. Make sure you put your app name: “testapp100500-bucket” and REDIS SECRET we have obtained on previous steps.

/var/www/testapp/app.json

https://gist.github.com/Blackening999/eb4c56f5f44853d7a1e373811932de6f

{
  "name": "testapp",
  "description": "Ember lightning hosting",
  "keywords": [
    "ember",
"deploy",
"grandma"
  ],
  "env": {
    "NODE_ENV": {
       "value": "production"
    },
    "APP_NAME": {
      "description": "Name of your emberjs app",
      "value": "testapp100500-bucket"
    },
  "APP_PORT": {
    "description": "The port the app runs on",
    "value": 8782
  },
    "REDIS_PORT": {
      "description": "Redis port.",
      "value": 6379
    },
    "REDIS_HOST": {
      "description": "Redis host.",
      "value": "127.0.0.1"
    },
    "REDIS_SECRET": {
      "description": "Redis secret.",
      "value":  "mysupersecretredispassword"
    }
  }
}

Let’s create our server file, index.js,

$ vim index.js

with the next contents:

/var/www/testapp/index.js

https://gist.github.com/Blackening999/00369f075baca1ca22316da7d42d95b9

'use strict';
const redis = require('redis'),
      co = require('co'),
      coRedis = require('co-redis'),
      Koa = require('koa'),
       config = require('./app.json');
const app = exports.app = new Koa(),
client  = redis.createClient(
  config.env.REDIS_PORT.value,
  config.env.REDIS_HOST.value
),
dbCo = coRedis(client);

if (config.env.REDIS_SECRET.value) {
  client.auth(config.env.REDIS_SECRET.value);
}
client.on('error', function (err) {
  console.log('Redis client error: ' + err);
});
app.use(co.wrap(function* (ctx) {
  var indexkey;
  if (ctx.request.query.index_key) {
    indexkey = config.env.APP_NAME.value +':'+   ctx.request.query.index_key;
  } else {
    indexkey = config.env.APP_NAME.value +':current-content';
}
var index = yield dbCo.get(indexkey);

if (index) {
  ctx.body = index;
} else {
  ctx.status = 404;
}
}));
var server = app.listen(process.env.PORT || 8782);
process.on('SIGINT', function () {
  server.close(function () {
  process.exit(0);
});
});

Last file we’ll need is package.json to configure libraries we’re going to use:

/var/www/testapp/package.json

https://gist.github.com/Blackening999/fe1394b380781e87e7feafcc14a8d33a

{
"name": "ember-lightning",
"version": "0.2.1",
"description": "Ember lightning hosting for https://github.com/LevelbossMike/ember-deploy",
"main": "index.js",
"scripts": {
"start": "node --harmony index.js",
"test": "mocha --harmony --full-trace test.js"
},
"repository": {
"type": "git",
"url": "https://github.com/philipheinser/ember-lightning.git"
},
"keywords": [
"ember",
"lightning",
"hosting",
"server",
"deploy"
],
"author": "Philip Heinser <philipheinser@me.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/philipheinser/ember-lightning/issues"
},
"homepage": "https://github.com/philipheinser/ember-lightning",
"dependencies": {
"co": "^4.6.0",
"co-redis": "^2.1.0",
"hiredis": "^0.4.1",
"koa": "^2.0.0-alpha.2",
"redis": "^2.6.0-1"
},
"engines": {
"node": "4.2.x"
},
"devDependencies": {
"chai": "^3.5.0",
"co-supertest": "0.0.10",
"mocha": "^2.4.5",
"should": "^8.3.0",
"supertest": "^1.2.0",
"supertest-koa-agent": "^0.2.1"
}
}

Time to install dependencies!

$ npm install

We’ll also will need forever to run our script in endless mode:

$ npm install -g forever

Congratulations! Node server is ready!

Before we’ll proceed let’s check whether it runs without errors:

$ forever start index.js

and check it’s running

$ forever list
info: Forever processes running
data:        uid  command                                   script   forever pid  id logfile                 uptime
data:    [0] VUD7 /root/.nvm/versions/node/v6.11.0/bin/node index.js 7802    2308    /root/.forever/LLD7.log 0:0:0:6.175

DONE, our node server process just daemonized!

Setup Amazon AWS S3

  1. Register in Amazon AWS: https://aws.amazon.com/free
  2. Hit “Create AWS account” and follow all steps required
  3. Once you’ve registered, go here: https://aws.amazon.com/s3 and hit “Get Started with Amazon S3”. (They might annoy you with sign in form which includes account number, simply select “Sign-in using root credentials”
  4. Let’s create our first bucket. I’m calling it “testapp100500-bucket”, select region close to your location (I’m selecting Virginia) skip all other options. Hit “Next” until the button will become “Create bucket”. Done!
  5. Select your bucket. You should see the window with settings. Hit on Permissions. Hit “Bucket Policy”.

6. Now copy and paste URL (current page) and open in a different window. You should have the same page in a 2 different tabs.

7. In the another tab, select “My Security credentials” in navigation dropdown under your name:

8. Click “Continue to Secuirty credentials” if modal window appears.

9. Select “Users” on the left and then “Add User”.

10. Add user name, for instance “TestAppDeployer” and check “Programmatic access” under “Access type”. Hit “Next:Permissions”

11. Hit “Create Group” and name it: “S3Manager” and select “S3 Full Access” in the options below. Then hit “Create Group”

12. Scroll down and hit “Next” and then “Create User”.

Congratulations! You have created IAM user, this will limit access to your bucket only for this specified user, you’ll see how.

The final page shows your credentials associated with newly created user. Copy and save Access key ID and Secret access key we’re going to use them later. You can also download .csv with credentials. The link highlighted on the screenshot is for quick access to a console for the newly user created. You can save it for future.

Hit “Close”.

13. Click on newly created User

and copy User ARN , something like that: “arn:aws:iam::354222777925:user/TestAppDeployer

We will need these credentials really soon.

14. Go back to the tab with “Bucket Policy” opened.

and then hit “Next: Review”

15. Copy and paste next policy. You can user config provided by ember-cli-deploy-s3, but it lacks few fields, so I suggest to follow with my example

{
“Version”: “2008–10–17”,
“Statement”: [
{
“Sid”: “Stmt1EmberCLIS3AccessPolicy”,
“Effect”: “Allow”,
“Principal”: {
“AWS”: “arn:aws:iam::354222777925:user/TestAppDeployer
},
“Action”: [
“s3:GetObject”,
“s3:PutObject”,
“s3:PutObjectACL”
],
“Resource”: “arn:aws:s3:::testapp100500-bucket/*”
}
]
}

You need to make sure you passed your own User ARN and bucket name:

Then hit “Save”.

That’s it! Amazon S3 configured and ready to be used!

Configure Ember App

Open another tab in your Terminal. And create a project if you don’t have one. Or skip and use yours:

$ ember new testapp

app created — done! :)

I’m not gonna install straight lightning pack, rather go with packages one by one:

$ ember install ember-cli-deploy
$ ember install
ember-cli-deploy-build
$ ember install ember-cli-deploy-display-revisions
$ ember install ember-cli-deploy-redis
$ ember install ember-cli-deploy-revision-data
$ ember install ember-cli-deploy-s3

Now, let’s update our config/deploy.js to match the next file with your credentials. Use Access key ID and Secret access key we have obtained before, as well as REDIS SECRET. I’m using the same demo keys:

testapp/config/deploy.js

https://gist.github.com/Blackening999/955318ea8e1549cee59d9a0f1aa478d7

/* jshint node: true */
var VALID_DEPLOY_TARGETS = [ //update these to match what you call your deployment targets
'production'
];
module.exports = function(deployTarget) {
var ENV = {
build: {}
// include other plugin configuration that applies to all deploy targets here
};
if (VALID_DEPLOY_TARGETS.indexOf(deployTarget) === -1) {
throw new Error('Invalid deployTarget ' + deployTarget);
}
if (deployTarget === 'production') {
ENV["redis"] = {
host: '444.555.89.19',
port: 6379,
password: 'mysupersecretredispassword',
keyPrefix: 'testapp100500-bucket'
}
ENV["s3"] = {
accessKeyId: 'AMAZON ACCESS KEY',
secretAccessKey: 'AMAZON SECRET KEY',
bucket: 'testapp100500-bucket',
region: 'us-east-1',
filePattern: '**/*.{js,css,png,gif,ico,jpg,map,xml,txt,svg,swf,eot,ttf,woff,woff2,html}',
}
}
return ENV;
};

There’s a small trick that not described in the docs. It’s about ember-cli-build.js . We need to prepend our assets with bucket key.

testapp/ember-cli-build.js

https://gist.github.com/Blackening999/32369631ab027d53d44df10d4f3b9d23

/*jshint node:true*/
/* global require, module */
var EmberApp = require('ember-cli/lib/broccoli/ember-app');
var config = require('./config/environment')(process.env.EMBER_ENV);
var fingerprintConfig = process.env.EMBER_ENV === 'development' ? {enabled: false} : {enabled: true,prepend: config.s3BuildEndpoint};
module.exports = function(defaults) {
var app = new EmberApp(defaults, {
fingerprint: fingerprintConfig
});
return app.toTree();
};

and you can store s3BuildEndpoint in the “config/environment” like this:

https://gist.github.com/Blackening999/092671d9931929ecd1ec556267fa630d

/* eslint-env node */
module.exports = function(environment) {
var ENV = {
modulePrefix: 'testapp',
environment: environment,
rootURL: '/',
locationType: 'auto',
EmberENV: {
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. 'with-controller': true
},
EXTEND_PROTOTYPES: {
// Prevent Ember Data from overriding Date.parse.
Date: false
}
},
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
//Your custom s3BuildEndpoint
s3BuildEndpoint: ' https://testapp100500-bucket.s3.amazonaws.com/',
};
if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}
if (environment === 'test') {
// Testem prefers this...
ENV.locationType = 'none';
// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;
ENV.APP.rootElement = '#ember-testing';
}
if (environment === 'production') {
}
return ENV;
};

You’ve made it! Now we can deploy our app and test overall infrastructure:

  1. Run
$ ember deploy production
cleaning up...
- Listing revisions for key: `testapp100500-bucket`
Deploying [====🚀 ] 100% [plugin: redis -> didDeploy]
- Deployed but did not activate revision 4dfa3e98c41e81897fc466e7ca02c947. To activate, run: ember deploy:activate production --revision=4dfa3e98c41e81897fc466e7ca02c947

This means everything works just fine.

2. Now, let’s activate our build:

$ ember deploy:activate production --revision=4dfa3e98c41e81897fc466e7ca02c947

If you’ll go to your bucket opened and refresh the page, you’ll soon see that all your assets + index.html uploaded.

Let’s check our Node server, put “444.555.89.19:8782” to browser URL and you should see your app! We need to specify the port here so we can directly to go to our app. You don’t need this if you have domain name registered, in our case “testapp.com” should work!

You can check the list of revision you have from now on, activate whatever version you need. It’s extremely useful when you need to backup really fast.

Hope you enjoyed the article! You learned easy and powerful way to deploy and host your ember-cli apps! If you have any questions — do not hesitate to ask!

❤ If this post helped you, please hit the little green heart!

Thanks to: