Konfigurowanie klastra Nomad przy użyciu Consul i integracja z Gitlabem

Wprowadzenie

W ostatnim czasie popularność Kubernetesa gwałtownie rośnie – coraz więcej projektów go wdraża. Chciałem poruszyć temat orkiestratora takiego jak Nomad: jest idealny do projektów, które korzystają już z innych rozwiązań firmy HashiCorp, na przykład Vault i Consul, a same projekty nie są skomplikowane pod względem infrastruktury. W tym materiale będzie zawarta instrukcja instalacji Nomada, połączenia dwóch węzłów w klaster, a także integracji Nomada z Gitlabem.

Konfigurowanie klastra Nomad przy użyciu Consul i integracja z Gitlabem

Stanowisko badawcze

Trochę o stanowisku testowym: wykorzystywane są trzy serwery wirtualne o charakterystyce 2 procesorów, 4 pamięci RAM, dysku SSD 50 Gb, połączone we wspólną sieć lokalną. Ich nazwy i adresy IP:

  1. nomad-livelinux-01: 172.30.0.5
  2. nomad-livelinux-02: 172.30.0.10
  3. consul-livelinux-01: 172.30.0.15

Instalacja Nomada, Konsul. Tworzenie klastra Nomadów

Zacznijmy od podstawowej instalacji. Chociaż konfiguracja była prosta, opiszę ją ze względu na spójność artykułu: zasadniczo utworzono ją na podstawie wersji roboczych i notatek, aby w razie potrzeby można było do nich szybko uzyskać dostęp.

Zanim zaczniemy praktykę, omówimy część teoretyczną, ponieważ na tym etapie ważne jest zrozumienie przyszłej struktury.

Mamy dwa węzły nomadyczne i chcemy je połączyć w klaster, a w przyszłości potrzebne będzie nam także automatyczne skalowanie klastrów - do tego będzie nam potrzebny Consul. Dzięki temu narzędziu grupowanie i dodawanie nowych węzłów staje się bardzo prostym zadaniem: utworzony węzeł Nomad łączy się z agentem Consul, a następnie łączy się z istniejącym klastrem Nomad. Dlatego na początek zainstalujemy serwer Consul, skonfigurujemy podstawową autoryzację http dla panelu WWW (domyślnie jest on bez autoryzacji i można uzyskać do niego dostęp pod adresem zewnętrznym), a także samych agentów Consul na serwerach Nomad, po czym przejdziemy tylko do Nomada.

Instalacja narzędzi HashiCorp jest bardzo prosta: zasadniczo po prostu przenosimy plik binarny do katalogu bin, konfigurujemy plik konfiguracyjny narzędzia i tworzymy jego plik serwisowy.

Pobierz plik binarny Consul i rozpakuj 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 współpracować z Consulem, musimy utworzyć unikalny klucz za pomocą polecenia keygen:

root@consul-livelinux-01:~# consul keygen

Przejdźmy do ustawienia konfiguracji Consula, tworząc katalog /etc/consul.d/ o następującej strukturze:

/etc/consul.d/
├── bootstrap
│   └── config.json

W katalogu bootstrap będzie znajdował się plik konfiguracyjny config.json - w nim ustawimy ustawienia Consula. To zawiera:

{
"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"]
}

Przyjrzyjmy się głównym dyrektywom i ich znaczeniu osobno:

  • bootstrap: PRAWDA. Umożliwiamy automatyczne dodawanie nowych węzłów, jeżeli są one połączone. Zwracam uwagę, że nie wskazujemy tutaj dokładnej liczby oczekiwanych węzłów.
  • serwer: PRAWDA. Włącz tryb serwera. Konsul na tej maszynie wirtualnej będzie w tej chwili jedynym serwerem i masterem, a maszyna wirtualna Nomada będzie klientami.
  • centrum danych: dc1. Określ nazwę centrum danych, w którym chcesz utworzyć klaster. Musi być identyczny zarówno na klientach, jak i na serwerach.
  • Szyfrowanie: Twój klucz. Klucz, który musi być unikalny i pasować na wszystkich klientach i serwerach. Wygenerowano przy użyciu polecenia consul keygen.
  • start_dołącz. Na tej liście wskazujemy listę adresów IP, z którymi zostanie nawiązane połączenie. Na chwilę obecną pozostawiamy jedynie własny adres.

W tym momencie możemy uruchomić consul za pomocą wiersza poleceń:

root@consul-livelinux-01:~# /usr/local/bin/consul agent -config-dir /etc/consul.d/bootstrap -ui

Jest to dobry sposób na debugowanie już teraz, jednak z oczywistych powodów nie będziesz mógł używać tej metody na bieżąco. Utwórzmy plik usługi do zarządzania Consulem poprzez systemd:

root@consul-livelinux-01:~# nano /etc/systemd/system/consul.service

Zawartość pliku 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.target

Uruchom Consul poprzez systemctl:

root@consul-livelinux-01:~# systemctl start consul

Sprawdźmy: nasza usługa musi być uruchomiona i 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         dc1  <all>

Kolejny etap: instalacja Nginx oraz konfiguracja proxy i autoryzacji http. Instalujemy nginx poprzez menedżera pakietów i w katalogu /etc/nginx/sites-enabled tworzymy plik konfiguracyjny consul.conf o następującej 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 "Password-protected Area";
    }
}

Nie zapomnij utworzyć pliku .htpasswd i wygenerować dla niego nazwę użytkownika i hasło. Pozycja ta jest wymagana, aby panel WWW nie był dostępny dla każdego, kto zna naszą domenę. Jednak podczas konfiguracji Gitlaba będziemy musieli z tego zrezygnować – w przeciwnym razie nie będziemy mogli wdrożyć naszej aplikacji na Nomadzie. W moim projekcie zarówno Gitlab, jak i Nomad działają tylko w szarej sieci, więc tutaj nie ma takiego problemu.

Na pozostałych dwóch serwerach instalujemy agenty Consul zgodnie z poniższą instrukcją. Powtarzamy kroki 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/

Analogicznie do poprzedniego serwera tworzymy katalog na pliki konfiguracyjne /etc/consul.d o następującej strukturze:

/etc/consul.d/
├── client
│   └── config.json

Zawartość 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", # локальный адрес вм
    "start_join": ["172.30.0.15"], # удаленный адрес консул сервера
    "ports": {
      "dns": 53
     }

Zapisz zmiany i przejdź do konfiguracji pliku usługi, jego zawartości:

/etc/systemd/system/consul.service:

[Unit]
Description="HashiCorp Consul - A service mesh solution"
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.target

Uruchamiamy consul na serwerze. Teraz po uruchomieniu powinniśmy zobaczyć skonfigurowaną usługę w członkach nsul. Będzie to oznaczać, że pomyślnie połączył się z klastrem jako klient. Powtórz to samo na drugim serwerze, a następnie możemy rozpocząć instalację i konfigurację Nomada.

Bardziej szczegółową instalację Nomada opisano w jego oficjalnej dokumentacji. Istnieją dwie tradycyjne metody instalacji: pobranie pliku binarnego i kompilacja ze źródła. Wybiorę pierwszą metodę.

Operacja: Projekt rozwija się bardzo szybko, często wydawane są nowe aktualizacje. Być może do czasu ukończenia tego artykułu zostanie wydana nowa wersja. Dlatego przed lekturą polecam sprawdzić aktualną wersję Nomada i pobrać ją.

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.d

Po rozpakowaniu otrzymamy plik binarny Nomad o wadze 65 MB - należy go przenieść do /usr/local/bin.

Utwórzmy katalog danych dla Nomada i edytujmy jego plik usługi (najprawdopodobniej na początku nie będzie istniał):

root@nomad-livelinux-01:~# mkdir --parents /opt/nomad
root@nomad-livelinux-01:~# nano /etc/systemd/system/nomad.service

Wklej 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.target

Nie spieszymy się jednak z uruchomieniem nomada – nie stworzyliśmy jeszcze 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 katalogów będzie wyglądać następująco:

/etc/nomad.d/
├── nomad.hcl
└── server.hcl

Plik nomad.hcl powinien zawierać następującą konfigurację:

datacenter = "dc1"
data_dir = "/opt/nomad"

Zawartość pliku serwer.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ędziesz musiał zmienić wartość dyrektywy http.

Ostatnią rzeczą na tym etapie jest skonfigurowanie Nginxa do proxy i skonfigurowanie 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 "Password-protected Area";
        }
        
}

Teraz możemy uzyskać dostęp do panelu WWW poprzez sieć zewnętrzną. Połącz się i przejdź do strony serwerów:

Konfigurowanie klastra Nomad przy użyciu Consul i integracja z Gitlabem
Zdjęcie 1. Lista serwerów w klastrze Nomad

Oba serwery zostały pomyślnie wyświetlone w panelu, to samo zobaczymy w wynikach polecenia statusu węzła nomad:

Konfigurowanie klastra Nomad przy użyciu Consul i integracja z Gitlabem
Zdjęcie 2. Dane wyjściowe polecenia statusu węzła nomad

A co z Konsulem? Przyjrzyjmy się. Przejdź do panelu sterowania Consul, na stronę węzłów:
Konfigurowanie klastra Nomad przy użyciu Consul i integracja z Gitlabem
Zdjęcie 3. Lista węzłów w klastrze Consul

Teraz mamy przygotowanego Nomada współpracującego z Konsulem. W ostatnim etapie przejdziemy do zabawniejszej części: skonfigurowania dostawy kontenerów Docker z Gitlab do Nomada, a także omówienia niektórych innych charakterystycznych funkcji.

Tworzenie Gitlab Runnera

Aby wdrożyć obrazy dockera w Nomadzie, użyjemy osobnego modułu uruchamiającego, w którym znajduje się plik binarny Nomad (tutaj, nawiasem mówiąc, możemy zauważyć kolejną cechę aplikacji Hashicorp - indywidualnie są one pojedynczym plikiem binarnym). Prześlij go do katalogu runner. Stwórzmy dla niego prosty plik Dockerfile z następującą 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}

Dzięki temu będziemy mieli dostępny obraz runnera Nomada w Rejestrze Gitlab, teraz możemy przejść bezpośrednio do repozytorium projektu, stworzyć Pipeline i skonfigurować zadanie nomada Nomada.

Konfiguracja projektu

Zacznijmy od akt pracy dla Nomada. Mój projekt w tym artykule będzie dość prymitywny: będzie składał się z jednego zadania. 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}' < project.nomad > 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: manual

W tym przypadku wdrożenie odbywa się ręcznie, ale można je skonfigurować w celu zmiany zawartości katalogu projektu. Pipeline składa się z dwóch etapów: montażu obrazu i jego wdrożenia w nomadzie. W pierwszym etapie składamy obraz dockera i wrzucamy go do naszego Rejestru, a w drugim uruchamiamy naszą pracę 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" {}
                }
            }
        }
    }
}

Pamiętaj, że mam prywatny rejestr i aby pomyślnie pobrać obraz okna dokowanego, muszę się do niego zalogować. Najlepszym rozwiązaniem w tym przypadku jest podanie loginu i hasła do Vault, a następnie zintegrowanie go z Nomadem. Nomad natywnie obsługuje Vault. Ale najpierw zainstalujmy niezbędne polityki dla Nomada w samym Vault; można je pobrać:

# 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.json

Teraz po stworzeniu niezbędnych polityk dodamy integrację z Vault w bloku zadań w pliku job.nomad:

vault {
  enabled = true
  address = "https://vault.domain.name:8200"
  token = "token"
}

Korzystam z autoryzacji tokenem i rejestruję ją bezpośrednio tutaj, istnieje również możliwość określenia tokena jako zmiennej przy uruchamianiu agenta nomad:

$ VAULT_TOKEN=<token> nomad agent -config /path/to/config

Teraz możemy używać kluczy z Vault. Zasada działania jest prosta: w zadaniu Nomad tworzymy plik, w którym będą przechowywane wartości zmiennych, np.:

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
}

Dzięki temu prostemu podejściu możesz skonfigurować dostarczanie kontenerów do klastra Nomad i pracować z nim w przyszłości. Powiem, że w pewnym stopniu sympatyzuję z Nomadem – bardziej nadaje się do małych projektów, gdzie Kubernetes może narobić dodatkowej komplikacji i nie wykorzysta w pełni swojego potencjału. Ponadto Nomad jest idealny dla początkujących — jest łatwy w instalacji i konfiguracji. Jednak podczas testów na niektórych projektach napotykam problem z jego wczesnymi wersjami - wielu podstawowych funkcji po prostu nie ma lub nie działają poprawnie. Wierzę jednak, że Nomad będzie się dalej rozwijał i w przyszłości nabędzie funkcje potrzebne każdemu.

Autor: Ilya Andreev, pod redakcją Alexeya Zhadana i zespołu Live Linux


Źródło: www.habr.com

Dodaj komentarz