Pull to refresh

Домашняя лаборатория

Reading time7 min
Views12K

Угадай, данную статью написал ChatGPT или нет?

Хотите потестировать приложение, или опробовать в работе инструмент? В этой статье опишу то, как организовал тестовый стенд на Linux. Стенд поддерживает работу с доменами, умеет генерировать TLS сертификаты, легко масштабируется, окружение строится по принципе IaaC, не требует много ресурсов, легко разворачивается скриптами или SCM, есть UI, не зависит от внешних сервисов.

Я являюсь системным инженером и наиболее эффективным языком для инструкций считаю "птичий" технический язык. Содержимое представляет собой последовательность команд с вкраплением комментариев, по которым коллега может однозначно понять этапы построения окружения без лишних слов. А дополнительную информацию о перечисленных инструментах можно легко получить уже из документации и после чего внести правки для тестового стенда. Кроме примеров кода и команд опишу те плюсы и те минусы, которые свойственны этой архитектуре, как бы громко это не звучало.

Конфигурация обыденная и не представляет ценности или сложности с точки зрения моего опыта, но может пригодится начинающим инженерам или даже опытным программистам, которые пишут код для сервисов или используют сервисную архитектуру для решения задач, но не погружаются в технические детали.

Используемые инструменты

Nomad — https://www.nomadproject.io
Consul — https://www.consul.io
Vault — https://www.vaultproject.io
Traefik — https://traefik.io
Packer — https://www.packer.io
Docker — http://docker.io

Плюсы

  • Набор инструментов с легкостью умещается на небольшой виртуальной машине

  • Инженер использует на практике современные подходы по релизу и выкладке программного обеспечения

  • В случае необходимости стенд легко масштабируется в боевое окружение. Отлично подходит для MVP проекта

Минусы

  • Не подходит для отладки "на лету", но такая возможность есть

  • Не пользуется популярностью

Краткое описание

Код приложения собирается с помощью Packer в образ и размещается на локальном сервере (аналог Docker Registry). На данном этапе можно потренироваться с версионированием и работой в различных окружениях.

Приложение запускается в виде контейнера Docker и вместе с тем появляется возможность оркестрации, т. е. множественных запусков и стратегий обновления контейнеров с помощью Nomad. Секретные данные передаются в контейнер шаблонами go-template или через переменные окружения из Vault.

В случае Web-приложения или приложения с административным интерфейсом в Traefik создаются точки входа HTTPS с сертификатами TLS от Let'sEncrypt.

Как будет проходить настройка

Я буду приводить примеры установки и настройки сервера для Ubuntu, но различия с другими дистрибутивами будут только на этапе установки пакетов.

Виртуальная машина

Для тестового стенда подойдет виртуальная машина с 2 ядрами, 4Г памяти и диском объемом 20Г. Можете оценить объем используемой памяти на примере моего стенда:

 3236 root     traefik traefik                  N/A    1.37%    1.37%    1.37%
 3488 root     bin/console bot run              N/A    1.16%    1.28%    1.42%
 3416 root     bin/console bot run              N/A    1.17%    1.28%    1.42%
  858 root     /usr/bin/containerd              N/A    1.49%    1.49%    1.52%
 3300 nobody   /bin/prometheus --config.fi      N/A    1.56%    1.56%    1.56%
  986 root     /usr/bin/dockerd -H unix://      N/A    2.13%    2.13%    2.21%
  928 consul   /usr/bin/consul agent -conf      N/A    2.35%    2.35%    2.35%
 3840 root     /usr/bin/nomad agent -confi      N/A    1.57%    1.67%    2.48%
  934 vault    /usr/bin/vault server -conf      N/A    5.45%    5.45%    5.45%
-------------------------------------------------------------------------------
   88 12                                        N/A   25.49%   28.31%   45.90%

Это полностью запущенный стенд состоящий из 5 сервисов на 4Г памяти (45% занято). Дороже всего обходится секретность.

Установка инструментов и репозиториев

https://developer.hashicorp.com/nomad/downloads
https://docs.docker.com/engine/install/ubuntu/

Hidden text

Я не учитываю блокировки доступа из России.

mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
curl -fsSL https://apt.releases.hashicorp.com/gpg | gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com \
    $(lsb_release -cs) main" > /etc/apt/sources.list.d/hashicorp.list
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) stable" > /etc/apt/sources.list.d/docker.list

