Reputation: 4559
I am trying to deploy an Angular 10 web app in Docker using NGINX, but I cannot get it to be served by it. Googling around I found a lot of sparse information, but I came up with this procedure:
normally build the Angular app for production (--prod
), outside Docker. The result gets saved into the dist
folder of the project, which then will be copied into a container. I found other examples which use multi-stage build inside the container; but this is discouraged for scalability purposes. At any rate, building the app manually gives me easier control on the result, so to me this is the way to go.
provide a Dockerfile using nginx:alpine
, where I just copy a custom configuration and my compiled app to the container, and then run it as an executable via ENTRYPOINT
:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
COPY dist/myapp/ .
ENTRYPOINT ["nginx", "-g", "daemon off;"]
This way, NGINX log should be redirected to the console, and thus shown in the container's terminal.
nginx.conf
, saved in the same folder of the Dockerfile
):worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html index.htm;
include /etc/nginx/mime.types;
gzip on;
gzip_min_length 1000;
gzip_proxied expired no-cache no-store private auth;
gzip_types text/plain text/css application/json application/javascript application/x-javascript text/xml application/xml application/xml+rss text/javascript;
location / {
try_files $uri $uri/ /index.html;
}
}
}
(I also found this other post, but it has a less complete configuration for Angular's purposes).
docker build . -t myrepo/myapp:tag
). When doing so, it took a lot of time for sending useless content outside the dist
folder. Thus, following a tip at https://denys.dev/2018-03-02/your-angular-apps-as-docker-containers/ ("optimizing the build"), I added a .dockerignore with:e2e
node_modules
src
This time, the build was much faster. In both cases it was successfully built.
I can now run the container, either directly (docker run -d -p 4200:80 myrepo/myapp:tag
), or from a docker compose script. In both cases, I will need to redirect the container's port 80 (NGINX) to the host port 4200 (the usual Angular app localhost port).
The problem: I'd expect to see my app served by NGINX at localhost:4200
. Instead, what I find is just the default welcome NGINX page, telling me that the NGINX server is successfully installed and working, but further configuration is required.
I looked for something in the logs, but docker logs mycontainer
displayed nothing.
I tried to inspect the container's filesystem to see if the Angular app is in its place (usr/share/nginx/html
). To this end, I followed the advice at Exploring Docker container's file system: for the alpine image there is no bash, so I dumped the filesystem into a TAR file with docker export mycontainer > mydump.tar
. By looking at the TAR's content I can see that my app files are there: index.html
, JS files, assets, and the like.
What am I missing here to complete the NGINX configuration and get my app served?
UPDATE
I followed @Derek's directions: deleted .gitignore
, replaced my nginx.conf
with the suggested one, rebuilt the app, and run it. This time the build took about 15 minutes, and sent 1.134 GB of build context to the Docker daemon.
To quickly run it in my test machine I published the image in the Docker Hub. The whole project is open source, so if anyone wants to diagnose something in a repro environment there is both the binary image and its source:
vedph2020/cadmus_web:1.0.5
(1.0.4
is the one with my nginx). Just use the docker-compose.yml
there, with tag 1.0.5
for the cadmus-web
service. There is some backend stuff there, but I am sure it works because I can connect to its endpoint at localhost:60380/swagger.cadmus_web
; thus the build was docker build . -t vedph2020/cadmus_web:1.0.5
).Meantime I tried to create an empty dummy Angular app, and use my original Dockerfile and nginx files. To my surprise it worked. The only difference is that the dummy Angular app had no routing, whereas mine has it with hash location strategy. Supposing some hash-related config issue in NGINX, I tried to access mine from http://localhost:4200/#/home, with no luck: same welcome page.
Upvotes: 5
Views: 5892
Reputation: 4559
I think I've found the problem: by looking at the filesystem dump, I noticed the default.conf
file; inspecting its content, I saw that there already was a location for localhost
at port 80. I was thinking that this should not be a problem, because my nginx.conf
file did not import (include
directive) it; but it seems that it still gets evaluated. At least, once I removed it in my Dockerfile:
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
RUN rm /etc/nginx/conf.d/default.conf
WORKDIR /usr/share/nginx/html
COPY dist/apps/cadmus/ .
EXPOSE 80
the app was now launched as expected. Hope this can help newbies like me, defining a clear procedure for serving Angular (I suppose anyway I could reintroduce the gzip
directive and even the .dockerignore
)...
Upvotes: 2
Reputation: 2367
I put you how I do my container... I see all it's the same but please delete the .dockerignore... you don't need to ignore src... etc when you only copy the dist file...
IMPORTANT
STRUCTURE
This need to be in the root of your app
src
dist
name_of_your_app
index.html //and the other files
e2e
node_modules
nginx.conf
Dockerfile
DOCKERFILE
FROM nginx:alpine
COPY nginx.conf /etc/nginx/nginx.conf
COPY dist/name_of_your_app/ /usr/share/nginx/html
EXPOSE 80
NGINX CONF
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
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;
keepalive_timeout 65;
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
}
build
docker build -t my_app .
run
docker run -d -p 4200:80 -t my_app
And that's all... maybe somem configuration, tags ... dockerignore it's your problem. Tell me if that solve your problem.
Upvotes: 3