Oppsett av en minimal Kubernetes-klynge hos Safespring med Kubespray

Safespring er en skyplattform bygget på OpenStack. Dette innlegget viser én måte å komme fra null ressurser til en minimal Kubernetes-klynge, utelukkende ved hjelp av kode.

Jarle Bjørgeengen

Jarle Bjørgeengen

Former Chief Product Officer

Denne tekst er automatisk oversat for din bekvemmelighed. Du kan læse teksten på:

.

Kubespray er et omfattende verktøy som dekker mange ulike bruksområder. I dette innlegget fokuserer vi på nødvendig konfigurasjon for å installere et minimalt K8S-kluster for testing og eksperimentering på Safespring.

Forutsetninger

  1. Dette blogginnlegget forutsetter at du bruker det åpne kildekode‑verktøyet Terraform CLI. Terraform CLI er bare et binært program som du laster ned fra utgivelsessiden for din arkitektur/plattform. Her finner du også sjekksummer for filene for å verifisere deres integritet. Det finnes også den offisielle Terraform-dokumentasjonen.
  2. En grunnleggende forståelse av Ansible playbooks og inventory er også nødvendig.
  3. Noe grunnleggende bruk av OpenStack CLI vil også være nødvendig.

Introduksjon til Kubespray

Kubespray er en samling av Ansible playbooks, inventory, provisjoneringsverktøy og domenekunnskap for generelle oppgaver innen konfigurasjonsstyring av OS-/Kubernetes-klynger. Det er mye brukt, har stor fleksibilitet via parametere og vedlikeholdes aktivt.

For å provisjonere virtuell infrastruktur på OpenStack bruker Kubespray Terraform‑kode sammen med et inventory‑skript som forsyner Kubespray sine Ansible‑playbooks med inventory basert på Terraform‑tilstandsfilen som beskriver infrastrukturen provisjonert av Terraform.

Provisjoner infrastrukturen

  1. Opprett først en katalog der du legger lokal klyngekonfigurasjon og tilstandsfiler:
     $ mkdir -p ~/kubespray-clusters/minimal-k8s
  1. Klon kubespray-repositoriet til din foretrukne plassering, og sjekk ut den nyeste versjonen:
   $ cd ~/git/
   $ git clone https://github.com/kubernetes-sigs/kubespray.git
   $ cd kubespray
   $ git tag
   (....)
   $ git checkout v2.19
  1. Opprett en Terraform-variabelfil for infrastrukturen (eksempel):
   $ cat ~/kubespray-clusters/minimal-k8s/cluster.tfvars
   use_existing_network = "true"
   force_null_port_security = "true"

   # your Kubernetes cluster name here
   cluster_name = "minimal-k8s"

   # image to use for bastion, masters, standalone etcd instances, and nodes
   image = "ubuntu-20.04"

   # 0|1 bastion nodes
   number_of_bastions = 0

   use_neutron = 0

   # standalone etcds
   number_of_etcd = 0

   # masters
   number_of_k8s_masters = 0
   number_of_k8s_masters_no_etcd = 0
   number_of_k8s_masters_no_floating_ip = 1
   number_of_k8s_masters_no_floating_ip_no_etcd = 0
   flavor_k8s_master = "8f84ceab-89c1-4dfb-9ef6-97504475bd3a" #l2.c4r8.500
   #flavor_k8s_master = "e91ff4b7-cf9e-4d95-8374-aa3b1d765200" #l2.c4r8.1000

   # nodes
   number_of_k8s_nodes = 0
   number_of_k8s_nodes_no_floating_ip = 2
   flavor_k8s_node = "8f84ceab-89c1-4dfb-9ef6-97504475bd3a" # l2.c4r8.500

   # networking
   # ssh access to nodes
   k8s_allowed_remote_ips = ["0.0.0.0/0"]

   # Security group config
   worker_allowed_ports = [
     { # Node ports
       "protocol"         = "tcp"
       "port_range_min"   = 30000
       "port_range_max"   = 32767
       "remote_ip_prefix" = "0.0.0.0/0"
     },
     { # HTTP
       "protocol"         = "tcp"
       "port_range_min"   = 80
       "port_range_max"   = 80
       "remote_ip_prefix" = "0.0.0.0/0"
     },
     { # HTTPS
       "protocol"         = "tcp"
       "port_range_min"   = 443
       "port_range_max"   = 443
       "remote_ip_prefix" = "0.0.0.0/0"
     }
   ]
   external_net = "33dc493f-f4d5-4ab4-bf8e-43bee3faf3ef"
   use_access_ip = 1
   network_name = "public"
   public_key_path = "~/.ssh/id_rsa.pub"

