【kubernetes系列】Kubernetes之Ingress

概述

从前面的学习,我们可以了解到Kubernetes暴露服务的方式目前常见的只有三种:LoadBlancer Service、NodePort Service、Ingress;而我们需要将集群内服务提供外界访问就会面临以下几个问题:

Pod 漂移问题
Kubernetes 具有强大的副本控制能力,能保证在任意副本(Pod)挂掉时自动启动一个新的,还可以进行动态扩容等。那么自然随着 Pod 的销毁和创建,Pod IP 也会动态变化;那么通过Pod IP去访问某个服务的话是不现实的,而且如果是多个pod实例,怎么做到负载均衡呢?这里借助于 Kubernetes 的 Service 机制,Service 可以以标签的形式选定一组带有指定标签的 Pod,并监控和自动负载他们的 Pod IP,那么我们向外暴露只暴露 Service IP 就行了;这就是 NodePort 模式:即在每个节点上开起一个端口,然后转发到内部 Pod IP 上。

端口管理问题
如果采用 NodePort 方式暴露服务需要的面临问题是,服务一旦多起来,NodePort 在每个节点上开启的端口会及其庞大,而且很容易造成端口冲突,效率低,难以维护。这时,我们可以在前端部署一个Nginx Pod直接对内进行转发。因为Pod与Pod之间是可以互相通信的,而Pod是可以共享宿主机的网络名称空间的,也就是说当在共享网络名称空间时,Pod上所监听的就是Node上的端口。那么这又该如何实现呢?简单的实现就是使用 DaemonSet 在每个 Node 上监听 80,然后写好规则,因为 Nginx 外面绑定了宿主机 80 端口(就像 NodePort),本身又在集群内,那么向后直接转发到相应 Service IP 就行了。

域名分配及动态更新问题
从上面的方法,采用 Nginx Pod 似乎已经解决了问题,但是其实这里面有一个很大缺陷:当每次有新服务加入又该如何修改 Nginx 配置呢??我们知道使用Nginx可以通过虚拟主机域名进行区分不同的服务,而每个服务通过upstream进行定义不同的负载均衡池,再加上location进行负载均衡的反向代理,在日常使用中只需要修改nginx.conf即可实现,那在K8S中又该如何实现这些配置呢???

假设后端的服务初始服务只有api,后面增加了web和docs服务,那么又该如何将这2个服务加入到Nginx Pod进行调度呢?总不能每次手动去修改nginx的相关配置然后重启nginx的pod吧!!此时 Ingress 出现了,Ingress 包含两大组件:Ingress Controller 和 Ingress。

Ingress是kubernetes中的一种资源对象,作用是定义请求如何转发到service的规则,简单的理解就是你原来需要改 Nginx 配置,然后配置各种域名对应哪个 Service,现在把这个动作抽象出来,变成一个 Ingress 对象,你可以用 yaml 创建,每次不要去改 Nginx 了,直接改 yaml 然后创建/更新就行了;那么问题来了:”Nginx 该怎么处理?”

而Ingress Controller 这东西就是解决 “Nginx 的处理方式” 的;Ingress Controoler 通过与 Kubernetes API 交互,动态的去感知集群中 Ingress 规则变化,然后对它进行解析,按照它自己模板生成一段 Nginx 配置,再写到 Nginx Pod 里,最后 reload 一下,工作流程如下图:

在这里插入图片描述

Ingress Controller的实现方式常见的有两种,nginx和traefik,工作架构如下,借用traefik官方的图:

ingress-5
ingress-5

通过设置可以将api.domain.com进来的流量路由到集群里api的pod上,你可以将domain.com/web流量路由到web的一组pod上(注意:ingress nginx是怎么识别那些Pod是那个应该加入那个upstream呢?这里就用到了service使用service的labels将一组服务关联起来,虽然创建了service但是ingress controller在解析service的是时候实际是解析到后面的Pod上。)

ingress-6
ingress-6

看起来按照nginx来理解转发是client——>nginx——>svc——>pod;
实际上转发是client—–>nginx——>pod,是直接负载到svc后面的Pod上面的

部署Ingress (以 nginx-ingress为例)

选择适合自己k8s集群版本的nginx-ingress,具体可以参考https://github.com/kubernetes/ingress-nginx/tree/controller-v1.8.1,大致如下:

