How to update code from Git to a Docker container

Question:

I have a Docker file trying to deploy Django code to a container

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 [email protected]/{user}/{repo}

RUN pip install -r website/requirements.txt

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

And then I build my code as docker build -t dockerhubaccount/demo:v1 ., and this pulls my code from Bitbucket to the container. I run it as docker run -p 8000:8080 -td felixcheruiyot/demo:v1 and things appear to work fine.

Now I want to update the code i.e since I used git clone ..., I have this confusion:

  • How can I update my code when I have new commits and upon Docker containers build it ships with the new code (note: when I run build it does not fetch it because of cache).
  • What is the best workflow for this kind of approach?
Asked By: Cheruiyot Felix

||

Answers:

There are a couple of approaches you can use.

  1. You can use docker build --no-cache to avoid using the cache of the Git clone.
  2. The startup command calls git pull. So instead of running python manage.py, you’d have something like CMD cd /repo && git pull && python manage.py or use a start script if things are more complex.

I tend to prefer 2. You can also run a cron job to update the code in your container, but that’s a little more work and goes somewhat against the Docker philosophy.

Answered By: seanmcl

Another solution.

Docker build command uses cache as long as a instruction string is exactly same as the one of cached image. So, if you write

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

On next update, you change as follows.

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

This can prevents docker from using cache.

Answered By: takaomag

I would recommend you checkout out the code on your host and COPY it into the image. That way it will be updated whenever you make a change. Also, during development you can bind mount the source directory over the code directory in the container, meaning any changes are reflected immediately in the container.

A docker command for git repositories that checks for the last update would be very useful though!

Answered By: Adrian Mouat

I would like to offer another possible solution. I need to warn however that it’s definitely not the “docker way” of doing things and relies on the existence of volumes (which could be a potential blocker in tools like Docker Swarm and Kubernetes)

The basic principle that we will be taking advantage of is the fact that the contents of container directories that are used as Docker Volumes, are actually stored in the file system of the host. Check out this part of the documentation.

In your case you would make /opt/app a Docker Volume. You don’t need to map the Volume explicitly to a location on the host’s file-system since as a I will describe below, the mapping can be obtained dynamically.

So for starters leave your Dockerfile exactly as it is and switch your container creation command to something like:

docker run -p 8000:8080 -v /opt/app --name some-name -td felixcheruiyot/demo:v1

The command docker inspect -f {{index .Volumes "/opt/webapp"}} some-name will print the full file system path on the host where your code is stored (this is where I picked up the inspect trick).

Armed with that knowledge all you have to do is replace that code and your all set.
So a very simple deploy script would be something like:

code_path=$(docker inspect -f {{index .Volumes "/opt/webapp"}} some-name)
rm -rfv $code_path/*
cd $code_path
git clone [email protected]/{user}/{repo}

The benefits you get with an approach like this are:

  • There are no potentially costly cacheless image rebuilds
  • There is no need to move application specific running information into the run command. The Dockerfile is the only source of needed for instrumenting the application

UPDATE

You can achieve the same results I have mentioned above using docker cp (starting Docker 1.8). This way the container need not have volumes, and you can replace code in the container as you would on the host file-system.

Of course as I mentioned in the beginning of the answer, this is not the “docker way” of doing things, which advocates containers being immutable and reproducible.

Answered By: geoand

If you use GitHub you can use the GitHub API to not cache specific RUN commands.

You need to have jq installed to parse JSON: apt-get install -y jq

Example:

docker build --build-arg SHA=$(curl -s 'https://api.github.com/repos/Tencent/mars/commits' | jq -r '.[0].sha') -t imageName .

In Dockerfile (ARG command should be right before RUN):

ARG SHA=LATEST
RUN SHA=${SHA} 
    git clone https://github.com/Tencent/mars.git

Or if you don’t want to install jq:

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

If a repository has new commits, git clone will be executed.

Answered By: Michal Z m u d a