Home > Blockchain >  How to share and run an app with docker-compose
How to share and run an app with docker-compose

Time:09-22

After spending hours to make it happen, I just can't make it work. I'm desperate for help as I couldn't find any questions related to my issue.

I've developed a Node.js web app for my university. IT department needs me to prepare a Docker image shared on a Docker Hub (although I chose Github Packages) and a docker-compose file so it can be easily run. I tried to host the app on my Raspberry Pi, but when I pull the image (with docker-compose.yaml, Dockerfile and .env present) it fails during build process:

npm ERR! enoent ENOENT: no such file or directory, open '/usr/src/app/package.json'

and during compose up process:

pi@raspberrypi:~/projects $ docker-compose up
Starting mysql ... done
Starting backend ... done
Attaching to mysql, backend
backend    | exec /usr/local/bin/docker-entrypoint.sh: exec format error
mysql      | 2022-09-22 08:04:47 00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.30-1.el8 started.
mysql      | 2022-09-22 08:04:48 00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
mysql      | 2022-09-22 08:04:48 00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.30-1.el8 started.
mysql      | '/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
backend exited with code 1

I executed bash inside my Docker container (on my dev machine) so I'm sure that /usr/src/app folder structure matches my app folder structure.

What's wrong with my solution? Should I provide more files than just docker-compose.yaml, Dockerfile and .env?


Dockerfile:

FROM node:18-alpine

WORKDIR /usr/src/app

COPY . ./

RUN npm i && npm cache clean --force
RUN npm run build
ENV NODE_ENV production

CMD [ "node", "dist/main.js" ]

EXPOSE ${PORT}

docker-compose.yaml:

version: "3.9"

services:
  backend:
    command: npm run start:prod
    container_name: backend
    build:
      context: .
      dockerfile: Dockerfile
    image: ghcr.io/rkn-put/web-app/docker-backend/test
    ports:
      - ${PORT}:${PORT}
    depends_on:
      - mysql
    environment:
      - NODE_ENV=${NODE_ENV}
      - PORT=${PORT}
      - ORIGIN=${ORIGIN}
      - DB_HOST=${DB_HOST}
      - DB_PORT=${DB_PORT}
      - DB_NAME=${DB_NAME}
      - DB_USERNAME=${DB_USERNAME}
      - DB_PASSWORD=${DB_PASSWORD}
      - DB_SYNCHRONIZE=${DB_SYNCHRONIZE}
      - EXPIRES_IN=${EXPIRES_IN}
      - SECRET=${SECRET}
      - GMAIL_USER=${GMAIL_USER}
      - GMAIL_CLIENT_ID=${GMAIL_CLIENT_ID}
      - GMAIL_CLIENT_SECRET=${GMAIL_CLIENT_SECRET}
      - GMAIL_REFRESH_TOKEN=${GMAIL_REFRESH_TOKEN}
      - GMAIL_ACCESS_TOKEN=${GMAIL_ACCESS_TOKEN}

  mysql:
    image: mysql:latest
    container_name: mysql
    hostname: mysql
    restart: always
    ports:
      - ${DB_PORT}:${DB_PORT}
    environment:
      - MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
      - MYSQL_DATABASE=${DB_NAME}
      - MYSQL_USER=${DB_USERNAME}
      - MYSQL_PASSWORD=${DB_PASSWORD}
    volumes:
      - ./mysql:/var/lib/mysql
    cap_add:
      - SYS_NICE

CodePudding user response:

Even if this is not a clear solution, there are multiple things that you should fix (and understand) and then things should work.

You say: "but when I pull the image (with docker-compose.yaml, Dockerfile and .env present) it fails during build process". This is actually where the biggest confusion happens. If you pull, there should be no build anymore.

You build locally, you push with docker-compose push and the image that you have in Github is ready to use. Because of this, on the target machine (where you want to run the project) you don't need to build any more - therefor you don't need a Dockerfile anymore.

The docker-compose.yml that you deliver should not have the build section for your app. Only the image name so that docker-compose knows where to pull the image from.

In local (your development environment) you should have the same docker-compose.yml without the build section, but also a file docker-compose.override.yml that should look like:

version: "3.9"

services:
  backend:
    build:
      context: .

docker-compose automatically merges docker-compose.yml and docker-compose.override.yml when it finds the second one. That's also why it is important to not deliver the override file.

Only this should make your application work on the target machine. Remember all you need there is docker-compose.yml (no build section) and the .env.

Other points that you might want to address:

  • dockerfile: Dockerfile - not needed since that is the default
  • command: npm run start:prod if you overwrite it, why not just put it this way in the Dockerfile? If you have a good reason to do this then leave it
  • EXPOSE ${PORT} you are not declaring PORT anywhere in your Dockerfile. Just make your run on port 80 and expose port 80.
  • read the docs and save yourself some typing. if the env variables have the same names as in .env then docker-compose is clever enough to pick them if you only declare them
  • don't expose mysql ports on host: ${DB_PORT}:${DB_PORT}
  • consider using a volume for mysql instead of a folder. If you use a folder maybe place is in a different location so that you don't delete it by mistake
  • Related