Ingress-配置和使用

1 环境准备

下面的 yaml 文件内容,是使用 sts 创建两个 web 服务,并配置对应的 servcie。web 服务的首页内容使用 configmap 配置并挂载到各自的 POD 中。

apiVersion: v1
kind: Namespace
metadata:
  name: shark-test
---
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: shark-test
  name: index.html
data:
  # 类属性键;每一个键都映射到一个简单的值
  web1.index.html: |
    web1 站点

  web2.index.html: |
    web2 站点
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: shark-test
  name: web1
spec:
  selector:
    matchLabels:
      app: web1 # 必须匹配 .spec.template.metadata.labels
  serviceName: "web1"
  replicas: 1
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: web1 # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: web1
        image: nginx:1.21.6-alpine
        ports:
        - containerPort: 80
          name: web1
        volumeMounts:
        - name: index-html
          mountPath: /usr/share/nginx/html
      volumes:
      - name: index-html          # 给 volumeMounts[].name使用
        configMap:
          name: index.html   # configmap 的名称
          items:
          - key: web1.index.html  # configmap 对象中 data 中的一个 key
            path: index.html      # 挂载到 pod 后,被创建的文件名称
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: shark-test
  name: web2
spec:
  selector:
    matchLabels:
      app: web2 # 必须匹配 .spec.template.metadata.labels
  serviceName: "web2"
  replicas: 1
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: web2 # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: web2
        image: nginx:1.21.6-alpine
        ports:
        - containerPort: 80
          name: web2
        volumeMounts:
        - name: index-html
          mountPath: /usr/share/nginx/html
      volumes:
      - name: index-html          # 给 volumeMounts[].name使用
        configMap:
          name: index.html   # configmap 的名称
          items:
          - key: web2.index.html  # configmap 对象中 data 中的一个 key
            path: index.html      # 挂载到 pod 后,被创建的文件名称
---
apiVersion: v1
kind: Service
metadata:
  namespace: shark-test
  name: web1
  labels:
    app: web1
spec:
  type: ClusterIP
  ports:
  - port: 8080
    name: web1-http
    targetPort: 80
  clusterIP: None
  selector:
    app: web1
---
apiVersion: v1
kind: Service
metadata:
  namespace: shark-test
  name: web2
  labels:
    app: web2
spec:
  type: ClusterIP
  ports:
  - port: 8080
    name: web2-http
    targetPort: 80
  clusterIP: None
  selector:
    app: web2

2 什么是 Ingress

Ingress 是管理集群外部访问集群内部服务的流量的 API 对象,是 kubernetes 中对 service 的反向代理。 流量的去向由 Ingress 资源所定义的规则来控制。
可提供负载均衡、SSL 和基于名称的虚拟主机功能。

主要的访问方式是 HTTP/HTTPS,不支持四层协议。下一代替代 Ingress 的产品 Gateway API 可以实现 四层和七层协议流量管理。

Ingress 在 kubernentes v1.28 版本停止更新,并推出了可以实现更多功能的 Gateway API

集群
负载均衡器
Ingress 的路由规则
Ingress
Pod
Service
Pod
客户端

2.1 认识 Ingress 资源

Ingress 资源的 YAML 文件,可以理解为是对 Nginx 子配置文件的抽象,因为它和 nginx 中关于虚拟机主机 server 配置块的功能一致 。

下面是 Nginx 子配置文件和Ingress 的 yaml 文件的对比图。
在这里插入图片描述

  1. 红色框就是请求中的 url
  2. 黄色框就是 nginx 的 rewrite ,用于实现地址(url)重写,这里实现的功能是去掉请求 url 中的前缀。
  3. 绿色框就是 后端服务的连接信息,nginx 中是 upstream 的名称,kubernetes 中是 service 名称
  4. 紫色框就是后端服务的监听端口。

资源名称: Ingress 对象的命名必须是合法的 DNS 子域名名称。

  • 不能超过 253 个字符
  • 只能包含小写字母、数字,以及 ‘-’ 和 ‘.’
  • 必须以字母数字开头
  • 必须以字母数字结尾
    注解(annotations): Ingress 经常使用一些注解来配置一些选项,以便扩展功能,例如 rewrite-target 注解。 不同的 Ingress 控制器支持不同的注解。

