This commit is contained in:
ouyang1204 2026-02-04 09:09:18 +08:00 committed by GitHub
commit bbc2a7ad06
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 634 additions and 2 deletions

View file

@ -221,6 +221,8 @@ const (
podUID = metaLabelPrefix + "pod_uid"
podControllerKind = metaLabelPrefix + "pod_controller_kind"
podControllerName = metaLabelPrefix + "pod_controller_name"
podIPv4Label = metaLabelPrefix + "pod_ipv4"
podIPv6Label = metaLabelPrefix + "pod_ipv6"
)
// GetControllerOf returns a pointer to a copy of the controllerRef if controllee has a controller
@ -244,6 +246,36 @@ func podLabels(pod *apiv1.Pod) model.LabelSet {
podUID: lv(string(pod.UID)),
}
// PodIPs contains all the IP addresses of the eth0 network interface in the Pod container.
// The specific number of IP addresses depends on the specific cni plugin response, which means there may be more than two IP addresses.
// However, in most cases, there is one IP address (single-stack; IPv4 or IPv6) or two IP addresses (dual-stack; IPv4 and IPv6 co-existing simultaneously).
// If there are multiple IP addresses, use the first IPv4 and IPv6 respectively, this way, it can be consistent with the primary IP address (pod.Status.PodIP behavior) and avoid exposing redundant IP information.
// Reference1: https://github.com/kubernetes/kubernetes/blob/c8fb7c9174b03ebc9a072913ca12c08ef3ed83c6/pkg/kubelet/kuberuntime/kuberuntime_sandbox.go#L317
// Reference2: https://github.com/containerd/containerd/blob/b99b82db7d05dfd257edcdb50bc72769e782d6c2/internal/cri/server/sandbox_run.go#L572
var ipv4Exists, ipv6Exists bool
for _, podIP := range pod.Status.PodIPs {
if podIP.IP == "" {
continue
}
_, ipv4Exists = ls[podIPv4Label]
_, ipv6Exists = ls[podIPv6Label]
if ipv4Exists && ipv6Exists {
break
}
ip := net.ParseIP(podIP.IP)
if ip != nil {
if ip.To4() != nil {
if !ipv4Exists {
ls[podIPv4Label] = lv(podIP.IP)
}
} else if ip.To16() != nil {
if !ipv6Exists {
ls[podIPv6Label] = lv(podIP.IP)
}
}
}
}
addObjectMetaLabels(ls, pod.ObjectMeta, RolePod)
createdBy := GetControllerOf(pod)
@ -310,8 +342,16 @@ func (p *Pod) buildPod(pod *apiv1.Pod) *targetgroup.Group {
if len(c.Ports) == 0 {
// We don't have a port so we just set the address label to the pod IP.
// The user has to add a port manually.
addr := pod.Status.PodIP
ip := net.ParseIP(addr)
if ip != nil && ip.To4() == nil {
// If pod.Status.PodIP is a IPv6 address, addr should be "[pod.Status.PodIP]".
// Contrary to what net.JoinHostPort does when the port is empty, RFC3986 forbids a ":" delimiter as the last character in such cases, leaving us with the following implementation.
// Refer https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.3
addr = "[" + ip.String() + "]"
}
tg.Targets = append(tg.Targets, model.LabelSet{
model.AddressLabel: lv(pod.Status.PodIP),
model.AddressLabel: lv(addr),
podContainerNameLabel: lv(c.Name),
podContainerIDLabel: lv(cID),
podContainerImageLabel: lv(c.Image),

View file

@ -627,3 +627,593 @@ func TestPodDiscoveryWithUpdatedNamespaceMetadata(t *testing.T) {
},
}.Run(t)
}
func TestPodDiscoveryAddWithDualStackIPv4First(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
Ports: []v1.ContainerPort{
{
Name: "testport",
Protocol: v1.ProtocolTCP,
ContainerPort: int32(9000),
},
},
},
},
},
Status: v1.PodStatus{
PodIP: "100.117.147.144",
PodIPs: []v1.PodIP{
{IP: "100.117.147.144"},
{IP: "2001::3238:634c:2ed4:ed01"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "100.117.147.144:9000",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_port_name": "testport",
"__meta_kubernetes_pod_container_port_number": "9000",
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "100.117.147.144",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv4": "100.117.147.144",
"__meta_kubernetes_pod_ipv6": "2001::3238:634c:2ed4:ed01",
},
Source: key,
},
},
}.Run(t)
}
func TestPodDiscoveryAddWithDualStackIPv6First(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
Ports: []v1.ContainerPort{
{
Name: "testport",
Protocol: v1.ProtocolTCP,
ContainerPort: int32(9000),
},
},
},
},
},
Status: v1.PodStatus{
PodIP: "2001::3238:634c:2ed4:ed01",
PodIPs: []v1.PodIP{
{IP: "2001::3238:634c:2ed4:ed01"},
{IP: "100.117.147.144"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "[2001::3238:634c:2ed4:ed01]:9000",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_port_name": "testport",
"__meta_kubernetes_pod_container_port_number": "9000",
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "2001::3238:634c:2ed4:ed01",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv4": "100.117.147.144",
"__meta_kubernetes_pod_ipv6": "2001::3238:634c:2ed4:ed01",
},
Source: key,
},
},
}.Run(t)
}
func TestPodDiscoveryAddWithSingleStackIPv4(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
Ports: []v1.ContainerPort{
{
Name: "testport",
Protocol: v1.ProtocolTCP,
ContainerPort: int32(9000),
},
},
},
},
},
Status: v1.PodStatus{
PodIP: "100.117.147.144",
PodIPs: []v1.PodIP{
{IP: "100.117.147.144"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "100.117.147.144:9000",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_port_name": "testport",
"__meta_kubernetes_pod_container_port_number": "9000",
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "100.117.147.144",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv4": "100.117.147.144",
},
Source: key,
},
},
}.Run(t)
}
func TestPodDiscoveryAddWithSingleStackIPv6(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
Ports: []v1.ContainerPort{
{
Name: "testport",
Protocol: v1.ProtocolTCP,
ContainerPort: int32(9000),
},
},
},
},
},
Status: v1.PodStatus{
PodIP: "2001::3238:634c:2ed4:ed01",
PodIPs: []v1.PodIP{
{IP: "2001::3238:634c:2ed4:ed01"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "[2001::3238:634c:2ed4:ed01]:9000",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_port_name": "testport",
"__meta_kubernetes_pod_container_port_number": "9000",
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "2001::3238:634c:2ed4:ed01",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv6": "2001::3238:634c:2ed4:ed01",
},
Source: key,
},
},
}.Run(t)
}
func TestPodDiscoveryAddWithSingleStackIPv6WithoutPorts(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
},
},
},
Status: v1.PodStatus{
PodIP: "2001::3238:634c:2ed4:ed01",
PodIPs: []v1.PodIP{
{IP: "2001::3238:634c:2ed4:ed01"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "[2001::3238:634c:2ed4:ed01]",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "2001::3238:634c:2ed4:ed01",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv6": "2001::3238:634c:2ed4:ed01",
},
Source: key,
},
},
}.Run(t)
}
func TestPodDiscoveryAddWithSingleStackIPv4WithoutPorts(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
},
},
},
Status: v1.PodStatus{
PodIP: "100.117.147.144",
PodIPs: []v1.PodIP{
{IP: "100.117.147.144"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "100.117.147.144",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "100.117.147.144",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv4": "100.117.147.144",
},
Source: key,
},
},
}.Run(t)
}
func TestPodDiscoveryAddWithDualStackAndMultiIPs(t *testing.T) {
t.Parallel()
n, c := makeDiscovery(RolePod, NamespaceDiscovery{})
ns := "default"
key := fmt.Sprintf("pod/%s/testpod", ns)
k8sDiscoveryTest{
discovery: n,
afterStart: func() {
obj := &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "testpod",
Namespace: "default",
UID: types.UID("abc123"),
},
Spec: v1.PodSpec{
NodeName: "testnode",
Containers: []v1.Container{
{
Name: "testcontainer",
Image: "testcontainer:latest",
Ports: []v1.ContainerPort{
{
Name: "testport",
Protocol: v1.ProtocolTCP,
ContainerPort: int32(9000),
},
},
},
},
},
Status: v1.PodStatus{
PodIP: "100.117.147.144",
PodIPs: []v1.PodIP{
{IP: "100.117.147.144"},
{IP: "110.117.147.144"},
{IP: "2001::3238:634c:2ed4:ed01"},
{IP: "2002::3238:634c:2ed4:ed01"},
},
HostIP: "2.3.4.5",
Phase: "Running",
Conditions: []v1.PodCondition{
{
Type: v1.PodReady,
Status: v1.ConditionTrue,
},
},
ContainerStatuses: []v1.ContainerStatus{
{
Name: "testcontainer",
ContainerID: "docker://a1b2c3d4e5f6",
},
},
},
}
c.CoreV1().Pods(obj.Namespace).Create(context.Background(), obj, metav1.CreateOptions{})
},
expectedMaxItems: 1,
expectedRes: map[string]*targetgroup.Group{
key: {
Targets: []model.LabelSet{
{
"__address__": "100.117.147.144:9000",
"__meta_kubernetes_pod_container_name": "testcontainer",
"__meta_kubernetes_pod_container_image": "testcontainer:latest",
"__meta_kubernetes_pod_container_port_name": "testport",
"__meta_kubernetes_pod_container_port_number": "9000",
"__meta_kubernetes_pod_container_port_protocol": "TCP",
"__meta_kubernetes_pod_container_init": "false",
"__meta_kubernetes_pod_container_id": "docker://a1b2c3d4e5f6",
},
},
Labels: model.LabelSet{
"__meta_kubernetes_pod_name": "testpod",
"__meta_kubernetes_namespace": lv(ns),
"__meta_kubernetes_pod_node_name": "testnode",
"__meta_kubernetes_pod_ip": "100.117.147.144",
"__meta_kubernetes_pod_host_ip": "2.3.4.5",
"__meta_kubernetes_pod_ready": "true",
"__meta_kubernetes_pod_phase": "Running",
"__meta_kubernetes_pod_uid": "abc123",
"__meta_kubernetes_pod_ipv4": "100.117.147.144",
"__meta_kubernetes_pod_ipv6": "2001::3238:634c:2ed4:ed01",
},
Source: key,
},
},
}.Run(t)
}