Merk at vi bruker parameterne no_floating_ip for å angi antallet mastere og arbeidere. Dette er fordi Safespring IaaS-plattformen ikke har brukerkontrollerbare nettverkskomponenter som floating IP, subnett, ruter og så videre. Hvis du ikke er kjent med dette, les blogginnlegget som forklarer hvordan og hvorfor. Av samme grunn må vi også sette use_existing_network = "true" og force_null_port_security = "true".

Flavors må angis som flavor-ID-er. De kan finnes ved å bruke OpenStack CLI med openstack flavor list. external_net er ID-en til public-nettverket og kan finnes med openstack network list.

Unngå å lekke din personlige OpenStack-påloggingsinformasjon

Kubespray-installasjonen som vi skal bruke senere, vil konfigurere en skyleverandørkonfigurasjon i klyngen for å bruke dynamisk provisjonert lagring og koble til podder i klyngen. Installasjonen vil kopiere miljøvariablene som brukes til å provisjonere infrastrukturen, og lagre dem i klyngen for dette formålet. For å unngå å lekke din personlige OpenStack-påloggingsinformasjon inn i klyngens cloud-config, bør du heller opprette en applikasjonslegitimasjon og bruke den både til infrastrukturprovisjonering med Terraform og slik at Ansible-playbookene kan plukke den opp og kopiere den ved installasjon av K8S.

  1. Kort fortalt, gjør dette:
   $ openstack application credential create dummy
   +--------------+----------------------------------------------------------------------------------------+
   | Field        | Value                                                                                  |
   +--------------+----------------------------------------------------------------------------------------+
   | description  | None                                                                                   |
   | expires_at   | None                                                                                   |
   | id           | 26eb762775cc4544b4d4f76fdb642f9a                                                       |
   | name         | dummy                                                                                  |
   | project_id   | 74cf3e20e55345d29935625c7b3e5618                                                       |
   | roles        | member reader                                                                          |
   | secret       | fWtpQpR1-HShPkjjCa8pdMoI6oTWPQtB38qQVR-fjTJyynnLMjexY0T7M-2CQjqZMjLv4hct15leIRA4up4WWA |
   | system       | None                                                                                   |
   | unrestricted | False                                                                                  |
   | user_id      | 9956b53cf1ca4967a7a945b4e6657cf6                                                       |
   +--------------+----------------------------------------------------------------------------------------+
  1. Opprett deretter en miljøfil med følgende informasjon og source den:
   export OS_AUTH_URL=<same-as-in-your-personal-openstack-cli-config>
   export OS_REGION_NAME=<same-as-in-your-personal-openstack-cli-config>
   export OS_APPLICATION_CREDENTIAL_SECRET=fWtpQpR1-HShPkjjCa8pdMoI6oTWPQtB38qQVR-fjTJyynnLMjexY0T7M-2CQjqZMjLv4hct15leIRA4up4WWA
   export OS_APPLICATION_CREDENTIAL_ID=26eb762775cc4544b4d4f76fdb642f9a
   export OS_APPLICATION_CREDENTIAL_NAME=dummy
   export OS_AUTH_TYPE=v3applicationcredential
  1. ID og secret må selvfølgelig erstattes med verdiene fra applikasjonslegitimasjonen du selv har opprettet. Jeg sletter bare denne dummy-legitimasjonen for å unngå misbruk. $ openstack application credential delete dummy
  2. Gå til Terraform OpenStack-contrib-katalogen i kubespray-repoet og kjør terraform init og apply derfra. $ cd ~/git/kubespray/contrib/terraform/openstack/ $ terraform init $ terraform apply -var-file=$HOME/kubespray-clusters/minimal-k8s/cluster.tfvars -state=$HOME/kubespray-clusters/minimal-k8s/terraform.tfstate (...)
  3. Legg merke til at vi fikk infrastrukturen som lovet av terraform-koden, for eksempel:
   $ openstack server list |grep mini
   | 0ac5616f-aff7-4d5a-849b-54cbec614137 | minimal-k8s-k8s-master-nf-1 | ACTIVE  | public=212.162.146.113, 2a09:d400:0:1::296 | ubuntu-20.04             | l2.c4r8.500  |
   | 169d54b5-995b-4271-9b18-87547097d295 | minimal-k8s-k8s-node-nf-1   | ACTIVE  | public=212.162.147.139, 2a09:d400:0:1::221 | ubuntu-20.04             | l2.c4r8.500  |
   | a757b87c-03e1-4dd9-813b-8e40fd575196 | minimal-k8s-k8s-node-nf-2   | ACTIVE  | public=212.162.147.52, 2a09:d400:0:1::17b  | ubuntu-20.04             | l2.c4r8.500  |

