Overview

This document entails steps and procedures to install k8s in an offline environment without internet access, whether in the cloud (public or private), or on bare-metal hardware.

System Parameters/Versions Used for this Example:

  • Rocky Linux: 8.10
  • K8s: 1.29.12
  • Containerd: 1.7.23
  • Docker: 27.2.0

Tools you will need

  • One computer/system with containerd/docker installed and internet access.
    • recommend to use a system with the same chipset as installation system (i.e. AMD64)
  • Destination offline system for installation (no internet access).

Prerequisites

The following packages/dependencies should already be completed:
  • base operating system (OS) updated and patched
  • containerd or docker installed (both on-line and off-line systems)
  • firewalld installed

Prepare K8S Images/Binaries

System with Internet Access

Download all required binaries/images. Perform the following steps from the system with internet access. This example uses a linux machine.

Step 1: Set Version

Set desired K8S version:
export K8S_VERSION=v1.29

Step 2: Download K8S Packages

Download all K8S packages for installation on Rocky 8. Set the repository:
# set repository and repo
cat <<EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://pkgs.k8s.io/core:/stable:/$K8S_VERSION/rpm/
enabled=1
gpgcheck=1
gpgkey=https://pkgs.k8s.io/core:/stable:/$K8S_VERSION/rpm/repodata/repomd.xml.key
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
EOF
Pull packages, saved in /k8s directory:
sudo dnf install -y --downloadonly --downloaddir=/k8s kubelet kubeadm kubectl --disableexcludes=kubernetes
Compress directory for easier transfer:
tar -zcvf /k8s.tar.gz /k8s

Step 3 (optional): Determine Dependencies

Required dependencies are already listed in the following step, but can verify if necessary. Check which version of K8S was downloaded:
ls /k8s | grep kubeadm
Output should look similar to the following:
kubeadm-1.29.12-150500.1.1.x86_64.rpm
In this example, using v1.29.12. Set variable for kubeadm version:
export KUBEADM_VERSION=v1.29.12
Download kubeadm binary, make executable, and move to bin:
# download
curl -L https://dl.k8s.io/release/$KUBEADM_VERSION/bin/linux/amd64/kubeadm -o kubeadm_$KUBEADM_VERSION

# move binary
sudo mv kubeadm_$KUBEADM_VERSION /usr/local/bin/kubeadm

# set permissions to execute
sudo chmod +x /usr/local/bin/kubeadm
List dependencies:
kubeadm config images list --kubernetes-version=$KUBEADM_VERSION
Output should be similar to the following:
registry.k8s.io/kube-apiserver:v1.29.12
registry.k8s.io/kube-controller-manager:v1.29.12
registry.k8s.io/kube-scheduler:v1.29.12
registry.k8s.io/kube-proxy:v1.29.12
registry.k8s.io/coredns/coredns:v1.11.1
registry.k8s.io/pause:3.9
registry.k8s.io/etcd:3.5.16-0

Step 4: Prepare Dependency Images

Pull all required images, can use Docker or containerd. Using Docker:
docker pull --platform=linux/amd64 registry.k8s.io/kube-apiserver:v1.29.12
docker pull --platform=linux/amd64 registry.k8s.io/kube-controller-manager:v1.29.12
docker pull --platform=linux/amd64 registry.k8s.io/kube-scheduler:v1.29.12
docker pull --platform=linux/amd64 registry.k8s.io/kube-proxy:v1.29.12
docker pull --platform=linux/amd64 registry.k8s.io/coredns/coredns:v1.11.1
docker pull --platform=linux/amd64 registry.k8s.io/pause:3.9
docker pull --platform=linux/amd64 registry.k8s.io/etcd:3.5.16-0
Save the images as compressed files so that they can be transferred to the offline system:
docker save -o kube-apiserver_v1.29.12.tar registry.k8s.io/kube-apiserver:v1.29.12
docker save -o kube-controller-manager_v1.29.12.tar registry.k8s.io/kube-controller-manager:v1.29.12
docker save -o kube-scheduler_v1.29.12.tar registry.k8s.io/kube-scheduler:v1.29.12
docker save -o kube-proxy_v1.29.12.tar registry.k8s.io/kube-proxy:v1.29.12
docker save -o coredns_v1.11.1.tar registry.k8s.io/coredns/coredns:v1.11.1
docker save -o pause_3.9.tar registry.k8s.io/pause:3.9
docker save -o etcd_3.5.16-0.tar registry.k8s.io/etcd:3.5.16-0
Using containerd:
ctr image pull --platform=linux/amd64 registry.k8s.io/kube-apiserver:v1.29.12
ctr image pull --platform=linux/amd64 registry.k8s.io/kube-controller-manager:v1.29.12
ctr image pull --platform=linux/amd64 registry.k8s.io/kube-scheduler:v1.29.12
ctr image pull --platform=linux/amd64 registry.k8s.io/kube-proxy:v1.29.12
ctr image pull --platform=linux/amd64 registry.k8s.io/coredns/coredns:v1.11.1
ctr image pull --platform=linux/amd64 registry.k8s.io/pause:3.9
ctr image pull --platform=linux/amd64 registry.k8s.io/etcd:3.5.16-0
Save the images as compressed files so that they can be transferred to the offline system:
ctr image export --platform=linux/amd64 kube-apiserver_v1.29.12.tar registry.k8s.io/kube-apiserver:v1.29.12
ctr image export --platform=linux/amd64 kube-controller-manager_v1.29.12.tar registry.k8s.io/kube-controller-manager:v1.29.12
ctr image export --platform=linux/amd64 kube-scheduler_v1.29.12.tar registry.k8s.io/kube-scheduler:v1.29.12
ctr image export --platform=linux/amd64 kube-proxy_v1.29.12.tar registry.k8s.io/kube-proxy:v1.29.12
ctr image export --platform=linux/amd64 coredns_v1.11.1.tar registry.k8s.io/coredns/coredns:v1.11.1
ctr image export --platform=linux/amd64 pause_3.9.tar registry.k8s.io/pause:3.9
ctr image export --platform=linux/amd64 etcd_3.5.16-0.tar registry.k8s.io/etcd:3.5.16-0

