You are not going to run 'ng serve' within a Docker container. ng serve
is for your localhost needs to run your Angular app and develop features.
Your Docker container is a production environment concept, even if using localhost/development/staging as well.
A Docker container handling an HTTP request is going to serve a resource, which is index.html in the case of Angular but really for any initial HTTP GET to a server from a web browser/client is default index.html that is the resource returned, the home page if you will. You are not really going to want to use the Angular CLI in your Docker Container.
You will want to build your app with ng build
and then copy the distribution files into a Docker container and use an HTTP server to handle HTTP requests and serve index.html, index.html is going to reference your Javascript and CSS resources and your web browser is going to make additional requests to your Docker container for those resources files which are part of your ng build
dist output files.
Here is an example of a container wrapping up Nginx as the HTTP server:
FROM centos:7
MAINTAINER Brian Ogden
# Not currently being used but may come in handy
ARG ENVIRONMENT
ENV NODE_VERSION 10.15.1
#ENV NODE_OPTIONS --max-old-space-size=12000
RUN yum -y update && \
yum clean all && \
yum -y install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm \
yum -y makecache && \
yum -y install nginx-1.12.0 wget
# Cleanup some default NGINX configuration files we don’t need
RUN rm /etc/nginx/conf.d/default.conf
#############################################
# NodeJs Install
#############################################
#Download NodeJs package
RUN wget -q -O - https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz \
| tar --strip-components=1 -xzf - -C /usr/local
# https://stackoverflow.com/a/35774741/1258525
# use changes to package.json to force Docker not to use the cache
# when we change our application's nodejs dependencies:
COPY ./package.json /tmp/package.json
RUN cd /tmp && npm cache clean --force && npm install
RUN mkdir /app && cp -a /tmp/node_modules /app/
WORKDIR /app
COPY . /app
RUN npm run build-$ENVIRONMENT
RUN cd /app && cp -a dist/* /usr/share/nginx/html
COPY ./docker/conf/frontend.conf /etc/nginx/conf.d/frontend.conf
COPY ./docker/conf/nginx.conf /etc/nginx/nginx.conf
CMD ["nginx"]
The configuration maybe overly complex for your needs but it does demonstrate handling potential CORS issues and other challenges you may run into as your app evolves:
nginx.conf:
daemon off;
user nginx;
worker_processes 2;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
accept_mutex off;
}
http {
include /etc/nginx/mime.types;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
client_max_body_size 300m;
client_body_buffer_size 300k;
large_client_header_buffers 8 64k;
gzip on;
gzip_http_version 1.0;
gzip_comp_level 6;
gzip_min_length 0;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json;
gzip_disable "MSIE [1-6]\.";
gzip_vary on;
include /etc/nginx/conf.d/*.conf;
}
frontend.conf:
# Expires map
map $sent_http_content_type $expires {
default off;
text/html off;
text/css 2d;
application/javascript 2d;
~image/ max;
application/pdf max;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
expires $expires;
charset UTF-8; #a must have for AOT compilation with lazy loading: https://stackoverflow.com/questions/51451556/lazy-loaded-modules-with-aot-typeerror-is-not-a-function-when-served-from
# Main
location / {
set $cors "true";
if ($http_origin ~* (http:\/\/d\.tradeservice\.com\S*)$) {
set $cors "true";
}
if ($request_method = 'OPTIONS') {
set $cors "${cors}options";
}
if ($request_method = 'GET') {
set $cors "${cors}get";
}
if ($request_method = 'POST') {
set $cors "${cors}post";
}
if ($cors = "trueget") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
}
if ($cors = "truepost") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
}
if ($cors = "trueoptions") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Authorization,Content-Type,Accept,Origin,User-Agent,DNT,Cache-Control,X-Mx-ReqToken,Keep-Alive,X-Requested-With,If-Modified-Since';
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
#index index.html index.htm;
try_files $uri $uri/ @index; # This will allow you to refresh page in your angular app. Which will not give error 404.
}
location @index {
expires 0;
add_header Pragma "no-cache";
add_header Cache-Control "no-cache, no-store, must-revalidate";
try_files /index.html =404;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
And here is my package.json for my Angular app, I am using Webpack but all the same my Docker file is executing the npm "scripts" section you would just change to use Angular CLI commands:
{
"name": "tsl-frontend",
"version": "0.1.0",
"scripts": {
"test": "karma start",
"build-localhost": "webpack --mode development --progress --colors --env.env localhost",
"build-development": "webpack --mode development --progress --colors --env.env development",
"build-staging": "node --max_old_space_size=5000 node_modules/webpack/bin/webpack.js --mode production --progress --colors --env.env staging",
"build-production": "node --max_old_space_size=4096 node_modules/webpack/bin/webpack.js --mode production --progress --colors --env.env production",
"build-maintenance": "webpack --mode production -p --progress --colors --env.env maintenance",
"serve": "webpack-dev-server --mode development --inline --progress --colors --env.env development",
"serve-production": "webpack-dev-server --mode production --inline --progress --colors --env.env production",
"serve-staging": "webpack-dev-server --mode production --inline --progress --colors --env.env staging",
"serve-localhost": "webpack-dev-server --mode development --inline --progress --colors --env.env localhost",
"serve-host": "webpack-dev-server --host 0.0.0.0 --port 80 --disable-host-check --mode development --inline --progress --colors --env.env localhost",
"serve-maintenance": "webpack-dev-server --mode development --inline --progress --colors --env.env maintenance"
},
"dependencies": {
"@angular/animations": "^7.0.0",
"@angular/cdk": "^7.0.0",
"@angular/common": "^7.0.0",
"@angular/compiler": "^7.0.0",
"@angular/compiler-cli": "^7.0.0",
"@angular/core": "^7.0.0",
"@angular/forms": "^7.0.0",
"@angular/http": "^7.0.0",
"@angular/material": "^7.0.0",
"@angular/platform-browser": "^7.0.0",
"@angular/platform-browser-dynamic": "^7.0.0",
"@angular/platform-server": "^7.0.0",
"@angular/router": "^7.0.0",
"@auth0/angular-jwt": "^2.1.0",
"@ng-bootstrap/ng-bootstrap": "^4.0.0",
"@types/file-saver": "^1.3.0",
"angular2-text-mask": "^8.0.5",
"bootstrap": "^4.1.2",
"chart.js": "^2.7.2",
"clipboard": "^2.0.1",
"devextreme": "^18.1.4",
"devextreme-angular": "^18.1.4",
"file-saver": "^1.3.8",
"font-awesome": "^4.7.0",
"jquery": "^3.3.1",
"jquery-ui": "^1.12.1",
"jquery.easing": "^1.4.1",
"moment": "^2.22.2",
"moment-timezone": "0.5.13",
"ng2-bootstrap-modal": "1.0.1",
"ng2-charts": "^1.6.0",
"ng2-drag-drop": "^2.9.2",
"ng2-page-scroll": "^4.0.0-beta.12",
"ng2-pdf-viewer": "^5.2.3",
"ngx-toastr": "^9.1.1",
"popper.js": "^1.14.3",
"raphael": "^2.2.7",
"reflect-metadata": "0.1.8",
"rxjs": "^6.3.3",
"rxjs-compat": "^6.3.3",
"systemjs": "0.19.40",
"typescript": "3.1.6",
"xlsx": "^0.11.19",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@ngtools/webpack": "^6.0.8",
"@servicestack/client": "^1.0.14",
"@types/file-saver": "^1.3.0",
"@types/jasmine": "^2.8.8",
"@types/jquery": "^3.3.6",
"@types/node": "7.0.7",
"angular-router-loader": "^0.6.0",
"angular2-router-loader": "^0.3.5",
"angular2-template-loader": "^0.6.2",
"b64-to-blob": "^1.2.19",
"babel-polyfill": "^6.26.0",
"css-loader": "^0.28.11",
"extended-define-webpack-plugin": "^0.1.3",
"file-loader": "^1.1.11",
"file-saver": "^1.3.8",
"html-webpack-plugin": "^4.0.0-beta.2",
"jasmine": "^2.99.0",
"karma": "^1.7.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.13",
"ng-intercom": "^1.0.0-beta.5-2",
"node-sass": "^4.11.0",
"open-browser-webpack-plugin": "0.0.5",
"path": "^0.12.7",
"raw-loader": "^0.5.1",
"sass-loader": "^6.0.7",
"style-loader": "^0.13.2",
"text-mask-addons": "^3.7.2",
"toposort": "^1.0.7",
"ts-loader": "^4.4.2",
"webpack": "^4.27.0",
"webpack-cli": "^3.1.1",
"webpack-del-plugin": "0.0.1",
"webpack-dev-server": "^3.1.10",
"webpack-merge": "^4.1.3",
"webpack-rev-replace-plugin": "^0.1.1",
"xml2js": "^0.4.19"
}
}