Installer K8S med Kubespray

  1. Kopier inventory-skriptet fra Kubespray Git-repoet til klyngens config/state-katalog og sørg for at det er kjørbart. bash $ cp ~/git/kubespray/contrib/terraform/terraform.py ~/kubespray-clusters/minimal-k8s $ chmod +x ~/kubespray-clusters/minimal-k8s/terraform.py
  2. Gå til Kubespray-repoet, opprett et Python-virtualenv, aktiver det og installer Kubespray-avhengighetene.
   $ python3 -m venv venv
   $ source venv/bin/activate
   $ pip install -r requirements.txt
  1. Sjekk at du har ssh-tilgang og at inventory fungerer.
   $ ansible -i ~/kubespray-clusters/minimal-k8s/terraform.py -m ping all
   [WARNING]: Skipping callback plugin 'ara_default', unable to load
   minimal-k8s-k8s-master-nf-1 | SUCCESS => {
       "changed": false,
       "ping": "pong"
   }
   minimal-k8s-k8s-node-nf-2 | SUCCESS => {
       "changed": false,
       "ping": "pong"
   }
   minimal-k8s-k8s-node-nf-1 | SUCCESS => {
       "changed": false,
       "ping": "pong"
   }
  1. Opprett en konfigurasjonsfil for Kubespray Ansible-oppsettet, for eksempel slik:
   mkdir -p ~/kubespray-clusters/minimal-k8s-old/group_vars/k8s_cluster
   vi ~/kubespray-clusters/minimal-k8s-old/group_vars/k8s_cluster/ck8s-k8s-cluster.yaml
   (edit stuff)
   cat ~/kubespray-clusters/minimal-k8s-old/group_vars/k8s_cluster/ck8s-k8s-cluster.yaml
   artifacts_dir: "{{ inventory_dir }}/artifacts"
   cluster_admin_users:
   - k8sadmin@exmaple.com
   cluster_admin_groups:
   - admin-group
   kubeconfig_localhost: true
   force_null_port_security: true
   ingress_nginx_enabled: true
   ngress_nginx_host_network: true
   cloud_provider: external
   external_cloud_provider: openstack
   calico_mtu: 1480
   external_openstack_cloud_controller_extra_args:
     # Must be different for every cluster in the same openstack project
     cluster-name: "minimal-k8s"
   cinder_csi_enabled: true
   persistent_volumes_enabled: true
   storage_classes:
     - name: cinder-csi
       is_default: true
       parameters:
         allowVolumeExpansion: true
         availability: nova

En omfattende veiledning for tilgjengelige parametere finnes i Kubespray- dokumentasjonen

  1. Nå er det på tide å installere Kubernetes med Kubespray-playbooken og rollene.
   $ .venv/bin/ansible-playbook -i ~/kubespray-clusters/minimal-k8s/terraform.py cluster.yml -b
  1. Ta deg en kaffe eller to ….. eller kanskje til og med tre :-)

