工作区文件夹结构(=my_application
):
➜ tree -L 2
.
├── Dockerfile
├── docker-compose.yml
├── configs
│ ├── configure_all.sh
│ ├── create_connections.sh
│ ├── requirements
│ └── variables.json
...
docker-compose.yml
:
services:
my_service:
build: .
...
volumes:
- .:/opt/my_application
...
注意,整个工作空间目录都挂载在/opt/my_application
上。
FROM ubuntu
USER root
RUN apt-get update
&& apt-get install -y --no-install-recommends
build-essential gcc git wget unzip
&& apt-get autoremove -yqq --purge
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*
RUN sh /opt/my_application/configs/configure_all.sh
但是这引发了一个错误,因为/opt/my_application/
在构建时还没有挂载。
我知道我可以使用类似COPY
命令的东西,但它使我每次更改(或修复)configs
中的任何文件时都要构建图像,这真的很烦人。我想利用docker-compose.yml
的volumes
,它可以连续同步更改。
有什么办法可以做到吗?
你不能按照你描述的方式来做。从本质上讲,卷在构建映像之前不会被挂载。您可以在同一个映像上运行多个容器,可能会挂载不同(或没有)卷,并且映像不会重新构建。
…每次我更改(或修复)
configs
…中的任何文件时构建映像
将您的应用程序分成几个不同的部分可能会有所帮助:
- 你有应用程序本身,它不会改变,当它被重新运行。
- 你有
configs
,可以不同的运行。 - 你有应用程序运行时生成的任何数据,当它结束时可以丢弃。
你可以使用Docker绑定挂载注入配置,即使应用程序的其余部分已经"嵌入"了。到图像。
我建议做的第一件事,然后,在应用程序代码中更新你的Dockerfile到COPY
,RUN
任何你需要运行来构建它的命令,并设置默认的CMD
来运行应用程序。良好的第一步是在图像中内置configs
的情况下运行此操作。
设置的一个复杂之处在于注入的配置的一部分是一个可运行的脚本。由于这来自绑定挂载,因此在容器启动之前脚本将不可用。您可以使用入口点包装器脚本在容器启动时运行此脚本:提供一个shell脚本,作为映像的ENTRYPOINT
运行,执行任何初始设置,然后以exec "$@"
结束以运行映像的CMD
。
#!/bin/sh
# docker-entrypoint.sh
# If the configuration includes an initialization script, run it
if [ -x ./configs/configure_all.sh ]; then
./configs/configure_all.sh
fi
# Run the main container CMD
exec "$@"
(由于这是一个shell脚本,您可以做任何shell脚本可以做的事情。例如,如果脚本设置环境变量,您可以使用内置的.
在包含shell中设置它们;或者您可以根据是否设置了变量或是否存在文件来设置逻辑;或……)
在Dockerfile中,保持CMD
不变,但将此脚本添加为ENTRYPOINT
。
COPY docker-entrypoint.sh . # if a `COPY . .` doesn't already include it
ENTRYPOINT ["./docker-entrypoint.sh"] # must be JSON-array syntax
CMD the_application # same as before
最后,当您启动应用程序时,使用docker run -v
绑定挂载到,只有注入configs
目录;不要用绑定挂载覆盖主应用程序代码。
volumes:
- ./configs:/opt/my_application/configs
如果你想检查发生了什么,docker-compose run
启动一个交互式调试shell将取代CMD
而不是ENTRYPOINT
,这意味着安装包装器仍然会运行,但会启动交互式shell而不是主应用程序。
docker-compose run my_service
cat /opt/my_application/etc/output_of_configure_all.sh
# ^^^ runs _after_ the setup in the entrypoint script
# runs _instead of_ the default container CMD (or Compose command:)