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

搭建 SonarQube 和 PostgreSQL 服务

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

1、部署 PostgreSQL 服务

1. 建立命名空间

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

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

2. 建立 PostgreSQL 使用的 PV 和 PVC

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

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

在个人机器环境中,/opt/ops_ceph_data 是挂载的 cephfs 文件系统,因此在任意节点上建立目录后,其余节点上都会存在。这也保证了 PostgreSQL 容器能够在任意节点上进行漂移。sql

同时因为我是将 cephfs 直接挂载到物理机上,因此在下面建立 pv 的时候,指定的存储类型是 local。数据库

若是但愿学习如何搭建 Ceph 集群,能够参考个人另外一篇博文:CentOS 7 搭建 Ceph 集群(nautilus 版本)api

建立 PV 和 PVC 的 yaml 文件内容以下:bash

---
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,命令以下:session

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

注意,PV 中配置的 matchExpressions 必定要与 labels 一致,否则会没法匹配。app

4. 建立 Service

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

---
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 文件。测试是否能够正常链接便可。

2、部署 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 就是初始化容器,也就是在主容器启动以前,首先启动初始化容器。若是有多个初始化容器,会按照定义的顺序依次启动。只有在初始化容器启动完成后,主容器才会启动。

使用初始化容器有以下几个做用:

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

须要注意的是,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,那么在宿主机的挂载目录下显示的就是以下形式:

[@k8s-master1 ~]# ll /opt/ops_ceph_data/sonarqube/sonar_data/
总用量 0
drwxrwxrwx 1 root root 0 10月 29 11:41 conf
drwxrwxrwx 1 root root 2 10月 29 15:57 data
drwxrwxrwx 1 root root 2 10月 29 16:01 extensions

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

[@k8s-master1 ~]# ll /opt/ops_ceph_data/sonarqube/sonar_data/
总用量 0
drwxrwxrwx 1 root root 0 10月 29 11:41 sonar_conf
drwxrwxrwx 1 root root 2 10月 29 15:57 sonar_data
drwxrwxrwx 1 root root 2 10月 29 16:01 sonar_extensions

4. 访问 SonarQube 并安装插件

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

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