Linux 内核兼容性说明

对于运行 Linux 内核版本早于 4.11(例如 CentOS 7)的节点,网关安装前需要进行额外配置。根据是否需要监听 1024 以下端口,有两种场景:

  • 场景 1:仅针对内核版本早于 4.11 — 网关监听端口为 1024 或以上。需要将 sysctls: [] 设置为空,以移除默认的 net.ipv4.ip_unprivileged_port_start sysctl,因为该项在早于 4.11 的内核中不可用。
  • 场景 2:内核版本早于 4.11 且需要监听特权端口 — 网关需要监听 1024 以下端口。除了场景 1 中的 sysctls: [] 变更外,还必须添加 NET_BIND_SERVICE 能力,以允许绑定特权端口。
INFO

如果您的内核版本是 4.11 或更高版本,请跳过本节。

Istio Gateway

前提条件

  • 本地安装 jq,用于处理本步骤中的 JSON。

操作步骤

场景 1:仅针对内核版本早于 4.11(端口 ≥ 1024)

  1. 创建一个名为 gateway-injection-template.txt 的 YAML 文件,内容为网关的默认注入模板。

    点击展开
    gateway-injection-template.txt
    {{- $containers := list }}
    {{- range $index, $container := .Spec.Containers }}{{ if not (eq $container.Name "istio-proxy") }}{{ $containers = append $containers $container.Name }}{{end}}{{- end}}
    metadata:
      labels:
        service.istio.io/canonical-name: {{ index .ObjectMeta.Labels `service.istio.io/canonical-name` | default (index .ObjectMeta.Labels `app.kubernetes.io/name`) | default (index .ObjectMeta.Labels `app`) | default .DeploymentMeta.Name  | quote }}
        service.istio.io/canonical-revision: {{ index .ObjectMeta.Labels `service.istio.io/canonical-revision` | default (index .ObjectMeta.Labels `app.kubernetes.io/version`) | default (index .ObjectMeta.Labels `version`) | default "latest"  | quote }}
      annotations:
        istio.io/rev: {{ .Revision | default "default" | quote }}
        {{- if ge (len $containers) 1 }}
        {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-logs-container`) }}
        kubectl.kubernetes.io/default-logs-container: "{{ index $containers 0 }}"
        {{- end }}
        {{- if not (isset .ObjectMeta.Annotations `kubectl.kubernetes.io/default-container`) }}
        kubectl.kubernetes.io/default-container: "{{ index $containers 0 }}"
        {{- end }}
        {{- end }}
    spec:
      securityContext:
      {{- if .Values.gateways.securityContext }}
        {{- toYaml .Values.gateways.securityContext | nindent 4 }}
      {{- else }}
        sysctls: []  
      {{- end }}
      containers:
      - name: istio-proxy
      {{- if contains "/" (annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image) }}
        image: "{{ annotation .ObjectMeta `sidecar.istio.io/proxyImage` .Values.global.proxy.image }}"
      {{- else }}
        image: "{{ .ProxyImage }}"
      {{- end }}
        ports:
        - containerPort: 15090
          protocol: TCP
          name: http-envoy-prom
        args:
        - proxy
        - router
        - --domain
        - $(POD_NAMESPACE).svc.{{ .Values.global.proxy.clusterDomain }}
        - --proxyLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/logLevel` .Values.global.proxy.logLevel }}
        - --proxyComponentLogLevel={{ annotation .ObjectMeta `sidecar.istio.io/componentLogLevel` .Values.global.proxy.componentLogLevel }}
        - --log_output_level={{ annotation .ObjectMeta `sidecar.istio.io/agentLogLevel` .Values.global.logging.level }}
      {{- if .Values.global.sts.servicePort }}
        - --stsPort={{ .Values.global.sts.servicePort }}
      {{- end }}
      {{- if .Values.global.logAsJson }}
        - --log_as_json
      {{- end }}
      {{- if .Values.global.proxy.lifecycle }}
        lifecycle:
          {{ toYaml .Values.global.proxy.lifecycle | indent 6 }}
      {{- end }}
        securityContext:
          runAsUser: {{ .ProxyUID | default "1337" }}
          runAsGroup: {{ .ProxyGID | default "1337" }}
        env:
        - name: PILOT_CERT_PROVIDER
          value: {{ .Values.global.pilotCertProvider }}
        - name: CA_ADDR
        {{- if .Values.global.caAddress }}
          value: {{ .Values.global.caAddress }}
        {{- else }}
          value: istiod{{- if not (eq .Values.revision "") }}-{{ .Values.revision }}{{- end }}.{{ .Values.global.istioNamespace }}.svc:15012
        {{- end }}
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: INSTANCE_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: SERVICE_ACCOUNT
          valueFrom:
            fieldRef:
              fieldPath: spec.serviceAccountName
        - name: HOST_IP
          valueFrom:
            fieldRef:
              fieldPath: status.hostIP
        - name: ISTIO_CPU_LIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        - name: PROXY_CONFIG
          value: |
                {{ protoToJSON .ProxyConfig }}
        - name: ISTIO_META_POD_PORTS
          value: |-
            [
            {{- $first := true }}
            {{- range $index1, $c := .Spec.Containers }}
              {{- range $index2, $p := $c.Ports }}
                {{- if (structToJSON $p) }}
                {{if not $first}},{{end}}{{ structToJSON $p }}
                {{- $first = false }}
                {{- end }}
              {{- end}}
            {{- end}}
            ]
        - name: GOMEMLIMIT
          valueFrom:
            resourceFieldRef:
              resource: limits.memory
        - name: GOMAXPROCS
          valueFrom:
            resourceFieldRef:
              resource: limits.cpu
        {{- if .CompliancePolicy }}
        - name: COMPLIANCE_POLICY
          value: "{{ .CompliancePolicy }}"
        {{- end }}
        - name: ISTIO_META_APP_CONTAINERS
          value: "{{ $containers | join "," }}"
        - name: ISTIO_META_CLUSTER_ID
          value: "{{ valueOrDefault .Values.global.multiCluster.clusterName `Kubernetes` }}"
        - name: ISTIO_META_NODE_NAME
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName
        - name: ISTIO_META_INTERCEPTION_MODE
          value: "{{ .ProxyConfig.InterceptionMode.String }}"
        {{- if .Values.global.network }}
        - name: ISTIO_META_NETWORK
          value: "{{ .Values.global.network }}"
        {{- end }}
        {{- if .DeploymentMeta.Name }}
        - name: ISTIO_META_WORKLOAD_NAME
          value: "{{ .DeploymentMeta.Name }}"
        {{ end }}
        {{- if and .TypeMeta.APIVersion .DeploymentMeta.Name }}
        - name: ISTIO_META_OWNER
          value: kubernetes://apis/{{ .TypeMeta.APIVersion }}/namespaces/{{ valueOrDefault .DeploymentMeta.Namespace `default` }}/{{ toLower .TypeMeta.Kind}}s/{{ .DeploymentMeta.Name }}
        {{- end}}
        {{- if .Values.global.meshID }}
        - name: ISTIO_META_MESH_ID
          value: "{{ .Values.global.meshID }}"
        {{- else if (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}
        - name: ISTIO_META_MESH_ID
          value: "{{ (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain) }}"
        {{- end }}
        {{- with (valueOrDefault .MeshConfig.TrustDomain .Values.global.trustDomain)  }}
        - name: TRUST_DOMAIN
          value: "{{ . }}"
        {{- end }}
        {{- range $key, $value := .ProxyConfig.ProxyMetadata }}
        - name: {{ $key }}
          value: "{{ $value }}"
        {{- end }}
        {{with .Values.global.imagePullPolicy }}imagePullPolicy: "{{.}}"{{end}}
        readinessProbe:
          httpGet:
            path: /healthz/ready
            port: 15021
          initialDelaySeconds: {{.Values.global.proxy.readinessInitialDelaySeconds }}
          periodSeconds: {{ .Values.global.proxy.readinessPeriodSeconds }}
          timeoutSeconds: 3
          failureThreshold: {{ .Values.global.proxy.readinessFailureThreshold }}
        volumeMounts:
        - name: workload-socket
          mountPath: /var/run/secrets/workload-spiffe-uds
        - name: credential-socket
          mountPath: /var/run/secrets/credential-uds
        {{- if eq .Values.global.caName "GkeWorkloadCertificate" }}
        - name: gke-workload-certificate
          mountPath: /var/run/secrets/workload-spiffe-credentials
          readOnly: true
        {{- else }}
        - name: workload-certs
          mountPath: /var/run/secrets/workload-spiffe-credentials
        {{- end }}
        {{- if eq .Values.global.pilotCertProvider "istiod" }}
        - mountPath: /var/run/secrets/istio
          name: istiod-ca-cert
        {{- end }}
        - mountPath: /var/lib/istio/data
          name: istio-data
        # SDS channel between istioagent and Envoy
        - mountPath: /etc/istio/proxy
          name: istio-envoy
        - mountPath: /var/run/secrets/tokens
          name: istio-token
        {{- if .Values.global.mountMtlsCerts }}
        # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications.
        - mountPath: /etc/certs/
          name: istio-certs
          readOnly: true
        {{- end }}
        - name: istio-podinfo
          mountPath: /etc/istio/pod
      volumes:
      - emptyDir: {}
        name: workload-socket
      - emptyDir: {}
        name: credential-socket
      {{- if eq .Values.global.caName "GkeWorkloadCertificate" }}
      - name: gke-workload-certificate
        csi:
          driver: workloadcertificates.security.cloud.google.com
      {{- else}}
      - emptyDir: {}
        name: workload-certs
      {{- end }}
      # SDS channel between istioagent and Envoy
      - emptyDir:
          medium: Memory
        name: istio-envoy
      - name: istio-data
        emptyDir: {}
      - name: istio-podinfo
        downwardAPI:
          items:
            - path: "labels"
              fieldRef:
                fieldPath: metadata.labels
            - path: "annotations"
              fieldRef:
                fieldPath: metadata.annotations
      - name: istio-token
        projected:
          sources:
          - serviceAccountToken:
              path: istio-token
              expirationSeconds: 43200
              audience: {{ .Values.global.sds.token.aud }}
      {{- if eq .Values.global.pilotCertProvider "istiod" }}
      - name: istiod-ca-cert
      {{- if eq (.Values.pilot.env).ENABLE_CLUSTER_TRUST_BUNDLE_API true }}
        projected:
          sources:
          - clusterTrustBundle:
            name: istio.io:istiod-ca:root-cert
            path: root-cert.pem
      {{- else }}
        configMap:
          name: istio-ca-root-cert
      {{- end }}
      {{- end }}
      {{- if .Values.global.mountMtlsCerts }}
      # Use the key and cert mounted to /etc/certs/ for the in-cluster mTLS communications.
      - name: istio-certs
        secret:
          optional: true
          {{ if eq .Spec.ServiceAccountName "" }}
          secretName: istio.default
          {{ else -}}
          secretName: {{  printf "istio.%s" .Spec.ServiceAccountName }}
          {{  end -}}
      {{- end }}
      {{- if .Values.global.imagePullSecrets }}
      imagePullSecrets:
        {{- range .Values.global.imagePullSecrets }}
        - name: {{ . }}
        {{- end }}
      {{- end }}
    1. 移除 sysctls,因为 net.ipv4.ip_unprivileged_port_start 在早于 4.11 的 Linux 内核中不可用。

场景 2:内核版本早于 4.11 且需要监听特权端口(端口 < 1024)

如果网关需要监听 1024 以下端口,除了 sysctls: [] 变更外,还必须为 istio-proxy 容器的 securityContext 添加 NET_BIND_SERVICE 能力。修改 gateway-injection-template.txt 中的 securityContext 部分:

    securityContext:
      capabilities:
        add:
        - NET_BIND_SERVICE
      runAsUser: {{ .ProxyUID | default "1337" }}
      runAsGroup: {{ .ProxyGID | default "1337" }}
      # 如有必要,取消注释以下字段以 root 用户身份运行网关。
      # runAsUser: 0
      # runAsGroup: 0
      # runAsNonRoot: false
WARNING

如果添加 NET_BIND_SERVICE 能力后,网关仍无法监听 1024 以下端口,请考虑以下替代方案:

  1. 使用 1024 或以上端口(推荐)— 重新配置网关监听端口为 ≥ 1024,避免权限问题。
  2. 以 root 用户身份运行网关 — 在容器的 securityContext 中设置 runAsUser: 0runAsGroup: 0runAsNonRoot: false。这将赋予网关进程全部权限。使用前请谨慎评估安全风险。

应用注入模板

  1. Istio 资源的默认 gateway 注入模板打补丁:

    TEMPLATE_CONTENT=$(cat gateway-injection-template.txt)
    PATCH_DATA=$(jq -n \
      --arg template "${TEMPLATE_CONTENT}" \
      '{
        "spec": {
          "values": {
            "sidecarInjectorWebhook": {
              "templates": {
                "gateway": $template
              }
            }
          }
        }
      }')
    # 最后将补丁应用到名为 `default` 的 Istio 资源:
    kubectl patch istio default --type=merge -p "${PATCH_DATA}"
  2. 运行以下命令,等待控制平面返回 Ready 状态条件:

    kubectl wait --for condition=Ready istio/default --timeout=3m

Kubernetes Gateway API

前提条件

  • Alauda Container Platform 4.2.0 或更高版本,或升级 Gateway API CRDs 至最新版本。

操作步骤

场景 1:仅针对内核版本早于 4.11(端口 ≥ 1024)

  1. 在计划部署 Gateway 的同一命名空间中,创建名为 asm-kube-gateway-options 的 ConfigMap:

    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: asm-kube-gateway-options
      # 将 `<your-gateway-namespace>` 替换为实际部署 Gateway 的命名空间。
      namespace: <your-gateway-namespace>
    data:
      deployment: |
        spec:
          template:
            spec:
              securityContext:
                sysctls: []
    1. 移除 sysctls,因为 net.ipv4.ip_unprivileged_port_start 在早于 4.11 的 Linux 内核中不可用。

场景 2:内核版本早于 4.11 且需要监听特权端口(端口 < 1024)

如果网关需要监听 1024 以下端口,在 ConfigMap 中添加 NET_BIND_SERVICE 能力。修改 data.deployment 字段,增加容器级别的安全上下文:

apiVersion: v1
kind: ConfigMap
metadata:
  name: asm-kube-gateway-options
  namespace: <your-gateway-namespace>
data:
  deployment: |
    spec:
      template:
        spec:
          securityContext:
            sysctls: []
          # 添加以下容器级安全上下文以授予 NET_BIND_SERVICE 能力。
          containers:
          - name: istio-proxy
            securityContext:
              capabilities:
                add:
                - NET_BIND_SERVICE
              # 如有必要,取消注释以下字段以 root 用户身份运行网关。
              # runAsUser: 0
              # runAsGroup: 0
              # runAsNonRoot: false
WARNING

如果添加 NET_BIND_SERVICE 能力后,网关仍无法监听 1024 以下端口,请考虑以下替代方案:

  1. 使用 1024 或以上端口(推荐)— 重新配置网关监听端口为 ≥ 1024,避免权限问题。
  2. 以 root 用户身份运行网关 — 在容器的 securityContext 中设置 runAsUser: 0runAsGroup: 0runAsNonRoot: false。这将赋予网关进程全部权限。使用前请谨慎评估安全风险。

应用 ConfigMap

  1. 在 Gateway 资源中引用该 ConfigMap,添加 infrastructure.parametersRef 字段:

    apiVersion: gateway.networking.k8s.io/v1
    kind: Gateway
    metadata:
      name: <your-gateway-name>
      namespace: <your-gateway-namespace>
    spec:
      # 在 Gateway CR 中添加以下基础设施配置
      infrastructure:
        parametersRef:
          group: ""
          kind: ConfigMap
          name: asm-kube-gateway-options
      # ... 其余 Gateway 配置

    此配置确保网关部署使用 ConfigMap 中定义的自定义安全上下文设置。