×

在开发利用裸机集群节点上的精确时间协议 (PTP) 事件的使用者应用程序时,您需要在单独的应用程序 Pod 中部署使用者应用程序。使用者应用程序使用 PTP 事件 REST API v1 订阅 PTP 事件。

以下信息提供了开发使用 PTP 事件的使用者应用程序的一般指导。完整的事件使用者应用程序示例不在本信息的范围内。

PTP 事件 REST API v1 和事件使用者应用程序 Sidecar 是已弃用的功能。已弃用的功能仍包含在 OpenShift Container Platform 中,并继续得到支持;但是,它将在该产品的未来版本中删除,不建议用于新部署。

有关 OpenShift Container Platform 中已弃用或删除的主要功能的最新列表,请参阅 OpenShift Container Platform 发行说明的“已弃用和删除的功能”部分。

关于 PTP 快速事件通知框架

使用精确时间协议 (PTP) 快速事件 REST API v2 将集群应用程序订阅到裸机集群节点生成的 PTP 事件。

快速事件通知框架使用 REST API 进行通信。PTP 事件 REST API v1 和 v2 基于可从O-RAN ALLIANCE Specifications获取的O-RAN O-Cloud 事件使用者通知 API 规范 3.0

只有 PTP 事件 REST API v2 符合 O-RAN v3。

使用 PTP 事件 REST API v1 获取 PTP 事件

应用程序以 Sidecar 模式运行cloud-event-proxy容器来订阅PTP事件。cloud-event-proxy Sidecar容器可以访问与主应用程序容器相同的资源,而无需使用任何主应用程序资源,并且不会产生明显的延迟。

Overview of PTP fast events with consumer sidecar and HTTP message transport
图1. 使用消费者Sidecar和HTTP消息传输的PTP快速事件概述
20 事件在集群主机上生成

PTP Operator管理的Pod中的linuxptp-daemon作为Kubernetes DaemonSet运行,并管理各种linuxptp进程(ptp4lphc2sys,以及可选的大主控时钟ts2phc)。linuxptp-daemon将事件传递到UNIX域套接字。

20 事件传递到cloud-event-proxy Sidecar

PTP插件从UNIX域套接字读取事件,并将其传递到PTP Operator管理的Pod中的cloud-event-proxy Sidecar。cloud-event-proxy以低延迟将事件从Kubernetes基础设施传递到云原生网络功能(CNF)。

20 事件持久化

PTP Operator管理的Pod中的cloud-event-proxy Sidecar处理事件,并使用REST API发布云原生事件。

20 消息传输

消息传输器通过HTTP将事件传输到应用程序Pod中的cloud-event-proxy Sidecar。

20 事件可通过 REST API 获取。

应用程序 Pod 中的 cloud-event-proxy 侧车会处理事件,并使用 REST API 提供该事件。

20 消费者应用程序请求订阅并接收已订阅的事件。

消费者应用程序向应用程序 Pod 中的 cloud-event-proxy 侧车发送 API 请求以创建 PTP 事件订阅。cloud-event-proxy 侧车为订阅中指定的资源创建 HTTP 消息侦听器协议。

应用程序 Pod 中的 cloud-event-proxy 侧车从 PTP 运营商管理的 Pod 接收事件,解包云事件对象以检索数据,并将事件发布到消费者应用程序。消费者应用程序侦听资源限定符中指定的地址,并接收和处理 PTP 事件。

配置 PTP 快速事件通知发布者

要在集群中开始对网络接口使用 PTP 快速事件通知,必须在 PTP 运营商 PtpOperatorConfig 自定义资源 (CR) 中启用快速事件发布者,并在创建的 PtpConfig CR 中配置 ptpClockThreshold 值。

先决条件
  • 您已安装 OpenShift Container Platform CLI (oc)。

  • 您已以具有 cluster-admin 权限的用户身份登录。

  • 您已安装 PTP 运营商。

