0

I'm new to Docker and can't find a container example for my development setup. I have an Angular 8 frontend that is in a folder labeled 'Web'. I have a .NET Core backend folder labeled 'API'. To run the application locally on my computer I go into the API folder and run 'dotnet run' command, then in the Web folder I do 'ng serve'. In a separate folder labeled 'library' I have dependencies for both .NET Core and Angular. I'm using Node 12.13 and angular-cli 8.13.19.

How would I set up a Dockerfile for an existing project that would run the above commands in the above folders? Is it better to have separate containers for .NET Core and Angular or should I combine them into one?

UPDATE: I'll explain further why I want to use Docker. As part of a six person dev team, I want us to be using the same versions of Node, Angular, and .NET Core and the accompanying dependencies. I'm thinking it makes sense to have a Angular/Node container and a .NET Core container. Right now the application's front end is already setup to communicate to the backend using 'localhost:5000'. The Web and API folders are next to each other in the same Git repository and the library folder is a Git submodule in the same repository. I think I would need to create two Dockerfiles and a Docker Compose file. The closest example I found was this:https://mherman.org/blog/dockerizing-an-angular-app/

mdailey77
  • 1,673
  • 4
  • 26
  • 52
  • What have you tried so far? Docker is really just a way to containerize things so you could really set them up however you want. I would suggest spending some time on the Docker documentation getting familiar with how it works / the APIs. For instance you could have two containers, one for .NET & one for Node.js and use docker-compose. – Scott Sword Nov 23 '19 at 23:43

1 Answers1

4

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"
  }
}
Brian Ogden
  • 18,439
  • 10
  • 97
  • 176
  • Thanks for your response. This is what I'm going for: [https://mherman.org/blog/dockerizing-an-angular-app/](https://mherman.org/blog/dockerizing-an-angular-app/) but add .NET to it. – mdailey77 Nov 24 '19 at 17:33
  • 1
    @mdailey77 Nice, just note that ng serve is not suited for production needs, it will not handle many requests – Brian Ogden Nov 24 '19 at 23:54