在 k8S 中搭建 SonarQube 7.4.9 版本(使用 PostgreSQL 資料庫)

徐似安然Aaron發表於2020-10-29

搭建 SonarQube 和 PostgreSQL 服務

本文搭建的 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。

如果希望學習如何搭建 Ceph 叢集,可以參考我的另一篇博文:CentOS 7 搭建 Ceph 叢集(nautilus 版本)

建立 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 就是初始化容器,也就是在主容器啟動之前,首先啟動初始化容器。如果有多個初始化容器,會按照定義的順序依次啟動。只有在初始化容器啟動完成後,主容器才會啟動。

使用初始化容器有如下幾個作用:

  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 外掛用於漢化介面,安裝完成後需要重啟服務(在頁面上方會有提示)。

相關文章