Skip to content

Commit a782fc1

Browse files
authored
Support analysis waypoint metrics in Envoy ALS receiver (#13244)
1 parent 3739add commit a782fc1

File tree

6 files changed

+132
-37
lines changed

6 files changed

+132
-37
lines changed

docs/en/changes/changes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
* BanyanDB: Support cold stage data query for metrics/traces/logs.
1717
* Increase the idle check interval of the message queue to 200ms to reduce CPU usage under low load conditions.
1818
* Limit max attempts of DNS resolution of Istio ServiceEntry to 3, and do not wait for first resolution result in case the DNS is not resolvable at all.
19+
* Support analysis waypoint metrics in Envoy ALS receiver.
1920

2021
#### UI
2122

oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/AccessLogAnalyzer.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ default Role identify(StreamAccessLogsMessage.Identifier alsIdentifier, Role def
6565
return Role.PROXY;
6666
} else if (id.startsWith("sidecar~")) {
6767
return Role.SIDECAR;
68+
} else if (id.startsWith("waypoint~")) {
69+
return Role.WAYPOINT;
6870
}
6971
return defaultRole;
7072
}

oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/Role.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,5 +33,9 @@ public enum Role {
3333
/**
3434
* Sidecar in mesh
3535
*/
36-
SIDECAR
36+
SIDECAR,
37+
/**
38+
* Waypoint in ambient mesh
39+
*/
40+
WAYPOINT,
3741
}

oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/k8s/Addresses.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,34 @@
2020

2121
import io.envoyproxy.envoy.config.core.v3.Address;
2222

23-
import static java.util.Objects.nonNull;
23+
import static java.util.Objects.isNull;
2424
import static org.apache.skywalking.oap.server.library.util.StringUtil.isNotBlank;
2525

2626
public class Addresses {
2727
public static boolean isValid(final Address address) {
28-
return nonNull(address)
29-
&& address.hasSocketAddress()
30-
&& isNotBlank(address.getSocketAddress().getAddress());
28+
if (isNull(address)) {
29+
return false;
30+
}
31+
if (address.hasSocketAddress()) {
32+
return isNotBlank(address.getSocketAddress().getAddress());
33+
}
34+
if (address.hasEnvoyInternalAddress()) {
35+
return isNotBlank(address.getEnvoyInternalAddress().getEndpointId()) &&
36+
address.getEnvoyInternalAddress().getEndpointId().split(":").length == 2;
37+
}
38+
return false;
39+
}
40+
41+
public static String getAddressIP(final Address address) {
42+
if (isNull(address)) {
43+
return null;
44+
}
45+
if (address.hasSocketAddress()) {
46+
return address.getSocketAddress().getAddress();
47+
}
48+
if (address.hasEnvoyInternalAddress()) {
49+
return address.getEnvoyInternalAddress().getEndpointId().split(":")[0];
50+
}
51+
return null;
3152
}
3253
}

oap-server/server-receiver-plugin/envoy-metrics-receiver-plugin/src/main/java/org/apache/skywalking/oap/server/receiver/envoy/als/k8s/K8sALSServiceMeshHTTPAnalysis.java

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818

1919
package org.apache.skywalking.oap.server.receiver.envoy.als.k8s;
2020

21+
import com.google.protobuf.Value;
2122
import io.envoyproxy.envoy.config.core.v3.Address;
22-
import io.envoyproxy.envoy.config.core.v3.SocketAddress;
2323
import io.envoyproxy.envoy.data.accesslog.v3.AccessLogCommon;
2424
import io.envoyproxy.envoy.data.accesslog.v3.HTTPAccessLogEntry;
2525
import io.envoyproxy.envoy.service.accesslog.v3.StreamAccessLogsMessage;
@@ -28,6 +28,7 @@
2828
import org.apache.skywalking.apm.network.servicemesh.v3.HTTPServiceMeshMetric;
2929
import org.apache.skywalking.apm.network.servicemesh.v3.ServiceMeshMetrics;
3030
import org.apache.skywalking.oap.server.library.module.ModuleManager;
31+
import org.apache.skywalking.oap.server.library.util.StringUtil;
3132
import org.apache.skywalking.oap.server.receiver.envoy.EnvoyMetricReceiverConfig;
3233
import org.apache.skywalking.oap.server.receiver.envoy.ServiceMetaInfoFactory;
3334
import org.apache.skywalking.oap.server.receiver.envoy.als.AbstractALSAnalyzer;
@@ -79,9 +80,9 @@ public Result analysis(
7980
return result;
8081
}
8182
return analyzeSideCar(result, entry);
83+
case WAYPOINT:
84+
return analyzeWaypoint(result, identifier, entry);
8285
case NONE:
83-
// For the Ambient Istio with waypoint mode, the role is NONE.
84-
// The analysis should keep the result from other roles.
8586
return result;
8687
}
8788

