Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ember.js, DevOps, and You - MadisonRuby 2016

Ember.js, DevOps, and You - MadisonRuby 2016

My talk on Ember.js, DevOps, and You given at MadisonRuby 2016.

Iheanyi Ekechukwu

July 08, 2016
Tweet

More Decks by Iheanyi Ekechukwu

Other Decks in Programming

Transcript

  1. Ember.js, DevOps, and You
    Iheanyi Ekechukwu
    Madison+Ruby 2016

    View full-size slide

  2. Iheanyi Ekechukwu
    Software Engineer @
    DigitalOcean
    @kwuchu

    View full-size slide

  3. Defining DevOps
    What exactly is DevOps?

    View full-size slide

  4. Development + Operations

    View full-size slide

  5. DevOps is deploying.

    View full-size slide

  6. DevOps is provisioning.

    View full-size slide

  7. DevOps is versioning.

    View full-size slide

  8. DevOps is complicated.

    View full-size slide

  9. Automation is

    View full-size slide

  10. Ember.js and DevOps
    What do Ember.js and DevOps have to
    do with one another?

    View full-size slide

  11. Ember-CLI-Deploy Plugins

    View full-size slide

  12. An Ember-CLI-Deploy Plugin
    can implement up to 11 hooks

    View full-size slide

  13. So, it doesn’t just deploy.

    View full-size slide

  14. Ember-CLI-Deploy can provision.

    View full-size slide

  15. Ember-CLI-Deploy can build.

    View full-size slide

  16. Ember-CLI-Deploy can deploy.

    View full-size slide

  17. Ember-CLI-Deploy can manage releases.

    View full-size slide

  18. Ember-CLI-Deploy

    View full-size slide

  19. Ember-CLI-DevOps

    View full-size slide

  20. Building a Plugin

    View full-size slide

  21. Create the Ember addon and
    install the Ember-CLI-Deploy
    base plugin.
    STEP ONE

    View full-size slide

  22. ember addon ember-cli-deploy-digitalocean

    View full-size slide

  23. cd ember-cli-deploy-digitalocean
    npm install ember-cli-deploy-plugin --save

    View full-size slide

  24. STEP TWO
    Manually deploy a Fastboot
    application, taking note of each
    action.
    (Provisioning, Deployment, etc.)

    View full-size slide

  25. Let’s go through the steps.

    View full-size slide

  26. Create a Droplet.

    View full-size slide

  27. SSH into the droplet.

    View full-size slide

  28. Install Nginx, Node, NPM, and
    Ember-Fastboot-Server on droplet

    View full-size slide

  29. On local machine, build the
    Fastboot application.

    View full-size slide

  30. SCP the built Ember
    application onto the droplet

    View full-size slide

  31. On droplet, go to the uploaded
    app and run the Fastboot
    application.

    View full-size slide

  32. Configure Nginx to proxy over the
    Fastboot’s server’s port and serve
    static assets.

    View full-size slide

  33. Reload Nginx.

    View full-size slide

  34. And you’re done.

    View full-size slide

  35. STEP THREE
    Translate each recorded step
    into code.
    (Automation)

    View full-size slide

  36. Automation Problems
    and Solutions

    View full-size slide

  37. PROBLEM
    How do I create a droplet for
    the user?

    View full-size slide

  38. SOLUTION
    Use DigitalOcean’s API and
    create an Ember command
    that creates the droplet.

    View full-size slide

  39. export DO_ACCESS_TOKEN=

    View full-size slide

  40. ember do:provision

    View full-size slide

  41. PROBLEM
    Installing all software
    dependencies on the droplet.
    (Provisioning)

    View full-size slide

  42. WITHOUT FASTBOOT
    One package dependency.
    Web Server (NGINX) serves
    the static assets.

    View full-size slide

  43. WITH FASTBOOT
    More dependencies. It’s not
    only NGINX. We now need
    Node, NPM, and Fastboot.

    View full-size slide

  44. SOLUTION
    DigitalOcean Images and
    execution of commands via
    Node SSH2.

    View full-size slide

  45. this.conn = new SSHClient();
    this.sshConfig = {
    host: config.ipAddress,
    port: 22,
    username: config.dropletUsername,
    privateKey:
    require('fs').readFileSync(config.privateKeyPath),
    password: config.dropletPassword,
    passphrase: config.passphrase,
    };

    View full-size slide

  46. willUpload: function() {
    var conn = this.conn;
    conn.on('ready', () => {
    conn.exec(‘sudo apt-get update -y; sudo apt-get
    upgrade -y; sudo apt-get install -y nginx gcc build-
    essential; rm /etc/nginx/sites-enabled/default', (err,
    stream) => {
    if (err) throw err;
    stream.on('data', (data) => {
    this.log('STDOUT: ' + data);
    }).on('end', (data) => {
    resolve();
    });
    })
    }).connect(this.sshConfig);
    })

    View full-size slide

  47. PROBLEM
    How do I build my Ember
    application and get it onto the
    droplet?

    View full-size slide

  48. SOLUTION
    Use ember-cli-deploy-build to
    build the application, NPM install
    dependencies, then SCP files
    onto the droplet.

    View full-size slide

  49. configure: function() {
    //…
    this.scpConfig = {
    host: config.ipAddress,
    port: 22,
    username: process.env.DROPLET_USERNAME || 'root',
    privateKey:
    require('fs').readFileSync(process.env.PRIVATE_KEY_DIR),
    password: process.env.DROPLET_PASSWORD,
    passphrase: process.env.PASSPHRASE,
    path: '/etc/nginx/sites-enabled/ember-app'
    };
    this.scpClient = SCPClient;
    },

    View full-size slide

  50. willUpload: function(context) {
    var npmInstallTask = new NPMInstallTask({
    log: this.log.bind(this),
    distDir: context.distDir
    });
    return npmInstallTask.run()
    // more code down here
    }

    View full-size slide

  51. upload: function(context) {
    this.log('Uploading assets to the droplet!');
    var scpClient = this.scpClient;
    return new Promise((resolve, reject) => {
    scpClient.scp(context.distDir, this.scpConfig, (err) => {
    if (err) {
    throw err;
    }
    //…
    return resolve();
    });
    });
    },

    View full-size slide

  52. context.distDir

    View full-size slide

  53. PROBLEM
    What if my droplet restarts
    and kills the Fastboot server?

    View full-size slide

  54. SOLUTION
    Create a Fastboot Upstart
    Service on the droplet.

    View full-size slide

  55. description "A job file for starting up the Fastboot service for Ember."
    author "Iheanyi Ekechukwu"
    start on filesystem or runlevel [2345]
    stop on shutdown
    pre-start script
    npm install -g ember-fastboot-server
    echo "Starting Fastboot server" >> /var/log/fastboot.log
    end script
    script
    export HOME
    echo $$ > /var/run/fastboot.pid
    exec ember-fastboot /etc/nginx/sites-enabled/ember-app >> /var/log/fastboot.log 2>&1
    end script
    pre-stop script
    rm /var/run/fastboot.pid
    echo "Fastboot Server Stopping" >> /var/log/fastboot.log
    end script

    View full-size slide

  56. didUpload: function(context) {
    // other code…upload fast boot
    fileClient.upload('./node_modules/ember-cli-
    deploy-digitalocean/templates/fastboot.conf', '/
    etc/init/fastboot.conf', err => {
    if (err) {
    this.log(err, {color: 'red'});
    throw err;
    }
    // other code…
    });

    View full-size slide

  57. PROBLEM
    How do I get the application
    up and served up to the user?

    View full-size slide

  58. SOLUTION
    Upload a custom NGINX
    config and restart everything.

    View full-size slide

  59. http {
    include /etc/nginx/mime.types*;
    default_type application/octet-stream;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    server {
    listen 80;
    root /etc/nginx/sites-enabled/ember-app;
    index index.html index.html;
    server_name localhost;
    location / {
    index index.html;
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-NginX-Proxy true;
    }
    location ~ \.(ttf|ttc|otf|eot|woff|font.css|css)$ {
    add_header Access-Control-Allow-Origin "*";
    }
    location /assets {
    autoindex on;
    }
    }
    }

    View full-size slide

  60. didUpload: function(context) {
    // upload nginx conf, other code left out
    fileClient.upload('./node_modules/ember-cli-deploy-digitalocean/
    templates/nginx.conf', '/etc/nginx/nginx.conf', (err) => {
    if (err) {
    this.log(err);
    throw err;
    }
    return resolve();
    });
    // other code…
    }

    View full-size slide

  61. didUpload: function(context) {
    // restart nginx and fastboot service, other code left out
    conn.exec("sudo service nginx restart; sudo service fastboot
    restart;",
    (err, stream) => {
    if (err) throw err;
    stream.on('data', (data) => {
    this.log('STDOUT: ' + data);
    }).on('end', (data) => {
    this.log("We're in business!");
    return resolve();
    }).stderr.on('data', (data) => {
    this.log('STDERR: ' + data);
    });
    });
    }

    View full-size slide

  62. And we’re done automating.

    View full-size slide

  63. One small NPM trick that
    makes building deploy plugins
    easier…

    View full-size slide

  64. ember-cli-deploy-digitalocean
    (github.com/iheanyi/ember-cli-deploy-
    digitalocean)

    View full-size slide

  65. Plugin Limitations

    View full-size slide

  66. Deploys limited to one droplet.

    View full-size slide

  67. Provisioning tasks get re-run
    unnecessarily.

    View full-size slide

  68. No controlled release
    management.

    View full-size slide

  69. Old Fastboot Serving Logic

    View full-size slide

  70. Ideas for Future Enhancements

    View full-size slide

  71. Zero Downtime Deployments

    View full-size slide

  72. SSL Support?

    View full-size slide

  73. Semantic Versioning
    (semantic-release)

    View full-size slide

  74. The Future of DevOps?

    View full-size slide

  75. Thanks to…
    @tomdale
    @lukemelia
    @davidpett
    @digitalocean
    and others!

    View full-size slide

  76. Thank you.
    @kwuchu

    View full-size slide