Ingress-NGINX versionk8s supported versionAlpine VersionNginx VersionHelm Chart Version
v1.8.11.27,1.26, 1.25, 1.243.18.21.21.64.7.*
v1.8.01.27,1.26, 1.25, 1.243.18.01.21.64.7.*
v1.7.11.27,1.26, 1.25, 1.243.17.21.21.64.6.*
v1.7.01.26, 1.25, 1.243.17.21.21.64.6.*
v1.6.41.26, 1.25, 1.24, 1.233.17.01.21.64.5.*
v1.5.11.25, 1.24, 1.233.16.21.21.64.4.*
v1.4.01.25, 1.24, 1.23,1.223.16.21.19.10+4.3.0
v1.3.11.24, 1.23, 1.22, 1.21, 1.203.16.21.19.10+4.2.5
v1.3.01.24, 1.23, 1.22, 1.21, 1.203.16.01.19.10+4.2.3
v1.2.11.23, 1.22, 1.21, 1.20, 1.193.14.61.19.10+4.1.4
v1.1.31.23, 1.22, 1.21, 1.20, 1.193.14.41.19.10+4.0.19
v1.1.21.23, 1.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.18
v1.1.11.23, 1.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.17
v1.1.01.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.13
v1.0.51.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.9
v1.0.41.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.6
v1.0.31.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.5
v1.0.21.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.3
v1.0.11.22, 1.21, 1.20, 1.193.14.21.19.9+4.0.2
v1.0.01.22, 1.21, 1.20, 1.193.13.51.20.14.0.1

由于我的kubernetes版本比较老为1.19.16,按表看应该是nginx版本大于1.19.10的都可以
部署Ingress的步骤:(注意先后顺序,如果先执行了mandatory.yaml文件在执行service-nodeport.yaml文件使用kubectl logs -f 看ingress-controller的pod的日志会有很多报错信息)

1)下载Ingress-controller相关的yaml文件,并给Ingress-controller创建独立的名称空间命名为ingress-nginx;
2)按需修改下载的yaml文件
3)创建Ingress-controller的service,以实现接入集群外部流量;
4)部署Ingress-controller;
5)部署后端的服务,如nginx,并通过service进行暴露;
6)部署Ingress,编写规则,使Ingress-controller和后端服务的Pod组进行关联。

1)下载yaml文件

#部署nginx ingress服务的yaml文件
[root@k8s-m1 nginx-ingress]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.0/deploy/mandatory.yaml
#用于暴露服务供外部用户访问
[root@k8s-m1 nginx-ingress]# wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.24.0/deploy/provider/baremetal/service-nodeport.yaml

2)修改yaml文件
入口nginx-ingress的访问方式有两种

  • 可以用nodeport的方式暴露端口
  • 通过设置 hostNetwork为true直接使用宿主机的网络空间
#添加了nodeport的端口,主要为了固定端口
[root@k8s-m1 nginx-ingress]# cat  service-nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
      nodePort: 30080
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP
      nodePort: 30443
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

3)部署Ingress Nginx

[root@k8s-m1 nginx-ingress]# kubectl apply  -f service-nodeport.yaml -f mandatory.yaml 
clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
deployment.apps/nginx-ingress-controller created

#检查部署情况,pod是否正常,日志是否有报错等
[root@k8s-m1 nginx-ingress]# kubectl get po -n ingress-nginx 
NAME                                       READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-7c9bd444c-bhsvn   1/1     Running   0          113s
[root@k8s-m1 nginx-ingress]# kubectl logs -n ingress-nginx nginx-ingress-controller-7c9bd444c-bhsvn 

4)创建后端服务用于测试

[root@k8s-m1 nginx-ingress]# cat nginx-deployment.yml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
  labels:
    app: nginx
spec:
  selector:
    matchLabels:
      tier: frontend
  replicas: 2
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
        - name: nginx-gateway
          image: nginx
          ports:
            - containerPort: 80
[root@k8s-m1 nginx-ingress]# cat nginx-svc.yml 
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 80
  selector:
    tier: frontend

[root@k8s-m1 nginx-ingress]# kubectl apply -f nginx-deployment.yml -f  nginx-svc.yml 

5)创建ingress
ingress资源定义格式查看

[root@k8s-m1~]# kubectl explain ingress

编写ingress的配置清单:

[root@k8s-m1 nginx-ingress]# cat my-nginx-ingress.yml 
apiVersion: extensions/v1beta1
kind: Ingress      
metadata:         
  name: ingress-fronted
  annotations:         
    kubernetes.io/ingress.class: "nginx"
spec:     
  rules: 
  - host: nginx.margu.com    
    http:
      paths:       
      - path:       #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        backend:    #配置后端服务
          serviceName: nginx-service
          servicePort: 80  #注意端口要与之前svc里面的一致

进入ingress nginx的pod可以看到nginx的配置里面已经有根据ingress规则生成的相关配置信息,如下:

[root@k8s-m1 nginx-ingress]# kubectl exec -it -n ingress-nginx nginx-ingress-controller-7c9bd444c-bhsvn -- /bin/bash
www-data@nginx-ingress-controller-7c9bd444c-bhsvn:/etc/nginx$ cat nginx.conf|more
        ## start server nginx.margu.com
        server {
                server_name nginx.margu.com ;

                listen 80;

                set $proxy_upstream_name "-";

                location / {

                        set $namespace      "default";
                        set $ingress_name   "ingress-fronted";
                        set $service_name   "nginx-service";
                        set $service_port   "80";
                        set $location_path  "/";

                        rewrite_by_lua_block {
                                balancer.rewrite()
                        }

                        header_filter_by_lua_block {

                        }
                        body_filter_by_lua_block {

                        }

                        log_by_lua_block {

                                balancer.log()

                                monitor.call()

                        }

找一台非集群的机器,向hosts文件内添加域名nginx.margu.com设置到集群的任意一个node节点ip上(因为我们使用的是nodeport的方式暴露的ingress nginx,所以每个节点都会有端口暴露出来),打开浏览器访问nginx.margu.com即可发现集群内的nginx已经暴露在集群外。如下:

配置域名解析(Node节点IP),如我的环境以下任意一条域名解析都是可以的

192.168.2.140 nginx.margu.com
192.168.2.141 nginx.margu.com
192.168.2.142 nginx.margu.com

6)多个Ingress controllers:
如果环境中运行多个Ingress controllers,则需要kubernetes.io/ingress.class: "nginx"指定将ingress对象加入到那个Ingress

controllers中
metadata:
  name: foo
  annotations:
    kubernetes.io/ingress.class: "pre"

将以pre控制器作为目标,使它忽略其他种类控制器。

metadata:
  name: foo
  annotations:
    kubernetes.io/ingress.class: "pro"

将以pro控制器作为目标,使它忽略其他种类控制器。

Rewrite(重写):
必须条件:当集群中有多个Ingress controller时需要通过指定Ingress.class注释来确保您的Ingress只针对一个Ingress controller生效,并且的集群中必须运行着这个Ingress controller。ingress可以使用以下标签来控制重写,详情可参考官网https://kubernetes.github.io/ingress-nginx/examples/rewrite/

名称描述
nginx.ingress.kubernetes.io/rewrite-targetTarget URI where the traffic must be redirectedstring
nginx.ingress.kubernetes.io/ssl-redirectIndicates if the location section is only accessible via SSL (defaults to True when Ingress contains a Certificate)bool
nginx.ingress.kubernetes.io/force-ssl-redirectForces the redirection to HTTPS even if the Ingress is not TLS Enabledbool
nginx.ingress.kubernetes.io/app-rootDefines the Application Root that the Controller must redirect if it’s in / contextstring
nginx.ingress.kubernetes.io/use-regexIndicates if the paths defined on an Ingress use regular expressionsbool

7)配置无host字段ingress

[root@k8s-m1 nginx-ingress]# cat no-domain-ingress.yml 
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: no-domain-ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/server-alias: "nginx.margu.com"
    nginx.ingress.kubernetes.io/use-regex: "true"   #支持nginx的rui的正则匹配
spec:
  rules:
  - http:
      paths: 
      - path: /(.*)
        backend:
          serviceName: nginx-service
          servicePort: 80
[root@k8s-m1 nginx-ingress]# 

