Docker Compose:exec: "docker-deploy.sh": executable file not found in $PATH: unknown

回答 2 浏览 1571 2022-08-18

我的项目的目录结构。

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

其中wait-for-it.shwait-for-it脚本的一个副本。

我的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"]

docker-deploy.sh则是。

#!/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

还有我的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:

以及我的.env是:

DB_PASSWORD=1234
DB_USER=root
DB_PORT=5432

当我从项目根目录运行下面的命令行时。

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

我得到的是。

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.

发生了什么事?错误是来自wait-for-it.sh脚本本身,还是来自Dockerfile中配置不当的CMD指令,还是来自以my-service运行的实际Node/JS应用?

更新信息

应用@ErikMD建议的修改后,出现了最新的错误。

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.

因此,它在启动数据库(postgres)方面没有问题,但由于某些原因,在docker-deploy.sh脚本方面仍然出现了与权限有关的问题。

hotmeatballsoup 提问于2022-08-18
实际上,这似乎是你的容器试图运行类似于node ./wait-for-it.sh的东西,你能试着用ENTRYPOINT而不是CMD吗?derpirscher 2022-08-19
另外,你是否检查过你的wait-for-it.sh是否真的是一个可执行文件?如果不是,CMD ["./wait-for-it.sh", ...]可能不会被正确地解释为CMD的"执行形式",而会被解释为"param from",因此,所有的参数都会传递给容器的默认ENTRYPOINT,也就是node可执行文件(这将符合观察到的行为)。derpirscher 2022-08-19
谢谢,我已经尝试让它可执行,以及引入一个docker-deploy.sh脚本并从ENTRYPOINT中调用它,这似乎是一种常见的做法,但仍然有问题!我已经尝试了。hotmeatballsoup 2022-08-23
@hotmeatballsoup 在你的编辑后,你的ENTRYPOINT看起来很奇怪,请尝试ENTRYPOINT ["./docker-deploy.sh"]ErikMD 2022-08-23
发现这个这个,可以解释这个问题。你应该检查所描述的解决方法在你的情况下是否有效。Lety 2022-08-28
2 个回答
#1楼 已采纳
得票数 4

正如@derpirscher的评论中所指出的,其中一个问题是你的脚本(s)的权限,以及它们应该作为ENTRYPOINT(而不是CMD)被调用的方式。

可以考虑在你的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 脚本。

#!/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

请参阅此另一个SO问题,以了解更多关于Docker shell入口中需要exec内建程序的背景。

另外,请注意,这个exec ...命令行是在shell脚本中写的(而不是直接在ENTRYPOINT / CMDexec形式中),这是使用参数扩展的一个关键要素。
换句话说:在你问题的修订版2中,"${DB_HOST}:${DB_PORT}"参数被理解为字面意思,因为在ENTRYPOINT / CMDexec形式中没有发生shell插值的情况。

关于docker-compose.yml .NET的问题。

# 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

注意,在这个Docker设置中,wait-for-it主机应该是postgres(你的数据库的Docker服务名称),而不是0.0.0.0localhost。因为wait-for-it脚本作为一个客户端,试图连接到环境中docker-compose网络的指定网络服务。

关于0.0.0.0(一个服务器端的、包罗万象的特殊IP)和localhost在Docker上下文中的区别的更多细节,请参见例如我的这个其他SO答案

(§): 最后但并非最不重要的是,ports: [ "${DB_PORT}:${DB_PORT}" ]行应该被删除,因为它们对于合成器服务的通信并不是必需的(服务只需要属于一个共同的合成器网络并使用其他合成器服务的主机名),而在主机上直接暴露一个这样的端口会增加攻击的表面。

最后但并非最不重要的:

为了跟进我的这个评论,建议在你的myapp/目录下运行ls -l docker-deploy.sh; file docker-deploy.sh作为调试步骤(BTW:以后随意做,然后评论,以记录在案)。

假设Docker中可能存在类似于这个的意外错误,正如@Lety所指出的那样。

我建议只需替换(在Docker文件中)这一行

RUN chmod +x wait-for-it.sh \
  && chmod +x docker-deploy.sh \
  && npm install --legacy-peer-deps

RUN npm install --legacy-peer-deps

直接在主机上的终端中运行。

cd myapp/
chmod -v 755 docker-deploy.sh
chmod -v 755 wait-for-it.sh

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

如果这不起作用,这里有另一个有用的信息,你可能想提供:你的操作系统是什么,你的Docker包名称是什么?(如docker-ce或podman......)。

ErikMD 提问于2022-08-23
ErikMD 修改于2022-08-29
推荐可执行程序/usr/bin/env或bash shell用于容器内环境是不理智的。通常的期望是/bin/sh的存在(对于linux容器,谁愿意使用其他东西,对吗?)参数扩展在Docker文件中也是可用的,在文件的层面上有构建参数(docker-compose确实发挥得很好),在构建过程中的容器环境也是如此。只是说说而已。hakre 2022-08-24
谢谢@ErikMD(+1)的精彩回答。我应用了你所有的修改,并运行了docker-compose --env-file .env up --build,得到了新的错误,请看我上面的更新!看起来docker-deploy.sh仍然得到与权限有关的错误。也许也有类似chmod 400的情况?再次非常感谢!hotmeatballsoup 2022-08-24
或者是用sudo exec ...来调用wait-for-it.sh? 我想知道目前是哪个操作系统的用户在运行exechotmeatballsoup 2022-08-24
@hakre 我相信你的建议既是正确的(事实上/bin/sh总是可用的),也是主观的(事实上我们应该总是避免bash),的确,鉴于OP对基础图像有完全的控制,很容易检查bashnode:16中是否可用。然而,我同意使用/bin/bash/usr/bin/env bash(通常推荐以最大限度地提高可移植性)是相当的,而且前者要简单得多......将相应地编辑我的答案。ErikMD 2022-08-24
@hotmeatballsoup 我无法重现你的错误,除非我在我的minimal-reproducible-example Dockerfile中注释掉RUN chmod +x docker-deploy.sh命令(→然后我得到«docker: Error response from daemon: failed to create shim task:OCI runtime create failed: runc create failed: unable to start container process: exec: "./docker-deploy.sh": permission denied: unknown. »)。所以,你能仔细检查一下,你确实在你的Docker文件中运行了这个命令,而且你测试的是正确的镜像?(BTW,你的操作系统和Docker包的名字是什么?)ErikMD 2022-08-24
#2楼
得票数 0

这有点像在你的电脑上使用的shell。你输入要执行的命令(只是命令的基本名称),如果找不到它,shell会告诉你--只不过它不会告诉你它不在$PATH的范围内,因为它可能是(比较hash实用)。

现在,Docker不是一个shell,因此信息要更啰嗦一些(而且docker-compose正在运行docker也会加在前面)。

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

因此,Docker的部分内容是。

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

而这实际上是由于Docker文件中的戒律。

ENTRYPOINT ["docker-deploy.sh"]

无论docker-deploy.sh在容器中的(绝对)路径是什么,它的基名(docker-deploy.sh,再次)无法在容器环境PATH参数中找到(比较PATH(在)Pathname Resolution,等等)。

使用可执行文件的基本名称,该文件实际上在容器内的PATH或绝对值(或相对于容器的PWD环境参数,又称工作目录),以便它实际上可以被执行(由Docker执行)。

hakre 提问于2022-08-23
hakre 修改于2022-08-24