零基础手动搭建 k8s 那点事

工具 2021-03-14 13679 字 2265 浏览 点赞

起步

这是我第二次手动搭建 k8s 了,相较于第一次用掉一天时间,这次花费半天。当然,这其中倒不全是 k8s 的问题。是网络。而网络又是一个很大的概括,更细分则是:k8s 需要的镜像国内拉取不下来;需要的 yaml 文件下载不下来;B 电脑里的虚拟机的端口在 A 电脑上访问不到。

事实上我遇到的问题可以通过代理和 virtual box 的端口映射解决,但我没有。我很庆幸,这个“没有”让我明明才接触 k8s,却学到了一些故障排查方式,以及对 yaml 文件的部分 key 值有所了解,还学到了 ssh 端口转发。赚大发了。

以下是整个流程的记录,半数在过程中记下,半数靠成功之后回忆所来,若有纰漏,望不吝赐教。

环境准备

(这一小节中的操作,需要在所有参与集群的服务器上操作)

先说说我的环境准备。

我在笔记本电脑 B 上,用虚拟机起了三个 ubuntu 16 server 服务。其中,k1 作为 master,分配了 2g 内存 2核,k2、k3 是普通 node,同为 1g 1核。要求三台机器可以相互 ping 通。

普通用户执行 sudo passwd root 修改 root 密码,允许 root 用户 ssh 登陆则需要大家自行百度。我均以 root 账户操作,可以避免一些不必要的麻烦。

关闭 swap

k8s 要求 linux 关闭 swap 功能。打开 /etc/fstab 文件,注释最后一行内容:

$ vim /etc/fstab
# 注释以下内容
# /dev/mapper/ubuntu--vg-swap_1 none            swap    sw              0       0

$ reboot  # 重启服务器

安装 docker

添加 docker 源:

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
$ add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
$ apt update

使用 apt-cache madison docker-ce 查询可安装版本。这里我选择了 17.03.3~ce-0~ubuntu-xenial:

$ apt install docker-ce=17.03.3~ce-0~ubuntu-xenial

安装 kubeadm、kubelet、kubectl

添加 k8s 国内源:

$ curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add -
$ echo "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main" >> /etc/apt/sources.list.d/kubernetes.list
$ apt update

安装相关工具:

$ apt install -y kubelet kubeadm kubectl

安装成功后可通过 kubeadm version 查看版本,我的环境输出如下:

kubeadm version: &version.Info{
  Major:"1", Minor:"20", GitVersion:"v1.20.4",
  GitCommit:"e87da0bd6e03ec3fea7933c4b5263d151aafd07c", 
  GitTreeState:"clean", BuildDate:"2021-02-18T16:09:38Z", 
  GoVersion:"go1.15.8", Compiler:"gc", Platform:"linux/amd64"
}

初始化 master 节点

k8s 需要一个 master 节点,这里我选择 k1,也就是 2g 2核 那个环境。

初始化 k8s 需要一些镜像,我们可以先执行 kubeadm config images list 查看所需镜像:

k8s.gcr.io/kube-apiserver:v1.20.4
k8s.gcr.io/kube-controller-manager:v1.20.4
k8s.gcr.io/kube-scheduler:v1.20.4
k8s.gcr.io/kube-proxy:v1.20.4
k8s.gcr.io/pause:3.2
k8s.gcr.io/etcd:3.4.13-0
k8s.gcr.io/coredns:1.7.0

k8s.gcr.io 开头的镜像不用代理拉不下来,这里可以用 --image-repository 指定阿里镜像源。初始化命令如下:

$ kubeadm init \
  --image-repository registry.aliyuncs.com/google_containers \
  --apiserver-advertise-address=10.0.2.15 \
  --pod-network-cidr=192.168.16.0/20
  • --apiserver-advertise-address 指定了 k1 的 ip 地址,不同环境对应的值不同
  • --pod-network-cidr 用于 k8s 中的 pod 通信,如果你不懂什么意思,就跟着我设相同的值

