Tools: Report: Setting Up a Production-Ready Kubernetes Cluster on RHEL 9.7

Tools: Report: Setting Up a Production-Ready Kubernetes Cluster on RHEL 9.7

Running Kubernetes on Red Hat Enterprise Linux (RHEL) is a common requirement in enterprise environments, especially in regulated industries where stability, security, and support matter. This guide walks through how to set up a Kubernetes cluster on RHEL 9.7, using a multi-node architecture suitable for production workloads. Architecture Overview A typical production setup includes: etcd (stacked or external) run application workloads Load Balancer (recommended) provides a single endpoint for the API server Each node should have: Step 1: Disable SwapKubernetes requires swap to be disabled. Remove it permanently: Step 2: Configure Kernel Modules and Networking Enable required modules: Set sysctl parameters: Step 3: Configure Hostname and Hosts File Set hostnames on each node: Update /etc/hosts on all nodes: This helps with internal name resolution. Step 4: Install Container Runtime (containerd) Kubernetes no longer supports Docker directly as a runtime. Use containerd. Generate default config: Enable systemd cgroup driver: Step 5: Install Kubernetes Components Step 6: Initialize the Control Plane Run on the first control plane node: If no load balancer exists, you can temporarily use the first node’s IP. After initialization: Step 7: Join Additional Control Plane Nodes Use the join command generated from kubeadm init, including: Step 8: Join Worker Nodes Step 9: Install CNI (Networking) Without a CNI plugin, pods cannot communicate.Popular options: Step 10: Remove Control Plane Taints (Optional for testing) By default, control planes don’t run workloads. For production, leave taints in place. Step 11: Verify Cluster Health All nodes should be Ready. Step 12: Add Load Balancer for Services (Optional) For on-prem environments, you can use: Cluster will fail to initialize. Mismatch between containerd and kubelet causes instability. Ensure required ports are open between nodes. Pods will remain in Pending. You’ll see errors like: Production ConsiderationsFor a real-world deployment: Conclusion

