上下文
我最近设置了一个 PHP 应用程序,以便在连接到不同容器中的数据库的 Docker 容器中工作。
在生产中,我们使用单个容器环境,因为它仅连接到托管在其他地方的数据库。尽管如此,为了简化开发工作流程,我们决定在本地使用两个容器和 docker-compose。
问题
我们遇到的问题是,第一次通过 docker-compose up --build 构建并运行应用程序时,Composer 的 vendor
目录在容器,即使我们在 Dockerfile
中有一个特定的 RUN Composer install
行。一旦容器运行,我们就必须在容器内执行composer install
。
找到解决方案
经过大量搜索后,我们发现有两种可能的解决方案:
- 将 Docker 镜像的默认命令更改为以下内容:
bash -c "composer install && /usr/sbin/apache2ctl -D FOREGROUND"
- 或者简单地通过 docker-compose 的
command
将容器的默认命令覆盖到上面的命令.
不同之处在于,如果我们通过 docker-compose 覆盖该命令,则在将应用程序部署到我们的服务器时,它将无缝运行,因为它应该,但是当更改 Dockerfile
中的默认命令时每次我们部署时,它都会遭受 1 分钟左右的停机时间。
这在此过程中有所帮助:
一些(可能是错误的)结论
我的结论是,那一分钟的停机时间是由于容器必须在运行 Apache 服务器之前通过 Composer 安装所有依赖项,而不是简单地运行服务器。
此外,我从所有研究中得出的另一个结论是,docker-compose up --build
不会安装 Composer 依赖项的原因是因为我们在docker-compose.yml
覆盖容器中的目录。
这些有帮助:
实际问题
我希望有人能够阐明这一切,因为我不太明白到底发生了什么 - 为什么运行 docker-compose 不会安装 PHP 依赖项,但包括 composer install
在默认命令中会以及为什么将 composer install
添加到 docker-compose.yml
更好。此外,卷是如何造成这一切的,它是所有麻烦的真正原因吗?
我们当前的 docker 文件如下所示:
FROM php:7.1.27-apache-stretch
ENV DEBIAN_FRONTEND=noninteractive
# install some stuff, PHP, Apache, etc.
WORKDIR /srv/app
COPY . .
RUN composer install
RUN service apache2 restart
EXPOSE 80
CMD ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
我们当前的docker-compose.yml
如下所示:
version: '3'
services:
database:
image: mysql:5.7
container_name: container-cool-name
command: mysqld --user=root --sql_mode=""
ports:
- "3306:3306"
volumes:
- ./db_backup.sql:/tmp/db_backup.sql
- ./.docker/import.sh:/tmp/import.sh
environment:
MYSQL_DATABASE: my_db
MYSQL_USER: my_user
MYSQL_PASSWORD: password
MYSQL_ROOT_PASSWORD: test
app:
build:
context: .
dockerfile: Dockerfile
image: image-name
command: bash -c "composer install && /usr/sbin/apache2ctl -D FOREGROUND"
ports:
- 8080:80
volumes:
- .:/srv/app
links:
- database:db
depends_on:
- database
environment:
DB_HOST: db
DB_PORT: 3306
DB_DATABASE: my_db
DB_USER: my_user
DB_PASSWORD: password
最佳答案
您在 Dockerfile
中安装的第一个 composer
工作正常,并且您生成的镜像具有 vendor/
等。
但是稍后您从该镜像创建一个容器,并且该容器在执行时整个目录被主机目录挂载替换:
volumes:
- .:/srv/app
因此,您的 docker 镜像同时包含您的文件和已安装的供应商文件,但随后您将项目目录替换为主机上没有供应商文件的目录,最终结果看起来像是从未完成构建。
我的建议是:
- 不要将第二个命令构建添加到
Dockerfile
- 在容器中挂载单个文件夹,即不是
.:/srv/app
,而是./src:/srv/app/src
等。 - 或者,映射整个文件夹,但将供应商文件从镜像/容器复制到您的主机
- 或者使用一些第三方实用程序来解决这个问题,例如http://docker-sync.io或许多其他
关于php - docker-compose 覆盖容器中的目录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56690385/