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.
- I put config
<meta>
tag intoindex.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>
- 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 "$@"
- This script will be Docker image
ENTRYPOINT
. Here is theDockerfile
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;"]
- 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
- 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
inENV.APP_CONFIG
group. This lets me take Dev config fromconfig/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";
}
}