Setting up Kubernetes on RHEL 9.7 gives you: The key is not just getting the cluster running, but designing it for: Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 600;">sudo swapoff -a -weight: 600;">sudo swapoff -a -weight: 600;">sudo swapoff -a -weight: 600;">sudo sed -i '/swap/d' /etc/fstab -weight: 600;">sudo sed -i '/swap/d' /etc/fstab -weight: 600;">sudo sed -i '/swap/d' /etc/fstab free -h swapon --show free -h swapon --show free -h swapon --show cat <<EOF | -weight: 600;">sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF -weight: 600;">sudo modprobe overlay -weight: 600;">sudo modprobe br_netfilter cat <<EOF | -weight: 600;">sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF -weight: 600;">sudo modprobe overlay -weight: 600;">sudo modprobe br_netfilter cat <<EOF | -weight: 600;">sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF -weight: 600;">sudo modprobe overlay -weight: 600;">sudo modprobe br_netfilter cat <<EOF | -weight: 600;">sudo tee /etc/sysctl.d/99-kubernetes.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF -weight: 600;">sudo sysctl --system cat <<EOF | -weight: 600;">sudo tee /etc/sysctl.d/99-kubernetes.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF -weight: 600;">sudo sysctl --system cat <<EOF | -weight: 600;">sudo tee /etc/sysctl.d/99-kubernetes.conf net.bridge.bridge-nf-call-iptables = 1 net.bridge.bridge-nf-call-ip6tables = 1 net.ipv4.ip_forward = 1 EOF -weight: 600;">sudo sysctl --system hostnamectl set-hostname <node-name> hostnamectl set-hostname <node-name> hostnamectl set-hostname <node-name> <IP> controlplane1 <IP> controlplane2 <IP> controlplane3 <IP> worker1 <IP> worker2 <IP> controlplane1 <IP> controlplane2 <IP> controlplane3 <IP> worker1 <IP> worker2 <IP> controlplane1 <IP> controlplane2 <IP> controlplane3 <IP> worker1 <IP> worker2 -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -y containerd -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -y containerd -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -y containerd -weight: 600;">sudo mkdir -p /etc/containerd containerd config default | -weight: 600;">sudo tee /etc/containerd/config.toml -weight: 600;">sudo mkdir -p /etc/containerd containerd config default | -weight: 600;">sudo tee /etc/containerd/config.toml -weight: 600;">sudo mkdir -p /etc/containerd containerd config default | -weight: 600;">sudo tee /etc/containerd/config.toml -weight: 600;">sudo vi /etc/containerd/config.toml -weight: 600;">sudo vi /etc/containerd/config.toml -weight: 600;">sudo vi /etc/containerd/config.toml SystemdCgroup = true SystemdCgroup = true SystemdCgroup = true -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart containerd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable containerd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart containerd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable containerd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart containerd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable containerd cat <<EOF | -weight: 600;">sudo tee /etc/-weight: 500;">yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key EOF cat <<EOF | -weight: 600;">sudo tee /etc/-weight: 500;">yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key EOF cat <<EOF | -weight: 600;">sudo tee /etc/-weight: 500;">yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/ enabled=1 gpgcheck=1 repo_gpgcheck=1 gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key EOF -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -y kubelet kubeadm -weight: 500;">kubectl -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -y kubelet kubeadm -weight: 500;">kubectl -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -y kubelet kubeadm -weight: 500;">kubectl -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable kubelet -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable kubelet -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable kubelet -weight: 600;">sudo kubeadm init \ --control-plane-endpoint "<LOAD_BALANCER_DNS>:6443" \ --upload-certs -weight: 600;">sudo kubeadm init \ --control-plane-endpoint "<LOAD_BALANCER_DNS>:6443" \ --upload-certs -weight: 600;">sudo kubeadm init \ --control-plane-endpoint "<LOAD_BALANCER_DNS>:6443" \ --upload-certs mkdir -p $HOME/.kube -weight: 600;">sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config -weight: 600;">sudo chown $(id -u):$(id -g) $HOME/.kube/config mkdir -p $HOME/.kube -weight: 600;">sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config -weight: 600;">sudo chown $(id -u):$(id -g) $HOME/.kube/config mkdir -p $HOME/.kube -weight: 600;">sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config -weight: 600;">sudo chown $(id -u):$(id -g) $HOME/.kube/config -weight: 500;">kubectl get nodes -weight: 500;">kubectl get nodes -weight: 500;">kubectl get nodes kubeadm join <endpoint>:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane \ --certificate-key <key> kubeadm join <endpoint>:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane \ --certificate-key <key> kubeadm join <endpoint>:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> \ --control-plane \ --certificate-key <key> kubeadm join <endpoint>:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> kubeadm join <endpoint>:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> kubeadm join <endpoint>:6443 \ --token <token> \ --discovery-token-ca-cert-hash sha256:<hash> -weight: 500;">kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml -weight: 500;">kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml -weight: 500;">kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml -weight: 500;">kubectl get pods -n kube-system -weight: 500;">kubectl get pods -n kube-system -weight: 500;">kubectl get pods -n kube-system -weight: 500;">kubectl taint nodes --all node-role.kubernetes.io/control-plane- -weight: 500;">kubectl taint nodes --all node-role.kubernetes.io/control-plane- -weight: 500;">kubectl taint nodes --all node-role.kubernetes.io/control-plane- -weight: 500;">kubectl get nodes -weight: 500;">kubectl get pods -A -weight: 500;">kubectl cluster-info -weight: 500;">kubectl get nodes -weight: 500;">kubectl get pods -A -weight: 500;">kubectl cluster-info -weight: 500;">kubectl get nodes -weight: 500;">kubectl get pods -A -weight: 500;">kubectl cluster-info connection refused to localhost:8080 connection refused to localhost:8080 connection refused to localhost:8080 - Control Plane Nodes (3) - controller manager - etcd (stacked or external) - Worker Nodes - run application workloads - Load Balancer (recommended) - provides a single endpoint for the API server - high availability - fault tolerance - scalability - RHEL 9.7 installed - at least 2 CPUs (4+ recommended) - 4GB RAM minimum (8GB+ recommended) - stable network connectivity - unique hostname - discovery token CA cert hash - certificate key Example: - Flannel Example using Calico: - HAProxy + Keepalived - external hardware load balancer MetalLB allows you to assign IPs to services of type LoadBalancer. - Swap not disabled - Wrong cgroup driver - Firewall issues - Missing CNI - kubeconfig not set - use a load balancer for API server - separate etcd if scale increases - implement monitoring (Prometheus, Grafana) - -weight: 500;">enable logging aggregation - enforce RBAC policies - use TLS everywhere - implement backup strategy for etcd - enterprise-grade stability - full control over infrastructure - flexibility for hybrid or on-prem environments - observability - scalability Once the foundation is solid, you can confidently run critical workloads on top of it.