Step 5: Transfer Binaries/Images to Offline System

Transfer downloaded binaries and images to offline system. This can be done via secure ssh or removable storage media.
  • k8s.tar.gz
  • kube-apiserver_v1.29.12.tar
  • kube-controller-manager_v1.29.12.tar
  • kube-scheduler_v1.29.12.tar
  • kube-proxy_v1.29.12.tar
  • coredns_v1.11.1.tar
  • pause_3.9.tar
  • etcd_3.5.16-0.tar

Install K8S

Offline System without Internet Access

Import all binaries and images from the system with internet access to the offline system. This example uses a linux machine. Commands have to be performed as root (since it involves installation of Kubernetes).
sudo su -

Step 1: Install K8S Packages

Unzip k8s.tar and install, then enable kubelet:
# unzip k8s components
tar -zxvf k8s.tar.gz

# install k8s
rpm -ivh --replacefiles --replacepkgs /k8s/*.rpm

# enable kubelet
systemctl enable kubelet.service
systemctl start kubelet.service

# check status
sudo systemctl status kubelet

Step 2: Load K8S Dependency Images

Load K8S images with either docker or containerd(using containerd for this example):
sudo ctr -n=k8s.io images import kube-apiserver_v1.29.12.tar
sudo ctr -n=k8s.io images import kube-controller-manager_v1.29.12.tar
sudo ctr -n=k8s.io images import kube-scheduler_v1.29.12.tar
sudo ctr -n=k8s.io images import kube-proxy_v1.29.12.tar
sudo ctr -n=k8s.io images import coredns_v1.11.1.tar
sudo ctr -n=k8s.io images import pause_3.9.tar
sudo ctr -n=k8s.io images import etcd_3.5.16-0.tar

Step 3: Initialize K8S

Initialize and setup cluster control-plane node:
CNI_CIDR="192.168.100.0/19"
SERVICE_CIDR="192.168.200.0/19"
NODE_NAME="my-node"
kubeadm init \
    --pod-network-cidr $CNI_CIDR \
    --service-cidr $SERVICE_CIDR \
    --node-name $NODE_NAME
NOTE: Ensure that CNI and Service CIDRs are consistent with previous or desired installation.
Set kubectl config:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
export KUBECONFIG=/etc/kubernetes/admin.conf
Verify that the cluster is running:
kubectl get nodes
Output should be similar to the following:
NAME                           STATUS     ROLES           AGE   VERSION
ip-172-31-9-184.ec2.internal   NotReady   control-plane   37m   v1.29.12
List running pods:
kubectl get pods -A
Output should be similar to the following:
NAMESPACE     NAME                                                   READY   STATUS    RESTARTS   AGE
kube-system   coredns-76f75df574-msppj                               0/1     Pending   0          37m
kube-system   coredns-76f75df574-pdn7f                               0/1     Pending   0          37m
kube-system   etcd-ip-172-31-9-184.ec2.internal                      1/1     Running   0          37m
kube-system   kube-apiserver-ip-172-31-9-184.ec2.internal            1/1     Running   0          37m
kube-system   kube-controller-manager-ip-172-31-9-184.ec2.internal   1/1     Running   0          37m
kube-system   kube-proxy-fj4k8                                       1/1     Running   0          37m
kube-system   kube-scheduler-ip-172-31-9-184.ec2.internal            1/1     Running   0          37m
NOTE: coredns pods will be in “Pending” state until flannel (or other CNI) is installed.