Review
A ideia aqui é somente abordar um review dos diferentes recursos do Kubernetes.
Pods
São a menor unidade do Kubernetes. Um pod não deve rodar mais de uma instância do mesmo container. É possível um pod ter mais de um container, mas geralmente são sidecars que apoiam o container principal. Não se escala uma aplicação criando mais de um container da aplicação dentro do mesmo pod.
Esses containers podem se comunicar diretamente (localhost) já que estão compartilhando o mesmo espaço de rede. Os containers em um pod também compartilham o mesmo espaço de armazenamento.
kubectl run nginx --image nginx
pod/nginx created
kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 41s
kubectl describe pod nginx
Name: nginx
Namespace: default
Priority: 0
Service Account: default
Node: k3d-k3d-cluster-agent-1/172.18.0.4
Start Time: Sat, 23 Dec 2023 10:21:05 -0300
Labels: run=nginx
Annotations: <none>
Status: Running
IP: 10.42.1.25
IPs:
IP: 10.42.1.25
Containers:
nginx:
Container ID: containerd://70ebdd2cc5ebf9ce43d69d323a17c8db53022221d2bc813b4b2f3838d30b6d9d
Image: nginx
Image ID: docker.io/library/nginx@sha256:2bdc49f2f8ae8d8dc50ed00f2ee56d00385c6f8bc8a8b320d0a294d9e3b49026
Port: <none>
Host Port: <none>
State: Running
Started: Sat, 23 Dec 2023 10:21:13 -0300
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-rswr7 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-rswr7:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 75s default-scheduler Successfully assigned default/nginx to k3d-k3d-cluster-agent-1
Normal Pulling 76s kubelet Pulling image "nginx"
Normal Pulled 68s kubelet Successfully pulled image "nginx" in 7.355673083s (7.355680823s including waiting)
Normal Created 68s kubelet Created container nginx
Normal Started 68s kubelet Started container nginx
Criando com um arquivo podemos executar com um apply.
kubectl apply -f pod.yaml
#OR
kubectl create -f pod.yaml
Qualquer manifesto yaml no Kubernetes possui as chaves abaixo.
apiVersion:
kind:
metadata:
spec:
Para pod o kind é Pod
.
Crie um pod com esse arquivo. kubectl apply -f pod.yaml
.
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
- name: redis
image: redis
Alguns comando iniciais essenciais.
kubectl get nodes
kubectl get pods
kubectl get pods -o wide
kubectl get pods -n default
kubectl describe pods nginx -n default
kubectl edit pods pod_name
kubectl get pods -o wide
kubectl run nginx-1 --image nginx
kubectl run nginx-2 --image nginx --dry-run=client -o yaml
kubectl delete pods nginx nginx-1
Replica Set
Este é um controlador especial que assegura que uma quantidade idêntica do mesmo pod esteja em execução no cluster, garantindo alta disponibilidade. Ele supervisiona para garantir que, se um pod parar, ele seja automaticamente reiniciado para atender à quantidade desejada. Lançar pods isolados, sem um ReplicaSet, não garante que eles serão reiniciados automaticamente em caso de falha.
Os pods podem se estender por múltiplos nodes para balancear a carga entre os nodes.
ReplicationController é o kind antigo, o novo a ser usado é o ReplicaSet
Observe que precisamos do selector para que o ReplicaSet saiba qual pod ele irá controlar e monitorar. O selector é necessário pois é possível criar um pod anteriormente ao ReplicaSet e depois criar um ReplicaSet para esse pod sem defini-lo no template.
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: myapp-rs
labels:
app: myapp
spec:
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
selector:
matchLabels:
app: nginx
replicas: 3
kubectl get replicaset
NAME DESIRED CURRENT READY AGE
myapp-rs 3 3 3 29s
## rs = replicaset
kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-rs 3 3 3 39s
kubectl describe rs myapp-rs
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h12m
myapp-rs-7l8z6 1/1 Running 0 4h12m
myapp-rs-z8vzw 1/1 Running 0 4h12m
Name: myapp-rs
Namespace: default
Selector: app=nginx
Labels: app=myapp
Annotations: <none>
Replicas: 3 current / 3 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Events: <none>
Observe que o ReplicaSet criou os pods e adicionou uns números alguns caracteres depois.
Um detalhe importante é que o template sempre é necessário. Caso os pods definidos no selector já existirem ele não irá criar, mas se algum deles falhar e o ReplicaSet precisar lançar um novo é no seu próprio template que ele irá verificar os requisitos para lançar um novo pod para substituir o que está faltando.
Para escalar mais replicas temos alguns métodos.
kubectl scale --replicas 6 rs myapp-rs
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h25m
myapp-rs-7l8z6 1/1 Running 0 4h25m
myapp-rs-z8vzw 1/1 Running 0 4h25m
myapp-rs-jwxf5 1/1 Running 0 47s
myapp-rs-79hps 1/1 Running 0 47s
myapp-rs-8qp6p 1/1 Running 0 47s
#outro métodos usando o arquivo
kubectl scale --replicas 6 -f ./files/replicaset.yaml
replicaset.apps/myapp-rs scaled
#nesse método se tivéssemos editado o arquivo replicaset.yaml com 6 ele iria substituir, porém ele acabou voltando pois estava com 3
kubectl replace -f ./files/replicaset.yaml
replicaset.apps/myapp-rs replaced
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h27m
myapp-rs-7l8z6 1/1 Running 0 4h27m
myapp-rs-z8vzw 1/1 Running 0 4h27m
# delete todos os pods e veja o que acontece
kubectl delete pods --selector app=nginx
pod "myapp-rs-w7fwh" deleted
pod "myapp-rs-thfbw" deleted
pod "myapp-rs-szr6m" deleted
# veja que recriou novamente com 4s
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-5j77g 1/1 Running 0 4s
myapp-rs-hm9nz 1/1 Running 0 4s
myapp-rs-dmh29 1/1 Running 0 4s
#ou usando o comando edit.. editei de 3 para 6 novamente
kubectl edit replicasets myapp-rs
replicaset.apps/myapp-rs edited
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-prnh7 1/1 Running 0 4h30m
myapp-rs-7l8z6 1/1 Running 0 4h30m
myapp-rs-z8vzw 1/1 Running 0 4h30m
myapp-rs-b9bzj 1/1 Running 0 8s
myapp-rs-2x426 1/1 Running 0 8s
myapp-rs-l2jvq 1/1 Running 0 8s
kubectl delete rs myapp-rs
replicaset.apps "myapp-rs" deleted
kubectl get pods
No resources found in default namespace.
kubectl create -f ./files/replicaset.yaml
replicaset.apps/myapp-rs created
kubectl get pods
NAME READY STATUS RESTARTS AGE
myapp-rs-crfns 1/1 Running 0 12s
myapp-rs-9pqhh 1/1 Running 0 12s
myapp-rs-p4bks 1/1 Running 0 12s
kubectl delete -f ./files/replicaset.yaml
replicaset.apps "myapp-rs" deleted
kubectl get pods
No resources found in default namespace.
Um comando para verificar sobre os manifestos é o kubectl explain replicaset
.
Aplique um replicaset para um pod criado anteriormente como como estudo. Não existe um comando kubectl create replicaset.
DaemonSet
São idênticos ao ReplicaSet, mas não é necessário passar o número de réplicas pois é um único pod por node. Isso é um tipo especial muito usado para coleta de logs, monitoramento do node e rede.
Por exemplo, o Kube-Proxy seria um ótimo exemplo.
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: myapp-ds
labels:
app: myapp
spec:
template:
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
selector:
matchLabels:
app: nginx
Deployments
Eles são capazes de executar rolling updates, onde os pods são substituídos um por um em vez de serem todos destruídos e substituídos de uma vez. Isso garante que sua aplicação permaneça operacional durante a atualização para uma nova imagem, por exemplo. Se a atualização falhar, é possível reverter para a versão anterior por meio de um rollback. Além disso, os Deployments gerenciam diferentes versões do ReplicaSet, proporcionando uma maneira eficiente de controlar as alterações na infraestrutura.
Observe que somente trocando o kind ReplicaSet por Deployment é o necessário para que tudo funcione podendo manter a mesma estrutura.
deployment.apps/myapp created
kubectl get all
NAME READY STATUS RESTARTS AGE
pod/myapp-98d49bcf5-s6kmm 1/1 Running 0 4s
pod/myapp-98d49bcf5-tjm4j 1/1 Running 0 4s
pod/myapp-98d49bcf5-pzjsm 1/1 Running 0 4s
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/myapp 3/3 3 3 4s
NAME DESIRED CURRENT READY AGE
replicaset.apps/myapp-98d49bcf5 3 3 3 4s
kubectl describe deployment myapp
Name: myapp
Namespace: default
CreationTimestamp: Sun, 24 Dec 2023 16:39:40 -0300
Labels: app=myapp
Annotations: deployment.kubernetes.io/revision: 1
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge # VEJA ISSO
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: myapp-98d49bcf5 (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 2m39s deployment-controller Scaled up replica set myapp-98d49bcf5 to 3
É importante observar que o Deployment cria tanto o ReplicaSet quanto os pods. Além disso, o objeto Deployment inclui uma chave que define a tolerância a falhas dos pods durante uma atualização, bem como a estratégia utilizada para realizar essa atualização.
Dica: O comando abaixo cria uma manifesto no console e se preferir poderá redirecionar para um arquivo.
kubectl create deployment --image=nginx nginx --dry-run=client -o yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
status: {}
kubectl create deployment --image=nginx nginx --replicas 5 --dry-run=client -o yaml > deployment-dry-run.yaml
cat deployment-dry-run.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 5
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
status: {}
Um Deployment não pode ser modelado como um DaemonSet, ou seja, sempre precisa ter um número de réplicas, nem que seja 1.
Services
Os services ajudam a conectar aplicações com outras aplicações ou usuários.
Existem algumas formas diferentes de fazer isso e é através do Kube-Proxy que manipula a tabela de roteamento dentro dos nodes para garantir a conectividade.
- Cada node possui um IP.
- Cada pod também possui um IP e geralmente estão em uma rede diferente do node.
- Os pods dentro de cada node possui uma interface virtual de rede que usam a mesma interface de rede do node, mas estão em outra rede.
- O Kube-Proxy cria uma conexão entre a rede que o node está e a rede do pod, por isso que se tentarmos pingar em um pod através do shell do node conseguimos.
NodePort
A função é avisar para todos os nodes para escutar em uma porta entre 30000 e 32767 e encaminhar a solicitação desta porta para um pod que está oferecendo o serviço em outra porta. Para isso precisa que todos os nodes tenham o encaminhamento desta porta específica para o grupo de pods específico, afinal esse pod pode estar rodando em qualquer node.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: NodePort
ports:
- targetPort: 80
port: 80
nodePort: 30008
# Você precisa filtrar as labels do pod e não as labels do Deployment ou do ReplicaSet
selector:
app: nginx
kubectl apply -f ./files/nodeport.yaml
service/myapp-service created
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-service NodePort 10.43.164.162 <none> 80:30008/TCP 77s
# usando um alias. Observe também que possui um clusterIP que falaremos adiante
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myapp-service NodePort 10.43.164.162 <none> 80:30008/TCP 3m15s
kubectl describe service myapp-service
Name: myapp-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=nginx
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.43.164.162
IPs: 10.43.164.162
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30008/TCP
# VEJA OS ENDPOINTS
Endpoints: 10.42.0.11:80,10.42.2.39:80,10.42.3.251:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# Conferindo
kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myapp-98d49bcf5-s6kmm 1/1 Running 0 17h 10.42.2.39 k3d-k3d-cluster-agent-2 <none> <none>
myapp-98d49bcf5-tjm4j 1/1 Running 0 17h 10.42.0.11 k3d-k3d-cluster-agent-0 <none> <none>
myapp-98d49bcf5-pzjsm 1/1 Running 0 17h 10.42.3.251 k3d-k3d-cluster-server-0 <none> <none>
# Um comando um pouco mais avançado para imprimir somente ip dos pods
kubectl get pods -o custom-columns=IP:.status.podIP
IP
10.42.2.39
10.42.0.11
10.42.3.251
# Um comando um pouco mais avançado para imprimir somente ip dos de cada pod
kubectl get pods -o custom-columns=IP:.status.hostIP
IP
172.18.0.5
172.18.0.2
172.18.0.3
Veja agora que em qualquer node que eu pingar na porta 30008 irá responder.
curl http://172.18.0.5:30008
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
# html { color-scheme: light dark; }
# body { width: 35em; margin: 0 auto;
# font-family: Tahoma, Verdana, Arial, sans-serif; }
# </style>
# </head>
# <body>
# <h1>Welcome to nginx!</h1>
# <p>If you see this page, the nginx web server is successfully installed and
# working. Further configuration is required.</p>
# <p>For online documentation and support please refer to
# <a href="http://nginx.org/">nginx.org</a>.<br/>
# Commercial support is available at
# <a href="http://nginx.com/">nginx.com</a>.</p>
# <p><em>Thank you for using nginx.</em></p>
# </body>
# </html>
curl http://172.18.0.2:30008
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
...
curl http://172.18.0.3:30008
# <!DOCTYPE html>
# <html>
# <head>
# <title>Welcome to nginx!</title>
# <style>
...
ClusterIP
Qualquer que seja o service criado, possuirá o ClusterIP. Este definirá um IP que representará um grupo de pods. Um ClusterIP possui vários endpoints que podem responder à solicitação. É utilizado um algoritmo aleatório para escolher o node.
ClusterIP só funciona dentro do cluster e serve para comunicação entre as próprias aplicações.
Escalar um Deployment, por exemplo, faz com que novos endpoints apareçam.
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: ClusterIP
ports:
- targetPort: 80 # Porta do pod
port: 80 # Porta do service
# não é necessário o NodePort
selector:
app: nginx
Como visto anteriormente, o NodePort cria automaticamente um ClusterIP.
LoadBalancer
Esse tipo cria um balanceador de carga para a aplicação em provedores de nuvem para que sua aplicação seja oferecida através de uma URL.
Veremos sobre isso mais adiante, mas a única diferença é mudar o type de NodePort para LoadBalancer, mas ainda mantemos a chave nodePort:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
type: LoadBalancer # <<<<<
ports:
- targetPort: 80
port: 80
nodePort: 30008 # mantém
selector:
app: nginx
Namespaces
É como o Kubernetes divide as coisas e isola os recursos no cluster. Dois Deployments podem ter o mesmo nome, mas precisam estar em namespaces diferentes.
Alguns namespaces são padrões:
- default:
- kube-system: Usado para implantar objetos que mantém o Kubernetes funcionando. É isolado para evitar que o usuário delete coisas importantes.
- kube-public: Usado para implantar recursos que são usados por todos os usuários.
Por exemplo, um uso de namespace é ter um de ambiente de produção
e outro de desenvolvimento
no mesmo cluster. Se for um cluster somente de produção podemos separar diferentes aplicações ou tools.
Podemos aplicar policies diferentes em cada namespace assim como regras de rede ou até mesmo de cotas.
Para um pod conversar com outro no mesmo namespace podemos apenas usar o nome do service que aponta para o pod no caso de muitos pods, ou no caso de um único pod, podemos usar o próprio nome do pod.
Para referenciar um service em outro namespace usamos
service_name.namespace_name.svc.cluster.local
Quando um service é criado, o CoreDNS já cria esse sistema de resolução de nomes.
Para criar um namespace:
apiVersion: v1
kind: Namespace
metadata:
name: dev
kubectl get namespaces
NAME STATUS AGE
default Active 68d
kube-system Active 68d
kube-public Active 68d
kube-node-lease Active 68d
crossplane-system Active 62d
# Estamos pedindo para filtrar os pods desse namespace específico, se não for passado pegará o namespace corrente que seria o default
kubectl get pods --namespace kube-system
NAME READY STATUS RESTARTS AGE
helm-install-traefik-crd-mntff 0/1 Completed 0 68d
helm-install-traefik-hknb2 0/1 Completed 1 68d
svclb-traefik-fe102d0a-pdrkf 2/2 Running 92 (2d11h ago) 68d
svclb-traefik-fe102d0a-5h7hh 2/2 Running 92 (2d11h ago) 68d
metrics-server-648b5df564-n8xp7 1/1 Running 92 (2d11h ago) 68d
local-path-provisioner-957fdf8bc-r9t55 1/1 Running 78 (2d11h ago) 68d
svclb-traefik-fe102d0a-85546 2/2 Running 92 (2d11h ago) 68d
svclb-traefik-fe102d0a-qhgk9 2/2 Running 92 (2d11h ago) 68d
traefik-64f55bb67d-q9wv9 1/1 Running 46 (2d11h ago) 68d
coredns-77ccd57875-v49sl 1/1 Running 46 (2d11h ago) 68d
kubectl describe ns dev
Name: dev
Labels: kubernetes.io/metadata.name=dev
Annotations: <none>
Status: Active
No resource quota.
No LimitRange resource.
kubectl create ns prod
namespace/prod created
kubectl get ns
NAME STATUS AGE
default Active 68d
kube-system Active 68d
kube-public Active 68d
kube-node-lease Active 68d
crossplane-system Active 62d
cert-manager Active 37d
controller-plane Active 21d
dev Active 2m55s
prod Active 3s
kubectl delete ns dev prod
namespace "dev" deleted
namespace "prod" deleted
No metadata podemos definir que um recurso seja sempre criado passando o nome do namespace que queremos.
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: dev ## <<<<<
labels:
app: nginx
name: nginx
...
Existem recursos que são à nível de namespace e outros à nível de cluster. Nos recursos à nível de cluster não conseguimos definir um namespace específico como demonstrado acima.
kubectl config set-context --current --namespace=dev
Context "k3d-k3d-cluster" modified.
kubectl get pods
No resources found in dev namespace.
# Para ver os pods em todos os namespaces
kubectl get pods --all-namespace
kubectl get pod -A
kubectl get pods -A --selector name=pod_name
Para criar cotas podemos usar o recurso ResourceQuota.