Compare commits
190 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a9342e8ad | |||
| 916e277dd3 | |||
| 4f680cf63f | |||
| 94ea966dd8 | |||
| 2a3fa493b2 | |||
| c87a0127f8 | |||
| e736eb9646 | |||
| da0d12a837 | |||
| 913bfc8dab | |||
| d84dfbc6f5 | |||
| 819768cb44 | |||
| e197b5e0ff | |||
| 11c7275b96 | |||
| 82c8bbcd85 | |||
| e9b39f0724 | |||
| 575e4a080b | |||
| 9c751a66dc | |||
| 96cabba22c | |||
| 5b9daf3457 | |||
| caad4499bf | |||
| 4270c65a26 | |||
| 5b51a90b4d | |||
| 2e7a604149 | |||
| b5268a1370 | |||
| 038e25150d | |||
| c498e391d2 | |||
| 5dbfc8a290 | |||
| 6fab2cab4d | |||
| 441969f862 | |||
| 7de8fc4fbb | |||
| 8f021f73cc | |||
| db3f828e88 | |||
| 48060d8668 | |||
| 9cf0d09a3e | |||
| 73c76b404c | |||
| 0a94c5f7b4 | |||
| 3939c0a437 | |||
| 4c4515e096 | |||
| 9410f7df20 | |||
| 7f8349661f | |||
| 1781ad4310 | |||
| 4f4731ccc7 | |||
| 71e0b53442 | |||
| b1b8546066 | |||
| f686140890 | |||
| bcf8d900c0 | |||
| a3096ba1b6 | |||
| 5dfc2e7428 | |||
| 61a95ffd50 | |||
| fb4b18368b | |||
| 792fe1b401 | |||
| 6aec724e6f | |||
| 3eccbe572e | |||
| c7c34fbbbf | |||
| 8df7811705 | |||
| 5fcacd72fe | |||
| cf2c1e633a | |||
| 91d0541f99 | |||
| cc1297a674 | |||
| cf7e8c27f2 | |||
| 6b5f084474 | |||
| 17645cb717 | |||
| baa0cb8ca6 | |||
| c9168ad6e1 | |||
| 3d932f172e | |||
| 4e5bd60702 | |||
| fa0e7533ae | |||
| f9f6fd45c5 | |||
| 3719f701ed | |||
| bfa45b409c | |||
| 6eb86580b9 | |||
| 605c94642c | |||
| 74109940d9 | |||
| 5c973fe83b | |||
| 6dc0b0ec9f | |||
| 6d127803da | |||
| 4e51fca06b | |||
| ba3b1bd592 | |||
| 2e574a6137 | |||
| b6083748b2 | |||
| 118b94d9a2 | |||
| 0115945805 | |||
| fbece247aa | |||
| c4ad5d2340 | |||
| aa356db95c | |||
| 6a60a4994a | |||
| a77475a313 | |||
| 15e64885aa | |||
| f297e4a942 | |||
| 2befd048b1 | |||
| 80bba26666 | |||
| 8959c84e46 | |||
| bb13a1061e | |||
| 55f07be3d0 | |||
| d0c2a3e7ab | |||
| ac52a84f9d | |||
| f1bad04e52 | |||
| e4f139a3e2 | |||
| 7f4f3b2701 | |||
| 2ef3469443 | |||
| 93dcd900fd | |||
| a7c5bd2e6d | |||
| cbfe59f7cf | |||
| e25749fa66 | |||
| f0ea7634b9 | |||
| 6eec8fe432 | |||
| 8468fc0c80 | |||
| c7a639e42c | |||
| 7870726c8c | |||
| d91cf25300 | |||
| c5157f19fe | |||
| 055835d5bd | |||
| a99efd3182 | |||
| 5f8b131742 | |||
| cf6348de9d | |||
| ce2fdddc9e | |||
| 991819fa39 | |||
| 500c7cac71 | |||
| c464d8ccbe | |||
| dd9731b3d5 | |||
| 6867d8f214 | |||
| f0ccb8abfc | |||
| 0c2b7164e5 | |||
| 30e6c7d15c | |||
| 668518caa7 | |||
| 6c52082e89 | |||
| f41bc70752 | |||
| 4db8d0e5cb | |||
| fa0db0f0c9 | |||
| 89a102965c | |||
| eb9e50219b | |||
| d08d7fbd23 | |||
| 7f1c39c306 | |||
| 10cd187006 | |||
| 744437c4d6 | |||
| 4ab000a022 | |||
| 2981f9cab8 | |||
| 31fbe09273 | |||
| c5a965e2e6 | |||
| 2f7fd3bee7 | |||
| e8c9ed8597 | |||
| e03f31515e | |||
| 7ff630ea1b | |||
| 095dd4d5e7 | |||
| bdefa134e4 | |||
| 031663a2d0 | |||
| e7ab05b3d7 | |||
| b381b87108 | |||
| ab5a27d49b | |||
| 0102e39a28 | |||
| 65961da59c | |||
| 1803ea0ee5 | |||
| 9f2416a8e6 | |||
| 66e89b14ba | |||
| 85cbbfcd49 | |||
| d09a34019f | |||
| c415c28414 | |||
| a7d9728049 | |||
| 661a776242 | |||
| b65464a793 | |||
| b84f3d65c8 | |||
| b8152b19d7 | |||
| bfbfbe4741 | |||
| baeffe48f3 | |||
| 8b97fa3d96 | |||
| 7809040991 | |||
| e545b8389a | |||
| 78a0ccf4d8 | |||
| a3b6b95ff7 | |||
| 29babcee44 | |||
| ae9d777eb0 | |||
| 076547c1d2 | |||
| 9a7cce46fa | |||
| 2272f0eb4c | |||
| 46b258727c | |||
| 37a74a2cf9 | |||
| e8132417ba | |||
| 1d4751ae34 | |||
| 2809e3318f | |||
| 4892793e80 | |||
| fc54720d4b | |||
| 5efdd24822 | |||
| 4669b409b9 | |||
| b305eaa877 | |||
| f767ec7e6a | |||
| aa455677ef | |||
| f176fb5cbc | |||
| 28992c5067 | |||
| fb5a128942 | |||
| f5483693e4 |
88
readme.md
@@ -2,5 +2,89 @@
|
||||
|
||||
## Homeworks
|
||||
|
||||
* [1.1 Введение в DevOps](/src/homework/1.1)
|
||||
* [2.1 Системы контроля версий](/src/homework/2.1)
|
||||
* [01.1 Введение в DevOps](/src/homework/01-intro/1.1)
|
||||
* [02.1 Системы контроля версий](/src/homework/02-git/2.1)
|
||||
* [02.2 Основы Git](/src/homework/02-git/2.2)
|
||||
* [02.3 Ветвления в Git](/src/homework/02-git/2.3)
|
||||
* [02.4 Ветвления в Git](/src/homework/02-git/2.4)
|
||||
* [03.1 Работа в терминале, лекция 1](/src/homework/03-sysadmin/3.1)
|
||||
* [03.2 Работа в терминале, лекция 2](/src/homework/03-sysadmin/3.2)
|
||||
* [03.3. Операционные системы, лекция 1](/src/homework/03-sysadmin/3.3)
|
||||
* [03.4. Операционные системы, лекция 2](/src/homework/03-sysadmin/3.4)
|
||||
* [03.5. Файловые системы](/src/homework/03-sysadmin/3.5)
|
||||
* [03.6. Компьютерные сети, лекция 1](/src/homework/03-sysadmin/3.6)
|
||||
* [03.7. Компьютерные сети, лекция 2](/src/homework/03-sysadmin/3.7)
|
||||
* [03.8. Компьютерные сети, лекция 3](/src/homework/03-sysadmin/3.8)
|
||||
* [03.9. Элементы безопасности информационных систем](/src/homework/03-sysadmin/3.9)
|
||||
* [04.1. Командная оболочка Bash: Практические навыки](/src/homework/04-script/4.1)
|
||||
* [04.2. Использование Python для решения типовых DevOps задач](/src/homework/04-script/4.2)
|
||||
* [04.3. Языки разметки JSON и YAML](/src/homework/04-script/4.3)
|
||||
* [05.1. Основы виртуализации](/src/homework/05-virtualization/5.1)
|
||||
* [05.2. Применение принципов IaaC в работе с виртуальными машинами](/src/homework/05-virtualization/5.2)
|
||||
* [05.3. Введение. Экосистема. Архитектура. Жизненный цикл Docker контейнера](/src/homework/05-virtualization/5.3)
|
||||
* [05.4. Оркестрация группой Docker контейнеров на примере Docker Compose](/src/homework/05-virtualization/5.4)
|
||||
* [05.5. Оркестрация кластером Docker контейнеров на примере Docker Swarm](/src/homework/05-virtualization/5.5)
|
||||
* [06.1. Типы и структура СУБД](/src/homework/06-database/6.1)
|
||||
* [06.2. SQL](/src/homework/06-database/6.2)
|
||||
* [06.3. MySQL](/src/homework/06-database/6.3)
|
||||
* [06.4. PostgreSQL](/src/homework/06-database/6.4)
|
||||
* [06.5. Elasticsearch](/src/homework/06-database/6.5)
|
||||
* [06.6. Troubleshooting](/src/homework/06-database/6.6)
|
||||
* [07.1. Инфраструктура как код](/src/homework/07-terraform/7.1)
|
||||
* [07.2. Облачные провайдеры и синтаксис Terraform](/src/homework/07-terraform/7.2)
|
||||
* [07.3. Основы и принцип работы Terraform](/src/homework/07-terraform/7.3)
|
||||
* [07.4. Средства командной работы над инфраструктурой](/src/homework/07-terraform/7.4)
|
||||
* [07.5. Основы golang](/src/homework/07-terraform/7.5)
|
||||
* [07.6. Написание собственных провайдеров для Terraform](/src/homework/07-terraform/7.6)
|
||||
* [08.1. Введение в Ansible](/src/homework/08-ansible/8.1)
|
||||
* [08.2. Работа с Playbook](/src/homework/08-ansible/8.2)
|
||||
* [08.3. Использование Yandex Cloud](/src/homework/08-ansible/8.3)
|
||||
* [08.4. Работа с Roles](/src/homework/08-ansible/8.4)
|
||||
* [08.5. Тестирование Roles](/src/homework/08-ansible/8.5)
|
||||
* [08.6. Создание собственных modules](/src/homework/08-ansible/8.6)
|
||||
* [09.1. Жизненный цикл ПО](/src/homework/09-ci/9.1)
|
||||
* [09.3. CI\CD](/src/homework/09-ci/9.3)
|
||||
* [09.4. Jenkins](/src/homework/09-ci/9.4)
|
||||
* [09.5. Teamcity](/src/homework/09-ci/9.5)
|
||||
* [09.6. Gitlab](/src/homework/09-ci/9.6)
|
||||
* [10.1. Зачем и что нужно мониторить](/src/homework/10-monitoring/10.1)
|
||||
* [10.2. Системы мониторинга](/src/homework/10-monitoring/10.2)
|
||||
* [10.3. Grafana](/src/homework/10-monitoring/10.3)
|
||||
* [10.4. ELK](/src/homework/10-monitoring/10.4)
|
||||
* [10.5. Sentry](/src/homework/10-monitoring/10.5)
|
||||
* [10.6. Инцидент-менеджмент](/src/homework/10-monitoring/10.6)
|
||||
* [11.1. Введение в микросервисы](/src/homework/11-microservices/11.1)
|
||||
* [11.2. Микросервисы: принципы](/src/homework/11-microservices/11.2)
|
||||
* [11.3. Микросервисы: подходы](/src/homework/11-microservices/11.3)
|
||||
* [11.4. Микросервисы: масштабирование](/src/homework/11-microservices/11.4)
|
||||
* [12.1. Компоненты Kubernetes](/src/homework/12-kubernetes/12.1)
|
||||
* [12.2. Команды для работы с Kubernetes](/src/homework/12-kubernetes/12.2)
|
||||
* [12.3. Развертывание кластера на собственных серверах, лекция 1](/src/homework/12-kubernetes/12.3)
|
||||
* [12.4. Развертывание кластера на собственных серверах, лекция 2](/src/homework/12-kubernetes/12.4)
|
||||
* [12.5. Сетевые решения CNI](/src/homework/12-kubernetes/12.5)
|
||||
* [13.1. Контейнеры, поды, deployment, statefulset, services, endpoints](/src/homework/13-kubernetes-config/13.1)
|
||||
* [13.2. Разделы и монтирование](/src/homework/13-kubernetes-config/13.2)
|
||||
* [13.3. Работа с kubectl](/src/homework/13-kubernetes-config/13.3)
|
||||
* [13.4. Инструменты для упрощения написания конфигурационных файлов. Helm и Jsonnet](/src/homework/13-kubernetes-config/13.4)
|
||||
* [13.5. Поддержка нескольких окружений на примере Qbec](/src/homework/13-kubernetes-config/13.5)
|
||||
* [14.1. Создание и использование секретов](/src/homework/14-kubernetes-security/14.1)
|
||||
* [14.2. Синхронизация секретов с внешними сервисами. Vault](/src/homework/14-kubernetes-security/14.2)
|
||||
* [14.3. Карты конфигураций](/src/homework/14-kubernetes-security/14.3)
|
||||
* [14.4. Сервис-аккаунты](/src/homework/14-kubernetes-security/14.4)
|
||||
* [14.5. SecurityContext, NetworkPolicies](/src/homework/14-kubernetes-security/14.5)
|
||||
* [15.1. Организация сети](/src/homework/15-cloud-providers/15.1)
|
||||
* [15.2. Вычислительные мощности. Балансировщики нагрузки](/src/homework/15-cloud-providers/15.2)
|
||||
* [15.3. Безопасность в облачных провайдерах](/src/homework/15-cloud-providers/15.3)
|
||||
|
||||
## Graduate work
|
||||
|
||||
[**Дипломная работа**](/src/graduate_work)
|
||||
|
||||
## Related repositories
|
||||
|
||||
* [Dannecron/netology-devops-ansible-lighthouse](https://github.com/Dannecron/netology-devops-ansible-lighthouse)
|
||||
* [Dannecron/netology-devops-ansible-yandex-cloud-cvl](https://github.com/Dannecron/netology-devops-ansible-yandex-cloud-cvl)
|
||||
* [Dannecron/netology-devops-ansible-vector](https://github.com/Dannecron/netology-devops-ansible-vector)
|
||||
* [Dannecron/netology-devops-teamcity-example](https://github.com/Dannecron/netology-devops-teamcity-example)
|
||||
* [Dannecron/netology-devops-gitlab](https://github.com/Dannecron/netology-devops-gitlab)
|
||||
* [Dannecron/netology-devops-gw-infra](https://github.com/Dannecron/netology-devops-gw-infra)
|
||||
|
||||
316
src/graduate_work/readme.md
Normal file
@@ -0,0 +1,316 @@
|
||||
## Дипломная работа
|
||||
|
||||
Выполнение дипломной работы курса netology DevOps инженер. Оригинал задания доступен по [ссылке](https://github.com/netology-code/devops-diplom-yandexcloud/blob/main/README.md).
|
||||
|
||||
Весь код, выполненный по ходу выполнения работы находится в репозиториях на github:
|
||||
* terraform/ansible/helm: [Dannecron/netology-devops-gw-infra](https://github.com/Dannecron/netology-devops-gw-infra)
|
||||
* приложение: [Dannecron/parcel-example-neko](https://github.com/Dannecron/parcel-example-neko)
|
||||
|
||||
### Создание облачной инфраструктуры
|
||||
|
||||
[Задание](./tasks.md#создание-облачной-инфраструктуры).
|
||||
|
||||
#### Предварительная настройка
|
||||
|
||||
Данный параграф описывает выполнения шагов 1-3 из задания.
|
||||
|
||||
Предварительная настройка включает в себя несколько шагов, необходимых для последующей работы с `yandex.cloud` через `terraform`.
|
||||
Данные шаги выполняются в ручную, но могут быть автоматизированы, например, через `ansible`.
|
||||
|
||||
1. Установить утилиту [yc](https://cloud.yandex.ru/docs/cli/quickstart) и подключится к облаку.
|
||||
2. Создание сервисного аккаунта с ролью `editor` на дефолтной директории облака:
|
||||
|
||||
```shell
|
||||
yc iam service-account create --name terraform-acc
|
||||
yc resource-manager folder add-access-binding --name default --role editor --subject "serviceAccount:<accId>"
|
||||
```
|
||||
|
||||
где `<accId>` - это уникальный идентификатор нового сервисного аккаунта.
|
||||
Затем нужно получить ключ доступа для данного сервисного аккаунта:
|
||||
|
||||
```shell
|
||||
yc iam access-key create --service-account-name terraform-acc --format=json
|
||||
```
|
||||
3. Создание s3-bucket для хранения состояния `terraform`
|
||||
|
||||
```shell
|
||||
yc storage bucket create --name=dnc-netology-tf-state
|
||||
```
|
||||
|
||||
Следующий шаг - инициализация terraform и создание нового workspace. Для инициализации используется команда:
|
||||
|
||||
```shell
|
||||
terraform init \
|
||||
-backend-config="bucket=dnc-netology-tf-state" \
|
||||
-backend-config="access_key=<service_account_key_id>" \
|
||||
-backend-config="secret_key=<service_account_secret_key>"
|
||||
```
|
||||
|
||||
где `<service_account_key_id>` и `<service_account_secret_key>` данные полученные на шаге получения ключа доступа для сервисного аккаунта.
|
||||
|
||||
Создание и переключение на новый workspace с названием `prod`:
|
||||
|
||||
```shell
|
||||
terraform workspace new prod
|
||||
```
|
||||
|
||||
Для упрощения процесса был создан ansible-playbook [terraform_init.yml](https://github.com/Dannecron/netology-devops-gw-infra/blob/main/terraform_init.yml).
|
||||
Чтобы усилить безопасность некоторые переменные были зашифрованы через `ansible-vault`.
|
||||
Таким образом, для запуска достаточно выполнить команду
|
||||
|
||||
```shell
|
||||
ansible-playbook --ask-vault-pass -i ansible/terraform_init terraform_init.yml
|
||||
```
|
||||
|
||||
После выполнения данных шагов можно приступать непосредственно к разворачиванию инфрастуктуры через команды terraform.
|
||||
|
||||
#### Создание VPC и подсетей через terraform
|
||||
|
||||
Для создания VPC и двух подсетей будет использована следующая конфигурация:
|
||||
|
||||
```terraform
|
||||
resource "yandex_vpc_network" "netology-gw-network" {
|
||||
name = "netology-gw-network"
|
||||
}
|
||||
|
||||
resource "yandex_vpc_subnet" "netology-gw-subnet-a" {
|
||||
name = "netology-gw-subnet-a"
|
||||
zone = "ru-central1-a"
|
||||
network_id = yandex_vpc_network.netology-gw-network.id
|
||||
v4_cidr_blocks = ["192.168.10.0/24"]
|
||||
}
|
||||
|
||||
resource "yandex_vpc_subnet" "netology-gw-subnet-b" {
|
||||
name = "netology-gw-subnet-b"
|
||||
zone = "ru-central1-b"
|
||||
network_id = yandex_vpc_network.netology-gw-network.id
|
||||
v4_cidr_blocks = ["192.168.15.0/24"]
|
||||
}
|
||||
```
|
||||
|
||||
Затем нужно последовательно выполнить команды для проверки применения конфигурации в облаке (выполняется из [директории terraform](https://github.com/Dannecron/netology-devops-gw-infra/tree/main/terraform)):
|
||||
|
||||
```shell
|
||||
terraform plan
|
||||
terraform apply
|
||||
terraform destroy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Создание Kubernetes кластера
|
||||
|
||||
[Задание](./tasks.md#создание-Kubernetes-кластера).
|
||||
|
||||
Конфигурация машин будет одинаковая, поэтому terraform-конфигурация будет выглядеть следующим образом:
|
||||
|
||||
```terraform
|
||||
resource "random_shuffle" "netology-gw-subnet-random" {
|
||||
input = [yandex_vpc_subnet.netology-gw-subnet-a.id, yandex_vpc_subnet.netology-gw-subnet-b.id]
|
||||
result_count = 1
|
||||
}
|
||||
|
||||
resource "yandex_compute_instance" "k8s-cluster" {
|
||||
for_each = toset(["control", "node01", "node2"])
|
||||
|
||||
name = each.key
|
||||
|
||||
resources {
|
||||
cores = 2
|
||||
memory = 2
|
||||
}
|
||||
|
||||
boot_disk {
|
||||
initialize_params {
|
||||
image_id = "fd8kdq6d0p8sij7h5qe3" # ubuntu-20-04-lts-v20220822
|
||||
size = "20"
|
||||
}
|
||||
}
|
||||
|
||||
network_interface {
|
||||
subnet_id = random_shuffle.netology-gw-subnet-random.result[0]
|
||||
nat = true
|
||||
}
|
||||
|
||||
metadata = {
|
||||
ssh-keys = "ubuntu:${file("~/.ssh/id_rsa.pub")}"
|
||||
}
|
||||
}
|
||||
|
||||
output "cluster_ips" {
|
||||
value = {
|
||||
internal = values(yandex_compute_instance.k8s-cluster)[*].network_interface.0.ip_address
|
||||
external = values(yandex_compute_instance.k8s-cluster)[*].network_interface.0.nat_ip_address
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Для распределения по разным зонам доступности использован ресурс `random_shuffle`.
|
||||
|
||||
После деплоя инфраструктуры необходимо скачать репозиторий [kubespray](https://github.com/kubernetes-sigs/kubespray),
|
||||
сформировать inventory-директорию, содержащую сам `inventory.ini` с данными о виртуальных машинах и `group_vars`.
|
||||
|
||||
После данного шага достаточно запустить ansible-playbook `cluster.yml` с переданным inventory:
|
||||
|
||||
```shell
|
||||
ansible-playbook -i ansible/kubespray/inventory.ini vendor/kubespray/cluster.yml
|
||||
```
|
||||
|
||||
Когда установка кластера закончится необходимо с control-node взять файл `/etc/kubernetes/admin.conf`,
|
||||
положить его локально по пути `~/.kube/conf` и изменить ip-адрес кластера на ip-адрес самой control-node.
|
||||
Этого будет достаточно, чтобы подключится к кластеру через утилиту `kubectl`.
|
||||
|
||||
```shell
|
||||
kubectl get pods --all-namespaces
|
||||
```
|
||||
|
||||
```text
|
||||
NAMESPACE NAME READY STATUS RESTARTS AGE
|
||||
kube-system calico-kube-controllers-7f679c5d6f-kfmkz 1/1 Running 0 49m
|
||||
kube-system calico-node-8v2d9 1/1 Running 0 50m
|
||||
kube-system calico-node-rrbcv 1/1 Running 0 50m
|
||||
kube-system calico-node-w67gl 1/1 Running 0 50m
|
||||
kube-system coredns-5867d9544c-7n4qz 1/1 Running 0 47m
|
||||
kube-system coredns-5867d9544c-rfbxs 1/1 Running 0 47m
|
||||
kube-system dns-autoscaler-59b8867c86-2rqdd 1/1 Running 0 47m
|
||||
<...>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Создание тестового приложения
|
||||
|
||||
[Задание](./tasks.md#создание-тестового-приложения).
|
||||
|
||||
Для данного задания будет использован [репозиторий тестового приложения](https://github.com/Dannecron/parcel-example-neko)
|
||||
в котором расположено небольшое тестовое приложение на JS. Данное приложение запаковывается в образ с nginx.
|
||||
Dockerfile расположен внутри репозитория ([file](https://github.com/Dannecron/parcel-example-neko/blob/main/Dockerfile))
|
||||
|
||||
Docker-образ доступен на [Docker Hub](https://hub.docker.com/r/dannecron/parcel-example-neko).
|
||||
|
||||
---
|
||||
|
||||
### Подготовка системы мониторинга и деплой приложения
|
||||
|
||||
[Задание](./tasks.md#подготовка-системы-мониторинга-и-деплой-приложения).
|
||||
|
||||
Перед деплоем необходимо было активировать nginx-ingress-controller в конфигурации kubespray.
|
||||
Для этого в файле [ansible/kubespray/group_vars/k8s_cluster/addons.yml](https://github.com/Dannecron/netology-devops-gw-infra/blob/main/ansible/kubespray/group_vars/k8s_cluster/addons.yml) изменено значение
|
||||
по ключам `ingress_nginx_enabled` и `ingress_nginx_host_network` на `true`.
|
||||
|
||||
Для деплоя всех необходимых сервисов было создано 2 helm-чарта и использован готовый helm-чарт:
|
||||
* чарт [k8s/helm/atlantis](https://github.com/Dannecron/netology-devops-gw-infra/tree/main/k8s/helm/atlantis) для упрощённого деплоя `atlantis`
|
||||
* чарт [k8s/helm/simple-app](https://github.com/Dannecron/netology-devops-gw-infra/tree/main/k8s/helm/simple-app) для деплоя приложения
|
||||
* готовый чарт [kube-prometheus-stack](https://artifacthub.io/packages/helm/prometheus-community/kube-prometheus-stack)
|
||||
и конфигурация для него [k8s/helm/kube-prometheus-stack/values.yml](https://github.com/Dannecron/netology-devops-gw-infra/blob/main/k8s/helm/kube-prometheus-stack/values.yml)
|
||||
|
||||
Применение изменений производится командами `helm`:
|
||||
* `helm install` - первый деплой чарта
|
||||
* `helm upgrade` - повторный деплой чарта для применения изменений
|
||||
* `helm upgrade -i` - установка или обновление чарта
|
||||
|
||||
Конкретные команды, которые были выполнены:
|
||||
|
||||
```shell
|
||||
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
|
||||
helm install monitoring prometheus-community/kube-prometheus-stack -f k8s/helm/kube-prometheus-stack/values.yml
|
||||
helm install simple-app k8s/helm/simple-app
|
||||
helm install --set "config.github.user=<access_token>" --set "config.github.token=<token_secret>" --set "config.github.secret=<webhook_secret>" atlantis k8s/helm/atlantis
|
||||
```
|
||||
|
||||
где `<access_token>`, `<token_secret>` - это данные персонального access-токена, созданного на github,
|
||||
а `<webhook_secret>` - строка, которая должна совпадать в конфигурации webhook и atlantis.
|
||||
|
||||
После выполнения сервисы стали доступны по следующим доменам:
|
||||
* `http://grafana-gw.my.to` - grafana (логин `admin`, пароль `prom-operator`)
|
||||
* `http://app-gw.my.to` - приложение
|
||||
* `http://atlantis-gw.my.to/` - atlantis
|
||||
|
||||
_UPD_ atlantis был подключён к репозиторию, но не получается корректно настроить backend для terraform.
|
||||
Особенно вызывает проблемы необходимость каждый день выпускать новый токен для сервисного аккаунта yandex.cloud.
|
||||
|
||||
---
|
||||
|
||||
### Установка и настройка CI/CD
|
||||
|
||||
[Задание](./tasks.md#установка-и-настройка-CI/CD).
|
||||
|
||||
В качестве сервиса автоматизации сборки и развёртывания приложения был выбран [jenkins](https://www.jenkins.io/).
|
||||
Для его деплоя создан helm-чарт [k8s/helm/jenkins](https://github.com/Dannecron/netology-devops-gw-infra/tree/main/k8s/helm/jenkins),
|
||||
деплой которого производится стандартно:
|
||||
|
||||
```shell
|
||||
helm install --set "docker.dockerHubUser=<dockerHubUser>" --set "docker.dockerHubPassword=<dockerHubPassword>" jenkins k8s/helm/jenkins
|
||||
```
|
||||
|
||||
где `<dockerHubUser>` и `<dockerHubPassword>` - данные авторизации в [hub.docker.com](https://hub.docker.com)
|
||||
для возможности пушить образы в регистри.
|
||||
|
||||
После установки jenkins будет доступен по ip-адресу любой рабочей node кластера по пути `/jenkins` (например ` http://84.201.172.95/jenkins`).
|
||||
Далее необходима первоначальная настройка сервиса в которую входят:
|
||||
* авторизация в качестве начального администратора. Ключ доступа можно посмотреть в логах pod, например, командой:
|
||||
|
||||
```shell
|
||||
kubectl --namespace ci-cd logs jenkins-production-main-0
|
||||
```
|
||||
* установка первоначальных плагинов (можно выбрать рекомендованный вариант).
|
||||
* создание дополнительного пользователя (можно пропустить).
|
||||
* изменение конфигурации безопасности `Host Key Verification Strategy` на `Accept first connection`
|
||||
* установка дополнительных плагинов:
|
||||
* [Kubernetes](https://plugins.jenkins.io/kubernetes/) для возможности запускать jenkins-воркеры внутри k8s-кластера
|
||||
* [Generic Webhook Trigger](https://plugins.jenkins.io/generic-webhook-trigger/) для возможности более гибко настраивать
|
||||
поведение скриптов jenkins на github-webhooks.
|
||||
* выставить значение 0 в настройке `Количество процессов-исполнителей` для мастер-ноды.
|
||||
* в `Configure Clouds` добавить новую конфигурацию kubernetes. Адрес кластера и сертификат можно взять из локальной конфигурации `kubectl`.
|
||||
|
||||
После данных действий останется создать два проекта с типом `pipeline`:
|
||||
* для сборки образов при каждом изменении кода в ветках. Скрипт для этого проекта находится в файле [jenkins/ref.jenkinsfile](https://github.com/Dannecron/netology-devops-gw-infra/blob/main/jenkins/ref.jenkinsfile)
|
||||
* для сборки образов и деплое изменений при создании нового git-тэга. Скрипт для этого проекта находится в файле [jenkins/tag.jenkinsfile](https://github.com/Dannecron/netology-devops-gw-infra/blob/main/jenkins/tag.jenkinsfile)
|
||||
|
||||
Плагин `Generic Webhook Trigger`, который используется внутри данных скриптов, требует, чтобы сборка каждого проекта была запущена
|
||||
хотя бы раз перед фактическим использованием. Это необходимо для применения конфигурации переменных для проекта.
|
||||
|
||||
Последним шагом будет настройка двух github-webhook в репозитории приложения [Dannecron/parcel-example-neko](https://github.com/Dannecron/parcel-example-neko).
|
||||
Webhook для первого приложения должен инициироваться при каждом push в репозиторий (`Just the push event.`).
|
||||
Webhook для второго приложения должен инициироваться только при создании тэга (`Branch or tag creation`, создание веток будет отфильтровано).
|
||||
|
||||
По такой логике были созданы следующие теги в [регистри](https://hub.docker.com/r/dannecron/parcel-example-neko/tags)
|
||||
* `feature-1` - при создании новой git-ветки в репозитории.
|
||||
* `0.1.0` - при создании нового тега. При этом в кластер была задеплоена соответствующая версия приложения. Это можно проверить, выполнив команды
|
||||
|
||||
```shell
|
||||
helm list --selector "name=simple-app"
|
||||
kubectl describe pod --show-events=false simple-app-production-application-7c777968c6-cndh2 | grep Image
|
||||
```
|
||||
|
||||
```text
|
||||
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
|
||||
simple-app default 3 2023-04-01 04:48:20.999406345 +0000 UTC deployed simple-app-0.1.0 latest
|
||||
|
||||
Image: dannecron/parcel-example-neko:0.1.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Данные, необходимые для сдачи задания
|
||||
|
||||
1. Репозиторий с конфигурационными файлами Terraform: [Dannecron/netology-devops-gw-infra](https://github.com/Dannecron/netology-devops-gw-infra) директория `terraform`;
|
||||
2. Пример pull request с комментариями созданными atlantis'ом: [github](https://github.com/Dannecron/netology-devops-gw-infra/pull/2);
|
||||
3. Репозиторий с конфигурацией ansible, если был выбран способ создания Kubernetes кластера при помощи ansible:
|
||||
[Dannecron/netology-devops-gw-infra](https://github.com/Dannecron/netology-devops-gw-infra) директория `ansible/kubespray`;
|
||||
4. Репозиторий с Dockerfile тестового приложения и ссылка на собранный docker image:
|
||||
* репозиторий тестового приложения: [Dannecron/parcel-example-neko](https://github.com/Dannecron/parcel-example-neko);
|
||||
* docker image: [dannecron/parcel-example-neko](https://hub.docker.com/r/dannecron/parcel-example-neko);
|
||||
5. Репозиторий с конфигурацией Kubernetes кластера: [Dannecron/netology-devops-gw-infra](https://github.com/Dannecron/netology-devops-gw-infra) директория `k8s`;
|
||||
6. Ссылка на тестовое приложение и веб интерфейс Grafana с данными доступа:
|
||||
* тестовое приложение: `http://app-gw.my.to`
|
||||
* web-интерфейс grafana: `http://grafana-gw.my.to` (логин `admin`, пароль `prom-operator`)
|
||||
|
||||
### Допущения
|
||||
|
||||
Небольшой список допущений, которые были сделаны во время выполнения работы:
|
||||
* Все ноды имеют внешний ip-адрес. В реальном проекте стоило сделать `Bastion host` для доступа по ssh до всех остальных нод,
|
||||
а так же перенаправления трафика к кластеру.
|
||||
* Создано несколько ansible-playbook под разные задачи. Возможно, стоило объединить всё в один playbook с подключением разных ролей.
|
||||
* Helm-чарт для деплоя приложения никуда не опубликован, что усложняет работу с ним.
|
||||
Таким образом, в дальнейшем стоит настроить хотя бы публикацию версии (архива) в качестве артефактов в релизах github.
|
||||
136
src/graduate_work/tasks.md
Normal file
@@ -0,0 +1,136 @@
|
||||
## Цели
|
||||
|
||||
* [Цели](#цели)
|
||||
* [Этапы выполнения](#этапы-выполнения)
|
||||
* [Создание облачной инфраструктуры](#создание-облачной-инфраструктуры)
|
||||
* [Создание Kubernetes кластера](#создание-Kubernetes-кластера)
|
||||
* [Создание тестового приложения](#создание-тестового-приложения)
|
||||
* [Подготовка cистемы мониторинга и деплой приложения](#подготовка-cистемы-мониторинга-и-деплой-приложения)
|
||||
* [Установка и настройка CI/CD](#установка-и-настройка-CI/CD)
|
||||
* [Что необходимо для сдачи задания?](#что-необходимо-для-сдачи-задания)
|
||||
|
||||
## Этапы выполнения
|
||||
|
||||
### Создание облачной инфраструктуры
|
||||
|
||||
Для начала необходимо подготовить облачную инфраструктуру в ЯО при помощи [Terraform](https://www.terraform.io/).
|
||||
|
||||
Особенности выполнения:
|
||||
|
||||
- Бюджет купона ограничен, что следует иметь в виду при проектировании инфраструктуры и использовании ресурсов;
|
||||
- Следует использовать последнюю стабильную версию [Terraform](https://www.terraform.io/).
|
||||
|
||||
Предварительная подготовка к установке и запуску Kubernetes кластера.
|
||||
|
||||
1. Создайте сервисный аккаунт, который будет в дальнейшем использоваться Terraform для работы с инфраструктурой с необходимыми и достаточными правами. Не стоит использовать права суперпользователя
|
||||
2. Подготовьте [backend](https://www.terraform.io/docs/language/settings/backends/index.html) для Terraform:
|
||||
а. Рекомендуемый вариант: [Terraform Cloud](https://app.terraform.io/)
|
||||
б. Альтернативный вариант: S3 bucket в созданном ЯО аккаунте
|
||||
3. Настройте [workspaces](https://www.terraform.io/docs/language/state/workspaces.html)
|
||||
а. Рекомендуемый вариант: создайте два workspace: *stage* и *prod*. В случае выбора этого варианта все последующие шаги должны учитывать факт существования нескольких workspace.
|
||||
б. Альтернативный вариант: используйте один workspace, назвав его *stage*. Пожалуйста, не используйте workspace, создаваемый Terraform-ом по-умолчанию (*default*).
|
||||
4. Создайте VPC с подсетями в разных зонах доступности.
|
||||
5. Убедитесь, что теперь вы можете выполнить команды `terraform destroy` и `terraform apply` без дополнительных ручных действий.
|
||||
6. В случае использования [Terraform Cloud](https://app.terraform.io/) в качестве [backend](https://www.terraform.io/docs/language/settings/backends/index.html) убедитесь, что применение изменений успешно проходит, используя web-интерфейс Terraform cloud.
|
||||
|
||||
Ожидаемые результаты:
|
||||
|
||||
1. Terraform сконфигурирован и создание инфраструктуры посредством Terraform возможно без дополнительных ручных действий.
|
||||
2. Полученная конфигурация инфраструктуры является предварительной, поэтому в ходе дальнейшего выполнения задания возможны изменения.
|
||||
|
||||
---
|
||||
|
||||
### Создание Kubernetes кластера
|
||||
|
||||
На этом этапе необходимо создать [Kubernetes](https://kubernetes.io/ru/docs/concepts/overview/what-is-kubernetes/) кластер на базе предварительно созданной инфраструктуры. Требуется обеспечить доступ к ресурсам из Интернета.
|
||||
|
||||
Это можно сделать двумя способами:
|
||||
|
||||
1. Рекомендуемый вариант: самостоятельная установка Kubernetes кластера.
|
||||
а. При помощи Terraform подготовить как минимум 3 виртуальных машины Compute Cloud для создания Kubernetes-кластера. Тип виртуальной машины следует выбрать самостоятельно с учётом требовании к производительности и стоимости. Если в дальнейшем поймете, что необходимо сменить тип инстанса, используйте Terraform для внесения изменений.
|
||||
б. Подготовить [ansible](https://www.ansible.com/) конфигурации, можно воспользоваться, например [Kubespray](https://kubernetes.io/docs/setup/production-environment/tools/kubespray/)
|
||||
в. Задеплоить Kubernetes на подготовленные ранее инстансы, в случае нехватки каких-либо ресурсов вы всегда можете создать их при помощи Terraform.
|
||||
2. Альтернативный вариант: воспользуйтесь сервисом [Yandex Managed Service for Kubernetes](https://cloud.yandex.ru/services/managed-kubernetes)
|
||||
а. С помощью terraform resource для [kubernetes](https://registry.terraform.io/providers/yandex-cloud/yandex/latest/docs/resources/kubernetes_cluster) создать региональный мастер kubernetes с размещением нод в разных 3 подсетях
|
||||
б. С помощью terraform resource для [kubernetes node group](https://registry.terraform.io/providers/yandex-cloud/yandex/latest/docs/resources/kubernetes_node_group)
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
1. Работоспособный Kubernetes кластер.
|
||||
2. В файле `~/.kube/config` находятся данные для доступа к кластеру.
|
||||
3. Команда `kubectl get pods --all-namespaces` отрабатывает без ошибок.
|
||||
|
||||
---
|
||||
|
||||
### Создание тестового приложения
|
||||
|
||||
Для перехода к следующему этапу необходимо подготовить тестовое приложение, эмулирующее основное приложение разрабатываемое вашей компанией.
|
||||
|
||||
Способ подготовки:
|
||||
|
||||
1. Рекомендуемый вариант:
|
||||
а. Создайте отдельный git репозиторий с простым nginx конфигом, который будет отдавать статические данные.
|
||||
б. Подготовьте Dockerfile для создания образа приложения.
|
||||
2. Альтернативный вариант:
|
||||
а. Используйте любой другой код, главное, чтобы был самостоятельно создан Dockerfile.
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
1. Git репозиторий с тестовым приложением и Dockerfile.
|
||||
2. Регистр с собранным docker image. В качестве регистра может быть DockerHub или [Yandex Container Registry](https://cloud.yandex.ru/services/container-registry), созданный также с помощью terraform.
|
||||
|
||||
---
|
||||
|
||||
### Подготовка системы мониторинга и деплой приложения
|
||||
|
||||
Уже должны быть готовы конфигурации для автоматического создания облачной инфраструктуры и поднятия Kubernetes кластера.
|
||||
Теперь необходимо подготовить конфигурационные файлы для настройки нашего Kubernetes кластера.
|
||||
|
||||
Цель:
|
||||
1. Задеплоить в кластер [prometheus](https://prometheus.io/), [grafana](https://grafana.com/), [alertmanager](https://github.com/prometheus/alertmanager), [экспортер](https://github.com/prometheus/node_exporter) основных метрик Kubernetes.
|
||||
2. Задеплоить тестовое приложение, например, [nginx](https://www.nginx.com/) сервер отдающий статическую страницу.
|
||||
|
||||
Рекомендуемый способ выполнения:
|
||||
1. Воспользовать пакетом [kube-prometheus](https://github.com/prometheus-operator/kube-prometheus), который уже включает в себя [Kubernetes оператор](https://operatorhub.io/) для [grafana](https://grafana.com/), [prometheus](https://prometheus.io/), [alertmanager](https://github.com/prometheus/alertmanager) и [node_exporter](https://github.com/prometheus/node_exporter). При желании можете собрать все эти приложения отдельно.
|
||||
2. Для организации конфигурации использовать [qbec](https://qbec.io/), основанный на [jsonnet](https://jsonnet.org/). Обратите внимание на имеющиеся функции для интеграции helm конфигов и [helm charts](https://helm.sh/)
|
||||
3. Если на первом этапе вы не воспользовались [Terraform Cloud](https://app.terraform.io/), то задеплойте в кластер [atlantis](https://www.runatlantis.io/) для отслеживания изменений инфраструктуры.
|
||||
|
||||
Альтернативный вариант:
|
||||
1. Для организации конфигурации можно использовать [helm charts](https://helm.sh/)
|
||||
|
||||
Ожидаемый результат:
|
||||
1. Git репозиторий с конфигурационными файлами для настройки Kubernetes.
|
||||
2. Http доступ к web интерфейсу grafana.
|
||||
3. Дашборды в grafana отображающие состояние Kubernetes кластера.
|
||||
4. Http доступ к тестовому приложению.
|
||||
|
||||
---
|
||||
|
||||
### Установка и настройка CI/CD
|
||||
|
||||
Осталось настроить ci/cd систему для автоматической сборки docker image и деплоя приложения при изменении кода.
|
||||
|
||||
Цель:
|
||||
|
||||
1. Автоматическая сборка docker образа при коммите в репозиторий с тестовым приложением.
|
||||
2. Автоматический деплой нового docker образа.
|
||||
|
||||
Можно использовать [teamcity](https://www.jetbrains.com/ru-ru/teamcity/), [jenkins](https://www.jenkins.io/) либо [gitlab ci](https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/)
|
||||
|
||||
Ожидаемый результат:
|
||||
|
||||
1. Интерфейс ci/cd сервиса доступен по http.
|
||||
2. При любом коммите в репозиторие с тестовым приложением происходит сборка и отправка в регистр Docker образа.
|
||||
3. При создании тега (например, v1.0.0) происходит сборка и отправка с соответствующим label в регистр, а также деплой соответствующего Docker образа в кластер Kubernetes.
|
||||
|
||||
---
|
||||
|
||||
## Что необходимо для сдачи задания?
|
||||
|
||||
1. Репозиторий с конфигурационными файлами Terraform и готовность продемонстрировать создание всех ресурсов с нуля.
|
||||
2. Пример pull request с комментариями созданными atlantis'ом или снимки экрана из Terraform Cloud.
|
||||
3. Репозиторий с конфигурацией ansible, если был выбран способ создания Kubernetes кластера при помощи ansible.
|
||||
4. Репозиторий с Dockerfile тестового приложения и ссылка на собранный docker image.
|
||||
5. Репозиторий с конфигурацией Kubernetes кластера.
|
||||
6. Ссылка на тестовое приложение и веб интерфейс Grafana с данными доступа.
|
||||
7. Все репозитории рекомендуется хранить на одном ресурсе (github, gitlab)
|
||||
@@ -1,4 +1,15 @@
|
||||
## Задание №2 - Описание жизненного цикла задачи
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/01-intro-01/README.md)
|
||||
по теме "Введение в DevOps".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||

|
||||
|
||||
### Задача 2
|
||||
|
||||
Описание жизненного цикла задачи:
|
||||
|
||||
1. Постановка задачи (менеджер)
|
||||
2. Оценка задачи (менеджер + разработчик)
|
||||
|
Before Width: | Height: | Size: 243 KiB After Width: | Height: | Size: 243 KiB |
@@ -1,6 +1,11 @@
|
||||
## gitignore
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/02-git-01-vcs/README.md)
|
||||
по теме "Системы контроля версий".
|
||||
|
||||
Создана директория [src/terraform](/src/terraform), добавлен файл: [terraform/.gitignore](/src/terraform/.gitignore).
|
||||
## Q/A
|
||||
|
||||
### Задача 1 - gitignore
|
||||
|
||||
Создана директория [src/terraform](./terraform), добавлен файл: [terraform/.gitignore](./terraform/.gitignore).
|
||||
|
||||
Файлы, которые будут проигнорированы git:
|
||||
|
||||
8
src/homework/02-git/2.2/readme.md
Normal file
@@ -0,0 +1,8 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/02-git-02-base/README.md)
|
||||
по теме "Основы Git".
|
||||
|
||||
### Other repos
|
||||
|
||||
В ходе выполнения ДЗ были созданы следующие репозитории-зеркала:
|
||||
- [gitlab](https://gitlab.com/Dannecron/netology-devops)
|
||||
- [bitbucket](https://bitbucket.org/dannecron/netology-devops/src/main/)
|
||||
9
src/homework/02-git/2.3/branching/merge.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# display command line options
|
||||
|
||||
count=1
|
||||
while [[ -n "$1" ]]; do
|
||||
echo "Parameter #$count = $1"
|
||||
count=$(( $count + 1 ))
|
||||
shift
|
||||
done
|
||||
10
src/homework/02-git/2.3/branching/rebase.sh
Normal file
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# display command line options
|
||||
|
||||
count=1
|
||||
for param in "$@"; do
|
||||
echo "Next parameter: $param"
|
||||
count=$(( $count + 1 ))
|
||||
done
|
||||
|
||||
echo "====="
|
||||
@@ -1 +1,2 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/02-git-01-vcs/README.md) по теме "Системы контроля версий".
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/02-git-03-branching/README.md)
|
||||
по теме "Ветвления в Git".
|
||||
120
src/homework/02-git/2.4/readme.md
Normal file
@@ -0,0 +1,120 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/02-git-04-tools/README.md)
|
||||
по теме "Инструменты Git".
|
||||
|
||||
## Git search
|
||||
|
||||
- Какому тегу соответствует коммит `85024d3`?
|
||||
|
||||
Для поиска информации о коммите можно использовать функцию `git show`. Вывод краткой информации о коммите в саму консоль:
|
||||
|
||||
```shell
|
||||
git --no-pager show --oneline -s 85024d3
|
||||
85024d310 (tag: v0.12.23) v0.12.23
|
||||
```
|
||||
|
||||
где: `--no-pager` - отключает открытие информации в отдельной утилите, `-s` - убирает информацию о `diff`.
|
||||
|
||||
Ответ: `v0.12.23`
|
||||
|
||||
- Сколько родителей у коммита `b8d720`? Напишите их хеши.
|
||||
|
||||
Есть два способа найти необходимую информацию: через `git show` или через `git log`.
|
||||
|
||||
Для вывода информации через `git show` нужно описать определённый формат для отображения:
|
||||
```shell
|
||||
git --no-pager show --pretty=format:"commit: %h%nparents: %p%n" -s b8d720
|
||||
commit: b8d720f83
|
||||
parents: 56cd7859e 9ea88f22f
|
||||
```
|
||||
|
||||
Для вывода информации через `git log` нужно по аналогии с `git show` описать формат для отображения:
|
||||
|
||||
```shell
|
||||
git --no-pager log --pretty="commit: %h%nparents: %p%n" --graph -n 1 b8d720
|
||||
* commit: b8d720f83
|
||||
|\ parents: 56cd7859e 9ea88f22f
|
||||
| |
|
||||
```
|
||||
|
||||
Ответ: 2 родителя с хэшами `56cd7859e` и `9ea88f22f`.
|
||||
|
||||
- Перечислите хеши и комментарии всех коммитов которые были сделаны между тегами `v0.12.23` и `v0.12.24`.
|
||||
|
||||
Вывод данной информации возможен через команду `git log`. Чтобы включить в отображение сам коммит, к которому создана первая версия, нужно добавить `^`.
|
||||
|
||||
```shell
|
||||
git log --oneline --graph v0.12.23^..v0.12.24
|
||||
* 33ff1c03b (tag: v0.12.24) v0.12.24
|
||||
* b14b74c49 [Website] vmc provider links
|
||||
* 3f235065b Update CHANGELOG.md
|
||||
* 6ae64e247 registry: Fix panic when server is unreachable
|
||||
* 5c619ca1b website: Remove links to the getting started guide's old location
|
||||
* 06275647e Update CHANGELOG.md
|
||||
* d5f9411f5 command: Fix bug when using terraform login on Windows
|
||||
* 4b6d06cc5 Update CHANGELOG.md
|
||||
* dd01a3507 Update CHANGELOG.md
|
||||
* 225466bc3 Cleanup after v0.12.23 release
|
||||
* 85024d310 (tag: v0.12.23) v0.12.23
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
| commit | comment |
|
||||
|-----------|-------------------------------------------------------------------|
|
||||
| b14b74c49 | [Website] vmc provider links |
|
||||
| 3f235065b | Update CHANGELOG.md |
|
||||
| 6ae64e247 | registry: Fix panic when server is unreachable |
|
||||
| 5c619ca1b | website: Remove links to the getting started guide's old location |
|
||||
| 06275647e | Update CHANGELOG.md |
|
||||
| d5f9411f5 | command: Fix bug when using terraform login on Windows |
|
||||
| 4b6d06cc5 | Update CHANGELOG.md |
|
||||
| dd01a3507 | Update CHANGELOG.md |
|
||||
| 225466bc3 | Cleanup after v0.12.23 release |
|
||||
|
||||
- Найдите коммит в котором была создана функция `func providerSource`, ее определение в коде выглядит так `func providerSource(...)` (вместо троеточия перечислены аргументы).
|
||||
|
||||
Для поиска самого раннего коммита воспользуемся возможностью команды `git log` искать содержимое по регулярному выражению (флаг `-G`).
|
||||
|
||||
```shell
|
||||
git --no-pager log --oneline -G"func providerSource(.*)"
|
||||
f5012c12d command/cliconfig: Installation methods, not installation sources
|
||||
5af1e6234 main: Honor explicit provider_installation CLI config when present
|
||||
8c928e835 main: Consult local directories as potential mirrors of providers
|
||||
```
|
||||
|
||||
Проверить правильность поиска можно посмотрев все изменения в коммите:
|
||||
```shell
|
||||
git show 8c928e835
|
||||
```
|
||||
|
||||
Ответ: `8c928e835`
|
||||
|
||||
- Найдите все коммиты в которых была изменена функция `globalPluginDirs`
|
||||
|
||||
По аналогии с предыдущим пунктом:
|
||||
|
||||
```shell
|
||||
git --no-pager log --oneline -G"func globalPluginDirs(.*)"
|
||||
8364383c3 Push plugin discovery down into command package
|
||||
```
|
||||
|
||||
Ответ: после добавления функции её изменений не было.
|
||||
|
||||
- Кто автор функции `synchronizedWriters`?
|
||||
|
||||
По аналогии с предыдущим пунктом используем функцию `git log`, только изменим формат отображения на `short`:
|
||||
|
||||
```shell
|
||||
git --no-pager log --pretty=short -G"func synchronizedWriters(.*)"
|
||||
commit bdfea50cc85161dea41be0fe3381fd98731ff786
|
||||
Author: James Bardin <j.bardin@gmail.com>
|
||||
|
||||
remove unused
|
||||
|
||||
commit 5ac311e2a91e381e2f52234668b49ba670aa0fe5
|
||||
Author: Martin Atkins <mart@degeneration.co.uk>
|
||||
|
||||
main: synchronize writes to VT100-faker on Windows
|
||||
```
|
||||
|
||||
Ответ: `Martin Atkins`
|
||||
17
src/homework/02-git/notes.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Git
|
||||
|
||||
## Git new commands
|
||||
|
||||
В git постепенно появляются алиасы, чтобы команды становились узконаправленными. Новые полезные команды:
|
||||
1. [`git restore`](https://git-scm.com/docs/git-restore) - восстановление файлов в рабочей директории (например, откат изменений как при использовании `git checkout -- .`)
|
||||
2. [`git switch`](https://git-scm.com/docs/git-switch) - переключение веток. Более узконаправленная команда, чем `git checkout` или `git branch`.
|
||||
|
||||
## Git rebase
|
||||
|
||||
Примерный порядок действий перебазирования веток в `main`:
|
||||
1. `git switch new-branch`
|
||||
2. `git rebase main`
|
||||
3. _optional_ исправление конфликтов, выполнение команды `git rebase --continue`
|
||||
4. `git checkout main`
|
||||
5. `git merge new-branch`
|
||||
6. `git branch -D new-branch`
|
||||
7
src/homework/03-sysadmin/3.1/double_square_braket.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
[[ -d /tmp ]]
|
||||
echo $?
|
||||
|
||||
[[ -d /tmp/some-dir ]]
|
||||
echo $?
|
||||
78
src/homework/03-sysadmin/3.1/readme.md
Normal file
@@ -0,0 +1,78 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-01-terminal/README.md)
|
||||
по теме "Работа в терминале, лекция 1".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
Какие ресурсы выделены для виртуальной машины по умолчанию?
|
||||
|
||||
```
|
||||
RAM: 1024 MB
|
||||
CPU: 2
|
||||
VRAM: 4 MB
|
||||
HDD: 64GB
|
||||
```
|
||||
|
||||
### Задача 2
|
||||
|
||||
Как добавить оперативной памяти или ресурсов процессора виртуальной машине?
|
||||
|
||||
В конфигурацию `Vargrantfile` необходимо добавить следующие строки:
|
||||
|
||||
```
|
||||
config.vm.provider "virtualbox" do |v|
|
||||
v.memory = 2048
|
||||
v.cpus = 4
|
||||
end
|
||||
```
|
||||
|
||||
### Задача 3
|
||||
|
||||
Исследование `man bash`.
|
||||
|
||||
> 1. какой переменной можно задать длину журнала history, и на какой строчке manual это описывается?
|
||||
|
||||
При помощи проставления переменной `HISTSIZE`. Описание находится на 591 строке мануала.
|
||||
Показать строки возможно при помощи указания кастомного пейджера для команды `man`:
|
||||
```shell
|
||||
man --pager="less -N" bash
|
||||
```
|
||||
|
||||
> 2. что делает директива `ignoreboth` в bash?
|
||||
|
||||
Значение `ignoreboth` проставляется для переменной `HISTCONTROL` и является сокращением для значений `ignorespace` и `ignoredups`.
|
||||
Конкретно это означает, что команды, которые начинаются с пробела или совпадают с предыдущей записью в истории, не попадут в файл истории `bash`.
|
||||
|
||||
> 3. в каких сценариях использования применимы скобки `{}` и на какой строчке man bash это описано?
|
||||
|
||||
Фигурные скобки используются при написании сложных команд (`Compound Commands`). Про это написано на 196 строке мануала.
|
||||
|
||||
> 4. Как создать однократным вызовом touch 100000 файлов? Получится ли аналогичным образом создать 300000? Если нет, то почему?
|
||||
|
||||
Создать множество файлов можно написанием сложной команды `touch {1..100000}`. Создать более `ARG_MAX` файлов в рамках одного запуска не получится.
|
||||
На текущей версии linux данная переменная равна 2097152.
|
||||
|
||||
> 5. Что делает конструкция `[[ -d /tmp ]]`
|
||||
|
||||
Возвращает 0, если `/tmp` существует и это директория, или 1, в ином случае.
|
||||
Скрипт, проверяющий данный ответ находится в файле [double_square_braket.sh](double_square_braket.sh).
|
||||
|
||||
> 4. Добейтесь в выводе type -a bash в виртуальной машине наличия первым пунктом в списке.
|
||||
|
||||
Подобного можно добиться, сделав три шага:
|
||||
* Создать новую директорию `mkdir /tmp/new_path_directory`
|
||||
* Сделать символическую ссылку для баша `ln -s /usr/bin/bash /tmp/new_path_directory/bash`
|
||||
* Изменив переменную окружения `PATH` с помощью `export PATH=/tmp/new_path_directory:${PATH}`
|
||||
|
||||
Таким образом вывод команды будет таким, как в задаче:
|
||||
```shell
|
||||
type -a bash
|
||||
bash is /tmp/new_path_directory/bash
|
||||
bash is /usr/bin/bash
|
||||
bash is /bin/bash
|
||||
```
|
||||
> 5. Чем отличается планирование команд с помощью `batch` и `at`?
|
||||
|
||||
Ничем, это одна и та же команда.
|
||||
|
||||
139
src/homework/03-sysadmin/3.2/readme.md
Normal file
@@ -0,0 +1,139 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-02-terminal/README.md)
|
||||
по теме "Работа в терминале, лекция 2".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Какого типа команда `cd`?
|
||||
|
||||
При вызове `type cd` будет выведено, что `cd is a shell builtin`, то есть команда встроенная в оболочку.
|
||||
Данная команда необходима для работы пользователя с терминалом и для работы других команд, именно поэтому она встроена в ядро операционной системы.
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Какая альтернатива без pipe команде `grep <some_string> <some_file> | wc -l`?
|
||||
|
||||
Альтернативой является команда `grep -c <some_string> <some_file>`.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Какой процесс с PID 1 является родителем для всех процессов в вашей виртуальной машине Ubuntu 20.04?
|
||||
|
||||
Найти родителя всех процессов можно вызвав команду `pstree -p | grep \(1\)`.
|
||||
Ответ: процесс с PID 1 - `systemd`.
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Как будет выглядеть команда, которая перенаправит вывод stderr `ls` на другую сессию терминала?
|
||||
|
||||
Откроем два терминала на виртуальной машине. Для каждого из них будет создан новый файл в директории `/dev/pts/`.
|
||||
Таким образом, если мы хотим перенаправить stderr команды, то нужно выполнить следующие действия:
|
||||
перенаправить вывод stderr в stdout, затем перенаправить stdout в `/dev/pts/<n>`, где <n> - целое число, идентификатор сессии терминала.
|
||||
Полный пример команды:
|
||||
|
||||
```shell
|
||||
ls /some/invalid/dir 2>&1 1>&/dev/pts/<n>
|
||||
```
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Получится ли одновременно передать команде файл на stdin и вывести ее stdout в другой файл?
|
||||
|
||||
Да, подобное можно реализовать следующим способом.
|
||||
|
||||
```shell
|
||||
touch in_file
|
||||
echo "abc" > in_file
|
||||
echo "bcd" >> in_file
|
||||
grep abc < in_file 1>&out_file
|
||||
cat out_file
|
||||
```
|
||||
|
||||
Содержимое out_file является выводом команды grep, а именно одна строка `abc`.
|
||||
|
||||
### Задача 6
|
||||
|
||||
> Получится ли находясь в графическом режиме, вывести данные из PTY в какой-либо из эмуляторов TTY? Сможете ли вы наблюдать выводимые данные?
|
||||
|
||||
Да, подобное возможно. Достаточно, например, открыть новую сессию терминала в графической оболочке и выполнить перенаправление потока в `/dev/pts/<n>`.
|
||||
Так же любая программа с графическим интерфейсом может выполнить данное действие. Наблюдать данные возможно,
|
||||
если открыт терминал, подключённый к конкретной сессии.
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Выполните команду `bash 5>&1`. К чему она приведет? Что будет, если вы выполните `echo netology > /proc/$$/fd/5`? Почему так происходит?
|
||||
|
||||
Насколько я понимаю, команда `bash 5>&1` создаёт новую терминальную сессию с перенаправлением потока из `5` в `1`, то есть в `stdout`.
|
||||
Но так как `5` - это не стандартный идентификатор потока, поэтому оболочка создаст новый файл `/proc/$$/fd/5`.
|
||||
Данный файл будет алиасом для stdout, поэтому выполнение `echo` выведет содержимое в терминал,
|
||||
как это сделал бы обычный вызов команды без перенаправлений потока.
|
||||
|
||||
### Задача 8
|
||||
|
||||
> Получится ли в качестве входного потока для pipe использовать только stderr команды, не потеряв при этом отображение stdout на pty?
|
||||
|
||||
Да, такая возможность есть. Сначала необходимо запустить терминал с новым перенаправлением, как в предыдущем вопросе: `bash 5>&1`.
|
||||
Затем уже можно выполнить перенаправление потоков следующим образом:
|
||||
|
||||
```shell
|
||||
ls -la /tmp/wrong_dir 2>&1 1>&5 | less
|
||||
```
|
||||
|
||||
Таким образом в команду `less` попадёт следующий текст: `ls: cannot access '/tmp/wrong_dir': No such file or directory`.
|
||||
|
||||
### Задача 9
|
||||
|
||||
> Что выведет команда `cat /proc/$$/environ`? Как еще можно получить аналогичный по содержанию вывод?
|
||||
|
||||
Данная команда выведет все переменные окружения, которые инициализированы в текущей сессии терминала.
|
||||
Аналогичный результат может быть достигнут вызовом команд `printenv` или `env`.
|
||||
|
||||
### Задача 11
|
||||
|
||||
> Используя `man`, опишите что доступно по адресам `/proc/<PID>/cmdline`, `/proc/<PID>/exe`.
|
||||
|
||||
Файл `/proc/<PID>/cmdline` - это файл, предназначенный только для чтения, который содержит в себе полную команду процесса, но только если процесс не зомби.
|
||||
Файл `/proc/<PID>/exe` - это файл-символическая ссылка, содержащая актуальный полный путь до выполняемой команды.
|
||||
|
||||
### Задача 12
|
||||
|
||||
> Узнайте, какую наиболее старшую версию набора инструкций SSE поддерживает ваш процессор с помощью `/proc/cpuinfo`
|
||||
|
||||
Выполним команду `cat /proc/cpuinfo | grep sse`. В выводе можно найти такие версии, как `sse`, `sse2`, `ssse3`, `sse4_1`, `sse4_2`.
|
||||
Таким образом, ответом на вопрос будет `sse4_2`.
|
||||
|
||||
### Задача 13
|
||||
|
||||
> Почему команда `ssh localhost 'tty'` внутри виртуальной машины выдаёт ошибку `not a tty`
|
||||
|
||||
Изначально, нужно понять, что происходит при выполнении исходной команды. Судя по мануалу команды `ssh`,
|
||||
`localhost` является пунктом назначения, куда будет произведено подключение,
|
||||
а `tty` - это команда, которая будет выполнена на удалённом сервере. Из того же мануала следует,
|
||||
что при указании команды будет произведено выполнение команды, а не логин пользователя. То есть,
|
||||
команда будет выполнена без выделения отдельной сессии для пользователя,
|
||||
а значит команда `tty` в данном случае действительно не сможет найти запущенный экземпляр терминала.
|
||||
|
||||
Изменить поведение можно принудительно заставив `ssh` создать новую сессию, для этого нужно добавить ключ `-t`,
|
||||
то есть выполнить команду `ssh -t localhost 'tty'`.
|
||||
|
||||
### Задача 14
|
||||
|
||||
> Необходимо переместить запущенный процесс из одной сессии в другую с использованием утилиты `reptyr`
|
||||
|
||||
На виртуальной машине утилита не установлена, поэтому установим её `sudo apt install reptyr`.
|
||||
В качестве примера запустим утилиту `screen`, которая запустит новую сессию внутри себя.
|
||||
В другом подключении через ssh к виртуальной машине запустим "долгий" процесс, например, `less .bash_history`.
|
||||
Внутри сессии `screen` найдём PID процесса `less`, для этого выполним команду `lsof | grep less`.
|
||||
Затем выполним команду `reptyr <PID>`.
|
||||
|
||||
К сожалению, полностью перенести процесс не получилось, есть какие-то странные ошибки по типу `Operation not permitted`
|
||||
или "зависания" второго терминала, если выполнить команду от администратора.
|
||||
|
||||
### Задача 15
|
||||
|
||||
> Узнайте что делает команда `tee` и почему в отличие от `sudo echo` команда с `sudo tee` будет работать.
|
||||
|
||||
Команда `tee` читает стандартный поток ввода и пишет его в стандартный поток вывода и файлы.
|
||||
В отличие от `echo` перенаправление идёт в рамках одного процесса, именно поэтому `sudo tee` будет работать,
|
||||
так как процесс берёт стандартный поток ввода от текущего пользователя, затем начинает запись с повышенными привилегиями.
|
||||
140
src/homework/03-sysadmin/3.3/readme.md
Normal file
@@ -0,0 +1,140 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-03-os/README.md)
|
||||
по теме "3.3. Операционные системы, лекция 1".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Какой системный вызов делает команда `cd`?
|
||||
|
||||
Для удобства поиска перенаправим вывод команды `strace` в утилиту `less`
|
||||
|
||||
```shell
|
||||
strace /bin/bash -c 'cd /tmp' 2>&1 | less
|
||||
```
|
||||
|
||||
Далее, найдём в трейсе упоминание директории `tmp`, указанной в команде. Таким образом, обнаружим, что команда `cd`
|
||||
делает системный вызов `chdir("/tmp")`.
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Используя `strace` выясните, где находится база данных `file` на основании которой она делает свои догадки.
|
||||
|
||||
При использовании команды `file` на любом файле, `strace` выведет такую строку:
|
||||
|
||||
```
|
||||
openat(AT_FDCWD, "/usr/share/misc/magic.mgc", O_RDONLY) = 3
|
||||
```
|
||||
|
||||
Предположительно, `/usr/share/misc/magic.mgc` - это и есть база данных для команды `file`.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Предложите способ обнуления открытого удаленного файла (чтобы освободить место на файловой системе)
|
||||
|
||||
Зная PID процесса и то, что каждый процесс пишет свои потоки в файлы в директории `/proc/<PID>/fd`.
|
||||
Таким образом, можно найти конкретный поток, который пишет в файл, вызвав команду
|
||||
|
||||
```shell
|
||||
ls -la /proc/1988/fd/
|
||||
|
||||
total 0
|
||||
dr-x------ 2 vagrant vagrant 0 Feb 16 03:25 .
|
||||
dr-xr-xr-x 9 vagrant vagrant 0 Feb 16 03:25 ..
|
||||
lrwx------ 1 vagrant vagrant 64 Feb 16 03:25 0 -> /dev/pts/0
|
||||
lrwx------ 1 vagrant vagrant 64 Feb 16 03:25 1 -> /dev/pts/0
|
||||
lrwx------ 1 vagrant vagrant 64 Feb 16 03:25 2 -> /dev/pts/0
|
||||
lr-x------ 1 vagrant vagrant 64 Feb 16 03:25 3 -> /tmp/do_not_delete_me
|
||||
```
|
||||
|
||||
В данном случае, поток под номером 3 держит соединение с искомым файлом. Таким образом, чтобы обнулить данный файл,
|
||||
достаточно направить пустую строку в данный поток:
|
||||
|
||||
```shell
|
||||
echo "" > /proc/1988/fd/3
|
||||
```
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Занимают ли зомби-процессы какие-то ресурсы в ОС (CPU, RAM, IO)?
|
||||
|
||||
Нет, zombie-процессы не используют никаких ресурсов операционной системы.
|
||||
Единственное, что они занимают - это идентификатор процесса.
|
||||
|
||||
### Задача 5
|
||||
|
||||
> На какие файлы вы увидели вызовы группы `open` за первую секунду работы утилиты `opensnoop-bpfcc`?
|
||||
|
||||
За первую секунду работы утилиты был произведён следующий вывод:
|
||||
|
||||
```shell
|
||||
sudo opensnoop-bpfcc
|
||||
|
||||
PID COMM FD ERR PATH
|
||||
833 vminfo 5 0 /var/run/utmp
|
||||
619 dbus-daemon -1 2 /usr/local/share/dbus-1/system-services
|
||||
619 dbus-daemon 19 0 /usr/share/dbus-1/system-services
|
||||
619 dbus-daemon -1 2 /lib/dbus-1/system-services
|
||||
619 dbus-daemon 19 0 /var/lib/snapd/dbus-1/system-services/
|
||||
```
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Какой системный вызов использует `uname -a`? Приведите цитату из man по этому системному вызову,
|
||||
> где описывается альтернативное местоположение в /proc, где можно узнать версию ядра и релиз ОС.
|
||||
|
||||
Утилита `uname` использует системный вызов `uname`, мануал к которому можно прочитать командой `man 2 uname`.
|
||||
|
||||
Цитата из мануала
|
||||
> Part of the utsname information is also accessible via /proc/sys/ker‐nel/{ostype, hostname, osrelease, version, domainname}.
|
||||
|
||||
PS. На виртуальной машине не было установленных мануалов для системных вызовов. Чтобы их установить,
|
||||
нужно выполнить команду `sudo apt install manpages-dev`.
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Чем отличается последовательность команд через `;` и через `&&` в bash?
|
||||
> Есть ли смысл использовать в bash `&&`, если применить `set -e`?
|
||||
|
||||
При использовании `&&` будет учитываться код выхода предыдущей выполненой команды. Если он не 0, то выполнение последовательности будет прервано.
|
||||
В случае же `;` последовательность будет выполнена в любом случае.
|
||||
|
||||
В случае `set -e` параметр имеет следующее определение
|
||||
|
||||
> When this option is on, if a simple command fails for any of the reasons listed in Consequences of Shell Errors or returns an exit status value >0,
|
||||
> and is not part of the compound list following a while, until, or if keyword,
|
||||
> and is not a part of an AND or OR list,
|
||||
> and is not a pipeline preceded by the ! reserved word, then the shell shall immediately exit.
|
||||
|
||||
То есть, если данная команда будет выполнена в связке с другими командами, то она не сможет прервать выполнение последовательности.
|
||||
Таким образом, для данной команды использование `&&` и `;` будет иметь одинаковое поведение.
|
||||
|
||||
### Задача 8
|
||||
|
||||
> Из каких опций состоит режим bash `set -euxo pipefail` и почему его хорошо было бы использовать в сценариях?
|
||||
|
||||
Параметр `-e` уже был рассмотрен в предыдущем вопросе.
|
||||
|
||||
`-u` - пишет ошибку в stderr, когда скрипт пытается обратиться к несуществующей переменной, но при этом не останавливает его работу.
|
||||
Если запущен интерактивный режим, то его работа никогда не прерывается.
|
||||
|
||||
`-x` - пишет в stderr трейс (последовательность выполнения системных вызовов) для каждой команды перед её выполнением.
|
||||
|
||||
`-o` - устанавливает настройку работы последовательности команд. В данном случае значение `pipefail` говорит о том,
|
||||
что статус работы всей последовательности будет значение последней команды с ненулевым кодом выхода или 0, если все команды выполнились успешно.
|
||||
|
||||
### Задача 9
|
||||
|
||||
> Используя `-o stat` для `ps`, определите, какой наиболее часто встречающийся статус у процессов в системе.
|
||||
|
||||
Выполним данную команду на виртуальной машине
|
||||
```shell
|
||||
ps -o stat
|
||||
|
||||
STAT
|
||||
Ss
|
||||
R+
|
||||
```
|
||||
|
||||
Таким образом, самым встречающимся статусом является `Ss`. Первая буква - это непосредственно статус (`S - interruptible sleep (waiting for an event to complete)`),
|
||||
вторая - дополнительная информация (`s - is a session leader`).
|
||||
234
src/homework/03-sysadmin/3.4/readme.md
Normal file
@@ -0,0 +1,234 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-04-os/README.md)
|
||||
по теме "3.4. Операционные системы, лекция 2".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Создайте самостоятельно простой unit-файл для [node_exporter](https://github.com/prometheus/node_exporter)
|
||||
|
||||
Для начала необходимо установить `node_exporter` в систему. Для этого воспользуемся [официальным гайдом](https://prometheus.io/docs/guides/node-exporter/#installing-and-running-the-node-exporter),
|
||||
а именно:
|
||||
|
||||
```shell
|
||||
wget https://github.com/prometheus/node_exporter/releases/download/v1.3.1/node_exporter-1.3.1.linux-amd64.tar.gz
|
||||
tar xvfz node_exporter-1.3.1.linux-amd64.tar.gz
|
||||
cd node_exporter-1.3.1.linux-amd64
|
||||
sudo mv node_exporter /usr/local/bin/
|
||||
```
|
||||
|
||||
Проверим, что всё работает, выполнив команду
|
||||
|
||||
```shell
|
||||
node_exporter --help
|
||||
|
||||
usage: node_exporter [<flags>]
|
||||
|
||||
Flags:
|
||||
-h, --help Show context-sensitive help (also try --help-long and --help-man).
|
||||
<...>
|
||||
```
|
||||
|
||||
Затем, создадим простой unit-файл по пути `/etc/systemd/system`:
|
||||
|
||||
```shell
|
||||
sudo touch /etc/systemd/system/prometheus_node_exporter.service
|
||||
```
|
||||
|
||||
И добавим в него следующее содержимое:
|
||||
|
||||
```unit file (systemd)
|
||||
[Unit]
|
||||
Description=Prometheuth node exporter service
|
||||
StartLimitIntervalSec=0
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
RestartSec=1
|
||||
User=vagrant
|
||||
ExecStart=/usr/local/bin/node_exporter
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
Затем запустим сервис, проверим, что он имеет статус "запущен", проверим работоспособность самого приложения и остановим выполнение:
|
||||
|
||||
```shell
|
||||
sudo systemctl start prometheus_node_exporter
|
||||
sudo systemctl status prometheus_node_exporter
|
||||
|
||||
● prometheus_node_exporter.service - Prometheuth node exporter service
|
||||
Loaded: loaded (/etc/systemd/system/prometheus_node_exporter.service; disabled; vendor preset: enabled)
|
||||
Active: active (running) since Wed 2022-02-23 03:52:14 UTC; 2s ago
|
||||
Main PID: 1520 (node_exporter)
|
||||
Tasks: 4 (limit: 1112)
|
||||
Memory: 2.2M
|
||||
CGroup: /system.slice/prometheus_node_exporter.service
|
||||
└─1520 /usr/local/bin/node_exporter
|
||||
|
||||
Feb 23 03:52:14 vagrant node_exporter[1520]: ts=2022-02-23T03:52:14.087Z caller=node_exporter.go:115 level=info collector=thermal_zone
|
||||
Feb 23 03:52:14 vagrant node_exporter[1520]: ts=2022-02-23T03:52:14.087Z caller=node_exporter.go:115 level=info collector=time
|
||||
<...>
|
||||
|
||||
curl -I http://localhost:9100/metrics
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: text/plain; version=0.0.4; charset=utf-8
|
||||
Date: Wed, 23 Feb 2022 04:01:31 GMT
|
||||
|
||||
sudo systemctl stop prometheus_node_exporter
|
||||
sudo systemctl status prometheus_node_exporter
|
||||
|
||||
sudo systemctl status prometheus_node_exporter
|
||||
● prometheus_node_exporter.service - Prometheuth node exporter service
|
||||
Loaded: loaded (/etc/systemd/system/prometheus_node_exporter.service; disabled; vendor preset: enabled)
|
||||
Active: inactive (dead)
|
||||
|
||||
<...>
|
||||
Feb 23 04:02:22 vagrant systemd[1]: Stopping Prometheuth node exporter service...
|
||||
Feb 23 04:02:22 vagrant systemd[1]: prometheus_node_exporter.service: Succeeded.
|
||||
Feb 23 04:02:22 vagrant systemd[1]: Stopped Prometheuth node exporter service.
|
||||
```
|
||||
|
||||
Для добавления конфигурации через файл, необходимо создать файл с переменной окружения `EXTRA_OPTS`:
|
||||
|
||||
```shell
|
||||
sudo mkdir -p /usr/local/lib/node_exporter
|
||||
sudo touch /usr/local/lib/node_exporter/conf.env
|
||||
sudo chmod +r /usr/local/lib/node_exporter/conf.env
|
||||
echo "EXTRA_OPTS=--collector.cpu.info" | sudo tee /usr/local/lib/node_exporter/conf.env
|
||||
```
|
||||
|
||||
Затем сделать изменения в unit-файле:
|
||||
* В блок `[Service]` добавить новую строку `EnvironmentFile=-/usr/local/lib/node_exporter/conf.env`
|
||||
* В ключ `ExecStart` после полного пути до приложения добавить вывод переменной окружения `$EXTRA_OPTS`
|
||||
|
||||
Далее проверить, что приложение запускается с дополнительным флагом:
|
||||
|
||||
```shell
|
||||
sudo systemctl start prometheus_node_exporter
|
||||
sudo systemctl status prometheus_node_exporter
|
||||
|
||||
● prometheus_node_exporter.service - Prometheuth node exporter service
|
||||
Loaded: loaded (/etc/systemd/system/prometheus_node_exporter.service; disabled; vendor preset: enabled)
|
||||
Active: active (running) since Wed 2022-02-23 04:18:07 UTC; 4s ago
|
||||
Main PID: 1820 (node_exporter)
|
||||
Tasks: 4 (limit: 1112)
|
||||
Memory: 2.3M
|
||||
CGroup: /system.slice/prometheus_node_exporter.service
|
||||
└─1820 /usr/local/bin/node_exporter --collector.cpu.info
|
||||
<...>
|
||||
```
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Приведите несколько опций, которые вы бы выбрали для базового мониторинга хоста по CPU, памяти, диску и сети.
|
||||
|
||||
По умолчанию в `node_exporter` включено достаточно много различной информации (~1000 строк). Поэтому, на мой взгляд,
|
||||
для начала стоит включить только следующие флаги:
|
||||
* `--collector.disable-defaults` - отключение всех коллекторов по умолчанию
|
||||
* `--collector.cpu` - отображение статистики по CPU
|
||||
* `--collector.filesystem` - отображение статистики по файловой системе (например, количество использованного места)
|
||||
* `--collector.meminfo` - отображение статистики по памяти
|
||||
* `--collector.os` - отображение информации об операционной системе
|
||||
* `--collector.time` - отображение информации о текущем системном времени
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Ознакомьтесь с метриками, которые по умолчанию собираются `Netdata`, и с комментариями, которые даны к этим метрикам.
|
||||
|
||||
`netdata` отображает следующие метрики:
|
||||
* `cpu` - утилизация CPU по всем ядрам
|
||||
* `load` - текущая загрузка системы (количество процессов, которые используют или ожидают различные системные ресурсы), поделённая на три усреднённых значения.
|
||||
* `disk` - текущие показатели I/O для физических дисков
|
||||
* `ram` - информация об оперативной памяти
|
||||
* `swap` - информация о файлах подкачки
|
||||
* `network` - информация о пропускной способности физических сетевых интерфейсов
|
||||
* и другие
|
||||
|
||||
Доступны так же подробные графики по каждой из категорий.
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Можно ли по выводу `dmesg` понять, осознает ли ОС, что загружена не на настоящем оборудовании, а на системе виртуализации?
|
||||
|
||||
В `dmesg` можно найти следующий вывод `Detected virtualization oracle.` от `systemd`. Таким образом да, можно понять, что система осознаёт,
|
||||
что находится внутри виртуальной машины, а не на физическом оборудовании.
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Как настроен `sysctl fs.nr_open` на системе по-умолчанию? Какой другой существующий лимит не позволит достичь такого числа?
|
||||
|
||||
```shell
|
||||
sysctl fs.nr_open
|
||||
fs.nr_open = 1048576
|
||||
```
|
||||
|
||||
`fs.nr_open` является системным лимитом на количество открытых файлов для пользователя.
|
||||
Для текущей сессии bash можно посмотреть и изменить ограничение при помощи команды `ulimit -n`.
|
||||
Для конкретных групп и пользователей ограничения можно задать в файле `/etc/security/limits.conf`.
|
||||
|
||||
### Задача 6
|
||||
|
||||
> Запустите любой долгоживущий процесс в отдельном неймспейсе процессов; покажите, что ваш процесс работает под PID 1 через `nsenter`.
|
||||
|
||||
Запустим процесс `bash` в изолированном пространстве имён:
|
||||
|
||||
```shell
|
||||
sudo unshare -f --pid --mount-proc /bin/bash
|
||||
ps aux
|
||||
|
||||
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||
root 1 0.2 0.3 8960 3988 pts/0 S 03:22 0:00 /bin/bash
|
||||
root 8 0.0 0.3 10616 3352 pts/0 R+ 03:22 0:00 ps aux
|
||||
```
|
||||
|
||||
Затем, в отдельной терминальной сессии подключимся к созданной сессии, для начала, найдя её в списке запущенных процессов через `ps`.
|
||||
|
||||
```shell
|
||||
ps au --forest -C unshare
|
||||
|
||||
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||
vagrant 2177 0.0 0.4 9092 4632 pts/1 Ss 03:23 0:00 -bash
|
||||
vagrant 2345 0.0 0.3 10616 3400 pts/1 R+ 03:34 0:00 \_ ps au --forest -C unshare
|
||||
vagrant 2081 0.0 0.4 9092 4512 pts/0 Ss 03:21 0:00 -bash
|
||||
root 2123 0.0 0.4 11016 4660 pts/0 S 03:22 0:00 \_ sudo unshare -f --pid --mount-proc /bin/bash
|
||||
root 2124 0.0 0.0 7232 528 pts/0 S 03:22 0:00 \_ unshare -f --pid --mount-proc /bin/bash
|
||||
root 2125 0.0 0.3 8960 3988 pts/0 S+ 03:22 0:00 \_ /bin/bash
|
||||
|
||||
sudo nsenter --target 2125 --pid --mount
|
||||
|
||||
ps aux
|
||||
|
||||
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
|
||||
root 1 0.0 0.3 8960 3988 pts/0 S+ 03:22 0:00 /bin/bash
|
||||
root 21 0.0 0.3 8960 4000 pts/1 S 03:35 0:00 -bash
|
||||
root 52 0.0 0.3 10616 3252 pts/1 R+ 03:36 0:00 ps aux
|
||||
```
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Найдите информацию о том, что такое `:(){ :|:& };:`.
|
||||
> Запустите эту команду в своей виртуальной машине. Некоторое время все будет "плохо", после чего (минуты) – ОС должна стабилизироваться.
|
||||
> Вызов `dmesg` расскажет, какой механизм помог автоматической стабилизации.
|
||||
> Как настроен этот механизм по-умолчанию, и как изменить число процессов, которое можно создать в сессии?
|
||||
|
||||
Команда - это `fork bomb`, которую можно разделить на следующие блоки:
|
||||
|
||||
```shell
|
||||
:() # define a function named :, () defines a function in bash
|
||||
{
|
||||
: | :; # the pipe needs two instances of this function, which forks two shells
|
||||
}
|
||||
; # end function definition
|
||||
: # run it
|
||||
```
|
||||
|
||||
В `dmesg` можно увидеть следующее сообщение:
|
||||
|
||||
```shell
|
||||
cgroup: fork rejected by pids controller in /user.slice/user-1000.slice/session-10.scope
|
||||
```
|
||||
|
||||
Число процессов можно изменить с помощью команды `ulimit -u <num>`.
|
||||
429
src/homework/03-sysadmin/3.5/readme.md
Normal file
@@ -0,0 +1,429 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-05-fs/README.md)
|
||||
по теме "3.5. Файловые системы".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Разряженные файлы
|
||||
|
||||
Суть таких файлов в том, чтобы разделить реальные данные последовательностью нуль-символов (`\x00`), которые не занимают реального места на физическом носителе.
|
||||
При этом сами данные записываются на разных фрагментах на физическом диске.
|
||||
|
||||
Например, если есть разряжённый файл размером в 4KB, то он может быть поделён на 4 блока по 1KB и записан на диск именно такими фрагментами.
|
||||
Это позволяет сделать запись туда, где доступно 1-2KB (например, после удаления другого файла),
|
||||
но куда оригинальный файл целиком не поместится.
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Могут ли файлы, являющиеся жесткой ссылкой на один объект, иметь разные права доступа и владельца? Почему?
|
||||
|
||||
Не могут. Причина в том, что жёсткие ссылки всегда ссылаются на одну `Inode` (идентификатор объекта файла внутри ОС).
|
||||
То есть, все файлы, которые имеют одну `Inode` будут синхронизированы по: содержимому, правам доступа и другим мета-данным.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Реконфигурация виртуальной машины
|
||||
|
||||
В текущую конфигурацию [`vagrant`](/src/vagrant/Vagrantfile) добавлена конфигурация дисков:
|
||||
|
||||
```
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
lvm_experiments_disk0_path = "/tmp/lvm_experiments_disk0.vmdk"
|
||||
lvm_experiments_disk1_path = "/tmp/lvm_experiments_disk1.vmdk"
|
||||
vb.customize ['createmedium', '--filename', lvm_experiments_disk0_path, '--size', 2560]
|
||||
vb.customize ['createmedium', '--filename', lvm_experiments_disk1_path, '--size', 2560]
|
||||
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium', lvm_experiments_disk0_path]
|
||||
vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 2, '--device', 0, '--type', 'hdd', '--medium', lvm_experiments_disk1_path]
|
||||
end
|
||||
```
|
||||
|
||||
После запуска ВМ (`vagrant up`) можно проверить, что файлы дисков из конфигурации были созданы:
|
||||
|
||||
```shell
|
||||
ls /tmp | grep lvm
|
||||
lvm_experiments_disk0.vmdk
|
||||
lvm_experiments_disk1.vmdk
|
||||
```
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Используя `fdisk`, разбейте первый диск на 2 раздела: 2 Гб, оставшееся пространство.
|
||||
|
||||
Найдём диски, которые были подключены на предыдущем шаге
|
||||
|
||||
```shell
|
||||
sudo fdisk -l
|
||||
|
||||
<...>
|
||||
|
||||
Disk /dev/sdb: 2.51 GiB, 2684354560 bytes, 5242880 sectors
|
||||
Disk model: VBOX HARDDISK
|
||||
Units: sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
|
||||
|
||||
Disk /dev/sdc: 2.51 GiB, 2684354560 bytes, 5242880 sectors
|
||||
Disk model: VBOX HARDDISK
|
||||
Units: sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
|
||||
<...>
|
||||
```
|
||||
|
||||
Проведём операцию на диске `/dev/sdb`. Утилита `fdisk` - интерактивная, поэтому вводить команды нужно непосредственно после её запуска.
|
||||
|
||||
```shell
|
||||
sudo fdisk /dev/sdb
|
||||
|
||||
Changes will remain in memory only, until you decide to write them.
|
||||
Be careful before using the write command.
|
||||
|
||||
Device does not contain a recognized partition table.
|
||||
Created a new DOS disklabel with disk identifier 0x0c6b10a9.
|
||||
|
||||
Command (m for help): g
|
||||
Created a new GPT disklabel (GUID: F75C9D4B-5A47-7540-8A8D-D7448C95BCE5).
|
||||
|
||||
Command (m for help): n
|
||||
Partition number (1-128, default 1): 1
|
||||
First sector (2048-5242846, default 2048): 2048
|
||||
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-5242846, default 5242846): +2G
|
||||
|
||||
Created a new partition 1 of type 'Linux filesystem' and of size 2 GiB.
|
||||
|
||||
Command (m for help): n
|
||||
Partition number (2-128, default 2): 2
|
||||
First sector (4196352-5242846, default 4196352):
|
||||
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4196352-5242846, default 5242846):
|
||||
|
||||
Created a new partition 2 of type 'Linux filesystem' and of size 511 MiB.
|
||||
|
||||
Command (m for help): w
|
||||
The partition table has been altered.
|
||||
Calling ioctl() to re-read partition table.
|
||||
Syncing disks.
|
||||
```
|
||||
|
||||
Проверим, что всё прошло успешно
|
||||
|
||||
```shell
|
||||
sudo fdisk -l /dev/sdb
|
||||
Disk /dev/sdb: 2.51 GiB, 2684354560 bytes, 5242880 sectors
|
||||
Disk model: VBOX HARDDISK
|
||||
Units: sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
Disklabel type: gpt
|
||||
Disk identifier: F75C9D4B-5A47-7540-8A8D-D7448C95BCE5
|
||||
|
||||
Device Start End Sectors Size Type
|
||||
/dev/sdb1 2048 4196351 4194304 2G Linux filesystem
|
||||
/dev/sdb2 4196352 5242846 1046495 511M Linux filesystem
|
||||
```
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Используя `sfdisk`, перенесите данную таблицу разделов на второй диск.
|
||||
|
||||
Перенесём таблицу разделов с `/dev/sdb` на `/dev/sdc`.
|
||||
|
||||
```shell
|
||||
sudo sfdisk --dump /dev/sdb > /tmp/sdb.dump
|
||||
|
||||
sudo sfdisk /dev/sdc < /tmp/sdb.dump
|
||||
|
||||
Checking that no-one is using this disk right now ... OK
|
||||
|
||||
Disk /dev/sdc: 2.51 GiB, 2684354560 bytes, 5242880 sectors
|
||||
Disk model: VBOX HARDDISK
|
||||
Units: sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
|
||||
>>> Script header accepted.
|
||||
>>> Script header accepted.
|
||||
>>> Script header accepted.
|
||||
>>> Script header accepted.
|
||||
>>> Script header accepted.
|
||||
>>> Script header accepted.
|
||||
>>> Created a new GPT disklabel (GUID: F75C9D4B-5A47-7540-8A8D-D7448C95BCE5).
|
||||
/dev/sdc1: Created a new partition 1 of type 'Linux filesystem' and of size 2 GiB.
|
||||
/dev/sdc2: Created a new partition 2 of type 'Linux filesystem' and of size 511 MiB.
|
||||
/dev/sdc3: Done.
|
||||
|
||||
New situation:
|
||||
Disklabel type: gpt
|
||||
Disk identifier: F75C9D4B-5A47-7540-8A8D-D7448C95BCE5
|
||||
|
||||
Device Start End Sectors Size Type
|
||||
/dev/sdc1 2048 4196351 4194304 2G Linux filesystem
|
||||
/dev/sdc2 4196352 5242846 1046495 511M Linux filesystem
|
||||
|
||||
The partition table has been altered.
|
||||
Calling ioctl() to re-read partition table.
|
||||
Syncing disks.
|
||||
```
|
||||
|
||||
Проверим, что всё прошло успешно
|
||||
|
||||
```shell
|
||||
sudo fdisk -l /dev/sdc
|
||||
Disk /dev/sdc: 2.51 GiB, 2684354560 bytes, 5242880 sectors
|
||||
Disk model: VBOX HARDDISK
|
||||
Units: sectors of 1 * 512 = 512 bytes
|
||||
Sector size (logical/physical): 512 bytes / 512 bytes
|
||||
I/O size (minimum/optimal): 512 bytes / 512 bytes
|
||||
Disklabel type: gpt
|
||||
Disk identifier: F75C9D4B-5A47-7540-8A8D-D7448C95BCE5
|
||||
|
||||
Device Start End Sectors Size Type
|
||||
/dev/sdc1 2048 4196351 4194304 2G Linux filesystem
|
||||
/dev/sdc2 4196352 5242846 1046495 511M Linux filesystem
|
||||
```
|
||||
|
||||
### Задача 6
|
||||
|
||||
> Соберите `mdadm` `RAID1` на паре разделов 2 Гб.
|
||||
|
||||
Разделы, которые необходимо объединить в `RAID1`: `/dev/sdb1` и `/dev/sdc1`.
|
||||
|
||||
```shell
|
||||
sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdb1 /dev/sdc1
|
||||
mdadm: Defaulting to version 1.2 metadata
|
||||
mdadm: array /dev/md0 started.
|
||||
```
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Соберите `mdadm` `RAID0` на второй паре маленьких разделов
|
||||
|
||||
Разделы, которые необходимо объединить в `RAID0`: `/dev/sdb2` и `/dev/sdc2`.
|
||||
|
||||
```shell
|
||||
sudo mdadm --create /dev/md1 --level=0 --raid-devices=2 /dev/sdb2 /dev/sdc2
|
||||
mdadm: Defaulting to version 1.2 metadata
|
||||
mdadm: array /dev/md1 started.
|
||||
```
|
||||
|
||||
### Задача 8
|
||||
|
||||
> Создайте 2 независимых PV на получившихся md-устройствах
|
||||
|
||||
```shell
|
||||
sudo pvcreate /dev/md0
|
||||
Physical volume "/dev/md0" successfully created.
|
||||
sudo pvcreate /dev/md1
|
||||
Physical volume "/dev/md1" successfully created.
|
||||
|
||||
sudo pvdisplay
|
||||
"/dev/md0" is a new physical volume of "<2.00 GiB"
|
||||
--- NEW Physical volume ---
|
||||
PV Name /dev/md0
|
||||
VG Name
|
||||
PV Size <2.00 GiB
|
||||
Allocatable NO
|
||||
PE Size 0
|
||||
Total PE 0
|
||||
Free PE 0
|
||||
Allocated PE 0
|
||||
PV UUID hNpE2z-h7m3-W1HT-dBIh-Xm24-6Zal-dhtPfl
|
||||
|
||||
"/dev/md1" is a new physical volume of "1017.00 MiB"
|
||||
--- NEW Physical volume ---
|
||||
PV Name /dev/md1
|
||||
VG Name
|
||||
PV Size 1017.00 MiB
|
||||
Allocatable NO
|
||||
PE Size 0
|
||||
Total PE 0
|
||||
Free PE 0
|
||||
Allocated PE 0
|
||||
PV UUID PB8hEQ-bZGA-M6Xe-tOQV-1CAb-DRSf-syoUSA
|
||||
```
|
||||
|
||||
### Задача 9
|
||||
|
||||
> Создайте общую volume-group на этих двух PV.
|
||||
|
||||
```shell
|
||||
sudo vgcreate test_vg /dev/md0 /dev/md1
|
||||
Volume group "test_vg" successfully created
|
||||
|
||||
sudo vgdisplay
|
||||
--- Volume group ---
|
||||
VG Name test_vg
|
||||
System ID
|
||||
Format lvm2
|
||||
Metadata Areas 2
|
||||
Metadata Sequence No 1
|
||||
VG Access read/write
|
||||
VG Status resizable
|
||||
MAX LV 0
|
||||
Cur LV 0
|
||||
Open LV 0
|
||||
Max PV 0
|
||||
Cur PV 2
|
||||
Act PV 2
|
||||
VG Size <2.99 GiB
|
||||
PE Size 4.00 MiB
|
||||
Total PE 765
|
||||
Alloc PE / Size 0 / 0
|
||||
Free PE / Size 765 / <2.99 GiB
|
||||
VG UUID 7xcN3Z-o9Ca-iXl2-5eao-iA4e-cEVJ-YgSajr
|
||||
```
|
||||
|
||||
### Задача 10
|
||||
|
||||
> Создайте LV размером 100 Мб, указав его расположение на PV с `RAID0`.
|
||||
|
||||
```shell
|
||||
sudo lvcreate --size=100MB test_vg /dev/md1
|
||||
Logical volume "lvol0" created.
|
||||
|
||||
sudo lvdisplay
|
||||
--- Logical volume ---
|
||||
LV Path /dev/test_vg/lvol0
|
||||
LV Name lvol0
|
||||
VG Name test_vg
|
||||
LV UUID 1fJJLO-zJeY-924D-O9gn-N1Cz-fyQ6-mnA7Z3
|
||||
LV Write Access read/write
|
||||
LV Creation host, time vagrant, 2022-03-02 02:55:17 +0000
|
||||
LV Status available
|
||||
# open 0
|
||||
LV Size 100.00 MiB
|
||||
Current LE 25
|
||||
Segments 1
|
||||
Allocation inherit
|
||||
Read ahead sectors auto
|
||||
- currently set to 4096
|
||||
Block device 253:1
|
||||
```
|
||||
|
||||
### Задача 11
|
||||
|
||||
> Создайте `mkfs.ext4` ФС на получившемся `LV`
|
||||
|
||||
```shell
|
||||
sudo mkfs.ext4 /dev/test_vg/lvol0
|
||||
mke2fs 1.45.5 (07-Jan-2020)
|
||||
Creating filesystem with 25600 4k blocks and 25600 inodes
|
||||
|
||||
Allocating group tables: done
|
||||
Writing inode tables: done
|
||||
Creating journal (1024 blocks): done
|
||||
Writing superblocks and filesystem accounting information: done
|
||||
```
|
||||
|
||||
### Задача 12
|
||||
|
||||
> Смонтируйте этот раздел в любую директорию.
|
||||
|
||||
```shell
|
||||
mkdir /tmp/new
|
||||
sudo mount /dev/test_vg/lvol0 /tmp/new
|
||||
```
|
||||
|
||||
### Задача 13
|
||||
|
||||
> Поместите туда тестовый файл.
|
||||
|
||||
```shell
|
||||
sudo wget https://mirror.yandex.ru/ubuntu/ls-lR.gz -O /tmp/new/test.gz
|
||||
|
||||
--2022-03-02 03:04:07-- https://mirror.yandex.ru/ubuntu/ls-lR.gz
|
||||
Resolving mirror.yandex.ru (mirror.yandex.ru)... 213.180.204.183, 2a02:6b8::183
|
||||
Connecting to mirror.yandex.ru (mirror.yandex.ru)|213.180.204.183|:443... connected.
|
||||
HTTP request sent, awaiting response... 200 OK
|
||||
Length: 22388361 (21M) [application/octet-stream]
|
||||
Saving to: ‘/tmp/new/test.gz’
|
||||
|
||||
2022-03-02 03:04:09 (9.33 MB/s) - ‘/tmp/new/test.gz’ saved [22388361/22388361]
|
||||
```
|
||||
|
||||
### Задача 14
|
||||
|
||||
> Прикрепите вывод `lsblk`.
|
||||
|
||||
```shell
|
||||
sudo lsblk
|
||||
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
|
||||
loop0 7:0 0 55.4M 1 loop /snap/core18/2128
|
||||
loop2 7:2 0 70.3M 1 loop /snap/lxd/21029
|
||||
loop3 7:3 0 55.5M 1 loop /snap/core18/2284
|
||||
loop4 7:4 0 43.6M 1 loop /snap/snapd/14978
|
||||
loop5 7:5 0 61.9M 1 loop /snap/core20/1361
|
||||
loop6 7:6 0 67.9M 1 loop /snap/lxd/22526
|
||||
sda 8:0 0 64G 0 disk
|
||||
├─sda1 8:1 0 1M 0 part
|
||||
├─sda2 8:2 0 1G 0 part /boot
|
||||
└─sda3 8:3 0 63G 0 part
|
||||
└─ubuntu--vg-ubuntu--lv 253:0 0 31.5G 0 lvm /
|
||||
sdb 8:16 0 2.5G 0 disk
|
||||
├─sdb1 8:17 0 2G 0 part
|
||||
│ └─md0 9:0 0 2G 0 raid1
|
||||
└─sdb2 8:18 0 511M 0 part
|
||||
└─md1 9:1 0 1017M 0 raid0
|
||||
└─test_vg-lvol0 253:1 0 100M 0 lvm /tmp/new
|
||||
sdc 8:32 0 2.5G 0 disk
|
||||
├─sdc1 8:33 0 2G 0 part
|
||||
│ └─md0 9:0 0 2G 0 raid1
|
||||
└─sdc2 8:34 0 511M 0 part
|
||||
└─md1 9:1 0 1017M 0 raid0
|
||||
└─test_vg-lvol0 253:1 0 100M 0 lvm /tmp/new
|
||||
```
|
||||
|
||||
### Задача 15
|
||||
|
||||
> Протестируйте целостность файла
|
||||
|
||||
```shell
|
||||
sudo gzip -t /tmp/new/test.gz
|
||||
echo $?
|
||||
|
||||
0
|
||||
```
|
||||
|
||||
### Задача 16
|
||||
|
||||
> Используя `pvmove`, переместите содержимое PV с `RAID0` на `RAID1`.
|
||||
|
||||
```shell
|
||||
sudo pvmove /dev/md1 /dev/md0
|
||||
/dev/md1: Moved: 16.00%
|
||||
/dev/md1: Moved: 100.00%
|
||||
```
|
||||
|
||||
### Задача 17
|
||||
|
||||
> Сделайте `--fail` на устройство в вашем `RAID1` md.
|
||||
|
||||
```shell
|
||||
sudo mdadm --fail /dev/md0 /dev/sdb1
|
||||
mdadm: set /dev/sdb1 faulty in /dev/md0
|
||||
```
|
||||
|
||||
### Задача 18
|
||||
|
||||
> Подтвердите выводом `dmesg`, что `RAID1` работает в деградированном состоянии
|
||||
|
||||
```shell
|
||||
sudo dmesg | tail -n 5
|
||||
[ 2711.025093] 02:44:35.853387 timesync vgsvcTimeSyncWorker: Radical guest time change: 81 954 170 807 000ns (GuestNow=1 646 189 075 853 355 000 ns GuestLast=1 646 107 121 682 548 000 ns fSetTimeLastLoop=true )
|
||||
[ 3814.237420] EXT4-fs (dm-1): mounted filesystem with ordered data mode. Opts: (null)
|
||||
[ 3814.237429] ext4 filesystem being mounted at /tmp/new supports timestamps until 2038 (0x7fffffff)
|
||||
[ 4328.371203] md/raid1:md0: Disk failure on sdb1, disabling device.
|
||||
md/raid1:md0: Operation continuing on 1 devices.
|
||||
```
|
||||
|
||||
### Задача 19
|
||||
|
||||
> Протестируйте целостность файла, несмотря на "сбойный" диск он должен продолжать быть доступен
|
||||
|
||||
```shell
|
||||
sudo gzip -t /tmp/new/test.gz
|
||||
echo $?
|
||||
0
|
||||
```
|
||||
231
src/homework/03-sysadmin/3.6/readme.md
Normal file
@@ -0,0 +1,231 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-06-net/README.md)
|
||||
по теме "3.6. Компьютерные сети, лекция 1".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Работа c HTTP через телнет.
|
||||
|
||||
```shell
|
||||
telnet stackoverflow.com 80
|
||||
Trying 151.101.1.69...
|
||||
Connected to stackoverflow.com.
|
||||
Escape character is '^]'.
|
||||
GET /questions HTTP/1.0
|
||||
HOST: stackoverflow.com
|
||||
|
||||
HTTP/1.1 301 Moved Permanently
|
||||
cache-control: no-cache, no-store, must-revalidate
|
||||
location: https://stackoverflow.com/questions
|
||||
x-request-guid: 345ccd64-092f-4bed-a6bb-f55d978ab74a
|
||||
feature-policy: microphone 'none'; speaker 'none'
|
||||
content-security-policy: upgrade-insecure-requests; frame-ancestors 'self' https://stackexchange.com
|
||||
Accept-Ranges: bytes
|
||||
Date: Wed, 09 Mar 2022 02:50:15 GMT
|
||||
Via: 1.1 varnish
|
||||
Connection: close
|
||||
X-Served-By: cache-hhn4072-HHN
|
||||
X-Cache: MISS
|
||||
X-Cache-Hits: 0
|
||||
X-Timer: S1646794215.885878,VS0,VE156
|
||||
Vary: Fastly-SSL
|
||||
X-DNS-Prefetch-Control: off
|
||||
Set-Cookie: prov=a65f8cdf-ae94-33aa-2d32-b4ba53feba61; domain=.stackoverflow.com; expires=Fri, 01-Jan-2055 00:00:00 GMT; path=/; HttpOnly
|
||||
```
|
||||
|
||||
В ответ пришёл статус код `301`, который означает постоянное перемещение с запрашиваемой страницы.
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Повторите задание 1 в браузере, используя консоль разработчика F12.
|
||||
|
||||
Код ответа при заходе на сайт `http://stackoverflow.com`: `Status Code: 307 Internal Redirect`.
|
||||
Наиболее долгих по загрузке запрос - это запрос на загрузку самой страницы после редиректа (`https://stackoverflow.com/`).
|
||||
Скриншот консоли браузера:
|
||||
|
||||

|
||||
|
||||
### Задача 3
|
||||
|
||||
> Какой IP адрес у вас в интернете?
|
||||
|
||||
Чтобы узнать текущий внешний ip-адрес, можно воспользоваться онлайн сервисами, такими как [myip.com](https://www.myip.com/) или [whoer.net](https://whoer.net/).
|
||||
В дополнение к этому, можно использовать только консоль и команду `dig`:
|
||||
|
||||
```shell
|
||||
dig +short myip.opendns.com @resolver1.opendns.com
|
||||
46.181.144.146
|
||||
|
||||
dig TXT +short o-o.myaddr.l.google.com @ns1.google.com
|
||||
"46.181.144.146"
|
||||
```
|
||||
|
||||
Ответ: `46.181.144.146`.
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Какому провайдеру принадлежит ваш IP адрес? Какой автономной системе AS?
|
||||
|
||||
```shell
|
||||
whois 46.181.144.146
|
||||
|
||||
<...>
|
||||
|
||||
% Information related to '46.181.128.0/18AS39927'
|
||||
|
||||
route: 46.181.128.0/18
|
||||
descr: Goodline.info
|
||||
origin: AS39927
|
||||
mnt-by: ELT-MNT
|
||||
created: 2010-12-22T11:28:33Z
|
||||
last-modified: 2010-12-22T11:28:33Z
|
||||
source: RIPE
|
||||
```
|
||||
|
||||
Ответ: оператор - `Goodline.info`, AS - `AS39927`.
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Через какие сети проходит пакет, отправленный с вашего компьютера на адрес 8.8.8.8? Через какие AS?
|
||||
|
||||
По умолчанию утилита `traceroute` не установлена в системе ubuntu. Для её установки достаточно выполнить команду
|
||||
|
||||
```shell
|
||||
sudo apt install traceroute
|
||||
```
|
||||
|
||||
Ответ:
|
||||
|
||||
```shell
|
||||
traceroute -A 8.8.8.8
|
||||
traceroute to 8.8.8.8 (8.8.8.8), 30 hops max, 60 byte packets
|
||||
1 router.asus.com (192.168.1.1) [*] 5.960 ms 3.192 ms 3.129 ms
|
||||
2 46-181-144-129.shah-95-kmr-cr01-6500.net.elt (46.181.144.129) [AS39927] 6.523 ms 6.484 ms 6.446 ms
|
||||
3 172.16.23.174 (172.16.23.174) [*] 6.577 ms 6.539 ms 6.502 ms
|
||||
4 172-16-22-178.kras-136-kmr-asbr01-asr.net.elt (172.16.22.178) [*] 5.984 ms 10.393 ms 10.354 ms
|
||||
5 host_91_221_180_4.milecom.ru (91.221.180.4) [AS13094] 56.869 ms 56.832 ms 56.796 ms
|
||||
6 108.170.250.34 (108.170.250.34) [AS15169] 54.496 ms 51.426 ms 108.170.250.66 (108.170.250.66) [AS15169] 51.329 ms
|
||||
7 * * *
|
||||
8 108.170.232.251 (108.170.232.251) [AS15169] 72.746 ms 72.14.238.168 (72.14.238.168) [AS15169] 62.523 ms 172.253.65.82 (172.253.65.82) [AS15169] 64.747 ms
|
||||
9 216.239.46.139 (216.239.46.139) [AS15169] 68.148 ms 72.14.236.73 (72.14.236.73) [AS15169] 69.164 ms 172.253.51.241 (172.253.51.241) [AS15169] 69.121 ms
|
||||
10 * * *
|
||||
11 * * *
|
||||
12 * * *
|
||||
13 * * *
|
||||
14 * * *
|
||||
15 * * *
|
||||
16 * * *
|
||||
17 * * *
|
||||
18 * * *
|
||||
19 * dns.google (8.8.8.8) [AS15169] 57.661 ms 56.687 ms
|
||||
```
|
||||
|
||||
### Задача 6
|
||||
|
||||
> Повторите задание 5 в утилите mtr. На каком участке наибольшая задержка - delay?
|
||||
|
||||
```shell
|
||||
mtr -w 8.8.8.8
|
||||
Start: 2022-03-09T10:25:09+0700
|
||||
HOST: host Loss% Snt Last Avg Best Wrst StDev
|
||||
1.|-- router.asus.com 0.0% 10 2.9 3.3 2.5 4.6 0.8
|
||||
2.|-- 46-181-144-129.shah-95-kmr-cr01-6500.net.elt 0.0% 10 4.0 4.0 2.5 5.5 1.1
|
||||
3.|-- 172.16.23.174 0.0% 10 4.1 4.4 3.1 6.0 0.9
|
||||
4.|-- 172-16-22-178.kras-136-kmr-asbr01-asr.net.elt 0.0% 10 4.3 4.2 3.5 5.5 0.6
|
||||
5.|-- host_91_221_180_4.milecom.ru 0.0% 10 57.9 55.7 54.5 57.9 1.0
|
||||
6.|-- 108.170.250.146 0.0% 10 55.7 55.4 54.4 56.4 0.7
|
||||
7.|-- 209.85.249.158 50.0% 10 58.7 63.8 58.7 81.0 9.6
|
||||
8.|-- 216.239.57.222 0.0% 10 62.5 62.3 61.5 64.3 0.9
|
||||
9.|-- 142.250.238.181 0.0% 10 66.4 66.0 65.3 66.9 0.5
|
||||
10.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
11.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
12.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
13.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
14.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
15.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
16.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
17.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
18.|-- ??? 100.0 10 0.0 0.0 0.0 0.0 0.0
|
||||
19.|-- dns.google 0.0% 10 58.5 58.5 57.6 60.3 0.8
|
||||
```
|
||||
|
||||
Наибольшая задержка была на 7-ом хопе с худшим показателем в 81ms.
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Какие DNS сервера отвечают за доменное имя `dns.google`? Какие A записи?
|
||||
|
||||
```shell
|
||||
dig dns.google
|
||||
|
||||
; <<>> DiG 9.16.1-Ubuntu <<>> dns.google
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 59679
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 1
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 65494
|
||||
;; QUESTION SECTION:
|
||||
;dns.google. IN A
|
||||
|
||||
;; ANSWER SECTION:
|
||||
dns.google. 370 IN A 8.8.4.4
|
||||
dns.google. 370 IN A 8.8.8.8
|
||||
|
||||
;; Query time: 8 msec
|
||||
;; SERVER: 127.0.0.53#53(127.0.0.53)
|
||||
;; WHEN: Ср мар 09 10:28:19 +07 2022
|
||||
;; MSG SIZE rcvd: 71
|
||||
```
|
||||
|
||||
Ответ: dns-сервера с ip-адресами `8.8.4.4` и `8.8.8.8`. Оба адреса являются `A`-записями.
|
||||
|
||||
### Задача 9
|
||||
|
||||
> Проверьте PTR записи для IP адресов из задания 7. Какое доменное имя привязано к IP?
|
||||
|
||||
```shell
|
||||
dig -x 8.8.4.4
|
||||
|
||||
; <<>> DiG 9.16.1-Ubuntu <<>> -x 8.8.4.4
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 33111
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 65494
|
||||
;; QUESTION SECTION:
|
||||
;4.4.8.8.in-addr.arpa. IN PTR
|
||||
|
||||
;; ANSWER SECTION:
|
||||
4.4.8.8.in-addr.arpa. 53775 IN PTR dns.google.
|
||||
|
||||
;; Query time: 4 msec
|
||||
;; SERVER: 127.0.0.53#53(127.0.0.53)
|
||||
;; WHEN: Ср мар 09 10:33:09 +07 2022
|
||||
;; MSG SIZE rcvd: 73
|
||||
|
||||
dig -x 8.8.8.8
|
||||
|
||||
; <<>> DiG 9.16.1-Ubuntu <<>> -x 8.8.8.8
|
||||
;; global options: +cmd
|
||||
;; Got answer:
|
||||
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3893
|
||||
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
|
||||
|
||||
;; OPT PSEUDOSECTION:
|
||||
; EDNS: version: 0, flags:; udp: 65494
|
||||
;; QUESTION SECTION:
|
||||
;8.8.8.8.in-addr.arpa. IN PTR
|
||||
|
||||
;; ANSWER SECTION:
|
||||
8.8.8.8.in-addr.arpa. 6386 IN PTR dns.google.
|
||||
|
||||
;; Query time: 4 msec
|
||||
;; SERVER: 127.0.0.53#53(127.0.0.53)
|
||||
;; WHEN: Ср мар 09 10:33:34 +07 2022
|
||||
;; MSG SIZE rcvd: 73
|
||||
```
|
||||
BIN
src/homework/03-sysadmin/3.6/stackoverflow.png
Normal file
|
After Width: | Height: | Size: 349 KiB |
133
src/homework/03-sysadmin/3.7/readme.md
Normal file
@@ -0,0 +1,133 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-07-net/README.md)
|
||||
по теме "3.7. Компьютерные сети, лекция 2".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Проверьте список доступных сетевых интерфейсов на вашем компьютере. Какие команды есть для этого в Linux и в Windows?
|
||||
|
||||
В Linux можно выполнить команду `ip link`, которая покажет все сетевые интерфейсы, включая виртуальные.
|
||||
Для Windows аналогом данной команды является вызов `ipconfig /all`.
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Какой протокол используется для распознавания соседа по сетевому интерфейсу? Какой пакет и команды есть в Linux для этого?
|
||||
|
||||
Для получения информации о соседнем устройстве используется протокол [LLDP](https://en.wikipedia.org/wiki/Link_Layer_Discovery_Protocol).
|
||||
Данный протокол в ОС Linux реализован в утилите `lldpd`.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Какая технология используется для разделения L2 коммутатора на несколько виртуальных сетей? Какой пакет и команды есть в Linux для этого? Приведите пример конфига.
|
||||
|
||||
Технология имеет название [VLAN](https://en.wikipedia.org/wiki/Virtual_LAN).
|
||||
В Linux для управления виртуальными сетями используется пакет `vlan`, а именно утилита `vconfig` (устарела, рекомендуется использовать утилиту `ip route`).
|
||||
|
||||
Вся конфигурация так или иначе хранится в файле `/etc/network/interfaces`, содержимое которого будет выглядеть примерно так
|
||||
|
||||
```
|
||||
auto eth0.1400
|
||||
iface eth0.1400 inet static
|
||||
address 192.168.1.1
|
||||
netmask 255.255.255.0
|
||||
vlan_raw_device eth0
|
||||
```
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Какие типы агрегации интерфейсов есть в Linux? Какие опции есть для балансировки нагрузки? Приведите пример конфига.
|
||||
|
||||
В конфигурации Linux предусмотрены следующие типы агрегации:
|
||||
* static/manual - ручная статическая настройка
|
||||
* dynamic - динамическая
|
||||
|
||||
Типы балансировки нагрузки:
|
||||
* `balance-rr` - балансировка по принципу [Round-robin](https://en.wikipedia.org/wiki/Round-robin_scheduling).
|
||||
* `active-backup` - один активный интерфейс, в случае выхода из строя которого, трафик перенаправляется на запасной.
|
||||
* `balance-xor` - "эксклюзивный ИЛИ", при котором происходит сопоставление MAC-адреса из запроса к MAC-адресу интерфейса. Эта связь используется в дальнейшем для всех запросов.
|
||||
* `broadcase` - транслирование запроса на все интерфейсы.
|
||||
* `802.3ad` - динамическая политика агрегации по стандарту [IEEE 802.3ad](https://www.ieee802.org/3/hssg/public/apr07/frazier_01_0407.pdf)
|
||||
* `balance-tlb` - балансировка трафика в зависимости от нагрузки интерфейсов.
|
||||
* `balance-alb` - надстройка над `balance-tlb`, убирающая необходимость специальной поддержки со стороны интерфейса.
|
||||
|
||||
Настройка производится в том же файле `/etc/network/interfaces`. Пример конфигурации с балансировкой нагрузки по типу `active-backup`,
|
||||
когда основным является интерфейс `eth0`, а `wlan0` - запасным, который активируется при выходе из строя основного:
|
||||
|
||||
```
|
||||
# Define slaves
|
||||
auto eth0
|
||||
iface eth0 inet manual
|
||||
bond-master bond0
|
||||
bond-primary eth0
|
||||
bond-mode active-backup
|
||||
|
||||
auto wlan0
|
||||
iface wlan0 inet manual
|
||||
wpa-conf /etc/network/wpa.conf
|
||||
bond-master bond0
|
||||
bond-primary eth0
|
||||
bond-mode active-backup
|
||||
|
||||
# Define master
|
||||
auto bond0
|
||||
iface bond0 inet dhcp
|
||||
bond-slaves none
|
||||
bond-primary eth0
|
||||
bond-mode active-backup
|
||||
bond-miimon 100
|
||||
```
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Сколько IP адресов в сети с маской `/29` ? Сколько `/29` подсетей можно получить из сети с маской `/24`. Приведите несколько примеров `/29` подсетей внутри сети `10.10.10.0/24`.
|
||||
|
||||
Всего в сети с маской `/29` находятся 8 IP адресов, 2 из которых зарезервированы.
|
||||
|
||||
Всего в сети с маской `/24` могут использоваться 256 IP адреса.
|
||||
Таким образом, если на каждую сеть с маской `/29` нужен пул из 8 адресов,
|
||||
то в оригинальную сеть поместится 256 / 8 = 32 подсети с маской `/29`.
|
||||
|
||||
Примеры подсетей с маской `/29` внутри сети `10.10.10.0/24`:
|
||||
* `10.10.10.1/29`
|
||||
* `10.10.10.9/29`
|
||||
* `10.10.10.19/29`
|
||||
|
||||
### Задача 6
|
||||
|
||||
> Задача: вас попросили организовать стык между 2-мя организациями. Диапазоны `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` уже заняты. Из какой подсети допустимо взять частные IP адреса? Маску выберите из расчета максимум 40-50 хостов внутри подсети.
|
||||
|
||||
Так как самые ходовые сети, используемые для внутренних сетей, уже заняты, то остаётся только вариант с сетью `100.64.0.0/10`.
|
||||
Если рассчитывать на 40-50 хостов в рамках данной сети, то можно взять подсеть `100.64.0.0/26`,
|
||||
у которой будет диапазон доступных адресов `100.64.0.1 - 100.64.0.62`.
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Как проверить ARP таблицу в Linux, Windows? Как очистить ARP кеш полностью? Как из ARP таблицы удалить только один нужный IP?
|
||||
|
||||
Работа с ARP-таблицами:
|
||||
* linux
|
||||
|
||||
```shell
|
||||
# вывод данных таблицы
|
||||
ip neigh
|
||||
# полная очистка
|
||||
ip neigh flush all
|
||||
# удаление одного ip-адреса
|
||||
ip neigh del <ip-address> lladdr <link-layer address> <device>
|
||||
# пример удаления адреса
|
||||
ip neigh del 192.168.1.3 lladdr 02:01:02:03:04:05 dev eth0
|
||||
```
|
||||
|
||||
* windows
|
||||
|
||||
```shell
|
||||
# вывод данных таблицы
|
||||
arp -a
|
||||
# полная очистка
|
||||
arp -d *
|
||||
# удаление одного ip-адреса
|
||||
arp -d <ip-address>
|
||||
# пример удаление адреса
|
||||
arp -d 192.168.1.3
|
||||
```
|
||||
BIN
src/homework/03-sysadmin/3.8/network.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
1
src/homework/03-sysadmin/3.8/network.xml
Normal file
@@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2022-03-17T03:03:17.629Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36 OPR/83.0.4254.70" etag="NbVIYXF-muBXRySHjtu2" version="14.6.7" type="device"><diagram name="Page-1" id="c37626ed-c26b-45fb-9056-f9ebc6bb27b6">7Vpbc5s6EP41fgzDTVwec23PTM9MZjLT9jx1ZJCBE0BUyLHdX39WRoAExHEcu2566swEtBIrsd+3q13ZM+e6WH9guEr/pjHJZ7YZr2fOzcy2LTMM4SIkm0YS+lYjSFgWy0G94CH7QdonpXSZxaTWBnJKc55VujCiZUkirskwY3SlD1vQXJ+1wgkZCR4inI+lX7KYp1JqmWbf8ZFkSSqnDpDsmOPoMWF0Wcr5SlqSpqfArRo5tE5xTFeKyLmdOdeMUt7cFetrkguzthZrnrt7prdbMiMl3+uByMeeP18Ei9ixHXNxYaFGxRPOl9IOf5WcsJJwuWK+aQ0EqgALaFzBa1RCGOV0CZqvVmnGyUOFIyFcAT1AlvIih5YFt1vbELEEs3m4sYFoRLTIoraDM/rY2V48uKAlv8NFlgtKfSYsxiWWYskfC0x4NbaCNMwTYZysFZG0ygdCC8LZBobIXrdFSJIX+dIwK4UKLeCpwoKOHljSL+l09zjAjYRiT1iQPbZ+DGyVzS3DNBPneE7yq46I1zSnrB9Yc8z4XSbm31qalPGl8Jh+BEiUfh0J+whI1HTJIvLM27qBdHfMEsJfYKqww05EGckxz550r34LOpeLB/Or/z2J2Wd8Yf24nJv+lwt75Dc7EaOMpzShJc5ve+nAMfoxnyitJLD/Es430sR4yakO+0tAknXGv4p7A8nWP0rPzVptbJTGPWEZ2ImwjjBgM6kIte2tKiPs2r26bWujtoYKJ7E8DnMm4XorV+Sj9zSD5fVRI/QNU/nYWgxxTaQrbF5F6lCj9EAtQnowcryBouZ1R4qACnijDKvEgHrX8pE2j+2HO9cFr7trPNw0K+i9qjP1YWGwhXeXl7V7UbFORF5iRFkdUQM8C8hWy6vuNDGu087pFuAsbbScwaSOF8BbtiFQ6VlsP5PBUWwzGSQSn0QQvqd1xjNaQt+cck4LZcBlniWigwv33ndb/ImboCvixCSF1W3QDMfboO8apneiUGtNkMDLubSBxgbv+5K2HRf11jqXMMByq3XfCXeJvKpaog7rfqAjvBpQV0SHzvYRrAGdOQUewBUSrBVlj+0awDDNMvSlzVkrsULbsLzAsAyIDXe2++yD7ytpG5FzgsLP8tXx9ThpTSRtAOFE0uYcIWmb5Kp7aMACm1bbkPUNl/G3CrbLKiUM57VRRTow26hO2O0TEcH91wppWLYiUjZhF8JvnpUwaVuzmceBHiLTIFCNkPeDiTB1KtzHZdSbcc9x1Rj1D/aDbQqFo21qhH444fbeqYoB71D0CxqTQkKfQgJfGxFM19wfFXk1QJ8U3xHRrm7E33GQ9wZej8a4O+4YdxScCHf/ZdzHFZpWvvVllVpVdX2vKan6Ws/01WLPcF9d7s0Oqs52JW8vFmjuNPAKslOnL63sjXWcPTj9sb3BPrFv5WYPMpLucPOFyu2AYmnSinvUS+cgpGdrxw+G/R4Yic7JSBf5hhkqHz3RDfzD+OkMThaG+dCJ6RkejZ5HJGcQ6NxE74Gc3jnJiQJIwGzPD4IQql3fHJLTNWzTRMgMPRRaqN2RX8tVzxxUdz85lrbTKWyN06gaMRbSGa7TVM+BJI3V/EyKRhnVMPEqsjjeHhRP1ep6gX6E7MpxBgZH43LanWCVfaqiyhofsv/eCIDLDCqb7qTibBg4/zcMwoEX+OdG4OAzpQL+G/zpzxHCONR5+jcZEwcIyJnYP51TYbzH+dE5Eve2dGxSI+s9ZEbWWStJV3wR8mzebjuBceB3gkPGWrY1VHXqfGh8yvV77wSuNdqN7ZPtBdDsfwbUQNb/zMq5/Q8=</diagram></mxfile>
|
||||
243
src/homework/03-sysadmin/3.8/readme.md
Normal file
@@ -0,0 +1,243 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-08-net/README.md)
|
||||
по теме "3.8. Компьютерные сети, лекция 3".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Подключитесь к публичному маршрутизатору в интернет. Найдите маршрут к вашему публичному IP
|
||||
|
||||
```shell
|
||||
telnet route-views.routeviews.org
|
||||
Trying 128.223.51.103...
|
||||
Connected to route-views.routeviews.org.
|
||||
|
||||
<...>
|
||||
|
||||
User Access Verification
|
||||
|
||||
Username: rviews
|
||||
|
||||
route-views>show ip route 46.181.144.146
|
||||
Routing entry for 46.180.0.0/15
|
||||
Known via "bgp 6447", distance 20, metric 0
|
||||
Tag 6939, type external
|
||||
Last update from 64.71.137.241 7w0d ago
|
||||
Routing Descriptor Blocks:
|
||||
* 64.71.137.241, from 64.71.137.241, 7w0d ago
|
||||
Route metric is 0, traffic share count is 1
|
||||
AS Hops 3
|
||||
Route tag 6939
|
||||
MPLS label: none
|
||||
|
||||
route-views>show bgp 46.181.144.146
|
||||
BGP routing table entry for 46.180.0.0/15, version 150820343
|
||||
Paths: (23 available, best #22, table default)
|
||||
Not advertised to any peer
|
||||
Refresh Epoch 1
|
||||
3333 31133 39927, (aggregated by 65423 192.168.21.211)
|
||||
193.0.0.56 from 193.0.0.56 (193.0.0.56)
|
||||
Origin IGP, localpref 100, valid, external, atomic-aggregate
|
||||
path 7FE1040964D8 RPKI State not found
|
||||
rx pathid: 0, tx pathid: 0
|
||||
Refresh Epoch 1
|
||||
8283 1299 39927, (aggregated by 65423 192.168.21.136)
|
||||
94.142.247.3 from 94.142.247.3 (94.142.247.3)
|
||||
Origin IGP, metric 0, localpref 100, valid, external, atomic-aggregate
|
||||
Community: 1299:30000 8283:1 8283:101
|
||||
unknown transitive attribute: flag 0xE0 type 0x20 length 0x18
|
||||
value 0000 205B 0000 0000 0000 0001 0000 205B
|
||||
0000 0005 0000 0001
|
||||
path 7FE0A25887D8 RPKI State not found
|
||||
rx pathid: 0, tx pathid: 0
|
||||
Refresh Epoch 1
|
||||
4901 6079 31133 39927, (aggregated by 65423 192.168.21.211)
|
||||
162.250.137.254 from 162.250.137.254 (162.250.137.254)
|
||||
Origin IGP, localpref 100, valid, external, atomic-aggregate
|
||||
Community: 65000:10100 65000:10300 65000:10400
|
||||
path 7FE154880220 RPKI State not found
|
||||
rx pathid: 0, tx pathid: 0
|
||||
<...>
|
||||
```
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Создайте dummy0 интерфейс в Ubuntu. Добавьте несколько статических маршрутов. Проверьте таблицу маршрутизации.
|
||||
|
||||
Создание dummy-интерфейса:
|
||||
|
||||
```shell
|
||||
echo "dummy" | sudo tee -a /etc/modules
|
||||
sudo touch /etc/modprobe.d/dummy.conf
|
||||
echo "options dummy numdummies=1" | sudo tee /etc/modprobe.d/dummy.conf
|
||||
sudo ip link add dummy0 type dummy
|
||||
```
|
||||
|
||||
Добавление маршрутов и вывод таблицы маршрутизации:
|
||||
|
||||
```shell
|
||||
sudo ip route add 10.2.2.2/32 dev eth0
|
||||
sudo ip route add 10.2.2.3/32 via 10.0.2.16
|
||||
ip route
|
||||
default via 10.0.2.2 dev eth0 proto dhcp src 10.0.2.15 metric 100
|
||||
10.0.2.0/24 dev eth0 proto kernel scope link src 10.0.2.15
|
||||
10.0.2.2 dev eth0 proto dhcp scope link src 10.0.2.15 metric 100
|
||||
10.2.2.2 dev eth0 scope link
|
||||
10.2.2.3 via 10.0.2.16 dev eth0
|
||||
```
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Проверьте открытые TCP порты в Ubuntu, какие протоколы и приложения используют эти порты? Приведите несколько примеров.
|
||||
|
||||
Для вывода открытых TCP-портов используем утилиту `ss` со следующими флагами:
|
||||
* `-t` вывод только TCP-портов
|
||||
* `-l` вывод портов в состоянии `LISTEN`, то есть открытые для прослушивания
|
||||
* `-n` использовать числовое представление портов (например, `:ssh` -> `:22`)
|
||||
*
|
||||
|
||||
```shell
|
||||
ss -tln
|
||||
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
||||
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
|
||||
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
|
||||
LISTEN 0 128 [::]:22 [::]:*
|
||||
```
|
||||
|
||||
В данном случае открыты только порты для соединения по `ssh` (порты `:22`) и для [`systemd-resolved`](https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html) (порт `:53`).
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Проверьте используемые UDP сокеты в Ubuntu, какие протоколы и приложения используют эти порты?
|
||||
|
||||
По аналогии с предыдущим заданием используем утилиту `ss`, заменив флаг `-t` на `-u`
|
||||
|
||||
```shell
|
||||
ss -ulpn
|
||||
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
|
||||
UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
|
||||
UNCONN 0 0 10.0.2.15%eth0:68 0.0.0.0:*
|
||||
```
|
||||
|
||||
Порт `:53` предназначается для использования [`systemd-resolved`](https://www.freedesktop.org/software/systemd/man/systemd-resolved.service.html),
|
||||
а порт `68` используется для получения информации о динамической IP-адресации от DHCP-сервера.
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Используя diagrams.net, создайте L3 диаграмму вашей домашней сети или любой другой сети, с которой вы работали.
|
||||
|
||||
В качестве сети взята стандартная домашняя сеть с wi-fi-роутером.
|
||||
|
||||
Файл [network.xml](network.xml) для открытия в [diagrams.net](https://diagrams.net).
|
||||
|
||||

|
||||
|
||||
### Задача 6
|
||||
|
||||
> Установите Nginx, настройте в режиме балансировщика TCP или UDP.
|
||||
|
||||
Установка nginx:
|
||||
|
||||
```shell
|
||||
sudo apt install nginx
|
||||
|
||||
sudo service nginx status
|
||||
● nginx.service - A high performance web server and a reverse proxy server
|
||||
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Thu 2022-03-17 03:14:31 UTC; 24s ago
|
||||
Docs: man:nginx(8)
|
||||
Main PID: 1830 (nginx)
|
||||
Tasks: 3 (limit: 1107)
|
||||
Memory: 5.0M
|
||||
CGroup: /system.slice/nginx.service
|
||||
├─1830 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
|
||||
├─1831 nginx: worker process
|
||||
└─1832 nginx: worker process
|
||||
```
|
||||
|
||||
Далее, определим, что есть два ip-адреса, на которые необходимо сделать проксирование и балансировку трафика:
|
||||
* `10.2.2.2`
|
||||
* `10.2.2.3`
|
||||
|
||||
Оба ip-адреса слушают следующие порты: `:25` - upd-трафик, `:80` - tcp-трафик.
|
||||
|
||||
Настроим nginx на прослушивание порта `:80` для обоих типов трафика с дальнейшей балансировкой и проксированием.
|
||||
|
||||
Добавим новый блок конфигурации в файл `/etc/nginx/nginx.conf`:
|
||||
|
||||
```
|
||||
stream {
|
||||
upstream tcp_backend {
|
||||
server 10.2.2.2:80;
|
||||
server 10.2.2.3:80;
|
||||
}
|
||||
|
||||
upstream upd_backend {
|
||||
server 10.2.2.2:25;
|
||||
server 10.2.2.3:25;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
proxy_pass tcp_backend;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80 udp;
|
||||
proxy_pass upd_backend;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Дополнительно, нужно убрать конфигурацию приветственной страницы, которая работает по умолчанию и слушает порт `80`:
|
||||
|
||||
```shell
|
||||
sudo rm -f /etc/nginx/sites-enabled/default
|
||||
```
|
||||
|
||||
Проверим, что конфигурация в порядке:
|
||||
|
||||
```shell
|
||||
sudo nginx -t
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
Затем нужно применить конфигурацию nginx
|
||||
|
||||
```shell
|
||||
sudo nginx -s reload
|
||||
sudo systemctl status nginx
|
||||
● nginx.service - A high performance web server and a reverse proxy server
|
||||
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Mon 2022-03-21 03:37:24 UTC; 6s ago
|
||||
Docs: man:nginx(8)
|
||||
Process: 1364 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
|
||||
Process: 1367 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=0/SUCCESS)
|
||||
Main PID: 1376 (nginx)
|
||||
Tasks: 3 (limit: 1107)
|
||||
Memory: 3.3M
|
||||
CGroup: /system.slice/nginx.service
|
||||
├─1376 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
|
||||
├─1377 nginx: worker process
|
||||
└─1378 nginx: worker process
|
||||
```
|
||||
|
||||
Проверим, что nginx принимает соединение на порт `80`
|
||||
|
||||
```shell
|
||||
curl -v --max-time 5 http://localhost:80
|
||||
* Trying 127.0.0.1:80...
|
||||
* TCP_NODELAY set
|
||||
* Connected to localhost (127.0.0.1) port 80 (#0)
|
||||
> GET / HTTP/1.1
|
||||
> Host: localhost
|
||||
> User-Agent: curl/7.68.0
|
||||
> Accept: */*
|
||||
>
|
||||
* Operation timed out after 5001 milliseconds with 0 bytes received
|
||||
* Closing connection 0
|
||||
curl: (28) Operation timed out after 5001 milliseconds with 0 bytes received
|
||||
```
|
||||
|
||||
В данном случае добавили `--max-time 5`, чтобы быстрее получить ошибку, так как nginx пытается проксировать запрос на несуществующий адрес.
|
||||
BIN
src/homework/03-sysadmin/3.9/bitwarden-2fa.png
Executable file
|
After Width: | Height: | Size: 135 KiB |
BIN
src/homework/03-sysadmin/3.9/bitwarden.png
Executable file
|
After Width: | Height: | Size: 37 KiB |
282
src/homework/03-sysadmin/3.9/readme.md
Normal file
@@ -0,0 +1,282 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/03-sysadmin-09-security/README.md)
|
||||
по теме "3.9. Элементы безопасности информационных систем".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Установите Bitwarden плагин для браузера. Зарегестрируйтесь и сохраните несколько паролей.
|
||||
|
||||

|
||||
|
||||
### Задача 2
|
||||
|
||||
> Установите Google authenticator на мобильный телефон. Настройте вход в Bitwarden акаунт через Google authenticator OTP.
|
||||
|
||||
Заместо `Google Authenticator` использовал приложение `Microsoft Authenticator`.
|
||||
|
||||

|
||||
|
||||
### Задача 3
|
||||
|
||||
> Установите apache2, сгенерируйте самоподписанный сертификат, настройте тестовый сайт для работы по HTTPS.
|
||||
|
||||
Вместо `apache2` будем производить настройку уже установленного в виртуальную машину `nginx` (установка производилась в [дз 3.8](/src/homework/03-sysadmin/3.8/readme.md#Задача 6)).
|
||||
|
||||
Первым шагом необходимо сгенерировать сертификат:
|
||||
|
||||
```shell
|
||||
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout /etc/ssl/private/nginx-selfsigned.key \
|
||||
-out /etc/ssl/certs/nginx-selfsigned.crt \
|
||||
-subj "/C=RU/ST=Moscow/L=Moscow/O=Company Name/OU=Org/CN=site.localhost"
|
||||
|
||||
sudo ls -lah /etc/ssl/private/ | grep nginx
|
||||
-rw------- 1 root root 1.7K Mar 28 03:01 nginx-selfsigned.key
|
||||
|
||||
sudo ls -lah /etc/ssl/certs/ | grep nginx
|
||||
-rw-r--r-- 1 root root 1.4K Mar 28 03:01 nginx-selfsigned.crt
|
||||
```
|
||||
|
||||
Воспользуемся [конфигуратором](https://ssl-config.mozilla.org/#server=nginx&version=1.17.7&config=modern&openssl=1.1.1k&guideline=5.6),
|
||||
чтобы сгенерировать основу конфигурации для сервера. Получим следующую конфигурацию, которую расположим в файле `/etc/site-available/ssl`:
|
||||
|
||||
```
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name site.localhost;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
|
||||
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m; # about 40000 sessions
|
||||
ssl_session_tickets off;
|
||||
|
||||
# modern configuration
|
||||
ssl_protocols TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
# HSTS (ngx_http_headers_module is required) (63072000 seconds)
|
||||
add_header Strict-Transport-Security "max-age=63072000" always;
|
||||
|
||||
# OCSP stapling
|
||||
ssl_stapling on;
|
||||
ssl_stapling_verify on;
|
||||
|
||||
location / {
|
||||
root /var/www/html;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Переведём новую конфигурацию в боевые настройки и проверим, что всё в порядке:
|
||||
|
||||
```shell
|
||||
sudo ln -s /etc/nginx/sites-available/ssl /etc/nginx/sites-enabled/ssl.conf
|
||||
|
||||
sudo nginx -t
|
||||
nginx: [warn] "ssl_stapling" ignored, issuer certificate not found for certificate "/etc/ssl/certs/nginx-selfsigned.crt"
|
||||
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
|
||||
nginx: configuration file /etc/nginx/nginx.conf test is successful
|
||||
```
|
||||
|
||||
Перезапускаем сервис `nginx`, проверяем его состояние:
|
||||
|
||||
```shell
|
||||
sudo service nginx reload
|
||||
sudo service nginx status
|
||||
service nginx status
|
||||
● nginx.service - A high performance web server and a reverse proxy server
|
||||
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
|
||||
Active: active (running) since Mon 2022-03-28 02:53:00 UTC; 21min ago
|
||||
Docs: man:nginx(8)
|
||||
Process: 2256 ExecReload=/usr/sbin/nginx -g daemon on; master_process on; -s reload (code=exited, status=0/SUCCESS)
|
||||
Main PID: 736 (nginx)
|
||||
Tasks: 3 (limit: 1107)
|
||||
Memory: 13.3M
|
||||
CGroup: /system.slice/nginx.service
|
||||
├─ 736 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;
|
||||
├─2257 nginx: worker process
|
||||
└─2258 nginx: worker process
|
||||
```
|
||||
|
||||
При помощи `curl`, убедимся, что сервер работает корректно:
|
||||
|
||||
```shell
|
||||
curl https://site.localhost --resolve 'site.localhost:127.0.0.1'
|
||||
curl: (60) SSL certificate problem: self signed certificate
|
||||
|
||||
# проигнорируем ошибки самоподписного сертификата
|
||||
curl -k https://site.localhost --resolve 'site.localhost:127.0.0.1'
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to nginx!</title>
|
||||
<style>
|
||||
body {
|
||||
width: 35em;
|
||||
margin: 0 auto;
|
||||
font-family: Tahoma, Verdana, Arial, sans-serif;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Welcome to nginx!</h1>
|
||||
<p>If you see this page, the nginx web server is successfully installed and
|
||||
working. Further configuration is required.</p>
|
||||
|
||||
<p>For online documentation and support please refer to
|
||||
<a href="http://nginx.org/">nginx.org</a>.<br/>
|
||||
Commercial support is available at
|
||||
<a href="http://nginx.com/">nginx.com</a>.</p>
|
||||
|
||||
<p><em>Thank you for using nginx.</em></p>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### Задача 4
|
||||
|
||||
> Проверьте на TLS уязвимости произвольный сайт в интернете
|
||||
|
||||
```shell
|
||||
git clone --depth 1 https://github.com/drwetter/testssl.sh.git
|
||||
cd testssl
|
||||
./testssl.sh -U --sneaky https://metagamerscore.com
|
||||
|
||||
Start 2022-03-28 10:31:09 -->> 217.61.246.126:443 (metagamerscore.com) <<--
|
||||
|
||||
rDNS (217.61.246.126): --
|
||||
Service detected: HTTP
|
||||
|
||||
|
||||
Testing vulnerabilities
|
||||
|
||||
Heartbleed (CVE-2014-0160) not vulnerable (OK), no heartbeat extension
|
||||
CCS (CVE-2014-0224) not vulnerable (OK)
|
||||
Ticketbleed (CVE-2016-9244), experiment. not vulnerable (OK)
|
||||
ROBOT Server does not support any cipher suites that use RSA key transport
|
||||
Secure Renegotiation (RFC 5746) supported (OK)
|
||||
Secure Client-Initiated Renegotiation not vulnerable (OK)
|
||||
CRIME, TLS (CVE-2012-4929) not vulnerable (OK)
|
||||
BREACH (CVE-2013-3587) potentially NOT ok, "gzip" HTTP compression detected. - only supplied "/" tested
|
||||
Can be ignored for static pages or if no secrets in the page
|
||||
POODLE, SSL (CVE-2014-3566) not vulnerable (OK)
|
||||
TLS_FALLBACK_SCSV (RFC 7507) No fallback possible (OK), no protocol below TLS 1.2 offered
|
||||
SWEET32 (CVE-2016-2183, CVE-2016-6329) not vulnerable (OK)
|
||||
FREAK (CVE-2015-0204) not vulnerable (OK)
|
||||
DROWN (CVE-2016-0800, CVE-2016-0703) not vulnerable on this host and port (OK)
|
||||
make sure you don't use this certificate elsewhere with SSLv2 enabled services
|
||||
https://censys.io/ipv4?q=DA0C37574C249C089F819CDC857AFDD834D0ADA90888D97A0B81071AB288732E could help you to find out
|
||||
LOGJAM (CVE-2015-4000), experimental not vulnerable (OK): no DH EXPORT ciphers, no DH key detected with <= TLS 1.2
|
||||
BEAST (CVE-2011-3389) not vulnerable (OK), no SSL3 or TLS1
|
||||
LUCKY13 (CVE-2013-0169), experimental not vulnerable (OK)
|
||||
Winshock (CVE-2014-6321), experimental not vulnerable (OK)
|
||||
RC4 (CVE-2013-2566, CVE-2015-2808) no RC4 ciphers detected (OK)
|
||||
|
||||
|
||||
Done 2022-03-28 10:31:34 [ 27s] -->> 217.61.246.126:443 (metagamerscore.com) <<--
|
||||
```
|
||||
|
||||
### Задача 5
|
||||
|
||||
> Установите на Ubuntu ssh сервер, сгенерируйте новый приватный ключ. Скопируйте свой публичный ключ на другой сервер. Подключитесь к серверу по SSH-ключу.
|
||||
|
||||
На виртуальной машине уже установлен ssh-сервер и настроен один ssh-ключ для подключения к машине с хоста.
|
||||
Таким образом сгенерируем новый ssh-ключ для пользователя `vagrant` и попробуем подключиться как пользователь `root` через ssh-сервер.
|
||||
|
||||
Генерация приватного и открытого ssh-ключей:
|
||||
|
||||
```shell
|
||||
ssh-keygen
|
||||
Generating public/private rsa key pair.
|
||||
Enter file in which to save the key (/home/vagrant/.ssh/id_rsa):
|
||||
Enter passphrase (empty for no passphrase):
|
||||
Enter same passphrase again:
|
||||
Your identification has been saved in /home/vagrant/.ssh/id_rsa
|
||||
Your public key has been saved in /home/vagrant/.ssh/id_rsa.pub
|
||||
The key fingerprint is:
|
||||
SHA256:id08L1eU83CEIr5iolPo2NKg+J7palNIO0o7D7agGuU vagrant@vagrant
|
||||
The key's randomart image is:
|
||||
+---[RSA 3072]----+
|
||||
| ..|
|
||||
| . . .o |
|
||||
| . . .= .|
|
||||
| . o +. . = |
|
||||
|. + .. S +. . .|
|
||||
| B... o o .o . |
|
||||
|*+E* o o .. o |
|
||||
|B*=o* o |
|
||||
|**Xo . |
|
||||
+----[SHA256]-----+
|
||||
```
|
||||
|
||||
Добавим открытый ключ в файл `authorized_keys` для пользователя `root`:
|
||||
|
||||
```shell
|
||||
cat .ssh/id_rsa.pub | sudo tee /root/.ssh/authorized_keys
|
||||
```
|
||||
|
||||
Проверим, что всё работает:
|
||||
```shell
|
||||
ssh -l root 127.0.0.1
|
||||
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-104-generic x86_64)
|
||||
<...>
|
||||
root@vagrant:~#
|
||||
```
|
||||
|
||||
### Задача 6
|
||||
|
||||
> Переименуйте файлы ключей из задания 5. Настройте файл конфигурации SSH клиента, так чтобы вход на удаленный сервер осуществлялся по имени сервера.
|
||||
|
||||
```shell
|
||||
mv ~/.ssh/id_rsa ~/.ssh/localhost_key
|
||||
mv ~/.ssh/id_rsa.pub ~/.ssh/localhost_key.pub
|
||||
```
|
||||
|
||||
Добавим новую конфигурацию в файл `~/.ssh/config`:
|
||||
|
||||
```
|
||||
host local-root
|
||||
hostname 127.0.0.1
|
||||
user root
|
||||
identityfile ~/.ssh/localhost_key
|
||||
```
|
||||
|
||||
Проверим, что всё работает:
|
||||
```shell
|
||||
ssh local-root
|
||||
Welcome to Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-104-generic x86_64)
|
||||
<...>
|
||||
root@vagrant:~#
|
||||
```
|
||||
|
||||
### Задача 7
|
||||
|
||||
> Соберите дамп трафика утилитой tcpdump в формате pcap, 100 пакетов. Откройте файл pcap в Wireshark.
|
||||
|
||||
```shell
|
||||
sudo tcpdump -c 100 -w dump.pcap
|
||||
|
||||
tcpdump -r dump.pcap
|
||||
reading from file dump.pcap, link-type EN10MB (Ethernet)
|
||||
<...>
|
||||
```
|
||||
|
||||
Для просмотра полученного файла в `Wireshark`, установим утилиту на хосте:
|
||||
|
||||
```shell
|
||||
sudo apt install wireshark
|
||||
```
|
||||
|
||||
Теперь, скопируем файл из виртуальной машины на хост, чтобы была возможность открыть его без проблем:
|
||||
|
||||
```shell
|
||||
scp -P 2222 vagrant@127.0.0.1:/home/vagrant/dump.pcap ./dump.pcap
|
||||
```
|
||||
|
||||
Затем запустим графическое отображение утилиты и откроем в ней файл:
|
||||
|
||||

|
||||
BIN
src/homework/03-sysadmin/3.9/wireshark.png
Normal file
|
After Width: | Height: | Size: 493 KiB |
119
src/homework/03-sysadmin/notes.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Linux operating system
|
||||
|
||||
## Streams
|
||||
|
||||
Базовые потоки:
|
||||
* `0` - stdin (`/proc/<pid>/fd/0`)
|
||||
* `1` - stdout (`/proc/<pid>/fd/1`)
|
||||
* `2` - stderr (`/proc/<pid>/fd/2`)
|
||||
|
||||
Для перенаправления потока можно использовать `X>&Y`, где `X` поток, который нужно перенаправить,
|
||||
`Y` - поток, в который нужно направить данные (может быть стандартным файлом).
|
||||
|
||||
## Kernel
|
||||
|
||||
Версия ядра: `uname -r`.
|
||||
Версия дистрибутива: `cat /etc/issue` (debian-based), `cat /etc/redhat-release` (centos-based).
|
||||
|
||||
Посмотреть всю конфигурацию системы: `sysctl -a`.
|
||||
|
||||
Логи системы: `dmesg`, `syslog`.
|
||||
|
||||
## Systemctl
|
||||
|
||||
* `systemctl list-units --all`
|
||||
* `systemctl status <service>`
|
||||
* `systemctl cat <service>` - просмотреть файл настроек сервиса
|
||||
* `systemctl list-dependencies <service>`
|
||||
```shell
|
||||
systemctl list-dependencies docker
|
||||
|
||||
docker.service
|
||||
● ├─containerd.service
|
||||
● ├─docker.socket
|
||||
● ├─system.slice
|
||||
● ├─network-online.target
|
||||
● │ └─NetworkManager-wait-online.service
|
||||
● └─sysinit.target
|
||||
● ├─apparmor.service
|
||||
<...>
|
||||
```
|
||||
* `journalctl -f`
|
||||
* `journalctl -f -u docker`
|
||||
|
||||
## Filesystems
|
||||
|
||||
* `stat <file>`
|
||||
|
||||
### File types
|
||||
|
||||
* regular file (`ls -la` - `-`)
|
||||
* directory (`ls -la` - `d`)
|
||||
* hardlink (`ls -la` - `l`)
|
||||
|
||||
1 файл - 1 hardlink
|
||||
1 директрория - минимум 2 hardlink (у пустой директории - 2, +1 за каждую директорию внутри)
|
||||
|
||||
* symlink (`ls -la` - `l`)
|
||||
* pipe (`ls -la` - `p`)
|
||||
|
||||
Перенаправление потоков, только однонаправленный
|
||||
`mkfifo <pipe>`
|
||||
|
||||
* socket (`ls -la` - `s`)
|
||||
|
||||
Двунаправленный поток, производительнее, чем pipe. Используется для взаимодействия между процессами.
|
||||
|
||||
### File access
|
||||
|
||||
`chown`, `chmod`, `umask`
|
||||
|
||||
Права по умолчанию:
|
||||
* `file`: `666 - umask`
|
||||
* `dir`: `777 - umask`
|
||||
|
||||
Дополнительные права доступа:
|
||||
* `sticky` - создание доступно всем, удаление только файлы пользователя
|
||||
* `setuid`
|
||||
* `setgid`
|
||||
|
||||
`lsattr`/`chattr`
|
||||
|
||||
### Raid
|
||||
|
||||
`mdadm`
|
||||
|
||||
### LVM
|
||||
|
||||
`lvs`, `vgs`, `vgdisplay`, `pvdisplay`
|
||||
|
||||
### Partitions
|
||||
|
||||
`fdisk -l`/`fdisk`, `sfdisk`
|
||||
|
||||
### Filesystems
|
||||
|
||||
`mkfs`, `mount`, `/etc/fstab`
|
||||
|
||||
## Network
|
||||
|
||||
* `ping <domain/ip>`
|
||||
* `whois <ip>`
|
||||
* `whois -h whois.radb.net <ip>`
|
||||
* `bgpq3 -J <AS>`
|
||||
* `traceroute -An <ip>`
|
||||
* `mtr -zn <ip>`
|
||||
* `dig +trace @8.8.8.8 <domain>`
|
||||
* `dig -x <ip>`
|
||||
* `telnet <domain/ip> <port>`
|
||||
* `ipcalc <network ip>/<mask>`
|
||||
|
||||
### SSH
|
||||
|
||||
* `ssh-copy-id user@server` - добавление ssh-ключа на сервер для пользователя
|
||||
* `ssh-keygen -F server` - проверка ssh-сертификатов сервера
|
||||
* `ssh-keygen -R server` - удаление записи сервера из `known_hosts`
|
||||
|
||||
### Web-servers
|
||||
|
||||
* ssl config generation: https://ssl-config.mozilla.org/
|
||||
2
src/homework/04-script/4.1/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/curl.log
|
||||
/error.log
|
||||
9
src/homework/04-script/4.1/q1_script.sh
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
a=1
|
||||
b=2
|
||||
c=a+b
|
||||
d=$a+$b
|
||||
e=$(($a+$b))
|
||||
|
||||
echo a=$a b=$b c=$c d=$d e=$e
|
||||
13
src/homework/04-script/4.1/q2_script.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
while ((1==1))
|
||||
do
|
||||
curl https://localhost:4757
|
||||
|
||||
if (($? != 0))
|
||||
then
|
||||
date > curl.log;
|
||||
else
|
||||
break;
|
||||
fi
|
||||
done
|
||||
14
src/homework/04-script/4.1/q3_script.sh
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
serverPort=80
|
||||
servers=("192.168.0.1" "173.194.222.113" "87.250.250.242")
|
||||
|
||||
for i in {1..5}
|
||||
do
|
||||
for server in ${servers[@]}
|
||||
do
|
||||
curl --connect-timeout 3 --max-time 5 http://${server}:${serverPort}
|
||||
curlResult=$?
|
||||
echo "$(date) curl result for ${server} is ${curlResult}" >> curl.log;
|
||||
done
|
||||
done
|
||||
27
src/homework/04-script/4.1/q4_script.sh
Executable file
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
serverPort=80
|
||||
servers=("192.168.0.1" "173.194.222.113" "87.250.250.242")
|
||||
|
||||
for i in {1..5}
|
||||
do
|
||||
isError=0
|
||||
for server in ${servers[@]}
|
||||
do
|
||||
curl --connect-timeout 3 --max-time 5 http://${server}:${serverPort}
|
||||
curlResult=$?
|
||||
echo "$(date) curl result for ${server} is ${curlResult}" >> curl.log;
|
||||
if (($curlResult!=0))
|
||||
then
|
||||
echo ${server} > error.log
|
||||
isError=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if (($isError!=0))
|
||||
then
|
||||
echo "going to break"
|
||||
break
|
||||
fi
|
||||
done
|
||||
1
src/homework/04-script/4.1/q5_example_bad_lenght.txt
Normal file
@@ -0,0 +1 @@
|
||||
[04-script-01-bash] сломал хук слишком длинный комментарий
|
||||
1
src/homework/04-script/4.1/q5_example_bad_pattern.txt
Normal file
@@ -0,0 +1 @@
|
||||
сломал хук
|
||||
1
src/homework/04-script/4.1/q5_example_good.txt
Normal file
@@ -0,0 +1 @@
|
||||
[04-script-01-bash] сломал хук
|
||||
19
src/homework/04-script/4.1/q5_script.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
MSG="$1"
|
||||
|
||||
if ! grep -qE "^\[.+\]\s.+\n{0,1}$" "$MSG"
|
||||
then
|
||||
cat "$MSG"
|
||||
echo $'\nYour commit message must match the pattern'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msgStr=$(cat $MSG)
|
||||
msgLen=${#msgStr}
|
||||
if ((msgLen>50))
|
||||
then
|
||||
cat "$MSG"
|
||||
echo $'\nYour commit message is too long'
|
||||
exit 1
|
||||
fi
|
||||
160
src/homework/04-script/4.1/readme.md
Normal file
@@ -0,0 +1,160 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/04-script-01-bash/README.md)
|
||||
по теме "4.1. Командная оболочка Bash: Практические навыки".
|
||||
|
||||
## Q/A
|
||||
|
||||
## Обязательная задача 1
|
||||
|
||||
Есть [скрипт](q1_script.sh):
|
||||
```shell
|
||||
a=1
|
||||
b=2
|
||||
c=a+b
|
||||
d=$a+$b
|
||||
e=$(($a+$b))
|
||||
```
|
||||
|
||||
Какие значения переменным `c`,`d`,`e` будут присвоены? Почему?
|
||||
|
||||
| Переменная | Значение | Обоснование |
|
||||
|------------|----------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| `c` | "a+b" | Присвоение обычной строки "a+b" без каких-либо преобразований, так как нет никаких спец-символов. |
|
||||
| `d` | "1+2" | Конкатенация значений переменных `a` и `b` с символом "+", так как не заданы условия для произведения операции сложения. |
|
||||
| `e` | "3" | Сложение значений переменных `a` и `b` используя арифметику целых чисел. |
|
||||
|
||||
|
||||
## Обязательная задача 2
|
||||
На нашем локальном сервере упал сервис и мы написали скрипт, который постоянно проверяет его доступность, записывая дату проверок до тех пор, пока сервис не станет доступным (после чего скрипт должен завершиться). В скрипте допущена ошибка, из-за которой выполнение не может завершиться, при этом место на Жёстком Диске постоянно уменьшается. Что необходимо сделать, чтобы его исправить:
|
||||
```shell
|
||||
while ((1==1)
|
||||
do
|
||||
curl https://localhost:4757
|
||||
if (($? != 0))
|
||||
then
|
||||
date >> curl.log
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
### Ваш скрипт:
|
||||
|
||||
[script](q2_script.sh)
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
|
||||
while ((1==1))
|
||||
do
|
||||
curl https://localhost:4757
|
||||
|
||||
if (($? != 0))
|
||||
then
|
||||
date > curl.log;
|
||||
else
|
||||
break;
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Обязательная задача 3
|
||||
Необходимо написать скрипт, который проверяет доступность трёх IP: `192.168.0.1`, `173.194.222.113`, `87.250.250.242` по `80` порту и записывает результат в файл `log`. Проверять доступность необходимо пять раз для каждого узла.
|
||||
|
||||
### Ваш скрипт:
|
||||
|
||||
[script](q3_script.sh)
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
|
||||
serverPort=80
|
||||
servers=("192.168.0.1" "173.194.222.113" "87.250.250.242")
|
||||
|
||||
for i in {1..5}
|
||||
do
|
||||
for server in ${servers[@]}
|
||||
do
|
||||
curl --connect-timeout 3 --max-time 5 http://${server}:${serverPort}
|
||||
curlResult=$?
|
||||
echo "$(date) curl result for ${server} is ${curlResult}" >> curl.log;
|
||||
done
|
||||
done
|
||||
```
|
||||
|
||||
## Обязательная задача 4
|
||||
Необходимо дописать скрипт из предыдущего задания так, чтобы он выполнялся до тех пор, пока один из узлов не окажется недоступным. Если любой из узлов недоступен - IP этого узла пишется в файл error, скрипт прерывается.
|
||||
|
||||
### Ваш скрипт:
|
||||
|
||||
[script](q4_script.sh)
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
|
||||
serverPort=80
|
||||
servers=("192.168.0.1" "173.194.222.113" "87.250.250.242")
|
||||
|
||||
for i in {1..5}
|
||||
do
|
||||
isError=0
|
||||
for server in ${servers[@]}
|
||||
do
|
||||
curl --connect-timeout 3 --max-time 5 http://${server}:${serverPort}
|
||||
curlResult=$?
|
||||
echo "$(date) curl result for ${server} is ${curlResult}" >> curl.log;
|
||||
if (($curlResult!=0))
|
||||
then
|
||||
echo ${server} > error.log
|
||||
isError=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if (($isError!=0))
|
||||
then
|
||||
echo "going to break"
|
||||
break
|
||||
fi
|
||||
done
|
||||
```
|
||||
|
||||
## Дополнительное задание (со звездочкой*) - необязательно к выполнению
|
||||
|
||||
Мы хотим, чтобы у нас были красивые сообщения для коммитов в репозиторий. Для этого нужно написать локальный хук для git, который будет проверять,
|
||||
что сообщение в коммите содержит код текущего задания в квадратных скобках и количество символов в сообщении не превышает 30.
|
||||
Пример сообщения: \[04-script-01-bash\] сломал хук.
|
||||
|
||||
### Ваш скрипт:
|
||||
|
||||
[script](q5_script.sh)
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env bash
|
||||
|
||||
MSG="$1"
|
||||
|
||||
if ! grep -qE "^\[.+\]\s.+\n{0,1}$" "$MSG"
|
||||
then
|
||||
cat "$MSG"
|
||||
echo $'\nYour commit message must match the pattern'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msgStr=$(cat $MSG)
|
||||
msgLen=${#msgStr}
|
||||
if ((msgLen>50))
|
||||
then
|
||||
cat "$MSG"
|
||||
echo $'\nYour commit message is too long'
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
Протестировал запуск скрипта следующими способами:
|
||||
|
||||
```shell
|
||||
# несоответствие паттерну
|
||||
./q5_script.sh q5_example_bad_pattern.txt
|
||||
# несоответствие длины
|
||||
./q5_script.sh q5_example_bad_lenght.txt
|
||||
# корректное сообщение
|
||||
./q5_script.sh q5_example_good.txt
|
||||
```
|
||||
1
src/homework/04-script/4.2/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hosts.json
|
||||
7
src/homework/04-script/4.2/q1_1.py
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
a = 1
|
||||
b = '2'
|
||||
c = a + b
|
||||
|
||||
print(a, b, c)
|
||||
7
src/homework/04-script/4.2/q1_2.py
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
a = '1'
|
||||
b = '2'
|
||||
c = a + b
|
||||
|
||||
print(a, b, c)
|
||||
7
src/homework/04-script/4.2/q1_3.py
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
a = 1
|
||||
b = 2
|
||||
c = a + b
|
||||
|
||||
print(a, b, c)
|
||||
13
src/homework/04-script/4.2/q2.py
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
cd_command = "cd ~/netology/sysadm-homeworks"
|
||||
bash_command = [cd_command, "git status"]
|
||||
top_level_command = [cd_command, "git rev-parse --show-toplevel"]
|
||||
top_level = os.popen(' && '.join(top_level_command)).read().replace('\n', '')
|
||||
result_os = os.popen(' && '.join(bash_command)).read()
|
||||
for result in result_os.split('\n'):
|
||||
if result.find('modified') != -1:
|
||||
prepare_result = result.replace('\tmodified: ', '')
|
||||
full_path = top_level + '/' + prepare_result
|
||||
print(full_path)
|
||||
39
src/homework/04-script/4.2/q3.py
Executable file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
repo_path = sys.argv[1]
|
||||
|
||||
if repo_path == '':
|
||||
print('необходимо указать путь до локального репозитория')
|
||||
exit(1)
|
||||
|
||||
# запускаем под-процесс в рабочей директории (cwd)
|
||||
top_level_command = subprocess.Popen(
|
||||
['git rev-parse --show-toplevel'],
|
||||
cwd=repo_path,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
# ожидаем выполнение под-процесса
|
||||
top_level_command.wait()
|
||||
if top_level_command.returncode != 0:
|
||||
print('директория {} не является git-репозиторием'.format(repo_path))
|
||||
exit(1)
|
||||
|
||||
# на выходе у read() идёт последовательность байт, которые необходимо декодировать в строку
|
||||
top_level_path = top_level_command.stdout.read().decode("utf-8").rstrip()
|
||||
|
||||
bash_command = ['cd ' + top_level_path, "git status"]
|
||||
|
||||
result_os = os.popen(' && '.join(bash_command)).read()
|
||||
|
||||
for result in result_os.split('\n'):
|
||||
if result.find('modified') != -1:
|
||||
prepare_result = result.replace('\tmodified: ', '')
|
||||
full_path = top_level_path + '/' + prepare_result
|
||||
print(full_path)
|
||||
40
src/homework/04-script/4.2/q4.py
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import io
|
||||
import socket
|
||||
|
||||
filename = 'hosts.json'
|
||||
|
||||
hostsList = {
|
||||
"drive.google.com",
|
||||
"mail.google.com",
|
||||
"google.com"
|
||||
}
|
||||
|
||||
with open(filename, 'r+') as file:
|
||||
jsonStr = file.read()
|
||||
try:
|
||||
jsonObj = json.load(io.StringIO(jsonStr))
|
||||
except BaseException as err:
|
||||
jsonObj = dict({})
|
||||
print('error {}'.format(err))
|
||||
exit(1)
|
||||
|
||||
file.truncate(0)
|
||||
file.seek(0)
|
||||
|
||||
for hostname in hostsList:
|
||||
ipAddr = socket.gethostbyname(hostname)
|
||||
prevIpAddr = jsonObj.get(hostname)
|
||||
|
||||
if prevIpAddr is None or prevIpAddr == '':
|
||||
prevIpAddr = ipAddr
|
||||
|
||||
print('{} - {}'.format(hostname, ipAddr))
|
||||
if ipAddr != prevIpAddr:
|
||||
print('[ERROR] {} IP mismatch: {} {}'.format(hostname, ipAddr, prevIpAddr))
|
||||
|
||||
jsonObj[hostname] = ipAddr
|
||||
|
||||
file.write(json.dumps(jsonObj))
|
||||
206
src/homework/04-script/4.2/readme.md
Normal file
@@ -0,0 +1,206 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/04-script-02-py/README.md)
|
||||
по теме "4.2. Использование Python для решения типовых DevOps задач".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Обязательная задача 1
|
||||
|
||||
Есть скрипт:
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
a = 1
|
||||
b = '2'
|
||||
c = a + b
|
||||
```
|
||||
|
||||
#### Вопросы:
|
||||
| Вопрос | Ответ |
|
||||
|------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Какое значение будет присвоено переменной `c`? | Переменной не будет присвоено никакое значение, потому что производится сложение целого числа и строки, при этом будет инициировано исключение `TypeError: unsupported operand type(s) for +: 'int' and 'str'` |
|
||||
| Как получить для переменной `c` значение 12? | Для этого необходимо присвоить переменной `a` строковое значение `'1'`, чтобы была произведена конкатенация строк. [script](./q1_2.py) |
|
||||
| Как получить для переменной `c` значение 3? | Для этого необходимо присвоить переменной `b` целочисленное значение `2` (без кавычек). [script](./q1_3.py) |
|
||||
|
||||
### Обязательная задача 2
|
||||
Мы устроились на работу в компанию, где раньше уже был DevOps Engineer. Он написал скрипт, позволяющий узнать, какие файлы модифицированы в репозитории, относительно локальных изменений. Этим скриптом недовольно начальство, потому что в его выводе есть не все изменённые файлы, а также непонятен полный путь к директории, где они находятся. Как можно доработать скрипт ниже, чтобы он исполнял требования вашего руководителя?
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
|
||||
bash_command = ["cd ~/netology/sysadm-homeworks", "git status"]
|
||||
result_os = os.popen(' && '.join(bash_command)).read()
|
||||
is_change = False
|
||||
for result in result_os.split('\n'):
|
||||
if result.find('modified') != -1:
|
||||
prepare_result = result.replace('\tmodified: ', '')
|
||||
print(prepare_result)
|
||||
break
|
||||
```
|
||||
|
||||
#### Ваш скрипт:
|
||||
|
||||
[script](./q2.py)
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
cd_command = "cd ~/netology/sysadm-homeworks"
|
||||
bash_command = [cd_command, "git status"]
|
||||
top_level_command = [cd_command, "git rev-parse --show-toplevel"]
|
||||
top_level = os.popen(' && '.join(top_level_command)).read().replace('\n', '')
|
||||
result_os = os.popen(' && '.join(bash_command)).read()
|
||||
for result in result_os.split('\n'):
|
||||
if result.find('modified') != -1:
|
||||
prepare_result = result.replace('\tmodified: ', '')
|
||||
full_path = top_level + '/' + prepare_result
|
||||
print(full_path)
|
||||
```
|
||||
|
||||
#### Вывод скрипта при запуске при тестировании:
|
||||
|
||||
_note_: при запуске скрипта изменил путь до репозитория.
|
||||
```
|
||||
./q2.py
|
||||
/home/dannc/code/learning/netology/readme.md
|
||||
/home/dannc/code/learning/netology/src/homework/04-script/4.2/q1_2.py
|
||||
/home/dannc/code/learning/netology/src/homework/04-script/4.2/q1_3.py
|
||||
/home/dannc/code/learning/netology/src/homework/04-script/4.2/q2.py
|
||||
/home/dannc/code/learning/netology/src/homework/04-script/4.2/readme.md
|
||||
|
||||
```
|
||||
|
||||
### Обязательная задача 3
|
||||
1. Доработать скрипт выше так, чтобы он мог проверять не только локальный репозиторий в текущей директории, а также умел воспринимать путь к репозиторию, который мы передаём как входной параметр. Мы точно знаем, что начальство коварное и будет проверять работу этого скрипта в директориях, которые не являются локальными репозиториями.
|
||||
|
||||
#### Ваш скрипт:
|
||||
|
||||
[script](./q3.py)
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
repo_path = sys.argv[1]
|
||||
|
||||
if repo_path == '':
|
||||
print('необходимо указать путь до локального репозитория')
|
||||
exit(1)
|
||||
|
||||
# запускаем под-процесс в рабочей директории (cwd)
|
||||
top_level_command = subprocess.Popen(
|
||||
['git rev-parse --show-toplevel'],
|
||||
cwd=repo_path,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
|
||||
# ожидаем выполнение под-процесса
|
||||
top_level_command.wait()
|
||||
if top_level_command.returncode != 0:
|
||||
print('директория {} не является git-репозиторием'.format(repo_path))
|
||||
exit(1)
|
||||
|
||||
# на выходе у read() идёт последовательность байт, которые необходимо декодировать в строку
|
||||
top_level_path = top_level_command.stdout.read().decode("utf-8").rstrip()
|
||||
|
||||
bash_command = ['cd ' + top_level_path, "git status"]
|
||||
|
||||
result_os = os.popen(' && '.join(bash_command)).read()
|
||||
|
||||
for result in result_os.split('\n'):
|
||||
if result.find('modified') != -1:
|
||||
prepare_result = result.replace('\tmodified: ', '')
|
||||
full_path = top_level_path + '/' + prepare_result
|
||||
print(full_path)
|
||||
|
||||
```
|
||||
|
||||
#### Вывод скрипта при запуске при тестировании:
|
||||
```
|
||||
./q3.py ~/code/learning/netology
|
||||
/home/dannc/code/learning/netology/src/homework/04-script/4.2/q3.py
|
||||
```
|
||||
|
||||
### Обязательная задача 4
|
||||
Наша команда разрабатывает несколько веб-сервисов, доступных по http.
|
||||
Мы точно знаем, что на их стенде нет никакой балансировки, кластеризации, за DNS прячется конкретный IP сервера,
|
||||
где установлен сервис. Проблема в том, что отдел, занимающийся нашей инфраструктурой очень часто меняет нам сервера,
|
||||
поэтому IP меняются примерно раз в неделю, при этом сервисы сохраняют за собой DNS имена.
|
||||
Это бы совсем никого не беспокоило, если бы несколько раз сервера не уезжали в такой сегмент сети нашей компании,
|
||||
который недоступен для разработчиков. Мы хотим написать скрипт, который опрашивает веб-сервисы, получает их IP,
|
||||
выводит информацию в стандартный вывод в виде: <URL сервиса> - <его IP>.
|
||||
Также, должна быть реализована возможность проверки текущего IP сервиса c его IP из предыдущей проверки.
|
||||
Если проверка будет провалена - оповестить об этом в стандартный вывод сообщением: [ERROR] <URL сервиса> IP mismatch: <старый IP> <Новый IP>.
|
||||
Будем считать, что наша разработка реализовала сервисы: `drive.google.com`, `mail.google.com`, `google.com`.
|
||||
|
||||
#### Ваш скрипт:
|
||||
|
||||
[script](./q4.py)
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import io
|
||||
import socket
|
||||
|
||||
filename = 'hosts.json'
|
||||
|
||||
hostsList = {
|
||||
"drive.google.com",
|
||||
"mail.google.com",
|
||||
"google.com"
|
||||
}
|
||||
|
||||
with open(filename, 'r+') as file:
|
||||
jsonStr = file.read()
|
||||
try:
|
||||
jsonObj = json.load(io.StringIO(jsonStr))
|
||||
except BaseException as err:
|
||||
jsonObj = dict({})
|
||||
print('error {}'.format(err))
|
||||
exit(1)
|
||||
|
||||
file.truncate(0)
|
||||
file.seek(0)
|
||||
|
||||
for hostname in hostsList:
|
||||
ipAddr = socket.gethostbyname(hostname)
|
||||
prevIpAddr = jsonObj.get(hostname)
|
||||
|
||||
if prevIpAddr is None or prevIpAddr == '':
|
||||
prevIpAddr = ipAddr
|
||||
|
||||
print('{} - {}'.format(hostname, ipAddr))
|
||||
if ipAddr != prevIpAddr:
|
||||
print('[ERROR] {} IP mismatch: {} {}'.format(hostname, ipAddr, prevIpAddr))
|
||||
|
||||
jsonObj[hostname] = ipAddr
|
||||
|
||||
file.write(json.dumps(jsonObj))
|
||||
```
|
||||
|
||||
#### Вывод скрипта при запуске при тестировании:
|
||||
|
||||
Предположим, что в какой-то момент времени были следующие значения ip-адресов серверов:
|
||||
|
||||
```json
|
||||
{"drive.google.com": "173.194.221.194", "mail.google.com": "142.251.1.18", "google.com": "64.233.162.139"}
|
||||
```
|
||||
|
||||
Тогда запуск команды будет выглядеть следующим образом:
|
||||
|
||||
```
|
||||
./q4.py
|
||||
mail.google.com - 173.194.221.17
|
||||
[ERROR] mail.google.com IP mismatch: 173.194.221.17 142.251.1.18
|
||||
google.com - 64.233.162.100
|
||||
[ERROR] google.com IP mismatch: 64.233.162.100 64.233.162.139
|
||||
drive.google.com - 173.194.221.194
|
||||
```
|
||||
2
src/homework/04-script/4.3/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
hosts.json
|
||||
hosts.yaml
|
||||
65
src/homework/04-script/4.3/q2.py
Executable file
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
import socket
|
||||
import yaml
|
||||
|
||||
hosts_list = {
|
||||
"drive.google.com",
|
||||
"mail.google.com",
|
||||
"google.com"
|
||||
}
|
||||
|
||||
filename_json = 'hosts.json'
|
||||
filename_yaml = 'hosts.yaml'
|
||||
|
||||
if not os.path.exists(filename_json):
|
||||
os.mknod(filename_json)
|
||||
|
||||
if not os.path.exists(filename_yaml):
|
||||
os.mknod(filename_yaml)
|
||||
|
||||
file_json = None
|
||||
file_yaml = None
|
||||
|
||||
try:
|
||||
file_json = open(filename_json, 'r+')
|
||||
file_yaml = open(filename_yaml, 'r+')
|
||||
|
||||
json_str = file_json.read()
|
||||
try:
|
||||
json_obj = json.load(io.StringIO(json_str))
|
||||
except BaseException as err:
|
||||
json_obj = dict({})
|
||||
print('error: {}'.format(err))
|
||||
|
||||
for hostname in hosts_list:
|
||||
ip_addr = socket.gethostbyname(hostname)
|
||||
prev_ip_addr = json_obj.get(hostname)
|
||||
|
||||
if prev_ip_addr is None or prev_ip_addr == '':
|
||||
prev_ip_addr = ip_addr
|
||||
|
||||
print('{} - {}'.format(hostname, ip_addr))
|
||||
if ip_addr != prev_ip_addr:
|
||||
print('[ERROR] {} IP mismatch: {} {}'.format(hostname, ip_addr, prev_ip_addr))
|
||||
|
||||
json_obj[hostname] = ip_addr
|
||||
|
||||
file_json.truncate(0)
|
||||
file_json.seek(0)
|
||||
file_json.write(json.dumps(json_obj))
|
||||
|
||||
file_yaml.truncate(0)
|
||||
file_yaml.seek(0)
|
||||
file_yaml.write(yaml.dump(json_obj))
|
||||
except BaseException as err:
|
||||
print('error {}'.format(err))
|
||||
finally:
|
||||
if file_json is not None:
|
||||
file_json.close()
|
||||
|
||||
if file_yaml is not None:
|
||||
file_yaml.close()
|
||||
150
src/homework/04-script/4.3/readme.md
Normal file
@@ -0,0 +1,150 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/sysadm-homeworks/blob/devsys10/04-script-03-yaml/README.md)
|
||||
по теме "4.3. Языки разметки JSON и YAML".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Обязательная задача 1
|
||||
|
||||
Мы выгрузили JSON, который получили через API запрос к нашему сервису:
|
||||
|
||||
```
|
||||
{ "info" : "Sample JSON output from our service\t",
|
||||
"elements" :[
|
||||
{ "name" : "first",
|
||||
"type" : "server",
|
||||
"ip" : 7175
|
||||
}
|
||||
{ "name" : "second",
|
||||
"type" : "proxy",
|
||||
"ip : 71.78.22.43
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Нужно найти и исправить все ошибки, которые допускает наш сервис.
|
||||
|
||||
Решение:
|
||||
|
||||
```json
|
||||
{
|
||||
"info": "Sample JSON output from our service\t",
|
||||
"elements": [
|
||||
{
|
||||
"name": "first",
|
||||
"type": "server",
|
||||
"ip": 7175
|
||||
},
|
||||
{
|
||||
"name": "second",
|
||||
"type": "proxy",
|
||||
"ip": "71.78.22.43"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Обязательная задача 2
|
||||
|
||||
В прошлый рабочий день мы создавали скрипт, позволяющий опрашивать веб-сервисы и получать их IP.
|
||||
К уже реализованному функционалу нам нужно добавить возможность записи JSON и YAML файлов, описывающих наши сервисы.
|
||||
Формат записи JSON по одному сервису: `{ "имя сервиса" : "его IP"}`.
|
||||
Формат записи YAML по одному сервису: `- имя сервиса: его IP`.
|
||||
Если в момент исполнения скрипта меняется IP у сервиса - он должен так же поменяться в yml и json файле.
|
||||
|
||||
#### Ваш скрипт:
|
||||
|
||||
[script](./q2.py)
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import io
|
||||
import os
|
||||
import socket
|
||||
import yaml
|
||||
|
||||
hosts_list = {
|
||||
"drive.google.com",
|
||||
"mail.google.com",
|
||||
"google.com"
|
||||
}
|
||||
|
||||
filename_json = 'hosts.json'
|
||||
filename_yaml = 'hosts.yaml'
|
||||
|
||||
if not os.path.exists(filename_json):
|
||||
os.mknod(filename_json)
|
||||
|
||||
if not os.path.exists(filename_yaml):
|
||||
os.mknod(filename_yaml)
|
||||
|
||||
file_json = None
|
||||
file_yaml = None
|
||||
|
||||
try:
|
||||
file_json = open(filename_json, 'r+')
|
||||
file_yaml = open(filename_yaml, 'r+')
|
||||
|
||||
json_str = file_json.read()
|
||||
try:
|
||||
json_obj = json.load(io.StringIO(json_str))
|
||||
except BaseException as err:
|
||||
json_obj = dict({})
|
||||
print('error: {}'.format(err))
|
||||
|
||||
for hostname in hosts_list:
|
||||
ip_addr = socket.gethostbyname(hostname)
|
||||
prev_ip_addr = json_obj.get(hostname)
|
||||
|
||||
if prev_ip_addr is None or prev_ip_addr == '':
|
||||
prev_ip_addr = ip_addr
|
||||
|
||||
print('{} - {}'.format(hostname, ip_addr))
|
||||
if ip_addr != prev_ip_addr:
|
||||
print('[ERROR] {} IP mismatch: {} {}'.format(hostname, ip_addr, prev_ip_addr))
|
||||
|
||||
json_obj[hostname] = ip_addr
|
||||
|
||||
file_json.truncate(0)
|
||||
file_json.seek(0)
|
||||
file_json.write(json.dumps(json_obj))
|
||||
|
||||
file_yaml.truncate(0)
|
||||
file_yaml.seek(0)
|
||||
file_yaml.write(yaml.dump(json_obj))
|
||||
except BaseException as err:
|
||||
print('error {}'.format(err))
|
||||
finally:
|
||||
if file_json is not None:
|
||||
file_json.close()
|
||||
|
||||
if file_yaml is not None:
|
||||
file_yaml.close()
|
||||
|
||||
```
|
||||
|
||||
#### Вывод скрипта при запуске при тестировании:
|
||||
|
||||
```shell
|
||||
./q2.py
|
||||
error: Expecting value: line 1 column 1 (char 0)
|
||||
mail.google.com - 64.233.162.17
|
||||
google.com - 74.125.205.102
|
||||
drive.google.com - 142.250.150.194
|
||||
```
|
||||
|
||||
#### json-файл(ы), который(е) записал ваш скрипт:
|
||||
|
||||
```json
|
||||
{"mail.google.com": "64.233.162.17", "google.com": "74.125.205.102", "drive.google.com": "142.250.150.194"}
|
||||
```
|
||||
|
||||
#### yml-файл(ы), который(е) записал ваш скрипт:
|
||||
|
||||
```yaml
|
||||
drive.google.com: 142.250.150.194
|
||||
google.com: 74.125.205.102
|
||||
mail.google.com: 64.233.162.17
|
||||
```
|
||||
78
src/homework/04-script/notes.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Script languages
|
||||
|
||||
## Bash
|
||||
|
||||
* замена значения переменной
|
||||
|
||||
```shell
|
||||
a=1234
|
||||
a=${a/12/FOO}
|
||||
echo $a
|
||||
FOO34
|
||||
```
|
||||
|
||||
* объявление и вывод массива
|
||||
|
||||
```shell
|
||||
arrayInt=(1 2 3 4 5)
|
||||
# первый элемент
|
||||
echo $arrayInt
|
||||
1
|
||||
# все элементы
|
||||
echo ${arrayInt[@]}
|
||||
1 2 3 4 5
|
||||
# конкретный элемент
|
||||
echo ${arrayInt[3]}
|
||||
4
|
||||
# индексы
|
||||
echo ${!arrayInt[@]}
|
||||
# размерность
|
||||
echo ${#arrayInt[@]}
|
||||
# присвоение массива из команды
|
||||
arrayLs=($(ls))
|
||||
# добавление элементов в конец массива
|
||||
arrayInt+=(12 123 13)
|
||||
```
|
||||
|
||||
* Разделитель значений для bash
|
||||
|
||||
```shell
|
||||
echo $IFS
|
||||
export IFS=;
|
||||
unset IFS
|
||||
```
|
||||
|
||||
## YAML
|
||||
|
||||
* Типы данных
|
||||
|
||||
```yaml
|
||||
root:
|
||||
emptyValue:
|
||||
booleanTrue: true
|
||||
booleanFalse: false
|
||||
canonTime: 2020-12-15T00:30:44.1Z
|
||||
date: 2020-12-15
|
||||
list:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
- name: one
|
||||
type: two
|
||||
default: true
|
||||
using: [ localhost, 7.7.7.7 ]
|
||||
```
|
||||
|
||||
* Многострочные значения в ключе
|
||||
|
||||
```yaml
|
||||
---
|
||||
root:
|
||||
first:|
|
||||
Этот вид
|
||||
сохранит все переходы на новую строку
|
||||
second:>
|
||||
А этот
|
||||
преобразует каждый переход на новую строку
|
||||
в пробел
|
||||
```
|
||||
115
src/homework/05-virtualization/5.1/readme.md
Normal file
@@ -0,0 +1,115 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/virt-homeworks/blob/virt-11/05-virt-01-basics/README.md)
|
||||
по теме "5.1. Основы виртуализации".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Опишите кратко, как вы поняли: в чем основное отличие полной (аппаратной) виртуализации, паравиртуализации и виртуализации на основе ОС.
|
||||
|
||||
Главное отличие данных типов вирутализации в том, как именно ОС внутри виртуальной машины взаимодействует с реальным аппаратным комплексом:
|
||||
1. Полной (аппаратная) виртуализация - это полноценная операционная система, которая имеет непосредственный доступ к "железу".
|
||||
2. Паравиртуализация реализует взаимодействие через прослойку-гипервизор, которая имеет специальное API для гостевой ОС,
|
||||
но при этом обращается к аппаратной части через ядро хостовой операционной системы.
|
||||
3. Виртуализации на основе ОС реализует непосредственное взаимодействие гостевой ОС через системные вызовы ядра хостовой операционной системы
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Выберите один из вариантов использования организации физических серверов, в зависимости от условий использования.
|
||||
>
|
||||
> Организация серверов:
|
||||
> * физические сервера
|
||||
> * паравиртуализация
|
||||
> * виртуализация уровня ОС
|
||||
>
|
||||
> Условия использования:
|
||||
> * Высоконагруженная база данных, чувствительная к отказу
|
||||
> * Различные web-приложения
|
||||
> * Windows системы для использования бухгалтерским отделом
|
||||
> * Системы, выполняющие высокопроизводительные расчеты на GPU
|
||||
>
|
||||
> Опишите, почему вы выбрали к каждому целевому использованию такую организацию.
|
||||
|
||||
1. Высоконагруженная база данных, чувствительная к отказу
|
||||
|
||||
Для данной задачи лучше всего подойдут физические сервера. Это объясняется тем, что любая виртуализация накладывает дополнительные
|
||||
риски на стабильность работы системы. При этом во взаимодействии между гостевой ОС и хостом есть дополнительные расходы,
|
||||
что тоже может быть очень критично в высоконагруженных системах.
|
||||
|
||||
2. Различные web-приложения
|
||||
|
||||
В данном случае лучше всего использовать виртуализацию уровня операционной системы. Это обосновано тем,
|
||||
что по задаче приложения имеют различное назначение (нет уточнений про отказоусточивость, ожидаемую нагрузку, скалируемость и т.п.).
|
||||
При этом они должны запускаться в определённом окружении (желательно одинаковым для каждого приложения),
|
||||
а для безопасности, данные приложения должны быть изолированы. Таким образом вируализация уровня ОС подходит под данную задачу.
|
||||
|
||||
3. Windows системы для использования бухгалтерским отделом
|
||||
|
||||
В данном случае лучше всего будет использовать паравиртуализацию,
|
||||
что обеспечит изолированность систем с минимальной потерей скорости взаимодействия пользователей с системами.
|
||||
|
||||
4. Системы, выполняющие высокопроизводительные расчеты на GPU
|
||||
|
||||
Для данной задачи лучше всего будет использовать физические сервера, так как не все гипервизоры для паравиртуализации имеют поддержку GPU,
|
||||
а некоторые поддерживают GPU только определённых вендоров/моделей. Вирутализация уровня ОС здесь только помешает,
|
||||
так как добавляет дополнительные расходы на взаимодействие виртуальной машины и хоста.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Выберите подходящую систему управления виртуализацией для предложенного сценария. Детально опишите ваш выбор.
|
||||
|
||||
1. 100 виртуальных машин на базе `Linux` и `Windows`, общие задачи, нет особых требований. Преимущественно `Windows` based инфраструктура, требуется реализация программных балансировщиков нагрузки, репликации данных и автоматизированного механизма создания резервных копий.
|
||||
|
||||
Для данного сценария наиболее предпочтительно будет использование решение `Microsoft Hyper-V`. Выбор обсловлен следующими факторами:
|
||||
* `Преимущественно Windows based инфраструктура`, а значит лучшим выбором здесь будет решение, которое нативно умеет поддерживать данную ОС.
|
||||
При этом, `Hyper-V` имеет поддержку и [Linux-систем](https://www.windowscentral.com/how-run-linux-distros-windows-10-using-hyper-v).
|
||||
* "Из коробки" имеет поддержку репликации и миграции виртуальных машин, а значит подходит под требование резервного копирования и балансировки нагрузки.
|
||||
* Так как больше нет никаких особых требований, то в этом случае более низкий порог вхождения для инженеров в данную технологию будет плюсом.
|
||||
|
||||
2. Требуется наиболее производительное бесплатное open source решение для виртуализации небольшой (20-30 серверов) инфраструктуры на базе Linux и Windows виртуальных машин.
|
||||
|
||||
В данном сценарии наиболее подходящей технологией является `Xen`:
|
||||
* `Xen` - это open source решение.
|
||||
* Имеет поддержку гостевых ОС как `Linux`, так и `Windows`. Для каждого типа ОС есть свой оптимальный режим виртуализации.
|
||||
Данные режимы можно использовать одновременно на одном хосте.
|
||||
* Имеет высокую производительность.
|
||||
* Каждая виртуальная машина утилизирует только точно выделенные ресурсы без влияния на производительность других соседних виртуальных машин.
|
||||
Подобная проблема есть в `KVM`, поэтому здесь предпочтение отдаётся `Xen`.
|
||||
|
||||
3. Необходимо бесплатное, максимально совместимое и производительное решение для виртуализации Windows инфраструктуры.
|
||||
|
||||
В данном случае стоит использовать `Microsoft Hyper-V Server`:
|
||||
* Является бесплатной операционной системой
|
||||
* Разработан для нативной поддержки виртуальных машин на базе Windows, а значит имеет максимальную совместимость и производительность для текущих потребностей.
|
||||
|
||||
4. Необходимо рабочее окружение для тестирования программного продукта на нескольких дистрибутивах `Linux`.
|
||||
|
||||
Для данного сценария подойдёт `KVM`. Из плюсов этого выбора можно выделить:
|
||||
* Хорошая поддержка `Linux` в качестве гостевых ОС
|
||||
* Близкая к нативной производительность, что полезно для тестирования, так как минимизирует различные накладные расходы на взаимодействие с виртуальной машиной.
|
||||
* Есть возможность построить мониторинг всех виртуальных машин для тестов на хосте, то есть считывать различные метрики приложений из хостовой ОС.
|
||||
* В моём понимании тестирование продукта - это краткосрочный процесс, который предполагает,
|
||||
что виртуальная машина поднимается на короткий срок, и после окончания тестирования удаляется.
|
||||
Исходя из этого понимания, появляется преимущество в виде создания шаблонов виртуальных машин для тестирования в формате ISO-образа,
|
||||
которые могут использоваться как для развёртывания ВМ внутри стенда, так для использования людьми (например, специалистами QA-отдела или разработки).
|
||||
|
||||
### Задача 4
|
||||
> Опишите возможные проблемы и недостатки гетерогенной среды виртуализации (использования нескольких систем управления виртуализацией одновременно) и что необходимо сделать для минимизации этих рисков и проблем.
|
||||
|
||||
Самая большая проблема гетерогенной среды виртуализации - это высокие расходы на поддержку и управления нескольких систем управления виртуальными машинами одновременно.
|
||||
То есть, для подобного решения необходимо сразу несколько инженеров, которые будут специалистами в своей конкретной области/технологии.
|
||||
|
||||
Ещё одна проблема, которая может возникнуть - проблемы перемещения данных между виртуальными машинами, работающими в разных гипервизорах.
|
||||
|
||||
Для минимизации проблем, в общем случае, необходимо стараться максимально стандартизировать подход к виртуализации внутри компании.
|
||||
|
||||
> Если бы у вас был выбор, то создавали бы вы гетерогенную среду или нет? Мотивируйте ваш ответ примерами.
|
||||
|
||||
Для выбора решения необходимо на этапе построения всей инфраструктуры решить множество вопросов:
|
||||
* определить, какие решения есть на рынке виртуализаций и сделать некие прогнозы на будущее
|
||||
* сделать анализ рынка вакансий, определить "популярность" решений вирутализации
|
||||
* определить конкретные требования к инфрастуктуре: какие системы/сервисы будут разворачиваться на виртуальных машинах,
|
||||
кто будет пользовать данными системами и т.п.
|
||||
* определить требования к безопасности
|
||||
|
||||
Только после ответа на данные вопросы можно решить, использовать ли единое решение для построения виртуализации, либо совместить решения.
|
||||
119
src/homework/05-virtualization/5.2/readme.md
Normal file
@@ -0,0 +1,119 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/virt-homeworks/blob/virt-11/05-virt-02-iaac/README.md)
|
||||
по теме "5.2. Применение принципов IaaC в работе с виртуальными машинами".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
> Опишите своими словами основные преимущества применения на практике IaaC паттернов.
|
||||
|
||||
Основные преимущества применения IaaC паттернов:
|
||||
1. Хранение всей конфигурации инфраструктуры под системой контроля версий. Это даёт множество приемуществ:
|
||||
1. Версионирование конфигурации, что позволяет сравнивать версии, видеть развитие, отлавливать ошибки на код-ревью.
|
||||
2. Хранение конфигурации в централизованном хранилище (например, `github`/`gitlab`/`bitbucket`)
|
||||
3. Возможность коллаборации инженеров в работе над конфигурацией. То есть, возможность одновременно вносить доработки
|
||||
в одну часть инфраструктуры одним человеком, и добавлять новые сервисы другим.
|
||||
2. Возможность "прочитать" конфигурацию, чтобы понять, как она работает, а не выяснять это опытным путём.
|
||||
3. Возможность тестирования конфигурации.
|
||||
4. Возможность автоматизировать частично или полностью применение конфигурации к инфраструктуре.
|
||||
|
||||
> Какой из принципов IaaC является основополагающим?
|
||||
|
||||
Основополагающий принцип IaaC - это идемпотентность. То есть, применяя готовую конфигурацию к инфраструктуре
|
||||
(например, развёртывание виртуальных машин) инженер будет получать один и тот же ожидаемый результат, который не будет меняться,
|
||||
сколько бы попыток не было.
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Чем Ansible выгодно отличается от других систем управление конфигурациями?
|
||||
|
||||
Самое главное преимущество `ansible` - это необходимость установки утилиты только на машине,
|
||||
откуда необходимо запустить применение конфигурации. То есть, на физических/виртуальных машинах, где будут происходить действия
|
||||
по настройке никаких дополнительных утилит устанавливать не нужно.
|
||||
|
||||
> Какой, на ваш взгляд, метод работы систем конфигурации более надёжный push или pull?
|
||||
|
||||
Принцип `push` более надёжный, так как для данного метода не нужно держать активным некий сервис,
|
||||
который будет принимать и обрабатывать запросы клиентов на обновление конфигурации.
|
||||
|
||||
При этом построение развёртывания конфигурации по принципу `push` проще, чем построение гибридного подхода,
|
||||
что повышает надёжность на первых этапах построения инфраструктуры.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> Установить на личный компьютер:
|
||||
> * VirtualBox
|
||||
> * Vagrant
|
||||
> * Ansible
|
||||
>
|
||||
> Приложить вывод команд установленных версий каждой из программ, оформленный в markdown.
|
||||
|
||||
```shell
|
||||
virtualbox --help
|
||||
Oracle VM VirtualBox VM Selector v6.1.32
|
||||
(C) 2005-2022 Oracle Corporation
|
||||
All rights reserved.
|
||||
```
|
||||
|
||||
```shell
|
||||
vagrant --version
|
||||
Vagrant 2.2.19
|
||||
```
|
||||
|
||||
```shell
|
||||
ansible --version
|
||||
ansible [core 2.12.4]
|
||||
config file = /etc/ansible/ansible.cfg
|
||||
configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
|
||||
ansible python module location = /usr/lib/python3/dist-packages/ansible
|
||||
ansible collection location = ~/.ansible/collections:/usr/share/ansible/collections
|
||||
executable location = /usr/bin/ansible
|
||||
python version = 3.8.10 (default, Mar 15 2022, 12:22:08) [GCC 9.4.0]
|
||||
jinja version = 2.10.1
|
||||
libyaml = True
|
||||
```
|
||||
|
||||
### Задача 4 (*)
|
||||
|
||||
> Воспроизвести практическую часть лекции самостоятельно.
|
||||
> Создать виртуальную машину.
|
||||
|
||||
Для создания виртуальной машины используется уже готовый [`Vagrantfile`](/src/vagrant/Vagrantfile),
|
||||
в который дополнительно добавлены команды необходимые для установки `docker`:
|
||||
|
||||
```
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
apt update
|
||||
apt install -y ca-certificates \
|
||||
curl \
|
||||
gnupg \
|
||||
lsb-release
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
|
||||
$(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
apt update
|
||||
apt install -y docker-ce docker-ce-cli containerd.io
|
||||
usermod -aG docker vagrant
|
||||
SHELL
|
||||
```
|
||||
|
||||
> Зайти внутрь ВМ, убедиться, что Docker установлен с помощью команды `docker ps`
|
||||
|
||||
```shell
|
||||
vagrant ssh
|
||||
Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.4.0-91-generic x86_64)
|
||||
<...>
|
||||
|
||||
vagrant@vagrant:~$ docker ps -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
|
||||
vagrant@vagrant:~$ docker run --rm hello-world
|
||||
Unable to find image 'hello-world:latest' locally
|
||||
latest: Pulling from library/hello-world
|
||||
2db29710123e: Pull complete
|
||||
Digest: sha256:10d7d58d5ebd2a652f4d93fdd86da8f265f5318c6a73cc5b6a9798ff6d2b2e67
|
||||
Status: Downloaded newer image for hello-world:latest
|
||||
|
||||
Hello from Docker!
|
||||
<...>
|
||||
```
|
||||
|
||||
3
src/homework/05-virtualization/5.3/linux-data/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*
|
||||
|
||||
!.gitignore
|
||||
4
src/homework/05-virtualization/5.3/nginx/Dockerfile
Normal file
@@ -0,0 +1,4 @@
|
||||
FROM nginx:1.21.6-alpine
|
||||
|
||||
COPY site.conf /etc/nginx/conf.d/default.conf
|
||||
COPY index.html /var/www/netology/index.html
|
||||
39
src/homework/05-virtualization/5.3/nginx/build.yml
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
- hosts: 127.0.0.1
|
||||
gather_facts: no
|
||||
connection: local
|
||||
tasks:
|
||||
- name: create build directory
|
||||
file:
|
||||
path: /tmp/ansible-docker-build
|
||||
state: directory
|
||||
owner: 1000
|
||||
group: 1000
|
||||
mode: '0755'
|
||||
- name: copy Dockerfile
|
||||
copy:
|
||||
src: ./Dockerfile
|
||||
dest: /tmp/ansible-docker-build/Dockerfile
|
||||
owner: 1000
|
||||
group: 1000
|
||||
mode: '0644'
|
||||
- name: copy site.conf
|
||||
copy:
|
||||
src: ./site.conf
|
||||
dest: /tmp/ansible-docker-build/site.conf
|
||||
owner: 1000
|
||||
group: 1000
|
||||
mode: '0644'
|
||||
- name: copy index.html
|
||||
copy:
|
||||
src: ./index.html
|
||||
dest: /tmp/ansible-docker-build/index.html
|
||||
owner: 1000
|
||||
group: 1000
|
||||
mode: '0644'
|
||||
- name: build container image
|
||||
docker_image:
|
||||
name: dannecron/netology-devops-nginx:ansible
|
||||
source: build
|
||||
build:
|
||||
path: /tmp/ansible-docker-build/
|
||||
8
src/homework/05-virtualization/5.3/nginx/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
Hey, Netology
|
||||
</head>
|
||||
<body>
|
||||
<h1>I`m DevOps Engineer!</h1>
|
||||
</body>
|
||||
</html>
|
||||
12
src/homework/05-virtualization/5.3/nginx/publish.yml
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
- hosts: 127.0.0.1
|
||||
gather_facts: no
|
||||
connection: local
|
||||
tasks:
|
||||
- name: login to hub.docker.com
|
||||
command: docker login
|
||||
- name: push image to hub.docker.com
|
||||
docker_image:
|
||||
name: dannecron/netology-devops-nginx:ansible
|
||||
source: local
|
||||
push: yes
|
||||
9
src/homework/05-virtualization/5.3/nginx/site.conf
Normal file
@@ -0,0 +1,9 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
|
||||
location / {
|
||||
root /var/www/netology;
|
||||
index index.html index.htm;
|
||||
}
|
||||
}
|
||||
215
src/homework/05-virtualization/5.3/readme.md
Normal file
@@ -0,0 +1,215 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/virt-homeworks/blob/virt-11/05-virt-03-docker/README.md)
|
||||
по теме "5.3. Введение. Экосистема. Архитектура. Жизненный цикл Docker контейнера".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Сценарий выполения задачи:
|
||||
>
|
||||
> - создайте свой репозиторий на https://hub.docker.com;
|
||||
> - выберете любой образ, который содержит веб-сервер Nginx;
|
||||
> - создайте свой fork образа;
|
||||
> - реализуйте функциональность:
|
||||
> запуск веб-сервера в фоне с индекс-страницей, содержащей HTML-код ниже:
|
||||
> ```html
|
||||
> <html>
|
||||
> <head>
|
||||
> Hey, Netology
|
||||
> </head>
|
||||
> <body>
|
||||
> <h1>I’m DevOps Engineer!</h1>
|
||||
> </body>
|
||||
> </html>
|
||||
> ```
|
||||
> Опубликуйте созданный форк в своем репозитории и предоставьте ответ в виде ссылки на https://hub.docker.com/username_repo.
|
||||
|
||||
Репозиторий с образом: [https://hub.docker.com/r/dannecron/netology-devops-nginx](https://hub.docker.com/r/dannecron/netology-devops-nginx)
|
||||
|
||||
За основу был взят [официальный образ nginx](https://hub.docker.com/_/nginx). Для решения задачи был сформирован [Dockerfile](./nginx/Dockerfile),
|
||||
а также конфигурация [site.conf](./nginx/site.conf) и [index.html](./nginx/index.html).
|
||||
|
||||
Для сборки образа используем команду из директории [nginx](./nginx):
|
||||
|
||||
```shell
|
||||
docker build -t dannecron/netology-devops-nginx:latest .
|
||||
[+] Building 6.6s (9/9) FINISHED
|
||||
```
|
||||
|
||||
Проверим, что всё работает. Для этого запустим контейнер из только что созданного образа и сделаем curl-запрос:
|
||||
|
||||
```shell
|
||||
docker run --rm -it -p 18800:80 dannecron/netology-devops-nginx:latest
|
||||
|
||||
curl http://localhost:18800
|
||||
<html>
|
||||
<head>
|
||||
Hey, Netology
|
||||
</head>
|
||||
<body>
|
||||
<h1>I`m DevOps Engineer!</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
После этого опубликуем образ:
|
||||
|
||||
```shell
|
||||
docker push dannecron/netology-devops-nginx:latest
|
||||
```
|
||||
|
||||
### Задача 2
|
||||
|
||||
> Посмотрите на сценарий ниже и ответьте на вопрос:
|
||||
> "Подходит ли в этом сценарии использование Docker контейнеров или лучше подойдет виртуальная машина, физическая машина? Может быть возможны разные варианты?"
|
||||
>
|
||||
> Детально опишите и обоснуйте свой выбор.
|
||||
> --
|
||||
>
|
||||
> Сценарий:
|
||||
>
|
||||
> - Высоконагруженное монолитное java веб-приложение;
|
||||
> - Nodejs веб-приложение;
|
||||
> - Мобильное приложение c версиями для Android и iOS;
|
||||
> - Шина данных на базе Apache Kafka;
|
||||
> - Elasticsearch кластер для реализации логирования продуктивного веб-приложения - три ноды elasticsearch, два logstash и две ноды kibana;
|
||||
> - Мониторинг-стек на базе Prometheus и Grafana;
|
||||
> - MongoDB, как основное хранилище данных для java-приложения;
|
||||
> - Gitlab сервер для реализации CI/CD процессов и приватный (закрытый) Docker Registry.
|
||||
|
||||
* Высоконагруженное монолитное java веб-приложение
|
||||
|
||||
В данном случае `docker` будет оптимальным решением, так как не будет требовать каких-то дополнительных настроек JVM, а для запуска достаточно будет установленного docker.
|
||||
В процессе упаковки приложения в образ можно произвести оптимизацию, которая уменьшит размер финального образа,
|
||||
что минимизирует растраты на трафик при деплое и растраты на хранение в registry.
|
||||
Ещё одним плюсом является возможность горизонтального масштабирования приложения без дополнительных настроек самого приложения.
|
||||
|
||||
* Nodejs веб-приложение
|
||||
|
||||
Аналогично, как и для java веб-приложения, docker будет оптимальным решением. Плюсы подхода сохраняются.
|
||||
|
||||
* Мобильное приложение c версиями для Android и iOS
|
||||
|
||||
Само мобильное приложение необходимо запускать только на физическом устройстве, так как мобильные устройства не поддерживают
|
||||
использование `docker` или виртуализацию.
|
||||
|
||||
* Шина данных на базе Apache Kafka
|
||||
|
||||
Шина данных на базе Apache Kafka - это кластерное решение, состоящее как минимум из двух сервисов
|
||||
и очень сильно зависящее от доступности сети.
|
||||
В таком случае будет наиболее оптимально запускать сервисы в виртуальных машинах, чтобы была возможность
|
||||
более гибко конфигурировать локальную сеть для данного кластера, при этом увеличив стабильность работы.
|
||||
|
||||
* Elasticsearch кластер для реализации логирования продуктивного веб-приложения - три ноды elasticsearch, два logstash и две ноды kibana
|
||||
|
||||
Ввиду того, что `elasticsearch` - это своеобразная база данных, нуждающаяся в достаточно тонкой настройке,
|
||||
особенно для большого количества логов, то оптимально будет использовать виртуальные либо физические машины,
|
||||
в зависимости от требований к нагрузке.
|
||||
|
||||
* Мониторинг-стек на базе Prometheus и Grafana
|
||||
|
||||
Так как `Prometheus` - это база данных, то оптимально будет выбрать решение на базе физической машины,
|
||||
чтобы увеличить отказоустойчивость и уменьшить расходы с взаимодействием с физическими дисками.
|
||||
|
||||
При этом само веб-приложение `Grafana` можно развернуть с использованием докера, так как это стандартный сервер для обработки запросов.
|
||||
|
||||
* MongoDB, как основное хранилище данных для java-приложения
|
||||
|
||||
Так как `MongoDB` - это база данных, то оптимально будет выбрать решение на базе физической машины,
|
||||
чтобы увеличить отказоустойчивость и уменьшить расходы с взаимодействием с физическими дисками.
|
||||
|
||||
* Gitlab сервер для реализации CI/CD процессов и приватный (закрытый) Docker Registry
|
||||
|
||||
В данном случае оптимально будет запустить сервер на физической или виртуальной машине. Это обусловлено тем,
|
||||
что `Docker Registry` активно взаимодействует с файловой системой, поэтому необходимо минимизировать издержки на данное взаимодействие.
|
||||
При этом сам `Gitlab` - это стек из множества сервисов, в том числе и база данных `postgresql`,
|
||||
поэтому для более удобной конфигурации и администрирования в данном случае лучше не использовать `docker`.
|
||||
|
||||
### Задача 3
|
||||
|
||||
> - Запустите первый контейнер из образа ***centos*** c любым тэгом в фоновом режиме, подключив папку ```/data``` из текущей рабочей директории на хостовой машине в ```/data``` контейнера;
|
||||
> - Запустите второй контейнер из образа ***debian*** в фоновом режиме, подключив папку ```/data``` из текущей рабочей директории на хостовой машине в ```/data``` контейнера;
|
||||
> - Подключитесь к первому контейнеру с помощью ```docker exec``` и создайте текстовый файл любого содержания в ```/data```;
|
||||
> - Добавьте еще один файл в папку ```/data``` на хостовой машине;
|
||||
> - Подключитесь во второй контейнер и отобразите листинг и содержание файлов в ```/data``` контейнера.
|
||||
|
||||
Запуск контейнера с `centos` в фоновом режиме:
|
||||
|
||||
```shell
|
||||
docker run --rm -it -d --name centos-linux -v `pwd`/linux-data:/data centos:7.9.2009 tail -f /dev/null
|
||||
docker ps | grep centos
|
||||
763f82085972 centos:7.9.2009 "tail -f /dev/null" 4 seconds ago Up 3 seconds centos-linux
|
||||
```
|
||||
|
||||
Запуск контейнера с `debian` в фоновом режиме:
|
||||
|
||||
```shell
|
||||
docker run --rm -it -d --name debian-linux -v `pwd`/linux-data:/data debian:11.3-slim tail -f /dev/null
|
||||
docker ps | grep debian
|
||||
ed46ef5be3dc debian:11.3-slim "tail -f /dev/null" 15 seconds ago Up 14 seconds debian-linux
|
||||
```
|
||||
|
||||
Подключаемся к контейнеру `centos-linux` и создадим там текстовый файл:
|
||||
|
||||
```shell
|
||||
docker exec -it centos-linux bash
|
||||
cd /data/
|
||||
echo "some test text" >> test.txt
|
||||
ls -l
|
||||
total 8
|
||||
-rw-r--r-- 1 root root 15 May 5 03:06 test.txt
|
||||
```
|
||||
|
||||
Создадим ещё один текстовый файл на хост-машине:
|
||||
|
||||
```shell
|
||||
echo "host text test" >> linux-data/host.txt
|
||||
ls -l linux-data
|
||||
total 8
|
||||
-rw-rw-r-- 1 dannc dannc 15 мая 5 10:08 host.txt
|
||||
-rw-r--r-- 1 root root 15 мая 5 10:06 test.txt
|
||||
```
|
||||
|
||||
Проверим, что оба файла находятся в контейнере `debian-linux`:
|
||||
|
||||
```shell
|
||||
docker exec debian-linux bash -c "ls -l /data"
|
||||
total 8
|
||||
-rw-rw-r-- 1 1000 1000 15 May 5 03:08 host.txt
|
||||
-rw-r--r-- 1 root root 15 May 5 03:06 test.txt
|
||||
```
|
||||
|
||||
### Задача 4 (*)
|
||||
|
||||
> Соберите Docker образ с Ansible, загрузите на Docker Hub и пришлите ссылку вместе с остальными ответами к задачам.
|
||||
|
||||
За основу возьмём [`Dockerfile`](./nginx/Dockerfile) для `nginx` из [задачи 1](#задача-1).
|
||||
|
||||
Создадим новый `playbook` в файле [build.yml](./nginx/build.yml). Добавим туда несколько действий:
|
||||
|
||||
1. создание временной директории.
|
||||
2. копирование `Dockerfile`, конфигурации и `index.html` в данную директорию.
|
||||
3. сборка образа.
|
||||
|
||||
Проверим, что всё работает и образ собирается:
|
||||
|
||||
```shell
|
||||
ansible-playbook build.yml
|
||||
<...>
|
||||
docker image list | grep ansible
|
||||
dannecron/netology-devops-nginx ansible 58df80189129 17 seconds ago 23.4MB
|
||||
```
|
||||
|
||||
Для публикации в публичный регстри выделим отдельный `playbook`: [publish.yml](./nginx/publish.yml), в котром будет два шага:
|
||||
|
||||
1. авторизация в `hub.docker.com`.
|
||||
2. публикация образа.
|
||||
|
||||
Проверим, что всё работает:
|
||||
|
||||
```shell
|
||||
ansible-playbook publish.yml
|
||||
<...>
|
||||
```
|
||||
|
||||
Образ доступен по ссылке [hub.docker.com](https://hub.docker.com/layers/208296992/dannecron/netology-devops-nginx/ansible/images/sha256-e007739a276cbc1f556b674b1ec142360afe4fa35364f3a8b464e74786da4e3d?context=repo)
|
||||
1
src/homework/05-virtualization/5.4/ansible/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
inventory
|
||||
5
src/homework/05-virtualization/5.4/ansible/ansible.cfg
Normal file
@@ -0,0 +1,5 @@
|
||||
[defaults]
|
||||
inventory=./inventory
|
||||
deprecation_warnings=False
|
||||
command_warnings=False
|
||||
ansible_port=22
|
||||
@@ -0,0 +1,5 @@
|
||||
[nodes:children]
|
||||
manager
|
||||
|
||||
[manager]
|
||||
node01.netology.cloud ansible_host=
|
||||
63
src/homework/05-virtualization/5.4/ansible/provision.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
|
||||
- hosts: nodes
|
||||
become: yes
|
||||
become_user: root
|
||||
remote_user: centos
|
||||
|
||||
tasks:
|
||||
- name: Create directory for ssh-keys
|
||||
file: state=directory mode=0700 dest=/root/.ssh/
|
||||
|
||||
- name: Adding rsa-key in /root/.ssh/authorized_keys
|
||||
copy: src=~/.ssh/id_rsa.pub dest=/root/.ssh/authorized_keys owner=root mode=0600
|
||||
ignore_errors: yes
|
||||
|
||||
- name: Checking DNS
|
||||
command: host -t A google.com
|
||||
|
||||
- name: Installing tools
|
||||
yum: >
|
||||
pkg={{ item }}
|
||||
state=present
|
||||
update_cache=yes
|
||||
with_items:
|
||||
- git
|
||||
- curl
|
||||
|
||||
- name: Add docker repository
|
||||
command: yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||||
|
||||
- name: Installing docker package
|
||||
yum: >
|
||||
pkg={{ item }}
|
||||
state=present
|
||||
update_cache=yes
|
||||
with_items:
|
||||
- docker-ce
|
||||
- docker-ce-cli
|
||||
- containerd.io
|
||||
|
||||
- name: Enable docker daemon
|
||||
systemd:
|
||||
name: docker
|
||||
state: started
|
||||
enabled: yes
|
||||
|
||||
- name: Install docker-compose
|
||||
raw: $(curl -L https://github.com/docker/compose/releases/download/1.29.2/docker-compose-`uname -s`-`uname -m` -o /usr/bin/docker-compose && chmod +x /usr/bin/docker-compose)
|
||||
|
||||
- name: Synchronization
|
||||
copy:
|
||||
src: stack/
|
||||
dest: "/opt/stack/"
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
become: true
|
||||
|
||||
- name: Pull all images in compose
|
||||
command: docker-compose -f /opt/stack/docker-compose.yaml pull
|
||||
|
||||
- name: Up all services in compose
|
||||
command: docker-compose -f /opt/stack/docker-compose.yaml up -d
|
||||
2
src/homework/05-virtualization/5.4/ansible/stack/.env
Normal file
@@ -0,0 +1,2 @@
|
||||
ADMIN_USER=admin
|
||||
ADMIN_PASSWORD=admin
|
||||
@@ -0,0 +1,11 @@
|
||||
route:
|
||||
receiver: 'slack'
|
||||
|
||||
receivers:
|
||||
- name: 'slack'
|
||||
slack_configs:
|
||||
- send_resolved: true
|
||||
text: "{{ .CommonAnnotations.description }}"
|
||||
username: 'Prometheus'
|
||||
channel: '#<channel-name>'
|
||||
api_url: 'https://hooks.slack.com/services/<webhook-id>'
|
||||
@@ -0,0 +1,39 @@
|
||||
:9090 {
|
||||
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
|
||||
proxy / prometheus:9090 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
:9093 {
|
||||
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
|
||||
proxy / alertmanager:9093 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
:9091 {
|
||||
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
|
||||
proxy / pushgateway:9091 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
:3000 {
|
||||
proxy / grafana:3000 {
|
||||
transparent
|
||||
websocket
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
version: '2.1'
|
||||
|
||||
networks:
|
||||
monitoring:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
prometheus_data: {}
|
||||
grafana_data: {}
|
||||
|
||||
services:
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.17.1
|
||||
container_name: prometheus
|
||||
volumes:
|
||||
- ./prometheus:/etc/prometheus
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--web.console.libraries=/etc/prometheus/console_libraries'
|
||||
- '--web.console.templates=/etc/prometheus/consoles'
|
||||
- '--storage.tsdb.retention.time=15d'
|
||||
- '--web.enable-lifecycle'
|
||||
restart: always
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
alertmanager:
|
||||
image: prom/alertmanager:v0.20.0
|
||||
container_name: alertmanager
|
||||
volumes:
|
||||
- ./alertmanager:/etc/alertmanager
|
||||
command:
|
||||
- '--config.file=/etc/alertmanager/config.yml'
|
||||
- '--storage.path=/alertmanager'
|
||||
restart: always
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
nodeexporter:
|
||||
image: prom/node-exporter:v0.18.1
|
||||
container_name: nodeexporter
|
||||
volumes:
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /:/rootfs:ro
|
||||
command:
|
||||
- '--path.procfs=/host/proc'
|
||||
- '--path.rootfs=/rootfs'
|
||||
- '--path.sysfs=/host/sys'
|
||||
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
|
||||
restart: always
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
cadvisor:
|
||||
image: gcr.io/google-containers/cadvisor:v0.34.0
|
||||
container_name: cadvisor
|
||||
volumes:
|
||||
- /:/rootfs:ro
|
||||
- /var/run:/var/run:rw
|
||||
- /sys:/sys:ro
|
||||
- /var/lib/docker:/var/lib/docker:ro
|
||||
- /cgroup:/cgroup:ro
|
||||
restart: always
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:7.4.2
|
||||
container_name: grafana
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/provisioning:/etc/grafana/provisioning
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
|
||||
- GF_USERS_ALLOW_SIGN_UP=false
|
||||
restart: always
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
pushgateway:
|
||||
image: prom/pushgateway:v1.2.0
|
||||
container_name: pushgateway
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
caddy:
|
||||
image: stefanprodan/caddy
|
||||
container_name: caddy
|
||||
ports:
|
||||
- "0.0.0.0:3000:3000"
|
||||
- "0.0.0.0:9090:9090"
|
||||
- "0.0.0.0:9093:9093"
|
||||
- "0.0.0.0:9091:9091"
|
||||
volumes:
|
||||
- ./caddy:/etc/caddy
|
||||
environment:
|
||||
- ADMIN_USER=${ADMIN_USER:-admin}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
|
||||
restart: always
|
||||
networks:
|
||||
- monitoring
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
@@ -0,0 +1,36 @@
|
||||
version: '2.1'
|
||||
|
||||
services:
|
||||
|
||||
nodeexporter:
|
||||
image: prom/node-exporter:v0.18.1
|
||||
container_name: nodeexporter
|
||||
volumes:
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /:/rootfs:ro
|
||||
command:
|
||||
- '--path.procfs=/host/proc'
|
||||
- '--path.rootfs=/rootfs'
|
||||
- '--path.sysfs=/host/sys'
|
||||
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
cadvisor:
|
||||
image: gcr.io/google-containers/cadvisor:v0.34.0
|
||||
container_name: cadvisor
|
||||
volumes:
|
||||
- /:/rootfs:ro
|
||||
- /var/run:/var/run:rw
|
||||
- /sys:/sys:ro
|
||||
- /var/lib/docker/:/var/lib/docker:ro
|
||||
- /cgroup:/cgroup:ro
|
||||
restart: unless-stopped
|
||||
network_mode: host
|
||||
labels:
|
||||
org.label-schema.group: "monitoring"
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: 1
|
||||
|
||||
providers:
|
||||
- name: 'Prometheus'
|
||||
orgId: 1
|
||||
folder: ''
|
||||
type: file
|
||||
disableDeletion: false
|
||||
editable: true
|
||||
allowUiUpdates: true
|
||||
options:
|
||||
path: /etc/grafana/provisioning/dashboards
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: 1
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://prometheus:9090
|
||||
basicAuth: false
|
||||
isDefault: true
|
||||
editable: true
|
||||
@@ -0,0 +1,69 @@
|
||||
groups:
|
||||
- name: targets
|
||||
rules:
|
||||
- alert: monitor_service_down
|
||||
expr: up == 0
|
||||
for: 30s
|
||||
labels:
|
||||
severity: critical
|
||||
annotations:
|
||||
summary: "Monitor service non-operational"
|
||||
description: "Service {{ $labels.instance }} is down."
|
||||
|
||||
- name: host
|
||||
rules:
|
||||
- alert: high_cpu_load
|
||||
expr: node_load1 > 1.5
|
||||
for: 30s
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Server under high load"
|
||||
description: "Docker host is under high load, the avg load 1m is at {{ $value}}. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
|
||||
|
||||
- alert: high_memory_load
|
||||
expr: (sum(node_memory_MemTotal_bytes) - sum(node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes) ) / sum(node_memory_MemTotal_bytes) * 100 > 85
|
||||
for: 30s
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Server memory is almost full"
|
||||
description: "Docker host memory usage is {{ humanize $value}}%. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
|
||||
|
||||
- alert: high_storage_load
|
||||
expr: (node_filesystem_size_bytes{fstype="aufs"} - node_filesystem_free_bytes{fstype="aufs"}) / node_filesystem_size_bytes{fstype="aufs"} * 100 > 85
|
||||
for: 30s
|
||||
labels:
|
||||
severity: warning
|
||||
annotations:
|
||||
summary: "Server storage is almost full"
|
||||
description: "Docker host storage usage is {{ humanize $value}}%. Reported by instance {{ $labels.instance }} of job {{ $labels.job }}."
|
||||
|
||||
- name: containers
|
||||
rules:
|
||||
#- alert: jenkins_down
|
||||
# expr: absent(container_memory_usage_bytes{name="jenkins"})
|
||||
# for: 30s
|
||||
# labels:
|
||||
# severity: critical
|
||||
# annotations:
|
||||
# summary: "Jenkins down"
|
||||
# description: "Jenkins container is down for more than 30 seconds."
|
||||
|
||||
#- alert: jenkins_high_cpu
|
||||
# expr: sum(rate(container_cpu_usage_seconds_total{name="jenkins"}[1m])) / count(node_cpu_seconds_total{mode="system"}) * 100 > 10
|
||||
# for: 30s
|
||||
# labels:
|
||||
# severity: warning
|
||||
# annotations:
|
||||
# summary: "Jenkins high CPU usage"
|
||||
# description: "Jenkins CPU usage is {{ humanize $value}}%."
|
||||
|
||||
#- alert: jenkins_high_memory
|
||||
# expr: sum(container_memory_usage_bytes{name="jenkins"}) > 1200000000
|
||||
# for: 30s
|
||||
# labels:
|
||||
# severity: warning
|
||||
# annotations:
|
||||
# summary: "Jenkins high memory usage"
|
||||
# description: "Jenkins memory consumption is at {{ humanize $value}}."
|
||||
@@ -0,0 +1,55 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
# Attach these labels to any time series or alerts when communicating with
|
||||
# external systems (federation, remote storage, Alertmanager).
|
||||
external_labels:
|
||||
monitor: 'prometheus'
|
||||
|
||||
# Load and evaluate rules in this file every 'evaluation_interval' seconds.
|
||||
rule_files:
|
||||
- "alert.rules"
|
||||
|
||||
# A scrape configuration containing exactly one endpoint to scrape.
|
||||
scrape_configs:
|
||||
- job_name: 'nodeexporter'
|
||||
scrape_interval: 5s
|
||||
static_configs:
|
||||
- targets: ['nodeexporter:9100']
|
||||
|
||||
- job_name: 'cadvisor'
|
||||
scrape_interval: 5s
|
||||
static_configs:
|
||||
- targets: ['cadvisor:8080']
|
||||
|
||||
- job_name: 'prometheus'
|
||||
scrape_interval: 10s
|
||||
static_configs:
|
||||
- targets: ['localhost:9090']
|
||||
|
||||
- job_name: 'pushgateway'
|
||||
scrape_interval: 10s
|
||||
honor_labels: true
|
||||
static_configs:
|
||||
- targets: ['pushgateway:9091']
|
||||
|
||||
- job_name: 'alertmanager'
|
||||
scrape_interval: 10s
|
||||
honor_labels: true
|
||||
static_configs:
|
||||
- targets: ['alertmanager:9093']
|
||||
|
||||
- job_name: 'grafana'
|
||||
scrape_interval: 10s
|
||||
honor_labels: true
|
||||
static_configs:
|
||||
- targets: ['grafana:3000']
|
||||
|
||||
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- scheme: http
|
||||
static_configs:
|
||||
- targets:
|
||||
- 'alertmanager:9093'
|
||||
BIN
src/homework/05-virtualization/5.4/assets/cloud-images.png
Normal file
|
After Width: | Height: | Size: 137 KiB |
BIN
src/homework/05-virtualization/5.4/assets/cloud-vm.png
Normal file
|
After Width: | Height: | Size: 123 KiB |
BIN
src/homework/05-virtualization/5.4/assets/grafana.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
1
src/homework/05-virtualization/5.4/packer/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
centos-7-base.json
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"builders": [
|
||||
{
|
||||
"disk_type": "network-nvme",
|
||||
"folder_id": "",
|
||||
"image_description": "build by packer",
|
||||
"image_family": "centos",
|
||||
"image_name": "centos-7-base",
|
||||
"source_image_family": "centos-7",
|
||||
"ssh_username": "centos",
|
||||
"subnet_id": "",
|
||||
"token": "",
|
||||
"type": "yandex",
|
||||
"use_ipv4_nat": true,
|
||||
"zone": "ru-central1-a"
|
||||
}
|
||||
],
|
||||
"provisioners": [
|
||||
{
|
||||
"inline": [
|
||||
"sudo yum -y update",
|
||||
"sudo yum -y install bridge-utils bind-utils iptables curl net-tools tcpdump rsync telnet openssh-server"
|
||||
],
|
||||
"type": "shell"
|
||||
}
|
||||
]
|
||||
}
|
||||
77
src/homework/05-virtualization/5.4/readme.md
Normal file
@@ -0,0 +1,77 @@
|
||||
Выполнение [домашнего задания](https://github.com/netology-code/virt-homeworks/blob/virt-11/05-virt-04-docker-compose/README.md)
|
||||
по теме "5.4. Оркестрация группой Docker контейнеров на примере Docker Compose".
|
||||
|
||||
## Q/A
|
||||
|
||||
### Задача 1
|
||||
|
||||
> Создать собственный образ операционной системы с помощью Packer.
|
||||
>
|
||||
> Для получения зачета, вам необходимо предоставить:
|
||||
> - Скриншот страницы, как на слайде из презентации (слайд 37).
|
||||
|
||||
Пошаговая инструкция работы с yandex-облаком:
|
||||
1. Установка `yc`: `curl -sSL https://storage.yandexcloud.net/yandexcloud-yc/install.sh | bash`
|
||||
2. Инициализация конфигурации: `yc init`
|
||||
3. Проверка, что всё работает:
|
||||
```shell
|
||||
yc compute image list
|
||||
+----+------+--------+-------------+--------+
|
||||
| ID | NAME | FAMILY | PRODUCT IDS | STATUS |
|
||||
+----+------+--------+-------------+--------+
|
||||
+----+------+--------+-------------+--------+
|
||||
```
|
||||
4. Создание сети: `yc vpc network create --name net`
|
||||
5. Создание подсети: `yc vpc subnet create --name my-subnet-a --zone ru-central1-a --range 10.1.2.0/24 --network-name net --description "test subnet for test net"`
|
||||
6. Копирование примера конфигурации packer [centos-7-base.example.json](./packer/centos-7-base.example.json) в `centos-7-base.json`.
|
||||
7. Затем нужно заполнить недостающие поля в конфигурации.
|
||||
8. Запуск валидации конфигурации
|
||||
```shell
|
||||
packer validate packer/centos-7-base.json
|
||||
The configuration is valid.
|
||||
```
|
||||
9. Запуск сборки образа `packer build packer/centos-7-base.json`
|
||||
10. Удаление подсети: `yc vpc subnet delete --name my-subnet-a`
|
||||
11. Удаление сети: `yc vpc network delete --name net`
|
||||
|
||||
**Результат**:
|
||||
|
||||

|
||||
|
||||
### Задача 2
|
||||
|
||||
> Создать вашу первую виртуальную машину в Яндекс.Облаке.
|
||||
>
|
||||
> Для получения зачета, вам необходимо предоставить:
|
||||
> - Скриншот страницы свойств созданной ВМ
|
||||
|
||||
1. Копирование секретов для `terraform` из [variables.tf.example](./terraform/variables.tf.example) в `variables.tf`
|
||||
2. Затем нужно изменить поля в конфигурации.
|
||||
3. Инициализировать конфигурацию: `terraform init` (не работает без vpn, при получении данных отдаётся 403 статус код)
|
||||
4. Просмотреть конфигурацию `terraform plan`
|
||||
5. Применить конфигурацию к облаку `terraform apply -auto-approve`
|
||||
|
||||
**Результат**:
|
||||
|
||||

|
||||
|
||||
### Задача 3
|
||||
|
||||
> Создать ваш первый готовый к боевой эксплуатации компонент мониторинга, состоящий из стека микросервисов.
|
||||
>
|
||||
> Для получения зачета, вам необходимо предоставить:
|
||||
> - Скриншот работающего веб-интерфейса Grafana с текущими метриками
|
||||
|
||||
1. Копирование inventory файла для `ansible` из [inventory.example](./ansible/inventory.example) в `inventory`
|
||||
2. Запуск `ansible`: `cd ansible && ansible-playbook provision.yml`
|
||||
|
||||
**Результат**:
|
||||
|
||||

|
||||
|
||||
### Clean up
|
||||
|
||||
Удаление всей инфраструктуры:
|
||||
|
||||
1. Удаление ВМ, сетей: `terraform destroy -auto-approve`
|
||||
2. Удаление образа ОС: `yc compute image delete --id fd8oponkic4t99ecuk8k`
|
||||
37
src/homework/05-virtualization/5.4/terraform/.gitignore
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
variables.tf
|
||||
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Exclude all .tfvars files, which are likely to contain sentitive data, such as
|
||||
# password, private keys, and other secrets. These should not be part of version
|
||||
# control as they are data points which are potentially sensitive and subject
|
||||
# to change depending on the environment.
|
||||
#
|
||||
*.tfvars
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
#
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
22
src/homework/05-virtualization/5.4/terraform/.terraform.lock.hcl
generated
Normal file
@@ -0,0 +1,22 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/yandex-cloud/yandex" {
|
||||
version = "0.74.0"
|
||||
hashes = [
|
||||
"h1:WE0V59Nb+oj8gN02X7Xt5ZuP+Z+dP3lLaQgChj+8t1g=",
|
||||
"zh:01914a42590934918a312324fcf8b0b342da113da76c13bc00d40b9d3c0a78d9",
|
||||
"zh:0ae93ec70084677f0026c44513c99252dde3be31435c4d1ef5259c8ab5bde225",
|
||||
"zh:59acf5f27d378069d7332549c1645e03de2a2ff9208e02e1546491d276031e23",
|
||||
"zh:6662ab75109138675de0060957ce259c96c141e87617bc211dd80f1213d69419",
|
||||
"zh:86143792b6395e582b2363ac052675e51741bb9b09dcdabc3f5512f501d49fe5",
|
||||
"zh:883a06e44b64764459c1d0b37f24b52134a9fb95d7332f95b2b3c2271b76a958",
|
||||
"zh:96ca7255602e1f38b42515533bac2e77313163638ef6e68c08a7772ab2515ed6",
|
||||
"zh:9bad5d9a023aa238f34db6a05c1ea67f19f2c27fe640be76ec77d850e8cbecf6",
|
||||
"zh:aebf8480d0cccbca57a085ccabb5af23d0e35a8d6e54b1bef15ae6432cfdf229",
|
||||
"zh:c7114896af26237cd01395c10f81a670752cc103d6ce602e88f81f205987e617",
|
||||
"zh:c84819a708453cc321746eba5fc4bab972e3735607b6533b3d9bab79c3f0d196",
|
||||
"zh:ee82069747c38737e88f01007de0a1180770c14de26c13b79c9cc340204237fc",
|
||||
"zh:f53439d40f328b0e4800d8ed00f18bc39b2b03ac3d776b0c7b497722d7f7f0b1",
|
||||
]
|
||||
}
|
||||
11
src/homework/05-virtualization/5.4/terraform/network.tf
Normal file
@@ -0,0 +1,11 @@
|
||||
# Network
|
||||
resource "yandex_vpc_network" "default" {
|
||||
name = "net"
|
||||
}
|
||||
|
||||
resource "yandex_vpc_subnet" "default" {
|
||||
name = "subnet"
|
||||
zone = "ru-central1-a"
|
||||
network_id = "${yandex_vpc_network.default.id}"
|
||||
v4_cidr_blocks = ["192.168.101.0/24"]
|
||||
}
|
||||
29
src/homework/05-virtualization/5.4/terraform/node01.tf
Normal file
@@ -0,0 +1,29 @@
|
||||
resource "yandex_compute_instance" "node01" {
|
||||
name = "node01"
|
||||
zone = "ru-central1-a"
|
||||
hostname = "node01.netology.cloud"
|
||||
allow_stopping_for_update = true
|
||||
|
||||
resources {
|
||||
cores = 8
|
||||
memory = 8
|
||||
}
|
||||
|
||||
boot_disk {
|
||||
initialize_params {
|
||||
image_id = "${var.centos-7-base}"
|
||||
name = "root-node01"
|
||||
type = "network-nvme"
|
||||
size = "50"
|
||||
}
|
||||
}
|
||||
|
||||
network_interface {
|
||||
subnet_id = "${yandex_vpc_subnet.default.id}"
|
||||
nat = true
|
||||
}
|
||||
|
||||
metadata = {
|
||||
ssh-keys = "centos:${file("~/.ssh/id_rsa.pub")}"
|
||||
}
|
||||
}
|
||||
7
src/homework/05-virtualization/5.4/terraform/output.tf
Normal file
@@ -0,0 +1,7 @@
|
||||
output "internal_ip_address_node01_yandex_cloud" {
|
||||
value = "${yandex_compute_instance.node01.network_interface.0.ip_address}"
|
||||
}
|
||||
|
||||
output "external_ip_address_node01_yandex_cloud" {
|
||||
value = "${yandex_compute_instance.node01.network_interface.0.nat_ip_address}"
|
||||
}
|
||||
14
src/homework/05-virtualization/5.4/terraform/provider.tf
Normal file
@@ -0,0 +1,14 @@
|
||||
# Provider
|
||||
terraform {
|
||||
required_providers {
|
||||
yandex = {
|
||||
source = "yandex-cloud/yandex"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "yandex" {
|
||||
token = var.yandex_cloud_token
|
||||
cloud_id = var.yandex_cloud_id
|
||||
folder_id = var.yandex_folder_id
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
# Заменить на ID своего облака
|
||||
# https://console.cloud.yandex.ru/cloud?section=overview
|
||||
variable "yandex_cloud_id" {
|
||||
default = "b1gu1gt5nqi6lqgu3t7s"
|
||||
}
|
||||
|
||||
# Заменить на Folder своего облака
|
||||
# https://console.cloud.yandex.ru/cloud?section=overview
|
||||
variable "yandex_folder_id" {
|
||||
default = "b1gaec42k169jqpo02f7"
|
||||
}
|
||||
|
||||
# OAuth токен, используемый утилитой yc. Применялся на этапе с packer.
|
||||
variable "yandex_cloud_token" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
# Заменить на ID своего образа
|
||||
# ID можно узнать с помощью команды yc compute image list
|
||||
variable "centos-7-base" {
|
||||
default = "fd8ft6norj68lo29qlpi"
|
||||
}
|
||||
1
src/homework/05-virtualization/5.5/ansible/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
inventory
|
||||
@@ -0,0 +1,7 @@
|
||||
FROM prom/alertmanager:v0.15.3
|
||||
|
||||
COPY conf /etc/alertmanager/
|
||||
|
||||
ENTRYPOINT [ "/etc/alertmanager/docker-entrypoint.sh" ]
|
||||
CMD [ "--config.file=/etc/alertmanager/alertmanager.yml", \
|
||||
"--storage.path=/alertmanager" ]
|
||||
@@ -0,0 +1,11 @@
|
||||
route:
|
||||
receiver: 'slack'
|
||||
|
||||
receivers:
|
||||
- name: 'slack'
|
||||
slack_configs:
|
||||
- send_resolved: true
|
||||
text: "{{ .CommonAnnotations.description }}"
|
||||
#username: <user>#
|
||||
#channel: <channel>#
|
||||
#api_url: <url>#
|
||||
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
cat /etc/alertmanager/alertmanager.yml |\
|
||||
sed "s@#api_url: <url>#@api_url: '$SLACK_URL'@g" |\
|
||||
sed "s@#channel: <channel>#@channel: '#$SLACK_CHANNEL'@g" |\
|
||||
sed "s@#username: <user>#@username: '$SLACK_USER'@g" > /tmp/alertmanager.yml
|
||||
|
||||
mv /tmp/alertmanager.yml /etc/alertmanager/alertmanager.yml
|
||||
|
||||
set -- /bin/alertmanager "$@"
|
||||
|
||||
exec "$@"
|
||||
@@ -0,0 +1,40 @@
|
||||
:9090 {
|
||||
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
|
||||
proxy / prometheus:9090 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
:9093 {
|
||||
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
|
||||
proxy / alertmanager:9093 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
:9094 {
|
||||
basicauth / {$ADMIN_USER} {$ADMIN_PASSWORD}
|
||||
proxy / unsee:8080 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
:3000 {
|
||||
proxy / grafana:3000 {
|
||||
transparent
|
||||
websocket
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
version: "3.3"
|
||||
|
||||
networks:
|
||||
net:
|
||||
driver: overlay
|
||||
attachable: true
|
||||
|
||||
volumes:
|
||||
prometheus: {}
|
||||
grafana: {}
|
||||
alertmanager: {}
|
||||
|
||||
configs:
|
||||
caddy_config:
|
||||
file: ./caddy/Caddyfile
|
||||
dockerd_config:
|
||||
file: ./dockerd-exporter/Caddyfile
|
||||
node_rules:
|
||||
file: ./prometheus/rules/swarm_node.rules.yml
|
||||
task_rules:
|
||||
file: ./prometheus/rules/swarm_task.rules.yml
|
||||
|
||||
services:
|
||||
dockerd-exporter:
|
||||
image: stefanprodan/caddy
|
||||
networks:
|
||||
- net
|
||||
environment:
|
||||
- DOCKER_GWBRIDGE_IP=172.18.0.1
|
||||
configs:
|
||||
- source: dockerd_config
|
||||
target: /etc/caddy/Caddyfile
|
||||
deploy:
|
||||
mode: global
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 64M
|
||||
|
||||
cadvisor:
|
||||
image: google/cadvisor
|
||||
networks:
|
||||
- net
|
||||
command: -logtostderr -docker_only
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||
- /:/rootfs:ro
|
||||
- /var/run:/var/run
|
||||
- /sys:/sys:ro
|
||||
- /var/lib/docker/:/var/lib/docker:ro
|
||||
deploy:
|
||||
mode: global
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 64M
|
||||
|
||||
grafana:
|
||||
image: stefanprodan/swarmprom-grafana:5.3.4
|
||||
networks:
|
||||
- net
|
||||
environment:
|
||||
- GF_SECURITY_ADMIN_USER=${ADMIN_USER:-admin}
|
||||
- GF_SECURITY_ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
|
||||
- GF_USERS_ALLOW_SIGN_UP=false
|
||||
#- GF_SERVER_ROOT_URL=${GF_SERVER_ROOT_URL:-localhost}
|
||||
#- GF_SMTP_ENABLED=${GF_SMTP_ENABLED:-false}
|
||||
#- GF_SMTP_FROM_ADDRESS=${GF_SMTP_FROM_ADDRESS:-grafana@test.com}
|
||||
#- GF_SMTP_FROM_NAME=${GF_SMTP_FROM_NAME:-Grafana}
|
||||
#- GF_SMTP_HOST=${GF_SMTP_HOST:-smtp:25}
|
||||
#- GF_SMTP_USER=${GF_SMTP_USER}
|
||||
#- GF_SMTP_PASSWORD=${GF_SMTP_PASSWORD}
|
||||
volumes:
|
||||
- grafana:/var/lib/grafana
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints:
|
||||
- node.role == manager
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 64M
|
||||
|
||||
alertmanager:
|
||||
image: stefanprodan/swarmprom-alertmanager:v0.14.0
|
||||
networks:
|
||||
- net
|
||||
environment:
|
||||
- SLACK_URL=${SLACK_URL:-https://hooks.slack.com/services/TOKEN}
|
||||
- SLACK_CHANNEL=${SLACK_CHANNEL:-general}
|
||||
- SLACK_USER=${SLACK_USER:-alertmanager}
|
||||
command:
|
||||
- '--config.file=/etc/alertmanager/alertmanager.yml'
|
||||
- '--storage.path=/alertmanager'
|
||||
volumes:
|
||||
- alertmanager:/alertmanager
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints:
|
||||
- node.role == manager
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 64M
|
||||
|
||||
unsee:
|
||||
image: cloudflare/unsee:v0.8.0
|
||||
networks:
|
||||
- net
|
||||
environment:
|
||||
- "ALERTMANAGER_URIS=default:http://alertmanager:9093"
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
|
||||
node-exporter:
|
||||
image: stefanprodan/swarmprom-node-exporter:v0.16.0
|
||||
networks:
|
||||
- net
|
||||
environment:
|
||||
- NODE_ID={{.Node.ID}}
|
||||
volumes:
|
||||
- /proc:/host/proc:ro
|
||||
- /sys:/host/sys:ro
|
||||
- /:/rootfs:ro
|
||||
- /etc/hostname:/etc/nodename
|
||||
command:
|
||||
- '--path.sysfs=/host/sys'
|
||||
- '--path.procfs=/host/proc'
|
||||
- '--collector.textfile.directory=/etc/node-exporter/'
|
||||
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($$|/)'
|
||||
- '--no-collector.ipvs'
|
||||
deploy:
|
||||
mode: global
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 64M
|
||||
|
||||
prometheus:
|
||||
image: stefanprodan/swarmprom-prometheus:v2.5.0
|
||||
networks:
|
||||
- net
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--storage.tsdb.path=/prometheus'
|
||||
- '--storage.tsdb.retention=${PROMETHEUS_RETENTION:-24h}'
|
||||
volumes:
|
||||
- prometheus:/prometheus
|
||||
configs:
|
||||
- source: node_rules
|
||||
target: /etc/prometheus/swarm_node.rules.yml
|
||||
- source: task_rules
|
||||
target: /etc/prometheus/swarm_task.rules.yml
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints:
|
||||
- node.role == manager
|
||||
resources:
|
||||
limits:
|
||||
memory: 2048M
|
||||
reservations:
|
||||
memory: 128M
|
||||
|
||||
caddy:
|
||||
image: stefanprodan/caddy
|
||||
ports:
|
||||
- "3000:3000"
|
||||
- "9090:9090"
|
||||
- "9093:9093"
|
||||
- "9094:9094"
|
||||
networks:
|
||||
- net
|
||||
environment:
|
||||
- ADMIN_USER=${ADMIN_USER:-admin}
|
||||
- ADMIN_PASSWORD=${ADMIN_PASSWORD:-admin}
|
||||
configs:
|
||||
- source: caddy_config
|
||||
target: /etc/caddy/Caddyfile
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
placement:
|
||||
constraints:
|
||||
- node.role == manager
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
reservations:
|
||||
memory: 64M
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:3000"]
|
||||
interval: 5s
|
||||
timeout: 1s
|
||||
retries: 5
|
||||
@@ -0,0 +1,8 @@
|
||||
:9323 {
|
||||
proxy / {$DOCKER_GWBRIDGE_IP}:9323 {
|
||||
transparent
|
||||
}
|
||||
|
||||
errors stderr
|
||||
tls off
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
FROM grafana/grafana:5.3.4
|
||||
# https://hub.docker.com/r/grafana/grafana/tags/
|
||||
|
||||
COPY datasources /etc/grafana/provisioning/datasources/
|
||||
COPY swarmprom_dashboards.yml /etc/grafana/provisioning/dashboards/
|
||||
COPY dashboards /etc/grafana/dashboards/
|
||||
|
||||
ENV GF_SECURITY_ADMIN_PASSWORD=admin \
|
||||
GF_SECURITY_ADMIN_USER=admin \
|
||||
GF_PATHS_PROVISIONING=/etc/grafana/provisioning/
|
||||