@@ -163,16 +164,68 @@ protected Result analyzeProxy(Result previousResult, final HTTPAccessLogEntry en
163164
return previousResult;
164165
}
165166

166-
final SocketAddress downstreamRemoteAddressSocketAddress = downstreamRemoteAddress.getSocketAddress();
167-
final SocketAddress downstreamLocalAddressSocketAddress = downstreamLocalAddress.getSocketAddress();
168-
final ServiceMetaInfo ingress = find(downstreamLocalAddressSocketAddress.getAddress());
167+
return buildUpstreamDownstreamMetrics(previousResult, entry,
168+
Addresses.getAddressIP(downstreamRemoteAddress),
169+
Addresses.getAddressIP(downstreamLocalAddress),
170+
Addresses.getAddressIP(upstreamRemoteAddress), NON_TLS);
171+
}
172+
173+
protected Result analyzeWaypoint(Result previousResult, StreamAccessLogsMessage.Identifier identifier, final HTTPAccessLogEntry entry) {
174+
if (!entry.hasCommonProperties()) {
175+
return previousResult;
176+
}
177+
if (previousResult.hasUpstreamMetrics() && previousResult.hasDownstreamMetrics()) {
178+
return previousResult;
179+
}
180+
181+
final String waypointIP = getWaypointIP(identifier);
182+
final AccessLogCommon properties = entry.getCommonProperties();
183+
final Address downstreamRemoteAddress = properties.hasDownstreamDirectRemoteAddress() &&
184+
Addresses.isValid(properties.getDownstreamDirectRemoteAddress()) ?
185+
properties.getDownstreamDirectRemoteAddress() : properties.getDownstreamRemoteAddress();
186+
final Address upstreamRemoteAddress = properties.getUpstreamRemoteAddress();
187+
if (!isValid(downstreamRemoteAddress) || !isValid(upstreamRemoteAddress)) {
188+
return previousResult;
189+
}
190+
191+
return buildUpstreamDownstreamMetrics(previousResult, entry,
192+
Addresses.getAddressIP(downstreamRemoteAddress),
193+
waypointIP,
194+
Addresses.getAddressIP(upstreamRemoteAddress), null);
195+
}
196+
197+
protected String getWaypointIP(StreamAccessLogsMessage.Identifier identifier) {
198+
final Value instanceIps = identifier.getNode().getMetadata().getFieldsMap().get("INSTANCE_IPS");
199+
if (instanceIps != null && instanceIps.hasStringValue()) {
200+
final String[] split = instanceIps.getStringValue().split(":", 2);
201+
if (split.length == 2) {
202+
return split[0];
203+
}
204+
}
205+
206+
final String nodeId = identifier.getNode().getId();
207+
final String[] nodeInfo = nodeId.split("~", 3);
208+
if (nodeInfo.length != 3) {
209+
return null;
210+
}
211+
return nodeInfo[1];
212+
}
213+
214+
protected Result buildUpstreamDownstreamMetrics(Result previousResult, final HTTPAccessLogEntry entry,
215+
String downStreamRemoteAddr, String downStreamLocalAddr,
216+
String upstreamRemoteAddr, String upstreamMetricsTLS) {
217+
if (StringUtil.isEmpty(downStreamRemoteAddr) || StringUtil.isEmpty(downStreamLocalAddr) || StringUtil.isEmpty(upstreamRemoteAddr)) {
218+
return previousResult;
219+
}
220+
221+
final ServiceMetaInfo ingress = find(downStreamLocalAddr);
169222

170223
final var newResult = previousResult.toBuilder();
171224
final var previousMetrics = previousResult.getMetrics();
172225
final var previousHttpMetrics = previousMetrics.getHttpMetricsBuilder();
173226

174227
if (!previousResult.hasDownstreamMetrics()) {
175-
final ServiceMetaInfo outside = find(downstreamRemoteAddressSocketAddress.getAddress());
228+
final ServiceMetaInfo outside = find(downStreamRemoteAddr);
176229

177230
final HTTPServiceMeshMetric.Builder metric = newAdapter(entry, outside, ingress).adaptToDownstreamMetrics();
178231

@@ -182,14 +235,15 @@ protected Result analyzeProxy(Result previousResult, final HTTPAccessLogEntry en
182235
}
183236

184237
if (!previousResult.hasUpstreamMetrics()) {
185-
final SocketAddress upstreamRemoteAddressSocketAddress = upstreamRemoteAddress.getSocketAddress();
186-
final ServiceMetaInfo targetService = find(upstreamRemoteAddressSocketAddress.getAddress());
238+
final ServiceMetaInfo targetService = find(upstreamRemoteAddr);
187239

188-
final HTTPServiceMeshMetric.Builder outboundMetric =
240+
HTTPServiceMeshMetric.Builder outboundMetric =
189241
newAdapter(entry, ingress, targetService)
190-
.adaptToUpstreamMetrics()
191-
// Can't parse it from tls properties, leave it to Server side.
192-
.setTlsMode(NON_TLS);
242+
.adaptToUpstreamMetrics();
243+
if (StringUtil.isNotEmpty(upstreamMetricsTLS)) {
244+
// Can't parse it from tls properties, leave it to Server side.
245+
outboundMetric = outboundMetric.setTlsMode(NON_TLS);
246+
}
193247

194248
log.debug("Transformed ingress outbound mesh metric {}", outboundMetric);
195249
previousHttpMetrics.addMetrics(outboundMetric);

oap-server/server-receiver-plugin/receiver-proto/src/main/proto/envoy/config/core/v3/address.proto

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import "validate/validate.proto";
1313
option java_package = "io.envoyproxy.envoy.config.core.v3";
1414
option java_outer_classname = "AddressProto";
1515
option java_multiple_files = true;
16+
option go_package = "github.com/envoyproxy/go-control-plane/envoy/config/core/v3;corev3";
1617
option (udpa.annotations.file_status).package_version_status = ACTIVE;
1718

1819
// [#protodoc-title: Network addresses]
@@ -30,19 +31,24 @@ message Pipe {
3031
uint32 mode = 2 [(validate.rules).uint32 = {lte: 511}];
3132
}
3233

33-
// [#not-implemented-hide:] The address represents an envoy internal listener.
34-
// TODO(lambdai): Make this address available for listener and endpoint.
35-
// TODO(asraa): When address available, remove workaround from test/server/server_fuzz_test.cc:30.
34+
// The address represents an envoy internal listener.
35+
// [#comment: TODO(asraa): When address available, remove workaround from test/server/server_fuzz_test.cc:30.]
3636
message EnvoyInternalAddress {
3737
oneof address_name_specifier {
3838
option (validate.required) = true;
3939

40-
// [#not-implemented-hide:] The :ref:`listener name <envoy_api_field_config.listener.v3.Listener.name>` of the destination internal listener.
40+
// Specifies the :ref:`name <envoy_v3_api_field_config.listener.v3.Listener.name>` of the
41+
// internal listener.
4142
string server_listener_name = 1;
4243
}
44+
45+
// Specifies an endpoint identifier to distinguish between multiple endpoints for the same internal listener in a
46+
// single upstream pool. Only used in the upstream addresses for tracking changes to individual endpoints. This, for
47+
// example, may be set to the final destination IP for the target internal listener.
48+
string endpoint_id = 2;
4349
}
4450

45-
// [#next-free-field: 7]
51+
// [#next-free-field: 8]
4652
message SocketAddress {
4753
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.SocketAddress";
4854

@@ -57,13 +63,13 @@ message SocketAddress {
5763
// to the address. An empty address is not allowed. Specify ``0.0.0.0`` or ``::``
5864
// to bind to any address. [#comment:TODO(zuercher) reinstate when implemented:
5965
// It is possible to distinguish a Listener address via the prefix/suffix matching
60-
// in :ref:`FilterChainMatch <envoy_api_msg_config.listener.v3.FilterChainMatch>`.] When used
61-
// within an upstream :ref:`BindConfig <envoy_api_msg_config.core.v3.BindConfig>`, the address
66+
// in :ref:`FilterChainMatch <envoy_v3_api_msg_config.listener.v3.FilterChainMatch>`.] When used
67+
// within an upstream :ref:`BindConfig <envoy_v3_api_msg_config.core.v3.BindConfig>`, the address
6268
// controls the source address of outbound connections. For :ref:`clusters
63-
// <envoy_api_msg_config.cluster.v3.Cluster>`, the cluster type determines whether the
64-
// address must be an IP (*STATIC* or *EDS* clusters) or a hostname resolved by DNS
65-
// (*STRICT_DNS* or *LOGICAL_DNS* clusters). Address resolution can be customized
66-
// via :ref:`resolver_name <envoy_api_field_config.core.v3.SocketAddress.resolver_name>`.
69+
// <envoy_v3_api_msg_config.cluster.v3.Cluster>`, the cluster type determines whether the
70+
// address must be an IP (``STATIC`` or ``EDS`` clusters) or a hostname resolved by DNS
71+
// (``STRICT_DNS`` or ``LOGICAL_DNS`` clusters). Address resolution can be customized
72+
// via :ref:`resolver_name <envoy_v3_api_field_config.core.v3.SocketAddress.resolver_name>`.
6773
string address = 2 [(validate.rules).string = {min_len: 1}];
6874

6975
oneof port_specifier {
@@ -72,7 +78,7 @@ message SocketAddress {
7278
uint32 port_value = 3 [(validate.rules).uint32 = {lte: 65535}];
7379

7480
// This is only valid if :ref:`resolver_name
75-
// <envoy_api_field_config.core.v3.SocketAddress.resolver_name>` is specified below and the
81+
// <envoy_v3_api_field_config.core.v3.SocketAddress.resolver_name>` is specified below and the
7682
// named resolver is capable of named port resolution.
7783
string named_port = 4;
7884
}
@@ -81,14 +87,19 @@ message SocketAddress {
8187
// this is empty, a context dependent default applies. If the address is a concrete
8288
// IP address, no resolution will occur. If address is a hostname this
8389
// should be set for resolution other than DNS. Specifying a custom resolver with
84-
// *STRICT_DNS* or *LOGICAL_DNS* will generate an error at runtime.
90+
// ``STRICT_DNS`` or ``LOGICAL_DNS`` will generate an error at runtime.
8591
string resolver_name = 5;
8692

8793
// When binding to an IPv6 address above, this enables `IPv4 compatibility
8894
// <https://tools.ietf.org/html/rfc3493#page-11>`_. Binding to ``::`` will
8995
// allow both IPv4 and IPv6 connections, with peer IPv4 addresses mapped into
9096
// IPv6 space as ``::FFFF:<IPv4-address>``.
9197
bool ipv4_compat = 6;
98+
99+
// The Linux network namespace to bind the socket to. If this is set, Envoy will
100+
// create the socket in the specified network namespace. Only supported on Linux.
101+
// [#not-implemented-hide:]
102+
string network_namespace_filepath = 7;
92103
}
93104

94105
message TcpKeepalive {
@@ -109,17 +120,18 @@ message TcpKeepalive {
109120
google.protobuf.UInt32Value keepalive_interval = 3;
110121
}
111122

123+
// [#next-free-field: 7]
112124
message BindConfig {
113125
option (udpa.annotations.versioning).previous_message_type = "envoy.api.v2.core.BindConfig";
114126

115127
// The address to bind to when creating a socket.
116-
SocketAddress source_address = 1 [(validate.rules).message = {required: true}];
128+
SocketAddress source_address = 1;
117129

118-
// Whether to set the *IP_FREEBIND* option when creating the socket. When this
130+
// Whether to set the ``IP_FREEBIND`` option when creating the socket. When this
119131
// flag is set to true, allows the :ref:`source_address
120-
// <envoy_api_field_config.cluster.v3.UpstreamBindConfig.source_address>` to be an IP address
132+
// <envoy_v3_api_field_config.core.v3.BindConfig.source_address>` to be an IP address
121133
// that is not configured on the system running Envoy. When this flag is set
122-
// to false, the option *IP_FREEBIND* is disabled on the socket. When this
134+
// to false, the option ``IP_FREEBIND`` is disabled on the socket. When this
123135
// flag is not set (default), the socket is not modified, i.e. the option is
124136
// neither enabled nor disabled.
125137
google.protobuf.BoolValue freebind = 2;
@@ -142,7 +154,8 @@ message Address {
142154

143155
Pipe pipe = 2;
144156

145-
// [#not-implemented-hide:]
157+
// Specifies a user-space address handled by :ref:`internal listeners
158+
// <envoy_v3_api_field_config.listener.v3.Listener.internal_listener>`.
146159
EnvoyInternalAddress envoy_internal_address = 3;
147160
}
148161
}
@@ -155,6 +168,6 @@ message CidrRange {
155168
// IPv4 or IPv6 address, e.g. ``192.0.0.0`` or ``2001:db8::``.
156169
string address_prefix = 1 [(validate.rules).string = {min_len: 1}];
157170

158-
// Length of prefix, e.g. 0, 32.
171+
// Length of prefix, e.g. 0, 32. Defaults to 0 when unset.
159172
google.protobuf.UInt32Value prefix_len = 2 [(validate.rules).uint32 = {lte: 128}];
160173
}

0 commit comments

Comments
 (0)