Step-by-Step Guide to Installing Kubernetes v1.16.0 on CentOS 7 with Docker and Flannel
This article provides a detailed, step‑by‑step tutorial for installing Kubernetes v1.16.0 on CentOS 7 virtual machines, covering Docker‑CE installation, prerequisite system configuration, master and node setup, flannel network plugin deployment, and includes all necessary command‑line snippets and the full kube‑flannel.yml manifest.
The author encountered repeated errors when trying to install Kubernetes v1.16.2, so they downgraded to v1.16.0 and documented the successful installation process to help others avoid the same pitfalls.
Prerequisites
All machines must run Docker, have at least 2 CPU cores and 2 GB RAM, and be able to ping each other. The author used Windows 10 with VirtualBox, running CentOS 7 (kernel 3.10.0‑957.el7.x86_64). Master IP: 192.168.99.104, Node IP: 192.168.99.105.
1. Install Docker‑CE 18.09.9 on All Machines
# Install Docker prerequisites
yum install -y yum-utils device-mapper-persistent-data lvm2
# Add Alibaba Cloud Docker repo
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Install specific Docker version
yum install -y docker-ce-18.09.9-3.el7
# Enable and start Docker
systemctl enable docker && systemctl start docker2. Set Kubernetes Environment Preconditions
# Disable firewall
systemctl disable firewalld
systemctl stop firewalld
# Disable SELinux temporarily and permanently
setenforce 0
sed -i 's/SELINUX=permissive/SELINUX=disabled/' /etc/sysconfig/selinux
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
# Turn off swap
swapoff -a
sed -i 's/.*swap.*/#&/' /etc/fstab
# Configure kernel parameters for bridge networking
cat <
/etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system3. Install Kubernetes v1.16.0 Master Node
First ensure Docker is installed and the environment prerequisites are applied.
3.1 Install kubeadm, kubelet, kubectl
# Add Alibaba Cloud Kubernetes repo
cat <
/etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
# Install specific version packages
yum install -y kubeadm-1.16.0-0 kubelet-1.16.0-0 kubectl-1.16.0-0
# Enable and start kubelet
systemctl enable kubelet && systemctl start kubelet3.2 Initialize the Cluster
The following command pulls required Docker images from the Alibaba Cloud registry and initializes the control plane using the master IP (192.168.99.104). It may pause at a pre‑flight check for about two minutes.
# Initialize with custom image repository and pod network CIDR
kubeadm init \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.16.0 \
--apiserver-advertise-address 192.168.99.104 \
--pod-network-cidr=10.244.0.0/16 \
--token-ttl 0After successful initialization, run the commands below to 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/config3.3 Save the Node Join Command
When kubeadm init finishes, it prints a kubeadm join command. You can also retrieve it later with:
kubeadm token create --print-join-command4. Install Kubernetes v1.16.0 Worker Node
Repeat Docker installation and environment preparation on each worker node, then install kubeadm and kubelet:
# Add Alibaba Cloud repo (same as master)
cat <
/etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
# Install packages
yum install -y kubeadm-1.16.0-0 kubelet-1.16.0-0
# Enable and start kubelet
systemctl enable kubelet && systemctl start kubeletJoin the cluster using the command obtained from the master, for example:
kubeadm join 192.168.99.104:6443 \
--token ncfrid.7ap0xiseuf97gikl \
--discovery-token-ca-cert-hash sha256:47783e9851a1a517647f1986225f104e81dbfd8fb256ae55ef6d68ce9334c6a25. Install Flannel Network Plugin on the Master
After the master and nodes are up, the master may still show NotReady . Install flannel to provide pod networking.
5.1 Create Modified kube‑flannel.yml
The original file from the official repository references quay.io , which is blocked in China. The author replaced it with the mirror quay-mirror.qiniu.com and placed the full manifest in the appendix.
5.2 Apply the Manifest
kubectl apply -f kube-flannel.yml6. Completion
Running kubectl get nodes now shows both master and worker nodes in the Ready state, indicating a successful Kubernetes cluster deployment.
Appendix: Full kube‑flannel.yml Manifest
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
privileged: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
allowedHostPaths:
- pathPrefix: "/etc/cni/net.d"
- pathPrefix: "/etc/kube-flannel"
- pathPrefix: "/run/flannel"
readOnlyRootFilesystem: false
runAsUser:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
allowedCapabilities: ['NET_ADMIN']
defaultAddCapabilities: []
requiredDropCapabilities: []
hostPID: false
hostIPC: false
hostNetwork: true
hostPorts:
- min: 0
max: 65535
seLinux:
rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel
rules:
- apiGroups: ['extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['psp.flannel.unprivileged']
- apiGroups: ['']
resources: ['pods']
verbs: ['get']
- apiGroups: ['']
resources: ['nodes']
verbs: ['list', 'watch']
- apiGroups: ['']
resources: ['nodes/status']
verbs: ['patch']
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {"portMappings": true}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {"Type": "vxlan"}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds-amd64
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: beta.kubernetes.io/os
operator: In
values: ["linux"]
- key: beta.kubernetes.io/arch
operator: In
values: ["amd64"]
hostNetwork: true
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni
image: quay-mirror.qiniu.com/coreos/flannel:v0.11.0-amd64
command: ["cp"]
args: ["-f", "/etc/kube-flannel/cni-conf.json", "/etc/cni/net.d/10-flannel.conflist"]
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: quay-mirror.qiniu.com/coreos/flannel:v0.11.0-amd64
command: ["/opt/bin/flanneld"]
args: ["--ip-masq", "--kube-subnet-mgr"]
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
volumes:
- name: run
hostPath:
path: /run/flannel
- name: cni
hostPath:
path: /etc/cni/net.d
- name: flannel-cfg
configMap:
name: kube-flannel-cfg
---
# (Additional DaemonSet definitions for arm64, arm, ppc64le, s390x follow the same pattern with appropriate image tags)Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.