执行成功之后,你能得到以下输出:

Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.0.2.15:6443 --token f6zc7w.jlz3kvks5vlgjqt1 \
    --discovery-token-ca-cert-hash sha256:bbac8cc746c186f569502edbdd680460e9825cf9d9050b44af4cfc1279f443ca

这段输出有两个点需要关注,第一个是 To start using your cluster,如果想通过 kubectl 控制集群,需要做一些小操作。因为我用的是 root 用户,选择第二种方式,操作如下:

$ vim ~/.bashrc
# 追加以下内容,并保存退出:
# export KUBECONFIG=/etc/kubernetes/admin.conf

$ source ~/.bashrc

不出意外的话,执行 kubectl get nodes 能看到如下输出:

NAME   STATUS   ROLES                  AGE   VERSION
k1     Ready    control-plane,master   19h   v1.20.4

第二个是 kubeadm join 这串内容,很重要,把它记下来,之后节点加入集群需要用到:

kubeadm join 10.0.2.15:6443 --token f6zc7w.jlz3kvks5vlgjqt1 \
    --discovery-token-ca-cert-hash sha256:bbac8cc746c186f569502edbdd680460e9825cf9d9050b44af4cfc1279f443ca

安装网络插件

k8s 的网络插件有很多选择,这里我选择 Weave Net。

# 1. 下载 weave.yaml
$ curl -L "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')" > weave.yaml

# 2.
$ vim weave.yaml
# 找到 containers.env 添加“IPALLOC_RANGE”和“192.168.16.0/20”,
# 后者对应 --pod-network-cidr 指定的值
#          containers:
#            - name: weave
#              command:
#                - /home/weave/launch.sh
#              env:
#                - name: HOSTNAME
#                  valueFrom:
#                    fieldRef:
#                      apiVersion: v1
#                      fieldPath: spec.nodeName
#                - name: IPALLOC_RANGE      ## add content
#                  value: 192.168.16.0/20   ## add content

# 3. 创建网络插件
$ kubectl apply -f weave.yaml

之后一般会执行 kubectl -n kube-system get pods 查看 pod 的状态,如果 name 为 weave-net-* 的 pod 的 status 是 Init,可能是需要的镜像拉不下来(但事实上,可能只是你的网络暂时波动,等一会就会好)。

查看 Weave Net 依赖的镜像:

$ grep 'image' ./weave.yaml -n

加入 Node

在 k2, k3 上执行 kubeadm join:

kubeadm join 10.0.2.15:6443 --token f6zc7w.jlz3kvks5vlgjqt1 \
    --discovery-token-ca-cert-hash sha256:bbac8cc746c186f569502edbdd680460e9825cf9d9050b44af4cfc1279f443ca

如果不出问题,只需要等一会儿就能在 k1 上就能看到 k2、k3 节点了。

root@k1:~# kubectl get nodes
NAME   STATUS   ROLES                  AGE   VERSION
k1     Ready    control-plane,master   9h    v1.20.4
k2     Ready    <none>                 84s   v1.20.4
k3     Ready    <none>                 53s   v1.20.4

此时在 k2、k3 上想查看集群信息,没得搞:

root@k2:~# kubectl get nodes
The connection to the server localhost:8080 was refused - did you specify the right host or port?

解决办法是:

# 在 k1 master 上执行
$ sudo scp /etc/kubernetes/admin.conf root@10.0.2.4:/etc/kubernetes/
# root@10.0.2.4 是我 k2 的 用户和 ip

# 以下命令切换到 k2 上执行
$ export KUBECONFIG=/etc/kubernetes/admin.conf  # 如果需要持久化,就把该命令写入 bashrc 文件中
$ kubectl get nodes  # 可查看到集群中的节点信息

dashboard

我们想在界面上操作 k8s 集群,需要安装面板插件。

安装 dashboard

下载 dashboard yaml 文件:

$ wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml

通常来说,这个 yaml 文件是下载不下来的。我的解决方案是在浏览器中打开 url,复制所有内容,vim 打开 dashboard.yaml,粘贴进去,保存退出。终端执行:

$ kubectl apply -f dashboard.yaml

查看 dashboard 运行状态:

$ kubectl -n kube-system get pods --all-namespaces

