Kubernetes FTW @RBC!

(Magnus Bengtsson)

Kubernetes är Google-projekt för att hantera microservices som kontainrar på ett enkelt men kraftfullt sätt. Kuberenetes är API-drivet med en (eller flera) masters som hanterar orkestreringsanrop, genom kubernetes-api server.

På mastern finns även en replication controller. Dess enda syfte är att se till den redundans som services skall ha är uppfylld, exempelvis har vi angett att tjänsten skall finns på 3 noder samtidigt ser rc till att så är fallet.

Sedan finns det så många container-noder som man vill. Dessa kör docker eller Rkt och styrs av en kubelet-tjänst som ser till att starta och övervaka kontainers på noderna. På noden finns även en service proxy som ser till att skicka in trafik till de tjänster som körs på noden.

Att komma igång med Kubernetes är inte alltid helt enkelt då det är många tjänster involverade samt att Kubernetes har ett eget koncept kring vad saker och ting är:

Pods: alla containrar körs i en Pod. En pod är en miljö, knuten till en nod där resurser kan delas mellan körande containrar. Pods är till sin natur flyktiga, en pod skall kunna försvinna utan ens applikation eller Service påverkas av det.Därför bör man alltid definera en Replication Controller tillsammans med sin tjänst som ser till att redundansen bibehålls med tjänsten.

Services: För att kunna publicera tjänster definerar man en Service. En service anger hur man kan nå en resurs i sina pods. Antingen genom att skapa en lastbalanserarservice eller genom en portservice, som publicerar tjänsten.

All konfiguration av Kuberenetes tjänster görs med YAML eller JSON.

Att få igång en fungerande Kubernetes miljö är som sagt inte helt enkelt, jag skall försöka beskriva stegen för att göra detta på ett befintligt CoreOS kluster, om du följt våra bloggar här tidigare så har du ett trevligt CoreOS kluster körandes i RBC!

Från CoreOS version 773.1.0 finns kublet med som standard, med kublet kan vi utan externa beroenden sätta upp ett komplett Kubernetes kluster på CoreOS.

I vår förra blog visade alltså jag hur vi kunde sätta upp ett komplett CoreOS kluster med CoreOS STABLE. För att dra nytta av CoreOS inbyggda kubelet behöver vi dock en nyare version av CoreOS och får hoppa på BETA kanalen (om du inte redan är där), Att byta kanal och uppdatera CoreOS är enkelt. Editera /etc/coreos/update.conf och sätt kanalen till Beta:

$ ssh 10.0.1.232 -l core -i demo.key
Last login: Sun Nov 22 19:03:40 2015 from 10.2.2.2
CoreOS stable (766.5.0)
$ vim /etc/coreos/update.conf
# GROUP=beta
$ sudo update_engine_client -check_for_update && exit

Upprepa ovan för samtliga dina CoreOS maskiner. Efter ett par minuter skall ditt kluster vara uppdaterat, ssh:a in till en maskin i klustret och kontrollera:

$ ssh 10.0.1.232 -l core -i demo.key
Last login: Sun Nov 22 19:21:31 2015 from 10.2.2.2
CoreOS beta (835.5.0)
core@coreos-demo-52c ~ $ fleetctl list-machines
MACHINE        IP        METADATA
38fd24c5...    10.0.1.232    region=rbc-se-sth-z1
5c5615dc...    10.0.1.148    region=rbc-se-sth-z1
6abaa20c...    10.0.1.97    region=rbc-se-sth-z1

Utmärkt! Vi är nu reda att starta konfigurationen av Kubernetes. Vi har 3 noder och kommer att sätta upp en Kubernetes master samt 2 st worker nodes.

CoreOS har en utmärkt guide i sin dokumentation som vi skall följa, lite förenklad. Vi kommer inte sätta upp TLS, i en produktionsmiljö bör detta dock alltid göras.

Vårt CoreOS kluster ser ut så här med de Kubernetes roller de skall få:

$ rbc-instances list
name             state    ipaddress   Kubernetes
---------------  -------  ----------- -------------
coreos-demo-52c  Running  10.0.1.232  Kube-Master
coreos-demo-a76  Running  10.0.1.97   Kube-Worker-1
coreos-demo-b09  Running  10.0.1.148  Kube-Worker-2

Om du följt den tidigare bloggen se till att ta bort cadvisor:

# fleetctl unload cadvisor

Kubernetes behöver lite adresser och variabler innan vi kan påbörja uppsättningen:

MASTER_HOST=10.0.1.232 # Adressen till Kuberentes mastern
ETCD_ENDPOINTS=http://10.0.1.232:4001,http://10.0.1.97:4001,http://10.0.1.148 # Kommasspearerad lista med etcd maskiner.
POD_NETWORK=10.132.0.0/16 # Vårt flannelnätverk som vi satt upp med cloud-config.
SERVICE_IP_RANGE=10.10.0.0/24 # Ett privat nätverk som kubenernetes kommer att använda.
K8S_SERVICE_IP=10.10.0.1 # VIP för kubernetes api endpoint
DNS_SERVICE_IP=10.10.0.10 # VIP för virtuell DNS tjänst i Kubernetes.
ADVERTISE_IP=x.x.x.x.x # Vår maskins interna ipadress, du kan se den i /etc/environment

För att förenkla uppsättningen skapar vi en miljövariabel-fil i /etc/kube-network, glöm inte att uppdatera ADVERTISE_IP med maskinens interna adresss.:

$ sudo -i
coreos-demo-52c ~ # cat >/etc/kube-network<<EOF
MASTER_HOST=10.0.1.232
ETCD_ENDPOINTS=http://10.0.1.232:4001,http://10.0.1.97:4001,http://10.0.1.148
POD_NETWORK=10.132.0.0/16
SERVICE_IP_RANGE=10.10.0.0/24
K8S_SERVICE_IP=10.10.0.1
DNS_SERVICE_IP=10.10.0.10
ADVERTISE_IP=X.X.X.X
EOF

Därefter behöver vi skapa en rsa nyckel för kube-serviceaccount:

$ sudo /bin/openssl genrsa -out /etc/kubernetes/kube-serviceaccount.key 2048 2>/dev/null

Vi skall även skapa ett script som vi kan använda som pre-exec för att vänta på tjänster:

$ sudo -i
coreos-demo-52c ~ # mkdir -p /opt/bin && cat >/opt/bin/wupiao<<EOF
#!/bin/bash
# [w]ait [u]ntil [p]ort [i]s [a]ctually [o]pen
[ -n "$1" ] && until curl -o /dev/null -sIf http://${1}; do sleep 1 && echo .; done;
exit $?
EOF
$ chmod +x /opt/bin/wupiao

Nu kan vi börja sätta upp vår Kubernetes master med systemd, vi börjar med Kubernetes API Server:

cat >/etc/systemd/system/kube-apiserver.service<<EOF
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
[Service]
EnvironmentFile=/etc/kube-network
ExecStartPre=-/usr/bin/mkdir -p /opt/bin
ExecStartPre=/usr/bin/curl -L -o /opt/bin/kube-apiserver -z /opt/bin/kube-apiserver https://storage.googleapis.com/kubernetes-release/release/v1.0.6/bin/linux/amd64/kube-apiserver
ExecStartPre=/usr/bin/chmod +x /opt/bin/kube-apiserver
ExecStartPre=/opt/bin/wupiao 127.0.0.1:2379/v2/machines
ExecStart=/opt/bin/kube-apiserver \
--service-account-key-file=/etc/kubernetes/kube-serviceaccount.key \
--service-account-lookup=false \
--advertise-adress=${ADDVERTISE_IP}
--admission-control=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota \
--runtime-config=api/v1 \
--allow-privileged=true \
--insecure-bind-address=0.0.0.0 \
--insecure-port=8080 \
--kubelet-https=true \
--secure-port=6443 \
--service-cluster-ip-range=${SERVICE_IP_RANGE} \
--etcd-servers=${ETCD_ENDPOINTS} \
--logtostderr=true
Restart=always
RestartSec=10
EOF

Sedan skall vi skapa tjänsten för replikeringskontrollanten:

cat > /etc/systemd/system/kube-controller-manager.service <<EOF
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
Requires=kube-apiserver.service
After=kube-apiserver.service
[Service]
ExecStartPre=/usr/bin/curl -L -o /opt/bin/kube-controller-manager -z /opt/bin/kube-controller-manager https://storage.googleapis.com/kubernetes-release/release/v1.0.6/bin/linux/amd64/kube-controller-manager
ExecStartPre=/usr/bin/chmod +x /opt/bin/kube-controller-manager
ExecStart=/opt/bin/kube-controller-manager \
--service-account-private-key-file=/etc/kubernetes/kube-serviceaccount.key \
--master=127.0.0.1:8080 \
--logtostderr=true
Restart=always
RestartSec=10
EOF

Slutligen skall vi få på Kubernetes Scheduler:

cat > /etc/systemd/system/kube-scheduler.service <<EOF
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
Requires=kube-apiserver.service
After=kube-apiserver.service
[Service]
ExecStartPre=/usr/bin/curl -L -o /opt/bin/kube-scheduler -z /opt/bin/kube-scheduler https://storage.googleapis.com/kubernetes-release/release/v1.0.6/bin/linux/amd64/kube-scheduler
ExecStartPre=/usr/bin/chmod +x /opt/bin/kube-scheduler
ExecStart=/opt/bin/kube-scheduler --master=127.0.0.1:8080
Restart=always
RestartSec=10
EOF

Med dessa tre tjänster definierade, gör en reload på systemd och starta tjänsterna:

systemctl daemon-reload
systemctl start kube-apiserver.service
systemctl start kube-controller-manager.service
systemctl start kube-scheduler.service

Kontrollera status på tjänsterna så att inga fel uppstått.

Bra! Det är nu dags att installera våra noder som skall köra containers.

Kubernetes-Noder

Börja med att ssh:a till den första instansen:

$ ssh 10.0.1.97 -l core -i demo.key
Last login: Sun Nov 22 19:20:50 2015 from 10.2.2.2
CoreOS beta (835.5.0)
core@coreos-demo-a76 ~ $ sudo -i

Skapa samma fil som på mastern: /etc/kube-network, med identiskt innehåll, sedan skapar vi systemd tjänsten för kube-proxy, /etc/systemd/system/kube-proxy.service:

[Unit]
Description=Kubernetes Proxy
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
Requires=setup-network-environment.service
After=setup-network-environment.service
[Service]
EnvironmentFile=/etc/kube-network
ExecStartPre=/usr/bin/curl -L -o /opt/bin/kube-proxy -z /opt/bin/kube-proxy https://storage.googleapis.com/kubernetes-release/release/v1.0.6/bin/linux/amd64/kube-proxy
ExecStartPre=/usr/bin/chmod +x /opt/bin/kube-proxy
# wait for kubernetes master to be up and ready
ExecStartPre=/opt/bin/wupiao ${MASTER_HOST} 8080
ExecStart=/opt/bin/kube-proxy \
--master=${MASTER_HOST}:8080 \
--logtostderr=true
Restart=always
RestartSec=10

Sedan skapar vi systemd servicen för kubelet. Kubelet är ansvarig för att hantera uppgifter på noden, som att starta och stoppa pods samt att övervaka och orkestrera kontainrar.

/etc/systemd/system/kube-kubelet.service:

[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
EnvironmentFile=/etc/kube-network
ExecStart=/usr/bin/kubelet \
--address=0.0.0.0 \
--port=10250 \
--api-servers=${MASTER_HOST}:8080 \
--hostname-override=${ADVERTISE_IP} \
--cluster_dns=${DNS_SERVICE_IP} \
--allow-privileged=true \
--logtostderr=true \
--cadvisor-port=4194 \
--healthz-bind-address=0.0.0.0 \
--healthz-port=10248
Restart=always
RestartSec=10

Skapa bin katalog för Kubernetes binärer:

mkdir -p /opt/bin

Starta sedan tjänsterna:

systemctl start kube-proxy
systemctl status kube-proxy
systemctl start kube-kubelet
systemctl status kube-kubelet

Upprepa stegen ovan på alla de noder som du vill skall köra kontainrar i ditt kluster.

Du skall nu ha ett körande Kubernetes kluster på dina CoreOS instanser.

För att hantera en Kubernetes installation behöver man kubectl. Så ta dig tillbaka till din master och ladda hem kubectl:

$ ARCH=linux; curl https://storage.googleapis.com/kubernetes-release/release/v1.0.6/bin/${ARCH}/amd64/kubectl -o kubectl && chmod +x kubectl
# Kontrollera att vi ser våra noder
core@coreos-demo-52c ~ $ ./kubectl get nodes
NAME              LABELS                                   STATUS
coreos-demo-a76   kubernetes.io/hostname=coreos-demo-a76   Ready
coreos-demo-b09   kubernetes.io/hostname=coreos-demo-b09   Ready

Nu skall vi bara skapa vårt kluster samt lägga till vår skyDNS tjänst:

./kubectl config set-cluster default-cluster

Skapa nedan fil som dns-addon.yml i din hemkatalog:

apiVersion: v1
kind: Service
metadata:
 name: kube-dns
 namespace: kube-system
 labels:
   k8s-app: kube-dns
   kubernetes.io/cluster-service: "true"
   kubernetes.io/name: "KubeDNS"
spec:
 selector:
   k8s-app: kube-dns
 clusterIP: 10.10.0.10
 ports:
 - name: dns
   port: 53
   protocol: UDP
 - name: dns-tcp
   port: 53
   protocol: TCP

---

apiVersion: v1
kind: ReplicationController
metadata:
 name: kube-dns-v9
 namespace: kube-system
 labels:
   k8s-app: kube-dns
   version: v9
   kubernetes.io/cluster-service: "true"
spec:
 replicas: 1
 selector:
   k8s-app: kube-dns
   version: v9
 template:
   metadata:
     labels:
       k8s-app: kube-dns
       version: v9
       kubernetes.io/cluster-service: "true"
   spec:
     containers:
     - name: etcd
       image: gcr.io/google_containers/etcd:2.0.9
       resources:
         limits:
           cpu: 100m
           memory: 50Mi
       command:
       - /usr/local/bin/etcd
       - -data-dir
       - /var/etcd/data
       - -listen-client-urls
       - http://127.0.0.1:2379,http://127.0.0.1:4001
       - -advertise-client-urls
       - http://127.0.0.1:2379,http://127.0.0.1:4001
       - -initial-cluster-token
       - skydns-etcd
       volumeMounts:
       - name: etcd-storage
         mountPath: /var/etcd/data
     - name: kube2sky
       image: gcr.io/google_containers/kube2sky:1.11
       resources:
         limits:
           cpu: 100m
           memory: 50Mi
       args:
       # command = "/kube2sky"
       - -domain=cluster.local
     - name: skydns
       image: gcr.io/google_containers/skydns:2015-03-11-001
       resources:
         limits:
           cpu: 100m
           memory: 50Mi
       args:
       # command = "/skydns"
       - -machines=http://localhost:4001
       - -addr=0.0.0.0:53
       - -domain=cluster.local.
       ports:
       - containerPort: 53
         name: dns
         protocol: UDP
       - containerPort: 53
         name: dns-tcp
         protocol: TCP
       livenessProbe:
         httpGet:
           path: /healthz
           port: 8080
           scheme: HTTP
         initialDelaySeconds: 30
         timeoutSeconds: 5
       readinessProbe:
         httpGet:
           path: /healthz
           port: 8080
           scheme: HTTP
         initialDelaySeconds: 1
         timeoutSeconds: 5
     - name: healthz
       image: gcr.io/google_containers/exechealthz:1.0
       resources:
         limits:
           cpu: 10m
           memory: 20Mi
       args:
       - -cmd=nslookup kubernetes.default.svc.cluster.local localhost >/dev/null
       - -port=8080
       ports:
       - containerPort: 8080
         protocol: TCP
     volumes:
     - name: etcd-storage
       emptyDir: {}
     dnsPolicy: Default

Sedan startar vi DNS-addon i Kubernetes:

core@coreos-demo-52c ~ $ ./kubectl create -f dns-addon.yml
services/kube-dns
replicationcontrollers/kube-dns-v9

Du bör dessutom kunna se poden:

$ ./kubectl get pods --all-namespaces
NAMESPACE     NAME                READY     STATUS    RESTARTS   AGE
kube-system   kube-dns-v9-f4clq   3/4       Running   0          2m

Du har nu ett komplett Kubernetes kluster installerat i RBC!. Nu är det dags att starta upp lite containrar med hjälp av kubernetes.

Det enklaste är att följa http://kubernetes.io/v1.0/examples/guestbook-go/README.html.

Du kommer att behöva ändra i  guestbook-service.json från type: loadBlancer till type: nodePort.

När du sedan följt guiden för Guestbook ovan skall du kunna sätta upp en lastbalanserare i RedBridge Cloud som når din nya gästbok!