无痛部署个人学习&测试用Kubernetes + Ceph集群

服务器

浏览数:36

2020-6-24

AD:资源代下载服务

最后买了一门课程,系统的学习一下Kubernetes。

根据课程,同时也参考了网上其他的资料,安装配置了一个本地K8S集群,这里记录一下。
课程中使用K8S为1.11.1版本,看了下课程发布时间:2018-09-17,已经有一年多了,现在(2020-02)K8S最新版本为1.17.3。考虑课程学习的一致性,本打算安装1.11.1版,可惜在Ubuntu和CentOS中都没有找到kubeadm的1.11.1版本,无奈这里安装了最新版本。
结过一番折腾总算安装成功,总结+改良操作过程之后发现得此记录。按此记录的操作流程执行比较顺利,不用怎么关心国内的网络限制,可以一气呵成的搭建一套本地学习&测试用的集群(而且是软件仓库中提供的最后版本),配合Rook项目,Ceph存储也加上了。好东西不能一人独赏,特拿来与诸君分享。

安装环境

我是在自己的MBP 2016上启动了三个Fusion虚拟机作为K8S节点的,我的MBP有16G内存,以此为前提,各节点推荐配置如下:

k8s node CPU cores RAM Disk size Hostname IP
master 4 4G 128G k8s-master 172.16.151.10
worker 2 4G 128G k8s-worker1 172.16.151.11
worker 2 4G 128G k8s-worker2 172.16.151.12

其中 master 至少要3G内存,2 CPU core(按我的MBP 16的CPU性能来说),再少master节点就会比较卡顿;worker节点可以减少内存到2G,在Pod数量比较少的情况下影响不大(就我目前的学习进度来说,还没有卡过)。磁盘空间按课程内容,要30G以上,这里因为是动态分配的虚拟磁盘, 我就一步到位加到128G了。
网络模式使用自定义模式:相当于host-only + NAT转发,将主机加入虚拟网络。虚拟网络网关和DHCP服务器为172.16.151.2,主机在虚拟网络中IP为172.16.151.1
注意在为虚拟机安装系统时,配置网络为ipv4/manual/static以固定IP地址。
虚拟机的操作系统选用了Ubuntu 18.04 X64系统,安装过程就不赘述了。

配置各节点环境,准备安装

为了方便后续操作,先在本地工作目录(如~/k8s-install) 中为各节点创建IP文件

echo '172.16.151.10' > ip-m1

echo '172.16.151.11' > ip-w1

echo '172.16.151.12' > ip-w2

使用这些IP文件我们就可以以如下方式批量操作这些节点了:

for IP_FILE in $(ls ip-*); do
  IP=$(cat $IP_FILE)
  echo "IP of $IP_FILE: $IP"
  #do anything with $IP here
  unset IP
done && unset IP_FILE

之后我为各虚拟机节点执行了以下配置:

配置root ssh登录

因为Ubuntu默认禁止root用户使用用户名密码登录,我们只能第一步配置ssh key登录安装系统时创建的普通用户。

for IP_FILE in $(ls ip-*); do
  IP=$(cat $IP_FILE)
  ssh-copy-id -i ~/.ssh/rsa.pub ubuntu@$IP # 此操作需要输入虚拟机用户ubuntu的密码
  ssh ubuntu@$IP # 以用户ubuntu的身份免密登录到虚拟机上,执行后面说明的操作
  unset IP
done && unset IP_FILE

在各节点上登录ubuntu执行的操作:

sudo mkdir -p /root/.ssh && sudo cp ~/.ssh/authorized_keys /root/.ssh/

配置apt源

为了加快下载速度,这时使用阿里云的kubernetes、docker-ce镜像仓库。

for IP_FILE in `ls ip-*`; do
  IP=`cat $IP_FILE`
  ssh root@$IP "
    apt update -y && apt upgrade -y && apt autoremove -y && apt install -y apt-transport-https ca-certificates curl software-properties-common
    curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | apt-key add - 
    cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
    curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
    cat <<EOF >/etc/apt/sources.list.d/docker-ce.list
deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \$(lsb_release -cs) stable
EOF
    apt update -y
  "
  unset IP
done && unset IP_FILE

安装

安装 Docker CE

for IP_FILE in `ls ip-*`; do
  IP=`cat $IP_FILE`
  ssh root@$IP "
    apt -y install docker-ce
    systemctl enable docker
    cat > /etc/docker/daemon.json <<EOF
{
  \"registry-mirrors\":[\"https://dockerhub.azk8s.cn\"],
  \"exec-opts\": [\"native.cgroupdriver=systemd\"],
  \"log-driver\": \"json-file\",
  \"log-opts\": {
    \"max-size\": \"100m\"
  },
  \"storage-driver\": \"overlay2\"
}
EOF
    mkdir -p /etc/systemd/system/docker.service.d
    systemctl daemon-reload
    systemctl restart docker
  "
  unset IP
done && unset IP_FILE

安装 kubeadm 工具

for IP_FILE in `ls ip-*`; do
  IP=`cat $IP_FILE`
  ssh root@$IP "
    apt install -y kubeadm # 这里会依赖安装 kubelet、kubectl
    systemctl enable kubelet
    systemctl start kubelet
  "
  unset IP
done && unset IP_FILE

准备k8s镜像

课程要求我们准备可以访问gcr.io的环境,但由于众所周知的原因,身处国内无法直接访问gcr.io镜像仓库,这里使用Azure 中国镜像的方案变通解决。

for IP_FILE in `ls ip-*`; do
  IP=`cat $IP_FILE`
  ssh root@$IP "
      kubeadm config images list 2>/dev/null|grep ^k8s\\.gcr\\.io|awk -F '/' '{print \$2}' - | while read imageName ; do
      docker pull gcr.azk8s.cn/google_containers/\$imageName
      docker tag gcr.azk8s.cn/google_containers/\$imageName k8s.gcr.io/\$imageName
      docker rmi gcr.azk8s.cn/google_containers/\$imageName
    done
  "
  unset IP
done && unset IP_FILE

解释下上面在节点上执行的脚本:

  • kubeadm config image list 会列出k8s必须的镜像。因为无法访问 grc.io ,该指令会输出一行错误到 stderr,使用 2>/dev/null 清除之
  • 输出的镜像tag列表,经过 grep 和 awk 取出 imageName,如 k8s.gcr.io/pause:3.1 取出 imageName 为 pause:3.1
  • 对每行取出 imageName 通过 Azure中国镜像代理pull、tag获得最终需要的docker image

禁用linux swap

kubernetes要求禁用linux swap,否则后继执行kubeadm指令时,preflight检查会失败

for IP_FILE in `ls ip-*`; do
  IP=`cat $IP_FILE`
  ssh root@$IP "
    swapoff -a
    sysctl -w vm.swappiness=0
    cp /etc/fstab /etc/fstab.origin
    sed -i '/swap/s/^/#/' /etc/fstab
  "
  unset IP
done && unset IP_FILE

使用 kubeadm init 配置 master 节点

ssh root@`cat ip-m1` " kubeadm init --token-ttl 0 " # 使用--token-ttl 0是为了之后方便添加节点,kubeadm不适合用于生产环境部署,测试/开发/学习用环境 token 永不过期应该也没多大问题,如果要用 kubeadm 部署生产环境,不要设置为永不过期

上述操作最后会输出一行kubeadm join指令,将这条指令保存为本地工作目录的kubeadm-join.sh文件备用

到此,虽然kubernetes集群还没有完成安装,但已经可以通过kubectl进行交互了。

下载 kubectl 配置文件

下面的操作假设你本地默认位置没有其他kubernetes集群的kubeconfig配置文件,如果有请自行备份。当然也有方法进行合并,合并的方法我会再另外的文章中说明。

mkdir -p ~/.kube

scp root@`cat ip-m1`:/etc/kubernetes/admin.conf ~/.kube/config

安装 CNI 插件

现在,使用kubectl已经能和k8s集群交互了,但通过kubectl get nodes命令可以发现,现在唯一的节点k8s-masterNotReady的状态。这是因为我们没有安装CNI容器网络插件。
Installing Addons页面列出了一些可用的插件,其中“Networking and Network Policy”小节列出了网络插件,这里我们按课程内容选用了“Weave Net”插件。

kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

等待一段时间,kubectl get nodes命令就会返回k8s-master节点是Ready状态了。

使用 kubeadm join 配置 worker 节点

for IP_FILE in `ls ip-w*`; do
  IP=`cat $IP_FILE`
  ssh root@$IP < kubeadm-join.sh
  unset IP
done && unset IP_FILE

等待各worker节点的Pod都启动之后,k8s集群就安装完成了。

配置&增强

在安装完集群之后,我们还需要一些配置:安装dashboard、部署存储插件等

删除 master 上的 Taint

默认情况下,master节点上会带有一个Taint:node-role.kubernetes.io/master:NoSchedule。如果新Pod没有对应的Toleration规则,就不会被kubernetes调度到master节点上。为了能利用master的资源,我们可以把这个Taint清除掉。

kubectl taint node --all node-role.kubernetes.io/master-

其实在本地的集群节点中,为了节约主机内存我是想只保留master节点的,仍然保留worker节点主要是为了保证后面要部署的
rook-ceph插件有足够的OSD节点数。

安装部署 Rook Ceph 插件

git clone --single-branch --branch release-1.2 https://github.com/rook/rook.git

kubectl create -f rook/cluster/examples/kubernetes/ceph/common.yaml

sed '/ROOK_ENABLE_FLEX_DRIVER/{N;s/false/true/;P;D;}' rook/cluster/examples/kubernetes/ceph/operator.yaml > rook/cluster/examples/kubernetes/ceph/operator.flex_enabled.yaml # 编辑 operator.yaml 设置 ROOK_ENABLE_FLEX_DRIVER 为 true ,否则 pod 无法挂载 volume。

kubectl create -f rook/cluster/examples/kubernetes/ceph/operator.flex_enabled.yaml

kubectl create -f rook/cluster/examples/kubernetes/ceph/cluster-test.yaml

部署 dashboard

安装

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-rc1/aio/deploy/recommended.yaml

登录 dashboard

创建登录用户,这里创建的是管理员权限用户,生产环境的话要谨慎操作

cat <<EOF | kubectl apply -f - # 创建SA
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
EOF

cat <<EOF | kubectl apply -f - # 通过ClusterRoleBinding为SA: admin-user绑定cluster-admin权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
EOF

kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret | grep admin-user | awk '{print $1}') | grep ^token\: | awk '{print $2}' > _token.txt # 取出admin-user的token保存

kubectl config --kubeconfig dashboard-admin.conf set-cluster cluster-name # 空的cluster即可,不需要 ca也不需要server

kubectl config --kubeconfig dashboard-admin.conf set-credentials dashboard-admin --token=`cat _token.txt | tr -d '\n'` # 配置user with token

rm -f _token.txt

kubectl config set-context dashboard-admin@cluster-name --kubeconfig dashboard-admin.conf --user dashboard-admin --cluster cluster-name

kubectl config --kubeconfig dashboard-admin.conf use-context dashboard-admin@cluster-name

登录 dashboard

使用kubectl proxy命令打开本地代理,之后访问http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/即可打开dashboard页面。在登录页面,使用 kubeconfig方式 + 前面生成的 dashboard-admin.conf 文件进行登录即可。

目前已知问题

  • PV可创建,也成功与PVC绑定,但Pod无法挂载Volume。本人不了解Ceph存储,可能是配置PV的方式不正确。报错如下:
Events:
  Type     Reason                  Age                 From                     Message
  ----     ------                  ----                ----                     -------
  Warning  FailedScheduling        47s (x5 over 2m8s)  default-scheduler        error while running "VolumeBinding" filter plugin for pod "pv-pod": pod has unbound immediate PersistentVolumeClaims
  Normal   Scheduled               38s                 default-scheduler        Successfully assigned default/pv-pod to k8s-master
  Normal   SuccessfulAttachVolume  38s                 attachdetach-controller  AttachVolume.Attach succeeded for volume "pv-volume"
  Warning  FailedMount             4s (x7 over 36s)    kubelet, k8s-master      MountVolume.WaitForAttach failed for volume "pv-volume" : fail to check rbd image status with: (executable file not found in $PATH), rbd output: ()

不过使用StorageClass的进行PVC自动创建绑定PV可以正常工作,应该是手动配置PV的配置没有写对造成的吧。

参考:

作者:Dowen