程式扎記: [Docker 工具] Docker-py - Image File

標籤

2015年6月22日 星期一

[Docker 工具] Docker-py - Image File

Client API#

To instantiate a Client class that will allow you to communicate with a Docker daemon, simply do:
from docker import Client
c = Client(base_url='unix://var/run/docker.sock')

Params:
  • base_url (str): Refers to the protocol+hostname+port where the Docker server is hosted.
  • version (str): The version of the API the client will use. Specify 'auto' to use the API version provided by the server.
  • timeout (int): The HTTP request timeout, in seconds.
  • tls (bool or TLSConfig): Equivalent CLI options: docker --tls ...

Docker Images#

A Docker image is a read-only template. For example, an image could contain an Ubuntu operating system with Apache and your web application installed. Images are used to create Docker containers. Docker provides a simple way to build new images or update existing images, or you can download Docker images that other people have already created. Docker images are the build component of Docker.

How does a Docker Image work?
We've already seen that Docker images are read-only templates from which Docker containers are launched. Each image consists of a series of layers. Docker makes use of union file systems to combine these layers into a single image. Union file systems allow files and directories of separate file systems, known as branches, to be transparently overlaid, forming a single coherent file system.

One of the reasons Docker is so lightweight is because of these layers. When you change a Docker image—for example, update an application to a new version— a new layer gets built. Thus, rather than replacing the whole image or entirely rebuilding, as you may do with a virtual machine, only that layer is added or updated. Now you don't need to distribute a whole new image, just the update, making distributing Docker images faster and simpler.

Every image starts from a base image, for example ubuntu, a base Ubuntu image, or fedora, a base Fedora image. You can also use images of your own as the basis for a new image, for example if you have a base Apache image you could use this as the base of all your web application images.
Note: Docker usually gets these base images from Docker Hub.

Docker images are then built from these base images using a simple, descriptive set of steps we call instructions. Each instruction creates a new layer in our image. Instructions include actions like:
  • Run a command.
  • Add a file or directory.
  • Create an environment variable.
  • What process to run when launching a container from this image.

These instructions are stored in a file called a Dockerfile. Docker reads this Dockerfile when you request a build of an image, executes the instructions, and returns a final image.

Listing images on the host#

Let's start with listing the images we have locally on our host. You can do this using the docker images command like so: 


From Docker-py, we can list images by API:images with parameters:
  • name (str): Only show images belonging to the repository name
  • quiet (bool): Only show numeric Ids. Returns a list
  • all (bool): Show all images (by default filter out the intermediate image layers)
  • filters (dict): Filters to be processed on the image list. Available filters:
    • dangling (bool)
    • label (str): format either "key" or "key=value"

Let's evaluate it a bit:
>>> images = c.images()
>>> len(images)
10 # We have 10 main images so far

# images is a list object with dict type which contains image information
>>> print("Supported Keys: {0}".format(images[0].keys()))
Supported Keys: [u'Created', u'VirtualSize', u'ParentId', u'RepoTags', u'Id', u'Size']
>>> images[0]
{u'Created': 1430357979, u'VirtualSize': 451960954, u'ParentId': u'f0f4ab557f954f3e04177663a3af90e88641bcdcce1f02ac900dbd9768ef4945', u'RepoTags': [u'192.168.140.133:5000/test:latest'], u'Id': u'3c6f5bae3acaf66fd0ff1bc2400f9689725ab3500287cf38977823bdf3f68ea1', u'Size': 4982522}

Getting a new image#

So how do we get new images? Well Docker will automatically download any image we use that isn't already present on the Docker host. But this can potentially add some time to the launch of a container. If we want to pre-load an image we can download it using the docker pull command. Let's say we'd like to download the centos image.














We can see that each layer of the image has been pulled down and now we can run a container from this image and we won't have to wait to download the image.
# docker run -t -i centos /bin/bash
bash-4.1#

From Docker-py, we can pull images by API:pull with parameters:
  • repository (str): The repository to pull
  • tag (str): The tag to pull
  • stream (bool): Stream the output as a generator
  • insecure_registry (bool): Use an insecure registry
  • auth_config (dict): Override the credentials that Client.login has set for this request auth_config should contain the username andpassword keys to be valid.

