There’s really no simple, tangible blurb to share, but it’s pretty awesome (though still in beta):
docker
Docker: Debugging a Running Container
This is invaluable if you run into insidious little problems in servers or automated processes that run in Docker.
docker exec -it <container name> <command>
This can even be an interactive process, such as a shell.
Use ADB to Connect to Your Android Device From a Docker Container
You may have a use-case where you want to write software to manipulate an Android device using a system or set of tools that are not natively available from your current system. However, you might be able to expose this as a Docker image. For example, your device is (or will be) connected to a Windows machine and you really want to or need to use Linux tools.
No problem. ADB implicitly uses a client-server model: The ADB tool (on your system) connects to the ADB server (runs in the background on your system) which interacts with the ADB daemon (runs on your device). This means that we can forward requests from ADB on the command-line in the guest container in Docker to the ADB server on the host system.
The ADB client and server have to be at the same version, or the client will indiscriminately kill/restart your ADB server. So, as I am currently running Ubuntu 14.04 on my host system, I will do the same in Docker.
First, I will make sure the ADB server is running on my host system. Most of the subcommands that will automatically start the local server, but I will start it directly:
$ adb start-server * daemon not running. starting it now on port 5037 * * daemon started successfully *
Now, I will start a container in Docker with Ubuntu 14.04 and automatically install ADB before dropping to a prompt. Note that we are passing “–network=host” in order to share the host’s network identity:
$ docker run -i -t --network=host ubuntu:14.04 /bin/bash -c "sudo apt-get update && sudo apt-get install -y android-tools-adb && /bin/bash"
Eventually, you will end-up at the prompt. Just do something simple like enumerating the devices:
root@mlll2664:/# adb devices List of devices attached 05157df572841820 device
The “mlll2664” hostname, represented in the prompt in the Docker container, is, actually, the same hostname as my host system.
So, there you go. Not too painful.
Go: Image-Processing Microservice
Imaginary is a fun little package that can be Dockerized and deployed next to the rest of your services to offload image-processing:
Fast HTTP microservice written in Go for high-level image processing backed by bimg and libvips. imaginary can be used as private or public HTTP service for massive image processing with first-class support for Docker & Heroku. It’s almost dependency-free and only uses net/http native package without additional abstractions for better performance.
Intro to Docker, and Private Image Registries
Docker is an application hosting framework. It enables you to wrap virtual-machine containers around your applications and both manufacture and control them via API.
Docker allows you to bundle your dependencies/servers and your application into a thin image that is layered on top of another one (such as Ubuntu, or something more formally prepared for your needs). These are different than virtual machines, in that even though they are heavily isolated from the other processes in the system using LXC and cgroups (Linux concepts talked about in previous articles), they share the same resources and have almost no overhead. When you start a VM, you end up at a prompt or UI that is ready for you to install and start applications. When you start an application container, you run a script that starts your applications and dependencies, and nothing else. You can run a handful of VMs on a system, but a thousand application containers. If you want to streamline distribution, you can then consider using CoreOS to host your images at the OS-level.
Another feature that Docker contributes to containers is version control. You can commit any change that you’ve made to your container as a new image. You can obviously also start as many containers as you’d like from the same image (images themselves are immutable).
Your own process for distributing images to other teams or other companies might require a place to publish or coordinate your images beyond your current system. This is done via a Registry. Though Docker provides the public Docker Hub Registry, you may want a private Registry of your own for your company or development team.
Because the components/accessories of Docker are, themselves, often distributed as Docker images, this example has the second effect of showing you how easy it is to start a Docker-based application (in case you were unfamiliar, before). You don’t need to know anything about the guest application other than what ports its services are hosted on. In fact, you can start Docker images (which are then referred to as containers) that may be required for other Docker images, have Docker map random local ports to them, and then automatically forward ports from the containers that provide a service to the containers that depend on them (via a feature called linking).
Start your Registry using something similar to the example from the Registry project homepage:
$ docker run -e SETTINGS_FLAVOR=s3 -e AWS_BUCKET=mybucket -e STORAGE_PATH=/registry -e AWS_KEY=myawskey -e AWS_SECRET=myawssecret -e SEARCH_BACKEND=sqlalchemy -p 5000:5000 registry
This essentially sets six environment variables for the application that tell it to store into S3 and forward port 5000 from the host (local) system to port 5000 in the guest (Registry). “registry” is the name of the image to run (if it’s owned by a particular user, it’ll look like “/”). If it’s not already available locally, it’ll be located and pulled. If not further qualified with a registry prefix, it’ll assume that it must be located at the Docker Hub.
An example session where we pull the Ubuntu image down from the Hub, and push it into our Registry. Notice that we qualify the “push to” and “pull from” requests on our registry by prefixing the hostname/port of our Registry:
$ sudo docker pull ubuntu:14.04 $ sudo docker tag 826544226fdc yourregistry.net:5000/ubuntu $ sudo docker push yourregistry.net:5000/ubuntu $ sudo docker pull yourregistry.net:5000/ubuntu
The tag command reserves a new spot in our registry for the given image from somewhere else. You’d get that ID string from the local listing.
By default, the Registry communicates only directly to the Docker socket or can be managed via REST. If you want to have an easier time of browsing your images, install the docker-registry-web project:
$ docker run -p 8080:8080 -e REG1=http://<system hostname>:5000/v1/ atcol/docker-registry-ui
Keep in mind that it’s going to need to be able to talk to your Registry instance, so make sure the hostname that you’re giving it for the registry is resolvable from within the docker-registry-web container.
A screenshot:
docker-registry-web is actually a Java application, but, again, it would be a poorly designed image if it was important for you to know that.
Lastly, when you’re done playing-around with your Registry instance, make sure to hide it behind an Nginx-proxy, and add authentication (mutual, HTTP, etc..).
Using Docker to Package and Distribute Applications as Containers
i’ve already posted a couple of articles referencing Linux’s LXC containers (see here), for lightweight process isolation and resource limiting.
Docker takes LXC and pushes the functionality to produce portable containers that can be:
- versioned alongside your sourcecode
- automatically built alongside binaries (using Dockerfiles)
- published to a repository (both public and private can be used)
In this way, Docker producer application containers that will work consistently from within a variety of different environments.
The purpose of this article is to provide a very quick introduction to Docker, and a tutorial that hastily explains how to create a Python web project within an Ubuntu container, and connect to it.
Docker Daemon
Just like with LXC, a daemon runs in the background to manage the running containers. Though there exists a Docker Ubuntu repository, we use the manual process so that we can provide a more universal tutorial.
$ wget https://get.docker.io/builds/Linux/x86_64/docker-latest -O docker --2014-02-12 01:08:01-- https://get.docker.io/builds/Linux/x86_64/docker-latest Resolving get.docker.io (get.docker.io)... 198.41.249.135, 162.159.251.135, 2400:cb00:2048:1::a29f:fb87, ... Connecting to get.docker.io (get.docker.io)|198.41.249.135|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 15119491 (14M) [binary/octet-stream] Saving to: ‘docker’ 100%[==================================================================================================================>] 15,119,491 3.17MB/s in 5.0s 2014-02-12 01:08:07 (2.91 MB/s) - ‘docker’ saved [15119491/15119491] $ chmod +x docker $ sudo ./docker -d [/var/lib/docker|9024eeb6] +job initserver() [/var/lib/docker|9024eeb6.initserver()] Creating server [/var/lib/docker|9024eeb6] +job init_networkdriver() [/var/lib/docker|9024eeb6.init_networkdriver()] creating new bridge for docker0 [/var/lib/docker|9024eeb6.init_networkdriver()] getting iface addr [/var/lib/docker|9024eeb6] -job init_networkdriver() = OK (0) 2014/02/12 01:08:27 WARNING: Your kernel does not support cgroup swap limit. Loading containers: : done. [/var/lib/docker|9024eeb6.initserver()] Creating pidfile [/var/lib/docker|9024eeb6.initserver()] Setting up signal traps [/var/lib/docker|9024eeb6] -job initserver() = OK (0) [/var/lib/docker|9024eeb6] +job serveapi(unix:///var/run/docker.sock) 2014/02/12 01:08:27 Listening for HTTP on unix (/var/run/docker.sock)
Filling the Container
In another terminal (the first is being used by the daemon), start the Ubuntu container. We do this by passing an image-name. If this image can’t be found locally, the tool will search the public repositor[y,ies]. If the image has not yet been downloaded, it will be automatically downloaded (“pulled”).
Whereas most images will look like “/”, some images have special aliases that take the place of both. Ubuntu has one such alias (which dotCloud, the Docker people, use for development): “ubuntu”.
First, we’re going to create/start the container.
$ sudo ./docker run -i -t ubuntu /bin/bash [sudo] password for dustin: Pulling repository ubuntu 9cd978db300e: Download complete eb601b8965b8: Download complete 9cc9ea5ea540: Download complete 5ac751e8d623: Download complete 9f676bd305a4: Download complete 511136ea3c5a: Download complete f323cf34fd77: Download complete 1c7f181e78b9: Download complete 6170bb7b0ad1: Download complete 7a4f87241845: Download complete 321f7f4200f4: Download complete WARNING: WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: [8.8.8.8 8.8.4.4] root@618cd8514fec:/# ls -l total 72 drwxr-xr-x 2 root root 4096 Jan 29 18:10 bin drwxr-xr-x 2 root root 4096 Apr 19 2012 boot drwxr-xr-x 11 root root 4096 Feb 12 06:34 dev drwxr-xr-x 56 root root 4096 Feb 12 06:34 etc drwxr-xr-x 2 root root 4096 Apr 19 2012 home drwxr-xr-x 12 root root 4096 Jan 29 18:10 lib drwxr-xr-x 2 root root 4096 Jan 29 18:10 lib64 drwxr-xr-x 2 root root 4096 Jan 29 18:10 media drwxr-xr-x 2 root root 4096 Apr 19 2012 mnt drwxr-xr-x 2 root root 4096 Jan 29 18:10 opt dr-xr-xr-x 249 root root 0 Feb 12 06:34 proc drwx------ 2 root root 4096 Jan 29 18:10 root drwxr-xr-x 5 root root 4096 Jan 29 18:10 run drwxr-xr-x 2 root root 4096 Jan 29 18:11 sbin drwxr-xr-x 2 root root 4096 Mar 5 2012 selinux drwxr-xr-x 2 root root 4096 Jan 29 18:10 srv dr-xr-xr-x 13 root root 0 Feb 12 06:34 sys drwxrwxrwt 2 root root 4096 Jan 29 18:10 tmp drwxr-xr-x 10 root root 4096 Jan 29 18:10 usr drwxr-xr-x 11 root root 4096 Jan 29 18:10 var
Now, add the dependencies for the sample Python application, and add the code.
root@618cd8514fec:/# mkdir app
root@618cd8514fec:/# cd app
root@618cd8514fec:/app# apt-get install python-pip
Reading package lists... Done
Building dependency tree... Done
The following extra packages will be installed:
python-pkg-resources python-setuptools
Suggested packages:
python-distribute python-distribute-doc
The following NEW packages will be installed:
python-pip python-pkg-resources python-setuptools
0 upgraded, 3 newly installed, 0 to remove and 63 not upgraded.
Need to get 599 kB of archives.
After this operation, 1647 kB of additional disk space will be used.
Do you want to continue [Y/n]?
Get:1 http://archive.ubuntu.com/ubuntu/ precise/main python-pkg-resources all 0.6.24-1ubuntu1 [63.1 kB]
Get:2 http://archive.ubuntu.com/ubuntu/ precise/main python-setuptools all 0.6.24-1ubuntu1 [441 kB]
Get:3 http://archive.ubuntu.com/ubuntu/ precise/universe python-pip all 1.0-1build1 [95.1 kB]
Fetched 599 kB in 26s (22.8 kB/s)
Selecting previously unselected package python-pkg-resources.
(Reading database ... 9737 files and directories currently installed.)
Unpacking python-pkg-resources (from .../python-pkg-resources_0.6.24-1ubuntu1_all.deb) ...
Selecting previously unselected package python-setuptools.
Unpacking python-setuptools (from .../python-setuptools_0.6.24-1ubuntu1_all.deb) ...
Selecting previously unselected package python-pip.
Unpacking python-pip (from .../python-pip_1.0-1build1_all.deb) ...
Setting up python-pkg-resources (0.6.24-1ubuntu1) ...
Setting up python-setuptools (0.6.24-1ubuntu1) ...
Setting up python-pip (1.0-1build1) ...
root@618cd8514fec:/app# pip install web.py
Downloading/unpacking web.py
Downloading web.py-0.37.tar.gz (90Kb): 90Kb downloaded
Running setup.py egg_info for package web.py
Installing collected packages: web.py
Running setup.py install for web.py
Successfully installed web.py
Cleaning up...
root@618cd8514fec:/app# cat < app.py
> #!/usr/bin/env python2.7
>
> import web
>
> urls = (
> '/', 'index'
> )
>
> class index:
> def GET(self):
> return 'Hello, world!'
>
> if __name__ == '__main__':
> app = web.application(urls, globals())
> app.run()
> CODE
root@618cd8514fec:/app# chmod u+x app.py
This is the code that we used (for easy copy-and-pasting):
#!/usr/bin/env python2.7 import web urls = ( '/', 'index' ) class index: def GET(self): return 'Hello, world!' if __name__ == '__main__': app = web.application(urls, globals()) app.run()
Make sure that the application starts correctly:
root@618cd8514fec:/app# ./app.py http://0.0.0.0:8080/
Feel free to do a cURL request to test. Afterwards, exit by doing CTRL+C, and running “exit”. We might’ve exited, but the container will still be running.
$ sudo ./docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 618cd8514fec ubuntu:12.04 /bin/bash 7 minutes ago Exit 0 berserk_engelbart
Commit the changes to the container (in the version-control sense). This won’t affect anything outside of your local system, yet.
$ sudo ./docker commit 618cd8514fec dsoprea/test_python_app 32278919fbe5b080a204fabc8ff430c6bdceaeb93faf5ad247a917de9e6b1f7a
Stop the container.
# sudo ./docker stop 618cd8514fec $ sudo ./docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Now, start it, again, with port-forwarding from port :1234 on the current system to :8080 on the container.
$ sudo ./docker run -d -p 1234:8080 dsoprea/test_python_app /app/app.py WARNING: WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: [8.8.8.8 8.8.4.4] 118ba88c5fa4e4102209a5a1dd226ae6588598812bf3ffab0692e5b0766d71d3
Test it using cURL.
$ curl http://localhost:1234 && echo Hello, world!
Publishing Your Docker Image
Now, we’ll push it up to the Docker Index, for public access (this is part is up to you). You’ll need to setup a free account, first. Here, I use my own account (“dsoprea”).
$ sudo ./docker push dsoprea/test_python_app The push refers to a repository [dsoprea/test_python_app] (len: 1) Sending image list Please login prior to push: Login against server at https://index.docker.io/v1/ Username: dsoprea Password: Email: myselfasunder@gmail.com Login Succeeded The push refers to a repository [dsoprea/test_python_app] (len: 1) Sending image list Pushing repository dsoprea/test_python_app (1 tags) 511136ea3c5a: Image already pushed, skipping Image 6170bb7b0ad1 already pushed, skipping Image 9cd978db300e already pushed, skipping 32278919fbe5: Image successfully pushed 3805625219a1: Image successfully pushed Pushing tag for rev [3805625219a1] on {https://registry-1.docker.io/v1/repositories/dsoprea/test_python_app/tags/latest}
The image is now available at (for my account): https://index.docker.io/u/dsoprea/test_python_app/
If you actually want to do a fresh download/execution of your software, delete the local image (using the image ID).
$ sudo ./docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE dsoprea/test_python_app latest 3805625219a1 13 minutes ago 208 MB $ sudo ./docker rmi 3805625219a1 $ sudo ./docker run -d -p 1234:8080 dsoprea/test_python_app /app/app.py Unable to find image 'dsoprea/test_python_app' (tag: latest) locally Pulling repository dsoprea/test_python_app 3805625219a1: Download complete 511136ea3c5a: Download complete 6170bb7b0ad1: Download complete 9cd978db300e: Download complete 32278919fbe5: Download complete WARNING: WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: [8.8.8.8 8.8.4.4] 531e18306f437277fcf19827afde2901ee6b78cd954213b693aa8ae73f651ea0 $ curl http://localhost:1234 && echo Hello, world!
Notice that at times we refer to the “image”, and at others we refer to the “container”. This might be clear to some and confusing to others. The image describes the template from which the container is constructed (instantiated).
Docker was built to coexist in a continuous-integration- and/or Github-type environment. There’s way more to the tool. It’d be well-worth your time to investigate it on your own, as you might find yourself integrating it into your development or deployment processes. Imagine automatically creating drop-in solutions alongside all of your projects, that you can publish with the ease of a version-control push.
You must be logged in to post a comment.