<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Flannel &#8211; Blog of Kliment Andreev &#8211; A place so I won&#039;t forget things</title>
	<atom:link href="https://blog.andreev.it/tag/flannel/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.andreev.it</link>
	<description></description>
	<lastBuildDate>Tue, 03 Nov 2020 13:55:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	
	<item>
		<title>CentOS: Kubernetes, Flannel and Calico in a single master configuration</title>
		<link>https://blog.andreev.it/2019/04/centos-kubernetes-flannel-and-calico-in-a-single-master-configuration/</link>
					<comments>https://blog.andreev.it/2019/04/centos-kubernetes-flannel-and-calico-in-a-single-master-configuration/#respond</comments>
		
		<dc:creator><![CDATA[Kliment Andreev]]></dc:creator>
		<pubDate>Tue, 09 Apr 2019 15:46:01 +0000</pubDate>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[Calico]]></category>
		<category><![CDATA[centos]]></category>
		<category><![CDATA[docker]]></category>
		<category><![CDATA[Flannel]]></category>
		<guid isPermaLink="false">https://blog.andreev.it/?p=5127</guid>

					<description><![CDATA[Kubernetes (k8s) is getting a lot of attention and it&#8217;s becoming more and more&#8230;]]></description>
										<content:encoded><![CDATA[<div id="bsf_rt_marker"></div><p>Kubernetes (k8s) is getting a lot of attention and it&#8217;s becoming more and more popular even in enterprises that are quite IT conservative. In this post I&#8217;ll explain how to install Kubernetes on a single master and one node on CentOS. Then, we&#8217;ll install the networking plugins (CNI), Flannel or Calico. At the end I&#8217;ll show an example of how to deploy a simple Node.js app and do rollout update and undoing the rollout. </p>
<div style="border:1px solid red; padding:16px;">
<p style="text-align:center;"><strong><span style="color:#800000;">NOTE ABOUT VERSIONS</span> </strong></p>
<p><center>Kubernetes and the surrounding components are changed on a daily basis. What works today, might not work tomorrow.</center></p>
<p><center>This tutorial assumes that you use CentOS 7, Docker 18.x and Kubernetes 1.14.</center></p>
</div>
<p>For this post, there are some pre-requisites. You will need 2 servers with 2 CPUs and at least 2GB RAM. It is also recommended to have a working DNS. If you don&#8217;t have DNS in your lab, make sure you use <strong>/etc/hosts</strong> for hostname resolution, but you can get away if you use IPs only (not recommended). </p>
<h1>Pre-requisites</h1>
<p>On a fresh installed CentOS 7, do these pre-req commands on both the master and the node at the same time. You need to be logged as root.<br />
Make sure SELinux is disabled.</p>
<pre class="brush: bash; title: ; notranslate">
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/selinux/config
</pre>
<p>Kubernetes doesn&#8217;t like swap, so if you have it in <strong>/etc/fstab</strong>, disable the swap.</p>
<pre class="brush: bash; title: ; notranslate">
swapoff -a
sed -i '/ swap / s/^\(.*\)$/#\1/g' /etc/fstab
</pre>
<p>Enable the bridge network module.</p>
<pre class="brush: bash; title: ; notranslate">
modprobe br_netfilter
echo '1' &gt; /proc/sys/net/bridge/bridge-nf-call-iptables
echo '1' &gt; /proc/sys/net/bridge/bridge-nf-call-ip6tables
</pre>
<p>Install Docker and change the cgroup from cfsgroup to systemd.</p>
<pre class="brush: bash; title: ; notranslate">
yum -y install yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum -y install docker-ce
mkdir /etc/docker
cat &lt;&lt;EOF &gt; /etc/docker/daemon.json
{
  &quot;exec-opts&quot;: &#x5B;&quot;native.cgroupdriver=systemd&quot;],
  &quot;log-driver&quot;: &quot;json-file&quot;,
  &quot;log-opts&quot;: {
    &quot;max-size&quot;: &quot;100m&quot;
  },
  &quot;storage-driver&quot;: &quot;overlay2&quot;,
  &quot;storage-opts&quot;: &#x5B;
    &quot;overlay2.override_kernel_check=true&quot;
  ]
}
EOF
mkdir -p /etc/systemd/system/docker.service.d
systemctl daemon-reload
systemctl enable docker &amp;&amp; systemctl start docker
</pre>
<p>Try this line and make sure the output says <strong>systemd</strong>.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
docker info | grep -i cgroup
Cgroup Driver: systemd
</pre>
<p>Add the Kubernetes repo. </p>
<pre class="brush: bash; title: ; notranslate">
cat &lt;&lt;EOF &gt; /etc/yum.repos.d/kubernetes.repo
&#x5B;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
EOF
</pre>
<h1>Master Node</h1>
<p>Open the firewall <a href="https://kubernetes.io/docs/setup/independent/install-kubeadm/#check-required-ports" rel="noopener noreferrer" target="_blank">ports</a>. </p>
<pre class="brush: bash; title: ; notranslate">
firewall-cmd --add-port=6443/tcp --permanent
firewall-cmd --add-port=2379-2380/tcp --permanent
firewall-cmd --add-port=10250-10252/tcp --permanent
firewall-cmd --reload
</pre>
<p>From the repo install kubelet, kubectl and kubeadm and make sure Kubernetes starts on boot.</p>
<pre class="brush: bash; title: ; notranslate">
yum -y install kubelet kubectl kubeadm
systemctl enable kubelet
</pre>
<p>Don&#8217;t start Kubernetes yet. It will fail with a message that it can&#8217;t find a config yaml file. Just initialize the cluster. This will also start the kubelet service. Pick one choice (Flannel or Calico).<br />
<strong>NOTE: This line initializes the cluster to be used for Flannel.</strong> </p>
<pre class="brush: bash; title: ; notranslate">
kubeadm init --pod-network-cidr=10.244.0.0/16
</pre>
<p><strong>NOTE: This line initializes the cluster to be used for Calico.</strong> </p>
<pre class="brush: bash; title: ; notranslate">
kubeadm init --pod-network-cidr=192.168.0.0/16
</pre>
<p>Look at the bottom of the output. You should see something like this. Lines 5,6,7 and 15 and 16 are important. </p>
<pre class="brush: plain; highlight: [5,6,7,15,16]; title: ; notranslate">
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run &quot;kubectl apply -f &#x5B;podnetwork].yaml&quot; with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.1.175:6443 --token 0z3iov.28rz29hxw9ft4jmg \
    --discovery-token-ca-cert-hash sha256:782d37b5c870ebebd2f17cc3ba1424f305e6a3e293afc04fc2030edfab6bf4b0
</pre>
<p>This means that the cluster initialized OK.<br />
Check the status of both Docker and Kubernetes.</p>
<pre class="brush: bash; title: ; notranslate">
systemctl status docker | grep Active
systemctl status kubelet | grep Active
</pre>
<p>Make sure they are both running. Check <strong>/var/log/messages</strong> if you have any issues.</p>
<h1>Nodes (workers)</h1>
<p>On the worker nodes, make sure you do the same as you did on the master (swap, SELinux, Docker) except that you don&#8217;t have to install kubectl. </p>
<pre class="brush: bash; title: ; notranslate">
yum -y install kubelet kubeadm
systemctl enable kubelet
</pre>
<p>Open the firewall <a href="https://kubernetes.io/docs/setup/independent/install-kubeadm/#check-required-ports" rel="noopener noreferrer" target="_blank">ports</a>. </p>
<pre class="brush: bash; title: ; notranslate">
firewall-cmd --add-port=10250/tcp --permanent
firewall-cmd --add-port=30000-32767/tcp --permanent
firewall-cmd --reload
</pre>
<p>Now, you can join the cluster. Use the command that was the output from the <strong>kubeadm init</strong> on the master (see above &#8211; lines 15 and 16).</p>
<pre class="brush: bash; title: ; notranslate">
kubeadm join 192.168.1.175:6443 --token 0z3iov.28rz29hxw9ft4jmg \
    --discovery-token-ca-cert-hash sha256:782d37b5c870ebebd2f17cc3ba1424f305e6a3e293afc04fc2030edfab6bf4b0
</pre>
<p>That&#8217;s how you join nodes to the master. Replace <strong>192.168.1.175</strong> with the IP or hostname of your master node. If everything is OK, you&#8217;ll see something like this.</p>
<pre class="brush: bash; title: ; notranslate">
This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.
</pre>
<h1>Kubernetes user</h1>
<p>While still logged as root on the master, create the Kubernetes user that you will use for managing the k8s cluster. In my case, I&#8217;ll create a user called k8s with <strong>secret </strong>as password.</p>
<pre class="brush: bash; title: ; notranslate">
useradd k8s -g docker
usermod -aG wheel k8s
echo -e &quot;secret\nsecret&quot; | passwd k8s
</pre>
<p>Log as this user (k8s) and execute these commands. These lines were also an output of the <strong>kubeadm init</strong> command above (5,6 and 7). </p>
<pre class="brush: bash; title: ; notranslate">
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
</pre>
<p>If you want to add another user to manage the Kubernetes cluster, make sure you execute these 3 lines above for that user. Check if everything looks good.</p>
<pre class="brush: bash; title: ; notranslate">
docker ps
</pre>
<p>You should see a bunch of Kubernetes system containers running (etcd, scheduler, API server).<br />
Then check the nodes.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get nodes
NAME                      STATUS     ROLES    AGE   VERSION
k8smaster.andreev.local   NotReady   master   12m   v1.14.0
k8snode1.andreev.local    NotReady   &lt;none&gt;   10m   v1.14.0
</pre>
<p>The reason the master and the node are not ready is because we don&#8217;t have a network for the cluster. </p>
<h1>Network CNI</h1>
<p>Depending on how you&#8217;ve initialized the cluster, pick one of the network plugins (Flannel or Calico). </p>
<h2>Flannel</h2>
<p>For the network to work, we&#8217;ll have to use one of the CNI plugins. There are many, Flannel, Weave Net, Calico etc.<br />
Let&#8217;s install Flannel. Do this on the master only logged as k8s user. The master will take care of the nodes.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
</pre>
<p>Start this little infinite loop and you&#8217;ll see that after 20-30 seconds, both the master and the node will change their status to <strong>Ready</strong>. Hit Ctrl-C to end.</p>
<pre class="brush: bash; title: ; notranslate">
while true
do
kubectl get nodes
sleep 3
done
</pre>
<p>Now, you have a fully working cluster ready. </p>
<h2>Calico</h2>
<p>For the network to work, we&#8217;ll have to use one of the CNI plugins. There are many, Flannel, Weave Net, Calico etc.<br />
Let&#8217;s install Flannel. Do this on the master only logged as k8s user. The master will take care of the nodes.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/rbac-kdd.yaml
kubectl apply -f https://docs.projectcalico.org/v3.3/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml
</pre>
<p>Start this little infinite loop and you&#8217;ll see that after 20-30 seconds, both the master and the node will change their status to <strong>Ready</strong>. Hit Ctrl-C to end.</p>
<pre class="brush: bash; title: ; notranslate">
while true
do
kubectl get nodes
sleep 3
done
</pre>
<p>Now, you have a fully working cluster ready. </p>
<h1>Deployment</h1>
<p>In this example, I&#8217;ll create a small container that runs a Node.js app that when run it will display &#8220;Hello from &#8221; the hostname of the container. On top of that we&#8217;ll create a load balancer, so we can see how that works.<br />
First, let&#8217;s create the container based on Node.js image. Create a file named <strong>Dockerfile </strong>with this content. </p>
<pre class="brush: bash; title: ; notranslate">
FROM node:latest
LABEL maintainer &quot;kliment@andreev.it&quot;
ADD appv1.js /app.js
ENTRYPOINT &#x5B;&quot;node&quot;, &quot;app.js&quot;]
</pre>
<p>This is our application. Save it as <strong>appv1.js</strong>.</p>
<pre class="brush: xml; title: ; notranslate">
const http = require('http');
const os = require('os');
const port = 3000;

const server = http.createServer((req, res) =&gt; {
  res.statusCode = 200;
  res.end('Hello from ' + os.hostname() + '\n');
});

server.listen(port);
</pre>
<p>Create the container. You&#8217;ll need a valid Docker Hub login. In my case, my username is klimenta. Replace it with yours.</p>
<pre class="brush: bash; title: ; notranslate">
docker build -t klimenta/appv1:latest .
</pre>
<p>It&#8217;s time to login to Docker Hub and upload the image there. You&#8217;ll be prompted for a username and password.</p>
<pre class="brush: bash; title: ; notranslate">
docker login
</pre>
<p>Upload the image.</p>
<pre class="brush: bash; title: ; notranslate">
docker push klimenta/appv1:latest
</pre>
<p>Create a Kubernetes deployment file named <strong>deployment.yaml</strong>.</p>
<pre class="brush: xml; title: ; notranslate">
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: appv1
spec:
  replicas: 3
  template:
    metadata:
      name: appv1
      labels:
        app: appv1
    spec:
      containers:
      - image: klimenta/appv1:latest
        name: nodejs
---
apiVersion: v1
kind: Service
metadata:
  name: loadbalancer
spec:
  type: LoadBalancer
  selector:
    app: appv1
  ports:
  - port: 80
    targetPort: 3000
</pre>
<p>We are creating a deployment with 3 replicas and a load balancer that listens on port 80 and sends the traffic to port 3000 on the pods with our application.<br />
Create the deployment and the load balanced service.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl create -f deployment.yaml
</pre>
<p>After about 30 seconds, you&#8217;ll see that your pods are ready. </p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
appv1-596dd64666-4k7qn   1/1     Running   0          93m
appv1-596dd64666-gn5gr   1/1     Running   0          93m
appv1-596dd64666-vv9h5   1/1     Running   0          93m
</pre>
<p>The load balancer is also ready. </p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
kubectl get svc
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes     ClusterIP      10.96.0.1       &lt;none&gt;        443/TCP        102m
loadbalancer   LoadBalancer   10.111.212.41   &lt;pending&gt;     80:30310/TCP   94m
</pre>
<p>If you hit the load balancer, you&#8217;ll see a response. Replace the IP with yours.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
curl http://10.111.212.41
Hello from appv1-596dd64666-gn5gr
</pre>
<p>Now, let&#8217;s say that we created a new version of our application. Copy appv1.js as appv2.js and change  <strong>appv2.js</strong> a little bit.</p>
<pre class="brush: bash; title: ; notranslate">
cp appv1.js appv2.js
</pre>
<p>The appv2.js should look like this.</p>
<pre class="brush: xml; highlight: [7]; title: ; notranslate">
const http = require('http');
const os = require('os');
const port = 3000;

const server = http.createServer((req, res) =&gt; {
  res.statusCode = 200;
  res.end('Greetings from ' + os.hostname() + '\n');
});

server.listen(port);
</pre>
<p>Change the <strong>Dockerfile </strong>to look like this.</p>
<pre class="brush: bash; title: ; notranslate">
FROM node:latest
LABEL maintainer &quot;kliment@andreev.it&quot;
ADD appv2.js /app.js
ENTRYPOINT &#x5B;&quot;node&quot;, &quot;app.js&quot;]
</pre>
<p>Build the new image and upload it to Docker Hub.</p>
<pre class="brush: bash; title: ; notranslate">
docker build -t klimenta/appv2:latest .
docker push klimenta/appv2:latest
</pre>
<p>Deploy the new application.</p>
<pre class="brush: bash; title: ; notranslate">
kubectl set image deployment appv1 nodejs=klimenta/appv2:latest
</pre>
<p>If you check the app now, you&#8217;ll see that it reflects the new version.</p>
<pre class="brush: bash; highlight: [1]; title: ; notranslate">
curl http://10.111.212.41
Greetings from appv1-5d949774f5-b794k
</pre>
<p>But what if there is a bug in our application and we want to revert it back to the initial one? Easy.</p>
<pre class="brush: bash; highlight: [1,3]; title: ; notranslate">
kubectl rollout undo deployment appv1
deployment.extensions/appv1 rolled back
curl http://10.111.212.41
Hello from appv1-596dd64666-94gqh
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.andreev.it/2019/04/centos-kubernetes-flannel-and-calico-in-a-single-master-configuration/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
