My dockerized react app is ready for deployment but shuts down after 10 or so seconds in the production environment. I have restart: unless-stopped
in the docker-compose.prod.yml
file which makes it restart every 10 or so seconds.
I have tried adding std_in: true
and tty: true
to the docker-compose.prod.yml
file, as suggested, to no avail.
Docker Compose:
version: "3.8"
services:
db:
image: postgres:13-alpine
restart: unless-stopped
env_file:
- .env
volumes:
- ./data:/var/lib/postgresql/data
backend:
build:
context: ./backend
dockerfile: Dockerfile.prod
image: "${BACKEND_IMAGE}"
ports:
- 8000:8000
env_file: .env
volumes:
- static_volume:/backend/static
restart: unless-stopped
depends_on:
- db
# redis:
# image: redis:alpine
# restart: unless-stopped
# depends_on:
# - backend
# celery:
# build:
# context: ./backend
# image: "${BACKEND_IMAGE}"
# command: celery -A backend worker -l info
# env_file: .env
# volumes:
# - ./backend/:/usr/src/app/
# restart: unless-stopped
# depends_on:
# - redis
# celery-beat:
# build:
# context: ./backend
# image: "${BACKEND_IMAGE}"
# command: celery -A backend beat -l info
# env_file: .env
# volumes:
# - ./backend/:/usr/src/app/
# restart: unless-stopped
# depends_on:
# - redis
frontend:
image: "${FRONTEND_IMAGE}"
stdin_open: true
volumes:
- frontend_build:/frontend/build
restart: unless-stopped
depends_on:
- backend
env_file: .env
nginx:
image: "${NGINX_IMAGE}"
ports:
- 80:80
- 443:443
volumes:
- ./nginx/certbot/conf:/etc/letsencrypt
- ./nginx/certbot/www:/var/www/certbot
- static_volume:/backend/static
- frontend_build:/var/www/frontend
restart: unless-stopped
depends_on:
- backend
- frontend
- db
command: '/bin/sh -c ''while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g "daemon off;"'''
certbot:
image: certbot/certbot
restart: unless-stopped
volumes:
- ./nginx/certbot/conf:/etc/letsencrypt
- ./nginx/certbot/www:/var/www/certbot
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
volumes:
frontend_build:
static_volume:
media_volume:
Dockerfile.prod:
FROM node:16.13.0
# Create and set the working directory on the container
# then copy over the package.json and package-lock.json
WORKDIR /frontend
COPY package*.json ./
# Install the node packages before copying the files
RUN npm install
COPY . .
# Run the production build
CMD ["npm", "run", "build"]
docker logs [frontend_container]
:
The project was built assuming it is hosted at /.
You can control this with the homepage field in your package.json.
The build folder is ready to be deployed.
You may serve it with a static server:
npm install -g serve
serve -s build
Find out more about deployment here:
https://cra.link/deployment
> [email protected] build
> react-scripts build
Creating an optimized production build...
Thoughts on how to fix the issue? Could this be a permissions issue?
EDIT: I am serving with Nginx in a docker container:
upstream backend_server {
server backend:8000;
}
server {
listen 80;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location /api/ {
return 301 https://$host$request_uri;
}
location /admin/ {
return 301 https://$host$request_uri;
}
location /static/ {
return 301 https://$host$request_uri;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
listen [::]:443 ssl;
root /var/www/frontend;
server_name example.co www.example.co;
ssl_certificate /etc/letsencrypt/live/exmplae.co/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.co/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
location /api/ {
proxy_pass http://backend_server$request_uri;
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 $https;
proxy_connect_timeout 360s;
proxy_read_timeout 360s;
}
location /admin/ {
proxy_pass http://backend_server$request_uri;
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 $https;
proxy_connect_timeout 360s;
proxy_read_timeout 360s;
}
location /static/ {
alias /backend/static/;
}
location / {
try_files $uri /index.html;
}
}
2
Answers
So, the other answers are correct. The way I had it setup, the
npm run build
command is run, then the container exits. Also, as the other answers are correct, a multi-stage build seems to work best here for the production image - though I made some adjustments, for a better CI/CD pipeline, that I would like to share.First, I do not like merging my
frontend
build and mynginx
build as these are separate services. Nginx interacts with not only the frontend, but the backend as well and I would like to run frontend tests on my React code before building thenginx
image. Therefore I separated thefrontend
image and thenginx
image to help in my CI/CD pipeline.Here is my project tree structure:
First, I build a
frontend
image using aDockerfile.ci
file which runs tests using theCMD
option - theCMD
option is important here as it will be overridden if you pass anotherCMD
in aDockerfile
ordocker-compose.yml
file (which we will do later on):My GitHub Actions workflow runs the
frontend
build on push to the repo. If tests fail, it won't get to the next step - building thenginx
image. If tests pass, it will push the image to GitHub container registry and build thenginx
image.The
nginx
image uses thefrontend
image as abuild
image:This way the
docker-compose.ci
, which is used for building images and running tests, looks like this:This keeps a separation between the services! Hope someone finds this useful!
Once you have build your app, you should serve your app with a webserver like nginx. So, in your dockerfile add these lines to use an nginx docker image, copy your build into into and run it to the port 80.
This is the nginx.conf: