为了账号安全,请及时绑定邮箱和手机立即绑定

使用kubeadm安装kuberneters/部署前准备/flannel网络插件/镜像下载/

标签:
Java

本文内容参考《kuberneters进阶实战》/马哥的新书/推荐

部署前的准备

主机名称解析

分布式系统环境中的多主机通信通常基于主机名称进行,这在IP地址存在变化的可能性时为主机提供了固定的访问入口,因此一般需要有专用的DNS服务负责解决各节点主机名。不过,考虑到此处部署的是测试集群,因此为了降低系统的复杂度,这里将采用基于hosts的文件进行主机名称解析

master节点和nodes都需要修改hosts文件

192.168.18.64 master192.168.18.65 node1192.168.18.66 node2192.168.18.67 node3
主机时间同步

如果使用的是云服务器,一般云厂商都已经设置好了

关闭防火墙

各Node运行的kube-proxy组件均要借助iptables或ipvs构建Service资源对象,该资源对象是Kubernetes的核心资源之一。出于简化问题复杂度之需,这里需要事先关闭所有主机之上的iptables或firewalld服务

# systemctl stop iptables.service# systemctl disable iptables.service
关闭并仅用SELinux
禁用swap设备 (可选)

kubeadm默认会预先检查当前主机是否禁用了Swap设备,并在未禁用时强制终止部署过程。因此,在主机内存资源充裕的条件下,需要禁用所有的Swap设备。

# 关闭swap设备swapoff -a

然后编辑/etc/fstab配置文件,另外部署时也可以不禁用swap设备,可在使用kubeadm命令时额外使用相关命令忽略检查错误

启用ipvs内核模块 (可选)

Kubernetes1.11之后的版本默认支持使用ipvs代理模式的Service资源,但它依赖于ipvs相关的内核模块,而这些模块默认不会自动载入。因此,这里选择创建载入内核模块相关的脚本文件/etc/sysconfig/modules/ipvs.modules,设定于系统引导时自动载入的ipvs相关的内核模块,以支持使用ipvs代理模式的Service资源。文件内容如下:

#!/bin/bash ipvs_mods_dir="/usr/lib/modules/$(uname -r)/kernel/net/netfilter/ipvs" for i in $(ls $ipvs_mods_dir | grep -o "^[^.] *"); do 
    /sbin/modinfo -F filename $i &> /dev/null 
    if [ $ -eq 0 ]; then 
        /sbin/ modprobe $i 
    fi done

然后修改文件权限,并手动为当前系统环境加载内核

chmod +x /etc/sysconfig/modules/ipvs.modules/etc/sysconfig/modules/ipvs.modules

镜像下载

为啥要将镜像单独准备,因为这个门槛越不过去,你会遇到很多莫名其妙的问题(对于新手来讲)

以下是必须的镜像和正在运行的docker容器

[root@master ~]# docker images
REPOSITORY                                                               TAG                 IMAGE ID            CREATED             SIZE
k8s.gcr.io/kube-proxy                                                    v1.13.1             fdb321fd30a0        4 weeks ago         80.2MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-proxy-amd64                v1.13.1             fdb321fd30a0        4 weeks ago         80.2MB
k8s.gcr.io/kube-apiserver                                                v1.13.1             40a63db91ef8        4 weeks ago         181MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-apiserver-amd64            v1.13.1             40a63db91ef8        4 weeks ago         181MB
k8s.gcr.io/kube-scheduler                                                v1.13.1             ab81d7360408        4 weeks ago         79.6MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-scheduler-amd64            v1.13.1             ab81d7360408        4 weeks ago         79.6MB
k8s.gcr.io/kube-controller-manager                                       v1.13.1             26e6f1db2a52        4 weeks ago         146MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-controller-manager-amd64   v1.13.1             26e6f1db2a52        4 weeks ago         146MB
k8s.gcr.io/coredns                                                       1.2.6               f59dcacceff4        2 months ago        40MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/coredns                         1.2.6               f59dcacceff4        2 months ago        40MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/etcd-amd64                      3.2.24              3cab8e1b9802        3 months ago        220MB
k8s.gcr.io/etcd                                                          3.2.24              3cab8e1b9802        3 months ago        220MB
quay.io/coreos/flannel                                                   v0.10.0-amd64       f0fad859c909        11 months ago       44.6MB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/flannel                         v0.10.0-amd64       f0fad859c909        11 months ago       44.6MB
k8s.gcr.io/pause                                                         3.1                 da86e6ba6ca1        12 months ago       742kB
registry.cn-hangzhou.aliyuncs.com/mmyk8s/pause                           3.1                 da86e6ba6ca1        12 months ago       742kB
[root@master ~]# docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS               NAMES36d09d27fd26        f59dcacceff4           "/coredns -conf /etc…"   8 hours ago         Up 8 hours                              k8s_coredns_coredns-86c58d9df4-cm4pm_kube-system_6e51adc0-14c0-11e9-ac10-000c29bed493_005bb6e3b85e9        f59dcacceff4           "/coredns -conf /etc…"   8 hours ago         Up 8 hours                              k8s_coredns_coredns-86c58d9df4-g2mf4_kube-system_6e50f388-14c0-11e9-ac10-000c29bed493_051b284840974        k8s.gcr.io/pause:3.1   "/pause"                 8 hours ago         Up 8 hours                              k8s_POD_coredns-86c58d9df4-cm4pm_kube-system_6e51adc0-14c0-11e9-ac10-000c29bed493_0
b3e5b80bb640        k8s.gcr.io/pause:3.1   "/pause"                 8 hours ago         Up 8 hours                              k8s_POD_coredns-86c58d9df4-g2mf4_kube-system_6e50f388-14c0-11e9-ac10-000c29bed493_05f50cad2717e        f0fad859c909           "/opt/bin/flanneld -…"   8 hours ago         Up 8 hours                              k8s_kube-flannel_kube-flannel-ds-amd64-7xh79_kube-system_ad897b0d-14c0-11e9-ac10-000c29bed493_073bb596c654e        3cab8e1b9802           "etcd --advertise-cl…"   23 hours ago        Up 23 hours                             k8s_etcd_etcd-master_kube-system_54caa73c3a366810eb1982ce52213834_1
d5d71dfc331c        ab81d7360408           "kube-scheduler --ad…"   23 hours ago        Up 23 hours                             k8s_kube-scheduler_kube-scheduler-master_kube-system_44b569a35761491825f4e7253fbf0543_17ff702474352        40a63db91ef8           "kube-apiserver --au…"   23 hours ago        Up 23 hours                             k8s_kube-apiserver_kube-apiserver-master_kube-system_4e3289b38af39fb55c00956ffb875c26_1590cb4078099        26e6f1db2a52           "kube-controller-man…"   23 hours ago        Up 23 hours                             k8s_kube-controller-manager_kube-controller-manager-master_kube-system_7d0a1ff3545bbc890a67b0d02dc0f191_1
c22bcc9f6903        fdb321fd30a0           "/usr/local/bin/kube…"   23 hours ago        Up 23 hours                             k8s_kube-proxy_kube-proxy-m8wsf_kube-system_b8572ad6-14bf-11e9-ac10-000c29bed493_18d63b75de565        k8s.gcr.io/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_kube-flannel-ds-amd64-7xh79_kube-system_ad897b0d-14c0-11e9-ac10-000c29bed493_1
fe0b44fe8c09        k8s.gcr.io/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_kube-proxy-m8wsf_kube-system_b8572ad6-14bf-11e9-ac10-000c29bed493_1729321ce5699        k8s.gcr.io/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_kube-scheduler-master_kube-system_44b569a35761491825f4e7253fbf0543_1
b635e5dd38d0        k8s.gcr.io/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_etcd-master_kube-system_54caa73c3a366810eb1982ce52213834_185502af97ba9        k8s.gcr.io/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_kube-apiserver-master_kube-system_4e3289b38af39fb55c00956ffb875c26_2240c427dc4dc        k8s.gcr.io/pause:3.1   "/pause"                 23 hours ago        Up 23 hours                             k8s_POD_kube-controller-manager-master_kube-system_7d0a1ff3545bbc890a67b0d02dc0f191_1

在后面初始化kuberneters集群的时候,依赖的这些镜像默认是从k8s.gcr.io去pull镜像,但是在国内是无法访问的

我们可以通过阿里云或者腾讯云先将镜像构建在阿里的镜像仓库中,然后再tag为需要的镜像标签

阿里云上自定义镜像仓库

image

仓库中自定义的构建信息,这里指定了dockerfile文件的地址在github上

image

这里为github上dockerfile的内容,以官方镜像为基础镜像构建后存在阿里的镜像仓库中

image

所有镜像构建完成后,通过命令pull到本地,打tag即可使用

写了一个脚本,避免重复的工作

#!/bin/bashset -e

IMAGE_URL=registry.cn-hangzhou.aliyuncs.com/
IMAGE_URL_TAG=k8s.gcr.io# 这里为个人的阿里云镜像仓库账户USER=
PASSWD=
REGNAME=

