2015-04-02 15:11:19 +00:00
|
|
|
page_title: Deploying a registry service
|
|
|
|
page_description: Explains how to deploy a registry service
|
|
|
|
page_keywords: registry, service, images, repository
|
2015-03-03 07:33:02 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
# Deploying a registry service
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
This section explains how to deploy a Docker Registry Service either privately
|
|
|
|
for your own company or publicly for other users. For example, your company may
|
|
|
|
require a private registry to support your continuous integration (CI) system as
|
|
|
|
it builds new releases or test servers. Alternatively, your company may have a
|
|
|
|
large number of products or services with images you wish to server in a branded
|
|
|
|
manner.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
Docker's public registry maintains a default `registry` image to assist you in the
|
|
|
|
deployment process. This registry image is sufficient for running local tests
|
|
|
|
but is insufficient for production. For production you should configure and
|
|
|
|
build your own custom registry image from the `docker/distribution` code.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
## Simple example with the official image
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
In this section, you create a local registry using Docker's official image. You
|
|
|
|
push an image to, and then pull the same image from, the registry. This a good
|
|
|
|
exercise for understanding the basic interactions a client has with a
|
|
|
|
local registry.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
1. Install Docker.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
2. Run the `hello-world` image from the Docker public registry.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
$ docker run hello-world
|
|
|
|
|
|
|
|
The `run` command automatically pulls the image from Docker's official images.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
3. Start a registry service on your localhost.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
$ docker run -p 5000:5000 registry
|
|
|
|
|
|
|
|
This starts a registry on your `DOCKER_HOST` running on port `5000`.
|
|
|
|
|
|
|
|
3. List your images.
|
|
|
|
|
|
|
|
$ docker images
|
|
|
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
|
|
|
registry 2.0 bbf0b6ffe923 3 days ago 545.1 MB
|
|
|
|
golang 1.4 121a93c90463 5 days ago 514.9 MB
|
|
|
|
hello-world latest e45a5af57b00 3 months ago 910 B
|
|
|
|
|
|
|
|
Your list should include a `hello-world` image from the earlier run.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
4. Retag the `hello-world` image for your local repoistory.
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
$ docker tag hello-world:latest localhost:5000/hello-mine:latest
|
2015-04-03 18:12:34 +00:00
|
|
|
|
2015-04-02 15:11:19 +00:00
|
|
|
The command labels a `hello-world:latest` using a new tag in the
|
|
|
|
`[REGISTRYHOST/]NAME[:TAG]` format. The `REGISTRYHOST` is this case is
|
|
|
|
`localhost`. In a Mac OSX environment, you'd substitute `$(boot2docker
|
|
|
|
ip):5000` for the `localhost`.
|
|
|
|
|
|
|
|
5. List your new image.
|
|
|
|
|
|
|
|
$ docker images
|
|
|
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
|
|
|
registry 2.0 bbf0b6ffe923 3 days ago 545.1 MB
|
|
|
|
golang 1.4 121a93c90463 5 days ago 514.9 MB
|
|
|
|
hello-world latest e45a5af57b00 3 months ago 910 B
|
|
|
|
localhost:5000/hello-mine latest ef5a5gf57b01 3 months ago 910 B
|
|
|
|
|
|
|
|
You should see your new image in your listing.
|
|
|
|
|
|
|
|
5. Push this new image to your local registry.
|
|
|
|
|
|
|
|
$ docker push localhost:5000/hello-mine:latest
|
|
|
|
|
|
|
|
6. Remove all the unused images from your local environment:
|
|
|
|
|
|
|
|
$ docker rmi -f $(docker images -q -a )
|
|
|
|
|
|
|
|
This command is for illustrative purposes; removing the image forces any `run`
|
|
|
|
to pull from a registry rather than a local cache. If you run `docker images`
|
|
|
|
after this you should not see any instance of `hello-world` or `hello-mine` in
|
|
|
|
your images list.
|
|
|
|
|
|
|
|
$ docker images
|
|
|
|
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
|
|
|
registry 2.0 bbf0b6ffe923 3 days ago 545.1 MB
|
|
|
|
golang 1.4 121a93c90463 5 days ago 514.9 MB
|
|
|
|
|
|
|
|
7. Try running `hello-mine`.
|
|
|
|
|
|
|
|
$ docker run hello-mine
|
|
|
|
Unable to find image 'hello-mine:latest' locally
|
|
|
|
Pulling repository hello-mine
|
|
|
|
FATA[0001] Error: image library/hello-mine:latest not found
|
|
|
|
|
|
|
|
The `run` command fails because your new image doesn't exist in the Docker public
|
|
|
|
registry.
|
|
|
|
|
|
|
|
8. Now, try running the image but specifying the image's registry:
|
|
|
|
|
|
|
|
$ docker run localhost:5000/hello-mine
|
|
|
|
|
|
|
|
If you run `docker images` after this you'll fine a `hello-mine` instance.
|
|
|
|
|
|
|
|
### Making Docker's official registry image production ready
|
|
|
|
|
|
|
|
Docker's official image is for simple tests or debugging. Its configuration is
|
|
|
|
unsuitable for most production instances. For example, any client with access to
|
|
|
|
the server's IP can push and pull images to it. See the next section for
|
|
|
|
information on making this image production ready.
|
|
|
|
|
|
|
|
## Understand production deployment
|
|
|
|
|
|
|
|
When deploying a registry for a production deployment you should consider these
|
|
|
|
factors:
|
|
|
|
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th align="left">
|
|
|
|
backend storage
|
|
|
|
</th>
|
|
|
|
<td>
|
|
|
|
Where should you store the images?
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th align="left">
|
|
|
|
access and/or authentication
|
|
|
|
</th>
|
|
|
|
<td>
|
|
|
|
Do users should have full or controlled access? This can depend on whether
|
|
|
|
you are serving images to the public or internally to your company only.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th align="left">
|
|
|
|
debugging
|
|
|
|
</th>
|
|
|
|
<td>
|
|
|
|
When problems or issues arise, do you have the means of solving them. Logs
|
|
|
|
are useful as is reporting to see trends.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<th align="left">
|
|
|
|
caching
|
|
|
|
</th>
|
|
|
|
<td>
|
|
|
|
Quickly retrieving images can be crucial if you are relying on images for
|
|
|
|
tests, builds, or other automated systems.
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
|
|
|
You can configure your registry features to adjust for these factors. You do
|
|
|
|
this by specifying options on the command line or, more typically, by writing a
|
|
|
|
registry configuration file. The configuration file is in YAML format.
|
|
|
|
|
|
|
|
Docker's official repository image it is preconfigured using the following
|
|
|
|
configuration file:
|
|
|
|
|
|
|
|
```yaml
|
|
|
|
version: 0.1
|
|
|
|
log:
|
|
|
|
level: debug
|
|
|
|
fields:
|
|
|
|
service: registry
|
|
|
|
environment: development
|
|
|
|
storage:
|
|
|
|
cache:
|
|
|
|
layerinfo: inmemory
|
|
|
|
filesystem:
|
|
|
|
rootdirectory: /tmp/registry-dev
|
|
|
|
http:
|
|
|
|
addr: :5000
|
|
|
|
secret: asecretforlocaldevelopment
|
|
|
|
debug:
|
|
|
|
addr: localhost:5001
|
|
|
|
redis:
|
|
|
|
addr: localhost:6379
|
|
|
|
pool:
|
|
|
|
maxidle: 16
|
|
|
|
maxactive: 64
|
|
|
|
idletimeout: 300s
|
|
|
|
dialtimeout: 10ms
|
|
|
|
readtimeout: 10ms
|
|
|
|
writetimeout: 10ms
|
|
|
|
notifications:
|
|
|
|
endpoints:
|
|
|
|
- name: local-8082
|
|
|
|
url: http://localhost:5003/callback
|
|
|
|
headers:
|
|
|
|
Authorization: [Bearer <an example token>]
|
|
|
|
timeout: 1s
|
|
|
|
threshold: 10
|
|
|
|
backoff: 1s
|
|
|
|
disabled: true
|
|
|
|
- name: local-8083
|
|
|
|
url: http://localhost:8083/callback
|
|
|
|
timeout: 1s
|
|
|
|
threshold: 10
|
|
|
|
backoff: 1s
|
|
|
|
disabled: true
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
This configuration is very basic and you can see it would present some problems
|
|
|
|
in a production. For example, the `http` section details the configuration for
|
|
|
|
the HTTP server that hosts the registry. The server is not using even the most
|
|
|
|
minimal transport layer security (TLS). Let's configure that in the next section.
|
|
|
|
|
|
|
|
## Configure TLS on a registry server
|
|
|
|
|
|
|
|
In this section, you configure TLS on the server to enable communication through
|
|
|
|
the `https` protocol. Enabling TLS on the server is the minimum layer of
|
|
|
|
security recommended for running a registry behind a corporate firewall. The
|
|
|
|
easiest way to do this is to build your own registry image.
|
|
|
|
|
|
|
|
### Download the registry source and generated certificates
|
|
|
|
|
|
|
|
1. [Download the registry
|
|
|
|
source](https://github.com/docker/distribution/releases/tag/v2.0.0).
|
|
|
|
|
|
|
|
Alternatively, use the `git clone` command if you are more comfortable with that.
|
|
|
|
|
|
|
|
2. Unpack the the downloaded package into a local directory.
|
|
|
|
|
|
|
|
The package creates a `distribution` directory.
|
|
|
|
|
|
|
|
3. Change to the root of the new `distribution` directory.
|
|
|
|
|
|
|
|
$ cd distribution
|
|
|
|
|
|
|
|
4. Make a `certs` subdirectory.
|
|
|
|
|
|
|
|
$ mkdir certs
|
|
|
|
|
|
|
|
5. Use SSL to generate some self-signed certificates.
|
|
|
|
|
|
|
|
$ openssl req \
|
|
|
|
-newkey rsa:2048 -nodes -keyout certs/domain.key \
|
|
|
|
-x509 -days 365 -out certs/domain.crt
|
|
|
|
|
|
|
|
|
|
|
|
### Add the certificates to the image
|
|
|
|
|
|
|
|
In this section, you copy the certifications from your `certs` directory into
|
|
|
|
your base image.
|
|
|
|
|
|
|
|
1. Edit the `Dockerfile` and add a `CERTS_PATH` environment variable.
|
|
|
|
|
|
|
|
ENV CERTS_PATH /etc/docker/registry/certs
|
|
|
|
|
|
|
|
2. Add a line to make the `CERTS_PATH` in the filesystem.
|
|
|
|
|
|
|
|
RUN mkdir -v $CERTS_PATH
|
|
|
|
|
|
|
|
3. Add `RUN` instructions to hard link your new certifications into this path:
|
|
|
|
|
|
|
|
RUN cp -lv ./certs/domain.crt $CERTS_PATH
|
|
|
|
RUN cp -lv ./certs/domain.key $CERTS_PATH
|
|
|
|
|
|
|
|
This copies your certifications into your container.
|
|
|
|
|
|
|
|
4. Save your work.
|
|
|
|
|
|
|
|
At this point your Dockerfile should look like the following:
|
|
|
|
|
|
|
|
FROM golang:1.4
|
|
|
|
|
|
|
|
ENV CONFIG_PATH /etc/docker/registry/config.yml
|
|
|
|
ENV CERTS_PATH /etc/docker/registry/certs
|
|
|
|
ENV DISTRIBUTION_DIR /go/src/github.com/docker/distribution
|
|
|
|
ENV GOPATH $DISTRIBUTION_DIR/Godeps/_workspace:$GOPATH
|
|
|
|
|
|
|
|
WORKDIR $DISTRIBUTION_DIR
|
|
|
|
COPY . $DISTRIBUTION_DIR
|
|
|
|
RUN make PREFIX=/go clean binaries
|
|
|
|
RUN mkdir -pv "$(dirname $CONFIG_PATH)"
|
|
|
|
RUN mkdir -v $CERTS_PATH
|
|
|
|
RUN cp -lv ./certs/domain.crt $CERTS_PATH
|
|
|
|
RUN cp -lv ./certs/domain.key $CERTS_PATH
|
|
|
|
RUN cp -lv ./cmd/registry/config.yml $CONFIG_PATH
|
|
|
|
|
|
|
|
5. Before you close the Dockerfile look for an instruction to copy the `config.yml` file.
|
|
|
|
|
|
|
|
RUN cp -lv ./cmd/registry/config.yml $CONFIG_PATH
|
|
|
|
|
|
|
|
This is the default registry configuration file. You'll need to edit the file
|
|
|
|
to add TLS.
|
|
|
|
|
|
|
|
### Add TLS to the registry configuration
|
|
|
|
|
|
|
|
1. Edit the `./cmd/registry/config.yml` file.
|
|
|
|
|
|
|
|
$ vi ./cmd/registry/config.yml
|
|
|
|
|
|
|
|
2. Locate the `http` block.
|
|
|
|
|
|
|
|
http:
|
|
|
|
addr: :5000
|
|
|
|
secret: asecretforlocaldevelopment
|
|
|
|
debug:
|
|
|
|
addr: localhost:5001
|
|
|
|
|
|
|
|
3. Add a `tls` block for the server's self-signed certificates:
|
|
|
|
|
|
|
|
http:
|
|
|
|
addr: :5000
|
|
|
|
secret: asecretforlocaldevelopment
|
|
|
|
debug:
|
|
|
|
addr: localhost:5001
|
|
|
|
tls:
|
|
|
|
certificate: /etc/docker/registry/certs/domain.crt
|
|
|
|
key: /etc/docker/registry/certs/domain.key
|
|
|
|
|
|
|
|
You provide the paths to the certificates in the container. If you want
|
|
|
|
two-way authentication across the layer, you can add an optional `clientcas`
|
|
|
|
section.
|
|
|
|
|
|
|
|
4. Save and close the file.
|
|
|
|
|
|
|
|
|
|
|
|
### Run your new image
|
|
|
|
|
|
|
|
1. Build your registry image.
|
|
|
|
|
|
|
|
$ docker build -t secure_registry .
|
|
|
|
|
|
|
|
2. Run your new image.
|
|
|
|
|
|
|
|
$ docker run -p 5000:5000 secure_registry
|
|
|
|
|
|
|
|
Watch the messages at startup. You should see that `tls` is running:
|
|
|
|
|
|
|
|
ubuntu@ip-172-31-34-181:~/repos/distribution$ docker run -p 5000:5000 secure_registry
|
|
|
|
time="2015-04-05T23:56:47Z" level=info msg="endpoint local-8082 disabled, skipping" app.id=3dd802ad-3bd4-4413-b56d-90c4acff41c7 environment=development service=registry
|
|
|
|
time="2015-04-05T23:56:47Z" level=info msg="endpoint local-8083 disabled, skipping" app.id=3dd802ad-3bd4-4413-b56d-90c4acff41c7 environment=development service=registry
|
|
|
|
time="2015-04-05T23:56:47Z" level=info msg="using inmemory layerinfo cache" app.id=3dd802ad-3bd4-4413-b56d-90c4acff41c7 environment=development service=registry
|
|
|
|
time="2015-04-05T23:56:47Z" level=info msg="listening on :5000, tls" app.id=3dd802ad-3bd4-4413-b56d-90c4acff41c7 environment=development service=registry
|
|
|
|
time="2015-04-05T23:56:47Z" level=info msg="debug server listening localhost:5001"
|
|
|
|
2015/04/05 23:57:23 http: TLS handshake error from 172.17.42.1:52057: remote error: unknown certificate authority
|
|
|
|
|
|
|
|
3. Use `curl` to verify that you can connect over `https`.
|
|
|
|
|
|
|
|
$ curl https://localhost:5000
|
|
|
|
|
|
|
|
|
|
|
|
## Adding a middleware configuration
|
|
|
|
|
|
|
|
This section describes how to configure storage middleware in a registry.
|
|
|
|
Middleware allows the registry to server layers via a content delivery network
|
|
|
|
(CDN). This is useful for reducing requests to the storage layer.
|
|
|
|
|
|
|
|
Currently, the registry supports [Amazon
|
|
|
|
Cloudfront](http://aws.amazon.com/cloudfront/). You can only use Cloudfront in
|
|
|
|
conjunction with the S3 storage driver.
|
|
|
|
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<th>Parameter</th>
|
|
|
|
<th>Description</th>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td><code>name</code></td>
|
|
|
|
<td>The storage middleware name. Currently <code>cloudfront</code> is an accepted value.</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td><code>disabled<code></td>
|
|
|
|
<td>Set to <code>false</code> to easily disable the middleware.</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td><code>options:</code></td>
|
|
|
|
<td>
|
|
|
|
A set of key/value options to configure the middleware.
|
|
|
|
<ul>
|
|
|
|
<li><code>baseurl:</code> The Cloudfront base URL.</li>
|
|
|
|
<li><code>privatekey:</code> The location of your AWS private key on the filesystem. </li>
|
|
|
|
<li><code>keypairid:</code> The ID of your Cloudfront keypair. </li>
|
|
|
|
<li><code>duration:</code> The duration in minutes for which the URL is valid. Default is 20. </li>
|
|
|
|
</ul>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
|
|
|
|
The following example illustrates these values:
|
2015-04-03 18:12:34 +00:00
|
|
|
|
|
|
|
```
|
|
|
|
middleware:
|
|
|
|
storage:
|
|
|
|
- name: cloudfront
|
|
|
|
disabled: false
|
|
|
|
options:
|
|
|
|
baseurl: http://d111111abcdef8.cloudfront.net
|
|
|
|
privatekey: /path/to/asecret.pem
|
|
|
|
keypairid: asecret
|
|
|
|
duration: 60
|
2015-04-02 15:11:19 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
>**Note**: Cloudfront keys exist separately to other AWS keys. See
|
|
|
|
>[the documentation on AWS credentials](http://docs.aws.amazon.com/AWSSecurityCredentials/1.0/
|
|
|
|
>AboutAWSCredentials.html#KeyPairs) for more information.
|
|
|
|
|
|
|
|
|
|
|
|
**TODO(stevvooe): Need a "best practice" configuration overview. Perhaps, we can point to a documentation section.
|
|
|
|
|
|
|
|
|