Wprowadzenie
Ostatnio rośnie popularność Kubernetes – coraz więcej projektów wdraża go w swoich systemach. Chciałbym jednak poruszyć temat innego orkiestratora, jakim jest Nomad: doskonale nadaje się dla projektów, w których już wykorzystuje się inne rozwiązania firmy HashiCorp, takie jak Vault i Consul, a same projekty nie mają skomplikowanej infrastruktury. W niniejszym materiale znajdziesz instrukcję instalacji Nomad, połączenia dwóch węzłów w klaster oraz integracji Nomad z GitLab.

Stojak testowy
Kilka słów o środowisku testowym: używane są trzy serwery wirtualne o parametrach 2 CPU, 4 RAM, 50 Gb SSD, połączone w jedną lokalną sieć. Ich nazwy i adresy IP to:
- nomad-livelinux-01: 172.30.0.5
- nomad-livelinux-02: 172.30.0.10
- consul-livelinux-01: 172.30.0.15
Instalacja Nomad, Consul. Tworzenie klastra Nomad
Zacznijmy od podstawowej instalacji. Mimo jej prostoty, opiszę ją dla pełności artykułu: w zasadzie została stworzona z roboczych notatek dla szybkiego dostępu w razie potrzeby.
Zanim przejdziemy do praktyki, omówmy część teoretyczną, ponieważ na tym etapie ważne jest zrozumienie przyszłej struktury.
Mamy dwa węzły Nomad i chcemy połączyć je w klaster, a także w przyszłości będziemy potrzebować automatycznego skalowania klastra — do tego potrzebujemy Consul. Dzięki temu narzędziu klastrowanie i dodawanie nowych węzłów staje się bardzo prostym zadaniem: stworzony węzeł Nomad łączy się z agentem Consul, a następnie łączy się z istniejącym klastrem Nomad. Dlatego najpierw zainstalujemy serwer Consul, skonfigurujemy podstawową autoryzację http dla panelu webowego (domyślnie jest bez autoryzacji i może być dostępny pod adresem zewnętrznym), a także same agenty Consul na serwerach Nomad, po czym przystąpimy do instalacji Nomad.
Instalacja narzędzi firmy HashiCorp jest bardzo prosta: w zasadzie przenosimy plik binarny do katalogu bin, konfigurujemy plik konfiguracyjny narzędzia i tworzymy plik usługi.
Pobieramy plik binarny Consul i rozpakowujemy go do katalogu domowego użytkownika:
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/Teraz mamy gotowy plik binarny consul do dalszej konfiguracji.
Aby pracować z Consul, musimy stworzyć unikalny klucz za pomocą polecenia keygen:
root@consul-livelinux-01:~# consul keygen
Przejdźmy do konfiguracji Consul, tworząc katalog /etc/consul.d/ z następującą strukturą:
/etc/consul.d/
├── bootstrap
│ └── config.jsonW katalogu bootstrap znajduje się plik konfiguracyjny config.json — w nim ustawimy konfigurację Consul. Jego zawartość:
{
"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"]
}Przeanalizujmy osobno kluczowe dyrektywy i ich znaczenia:
- bootstrap: true. Włączamy automatyczne dodawanie nowych węzłów w momencie ich podłączenia. Zauważę, że nie podajemy tutaj dokładnej liczby oczekiwanych węzłów.
- server: true. Włączamy tryb serwera. Consul na tej maszynie wirtualnej będzie obecnie jedynym serwerem i mistrzem, a maszyny wirtualne Nomad’a będą klientami.
- datacenter: dc1. Podajemy nazwę centrum danych dla stworzenia klastra. Powinna być identyczna zarówno na klientach, jak i na serwerach.
- szyfruj: your-key. Klucz, który również powinien być unikalny i zgodny na wszystkich klientach i serwerach. Generowany za pomocą polecenia consul keygen.
- start_join. W tej liście podajemy adresy IP, do których będzie realizowane połączenie. Na razie zostawiamy tylko własny adres.
Na tym etapie możemy uruchomić consul za pomocą wiersza poleceń:
root@consul-livelinux-01:~# /usr/local/bin/consul agent -config-dir /etc/consul.d/bootstrap -uiTo dobry sposób na debugowanie w tej chwili, jednakże, nie możemy używać tego sposobu na stałe z oczywistych powodów. Stwórzmy plik serwisowy do zarządzania Consulem przez systemd:
root@consul-livelinux-01:~# nano /etc/systemd/system/consul.service
Zawartość pliku consul.service:
[Unit]
Description=Proces uruchamiania Consul
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.targetUruchamiamy Consul za pomocą systemctl:
root@consul-livelinux-01:~# systemctl start consul
Sprawdzamy: nasz serwis powinien być uruchomiony, a wykonując polecenie consul members powinniśmy zobaczyć nasz serwer:
root@consul-livelinux:/etc/consul.d# consul members
consul-livelinux 172.30.0.15:8301 alive server 1.5.0 2 dc1Następnym krokiem jest instalacja Nginx oraz skonfigurowanie proxy i autoryzacji HTTP. Instalujemy nginx przez menedżera pakietów i w katalogu /etc/nginx/sites-enabled tworzymy plik konfiguracyjny consul.conf z następującą zawartością:
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 "Strefa chroniona hasłem";
}
}Nie zapomnij utworzyć pliku .htpasswd i wygenerować dla niego nazwę użytkownika oraz hasło. Ten krok jest wymagany, aby panel webowy nie był dostępny dla wszystkich, którzy znają naszą domenę. Jednak przy konfigurowaniu Gitlab będziemy musieli z tego zrezygnować — w przeciwnym razie nie będziemy mogli wdrożyć naszej aplikacji w Nomad. W moim projekcie zarówno Gitlab, jak i Nomad znajdują się tylko w szarej sieci, więc nie ma tu takiego problemu.
Na pozostałych dwóch serwerach instalujemy agentów Consul zgodnie z następującą instrukcją. Powtarzamy działania z plikiem binarnym:
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/Podobnie jak w przypadku poprzedniego serwera, tworzymy katalog dla plików konfiguracyjnych /etc/consul.d z następującą strukturą:
/etc/consul.d/
├── client
│ └── config.jsonZawartość pliku 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", # lokalny adres vm
"start_join": ["172.30.0.15"], # zdalny adres serwera konsul
"ports": {
"dns": 53
}Zapisujemy zmiany i przechodzimy do ustawień pliku serwisowego, jego zawartość:
/etc/systemd/system/consul.service:
[Unit]
Description="Rozwiązanie do siatki serwisowej HashiCorp Consul"
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.targetUruchamiamy consul na serwerze. Teraz, po uruchomieniu, powinniśmy w nsul members zobaczyć skonfigurowaną usługę. Będzie to oznaczać, że pomyślnie połączył się z klastrem jako klient. Powtórz to samo na drugim serwerze, a następnie będziemy mogli przystąpić do instalacji i konfiguracji Nomad.
Szczegółowa instalacja Nomad opisana jest w jego oficjalnej dokumentacji. Są dwa tradycyjne sposoby instalacji: pobranie pliku binarnego i kompilacja ze źródeł. Wybiorę pierwszy sposób.
Uwaga: projekt rozwija się bardzo szybko, często wydawane są nowe aktualizacje. Możliwe, że w momencie ukończenia artykułu pojawi się nowa wersja. Dlatego zalecam sprawdzenie aktualnej wersji Nomad przed przeczytaniem i pobranie именно ее.
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.dPo wypakowaniu otrzymamy plik binarny Nomad'a o wadze 65 MB — należy go przenieść do /usr/local/bin.
Stwórzmy katalog data dla Nomad'a i edytujmy jego plik serwisowy (prawdopodobnie nie będzie on istniał na początku):
root@nomad-livelinux-01:~# mkdir --parents /opt/nomad
root@nomad-livelinux-01:~# nano /etc/systemd/system/nomad.serviceWklejamy tam następujące linie:
[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.targetJednak nie spieszmy się z uruchamianiem nomad — jeszcze nie stworzyliśmy jego pliku konfiguracyjnego:
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
Ostateczna struktura katalogu będzie wyglądała następująco:
/etc/nomad.d/
├── nomad.hcl
└── server.hclPlik nomad.hcl powinien zawierać następującą konfigurację:
datacenter = "dc1"
data_dir = "/opt/nomad"Zawartość pliku 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
}Nie zapomnij zmienić pliku konfiguracyjnego na drugim serwerze — tam będzie trzeba zmienić wartość dyrektywy http.
Ostatnim etapem jest konfiguracja Nginx do proxy i ustawienia autoryzacji HTTP. Zawartość pliku 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 "Obszar chroniony hasłem";
}
}Teraz możemy uzyskać dostęp do panelu webowego przez zewnętrzną sieć. Łączymy się i przechodzimy na stronę serwerów:

