Salve galerinha! Gabriel aqui.
Uma dúvida comum que pessoas novas ao Terraform têm é: “depois de criar a VM, como eu rodo minha aplicação?”. Algumas soluções incluem cloud-init, Ansible, etc.
Nesse guia, eu quero mostrar uma das melhores (na minha opinião) soluções para isso: o NixOS. Mostrarei como usar Terraform + NixOS para provisionar um servidor já rodando uma aplicação de sua escolha, sem nenhum passo manual.
Factorio é um jogo de automação com uma qualidade absurda e uma gameplay extremamente polida. Se você valoriza seu sono, recomendo não jogar! Pro resto de nós, já viciados, a fábrica deve crescer!
Com isso em mente, esse guia irá, como exemplo divertido, focar em subir um servidor de Factorio! ⚙️
Mas, Gabriel, eu não gosto de Factorio!
Ótimo! Melhor ficar longe desse vício.
O factorio é só um exemplo, e é a parte menos importante do guia. Explicarei como fazer isso para outros jogos (e.g. minecraft), continue lendo!
O versão final está disponível aqui: GitHub - Misterio77/hackathon-mgc-factorio-terraform. Esse tutorial irá mostrar como construir esse código iterativamente.
Intro 🏁
O NixOS é uma distribuição Linux baseada no gerenciador de pacotes Nix. O Nix permite empacotar programas de forma reproduzível e isolada, numa linguagem declarativa e pura. O NixOS leva isso a um outro nível, e permite configurar sistemas inteiros usando essa mesma linguagem. Por exemplo, para subir um servidor de Factorio:
{ services.factorio = { enable = true; }; }
Lembra bastante o Terraform, né?
Vou mostrar pra vocês como implantar e configurar um servidor no Magalu Cloud, via Terraform e NixOS, por meio do nixos-anywhere.
A idéia é que, com apenas um tofu apply
, o servidor seja criado já rodando exatamente o que você quer que rode, sem nenhum passo manual. 👷
Mãos à obra!
Setup 🔨
Caso queira acompanhar o tutorial e ir rodando coisas na sua máquina (que recomendo!), você vai precisar:
- Qualquer distro Linux (pode ser WSL) ou MacOS;
- Instalar o gerenciador de pacotes Nix (sua máquina não precisa ser NixOS);
- Instalar a MGC CLI;
- Instalar o OpenTofu;
- Uma chave SSH;
- Um editor de texto que você goste.
Configuração de NixOS ❄️
Para usarmos algumas funcionalidades novas do Nix, vamos habilitar flakes
e nix-command
:
$ mkdir -p ~/.config/nix $ echo "extra-experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
Vamos começar configurando nosso NixOS. Crie uma configuration.nix
:
{ networking = { hostName = "factorio-server"; # Usar DHCP para conectar useDHCP = true; }; system.stateVersion = "25.05"; nixpkgs = { # Arquitetura e sistema hostPlatform = "x86_64-linux"; # Habilitar pacotes proprietários config.allowUnfree = true; }; services.factorio = { enable = true; # Abrir porta no firewall openFirewall = true; }; # TODO: Iremos remover isso depois users.users.root = { initialPassword = "123456"; }; }
Gabriel, o que mais podemos rodar além de factorio?
O que seu coração mandar! O NixOS tem abstrações prontas para vários jogos, e você pode rodar qualquer software que imaginar.
Experimenteservices.minecraft
!
Vamos usar flakes nesse tutorial. Crie uma flake.nix
:
{ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; }; outputs = { nixpkgs, ... }: { nixosConfigurations.factorio-server = nixpkgs.lib.nixosSystem { modules = [./configuration.nix]; }; }; }
Gabriellll, o que é diachos é um flake?
Ótima pergunta! Ter um
flake.nix
torna seu repositório um flake (duh). Em resumo, um flake é um "projeto nix", com dependências em outros "projetos nix", e que provê "coisas nix". Nesse caso, nosso projeto depende donixpkgs
(o conjunto principal de software empacotado para Nix) na versãonixos-unstable
, e provê uma configuraçao de NixOS chamadafactorio-server
.É possível usar Nix sem flakes, mas criar um flake te permite gerir melhor as dependências do seu projeto e usar uma CLI mais intuitiva.
Vamos testar? O Nix permite criar uma VM para uma dada configuração de NixOS. Rode:
$ nix run .#nixosConfigurations.factorio-server.config.system.build.vm
Pode levar alguns minutos, pois o Nix irá baixar absolutamente tudo nescessário para esse sistema. Fique tranquilo, pois seu sistema irá re-usar isso sempre que possível.
Irá abrir uma janela com o console da sua VM. Faça login com root
e 123456
.
Vamos ver se o servidor está okay:
systemctl status factorio
Sucesso! Nossa VM está rodando Factorio ⚙️
Agora, vamos colocar isso na Cloud! Bora para o Terraform. Depois voltaremos para fazer alguns ajustes nessa configuração.
Subindo VM com Terraform ☁️
Crie um arquivo main.tf
, com o conteúdo:
terraform { backend "local" { path = ".terraform.tfstate" } required_providers { mgc = { source = "registry.terraform.io/magalucloud/mgc" version = "0.36.1" } } } provider "mgc" { region = "br-se1" }
Nice. Esse é o nosso boilerplate básico pra usar o provider e manter o estado do terraform no nosso diretório local.
O terraform lê todas as .tf
no diretório, então vamos deixar as coisas organizadinhas.
Criando a VM
Vamos preparar nossa VM. Crie um vm.tf
:
resource "mgc_virtual_machine_instances" "factorio_server" { name = "factorio" # 2 vCPUs, 8GB de RAM, 40GB de disco machine_type = "BV2-8-40" image = "cloud-debian-12 LTS" }
Ué, Debian? Não íamos usar NixOS? Que sacrilégio é esse?
Calma calma foguetinho 🚀! O Magalu Cloud ainda não tem imagem de NixOS, mas temos uma carta na manga para instalar e configurar o NixOS, aguenta ai!
Tornando ela acessível ao mundo
Precisamos acessar essa VM. Pra isso, vamos:
- Adicionar nossa chave SSH
- Alocar um IP público e atribuir ele à máquina
- Abrir a porta do SSH (22 TCP) e do Factorio (34197 UDP) no firewall da cloud
Começando pela chave pública. Na sua vm.tf
:
resource "mgc_ssh_keys" "key" { name = "chave-do-gabriel" # Altere para a sua chave publica key = "<SUA CHAVE SSH PUBLICA>" } resource "mgc_virtual_machine_instances" "factorio_server" { name = "factorio" # 2 vCPUs, 8GB de RAM, 40GB de disco machine_type = "BV2-8-40" image = "cloud-debian-12 LTS" # Passar nossa chave ssh_key_name = mgc_ssh_keys.key.name }
E vamos alocar um IPv4 dedicado e atribuir à maquina, crie uma ip.tf
:
# Alocar um IPv4 resource "mgc_network_public_ips" "factorio_ip" { vpc_id = mgc_virtual_machine_instances.factorio_server.vpc_id } # Atribuir à máquina resource "mgc_network_public_ips_attach" "factorio_ip_attach" { public_ip_id = mgc_network_public_ips.factorio_ip.id interface_id = mgc_virtual_machine_instances.factorio_server.network_interfaces[0].id } # O terraform vai mostrar esse valor para nós output "ip" { value = mgc_network_public_ips.factorio_ip.public_ip }
Por fim, vamos abrir as portas. Crie uma firewall.tf
:
# Nosso security group resource "mgc_network_security_groups" "factorio_server" { name = "factorio-hackathon" } # Regra para SSH resource "mgc_network_security_groups_rules" "incoming_ssh" { direction = "ingress" port_range_min = 22 port_range_max = 22 protocol = "tcp" ethertype = "IPv4" remote_ip_prefix = "0.0.0.0/0" security_group_id = mgc_network_security_groups.factorio_server.id } # Regra para o factorio resource "mgc_network_security_groups_rules" "incoming_factorio" { direction = "ingress" port_range_min = 34197 port_range_max = 34197 protocol = "udp" ethertype = "IPv4" remote_ip_prefix = "0.0.0.0/0" security_group_id = mgc_network_security_groups.factorio_server.id } # Atribuir o group à nossa VM resource "mgc_network_security_groups_attach" "factorio_firewall_attach" { security_group_id = mgc_network_security_groups.factorio_server.id interface_id = mgc_virtual_machine_instances.factorio_server.network_interfaces[0].id }
Lembre-se de trocar (ou adicionar) regras conforme o que você está deployando. Por exemplo, se está subindo minecraft, faça uma regra que abre a porta
25565
no protocolotcp
.
O primeiro deploy
Maravilha! Hora de deployar. Vamos começar autenticando via MGC CLI:
$ mgc auth login
E siga os passos na tela.
Agora precisamos criar uma API key e passar para o terraform.
$ mgc auth api-key create --name terraform-factorio
Aperte a flecha direita para dar todas as permissões, e tab para aceitar. Agora vamos pegar a key e passar para o terraform. Um oneliner que facilita:
export TF_VAR_mgc_api_key=$(mgc auth api-key get --id $(mgc auth api-key list -o json -r | jq 'map(select(.name == "terraform-factorio"))[0].id' -r) -o json -r | jq .api_key -r)
Feito isso, vamos preparar o terraform:
$ tofu init
E aplicar:
$ tofu apply
Digite yes
, e aguarde um pouquinho.
Feito isso, é hora de validar que o servidor está okay e está acessível pela sua chave. O seu apply deve ter retornado o ip
como output. Rode:
$ ssh debian@<IP DA VM>
Agora temos uma VM… Rodando Debian (por enquanto):
Vamos agora infectar essa querida com NixOS! Iremos utilizar o nixos-anywhere.
Instalando NixOS na VM 🪄
A idéia do nixos-anywhere
é iniciar um NixOS via kexec, desmontar o disco da máquina, re-particionar ele, e instalar NixOS de verdade (com a nossa configuração). Eles provêm um módulo de Terraform, que é perfeito para a gente!
Gabriel, kexec? Que diacho é isso?
Não tema! É bem mais simples do que parece. Significa "kernel execute".
Pense assim: quando você vai instalar Linux numa máquina, você geralmente boota uma ISO por pendrive. Isso é necessário para você poder liberar e formatar o disco do seu computador.
No Magalu não temos como espetar um pendrive, então usamos um truque de executar outro Linux direto na memória RAM da máquina, isso nos permite desmontar e formatar os discos da máquina, igual com um pendrive!
Vamos precisar fazer alguns ajustes na nossa configuração de NixOS para comportar isso. O NixOS anywhere usa uma ferramenta chamada disko para particionar declarativamente. Vamos adicioná-la no nosso flake.nix
:
{ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; disko.url = "github:nix-community/disko/latest"; disko.inputs.nixpkgs.follows = "nixpkgs"; }; outputs = { nixpkgs, disko, ... }: { nixosConfigurations.factorio-server = nixpkgs.lib.nixosSystem { modules = [ ./configuration.nix disko.nixosModules.disko ]; }; }; }
Rode:
$ nix flake lock
Para registrar a mudança na flake.lock.
Nice. Agora vamos adicionar as configurações específicas da máquina (partições, módulos de kernel). Vamos fazer isso num arquivo separado da configuration.nix
, para separar o “what it runs” e o “where it runs”.
Crie um arquivo hardware-configuration.nix
:
{modulesPath, ...}: { imports = [(modulesPath + "/profiles/qemu-guest.nix")]; boot = { initrd.availableKernelModules = ["ata_piix" "uhci_hcd"]; kernelModules = ["kvm-intel"]; }; # Nossas partições disko.devices.disk.main = { device = "/dev/vda"; type = "disk"; content = { type = "gpt"; partitions = { boot = { size = "1M"; type = "EF02"; }; esp = { size = "512M"; type = "EF00"; content = { type = "filesystem"; format = "vfat"; mountpoint = "/boot"; }; }; root = { size = "100%"; content = { type = "filesystem"; format = "ext4"; mountpoint = "/"; }; }; }; }; }; }
Não se preocupe muito se isso parece complexo. A maior parte desse arquivo foi gerado automaticamente. A parte do disko
define quais nossas partições.
Precisamos importar esse arquivo da nossa configuration.nix
. Também vamos tirar a senha 123456
, e habilitar SSH. Edite ela:
{ imports = [ ./hardware-configuration.nix ]; networking = { hostName = "factorio-server"; useDHCP = true; }; system.stateVersion = "25.05"; nixpkgs = { hostPlatform = "x86_64-linux"; config.allowUnfree = true; }; services.factorio = { enable = true; openFirewall = true; }; services.openssh = { enable = true; settings = { PermitRootLogin = "yes"; PasswordAuthentication = false; }; }; users.users.root = { openssh.authorizedKeys.keys = [ # Troque para sua chave. "<SUA CHAVE SSH PUBLICA>" ]; }; }
Lembre-se de trocar a chave SSH para a sua.
Gabriel, por que temos a chave SSH em dois lugares?
A do terraform define qual vai ser a chave autorizada assim que a máquina é provisionada. Essa chave será usada pela instalação inicial do nixos-anywhere. A do NixOS é qual será a chave autorizada após a instalação (e nescessária para rebuilds).
No repositório (link no fim do post), fizemos de um jeito mais robusto: o terraform cria a chave.
Certo, agora vamos configurar o nixos-anywhere pelo terraform. Crie um nixos.tf
:
module "deploy" { source = "github.com/nix-community/nixos-anywhere//terraform/all-in-one" nixos_system_attr = ".#nixosConfigurations.factorio-server.config.system.build.toplevel" nixos_partitioner_attr = ".#nixosConfigurations.factorio-server.config.system.build.diskoScript" debug_logging = true instance_id = mgc_virtual_machine_instances.factorio_server.id target_host = mgc_virtual_machine_instances.factorio_server.network.public_address install_user = "debian" }
Adicionamos um novo módulo externo, então rode novamente o init:
$ tofu init
Certinho! Estamos prontos. Vamos aplicar a configuração do Terraform:
$ tofu apply
Aguarde alguns minutos (geralmente menos de 5). O NixOS anywhere irá instalar NixOS na VM, e aplicar nossa configuração!
Sempre que você modificar a configuração, basta dar apply novamente, ele irá detectar a mudança e fazer alterações na VM conforme nescessário.
Sucesso! Podemos usar o IP agora para jogar factorio:
Fechamento
Espero que esse tutorial tenha ajudado você a ver algumas das coisas que são possíveis no modelo declarativo!
A magia da coisa é que qualquer um pode rodar tofu apply
e ter um servidor exatamente igual. Fazendo as mudanças nos arquivos e rodando apply, você garante que não existe nenhum passo de setup (e.g. instale coisa X, altere arquivo Y) além de simplesmente ter os arquivos .tf
e .nix
.
O versão final está disponível no repositório: GitHub - Misterio77/hackathon-mgc-factorio-terraform
Feedback é muito bem vindo, e fico a disposição para qualquer dúvida!
Beijos,
Gab
Top comments (0)