Угадай, данную статью написал 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?

Оригинал тут

Комментарии (8)


  1. gionet
    21.01.2023 17:02

    Пора открывать на хабре отдельную рубрику, где как в советском документальном фильме будем вместе решать - ИИ написал\нарисовал, или человек ...


    1. Wiljaeden Автор
      21.01.2023 17:26

      Не могу не поддержать, ситуация веет тм, что мы отключим комменты. Я же знаю вы ли это написали... ;)


  1. Dart55
    21.01.2023 17:43

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

    По моему мнению ChatGPT просто инструмент, такой же как googl или карандаш. Если автор использует ChatGPT для написания статьи, статья всё равно написана автором.


    1. develmax
      21.01.2023 18:27

      Вот если интегрировать ChatGPT с хабром и настроить интеграцию на написание статей раз в некоторое время, то никто и не заметит)) так же как и можно отвечать на комментарии под статьей, используя ChatGPT без участия человека.


  1. texder
    21.01.2023 18:26
    +1

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

    ChatGPT, конечно, изумительный, но когда его загоняешь в угол, начинает формулировать по кругу, либо пересказывать твой же вопрос в виде ответа. Такое вот перекидывание мячика получается.

    Вот так играл сегодня. Осторожно, много картинок

    Простите, что поделился. Интересный диалог получился, но не писать же из-за этого статью )


  1. EskakDolar
    21.01.2023 20:15

    А сам ChatGPT может сказать он это написал или нет ?


  1. dimitrii_z
    21.01.2023 23:27

    Сам я не админ и не совсем спец в теме, но даже по беглому просмотру текста бросаются в глаза:

    • странные технически неуместные фразы ("Дороже всего обходится секретность.", "...публичному облаку или внутреннему в компании", "для хранения наших секретов" и пр.), незавершённые команды с пропущенными словами ("Панель по адресу")

    • элементарные ошибки (но может пригодится, Let'sEncrypt, 4Г памяти и диском объемом 20Г и пр.)

    • ну и вишенка: Скрипт не идемпотентный, будте осторожны!

    В итоге: на 99% уверен, что статью писал ИИ, ну или очень косноязычный админ после весёлой пятницы ))


  1. perfect_genius
    22.01.2023 19:19

    Такими темпами кто-то 1 апреля ради шутки выложит статью, написанную им самим, а не нейросетью.