Build
O build faz referencia aos sources podendo ter provisionados que irão criar o artefato, por exemplo uma imagem, e os pos processadores que irão manipular resolver o que fazer com o artefato.
Sources
O build precisa de pelo menos um source bloco source definido. Este bloco pode ser referenciado várias vezes dentro do build para gerar vários construtores diferentes e os comandos do provisioner serão executados dentro de cada um dos construtores selecionados.
Sabendo isso, é de boa prática declarar nos blocos source valores que não mudam e as mudanças declarar dentro da referencia ao source no build como no exemplo que vou mostrar.
O exemplo abaixo ilustra o source do tipo null que não é realmente um construtor, apenas configura uma conexão SSH e executa os provisionadores. Ele pode ser usado para depurar provisionadores sem incorrer em altos tempos de espera. Não cria nenhum tipo de imagem ou artefato.
Uma boa prática é definir o name no build para aparecer na saída.
# Comunicator é obrigatório pode ser ssh winrm ou none no caso de null
# Dependendo do source source ssh deve ser definido por exemplo; no caso da aws e no caso da azure ssh é o padrão uma vez que todas as máquinas tem o user azure.
variable "pass" {
description = "My password"
type = string
# usando função para pegar minha variável exportada
default = env("MY_PASS")
}
source "null" "first-example" {
communicator = "none"
}
# Pode ter vários sources do mesmo tipo, mas nunca com o mesmo nome
source "null" "second-example" {
ssh_host= "127.0.0.1"
ssh_username = "david"
ssh_password = var.pass
# O argumento abaixo é necessário mas eu vou setar ele lá no bloco build
# Se esse argumento for setado aqui não é possível setar lá.
# ssh_password = "xxxxxx"
}
build {
name = "MeuNullBuild"
# usando o primeiro construtor e adicionando a variável name
source "null.first-example" {
name = "consul"
}
# usando o primeiro construtor e adicionando uma variável extra
source "null.first-example" {
name = "nomad"
}
source "null.second-example" {
name = "vault"
}
sources = ["null.first-example","null.second-example"]
# É possível referenciar name e type dos sources da seguinte maneira
# Não é possível referenciar outras variáveis como othername usando source.othername
provisioner "shell-local" {
inline = ["echo ${source.name} and ${source.type} TEST PROVISIONER"
]
}
post-processor "shell-local" {
inline = ["echo ${source.name} OR ${source.type} TEST POST-PROCESSOR"]
}
}
Para executar o build abaixo e é necessário que habilitar o ssh na sua máquina para permitir autenticação por senha e iniciar o serviço do sshd
.
Confira o projeto e faça seus testes no exemplo rodando o comando dentro da pasta.
~/gitlab/personal/study-packer/examples/build main ⇡3 !3 ?1
❯ packer build .
MeuNullBuild.null.first-example: output will be in this color.
MeuNullBuild.null.second-example: output will be in this color.
MeuNullBuild.null.consul: output will be in this color.
MeuNullBuild.null.nomad: output will be in this color.
MeuNullBuild.null.vault: output will be in this color.
==> MeuNullBuild.null.vault: Using SSH communicator to connect: 127.0.0.1
==> MeuNullBuild.null.second-example: Using SSH communicator to connect: 127.0.0.1
==> MeuNullBuild.null.vault: Waiting for SSH to become available...
==> MeuNullBuild.null.second-example: Waiting for SSH to become available...
==> MeuNullBuild.null.nomad: Running local shell script: /tmp/packer-shell4018821226
==> MeuNullBuild.null.consul: Running local shell script: /tmp/packer-shell24449155
==> MeuNullBuild.null.first-example: Running local shell script: /tmp/packer-shell590834333
MeuNullBuild.null.nomad: nomad and null TEST PROVISIONER
MeuNullBuild.null.consul: consul and null TEST PROVISIONER
==> MeuNullBuild.null.nomad: Running post-processor: (type shell-local)
==> MeuNullBuild.null.consul: Running post-processor: (type shell-local)
MeuNullBuild.null.first-example: first-example and null TEST PROVISIONER
==> MeuNullBuild.null.first-example: Running post-processor: (type shell-local)
==> MeuNullBuild.null.nomad (shell-local): Running local shell script: /tmp/packer-shell661443636
==> MeuNullBuild.null.consul (shell-local): Running local shell script: /tmp/packer-shell3268706950
==> MeuNullBuild.null.first-example (shell-local): Running local shell script: /tmp/packer-shell3568713064
MeuNullBuild.null.nomad (shell-local): nomad OR null TEST POST-PROCESSOR
Build 'MeuNullBuild.null.nomad' finished after 12 milliseconds 418 microseconds.
MeuNullBuild.null.consul (shell-local): consul OR null TEST POST-PROCESSOR
Build 'MeuNullBuild.null.consul' finished after 12 milliseconds 632 microseconds.
MeuNullBuild.null.first-example (shell-local): first-example OR null TEST POST-PROCESSOR
Build 'MeuNullBuild.null.first-example' finished after 14 milliseconds 121 microseconds.
==> MeuNullBuild.null.vault: Connected to SSH!
==> MeuNullBuild.null.second-example: Connected to SSH!
==> MeuNullBuild.null.vault: Running local shell script: /tmp/packer-shell1580315204
==> MeuNullBuild.null.second-example: Running local shell script: /tmp/packer-shell2396870170
MeuNullBuild.null.vault: vault and null TEST PROVISIONER
==> MeuNullBuild.null.vault: Running post-processor: (type shell-local)
MeuNullBuild.null.second-example: second-example and null TEST PROVISIONER
==> MeuNullBuild.null.second-example: Running post-processor: (type shell-local)
==> MeuNullBuild.null.vault (shell-local): Running local shell script: /tmp/packer-shell1814621561
==> MeuNullBuild.null.second-example (shell-local): Running local shell script: /tmp/packer-shell4080843186
MeuNullBuild.null.vault (shell-local): vault OR null TEST POST-PROCESSOR
Build 'MeuNullBuild.null.vault' finished after 165 milliseconds 197 microseconds.
MeuNullBuild.null.second-example (shell-local): second-example OR null TEST POST-PROCESSOR
Build 'MeuNullBuild.null.second-example' finished after 167 milliseconds 229 microseconds.
==> Wait completed after 167 milliseconds 336 microseconds
==> Builds finished. The artifacts of successful builds are:
--> MeuNullBuild.null.nomad: Did not export anything. This is the null builder
--> MeuNullBuild.null.nomad: Did not export anything. This is the null builder
--> MeuNullBuild.null.consul: Did not export anything. This is the null builder
--> MeuNullBuild.null.consul: Did not export anything. This is the null builder
--> MeuNullBuild.null.first-example: Did not export anything. This is the null builder
--> MeuNullBuild.null.first-example: Did not export anything. This is the null builder
--> MeuNullBuild.null.vault: Did not export anything. This is the null builder
--> MeuNullBuild.null.vault: Did not export anything. This is the null builder
--> MeuNullBuild.null.second-example: Did not export anything. This is the null builder
--> MeuNullBuild.null.second-example: Did not export anything. This is the null builder
❯ packer build .
roles.null.first-example: output will be in this color.
roles.null.second-example: output will be in this color.
roles.null.consul: output will be in this color.
roles.null.nomad: output will be in this color.
roles.null.vault: output will be in this color.
==> roles.null.second-example: Running local shell script: /tmp/packer-shell2204105492
==> roles.null.nomad: Running local shell script: /tmp/packer-shell3063727616
==> roles.null.consul: Running local shell script: /tmp/packer-shell2232404743
==> roles.null.vault: Running local shell script: /tmp/packer-shell607664062
==> roles.null.first-example: Running local shell script: /tmp/packer-shell3215393418
roles.null.consul: consul and null
roles.null.nomad: nomad and null
roles.null.second-example: second-example and null
roles.null.first-example: first-example and null
Build 'roles.null.nomad' finished after 52 milliseconds 323 microseconds.
Build 'roles.null.second-example' finished after 53 milliseconds 750 microseconds.
Build 'roles.null.consul' finished after 53 milliseconds 860 microseconds.
Build 'roles.null.first-example' finished after 54 milliseconds 339 microseconds.
roles.null.vault: vault and null
Build 'roles.null.vault' finished after 65 milliseconds 939 microseconds.
==> Wait completed after 66 milliseconds 290 microseconds
==> Builds finished. The artifacts of successful builds are:
--> roles.null.nomad: Did not export anything. This is the null builder
--> roles.null.second-example: Did not export anything. This is the null builder
--> roles.null.consul: Did not export anything. This is the null builder
--> roles.null.first-example: Did not export anything. This is the null builder
--> roles.null.vault: Did not export anything. This is the null builde
Para o build poder acessar a máquina existem duas formas, ou declarar ter ssh no caso do Linux ou winrm no caso do windows. Geralmente usa-se ssh. Se não existir um comunicador definido, a maioria dos passos do build não funcionarão.
Os plugins estendem os tipos dos sources e provisioners.
Provisioners
Os provisioners usam trabalham em cima da instancia disponibilizada pelos sources e após o boot instala e configura a imagem da máquina após o boot.
Todos os provisionadores tem parâmetros comuns
-
pause_before(duração) - Dorme por um período antes da execução.
-
max_retries(int) - Máximo de vezes que o provisionador tentará novamente em caso de falha. O padrão é zero (0).
-
only(array of string) - Execute o provisionador apenas para os provisioners listados pelo nome.
-
override(object) - Substitua o construtor com configurações diferentes para um construtor específico
Provisioners Nativos
-
file
: Este provisionador é usado para fazer upload de um arquivo para dentro da máquinas disponibilizada pelos sources. Somente é possível fazer upload para diretório que o user utilizado no source para fazer o ssh tenha permissão. Uma boa prática é fazer para dentro de /tmp e depois com outro provisionador manipular os arquivos.build {
provisioner "file" {
# Os caminhos para os arquivos ou diretórios local para carregar na máquina
sources = ["app.tar.gz"]
# O caminho para onde o arquivo será carregado na máquina
destination = "/tmp/app.tar.gz"
# Se for setado download irá puxar arquivo da vm para dentro da máquina local. O padrão é upload.
direction = "upload"
}
}Usa-se o direction como download muitas vezes para puxar algo no post processor.
-
shell
: Este provisionador irá executar shell scripts. É o principal provisionador. É com ele que se manipula os files que foram carregados na vm. Uma boa prática na minha opinião é sempre referenciar scripts em arquivos ao invés de mostrar comando a comando. Isso evita poluição no código e organiza melhor as coisas. Por isso no projeto base existe uma pasta só para scripts. Uma das vantagens é sempre definir o shell que é usado quando se tem o script. Atenção no execute_command que é um atributo deste provisionador.- execute_command(string) - O comando a ser usado para executar o script. Por padrão, é
chmod +x {{ .Path }}; {{ .Vars }} {{ .Path }}
. Se o usuário definir um arquivo onde estão as variáveis de ambiente o padrão do comando seráchmod +x {{.Path}}; . {{.EnvVarFile}} && {{.Path}}
existem três variáveis extras disponíveis:- Path é o caminho para ser executado
- Vars é a lista de environment_vars se estiver definida
- EnvVarFile é o caminho para o arquivo que contém env vars, se use_env_var_file for true
build {
provisioner "shell" {
environment_vars = [
"MY_TEST=david",
"MY_TEST2=puziol"
]
# Exemplo de como excutar com sudo
# "echo 'packer' | sudo -S sh -c '{{ .Vars }} {{ .Path }}'"
# "echo 'packer' | sudo -S env {{ .Vars }} {{ .Path }}"
execute_command = "{{ .Vars }} /bin/bash '{{ .Path }}'"
scripts = [
"./scripts/script1.sh",
"./scripts/script2.sh",
"./scripts/script3.sh"
]
}
} - execute_command(string) - O comando a ser usado para executar o script. Por padrão, é
-
shell-local
: executará um script na máquina em que o Packer está sendo executado. Os parâmetros são os mesmos do provisionador shell. Bastante usado durante o post-processor para executar comandos.
Vale mencionar que existem algumas envs que são default.
- PACKER_BUILD_NAME : usado para diferenciar o nome entre as compilações senão todas usaram o mesmo name do build.
- PACKER_BUILDER_TYPE : define o tipo do construtor que foi usado para criar a máquina que vai executar o script.
- PACKER_HTTP_ADDR: Só é usado caso o construtor forneça um http server para transferência de arquivo
NÃO ADIANTA FALAR DE TUDO QUE EXISTE, VOCÊ PRECISA SABER QUE EXISTE, E QUANDO VC PRECISAR USAR VC ESTUDA. ESSES SÃO OS PRINCIPAIS.
Como encaminhar seu agente ssh local para a máquina?
provisioner "shell" {
inline = [
"sudo apt-get install -y git",
"ssh-keyscan github.com >> ~/.ssh/known_hosts",
"git clone [email protected]:exampleorg/myprivaterepo.git"
]
}
Porém existe usar o plugin do git facilitaria as coisas e garantiria maior segurança.
Post-processors
É a ultima coisa a ser executado, após os construtores e provisionadores. São opcionais e usados para resolver o que fazer com os artefatos.
- Fazer upload
- Reempacotar
- Checksum
- Manifestos
- Carregar em um bucket s3
- etc