规则(rule): 其中包含对所有入站请求进行匹配的规则列表。

2.2 Ingress 控制器(controller)

为了让 Ingress 对象有效的创建和工作,需要有一个 Ingress Controller 。
Ingress Controller 的角色是用一个反向代理实现的。反向代理可以是 nginx、haproxy 等其中的一个,因此 Ingress Controller 可以有很多种。

这里我用最常用的 Ingress-Nginx Controller 为例说明。

当一个 Ingress 对象被成功创建后,Ingress Controller 会把 Ingress 对象中的内容转换成 nginx 的子配置文件,并让 Ingress Controller 使用。

后续会详细介绍。

2.3 Ingress 规则

一个简单的 Ingress 资源示例。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx-example
  rules:
  - http:
      paths:
      - path: "/web1"
        pathType: Prefix
        backend:
          service:
            name: web1
            port:
              number: 8080

每个 HTTP 规则都包含以下信息:

  • 可选的 host。在此示例中,未指定 host,因此该规则适用于使用指定 IP 地址进行入站 HTTP 请求的情况。 如果提供了 host(例如 foo.shark.com),则 rules 适用于所指定的主机( foo.shark.com)。
  • path 路径列表(例如 /web1)。每个路径都有一个由 service.nameservice.port.nameservice.port.number 确定的关联后端。入站请求的内容都必须与 hostpath 的值相匹配,负载均衡器才会将流量引导到所引用的 Service,
  • backend(后端)是 Service 中所定义的 名称和端口的组合, 或者是通过 CRD 方式来实现的自定义资源后端。

通常会在 Ingress 控制器中配置 defaultBackend(默认后端), 以便为无法与规约中任何路径匹配的所有请求提供服务,也就是通常会返回 404 页面。

2.4 pathType 路径类型

Ingress 中的每个路径都需要有对应的路径类型(Path Type)。未明确设置 pathType 的路径无法通过合法性检查。当前支持的路径类型有三种:

  1. ImplementationSpecific:对于这种路径类型,匹配方法取决于 IngressClass。 具体实现可以将其作为单独的 pathType 处理或者作与 Prefix 或 Exact 类型相同的处理。nginx 的 ingress 是支持 path 使用正则。

  2. Exact:精确匹配 URL 路径,且区分大小写。

  3. Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写, 并且对路径中各个元素逐个执行匹配操作。 路径元素指的是由 / 分隔符分隔的路径中的标签列表。

说明: 如果 path 值的最后一个元素是请求路径中最后一个元素的子字符串,则不会被视为匹配 (例如:/foo/bar 匹配 /foo/bar/baz, 但不匹配 /foo/barbaz)。

2.5 多重匹配

有的时候,一个请求会和一个 Ingress 中的多个 path 匹配,这时 path 最长者优先匹配。 如果仍然有两条同等的匹配路径,则精确路径类型优先于前缀路径类型。

2.6 Ingress 类

Ingress 可以由不同的控制器实现,通常使用不同的配置。 每个 Ingress 应当指定一个类,也就是一个对 IngressClass 资源的引用。 IngressClass 资源包含额外的配置,其中包括应当实现该类的控制器名称。

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
spec:
  controller: k8s.io/ingress-nginx

设置默认的 Ingress 类,只需要在注解中添加:
ingressclass.kubernetes.io/is-default-class: "true"

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  labels:
    app.kubernetes.io/component: controller
  name: nginx-example
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
spec:
  controller: k8s.io/ingress-nginx

2.7 TLS

2.7.1 生成证书

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj “/C=CN/ST=BJ/L=BJ/O=nginx/CN=itheima.com”

2.7.2 创建密钥

可以通过设定包含 TLS 私钥和证书的Secret 来保护 Ingress。TLS Secret 的数据中必须包含键名为 tls.crt 的证书和键名为 tls.key 的私钥, 才能用于 TLS 目的。例如:

apiVersion: v1
kind: Secret
metadata:
  name: testsecret-tls
  namespace: default
data:
  tls.crt: base64 编码的证书
  tls.key: base64 编码的私钥
type: kubernetes.io/tls

也可以使用下面的方法创建

kubectl create secret tls tls-secret --key tls.key --cert tls.crt

注意,不能针对默认规则使用 TLS,因为这样做需要为所有可能的子域名签发证书。 因此,tls 字段中的 hosts 的取值需要与 rules 字段中的 host 完全匹配。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-example-ingress
spec:
  tls:
  - hosts:
      - https-example.foo.com
    secretName: testsecret-tls
  rules:
  - host: https-example.foo.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: service1
            port:
              number: 80

3 自定义配置

有三种方式可以自定义NGINX:

  1. ConfigMap:使用ConfigMap在NGINX中设置全局配置。

  2. annotations 注解:如果您想要特定Ingress规则的特定配置,请使用此注释。

  3. Custom NGINX template 自定义模板:当需要更具体的设置时,如open_file_cache,将侦听选项调整为rcvbuf,或者当无法通过ConfigMap更改配置时。

3.1 使用 ConfigMap(作用于全局)

ConfigMap API资源将配置数据存储为键-值对。该数据提供了nginx控制器的系统组件配置。

ConfigMap 的配置会是全局生效,就是会组用于所有使用默认值的 location 中的配置,如果某个 location 已经已经创建并且指定了自定义的值,ConfigMap 的值不会覆盖这个 location 的值。

ingress-nginx 的 controller 使用的 ConfigMap 是在 ingress-nginx controller 的 Deployment 中通过 args 参数指定的,如下所示:

    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
        - --election-id=ingress-nginx-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller  # 我在这里
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace

为了覆盖 ingress-nginx 的配置值,可以在 config-map 中使用 key - value 的方式进行配置。

3.1.1 配置 ssl 算法

some-configmap.yml

kind: ConfigMap
apiVersion: v1
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  allow-snippet-annotations: "true"  # 默认的 ConfigMap 中自带的,如果不设置,默认值会是 false. true 表示可以使用注解配置片段。后面会讲到。
  ssl-ciphers: 'ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!eNULL:!LOW:!ADH:!RC4:!3DES:!MD5:!EXP:!PSK:!SRP:!DSS'
  

如上配置中,ssl-ciphers 是新增的配置,它会覆盖原有配置的值。

可以使用如下命令验证:

kubectl -n ingress-nginx exec -it ingress-nginx-controller-64946b8fcf-kr8f5 -- cat nginx.conf |grep ssl

上面命令中的 pod ingress-nginx-controller-64946b8fcf-kr8f5 更换为你自己环境中的 pod。

如果在 configmap 中表示 truefalse 或者数字,需要加上双引号 ""

这里的 ConfigMap 对象的名称 ingress-nginx-controller 是固定的。

ConfigMap 中可以使用的配置选项只能是官方文档已公布的配置选项,具体可以参考这里的官方文档

3.1.2 配置全局的允许客户端请求文件大小

ConfigMap 的配置会是全局生效,就是会组用于所有使用默认值的 location 中的配置,如果某个 location 已经已经创建并且指定了自定义的值,ConfigMap 的值不会覆盖这个 location 的值。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  allow-snippet-annotations: "true"
  proxy-body-size: 1020M

3.2 使用 注解 annotations

前提条件: 需要在 Ingress-controller 对象使用的 ConfigMap 中添加 allow-snippet-annotations: "true"

安全隐患: 这将允许可创建或更新入口对象的用户可使用自定义代码段功能获取集群中的所有密钥

你可以通过在 Ingress 资源里添加不同的 annotations ,对 Ingress 扩展不同的功能。例如,地址重写。

annotations 的键和值只能是字符串。其他类型,如布尔值或数值,必须加上英文的双引号,例如“true”、“false”、“100”。

3.2.1 地址重写

可以使用如下注解,控制地址重写。

