简体   繁体   中英

Docker Compose: executable file not found in $PATH: unknown

My project directory structure:

myapp/
    src/
    Dockerfile
    docker-compose.yml
    docker-deploy.sh
    wait-for-it.sh
    .env

Where wait-for-it.sh is a copy of the famous wait-for-it script.

My Dockerfile :

FROM node:16

WORKDIR /usr/src/app

COPY package*.json ./
COPY wait-for-it.sh ./
COPY docker-deploy.sh ./

RUN chmod +x docker-deploy.sh

RUN npm install --legacy-peer-deps

COPY . .

RUN npm run build

ENTRYPOINT ["docker-deploy.sh"]

And docker-deploy.sh is:

#!/bin/bash

# make wait-for-it executable
chmod +x wait-for-it.sh

# call wait-for-it with passed in args and then start node if it succeeds
bash wait-for-it.sh -h $1 -p $2 -t 300 -s -- node start

And my docker-compose.yml :

version: '3.7'

services:
  my-service:
    build: .
  postgres:
    container_name: postgres
    image: postgres:14.3
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_DB: my-service-db
      PG_DATA: /var/lib/postgresql2/data
    ports:
      - ${DB_PORT}:${DB_PORT}
    volumes:
      - pgdata:/var/lib/postgresql2/data
volumes:
  pgdata:

And where my .env looks like:

DB_PASSWORD=1234
DB_USER=root
DB_PORT=5432

When I run the following command-line from the project root:

docker-compose --env-file .env up --build

I get:

Creating myapp_my-service_1 ... error

Creating postgres                    ... 
Creating postgres                    ... done

ERROR: for my-service  Cannot start service my-service: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "docker-deploy.sh": executable file not found in $PATH: unknown

ERROR: Encountered errors while bringing up the project.

What is going on? Is the error coming from the wait-for-it.sh script itself, from a poorly configured CMD directive in the Dockerfile , or from the actual Node/JS app running as my-service ?

Update

Latest errors after applying @ErikMD's suggested changes:

Creating postgres ... done
Creating myapp_my-service_1 ... error

ERROR: for myapp_my-service_1  Cannot start service my-service: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "./docker-deploy.sh": permission denied: unknown

ERROR: for my-service  Cannot start service my-service: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "./docker-deploy.sh": permission denied: unknown
ERROR: Encountered errors while bringing up the project.

So it is spinning up the DB ( postgres ) no problem but is still for some reason getting permissions-related issues with the docker-deploy.sh script.

As pointed out in @derpirscher 's comment and mine, one of the issues was the permission of your script(s) and the way they should be called as the ENTRYPOINT (not CMD ).

Consider this alternative code for your Dockerfile :

FROM node:16

WORKDIR /usr/src/app

COPY package*.json ./
COPY wait-for-it.sh ./
COPY docker-deploy.sh ./

# Use a single RUN command to avoid creating multiple RUN layers
RUN chmod +x wait-for-it.sh \
  && chmod +x docker-deploy.sh \
  && npm install --legacy-peer-deps

COPY . .

RUN npm run build

ENTRYPOINT ["./docker-deploy.sh"]

docker-deploy.sh script:

#!/bin/sh

# call wait-for-it with args and then start node if it succeeds
exec ./wait-for-it.sh -h "${DB_HOST}" -p "${DB_PORT}" -t 300 -s -- node start

See this other SO question for more context on the need for the exec builtin in a Docker shell entrypoint.

Also, note that the fact this exec... command line is written inside a shell script (not directly in an ENTRYPOINT / CMD exec form) is a key ingredient for using the parameter expansion.
In other words: in the revision 2 of your question , the "${DB_HOST}:${DB_PORT}" argument was understood literally because no shell interpolation occurs in an ENTRYPOINT / CMD exec form.

Regarding the docker-compose.yml :

# version: '3.7'
# In the Docker Compose specification, "version:" is now deprecated.

services:
  my-service:
    build: .
    # Add "image:" for readability
    image: some-optional-fresh-tag-name
    # Pass environment values to the entrypoint
    environment:
      DB_HOST: postgres
      DB_PORT: ${DB_PORT}
      # etc.
    # Add network spec to make it explicit what services can communicate together
    networks:
      - postgres-network
    # Add "depends_on:" to improve "docker-run scheduling":
    depends_on:
      - postgres

  postgres:
    # container_name: postgres # unneeded
    image: postgres:14.3
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USER}
      POSTGRES_DB: my-service-db
      PG_DATA: /var/lib/postgresql2/data
    volumes:
      - pgdata:/var/lib/postgresql2/data
    networks:
      - postgres-network
    # ports:
    #   - ${DB_PORT}:${DB_PORT}
    # Rather remove this line in prod, which is a typical weakness, see (§)

networks:
  postgres-network:
    driver: bridge

volumes:
  pgdata:
    # let's be more explicit
    driver: local

Note that in this Docker setting, the wait-for-it host should be postgres (the Docker service name of your database), not 0.0.0.0 nor localhost . Because the wait-for-it script acts as a client that tries to connect to the specified web service in the ambient docker-compose network.

For a bit more details on the difference between 0.0.0.0 (a server-side, catch-all special IP) and localhost in a Docker context, see eg this other SO answer of mine .

(§) : last but not least, the ports: [ "${DB_PORT}:${DB_PORT}" ] lines should rather be removed because it is not necessary for the Compose services to communicate (they just need to belong to a common Compose network and use the Compose service hostname), while exposing one such port directly on the host increases the attack surface.

It's a bit like in the shell on your computer: You enter the command to execute (just a basename of the command) and the shell tells you if it can't find it - except it won't tell you its not withing $PATH as it could be (compare hash utility ).

Now Docker ain't a shell and therefore the message is a bit more verbose (and that docker-compose is running docker is also adding in front of it):

ERROR: for my-service Cannot start service my-service: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "docker-deploy.sh": executable file not found in $PATH: unknown

So the part of Docker is:

OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "docker-deploy.sh": executable file not found in $PATH: unknown

And that is effectively due to the commandment in the Dockerfile:

 ENTRYPOINT ["docker-deploy.sh"]

Whatever the (absolute) path of docker-deploy.sh is within the container, the basename of it ( docker-deploy.sh , again) could not be found within the containers environment PATH parameter (compare PATH (in) , Pathname Resolution , etc.).

Use the basename of an executable that is actually in PATH within the container or an absolute (or relative to the containers PWD environment parameter aka working directory ) so that it actually can be executed (by Docker).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM