KubeEdge+Sedna安装全流程(超级避坑、精简版)

KubeEdge+Sedna安装全流程(超级避坑、精简版)

0. 你必须知道的

  1. k8s只需要安装在master节点上,其他的节点都不用

  2. kubeedge的运行前提是master上必须有k8s

  3. docker只是用来发布容器pods的

  4. calico只需要安装在master上,它是节点通信的插件,如果没有这个,master上安装kubeedge的coredns会报错。但是,节点上又不需要安装这个,因为kubeedge针对这个做了自己的通信机制

  5. 一些插件比如calico、edgemesh、sedna、metric-service还有kuborad等,都是通过yaml文件启动的,所以实际要下载的是k8s的控制工具kubeadm和kubeedge的控制工具keadm。然后提前准备好刚才的yaml文件,启动k8s和kubeedge后,直接根据yaml文件容器创建

  6. namespace可以看作不同的虚拟项目,service是指定的任务目标文件,pods是根据service或者其他yaml文件创建的具体容器

  7. 一个物理节点上可以有很多个pods,pods是可操作的最小单位,一个service可以设置很多pods,一个service可以包含很多物理节点

  8. 一个pods可以看作一个根据docker镜像创建的实例

  9. 如果是主机容器创建任务,要设置dnsPolicy(很重要)

  10. 拉去docker镜像的时候,一定要先去确认架构是否支持

1. 前提准备

最好把相关的yaml下载文件放在一个目录下,不然后面找不到

安装顺序

  1. master

  2. docker

  3. k8s(kubeadm容器安装)

  4. kubeedge(keadm容器安装)

  5. node

  6. docker

  7. kubeedge(keadm容器安装)

  8. master

  9. mesh

  10. sedna

1.1. master

1.1.1. 关闭防火墙

ufw disable
setenforce 0  #临时关闭

1.1.2. 开启ipv4转发,配置iptables参数

将桥接的 IPv4/IPv6 流量传递到 iptables 的链

modprobe br_netfilter
#方法1
cat >> /etc/sysctl.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p   #执行此命令生效配置

1.1.3. 禁用swap分区

swapoff -a                                          #临时关闭
sed -ri 's/.*swap.*/#&/' /etc/fstab                 #永久关闭

1.1.4. 设置主机名

在不同的虚拟机上执行

hostnamectl set-hostname master    
bash

hostnamectl set-hostname node
bash

hostnamectl set-hostname node
bash

1.1.5. 设置DNS

#不同主机上使用ifconfig查看ip地址
sudo vim /etc/hosts

#根据自己的ip添加
#ip 主机名
192.168.199.129 master
192.168.199.130 node-1
192.168.199.131 node-2
192.168.199.132 node-3

1.1.6. 安装docker

sudo apt-get install docker.io -y
仍然让人仍然让人仍然让人仍然让人仍然让人仍然让人仍然让人仍然让人仍然让人仍然让人仍然让人仍然
sudo systemctl start docker
sudo systemctl enable docker

docker --version

一定要配置的

sudo vim /etc/docker/daemon.json

#master添加:
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}

#node添加:
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=cgroupfs"]
}

#都要执行
sudo systemctl daemon-reload
sudo systemctl restart docker

#查看修改后的docker Cgroup的参数
docker info | grep Cgroup

1.1.7. 安装KuberNetes(k8s)

主要通过kubeadm一键安装,以及相关的其他插件

  • 添加k8s阿里源
curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
cat > /etc/apt/sources.list.d/kubernetes.list <<EOF
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF
apt-get update
  • 组件

你需要在每台机器上安装以下的软件包:

  • kubeadm:用来初始化集群的指令。

  • kubelet:在集群中的每个节点上用来启动 Pod 和容器等。

  • kubectl:用来与集群通信的命令行工具。

#查看软件版本
apt-cache madison kubeadm

#安装kubeadm、kubelet、kubectl
sudo apt-get update
sudo apt-get install -y kubelet=1.22.2-00 kubeadm=1.22.2-00 kubectl=1.22.2-00 
#锁定版本
sudo apt-mark hold kubelet kubeadm kubectl         
1.1.7.1. 安装calico

官方解释:因为在整个kubernetes集群里,pod都是分布在不同的主机上的,为了实现这些pod的跨主机通信所以我们必须要安装CNI网络插件,这里选择calico网络。

  • 步骤1:在master上下载配置calico网络的yaml。
#注意对应版本,v1.22和v3.20
wget https://docs.projectcalico.org/v3.20/manifests/calico.yaml --no-check-certificate

  • 步骤2:修改calico.yaml里的pod网段。
#把calico.yaml里pod所在网段改成kubeadm init时选项--pod-network-cidr所指定的网段,
#直接用vim编辑打开此文件查找192,按如下标记进行修改:

# no effect. This should fall within `--cluster-cidr`.
# - name: CALICO_IPV4POOL_CIDR
#   value: "192.168.0.0/16"
# Disable file logging so `kubectl logs` works.
- name: CALICO_DISABLE_FILE_LOGGING
  value: "true"

#把两个#及#后面的空格去掉,并把192.168.0.0/16改成10.244.0.0/16

# no effect. This should fall within `--cluster-cidr`.
- name: CALICO_IPV4POOL_CIDR
  value: "10.244.0.0/16"
# Disable file logging so `kubectl logs` works.
- name: CALICO_DISABLE_FILE_LOGGING
  value: "true"

改的时候请看清缩进关系,即这里的对齐关系。

  • 步骤3:提前下载所需要的镜像。
#查看此文件用哪些镜像:
grep image calico.yaml

#image: calico/cni:v3.20.6
#image: calico/cni:v3.20.6
#image: calico/pod2daemon-flexvol:v3.20.6
#image: calico/node:v3.20.6
#image: calico/kube-controllers:v3.20.6

在master上把这些镜像下载下来:

#换成自己的版本
for i in calico/cni:v3.20.6 calico/pod2daemon-flexvol:v3.20.6 calico/node:v3.20.6 calico/kube-controllers:v3.20.6 ; do docker pull $i ; done
1.1.7.2. 安装可视化工具kuboard
#master上
wget https://kuboard.cn/install-script/kuboard.yaml
#所有节点下载镜像
docker pull eipwork/kuboard:latest 
1.1.7.3. kube-shell

kube-shell(推荐,kubectl编辑的小工具)

#安装
pip install kube-shell

#启动
kube-shell

#推出
exit

1.1.8. 安装KubeEdge

  • 安装版本:v1.12.1
1.1.8.1. 安装keadm(可以尝试离线安装)

kubeEdge是kubeEdge的控制面板

进入官网版本链接:https://github.com/kubeedge/kubeedge/releases/tag/v1.12.1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JLhELsoy-1679816730614)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image.png)]

右键复制链接地址(注意架构),节点中输入:

#查看自己的架构
uname -u

#下载对应版本以及架构
wget https://github.com/kubeedge/kubeedge/releases/download/v1.12.1/keadm-v1.12.1-linux-amd64.tar.gz
#解压
tar zxvf keadm-v1.12.1-linux-amd64.tar.gz
#添加执行权限
chmod +x keadm-v1.12.1-linux-amd64/keadm/keadm 
#移动目录
mv keadm-v1.12.1-linux-amd64/keadm/keadm /usr/local/bin/
1.1.8.2. 安装metrics-server

追踪边缘节点日志的

  • 官网安装(不建议使用)

但是会出现拉去镜像超时问题

kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

#跳过验证
kubectl patch deploy metrics-server -n kube-system --type='json' -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
  • 本地安装(我的方法)

    1. 将该地址的文件下载到本地

    2. 修改镜像地址

    容器 & 服务:metrics-server 安装探索

    原始地址:k8s.gcr.io/metrics-server/metrics-server:v0.6.2

    目标地址:xin053/metrics-server:v0.6.2

    根据原始地址(主要是版本)去dockerhub找到替代的镜像

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zvd05f7p-1679816503062)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e5-9ca2-cf64beba87a8/image 2.png)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 1.png)]

    #提前下载
    docker pull xin053/metrics-server:v0.6.2
    

1.1.9. EdgeMesh

kubeedge的通讯组件,把github源码下载下来

git clone https://github.com/kubeedge/edgemesh.git

1.1.10. Sedna

1.1.10.1. 官网一键化安装seda(不建议,会出错)
curl https://raw.githubusercontent.com/kubeedge/sedna/main/scripts/installation/install.sh | SEDNA_ACTION=create bash -

可能会报错

1.1.10.2. 本地安装(我的)
  • 下载install.sh文件(不要保存到build中,可以永久使用)
wget https://raw.githubusercontent.com/kubeedge/sedna/main/scripts/installation/install.sh
  • 相关的修改(不要保存到build中,可以永久使用)

可以直接下载这个我修改好的

offline-sedna.sh

#改名字
mv install.sh offline-install.sh

#修改文件中的一下内容(sh的语法)
1.第28行:TMP_DIR='opt/sedna'
2.第34行:删除  trap "rm -rf '%TMP_DIR'" EXIT
3.第415行:删除  prepare
  • 下载sedna的官网github(不要保存到build中,可以永久使用)

官方地址:github.com/kubeedge/sedna/tree/main/build

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DlOC6j1f-1679816730616)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 2.png)]

  • 创建安装目录(和上面的路径要一样),并且复制文件
mkdir /opt/sedna
#把下载的sedna文件中的build目录复制到该地址
mv /sedna/build /opt/sedna/build

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SqMc56ue-1679816730616)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 3.png)]

1.2. Node上

1.2.1. 关闭防火墙

ufw disable
setenforce 0  #临时关闭

1.2.2. 开启ipv4转发,配置iptables参数

将桥接的 IPv4/IPv6 流量传递到 iptables 的链

modprobe br_netfilter
#方法1
cat >> /etc/sysctl.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p   #执行此命令生效配置

1.2.3. 禁用swap分区

swapoff -a                                          #临时关闭
sed -ri 's/.*swap.*/#&/' /etc/fstab                 #永久关闭

1.2.4. 设置主机名

在不同的虚拟机上执行

hostnamectl set-hostname master    
bash

hostnamectl set-hostname node
bash

hostnamectl set-hostname node
bash

1.2.5. 安装docker

sudo apt-get install docker.io -y

sudo systemctl start docker
sudo systemctl enable docker

docker --version

一定要配置的

sudo vim /etc/docker/daemon.json

#master添加:
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"]
}

#node添加:
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=cgroupfs"]
}

#都要执行
sudo systemctl daemon-reload
sudo systemctl restart docker

#查看修改后的docker Cgroup的参数
docker info | grep Cgroup

1.2.6. 安装KubeEdge

  • 安装版本:v1.12.1
1.2.6.1. 安装keadm(可以尝试离线安装)

kubeEdge是kubeEdge的控制面板

进入官网版本链接:https://github.com/kubeedge/kubeedge/releases/tag/v1.12.1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IjtbI6Jf-1679816730616)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 4.png)]

右键复制链接地址(注意架构),节点中输入:

#查看自己的架构
uname -a

#下载对应版本以及架构
wget https://github.com/kubeedge/kubeedge/releases/download/v1.12.1/keadm-v1.12.1-linux-amd64.tar.gz
#解压
tar zxvf keadm-v1.12.1-linux-amd64.tar.gz
#添加执行权限
chmod +x keadm-v1.12.1-linux-amd64/keadm/keadm 
#移动目录
mv keadm-v1.12.1-linux-amd64/keadm/keadm /usr/local/bin/

2. 安装和运行

2.1. master

2.1.1. K8s启动

2.1.1.1. 删除从前的信息(如果是第一次可跳过)
swapoff -a
kubeadm reset
systemctl daemon-reload
systemctl restart docker kubelet
rm -rf $HOME/.kube/config

rm -f /etc/kubernetes/kubelet.conf    #删除k8s配置文件
rm -f /etc/kubernetes/pki/ca.crt    #删除K8S证书

iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X  
2.1.1.2. 初始化k8s集群

注意“apiserver-advertise-address”要写自己master节点的地址

kubeadm init \
  --apiserver-advertise-address=192.168.124.40 \
  --image-repository registry.aliyuncs.com/google_containers \
  --kubernetes-version v1.22.10 \
  --service-cidr=10.96.0.0/12 \
  --pod-network-cidr=10.244.0.0/16 \
  --ignore-preflight-errors=all
  
  
#上一步创建成功后,会提示:主节点执行提供的代码
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

#如果只有一个点,需要容纳污点
kubectl taint nodes --all node-role.kubernetes.io/master node-role.kubernetes.io/master-

#打开转发路由
kubectl get cm tunnelport -n kubeedge -o yaml
#找到10350 或者10351
#set the rule of trans,设置自己的端口
CLOUDCOREIPS=192.168.124.40
iptables -t nat -A OUTPUT -p tcp --dport 10351 -j DNAT --to $CLOUDCOREIPS:10003
2.1.1.3. 启动calico和kuboard
#进入这两个文件的目录,一定要执行,不然coredns无法初始化
kubectl apply -f calico.yaml
kubectl apply -f kuboard.yaml

#检查启动状态,需要都running,除了calico可以不用
kubectl get pods -A

#获取 kuboard  token
echo $(kubectl -n kube-system get secret $(kubectl -n kube-system get secret | grep kuboard-user | awk '{print $1}') -o go-template='{{.data.token}}' | base64 -d) 

2.1.2. 启动kubeEdge

2.1.2.1. 重启(如果第一次可以跳过)
keadm reset
2.1.2.2. 启动cloudcore

通过keadm启动

#注意修改--advertise-address的ip地址
keadm init --advertise-address=192.168.124.40 --set iptablesManager.mode="external" --profile version=v1.12.1

#其他设置,边缘节点上不能执行kube-proxy,所以提前设置补丁命令
kubectl get daemonset -n kube-system | grep -v NAME | awk '{print $1}' |xargs -n 1 kubectl patch daemonset -n kube-system --type='json' -p='[{"op":"replace","path":"/spec/template/spec/affinity","value":{"nodeAffinity":{"requireDuringSchedulingIgnoredDuringExecution":{"nodeSelectorTerms":[{"matchExpressions":[{"key":"node-role.kubernetes.io/edge","operator":"DoesNotExist"}]}]}}}}]'
2.1.2.3. 启动metric-service

方便跟踪日志

#进入自己的下载目录
kubectl apply -f components.yaml
#line 144
#image: xin053/metrics-server:v0.6.2

#跳过安全验证get by local:docker pull xin053/metics-server:v0.6.2
kubectl patch deploy metrics-server -n kube-system --type='json' -p='[{"op":"add","path":"/spec/template/spec/containers/0/args/-","value":"--kubelet-insecure-tls"}]'
2.1.2.4. 获取证书token

这个是edge上加入master时的认证

keadm gettoken

2.2. Node上

2.2.1. 启动edgecore

2.2.1.1. 消除之前的信息(如果第一次,请跳过)
#重启
keadm reset

#重新加入时,报错存在文件夹,直接删除
rm -rf /etc/kubeedge

#docker容器占用(通常是mqtt)
docker ps -a
docker stop mqtt
docker rm mqtt
2.2.1.2. 启动
#注意这里是要加入master的IP地址,token是master上获取的
keadm join --cloudcore-ipport=192.168.124.40:10000 --kubeedge-version=1.12.1 --token=6ff1a09e9cc6beca30061ed96b189859e3e43d7932583d1b712adba49b4a28d6.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2Nzk3OTQ1MTV9.xUx5Q4-HcYpvvaSaStmgTmYNhzIgcmtm-Ev-EG5QblQ

重要

#加入后,先查看信息
journalctl -u edgecore.service -f

如果提示因为kube-proxy的原因导致通信失败

#手动删除kube-proxy的容器
docker ps
#get the id of kube-prox
docker stop id
docker rm id

2.3. EdgeMesh

全网最全EdgeMesh Q&A手册

很重要,这个时候,只能说连接节点成功了,但是节点之间的通信不正常

2.3.1. 前提准备

  • 步骤1: 去除 K8s master 节点的污点
$ kubectl taint nodes --all node-role.kubernetes.io/master-
  • 步骤2: 给 Kubernetes API 服务添加过滤标签
$ kubectl label services kubernetes service.edgemesh.kubeedge.io/service-proxy-name=""

正常情况下你不会希望 EdgeMesh 去代理 Kubernetes API 服务,因此需要给它添加过滤标签,更多信息请参考 服务过滤

  • 步骤3: 启用 KubeEdge 的边缘 Kube-API 端点服务important
  1. 在云端,开启 dynamicController 模块,配置完成后,需要重启 cloudcore
#keadm安装的
kubectl edit cm -n kubeedge cloudcore
#修改
modules:
  ...
  dynamicController:
    enable: true
...

#执行完后,检查一下
kubectl describe cm -n kubeedge cloudcore

