Sätt upp ett Kubernetes kluster i AKS med HTTPS
Nu ska vi lära oss att sätta upp ett Kubernetes kluster i Azure med deras AKS tjänst. I klustret startar vi upp en simpel demo app och kopplar på HTTPS med hjälp av en Nginx ingress och en Cert-manager.
#Förutsättning
Du har en förståelse över hur kubernetes fungerar. Du vet vad följande saker är.
- pods
- services
- deployment
#Installera verktyg
Vi behöver lite verktyg för att lyckas med detta.
#az cli
Vi behöver få tillgång till Azure via terminalen, det använder vi az cli till.
Installera az cli genom att följa instruktionerna i Installera Azure CLI.
Kör sen az login
och logga in på er Azure användare.
#kubectl
Vi ska använda kubectl för att jobba mot vårt kluster från terminalen.
Installera kubectl genom att följa instruktionerna i Install Tools. Jag rekommendera att göra Enabling shell autocompletion så kan ni använda tab
för att autocomplete kommandon och resurser i kubectl.
#helm
Helm är en pakethanterare för kubernetes. Med helm går det att ladda ner färdiga pods och andra resurser till vårt kluster. Vi ska använda det för att installera en cert-manager
för att få till HTTPS.
Installera helm genom att följ instruktionerna i Install Tools.
#Skapa ett kluster på AKS
AKS är ett “managed kubernetes kluster”, vilket betyder att Azure sätter upp kubernetes miljön. Då behöver vi bara tänka på att använda klustret. Alla stora molntjänster har sin egna version av AKS.
Jobba igenom videon nedanför och skapa ett kluster.
När ni har skapat ett kluster ska vi göra så att vi kommer åt det med kubectl.
#Hämta config till kubectl
Börja med att kolla att vi hitta klustret med az cli. Om ni inte körde az login
efter installationen behöver ni göra det nu.
az aks show --name <namnet ni gav ert kluster> --resource-group <namn på er resursgrupp>
Om ni fick en lång utskrift json data har allt gått bra. Kör nästa kommando för att ladda ner config till kubectl.
$ az aks get-credentials --name <klusternamn> --resource-group <resursgrupp> Merged "demo" as current context in /home/zeldah/.kube/config
Kolla att det fungerade med följande kommand:
$ kubectl get nodes NAME STATUS ROLES AGE VERSION aks-agentpool-86164874-vmss000000 Ready agent 11m v1.20.9 aks-userpool-86164874-vmss000000 Ready agent 9m46s v1.20.9
#Felsök
Det är mycket möjligt att något går fel när ni ska göra resten av stegen. Ni kan använda följande kommandon för att försöka felsöka.
kubectl get pods kubectl describe <pod> kubectl describe deployment <deployment> kubectl logs <pod>
#Ingress som load balancer
Ni ska använda Ingress-Nginx som load balancer i klustret.
Innan ni sätter upp den ska ni läsa om skillnaden mellan NodePort, load balancer och ingress.
Läs Kubernetes NodePort vs LoadBalancer vs Ingress? When should I use what?.
Nu när ni vet skillnaden på olika sätt att hantera tillgång till vårt kluster ska ni sätta upp en nginx ingress.
#Installera ingress-nginx
Använd helm för att ladda ner ingress-ngingx
.
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx # lägger till repot för nginx-ingress $ helm repo update # laddar ner innehållet från repot Hang tight while we grab the latest from your chart repositories... ...Skip local chart repository ...Successfully got an update from the "stable" chart repository ...Successfully got an update from the "ingress-nginx" chart repository ...Successfully got an update from the "coreos" chart repository Update Complete. ⎈ Happy Helming!⎈ $ helm install ingress-nginx ingress-nginx/ingress-nginx \ --version 4.8.4 \ --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz # installerar ingressen i vårt kluster, sätter upp resurserna. NAME: nginx LAST DEPLOYED: Wed Dec 1 14:23:02 2021 NAMESPACE: default STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: The ingress-nginx controller has been installed. It may take a few minutes for the LoadBalancer IP to be available. You can watch the status by running 'kubectl --namespace default get services -o wide -w nginx-ingress-nginx-controller' An example Ingress that makes use of the controller: apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example namespace: foo spec: ingressClassName: nginx rules: - host: www.example.com http: paths: - backend: service: name: exampleService port: number: 80 path: / # This section is only required if TLS is to be enabled for the Ingress tls: - hosts: - www.example.com secretName: example-tls If TLS is enabled for the Ingress, a Secret containing the certificate and key must also be provided: apiVersion: v1 kind: Secret metadata: name: example-tls namespace: foo data: tls.crt: <base64 encoded cert> tls.key: <base64 encoded key> type: kubernetes.io/tls
Ni borde fått en liknande utskrift som ovanför. Vi kan kolla att det fungerade.
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 12m nginx-ingress-nginx-controller LoadBalancer 10.0.110.154 20.105.99.40 80:31787/TCP,443:30269/TCP 43s nginx-ingress-nginx-controller-admission ClusterIP 10.0.190.63 <none> 443/TCP 43s
Noter vilken ip ni har under EXTERNAL-IP
, om det står “pending” håller AKS på att skapa en IP åt er. Det kan ta ett par minuter. Kör kommandot igen tills ni har en EXTERNAL-IP
.
Öppna den IP i er webbläsare, då ska ni få upp en Nginx 404 sida.
#Koppla er domän till IP
Gå in på Azure och DNS zone, uppdatera era A records till att peka på er externa IP.
#Driftsätt demo appen
Vi kommer använda oss av demo appen kuard för att testa få igång allt. Jag förväntar mig att hi har koll på hur yml filerna ser ut för kubernetes och kommer därför inte förklara innehåller nedanför jätte mycket.
Vi börjar med en deployment för själva appen. Lägg följande innehåll i filen 01-deployment.yml
# 01-deployment.yml apiVersion: apps/v1 kind: Deployment metadata: name: kuard spec: selector: matchLabels: app: kuard replicas: 1 template: metadata: labels: app: kuard spec: containers: - image: gcr.io/kuar-demo/kuard-amd64:1 imagePullPolicy: Always name: kuard ports: - containerPort: 8080
Aktivera den med:
$ kubectl apply -f 01-deployment.yml deployment.apps/kuard created $ kubectl get deployment NAME READY UP-TO-DATE AVAILABLE AGE kuard 1/1 1 1 75s nginx-ingress-nginx-controller 1/1 1 1 9m28s
Nu ska vi lägga till en service så vi kan komma åt poden. Lägg följande i 02-service.yml
.
# 02-service.yml apiVersion: v1 kind: Service metadata: name: kuard spec: ports: - port: 80 targetPort: 8080 protocol: TCP selector: app: kuard
Aktivera det med:
$ kubectl apply -f 002-service.yaml service/kuard created $ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kuard ClusterIP 10.0.92.208 <none> 80/TCP 67s kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 31m nginx-ingress-nginx-controller LoadBalancer 10.0.194.219 20.105.99.40 80:30710/TCP,443:32004/TCP 10m nginx-ingress-nginx-controller-admission ClusterIP 10.0.189.69 <none> 443/TCP 10m
Nu ska vi skapa vår ingress som exponerar vår service utanför klustret. Lägg följande i filen 03-ingress.yml
, byt ut <ditt domännamn>
till ditt domännamn.
# 03-ingress.yml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: kuard annotations: # cert-manager.io/issuer: "letsencrypt-staging" spec: ingressClassName: nginx # kopplar vår ingress till den installerade nginx-ingress tls: # sätter att vi ska bara acceptera https trafik till er domän - hosts: - <ditt domännamn> secretName: demo-tls # det kommer senare skapas en secret med detta namnet som innehåller certificatet för vårt domännamn. rules: - host: <ditt domännamn> http: paths: - path: / pathType: Prefix backend: service: name: kuard port: number: 80
Kör nu följande för att aktivera den:
$ kubectl apply -f 003-ingress.yaml ingress.networking.k8s.io/kuard created $ kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE kuard <none> <domännamn> 80, 443 16s
Det kan ta några minuter att skapa den. Ni kan köra kubectl get ingress -w
för att se när ingressen är klar. När den får en ip under ADDRESS är den redo. Tryck då ctrl+c
för att avbryta att kolla på er ingress.
$ kubectl get ingress -w NAME CLASS HOSTS ADDRESS PORTS AGE kuard <none> <domännamn> 80, 443 16s kuard <none> <domännamn> 20.105.99.40 80, 443 58s
För att kolla att det fungerar, gå till webbläsaren och gå till ert domännamn. Använd http://
framför, vi har inte skaffat certifikat för HTTPS än. I webbläsaren borde ni få upp en säkerhetsvarning, då funkar det.
Ni kan också testa det från terminalen med curl -kivL http://<domän>
eller http://<ip>
. Då ska ni få en av två följande ut utskrifter:
... <html> <head><title>404 Not Found</title></head> <body> <center><h1>404 Not Found</h1></center> <hr><center>nginx</center> </body> </html>
alternativt
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>KUAR Demo</title> <link rel="stylesheet" href="/static/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/styles.css"> <script> var pageContext = {"hostname":"kuard-5cd5556bc9-mml79","addrs":["10.244.0.10"],"version":"v0.8.1-1","versionColor":"hsl(18,100%,50%)","requestDump":"GET / HTTP/1.1\r\nHost: arnesson.tech\r\nAccept: */*\r\nUser-Agent: curl/7.74.0\r\nX-Forwarded-For: 10.244.0.1\r\nX-Forwarded-Host: arnesson.tech\r\nX-Forwarded-Port: 443\r\nX-Forwarded-Proto: https\r\nX-Forwarded-Scheme: https\r\nX-Real-Ip: 10.244.0.1\r\nX-Request-Id: 8d31add9483d046f572171216e7f01b7\r\nX-Scheme: https","requestProto":"HTTP/1.1","requestAddr":"10.244.0.8:48746"} </script> </head> <svg style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <symbol id="icon-power" viewBox="0 0 32 32"> <title>power</title> <path class="path1" d="M12 0l-12 16h12l-8 16 28-20h-16l12-12z"></path> </symbol> <symbol id="icon-notification" viewBox="0 0 32 32"> <title>notification</title> <path class="path1" d="M16 3c-3.472 0-6.737 1.352-9.192 3.808s-3.808 5.72-3.808 9.192c0 3.472 1.352 6.737 3.808 9.192s5.72 3.808 9.192 3.808c3.472 0 6.737-1.352 9.192-3.808s3.808-5.72 3.808-9.192c0-3.472-1.352-6.737-3.808-9.192s-5.72-3.808-9.192-3.808zM16 0v0c8.837 0 16 7.163 16 16s-7.163 16-16 16c-8.837 0-16-7.163-16-16s7.163-16 16-16zM14 22h4v4h-4zM14 6h4v12h-4z"></path> </symbol> </defs> </svg> <body> <div id="root"></div> <script src="/built/bundle.js" type="text/javascript"></script> </body> </html> * Connection #1 to host arnesson.tech left intact
Nu är ni redo att skaffa certifikat för HTTPS.
#Installera cert-manager
Vi ska nu installer en cert-manager den lägger till två nya egenskapade resurs typer i vårt kluster, Issuer
och Certificate
.
Kör följande kommando för att installera:
$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.8.0/cert-manager.yaml customresourcedefinition.apiextensions.k8s.io/certificaterequests.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/certificates.cert-manager.io created customresourcedefinition.apiextensions.k8s.io/challenges.acme.cert-manager.io created ... $ kubectl get pods -n cert-manager NAME READY STATUS RESTARTS AGE cert-manager-55658cdf68-gxt9d 1/1 Running 0 4m58s cert-manager-cainjector-967788869-d82vt 1/1 Running 0 4m58s cert-manager-webhook-7b86bc6578-b2vsf 1/1 Running 0 4m58s
Vänta tills att de är redo innan ni går vidare till nästa steg.
#Let’s Encrypt Issuer
Nästa steg är att sätta upp en Issuer, det är den som genererar certifikaten. Det finns två typer av issuers, Issuer och ClusterIssuer. En Issuer är begränsad till ett namespace, medans en CluserIssuer kan jobba i alla namespace. Vi använder Issuer för att vi jobbar bara i ett namespace, default
.
Vi kommer sätta upp två issuers då Let’s Encrypt har en begränsning på antalet gånger man får fråga efter nya certifikat till produktion. Det blir lätt fel i början så då testar vi först med en staging issuer.
Skapa 04-issuer-staging.yml
och lägg till följande konfiguration. Byt ut <din email>
mot din email.
# 04-issuer-staging.yml apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: letsencrypt-staging spec: acme: # The ACME server URL server: https://acme-staging-v02.api.letsencrypt.org/directory # Email address used for ACME registration email: <din email> # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-staging # Enable the HTTP-01 challenge provider solvers: - http01: ingress: class: nginx
Aktivera den med kubectl apply -f 04-issuer-staging.yml
. Kör sen kubectl describe Issuer letsencrypt-staging
för att se att allt gick bra.
Name: letsencrypt-staging Namespace: default Labels: <none> Annotations: <none> API Version: cert-manager.io/v1 Kind: Issuer Metadata: Creation Timestamp: 2021-12-01T14:58:15Z Generation: 1 Managed Fields: API Version: cert-manager.io/v1 Fields Type: FieldsV1 fieldsV1: f:metadata: f:annotations: .: f:kubectl.kubernetes.io/last-applied-configuration: f:spec: .: f:acme: .: f:email: f:privateKeySecretRef: .: f:name: f:server: f:solvers: Manager: kubectl-client-side-apply Operation: Update Time: 2021-12-01T14:58:15Z API Version: cert-manager.io/v1 Fields Type: FieldsV1 fieldsV1: f:status: .: f:acme: .: f:lastRegisteredEmail: f:uri: f:conditions: Manager: controller Operation: Update Time: 2021-12-01T14:58:16Z Resource Version: 11689 UID: 584f8f36-4526-4432-9913-fab095dd6725 Spec: Acme: Email: aar@bth.se Preferred Chain: Private Key Secret Ref: Name: letsencrypt-staging Server: https://acme-staging-v02.api.letsencrypt.org/directory Solvers: http01: Ingress: Class: nginx Status: Acme: Last Registered Email: aar@bth.se Uri: https://acme-staging-v02.api.letsencrypt.org/acme/acct/35545288 Conditions: Last Transition Time: 2021-12-01T14:58:16Z Message: The ACME account was registered with the ACME server Observed Generation: 1 Reason: ACMEAccountRegistered Status: True Type: Ready Events: <none>
Kolla att Status
är True. Vi använder HTTP01 challenge providers för att få certifikaten, det finns också DNS01.
Nu ska vi skapa en likadan för prod istället för staging. Skapa 05-issuer-prod.yml
och lägg till följande konfiguration. Byt ut <din email>
mot din email.
# 05-issuer-prod.yml apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: letsencrypt-prod spec: acme: # The ACME server URL server: https://acme-v02.api.letsencrypt.org/directory # Email address used for ACME registration email: <din email> # Name of a secret used to store the ACME account private key privateKeySecretRef: name: letsencrypt-prod # Enable the HTTP-01 challenge provider solvers: - http01: ingress: class: nginx
Aktivera den med kubectl apply -f 05-issuer-prod.yml
. Kör sen kubectl describe Issuer letsencrypt-prod
och kolla att ni får liknande utskrift som för staging.
#Aktivera HTTPS (TLS)
Nu borde vi ha grunden för att få igång HTTPS för klustret.
#Staging
Öppna 03-ingress.yml
och avkommentera raden # cert-manager.io/issuer: "letsencrypt-staging"
. Aktivera sen er uppdaterade Ingress, cert-manager kommer då se annotationen för staging och aktivera er staging Issuer. Det kommer skapa ett fejk certifikat som läggs i er secret.
$ kubectl apply -f 003-ingress.yaml ingress.networking.k8s.io/kuard configured $ kubectl get certificate NAME READY SECRE AGE demo-tls False demo-tls 4m
Vänta på att den blir redo, ni kan köra kubectl get certificate -w
för att övervaka den tills den blir redo. Det kan ta någon minut innan den blir redo. Om det tar med än 3-5 min kan ni kolla loggarna för cert-managern och kolla om något är fel:
$ kubectl get po -n cert-manager NAME READY STATUS RESTARTS AGE cert-manager-5b54dc9c97-wrj8w 1/1 Running 0 179m cert-manager-cainjector-84565c968b-797x4 1/1 Running 0 179m cert-manager-webhook-d9d886b4c-rjql5 1/1 Running 0 179m $ kubectl logs cert-manager-5b54dc9c97-wrj8w -n cert-manager
Vi kan få en mer detaljerad vy över certifikatet.
$ kubectl describe certificate demo-tls Name: demo-tls Namespace: default Labels: <none> Annotations: <none> API Version: cert-manager.io/v1 Kind: Certificate Metadata: Creation Timestamp: 2021-12-01T15:00:59Z Generation: 1 Managed Fields: API Version: cert-manager.io/v1 Fields Type: FieldsV1 fieldsV1: f:metadata: f:ownerReferences: .: k:{"uid":"91034500-dd3b-4a05-b53f-341b25600cd2"}: .: f:apiVersion: f:blockOwnerDeletion: f:controller: f:kind: f:name: f:uid: f:spec: .: f:dnsNames: f:issuerRef: .: f:group: f:kind: f:name: f:privateKey: f:secretName: f:usages: f:status: .: f:conditions: f:notAfter: f:notBefore: f:renewalTime: f:revision: Manager: controller Operation: Update Time: 2021-12-01T15:01:25Z Owner References: API Version: networking.k8s.io/v1 Block Owner Deletion: true Controller: true Kind: Ingress Name: kuard UID: 91034500-dd3b-4a05-b53f-341b25600cd2 Resource Version: 12085 UID: 102284a3-0cdb-43b0-8182-86ea7a76246f Spec: Dns Names: arnesson.tech Issuer Ref: Group: cert-manager.io Kind: Issuer Name: letsencrypt-staging Secret Name: demo-tls Usages: digital signature key encipherment Status: Conditions: Last Transition Time: 2021-12-01T15:01:25Z Message: Certificate is up to date and has not expired Observed Generation: 1 Reason: Ready Status: True Type: Ready Not After: 2022-03-01T14:01:24Z Not Before: 2021-12-01T14:01:25Z Renewal Time: 2022-01-30T14:01:24Z Revision: 1 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Issuing 5m27s cert-manager Issuing certificate as Secret was previously issued by Issuer.cert-manager.io/letsencrypt-prod Normal Reused 5m27s cert-manager Reusing private key stored in existing Secret resource "demo-tls" Normal Requested 5m27s cert-manager Created new CertificateRequest resource "demo-tls-llqhg" Normal Issuing 5m1s cert-manager The certificate has been successfully issued
På slutet vill vi att det ska stå typ Normal Issuing 45s cert-manager The certificate has been successfully issued
.
Vi kan också kolla att vi har fått en secret.
$ kubectl describe secret demo-tls Name: demo-tls Namespace: default Labels: <none> Annotations: cert-manager.io/alt-names: arnesson.tech cert-manager.io/certificate-name: demo-tls cert-manager.io/common-name: arnesson.tech cert-manager.io/ip-sans: cert-manager.io/issuer-group: cert-manager.io cert-manager.io/issuer-kind: Issuer cert-manager.io/issuer-name: letsencrypt-prod cert-manager.io/uri-sans: Type: kubernetes.io/tls Data ==== tls.crt: 5595 bytes tls.key: 1679 bytes
Nu är ni redo att skapa certifikatet på riktigt och aktivera HTTPS.
#prod
Öppna 03-ingress.yaml
igen, på raden vi avkommenterade, byt ut staging
mot prod
. Aktivera er Ingress igen.
$ kubectl apply -f 003-ingress.yaml ingress.networking.k8s.io/kuard configured
Nu har vi dock redan en secret med samma namn. Det gör att det inte skapas ett nytt certifikat. För att det ska ske måste vi ta bort den gamla.
$ kubectl delete secret demo-tls secret "demo-tls" deleted
Nu kan ni köra samma kommandon för att kolla att det gick bra som ni gjorde för staging.
$ kubectl describe certificate demo-tls ... $ kubectl describe secret demo-tls ... $ kubectl get certificate ...
Om det tar tid kan ni kolla om kubectl describe order
och kubectl describe challenge
finns och om de har någon info.
That’s it! Nu kan ni gå till er domän i webbläsaren och ni borde mötas av en liknande vy. Om ni får fel kan det vara så att Microsoft’s DNS inte har uppdaterats. Gå till https://www.nslookup.io/website-to-ip-lookup/
och skriva in er domän. Kolla att den är kopplad till rätt IP. Om den inte är det dubbelkolla att ni har sparat ändringen i Azure annars är det bara att vänta. Ni kan också testa en annan webbläsare eller wget <er domän> && cat index.html
.
#Städa upp
Gå in på Azure och radera ert kluster eller kör kubectl delete all --all
och kubectl delete ingress <name> -n <namespace>
för att radera alla resurser i klustret. Använd helm uninstall ingress-nginx
för att ta bort den från helm också.
#Avslut
Då har ni satt upp ett kluster i AKS och aktiverat HTTPS till det. Många nya begrepp och kommandon då blir det rätt lite rörigt. Ta en liten paus innan ni fortsätter med resen av kursmomentet.
#Revision history
- 2021-12-03: (A, aar) Första revisionen.