在开发利用裸机集群节点上的精确时间协议 (PTP) 事件的使用者应用程序时,您需要在单独的应用程序 Pod 中部署使用者应用程序。使用者应用程序使用 PTP 事件 REST API v1 订阅 PTP 事件。
以下信息提供了开发使用 PTP 事件的使用者应用程序的一般指导。完整的事件使用者应用程序示例不在本信息的范围内。 |
PTP 事件 REST API v1 和事件使用者应用程序 Sidecar 是已弃用的功能。已弃用的功能仍包含在 OpenShift Container Platform 中,并继续得到支持;但是,它将在该产品的未来版本中删除,不建议用于新部署。 有关 OpenShift Container Platform 中已弃用或删除的主要功能的最新列表,请参阅 OpenShift Container Platform 发行说明的“已弃用和删除的功能”部分。 |
使用精确时间协议 (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。 |
应用程序以 Sidecar 模式运行cloud-event-proxy
容器来订阅PTP事件。cloud-event-proxy
Sidecar容器可以访问与主应用程序容器相同的资源,而无需使用任何主应用程序资源,并且不会产生明显的延迟。
PTP Operator管理的Pod中的linuxptp-daemon
作为Kubernetes DaemonSet
运行,并管理各种linuxptp
进程(ptp4l
、phc2sys
,以及可选的大主控时钟ts2phc
)。linuxptp-daemon
将事件传递到UNIX域套接字。
PTP插件从UNIX域套接字读取事件,并将其传递到PTP Operator管理的Pod中的cloud-event-proxy
Sidecar。cloud-event-proxy
以低延迟将事件从Kubernetes基础设施传递到云原生网络功能(CNF)。
PTP Operator管理的Pod中的cloud-event-proxy
Sidecar处理事件,并使用REST API发布云原生事件。
消息传输器通过HTTP将事件传输到应用程序Pod中的cloud-event-proxy
Sidecar。
应用程序 Pod 中的 cloud-event-proxy
侧车会处理事件,并使用 REST API 提供该事件。
消费者应用程序向应用程序 Pod 中的 cloud-event-proxy
侧车发送 API 请求以创建 PTP 事件订阅。cloud-event-proxy
侧车为订阅中指定的资源创建 HTTP 消息侦听器协议。
应用程序 Pod 中的 cloud-event-proxy
侧车从 PTP 运营商管理的 Pod 接收事件,解包云事件对象以检索数据,并将事件发布到消费者应用程序。消费者应用程序侦听资源限定符中指定的地址,并接收和处理 PTP 事件。
要在集群中开始对网络接口使用 PTP 快速事件通知,必须在 PTP 运营商 PtpOperatorConfig
自定义资源 (CR) 中启用快速事件发布者,并在创建的 PtpConfig
CR 中配置 ptpClockThreshold
值。
您已安装 OpenShift Container Platform CLI (oc
)。
您已以具有 cluster-admin
权限的用户身份登录。
您已安装 PTP 运营商。
修改默认 PTP 运营商配置以启用 PTP 快速事件。
将以下 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
CR
$ oc apply -f ptp-operatorconfig.yaml
为启用了 PTP 的接口创建一个 PtpConfig
自定义资源 (CR),并设置 ptpClockThreshold
和 ptp4lOpts
的所需值。以下 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 将消息打印到 stdout 。linuxptp-daemon DaemonSet 解析日志并生成 Prometheus 指标。 |
3 | 指定一个包含配置的字符串,以替换默认的 /etc/ptp4l.conf 文件。要使用默认配置,请将该字段留空。 |
4 | 可选。如果不存在 ptpClockThreshold 节,则对 ptpClockThreshold 字段使用默认值。该节显示默认的 ptpClockThreshold 值。ptpClockThreshold 值配置 PTP 主时钟断开连接后触发 PTP 事件之前的持续时间。holdOverTimeout 是在 PTP 主时钟断开连接时 PTP 时钟事件状态更改为 FREERUN 之前的秒数时间值。maxOffsetThreshold 和 minOffsetThreshold 设置配置以纳秒为单位的偏移值,这些值与 CLOCK_REALTIME (phc2sys ) 或主偏移 (ptp4l ) 的值进行比较。当 ptp4l 或 phc2sys 偏移值超出此范围时,PTP 时钟状态将设置为 FREERUN 。当偏移值在此范围内时,PTP 时钟状态将设置为 LOCKED 。 |
有关配置 linuxptp
服务作为具有 PTP 快速事件的普通时钟的完整示例 CR,请参阅 将 linuxptp 服务配置为普通时钟。
PTP 事件消费者应用程序需要以下功能
运行带有 POST
处理程序的 Web 服务以接收云原生 PTP 事件 JSON 有效负载
一个 createSubscription
函数,用于订阅 PTP 事件生产者
一个 getCurrentState
函数,用于轮询 PTP 事件生产者的当前状态
以下 Go 代码片段说明了这些需求
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)
}
}
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 。 |
//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)
}
}
在部署 PTP 事件消费者应用程序时,请参考以下示例 cloud-event-proxy
部署和订阅者服务 CR。
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: {}
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
将您的 cloud-event-consumer
应用程序容器和 cloud-event-proxy
侧车容器部署到单独的应用程序 Pod 中。
在应用程序 pod 中,将cloud-event-consumer
应用程序订阅到cloud-event-proxy
容器在https://127.0.0.1:8089/api/ocloudNotifications/v1/
发布的PTP事件。
|
验证应用程序pod中的cloud-event-proxy
容器是否正在接收PTP事件。
您已安装OpenShift CLI (oc
)。
您已以具有 cluster-admin
权限的用户身份登录。
您已安装并配置了PTP Operator。
获取活动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
通过运行以下命令访问所需消费者端cloud-event-proxy
容器的指标:
$ oc exec -it <linuxptp-daemon> -n openshift-ptp -c cloud-event-proxy -- curl 127.0.0.1:9091/metrics
其中
指定您要查询的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
您可以监控运行linuxptp-daemon
的集群节点上的PTP快速事件指标。您还可以使用预配置的和自更新的Prometheus监控堆栈,在OpenShift Container Platform Web控制台中监控PTP快速事件指标。
安装OpenShift Container Platform CLI oc
。
以具有cluster-admin
权限的用户身份登录。
在具有PTP功能的硬件的节点上安装并配置PTP Operator。
通过运行以下命令启动节点的调试pod:
$ oc debug node/<node_name>
检查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
可选。您也可以在cloud-event-proxy
容器的日志中找到PTP事件。例如,运行以下命令:
$ oc logs -f linuxptp-daemon-cvgr6 -n openshift-ptp -c cloud-event-proxy
要在OpenShift Container Platform Web控制台中查看PTP事件,请复制要查询的PTP指标名称,例如,openshift_ptp_offset_ns
。
在OpenShift Container Platform Web控制台中,单击**观察** → **指标**。
将PTP指标名称粘贴到**表达式**字段中,然后单击**运行查询**。
下表描述了可在运行linuxptp-daemon
服务的集群节点上获得的PTP快速事件指标。
指标 | 描述 | 示例 |
---|---|---|
|
返回接口的PTP时钟类别。PTP时钟类别的可能值为6 ( |
|
|
返回接口的当前PTP时钟状态。PTP时钟状态的可能值为 |
|
|
返回主时钟发送计时数据包与从时钟接收计时数据包之间的延迟(纳秒)。 |
|
|
当不同网卡上有多个时间源时,返回高可用系统时钟的当前状态。可能的值为0 ( |
|
|
返回两个PTP时钟之间的频率调整(纳秒)。例如,上游时钟和网卡之间、系统时钟和网卡之间或PTP硬件时钟 ( |
|
|
返回接口配置的PTP时钟角色。可能的值为0 ( |
|
|
返回两个时钟或接口之间的最大偏移量(纳秒)。例如,上游GNSS时钟和网卡 ( |
|
|
返回DPLL时钟或GNSS时钟源与网卡硬件时钟之间的偏移量(纳秒)。 |
|
|
返回 |
|
|
返回一个状态代码,指示PTP进程是否正在运行。 |
|
|
返回
|
|
下表描述了仅当启用PTP主时钟 (T-GM) 时才可用的PTP快速事件指标。
指标 | 描述 | 示例 |
---|---|---|
|
返回网卡数字锁相环 (DPLL) 频率的当前状态。可能的值为-1 ( |
|
|
返回NMEA连接的当前状态。NMEA是用于1PPS网卡连接的协议。可能的值为0 ( |
|
|
返回网卡DPLL相位的状态。可能的值为-1 ( |
|
|
返回网卡1PPS连接的当前状态。您可以使用1PPS连接来同步连接的网卡之间的时间。可能的值为0 ( |
|
|
返回全球导航卫星系统 (GNSS) 连接的当前状态。GNSS 全球范围内提供基于卫星的定位、导航和授时服务。可能的值为 0 ( |
|