Escrevendo mais uma ferramenta de modelagem do Kubernetes


Se vocĂȘ estiver trabalhando com o ambiente Kubernetes, provavelmente utilizarĂĄ vĂĄrias ferramentas de modelagem existentes, algumas delas fazendo parte de gerenciadores de pacotes, como Helm ou Ksonnet , ou apenas linguagens de modelagem (Jinja2, modelo Go etc.). Todos eles tĂȘm suas prĂłprias desvantagens, alĂ©m de vantagens, e vamos analisĂĄ-los e escrever nossa prĂłpria ferramenta que tentarĂĄ combinar os melhores recursos.


EntĂŁo, por que nĂŁo Helm?


Existem vårios artigos que criticam o Helm (por exemplo, apenas um deles: pense duas vezes antes de usar o Helm ). O principal problema do Helm é que ele funciona com representaçÔes de strings e os manifestos do Kubernetes são objetos (json) . O verdadeiro inferno para um desenvolvedor de gråficos Helm começa quando ele precisa calcular recuos para um manifesto yaml , às vezes parece com isso (é um exemplo real do meu gråfico):


 spec: jobTemplate: spec: template: spec: containers: - name: my-awesome-container resources: {{ toYaml .Values.resources | indent 14 }} 


Hoje, porĂ©m, o Helm Ă© de fato o padrĂŁo para o empacotamento de aplicativos Kubernetes. A principal vantagem do Helm Ă© uma grande comunidade e um grande nĂșmero de repositĂłrios pĂșblicos com grĂĄficos. E, recentemente, os desenvolvedores do Helm anunciaram um hub Helm . O Helm hoje Ă© como o Docker - nĂŁo Ă© o Ășnico, mas tem comunidade e suporte.


Existem mudanças promissoras no lançamento do Helm 3, mas ninguém sabe quando poderia ser.


Para concluir, as vantagens do Helm:


  • Grande comunidade e vĂĄrios grĂĄficos pĂșblicos
  • (Relativamente) sintaxe amigĂĄvel ao ser humano. Pelo menos Ă© o modelo yaml + go;)

Desvantagens:


  • Trabalhando com strings e nĂŁo objetos
  • NĂșmero limitado de operadores e funçÔes que vocĂȘ pode usar

OK, entĂŁo talvez Ksonnet?


Se vocĂȘ estĂĄ comparando o Helm ao Ksonnet, o Ășltimo possui uma enorme vantagem, a saber, funciona com objetos. O Ksonnet Ă© uma ferramenta baseada na linguagem de modelagem JSON Jsonnet. Outro recurso interessante sobre o Ksonnet Ă© que ele possui bibliotecas Jsonnet compatĂ­veis com a API do Kubernetes que vocĂȘ pode importar para o seu modelo e trabalhar com objetos do Kubernetes, como em qualquer linguagem OOP:


 local k = import "k.libsonnet"; local deployment = k.apps.v1beta1.deployment; local appDeployment = deployment .new( params.name, params.replicas, container .new(params.name, params.image) .withPorts(containerPort.new(targetPort)), labels); 

Parece impressionante, nĂŁo Ă©?
É um pouco menos interessante quando vocĂȘ trabalha nĂŁo com objetos API, mas apenas com objetos json importados do arquivo yaml / json :


 { global: {}, components: { "deployment-nginx-deployment-dkecx"+: { spec+: { replicas: 10, template+: { spec+: { containers+: [ { name: "nginx", image: "nginx:latest", ports: [ { containerPort: 80, }, ], }, ], }, }, }, }, }, } 

Mas ainda Ă© algo e Ă© melhor do que trabalhar com strings no Helm. A desvantagem do Ksonnet Ă© que ele possui uma comunidade menor e menos pacotes que o Helm (embora vocĂȘ possa importar grĂĄficos do Helm para o seu projeto Ksonnet, mas vocĂȘ estarĂĄ trabalhando com eles como objetos json, nĂŁo como objetos da biblioteca jsonnet). E, como resultado de uma comunidade e contribuição menores, faltam alguns recursos ao tentar escrever seu prĂłprio grĂĄfico. Um deles eu experimentei: vocĂȘ sabe que no Helm vocĂȘ pode criar um ConfigMap partir de um diretĂłrio que contĂ©m vĂĄrios arquivos de configuração desta maneira:


 apiVersion: v1 kind: ConfigMap metadata: name: conf data: {{- (.Files.Glob "foo/*").AsConfig | nindent 2 }} 