Let's evaluate it a little bit:
>>> for line in c.pull('busybox', stream=True):
... print(json.dumps(json.loads(line), indent=4))
...

"status": "Pulling image (latest) from busybox", 
"progressDetail": {}, 
"id": "e72ac664f4f0" 


"status": "Pulling image (latest) from busybox, endpoint: ...", 
"progressDetail": {}, 
"id": "e72ac664f4f0" 

... 

Then we try to list the "busybox" images with command docker images:


Finding images#

One of the features of Docker is that a lot of people have created Docker images for a variety of purposes. Many of these have been uploaded toDocker Hub. We can search these images on the Docker Hub website.


We can also search for images on the command line using the docker search command. Let's say our team wants an image with Ruby and Sinatra installed on which to do our web application development. We can search for a suitable image by using the docker search command to find all the images that contain the term sinatra.


We can see we've returned a lot of images that use the term sinatra. We've returned a list of image names, descriptions, Stars (which measure the social popularity of images - if a user likes an image then they can "star" it), and the Official and Automated build statuses. Official Repositories are a carefully curated set of Docker repositories supported by Docker, Inc. Automated repositories are Automated Builds that allow you to validate the source and content of an image.

From Docker-py, we can search images by API:search with parameters:
  • term (str): A term to search for

Let's evaluate it a little bit:
>>> response = c.search('sinatra')
>>> len(response) 25 # We have 25 records being returned
>>> response[0] # Let's check what we have
{u'star_count': 1, u'is_official': False, u'description': u, u'is_trusted': True, u'name': u'docker.io: docker.io/tdiary/rpaproxy-sinatra'}

Updating and committing an image#

To update an image we first need to create a container from the image we'd like to update.
# docker run -t -i docker.io/centos:centos7 /bin/bash
[root@e691e51f1a63 /]#

Note: Take note of the container ID that has been created, e691e51f1a63, as we'll need it in a moment.

Let's modify the container:
[root@e691e51f1a63 /]# touch test && echo "hello" > test
[root@e691e51f1a63 /]# cat test
hello

Once this has completed let's exit our container using the exit command. Now we have a container with the change we want to make. We can then commit a copy of this container to an image using the docker commit command.
// Now we are in host
# docker ps -a | grep e691e51f1a63 // Check our container exist

// -a, --author= Author
// -m, --message= Commit message
# docker commit -m "test" -a "john" e691e51f1a63 docker.io/centos:centos7_test
d4c76ab64ae356012a0cad7253d055db2201d06ff87d5a87fb800b5afbfa74a8
# docker images | grep centos7_test // Make sure out commit is successful
docker.io/centos centos7_test d4c76ab64ae3 2 minutes ago 215.7 MB

From Docker-py, we can search images by API:commit with parameters:
  • container (str): The image hash of the container
  • repository (str): The repository to push the image to
  • tag (str): The tag to push
  • message (str): A commit message
  • author (str): The name of the author
  • conf (dict): The configuraton for the container. See the Docker remote api for full details.

Let's evaluate it a little bit:
>>> c.commit(message="test", author="john", container="e691e51f1a63", repository="docker.io/centos", tag="centos7_test")
{u'Id': u'31762f4ab284090a01e6da9ccca65bc732c3238021557636dce3f0f9a8f8b748'} 

// Confirm the commitment
>>> images = c.images(cfilters={u'RepoTags':u'docker.io/centos:centos7_test'})
>>> len(images)
1
>>> images[0]
{u'Created': 1433751741, u'VirtualSize': 215676186, u'ParentId': u'fd44297e2ddb050ec4fa9752b7a4e3a8439061991886e2091e7c1f007c906d75', u'RepoTags': [u'docker.io/centos:centos7_test'], u'Id': u'31762f4ab284090a01e6da9ccca65bc732c3238021557636dce3f0f9a8f8b748', u'Size': 82}

Building an image from a Dockerfile#

Using the docker commit command is a pretty simple way of extending an image but it’s a bit cumbersome and it’s not easy to share a development process for images amongst a team. Instead we can use a new command, docker build, to build new images from scratch.

