Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using docker during the development workflow

I'm using boot2docker on OS X and cloned the following repo:

https://github.com/enokd/docker-node-hello

It basically has a Dockerfile and a very simple express app that prints hello world. Everything runs great when I build and run the image, but of course if I make any changes to index.js on my Mac none of that gets reflected in my running image. I can't seem to find any references on how I'd setup docker so that I can run it in my development environment to automatically pick up my source code changes so I feel like I'm "Doing it wrong". Any suggestions?

Here's how I'm currently running it (I'm not using Vagrant, and not quite sure if that makes any difference):

$ docker build -t gasi/centos-node-hello .
$ docker run -p 49160:8080 -d gasi/centos-node-hello
$ curl localhost:49160

Update: Added an answer with what I ended up doing.

Update: Added more current answer using boot2docker 1.3+ and fig.

like image 360
Abdullah Jibaly Avatar asked May 22 '14 02:05

Abdullah Jibaly


People also ask

Should you use Docker in development?

Docker is great at setting up a local development environment because it easily adds the running process without duplicating the virtualized resource. Second, it's more modular. Docker makes it easy to run multiple versions or instances of the same program without configuration headaches and port collisions.

What is Docker used for in software development?

Docker is an open source containerization platform. It enables developers to package applications into containers—standardized executable components combining application source code with the operating system (OS) libraries and dependencies required to run that code in any environment.


3 Answers

Your question is really interesting, and (partially) not specifically related to Docker. Let me say that the main and final problem here is the use of a virtual machine.

Tracking code changes on host computer can be a challenge when you use a VM (bare Virtualbox VM, Vagrant VM, Docker boot2docker VM...)

The VM is a complete layer of abstraction between your Mac (where your code changes happen) and your application stack (here, the Docker container running on a tiny Linux VM).

  • You have to explicitly define a shared folder (NFS, ...) in Virtualbox, between your Mac and boot2docker. This shared folder would be the root of your code repository. That's the first step.

    Mac: /Users/You/stuff/approot ===> boot2docker: /something


  • Then, you would have to tell your Docker container to use a Volume (docker terminology), bound to boot2docker, and point your Docker application to it:
   docker run -v /something:/app/path/inside/docker -p 49160:8080 \
     -d gasi/centos-node-hello /usr/bin/node /app/path/inside/docker/index.js


  • Having NodeJs detecting files change is another challenge. You need an additional wrapper watching the filesystem and restarting Node upon code changes (Forever, Nodemon...).

  • And, after that since the changes don't occur on a local filesystem, but on a shared folder, you'll probably have to tell the Nodejs watcher (Forever/Nodemon/...) to use polling mode. It works (more or less) but will burn a lot of CPU.

like image 172
mbarthelemy Avatar answered Oct 21 '22 02:10

mbarthelemy


Concerning shared folders with VirtualBox, please check out:

boot2docker together with VirtualBox Guest Additions
How to mount /Users into boot2docker

https://medium.com/boot2docker-lightweight-linux-for-docker/boot2docker-together-with-virtualbox-guest-additions-da1e3ab2465c

tl;dr Build your own custom boot2docker.iso with VirtualBox Guest Additions (see link) or download http://static.dockerfiles.io/boot2docker-v1.1.0-virtualbox-guest-additions-v4.3.12.iso and save it to ~/.boot2docker/boot2docker.iso.

EDIT: updated link to boot2docker v1.1.0

like image 37
mattes Avatar answered Oct 21 '22 04:10

mattes


This is what I ended up doing, so far seems to work but I'm still digging into it:

# script located in bin/run
NS=mycompany
PROJECT=myproject

# kill and remove old container if it exists
docker kill $PROJECT
docker rm $PROJECT

# tag the previously built image
docker tag $NS/$PROJECT $NS/$PROJECT:old

# build the new image
docker build -t $NS/$PROJECT .

# remove the old image
docker rmi $NS/$PROJECT:old

docker run -dP --name=$PROJECT $NS/$PROJECT /sbin/my_init

In my project root, I simply run:

nodemon -x bin/run

Credit goes to this source.

Update for docker 1.3 and fig

Fig is great, it really took a lot of the complexity out of the script I had before. In addition, boot2docker now natively supports mounting volumes on Mac OS X using Virtual Box's shared folders. This is what I find works really well for me now:

First, the Dockerfile:

FROM ubuntu:14.04

# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# Set debconf to run non-interactively
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections

# Install base dependencies
RUN apt-get update && apt-get install -y -q --no-install-recommends \
        build-essential \
        ca-certificates \
        curl \
        git \
        libssl-dev \
        python \
        rsync \
        software-properties-common \
        wget \
    && rm -rf /var/lib/apt/lists/*

ENV NVM_DIR /usr/local/nvm
ENV NODE_VERSION 0.10.33

# Install nvm with node and npm
RUN curl https://raw.githubusercontent.com/creationix/nvm/v0.20.0/install.sh | bash \
    && source $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH      $NVM_DIR/v$NODE_VERSION/bin:$PATH

CMD ["npm", "start"]

The fig.yml:

app:
    image: myNodeImage
    working_dir: /home/myProject
    volumes_from:
     - myvols

Here's the new bin/run:

#!/usr/bin/env bash
# This is the the bin/run script

docker run --rm --volumes-from myvols myNodeImage \
    rsync \
        --delete \
        --recursive \
        --safe-links \
        --exclude .git  --exclude node_modules  \
    /data/myProject/ /home/myProject

fig up

I also have a bin/install script that does the node_modules dependency installs. This assumes I've already done an npm install on my host so that any private packages will work. Also, this works great with npm links, you just need to make a symlink from your /home/linkedProject into $NODE_PATH/linkedProject in your container.

#!/usr/bin/env bash
# This is the the bin/install script

docker run --rm --volumes-from myvols myNodeImage \
    rm -rf /home/myProject && \
    rsync \
        --delete \
        --recursive \
        --safe-links \
        --exclude .git \
        /data/myProject/ /home/myProject && \
    cd /home/myProject && \
    npm rebuild

So, to put this all together, here's the steps in order:

  1. Create my data volume container:

    docker run -v $HOME/data:/data:ro \ -v /home \ -v /path/to/NODE_PATH \ --name myvols myNodeImage echo Creating my volumes

  2. Run my install script: cd ~/data/myProject && ./bin/install

  3. Run my run script: nodemon -x bin/run

like image 4
Abdullah Jibaly Avatar answered Oct 21 '22 02:10

Abdullah Jibaly