Skip to content

Getting started with Kubernetes

Kubernetes (K8s) is an open-source system for automating deployment, scaling, and management of containerized applications.

It groups containers that make up an application into logical units for easy management and discovery. Kubernetes builds upon 15 years of experience of running production workloads at Google, combined with best-of-breed ideas and practices from the community.

Kubernetes

Install on linux cluster

Compile kubernetes

git clone https://github.com/kubernetes/kubernetes.git
cd kubernetes
make release

Install master node

#Add repos
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*
EOF

#Set SELinux in permissive mode (effectively disabling it)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

#Install
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
systemctl enable --now kubelet

cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

#Install network plugin
sudo kubeadm init --pod-network-cidr=192.168.0.0/16
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/rbac-kdd.yamlrm 
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml

#Copy kubernetes config to user folder
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Install slave node

#Run on master node to get the join commands
sudo kubeadm token create --print-join-command

#Copy the join commands and run on slave node
kubeadm join <master-ip>:6443 --token 2kjo2d.8qno0vzvbgabp1e8 --discovery-token-ca-cert-hash sha256:2f1ebea7d7369a2d18b58f2926573e193e13ef7525649df1740ddb87963e1315

Install metrics server

git clone https://github.com/kodekloudhub/kubernetes-metrics-server.git
kubectl create -f kubernetes-metrics-server/
kubectl top nodes
kubectl top pods

Install prometheus

kubectl apply --filename https://raw.githubusercontent.com/giantswarm/kubernetes-prometheus/master/manifests-all.yaml
#Maybe you should download the manifests-all.yaml and customize it yourself. For example, you would add Grafana configuration in it.
#Create Traefik ingress yaml and apply

Install helm

#Download helm tar.gz file from https://github.com/helm/helm/releases
tar -zxvf helm-v2.14.1-linux-amd64.tar.gz
sudo cp linux-amd64/helm /usr/local/bin
helm init
helm version
kubectl get pod -n kube-system -l app=helm
kubectl create -f rbac.yml
kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

Traefik

Create Traefik toml

defaultEntryPoints = ["http","https"]
# Enable inner-cluster https navigation
insecureSkipVerify = true
[entryPoints]
  [entryPoints.http]
  address = ":80"
    [entryPoints.http.redirect]
    entryPoint = "https"
  [entryPoints.https]
  address = ":443"
    [entryPoints.https.tls]
      [[entryPoints.https.tls.certificates]]
      certFile = "/etc/kubernetes/ssl/cert.pem"
      keyFile = "/etc/kubernetes/ssl/privkey.pem"
[metrics]
  [metrics.prometheus]
    entryPoint = "traefik"
    buckets = [0.1, 0.3, 1.2, 5.0]

Create Traefik yaml

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: traefik-ingress-controller
  namespace: kube-system
  labels:
    k8s-app: traefik-ingress-lb
spec:
  replicas: 1
  selector:
    matchLabels:
      k8s-app: traefik-ingress-lb
  template:
    metadata:
      labels:
        k8s-app: traefik-ingress-lb
        name: traefik-ingress-lb
    spec:
      serviceAccountName: traefik-ingress-controller
      terminationGracePeriodSeconds: 60
      tolerations:
        - operator: "Exists"
      nodeSelector:
        node: master
      volumes:
        - name: ssl
          secret:
            secretName: traefik-cert
        - name: config
          configMap:
            name: traefik-conf
      containers:
        - image: traefik
          name: traefik-ingress-lb
          volumeMounts:
            - mountPath: "/etc/kubernetes/ssl"
              name: "ssl"
            - mountPath: "/home/ec2-user/kube/traefik"
              name: "config"
          ports:
            - name: http
              containerPort: 80
              hostPort: 80
            - name: https
              containerPort: 443
              hostPort: 443
            - name: admin
              containerPort: 8080
          args:
            - --api
            - --kubernetes
            - --logLevel=INFO
            - --configfile=/home/ec2-user/kube/traefik/traefik.toml
---
kind: Service
apiVersion: v1
metadata:
  name: traefik-ingress-service
  namespace: kube-system
spec:
  selector:
    k8s-app: traefik-ingress-lb
  ports:
    - protocol: TCP
      port: 80
      name: web
    - protocol: TCP
      port: 443
      name: https
    - protocol: TCP
      port: 8080
      name: admin
  type: NodePort

Create Traefik rbac

apiVersion: v1
kind: ServiceAccount
metadata:
  name: traefik-ingress-controller
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
rules:
  - apiGroups:
      - ""
    resources:
      - services
      - endpoints
      - secrets
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - extensions
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: traefik-ingress-controller
    namespace: kube-system

Create Traefik web-ui ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: traefik-web-ui
  namespace: kube-system
  annotations:
    kubernetes.io/ingress.class: traefik
spec:
  rules:
    - host: [domain]
      http:
        paths:
          - backend:
              serviceName: traefik-ingress-service
              servicePort: 8080

Create Traefik HTTPS cert

sudo -s
cd /etc/letsencrypt/live/[domain]
#Update https cert keys and go to domain management console to modify the txt record
kubectl create secret generic traefik-cert --from-file=privkey.pem --from-file=cert.pem -n kube-system
cp cert.pem privkey.pem /etc/kubernetes/ssl/
#exit and go to the traefik k8s scripts folder
kubectl create configmap traefik-conf --from-file=traefik.toml -n kube-system
kubectl get cm -n kube-system

Renew Traefik HTTPS cert

kubectl delete secret traefik-cert -n kube-system
kubectl delete configmap traefik-conf -n kube-system
kubectl delete -f traefik.yml
#Re-run the above "Create Traefik HTTPS cert" steps
kubectl create -f traefik.yml

Update Traefik.toml

#Firstly modify the traefik.toml
kubectl delete configmap traefik-conf -n kube-system
kubectl create configmap traefik-conf --from-file=traefik.toml -n kube-system
kubectl delete -f traefik.yml
kubectl create -f traefik.yml

Kubernetes Dashboard for local

Create service account

#admin-user.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system

Create role binding

#admin-user-role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system

Apply yaml

#Apply service account
kubectl create -f admin-user.yaml

#Apply role binding
kubectl create -f admin-user-role-binding.yaml

Retrieve login token

kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')

Install dashboard

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/aio/deploy/recommended/kubernetes-dashboard.yaml

#Create proxy to allow only localhost navigating
kubectl proxy
kubectl proxy --address='0.0.0.0' --accept-hosts='^*$'

Kubernetes Dashboard for public

Create certificates

mkdir $HOME/certs
cd $HOME/certs
openssl genrsa -out dashboard.key 2048
openssl rsa -in dashboard.key -out dashboard.key
openssl req -sha256 -new -key dashboard.key -out dashboard.csr -subj '/CN=localhost'
openssl x509 -req -sha256 -days 365 -in dashboard.csr -signkey dashboard.key -out dashboard.crt
kubectl -n kube-system create secret generic kubernetes-dashboard-certs --from-file=$HOME/certs

Install dashboard

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml

#Wait and check if the replica set is fulfilled
kubectl -n kube-system get rs

Create PSP

kubectl -n kube-system create -f - <<EOF
apiVersion: extensions/v1beta1
kind: PodSecurityPolicy
metadata:
  name: dashboard
spec:
  privileged: false
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'
EOF

Create role

kubectl -n kube-system create role psp:dashboard --verb=use --resource=podsecuritypolicy --resource-name=dashboard

Bind role and account

kubectl -n kube-system create rolebinding kubernetes-dashboard-policy --role=psp:dashboard --serviceaccount=kube-system:kubernetes-dashboard
kubectl --as=system:serviceaccount:kube-system:kubernetes-dashboard -n kube-system auth can-i use podsecuritypolicy/dashboard

Expose service on NodePort

#Edit the kubernetes-dashboard service and change the following options:
#   spec.type from ClusterIP to NodePort
*   spec.ports[0].nodePort from 32641 to whatever port you want it to be exposed on
kubectl -n kube-system edit service kubernetes-dashboard
kubectl -n kube-system get services
#Configure the dns in the domain provider console
https://[domain]:30104/

Expose Traefik ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-ingress
  namespace: kube-system