Obrazek 1. Lista serwerów w klastrze Nomad
Oba serwery wyświetlają się poprawnie na panelu, to samo zobaczymy w wynikach polecenia nomad node status:

Obrazek 2. Wynik polecenia nomad node status
Co z perspektywy Consul? Sprawdźmy. Przechodzimy do panelu zarządzania Consul, na stronę nodów:

Obrazek 3. Lista nodów w klastrze Consul
Teraz mamy przygotowanego Nomada, który działa w połączeniu z Consul. W ostatnim etapie przejdziemy do najciekawszego: skonfigurujemy przesyłanie kontenerów Docker z GitLab do Nomada, a także omówimy niektóre z jego innych wyróżniających cech.
Tworzenie GitLab Runnera
Do wdrażania obrazów Dockera w Nomadzie użyjemy osobnego runnera z plikiem binarnym Nomada wewnątrz (tutaj, nota bene, warto zauważyć jeszcze jedną cechę aplikacji Hashicorp — osobno stanowią one pojedynczy plik binarny). Pobierz go do katalogu runnera. Utworzymy dla niego najprostszy Dockerfile o następującej zawartości:
FROM alpine:3.9
RUN apk add --update --no-cache libc6-compat gettext
COPY nomad /usr/local/bin/nomad
W tym samym projekcie tworzymy .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}W rezultacie będziemy mieć dostępny obraz runnera Nomada w GitLab Registry, teraz możemy przejść do repozytorium projektu, stworzyć Pipeline i skonfigurować zadanie nomad Job Nomada.
Konfiguracja projektu
Zacznijmy od pliku job's dla Nomada. Mój projekt w tym artykule będzie dość prymitywny: będzie składał się z jednej taski. Zawartość .gitlab-ci będzie następująca:
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: manualTutaj wdrożenie odbywa się w trybie ręcznym, ale możesz dostosować je do zmiany zawartości katalogu projektu. Pipeline składa się z dwóch etapów: budowy obrazu i jego wdrożenia w Nomadzie. Na pierwszym etapie budujemy obraz dockerowy i wypychamy go do naszego Registry, podczas gdy na drugim uruchamiamy nasze zadanie w Nomadzie.
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" {}
}
}
}
}
}Zwróć uwagę, że mam prywatny Registry i do pomyślnego pobrania obrazu Dockera muszę się w nim autoryzować. Najlepszym rozwiązaniem w tym przypadku jest umieszczenie loginu i hasła w Vault, a następnie integracja z Nomad. Nomad natively obsługuje Vault. Najpierw jednak w samej aplikacji Vault zdefiniujemy potrzebne polityki dla Nomad, które można załadować:
# 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.jsonTeraz, tworząc potrzebne polityki, dodamy integrację z Vault w bloku task w pliku job.nomad:
vault {
enabled = true
address = "https://vault.domain.name:8200"
token = "token"
}Używam autoryzacji na podstawie tokena i wpisuję go bezpośrednio tutaj, istnieje też możliwość podania tokena jako zmiennej przy uruchamianiu agenta nomad:
$ VAULT_TOKEN= nomad agent -config /path/to/config
Teraz możemy korzystać z kluczy z Vault. Zasada działania jest prosta: tworzymy plik w zadaniu Nomad, który będzie przechowywał wartości zmiennych, na przykład:
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
}Takim prostym podejściem można skonfigurować wdrożenie kontenerów w klastrze Nomad i pracować z nim w przyszłości. Powiem, że w pewnym sensie sympatyzuję z Nomad — bardziej pasuje do małych projektów, gdzie Kubernetes może powodować dodatkowe trudności i nie zostanie w pełni wykorzystany. Ponadto, Nomad doskonale nadaje się dla początkujących — jest łatwy do zainstalowania i skonfigurowania. Jednak podczas testowania na niektórych projektach napotkałem problem z jego wczesnymi wersjami — wielu podstawowych funkcji po prostu brakuje lub działają one niepoprawnie. Niemniej jednak, uważam, że Nomad będzie się dalej rozwijać i w przyszłości wzbogaci się w potrzebne wszystkim funkcje.
Autor: Ilia Andriejew, redagowane przez Alieksieja Żadana i zespół „Live Linux”
Źródło: habr.com