VocĂȘ pode imaginar minha frustração quando descobri que nĂŁo existe esse recurso no Ksonnet. Existem soluçÔes alternativas. Mas o ponto Ă© que Ă© apenas um exemplo da situação em que vocĂȘ estĂĄ escrevendo seu grĂĄfico com alegria e, de repente, a falta de algum recurso o impede no meio do caminho.
No total, vantagens do Ksonnet:


  • Trabalhando com objetos
  • Bibliotecas Jsonnet compatĂ­veis com API do Kubernetes
  • Suporte Ă  importação de grĂĄficos de leme

Desvantagens:


  • Comunidade menor e menor nĂșmero de pacotes nativos do Ksonnet
  • Falta de algumas funcionalidades que vocĂȘ pode usar no Helm
  • Nova sintaxe => aumento do tempo de aprendizado => aumento do fator de barramento
  • Às vezes, a sintaxe pode se tornar feia e menos legĂ­vel por humanos (especialmente quando vocĂȘ faz soluçÔes alternativas por falta de recursos)

Vamos pensar em uma ferramenta de modelagem ideal


Aqui estão alguns critérios para a ferramenta de modelagem "ideal":


  • Deve funcionar com objetos, nĂŁo com strings
  • Ele deve ter a capacidade de trabalhar com objetos compatĂ­veis com a API do Kubernetes
  • Deve ter um conjunto decente de funçÔes para trabalhar com strings
  • Deve funcionar bem com os formatos json e yaml
  • Deve ser amigĂĄvel ao ser humano
  • Deveria ser simples
  • Deve ter a capacidade de importar grĂĄficos Helm existentes (porque isso Ă© realidade e queremos fazer uso da comunidade Helm)

Isso é o suficiente por enquanto. Examinei esta lista na minha cabeça e pensei: tudo bem, por que não tentar o Python? Vamos ver se ele se encaixa nos nossos critérios:


  • Trabalhe com objetos, nĂŁo cordas . Sim, podemos usar tipos de dict e list para isso.
  • Possibilidade de trabalhar com objetos compatĂ­veis com a API do Kubernetes . Sim, from kubernetes import client
  • Tenha um conjunto decente de funçÔes para trabalhar com strings . Muito!
  • Trabalhe bem com os formatos json e yaml . Muito bem.
  • Amigo do Homem NĂŁo brinca.
  • Simples Sim
  • Capacidade de importar grĂĄficos Helm existentes . Vamos nos adicionar.

Ok, parece promissor. Decidi escrever uma ferramenta simples de modelagem no topo da biblioteca cliente oficial do Python para o kubernetes e agora deixe-me mostrar o que saiu dela.


Conheça karavel


Não hå nada de especial ou complicado nessa ferramenta. Acabei de pegar a biblioteca Kubernetes (que me permitiu trabalhar com objetos Kubernetes) e escrevi algumas funcionalidades båsicas para gråficos Helm existentes (para que alguém pudesse buscå-los e adicionå-los ao seu próprio gråfico). Então, vamos fazer um tour.
Primeiro, esta ferramenta estĂĄ acessĂ­vel no repositĂłrio do Github e vocĂȘ pode encontrar um diretĂłrio com exemplos lĂĄ.


InĂ­cio rĂĄpido com a imagem do Docker


Se vocĂȘ quiser experimentar, a maneira mais simples Ă© usar esta imagem do docker :


 $ docker run greegorey/karavel -h usage: karavelcli.py [-h] subcommand ... optional arguments: -h, --help show this help message and exit list of subcommands: subcommand template generates manifests from template ensure ensure helm dependencies 

Obviamente, se vocĂȘ deseja modelar grĂĄficos, precisarĂĄ montar o diretĂłrio do seu grĂĄfico:


 $ cd example $ docker run -v $PWD:/chart greegorey/karavel template . 

