Sitelet https://github.com/kubernetes/kops/pull/18632/files
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/cluster_spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,23 @@ spec:
elbSecurityGroup: sg-123445678
```

### useIPBasedNodeNames
{{ kops_feature_table(kops_added_default='1.37') }}

*AWS only*

By default, kOps names Kubernetes nodes after the EC2 instance ID (for example `i-0123456789abcdef0`). Setting `useIPBasedNodeNames: true` names nodes after the EC2 private DNS name instead (for example `ip-10-0-0-1.eu-west-1.compute.internal`), restoring the naming used before kOps 1.24.

When enabled, kOps configures managed subnets to launch instances with IP-based EC2 hostnames instead of resource-based ones. Shared subnets are not modified and must be configured with IP-based hostnames (the EC2 default) by their owner. This option is not supported on IPv6-only clusters.

Changing this value only affects newly launched nodes; roll the cluster to rename existing nodes. When changing it on a running cluster, first make sure kops-controller picks up the new configuration, either by rolling the control plane or by deleting the kops-controller pods (`kubectl -n kube-system delete pod -l k8s-app=kops-controller`); nodes launched before that may fail to bootstrap and need replacement.

```yaml
spec:
cloudConfig:
useIPBasedNodeNames: true
```

### manageStorageClasses
{{ kops_feature_table(kops_added_default='1.20') }}

Expand Down Expand Up @@ -1643,6 +1660,7 @@ the removal of fields no longer in use.
| cloudConfig.openstack | cloudProvider.openstack |
| cloudConfig.spotinstOrientation | cloudProvider.aws.spotinstOrientation |
| cloudConfig.spotinstProduct | cloudProvider.aws.spotinstProduct |
| cloudConfig.useIPBasedNodeNames | cloudProvider.aws.useIPBasedNodeNames |
| cloudProvider (string) | cloudProvider (map) |
| configBase | configStore.base |
| DisableSubnetTags | tagSubnets (value inverted) |
Expand Down
2 changes: 2 additions & 0 deletions docs/releases/1.37-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This is a document to gather the release notes prior to the release.

* Support for AWS Classic Load Balancer (CLB) for the API, deprecated since kOps 1.26, has been removed. See the breaking changes section below for the required actions.

* On AWS, the new `spec.cloudProvider.aws.useIPBasedNodeNames` field (`spec.cloudConfig.useIPBasedNodeNames` in the v1alpha2 API) names Kubernetes nodes after the EC2 private DNS name (e.g. `ip-10-0-0-1.eu-west-1.compute.internal`) instead of the EC2 instance ID, restoring the naming used before kOps 1.24. Only newly launched nodes are affected; roll the cluster to rename existing nodes.

# Other changes of note

* TODO
Expand Down
5 changes: 5 additions & 0 deletions k8s/crds/kops.k8s.io_clusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,11 @@ spec:
spotinstProduct:
description: Spotinst cloud-config specs
type: string
useIPBasedNodeNames:
description: |-
UseIPBasedNodeNames names Kubernetes nodes after the EC2 private DNS name instead of the EC2
instance ID (AWS only).
type: boolean
vSphereCoreDNSServer:
description: VSphereCoreDNSServer is unused.
type: string
Expand Down
24 changes: 18 additions & 6 deletions nodeup/pkg/model/kubelet.go
Original file line number Diff line number Diff line change
Expand Up @@ -863,18 +863,19 @@ func (b *KubeletBuilder) buildKubeletServingCertificate(c *fi.NodeupModelBuilder
name := "kubelet-server"
dir := b.PathSrvKubernetes()

names, err := b.kubeletNames(c.Context())
if err != nil {
return err
}

var cert, key fi.Resource
if !b.HasAPIServer {
var err error
cert, key, err = b.GetBootstrapCert(name, fi.CertificateIDCA)
if err != nil {
return err
}
} else {
names, err := b.kubeletNames(c.Context())
if err != nil {
return err
}

issueCert := &nodetasks.IssueCert{
Name: name,
Signer: fi.CertificateIDCA,
Expand Down Expand Up @@ -925,7 +926,16 @@ func (b *KubeletBuilder) kubeletNames(ctx context.Context) ([]string, error) {
return append(addrs, name), nil
}

// The node name goes first when it differs from the instance ID, as it becomes the certificate
// CommonName.
addrs := []string{b.InstanceID}
nodeName, err := b.NodeName()
if err != nil {
return nil, fmt.Errorf("error getting NodeName: %v", err)
}
if nodeName != b.InstanceID {
addrs = append([]string{nodeName}, addrs...)
}
config, err := awsconfig.LoadDefaultConfig(ctx)
if err != nil {
return nil, fmt.Errorf("error loading AWS config: %v", err)
Expand All @@ -934,7 +944,9 @@ func (b *KubeletBuilder) kubeletNames(ctx context.Context) ([]string, error) {

if localHostname, err := getMetadata(ctx, metadata, "local-hostname"); err == nil {
klog.V(2).Infof("Local Hostname: %s", localHostname)
addrs = append(addrs, localHostname)
if localHostname != addrs[0] {
addrs = append(addrs, localHostname)
}
}
if localIPv4, err := getMetadata(ctx, metadata, "local-ipv4"); err == nil {
klog.V(2).Infof("Local IPv4: %s", localIPv4)
Expand Down
6 changes: 6 additions & 0 deletions pkg/apis/kops/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,12 @@ type AWSSpec struct {

// NodeIPFamilies control the IP families reported for each node.
NodeIPFamilies []string `json:"nodeIPFamilies,omitempty"`
// UseIPBasedNodeNames names Kubernetes nodes after the EC2 private DNS name, e.g.
// ip-10-0-0-1.eu-west-1.compute.internal, instead of the EC2 instance ID. When enabled, managed
// subnets launch instances with IP-based EC2 hostnames; shared subnets must be configured with
// IP-based hostnames by their owner. Not supported on IPv6-only clusters. Changing this value
// only affects newly launched nodes; existing nodes must be replaced to be renamed.
UseIPBasedNodeNames *bool `json:"useIPBasedNodeNames,omitempty"`
// DisableSecurityGroupIngress disables the Cloud Controller Manager's creation
// of an AWS Security Group for each load balancer provisioned for a Service.
DisableSecurityGroupIngress *bool `json:"disableSecurityGroupIngress,omitempty"`
Expand Down
4 changes: 4 additions & 0 deletions pkg/apis/kops/v1alpha2/componentconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -1011,6 +1011,10 @@ type CloudConfiguration struct {
// security groups for Network Load Balancers (AWS only). Valid value: "Managed"
// +k8s:conversion-gen=false
NLBSecurityGroupMode *string `json:"nlbSecurityGroupMode,omitempty"`
// UseIPBasedNodeNames names Kubernetes nodes after the EC2 private DNS name instead of the EC2
// instance ID (AWS only).
// +k8s:conversion-gen=false
UseIPBasedNodeNames *bool `json:"useIPBasedNodeNames,omitempty"`
// VSphereUsername is unused.
// +k8s:conversion-gen=false
VSphereUsername *string `json:"vSphereUsername,omitempty"`
Expand Down
14 changes: 14 additions & 0 deletions pkg/apis/kops/v1alpha2/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ func Convert_v1alpha2_ClusterSpec_To_kops_ClusterSpec(in *ClusterSpec, out *kops
val := *in.CloudConfig.ElbSecurityGroup
out.CloudProvider.AWS.ElbSecurityGroup = &val
}
if in.CloudConfig.UseIPBasedNodeNames != nil {
if out.CloudProvider.AWS == nil {
return field.Forbidden(field.NewPath("spec").Child("cloudConfig", "useIPBasedNodeNames"), "useIPBasedNodeNames supports only AWS")
}
val := *in.CloudConfig.UseIPBasedNodeNames
out.CloudProvider.AWS.UseIPBasedNodeNames = &val
}
if in.CloudConfig.NLBSecurityGroupMode != nil {
if out.CloudProvider.AWS == nil {
return field.Forbidden(field.NewPath("spec").Child("cloudConfig", "nlbSecurityGroupMode"), "nlbSecurityGroupMode supports only AWS")
Expand Down Expand Up @@ -463,6 +470,13 @@ func Convert_kops_ClusterSpec_To_v1alpha2_ClusterSpec(in *kops.ClusterSpec, out
val := *aws.DisableSecurityGroupIngress
out.CloudConfig.DisableSecurityGroupIngress = &val
}
if aws.UseIPBasedNodeNames != nil {
if out.CloudConfig == nil {
out.CloudConfig = &CloudConfiguration{}
}
val := *aws.UseIPBasedNodeNames
out.CloudConfig.UseIPBasedNodeNames = &val
}
if aws.EBSCSIDriver != nil {
if out.CloudConfig == nil {
out.CloudConfig = &CloudConfiguration{}
Expand Down
1 change: 1 addition & 0 deletions pkg/apis/kops/v1alpha2/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/kops/v1alpha2/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions pkg/apis/kops/v1alpha3/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,12 @@ type AWSSpec struct {

// NodeIPFamilies control the IP families reported for each node.
NodeIPFamilies []string `json:"nodeIPFamilies,omitempty"`
// UseIPBasedNodeNames names Kubernetes nodes after the EC2 private DNS name, e.g.
// ip-10-0-0-1.eu-west-1.compute.internal, instead of the EC2 instance ID. When enabled, managed
// subnets launch instances with IP-based EC2 hostnames; shared subnets must be configured with
// IP-based hostnames by their owner. Not supported on IPv6-only clusters. Changing this value
// only affects newly launched nodes; existing nodes must be replaced to be renamed.
UseIPBasedNodeNames *bool `json:"useIPBasedNodeNames,omitempty"`
// DisableSecurityGroupIngress disables the Cloud Controller Manager's creation
// of an AWS Security Group for each load balancer provisioned for a Service.
DisableSecurityGroupIngress *bool `json:"disableSecurityGroupIngress,omitempty"`
Expand Down
2 changes: 2 additions & 0 deletions pkg/apis/kops/v1alpha3/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions pkg/apis/kops/v1alpha3/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions pkg/apis/kops/validation/aws.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ func awsValidateCluster(c *kops.Cluster, strict bool) field.ErrorList {

allErrs = append(allErrs, awsValidateNLBSecurityGroupMode(c)...)

allErrs = append(allErrs, awsValidateUseIPBasedNodeNames(c)...)

if c.Spec.Authentication != nil && c.Spec.Authentication.AWS != nil {
allErrs = append(allErrs, awsValidateIAMAuthenticator(field.NewPath("spec", "authentication", "aws"), c.Spec.Authentication.AWS)...)
}
Expand Down Expand Up @@ -88,6 +90,16 @@ func awsValidateNLBSecurityGroupMode(cluster *kops.Cluster) (allErrs field.Error
return allErrs
}

func awsValidateUseIPBasedNodeNames(cluster *kops.Cluster) (allErrs field.ErrorList) {
c := cluster.Spec

fldPath := field.NewPath("spec", "cloudProvider", "aws", "useIPBasedNodeNames")
if fi.ValueOf(c.CloudProvider.AWS.UseIPBasedNodeNames) && c.IsIPv6Only() {
allErrs = append(allErrs, field.Forbidden(fldPath, "IP-based node names are not supported on IPv6-only clusters"))
}
return allErrs
}

func awsValidateInstanceGroup(ig *kops.InstanceGroup, cloud awsup.AWSCloud) field.ErrorList {
allErrs := field.ErrorList{}

Expand Down
54 changes: 54 additions & 0 deletions pkg/apis/kops/validation/aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -981,3 +981,57 @@ func TestAWSValidateNLBSecurityGroupMode(t *testing.T) {
testErrors(t, g.Input, errs, g.ExpectedErrors)
}
}

func TestAWSValidateUseIPBasedNodeNames(t *testing.T) {
grid := []struct {
Input kops.ClusterSpec
ExpectedErrors []string
}{
{
Input: kops.ClusterSpec{
CloudProvider: kops.CloudProviderSpec{
AWS: &kops.AWSSpec{},
},
},
},
{
Input: kops.ClusterSpec{
CloudProvider: kops.CloudProviderSpec{
AWS: &kops.AWSSpec{
UseIPBasedNodeNames: new(true),
},
},
},
},
{
Input: kops.ClusterSpec{
CloudProvider: kops.CloudProviderSpec{
AWS: &kops.AWSSpec{
UseIPBasedNodeNames: new(true),
},
},
Networking: kops.NetworkingSpec{
NonMasqueradeCIDR: "::/0",
},
},
ExpectedErrors: []string{"Forbidden::spec.cloudProvider.aws.useIPBasedNodeNames"},
},
{
Input: kops.ClusterSpec{
CloudProvider: kops.CloudProviderSpec{
AWS: &kops.AWSSpec{
UseIPBasedNodeNames: new(false),
},
},
Networking: kops.NetworkingSpec{
NonMasqueradeCIDR: "::/0",
},
},
},
}
for _, g := range grid {
cluster := &kops.Cluster{Spec: g.Input}
errs := awsValidateUseIPBasedNodeNames(cluster)
testErrors(t, g.Input, errs, g.ExpectedErrors)
}
}
5 changes: 5 additions & 0 deletions pkg/apis/kops/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions pkg/apis/nodeup/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ type Config struct {
NLBSecurityGroupMode *string `json:"nlbSecurityGroupMode,omitempty"`
// NodeIPFamilies controls the IP families reported for each node.
NodeIPFamilies []string `json:"nodeIPFamilies,omitempty"`
// UseIPBasedNodeNames names the node after the EC2 private DNS name instead of the instance ID.
UseIPBasedNodeNames bool `json:"useIPBasedNodeNames,omitempty"`
// WarmPoolImages are the container images to pre-pull during instance pre-initialization
WarmPoolImages []string `json:"warmPoolImages,omitempty"`

Expand Down Expand Up @@ -294,6 +296,8 @@ func NewConfig(cluster *kops.Cluster, instanceGroup *kops.InstanceGroup) (*Confi
config.NLBSecurityGroupMode = aws.NLBSecurityGroupMode
config.NodeIPFamilies = aws.NodeIPFamilies
}

config.UseIPBasedNodeNames = aws.UseIPBasedNodeNames != nil && *aws.UseIPBasedNodeNames
}

if cluster.Spec.CloudProvider.Azure != nil {
Expand Down
32 changes: 32 additions & 0 deletions pkg/bootstrap/awsbootstrap/names.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copyright 2026 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package awsbootstrap

import "strings"

// PrivateDNSName returns the DNS name that EC2 generates for an IP-named instance from its primary
// private IPv4 address: ip-a-b-c-d.ec2.internal in us-east-1 and
// ip-a-b-c-d.<region>.compute.internal in all other regions. With IP-based node names, both nodeup
// and kops-controller derive the node name with this formula, guaranteeing that the name the node
// registers with matches the name its certificates are issued for.
func PrivateDNSName(privateIPv4, region string) string {
domain := region + ".compute.internal"
if region == "us-east-1" {
domain = "ec2.internal"
}
return "ip-" + strings.ReplaceAll(privateIPv4, ".", "-") + "." + domain
}
Loading
Loading