docker login $IMAGE_URL -u $USER -p $PASSWD# 初始化集群需要的镜像#IMAGE_LIST=(coredns:1.2.6 etcd-amd64:3.2.24 kube-apiserver-amd64:v1.13.1 kube-controller-manager-amd64:v1.13.1 kube-proxy-amd64:v1.13.1 kube-scheduler-amd64:v1.13.1 pause:3.1)for i in coredns:1.2.6 etcd-amd64:3.2.24 kube-apiserver-amd64:v1.13.1 kube-controller-manager-amd64:v1.13.1 kube-proxy-amd64:v1.13.1 kube-scheduler-amd64:v1.13.1 pause:3.1do
    docker pull $IMAGE_URL$REGNAME/$i
    echo "$i下载成功"done# flannel插件镜像docker pull registry.cn-hangzhou.aliyuncs.com/mmyk8s/flannel:v0.10.0-amd64

docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-proxy-amd64:v1.13.1 k8s.gcr.io/kube-proxy:v1.13.1
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-apiserver-amd64:v1.13.1 k8s.gcr.io/kube-apiserver:v1.13.1
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-controller-manager-amd64:v1.13.1 k8s.gcr.io/kube-controller-manager:v1.13.1
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-scheduler-amd64:v1.13.1 k8s.gcr.io/kube-scheduler:v1.13.1
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/kube-scheduler-amd64:v1.13.1 k8s.gcr.io/kube-scheduler:v1.13.1
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/coredns:1.2.6 k8s.gcr.io/coredns:1.2.6
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/etcd-amd64:3.2.24 k8s.gcr.io/etcd:3.2.24
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/flannel:v0.10.0-amd64 quay.io/coreos/flannel:v0.10.0-amd64
docker tag registry.cn-hangzhou.aliyuncs.com/mmyk8s/pause:3.1 k8s.gcr.io/pause:3.1echo “标签修改成功”

每个节点都需要这些镜像!

还有一种方法没用实践过,在下文中初始化集群使用的是第一种方式,在第二种方式中,imageRepository: k8s.gcr.io指定了镜像仓库地址,是否可以改为国内地址或者自己的私有仓库地址,这样就不用tag了。

部署kuberneters集群

kubeadm是用于快速构建Kubernetes集群的工具,随着Kubernetes的发行版本而提供,使用它构建集群时,大致可分为如下几步:

  1. 在Master及各Node安装Docker、kubelet及kubeadm,并以系统守护进程的方式启动Docker和kubelet服务。

  2. 在Master节点上通过kubeadminit命令进行集群初始化。

  3. 各Node通过kubeadmjoin命令加入初始化完成的集群中。

  4. 在集群上部署网络附件,如flannel或Calico等以提供Service网络及Pod网络。

为了简化部署过程,kubeadm使用一组固定的目录及文件路径存储相关的配置及数据文件,其中/etc/kubernetes目录是所有文件或目录的统一存储目录。它使用/etc/kubernetes/manifests目录存储各静态Pod资源的配置清单,用到的文件有etcd.yaml、kube-apiserver.yaml、kube-controller-manager.yaml和kube-scheduler.yaml四个,它们的作用基本能够见名知义。另外,/etc/kubernetes/目录中还会为Kubernetes的多个组件存储专用的kubeconfig文件,如kubelet.conf、controller-manager.conf、scheduler.conf和admin.conf等,它们分别为相关的组件提供接入APIServer的认证信息等。此外,它还会在/etc/kubernetes/pki目录中存储若干私钥和证书文件

设定容器运行的环境

这里基本就是docker的安装和一些简单的配置

安装kubelet和kubeadm

kubelet是运行于集群中每个节点之上的Kubernetes代理程序,它的核心功能在于通过APIServer获取调度至自身运行的Pod资源的PodSpec并依之运行Pod对象。事实上,以自托管方式部署的Kubernetes集群,除了kubelet和Docker之外的所有组件均以Pod对象的形式运行

设定用于安装kubelet、kubeadm和kubectl等组件的yum仓库,编辑配置文件/etc/yum.repos.d/kubernetes.repo

kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
yum install kubelet kubeadm kubectl
配置kubelet

Kubernetes自1.8版本起强制要求关闭系统上的交换分区(Swap),否则kubelet将无法启动。当然,用户也可以通过将kubelet的启动参数“--fail-swap-on”设置为“false”忽略此限制,尤其是系统上运行有其他重要进程且系统内存资源稍嫌不足时建议保留交换分区。

编辑kubelet的配置文件/etc/sysconfig/kubelet,设置其配置参数如下,以忽略禁止使用Swap的限制:

