Upload file to dockerized app on the fly, then serve - docker

I've got a Python web app that runs inside two Docker containers, one for the FastAPI backend, the other for the Vue.JS front-end (and there's besides a third one, with the Postgres Db). Now my task is to upload a file from inside the front-end client to the server, store it permanently and serve, so that I can use static URLs in my img tags.
A possible similar question is here: How to upload file outside Docker container in Flask app But it concerns an approach where the server must be restarted upon upload to reflect the changes. I need to do everything on the fly, as the app is running.
Dockerfile for front-end:
FROM node:16.14.2 as builder
WORKDIR /admin
COPY package*.json ./
COPY vite.config.js ./
RUN npm install
COPY . .
RUN npm run build
FROM nginx:1.21
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
RUN rm -rf /usr/share/nginx/html/*
COPY --from=builder /admin/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Dockerfile for backend:
FROM tiangolo/uvicorn-gunicorn:python3.9
EXPOSE 8000
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
COPY ./requirements.txt /tmp/requirements.txt
RUN pip install --no-cache-dir -r /tmp/requirements.txt
WORKDIR /app
COPY . /app
I can certainly transfer a file (as raw bytes stream) from the front-end to the backend via Axios and receive it on the backend Python side. I can then process it however I like. But how can I store it in the location where the front-end container could read it and serve statically?
UPDATE (Use Docker Volumes)
As suggested in this question, I've tried to use a shared volume for my purpose. My COMPOSE file now looks like this:
version: "3.8"
services:
use_frontend:
container_name: 'use_frontend'
# --> ADDED <--
volumes:
- 'myshare:/etc/nginx'
- 'myshare:/usr/share/nginx/html'
build:
context: ./admin
dockerfile: Dockerfile
restart: always
depends_on:
- use_backend
ports:
- 8090:80
use_db:
container_name: use_db
image: postgres:14.2
# etc etc...
use_backend:
container_name: 'use_backend'
volumes:
# --> ADDED <--
- 'myshare:/usr/share/nginx/html'
build:
context: ./api
dockerfile: Dockerfile
restart: always
depends_on:
- use_db
# etc etc...
# --> ADDED <--
volumes:
myshare:
driver: local
The Dockerfile for use_frontend hasn't changed (must it?)
FROM node:16.14.2 as builder
WORKDIR /admin
# copy out files for npm
COPY package*.json ./
COPY vite.config.js ./
# install and build Vue.js
RUN npm install
COPY . .
RUN npm run build
# Nginx image
FROM nginx:1.21
# copy Nginx conf file to VOLUME mounted folder
COPY ./nginx/nginx.conf /etc/nginx/nginx.conf
# clean everything in VOLUME mounted folder (app entry point)
RUN rm -rf /usr/share/nginx/html/*
# copy compiled app to to VOLUME mounted folder (app entry point)
COPY --from=builder /admin/dist /usr/share/nginx/html
# expose port 80 for HTTP access
EXPOSE 80
# run Nginx
CMD ["nginx", "-g", "daemon off;"]
The Nginx conf file hasn't changed either:
events {}
http {
server {
listen 80;
# app entry point
root /usr/share/nginx/html;
# MIME types
include /etc/nginx/mime.types;
client_max_body_size 20M;
location / {
try_files $uri /index.html;
}
# etc etc...
}
}
But after doing
docker compose build
docker compose up
I'm getting file not found errors from Nginx:
use_frontend | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
use_frontend | /docker-entrypoint.sh: Configuration complete; ready for start up
use_frontend | 2022/07/24 23:21:32 [emerg] 1#1: open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
use_frontend | nginx: [emerg] open() "/etc/nginx/nginx.conf" failed (2: No such file or directory)
What exactly am I doing wrong with the Docker volume mounting?

Related

Svelte Dockerize "Failed to load module script" in production

I have a svelte app running on my production instance, but when I access it I see a blanc HTML page. On my console I get the following error:
Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.
I have looked into other answers and google the problem but I couldn't find any solution that worked for me.
My app's pretty straightforward and it uses vite and svelte-routing.
To deploy I'm using docker-compose.
My Dockerfile:
FROM --platform=linux/amd64 node:16 as build
WORKDIR /app
COPY package.json ./
COPY package-lock.json ./
RUN npm install
COPY . ./
RUN npm run build
FROM nginx:1.19-alpine
COPY --from=build /app/dist /usr/share/nginx/html
My docker compose file (on my server):
version: '3.7'
services:
webapp-production:
image: "me/image:latest"
ports: [ "5000:80" ]
restart: "on-failure"
My default.conf (Nginx installed on the server):
server {
server_name example.com www.example.com;
location / {
proxy_pass http://localhost:5000;
include /etc/Nginx/conf.d/includes/proxy-vars.conf;
try_files $uri /index.html;
}
}
EDIT: Sometimes when I Empty cache and hard reload the app loads normally.
Thank you all in advance!

Unable to access the application with private external IP in Azure Kubernetes cluster

Below is the docker compose file and I am trying to deploy a react application container inside a pod:
version: "3"
services:
client:
build:
context: .
# dockerfile: Dockerfile
args:
- URI=http://localhost:6000
ports:
- 3000:3000
Below is the Docker file.I added the second stanza/snippet as previously I was not able to access the application via browser from my local laptop. But I am still thinking if EXPOSE 80 should be added or not as the yaml has a mention of container port 80 :
FROM node:12 as build-stage
WORKDIR /app
COPY package.json /app/
COPY package-lock.json /app/
RUN npm install
COPY ./ /app/
ARG URI
ENV REACT_APP_URI=$URI
RUN npm run build
FROM httpd:alpine
WORKDIR /var/www/html
COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf
COPY --from=build-stage /app/build/ .
The deployment yaml file is as below.In the below yaml has a Deployment and Service. The deployment would have two sidecar container for logs as count-log-1 and count-log-2.The service used to expose the application is loadbalancer .The file is pasted in below path:
https://pastebin.com/dM7p3fn7
Could someone please help?

Using docker to deploy Vue frontend and .net backend, frontend not resolving backend container name

I am trying to deploy a Vue app in the frontend and the .net core api in the backend. Using docker-compose file I have been to able to spin up the network and containers but I am struggling to have them communicate. I am pretty new to docker however, do understand that the Vue app dockerfile uses an Nginx base to feed the web app but it seems that may be affecting the network communication as the frontend container does not resolve the backend container name though it should since they are in the same virtual private network with the default bridge driver. When executing a bash shell in the frontend container and doing a curl to the backend container, I do get the correct response.
I did try to expose the backend container to the host and then use the server IP to reach it from the frontend and that does work however, I was hoping to not have to expose my api this way and wanted to make it work through the docker virtual private network if possible.
Example url I am trying in the frontend which encounters a name not resolved error: littlepiggy-api/api or littlepiggy-api:5000/api
Frontend Dockerfile
FROM node:14.18-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY ./ .
RUN npm run build
# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Backend Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
WORKDIR /app
EXPOSE 5000
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["LittlePiggy.Api/LittlePiggy.Api.csproj", "LittlePiggy.Api/"]
RUN dotnet restore "LittlePiggy.Api/LittlePiggy.Api.csproj"
COPY . .
WORKDIR "/src/LittlePiggy.Api"
RUN dotnet build "LittlePiggy.Api.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "LittlePiggy.Api.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "LittlePiggy.Api.dll"]
Docker-compose file
version: '3'
services:
front:
container_name: littlepiggy-front
image: josh898/angie-app-front:latest
ports:
- 80:80
networks:
- littlepiggy
depends_on:
- api
api:
container_name: littlepiggy-api
image: josh898/angie-app-backend:latest
networks:
- littlepiggy
networks:
littlepiggy:
driver: bridge
You need to configure Nginx to pass requests that match the /api route on to the backend service.
If you create a nginx configuration file like this, called nginx.conf and place it in your frontend directory
server {
listen 80;
location / {
index index.html;
root /usr/share/nginx/html;
try_files $uri $uri/ $uri.html =404;
}
location /api/ {
proxy_pass http://littlepiggy-api/;
}
}
Then copy it into your frontend container by changing your frontend Dockerfile to
FROM node:14.18-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY ./ .
RUN npm run build
# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
Then requests to http://localhost/api/xxxx should be passed on to the backend and requests to http://localhost/index.html should be served by the Nginx container directly.

Dockerfile returns built dist folder

I have simple vue.js app, and I wanted to use Dockerfile to build it:
FROM node:14.14.0-stretch
WORKDIR '/app'
COPY package*.json ./
RUN npm install
COPY . ./
RUN env
RUN npm run generate
is it possible now, using docker-compose, and in compose not build it, but use already prepared image, to get from volume dist folder ? so I could copy it to nginx ?
I suggest that you wanna serve dist folder using nginx.
You should use multi stage build for this https://docs.docker.com/develop/develop-images/multistage-build/
Dockerfile
FROM node:14.14.0-stretch as build
WORKDIR '/app'
COPY package*.json ./
RUN npm install
COPY . ./
RUN env
RUN npm run generate
# create cdn stage from nginx image
FROM nginx:stable-alpine as cdn
# copy nginx config to serve files from /data/www
COPY nginx.conf /etc/nginx/nginx.conf
# copy built files from build stage to /data/www
COPY --from=build /app/dist /data/www
# nginx listen on port 80
EXPOSE 80
CMD [ "nginx" ]
nginx.conf
events {
worker_connections 1024;
}
http {
server {
location / {
root /data/www;
}
}
}
docker-compose.yml
version: '2.4'
services:
nginx-cdn:
build:
context: path/to/Dockerfile
target: cdn
ports:
- '80:80'

Use Traefik with Docker Compose to serve 2 services on the same host but different routes

I have 2 frontend apps, both are served by Nginx in port 80
app-login
app-contacts
I'm trying to configure Traefik with Docker Compose to achieve this:
Both apps are served on the same host (for example abc.com or localhost:4000)
app-login will be accessed from localhost:4000/login (or abc.com/login)
app-contacts will be accessed from localhost:4000/contacts (or abc.com/contacts)
But the problem is: it doesn't work with what I did so far.
Below are 2 Dockerfile of 2 apps and the docker-compose.yml
app-login Dockerfile:
# build stage
FROM node:lts-alpine as build-stage
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# production stage
FROM nginx:stable-alpine as production-stage
COPY --from=build-stage /app/dist /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
app-contacts Dockerfile: (a bit more complicated but it's just similar to login app)
### STAGE 1: Build the AngularJS app ###
FROM node:lts-alpine as build-stage
RUN apk --no-cache add git
WORKDIR /app
COPY package.json /app/
RUN npm install
COPY . .
# Production mode build
RUN npm run build:prod
### STAGE 2: Add Nginx for hosting the AngularJS app ###
FROM nginx:stable-alpine as production-stage
# Removes the default nginx html files
RUN rm -rf /usr/share/nginx/html/*
# Copy the bundle
COPY --from=build-stage /app/dist /usr/share/nginx/html
# Copy the default nginx.conf
COPY --from=build-stage /app/nginx.conf /etc/nginx/conf.d/default.conf
VOLUME ["/usr/share/nginx/html/env"]
EXPOSE 80
# Copy .env file and shell script to container
WORKDIR /usr/share/nginx/html
COPY ./scripts/generate_env.sh .
COPY .env .
# Add bash
RUN apk add --no-cache bash
# Make our shell script executable
RUN chmod +x generate_env.sh
# Start Nginx server
CMD ["/bin/bash", "-c", "/usr/share/nginx/html/generate_env.sh && nginx -g \"daemon off;\""]
docker-compose.yml
version: '3'
services:
reverse-proxy:
# The official v2 Traefik docker image
image: traefik:v2.2
# Enables the web UI and tells Traefik to listen to docker
command: --api.insecure=true --providers.docker
ports:
# The HTTP port
- "80:80"
# The Web UI (enabled by --api.insecure=true)
- "8080:8080"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
app-login:
image: app-login
labels:
- "traefik.http.routers.app-login.rule=Path(/login)"
app-contacts:
image: app-contacts
labels:
- "traefik.http.routers.app-contacts.rule==Path(/contacts)"
Not sure where I did wrong.
I searched Google and somewhere I see that people are using https://www.getambassador.io/ with this stack, but I don't know how to configure it either.
Thank you.
I found the issue
Based on this link https://docs.traefik.io/routing/routers/#rule
Backticks or Quotes?
To set the value of a rule, use backticks ` or escaped double-quotes \".
Single quotes ' are not accepted as values are Golang's String Literals.
So the rule must be
"traefik.http.routers.app-contacts.rule==Path(`/contacts`)"
By default, Traefik listen on port 80. You have to replace "81:81" by "81:80". In you web browser, you have to use port 81, it will be mapped to 80 in Traefik container.
EDIT:
There are several syntax errors in your labels.
== instead of = in last line
Paths should be quoted with ``
If you check the Traefik Dashboard (http://localhost:8080/), you can see routers status and configuration errors.
With your current configuration, NGINX should take requets on path /login and /contacts not the default /. You can use below labels, the 2 additonal lines are useful if you want to remove the path part (login or contacts) before forwarding to the NGINX so you NGINX listen on default /.
app-login:
image: app-login
labels:
- "traefik.http.routers.app-login.rule=PathPrefix(`/login`)"
- "traefik.http.routers.app-login.middlewares=app-login-stripprefix"
- "traefik.http.middlewares.app-login-stripprefix.stripprefix.prefixes=/login"
app-contacts:
image: app-contacts
labels:
- "traefik.http.routers.app-contacts.rule=PathPrefix(`/contacts`)"
- "traefik.http.routers.app-contacts.middlewares=app-contacts-stripprefix"
- "traefik.http.middlewares.app-contacts-stripprefix.stripprefix.prefixes=/contacts"

Resources