在配置中我们没有host字段,这将在ingress-nginx配置中会以如下形式展示,此种方式我们可以用IP或域名的方式访问,因为我们还配置了nginx.ingress.kubernetes.io/server-alias: “nginx.margu.com”,所以当我们用域名访问时用nginx.margu.com访问。
但是需要注意的是,在使用无域名的Ingress转发规则时,将默认使用HTTPS安全协议进行转发。如需使用非安全的HTTP,还需要额外调整Ingress Controller的相关配置,通常在一个安全的网络环境下使用。

        ## start server _
        server {
                server_name _ nginx.margu.com;

                listen 80 default_server reuseport backlog=4096;

                set $proxy_upstream_name "-";

                listen 443  default_server reuseport backlog=4096 ssl http2;

                # PEM sha: e177c1bdc45a48a1cd43a53bd559f8254c442ce7
                ssl_certificate                         /etc/ingress-controller/ssl/default-fake-certificate.pem;
                ssl_certificate_key                     /etc/ingress-controller/ssl/default-fake-certificate.pem;

                location ~* "^/(.*)" {

                        set $namespace      "default";
                        set $ingress_name   "no-domain-ingress";
                        set $service_name   "nginx-service";
                        set $service_port   "80";
                        set $location_path  "/(.*)";

构建TLS站点

为了安全起见,我们一般都是要求客户端通过https进行访问,下面我们进行测试。注意:我们需要标准证书格式,不能将key+server+ca放在一个pem文件中。
1)准备证书

[root@k8s-m1 nginx-ingress]# openssl genrsa -out tls.key 2048 
Generating RSA private key, 2048 bit long modulus
.......+++
.......................................................................+++
e is 65537 (0x10001)
[root@k8s-m1 nginx-ingress]#  openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=nginx.margu.com

2)生成secret

[root@k8s-m1 nginx-ingress]# kubectl create secret tls nginx-ingress-secret --cert=tls.crt --key=tls.key
secret/nginx-ingress-secret created
[root@k8s-m1 nginx-ingress]# kubectl get secret
NAME                                  TYPE                                  DATA   AGE
nginx-ingress-secret                  kubernetes.io/tls                     2      8s
[root@k8s-m1 nginx-ingress]# kubectl describe secret nginx-ingress-secret 
Name:         nginx-ingress-secret
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  kubernetes.io/tls

Data
====
tls.crt:  1289 bytes
tls.key:  1679 bytes

3)ingress规则定义并部署

[root@k8s-m1 nginx-ingress]# cat  my-nginx-tls-ingress.yml 
apiVersion: extensions/v1beta1
kind: Ingress      
metadata:         
  name: ingress-fronted
  annotations:         
    kubernetes.io/ingress.class: "nginx"
spec:     
  tls:
  - hosts:
    - nginx.margu.com
    secretName: nginx-ingress-secret
  rules: 
  - host: nginx.margu.com    
    http:
      paths:       
      - path:       #配置访问路径,如果通过url进行转发,需要修改;空默认为访问的路径为"/"
        backend:    #配置后端服务
          serviceName: nginx-service
          servicePort: 80

[root@k8s-m1 nginx-ingress]# kubectl  apply -f my-nginx-tls-ingress.yml 
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
ingress.extensions/ingress-fronted-tls created
[root@k8s-m1 nginx-ingress]# kubectl get ingress
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+; use networking.k8s.io/v1 Ingress
NAME                  CLASS    HOSTS             ADDRESS   PORTS     AGE
ingress-fronted       <none>   *                           80        24m
ingress-fronted-tls   <none>   nginx.margu.com             80, 443   16s

4)访问测试:https://nginx.margu.com:30443
在这里插入图片描述

5)不想添加端口访问,改使用正常的80/443端口进行访问

#先删除开始nginx-ingress的nodeport暴露方式
[root@k8s-m1 nginx-ingress]# kubectl delete -f nginx-svc.yml 

#修改yaml,在template下面spec处添加hostNetwork和hostPID,如下
[root@k8s-m1 nginx-ingress]# vim mandatory.yaml 
    spec:
      hostNetwork: true
      hostPID: true
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.23.0
......

[root@k8s-m1 nginx-ingress]# kubectl apply  -f mandatory.yaml 

[root@k8s-m1 nginx-ingress]# kubectl get po -n ingress-nginx -o wide
NAME                                        READY   STATUS        RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
nginx-ingress-controller-554586bf4d-74pp2   1/1     Running       0          30s     192.168.2.142   k8s-m3   <none>           <none>
I

测试:此种方法只会在nginx-ingress 的pod所在节点上暴露端口80和443,所以在本地hosts中添加域名解析时需要注意IP地址。如上,需要解析到192.168.2.142这个IP地址。实际使用中,我们可以通过打标签的方式在多个节点部署nginx-ingess的示例,然后通过前端负载均衡器进行负载。

