361 lines
7.2 KiB
Markdown
361 lines
7.2 KiB
Markdown
# Docker SDK for Python – Working with Images
|
||
|
||
This document explains how to **manage Docker images** using the Docker SDK for Python. Instead of treating the SDK as a set of function calls, we’ll approach images the same way Docker itself does: as immutable artifacts that are pulled, built, tagged, pushed, inspected, and eventually cleaned up.
|
||
|
||
All examples assume:
|
||
|
||
* Docker is installed and running
|
||
* The Python process has access to the Docker socket
|
||
|
||
---
|
||
|
||
## 1. Creating the Docker Client
|
||
|
||
```python
|
||
import docker
|
||
|
||
client = docker.DockerClient(base_url='unix://var/run/docker.sock')
|
||
ping_docker = client.ping()
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* `DockerClient` establishes a connection to the Docker daemon.
|
||
* `ping()` verifies that Docker is reachable before doing any real work.
|
||
|
||
This is a common pattern in automation:
|
||
|
||
* Fail fast if Docker is unavailable
|
||
* Avoid partial execution later in the script
|
||
|
||
---
|
||
|
||
## 2. Pulling Images from a Registry
|
||
|
||
```python
|
||
def pull_image(name_image, tag_image):
|
||
image = client.images.pull(name_image, tag=tag_image)
|
||
print(image)
|
||
```
|
||
|
||
### What this does
|
||
|
||
* Downloads an image from a registry (Docker Hub or private registry).
|
||
* If the image already exists locally, Docker may reuse layers.
|
||
|
||
Parameters:
|
||
|
||
* `name_image`: repository name (e.g. `alpine`, `nginx`, `myrepo/app`)
|
||
* `tag_image`: specific version or variant (e.g. `3.20`, `latest`)
|
||
|
||
Returned value:
|
||
|
||
* An `Image` object representing the pulled image
|
||
|
||
Why this matters:
|
||
|
||
* Pulling explicitly avoids relying on implicit image downloads
|
||
* Makes automation predictable and repeatable
|
||
|
||
---
|
||
|
||
## 3. Building Images from a Dockerfile
|
||
|
||
### Basic build
|
||
|
||
```python
|
||
def build_image():
|
||
image, logs = client.images.build(
|
||
path=".",
|
||
tag="myapp:1.0"
|
||
)
|
||
|
||
for log in logs:
|
||
if "stream" in log:
|
||
print(log["stream"].strip())
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* `path="."` tells Docker to use the current directory as the build context.
|
||
* Docker automatically looks for a file named `Dockerfile`.
|
||
* `tag="myapp:1.0"` assigns a name and version to the resulting image.
|
||
|
||
The build process returns:
|
||
|
||
* `image`: the final built image object
|
||
* `logs`: a stream of build output messages
|
||
|
||
Printing build logs is important because:
|
||
|
||
* Docker build failures are only visible in logs
|
||
* CI pipelines rely on this output for debugging
|
||
|
||
---
|
||
|
||
## 4. Advanced Build with Custom Dockerfile and Build Arguments
|
||
|
||
```python
|
||
def build_image_2():
|
||
image, logs = client.images.build(
|
||
path=".",
|
||
dockerfile="Dockerfile.prod",
|
||
tag="myapp:prod",
|
||
buildargs={
|
||
"APP_ENV": "production",
|
||
"VERSION": "1.0.0"
|
||
}
|
||
)
|
||
```
|
||
|
||
### Explanation
|
||
|
||
This version adds more control:
|
||
|
||
* `dockerfile="Dockerfile.prod"`
|
||
|
||
* Allows multiple Dockerfiles per project
|
||
* Common for dev vs prod builds
|
||
|
||
* `buildargs`
|
||
|
||
* Passed to `ARG` instructions inside the Dockerfile
|
||
* Enables parameterized builds without editing the Dockerfile
|
||
|
||
Typical DevOps use cases:
|
||
|
||
* Environment-specific builds
|
||
* Injecting version numbers
|
||
* Feature flags during build time
|
||
|
||
---
|
||
|
||
## 5. Tagging Images
|
||
|
||
```python
|
||
def tag_image():
|
||
image = client.images.get("myapp:1.0")
|
||
image.tag("myrepo/myapp", tag="latest")
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* Docker images are immutable, but tags are not.
|
||
* This creates an additional reference to the same image ID.
|
||
|
||
Why tagging is important:
|
||
|
||
* One image can have multiple tags
|
||
* Tags represent lifecycle stages (`1.0`, `prod`, `latest`)
|
||
|
||
This is how promotion pipelines work:
|
||
|
||
* Build once
|
||
* Tag many times
|
||
* Push selectively
|
||
|
||
---
|
||
|
||
## 6. Removing Images
|
||
|
||
```python
|
||
def remove_image():
|
||
client.images.remove("myapp:1.0", force=True)
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* Removes the image reference from the local Docker host.
|
||
* `force=True` removes the image even if it is in use by stopped containers.
|
||
|
||
Use with care:
|
||
|
||
* Running containers still prevent deletion
|
||
* Forced removal is destructive
|
||
|
||
---
|
||
|
||
## 7. Pushing Images to a Registry
|
||
|
||
```python
|
||
def push_image():
|
||
client.images.push(
|
||
repository="myrepo/myapp",
|
||
tag="latest"
|
||
)
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* Uploads the image layers to a registry.
|
||
* Requires prior authentication (`client.login`).
|
||
|
||
Important notes:
|
||
|
||
* Only new or changed layers are pushed
|
||
* Tags determine what remote users pull
|
||
|
||
This step is usually automated in:
|
||
|
||
* CI/CD pipelines
|
||
* Release workflows
|
||
|
||
---
|
||
|
||
## 8. Cleaning Up Unused Images
|
||
|
||
```python
|
||
def prune_images():
|
||
result = client.images.prune()
|
||
print(result)
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* Removes dangling images (untagged and unused).
|
||
* Helps reclaim disk space on build servers.
|
||
|
||
The result includes:
|
||
|
||
* Number of images removed
|
||
* Amount of disk space freed
|
||
|
||
This is essential for:
|
||
|
||
* CI runners
|
||
* Long-lived build machines
|
||
|
||
---
|
||
|
||
## 9. Inspecting Image Metadata
|
||
|
||
```python
|
||
def inspect_image():
|
||
alpine_image = client.images.get("alpine:3.20")
|
||
|
||
print(alpine_image.attrs)
|
||
print(alpine_image.attrs["Id"])
|
||
print(alpine_image.attrs["Size"])
|
||
print(alpine_image.attrs["Config"]["Env"])
|
||
print(alpine_image.attrs["Config"]["Cmd"])
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* `attrs` exposes the raw Docker image inspection data.
|
||
* This is equivalent to `docker image inspect`.
|
||
|
||
Useful fields:
|
||
|
||
* `Id`: content-addressable image hash
|
||
* `Size`: image size in bytes
|
||
* `Config.Env`: environment variables baked into the image
|
||
* `Config.Cmd`: default command
|
||
|
||
This information is often used for:
|
||
|
||
* Debugging unexpected behavior
|
||
* Auditing images
|
||
* Validating build output
|
||
|
||
---
|
||
|
||
## 10. Listing Local Images
|
||
|
||
```python
|
||
def image_list():
|
||
images = client.images.list()
|
||
for item in images:
|
||
print(item.id, item.tags)
|
||
```
|
||
|
||
### Explanation
|
||
|
||
* Lists all images stored locally.
|
||
* Each image may have multiple tags or none.
|
||
|
||
This mirrors:
|
||
|
||
* `docker images`
|
||
|
||
---
|
||
|
||
## 11. Ensuring an Image Exists
|
||
|
||
```python
|
||
def ensure_image(name):
|
||
try:
|
||
client.images.get(name)
|
||
print(f"{name} exists")
|
||
except docker.errors.ImageNotFound:
|
||
print(f"Pulling {name}")
|
||
client.images.pull(name)
|
||
```
|
||
|
||
### Explanation
|
||
|
||
This is a very common DevOps pattern:
|
||
|
||
* Check if the image exists locally
|
||
* Pull it only if necessary
|
||
|
||
Benefits:
|
||
|
||
* Avoids unnecessary network calls
|
||
* Makes scripts idempotent
|
||
|
||
---
|
||
|
||
## 12. Important Exceptions to Know
|
||
|
||
When working with images, you must handle failures explicitly.
|
||
|
||
### `docker.errors.ImageNotFound`
|
||
|
||
* Raised when an image does not exist locally
|
||
* Common when calling `get()`
|
||
|
||
### `docker.errors.BuildError`
|
||
|
||
* Raised when an image build fails
|
||
* Usually due to Dockerfile errors or missing files
|
||
|
||
### `docker.errors.APIError`
|
||
|
||
* Raised for general Docker API failures
|
||
* Includes permission issues, daemon errors, and network problems
|
||
|
||
Catching these exceptions is critical for:
|
||
|
||
* Reliable automation
|
||
* Meaningful error reporting
|
||
* CI/CD stability
|
||
|
||
---
|
||
|
||
## 13. Summary
|
||
|
||
In this section, you learned how to:
|
||
|
||
* Pull images from registries
|
||
* Build images using Dockerfiles
|
||
* Use build arguments and multiple Dockerfiles
|
||
* Tag and push images
|
||
* Inspect and list images
|
||
* Clean up unused images
|
||
* Handle common Docker image errors
|
||
|
||
This image workflow is the backbone of:
|
||
|
||
* CI pipelines
|
||
* Release automation
|
||
* Platform engineering systems
|
||
|
||
---
|
||
|
||
## References
|
||
|
||
Official Docker SDK for Python documentation:
|
||
|
||
* [https://docker-py.readthedocs.io/](https://docker-py.readthedocs.io/)
|