yusorokin microservices repository
- Настроил интеграция с travis;
- Установил docker, docker-compose, docker-machine;
- Запустил первый контейнер hello-world;
- Научился выводить список контейнеров (docker ps) и образов (docker images);
- Познакомился с командами docker start и attach;
- Изучил параметры запуска контейнера (-i, -t, -d);
- Использовал docker exec;
- Создал новый образ из измененного контейнера командой docker commit;
- Изучил docker kill, stop, system df, rm, rmi;
- Удалил все контейнеры и образы.
Описание контейнера в отличие от образа содержит в себе:
- Описание образа, из которого он был запущен;
- Параметра сети;
- Подключенные тома;
- Состояние контейнера;
- Пути к директориям контейнера (логи, хранилище, подключенные директории и пр.);
- Параметры запуска;
- Параметры хоста.
Описание образа в отличие от контейнера содержит:
- Описание образа;
- Описание репозитория образов;
- Описание родительского образа.
- Создал новый проект в GCE;
- Настроил gcloud для работы с новым проектом;
- Установил docker-machine;
- Создал в GCE машину с помощью docker-machine;
- Переключился на работу с удаленным докером
eval $(docker-machine env docker-host); - Проверил изоляцию PID namespace:
-
-
docker run --rm -ti tehbilly/htop docker run --rm --pid host -ti tehbilly/htop
-
-
- При использовании
--pid hostконтейнер получает доступ к процессам хоста;
- При использовании
- Включил изоляцию user namespace:
-
- В файл
/etc/docker/daemon.jsonзаписал строки:{ "userns-remap": "default" }
- В файл
-
- Перезапустил демон докера;
-
- При запуске докер создал пользователя dockremap:
$ id dockremap uid=112(dockremap) gid=116(dockremap) groups=116(dockremap)
- При запуске докер создал пользователя dockremap:
-
- Пользователю был присвоен диапазон пидов:
$ grep dockremap /etc/subuid dockremap:231072:65536 $ grep dockremap /etc/subgid dockremap:231072:65536
- Пользователю был присвоен диапазон пидов:
-
- Запустил контейнер hello-world, при этом в /var/lib/docker/ создался каталог с именем
<UID>.<GID>, который является по сути копией /var/lib/docker/ с правами под нового пользователя:$ sudo ls -ld /var/lib/docker/231072.231072/ drwx------ 11 231072 231072 11 Jun 21 21:19 /var/lib/docker/231072.231072/ $ sudo ls -l /var/lib/docker/231072.231072/ total 14 drwx------ 5 231072 231072 5 Jun 21 21:19 aufs drwx------ 3 231072 231072 3 Jun 21 21:21 containers drwx------ 3 root root 3 Jun 21 21:19 image drwxr-x--- 3 root root 3 Jun 21 21:19 network drwx------ 4 root root 4 Jun 21 21:19 plugins drwx------ 2 root root 2 Jun 21 21:19 swarm drwx------ 2 231072 231072 2 Jun 21 21:21 tmp drwx------ 2 root root 2 Jun 21 21:19 trust drwx------ 2 231072 231072 3 Jun 21 21:19 volumes
- Запустил контейнер hello-world, при этом в /var/lib/docker/ создался каталог с именем
-
- Благодаря этому обеспечивается лучшая изоляция процессов и их доступа к файлам, при этом процессы не знают о своих ограничениях. Тем самым предотвращается эскалация привилегий;
- Создал файлы шаблонов для деплоя приложения;
- Создал Dockerfile по инструкции и запустил сборку образа
docker build -t reddit:latest .; - Запустил контейнер
docker run --name reddit -d --network=host reddit:latest; - Получил ошибку при проверке о недоступности хоста, добавил правило файерволла через gcloud;
- Проверил работу приложения в контейнере;
- Добавил тэг к созданному образу
docker tag reddit:latest <your-login>/otus-reddit:1.0; - Загрузил образ в Docker hub
docker push <your-login>/otus-reddit:1.0; - Запустил контейнер на локальной машине
docker run --name reddit -d -p 9292:9292 <your-login>/otus-reddit:1.0; - Пожкспериментировал с командами докера в конце задания.
- Создал директории:
├── docker-monolith │ ├── infra │ │ ├── ansible │ │ ├── packer │ │ └── terrafrom - Написал плейбуки ansible для установки питона, пип, докера и запуска контейнера с приложением;
- Описал создание образа с помощью packer и создал образ;
- Описал в terraform инфраструктуру с деплоем приложения через ansible через провижининг terraform;
- Провижининг в терраформ можно отключить переменно
do_provision = falseи провижинить через ансибл самостоятельно.
- Добавил каталог src с сервисами, распакованными внутри;
- Добавил Dockerfile-ы для сервисов post-py, comment, ui;
- Скачал последний образ MongoDB через docker pull;
- Собрал образы всех сервисов;
- Сборка ui началась не с первого шага, так для шагов, идентичным сборке comment были использованы образы из кеша;
- Создал сеть
docker network create reddit; - Запустил контейнеры в созданной ранее сети и проверил работу приложения;
- Выполнил задание со * (1);
- Поменял содержимое ui/Dockerfile для уменьшения размера образа;
- Сборка началась со второго шага, так как базовый образ присутствовал, но команда RUN была изменена;
- Выполнил задание со * (2);
- Создал раздел
docker volume create reddit_dbи подключил его к монго-v reddit_db:/data/db; - Запустил контейнеры, создал пост, перезапустил контейнеры и убедился, что созданный ранее пост на месте.
- Запустил приложение с новыми сетевыми алиасами и переопределенными переменными окружения алиасов, проверил работоспособность:
docker run -d --network=reddit \ --network-alias=new_post_db \ --network-alias=new_comment_db mongo:latest && \ docker run -d --network=reddit \ --network-alias=new_post \ -e POST_DATABASE_HOST=new_post_db yurich00/post:1.0 && \ docker run -d --network=reddit \ --network-alias=new_comment \ -e COMMENT_DATABASE_HOST=new_comment_db yurich00/comment:1.0 && \ docker run -d --network=reddit \ -p 9292:9292 \ -e POST_SERVICE_HOST=new_post \ -e COMMENT_SERVICE_HOST=new_comment yurich00/ui:1.0
- Уменьшил образы до минимума, используя базовые образы
ruby:2.3-alpineиpython:3.6.0-alpine, минимальный набор пакетов и очистку кеша apk; - В результате получились следующие образы:
yurich00/ui 2.0_alpine 0f6ab492035c 7 seconds ago 136MB yurich00/post 2.0_alpine ad4917ef894f 4 minutes ago 109MB yurich00/comment 2.0_alpine 190278e4f748 13 minutes ago 134MB - Файлы полученных образов называются
Dockerfile.small.
- Запустил контейнер с параметром
--network none, проверил, что единственный доступный сетевой интерфейс у контейнера - это loopback; - Запустил контейнер с параметром
--network host, сравнил вывод команды ifconfig контейнера с выводом ifconfig хоста. Отличие только в том, что у контейнера после адреса IPv6 следует идентификатор области (сетевой интерфейс):inet6 addr: fe80::42:f8ff:fe94:c1eb%32710/64 Scope:Link
- Запустил несколько раз
docker run --network host -d nginx, следующие после первого контейнера, контейнеры завершали свое выполнение. Причину узнал, когда запустил еще один контейнер в интерактивном режимеdocker run --network host -ti nginx:2019/01/07 14:27:46 [emerg] 1#1: bind() to 0.0.0.0:80 failed (98: Address already in use) nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use) 2019/01/07 14:27:46 [emerg] 1#1: still could not bind() nginx: [emerg] still could not bind() - На докер-хосте выполнил команду
sudo ln -s /var/run/docker/netns /var/run/netns; - Запуская контейнеры в неймспейсах
noneиhost, и выполняяifconfigв каждом неймспейсе получил следующее:null:
# sudo ip netns 68a75034bdca # ip netns exec 68a75034bdca ifconfig lo ...host:
# sudo ip netns default # ip netns exec default ifconfig br-6630cc1a9a9e ... docker0 ... ens4 ... lo ... - Запустил проект с драйвером сети
bridge:docker run -d --network=reddit mongo:latest docker run -d --network=reddit <your-dockerhub-login>/post:1.0 docker run -d --network=reddit <your-dockerhub-login>/comment:1.0 docker run -d --network=reddit -p 9292:9292 <your-dockerhub-login>/ui:1.0 - Контейнеры не увидели друг-друга в сети, т.к. обращение было настроено по именам, а алиасов или имен контейнеров при запуске указано не было;
- Запустил контейнеры с алиасами:
docker run -d --network=reddit --network-alias=post_db --networkalias=comment_db mongo:latest docker run -d --network=reddit --network-alias=post <your-login>/post:1.0 docker run -d --network=reddit --network-alias=comment <your-login>/comment:1.0 docker run -d --network=reddit -p 9292:9292 <your-login>/ui:1.0 - Создал сети и запустил в них контейнеры:
docker network create back_net --subnet=10.0.2.0/24 docker network create front_net --subnet=10.0.1.0/24 docker run -d --network=front_net -p 9292:9292 --name ui <your-login>/ui:1.0 docker run -d --network=back_net --name comment <your-login>/comment:1.0 docker run -d --network=back_net --name post <your-login>/post:1.0 docker run -d --network=back_net --name mongo_db --network-alias=post_db --network-alias=comment_db mongo:latest - Схема не заработала, т.к. Docker при инициализации контейнера может подключить к нему только 1 сеть;
- Подключил контейнеры
postиcommentко второй сети и приложение заработало:docker network connect front_net post docker network connect front_net comment - Установил на докер-хосте bridge-utils;
- Выполнил
docker network ls, получил список созданных сетей; - Выполнил
ifconfig | grep br, увидел список мостов; - Посмотрел информацию о мостах с помощью
brctl show <interface>; - Выполнил
sudo iptables -nL -t -v nat, посмотрел информацию о маршрутах iptables; - Выполнил
ps ax | grep docker-proxy, нашел процесс docker-proxy; - Установил docker-compose;
- Создал docker-compose.yml;
- Выполнил:
export USERNAME=<your-login> docker-compose up -d docker-compose ps - Проверил работу приложения;
- Создал файл .env, внес туда переменные окружения версии образов приложения, порт UI и имя пользователя в Docker Hub. Продублировал файл в .env.example, внес .env в .gitignore;
- Узнал, как образуется имя запускаемого контейнера - имя текущей директории + имя образа + номер п/п. Имя контейнера можно изменить с помощью директивы container_name;
- Выполнил задание со *.
- Создал docker-compose.override.yml;
- Примонтировал в нем к каждому контейнеру его директорию с кодом приложения, что позволило вносить правки в код при запущенном контейнере;
- Переопределил команду для запуска руби-приложений директивой
command: puma --debug -w 2.
- Создал сервер GitLab использовав Terraform, скрипты лежат в директории
gitlab-ci/terraform; - Развернул GitLab на созданном сервере с помощью Ansible (
gitlab-ci/ansible). Плейбук для поднятия сервиса -site.yml, он включает в себя: -
- Установку python и pip;
-
- Установку docker;
-
- Создание требуемых директорий;
-
- Копирование шаблона docker-compose;
-
- Установку требуемых пакетов pip для работы с docker-compose;
-
- Поднятие контейнера Gitlab;
- Произвел настройку GitLab, создал группу и проект;
- Добавил созданный проект в remotes в локальный репозиторий, пушнул локальный репозиторий в гитлаб;
- Создал скрипт
.gitlab-ci.ymlи пушнул его в гитлаб; - Запустил runner на хосте с гитлаб;
- Зарегистрировал раннер и увидел, что пайплайн успешно выполнился;
- Склонировал репозиторий https://github.com/express42/reddit.git в текуший репозиторий, пушнул его в гитлаб;
- Изменил код скрипта
.gitlab-ci.yml, добавив тестирование руби-приложения; - Создал скрипт
reddit/simpletest.rb, добавил гемrack-testвGemfile; - Пушнул все изменения в гитлаб, проверил статус пайплайна;
- Выполнил задания со *.
- Написал плейбук для поднятия GitLab Runner, который выполняет:
-
- Установку python, pip и пакета pip docker;
-
- Запуск раннера в докере;
-
- Проверку уже зарегистрированного раннера и регистрацию его, если он не был ранее зарегистрирован;
- Синтегрировал Slack и GitLab в канал
https://devops-team-otus.slack.com/messages/CDAKS754G.
- Создал новый проект example2 в GitLab, включил раннер в этом проекте;
- Добавил в
.gitlab-ci.ymlокружение dev; - Определил два новых этапа staging и production с запуском в ручном режиме;
- Определил для этих этапов ограничения в виде регулярных выражений
/^\d+\.\d+\.\d+/, для запуска этих этапов только если запушен тег; - Добавил задание branch_review с условием запуска только если пуш был в ветку, отличную от master;
- Выполнил задания со *.
- Изменил задание
branch_reviewдля создания окружения в GCP: -
- Создал в GitLab CI переменные:
GC_CRED - json-ключ для сервисного аккаунта google; GC_PROJECT - ID проекта; GC_ZONE - зона, где будет создан инстанс;
- Создал в GitLab CI переменные:
-
- В качестве образа задания использовал
google/cloud-sdk:latest;
- В качестве образа задания использовал
-
- Настроил соединение с gcloud с помощью заданного в переменной GC_CRED ключа от сервисного аккаунта;
-
- Установил docker-machine;
-
- Создал с помощью docker-machine инстанс в GCP с docker на борту;
-
- Приаттачил docker-engine на раннере с engine на созданной в облаке машине;
-
- Добавил параметр окружения
on_stop: stop_branch_reviewдля запуска задания удаления машины в облаке при остановке текущего окружения;
- Добавил параметр окружения
- Для удаления удаленной машины создал новое задание
stop_branch_reviewс запуском по кнопке: -
- Использовал тот же образ
google/cloud-sdk:latest;
- Использовал тот же образ
-
- Настроил подключение через gcloud;
-
- Описал удаление инстанса с помощью gcloud и остановку окружения GitLab;
- Описал сборку приложения
docker-monolithв заданииbuild_job: -
- Для сборки docker-in-docker пришлось переподнять раннер с параметром
--docker-volumes /var/run/docker.sock:/var/run/docker.sockи использованием образаdocker;
- Для сборки docker-in-docker пришлось переподнять раннер с параметром
-
- Использовал переменные
DOCKER_LOGINиDOCKER_PASSдля хранение учтных данных;
- Использовал переменные
-
- Залогинился в docker с командой
echo ${DOCKER_PASS} | docker login --username ${DOCKER_LOGIN} --password-stdin;
- Залогинился в docker с командой
-
- Собрал образ
docker-monolithс тегом${DOCKER_LOGIN}/reddit:${CI_COMMIT_SHA};
- Собрал образ
-
- Пушнул собранный образ в свой докер-репозиторий;
- Деплой приложения описал также в задании
branch_review: -
- Запустил контейнер с помощью приаттаченного ранее docker-engine созданной машины в облаке -
docker run -d -p 9292:9292 ${DOCKER_LOGIN}/reddit:${CI_COMMIT_SHA}.
- Запустил контейнер с помощью приаттаченного ранее docker-engine созданной машины в облаке -
- Запустил Prometheus из готового образа;
- Ознакомился с веб-интерфейсом;
- Упорядочил директории;
- Создал докерфайл для Prometheus;
- Создал файл конфигурации
prometheus.yml; - Собрал образ prometheus;
- Собрал образы приложения reddit;
- Добавил сервис prometheus в
docker-compose.yml; - Поднял сервисы через docker-compose;
- Проверил в прометеус, что все таргеты в поднятом состоянии;
- Проверил healthcheck ui, он отображал 0, поправил алиасы БД;
- Остановил post, увидел, что сервис ui стал незодров, убедился, что это из-за post;
- Запустил post, мониторинг снова показал, что все хорошо;
- Определил node-exporter в
docker-compose.yml; - Добавил конфигурацию джоба в
prometheus.yml, пересобрал прометеус; - Перезапустил сервисы, посмотрел метрики node_exporter;
- Создал нагрузку на ЦП и убедился, что мониторнг отображает эту нагрузку;
- Пушнул образы в свой реджистри;
- Выполнил задания со *.
- Для мониторинга монги использовал percona/mongodb_exporter;
- Создал докерфайл с описанием билда;
- Добавил экспортер в
docker-compose.yml; - Добавил джоб в
prometheus.yml.
- Добавил balckbox_exporter в
docker-compose.yml; - Добавил джоб balckbox_exporter в
prometheus.ymlс целью мониторить 200-е ответы от сервисов.
- Создал Makefile с возможностью собирать все образы сразу и каждый по отдельности, а также пушить их в реджистри.
https://hub.docker.com/u/yurich00/
- Выделил из файла docker-compose в файл docker-compose-monitoring описание сервисов мониторинга;
- Добавил описание сервиса cAdvisor в compose-файл, добавил таргет cAdvisor в prometheus.yml;
- Запустил проект, пощупал интерфейс cAdvisor;
- Добавил Grafana в compose-файл;
- Запустил и настроил графану;
- Скачал с сайта графаны дэшборд с мониторингом докера, импортировал его в графану, сохранил его также в папке monitoring/grafana/dashboards;
- Добавил в prometheus.yml таргет posr с метриками приложения;
- Перезапустил мониторинги, добавил несколько постов в приложение и проверил, что метрики post работают;
- Создал дэшборд
UI service monitoring, добавил туда панельUI HTTP Requestsс метрикой ui_request_count; - Добавил панель
Rate of UI HTTP requests with errorс метрикой rate(ui_request_count{http_status=~"^[45].*"}[1m]), сгенерировал 400-х ошибок и убедился, что график их отображает; - Посмотрел историю изменений дэшюорда;
- Изменил панель
UI HTTP Requests, довив функцию rate к метрике (rate(ui_request_count[5m])), переименовал панель вRate of UI HTTP Requests; - Добавил новую панель
HTTP response time 95th percentileс метрикой histogram_quantile(0.95, sum(rate(ui_request_latency_seconds_bucket[5m])) by (le)); - Соханил дэшборд, экспортировал его и сохранил в папке monitoring/grafana/dashboards;
- Создал новый дэшборд
Business_Logic_Monitoring, добавил на него панельPosts Rateс метрикой rate(post_count[1h]); - Добавил панель
Comments Rateс метрикой rate(comment_count[1h]), сохранил и экспортировал в папку с дэшбордами; - Создал директорию monitoring/alertmanager, а в ней Dockerfile и config.yml;
- В config.yml описал интеграцию со Slack и маршрут алертинга, собрал образ;
- Добавил alertmanager в компоуз-файл;
- Создал в директории прометеус файл alerts.yml, где описал правило алертинга на падение инстансов;
- Добавил этот файл в Dockerfile, подключил его и описал подклчение к alertmanager в prometheus.yml, пересобрал образы;
- Перезапустил мониторинги и проверил работу алертов;
- Пушнул все собранные образы хаб;
- Выполнил часть заданий со *.
- Добавил в Makefile все собираемые образы;
- Метрики Docker Engine:
-
- Прописал в настройках докер-демона (/etc/docker/daemon.json)
{ "metrics-addr" : "0.0.0.0:9323", "experimental" : true }, перезапустил демон;
- Прописал в настройках докер-демона (/etc/docker/daemon.json)
-
- Добавил в promehteus.yml таргет с этими метриками;
-
- Метрики включают в себя в основном состояние docker engine, нет детальной информации по контейнерам в отличие от cAdvisor;
-
- Импортировал с сайта графаны готовый дэшборд
Docker Engine Metrics, сохранил его также в папке со всеми остальными дэшбордами;
- Импортировал с сайта графаны готовый дэшборд
- Telegraf:
-
- Создал каталог monitoring/grafana/telegraf и файл конфига telegraf.conf;
-
- В telegraf.conf описал настройки экспортера метрик докера и отдачу в формате прометеус;
-
- Описал сборку образа с добавлением файла конфига в него, добавил сервис телеграфа в компоуз-файл;
-
- Добавил таргет в конфиг прометеуса;
-
- Нашел дэшборд где-то в интернете, на сайте графаны под прометеус подобного не было;
-
- Добавил дэшборд в репозиторий;
- Создал алерт на примере 95 процентиля;
- Настроил интеграцию alertmanager с Gmail, однако выяснил, что alertmanager не умеет в секреты, поэтому все секреты пришлось прописывать прямо в конфиге.
- Grafana Deploy:
-
- Создал файл
monitoring/grafana/dashboard_provisoning/dashboards.ymlс описанием деплоя дэшбордов;
- Создал файл
-
- Создал файл
monitoring/grafana/datasources/prometheus.ymlс описанием датасорса prometheus;
- Создал файл
-
- Создал Dockerfile и описал в нем копирование всех файлов деплоя по нужным директориям;
- Stackdriver exporter:
-
- В качестве экспортера использовал https://github.com/frodenas/stackdriver_exporter;
-
- В компоуз-файле описал в качестве секрета json-файл для авторизации в GCP;
-
- Описал сервис
stackdriver_exporterс подключением секрета и настройками экспортера;
- Описал сервис
-
- Создал небольшой дэшборд с несколькими метриками, сохранил в каталоге с дэшбордами;
-
- Набор метрик получился следующий:
stackdriver_exporter_build_info stackdriver_gce_instance_compute_googleapis_com_firewall_dropped_bytes_count stackdriver_gce_instance_compute_googleapis_com_firewall_dropped_packets_count stackdriver_gce_instance_compute_googleapis_com_instance_cpu_reserved_cores stackdriver_gce_instance_compute_googleapis_com_instance_cpu_usage_time stackdriver_gce_instance_compute_googleapis_com_instance_cpu_utilization stackdriver_gce_instance_compute_googleapis_com_instance_disk_read_bytes_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_read_ops_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_throttled_read_bytes_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_throttled_read_ops_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_throttled_write_bytes_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_throttled_write_ops_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_write_bytes_count stackdriver_gce_instance_compute_googleapis_com_instance_disk_write_ops_count stackdriver_gce_instance_compute_googleapis_com_instance_integrity_early_boot_validation_status stackdriver_gce_instance_compute_googleapis_com_instance_integrity_late_boot_validation_status stackdriver_gce_instance_compute_googleapis_com_instance_network_received_bytes_count stackdriver_gce_instance_compute_googleapis_com_instance_network_received_packets_count stackdriver_gce_instance_compute_googleapis_com_instance_network_sent_bytes_count stackdriver_gce_instance_compute_googleapis_com_instance_network_sent_packets_count stackdriver_gce_instance_compute_googleapis_com_instance_uptime
- Набор метрик получился следующий:
https://hub.docker.com/u/yurich00/
https://devops-team-otus.slack.com/messages/CDAKS754G
- Обновил код приложения src;
- Добавил в Dockerfile post установку
gccиmusl-dev; - Пересобрал все образы приложения reddit;
- Создал докер-хост;
- Создал docker-compose-logging.yml;
- Elasticsearch не проставляет тэг latest своим образам, так как считает это плохой практикой, использовал версию 6.6.0 для Kibana и Elasticsearch;
- Elasticsearch не захотел заводиться с параметрами из ДЗ, нагуглил по коду ошибки, что нужно передать в окружение следующие параметры
и открыть порт 9300;
environment: transport.host: localhost network.host: 0.0.0.0 - Создал Dockerfile для fluentd в директории logging/fluentd;
- Там же создал fluent.conf, заполнил его и собрал образ fluentd;
- Запустил приложение и понаблюдал за логами post;
- Описал в компоуз файле использование для сервиса post логгирования через fluentd, переподнял контейнеры;
- Открыл Кибана и создал паттерн индексов (
И создадим индекс маппинг, - такой опции я не нашел, судя по скрину она уже была устаревшей и ее выпилили); - Ознакомился с интерфейсом Кибана, нашел логи post, попробовал использовать поиск по логам;
- Добавил во fluent.conf фильтр для парсинга JSON для сервиса post;
- Перезапустил fluent и убедился в кибана, что json-поле распарсилось;
- Добавил в компоуз-файле вывод логов в fluent для ui;
- Перезапустил приложение и проверил как собираются логи от ui;
- Добавил во fluent.conf фильтр для парсинга логов ui через регулярное выражение;
- Перезапустил сервисы логгирования и проверил как распарсились логи ui;
- Заменил фильтр для логов ui с регулярным выражением на grok-pattern;
- Добавил еще один парсинг через grok-pattern для парсинга оставшейся части лога ui;
- Выполнил задание со * (1);
- Добавил Zipkin в
docker-compose-logging.yml(использовал версию 2.11.8, т.к. последняя версия багованая, не отображает время спанов); - Добавил для каждого сервиса в
docker-compose.ymlпеременую окружения ZIPKIN_ENABLED=true и пересоздал все сервисы; - Открыл интерфейс zipkin и нашел запросы от ui_app;
- Запрос на загрузку главной страницы у меня состоял из двух спанов, в отличие от приведенного примера в ДЗ, у меня отсутствовал запрос от post к db;
- Выполнил задание со * (2).
- Добавил еще один grok-pattern для парсинга второй части лога ui. Фильтр
service.uiстал выглядеть следующим образом:
<filter service.ui>
@type parser
format grok
<grok>
pattern service=%{WORD:service} \| event=%{WORD:event} \| request_id=%{GREEDYDATA:request_id} \| message='%{GREEDYDATA:message}'
</grok>
<grok>
pattern service=%{WORD:service} \| event=%{WORD:event} \| path=%{URIPATH:path} \| request_id=%{GREEDYDATA:request_id} \| remote_addr=%{IPV4:remote_addr} \| method=%{GREEDYDATA:method} \| response_status=%{INT:response_status}
</grok>
key_name message
reserve_data true
</filter>
- Склонировал забагованное приложение в каталог src_bugged;
- Добавил Dockerfile в сервисы и собрал образы с тегом
:bugged; - Создал копию 'docker-compose.yml', где изменил образы на bugged, поднял приложение;
- Нажал на созданный ранее пост, пост открылся с существенной задержкой;
- Открыл Zipkin и нашел в нем запрос загрузки поста, увидел, что он занимает
3.055s; - Виновником оказался спан post, запрос выполнялся немногим более трех секунд
http.method GET
http.path /post/5c6afe1cecfc09000e0c5c45
- Нашел в коде post_app.py функцию поиска поста
find_postпо маршруту@app.route('/post/<id>'), а в ней - строчки кода, вызывавшие задержку в три секунды
max_resp_time = 3
...
median_time = time.sleep(max_resp_time)- Закомментировал эти строчки и пост стал открываться за
53.005ms.
https://hub.docker.com/u/yurich00/
- Описал манифесты сервисов reddit в директории kubernetes/reddit;
- Прошел The Hard Way, все созданные в ходе прохождения файлы сохранил в kubernetes/the_hard_way;
- Проверил, что
kubectl apply -f (ui, post, mongo, comment)работает и поды создаются; - Удалил кластер.
- Создал директорию kubernetes/ansible;
- В качестве примера описал выполнение шагов
Installing the Client ToolsиProvisioning Compute Resources; - Для шага
Provisioning Compute Resourcesиспользовалgce*модули ansible, так как в модулеgcp_compute_networkимеется баг, который при указании параметраauto_create_subnetworks: falseсоздает сеть типа legacy вместо описанного в документации custom; - Из-за использования модулей
gce*, не удалось явно указать IP-адрес инстансам, как того требует The Hard Way, но, думаю, это не страшно.
- Установил и запустил minikube;
- Проверил, что нода создалась;
- Изучил файл конфига
~/.kube/configи порядок настройки kubectl; - Изменил файл деплоймента ui, запустил ui в кластере;
- Пробросил порт 9292 пода ui на порт 8080 моей машины, перешел на localhost:8080 и проверил, что страница ui открывается;
- Обновил деплойменты comment, post и mongo, в mongo дополнительно примонтировал раздел;
- Описал сервисы
comment-service.yml,post-service.ymlиmongodb-service.yml, запустил их; - Пробросил порт ui 9292:9292, зашел на localhost:9292, увидел, что приложение не работает;
- В логах пода comment увидел, что нет доступа к comment_db;
- Создал два новых сервиса для mongo
comment-mongodb-service.ymlиpost-mongodb-service.yml; - Добавил в
mongo-deployment.ymlстрочкиcomment-db: "true"иpost-db: "true"; - Прописал в переменных окружения деплойментов comment и post параметр подключения к mongp;
- Убедился, что приложение заработало;
- Удалил mongodb-service;
- Создал
ui-service.ymlс добавлением типа сервисаtype: NodePortи указанием статичного порта; - Запустил
minikube service ui, открылось ссылка приложения; - Нашел все запущенные компоненты аддона
dashboardкомандойkubectl get all -n kube-system --selector app=kubernetes-dashboard; - Зашел в дэшборд и ознакомился с его функциональностью;
- Создал и применил
dev-namespace.yml, запустил приложение в неймспейсе dev; - Добавил в
ui-deployment.ymlинформацию об окружении, запустил и проверил, что она отображается; - Зашел в консоль GKE и создал кластер с указанными параметрами, с разницей в том, что использовал дефолтную версию кластера, под
1.8.10-gke.0кластер не смог развернуться, ругалось, что системные поды уже существуюти не позволяло им запуститься; - Пока разбирался, почему не поднимается кластер 1.8.10-gke.0, изучил консоль GKE;
- Подключился к созданному кластеру, посмотрел, как изменился
~/.kube/config, убедился, что kubecrl подключен к контексту кластера GKE; - Создал dev неймспейс и развернул в нем приложение;
- Создал правило фаервола для ui;
- Зашел по адресу ноды и порту на сервис ui;
- Скриншот перед удалением кластера сделать забыл, но есть логи GCP https://www.radikal.kz/image/DLO;
- Включил панель управления Kubernetes для кластера;
- Выполнил
kubectl proxyи перешел по ссылке http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/, вместо описанной в ДЗ нерабочей http://localhost:8001/ui; - Получил ошибку RBAC;
- Назначил роль cluster-admin сервис-аккаунту дэшборда командой
kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard; - Перешел снова в панель у правления и убедился, что она работает.
- Создал директорию kubernetes/terraform;
- Описал параметры создания кластера в файле
main.tfпо примеру из документации терраформ; - Для биндинга сервис-аккаунта панели управления к роли cluster-admin, как уже выше описывал, я использовал команду
kubectl create clusterrolebinding kubernetes-dashboard --clusterrole=cluster-admin --serviceaccount=kube-system:kubernetes-dashboard, но при добавлении параметра-o yamlкоманда сохраняет описание созданного объекта в YAML; - Создал описанным выше способом файл
kubernetes-dashboard-rolebind.yaml.
- Отключил kube-dns, изменив количество реплик в ноль, попробовал достучаться из comment в post, имя не зарезолвилось;
- Вернул количество реплик kube-dns как было;
- Настроил ui-service на использование балансировщика нагрузки (LoadBalancer);
- Применил изменения, проверил описание сервиса ui, перешел по адресу балансировщика и проверил работу приложения;
- Создал Ingress для сервиса ui;
- Изменил тип сервиса ui на NodePort;
- Добавил маршрутизацию трафика по пути /* на порт 9292 в Ingress;
- Выпустил самоподписанный сертифика для Ingress;
- Создал секрет в kubernetes с созадынным сертификатом;
- Отключил проброс HTTP в Ingress и подключил сертификат;
- Зашел на адрес приложения по HTTPS;
- Включил network-policy для кластера GKE;
- Создал сетевую политику, которая разрешает доступ к mongo только с comment;
- Обновил политику, добавив туда доступ к mongo для post;
- В итоге доступ запретить у меня не получилось;
- Создал несколько постов, удалил деплоймент mongo, создал его заново и убедился, что посты пропали;
- Создал диск в GCP с объемом в 25Gi и добавил новый раздел в деплоймент mongo;
- Применил изменения и убедился с помощью пересоздания деплоймента, что данные сохраняются;
- Создал описание PersistentVolume и добавил его в кластер;
- Создал описание PersistentVolumeClaim, добавил его также в кластер;
- Подключил PersistentVolumeClaim к деплойменту mongo, обновил деплоймент в кластере;
- Создал описание StorageClass’а и PersistentVolumeClaim с использованием этого класса;
- Подключил созданный PVC к деплойменту mongo;
- Проверил итоговую конфигурацию разделов.
- Создал описание секрета в файле
ui-tls-secret.yaml.
- Установил Helm, создал манифест тиллера, проинициализировал Helm;
- Создал директории под чарты, создал ui/Chart.yaml;
- Создал директорию ui/templates и перенес туда манифесты ui;
- Установил чарт ui;
- Шаблонизировал ui/templates/service.yaml, deployment.yaml и ingress.yaml, определил переменные в ui/values.yaml;
- Установил несколько релизов ui, проверил работу приложений;
- Кастомизировал переменными deployment.yaml, service.yaml и ingress.yaml, добавил новые переменные в values.yaml;
- Проапгрейдил релиз ui;
- Добавил шаблоны для post и comment;
- Описал функцию comment.fullname в _helpers.tpl, подменил названия на результат функции в нужных местах;
- Проделал аналогичное для ui и post;
- В директрии reddit/ создал необходимые файлы, загрузил зависимости;
- Нашел чарт монги, добавил его в файл зависимостей, обновил зависимости;
- Установил приложение с зависимостями;
- Добавил в ui/deployments.yaml шаблонизированные переменные окружения;
- Обновил зависимости и обновил релиз;
- Создал новый пул узлов в GKE под GitLab;
- Добавил репо гитлаба, скачал его чарт;
- Поправил необходимые конфиги;
- Установил чарт гитлаба, он не поднялся, уперся в лимиты по разммеру SSD в 100ГБ, поэтому пришлось подкрутить в конфиге размеры разделов:
#postgresStorageSize: 30Gi
postgresStorageSize: 20Gi
#gitlabDataStorageSize: 30Gi
gitlabDataStorageSize: 20Gi
#gitlabRegistryStorageSize: 30Gi
gitlabRegistryStorageSize: 20Gi- После этих манипуляций гитлаб запустился;
- Создал группу и проекты, добавил в переменные CI/CD группы даныне для входа в докер-хаб;
- Перенес исходники ui в Gitlab_ci/ui, проинициализировал репозиторий для гитлаба и пушнул код в него;
- Аналогичные действия сделал для post и comment;
- Перенес чарты в Gitlab_ci/reddit-deploy, пушнул в гитлаб;
- Создал Gitlab_ci/ui/.gitlab-ci.yml, пушнул, проверил пайплайн, повторил для post и comment;
- Обновил конфиг ингресса для ui;
- Создал ветку feature/3 в ui, обновил CI-скрипт, пушнул в гитлаб;
- Не деплоилось приложение из фича-бренча из-за ошибки совместимости версии тиллера, - изменил версию хельма на версию со своей машины в .gitlab-ci.yml (v2.13.0);
- Добавил в CI-скрипт стейдж удаления окружения, проверил работу пайплайна в гитлабе, проверил удаление окружения;
- Повторил те же шаги для post и comment;
- Создал reddit-deploy/.gitlab-ci.yml, пушнул в гитлаб, проверил пайплайн и окружения;
- Переместил файлы по описанным директориям, обновил .gitignore.
- Создал в репозитории reddit-deploy триггер, токен триггера поместил в переменные CI/CD группы;
- В скрипте пайплайна reddit-deploy сделал, чтобы staging не запускался по триггеру, а production не запускался при пуше, отключил флаг ручного запуска;
- В скрипты пайплайна сервисов добавил функцию запуска триггера деплоя
run_deployment_to_prod, описал новый стейдж deploy с использованием этой функции.
- Отключил в GKE Stackdriver Logging, Stackdriver Monitoring, включил Устаревшие права доступа;
- Установил ingress-контроллер nginx из helm-чарта;
- Добавил /etc/hosts имена хостов по адресу ингресса;
- Стянул чарт прометеуса из репозитория чартов хелм;
- Создал custom_values.yml;
- Установил прометеус;
- Включил в конфиге kube-state-metrics, обновил релиз;
- Включил node-exporter, обновил релиз, проверил метрики;
- Запустил релизы reddit-test, production и staging;
- Обновил конфиг, добавив джоб reddit-endpoints, обновил релиз;
- Добавил метки k8s и prometheus в конфиг релейбла;
- Добавил джоб reddit-production, обновил релиз;
- Разбил джоб reddit-endpoints на джобы по эндпойнтам ui, post и comment;
- Поставил grafana через helm;
- При входе указанный пароль не подошел, параметр
adminPassword=adminне работает helm/charts#7891, он создает секрет с паролем, но фактически не меняет пароль в графане, обошел это командой:
kubectl get secret grafana -o jsonpath='{.data.admin-password}' | \
base64 --decode | xargs -I {} kubectl exec -it grafana-59b795cfd8-hrjdf \
-- grafana-cli admin reset-admin-password --homepath /usr/share/grafana {}- Добавил датасорс прометеуса, загрузил дэшборд кубернетиса;
- Добавил дэшборды, ранее созданные в предыдущих ДЗ (Business_Logic_Monitoring и UI_service_monitoring);
- Добавил на дэшборд UI_service_monitoring переменую namespace, в запросе вместо namespace использовал
kubernetes_namespace; - Добавил фильтр по переменной в графики дэшборда, то же самое проделал с дэшюордом Business_Logic_Monitoring;
- Все используемые в рамках этого ДЗ дэшборды сохранил в
kubernetes/grafana/dashboards; - Импортировал график №741;
- Выполнил задание со * (1);
- Выполнил задание со * (2);
- Для раздела про логгирование мощностей трех моих нод уже не хватало, поэтому добавил еще одну;
- Установил для нее лейбл elastichost=true;
- Создал директорию kubernetes/efk/, создал в ней необходимые манифесты и применил их;
- Установил кибану через хелм, настроил ее;
- Выполнил задание со * (3).
- Активировал alertmanager в чарте prometheus в
custom_values.yml; - Описал в разделе
alertmanagerFiles.alertmanager.ymlнастройки alertmanager; - В разделе
serverFiles.alertsописал правила алертинга.
- Загрузил чарт Prometheus Operator в директорию
kubernetes/Charts/prometheus-operator; - Создал копию values.yaml, назвал ее custom_values.yml, далее все настройки выполнял в этой копии;
- Включил ингресс (
ingress.enabled=true); - Присвоил имя хоста (
hosts: ["prometheus-operator"]); - Настроил ServiceMonitor:
additionalServiceMonitors:
- name: post-monitor
selector:
matchLabels:
app: reddit
component: post
namespaceSelector:
any: true
endpoints:
- port: post- Создал в директории elasticsearch и fluentd в kubernetes/Charts, где описал чарты развертывания соответствующих сервисов;
- Там же создал директорию чарта efk, где описал зависимости от созданных ранее сервисов и добавил в зависимости kibana.