Abyśmy mogli w polu "image" ustawić nazwę FQDN naszej usługi Registry musimy zainstalować dnsmasq, które będzie korzystać z serwera DNS-owego Kubernetesa. Najpierw musimy sprawdzić jaki jest adres IP serwisu odwołującego się do serwera nazw domenowych Kubernetes (na jednym z węzłów zarządzających):
bkorpala@kubernetes-master1:~$ kubectl -n kube-system get services | grep kube-dns | awk '{ print $3 }'Następnie instalujemy na wszystkich węzłach usługę dnsmasq komendą "sudo apt install dnsmasq".
10.233.0.3
Kolejnym krokiem jest edycja pliku "/etc/dnsmasq.d/kube.conf" i wprowadzenie do niego poniższej zawartości (wraz z odpowiednim adresem IP usługi kube-dns):
server=/cluster.local/10.233.0.3Teraz na wszystkich serwerach restartujemy sieć "sudo systemctl restart networking" oraz restartujemy dnsmasq poprzez "sudo systemctl restart dnsmasq".
Sprawdzamy czy np. adres "kube-dns.kube-system.svc.cluster.local" jest rozwiązywane na IP przy użyciu pinga. Musimy się także upewnić, że w "/etc/resolv.conf" zawsze na pierwszym miejscu będzie "nameserver 127.0.0.1" bo w przeciwnym wypadku nazwy mogą nie być rozwiązywane. Dobrze zrestartować serwer i sprawdzić co się pojawi w "/etc/resolv.conf". Dla pewności można też dodać wpis "nameserver 127.0.0.1" do "/etc/resolvconf/resolv.conf.d/head" i wykonać komendę "sudo resolvconf -u".
Następny punkt dotyczy wygenerowania ceryfikatu dla HTTPS w Registry:
bkorpala@MacBook-Pro-Bartlomiej:~|⇒ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout registry.key -out registry.crtTworzymy przestrzeń nazw komendą "kubectl create namespace registry".
Generating a 2048 bit RSA private key
........................+++
............................+++
writing new private key to 'registry.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) []:PL
State or Province Name (full name) []:Malopolskie
Locality Name (eg, city) []:Krakow
Organization Name (eg, company) []:Nazwa_firmy
Organizational Unit Name (eg, section) []:
Common Name (eg, fully qualified host name) []:kube-registry.registry.svc.cluster.local
Email Address []:adres_poczty
Certyfikat i klucz prywatny będziemy trzymać w sekretach Kubernetesa, ale najpierw musimy je przekonwertować do Base64 jak np.:
bkorpala@kubernetes-master1:~$ cat registry.crt | base64 -w0Gdy już mamy zakodowaną wartość to tworzymy pierwszą część manifestu:
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR0RENDQXB3Q0NRQzlPSXoyN21EaDVEQU5CZ2txaGtpRzl3MEJBUXNGQURDQm16RUxNQWtHQTFVRUJoTUMKVUV3eEZEQVNCZ05WQkFnTUMwMWhiRzl3YjJ4emEybGxNUTh3RFFZRFZRUUhEQVpMY21GcmIzY3hFREFPQmdOVgpCQW9NQjAxcGNYVnBaRzh4TVRBdkJnTlZCQU1NS0d0MVltVXRjbVZuYVhOMGNua3VjbVZuYVhOMGNua3VjM1pqCkxtTnNkWE4wWlhJdWJHOWpZV3d4SURBZUJna3Foa2lHOXcwQkNRRVdFV2hsYkd4dlFHMXBjWFZwWkc4dVkyOXQKTUI0WERURTRNRGN3TWpFeU1ETXdOVm9YRFRFNU1EY3dNakV5TURNd05Wb3dnWnN4Q3pBSkJnTlZCQVlUQWxCTQpNUlF3RWdZRFZRUUlEQXROWVd4dmNHOXNjMnRwWlRFUE1BMEdBMVVFQnd3R1MzSmhhMjkzTVJBd0RnWURWUVFLCkRBZE5hWEYxYVdSdk1URXdMd1lEVlFRRERDaHJkV0psTFhKbFoybHpkSEo1TG5KbFoybHpkSEo1TG5OMll5NWoKYkhWemRHVnlMbXh2WTJGc01TQXdIZ1lKS29aSWh2Y05BUWtCRmhGb1pXeHNiMEJ0YVhGMWFXUnZMbU52YlRDQwpBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUs2RW1ZT2lKejZydmNPUjByV3RTVHkyClVhSTdPU2tPOU5kT1BIUUdQRjhsbjNpVUJ6MCtDeEJaRzEra2lxMmtSRVlJc1JDbkJ3RGMyNkdNYWowM0NicUcKbWlTOUpTSklHMCtBMEoxdWsrUDdqNnUzdURaMEhlRWdHeHpqK2ZmazNtUWpOcysrMms4TDVTRTFVM2lhRUhiRAphUVR4cVcyMFM5VXFXWjFxT21lZExaN0hhQ0VBa1BQU0xOeitwUnlIbElianVPM0lPam5Za1c1V2ZCRTBpU0ZTCnhxRFdpL2lYTGtHOFRwZDExYWZ0amFPRHJjRklISzF0NTJyV0w3cUtvOVFLdVpXSFM1RjNReFdYbHVKM09ndU0KSEVQZzVtaDk2MlRGeVVzN2gvOWVicXV0akxmMk9FY2Q1YlNUZXNqbk1sZWZNN2lrN2tUYXBwTC84TGVSNGo4QwpBd0VBQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWRtbkc5eTlSYXlFaFhqdEtYcjB5cW1TUitmTFFHMkZXCnB6TDRwTDJ1ZzNIQWVDS0Y0ZEgyak9qRUhVYjN6TU9sVW5zRGRvMDlOa1I2eGRrWXFXYXJWc3I4RGlHZ3h1LzcKb2YrZnIybUdYNFJtNmlqekgwcXRIMWlsdzA0OGwrNER2L1VVcjJwZWthTUMrMHhmU1U2c3B0NXNYWk1LS0JPRQpMUjFKbE1BeWI0OEd1Y2E0Tko2bUw4L2dxcVUvb25WQTVPbDBoOGtyZHNONzUrUStQTThibitTYVQ2bjdaTlVKCmZ6dllyTmU1Ynp2aXdyc0ZYcnBOR2VBWVpnV01BZS9CU1NReUpjZHpDSjg4YW8yazlocHNYbi9XUGtOcUVJUFAKOGZOMkZNbHlaTDFQMmFDQ2ZjSUREZGZGYzlWWW0vbGQ0REZlN2IyT1IvWVlpWUtiNXhsMmdnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
apiVersion: v1Wgrywamy za pomocą komendy "kubectl apply -n registry -f registry_deployment.yml" i sprawdzamy jak nam poszło:
kind: Secret
metadata:
name: registry-certs
type: Opaque
data:
registry.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUR0RENDQXB3Q0NRQzlPSXoyN21EaDVEQU5CZ2txaGtpRzl3MEJBUXNGQURDQm16RUxNQWtHQTFVRUJoTUMKVUV3eEZEQVNCZ05WQkFnTUMwMWhiRzl3YjJ4emEybGxNUTh3RFFZRFZRUUhEQVpMY21GcmIzY3hFREFPQmdOVgpCQW9NQjAxcGNYVnBaRzh4TVRBdkJnTlZCQU1NS0d0MVltVXRjbVZuYVhOMGNua3VjbVZuYVhOMGNua3VjM1pqCkxtTnNkWE4wWlhJdWJHOWpZV3d4SURBZUJna3Foa2lHOXcwQkNRRVdFV2hsYkd4dlFHMXBjWFZwWkc4dVkyOXQKTUI0WERURTRNRGN3TWpFeU1ETXdOVm9YRFRFNU1EY3dNakV5TURNd05Wb3dnWnN4Q3pBSkJnTlZCQVlUQWxCTQpNUlF3RWdZRFZRUUlEQXROWVd4dmNHOXNjMnRwWlRFUE1BMEdBMVVFQnd3R1MzSmhhMjkzTVJBd0RnWURWUVFLCkRBZE5hWEYxYVdSdk1URXdMd1lEVlFRRERDaHJkV0psTFhKbFoybHpkSEo1TG5KbFoybHpkSEo1TG5OMll5NWoKYkhWemRHVnlMbXh2WTJGc01TQXdIZ1lKS29aSWh2Y05BUWtCRmhGb1pXeHNiMEJ0YVhGMWFXUnZMbU52YlRDQwpBU0l3RFFZSktvWklodmNOQVFFQkJRQURnZ0VQQURDQ0FRb0NnZ0VCQUs2RW1ZT2lKejZydmNPUjByV3RTVHkyClVhSTdPU2tPOU5kT1BIUUdQRjhsbjNpVUJ6MCtDeEJaRzEra2lxMmtSRVlJc1JDbkJ3RGMyNkdNYWowM0NicUcKbWlTOUpTSklHMCtBMEoxdWsrUDdqNnUzdURaMEhlRWdHeHpqK2ZmazNtUWpOcysrMms4TDVTRTFVM2lhRUhiRAphUVR4cVcyMFM5VXFXWjFxT21lZExaN0hhQ0VBa1BQU0xOeitwUnlIbElianVPM0lPam5Za1c1V2ZCRTBpU0ZTCnhxRFdpL2lYTGtHOFRwZDExYWZ0amFPRHJjRklISzF0NTJyV0w3cUtvOVFLdVpXSFM1RjNReFdYbHVKM09ndU0KSEVQZzVtaDk2MlRGeVVzN2gvOWVicXV0akxmMk9FY2Q1YlNUZXNqbk1sZWZNN2lrN2tUYXBwTC84TGVSNGo4QwpBd0VBQVRBTkJna3Foa2lHOXcwQkFRc0ZBQU9DQVFFQWRtbkc5eTlSYXlFaFhqdEtYcjB5cW1TUitmTFFHMkZXCnB6TDRwTDJ1ZzNIQWVDS0Y0ZEgyak9qRUhVYjN6TU9sVW5zRGRvMDlOa1I2eGRrWXFXYXJWc3I4RGlHZ3h1LzcKb2YrZnIybUdYNFJtNmlqekgwcXRIMWlsdzA0OGwrNER2L1VVcjJwZWthTUMrMHhmU1U2c3B0NXNYWk1LS0JPRQpMUjFKbE1BeWI0OEd1Y2E0Tko2bUw4L2dxcVUvb25WQTVPbDBoOGtyZHNONzUrUStQTThibitTYVQ2bjdaTlVKCmZ6dllyTmU1Ynp2aXdyc0ZYcnBOR2VBWVpnV01BZS9CU1NReUpjZHpDSjg4YW8yazlocHNYbi9XUGtOcUVJUFAKOGZOMkZNbHlaTDFQMmFDQ2ZjSUREZGZGYzlWWW0vbGQ0REZlN2IyT1IvWVlpWUtiNXhsMmdnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
registry.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3VoSm1Eb2ljK3E3M0QKa2RLMXJVazh0bEdpT3prcER2VFhUangwQmp4ZkpaOTRsQWM5UGdzUVdSdGZwSXF0cEVSR0NMRVFwd2NBM051aApqR285TndtNmhwb2t2U1VpU0J0UGdOQ2RicFBqKzQrcnQ3ZzJkQjNoSUJzYzQvbjM1TjVrSXpiUHZ0cFBDK1VoCk5WTjRtaEIydzJrRThhbHR0RXZWS2xtZGFqcG5uUzJleDJnaEFKRHowaXpjL3FVY2g1U0c0N2p0eURvNTJKRnUKVm53Uk5Ja2hVc2FnMW92NGx5NUJ2RTZYZGRXbjdZMmpnNjNCU0J5dGJlZHExaSs2aXFQVUNybVZoMHVSZDBNVgpsNWJpZHpvTGpCeEQ0T1pvZmV0a3hjbExPNGYvWG02cnJZeTM5amhISGVXMGszckk1ekpYbnpPNHBPNUUycWFTCi8vQzNrZUkvQWdNQkFBRUNnZ0VBSENPSE9TRkJJS3JDV3pFOC8wd2tmZVNMdnhPN2dMSkhxaHVVUmNUbm9SUEkKNWNGQWRaQjJhamxqMzRVQlUwUWtPZ0tXd2krY1FuaFo5VzlWaGU5RTQwMW10enZFTEFYaVdXeFV0cjJvbk43bgo2SEVrQTZ1dlVhaENsdUx2WUJnSC82OXAzQTlTMWVIK0hOK2pTTlBXaWIreVJEak83OEJkWmM2QlNvOWhRV2xqClVweHV2MXB1ZUZIK2trckljazh3aEhJQ2pnQWlwNTl4MWRUbmRNVWI5M0N3UkxZaW1HUG1yTE9ydTU1K0tEUEoKbkVLT0FPT0JQMXE1RTFaN25sZ3RQUEVDZHhxWnVMc2NaT24xVXBFR05WN2o0YkJYeUI3VTV1KzhVcWUwWUhvcwowdmdTa2hEbHBGZkx5M0xoVTBVelBCcFBlbXYrR2pZUDRiUGhDZll6b1FLQmdRRFZMR0duazNOeDI3MTZNR2krCngwTmtCaVJtVjVrWEJROVhpWjgySlNPTVF2ZmJ0d3ArU1o3MS9adm9yNmY2Tnhac1BIL2RTaE4wd2dnaG9OcHoKYkhYK09IQjRmZWxMc1NJeDhqQUNkdERnd1BkTCtPRStPdUNtT01KNWE4OE4yRmRHQXAyME0yRzBDK2oxOXE1Rwo5c2RDVnRUbkVJVm5nOHJkWit6NFFRUVcwUUtCZ1FEUmxDYVYyTmVybHJtY0EveFZ5OXo5UmVPRVRJVHVjbkNTCkhvQ0o3dlczRzRqNm9HR2swYXlJK1VodkNDUVBYWUpSaWY1bnpBaTY4TXo5b1JtVllKNW9lMW5zWk9YUGlXTlAKRzlaaXE4cjExU0VtbnlmV1MwQ20rcmlYYzhMWDUzSFIrWmZFVDB1SGtaWmZJUERKd1E2QnF6dlZsek9sVXFoNwpldUd0R1ViTUR3S0JnRUU3dHBxSVJjQ201TUc2aEtNMDZRdDMwYlc3d3E1SHJ4MHprUlFKbzJvdHFCbUZWdUcwCmQ3K0JIeS9DYVIzcHM2UGxrNTlOSnMwSS8ySUREalphRDNWL0lmUE5YQkg4bjZFM0lyZnVVaGNsRmNnYTNocWoKKzlFQjk0a2VrNXNDQkJyNWUvY0Q3amNobWVXQzJtdis0cFVMYTFWV09leW1hUEw5OVljMnBpQ3hBb0dBU3dxTApEbGlJVUs0MVk0R2gxbTZhZ0MySUc1VHlmQmFKN0NmenltKy9sNExLZDBMNTgzUUlIVHQxRGh5U0hOWlVsVEdkCjREaVVYNnRkR3V1V0o0Qmk5L0pDN252cU5YZHlFWjljRWhTRkphVGNxK0dNK0JRVjREUWg2RWpRMlZITDFXMVQKUkQxSVJCQ0RFU2pRVHMxTGptNXJqRDBKaHdhandTTHNyM2cvZTAwQ2dZRUFsZjkrU1NManVxU0JCbmFDY0NLbApTbW5rM2RJMmxYOG5kY0RiUTVyOXhWTHJXcXBIK1BwSkIrUk45VWpVTWlrdFZrYmYrbjNwOVp5T2NxeG9YVk9wClRYMXY0Y0NjbDR5cC9xOXhtOU5FNGhRSkVsRm10YTlwMVJ3a2JmeWpqTnVVOUlMZjljM21MMlN0STFTV3gyUjAKWmlpT3hvUXdUSlFvK2phdlR4dUJHeFU9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
bkorpala@kubernetes-master1:~$ kubectl -n registry get secretsDalej musimy stworzyć usługę, która umożliwi nam komunikację z naszym kontenerem Registry (dodajemy do naszego manifestu oddzielając "---"):
NAME TYPE DATA AGE
default-token-k8x4m kubernetes.io/service-account-token 3 51m
registry-certs Opaque
apiVersion: v1Standardowo sprawdźmy co zrobiliśmy:
kind: Service
metadata:
name: kube-registry
labels:
k8s-app: kube-registry-upstream
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeRegistry"
spec:
selector:
k8s-app: kube-registry-upstream
ports:
- name: registry
port: 443
protocol: TCP
bkorpala@kubernetes-master1:~$ kubectl -n registry get servicesTeraz musimy zadeklarować obiekty PersistentVolume i PersistentVolumeClaim określające ilość miejsca, które możemy wykorzystać (dla mojego przykładu do przechowywania danych użyłem GlusterFS, na temat którego możecie przeczytać tutaj):
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
glusterfs-cluster ClusterIP 10.233.28.208 <none> 1/TCP 18h
kube-registry ClusterIP 10.233.29.16 <none> 443/TCP 10m
kind: PersistentVolumeWynik powinien być zbliżony do tego:
apiVersion: v1
metadata:
name: kube-registry-pv
labels:
kubernetes.io/cluster-service: "true"
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
glusterfs:
endpoints: glusterfs-cluster
path: registry
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: kube-registry-pvc
labels:
kubernetes.io/cluster-service: "true"
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1500M
bkorpala@kubernetes-master1:~$ kubectl -n registry get pvTeraz najważniejsza rzecz czyli wdrożenie naszego strąka z Docker Registry:
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
kube-registry-pv 2Gi RWO Retain Bound registry/kube-registry-pvc 9s
bkorpala@kubernetes-master1:~$ kubectl -n registry get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
kube-registry-pvc Bound kube-registry-pv 2Gi RWO 11s
apiVersion: v1Pobierzmy rezultat:
kind: ReplicationController
metadata:
name: kube-registry-v0
labels:
k8s-app: kube-registry-upstream
version: v0
kubernetes.io/cluster-service: "true"
spec:
replicas: 1
selector:
k8s-app: kube-registry-upstream
version: v0
template:
metadata:
labels:
k8s-app: kube-registry-upstream
version: v0
kubernetes.io/cluster-service: "true"
spec:
containers:
- name: registry
image: registry:2
resources:
limits:
cpu: 100m
memory: 100Mi
env:
- name: REGISTRY_HTTP_ADDR
value: :443
- name: REGISTRY_HTTP_TLS_CERTIFICATE
value: /certs/registry.crt
- name: REGISTRY_HTTP_TLS_KEY
value: /certs/registry.key
- name: REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY
value: /var/lib/registry
volumeMounts:
- name: registry-vol
mountPath: /var/lib/registry
subPath: registry
- name: registry-certs-vol
mountPath: /certs
ports:
- containerPort: 443
name: registry
protocol: TCP
volumes:
- name: registry-vol
persistentVolumeClaim:
claimName: kube-registry-pvc
- name: registry-certs-vol
secret:
secretName: registry-certs
bkorpala@kubernetes-master1:~$ kubectl -n registry get rcTeraz z dowolnego węzła Kubernetes możemy pingować nasz strąk i nazwa domeny powinna zostać rozwiązana na adres IP:
NAME DESIRED CURRENT READY AGE
kube-registry-v0 1 1 1 26s
bkorpala@kubernetes-master1:~$ kubectl -n registry get pods
NAME READY STATUS RESTARTS AGE
kube-registry-v0-xshs8 1/1 Running 0 35s
bkorpala@kubernetes-master1:~$ ping kube-registry.registry.svc.cluster.localSprawdźmy zatem czy Registry działa:
PING kube-registry.registry.svc.cluster.local (10.233.29.16) 56(84) bytes of data.
bkorpala@kubernetes-master1:~$ curl -k https://kube-registry.registry.svc.cluster.local/v2/_catalogPasuje coś wrzucić do naszego rejestru. Niechaj to będzie Nginx. Za pomocą komendy "docker pull nginx" pobieramy ostatnią wersję obrazu. Teraz pobieramy identyfikator obrazu:
{"repositories":[]}
bkorpala@kubernetes-master1:~$ sudo docker images | grep latest | grep nginxKomendą "docker tag 5699ececb21c kube-registry.registry.svc.cluster.local/nginx" oznaczamy nasz obraz celem wypchnięcia go na prywatne Registry: "docker push kube-registry.registry.svc.cluster.local/nginx". Standardowo weryfikujemy:
nginx latest 5699ececb21c 6 days ago 109 MB
bkorpala@kubernetes-master1:~$ curl -k https://kube-registry.registry.svc.cluster.local/v2/_catalogTeraz definiując manifest w polu "image" dla konkretnego kontenera możemy użyć wyżej wymienionego adresu.
{"repositories":["nginx"]}
bkorpala@kubernetes-master1:~$ curl -k https://kube-registry.registry.svc.cluster.local/v2/nginx/tags/list
{"name":"nginx","tags":["latest"]}
Jeżeli krok z dnsmasq pominęliśmy lub zwyczajnie nie chcemy w ten sposób rozwiązywać nazw to możemy stworzyć usługę, która na każdym z węzłów wystawi nam Registry na konkretnym porcie:
apiVersion: v1Sprawdźmy jak poszło:
kind: Service
metadata:
name: kube-registry-every-node
labels:
k8s-app: kube-registry-upstream
kubernetes.io/cluster-service: "true"
kubernetes.io/name: "KubeRegistry"
spec:
selector:
k8s-app: kube-registry-upstream
type: NodePort
ports:
- name: registry-every-node
nodePort: 31337
port: 443
protocol: TCP
bkorpala@kubernetes-master1:~$ sudo kubectl -n registry get servicesJeżeli chcemy tylko tymczasowo sprawdzić poprawność działania to możemy zwyczajnie przekazać konkretny port do naszej stacji roboczej:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
glusterfs-cluster ClusterIP 10.233.28.208 <none> 1/TCP 21h
kube-registry ClusterIP 10.233.29.16 <none> 443/TCP 3h
kube-registry-every-node NodePort 10.233.8.16 <none> 443:31337/TCP 30s
bkorpala@kubernetes-master1:~$ curl -k https://localhost:31337/v2/_catalog
{"repositories":["nginx"]}
bkorpala@kubernetes-master1:~$ sudo kubectl port-forward --namespace registry kube-registry-v0-xshs8 31337:443 &
[1] 20103
bkorpala@kubernetes-master1:~$ Forwarding from 127.0.0.1:31337 -> 443
bkorpala@kubernetes-master1:~$ curl -k https://localhost:31337/v2/_catalog
Handling connection for 31337
{"repositories":["nginx"]}
E0703 12:01:31.963822 20104 portforward.go:316] error copying from local connection to remote stream: read tcp4 127.0.0.1:31337->127.0.0.1:46754: read: connection reset by peer