在 k8S 中搭建 SonarQube 7.4.9 版本(使用 PostgreSQL 数据库)

news2025/7/31 23:30:34

本文搭建的 SonarQube 版本是 7.4.9-community,由于在官方文档中声明 7.9 版本之后就不再支持使用 MySQL 数据库。所以此次搭建使用的数据库是 PostgreSQL 11.4 版本。

一、部署 PostgreSQL 服务

1. 创建命名空间

将 PostgreSQL 和 SonarQube 放在同一个命名空间 ns-sonar 中,创建命名空间的 yaml 文件如下:

---
apiVersion: v1
kind: Namespace
metadata:
  name: ns-sonar
  labels:
    name: ns-sonar

2. 创建 PostgreSQL 使用的 PV 和 PVC

为了实现 PostgreSQL 数据的持久化存储,需要将数据存放在本地存储中。首先在宿主机的 /opt/ops_ceph_data 目录下创建如下目录:

mkdir -p /opt/ops_ceph_data/sonarqube/{PostgreSQL_data,sonar}

在我的机器环境中,/opt/ops_ceph_data 是挂载的 cephfs 文件系统,所以在任意节点上创建目录后,其他节点上都会存在。这也保证了 PostgreSQL 容器可以在任意节点上进行漂移。

同时由于我是将 cephfs 直接挂载到物理机上,所以在下面创建 pv 的时候,指定的存储类型是 local。

创建 PV 和 PVC 的 yaml 文件内容如下:

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgresql-pv
  namespace: ns-sonar
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 5Gi
  local:
    path: /opt/ops_ceph_data/sonarqube/PostgreSQL_data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: sonar-node
          operator: In
          values:
          - "true"
  persistentVolumeReclaimPolicy: Retain
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: postgresql-pvc
  namespace: ns-sonar
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

3. 配置 labels

由于上面配置的 PV 存储类型是 local,所以需要在允许运行 PostgreSQL 容器的 Node 上设置 labels,labels 为 sonar-node=true,这里我是将所有的 Node 节点上都添加了这个 label,命令如下:

for i in 1 2 3 4 5
do
	kubectl label nodes k8s-node${i} sonar-node=true
done

注意,PV 中配置的 matchExpressions 一定要与 labels 一致,不然会无法匹配。 

4. 创建 Service

接下来需要配置用于映射 PostgreSQL 容器端口的 Service 文件,这里我使用 NodePort 类型,yaml 文件内容如下:

---
apiVersion: v1
kind: Service
metadata:
  name: postgresql-service
  namespace: ns-sonar
  labels:
    app: postgresql
spec:
  type: NodePort
  ports:
    - port: 5432
      targetPort: 5432
      nodePort: 30543
      protocol: TCP
  selector:
    app: postgresql

5. 创建 PostgreSQL 的 Pod

因为我搭建的环境中,PostgreSQL 使用的单点模式,所以直接使用 Deployment 类型来创建 Pod,yaml 文件内容如下:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
  namespace: ns-sonar
  labels:
    app: postgresql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
      - name: postgresql-for-sonar
        image: postgres:11.4
        imagePullPolicy: "IfNotPresent"
        ports:
        - containerPort: 5432
        env:                                        # 这里设置 PostgreSQL 启动时候所需要的环境变量
          - name: POSTGRES_DB                       # 定义要创建的数据库名称
            value: sonarDB
          - name: POSTGRES_USER                     # 定义要创建访问数据库的用户
            value: sonarUser
          - name: POSTGRES_PASSWORD                 # 定义数据库的密码
            value: sonar_admin
        resources:
          limits:
            cpu: 1000m
            memory: 2048Mi
          requests:
            cpu: 500m
            memory: 1024Mi
        volumeMounts:
          - mountPath: /var/lib/postgresql/data     # 这个目录是 PostgreSQL 容器内默认的数据存储路径
            name: postgredb
      volumes:
        - name: postgredb
          persistentVolumeClaim:
            claimName: postgresql-pvc               # 将上面创建的 PVC 挂载到 PostgreSQL 的数据目录下

