「Kubernetes」- 在集群初始化时,如果无法拉取官方镜像怎么办?

问题描述

当使用 kubeadm 命令初始化集群时,会去官方镜像仓库(k8s.gcr.io)拉取镜像。但是国内网络无法访问官方镜像仓库,导致集群初始化失败;

该笔记将记录:在 Kubernetes 集群初始化时,拉取官方镜像的方法;

解决方案

简而言之,我们推荐使用如下方式:

通用场景:crane, gcrane, krane

通过 crane cp 命令,能够将镜像复制到自建仓库中:

crane auth login -u username -p password registry.example.com

crane cp                                                   \
    registry.k8s.io/ingress-nginx/controller:v1.2.1        \
    registry.example.com/ingress-nginx/controller:v1.2.1

# 在 Harbor 中,crane 无法使用多级镜像路径,即 registry.example.com/a/b/c/ingress-nginx/controller:v1.2.1 将失败;

特性特征:
1)能够识别 HTTP_PROXY HTTPS_PROXY NO_PROXY 等等环境变量;
2)无需运行 docker 服务;

复制镜像,并保存摘要(镜像摘要发生变化)

go-containerregistry/crane.md at main · google/go-containerregistry · GitHub

我们现在发现的问题是
1)通过 docker inspect --format='{{index .RepoDigests 0}}' $IMAGE 会发现多个摘要;
2)当推送镜像后,将仅保留当前仓库的 Digest 信息,而原始仓库的 Digest 信息将不会被推送到当前仓库中;

通过 crane cp 命令,能够解决该问题;

针对 Kubernetes 集群:新版集群(推荐)

在使用 kubeadm v1.14 初始化集群时,我们无意间发现它支持 --image-repository 选项,该选项能够指定镜像仓库地址:

kubeadm init --image-repository registry.aliyuncs.com/google_containers

如果通过配置文件初始化集群,能够使用 imageRepository 字段

针对 Kubernetes 集群:手动拉取镜像

我学到的首个方法是手动拉取镜像,然后在本地打标签。因为如果镜像存在于本地,则 kubeadm 将不再去官方拉取镜像,因此该方法可行;

第一步、查看需要拉取的镜像

首先需要知道拉取那些镜像:

#!/bin/sh

kubeadm config images list

# 而上述命令会卡顿,因为它需要访问 dl.k8s.io 地址获取版本。你可以手动指定版本:
kubeadm config images list --kubernetes-version 1.12.10

命令会产生类似如下输出:

k8s.gcr.io/kube-apiserver:v1.12.10
k8s.gcr.io/kube-controller-manager:v1.12.10
k8s.gcr.io/kube-scheduler:v1.12.10
k8s.gcr.io/kube-proxy:v1.12.10
k8s.gcr.io/pause:3.1
k8s.gcr.io/etcd:3.2.24
k8s.gcr.io/coredns:1.2.2

第二步、拉取目标镜像到本地

根据上面输出,我们已经得知所有需要拉取的镜像。接下来我们以kube-apiserver:v1.12.10为例;

在 Docker Hub 中(或者其他地方)找到kube-apiserver:v1.12.10镜像,并拉取到本地:

# 我们随便拉取一个
docker pull imdingtalk/kube-apiserver:v1.12.10

第三步、使用官方镜像标签

docker tag imdingtalk/kube-apiserver:v1.12.10 k8s.gcr.io/kube-apiserver:v1.12.10

第四步、推送到私有仓库(可选)

建议将镜像推送到本地私有奖项仓库,预备日后使用。另外,以可以防止官方镜像无法访问等问题;

拉取“官方镜像”到本地,并推送私有仓库

以下脚本用于处理与镜像有关的问题:
1)将官方镜像拉取到本地,解决镜像无法拉取问题;
2)将镜像推送到私有仓库,进行备份;

通常某个用户会提供所有相关镜像。可以使用如下脚本从 imdingtalk 中拉取所有镜像,并创建标签:

dockerhub_username="imdingtalk"
kubernetes_version="1.12.10"
private_registry_prefix="10.10.50.4/k8s"

for img in $(kubeadm config images list --kubernetes-version "${kubernetes_version}")
do
	echo "### $img"

	# 获取原始镜像名
    img_name=$(echo $img | sed 's%k8s.gcr.io/%%g')

    # 从 Docker Hub 中拉取镜像
    docker pull "${dockerhub_username}/${img_name}"

    # 为镜像创建新的标签
    docker tag "${dockerhub_username}/${img_name}" "k8s.gcr.io/${img_name}"

    # 为了防止官方镜像日后无法访问,我们有必要对镜像进行备份:
    docker tag "${dockerhub_username}/${img_name}" "${private_registry_prefix}/${img_name}"
    docker push "${private_registry_prefix}/${img_name}"

done

针对 Kubernetes 集群:旧版方法(使用旧版集群)

对于旧版集群,比如 1.12.1 版本。现在需要升级集群,它不支持 --image-repository 选项,因此你可以使用“方法一”。但是呢,我们还有其他办法,可能比较复杂,但更加通用;

第一步、导出默认配置

当我们执行kubeadm init时,集群便开始初始化。但是在内部它有默认配置文件,该配置文件可以使用kubeadm config print-default命令查看(新版需要使用kubeadm config print命令)。然后在配置文件中搜索imageRepository字段。是的,这就是默认镜像仓库地址;

第二步、修改默认配置

将上面imageRepository字段修改为可用的镜像仓库,保存并退出;

第三步、使用配置文件

在 kubeadm 中,很多子命令(config、init、upgrade)都支持--config选项,而该选项参数则为前面的配置文件;

比如集群升级命令,我们使用修改过的配置文件:

kubeadm upgrade apply v1.12.10 --config kubeadm-config.default.v1alpha3-aliyun

(当然,我们仅修改imageRepository字段)