Docker Registry Redux
Wherein I finally bring together all we’ve learned so far and stand this thing up properly.
Create a namespace
I’d like to run it in a separate namespace:
$ kubectl create namespace docker-registry
Create certificates
Rather than use an insecure registry, we should create a TLS certificate. Because OpenSSL sucks, I’m using an Elixir script that uses the ‘x509’ library.
Read that post for instructions for creating the root CA and installing it. Or use OpenSSL. You do you.
Restart services
Once you’ve installed the root certificate, you need to restart the docker service:
sudo systemctl restart docker
On the control node, restart k3s
:
sudo systemctl restart k3s
On the worker nodes, restart k3s-agent
:
sudo systemctl restart k3s-agent
Create server keypair and certificate
./certs create-cert \
--issuer-cert k3s-ca.crt --issuer-key k3s-ca.key \
--out-cert docker-registry.crt --out-key docker-registry.key \
--template server \
--subject '/CN=docker.k3s.differentpla.net'
Create k8s secret
kubectl --namespace docker-registry \
create secret tls docker-registry-tls \
--cert=docker-registry.crt \
--key=docker-registry.key
Inspecting k8s secrets
Kubernetes secrets aren’t really secret. You can inspect them. For example:
kubectl --namespace docker-registry \
get secret docker-registry-tls \
--template="{{index .data \"tls.key\" | base64decode}}"
kubectl --namespace docker-registry \
get secret docker-registry-tls \
--template="{{index .data \"tls.crt\" | base64decode}}"
Create the deployment
Create a file, deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: docker-registry
namespace: docker-registry
labels:
app: docker-registry
spec:
replicas: 1
selector:
matchLabels:
app: docker-registry
template:
metadata:
labels:
app: docker-registry
name: docker-registry
spec:
containers:
- name: registry
image: registry:2
env:
- name: REGISTRY_HTTP_TLS_KEY
value: /secrets/tls.key
- name: REGISTRY_HTTP_TLS_CERTIFICATE
value: /secrets/tls.crt
ports:
- containerPort: 5000
volumeMounts:
- name: docker-registry-vol
mountPath: /var/lib/registry
- name: docker-registry-tls
mountPath: /secrets
volumes:
- name: docker-registry-vol
persistentVolumeClaim:
claimName: docker-registry-pvc
- name: docker-registry-tls
secret:
secretName: docker-registry-tls
Create a persistent volume claim
We need some storage, so create a file pvc.yaml
. It uses Longhorn, which we installed previously.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: docker-registry-pvc
namespace: docker-registry
spec:
storageClassName: longhorn
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
Create a load balancer
We could probably get away with using an Ingress for this, but we’ve installed MetalLB, so we’ll use that.
apiVersion: v1
kind: Service
metadata:
labels:
app: docker-registry
name: docker-registry
namespace: docker-registry
spec:
type: LoadBalancer
ports:
- port: 443
protocol: TCP
targetPort: 5000
selector:
app: docker-registry
Apply
$ kubectl apply -f deployment.yaml -f pvc.yaml -f lb.yaml
$ kubectl --namespace docker-registry get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
docker-registry LoadBalancer 10.43.125.31 192.168.28.12 443:31355/TCP 3h13m
Configuring DNS
My router uses dnsmasq by default, and we can add extra entries by using the --addn-hosts=<file>
option.
ssh root@192.168.28.1 # same password as 'admin'
Create a file, /etc/dhcpd/addn-hosts
, containing the following:
192.168.28.11 nginx.k3s.differentpla.net
192.168.28.12 docker.k3s.differentpla.net
The nginx
entry is our test from installing MetalLB; the docker
entry is from above.
Create a file, /etc/dhcpd/dhcpd-addn-hosts.conf
, containing the following:
addn-hosts=/etc/dhcpd/addn-hosts
Create a file, /etc/dhcpd/dhcpd-addn-hosts.info
, containing the following:
enable="yes"
Restart the DHCP server:
/etc/rc.network nat-restart-dhcp
Caveats
- It only balances TCP traffic, not ICMP. So
ping
doesn’t work. - dnsmasq doesn’t reload the additional hosts automatically. See this Server Fault question, for example. This is by design.
- I’m not entirely happy about manually editing files (and restarting services) on the router. It seems fragile. It also means that I ought to notify everyone else in the house about potential downtime.
- At some point, I’ll add another DNS server (or co-opt CoreDNS?), and set up dnsmasq on the router to forward the subdomain to it.
Does it work?
$ curl https://docker.k3s.differentpla.net/v2/_catalog
{"repositories":[]}
$ docker pull hello-world
$ docker tag hello-world docker.k3s.differentpla.net/hello-world
$ docker push docker.k3s.differentpla.net/hello-world
$ docker pull docker.k3s.differentpla.net/hello-world
$ kubectl create deployment hello-world --image=docker.k3s.differentpla.net/hello-world