在环境变量设置的部分,我一开始使用的是引用 Secret 的方式,但是在容器启动后没有正确创建用户和密码。所以还是使用了直接指定 value 的方式。具体为什么 Secret 没有生效现在还不清楚,后续查出原因后再补充。

6. 验证数据库连接

使用容器搭建 PostgreSQL 服务,默认会在容器内监听 0.0.0.0 地址,所以像传统方式部署那样去手动修改监听地址。

在其他机器中验证连接 PostgreSQL,IP 地址为任意 Node 节点 IP。用户名密码和数据库名称参考上面的 yaml 文件。测试是否可以正常连接即可。

二、部署 SonarQube 服务

1. 创建 SonarQube 使用的 PV 和 PVC

用于 SonarQube 的持久化存储目录已经在前面创建好了,下面直接编写 yaml 文件,内容如下:

---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: sonar-pv
  namespace: ns-sonar
spec:
  accessModes:
  - ReadWriteOnce
  capacity:
    storage: 20Gi
  local:
    path: /opt/ops_ceph_data/sonarqube/sonar_data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: sonar-node
          operator: In
          values:
          - "true"
  persistentVolumeReclaimPolicy: Retain
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: sonar-pvc
  namespace: ns-sonar
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

需要注意的是,PV 中匹配的 labels 已经在前面创建好了,所以此处不需要重复设置 labels。

另外 SonarQube 容器运行的时候,不是以 root 用户运行的,所以需要确保挂载的目录要允许其他用户读写,否则容器启动会失败。

chmod -R 777 /opt/ops_ceph_data/sonarqube/sonar_data

2. 创建 Service

使用 NodePort 类型将 SonarQube 端口映射出来,yaml 文件内容如下:

---
apiVersion: v1
kind: Service
metadata:
  name: sonarqube-service
  labels:
    app: sonarqube-service
spec:
  type: NodePort
  ports:
    - port: 9000
      targetPort: 9000
      nodePort: 30900
      protocol: TCP
  selector:
    app: sonarqube

3. 创建 SonarQube 的 Pod

SonarQube 的 Pod 使用 Deployment 来创建,yaml 文件内容如下:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sonarqube
  namespace: ns-sonar
  labels:
    app: sonarqube
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      initContainers:                                           # 设置初始化镜像,用于执行 system 命令,此处的配置在下文会有说明
      - name: init-sysctl
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sysctl", "-w", "vm.max_map_count=262144"]    # 设置vm.max_map_count这个值调整内存权限,否则启动可能报错
        securityContext:
          privileged: true                                      # 设置可以以 root 权限执行命令
      containers:
      - name: sonarqube
        image: sonarqube:7.9.4-community
        ports:
        - containerPort: 9000
        env:
        - name: SONARQUBE_JDBC_USERNAME                         # 设置 SonarQube 连接数据库使用的用户名
          value: sonarUser
        - name: SONARQUBE_JDBC_PASSWORD                         # 设置 SonarQube 连接数据库使用的密码
          value: sonar_admin
        - name: SONARQUBE_JDBC_URL                              # 设置 SonarQube 连接数据库使用的地址
          value: "jdbc:postgresql://10.16.12.206:30543/sonarDB" # 这里可以指定 Node 节点的 IP 地址和 PostgreSQL 映射出来的端口
        livenessProbe:                     # 设置容器存活检查策略,如果失败将杀死容器,然后根据 Pod 的 restartPolicy 来决定是否进行重启操作
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60          # 设置在容器启动多长时间后开始探针检测,此处设置为 60s
          periodSeconds: 30                # 设置探针检查的频率,此处设置为每 30s 检查一次
        readinessProbe:                    # 设置容器的就绪检查策略,查看容器是否准备好接受 HTTP 请求
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60          # 设置在容器启动多长时间后开始探针检测,此处设置为 60s
          periodSeconds: 30                # 设置探针检查的频率,此处设置为每 30s 检查一次
          failureThreshold: 6              # 在检查失败的情况下,重复检查的次数,此处设置为 6
        resources:
          limits:
            cpu: 2000m
            memory: 2048Mi
          requests:
            cpu: 1000m
            memory: 1024Mi
        volumeMounts:
        - mountPath: /opt/sonarqube/conf
          name: sonarqube
          subPath: conf                    # 使用 subPath 在宿主机的挂载目录上设置一个子目录,用于存放上面指定目录的数据
        - mountPath: /opt/sonarqube/data
          name: sonarqube
          subPath: data
        - mountPath: /opt/sonarqube/extensions
          name: sonarqube
          subPath: extensions
      volumes:
      - name: sonarqube
        persistentVolumeClaim:
          claimName: sonar-pvc    #绑定上面创建的 PVC

