Autor: Fábio Sartori
Para este workshop iremos provisionar uma máquina na nuvem, conforme abaixo:
| Item | Descrição |
|---|---|
| Nuvem | Linode |
| IaC | Terraform |
| IT Automation Tool | Ansible |
| Linux | Ubuntu |
$ tree
.
├── IaC
│ ├── ansible
│ │ ├── hosts
│ │ ├── hosts.sample
│ │ └── playbook.yml
│ └── terraform
│ ├── main.tf
│ ├── terraform.auto.tfvars
│ ├── terraform.auto.tfvars.sampleA máquina virtual foi provisionada utilizando a ferramenta de Infrastructure as Code Terraform
Para esta etapa, entraremos no diretório IaC/terraform. Estrutura abaixo listada:
$ tree
.
├── IaC
│ └── terraform
│ ├── main.tf
│ ├── terraform.auto.tfvars
│ ├── terraform.auto.tfvars.sampleEntre no diretório do terraform.
cd IaC/terraform# Autor: Fábio Sartori
# Copyright: 20220524
terraform {
required_providers {
linode = {
source = "linode/linode"
version = "1.26.1"
}
}
}
provider "linode" {
token = var.shared_token
}
resource "linode_instance" "ubuntupi" {
label = var.ubuntu_label
image = var.ubuntu_image
type = var.ubuntu_type
region = var.shared_region
root_pass = var.shared_root_pass
authorized_keys = var.authorized_keys
private_ip = var.shared_private_ip
tags = var.ubuntu_tags
}
variable "shared_token" {}
variable "region" {
default = "us-central"
}
variable "shared_root_pass" {}
variable "shared_private_ip" {}
variable "shared_region" {}
variable "authorized_keys" {}
variable "ubuntu_tags" {}
variable "ubuntu_label" {}
variable "ubuntu_image" {}
variable "ubuntu_type" {}Para criar este acruivo de variáveis, use como base o arquivo de exemplo terraform.auto.tfvars.sample
# Token de usuário do linode (ESTE TOKEN É SOMENTE PARA EXEMPLIFICAR, POIS NÃO É VÁLIDO)
shared_token = "00e3261a6e0d79c329445acd540fb2b07187a0dcf6017065c8814010283ac67f"
# Senha de root dos servidores
shared_root_pass = "_SENHA_DO_ROOT_"
# Região do Datacenter Linode
shared_region = "us-central"
# true se a máquina tiver ip privado
shared_private_ip = true
# Array com as chaves públicas autorizadas a logar no servidor (AS CHAVES ABAIXO SÃO SOMENTE PARA EXEMPLIFICAR, POIS NÃO SÃO VÁLIDAS)
authorized_keys = ["ssh-rsa 724c70d82400c99516c0b9513125c14bfd995fecfb7cc9ab757943459c0397fff92bbc8e1b9facbbaec8f27e0426cb4f7def7c6c83df3f3955373729a366303b= juvenal@bla"]
# Tags para identificar o servidor
ubuntu_tags = ["workshop","kind"]
# Label para identificar o servidor
ubuntu_label = "ubuntu_pi"
# Imagem de Linux para a criação do servidor
ubuntu_image = "linode/ubuntu20.04"
# Tipo do linode com 4GB de RAM (verifique os custo $$ antes)
ubuntu_type = "g6-standard-4"
terraform initRetorno
Initializing the backend...
Initializing provider plugins...
- Reusing previous version of linode/linode from the dependency lock file
- Using previously-installed linode/linode v1.26.1
Terraform has been successfully initialized!
terraform planRetorno
Terraform used the selected providers to generate the following execution plan. Resource
actions are indicated with the following symbols:
+ create
.
.
.
Plan: 1 to add, 0 to change, 0 to destroy.
Para criar a máquina virtual, aplique o plano de execução, conforme abaixo.
terraform applyRetorno
Terraform used the selected providers to generate the following execution plan. Resource
actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# linode_instance.ubuntupi will be created
+ resource "linode_instance" "ubuntupi" {
.
.
.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value:
Confirme, digitando yes e pressionando <ENTER>
Ao final da execução, você deverá receber uma mensagem conforme abaixo: . . linode_instance.ubuntupi: Creation complete after 55s [id=36484787] Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
terraform showNos exemplos, xxx.xxx.xxx.xxx representa o IP externo do servidor.
terraform show -json | jq .values.root_module.resources[0].values.ip_address
xxx.xxx.xxx.xxxArquivo playbook.yaml
# Autor: Fábio Sartori
# Copyright: 20220525
- hosts: ubuntukind
become: yes
tasks:
- name: Alterando o hostname para ubuntukind
hostname:
name: ubuntukind
become: yes
- name: Instalando Docker
apt:
name: ["docker.io", "docker-compose"]
state: present
update_cache: yes
become: yes
- name: Desabilidando autenticacao com senha
lineinfile: dest=/etc/ssh/sshd_config
regexp='^#PasswordAuthentication no|^#PasswordAuthentication yes|^PasswordAuthentication yes'
line="PasswordAuthentication no"
state=present
backup=yes
notify:
- restart ssh
- name: Habilitando autenticacao por chave publica
lineinfile: dest=/etc/ssh/sshd_config
regexp='^#PubkeyAuthentication no|^#PubkeyAuthentication yes|^PubkeyAuthentication no'
line="PubkeyAuthentication yes"
state=present
backup=yes
notify:
- restart ssh
handlers:
- name: restart ssh
service: name=sshd
state=restarted
Arquivo de inventário hosts
[ubuntukind]
xxx.xxx.xxx.xxxPara aplicar o playbook execute o comando abaixo:
ansible-playbook -i hosts -u root playbook.ymlFerramenta capaz de executar clusters locais de Kubernetes usando nó de containers Docker.
Kind consiste em:
- Pacotes GO: Responsáveis pela criação de clusters, build de imagens, etc
- Kind CLI: Utilitário de linha de comando para administrar o cluster kind
- Image Docker: Serve para executar o systemd, k8s, etc;
| Requisito | Tipo | Obs |
|---|---|---|
| docker | Software | https://docs.docker.com/get-docker/ |
| kind CLI | Software | https://kind.sigs.k8s.io/docs/user/quick-start#installing-from-release-binaries |
| kubectl | Software | https://kubernetes.io/docs/tasks/tools/ |
| Memória Mínima | Hardware | 6GB RAM Dedicado |
| Memória Recomendado | Hardware | 8GB RAM |
| CPU | Hardware | 4 Cores no mínimo |
Para instalar o Kind CLI, execute os comandos abaixo:
curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.14.0/kind-linux-amd64
chmod +x ./kind
mv ./kind /usr/bin/kindCaso a execução não seja com o usuário root, é recomendado lhe conceder permissões para acesso ao socket do docker.
No exemplo abaixo, vamos supor que o usuário juvenal precisa ter acesso ao docker. Execute o comando abaixo no terminal:
sudo usermod -a -G docker juvenal- Atualize o catálogo de pacotes apt e instale os pacotes necessários para utilizar o repositório apt do Kubernetes:
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl- Faça download da chave pública do Google Cloud:
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg- Adicione o repositório apt do Kubernetes:
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list- Atualize o catálogo de pacotes apt com o novo repositório e instale o kubectl
sudo apt-get update
sudo apt-get install -y kubectl- Teste o kubectl
kubectl
.
.
.
Usage:
kubectl [flags] [options]
Use "kubectl <command> --help" for more information about a given command.
Use "kubectl options" for a list of global command-line options (applies to all
commands).Para seguir com este tutorial, inicialize o serviço do docker caso não esteja no ar
sudo systemctl start docker
kind create cluster --name <NOME_DO_CLUSTER>Para customizar a criação de um cluster, é necessário criar um arquivo no formato YAML seguindo, é claro, convenções do kubernetes, como versionamento, etc.
As customizações vão desde o nome do cluster, passando por configurações de runtime, redes, port mapping, etc.
Segue o link para maiores referências sobre as configurações possiveis.
https://pkg.go.dev/sigs.k8s.io/kind/pkg/apis/config/v1alpha4
- Criação de um de um cluster kind (single-node) contendo a configuração do cluster e pronto para o ingress com mapeamentos nas portas 80 e 443
Crie um arquivo chamado cluster-config.yaml para um cluster single-node com o conteúdo abaixo:
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: cons-prosp
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCPOu para um cluster multi-node
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: cons-prosp
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
- role: workerIMPORTANTE: A configuração do node-labels
Aplique o script do passo anterior e crie o cluster
kind create cluster --config cluster-config.yamlA partir deste momento, o cluster está pronto para a instalação de um ingress controller.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/kind/deploy.yamlExecutando Aguardando a conclusão
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=90sNeste exemplo o cons-prosp, é o nome do cluster criado no Kind
kind get clusters
cons-prospkubectl get nodes | grep "cons-prosp"
NAME STATUS ROLES AGE VERSION
cons-prosp-control-plane Ready control-plane,master 50m v1.21.1docker container inspect cons-prosp-control-plane --format '{{ .NetworkSettings.Networks.kind.IPAddress }}'kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: versao-antiga
nodes:
- role: control-plane
image: kindest/node:v1.19.16@sha256:d9c819e8668de8d5030708e484a9fdff44d95ec4675d136ef0a0a584e587f65ckubectl config current-context
kind-versao-antigakubectl config get-contexts
kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kind-cons-prosp kind-cons-prosp kind-cons-prosp
kind-versao-antiga kind-versao-antiga kind-versao-antigakubectl config use-context kind-cons-prosp kind-cons-prospkind export logs --name cons-prosp /tmp/logs
Exporting logs for cluster "cons-prosp" to:
/tmp/logs
ls /tmp/logs
cons-prosp-control-plane cons-prosp-control-plane2 cons-prosp-worker cons-prosp-worker2 docker-info.txt kind-version.txtkind get kubeconfig --name cons-prosp > ARQUIVO_KUBECONFIGkubectl config use-context kind-cons-prosp kind-cons-prospPara quem for trabalhar em locais sem acesso a internet, é bastante interessante ter as imagens dos nós de k8s localmente, para que assim seja possível criar novos clusters, se necessário.
kind --version
kind version 0.14.0https://github.com/kubernetes-sigs/kind/releases
Para exemplificar, usaremos a versão 1.24.0
Faça pull da versão: Não esqueça de adicionar na versão da tag, o digest sha512...
docker pull kindest/node:v1.24.0@sha256:0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8eDICA: Salve a imagem em disco, para ter um becape, além do registry local.
docker save kindest/node:v1.24.0@sha256:0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e | gzip > kind.v1.24.0.tar.gzSe você não tiver a image carregada no seu registro local, para o caso de terr somente o arquivo kind.v1.24.0.tar.gz salvo. Imaginando que tenha recebido o arquivo de um colega.
docker load -i kind.v1.24.0.tar.gzApós ter a imagem carregada no registry local, crie uma TAG para ela.
docker image tag kindest/node:v1.24.0@sha256:0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e kindest/node:v1.24.0Agora, quando for criar um clusster com esta versão, basta usar o parâmetro --image, caso use a linha de comando.
kind create cluster --image kindest/node:v1.24.0Ou adicione ao script de criação do cluster
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: versao-antiga
nodes:
- role: control-plane
image: kindest/node:v1.24.0
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCPAPI geradora aleatória de prospects.
Para exemplificar, iremos criar o seguinte cenário
| Variável | Descrição | Exemplo |
|---|---|---|
| PORT | Número da porta da API | 5000 |
| ENVIRONMENT | Ambiente no qual a API está executando | development |
| PROSPECTS_ROUTE | Rota para listar os prospects | /prospects |
Por padrão, serão retornados 50 registros FAKE
GET /prospects
Content-type/application/json
Exemplo de retorno:
[
{
"name": "Sra. Maria Fernanda Costela",
"mail": "costelamaria-fernanda@hotmail.com",
"phone_number": "61 3949-7553"
},
{
"name": "Danilo Vieira",
"mail": "cunhaluiz-fernando@hotmail.com",
"phone_number": "+55 61 4916-6943"
}
]Arquivo configmap.yaml
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "random-prospects-genetator-api-config",
"labels": {
"app": "random-prospects-genetator-api"
}
},
"data": {
"PORT": "5000",
"ENVIRONMENT": "development",
"PROSPECTS_ROUTE": "/prospects"
}
}Aplique o script com o comando abaixo:
kubectl apply -f https://raw.githubusercontent.com/kiosanim/kind-kubernetes-clusters-para-desenvolvedores-workshop/master/random-prospects-generator-api/k8s/configmap.yamlArquivo deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: "random-prospects-genetator-api"
spec:
selector:
matchLabels:
app: "random-prospects-genetator-api"
replicas: 1
template:
metadata:
labels:
app: "random-prospects-genetator-api"
spec:
containers:
- name: "random-prospects-genetator-api"
image: "fabiosartori/random-prospects-genetator-api:0.1.1"
resources:
limits:
memory: 128Mi
cpu: 256m
env:
- name: "PORT"
value: "5000"
- name: "ENVIRONMENT"
value: "development"
- name: "PROSPECTS_ROUTE"
value: "/prospects"
envFrom:
- configMapRef:
name: "random-prospects-genetator-api-config"
ports:
- containerPort: 8000Aplique o script com o comando abaixo:
kubectl apply -f https://raw.githubusercontent.com/kiosanim/kind-kubernetes-clusters-para-desenvolvedores-workshop/master/random-prospects-generator-api/k8s/deployment.yamlArquivo service.yaml
kind: Service
apiVersion: v1
metadata:
name: random-prospects-genetator-api-svc
spec:
selector:
app: random-prospects-genetator-api
ports:
- port: 5000Aplique o script com o comando abaixo:
kubectl apply -f https://raw.githubusercontent.com/kiosanim/kind-kubernetes-clusters-para-desenvolvedores-workshop/master/random-prospects-generator-api/k8s/service.yamlArquivo ingress_rules.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: random-prospects-genetator-api-ingress
spec:
rules:
- host: juvenal.example.com
http:
paths:
- pathType: ImplementationSpecific
path: "/prospects"
backend:
service:
name: random-prospects-genetator-api-svc
port:
number: 5000Aplique o script com o comando abaixo:
kubectl apply -f https://raw.githubusercontent.com/kiosanim/kind-kubernetes-clusters-para-desenvolvedores-workshop/master/random-prospects-generator-api/k8s/ingress_rules.yamlAdicione a linha abaixo no arquivo /etc/hosts
172.18.0.2 juvenal.example.comcurl -X GET http://juvenal.example.com/prospects
[
{
"name": "Luiza Jesus",
"mail": "lauramendes@ig.com.br",
"phone_number": "+55 31 8511-6848"
},
{
"name": "Maitê Porto",
"mail": "rezendeluna@bol.com.br",
"phone_number": "41 5822-4474"
}
.
.
.
]kubectl scale --replicas 2 deployment/random-prospects-genetator-apiComo resultado, teremos os pods distribuídos entre as instâncias.
kubectl get pods -o wide Como resultado, poderá perceber que existem instâncias nas duas instâncias do cluster.
NUNCA USE O KIND EM AMBIENTES DE PRODUÇÃO, POIS O KIND NÃO POSSUI VÊM COM AS MELHORES PRÁTICAS DE SEGURANÇA, COMO UM CLUSTER REAL.
https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands
https://github.com/kiosanim/kind-kubernetes-clusters-para-desenvolvedores-workshop





