Skip to content

Instantly share code, notes, and snippets.

@arkadiyk
Last active January 15, 2020 02:00
Show Gist options
  • Save arkadiyk/ac54e5deb991014bbcba56e0044d5f01 to your computer and use it in GitHub Desktop.
Save arkadiyk/ac54e5deb991014bbcba56e0044d5f01 to your computer and use it in GitHub Desktop.
Compiled EmberJS in Docker

Compiled EmberJS App in Docker

The biggest problem deploying compiled EmberJS apps as a container is the configuration defined during compile time and becomes part of the image. Which is not ideal as you probably want to move the image between QA/UAT and prod without any modification.

The solution I will describe here looks like a hack but it's been working for me.

  1. I put config <meta> tag into index.html:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Sonic Dialer</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- THOSE TWO LINE WILL BE UPDATED WITH ENV WHEN APP STARTS IN CONTAINER -->
    <meta name="CONFIG_API_URL">
    <meta name="CONFIG_WEB_SOCKET_URL">

    {{content-for "head"}}

    <link rel="icon" href="/sonic/assets/favicon.ico">
    <link integrity="" rel="stylesheet" href="{{rootURL}}assets/vendor.css">
    <link integrity="" rel="stylesheet" href="{{rootURL}}assets/sonic.css">

    {{content-for "head-footer"}}
  </head>
  <body id="sonic-body" class="container">
    {{content-for "body"}}

    <script src="{{rootURL}}assets/vendor.js"></script>
    <script src="{{rootURL}}assets/sonic.js"></script>

    {{content-for "body-footer"}}

    <datalist id="phone-tags">
      <option value="mobile">mobile</option>
      <option value="office">office</option>
      <option value="home">home</option>
      <option value="other">other</option>
    </datalist>

  </body>
</html>
  1. Then I wrote a tiny shell script to replace <META> tags with ENV vars. I tried to be as generic as possible so I look for every ENV vars which name starts with CONFIG and try to find matching <meta> tag
#!/bin/sh
set -e

for i in $(env | grep CONFIG_)
do
  var_name="$(echo $i | cut -d'=' -f 1)"
  var_value="$(echo $i | cut -d'=' -f 2)"
  sed -i "/$var_name/c <meta name='$var_name' content='$var_value'>" /app/index.html
done

exec "$@"
  1. This script will be Docker image ENTRYPOINT. Here is the Dockerfile for multistep build:
# Compile
FROM node:9-alpine as builder

RUN apk update && apk upgrade && \
    apk add --no-cache bash git openssh

RUN mkdir /app
COPY . /app

WORKDIR /app
RUN npm install

RUN chmod 644 public/assets/favicon.ico
RUN ./node_modules/.bin/ember build --prod


# Build runtime Image
FROM nginx:alpine

COPY --from=builder /app/dist /app
ADD deployment-scripts/nginx.conf /etc/nginx/conf.d/default.conf
ADD deployment-scripts/configure_app.sh /configure_app.sh

EXPOSE 80

ENTRYPOINT ["/configure_app.sh"]
CMD ["nginx", "-g", "daemon off;"]
  1. And I run the image supplying a simple ENV file
CONFIG_API_URL=http://sonic-api.example.com/graphql
CONFIG_WEB_SOCKET_URL=ws://sonic-api.example.com/socket
  1. To read the config in my app I use a simple Ember service. The service combines the ENV config with the config defined in config/environment.js in ENV.APP_CONFIG group. This lets me take Dev config from config/environment.js and PROD/QA from ENV
import Service from "@ember/service"
import config from "../config/environment"
import { computed } from "@ember/object"
import $ from "jquery"

const readConfig = function readConfig() {
  const metas = $("meta[name*='CONFIG_'][content]").toArray()
  return metas.reduce((acc, e) => {
    const name = $(e).attr("name").replace(/^CONFIG_/,"")
    acc[name] = $(e).attr("content")
    return acc
    },
  {})
}

export default Service.extend({
  appConfig: computed(function() {
    return {...config.APP_CONFIG, ...readConfig()}
  })
})

Hope this helps. Please let me know if you know any better way or found problems in my approach.

ps. Here is my nginx.conf for references

server {
  listen 80 default_server;
  listen [::]:80 default_server;

  root /app;

  index index.html;

  server_name sonic.example.com;

  location / {
    try_files $uri $uri/ @rewrites;
  }

  location @rewrites {
    rewrite ^(.+)$ /index.html last;
  }

  location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    # Some basic cache-control for static files to be sent to the browser
    expires max;
    add_header Pragma public;
    add_header Cache-Control "public, must-revalidate, proxy-revalidate";
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment