如何将Git中的代码更新至Docker容器

31
我有一个Docker文件,试图将Django代码部署到容器中。
FROM ubuntu:latest
MAINTAINER { myname }

#RUN echo "deb http://archive.ubuntu.com/ubuntu/ $(lsb_release -sc) main universe" >> /etc/apt/sou$

RUN apt-get update

RUN DEBIAN_FRONTEND=noninteractive apt-get install -y tar git curl dialog wget net-tools nano buil$
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y python python-dev python-distribute python-p$

RUN mkdir /opt/app
WORKDIR /opt/app

#Pull Code
RUN git clone git@bitbucket.org/{user}/{repo}

RUN pip install -r website/requirements.txt

#EXPOSE = ["8000"]
CMD python website/manage.py runserver 0.0.0.0:8000

接下来我使用docker build -t dockerhubaccount/demo:v1 .命令构建我的代码,这将把我的代码从Bitbucket拉到容器中。然后我使用docker run -p 8000:8080 -td felixcheruiyot/demo:v1命令运行它,一切似乎都正常。

现在我想要更新代码,因为我使用了git clone ...命令,所以我有些困惑:

  • 当我有新的提交时,我该如何更新我的代码,并在构建Docker容器时自动使用新代码(注意:由于缓存,当我运行构建命令时并不会获取最新代码)?
  • 这种情况下最佳的工作流程是什么?
5个回答

24

有几种方法可以使用。

  1. 您可以使用 docker build --no-cache 来避免使用Git克隆的缓存。
  2. 启动命令调用 git pull。因此,不要运行 python manage.py,而是要像这样运行 CMD cd /repo && git pull && python manage.py 或者如果情况更加复杂可以使用一个启动脚本。

我倾向于首选第二种方法。您也可以运行cron作业来更新容器中的代码,但这需要做更多的工作,有些违反了Docker的理念。


2
我会选择2作为答案。我认为这样更合理,因为使用--no-cache将会完全重新安装所有内容,这不是一个好的方法。感谢您的贡献。 - Cheruiyot Felix
如果有人执行 docker exec -it con/tainer bash,那么将运行 bash 而不是在 CMD 中指定的任何内容。对我来说,这两个选项都不太理想,这必须是非常常见的问题 - 我觉得应该有一种方法在 Dockerfile 的中间禁用缓存。 - avloss

12

我建议你检查主机上的代码并将其COPY到镜像中。这样,每当您进行更改时,它就会得到更新。此外,在开发期间,您可以将源目录绑定安装到容器中的代码目录上,从而使任何更改立即反映在容器中。

不过,一个可以检查最后更新时间的git库的docker命令会非常有用!


9

另一种解决方法。

Docker build命令会使用缓存,只要指令字符串与已缓存镜像的字符串完全相同。因此,如果您编写:

RUN echo '2014122400' >/dev/null && git pull ...

在下一次更新中,您需要做出以下更改。
RUN echo '2014122501' >/dev/null && git pull ...

这可以防止Docker使用缓存。

我认为最好的答案是这个。使用这种方法,我们可以在恰当的位置关闭缓存。 - makerj
需要注意的是,这将导致此行之后的所有内容都被重建。如果您的git pull像OPs问题中那样是最后一行,那么就没问题了。但如果您的git pull在Dockerfile中较早出现,则可能会有问题。 - JHowIX

3
我想提供另一种可能的解决方案。但是我需要警告,这绝对不是“Docker方式”做事情的方式,并且依赖于卷的存在(这可能是像Docker Swarm和Kubernetes这样的工具中的潜在阻塞器)。
我们将利用的基本原则是容器目录的内容被用作Docker卷,实际上存储在主机的文件系统中。请查看文档的 this 部分。
在您的情况下,您将使 /opt/app 成为Docker卷。您不需要将卷显式地映射到主机文件系统上的位置,因为如下所述,可以动态获取映射。
因此,首先将Dockerfile保留在原样,然后将容器创建命令切换为以下内容:
docker run -p 8000:8080 -v /opt/app --name some-name -td felixcheruiyot/demo:v1

命令docker inspect -f {{index .Volumes "/opt/webapp"}} some-name将在主机上打印存储代码的完整文件系统路径(this是我学到的检查技巧)。
有了这个知识,你只需要替换那段代码就可以了。因此,一个非常简单的部署脚本可能是这样的:
code_path=$(docker inspect -f {{index .Volumes "/opt/webapp"}} some-name)
rm -rfv $code_path/*
cd $code_path
git clone git@bitbucket.org/{user}/{repo}

这种方法带来的好处包括:
  • 没有潜在的昂贵的无缓存图像重建
  • 不需要将应用程序特定的运行信息移动到运行命令中。Dockerfile 是唯一需要为应用程序提供工具的来源

更新

您可以使用 docker cp(自 Docker 1.8 起)实现上述相同的结果。这样,容器无需拥有卷,您可以像在主机文件系统上一样替换容器中的代码。

当然,正如我在回答开头提到的那样,这不是 "Docker 方式" 做事情的方式,Docker 倡导容器是不可变和可复制的。


只是一个注记...不变的Docker容器?当应用程序更新使用新的依赖项时,这如何处理正在改变的Python环境? - Yoeri
@Yoeri,Docker 的方式主张在任何更改时都使用容器。这意味着当应用程序以任何方式(无论是应用程序代码、依赖项、配置等)更新时,都应创建一个新的容器。目标是实现容器的完全可重复性。 - geoand
1
最好使用一个单独的容器(或主机)来存储源代码和环境,并共享这些卷。我总是发现有关更改源代码的问题,而不是更改依赖项的问题... - Yoeri
从Docker的角度来看,无论是什么类型的更改,它都是一种更改。您的最终目标始终是能够重新创建容器,这意味着您不能更改容器内部的内容。 - geoand

0
如果您使用GitHub,可以使用GitHub API来不缓存特定的RUN命令。
您需要安装jq来解析JSON:apt-get install -y jq 示例:
docker build --build-arg SHA=$(curl -s 'https://api.github.com/repos/Tencent/mars/commits' | jq -r '.[0].sha') -t imageName .

在 Dockerfile 中(ARG 命令应该紧跟在 RUN 命令之前):
ARG SHA=LATEST
RUN SHA=${SHA} \
    git clone https://github.com/Tencent/mars.git

或者如果您不想安装 jq:

SHA=$(curl -s 'https://api.github.com/repos/Tencent/mars/commits' | grep sha | head -1)

如果一个代码库有新的提交,将会执行 git clone 命令。

你也可以使用 ADD,让 docker build 每次从 API 下载当前的 HEAD 引用。当 HEAD 更改时,缓存将失效。https://dev59.com/n1oU5IYBdhLWcg3w35vN#39278224 - anq

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接