Teste klyngen

  1. Sørg for at du har kubectl installert.
  2. Installasjonen kopierer en kubeconfig til ~/kubespray-clusters/minimal-k8s/artifacts/admin.conf fordi kubeconfig_localhost: true og artifacts_dir: "{{ inventory_dir }}/artifacts". Denne filen gir full administratortilgang til klyngen (root) og bør beskyttes deretter.
  3. Sett miljøvariabelen KUBECONFIG til å peke på kubeconfigen for cluster-admin:
   $ cd ~/kubespray-clusters/minimal-k8s
   $ export KUBECONFIG=artifacts/admin.conf
  1. Undersøk noen egenskaper med kubectl:
   $ kubectl get nodes
   NAME                          STATUS   ROLES                  AGE     VERSION
   minimal-k8s-k8s-master-nf-1   Ready    control-plane,master   6m40s   v1.23.7
   minimal-k8s-k8s-node-nf-1     Ready    <none>                 5m27s   v1.23.7
   minimal-k8s-k8s-node-nf-2     Ready    <none>                 5m27s   v1.23.7

    kubectl get pods --all-namespaces -o wide
   NAMESPACE       NAME                                                  READY   STATUS    RESTARTS   AGE   IP                NODE                          NOMINATED NODE   READINESS GATES
   ingress-nginx   ingress-nginx-controller-mkn8d                        1/1     Running   0          20m   10.233.113.1      minimal-k8s-k8s-node-nf-1     <none>           <none>
   ingress-nginx   ingress-nginx-controller-s62ss                        1/1     Running   0          20m   10.233.125.1      minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     calico-kube-controllers-6dd874f784-mjsds              1/1     Running   0          20m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     calico-node-d42n5                                     1/1     Running   0          21m   212.162.147.139   minimal-k8s-k8s-node-nf-1     <none>           <none>
   kube-system     calico-node-p5jrt                                     1/1     Running   0          21m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     calico-node-qwj5l                                     1/1     Running   0          21m   212.162.147.52    minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     coredns-76b4fb4578-6n4rj                              1/1     Running   0          20m   10.233.113.2      minimal-k8s-k8s-node-nf-1     <none>           <none>
   kube-system     coredns-76b4fb4578-t8gxw                              1/1     Running   0          20m   10.233.72.1       minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     csi-cinder-controllerplugin-cf59469bb-xxt7j           6/6     Running   0          19m   10.233.125.2      minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     csi-cinder-nodeplugin-9mpf8                           3/3     Running   0          19m   212.162.147.52    minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     csi-cinder-nodeplugin-b2t99                           3/3     Running   0          19m   212.162.147.139   minimal-k8s-k8s-node-nf-1     <none>           <none>
   kube-system     dns-autoscaler-7979fb6659-67cv5                       1/1     Running   0          20m   10.233.72.2       minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     kube-apiserver-minimal-k8s-k8s-master-nf-1            1/1     Running   1          23m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     kube-controller-manager-minimal-k8s-k8s-master-nf-1   1/1     Running   1          23m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     kube-proxy-4rp76                                      1/1     Running   0          21m   212.162.147.139   minimal-k8s-k8s-node-nf-1     <none>           <none>
   kube-system     kube-proxy-j5w9g                                      1/1     Running   0          21m   212.162.147.52    minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     kube-proxy-jx5zq                                      1/1     Running   0          21m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     kube-scheduler-minimal-k8s-k8s-master-nf-1            1/1     Running   1          23m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     nginx-proxy-minimal-k8s-k8s-node-nf-1                 1/1     Running   0          21m   212.162.147.139   minimal-k8s-k8s-node-nf-1     <none>           <none>
   kube-system     nginx-proxy-minimal-k8s-k8s-node-nf-2                 1/1     Running   0          21m   212.162.147.52    minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     nodelocaldns-hg2gh                                    1/1     Running   0          20m   212.162.147.52    minimal-k8s-k8s-node-nf-2     <none>           <none>
   kube-system     nodelocaldns-qzxqz                                    1/1     Running   0          20m   212.162.147.139   minimal-k8s-k8s-node-nf-1     <none>           <none>
   kube-system     nodelocaldns-rbh2g                                    1/1     Running   0          20m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     openstack-cloud-controller-manager-jlzmd              1/1     Running   0          20m   212.162.146.113   minimal-k8s-k8s-master-nf-1   <none>           <none>
   kube-system     snapshot-controller-754bc6769f-4hqcd                  1/1     Running   0          19m   10.233.113.3      minimal-k8s-k8s-node-nf-1     <none>           <none>

   $ kubectl get storageclass
   NAME                   PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
   cinder-csi (default)   cinder.csi.openstack.org   Delete          WaitForFirstConsumer   true                   20m
  1. Test at lagringsklassen fungerer:
   cat pod-with-pvc.yml
   apiVersion: v1
   kind: Pod
   metadata:
     name: task-pv-pod
   spec:
     volumes:
       - name: task-pv-storage
         persistentVolumeClaim:
           claimName: task-pv-claim
     containers:
       - name: task-pv-container
         image: nginx
         ports:
           - containerPort: 80
             name: "http-server"
         volumeMounts:
           - mountPath: "/usr/share/nginx/html"
             name: task-pv-storage
   ---
   apiVersion: v1
   kind: PersistentVolumeClaim
   metadata:
     name: task-pv-claim
   spec:
      accessModes:
        - ReadWriteOnce
      resources:
          requests:
              storage: 2Gi


   $ kubectl apply -f pod-with-pvc.yml
   pod/task-pv-pod created
   persistentvolumeclaim/task-pv-claim created
   $ kubectl get pods
   NAME          READY   STATUS              RESTARTS   AGE
   task-pv-pod   0/1     ContainerCreating   0          13s
   $ kubectl get pods
   NAME          READY   STATUS    RESTARTS   AGE
   task-pv-pod   1/1     Running   0          20s
   $ kubectl get pvc
   NAME            STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
   task-pv-claim   Bound    pvc-e09d7ae7-391e-4659-a46b-2a9e7213b90a   2Gi        RWO            cinder-csi     27s

   $ kubectl delete  -f pod-with-pvc.yml
   pod "task-pv-pod" deleted
   persistentvolumeclaim "task-pv-claim" deleted

   $ kubectl get pvc
   No resources found in default namespace.
   $ kubectl get pods
   No resources found in default namespace.
  1. Test at ingressen fungerer:
   $ cat apple.yml
   kind: Pod
   apiVersion: v1
   metadata:
     name: apple-app
     labels:
       app: apple
   spec:
     containers:
       - name: apple-app
         image: hashicorp/http-echo
         args:
           - "-text=apple"

   ---

   kind: Service
   apiVersion: v1
   metadata:
     name: apple-service
   spec:
     selector:
       app: apple
     ports:
       - port: 5678 # Default port for image

   $ cat banana.yml
   kind: Pod
   apiVersion: v1
   metadata:
     name: banana-app
     labels:
       app: banana
   spec:
     containers:
       - name: banana-app
         image: hashicorp/http-echo
         args:
           - "-text=banana"

   ---

   kind: Service
   apiVersion: v1
   metadata:
     name: banana-service
   spec:
     selector:
       app: banana
     ports:
       - port: 5678 # Default port for image

   $ cat ingress-path-apple-banana.yml
   kind: Ingress
   apiVersion: networking.k8s.io/v1
   metadata:
     name: example-ingress
     annotations:
       ingress.kubernetes.io/rewrite-target: /
   spec:
     rules:
     - http:
         paths:
           - path: /apple
             pathType: Prefix
             backend:
               service:
                 name: apple-service
                 port:
                   number: 5678
           - path: /banana
             pathType: Prefix
             backend:
               service:
                 name: banana-service
                 port:
                   number: 5678

   $ kubectl apply -f apple.yml -f banana.yml -f ingress-path-apple-banana.yml
   pod/apple-app created
   service/apple-service created
   pod/banana-app created
   service/banana-service created
   ingress.networking.k8s.io/example-ingress created