Então, vamos dar uma olhada na estrutura do gráfico. É muito semelhante a um dos Helm:


 $ cd example $ tree . . ├── dependencies ├── prod.yaml ├── requirements.yaml ├── templates │  ├── custom-resource.py │  ├── deployment.py │  └── service-helm.py └── values.yaml 2 directories, 6 files 

Como o Helm, ele possui o arquivo requirements.yaml com o mesmo layout:


 dependencies: - name: mysql version: 0.13.1 repository: https://kubernetes-charts.storage.googleapis.com/ 

Aqui, vocĂȘ apenas lista as dependĂȘncias do Helm que deseja importar para o seu grĂĄfico. As dependĂȘncias vĂŁo para o diretĂłrio de dependencies . Para buscĂĄ-los ou atualizĂĄ-los, use o comando ensure :


 $ karavel ensure . 

Depois disso, o diretĂłrio de dependencies ficarĂĄ assim:


 $ tree dependencies dependencies └── mysql-0.13.1 └── mysql ├── Chart.yaml ├── README.md ├── templates │  ├── NOTES.txt │  ├── _helpers.tpl │  ├── configurationFiles-configmap.yaml │  ├── deployment.yaml │  ├── initializationFiles-configmap.yaml │  ├── pvc.yaml │  ├── secrets.yaml │  ├── svc.yaml │  └── tests │  ├── test-configmap.yaml │  └── test.yaml └── values.yaml 4 directories, 13 files 

Agora, depois de garantirmos nossas dependĂȘncias, vamos dar uma olhada nos modelos. Primeiro, criamos uma implantação simples do nginx:


 from kubernetes import client from karavel.helpers import Values def template(): values = Values().values # Configure Pod template container container = client.V1Container( name='nginx', image='{}:{}'.format(values.nginx.image.repository, values.nginx.image.tag), ports=[client.V1ContainerPort(container_port=80)]) # Create and configurate a spec section template = client.V1PodTemplateSpec( metadata=client.V1ObjectMeta(labels={'app': 'nginx'}), spec=client.V1PodSpec(containers=[container])) # Create the specification of deployment spec = client.ExtensionsV1beta1DeploymentSpec( replicas=3, template=template) # Instantiate the deployment object deployment = client.ExtensionsV1beta1Deployment( api_version='extensions/v1beta1', kind='Deployment', metadata=client.V1ObjectMeta(name='nginx-deployment'), spec=spec) return deployment # [deployment], (deployment, deployment) are valid 

Portanto, para que o modelo seja vĂĄlido, vocĂȘ precisa ter a função template() que retorna um Ășnico objeto Kubernetes ou uma list / tuple deles. VocĂȘ pode encontrar a lista de objetos de API para o cliente Python aqui .
Como vocĂȘ pode ver, o cĂłdigo Ă© limpo, simples, legĂ­vel. VocĂȘ pode se perguntar de onde vem o values.nginx.image.repository ? Ele obtĂ©m valores dos arquivos de valores que vocĂȘ passa ao modelar o grĂĄfico, assim como no Helm: karavel template -f one.yaml --values two.yaml . Vamos dar uma olhada neles mais tarde.


Ok, e os grĂĄficos Helm?


Agora, criamos nossa própria implantação. Mas e se quisermos importar o gråfico Helm ou parte dele? Vamos dar uma olhada em templates/service-helm.py :


 from kubernetes import client from karavel.helm import HelmChart from karavel.helpers import Values def template(): values = Values().values # Initialize the chart (== helm template --values) chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm) # Get the desired object from chart service = chart.get(name='svc', obj_class=client.V1Service) # Create custom objects to add custom_ports = [ client.V1ServicePort( name='my-custom-port', protocol=values.mysql.protocol, port=values.mysql.port, target_port=39000, ) ] # Add custom objects to the service service.spec['ports'] = custom_ports # Change Helm-generated label service.metadata['labels']['release'] += '-suffix' # Delete Helm-generated label `heritage: Tiller` del service.metadata['labels']['heritage'] return service # [service], (service, service) are valid 

Simples, nĂ©? Observe esta linha: service = chart.get(name='svc', obj_class=client.V1Service) - criamos o objeto da classe yaml arquivo Helm yaml . Se vocĂȘ nĂŁo quiser / precisar fazer isso - sempre poderĂĄ trabalhar com apenas dict .


