성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - kubectl 명령어 또는 REST API 대신 Kubernetes 클라이언트 라이브러리를 통해 프로그래밍으로 접근</h1> <p> 전에 k8s 서비스와 REST API 통신을 하는 방법에 대해 알아봤는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Kubernetes - kube-apiserver와 REST API 통신하는 방법 (Docker Desktop for Windows 환경) ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12566'>https://www.sysnet.pe.kr/2/0/12566</a> </pre> <br /> 이미 고마우신 분들이 ^^ C# 전용 클라이언트를 만들어 두었기 때문에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > kubernetes-client/csharp ; <a target='tab' href='https://github.com/kubernetes-client/csharp'>https://github.com/kubernetes-client/csharp</a> </pre> <br /> 패키지 참조 추가로 간단하게,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // .NET 5 또는 .NET Standard 2.1 이상 Install-Package KubernetesClient // 물론 C# 뿐만 아니라 Go, Python, Java, JavaScript, Haskell 언어도 공식 클라이언트 라이브러리를 지원합니다. // // Access Clusters Using the Kubernetes API // ; <a target='tab' href='https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/'>https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/</a> </pre> <br /> 형식 안정성 있는 프로그램이 가능합니다. 예를 들어, <a target='tab' href='https://www.sysnet.pe.kr/2/0/12566#get_service_token'>"service account token" 값을 가져오는 kubectl 명령어</a>가 있었는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> <span style='color: blue; font-weight: bold'>kubectl get secrets</span> NAME TYPE DATA AGE <span style='color: blue; font-weight: bold'>default-token-qnwbl</span> kubernetes.io/service-account-token 3 50d c:\temp> <span style='color: blue; font-weight: bold'>kubectl describe secret default-token-qnwbl</span> Name: default-token-qnwbl Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name: default kubernetes.io/service-account.uid: 6a8f5d0c-db54-4018-b060-f311583546c0 Type: kubernetes.io/service-account-token Data ==== namespace: 7 bytes <span style='color: blue; font-weight: bold'>token: eyJhbGciOiJSUzI...[생략]...lzn1lGmAh1w</span> ca.crt: 1025 bytes </pre> <br /> 이런 정보를 KubernetesClient 패키지를 이용해 다음과 같이 조회할 수 있습니다.<br /> <br /> <pre style='height: 400px; margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using k8s; using System; using System.Text; namespace k8sutil { class Program { static void Main(string[] args) { string path = Environment.ExpandEnvironmentVariables(@"%USERPROFILE%\.kube\config"); KubernetesClientConfiguration config = KubernetesClientConfiguration.BuildConfigFromConfigFile(path); // 또는, KubernetesClientConfiguration config = KubernetesClientConfiguration.BuildDefaultConfig(); IKubernetes client = new Kubernetes(config); ListSecrets(client); Console.WriteLine(); /* 혹은, secrets의 이름과 네임스페이스를 알고 있다면 곧바로 쿼리 c:\temp> kubectl get secrets NAME TYPE DATA AGE default-token-qnwbl kubernetes.io/service-account-token 3 50d */ // var v1secret = client.ReadNamespacedSecret("default-token-qnwbl", "default"); string secretName = client.ListNamespacedSecret("default").Items[0].Metadata.Name; string namespaceProperty = "default"; var v1secret = client.ReadNamespacedSecret(secretName, namespaceProperty); foreach (var data in v1secret.Data) { Console.WriteLine($"\t{data.Key} == {data.Value.Length} byte(s)"); } } private static void ListSecrets(IKubernetes client) { var list = client.ListNamespacedSecret("default"); foreach (var item in list.Items) { Console.WriteLine($"{nameof(item.ApiVersion)}: {item.ApiVersion}"); // (null) Console.WriteLine($"{nameof(item.StringData)}: {item.StringData}"); // (null) Console.WriteLine($"{nameof(item.Type)}: {item.Type}"); // kubernetes.io/service-account-token Console.WriteLine($"{nameof(item.Kind)}: {item.Kind}"); // (null) Console.WriteLine($"{nameof(item.Immutable)}: {item.Immutable}"); // (null) Console.WriteLine(); Console.WriteLine("=== V1ObjectMetadata ==="); Console.WriteLine($"\t{nameof(item.Metadata.Name)}: {item.Metadata.Name}"); // default-token-qnwbl Console.WriteLine($"\t{nameof(item.Metadata.ClusterName)}: {item.Metadata.ClusterName}"); // (null) Console.WriteLine($"\t{nameof(item.Metadata.CreationTimestamp)}: {item.Metadata.CreationTimestamp}"); // 2021-01-25 오전 6:01:59 Console.WriteLine($"\t{nameof(item.Metadata.DeletionGracePeriodSeconds)}: {item.Metadata.DeletionGracePeriodSeconds}"); // (null) Console.WriteLine($"\t{nameof(item.Metadata.GenerateName)}: {item.Metadata.GenerateName}"); // (null) Console.WriteLine($"\t{nameof(item.Metadata.Generation)}: {item.Metadata.Generation}"); // (null) Console.WriteLine($"\t{nameof(item.Metadata.NamespaceProperty)}: {item.Metadata.NamespaceProperty}"); // default Console.WriteLine($"\t{nameof(item.Metadata.SelfLink)}: {item.Metadata.SelfLink}"); // /api/v1/namespaces/default/secrets/default-token-qnwbl Console.WriteLine($"\t{nameof(item.Metadata.Uid)}: {item.Metadata.Uid}"); // 6c584cda-d9a9-4b5c-8489-7e4365866014 Console.WriteLine(); Console.WriteLine($"=== Annotations ({item.Metadata.Annotations.Count}) ==="); foreach (var (k, v) in item.Metadata.Annotations) { Console.WriteLine($"\t{k} == {v}"); } Console.WriteLine(); Console.WriteLine($"=== Labels ({item.Metadata.Labels?.Count}) ==="); if (item.Metadata.Labels != null) { foreach (var (k, v) in item.Metadata.Labels) { Console.WriteLine($"\t{k} == {v}"); } } Console.WriteLine(); Console.WriteLine($"=== OwnerReferences ({item.Metadata.OwnerReferences?.Count}) ==="); if (item.Metadata.OwnerReferences != null) { foreach (var data in item.Metadata.OwnerReferences) { PrintOwnerReference(data); } } Console.WriteLine(); Console.WriteLine($"=== ManagedFields ({item.Metadata.ManagedFields?.Count}) ==="); if (item.Metadata.ManagedFields != null) { foreach (var data in item.Metadata.ManagedFields) { Console.WriteLine($"\t{nameof(data.ApiVersion)}: {data.ApiVersion}"); // v1 Console.WriteLine($"\t{nameof(data.FieldsType)}: {data.FieldsType}"); // FieldsV1 Console.WriteLine($"\t{nameof(data.FieldsV1)}: {data.FieldsV1}"); // ...json... Console.WriteLine($"\t{nameof(data.Operation)}: {data.Operation}"); // Update Console.WriteLine($"\t{nameof(data.Manager)}: {data.Manager}"); // Kube-controller-manager Console.WriteLine($"\t{nameof(data.Time)}: {data.Time}"); // v1 } } Console.WriteLine("=== Data ==="); /* ca.crt == byte [] namespace == byte [] ("default") token == byte [] */ foreach (var (k, v) in item.Data) { Console.WriteLine($"\t{k} == {PrintValue(v)}"); } } } private static void PrintOwnerReference(k8s.Models.V1OwnerReference data) { Console.WriteLine($"\t{nameof(data.ApiVersion)} == {data.ApiVersion}"); Console.WriteLine($"\t{nameof(data.BlockOwnerDeletion)} == {data.BlockOwnerDeletion}"); Console.WriteLine($"\t{nameof(data.Controller)} == {data.Controller}"); Console.WriteLine($"\t{nameof(data.Kind)} == {data.Kind}"); Console.WriteLine($"\t{nameof(data.Name)} == {data.Name}"); Console.WriteLine($"\t{nameof(data.Uid)} == {data.Uid}"); } private static string PrintValue(byte[] value) { return Encoding.UTF8.GetString(value); } } } </pre> <br /> 출력 결과를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ApiVersion: StringData: Type: kubernetes.io/service-account-token Kind: Immutable: === V1ObjectMetadata === Name: default-token-qnwbl ClusterName: CreationTimestamp: 2021-01-25 오전 6:01:59 DeletionGracePeriodSeconds: GenerateName: Generation: NamespaceProperty: default SelfLink: /api/v1/namespaces/default/secrets/default-token-qnwbl Uid: 6c584cda-d9a9-4b5c-8489-7e4365866014 === Annotations (2) === kubernetes.io/service-account.name == default kubernetes.io/service-account.uid == 6a8f5d0c-db54-4018-b060-f311583546c0 === Labels () === === OwnerReferences () === === ManagedFields (1) === ApiVersion: v1 FieldsType: FieldsV1 FieldsV1: { "f:data": { ".": {}, "f:ca.crt": {}, "f:namespace": {}, "f:token": {} }, "f:metadata": { "f:annotations": { ".": {}, "f:kubernetes.io/service-account.name": {}, "f:kubernetes.io/service-account.uid": {} } }, "f:type": {} } Operation: Update Manager: kube-controller-manager Time: 2021-01-25 오전 6:01:59 === Data === ca.crt == -----BEGIN CERTIFICATE----- MIICyDCCAbCgAwIBAgIBADANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwprdWJl AxMKa3Vi...[생략]...pGl6vpH/zMokqw 8FBcTBqYAlRJRiDWefzIy7tEPiSkOnLqtoVhBqL8WK6Xu15xzHJ5yO2yHTs= -----END CERTIFICATE----- namespace == default token == eyJhbGciOiJSUzI1NiIsImtpZCI6IkpaSndaVndMbTByU1NBNnFXZl80ekZibE9zMk5oRHJtYzZSN19aSDNUSEkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2Nv...[생략]...NHgPtK1Z14-fIbt-jouEHsGa4ZZB1J3Xagxy0A4N7GCuNS-5fvn65B1EqVaaUgfbdHtz8E6cb0ZsEtqRvlsIjQYXlHv_3HyXWyF7SINjPRLTlzn1lGmAh1w ca.crt == 1025 byte(s) namespace == 7 byte(s) token == 900 byte(s) </pre> <br /> 이로부터 "kubectl get secrets" 명령어의 출력 결과와 동일한 값들을 뽑아낼 수 있을 것입니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1742&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, Yaml을 다루는 패키지도 함께 소개합니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // aaubry/YamlDotNet // ; <a target='tab' href='https://github.com/aaubry/YamlDotNet'>https://github.com/aaubry/YamlDotNet</a> Install-Package YamlDotNet </pre> <br /> 자신이 정의한 yaml 규칙이 있다면 YamlDotNet을 이용해 I/O를 하는 것도 괜찮은 방법이겠습니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1480
(왼쪽의 숫자를 입력해야 합니다.)