Устанавливаем пакеты

apt update
apt install nomad vault consul docker-ce docker-compose packer jq

Настройка Docker + Docker Registry

systemctl start docker

Сейчас нам нужно запустить локальное хранилище образов, которое будет работать идентично публичному облаку или внутреннему в компании.

Создаем файл с описанием сервиса docker-compose.yml

cat >docker-compose.yml <<EndOfText
version: "3.9"
services:
  registry:
    container_name: registry
    image: registry:2
    ports:
      - 5000:5000
    volumes:
      - ./config.yml:/etc/docker/registry/config.yml
      - /var/lib/registry:/var/lib/registry
EndOfText

И файл с конфигурацией сервиса config.yml

cat >config.yml <<EndOfText
version: 0.1
log:
  fields:
    service: registry
storage:
  cache:
    blobdescriptor: inmemory
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3
EndOfText

После запуска наши образы будут храниться в папке /var/lib/registry

Запускаем

docker-compose up -d

Настройка Consul

Генерируем ключ шифрования

CONSUL_KEY=$(consul keygen)

Создаем файл /etc/consul.d/lab_consul.hcl

cat >/etc/consul.d/lab_consul.hcl <<EndOfText
client_addr = "0.0.0.0"
acl {
  enabled = true
}
ui_config {
  enabled = true
}
bind_addr = "127.0.0.1"
bootstrap_expect = 1
server = true
encrypt = "$CONSUL_KEY"
EndOfText

Запускаем

systemctl restart consul; sleep 10
consul acl bootstrap --format json > ./acl-token-bootstrap-consul.json
export CONSUL_HTTP_TOKEN=`cat ./acl-token-bootstrap-consul.json | jq -r ".SecretID"`

Проверяем

# consul members
Node  Address         Status  Type    Build   Protocol  DC   Partition  Segment
test  127.0.0.1:8301  alive   server  1.14.0  2         dc1  default    <all>

Панель по адресу

echo "http://$(curl -s ifconfig.me):8500"

Настройка Vault

Создаем файл /etc/vault.d/vault.hcl

cat >/etc/vault.d/vault.hcl <<EndOfText
ui = true
storage "file" {
  path = "/opt/vault/data"
}
listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_disable = "true"
}
api_addr = "http://127.0.0.1:8200"
cluster_addr = "https://127.0.0.1:8201"
EndOfText

Запускаем

systemctl restart vault; sleep 10
export VAULT_ADDR='http://127.0.0.1:8200'
vault operator init -key-threshold 1 -key-shares 1 -format json > ./acl-token-bootstrap-vault.json
export VAULT_TOKEN=$(cat ./acl-token-bootstrap-vault.json | jq -r ".root_token")
export VAULT_UNSEAL_KEY=$(cat ./acl-token-bootstrap-vault.json | jq -r ".unseal_keys_b64 | .[]")
vault operator unseal $VAULT_UNSEAL_KEY
vault login $VAULT_TOKEN

В Vault нам потребуется подключить модуль для хранения наших секретов

vault secrets enable -version=2 kv

Теперь сохраним токен Consul в хранилище Vault

vault kv put kv/consul/token value=$CONSUL_HTTP_TOKEN

Панель по адресу

echo "http://$(curl -s ifconfig.me):8200"

Настройка Nomad

Создаем файл /etc/nomad.d/lab_nomad.hcl

cat >/etc/nomad.d/lab_nomad.hcl <<EndOfText
consul {
  address = "127.0.0.1:8500"
  token   = "$CONSUL_HTTP_TOKEN"
}
acl {
  enabled = true
}
telemetry {
  publish_allocation_metrics = true
  publish_node_metrics       = true
}
plugin "docker" {
  config {
    volumes {
      enabled = true
    }
  }
}
vault {
  enabled = true
  address = "http://localhost:8200"
  token = "$VAULT_TOKEN"
}
EndOfText

Запускаем

systemctl restart nomad; sleep 10
nomad acl bootstrap -json > ./acl-token-bootstrap-nomad.json
export NOMAD_TOKEN=`cat ./acl-token-bootstrap-nomad.json | jq -r ".SecretID"`

Панель по адресу

echo "http://$(curl -s ifconfig.me):4646"

Запуск Traefik

Создаем файл traefik.nomad

