docs: add hugo site
Signed-off-by: David Karlsson <35727626+dvdksn@users.noreply.github.com>
This commit is contained in:
parent
f7b3869062
commit
e2ae76f1f2
291 changed files with 2833 additions and 2 deletions
73
docs/content/about/_index.md
Normal file
73
docs/content/about/_index.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
---
|
||||
description: Explains what the Registry is, basic use cases and requirements
|
||||
keywords: registry, on-prem, images, tags, repository, distribution, use cases, requirements
|
||||
title: About Registry
|
||||
---
|
||||
|
||||
A registry is a storage and content delivery system, holding named Docker
|
||||
images, available in different tagged versions.
|
||||
|
||||
> Example: the image `distribution/registry`, with tags `2.0` and `2.1`.
|
||||
|
||||
Users interact with a registry by using docker push and pull commands.
|
||||
|
||||
> Example: `docker pull registry-1.docker.io/distribution/registry:2.1`.
|
||||
|
||||
Storage itself is delegated to drivers. The default storage driver is the local
|
||||
posix filesystem, which is suitable for development or small deployments.
|
||||
Additional cloud-based storage drivers like S3, Microsoft Azure and Google Cloud Storage
|
||||
are supported. People looking into using other storage drivers should consider if
|
||||
the driver they'd like to be supported is S3 compatible like many cloud storage systems
|
||||
as adding new storage driver support has been put on hold for the time being.
|
||||
|
||||
Since securing access to your hosted images is paramount, the Registry natively
|
||||
supports TLS and basic authentication.
|
||||
|
||||
The Registry GitHub repository includes additional information about advanced
|
||||
authentication and authorization methods. Only very large or public deployments
|
||||
are expected to extend the Registry in this way.
|
||||
|
||||
Finally, the Registry ships with a robust [notification system](notifications.md),
|
||||
calling webhooks in response to activity, and both extensive logging and reporting,
|
||||
mostly useful for large installations that want to collect metrics.
|
||||
|
||||
## Understanding image naming
|
||||
|
||||
Image names as used in typical docker commands reflect their origin:
|
||||
|
||||
* `docker pull ubuntu` instructs docker to pull an image named `ubuntu` from the official Docker Hub. This is simply a shortcut for the longer `docker pull docker.io/library/ubuntu` command
|
||||
* `docker pull myregistrydomain:port/foo/bar` instructs docker to contact the registry located at `myregistrydomain:port` to find the image `foo/bar`
|
||||
|
||||
You can find out more about the various Docker commands dealing with images in
|
||||
the [official Docker engine documentation](../engine/reference/commandline/cli.md).
|
||||
|
||||
## Use cases
|
||||
|
||||
Running your own Registry is a great solution to integrate with and complement
|
||||
your CI/CD system. In a typical workflow, a commit to your source revision
|
||||
control system would trigger a build on your CI system, which would then push a
|
||||
new image to your Registry if the build is successful. A notification from the
|
||||
Registry would then trigger a deployment on a staging environment, or notify
|
||||
other systems that a new image is available.
|
||||
|
||||
It's also an essential component if you want to quickly deploy a new image over
|
||||
a large cluster of machines.
|
||||
|
||||
Finally, it's the best way to distribute images inside an isolated network.
|
||||
|
||||
## Requirements
|
||||
|
||||
You absolutely need to be familiar with Docker, specifically with regard to
|
||||
pushing and pulling images. You must understand the difference between the
|
||||
daemon and the cli, and at least grasp basic concepts about networking.
|
||||
|
||||
Also, while just starting a registry is fairly easy, operating it in a
|
||||
production environment requires operational skills, just like any other service.
|
||||
You are expected to be familiar with systems availability and scalability,
|
||||
logging and log processing, systems monitoring, and security 101. Strong
|
||||
understanding of http and overall network communications, plus familiarity with
|
||||
golang are certainly useful as well for advanced operations or hacking.
|
||||
|
||||
## Next
|
||||
|
||||
Dive into [deploying your registry](deploying.md)
|
52
docs/content/about/architecture.md
Normal file
52
docs/content/about/architecture.md
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
published: false
|
||||
---
|
||||
|
||||
# Architecture
|
||||
|
||||
## Design
|
||||
**TODO(stevvooe):** Discuss the architecture of the registry, internally and externally, in a few different deployment scenarios.
|
||||
|
||||
### Eventual Consistency
|
||||
|
||||
> **NOTE:** This section belongs somewhere, perhaps in a design document. We
|
||||
> are leaving this here so the information is not lost.
|
||||
|
||||
Running the registry on eventually consistent backends has been part of the
|
||||
design from the beginning. This section covers some of the approaches to
|
||||
dealing with this reality.
|
||||
|
||||
There are a few classes of issues that we need to worry about when
|
||||
implementing something on top of the storage drivers:
|
||||
|
||||
1. Read-After-Write consistency (see this [article on
|
||||
s3](http://shlomoswidler.com/2009/12/read-after-write-consistency-in-amazon.html)).
|
||||
2. [Write-Write Conflicts](http://en.wikipedia.org/wiki/Write%E2%80%93write_conflict).
|
||||
|
||||
In reality, the registry must worry about these kinds of errors when doing the
|
||||
following:
|
||||
|
||||
1. Accepting data into a temporary upload file may not have latest data block
|
||||
yet (read-after-write).
|
||||
2. Moving uploaded data into its blob location (write-write race).
|
||||
3. Modifying the "current" manifest for given tag (write-write race).
|
||||
4. A whole slew of operations around deletes (read-after-write, delete-write
|
||||
races, garbage collection, etc.).
|
||||
|
||||
The backend path layout employs a few techniques to avoid these problems:
|
||||
|
||||
1. Large writes are done to private upload directories. This alleviates most
|
||||
of the corruption potential under multiple writers by avoiding multiple
|
||||
writers.
|
||||
2. Constraints in storage driver implementations, such as support for writing
|
||||
after the end of a file to extend it.
|
||||
3. Digest verification to avoid data corruption.
|
||||
4. Manifest files are stored by digest and cannot change.
|
||||
5. All other non-content files (links, hashes, etc.) are written as an atomic
|
||||
unit. Anything that requires additions and deletions is broken out into
|
||||
separate "files". Last writer still wins.
|
||||
|
||||
Unfortunately, one must play this game when trying to build something like
|
||||
this on top of eventually consistent storage systems. If we run into serious
|
||||
problems, we can wrap the storagedrivers in a shared consistency layer but
|
||||
that would increase complexity and hinder registry cluster performance.
|
78
docs/content/about/compatibility.md
Normal file
78
docs/content/about/compatibility.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
---
|
||||
description: describes get by digest pitfall
|
||||
keywords: registry, manifest, images, tags, repository, distribution, digest
|
||||
title: Registry compatibility
|
||||
---
|
||||
|
||||
## Synopsis
|
||||
If a manifest is pulled by _digest_ from a registry 2.3 with Docker Engine 1.9
|
||||
and older, and the manifest was pushed with Docker Engine 1.10, a security check
|
||||
causes the Engine to receive a manifest it cannot use and the pull fails.
|
||||
|
||||
## Registry manifest support
|
||||
|
||||
Historically, the registry has supported a [single manifest type](./spec/manifest-v2-1.md)
|
||||
known as _Schema 1_.
|
||||
|
||||
With the move toward multiple architecture images, the distribution project
|
||||
introduced two new manifest types: Schema 2 manifests and manifest lists. Registry
|
||||
2.3 supports all three manifest types and sometimes performs an on-the-fly
|
||||
transformation of a manifest before serving the JSON in the response, to
|
||||
preserve compatibility with older versions of Docker Engine.
|
||||
|
||||
This conversion has some implications for pulling manifests by digest and this
|
||||
document enumerates these implications.
|
||||
|
||||
|
||||
## Content Addressable Storage (CAS)
|
||||
|
||||
Manifests are stored and retrieved in the registry by keying off a digest
|
||||
representing a hash of the contents. One of the advantages provided by CAS is
|
||||
security: if the contents are changed, then the digest no longer matches.
|
||||
This prevents any modification of the manifest by a MITM attack or an untrusted
|
||||
third party.
|
||||
|
||||
When a manifest is stored by the registry, this digest is returned in the HTTP
|
||||
response headers and, if events are configured, delivered within the event. The
|
||||
manifest can either be retrieved by the tag, or this digest.
|
||||
|
||||
For registry versions 2.2.1 and below, the registry always stores and
|
||||
serves _Schema 1_ manifests. Engine 1.10 first
|
||||
attempts to send a _Schema 2_ manifest, falling back to sending a
|
||||
Schema 1 type manifest when it detects that the registry does not
|
||||
support the new version.
|
||||
|
||||
|
||||
## Registry v2.3
|
||||
|
||||
### Manifest push with Docker 1.10
|
||||
|
||||
The Engine constructs a _Schema 2_ manifest which the
|
||||
registry persists to disk.
|
||||
|
||||
When the manifest is pulled by digest or tag with Docker Engine 1.10, a
|
||||
_Schema 2_ manifest is returned. Docker Engine 1.10
|
||||
understands the new manifest format.
|
||||
|
||||
When the manifest is pulled by *tag* with Docker Engine 1.9 and older, the
|
||||
manifest is converted on-the-fly to _Schema 1_ and sent in the
|
||||
response. The Docker Engine 1.9 is compatible with this older format.
|
||||
|
||||
When the manifest is pulled by _digest_ with Docker Engine 1.9 and older, the
|
||||
same rewriting process does not happen in the registry. If it did,
|
||||
the digest would no longer match the hash of the manifest and would violate the
|
||||
constraints of CAS.
|
||||
|
||||
For this reason if a manifest is pulled by _digest_ from a registry 2.3 with Docker
|
||||
Engine 1.9 and older, and the manifest was pushed with Docker Engine 1.10, a
|
||||
security check causes the Engine to receive a manifest it cannot use and the
|
||||
pull fails.
|
||||
|
||||
### Manifest push with Docker 1.9 and older
|
||||
|
||||
The Docker Engine constructs a _Schema 1_ manifest which the
|
||||
registry persists to disk.
|
||||
|
||||
When the manifest is pulled by digest or tag with any Docker version, a
|
||||
_Schema 1_ manifest is returned.
|
||||
|
1215
docs/content/about/configuration.md
Normal file
1215
docs/content/about/configuration.md
Normal file
File diff suppressed because it is too large
Load diff
577
docs/content/about/deploying.md
Normal file
577
docs/content/about/deploying.md
Normal file
|
@ -0,0 +1,577 @@
|
|||
---
|
||||
description: Explains how to deploy a registry
|
||||
keywords: registry, on-prem, images, tags, repository, distribution, deployment
|
||||
title: Deploy a registry server
|
||||
---
|
||||
|
||||
Before you can deploy a registry, you need to install Docker on the host.
|
||||
A registry is an instance of the `registry` image, and runs within Docker.
|
||||
|
||||
This topic provides basic information about deploying and configuring a
|
||||
registry. For an exhaustive list of configuration options, see the
|
||||
[configuration reference](configuration.md).
|
||||
|
||||
If you have an air-gapped datacenter, see
|
||||
[Considerations for air-gapped registries](#considerations-for-air-gapped-registries).
|
||||
|
||||
## Run a local registry
|
||||
|
||||
Use a command like the following to start the registry container:
|
||||
|
||||
```console
|
||||
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
|
||||
```
|
||||
|
||||
The registry is now ready to use.
|
||||
|
||||
> **Warning**: These first few examples show registry configurations that are
|
||||
> only appropriate for testing. A production-ready registry must be protected by
|
||||
> TLS and should ideally use an access-control mechanism. Keep reading and then
|
||||
> continue to the [configuration guide](configuration.md) to deploy a
|
||||
> production-ready registry.
|
||||
|
||||
## Copy an image from Docker Hub to your registry
|
||||
|
||||
You can pull an image from Docker Hub and push it to your registry. The
|
||||
following example pulls the `ubuntu:16.04` image from Docker Hub and re-tags it
|
||||
as `my-ubuntu`, then pushes it to the local registry. Finally, the
|
||||
`ubuntu:16.04` and `my-ubuntu` images are deleted locally and the
|
||||
`my-ubuntu` image is pulled from the local registry.
|
||||
|
||||
1. Pull the `ubuntu:16.04` image from Docker Hub.
|
||||
|
||||
```console
|
||||
$ docker pull ubuntu:16.04
|
||||
```
|
||||
|
||||
2. Tag the image as `localhost:5000/my-ubuntu`. This creates an additional tag
|
||||
for the existing image. When the first part of the tag is a hostname and
|
||||
port, Docker interprets this as the location of a registry, when pushing.
|
||||
|
||||
```console
|
||||
$ docker tag ubuntu:16.04 localhost:5000/my-ubuntu
|
||||
```
|
||||
|
||||
3. Push the image to the local registry running at `localhost:5000`:
|
||||
|
||||
```console
|
||||
$ docker push localhost:5000/my-ubuntu
|
||||
```
|
||||
|
||||
4. Remove the locally-cached `ubuntu:16.04` and `localhost:5000/my-ubuntu`
|
||||
images, so that you can test pulling the image from your registry. This
|
||||
does not remove the `localhost:5000/my-ubuntu` image from your registry.
|
||||
|
||||
```console
|
||||
$ docker image remove ubuntu:16.04
|
||||
$ docker image remove localhost:5000/my-ubuntu
|
||||
```
|
||||
|
||||
5. Pull the `localhost:5000/my-ubuntu` image from your local registry.
|
||||
|
||||
```console
|
||||
$ docker pull localhost:5000/my-ubuntu
|
||||
```
|
||||
|
||||
## Stop a local registry
|
||||
|
||||
To stop the registry, use the same `docker container stop` command as with any other
|
||||
container.
|
||||
|
||||
```console
|
||||
$ docker container stop registry
|
||||
```
|
||||
|
||||
To remove the container, use `docker container rm`.
|
||||
|
||||
```console
|
||||
$ docker container stop registry && docker container rm -v registry
|
||||
```
|
||||
|
||||
## Basic configuration
|
||||
|
||||
To configure the container, you can pass additional or modified options to the
|
||||
`docker run` command.
|
||||
|
||||
The following sections provide basic guidelines for configuring your registry.
|
||||
For more details, see the [registry configuration reference](configuration.md).
|
||||
|
||||
### Start the registry automatically
|
||||
|
||||
If you want to use the registry as part of your permanent infrastructure, you
|
||||
should set it to restart automatically when Docker restarts or if it exits.
|
||||
This example uses the `--restart always` flag to set a restart policy for the
|
||||
registry.
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
-p 5000:5000 \
|
||||
--restart=always \
|
||||
--name registry \
|
||||
registry:2
|
||||
```
|
||||
|
||||
### Customize the published port
|
||||
|
||||
If you are already using port 5000, or you want to run multiple local
|
||||
registries to separate areas of concern, you can customize the registry's
|
||||
port settings. This example runs the registry on port 5001 and also names it
|
||||
`registry-test`. Remember, the first part of the `-p` value is the host port
|
||||
and the second part is the port within the container. Within the container, the
|
||||
registry listens on port `5000` by default.
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
-p 5001:5000 \
|
||||
--name registry-test \
|
||||
registry:2
|
||||
```
|
||||
|
||||
If you want to change the port the registry listens on within the container, you
|
||||
can use the environment variable `REGISTRY_HTTP_ADDR` to change it. This command
|
||||
causes the registry to listen on port 5001 within the container:
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
-e REGISTRY_HTTP_ADDR=0.0.0.0:5001 \
|
||||
-p 5001:5001 \
|
||||
--name registry-test \
|
||||
registry:2
|
||||
```
|
||||
|
||||
|
||||
## Storage customization
|
||||
|
||||
### Customize the storage location
|
||||
|
||||
By default, your registry data is persisted as a [docker volume](../storage/volumes.md)
|
||||
on the host filesystem. If you want to store your registry contents at a specific
|
||||
location on your host filesystem, such as if you have an SSD or SAN mounted into
|
||||
a particular directory, you might decide to use a bind mount instead. A bind mount
|
||||
is more dependent on the filesystem layout of the Docker host, but more performant
|
||||
in many situations. The following example bind-mounts the host directory
|
||||
`/mnt/registry` into the registry container at `/var/lib/registry/`.
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
-p 5000:5000 \
|
||||
--restart=always \
|
||||
--name registry \
|
||||
-v /mnt/registry:/var/lib/registry \
|
||||
registry:2
|
||||
```
|
||||
|
||||
### Customize the storage back-end
|
||||
|
||||
By default, the registry stores its data on the local filesystem, whether you
|
||||
use a bind mount or a volume. You can store the registry data in an Amazon S3
|
||||
bucket, Google Cloud Platform, or on another storage back-end by using
|
||||
[storage drivers](./storage-drivers/index.md). For more information, see
|
||||
[storage configuration options](./configuration.md#storage).
|
||||
|
||||
## Run an externally-accessible registry
|
||||
|
||||
Running a registry only accessible on `localhost` has limited usefulness. In
|
||||
order to make your registry accessible to external hosts, you must first secure
|
||||
it using TLS.
|
||||
|
||||
This example is extended in [Run the registry as a
|
||||
service](#run-the-registry-as-a-service) below.
|
||||
|
||||
### Get a certificate
|
||||
|
||||
These examples assume the following:
|
||||
|
||||
- Your registry URL is `https://myregistry.domain.com/`.
|
||||
- Your DNS, routing, and firewall settings allow access to the registry's host
|
||||
on port 443.
|
||||
- You have already obtained a certificate from a certificate authority (CA).
|
||||
|
||||
If you have been issued an _intermediate_ certificate instead, see
|
||||
[use an intermediate certificate](#use-an-intermediate-certificate).
|
||||
|
||||
1. Create a `certs` directory.
|
||||
|
||||
```console
|
||||
$ mkdir -p certs
|
||||
```
|
||||
|
||||
Copy the `.crt` and `.key` files from the CA into the `certs` directory.
|
||||
The following steps assume that the files are named `domain.crt` and
|
||||
`domain.key`.
|
||||
|
||||
2. Stop the registry if it is currently running.
|
||||
|
||||
```console
|
||||
$ docker container stop registry
|
||||
```
|
||||
|
||||
3. Restart the registry, directing it to use the TLS certificate. This command
|
||||
bind-mounts the `certs/` directory into the container at `/certs/`, and sets
|
||||
environment variables that tell the container where to find the `domain.crt`
|
||||
and `domain.key` file. The registry runs on port 443, the default HTTPS port.
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
--restart=always \
|
||||
--name registry \
|
||||
-v "$(pwd)"/certs:/certs \
|
||||
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
|
||||
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
|
||||
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
|
||||
-p 443:443 \
|
||||
registry:2
|
||||
```
|
||||
|
||||
4. Docker clients can now pull from and push to your registry using its
|
||||
external address. The following commands demonstrate this:
|
||||
|
||||
```console
|
||||
$ docker pull ubuntu:16.04
|
||||
$ docker tag ubuntu:16.04 myregistry.domain.com/my-ubuntu
|
||||
$ docker push myregistry.domain.com/my-ubuntu
|
||||
$ docker pull myregistry.domain.com/my-ubuntu
|
||||
```
|
||||
|
||||
#### Use an intermediate certificate
|
||||
|
||||
A certificate issuer may supply you with an *intermediate* certificate. In this
|
||||
case, you must concatenate your certificate with the intermediate certificate to
|
||||
form a *certificate bundle*. You can do this using the `cat` command:
|
||||
|
||||
```console
|
||||
cat domain.crt intermediate-certificates.pem > certs/domain.crt
|
||||
```
|
||||
|
||||
You can use the certificate bundle just as you use the `domain.crt` file in
|
||||
the previous example.
|
||||
|
||||
### Support for Let's Encrypt
|
||||
|
||||
The registry supports using Let's Encrypt to automatically obtain a
|
||||
browser-trusted certificate. For more information on Let's Encrypt, see
|
||||
[https://letsencrypt.org/how-it-works/](https://letsencrypt.org/how-it-works/)
|
||||
and the relevant section of the
|
||||
[registry configuration](configuration.md#letsencrypt).
|
||||
|
||||
### Use an insecure registry (testing only)
|
||||
|
||||
It is possible to use a self-signed certificate, or to use our registry
|
||||
insecurely. Unless you have set up verification for your self-signed
|
||||
certificate, this is for testing only. See [run an insecure registry](insecure.md).
|
||||
|
||||
## Run the registry as a service
|
||||
|
||||
[Swarm services](../engine/swarm/services.md) provide several advantages over
|
||||
standalone containers. They use a declarative model, which means that you define
|
||||
the desired state and Docker works to keep your service in that state. Services
|
||||
provide automatic load balancing scaling, and the ability to control the
|
||||
distribution of your service, among other advantages. Services also allow you to
|
||||
store sensitive data such as TLS certificates in
|
||||
[secrets](../engine/swarm/secrets.md).
|
||||
|
||||
The storage back-end you use determines whether you use a fully scaled service
|
||||
or a service with either only a single node or a node constraint.
|
||||
|
||||
- If you use a distributed storage driver, such as Amazon S3, you can use a
|
||||
fully replicated service. Each worker can write to the storage back-end
|
||||
without causing write conflicts.
|
||||
|
||||
- If you use a local bind mount or volume, each worker node writes to its
|
||||
own storage location, which means that each registry contains a different
|
||||
data set. You can solve this problem by using a single-replica service and a
|
||||
node constraint to ensure that only a single worker is writing to the bind
|
||||
mount.
|
||||
|
||||
The following example starts a registry as a single-replica service, which is
|
||||
accessible on any swarm node on port 80. It assumes you are using the same
|
||||
TLS certificates as in the previous examples.
|
||||
|
||||
First, save the TLS certificate and key as secrets:
|
||||
|
||||
```console
|
||||
$ docker secret create domain.crt certs/domain.crt
|
||||
|
||||
$ docker secret create domain.key certs/domain.key
|
||||
```
|
||||
|
||||
Next, add a label to the node where you want to run the registry.
|
||||
To get the node's name, use `docker node ls`. Substitute your node's name for
|
||||
`node1` below.
|
||||
|
||||
```console
|
||||
$ docker node update --label-add registry=true node1
|
||||
```
|
||||
|
||||
Next, create the service, granting it access to the two secrets and constraining
|
||||
it to only run on nodes with the label `registry=true`. Besides the constraint,
|
||||
you are also specifying that only a single replica should run at a time. The
|
||||
example bind-mounts `/mnt/registry` on the swarm node to `/var/lib/registry/`
|
||||
within the container. Bind mounts rely on the pre-existing source directory,
|
||||
so be sure `/mnt/registry` exists on `node1`. You might need to create it before
|
||||
running the following `docker service create` command.
|
||||
|
||||
By default, secrets are mounted into a service at `/run/secrets/<secret-name>`.
|
||||
|
||||
```console
|
||||
$ docker service create \
|
||||
--name registry \
|
||||
--secret domain.crt \
|
||||
--secret domain.key \
|
||||
--constraint 'node.labels.registry==true' \
|
||||
--mount type=bind,src=/mnt/registry,dst=/var/lib/registry \
|
||||
-e REGISTRY_HTTP_ADDR=0.0.0.0:443 \
|
||||
-e REGISTRY_HTTP_TLS_CERTIFICATE=/run/secrets/domain.crt \
|
||||
-e REGISTRY_HTTP_TLS_KEY=/run/secrets/domain.key \
|
||||
--publish published=443,target=443 \
|
||||
--replicas 1 \
|
||||
registry:2
|
||||
```
|
||||
|
||||
You can access the service on port 443 of any swarm node. Docker sends the
|
||||
requests to the node which is running the service.
|
||||
|
||||
## Load balancing considerations
|
||||
|
||||
One may want to use a load balancer to distribute load, terminate TLS or
|
||||
provide high availability. While a full load balancing setup is outside the
|
||||
scope of this document, there are a few considerations that can make the process
|
||||
smoother.
|
||||
|
||||
The most important aspect is that a load balanced cluster of registries must
|
||||
share the same resources. For the current version of the registry, this means
|
||||
the following must be the same:
|
||||
|
||||
- Storage Driver
|
||||
- HTTP Secret
|
||||
- Redis Cache (if configured)
|
||||
|
||||
Differences in any of the above cause problems serving requests.
|
||||
As an example, if you're using the filesystem driver, all registry instances
|
||||
must have access to the same filesystem root, on
|
||||
the same machine. For other drivers, such as S3 or Azure, they should be
|
||||
accessing the same resource and share an identical configuration.
|
||||
The _HTTP Secret_ coordinates uploads, so also must be the same across
|
||||
instances. Configuring different redis instances works (at the time
|
||||
of writing), but is not optimal if the instances are not shared, because
|
||||
more requests are directed to the backend.
|
||||
|
||||
### Important/Required HTTP-Headers
|
||||
|
||||
Getting the headers correct is very important. For all responses to any
|
||||
request under the "/v2/" url space, the `Docker-Distribution-API-Version`
|
||||
header should be set to the value "registry/2.0", even for a 4xx response.
|
||||
This header allows the docker engine to quickly resolve authentication realms
|
||||
and fallback to version 1 registries, if necessary. Confirming this is setup
|
||||
correctly can help avoid problems with fallback.
|
||||
|
||||
In the same train of thought, you must make sure you are properly sending the
|
||||
`X-Forwarded-Proto`, `X-Forwarded-For`, and `Host` headers to their "client-side"
|
||||
values. Failure to do so usually makes the registry issue redirects to internal
|
||||
hostnames or downgrading from https to http.
|
||||
|
||||
A properly secured registry should return 401 when the "/v2/" endpoint is hit
|
||||
without credentials. The response should include a `WWW-Authenticate`
|
||||
challenge, providing guidance on how to authenticate, such as with basic auth
|
||||
or a token service. If the load balancer has health checks, it is recommended
|
||||
to configure it to consider a 401 response as healthy and any other as down.
|
||||
This secures your registry by ensuring that configuration problems with
|
||||
authentication don't accidentally expose an unprotected registry. If you're
|
||||
using a less sophisticated load balancer, such as Amazon's Elastic Load
|
||||
Balancer, that doesn't allow one to change the healthy response code, health
|
||||
checks can be directed at "/", which always returns a `200 OK` response.
|
||||
|
||||
## Restricting access
|
||||
|
||||
Except for registries running on secure local networks, registries should always
|
||||
implement access restrictions.
|
||||
|
||||
### Native basic auth
|
||||
|
||||
The simplest way to achieve access restriction is through basic authentication
|
||||
(this is very similar to other web servers' basic authentication mechanism).
|
||||
This example uses native basic authentication using `htpasswd` to store the
|
||||
secrets.
|
||||
|
||||
> **Warning**:
|
||||
> You **cannot** use authentication with authentication schemes that send
|
||||
> credentials as clear text. You must
|
||||
> [configure TLS first](deploying.md#run-an-externally-accessible-registry) for
|
||||
> authentication to work.
|
||||
{:.warning}
|
||||
|
||||
> **Warning**
|
||||
> The official registry image **only** supports htpasswd credentials in
|
||||
> bcrypt format, so if you omit the `-B` option when generating the credential
|
||||
> using htpasswd, all authentication attempts will fail.
|
||||
{:.warning}
|
||||
|
||||
1. Create a password file with one entry for the user `testuser`, with password
|
||||
`testpassword`:
|
||||
|
||||
```console
|
||||
$ mkdir auth
|
||||
$ docker run \
|
||||
--entrypoint htpasswd \
|
||||
httpd:2 -Bbn testuser testpassword > auth/htpasswd
|
||||
```
|
||||
|
||||
On Windows, make sure the output file is correctly encoded:
|
||||
|
||||
```powershell
|
||||
docker run --rm --entrypoint htpasswd httpd:2 -Bbn testuser testpassword | Set-Content -Encoding ASCII auth/htpasswd
|
||||
```
|
||||
|
||||
2. Stop the registry.
|
||||
|
||||
```console
|
||||
$ docker container stop registry
|
||||
```
|
||||
|
||||
3. Start the registry with basic authentication.
|
||||
|
||||
```console
|
||||
$ docker run -d \
|
||||
-p 5000:5000 \
|
||||
--restart=always \
|
||||
--name registry \
|
||||
-v "$(pwd)"/auth:/auth \
|
||||
-e "REGISTRY_AUTH=htpasswd" \
|
||||
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
|
||||
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
|
||||
-v "$(pwd)"/certs:/certs \
|
||||
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
|
||||
-e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
|
||||
registry:2
|
||||
```
|
||||
|
||||
4. Try to pull an image from the registry, or push an image to the registry.
|
||||
These commands fail.
|
||||
|
||||
5. Log in to the registry.
|
||||
|
||||
```console
|
||||
$ docker login myregistrydomain.com:5000
|
||||
```
|
||||
|
||||
Provide the username and password from the first step.
|
||||
|
||||
Test that you can now pull an image from the registry or push an image to
|
||||
the registry.
|
||||
|
||||
> **X509 errors**: X509 errors usually indicate that you are attempting to use
|
||||
> a self-signed certificate without configuring the Docker daemon correctly.
|
||||
> See [run an insecure registry](insecure.md).
|
||||
|
||||
### More advanced authentication
|
||||
|
||||
You may want to leverage more advanced basic auth implementations by using a
|
||||
proxy in front of the registry. See the [recipes list](recipes/index.md).
|
||||
|
||||
The registry also supports delegated authentication which redirects users to a
|
||||
specific trusted token server. This approach is more complicated to set up, and
|
||||
only makes sense if you need to fully configure ACLs and need more control over
|
||||
the registry's integration into your global authorization and authentication
|
||||
systems. Refer to the following [background information](spec/auth/token.md) and
|
||||
[configuration information here](configuration.md#auth).
|
||||
|
||||
This approach requires you to implement your own authentication system or
|
||||
leverage a third-party implementation.
|
||||
|
||||
## Deploy your registry using a Compose file
|
||||
|
||||
If your registry invocation is advanced, it may be easier to use a Docker
|
||||
compose file to deploy it, rather than relying on a specific `docker run`
|
||||
invocation. Use the following example `docker-compose.yml` as a template.
|
||||
|
||||
```yaml
|
||||
registry:
|
||||
restart: always
|
||||
image: registry:2
|
||||
ports:
|
||||
- 5000:5000
|
||||
environment:
|
||||
REGISTRY_HTTP_TLS_CERTIFICATE: /certs/domain.crt
|
||||
REGISTRY_HTTP_TLS_KEY: /certs/domain.key
|
||||
REGISTRY_AUTH: htpasswd
|
||||
REGISTRY_AUTH_HTPASSWD_PATH: /auth/htpasswd
|
||||
REGISTRY_AUTH_HTPASSWD_REALM: Registry Realm
|
||||
volumes:
|
||||
- /path/data:/var/lib/registry
|
||||
- /path/certs:/certs
|
||||
- /path/auth:/auth
|
||||
```
|
||||
|
||||
Replace `/path` with the directory which contains the `certs/` and `auth/`
|
||||
directories.
|
||||
{:.warning}
|
||||
|
||||
Start your registry by issuing the following command in the directory containing
|
||||
the `docker-compose.yml` file:
|
||||
|
||||
```console
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
## Considerations for air-gapped registries
|
||||
|
||||
You can run a registry in an environment with no internet connectivity.
|
||||
However, if you rely on any images which are not local, you need to consider the
|
||||
following:
|
||||
|
||||
- You may need to build your local registry's data volume on a connected
|
||||
host where you can run `docker pull` to get any images which are available
|
||||
remotely, and then migrate the registry's data volume to the air-gapped
|
||||
network.
|
||||
|
||||
- Certain images, such as the official Microsoft Windows base images, are not
|
||||
distributable. This means that when you push an image based on one of these
|
||||
images to your private registry, the non-distributable layers are **not**
|
||||
pushed, but are always fetched from their authorized location. This is fine
|
||||
for internet-connected hosts, but not in an air-gapped set-up.
|
||||
|
||||
You can configure the Docker daemon to allow pushing non-distributable layers
|
||||
to private registries.
|
||||
**This is only useful in air-gapped set-ups in the presence of
|
||||
non-distributable images, or in extremely bandwidth-limited situations.**
|
||||
You are responsible for ensuring that you are in compliance with the terms of
|
||||
use for non-distributable layers.
|
||||
|
||||
1. Edit the `daemon.json` file, which is located in `/etc/docker/` on Linux
|
||||
hosts and `C:\ProgramData\docker\config\daemon.json` on Windows Server.
|
||||
Assuming the file was previously empty, add the following contents:
|
||||
|
||||
```json
|
||||
{
|
||||
"allow-nondistributable-artifacts": ["myregistrydomain.com:5000"]
|
||||
}
|
||||
```
|
||||
|
||||
The value is an array of registry addresses, separated by commas.
|
||||
|
||||
Save and exit the file.
|
||||
|
||||
2. Restart Docker.
|
||||
|
||||
3. Restart the registry if it does not start automatically.
|
||||
|
||||
4. When you push images to the registries in the list, their
|
||||
non-distributable layers are pushed to the registry.
|
||||
|
||||
> **Warning**: Non-distributable artifacts typically have restrictions on
|
||||
> how and where they can be distributed and shared. Only use this feature
|
||||
> to push artifacts to private registries and ensure that you are in
|
||||
> compliance with any terms that cover redistributing non-distributable
|
||||
> artifacts.
|
||||
|
||||
|
||||
## Next steps
|
||||
|
||||
More specific and advanced information is available in the following sections:
|
||||
|
||||
- [Configuration reference](configuration.md)
|
||||
- [Working with notifications](notifications.md)
|
||||
- [Advanced "recipes"](recipes/index.md)
|
||||
- [Registry API](spec/api.md)
|
||||
- [Storage driver model](storage-drivers/index.md)
|
||||
- [Token authentication](spec/auth/token.md)
|
20
docs/content/about/deprecated.md
Normal file
20
docs/content/about/deprecated.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
description: describes deprecated functionality
|
||||
keywords: registry, manifest, images, signatures, repository, distribution, digest
|
||||
title: Docker Registry deprecation
|
||||
---
|
||||
|
||||
This document details functionality or components which are deprecated within
|
||||
the registry.
|
||||
|
||||
### v2.5.0
|
||||
|
||||
The signature store has been removed from the registry. Since `v2.4.0` it has
|
||||
been possible to configure the registry to generate manifest signatures rather
|
||||
than load them from storage. In this version of the registry this becomes
|
||||
the default behavior. Signatures which are attached to manifests on put are
|
||||
not stored in the registry. This does not alter the functional behavior of
|
||||
the registry.
|
||||
|
||||
Old signatures blobs can be removed from the registry storage by running the
|
||||
garbage-collect subcommand.
|
124
docs/content/about/garbage-collection.md
Normal file
124
docs/content/about/garbage-collection.md
Normal file
|
@ -0,0 +1,124 @@
|
|||
---
|
||||
description: High level discussion of garbage collection
|
||||
keywords: registry, garbage, images, tags, repository, distribution
|
||||
title: Garbage collection
|
||||
---
|
||||
|
||||
As of v2.4.0 a garbage collector command is included within the registry binary.
|
||||
This document describes what this command does and how and why it should be used.
|
||||
|
||||
## About garbage collection
|
||||
|
||||
In the context of the Docker registry, garbage collection is the process of
|
||||
removing blobs from the filesystem when they are no longer referenced by a
|
||||
manifest. Blobs can include both layers and manifests.
|
||||
|
||||
Registry data can occupy considerable amounts of disk space. In addition,
|
||||
garbage collection can be a security consideration, when it is desirable to ensure
|
||||
that certain layers no longer exist on the filesystem.
|
||||
|
||||
## Garbage collection in practice
|
||||
|
||||
Filesystem layers are stored by their content address in the Registry. This
|
||||
has many advantages, one of which is that data is stored once and referred to by manifests.
|
||||
See [here](compatibility.md#content-addressable-storage-cas) for more details.
|
||||
|
||||
Layers are therefore shared amongst manifests; each manifest maintains a reference
|
||||
to the layer. As long as a layer is referenced by one manifest, it cannot be garbage
|
||||
collected.
|
||||
|
||||
Manifests and layers can be `deleted` with the registry API (refer to the API
|
||||
documentation [here](spec/api.md#deleting-a-layer) and
|
||||
[here](spec/api.md#deleting-an-image) for details). This API removes references
|
||||
to the target and makes them eligible for garbage collection. It also makes them
|
||||
unable to be read via the API.
|
||||
|
||||
If a layer is deleted, it is removed from the filesystem when garbage collection
|
||||
is run. If a manifest is deleted the layers to which it refers are removed from
|
||||
the filesystem if no other manifests refers to them.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
In this example manifest A references two layers: `a` and `b`. Manifest `B` references
|
||||
layers `a` and `c`. In this state, nothing is eligible for garbage collection:
|
||||
|
||||
```
|
||||
A -----> a <----- B
|
||||
\--> b |
|
||||
c <--/
|
||||
```
|
||||
|
||||
Manifest B is deleted via the API:
|
||||
|
||||
```
|
||||
A -----> a B
|
||||
\--> b
|
||||
c
|
||||
```
|
||||
|
||||
In this state layer `c` no longer has a reference and is eligible for garbage
|
||||
collection. Layer `a` had one reference removed but not garbage
|
||||
collected as it is still referenced by manifest `A`. The blob representing
|
||||
manifest `B` is eligible for garbage collection.
|
||||
|
||||
After garbage collection has been run, manifest `A` and its blobs remain.
|
||||
|
||||
```
|
||||
A -----> a
|
||||
\--> b
|
||||
```
|
||||
|
||||
|
||||
### More details about garbage collection
|
||||
|
||||
Garbage collection runs in two phases. First, in the 'mark' phase, the process
|
||||
scans all the manifests in the registry. From these manifests, it constructs a
|
||||
set of content address digests. This set is the 'mark set' and denotes the set
|
||||
of blobs to *not* delete. Secondly, in the 'sweep' phase, the process scans all
|
||||
the blobs and if a blob's content address digest is not in the mark set, the
|
||||
process deletes it.
|
||||
|
||||
|
||||
> **Note**: You should ensure that the registry is in read-only mode or not running at
|
||||
> all. If you were to upload an image while garbage collection is running, there is the
|
||||
> risk that the image's layers are mistakenly deleted leading to a corrupted image.
|
||||
|
||||
This type of garbage collection is known as stop-the-world garbage collection.
|
||||
|
||||
## Run garbage collection
|
||||
|
||||
Garbage collection can be run as follows
|
||||
|
||||
`bin/registry garbage-collect [--dry-run] /path/to/config.yml`
|
||||
|
||||
The garbage-collect command accepts a `--dry-run` parameter, which prints the progress
|
||||
of the mark and sweep phases without removing any data. Running with a log level of `info`
|
||||
gives a clear indication of items eligible for deletion.
|
||||
|
||||
The config.yml file should be in the following format:
|
||||
|
||||
```yaml
|
||||
version: 0.1
|
||||
storage:
|
||||
filesystem:
|
||||
rootdirectory: /registry/data
|
||||
```
|
||||
|
||||
_Sample output from a dry run garbage collection with registry log level set to `info`_
|
||||
|
||||
```
|
||||
hello-world
|
||||
hello-world: marking manifest sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf
|
||||
hello-world: marking blob sha256:03f4658f8b782e12230c1783426bd3bacce651ce582a4ffb6fbbfa2079428ecb
|
||||
hello-world: marking blob sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
|
||||
hello-world: marking configuration sha256:690ed74de00f99a7d00a98a5ad855ac4febd66412be132438f9b8dbd300a937d
|
||||
ubuntu
|
||||
|
||||
4 blobs marked, 5 blobs eligible for deletion
|
||||
blob eligible for deletion: sha256:28e09fddaacbfc8a13f82871d9d66141a6ed9ca526cb9ed295ef545ab4559b81
|
||||
blob eligible for deletion: sha256:7e15ce58ccb2181a8fced7709e9893206f0937cc9543bc0c8178ea1cf4d7e7b5
|
||||
blob eligible for deletion: sha256:87192bdbe00f8f2a62527f36bb4c7c7f4eaf9307e4b87e8334fb6abec1765bcb
|
||||
blob eligible for deletion: sha256:b549a9959a664038fc35c155a95742cf12297672ca0ae35735ec027d55bf4e97
|
||||
blob eligible for deletion: sha256:f251d679a7c61455f06d793e43c06786d7766c88b8c24edf242b2c08e3c3f599
|
||||
```
|
68
docs/content/about/glossary.md
Normal file
68
docs/content/about/glossary.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
published: false
|
||||
---
|
||||
|
||||
# Glossary
|
||||
|
||||
This page contains definitions for distribution related terms.
|
||||
|
||||
<dl>
|
||||
<dt id="blob"><h4>Blob</h4></dt>
|
||||
<dd>
|
||||
<blockquote>A blob is any kind of content that is stored by a Registry under a content-addressable identifier (a "digest").</blockquote>
|
||||
<p>
|
||||
<a href="#layer">Layers</a> are a good example of "blobs".
|
||||
</p>
|
||||
</dd>
|
||||
|
||||
<dt id="image"><h4>Image</h4></dt>
|
||||
<dd>
|
||||
<blockquote>An image is a named set of immutable data from which a Docker container can be created.</blockquote>
|
||||
<p>
|
||||
An image is represented by a json file called a <a href="#manifest">manifest</a>, and is conceptually a set of <a href="#layer">layers</a>.
|
||||
|
||||
Image names indicate the location where they can be pulled from and pushed to, as they usually start with a <a href="#registry">registry</a> domain name and port.
|
||||
|
||||
</p>
|
||||
</dd>
|
||||
|
||||
<dt id="layer"><h4>Layer</h4></dt>
|
||||
<dd>
|
||||
<blockquote>A layer is a tar archive bundling partial content from a filesystem.</blockquote>
|
||||
<p>
|
||||
Layers from an <a href="#image">image</a> are usually extracted in order on top of each other to make up a root filesystem from which containers run out.
|
||||
</p>
|
||||
</dd>
|
||||
|
||||
<dt id="manifest"><h4>Manifest</h4></dt>
|
||||
<dd><blockquote>A manifest is the JSON representation of an image.</blockquote></dd>
|
||||
|
||||
<dt id="namespace"><h4>Namespace</h4></dt>
|
||||
<dd><blockquote>A namespace is a collection of repositories with a common name prefix.</blockquote>
|
||||
<p>
|
||||
The namespace with an empty prefix is considered the Global Namespace.
|
||||
</p>
|
||||
</dd>
|
||||
|
||||
<dt id="registry"><h4>Registry</h4></dt>
|
||||
<dd><blockquote>A registry is a service that let you store and deliver <a href="#images">images</a>.</blockquote>
|
||||
</dd>
|
||||
|
||||
<dt id="registry"><h4>Repository</h4></dt>
|
||||
<dd>
|
||||
<blockquote>A repository is a set of data containing all versions of a given image.</blockquote>
|
||||
</dd>
|
||||
|
||||
<dt id="scope"><h4>Scope</h4></dt>
|
||||
<dd><blockquote>A scope is the portion of a namespace onto which a given authorization token is granted.</blockquote></dd>
|
||||
|
||||
<dt id="tag"><h4>Tag</h4></dt>
|
||||
<dd><blockquote>A tag is conceptually a "version" of a <a href="#image">named image</a>.</blockquote>
|
||||
<p>
|
||||
Example: `docker pull myimage:latest` instructs docker to pull the image "myimage" in version "latest".
|
||||
</p>
|
||||
|
||||
</dd>
|
||||
|
||||
|
||||
</dl>
|
14
docs/content/about/help.md
Normal file
14
docs/content/about/help.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
---
|
||||
description: Getting help with the Registry
|
||||
keywords: registry, on-prem, images, tags, repository, distribution, help, 101, TL;DR
|
||||
title: Get help
|
||||
---
|
||||
|
||||
If you need help, or just want to chat about development, you can reach us on the #distribution channel in the CNCF Slack.
|
||||
|
||||
If you want to report a bug:
|
||||
|
||||
- be sure to first read about [how to contribute](https://github.com/distribution/distribution/blob/master/CONTRIBUTING.md).
|
||||
- you can then do so on the [GitHub project bugtracker](https://github.com/distribution/distribution/issues).
|
||||
|
||||
You can also find out more about the Docker's project [Getting Help resources](../opensource/ways.md).
|
165
docs/content/about/insecure.md
Normal file
165
docs/content/about/insecure.md
Normal file
|
@ -0,0 +1,165 @@
|
|||
---
|
||||
description: Deploying a Registry in an insecure fashion
|
||||
keywords: registry, on-prem, images, tags, repository, distribution, insecure
|
||||
title: Test an insecure registry
|
||||
---
|
||||
|
||||
While it's highly recommended to secure your registry using a TLS certificate
|
||||
issued by a known CA, you can choose to use self-signed certificates, or use
|
||||
your registry over an unencrypted HTTP connection. Either of these choices
|
||||
involves security trade-offs and additional configuration steps.
|
||||
|
||||
## Deploy a plain HTTP registry
|
||||
|
||||
> **Warning**:
|
||||
> It's not possible to use an insecure registry with basic authentication.
|
||||
{:.warning}
|
||||
|
||||
This procedure configures Docker to entirely disregard security for your
|
||||
registry. This is **very** insecure and is not recommended. It exposes your
|
||||
registry to trivial man-in-the-middle (MITM) attacks. Only use this solution for
|
||||
isolated testing or in a tightly controlled, air-gapped environment.
|
||||
|
||||
1. Edit the `daemon.json` file, whose default location is
|
||||
`/etc/docker/daemon.json` on Linux or
|
||||
`C:\ProgramData\docker\config\daemon.json` on Windows Server. If you use
|
||||
Docker Desktop for Mac or Docker Desktop for Windows, click the Docker icon, choose
|
||||
**Preferences** (Mac) or **Settings** (Windows), and choose **Docker Engine**.
|
||||
|
||||
If the `daemon.json` file does not exist, create it. Assuming there are no
|
||||
other settings in the file, it should have the following contents:
|
||||
|
||||
```json
|
||||
{
|
||||
"insecure-registries" : ["myregistrydomain.com:5000"]
|
||||
}
|
||||
```
|
||||
|
||||
Substitute the address of your insecure registry for the one in the example.
|
||||
|
||||
With insecure registries enabled, Docker goes through the following steps:
|
||||
|
||||
- First, try using HTTPS.
|
||||
- If HTTPS is available but the certificate is invalid, ignore the error
|
||||
about the certificate.
|
||||
- If HTTPS is not available, fall back to HTTP.
|
||||
|
||||
|
||||
2. Restart Docker for the changes to take effect.
|
||||
|
||||
|
||||
Repeat these steps on every Engine host that wants to access your registry.
|
||||
|
||||
|
||||
## Use self-signed certificates
|
||||
|
||||
> **Warning**:
|
||||
> Using this along with basic authentication requires to **also** trust the certificate into the OS cert store for some versions of docker (see below)
|
||||
{:.warning}
|
||||
|
||||
This is more secure than the insecure registry solution.
|
||||
|
||||
1. Generate your own certificate:
|
||||
|
||||
```console
|
||||
$ mkdir -p certs
|
||||
|
||||
$ openssl req \
|
||||
-newkey rsa:4096 -nodes -sha256 -keyout certs/domain.key \
|
||||
-addext "subjectAltName = DNS:myregistry.domain.com" \
|
||||
-x509 -days 365 -out certs/domain.crt
|
||||
```
|
||||
|
||||
Be sure to use the name `myregistry.domain.com` as a CN.
|
||||
|
||||
2. Use the result to [start your registry with TLS enabled](./deploying.md#get-a-certificate).
|
||||
|
||||
3. Instruct every Docker daemon to trust that certificate. The way to do this
|
||||
depends on your OS.
|
||||
|
||||
- **Linux**: Copy the `domain.crt` file to
|
||||
`/etc/docker/certs.d/myregistrydomain.com:5000/ca.crt` on every Docker
|
||||
host. You do not need to restart Docker.
|
||||
|
||||
- **Windows Server**:
|
||||
|
||||
1. Open Windows Explorer, right-click the `domain.crt`
|
||||
file, and choose Install certificate. When prompted, select the following
|
||||
options:
|
||||
|
||||
| Store location | local machine |
|
||||
| Place all certificates in the following store | selected |
|
||||
|
||||
2. Click **Browser** and select **Trusted Root Certificate Authorities**.
|
||||
|
||||
3. Click **Finish**. Restart Docker.
|
||||
|
||||
- **Docker Desktop for Mac**: Follow the instructions in
|
||||
[Adding custom CA certificates](../desktop/mac/index.md#add-tls-certificates){: target="_blank" rel="noopener" class="_"}.
|
||||
Restart Docker.
|
||||
|
||||
- **Docker Desktop for Windows**: Follow the instructions in
|
||||
[Adding custom CA certificates](../desktop/windows/index.md#adding-tls-certificates){: target="_blank" rel="noopener" class="_"}.
|
||||
Restart Docker.
|
||||
|
||||
|
||||
## Troubleshoot insecure registry
|
||||
|
||||
This section lists some common failures and how to recover from them.
|
||||
|
||||
### Failing...
|
||||
|
||||
Failing to configure the Engine daemon and trying to pull from a registry that is not using
|
||||
TLS results in the following message:
|
||||
|
||||
```none
|
||||
FATA[0000] Error response from daemon: v1 ping attempt failed with error:
|
||||
Get https://myregistrydomain.com:5000/v1/_ping: tls: oversized record received with length 20527.
|
||||
If this private registry supports only HTTP or HTTPS with an unknown CA certificate, add
|
||||
`--insecure-registry myregistrydomain.com:5000` to the daemon's arguments.
|
||||
In the case of HTTPS, if you have access to the registry's CA certificate, no need for the flag;
|
||||
simply place the CA certificate at /etc/docker/certs.d/myregistrydomain.com:5000/ca.crt
|
||||
```
|
||||
|
||||
### Docker still complains about the certificate when using authentication?
|
||||
|
||||
When using authentication, some versions of Docker also require you to trust the
|
||||
certificate at the OS level.
|
||||
|
||||
#### Ubuntu
|
||||
|
||||
```console
|
||||
$ cp certs/domain.crt /usr/local/share/ca-certificates/myregistrydomain.com.crt
|
||||
update-ca-certificates
|
||||
```
|
||||
|
||||
#### Red Hat Enterprise Linux
|
||||
|
||||
```console
|
||||
$ cp certs/domain.crt /etc/pki/ca-trust/source/anchors/myregistrydomain.com.crt
|
||||
update-ca-trust
|
||||
```
|
||||
|
||||
#### Oracle Linux
|
||||
|
||||
```console
|
||||
$ update-ca-trust enable
|
||||
```
|
||||
|
||||
Restart Docker for the changes to take effect.
|
||||
|
||||
### Windows
|
||||
|
||||
Open Windows Explorer, right-click the certificate, and choose
|
||||
**Install certificate**.
|
||||
|
||||
Then, select the following options:
|
||||
|
||||
* Store location: local machine
|
||||
* Check **place all certificates in the following store**
|
||||
* Click **Browser**, and select **Trusted Root Certificate Authorities**
|
||||
* Click **Finish**
|
||||
|
||||
[Learn more about managing TLS certificates](https://technet.microsoft.com/en-us/library/cc754841(v=ws.11).aspx#BKMK_addlocal).
|
||||
|
||||
After adding the CA certificate to Windows, restart Docker Desktop for Windows.
|
348
docs/content/about/notifications.md
Normal file
348
docs/content/about/notifications.md
Normal file
|
@ -0,0 +1,348 @@
|
|||
---
|
||||
description: Explains how to work with registry notifications
|
||||
keywords: registry, on-prem, images, tags, repository, distribution, notifications, advanced
|
||||
title: Work with notifications
|
||||
---
|
||||
|
||||
The Registry supports sending webhook notifications in response to events
|
||||
happening within the registry. Notifications are sent in response to manifest
|
||||
pushes and pulls and layer pushes and pulls. These actions are serialized into
|
||||
events. The events are queued into a registry-internal broadcast system which
|
||||
queues and dispatches events to [_Endpoints_](notifications.md#endpoints).
|
||||
|
||||

|
||||
|
||||
## Endpoints
|
||||
|
||||
Notifications are sent to _endpoints_ via HTTP requests. Each configured
|
||||
endpoint has isolated queues, retry configuration and http targets within each
|
||||
instance of a registry. When an action happens within the registry, it is
|
||||
converted into an event which is dropped into an inmemory queue. When the
|
||||
event reaches the end of the queue, an http request is made to the endpoint
|
||||
until the request succeeds. The events are sent serially to each endpoint but
|
||||
order is not guaranteed.
|
||||
|
||||
## Configuration
|
||||
|
||||
To setup a registry instance to send notifications to endpoints, one must add
|
||||
them to the configuration. A simple example follows:
|
||||
|
||||
```yaml
|
||||
notifications:
|
||||
endpoints:
|
||||
- name: alistener
|
||||
url: https://mylistener.example.com/event
|
||||
headers:
|
||||
Authorization: [Bearer <your token, if needed>]
|
||||
timeout: 500ms
|
||||
threshold: 5
|
||||
backoff: 1s
|
||||
```
|
||||
|
||||
The above would configure the registry with an endpoint to send events to
|
||||
`https://mylistener.example.com/event`, with the header "Authorization: Bearer
|
||||
<your token, if needed>". The request would timeout after 500 milliseconds. If
|
||||
5 failures happen consecutively, the registry backs off for 1 second before
|
||||
trying again.
|
||||
|
||||
For details on the fields, see the [configuration documentation](configuration.md#notifications).
|
||||
|
||||
A properly configured endpoint should lead to a log message from the registry
|
||||
upon startup:
|
||||
|
||||
```
|
||||
INFO[0000] configuring endpoint alistener (https://mylistener.example.com/event), timeout=500ms, headers=map[Authorization:[Bearer <your token if needed>]] app.id=812bfeb2-62d6-43cf-b0c6-152f541618a3 environment=development service=registry
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
Events have a well-defined JSON structure and are sent as the body of
|
||||
notification requests. One or more events are sent in a structure called an
|
||||
envelope. Each event has a unique ID that can be used to uniquely identify incoming
|
||||
requests, if required. Along with that, an _action_ is provided with a
|
||||
_target_, identifying the object mutated during the event.
|
||||
|
||||
The fields available in an `event` are described below.
|
||||
|
||||
Field | Type | Description
|
||||
----- | ----- | -------------
|
||||
id | string |ID provides a unique identifier for the event.
|
||||
timestamp | Time | Timestamp is the time at which the event occurred.
|
||||
action | string | Action indicates what action encompasses the provided event.
|
||||
target | distribution.Descriptor | Target uniquely describes the target of the event.
|
||||
length | int | Length in bytes of content. Same as Size field in Descriptor.
|
||||
repository | string | Repository identifies the named repository.
|
||||
fromRepository | string | FromRepository identifies the named repository which a blob was mounted from if appropriate.
|
||||
url | string | URL provides a direct link to the content.
|
||||
tag | string | Tag identifies a tag name in tag events.
|
||||
request | [RequestRecord](https://pkg.go.dev/github.com/distribution/distribution/notifications#RequestRecord) | Request covers the request that generated the event.
|
||||
actor | [ActorRecord](https://pkg.go.dev/github.com/distribution/distribution/notifications#ActorRecord). | Actor specifies the agent that initiated the event. For most situations, this could be from the authorization context of the request.
|
||||
source | [SourceRecord](https://pkg.go.dev/github.com/distribution/distribution/notifications#SourceRecord) | Source identifies the registry node that generated the event. Put differently, while the actor "initiates" the event, the source "generates" it.
|
||||
|
||||
|
||||
|
||||
The following is an example of a JSON event, sent in response to the pull of a
|
||||
manifest:
|
||||
|
||||
```json
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"id": "320678d8-ca14-430f-8bb6-4ca139cd83f7",
|
||||
"timestamp": "2016-03-09T14:44:26.402973972-08:00",
|
||||
"action": "pull",
|
||||
"target": {
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||
"size": 708,
|
||||
"length": 708,
|
||||
"repository": "hello-world",
|
||||
"url": "http://192.168.100.227:5000/v2/hello-world/manifests/sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||
"tag": "latest"
|
||||
},
|
||||
"request": {
|
||||
"id": "6df24a34-0959-4923-81ca-14f09767db19",
|
||||
"addr": "192.168.64.11:42961",
|
||||
"host": "192.168.100.227:5000",
|
||||
"method": "GET",
|
||||
"useragent": "curl/7.38.0"
|
||||
},
|
||||
"actor": {},
|
||||
"source": {
|
||||
"addr": "xtal.local:5000",
|
||||
"instanceID": "a53db899-3b4b-4a62-a067-8dd013beaca4"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
The target struct of events which are sent when manifests and blobs are deleted
|
||||
contains a subset of the data contained in Get and Put events. Specifically,
|
||||
only the digest and repository are sent.
|
||||
|
||||
```json
|
||||
{
|
||||
"target": {
|
||||
"digest": "sha256:d89e1bee20d9cb344674e213b581f14fbd8e70274ecf9d10c514bab78a307845",
|
||||
"repository": "library/test"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**: As of version 2.1, the `length` field for event targets
|
||||
> is being deprecated for the `size` field, bringing the target in line with
|
||||
> common nomenclature. Both will continue to be set for the foreseeable
|
||||
> future. Newer code should favor `size` but accept either.
|
||||
|
||||
## Envelope
|
||||
|
||||
The envelope contains one or more events, with the following json structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"events": [ "..." ]
|
||||
}
|
||||
```
|
||||
|
||||
While events may be sent in the same envelope, the set of events within that
|
||||
envelope have no implied relationship. For example, the registry may choose to
|
||||
group unrelated events and send them in the same envelope to reduce the total
|
||||
number of requests.
|
||||
|
||||
The full package has the mediatype
|
||||
"application/vnd.docker.distribution.events.v2+json", which is set on the
|
||||
request coming to an endpoint.
|
||||
|
||||
An example of a full event may look as follows:
|
||||
|
||||
```http request
|
||||
POST /callback HTTP/1.1
|
||||
Host: application/vnd.docker.distribution.events.v2+json
|
||||
Authorization: Bearer <your token, if needed>
|
||||
Content-Type: application/vnd.docker.distribution.events.v2+json
|
||||
|
||||
{
|
||||
"events": [
|
||||
{
|
||||
"id": "asdf-asdf-asdf-asdf-0",
|
||||
"timestamp": "2006-01-02T15:04:05Z",
|
||||
"action": "push",
|
||||
"target": {
|
||||
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
|
||||
"digest": "sha256:fea8895f450959fa676bcc1df0611ea93823a735a01205fd8622846041d0c7cf",
|
||||
"length": 1,
|
||||
"repository": "library/test",
|
||||
"url": "https://example.com/v2/library/test/manifests/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
|
||||
},
|
||||
"request": {
|
||||
"id": "asdfasdf",
|
||||
"addr": "client.local",
|
||||
"host": "registrycluster.local",
|
||||
"method": "PUT",
|
||||
"useragent": "test/0.1"
|
||||
},
|
||||
"actor": {
|
||||
"name": "test-actor"
|
||||
},
|
||||
"source": {
|
||||
"addr": "hostname.local:port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "asdf-asdf-asdf-asdf-1",
|
||||
"timestamp": "2006-01-02T15:04:05Z",
|
||||
"action": "push",
|
||||
"target": {
|
||||
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
||||
"digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
||||
"length": 2,
|
||||
"repository": "library/test",
|
||||
"url": "https://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
|
||||
},
|
||||
"request": {
|
||||
"id": "asdfasdf",
|
||||
"addr": "client.local",
|
||||
"host": "registrycluster.local",
|
||||
"method": "PUT",
|
||||
"useragent": "test/0.1"
|
||||
},
|
||||
"actor": {
|
||||
"name": "test-actor"
|
||||
},
|
||||
"source": {
|
||||
"addr": "hostname.local:port"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "asdf-asdf-asdf-asdf-2",
|
||||
"timestamp": "2006-01-02T15:04:05Z",
|
||||
"action": "push",
|
||||
"target": {
|
||||
"mediaType": "application/vnd.docker.container.image.rootfs.diff+x-gtar",
|
||||
"digest": "sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5",
|
||||
"length": 3,
|
||||
"repository": "library/test",
|
||||
"url": "https://example.com/v2/library/test/blobs/sha256:c3b3692957d439ac1928219a83fac91e7bf96c153725526874673ae1f2023f8d5"
|
||||
},
|
||||
"request": {
|
||||
"id": "asdfasdf",
|
||||
"addr": "client.local",
|
||||
"host": "registrycluster.local",
|
||||
"method": "PUT",
|
||||
"useragent": "test/0.1"
|
||||
},
|
||||
"actor": {
|
||||
"name": "test-actor"
|
||||
},
|
||||
"source": {
|
||||
"addr": "hostname.local:port"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Responses
|
||||
|
||||
The registry is fairly accepting of the response codes from endpoints. If an
|
||||
endpoint responds with any 2xx or 3xx response code (after following
|
||||
redirects), the message is considered to have been delivered, and is discarded.
|
||||
|
||||
In turn, it is recommended that endpoints are accepting of incoming responses,
|
||||
as well. While the format of event envelopes are standardized by media type,
|
||||
any "pickyness" about validation may cause the queue to backup on the
|
||||
registry.
|
||||
|
||||
## Monitoring
|
||||
|
||||
The state of the endpoints are reported via the debug/vars http interface,
|
||||
usually configured to `http://localhost:5001/debug/vars`. Information such as
|
||||
configuration and metrics are available by endpoint.
|
||||
|
||||
The following provides an example of a few endpoints that have experienced
|
||||
several failures and have since recovered:
|
||||
|
||||
```json
|
||||
{
|
||||
"notifications": {
|
||||
"endpoints": [
|
||||
{
|
||||
"name": "local-5003",
|
||||
"url": "http://localhost:5003/callback",
|
||||
"Headers": {
|
||||
"Authorization": [
|
||||
"Bearer \u003can example token\u003e"
|
||||
]
|
||||
},
|
||||
"Timeout": 1000000000,
|
||||
"Threshold": 10,
|
||||
"Backoff": 1000000000,
|
||||
"Metrics": {
|
||||
"Pending": 76,
|
||||
"Events": 76,
|
||||
"Successes": 0,
|
||||
"Failures": 0,
|
||||
"Errors": 46,
|
||||
"Statuses": {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "local-8083",
|
||||
"url": "http://localhost:8083/callback",
|
||||
"Headers": null,
|
||||
"Timeout": 1000000000,
|
||||
"Threshold": 10,
|
||||
"Backoff": 1000000000,
|
||||
"Metrics": {
|
||||
"Pending": 0,
|
||||
"Events": 76,
|
||||
"Successes": 76,
|
||||
"Failures": 0,
|
||||
"Errors": 28,
|
||||
"Statuses": {
|
||||
"202 Accepted": 76
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If using notification as part of a larger application, it is _critical_ to
|
||||
monitor the size ("Pending" above) of the endpoint queues. If failures or
|
||||
queue sizes are increasing, it can indicate a larger problem.
|
||||
|
||||
The logs are also a valuable resource for monitoring problems. A failing
|
||||
endpoint leads to messages similar to the following:
|
||||
|
||||
```none
|
||||
ERRO[0340] retryingsink: error writing events: httpSink{http://localhost:5003/callback}: error posting: Post http://localhost:5003/callback: dial tcp 127.0.0.1:5003: connection refused, retrying
|
||||
WARN[0340] httpSink{http://localhost:5003/callback} encountered too many errors, backing off
|
||||
```
|
||||
|
||||
The above indicates that several errors caused a backoff and the registry
|
||||
waits before retrying.
|
||||
|
||||
## Considerations
|
||||
|
||||
Currently, the queues are inmemory, so endpoints should be _reasonably
|
||||
reliable_. They are designed to make a best-effort to send the messages but if
|
||||
an instance is lost, messages may be dropped. If an endpoint goes down, care
|
||||
should be taken to ensure that the registry instance is not terminated before
|
||||
the endpoint comes back up or messages are lost.
|
||||
|
||||
This can be mitigated by running endpoints in close proximity to the registry
|
||||
instances. One could run an endpoint that pages to disk and then forwards a
|
||||
request to provide better durability.
|
||||
|
||||
The notification system is designed around a series of interchangeable _sinks_
|
||||
which can be wired up to achieve interesting behavior. If this system doesn't
|
||||
provide acceptable guarantees, adding a transactional `Sink` to the registry
|
||||
is a possibility, although it may have an effect on request service time.
|
||||
See the
|
||||
[godoc](https://pkg.go.dev/github.com/distribution/distribution/notifications#Sink)
|
||||
for more information.
|
Loading…
Add table
Add a link
Reference in a new issue