Docker 008 自建Registry
Docker 008 自建Registry
registry 是什么?可以做什么?
Registry 是一个无状态高扩展的服务程序,他可以存储并分发镜像。
很多时候使用 docker hub 并不方便,尤其是公司内网无法访问公网或者有不想公开的信息的时候,这个时候我们可以自建 registry。
使用 Registry,我们可以:
- 严格控制镜像的存储位置
- 镜像发布管道的全部所有权
- 将镜像的存储和发布紧密的继承到你的内部开发工作流中
如果要执行以下操作,则可使用registry:
- 严格控制图像的存储位置
- 完全拥有您的图像分发管道
- 将图像存储和分发紧密集成到您的内部开发工作流程中
registry 的分类
Sponsor Registry | 第三方的registry,客户和Docker社区使用 |
---|---|
Mirror Registry | 第三方的registry,只让客户使用 |
Vendor Registry | 由发布Docker镜像的供应商提供的registry |
Private Registry | 通过设有防火墙和额外的安全层的私有实体提供的registry |
国内可用的 registry
一般使用 docker hub 会比较慢,幸好的是,国内的一些厂商也提供了registry 服务:
国内的镜像源有
- docker官方中国区
https://registry.docker-cn.com
- 网易
http://hub-mirror.c.163.com
- ustc
http://docker.mirrors.ustc.edu.cn
- 阿里云
http://<你的ID>.mirror.aliyuncs.com
如果想使用国内的源,可做如下配置():
# 指定源后,需要重启 docker
$ vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://registry.docker-cn.com"] # 这里是一个列表,可添加多个
}
关于配置文件
参考https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
Linux上可配置选项的完整示例
{
"authorization-plugins": [],
"data-root": "",
"dns": [],
"dns-opts": [],
"dns-search": [],
"exec-opts": [],
"exec-root": "",
"experimental": false,
"features": {},
"storage-driver": "",
"storage-opts": [],
"labels": [],
"live-restore": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file":"5",
"labels": "somelabel",
"env": "os,customer"
},
"mtu": 0,
"pidfile": "",
"cluster-store": "",
"cluster-store-opts": {},
"cluster-advertise": "",
"max-concurrent-downloads": 3,
"max-concurrent-uploads": 5,
"default-shm-size": "64M",
"shutdown-timeout": 15,
"debug": true,
"hosts": [],
"log-level": "",
"tls": true,
"tlsverify": true,
"tlscacert": "",
"tlscert": "",
"tlskey": "",
"swarm-default-advertise-addr": "",
"api-cors-header": "",
"selinux-enabled": false,
"userns-remap": "",
"group": "",
"cgroup-parent": "",
"default-ulimits": {
"nofile": {
"Name": "nofile",
"Hard": 64000,
"Soft": 64000
}
},
"init": false,
"init-path": "/usr/libexec/docker-init",
"ipv6": false,
"iptables": false,
"ip-forward": false,
"ip-masq": false,
"userland-proxy": false,
"userland-proxy-path": "/usr/libexec/docker-proxy",
"ip": "0.0.0.0",
"bridge": "",
"bip": "",
"fixed-cidr": "",
"fixed-cidr-v6": "",
"default-gateway": "",
"default-gateway-v6": "",
"icc": false,
"raw-logs": false,
"allow-nondistributable-artifacts": [],
"registry-mirrors": [],
"seccomp-profile": "",
"insecure-registries": [],
"no-new-privileges": false,
"default-runtime": "runc",
"oom-score-adjust": -500,
"node-generic-resources": ["NVIDIA-GPU=UUID1", "NVIDIA-GPU=UUID2"],
"runtimes": {
"cc-runtime": {
"path": "/usr/bin/cc-runtime"
},
"custom": {
"path": "/usr/local/bin/my-runc-replacement",
"runtimeArgs": [
"--debug"
]
}
},
"default-address-pools":[
{"base":"172.80.0.0/16","size":24},
{"base":"172.90.0.0/16","size":24}
]
}
Win 下的可配置选项的完整示例:
{
"authorization-plugins": [],
"data-root": "",
"dns": [],
"dns-opts": [],
"dns-search": [],
"exec-opts": [],
"experimental": false,
"features":{},
"storage-driver": "",
"storage-opts": [],
"labels": [],
"log-driver": "",
"mtu": 0,
"pidfile": "",
"cluster-store": "",
"cluster-advertise": "",
"max-concurrent-downloads": 3,
"max-concurrent-uploads": 5,
"shutdown-timeout": 15,
"debug": true,
"hosts": [],
"log-level": "",
"tlsverify": true,
"tlscacert": "",
"tlscert": "",
"tlskey": "",
"swarm-default-advertise-addr": "",
"group": "",
"default-ulimits": {},
"bridge": "",
"fixed-cidr": "",
"raw-logs": false,
"allow-nondistributable-artifacts": [],
"registry-mirrors": [],
"insecure-registries": []
}
使用容器部署 registry
从容器运行 registry
从容器安装一个 registry 非常简单:
# 启动一个 regidtry 容器
$ docker run -d -p 5000:5000 --name registry registry:2
# --restart参数可以让容器自动重启
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2
# 自定义端口
$ docker run -d -p 5001:5000 --name registry-test registry:2
# 自定义存储后端
$ docker run -d -p 5000:5000 --restart=always --name registry -v /mnt/registry:/var/lib/registry registry:2
# 从 hub 上拉取一个镜像
$ docker pull ubuntu
# 修改镜像 tag,将 tag 指向自建 registry
docker image tag ubuntu localhost:5000/myfirstimage
# 把镜像推送到自建 registry
docker push localhost:5000/myfirstimage
# 从自建 registry 上拉取镜像
docker pull localhost:5000/myfirstimage
# 停止registry容器并删除所有数据
docker container stop registry && docker container rm -v registry
运行一个可外部访问的 registry
上面部署的 registry 只有本地可访问,用途有限;为了使registry可供外部访问,我们首先应使用 TLS 保护它。
获取证书
这里的示例假定已满足如下条件:
- 你的 registry 的 URL 是 https://myregistry.domain.com/
- registry 所在主机的443端口允许被访问
- 你已经从证书颁发机构获取到了证书
-
创建certs 目录,并将从 CA 获取到的 .crt和 .key文件复制到 certs 目录;下面的步骤中假定文件名是
domain.crt
和domain.key
:$ mkdir -p certs
-
关闭当前运行的 registry:
$ docker container stop registry
-
指定 TLS 证书后,重启 registry;下面的命令将 certs 目录绑定到容器的 /certs 目录上,并设置了环境变量,环境变量告诉容器在哪里可以找到
domain.crt
和domain.key
:$ 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
-
Docker客户端现在可以用外部地址拉取或推送镜像到你的 registry:
$ 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
使用中间证书
证书颁发机构可能会向你提供中间证书,这时,就必须将证书与中间证书连接起来,可使用如下命令完成连接:
$ cat domain.crt intermediate-certificates.pem > certs/domain.crt
可以使用自签名证书,或者使用不安全的 registry (无 TLS),除非已为自签名证书设置了验证,否则不推荐用于非测试环境。
使用 yum 部署 registry
下面的例子中以centos 为例,centos 版本为7.4.1708:
# 安装
$ yum install docker-registry
# 实际安装的软件包是: docker-distribution
# 我这里安装的是 docker-distribution-2.6.2-2.git48294d9.el7.x86_64
# 查看有哪些文件
$ rpm -ql docker-distribution-2.6.2-2.git48294d9.el7.x86_64
/etc/docker-distribution/registry/config.yml
/usr/bin/registry
/usr/lib/systemd/system/docker-distribution.service
/usr/share/doc/docker-distribution-2.6.2
/usr/share/doc/docker-distribution-2.6.2/AUTHORS
/usr/share/doc/docker-distribution-2.6.2/CONTRIBUTING.md
/usr/share/doc/docker-distribution-2.6.2/LICENSE
/usr/share/doc/docker-distribution-2.6.2/MAINTAINERS
/usr/share/doc/docker-distribution-2.6.2/README.md
/var/lib/registry
可以看到,配置文件为/etc/docker-distribution/registry/config.yml,config.yml的格式为 yaml,
# 默认配置
$ cat /etc/docker-distribution/registry/config.yml
version: 0.1
log:
fields:
service: registry
storage:
cache:
layerinfo: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
-
version: 必填项,用于指定配置的版本
-
log :用户配置日志记录系统的行为,日志记录系统会将所有的内容输出到标准输出,通过 log 可以调整输出的格式和颗粒度:
log: accesslog: disabled: true level: debug formatter: text fields: service: registry environment: staging
Parameter Required 描述 level
no 设置日志输出级别. 可用的值有 error
、warn
、info
、debug
,默认是info
.formatter
no 设置日志输出的格式,格式主要影响日志行中键控属性的编码方式,值可以是 text
、json
、logstash
。默认是text
.fields
no 字段名和值的映射,会被添加到每个日志行的上下文中,对其他系统中混合日志消息源的识别很有用 -
accesslog:
accesslog: disabled: true
在 log 中,accesslog 记录访问日志记录系统的行为,默认访问日志记录系统会用组合日志格式输出到标准输出,通过将 disable 设置为 true 来禁用访问日志记录
更多内容: https://docs.docker.com/registry/configuration/
配置文件的可配置项
参考 https://docs.docker.com/registry/configuration/
version: 0.1
log:
accesslog:
disabled: true
level: debug
formatter: text
fields:
service: registry
environment: staging
hooks:
- type: mail
disabled: true
levels:
- panic
options:
smtp:
addr: mail.example.com:25
username: mailuser
password: password
insecure: true
from: sender@example.com
to:
- errors@example.com
loglevel: debug # deprecated: use "log"
storage:
filesystem:
rootdirectory: /var/lib/registry
maxthreads: 100
azure:
accountname: accountname
accountkey: base64encodedaccountkey
container: containername
gcs:
bucket: bucketname
keyfile: /path/to/keyfile
credentials:
type: service_account
project_id: project_id_string
private_key_id: private_key_id_string
private_key: private_key_string
client_email: client@example.com
client_id: client_id_string
auth_uri: http://example.com/auth_uri
token_uri: http://example.com/token_uri
auth_provider_x509_cert_url: http://example.com/provider_cert_url
client_x509_cert_url: http://example.com/client_cert_url
rootdirectory: /gcs/object/name/prefix
chunksize: 5242880
s3:
accesskey: awsaccesskey
secretkey: awssecretkey
region: us-west-1
regionendpoint: http://myobjects.local
bucket: bucketname
encrypt: true
keyid: mykeyid
secure: true
v4auth: true
chunksize: 5242880
multipartcopychunksize: 33554432
multipartcopymaxconcurrency: 100
multipartcopythresholdsize: 33554432
rootdirectory: /s3/object/name/prefix
swift:
username: username
password: password
authurl: https://storage.myprovider.com/auth/v1.0 or https://storage.myprovider.com/v2.0 or https://storage.myprovider.com/v3/auth
tenant: tenantname
tenantid: tenantid
domain: domain name for Openstack Identity v3 API
domainid: domain id for Openstack Identity v3 API
insecureskipverify: true
region: fr
container: containername
rootdirectory: /swift/object/name/prefix
oss:
accesskeyid: accesskeyid
accesskeysecret: accesskeysecret
region: OSS region name
endpoint: optional endpoints
internal: optional internal endpoint
bucket: OSS bucket
encrypt: optional data encryption setting
secure: optional ssl setting
chunksize: optional size valye
rootdirectory: optional root directory
inmemory: # This driver takes no parameters
delete:
enabled: false
redirect:
disable: false
cache:
blobdescriptor: redis
maintenance:
uploadpurging:
enabled: true
age: 168h
interval: 24h
dryrun: false
readonly:
enabled: false
auth:
silly:
realm: silly-realm
service: silly-service
token:
autoredirect: true
realm: token-realm
service: token-service
issuer: registry-token-issuer
rootcertbundle: /root/certs/bundle
htpasswd:
realm: basic-realm
path: /path/to/htpasswd
middleware:
registry:
- name: ARegistryMiddleware
options:
foo: bar
repository:
- name: ARepositoryMiddleware
options:
foo: bar
storage:
- name: cloudfront
options:
baseurl: https://my.cloudfronted.domain.com/
privatekey: /path/to/pem
keypairid: cloudfrontkeypairid
duration: 3000s
ipfilteredby: awsregion
awsregion: us-east-1, use-east-2
updatefrenquency: 12h
iprangesurl: https://ip-ranges.amazonaws.com/ip-ranges.json
storage:
- name: redirect
options:
baseurl: https://example.com/
reporting:
bugsnag:
apikey: bugsnagapikey
releasestage: bugsnagreleasestage
endpoint: bugsnagendpoint
newrelic:
licensekey: newreliclicensekey
name: newrelicname
verbose: true
http:
addr: localhost:5000
prefix: /my/nested/registry/
host: https://myregistryaddress.org:5000
secret: asecretforlocaldevelopment
relativeurls: false
draintimeout: 60s
tls:
certificate: /path/to/x509/public
key: /path/to/x509/private
clientcas:
- /path/to/ca.pem
- /path/to/another/ca.pem
letsencrypt:
cachefile: /path/to/cache-file
email: emailused@letsencrypt.com
hosts: [myregistryaddress.org]
debug:
addr: localhost:5001
prometheus:
enabled: true
path: /metrics
headers:
X-Content-Type-Options: [nosniff]
http2:
disabled: false
notifications:
events:
includereferences: true
endpoints:
- name: alistener
disabled: false
url: https://my.listener.com/event
headers: <http.Header>
timeout: 1s
threshold: 10
backoff: 1s
ignoredmediatypes:
- application/octet-stream
ignore:
mediatypes:
- application/octet-stream
actions:
- pull
redis:
addr: localhost:6379
password: asecret
db: 0
dialtimeout: 10ms
readtimeout: 10ms
writetimeout: 10ms
pool:
maxidle: 16
maxactive: 64
idletimeout: 300s
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
file:
- file: /path/to/checked/file
interval: 10s
http:
- uri: http://server.to.check/must/return/200
headers:
Authorization: [Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==]
statuscode: 200
timeout: 3s
interval: 10s
threshold: 3
tcp:
- addr: redis-server.domain.com:6379
timeout: 3s
interval: 10s
threshold: 3
proxy:
remoteurl: https://registry-1.docker.io
username: [username]
password: [password]
compatibility:
schema1:
signingkeyfile: /etc/registry/key.json
enabled: true
validation:
manifests:
urls:
allow:
- ^https?://([^/]+\.)*example\.com/
deny:
- ^https?://www\.example\.com/