├── Lab1.md ├── Lab2.md ├── Lab3.md ├── Lab4.md ├── Plan.md ├── README.md ├── Summary.md ├── Wiki.md ├── ansible ├── README.md ├── ansible.cfg ├── deploy-stage.yml ├── group_vars │ └── all │ │ ├── main.yml │ │ └── secret.yml ├── host_vars │ └── enterprise ├── inventory.ini ├── inventory.yml └── roles │ ├── atop │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ └── tasks │ │ └── main.yml │ ├── nginx │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ └── main.yml │ └── templates │ │ ├── acl │ │ ├── blacklist.conf.j2 │ │ ├── external.conf.j2 │ │ └── main.conf.j2 │ │ ├── default.conf.j2 │ │ ├── mysite.html.j2 │ │ ├── nginx-site.conf.j2 │ │ └── nginx.conf.j2 │ ├── node_exporter │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── check.yml │ │ ├── configure.yml │ │ ├── install.yml │ │ └── main.yml │ └── templates │ │ └── init.service.j2 │ └── ntpd │ ├── defaults │ └── main.yml │ ├── handlers │ └── main.yml │ ├── tasks │ └── main.yml │ └── templates │ └── chronyd.conf.j2 └── img ├── 54-http.png ├── 55-arping.png ├── 56-nc.png ├── 57-tcpdump.png ├── 58-iptables.jpg ├── 59-input.png ├── 60-spoiler.png ├── 61-virt-1.png ├── 61-virt-2.png ├── 61-virt-3.png ├── 62-docker.png ├── 63-docker-run.png ├── 64-docker-ps.png ├── 65-docker-image.png ├── 66-docker-exec.png ├── 67-docker-ps.png ├── 68-docker-proxy.png ├── 69-docker-build.png ├── 70-docker-mysuperapp.png ├── 71-docker-tag.png ├── 72-docker-run.png ├── 73-mysql.png ├── 75-gitlab.png ├── 76-gitlab.png ├── 77-gitlab.png ├── Lab4-build-job-not-fail-when-no-artifacts.png ├── Lab4-inside-docker-whereis-pip-libs.png ├── Lab4-pipeline-artifact-pass1.png ├── Lab4-pipeline-artifact-pass2.png ├── Lab4-pipeline-cov.png ├── Lab4-pipeline-junit.png ├── Lab4-win-security.png ├── Lab4_Merge-Sort-Algorithm.png ├── s0-rwx.png ├── s1.png ├── s10-revert.png ├── s11-confirm.png ├── s12-reset.png ├── s13-apt.png ├── s14-nat.png ├── s15-ssh-rule.png ├── s16-win-1.png ├── s16-win-2.png ├── s16-win-3.png ├── s17-unix.png ├── s18-win-keygen-1.png ├── s18-win-keygen-2.png ├── s19-ssh-keygen.png ├── s2.png ├── s20-ulimit.png ├── s21-ssh-add.png ├── s22-which.png ├── s23-sshd-limits.png ├── s24-limit-check.png ├── s25-user.png ├── s26-bashrc.png ├── s27-colors.png ├── s28-chattr.png ├── s29-passwd.png ├── s3.png ├── s30-pipeline.jpg ├── s31-pipeline.png ├── s32-tty-before.png ├── s33-tty-after.png ├── s34-pts.png ├── s35-tty-linux.png ├── s36-tmux.png ├── s37-sudoers.png ├── s38-lvm.png ├── s39-pstates.png ├── s4.png ├── s40-ansible.png ├── s41-ntpd.png ├── s42-systemd.png ├── s43-top.png ├── s44-atop.png ├── s45-disk.png ├── s46-fdisk.png ├── s47-mount.png ├── s48-bind-mount.png ├── s49-fstab.png ├── s5-installer.png ├── s50-lv.png ├── s51-lvextend.png ├── s52-ln.png ├── s53-ip.png ├── s6-lvm-ex.png ├── s7-grub.png ├── s8-onlogin.png ├── s9-snapshots-2.png └── s9-snapshots.png /Lab1.md: -------------------------------------------------------------------------------- 1 | # Лабораторная работа №1. Введение в Linux. 2 | 3 | **Что потребуется перед началом**: 4 | 5 | - Компьютер, способный запустить систему виртуализации с виртуальной машиной GNU/Linux. 6 | - Минимум 5 GiB свободного места на жестком диске (2.6 под систему, остальное под пакеты). 7 | - [Желательно] Загруженный образ дистрибутива Debian. 8 | 9 | **План и задачи лабораторной**: 10 | 11 | 1. Настройка виртуальной машины, основные команды, bash, пакеты, потоки 12 | - Установить ОС, проделать некоторый набор ручных операций, написать и запустить скрипт автоматизации 13 | 2. Пользователи и права доступа в Linux, лимиты, настройка sshd, генерация ключей, ssh-agent 14 | - Создать пользователя, выдать ему права, ограничить лимиты, настроить ssh-подключение по ключу 15 | 16 | **Отчет** - в любом читаемом формате (pdf, md, doc, docx, pages). 17 | 18 | Обязательное содержимое отчета: 19 | 20 | 0. Фамилия и инициалы студента, номер группы, номер варианта 21 | 1. План и задачи лабораторной 22 | 2. Часть 1 - кратко описать, что было сделано 23 | 3. Часть 2 - кратко описать, что было сделано 24 | 4. Приложить очищенный вывод `history` выполненных команд 25 | 5. [Универсальный вывод по лабораторной работе][default-summary] 26 | 27 | 28 | 29 | ## Вступление 30 | 31 | Для полного понимания рекомендуется ознакомиться с разделами "Терминология", "Файлы, каталоги, права" и "Работа с системой" [справочных материалов][wiki-page]. 32 | 33 | 34 | 35 | 36 | ## Часть 1. 37 | 38 | Задача: установить любую систему виртуализации, настроить виртуальную машину с любым дистрибутивом GNU/Linux и выполнить набор простейших действий (подробно описан в п. 1.2). 39 | 40 | > Примечание: Если вы не работали с виртуализацией раньше - ставьте VirtualBox. Вопросы различий виртуализации и решения проблем с системами виртуализации выходят за рамки курса. 41 | 42 | > Примечание: Если вы не работали раньше с GNU/Linux - ставьте дистрибутив Debian (он используется в методических указаниях). Вопросы различий дистрибутивов и проблем с установкой ПО на разных дистрибутивах выходят за рамки курса. 43 | 44 | > Примечание: Если вы опытный "линуксоид" - стоит сразу начинать работать по ssh (сразу проделав все из части 2) 45 | 46 | 47 | ### 1.1. Подготовка рабочего окружения 48 | 49 | 0. Устанавливаем [VirtualBox][virtualbox-install] или любую другую систему виртуализации. 50 | 51 | > Примечание: Речь идет об аппаратной или программной виртуализации. Т.е. "Просто накатить Docker" - не прокатит. 52 | > А если вы все еще не понимаете, что контейнеризация это частный случай виртуализации на уровне операционной системы - вам стоит посмотреть доклад про DevOps и/или прочитать дополнительные материалы. 53 | 54 | 1. Скачиваем образ для установки дистрибутива Debian. 55 | Это можно сделать с [зеркала МИФИ][debian-mirror]. Впрочем, вы можете выбрать любой способ загрузки, подходящий вам, например через [BitTorrent][debian-torrent]. Все варианты можно узнать [тут][debian-all-mirrors]. 56 | 57 | > Во время подготовки курса использовался [образ][debian-iso] `debian-11.2.0-amd64-DVD-1.iso`, как наиболее универсальный вариант. Но с серверов МИФИ он загружался целую вечность. **Лучше использовать BitTorrent**. 58 | 59 | 2. Пока качается образ - создаем виртуальную машину (далее ВМ) в системе виртуализации. Ставить будем в VirtualBox (далее VB). 60 | 61 | Как назвать - дело ваше. Удобно набрать "debian" в названии, тогда VB сразу подставит значения для Debian в выпадающие списки. 62 | ![Создаем ВМ](./img/s1.png) 63 | 64 | Выделяем диск. Использовать рекомендуется динамический виртуальный диск. Тогда покуда внутри ВМ вы не забьете место на диске - оно не будет занято и в вашей хостовой системе. Фактический размер будет динамически растягиваться под требования (но автоматически сжиматься не будет). Достаточно будет и 10 GiB. Лучше сразу больше, все равно место оно не занимает, пока не потребуется. 65 | ![Создаем диск](./img/s2.png) 66 | 67 | Получится как-то так: 68 | ![Результат](./img/s3.png) 69 | 70 | 3. В настройках ВМ в системе виртуализации выбираем загруженный образ в качестве содержимого оптического носителя, чтобы с него загрузиться: 71 | ![Выбираем образ](./img/s4.png) 72 | 73 | > Примечание: Для систем с большим разрешением экрана советую сразу поставить в "Настройки ВМ" -> "Дисплей" коэффициент масштабирования побольше. На MacBook-ах с Retina-display иначе слишком мелко. 74 | 75 | 4. Запускаем виртуальную машину и устанавливаем систему. Подробно рассматривать процесс установки не будем, остановимся только на самых важных деталях. Для интересующихся руководство по установке Debian для x86-64 архитектуры вы можете найти [тут][debian-install]. 76 | [Полный список руководств][debian-all-install]. 77 | 78 | ![Загрузились с образа](./img/s5-installer.png) 79 | 80 | Итак, к важным деталям, использовать будем текстовый установщик, чтобы не пришлось делать миллион снимков экрана в методичку. Разберем подробно, чего от нас хочет установщик: 81 | 82 | + Выбор региона и раскладки клавиатуры. Если выбираете себе что-то кроме английской раскладки - дело ваше, страдать Вам. Я оставляю все по-умолчанию. 83 | + Далее установщик попросит вас ввести `hostname` - [сетевое имя системы][hostname]. На ваше усмотрение. В нашем примере выберем `enterprise`. 84 | + Теперь `domain`, для ВМ можно использовать `vm`. Получится что-то вроде `enterprise.vm`. 85 | + Пароль [root][root]. Пользователя с идентификатором (UID - user ID) 0 и имеющим неограниченные привилегии. Аналог администратора. Далее будем называть его root. Настоятельно не советуем ставить свой настоящий сложный пароль на ВМ. После ввода его попросят еще раз для подтверждения, что вы в своем уме. 86 | + Имя вашего аккаунта и имя пользователя. Выбирайте что-то простое и ёмкое. В примере используется `mak` для всего. 87 | + Пароль аккаунта. Те же советы, что и для пароля root. 88 | + Разметка диска. Для общей однообразности выбираем `Guided - use entire disk and set up LVM`. Система автомагически поставится поверх LVM. Для реальных систем идут жаркие споры о том стоит или не стоит ставить систему на LVM. Но у нас требований к надежности нашей виртуальной машины нет, поэтому установим сразу поверх. Из плюсов решения: можно легко расширять доступное под систему место с помощью добавления новых дисков в lvm. Из минусов: при потере части этих дисков есть шанс систему больше не запустить. 89 | + Далее может отличаться в зависимости от того, что вы выбрали на прошлом шаге, будем считать, что нужное. 90 | + Какой диск пойдет под нож разметки диска и куда будет установлена система. Выбора нет, виртуальный диск в ВМ пока только один. 91 | + Необходимо ли разделить диск на несколько разделов для отдельных директорий. Выбираем `Separate /home, /var, and /tmp partitions`. Все по-взрослому. Часто эти директории вообще выносят на отдельные диски, что очень удобно, например: система и программы на ssd, а логи и ваши фотографии из отпуска, которые вы никогда больше не будете смотреть, на hdd. 92 | + Подтверждение, что ранее по разметке все выбрано верно. Выбираем `Yes`. 93 | + Сколько места вы хотите отвести под логический том LVM. Выбираем максимум или пишем `max`. 94 | + Подтверждение, что вы действительно хотите записать изменения на диск. Выбираем `Yes`. У нас получились вот такие разделы: ![разделы](./img/s6-lvm-ex.png) 95 | + Начнется установка системы. Это может занять значительное время, особенно если вы используете netinstall-образ и медленную сеть. Самое время сделать себе чаю. 96 | + Далее установщик предложит просканировать дополнительные источники для установки пакетов. Актуально, когда вы работаете в банке у вас нет доступа в интернет, но есть требования сразу установить определенное ПО, тогда его можно установить с других носителей. Игнорируем, выбираем `No`. 97 | + Далее установщик предложит выбрать источник репозиториев в системе - c текущего образа DVD (на нем есть некоторые пакеты) или из зеркала в сети. `Use network mirror?` выбираем `Yes`. Это более простой вариант поиска нужного ПО, а ограничений на доступ к интернету у нас нет. 98 | + Страна, для определения списка ближайших зеркал. Выбираем `Russia`, а далее понравившийся. Мы в примере все же будем использовать `deb.debian.org`, привычнее и надежнее для демонстрации. 99 | + HTTP-proxy для доступа к интернету. Иногда из корпоративного контура нет прямого доступа в интернет. Нам не актуально, будем ходить напрямую. Оставляем пустую строку, выбираем `Continue`. 100 | + Участие в программе сбора статистики - на ваше усмотрение. 101 | + Список предустанавливаемого ПО. Выбираем только ssh-сервер и стандартные системные утилиты. За использование "иксов" (визуального окружения рабочего стола, которые ранее назывались X Window System с реализацией в виде Xorg, но уже давно используются Wayland или Mir) получите минус в карму. Тыкаем "ввод", начнется установка ПО. Включаем у себя режим Хатико и вспоминаем про остывший чай. 102 | + Варианты установки GRUB. Что такое grub - поговорим отдельно. У нас на диске будет обнаружена только одна система на одном диске, так что установщик предложит установить его сразу на основной диск. Соглашаемся, `Yes`. 103 | + Выбираем этот диск (/dev/sda), он у нас один. ![Примерно так](./img/s7-grub.png) 104 | + Установка завершена! Успех. Установщик услужливо отправит команду на извлечение устройства с установщиком, чтобы вы при перезагрузке не попытались снова устанавливать систему. VB нормально обрабатывает такое поведение и удаляет образ из виртуального оптического привода (в настройках отображаться не будет больше). Начинается перезагрузка. 105 | + Пробуем войти своим пользователем. Если все введено верно, то вы увидите приглашение ввода команд (command prompt), например: `mak@enterprise:~$ `. ВМ "на логине". Критический успех!![На логине](./img/s8-onlogin.png) 106 | 107 | 5. Делаем мгновенный снимок виртуальной машины, если ваша система виртуализации это поддерживает. Тогда в любой момент мы сможем вернуться на данное состояние системы, если что-то сломаем. В VB переходим во вкладку "Снимки" (Snapshots) и нажимаем "сделать". Названия не принципиальны. 108 | 109 | > Люди делятся на два типа: 110 | > 1) Еще не делают бэкапы 111 | > 2) Уже делают бэкапы 112 | 113 | ![Снапшоты 1](./img/s9-snapshots.png) 114 | 115 | ![Снапшоты 2](./img/s9-snapshots-2.png) 116 | 117 | 6. Что-нибудь изменим в системе, например напишем пару команд и выключим ВМ. Теперь попробуем восстановить мгновенный снимок из шага 5. Если все в полном порядке, вы откатитесь к состоянию ВМ на момент конца шага 4. 118 | 119 | > Люди, которые делают бэкапы, делятся на два типа: 120 | > 1) Еще не проверяют свои бэкапы после создания 121 | > 2) Уже проверяют свои бэкапы после создания 122 | 123 | ![Накатили-откатили](./img/s10-revert.png) 124 | 125 | Снимаем галочку, чтобы не создать снимок текущего (будем считать его испорченным) состояния: 126 | ![Подтверждение](./img/s11-confirm.png) 127 | 128 | > Можете добавить в список навыков в выводе отчета, что вы научились делать резервные копии и сразу проверять их на работоспособность. **Очень важный навык для любого DevOps-инженера или администратора.** 129 | 130 | 7. Перезагружаемся (reset) с помощью VB. ВМ мгновенно перезапустится (будто мы обесточили и снова запустили сервер). Если все в порядке - вы снова окажетесь "на логине". ![Перезагрузка](./img/s12-reset.png) 131 | 132 | ### 1.2. Выполнение базовых команд 133 | 134 | Шпаргалка наиболее частых команд: 135 | 136 | - cd (change directory) - сменить рабочий каталог 137 | - mv (move) - переместить объект фс 138 | - cp (copy) - скопировать объект фс 139 | - mkdir (make directory) - создать каталог 140 | - echo - вывод аргументов на экран 141 | - cat - вывод объекта (например, файла) на экран 142 | - less - утилита для удобного просмотра больших текстовых файлов 143 | - sort - утилита для сортировки 144 | - grep - фильтрация строк по подстрокам 145 | 146 | > Статья о циклах в командной оболочке [тут][bash-loop] 147 | 148 | Для выполнения работы вам непременно потребуется знание о том, что такое конвейер (pipeline). Небольшое напоминание о работе конвейера: 149 | 150 | `cat file.txt | grep bmstu | sort -u` 151 | `cat file.txt` - вывести содержимое файла, передаем это утилите grep 152 | `grep bmstu` - оставить только строки, содержащие bmstu, передать вывод утилите `sort` 153 | `sort -u` - отсортировать в алфавитном порядке и оставить только уникальные значения 154 | 155 | 156 | 157 | 1. Выполнить набор ручных операций в терминале 158 | 159 | > Примечание: если написано "с содержимым `xyz\n`", то в файл нужно записать `xyz` и перевод строки. Однако, утилита echo по-умолчанию добавляет в конец записи перевод строки, не требуется специально это прописывать. 160 | 161 | + Перейти в домашнюю директорию вашего пользователя `~` 162 | + Вывести содержимое директории, включая скрытые файлы 163 | + Создать в текщей директории новую директорию `test` 164 | + Перейти в директорию `test/` 165 | + Вывести полный путь до текущей директории 166 | + Вернуться в домашнюю директорию 167 | + Создать пустой файл `~/test/file1.txt` 168 | + Создать файл `~/test/file2.txt` с содержимым `Hello\n` 169 | + Создать файл `~/test/file3.txt` с содержимым `World\n` 170 | + Дописать в файл `~/test/file3.txt` строку `Hello3\n` 171 | + Создать файлы `~/test/file${N}.txt` с содержимым `Hello${N}\n`, где `${N}` заменить на число, пробегающее [4; 6] 172 | + Создать файлы `~/test/file${N}.txt` с содержимым `World${N}\n`, где `${N}` заменить на число, пробегающее [7; 39] 173 | 174 | + Прейти в директорию `test/`, используя поиск по последним командам bash (CTRL+R) 175 | + Вывести содержимое файла `file31.txt` 176 | + Вывести только последнюю строку файла `file3.txt` 177 | + Вывести список всех файлов в текущей директории, содержащих подстроку `Hello` (и не забыть more/less) 178 | + Вывести список всех файлов в текущей директории, отсортированный по числовому возрастанию номера после `file` в названии (и не забыть more/less) 179 | 180 | > Интерактив: как вернуться обратно в домашнюю директорию максимально возможным числом способов? 181 | > Интерактив: поразмыслить о том, какие могут быть варианты создать пустой файл? 182 | 183 | 2. Станьте суперпользователем (root-ом) с помощью утилиты `su`. 184 | 185 | > **Root** (англ. *root* — корень; читается «рут»), или **суперпо́льзователь** — это специальный аккаунт и группа пользователей в UNIX-подобных системах с идентификатором UID 0 (User IDentifier), владелец которого имеет право на выполнение всех без исключения операций. Суперпользователь UNIX-систем имеет логин «root» только по умолчанию и легко переименовывается при необходимости, часто встречается переименование в «toor» для усложнения подбора паролей автоматическими сканерми. 186 | > 187 | > **su** (англ. **S**ubstitute **U**ser, **S**et **U**ID, **S**witch **U**ser*, **S**uper **U**ser* — замена пользователя, переключение пользователя, суперпользователь) — команда Unix-подобных операционных систем, позволяющая пользователю войти в систему под другим именем, не завершая текущий сеанс. Обычно используется для временного входа суперпользователем для выполнения административных работ. По умолчанию предполагается работа от имени пользователя root. Если первый аргумент `su` — дефис `-`, среда будет установлена такой же, как при регистрации заданного пользователя. Иначе передается текущая среда, за исключением значения $PATH, которое задается переменными PATH и SUPATH в файле `/etc/default/su`. 188 | 189 | ```bash 190 | su - 191 | # вводим пароль root, становимся root-ом (видно по строке приглашения) 192 | ``` 193 | 194 | 3. Отредактируйте список репозиториев `/etc/apt/sources.list` с помощью любого текстового редактора. 195 | 196 | > Примечание: Если вы не работали ранее с vi/vim - используйте nano. Вопросы "как выйти из vim" выходят за рамки курса. 197 | 198 | Пример того, как открыть файл на редактирование в nano: 199 | 200 | ```bash 201 | nano /etc/apt/sources.list 202 | ``` 203 | 204 | Нужно удалить или закомментировать строку `deb cdrom:[Debian GN...`. Это остатки от нашей установки с DVD-носителя, мы его извлекли уже и устанавливать оттуда ничего не собираемся. 205 | 206 | ![Пример файла sources.list](./img/s13-apt.png) 207 | 208 | 4. Определить, есть ли у вас в системе утилита `sudo`: 209 | 210 | - Выполняются только исполняемые файлы, которые обнаружены в переменной PATH или передан полный путь до файла. Поиск по PATH происходит последовательно, слева направо. Как правило, PATH задается в .bashrc. 211 | 212 | - `which sudo` - покажет путь до исполняемого файла программы, если она найдена в PATH. 213 | 214 | ![which](./img/s22-which.png) 215 | 216 | - Установить пакет `sudo` с помощью пакетного менеджера `apt` если его нет. [Руководство][man-apt]. 217 | Пример: 218 | 219 | ```bash 220 | apt install -y sudo 221 | ``` 222 | 223 | 5. Перестаньте быть root - наберите команду `exit` или нажмите комбинацию `^D` (CTRL+D) 224 | 225 | 6. Напишите "исполняемый" bash-скрипт (лучше sh), который создает в текущей директории файлы `test/file${N}.txt` с содержимым `Hello${N}\n`, где `${N}` заменить на число, пробегающее [X; Y], если они еще не созданы. X и Y задаются как 1 и 2 аргументы соответсвенно. 226 | 227 | > Примечание: в GNU/Linux CTRL часто обозначается как ^ 228 | > 229 | > Сохранить в nano: `^O` 230 | > Выйти из nano: `^X` 231 | 232 | Пример скрипта, который создает файл с содержимым первого и второго аргументов: 233 | 234 | ```bash 235 | #!/bin/sh 236 | 237 | echo "$1" > test.txt 238 | echo "$2" >> test.txt 239 | ``` 240 | 241 | Пример того, как сделать файл "исполняемым": 242 | ```bash 243 | chmod +x file.sh 244 | ``` 245 | 246 | Пример того, как запустить “исполняемый” файл c аргументами из текущей директории: 247 | ```bash 248 | ./file.sh 10 15 249 | ``` 250 | 251 | > Интересный факт: а почему исполняемый файл нельзя запустить просто написав file.sh как в windows? 252 | > 253 | > Все дело в безопасности. Это очень плохая идея позволять подменять стандартные утилиты файлами из текущей директории, есть риск исполнить не то, что подразумевал пользователь, а то, что подложил ему в каталог злоумышленник. Поэтому текущая директория **не добавляется** к переменной PATH, списку директорий в которых производится поиск исполняемых файлов. 254 | 255 | 7. Продемонстрировать умение пользоваться утилитой [man][https://ru.wikipedia.org/wiki/Man]. Вывести страницу мануала для утилиты apt. 256 | 257 | 8. Демонстрация проделанной работы происходит через вывод утилиты `history` и вывод содержимого скрипта из п.4. 258 | 259 | 260 | 261 | ## Часть 2. 262 | 263 | Задача: создать пользователя, выдать ему права и доступ к sudo, настроить подключение по ssh. 264 | 265 | ### 2.1. Пользователи и права 266 | 267 | > Примечание: в этом пункте потребуется доступ суперпользователя (root), опустим процесс, вы уже научились это делать в первой части лабораторной работы. 268 | 269 | 1. Создать пользователя `ansible` с домашней директорией `/home/ansible/`. Например, с помощью утилиты `adduser`. Хорошая [статья][useradd-vs-adduser] о создании пользователей. 270 | 271 | > Важный момент: в GNU/Linux нет магии, все происходит максимально логично. Так, добавление пользователя сводится к изменению файлов `/etc/passwd` и `/etc/shadow`. Исторически файл `/etc/passwd` содержал информацию о пользователях и их зашифрованные (чаще всего - хешированные) пароли. Однако, для того, чтобы пользователь мог взаимодействовать с системой - запускать программы от лица других пользователей, просматривать права доступа и так далее, нам необходимо, чтобы этот файл был доступен для чтения всем. А значит, в теории, пароли других пользователей можно было бы подобрать перебором, ведь они нам были бы тоже доступны. Для того, чтобы избежать проблем с утечкой паролей - их вынесли в отдельный файл `/etc/shadow`. Больше о формате этих файлов стоит узнать в справочных материалах. 272 | 273 | ![passwd](./img/s29-passwd.png) 274 | 275 | 2. Поменять пользователю `ansible` пароль с помощью `passwd`. 276 | 277 | 3. Создать группу `admin` с помощью `groupadd`. 278 | 279 | 4. Создать директорию `/admin` c помощью `mkdir`. 280 | 281 | 5. Сделать owner-ом директории `/admin` пользователя `ansible` и выдать права группе `admin`. 282 | 283 | 6. Выдать на права на чтение и редактирование пользовтелю `ansible`, а группе `admin` только не чтение. 284 | 285 | ```bash 286 | chmod 755 /admin 287 | ``` 288 | 289 | 7. > **sudo** (англ. **S**ubstitute **U**ser and **do**, дословно «подменить пользователя и выполнить») — программа для системного администрирования UNIX-систем, позволяющая делегировать те или иные привилегированные ресурсы пользователям с ведением протокола работы. Основная идея — дать пользователям как можно меньше прав, но при этом достаточных для решения поставленных задач. 290 | 291 | С помощью утилиты `visudo` можно отредактировать файл `/etc/sudoers` (через утилиту - чтобы ничего не сломать, она создает временный файл и перед применением изменений проверяет синтаксис) - разрешить группе `wheel` делать `sudo` с паролем. 292 | 293 | Этого можно добиться добавив строку `%wheel ALL=(ALL:ALL) ALL` 294 | 295 | - **%wheel** ALL=(ALL:ALL) ALL 296 | Первое поле показывает имя пользователя или группы, к которым будет применяться правило, в данном случае к группе **wheel**, для пользователя нужно убрать `%`. 297 | - %wheel **ALL=**(ALL:ALL) ALL 298 | Первое “ALL” означает, что данное правило применяется ко всем хостам. 299 | - %wheel ALL=(**ALL**:ALL) ALL 300 | Данное “ALL” означает, что пользователь группы **wheel** может запускать команды от лица всех пользователей. 301 | - %wheel ALL=(ALL:**ALL**) ALL 302 | Данное “ALL” означает, что пользователь группы **wheel** может запускать команды от лица всех групп. 303 | - %wheel ALL=(ALL:ALL) **ALL** 304 | Последнее “ALL” означает, что данные правила применяются всем командам. 305 | 306 | Хорошая статья о sudoers файле [тут][sudoers]. 307 | 308 | 8. Добавить пользователя `ansible` в группу `wheel` с помощью утилиты `usermod` - теперь он сможет выполнять команды от лица суперпользователя через `sudo` с использованием своего пароля. 309 | 310 | Пример частичного выполнения данного задания: 311 | 312 | ![2.1](./img/s25-user.png) 313 | 314 | 315 | 316 | ### 2.2. Подключение по SSH 317 | 318 | **SSH** (англ. **S**ecure **Sh**ell — «безопасная оболочка») — сетевой протокол прикладного уровня, позволяющий производить удалённое управление операционной системой и [туннелирование](https://ru.wikipedia.org/wiki/Туннелирование_(компьютерные_сети)) TCP-соединений (например, для передачи файлов). Схож по функциональности с более старыми протоколами Telnet и rlogin, но, в отличие от них, шифрует весь трафик, включая и передаваемые пароли. SSH допускает выбор различных алгоритмов шифрования. 319 | 320 | **По-умолчанию ssh-сервер использует 22-ой TCP-порт.** 321 | 322 | 0. Установить на **основную систему** (ее еще называют хостовой или гипервизором) ssh-клиент. В Unix/Linux системах `ssh` стоит по-умолчанию. В Windows - [PuTTY][putty], проще всего скачать установщик всех утилит (сам PuTTY, PuTTYgen, pageant и прочие программы). 323 | 324 | 1. Настроить "проброс портов" для виртуальной машины **127.0.0.1:2222 -> 10.0.2.15:22** 325 | 326 | ![Port forwarding](./img/s14-nat.png) 327 | 328 | ![SSH-rule](./img/s15-ssh-rule.png) 329 | 330 | 2. На **ВМ** - установить и запустить sshd (проверить запущен ли: `systemctl status sshd`, запустить: `systemctl start sshd`) 331 | 332 | 3. На **основной** системе настроить ssh-подключение по ключу 333 | 334 | > Примечание: подробная статья о PuTTY есть на [хабре][putty-habr]. 335 | 336 | + Сгенерировать ssh-ключи (в утилите [PuTTYgen][puttygen] или `ssh-keygen`). Для этого нужно перегенерировать ключ 2 версии. 337 | 338 | Windows: 339 | 340 | ![PuTTYgen](./img/s18-win-keygen-1.png) 341 | 342 | ![PuTTYgen](./img/s18-win-keygen-2.png) 343 | 344 | Unix/Linux: 345 | 346 | ![ssh-keygen](./img/s19-ssh-keygen.png) 347 | 348 | + Положить публичную часть ключа в файл `/home/ansible/.ssh/authorized_keys` на **ВМ** 349 | 350 | + Проверить права файла `/home/ansible/.ssh/authorized_keys`, они должны быть `rw-r--r--` 351 | 352 | 4. Продемонстрировать подключение по ssh (PuTTY+pageant для windows) под пользователем `ansible` и стать root-ом с помощью sudo. 353 | 354 | Windows: 355 | 356 | ![win1](./img/s16-win-1.png) 357 | 358 | ![win1](./img/s16-win-2.png) 359 | 360 | ![win1](./img/s16-win-3.png) 361 | 362 | Unix/Linux, сразу с применением `ssh-copy-id` вместо ручного редактирования `authorized_keys`: 363 | 364 | ![Unix](./img/s17-unix.png) 365 | 366 | 367 | 368 | 5. Показать soft-лимиты (`ulimit`) и hard-лимиты, пояснить как поняли, в чем разница, как изменить? 369 | 370 | Хорошая [статья][linux-limits] о лимитах. 371 | 372 | > Примечание: это может оказаться жизненно важным знанием в случае появления высоких нагрузок на вашем сервере. Ошибка "Too Many Open Files" может появляться как раз из-за ограничений в лимитах, по-умолчанию они довольно небольшие (1024 для open files). 373 | 374 | Пример вывода `ulimit` для пары систем (ВМ и реальный Raspberry-Pi сервер): 375 | 376 | ![ulimit](./img/s20-ulimit.png) 377 | 378 | Посмотрим лимиты для конкретного процесса, тоже бывает полезно: 379 | 380 | ```bash 381 | # Некоторые программы кладут свой Process ID (PID) в /var/run 382 | cat /var/run/sshd.pid # Выведет PID sshd 383 | 384 | # Выведем лимиты процесса sshd с помощью procfs 385 | cat /proc/$(cat /var/run/sshd.pid)/limits 386 | # P.S. Про procfs подробно поговорим во второй лабораторной работе 387 | ``` 388 | 389 | ![sshd-limits](./img/s23-sshd-limits.png) 390 | 391 | Занятное наблюдение работы лимитов: 392 | 393 | ![limit-check](./img/s24-limit-check.png) 394 | 395 | 396 | 397 | 6. Перестать быть root-ом уже! 398 | 399 | 7. Показать что в ssh-agent есть ваш ключ (`ssh-add -L`), если нет, то проверить настройки ssh-клиента (`ForwardAgent yes`). 400 | 401 | > `ssh-agent` — это менеджер ключей для SSH. Он хранит ваши ключи и сертификаты в памяти, незашифрованные и готовые к использованию `ssh`. Это избавляет вас от необходимости вводить пароль каждый раз, когда вы подключаетесь к серверу. Он работает в фоновом режиме в вашей системе, отдельно от `ssh`, и обычно запускается при первом запуске `ssh`-подключения. Опиция ForwardAgent позволяет пробрасывать ваши ключи от одного сервера к другому через цепочку ssh-агентов. Таким образом вам не придется копировать свой ключ последовательно на все узлы в цепочке подключений, что значительно повышает безопасность. 402 | 403 | ![ssh-add](./img/s21-ssh-add.png) 404 | 405 | 406 | 407 | 8. **Дополнительное задание**: настроить `/root/.bashrc` файл, применяющийся при создании новой оболочки для root пользователя. 408 | 409 | Хорошая [статья][bash-profile-rc] о bash_profile и bashrc. 410 | 411 | [Пример][bashrc-example] большого и сложного .bashrc файла. 412 | 413 | PS1 - переменная для изменения вида приглашения, например такое значение красит в красный: 414 | 415 | ```bash 416 | PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w \$\[\033[00m\] ' 417 | ``` 418 | 419 | Синонимы (alias-ы) для подкрашивания вывода команд: 420 | 421 | ```bash 422 | # enable color support of ls and also add handy aliases 423 | if [ -x /usr/bin/dircolors ]; then 424 | test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)" 425 | alias ls='ls --color=auto' 426 | alias dir='dir --color=auto' 427 | alias vdir='vdir --color=auto' 428 | 429 | alias grep='grep --color=auto' 430 | alias fgrep='fgrep --color=auto' 431 | alias egrep='egrep --color=auto' 432 | fi 433 | ``` 434 | 435 | Просто удобные общепринятые сокращения: 436 | 437 | ```bash 438 | # some more ls aliases 439 | alias ll='ls -l' 440 | alias la='ls -A' 441 | alias l='ls -lA' 442 | ``` 443 | 444 | Переопределения стандартных команд с ключами, чтобы спрашивали подтверждение: 445 | 446 | ```bash 447 | # Some more alias to avoid making mistakes: 448 | alias rm='rm -i' 449 | alias cp='cp -i' 450 | alias mv='mv -i' 451 | ``` 452 | 453 | ![.bashrc](./img/s26-bashrc.png) 454 | 455 | ![Colors](./img/s27-colors.png) 456 | 457 | 458 | 459 | 9. Создайте еще один мгновенный снимок ВМ, как в пункте 5 раздела 1.1. 460 | *Чтобы не случалось “упс, у меня все работало, а вот сейчас перестало”.* 461 | 462 | 463 | 464 | **Далее в лабораторных рекомендуется использовть способ работы с системой по ssh и запускать ВМ в фоновом режиме (без интерфейса вообще)** 465 | 466 | 467 | 468 | ## Контрольные вопросы 469 | 470 | 1. Что такое Linux? 471 | 2. Что делает каждая из команд, применяемых в лабораторной работе? 472 | 3. В чем разница между su и sudo? 473 | 4. Что происходит в подробностях, когда вы вводите `cat file.txt`? 474 | 5. Что такое файловый дескриптор? Какие создаются по умолчанию? 475 | 6. Как дописать в файл вывод команды? Как перезаписать файл? Как избавиться от вывода ошибок? 476 | 7. Как определяются права на файл? 477 | 8. Как создать пользователя в GNU/Linux? 478 | 9. Как пользоваться man? 479 | 10. Как сделать файл исполняемым? 480 | 11. Как выдать права на sudo? 481 | 12. Зачем .bashrc файл? 482 | 13. Как подключиться к сервиру по ssh? Подробно о всех возможных проблемах. 483 | 14. Что такое публичный и приватный ключ? Какой оставляем себе, а какой копируем на сервер? 484 | 15. ssh-agent - зачем? 485 | 16. В чем разница между soft и hard лимитами? 486 | 17. Кто такой суперпользователь и что ему можно? 487 | 18. Нужно ли делать резернвые копии? А что еще нужно делать? 488 | 489 | 490 | 491 | [habr-debian]: https://habr.com/ru/news/t/572976/ 492 | [debian-all-mirrors]: https://www.debian.org/CD/http-ftp/#mirrors 493 | [debian-mirror]: http://mirror.mephi.ru/debian-cd/current/ 494 | [debian-torrent]: https://www.debian.org/CD/torrent-cd/ 495 | [debian-iso]: http://mirror.mephi.ru/debian-cd/current/amd64/iso-dvd/debian-11.2.0-amd64-DVD-1.iso 496 | [debian-install]: https://www.debian.org/releases/stable/amd64/index.ru.html 497 | [debian-all-install]: https://www.debian.org/releases/stable/installmanual 498 | [virtualbox-install]: https://www.virtualbox.org/wiki/Downloads 499 | [hostname]: https://en.wikipedia.org/wiki/Hostname 500 | [root]: https://ru.wikipedia.org/wiki/Root 501 | [default-summary]: ./Summary.md 502 | [man-apt]: https://www.opennet.ru/man.shtml?topic=apt-get&category=8&russian=0 503 | [putty]: https://www.putty.org/ 504 | [puttygen]: https://www.ssh.com/academy/ssh/putty/windows/puttygen 505 | [putty-habr]: https://habr.com/ru/post/127521/ 506 | [linux-limits]: https://andreyex.ru/linux/komandy-linux-i-komandy-shell/upravlenie-resursami-sistemy-s-pomoshhyu-komandy-ulimit/ 507 | [useradd-vs-adduser]: https://andreyex.ru/linux/komandy-linux-i-komandy-shell/useradd-protiv-adduser-v-chem-raznitsa/ 508 | [bash-profile-rc]: https://devacademy.ru/article/razbiraiemsia-s-failami-etc-profile-i-etc-bashrc 509 | [bashrc-example]: https://www.opennet.ru/docs/RUS/bash_scripting_guide/a15124.html 510 | [sudoers]: https://www.digitalocean.com/community/tutorials/how-to-edit-the-sudoers-file-ru 511 | [linux-core]: https://ru.wikipedia.org/wiki/Ядро_Linux 512 | [linux-os]: https://ru.wikipedia.org/wiki/Linux 513 | [unix-way]: https://ru.wikipedia.org/wiki/Философия_Unix 514 | [streams]: https://ru.wikipedia.org/wiki/Стандартные_потоки 515 | [descriptor]: https://ru.wikipedia.org/wiki/Дескриптор 516 | [fd]: https://ru.wikipedia.org/wiki/Файловый_дескриптор 517 | [file]: http://linux.yaroslavl.ru/docs/setup/mandrake/cl/ch09s02.html 518 | [dir-term]: https://ru.wikipedia.org/wiki/Каталог_(файловая_система) 519 | [syscall]: https://ru.wikipedia.org/wiki/Системный_вызов 520 | [options]: http://www.linuxcookbook.ru/books/textbooks/linux_intro/ch03s04.html 521 | [libc]: https://en.wikipedia.org/wiki/C_standard_library 522 | [hard-io]: https://www.opennet.ru/docs/RUS/zlp/005.html 523 | [golang-args]: https://gobyexample.com/command-line-flags 524 | [python-args]: https://docs.python.org/3/library/argparse.html 525 | [chattr]: https://ru.wikipedia.org/wiki/Chattr 526 | [selinux]: https://wiki.gentoo.org/wiki/SELinux/Tutorials/How_SELinux_controls_file_and_directory_accesses 527 | [command-prompt]: https://pingvinus.ru/note/bash-promt 528 | [process-managment]: http://www.opennet.ru/docs/RUS/lnx_process/process2.html 529 | [processes]: http://www.opennet.ru/docs/RUS/lnx_process/ 530 | [distro]: https://ru.wikipedia.org/wiki/Дистрибутив_Linux 531 | [fhs]: https://refspecs.linuxfoundation.org/FHS_3.0/fhs/index.html 532 | [process-article]: https://ru.wikipedia.org/wiki/Процесс_(информатика) 533 | [thread-article]: http://citforum.ru/programming/unix/threads/ 534 | [thread-wiki]: https://ru.wikipedia.org/wiki/Поток_выполнения 535 | 536 | [bash-loop]: https://losst.ru/tsikly-bash 537 | 538 | [wiki-page]: ./Wiki.md 539 | 540 | -------------------------------------------------------------------------------- /Lab2.md: -------------------------------------------------------------------------------- 1 | # Лабораторная работа №2. Процессы и VFS. 2 | 3 | **Что потребуется перед началом**: 4 | 5 | - Виртуальная машина с установленной GNU/Linux. 6 | 7 | **План и задачи лабораторной**: 8 | 9 | 1. Процессы в Linux, мониторинг и управление системой, systemd, initrd, top/atop, dmesg, логи 10 | - Создать простейший systemd-unit, запустить набор сервисов, показать список процессов и чем они занимаются. 11 | 2. Файловая подсистема в Linux - VFS, inode, ссылки, ext4, mount, LVM 12 | - Подключить диск, создать фс, примонтировать, жесткие и мягкие ссылки, создать LV и растянуть его. 13 | 14 | **Отчет** - в любом читаемом формате (pdf, md, doc, docx, pages). 15 | 16 | Обязательное содержимое отчета: 17 | 18 | 0. Фамилия и инициалы студента, номер группы, номер варианта 19 | 1. План и задачи лабораторной 20 | 2. Часть 1 - кратко описать, что было сделано 21 | 3. Часть 2 - кратко описать, что было сделано 22 | 4. Приложить очищенный вывод `history` выполненных команд 23 | 5. [Универсальный вывод по лабораторной работе] 24 | 25 | 26 | 27 | ## Вступление 28 | 29 | Для полного понимания рекомендуется ознакомиться с разделами "Терминология", "Процессы" и “Файловая подсистема” [справочных материалов][wiki-page]. 30 | 31 | На практике сервера уже почти не управляются вручную. Это может приводить к ошибкам из-за человеческого фактора. А еще это значительно дольше и каждый сервер приходилось бы приводить в требуемое состояние отдельно. Чтобы избавиться от этих проблем создали подход **IaC** (**I**nfrastructure **a**s **C**ode). Ansible является системой для удаленного управления конфигураций и реализует подход IaC. Требуемое состояние сервера описывается в YAML-файлах, а затем интерпретатор за счет модулей сам принимает решение о том, как сервер в это состояние привести. 32 | 33 | IaC является неотъемлемой частью DevOps/SRE. Любой отказ сервера не приводит к длительной настройке нового - нам лишь нужно применить на него нашу IaC-систему. Более того, за счет таких систем как Ansible, Puppet, Chef, Salt - нам более не нужно настраивать каждый сервер отдельно. Мы управляем всеми разом. 34 | 35 | Для экономии времени лабораторных работ мы будем использовать заранее подготовленный репозиторий с файлами Ansible. 36 | 37 | [Знакомство с Ansible](https://ealebed.github.io/posts/2015/%D0%B7%D0%BD%D0%B0%D0%BA%D0%BE%D0%BC%D1%81%D1%82%D0%B2%D0%BE-%D1%81-ansible-%D1%87%D0%B0%D1%81%D1%82%D1%8C-1-%D0%B2%D0%B2%D0%B5%D0%B4%D0%B5%D0%BD%D0%B8%D0%B5/) для тех, кто хочет знать больше. 38 | 39 | 40 | 41 | ## Часть 1: 42 | 43 | 1. Установить `git`, `ansible` и развернуть сервисы с systemd-unit-ами. 44 | 45 | > Так мы автоматически произведем установку пакетов, если их еще нет, создадим пользователей, выдадим права, запустим процессы и так далее. В случае, если система уже находится в нужном состоянии, то Ansible не проводит повторных изменений. 46 | 47 | ```bash 48 | git clone https://github.com/SnipGhost/linux-course 49 | cd linux-course/ansible 50 | less README.md 51 | 52 | # Отредактировать шифрованный файл с секретами 53 | # vault-password: Zelda 54 | ansible-vault edit group_vars/all/secret.yml 55 | 56 | # Запустить раскатку 57 | ansible-playbook deploy-stage.yml 58 | ``` 59 | 60 | > Может пригодится: зашифровать обычный файл можно так: `ansible-vault encrypt filename` 61 | 62 | ![ansible](./img/s40-ansible.png) 63 | 64 | 2. Проверить состояние сервиса `chronyd`, остановить его, снова проверить состояние, запустить: 65 | 66 | ```bash 67 | systemctl status chronyd 68 | systemctl stop chronyd 69 | systemctl status chronyd 70 | systemctl start chronyd 71 | ``` 72 | 73 | > Это интересно: в терминах systemd кроме сервисов существуют еще “цели” - target. Они позволяют управлять несколькими процессами группой, одновременно. 74 | > 75 | > Кстати, а chronyd это программа-демон для синхронизации времени. Работает по протоколу NTP и является более продвинутой реализацией ntpd. 76 | 77 | ![chronyd](./img/s41-ntpd.png) 78 | 79 | 3. Открыть systemd-unit-файл `chronyd` и пояснить его содержимое. Сравнить его с файлом сервиса `node_exporter`. Отредактировать содержимое файла сервиса `node_exporter` (например, удалить часть аргументов запуска в секции ExecStart). Попробовать перезапустить. Убедиться, что systemd ругается на неподгруженные изменения списка сервисов. Запустить `systemctl daemon-reload` а потом перезапустить сервис вновь. Подробная [статья о systemd-unit-ах](https://linux-notes.org/pishem-systemd-unit-fajl/). 80 | 81 | ![systemd](./img/s42-systemd.png) 82 | 83 | 4. Показать список процессов (в `top`, `atop` **или** с помощью` /proc`), отсортировать его по потреблению процессора, затем памяти, затем swap. Объяснить что такое [LA](https://ru.wikipedia.org/wiki/Load_Average) ([совсем подробная статья](https://habr.com/ru/company/vk/blog/335326/)). 84 | 85 | ![top](./img/s43-top.png) 86 | 87 | 5. Показать исторические данные `atop` с помощью ключа `-r` за прошедшие время из каталога: `/var/log/atop/`. ([статья про atop](https://fornex.com/ru/help/atop/)) 88 | 89 | ![atop](./img/s44-atop.png) 90 | 91 | 6. Показать последние 10 сообщений в кольцевом буфере ядра с помощью `dmesg -T`. Статья [тут](http://rus-linux.net/MyLDP/consol/Linux_dmesg_Command.html). 92 | 93 | 7. Показать сообщения от момента последнего старта системы из файла `/var/log/messages`. 94 | 95 | 8. Пояснить, что такое [procfs](https://ru.bmstu.wiki/Procfs_(Proc_File_System)) и какую информацию можно получить из `/proc`. 96 | 97 | 9. Показать, сколько работает система без перезагрузки (`uptime`). Как узнать с большей точностью? Подсказка: с помощью procfs. 98 | 99 | 10. Показать загрузку дисков с детализацией за 5 секунд с помощью `iostat` ([статья про iostat](https://rtfm.co.ua/linux-opisanie-utility-iostat/)). 100 | 101 | 11. Показать сколько свободно оперативной памяти с помощью `free`. ([статья про free](https://andreyex.ru/operacionnaya-sistema-linux/komanda-free-v-linux-s-primerami/)) 102 | 103 | 12. Запустить утилиту `top` в интерактивном режиме. Поставить на паузу отправив сигнал SIGSTOP с помощью `^Z`. Запустить просмотр файла `less /etc/passwd`. Также поставить на паузу. Просмотреть список задач с помощью `jobs -l`. Заставить выполняться `top` в фоновом режиме с помощью `bg`. Спойлер: он остановится, потому что есть защита от дурака. Запустить на первом плане просмотр файла с помощью `fg`.Поставить на паузу. Послать обеим процессам сигнал обрабатываемого завершения работы (SIGTERM, 15) с помощью kill. Объяснить в чем разница между SIGTERM (15) и SIGKILL (9). Статьи о [jobs, fg, bg](https://rtfm.co.ua/linux-upravlenie-fonovymi-processami/) и [более подробная](http://www.opennet.ru/docs/RUS/lnx_process/process2.html). 104 | 105 | > Если ваш терминал завершится, то процессу автоматически направится сигнал SIGHUP, сигнализирующий о потере соединения с управляющим терминалом пользователя. Большая часть приложений после его обработки завершают работу. Чтобы избежать этого существует утилита nohup. Она становится родительской для команды, предаваемой ей как аргумент, и перехватывает сигнал SIGHUP. 106 | > 107 | > Это нужно, если вы хотите запустить какой-то процесс и быть уверенным, что если ssh-сессия разорвется, то он не завершится. Однако на практике для администраторов куда удобнее использовать tmux. 108 | 109 | 110 | 111 | ## Часть 2: 112 | 113 | **VFS** - подсистема ядра Linux. Задает универсальный интерфейс для файловой системы. 114 | 115 | 1. Показать сколько свободно места в системе с помощью `df`. 116 | 117 | 2. Пояснить, что такое inode, какая там хранится информация и какие могут быть связаны с ними проблемы. Показать сколько свободно inode с помощью `df`. 118 | 119 | > Информация, которая содержится в inode: 120 | > 121 | > - Номер inode (ino); 122 | > - ID устройства; 123 | > - ID владельца; 124 | > - ID группы файла; 125 | > - Режим доступа файла; 126 | > - Timestamp указывает дату последнего изменения inode (ctime, change time), последней модификации содержимого файла (mtime, modification time), и последнего доступа (atime, access time); 127 | > - Счётчик количества жестких ссылок, указывающих на индексный дескриптор; 128 | > - Указатели на блоки диска, хранящие содержимое файла. (зависит от ФС) 129 | > - Длина файла в байтах; 130 | 131 | 3. Выключить ВМ. Подключить еще один виртуальный диск размером 1 ГиБ. Включить ВМ. Должно появиться еще одно устройство - /dev/sdb. 132 | 133 | ![disk](./img/s45-disk.png) 134 | 135 | 4. Разбить вновый виртуальный диск на три раздела с помощью `fdisk`, разметить на первом ФС (`mkfs.ext4`), примонтировать (`mount`), точка монтирования: `/mnt/vdisk1/`. Показать вывод списка блочных устройств `lsblk`. Создать директорию `/mnt/vdisk1/vdir1` и записать туда файлик `test` с любым содержимым (но не пустой). 136 | 137 | ![fdisk](./img/s46-fdisk.png) 138 | 139 | ![mount](./img/s47-mount.png) 140 | 141 | 5. Примонтировать директорию `/mnt/vdisk1/vdir1` в домашний каталог `/root/vdir1` так, чтобы содердижимое первого отображалось во втором и наоборот: `mount --bind /mnt/vdisk1/vdir1 /root/vdir1` . [Статья](https://access.redhat.com/documentation/ru-ru/red_hat_enterprise_linux/6/html/global_file_system_2/s1-manage-pathnames). 142 | 143 | ![bind-mount](./img/s48-bind-mount.png) 144 | 145 | 6. Доказать, что файл `/mnt/vdisk1/vdir1/test` и `/root/vdir1/test` - один файл (`diff`, `ls -li`). 146 | 147 | 7. Реализовать автомонтирование первого раздела диска при старте системы (`/etc/fstab`). 148 | 149 | ![fstab](./img/s49-fstab.png) 150 | 151 | 8. Теперь LVM ([про LVM](https://koobik.net/%D1%83%D0%BF%D1%80%D0%B0%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D0%B5-linux-lvm/)). Создать на 2 разделе physical volume (PV - `pvcreate`), volume group (VG - `vgcreate`) и logical volume (LV - `lvcreate`). Разметить поверх LV файловую систему ext4. Примонтировать, записать любой файлик. Затем создать на 3 разделе еще один PV, добавить его в VG (`vgextend`) и растянуть LV (`lvextend`). Не забыть растянуть ФС: `resize2fs`. 152 | 153 | ![LVM](./img/s38-lvm.png) 154 | 155 | ![lv](./img/s50-lv.png) 156 | 157 | ![lvextend](./img/s51-lvextend.png) 158 | 159 | 9. Создать “жесткие” и “мягкие” ссылки с помощью утилиты `ln`, объяснить разницу. 160 | 161 | ![ln](./img/s52-ln.png) 162 | 163 | 10. Дополнительное задание: установить immutable bit и проверить, что root не может удалить файл. 164 | 165 | 11. Дополнительное задание: смонтировать ext4 с параметрами: 166 | `defaults,errors=remount-ro,nodiratime,noatime,data=ordered` 167 | 168 | - `noatime` и `nodiratime` - выключает изменение atime файлов. При добавлении этой опции каждое чтение файла не будет дергать диск для записи "времени последнего доступа" к файлу. Экономит от 0 до 25% IO в зависимости от использования FS. В разделах с СУБД и разделах, с которых и на которые выполняется rsync, опции добавлять очень полезно. 169 | - `errors=remount-ro` - при появлении ошибок в FS во время работы системы не позволяет ее доломать и не позволяет уронить ОС. Переводит файловую систему в режим Read-Only и можно хотя бы сделать бекап критичных файлов перед попыткой восстановления FS/RAID/... 170 | - `data=ordered` - позволяет использовать кеш записи, сильно ускоряет работу FS. Включать опцию нужно только на серверах, обеспеченных хорошим электропитанием, например, установленных в хороших ЦОД с резервной линией или подключенных к сети через UPS. 171 | 172 | 12. Дополнительное задание: установить и смонтировтаь NFS. 173 | 174 | ```bash 175 | # Сервер 176 | apt -y install nfs-kernel-server 177 | systemctl enable nfs-server 178 | mkdir /var/nfs 179 | echo "/var/nfs 127.0.0.1(rw,sync,no_subtree_check)" >> /etc/exports 180 | exportfs -a 181 | systemctl restart nfs-server 182 | 183 | # Клиент 184 | apt -y install nfs-common 185 | modprobe nfs 186 | cat /proc/filesystems | grep nfs 187 | mkdir /mnt/vdisk-nfs 188 | mount -t nfs 127.0.0.1:/var/nfs/ /mnt/vdisk-nfs 189 | ``` 190 | 191 | 192 | 193 | 194 | 195 | ## Контрольные вопросы 196 | 197 | 1. Как загружается Linux? 198 | 2. Что такое PID, PPID? 199 | 3. Состояния процессов? 200 | 4. Что делает каждая из команд, применяемых в лабораторной работе? 201 | 5. Что такое Ansible? 202 | 6. Что такое `systemd` и systemd-unit? 203 | 7. Объясните метрики load average и uptime? 204 | 8. Утилита `top` - что показывают: us, sy, ni, id, wa, hi, si, st? 205 | 9. Как пользоваться `jobs`, `fg`, `bg`, `nohup`? 206 | 10. Что такое VFS? 207 | 11. Что такое inode, какую информацию хранит? 208 | 12. Каков процесс монтирования? 209 | 13. Автомонтирование, `/etc/fstab`? 210 | 14. Что такое bind-mount? 211 | 15. Что такое LVM и зачем? 212 | 16. “Жесткие” и “мягкие” ссылки - в чем разница? 213 | 214 | 215 | 216 | [wiki-page]: ./Wiki.md -------------------------------------------------------------------------------- /Lab3.md: -------------------------------------------------------------------------------- 1 | # Лабораторная работа №3. Сетевая подсистема Linux. 2 | 3 | **Что потребуется перед началом**: 4 | 5 | - Виртуальная машина с установленной GNU/Linux. 6 | 7 | **План и задачи лабораторной**: 8 | 9 | 1. Сетевая подсистема в Linux. Сетевые утилиты. 10 | - Продемонстрировать ip-адрес виртуальной машины и список интерфейсов и потери пакетов на них 11 | - Поднять Nginx (с помощью готового Ansible), проверить, показать какие порты он слушает 12 | - Показать работу утилит curl, wget, nc, telnet 13 | - Показать работу утилит nmap, traceroute, ping, arping, dig, host 14 | - tmux, чатик с помощью nc 15 | - Cнять дамп трафика tcpdump-ом, объяснить 16 | 2. Сетевая подсистема в Linux. iptables, SSH-туннели. 17 | - Настроить iptables, iptables-save, iptables-restore, убедиться в его работе 18 | - Разрешить доступ c основной ОС, удалить доступ. 19 | - Поднять ssh-туннель для обхода правил iptables 20 | 3. [Дополнительно] Мониторинг 21 | 22 | **Отчет** - в любом читаемом формате (pdf, md, doc, docx, pages). 23 | 24 | Обязательное содержимое отчета: 25 | 26 | 0. Фамилия и инициалы студента, номер группы, номер варианта 27 | 1. План и задачи лабораторной 28 | 2. Часть 1 - кратко описать, что было сделано 29 | 3. Часть 2 - кратко описать, что было сделано 30 | 4. Приложить очищенный вывод `history` выполненных команд 31 | 5. [Универсальный вывод по лабораторной работе] 32 | 33 | 34 | 35 | ## Часть 1: 36 | 37 | 1. Продемонстрировать ip-адрес виртуальной машины и список интерфейсов и потери пакетов на них 38 | 39 | Утилитами ip и ifconfig (не предустановлен, попробуйте `apt search ifconfig`) 40 | 41 | ![ip](./img/s53-ip.png) 42 | 43 | ```bash 44 | ip -s link 45 | # Или 46 | ifconfig 47 | ``` 48 | 49 | 2. Поднять Nginx (с помощью готового Ansible), проверить, показать какие порты он слушает 50 | 51 | `ansible-playbook deploy-stage.yml --tags nginx` 52 | 53 | `curl enterprise.vm` (`curl` не предустановлен) 54 | 55 | `curl node.enterprise.vm` (но прежде добавить запись в [/etc/hosts](https://man7.org/linux/man-pages/man5/hosts.5.html)) 56 | 57 | `netstat -nlpt` || `ss -nlpt` - TCP/LISTEN 58 | 59 | `netstat -nlpu` || `ss -nlpu` - UDP/LISTEN 60 | 61 | Дополнительно: понять, почему `curl enterprise.vm` возвращает *401 Authorization Required*. 62 | 63 | 3. `wget http://node.enterprise.vm/metrics` - загрузить файл (страницу) 64 | 65 | `nc -vz enterprise.vm 80` - проверить открыт ли порт 80 66 | 67 | `nc -vz enterprise.vm 443` - проверить открыт ли порт 443 68 | 69 | `telnet node.enterprise.vm 80` - установить соединение по текстовому протоклу с 80 портом 70 | 71 | ``` 72 | GET /metrics HTTP/1.0 73 | Host: node.enterprise.vm 74 | ``` 75 | 76 | ![http](./img/54-http.png) 77 | 78 | 4. Показать работу nmap, traceroute, ping, arping, dig, host. Не предустановлены. Основы сетей с прошлого семестра. 79 | 80 | ![arping](./img/55-arping.png) 81 | 82 | 5. Netcat - NC. Статья [тут](https://habr.com/ru/post/336596/). Про tmux в [Wiki](./Wiki.md). 83 | 84 | ![nc](./img/56-nc.png) 85 | 86 | 6. Tcpdump (консольный аналог wireshark) - статья [тут](https://habr.com/ru/company/alexhost/blog/531170/) 87 | 88 | ![tcpdump](./img/57-tcpdump.png) 89 | 90 | 91 | 92 | ## Часть 2: 93 | 94 | Вводная статья про iptables [тут](https://losst.ru/nastrojka-iptables-dlya-chajnikov). Очень подробное на русском - [тут](https://www.opennet.ru/docs/RUS/iptables/). О том, как работает - [тут](https://www.k-max.name/linux/netfilter-iptables-v-linux/). 95 | 96 | [Переход с iptables на nftables](https://habr.com/ru/company/ruvds/blog/580648/). 97 | 98 | ![iptables](./img/58-iptables.jpg) 99 | 100 | #### Цепочки netfilter: 101 | 102 | - **PREROUTING** — для изначальной обработки **входящих** пакетов 103 | - **INPUT** — для входящих пакетов, адресованных непосредственно **локальному компьютеру** 104 | - **FORWARD** — для **проходящих** (маршрутизируемых) пакетов 105 | - **OUTPUT** — для пакетов, **создаваемых** локальным компьютером (исходящих) 106 | - **POSTROUTING**— для окончательной обработки **исходящих** пакетов 107 | - Также можно создавать и уничтожать собственные цепочки при помощи утилиты iptables. 108 | 109 | #### Цепочки организованны в 4 **таблицы**: 110 | 111 | - **raw** — пакет проходит данную таблицу до передачи [системе определения состояний](https://www.k-max.name/linux/netfilter-iptables-v-linux/#conn). Используется редко, например для маркировки пакетов, которые НЕ должны обрабатываться системой определения состояний. Для этого в правиле указывается действие *NOTRACK*. Содержитcя в цепочках *PREROUTING* и *OUTPUT*. 112 | 113 | - **mangle** — содержит правила модификации (обычно полей заголовка) IP‐пакетов. Среди прочего, поддерживает действия *TTL*, *TOS*, и *MARK* (для изменения полей TTL и TOS, и для изменения маркеров пакета). Редко необходима и может быть опасна. Содержится во всех пяти стандартных цепочках. 114 | 115 | - **nat** — предназначена для подмены адреса отправителя или получателя. Данную таблицу проходят только первый пакет из потока, трансляция адресов или маскировка (подмена адреса отправителя или получателя) применяются ко всем последующим пакетам в потоке **автоматически**. Поддерживает действия *DNAT*, *SNAT*, *MASQUERADE*, *REDIRECT*. Содержится в цепочках *PREROUTING*, *OUTPUT*, и *POSTROUTING*. 116 | 117 | - **filter** — **основная таблица**, используется по умолчанию если название таблицы не указано. Используется для фильтрации пакетов. Содержится в цепочках *INPUT*, *FORWARD*, и *OUTPUT*. 118 | 119 | 120 | 121 | [Шпаргалка по правилам iptables](https://www.digitalocean.com/community/tutorials/iptables-essentials-common-firewall-rules-and-commands) 122 | 123 | 124 | 125 | ### Часть 2. Iptables. 126 | 127 | 1. `apt install iptables` && `iptables -A INPUT -s 10.0.2.0/24 -j DROP` - ограничить ВСЕ пакеты из сети. 128 | 129 | 2. Да, отвалился SSH. Печально. Сбросим `iptables -F` из консоли или ребусом. 130 | 131 | 3. ```bash 132 | # Разрешим уже установленные соединения (и наш SSH) 133 | iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 134 | 135 | # Теперь снова попытаемся все сломать: 136 | iptables -A INPUT -s 10.0.2.0/24 -j DROP 137 | 138 | # Выжили, разрешим еще все TCP/22 соединения 139 | iptables -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT 140 | 141 | # SSH текущий ок, новый не работает. Исправляем ситуацию вставив правило выше DROP 142 | iptables -I INPUT 1 -p tcp -m tcp --dport 22 -j ACCEPT 143 | ``` 144 | 145 | 4. Удаляем лишнее с помощью `iptables -D` 146 | 147 | ![INPUT-chain](./img/59-input.png) 148 | 149 | 5. Сохраняем с помощью `iptables-save > /etc/iptables.rules.v4` 150 | 151 | 6. Вносим изменения, восстанавливаем с помощью `iptables-restore < /etc/iptables.rules.v4` 152 | 153 | 7. Создаем свою цепочку:`iptables -N my-chain` 154 | 155 | 8. Создаем port-forwarding на 80-ый порт виртуалки. Идем браузером, добиваемся открытия с помощью iptables. 156 | 157 | 9. Дополнительно: Немного переконфигурим nginx, чтобы открылось что-то красивое. 158 | 159 | ![spoiler](./img/60-spoiler.png) 160 | 161 | 10. Теперь удаляем доступы и запрещаем в iptables ходить на 80-ый порт снаружи. (См часть2, п.3) 162 | 163 | 164 | 165 | ### Часть 2. SSH-tunnel. 166 | 167 | SSH-туннели в PuTTY - [статья](https://putty.org.ru/articles/putty-ssh-tunnels.html). 168 | 169 | Классная статья про сложные случаи [тут](https://habr.com/ru/post/331348/). 170 | 171 | Руководство для полного новичка [тут](https://selectel.ru/blog/ssh-tunnels/). 172 | 173 | 174 | 175 | Настраиваем ssh-туннель для проброса 80-го порта и пытаемся открыть таким образом себе доступ к запретному. Сработает через NAT, VPN и тройные фаерволы. Лишь бы был ssh! 176 | 177 | 178 | 179 | ssh -L :127.0.0.1: @ 180 | 181 | 182 | 183 | ## Контрольные вопросы 184 | 185 | 1. Что такое сетевой интерфейс? 186 | 2. Что значит слушать порт? 187 | 3. Что такое netfilter? 188 | 4. Как запретить доступ к серверу по определенному порту? 189 | 5. Как указать nginx-у слушать или проксировать на определнный порт? 190 | 6. Что могут ssh-туннели? 191 | 7. Что может nc? 192 | 8. Зачем нам слушать 0.0.0.0? -------------------------------------------------------------------------------- /Lab4.md: -------------------------------------------------------------------------------- 1 | # Лабораторная работа №4. Docker & GitLab-CI. 2 | 3 | **Что потребуется перед началом**: 4 | 5 | - Виртуальная машина с установленной GNU/Linux. 6 | - Базовые знания о работе GNU/Linux 7 | 8 | **План и задачи лабораторной**: 9 | 10 | 1. Docker - установка, настройка, запуск контейнера 11 | - Установить и запустить Docker 12 | - Поднять готовый контейнер 13 | - Операции над контейнерами 14 | - Создание Dockerfile 15 | - Сборка своего контейнера 16 | - docker diff 17 | 2. GitLab-CI - зачем нужно, настройка, gitlab-runner, написание своего pipeline 18 | - Регистрация в bmstu.codes, анализ уже готового CI/CD процесса 19 | - Запуск gitlub-runner-а 20 | - Добавить в репозиторий .gitlab-ci.yml 21 | - Продемонстрировать работу автоматизации развертывания 22 | 23 | **Отчет** - в любом читаемом формате (pdf, md, doc, docx, pages). 24 | 25 | Обязательное содержимое отчета: 26 | 27 | 0. Фамилия и инициалы студента, номер группы, номер варианта 28 | 1. План и задачи лабораторной 29 | 2. Часть 1 - кратко описать, что было сделано 30 | 3. Часть 2 - кратко описать, что было сделано 31 | 4. Приложить очищенный вывод `history` выполненных команд 32 | 5. [Универсальный вывод по лабораторной работе] 33 | 34 | 35 | 36 | ## Введение 37 | 38 | Итак, мы шли к этому моменту целый семестр. DevOps! 39 | 40 | > Для общего понимания принципов и философии DevOps можно послушать две моих презентации “Думай как SRE” и “Философия DevOps”. В рамках курса упор делается на практику и технологии, а не на подходы к разработке и построению рабочих процессов. 41 | 42 | И для начала - неплохо бы разобраться, а вообще зачем нам это все? 43 | 44 | Ответ прост: разработчикам понадобилось разделять ресурсы одного мощного сервера на несколько независимых друг от друга окружений. По различным причинам - доступы, разные версии ПО, разные требования к ресурсам. И для этого есть масса инструментов, например, venv в python. Что совершенно не дает изоляции на уровне процессов. Это плохо. Как поддерживать много отдельных сайтов разных клиентов на одном сервере? Они ведь смогут пролезть друг к другу и украсть чужие данные. А еще неплохо бы дать возможность одному клиенту использовать Debian, другому - Centos, а третьему вообще Windows. 45 | 46 | Отличная новость: для этого есть решение - виртуализация. Давайте внутри одной операционной системы (основной, “хостовой”) запустим другую (гостевая), пусть основная прикидывается аппаратным обеспечением. ПО, которое позволяет запустить ОС внутри ОС называют гипервизор (hypervisor), например, VirtualBox, VMWare или QEMU-KVM. Вот у нас и полная изоляция (ОС же разные) и разделение окружений (отдельная файловая система) и даже есть возможность прикидываться другой архитектурой процессора (транслируем команды одной архитектуры в другую на уровне хостовой ОС). Такой подход называется эмуляцией. Но у него есть серьезная проблема - получаем огромные накладные расходы на трансляцию команд. Вместо этого, давайте дадим доступ к аппаратному обеспечению напрямую (пусть и ограниченно) для гостевой ОС. Так мы частично потеряем возможность прикидываться другой архитектурой, но придется смириться с этой потерей ради повышения производительности. А еще наше аппаратное обеспечение должно уметь такую виртуализацию поддерживать, из-за чего такой подход называют аппаратной виртуализацией. 47 | 48 | Но увы, наше ПО все еще работает медленнее, чем на системе без виртуализации. И эта разница может быть очень значительной. Что же мешает работать быстрее? Взглянем на схему работы аппаратной виртуализации. 49 | 50 | ![Аппратная виртуализация](./img/61-virt-1.png) 51 | 52 | Как это можно оптимизировать? Очевидно, выбросить саму прослойку в виде гостевой ОС. У нас уже есть ядро основной ОС и оно может обрабатывать запросы от приложений. И по принципу бритвы Оккама еще неплохо бы не множить сущности без необходимости, т.е. как-то объединить похожие окружения. Получаем контейнерную виртуализацию - наши процессы исполняются на ядре основной ОС, а мы лишь пытаемся изолировать их друг от друга. 53 | 54 | ![Контейнеризация](./img/61-virt-2.png) 55 | 56 | Работает это за счет механизмов linux-namespace, vfs, cgroups, capabilities, netfilter и так далее, часть из которых мы изучали в текущем курсе лабораторных. 57 | 58 | Но тут же возникает вопрос, каким образом тогда Docker работает на Windows или MacOS? Ведь сборки есть и они работают. Что ж, тут тоже все просто. Он не работает. На других ОС приложение Docker запускает леговесную виртуальную машину с ядром Linux, поверх которой уже работает все остальное. Костыли. 59 | 60 | ![Контейнеризация](./img/61-virt-3.png) 61 | 62 | Таким образом, контейнер - это набор изоляций для процессов, работающий поверх нашего ядра Linux. А сеть для него настраивается правилами netfilter. Никакой магии. 63 | 64 | 65 | 66 | ## Часть 1. 67 | 68 | Устанавливаем Docker по [документации](https://docs.docker.com/engine/install/debian/). 69 | 70 | ```bash 71 | # Качаем ключ 72 | curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg 73 | 74 | # Добавляем репозиторий 75 | echo \ 76 | "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ 77 | $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null 78 | 79 | # Ставим 80 | sudo apt update 81 | sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin 82 | ``` 83 | 84 | > Нужно обязательно обновить apt cache (`apt update`) после добавления нового репозитория. 85 | 86 | Проверяем: 87 | 88 | ```bash 89 | sudo docker run hello-world 90 | ``` 91 | 92 | ![Docker-hello-world](./img/62-docker.png) 93 | 94 | > Стоит ознакомиться с предлагаемыми возможностями и руководствами 95 | 96 | Теперь нам предстоит запустить свой первый настоящий контейнер (помимо hello-world), с python на борту. 97 | 98 | ```bash 99 | sudo docker run -it python:3.8-slim-buster bash 100 | ``` 101 | 102 | ![Docker-run](./img/63-docker-run.png) 103 | 104 | Смотрим список образов: 105 | 106 | ```bash 107 | sudo docker image ls 108 | ``` 109 | 110 | Смотрим список запущенных контейнеров (пуст): 111 | 112 | ```bash 113 | sudo docker ps 114 | ``` 115 | 116 | Смотрим список всех контейнеров (2 завершенных): 117 | 118 | ```bash 119 | sudo docker ps --all 120 | ``` 121 | 122 | ![Docker-run](./img/64-docker-ps.png) 123 | 124 | Образ можно представить себе как набор расширений вокруг некого базового образа: 125 | 126 | ![Docker image](./img/65-docker-image.png) 127 | 128 | Каждая следующая “обертка” добавляет какой-то функционал. И верхняя обертка, как правило, код или сборка нашего ПО. Соответственно контейнер это набор из образа и команды которая запускает определенное ПО из образа и выполняет определенную задачу. Как только команда, запустившая образ завешена - контейнер остановится. Так, в примере с python-образом я запустил контейнер с командой bash и когда интерпретатор завершился - остановился и контейнер. 129 | 130 | Попытка запустить образ без опций (`docker run python:3.8-slim-buster`) в данном случае приводит к тому, что там запускается команда, зашитая в образ (python), интерпретатор без псевдотелетайпа завершается, контейнер останавливается. 131 | 132 | > Лучше вспомнить про tmux сейчас 133 | 134 | Теперь попробуем запустить что-то более осмысленное и не в интерактивном режиме: 135 | 136 | ```bash 137 | sudo docker run python:3.8-slim-buster python3 -m http.server 8000 138 | ``` 139 | 140 | В соседней консоли видим запущенный контейнер: 141 | 142 | ```bash 143 | sudo docker ps 144 | ``` 145 | 146 | ![Docker ps 2](./img/67-docker-ps.png) 147 | 148 | Однако `netstat -nlpt | grep 8000` не покажет ни одного приложения, слушающего 8000 порт. Это из-за изоляции. Контейнер имеет свой собственный сетевое пространство имен. Вот там этот порт слушается. Проверим, подключившись внутрь контейнера с помощью `docker exec`: 149 | 150 | ```bash 151 | sudo docker exec -it 3ee16e5e9686 bash 152 | ``` 153 | 154 | Внутри самый обычный linux, в данном случае debian-дистрибутив. Поставим руками net-tools и проверим 8000 порт: 155 | 156 | ![Docker exec](./img/66-docker-exec.png) 157 | 158 | Слушается. Отлично. Теперь остановим наш контейнер. Это можно сделать нажав `^C` в консоли, где мы его запустили. Либо убив контейнер через docker: 159 | 160 | ```bash 161 | sudo docker stop 3ee16e5e9686 162 | ``` 163 | 164 | Он пошлет сигнал SIGTERM процессу контейнера для завершения. Если тот не успеет завершиться за 10 секунд (настраивается ключом `-t`), то пошлет SIGKILL. 165 | 166 | Но хотелось бы получить к нему доступ из нашей основной системы. Для этого нужно запустить контейнер с ключом `-p` + `-d` флаг для удобства (запустить в фоне, detached): 167 | 168 | ```bash 169 | sudo docker run -dp 8080:8000 python:3.8-slim-buster python3 -m http.server 8000 170 | ``` 171 | 172 | ![Docker proxy](./img/68-docker-proxy.png) 173 | 174 | Посмотрим логи контейнера за последние 120 секунд (пусты): 175 | 176 | ```bash 177 | sudo docker logs --until=120s 51644b8cb1c5 178 | ``` 179 | 180 | Теперь соберем свой образ ([так](https://docs.docker.com/language/python/build-images/)). Для этого нам потребуется приложение (`app.py` + `requirements.txt`) и Dockerfile. 181 | 182 | Файл `app.py` (вообще-то любое приложение): 183 | 184 | ```python 185 | from flask import Flask 186 | app = Flask(__name__) 187 | 188 | @app.route('/') 189 | def hello_world(): 190 | return 'Hello, Docker!' 191 | ``` 192 | 193 | Файл ``requirements.txt``, можно сгенерировать `pip3 freeze > requirements.txt`: 194 | 195 | ``` 196 | Flask==2.1.2 197 | ``` 198 | 199 | Файл `Dockerfile`: 200 | 201 | ```dockerfile 202 | # syntax=docker/dockerfile:1 203 | 204 | FROM python:3.8-slim-buster 205 | 206 | WORKDIR /app 207 | 208 | COPY requirements.txt requirements.txt 209 | RUN pip3 install -r requirements.txt 210 | 211 | COPY . . 212 | 213 | CMD [ "python3", "-m" , "flask", "run", "--host=0.0.0.0"] 214 | ``` 215 | 216 | Собираем: 217 | 218 | ```bash 219 | sudo docker build --tag my-super-app . 220 | ``` 221 | 222 | ![Docker build](./img/69-docker-build.png) 223 | 224 | ![Docker my image](./img/70-docker-mysuperapp.png) 225 | 226 | Образа можно тэгать (именовать): 227 | 228 | ```bash 229 | sudo docker tag my-super-app:latest my-super-app:v0.1.0 230 | ``` 231 | 232 | Можно удалять: 233 | 234 | ```bash 235 | sudo docker rmi my-super-app:v0.1.0 236 | ``` 237 | 238 | ![Docker tag](./img/71-docker-tag.png) 239 | 240 | Теперь запустим наш образ (можно добавить `--name ИМЯ` чтобы выбрать имя, иначе будет сгенерированное, в примере - great_turing): 241 | 242 | ```bash 243 | sudo docker run -dp 8080:5000 my-super-app 244 | ``` 245 | 246 | ![Docker run own](./img/72-docker-run.png) 247 | 248 | 249 | 250 | Создадим сеть: 251 | 252 | ```bash 253 | sudo docker network create mysqlnet 254 | ``` 255 | 256 | Запустим в контейнере с отдельным диском MySQL: 257 | 258 | ```bash 259 | sudo docker run --rm -d -v mysql:/var/lib/mysql \ 260 | -v mysql_config:/etc/mysql -p 3306:3306 \ 261 | --network mysqlnet \ 262 | --name mysqldb \ 263 | -e MYSQL_ROOT_PASSWORD=p@ssw0rd1 \ 264 | mysql 265 | ``` 266 | 267 | Подключимся к базе для проверки: 268 | 269 | ```bash 270 | sudo docker exec -ti mysqldb mysql -u root -p 271 | ``` 272 | 273 | ![MySQL](./img/73-mysql.png) 274 | 275 | 276 | 277 | Создадим более сложное приложение: 278 | 279 | ```python 280 | import mysql.connector 281 | import json 282 | from flask import Flask 283 | 284 | app = Flask(__name__) 285 | 286 | db_host = "my-super-storage" 287 | db_user = "root" 288 | db_pass = "p@ssw0rd1" 289 | db_name = "inventory" 290 | 291 | @app.route('/') 292 | def hello_world(): 293 | return 'Hello, Docker!' 294 | 295 | @app.route('/widgets') 296 | def get_widgets(): 297 | mydb = mysql.connector.connect( 298 | host=db_host, 299 | user=db_user, 300 | password=db_pass, 301 | database=db_name 302 | ) 303 | cursor = mydb.cursor() 304 | 305 | 306 | cursor.execute("SELECT * FROM widgets") 307 | 308 | row_headers=[x[0] for x in cursor.description] #this will extract row headers 309 | 310 | results = cursor.fetchall() 311 | json_data=[] 312 | for result in results: 313 | json_data.append(dict(zip(row_headers,result))) 314 | 315 | cursor.close() 316 | 317 | return json.dumps(json_data) 318 | 319 | @app.route('/initdb') 320 | def db_init(): 321 | mydb = mysql.connector.connect( 322 | host=db_host, 323 | user=db_user, 324 | password=db_pass 325 | ) 326 | cursor = mydb.cursor() 327 | 328 | cursor.execute("DROP DATABASE IF EXISTS inventory") 329 | cursor.execute("CREATE DATABASE inventory") 330 | cursor.close() 331 | 332 | mydb = mysql.connector.connect( 333 | host=db_host, 334 | user=db_user, 335 | password=db_pass, 336 | database=db_name 337 | ) 338 | cursor = mydb.cursor() 339 | 340 | cursor.execute("DROP TABLE IF EXISTS widgets") 341 | cursor.execute("CREATE TABLE widgets (name VARCHAR(255), description VARCHAR(255))") 342 | cursor.close() 343 | 344 | return 'init database' 345 | 346 | if __name__ == "__main__": 347 | app.run(host ='0.0.0.0') 348 | ``` 349 | 350 | Не забудем обновить зависимости: 351 | 352 | ```bash 353 | pip3 install mysql-connector-python 354 | pip3 freeze | grep mysql-connector-python >> requirements.txt 355 | ``` 356 | 357 | Еще раз соберем образ: 358 | 359 | > Более правильно будет использовать `docker buildx` для сборки под [мультиплатформу](https://www.docker.com/blog/multi-arch-images/) 360 | 361 | ```bash 362 | sudo docker build --tag my-super-app . 363 | ``` 364 | 365 | Запустим: 366 | 367 | ```bash 368 | sudo docker run \ 369 | --rm -d \ 370 | --network mysqlnet \ 371 | --name rest-server \ 372 | -p 8080:5000 \ 373 | my-super-app 374 | ``` 375 | 376 | Проверим: 377 | 378 | ```bash 379 | curl http://localhost:8080/initdb 380 | curl http://localhost:8080/widgets 381 | ``` 382 | 383 | Можно узнать, что изменил локально конкретный контейнер от образа: 384 | 385 | ```bash 386 | sudo docker diff rest-server 387 | ``` 388 | 389 | Остановим все принудительно: 390 | 391 | ```bash 392 | sudo docker kill $(sudo docker ps -q) 393 | ``` 394 | 395 | 396 | 397 | Не очень удобно каждый раз запускать все микросервисы вручную? Есть docker-compose. 398 | 399 | ```bash 400 | sudo apt install docker-compose 401 | ``` 402 | 403 | Файл `docker-compose.yml`: 404 | 405 | ```yaml 406 | version: '3.3' 407 | 408 | services: 409 | my-super-app-api: 410 | build: 411 | context: . 412 | ports: 413 | - 8080:5000 414 | networks: 415 | my_super_net: 416 | 417 | my-super-storage: 418 | image: mysql 419 | ports: 420 | - 3306:3306 421 | environment: 422 | - MYSQL_ROOT_PASSWORD=p@ssw0rd1 423 | volumes: 424 | - mysql:/var/lib/mysql 425 | - mysql_config:/etc/mysql 426 | networks: 427 | my_super_net: 428 | 429 | volumes: 430 | mysql: 431 | mysql_config: 432 | 433 | # У меня просто сеть пересекается с дефолтной 172.X.Y.Z 434 | networks: 435 | my_super_net: 436 | driver: bridge 437 | ipam: 438 | driver: default 439 | config: 440 | - 441 | subnet: 192.168.2.0/24 442 | ``` 443 | 444 | > By default Compose sets up a single [network](https://docs.docker.com/engine/reference/commandline/network_create/) for your app. Each container for a service joins the default network and is both *reachable* by other containers on that network, and *discoverable* by them at a hostname identical to the container name. 445 | 446 | Запустим - он сам соберет и запустит все, что нужно: 447 | 448 | ```bash 449 | sudo docker-compose up -d 450 | 451 | # Или более полно: 452 | sudo docker-compose -f docker-compose.yml up --build -d 453 | ``` 454 | 455 | Остановить: 456 | 457 | ```bash 458 | sudo docker-compose down 459 | ``` 460 | 461 | Удалить и volumes: 462 | 463 | ```bash 464 | sudo docker-compose down -v 465 | ``` 466 | 467 | 468 | 469 | 470 | 471 | ## Часть 2 472 | 473 | Заходим в gitlab, создаем проект, смотрим в CI/CD раздел: 474 | 475 | ![Gitlab](./img/75-gitlab.png) 476 | 477 | Запускаем у себя создание раннера: 478 | 479 | ```bash 480 | sudo docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest 481 | ``` 482 | 483 | Регистрируем: 484 | 485 | ```bash 486 | sudo docker run --rm -it -v gitlab-runner-config:/etc/gitlab-runner gitlab/gitlab-runner:latest register --locked=false 487 | ``` 488 | 489 | Ждем, пока на странице раннеров он станет активен. 490 | 491 | 492 | 493 | Пушим в репозиторий .gitlab-ci.yml: 494 | 495 | ```yaml 496 | stages: 497 | - test 498 | - build 499 | - upload 500 | 501 | run tests: 502 | image: python:3.8-slim-buster 503 | stage: test 504 | script: 505 | - python --version 506 | - echo "DONE!" 507 | 508 | build image: 509 | image: docker:latest 510 | stage: build 511 | services: 512 | - docker:dind 513 | before_script: 514 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 515 | script: 516 | - docker build --pull -t $CI_REGISTRY_IMAGE . 517 | - echo $CI_REGISTRY_IMAGE 518 | only: 519 | - master 520 | 521 | upload image: 522 | image: docker:latest 523 | stage: upload 524 | services: 525 | - docker:dind 526 | when: manual 527 | before_script: 528 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 529 | script: 530 | - docker build --pull -t $CI_REGISTRY_IMAGE . 531 | - echo $CI_REGISTRY_IMAGE 532 | - docker push $CI_REGISTRY_IMAGE 533 | only: 534 | - master 535 | ``` 536 | 537 | ![Gitlab](./img/76-gitlab.png) 538 | 539 | После выполнения ручного этапа должны увидеть в registry свой образ: 540 | 541 | ![Gitlab](./img/77-gitlab.png) 542 | 543 | 544 | ## Этапы жизненного цикла ПО (ЖЦ): 545 | Типичные пайплайны на работе: 546 | 1. test 547 | - тестирование кода 548 | - протестированный код передается далее как [артефакт](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html) 549 | 2. build rpm 550 | - сборка rpm-пакетов. В debian это deb. В Ubuntu - apt 551 | - собирается docker-образ, передается дальше через registry 552 | 3. rsync rpm to repo-server 553 | - ручная фаза копирования изменений на *development* или *production*-сервер 554 | - Протокол scp. Scp это cp over ssh 555 | 556 | [Этапы ЖЦ](https://docs.gitlab.com/ee/ci/yaml/#stages), выделяемые гитлаб. 557 | 558 | 559 | 560 | ## Контрольные вопросы 561 | 562 | 1. Что такое виртуализация, контейнерная виртуализация? 563 | 2. Как собрать образ контейнера? 564 | 3. Я запустил контейнер, а в списке запущенных его нет, что делать? 565 | 4. Как обратиться в контейнер на 80-ый порт с хостовой ОС? 566 | 5. Может ли контейнер запуститься с командой, отличной от указанной в dockerfile 567 | 6. Как аутентифицироваться в приватном registry? 568 | 569 | 570 | 571 | 572 | # Extra задание с двумя звездочками 573 | 574 | Материал разработан при содействии [студентов](https://gitlab.com/Denactive/education_devops_gitlabci). 575 | 576 | ## Содержание 577 | + Тестирование кода 578 | + Замер покрытия 579 | + Отображение тестов в pipeline 580 | + Артефакты 581 | + Дополнительно к дополнительному 582 | 583 | ## Тестирование кода 584 | До сих пор пайплайн ничего не проверял. Напишем новый скрипт и код к нему. 585 | 586 | Создадим папочки: 587 | 588 | ``` 589 | root 590 | | 591 | + mylib/ -| 592 | | + src/ lib.py 593 | | + test/test.py 594 | | 595 | + __init__.py 596 | + .gitignore 597 | ``` 598 | 599 | `mylib/src/lib.py` 600 | ```python 601 | def merge(*arrays): 602 | ''' 603 | Эта функция объединяет элементы массивы вида list> 604 | в словарь dict. Элементы будут расположены в том порядке, в они котором идут 605 | в исходных массивах, при этом на каждом шаге из первых элементов списков 606 | выбирается минимальный 607 | ''' 608 | arrays = list(filter(lambda array: len(array), arrays)) 609 | positions = [0] * len(arrays) 610 | res = {} 611 | while sum(positions) != -len(arrays): 612 | column = [arrays[i][pos] for i, pos in enumerate(positions)] 613 | minimum = column[0][0] 614 | minimum_idxs = [] 615 | minimum_num = 0 616 | for i, (el, num) in enumerate(column): 617 | if el < minimum: 618 | minimum = el 619 | minimum_num = num 620 | minimum_idxs = [i] 621 | elif el == minimum: 622 | minimum_num = minimum_num + num 623 | minimum_idxs.append(i) 624 | 625 | res[minimum] = (0 if not minimum in res else res[minimum]) + minimum_num 626 | for i in minimum_idxs: 627 | positions[i] = positions[i] + 1 628 | # удаляю с конца, чтобы не было проблем из-за смещения индексов 629 | for i in reversed(minimum_idxs): 630 | if positions[i] == len(arrays[i]): 631 | positions.pop(i) 632 | arrays.pop(i) 633 | return res 634 | 635 | ``` 636 | 637 | `mylib/test/test.py` 638 | ```python 639 | import unittest 640 | 641 | # Чтобы файл с тестами видел новый модуль 642 | import sys 643 | import os 644 | # Получение пути текущей директории 645 | current = os.path.dirname(os.path.realpath(__file__)) 646 | root = os.path.join(current, f'..{os.path.sep}..') 647 | sys.path.append(root) 648 | # Теперь можно импортировать наш модуль 649 | from mylib.src.lib import merge 650 | 651 | cases = [ 652 | { 653 | 'test': [[('hello', 1), ('hello1', 1)]], 654 | 'answer': {'hello': 1, 'hello1': 1}, 655 | }, 656 | { 657 | 'test': [[('hello', 1), ('hello', 1)]], 658 | 'answer': {'hello': 2}, 659 | }, 660 | ] 661 | 662 | class TestWordCount(unittest.TestCase): 663 | # Действия до запуска тестов 664 | def setUp(self): 665 | pass 666 | 667 | # Действия по окончании тестирования 668 | def tearDown(self): 669 | pass 670 | 671 | # Каждый тест должен начинаться с test_ 672 | def test_merge(self): 673 | print('running test_merge...') 674 | for i, case in enumerate(cases): 675 | print('case', i + 1) 676 | self.assertDictEqual(merge(*case['test']), case['answer']) 677 | 678 | if __name__ == "__main__": 679 | unittest.main() 680 | 681 | ``` 682 | 683 | `__init__.py` 684 | ```python 685 | from .src.lib import merge 686 | ``` 687 | 688 | Добавим `.gitignore`, чтобы не спамить мусор 689 | ``` 690 | __pycache__/ 691 | ``` 692 | 693 | Проверяем, что тест работает. 694 | ```bash 695 | python ./mylib/test/test.py 696 | ``` 697 | ``` 698 | Running tests... 699 | ---------------------------------------------------------------------- 700 | running test_merge... 701 | case 1 702 | case 2 703 | ... 704 | ---------------------------------------------------------------------- 705 | Ran 3 tests in 0.002s 706 | 707 | OK 708 | ``` 709 | 710 | Перепишем `.gitlab-ci.yml` 711 | ```yaml 712 | stages: 713 | - test 714 | 715 | 716 | run tests: 717 | image: python:3.9.12-alpine3.15 718 | stage: test 719 | before_script: 720 | script: 721 | - echo "Testing" 722 | - python -m unittest discover ./mylib/test/ 723 | 724 | ``` 725 | 726 | В этом пайплайне используется другой легковесный образ python:3.9.12-alpine3.15 с уже предустановленным питоном 3 и последним pip. Он весит 57 Мб против 900 у полного образа с питоном. 727 | 728 | Команда `python -m unittest discover .\mylib\test\` используется для выполнения всех тестов в модуле. 729 | 730 | Функция нашей библиотеки выполняет часть соединения массивов (step 4) в сортировке слиянием (merge sort): 731 | 732 | ![merge sort](./img/Lab4_Merge-Sort-Algorithm.png) 733 | 734 | ## Замер покрытия 735 | 736 | Добавим замер покрытия кода тестами. 737 | Используем python утилиту [coverage](https://coverage.readthedocs.io/en/6.3.3/). 738 | 739 | ```bash 740 | pip install coverage 741 | ``` 742 | 743 | После установки можно прогонять код вот так: 744 | ```bash 745 | python -m coverage run file.py 746 | ``` 747 | для python-модулей: 748 | ```bash 749 | python -m coverage run -m path.to.module 750 | ``` 751 | Более того, вместе с библиотекой ставится команда `coverage`, аналогичная `python -m coverage` 752 | 753 | Замер покрытия генерирует в корне проекта файл `.coverage`. Добавим его в гитигнор. 754 | 755 | После выполнения замера, можно вывести отчет на экран: 756 | 757 | ```bash 758 | coverage report file.py 759 | ``` 760 | 761 | ``` 762 | PS C:\ДЗ\DevOps\расширение для лаб-4> coverage report 763 | Name Stmts Miss Cover 764 | ---------------------------------------- 765 | mylib\__init__.py 1 0 100% 766 | mylib\src\lib.py 25 3 88% 767 | mylib\test\test.py 29 0 100% 768 | ---------------------------------------- 769 | TOTAL 55 3 95% 770 | 771 | ``` 772 | Однако он может содержать множество ненужных файлов, которые мы не собирались использовать. Чаще всего это различные `__init__.py` или чужие библиотеки. Настроить правила вывода отчета, игнорирование файлов и отдельных строк, путь, куда нужно положить отчет и многое другое можно в конфиге .coveragerc. Создадим его: 773 | 774 | `.coveragerc` 775 | ```toml 776 | [run] 777 | relative_files = True 778 | branch = True 779 | 780 | [report] 781 | omit = 782 | ./wordcounter/test/progress_bar.py 783 | ./wordcounter/test/cases_test_decorator.py 784 | ./wordcounter/test/test_cases/* 785 | ./**/__init__.py 786 | 787 | exclude_lines = 788 | if __name__ == "__main__": 789 | unittest.main() 790 | pragma: no cover 791 | 792 | show_missing = True 793 | skip_empty = True 794 | fail_under=90 795 | 796 | ``` 797 | 798 | Все его поля - это соответствующие опции команды coverage (см --help), только дефисы заменены нижними подчеркиваниями. Опция fail_under очень полезна для пайплайнов. Пайплайн не пройдет, если покрытие не будет достигать 90%. Запускаем `coverage run mylib/test/test.py`, `coverage report` 799 | 800 | ``` 801 | PS C:\ДЗ\DevOps\расширение для лаб-4> coverage report 802 | Name Stmts Miss Branch BrPart Cover Missing 803 | ---------------------------------------------------------------- 804 | mylib\src\lib.py 25 3 18 2 88% 18-20, 21->16 805 | mylib\test\test.py 26 0 2 0 100% 806 | ---------------------------------------------------------------- 807 | TOTAL 51 3 20 2 93% 808 | ``` 809 | Появилась дополнительная информация. 810 | 811 | Отчеты так же можно выводить в .json, .xml и .html форматы. 812 | 813 | Отправим все это на гитлаб. Перепишем `.gitlab-ci.yml` 814 | ```yaml 815 | stages: 816 | - test 817 | 818 | 819 | run tests: 820 | image: python:3.9.12-alpine3.15 821 | stage: test 822 | before_script: 823 | - python -m pip install coverage 824 | script: 825 | - echo "Testing & Coverage" 826 | - python -m coverage run ./mylib/test/test.py 827 | - python -m coverage report 828 | 829 | ``` 830 | На гитлаб увидим следующее: 831 | 832 | ![pipeline-coverage](./img/Lab4-pipeline-cov.png) 833 | 834 | 835 | 836 | ## Отображение тестов в pipeline 837 | В гитлаб есть поддержка тестов. Во время прохождения пайплайна, видно сколько тестов было пройдено, и какой текст выводился во время выполнения каждого из них. [Подробно с примерами для разных языков](https://docs.gitlab.com/ee/ci/unit_test_reports.html). Для этого нужно оформить отчет в виде .xml в формате [Jenkins JUnit](https://plugins.jenkins.io/junit/), и положить его в **корень!** как артефакт. 838 | 839 | [Аналогично для покрытия](https://docs.gitlab.com/runner/development/reviewing-gitlab-runner.html#reviewing-tests-coverage-reports), но пока покрытие показывается только в UI merge-request. 840 | 841 | Используемый нами фреймворк unittest не поддерживает junit формат (сейчас можете сохранить где-нибудь отчет `coverage xml`), поэтому для него есть расширение. 842 | ```bash 843 | pip install xmlrunner 844 | ``` 845 | Существуют аналогичные расширения для [других языков](https://stackoverflow.com/questions/11241781 python-unittests-in-jenkins). 846 | 847 | изменим `mylib/test/test.py`: 848 | 849 | в начале: 850 | ```python 851 | import unittest 852 | import xmlrunner 853 | 854 | ``` 855 | 856 | Допишем еще бесполезных тестов: 857 | ```python 858 | def test_useless_1(self): 859 | print('running test_useless_1...') 860 | self.assertFalse(10 < 4) 861 | 862 | def test_useless_2(self): 863 | print('running test_useless_2...') 864 | self.assertFalse(10 < 4) 865 | 866 | ``` 867 | 868 | Заменим main внизу: 869 | ```python 870 | if __name__ == "__main__": 871 | with open(XML_RESULT_FILE, mode='w', encoding='utf-8') as output: 872 | # да, эта штука пишет в открытый файл 873 | unittest.main(testRunner=xmlrunner.XMLTestRunner(output=output)) 874 | 875 | ``` 876 | 877 | Про [with](https://pycoder.ru/python-with-statement/) и [этот модуль](https://github.com/xmlrunner/unittest-xml-reporting). 878 | 879 | Добавим установку этого пакета и генерацию артефактов. 880 | ```yaml 881 | stages: 882 | - test 883 | 884 | 885 | run tests: 886 | image: python:3.9.12-alpine3.15 887 | stage: test 888 | before_script: 889 | - python -m pip install xmlrunner 890 | - python -m pip install coverage 891 | script: 892 | - echo "Testing & Coverage" 893 | - python -m coverage run ./mylib/test/test.py 894 | - python -m coverage report 895 | - python -m coverage xml 896 | artifacts: 897 | when: always 898 | reports: 899 | junit: 900 | - report.xml 901 | coverage_report: 902 | coverage_format: cobertura 903 | path: coverage.xml 904 | 905 | ``` 906 | 907 | Если тестировали все это локально перед пушем, что правильно, не забудьте дописать в `.gitignore`: 908 | ``` 909 | __pycache__/ 910 | .coverage 911 | **/report.xml 912 | **/coverage.xml 913 | 914 | ``` 915 | 916 | Что же изменилось? Открываем подробную информацию по пайплайн, вкладка **тесты**: 917 | 918 | ![pipeline-junit](./img/Lab4-pipeline-junit.png) 919 | 920 | Самостоятельно попробуйте сломать какой-нибудь тест и посмотреть на результат. 921 | Попробуйте повысить коэффициент покрытия, необходимый для прохождения, либо добавить код в библиотеку, который не будет выполняться: `if False:`. 922 | 923 | ## Артефакты 924 | [Артефакт](https://docs.gitlab.com/ee/ci/pipelines/job_artifacts.html) - это файл, генерируемый джобой во время выполнения пайплайна. Его можно передавать другим джобам или сохранять в репозитории. 925 | 926 | Передадим артефакт с результатами тестов от одной джобы к джобе, которая отправляет отчеты в какое-нибудь другое место. 927 | 928 | ```yaml 929 | stages: 930 | - test 931 | - report 932 | 933 | 934 | run tests: 935 | image: python:3.9.12-alpine3.15 936 | stage: test 937 | before_script: 938 | - python -m pip install xmlrunner 939 | - python -m pip install coverage 940 | script: 941 | - echo "Testing & Coverage" 942 | - python -m coverage run ./mylib/test/test.py > report.txt 943 | - echo -e "\n---------------\n" >> report.txt 944 | - python -m coverage report >> report.txt 945 | artifacts: 946 | paths: 947 | - report.txt 948 | untracked: true 949 | 950 | send tests: 951 | stage: report 952 | script: 953 | - echo "Sending a report" 954 | - cat report.txt 955 | 956 | ``` 957 | 958 | ![pipeline-artifact-pass1](./img/Lab4-pipeline-artifact-pass1.png) 959 | 960 | Файл доступен внутри джобы с отчетами, иначе она бы упала: 961 | 962 | ![pipeline-artifact-pass2](./img/Lab4-pipeline-artifact-pass2.png) 963 | 964 | **У артефактов не мало особенностей** 965 | 966 | Не найденные артефакты **не ломают джобу**, пайплайн не падает: 967 | 968 | ![job-not-fail-when-no-artifacts](./img/Lab4-build-job-not-fail-when-no-artifacts.png) 969 | 970 | Путь к артефактам может быть указан только **относительно рабочей директории пайплайна**. К сожалению, этого [не написано в доке](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/15530). Путь к рабочей директории можно получить с помощью [предустановленной переменной $CI_PROJECT_DIR](https://docs.gitlab.com/ee/ci/variables/predefined_variables.html): 971 | 972 | ```pwd при старте = echo $CI_PROJECT_DIR при условии, что не задана builds_dir в конфиге раннера``` 973 | 974 | [Более подробно про передачу артефактов между джобами](https://stackoverflow.com/questions/38140996/how-can-i-pass-artifacts-to-another-stage). 975 | 976 | Обратите внимание, **джобы выполняются последовательно**: артефакт, генерируемый одной джобой доступен только следующей. Если нужно, чтобы артефакт использовался спустя несколько джоб, то необходимо указать [dependencies](https://docs.gitlab.com/ee/ci/yaml/#dependencies), чтобы нарушить линейный порядок. 977 | 978 | 979 | ## Дополнительно 980 | ### Когда джоба падает? 981 | Джоба падает, если хотя бы одна строчка скрипта вернула ReturnCode (коротко rc) ($? В баше) не ноль. rc можно подруливать самому, написав условия и обертку над командой. В posix - rc=0 считается однозначным критерием, что программы выполнилась корректно. К сожалению, не все соблюдают posix. 982 | 983 | ### Как передать пароли? 984 | [Передача паролей, токенов и т.п. в GitLab-CI](https://parsiya.net/blog/2021-10-11-modify-gitlab-repositories-from-the-ci-pipeline/#future-work) 985 | 986 | ### Переменные 987 | [Переменные: глобальные и локальные для джобов](https://docs.gitlab.com/ee/ci/variables/). Ниже есть пример. 988 | 989 | ### Передача готовых сборок между джобами 990 | Установить пакеты в одной джобе и и передать их в другую не выйдет. Лучше воспользоваться registry. Докер удобнее тем, что ему не важно, по каким директориям apt-get или pip разбросал файлы. Настройка всего окружения с нуля - крайне сложное занятие ([пример того, как это делается в python:alpine](https://github.com/docker-library/python/blob/f871d0435e7f35a693fa1b60aa159e6a1a4c6a2e/3.9/alpine3.15/Dockerfile)). Докер позволяет всю эту свору команд превратить в одну строчку - image: ... 991 | Передача артефактами удобна, когда нужно пробросить конфиги или отдельные собранные самописные библиотеки. Сделать это можно как-то так: 992 | ```yaml 993 | variables: 994 | PYTHON_PIP_FOLDER: /usr/local/lib/python3.9/site-packages 995 | 996 | build python: 997 | image: python:3.9.12-alpine3.15 998 | stage: build 999 | script: 1000 | - echo "Python building" 1001 | - python -m pip install xmlrunner 1002 | - python -m pip install coverage 1003 | - mkdir libs/ 1004 | - mv $PYTHON_PIP_FOLDER/xmlrunner libs/xmlrunner 1005 | - mv $PYTHON_PIP_FOLDER/coverage libs/coverage 1006 | artifacts: 1007 | paths: 1008 | - libs/ 1009 | untracked: true 1010 | 1011 | run tests: 1012 | image: python:3.9.12-alpine3.15 1013 | stage: test 1014 | dependencies: 1015 | - build python 1016 | before_script: 1017 | - mv libs/* $PYTHON_PIP_FOLDER/ 1018 | script: 1019 | - echo "Testing & Coverage" 1020 | - python -m coverage run ./wordcounter/test/test.py 1021 | - python -m coverage report 1022 | - python -m coverage xml 1023 | artifacts: 1024 | when: always 1025 | reports: 1026 | junit: 1027 | - report.xml 1028 | coverage_report: 1029 | coverage_format: cobertura 1030 | path: coverage.xml 1031 | 1032 | ``` 1033 | Можно было бы сохранить библиотеки в свою директорию, передав ее питону через переменную `$PYTHONPATH`: 1034 | ```yaml 1035 | # в одной джобе получаем библиотеку: 1036 | script: 1037 | - echo "Python building" 1038 | - python -m pip install xmlrunner 1039 | - python -m pip install coverage 1040 | - mkdir libs/ 1041 | - mv $PYTHON_PIP_FOLDER/xmlrunner libs/xmlrunner 1042 | - mv $PYTHON_PIP_FOLDER/coverage libs/coverage 1043 | 1044 | # в другой используем 1045 | script: 1046 | - echo $CI_PROJECT_DIR 1047 | - export PYTHONPATH="$CI_PROJECT_DIR/libs:$PYTHONPATH" 1048 | - echo $PYTHONPATH 1049 | 1050 | ``` 1051 | но coverage будет проходить по этой директории, а философия CI не предполагает изменения исходного кода, в т.ч. конфига `.coveragerc`. 1052 | 1053 | Можно было бы передавать собранный образ докера, минуя registry. 1054 | ```yaml 1055 | # Билдиим образ с опцией --output 1056 | artifacts: 1057 | paths: 1058 | - /var/lib/docker/my_super_app_image 1059 | 1060 | # Либо по хардкору прямо: 1061 | artifacts: 1062 | paths: 1063 | - /var/lib/docker/ 1064 | 1065 | ``` 1066 | 1067 | ### Настройка раннера, передача конфига 1068 | 1069 | Мы пробрасывали volume с конфигом в начале: 1070 | 1071 | ```bash 1072 | docker run -d --name gitlab-runner \ 1073 | --restart always \ 1074 | -v "/gitlab-runner/config:/etc/gitlab-runner" \ 1075 | -v /var/run/docker.sock:/var/run/docker.sock \ 1076 | gitlab/gitlab-runner:latest 1077 | ``` 1078 | Если этот путь существует, то содержимое `gitlab-runner/config` попадет внутрь докера. Туда можно положить файл с названием `config.toml`, согласно [этому](https://docs.gitlab.com/runner/register/#runners-configuration-template-file), [и этому](https://docs.gitlab.com/runner/configuration/advanced-configuration.html) разделам документации: 1079 | 1080 | ```yaml 1081 | [[runners]] 1082 | executor = "docker" 1083 | 1084 | [runners.docker] 1085 | pull_policy = ["if-not-present"] 1086 | ``` 1087 | 1088 | Тут мы кешируем любой образ, указанный в `.gitlab-ci.yml`. Это не ускорит сборку, но ускорит реакцию раннера на новые коммиты. Тесты начнут запускаться мгновенно, т.к. время на скачивание образа больше не требуется. Можно указать образы, которые будут развернуты в раннере постоянно. 1089 | 1090 | 1091 | На Windows аналогично, только путь другой: 1092 | docker run -d --name gitlab-runner --restart always -v :/etc/gitlab-runner" -v /var/run/docker.sock:/var/run/docker.sock gitlab/gitlab-runner:latest 1093 | ```bash 1094 | docker run -d --name gitlab-runner \ 1095 | --restart always \ 1096 | -v "C:\Users\...\gitlab-runner\config:/etc/gitlab-runner" \ 1097 | -v /var/run/docker.sock:/var/run/docker.sock \ 1098 | gitlab/gitlab-runner:latest 1099 | ``` 1100 | 1101 | Обязательно изменяем права доступа к этой директории. 1102 | 1103 | Linux 1104 | ```bash 1105 | chmod 444 /gitlab-runner/config 1106 | ``` 1107 | 1108 | Windows. Можно также забрать права просмотр списка файлов. Будьте готовы, что IDE будет считать папку пустой, а скопировать ее будет нельзя до восстановления прав. **Пользователя СИСТЕМА не трогаем**. 1109 | 1110 | ![win-runner-security](./img/Lab4-win-security.png) 1111 | 1112 | 1113 | ### Настройка GitLab-CI без тысячи пушей 1114 | Докер удобен тем, что проверить работу пайплайна можно в локально. 1115 | ```yaml 1116 | FROM python:3.9.12-alpine3.15 1117 | 1118 | WORKDIR /app 1119 | 1120 | RUN python -m pip install xmlrunner 1121 | 1122 | RUN python -m pip install coverage 1123 | 1124 | CMD ["python", "--version"] 1125 | 1126 | ``` 1127 | 1128 | ```bash 1129 | # Запускаем одноразовый контейнер для изучения. Но в этом образе нет bash! 1130 | docker run --rm -it --name ci-test python:3.9.12-alpine3.15 sh 1131 | # изучаем структуру родительского образа изнутри 1132 | docker exec -it <имя контейнера> bash 1133 | # смотрим, куда установился пакет 1134 | docker diff <имя контейнера> 1135 | 1136 | ``` 1137 | 1138 | применительно к этой работе 1139 | - Куда ставится pip? 1140 | ```bash 1141 | pip -V 1142 | ``` 1143 | - Пакеты python, установленные через pip? 1144 | на 1 уровень назад: "pip -V)".. 1145 | 1146 | ![where-are-pip-libs-inside-docker](./img/Lab4-inside-docker-whereis-pip-libs.png) 1147 | -------------------------------------------------------------------------------- /Plan.md: -------------------------------------------------------------------------------- 1 | # Linux & DevOps 2 | 3 | Курс построен на 4х лабораторных работах по 2 академических часа каждая. 4 | 5 | За неделю до каждой лабораторной выдается справочный материал для самостоятельного изучения, содержащий также ссылки для дальнейшего углубленного изучения, какие-то интересные факты. Это позволит одаренным студентам продвигаться в изучении дальше самостоятельно. 6 | 7 | В теории возможно проведение **необязательных** срезов знаний в середине курса и в конце. 8 | 9 | 10 | ## Лабораторная №1. Введение в Linux. 11 | 1) Настройка виртуальной машины, основные команды, bash, пакеты, потоки 12 | - Установить ОС, проделать некоторый набор ручных операций, написать и запустить скрипт автоматизации 13 | 2) Пользователи и права доступа в Linux, лимиты, настройка sshd, генерация ключей, ssh-agent 14 | - Создать пользователя, выдать ему права, ограничить лимиты, настроить ssh-подключение по ключу 15 | 16 | ## Лабораторная №2. Процессы и VFS. 17 | 3) Процессы в Linux, мониторинг и управление системой, systemd, top/atop, dmesg, логи 18 | - initrd 19 | - Создать простейший systemd-unit, запустить набор сервисов, показать список процессов и чем они занимаются 20 | 21 | 4) Файловая подсистема в Linux - что такое VFS, иноды, линки, пощупать ext4, mount, LVM 22 | 23 | - Подключить диск, создать фс, примонтировать, сделать жесткие и мягкие ссылки, создать LVM и растянуть его 24 | 25 | ## Промежуточное тестирование вне цикла лабораторных 26 | 5) Для тех, кто хочет набрать больше баллов и проверить себя 27 | - [Возможно] Срез знаний - зайти на подготовленную заранее виртуалку и рассказать что с ней не так, починить 28 | 29 | ## Лабораторная №3. Сетевая подсистема Linux. 30 | 6) Сетевая подсистема в Linux - ip, net-tools, iptables, ssh-туннели, VNC, curl, nc, telnet, tcpdump 31 | - Поднять Nginx, MySQL и сервис (с помощью готового Ansible), проверить их работу, показать какие порты они слушают 32 | - Показать работу утилит curl, nc, telnet, настроить vnc 33 | - [Возможно] Чатик на nc/telent 34 | - Ограничить доступ к СУБД, поднять ssh-туннель 35 | - Cнять дамп трафика tcpdump-ом 36 | - [Возможно] Показать работу node_exporter, Prometheus и Grafana 37 | 38 | ## Лабораторная №4. DevOps 39 | 7) Docker - зачем нужно, установка, поднять готовый контейнер, сборка своего контейнера 40 | - Установить и запустить Docker, поднять готовый контейнер 41 | - Написать dockerfile и собрать свой docker-образ, запустить контейнер 42 | - [Возможно] запустить свое поделие в готовый kubernetes-кластер 43 | 8) GitLab-CI - зачем нужно, настройка, gitlab-runner, написание своего pipeline 44 | - Регистрация в bmstu.codes, анализ уже готового CI/CD процесса 45 | - [Возможно] Запуск gitlub-runner-а 46 | - Добавить в репозиторий подготовленный заранее .gitlab-ci.yml 47 | - [Возможно] Модифицировать ci/cd pipeline 48 | - Продемонстрировать работу автоматизации развертывания 49 | 50 | ## Итоговое тестирование вне цикла лабораторных 51 | 9) Для тех, кто хочет набрать больше баллов и проверить себя 52 | - [Возможно] CI/CD для своей работы по РИП в качестве выпускного проекта 53 | - [Возможно] Подключить к системе мониторинга 54 | 55 | 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Курс лабораторных работ "Linux & DevOps" 2 | 3 | Экспериментальный курс лабораторных работ "Linux & DevOps" для студентов 3 курса кафедры ИУ5 МГТУ им. Н.Э. Баумана 4 | 5 | ## Основные материалы курса 6 | 7 | [План курса](./Plan.md) 8 | 9 | [Справочные материалы по курсу](./Wiki.md) 10 | 11 | 12 | 13 | Лабораторные работы: 14 | 15 | - [Лабораторная работа №1](./Lab1.md) - срок: 05.03.2022 16 | - [Лабораторная работа №2](./Lab2.md) - срок: 19.03.2022 17 | - [Лабораторная работа №3](./Lab3.md) - срок: 30.05.2022 18 | - [Лабораторная работа №4](./Lab4.md) - срок: 30.05.2022 19 | 20 | 21 | ## FAQ 22 | 23 | #### Можно ли использовать другую систему виртуализации? 24 | 25 | Да, можно. Но на ваш страх и риск. Техническая поддержка студентов будет осуществляться только с VirtualBox. Если вы не в силах будете решить какие-то проблемы виртуализации с другими системами самостоятельно - придется сменить. 26 | 27 | #### Можно ли использовать другой дистрибутив? 28 | 29 | Да, можно. Но на ваш страх и риск. Будут небольшие отличия при работе в разных дистрибутивах - пути конфигурационных файлов, названия пакетов и так далее. Наиболее близким к рассматриваемому на курсе дистрибутиву (Debian) является самый популярный сейчас в среде персональных компьютеров дистрибутив - Ubuntu. Отличия между ними будут минимальны. 30 | 31 | #### Можно ли использовать систему с GUI? 32 | 33 | Да, можно. Но лабораторные работы придется выполнять и демонстрировать исключительно из консоли - эмулятора терминала, виртуального телетайпа или ssh-сессии с хостовой машины. *GUI - зло.* 34 | 35 | #### Можно ли использовать реальную систему, например GNU/Linux установленный второй системой? 36 | 37 | Да, можно. Но это немного затруднит подготовку лабораторных. Нужно заранее уведомить об этом, чтобы преподаватель/принимающий мог подготовить свою виртуальную машину для симуляции различных сбоев. 38 | 39 | Соответственно для отработки работы с ssh придется делать подключение на localhost - 127.0.0.1. 40 | 41 | Размечать не виртуальный диск, а флешку или виртуальный раздел. 42 | 43 | Настраивать подключение по сети нескольких машин. 44 | 45 | #### Как реализовать копирование из консоли VB? 46 | 47 | Для работы копирования общего буфера обмена вроде нужно поставить внутри виртуальной машины набор инструментов virtualbox. [Ответ на форуме - нужно установить vb guest additions](https://superuser.com/questions/42134/how-do-i-enable-the-shared-clipboard-in-virtualbox). 48 | 49 | Но проще сразу настроить подключение по ssh с удобным ssh-клиентом и не мучиться. 50 | 51 | Так, в PuTTY можно вставлять нажатием ПКМ или SHIFT+INSERT. Копировать в буфер - выделяя нужное курсором (сразу после выделения, ничего нажимать дополнительно не нужно). 52 | 53 | Еще один вариант - использовать команду `scp src_user@src_host:/path/to/file /local_dst/path/to/file` (Linux & MacOS) или [WinSCP](https://winscp.net/eng/index.php). 54 | 55 | -------------------------------------------------------------------------------- /Summary.md: -------------------------------------------------------------------------------- 1 | # Универсальный вывод по лабораорной работе 2 | 3 | Были выполнены все задачи по плану данной лабораторной работы. 4 | Соответствующие действия по выполнению задач лабораторной работы были описаны и внесены в настоящий отчет. 5 | Получены соответствующие навыки работы с ОС семейства GNU/Linux: 6 | 7 | - **взять из пояснений в плане** 8 | 9 | -------------------------------------------------------------------------------- /ansible/README.md: -------------------------------------------------------------------------------- 1 | # Пример ansible-репозитория 2 | 3 | ## Список сервисов 4 | Устанавливает следующие сервисы через systemd: 5 | - ntpd 6 | - atop 7 | - node_exporter 8 | 9 | ## Обратите внимание 10 | 11 | Ansible понимает два варианта inventory-файлов - INI и YAML. На мой взгляд формат INI хорош, когда структура групп и серверов не слишком сложна. Второй более объемный получается, зато его легче читать, когда иерархия групп сложная. По функционалу они совпадают. Я привел оба варианта с идентичным содержимым. 12 | 13 | ## Перед установкой 14 | 15 | 1. Убедиться, что inventory.ini файл настроен верно. 16 | 2. Отредактировать `group_vars/all/secret.yml` (vault-pass = `Zelda`): 17 | ```bash 18 | ansible-vault edit group_vars/all/secret.yml 19 | ``` 20 | 21 | ## Как пользоваться 22 | 23 | Проверить все (dry-run): 24 | ```bash 25 | ansible-playbook deploy-stage.yml --diff --check 26 | ``` 27 | 28 | Настроить все: 29 | ```bash 30 | ansible-playbook deploy-stage.yml --diff 31 | ``` 32 | 33 | Настроить все, но только на группе stage: 34 | ```bash 35 | ansible-playbook deploy-stage.yml --limit stage 36 | ``` 37 | 38 | Настроить только atop: 39 | ```bash 40 | ansible-playbook deploy-stage.yml --tags atop 41 | ``` 42 | 43 | Настроить только ntpd: 44 | ```bash 45 | ansible-playbook deploy-stage.yml --tags ntpd 46 | ``` 47 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | roles_path = ./roles 3 | inventory = inventory.ini 4 | remote_user = ansible 5 | ask_vault_pass = True 6 | 7 | [privilege_escalation] 8 | become = True 9 | become_method = sudo 10 | become_ask_pass = False 11 | 12 | [ssh_connection] 13 | ssh_args=-o ControlMaster=auto -o ControlPersist=60s -o ControlPath=/tmp/ansible-ssh-%h-%p-%r -o ForwardAgent=yes 14 | -------------------------------------------------------------------------------- /ansible/deploy-stage.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: all 3 | roles: 4 | - name: atop 5 | tags: 6 | - atop 7 | - monitoring 8 | 9 | - hosts: stage 10 | roles: 11 | - name: ntpd 12 | tags: ntpd 13 | - name: node_exporter 14 | tags: node_exporter 15 | - name: nginx 16 | tags: nginx 17 | -------------------------------------------------------------------------------- /ansible/group_vars/all/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | atop_log_interval: 600 3 | atop_log_generations: 28 4 | 5 | ntpd_servers: 6 | - ntp1.mail.ru 7 | - ntp2.mail.ru 8 | 9 | node_exporter_version: 1.3.1 10 | 11 | nginx_enable_default: no 12 | 13 | # Nginx limits 14 | nginx_conn_limits: 15 | - name: conn_limit_per_ip 16 | field: "$binary_remote_addr" 17 | max_conn: 40 # default value 18 | nginx_rate_limits: 19 | - name: req_limit_per_ip 20 | field: "$binary_remote_addr" 21 | rate: 150r/s 22 | burst: 50 # default value 23 | 24 | # Get only names: nginx_sites | map(attribute='name') | flatten 25 | nginx_sites: 26 | - name: mysite # default site (0.0.0.0) 27 | server_name: enterprise.vm 28 | addr: 0.0.0.0 29 | static_root: "/var/www/mysite/" 30 | static_path: / 31 | static_hard: yes 32 | basic_auth: yes 33 | hosts: all 34 | ssl: no 35 | redirect: no 36 | whitelist: 37 | - external 38 | 39 | - name: node_exporter 40 | server_name: node.enterprise.vm 41 | addr: "{{ ansible_default_ipv4['address'] }}" 42 | local: yes 43 | port: 9100 44 | hosts: all 45 | ssl: no 46 | redirect: no 47 | -------------------------------------------------------------------------------- /ansible/group_vars/all/secret.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 61306235393132323739363631393538386163363431636363656431323439386339393037633662 3 | 6135316634366138663937356366373662616666353338340a616462666135316630376139363938 4 | 39373261353635663338336536326137336637623437306639613461613333383733313330643730 5 | 3632383636643961620a316262323930643964303338343134336365633534303236396236626261 6 | 35396461643465633130353134366339353534316162323132326639666663633030396261623037 7 | 62306362326337316230633539393230353336633361333035653530613131343330333964336661 8 | 31656362316632346666663032353362323738643434303938633638656630383839626637346539 9 | 61333336623332346262373662646236666261613935643338643930303163656635353832303464 10 | 61383438636634636262636263656536633339376335333739656464396335396134636632313732 11 | 66316139326333346532313361326165633033393335316633623664306230353936353332383866 12 | 65663963653966613332323135343063336238326139633862393530623531356231323238663931 13 | 63613435626362366437373965386530313731613663313038346636396536626464653762353166 14 | 31366333366531636664653135623439626237393463373663383132346261626363326136633565 15 | 62393561306164626664303737323130613131633330626131343836386532356632386262396238 16 | 62343965333038373539366165643663646439336336333831383361656531386633356133326434 17 | 38373335366661313565626333333264353963636532343638393737306135323430303563373435 18 | 6239 19 | -------------------------------------------------------------------------------- /ansible/host_vars/enterprise: -------------------------------------------------------------------------------- 1 | # Here is host-specific variables 2 | proc_version: amd64 3 | -------------------------------------------------------------------------------- /ansible/inventory.ini: -------------------------------------------------------------------------------- 1 | [stage] 2 | enterprise.vm ansible_host=127.0.0.1 ansible_port=22 3 | 4 | [stage:vars] 5 | env=stage 6 | 7 | [web:children] 8 | stage 9 | #prod 10 | -------------------------------------------------------------------------------- /ansible/inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | # Группа web 4 | web: 5 | children: 6 | # Группа stage 7 | stage: 8 | vars: 9 | env: stage 10 | hosts: 11 | ansible_host: 127.0.0.1 12 | ansible_port: 22 13 | # Потенциальная группа prod 14 | # prod: ... 15 | -------------------------------------------------------------------------------- /ansible/roles/atop/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | atop_log_interval: 600 3 | atop_log_generations: 28 4 | -------------------------------------------------------------------------------- /ansible/roles/atop/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # For educational purposes only 3 | # Using "listen" can call multiple handlers at once 4 | # Preferred: use daemon_reload in the "Restart" handler instead 5 | - name: reload systemd 6 | command: systemctl daemon-reload 7 | listen: atop_restart 8 | 9 | - name: restart atop 10 | service: 11 | name: atop 12 | state: restarted 13 | #daemon_reload: yes 14 | listen: atop_restart 15 | -------------------------------------------------------------------------------- /ansible/roles/atop/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install packages 3 | package: 4 | name: atop 5 | state: present 6 | 7 | - lineinfile: 8 | path: /usr/share/atop/atop.daily 9 | regexp: '^LOGINTERVAL=[0-9]*(.*)$' 10 | line: 'LOGINTERVAL={{ atop_log_interval }}' 11 | notify: atop_restart 12 | 13 | - lineinfile: 14 | path: /usr/share/atop/atop.daily 15 | regexp: '^LOGGENERATIONS=[0-9]*(.*)$' 16 | line: 'LOGGENERATIONS={{ atop_log_generations }}' 17 | notify: atop_restart 18 | 19 | - meta: flush_handlers 20 | 21 | - name: Start and enable atop 22 | systemd: 23 | name: atop 24 | state: started 25 | enabled: yes 26 | -------------------------------------------------------------------------------- /ansible/roles/nginx/defaults/main.yml: -------------------------------------------------------------------------------- 1 | userId: nginx 2 | groupId: nginx 3 | 4 | default_cert: /etc/nginx/certs/fullchain.pem 5 | default_cert_key: /etc/nginx/certs/privkey.pem 6 | 7 | external_blacklist: [] 8 | -------------------------------------------------------------------------------- /ansible/roles/nginx/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: nginx_reload 3 | systemd: 4 | name: nginx 5 | state: reloaded 6 | 7 | - name: nginx_restart 8 | systemd: 9 | name: nginx 10 | state: restarted 11 | -------------------------------------------------------------------------------- /ansible/roles/nginx/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install nginx 3 | package: 4 | name: "{{ item }}" 5 | state: latest 6 | with_items: 7 | - nginx 8 | - python3-passlib # htpasswd module 9 | 10 | - name: Creating nginx user group 11 | group: name="{{groupId}}" 12 | 13 | - name: Creating nginx user 14 | user: 15 | name: "{{userId}}" 16 | group: "{{groupId}}" 17 | system: yes 18 | shell: "/sbin/nologin" 19 | comment: "{{userId}} nologin User" 20 | createhome: "no" 21 | state: present 22 | 23 | - name: Configure nginx 24 | template: 25 | src: nginx.conf.j2 26 | dest: /etc/nginx/nginx.conf 27 | backup: yes 28 | notify: 29 | - nginx_reload 30 | 31 | - name: Create paths for configs 32 | file: 33 | path: "{{ item }}" 34 | state: directory 35 | owner: root 36 | group: root 37 | mode: 0775 38 | with_items: 39 | - /etc/nginx/acl 40 | - /etc/nginx/conf.d 41 | - /etc/nginx/sites-available 42 | - /etc/nginx/sites-enabled 43 | - /etc/nginx/certs 44 | - /var/www/mysite/ # Just for fun 45 | 46 | - name: Configure mysite page # Just for fun 47 | template: 48 | src: mysite.html.j2 49 | dest: /var/www/mysite/index.html 50 | mode: 0644 51 | 52 | - name: Configure htpasswd files 53 | htpasswd: 54 | path: "{{ item.0.name }}" 55 | name: "{{ item.1.name }}" 56 | password: "{{ item.1.pass }}" 57 | owner: root 58 | group: "{{ groupId }}" 59 | mode: 0640 60 | with_subelements: 61 | - "{{ htpasswds }}" 62 | - users 63 | notify: nginx_reload 64 | 65 | - name: Configure nginx default site 66 | template: 67 | src: default.conf.j2 68 | dest: /etc/nginx/sites-available/default 69 | mode: 0644 70 | notify: nginx_reload 71 | 72 | - name: Configure nginx whitelist/blacklist 73 | template: 74 | src: "{{ item }}" 75 | dest: "/etc/nginx/acl/{{ item | basename | regex_replace('\\.j2$', '') }}" 76 | mode: 0644 77 | with_fileglob: 78 | - ../templates/acl/*.j2 79 | notify: nginx_restart 80 | 81 | - name: Configure nginx main sites 82 | template: 83 | src: nginx-site.conf.j2 84 | dest: /etc/nginx/sites-available/{{ item.name }} 85 | with_items: "{{ nginx_sites }}" 86 | notify: nginx_reload 87 | 88 | - name: Create a symbolic link in apps-enabled 89 | file: 90 | src: /etc/nginx/sites-available/{{ item.name }} 91 | dest: /etc/nginx/sites-enabled/{{ item.name }} 92 | owner: root 93 | group: root 94 | mode: 0644 95 | state: link 96 | with_items: "{{ nginx_sites }}" 97 | when: inventory_hostname in groups[item.hosts | default(web_group)] 98 | notify: nginx_reload 99 | 100 | - name: Find all enabled sites 101 | shell: ls -1 /etc/nginx/sites-enabled/ 102 | register: all_contents 103 | changed_when: False 104 | check_mode: no 105 | 106 | - name: Aggregate valid sites 107 | set_fact: 108 | valid: "{{ valid | default(['default']) + [item.name] }}" 109 | with_items: "{{ nginx_sites }}" 110 | check_mode: no 111 | when: inventory_hostname in groups[item.hosts | default(web_group)] 112 | 113 | - name: Disable wrong sites 114 | file: 115 | dest: /etc/nginx/sites-enabled/{{ item }} 116 | state: absent 117 | with_items: "{{ all_contents['stdout_lines'] | difference(valid) }}" 118 | when: not ansible_check_mode 119 | notify: nginx_reload 120 | 121 | - name: Disable nginx default site 122 | file: 123 | dest: /etc/nginx/sites-enabled/default 124 | state: absent 125 | when: not nginx_enable_default | default(false) | bool 126 | notify: nginx_reload 127 | 128 | - name: Enable nginx default site 129 | file: 130 | src: /etc/nginx/sites-available/default 131 | dest: /etc/nginx/sites-enabled/default 132 | owner: root 133 | group: root 134 | mode: 0644 135 | state: link 136 | when: nginx_enable_default | default(false) | bool 137 | notify: nginx_reload 138 | 139 | - meta: flush_handlers 140 | 141 | - name: Start & enable nginx 142 | systemd: 143 | name: nginx 144 | state: started 145 | enabled: yes 146 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/acl/blacklist.conf.j2: -------------------------------------------------------------------------------- 1 | {% for cidr in external_blacklist %} 2 | deny {{ cidr }}; 3 | {% endfor %} 4 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/acl/external.conf.j2: -------------------------------------------------------------------------------- 1 | allow 0.0.0.0/0; 2 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/acl/main.conf.j2: -------------------------------------------------------------------------------- 1 | allow 127.0.0.1/32; # Local loopback 2 | allow 172.0.1.1/32; # Local loopback 3 | allow 10.0.2.0/24; # LAN 4 | deny all; 5 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/default.conf.j2: -------------------------------------------------------------------------------- 1 | server { 2 | listen 0.0.0.0:80 default_server; 3 | listen 0.0.0.0:443 default_server; 4 | 5 | access_log /var/log/nginx/default.access.log; 6 | error_log /var/log/nginx/default.error.log; 7 | 8 | location / { 9 | return 444; 10 | } 11 | 12 | # For nginx_exporter 13 | location /stub_status { 14 | stub_status on; 15 | access_log off; 16 | allow 127.0.0.1; 17 | deny all; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/mysite.html.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 64 | 65 | 66 |
67 | 68 |
69 | Hello from mysite? 70 |
71 |
72 | 73 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/nginx-site.conf.j2: -------------------------------------------------------------------------------- 1 | {% if item.port is defined %} 2 | upstream {{ item.name }} { 3 | {% if item.local | default(false) | bool %} 4 | server 127.0.0.1:{{ item.port }}; 5 | {% else %} 6 | {% for host in groups[item.group] %} 7 | server {{ lookup('dig', host + '.i') }}:{{ item.port }}; 8 | {% endfor %} 9 | {% endif %} 10 | } 11 | {% endif %} 12 | 13 | {% if item.server_name | default(false) %} 14 | {% if item.redirect | default(true) %} 15 | server { 16 | listen {{ item.addr | default("0.0.0.0") }}:{{ item.http_port | default(80) }}{% if item.default | default(false) | bool %} default_server{% endif %}; 17 | server_name {{ item.server_name }}; 18 | 19 | include /etc/nginx/acl/blacklist.conf; 20 | {% for whitelist_name in item.whitelist|default([]) %} 21 | include /etc/nginx/acl/{{ whitelist_name }}.conf; 22 | {% endfor %} 23 | include /etc/nginx/acl/main.conf; 24 | 25 | {% for limit in nginx_rate_limits | default([]) %} 26 | limit_req zone={{ limit.name }}{% if limit.burst is defined %} burst={{ limit.burst }}{% endif %}{% if limit.nodelay | default(false) | bool %} nodelay{% endif %}; 27 | {% endfor %} 28 | 29 | location / { 30 | return 301 https://$host$request_uri; 31 | } 32 | } 33 | 34 | {% endif %} 35 | server { 36 | {% if not item.redirect | default(true) %} 37 | listen {{ item.addr | default("0.0.0.0") }}:{{ item.http_port | default(80) }}{% if item.default | default(false) | bool %} default_server{% endif %}; 38 | {% endif %} 39 | {% if item.ssl | default(true) | bool %} 40 | listen {{ item.addr | default("0.0.0.0") }}:{{ item.https_port | default(443) }} ssl{% if item.default | default(false) | bool %} default_server{% endif %}; 41 | 42 | ssl_certificate {{ item.ssl_certificate | default(default_cert) }}; 43 | ssl_certificate_key {{ item.ssl_certificate_key | default(default_cert_key) }}; 44 | {% endif %} 45 | 46 | include /etc/nginx/acl/blacklist.conf; 47 | {% for whitelist_name in item.whitelist|default([]) %} 48 | include /etc/nginx/acl/{{ whitelist_name }}.conf; 49 | {% endfor %} 50 | include /etc/nginx/acl/main.conf; 51 | 52 | {% for limit in item.conn_limits | default( nginx_conn_limits | default([]) ) %} 53 | limit_conn {{ limit.name }} {{ limit.max_conn }}; 54 | {% endfor %} 55 | {% for limit in item.rate_limits | default( nginx_rate_limits | default([]) )%} 56 | limit_req zone={{ limit.name }}{% if limit.burst is defined %} burst={{ limit.burst }}{% endif %}{% if limit.nodelay | default(false) | bool %} nodelay{% endif %}; 57 | {% endfor %} 58 | 59 | {% if item.basic_auth | default(false) | bool %} 60 | auth_basic "Restricted Area"; 61 | auth_basic_user_file {{ item.basic_auth_file | default('/etc/nginx/.htpasswd') }}; 62 | 63 | {% endif %} 64 | access_log /var/log/nginx/{{ item.name }}.access.log main; 65 | error_log /var/log/nginx/{{ item.name }}.error.log; 66 | 67 | server_name {{ item.server_name }}; 68 | 69 | client_max_body_size {{ item.client_max_body_size | default('100M') }}; 70 | 71 | location ~ /\. { 72 | deny all; 73 | } 74 | 75 | {% if item.port is defined %} 76 | location {{ item.path | default('/') }} { 77 | proxy_pass http://{{ item.name }}{% if item.add_path | default(false) %}{{ item.add_path }}{% endif %}; 78 | proxy_http_version 1.1; 79 | proxy_set_header Host $http_host; 80 | proxy_set_header Upgrade $http_upgrade; 81 | proxy_set_header Connection $connection_upgrade; 82 | proxy_set_header X-Real-IP $remote_addr; 83 | proxy_set_header X-Forwarded-Proto $scheme; 84 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 85 | } 86 | {% endif %} 87 | 88 | {% if item.static_root | default(false) %} 89 | location {{ item.static_path | default('static') }} { 90 | add_header Cache-Control "public, max-age=31536000"; 91 | {% if item.static_hard | default(false) | bool %}root {% else %}alias{% endif %} {{ item.static_root }}; 92 | index {{ item.static_index | default('index.html') }}; 93 | {% if item.ignore_not_found | default(false) | bool %} 94 | try_files $uri $uri/ {{ item.not_found | default('/index.html') }} =404; 95 | {% else %} 96 | try_files $uri $uri/ =404; 97 | {% endif %} 98 | } 99 | {% endif %} 100 | } 101 | {% endif %} 102 | -------------------------------------------------------------------------------- /ansible/roles/nginx/templates/nginx.conf.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | 3 | user {{ userId }} {{ groupId }}; 4 | 5 | worker_processes auto; 6 | worker_cpu_affinity auto; 7 | worker_rlimit_nofile 20000; 8 | 9 | pcre_jit on; 10 | 11 | error_log /var/log/nginx/error.log error; 12 | pid /var/run/nginx.pid; 13 | 14 | events { 15 | worker_connections 768; 16 | } 17 | 18 | http { 19 | include /etc/nginx/mime.types; 20 | default_type application/octet-stream; 21 | 22 | log_format main '$remote_addr $host $remote_user [$time_local] "$request" ' 23 | '$status $body_bytes_sent "$http_referer" ' 24 | '"$http_user_agent" "$http_x_forwarded_for" ' 25 | '$request_length $request_time [$upstream_cache_status] ' 26 | '[$upstream_addr $upstream_response_time $upstream_status] ' 27 | '$content_length'; 28 | access_log /var/log/nginx/access.log main; 29 | 30 | sendfile on; 31 | tcp_nopush on; 32 | 33 | keepalive_timeout 65; 34 | keepalive_requests 100; 35 | 36 | gzip on; 37 | gzip_disable "msie6"; 38 | gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript application/pdf image/svg+xml; 39 | 40 | map $http_upgrade $connection_upgrade { 41 | default upgrade; 42 | '' close; 43 | } 44 | 45 | server_names_hash_bucket_size 128; 46 | 47 | proxy_buffer_size 64k; 48 | proxy_buffers 8 64k; 49 | 50 | proxy_connect_timeout 5s; 51 | proxy_read_timeout 30m; 52 | proxy_send_timeout 30m; 53 | 54 | {% for limit in nginx_conn_limits %} 55 | limit_conn_zone {{ limit.field }} zone={{ limit.name }}:{{ limit.size | default('10m') }}; 56 | {% endfor %} 57 | {% for limit in nginx_rate_limits %} 58 | limit_req_zone {{ limit.field }} zone={{ limit.name }}:{{ limit.size | default('10m') }} rate={{ limit.rate }}; 59 | {% endfor %} 60 | 61 | ssl_prefer_server_ciphers on; 62 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; 63 | ssl_session_cache shared:SSL:10m; 64 | ssl_session_tickets on; 65 | ssl_session_timeout 24h; 66 | ssl_buffer_size 16k; 67 | 68 | include /etc/nginx/conf.d/*.conf; 69 | include /etc/nginx/sites-enabled/*; 70 | } 71 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | serviceName: node_exporter 3 | userId: node_exporter 4 | groupId: node_exporter 5 | 6 | node_exporter_version: 1.0.1 7 | node_exporter_pack_name: node_exporter-{{ node_exporter_version }}.linux-{{ proc_version }} 8 | node_exporter_pack_ext: tar.gz 9 | node_exporter_download_link: https://github.com/prometheus/node_exporter/releases/download/v{{ node_exporter_version }}/{{ node_exporter_pack_name }}.{{ node_exporter_pack_ext }} 10 | 11 | node_exporter_bin_dir: /usr/local/bin 12 | exec_command: "{{ node_exporter_bin_dir }}/node_exporter --collector.systemd --collector.filesystem.ignored-mount-points=^/(sys|proc|dev|run)($|/)" 13 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart node_exporter 3 | systemd: 4 | name: node_exporter 5 | state: restarted 6 | daemon_reload: yes 7 | enabled: yes 8 | register: node_exporter_restarted 9 | when: not node_exporter_started.changed 10 | 11 | - name: reload node_exporter 12 | systemd: 13 | name: node_exporter 14 | state: reloaded 15 | when: 16 | - not node_exporter_started.changed 17 | - not (node_exporter_restarted.changed | default(false)) 18 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/tasks/check.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check if node_exporter emits metrices 3 | uri: 4 | url: http://127.0.0.1:9100/metrics 5 | method: GET 6 | status_code: 200 7 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/tasks/configure.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Copy systemd init file 3 | template: 4 | src: init.service.j2 5 | dest: /etc/systemd/system/node_exporter.service 6 | notify: restart node_exporter 7 | 8 | - name: Start node_exporter service 9 | systemd: 10 | name: node_exporter 11 | state: started 12 | daemon_reload: yes 13 | enabled: yes 14 | register: node_exporter_started 15 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Download prometheus node_exporter to local folder 3 | become: false 4 | get_url: 5 | url: "{{ node_exporter_download_link }}" 6 | dest: "/tmp/{{ node_exporter_pack_name }}.{{ node_exporter_pack_ext }}" 7 | register: _download_archive 8 | until: _download_archive is succeeded 9 | retries: 3 10 | delay: 2 11 | run_once: true 12 | delegate_to: localhost 13 | check_mode: false 14 | 15 | - name: Unpack node_exporter binaries 16 | become: false 17 | unarchive: 18 | src: "/tmp/{{ node_exporter_pack_name }}.{{ node_exporter_pack_ext }}" 19 | dest: "/tmp/" 20 | creates: "/tmp/{{ node_exporter_pack_name }}/node_exporter" 21 | run_once: true 22 | delegate_to: localhost 23 | check_mode: false 24 | 25 | - name: Creating node_exporter user group 26 | group: name="{{ groupId }}" 27 | when: groupId != "root" 28 | 29 | - name: Creating node_exporter user 30 | user: 31 | name: "{{ userId }}" 32 | group: "{{ groupId }}" 33 | system: yes 34 | shell: "/sbin/nologin" 35 | comment: "{{ userId }} nologin User" 36 | createhome: "no" 37 | state: present 38 | when: userId != "root" 39 | 40 | - name: Copy node_exporter binaries 41 | copy: 42 | src: "/tmp/{{ node_exporter_pack_name }}/node_exporter" 43 | dest: "{{ node_exporter_bin_dir }}/" 44 | mode: 0755 45 | owner: "{{ userId }}" 46 | group: "{{ groupId }}" 47 | notify: restart node_exporter 48 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: install.yml 3 | - include: configure.yml 4 | 5 | - name: Flush handlers 6 | meta: flush_handlers 7 | 8 | - include: check.yml 9 | -------------------------------------------------------------------------------- /ansible/roles/node_exporter/templates/init.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description={{serviceName}} 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | User={{ userId }} 8 | Group={{ groupId }} 9 | Restart=on-failure 10 | Type=simple 11 | ExecStart={{ exec_command }} 12 | 13 | [Install] 14 | WantedBy=multi-user.target 15 | -------------------------------------------------------------------------------- /ansible/roles/ntpd/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ntpd_servers: 3 | - ru.pool.ntp.org 4 | -------------------------------------------------------------------------------- /ansible/roles/ntpd/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: restart chronyd 3 | systemd: 4 | name: chronyd 5 | state: restarted 6 | daemon_reload: yes 7 | -------------------------------------------------------------------------------- /ansible/roles/ntpd/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install chronyd 3 | package: 4 | name: chrony 5 | state: latest 6 | 7 | - name: Delete old/default config 8 | file: 9 | path: /etc/chronyd.conf 10 | state: absent 11 | notify: restart chronyd 12 | 13 | - name: Create config for chronyd 14 | template: 15 | src: chronyd.conf.j2 16 | dest: /etc/chrony/chrony.conf 17 | owner: root 18 | group: root 19 | mode: 0644 20 | backup: yes 21 | notify: restart chronyd 22 | 23 | - meta: flush_handlers 24 | 25 | - name: Start and enable chronyd 26 | systemd: 27 | name: chronyd 28 | state: started 29 | enabled: yes 30 | daemon_reload: yes 31 | -------------------------------------------------------------------------------- /ansible/roles/ntpd/templates/chronyd.conf.j2: -------------------------------------------------------------------------------- 1 | # {{ ansible_managed }} 2 | 3 | # Welcome to the chrony configuration file. See chrony.conf(5) for more 4 | # information about usuable directives. 5 | # pool 2.debian.pool.ntp.org iburst 6 | 7 | # Configured NTP servers 8 | {% for host in ntpd_servers %} 9 | server {{ host }} iburst prefer 10 | {% endfor %} 11 | 12 | # This directive specify the location of the file containing ID/key pairs for 13 | # NTP authentication. 14 | keyfile /etc/chrony/chrony.keys 15 | 16 | # This directive specify the file into which chronyd will store the rate 17 | # information. 18 | driftfile /var/lib/chrony/chrony.drift 19 | 20 | # Uncomment the following line to turn logging on. 21 | #log tracking measurements statistics 22 | 23 | # Log files location. 24 | logdir /var/log/chrony 25 | 26 | # Stop bad estimates upsetting machine clock. 27 | maxupdateskew 100.0 28 | 29 | # This directive enables kernel synchronisation (every 11 minutes) of the 30 | # real-time clock. Note that it can’t be used along with the 'rtcfile' directive. 31 | rtcsync 32 | 33 | # Step the system clock instead of slewing it if the adjustment is larger than 34 | # one second, but only in the first three clock updates. 35 | makestep 1 3 36 | -------------------------------------------------------------------------------- /img/54-http.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/54-http.png -------------------------------------------------------------------------------- /img/55-arping.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/55-arping.png -------------------------------------------------------------------------------- /img/56-nc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/56-nc.png -------------------------------------------------------------------------------- /img/57-tcpdump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/57-tcpdump.png -------------------------------------------------------------------------------- /img/58-iptables.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/58-iptables.jpg -------------------------------------------------------------------------------- /img/59-input.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/59-input.png -------------------------------------------------------------------------------- /img/60-spoiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/60-spoiler.png -------------------------------------------------------------------------------- /img/61-virt-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/61-virt-1.png -------------------------------------------------------------------------------- /img/61-virt-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/61-virt-2.png -------------------------------------------------------------------------------- /img/61-virt-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/61-virt-3.png -------------------------------------------------------------------------------- /img/62-docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/62-docker.png -------------------------------------------------------------------------------- /img/63-docker-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/63-docker-run.png -------------------------------------------------------------------------------- /img/64-docker-ps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/64-docker-ps.png -------------------------------------------------------------------------------- /img/65-docker-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/65-docker-image.png -------------------------------------------------------------------------------- /img/66-docker-exec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/66-docker-exec.png -------------------------------------------------------------------------------- /img/67-docker-ps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/67-docker-ps.png -------------------------------------------------------------------------------- /img/68-docker-proxy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/68-docker-proxy.png -------------------------------------------------------------------------------- /img/69-docker-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/69-docker-build.png -------------------------------------------------------------------------------- /img/70-docker-mysuperapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/70-docker-mysuperapp.png -------------------------------------------------------------------------------- /img/71-docker-tag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/71-docker-tag.png -------------------------------------------------------------------------------- /img/72-docker-run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/72-docker-run.png -------------------------------------------------------------------------------- /img/73-mysql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/73-mysql.png -------------------------------------------------------------------------------- /img/75-gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/75-gitlab.png -------------------------------------------------------------------------------- /img/76-gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/76-gitlab.png -------------------------------------------------------------------------------- /img/77-gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/77-gitlab.png -------------------------------------------------------------------------------- /img/Lab4-build-job-not-fail-when-no-artifacts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-build-job-not-fail-when-no-artifacts.png -------------------------------------------------------------------------------- /img/Lab4-inside-docker-whereis-pip-libs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-inside-docker-whereis-pip-libs.png -------------------------------------------------------------------------------- /img/Lab4-pipeline-artifact-pass1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-pipeline-artifact-pass1.png -------------------------------------------------------------------------------- /img/Lab4-pipeline-artifact-pass2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-pipeline-artifact-pass2.png -------------------------------------------------------------------------------- /img/Lab4-pipeline-cov.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-pipeline-cov.png -------------------------------------------------------------------------------- /img/Lab4-pipeline-junit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-pipeline-junit.png -------------------------------------------------------------------------------- /img/Lab4-win-security.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4-win-security.png -------------------------------------------------------------------------------- /img/Lab4_Merge-Sort-Algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/Lab4_Merge-Sort-Algorithm.png -------------------------------------------------------------------------------- /img/s0-rwx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s0-rwx.png -------------------------------------------------------------------------------- /img/s1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s1.png -------------------------------------------------------------------------------- /img/s10-revert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s10-revert.png -------------------------------------------------------------------------------- /img/s11-confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s11-confirm.png -------------------------------------------------------------------------------- /img/s12-reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s12-reset.png -------------------------------------------------------------------------------- /img/s13-apt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s13-apt.png -------------------------------------------------------------------------------- /img/s14-nat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s14-nat.png -------------------------------------------------------------------------------- /img/s15-ssh-rule.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s15-ssh-rule.png -------------------------------------------------------------------------------- /img/s16-win-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s16-win-1.png -------------------------------------------------------------------------------- /img/s16-win-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s16-win-2.png -------------------------------------------------------------------------------- /img/s16-win-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s16-win-3.png -------------------------------------------------------------------------------- /img/s17-unix.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s17-unix.png -------------------------------------------------------------------------------- /img/s18-win-keygen-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s18-win-keygen-1.png -------------------------------------------------------------------------------- /img/s18-win-keygen-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s18-win-keygen-2.png -------------------------------------------------------------------------------- /img/s19-ssh-keygen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s19-ssh-keygen.png -------------------------------------------------------------------------------- /img/s2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s2.png -------------------------------------------------------------------------------- /img/s20-ulimit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s20-ulimit.png -------------------------------------------------------------------------------- /img/s21-ssh-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s21-ssh-add.png -------------------------------------------------------------------------------- /img/s22-which.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s22-which.png -------------------------------------------------------------------------------- /img/s23-sshd-limits.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s23-sshd-limits.png -------------------------------------------------------------------------------- /img/s24-limit-check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s24-limit-check.png -------------------------------------------------------------------------------- /img/s25-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s25-user.png -------------------------------------------------------------------------------- /img/s26-bashrc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s26-bashrc.png -------------------------------------------------------------------------------- /img/s27-colors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s27-colors.png -------------------------------------------------------------------------------- /img/s28-chattr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s28-chattr.png -------------------------------------------------------------------------------- /img/s29-passwd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s29-passwd.png -------------------------------------------------------------------------------- /img/s3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s3.png -------------------------------------------------------------------------------- /img/s30-pipeline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s30-pipeline.jpg -------------------------------------------------------------------------------- /img/s31-pipeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s31-pipeline.png -------------------------------------------------------------------------------- /img/s32-tty-before.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s32-tty-before.png -------------------------------------------------------------------------------- /img/s33-tty-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s33-tty-after.png -------------------------------------------------------------------------------- /img/s34-pts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s34-pts.png -------------------------------------------------------------------------------- /img/s35-tty-linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s35-tty-linux.png -------------------------------------------------------------------------------- /img/s36-tmux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s36-tmux.png -------------------------------------------------------------------------------- /img/s37-sudoers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s37-sudoers.png -------------------------------------------------------------------------------- /img/s38-lvm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s38-lvm.png -------------------------------------------------------------------------------- /img/s39-pstates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s39-pstates.png -------------------------------------------------------------------------------- /img/s4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s4.png -------------------------------------------------------------------------------- /img/s40-ansible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s40-ansible.png -------------------------------------------------------------------------------- /img/s41-ntpd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s41-ntpd.png -------------------------------------------------------------------------------- /img/s42-systemd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s42-systemd.png -------------------------------------------------------------------------------- /img/s43-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s43-top.png -------------------------------------------------------------------------------- /img/s44-atop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s44-atop.png -------------------------------------------------------------------------------- /img/s45-disk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s45-disk.png -------------------------------------------------------------------------------- /img/s46-fdisk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s46-fdisk.png -------------------------------------------------------------------------------- /img/s47-mount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s47-mount.png -------------------------------------------------------------------------------- /img/s48-bind-mount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s48-bind-mount.png -------------------------------------------------------------------------------- /img/s49-fstab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s49-fstab.png -------------------------------------------------------------------------------- /img/s5-installer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s5-installer.png -------------------------------------------------------------------------------- /img/s50-lv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s50-lv.png -------------------------------------------------------------------------------- /img/s51-lvextend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s51-lvextend.png -------------------------------------------------------------------------------- /img/s52-ln.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s52-ln.png -------------------------------------------------------------------------------- /img/s53-ip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s53-ip.png -------------------------------------------------------------------------------- /img/s6-lvm-ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s6-lvm-ex.png -------------------------------------------------------------------------------- /img/s7-grub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s7-grub.png -------------------------------------------------------------------------------- /img/s8-onlogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s8-onlogin.png -------------------------------------------------------------------------------- /img/s9-snapshots-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s9-snapshots-2.png -------------------------------------------------------------------------------- /img/s9-snapshots.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iu5git/linux-course/ad50171abc4aea7db7134c12b8552b5494474852/img/s9-snapshots.png --------------------------------------------------------------------------------