To do this we create a Dockerfile that contains a set of instructions that tell Docker how to build our image.

Let’s create a directory and a Dockerfile first.
# mkdir sinatra
# cd sinatra
# touch Dockerfile

Each instruction of Dockerfile creates a new layer of the image. Let’s look at a simple example now for building our own Sinatra image for our development team.
# This is a comment
FROM ubuntu:14.04
MAINTAINER Kate Smith <ksmith@example.com>
RUN apt-get update && apt-get install -y ruby ruby-dev
RUN gem install sinatra

Let’s look at what our Dockerfile does. Each instruction prefixes a statement and is capitalized.
Note: We use # to indicate a comment

The first instruction FROM tells Docker what the source of our image is, in this case we’re basing our new image on an Ubuntu 14.04 image.

Next we use the MAINTAINER instruction to specify who maintains our new image.

Lastly, we’ve specified two RUN instructions. A RUN instruction executes a command inside the image, for example installing a package. Here we’re updating our APT cache, installing Ruby and RubyGems and then installing the Sinatra gem.
Note: There are a lot more instructions available to us in a Dockerfile.

Now let’s take our Dockerfile and use the docker build command to build an image.
// Usage: docker build [OPTIONS] PATH | URL | - 
// Build a new image from the source code at PATH 
// -t, --tag= Repository name (and optionally a tag) for the image 
# docker build -t ouruser/sinatra:v2 . 
... 
Step 0 : FROM ubuntu:14.04 
14.04: Pulling from docker.io/ubuntu 
... 
Step 1 : MAINTAINER Kate Smith  
---> Running in 5998f67f8ddd 
---> 81dea194dd51 
Removing intermediate container 5998f67f8ddd 
Step 2 : RUN apt-get update && apt-get install -y ruby ruby-dev 
---> Running in 0bb273b9d16b 
... 
Removing intermediate container e8e47e7f2ba8 
Successfully built fa6a9fb0d38c 

# docker images | grep fa6a9fb0d38c 
ouruser/sinatra v2 fa6a9fb0d38c 55 seconds ago 188.3 MB

We’ve specified our docker build command and used the -t flag to identify our new image as belonging to the user ouruser, the repository namesinatra and given it the tag v2. We’ve also specified the location of our Dockerfile using the . to indicate a Dockerfile in the current directory.
Note: You can also specify a path to a Dockerfile.

Now we can see the build process at work. The first thing Docker does is upload the build context: basically the contents of the directory you’re building in. This is done because the Docker daemon does the actual build of the image and it needs the local context to do it.

Next we can see each instruction in the Dockerfile being executed step-by-step. We can see that each step creates a new container, runs the instruction inside that container and then commits that change - just like the docker commit work flow we saw earlier. When all the instructions have executed we’re left with the fa6a9fb0d38c image (also helpfully tagged as ouruser/sinatra:v2) and all intermediate containers will get removed to clean things up. 
Note: An image can’t have more than 127 layers regardless of the storage driver. This limitation is set globally to encourage optimization of the overall size of images.

We can then create a container from our new image.
# docker run -t -i ouruser/sinatra:v2 /bin/bash
root@b117d36bbf5d:/#

From Docker-py, we can pull images by API:build:
>>> from docker import Client
>>> cli = Client(base_url='unix://var/run/docker.sock')
>>> response = [line for line in cli.build(path='.', rm=True, tag='ouruser/sinatra:v2')] >>> response[-1]
'{"stream":"Successfully built 82a8bd2a342d
n"}\r\n' 

>>> cli.images(cfilters={'RepoTags':'ouruser/sinatra:v2'})
[{u'Created': 1434948950, u'VirtualSize': 188284999, u'ParentId': u'76b80c282ac18c8e84a46d651abed2e323a9c724ef6055c7b6a92a610240bd3b', u'RepoTags': [u'ouruser/sinatra:v2'], u'Id': u'82a8bd2a342dc79fdcc4926ac171c123b2241e28adcb1b32b58359e5aa703315', u'Size': 5}]

