阿里云K8s全栈部署:Vue2+Nginx+SpringBoot+Nacos完整链路实践
简介面向需要在阿里云Kubernetes集群中落地前后端分离应用的开发者这份资源完整打包了Vue2前端Nginx反向代理Spring Boot 2.5后端Nacos 2.0.3注册中心的部署方案。压缩包共16个文件、34.69MB以8个YAML配置为核心覆盖Nacos有状态服务、Ingress入口、Service与Headless Service等编排定义同时包含两个Dockerfile、两个Shell部署脚本、一个MySQL初始化SQL、Nginx配置以及打好的JAR包和前端压缩包。已有1340人学习下载适合有K8s基础、正在对接微服务注册中心或排查部署细节的开发者。其中YAML编写逻辑、前后端镜像构建方式与一键部署流程均可直接参考尤其Nacos高可用部署中的StatefulSet、配置挂载和数据库初始化等关键细节具有很强的复用性。1. 一个阿里云K8s上的全栈部署为什么vue2nginxspringboot2.5nacos2.0.3值得一起跑把Vue2的SPA页面、Nginx的静态托管、Spring Boot 2.5的后端服务、Nacos 2.0.3的注册与配置中心全部搬到一个阿里云托管K8s集群里这套组合单独拆开看都不难但一旦串成链路问题就从业务代码挪到了容器网络和版本匹配上。之前我接手一个后台管理系统本地docker-compose跑得很顺畅上了K8s之后Nacos服务一直注册不上去、前端路由一刷新就404折腾两天才发现坑全出在端口暴露、bootstrap加载方式和Nginx回退规则上。这篇文章按“镜像构建 → Nacos底座 → 后端接入 → 前端编排 → 避坑”的顺序把整条链路讲透适合正在做前后端分离应用容器化改造、想把这套技术栈平移到阿里云K8s的团队或个人开发者。2. 镜像构建是第一关Vue2Nginx与Spring Boot 2.5分头构建统一推到ACR2.1 基础镜像选择先定构建环境再谈部署镜像构建最容易翻车的地方不是Dockerfile语法而是基础镜像版本和实际项目不匹配。Vue2项目大多还是webpack4构建用node:18及以上版本经常会在npm install或build阶段报OpenSSL相关的error:0308010C:digital envelope routines::unsupported这是因为webpack4依赖的OpenSSL算法在新版本Node里被默认移除了。所以我一般直接定node:16-alpine作为前端构建镜像Vue2配Node 16是兼容性最稳的组合别图新。运行镜像用nginx:1.22-alpine体积在20MB左右拉取快容器里跑静态托管足够了。Spring Boot 2.5项目大多数还跑在JDK8上构建镜像用maven:3.8-openjdk-8运行镜像用openjdk:8-jdk-alpine这套是老组合网上资料多遇到问题也好搜。如果团队对基础镜像漏洞扫描有要求也可以换成eclipse-temurin:8-jdk-alpine用法几乎一样。镜像体积直接影响ACR拉取速度alpine系列能帮你省不少时间。2.2 Vue2Nginx的Dockerfile多阶段构建与SPA路由回退前端镜像的标准做法是分阶段构建第一阶段用Node环境把Vue2源码编译成静态文件第二阶段只把静态文件丢进Nginx镜像。完整Dockerfile如下# 构建阶段 FROM node:16-alpine AS build-stage WORKDIR /app COPY package*.json ./ RUN npm install --registryhttps://registry.npmmirror.com COPY . . RUN npm run build -- --mode production # 运行阶段 FROM nginx:1.22-alpine COPY --frombuild-stage /app/dist /usr/share/nginx/html COPY nginx/default.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD [nginx, -g, daemon off;]逻辑说明npm install单独放在COPY . .之前是为了利用Docker层缓存只要package.json没变就不会重复安装依赖。构建产物默认在/app/dist如果你的Vue2项目通过vue.config.js改了outputDir记得同步调整。运行时镜像只保留dist目录和Nginx配置不包含Node环境镜像体积能从1GB降到30MB左右。CMD里的daemon off;是让Nginx以前台方式运行保证容器启动后进程不退出。构建时如果公司内网有私有npm源把registry参数换成内部地址能避免外网依赖拉取超时。nginx/default.conf里的SPA回退规则是这个Dockerfile的灵魂server { listen 80; server_name _; root /usr/share/nginx/html; index index.html; location /api/ { proxy_pass http://order-service:8080/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { try_files $uri $uri/ /index.html; } }说明Vue2如果用history路由模式浏览器刷新时请求的是真实路径比如/system/userNginx在静态目录里找不到这个文件就会返回404。try_files $uri $uri/ /index.html;这条规则把所有找不到的路径回退到index.html由前端路由自己接管。/api/反代到order-service:8080这里的order-service不是IP是K8s集群内的Service域名容器网络会自动解析到后端Pod。2.3 Spring Boot 2.5的Dockerfile构建期与运行期分离后端镜像同样用多阶段构建开发机上不需要装Maven和JDK环境只要装了Docker就能打出可部署镜像# 构建阶段 FROM maven:3.8-openjdk-8 AS build-stage WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline -B COPY src ./src RUN mvn clean package -DskipTests -B # 运行阶段 FROM openjdk:8-jdk-alpine WORKDIR /app COPY --frombuild-stage /app/target/*.jar app.jar ENV JAVA_OPTS-Xms512m -Xmx512m EXPOSE 8080 ENTRYPOINT [sh, -c, java $JAVA_OPTS -jar app.jar]逻辑说明mvn dependency:go-offline会把pom里声明的依赖提前拉到本地仓库并缓存到镜像层后续改代码重新构建时不会每次联网解析依赖本地反复构建能明显感觉到速度差异。-DskipTests跳过测试CI流程里建议改成-Dmaven.test.skiptrue连测试代码编译都跳过进一步加速。运行阶段只留JDK8和jar包启动参数用JAVA_OPTS环境变量注入方便在K8s的Deployment里按Pod规格动态调整堆大小镜像里写死内存值在容器化环境里不灵活。2.4 推送到ACR命名空间、地域endpoint和tag规范镜像构建完要推到阿里云容器镜像服务ACRK8s节点才能拉取。完整命令如下docker login registry.cn-hangzhou.aliyuncs.com -u 用户名 -p 密码 docker tag vue-web:1.0.0 registry.cn-hangzhou.aliyuncs.com/命名空间/vue-web:20240501-abc1234 docker tag order-service:1.0.0 registry.cn-hangzhou.aliyuncs.com/命名空间/order-service:20240501-abc1234 docker push registry.cn-hangzhou.aliyuncs.com/命名空间/vue-web:20240501-abc1234 docker push registry.cn-hangzhou.aliyuncs.com/命名空间/order-service:20240501-abc1234这里有个关键细节镜像tag里包含了完整的ACR地址docker tag时目标地址必须和push地址完全一致否则push会提示找不到仓库。登录账号不要用阿里云主账号的登录密码在ACR控制台里创建一个专用的固定密码或临时token权限收敛到指定命名空间避免泄露后影响整个账号下的其他镜像。tag建议用“日期git commit短SHA”的组合比如20240501-abc1234出问题能直接定位到代码版本比用latest强太多latest在回滚时根本不知道拉的是哪一天的产物。3. Nacos 2.0.3的底座用StatefulSet和MySQL把它在K8s上立住3.1 Nacos 2.0.3该用单机还是集群先看数据可靠性要求Nacos在K8s里的部署形态我一般分两种情况测试环境直接单节点standalone模式生产环境至少3个副本组成集群。但无论哪种形态配置数据必须存MySQL不要用Nacos自带的Derby内嵌数据库。Derby是单机存储Pod一重建配置和注册数据就全没了而且Nacos集群模式下每个节点各存一份Derby数据根本不一致后面出了问题连排查方向都没有。选择StatefulSet而不是Deployment部署Nacos核心原因是Nacos 2.x集群节点之间需要用稳定的网络标识互相通信。StatefulSet会为每个Pod生成nacos-0、nacos-1这种固定名称配合headless Service节点之间能通过域名直接访问而Deployment生成的Pod名称是随机后缀每次重启都变集群节点没法稳定互相发现。哪怕你现在只跑单副本也建议直接写StatefulSet后续扩集群不用改部署文件结构只改replicas就行。3.2 把MySQL先准备好Nacos必须的库表初始化Nacos 2.0.3的配置、服务、权重等元数据都存在MySQL里所以要先准备一个MySQL实例。阿里云上可以用RDS MySQL 5.7或8.0也可以自己在K8s里跑一个有状态MySQL我这里假设你已经有了可用的MySQL地址。初始化数据库的命令如下mysql -h mysql地址 -u 用户名 -p \ -e CREATE DATABASE nacos_config DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; docker run --rm nacos/nacos-server:v2.0.3 \ tar -czf - -C /root/nacos/conf nacos-mysql.sql | tar -xzf - -C . mysql -h mysql地址 -u 用户名 -p nacos_config nacos-mysql.sql第二条命令把Nacos官方镜像里的nacos-mysql.sql脚本提取到本地再导入刚建好的nacos_config库。如果你不确定SQL脚本在容器里的具体路径可以先docker run --rm -it nacos/nacos-server:v2.0.3 find / -name nacos-mysql.sql查找不同版本的镜像脚本位置有差异。这一步必须做否则Nacos启动后会在日志里反复报Table config_info doesnt exist控制台打开也是各种报错。数据库字符集用utf8mb4避免配置内容里存emoji或特殊符号时写入失败。3.3 StatefulSet部署存储用云盘还是NAS环境变量怎么传我常用的单副本Nacos StatefulSet配置如下生产环境把replicas改成3即可apiVersion: apps/v1 kind: StatefulSet metadata: name: nacos labels: app: nacos spec: serviceName: nacos-headless replicas: 1 selector: matchLabels: app: nacos template: metadata: labels: app: nacos spec: containers: - name: nacos image: nacos/nacos-server:v2.0.3 ports: - containerPort: 8848 - containerPort: 9848 env: - name: MODE value: standalone - name: SPRING_DATASOURCE_PLATFORM value: mysql - name: MYSQL_SERVICE_HOST value: mysql地址 - name: MYSQL_SERVICE_PORT value: 3306 - name: MYSQL_SERVICE_DB_NAME value: nacos_config - name: MYSQL_SERVICE_USER value: nacos - name: MYSQL_SERVICE_PASSWORD value: mysql密码 volumeMounts: - name: data mountPath: /root/nacos/data volumeClaimTemplates: - metadata: name: data spec: accessModes: [ReadWriteOnce] storageClassName: alicloud-disk-essd resources: requests: storage: 20Gi参数说明MODEstandalone告诉Nacos以单机模式启动集群模式改成cluster并额外配置Nacos节点地址列表。SPRING_DATASOURCE_PLATFORMmysql强制走MySQL数据源不配这个值Nacos会默认用Derby。volumeClaimTemplates会自动为每个副本创建一块独立的云盘alicloud-disk-essd是阿里云ACK里默认的ESSD云盘StorageClass名称如果集群里没有这个名字先执行kubectl get storageclass看看实际叫什么。这块存储主要放Nacos的日志和本地缓存配置数据本身在MySQL里所以云盘损坏不会导致配置丢失但日志丢了排障会很痛苦。3.4 Service与访问方式8848和9848必须同时暴露Nacos部署完要创建Service这里有一个Nacos 2.x特有的坑除了8848必须把9848端口也暴露出来否则客户端注册会失败。完整的Service配置如下apiVersion: v1 kind: Service metadata: name: nacos-headless spec: clusterIP: None selector: app: nacos ports: - name: http port: 8848 targetPort: 8848 --- apiVersion: v1 kind: Service metadata: name: nacos spec: selector: app: nacos ports: - name: http port: 8848 targetPort: 8848 - name: grpc port: 9848 targetPort: 9848headless ServiceclusterIP为None用于Nacos集群节点之间的互相发现普通Service给Spring Boot客户端调用。Nacos 2.0开始客户端和服务端之间的长连接走gRPC协议gRPC端口是8848加1000偏移得到的9848客户端拿到nacos:8848地址后会自动尝试连接9848这个端口不暴露客户端会一直报注册失败并无限重试日志看起来像网络不通实际是端口被Service挡掉了。集群内部访问直接用nacos:8848控制台调试可以临时用kubectl port-forward svc/nacos 8848:8848映射到本地浏览器。4. 应用编排后端接入Nacos前端Nginx与Ingress接入流量4.1 依赖版本Spring Boot 2.5.6 Spring Cloud 2020.0.5 Alibaba 2021.1Spring Boot接入Nacos最常用的方式是通过Spring Cloud Alibaba。但版本对应关系卡住过很多人Spring Cloud Alibaba对Spring Boot的版本匹配非常严格选错直接启动报错或者配置完全不生效。我在这套环境里用的组合是Spring Boot 2.5.6、Spring Cloud 2020.0.5、Spring Cloud Alibaba 2021.1能稳定跑通注册和配置拉取。pom关键配置如下parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.5.6/version relativePath/ /parent dependencyManagement dependencies dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version2020.0.5/version typepom/type scopeimport/scope /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2021.1/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement实际依赖只写artifactId版本由BOM统一管理dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-bootstrap/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency这里有个容易忽略的点Spring Cloud 2020.0.x开始默认关闭了bootstrap上下文即使你在src/main/resources下放了bootstrap.ymlSpring Boot也不会自动加载它导致Nacos配置完全不生效。所以必须额外引入spring-cloud-starter-bootstrap依赖把bootstrap机制重新打开。actuator依赖是给K8s探针用的后面的readinessProbe要调/actuator/health缺了这个依赖探针永远返回404。4.2 bootstrap.ymlnamespace、group、file-extension三个必写项我把Nacos相关配置放在bootstrap.yml里确保应用启动早期就能连上配置中心和注册中心spring: application: name: order-service cloud: nacos: server-addr: nacos:8848 username: nacos password: nacos discovery: namespace: 命名空间ID config: namespace: 命名空间ID group: DEFAULT_GROUP file-extension: yaml refresh-enabled: true逻辑说明server-addr填的是K8s集群内的Service名称nacos:8848Spring Boot容器和Nacos在同一个集群网络里直接通过DNS解析访问不需要暴露公网地址。namespace填的是Nacos控制台里创建命名空间时生成的ID一串UUID不要填命名空间的名称填名称会导致配置拉取不到但又不报错这个坑我踩过排查了一下午。spring.application.name加上file-extension: yaml组合起来就是Nacos配置中心里的Data IDorder-service.yaml你需要在Nacos控制台提前创建这个配置里面放数据库连接、Redis地址等公共配置本地application.yml只保留不依赖环境的配置。4.3 后端Deployment与Service镜像、端口、探针、环境变量后端的K8s编排文件如下apiVersion: apps/v1 kind: Deployment metadata: name: order-service labels: app: order-service spec: replicas: 1 selector: matchLabels: app: order-service template: metadata: labels: app: order-service spec: imagePullSecrets: - name: acr-secret containers: - name: order-service image: registry.cn-hangzhou.aliyuncs.com/命名空间/order-service:20240501-abc1234 ports: - containerPort: 8080 readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 10 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 60 periodSeconds: 30 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1 memory: 1Gi --- apiVersion: v1 kind: Service metadata: name: order-service spec: selector: app: order-service ports: - port: 8080 targetPort: 8080参数说明imagePullSecrets引用的是ACR私有仓库的拉取凭证没有这个配置Pod会一直停在ImagePullBackOff状态后面避坑章节会细说。readinessProbe用/actuator/health判断Pod是否就绪initialDelaySeconds给Spring Boot 30秒启动缓冲如果应用启动慢要加大这个值否则探针在应用还没监听端口时就打过来Pod会被反复重启。livenessProbe失败超过阈值K8s会杀掉容器重建阈值和period根据业务容忍度调整一般不要比readiness更激进。Service端口8080对应容器端口8080前端Nginx反代时访问的就是order-service:8080。4.4 前端Nginx与Ingress把Vue2流量接进集群前端Deployment和Service相对简单但有两个细节要处理好镜像里必须带SPA回退配置Service名称要和Nginx反代地址一致apiVersion: apps/v1 kind: Deployment metadata: name: vue-web labels: app: vue-web spec: replicas: 2 selector: matchLabels: app: vue-web template: metadata: labels: app: vue-web spec: imagePullSecrets: - name: acr-secret containers: - name: vue-web image: registry.cn-hangzhou.aliyuncs.com/命名空间/vue-web:20240501-abc1234 ports: - containerPort: 80 readinessProbe: httpGet: path: / port: 80 initialDelaySeconds: 10 periodSeconds: 10 --- apiVersion: v1 kind: Service metadata: name: vue-web spec: selector: app: vue-web ports: - port: 80 targetPort: 80前端容器镜像里已经包含nginx/default.conf其中proxy_pass http://order-service:8080/api/;这行的order-service就是上面创建的后端Service名称。K8s集群内置DNS会把order-service解析到后端Pod的ClusterIP所以Nginx配置里不要写死IPPod重建后IP会变写Service名才能保证稳定。最后是Ingress入口把域名流量导到前端ServiceapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: demo-ingress annotations: nginx.ingress.kubernetes.io/proxy-body-size: 20m spec: ingressClassName: nginx rules: - host: demo.example.com http: paths: - path: / pathType: Prefix backend: service: name: vue-web port: number: 80Ingress只暴露前端入口用户通过demo.example.com访问请求先到Ingress Controller再转给vue-webServiceNginx容器收到请求后静态资源自己处理/api/路径反代到order-service:8080。proxy-body-size注解是防上传文件大小限制的如果业务里有超过默认1MB的上传需求记得调大。5. 避坑实操从镜像到注册中心5个反复出现的现实问题5.1 Nacos 2.x必须放行9848端口实例注册失败且无限重试现象Spring Boot应用启动后日志里反复出现nacos registry, order-service register failedNacos控制台服务列表始终为空重启应用也没用本地直接连Nacos却正常。原因Nacos 2.0.3的客户端注册不再走单纯的HTTP协议服务端会主动建立gRPC长连接端口在8848的基础上偏移1000也就是9848。K8s的Service只暴露了8848端口gRPC连接建不起来注册请求就一直重试。解决在Nacos的Service定义里增加9848端口映射具体配置参考3.4节的YAML如果Nacos通过NodePort暴露到集群外云安全组也要同步放行9848不然本地调试还是连不上。5.2 bootstrap.yml完全没生效配置中心静默失效现象应用能起来Nacos控制台能看到服务注册但Nacos配置中心里的配置项在应用里全部读不到日志里也没有加载远端配置的迹象。原因Spring Cloud 2020.0.x默认关闭了bootstrap上下文bootstrap.yml根本没被加载。很多教程基于旧版Spring Cloud写新老版本混着用就会踩这个坑。解决在pom里增加spring-cloud-starter-bootstrap依赖并重新构建镜像Spring Boot启动日志里出现Located property source字样就说明配置拉取成功了。另一个替代方案是不用bootstrap.yml改用spring.config.import: nacos:order-service.yaml但这样每个环境都要改配置我建议统一走bootstrap方式。5.3 探针路径与actuator端口不匹配Pod反复重启现象kubectl get pods看到后端Pod一直CrashLoopBackOff或反复Restartingkubectl describe pod里显示readiness探针失败但进入容器看日志Spring Boot已经正常启动8080端口也监听着。原因探针配了/actuator/health但项目里没引入actuator依赖或者management.server.port单独设置了一个管理端口探针打到了业务端口8080上这个端口根本没有health接口。解决确认pom里有spring-boot-starter-actuator并在application.yml里显式配置management.endpoints.web.exposure.include: health,info把management端口和业务端口保持一致或者把探针的port改成实际的管理端口。这是K8s部署里最容易出现的“容器活着但探针认为不健康”的情况。5.4 ACR私有镜像拉取失败ImagePullBackOff和ErrImagePull现象前端或后端Pod状态一直是ImagePullBackOffkubectl describe pod显示Failed to pull image...repository does not exist或authentication required。原因镜像tag没有带完整的ACR地址或者Deployment所在的namespace没有配置imagePullSecret。私有仓库拉取必须认证光在本地docker login过只对本地Docker生效K8s节点不知道你的凭证。解决镜像地址必须写成registry.cn-hangzhou.aliyuncs.com/命名空间/仓库名:tag的完整格式然后创建Secret并在Deployment里引用kubectl create secret docker-registry acr-secret \ --docker-serverregistry.cn-hangzhou.aliyuncs.com \ --docker-username用户名 \ --docker-password密码 kubectl get secret acr-secret -o yaml如果每个namespace都要用就在每个namespace里都创建一份或者用镜像拉取凭证的自动同步机制但手动创建最简单直观。Pod起来后kubectl describe pod里能看到Successfully pulled image就说明凭证生效了。5.5 前端刷新404和Nginx反代502前端路由与后端连通性问题现象浏览器打开首页正常在页面内跳转也正常但按F5刷新后直接404或者页面能打开但所有接口请求返回502 Bad GatewayNginx日志里出现connect() failed (111: Connection refused)。原因404是Vue2 history路由引起的Nginx收到/system/user这类路径请求后在静态目录里找不到同名文件直接返回404缺了SPA回退规则。502是Nginx反代到后端时proxy_pass里的后端地址写成了IP或者写了order-service:8080但后端Service没有正确关联到Pod。解决前端Nginx配置的location /里加try_files $uri $uri/ /index.html;后端反代地址统一用K8s Service域名不要用写死的IP排查502时先进入前端容器手动curl后端Servicekubectl exec -it deploy/vue-web -- curl -s http://order-service:8080/actuator/health能返回健康检查结果说明网络通了再回头看Nginx配置的路径和后端Controller的实际路由是否匹配。6. 全链路验证与两个进阶动作Nacos配置热更新和K8s滚动发布整套部署完成后我习惯按下面顺序做一次全链路验证哪个环节断了能快速定位kubectl get pods kubectl get svc nacos order-service vue-web kubectl get ingress kubectl exec -it deploy/vue-web -- curl -s http://order-service:8080/actuator/health逐个确认Nacos的Pod处于Running且ReadySpring Boot的Pod没有持续重启前端Pod两个副本都就绪进入Nacos控制台服务列表里能看到order-service实例在线在浏览器里访问域名能打开页面登录接口能正常返回数据。这套验证顺序符合从底层到上层的排查习惯先看Pod状态再看Service和DNS解析最后验证真实业务链路。进阶操作里我最常用的是Nacos配置热更新在Nacos控制台直接修改order-service.yaml里的某个配置项后端代码里加了RefreshScope注解的Bean会自动刷新不需要重启Pod。这个能力在调整线程池参数、开关降级策略时非常有用前提是配置类的声明方式规范不要用static变量接配置值否则刷新生效不了。另一个高频操作是滚动发布kubectl set image deployment/vue-web vue-webregistry.cn-hangzhou.aliyuncs.com/命名空间/vue-web:20240502-def5678 --record kubectl rollout status deployment/vue-web kubectl rollout undo deployment/vue-web--record会把变更原因记到Deployment的annotation里出问题能查到谁改的。发布后如果发现接口异常一条rollout undo就能回滚到上一个版本比自己手动改镜像tag再apply快得多。这套方案我从测试环境一路推到生产踩得最多的还是那句老话本地能跑不代表K8s能跑端口、Service名称、探针路径这些容器化特有的细节才是真正决定部署成败的地方。希望这份从镜像到链路验证的完整过程能帮到你。本文还有配套的精品资源点击获取