dnvlasov microservices repository
- Устанвливаем packages repository to allow apt use a repository ower HTTP
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg2 \
software-properties-commonДобавим ключ GPG
$ curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -Проверим что ключи добавились а apt-key
$ sudo apt-key fingerprint 0EBFCD88
pub 4096R/0EBFCD88 2017-02-22
Key fingerprint = 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
uid Docker Release (CE deb) <docker@docker.com>
sub 4096R/F273FCD8 2017-02-22Добавим в source.list stable repository
$ sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/debian \
$(lsb_release -cs) \
stable"Делаем обновление пакетов
sudo apt updateИ устанавливаем docker-ce.
$ sudo apt install docker-ce=17.06.2~ce-0~debianЗапустим первый контейнер.
docker run hello-worldСписок запущенных контейнеров
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
Список всех контейнеров
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ea689d42d82 ubuntu:16.04 "/bin/bash" 4 hours ago Exited (1) 4 hours ago kind_merkle
7639619723b9 ubuntu:16.04 "/bin/bash" 4 hours ago Exited (137) 3 hours ago kind_cohen
68c1fd6f7647 hello-world "/hello" 4 hours ago Exited (0) 4 hours ago jovial_kare
Список сохраненных образов
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-docker/ubuntu-tmp-file latest 0ac4cc36d7e7 25 hours ago 116MB
ubuntu 16.04 a51debf7e1eb 2 weeks ago 116MB
hello-world latest 4ab4c602aa5e 2 months ago 1.84kB
Docker run каждый раз запускает новый контейнер. Если не указывать флаг --rm при запуске docker run, то после остановки контейнер в месте с содержимым остается на диске.
docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.CreateAt}}\t{{.Names}}"
CONTAINER ID IMAGE CREATED AT NAMES
8ea689d42d82 ubuntu:16.04 2018-12-04 14:04:03 +0300 MSK kind_merkle
7639619723b9 ubuntu:16.04 2018-12-04 14:02:25 +0300 MSK kind_cohen
68c1fd6f7647 hello-world 2018-12-04 13:59:12 +0300 MSK jovial_kare
Запуск нового процесса внутри контейнера например bash start запускает уже созданный контейнер attach подсоединяет терминал к созданному контейнеру
docker start 8ea689d42d82
docker attach 8ea689d42d8
ENTER
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8ea689d42d82 ubuntu:16.04 "/bin/bash" 2 days ago Up 4 minutes kind_merkle
Запускаем новый процесс внутри контейнера
docker exec -it 8ea689d42d82 bash
Docker commit создает image из контенера, контейнер при этом остается запущенным.
sha256:839f6a935c85d9ff8bbbe668fc872b908bafccf25abc1d64e297da58961b3551
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my-docker/ubuntu-tmp-file latest 0ac4cc36d7e7 2 days ago 116MB
ubuntu 16.04 a51debf7e1eb 2 weeks ago 116MB
hello-world latest 4ab4c602aa5e 2 months ago 1.84kB
- Создаём новый проект в GCP с названем docker
- Создаем новый конфигурационный файл командой
gcloud initСоздаем аутентификационные данные для доступа к приложению
gcloud auth application-default loginСоздаем скрипт для создания docker-machine
#!/bin/bash
export GOOGLE_PROJECT=docker-XXXXXX
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
docker-hostПосле запускаем и проверяем что Docker успешно запустился
$bash docker.machine.up
$docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host - google Running tcp://222.222.222.222:2376 v18.09.0 И переключаемся на него
eval $(docker-machine env docker-host)Для дальнейшей работы нужно создать следующие файлы
- Dockerfile - текстовое описание нашего образа
- mongod.conf - подготовленный конфиг для mongodb
- db_config - содержит переменную окружения со ссылкой на mongodb
- start.sh - скрипт запуска приложения Вся работа происходят в папке docker-monolith
mongodb.conf
# Where and how to store data.
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true
# where to write logging data.
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log
# network interfaces
net:
port: 27017
bindIp: 127.0.0.1
start.sh
#!/bin/bash
/usr/bin/mongod --fork --logpath /var/log/mongod.log --config /etc/mongodb.conf
source /reddit/db_config
cd /reddit && puma || exit
db_config
DATABASE_URL=127.0.0.1Создадим образ приложения ubuntu 16.04
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install -y mongodb-server ruby-full ruby-dev build-essential git
RUN gem install bundler
RUN git clone -b monolith https://github.com/express42/reddit.git
COPY mongod.conf /etc/mongod.conf
COPY db_config /reddit/db_config
COPY start.sh /start.sh
RUN cd /reddit && bundle install
RUN chmod 0777 /start.sh
CMD ["/start.sh"]Выполним сборку образа
$ docker build -t reddit:latest .
Теперь запустим наш контейнер
$ docker run --name reddit -d --network=host reddit:latest
Проверим результат
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host - google Running tcp://222.222.222.222:2376 v17.09.0-ce
Разрешим входящий TCP-трафик на порт 9292 выполнив команду
gcloud compute firewall-rules create reddit-app \
--allow tcp:9292 \
--target-tags=docker-machine \
--description="Allow PUMA connections" \
--direction=INGRESSЗагрузим образ на docker hub
docker login
docker tag reddit:latest login/otus-reddit:1.0
docker push login/otus-reddit:1.0
И еще проверка
docker inspect <your-login>/otus-reddit:1.0
[
{
"Id": "sha256:05b2cb5a01083e2de7693d151285d0b37f784f3d0143a9fca56c279944a64c91",
"RepoTags": [
"reddit:latest",
"login/otus-reddit:1.0"
],
"RepoDigests": [
"login/otus-reddit@sha256:c867984622cda30f7d03bf0d13d7f431765861cf1b897e9ab53c7d5a444e7aed"
],
"Parent": "sha256:ca8af415e9713a12f1c6ae21cabfcca690d5b9aacd89ec142678af893eac3642",
"Comment": "",
"Created": "2018-12-08T12:03:46.409836381Z",
"Container": "3c9ee932cc71abbf89e760d5ed7a1474958490c05dbda6a8c795f565bef40cd6",
"ContainerConfig": {
"Hostname": "3c9ee932cc71",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"/start.sh\"]"
],
"ArgsEscaped": true,
"Image": "sha256:ca8af415e9713a12f1c6ae21cabfcca690d5b9aacd89ec142678af893eac3642",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": {}
},
"DockerVersion": "18.09.0",
"Author": "",
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/start.sh"
],
"ArgsEscaped": true,
"Image": "sha256:ca8af415e9713a12f1c6ae21cabfcca690d5b9aacd89ec142678af893eac3642",
"Volumes": null,
"WorkingDir": "",
"Entrypoint": null,
"OnBuild": null,
"Labels": null
},
"Architecture": "amd64",
"Os": "linux",
"Size": 681077551,
"VirtualSize": 681077551,
"GraphDriver": {
"Data": {
"LowerDir": "/var/lib/docker/overlay2/4da0e10e48eafc9f44ac4165578c4a6d285b8e94c5de9a72f48c95a4d66a4b12/diff:/var/lib/docker/overlay2/0d78366f3c5bdee9d1847250e501d2893c6356397143672723714b3a7f1c20ba/diff:/var/lib/docker/overlay2/7de0c82195a605e8e4ab4f4cc92f1f54aad3914a07b85efce02195fec648029d/diff:/var/lib/docker/overlay2/bb7a269f4456258522a33d889eeacf873c5a59f12a1efd25137a09a33cdff22c/diff:/var/lib/docker/overlay2/e5e62bfbb09afc72daba59178ceda8e8bc6b6ba35c2bfa27ed3b559919908603/diff:/var/lib/docker/overlay2/402e1798479db11ae552517f0dde3bed2ebc1c9a9093027bb3bb160335d44935/diff:/var/lib/docker/overlay2/8d69fb56ca3872a0b72b767adc5202174c6c9a7a68220d7ddd8c85c02387f24f/diff:/var/lib/docker/overlay2/701695213c9aebe88f2f003b2678359175ca1108cff6494b939d6d06a4f72fde/diff:/var/lib/docker/overlay2/715ca389e78c8809c070e0d6af18e65e79e205171e5db0a3d0db0cf2f89030e9/diff:/var/lib/docker/overlay2/cb8e26649a1cbdf0ec4f83f19f0fdff7386eff4bd9b634df69676ee983e5760b/diff:/var/lib/docker/overlay2/c93817764554a5a98c739701be797d24c7a5bb20a122d1f79233a39ebcc5fd63/diff:/var/lib/docker/overlay2/5a2e1cb34bc0d3701c5ad95b50539f278916baf248cd9a691556c1734a535b53/diff",
"MergedDir": "/var/lib/docker/overlay2/ead4f7b20e6815b14c7be7389358cb191041c64f7b16b4a7456fbfe9f874c3af/merged",
"UpperDir": "/var/lib/docker/overlay2/ead4f7b20e6815b14c7be7389358cb191041c64f7b16b4a7456fbfe9f874c3af/diff",
"WorkDir": "/var/lib/docker/overlay2/ead4f7b20e6815b14c7be7389358cb191041c64f7b16b4a7456fbfe9f874c3af/work"
},
"Name": "overlay2"
},
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:41c002c8a6fd36397892dc6dc36813aaa1be3298be4de93e4fe1f40b9c358d99",
"sha256:647265b9d8bc572a858ab25a300c07c0567c9124390fd91935430bf947ee5c2a",
"sha256:819a824caf709f224c414a56a2fa0240ea15797ee180e73abe4ad63d3806cae5",
"sha256:3db5746c911ad8c3398a6b72aa30580b25b6edb130a148beed4d405d9c345a29",
"sha256:9a4bc08cbc6aa6710d48343231853e47fc6e5fc16c6d901629d29540e6056a50",
"sha256:6c2c6a379303cdb7a00f2175d163cd626fe537e6c77e1d23c2b6ac813a9e992b",
"sha256:298d1d015927df78a17f6be9ce732e714096fb360f1ffec8e6677131d0b405c5",
"sha256:2cea5f58ac5fa068a2deee03a22990abca4b54f595b05cecd1a7155913157f89",
"sha256:e5532dac33bcaa71f178a2b4a5051630e5dd25d3df3de1c9a34f6914c7280010",
"sha256:8825a868bcb118c35703eb3139a4b531e9f0dc83f0ae7e507d8838697c12c27c",
"sha256:6efbada0efe2e0f262de308758bd58379e9ae85fdf555ca2fe5c6d2546680c37",
"sha256:9900fb8186201e327dfc6dd08da85c419762377780a737748b664998d4659275",
"sha256:fd6d299e2682fb2adae33b352f9a6f0c296aa323585a453063bb40fb8b4f2e59"
]
},
"Metadata": {
"LastTagTime": "2018-12-08T17:24:56.840982562+03:00"
}
}
]docker inspect <your-login>/otus-reddit:1.0 -f '{{.ContainerConfig.Cmd}}'
[/bin/sh -c #(nop) CMD ["/start.sh"]]Подключаемся к Docker host
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
docker-host - google Running tcp://35.240.74.230:2376 v18.09.0
eval $(docker-machine env docker-host)Скачаем zip архив и распакуем его содержимое внутрь репозитория,
В нутри репозитория у нас появиться каталог reddit-microervices переменуем его в src
Теперь наше приложение состоит из трех компонентов
post-py- сервис отвечающий за написание постовcomment- сервис отвечающий за написание коменрариевui- веб-интерфейс, работающий с другими сервисами
Создадим файл ./post-py/Dockerfile
FROM python:3.6.0-alpine
WORKDIR /app
ADD . /app
RUN pip install -r /app/requirements.txt
ENV POST_DATABASE_HOST post_db
ENV POST_DATABASE posts
ENTRYPOINT ["python3", "post_app.py"]
./comment/Dockerfile
FROM ruby:2.2
RUN apt-get update -qq && apt-get install -y build-essential
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install
ADD . $APP_HOME
ENV COMMENT_DATABASE_HOST comment_db
ENV COMMENT_DATABASE comments
CMD ["puma"]
./ui/Dockerfile
FROM ruby:2.2
RUN apt-get update -qq \
&& apt-get install -y build-essential
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install
ADD . $APP_HOME
ENV POST_SERVICE_HOST post
ENV POST_SERVICE_PORT 5000
ENV COMMENT_SERVICE_HOST comment
ENV COMMENT_SERVICE_PORT 9292
CMD ["puma"]
Скачаем последний образ MongoDB:
docker pull mongo:latestСоберем образы с нашими сервисами:
docker build -t login/post:1.0 ./post-py
docker build -t login/comment:1.0 ./comment
docker build -t login/ui:1.0 ./uiCозздадим специальную сеть для приложения
docker network create reddit
Запустим наши контейнеры
docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db mongo:laest
docker run -d --network=reddit --network-alias=post login-dockerhub/post:1.0
docker run -d --network=reddit --network-alias=post login-dockerhub/comment:1.0
docker run -d --network=reddit -p 9292:9292 login-dockerhub/ui:1.0Что было сделанно.
- Создана bridge-сеть для контейнеров
- Запустили контейнеры в этой сети
- Добавили сетевые алиасы контейнерам
Образы преложения ранимают много места для одного приложения
REPOSITORY TAG IMAGE ID CREATED SIZE
verty/ui 1.0 94ba693f3bcf 8 days ago 781MB
verty/comment 1.0 a2f2b084ff87 8 days ago 773MB
verty/post 1.0 f524ee9a94a2 8 days ago 102MB
mongo latest 525bd2016729 4 weeks ago 383MB
ubuntu 16.04 a51debf7e1eb 4 weeks ago 116MB
ruby 2.2 6c8e6f9667b2 7 months ago 715MB
python 3.6.0-alpine cb178ebbf0f2 21 months ago 88.6MB
- Улучшаем образ сервиса ui поменяем содержимое ./ui/Dockerfile
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y ruby-full ruby-dev build-essential \
&& gem install bundler --no-ri --rdoc
ENV APP_HOME /app
RUN mkdir $APP_HOME
WORKDIR $APP_HOME
ADD Gemfile* $APP_HOME/
RUN bundle install
ADD . $APP_HOME
ENV POST_SERVICE_HOST post
ENV POST_SERVICE_PORT 5000
ENV COMMENT_SERVICE_HOST comment
ENV COMMENT_SERVICE_PORT 9292
CMD ["puma"]
Пересоберем ui docker build -t login/ui:2.0 ./ui
POSITORY TAG IMAGE ID CREATED SIZE
ui 2.0 f17bd1160df4 6 minutes ago 455MB
ui 1.0 94ba693f3bcf 9 days ago 781MB
comment 1.0 a2f2b084ff87 9 days ago 773MB
post-py 1.0 f524ee9a94a2 9 days ago 102MB
mongo latest 525bd2016729 4 weeks ago 383MB
ubuntu 16.04 a51debf7e1eb 4 weeks ago 116MB
ruby 2.2 6c8e6f9667b2 7 months ago 715MB
python 3.6.0-alpine cb178ebbf0f2 21 months ago 88.6MB
Выключим старые копии контейнеров
docker kill $(docker ps -q)
Запустим новые копии контейнеров.
docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db mongo:latest
docker run -d --network=reddit --network-alias=post dockerhub-login/post:1.0
docker run -d --network=reddit --network-alias=comment dockerhub-login/comment:1.0
docker run -d --network=reddit -p 9292:9292 dockerhub-login/ui:2.0
Проверим на http://ip_docker_host:9292. Ранее созданный пост пропал вместе с остановкой контейнера mongo.
- Создадим Docker volume:
$ docker volume create reddit_db
И подключим его к контейнеру с MongoDB.
Выключим старые копии контейнеров:
docker kill $(docker ps -q)
Запустим новые копии контейнеров:
docker run -d --network=reddit --network-alias=post_db --network-alias=comment_db -v reddit_db:/data/db mongo:latest
docker run -d --network=reddit --network-alias=post dockerhub-login/post:1.0
docker run -d --network=reddit --network-alias=comment dockerhub-login/comment:1.0
docker run -d --network=reddit -p 9292:9292 dockerhub-login/ui:2.0
- Заходим на http://ip_docker_host:9292
- Пишем пост
- Перезапустим контейнеры bash docker-conteiner_restart
- Проверяем наличие поста
Создадим bridge-сеть в docker
docker network create reddit --driver bridgeЗапустим наш проект reddit с использованием bridge-сети.
docker run -d --network=reddit mongo:latest
docker run -d --network=reddit dockerhub-login/post:1.0
docker run -d --network=reddit dockerhub-login/comment:1.0
docker run -d --network-reddit -p 9292:9292 dockerhub-login/ui:1.0При просмотре web по ip:9292 будет возникать ошибка "Can't show blog posts, some problems with the post service. Refresh?" Для решения необходимо присвоение имени контейнерам --name (можно задать только 1 имя) --network-alias (можно задать множество алиасов) Остановим старые копии контейнеров
docker kill $(docker ps -q)Запустим новые.
docker run -d --network=reddit --network-alias=post_db --network-alias=coment_db mongo:latest
docker run -d --network=reddit --network-alias=post login/post:1.0
docker run -d --network=reddit --network-alias=comment login/comment:1.0
docker rnn -d --network=reddit -p 9292:9292 login/ui:1.0
Проверим что предупреждение об ошибке пропала.
Запустим проект в 2-х bridge сетях. Так, чтобы сервис ui не имел доступа к базе данных в соответствии со схемой.
front_net back_net ui comment db post
Остановим старые копии контейнеров
docker kill $(docker ps -q)Создадим docker-сети
docker network create back_net --subnet=10.0.2.0/24
docker network create front_net --subnet=10.0.1.0/24Запустим контейнеры
docker run --network=front_net -p 9292:9292 --name ui login/ui:1.0
docker run --network=back_net --name comment login/comment:1.0
docker run --network=back_net --name post login/post:1.0
docker run --network=back_net --name mongo_db --network-alias=post_db --network-alias=comment_db mongo:latestКонтейнеры post и comment нужно поместить в обе сети командой docker network connect подключим ко второй сети
docker network connect front_net post
docker network connect front_net commentЗайдем на web по ip_address:9292 проверим что все работает без ошибок.
Заходим по ssh на docker-host и установим пакет bridge-utils
docker-machine ssh docker-host
sudo apt-get update && sudo apt-get install bridge-utilsВыполним
docker network lsНаходим ID сетей созданных в рамках проекта.
ifconfig | grep br
br-30d790cce3d1 Link encap:Ethernet HWaddr 02:42:f8:73:78:61
br-870ad93a7408 Link encap:Ethernet HWaddr 02:42:cb:6b:ad:9f
br-dc9ad54119fe Link encap:Ethernet HWaddr 02:42:df:f0:85:38
brctl show br-30d790cce3d1
bridge name bridge id STP enabled interfaces
br-30d790cce3d1 8000.0242f8737861 no veth4c9d2ae
veth8815442
vethd0e7d04
brctl show br-870ad93a7408
bridge name bridge id STP enabled interfaces
br-870ad93a7408 8000.0242cb6bad9f no vetha8392b3
vethaa4417a
vethffde6e7
bridge name bridge id STP enabled interfaces
br-dc9ad54119fe 8000.0242dff08538 noЦепочка iptables POSTROUTING (policy ACCEPT)
Chain POSTROUTING (policy ACCEPT)
target prot opt source destination
MASQUERADE all -- 10.0.1.0/24 0.0.0.0/0
MASQUERADE all -- 10.0.2.0/24 0.0.0.0/0
MASQUERADE all -- 172.17.0.0/16 0.0.0.0/0
MASQUERADE all -- 172.18.0.0/16 0.0.0.0/0
MASQUERADE tcp -- 10.0.1.2 10.0.1.2 tcp dpt:9292Правило отвечает за выпуск во внешнюю сеть контейнеров из bridge-сетей
Цепочка Docker и правила в ней отвечают за перенаправление трафика на адреса конкретных контейнеров.
Chain DOCKER
DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:9292 to:10.0.1.2:9292Процесс docker-proxy слушает на tcp-порту 9292
ps ax | grep docker-proxy
13479 ? Sl 0:00 /usr/bin/docker-proxy -proto tcp -host-ip 0.0.0.0 -host-port 9292 -container-ip 10.0.1.2 -container-port 9292В директории src создаем файл docker-compose.yml
version: '3.3'
services:
post_db:
container_name: post_db
image: mongo:3.2
volumes:
- post_db:/data/db
networks:
- reddit
ui:
build: ./ui
container_name: ui
image: ${USERNAME}/ui:1.0
ports:
- 9292:9292/tcp
networks:
- reddit
post:
build: ./post-py
container_name: post-py
image: ${USERNAME}/post:1.0
networks:
- reddit
comment:
build: ./comment
container_name: comment
image: ${USERNAME}/comment:1.0
networks:
- reddit
volumes:
post_db:
networks:
reddit:
Остановим контейнеры
docker kill $(docker ps -q)Экспортируем переменную USERNAME и запустим docker-compose
export USERNAME=<your-login>
docker-compose up -d
docker-compose ps
```bash
Name Command State Ports
----------------------------------------------------------------------------
src_comment_1 puma Up
src_post_1 python3 post_app.py Up
src_post_db_1 docker-entrypoint.sh mongod Up 27017/tcp
src_ui_1 puma Up 0.0.0.0:9292->9292/tcp
Заходим на http://ip_docker_machine:9292 работает корректно.
- Изменеие docker-compose под кейс с множеством сетей, сетевых алисов.
- Параметризация с помощью переменных окружений:
- порт публикации сервиса ui
- версия сервисов
- Параметризованные параметры запишите в отдельный файл c расширением .env
- Сущности создаваемые docker-compose можно изменить параметром containers_name
version: '3.3'
services:
post_db:
container_name: post_db
image: "mongo:${MONGO}"
volumes:
- post_db:/data/db
networks:
- back_net
ui:
build: ./ui
container_name: ui
image: "${USERNAME}/ui:${UI}"
ports:
- ${NETWORK}
networks:
- front_net
post:
build: ./post-py
container_name: post-py
image: "${USERNAME}/post:${POST}"
networks:
- back_net
- front_net
comment:
build: ./comment
container_name: comment
image: "${USERNAME}/comment:${COMMENT}"
networks:
- back_net
- front_net
volumes:
post_db:
networks:
front_net:
back_net:Создаем виртуальную машину.
#!/bin/bash
export GOOGLE_PROJECT=docker-224713
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-disk-size 100 \
--google-zone europe-west1-b \
gitlab-ci-2
Разрешаем подключение по HTTP/HTTPS
#!/bin/bash
gcloud compute firewall-rules create gitlab-ci\
--allow tcp:80,tcp:443 \
--target-tags=docker-machine \
--description="gitlab-ci connections http & https" \
--direction=INGRESS
Ставим docker с помощью ansible
ansible.cfg
[defaults]
inventory = ./inventory
remote_user = docker-user
private_key_file = ~/.docker/machine/machines/gitlab-ci/id_rsa
host_key_checking = False
retry_files_enabled = False
inventory
docker ansible_host=35.195.228.47
docker-composer-install
---
- name: install docker
hosts: docker2
become: true
tasks:
- name: install dependencies
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg2
- software-properties-common
- python-pip
tags:
- docker
- name: Debian add docker key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
tags:
- docker
- name: Verify key
apt_key:
id: 0EBFCD88
state: present
tags:
- docker
- name: Add repository
apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable
state: present
update_cache: yes
tags:
- docker
- name: install docker
apt:
name:
- docker-ce
- docker-compose
update_cache: yes
tags:
- docker На сервере создаем необходимые директории
# mkdir -p /srv/gitlab/config /srv/gitlab/data /srv/gitlab/logs
# cd /srv/gitlab/
# touch docker-compose.ymlСоздаем файл docker-compose.yml
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'gitlab.example.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://<YOUR-VM-IP>'
ports:
- '80:80'
- '443:443'
- '2222:22'
volumes:
- '/srv/gitlab/config:/etc/gitlab'
- '/srv/gitlab/logs:/var/log/gitlab'
- '/srv/gitlab/data:/var/opt/gitlab'`
Запускаем docker-compose up -d
Проверям что все прошло успешно http://ip_gitlab
Указываем пароль root, переходим в админку выключаем регистацию новых пользователей.
Создаем private группу homework,создадим проект.
Добавим в username_microservices
git checkout -b gitlab-ci-1
git remote add gitlab http://<your-vm-ip>/homework/example.git
git push gitlab gitlab-ci-1
Переходим к определению CI/CD Pipeline для проекта, добавим в рерозиторий файл .gitlab-ci.yml
stages:
- build
- test
- deploy
build_job:
stage: build
script:
- echo 'Building'
test_unit_job:
stage: test
script:
- echo 'Testing 1'
test_integration_job:
stage: test
script:
- echo 'Testing 2'
deploy_job:
stage: deploy
script:
- echo 'Deploy'Сохраняем файл.
git add .gitlab-ci.yml
git commit -m 'add pipeline definition'
git push gitlab gitlab-ci-1Теперь если перейти в раздел CI/CD мы увидим, что пайплайн готов к запуску. Но находится в статусе pending / stuck так как у нас нет runner, запустим Runner и зарегистрируем его в интерактивном режиме.
Перед тем, как запускать и регистрировать runner нужно получить токен, переходим
http://your_ip/homework/example/settings/ci_cdОткрываем Runner, копируем
- 3 Use the following registration token during setup: На сервере, где работает Gitlab CI выполняем команду:
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest После запуска Runner нужно зарегистрировать, это можно сделать командой
root@gitlab-ci:~# docker exec -it gitlab-runner gitlab-runner register
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://<YOUR-VM-IP>/
Please enter the gitlab-ci token for this runner:
<TOKEN>
Please enter the gitlab-ci description for this runner:
[38689f5588fe]: my-runner
Please enter the gitlab-ci tags for this runner (comma separated):
linux,xenial,ubuntu,docker
Whether to run untagged builds [true/false]:
[false]: true
Whether to lock the Runner to current project [true/false]:
[true]: false
Please enter the executor:
docker
Please enter the default Docker image (e.g. ruby:2.1):
alpine:latest
Runner registered successfully.Если все получилось, то в настройках новый runner
-
- Start the Runner!
- Runners activated for this project После добавления Runner пайплайн запустился
git clone https://github.com/express42/reddit.git && rm -rf ./reddit/.git
git add reddit/
git commit -m “Add reddit app”
git push gitlab gitlab-ci-1
Изменим описание пайплайна в .gitlab-ci.yml
image: ruby:2.4.2
stages:
...
variables:
DATABASE_URL: 'mongodb://mongo/user_posts'
before_script:
- cd reddit
- bundle install
...
test_unit_job:
stage: test
services:
- mongo:latest
script:
- ruby simpletest.rbВ описании pipeline мы добавили вызов теста в файле simpletest.rb, нужно создать его в папке reddit.
require_relative './app'
require 'test/unit'
require 'rack/test'
set :environment, :test
class MyAppTest < Test::Unit::TestCase
include Rack::Test::Methods
def app
Sinatra::Application
end
def test_get_request
get '/'
assert last_response.ok?
end
end
добавить библиотеку для тестирования в reddit/Gemfile приложения.
- gem 'rack-test'
source 'https://rubygems.org'
gem 'sinatra', '~> 2.0.1'
gem 'haml'
gem 'bson_ext'
gem 'bcrypt'
gem 'puma'
gem 'mongo'
gem 'json'
gem 'rack-test'
group :development do
gem 'capistrano', require: false
gem 'capistrano-rvm', require: false
gem 'capistrano-bundler', require: false
gem 'capistrano3-puma', require: false
end
Теперь на каждое изменение в коде приложения будет запущен тест.
Создадаем новый проект, example2.
Добавляем новый remote
git checkout -b gitlab-ci-2
git remote add gitlab2 http://ip_gitlab-ci/homework/example2.git
git push gitlab2 gitlab-ci-2Чтобы заработал pipeline включаем runner
- Переименуем deploy stage в rewiew.
- deploy_job меняем на deploy_dev_job
- Добавляем environment
deploy_dev_job:
stage: review
script:
- echo 'Deploy'
environment:
name: dev
url: http://dev.example.comПосле завершения pipeline с определением окружения переходим
Environments появится определение первого окружения dev
Определим два новых этапа: stage и production, первый будет содержать job имитирующий выкатку на staging окружение, второй на production окружение. Определим эти job таким образом, чтобы они запускались с кнопки
stages:
- build
- test
- review
- stage
- production
...
deploy_dev_job:
stage: review
script:
- echo 'Deploy'
environment:
name: dev
url: http://dev.example.com
staging:
stage: stage
when: manual
script:
- echo 'Deploy'
environment:
name: stage
url: https://beta.example.com
production:
stage: production
when: manual
script:
- echo 'Deploy'
environment:
name: production
url: https://example.comwhen: manual – говорит о том, что job должен быть запущен по кнопке из UI.
На странице окружений появиться оружения staging и production.
На production окружение выводится приложение с явно зафиксированной версией (например, 2.4.10).
Добавим в описание pipeline директиву, которая не позволит нам выкатить на staging и production код, не помеченный с помощью тэга в git.
stages:
- build
- test
- review
- stage
- production
… … …
stage:
stage: stage
when: manual
only:
- /^\d+\.\d+\.\d+/
script:
- echo 'Deploy'
environment:
name: stage
url: https://beta.example.com
production:
stage: production
when: manual
only:
- /^\d+\.\d+\.\d+/
script:
- echo 'Deploy'
environment:
name: production
url: https://example.comДиректива only описывает список условий, которые должны быть истинны, чтобы job мог запуститься. Регулярное выражение слева означает, что должен стоять semver тэг в git, например, 2.4.10
Изменение, помеченное тэгом в git запустит полный pipeline.
git commit -a -m ‘#4 add logout button to profile page’
git tag 2.4.10
git push gitlab2 gitlab-ci-2 --tagsДинамические окружения
stages:
- build
- test
- review
- stage
- production
. . .
branch review:
stage: review
script: echo "Deploy to $CI_ENVIRONMENT_SLUG"
environment:
name: branch/$CI_COMMIT_REF_NAME
url: http://$CI_ENVIRONMENT_SLUG.example.com
only:
- branches
except:
- master
staging:
stage: stage
when: manualЭтот job определяет динамическое окружение для каждой ветки в репозитории, кроме ветки master
Теперь, на каждую ветку в git отличную от master Gitlab CI будет определять новое окружени
Создадим правило фаервола для Prometheus и Puma:
$ gcloud compute firewall-rules create prometheus-default --allow tcp:9090
$ gcloud compute firewall-rules create puma-default --allow tcp:9292Содаем Docker хост в GCE настраиваем локальное окружение на работу с ним.
#!/bin/bash
export GOOGLE_PROJECT=docker-224713
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
--google-disk-size 100 \
docker-hostПодключаемся к созданной docker-machine
$ eval $(docker-machine env docker-host)Запускаем Prometheus внутри Docker контейнера готовый образ с DockerHub.
$ docker run -rm -p 9090:9090 -d --name prometheus prom/prometheus:v2.1.0Открываем веб интерфейс.
По умолчанию сервер слушает на порту 9090, а IP адрес созданной VM можно узнать, используя команду:
$ docker-machine ip docker-hostВкладка Console, которая сейчас активирована, выводит численное значение выражений. Вкладка Graph, левее от нее, строит график изменений значений метрик со временем. Если кликнем по "insert metric at cursor", то увидим, что Prometheus уже собирает какие-то метрики. По умолчанию он собирает статистику о своей работе. Выберем, например, метрику prometheus_build_info и нажмем Execute, чтобы посмотреть информацию о версии.
prometheus_build_info{branch="HEAD",goversion="go1.9.1",instanc e="localhost:9090", job="prometheus", revision= "3a7c51ab70fc7615cd318204d3aa7c078b7c5b20",version="1.8.1"} 1
prometheus_build_info - идентификатор собранной информации.
branch,goversion,instance,job,revision,version - добавляет метаданных метрике, уточняет ее.
Использование лейблов дает нам возможность не ограничиваться
лишь одним названием метрик для идентификации получаемой
информации. Лейблы содержаться в {} скобках и представлены
наборами "ключ=значение".
В конце единица - численное значение метрики, либо NaN, если значение недоступно
Targets (цели) - представляют собой системы или процессы, за которыми следит Prometheus. Помним, что Prometheus является pull системой, поэтому он постоянно делает HTTP запросы на имеющиеся у него адреса (endpoints). Посмотрим текущий список целей.
Создаем директорию monitoring/prometheus, в этой директории создем Dockerfile
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/Определим простой конфигурационный файл для сбора метрик с наших микросервисов. В директории monitoring/prometheus создайте файл prometheus.yml со следующим содержимым
---
global:
scrape_interval: '5s'
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets:
- 'localhost:9090'
- job_name: 'ui'
static_configs:
- targets:
- 'ui:9292'
- job_name: 'comment'
static_configs:
- targets:
- 'comment:9292'$ export USER_NAME=username
$ docker build -t $USER_NAME/prometheus .Сборку образов теперь необходимо производить при помощи скриптов docker_build.sh, которые есть в директории каждого сервиса
Выполните сборку образов при помощи скриптов docker_build.sh
for i in ui post-py comment; do cd src/$i; bash
docker_build.sh; cd -; doneОпределите в docker/docker-compose.yml файле новый сервис.
version: '3.3'
services:
post_db:
container_name: post_db
image: "mongo:${MONGO}"
volumes:
- post_db:/data/db
networks:
- back_net
ui:
container_name: ui
image: "${USERNAME}/ui:${UI}"
ports:
- ${NETWORK}
networks:
- front_net
post:
container_name: post-py
image: "${USERNAME}/post:${POST}"
networks:
- back_net
- front_net
comment:
container_name: comment
image: "${USERNAME}/comment:${COMMENT}"
networks:
- back_net
- front_net
prometheus:
image: ${USERNAME}/prometheus
ports:
- '9090:9090'
volumes:
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus' - Передаем доп. параметры в командной строке
- '--storage.tsdb.retention=1d' - Задаем время хранения метрик в 1 день
networks:
- back_net
- front_net
volumes:
prometheus_data:
post_db:
networks:
front_net:
back_net:
Сборка Docker образов с данного момента производится через скрипт
docker_build.sh поэтому из файла docker_compose.yml удалениы все дерективы build и используется image
Поднимем сервисы, определенные в docker/dockercompose.yml
$ docker-compose up -d Посмотрим список endpoint-ов, с которых собирает информацию Prometheus.
Endpoint-ы в состоянии UP.
Healthcheck-и представляют собой проверки того, что наш сервис здоров и работает в ожидаемом режиме. В нашем случае healthcheck выполняется внутри кода микросервиса и выполняет проверку того, что все сервисы, от которых зависит его работа, ему доступны.
Если требуемые для его работы сервисы здоровы, то healthcheck проверка возвращает status = 1, что соответсвует тому, что сам сервис здоров.
Если один из нужных ему сервисов нездоров или недоступен, то проверка вернет status = 0.
В веб интерфейсе Prometheus выполним поиск по названию метрики ui_health.
Построим график того, как менялось значение метрики ui_health со временем.
Останавливаем сервис post на некоторое время и проверим, как изменится статус ui сервиса, который зависим от post.
$ docker-compose stop postМетрика изменила свое значение на 0, что означает, что UI сервис стал нездоров
Наберем в строке выражений ui_health_ и Prometheus нам предложит дополнить названия метрик.
ui_health_comment_availability
или
ui_health_post_availability
Выберем ui_health_comment_availability
видим, что сервис свой статус не менял в данный промежуток
времени
А с ui_health_post_availability все плохо.
Поднимем post сервис.
$ docker-compose start post Post сервис поправился. UI сервис тоже.
Воспользуемся Node экспортер для сбора информации о работе Docker хоста (виртуалки, где у нас запущены контейнеры) и предоставлению этой информации в Prometheus.
Определим еще один сервис в docker/docker-compose.yml файле.
services:
........
node-exporter:
image: prom/node-exporter:v0.15.2
user: root
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.ignored-mount-points="^/(sys|proc|dev|host|etc)($$|/)"'
networks:
- back_net
Чтобы Prometheus следить за еще одним сервисом, добавим информацию о нем в конфиг
scrape_configs:
...
- job_name: 'node'
static_configs:
- targets:
- 'node-exporter:9100'
Соберем новый Docker для Prometheus:
monitoring/prometheus $ docker build -t $USER_NAME/prometheus .Пересоздадим наши сервисы
$ docker-compose down
$ docker-compose up -d В списоке endpoint-ов Prometheus появится еще один endpoint
Получим информацию об использовании CPU
node_load1
- Зайдем на хост: docker-machine ssh docker-host
- Добавим нагрузки: yes > /dev/null
Нагрузка выросла,мониторинг отображает повышение загруженности CPU
Ссылки на dockerhub
https://cloud.docker.com/u/verty/repository/docker/verty/prometheus
https://cloud.docker.com/repository/docker/verty/post
https://cloud.docker.com/repository/docker/verty/comment
https://cloud.docker.com/repository/docker/verty/uiСодаем Docker хост в GCE.
#!/bin/bash
export GOOGLE_PROJECT=docker-<number project>
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
--google-disk-size 100 \
docker-hostНастраиваем локальное окружение
$ eval $(docker-machine env docker-host) Узнаем IP адрес
$ docker-machine ip docker-hostРазделим файлы Docker compose
Описание приложений в docker-compose.yml
Мониторинг dockercompose-monitoring.yml
Настраиваем cAdvisor для наблюдения за состоянием наших Docker контейнеров.
cAdvisor будем запускать в контейнере. Добавим новый сервис в наш компоуз файл мониторинга docker-compose-monitoring.yml
version: '3.3'
services:
...
cadvisor:
image: google/cadvisor:v0.29.0
volumes:
- '/:/rootfs:ro'
- '/var/run:/var/run:rw'
- '/sys:/sys:ro'
- '/var/lib/docker/:/var/lib/docker:ro'
ports:
- '8080:8080'
networks:
- front_netДобавим информацию о новом сервисе в конфигурацию Prometheus, чтобы он начал собирать метрики
---
global:
scrape_interval: '5s'
scrape_configs:
- job_name: 'cadvisor'
static_configs:
- targets:
- 'cadvisor:8080' Пересоберем образ Prometheus с обновленной конфигурацией.
$ export USER_NAME=dockerhab
$ docker build -t $USER_NAME/prometheus .Запустим сервисы:
$ docker-compose up -d
$ docker-compose -f docker-compose-monitoring.yml up -d Откроем страницу Web UI по адресу http://:8080
Используем инструмент Grafana для визуализации данных из Prometheus.
Добавим новый сервис в docker-compose-monitoring.yml и сервис grafana в одну сеть с Prometheus.
#docker-compose-monitoring.yml
services:
...
grafana:
image: grafana/grafana:5.0.0
volumes:
- grafana_data:/var/lib/grafana
environment:
- GF_SECURITY_ADMIN_USER=admin
- GF_SECURITY_ADMIN_PASSWORD=secret
depends_on:
- prometheus
ports:
- 3000:3000
networks:
- front_net
volumes:
grafana_data: Запустим новый сервис
$ docker-compose -f docker-compose-monitoring.yml up -d grafana Откроем страницу Web UI графаны по адресу http://:3000 и используем для входа логин и пароль админ пользователя, которые мы передали через переменные окружения
После входа добавим источник данных
Зададим нужный тип и параметры подключения
Добавим dashboard загрузить json и поместим его в monitoring/grafana/dashboard поменяв
название файла DockerMonitoring.json
Откроем вновь веб интерфейс Grafana и выберем импортировать шаблон
Загружаем скачанный дашборд. При загрузке указыываем источник данных для визуализации.
Появился набор графиков с информацией о состоянии хостовой системы и работе контейнеров.
В качестве примера метрик приложения в сервис UI i мы добавили: • счетчик ui_request_count, который считает каждый приходящий HTTP запрос (добавляя через лейблы такую информацию как HTTP метод, путь, код возврата, мы уточняем данную метрику) • гистограмму ui_request_latency_seconds, которая позволяет отслеживать информацию о времени обработки каждого запроса
В качестве примера метрик приложения в сервис Post мы добавили: • Гистограмму post_read_db_seconds, которая позволяет отследить информацию о времени требуемом для поиска поста в БД
Добавим информацию о post сервисе в конфигурацию Prometheus, чтобы он начал собирать метрики и с него.
---
global:
scrape_interval: '5s'
scrape_configs:
...
- job_name: 'post'
static_configs:
- targets:
- 'post:5000'
Пересоберем образ Prometheus с обновленной конфигурацией.
$ export USER_NAME=username
$ docker build -t $USER_NAME/prometheus .Пересоздадим нашу Docker инфраструктуру мониторинга:
$ docker-compose -f docker-compose-monitoring.yml down
$ docker-compose -f docker-compose-monitoring.yml up -d Добавим несколько постов в приложении и несколько коментов, чтобы собрать значения метрик приложения.
Создание дашборда в Grafana. В WEB UI по адресу ip_address:3000
Построим графики собираемых метрик приложения. Выберем создать новый дашборд
Выбираем построить график
Жмем один раз на имя графика, затем выбираем Edit
Построим для начала простой график изменения счетчика HTTP запросов по времени. Выберем источник данных и в поле запроса введем название метрики.
Далее достаточно нажать мышкой на любое место UI, чтобы убрать курсов из поля запроса, и Grafana выполнит запрос и построит график.
В правом верхнем углу мы можем уменьшить временной интервал, на котором строим график, и настроить автообновление данных.
Изменим заголовок графика и описание
Построим график запросов, которые возвращают код ошибки на этом же дашборде.
Добавим еще один график на наш дашборд.
Переходим в режим правки графика.
В поле запросов запишем выражение для поиска всех http запросов, у которых код возврата начинается либо с 4 либо с 5 (используем регулярное выражения для поиска по лейблу). Будем использовать функцию rate(), чтобы посмотреть не просто значение счетчика за весь период наблюдения, но и скорость увеличения данной величины за промежуток времени (возьмем, к примеру 1-минутный интервал, чтобы график был хорошо видим)
Для проверки правильности нашего запроса обратимся по несуществующему HTTP пути,
http://docker-machinei-ip:9292/nonexistent
Проверим график (временной промежуток делаем меньше для лучшей видимости графика).
Grafana поддерживает версионирование дашбордов, именно поэтому при сохранении нам предлагалось ввести сообщение, поясняющее изменения дашборда.
Добавим третий по счету график на ваш дашборд. В поле запроса введите следующее выражение для вычисления 95 процентиля времени ответа на запрос
histogram_quantile(0.95, sum(rate(ui_request_latency_seconds_bucket[5m])) by (le))
#Мониторинг бизнес логики Построим график скорости роста значения счетчика за последний час, используя функцию rate(). Это позволит нам получать информацию об активности пользователей приложения.
- Создадим новый дашборд, назовем его Business_Logic_Monitoring и построим график функции rate(post_count[1h]) и rate(comment_count[1h])
Делаем экспорт дашборда и сохраняем его в директории monitoring/grafana/dashboards под названием Business_Logic_Monitoring.json
Создаем новую директорию monitoring/alertmanager. В этой директории Dockerfile со следующим содержимым:
FROM prom/alertmanager:v0.14.0
ADD config.yml /etc/alertmanager/В директории monitoring/alertmanager создаем файл config.yml, в котором определим отправку нотификаций в тестовый слак канал, через Incomig Webhook
global:
slack_api_url: 'https://hooks.slack.com/services/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'
route:
receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#dmitrii_vlasov'- Соберем образ alertmanager: monitoring/alertmanager $ docker build -t $USER_NAME/alertmanager .
- Добавим новый сервис в компоуз файл мониторинга.
version: '3.3'
services:
...
alertmanager:
image: ${USER_NAME}/alertmanager
command:
- '--config.file=/etc/alertmanager/config.yml'
ports:
- '9093:9093'
networks:
- front_netСоздадим файл alerts.yml в директории prometheus
monitoring/prometheus/alerts.yml
groups:
- name: alert.rules
rules:
- alert: InstanceDown
expr: up == 0
for: 1m
labels:
severity: page
annotations:
description: '{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 1 minute'
summary: 'Instance {{ $labels.instance }} down'Добавим операцию копирования данного файла в Dockerfile:
monitoring/prometheus/Dockerfile
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/
ADD alerts.yml /etc/prometheus/Добавим информацию о правилах, в конфиг Prometheus
---
global:
scrape_interval: '5s'
rule_files:
- "alerts.yml"
alerting:
alertmanagers:
- scheme: http
static_configs:
- targets:
- "alertmanager:9093"Пересоберем образ Prometheus:
$ docker build -t $USER_NAME/prometheus .Пересоздадим нашу Docker инфраструктуру мониторинга:
$ docker-compose -f docker-compose-monitoring.yml down
$ docker-compose -f docker-compose-monitoring.yml up -d Алерты можно посмотреть в веб интерфейсе Prometheus вкладка "Alerts"
Остановим один из сервисов и подождем одну минуту
$ docker-compose stop post В канал должно придти сообщение
AlertManager
| [FIRING:1] InstanceDown(post:5000 post)
Ссылки на dockerhub
https://cloud.docker.com/u/verty/repository/docker/verty/prometheus
https://cloud.docker.com/repository/docker/verty/post
https://cloud.docker.com/repository/docker/verty/comment
https://cloud.docker.com/repository/docker/verty/ui
https://cloud.docker.com/repository/docker/verty/alertmanagerВыполните сборку образов при помощи скриптов docker_build.sh из корня репозитория.
for i in ui post-py comment; do cd src/$i; bash docker_build.sh; cd -; doneСоздадим Docker хост в GCE и настроим локальное окружение на работу с ним
#!/bin/bash
export GOOGLE_PROJECT=docker-224713
docker-machine create --driver google \
--google-machine-image https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/family/ubuntu-1604-lts \
--google-machine-type n1-standard-1 \
--google-zone europe-west1-b \
--google-disk-size 100 \
--google-open-port 5601/tcp \
--google-open-port 9292/tcp \
--google-open-port 9411/tcp \
loggingСоздадим отдельный compose-файл для нашей системы логирования в папке docker/
docker/docker-compose-logging.yml
version: '3'
services:
fluentd:
image: ${USERNAME}/fluentd
ports:
- "24224:24224"
- "24224:24224/udp"
elasticsearch:
image: elasticsearch:5.6.14-alpine
expose:
- 9200
ports:
- "9200:9200"
kibana:
image: kibana:5.6.14
ports:Создадим образ Fluentd с нужной нам конфигурацией.
Создаём в вашем проекте microservices директорию logging/fluentd
В созданной директорий, создем Dockerfile.
FROM fluent/fluentd:v0.12
RUN gem install fluent-plugin-elasticsearch --no-rdoc --no-ri --version 1.9.5
RUN gem install fluent-plugin-grok-parser --no-rdoc --no-ri --version 1.0.0
ADD fluent.conf /fluentd/etcВ этой же директории создаем файл конфигурации.
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<match *.**>
@type copy
<store>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix fluentd
logstash_dateformat %Y%m%d
include_tag_key true
type_name access_log
tag_key @log_name
flush_interval 1s
</store>
<store>
@type stdout
</store>
</match>
Соберем docker image для fluentd
docker build -t $USER_NAME/fluentd .Запускаем сервисы приложения.
docker/ $ docker-compose up -dВыполним команду для просмотра логов post сервиса:
docker/ $ docker-compose logs -f post Определим драйвер для логирования для сервиса post внутри compose-файла
version: '3.3'
services:
...
post:
container_name: reddit_post_1
image: "${USERNAME}/post:${POST}"
environment:
- POST_DATABASE_HOST=post_db
- POST_DATABASE=posts
- ZIPKIN_ENABLED=${ZIPKIN_ENABLED}
depends_on:
- post_db
ports:
- "5000:5000"
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: service.postПоднимем инфраструктуру централизованной системы
$ docker-compose -f docker-compose-logging.yml up -d
$ docker-compose down
$ docker-compose up -d Создадим несколько постов в приложении
Добавим фильтр для парсинга json логов, приходящих от post сервиса, в конфиг fluentd
<source>
@type forward
port 24224
bind 0.0.0.0
</source>
<filter service.post>
@type parser
format json
key_name log
</filter>
<filter service.ui>
@type parser
key_name log
format grok
grok_pattern %{RUBY_LOGGER}
</filter>
<filter service.ui>
@type parser
format grok
grok_pattern service=%{WORD:service} \| event=%{WORD:event} \| request_id=%{GREEDYDATA:request_id} \| message='%{GREEDYDATA:message}'
key_name message
reserve_data true
</filter>
<filter service.ui>
@type parser
format grok
key_name message
grok_pattern service=%{WORD:service} \| event=%{WORD:event} \| path=%{URIPATH:path} \| request_id=%{UUID:request_id} \| remote_addr=%{IP:remote_addr} \| method=%{GREEDYDATA:method} \| response_status=%{INT:response_status}
</filter>
<match *.**>
@type copy
<store>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix fluentd
logstash_dateformat %Y%m%d
include_tag_key true
type_name access_log
tag_key @log_name
flush_interval 1s
</store>
<store>
@type stdout
</store>
</match>
Обновим образ
docker build -t $USER_NAME/fluentd .
Перзапустим docker-compose
$ docker-compose -f docker-compose-logging.yml down
$ docker-compose -f docker-compose-logging.yml up -dДобавим в compose-файл для сервисов логирования сервис распределенного трейсинга Zipkin
version: '3'
services:
zipkin:
image: openzipkin/zipkin
ports:
- "9411:9411"
fluentd:
build: ./fluentd
ports:
- "24224:24224"
- "24224:24224/udp"
elasticsearch:
image: elasticsearch
expose:
- 9200
ports:
- "9200:9200"
kibana:
image: kibana
ports:
- "8080:5601"В docker/docker-compose.yml добавим для каждого сервиса поддержку ENV переменных и задаем параметризованный параметр ZIPKIN_ENABLED
environment:
- ZIPKIN_ENABLED=${ZIPKIN_ENABLED}
В .env файле
ZIPKIN_ENABLED=true Обновим приложения
docker-compose up -dZipkin должен быть в одной сети с приложениями
version: '3'
services:
zipkin:
image: openzipkin/zipkin
ports:
- "9411:9411"
networks:
- back_net
- front_net
etworks:
back_net:
front_net: Пересоздадим наши сервисы
$ docker-compose -f docker-compose-logging.yml -f docker-compose.yml down
$ docker-compose -f docker-compose-logging.yml -f docker-compose.yml up -dОткроем Zipkin WEB UI на порту 9411
Пройдена Kubernetes The Hard Way.
kubectl apply -f проходит по созданным deployment-ам (ui, post, mongo, comment) и поды запускаются.
Устанавливаем virtualbox minikube.
Запускаем Minukube-кластер.
$ minikube start
Starting local Kubernetes v1.10.0 cluster...
Starting VM...
Getting VM IP address...
Moving files into cluster...
Setting up certs...
Connecting to cluster...
Setting up kubeconfig...
Starting cluster components...
Kubectl is now configured to use the cluster.
Loading cached images from config file.
Minikube-кластер развернут. При этом автоматически был настроен конфиг kubectl.
Проверим, что это так:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
minikube Ready master 21h v1.10.0Создаем компоненты.
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
replicas: 3
selector:
matchLabels:
app: reddit
component: ui
template:
metadata:
name: ui-pod
labels:
app: reddit
component: ui
spec:
containers:
- image: verty/ui
name: uiЗапустим в Minikube ui-компоненту
$ kubectl apply -f ui-deployment.yml
deployment "ui" created Убедимся, что во 2,3,4 и 5 столбцах стоит число 3 (число реплик ui):
$ kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
ui 3 3 3 3 23hПробросим порт на локальную машину с помощью kubectl
$ kubectl port-forward <pod-name> 8080:9292Зайдем в браузере на http://localhost:8080 убеддимся что UI работает и подключим остальные компоненты.
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: comment
labels:
app: reddit
component: comment
spec:
replicas: 3
selector:
matchLabels:
app: reddit
component: comment
template:
metadata:
name: comment
labels:
app: reddit
component: comment
spec:
containers:
- image: verty/comment
name: comment---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: post
labels:
app: reddit
component: post
spec:
replicas: 3
selector:
matchLabels:
app: reddit
component: post
template:
metadata:
name: post
labels:
app: reddit
component: post
spec:
containers:
- image: verty/post
name: post---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mongo
labels:
app: reddit
component: mongo
comment-db: "true"
post-db: "true"
spec:
replicas: 1
selector:
matchLabels:
app: reddit
component: mongo
template:
metadata:
name: mongo
labels:
app: reddit
component: mongo
comment-db: "true"
post-db: "true"
spec:
containers:
- image: mongo:3.2
name: mongo
volumeMounts:
- name: mongo-persistent-storage
mountPath: /data/db
volumes:
- name: mongo-persistent-storage
emptyDir: {}Создаем для связи объект Service - абстракция, которая определяет набор POD-ов (Endpoints) и
способ доступа к ним.
---
apiVersion: v1
kind: Service
metadata:
name: comment
labels:
app: reddit
component: comment
spec:
ports:
- port: 9292
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: comment---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: NodePort
ports:
- port: 9292
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: ui---
apiVersion: v1
kind: Service
metadata:
name: post
labels:
app: reddit
component: post
spec:
ports:
- port: 5000
protocol: TCP
targetPort: 5000
selector:
app: reddit
component: postПО label-ам должны были быть найдены соответствующие POD-ы.
$ kubectl describe service comment | grep -i end
Endpoints: 172.17.0.13:9292,172.17.0.15:9292,172.17.0.16:9292Изнутри любого POD-а должно разрешаться nslookup:
nslookup: can't resolve '(null)': Name does not resolve
Name: comment
Address 1: 10.100.11.98 comment.default.svc.cluster.local---
apiVersion: v1
kind: Service
metadata:
name: mongodb
labels:
app: reddit
component: mongo
spec:
ports:
- port: 27017
protocol: TCP
targetPort: 27017
selector:
app: reddit
component: mongo
Проверяем пробрасываем порт на ui pod
kubectl port-forward ui 9292:9292 Заходим на http://locahost:9292
Не доступен blog post
Can't show blog posts,some problems with the post service. Refresh& Решаем проблемму с помощью сервисов. Делаем service для comment и post.
---
apiVersion: v1
kind: Service
metadata:
name: comment-db
labels:
app: reddit
component: mongo
comment-db: "true"
spec:
ports:
- port: 27017
protocol: TCP
targetPort: 27017
selector:
app: reddit
component: mongo
comment-db: "true"---
apiVersion: v1
kind: Service
metadata:
name: post-db
labels:
app: reddit
component: mongo
post-db: "true"
spec:
ports:
- port: 27017
protocol: TCP
targetPort: 27017
selector:
app: reddit
component: mongo
post-db: "true"---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mongo
labels:
app: reddit
component: mongo
comment-db: "true"
post-db: "true"
spec:
replicas: 1
selector:
matchLabels:
app: reddit
component: mongo
template:
metadata:
name: mongo
labels:
app: reddit
component: mongo
comment-db: "true"
post-db: "true"
spec:
containers:
- image: mongo:3.2
name: mongo
volumeMounts:
- name: mongo-persistent-storage
mountPath: /data/db
volumes:
- name: mongo-persistent-storage
emptyDir: {}В ui-service.yml добавляем новый тип service NodePort.
---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: NodePort
ports:
- port: 9292
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: uiПроверка
$ minikube service ui
|-------------|----------------------|-----------------------------|
| NAMESPACE | NAME | URL |
|-------------|----------------------|-----------------------------|
| default | comment | No node port |
| default | comment-db | No node port |
| default | kubernetes | No node port |
| default | mongodb | No node port |
| default | post | No node port |
| default | post-db | No node port |
| default | ui | http://192.168.99.128:31400 |
| kube-system | kube-dns | No node port |
|-------------|----------------------|-----------------------------|---
apiVersion: v1
kind: Namespace
metadata:
name: dev
$ kubectl apply -f dev-namespace.ymlЗапускаем приложение в namespace.
$ kubectl apply -n dev -f kubernates/redditПроверяем что запустилось, смотрим результат.
$ minikube service ui -n devOpening kubernetes service dev/ui in default browser...
[1]Microservices Reddit in dev ui-5769658b67-hzglw container
Menu
* [2]All posts
* [3]New postДобавим инфу об окружении внутрь контейнера UI
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: ui
...
spec:
containers:
- image: verty/ui
name: ui
env:
- name: ENV
valueFrom:
fieldRef:
fieldPath: metadata.namespace
В gcloud console, переходим в “kubernetes clusters” Создаем кластер со следующими настройками.
- Версия кластера - 1.8.10-gke.0
- Тип машины - небольшая машина (1,7 ГБ) (для экономии ресурсов)
- Размер - 2
- Базовая аутентификация - отключена
- Устаревшие права доступа - отключено
- Панель управления Kubernetes - отключено
- Размер загрузочного диска - 20 ГБ
После создания подключаемся к GKE для запуска приложения, убедимся что подключились к кластеру
kubectl config current-context
gke_docker-224713_europe-west1-b_standard-cluster-1Запустим приложение в GKE Создадим dev namespace.
$ kubectl apply -f ./kubernetes/reddit/dev-namespace.yml Задеплоим все компоненты приложения в namespace dev.
$ kubectl apply -f ./kubernetes/reddit/ -n devОткроем Reddit в правила брандмауэра.
Найдем внешний ip-адрес любой ноды из кластера
$ kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
gke-standard-cluster-1-default-pool-2dd150ba-ljhm Ready <none> 4d v1.10.11-gke.1 35.240.56.126 Container-Optimized OS from Google 4.14.65+ docker://17.3.2
gke-standard-cluster-1-default-pool-2dd150ba-qwwj Ready <none> 4d v1.10.11-gke.1 35.195.171.80 Container-Optimized OS from Google 4.14.65+ docker://17.3.2Порт публикации сервиса ui
$ kubectl describe service ui -n dev | grep NodePort
Type: NodePort
NodePort: <unset> 31689/TCPПроверяем по адресу
http://35.240.56.126:31689
[1]Microservices Reddit in dev ui-557c59b75f-kkz5b container
Menu
* [2]All posts
* [3]New post
Настроим соответствующим образом Service UI
ui-service.yml
---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: LoadBalancer
ports:
- port: 80
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: ui
Применем конфигурацию
$ kubectl apply -f ui-service.yml -n devСмотрим внешний ip
kubectl get service -n dev --selector component=ui
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ui NodePort 10.7.242.81 33.135.3.22 80:30227/TCP 5dСоздадим Ingress для сервиса UI
ui-ingress.yml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ui
spec:
backend:
serviceName: ui
servicePort: 9292Применим конфиг
$ kubectl apply -f ui-ingress.yml -n devСмотрим в сам кластер:
NAME HOSTS ADDRESS PORTS AGE
ui * 35.227.215.170 80 3hУбираемодин балансировщик.
ui-service.yml
---
apiVersion: v1
kind: Service
metadata:
name: ui
labels:
app: reddit
component: ui
spec:
type: NodePort
ports:
- port: 9292
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: uiНастроим на работу Ingress Controller как классический веб
ui-ingress.yml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ui
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: ui
servicePort: 9292Смотрим external адрес Ingress
$ kubectl get ingress -n dev
NAME HOSTS ADDRESS PORTS AGE
ui * 35.227.215.170 80 3hПодготовим сертификат используя IP как CN.
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=35.227.215.170"Грузим сертификат в кластер kubernetes
$ kubectl create secret tls ui-ingress --key tls.key --cert tls.crt -n dev Проверяем командой
$ kubectl describe secret ui-ingress -n dev
Name: ui-ingress
Namespace: dev
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tls
Data
====
tls.crt: 1111 bytes
tls.key: 1704 bytesНастроим Ingress на прием только HTTPS траффика
ui-ingress.yml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ui
annotations:
kubernetes.io/ingress.allow-http: "false"
spec:
tls:
- secretName: ui-ingress
backend:
serviceName: ui
servicePort: 9292Применим
$ kubectl apply -f ui-ingress.yml -n devЗаходим на страницу приложения по https
Найдите имя кластера
$ gcloud beta container clusters list
NAME LOCATION MASTER_VERSION MASTER_IP MACHINE_TYPE NODE_VERSION NUM_NODES STATUS
standard-cluster-1 europe-west1-b 1.10.11-gke.1 35.205.148.14 g1-small 1.10.11-gke.1 2 RUNNINGВключим network-policy для GKE.
$ gcloud beta container clusters update standard-cluster-1 \
--zone=europe-west1-b --update-addons=NetworkPolicy=ENABLED
$ gcloud beta container clusters update standard-cluster-1 \
--zone=europe-west1-b --enable-network-policyСоздадим mongo-network-policy
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-db-traffic
labels:
app: reddit
spec:
podSelector:
matchLabels:
app: reddit
component: mongo
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: reddit
component: comment
Применяем политику
$ kubectl apply -f mongo-network-policy.yml -n devЗаходим в приложение.Post-сервис не может достучаться до базы
Добавляем Post-сервис в mongo-network-policy
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-db-traffic
labels:
app: reddit
spec:
podSelector:
matchLabels:
app: reddit
component: mongo
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: reddit
component: comment
- podSelector:
matchLabels:
app: reddit
component: postСоздадим диск в Google Cloud
$ gcloud compute disks create --size=25GB --zone=europe-west1-b reddit-mongo-diskДобавим новый Volume POD-у базы.
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mongo
labels:
...
containers:
- image: mongo:3.2
name: mongo
volumeMounts:
- name: mongo-gce-pd-storage
mountPath: /data/db
volumes:
- name: mongo-gce-pd-storage
gcePersistentDisk:
pdName: reddit-mongo-disk
fsType: ext4Применем конфигурацию
$ kubectl apply -f mongo-deployment.ymlПосле пересоздания poda приложение и добавим пост. Удалим deployment.
$ kubectl delete deploy mongo -n devСнова создадим деплой mongo.
$ kubectl apply -f mongo-deployment.yml -n devПроверим что пост остался на месте.
Создадим описание PersistentVolume
mongo-volume.yml
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: reddit-mongo-disk
spec:
capacity:
storage: 25Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
gcePersistentDisk:
fsType: "ext4"
pdName: "reddit-mongo-disk"Добавим PersistentVolume в кластер
$ kubectl apply -f mongo-volume.yml -n dev Создадим описание PersistentVolumeClaim (PVC)
mongo-claim.yml
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mongo-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 15GiДобавим PersistentVolumeClaim в кластер
$ kubectl apply -f mongo-claim.yml -n devПодключим PVC к нашим Pod'ам
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mongo
...
spec:
containers:
- image: mongo:3.2
name: mongo
volumeMounts:
- name: mongo-gce-pd-storage
mountPath: /data/db
volumes:
- name: mongo-gce-pd-storage
persistentVolumeClaim:
claimName: mongo-pvcОбновим описание нашего Deployment’а
$ kubectl apply -f mongo-deployment.yml -n dev Создадим описание StorageClass’а
storage-fast.yml
---
kind: StorageClass
apiVersion: storage.k8s.io/v1beta1
metadata:
name: fast
provisioner: kubernetes.io/gce-pd
parameters:
type: pd-ssdДобавим StorageClass в кластер
$ kubectl apply -f storage-fast.yml -n devСоздадим описание PersistentVolumeClaim
mongo-claim-dynamic.yml
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mongo-pvc-dynamic
spec:
accessModes:
- ReadWriteOnce
storageClassName: fast
resources:
requests:
storage: 10GiДобавим StorageClass в кластер
$ kubectl apply -f mongo-claim-dynamic.yml -n devПодключим PVC к нашим Pod'ам
mongo-deployment.yml
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: mongo
...
spec:
containers:
- image: mongo:3.2
name: mongo
volumeMounts:
- name: mongo-gce-pd-storage
mountPath: /data/db
volumes:
- name: mongo-gce-pd-storage
persistentVolumeClaim:
claimName: mongo-pvc-dynamic Обновим описание нашего Deployment'а
$ kubectl apply -f mongo-deployment.yml -n devПосмотрит какие у нас получились PersistentVolume'ы
$ kubectl get persistentvolume -n dev
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-767eac0f-2462-11e9-a09e-42010a840134 10Gi RWO Delete Bound default/mongo-pvc-dynamic fast 1h
pvc-8ab285e7-2401-11e9-b2db-42010a84011e 15Gi RWO Delete Bound dev/mongo-pvc standard 12h
reddit-mongo-disk 25Gi RWO Retain Available 12hСоздем файл tiller.yml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-systemПрименим его.
$ kubectl apply -f tiller.yml Запустим tiller-сервер
$ helm init --service-account tiller Проверим
$ kubectl get pods -n kube-system --selector app=helm
NAME READY STATUS RESTARTS AGE
tiller-deploy-689d79895f-f22hj 1/1 Running 0 3dСоздаем директорию Charts co следующей структурой директорий
kubernetes/Charts/
├── comment
├── post
├── reddit
└── ui
Создаем файл-описание chart’а
---
name: ui
version: 1.0.0
description: OTUS reddit application UI
maintainers:
- name: Dmitry Mischenko
email: my@mail.com
appVersion: 1.0
- Создаем директорию ui/templates
- Перенесите в неё все манифесты, разработанные ранее для сервиса ui (ui-service, ui-deployment, ui-ingress)
- Переименуем их (уберем префикс “ui-“) и поменяем расширение на .yaml
kubernetes/Charts/ui/
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yamlУстановим Chart
$ helm install --name test-ui-1 ui/ Просмотрим что получилось
$ helm ls
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
test-ui 2 Sat Feb 2 00:31:41 2019 DEPLOYED test-ui-0.1.0 default Шаблонизируем Chart.
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
app: reddit
component: ui
release: {{ .Release.Name }}
spec:
type: NodePort
ports:
- port: {{ .Values.service.externalPort }}
protocol: TCP
targetPort: 9292
selector:
app: reddit
component: ui
release: {{ .Release.Name }} Шаблонизируем подобным образом остальные сущности
kubernetes/Charts/ui/templates/deployment.yaml
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
app: reddit
component: ui
release: {{ .Release.Name }}
spec:
replicas: 3
startegy:
type: Recreate
selector:
matchLabels:
app: reddit
component: ui
release: {{ .Release.Name }}
template:
metadata:
name: ui-pod
labels:
app: reddit
component: ui
release: {{ .Release.Name }}
spec:
containers:
- image: verty/ui
name: ui
ports:
- containerPort:
name: ui
protocol: TCP
env:
- name: ENV
valueFrom:
fieldRef:
fieldPath: metadata.namespace
kubernetes/Charts/ui/templates/ingress.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
annotations:
kubernetes.io/ingress.class: "gce"
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: {{ .Release.Name }}-{{ .Chart.Name }}
servicePort: 9292 Установим несколько релизов ui
$ helm install ui --name ui-1
$ helm install ui --name ui-2
$ helm install ui --name ui-3 Смотрим процессы ingresa
$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
ui-1-ui * 35.233.15.11 80 2d
ui-2-ui * 35.190.33.14 80 2d
ui-3-ui * 35.244.132.114 80 2dПо IP-адресам можно попасть на разные релизы ui приложений.
kubernetes/Charts/ui/templates/deployment.yaml
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
app: reddit
component: ui
release: {{ .Release.Name }}
spec:
replicas: 3
startegy:
type: Recreate
selector:
matchLabels:
app: reddit
component: ui
release: {{ .Release.Name }}
template:
metadata:
name: ui-pod
labels:
app: reddit
component: ui
release: {{ .Release.Name }}
spec:
containers:
- image: "{{ .Values.image.repository }}/ui:{{ .Values.image.tag }}"
name: ui
ports:
- containerPort: {{ .Values.service.internalPort }}
name: ui
protocol: TCP
env:
- name: ENV
valueFrom:
fieldRef:
fieldPath: metadata.namespace
kubernetes/Charts/ui/templates/service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
labels:
app: reddit
component: ui
release: {{ .Release.Name }}
spec:
type: NodePort
ports:
- port: {{ .Values.service.externalPort }}
protocol: TCP
targetPort: {{ .Values.service.internalPort }}
selector:
app: reddit
component: ui
release: {{ .Release.Name }}
kubernetes/Charts/ui/templates/ingress.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ .Release.Name }}-{{ .Chart.Name }}
annotations:
kubernetes.io/ingress.class: "gce"
spec:
rules:
- http:
paths:
- path: /*
backend:
serviceName: {{ .Release.Name }}-{{ .Chart.Name }}
servicePort: {{ .Values.service.externalPort }} Определим значения собственных переменных
kubernetes/Charts/ui/values.yaml
---
service:
internalPort: 9292
externalPort: 9292
image:
repository: verty/ui
tag: latestОбновляем
$ helm upgrade ui-1 ui/
$ helm upgrade ui-2 ui/
$ helm upgrade ui-3 ui/Собран Chart для развертывания ui-компоненты приложения. Он имеет следующую структуру.
kubernetes/Charts/ui
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yaml
└── values.yamlСобраем пакеты для остальных компонент post,comment
Итоговая структура выглядет так:
kubernetes/Charts/
├── comment
│ ├── Chart.yaml
│ ├── templates
│ │ ├── deployment.yaml
│ │ ├── ingress.yaml
│ │ └── service.yaml
│ └── values.yaml
├── post
│ ├── Chart.yaml
│ ├── templates
│ │ ├── deployment.yaml
│ │ ├── ingress.yaml
│ │ └── service.yaml
│ └── values.yaml
└── ui
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── ingress.yaml
│ └── service.yaml
└── values.yamlИспользование "Helper" шаблонов, располагаюся в файле "_helpers.tpl"
{{- define "comment.fullname" -}}
{{- printf "%s-%s" .Release.Name .Chart.Name }}
{{- end -}} Которая в результате выдаст то же, что и:
{{ .Release.Name }}-{{ .Chart.Name }}Меняем в соответствующие строчки в файле, чтобы использовать helper
kubernetes/Charts/comment/templates/service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: {{ template "comment.fullname" . }}
labels:
app: reddit
component: comment
release: {{ .Release.Name }}
spec:
type: ClusterIP
ports:
- port: {{ .Values.service.externalPort }}
protocol: TCP
targetPort: {{ .Values.service.internalPort }}
selector:
app: reddit
component: comment
release: {{ .Release.Name }}Структура становится следующая:
kubernetes/Charts/
├── comment
│ ├── Chart.yaml
│ ├── templates
│ │ ├── deployment.yaml
│ │ ├── _helpers.tpl
│ │ ├── ingress.yaml
│ │ └── service.yaml
│ └── values.yaml
├── post
│ ├── Chart.yaml
│ ├── templates
│ │ ├── deployment.yaml
│ │ ├── _helpers.tpl
│ │ ├── ingress.yaml
│ │ └── service.yaml
│ └── values.yaml
└── ui
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── ingress.yaml
│ └── service.yaml
└── values.yaml
- Создаем reddit/Chart.yaml
name: reddit
version: 0.1.0
description: OTUS sample reddit application
maintainers:
- name: Dmitrii Vlasov
email: gcp@moslex.ru- Пустой reddit/values.yaml
В директории Chart’а reddit создадим файл
dependencies:
- name: ui
version: 1.0.0
repository: file://../ui
- name: post
version: 1.0.0
repository: file://../post
- name: comment
version: 1.0.0
repository: file://../commentНужно загрузить зависимости
$ helm dep update- Появится файл requirements.lock с фиксацией зависимостей
- Будет создана директория charts с зависимостями в виде архивов Структура станет следующей:
kubernetes/Charts/reddit/
├── charts
│ ├── comment-1.0.0.tgz
│ ├── mongodb-0.4.18.tgz
│ ├── post-1.0.0.tgz
│ └── ui-1.0.0.tgz
├── Chart.yaml
├── requirements.lock
├── requirements.yaml
└── values.yamlChart для базы данных возьмем готовый.
- Найдем Chart в общедоступном репозитории
$ helm search mongo
NAME CHART VERSION APP VERSION DESCRIPTION
stable/mongodb 5.3.1 4.0.5 NoSQL document-oriented database that stores JSON-like do...
stable/mongodb-replicaset 3.9.0 3.6 NoSQL document-oriented database that stores JSON-like do...
stable/unifi 0.2.8 5.9.29 Ubiquiti Network's Unifi Controller - добавим в reddit/requirements.yaml
dependencies:
- name: ui
version: 1.0.0
repository: file://../ui
- name: post
version: 1.0.0
repository: file://../post
- name: comment
version: 1.0.0
repository: file://../comment
- name: mongodb
version: 0.4.18
repository: https://kubernetes-charts.storage.googleapis.com - Выгрузим зависимости
$ helm dep update Установим приложение:
$ helm install reddit --name reddit-test Смотрим адресс с помощью ingress
NAME HOSTS ADDRESS PORTS AGE
reddit-test-comment * 80 3d
reddit-test-post * 80 3d
reddit-test-ui * 35.244.132.114 80 3d
Проверяем работоспособность приложения.
elinks 35.244.132.114
[1]Microservices Reddit in default reddit-test-ui-7d7fbdb6c9-pkjjt
container
1
[2]Работает!
01-02-2019
21:32
[3]Go to the link
Menu
* [4]All posts
* [5]New post
References
Visible links
1. http://35.244.132.114/
2. http://35.244.132.114/post/5c54baee4406720014dab941
3. http://#/
4. http://35.244.132.114/
5. http://35.244.132.114/newaДобавим в ui/templates/deployments.yaml
---
spec:
containers:
...
env:
- name: POST_SERVICE_HOST
value: {{ .Values.postHost | default (printf "%s-post" .Release.Name) }}
- name: POST_SERVICE_PORT
value: {{ .Values.postPort | default "5000" | quote }}
- name: COMMENT_SERVICE_HOST
value: {{ .Values.commentHost | default (printf "%s-comment" .Release.Name) }}
- name: COMMENT_SERVICE_PORT
value: {{ .Values.commentPort | default "9292" | quote }}
- name: ENVДобавим в ui/values.yaml
---
postHost:
postPort:
commentHost:
commentPort:Задавать переменные для зависимостей прямо в values.yaml самого Chart’а reddit.
comment:
image:
repository: verty/comment
tag: latest
service:
externalPort: 9292
post:
image:
repository: verty/post
tag: latest
service:
externalPort: 5000
ui:
image:
repository: verty/ui
tag: latest
service:
externalPort: 9292После обновления UI - нужно обновить зависимости чарта reddit.
$ helm dep update reddit
Hang tight while we grab the latest from your chart repositories...
...Unable to get an update from the "local" chart repository (http://127.0.0.1:8879/charts):
Get http://127.0.0.1:8879/charts/index.yaml: dial tcp 127.0.0.1:8879: connect: connection refused
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 4 charts
Downloading mongodb from repo https://kubernetes-charts.storage.googleapis.com
Deleting outdated chartsОбновим релиз, установленный в k8s
$ helm upgrade reddit-test ./reddit
Release "reddit-test" has been upgraded. Happy Helming!
LAST DEPLOYED: Mon Feb 4 16:59:16 2019
NAMESPACE: default
STATUS: DEPLOYED
RESOURCES:
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
reddit-test-comment-5cd8f9ff8-2tjhc 0/1 Evicted 0 2d2h
reddit-test-comment-5cd8f9ff8-59rjz 0/1 Evicted 0 8h
reddit-test-comment-5cd8f9ff8-bp4r6 0/1 Evicted 0 34h
reddit-test-comment-5cd8f9ff8-d28b9 0/1 Evicted 0 18h
reddit-test-comment-5cd8f9ff8-d6g4w 0/1 Evicted 0 40h
reddit-test-comment-5cd8f9ff8-l65p6 0/1 Evicted 0 13h
reddit-test-comment-5cd8f9ff8-m42vw 0/1 Evicted 0 3d
reddit-test-comment-5cd8f9ff8-p95kb 0/1 Evicted 0 45h
reddit-test-comment-5cd8f9ff8-rqs92 0/1 Evicted 0 23h
reddit-test-comment-5cd8f9ff8-rw26x 0/1 Evicted 0 29h
reddit-test-comment-5cd8f9ff8-w4j6r 0/1 Evicted 0 2d8h
reddit-test-comment-5cd8f9ff8-wm9mt 0/1 Evicted 0 2d13h
reddit-test-comment-5cd8f9ff8-x7cnr 1/1 Running 0 171m
reddit-test-mongodb-7cc9bcc5bb-jsqzt 1/1 Running 0 3d
reddit-test-post-6574b88755-qtx87 1/1 Running 0 3d
reddit-test-ui-7d7fbdb6c9-frsdp 1/1 Running 0 2d16h
reddit-test-ui-7d7fbdb6c9-pkjjt 1/1 Running 0 2d16h
reddit-test-ui-7d7fbdb6c9-pmr5t 1/1 Running 0 2d16h
==> v1/Secret
NAME TYPE DATA AGE
reddit-test-mongodb Opaque 2 3d
==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
reddit-test-mongodb Bound pvc-5f5a472a-2623-11e9-a64f-42010a8402b4 8Gi RWO standard 3d
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
reddit-test-comment ClusterIP 10.7.240.172 <none> 9292/TCP 3d
reddit-test-mongodb ClusterIP 10.7.247.102 <none> 27017/TCP 3d
reddit-test-post ClusterIP 10.7.255.28 <none> 5000/TCP 3d
reddit-test-ui NodePort 10.7.253.33 <none> 9292:30990/TCP 3d
==> v1beta2/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
reddit-test-comment 1 1 1 1 3d
reddit-test-post 1 1 1 1 3d
reddit-test-ui 3 3 3 3 3d
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
reddit-test-mongodb 1 1 1 1 3d
==> v1beta1/Ingress
NAME HOSTS ADDRESS PORTS AGE
reddit-test-comment * 80 3d
reddit-test-post * 80 3d
reddit-test-ui * 35.244.132.114 80 3d
Проверяем UI.
[1]Microservices Reddit in default reddit-test-ui-7d7fbdb6c9-pkjjt
container
1
[2]Работает!
01-02-2019
21:32
[3]Go to the link
Menu
* [4]All posts
* [5]New post
References
Visible links
1. http://35.244.132.114/
2. http://35.244.132.114/post/5c54baee4406720014dab941
3. http://#/
4. http://35.244.132.114/
5. http://35.244.132.114/newСоздаем GKE-кластер
Делаем новый пулузлов:
- bigpool
- 1 узел типаn n1-standard-2 (7,5 Гб, 2 виртуальныхЦП)
- Размердиска 40 Гб
Устанавливаем Gitlab помощью Helm Chart’а из пакета Omnibus
$ helm repo add gitlab https://charts.gitlab.ioКачаем Chart
$ helm fetch gitlab/gitlab-omnibus --version 0.1.37 --untar
$ cd gitlab-omnibus
Правим yml
gitlab-omnibus/values.yaml
---
baseDomain: example.com
legoEmail: you@example.com
gitlab-omnibus/templates/gitlab/gitlab-svc.yaml
---
apiVersion: v1
kind: Service
metadata:
name: {{ template "fullname" . }}
...
selector:
name: {{ template "fullname" . }}
ports:
...
- name: prometheus
port: 9090
targetPort: prometheus
- name: web
port: 80
targetPort: workhorse
gitlab-omnibus/templates/gitlab-config.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ template "fullname" . }}-config
labels:
app: {{ template "fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
data:
external_scheme: http
external_hostname: {{ template "fullname" . }}
gitlab-omnibus/templates/ingress/gitlab-ingress.yaml
---
apiVersion: extensions/v1beta1
kind: Ingress
...
spec:
tls:
...
rules:
- host: {{ template "fullname" . }}
http:
paths:Устанавливаем gitlab
$ helm install --name gitlab . -f values.yaml Смотрим выданный IP-адрес ingress-контроллера nginx.
$ kubectl get service -n nginx-ingress nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx LoadBalancer 10.23.255.237 35.246.112.70 80:31846/TCP,443:30749/TCP,22:30505/TCP 1dДобавляем его в локальный файл /etc/hosts
echo "35.246.112.70 gitlab-gitlab staging production" >> /etc/hosts
Проверяем что gitlab поднялся
NAME READY STATUS RESTARTS AGE
gitlab-gitlab-6654b8f4c5-hvmr8 1/1 Running 0 1dИдем по адресу http://gitlb-gitlab
Создаем директорию Gitlab_ci со следующей структурой директорий
Gitlab_ci
├── comment
├── post
├── reddit-deploy
└── uiПереносим исходные коды сервиса ui в Gitlab_ci/ui
Gitlab_ci/ui/
├── build_info.txt
├── config.ru
├── docker_build.sh
├── Dockerfile
├── Gemfile
├── Gemfile.lock
├── helpers.rb
├── middleware.rb
├── ui_app.rb
├── VERSION
└── views
├── create.haml
├── index.haml
├── layout.haml
└── show.hamlВ директории Gitlab_ci/ui:
- Инициализируем локальный git-репозиторий
$ git init - Добавим удаленный репозиторий
$ git remote add origin http://gitlab-gitlab/verty/ui.git- Закоммитим и отправим в gitlab
$ git add .
$ git commit -m “init”
$ git push origin masterДелаем то же самое для post и comment. Переносим содержимое директории Charts (папки ui, post, comment, reddit) в Gitlab_ci/reddit-deploy
- Создаем файл Gitlab_ci/ui/.gitlab-ci.yml
mage: alpine:latest
stages:
- build
- test
- release
- cleanup
build:
stage: build
image: docker:git
services:
- docker:dind
script:
- setup_docker
- build
variables:
DOCKER_DRIVER: overlay2
only:
- branches
test:
stage: test
script:
- exit 0
only:
- branches
release:
stage: release
image: docker
services:
- docker:dind
script:
- setup_docker
- release
only:
- master
.auto_devops: &auto_devops |
[[ "$TRACE" ]] && set -x
export CI_REGISTRY="index.docker.io"
export CI_APPLICATION_REPOSITORY=$CI_REGISTRY/$CI_PROJECT_PATH
export CI_APPLICATION_TAG=$CI_COMMIT_REF_SLUG
export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
export TILLER_NAMESPACE="kube-system"
function setup_docker() {
if ! docker info &>/dev/null; then
if [ -z "$DOCKER_HOST" -a "$KUBERNETES_PORT" ]; then
export DOCKER_HOST='tcp://localhost:2375'
fi
fi
}
function release() {
echo "Updating docker images ..."
if [[ -n "$CI_REGISTRY_USER" ]]; then
echo "Logging to GitLab Container Registry with CI credentials..."
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
echo ""
fi
docker pull "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
docker tag "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
docker push "$CI_APPLICATION_REPOSITORY:$(cat VERSION)"
echo ""
}
function build() {
echo "Building Dockerfile-based application..."
echo `git show --format="%h" HEAD | head -1` > build_info.txt
echo `git rev-parse --abbrev-ref HEAD` >> build_info.txt
docker build -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
if [[ -n "$CI_REGISTRY_USER" ]]; then
echo "Logging to GitLab Container Registry with CI credentials..."
docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
echo ""
fi
echo "Pushing to GitLab Container Registry..."
docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
echo ""
}
before_script:
- *auto_devops- Комитем и запушим в gitlab
- В Pipeline работает
Добавляем .gitlab-ci.yml в Post и Comment. Смотрим что сборка прошла успешно
Дадим возможность разработчику запускать отдельное окружение в Kubernetes по коммиту в feature-бранч.
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ template "ui.fullname" }}
annotations:
kubernetes.io/ingress.class: {{ .Values.ingress.class }}
spec:
rules:
- host: {{ .Values.ingress.host | default .Release.Name }}
http:
paths:
- path: /
backend:
serviceName: {{ template "ui.fullname" }}
servicePort: {{ .Values.service.externalPort }}
Обновим конфиг ингресса для сервиса UI:
---
service:
internalPort: 9292
externalPort: 9292
image:
repository: verty/ui
tag: latest
ingress:
class: nginx
postHost:
postPort:
commentHost:
commentPort:Дадим возможность разработчику запускать отдельное окружение в Kubernetes по коммиту в feature-бранч.
- Создадим новый бранч в репозитории ui
$ git checkout -b feature/3- Обновим ui/.gitlab-ci.yml файл
- Закоммитим и запушем изменения
$ git commit -am "Add review feature"
$ git push origin feature/3Можем увидеть какие релизы запущены
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
gitlab 1 Thu Feb 7 11:05:59 2019 DEPLOYED gitlab-omnibus-0.1.37 default
review-verty-ui-f-nwgt56 1 Fri Feb 8 13:40:01 2019 DEPLOYED reddit-0.1.0 review Созданные для таких целей окружения временны, их требуется “убивать”, когда они больше не нужны.
Добавим в .gitlab-ci.yml
stop_review:
stage: cleanup
variables:
GIT_STRATEGY: none
script:
- install_dependencies
- delete
environment:
name: review/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME
action: stop
when: manual
allow_failure: true
only:
refs:
- branches
kubernetes: active
except:
- master
Добавим также
stages:
- build
- test
- review
- release
- cleanup
review:
stage: review
...
environment:
name: review/$CI_PROJECT_PATH/$CI_COMMIT_REF_NAME
url: http://$CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG
on_stop: stop_review Добавим функцию удаления окружения .auto_devops: &auto_devops | ... function delete() { track="${1-stable}" name="$CI_ENVIRONMENT_SLUG" helm delete "$name" --purge || true }
Пушем измениения в git
Заходим в Pipelines запускаем удаление окружения.
```bash
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
gitlab 1 Thu Feb 7 11:05:59 2019 DEPLOYED gitlab-omnibus-0.1.37 default
Создадим файл reddit-deploy/.gitlab-ci.yml
Пушим его в репозиторий reddit-deploy ветку master
Смотрим что в helm также все видно
$ helm ls
NAME REVISION UPDATED STATUS CHART NAMESPACE
gitlab 1 Mon Feb 11 10:28:08 2019 DEPLOYED gitlab-omnibus-0.1.37 default
production 8 Tue Feb 12 10:30:36 2019 DEPLOYED reddit-0.1.0 production
staging 10 Tue Feb 12 10:37:35 2019 DEPLOYED reddit-0.1.0 staging Из Helm-чарта установим ingress-контроллер nginx
Смтрим IP-адрес, выданный nginx’у
kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-nginx-ingress-controller LoadBalancer 10.51.253.45 35.189.75.167 80:30280/TCP,443:32385/TCP 1hДобавим в /etc/hosts
35.198.75.167 reddit reddit-prometheus reddit-grafana reddit-non-prod production reddit-kibana staging prodЗагрузим prometheus локально в Charts каталог
$ cd kubernetes/charts && helm fetch —-untar stable/prometheusСоздаем внутри директории чарта файл custom_values.yml
Запустим Prometheus в k8s из charsts/prometheus
$ helm upgrade prom . -f custom_values.yml --install
Для сбора информации будем использовать сервис
kube-state-metrics. Он входит в чарт Prometheus. Включим его.
prometheus/custom_values.ym
```yml
kubeStateMetrics:
## If false, kube-state-metrics will not be installed
##
enabled: trueОбновим релиз
helm upgrade prom . -f custom_values.yml --installВключаем поды node-exporter в custom_values.yml
nodeExporter:
enabled: trueЗапустим приложение из helm чарта reddit
$ helm upgrade reddit-test ./reddit —install
$ helm upgrade production --namespace production ./reddit --install
$ helm upgrade staging --namespace staging ./reddit —installМодернизируем конфиг prometheus Используем действие keep, чтобы оставить только эндпоинты сервисов с метками “app=reddit”
custom_values.yml
- job_name: 'reddit-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app]
action: keep
regex: redditОбновиь релиз prometheus
$ helm upgrade prom . -f custom_values.yml --installДобавим метрики k8s
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)Обновим релиз prometheus
$ helm upgrade prom . -f custom_values.yml --installТеперь появились лейблы k8s, присвоенные POD’ам
Добавим еще label’ы для prometheus и обновим helm-релиз
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_nameОтделяем target-ы компонент друг от друга (по окружениям, по самим компонентам), а также выключать и включать опцию мониторинга для них с помощью все тех же label-ов
- job_name: 'reddit-production'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)
- source_labels: [__meta_kubernetes_service_label_app, __meta_kubernetes_namespace]
action: keep
regex: reddit;(production|staging)+
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_nameМетрики будут отображаться для всех инстансов приложений
Разбиваем конфигурацию job’а reddit-endpoints:
post-endpoints, commentendpoints, ui-endpoints
- job_name: 'endpoints-post'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app]
action: keep
regex: reddit
- source_labels: [__meta_kubernetes_service_label_component]
action: keep
regex: post
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_name
- job_name: 'endpoints-comment'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app]
action: keep
regex: reddit
- source_labels: [__meta_kubernetes_service_label_component]
action: keep
regex: comment
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_name
- job_name: 'endpoints-ui'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_label_app]
action: keep
regex: reddit
- source_labels: [__meta_kubernetes_service_label_component]
action: keep
regex: ui
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
target_label: kubernetes_name
Установим grafana с помощью helm
$ helm upgrade --install grafana stable/grafana --set "adminPassword=admin" \
--set "service.type=NodePort" \
--set "ingress.enabled=true" \
--set "ingress.hosts={reddit-grafana}"Смотрим адрес сервиса prometheus сервера
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
prom-prometheus-server LoadBalancer 10.11.247.75 35.224.121.85 80:30282/TCP 12h