KUBELET_ EXTRA_ ARGS="--fail-swap-on=false"

待配置文件修改完成后,需要设定kubelet服务开机自动启动,这也是kubeadm的强制要求:

systemctl enable kubelet.service

以上部分的配置需要在集群所有机器配置,install kubelet kubeadm kubectl是安装集群的基本条件,这些服务的安装使用基于docker,环境的配置,例如防火墙等是保证初始化集群等的最基本条件

master节点初始化集群

在Master节点上执行“kubeadminit”命令进行集群初始化。kubeadminit命令支持两种初始化方式,一是通过命令行选项传递关键的参数设定,另一个是基于yaml格式的专用配置文件设定更详细的配置参数

第一种方式:

kubeadm init --kubernetes-version=v1.13.1 --pod-network-cidr=10.244.0.0/16 --service-cidr=10.96.0.0/12 --apiserver-advertise-address=0.0.0.0 --ignore-preflight-errors=Swap
--kubernetes-version:正在使用的Kubernetes程序组件的版本号,需要与kubelet的版本号相同。--pod-network-cidr:Pod网络的地址范围,其值为CIDR格式的网络地址;使用flannel网络插件时,其默认地址为10.244.0.0/16。--service-cidr:Service的网络地址范围,其值为CIDR格式的网络地址,默认地址为10.96.0.0/12。--apiserver-advertise-address:APIserver通告给其他组件的IP地址,一般应该为Master节点的IP地址,0.0.0.0表示节点上所有可用的地址。--ignore-preflight-errors:忽略哪些运行时的错误信息,其值为Swap时,表示忽略因swap未关闭而导致的错误。

第二种方式:

kubeadminit也配置文件加载配置,以定制更丰富的部署选项。以下是符合前述命令设定方式的使用示例,不过,它明确定义了kubeProxy的模式为ipvs,并支持通过修改imageRepository的值来修改获取系统镜像时使用的镜像仓库。

apiVersion: kubeadm.k8s.io/v1alpha2 kind: MasterConfiguration kubernetesVersion: v1.13.1 api:    advertiseAddress: 192.168.18.64     bindPort: 6443     controlPlaneEndpoint: "" imageRepository: k8s.gcr.io kubeProxy:     config:     mode: "ipvs"     ipvs:         ExcludeCIDRs: null         minSyncPeriod: 0s         scheduler: ""         syncPeriod: 30s kubeletConfiguration:     baseConfig:         cgroupDriver: cgroupfs         clusterDNS:         -10.96.0.10         clusterDomain: cluster.local         failSwapOn: false         resolvConf: /etc/resolv.conf         staticPodPath: /etc/kubernetes/manifests networking:     dnsDomain: cluster.local     podSubnet: 10.244.0.0/16     serviceSubnet: 10.96.0.0/12

将以上配置保存为kubedm-config.yaml

kubeadm init --config kubeadm-config.yaml --ignore-preflight-errors=Swap

成功后会生成node节点加入集群的命令

kubeadm join 192.168.18.64:6443 --token 4mvox7.5l6fgyyqo761yxyf --discovery-token-ca-cert-hash sha256:1cd687ae5837b85a48f9ca93873aa67b30d29b1584db3b1f2ede946506735035

完成集群部署还需要执行三类操作:设定kubectl的配置文件、部署网络附件以及将各Node加入集群。下面就来讲解如何进行这三步操作。

设定kubectl的配置文件根据输出的命令执行就可以

至此为止,一个KubernetesMaster节点已经基本配置完成。接下来即可通过APIServer来验证其各组件的运行是否正常。kubectl有着众多子命令,其中“getcompontsstatuses”即能显示出集群组件当前的状态,也可使用其简写格式“get cs”:

[root@master ~]# kubectl get cs
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok                   
scheduler            Healthy   ok                   
etcd-0               Healthy   {"health": "true"}

若上面命令结果的STATUS字段为“Healthy”,则表示组件处于健康运行状态,否则需要检查其错误所在,必要时可使用“kubeadm reset”命令重置之后重新进行集群初始化。

另外,使用“kubectl get nodes”命令能够获取集群节点的相关状态信息,显示了Master节点的状态为“NotReady”(未就绪),这是因为集群中尚未安装网络插件所致,执行完后面的其他步骤后它即自行转为“Ready”:

注: 此处为已经安装fannel网络插件后的结果

[root@master ~]# kubectl get nodesNAME     STATUS   ROLES    AGE   VERSIONmaster   Ready    master   21h   v1.13.1node1    Ready    <none>   63m   v1.13.1
master节点部署网络插件fannel