对于上面的 yaml 文件有些配置需要进行说明。

3.1 initContainers
initContainers 就是初始化容器,也就是在主容器启动之前,首先启动初始化容器。如果有多个初始化容器,会按照定义的顺序依次启动。只有在初始化容器启动完成后,主容器才会启动。

使用初始化容器有如下几个作用:

为主容器初始化环境:例如本文中的例子,由于 SonarQube 在启动服务的时候,要确保已经设置了 vm.max_map_count 这个值,但是由于 SonarQube 镜像本身不能执行这个命令,所以可以使用一个初始化容器来执行该命令(同一个Pod下的容器是共享文件系统的),并且保证该命令已经执行完成的情况下,主容器才会启动。或者另一种情况是主容器启动的时候需要安装一些依赖包,为了避免安装依赖包时间过长,影响健康检查策略,可以选择将这个安装的任务交给初始化容器去执行。
等待其他服务 Ready:例如一个 web 服务的 Pod 启动时,需要确保另一个数据库服务的 Pod 已经启动了并且可以接受连接(不然 web 服务可能会报错或者启动失败),所以可以在 web 服务的 Pod 中部署一个初始化容器,去检查数据库服务是否已经准备好,直到数据库可以开始连接,初始化容器才会推出。
初始化集群配置:例如可以使用初始化容器检测当前业务集群中已经存在的节点信息,并为主容器准备好集群的配置信息,这样集群启动时就可以根据这个配置信息加入到集群中。
需要注意的是,initContainers 是以 sideCar 模式运行在 Pod 中的。

3.2 健康检查策略
关于健康检查策略,上面的 yaml 文件中已经给出了一些注释。其他的配置项可以参考官网文档:配置存活探针和就绪探针

3.3 subPath 配置
上面的 yaml 文件中在存储挂载的部分使用了 subPath 配置,这是因为 SonarQube 中一共有三个需要挂载的目录:

/opt/sonarqube/conf
/opt/sonarqube/data
/opt/sonarqube/extensions

而宿主机上的存储目录只提供了一个 /opt/ops_ceph_data/sonarqube/sonar_data,默认情况下,以上三个目录的数据都会存储在宿主机这一个目录下,这样就会造成数据混乱,没有办法区分某个数据文件或目录具体是哪个父目录下的。可以使用 subPath 配置解决这个问题,这个配置的功能就是在宿主机的挂载目录下创建一个子目录来存放对应目录的数据。

例如上面的 subPath 配置项分别创建了三个子目录:conf、data、extensions,那么在宿主机的挂载目录下显示的就是如下形式

 这三个子目录的名称可以随意指定,上面的 yaml 文件中 subPath 指定的子目录名称与容器中的目录名称一致是为了更方便的区分。如果将 subPath 的配置分别改为:sonar_conf、sonar_data、sonar_extensions,那么在宿主机挂载目录下显示的就会是如下形式:

4. 访问 SonarQube 并安装插件

SonarQube 部署完成后,可以通过任意 Node 节点的 IP 地址加上映射的端口访问。

