ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes 上部署高可用 Consul 集群:基于 StatefulSet 的 Helm Chart 实战指南

Kubernetes 上部署高可用 Consul 集群:基于 StatefulSet 的 Helm Chart 实战指南 【免费下载链接】charts⚠️(OBSOLETE) Curated applications for Kubernetes项目地址https://gitcode.com/gh_mirrors/chart/charts点击查看免费下载本指南以 Kubernetes 官方 charts 仓库已归档中stable/consulHelm Chart 为核心系统讲解如何在 Kubernetes 集群中通过 StatefulSet 部署一个动态可扩展、可自动组网的 Consul Server 集群并覆盖安装、配置参数、Gossip 加密、ACL、Web UI、健康检查、故障转移、水平伸缩与清理等完整运维闭环。读完本文你将能够独立使用该 Chart 部署一个具备 quorum 保障与自愈能力的 Consul 集群并理解其底层模板实现原理。一、Chart 定位与适用前提1.1 已弃用状态说明该 Chart 位于本仓库的 stable/consul 目录在 Chart.yaml 中已标记deprecated: true官方弃用原因是在其维护后期Consul 在 Kubernetes 上的部署方案已逐步移交至 HashiCorp 官方 Helm Chart。Chart 版本为3.9.6对应appVersion: 1.5.3。尽管已弃用该 Chart 的模板设计与运维思路仍具有很高的参考价值它以 StatefulSet 承载有状态服务、用 Headless Service 暴露稳定网络标识、用启动脚本实现自动组网是学习 Kubernetes 有状态应用编排的经典范本。1.2 部署前提Prerequisites根据 stable/consul/README.md部署需要满足两个前提前提说明Kubernetes 1.10依赖 StatefulSet 的稳定 APIapps/v1与相关特性底层基础设施支持 PVConsul 数据需要持久化集群必须有可用的 PersistentVolume 存储从模板实现看consul-statefulset.yaml 中的statefulset.apiVersion会根据集群版本自动选择Kubernetes 1.9 时使用apps/v1beta2≥ 1.9 时使用apps/v1见 _helpers.tpl这也解释了为何要求 1.10 以获得完整的apps/v1能力。1.3 Chart 提供的核心能力按 README 描述该 Chart 的核心价值是Implemented a dynamically scalable consul cluster using Kubernetes StatefulSet即利用 Kubernetes StatefulSet 实现一个可动态伸缩的 Consul 集群。具体包括每个 Pod 拥有稳定的网络标识与有序的启动/销毁顺序天然契合 Consul 的 Raft 共识协议对节点命名的要求通过volumeClaimTemplates为每个成员分配独立持久卷保证重启后数据不丢失通过 Headless Service 提供集群内稳定的 DNS 解析配合-retry-join自动组网支持kubectl patch直接扩缩容新成员会自动加入集群。二、核心架构StatefulSet Headless Service 独立端口2.1 资源组成一览Chart 渲染后会创建以下 Kubernetes 资源对应 templates 目录模板文件生成的资源作用consul-statefulset.yamlStatefulSet承载 Consul Server 容器含启动脚本、探针、数据卷consul-service.yamlHeadless ServiceclusterIP: None为各 Pod 提供稳定的 DNS 名称gossip-secret.yamlSecretOpaque保存 Gossip 加密密钥ui-service.yamlService暴露 Consul Web UIconsul-ingress.yamlIngress通过域名访问 Web UIbasic-acls.yamlJobpost-install hook安装后自动配置基础 ACLpod-dist-budget.yamlPodDisruptionBudget防止运维操作一次下线过多 Consul 节点consul-test.yaml等Test Pod 及 RBAC供helm test验证集群健康2.2 Headless Service集群网络的基石consul-service.yaml 创建的 Service 使用clusterIP: None的 Headless 模式并带有注解annotations: service.alpha.kubernetes.io/tolerate-unready-endpoints: true该注解允许未就绪的 Pod 也保留 DNS 记录——这正是 StatefulSet 应用组网的关键即使consul-1、consul-2尚未启动完成consul-0也能通过 DNS 解析到它们的地址进行-retry-join从而在集群冷启动时自动完成选举与组网。Service 暴露了 Consul 的全部 7 个协议端口TCP/UDP 双栈与 values.yaml 中的端口配置一一对应端口名称端口号协议Consul 用途http8500TCPHTTP API / Web UIrpc8400TCPRPC 协议旧版 CLIserflan-tcp / serflan-udp8301TCP/UDPSerf LAN集群内部成员通信serfwan-tcp / serfwan-udp8302TCP/UDPSerf WAN跨数据中心通信server8300TCPServer RPCRaft 共识consuldns-tcp / consuldns-udp8600TCP/UDPDNS 服务2.3 数据持久化volumeClaimTemplatesStatefulSet 通过volumeClaimTemplates见 consul-statefulset.yaml为每个副本声明独立的 PVCvolumeClaimTemplates: - metadata: name: datadir spec: accessModes: [ReadWriteOnce] resources: requests: storage: {{ .Values.Storage }}挂载点为/var/lib/consulConsul 默认数据目录。模板注释还提示了上游推荐的存储上限为 700M。StorageClass有三种取值语义不设置默认不写storageClassName使用集群默认 provisionerAWS 上为 gp2、GKE/AWS/OpenStack 上为 standard设置为-写storageClassName: 禁用动态供给指定具体类名使用该 StorageClass。2.4 调度策略反亲和与 PDB默认配置在 values.yaml 中定义了一条podAntiAffinitypreferred按kubernetes.io/hostname拓扑键尽量将 Consul 副本分散到不同节点降低单点故障风险。同时 pod-dist-budget.yaml 会创建一个 PodDisruptionBudget默认maxUnavailable: 1防止节点维护等自愿中断事件一次性停掉过多 Server 导致集群失去 quorum。调参建议README 明确给出公式——maxUnavailable应设置为ceil((n/2) - 1)其中n Replicas。例如默认 3 副本时设为 15 或 6 副本时设为 2。三、安装 Chart3.1 使用默认配置安装$ helm install --name my-release stable/consul执行后将以my-release为 release 名创建 StatefulSetPod 命名为my-release-consul-0/1/2。3.2 覆盖参数安装参数可通过--set keyvalue[,keyvalue]传入$ helm install --name my-release stable/consul \ --set Replicas5 \ --set ui.enabledtrue也可以使用独立的 YAML 文件$ helm install --name my-release -f values.yaml stable/consul重要提示README 原话ConsulConfig无法用--set设置目前--set不支持列表内的哈希对象必须通过 YAML 文件传入。四、配置参数全解核心参考表下表完整列出 stable/consul/README.md 中的全部可配置参数及默认值。其中ImageTag在 README 中记录为1.5.2而当前仓库的 values.yaml 实际默认值为1.5.3与 Chart.yaml 的appVersion一致实际生效值以 values.yaml 为准参数说明默认值NameConsul statefulset 名称consulImage容器镜像名consulImageTag容器镜像标签1.5.3README 记载 1.5.2ImagePullPolicy镜像拉取策略AlwaysReplicasStatefulSet 副本数3Componentk8s selector 键consulConsulConfig包含 Consul 配置的 secret / configMap 列表[]Cpu容器请求的 CPU100mDatacenterNameConsul 数据中心名dc1Consul 默认值DisableHostNodeId禁用 Node Id 生成改用随机falsejoinPeers-retry-join目标主机列表[]joinWan-retry-join-wan目标主机列表[]Gossip.Encrypt是否加密 Gossip 流量trueGossipKey所有成员使用的 Gossip 密钥nil自动生成Storage持久卷大小1GiStorageClass持久卷存储类nilHttpPortConsul HTTP 监听端口8500Resources容器资源 requests / limits{}priorityClassNamePod priorityClassNamenilRpcPortConsul RPC 监听端口8400SerflanPortSerf LAN TCP 监听端口8301SerflanUdpPortSerf LAN UDP 监听端口8301SerfwanPortSerf WAN TCP 监听端口8302SerfwanUdpPortSerf WAN UDP 监听端口8302ServerPortServer 监听端口8300ConsulDnsPortDNS 监听端口8600affinityConsul 亲和性设置见 values.yamlnodeSelectorPod 调度的节点标签{}tolerationsPod 调度容忍[]podAnnotationsPod 注解{}maxUnavailablePDB maxUnavailable1ui.enabled启用 Consul Web UItrueuiIngress.enabled为 Web UI 创建 IngressfalseuiIngress.annotationsIngress 注解{}uiIngress.labelsIngress 标签{}uiIngress.hostsIngress 关联主机[]uiIngress.pathIngress 路径/uiIngress.tlsIngress TLS 配置[]uiService.enabled创建独立的 Web UI ServicetrueuiService.typeWeb UI Service 类型NodePortuiService.annotationsUI Service 额外注解{}acl.enabled启用基础 ACL 配置falseacl.masterTokenACL 配置文件中提供的 Master tokenacl.agentTokenACL 配置文件中提供的 Agent tokentest.image测试容器镜像需 kubectl bashlachlanevenson/k8s-kubectltest.imageTag测试容器镜像标签v1.4.8-bashtest.rbac.create为测试容器创建 RBACfalsetest.rbac.serviceAccountName测试容器使用的已存在 ServiceAccountadditionalLabels添加到 Pod 与 StatefulSet 的标签{}lifecycleStatefulSet 的 lifecycle 配置YAMLnilforceIpv6强制监听 IPv6 地址false补充说明几个 README 未展开但 values.yaml 中存在的参数Domain默认consul作为-domain参数传给 Consul agent决定集群内 DNS 域名后缀Gossip.Create默认true是否自动创建 Gossip 密钥 SecretnodeAffinity提供与affinity并行的节点亲和性配置模板values.yaml 中注释有 GKE 抢占式节点的示例。五、源码剖析StatefulSet 启动脚本如何实现自动组网这是本 Chart 最有技术含量、也最值得深入的部分。在 consul-statefulset.yaml 中容器的command是一段完整的内嵌 Shell 启动脚本整体流程如下5.1 注入环境变量Kubernetes 在容器启动前注入以下环境变量env: - name: INITIAL_CLUSTER_SIZE value: {{ default 3 .Values.Replicas | quote }} # 初始集群规模 - name: STATEFULSET_NAME value: {{ template consul.fullname . }} # 如 my-release-consul - name: POD_IP # 当前 Pod IP valueFrom: { fieldRef: { fieldPath: status.podIP } } - name: STATEFULSET_NAMESPACE # Pod 所在命名空间 - name: DNSPORT # DNS 端口5.2 拼接完整 FQDN脚本首先基于 StatefulSet 名称与命名空间构造 Pod 的完整 DNS 名称FQDN_SUFFIX${STATEFULSET_NAME}.${STATEFULSET_NAMESPACE}.svc NODE_NAME$(hostname -s).${FQDN_SUFFIX}这保证每个成员都使用稳定标识如my-release-consul-0.my-release-consul.default.svc参与组网与 Raft 共识。5.3 自动构建 Join 列表核心组网逻辑JOIN_PEERS for i in $( seq 0 $((${INITIAL_CLUSTER_SIZE} - 1)) ); do JOIN_PEERS${JOIN_PEERS}${JOIN_PEERS: }${STATEFULSET_NAME}-${i}.${FQDN_SUFFIX} done JOIN_PEERS$( printf %s\n $JOIN_PEERS | sort | uniq )即默认按照 StatefulSet 的序号规则自动生成全部成员的 FQDN。如果显式设置了joinPeers则跳过自动构建直接使用用户提供的-retry-join目标列表——脚本注释明确说明这些主机名会在 Consul 启动前被验证“可解析且可 ping 通”。5.4 就绪等待多轮 ping 验证考虑到 DNS 在集群冷启动时可能不稳定脚本采用连续 5 次成功才算就绪的防御性策略SUCCESS_LOOPS5 while [ $SUCCESS_LOOPS -gt 0 ]; do ALL_READYtrue JOIN_LAN for THIS_PEER in $JOIN_PEERS; do if PEER_IP$(ping -c 1 $THIS_PEER | awk -F[()] /PING/{print $2}) [ $PEER_IP ! ]; then if [ ${PEER_IP} ! ${POD_IP} ]; then JOIN_LAN${JOIN_LAN}${JOIN_LAN: } -retry-join$THIS_PEER fi else ALL_READYfalse break fi done ... sleep 1s done要点只有能 ping 通且不是自己的成员才会被加入-retry-join参数循环最多持续 5 轮且每轮都要求全部成员解析成功最大限度规避 DNS 抖动WAN 侧joinWan走同样的流程但注释明确说明“不要求能 ping 通只要求能解析出 IP”。5.5 最终启动 Consul Agentexec /bin/consul agent \ -data-dir/var/lib/consul \ -server \ -bootstrap-expect$( echo $JOIN_PEERS | wc -w ) \ -disable-keyring-file \ -bind0.0.0.0 \ -advertise${POD_IP} \ ${JOIN_LAN} \ ${JOIN_WAN} \ -dns-port${DNSPORT} \ -http-port{{ .Values.HttpPort }}关键参数说明-server以 Server 模式运行参与 Raft 投票与选举-bootstrap-expect动态取 Join 列表成员数这是自动组网的精髓——脚本无需硬编码期望的 Server 数直接由解析成功的 Peer 数量决定因此扩容后新 Pod 也能正确计算期望值-advertise${POD_IP}以 Pod IP 作为广播地址避免容器网络环境下地址漂移-disable-keyring-file配合 Gossip 密钥以配置文件方式注入详见下文-bind0.0.0.0默认 IPv4 监听设置forceIpv6: true后变为-bind::与-client::。5.6 健康探针livenessProbe: exec: command: [consul, members, -http-addrhttp://127.0.0.1:{{ .Values.HttpPort }}] initialDelaySeconds: 300 timeoutSeconds: 5存活探针通过本机 HTTP API 执行consul members且initialDelaySeconds: 3005 分钟为集群选举留出充足时间。六、通过 ConsulConfig 注入复杂配置Secret / ConfigMap6.1 配置方式对于--set难以表达的复杂配置Chart 支持把 Consul 配置文件以ConfigMap 或 Secret形式挂载进 Pod。README 给出的示例 values.yamlConsulConfig: - type: configMap name: consul-defaults - type: secret name: consul-secrets要求这两个对象存在于与 release 相同的命名空间。从模板看每个条目会被同时挂载为卷和配置目录卷volumes中生成userconfig-{{ .name }}类型按configMap/secret分别解析为configMap.name或secret.secretName见 consul-statefulset.yaml挂载mountPath: /etc/consul/userconfig/{{ .name }}只读启动参数追加-config-dir /etc/consul/userconfig/{{ .name }}。6.2 加载顺序与覆盖规则README 原义其中包含的每个 JSON 文件都会被 Consul 解释为一份配置可同时放入多个文件各条目按ConsulConfig列表中声明的顺序加载后声明者覆盖先声明者单个条目内部若含多个文件文件间顺序由 Consul 的--config-dir机制决定但ConsulConfig列表顺序依然被尊重Chart 自动生成的配置最后加载因此会覆盖用户在此注入的配置。6.3 两个必须注意的运维约束变更内容需重启这些配置含 Secret以文件形式挂载进 Consul Pod修改后集群可能需要重启容器才会重新读取Kubernetes 不允许修改 StatefulSet 的卷定义如果要在ConsulConfig列表中新增条目必须删除并重建 StatefulSet例如kubectl delete statefulset name kubectl apply -f ...或先卸载再重装 Chart但已有条目的内容变更是允许的容器重载/重启后即可生效。七、Gossip 加密与密钥管理7.1 密钥生成默认Gossip.Encrypt: true且Gossip.Create: truegossip-secret.yaml 会创建一个 Opaque Secretrelease-consul-gossip-key密钥生成逻辑为data: {{ if .Values.GossipKey }} gossip-key: {{ .Values.GossipKey | b64enc }} # 用户预置密钥 {{ else }} gossip-key: {{ randAlphaNum 24 | b64enc }} # 随机生成 24 位字母数字 {{ end }}即提供GossipKey则使用用户值base64 编码存储否则安装时随机生成。7.2 加密注入流程在 StatefulSet 启动脚本中密钥会转为 Consul 认可的 JSON 配置再传入if [ -e /etc/consul/secrets/gossip-key ]; then echo {\encrypt\: \$(base64 /etc/consul/secrets/gossip-key)\} /etc/consul/encrypt.json GOSSIP_KEY-config-file /etc/consul/encrypt.json fi随后-config-file /etc/consul/encrypt.json随${GOSSIP_KEY}参数注入consul agent启动命令实现全集群一致的 Gossip 对称加密防止集群间通信被窃听。八、ACL 基础配置post-install Job 自动初始化8.1 启用方式acl: enabled: true masterToken: master-token agentToken: agent-token启用后basic-acls.yaml 会创建一个带helm.sh/hook: post-install注解的Jobconfigure-basic-acls在安装成功后自动调用 Consul HTTP API 完成两项初始化创建 Agent Token/v1/acl/createcurl -X PUT --header X-Consul-Token: masterToken --data \ { Name: Agent Token, Type: client, \ Rules: node \\ { policy \write\ } service \\ { policy \read\ } key \_rexec\ { policy \write\ }, \ ID: agentToken} \ http://release.namespace.svc.cluster.local:httpPort/v1/acl/create修改匿名 TokenID: anonymous为其授予node { policy read }的只读节点权限。Job 的helm.sh/hook-delete-policy: hook-succeeded保证成功后自动清理不会留下遗留资源。8.2 关键 PitfallREADME 明确警告当 ACL 启用且acl_default_policy为deny时必须保证 Agent 使用的 token 至少具备consul members权限即节点读取权限否则Kubernetes liveness probe 会持续失败容器将每 5 分钟被杀死一次。原因对应 consul-statefulset.yaml 的探针定义livenessProbe执行consul members -http-addr...该调用需要相应 ACL 权限。若 token 无权限探针失败 → kubelet 周期性重启容器集群将陷入不稳定状态。基础 ACL 的正确做法就是按上述方式设置acl.enabled: true并填好acl.masterToken/acl.agentToken。九、Web UI 与 Ingress 暴露9.1 启用 Web UI默认ui.enabled: trueStatefulSet 启动命令中会追加-ui参数开启内置 Web 界面同时默认创建独立 UI Serviceui-service.yamluiService: enabled: true type: NodePort # 可通过节点 IP NodePort 直接访问 annotations: {}9.2 通过 Ingress 暴露设置uiIngress.enabled: true后consul-ingress.yaml 会为 UI 创建 IngressuiIngress: enabled: true hosts: [consul.example.com] # 关联域名 path: / annotations: {} # 如 nginx ingress 的 rewrite 注解 labels: {} tls: - secretName: consul-tls # TLS 证书 Secret hosts: [consul.example.com]模板会将 Ingress 的 backend 指向consul.fullnameService 的HttpPort8500并完整透传 annotations、labels、hosts 与 tls 配置。十、运维实战健康检查、故障转移与水平伸缩10.1 集群健康检查遍历每个 Pod 执行consul members确认所有成员状态为alivefor i in {0..2}; do kubectl exec consul-$i --namespaceconsul -- sh -c consul members; done预期输出README 示例节点按实际部署的 IP 显示Node Address Status Type Build Protocol DC consul-0 10.244.2.6:8301 alive server 0.6.4 2 dc1 consul-1 10.244.3.8:8301 alive server 0.6.4 2 dc1 consul-2 10.244.1.7:8301 alive server 0.6.4 2 dc1 cluster is healthy当所有成员alive且存在可用的 Server leader 时即认为集群健康。10.2 故障转移验证README 给出了主动演练故障转移的方法在某个 Pod 内杀掉 Consul 进程观察其自动恢复并重新加入集群$ ps aux | grep consul $ kill CONSUL_PID随后查看该 Pod 日志kubectl logs consul-0 --namespaceconsul典型的恢复过程日志包括Waiting for consul-0.consul to come up WARNING: Expect Mode enabled, expecting 3 servers Starting Consul agent... Starting Consul agent RPC... Consul agent running! Node name: consul-0 Datacenter: dc1 Server: true (bootstrap: false) Client Addr: 0.0.0.0 (HTTP: 8500, HTTPS: -1, DNS: 8600, RPC: 8400) Cluster Addr: 10.244.2.6 (LAN: 8301, WAN: 8302) ... [INFO] agent: (LAN) joined: 3 Err: nil [INFO] agent: Join completed. Synced with 3 initial agents注意日志开头的Expect Mode enabled, expecting 3 servers——这正是 5.5 节所述-bootstrap-expect动态计算的结果。Pod 被 kubelet 重启后容器重新执行启动脚本先等待所有 Peer 可达再自动 re-join 既有集群。10.3 水平伸缩扩容 / 缩容扩容直接 patch StatefulSet 的副本数无需重装 Chartkubectl get pods -l component${RELEASE-NAME}-consul --namespaceconsul $ kubectl patch statefulset/consul -p {spec:{replicas: 5}} consul patched扩容后新 Podconsul-3、consul-4会自动通过启动脚本加入集群consul members中即可看到全部 5 个alive成员。缩容kubectl patch statefulset/consul -p {spec:{replicas: 3}} --namespaceconsul缩容后被移除的节点consul-3、consul-4在剩余成员的consul members中会短暂显示为failed状态属预期现象README 示例输出即为该状态无需干预。由于 StatefulSet 扩容不会破坏既有 quorum而缩容数量过多可能引发选举风险建议结合 2.4 节 PDB 的maxUnavailable公式谨慎操作。十一、清理孤儿 Persistent VolumesREADME 特别提醒删除 StatefulSet 并不会删除其关联的 PVC/PV。卸载 Chart 后需手动清理遗留的持久卷避免存储资源泄漏$ kubectl delete pvc -l component${RELEASE-NAME}-consul其中${RELEASE-NAME}需替换为实际 release 名例如my-release。该 label 选择器与模板中 StatefulSet/Service 使用的component: release-consul标签保持一致。十二、自动化验证helm testChart 内置了 helm 测试资源consul-test.yaml 等用于在安装后确认集群健康helm test RELEASE_NAME RUNNING: inky-marsupial-ui-test-nn6lv PASSED: inky-marsupial-ui-test-nn6lv测试实现的要点对应模板源码测试 Pod 以helm.sh/hook: test-success运行命名带随机后缀使用initContainers从dduportal/bats:0.4.0拷贝 Bats 测试框架主容器运行测试脚本测试确认至少存在 3 个 Consul Server 成员即前三个成员达成 quorum默认测试镜像为lachlanevenson/k8s-kubectl:v1.4.8-bash若集群启用了 RBAC可设置test.rbac.create: true自动创建 ServiceAccount模板见consul-test-serviceaccount.yaml等文件。十三、总结stable/consul虽然是已弃用的 Chart但它沉淀了一套完整、成熟且极具学习价值的有状态集群编排范式架构上StatefulSet 提供稳定标识与有序生命周期Headless Service tolerate-unready-endpoints解决冷启动组网难题volumeClaimTemplates保障数据持久化组网上内嵌 Shell 启动脚本动态生成-retry-join列表与-bootstrap-expect配合多轮 ping 等待机制实现真正的“开箱即用”自动组网安全上Gossip 加密密钥自动生成并注入ACL 通过 post-install Job 一键初始化运维上PDB 保护 quorum、helm test自动验证、kubectl patch一键伸缩、PVC 清理有明确指引。无论你是要在生产环境参考该方案部署 Consul还是想学习 Kubernetes StatefulSet 应用的设计模式本 Chart 的模板与 values.yaml 都是值得反复研读的第一手材料。若需要更现代的 Consul on Kubernetes 方案建议查阅官方文档说明并评估 HashiCorp 官方 Chart。赞分享【免费下载链接】charts⚠️(OBSOLETE) Curated applications for Kubernetes项目地址https://gitcode.com/gh_mirrors/chart/charts点击查看免费下载相关推荐在 Kubernetes 上部署高可用 etcd 集群incubator/etcd Helm Chart 实战指南在 Kubernetes 上部署高可用 etcd 集群incubator/etcd Helm Chart 实战指南 导读 本文以开源 Helm Charts基于 Patroni 与 Spilo 的 Kubernetes 高可用 PostgreSQL 集群 Helm Chart 部署指南incubator/patroni基于 Patroni 与 Spilo 的 Kubernetes 高可用 PostgreSQL 集群 Helm Chart 部署指南incubator/patrmicroTVM 教程依赖安装指南pyserial 与 tflite 的用途、版本与完整实战microTVM 教程依赖安装指南pyserial 与 tflite 的用途、版本与完整实战 导读 microTVM 是 TVM 面向嵌入式设备与微控制器的编上一篇es-toolkit 兼容版 map 全解析从 lodash 迁移到更现代、更快的数组映射方案下一篇[2.9.0](https://github.com/airyland/vux/compare/v2.8.1...v2.9.0) (2023-11-07)创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表