Also you can create your Dockerfile in code like this:
>>> from io import BytesIO
>>> dockerfile = 
... # Testing Dockerfile
... FROM ubuntu:14.04
... MAINTAINER Kate Smith
... RUN touch TEST
... RUN echo "test" >> TEST
... VOLUME /data
... CMD ["/bin/sh"] 
... ''
>>> f = BytesIO(dockerfile.encode('utf-8'))
>>> response = [line for line in cli.build(fileobj=f, rm=True, tag='repos/stream:tag')] >>> response[-1] '{"stream":"Successfully built 17ff93fee9ce
n"}\r\n'

>>> cli.images(cfilters={'RepoTags':'repos/stream:tag'})
[{u'Created': 1434949444, u'VirtualSize': 188284999, u'ParentId': u'44dff8bd8b52b101beebe2ce9e613be4d1cad56dc9ba9348abd806555d198536', u'RepoTags': [u'repos/stream:tag'], u'Id': u'17ff93fee9ce3c0f81081cfd89fc019e715b84b5a48406adfca8638a1e3af7fc', u'Size': 0}]

Setting tags on an image#

You can also add a tag to an existing image after you commit or build it. We can do this using the docker tag command. Let's add a new tag to the image of last example.
# docker images
... 
docker.io/centos centos7_test 31762f4ab284 41 minutes ago 215.7 MB 


# docker tag 31762f4ab284 docker.io/centos:centos7_test2
# docker images
... 
docker.io/centos centos7_test 31762f4ab284 42 minutes ago 215.7 MB 
docker.io/centos centos7_test2 31762f4ab284 42 minutes ago 215.7 MB 

From Docker-py, we can tag images by API:tag with parameters:
  • image (str): The image to tag
  • repository (str): The repository to set for the tag
  • tag (str): The tag name
  • force (bool): Force

Let's evaluate it a little bit:
>>> c.tag(image='31762f4ab284', repository='docker.io/centos', tag='centos7_test3')
True
>>> len(c.images(cfilters={u'RepoTags':u'docker.io/centos:centos7_test3'}))
1

Push an image to Docker Hub#

Once you've built or created a new image you can push it to Docker Hub using the docker push command. This allows you to share it with others, either publicly, or push it into a private repository.









From Docker-py, we can push images by API:push with parameters:
  • repository (str): The repository to push to
  • tag (str): An optional tag to push
  • stream (bool): Stream the output as a blocking generator
  • insecure_registry (bool): Use http:// to connect to the registry

Below is execution sample:
>>> response = [line for line in c.push('yourname/app', stream=True)]
>>> response
['{"status":"Pushing repository yourname/app (1 tags)"}
n', 
'{"status":"Pushing","progressDetail":{},"id":"511136ea3c5a"}
n', 
'{"status":"Image already pushed, skipping","progressDetail":{}, 
"id":"511136ea3c5a"}
n', 
... 
'{"status":"Pushing tag for rev [918af568e6e5] on { 
https://cdn-registry-1.docker.io/v1/repositories/ 
yourname/app/tags/latest}"}
n'] 

Remove an image from the host#

You can also remove images on your Docker host in a way similar to containers using the docker rmi command.

Let's delete the training/sinatra image as we don't need it anymore.


From Docker-py, we can push images by API:remove_image with parameters:
  • image (str): The image to remove
  • force (bool): Force removal of the image
  • noprune (bool): Do not delete untagged parents

Let's test it a little bit:
>>> c.remove_image(image='docker.io/centos:centos7_test3')
>>> len(c.images(cfilters={u'RepoTags':u'docker.io/centos:centos7_test3'}))
0

FAQ#

ImportError: No module named urllib.parse#

Execution in Python 2.7 with below error message:
  File "/usr/lib/python2.7/site-packages/websocket/_url.py", line 23, in <module>
    from six.moves.urllib.parse import urlparseImportError: No module named urllib.parse

Try to update (ref):
# pip install -U six
... 
Installing collected packages: six 
Found existing installation: six 1.3.0 
DEPRECATION: Uninstalling a distutils installed project (six) has been deprecated and will be removed in a future version. This is due to the fact that uninstalling a distutils project will only partially uninstall the project. 
Uninstalling six-1.3.0: 
Successfully uninstalled six-1.3.0 
Successfully installed six-1.9.0

Supplement #

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!