Cloud Native 33 min read

Build a Highly Available Kubernetes Cluster with HAProxy & Keepalived on CentOS

This step‑by‑step guide shows how to prepare five CentOS 7 machines, configure hostnames, set up password‑less SSH, upgrade the kernel, disable firewalls and swap, tune sysctl, add Docker and Kubernetes repositories, install Docker, install kubeadm/kubelet/kubectl, configure HAProxy and Keepalived for a virtual IP, join master and worker nodes, deploy a flannel network plugin, and finally verify the cluster with an nginx pod and the Kubernetes dashboard.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Build a Highly Available Kubernetes Cluster with HAProxy & Keepalived on CentOS

Installation Requirements

Before starting, the five machines that will form the Kubernetes cluster must meet the following conditions:

Five machines running CentOS 7.5+ (minimal installation)

Hardware: at least 2 GB RAM, 2 vCPU+, 30 GB+ disk space

All machines must have network connectivity to each other and to the Internet

Installation Steps

Roles and IP addresses used in the tutorial:

k8s-lb   192.168.50.100
master1  192.168.50.128
master2  192.168.50.129
master3  192.168.50.130
node1    192.168.50.131
node2    192.168.50.132

2.1 Pre‑processing

Configure hostnames

# hostnamectl set-hostname master1   # repeat for master2, master3, node1, node2

Reload the hostname configuration: # bash Add hosts entries

# cat >> /etc/hosts <<EOF
192.168.50.100 k8s-lb
192.168.50.128 master1
192.168.50.129 master2
192.168.50.130 master3
192.168.50.131 node1
192.168.50.132 node2
EOF

Configure password‑less SSH

# ssh-keygen -t rsa -b 1200
# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master1
# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master2
# ssh-copy-id -i ~/.ssh/id_rsa.pub root@master3
# ssh-copy-id -i ~/.ssh/id_rsa.pub root@node1
# ssh-copy-id -i ~/.ssh/id_rsa.pub root@node2

Upgrade kernel

# yum localinstall -y kernel-lt*
# grub2-set-default 0 && grub2-mkconfig -o /etc/grub2.cfg
# grubby --args="user_namespace.enable=1" --update-kernel="$(grubby --default-kernel)"

Disable firewall and SELinux

# systemctl disable --now firewalld
# setenforce 0
# sed -i 's/enforcing/disabled/' /etc/selinux/config

Disable swap

# swapoff -a
# sed -i.bak 's/^.*centos-swap/#&/' /etc/fstab

Sysctl tuning for Kubernetes

# cat > /etc/sysctl.d/k8s.conf <<EOF
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
fs.may_detach_mounts = 1
vm.overcommit_memory=1
vm.panic_on_oom=0
fs.inotify.max_user_watches=89100
fs.file-max=52706963
fs.nr_open=52706963
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_intvl = 15
net.ipv4.tcp_max_tw_buckets = 36000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_max_orphans = 327680
net.ipv4.tcp_orphan_retries = 3
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.ip_conntrack_max = 65536
net.core.somaxconn = 16384
EOF
# sysctl --system

Configure yum repositories (Alibaba Cloud mirrors)

# mv /etc/yum.repos.d/* /tmp
# curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
# curl -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo

Set timezone and synchronize time

# ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# yum install dnf ntpdate -y
# ntpdate ntp.aliyun.com

2.2 Install Docker

Add Docker yum repository from Alibaba Cloud and install the latest Docker CE:

# curl -o /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# dnf list docker-ce --showduplicates
# dnf install -y docker-ce docker-ce-cli
# systemctl enable --now docker
# docker --version   # verify installation

Configure a local registry mirror for faster image pulls:

# cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": ["https://f1bhsuge.mirror.aliyuncs.com"]
}
EOF
# systemctl restart docker

2.3 Install Kubernetes components

Add the Kubernetes yum repository (Alibaba Cloud mirror) and install a specific version (1.18.6 in the example):

# cat > /etc/yum.repos.d/kubernetes.repo <<EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
EOF
# dnf clean all && dnf makecache
# dnf install -y kubelet-1.18.6 kubeadm-1.18.6 kubectl-1.18.6
# systemctl enable kubelet
Note: kubelet is enabled but not started yet.