基于kubeadm部署时,flannel同样运行为Kubernetes集群的附件,以Pod的形式部署运行于每个集群节点上以接受Kubernetes集群管理。

事实上,也可以直接将flannel程序包安装并以守护进程的方式运行于集群节点上,即以非托管的方式运行。部署方式既可以是获取其资源配置清单于本地而后部署于集群中,也可以直接在线进行应用部署。

部署命令是“kubectlapply”或“kubectlcreate”,例如,下面的命令将直接使用在线的配置清单进行flannel部署:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

参考 https://github.com/coreos/flannel

至此master节点就完成了!

node节点加入

Master各组件运行正常后即可将各Node添加至集群中。配置节点时,需要事先参考前面“设置容器运行环境”和“设定Kubernetes集群节点”两节中的配置过程设置好Node主机,而后即可在Node主机上使用“kubeadmjoin”命令将其加入集群中。

kubeadm join 192.168.18.64:6443 --token 4mvox7.5l6fgyyqo761yxyf --discovery-token-ca-cert-hash sha256:1cd687ae5837b85a48f9ca93873aa67b30d29b1584db3b1f2ede946506735035

提供给APIServer的bootstraptoken认证完成后,kubeadmjoin命令会为后续Master与Node组件间的双向ssl/tls认证生成私钥及证书签署请求,并由Node在首次加入集群时提交给Master端的CA进行签署。

默认情况下,kubeadm配置kube-apiserver启用了bootstrapTLS功能,并支持证书的自动签署。于是,kubelet及kube-proxy等组件的相关私钥和证书文件在命令执行结束后便可自动生成,它们默认保存于/var/lib/kubelet/pki目录中。

在每个节点上重复上述步骤就能够将其加入集群中。所有节点加入完成后,即可使用“kubectlgetnodes”命令验证集群的节点状态,包括各节点的名称、状态就绪与否、角色(是否为节点Master)、加入集群的时长以及程序的版本等信息:

[root@master ~]# kubectl get nodesNAME     STATUS   ROLES    AGE   VERSIONmaster   Ready    master   21h   v1.13.1node1    Ready    <none>   79m   v1.13.1
重新生成用于节点加入集群的认证命令

如果忘记了节点加入集群的认证命令,需要在master节点通过kubeadm tokem list获取认证令牌,再生成验证CA公钥的哈希值

[root@master ~]# kubeadm token listTOKEN                     TTL       EXPIRES                     USAGES                   DESCRIPTION                                                EXTRA GROUPS4mvox7.5l6fgyyqo761yxyf   12m       2019-01-11T18:08:33+08:00   authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token
[root@master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'1cd687ae5837b85a48f9ca93873aa67b30d29b1584db3b1f2ede946506735035

将上述命令生成的值合为如下命令:

kubeadm join 192.168.18.64:6443 --token TOKEN --discover-token-ca-cert-hash sha256:HASH

即为

kubeadm join 192.168.18.64:6443 --token 4mvox7.5l6fgyyqo761yxyf --discover-token-ca-cert-hash sha256:1cd687ae5837b85a48f9ca93873aa67b30d29b1584db3b1f2ede946506735035
一些坑
[root@node2 ~]# kubeadm join 192.168.18.64:6443 --token 4mvox7.5l6fgyyqo761yxyf --discovery-token-ca-cert-hash sha256:1cd687ae5837b85a48f9ca93873aa67b30d29b1584db3b1f2ede946506735035[preflight] Running pre-flight checks
    [WARNING SystemVerification]: this Docker version is not on the list of validated versions: 18.09.0. Latest validated version: 18.06
    [WARNING Service-Kubelet]: kubelet service is not enabled, please run 'systemctl enable kubelet.service'[preflight] Some fatal errors occurred:
    [ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1
    [ERROR FileContent--proc-sys-net-ipv4-ip_forward]: /proc/sys/net/ipv4/ip_forward contents are not set to 1[preflight] If you know what you are doing, you can make a check non-fatal with `--ignore-preflight-errors=...`

node节点加入集群时

第一个warnning是版本验证问题,不用管

第二个warnning前面说到过,kubectl开机自启动是kubeadm的强制要求

error问题

[root@node2 ~]# vim /proc/sys/net/bridge/bridge-nf-call-iptables[root@node2 ~]# vim /etc/sysctl.conf[root@node2 ~]# echo "1" >/proc/sys/net/ipv4/ip_forward[root@node2 ~]# sysctl -p /etc/sysctl.conf

原文出处:https://www.cnblogs.com/mumengyun/p/10256598.html 

作者:小酒馆

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消