export MY_MAIL="some@domain.ltd"
cat >traefik.nomad <<EndOfText
job "traefik" {
  datacenters = ["dc1"]
  type        = "service"
  group "traefik" {
    count = 1
    network {
      port "http"  {static = 80}
      port "https" {static = 443}
      port "api"   {static = 8081}
    }
    service {
      name = "traefik"
      port = "api"
      tags = ["traefik.enable=true", "traefik.port=8081"]
    }
    task "traefik" {
      driver = "docker"
      config { 
        image        = "traefik:v2.2"
        network_mode = "host"
        volumes = [ "local/traefik.yaml:/etc/traefik/traefik.yaml" ]
      }
      template {
        destination = "local/traefik.yaml"
        data = <<EOF
entryPoints:
  http:
    address: ':80'
  https:
    address: ':443'
  traefik:
    address: ':8081'
api:
  dashboard: true
  insecure: true
log:
  level: DEBUG
certificatesResolvers:
  myresolver:
    acme:
      email: $MY_MAIL
      storage: /data/acme.json
      httpChallenge:
        entryPoint: http
providers:
  consulCatalog:
    prefix: traefik
    exposedByDefault: false
    endpoint:
      address: 127.0.0.1:8500
      scheme: http
      token: {{with secret "kv/consul/token"}}{{.Data.data.value}}{{end}}
      tls:
        insecureSkipVerify: true
EOF
      }
    }
  }
}
EndOfText

Проверяем наш конфиг и применяем

nomad plan traefik.nomad
nomad run traefik.nomad

Панель по адресу

echo "http://$(curl -s ifconfig.me):8081"

Сборка приложения

Создаем файл whoami.pkr.hcl

cat >whoami.pkr.hcl <<EndOfText
packer {
  required_plugins {
    docker = {
      version = ">= 1.0.1"
      source = "github.com/hashicorp/docker"
    }
  }
}
source "docker" "whoami" {
  image  = "traefik/whoami"
  commit = true
  run_command = [
    "-d", "-i", "--rm", "-t", "--", "{{.Image}}"
  ]
}
build {
  sources = ["source.docker.whoami"]
  post-processors {
    post-processor "docker-tag" {
      repository =  "localhost:5000/dev/whoami"
        tags = ["0.0.1"]
      }
    post-processor "docker-push" {}
  }
}
EndOfText

И запускаем сборку

packer init .
packer build .

В конце сборки будет указан адрес образа для следующего шага

Запуск тестового приложения

Создаем файл whoami.nomad

cat >whoami.nomad <<EndOfText
job "whoami" {
  datacenters = ["dc1"]
  type        = "service"
  group "whoami" {
    count = 1
    network {
      port "http" {to = 80}
    }
    service {
      name = "whoami"
      port = "http"
      tags = [
        "traefik.enable=true",
        "traefik.http.routers.whoami.entrypoints=http",
        "traefik.http.routers.whoami.rule=host(\`$(curl -s ifconfig.me)\`)",
        "traefik.http.routers.whoami.middlewares=whoami-to-https",
        "traefik.http.middlewares.whoami-to-https.redirectscheme.scheme=https",
        "traefik.http.middlewares.whoami-to-https.redirectscheme.permanent=true",
        "traefik.http.routers.whoami-https.entrypoints=https",
        "traefik.http.routers.whoami-https.rule=host(\`$(curl -s ifconfig.me)\`)",
        "traefik.http.routers.whoami-https.tls.certresolver=myresolver"]
    }
    task "whoami" {
      driver = "docker"
      config {
        image = "localhost:5000/dev/whoami:0.0.1"
        ports = ["http"]
      }
    }
  }
}
EndOfText

Проверяем и запускаем наш сервис

nomad plan whoami.nomad
nomad run whoami.nomad

Проверить можно по адресу

echo "http://$(curl -s ifconfig.me)"

После запуска обработать напильником...

Думаю на примере готового приложения дальше будет разобраться попроще.

Пример скрипта для быстрой настройки стенда

Скрипт не идемпотентный, будте осторожны! Запускайте только на новой виртуальной машине с Ubuntu.

curl https://git.sr.ht/~wilful/Shared/blob/master/init.sh | bash -x

Посмотреть все доступные адреса панелей можно так

journalctl -t laboratory -xe

Что можно улучшить?

https://www.waypointproject.io?

Оригинал тут

Tags:
Hubs:
Total votes 20: ↑17 and ↓3+14
Comments8

Articles