E se eu quiser criar um recurso personalizado?


Bem, hĂĄ um pequeno problema com isso. A API do Kubernetes nĂŁo adiciona objetos CRD Ă  definição swagger json em /openapi/v2 , e os objetos cliente Python sĂŁo /openapi/v2 base nessa definição. Mas vocĂȘ ainda pode trabalhar facilmente com objetos de dict . Assim:


 from kubernetes import client def template(): resource = { 'apiVersion': 'stable.example.com/v1', 'kind': 'Whale', 'metadata': client.V1ObjectMeta( name='my-object', ), 'spec': { 'image': 'my-whale-image:0.0.1', 'tail': 1, 'fins': 4, } } return resource # [resource], (resource, resource) are valid 

Ainda parece bom, nĂŁo Ă©?


Posso ter valores para ambientes diferentes, por exemplo, dev / prod?


Sim vocĂȘ pode!
Vamos dar uma olhada em values.yaml primeiro:


 nginx: image: repository: nginx tag: 1.15-alpine mysql: port: 3307 protocol: TCP helm: releaseName: my-release namespace: prod imageTag: '5.7.14' service: type: NodePort 

Observe a chave helm no dict do mysql : usamos quando especificamos valores para o gråfico do helm chart chart = HelmChart(name='mysql', version='0.13.1', values=values.mysql.helm) . Alguns gråficos Helm precisam de releaseName para nomeação de aplicativos e namespace para políticas RBAC. Esses dois valores são transmitidos ao Helm como argumentos --namespace e NAME no helm template .


Agora, vocĂȘ pode especificar um arquivo adicional para prod env e modelar todos os nossos exemplos:


 $ karavel template -f values.yaml -f prod.yaml . --- # Source: templates/custom-resource.py apiVersion: stable.example.com/v1 kind: Whale metadata: name: my-object spec: fins: 4 image: my-whale-image:0.0.1 tail: 1 --- # Source: templates/deployment.py apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx-deployment spec: replicas: 3 template: metadata: labels: app: nginx spec: containers: - image: nginx:1.14-alpine name: nginx ports: - containerPort: 80 --- # Source: templates/service-helm.py apiVersion: v1 kind: Service metadata: annotations: null labels: app: prod-release-mysql chart: mysql-0.13.1 release: prod-release-suffix name: prod-release-mysql spec: ports: - name: my-custom-port port: 3308 protocol: TCP targetPort: 39000 selector: app: prod-release-mysql type: NodePort 

Depois disso, vocĂȘ pode kubeclt apply e implantar esses objetos no cluster.


Legal! E quanto a codificação e base64?


import base64


Que tal usar o Vault para segredos?


import hvac


Buscando URLs?


import importlib


FunçÔes hash seguras?


import Crypto


VocĂȘ entendeu. Com o Python, vocĂȘ pode fazer muitas coisas com seus manifestos do Kubernetes.


É síndrome do NIH?


NĂŁo :)
Eu sou alegremente usando Helm em meus projetos atuais. Mas hå coisas que sinto falta. Também usei o Ksonnet em alguns dos meus projetos.
Gostaria de pensar nessa ferramenta como uma prova de conceito de que podemos ter ferramentas de modelagem melhores que o Helm e nĂŁo Ă© muito difĂ­cil desenvolvĂȘ-las usando o Python. Se houver um interesse / necessidade da comunidade em tal ferramenta, podemos juntos continuar a desenvolvĂȘ-la. Ou podemos esperar pelo lançamento do Helm 3;)


ConclusĂŁo


Eu mostrei a vocĂȘ a ferramenta de modelagem baseada em Python para o Kubernetes, que possui suporte a objetos compatĂ­veis com a API do Kubernetes e suporta a importação de grĂĄficos Helm. Quaisquer comentĂĄrios e discussĂ”es da comunidade sĂŁo bem-vindos e novamente bem-vindos ao repositĂłrio .


Obrigado por ler isso e tenha um bom dia!


ReferĂȘncias


Source: https://habr.com/ru/post/pt437682/


All Articles