export GOOGLE_PROJECT=docker-194414
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 \
vm1
eval $(docker-machine env vm1)
docker-machine ip vm1
35.192.64.106
cd docker/
docker-compose up -d
Оставим описание приложений в docker-compose.yml, а мониторинг выделим в отдельный файл docker-compose-monitoring.yml
Для запуска приложений будем как и ранее использовать
docker-compose up -d
Для мониторинга -
docker-compose -f docker-compose-monitoring.yml up -d
Мы будем использовать cAdvisor для наблюдения за состоянием наших Docker контейнеров. cAdvisor собирает информацию о ресурсах потребляемых контейнерами и характеристиках их работы. Примерами метрик являются: процент использования контейнером CPU и памяти, выделенные для его запуска, объем сетевого трафика и др
Поместим его в одну сеть с Прометеем, чтобы он мог собирать с него метрички
docker-compose-monitoring.yml
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'
Откроем порт 8080 в GCE
gcloud compute firewall-rules create cadvisor-default --allow tcp:8080
- job_name: 'cadvisor'
static_configs:
- targets:
- 'cadvisor:8080'
export USER_NAME=asomir
docker build -t $USER_NAME/prometheus .
cd ../ ../docker
docker-compose down
docker-compose up -d
docker-compose -f docker-compose-monitoring.yml up -d
Откроем страницу Web UI по адресу http://104.154.181.55:8080
по пути /metrics все собираемые метрики публикуются для сбора Prometheus
Subcontainers
comment (/docker/f03c8787bcd9bd98d74f24bc99c81ca959e6b2060e800efcc8454576dc4ffe0b)
post-py (/docker/99c47e0f760bfb1e115ec71ac01a835b1f02f213698f62231d78736ed49ddb66)
ui (/docker/80e8d9da6954faf0eb04a8b665f9a1ae53c6b7834f8cc4b4df307585cf58cd0a)
docker_post_db_1 (/docker/34e1634d3c0251e55c2bd608b58af6ea40151e944d31a80bd0173df45fef3ca6)
docker_node-exporter_1 (/docker/48f5424a7383963b288e9a830f1ca10997ca1bdde526cafc9b67a884935f4d5b)
docker_prometheus_1 (/docker/24c99069cfb8b0ec891bae5a31d947780fa1f1357f91bcf176a60a48d4e8bb66)
docker_cadvisor_1 (/docker/594e93a52e967d2a00a373d3cd776aad9ea504caf912cc4ca2adc808f06c0717)
Driver Status
Docker Version 18.02.0-ce
Docker API Version 1.36
Kernel Version 4.13.0-1011-gcp
OS Version Ubuntu 16.04.4 LTS
Host Name vm1
Docker Root Directory /var/lib/docker
Execution Driver
Number of Images 9
Number of Containers 7
Storage
Driver aufs
Root Dir /var/lib/docker/aufs
Backing Filesystem extfs
Dirs 84
Dirperm1 Supported true
Проверим, что метрики контейнеров собираются
Prometheus. Введем, слово container
и посмотрим,
что он предложит дополнить
Добавим новый сервис в docker-compose-monitoring.yml
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
volumes:
grafana_data:
docker-compose -f docker-compose-monitoring.yml up -d grafana
gcloud compute firewall-rules create grafana-default --allow tcp:3000
И введём admin secret
Сервер назовём Prometheus Server и добавим его по адресу http://104.154.181.55:9090
Импортируем Dashboard с сайта https://grafana.com/dashboards : Data Source "Prometheus" - Category "Docker"
Загрузили источник данных для визуализации Prometheus Server
Добавим информацию о post сервисе в конфигурацию Prometheus, чтобы он начал собирать метрики и с него.
- job_name: 'post'
static_configs:
- targets:
- 'post:5000'
$ export USER_NAME=username
$ docker build -t $USER_NAME/prometheus .
$ docker-compose -f docker-compose-monitoring.yml down
$ docker-compose -f docker-compose-monitoring.yml up -d
И добавим несколько постов в приложении и несколько коментов, чтобы собрать значения метрик приложения.
rate(ui_request_count{http_status=~"^[45].*"}[1m])
rate(ui_request_count{http_status=~"^[23].*"}[1m])
В Prometheus есть тип метрик histogram. Данный тип метрик в качестве своего значение отдает ряд распределения измеряемой величины в заданном интервале значений. Мы используем данный тип метрики для измерения времени обработки HTTP запроса нашим приложением.
Посмотрим информацию по времени обработки запроса приходящих на главную страницу приложения
ui_request_latency_seconds_bucket{path="/"}
-
числовое значение в наборе значений
-
все числа в наборе меньше перцентиля, попадают в границы заданного процента значений от всего числа значений в наборе
Часто для анализа данных мониторинга применяются значения 90, 95 или 99-й перцентиля. Мы вычислим 95-й перцентиль для выборки времени обработки запросов, чтобы посмотреть какое значение является максимальной границей для большинства (95%) запросов. Для этого воспользуемся встроенной функцией histogram_quantile():
histogram_quantile(0.95, sum(rate(ui_request_latency_seconds_bucket[5m])) by (le))
Сохраним изменения дашборда и эспортируем его в JSON файл, который загрузим на нашу локальную машину
monitoring/grafana/dashboards/UI_Service_Monitoring.json
В качестве примера метрик бизнес логики мы в наше приложение мы добавили счетчики количества постов и комментариев.
• post_count
• comment_count
Мы построим график скорости роста значения счетчика за последний час, используя функцию rate(). Это позволит нам получать информацию об активности пользователей приложения.
построили график функции rate(comment_count[1h]) и экспортировали график в monitoring/grafana/dashboards/Business_Logic_Monitoring.json
Мы определим несколько правил, в которых зададим условия состояний наблюдаемых систем, при которых мы должны получать оповещения, т.к. заданные условия могут привести к недоступности или неправильной работе нашего приложения
Alertmanager - дополнительный компонент для системы мониторинга Prometheus, который отвечает за первичную обработку алертов и дальнейшую
отправку оповещений по заданному назначению. Создаём новую директорию monitoring/alertmanager. В этой директории создаём Dockerfile со следующим содержимым:
FROM prom/alertmanager:v0.14.0
ADD config.yml /etc/alertmanager/
В директории monitoring/alertmanager создали файл config.yml, в котором определили отправку нотификаций в мой тестовый слак канал #alexander-akilin.
global:
slack_api_url: 'https://hooks.slack.com/services/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'
route:
receiver: 'slack-notifications'
receivers:
- name: 'slack-notifications'
slack_configs:
- channel: '#alexander-akilin'
monitoring/alertmanager
docker build -t $USER_NAME/alertmanager .
alertmanager:
image: ${USER_NAME}/alertmanager
command:
- '--config.file=/etc/alertmanager/config.yml'
ports:
- 9093:9093
monitoring/prometheus/Dockerfile
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/
ADD alerts.yml /etc/prometheus/
prometheus.yml
rule_files:
- "alerts.yml"
alerting:
alertmanagers:
- scheme: http
static_configs:
- targets:
- "alertmanager:9093"
docker build -t $USER_NAME/prometheus .
docker-compose -f docker-compose-monitoring.yml down
docker-compose -f docker-compose-monitoring.yml up -d
gcloud compute firewall-rules create alertmanager-default --allow tcp:9093
$ docker login Login Succeeded
docker push $USER_NAME/ui
docker push $USER_NAME/comment
docker push $USER_NAME/post
docker push $USER_NAME/prometheus
docker push $USER_NAME/alertmanager
https://hub.docker.com/r/asomir/
$ gcloud compute firewall-rules create prometheus-default --allow tcp:9090
$ gcloud compute firewall-rules create puma-default --allow tcp:9292
export GOOGLE_PROJECT=docker-194414
# create docker host
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 \
vm1
# configure local env
eval $(docker-machine env vm1)
$ docker run --rm -p 9090:9090 -d --name prometheus prom/prometheus:v2.1.0
проверяем ШуЗы Прометея
docker-machine ip vm1
и идём по полученному ШуЗу http://35.225.212.35:9090/graph
Targets (цели) - представляют собой системы или процессы, за которыми следит Prometheus. Помним, что Prometheus является pull системой, поэтому он постоянно делает HTTP запросы на имеющиеся у него адреса (endpoints). Посмотрим текущий список целей
В Targets сейчас мы видим только сам Prometheus. У каждой цели есть свой список адресов (endpoints), по которым следует обращаться для получения информации.
В веб интерфейсе мы можем видеть состояние каждого endpoint-а (up); лейбл (instance="someURL"), который Prometheus автоматически добавляет к каждой метрике, получаемой с данного endpoint-а; а также время, прошедшее с момента последней операции сбора информации с endpoint-а.
Также здесь отображаются ошибки при их наличии и можно отфильтровать только неживые таргеты.
Мы можем открыть страницу в веб браузере по данному HTTP пути (host:port/metrics), чтобы посмотреть, как выглядит та информация, которую собирает Prometheus.
docker stop prometheus
6. Создаём докер файл monitoring/prometheus/Dockerfile, который копирует файл конфигурации с нашей машины внутрь контейнера:
FROM prom/prometheus:v2.1.0
ADD prometheus.yml /etc/prometheus/
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=asomir
$ docker build -t $USER_NAME/prometheus .
9. Сборку образов производим при помощи скриптов docker_build.sh, которые есть в директории каждого сервиса. С его помощью мы добавим информацию из Git в наш healthcheck.
Запустиим сразу все из корня репы и пойдём пить кофе
for i in ui post-py comment; do cd src/$i; bash
docker_build.sh; cd -; done
version: '3.3'
services:
post_db:
image: mongo:${VERSION_MONGO}
volumes:
- post_db:/data/db
networks:
back_net:
aliases:
- post_db
- comment_db
ui:
container_name: ui
image: ${USERNAME}/ui:latest
ports:
- ${APP_PORT}:9292/tcp
networks:
- front_net
post:
container_name: post-py
image: ${USERNAME}/post:latest
networks:
- front_net
- back_net
comment:
container_name: comment
image: ${USERNAME}/comment:latest
networks:
- back_net
- front_net
prometheus:
image: ${USER_NAME}/prometheus
ports:
- '9090:9090'
volumes:
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--storage.tsdb.retention=1d'
networks:
- back_net
- front_net
volumes:
post_db:
prometheus_data:
networks:
front_net:
back_net:
Запускаем docker-compose up -d и проверяем работоспособность
http://35.225.212.35:9090/graph http://35.225.212.35:9292/
Проверяем состояние наших эндпойнтов: comment, ui, prometheus
http://35.225.212.35:9090/targets
Healthcheck-и представляют собой проверки того, что наш сервис здоров и работает в ожидаемом режиме. В нашем случае healthcheck выполняется внутри кода микросервиса и выполняет проверку того, что все сервисы, от которых зависит его работа, ему доступны. Если требуемые для его работы сервисы здоровы, то healthcheck проверка возвращает status = 1, что соответсвует тому, что сам сервис здоров. Если один из нужных ему сервисов нездоров или недоступен, то проверка вернет status = 0.
Выполнили поиск в веб-интерфейсе прометея ui_health, однако он ничего не нашёл. Зашёл на сайт реддит и сделал пост, - после этого Прометей нашёл метрику
ui_health{branch="monitoring-1",commit_hash="d3d08a6",instance="ui:9292",job="ui",version="0.0.1"}
Где показано название ветки в гите, коммите и лейблах
Перешли в графическое отображение, увидели единицу, и она прекрасна. Остановили сервис post.
docker-compose stop post
график упал в ноль. И это ужасно. Поплачем друзья, ведь сервис нездоров.
Зашёл посмотреть на ui_health_comment_availability и вижу прекрасную единицу.
Зашёл глянуть на ui_health_post_availability и обожечки! Что я вижу! Полный ноль! Сервис мёртв! Что же делать?!
Поднимем же сервис пост, вставай, дружочек:
docker-compose start post
ui_health_post_availability{branch="monitoring-1",commit_hash="d3d08a6",instance="ui:9292",job="ui",version="0.0.1"}
Жив и здоров, мы пришили ему ножки!
• Программа, которая делает метрики доступными для сбора Prometheus
• Дает возможность конвертировать метрики в нужный для Prometheus формат
• Используется когда нельзя поменять код приложения
• Примеры: PostgreSQL, RabbitMQ, Nginx, Node exporter, cAdvisor
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)($$|/)"'
- job_name: 'node'
static_configs:
- targets:
- 'node-exporter:9100'
monitoring/prometheus
$ docker build -t $USER_NAME/prometheus .
$ docker-compose down
$ docker-compose up -d
В списке эндпойнтов появился ещё один сервис node
Зайдем на хост:
docker-machine ssh vm1
Добавим нагрузки:
yes > /dev/null
Посмотрим на весёлые графики
$ docker login Login Succeeded
docker push $USER_NAME/ui docker push $USER_NAME/comment docker push $USER_NAME/post docker push $USER_NAME/prometheus
Удалили виртуалку: $ docker-machine rm vm1
https://hub.docker.com/r/asomir/
git checkout -b docker-7 git remote add gitlab2 http://35.195.25.42/homework/example2.git git push gitlab2 docker-7
image: ruby:2.4.2
stages: # в каких окружениях что происходить будет
- build
- test
- review
- stage
- production
variables:
DATABASE_URL: 'mongodb://mongo/user_posts'
before_script: # что бы запустить в самом начале
- cd reddit
- bundle install
build_job: # билдим
stage: build
script:
- echo 'Building'
test_unit_job: # юнит тестирование
stage: test
services:
- mongo:latest
script:
- ruby simpletest.rb
test_integration_job: # интегрированные тесты
stage: test
script:
- echo 'Testing 2'
deploy_dev_job: # деплой в дев
stage: review
script:
- echo 'Deploy'
environment: # разворачиваем окружение
name: dev
url: http://dev.example.com
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 должен быть запущен человеком из UI
only:
- /^\d+\.\d+.\d+/ # директива, которая не позволит нам выкатить на staging и production код,
# не помеченный с помощью тэга в git
# описывает список условий, которые должны быть
# истинны, чтобы job мог запуститься. Регулярное выражение слева
# означает, что должен стоять semver тэг в git, например, 2.4.10
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: http://example.com
git commit -a -m ‘#4 add logout button to profile page’
git tag 2.4.10
git push gitlab2 docker-7 --tags
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
Теперь, на каждую ветку в git отличную от master Gitlab CI будет определять новое окружение.
#####1. Нам потребуется создать в Google Cloud новую виртуальную машину со следующими параметрами
- 1 CPU
- 3.75GB RAM
- 50-100 GB HDD
- Ubuntu 16.04
# Подключаемся к гуглу к нашему проекту в регионе eur
provider "google" {
version = "1.4.0"
project = "${var.project}"
region = "${var.region}"
}
# Подключение SSH ключей для пользователя asomirl
resource "google_compute_project_metadata" "ssh-asomirl" {
metadata {
ssh-keys = "${var.gitlab_admin}:${file(var.public_key_path)}"
}
}
# Создаём в west-1b машину n1-standard-1 Standard machine type with 1 virtual CPU and 3.75 GB of memory.
resource "google_compute_instance" "gitlab" {
name = "gitlab"
machine_type = "n1-standard-1"
zone = "${var.zone}"
# Прибавили теги, чтобы ходить по SSH
tags = ["docker-host", "default-allow-ssh"]
# определение загрузочного диска 50Gb
boot_disk {
initialize_params {
# По умолчанию "ubuntu-1604-lts"
image = "${var.disk_image}"
size = 50
}
}
# определение сетевого интерфейса
network_interface {
# сеть, к которой присоединить данный интерфейс
network = "default"
# использовать ephemeral IP для доступа из Интернет
access_config {}
}
# включаем подключение по ssh с путём к приватному ключу
connection {
type = "ssh"
user = "${var.gitlab_admin}"
agent = false
private_key = "${file(var.private_key_path)}"
}
}
# Создание правила для firewall открываем для начала все порты и протокол ICMP
resource "google_compute_firewall" "docker-host-allow" {
name = "docker-host-allow"
# Название сети, в которой действует правило
network = "default"
# Какой доступ разрешить
allow {
protocol = "tcp"
ports = ["0-65535"]
}
allow {
protocol = "icmp"
}
# Каким адресам разрешаем доступ
source_ranges = ["0.0.0.0/0"]
# Правило применимо для инстансов с тегом …
target_tags = ["docker-host"]
}
# Создаём внешний адрес для машины
resource "google_compute_address" "gitlab_ip" {
name = "gitlab-ip"
}
# Открываем порт 22
resource "google_compute_firewall" "firewall_ssh" {
name = "gitlab-allow-ssh"
network = "default"
allow {
protocol = "tcp"
ports = ["22"]
}
source_ranges = "${var.source_ranges}"
}
В папке terraform-gitlab выполняем
$ terraform import google_compute_firewall.firewall_ssh defaultallow-ssh
В файле inventory указываем имя хоста gitlab и его ip,
gitlab ansible_host=35.195.130.13
Также ip внесём в /roles/gitlab/defaults/main.yml, там же укажем имя нашего пользователя и папку для установки GitLab
deploy_user: asomirl
gitlab_ip: 35.195.130.13
gitlab_folder: /srv/gitlab
3. Нам необходимо установить докер и докер композ. Из-за присущей лени мы тупо скачиваем ansible роль, которая ставит обадва сразу:
В папке с ролями выполняем скачивание роли ansible-galaxy nickjj.docker
ansible-galaxy install nickjj.docker -p ./ --force
ansible-galaxy init gitlab
# tasks file for gitlab/
- name: Install python for Ansible
raw: test -e /usr/bin/python || (apt -y update && apt install -y python-minimal && apt install -y python-apt )
become: true
changed_when: false
6. После тестирования и попытки запускать наш докер контейнер выпадает ошибка отсутствия docker-py, поэтому было принято ставить докер и пипы вручную.
В файле install_pip.yml устанавливаем необходимые нам пипули с хтопом, гитом на всякий, питошами. Также указываем нашего пользователя asomirl как деплой юзверя
# Examples from Ansible Playbooks
- name: Install base packages
apt: name={{ item }} state=installed
with_items:
- htop #чтобы было удобно следить за установкой гитлаба
- git # на всякий пожарный, чтобы выкачивать всякие нужные штуки из гита
- python-pip # без этого ничего не работает
- python3-pip # так и не понял, нужна ли 3 версия, паходу разберёмся
tags:
- packages
- name: Upgrade pip # обновляем пипы на самые последние версии
pip: name=pip state=latest
tags:
- packages
- name: Create user
user:
name: "{{ deploy_user }}" # создадим нашего пользователя на серваке на всякие пожарные
comment: "Used to deploy Gitlab"
state: present
create_folders.yml
- name: Creates directoryes
file:
path: "{{ gitlab_folder }}/config"
path: "{{ gitlab_folder }}/data"
path: "{{ gitlab_folder }}/logs"
state: directory
owner: "{{ deploy_user }}"
group: "{{ deploy_user }}"
install_docker.yml
- name: ensure repository key is installed
apt_key:
id: "58118E89F3A912897C070ADBF76221572C52609D"
keyserver: "hkp://p80.pool.sks-keyservers.net:80"
state: present
- name: ensure docker registry is available
# For Ubuntu 16.04 LTS, use this repo instead:
apt_repository: repo='deb https://apt.dockerproject.org/repo ubuntu-xenial main' state=present
- name: ensure docker and dependencies are installed
apt: name=docker-engine update_cache=yes
- service: name=docker state=restarted
install_docker_compose.yml
- name: Install docker python module
pip:
name: "docker-compose"
web:
image: 'gitlab/gitlab-ce:latest'
restart: always
hostname: 'gitlab.example.com'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'http://{{ gitlab_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'
11. ...и копируем его содержимое на сервер гитлаб в приготовленную папку /srv/gitlab/ в файл docker-compose.yml
omnibus.yml
- name: Change mongo config file
template:
src: templates/docker-compose.yml.j2
dest: /srv/gitlab/docker-compose.yml
mode: 0644
compose.yml
- name: docker-compose via ansible docker_service
tags: "docker"
docker_service:
files: docker-compose.yml
project_src: /srv/gitlab/
project_name: "gitlab-service"
- include: install_py.yml
- include: install_pip.yml
- include: create_folders.yml
- include: install_docker.yml
- include: install_docker_compose.yml
- include: omnibus.yml
- include: compose.yml
terraform apply
15. Запускаем конфигурирование и установку необоходимых пакетов и компонентов для GitLab с помощью Ansible:
cd ~/asomir_microservices/ansible-gitlab/roles
ansible-playbook gitlab.yml --vv
16. И бешено радуемся установке. Пока ставится, мы заходим по ssh на созданную Машину GitLab запускаем htop и радуемся циферкам.
ssh asomirl@35.195.130.13
htop
17. Переходим в браузере по айпи 35.195.130.13 и через некоторое время видим морду лисы. Вводим дважды придуманный пароль, затем вводим имя root и этот пароль, попадаем на приветственную Страницу гитлаба
git checkout -b docker-6
git remote add gitlab http://35.195.130.13/homework/example.git
git push gitlab docker-6
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 docker-6
Specify the following URL during the Runner setup: http://35.195.130.13/
Use the following registration token during setup: qhRTVTPL5kmP4N9Tx5_U
sudo 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
docker exec -it gitlab-runner gitlab-runner register
и отвечаем на вопросы:
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):
http://35.195.130.13/
Please enter the gitlab-ci token for this runner:
qhRTVTPL5kmP4N9Tx5_U
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.
git clone https://github.com/express42/reddit.git && rm -rf ./reddit/.git
git add reddit/
git commit -m “Add reddit app”
git push gitlab docker-6
image: ruby:2.4.2
stages:
- build
- test
- deploy
variables:
DATABASE_URL: 'mongodb://mongo/user_posts'
before_script:
- cd reddit
- bundle install
build_job:
stage: build
script:
- echo 'Building'
test_unit_job:
stage: test
services:
- mongo:latest
script:
- ruby simpletest.rb
test_integration_job:
stage: test
script:
- echo 'Testing 2'
deploy_job:
stage: deploy
script:
- echo 'Deploy'
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
reddit\Gemfile
source 'https://rubygems.org'
gem 'sinatra'
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
Теперь на каждое изменение в коде приложения будет запущен тест.
28. Пушим всё в ГитЛаб и набюдаем, как ревьюверы whitew1nd (Yury Ignatov) и postgred (Andrey Aleksandrov) активно фейспалмят!
1. Идём по ссылке https://devops-team-otus.slack.com/apps Ищем Incoming Webhook - выбираем Add Configuration
2. Выбираем канал, куда мы хотим получать сообщения или создаём новый канал, нажимаем Add WebHooks Integration
3. Меняем иконку на более удобоваримую Upload An Image. В поле Customize Name вводим имя пользователя, от которого будут приходить Эвенты. Копируем URL Webhook. Save Settings
https://hooks.slack.com/services/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
4. Заходим в проект Example - Settings - Integration - Slack Notifications. Кликаем галочку Active, в тригерах #Push указываем название комнаты, куда будет приходить уведомление #alexander-akilin
Вставляем наш webhook, указываем username - Test And save Changes. Радуемся пришедшим нотификашкам.
- Запустим контейнер с использованием none-драйвера, с временем жизни 100 секунд, по истечении автоматически удаляется. В качестве образа используем joffotron/docker-net-tools, в него входят утилиты bind-tools, net-tools и curl.
docker run --network none --rm -d --name net_test joffotron/docker-net-tools -c "sleep 100"
docker exec -ti net_test ifconfig
В результате, видим:
• что внутри контейнера из сетевых интерфейсов существует
только loopback.
• сетевой стек самого контейнера работает (ping localhost), но без
возможности контактировать с внешним миром.
• Значит, можно даже запускать сетевые сервисы внутри такого
контейнера, но лишь для локальных экспериментов
(тестирование, контейнеры для выполнения разовых задач и
т.д.)
- Запустили контейнер в сетевом пространстве docker-хоста
docker run --network host --rm -d --name net_test joffotron/docker-net-tools -c "sleep 100"
- Вывод команды docker exec -ti net_test ifconfig
br-9847ceaf8390 Link encap:Ethernet HWaddr 02:42:1A:94:FA:64
inet addr:172.18.0.1 Bcast:172.18.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:1aff:fe94:fa64%32531/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1731 errors:0 dropped:0 overruns:0 frame:0
TX packets:1795 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:145258 (141.8 KiB) TX bytes:283122 (276.4 KiB)
docker0 Link encap:Ethernet HWaddr 02:42:27:F2:53:24
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:27ff:fef2:5324%32531/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:18460 errors:0 dropped:0 overruns:0 frame:0
TX packets:28983 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1440883 (1.3 MiB) TX bytes:346814939 (330.7 MiB)
ens4 Link encap:Ethernet HWaddr 42:01:0A:84:00:02
inet addr:10.132.0.2 Bcast:10.132.0.2 Mask:255.255.255.255
inet6 addr: fe80::4001:aff:fe84:2%32531/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1460 Metric:1
RX packets:146266 errors:0 dropped:0 overruns:0 frame:0
TX packets:112334 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:908455582 (866.3 MiB) TX bytes:254880160 (243.0 MiB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1%32531/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:151209 errors:0 dropped:0 overruns:0 frame:0
TX packets:151209 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:20506793 (19.5 MiB) TX bytes:20506793 (19.5 MiB)
veth09faf80 Link encap:Ethernet HWaddr 92:7D:D2:89:4A:BA
inet6 addr: fe80::907d:d2ff:fe89:4aba%32531/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:20324 errors:0 dropped:0 overruns:0 frame:0
TX packets:10393 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1922371 (1.8 MiB) TX bytes:3072704 (2.9 MiB)
veth70277fa Link encap:Ethernet HWaddr 5E:74:D5:EE:84:5B
inet6 addr: fe80::5c74:d5ff:feee:845b%32531/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:35274 errors:0 dropped:0 overruns:0 frame:0
TX packets:70262 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:10678546 (10.1 MiB) TX bytes:6665213 (6.3 MiB)
vethda70fe7 Link encap:Ethernet HWaddr 9E:6E:BB:55:B3:17
inet6 addr: fe80::9c6e:bbff:fe55:b317%32531/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:49921 errors:0 dropped:0 overruns:0 frame:0
TX packets:25008 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:4742482 (4.5 MiB) TX bytes:7615475 (7.2 MiB)
vethe3618d9 Link encap:Ethernet HWaddr A2:15:EC:B9:20:A0
inet6 addr: fe80::a015:ecff:feb9:20a0%32531/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:70 errors:0 dropped:0 overruns:0 frame:0
TX packets:110 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:15456 (15.0 KiB) TX bytes:14755 (14.4 KiB)
Вывод команды docker exec -ti net_test ifconfig
br-9847ceaf8390 Link encap:Ethernet HWaddr 02:42:1a:94:fa:64
inet addr:172.18.0.1 Bcast:172.18.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:1aff:fe94:fa64/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:1731 errors:0 dropped:0 overruns:0 frame:0
TX packets:1795 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:145258 (145.2 KB) TX bytes:283122 (283.1 KB)
docker0 Link encap:Ethernet HWaddr 02:42:27:f2:53:24
inet addr:172.17.0.1 Bcast:172.17.255.255 Mask:255.255.0.0
inet6 addr: fe80::42:27ff:fef2:5324/64 Scope:Link
UP BROADCAST MULTICAST MTU:1500 Metric:1
RX packets:18460 errors:0 dropped:0 overruns:0 frame:0
TX packets:28983 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1440883 (1.4 MB) TX bytes:346814939 (346.8 MB)
ens4 Link encap:Ethernet HWaddr 42:01:0a:84:00:02
inet addr:10.132.0.2 Bcast:10.132.0.2 Mask:255.255.255.255
inet6 addr: fe80::4001:aff:fe84:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1460 Metric:1
RX packets:146332 errors:0 dropped:0 overruns:0 frame:0
TX packets:112405 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:908469183 (908.4 MB) TX bytes:254894834 (254.8 MB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:151209 errors:0 dropped:0 overruns:0 frame:0
TX packets:151209 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:20506793 (20.5 MB) TX bytes:20506793 (20.5 MB)
veth09faf80 Link encap:Ethernet HWaddr 92:7d:d2:89:4a:ba
inet6 addr: fe80::907d:d2ff:fe89:4aba/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:20364 errors:0 dropped:0 overruns:0 frame:0
TX packets:10413 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1926171 (1.9 MB) TX bytes:3078804 (3.0 MB)
veth70277fa Link encap:Ethernet HWaddr 5e:74:d5:ee:84:5b
inet6 addr: fe80::5c74:d5ff:feee:845b/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:35344 errors:0 dropped:0 overruns:0 frame:0
TX packets:70402 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:10699896 (10.6 MB) TX bytes:6678513 (6.6 MB)
vethda70fe7 Link encap:Ethernet HWaddr 9e:6e:bb:55:b3:17
inet6 addr: fe80::9c6e:bbff:fe55:b317/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:50021 errors:0 dropped:0 overruns:0 frame:0
TX packets:25058 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:4751982 (4.7 MB) TX bytes:7630725 (7.6 MB)
vethe3618d9 Link encap:Ethernet HWaddr a2:15:ec:b9:20:a0
inet6 addr: fe80::a015:ecff:feb9:20a0/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:70 errors:0 dropped:0 overruns:0 frame:0
TX packets:110 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:15456 (15.4 KB) TX bytes:14755 (14.7 KB)
Видим, что значения совпадают, кроме того, что в первом случае сетевуха называется s4, во втором ens4
-
Запустили docker run --network host -d nginx несколько раз, а контейнер всё равно один запущен. Это фишка, а не баг.
-
На docker-host машине выполнили команду:
> sudo ln -s /var/run/docker/netns /var/run/netns
Теперь можно просматривать существующие неймспейсы с помощью
> sudo ip netns
• Теперь наше приложение состоит из трех компонент:
• post-py - сервис отвечающий за написание постов
• comment - сервис отвечающий за написание комментариев
• ui - веб-интерфейс, работающий с другими сервисами
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"]
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
COPY . $APP_HOME
ENV COMMENT_DATABASE_HOST comment_db
ENV COMMENT_DATABASE comments
CMD ["puma"]
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"]
> docker pull mongo:latest
> docker build -t <your-dockerhub-login>/post:1.0 ./post-py
> docker build -t <your-dockerhub-login>/comment:1.0 ./comment
> docker build -t <your-dockerhub-login>/ui:1.0 ./ui
Сборка ЮИ началась не с первого шага, потому как предыдущие шаги уже были проделаны при старте Каммента Памятка:
Что мы сделали? • Создали bridge-сеть для контейнеров, так как сетевые алиасы не работают в сети по умолчанию ( о сетях в Docker мы еще поговорим на следующем занятии) • Запустили наши контейнеры в этой сети • Добавили сетевые алиасы контейнерам • Сетевые алиасы могут быть использованы для сетевых соединений, как доменные имена
FROM ubuntu:16.04
RUN apt-get update \
&& apt-get install -y ruby-full ruby-dev build-essential \
&& gem install bundler --no-ri --no-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"]
И пометили его как версия 2
> docker build -t <your-login>/ui:2.0 ./ui
> 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 <your-dockerhub—login>/post:1.0
> docker run -d --network=reddit \
--network-alias=comment <your-dockerhub-login>/comment:1.0
> docker run -d --network=reddit \
-p 9292:9292 <your-dockerhub-login>/ui:2.0
> docker volume create reddit_db
и подключим его к базе данных, Выключив предварительно старые копии контейнеров
> 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 <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:2.0
####1. Создали новый проект в GCP docker-194414 и создали докер хост:
> docker-machine create --driver google \
--google-project docker-194414 \
--google-zone europe-west1-b \
--google-machine-type g1-small \
--google-machine-image $(gcloud compute images list --filter ubuntu-1604-lts --uri) \
docker-host
docker-machine ls
eval $(docker-machine env docker-host)
- Dockerfile - текстовое описание нашего образа
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 https://github.com/Artemmkin/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"]
• mongod.conf - преподготовленный конфиг для mongodb
# 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
• db_config - содержит переменную со ссылкой на mongodb
DATABASE_URL=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
docker build -t reddit:latest .
docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 6d5f15dea66c 24 minutes ago 682MB
asomir/otus-reddit 1.0 3ddd4d70e49f 24 minutes ago 682MB
reddit latest 3ddd4d70e49f 24 minutes ago 682MB
<none> <none> 33961ea15acd 24 minutes ago 682MB
<none> <none> fdd30df927aa 24 minutes ago 650MB
<none> <none> 1b07d9acf2a1 24 minutes ago 650MB
<none> <none> fd5c5ee9c266 24 minutes ago 650MB
<none> <none> 99eb7073e4ab 24 minutes ago 650MB
<none> <none> 29feb728d310 24 minutes ago 650MB
<none> <none> b21f1e029915 24 minutes ago 647MB
<none> <none> 28f07a2ab0c6 26 minutes ago 151MB
ubuntu 16.04 0458a4468cbc 2 weeks ago 112MB
docker run --name reddit -d --network=host reddit:latest
gcloud compute firewall-rules create reddit-app \
--allow tcp:9292 --priority=65534 \
--target-tags=docker-machine \
--description="Allow TCP connections" \
--direction=INGRESS
9. Пошли по ссылке http://35.205.170.223:9292/ - порадовались!
docker login
docker tag reddit:latest asomir/otus-reddit:1.0
docker push asomir/otus-reddit:1.0
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
asomir/ubuntu-tmp-file latest 46a62528372b 9 hours ago 112MB
asomir/nginx-tmp-file latest 5ecfc7c8efbd 9 hours ago 108MB
ubuntu 16.04 0458a4468cbc 10 days ago 112MB
ubuntu latest 0458a4468cbc 10 days ago 112MB
nginx latest 3f8a4339aadd 5 weeks ago 108MB
hello-world latest f2a91732366c 2 months ago 1.85kB
- Дз со свездой Выполним команду docker inspect 3f8a4339aadd > docker_image и docker inspect 979cef3539af > docker_container и сравним их вывод. Когда мы инспектим контейнер, то видим там описани его состояния, настроек сети :
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 3850,
"ExitCode": 0,
"Error": "",
"StartedAt": "2018-02-05T05:34:44.361548395Z",
"FinishedAt": "0001-01-01T00:00:00Z"
"Gateway": "172.17.0.1",
"IPAddress": "172.17.0.3",
"IPPrefixLen": 16,
"IPv6Gateway": "",
"GlobalIPv6Address": "",
"GlobalIPv6PrefixLen": 0,
"MacAddress": "02:42:ac:11:00:03",
"DriverOpts": null
Когда мы инспектим Image, мы видим архитектуру
"Architecture": "amd64",
"Os": "linux",
"Size": 108492271,
"VirtualSize": 108492271,
"GraphDriver": {
Config нашёлся в обоих местах image:
"Config": {
"Hostname": "",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": false,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.13.8-1~stretch",
"NJS_VERSION=1.13.8.0.1.15-1~stretch"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
container
"Config": {
"Hostname": "979cef3539af",
"Domainname": "",
"User": "",
"AttachStdin": false,
"AttachStdout": false,
"AttachStderr": false,
"ExposedPorts": {
"80/tcp": {}
},
"Tty": true,
"OpenStdin": false,
"StdinOnce": false,
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"NGINX_VERSION=1.13.8-1~stretch",
"NJS_VERSION=1.13.8.0.1.15-1~stretch"
],
"Cmd": [
"nginx",
"-g",
"daemon off;"
],
Итак, делаем вывод, что контейнер имеет состояние, конфигурацию, маунты, конфиг. В имидже также присутствует конфиг, метаинформация и информация об архитектуре.