Heya, Thanks for visiting!

Journey to running `gitter-webapp` on Windows with Docker

  • gitter
  • windows
  • docker
  • virtualbox

NB: This was my experience around September, 2016 and I would consider myself a bit of a Docker noob at the time. Things may have changed and improved since then to make life easier. This just documents the troubles I ran into and shouldn't be considered setup guide worthy.

TLDR;

  • Use Docker Toolbox with the VirtualBox driver (Docker for Windows isn't ready)
  • Watch out for CRLF line endings that can get introduced from things like checking stuff out with git.

gitter-webapp is the project/repo that is home to the backend and frontend code for Gitter webapp. The team is pretty much exclusively macOS based but I prefer Windows.

For a while, I used ShareMouse so that I could use my macOS machine as just another external monitor to my Windows machine.

We use Docker for local dev and CI testing to run services like Mongo, redis, and elasticsearch.

Skip to the working recipe

Initial Attempt

My very initial attempt(August, 2015) assisted by @malditogeek was with Vagrant which failed because of the following issues better described in other articles.

Just recently(September 2016), I got an itch to try to get something going again.

The Docker for Windows Hyper-V trap

Docker for Windows uses Hyper-V is a native hypervisor on Windows for virtualizing things and is supposed to be a lot faster than VirtualBox setup. I couldn't get the app working with Docker for Windows but here is some references to the trouble I ran into.

Hyper-V driven machines aren't ready in my opinion.

If you are running into something not laid out here, perhaps you can find something useful here, See https://github.com/docker/kitematic/wiki/Common-Issues-and-Fixes

It is only available on Windows Pro and needs to be enabled(restart required): Control Panel -> Programs -> Turn Windows features on or off

Turning windows features on and off

Most likely, the following command will fail to create the machine because it is "Waiting for ssh to be available…", move on to the next step to get around it.

docker-machine create --driver hyperv default

Create a virtual switch via the Hyper-V Manager -> Action -> Virtual Switch Manager.... I called mine external-switch:

Creating a Virtual Switch in Hyper-V Naming the switch 'external-switch'

docker-machine create --driver hyperv --hyperv-virtual-switch external-switch default

Tip courtesy of https://forums.docker.com/t/docker-machine-stuck-on-waiting-for-ssh-to-be-available/18702/3 -> https://forums.docker.com/t/using-docker-for-windows-docker-machine-create/8032/2

The lack of volume mounting support means that the entrypoint's defined in docker-compose.yml aren't found

docker-compose.yml

mongosetup:
  image: mongo:3.2
  volumes:
    - ${WORKSPACE}:/src
  working_dir: /src
  entrypoint: ['/src/mongo-setup.sh']

But the entrypoint can't be found. Perhaps this is just a line-ending issue but I wasn't aware of this problem at the time and was fed up and didn't want to go back after I got things working with Docker Toolbox.

$ docker-compose up -d --no-recreate
ERROR: for mongosetup  Cannot start service mongosetup: oci runtime error: exec: "/src/mongo-setup.sh": stat /src/mongo-setup.sh: no such file or directory
$ docker inspect mongosetup_1
...
"Mounts": [
    {
        "Source": "/c/Users/MLM/Documents/GitHub/something",
        "Destination": "/src",
        "Mode": "rw",
        "RW": true,
        "Propagation": "rprivate"
    },
...

Kitematic

I wish Kitematic could just skip the machine creation process and you could just choose an existing machine to inspect. I already have a machine running called default that I want it to pick up. Perhaps this possible with the barely documented .kitematicfg but I don't see an option that quite fits.

It fails first because it looks for Docker for Windows machines

Docker setup error, unexpected error with Kitematic: 'Error: connect ENONENT //./pipe/docker_engine'

This is fine, just a pain point and you need to click "Use Virtualbox". But unfortunately it can also fails to create the default machine:

Docker setup error, unexpected error with Kitematic: 'Command failed: C:\Program Files\Docker Toolbox\docker-machine.exe -D start default, Docker Machine Version: 0.8.0, build b85aac1, ...'

You might have slightly better luck with the latest Kitematic separate from the Docker Toolbox:

Further reading:

The working recipe

Prerequisites

  • Install Docker Toolbox
  • Disable Hyper-V if you previously enabled it: Control Panel -> Programs -> Turn Windows features on or off

Start the Docker machine backed by VirtualBox

docker-machine create --driver virtualbox default

Setup environment variables for the particular machine. You will need to run the last line of the output from that command to actually evaluate and apply the variables which looks like: @FOR /f "tokens=*" %i IN ('docker-machine env default') DO @%i

docker-machine env default

Your docker-compose.yml might also have some host environment variables you need to define which would look like ${FOO} in docker-compose.yml. In the case of gitter-webapp, I need to supply DOCKER_HOST_IP and WORKSPACE which can be populated with the following commands. That weird for command snippet is just Batch recipe used to put output of the pwd command in the WORKSPACE env variable on Windows.

for /f "delims=" %a in ('docker-machine ip default') do @set DOCKER_HOST_IP=%a
for /f "delims=" %a in ('pwd') do @set WORKSPACE=%a

If you are not sure whether you need to set any environment variables, the docker-compose up command you run later, in order to start the containers will complain like this:

WARNING: The DOCKER_HOST_IP variable is not set. Defaulting to a blank string.
WARNING: The WORKSPACE variable is not set. Defaulting to a blank string.

Line ending CRLF trap

A trap I fell into was line endings(CRLF, LF) causing some containers to not work correctly.

$ docker logs gitterwebapp_mongosetup_1
standard_init_linux.go:175: exec user process caused "no such file or directory"

Which would make it seem like docker couldn't find the log file but actually that is the contents of the log file and is just a esoteric error for CRLF line endings causing things to go sour.

Even if your project was checked into the repo with LF, Git could be converting them back to Windows style line endings on checkout. You can use the following commands to kick Git into shape.

git config --global core.autocrlf input
git config --global core.eol lf
// Check the value in the current context
git config core.autocrlf

If you want to make robust docker setup, consider running things through a utility like dos2unix.

Now you can re-checkout the project or run through the steps here to refresh the project with the correct LF line-endings: https://help.github.com/articles/dealing-with-line-endings/#refreshing-a-repository-after-changing-line-endings

As a small note if you are using the Atom editor, you can open a project file and check the line-endings in the bottom-right corner:

Switching the line endings in the Atom text editor

Start the containers

docker-compose up -d --no-recreate

If something goes wrong, you can reset and delete the containers with the following commands

docker-compose stop
docker-compose rm -f

Get the app running

With gitter-webapp, the script hooks defined package.json like preinstall use some bash which won't run on Windows batch shells.

In order to install the app properly, we need to use the undocumented --ignore-scripts flag which will skip any script hooks.

npm install --ignore-scripts

Because we used --ignore-scripts to skip things, we need to manually fix up some native module builds. With gitter-webapp, I needed to run the following. May be a bit tricky with npm 3.

for /f "delims=" %a in ('cd') do (rmdir %a\node_modules\cld /S /Q) && npm i cld
for /f "delims=" %a in ('cd') do (rmdir %a\node_modules\slug /S /Q) && npm i slug
// Start the app
npm run dev

Done, Rejoice, You can now develop on Windows :D

Command Quick Reference

default is just a machine name and can be whatever you want.

Commanddescription
docker-machine lsList all the Docker machines and check state
docker-machine create --driver virtualbox defaultCreate and start the default machine
docker-machine rm -f defaultRemove the default machine
docker-machine start defaultStart an existing default machine
docker-machine env defaultSetup the environment variables pertaining to the machine (copy paste the last line in the output for it to actually apply)
docker ps --allList all containers
docker logs containerShow logs for container. Useful for finding out why something errored out, etc
docker-compose up -d --no-recreateCreate/Start all containers defined in docker-compose.yml
docker-compose stop && docker-compose rm -fStop all the containers and remove them. Useful for resetting after a docker-compose up ...