步骤
  1. 修改默认 PTP 运营商配置以启用 PTP 快速事件。

    1. 将以下 YAML 保存到 ptp-operatorconfig.yaml 文件中

      apiVersion: ptp.openshift.io/v1
      kind: PtpOperatorConfig
      metadata:
        name: default
        namespace: openshift-ptp
      spec:
        daemonNodeSelector:
          node-role.kubernetes.io/worker: ""
        ptpEventConfig:
          enableEventPublisher: true (1)
      1 通过将 enableEventPublisher 设置为 true 来启用 PTP 快速事件通知。

      在 OpenShift Container Platform 4.13 或更高版本中,当您对 PTP 事件使用 HTTP 传输时,无需在 PtpOperatorConfig 资源中设置 spec.ptpEventConfig.transportHost 字段。

    2. 更新 PtpOperatorConfig CR

      $ oc apply -f ptp-operatorconfig.yaml
  2. 为启用了 PTP 的接口创建一个 PtpConfig 自定义资源 (CR),并设置 ptpClockThresholdptp4lOpts 的所需值。以下 YAML 演示了您必须在 PtpConfig CR 中设置的所需值

    spec:
      profile:
      - name: "profile1"
        interface: "enp5s0f0"
        ptp4lOpts: "-2 -s --summary_interval -4" (1)
        phc2sysOpts: "-a -r -m -n 24 -N 8 -R 16" (2)
        ptp4lConf: "" (3)
        ptpClockThreshold: (4)
          holdOverTimeout: 5
          maxOffsetThreshold: 100
          minOffsetThreshold: -100
    1 附加 --summary_interval -4 以使用 PTP 快速事件。
    2 所需的 phc2sysOpts 值。-m 将消息打印到 stdoutlinuxptp-daemon DaemonSet 解析日志并生成 Prometheus 指标。
    3 指定一个包含配置的字符串,以替换默认的 /etc/ptp4l.conf 文件。要使用默认配置,请将该字段留空。
    4 可选。如果不存在 ptpClockThreshold 节,则对 ptpClockThreshold 字段使用默认值。该节显示默认的 ptpClockThreshold 值。ptpClockThreshold 值配置 PTP 主时钟断开连接后触发 PTP 事件之前的持续时间。holdOverTimeout 是在 PTP 主时钟断开连接时 PTP 时钟事件状态更改为 FREERUN 之前的秒数时间值。maxOffsetThresholdminOffsetThreshold 设置配置以纳秒为单位的偏移值,这些值与 CLOCK_REALTIME (phc2sys) 或主偏移 (ptp4l) 的值进行比较。当 ptp4lphc2sys 偏移值超出此范围时,PTP 时钟状态将设置为 FREERUN。当偏移值在此范围内时,PTP 时钟状态将设置为 LOCKED
其他资源

PTP 事件消费者应用程序参考

PTP 事件消费者应用程序需要以下功能

  1. 运行带有 POST 处理程序的 Web 服务以接收云原生 PTP 事件 JSON 有效负载

  2. 一个 createSubscription 函数,用于订阅 PTP 事件生产者

  3. 一个 getCurrentState 函数,用于轮询 PTP 事件生产者的当前状态

以下 Go 代码片段说明了这些需求

Go 中的示例 PTP 事件消费者服务器函数
func server() {
  http.HandleFunc("/event", getEvent)
  http.ListenAndServe("localhost:8989", nil)
}

func getEvent(w http.ResponseWriter, req *http.Request) {
  defer req.Body.Close()
  bodyBytes, err := io.ReadAll(req.Body)
  if err != nil {
    log.Errorf("error reading event %v", err)
  }
  e := string(bodyBytes)
  if e != "" {
    processEvent(bodyBytes)
    log.Infof("received event %s", string(bodyBytes))
  } else {
    w.WriteHeader(http.StatusNoContent)
  }
}
Go 中的示例 PTP 事件 createSubscription 函数
import (
"github.com/redhat-cne/sdk-go/pkg/pubsub"
"github.com/redhat-cne/sdk-go/pkg/types"
v1pubsub "github.com/redhat-cne/sdk-go/v1/pubsub"
)

