在 .NET8 中獲取 k8s 集群的 Namespace id
將程序和機器進行綁定是一種 License 校驗的方法,需要能獲取到機器的唯一標識,比如獲取機器的 Mac 地址就是獲取唯一標識的一種方式,命令如下:
ifconfig |egrep 'ether' |awk '{{print $2}}'
但如果程序部署在 k8s 中,每次容器構(gòu)建,使用上面命令獲取的 Mac 地址就會發(fā)生變化,我使用 kubesphere 做測試發(fā)現(xiàn)的確如此。
那么在 k8s 環(huán)境中想要獲取唯一標識應該怎么辦呢?
思路
- 在 kubesphere 中,通常會以項目來進行組織,kubesphere 中的項目就是 k8s 中的 namespace,可以通過獲取 namespace id 的方式來獲取唯一標識。
- .NET8 容器內(nèi)部需要安裝 kubectl 命令。
步驟
構(gòu)建 .NET8 底包鏡像,供后面程序使用,Dockerfile 內(nèi)容如下:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get install -y curl
# 安裝 kubectl
RUN curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" && \
chmod +x kubectl && \
mv kubectl /usr/local/bin/kubectl
# 確保 kubectl 已正確安裝
RUN kubectl version --client
在 Dockerfile 所在目錄執(zhí)行下面命令進行鏡像構(gòu)建:
docker build -t net8-kube .
編寫示例程序獲取 namespace id,獲取 namespace id 的命令如下:
kubectl get namespace test -o jsonpath='{.metadata.uid}'
創(chuàng)建一個 .NET8 的 WebAPI 項目,執(zhí)行上面命令,并將結(jié)果輸出,代碼如下:
using System.Diagnostics;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.MapGet("/GetNamespaceId", (string name) =>
{
string result = "id is empty";
try
{
string cmd = "kubectl get namespace "+name+" -o jsonpath='{.metadata.uid}'";
result= "id is :"+ExecuteCommand(cmd);
} catch (Exception ex)
{ Console.WriteLine(ex.Message);
} return result;
}) .WithOpenApi();
app.Run();
string ExecuteCommand(string command)
{
var processInfo = new ProcessStartInfo("bash", "-c \"" + command + "\"")
{ RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
var process = new Process { StartInfo = processInfo };
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return output.Trim();
}
將程序發(fā)布,并在 publish 目錄中創(chuàng)建 Dockerfile 文件:
FROM net8-kube:latest
COPY . /app
WORKDIR /app
ENTRYPOINT ["dotnet", "namespaceid.dll"]
在 publish 目錄中執(zhí)行 docker build -t namespace-id-test . 命令進行測試程序的鏡像構(gòu)建。
在 kubesphere 中創(chuàng)建一個 test 項目,在該項目中創(chuàng)建無狀態(tài)負載部署示例程序,調(diào)用程序中的示例接口,發(fā)現(xiàn) namespace id 并沒有獲取到,日志中有報錯信息:
Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:test:default" cannot list resource "namespaces" in API group "" at the cluster scope
這個錯誤表明,當前在容器內(nèi)執(zhí)行 kubectl 命令的用戶(system:serviceaccount:test:default)沒有足夠的權限在集群范圍內(nèi)列出命名空間(namespaces)。這個問題通常與 k8s 中的角色綁定(RoleBinding)或集群角色綁定(ClusterRoleBinding)配置有關。
可以使用下面命令來查看對應賬戶是否有權限:
kubectl auth can-i list namespaces --as=system:serviceaccount:test:default
結(jié)果返回 yes 說明該 ServiceAccount 有權限,返回 no 說明沒有權限。
一種簡單的解決方法就是將賬戶綁定到管理員角色上,命令如下:
kubectl create clusterrolebinding test-admin-binding \ --clusterrole=cluster-admin \ --serviceaccount=test:default
但 cluster-admin 權限過大,在生產(chǎn)環(huán)境中不太安全,下面用另一種方法來解決,在服務器中創(chuàng)建一個名位 namespace_reader.yaml 的文件,內(nèi)容如下:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-reader
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "list", "watch"]
使用下面命令執(zhí)行后就創(chuàng)建了一個名為 namespace-reader 的角色。
kubectl apply -f namespace_reader.yaml
角色創(chuàng)建成功后,就可以將 ServiceAccount 綁定到這個只讀角色了,命令如下:
kubectl create clusterrolebinding test-namespace-reader-binding \
--clusterrole=namespace-reader \
--serviceaccount=test:default
ServiceAccount 權限綁定后,再調(diào)用接口進行測試,會發(fā)現(xiàn)已經(jīng)可以正常獲取 namespace id 了。