Introducere
Recent times have seen a rapid rise in the popularity of Kubernetes, with more and more projects implementing it. However, I want to touch upon an orchestrator like Nomad: it is an excellent fit for projects that already use other solutions from HashiCorp, like Vault and Consul, especially when the projects are not complex in terms of infrastructure. This material will provide a guide on installing Nomad, merging two nodes into a cluster, and integrating Nomad with GitLab.

Banc de testare
A bit about the test environment: three virtual servers are used with the following specifications: 2 CPU, 4 RAM, 50 Gb SSD, connected to a common local network. Their names and IP addresses are:
- nomad-livelinux-01: 172.30.0.5
- nomad-livelinux-02: 172.30.0.10
- consul-livelinux-01: 172.30.0.15
Installation of Nomad and Consul. Creating a Nomad cluster
Let's proceed with the basic installation. Despite the simplicity of the process, I will describe it for the completeness of the article: it was essentially created from drafts and notes for quick access when needed.
Before we start with the practical part, let's discuss the theoretical aspect because, at this stage, understanding the future structure is crucial.
We have two Nomad nodes and we want to merge them into a cluster. In the future, we will need automatic scaling of the cluster — for this, we will need Consul. With this tool, clustering and adding new nodes becomes a very simple task: the created Nomad node connects to the Consul agent and then connects to the existing Nomad cluster. Therefore, we will first install the Consul server, set up basic HTTP authentication for the web panel (which is by default unauthenticated and can be accessed externally), and also install the Consul agents on the Nomad servers, after which we will proceed to Nomad.
Installing HashiCorp tools is very straightforward: essentially, we just move the binary file into the bin directory, configure the tool's configuration file, and create its service file.
We download the Consul binary file and unpack it in the user's home directory:
root@consul-livelinux-01:~# wget https://releases.hashicorp.com/consul/1.5.0/consul_1.5.0_linux_amd64.zip
root@consul-livelinux-01:~# unzip consul_1.5.0_linux_amd64.zip
root@consul-livelinux-01:~# mv consul /usr/local/bin/Now we have a ready binary file of consul for further configuration.
To work with Consul, we need to create a unique key using the keygen command:
root@consul-livelinux-01:~# consul keygen
Să trecem la configurarea Consul, creând directorul /etc/consul.d/ cu următoarea structură:
/etc/consul.d/
├── bootstrap
│ └── config.jsonÎn directorul bootstrap va fi fișierul de configurare config.json — aici vom specifica setările pentru Consul. Conținutul său:
{
"bootstrap": true,
"server": true,
"datacenter": "dc1",
"data_dir": "/var/consul",
"encrypt": "your-key",
"log_level": "INFO",
"enable_syslog": true,
"start_join": ["172.30.0.15"]
}Să analizăm în detaliu directivele principale și semnificațiile lor:
- bootstrap: true. Activăm adăugarea automată a noilor noduri în cazul în care se conectează. Menționez că nu specificăm aici numărul exact de noduri așteptate.
- server: true. Activăm modul server. Consul pe această mașină virtuală va fi în acest moment singurul server și master, iar VM-urile Nomad vor fi clienți.
- datacenter: dc1. Specificăm numele centrului de date pentru crearea clusterului. Acesta trebuie să fie identic atât la clienți, cât și la servere.
- encrypt: your-key. Cheia, care de asemenea trebuie să fie unică și să coincidă pe toate clienții și serverele. Este generată cu comanda consul keygen.
- start_join. În această listă specificăm adresele IP la care se va conecta. Momentan lăsăm doar adresa noastră.
În această etapă putem porni consul folosind linia de comandă:
root@consul-livelinux-01:~# /usr/local/bin/consul agent -config-dir /etc/consul.d/bootstrap -uiAceasta este o modalitate decentă de depanare acum, totuși, nu o putem folosi constant din motive evidente. Să creăm un fișier de serviciu pentru a gestiona Consul prin systemd:
root@consul-livelinux-01:~# nano /etc/systemd/system/consul.service
Conținutul fișierului consul.service:
[Unit]
Description=Consul Startup process
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash -c '/usr/local/bin/consul agent -config-dir /etc/consul.d/bootstrap -ui'
TimeoutStartSec=0
[Install]
WantedBy=default.targetPornim Consul prin systemctl:
root@consul-livelinux-01:~# systemctl start consul
Verificăm: serviciul nostru ar trebui să fie pornit, iar prin executarea comenzii consul members ar trebui să vedem serverul nostru:
root@consul-livelinux:/etc/consul.d# consul members
consul-livelinux 172.30.0.15:8301 alive server 1.5.0 2 dc1Următoarea etapă: instalarea Nginx și configurarea proxy-ului, autorizației http. Instalăm nginx prin managerul de pachete și în directorul /etc/nginx/sites-enabled creăm fișierul de configurare consul.conf cu următorul conținut:
upstream consul-auth {
server localhost:8500;
}
server {
server_name consul.doman.name;
location / {
proxy_pass http://consul-auth;
proxy_set_header Host $host;
auth_basic_user_file /etc/nginx/.htpasswd;
auth_basic "Password-protected Area";
}
}Nu uitați să creați un fișier .htpasswd și să generați un nume de utilizator și o parolă pentru acesta. Această etapă este necesară pentru a asigura că panoul web nu este accesibil tuturor celor care știu domeniul nostru. Cu toate acestea, în timpul configurării Gitlab, va trebui să renunțăm la această opțiune — altfel nu ne vom putea desfășura aplicația în Nomad. În proiectul meu, atât Gitlab, cât și Nomad se află doar în rețeaua internă, așa că nu avem această problemă aici.
Pe celelalte două servere instalăm agenții Consul conform următoarei instrucțiuni. Repetăm acțiunile cu fișierul binar:
root@nomad-livelinux-01:~# wget https://releases.hashicorp.com/consul/1.5.0/consul_1.5.0_linux_amd64.zip
root@nomad-livelinux-01:~# unzip consul_1.5.0_linux_amd64.zip
root@nomad-livelinux-01:~# mv consul /usr/local/bin/Similar cu serverul anterior, creăm un director pentru fișierele de configurare /etc/consul.d cu următoarea structură:
/etc/consul.d/
├── client
│ └── config.jsonConținutul fișierului config.json:
{
"datacenter": "dc1",
"data_dir": "/opt/consul",
"log_level": "DEBUG",
"node_name": "nomad-livelinux-01",
"server": false,
"encrypt": "your-private-key",
"domain": "livelinux",
"addresses": {
"dns": "127.0.0.1",
"https": "0.0.0.0",
"grpc": "127.0.0.1",
"http": "127.0.0.1"
},
"bind_addr": "172.30.0.5", # adresa locală a VM
"start_join": ["172.30.0.15"], # adresa serverului Consul de la distanță
"ports": {
"dns": 53
}Salvăm modificările și trecem la configurarea fișierului de serviciu, conținutul său:
/etc/systemd/system/consul.service:
[Unit]
Description="HashiCorp Consul - O soluție pentru rețelele de servicii"
Documentation=https://www.consul.io/
Requires=network-online.target
After=network-online.target
[Service]
User=root
Group=root
ExecStart=/usr/local/bin/consul agent -config-dir=/etc/consul.d/client
ExecReload=/usr/local/bin/consul reload
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.targetPornim consul pe server. Acum, după ce a fost pornit, ar trebui să vedem serviciul configurat în nsul members. Aceasta va însemna că s-a conectat cu succes la cluster ca și client. Repetați aceeași procedură pe al doilea server și după aceasta putem începe instalarea și configurarea Nomad.
Instalarea mai detaliată a Nomad este descrisă în documentația oficială. Există două metode tradiționale de instalare: descărcarea fișierului binar și compilarea din surse. Eu voi alege prima metodă.
Notă: proiectul se dezvoltă foarte repede, noi actualizări sunt lansate frecvent. Este posibil ca până la finalizarea articolului să fie lansată o nouă versiune. Prin urmare, recomand să verificați versiunea actuală a Nomad înainte de a citi și să o descărcați pe aceea.
root@nomad-livelinux-01:~# wget https://releases.hashicorp.com/nomad/0.9.1/nomad_0.9.1_linux_amd64.zip
root@nomad-livelinux-01:~# unzip nomad_0.9.1_linux_amd64.zip
root@nomad-livelinux-01:~# mv nomad /usr/local/bin/
root@nomad-livelinux-01:~# nomad -autocomplete-install
root@nomad-livelinux-01:~# complete -C /usr/local/bin/nomad nomad
root@nomad-livelinux-01:~# mkdir /etc/nomad.dDupă extragere, vom obține un fișier binar Nomad cu o greutate de 65 MB — acesta trebuie mutat în /usr/local/bin.
Vom crea un director de date pentru Nomad și vom edita fișierul său de serviciu (cel mai probabil nu va exista la început):
root@nomad-livelinux-01:~# mkdir --parents /opt/nomad
root@nomad-livelinux-01:~# nano /etc/systemd/system/nomad.serviceIntroducem următoarele linii acolo:
[Unit]
Description=Nomad
Documentation=https://nomadproject.io/docs/
Wants=network-online.target
After=network-online.target
[Service]
ExecReload=/bin/kill -HUP $MAINPID
ExecStart=/usr/local/bin/nomad agent -config /etc/nomad.d
KillMode=process
KillSignal=SIGINT
LimitNOFILE=infinity
LimitNPROC=infinity
Restart=on-failure
RestartSec=2
StartLimitBurst=3
StartLimitIntervalSec=10
TasksMax=infinity
[Install]
WantedBy=multi-user.targetCu toate acestea, să nu ne grăbim să pornim nomad — nu am creat încă fișierul său de configurare:
root@nomad-livelinux-01:~# mkdir --parents /etc/nomad.d
root@nomad-livelinux-01:~# chmod 700 /etc/nomad.d
root@nomad-livelinux-01:~# nano /etc/nomad.d/nomad.hcl
root@nomad-livelinux-01:~# nano /etc/nomad.d/server.hcl
Structura finală a directorului va fi următoarea:
/etc/nomad.d/
├── nomad.hcl
└── server.hclFișierul nomad.hcl ar trebui să conțină următoarea configurație:
datacenter = "dc1"
data_dir = "/opt/nomad"Conținutul fișierului server.hcl:
server {
enabled = true
bootstrap_expect = 1
}
consul {
address = "127.0.0.1:8500"
server_service_name = "nomad"
client_service_name = "nomad-client"
auto_advertise = true
server_auto_join = true
client_auto_join = true
}
bind_addr = "127.0.0.1"
advertise {
http = "172.30.0.5"
}
client {
enabled = true
}Nu uitați să modificați fișierul de configurare pe al doilea server — va fi necesar să schimbați valoarea directivei http.
Ultimul pas în această etapă este configurarea Nginx pentru proxy și stabilirea autentificării http. Conținutul fișierului nomad.conf:
upstream nomad-auth {
server 172.30.0.5:4646;
}
server {
server_name nomad.domain.name;
location / {
proxy_pass http://nomad-auth;
proxy_set_header Host $host;
auth_basic_user_file /etc/nginx/.htpasswd;
auth_basic "Password-protected Area";
}
}Acum putem accesa panoul web prin rețeaua externă. Ne conectăm și mergem la pagina servers:

Imaginea 1. Lista serverelor din clusterul Nomad
Ambele servere se afișează cu succes în panou, același lucru îl vom vedea în ieșirea comenzii nomad node status:

Imaginea 2. Ieșirea comenzii nomad node status
Ce se întâmplă din partea Consul? Să aruncăm o privire. Trecem la panoul de control Consul, pe pagina nodes:

Imaginea 3. Lista nodurilor din clusterul Consul
Acum avem un Nomad pregătit, care funcționează împreună cu Consul. În etapa finală, ne vom ocupa de cel mai interesant aspect: vom configura livrarea containerelor Docker din Gitlab în Nomad și vom discuta despre unele dintre celelalte caracteristici distinctive ale acestuia.
Crearea Gitlab Runner
Pentru a depune imaginile Docker în Nomad, vom folosi un runner separat cu fișierul binar al lui Nomad în interior (de altfel, merită să menționăm o altă caracteristică a aplicațiilor Hashicorp — fiecare dintre ele reprezintă un fișier binar unic). Încărcați-l în directorul runner-ului. Vom crea un Dockerfile simplu cu următorul conținut:
FROM alpine:3.9
RUN apk add --update --no-cache libc6-compat gettext
COPY nomad /usr/local/bin/nomad
În același proiect, creăm .gitlab-ci.yml:
variables:
DOCKER_IMAGE: nomad/nomad-deploy
DOCKER_REGISTRY: registry.domain.name
stages:
- build
build:
stage: build
image: ${DOCKER_REGISTRY}/nomad/alpine:3
script:
- tag=${DOCKER_REGISTRY}/${DOCKER_IMAGE}:latest
- docker build --pull -t ${tag} -f Dockerfile .
- docker push ${tag}Rezultatul va fi o imagine a runner-ului Nomad disponibilă în Gitlab Registry, acum putem trece direct la repository-ul proiectului, vom crea un Pipeline și vom configura job-ul Nomad.
Configurarea proiectului
Să începem cu fișierul job’s pentru Nomad. Proiectul meu în acest articol va fi destul de primitiv: va consta dintr-o singură sarcină. Conținutul .gitlab-ci va fi următorul:
variables:
NOMAD_ADDR: http://nomad.address.service:4646
DOCKER_REGISTRY: registry.domain.name
DOCKER_IMAGE: example/project
stages:
- build
- deploy
build:
stage: build
image: ${DOCKER_REGISTRY}/nomad-runner/alpine:3
script:
- tag=${DOCKER_REGISTRY}/${DOCKER_IMAGE}:${CI_COMMIT_SHORT_SHA}
- docker build --pull -t ${tag} -f Dockerfile .
- docker push ${tag}
deploy:
stage: deploy
image: registry.example.com/nomad/nomad-runner:latest
script:
- envsubst '${CI_COMMIT_SHORT_SHA}' job.nomad
- cat job.nomad
- nomad validate job.nomad
- nomad plan job.nomad || if [ $? -eq 255 ]; then exit 255; else echo "success"; fi
- nomad run job.nomad
environment:
name: production
allow_failure: false
when: manualAici, desfășurarea se realizează manual, dar o puteți configura pentru a modifica conținutul directorului proiectului. Pipeline-ul constă în două etape: construirea imaginii și desfășurarea acesteia în Nomad. În prima etapă, construim imaginea Docker și o împingem în Registry-ul nostru, iar în a doua etapă, lansăm job-ul nostru în Nomad.
job "monitoring-status" {
datacenters = ["dc1"]
migrate {
max_parallel = 3
health_check = "checks"
min_healthy_time = "15s"
healthy_deadline = "5m"
}
group "zhadan.ltd" {
count = 1
update {
max_parallel = 1
min_healthy_time = "30s"
healthy_deadline = "5m"
progress_deadline = "10m"
auto_revert = true
}
task "service-monitoring" {
driver = "docker"
config {
image = "registry.domain.name/example/project:${CI_COMMIT_SHORT_SHA}"
force_pull = true
auth {
username = "gitlab_user"
password = "gitlab_password"
}
port_map {
http = 8000
}
}
resources {
network {
port "http" {}
}
}
}
}
}Te rog să observi că am un Registry privat și pentru a reuși să extrag imaginea Docker, trebuie să mă autentific în acesta. Cea mai bună soluție în acest caz este să stochez numele de utilizator și parola în Vault, urmată de integrarea acestuia cu Nomad. Nomad suportă nativ Vault. Dar mai întâi, în Vault, vom stabili politicile necesare pentru Nomad, acestea pot fi încărcate:
# Download the policy and token role
$ curl https://nomadproject.io/data/vault/nomad-server-policy.hcl -O -s -L
$ curl https://nomadproject.io/data/vault/nomad-cluster-role.json -O -s -L
# Write the policy to Vault
$ vault policy write nomad-server nomad-server-policy.hcl
# Create the token role with Vault
$ vault write /auth/token/roles/nomad-cluster @nomad-cluster-role.jsonAcum, odată ce am creat politicile necesare, în blocul task din fișierul job.nomad, vom adăuga integrarea cu Vault:
vault {
enabled = true
address = "https://vault.domain.name:8200"
token = "token"
}Folosesc autentificarea prin token și îl scriu direct aici, există de asemenea opțiunea de a specifica token-ul ca o variabilă atunci când pornesc agentul nomad:
$ VAULT_TOKEN= nomad agent -config /path/to/config
Acum putem utiliza cheile din Vault. Principiul de funcționare este simplu: creăm un fișier în job-ul Nomad, care va conține valorile variabilelor, de exemplu:
template {
data = <<EOH
{{with secret "secrets/pipeline-keys"}}
REGISTRY_LOGIN="{{ .Data.REGISTRY_LOGIN }}"
REGISTRY_PASSWORD="{{ .Data.REGISTRY_LOGIN }}{{ end }}"
EOH
destination = "secrets/service-name.env"
env = true
}Astfel, printr-o abordare simplă, putem configura livrarea containerelor în cluster-ul Nomad și să lucrăm cu acesta ulterior. Pot spune că, într-o anumită măsură, simpatizez cu Nomad — este mai potrivit pentru proiecte mici, acolo unde Kubernetes ar putea cauza complicații suplimentare și nu își va valorifica pe deplin potențialul. În plus, Nomad este ideal pentru începători — se instalează și se configurează ușor. Totuși, în timpul testării unor proiecte, m-am confruntat cu probleme în versiunile sale timpurii — multe funcții de bază lipseau sau funcționau incorect. Cu toate acestea, cred că Nomad se va dezvolta în continuare și în viitor va acumula funcțiile necesare tuturor.
Autor: Ilya Andreev, sub editarea lui Alexey Zhadan și echipa „Live Linux”
Sursa: habr.com
