{"id":24839115,"url":"https://github.com/winand/y-y-devops-trainings-final","last_synced_at":"2025-03-26T04:32:19.538Z","repository":{"id":210396815,"uuid":"724307914","full_name":"Winand/y-y-devops-trainings-final","owner":"Winand","description":null,"archived":false,"fork":false,"pushed_at":"2023-12-02T14:58:22.000Z","size":21,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-31T06:37:37.645Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"HCL","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Winand.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-27T20:21:49.000Z","updated_at":"2023-12-02T13:19:09.000Z","dependencies_parsed_at":"2023-12-02T14:27:42.134Z","dependency_job_id":"c1f7f61f-91c3-426f-b6cc-9fb0ced1c02a","html_url":"https://github.com/Winand/y-y-devops-trainings-final","commit_stats":null,"previous_names":["winand/y-y-devops-trainings-final"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Winand%2Fy-y-devops-trainings-final","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Winand%2Fy-y-devops-trainings-final/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Winand%2Fy-y-devops-trainings-final/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Winand%2Fy-y-devops-trainings-final/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Winand","download_url":"https://codeload.github.com/Winand/y-y-devops-trainings-final/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245589265,"owners_count":20640254,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-31T06:36:45.960Z","updated_at":"2025-03-26T04:32:19.512Z","avatar_url":"https://github.com/Winand.png","language":"HCL","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Финальный проект\n_Заметки во время работы велись на [странице](https://winand.notion.site/3c7bbac3a4e0443fb3fc49b85f0f45c9?pvs=4) в Notion_.\n_Данный README.md создан на их основе после завершения мероприятия._\n\n## Определение расположения конфигурации\n_Изучение исполняемого файла проводилось Docker-контейнере Debian_\n_с опцией `--privileged`._\n\nПриложение bingo не запускается из-за отсутствия файла с конфигурацией\n(`bingo print_current_config` и `bingo run_server`). Определить, к каким\nфайлам обращается приложение можно [с помощью `strace`](https://stackoverflow.com/questions/56507809/how-to-determine-what-files-a-program-is-trying-to-open):\n```bash\napt update\napt install strace sudo \nstrace -fe openat sudo -u app ./bingo print_current_config\n```\nТак как приложение не запускается под `root` используем `sudo -u app`.\nВ выводе видно, что происходило обращение к `/opt/bingo/config.yaml`:\n```\n[pid   421] openat(AT_FDCWD, \"/opt/bingo/config.yaml\", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)\n```\n\n## Создание конфигурации\nСохраняем конфигурацию по умолчанию в нужное расположение:\n`./bingo print_default_config \u003e /opt/bingo/config.yaml`.\nВносим туда свой почтовый адрес при регистрации.\n\n## Расположение файла с логом\nТеперь при запуске возникает ошибка `panic: failed to build logger`.\nПовторно запускаем `strace`. Программа пытается открыть файл\n`/opt/bongo/logs/cad7532b03/main.log`. Экспериментально выясняем, что\nпуть зависит от указанного в конфигурации адреса почты, создаём\nсоответствующую директорию.\n\nВ дальнейшем было определено, что в лог выводится большое количество\nдебаг-сообщений, поэтому лог был выведен в _/dev/null_:\n`ln -s /dev/null /opt/bongo/logs/${LOGDIR?}/main.log`\n\n## Установка PostgreSQL\nЕсли запустить приложение без доступной СУБД возникает ошибка\n`Error: failed Init Core: failed NewDatabase: could not connect to any host from the list`.\n\nСначала для локального тестирования был установлен PostgreSQL 16\nв том же контейнере, в котором тестировалось приложение. Затем в\nотдельном контейнере был поднят _postgres:16-alpine_ с заданными переменными\n`POSTGRES_USER`, `POSTGRES_PASSWORD`, `POSTGRES_DB`. В файле _pg_hba.conf_\nв этом контейнере прописана строка `host all all all scram-sha-256`,\nпоэтому дополнительная настройка не требуется.\n\nУстановка PostgreSQL 16 локально:\n```bash\napt update\napt install lsb-release wget  # если не установлены\nsh -c 'echo \"deb https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main\" \u003e /etc/apt/sources.list.d/pgdg.list'\napt -y install gnupg\nwget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -\napt update\napt install postgresql\n\npg_lsclusters  # посмотреть версию и имя кластера\npg_ctlcluster 16 main start\n\napt install lsof\nlsof -Pi | grep postgres  # проверить, что Postgres слушает 5432\n```\n\n### Настройка СУБД\nСоздадим нового пользователя _bingo_ и БД для работы приложения, также нужно\nвнести соответствующие правки в файл конфигурации приложения:\n```\ncreate role bingo with login;  # разрешить подключение\nalter role bingo with password 'str0ng_passw0rd';  # задать пароль\n\ncreate database bingo owner bingo;\n```\nПроверяем работоспособность, подключившись _psql_: `psql -U bingo -h localhost -d bingo`.\n\nДля наполнения БД необходимо выполнить `./bingo prepare_db`.\n\n## Запуск приложения и поиск секретных кодов\nПри запуске выдаётся сообщение с первым кодом:\n```\nMy congratulations.\nYou were able to start the server.\nHere's a secret code that confirms that you did it.\n--------------------------------------------------\ncode:         yoohoo_server_launched\n--------------------------------------------------\n```\nПриложение работает на порте `16482` (что видно через `lsof -i`), который\nзависит от адреса почты. На главной странице расположен второй код:\n`index_page_is_awesome`.\n\nС помощью `strings bingo | grep \"code: \"` был найден третий код:\n`google_dns_is_not_http`.\n\n## Контейнеризация и автоматизация\nПриложение упаковывается в контейнер distroless и загружается с помощью GitHub Actions\nв заранее созданный реестр на Яндексе. При сборке используются `docker/login-action`\nи `docker/build-push-action`. Авторизация в реестре осуществляется с помощью\nOAuth токена. Токен, почтовый адрес и пароль БД заданы в secrets в репозитории.\n\n_Dockerfile_ является multi-stage. На первой стадии в шаблон конфигурации `config.yaml`\nподставляются необходимые переменные и создаются файлы и каталоги в `/opt`.\nТакже загружаются утилиты `wget`, `kill`, `sh` с _busybox.net_ для реализации\nлогики healthcheck (см. ниже). На втором этапе создаётся образ на основе\n_distroless/static-debian12_, куда копируется папка `/opt` и утилиты, а также\nзагружается исполняемый файл приложения. Здесь же указывается внутренний порт приложения.\n\n### Healthcheck\nПоскольку приложение может упасть, перезапуск контейнера обеспечивается опцией\n`restart: always` в файле Docker Compose (см. _terraform/docker-compose.yaml_).\nОднако приложение может перестать правильно функционировать без падения,\nв этом случае контейнер продолжит работать и перезапуск не произойдёт.\n\nДля решения этой проблемы в _Dockerfile_ добавлен специальный _healthcheck_,\nкоторый вместо простого определения статуса приложения завершает его выполнение\nпри обнаружении проблемы, что вызывает остановку контейнера и его перезапуск.\n\nПроверка статуса осуществляется обращением к эндпоинту `/ping`, если он не\nвозвращает статус `200`, то команда `kill 1` завершает работу приложения.\nПроверка происходит каждые 40 секунд (`--interval=40s`).\n\nПоскольку используется контейнер distroless для реализации логики healthcheck\nбыли добавлены утилиты `wget` и `kill`, а также `sh` для их запуска.\n\n## Облако\nДля развёртывания приложения используется Яндекс.Облако и Terraform.\n\n### Terraform\nДля создания ресурсов в облаке выполняем команду `terraform apply` в папке _terraform_.\n\nРеестр образов создан заранее, образ приложения загружается туда из GitHub Actions\n(см. **Контейнеризация и автоматизация**).\nID реестра Terraform получает из переменной `vars.registry_id` в файле\n`yc.auto.tfvars` (не добавлен в репозиторий). Также в этом файле указан ID облака\nи внутренний порт приложения для балансировщика.\n\nПриложение поднимается на двух инстансах COI с помощью `yandex_compute_instance_group`.\nНа инстансах запускается `terraform/docker-compose.yaml`, где настроен автоматический\nперезапуск при падении контейнера (`restart: always`).\n\nПоверх инстансов работает балансировщик `yandex_lb_network_load_balancer`,\nкоторый проверяет доступность инстанса по эндпоинту `/ping`.\n\n### Настройка БД\nСУБД вручную развёрнута на инстансе `yandex_compute_instance.db-inst-1`\n(с образом _debian-10_) как в разделе выше **Установка PostgreSQL**.\nCоздана БД _bingo_ и соответствующий пользователь _bingo_.\n\nПомимо самой СУБД установлен PgBouncer:\n```bash\napt install pgbouncer lsof\nlsof -Pi | grep postgres  # проверка\n```\nВ файл `vim /etc/pgbouncer/pgbouncer.ini` добавлена БД и разрешение слушать\nподключения со всех интерфейсов:\n```\n[databases]\nbingo = host=localhost dbname=bingo\n[pgbouncer]\n;; Слушать все интерфейсы\nlisten_addr = *\n```\nВ файл `vim /etc/pgbouncer/userlist.txt` добавлен пользователь в формате\n`\"bingo\" \"hash-value\"`. Хеш пароля для этого получен через _psql_ запросом\n`select passwd from pg_shadow where usename='bingo';`.\n\nДля применения настроек перезапускаем PgBouncer `service pgbouncer restart`.\n\n### Наполнение БД\nДля наполнения БД необходимо выполнить `./bingo prepare_db`.\nДля этого используется специальный инстанс `yandex_compute_instance.bingo-db-init`.\nВ нём запускается Docker Compose `terraform/docker-compose-db-init.yaml`, где\nуказан аргумент `command: prepare_db`. Поднять конкретный инстанс можно командой\n`terraform apply -target yandex_compute_instance.bingo-db-init`.\n\nПроцесс наполнения отслеживался в логах контейнера на этом инстансе через SSH.\nПосле завершения процесса этому ресурсу присвоено\n[`count=0`](https://stackoverflow.com/q/53194995) в файле `main.tf`,\nпоскольку он не нужен для работы сервиса.\n\n### Оптимизация БД\nВо время запроса `/api/session` выполнил в _psql_ `SELECT * FROM pg_stat_activity WHERE wait_event IS NOT NULL AND backend_type = 'client backend';`.\n\nСУБД выполняла медленный запрос `SELECT sessions.id, sessions.start_time, customers.id, customers.name, customers.surname, customers.birthday, customers.email, movies.id, movies.name, movies.year, movies.duration FROM sessions INNER JOIN customers ON sessions.customer_id = customers.id INNER JOIN movies ON sessions.movie_id = movies.id ORDER BY movies.year DESC, movies.name ASC, customers.id, sessions.id DESC LIMIT 100000;`\n\nПосмотрел план выполнения с помощью `EXPLAIN` и создал индексы:\n```sql\ncreate index idx_customer on sessions(customer_id);\ncreate index idx_id on customers(id);\ncreate index idx_movie_id on movies(id);\nCREATE INDEX idx_movies_year_desc ON movies (year DESC);\nCREATE INDEX idx_movies_name_asc ON movies (name ASC);\ncreate index idx_movie on sessions(movie_id);\ncreate index idx_session_is on sessions(id);\n```\n\nУвеличил объём используемой памяти для внутренних операций в\n`/etc/postgresql/16/main/postgresql.conf`:\n```\nwork_mem = 64MB\n```\nВ конце перезапускаем СУБД `service postgresql restart` и проверяем объём памяти через _psql_ `SHOW work_mem;`","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinand%2Fy-y-devops-trainings-final","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwinand%2Fy-y-devops-trainings-final","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwinand%2Fy-y-devops-trainings-final/lists"}