// Subscribe to PTP events using REST API
s1,_:=createsubscription("/cluster/node/<node_name>/sync/sync-status/os-clock-sync-state") (1)
s2,_:=createsubscription("/cluster/node/<node_name>/sync/ptp-status/class-change")
s3,_:=createsubscription("/cluster/node/<node_name>/sync/ptp-status/lock-state")

// Create PTP event subscriptions POST
func createSubscription(resourceAddress string) (sub pubsub.PubSub, err error) {
  var status int
      apiPath:= "/api/ocloudNotifications/v1/"
      localAPIAddr:=localhost:8989 // vDU service API address
      apiAddr:= "localhost:8089" // event framework API address

  subURL := &types.URI{URL: url.URL{Scheme: "http",
    Host: apiAddr
    Path: fmt.Sprintf("%s%s", apiPath, "subscriptions")}}
  endpointURL := &types.URI{URL: url.URL{Scheme: "http",
    Host: localAPIAddr,
    Path: "event"}}

  sub = v1pubsub.NewPubSub(endpointURL, resourceAddress)
  var subB []byte

  if subB, err = json.Marshal(&sub); err == nil {
    rc := restclient.New()
    if status, subB = rc.PostWithReturn(subURL, subB); status != http.StatusCreated {
      err = fmt.Errorf("error in subscription creation api at %s, returned status %d", subURL, status)
    } else {
      err = json.Unmarshal(subB, &sub)
    }
  } else {
    err = fmt.Errorf("failed to marshal subscription for %s", resourceAddress)
  }
  return
}
1 <node_name> 替换为正在生成 PTP 事件的节点的 FQDN。例如,compute-1.example.com
Go 中的示例 PTP 事件消费者 getCurrentState 函数
//Get PTP event state for the resource
func getCurrentState(resource string) {
  //Create publisher
  url := &types.URI{URL: url.URL{Scheme: "http",
    Host: localhost:8989,
    Path: fmt.SPrintf("/api/ocloudNotifications/v1/%s/CurrentState",resource}}
  rc := restclient.New()
  status, event := rc.Get(url)
  if status != http.StatusOK {
    log.Errorf("CurrentState:error %d from url %s, %s", status, url.String(), event)
  } else {
    log.Debugf("Got CurrentState: %s ", event)
  }
}

参考 cloud-event-proxy 部署和服务 CR

在部署 PTP 事件消费者应用程序时,请参考以下示例 cloud-event-proxy 部署和订阅者服务 CR。

使用 HTTP 传输的参考 cloud-event-proxy 部署
apiVersion: apps/v1
kind: Deployment
metadata:
  name: event-consumer-deployment
  namespace: <namespace>
  labels:
    app: consumer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: consumer
  template:
    metadata:
      labels:
        app: consumer
    spec:
      serviceAccountName: sidecar-consumer-sa
      containers:
        - name: event-subscriber
          image: event-subscriber-app
        - name: cloud-event-proxy-as-sidecar
          image: openshift4/ose-cloud-event-proxy
          args:
            - "--metrics-addr=127.0.0.1:9091"
            - "--store-path=/store"
            - "--transport-host=consumer-events-subscription-service.cloud-events.svc.cluster.local:9043"
            - "--http-event-publishers=ptp-event-publisher-service-NODE_NAME.openshift-ptp.svc.cluster.local:9043"
            - "--api-port=8089"
          env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            - name: NODE_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.hostIP
              volumeMounts:
                - name: pubsubstore
                  mountPath: /store
          ports:
            - name: metrics-port
              containerPort: 9091
            - name: sub-port
              containerPort: 9043
          volumes:
            - name: pubsubstore
              emptyDir: {}
参考 cloud-event-proxy 订阅者服务
apiVersion: v1
kind: Service
metadata:
  annotations:
    prometheus.io/scrape: "true"
    service.alpha.openshift.io/serving-cert-secret-name: sidecar-consumer-secret
  name: consumer-events-subscription-service
  namespace: cloud-events
  labels:
    app: consumer-service
spec:
  ports:
    - name: sub-port
      port: 9043
  selector:
    app: consumer
  clusterIP: None
  sessionAffinity: None
  type: ClusterIP

使用 REST API v1 订阅 PTP 事件

将您的 cloud-event-consumer 应用程序容器和 cloud-event-proxy 侧车容器部署到单独的应用程序 Pod 中。

在应用程序 pod 中,将cloud-event-consumer应用程序订阅到cloud-event-proxy容器在https://127.0.0.1:8089/api/ocloudNotifications/v1/发布的PTP事件。

9089是部署在应用程序pod中的cloud-event-consumer容器的默认端口。您可以根据需要为您的应用程序配置不同的端口。

验证PTP事件REST API v1消费者应用程序是否正在接收事件

验证应用程序pod中的cloud-event-proxy容器是否正在接收PTP事件。

先决条件
  • 您已安装OpenShift CLI (oc)。

  • 您已以具有 cluster-admin 权限的用户身份登录。

  • 您已安装并配置了PTP Operator。

步骤
  1. 获取活动linuxptp-daemon pod 的列表。运行以下命令:

    $ oc get pods -n openshift-ptp
    示例输出
    NAME                    READY   STATUS    RESTARTS   AGE
    linuxptp-daemon-2t78p   3/3     Running   0          8h
    linuxptp-daemon-k8n88   3/3     Running   0          8h
  2. 通过运行以下命令访问所需消费者端cloud-event-proxy容器的指标:

    $ oc exec -it <linuxptp-daemon> -n openshift-ptp -c cloud-event-proxy -- curl 127.0.0.1:9091/metrics

    其中

    <linuxptp-daemon>

    指定您要查询的pod,例如,linuxptp-daemon-2t78p

    示例输出
    # HELP cne_transport_connections_resets Metric to get number of connection resets
    # TYPE cne_transport_connections_resets gauge
    cne_transport_connection_reset 1
    # HELP cne_transport_receiver Metric to get number of receiver created
    # TYPE cne_transport_receiver gauge
    cne_transport_receiver{address="/cluster/node/compute-1.example.com/ptp",status="active"} 2
    cne_transport_receiver{address="/cluster/node/compute-1.example.com/redfish/event",status="active"} 2
    # HELP cne_transport_sender Metric to get number of sender created
    # TYPE cne_transport_sender gauge
    cne_transport_sender{address="/cluster/node/compute-1.example.com/ptp",status="active"} 1
    cne_transport_sender{address="/cluster/node/compute-1.example.com/redfish/event",status="active"} 1
    # HELP cne_events_ack Metric to get number of events produced
    # TYPE cne_events_ack gauge
    cne_events_ack{status="success",type="/cluster/node/compute-1.example.com/ptp"} 18
    cne_events_ack{status="success",type="/cluster/node/compute-1.example.com/redfish/event"} 18
    # HELP cne_events_transport_published Metric to get number of events published by the transport
    # TYPE cne_events_transport_published gauge
    cne_events_transport_published{address="/cluster/node/compute-1.example.com/ptp",status="failed"} 1
    cne_events_transport_published{address="/cluster/node/compute-1.example.com/ptp",status="success"} 18
    cne_events_transport_published{address="/cluster/node/compute-1.example.com/redfish/event",status="failed"} 1
    cne_events_transport_published{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 18
    # HELP cne_events_transport_received Metric to get number of events received  by the transport
    # TYPE cne_events_transport_received gauge
    cne_events_transport_received{address="/cluster/node/compute-1.example.com/ptp",status="success"} 18
    cne_events_transport_received{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 18
    # HELP cne_events_api_published Metric to get number of events published by the rest api
    # TYPE cne_events_api_published gauge
    cne_events_api_published{address="/cluster/node/compute-1.example.com/ptp",status="success"} 19
    cne_events_api_published{address="/cluster/node/compute-1.example.com/redfish/event",status="success"} 19
    # HELP cne_events_received Metric to get number of events received
    # TYPE cne_events_received gauge
    cne_events_received{status="success",type="/cluster/node/compute-1.example.com/ptp"} 18
    cne_events_received{status="success",type="/cluster/node/compute-1.example.com/redfish/event"} 18
    # HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
    # TYPE promhttp_metric_handler_requests_in_flight gauge
    promhttp_metric_handler_requests_in_flight 1
    # HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
    # TYPE promhttp_metric_handler_requests_total counter
    promhttp_metric_handler_requests_total{code="200"} 4
    promhttp_metric_handler_requests_total{code="500"} 0
    promhttp_metric_handler_requests_total{code="503"} 0

监控PTP快速事件指标

您可以监控运行linuxptp-daemon的集群节点上的PTP快速事件指标。您还可以使用预配置的和自更新的Prometheus监控堆栈,在OpenShift Container Platform Web控制台中监控PTP快速事件指标。

先决条件
  • 安装OpenShift Container Platform CLI oc

  • 以具有cluster-admin权限的用户身份登录。

  • 在具有PTP功能的硬件的节点上安装并配置PTP Operator。

步骤
  1. 通过运行以下命令启动节点的调试pod:

    $ oc debug node/<node_name>
  2. 检查linuxptp-daemon容器公开的PTP指标。例如,运行以下命令:

    sh-4.4# curl https://127.0.0.1:9091/metrics
    示例输出
    # HELP cne_api_events_published Metric to get number of events published by the rest api
    # TYPE cne_api_events_published gauge
    cne_api_events_published{address="/cluster/node/compute-1.example.com/sync/gnss-status/gnss-sync-status",status="success"} 1
    cne_api_events_published{address="/cluster/node/compute-1.example.com/sync/ptp-status/lock-state",status="success"} 94
    cne_api_events_published{address="/cluster/node/compute-1.example.com/sync/ptp-status/class-change",status="success"} 18
    cne_api_events_published{address="/cluster/node/compute-1.example.com/sync/sync-status/os-clock-sync-state",status="success"} 27
  3. 可选。您也可以在cloud-event-proxy容器的日志中找到PTP事件。例如,运行以下命令:

    $ oc logs -f linuxptp-daemon-cvgr6 -n openshift-ptp -c cloud-event-proxy
  4. 要在OpenShift Container Platform Web控制台中查看PTP事件,请复制要查询的PTP指标名称,例如,openshift_ptp_offset_ns

  5. 在OpenShift Container Platform Web控制台中,单击**观察** → **指标**。

  6. 将PTP指标名称粘贴到**表达式**字段中,然后单击**运行查询**。

其他资源

PTP快速事件指标参考

下表描述了可在运行linuxptp-daemon服务的集群节点上获得的PTP快速事件指标。

表1. PTP快速事件指标
指标 描述 示例

openshift_ptp_clock_class

返回接口的PTP时钟类别。PTP时钟类别的可能值为6 (LOCKED), 7 (PRC UNLOCKED IN-SPEC), 52 (PRC UNLOCKED OUT-OF-SPEC), 187 (PRC UNLOCKED OUT-OF-SPEC), 135 (T-BC HOLDOVER IN-SPEC), 165 (T-BC HOLDOVER OUT-OF-SPEC), 248 (DEFAULT)或255 (SLAVE ONLY CLOCK)。

{node="compute-1.example.com",process="ptp4l"} 6

openshift_ptp_clock_state

返回接口的当前PTP时钟状态。PTP时钟状态的可能值为FREERUNLOCKEDHOLDOVER

{iface="CLOCK_REALTIME", node="compute-1.example.com", process="phc2sys"} 1

openshift_ptp_delay_ns

返回主时钟发送计时数据包与从时钟接收计时数据包之间的延迟(纳秒)。

{from="master", iface="ens2fx", node="compute-1.example.com", process="ts2phc"} 0

openshift_ptp_ha_profile_status

当不同网卡上有多个时间源时,返回高可用系统时钟的当前状态。可能的值为0 (INACTIVE)和1 (ACTIVE)。

{node="node1",process="phc2sys",profile="profile1"} 1 {node="node1",process="phc2sys",profile="profile2"} 0

openshift_ptp_frequency_adjustment_ns

返回两个PTP时钟之间的频率调整(纳秒)。例如,上游时钟和网卡之间、系统时钟和网卡之间或PTP硬件时钟 (phc) 和网卡之间。

{from="phc", iface="CLOCK_REALTIME", node="compute-1.example.com", process="phc2sys"} -6768

openshift_ptp_interface_role

返回接口配置的PTP时钟角色。可能的值为0 (PASSIVE)、1 (SLAVE)、2 (MASTER)、3 (FAULTY)、4 (UNKNOWN)或5 (LISTENING)。

{iface="ens2f0", node="compute-1.example.com", process="ptp4l"} 2

openshift_ptp_max_offset_ns

返回两个时钟或接口之间的最大偏移量(纳秒)。例如,上游GNSS时钟和网卡 (ts2phc) 之间,或PTP硬件时钟 (phc) 和系统时钟 (phc2sys) 之间。

{from="master", iface="ens2fx", node="compute-1.example.com", process="ts2phc"} 1.038099569e+09

openshift_ptp_offset_ns

返回DPLL时钟或GNSS时钟源与网卡硬件时钟之间的偏移量(纳秒)。

{from="phc", iface="CLOCK_REALTIME", node="compute-1.example.com", process="phc2sys"} -9

openshift_ptp_process_restart_count

返回ptp4lts2phc进程重启的次数。

{config="ptp4l.0.config", node="compute-1.example.com",process="phc2sys"} 1

openshift_ptp_process_status

返回一个状态代码,指示PTP进程是否正在运行。

{config="ptp4l.0.config", node="compute-1.example.com",process="phc2sys"} 1

openshift_ptp_threshold

返回HoldOverTimeoutMaxOffsetThresholdMinOffsetThreshold的值。

  • holdOverTimeout是PTP主时钟断开连接后PTP时钟事件状态更改为FREERUN之前的时间值(秒)。

  • maxOffsetThresholdminOffsetThreshold是以纳秒为单位的偏移值,这些值与您在网卡的PtpConfig CR中配置的CLOCK_REALTIME (phc2sys) 或主偏移值 (ptp4l) 进行比较。

{node="compute-1.example.com", profile="grandmaster", threshold="HoldOverTimeout"} 5

仅当启用T-GM时才显示PTP快速事件指标

下表描述了仅当启用PTP主时钟 (T-GM) 时才可用的PTP快速事件指标。

表2. 启用T-GM时的PTP快速事件指标
指标 描述 示例

openshift_ptp_frequency_status

返回网卡数字锁相环 (DPLL) 频率的当前状态。可能的值为-1 (UNKNOWN)、0 (INVALID)、1 (FREERUN)、2 (LOCKED)、3 (LOCKED_HO_ACQ)或4 (HOLDOVER)。

{from="dpll",iface="ens2fx",node="compute-1.example.com",process="dpll"} 3

openshift_ptp_nmea_status

返回NMEA连接的当前状态。NMEA是用于1PPS网卡连接的协议。可能的值为0 (UNAVAILABLE)和1 (AVAILABLE)。

{iface="ens2fx",node="compute-1.example.com",process="ts2phc"} 1

openshift_ptp_phase_status

返回网卡DPLL相位的状态。可能的值为-1 (UNKNOWN)、0 (INVALID)、1 (FREERUN)、2 (LOCKED)、3 (LOCKED_HO_ACQ)或4 (HOLDOVER)。

{from="dpll",iface="ens2fx",node="compute-1.example.com",process="dpll"} 3

openshift_ptp_pps_status

返回网卡1PPS连接的当前状态。您可以使用1PPS连接来同步连接的网卡之间的时间。可能的值为0 (UNAVAILABLE)和1 (AVAILABLE)。

{from="dpll",iface="ens2fx",node="compute-1.example.com",process="dpll"} 1

openshift_ptp_gnss_status

返回全球导航卫星系统 (GNSS) 连接的当前状态。GNSS 全球范围内提供基于卫星的定位、导航和授时服务。可能的值为 0 (NOFIX)、1 (仅依靠航位推算)、2 (二维定位)、3 (三维定位)、4 (GPS+航位推算定位)、5 (仅时间定位)。

{from="gnss",iface="ens2fx",node="compute-1.example.com",process="gnss"} 3