注解名称描述注解的值
nginx.ingress.kubernetes.io/rewrite-target必须重定向流量的目标URIstring
nginx.ingress.kubernetes.io/ssl-redirect表明 location 部分是否只能通过SSL访问(当Ingress包含证书时,默认为True)bool
nginx.ingress.kubernetes.io/force-ssl-redirect即使 Ingress 未启用TLS,也强制重定向到HTTPSbool
nginx.ingress.kubernetes.io/app-root定义控制器必须将请求根 / 路径转发到此注解指定的值string
nginx.ingress.kubernetes.io/use-regexIngress上定义的 path 的值是否可以使用正则表达式bool
示例1:重定向根到指定路径
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: shark-test
  name: minimal2-ingress
  annotations:
    nginx.ingress.kubernetes.io/app-root: /api/v1 # 重定向到 /api/v1
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: "/"
        pathType: Prefix
        backend:
          service:
            name: web1
            port:
              number: 8080

验证
在这里插入图片描述

示例2: 将去除前缀的地址重写(无正则)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: shark-test
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: "/web1"
        pathType: Prefix
        backend:
          service:
            name: web1
            port:
              number: 8080
      - path: "/web2"
        pathType: Prefix
        backend:
          service:
            name: web2
            port:
              number: 8080

验证
在这里插入图片描述

示例3: 将去除前缀的地址重写(有正则)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: shark-test
  name: regex-ingress
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: "/web-a(/|$)(.*)"
        pathType: ImplementationSpecific  # 注意这里的类型
        backend:
          service:
            name: web1
            port:
              number: 8080
      - path: "/web-b(/|$)(.*)"
        pathType: ImplementationSpecific
        backend:
          service:
            name: web2
            port:
              number: 8080

验证
在这里插入图片描述

3.2.2 使用配置片段 设置变量,添加请求头

使用注解,可以在服务器配置块中添加自定义配置

注解名称描述注解的值
nginx.ingress.kubernetes.io/server-snippet向 Nginx 的 server {…} 配置块中添加配置string
nginx.ingress.kubernetes.io/configuration-snippet向 Nginx 的 location {…} 配置块中添加配置string
示例1: 向 server 配置块中添加自定义配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: shark-test
  name: server-snippet-ingress
  annotations:
    nginx.ingress.kubernetes.io/server-snippet: |
      set $cors '';
      if ($http_origin ~* "^http://.*$") {
          set $cors $http_origin;
      }
      add_header Access-Control-Allow-Origin $cors;
      add_header Access-Control-Allow-Credentials 'true';
      add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
      add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
      if ($request_method = 'OPTIONS') {
          return 204;
      }

spec:
  ingressClassName: nginx
  ...

配置成功后,会在 Ingress controller POD 中的 /etc/nginx/nginx.conf 配置文件中出现如下配置
在这里插入图片描述

示例2: 向 location 配置块中添加自定义配置

会向此次yaml 文件中所有已配置的 path 中添加这些自定义配置

ingress-location-snippert.yml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: shark-test
  name: location-snippet-ingress
  annotations:
    nginx.ingress.kubernetes.io/configuration-snippet: |
      more_set_headers "Request-Id: $req_id";
      set $cors '';
      if ($http_origin ~* "^http://.*$") {
          set $cors $http_origin;
      }
      add_header Access-Control-Allow-Origin $cors;
      add_header Access-Control-Allow-Credentials 'true';
      add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
      add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
      if ($request_method = 'OPTIONS') {
          return 204;
      }

spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: "/shark"
        pathType: Prefix
        backend:
          service:
            name: web1
            port:
              number: 8080
      - path: "/xiguatian"
        pathType: Prefix
        backend:
          service:
            name: web2
            port:
              number: 8080

创建成功后会在 Ingress controller POD 中的 nginx.conf 中增加如下配置内容:

location ~* "^/shark" {
                       此处省略一万字
             
                        more_set_headers "Request-Id: $req_id";
                        set $cors '';
                        if ($http_origin ~* "^http://.*$") {
                                set $cors $http_origin;
                        }
                        add_header Access-Control-Allow-Origin $cors;
                        add_header Access-Control-Allow-Credentials 'true';
                        add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
                        add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
                        if ($request_method = 'OPTIONS') {
                                return 204;
                        }

                        proxy_pass http://upstream_balancer;

                        proxy_redirect                          off;

                }