View file

@ -2112,7 +2112,7 @@ Available meta labels:
* `__meta_kubernetes_namespace`: The namespace of the pod object.
* `__meta_kubernetes_pod_name`: The name of the pod object.
* `__meta_kubernetes_pod_ip`: The pod IP of the pod object.
* `__meta_kubernetes_pod_ip`: The pod IP of the pod object, corresponds to `pod.Status.PodIP`.
* `__meta_kubernetes_pod_label_<labelname>`: Each label from the pod object, with any unsupported characters converted to an underscore.
* `__meta_kubernetes_pod_labelpresent_<labelname>`: `true` for each label from the pod object, with any unsupported characters converted to an underscore.
* `__meta_kubernetes_pod_annotation_<annotationname>`: Each annotation from the pod object.
@ -2132,6 +2132,8 @@ Available meta labels:
* `__meta_kubernetes_pod_uid`: The UID of the pod object.
* `__meta_kubernetes_pod_controller_kind`: Object kind of the pod controller.
* `__meta_kubernetes_pod_controller_name`: Name of the pod controller.
* `__meta_kubernetes_pod_ipv4`: The pod IPv4 of the pod object, corresponds to `pod.Status.PodIPs`.
* `__meta_kubernetes_pod_ipv6`: The pod IPv6 of the pod object, corresponds to `pod.Status.PodIPs`.
#### `endpoints`