Thursday, 6 August 2015

Google kubernetes test drive

Setting up networking

Install openvswitch on both nodes (master and minion), good instruction for Centos 7 can be found here: openvswitch on centos 7
Assuming master node has IP:  159.107.152.121 and minion node has 159.107.152.161, and both can reach each other over these addresses.


Master node:

Edit /etc/sysconfig/docker-network and add option "--iptables=false --ip-masq=false --mtu=1400"
yum -y update && yum -y install net-tools bridge-utils
setenforce 0
service firewalld stop
service docker stop
ifconfig docker0 down
brctl delbr docker0
brctl addbr docker0
ifconfig docker0 10.244.1.1/16 mtu 1400 up
service docker start
ovs-vsctl add-br br0
ifconfig br0 mtu 1400
ovs-vsctl add-port br0 vx0 -- set interface vx0 \
type=vxlan options:remote_ip=159.107.152.161
ifconfig br0 up
brctl addif docker0 br0
route add -net 10.244.0.0/16 dev docker0

Minion node: 

Edit /etc/sysconfig/docker-network and add option "--iptables=false --ip-masq=false --mtu=1400"
setenforce 0
service firewalld stop
service docker stop
ifconfig docker0 down
brctl delbr docker0
brctl addbr docker0
ifconfig docker0 10.244.2.1/16 mtu 1400 up
service docker start
ovs-vsctl add-br br0
ifconfig br0 mtu 1400
ovs-vsctl add-port br0 vx0 -- set interface vx0 \
type=vxlan options:remote_ip=159.107.152.121
ifconfig br0 up
brctl addif docker0 br0
route add -net 10.244.0.0/16 dev docker0

Starting kubernetes on master node

On master node, run as root:
DOCKERIP="10.244.1.1"
service docker stop
docker -d -H \
unix:///var/run/docker-bootstrap.sock -p /var/run/docker-bootstrap.pid \
--iptables=false --ip-masq=false --bridge=none \
--graph=/var/lib/docker-bootstrap 2> \
/var/log/docker-bootstrap.log 1> /dev/null &

docker -H \
unix:///var/run/docker-bootstrap.sock run --net=host \
-d gcr.io/google_containers/etcd:2.0.9 /usr/local/bin/etcd \
--addr=127.0.0.1:4001 --bind-addr=0.0.0.0:4001 \
--data-dir=/var/etcd/data

docker -H \
unix:///var/run/docker-bootstrap.sock run \
--net=host gcr.io/google_containers/etcd:2.0.9 \
etcdctl set /coreos.com/network/config \
'{ "Network": "10.244.0.0/16" }'

service docker start
docker run --net=host -d -v /var/run/docker.sock:/var/run/docker.sock  \
gcr.io/google_containers/hyperkube:v1.0.1 /hyperkube kubelet \
--api_servers=http://localhost:8080 --v=2 --address=$DOCKERIP \
--enable_server --hostname_override=$DOCKERIP \
--config=/etc/kubernetes/manifests-multi

docker run -d --net=host --privileged gcr.io/google_containers/hyperkube:v1.0.1 \
/hyperkube proxy --master=http://127.0.0.1:8080 --v=2

Verify master node is up and running

Download kubectl from here Verify with ./kubectl get nodes should show only one:
[root@localhost ~]# ./kubectl -s 10.244.1.1:8080 get nodes
NAME         LABELS                              STATUS
10.244.1.1   kubernetes.io/hostname=10.244.1.1   Ready

Setting up worker node(s)

Now, on worker nodes do:
MASTER_DOCKER_IP=10.244.1.1
WORKER_DOCKER_IP=10.244.2.1
docker run --net=host -d -v /var/run/docker.sock:/var/run/docker.sock \
gcr.io/google_containers/hyperkube:v1.0.1 /hyperkube kubelet \
--api_servers=http://$MASTER_DOCKER_IP:8080 --v=2 \
--address=$WORKER_DOCKER_IP --enable_server \
--hostname_override=$WORKER_DOCKER_IP

#and service proxy:
docker run -d --net=host --privileged \
gcr.io/google_containers/hyperkube:v1.0.1 \
/hyperkube proxy --master=http://$MASTER_DOCKER_IP:8080 --v=2

Verify master and minion working

Now we should have two nodes, verified from master:
[root@localhost ~]# ./kubectl  get nodes
NAME         LABELS                              STATUS
10.244.1.1   kubernetes.io/hostname=10.244.1.1   Ready
10.244.2.1   kubernetes.io/hostname=10.244.2.1   Ready
and verified from slave (note -s $MASTER_IP):
[root@localhost ~]# ./kubectl -s 10.244.1.1:8080 get nodes
NAME         LABELS                              STATUS
10.244.1.1   kubernetes.io/hostname=10.244.1.1   Ready
10.244.2.1   kubernetes.io/hostname=10.244.2.1   Ready

Create simple application using nginx

Run application, from master:
./kubectl -s http://localhost:8080 run nginx --image=nginx --port=80
./kubectl expose rc nginx --port=80
###few seconds later:
./kubectl get pods
###shows pods running with ip**

Scale application up and down:

./kubectl scale rc nginx --replicas=3
#after few seconds
./kubectl get pods will show new pods
#and downscale 
./kubectl scale rc nginx --replicas=1
Deleting nginx we created above:
./kubectl delete rc nginx
./kubectl delete svc nginx

Creating application and service using yaml files

[root@localhost ~]# cat nginxrc.yaml 
apiVersion: v1
kind: ReplicationController
metadata:
  name: my-nginx
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80

##and run with: 
./kubectl -s $MASTER_DOCKER_IP:8080 create -f nginxrc.yaml
##then we can create service as well:
[root@localhost ~]# cat nginxsvc.yaml 
apiVersion: v1
kind: Service
metadata:
  name: nginxsvc
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    app: nginx

##and run it with:
./kubectl -s $MASTER_DOCKER_IP:8080 create -f nginxsvc.yaml
##after a while results can be seen with:
./kubectl describe svc nginxsvc

Adding DNS support

I will be setting up dns service on 10.0.0.10 for dev.deki.local domain. This has three steps, creation of skydns service, creation of skydns rc and restarting our master and minions to use dns, since example above was without DNS add on.
Create skydns-svc.yaml as shown bellow:
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.0.0.10"
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP
and skydns-rc.yaml as shown bellow (make sure to replace $MASTER_IP with IP address of docker on your master node, in my example 10.244.1.1):
apiVersion: v1
kind: ReplicationController
metadata:
  name: kube-dns-v8
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    version: v8
    kubernetes.io/cluster-service: "true"
spec:
  replicas: 1
  selector:
    k8s-app: kube-dns
    version: v8
  template:
    metadata:
      labels:
        k8s-app: kube-dns
        version: v8
        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"
        - -kube_master_url=http://$MASTER_IP:8080
        - -domain=dev.deki.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=dev.deki.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
      - name: healthz
        image: gcr.io/google_containers/exechealthz:1.0
        resources:
          limits:
            cpu: 10m
            memory: 20Mi
        args:
        - -cmd=nslookup kubernetes.default.svc.dev.deki.local localhost >/dev/null
        - -port=8080
        ports:
        - containerPort: 8080
          protocol: TCP
      volumes:
      - name: etcd-storage
        emptyDir: {}
      dnsPolicy: Default  # Don't use cluster DNS.
So now, lets kill master and minions and start them up again:
#Run this on master and all minions:
docker ps -a | egrep -v "STATUS" | \
awk '{print $1}' | xargs docker kill 
docker ps -a | grep "Exited" | \
awk '{print $1}' | xargs docker rm -v
This will clear master and minions, so lets start them up again, this time using dns domain and dns ip:
###On master####
docker run --net=host -d -v /var/run/docker.sock:/var/run/docker.sock  \
gcr.io/google_containers/hyperkube:v1.0.1 /hyperkube kubelet \
--api_servers=http://localhost:8080 --v=2 --address=$DOCKERIP \
--enable_server --cluster-dns=10.0.0.10 \
--cluster-domain=dev.deki.local \
--hostname_override=$DOCKERIP \
--config=/etc/kubernetes/manifests-multi

docker run -d --net=host --privileged gcr.io/google_containers/hyperkube:v1.0.1 \
/hyperkube proxy --master=http://127.0.0.1:8080 --v=2

####And on minion####
MASTER_DOCKER_IP=10.244.1.1
WORKER_DOCKER_IP=10.244.2.1
docker run --net=host -d -v /var/run/docker.sock:/var/run/docker.sock \
gcr.io/google_containers/hyperkube:v1.0.1 /hyperkube kubelet \
--api_servers=http://$MASTER_DOCKER_IP:8080 --v=2 \
--address=$WORKER_DOCKER_IP --enable_server \
--cluster-dns=10.0.0.10 \
--cluster-domain=dev.deki.local \
--hostname_override=$WORKER_DOCKER_IP

#and service proxy on minion:
docker run -d --net=host --privileged \
gcr.io/google_containers/hyperkube:v1.0.1 \
/hyperkube proxy --master=http://$MASTER_DOCKER_IP:8080 --v=2

### Finally start dns ###
./kubectl create -f skydns-svc.yaml
./kubectl create -f skydns-rc.yaml
Few moments later dns should be up and running. Using example of nginx service above, we can verify it's working by doing:
dig @10.0.0.10 nginxsvc.default.svc.dev.deki.local

Wednesday, 5 August 2015

Openstack kilo playing nicely with docker

This has been tested on Centos 7, Ubuntu should be absolutely same thing. Start up virtual machine, I am running Centos 7 (6 cores + 8Gigs of RAM assinged) through parallels on mac, smaller VM will work too.
To make things simpler, I will disable selinux on Centos 7 and make Dan Walsh cry, so either run setenforce 0, or edit /etc/sysconfig/selinux (set to disabled and reboot).
#Update system before we start
sudo yum install epel-release
sudo yum -y update
sudo yum -y install python-pip python-devel git openssh-server
sudo yum groupinstall "Development Tools"
sudo setenforce 0
sudo mkdir /opt/stack && chown deki:deki /opt/stack/ -R
git clone https://git.openstack.org/stackforge/nova-docker /opt/stack/nova-docker
git clone https://git.openstack.org/openstack-dev/devstack /opt/stack/devstack
#Switch to kilo stable branch
cd /opt/stack/nova-docker && git checkout -b kilo origin/stable/kilo
cd /opt/stack/devstack && git checkout -b kilo origin/stable/kilo
cd /opt/stack/nova-docker
./contrib/devstack/prepare_devstack.sh
Now the important bit, as stack.sh will not work without it:
Edit file: /opt/stack/nova-docker/novadocker/virt/docker/driver.py
Change:
from nova.openstack.common import fileutils to from oslo_utils import fileutils
To simplify further, I will not make any other modifications to devstack, so
cd /opt/stack/devstack 
#Run stack.sh and have a cup of coffee
./stack.sh
It should complete and show something like:
##This is your host IP address: 10.211.55.34
##2015-08-05 09:40:07.103 | Skip setting lvm filters for non Ubuntu systems
##This is your host IPv6 address: ::1
##Horizon is now available at http://10.211.55.34/
##Keystone is serving at http://10.211.55.34:5000/
##The default users are: admin and demo
##The password: mysecretpassword
##2015-08-05 09:40:07.108 | stack.sh completed in 278 seconds.
In my case I had to restart firewall, not sure yet why, so do:
sudo service firewalld status
#
#If it shows:
#Loaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled)
#   Active: inactive (dead)
#run:
sudo service firewalld start
So lets verify all is well:
docker version
# Lets try it out:
. openrc admin
docker pull larsks/thttpd
docker save larsks/thttpd | glance image-create \
--name larsks/thttpd \
--is-public true \
--container-format docker \
--disk-format raw

. openrc demo
nova boot --image larsks/thttpd \
--flavor m1.small test0

#Wait a bit and nova list will show:
[deki@localhost devstack]$ nova list
+--------------------------------------+-------+--------+------------+-------------+------------------+
| ID                                   | Name  | Status | Task State | Power State | Networks         |
+--------------------------------------+-------+--------+------------+-------------+------------------+
| 83678e40-78f9-4d41-90d0-c8dbd75c54b9 | test0 | ACTIVE | -          | Running     | private=10.0.0.2 |
+--------------------------------------+-------+--------+------------+-------------+------------------+

#then:
[deki@localhost devstack]$ nova floating-ip-create
+----+------------+-----------+----------+--------+
| Id | IP         | Server Id | Fixed IP | Pool   |
+----+------------+-----------+----------+--------+
| 1  | 172.24.4.1 | -         | -        | public |
+----+------------+-----------+----------+--------+

nova floating-ip-associate test0 172.24.4.1
#and finally try it out and behold:
curl http://172.24.4.1
#will return bellow:


             
  Your web server is working
    
 
 
  
  
  

This is a statically compiled version of thttpd put together to build a demonstration container for my Heat templates for Kubernetes. But maybe you'll find it useful for other things.


Tuesday, 4 August 2015

Multihost dns with skydns v1 and skydock

Skydns

Start skydns on one of the hosts, for example host1:
docker run -d --name skydns \
-p 53:53/udp \
-p 8080:8080/tcp \
crosbymichael/skydns:latest \
-nameserver="8.8.8.8:53" \
-domain="deki.local"
If running on Centos 7 use IP with port bind:
docker run -d --name skydns \
-p $HOSTIP:53:53/udp \
-p $HOSTIP:8080:8080/tcp \
crosbymichael/skydns:latest \
-nameserver="8.8.8.8:53" \
-domain="deki.local"
where $HOSTIP is the ip address of host1.

Skydock

Skydock will run on both hosts , host1 and host2. On run this on both hosts:
docker run -d -v /var/run/docker.sock:/docker.sock \
--name skydock crosbymichael/skydock \
-ttl 30 -environment dev -s /docker.sock \
-domain "deki.local" \
-skydns="http://$DNS_IP:8080/"
In this case since we run skydns on host1, $DNS_IP is ip address of host1. In this setup we have one DNS on host1, and skydock running on both. Each time container is started/stopped/destroyed skydock on that node will update DNS records for us. This setup can be further improved by running multiple DNS servers and multiple instances of skydock for higher availability.