使用docker compose构建多阶段DockerFile时,如何保留所有工件?
我正在做一个使用多阶段Dockerfile的项目。这对CircleCI很有效,但我对Docker不是很有经验,所以我在本地运行时遇到了问题。从文件中: 第二个FROM指令使用 阿尔卑斯山:以最新图片为基础。复制--from=0行仅复制 从上一个阶段到这个新阶段的构建工件。前进 SDK和任何中间构件都会被留下,而不会保存在中 最后的图像 如果我理解正确,这意味着如果我在第1阶段安装了一组依赖项,在第2阶段安装了另一组依赖项,那么在运行使用docker compose构建多阶段DockerFile时,如何保留所有工件?,docker,docker-compose,dockerfile,Docker,Docker Compose,Dockerfile,我正在做一个使用多阶段Dockerfile的项目。这对CircleCI很有效,但我对Docker不是很有经验,所以我在本地运行时遇到了问题。从文件中: 第二个FROM指令使用 阿尔卑斯山:以最新图片为基础。复制--from=0行仅复制 从上一个阶段到这个新阶段的构建工件。前进 SDK和任何中间构件都会被留下,而不会保存在中 最后的图像 如果我理解正确,这意味着如果我在第1阶段安装了一组依赖项,在第2阶段安装了另一组依赖项,那么在运行docker compose up之后,我将只看到安装的一些依赖
docker compose up
之后,我将只看到安装的一些依赖项。这就是当地正在发生的事情。但是,为了连接到容器并运行开发服务器,我需要安装所有项目依赖项。我在这里读到,可以将--rm=false
参数用于build命令(我不确定这是指docker build
,docker compose build
,还是有任何区别)。我试图在docker compose中添加一个rm
设置,但显然它不存在
version: "3.8"
services:
app:
container_name: my.container
build:
context: .
dockerfile: Dockerfile.dev
rm: false
entrypoint: sh -c
command: tail -f /dev/null
stdin_open: true
tty: true
app2:
...
我的Dockerfile大致如下所示:
ARG PYTHON_VERSION=3.8-alpine3.12
FROM python:$PYTHON_VERSION AS base
ARG USER=myuser
# Install application run-time dependencies.
RUN addgroup -S ${USER} && \
adduser -S -g ${USER} ${USER} && \
apk add --no-cache \
--upgrade \
bash && \
rm -rf /var/cache/apk/*
FROM base AS builder
RUN mkdir /install
RUN mkdir /e2e_install
WORKDIR /install
# Install application build-time dependencies.
RUN apk update && \
apk add --no-cache --upgrade \
bash \
build-base \
libffi-dev \
pcre-dev \
linux-headers \
zlib-dev \
python3-dev \
openssh \
openssh-keygen \
git \
gcc \
musl-dev && \
rm -rf /var/cache/apk/*
# Install project and test dependencies.
RUN pip install --no-cache-dir --disable-pip-version-check pip==20.0.2
COPY requirements.txt /tmp/requirements.txt
RUN pip install --no-cache-dir \
--no-warn-script-location \
--prefix=/install \
--disable-pip-version-check -r /tmp/requirements.txt
COPY test_requirements.txt /tmp/test_requirements.txt
RUN pip install --no-cache-dir \
--ignore-installed \
--no-warn-script-location \
--prefix=/install \
--disable-pip-version-check -r /tmp/test_requirements.txt
# install e2e-test dependencies
COPY e2e/requirements.txt /tmp/e2e_test_requirements.txt
RUN cat /tmp/e2e_test_requirements.txt
RUN pip install --no-cache-dir \
--ignore-installed \
--disable-pip-version-check \
--no-warn-script-location \
--prefix=/e2e_install \
-r /tmp/e2e_test_requirements.txt
# Tag version number into source code.
ARG VERSION_NUMBER=unversioned
WORKDIR /source
COPY . .
RUN sed -i -e "s/__version__ = \"unversioned\"/__version__ = \"${VERSION_NUMBER}\"/" external_api/__init__.py
# -------------------- e2e Image --------------------
FROM base as e2e-test
ARG USER=myuser
COPY --from=builder /e2e_install /usr/local
WORKDIR /opt/company/myapp
COPY --from=builder /source/e2e ./e2e
COPY --from=builder /source/scripts/entry_point.sh .
# Set file owner group,
RUN chown -R ${USER}:${USER} /opt/company
ENTRYPOINT ["./entry_point.sh"]
# -------------------- Production Image --------------------
FROM base
ARG USER=myuser
# Copy necessary files from builder.
WORKDIR /opt/company
COPY --from=builder /install /usr/local
COPY --from=builder /source .
# Set file owner group,
RUN chown -R ${USER}:${USER} /opt/company
# Run unit tests.
RUN PYTHONPATH=. pytest tests/unit_test --cov=. --cov-report=xml
COPY ./scripts/entry_point.sh .
ENTRYPOINT ["./entry_point.sh"]
当我运行docker compose build
时,我会得到一系列的结果:
Step 20/26 : ARG VERSION_NUMBER=unversioned
---> Running in 1ce3e682b483
Removing intermediate container 1ce3e682b483
---> dcc4d4b6b4b5
Step 21/26 : WORKDIR /source
---> Running in c8d9c1349305
Removing intermediate container c8d9c1349305
当我连接到容器并尝试运行Django development server时,它无法工作,因为依赖项未安装(即使
docker compose build
输出显示它们已安装)。我想这是因为“中间容器”正在被移除。我曾尝试将多阶段Dockerfile转换为单阶段Dockerfile,但我仍然不断收到“删除中间容器”消息,并且依赖项并未全部安装。我必须在一条运行指令中安装所有依赖项吗?这是我进入容器时安装所有依赖项的唯一方法吗?难道我没有办法保持这个Dockerfile的完整性,并告诉docker compose在使用它进行构建时停止“留下中间工件”吗?我是否正确使用了“工件”这个词?我找不到一个清晰的解释,我也不确定我是否完全理解“docker工件”是什么。在我看来,您需要单独的docker compose服务,每个图像一个。您说其中涉及4个独立的Python项目,所以听起来您的docker-compose.yaml中应该(至少)定义4个服务
因此,与在一个Dockerfile中定义所有服务不同,我希望每个服务都有自己的Dockerfile,并在您的docker compose.yaml
中分别定义
version: "3.7"
services:
e2e:
- build ./e2e
...
production:
- build ./production
...
你说你所有的服务都必须互相沟通。Docker Compose使这变得非常简单,例如,
生产
映像可以使用URIe2e
访问e2e
映像。(例如,curl e2e
)。您可以使用普通的Python虚拟环境进行日常开发吗?我正在从事的项目使用微服务体系结构。涉及到4个独立的Python项目,它们都必须相互通信,其中2个有自己的数据库,其他依赖项如redis、rabbitmq。不可能在本地安装所有这些东西。您应该能够将--no rm
应用于docker compose build
命令本身。你是正确的,在撰写文件中似乎没有这样做的方法。我认为你有2个选择。我不确定哪一个更好,我也没有试过。首先,确定哪个中间容器包含所需的依赖项,并且Python脚本不会被COPY---from
命令移动。编辑撰写文件以按ID引用中间容器映像。其次,将多阶段DockerFile合并到一个阶段中。正如您所写的,多阶段的目的通常是清除运行时不需要的构建-开发依赖关系,因此这种方法可以让您恢复一切。第二种方法可能是更好的选择。它还提供了两种我认为有用的图像。开发人员的开发映像包括所有内容,运行时的最终用户映像不包括。对不起,我用一个服务共享了docker compose的最低版本,但事实上我有很多服务,每个Python应用程序(+postgres、redit等)都有一个,而且它们都有自己的Dockerfile。问题是这些DockerFile都是多阶段的,当我在构建之后进入容器时,依赖项就不存在了。