A tradução do artigo foi preparada em antecipação ao início do curso .

Se você está lendo isso, provavelmente já ouviu falar do Kubernetes (e se não, como você chegou aqui?). Mas o que realmente é o Kubernetes? É ? Или ? Что вообще это значит?
Sinceramente, não tenho 100% de certeza. Mas acho interessante mergulhar nas entrelinhas e ver o que realmente acontece no Kubernetes por trás de suas muitas camadas de abstração. Então, por curiosidade, vamos dar uma olhada em como realmente é o mínimo “cluster Kubernetes”. (Isto será muito mais simples do que .)
Suponho que você tenha um conhecimento básico de Kubernetes, Linux e contêineres. Tudo o que discutiremos aqui é destinado apenas para pesquisa/estudo, não execute nada disso em produção!
Visão geral
O Kubernetes contém muitos componentes. De acordo com , a arquitetura é a seguinte:

Aqui estão pelo menos oito componentes mostrados, mas a maioria deles iremos ignorar. Quero afirmar que a menor coisa que pode ser razoavelmente chamada de Kubernetes consiste em três componentes principais:
- kubelet
- kube-apiserver (que depende do etcd — seu banco de dados)
- ambiente de execução de contêiner (neste caso, Docker)
Vamos ver o que cada um deles diz na documentação (., …). Primeiro kubelet:
Um agente que roda em cada nó no cluster. Ele garante que os contêineres sejam executados em um pod.
Parece bastante simples. E quanto ao ambiente de execução de contêiner (container runtime)?
O ambiente de execução de contêiner é um programa destinado a executar contêineres.
Muito informativo. Mas se você está familiarizado com o Docker, deve ter uma ideia geral do que ele faz. (Os detalhes da separação de responsabilidades entre o ambiente de execução de contêineres e o kubelet são na verdade bem sutis e aqui não vou me aprofundar neles.)
eu posso usar API-server?
O servidor API é um componente da interface de controle do Kubernetes, que representa a API do Kubernetes. O servidor API é a parte cliente da interface de controle do Kubernetes.
Qualquer um que já tenha feito alguma coisa com Kubernetes teve que interagir com a API, seja diretamente ou através do kubectl. Este é o coração do que faz o Kubernetes ser o Kubernetes — o cérebro que transforma montanhas de YAML, que todos nós conhecemos e amamos (?), em infraestrutura funcional. Parece óbvio que a API deve estar presente em nossa configuração mínima.
Condições preliminares
- Uma máquina virtual ou física Linux com acesso root (estou usando o Ubuntu 18.04 em uma máquina virtual).
- E isso é tudo!
Instalação chata
No sistema que vamos usar, é necessário instalar o Docker. (Não vou explicar em detalhes como o Docker e os containers funcionam; se você estiver interessado, existem ). Vamos apenas instalá-lo com apt:
$ sudo apt install docker.io
$ sudo systemctl start docker Depois disso, precisamos obter os binários do Kubernetes. Na verdade, para iniciar nosso “cluster”, precisamos apenas de kubelet, pois para executar os outros componentes do servidor poderemos usar kubelet. Para interagir com nosso cluster uma vez que ele esteja funcionando, também usaremos kubectl.
$ curl -L https://dl.k8s.io/v1.18.5/kubernetes-server-linux-amd64.tar.gz > server.tar.gz
$ tar xzvf server.tar.gz
$ cp kubernetes/server/bin/kubelet .
$ cp kubernetes/server/bin/kubectl .
$ ./kubelet --version
Kubernetes v1.18.5 O que acontece se simplesmente executarmos kubelet?
$ ./kubelet
F0609 04:03:29.105194 4583 server.go:254] mkdir /var/lib/kubelet: permission denied kubelet deve ser executado como root. Faz sentido, já que ele precisa gerenciar todo o nó. Vamos dar uma olhada nas suas opções:
$ ./kubelet -h
$ ./kubelet -h | wc -l
284Uau, quanta opção! Felizmente, só precisaremos de algumas delas. Aqui está um dos parâmetros que nos interessa:
--pod-manifest-path stringCaminho para o diretório contendo arquivos para pods estáticos, ou o caminho para um arquivo com a descrição dos pods estáticos. Arquivos que começam com pontos são ignorados. (OBSOLETE: este parâmetro deve ser configurado no arquivo de configuração passado para o Kubelet através da opção --config. Para mais informações, veja .)
Este parâmetro nos permite executar — pods que não são gerenciados através da API do Kubernetes. Pods estáticos são usados raramente, mas são muito convenientes para rapidamente levantar um cluster, e é exatamente isso que precisamos. Vamos ignorar esse aviso intenso (novamente, não execute isso em produção!) e ver se conseguimos iniciar um pod.
Primeiro, vamos criar um diretório para os pods estáticos e iniciar kubelet:
$ mkdir pods
$ sudo ./kubelet --pod-manifest-path=podsEntão, em outro terminal/janela tmux/em outro lugar, criaremos o manifesto do pod:
$ cat < pods/hello.yaml
apiVersion: v1
kind: Pod
metadata:
name: hello
spec:
containers:
- image: busybox
name: hello
command: ["echo", "hello world!"]
EOF kubelet Começa a escrever alguns avisos e parece que nada está acontecendo. Mas não é assim! Vamos dar uma olhada no Docker:
$ sudo docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c8a35e26663 busybox "echo 'hello world!'" há 36 segundos Encerrado (0) há 36 segundos k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
68f670c3c85f k8s.gcr.io/pause:3.2 "pause" há 2 minutos Em execução há 2 minutos k8s_POD_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_0
$ sudo docker logs k8s_hello_hello-mink8s_default_ab61ef0307c6e0dee2ab05dc1ff94812_4
hello world! kubelet Lemos o manifesto do pod e dissemos ao Docker para iniciar alguns contêineres de acordo com nossa especificação. (Se você estiver interessado em saber sobre o contêiner “pause”, isso é uma manobra do Kubernetes — mais detalhes podem ser vistos em .) O Kubelet iniciará nosso contêiner busybox com o comando especificado e o reiniciará indefinidamente até que o pod estático seja removido.
Parabéns! Acabamos de inventar uma das maneiras mais complicadas de imprimir texto no terminal!
Iniciando etcd
Nosso objetivo final é iniciar a API do Kubernetes, mas para isso primeiro precisamos iniciar . Vamos iniciar um cluster etcd mínimo, colocando suas configurações na pasta pods (por exemplo, pods/etcd.yaml):
apiVersion: v1
kind: Pod
metadata:
name: etcd
namespace: kube-system
spec:
containers:
- name: etcd
command:
- etcd
- --data-dir=/var/lib/etcd
image: k8s.gcr.io/etcd:3.4.3-0
volumeMounts:
- mountPath: /var/lib/etcd
name: etcd-data
hostNetwork: true
volumes:
- hostPath:
path: /var/lib/etcd
type: DirectoryOrCreate
name: etcd-dataSe você já trabalhou com Kubernetes, esses arquivos YAML devem ser familiares para você. Aqui vale a pena destacar apenas dois pontos:
Montamos a pasta do host /var/lib/etcd no pod para que os dados do etcd sejam mantidos após a reinicialização (se não fizer isso, o estado do cluster será apagado a cada reinicialização do pod, o que não é desejado mesmo para uma instalação mínima do Kubernetes).
Configuramos hostNetwork: true. Este parâmetro, como não poderia deixar de ser, configura o etcd para usar a rede do host em vez da rede interna do pod (o que facilitará para o servidor API encontrar o cluster etcd).
Uma simples verificação mostra que o etcd realmente está rodando no localhost e salvando dados no disco:
$ curl localhost:2379/version
{"etcdserver":"3.4.3","etcdcluster":"3.4.0"}
$ sudo tree /var/lib/etcd/
/var/lib/etcd/
└── member
├── snap
│ └── db
└── wal
├── 0.tmp
└── 0000000000000000-0000000000000000.walIniciando o servidor API
Iniciar um servidor de API Kubernetes é ainda mais simples. O único parâmetro que deve ser passado, --etcd-servers, faz o que você espera:
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
containers:
- name: kube-apiserver
command:
- kube-apiserver
- --etcd-servers=http://127.0.0.1:2379
image: k8s.gcr.io/kube-apiserver:v1.18.5
hostNetwork: true Coloque este arquivo YAML no diretório pods, e o servidor API será iniciado. A verificação com curl mostra que o API Kubernetes está escutando na porta 8080 com acesso total — autenticação não é necessária!
$ curl localhost:8080/healthz
ok
$ curl localhost:8080/api/v1/pods
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"selfLink": "api/v1/pods",
"resourceVersion": "59"
},
"items": []
}(Novamente, não execute isso em produção! Fiquei um pouco surpreso que a configuração padrão seja tão insegura. Mas imagino que isso seja feito para facilitar o desenvolvimento e os testes.)
E, uma grata surpresa, kubectl funciona imediatamente sem qualquer configuração adicional!
$ ./kubectl version
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:47:41Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.5", GitCommit:"e6503f8d8f769ace2f338794c914a96fc335df0f", GitTreeState:"clean", BuildDate:"2020-06-26T03:39:24Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
$ ./kubectl get pod
No resources found in default namespace.O problema
Mas se você aprofundar um pouco mais, parece que algo não está certo:
$ ./kubectl get pod -n kube-system
No resources found in kube-system namespace.Os pods estáticos que criamos desapareceram! Na verdade, nosso nó kubelet não é detectado:
$ ./kubectl get nodes
No resources found in default namespace.Qual é o problema? Se você se lembrar, há alguns parágrafos atrás, nós iniciamos o kubelet com um conjunto extremamente simples de parâmetros de linha de comando, por isso o kubelet não sabe como se conectar ao servidor API e notificá-lo sobre seu estado. Ao examinar a documentação, encontramos a flag correspondente:
--kubeconfig string
Caminho para o arquivo kubeconfig, que especifica como se conectar ao servidor API. A presença de --kubeconfig ativa o modo do servidor API, enquanto a ausência de --kubeconfig ativa o modo autônomo.
Todo esse tempo, sem saber, tínhamos executado o kubelet em "modo autônomo". (Se fôssemos meticulosos, poderíamos considerar o modo autônomo do kubelet como um "Kubernetes minimamente viável", mas isso seria muito chato). Para que a configuração "real" funcione, precisamos fornecer o arquivo kubeconfig ao kubelet, para que ele saiba como se comunicar com o servidor API. Felizmente, isso é bastante simples (já que não temos problemas com autenticação ou certificados):
apiVersion: v1
kind: Config
clusters:
- cluster:
server: http://127.0.0.1:8080
name: mink8s
contexts:
- context:
cluster: mink8s
name: mink8s
current-context: mink8s Salve isso como kubeconfig.yaml, mate o processo kubelet e reinicie com os parâmetros necessários:
$ sudo ./kubelet --pod-manifest-path=pods --kubeconfig=kubeconfig.yaml(A propósito, se você tentar acessar a API via curl quando o kubelet não estiver funcionando, descobrirá que ele ainda está ativo! O kubelet não é o "pai" de seus pods, como o Docker, ele se assemelha mais a um "demônio controlador". Os contêineres gerenciados pelo kubelet continuarão funcionando até que o kubelet os pare.)
Daqui a alguns minutos kubectl deve nos mostrar os pods e nós, como esperávamos:
$ ./kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
default hello-mink8s 0/1 CrashLoopBackOff 261 21h
kube-system etcd-mink8s 1/1 Running 0 21h
kube-system kube-apiserver-mink8s 1/1 Running 0 21h
$ ./kubectl get nodes -owide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
mink8s Ready 21h v1.18.5 10.70.10.228 Ubuntu 18.04.4 LTS 4.15.0-109-generic docker://19.3.6Vamos realmente nos parabenizar desta vez (eu sei que já fiz isso) — temos um "cluster" Kubernetes mínimo funcionando com uma API funcional!
Iniciando o pod
Agora vamos ver do que a API é capaz. Comecemos com o pod nginx:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginxAqui teremos um erro bastante interessante:
$ ./kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": pods "nginx" is
forbidden: error looking up service account default/default: serviceaccount
"default" not found
$ ./kubectl get serviceaccounts
No resources found in default namespace.Aqui vemos o quão horrivelmente incompleta está nossa infraestrutura Kubernetes — não temos contas de serviço. Vamos tentar novamente, criando uma conta de serviço manualmente, e ver o que acontece:
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
EOS
serviceaccount/default created
$ ./kubectl apply -f nginx.yaml
Error from server (ServerTimeout): error when creating "nginx.yaml": No API
token found for service account "default", retry after the token is
automatically created and added to the service accountMesmo quando criamos manualmente uma conta de serviço, o token de autenticação não é criado. Ao continuar experimentando com nosso 'cluster' minimalista, descobrimos que muitas coisas úteis que normalmente acontecem automaticamente estarão ausentes. O servidor API do Kubernetes é bastante minimalista, a maior parte das pesadas configurações automáticas acontece em vários controladores e tarefas em segundo plano que ainda não estão sendo executadas.
Podemos contornar esse problema definindo a opção automountServiceAccountToken para a conta de serviço (já que não precisamos utilizá-la):
$ cat <<EOS | ./kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: default
namespace: default
automountServiceAccountToken: false
EOS
serviceaccount/default configured
$ ./kubectl apply -f nginx.yaml
pod/nginx created
$ ./kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 0/1 Pending 0 13mFinalmente, o pod apareceu! Mas na verdade ele não vai iniciar, pois não temos (scheduler) — outro componente importante do Kubernetes. Novamente, vemos que a API do Kubernetes é surpreendentemente 'burra' — quando você cria um pod na API, ele o registra, mas não tenta descobrir em qual nó executá-lo.
Na verdade, para executar um pod, o agendador não é necessário. Você pode adicionar manualmente um nó no manifesto no parâmetro nodeName:
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
nodeName: mink8s
(Substitua mink8s pelo nome do nó.) Após delete e apply, vemos que o nginx iniciou e está ouvindo no endereço IP interno:
$ ./kubectl delete pod nginx
pod "nginx" deletado
$ ./kubectl apply -f nginx.yaml
pod/nginx criado
$ ./kubectl get pods -owide
NOME PRONTO STATUS REINICIAÇÕES IDADE IP NÓ NÓ NOMINADO PORTÕES DE PRONTIDÃO
nginx 1/1 Em Execução 0 30s 172.17.0.2 mink8s <none> <none>
$ curl -s 172.17.0.2 | head -4
<!DOCTYPE html>
<html>
<head>
<title>Bem-vindo ao nginx!</title>Para garantir que a rede entre os pods está funcionando corretamente, podemos executar curl de outro pod:
$ gato <<EOS | .\/kubectl apply -f -
apiversion: v1
kind: Pod
metadata:
name: curl
spec:
containers:
- image: curlimages\/curl
name: curl
command: ["curl", "172.17.0.2"]
nodeName: mink8s
EOS
pod\/curl criado
$ .\/kubectl logs curl | head -6
% Total % Recebido % Transferido Velocidade Média Tempo Tempo Tempo Atual
Baixar Enviar Total Gasto Restante Velocidade
<!DOCTYPE html>
<html>
<head>
<title>Bem-vindo ao nginx!</title>É bastante interessante explorar esse ambiente e ver o que funciona e o que não funciona. Descobri que ConfigMap e Secret funcionam como esperado, mas Service e Deployment não.
Sucesso!
Este post está crescendo, então vou declarar uma vitória e afirmar que essa é uma configuração viável que pode ser chamada de "Kubernetes". Resumindo: quatro arquivos binários, cinco parâmetros de linha de comando e "apenas" 45 linhas de YAML (não é muito pelos padrões do Kubernetes) e temos várias coisas funcionando:
- Os pods são gerenciados por meio da API padrão do Kubernetes (com algumas adaptações)
- É possível carregar imagens de contêiner públicas e gerenciá-las
- Os pods permanecem ativos e reiniciam automaticamente
- A rede entre os pods em um único nó funciona muito bem
- ConfigMap, Secret e montagem simples de volumes funcionam corretamente
Mas a maior parte do que torna o Kubernetes realmente útil ainda está faltando, como:
- Agendador de pods
- Autenticação / autorização
- Múltiplos nós
- Rede de serviços
- DNS interno do cluster
- Controladores para contas de serviço, implantações, integração com provedores de nuvem e a maioria das outras 'ferramentas' que o Kubernetes oferece
Então, o que realmente temos? A API do Kubernetes, por si só, é na verdade apenas uma plataforma para automação de contêineres. Ela não faz muito – essa é a função de vários controladores e operadores que utilizam a API – mas fornece um ambiente consistente para automação.
Leia mais:
Fonte: habr.com
