k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cluster/gce/gci/configure.sh (about) 1 #!/usr/bin/env bash 2 3 # Copyright 2016 The Kubernetes Authors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); 6 # you may not use this file except in compliance with the License. 7 # You may obtain a copy of the License at 8 # 9 # http://www.apache.org/licenses/LICENSE-2.0 10 # 11 # Unless required by applicable law or agreed to in writing, software 12 # distributed under the License is distributed on an "AS IS" BASIS, 13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 17 # Due to the GCE custom metadata size limit, we split the entire script into two 18 # files configure.sh and configure-helper.sh. The functionality of downloading 19 # kubernetes configuration, manifests, docker images, and binary files are 20 # put in configure.sh, which is uploaded via GCE custom metadata. 21 22 set -o errexit 23 set -o nounset 24 set -o pipefail 25 26 ### Hardcoded constants 27 DEFAULT_CNI_VERSION='v1.4.1' 28 DEFAULT_CNI_HASH='5cff10005288a78b484bbabbaa24669d62a6eedb6cc7c7604b2c1ea14b65e90a1b022c2a2975d7764ec41f24707f6349432e8f11564d03cea0da141195b0602e' 29 DEFAULT_NPD_VERSION='v0.8.16' 30 DEFAULT_NPD_HASH_AMD64='6d7c9a8e07d95085ef949373e2019df67b2c717aa3b8043eb7c63ac6a679e7894b0f6efea98354f0059001a35966353220613936ace499ad97b96b7c04f693d8' 31 DEFAULT_NPD_HASH_ARM64='8c6a61a92e846b2b6c844bac51f34fd90a97fe7109bc3d16520370b944b9a2d1b117c0d1d95464f374354700f4697baaa41265d44e233996b63ef4e5ae450b24' 32 DEFAULT_CRICTL_VERSION='v1.30.0' 33 DEFAULT_CRICTL_AMD64_SHA512='2bd1e85ab6abd2ac59e6d1a4ccb316fd08e3b025f8de85a6c8396defb7df5ee4e8daf884c4ad9d6451477ef9754240ec786cc5c5ccf1e9a2f616450f2a98a0d0' 34 DEFAULT_CRICTL_ARM64_SHA512='bf2d18b781fd4072058862e23e787dd9b1c6c18626bfc5140fbbf34b78b4fca4b3243eff42d28f0a10f1054b4547405a82afae319c9d9975669ef9a7e27a6b42' 35 DEFAULT_MOUNTER_TAR_SHA='7956fd42523de6b3107ddc3ce0e75233d2fcb78436ff07a1389b6eaac91fb2b1b72a08f7a219eaf96ba1ca4da8d45271002e0d60e0644e796c665f99bb356516' 36 AUTH_PROVIDER_GCP_HASH_LINUX_AMD64="${AUTH_PROVIDER_GCP_HASH_LINUX_AMD64:-156058e5b3994cba91c23831774033e0d505d6d8b80f43541ef6af91b320fd9dfaabe42ec8a8887b51d87104c2b57e1eb895649d681575ffc80dd9aee8e563db}" 37 AUTH_PROVIDER_GCP_HASH_LINUX_ARM64="${AUTH_PROVIDER_GCP_HASH_LINUX_ARM64:-1aa3b0bea10a9755231989ffc150cbfa770f1d96932db7535473f7bfeb1108bafdae80202ae738d59495982512e716ff7366d5f414d0e76dd50519f98611f9ab}" 38 ### 39 40 # Standard curl flags. 41 CURL_FLAGS='--fail --silent --show-error --retry 5 --retry-delay 3 --connect-timeout 10 --retry-connrefused' 42 43 function set-broken-motd { 44 cat > /etc/motd <<EOF 45 Broken (or in progress) Kubernetes node setup! Check the cluster initialization status 46 using the following commands. 47 48 Master instance: 49 - sudo systemctl status kube-master-installation 50 - sudo systemctl status kube-master-configuration 51 52 Node instance: 53 - sudo systemctl status kube-node-installation 54 - sudo systemctl status kube-node-configuration 55 EOF 56 } 57 58 # A function that fetches a GCE metadata value and echoes it out. 59 # Args: 60 # $1 : URL path after /computeMetadata/v1/ (without heading slash). 61 # $2 : An optional default value to echo out if the fetch fails. 62 # 63 # NOTE: this function is duplicated in configure-helper.sh, any changes here 64 # should be duplicated there as well. 65 function get-metadata-value { 66 local default="${2:-}" 67 68 local status 69 # shellcheck disable=SC2086 70 curl ${CURL_FLAGS} \ 71 -H 'Metadata-Flavor: Google' \ 72 "http://metadata/computeMetadata/v1/${1}" \ 73 || status="$?" 74 status="${status:-0}" 75 76 if [[ "${status}" -eq 0 || -z "${default}" ]]; then 77 return "${status}" 78 else 79 echo "${default}" 80 fi 81 } 82 83 function download-kube-env { 84 # Fetch kube-env from GCE metadata server. 85 ( 86 umask 077 87 local -r tmp_kube_env="/tmp/kube-env.yaml" 88 # shellcheck disable=SC2086 89 retry-forever 10 curl ${CURL_FLAGS} \ 90 -H "X-Google-Metadata-Request: True" \ 91 -o "${tmp_kube_env}" \ 92 http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-env 93 # Convert the yaml format file into a shell-style file. 94 eval "$(python3 -c ''' 95 import pipes,sys,yaml 96 items = yaml.load(sys.stdin, Loader=yaml.BaseLoader).items() 97 for k, v in items: 98 print("readonly {var}={value}".format(var=k, value=pipes.quote(str(v)))) 99 ''' < "${tmp_kube_env}" > "${KUBE_HOME}/kube-env")" 100 rm -f "${tmp_kube_env}" 101 ) 102 } 103 104 function download-kubelet-config { 105 local -r dest="$1" 106 echo "Downloading Kubelet config file, if it exists" 107 # Fetch kubelet config file from GCE metadata server. 108 ( 109 umask 077 110 local -r tmp_kubelet_config="/tmp/kubelet-config.yaml" 111 # shellcheck disable=SC2086 112 retry-forever 10 curl ${CURL_FLAGS} \ 113 -H "X-Google-Metadata-Request: True" \ 114 -o "${tmp_kubelet_config}" \ 115 http://metadata.google.internal/computeMetadata/v1/instance/attributes/kubelet-config 116 # only write to the final location if curl succeeds 117 mv "${tmp_kubelet_config}" "${dest}" 118 ) 119 } 120 121 function download-kube-master-certs { 122 # Fetch kube-env from GCE metadata server. 123 ( 124 umask 077 125 local -r tmp_kube_master_certs="/tmp/kube-master-certs.yaml" 126 # shellcheck disable=SC2086 127 retry-forever 10 curl ${CURL_FLAGS} \ 128 -H "X-Google-Metadata-Request: True" \ 129 -o "${tmp_kube_master_certs}" \ 130 http://metadata.google.internal/computeMetadata/v1/instance/attributes/kube-master-certs 131 # Convert the yaml format file into a shell-style file. 132 eval "$(python3 -c ''' 133 import pipes,sys,yaml 134 items = yaml.load(sys.stdin, Loader=yaml.BaseLoader).items() 135 for k, v in items: 136 print("readonly {var}={value}".format(var=k, value=pipes.quote(str(v)))) 137 ''' < "${tmp_kube_master_certs}" > "${KUBE_HOME}/kube-master-certs")" 138 rm -f "${tmp_kube_master_certs}" 139 ) 140 } 141 142 function validate-hash { 143 local -r file="$1" 144 local -r expected="$2" 145 146 actual_sha1=$(sha1sum "${file}" | awk '{ print $1 }') || true 147 actual_sha512=$(sha512sum "${file}" | awk '{ print $1 }') || true 148 if [[ "${actual_sha1}" != "${expected}" ]] && [[ "${actual_sha512}" != "${expected}" ]]; then 149 echo "== ${file} corrupted, sha1 ${actual_sha1}/sha512 ${actual_sha512} doesn't match expected ${expected} ==" 150 return 1 151 fi 152 } 153 154 # Get default service account credentials of the VM. 155 GCE_METADATA_INTERNAL="http://metadata.google.internal/computeMetadata/v1/instance" 156 function get-credentials { 157 # shellcheck disable=SC2086 158 curl ${CURL_FLAGS} \ 159 -H "Metadata-Flavor: Google" \ 160 "${GCE_METADATA_INTERNAL}/service-accounts/default/token" \ 161 | python3 -c 'import sys; import json; print(json.loads(sys.stdin.read())["access_token"])' 162 } 163 164 function valid-storage-scope { 165 # shellcheck disable=SC2086 166 curl ${CURL_FLAGS} \ 167 -H "Metadata-Flavor: Google" \ 168 "${GCE_METADATA_INTERNAL}/service-accounts/default/scopes" \ 169 | grep -E "auth/devstorage|auth/cloud-platform" 170 } 171 172 # Retry a download until we get it. Takes a hash and a set of URLs. 173 # 174 # $1 is the sha512/sha1 hash of the URL. Can be "" if the sha512/sha1 hash is unknown. 175 # $2+ are the URLs to download. 176 function download-or-bust { 177 local -r hash="$1" 178 shift 1 179 180 while true; do 181 for url in "$@"; do 182 local file="${url##*/}" 183 rm -f "${file}" 184 # if the url belongs to GCS API we should use oauth2_token in the headers if the VM service account has storage scopes 185 local curl_headers="" 186 187 if [[ "$url" =~ ^https://storage.googleapis.com.* ]] ; then 188 local canUseCredentials=0 189 190 echo "Getting the scope of service account configured for VM." 191 if ! valid-storage-scope ; then 192 canUseCredentials=1 193 # this behavior is preserved for backward compatibility. We want to fail fast if SA is not available 194 # and try to download without SA if scope does not exist on SA 195 echo "No service account or service account without storage scope. Attempt to download without service account token." 196 fi 197 198 if [[ "${canUseCredentials}" == "0" ]] ; then 199 echo "Getting the service account access token configured for VM." 200 local access_token=""; 201 if access_token=$(get-credentials); then 202 echo "Service account access token is received. Downloading ${url} using this token." 203 else 204 echo "Cannot get a service account token. Exiting." 205 exit 1 206 fi 207 208 curl_headers=${access_token:+Authorization: Bearer "${access_token}"} 209 fi 210 fi 211 if ! curl ${curl_headers:+-H "${curl_headers}"} -f --ipv4 -Lo "${file}" --connect-timeout 20 --max-time 300 --retry 6 --retry-delay 10 --retry-connrefused "${url}"; then 212 echo "== Failed to download ${url}. Retrying. ==" 213 elif [[ -n "${hash}" ]] && ! validate-hash "${file}" "${hash}"; then 214 echo "== Hash validation of ${url} failed. Retrying. ==" 215 else 216 if [[ -n "${hash}" ]]; then 217 echo "== Downloaded ${url} (HASH = ${hash}) ==" 218 else 219 echo "== Downloaded ${url} ==" 220 fi 221 return 222 fi 223 done 224 done 225 } 226 227 function is-preloaded { 228 local -r key=$1 229 local -r value=$2 230 grep -qs "${key},${value}" "${KUBE_HOME}/preload_info" 231 } 232 233 function split-commas { 234 echo -e "${1//,/'\n'}" 235 } 236 237 function remount-flexvolume-directory { 238 local -r flexvolume_plugin_dir=$1 239 mkdir -p "$flexvolume_plugin_dir" 240 mount --bind "$flexvolume_plugin_dir" "$flexvolume_plugin_dir" 241 mount -o remount,exec "$flexvolume_plugin_dir" 242 } 243 244 function install-gci-mounter-tools { 245 CONTAINERIZED_MOUNTER_HOME="${KUBE_HOME}/containerized_mounter" 246 local -r mounter_tar_sha="${DEFAULT_MOUNTER_TAR_SHA}" 247 if is-preloaded "mounter" "${mounter_tar_sha}"; then 248 echo "mounter is preloaded." 249 return 250 fi 251 252 echo "Downloading gci mounter tools." 253 mkdir -p "${CONTAINERIZED_MOUNTER_HOME}" 254 chmod a+x "${CONTAINERIZED_MOUNTER_HOME}" 255 mkdir -p "${CONTAINERIZED_MOUNTER_HOME}/rootfs" 256 download-or-bust "${mounter_tar_sha}" "https://storage.googleapis.com/kubernetes-release/gci-mounter/mounter.tar" 257 cp "${KUBE_HOME}/kubernetes/server/bin/mounter" "${CONTAINERIZED_MOUNTER_HOME}/mounter" 258 chmod a+x "${CONTAINERIZED_MOUNTER_HOME}/mounter" 259 mv "${KUBE_HOME}/mounter.tar" /tmp/mounter.tar 260 tar xf /tmp/mounter.tar -C "${CONTAINERIZED_MOUNTER_HOME}/rootfs" 261 rm /tmp/mounter.tar 262 mkdir -p "${CONTAINERIZED_MOUNTER_HOME}/rootfs/var/lib/kubelet" 263 } 264 265 # Install node problem detector binary. 266 function install-node-problem-detector { 267 if [[ -n "${NODE_PROBLEM_DETECTOR_VERSION:-}" ]]; then 268 local -r npd_version="${NODE_PROBLEM_DETECTOR_VERSION}" 269 local -r npd_hash="${NODE_PROBLEM_DETECTOR_TAR_HASH}" 270 else 271 local -r npd_version="${DEFAULT_NPD_VERSION}" 272 case "${HOST_PLATFORM}/${HOST_ARCH}" in 273 linux/amd64) 274 local -r npd_hash="${DEFAULT_NPD_HASH_AMD64}" 275 ;; 276 linux/arm64) 277 local -r npd_hash="${DEFAULT_NPD_HASH_ARM64}" 278 ;; 279 # no other architectures are supported currently. 280 # Assumption is that this script only runs on linux, 281 # see cluster/gce/windows/k8s-node-setup.psm1 for windows 282 # https://github.com/kubernetes/node-problem-detector/releases/ 283 *) 284 echo "Unrecognized version and platform/arch combination:" 285 echo "$DEFAULT_NPD_VERSION $HOST_PLATFORM/$HOST_ARCH" 286 echo "Set NODE_PROBLEM_DETECTOR_VERSION and NODE_PROBLEM_DETECTOR_TAR_HASH to overwrite" 287 exit 1 288 ;; 289 esac 290 fi 291 local -r npd_tar="node-problem-detector-${npd_version}-${HOST_PLATFORM}_${HOST_ARCH}.tar.gz" 292 293 if is-preloaded "${npd_tar}" "${npd_hash}"; then 294 echo "${npd_tar} is preloaded." 295 return 296 fi 297 298 if [[ -n "${NODE_PROBLEM_DETECTOR_RELEASE_PATH:-}" ]]; then 299 echo "Downloading ${npd_tar} from ${NODE_PROBLEM_DETECTOR_RELEASE_PATH}." 300 local -r download_path="${NODE_PROBLEM_DETECTOR_RELEASE_PATH}/node-problem-detector/${npd_tar}" 301 else 302 echo "Downloading ${npd_tar} from github." 303 local -r download_path="https://github.com/kubernetes/node-problem-detector/releases/download/${npd_version}/${npd_tar}" 304 fi 305 download-or-bust "${npd_hash}" "${download_path}" 306 local -r npd_dir="${KUBE_HOME}/node-problem-detector" 307 mkdir -p "${npd_dir}" 308 tar xzf "${KUBE_HOME}/${npd_tar}" -C "${npd_dir}" --overwrite 309 mv "${npd_dir}/bin"/* "${KUBE_BIN}" 310 chmod a+x "${KUBE_BIN}/node-problem-detector" 311 rmdir "${npd_dir}/bin" 312 rm -f "${KUBE_HOME}/${npd_tar}" 313 } 314 315 function install-cni-binaries { 316 local -r cni_version=${CNI_VERSION:-$DEFAULT_CNI_VERSION} 317 if [[ -n "${CNI_VERSION:-}" ]]; then 318 local -r cni_hash="${CNI_HASH:-}" 319 else 320 local -r cni_hash="${DEFAULT_CNI_HASH}" 321 fi 322 323 local -r cni_tar="${CNI_TAR_PREFIX}${cni_version}.tgz" 324 local -r cni_url="${CNI_STORAGE_URL_BASE}/${cni_version}/${cni_tar}" 325 326 if is-preloaded "${cni_tar}" "${cni_hash}"; then 327 echo "${cni_tar} is preloaded." 328 return 329 fi 330 331 echo "Downloading cni binaries" 332 download-or-bust "${cni_hash}" "${cni_url}" 333 local -r cni_dir="${KUBE_HOME}/cni" 334 mkdir -p "${cni_dir}/bin" 335 tar xzf "${KUBE_HOME}/${cni_tar}" -C "${cni_dir}/bin" --overwrite 336 mv "${cni_dir}/bin"/* "${KUBE_BIN}" 337 rmdir "${cni_dir}/bin" 338 rm -f "${KUBE_HOME}/${cni_tar}" 339 } 340 341 # Install crictl binary. 342 # Assumptions: HOST_PLATFORM and HOST_ARCH are specified by calling detect_host_info. 343 function install-crictl { 344 if [[ -n "${CRICTL_VERSION:-}" ]]; then 345 local -r crictl_version="${CRICTL_VERSION}" 346 local -r crictl_hash="${CRICTL_TAR_HASH}" 347 else 348 local -r crictl_version="${DEFAULT_CRICTL_VERSION}" 349 case "${HOST_PLATFORM}/${HOST_ARCH}" in 350 linux/amd64) 351 local -r crictl_hash="${DEFAULT_CRICTL_AMD64_SHA512}" 352 ;; 353 linux/arm64) 354 local -r crictl_hash="${DEFAULT_CRICTL_ARM64_SHA512}" 355 ;; 356 *) 357 echo "Unrecognized version and platform/arch combination:" 358 echo "$DEFAULT_CRICTL_VERSION $HOST_PLATFORM/$HOST_ARCH" 359 echo "Set CRICTL_VERSION and CRICTL_TAR_HASH to overwrite" 360 exit 1 361 esac 362 fi 363 local -r crictl="crictl-${crictl_version}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" 364 365 # Create crictl config file. 366 cat > /etc/crictl.yaml <<EOF 367 runtime-endpoint: ${CONTAINER_RUNTIME_ENDPOINT:-unix:///run/containerd/containerd.sock} 368 EOF 369 370 if is-preloaded "${crictl}" "${crictl_hash}"; then 371 echo "crictl is preloaded" 372 return 373 fi 374 375 echo "Downloading crictl" 376 local -r crictl_path="https://storage.googleapis.com/k8s-artifacts-cri-tools/release/${crictl_version}" 377 download-or-bust "${crictl_hash}" "${crictl_path}/${crictl}" 378 tar xf "${crictl}" 379 mv crictl "${KUBE_BIN}/crictl" 380 rm -f "${crictl}" 381 } 382 383 function install-kube-manifests { 384 # Put kube-system pods manifests in ${KUBE_HOME}/kube-manifests/. 385 local dst_dir="${KUBE_HOME}/kube-manifests" 386 mkdir -p "${dst_dir}" 387 local manifests_tar_urls 388 while IFS= read -r url; do 389 manifests_tar_urls+=("$url") 390 done < <(split-commas "${KUBE_MANIFESTS_TAR_URL}") 391 local -r manifests_tar="${manifests_tar_urls[0]##*/}" 392 if [ -n "${KUBE_MANIFESTS_TAR_HASH:-}" ]; then 393 local -r manifests_tar_hash="${KUBE_MANIFESTS_TAR_HASH}" 394 else 395 echo "Downloading k8s manifests hash (not found in env)" 396 download-or-bust "" "${manifests_tar_urls[@]/.tar.gz/.tar.gz.sha512}" 397 local -r manifests_tar_hash=$(cat "${manifests_tar}.sha512") 398 fi 399 400 if is-preloaded "${manifests_tar}" "${manifests_tar_hash}"; then 401 echo "${manifests_tar} is preloaded." 402 return 403 fi 404 405 echo "Downloading k8s manifests tar" 406 download-or-bust "${manifests_tar_hash}" "${manifests_tar_urls[@]}" 407 tar xzf "${KUBE_HOME}/${manifests_tar}" -C "${dst_dir}" --overwrite 408 local -r kube_addon_registry="${KUBE_ADDON_REGISTRY:-registry.k8s.io}" 409 if [[ "${kube_addon_registry}" != "registry.k8s.io" ]]; then 410 find "${dst_dir}" \( -name '*.yaml' -or -name '*.yaml.in' \) -print0 | \ 411 xargs -0 sed -ri "s@(image:\s.*)registry.k8s.io@\1${kube_addon_registry}@" 412 find "${dst_dir}" \( -name '*.manifest' -or -name '*.json' \) -print0 | \ 413 xargs -0 sed -ri "s@(image\":\s+\")registry.k8s.io@\1${kube_addon_registry}@" 414 fi 415 cp "${dst_dir}/kubernetes/gci-trusty/gci-configure-helper.sh" "${KUBE_BIN}/configure-helper.sh" 416 cp "${dst_dir}/kubernetes/gci-trusty/configure-kubeapiserver.sh" "${KUBE_BIN}/configure-kubeapiserver.sh" 417 if [[ -e "${dst_dir}/kubernetes/gci-trusty/gke-internal-configure-helper.sh" ]]; then 418 cp "${dst_dir}/kubernetes/gci-trusty/gke-internal-configure-helper.sh" "${KUBE_BIN}/" 419 fi 420 421 cp "${dst_dir}/kubernetes/gci-trusty/health-monitor.sh" "${KUBE_BIN}/health-monitor.sh" 422 423 rm -f "${KUBE_HOME}/${manifests_tar}" 424 rm -f "${KUBE_HOME}/${manifests_tar}.sha512" 425 } 426 427 # A helper function for loading a docker image. It keeps trying up to 5 times. 428 # 429 # $1: Full path of the docker image 430 function try-load-docker-image { 431 local -r img=$1 432 echo "Try to load docker image file ${img}" 433 # Temporarily turn off errexit, because we don't want to exit on first failure. 434 set +e 435 local -r max_attempts=5 436 local -i attempt_num=1 437 438 if [[ "${CONTAINER_RUNTIME_NAME:-}" == "containerd" || "${CONTAINERD_TEST:-}" == "containerd" ]]; then 439 load_image_command=${LOAD_IMAGE_COMMAND:-ctr -n=k8s.io images import} 440 tag_image_command=${TAG_IMAGE_COMMAND:-ctr -n=k8s.io images tag} 441 else 442 load_image_command="${LOAD_IMAGE_COMMAND:-}" 443 tag_image_command="${TAG_IMAGE_COMMAND:-}" 444 fi 445 446 # Deliberately word split load_image_command 447 # shellcheck disable=SC2086 448 until timeout 30 ${load_image_command} "${img}"; do 449 if [[ "${attempt_num}" == "${max_attempts}" ]]; then 450 echo "Fail to load docker image file ${img} using ${load_image_command} after ${max_attempts} retries. Exit!!" 451 exit 1 452 else 453 attempt_num=$((attempt_num+1)) 454 sleep 5 455 fi 456 done 457 458 if [[ -n "${KUBE_ADDON_REGISTRY:-}" ]]; then 459 # remove the prefix and suffix from the path to get the container name 460 container=${img##*/} 461 container=${container%.tar} 462 # find the right one for which we will need an additional tag 463 container=$(ctr -n k8s.io images ls | grep "registry.k8s.io/${container}" | awk '{print $1}' | cut -f 2 -d '/') 464 ${tag_image_command} "registry.k8s.io/${container}" "${KUBE_ADDON_REGISTRY}/${container}" 465 fi 466 # Re-enable errexit. 467 set -e 468 } 469 470 # Loads kube-system docker images. It is better to do it before starting kubelet, 471 # as kubelet will restart docker daemon, which may interfere with loading images. 472 function load-docker-images { 473 echo "Start loading kube-system docker images" 474 local -r img_dir="${KUBE_HOME}/kube-docker-files" 475 if [[ "${KUBERNETES_MASTER:-}" == "true" ]]; then 476 try-load-docker-image "${img_dir}/kube-apiserver.tar" 477 try-load-docker-image "${img_dir}/kube-controller-manager.tar" 478 try-load-docker-image "${img_dir}/kube-scheduler.tar" 479 else 480 try-load-docker-image "${img_dir}/kube-proxy.tar" 481 fi 482 } 483 484 # If we are on ubuntu we can try to install containerd 485 function install-containerd-ubuntu { 486 # bailout if we are not on ubuntu 487 if [[ -z "$(command -v lsb_release)" || $(lsb_release -si) != "Ubuntu" ]]; then 488 echo "Unable to automatically install containerd in non-ubuntu image. Bailing out..." 489 exit 2 490 fi 491 492 # Install dependencies, some of these are already installed in the image but 493 # that's fine since they won't re-install and we can reuse the code below 494 # for another image someday. 495 apt-get update 496 apt-get install -y --no-install-recommends \ 497 apt-transport-https \ 498 ca-certificates \ 499 socat \ 500 curl \ 501 gnupg2 \ 502 software-properties-common \ 503 lsb-release 504 505 release=$(lsb_release -cs) 506 507 # Add the Docker apt-repository (as we install containerd from there) 508 # shellcheck disable=SC2086 509 curl ${CURL_FLAGS} \ 510 --location \ 511 "https://download.docker.com/${HOST_PLATFORM}/$(. /etc/os-release; echo "$ID")/gpg" \ 512 | apt-key add - 513 add-apt-repository \ 514 "deb [arch=${HOST_ARCH}] https://download.docker.com/${HOST_PLATFORM}/$(. /etc/os-release; echo "$ID") \ 515 $release stable" 516 517 # Install containerd from Docker repo 518 apt-get update && \ 519 apt-get install -y --no-install-recommends containerd 520 rm -rf /var/lib/apt/lists/* 521 522 # Override to latest versions of containerd and runc 523 systemctl stop containerd 524 if [[ -n "${UBUNTU_INSTALL_CONTAINERD_VERSION:-}" ]]; then 525 # containerd versions have slightly different url(s), so try both 526 # shellcheck disable=SC2086 527 ( curl ${CURL_FLAGS} \ 528 --location \ 529 "https://github.com/containerd/containerd/releases/download/${UBUNTU_INSTALL_CONTAINERD_VERSION}/containerd-${UBUNTU_INSTALL_CONTAINERD_VERSION:1}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" \ 530 || curl ${CURL_FLAGS} \ 531 --location \ 532 "https://github.com/containerd/containerd/releases/download/${UBUNTU_INSTALL_CONTAINERD_VERSION}/containerd-${UBUNTU_INSTALL_CONTAINERD_VERSION:1}.${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" ) \ 533 | tar --overwrite -xzv -C /usr/ 534 fi 535 if [[ -n "${UBUNTU_INSTALL_RUNC_VERSION:-}" ]]; then 536 # shellcheck disable=SC2086 537 curl ${CURL_FLAGS} \ 538 --location \ 539 "https://github.com/opencontainers/runc/releases/download/${UBUNTU_INSTALL_RUNC_VERSION}/runc.${HOST_ARCH}" --output /usr/sbin/runc \ 540 && chmod 755 /usr/sbin/runc 541 fi 542 sudo systemctl start containerd 543 } 544 545 # If we are on cos we can try to install containerd 546 function install-containerd-cos { 547 # bailout if we are not on COS 548 if [ -e /etc/os-release ] && ! grep -q "ID=cos" /etc/os-release; then 549 echo "Unable to automatically install containerd in non-cos image. Bailing out..." 550 exit 2 551 fi 552 553 # Override to latest versions of containerd and runc 554 systemctl stop containerd 555 mkdir -p /home/containerd/ 556 mount --bind /home/containerd /home/containerd 557 mount -o remount,exec /home/containerd 558 if [[ -n "${COS_INSTALL_CONTAINERD_VERSION:-}" ]]; then 559 # containerd versions have slightly different url(s), so try both 560 # shellcheck disable=SC2086 561 ( curl ${CURL_FLAGS} \ 562 --location \ 563 "https://github.com/containerd/containerd/releases/download/${COS_INSTALL_CONTAINERD_VERSION}/containerd-${COS_INSTALL_CONTAINERD_VERSION:1}-${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" \ 564 || curl ${CURL_FLAGS} \ 565 --location \ 566 "https://github.com/containerd/containerd/releases/download/${COS_INSTALL_CONTAINERD_VERSION}/containerd-${COS_INSTALL_CONTAINERD_VERSION:1}.${HOST_PLATFORM}-${HOST_ARCH}.tar.gz" ) \ 567 | tar --overwrite -xzv -C /home/containerd/ 568 cp /usr/lib/systemd/system/containerd.service /etc/systemd/system/containerd.service 569 # fix the path of the new containerd binary 570 sed -i 's|ExecStart=.*|ExecStart=/home/containerd/bin/containerd|' /etc/systemd/system/containerd.service 571 fi 572 if [[ -n "${COS_INSTALL_RUNC_VERSION:-}" ]]; then 573 # shellcheck disable=SC2086 574 curl ${CURL_FLAGS} \ 575 --location \ 576 "https://github.com/opencontainers/runc/releases/download/${COS_INSTALL_RUNC_VERSION}/runc.${HOST_ARCH}" --output /home/containerd/bin/runc \ 577 && chmod 755 /home/containerd/bin/runc 578 # ensure runc gets picked up from the correct location 579 sed -i "/\[Service\]/a Environment=PATH=/home/containerd/bin:$PATH" /etc/systemd/system/containerd.service 580 fi 581 systemctl daemon-reload 582 sudo systemctl start containerd 583 } 584 585 function install-auth-provider-gcp { 586 local -r filename="auth-provider-gcp" 587 local -r auth_provider_storage_full_path="${AUTH_PROVIDER_GCP_STORAGE_PATH}/${AUTH_PROVIDER_GCP_VERSION}/${HOST_PLATFORM}_${HOST_ARCH}/${filename}" 588 echo "Downloading auth-provider-gcp ${auth_provider_storage_full_path}" . 589 590 case "${HOST_ARCH}" in 591 amd64) 592 local -r auth_provider_gcp_hash="${AUTH_PROVIDER_GCP_HASH_LINUX_AMD64}" 593 ;; 594 arm64) 595 local -r auth_provider_gcp_hash="${AUTH_PROVIDER_GCP_HASH_LINUX_ARM64}" 596 ;; 597 *) 598 echo "Unrecognized version and platform/arch combination: ${HOST_PLATFORM}/${HOST_ARCH}" 599 exit 1 600 esac 601 602 download-or-bust "${auth_provider_gcp_hash}" "${auth_provider_storage_full_path}" 603 604 mv "${KUBE_HOME}/${filename}" "${AUTH_PROVIDER_GCP_LINUX_BIN_DIR}" 605 chmod a+x "${AUTH_PROVIDER_GCP_LINUX_BIN_DIR}/${filename}" 606 607 cat >> "${AUTH_PROVIDER_GCP_LINUX_CONF_FILE}" << EOF 608 kind: CredentialProviderConfig 609 apiVersion: kubelet.config.k8s.io/v1 610 providers: 611 - name: auth-provider-gcp 612 apiVersion: credentialprovider.kubelet.k8s.io/v1 613 matchImages: 614 - "container.cloud.google.com" 615 - "gcr.io" 616 - "*.gcr.io" 617 - "*.pkg.dev" 618 args: 619 - get-credentials 620 - --v=3 621 defaultCacheDuration: 1m 622 EOF 623 } 624 625 function ensure-containerd-runtime { 626 # Install containerd/runc if requested 627 if [[ -n "${UBUNTU_INSTALL_CONTAINERD_VERSION:-}" || -n "${UBUNTU_INSTALL_RUNC_VERSION:-}" ]]; then 628 log-wrap "InstallContainerdUbuntu" install-containerd-ubuntu 629 fi 630 if [[ -n "${COS_INSTALL_CONTAINERD_VERSION:-}" || -n "${COS_INSTALL_RUNC_VERSION:-}" ]]; then 631 log-wrap "InstallContainerdCOS" install-containerd-cos 632 fi 633 634 # Fall back to installing distro specific containerd, if not found 635 if ! command -v containerd >/dev/null 2>&1; then 636 local linuxrelease="cos" 637 if [[ -n "$(command -v lsb_release)" ]]; then 638 linuxrelease=$(lsb_release -si) 639 fi 640 case "${linuxrelease}" in 641 Ubuntu) 642 log-wrap "InstallContainerdUbuntu" install-containerd-ubuntu 643 ;; 644 cos) 645 log-wrap "InstallContainerdCOS" install-containerd-cos 646 ;; 647 *) 648 echo "Installing containerd for linux release ${linuxrelease} not supported" >&2 649 exit 2 650 ;; 651 esac 652 fi 653 654 # when custom containerd version is installed sourcing containerd_env.sh will add all tools like ctr to the PATH 655 if [[ -e "/etc/profile.d/containerd_env.sh" ]]; then 656 log-wrap 'SourceContainerdEnv' source "/etc/profile.d/containerd_env.sh" 657 fi 658 659 # Verify presence and print versions of ctr, containerd, runc 660 if ! command -v ctr >/dev/null 2>&1; then 661 echo "ERROR ctr not found. Aborting." 662 exit 2 663 fi 664 ctr --version 665 if ! command -v containerd >/dev/null 2>&1; then 666 echo "ERROR containerd not found. Aborting." 667 exit 2 668 fi 669 containerd --version 670 if ! command -v runc >/dev/null 2>&1; then 671 echo "ERROR runc not found. Aborting." 672 exit 2 673 fi 674 runc --version 675 } 676 677 function ensure-container-runtime { 678 case "${CONTAINER_RUNTIME_NAME:-containerd}" in 679 containerd) 680 ensure-containerd-runtime 681 ;; 682 #TODO: Add crio support 683 *) 684 echo "Unsupported container runtime (${CONTAINER_RUNTIME_NAME})." >&2 685 exit 2 686 ;; 687 esac 688 } 689 690 # Downloads kubernetes binaries and kube-system manifest tarball, unpacks them, 691 # and places them into suitable directories. Files are placed in /home/kubernetes. 692 function install-kube-binary-config { 693 cd "${KUBE_HOME}" 694 local server_binary_tar_urls 695 while IFS= read -r url; do 696 server_binary_tar_urls+=("$url") 697 done < <(split-commas "${SERVER_BINARY_TAR_URL}") 698 local -r server_binary_tar="${server_binary_tar_urls[0]##*/}" 699 if [[ -n "${SERVER_BINARY_TAR_HASH:-}" ]]; then 700 local -r server_binary_tar_hash="${SERVER_BINARY_TAR_HASH}" 701 else 702 echo "Downloading binary release sha512 (not found in env)" 703 log-wrap "DownloadServerBinarySHA" download-or-bust "" "${server_binary_tar_urls[@]/.tar.gz/.tar.gz.sha512}" 704 local -r server_binary_tar_hash=$(cat "${server_binary_tar}.sha512") 705 fi 706 707 if is-preloaded "${server_binary_tar}" "${server_binary_tar_hash}"; then 708 echo "${server_binary_tar} is preloaded." 709 else 710 echo "Downloading binary release tar" 711 log-wrap "DownloadServerBinary" download-or-bust "${server_binary_tar_hash}" "${server_binary_tar_urls[@]}" 712 log-wrap "UntarServerBinary" tar xzf "${KUBE_HOME}/${server_binary_tar}" -C "${KUBE_HOME}" --overwrite 713 # Copy docker_tag and image files to ${KUBE_HOME}/kube-docker-files. 714 local -r src_dir="${KUBE_HOME}/kubernetes/server/bin" 715 local dst_dir="${KUBE_HOME}/kube-docker-files" 716 mkdir -p "${dst_dir}" 717 cp "${src_dir}/"*.docker_tag "${dst_dir}" 718 if [[ "${KUBERNETES_MASTER:-}" == "false" ]]; then 719 cp "${src_dir}/kube-proxy.tar" "${dst_dir}" 720 else 721 cp "${src_dir}/kube-apiserver.tar" "${dst_dir}" 722 cp "${src_dir}/kube-controller-manager.tar" "${dst_dir}" 723 cp "${src_dir}/kube-scheduler.tar" "${dst_dir}" 724 cp -r "${KUBE_HOME}/kubernetes/addons" "${dst_dir}" 725 fi 726 log-wrap "LoadDockerImages" load-docker-images 727 mv "${src_dir}/kubelet" "${KUBE_BIN}" 728 mv "${src_dir}/kubectl" "${KUBE_BIN}" 729 730 # Some older images have LICENSES baked-in as a file. Presumably they will 731 # have the directory baked-in eventually. 732 rm -rf "${KUBE_HOME}"/LICENSES 733 mv "${KUBE_HOME}/kubernetes/LICENSES" "${KUBE_HOME}" 734 mv "${KUBE_HOME}/kubernetes/kubernetes-src.tar.gz" "${KUBE_HOME}" 735 fi 736 737 if [[ "${KUBERNETES_MASTER:-}" == "false" ]] && \ 738 [[ "${ENABLE_NODE_PROBLEM_DETECTOR:-}" == "standalone" ]]; then 739 log-wrap "InstallNodeProblemDetector" install-node-problem-detector 740 fi 741 742 if [[ "${NETWORK_PROVIDER:-}" == "kubenet" ]] || \ 743 [[ "${NETWORK_PROVIDER:-}" == "cni" ]]; then 744 log-wrap "InstallCNIBinaries" install-cni-binaries 745 fi 746 747 # Put kube-system pods manifests in ${KUBE_HOME}/kube-manifests/. 748 log-wrap "InstallKubeManifests" install-kube-manifests 749 chmod -R 755 "${KUBE_BIN}" 750 751 # Install gci mounter related artifacts to allow mounting storage volumes in GCI 752 log-wrap "InstallGCIMounterTools" install-gci-mounter-tools 753 754 # Remount the Flexvolume directory with the "exec" option, if needed. 755 if [[ "${REMOUNT_VOLUME_PLUGIN_DIR:-}" == "true" && -n "${VOLUME_PLUGIN_DIR:-}" ]]; then 756 log-wrap "RemountFlexVolume" remount-flexvolume-directory "${VOLUME_PLUGIN_DIR}" 757 fi 758 759 # When ENABLE_AUTH_PROVIDER_GCP is set, following flags for out-of-tree credential provider for GCP 760 # are presented to kubelet: 761 # --image-credential-provider-config=${path-to-config} 762 # --image-credential-provider-bin-dir=${path-to-auth-provider-binary} 763 # Also, it is required that DisableKubeletCloudCredentialProviders 764 # feature gate is set to true for kubelet to use external credential provider. 765 if [[ "${ENABLE_AUTH_PROVIDER_GCP:-}" == "true" ]]; then 766 # Install out-of-tree auth-provider-gcp binary to enable kubelet to dynamically 767 # retrieve credentials for a container image registry. 768 log-wrap "InstallCredentialProvider" install-auth-provider-gcp 769 fi 770 # Install crictl on each node. 771 log-wrap "InstallCrictl" install-crictl 772 773 # Clean up. 774 rm -rf "${KUBE_HOME}/kubernetes" 775 rm -f "${KUBE_HOME}/${server_binary_tar}" 776 rm -f "${KUBE_HOME}/${server_binary_tar}.sha512" 777 } 778 779 780 # This function detects the platform/arch of the machine where the script runs, 781 # and sets the HOST_PLATFORM and HOST_ARCH environment variables accordingly. 782 # Callers can specify HOST_PLATFORM_OVERRIDE and HOST_ARCH_OVERRIDE to skip the detection. 783 # This function is adapted from the detect_client_info function in cluster/get-kube-binaries.sh 784 # and kube::util::host_os, kube::util::host_arch functions in hack/lib/util.sh 785 # This function should be synced with detect_host_info in ./configure-helper.sh 786 function detect_host_info() { 787 HOST_PLATFORM=${HOST_PLATFORM_OVERRIDE:-"$(uname -s)"} 788 case "${HOST_PLATFORM}" in 789 Linux|linux) 790 HOST_PLATFORM="linux" 791 ;; 792 *) 793 echo "Unknown, unsupported platform: ${HOST_PLATFORM}." >&2 794 echo "Supported platform(s): linux." >&2 795 echo "Bailing out." >&2 796 exit 2 797 esac 798 799 HOST_ARCH=${HOST_ARCH_OVERRIDE:-"$(uname -m)"} 800 case "${HOST_ARCH}" in 801 x86_64*|i?86_64*|amd64*) 802 HOST_ARCH="amd64" 803 ;; 804 aHOST_arch64*|aarch64*|arm64*) 805 HOST_ARCH="arm64" 806 ;; 807 *) 808 echo "Unknown, unsupported architecture (${HOST_ARCH})." >&2 809 echo "Supported architecture(s): amd64 and arm64." >&2 810 echo "Bailing out." >&2 811 exit 2 812 ;; 813 esac 814 } 815 816 # Retries a command forever with a delay between retries. 817 # Args: 818 # $1 : delay between retries, in seconds. 819 # $2... : the command to run. 820 function retry-forever { 821 local -r delay="$1" 822 shift 1 823 824 until "$@"; do 825 echo "== $* failed, retrying after ${delay}s" 826 sleep "${delay}" 827 done 828 } 829 830 # Initializes variables used by the log-* functions. 831 # 832 # get-metadata-value must be defined before calling this function. 833 # 834 # NOTE: this function is duplicated in configure-helper.sh, any changes here 835 # should be duplicated there as well. 836 function log-init { 837 # Used by log-* functions. 838 LOG_CLUSTER_ID=$(get-metadata-value 'instance/attributes/cluster-uid' 'get-metadata-value-error') 839 LOG_INSTANCE_NAME=$(hostname) 840 LOG_BOOT_ID=$(journalctl --list-boots | grep -E '^ *0' | awk '{print $2}') 841 declare -Ag LOG_START_TIMES 842 declare -ag LOG_TRAP_STACK 843 844 LOG_STATUS_STARTED='STARTED' 845 LOG_STATUS_COMPLETED='COMPLETED' 846 LOG_STATUS_ERROR='ERROR' 847 } 848 849 # Sets an EXIT trap. 850 # Args: 851 # $1:... : the trap command. 852 # 853 # NOTE: this function is duplicated in configure-helper.sh, any changes here 854 # should be duplicated there as well. 855 function log-trap-push { 856 local t="${*:1}" 857 LOG_TRAP_STACK+=("${t}") 858 # shellcheck disable=2064 859 trap "${t}" EXIT 860 } 861 862 # Removes and restores an EXIT trap. 863 # 864 # NOTE: this function is duplicated in configure-helper.sh, any changes here 865 # should be duplicated there as well. 866 function log-trap-pop { 867 # Remove current trap. 868 unset 'LOG_TRAP_STACK[-1]' 869 870 # Restore previous trap. 871 if [ ${#LOG_TRAP_STACK[@]} -ne 0 ]; then 872 local t="${LOG_TRAP_STACK[-1]}" 873 # shellcheck disable=2064 874 trap "${t}" EXIT 875 else 876 # If no traps in stack, clear. 877 trap EXIT 878 fi 879 } 880 881 # Logs the end of a bootstrap step that errored. 882 # Args: 883 # $1 : bootstrap step name. 884 # 885 # NOTE: this function is duplicated in configure-helper.sh, any changes here 886 # should be duplicated there as well. 887 function log-error { 888 local bootstep="$1" 889 890 log-proto "${bootstep}" "${LOG_STATUS_ERROR}" "encountered non-zero exit code" 891 } 892 893 # Wraps a command with bootstrap logging. 894 # Args: 895 # $1 : bootstrap step name. 896 # $2... : the command to run. 897 # 898 # NOTE: this function is duplicated in configure-helper.sh, any changes here 899 # should be duplicated there as well. 900 function log-wrap { 901 local bootstep="$1" 902 local command="${*:2}" 903 904 log-trap-push "log-error ${bootstep}" 905 log-proto "${bootstep}" "${LOG_STATUS_STARTED}" 906 $command 907 log-proto "${bootstep}" "${LOG_STATUS_COMPLETED}" 908 log-trap-pop 909 } 910 911 # Logs a bootstrap step start. Prefer log-wrap. 912 # Args: 913 # $1 : bootstrap step name. 914 # 915 # NOTE: this function is duplicated in configure-helper.sh, any changes here 916 # should be duplicated there as well. 917 function log-start { 918 local bootstep="$1" 919 920 log-trap-push "log-error ${bootstep}" 921 log-proto "${bootstep}" "${LOG_STATUS_STARTED}" 922 } 923 924 # Logs a bootstrap step end. Prefer log-wrap. 925 # Args: 926 # $1 : bootstrap step name. 927 # 928 # NOTE: this function is duplicated in configure-helper.sh, any changes here 929 # should be duplicated there as well. 930 function log-end { 931 local bootstep="$1" 932 933 log-proto "${bootstep}" "${LOG_STATUS_COMPLETED}" 934 log-trap-pop 935 } 936 937 # Writes a log proto to stdout. 938 # Args: 939 # $1: bootstrap step name. 940 # $2: status. Either 'STARTED', 'COMPLETED', or 'ERROR'. 941 # $3: optional status reason. 942 # 943 # NOTE: this function is duplicated in configure-helper.sh, any changes here 944 # should be duplicated there as well. 945 function log-proto { 946 local bootstep="$1" 947 local status="$2" 948 local status_reason="${3:-}" 949 950 # Get current time. 951 local current_time 952 current_time="$(date --utc '+%s.%N')" 953 # ...formatted as UTC RFC 3339. 954 local timestamp 955 timestamp="$(date --utc --date="@${current_time}" '+%FT%T.%NZ')" 956 957 # Calculate latency. 958 local latency='null' 959 if [ "${status}" == "${LOG_STATUS_STARTED}" ]; then 960 LOG_START_TIMES["${bootstep}"]="${current_time}" 961 else 962 local start_time="${LOG_START_TIMES["${bootstep}"]}" 963 unset 'LOG_START_TIMES['"${bootstep}"']' 964 965 # Bash cannot do non-integer math, shell out to awk. 966 latency="$(echo "${current_time} ${start_time}" | awk '{print $1 - $2}')s" 967 968 # The default latency is null which cannot be wrapped as a string so we must 969 # do it here instead of the printf. 970 latency="\"${latency}\"" 971 fi 972 973 printf '[cloud.kubernetes.monitoring.proto.SerialportLog] {"cluster_hash":"%s","vm_instance_name":"%s","boot_id":"%s","timestamp":"%s","bootstrap_status":{"step_name":"%s","status":"%s","status_reason":"%s","latency":%s}}\n' \ 974 "${LOG_CLUSTER_ID}" "${LOG_INSTANCE_NAME}" "${LOG_BOOT_ID}" "${timestamp}" "${bootstep}" "${status}" "${status_reason}" "${latency}" 975 } 976 977 ######### Main Function ########## 978 log-init 979 log-start 'ConfigureMain' 980 echo "Start to install kubernetes files" 981 log-wrap 'DetectHostInfo' detect_host_info 982 983 # if install fails, message-of-the-day (motd) will warn at login shell 984 log-wrap 'SetBrokenMotd' set-broken-motd 985 986 KUBE_HOME="/home/kubernetes" 987 KUBE_BIN="${KUBE_HOME}/bin" 988 989 # download and source kube-env 990 log-wrap 'DownloadKubeEnv' download-kube-env 991 log-wrap 'SourceKubeEnv' source "${KUBE_HOME}/kube-env" 992 993 log-wrap 'DownloadKubeletConfig' download-kubelet-config "${KUBE_HOME}/kubelet-config.yaml" 994 995 # master certs 996 if [[ "${KUBERNETES_MASTER:-}" == "true" ]]; then 997 log-wrap 'DownloadKubeMasterCerts' download-kube-master-certs 998 fi 999 1000 # ensure chosen container runtime is present 1001 log-wrap 'EnsureContainerRuntime' ensure-container-runtime 1002 1003 # binaries and kube-system manifests 1004 log-wrap 'InstallKubeBinaryConfig' install-kube-binary-config 1005 1006 echo "Done for installing kubernetes files" 1007 log-end 'ConfigureMain'