默认的登录用户名和密码均为 admin。登录完成后,首先点击 Administration --> Marketplace ,在 Plugin 部分查找 chinese 插件和 Codehawk Java 进行安装。chinese 插件用于汉化界面,安装完成后需要重启服务(在页面上方会有提示)。

#mkdir -p /data/sonarqube/{PostgreSQL_data,sonar_data}
#chmod -R 777 /data/sonarqube/sonar_data

---
---
apiVersion: v1
kind: Service
metadata:
  name: postgres-sonar
  labels:
    app: postgres-sonar
spec:
  clusterIP: None
  ports:
  - port: 5432
    protocol: TCP
    targetPort: 5432
  selector:
    app: postgres-sonar

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-sonar
  labels:
    app: postgres-sonar
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres-sonar
  template:
    metadata:
      labels:
        app: postgres-sonar
    spec:
      containers:
      - name: postgres-sonar
        image: postgres:11.4
        imagePullPolicy: "IfNotPresent"
        ports:
        - containerPort: 5432
        env:                                        # 这里设置 PostgreSQL 启动时候所需要的环境变量
          - name: POSTGRES_DB                       # 定义要创建的数据库名称
            value: sonarDB
          - name: POSTGRES_USER                     # 定义要创建访问数据库的用户
            value: sonarUser
          - name: POSTGRES_PASSWORD                 # 定义数据库的密码
            value: sonar_admin
        resources:
          limits:
            cpu: 1000m
            memory: 2048Mi
          requests:
            cpu: 500m
            memory: 1024Mi
        volumeMounts:
          - mountPath: /var/lib/postgresql/data     # 这个目录是 PostgreSQL 容器内默认的数据存储路径
            name: postgredb
      volumes:
      - name: postgredb
        hostPath:
          type: DirectoryOrCreate 
          path: /data/sonarqube/PostgreSQL_data
      nodeName: gem-yxyw-t-c03
---
---
apiVersion: v1
kind: Service
metadata:
  name: sonarqube-service
  labels:
    app: sonarqube-service
spec:
  type: NodePort
  ports:
    - port: 9000
      targetPort: 9000
      nodePort: 30900
      protocol: TCP
  selector:
    app: sonarqube
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sonarqube
  labels:
    app: sonarqube
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sonarqube
  template:
    metadata:
      labels:
        app: sonarqube
    spec:
      initContainers:                                           # 设置初始化镜像,用于执行 system 命令,此处的配置在下文会有说明
      - name: init-sysctl
        image: busybox
        imagePullPolicy: IfNotPresent
        command: ["sysctl", "-w", "vm.max_map_count=262144"]    # 设置vm.max_map_count这个值调整内存权限,否则启动可能报错
        securityContext:
          privileged: true                                      # 设置可以以 root 权限执行命令
      containers:
      - name: sonarqube
        image: sonarqube:7.9.4-community
        ports:
        - containerPort: 9000
        env:
        - name: SONARQUBE_JDBC_USERNAME                         # 设置 SonarQube 连接数据库使用的用户名
          value: sonarUser
        - name: SONARQUBE_JDBC_PASSWORD                         # 设置 SonarQube 连接数据库使用的密码
          value: sonar_admin
        - name: SONARQUBE_JDBC_URL                              # 设置 SonarQube 连接数据库使用的地址
          value: "jdbc:postgresql://postgres-sonar:5432/sonarDB" # 这里可以指定 Node 节点的 IP 地址和 PostgreSQL 映射出来的端口
        livenessProbe:                     # 设置容器存活检查策略,如果失败将杀死容器,然后根据 Pod 的 restartPolicy 来决定是否进行重启操作
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60          # 设置在容器启动多长时间后开始探针检测,此处设置为 60s
          periodSeconds: 30                # 设置探针检查的频率,此处设置为每 30s 检查一次
        readinessProbe:                    # 设置容器的就绪检查策略,查看容器是否准备好接受 HTTP 请求
          httpGet:
            path: /sessions/new
            port: 9000
          initialDelaySeconds: 60          # 设置在容器启动多长时间后开始探针检测,此处设置为 60s
          periodSeconds: 30                # 设置探针检查的频率,此处设置为每 30s 检查一次
          failureThreshold: 6              # 在检查失败的情况下,重复检查的次数,此处设置为 6
        resources:
          limits:
            cpu: 2000m
            memory: 2048Mi
          requests:
            cpu: 1000m
            memory: 1024Mi
        volumeMounts:
        - mountPath: /opt/sonarqube/conf
          name: sonarqube
          subPath: conf                    # 使用 subPath 在宿主机的挂载目录上设置一个子目录,用于存放上面指定目录的数据
        - mountPath: /opt/sonarqube/data
          name: sonarqube
          subPath: data
        - mountPath: /opt/sonarqube/extensions
          name: sonarqube
          subPath: extensions
      volumes:
      - name: sonarqube
        hostPath:
          type: DirectoryOrCreate 
          path: /data/sonarqube/sonar_data
      nodeName: gem-yxyw-t-c03

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/394473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker(二)