location ~* "^/xiguatian" {
                       此处省略一万字
             
                        more_set_headers "Request-Id: $req_id";
                        set $cors '';
                        if ($http_origin ~* "^http://.*$") {
                                set $cors $http_origin;
                        }
                        add_header Access-Control-Allow-Origin $cors;
                        add_header Access-Control-Allow-Credentials 'true';
                        add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,PATCH,OPTIONS;
                        add_header Access-Control-Allow-Headers 'App-Id,Secret-Key,Withcredentials,Keep-Alive,User-Agent,Content-Type,Accept,Language,Referer';
                        if ($request_method = 'OPTIONS') {
                                return 204;
                        }

                        proxy_pass http://upstream_balancer;

                        proxy_redirect                          off;

                }

3.2.3 引用自定义的 ConfigMap 添加请求头

你可以定义任意一个符合要求的 ConfigMap 名称的对象,之后在创建 ingress-nginx-controller ConfigMpa 使用引用。例如:

custom-addheader.yml

apiVersion: v1
data:
  X-Different-Name: "true"
  X-Request-Start: t=${msec}
  X-Using-Nginx-Controller: "true"
kind: ConfigMap
metadata:
  name: custom-headers      # 这个名称需要稍后使用
  namespace: ingress-nginx  # 这个需要和 ingress-controller 一致

注意 data 部分的内容将会是添加到 每个 location 中自定义请求头的内容。

创建这个 ConfigMap

kubectl apply -f custom-addheader.yml

此时,ingress-nginx 还不能直接使用这个 ConfigMap 中的内容。还需要在 ingress-controller 所在的 namespace (默认是 ingress-nginx)中创建一个名为 ingress-nginx-controller 的 ConfigMap, 并且 data 部分需要使用 proxy-set-headers 键,其中需要引用上面我们创建的 ConfigMap, 格式为: namespace/configmap的名称,如下所示:

apiVersion: v1
data:
  proxy-set-headers: "ingress-nginx/custom-headers"  # 这里的值: custom-headers 是引用上面创建  configmap
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx

3.2.4 使用 auth-url 设置额外的认证

假如你希望访问以 /shark 开头的图片资源都需要先到认证服务 shark-auth-server/api/v1/auth/check 认证,认证成功并返回 2xxx 后才可以继续访问 /shark/ 开头的图片资源。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: middleware
  name: middle-minio
  annotations:
    # 设置认证 url, 因为 /auth-images 是此 ingress Pod 所设置的一个location 所以,这里使用的 127.0.0.1
    nginx.ingress.kubernetes.io/auth-url: "http://127.0.0.1/auth-images"
    nginx.ingress.kubernetes.io/rewrite-target: "/$1"
#    nginx.ingress.kubernetes.io/configuration-snippet: |
#      auth_request /auth-images;
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: "/shark/(.*)"
        pathType: ImplementationSpecific
        backend:
          service:
            name: minio
            port:
              number: 9000

使用注解: nginx.ingress.kubernetes.io/auth-url: "http://127.0.0.1/auth-images" 或者使用如下注解都可以实现:

   annotations:
     nginx.ingress.kubernetes.io/configuration-snippet: |
       auth_request /auth-images;

同时需要添加如下 ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: spms-standard
  name: spms-standard-ingress-auth
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: "/api/v1/auth/check"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      proxy_set_header X-Original-URI $request_uri;
      proxy_pass_request_body off;
      proxy_set_header Content-Length "";
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: "/auth-images"
        pathType: Prefix
        backend:
          service:
            name: spms-standard-support-gateway
            port:
              number: 8080

4 查看内存中的后端

进入 ingress-nginx-controller Pod 内执行如下命令

curl localhost:10246/configuration/backends

5 参考文献

官方文档

  • 24
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

shark_西瓜甜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值