Has anyone managed to set `kubeletExtraArgs` on a ...
# aws
i
Has anyone managed to set
kubeletExtraArgs
on a managed nodegroup for eks? I see it in the docs, but I cant add it https://www.pulumi.com/registry/packages/eks/api-docs/managednodegroup/#clusternodegroupoptions
w
Yes, via a launch template with custom user data. I haven't got time to walk you through it, but here's a code dump in dotnet/c#:
Copy code
// node groups
Logger.LogDebug("Creating eks nodes");
foreach (var nodeGroup in AwsConfig.Eks.NodeGroups.Values)
{
    // optimized ami; <https://docs.aws.amazon.com/eks/latest/userguide/retrieve-ami-id.html>
    var imageId = Output.Create(GetParameter.InvokeAsync(
        new GetParameterArgs { Name = $"/aws/service/eks/optimized-ami/{K8sConfig.Version}/amazon-linux-2/recommended/image_id" },
        new InvokeOptions { Provider = awsProvider }));

    // user data
    var kubeletExtraArgs = "--allowed-unsafe-sysctls=net.ipv4.ip_unprivileged_port_start";
    var userData = Output.Tuple(cluster.Name, cluster.Endpoint, cluster.CertificateAuthority.Apply(ca => ca.Data!))
        .Apply(((string ClusterName, string ClusterEndpoint, string ClusterCa) tuple) =>
            RenderTemplate("EksUserData.sh", ReadResource, new { tuple.ClusterName, tuple.ClusterEndpoint, tuple.ClusterCa, K8sConfig.ContainerRuntime, kubeletExtraArgs }));

    // launch template; <https://docs.aws.amazon.com/eks/latest/userguide/launch-templates.html>
    var launchTemplate = new LaunchTemplate($"{awsEksPrefix}-nodes-{nodeGroup.Name}",
        new LaunchTemplateArgs
        {
            BlockDeviceMappings = new LaunchTemplateBlockDeviceMappingArgs
            {
                DeviceName = "/dev/xvda",
                Ebs = new LaunchTemplateBlockDeviceMappingEbsArgs
                {
                    Encrypted = "true",
                    VolumeSize = nodeGroup.EbsVolumeSize ?? AwsConfig.Ec2.EbsVolumeSize,
                    VolumeType = nodeGroup.EbsVolumeType ?? AwsConfig.Ec2.EbsVolumeType
                }
            },
            EbsOptimized = "true",
            ImageId = imageId.Apply(parameter => parameter.Value),
            InstanceType = nodeGroup.InstanceType ?? AwsConfig.Ec2.InstanceType,
            KeyName = nodeGroup.KeyName ?? AwsConfig.Ec2.KeyName,
            MetadataOptions = new LaunchTemplateMetadataOptionsArgs { HttpEndpoint = "enabled", HttpPutResponseHopLimit = 2 },
            Monitoring = new LaunchTemplateMonitoringArgs { Enabled = nodeGroup.Monitoring ?? AwsConfig.Ec2.Monitoring },
            TagSpecifications =
            {
                new LaunchTemplateTagSpecificationArgs
                {
                    ResourceType = "instance",
                    Tags = DefaultTags.Merge(AwsConfig.Ec2.InstanceTags, new Dictionary<string, string> { ["Name"] = $"{awsEksPrefix}-node-{nodeGroup.Name}" })
                },
                new LaunchTemplateTagSpecificationArgs
                {
                    ResourceType = "volume",
                    Tags = DefaultTags.Merge(new Dictionary<string, string> { ["Name"] = $"{awsEksPrefix}-node-{nodeGroup.Name}" })
                }
            },
            UpdateDefaultVersion = true,
            UserData = userData.Apply(script => script.ToBase64()),
            VpcSecurityGroupIds = !EnvConfig.Legacy ? vpnSgId != null ? new[] { clusterSgId, vpnSgId! } : new[] { clusterSgId } : new[] { clusterSgId, eksNodeSgId!, vpnSgId! }
        },
        new CustomResourceOptions { Provider = awsProvider });

    // node group; <https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html>
    var managedNodeGroup = new NodeGroup($"{awsEksPrefix}-nodes-{nodeGroup.Name}",
        new NodeGroupArgs
        {
            ClusterName = cluster.Name,
            LaunchTemplate = new NodeGroupLaunchTemplateArgs
            {
                Id = launchTemplate.Id,
                Version = launchTemplate.LatestVersion.Apply(version => version.ToString())
            },
            NodeRoleArn = nodeRole.Arn,
            SubnetIds = privateSubnetIds,
            ScalingConfig = new NodeGroupScalingConfigArgs
            {
                DesiredSize = nodeGroup.AutoScaling.DesiredCapacity,
                MinSize = nodeGroup.AutoScaling.MinSize,
                MaxSize = nodeGroup.AutoScaling.MaxSize
            },
            Labels = { ["role"] = nodeGroup.Name },
            Taints = nodeGroup.Tainted
                ? new NodeGroupTaintArgs[]
                {
                    new() { Key = "role", Value = nodeGroup.Name, Effect = "NO_EXECUTE" },
                    new() { Key = "role", Value = nodeGroup.Name, Effect = "NO_SCHEDULE" }
                }
                : Array.Empty<NodeGroupTaintArgs>(),
            UpdateConfig = new NodeGroupUpdateConfigArgs { MaxUnavailable = 2 }
        },
        new CustomResourceOptions { DependsOn = awsAuth.Ready(), Protect = true, Provider = awsProvider });

    // node group asg tags for cluster autoscaler; workaround <https://github.com/aws/containers-roadmap/issues/608>
    managedNodeGroup.Resources.Apply(resources =>
    {
        var asgNames = resources.SelectMany(resource => resource.AutoscalingGroups).Select(asg => asg.Name!).ToArray();
        foreach (var asgName in asgNames)
        {
            new Tag($"{awsEksPrefix}-nodes-{nodeGroup.Name}-label",
                new TagArgs
                {
                    AutoscalingGroupName = asgName,
                    TagDetails = new TagTagArgs
                    {
                        Key = "<http://k8s.io/cluster-autoscaler/node-template/label/role|k8s.io/cluster-autoscaler/node-template/label/role>",
                        Value = nodeGroup.Name,
                        PropagateAtLaunch = true
                    }
                },
                new CustomResourceOptions { DependsOn = managedNodeGroup, Provider = awsProvider });

            new Tag($"{awsEksPrefix}-nodes-{nodeGroup.Name}-taint",
                new TagArgs
                {
                    AutoscalingGroupName = asgName,
                    TagDetails = new TagTagArgs
                    {
                        Key = "<http://k8s.io/cluster-autoscaler/node-template/taint/role|k8s.io/cluster-autoscaler/node-template/taint/role>",
                        Value = "NoSchedule",
                        PropagateAtLaunch = true
                    }
                },
                new CustomResourceOptions { DependsOn = managedNodeGroup, Provider = awsProvider });
        }
        return resources;
    });
}
🙌 1
EksUserData.sh
(template):
Copy code
#!/bin/bash
set -ex

export APISERVER_ENDPOINT='{{ clusterEndpoint }}'
export B64_CLUSTER_CA='{{ clusterCa }}'
export CONTAINER_RUNTIME='{{ containerRuntime }}'
export KUBELET_EXTRA_ARGS='{{ kubeletExtraArgs }}'

/etc/eks/bootstrap.sh {{ clusterName }}
Note: this is not using the eks package, just the aws package.
i
Thanks Sean!