SonarQube 实例部署

本文档介绍 SonarQube Operator 的订阅以及部署 SonarQube 实例的功能。

Namespace Security Policy 限制

由于以下原因,SonarQube 不支持在 SPA(Security Policy Admission)策略设置为 RestrictedBaseline 的命名空间中部署:

  1. Init Container 需要 Root 权限:SonarQube 使用 init container 初始化 PVC 目录权限,而 Restricted 策略不允许 root 权限。
  2. InitSysctl 需要特权权限:默认情况下,SonarQube 使用 initSysctl container 配置 Elasticsearch 所需的 sysctl 参数(例如 vm.max_map_countfs.file-max)。这需要 privileged 权限才能修改系统级内核参数。

建议:为 SonarQube 部署创建专用命名空间,并确保其安全策略设置为 privileged

前提条件

  • 本文档适用于平台提供的 SonarQube 9.9.5 及以上版本。它通过 Operator 等技术与平台解耦。

  • 请确保 SonarQube Operator 已部署(已订阅)到目标集群,即已准备好通过 SonarQube Operator 创建实例。

部署规划

SonarQube 支持多种资源配置,以适配不同客户场景。在不同场景下,所需的资源、配置等会有显著差异。因此,本节介绍在部署 SonarQube 实例之前,需要在部署规划中考虑哪些方面,以及各个决策点会产生什么影响,以便用户基于这些信息进行后续具体的实例部署。

基本信息

  1. 平台提供的 SonarQube Operator 基于社区官方 SonarQube Chart,并增强了 IPv6 支持和安全漏洞修复等企业能力。其功能上与社区版本完全兼容,在用户体验上则通过可选、可定制模板等方式提升了 SonarQube 部署的便利性。

部署前资源规划

部署前资源规划是指在部署之前做出决策,并在部署过程中生效。

有关环境资源的更多建议,请参考官方文档: https://docs.sonarsource.com/sonarqube-community-build/server-installation/server-host-requirements

不要使用网络存储作为存储后端

在生产环境中,不要将基于网络存储的 StorageClass、PVC 或其他远程挂载存储用于 SonarQube 持久化。SonarQube 内部使用 Elasticsearch,官方 SonarSource 主机要求说明中指出,不应为 SonarQube 的 data 目录使用 NFS、SMB/CIFS 或 NAS 等远程挂载存储,因为这类存储通常更慢、延迟波动更大,并且会成为单点故障。官方文档还将受影响路径描述为 <sonarqubeHome>/data,该路径用于存放 Elasticsearch 索引,需要较高的 I/O 性能。

在规划 SonarQube 存储时,应使用本地节点存储或具有良好读写性能的块存储,而不是基于网络存储的存储类。如果在部署模板中选择了 StorageClass,请与存储提供商确认其后端不是远程挂载文件存储。

相关参考:

实例部署

Quickstart Template 模板部署

该模板用于快速创建轻量级 SonarQube 实例,适用于开发和测试场景,不建议用于生产环境。

  • 计算资源:800m CPU,4 Gi 内存
  • 存储:使用节点本地存储,配置存储节点 IP 和路径
  • 网络访问:使用 NodePort 访问服务,与存储共享节点 IP,并指定端口

Production Template 模板部署

该模板用于快速创建生产级 SonarQube 实例,适用于生产场景,推荐用于生产环境。

  • 计算资源:8 CPU 核,16 Gi 内存
  • 存储:使用 PVC 存储,配置 StorageClass
  • 网络访问:使用 Domain 访问服务。

通过 YAML 部署

资源配置

SonarQube 只需配置整体资源即可,例如:

spec:
  helmValues:
    resources:
      limits:
        cpu: 800m
        memory: 4Gi
      requests:
        cpu: 400m
        memory: 2Gi

更多信息请参考 SonarQube Chart 中的资源说明

网络配置

网络配置分为两类:

  • 基于 ingress 的网络配置
  • 基于 NodePort 的网络配置

基于 ingress 的网络配置同时支持 https 和 http 协议。需要提前在集群中部署 ingress controller。

spec:
  helmValues:
    ingress:
      enabled: true
      hosts:
        - name: test-ingress-https.example.com
      tls:
        - secretName: test-tls-cert
          hosts:
            - test-ingress-http.example.com

基于 NodePort 的网络配置:

spec:
  helmValues:
    service:
      name: sonarqube
      type: NodePort
      nodePort: <nodeport.http>

存储配置

存储配置主要分为三类:

  • 基于 StorageClass 的存储配置
  • 基于 PVC 的存储配置
  • 基于 HostPath 的存储配置

基于 StorageClass 的存储配置:

Warning

不要为 SonarQube 持久化选择基于网络存储的 StorageClass。请使用低延迟、读写性能良好的存储后端。

spec:
  helmValues:
    persistence:
      enabled: true
      storageClass: <storage-class.rwm> ## StorageClass needs to be created in advance
      size: 10Gi

基于 PVC 的存储配置:

spec:
  helmValues:
    persistence:
      enabled: true
      existingClaim: sonarqube-pvc ## PVC needs to be created in advance

基于 HostPath 的存储配置:

spec:
  helmValues:
    nodeSelector:
      kubernetes.io/hostname: <node.random>  ## Select the deployment node
    
    persistence:
      enabled: false
      host:
        nodeName: <node.random> ## Name of the node
        path: /tmp/sonarqube-<template.{{randAlphaNum 10}}> ## Select the deployment node and specify the storage path

PostgreSQL 访问凭据配置

需要先在平台上创建一个 PostgreSQL 实例,并在 PostgreSQL 中创建一个数据库供使用。

SonarQube 25.1.0 支持的 PostgreSQL 版本为 13 到 17。

通过配置具有特定格式内容的 secret 资源来实现 PostgreSQL 访问。详细信息请参见 配置 PostgreSQL 和账户访问凭据

在 SonarQube YAML 中使用 secret 作为访问 PG 的凭据:

spec:
  helmValues:
    postgresql:
      enabled: false ## Disable the default PostgreSQL instance
    jdbcOverwrite:
      enable: true
      jdbcSecretName: postgres-password ## Credentials for connecting to PG
      jdbcUrl: jdbc:postgresql://<pg.host>:<pg.port>/<pg.database>?socketTimeout=1500 ## Address for connecting to PG
      jdbcUsername: postgres ## PG access user

管理员账户配置

初始化 SonarQube 实例时,需要配置管理员账户及其密码。这通过配置 secret 资源完成。详细信息请参见 配置 PostgreSQL 和账户访问凭据

通过 YAML 将其指定给 SonarQube:

spec:
  helmValues:
    account:
      adminPasswordSecretName: sonarqube-root-password

完整 YAML 示例

NodePort、PVC、PostgreSQL、管理员账户

apiVersion: operator.alaudadevops.io/v1alpha1
kind: Sonarqube
metadata:
  name: sonarqube-demo
spec:
  helmValues:
    prometheusExporter: # Disable the default prometheus monitoring, jar packages need to be added in advance when starting
      enabled: false
    resources: # Set resource limits
      limits:
       cpu: 800m
       memory: 4Gi
      requests:
       cpu: 400m
       memory: 2Gi
    postgresql: # Disable the default PostgreSQL instance
      enabled: false
    jdbcOverwrite: # Use a pre-created PostgreSQL instance
      enable: true
      jdbcSecretName: postgres-password
      jdbcUrl: jdbc:postgresql://<pg.host>:<pg.port>/<pg.database>?socketTimeout=1500
      jdbcUsername: <pg.username>
    service: # Use NodePort to expose the SonarQube instance
      name: sonarqube
      type: NodePort
      nodePort: <nodeport.http>
    account: # Set Root password
      adminPasswordSecretName: sonarqube-root-password
    persistence: # Use PVC for storage mounting
      enabled: true
      existingClaim: sonarqube-pvc

SSO 配置

配置 SSO 包括以下步骤:

在 global 集群中注册一个 SSO 身份验证客户端

  • 在 global 集群中创建以下 OAuth2Client 资源以注册 SSO 身份验证客户端。
  • 配置 SonarQube 实例使用 SSO 身份验证
apiVersion: dex.coreos.com/v1
kind: OAuth2Client
name: OIDC
metadata:
  name: onxw4ylsof2wezjnmrsxrs7sttsiiirdeu # This value is calculated based on the hash of the id field, online calculator: https://go.dev/play/p/QsoqUohsKok
  namespace: cpaas-system
id: sonarqube-dex # Client id
public: false
redirectURIs:
  - <sonarqube-host>/* # SonarQube authentication callback address, where <sonarqube-host> is replaced with the access address of the SonarQube instance
secret: Z2l0bGFiLW9mZmljaWFsLTAK # Client secret
spec: {}
字段说明示例
name资源的显示名称。OIDC
id用于 SSO 身份验证的客户端 ID。允许任意值,但 alauda-dex 除外,因为它是保留值,可能引发冲突。sonarqube-dex
secret用于 SSO 身份验证的客户端 secret。可以设置为任意值。Z2l0bGFiLW9mZmljaWFsLTAK
redirectURIsSonarqube 身份验证回调 URL,格式为 <sonarqube-host>/*https://example.sonarqube.com/*
metadata.name资源名称,必须基于 id 字段的哈希值计算得出。你可以使用诸如 https://go.dev/play/p/QsoqUohsKok 之类的在线哈希计算器生成。onxw4ylsof2wezjnmrsxrs7sttsiiirdeu
metadata.namespace平台系统命名空间,必须为 cpaas-systemcpaas-system

将 SSO 配置添加到 SonarQube 实例:

spec:
  helmValues:
    sonarProperties:
      sonar.core.serverBaseURL: <sonarqube url>
      sonar.forceAuthentication: false
      sonar.auth.oidc.enabled: true
      sonar.auth.oidc.issuerUri: <platform-url>/dex
      sonar.auth.oidc.clientId.secured: sonarqube-dex # Client id
      sonar.auth.oidc.clientSecret.secured: Z2l0bGFiLW9mZmljaWFsLTAK  # Client secret
      sonar.auth.oidc.loginStrategy: Preferred username
      sonar.auth.oidc.providerConfiguration: '{"issuer":"<platform-url>/dex","authorization_endpoint":"<platform-url>/dex/auth","token_endpoint":"<platform-url>/dex/token","jwks_uri":"<platform-url>/dex/keys","response_types_supported":["code","id_token","token"],"subject_types_supported":["public"],"id_token_signing_alg_values_supported":["RS256"],"scopes_supported":["openid","email","groups","profile","offline_access"],"token_endpoint_auth_methods_supported":["client_secret_basic"],"claims_supported":["aud","email","email_verified","exp","iat","iss","locale","name","sub"]}'

为使用自签名证书的平台启用 SSO

如果平台通过 https 访问并使用自签名证书,则需要将该自签名证书的 CA 挂载到 SonarQube 实例。方法如下:

在 global 集群的 cpaas-system 命名空间中,找到名为 dex.tls 的 secret,从该 secret 中获取 ca.crt 和 tls.crt 内容,保存为新的 secret,并将其创建到 SonarQube 实例所在的命名空间中。

apiVersion: v1
data:
  ca.crt: <base64 encode data>
  tls.crt: <base64 encode data>
kind: Secret
metadata:
  name: dex-tls
  namespace: cpaas-system

编辑 SonarQube 实例以使用此 CA:

spec:
  helmValues:
    caCerts:
      enabled: true
      secret: dex-tls

在纯 IPv6 集群中使用

在纯 IPv6 集群环境中部署时,由于 Java 默认支持双栈,因此需要显式配置 IPv6 协议设置。添加以下配置以确保连通性:

spec:
  helmValues:
    env:
      - name: JAVA_TOOL_OPTIONS
        value: '-Djava.net.preferIPv6Addresses=true'
    sonarProperties:
      sonar.cluster.node.search.host: '[::1]'
      sonar.cluster.node.es.host: '[::1]'
      sonar.web.javaAdditionalOpts: '-javaagent:/opt/sonarqube/extensions/plugins/sonarqube-community-branch-plugin-26.1.0.jar=web -Djava.net.preferIPv6Addresses=true'