Recovering a master node if kubelet cannot load control plane components
This situation may occur if, in a cluster with a single master node, the control plane component images
were deleted on it (for example, the /var/lib/containerd directory was deleted). In this case, after a restart, kubelet cannot pull the control plane component images because the master node does not have authorization parameters for registry.deckhouse.ru.
Below are the instructions for recovering the master node.
containerd
To restore the master node, run the following command in any working cluster managed by Deckhouse:
d8 k -n d8-system get secrets deckhouse-registry -o json |
jq -r '.data.".dockerconfigjson"' | base64 -d |
jq -r '.auths."registry.deckhouse.ru".auth'Copy the command output and assign it to the AUTH variable on the damaged master node.
Next, pull the control-plane component images on the damaged master node:
for image in $(grep "image:" /etc/kubernetes/manifests/* | awk '{print $3}'); do
crictl pull --auth $AUTH $image
doneAfter pulling the images, restart kubelet.
Changing CRI for a NodeGroup
You can change the CRI for a separate node group.
Warning! You can only switch from
ContainerdtoNotManagedand back (the cri.type parameter).
Set the cri.type parameter to Containerd or NotManaged.
Example of a NodeGroup YAML manifest:
apiVersion: deckhouse.io/v1
kind: NodeGroup
metadata:
name: worker
spec:
nodeType: Static
cri:
type: ContainerdYou can also perform this operation using a patch:
For
Containerd:d8 k patch nodegroup <NODEGROUP_NAME> --type merge -p '{"spec":{"cri":{"type":"Containerd"}}}'For
NotManaged:d8 k patch nodegroup <NODEGROUP_NAME> --type merge -p '{"spec":{"cri":{"type":"NotManaged"}}}'
Warning! When changing
cri.typefor NodeGroups created usingdhctl, you must change it both indhctl config edit provider-cluster-configurationand in the NodeGroup object settings.
After a new CRI is configured for a NodeGroup, the node-manager module drains the nodes one by one and installs the new CRI on them. The node update
is accompanied by downtime (disruption). Depending on the disruption setting of the NodeGroup, the node-manager module either automatically allows node
updates or requires manual approval.
Changing CRI for the whole cluster
You can change the CRI for all nodes of the cluster.
Warning! You can only switch from
ContainerdtoNotManagedand back (the cri.type parameter).
Use the dhctl utility to edit the defaultCRI parameter in the cluster-configuration config.
You can also perform this operation using a patch. Example:
For
Containerd:data="$(d8 k -n kube-system get secret d8-cluster-configuration -o json | jq -r '.data."cluster-configuration.yaml"' | base64 -d | sed "s/NotManaged/Containerd/" | base64 -w0)" d8 k -n kube-system patch secret d8-cluster-configuration -p '{"data":{"cluster-configuration.yaml":"'${data}'"}}'For
NotManaged:data="$(d8 k -n kube-system get secret d8-cluster-configuration -o json | jq -r '.data."cluster-configuration.yaml"' | base64 -d | sed "s/Containerd/NotManaged/" | base64 -w0)" d8 k -n kube-system patch secret d8-cluster-configuration -p '{"data":{"cluster-configuration.yaml":"'${data}'"}}'
If you need to keep a NodeGroup on a different CRI, set the CRI for this NodeGroup before changing defaultCRI,
as described in Changing CRI for a NodeGroup.
Warning! Changing
defaultCRIresults in changing the CRI on all nodes, including master nodes. If there is only one master node, this operation is dangerous and may make the cluster completely inoperable! The preferred option is to switch to a multi-master setup and then change the CRI type!
When changing the CRI in the cluster, perform the following additional steps for master nodes:
Deckhouse updates the nodes in the master NodeGroup one by one, so determine which node is currently being updated:
d8 k get nodes -l node-role.kubernetes.io/control-plane="" -o json | jq '.items[] | select(.metadata.annotations."update.node.deckhouse.io/approved"=="") | .metadata.name' -rApprove the disruption for the master node obtained in the previous step:
d8 k annotate node <MASTER_NODE_NAME> update.node.deckhouse.io/disruption-approved=Wait until the updated master node switches to
Ready. Repeat the iteration for the next master node.
How to use containerd with Nvidia GPU support?
Create a separate NodeGroup for GPU nodes.
apiVersion: deckhouse.io/v1
kind: NodeGroup
metadata:
name: gpu
spec:
chaos:
mode: Disabled
disruptions:
approvalMode: Automatic
nodeType: CloudStaticNext, create a NodeGroupConfiguration for the gpu NodeGroup to configure containerd:
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: containerd-additional-config.sh
spec:
bundles:
- '*'
content: |
# Copyright 2023 Flant JSC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
mkdir -p /etc/containerd/conf.d
bb-sync-file /etc/containerd/conf.d/nvidia_gpu.toml - << "EOF"
[plugins]
[plugins."io.containerd.grpc.v1.cri"]
[plugins."io.containerd.grpc.v1.cri".containerd]
default_runtime_name = "nvidia"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia]
privileged_without_host_devices = false
runtime_engine = ""
runtime_root = ""
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.nvidia.options]
BinaryName = "/usr/bin/nvidia-container-runtime"
SystemdCgroup = false
EOF
nodeGroups:
- gpu
weight: 31Next, add a NodeGroupConfiguration to install Nvidia drivers for the gpu NodeGroup.
Ubuntu
NodeGroupConfiguration for Ubuntu:
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda.sh
spec:
bundles:
- ubuntu-lts
content: |
# Copyright 2023 Flant JSC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if [ ! -f "/etc/apt/sources.list.d/nvidia-container-toolkit.list" ]; then
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/libnvidia-container/gpgkey | sudo apt-key add -
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.list | sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
fi
bb-apt-install nvidia-container-toolkit nvidia-driver-535-server
nvidia-ctk config --set nvidia-container-runtime.log-level=error --in-place
nodeGroups:
- gpu
weight: 30Centos
NodeGroupConfiguration for CentOS:
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
name: install-cuda.sh
spec:
bundles:
- centos
content: |
# Copyright 2023 Flant JSC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if [ ! -f "/etc/yum.repos.d/nvidia-container-toolkit.repo" ]; then
distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
curl -s -L https://nvidia.github.io/libnvidia-container/$distribution/libnvidia-container.repo | sudo tee /etc/yum.repos.d/nvidia-container-toolkit.repo
fi
bb-yum-install nvidia-container-toolkit nvidia-driver
nvidia-ctk config --set nvidia-container-runtime.log-level=error --in-place
nodeGroups:
- gpu
weight: 30After that, bootstrap and reboot the node.
How to check that everything went well?
Create a Job in the cluster:
apiVersion: batch/v1
kind: Job
metadata:
name: nvidia-cuda-test
namespace: default
spec:
completions: 1
template:
spec:
restartPolicy: Never
nodeSelector:
node.deckhouse.io/group: gpu
containers:
- name: nvidia-cuda-test
image: nvidia/cuda:11.6.2-base-ubuntu20.04
imagePullPolicy: "IfNotPresent"
command:
- nvidia-smiAnd check the logs:
$ d8 k logs job/nvidia-cuda-test
Tue Jan 24 11:36:18 2023
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 525.60.13 Driver Version: 525.60.13 CUDA Version: 12.0 |
|-------------------------------+----------------------+----------------------+
| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |
| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |
| | | MIG M. |
|===============================+======================+======================|
| 0 Tesla T4 Off | 00000000:8B:00.0 Off | 0 |
| N/A 45C P0 25W / 70W | 0MiB / 15360MiB | 0% Default |
| | | N/A |
+-------------------------------+----------------------+----------------------+
+-----------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
| ID ID Usage |
|=============================================================================|
| No running processes found |
+-----------------------------------------------------------------------------+
Create a Job in the cluster:
apiVersion: batch/v1
kind: Job
metadata:
name: gpu-operator-test
namespace: default
spec:
completions: 1
template:
spec:
restartPolicy: Never
nodeSelector:
node.deckhouse.io/group: gpu
containers:
- name: gpu-operator-test
image: nvidia/samples:vectoradd-cuda10.2
imagePullPolicy: "IfNotPresent"And check the logs:
$ d8 k logs job/gpu-operator-test
[Vector addition of 50000 elements]
Copy input data from the host memory to the CUDA device
CUDA kernel launch with 196 blocks of 256 threads
Copy output data from the CUDA device to the host memory
Test PASSED
Done
How to add multiple static nodes to the cluster manually?
Use an existing NodeGroup custom resource or create a new one (see an example of a NodeGroup named worker).
You can automate adding nodes using any automation platform. Below is an example for Ansible.
Get one of the Kubernetes API server addresses. Note that the IP address must be reachable from the nodes being added to the cluster:
d8 k -n default get ep kubernetes -o json | jq '.subsets[0].addresses[0].ip + ":" + (.subsets[0].ports[0].port | tostring)' -rCheck the Kubernetes version. If the version is >= 1.25, create a
node-grouptoken:d8 k create token node-group --namespace d8-cloud-instance-manager --duration 1hSave the resulting token and add it to the
token:field of the Ansible playbook in the following steps.If the Kubernetes version is lower than 1.25, get a Kubernetes API token for the special ServiceAccount managed by Deckhouse:
d8 k -n d8-cloud-instance-manager get $(d8 k -n d8-cloud-instance-manager get secret -o name | grep node-group-token) -o json | jq '.data.token' -r | base64 -d && echo ""Create an Ansible playbook with
varsreplaced by the values obtained in the previous steps:- hosts: all become: yes gather_facts: no vars: kube_apiserver: <KUBE_APISERVER> token: <TOKEN> tasks: - name: Check if node is already bootsrapped stat: path: /var/lib/bashible register: bootstrapped - name: Get bootstrap secret uri: url: "https://{{ kube_apiserver }}/api/v1/namespaces/d8-cloud-instance-manager/secrets/manual-bootstrap-for-{{ node_group }}" return_content: yes method: GET status_code: 200 body_format: json headers: Authorization: "Bearer {{ token }}" validate_certs: no register: bootstrap_secret when: bootstrapped.stat.exists == False - name: Run bootstrap.sh shell: "{{ bootstrap_secret.json.data['bootstrap.sh'] | b64decode }}" args: executable: /bin/bash ignore_errors: yes when: bootstrapped.stat.exists == False - name: wait wait_for_connection: delay: 30 when: bootstrapped.stat.exists == FalseDefine an additional
node_groupvariable. Its value must match the name of the NodeGroup the node will belong to. You can pass the variable in various ways, for example, using an inventory file:[system] system-0 system-1 [system:vars] node_group=system [worker] worker-0 worker-1 [worker:vars] node_group=workerRun the playbook using the inventory file.
How to make werf ignore the Ready state of a node group?
werf checks the Ready state of resources and, if present, waits until its value becomes True.
Creating (updating) a NodeGroup resource in the cluster may take a significant amount of time to deploy the required number of nodes. When such a resource is deployed to the cluster using werf (for example, as part of a CI/CD process), the deployment may fail due to exceeding the resource readiness timeout. To make werf ignore the nodeGroup state, add the following annotations to the nodeGroup:
metadata:
annotations:
werf.io/fail-mode: IgnoreAndContinueDeployProcess
werf.io/track-termination-mode: NonBlocking