Ember
This documentation provides a step-by-step guide to prepare a Single Page Application (SPA) that was built using EmberJS for deploying it in any cloud using LocalOps.
Prerequisitesβ
To follow this tutorial, you will need the following:
- Node.js, v20.15.1 at the time of writing this doc.
- npm as a package manager for installing and maintaining dependencies. This usually comes bundled with Node.js.
- A foundational knowledge of Ember.js.
- nginx to serve static assets and also act as a reverse proxy server.
- docker to build standalone containers to serve your production app.
This guide assumes that you have basic knowledge of the above-mentioned technologies and tools. If you are not familiar with any of them, it is highly recommended to review their official documentation and tutorials to get up to speed before proceeding with this guide.
Installing the Ember CLIβ
You'll need to install Ember CLI to scaffold a new project. Open a terminal and run the following command:
npm install -g ember-cli
Check out the Ember CLI docs for more info.
Scaffolding the Ember Appβ
In your terminal, run the CLI command ember new
with the desired project name. In the following examples, we'll use the example project name ember-project
.
ember new <your project name> --lang en
Follow the prompts to create your first app. If you have more questions, check out the Quickstart guide to learn more about scaffolding the app.
Once the application is ready, navigate to the directory and install all the dependencies:
cd ember-project # replace with the name of the project you created
All of your dependencies should be installed at this point (which you can verify by checking for the existence of a node_modules
folder in your project). You can start your project by running the command:
npm start
If everything is successful, you should see a similar confirmation message in your terminal:
Livereload server on http://localhost:7020
Serving on http://localhost:4200/
Open http://localhost:4200
in your browser of choice. You should see an Ember welcome page and not much else.
Replace the <WelcomePage />
component in <rootDir>/app/templates/application.hbs
before building for production with your app logic or something else to avoid errors while starting the application.
Building Your Production Appβ
Now that we've written our application and verified that it works in development, it's time to get it ready to deploy to our users.
npm run build
The build
command packages up all of the assets that make up your applicationβJavaScript, templates, CSS, web fonts, images, and moreβin an optimized way for production use.
Configuring nginxβ
Once the application is built for production, we need a static web server to serve those files. For that purpose, we are using nginx. nginx is an open-source HTTP and reverse proxy server.
server {
listen ${PORT};
server_name _;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
# skip this block if you don't have a backend yet
location /api/ {
proxy_pass http://{BACKEND_HOST}:${BACKEND_PORT};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Create this file nginx.conf.template
in the root of your project. We won't be using this template now; we'll use it to run the nginx server inside the Docker container.
Note two important blocks in this configuration:
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
This block serves the index.html
from the files we built using Ember, stored in the /usr/share/nginx/html
folder. We will copy the assets to this folder while building the Docker application.
And then the API block:
location /api/ {
proxy_pass http://{BACKEND_HOST}:${BACKEND_PORT};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
You might want to skip the API block if you don't have a backend yet; otherwise, nginx will throw an error for an invalid host on the backend URL.
A typical non-static web application usually will have a backend, and we make AJAX requests using fetch or libraries like axios to get/send data to retrieve/store the application state.
Assume that the site we built is being served by nginx via the domain example.com
. Whenever an API call is made to example.com/api
, the nginx server acts as a reverse proxy server and forwards the request to the backend server.
You can use reverse proxying to navigate the traffic that is inbound to nginx. The reverse proxy server also acts as a mask, hiding the real backend from the outside world.
Create the Docker Imageβ
Dockerizing makes the app run anywhere, agnostic of the platform. As long as Docker is installed, whether it's Windows, Mac, or Linux, it can run with the same behavior.
Before creating the Dockerfile, let's create a .dockerignore
file and add the contents that should not be copied over to the Docker file system.
Dockerfile
.dockerignore
node_modules
npm-debug.log
README.md
.git
Read more about .dockerignore here.
Creating a .dockerignore
file and adding folders like node_modules
is a must since dependencies will be installed while building the image based on the platform preferences used. Copying those from the file system will overwrite the installed dependencies and might error out during deployment.
Now, create a Dockerfile. The Dockerfile is a text file that contains the instructions for Docker to build the image.
The Dockerfile is posted for reference with steps to create the production image. We leverage Docker's Multi-Stage Builds to create a smaller-sized image for running the production server.
# Stage 1: Base
# This stage is the first stage
FROM node:20-alpine AS base
# Switch to the app directory,
# this will be our app's root directory inside the container
WORKDIR /app
# Copy the manifest and lock file to the root
# Note: ./ refers to /app, since we switched context in the above step
COPY package.json package-lock.json* ./
# Install the dependencies
RUN npm ci
# Set the NODE_ENV to production
# to configure the application to build/run under production mode
ENV NODE_ENV=production
# Copy all the files from our machine to the container
COPY . .
# Run the build to create production assets
RUN npm run build
# Stage 2: Production
# Now we build a new stage from scratch to run the nginx server
# This won't include any images from the previous steps
FROM nginx:alpine AS production
# The port you'd like to use for Nginx
ENV PORT=4200
# The URL for the backend service
ENV BACKEND_HOST=host.docker.internal
# Port for the backend
ENV BACKEND_PORT=4500
# Copy the template we created earlier for nginx to the etc folder
# note the destination file name should be default.conf.template
# else the variables in the config won't work
COPY nginx.conf.template /etc/nginx/templates/default.conf.template
# Since we started a new stage, we need to copy the build from the
# build step to the nginx's html directory. Note we have configured
# this directory in the nginx config file
COPY /app/dist /usr/share/nginx/html
# Expose the port the nginx is running to the outside world
EXPOSE ${PORT}
# Start the nginx server
CMD ["nginx", "-g", "daemon off;"]
Now you can build and run the Docker image. To build the Docker image:
docker build --platform linux/amd64 -t ember-app:latest .
This command builds the ember-app
image for platform linux/amd64
and tags it as latest
.
If you are locally testing your application, you can skip the platform
key to build the images:
# only for testing on the local machine
docker build -t ember-app:latest .
Run the Docker Imageβ
Let's run the Docker container using the image created for the Ember application with the command below.
docker run \
-it \
--rm \
--name ember-app \
-e PORT=4200 \
-e BACKEND_PORT=4500 \
-e BACKEND_HOST=host.docker.internal \
-d \
-p 4200:4200 \
ember-app
-it
: enables interactivity with TTY--rm
: tells the Docker Daemon to clean up the container and remove the file system after the container exits--name ember-app
: Name of the containerember-app
.-e PORT=4200
: Sets the environment variable PORT in Docker to4200
.-e BACKEND_PORT=4500
: Sets the environment variable BACKEND_PORT in Docker to4500
.-e BACKEND_HOST=host.docker.internal
: Sets the environment variable BACKEND_HOST in Docker tohost.docker.internal
.-d
: Runs the container in detached (background) mode. You can skip the flag to see the logs directly in your terminal window.-p 4200:4200
: Maps port 4200 on your host to port 4200 in the container.ember-app
at the end is the name of the image.
The -d
flag in Docker starts the container in the background. You can skip the flag to see the logs directly in your terminal window.
After running the command, visit http://localhost:4200
to see the Ember application running inside the Docker container.
Hurray π. Now we have created and packaged an Ember app for production use.
Next Stepsβ
Once the Docker image is ready, the next steps will be:
- Publishing the Docker images
- Packaging the app with Helm
- Publishing your Helm charts
- Creating an app
- Creating a release
- Deploy π
Usually, you might also have a backend associated. Check out the tutorials to know more about the steps to integrate the app with your backend.