#如果不放心,直接去kuboard在kubeedge上把cloudcore删除掉,然后会根据新的模板创建新的容器
  1. 在边缘节点,打开 metaServer 模块(如果你的 KubeEdge < 1.8.0,还需关闭旧版 edgeMesh 模块),配置完成后,需要重启 edgecore
$ vim /etc/kubeedge/config/edgecore.yaml
modules:
  ...
  edgeMesh:
    enable: false
  ...
  metaManager:
    metaServer:
      enable: true
...
  1. 在边缘节点,配置 clusterDNS 和 clusterDomain,配置完成后,需要重启 edgecore

如果 KubeEdge >= v1.12.0,请这样配置:

$ vim /etc/kubeedge/config/edgecore.yaml
modules:
  ...
  edged:
    ...
    tailoredKubeletConfig:
      ...
      clusterDNS:
      - 169.254.96.16
      clusterDomain: cluster.local
...


#重启
systemctl restart edgecore.service
#检查状况
journalctl -u edgecore.service -f
#确定正常运行

  • 步骤4: 最后,在边缘节点,测试边缘 Kube-API 端点功能是否正常
#节点上
curl 127.0.0.1:10550/api/v1/services

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q57ItIJj-1679816503068)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 7.png)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 5.png)]

注意

如果返回值是空列表,或者响应时长很久(接近 10s)才拿到返回值,说明你的配置可能有误,请仔细检查。

完成上述步骤之后,KubeEdge 的边缘 Kube-API 端点功能就已经开启了,接着继续部署 EdgeMesh 即可。

2.3.2. 安装

  • 步骤1: 获取 EdgeMesh
#或者自己提前下载好
git clone https://github.com/kubeedge/edgemesh.git
cd edgemesh
  • 步骤2: 安装 CRDs
kubectl apply -f build/crds/istio/
  • 步骤3: 部署 edgemesh-agent
kubectl apply -f build/agent/resources/
  • 步骤4: 检验部署结果
kubectl get all -n kubeedge -o wide

2.4. Sedna

#直接进入之前准备好的文件目录运行
SEDNA_ACTION=create bash - offline-sedna.sh

3. 部署完成以及基本操作

  1. 使用命令实时刷新
#节点信息
watch -n 1 kubectl get nodes
#pods信息
watch -n 1 kubectl get pods -A -o wide

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aowmcKYH-1679816730617)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 6.png)]

  1. kuboard的使用

进入相关网页之后,可以查看资源状态,也可以查看所有service、节点、pods,也能进行增删改查操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9dLJ9NRQ-1679816503070)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba77a8/image 8.png)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 7.png)]

  1. 常见的命令
#master上

#1. 操作镜像
kubectl exec -n namespace pods-name -it -- /bin/bash
#2. 获取日志
kubectl logs -n namespace pods-name
#3. 查看配置信息
kubectl describe configmap -n namespace service-name
#4. 编辑配置信息
kubectl edit configmap -n namespace service-name
#5.根据yaml创建
kubectl create -f name.yaml
#6.根据yaml删除
kubectl delete -f name.yaml
  1. 发布任务时的Pod的DNS策略

全网最全EdgeMesh Q&A手册

Kubernetes POD 之DNS策略部署

自己在yaml中添加:

hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet

#创建成功之后
#验证dns配置
kubectl exec -it pod-name -n namespace -- cat /etc/resolv.conf
#出现nameserver 10.66.0.2则成功

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZNOir8vY-1679816730618)(KubeEdge+Sedna精简版+f7a20955-e7ee-40e6-9ca2-cf64beba87a8/image 8.png)]

例子:ClusterFirstWithHostNet (宿主机与 Kubernetes 共存 )

同时使用 hostNetwork 与 coredns 作为 Pod 预设 DNS 配置。

 cat dns.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dns-none
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: dns
      release: v1
  template:
    metadata:
      labels:
        app: dns
        release: v1
        env: test
    spec:
      hostNetwork: true
      containers:
      - name: dns
        image: registry.cn-beijing.aliyuncs.com/google_registry/myapp:v1
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80
      dnsPolicy: ClusterFirstWithHostNet
#kubectl  apply  -f dns,yml
#验证dns配置
#kubectl exec -it   dns-none-86nn874ba8-57sar  -n default -- cat /etc/resolv.conf
nameserver xxx
search default.svc.cluster.local svc.cluster.local cluster.local localdomain
options ndots:5
  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hanzoe_lwh

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值