5.7 KiB
class: title
Local Development Workflow with Docker
Objectives
At the end of this section, you will be able to:
-
Share code between container and host.
-
Use a simple local development workflow.
Using a Docker container for local development
We want to solve the following issues:
-
"Works on my machine"
-
"Not the same version"
-
"Missing dependency"
By using Docker containers, we will get a consistent development environment.
Our "namer" application
-
The code is available on https://github.com/jpetazzo/namer.
-
The image jpetazzo/namer is automatically built by the Docker Hub.
Let's run it with:
$ docker run -dP jpetazzo/namer
Check the port number with docker ps and open the application.
Let's look at the code
Let's download our application's source code.
$ git clone https://github.com/jpetazzo/namer
$ cd namer
$ ls -1
company_name_generator.rb
config.ru
docker-compose.yml
Dockerfile
Gemfile
Where's my code?
According to the Dockerfile, the code is copied into /src :
FROM ruby
MAINTAINER Education Team at Docker <education@docker.com>
COPY . /src
WORKDIR /src
RUN bundler install
CMD ["rackup", "--host", "0.0.0.0"]
EXPOSE 9292
We want to make changes inside the container without rebuilding it each time.
For that, we will use a volume.
Our first volume
We will tell Docker to map the current directory to /src in the container.
$ docker run -d -v $(pwd):/src -p 80:9292 jpetazzo/namer
-
-d: the container should run in detached mode (in the background). -
-v: the following host directory should be mounted inside the container. -
-p: connections to port 80 on the host should be routed to port 9292 in the container. -
jpetazzo/nameris the name of the image we will run. -
We don't specify a command to run because is is already set in the Dockerfile.
Mounting volumes inside containers
The -v flag mounts a directory from your host into your Docker
container. The flag structure is:
[host-path]:[container-path]:[rw|ro]
-
If [host-path] or [container-path] doesn't exist it is created.
-
You can control the write status of the volume with the
roandrwoptions. -
If you don't specify
rworro, it will berwby default.
There will be a full chapter about volumes!
Testing the development container
Now let us see if our new container is running.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
045885b68bc5 trai... rackup 3 seconds ago Up ... 0.0.0.0:80->9292/tcp ...
Viewing our application
Now let's browse to our web application on:
http://<yourHostIP>:80
We can see our company naming application.
Making a change to our application
Our customer really doesn't like the color of our text. Let's change it.
$ vi company_name_generator.rb
And change
color: royalblue;
To:
color: red;
Refreshing our application
Now let's refresh our browser:
http://<yourHostIP>:80
We can see the updated color of our company naming application.
Improving the workflow with Compose
- You can also start the container with the following command:
$ docker-compose up -d
- This works thanks to the Compose file,
docker-compose.yml:
www:
build: .
volumes:
- .:/src
ports:
- 80:9292
Why Compose?
-
Specifying all those "docker run" parameters is tedious.
-
And error-prone.
-
We can "encode" those parameters in a "Compose file."
-
When you see a
docker-compose.ymlfile, you know that you can usedocker-compose up. -
Compose can also deal with complex, multi-container apps.
(More on this later.)
Recap of the development workflow
-
Write a Dockerfile to build an image containing our development environment.
(Rails, Django, ... and all the dependencies for our app) -
Start a container from that image.
Use the-vflag to mount our source code inside the container. -
Edit the source code outside the containers, using regular tools.
(vim, emacs, textmate...) -
Test the application.
(Some frameworks pick up changes automatically.
Others require you to Ctrl-C + restart after each modification.) -
Iterate and repeat steps 3 and 4 until satisfied.
-
When done, commit+push source code changes.
class: extra-details
Debugging inside the container
Docker has a command called docker exec.
It allows users to run a new process in a container which is already running.
If sometimes you find yourself wishing you could SSH into a container: you can use docker exec instead.
You can get a shell prompt inside an existing container this way, or run an arbitrary process for automation.
class: extra-details
docker exec example
$ # You can run ruby commands in the area the app is running and more!
$ docker exec -it <yourContainerId> bash
root@5ca27cf74c2e:/opt/namer# irb
irb(main):001:0> [0, 1, 2, 3, 4].map {|x| x ** 2}.compact
=> [0, 1, 4, 9, 16]
irb(main):002:0> exit
class: extra-details
Stopping the container
Now that we're done let's stop our container.
$ docker stop <yourContainerID>
And remove it.
$ docker rm <yourContainerID>
Section summary
We've learned how to:
-
Share code between container and host.
-
Set our working directory.
-
Use a simple local development workflow.


