Azure Kubernetes Service の Kubernetes クラスタを C# から操作する

はじめに

Azure Kubernetes Service(AKS) をプログラムから操作する必要があり、 KubernetesClient を使えばリソースの作成や更新ができそうだったので試してみた。

www.nuget.org

github.com

認証情報を取得

操作対象は AKS なので、まずは認証情報を取得しておく必要がある。

az aks get-credentials --name <aks-cluster-name> --resource-group <resource-group-name>

実行すると次のようなメッセージが表示された。

Merged "<aks-cluster-name>" as current context in C:\Users\<user>\.kube\config

リソースを作成

名前空間とデプロイメントとサービスを作成。 AKS が HTTP/2 をサポートしていないようで、ハンドシェイク失敗で嵌った。

using k8s;
using k8s.Models;

var config = KubernetesClientConfiguration.BuildConfigFromConfigFile();

// AKS が HTTP/2 をサポートしていないのか、ハンドシェイクで失敗する。
// HTTP/2 を使わないようにしておく。
config.DisableHttp2 = true;

var client = new Kubernetes(config);

// 名前空間を作成
const string namespaceName = "<your-namespace-name>";
var newNamespace = new V1Namespace
{
    Metadata = new V1ObjectMeta
    {
        Name = namespaceName,
        Labels = new Dictionary<string, string>
        {
            ["name"] = namespaceName,
        },
    },
};
await client.CreateNamespaceAsync(newNamespace);

// デプロイメントを作成
const string deploymentName = "<your-deployment-name>";
const string imageName = "<your-image-name>";
var newDeployment = new V1Deployment
{
    Metadata = new V1ObjectMeta
    {
        Name = deploymentName,
        NamespaceProperty = namespaceName,
        Labels = new Dictionary<string, string>
        {
            ["app"] = deploymentName
        },
    },
    Spec = new V1DeploymentSpec
    {
        Replicas = 1,
        Template = new V1PodTemplateSpec
        {
            Metadata = new V1ObjectMeta
            {
                Name = deploymentName,
                Labels = new Dictionary<string, string>
                {
                    ["app"] = deploymentName,
                }
            },
            Spec = new V1PodSpec
            {
                NodeSelector = new Dictionary<string, string>
                {
                    ["kubernetes.io/os"] = "windows",
                },
                Containers = new List<V1Container>
                {
                    new V1Container
                    {
                        Name = deploymentName,
                        Image = imageName,
                        Ports = new List<V1ContainerPort>
                        {
                            new V1ContainerPort
                            {
                                ContainerPort = 443
                            }
                        }
                    }
                }
            }
        },
        Selector = new V1LabelSelector
        {
            MatchLabels = new Dictionary<string, string>
            {
                ["app"] = deploymentName,
            }
        },
    }
};
await client.CreateNamespacedDeploymentAsync(newDeployment, namespaceName);

// サービスを作成
const string serviceName = deploymentName;
var newService = new V1Service
{
    Metadata = new V1ObjectMeta
    {
        Name = serviceName,
        NamespaceProperty = namespaceName,
    },
    Spec = new V1ServiceSpec
    {
        Selector = new Dictionary<string, string>
        {
            ["app"] = deploymentName,
        },
        Ports = new List<V1ServicePort>
        {
            new V1ServicePort
            {
                Name = "https",
                Port = 443,
                TargetPort = 443,
            },
        },
        Type = "LoadBalancer",
    },
};
await client.CreateNamespacedServiceAsync(newService, namespaceName);

リソースを変更

デプロイメントを変更してみる。

// デプロイメントを変更
var deployment = await client.ReadNamespacedDeploymentAsync(deploymentName, namespaceName);
var container = deployment.Spec.Template.Spec.Containers.FirstOrDefault();
if (container != null)
{
    const string envVarName = "CONNECTION_STRING";
    const string newEnvVarValue = "NewConnectionString";
    var env = container.Env ?? new List<V1EnvVar>();
    var envVar = env.FirstOrDefault(x => x.Name == envVarName);
    if (envVar != null)
    {
        envVar.Value = newEnvVarValue;
    }
    else
    {
        env.Add(new V1EnvVar
        {
            Name = envVarName,
            Value = newEnvVarValue,
        });
    }
    container.Env = env;
    await client.ReplaceNamespacedDeploymentAsync(deployment, deploymentName, namespaceName);
}

リソースを削除

後始末。

// サービスを削除
await client.DeleteNamespacedServiceAsync(serviceName, namespaceName);

// デプロイメントを削除
await client.DeleteNamespacedDeploymentAsync(deploymentName, namespaceName);

// 名前空間を削除
await client.DeleteNamespaceAsync(namespaceName);

おわりに

Kubernetes で書いていた YAML ファイルと同じ内容を、C# で組み立てればいいだけだったので、 大して難しくはなかった。HTTP/2 回避して以降はスンナリ。

KubernetesClient の API は素直で好印象だった。