Utiliser External-DNS avec Kubernetes Kapsule de Scaleway
[Update 01/10/2020] La version 0.7.4
de External-DNS est released.
Kubernetes Kapsule est le service de Kubernetes managé de Scaleway. Nous avons déjà traité la présentation du service dans un précédent article. Nous avions été assez élogieux, mettant particulièrement en avant le travail effectué sur le provider Terraform permettant de déployer et de manager, entièrement en infrastructure as code, nos clusters Kubernetes.
Nous avons aussi abordé l’utilisation des PodSecurityPolicy sur Kapsule, nous vous laissons (re)découvrir cet article si vous le souhaitez.
Quelle est notre problématique ici ?
Au sein de Kubernetes, le système DNS interne (assuré par CoreDNS par défaut)
assure en permanence la création, modification et suppression d’enregistrements
DNS pour les pods et les Services. Ainsi tous vos Services se voient attribuer
un nom dns de la forme : mon-service.mon-namespace.svc.cluster.local
,
pointant sur la ClusterIP du Service. Pareil pour les pods, ceux ci ont un
enregistrement DNS de la forme
adresse-ip-du-pod.mon-namespace.pod.cluster.local
. Moins utile vu qu’il faut
connaître l’IP du pod alors qu’il s’agit de l’information justement recherchée
au moyen du DNS.
Quoi qu’il en soit, l’utilisation du DNS pour les Services est omniprésente et il s’agit du moyen que vous devriez utiliser pour faire communiquer vos applications à l’intérieur de Kubernetes.
En interne cela fonctionne bien. Le problème apparait quand ces Services sont
exposés à l’extérieur du cluster notamment avec le type LoadBalancer
qui va
générer des IP publiques. Kubernetes n’a, nativement, aucun moyen de faire
matcher votre application masuperapp.particule.io avec l’IP publique générée
par votre Service de type LoadBalancer
. Et c’est ce que External-DNS résout.
External-DNS
External-DNS est un addon à Kubernetes extrêmement populaire permettant de synchroniser les Services et Ingress exposés avec un provider DNS. Contrairement à CoreDNS, le composant DNS interne de Kubernetes, External-DNS n’est pas un serveur DNS, il ne fait qu’enregistrer les informations récupérées depuis l’API Server vers un provider DNS. External-DNS permet donc de dynamiquement configurer vos enregistrements DNS en vous concentrant uniquement sur Kubernetes.
Et ce qui nous intéressant ici c’est le provider Scaleway disponible pour External-DNS.
Déploiement d’un cluster Kubernetes Kapsule
Commençons par se servir du provider Terraform pour déployer un cluster Kubernetes Kapsule. Nous avons déjà abordé ce sujet dans un précédent article, vous pouvez vous y reporter pour plus d’infos.
Voici le code utilisé :
provider "scaleway" {
zone = "fr-par-1"
region = "fr-par"
version = "~> 1.11"
}
resource "scaleway_k8s_cluster_beta" "k8s" {
name = "particule"
version = "1.19.0"
cni = "weave"
enable_dashboard = false
ingress = "nginx"
}
resource "scaleway_k8s_pool_beta" "commonpool" {
cluster_id = scaleway_k8s_cluster_beta.k8s.id
name = "commonpool"
node_type = "GP1-XS"
size = 1
autoscaling = true
autohealing = true
min_size = 1
max_size = 5
}
On applique et quelques instants plus tard :
$ terraform apply
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
+ create
[...]
Plan: 4 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
scaleway_registry_namespace_beta.main: Creating...
scaleway_k8s_cluster_beta.k8s: Creating...
scaleway_registry_namespace_beta.main: Creation complete after 1s [id=fr-par/b6d93ffb-908e-4955-b192-e4f42f0a9be6]
scaleway_k8s_cluster_beta.k8s: Creation complete after 7s [id=fr-par/5e1a8c9b-7430-46bd-a24c-9eed4edf9490]
scaleway_k8s_pool_beta.commonpool: Creating...
local_file.kubeconfig: Creating...
local_file.kubeconfig: Creation complete after 0s [id=b1eb17dc3631477154ee08878f6e197578c9c439]
scaleway_k8s_pool_beta.commonpool: Still creating... [10s elapsed]
scaleway_k8s_pool_beta.commonpool: Still creating... [20s elapsed]
scaleway_k8s_pool_beta.commonpool: Still creating... [30s elapsed]
scaleway_k8s_pool_beta.commonpool: Creation complete after 37s [id=fr-par/c2ab627a-1f99-477c-bf29-bcab15576479]
Apply complete! Resources: 4 added, 0 changed, 0 destroyed.
Outputs:
cluster_url = https://5e1a8c9b-7430-46bd-a24c-9eed4edf9490.api.k8s.fr-par.scw.cloud:6443
$ kubectl --kubeconfig kubeconfig get node
NAME STATUS ROLES AGE VERSION
scw-particule-commonpool-b0b52be520ed4d8bbc170 Ready <none> 16m v1.19.0
Notre cluster est prêt.
Le DNS as a Service de Scaleway est sorti en juin 2020 mais est toujours en Beta.
Peu importe, les fonctions de base de l’API sont fonctionnelles n’avons qu’à y configurer un domaine.
Vous allez devoir valider la possession de ce domaine en renseignant un enregistrement TXT là où est géré actuellement votre domaine. Cela dépendra de chacun d’entre vous. Une fois la validation effectuée (celle ci est quasi instantannée) votre domaine sera activé chez Scaleway et il faudra faire pointer les NS de votre domaine vers ceux de Scaleway pour qu’une résolution complète fonctionne.
- ns0.dom.scw.cloud.
- ns1.dom.scw.cloud.
Déploiement de External-DNS
Comme la totalité des addons à Kubernetes, celui ci se déploie dans Kubernetes, ici sous forme d’un Deployment.
Le support de Scaleway par External-DNS est extrêmement récent (quelques semaines) et aucune release n’existe actuellement. Nous allons donc devoir builder notre propre image d’External-DNS depuis la branch master.
[Update] Depuis le 01/10/2020, la release 0.7.4
de External-DNS supporte
Scaleway DNS. Vous n’êtes pas obligé de builder vous même l’image et vous pouvez donc directement utiliser l’image officielle
k8s.gcr.io/external-dns/external-dns:v0.7.4
$ git clone https://github.com/kubernetes-sigs/external-dns
$ cd external-dns
$ make build.docker
Vous devriez récupérer une image
us.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.7.3-106-g0947994d
.
Je vais retag cette image et la pousser sur Docker Hub pour que mon
cluster Kubernetes puisse facilement y accéder.
$ docker tag us.gcr.io/k8s-artifacts-prod/external-dns/external-dns:v0.7.3-106-g0947994d particule/external-dns:scaleway
$ docker push particule/external-dns:scaleway
C’est donc cette dernière image que nous utiliserons pour notre déploiement.
Afin que External-DNS puisse synchroniser notre DNS avec nos Services et nos
ingress, il faut lui donner les droits correspondants. Nous créeons donc un
ServiceAccount pour porter ces droits et nous le donnons à notre pod
external-dns. Pour une question de simplicitié je ne vais surveiller que les
Services avec --source=service
.
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: external-dns
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: external-dns
rules:
- apiGroups: [""]
resources: ["services","endpoints","pods"]
verbs: ["get","watch","list"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses"]
verbs: ["get","watch","list"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["list","watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: external-dns-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: external-dns
subjects:
- kind: ServiceAccount
name: external-dns
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: external-dns
spec:
replicas: 1
kselector:
matchLabels:
app: external-dns
strategy:
type: Recreate
template:
metadata:
labels:
app: external-dns
spec:
serviceAccountName: external-dns
containers:
- name: external-dns
image: particule/external-dns:scaleway
args:
- --source=service
- --domain-filter=particule.cloud
- --provider=scaleway
env:
- name: SCW_ACCESS_KEY
value: "<your access key>"
- name: SCW_SECRET_KEY
value: "<your secret key>"
- name: SCW_DEFAULT_ORGANIZATION_ID
value: "<your organization ID>"
On applique :
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
external-dns-68fc6d9854-l9xqf 1/1 Running 0 10s
Il ne nous reste plus qu’à déployer une application pour tester.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: helloworld
labels:
app: helloworld
spec:
replicas: 3
selector:
matchLabels:
app: helloworld
template:
metadata:
labels:
app: helloworld
spec:
containers:
- name: helloworld
image: particule/helloworld
imagePullPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: helloworld
annotations:
external-dns.alpha.kubernetes.io/hostname: helloworld.particule.cloud
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: helloworld
type: LoadBalancer
External-DNS devrait donc associer l’IP du LoadBalancer qui sera crée au nom
helloworld.particule.cloud
.
On applique et on regarde les logs d’External-DNS :
$ kubectl apply -f helloworld-externaldns.yml
deployment.apps/helloworld configured
service/helloworld configured
$ kubectl logs external-dns-68fc6d9854-l9xqf
[...]
time="2020-09-10T14:19:26Z" level=info msg="Skipping record particule.cloud because type NS is not supported"
time="2020-09-10T14:19:26Z" level=info msg="Skipping record particule.cloud because type NS is not supported"
time="2020-09-10T14:19:26Z" level=info msg="Updating zone particule.cloud"
time="2020-09-10T14:19:26Z" level=info msg="Adding record" data=195.154.70.34 priority=0 record=helloworld.particule.cloud ttl=300 type=A
time="2020-09-10T14:19:26Z" level=info msg="Adding record" data="\"heritage=external-dns,external-dns/owner=default,external-dns/resource=service/default/helloworld\"" priority=0 record=helloworld.particule.cloud ttl=300 type=TXT
On voit que deux enregistrements ont été crée et le premier, celui qui nous intéresse, contient bien l’IP de notre LoadBalancer :
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helloworld LoadBalancer 10.36.30.30 195.154.70.34 80:30350/TCP 23m
Si on interroge les serveurs DNS de Scaleway, on voit que l’enregistrement est bien présent :
$ dig @ns0.dom.scw.cloud. helloworld.particule.cloud +short
195.154.70.34
Conclusion
External-DNS fait parti de nos addons préférés. Associé à la quasi totalité des
cloud provider, celui ci fait des miracles avec très peu de choses à faire au
préalable. L’effet obtenu est généralement extrêmement apprécié puisqu’il
permet avec un simple kubectl apply
de déployer une application accessible
via un nom de domaine sans aucune autre intervention manuelle. Couplez ça à
Let’s Encrypt et vous avez votre application accessible publiquement, via un
nom de domaine et via un canal sécurisé avec TLS.
Il est d’ailleurs aussi possible d’utiliser External-DNS en dehors d’un cloud provider, Kevin Lefevre en parle dans un de nos autres articles.
Romain Guichard, CEO & co-founder