2.4 Configure HAProxy + Keepalived for a virtual IP

Install keepalived and haproxy on the three master nodes: # dnf install -y keepalived haproxy HAProxy configuration (saved as /etc/haproxy/haproxy.cfg) – only the relevant sections are shown:

global
  maxconn 2000
  ulimit-n 16384
  log 127.0.0.1 local0 err
  stats timeout 30s

defaults
  log global
  mode http
  option httplog
  timeout connect 5000
  timeout client 50000
  timeout server 50000
  timeout http-request 15s
  timeout http-keep-alive 15s

frontend k8s-master
  bind 0.0.0.0:8443
  bind 127.0.0.1:8443
  mode tcp
  option tcplog
  default_backend k8s-master

backend k8s-master
  mode tcp
  balance roundrobin
  server master1 192.168.50.128:6443 check
  server master2 192.168.50.129:6443 check
  server master3 192.168.50.130:6443 check

Keepalived configuration (saved as /etc/keepalived/keepalived.cfg) sets the virtual IP 192.168.50.100 and priority for each master (100, 99, 98). A health‑check script /etc/keepalived/check_kubernetes.sh monitors the kube‑apiserver process and stops keepalived if the master is down.

#!/bin/bash
function check_kubernetes() {
  for i in {1..5}; do
    apiserver_pid=$(pgrep kube-apiserver)
    if [[ -n $apiserver_pid ]]; then
      return
    else
      sleep 2
    fi
  done
}
check_kubernetes
if [[ $apiserver_pid -eq 0 ]]; then
  /usr/bin/systemctl stop keepalived
  exit 1
else
  exit 0
fi

Enable and start both services on each master:

# systemctl enable --now keepalived haproxy

2.5 Deploy the first master (master1)

Generate the kubeadm init configuration file and edit the important fields (VIP address, API server SANs, pod subnet, etc.).

# kubeadm config print init-defaults > kubeadm-init.yaml
# vi kubeadm-init.yaml   # edit advertiseAddress, controlPlaneEndpoint, podSubnet, etc.

Pre‑pull required images to speed up the init:

# kubeadm config images pull --config kubeadm-init.yaml

Initialize the control plane:

# kubeadm init --config kubeadm-init.yaml --upload-certs

After a successful init, set up the local kubeconfig:

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

2.6 Join the remaining masters

Run the kubeadm join … --control-plane --certificate-key … command printed at the end of the init on master2 and master3, then copy the admin kubeconfig as above.

2.7 Join worker nodes

Run the plain kubeadm join … command (without --control-plane) on node1 and node2.

2.8 Install a network plugin (Flannel)

Because the default GitHub URL is blocked in China, add a hosts entry for raw.githubusercontent.com and download the manifest locally:

# echo "199.232.28.133  raw.githubusercontent.com" >> /etc/hosts
# curl -o kube-flannel.yml https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
# sed -i 's/quay.io/quay-mirror.qiniu.com/g' kube-flannel.yml
# kubectl apply -f kube-flannel.yml

Verify that the flannel pods are running and that all nodes become Ready:

# kubectl get pods -n kube-system | grep flannel
# kubectl get nodes

3 Test the Cluster

Create a simple nginx deployment and expose it as a NodePort service:

# kubectl create deployment nginx --image=nginx
# kubectl expose deployment nginx --port=80 --type=NodePort

Find the assigned NodePort (e.g., 30249) and access the service via the IP of the node where the pod is scheduled.

Deploy the Kubernetes dashboard, modify its Service to type: NodePort (port 30001 in the example), and access it with the admin token generated for the dashboard-admin service account.

# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta8/aio/deploy/recommended.yaml
# edit the Service to add "type: NodePort" and "nodePort: 30001"
# kubectl apply -f recommended.yaml
# kubectl create serviceaccount dashboard-admin -n kube-system
# kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin
# kubectl describe secret -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
Kubernetes dashboard
Kubernetes dashboard
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerKubernetesCluster SetupFlannel
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.