spec:
  rules:
    - host: [domain]
      http:
        paths:
          - path: /
            backend:
              serviceName: kubernetes-dashboard
              servicePort: 443

Scenarios

Run a busybox container

kubectl run -i --tty --image busybox test --restart=Never --rm /bin/sh

User docker hub secret in pods

#docker login and get the file: ~/.docker/config.json
docker login
kubectl create secret generic docker-hub --from-file=.dockerconfigjson=~/.docker/config.json --type=kubernetes.io/dockerconfigjson --namespace=test

#Create a Pod that uses the Secret like bellow
kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: test-server
  namespace: test
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: test-server
    spec:
      containers:
        - name: test-server
          image: demo/test-server
      imagePullSecrets:
        - name: docker-hub

Default storage class

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: standard
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
reclaimPolicy: Retain
mountOptions:
  - debug
volumeBindingMode: Immediate

Create MongoDB with two DB auth settings

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  name: dev-mongodb
  namespace: test
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: dev-mongodb
    spec:
      hostname: mongodb
      containers:
        - name: dev-mongodb
          image: hustakin/mongo-auth2:latest
          env:
            - name: MONGODB_ADMIN_USER
              value: admin
            - name: MONGODB_ADMIN_PASS
              value: 111
            - name: MONGODB_APP1_DATABASE
              value: db1
            - name: MONGODB_APP1_USER
              value: user1
            - name: MONGODB_APP1_PASS
              value: pass1
            - name: MONGODB_APP2_DATABASE
              value: db2
            - name: MONGODB_APP2_USER
              value: user2
            - name: MONGODB_APP2_PASS
              value: pass2
          ports:
            - containerPort: 27017
              protocol: TCP
              name: db
          volumeMounts:
            - mountPath: /data/db
              name: dev-mongo-persistent-storage
          livenessProbe:
            exec:
              command:
                - mongo
                - --eval
                - "db.adminCommand('ping')"
            failureThreshold: 3
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 5
          readinessProbe:
            exec:
              command:
                - mongo
                - --eval
                - "db.adminCommand('ping')"
            failureThreshold: 3
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1
      imagePullSecrets:
        - name: docker-hub
      volumes:
        - name: dev-mongo-persistent-storage
          persistentVolumeClaim:
            claimName: dev-local-data-pvc
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: mongodb
  name: mongodb
  namespace: test
  annotations:
    traefik.ingress.kubernetes.io/affinity: "true"
    traefik.ingress.kubernetes.io/session-cookie-name: "sticky"
spec:
  type: ClusterIP
  ports:
    - protocol: TCP
      port: 27017
      name: db
  selector:
    app: dev-mongodb
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: dev-local-data-pv
spec:
  capacity:
    storage: 50Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /mnt/data
  nodeAffinity:
    required:
      nodeSelectorTerms:
        - matchExpressions:
            - key: node
              operator: In
              values:
                - db
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: dev-local-data-pvc
  namespace: test
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 50Gi

Useful commands

Scenarios Commands
Create or update 1. kubectl create -f pod.yml
2. kubectl apply -f pod.yml
Sort top pods in all namespace kubectl top pod -A | sort -rnk 4
Add a label to the specific node kubectl label nodes [node-name] [label]=[value]
List remaining resources in a namespace kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n monitoring
Delete namespace forcely kubectl delete ns heptio-sonobuoy --grace-period=0 --force
Copy the file in a pod to local kubectl cp [pod-name]:/heap_dump.hprof heap_dump.hprof -n test
Check the k8s logs journalctl -f -u kubelet
Delete all evicted pods from all namespaces kubectl get pods --all-namespaces | grep Evicted | awk '{print $2 " --namespace=" $1}' | xargs kubectl delete pod
Delete all containers in ImagePullBackOff state from all namespaces kubectl get pods --all-namespaces | grep 'ImagePullBackOff' | awk '{print $2 " --namespace=" $1}' | xargs kubectl delete pod
Delete all containers in ImagePullBackOff or ErrImagePull or Evicted state from all namespaces kubectl get pods --all-namespaces | grep -E 'ImagePullBackOff|ErrImagePull|Evicted' | awk '{print $2 " --namespace=" $1}' | xargs kubectl delete pod