hosts:192.168.2.142 nginx.margu.com
在这里插入图片描述

Ingress Controller高可用

在实际使用中,为保证服务的高可用和负载均衡,我们往往需要部署多个nginx-ingress的pod,并使用haproxy+keepalived或者某些平台自带的负载均衡器进行负载。上面的例子里入口ingress-nginx使用的是nodePort的方式,Nodeport端口不是常用的web端口(但是可以修改Nodeport的范围改成web端口),如果当流量进来负载到某个node上的时候因为Ingress Controller的pod不在这个node上,会走这个node的kube-proxy转发到Ingress Controller的pod上,多转发了一次。(例如:我们Ingress Controller的pod在192.168.2.142上,但是域名解析的是192.168.2.140上,这个时候192.168.2.140这个node上的kube-proxy会把请求转发到192.168.2.142上)。故建议使用daemonset+nodeSelector的方式来ingress controller的Pod负载(每个Node节点一个ingress controller的Pod),并在前端添加使用一个负载均衡器。

不创建nginx svc,效率最高(不使用nodeport的方式进行暴露)。如果我们使用Nodeport的方式,流量是NodeIP->svc->ingress-controller(pod)这样的话会多走一层svc层,不管svc层是使用iptables还是lvs都会降低效率。如果使用hostNetwork的方式就是直接走Node节点的主机网络,唯一要注意的是hostNetwork下pod会继承宿主机的网络协议,也就是使用了主机的dns,会导致svc的请求直接走宿主机的上到公网的dns服务器而非集群里的dns server,需要设置pod的dnsPolicy: ClusterFirstWithHostNet即可解决

Ingress Controller部署方式:

  • daemonSet + nodeSeletor (一个Node节点运行一个Ingress Controller的Pod,当有多个Node的时候可以使用污点或者通过指定nodeSelector来指定部分主机来运行Ingress Controller的Pod)
  • deploy的方式部署多个ingress controller:设置replicas数量(不能大于node节点数) + nodeSeletor / podantifinity

使用 daemonSet + nodeSeletor 方式部署
1)创建一个名为ingress-nginx的service
首先我们要创建一个ingress-nginx的svc不然ingress nginx的log里会一直刷找不到ingress-nginx的svc不处理的话会狂刷log导致机器load过高,所以先创建一个同名的svc即可解决,例如创建一个不带选择器clusterip为None的。

[root@k8s-m1 nginx-ingress]#  cat ingress-nginx-no-clusterip-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: ClusterIP
  clusterIP: "None"
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP

[root@k8s-m1 nginx-ingress]#  kubectl get svc -n ingress-nginx
NAME            TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
ingress-nginx   ClusterIP   None         <none>        80/TCP,443/TCP   20s

2)修改ingress-controller mandatory.yaml文件

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      serviceAccountName: nginx-ingress-serviceaccount
·······

[root@k8s-m1 nginx-ingress]# kubectl apply -f  mandatory.yaml 

[root@k8s-m1 nginx-ingress]# kubectl get pod -n ingress-nginx  -o wide 
NAME                             READY   STATUS    RESTARTS   AGE     IP              NODE     NOMINATED NODE   READINESS GATES
nginx-ingress-controller-92rws   1/1     Running   0          4m35s   192.168.2.141   k8s-m2   <none>           <none>
nginx-ingress-controller-ffbww   1/1     Running   0          4m35s   192.168.2.142   k8s-m3   <none>           <none>
nginx-ingress-controller-qtnbg   1/1     Running   0          4m35s   192.168.2.140   k8s-m1   <none>           <none>

说明:修改部署方式为DaemonSet以及删除replicas副本集参数,新增 hostNetwork: true 和 dnsPolicy: ClusterFirstWithHostNet参数。我们可以看到Pod的IP就直接是Node节点的IP。

3)使用负载均衡器
使用平台自带的负载均衡器或者自己搭建一个haproxy+keepalived(后面分享)

最后,不同版本之间略有不同,请根据大致思路选择适合自己的版本。

更多关于kubernetes的知识分享,请前往博客主页。编写过程中,难免出现差错,敬请指出

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

margu_168

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

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

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

打赏作者

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

抵扣说明:

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

余额充值