本来安装 dashboard 应该就这样简单,但是无奈出现了意外。namespace 下 kubernetes-dashboard 对应的 status 不是 running,而且一直不是 running,最后报了 ErrImagePull

执行以下命令可以看到 pod 中更详细内容:

$ kubectl -n kube-system describe pod dashboard-metrics-scraper-7c6ff46d6d-d7pth --namespace=kubernetes-dashboard
  • kubectl -n kube-system describe pod 是固定格式,不用变。
  • dashboard-metrics-scraper-7c6ff46d6d-d7pth 是 pod 的 name,按需修改。
  • --namespace=kubernetes-dashboard 指定该 pod 所在的命名空间。

最后发现了以下错误。

Error response from daemon: Get https://registry-1.docker.io/v2/: dial tcp: lookup registry-1.docker.io on 192.168.31.1:53: read udp 192.168.16
.1:46098->192.168.31.1:53: i/o timeout

一番搜索之后说的是 DNS 问题,需要打开 /etc/resolv.conf 文件追加以下内容:

nameserver 8.8.4.4

因为我在 k1 上操作,保存之后就在 k1 上拉取 dashboard 需要的镜像,发现可以拉下来。

$ docker pull kubernetesui/dashboard:v2.2.0
$ docker pull kubernetesui/metrics-scraper:v1.0.6

之后执行 kubectl delete -f dashboard.yaml 删除 pod(其实不需要这一步),再 apply,最后 get pods 查看 pod 状态,这回 kubernetes-dashboard-xxx 正常了,但 dashboard-metrics-scraper-xxx 还是镜像拉取错误。

我承认我在这里懵逼了好一会,最后通过 describe pod 发现了问题。

在我的机器上,describe pod kubernetes-dashboard-xxx 输出如下:

Normal Scheduled  2m23s  default-scheduler Successfully assigned kubernetes-dashboard/kubernetes-dashboard-7cb9fd9999-b7l65 to k1
  • to k1

而 describe pod dashboard-metrics-scraper-xxx 输出如下:

Normal Scheduled 30s default-scheduler Successfully assigned kubernetes-dashboard/dashboard-metrics-scraper-7c6ff46d6d-d7pth to k2
  • to k2

也就说,kubernetes-dashboard-xxx 被调度到了 k1 上,k1 添加过 DNS 服务器,能成功拉取镜像,所以可以正常跑。但 k2 上没有做过这些操作,dashboard-metrics-scraper-xxx 被调度到了 k2 上,所以拉取镜像错误。

创建用户与角色

k8s 的风格就是 “用 yaml 说话”。所以我们需要编写 user.yaml,用来创建用户;编写 role.yaml,用来给用户赋予角色。

user.yaml:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: guan  # 注意用户名
  namespace: kube-system

role.yaml:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dashboard:guan
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: guan
  namespace: kube-system

执行:

$ kubectl create -f user.yaml
$ kubectl create -f role.yaml

访问 dashboard

一般来说,会执行 kubectl proxy开启代理,通过代理访问 dashboard。我在 master 节点上执行命令如下:

$ kubectl proxy
Starting to serve on 127.0.0.1:8001

再访问登陆 url:http://127.0.0.1:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login,得到一个登陆页面如下:


是的,本来就应该这么简单,但是出了意外。

在我创建 k1、k2、k3 三个虚拟机时,我在 virtual box 上新建了一张 10.0.2.0/24 网卡,选择 NAT,并在宿主机上端口映射了 9990、9991、9992 到 k1、k2、k3 的 22 端口。最后导致的结果是:宿主机 ping 不同虚拟机,虚拟机 ping 不同宿主机。ssh 登陆全靠宿主机上的端口映射。

代理是在 k1 上启动的,在 k1 上执行 curl 127.0.0.1:8001 能看到一串 url,成功近在咫尺,却又遥不可及。

当然,如果你现在选择用 virtual box 再端口映射出 k1 的 8001 端口,问题马上得到解决。可我偏不

事实是,我是在笔记本电脑 B 上用 virtual box 起的三个虚拟机,但操作都是在电脑 A 上,所以我的目的是在 A 上可以成功访问登陆 url。

