搭建etcd 3.4.15集群(详细教程,包括选举过程、数据备份和恢复)

推荐阅读

Helm3(K8S 资源对象管理工具)视频教程:https://edu.csdn.net/course/detail/32506
Helm3(K8S 资源对象管理工具)博客专栏:https://blog.csdn.net/xzk9381/category_10895812.html

本文原文链接:https://blog.csdn.net/xzk9381/article/details/116533333,转载请注明出处。如有发现文章中的任何问题,欢迎评论区留言。

现在 Kubernetes 已经成为行业内的主流,那么作为存储了 Kubernetes 集群所有数据的 ETCD 集群也成为了重中之重。了解 ETCD 的原理以及维护方式就显得尤为重要。所以本文首先单独搭建一套 ETCD 集群,并且演示如何备份 ETCD 数据以及恢复的过程。

一、搭建 3 节点 ETCD 集群

生产环境上搭建集群建议使用奇数节点,这样可以保证 ETCD 集群在损坏部分机器时也可以选举正常。这里我们演示搭建一个 3 节点的集群。使用的 ETCD 版本为 3.4.15。同时为了后续更换节点服务器,我们这里要对每个节点的 IP 地址配置域名。

ETCD 名称节点 IP 地址域名版本
etcd0110.211.55.11etcd1.ops.comv3.4.15
etcd0210.211.55.12etcd2.ops.comv3.4.15
etcd0310.211.55.13etcd3.ops.comv3.4.15
1. 使用 cfssl 制作证书

在 10.211.55.11 节点的 /home 目录中创建证书目录:

mkdir -p /home/etcd_cluster/ca

下载 cfssl 工具集

wget https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssl_1.4.1_linux_amd64 -O cfssl
wget https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssljson_1.4.1_linux_amd64 -O cfssljson
wget https://github.com/cloudflare/cfssl/releases/download/v1.4.1/cfssl-certinfo_1.4.1_linux_amd64 -O cfssl-certinfo

将下载的工具集拷贝到 /usr/local/bin 目录下并赋予执行权限(也可以在 wget 时直接使用 -O 选项指定下载目录)

mv cfssl* /usr/local/bin
chmod u+x /usr/local/bin/cfssl*

接下来签发一个根证书,进入到 /home/etcd_cluster/ca 目录下,创建一个名为 ca-config.json 的文件,用于生成 CA 文件。内容如下:

{
    "signing": {
        "default": {
            "expiry": "87600h"
        },
        "profiles": {
            "server": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            },
            "client": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            },
            "etcd": {
                "expiry": "87600h",
                "usages": [
                    "signing",
                    "key encipherment",
                    "server auth",
                    "client auth"
                ]
            }
        }
    }
}

然后创建一个用于生成 CA 证书签名请求(CSR)的 JSON 配置文件 ca-csr.json,内容如下:

{
  "CN": "kubernetes-ca",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "ops"
    }
  ],
    "ca": {
       "expiry": "87600h"
    }
}
  • CN:Common Name:kube-apiserver 从证书中提取该字段作为请求的用户名 (User Name),浏览器使用该字段验证网站是否合法;
  • O:Organization:kube-apiserver 从证书中提取该字段作为请求用户所属的组 (Group);
  • kube-apiserver 将提取的 User、Group 作为 RBAC 授权的用户标识;

需要注意的是,不同证书 csr 文件的 CN、C、ST、L、O、OU 组合必须不同,否则可能出现 PEER'S CERTIFICATE HAS AN INVALID SIGNATURE 错误;另外后续创建证书的 csr 文件时,CN 都要确保不相同(C、ST、L、O、OU 相同),以达到区分的目的。

接下来使用 cfssl 根据 ca-csr.json 文件生成 CA 证书和私钥,命令执行完成后会在当前目录下生成三个文件 ca.csr 、ca.pem 和 ca-key.pem。命令如下::

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

最后再创建一个 etcd-csr.json 文件,用于生成 ETCD 集群使用的证书,这里需要在 hosts 字段中指定授权使用该证书的的 IP 地址和域名列表,在这里我们直接指定域名就可以:

{
  "CN": "etcd-server",
  "hosts": [
    "etcd1.ops.com",
    "etcd2.ops.com",
    "etcd3.ops.com"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "ops"
    }
  ]
}

使用如下命令生成 ETCD 证书:

cfssl gencert -ca=ca.pem \
    -ca-key=ca-key.pem \
    -config=ca-config.json \
    -profile=etcd etcd-csr.json | cfssljson -bare etcd

命令执行完成后会生成与 ETCD 有关的三个证书文件:

etcd.csr
etcd-key.pem
etcd.pem

这样所需要的证书文件就已经全部生成好了,接下来我们将 10.211.55.11 机器上的 /home/etcd_cluster 目录拷贝到其他两个节点上:

for i in 12 13
do
	scp -pr /home/etcd_cluster root@10.211.55.${i}:/home
done
2. 安装 etcd 集群

首先关闭所有节点的防火墙和 SELinux:

systemctl stop firewalld.service && systemctl disable firewalld.service
setenforce 0 && sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config

到官网中下载 etcd 3.4.15 的二进制包到 10.211.55.11 机器上,并且解压到 /home/etcd_cluster 目录下:

wget https://github.com/etcd-io/etcd/releases/download/v3.4.15/etcd-v3.4.15-linux-amd64.tar.gztar zxf etcd-v3.4.15-linux-amd64.tar.gz -C /home/etcd_cluster

将解压后的目录创建一个软链接:

ln -sf /home/etcd_cluster/etcd-v3.4.15-linux-amd64 /home/etcd_cluster/etcd

进入 /home/etcd_cluster/etcd 目录,创建一个 etcd.conf 的文件作为 etcd 节点的配置文件,内容如下:

#[Member]
ETCD_NAME="etcd01"
ETCD_DATA_DIR="/home/etcd_cluster/etcd_data"
ETCD_LISTEN_PEER_URLS="https://0.0.0.0:2380"
ETCD_LISTEN_CLIENT_URLS="https://0.0.0.0:2379"

#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://etcd1.ops.com:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://etcd1.ops.com:2379"
ETCD_INITIAL_CLUSTER="etcd01=https://etcd1.ops.com:2380,etcd02=https://etcd2.ops.com:2380,etcd03=https://etcd3.ops.com:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"
ETCD_ENABLE_V2="true"

#[Security]
ETCD_CERT_FILE="/home/etcd_cluster/ca/etcd.pem"
ETCD_KEY_FILE="/home/etcd_cluster/ca/etcd-key.pem"
ETCD_TRUSTED_CA_FILE="/home/etcd_cluster/ca/ca.pem"
ETCD_CLIENT_CERT_AUTH="true"
ETCD_PEER_CERT_FILE="/home/etcd_cluster/ca/etcd.pem"
ETCD_PEER_KEY_FILE="/home/etcd_cluster/ca/etcd-key.pem"
ETCD_PEER_TRUSTED_CA_FILE="/home/etcd_cluster/ca/ca.pem"
ETCD_PEER_CLIENT_CERT_AUTH="true"

ETCD 3.4 版本中的 ETCDCTL_API=3 etcdctl 和 etcd --enable-v2=false 成为了默认配置,如果要使用v2版本,那么执行 etcdctl 的时候需要设置 ETCDCTL_API 的环境变量,例如:ETCDCTL_API=2 etcdctl

需要注意的是,在 etcd 3.4.x 版本中,会自动读取环境变量的参数,所以如果在 etcd.conf 中定义了环境变量,就不需要在 etcd.service 中添加相同的配置,否则会触发类似报错"conflicting environment variable “ETCD_NAME” is shadowed by corresponding command-line flag (either unset environment variable or disable flag)"

还有比较重要的一点,flannel 操作 etcd 使用的是 v2 的 API,而 kubernetes 操作 etcd 使用的 v3 的 API,为了兼容 flannel,将默认开启 v2 版本,所以需要在配置文件中设置 ETCD_ENABLE_V2=“true”

接下来将 10.211.55.11 上的 /home/etcd_cluster/etcd 目录拷贝到另外两个节点上:

for i in 12 13
do
	scp -pr /home/etcd_cluster/etcd root@10.211.55.${i}:/home/etcd_cluster
done

拷贝完成后,修改另外两个节点上的配置文件:

  • 首先将 ETCD_NAME 配置项分别修改为 etcd02 和 etcd03
  • 将 ETCD_INITIAL_ADVERTISE_PEER_URLS 配置项的域名分别修改为其节点对应的域名
  • 将 ETCD_ADVERTISE_CLIENT_URLS 配置项的域名分别修改为其节点对应的域名

本文原文链接:https://blog.csdn.net/xzk9381/article/details/116533333,转载请注明出处。如有发现文章中的任何问题,欢迎评论区留言。

3. 创建 systemd 文件

这里我们使用 systemd 来管理 etcd 服务的进程,首先创建 /usr/lib/systemd/system/etcd.service 文件,内容如下:

[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=/home/etcd_cluster/etcd/etcd.conf
ExecStart=/home/etcd_cluster/etcd/etcd
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

将这个文件拷贝到另外两个节点上:

for i in 12 13
do	
	scp -pr /usr/lib/systemd/system/etcd.service root@10.211.55.${i}:/usr/lib/systemd/system/etcd.service
done

然后分别在这三台机器中执行如下命令:

systemctl daemon-reload
4. 启动 etcd 并验证集群状态

启动各个节点的 etcd 服务:

systemctl start etcd

使用如下命令查看集群状态:

/home/etcd_cluster/etcd/etcdctl --cacert=/home/etcd_cluster/ca/ca.pem \
--cert=/home/etcd_cluster/ca/etcd.pem \
--key=/home/etcd_cluster/ca/etcd-key.pem \
--endpoints="https://etcd1.ops.com:2379,https://etcd2.ops.com:2379,https://etcd3.ops.com:2379" \
endpoint status --write-out=table

返回如下结果(内容太长,仅展示部分信息):

+----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+
|          ENDPOINT          |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | 
+----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+
| https://etcd1.ops.com:2379 | 94e79d1fc0bdf547 |  3.4.15 |   20 kB |      true |      false |       173 |          9 | 
| https://etcd2.ops.com:2379 | a398f8d1a7c02900 |  3.4.15 |   20 kB |     false |      false |       173 |          9 | 
| https://etcd3.ops.com:2379 | fee932c0172c33ae |  3.4.15 |   20 kB |     false |      false |       173 |          9 |
+----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+

可以看到当前集群中,ID 为 94e79d1fc0bdf547 的节点被选举为 LEADER。

二、ETCD 集群的选举过程

接下来我们验证一下 ETCD 集群的选举过程,我们当前有三个节点,每个节点的 ID 可以在上面的返回结果中看到。

首先我们停掉 10.211.55.11 上 ID 为 94e79d1fc0bdf547 的服务,然后 10.211.55.12 (a398f8d1a7c02900)机器上收到了消息,发现 94e79d1fc0bdf547 心跳超时,并且发起了新的选举过程(任期为 173+1 ):

5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 [term 173] received MsgTimeoutNow from 94e79d1fc0bdf547 and starts an election to get leadership.

10.211.55.12 (a398f8d1a7c02900)成为了候选者,并且为自己投了一票:

5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 became candidate at term 174
5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 received MsgVoteResp from a398f8d1a7c02900 at term 174

然后分别向 10.211.55.11(94e79d1fc0bdf547) 和 10.211.55.13(fee932c0172c33ae) 上发送消息,希望获得选票:

5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 [logterm: 173, index: 9] sent MsgVote request to 94e79d1fc0bdf547 at term 174
5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 [logterm: 173, index: 9] sent MsgVote request to fee932c0172c33ae at term 174

由于 10.211.55.11(94e79d1fc0bdf547)已经停掉,所以肯定无法收到消息:

5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: raft.node: a398f8d1a7c02900 lost leader 94e79d1fc0bdf547 at term 174

10.211.55.13(fee932c0172c33ae)上收到了 10.211.55.12(a398f8d1a7c02900)发送过来的投票信息,因为任期 174 大于当前的 173,所以知道是发起了新一轮选举,因此给 10.211.55.12(a398f8d1a7c02900)投了一票。这里任期 term 是关键,也就是说,谁先感受到 10.211.55.11(94e79d1fc0bdf547)宕机,发起投票,谁就可以成为新的 leader,这个也和进程初始的启动时间有关:

5月 08 15:18:46 etc3.ops.com etcd[1575]: raft2021/05/08 15:18:46 INFO: fee932c0172c33ae [term: 173] received a MsgVote message with higher term from a398f8d1a7c02900 [term: 174]
5月 08 15:18:46 etc3.ops.com etcd[1575]: raft2021/05/08 15:18:46 INFO: fee932c0172c33ae became follower at term 174
5月 08 15:18:46 etc3.ops.com etcd[1575]: raft2021/05/08 15:18:46 INFO: fee932c0172c33ae [logterm: 173, index: 9, vote: 0] cast MsgVote for a398f8d1a7c02900 [logterm: 173, index: 9] at term 174
5月 08 15:18:46 etc3.ops.com etcd[1575]: raft2021/05/08 15:18:46 INFO: raft.node: fee932c0172c33ae lost leader 94e79d1fc0bdf547 at term 174
5月 08 15:18:46 etc3.ops.com etcd[1575]: raft2021/05/08 15:18:46 INFO: raft.node: fee932c0172c33ae elected leader a398f8d1a7c02900 at term 174

这样 10.211.55.12(a398f8d1a7c02900)就获得了两票,超过了当前集群节点的半数,所以成为了新的 leader,任期为 174:

5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 received MsgVoteResp from fee932c0172c33ae at term 174
5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 has received 2 MsgVoteResp votes and 0 vote rejections
5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: a398f8d1a7c02900 became leader at term 174
5月 08 15:18:46 etc2.ops.com etcd[2377]: raft2021/05/08 15:18:46 INFO: raft.node: a398f8d1a7c02900 elected leader a398f8d1a7c02900 at term 174

这样一个完整的选举过程就结束了。

以上日志的信息可以通过 journalctl --no-pager -u etcd.service 命令进行查看

三、备份和导入数据

1. 备份数据

ETCD 集群的数据非常重要,所以需要定期进行备份,以便在出现问题的时候能够及时还原数据。这里我们演示一下如何对数据进行备份和恢复。

首先我们在 10.211.55.11 节点上开始写入数据,命令如下:

/home/etcd_cluster/etcd/etcdctl --cacert=/home/etcd_cluster/ca/ca.pem \
--cert=/home/etcd_cluster/ca/etcd.pem \
--key=/home/etcd_cluster/ca/etcd-key.pem \
--endpoints="https://etcd1.ops.com:2379,https://etcd2.ops.com:2379,https://etcd3.ops.com:2379" \
put name1 aaron

在 10.211.55.12 节点上验证一下是否能读到数据:

/home/etcd_cluster/etcd/etcdctl --cacert=/home/etcd_cluster/ca/ca.pem \
--cert=/home/etcd_cluster/ca/etcd.pem \
--key=/home/etcd_cluster/ca/etcd-key.pem \
--endpoints="https://etcd1.ops.com:2379,https://etcd2.ops.com:2379,https://etcd3.ops.com:2379" \
get name1

返回结果如下:

name1
aaron

接下来我们备份数据,由于集群中所有节点的数据都是同步的,所以我们可以只备份一个节点的数据,当然为了保险起见,实际使用中还是建议备份多个节点的数据。这里我们仅备份 10.211.55.11 节点上的数据,命令如下:

/home/etcd_cluster/etcd/etcdctl \
--cacert=/home/etcd_cluster/ca/ca.pem \
--cert=/home/etcd_cluster/ca/etcd.pem \
--key=/home/etcd_cluster/ca/etcd-key.pem \
--endpoints="https://etcd1.ops.com:2379" \
snapshot save /home/snap.db

需要注意的是,备份数据时,备份哪个节点的数据,–endpoints 参数就写哪个节点的 URL,不需要写所有节点的地址。

返回的结果如下:

{"level":"info","ts":1620460599.3984702,"caller":"snapshot/v3_snapshot.go:119","msg":"created temporary db file","path":"/home/snap.db.part"}
{"level":"info","ts":"2021-05-08T15:56:39.405+0800","caller":"clientv3/maintenance.go:200","msg":"opened snapshot stream; downloading"}
{"level":"info","ts":1620460599.4052668,"caller":"snapshot/v3_snapshot.go:127","msg":"fetching snapshot","endpoint":"https://etcd1.ops.com:2379"}
{"level":"info","ts":"2021-05-08T15:56:39.407+0800","caller":"clientv3/maintenance.go:208","msg":"completed snapshot read; closing"}
{"level":"info","ts":1620460599.407849,"caller":"snapshot/v3_snapshot.go:142","msg":"fetched snapshot","endpoint":"https://etcd1.ops.com:2379","size":"20 kB","took":0.009294772}
{"level":"info","ts":1620460599.4079185,"caller":"snapshot/v3_snapshot.go:152","msg":"saved","path":"/home/snap.db"}
Snapshot saved at /home/snap.db

这样就代表快照备份成功了。

2. 恢复数据

接下来开始恢复数据。恢复数据的时候需要停止所有节点的服务,如果 k8s 集群也在使用这个 etcd 集群,那么 k8s 集群上的 api-server 服务也需要停止:

systemctl stop etcd

首先将所有节点的数据目录进行重命名,我们前面搭建集群的时候指定的数据目录是 /home/etcd_cluster/etcd_data,那么我们将这个目录重命名即可:

mv /home/etcd_cluster/etcd_data /home/etcd_cluster/etcd_data_bak

然后在 10.211.55.11 节点上使用如下命令恢复快照数据:

/home/etcd_cluster/etcd/etcdctl snapshot restore /home/snap.db \
--name etcd01 \
--initial-cluster="etcd01=https://etcd1.ops.com:2380,etcd02=https://etcd2.ops.com:2380,etcd03=https://etcd3.ops.com:2380" \
--initial-cluster-token="etcd-cluster" \
--initial-advertise-peer-urls="https://etcd1.ops.com:2380" \
--data-dir="/home/etcd_cluster/etcd_data"

以上参数的值都是前面写入到 etcd.conf 配置文件中的内容

返回的信息如下:

{"level":"info","ts":1620461399.9693778,"caller":"netutil/netutil.go:112","msg":"resolved URL Host","url":"https://etcd1.ops.com:2380","host":"etcd1.ops.com:2380","resolved-addr":"10.211.55.11:2380"}
{"level":"info","ts":1620461399.9700494,"caller":"netutil/netutil.go:112","msg":"resolved URL Host","url":"https://etcd1.ops.com:2380","host":"etcd1.ops.com:2380","resolved-addr":"10.211.55.11:2380"}
{"level":"info","ts":1620461399.9701214,"caller":"snapshot/v3_snapshot.go:296","msg":"restoring snapshot","path":"/home/snap.db","wal-dir":"/home/etcd_cluster/etcd_data/member/wal","data-dir":"/home/etcd_cluster/etcd_data","snap-dir":"/home/etcd_cluster/etcd_data/member/snap"}
{"level":"info","ts":1620461399.972704,"caller":"membership/cluster.go:392","msg":"added member","cluster-id":"1e38f6c389d03db0","local-member-id":"0","added-peer-id":"94e79d1fc0bdf547","added-peer-peer-urls":["https://etcd1.ops.com:2380"]}
{"level":"info","ts":1620461399.9727504,"caller":"membership/cluster.go:392","msg":"added member","cluster-id":"1e38f6c389d03db0","local-member-id":"0","added-peer-id":"a398f8d1a7c02900","added-peer-peer-urls":["https://etcd2.ops.com:2380"]}
{"level":"info","ts":1620461399.972775,"caller":"membership/cluster.go:392","msg":"added member","cluster-id":"1e38f6c389d03db0","local-member-id":"0","added-peer-id":"fee932c0172c33ae","added-peer-peer-urls":["https://etcd3.ops.com:2380"]}
{"level":"info","ts":1620461399.9756339,"caller":"snapshot/v3_snapshot.go:309","msg":"restored snapshot","path":"/home/snap.db","wal-dir":"/home/etcd_cluster/etcd_data/member/wal","data-dir":"/home/etcd_cluster/etcd_data","snap-dir":"/home/etcd_cluster/etcd_data/member/snap"}

接下来将快照备份文件拷贝到另外两个节点上,然后分别执行恢复的命令。需要注意的是,执行恢复命令时,需要将命令中的 --name--initial-advertise-peer-urls 参数的值替换为当前机器中对应的值。

所有节点的数据都恢复完成后,启动 etcd 服务。然后在任意节点中查看数据:

/home/etcd_cluster/etcd/etcdctl --cacert=/home/etcd_cluster/ca/ca.pem \
--cert=/home/etcd_cluster/ca/etcd.pem \
--key=/home/etcd_cluster/ca/etcd-key.pem \
--endpoints="https://etcd1.ops.com:2379,https://etcd2.ops.com:2379,https://etcd3.ops.com:2379" \
get name1

返回结果如下:

name1
aaron

代表数据已经恢复成功。

3. etcd 的数据存储

最后说一下 etcd 的数据存储。etcd的存储分为内存存储和持久化(硬盘)存储两部分,内存中的存储除了顺序化的记录下所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。而持久化则使用预写式日志(WAL:Write Ahead Log)进行记录存储。

在WAL的体系中,所有的数据在提交之前都会进行日志记录。在etcd的持久化存储目录中,有两个子目录。一个是WAL,存储着所有事务的变化记录;另一个则是snapshot,用于存储某一个时刻etcd所有目录的数据。通过WAL和snapshot相结合的方式,etcd可以有效的进行数据存储和节点故障恢复等操作。

既然有了WAL实时存储了所有的变更,为什么还需要snapshot呢?随着使用量的增加,WAL存储的数据会暴增,为了防止磁盘很快就爆满,etcd默认每10000条记录做一次snapshot,经过snapshot以后的WAL文件就可以删除。而通过API可以查询的历史etcd操作默认为1000条。

本文原文链接:https://blog.csdn.net/xzk9381/article/details/116533333,转载请注明出处。如有发现文章中的任何问题,欢迎评论区留言。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

店伙计

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

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

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

打赏作者

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

抵扣说明:

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

余额充值