5.容器数据卷 1.什么是容器数据卷 docker理念回顾 将应用和环境打包成一个镜像! 数据?如果数据都在容器中,那么我们容器删除,数据就会丢失!需求:数据可以持久化 MySQL,容器删了&#xff0c…

都工作3年了,怎么能不懂双亲委派呢?(带你手把手断点源码)

💗推荐阅读文章💗 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》🌺MySQL系列🌺👉2️⃣《MySQL系列教程》🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》…

c盘怎么清理到最干净?有什么好的清理方法

c盘怎么清理到最干净?有什么好的清理方法?清理C盘空间是电脑维护的重要步骤之一。C盘是Windows操作系统的核心部分,保存了许多重要的系统文件,因此空间不足会影响计算机的性能和稳定性。下面是一些清理C盘空间的方法 一.清理临时文件 在使用…

【Java学习笔记】10.条件语句 - if...else及switch case 语句

前言 本章介绍Java的条件语句 - if…else和switch case 语句。 Java 条件语句 - if…else Java 中的条件语句允许程序根据条件的不同执行不同的代码块。 一个 if 语句包含一个布尔表达式和一条或多条语句。 语法 if 语句的语法如下: if(布尔表达式) {//如果布…

实验二:动态规划

1.双11的红包雨 问题描述 双11到了,据说这2天会下红包雨,每个红包有不同的价值,小k好开心,但有个规则,就只能接掉落在他身旁的10米范围内的红包(0-10这11个位置)。小k想尽可能的多抢红包&…

评价提高分子对接性能的组合策略

评价提高分子对接性能的组合策略 相关背景: 分子对接可能是应用于基于结构的药物设计和虚拟筛选活动中最快速、成本最低的计算技术,它可以从巨大的化合物文库中找到潜在的活性分子,用于先导发现。 在分子对接中,配体(通常是小分…

LIME: Low-light Image Enhancement viaIllumination Map Estimation

Abstract当人们在低光条件下拍摄图像时,图像通常会受到低能见度的影响。除了降低图像的视觉美感外,这种不良的质量还可能显著降低许多主要为高质量输入而设计的计算机视觉和多媒体算法的性能。在本文中,我们提出了一种简单而有效的微光图像增…

2023年最新的站内SEO指南:如何通过关键词优化提高网站排名

SEO或搜索引擎优化是指通过改善网站的内部和外部元素,以获得更好的自然搜索引擎排名和更多的网站流量。 链接建设和外链是SEO的重要组成部分,因为它们可以提高网站的权威性和可信度,从而使其在搜索引擎中排名更高。 在此指南中,…

MySQL三范式

1、查询语句写的烂2、索引失效(数据变更)3、关联查询太多join(设计缺陷或不得已的需求)4、服务器调优及各个参数设置(缓冲、线程数等) 通常SQL调优过程: 观察,至少跑1天&#xff0…

CF756div3 vp

又被薄纱了,rk就不放了,好丢人QwQDashboard - Codeforces Round 756 (Div. 3) - CodeforcesA. Make Even小分类讨论题意:给定一个数,每次操作可以选取其前缀然后翻转其前缀,问你最少操作几次可以把该数变为偶数思路&am…

基于深度学习的轴承寿命预测实践,开发CNN、融合LSTM/GRU/ATTENTION

关于轴承相关的项目之前做的大都是故障识别诊断类型的,少有涉及回归预测的,周末的时候宅家发现一个轴承寿命加速实验的数据集就想着拿来做一下寿命预测。首先看下数据集如下:直接百度即可搜到,这里就不再赘述了。Learning_set为训…

什么是蜕变测试?

文章目录1.传统测试2.蜕变测试2.1.蜕变测试的理解2.2.蜕变测试的步骤2.2.1.生成蜕变关系2.2.2.生成蜕变用例2.2.3.执行蜕变用例2.2.4.校验蜕变关系参考文献1.传统测试 在没有蜕变测试的时代,传统软件测试的原理是:给定输入,观察被测软件的输…

你把骑行当什么? 它就是你需要的

1.骑行是一种有活力的运动,尝试一下你一定会喜欢上它的!2.把骑行当作一种娱乐,让自己快乐地体验自然的美!3.骑行可以帮助你改变心态,让你的心情变得更加愉悦!4.让骑行成为你每天的计划,看看骑行…

项目实战典型案例27——单表的更新接口有9个之多

单表的更新接口有9个之多一:背景介绍环境准备引入pom依赖配置数据库连接mybatis配置文件Mybatis的配置类编写通用的更新语句可以覆盖的更新接口暂时无法覆盖的接口测试四:总结五:升华一:背景介绍 本篇博客是对项目开发中出现的单…

如何分析sql性能

1、前言 提到sql性能分析,可能都会想到explain,它在mysql里被称为执行计划,也就是说可以通过该命令看出mysql在通过优化器分析之后如何执行sql。mysql的内置优化器十分强大,它能帮我们把sql再次优化,以最低的成本去执…

记录自己开发一款小程序中所遇到的问题(uniapp+uview)(持续更新)

每次开发小程序中,都会遇到各种各样的问题。但是有的问题已经遇到过了,但是遇到的时候还是要各种的问度娘。 特此出这篇文章,方便自己也是方便大家。 仅供参考 1. u-collapse的样式在h5中正常,但是运行到微信小程序中样式就乱了…

滑动窗口求最大和最小

滑动窗口 要区分最小和最大滑窗,内层while循环的条件和更新结果的地方 核心: 关键的区别在于,最大滑窗是在迭代右移右边界的过程中更新结果,而最小滑窗是在迭代右移左边界的过程中更新结果。 最小滑窗 给定数组 nums&#xff0…

成都待慕电商:抖音极速版商品卡免佣扶持政策规则

新规,抖音极速版推出商品卡免佣扶持政策规则,本次抖音规则如何规定?具体往下看:一、政策简介1.1政策介绍为了更好地满足用户消费需求,丰富商家经营模式,降低商家经营成本,现平台针对商品卡场景推…

【数据结构】模拟实现 堆

堆数据结构是一种数组对象,它可以被看作一颗完全二叉树的结构(数组是完全二叉树),堆是一种静态结构。堆分为最大堆和最小堆。最大堆:每个父结点都大于孩子结点。最小堆:每个父结点都小于孩子结点。堆的优势…

前端脚手架搭建(part 1)

本篇主要介绍如何搭建前端脚手架,一步一步地实现通过搭建的脚手架下载对应的项目模板。通过脚手架的创建,可以快速搭建项目的基础配置和模板,在部门项目开发的规范中尤其总要。初始化项目:创建一个文件夹,命名随便&…