花了不少时间,但问题解决了,我是用 ssh 做端口转发。命令如下:

# 在电脑 A 上执行
$ ssh -L 8001:localhost:8001 guan@192.168.31.200 -t ssh -L 8001:localhost:8001 root@127.0.0.1 -p 9990

电脑 B 的地址是:guan@192.168.31.200。ssh -L 8001:localhost:8001 guan@192.168.31.200 的意思是,把电脑 B 的 8001 端口映射到电脑 A 的 8001 端口上。

k1 的地址是:root@127.0.0.1 -p 9990 (在电脑 B 上执行)。-t ssh -L 8001:localhost:8001 root@127.0.0.1 -p 9990 的意思是,在前面的基础上,把 k1 的 8001 端口映射到宿主机——电脑 B——的 8001 端口上。于是 k1 8001 --映射--> B 8001 -映射--> A 8001。

大功告成!

获取 token

想要登陆 dashboard 需要 token,这很简单。我之前创建的用户是 guan,所以:

$ kubectl -n kube-system get secret | grep guan
# 得到 NAME:guan-token-bhdnw

$ kubectl -n kube-system describe secret/guan-token-bhdnw
# 从输出中得到 token

安装监控

我们想要知道集群中每个节点的 cpu、内存使用情况,这时候就可以安装插件 metrics server。一定要放弃 heapster,我折腾了好久,历经千难万险,最后发现插件报接口找不到,应该是最新版的 k8s 不支持了。

下载 yaml 文件:

$ wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

打开文件,添加 --kubelet-insecure-tls:

- name: metrics-server
        image: registry.cn-shenzhen.aliyuncs.com/carp/metrics-server-amd64:0.3.1
        imagePullPolicy: IfNotPresent
        args:
          - --cert-dir=/tmp
          - --secure-port=4443
          - --kubelet-insecure-tls  ## add content

很好,metrics server 依赖的镜像又是国内拉不下来的那种 (grep 'image' components.yaml)。我用 docker search 找了一下,hub 里就发现一个 v0.4.1 的版本,但是问题不大。

$ docker search metrics-server:v0.4.1
NAME                      DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
phperall/metrics-server   k8s.gcr.io/metrics-server/metrics-server:v...   0
wdhcn/metric-server       From k8s.gcr.io/metrics-server/metrics-ser...   0

$ docker pull phperall/metrics-server:v0.4.1
# 将镜像重新打 tag, 改成 components.yaml 依赖的版本
$ docker tag phperall/metrics-server:v0.4.1 k8s.gcr.io/metrics-server/metrics-server:v0.4.2

应用 yaml:

$ kubectl apply -f components.yaml

过一会执行 kubectl top 就能看到每个节点的资源使用情况了。

$ kubectl top node
NAME   CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
k1     103m         5%     1203Mi          63%
k2     12m          1%     458Mi           51%
k3     13m          1%     464Mi           52%

你在 dashboard 页面上 “集群 - Nodes” 中也能看到。

出错了怎么办?

我安装是跟着教程走的,本以为很简单,却偏偏各种意外,执行命令 A 之后总得不到教程中 A 之后的结果。最后是各种百度解决了问题。

排错时常用到的命令如下:

# 针对命令 kubectl create/apply 的后悔操作
$ kubectl delete -f xxx.yaml

# 查看所有 pod 状态
$ kubectl -n kube-system get pods --all-namespaces

# 查看某个 pod 详情
$ kubectl -n kube-system describe pod [pod name] --namespace=[namespace]

# 查看某个 pod 日志
$ kubectl -n kube-system logs [pod name] --namespace=[namespace]

# 针对命令 kubeadm init/join 的后悔操作
$ kubeadm reset

感谢

参考,包括但不仅限于此:



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

2 条评论

  1. tflins
    tflins

    现在写一篇博文要花多长时间?感觉现在自己越来越不行了,憋半天憋不出几个字。

    1. Guan
      Guan

      不好说,这篇还是写得比较久,里面的内容反复核对过几次。

添加新评论