Nå har vi opprettet to enkle podder som returnerer teksten “apple” og “banana” henholdsvis, samt to tjenester for å eksponere dem internt i klyngen. Koden i ingress-path-apple-banana.yml oppretter en ingress som eksponerer disse tjenestene på stiene /apple og /banana henholdsvis på alle arbeidsnoder, via nginx ingress-kontrolleren som ble installert fordi ingress_nginx_enabled: true og ngress_nginx_host_network: true

  1. La oss se om vi kan nå dem utenfra klyngen:
   $ openstack server list |grep node
   | 169d54b5-995b-4271-9b18-87547097d295 | minimal-k8s-k8s-node-nf-1   | ACTIVE  | public=212.162.147.139, 2a09:d400:0:1::221 | ubuntu-20.04             | l2.c4r8.500  |
   | a757b87c-03e1-4dd9-813b-8e40fd575196 | minimal-k8s-k8s-node-nf-2   | ACTIVE  | public=212.162.147.52, 2a09:d400:0:1::17b  | ubuntu-20.04             | l2.c4r8.500  |

   $ curl http://212.162.147.52/banana
   banana
   $ curl http://212.162.147.52/apple
   apple

Oppsummering

Da runder vi av dette blogginnlegget. Husk at dette oppsettet på ingen måte er egnet for produksjonsbruk. Ta gjerne kontakt med oss på info@safespring.com, så hjelper vi deg med å finne riktig løsning for Kubernetes i produksjon også.

Når du er ferdig med å teste, kan du fjerne hele oppsettet med:

$ cd ~/git/kubespray/contrib/terraform/openstack
$ terraform destroy -var-file=$HOME/kubespray-clusters/minimal-k8s/cluster.tfvars  -state=$HOME/kubespray-clusters/minimal-k8s/terraform.tfstate