|
1 |
| -### What? |
2 |
| - |
3 |
| -An intricate, insecure, and hackish way of caching Docker images from private registries (eg, not from DockerHub). |
4 |
| -Caches via HTTP man-in-the-middle. |
5 |
| -It is highly dependent on Docker-client behavior, and was only tested against Docker 17.03 on Linux (that's the version recommended by Kubernetes 1.10). |
6 |
| - |
7 |
| -#### Why not use Docker's own registry, which has a mirror feature? |
8 |
| - |
9 |
| -Yes, Docker offers [Registry as a pull through cache](https://docs.docker.com/registry/recipes/mirror/), |
10 |
| -and, in fact, for a caching solution to be complete, you'll want to run one of those. |
11 |
| - |
12 |
| -**Unfortunately** this only covers the DockerHub case. It won't cache images from `quay.io`, `k8s.gcr.io`, `gcr.io`, or any such, including any private registries. |
13 |
| - |
14 |
| -That means that your shiny new Kubernetes cluster is now a bandwidth hog, since every image will be pulled from the Internet on every Node it runs on, with no reuse. |
| 1 | +## docker-registry-proxy |
15 | 2 |
|
16 |
| -This is due to the way the Docker "client" implements `--registry-mirror`, it only ever contacts mirrors for images with no repository reference (eg, from DockerHub). |
17 |
| -When a repository is specified `dockerd` goes directly there, via HTTPS (and also via HTTP if included in a `--insecure-registry` list), thus completely ignoring the configured mirror. |
| 3 | +### TL,DR |
18 | 4 |
|
19 |
| -_Even worse,_ to complement that client-Docker problem, there is also a one-URL limitation on the registry/mirror side of things, so even if it worked we would need to run multiple mirror-registries, one for each mirrored repo. |
20 |
| - |
21 |
| - |
22 |
| -#### Hey but that sounds like an important limitation on Docker's side. Shouldn't they fix it? |
23 |
| - |
24 |
| -**Hell, yes**. Actually if you search on Github you'll find a lot of people with the same issues. |
25 |
| -* This seems to be the [main issue on the Registry side of things](https://github.com/docker/distribution/issues/1431) and shows a lot of the use cases. |
26 |
| -* [Valentin Rothberg](https://github.com/vrothberg) from SUSE has implemented the support |
27 |
| - the client needs [in PR #34319](https://github.com/moby/moby/pull/34319) but after a lot of discussions and |
28 |
| - [much frustration](https://github.com/moby/moby/pull/34319#issuecomment-389783454) it is still unmerged. Sigh. |
| 5 | +A caching proxy for Docker; allows centralized management of registries and their authentication; caches images from *any* registry. |
29 | 6 |
|
| 7 | +### What? |
30 | 8 |
|
31 |
| -**So why not?** I have no idea; it's easy to especulate that "Docker Inc" has no interest in something that makes their main product less attractive. No matter, we'll just _hack_ our way. |
| 9 | +Created as an evolution and simplification of [docker-caching-proxy-multiple-private](https://github.com/rpardini/docker-caching-proxy-multiple-private) |
| 10 | +using the `HTTPS_PROXY` mechanism and injected CA root certificates instead of `/etc/hosts` hacks and _`--insecure-registry` |
32 | 11 |
|
33 |
| -### How? |
| 12 | +As a bonus it allows for centralized management of Docker registry credentials. |
| 13 | + |
| 14 | +You configure the Docker clients (_err... Kubernetes Nodes?_) once, and then all configuration is done on the proxy -- |
| 15 | +for this to work it requires inserting a root CA certificate into system trusted root certs. |
34 | 16 |
|
35 |
| -This solution involves setting up quite a lot of stuff, including DNS hacks. |
| 17 | +#### Why not use Docker's own registry, which has a mirror feature? |
36 | 18 |
|
37 |
| -You'll need a dedicated host for running two caches, both in containers, but you'll need ports 80, 443, and 5000 available. |
| 19 | +Yes, Docker offers [Registry as a pull through cache](https://docs.docker.com/registry/recipes/mirror/), *unfortunately* |
| 20 | +it only covers the DockerHub case. It won't cache images from `quay.io`, `k8s.gcr.io`, `gcr.io`, or any such, including any private registries. |
38 | 21 |
|
39 |
| -I'll refer to the caching proxy host's IP address as 192.168.66.62 in the next sections, substitute for your own. |
| 22 | +That means that your shiny new Kubernetes cluster is now a bandwidth hog, since every image will be pulled from the |
| 23 | +Internet on every Node it runs on, with no reuse. |
40 | 24 |
|
41 |
| -#### 0) A regular DockerHub registry mirror |
| 25 | +This is due to the way the Docker "client" implements `--registry-mirror`, it only ever contacts mirrors for images |
| 26 | +with no repository reference (eg, from DockerHub). |
| 27 | +When a repository is specified `dockerd` goes directly there, via HTTPS (and also via HTTP if included in a |
| 28 | +`--insecure-registry` list), thus completely ignoring the configured mirror. |
42 | 29 |
|
43 |
| -Just follow instructions on [Registry as a pull through cache](https://docs.docker.com/registry/recipes/mirror/) - expose it on 0.0.0.0:5000. |
44 |
| -This will only be used for DockerHub caching, and works well enough. |
| 30 | +#### Docker itself should provide this. |
45 | 31 |
|
46 |
| -#### 1) This caching proxy |
| 32 | +Yeah. Docker Inc should do it. So should NPM, Inc. Wonder why they don't. 😼 |
47 | 33 |
|
48 |
| -This is an `nginx` configured extensively for reverse-proxying HTTP/HTTPS to the registries, and apply caching to it. |
| 34 | +### Usage |
49 | 35 |
|
50 |
| -It should be run in a Docker container, and **needs** be mapped to ports 80 and 443. Theres a Docker volume you can mount for storing the cached layers. |
| 36 | +- Run the proxy on a dedicated machine. |
| 37 | +- Expose port 3128 |
| 38 | +- Map volume `/docker_mirror_cache` for up to 32gb of cached images from all registries |
| 39 | +- Map volume `/ca`, the proxy will store the CA certificate here across restarts |
| 40 | +- Env `REGISTRIES`: space separated list of registries to cache; no need to include Docker Hub, its already there |
| 41 | +- Env `AUTH_REGISTRIES`: space separated list of `registry:username:password` authentication info. Registry hosts here should be listed in the above ENV as well. |
51 | 42 |
|
52 | 43 | ```bash
|
53 |
| -docker run --rm --name docker_caching_proxy -it \ |
54 |
| - -p 0.0.0.0:80:80 -p 0.0.0.0:443:443 \ |
55 |
| - -v /docker_mirror_cache:/docker_mirror_cache \ |
56 |
| - rpardini/docker-caching-proxy-multiple-private:latest |
| 44 | +docker run --rm --name docker_caching_proxy -it \ |
| 45 | + -p 0.0.0.0:3128:3128 \ |
| 46 | + -v $(pwd)/docker_mirror_cache:/docker_mirror_cache \ |
| 47 | + -v $(pwd)/docker_mirror_certs:/ca \ |
| 48 | + -e REGISTRIES="k8s.gcr.io gcr.io quay.io your.own.registry another.private.registry" \ |
| 49 | + -e AUTH_REGISTRIES="your.own.registry:username:password another.private.registry:user:pass" \ |
| 50 | + rpardini/docker-caching-proxy:latest |
57 | 51 | ```
|
58 | 52 |
|
59 |
| -**Important**: the host running the caching proxy container should not have any extra configuration or DNS hacks shown below. |
60 |
| - |
61 |
| -The logging is done to stdout, but the format has been tweaked to show cache MISS/HIT(s) and other useful information for this use case. |
| 53 | +Let's say you did this on host `192.168.66.72`, you can then `curl http://192.168.66.72:3128/ca.crt` and get the proxy CA certificate. |
62 | 54 |
|
63 |
| -It goes to great lengths to try and get the highest hitratio possible, to the point of rewriting headers from registries when they try to redirect to a storage service like Amazon S3 or Google Storage. |
| 55 | +#### Configuring the Docker clients / Kubernetes nodes |
64 | 56 |
|
65 |
| -It is very insecure, anyone with access to the proxy will have access to its cached images regardless of authentication, for example. |
| 57 | +On each Docker host that is to use the cache: |
66 | 58 |
|
| 59 | +- [Configure Docker proxy](https://docs.docker.com/network/proxy/) pointing to the caching server |
| 60 | +- Add the caching server CA certificate to the list of system trusted roots. |
| 61 | +- Restart `dockerd` |
67 | 62 |
|
68 |
| -#### 2) dockerd DNS hacks |
69 |
| - |
70 |
| -We'll need to convince Docker (actually, `dockerd` on very host) to talk to our caching proxy via some sort of DNS hack. |
71 |
| -The simplest for sure is to just include entries in `/etc/hosts` for each registry you want to mirror, plus a fixed address used for redirects: |
| 63 | +Do it all at once, tested on Ubuntu Xenial: |
72 | 64 |
|
73 | 65 | ```bash
|
74 |
| -# /etc/hosts entries for docker caching proxy |
75 |
| -192.168.66.72 docker.proxy |
76 |
| -192.168.66.72 k8s.gcr.io |
77 |
| -192.168.66.72 quay.io |
78 |
| -192.168.66.72 gcr.io |
79 |
| -``` |
80 |
| - |
81 |
| -Only `docker.proxy` is always required, and each registry you want to mirror also needs an entry. |
82 |
| - |
83 |
| -I'm sure you can do stuff to the same effect with your DNS server but I won't go into that. |
84 |
| - |
85 |
| -#### 3) dockerd configuration for mirrors and insecure registries |
86 |
| - |
87 |
| -Of course, we don't have a TLS certificate for `quay.io` et al, so we'll need to tell Docker to treat all proxied registries as _insecure_. |
88 |
| - |
89 |
| -We'll also point Docker to the "regular" registry mirror in item 0. |
90 |
| - |
91 |
| -To do so in one step, edit `/etc/docker/daemon.json` (tested on Docker 17.03 on Ubuntu Xenial only): |
92 |
| - |
93 |
| -```json |
94 |
| -{ |
95 |
| - "insecure-registries": [ |
96 |
| - "k8s.gcr.io", |
97 |
| - "quay.io", |
98 |
| - "gcr.io" |
99 |
| - ], |
100 |
| - "registry-mirrors": [ |
101 |
| - "http://192.168.66.72:5000" |
102 |
| - ] |
103 |
| -} |
| 66 | +# Add environment vars pointing Docker to use the proxy |
| 67 | +cat << EOD > /etc/systemd/system/docker.service.d/http-proxy.conf |
| 68 | +[Service] |
| 69 | +Environment="HTTP_PROXY=http://192.168.66.72:3128/" |
| 70 | +Environment="HTTPS_PROXY=http://192.168.66.72:3128/" |
| 71 | +EOD |
| 72 | + |
| 73 | +# Get the CA certificate from the proxy and make it a trusted root. |
| 74 | +curl http://192.168.66.123:3128/ca.crt > /usr/share/ca-certificates/docker_caching_proxy.crt |
| 75 | +echo docker_caching_proxy.crt >> /etc/ca-certificates.conf |
| 76 | +update-ca-certificates --fresh |
| 77 | + |
| 78 | +# Reload systemd |
| 79 | +systemctl daemon-reload |
| 80 | + |
| 81 | +# Restart dockerd |
| 82 | +systemctl restart docker.service |
104 | 83 | ```
|
105 | 84 |
|
106 |
| -After that, restart the Docker daemon: `systemctl restart docker.service` |
107 |
| - |
108 | 85 | ### Testing
|
109 | 86 |
|
110 |
| -Clear the local `dockerd` of everything not currently running: `docker system prune -a -f` (this prunes everything not currently running, beware). |
| 87 | +Clear `dockerd` of everything not currently running: `docker system prune -a -f` *beware* |
| 88 | + |
111 | 89 | Then do, for example, `docker pull k8s.gcr.io/kube-proxy-amd64:v1.10.4` and watch the logs on the caching proxy, it should list a lot of MISSes.
|
| 90 | + |
112 | 91 | Then, clean again, and pull again. You should see HITs! Success.
|
113 | 92 |
|
114 |
| -### Gotchas |
| 93 | +Do the same for `docker pull ubuntu` and rejoice. |
| 94 | + |
| 95 | +Test your own registry caching and authentication the same way; you don't need `docker login`, or `.docker/config.json` anymore. |
115 | 96 |
|
116 |
| -Of course, this has a lot of limitations |
| 97 | +### Gotchas |
117 | 98 |
|
118 |
| -- Any HTTP/HTTPS request to the domains of the registries will be proxied, not only Docker calls. *beware* |
119 |
| -- If you want to proxy an extra registry you'll have multiple places to edit (`/etc/hosts` and `/etc/docker/daemon.json`) and restart `dockerd` - very brave thing to do in a k8s cluster, so set it up beforehand |
120 |
| -- If you authenticate to a private registry and pull through the proxy, those images will be served to any client that can reach the proxy, even without authentication. *beware* |
| 99 | +- If you authenticate to a private registry and pull through the proxy, those images will be served to any client that can reach the proxy, even without authentication. *beware* |
| 100 | +- Repeat, this will make your private images very public if you're not careful. |
0 commit comments