├── .ansible-lint ├── .github ├── dependabot.yml └── workflows │ └── AUTO_PR.yml ├── .gitignore ├── .markdownlint.yml ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── ansible ├── ansible.cfg ├── docker.yaml ├── group_vars │ └── all.yml ├── install_minio.yml ├── install_nginx.yml ├── inventory.yml ├── kubeadm.yml ├── patroni-postgresql.yml └── roles │ ├── create_filesystem │ └── tasks │ │ └── main.yml │ ├── docker_cleaner │ └── tasks │ │ ├── docker.yml │ │ └── main.yml │ ├── docker_install │ ├── files │ │ └── daemon.json │ ├── handlers │ │ └── main.yml │ └── tasks │ │ ├── main.yml │ │ └── ubuntu.yml │ ├── etcd │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── main.yml │ │ └── service.yml │ ├── templates │ │ └── etcd.service.j2 │ └── vars │ │ └── main.yml │ ├── haproxy │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── config.yml │ │ ├── install.yml │ │ └── main.yml │ ├── templates │ │ ├── haproxy.cfg.j2 │ │ └── haproxy.service.j2 │ └── vars │ │ └── main.yml │ ├── haproxy_static_pods │ ├── files │ │ ├── check_apiserver.sh │ │ ├── haproxy.cfg │ │ ├── haproxy.yaml │ │ └── keepalived.yaml │ ├── tasks │ │ └── main.yml │ └── templates │ │ └── keepalived.conf.j2 │ ├── keepalived │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── config.yml │ │ ├── install.yml │ │ ├── main.yml │ │ └── start.yml │ └── templates │ │ ├── keepalived.backup.conf.j2 │ │ └── keepalived.master.conf.j2 │ ├── minio_start │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── add_dirs.yml │ │ ├── add_user.yml │ │ ├── main.yml │ │ └── start_minio.yml │ ├── templates │ │ ├── minio.config.j2 │ │ └── minio.service.j2 │ └── vars │ │ └── main.yml │ ├── mount │ └── tasks │ │ └── main.yml │ ├── nginx_install │ ├── files │ │ └── conf.d │ │ │ └── minio.conf │ ├── handlers │ │ └── main.yml │ ├── tasks │ │ ├── cert.yml │ │ ├── config.yml │ │ ├── main.yml │ │ ├── nginx.yml │ │ └── user.yml │ ├── templates │ │ └── docker-compose.yml.j2 │ └── vars │ │ └── main.yml │ ├── ntp_install │ ├── README.md │ ├── tasks │ │ ├── debian.yml │ │ ├── main.yml │ │ └── ubuntu.yml │ └── templates │ │ ├── debian.conf.j2 │ │ └── ubuntu.conf.j2 │ └── patroni │ ├── handlers │ └── main.yml │ ├── tasks │ ├── config.yml │ ├── main.yml │ └── service.yml │ ├── templates │ ├── config.yml.j2 │ └── patroni.service.j2 │ └── vars │ └── main.yml ├── docker ├── README.md ├── blue-green-deployment │ ├── Dockerfile │ ├── README.md │ ├── deploy.sh │ ├── docker-compose.yaml │ ├── main.go │ └── nginx.tmpl └── uv │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Dockerfile.pip │ ├── Makefile │ ├── README.md │ ├── main.py │ ├── pyproject.toml │ └── requirements.txt ├── kubeadm ├── README.md ├── cilium │ ├── ippool.yaml │ ├── l2-announcement-policy.yaml │ └── values.yaml ├── kube-prometheus-stack │ └── values.yaml ├── test-app │ ├── deployment.yaml │ ├── ingress.yaml │ ├── namespace.yaml │ └── service.yaml └── traefik │ └── values.yaml ├── opentofu ├── README.md ├── kubeadm │ ├── .terraform.lock.hcl │ ├── control-plane.tf │ ├── node.tf │ ├── provider.tf │ ├── terraform.tfvars.example │ └── variables.tf ├── minio │ ├── .terraform.lock.hcl │ ├── minio.tf │ ├── provider.tf │ ├── terraform.tfvars.example │ └── variables.tf ├── nginx │ ├── .terraform.lock.hcl │ ├── nginx.tf │ ├── provider.tf │ ├── terraform.tfvars.example │ └── variables.tf ├── patroni-postgresql │ ├── .terraform.lock.hcl │ ├── etcd.tf │ ├── patroni-postgresql.tf │ ├── provider.tf │ ├── terraform.tfvars.example │ └── variables.tf └── test_vm │ ├── .terraform.lock.hcl │ ├── output.tf │ ├── provider.tf │ ├── terraform.tfvars.example │ ├── variables.tf │ └── vm.tf ├── requirements.txt └── talos ├── README.md ├── cilium ├── ippool.yaml ├── l2-announcement-policy.yaml └── values.yaml ├── cp0.patch ├── cp1.patch ├── cp2.patch ├── patch.yaml ├── traefik └── values.yaml ├── worker0.patch ├── worker1.patch └── worker2.patch /.ansible-lint: -------------------------------------------------------------------------------- 1 | skip_list: 2 | - risky-file-permissions 3 | - var-naming[no-role-prefix] 4 | - yaml[indentation] 5 | - run-once[task] 6 | - no-changed-when 7 | - partial-become[task] 8 | 9 | exclude_paths: 10 | - ansible/roles/haproxy_static_pods/files/haproxy.yaml 11 | - ansible/roles/haproxy_static_pods/files/keepalived.yaml 12 | - talos/ 13 | - .markdownlint.yml 14 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" # Location of package manifests 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/AUTO_PR.yml: -------------------------------------------------------------------------------- 1 | name: AURO PR 2 | 3 | on: 4 | push: 5 | branches: 6 | - "*" 7 | - "!main" 8 | 9 | jobs: 10 | pull-request: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | pull-requests: write 14 | steps: 15 | - uses: actions/checkout@v4.2.2 16 | - name: Auto Pull Request 17 | uses: diillson/auto-pull-request@v1.0.1 18 | with: 19 | destination_branch: main 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/terraform 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=terraform 3 | 4 | ### Terraform ### 5 | # Local .terraform directories 6 | **/.terraform/* 7 | 8 | # .tfstate files 9 | *.tfstate 10 | *.tfstate.* 11 | 12 | # Crash log files 13 | crash.log 14 | crash.*.log 15 | 16 | # Exclude all .tfvars files, which are likely to contain sensitive data, such as 17 | # password, private keys, and other secrets. These should not be part of version 18 | # control as they are data points which are potentially sensitive and subject 19 | # to change depending on the environment. 20 | *.tfvars 21 | *.tfvars.json 22 | 23 | # Ignore override files as they are usually used to override resources locally and so 24 | # are not checked in 25 | override.tf 26 | override.tf.json 27 | *_override.tf 28 | *_override.tf.json 29 | 30 | # Include override files you do wish to add to version control using negated pattern 31 | # !example_override.tf 32 | 33 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 34 | # example: *tfplan* 35 | 36 | # Ignore CLI configuration files 37 | .terraformrc 38 | terraform.rc 39 | 40 | # End of https://www.toptal.com/developers/gitignore/api/terraform 41 | 42 | # Created by https://www.toptal.com/developers/gitignore/api/ansible 43 | # Edit at https://www.toptal.com/developers/gitignore?templates=ansible 44 | 45 | ### Ansible ### 46 | *.retry 47 | 48 | # End of https://www.toptal.com/developers/gitignore/api/ansible 49 | 50 | # Created by https://www.toptal.com/developers/gitignore/api/go 51 | # Edit at https://www.toptal.com/developers/gitignore?templates=go 52 | 53 | ### Go ### 54 | # If you prefer the allow list template instead of the deny list, see community template: 55 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 56 | # 57 | # Binaries for programs and plugins 58 | *.exe 59 | *.exe~ 60 | *.dll 61 | *.so 62 | *.dylib 63 | 64 | # Test binary, built with `go test -c` 65 | *.test 66 | 67 | # Output of the go coverage tool, specifically when used with LiteIDE 68 | *.out 69 | 70 | # Dependency directories (remove the comment below to include it) 71 | # vendor/ 72 | 73 | # Go workspace file 74 | go.work 75 | 76 | # End of https://www.toptal.com/developers/gitignore/api/go 77 | 78 | # Other 79 | .vscode 80 | .DS_Store 81 | ansible/secrets 82 | talos/controlplane.yaml 83 | talos/worker.yaml 84 | talos/secrets.yaml 85 | talos/talosconfig 86 | talos/cp*.yaml 87 | talos/worker*.yaml 88 | -------------------------------------------------------------------------------- /.markdownlint.yml: -------------------------------------------------------------------------------- 1 | MD013: 2 | line_length: 150 3 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-added-large-files 6 | - id: check-case-conflict 7 | - id: check-merge-conflict 8 | - id: check-shebang-scripts-are-executable 9 | - id: check-vcs-permalinks 10 | - id: check-yaml 11 | files: .*\.(yaml|yml)$ 12 | - id: destroyed-symlinks 13 | - id: detect-private-key 14 | - id: end-of-file-fixer 15 | - id: fix-byte-order-marker 16 | - id: mixed-line-ending 17 | - id: no-commit-to-branch 18 | - id: requirements-txt-fixer 19 | - id: trailing-whitespace 20 | 21 | - repo: https://github.com/ansible-community/ansible-lint.git 22 | rev: v24.12.2 23 | hooks: 24 | - id: ansible-lint 25 | exclude: ^talos/ 26 | 27 | - repo: https://github.com/tofuutils/pre-commit-opentofu 28 | rev: v2.1.0 29 | hooks: 30 | - id: tofu_fmt 31 | - id: tofu_validate 32 | 33 | - repo: https://github.com/igorshubovych/markdownlint-cli 34 | rev: v0.43.0 35 | hooks: 36 | - id: markdownlint 37 | args: [--fix] 38 | files: \.md$ 39 | 40 | - repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks 41 | rev: v2.14.0 42 | hooks: 43 | - id: pretty-format-yaml 44 | args: [--autofix, --indent, '2', --offset, '2'] 45 | - id: pretty-format-toml 46 | args: [--autofix] 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Fedor Batonogov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # learn-devops 2 | 3 | ![GitHub stars](https://img.shields.io/github/stars/batonogov/learn-devops?style=flat-square) 4 | ![GitHub forks](https://img.shields.io/github/forks/batonogov/learn-devops?style=flat-square) 5 | ![GitHub issues](https://img.shields.io/github/issues/batonogov/learn-devops?style=flat-square) 6 | ![GitHub license](https://img.shields.io/github/license/batonogov/learn-devops?style=flat-square) 7 | 8 | ## Описание 9 | 10 | В этом репозитории собраны примеры с [YouTube](https://www.youtube.com/@fedor_batonogov)/[Rutube](https://rutube.ru/channel/31656928) каналов. 11 | Тут я рассказываю о разных инструментах необходимых для **DevOps специалиста** и делюсь опытом. 12 | 13 | ## Мы стремимся подходу **Инфраструктура как код** 14 | 15 | Основная идея **Infrastructure as Code (IaC)** в том, чтобы **описать инфраструктуру кодом** и сделать её доступной для понимания. 16 | IaC работает со всеми компонентами инфраструктуры так, будто это просто данные. 17 | Такое стало возможно благодаря умению платформ виртуализации и облачных провайдеров разделять инфраструктуру и оборудование, 18 | а для управления серверами, хранилищами и сетевыми устройствами предоставлять специальное API. 19 | 20 | ## Структура проекта 21 | 22 | 1. [Docker](./docker/) - это программная платформа для быстрой сборки, отладки и развертывания приложений с помощью **контейнеров**. 23 | 24 | 2. [kubeadm](./kubeadm/) - это инструмент для простого и быстрого развёртывания кластера Kubernetes. 25 | 26 | 3. [OpenTofu](./opentofu/) — программное обеспечение с **открытым исходным кодом**, 27 | используемое для **управления внешними ресурсами** (например, в рамках модели **инфраструктура как код**). Проект Linux Foundation. 28 | Пользователи определяют и предоставляют инфраструктуру центра обработки данных с помощью **декларативного языка конфигурации**, 29 | известного как HashiCorp Configuration Language (HCL) или JSON. 30 | 31 | 4. [Ansible](./ansible/) — система управления конфигурациями, написанная на языке программирования **Python**, 32 | с использованием **декларативного языка разметки** для **описания конфигураций**. 33 | Применяется для **автоматизации настройки и развёртывания программного обеспечения**. 34 | 35 | 5. [Talos](./talos/) — **Talos Linux** — это **Linux**, разработанный для **Kubernetes**: безопасный, неизменяемый и минимальный. 36 | 37 | ## Pre-commit hook 38 | 39 | **Pre-commit hook** — это скрипт, который выполняется перед тем, как изменения будут зафиксированы в системе контроля версий, например, в Git. 40 | Он позволяет автоматически проверять код и выполнять определенные действия, такие как запуск тестов, проверка стиля кода, 41 | статический анализ и другие проверки, чтобы предотвратить попадание некорректного или несоответствующего стандартам кода в репозиторий. 42 | 43 | Преимущества использования **pre-commit hook**-ов: 44 | 45 | - **Автоматизация проверки кода**: Хуки позволяют автоматизировать проверку качества кода, что помогает поддерживать стандарты кода в команде. 46 | - **Раннее выявление ошибок**: Хуки помогают обнаруживать ошибки и недочеты до того, как они попадут в репозиторий, 47 | что снижает количество багов и повышает качество кода. 48 | - **Повышение производительности**: Автоматические проверки экономят время разработчиков, которое они могли бы потратить на ручные проверки. 49 | 50 | Использование pre-commit hook-ов является хорошей практикой, которая помогает поддерживать качество и стабильность кода в проекте. 51 | 52 | - Установка 53 | 54 | Прежде чем запускать **хуки**, необходимо установить менеджер пакетов **pre-commit**. 55 | 56 | С помощью **pip**: 57 | 58 | ```console 59 | pip install pre-commit 60 | ``` 61 | 62 | В python-проекте добавьте в файл **requirements.txt** (или **requirements-dev.txt**) следующее: 63 | 64 | ```text 65 | pre-commit 66 | ``` 67 | 68 | С помощью **homebrew**: 69 | 70 | ```console 71 | brew install pre-commit 72 | ``` 73 | 74 | С помощью **conda** (через **conda-forge**): 75 | 76 | ```console 77 | conda install -c conda-forge pre-commit 78 | ``` 79 | 80 | - Быстрый запуск 81 | 82 | Следуйте инструкциям по установке, приведенным выше. 83 | 84 | pre-commit --version должен показать, какую версию вы используете. 85 | 86 | ```console 87 | pre-commit --version 88 | pre-commit 4.0.1 89 | ``` 90 | 91 | Устанавливаем **pre-commit** 92 | 93 | ```console 94 | pre-commit install 95 | ``` 96 | 97 | ### Обновление версии хуков 98 | 99 | Обновить все версии хуков до последней версии можно при помощи команды 100 | 101 | ```sh 102 | pre-commit autoupdate 103 | ``` 104 | -------------------------------------------------------------------------------- /ansible/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | timeout=60 3 | roles_path=./roles 4 | inventory=./inventory.yml 5 | forks=100 6 | serial=10 7 | host_key_checking=False 8 | callbacks_enabled=ansible.posix.profile_tasks 9 | -------------------------------------------------------------------------------- /ansible/docker.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Развертывание docker 3 | become: true 4 | hosts: 5 | - test_hosts 6 | roles: 7 | - docker_install 8 | -------------------------------------------------------------------------------- /ansible/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | patroni_user: patroni 2 | patroni_uid: 1666 3 | -------------------------------------------------------------------------------- /ansible/install_minio.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Подготовка узлов 3 | become: true 4 | hosts: 5 | - minio_hosts 6 | roles: 7 | - docker_install 8 | 9 | - name: Развертывание MinIO Multi-Node Multi-Drive 10 | become: true 11 | hosts: 12 | - minio_hosts 13 | vars: 14 | filesystem: xfs 15 | device_name: 16 | - /dev/vdb 17 | device: 18 | - { src: /dev/vdb, path: /mnt/disk1 } 19 | minio_username: minio 20 | roles: 21 | - create_filesystem 22 | - mount 23 | - minio_start 24 | - docker_cleaner 25 | -------------------------------------------------------------------------------- /ansible/install_nginx.yml: -------------------------------------------------------------------------------- 1 | - name: Подготавливаю узлы 2 | become: true 3 | hosts: 4 | - nginx_hosts 5 | roles: 6 | - docker_install 7 | - nginx_install 8 | 9 | - name: Настраиваю keepalived 10 | become: true 11 | hosts: 12 | - nginx-01 13 | roles: 14 | - role: keepalived 15 | unit_file: "keepalived.master.conf.j2" 16 | virtual_ip: "10.0.75.90/24" 17 | virtual_router_id: 10 18 | 19 | - name: Настраиваю keepalived 20 | become: true 21 | hosts: 22 | - nginx-02 23 | roles: 24 | - role: keepalived 25 | unit_file: "keepalived.backup.conf.j2" 26 | virtual_ip: "10.0.75.90/24" 27 | virtual_router_id: 10 28 | -------------------------------------------------------------------------------- /ansible/inventory.yml: -------------------------------------------------------------------------------- 1 | kubeadm: 2 | children: 3 | kubeadm_control_plane: 4 | hosts: 5 | kubeadm-cp-01: 6 | ansible_host: 10.0.75.81 7 | kubeadm-cp-02: 8 | ansible_host: 10.0.75.82 9 | kubeadm-cp-03: 10 | ansible_host: 10.0.75.83 11 | kubeadm_nodes: 12 | hosts: 13 | kubeadm-node-01: 14 | ansible_host: 10.0.75.84 15 | kubeadm-node-02: 16 | ansible_host: 10.0.75.85 17 | kubeadm-node-03: 18 | ansible_host: 10.0.75.86 19 | vars: 20 | ansible_user: infra 21 | ansible_port: 22 22 | 23 | patroni_postgresql_cluster: 24 | children: 25 | patroni_postgresql_hosts: 26 | hosts: 27 | patroni-postgresql-01: 28 | ansible_host: 10.0.75.111 29 | patroni-postgresql-02: 30 | ansible_host: 10.0.75.112 31 | patroni-postgresql-03: 32 | ansible_host: 10.0.75.113 33 | haproxy_hosts: 34 | hosts: 35 | haproxy-01: 36 | ansible_host: 10.0.75.114 37 | haproxy-02: 38 | ansible_host: 10.0.75.115 39 | vars: 40 | ansible_user: infra 41 | ansible_port: 22 42 | 43 | minio_hosts: 44 | hosts: 45 | minio1: 46 | ansible_host: 10.0.75.55 47 | minio2: 48 | ansible_host: 10.0.75.56 49 | minio3: 50 | ansible_host: 10.0.75.57 51 | minio4: 52 | ansible_host: 10.0.75.58 53 | vars: 54 | ansible_user: infra 55 | ansible_port: 22 56 | 57 | nginx_hosts: 58 | hosts: 59 | nginx-01: 60 | ansible_host: 10.0.75.91 61 | nginx-02: 62 | ansible_host: 10.0.75.92 63 | vars: 64 | ansible_user: infra 65 | ansible_port: 22 66 | -------------------------------------------------------------------------------- /ansible/kubeadm.yml: -------------------------------------------------------------------------------- 1 | # Подготовка к запуску Kubernetes кластера 2 | - name: Подготоваливаю узлы для kubernetes кластера 3 | become: true 4 | hosts: 5 | - kubeadm 6 | tasks: 7 | - name: Добавляю модули br_netfilter и overlay 8 | community.general.modprobe: 9 | name: "{{ item }}" 10 | state: present 11 | with_items: 12 | - br_netfilter 13 | - overlay 14 | 15 | - name: Добавляю модули br_netfilter и overlay в /etc/modules 16 | ansible.builtin.lineinfile: 17 | path: /etc/modules 18 | line: "{{ item }}" 19 | create: true 20 | with_items: 21 | - br_netfilter 22 | - overlay 23 | 24 | - name: Включаю маршрутизацию IP и iptables для моста 25 | ansible.posix.sysctl: 26 | name: "{{ item }}" 27 | value: 1 28 | state: present 29 | with_items: 30 | - net.ipv4.ip_forward 31 | - net.bridge.bridge-nf-call-iptables 32 | 33 | - name: Устанавливаю пакеты 34 | ansible.builtin.apt: 35 | name: 36 | - apt-transport-https 37 | - ca-certificates 38 | - curl 39 | - gpg 40 | - software-properties-common 41 | update_cache: true 42 | register: apt_res 43 | retries: 5 44 | until: apt_res is success 45 | 46 | - name: Добавляю gpg ключ для репозиториев Kubernetes и cri-o 47 | ansible.builtin.apt_key: 48 | url: '{{ item["url"] }}' 49 | state: present 50 | keyring: '{{ item["keyring"] }}' 51 | with_items: 52 | - { 53 | url: "https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key", 54 | keyring: "/etc/apt/keyrings/kubernetes-apt-keyring.gpg", 55 | } 56 | - { 57 | url: "https://pkgs.k8s.io/addons:/cri-o:/prerelease:/main/deb/Release.key", 58 | keyring: "/etc/apt/keyrings/cri-o-apt-keyring.gpg", 59 | } 60 | 61 | - name: Добавляю репозитории Kubernetes и cri-o 62 | ansible.builtin.apt_repository: 63 | repo: "{{ item }}" 64 | state: present 65 | with_items: 66 | - deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://pkgs.k8s.io/addons:/cri-o:/stable:/v1.30/deb/ / 67 | - deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.30/deb/ / 68 | 69 | - name: Устанавливаю пакеты kubelet, kubeadm, kubectl и cri-o 70 | ansible.builtin.apt: 71 | name: 72 | - kubelet 73 | - kubeadm 74 | - kubectl 75 | - cri-o 76 | state: present 77 | update_cache: true 78 | 79 | - name: Предотвращаю обновление kubelet, kubeadm и kubectl 80 | ansible.builtin.dpkg_selections: 81 | name: "{{ item }}" 82 | selection: hold 83 | with_items: 84 | - kubelet 85 | - kubeadm 86 | - kubectl 87 | 88 | - name: Включаю и запускаю службы kubelet и cri-o 89 | ansible.builtin.systemd: 90 | name: "{{ item }}" 91 | enabled: true 92 | state: started 93 | with_items: 94 | - kubelet 95 | - crio 96 | 97 | # Запуск сервисов keepalived и haproxy как статических подсистем 98 | # https://github.com/kubernetes/kubeadm/blob/main/docs/ha-considerations.md#option-2-run-the-services-as-static-pods 99 | - name: Настраиваю keepalived + haproxy и инициализирую кластер 100 | become: true 101 | hosts: 102 | - kubeadm_control_plane 103 | roles: 104 | - haproxy_static_pods 105 | tasks: 106 | - name: Инициализирую высокодоступный кластер 107 | run_once: true 108 | ansible.builtin.command: | 109 | kubeadm init \ 110 | --pod-network-cidr=10.244.0.0/16 \ 111 | --control-plane-endpoint=10.0.75.80:8888 \ 112 | --upload-certs \ 113 | --skip-phases=addon/kube-proxy 114 | args: 115 | creates: /etc/kubernetes/kubelet.conf 116 | notify: 117 | - Создаю token для control-plane 118 | - Создаю token для node 119 | - Добавляю control-plane узлы в кластер 120 | - Добавляю node узлы в кластер 121 | handlers: 122 | - name: Создаю token для control-plane 123 | ansible.builtin.shell: 124 | cmd: | 125 | set -o pipefail 126 | echo $(kubeadm token create --print-join-command) \ 127 | --control-plane \ 128 | --certificate-key \ 129 | $(kubeadm init phase upload-certs --upload-certs | grep -vw -e certificate -e Namespace) 130 | executable: /bin/bash 131 | register: join_control_plane_raw 132 | - name: Создаю token для node 133 | ansible.builtin.command: kubeadm token create --print-join-command 134 | register: join_node_raw 135 | - name: Добавляю control-plane узлы в кластер 136 | ansible.builtin.command: "{{ join_control_plane_raw.stdout }}" 137 | args: 138 | creates: /etc/kubernetes/kubelet.conf 139 | delegate_to: "{{ item }}" 140 | loop: "{{ groups['kubeadm_control_plane'] }}" 141 | - name: Добавляю node узлы в кластер 142 | ansible.builtin.command: "{{ join_node_raw.stdout }}" 143 | args: 144 | creates: /etc/kubernetes/kubelet.conf 145 | delegate_to: "{{ item }}" 146 | loop: "{{ groups['kubeadm_nodes'] }}" 147 | 148 | # Подготовка control-plane узлов 149 | - name: Подготовка control-plane узлов для работы с kubectl 150 | become: true 151 | gather_facts: false 152 | hosts: 153 | - kubeadm_control_plane 154 | tasks: 155 | - name: Создаю директорию .kube 156 | become_user: infra 157 | ansible.builtin.file: 158 | path: $HOME/.kube 159 | state: directory 160 | mode: "755" 161 | - name: Копирую admin.conf в директорию .kube 162 | ansible.builtin.copy: 163 | src: /etc/kubernetes/admin.conf 164 | dest: /home/infra/.kube/config 165 | remote_src: true 166 | owner: infra 167 | group: infra 168 | mode: "600" 169 | - name: Копирую kube/config 170 | run_once: true 171 | ansible.posix.synchronize: 172 | src: "~/.kube/config" # remote host 173 | dest: "~/.kube/config" # localhost 174 | mode: pull 175 | -------------------------------------------------------------------------------- /ansible/patroni-postgresql.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Подготовка узлов 3 | become: true 4 | hosts: 5 | - patroni_postgresql_hosts 6 | roles: 7 | - ntp_install 8 | - docker_install 9 | tasks: 10 | - name: Создаю пользователя 11 | ansible.builtin.user: 12 | name: "{{ patroni_user }}" 13 | uid: "{{ patroni_uid }}" 14 | shell: /usr/sbin/nologin 15 | groups: 16 | - docker 17 | 18 | - name: Подготовка кластера 19 | become: true 20 | hosts: 21 | - patroni_postgresql_hosts 22 | roles: 23 | - etcd 24 | - patroni 25 | 26 | - name: Подготовка кластера 27 | become: true 28 | hosts: 29 | - haproxy_hosts 30 | roles: 31 | - ntp_install 32 | - docker_install 33 | - haproxy 34 | 35 | - name: Настройка master keepalived 36 | become: true 37 | hosts: 38 | - haproxy-01 39 | roles: 40 | - role: keepalived 41 | unit_file: "keepalived.master.conf.j2" 42 | virtual_ip: "10.0.75.110/24" 43 | virtual_router_id: 250 44 | 45 | - name: Настройка backup keepalived 46 | become: true 47 | hosts: 48 | - haproxy-02 49 | roles: 50 | - role: keepalived 51 | unit_file: "keepalived.backup.conf.j2" 52 | virtual_ip: "10.0.75.110/24" 53 | virtual_router_id: 250 54 | -------------------------------------------------------------------------------- /ansible/roles/create_filesystem/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for create_filesystem 3 | - name: Create a filesystem 4 | community.general.filesystem: 5 | fstype: "{{ filesystem }}" 6 | dev: '{{ item["src"] }}' 7 | loop: "{{ device }}" 8 | -------------------------------------------------------------------------------- /ansible/roles/docker_cleaner/tasks/docker.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Навожу порядок 3 | community.docker.docker_prune: 4 | containers: false 5 | images: true 6 | images_filters: 7 | dangling: false 8 | networks: false 9 | volumes: false 10 | builder_cache: false 11 | -------------------------------------------------------------------------------- /ansible/roles/docker_cleaner/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for docker_cleaner 3 | - name: Чищу докер 4 | ansible.builtin.import_tasks: docker.yml 5 | -------------------------------------------------------------------------------- /ansible/roles/docker_install/files/daemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "registry-mirrors": [ 3 | "https://dockerhub.timeweb.cloud", 4 | "https://mirror.gcr.io", 5 | "https://public.ecr.aws" 6 | ], 7 | "log-driver": "json-file", 8 | "log-opts": { 9 | "max-size": "1g" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ansible/roles/docker_install/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Перезапускаю docker.service 3 | ansible.builtin.systemd: 4 | name: docker.service 5 | state: restarted 6 | enabled: true 7 | daemon_reload: true 8 | -------------------------------------------------------------------------------- /ansible/roles/docker_install/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for docker-install 3 | - name: Install Docker on Ubuntu 4 | ansible.builtin.import_tasks: ubuntu.yml 5 | when: ansible_distribution == 'Ubuntu' 6 | -------------------------------------------------------------------------------- /ansible/roles/docker_install/tasks/ubuntu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Добавляю ключ Docker GPG 3 | ansible.builtin.apt_key: 4 | url: https://download.docker.com/linux/ubuntu/gpg 5 | state: present 6 | keyring: /etc/apt/trusted.gpg.d/docker.gpg 7 | 8 | - name: Добавляю репозиторий Docker 9 | ansible.builtin.apt_repository: 10 | repo: deb https://download.docker.com/linux/ubuntu {{ ansible_distribution_release }} stable 11 | state: present 12 | filename: docker 13 | 14 | - name: Устанавливаю нужные пакеты 15 | ansible.builtin.apt: 16 | name: 17 | - docker-ce 18 | state: present 19 | update_cache: true 20 | 21 | - name: Наливаю daemon.json 22 | ansible.builtin.copy: 23 | src: daemon.json 24 | dest: /etc/docker/daemon.json 25 | mode: '644' 26 | notify: 27 | - Перезапускаю docker.service 28 | -------------------------------------------------------------------------------- /ansible/roles/etcd/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Перезапускаю etcd.service 3 | ansible.builtin.systemd: 4 | name: etcd.service 5 | state: restarted 6 | enabled: true 7 | daemon_reload: true 8 | -------------------------------------------------------------------------------- /ansible/roles/etcd/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Наливаю юнит файл 3 | ansible.builtin.import_tasks: service.yml 4 | -------------------------------------------------------------------------------- /ansible/roles/etcd/tasks/service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Задаю права для директории 3 | ansible.builtin.file: 4 | path: /var/lib/etcd 5 | state: directory 6 | recurse: true 7 | owner: "{{ patroni_uid }}" 8 | group: "{{ patroni_uid }}" 9 | 10 | - name: Наливаю юнит файл etcd 11 | ansible.builtin.template: 12 | src: etcd.service.j2 13 | dest: /etc/systemd/system/etcd.service 14 | notify: 15 | - Перезапускаю etcd.service 16 | 17 | - name: Настраиваю etcd.service 18 | ansible.builtin.systemd: 19 | name: etcd.service 20 | state: started 21 | enabled: true 22 | -------------------------------------------------------------------------------- /ansible/roles/etcd/templates/etcd.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=etcd 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | User={{ patroni_user }} 8 | Restart=always 9 | Environment="TOKEN={{ lookup('password', 'secrets/patroni-postgresql/etcd_cluster_token length=64') }}" 10 | Environment="CLUSTER=patroni-postgresql-01=http://10.0.75.111:2380,patroni-postgresql-02=http://10.0.75.112:2380,patroni-postgresql-03=http://10.0.75.113:2380" 11 | ExecStartPre=-/usr/bin/docker rm -f etcd 12 | ExecStart=/usr/bin/docker run \ 13 | --rm \ 14 | --user {{ patroni_uid }}:{{ patroni_uid }} \ 15 | --publish 2379:2379 \ 16 | --publish 2380:2380 \ 17 | --name etcd \ 18 | --volume=/var/lib/etcd:/etcd-data \ 19 | quay.io/coreos/etcd:{{ etcd_version }} \ 20 | /usr/local/bin/etcd \ 21 | --data-dir=/etcd-data \ 22 | --name {{ inventory_hostname }} \ 23 | --initial-advertise-peer-urls http://{{ ansible_host }}:2380 \ 24 | --listen-peer-urls http://0.0.0.0:2380 \ 25 | --advertise-client-urls http://{{ ansible_host }}:2379 \ 26 | --listen-client-urls http://0.0.0.0:2379 \ 27 | --initial-cluster ${CLUSTER} \ 28 | --initial-cluster-state new \ 29 | --initial-cluster-token ${TOKEN} \ 30 | --enable-v2=true 31 | ExecStop=/usr/bin/docker stop -t 10 etcd 32 | 33 | [Install] 34 | WantedBy=multi-user.target 35 | -------------------------------------------------------------------------------- /ansible/roles/etcd/vars/main.yml: -------------------------------------------------------------------------------- 1 | etcd_version: v3.5.14 2 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for haproxy 3 | - name: Перезапускаю haproxy.service 4 | ansible.builtin.systemd: 5 | name: haproxy.service 6 | state: restarted 7 | enabled: true 8 | daemon_reload: true 9 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю директорию /usr/local/etc/haproxy/ 3 | ansible.builtin.file: 4 | path: /usr/local/etc/haproxy/ 5 | state: directory 6 | 7 | - name: Наливаю haproxy.cfg 8 | ansible.builtin.template: 9 | src: haproxy.cfg.j2 10 | dest: "/usr/local/etc/haproxy/haproxy.cfg" 11 | mode: "755" 12 | notify: 13 | - Перезапускаю haproxy.service 14 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Наливаю юнит файл haproxy 3 | ansible.builtin.template: 4 | src: haproxy.service.j2 5 | dest: /etc/systemd/system/haproxy.service 6 | notify: 7 | - Перезапускаю haproxy.service 8 | 9 | - name: Настраиваю haproxy.service 10 | ansible.builtin.systemd: 11 | name: haproxy.service 12 | state: started 13 | enabled: true 14 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for haproxy 3 | - name: Копирую конфигурацию haproxy 4 | ansible.builtin.import_tasks: config.yml 5 | 6 | - name: Устанавливаю haproxy 7 | ansible.builtin.import_tasks: install.yml 8 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/templates/haproxy.cfg.j2: -------------------------------------------------------------------------------- 1 | global 2 | maxconn 100 3 | 4 | defaults 5 | log global 6 | mode tcp 7 | retries 2 8 | timeout client 30m 9 | timeout connect 4s 10 | timeout server 30m 11 | timeout check 5s 12 | 13 | listen stats 14 | mode http 15 | bind *:7000 16 | stats enable 17 | stats uri / 18 | 19 | listen patroni-postgresql-primary 20 | bind *:5000 21 | option httpchk OPTIONS /master 22 | http-check expect status 200 23 | default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions 24 | server 10.0.75.111 10.0.75.111:5432 maxconn 100 check port 8008 25 | server 10.0.75.112 10.0.75.112:5432 maxconn 100 check port 8008 26 | server 10.0.75.113 10.0.75.113:5432 maxconn 100 check port 8008 27 | 28 | listen patroni-postgresql-standbys 29 | balance roundrobin 30 | bind *:5001 31 | option httpchk OPTIONS /replica 32 | http-check expect status 200 33 | default-server inter 3s fall 3 rise 2 on-marked-down shutdown-sessions 34 | server 10.0.75.111 10.0.75.111:5432 maxconn 100 check port 8008 35 | server 10.0.75.112 10.0.75.112:5432 maxconn 100 check port 8008 36 | server 10.0.75.113 10.0.75.113:5432 maxconn 100 check port 8008 37 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/templates/haproxy.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=haproxy 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | Restart=always 8 | ExecStartPre=-/usr/bin/docker rm -f haproxy 9 | ExecStart=/usr/bin/docker run \ 10 | --rm \ 11 | --publish 5000:5000 \ 12 | --publish 5001:5001 \ 13 | --publish 7000:7000 \ 14 | --volume /usr/local/etc/haproxy/:/usr/local/etc/haproxy/:ro \ 15 | --sysctl net.ipv4.ip_unprivileged_port_start=0 \ 16 | --name haproxy \ 17 | haproxy:{{ haproxy_version }} 18 | ExecStop=/usr/bin/docker stop -t 10 haproxy 19 | 20 | [Install] 21 | WantedBy=multi-user.target 22 | -------------------------------------------------------------------------------- /ansible/roles/haproxy/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for haproxy 3 | haproxy_version: 3.0.2-alpine 4 | -------------------------------------------------------------------------------- /ansible/roles/haproxy_static_pods/files/check_apiserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | errorExit() { 4 | echo "*** $*" 1>&2 5 | exit 1 6 | } 7 | 8 | curl --silent --max-time 2 --insecure https://localhost:8888/ -o /dev/null || errorExit "Error GET https://localhost:8888/" 9 | if ip addr | grep -q 10.0.75.80; then 10 | curl --silent --max-time 2 --insecure https://10.0.75.80:8888/ -o /dev/null || errorExit "Error GET https://10.0.75.80:8888/" 11 | fi 12 | -------------------------------------------------------------------------------- /ansible/roles/haproxy_static_pods/files/haproxy.cfg: -------------------------------------------------------------------------------- 1 | # /etc/haproxy/haproxy.cfg 2 | #--------------------------------------------------------------------- 3 | # Global settings 4 | #--------------------------------------------------------------------- 5 | global 6 | log /dev/log local0 7 | log /dev/log local1 notice 8 | daemon 9 | 10 | #--------------------------------------------------------------------- 11 | # common defaults that all the 'listen' and 'backend' sections will 12 | # use if not designated in their block 13 | #--------------------------------------------------------------------- 14 | defaults 15 | mode http 16 | log global 17 | option httplog 18 | option dontlognull 19 | option http-server-close 20 | option forwardfor except 127.0.0.0/8 21 | option redispatch 22 | retries 1 23 | timeout http-request 10s 24 | timeout queue 20s 25 | timeout connect 5s 26 | timeout client 20s 27 | timeout server 20s 28 | timeout http-keep-alive 10s 29 | timeout check 10s 30 | 31 | #--------------------------------------------------------------------- 32 | # apiserver frontend which proxys to the control plane nodes 33 | #--------------------------------------------------------------------- 34 | frontend apiserver 35 | bind *:8888 36 | mode tcp 37 | option tcplog 38 | default_backend apiserverbackend 39 | 40 | #--------------------------------------------------------------------- 41 | # round robin balancing for apiserver 42 | #--------------------------------------------------------------------- 43 | backend apiserverbackend 44 | option httpchk GET /healthz 45 | http-check expect status 200 46 | mode tcp 47 | option ssl-hello-chk 48 | balance roundrobin 49 | server 10.0.75.81 10.0.75.81:6443 check 50 | server 10.0.75.82 10.0.75.82:6443 check 51 | server 10.0.75.83 10.0.75.83:6443 check 52 | # [...] 53 | -------------------------------------------------------------------------------- /ansible/roles/haproxy_static_pods/files/haproxy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: haproxy 5 | namespace: kube-system 6 | spec: 7 | containers: 8 | - image: haproxy:3.0.2 9 | name: haproxy 10 | livenessProbe: 11 | failureThreshold: 8 12 | httpGet: 13 | host: localhost 14 | path: /healthz 15 | port: 8888 16 | scheme: HTTPS 17 | volumeMounts: 18 | - mountPath: /usr/local/etc/haproxy/haproxy.cfg 19 | name: haproxyconf 20 | readOnly: true 21 | hostNetwork: true 22 | volumes: 23 | - hostPath: 24 | path: /etc/haproxy/haproxy.cfg 25 | type: FileOrCreate 26 | name: haproxyconf 27 | status: {} 28 | -------------------------------------------------------------------------------- /ansible/roles/haproxy_static_pods/files/keepalived.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | creationTimestamp: null 5 | name: keepalived 6 | namespace: kube-system 7 | spec: 8 | containers: 9 | - image: ghcr.io/batonogov/docker-keepalived:2.0.20 10 | name: keepalived 11 | resources: {} 12 | securityContext: 13 | capabilities: 14 | add: 15 | - NET_ADMIN 16 | - NET_BROADCAST 17 | - NET_RAW 18 | volumeMounts: 19 | - mountPath: /usr/local/etc/keepalived/keepalived.conf 20 | name: config 21 | - mountPath: /etc/keepalived/check_apiserver.sh 22 | name: check 23 | hostNetwork: true 24 | volumes: 25 | - hostPath: 26 | path: /etc/keepalived/keepalived.conf 27 | name: config 28 | - hostPath: 29 | path: /etc/keepalived/check_apiserver.sh 30 | name: check 31 | status: {} 32 | -------------------------------------------------------------------------------- /ansible/roles/haproxy_static_pods/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # tasks file for haproxy_static_pods 2 | - name: Создать директории /etc/kubernetes/manifests и /etc/keepalived 3 | ansible.builtin.file: 4 | path: "{{ item }}" 5 | state: directory 6 | mode: "755" 7 | with_items: 8 | - /etc/kubernetes/manifests 9 | - /etc/keepalived 10 | - /etc/haproxy 11 | 12 | - name: Наливаю конфигурацию keepalived 13 | ansible.builtin.template: 14 | src: keepalived.conf.j2 15 | dest: /etc/keepalived/keepalived.conf 16 | mode: "644" 17 | 18 | - name: Наливаю check_apiserver.sh 19 | ansible.builtin.copy: 20 | src: check_apiserver.sh 21 | dest: /etc/keepalived/check_apiserver.sh 22 | mode: "644" 23 | 24 | - name: Наливаю haproxy.cfg 25 | ansible.builtin.copy: 26 | src: haproxy.cfg 27 | dest: /etc/haproxy/haproxy.cfg 28 | mode: "644" 29 | 30 | - name: Наливаю keepalived static pods manifest 31 | ansible.builtin.copy: 32 | src: keepalived.yaml 33 | dest: /etc/kubernetes/manifests/keepalived.yaml 34 | mode: "644" 35 | 36 | - name: Наливаю haproxy static pods manifest 37 | ansible.builtin.copy: 38 | src: haproxy.yaml 39 | dest: /etc/kubernetes/manifests/haproxy.yaml 40 | mode: "644" 41 | -------------------------------------------------------------------------------- /ansible/roles/haproxy_static_pods/templates/keepalived.conf.j2: -------------------------------------------------------------------------------- 1 | ! /etc/keepalived/keepalived.conf 2 | ! Configuration File for keepalived 3 | global_defs { 4 | router_id LVS_DEVEL 5 | } 6 | vrrp_script check_apiserver { 7 | script "/etc/keepalived/check_apiserver.sh" 8 | interval 3 9 | weight -2 10 | fall 10 11 | rise 2 12 | } 13 | 14 | vrrp_instance VI_1 { 15 | state MASTER 16 | interface eth0 17 | virtual_router_id 51 18 | priority 101 19 | authentication { 20 | auth_type PASS 21 | auth_pass {{ lookup('password', 'secrets/kubeadm/keepalived/auth_pass length=64') }} 22 | } 23 | virtual_ipaddress { 24 | 10.0.75.80/24 25 | } 26 | track_script { 27 | check_apiserver 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for keepalived 3 | unit_file: keepalived.master.conf.j2 4 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Перезапускаю keepalived.service 3 | ansible.builtin.systemd: 4 | name: keepalived.service 5 | state: restarted 6 | enabled: true 7 | daemon_reload: true 8 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/tasks/config.yml: -------------------------------------------------------------------------------- 1 | - name: Наливаю конфигурацию keepalived 2 | ansible.builtin.template: 3 | src: "{{ unit_file }}" 4 | dest: /etc/keepalived/keepalived.conf 5 | mode: "644" 6 | notify: 7 | - Перезапускаю keepalived.service 8 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/tasks/install.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Устанавливаю keepalived 3 | ansible.builtin.apt: 4 | name: 5 | - keepalived 6 | state: present 7 | update_cache: true 8 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for keepalived 3 | - name: Устанавливаю keepalived 4 | ansible.builtin.import_tasks: install.yml 5 | 6 | - name: Наливаю конфигурацию 7 | ansible.builtin.include_tasks: config.yml 8 | 9 | - name: Запускаю сервис 10 | ansible.builtin.include_tasks: start.yml 11 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/tasks/start.yml: -------------------------------------------------------------------------------- 1 | - name: Настраиваю keepalived.service 2 | ansible.builtin.systemd: 3 | name: keepalived.service 4 | state: started 5 | enabled: true 6 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/templates/keepalived.backup.conf.j2: -------------------------------------------------------------------------------- 1 | vrrp_instance VI_1 { 2 | state BACKUP 3 | interface eth0 4 | virtual_router_id {{ virtual_router_id }} 5 | priority 100 # PAY ATTENTION ON PRIORITY!! 6 | authentication { 7 | auth_type PASS 8 | auth_pass {{ lookup('password', 'secrets/keepalived/' + virtual_ip + ' length=64') }} 9 | } 10 | 11 | virtual_ipaddress { 12 | {{ virtual_ip }} dev eth0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ansible/roles/keepalived/templates/keepalived.master.conf.j2: -------------------------------------------------------------------------------- 1 | vrrp_instance VI_1 { 2 | state MASTER 3 | interface eth0 4 | virtual_router_id {{ virtual_router_id }} 5 | priority 101 # PAY ATTENTION ON PRIORITY!! 6 | authentication { 7 | auth_type PASS 8 | auth_pass {{ lookup('password', 'secrets/keepalived/' + virtual_ip + ' length=64') }} 9 | } 10 | 11 | virtual_ipaddress { 12 | {{ virtual_ip }} dev eth0 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Загружаю образ 3 | community.docker.docker_image: 4 | name: "quay.io/minio/minio" 5 | tag: "{{ minio_version }}" 6 | source: pull 7 | 8 | - name: Перезапускаю minio.service 9 | ansible.builtin.systemd: 10 | name: minio.service 11 | state: restarted 12 | enabled: true 13 | daemon_reload: true 14 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/tasks/add_dirs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Create a directory if it does not exist 3 | ansible.builtin.file: 4 | path: /var/lib/minio 5 | state: directory 6 | owner: "{{ minio_username }}" 7 | group: "{{ minio_username }}" 8 | mode: "755" 9 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/tasks/add_user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю пользователя 3 | ansible.builtin.user: 4 | name: "{{ minio_username }}" 5 | shell: /sbin/nologin 6 | create_home: true 7 | groups: docker 8 | uid: "{{ minio_uid }}" 9 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for minio_start 3 | - name: Создаю пользователя 4 | ansible.builtin.import_tasks: add_user.yml 5 | 6 | - name: Создаю директории 7 | ansible.builtin.import_tasks: add_dirs.yml 8 | 9 | - name: Запускаю minio 10 | ansible.builtin.import_tasks: start_minio.yml 11 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/tasks/start_minio.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Наливаю переменные 3 | ansible.builtin.template: 4 | src: minio.config.j2 5 | dest: /etc/default/minio 6 | owner: "{{ minio_username }}" 7 | group: "{{ minio_username }}" 8 | mode: "644" 9 | notify: 10 | - Перезапускаю minio.service 11 | 12 | - name: Наливаю юнит файл 13 | ansible.builtin.template: 14 | src: minio.service.j2 15 | dest: /etc/systemd/system/minio.service 16 | mode: "644" 17 | notify: 18 | - Загружаю образ 19 | - Перезапускаю minio.service 20 | 21 | - name: Create a directory if it does not exist 22 | ansible.builtin.file: 23 | path: '{{ item["path"] }}/minio' 24 | state: directory 25 | owner: "{{ minio_username }}" 26 | group: "{{ minio_username }}" 27 | mode: "755" 28 | loop: "{{ device }}" 29 | 30 | - name: Настраиваю minio.service 31 | ansible.builtin.systemd: 32 | name: minio.service 33 | state: started 34 | enabled: true 35 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/templates/minio.config.j2: -------------------------------------------------------------------------------- 1 | # MINIO_ROOT_USER and MINIO_ROOT_PASSWORD sets the root account for the MinIO server. 2 | # This user has unrestricted permissions to perform S3 and administrative API operations on any resource in the deployment. 3 | # Omit to use the default values 'minioadmin:minioadmin'. 4 | # MinIO recommends setting non-default values as a best practice, regardless of environment. 5 | 6 | MINIO_ROOT_USER=admin 7 | MINIO_ROOT_PASSWORD={{ lookup('password', 'secrets/minio/admin_secret length=64') }} 8 | 9 | # MINIO_VOLUMES sets the storage volumes or paths to use for the MinIO server. 10 | # The specified path uses MinIO expansion notation to denote a sequential series of drives between 1 and 4, inclusive. 11 | # All drives or paths included in the expanded drive list must exist *and* be empty or freshly formatted for MinIO to start successfully. 12 | 13 | MINIO_VOLUMES="http://minio-node{1...4}:9000/mnt/disk1/minio" 14 | 15 | # MINIO_SERVER_URL sets the hostname of the local machine for use with the MinIO Server. 16 | # MinIO assumes your network control plane can correctly resolve this hostname to the local machine. 17 | 18 | # Uncomment the following line and replace the value with the correct hostname for the local machine. 19 | 20 | MINIO_SERVER_URL="http://10.0.75.90" 21 | MINIO_BROWSER_REDIRECT_URL="https://s3.example.local/minio/ui" 22 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/templates/minio.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=minio 3 | Requires=docker.service 4 | After=docker.service 5 | 6 | [Service] 7 | User={{ minio_username }} 8 | Group={{ minio_username }} 9 | Restart=always 10 | ExecStartPre=-/usr/bin/docker rm -f minio 11 | ExecStart=/usr/bin/docker run \ 12 | --rm \ 13 | --network host \ 14 | --user {{ minio_uid }}:{{ minio_uid }} \ 15 | --name minio \ 16 | --env "MINIO_CONFIG_ENV_FILE=/etc/config.env" \ 17 | --add-host "minio-node1:10.0.75.55" \ 18 | --add-host "minio-node2:10.0.75.56" \ 19 | --add-host "minio-node3:10.0.75.57" \ 20 | --add-host "minio-node4:10.0.75.58" \ 21 | --volume /etc/default/minio:/etc/config.env:ro \ 22 | --volume /var/lib/minio:/var/lib/minio \ 23 | --volume /mnt/disk1/minio:/mnt/disk1/minio \ 24 | quay.io/minio/minio:{{ minio_version }} \ 25 | server /var/lib/minio --console-address ":9001" 26 | ExecStop=/usr/bin/docker stop -t 10 minio 27 | 28 | [Install] 29 | WantedBy=multi-user.target 30 | -------------------------------------------------------------------------------- /ansible/roles/minio_start/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for minio_start 3 | # Мы качаем docker image отсюда: https://quay.io/minio/minio/ 4 | minio_version: RELEASE.2024-07-16T23-46-41Z 5 | minio_uid: 1111 6 | -------------------------------------------------------------------------------- /ansible/roles/mount/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Mount up device 3 | ansible.posix.mount: 4 | path: '{{ item["path"] }}' 5 | src: '{{ item["src"] }}' 6 | fstype: "{{ filesystem }}" 7 | state: mounted 8 | loop: "{{ device }}" 9 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/files/conf.d/minio.conf: -------------------------------------------------------------------------------- 1 | upstream minio_s3 { 2 | least_conn; 3 | server 10.0.75.55:9000; 4 | server 10.0.75.56:9000; 5 | server 10.0.75.57:9000; 6 | server 10.0.75.58:9000; 7 | } 8 | 9 | upstream minio_console { 10 | least_conn; 11 | server 10.0.75.55:9001; 12 | server 10.0.75.56:9001; 13 | server 10.0.75.57:9001; 14 | server 10.0.75.58:9001; 15 | } 16 | 17 | server { 18 | listen 80; 19 | listen [::]:80; 20 | listen *:443 ssl; 21 | server_name s3.example.local www.s3.example.local; 22 | ssl_certificate /etc/ssl/private/minio.crt; 23 | ssl_certificate_key /etc/ssl/private/private.key; 24 | 25 | server_tokens off; 26 | 27 | # Allow special characters in headers 28 | ignore_invalid_headers off; 29 | # Allow any size file to be uploaded. 30 | # Set to a value such as 1000m; to restrict file size to a specific value 31 | client_max_body_size 0; 32 | # Disable buffering 33 | proxy_buffering off; 34 | proxy_request_buffering off; 35 | 36 | location / { 37 | proxy_set_header Host $http_host; 38 | proxy_set_header X-Real-IP $remote_addr; 39 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 40 | proxy_set_header X-Forwarded-Proto $scheme; 41 | 42 | proxy_connect_timeout 300; 43 | # Default is HTTP/1, keepalive is only enabled in HTTP/1.1 44 | proxy_http_version 1.1; 45 | proxy_set_header Connection ""; 46 | chunked_transfer_encoding off; 47 | 48 | proxy_pass http://minio_s3; # This uses the upstream directive definition to load balance 49 | } 50 | 51 | location /minio/ui/ { 52 | rewrite ^/minio/ui/(.*) /$1 break; 53 | proxy_set_header Host $http_host; 54 | proxy_set_header X-Real-IP $remote_addr; 55 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 56 | proxy_set_header X-Forwarded-Proto $scheme; 57 | proxy_set_header X-NginX-Proxy true; 58 | 59 | # This is necessary to pass the correct IP to be hashed 60 | real_ip_header X-Real-IP; 61 | 62 | proxy_connect_timeout 300; 63 | 64 | # To support websockets in MinIO versions released after January 2023 65 | proxy_http_version 1.1; 66 | proxy_set_header Upgrade $http_upgrade; 67 | proxy_set_header Connection "upgrade"; 68 | 69 | chunked_transfer_encoding off; 70 | 71 | proxy_pass http://minio_console; # This uses the upstream directive definition to load balance 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Перезапускаю сервер nginx 3 | community.docker.docker_container_exec: 4 | container: nginx 5 | command: "{{ item }}" 6 | loop: 7 | - nginx -t 8 | - nginx -s reload 9 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/tasks/cert.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю директорию для ключей 3 | ansible.builtin.file: 4 | path: /etc/ssl/private 5 | state: directory 6 | owner: "{{ nginx_user }}" 7 | group: "{{ nginx_user }}" 8 | mode: "755" 9 | 10 | - name: Генерирую приватный ключ 11 | community.crypto.openssl_privatekey: 12 | path: "/etc/ssl/private/private.key" 13 | mode: "0600" 14 | owner: "{{ nginx_user }}" 15 | group: "{{ nginx_user }}" 16 | 17 | - name: Создаю запроса на подписание сертификата (CSR) для самоподписанного сертификата 18 | community.crypto.openssl_csr_pipe: 19 | privatekey_path: "/etc/ssl/private/private.key" 20 | common_name: "minio" 21 | organization_name: Example, Inc. 22 | subject_alt_name: 23 | - "DNS:s3.example.local" 24 | register: csr 25 | 26 | - name: Создаю самоподписанный сертификат из CSR 27 | community.crypto.x509_certificate: 28 | path: "/etc/ssl/private/minio.crt" 29 | csr_content: "{{ csr.csr }}" 30 | privatekey_path: "/etc/ssl/private/private.key" 31 | provider: selfsigned 32 | mode: "0640" 33 | owner: "{{ nginx_user }}" 34 | group: "{{ nginx_user }}" 35 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю директорию для nginx conf 3 | ansible.builtin.file: 4 | path: /etc/nginx/conf.d 5 | state: directory 6 | owner: "{{ nginx_user }}" 7 | group: "{{ nginx_user }}" 8 | mode: "755" 9 | 10 | - name: Синхронизирую конфигурационные файлы 11 | ansible.posix.synchronize: 12 | src: "conf.d/" 13 | dest: "/etc/nginx/conf.d/" 14 | delete: true 15 | rsync_opts: 16 | - "--chown={{ nginx_user }}:{{ nginx_user }}" 17 | - "--chmod=F640" 18 | notify: 19 | - Перезапускаю сервер nginx 20 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю пользователя nginx 3 | ansible.builtin.import_tasks: user.yml 4 | when: ansible_distribution == 'Ubuntu' 5 | 6 | - name: Копирую конфигурацию 7 | ansible.builtin.import_tasks: config.yml 8 | when: ansible_distribution == 'Ubuntu' 9 | 10 | - name: Генерирую самоподписанный сертификат 11 | ansible.builtin.import_tasks: cert.yml 12 | when: ansible_distribution == 'Ubuntu' 13 | 14 | - name: Устанавливаю nginx 15 | ansible.builtin.import_tasks: nginx.yml 16 | when: ansible_distribution == 'Ubuntu' 17 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/tasks/nginx.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Наливаю docker-compose файл nginx 3 | ansible.builtin.template: 4 | src: docker-compose.yml.j2 5 | dest: "/home/{{ nginx_user }}/docker-compose.yml" 6 | owner: "{{ nginx_user }}" 7 | group: "{{ nginx_user }}" 8 | mode: "640" 9 | 10 | - name: Налаживаю права для директории /etc/letsencrypt/ 11 | ansible.builtin.file: 12 | path: "{{ item }}" 13 | owner: "{{ nginx_user }}" 14 | group: "{{ nginx_user }}" 15 | recurse: true 16 | with_items: 17 | - "/etc/letsencrypt/" 18 | 19 | - name: Поднимаю nginx 20 | community.docker.docker_compose_v2: 21 | project_src: "/home/{{ nginx_user }}/" 22 | remove_orphans: true 23 | wait: true 24 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/tasks/user.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю пользователя {{ nginx_user }} 3 | ansible.builtin.user: 4 | name: "{{ nginx_user }}" 5 | state: present 6 | groups: docker 7 | shell: /bin/bash 8 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/templates/docker-compose.yml.j2: -------------------------------------------------------------------------------- 1 | services: 2 | nginx: 3 | image: nginx:{{ nginx_version }} 4 | container_name: nginx 5 | restart: always 6 | ports: 7 | - "80:80" 8 | - "443:443" 9 | environment: 10 | - TZ=Europe/Moscow 11 | volumes: 12 | - /etc/nginx/conf.d/:/etc/nginx/conf.d/:ro 13 | - /etc/nginx/include.d/:/etc/nginx/include.d/:ro 14 | - /etc/ssl/private/:/etc/ssl/private/:ro 15 | - /var/lib/letsencrypt/:/var/lib/letsencrypt/:ro 16 | - /etc/letsencrypt:/etc/letsencrypt:ro 17 | depends_on: 18 | - certbot 19 | 20 | certbot: 21 | image: certbot/certbot:{{ certbot_version }} 22 | container_name: certbot 23 | restart: always 24 | environment: 25 | - TZ=Europe/Moscow 26 | volumes: 27 | - /etc/letsencrypt:/etc/letsencrypt 28 | entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew --webroot -w /etc/letsencrypt/ -n; sleep 12h & wait $${!}; done;'" 29 | -------------------------------------------------------------------------------- /ansible/roles/nginx_install/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for nginx-install 3 | nginx_version: 1.27.0-alpine 4 | nginx_user: nginx 5 | certbot_version: v2.11.0 6 | -------------------------------------------------------------------------------- /ansible/roles/ntp_install/README.md: -------------------------------------------------------------------------------- 1 | Настройка NTP 2 | ========= 3 | 4 | Данная роль настраивает часовой пояс и NTP на узлах. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Тестирование приводилось на **Ubuntu 20.04 (Focal Fossa)** и **Ubuntu 22.04 (Jammy Jellyfish)**. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | Переменные не используются. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | Для настройки часового пояса используется роль 20 | [community.general.timezone](https://docs.ansible.com/ansible/latest/collections/community/general/timezone_module.html), 21 | в качестве параметра принимается строка с нужным часовым поясом, например **Europe/Moscow**. 22 | 23 | Example Playbook 24 | ---------------- 25 | 26 | - hosts: servers 27 | roles: 28 | - ntp-install 29 | 30 | License 31 | ------- 32 | 33 | MIT 34 | 35 | Author Information 36 | ------------------ 37 | 38 | Федор Батоногов 39 | -------------------------------------------------------------------------------- /ansible/roles/ntp_install/tasks/debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for ntp-install 3 | - name: Настраиваю Московское время 4 | community.general.timezone: 5 | name: Europe/Moscow 6 | 7 | - name: Устанавливаю ntp 8 | ansible.builtin.apt: 9 | pkg: 10 | - ntp 11 | update_cache: true 12 | 13 | - name: Настраиваю сервис ntp 14 | ansible.builtin.systemd: 15 | name: ntp 16 | state: started 17 | enabled: true 18 | 19 | - name: Наливаю конфигурацию ntp из шаблона 20 | ansible.builtin.template: 21 | src: debian.conf.j2 22 | dest: '/etc/ntpsec/ntp.conf' 23 | -------------------------------------------------------------------------------- /ansible/roles/ntp_install/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for ntp-install 3 | - name: Устанавливаю ntp на Ubuntu 4 | ansible.builtin.import_tasks: ubuntu.yml 5 | when: ansible_distribution == 'Ubuntu' 6 | 7 | - name: Устанавливаю ntp на Debian 8 | ansible.builtin.import_tasks: debian.yml 9 | when: ansible_distribution == 'Debian' 10 | -------------------------------------------------------------------------------- /ansible/roles/ntp_install/tasks/ubuntu.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for ntp-install 3 | - name: Настраиваю Московское время 4 | community.general.timezone: 5 | name: Europe/Moscow 6 | 7 | - name: Устанавливаю ntp 8 | ansible.builtin.apt: 9 | pkg: 10 | - ntp 11 | update_cache: true 12 | 13 | - name: Настраиваю сервис ntp 14 | ansible.builtin.systemd: 15 | name: ntp 16 | state: started 17 | enabled: true 18 | 19 | - name: Наливаю конфигурацию ntp из шаблона 20 | ansible.builtin.template: 21 | src: ubuntu.conf.j2 22 | dest: '/etc/ntp.conf' 23 | -------------------------------------------------------------------------------- /ansible/roles/ntp_install/templates/debian.conf.j2: -------------------------------------------------------------------------------- 1 | # /etc/ntpsec/ntp.conf, configuration for ntpd; see ntp.conf(5) for help 2 | 3 | driftfile /var/lib/ntpsec/ntp.drift 4 | leapfile /usr/share/zoneinfo/leap-seconds.list 5 | 6 | # To enable Network Time Security support as a server, obtain a certificate 7 | # (e.g. with Let's Encrypt), configure the paths below, and uncomment: 8 | # nts cert CERT_FILE 9 | # nts key KEY_FILE 10 | # nts enable 11 | 12 | # You must create /var/log/ntpsec (owned by ntpsec:ntpsec) to enable logging. 13 | #statsdir /var/log/ntpsec/ 14 | #statistics loopstats peerstats clockstats 15 | #filegen loopstats file loopstats type day enable 16 | #filegen peerstats file peerstats type day enable 17 | #filegen clockstats file clockstats type day enable 18 | 19 | # This should be maxclock 7, but the pool entries count towards maxclock. 20 | tos maxclock 11 21 | 22 | # Comment this out if you have a refclock and want it to be able to discipline 23 | # the clock by itself (e.g. if the system is not connected to the network). 24 | tos minclock 4 minsane 3 25 | 26 | # Specify one or more NTP servers. 27 | 28 | # Public NTP servers supporting Network Time Security: 29 | # server time.cloudflare.com nts 30 | 31 | # pool.ntp.org maps to about 1000 low-stratum NTP servers. Your server will 32 | # pick a different set every time it starts up. Please consider joining the 33 | # pool: 34 | pool 0.debian.pool.ntp.org iburst 35 | pool 1.debian.pool.ntp.org iburst 36 | pool 2.debian.pool.ntp.org iburst 37 | pool 3.debian.pool.ntp.org iburst 38 | 39 | # Access control configuration; see /usr/share/doc/ntpsec-doc/html/accopt.html 40 | # for details. 41 | # 42 | # Note that "restrict" applies to both servers and clients, so a configuration 43 | # that might be intended to block requests from certain clients could also end 44 | # up blocking replies from your own upstream servers. 45 | 46 | # By default, exchange time with everybody, but don't allow configuration. 47 | restrict default kod nomodify nopeer noquery limited 48 | 49 | # Local users may interrogate the ntp server more closely. 50 | restrict 127.0.0.1 51 | restrict ::1 52 | -------------------------------------------------------------------------------- /ansible/roles/ntp_install/templates/ubuntu.conf.j2: -------------------------------------------------------------------------------- 1 | # /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help 2 | 3 | driftfile /var/lib/ntp/ntp.drift 4 | 5 | # Leap seconds definition provided by tzdata 6 | leapfile /usr/share/zoneinfo/leap-seconds.list 7 | 8 | # Enable this if you want statistics to be logged. 9 | #statsdir /var/log/ntpstats/ 10 | 11 | statistics loopstats peerstats clockstats 12 | filegen loopstats file loopstats type day enable 13 | filegen peerstats file peerstats type day enable 14 | filegen clockstats file clockstats type day enable 15 | 16 | # Specify one or more NTP servers. 17 | 18 | # Use servers from the NTP Pool Project. Approved by Ubuntu Technical Board 19 | # on 2011-02-08 (LP: #104525). See http://www.pool.ntp.org/join.html for 20 | # more information. 21 | pool 0.ubuntu.pool.ntp.org iburst 22 | pool 1.ubuntu.pool.ntp.org iburst 23 | pool 2.ubuntu.pool.ntp.org iburst 24 | pool 3.ubuntu.pool.ntp.org iburst 25 | 26 | # Use Ubuntu's ntp server as a fallback. 27 | pool ntp.ubuntu.com 28 | 29 | # Access control configuration; see /usr/share/doc/ntp-doc/html/accopt.html for 30 | # details. The web page 31 | # might also be helpful. 32 | # 33 | # Note that "restrict" applies to both servers and clients, so a configuration 34 | # that might be intended to block requests from certain clients could also end 35 | # up blocking replies from your own upstream servers. 36 | 37 | # By default, exchange time with everybody, but don't allow configuration. 38 | restrict -4 default kod notrap nomodify nopeer noquery limited 39 | restrict -6 default kod notrap nomodify nopeer noquery limited 40 | 41 | # Local users may interrogate the ntp server more closely. 42 | restrict 127.0.0.1 43 | restrict ::1 44 | 45 | # Needed for adding pool entries 46 | restrict source notrap nomodify noquery 47 | 48 | # Clients from this (example!) subnet have unlimited access, but only if 49 | # cryptographically authenticated. 50 | #restrict 192.168.123.0 mask 255.255.255.0 notrust 51 | 52 | 53 | # If you want to provide time to your local subnet, change the next line. 54 | # (Again, the address is an example only.) 55 | #broadcast 192.168.123.255 56 | 57 | # If you want to listen to time broadcasts on your local subnet, de-comment the 58 | # next lines. Please do this only if you trust everybody on the network! 59 | #disable auth 60 | #broadcastclient 61 | -------------------------------------------------------------------------------- /ansible/roles/patroni/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Перезапускаю patroni.service 3 | ansible.builtin.systemd: 4 | name: patroni.service 5 | state: restarted 6 | enabled: true 7 | daemon_reload: true 8 | -------------------------------------------------------------------------------- /ansible/roles/patroni/tasks/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Создаю директории и задаю права 3 | ansible.builtin.file: 4 | path: "{{ patroni_config_dir }}" 5 | state: directory 6 | mode: "755" 7 | owner: "999" 8 | group: "999" 9 | 10 | - name: Наливаю конфиг patroni 11 | ansible.builtin.template: 12 | src: config.yml.j2 13 | dest: "{{ patroni_config_dir }}/config.yml" 14 | mode: "755" 15 | # backup: true 16 | notify: 17 | - Перезапускаю patroni.service 18 | -------------------------------------------------------------------------------- /ansible/roles/patroni/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Наливаю юнит файл 3 | ansible.builtin.import_tasks: service.yml 4 | 5 | - name: Наливаю конфигурацию 6 | ansible.builtin.import_tasks: config.yml 7 | -------------------------------------------------------------------------------- /ansible/roles/patroni/tasks/service.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Наливаю юнит файл patroni 3 | ansible.builtin.template: 4 | src: patroni.service.j2 5 | dest: /etc/systemd/system/patroni.service 6 | notify: 7 | - Перезапускаю patroni.service 8 | 9 | - name: Создаю директории и задаю права 10 | ansible.builtin.file: 11 | path: "{{ item }}" 12 | state: directory 13 | mode: "0700" 14 | owner: "999" 15 | group: "999" 16 | loop: 17 | - "{{ pg_data_dir }}" 18 | - "{{ pg_data_dir }}/data" 19 | notify: 20 | - Перезапускаю patroni.service 21 | 22 | - name: Настраиваю patroni.service 23 | ansible.builtin.systemd: 24 | name: patroni.service 25 | state: started 26 | enabled: true 27 | -------------------------------------------------------------------------------- /ansible/roles/patroni/templates/config.yml.j2: -------------------------------------------------------------------------------- 1 | scope: patroni 2 | name: {{ inventory_hostname }} 3 | 4 | restapi: 5 | listen: 0.0.0.0:8008 6 | connect_address: {{ inventory_hostname }}:8008 7 | 8 | etcd: 9 | host: {{ inventory_hostname }}:2379 10 | 11 | bootstrap: 12 | # this section will be written into Etcd:///config after initializing new cluster 13 | dcs: 14 | ttl: 30 15 | loop_wait: 10 16 | retry_timeout: 10 17 | maximum_lag_on_failover: 1048576 18 | # master_start_timeout: 300 19 | # synchronous_mode: false 20 | postgresql: 21 | use_pg_rewind: true 22 | use_slots: true 23 | parameters: 24 | wal_level: replica 25 | hot_standby: "on" 26 | logging_collector: 'on' 27 | max_wal_senders: 5 28 | max_replication_slots: 5 29 | wal_log_hints: "on" 30 | #archive_mode: "on" 31 | #archive_timeout: 600 32 | #archive_command: "cp -f %p /home/postgres/archived/%f" 33 | #recovery_conf: 34 | #restore_command: cp /home/postgres/archived/%f %p 35 | 36 | # some desired options for 'initdb' 37 | initdb: # Note: It needs to be a list (some options need values, others are switches) 38 | - encoding: UTF8 39 | - data-checksums 40 | 41 | pg_hba: # Add following lines to pg_hba.conf after running 'initdb' 42 | - host replication replicator 10.0.75.0/24 md5 43 | - host replication replicator 127.0.0.1/32 trust 44 | - host all all 10.0.75.0/24 md5 45 | - host all all 0.0.0.0/0 md5 46 | # - hostssl all all 0.0.0.0/0 md5 47 | 48 | # Additional script to be launched after initial cluster creation (will be passed the connection URL as parameter) 49 | # post_init: /usr/local/bin/setup_cluster.sh 50 | # Some additional users users which needs to be created after initializing new cluster 51 | users: 52 | admin: 53 | password: admin 54 | options: 55 | - createrole 56 | - createdb 57 | 58 | postgresql: 59 | listen: 0.0.0.0:5432 60 | connect_address: {{ inventory_hostname }}:5432 61 | data_dir: "/var/lib/postgresql/patroni/main" 62 | bin_dir: "/usr/lib/postgresql/16/bin" 63 | # config_dir: 64 | pgpass: /tmp/pgpass0 65 | authentication: 66 | replication: 67 | username: replicator 68 | password: {{ lookup('password', 'secrets/patroni-postgresql/replicator-password length=64') }} 69 | superuser: 70 | username: postgres 71 | password: {{ lookup('password', 'secrets/patroni-postgresql/postgres-password length=64') }} 72 | parameters: 73 | unix_socket_directories: '/var/run/postgresql' 74 | 75 | watchdog: 76 | mode: off 77 | 78 | tags: 79 | nofailover: false 80 | noloadbalance: false 81 | clonefrom: false 82 | nosync: false 83 | -------------------------------------------------------------------------------- /ansible/roles/patroni/templates/patroni.service.j2: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=patroni 3 | Requires=etcd.service 4 | After=etcd.service 5 | 6 | [Service] 7 | User={{ patroni_user }} 8 | Restart=always 9 | ExecStartPre=-/usr/bin/docker rm -f patroni 10 | ExecStart=/usr/bin/docker run \ 11 | --rm \ 12 | --name patroni \ 13 | --hostname {{ inventory_hostname }} \ 14 | --publish 5432:5432 \ 15 | --publish 8008:8008 \ 16 | --publish 8091:8091 \ 17 | --add-host "patroni-postgresql-01:10.0.75.111" \ 18 | --add-host "patroni-postgresql-02:10.0.75.112" \ 19 | --add-host "patroni-postgresql-03:10.0.75.113" \ 20 | --volume={{ patroni_config_dir }}/config.yml:{{ patroni_config_dir }}/config.yml:ro \ 21 | --volume={{ pg_data_dir }}:{{ pg_data_dir }} \ 22 | ghcr.io/batonogov/patroni-docker:{{ image_version }} 23 | ExecStop=/usr/bin/docker stop -t 10 patroni 24 | 25 | [Install] 26 | WantedBy=multi-user.target 27 | -------------------------------------------------------------------------------- /ansible/roles/patroni/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | image_version: v3.3.0-pg16.3 3 | pg_data_dir: /var/lib/postgresql 4 | patroni_config_dir: /etc/patroni 5 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Docker 2 | 3 | ## Blue-green deployment 4 | 5 | Сине-зеленое развертывание — это стратегия развертывания, при которой вы создаете два отдельных, но идентичные среды. 6 | Одна среда (синяя) использует текущую версию приложения и одна среда (зеленая) использует новую версию приложения. 7 | Использование синего/зеленого развертывания стратегия повышает доступность приложений 8 | и снижает риск развертывания за счет упрощения процесс отката в случае сбоя развертывания. 9 | После завершения тестирования на зеленом среда, трафик активных приложений направляется в зеленую среду и синюю среда устарела. 10 | 11 | [Blue-green deployment](./blue-green-deployment/) - это программная платформа для быстрой сборки, отладки и развертывания приложений с помощью **контейнеров**. 12 | 13 | ## uv 14 | 15 | Чрезвычайно быстрый менеджер пакетов и проектов Python, написанный на Rust. 16 | 17 | Один инструмент для замены **pip**, **pip-tools**, **pipx**, **poetry**, **pyenv**, **twine**, **virtualenv**, и многое другое. 18 | 19 | [uv](./uv/) 20 | [Официальный GitHub репозиторий](https://github.com/astral-sh/uv) 21 | -------------------------------------------------------------------------------- /docker/blue-green-deployment/Dockerfile: -------------------------------------------------------------------------------- 1 | # Используем официальный образ Golang 2 | FROM golang:1.23.4-alpine AS builder 3 | # Устанавливаем рабочую директорию 4 | WORKDIR /build 5 | # Копируем исходный код приложения в контейнер 6 | COPY ./main.go ./ 7 | # Сборка приложения 8 | RUN CGO_ENABLED=0 go build main.go 9 | 10 | # # Отдельный этап сборки для уменьшения размера образа 11 | # FROM alpine:3.20.3 AS runner 12 | # # Определяем аргумент для имени пользователя 13 | # ARG USERNAME=appuser 14 | # ARG GROUPNAME=appgroup 15 | # # Создаем пользователя и группу 16 | # RUN addgroup -S ${GROUPNAME} && adduser -S ${USERNAME} -G ${GROUPNAME} 17 | # # Устанавливаем рабочую директорию 18 | # WORKDIR /app 19 | # # Копируем скомпилированное приложение из предыдущего этапа 20 | # COPY --from=builder /build/main ./ 21 | # # Меняем владельца файлов на нового пользователя 22 | # RUN chown -R ${USERNAME}:${GROUPNAME} /app 23 | # # Запускаем контейнер от имени нового пользователя 24 | # USER ${USERNAME} 25 | # # Запускаем healthcheck, проверяющий доступность веб-сервера на порту 8080 26 | # HEALTHCHECK --interval=5s --timeout=5s --start-period=3s --retries=3 \ 27 | # CMD wget --quiet --tries=1 --spider http://localhost:8080/ || exit 1 28 | # # Запускаем приложение при старте контейнера 29 | # CMD ["./main"] 30 | 31 | # Отдельный этап сборки для уменьшения размера образа 32 | FROM scratch 33 | # Создаем пользователя 34 | USER 100:100 35 | # Копируем скомпилированное приложение из предыдущего этапа 36 | COPY --from=builder --chown=100:100 /build/main / 37 | # Копируем curl для работы HEALTHCHECK 38 | COPY --from=ghcr.io/tarampampam/curl:8.10.1 /bin/curl /bin/curl 39 | # Запускаем healthcheck, проверяющий доступность веб-сервера на порту 8080 40 | HEALTHCHECK --interval=5s --timeout=5s --start-period=3s --retries=3 \ 41 | CMD [ "curl", "--fail", "http://127.0.0.1:8080/" ] 42 | # Запускаем приложение при старте контейнера 43 | ENTRYPOINT ["/main"] 44 | -------------------------------------------------------------------------------- /docker/blue-green-deployment/README.md: -------------------------------------------------------------------------------- 1 | # Docker 2 | 3 | ## Описание приложения 4 | 5 | Простой веб сервис написанный на **Go**, возвращающий имя узла. 6 | 7 | Сервис можно запустить несколькими способами. 8 | 9 | ### Локальный запуск 10 | 11 | Сборка приложения: 12 | 13 | ```sh 14 | go build main.go 15 | ``` 16 | 17 | Запуск 18 | 19 | ```sh 20 | ./main 21 | ``` 22 | 23 | Вывод 24 | 25 | ```output 26 | Сервер запущен на порту 8080... 27 | ``` 28 | 29 | Проверка 30 | 31 | ```sh 32 | curl localhost:8080 33 | ``` 34 | 35 | Вывод 36 | 37 | ```output 38 | Имя узла: MacBook-Pro-Fedor.local 39 | ``` 40 | 41 | ### Запуск в контейнере 42 | 43 | ```sh 44 | docker build -t test . && docker run --hostname my_container --publish 8080:8080 test 45 | ``` 46 | 47 | Вывод 48 | 49 | ```output 50 | Сервер запущен на порту 8080... 51 | ``` 52 | 53 | Проверка 54 | 55 | ```sh 56 | curl localhost:8080 57 | ``` 58 | 59 | Вывод 60 | 61 | ```output 62 | Имя узла: my_container 63 | ``` 64 | 65 | ### Запуск при помощи docker compose 66 | 67 | Запуск 68 | 69 | ```sh 70 | docker compose --profile blue up --wait --remove-orphans --scale web-blue=3 71 | ``` 72 | 73 | Вывод 74 | 75 | ```output 76 | ✔ Network docker_default Created 77 | ✔ Container docker-web-blue-1 Healthy 78 | ✔ Container docker-nginx-proxy-1 Healthy 79 | ✔ Container docker-web-blue-3 Healthy 80 | ✔ Container docker-web-blue-2 Healthy 81 | ``` 82 | 83 | Проверка 84 | 85 | Поскольку запущено несколько реплик сервиса, то при повторной проверке мы можем увидеть разное имена узлов 86 | 87 | ```sh 88 | declare -A responses 89 | count=0 90 | 91 | while [ ${#responses[@]} -lt 3 ]; do 92 | response=$(curl -s localhost:8080) 93 | 94 | if [[ -z "${responses[$response]}" ]]; then 95 | responses["$response"]=1 96 | count=$((count + 1)) 97 | echo "Уникальный ответ $count: $response" 98 | fi 99 | done 100 | ``` 101 | 102 | Вывод 103 | 104 | ```output 105 | Уникальный ответ 1: Имя узла: eeec60605b3c 106 | Уникальный ответ 2: Имя узла: 1a0e8d8f0d64 107 | Уникальный ответ 3: Имя узла: d5822762eab6 108 | ``` 109 | 110 | ### Сине-зеленое развертывание 111 | 112 | Запуск 113 | 114 | ```sh 115 | bash ./deploy.sh 116 | ``` 117 | 118 | Вывод 119 | 120 | ```output 121 | Список контейнеров 122 | NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS 123 | docker-nginx-proxy-1 nginxproxy/nginx-proxy:1.6.2-alpine "/app/docker-entrypo…" nginx-proxy 22 seconds ago Up 22 seconds (healthy) 0.0.0.0:80->80/tcp 124 | docker-web-green-1 docker-web-green "./main" web-green 6 seconds ago Up 6 seconds (healthy) 8080/tcp 125 | docker-web-green-2 docker-web-green "./main" web-green 6 seconds ago Up 6 seconds (healthy) 8080/tcp 126 | docker-web-green-3 docker-web-green "./main" web-green 6 seconds ago Up 6 seconds (healthy) 8080/tcp 127 | Журналы запуска web-green 128 | web-green-3 | Сервер запущен на порту 8080... 129 | web-green-2 | Сервер запущен на порту 8080... 130 | web-green-1 | Сервер запущен на порту 8080... 131 | ``` 132 | 133 | [Проверка](README.md#L83) 134 | -------------------------------------------------------------------------------- /docker/blue-green-deployment/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Эта строка настраивает оболочку так, чтобы она выходила из скрипта при возникновении ошибки в любой команде. 4 | # Это помогает обнаружить и обрабатывать ошибки в скрипте. 5 | set -e 6 | 7 | # Проверяем состояние контейнера с именем "web-blue", чтобы определить, является ли он "здоровым" (healthy). 8 | # Если это так, переменным NEW и OLD присваиваются значения "green" и "blue" соответственно, иначе наоборот. 9 | if [ "$(docker compose ps web-blue | grep healthy)" ] 10 | then 11 | export NEW="green" 12 | export OLD="blue" 13 | else 14 | export NEW="blue" 15 | export OLD="green" 16 | fi 17 | 18 | # Выводим сообщение о том, какой профиль поднимается в данный момент (значение переменной NEW). 19 | echo Поднимаю проект с профилем ${NEW} 20 | # Эта команда использует docker-compose для запуска проекта с указанным профилем (значение переменной NEW), 21 | # разворачивая контейнеры в фоновом режиме, пересоздавая их, удаляя сиротские контейнеры, 22 | # масштабируя сервис "web" на три экземпляра и дожидаясь их запуска. 23 | docker compose \ 24 | --profile ${NEW} \ 25 | up \ 26 | --detach \ 27 | --build \ 28 | --remove-orphans \ 29 | --scale web-${NEW}=3 \ 30 | --wait \ 31 | --quiet-pull 32 | 33 | # Эта строка выводит сообщение о том, какие сервисы останавливаются в данный момент (значение переменной OLD). 34 | echo Останавливаю сервисы ${OLD} 35 | # Эта команда использует docker-compose для остановки сервиса "web" с именем, соответствующим значению переменной OLD. 36 | docker compose stop \ 37 | web-${OLD} 38 | 39 | # Эта строка выводит сообщение о том, какие сервисы удаляются в данный момент (значение переменной OLD). 40 | echo Удаляю сервисы ${OLD} 41 | # Эта команда использует docker-compose для принудительного удаления сервиса "web" с именем, соответствующим значению переменной OLD. 42 | docker compose rm -f \ 43 | web-${OLD} 44 | 45 | # Эта строка выводит сообщение о выводе списка всех контейнеров. 46 | echo Список контейнеров 47 | docker compose ps -a 48 | 49 | # Эта команда выводит сообщение о том, что будут выведены журналы запуска для сервиса "web" с именем, 50 | # соответствующим значению переменной NEW, и затем выводит эти журналы. 51 | echo Журналы запуска web-${NEW} 52 | docker compose logs web-${NEW} 53 | -------------------------------------------------------------------------------- /docker/blue-green-deployment/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | # Сервис для развертывания приложения с профилем "blue" 3 | web-blue: &web 4 | build: 5 | context: . 6 | environment: 7 | - VIRTUAL_HOST=web # Виртуальный хост для NGINX 8 | - VIRTUAL_PORT=8080 # Виртуальный порт для NGINX 9 | expose: 10 | - 8080 11 | deploy: 12 | resources: 13 | limits: 14 | cpus: '0.5' # Лимитированный доступ к ресурсам CPU 15 | memory: 32M # Лимит памяти 16 | restart: always # Перезапускать сервис при падении 17 | profiles: 18 | - blue 19 | 20 | # Сервис для развертывания приложения с профилем "green" 21 | web-green: 22 | <<: *web 23 | # Используем настройки из сервиса web-blue 24 | profiles: 25 | - green 26 | 27 | # NGINX-прокси 28 | nginx-proxy: 29 | image: nginxproxy/nginx-proxy:1.6.4-alpine 30 | ports: 31 | - 8080:8080 # Проксируем порт 8080 на хосте 32 | healthcheck: 33 | # Периодичность проверки состояния (5 секунд) 34 | interval: 5s 35 | # Максимальное время ожидания ответа (5 секунд) 36 | timeout: 5s 37 | # Количество попыток в случае неудачной проверки (5 попыток) 38 | retries: 5 39 | # Время ожидания перед началом проверок (3 секунды) 40 | start_period: 5s 41 | # Команда для выполнения теста 42 | test: curl -f http://localhost:8080/ || exit 1 43 | volumes: 44 | # Монтируем сокет Docker 45 | - /var/run/docker.sock:/tmp/docker.sock:ro 46 | # Монтируем шаблон NGINX 47 | - ./nginx.tmpl:/app/nginx.tmpl:ro 48 | deploy: 49 | resources: 50 | limits: 51 | cpus: '0.1' # Лимитированный доступ к ресурсам CPU 52 | memory: 128M # Лимит памяти 53 | restart: always 54 | profiles: 55 | - blue 56 | - green 57 | -------------------------------------------------------------------------------- /docker/blue-green-deployment/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | // Обработчик запросов 11 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 12 | // Получаем имя хоста 13 | hostname, err := os.Hostname() 14 | if err != nil { 15 | http.Error(w, err.Error(), http.StatusInternalServerError) 16 | return 17 | } 18 | 19 | // Отправляем имя хоста в ответ 20 | fmt.Fprintf(w, "Имя узла: %s\n", hostname) 21 | }) 22 | 23 | // Запуск веб-сервера на порту 8080 24 | fmt.Println("Сервер запущен на порту 8080...") 25 | if err := http.ListenAndServe(":8080", nil); err != nil { 26 | fmt.Printf("Ошибка запуска сервера: %s\n", err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docker/blue-green-deployment/nginx.tmpl: -------------------------------------------------------------------------------- 1 | {{ range $host, $containers := groupBy $ "Env.VIRTUAL_HOST" }} 2 | upstream {{ $host }} { 3 | 4 | {{ range $index, $value := $containers }} 5 | {{ with $address := index $value.Addresses 0 }} 6 | server {{ $value.Hostname }}:{{ $address.Port }}; 7 | {{ end }} 8 | {{ end }} 9 | 10 | } 11 | 12 | # конфигурация веб-сервера 13 | server { 14 | listen 8080; 15 | 16 | server_tokens off; 17 | 18 | location / { 19 | proxy_pass http://{{ $host }}; 20 | } 21 | } 22 | {{ end }} 23 | -------------------------------------------------------------------------------- /docker/uv/.dockerignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # poetry 102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 106 | #poetry.lock 107 | 108 | # pdm 109 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 110 | #pdm.lock 111 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 112 | # in version control. 113 | # https://pdm.fming.dev/#use-with-ide 114 | .pdm.toml 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | 153 | # pytype static type analyzer 154 | .pytype/ 155 | 156 | # Cython debug symbols 157 | cython_debug/ 158 | 159 | # PyCharm 160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 162 | # and can be added to the global gitignore or merged into this file. For a more nuclear 163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 164 | #.idea/ 165 | 166 | ### Python Patch ### 167 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 168 | poetry.toml 169 | 170 | # ruff 171 | .ruff_cache/ 172 | 173 | # LSP config files 174 | pyrightconfig.json 175 | 176 | # End of https://www.toptal.com/developers/gitignore/api/python 177 | 178 | # Other 179 | .gitignore 180 | Dockerfile* 181 | Makefile 182 | README.md 183 | pyproject.toml 184 | -------------------------------------------------------------------------------- /docker/uv/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=python 3 | 4 | ### Python ### 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | cover/ 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | db.sqlite3-journal 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | .pybuilder/ 80 | target/ 81 | 82 | # Jupyter Notebook 83 | .ipynb_checkpoints 84 | 85 | # IPython 86 | profile_default/ 87 | ipython_config.py 88 | 89 | # pyenv 90 | # For a library or package, you might want to ignore these files since the code is 91 | # intended to run in multiple environments; otherwise, check them in: 92 | # .python-version 93 | 94 | # pipenv 95 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 96 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 97 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 98 | # install all needed dependencies. 99 | #Pipfile.lock 100 | 101 | # poetry 102 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 103 | # This is especially recommended for binary packages to ensure reproducibility, and is more 104 | # commonly ignored for libraries. 105 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 106 | #poetry.lock 107 | 108 | # pdm 109 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 110 | #pdm.lock 111 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 112 | # in version control. 113 | # https://pdm.fming.dev/#use-with-ide 114 | .pdm.toml 115 | 116 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 117 | __pypackages__/ 118 | 119 | # Celery stuff 120 | celerybeat-schedule 121 | celerybeat.pid 122 | 123 | # SageMath parsed files 124 | *.sage.py 125 | 126 | # Environments 127 | .env 128 | .venv 129 | env/ 130 | venv/ 131 | ENV/ 132 | env.bak/ 133 | venv.bak/ 134 | 135 | # Spyder project settings 136 | .spyderproject 137 | .spyproject 138 | 139 | # Rope project settings 140 | .ropeproject 141 | 142 | # mkdocs documentation 143 | /site 144 | 145 | # mypy 146 | .mypy_cache/ 147 | .dmypy.json 148 | dmypy.json 149 | 150 | # Pyre type checker 151 | .pyre/ 152 | 153 | # pytype static type analyzer 154 | .pytype/ 155 | 156 | # Cython debug symbols 157 | cython_debug/ 158 | 159 | # PyCharm 160 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 161 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 162 | # and can be added to the global gitignore or merged into this file. For a more nuclear 163 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 164 | #.idea/ 165 | 166 | ### Python Patch ### 167 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 168 | poetry.toml 169 | 170 | # ruff 171 | .ruff_cache/ 172 | 173 | # LSP config files 174 | pyrightconfig.json 175 | 176 | # End of https://www.toptal.com/developers/gitignore/api/python 177 | -------------------------------------------------------------------------------- /docker/uv/Dockerfile: -------------------------------------------------------------------------------- 1 | # Базовый образ 2 | FROM python:3.13-alpine 3 | 4 | # Устанавливаем переменные окружения 5 | ENV PYTHONDONTWRITEBYTECODE=1 \ 6 | PYTHONUNBUFFERED=1 \ 7 | WORKDIR_PATH="/app" \ 8 | VENV_PATH="/opt/venv" 9 | 10 | # Определяем аргументы для пользователя и группы 11 | ARG USERNAME=appuser 12 | ARG GROUPNAME=appgroup 13 | 14 | # Создаем пользователя и рабочую директорию 15 | RUN addgroup --system ${GROUPNAME} \ 16 | && adduser --system --ingroup ${GROUPNAME} ${USERNAME} \ 17 | && mkdir -p ${WORKDIR_PATH} ${VENV_PATH} \ 18 | && chown -R ${USERNAME}:${GROUPNAME} ${WORKDIR_PATH} ${VENV_PATH} 19 | 20 | # Устанавливаем рабочую директорию 21 | WORKDIR ${WORKDIR_PATH} 22 | 23 | # Копируем uv чрезвычайно быстрый менеджер пакетов и проектов Python 24 | COPY --from=ghcr.io/astral-sh/uv:0.5 /uv /uvx /bin/ 25 | 26 | # Создаем виртуальное окружение и настраиваем PATH 27 | RUN uv venv ${VENV_PATH} 28 | ENV PATH="${VENV_PATH}/bin:$PATH" 29 | 30 | # Копируем requirements, устанавливаем зависимости в venv и устанавливаем права 31 | COPY --chown=${USERNAME}:${GROUPNAME} ./requirements.txt ./requirements.txt 32 | RUN uv pip install --no-cache --require-hashes --requirements ./requirements.txt \ 33 | && chown -R ${USERNAME}:${GROUPNAME} ${VENV_PATH} 34 | 35 | # Копируем остальные файлы приложения 36 | COPY --chown=${USERNAME}:${GROUPNAME} ./ ./ 37 | 38 | # Запускаем контейнер от имени нового пользователя 39 | USER ${USERNAME}:${GROUPNAME} 40 | 41 | # Настраиваем команду запуска 42 | ENTRYPOINT ["uvicorn", "main:app"] 43 | CMD [ "--host", "0.0.0.0", "--port", "8000" ] 44 | -------------------------------------------------------------------------------- /docker/uv/Dockerfile.pip: -------------------------------------------------------------------------------- 1 | # Базовый образ 2 | FROM python:3.13-alpine 3 | 4 | # Устанавливаем переменные окружения 5 | ENV PYTHONDONTWRITEBYTECODE=1 \ 6 | PYTHONUNBUFFERED=1 \ 7 | WORKDIR_PATH="/app" \ 8 | VENV_PATH="/opt/venv" 9 | 10 | # Определяем аргументы для пользователя и группы 11 | ARG USERNAME=appuser 12 | ARG GROUPNAME=appgroup 13 | 14 | # Создаем пользователя и рабочую директорию 15 | RUN addgroup --system ${GROUPNAME} \ 16 | && adduser --system --ingroup ${GROUPNAME} ${USERNAME} \ 17 | && mkdir -p ${WORKDIR_PATH} ${VENV_PATH} \ 18 | && chown -R ${USERNAME}:${GROUPNAME} ${WORKDIR_PATH} ${VENV_PATH} 19 | 20 | # Устанавливаем рабочую директорию 21 | WORKDIR ${WORKDIR_PATH} 22 | 23 | # Создаем виртуальное окружение и настраиваем PATH 24 | RUN python -m venv ${VENV_PATH} 25 | ENV PATH="${VENV_PATH}/bin:$PATH" 26 | 27 | # Копируем requirements, устанавливаем зависимости в venv и устанавливаем права 28 | COPY --chown=${USERNAME}:${GROUPNAME} ./requirements.txt ./requirements.txt 29 | RUN pip install --no-cache --require-hashes -r ./requirements.txt \ 30 | && chown -R ${USERNAME}:${GROUPNAME} ${VENV_PATH} 31 | 32 | # Копируем остальные файлы приложения 33 | COPY --chown=${USERNAME}:${GROUPNAME} ./ ./ 34 | 35 | # Запускаем контейнер от имени нового пользователя 36 | USER ${USERNAME}:${GROUPNAME} 37 | 38 | # Настраиваем команду запуска 39 | ENTRYPOINT ["uvicorn", "main:app"] 40 | CMD [ "--host", "0.0.0.0", "--port", "8000" ] 41 | -------------------------------------------------------------------------------- /docker/uv/Makefile: -------------------------------------------------------------------------------- 1 | reqs: 2 | rm ./requirements.txt || true 3 | uv pip compile --generate-hashes pyproject.toml --output-file ./requirements.txt 4 | 5 | venv: 6 | uv venv --python 3.13 7 | uv pip install --require-hashes --requirements ./requirements.txt 8 | . .venv/bin/activate 9 | 10 | build: 11 | time docker build --no-cache --file Dockerfile -t uv . 12 | docker images 13 | 14 | build-pip: 15 | time docker build --no-cache --file Dockerfile.pip -t pip . 16 | docker images 17 | 18 | outdated: 19 | uv pip list --outdated 20 | -------------------------------------------------------------------------------- /docker/uv/README.md: -------------------------------------------------------------------------------- 1 | # uv 2 | 3 | Чрезвычайно быстрый менеджер пакетов и проектов Python, написанный на Rust. 4 | 5 | Один инструмент для замены **pip**, **pip-tools**, **pipx**, **poetry**, **pyenv**, **twine**, **virtualenv**, и многое другое. 6 | 7 | [Официальный GitHub репозиторий](https://github.com/astral-sh/uv) 8 | 9 | ## Как работаь с **uv** 10 | 11 | [Установка **UV**](https://github.com/astral-sh/uv#installation) 12 | 13 | - Создание окружения 14 | 15 | ```sh 16 | make venv 17 | ``` 18 | 19 | - Обновление библиотек 20 | 21 | Обноваляем версии зависимостей в **pyproject.toml** и запускаем 22 | 23 | ```sh 24 | make reqs 25 | ``` 26 | 27 | Сборка образа с **uv** без кэша 28 | 29 | ```sh 30 | make build 31 | ``` 32 | 33 | Сборка образа с **pip** без кэша 34 | 35 | ```sh 36 | make build-pip 37 | ``` 38 | -------------------------------------------------------------------------------- /docker/uv/main.py: -------------------------------------------------------------------------------- 1 | from fastapi import FastAPI 2 | from pydantic import BaseModel 3 | 4 | app = FastAPI() 5 | 6 | class Item(BaseModel): 7 | name: str 8 | price: float 9 | 10 | @app.get("/") 11 | async def root(): 12 | return {"message": "Hello World"} 13 | 14 | @app.post("/items/") 15 | async def create_item(item: Item): 16 | return item 17 | -------------------------------------------------------------------------------- /docker/uv/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | authors = [{name = "Fedor Batonogov", email = "f.batonogov@yandex.ru"}] 3 | dependencies = [ 4 | "aioboto3==13.3.0", 5 | "aiobotocore==2.16.0", 6 | "aiofiles==24.1.0", 7 | "aiohappyeyeballs==2.4.4", 8 | "aiohttp==3.11.11", 9 | "aioitertools==0.12.0", 10 | "aiosignal==1.3.2", 11 | "annotated-types==0.7.0", 12 | "anyio==4.7.0", 13 | "asgi-lifespan==2.1.0", 14 | "asyncio==3.4.3", 15 | "attrs==24.3.0", 16 | "autoflake==2.3.1", 17 | "black==24.10.0", 18 | "boto3==1.35.*", 19 | "botocore==1.35.*", 20 | "certifi==2024.12.14", 21 | "cfgv==3.4.0", 22 | "charset-normalizer==3.4.1", 23 | "click==8.1.8", 24 | "contextvars==2.4", 25 | "distlib==0.3.9", 26 | "fastapi==0.115.6", 27 | "filelock==3.16.1", 28 | "flake8==7.1.1", 29 | "frozenlist==1.5.0", 30 | "h11==0.14.0", 31 | "httpcore==1.0.7", 32 | "httpx==0.28.1", 33 | "identify==2.6.*", 34 | "idna==3.10", 35 | "immutables==0.21", 36 | "iniconfig==2.0.0", 37 | "jmespath==1.0.1", 38 | "logging==0.4.9.6", 39 | "loguru==0.7.3", 40 | "mccabe==0.7.0", 41 | "multidict==6.1.0", 42 | "multipart==1.2.1", 43 | "mypy-extensions==1.0.0", 44 | "nodeenv==1.9.1", 45 | "packaging==24.2", 46 | "pathspec==0.12.1", 47 | "platformdirs==4.3.6", 48 | "pluggy==1.5.0", 49 | "pre_commit==4.0.1", 50 | "propcache==0.2.1", 51 | "pycodestyle==2.12.1", 52 | "pydantic==2.10.4", 53 | "pydantic-settings==2.7.1", 54 | "pydantic-core==2.27.2", 55 | "pyflakes==3.2.0", 56 | "pytest==8.3.4", 57 | "pytest-asyncio==0.25.1", 58 | "pytest-mock==3.14.0", 59 | "python-dateutil==2.9.0.post0", 60 | "python-dotenv==1.0.1", 61 | "python-multipart==0.0.20", 62 | "PyYAML==6.0.2", 63 | "requests==2.32.3", 64 | "s3transfer==0.10.4", 65 | "six==1.17.0", 66 | "sniffio==1.3.1", 67 | "starlette==0.41.*", 68 | "typing_extensions==4.12.2", 69 | "urllib3==2.3.0 ", 70 | "uvicorn==0.34.0", 71 | "virtualenv==20.28.1", 72 | "wrapt==1.17.*", 73 | "yarl==1.18.3" 74 | ] 75 | dynamic = ["version"] 76 | name = "uv" 77 | requires-python = ">=3.13" 78 | -------------------------------------------------------------------------------- /kubeadm/README.md: -------------------------------------------------------------------------------- 1 | # Настройка kubernetes кластера после чистой установки при помощи **kubeadm** 2 | 3 | ## Создание ВМ 4 | 5 | Наш кластер работает на 6 виртуальных машинах (3 control-plane и 3 node) и разворачивается с помощью [OpenTofu](../opentofu/kubeadm). 6 | 7 | ## Подготовка ВМ 8 | 9 | Тестирование проводилось на **Ubuntu 24.04** 10 | 11 | Подготавливаем ВМ при помощи [плейбука](../ansible/kubeadm.yml) 12 | 13 | Плейбук настроит все что нужно для работы **Kubernetes**. 14 | Добавит необходимые **модули ядра**, установит **kubelet**, **kubeadm**, **kubectl**, **cri-o**... и перезапустит машинки если это необходимо. 15 | Также произойдет инициализация кластера в HA режиме без **kube-proxy**. 16 | 17 | Запускаем плейбук 18 | 19 | ```sh 20 | ansible-playbook kubeadm.yml 21 | ``` 22 | 23 | Результат работы плейбука 24 | 25 | ```output 26 | PLAY RECAP ******************************************************************************************************* 27 | kubeadm-cp-01 : ok=28 changed=23 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 28 | kubeadm-cp-02 : ok=22 changed=17 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 29 | kubeadm-cp-03 : ok=22 changed=17 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 30 | kubeadm-node-01 : ok=13 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 31 | kubeadm-node-02 : ok=13 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 32 | kubeadm-node-03 : ok=13 changed=11 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 33 | 34 | четверг 02 мая 2024 19:33:56 +0500 (0:00:02.841) 0:04:44.638 *********** 35 | =============================================================================== 36 | Добавляю control-plane узлы в кластер ------------------------------------------------ 67.74s 37 | Инициализирую высокодоступный кластер ------------------------------------------------ 50.23s 38 | Устанавливаю пакеты kubelet, kubeadm, kubectl и cri-o -------------------------------- 44.44s 39 | Добавляю репозитории Kubernetes и cri-o ---------------------------------------------- 14.60s 40 | Устанавливаю нужные пакеты ----------------------------------------------------------- 13.38s 41 | Добавляю node узлы в кластер --------------------------------------------------------- 11.06s 42 | Устанавливаю пакеты ------------------------------------------------------------------- 7.83s 43 | Предотвращаю обновление kubelet, kubeadm и kubectl ------------------------------------ 4.89s 44 | Добавляю gpg ключ для репозиториев Kubernetes и cri-o --------------------------------- 4.74s 45 | haproxy_static_pods : Наливаю конфигурацию keepalived --------------------------------- 4.62s 46 | haproxy_static_pods : Создать директории /etc/kubernetes/manifests и /etc/keepalived -- 4.48s 47 | Gathering Facts ----------------------------------------------------------------------- 4.44s 48 | Включаю и запускаю службы kubelet и cri-o --------------------------------------------- 4.30s 49 | upgrade_packages : Обновляю все пакеты до актуальных версий --------------------------- 4.20s 50 | Gathering Facts ----------------------------------------------------------------------- 3.70s 51 | haproxy_static_pods : Наливаю check_apiserver.sh -------------------------------------- 3.52s 52 | haproxy_static_pods : Наливаю haproxy.cfg --------------------------------------------- 3.31s 53 | haproxy_static_pods : Наливаю haproxy static pods manifest ---------------------------- 3.31s 54 | haproxy_static_pods : Наливаю keepalived static pods manifest ------------------------- 3.23s 55 | Добавляю модули br_netfilter и overlay ------------------------------------------------ 3.21s 56 | ``` 57 | 58 | Если все прошло успешно, то можно перейти к [настройке CNI](README.md#%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-cni). 59 | 60 | ## Инициализация кластера 61 | 62 | Первый вариант подходит для тестирования, или создания не отказоустоичивого кластера, 63 | Второй вариант создаст отказоустойчивый кластер готовый для прода. 64 | Ansible Playbook создает второй вариант. 65 | 66 | Дальше описаны варианты для понимания работы. 67 | 68 | ### Non HA (Подходит для тестирования, не рекомендуется для прода) 69 | 70 | Для инициализации кластера запускаем на **control-plane** машинке команду 71 | 72 | ```sh 73 | sudo kubeadm init --pod-network-cidr=10.244.0.0/16 74 | ``` 75 | 76 | Команда инициализирует кластер Kubernetes с указанием диапазона подсети для сети плагина CNI (Container Network Interface). 77 | В результате выполнения вы увидите лог выполнения и получите краткую инструкцию по дальнейшей работе. 78 | 79 | В результате мы должны получить что-то подобное 80 | 81 | ```sh 82 | kubectl get nodes -o wide 83 | ``` 84 | 85 | ```output 86 | NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME 87 | kubeadm-cp-01 Ready control-plane 2m9s v1.30.0 10.0.70.70 Ubuntu 22.04.4 LTS 5.15.0-105-generic cri-o://1.30.0 88 | kubeadm-node-01 Ready 27s v1.30.0 10.0.70.71 Ubuntu 22.04.4 LTS 5.15.0-105-generic cri-o://1.30.0 89 | kubeadm-node-02 Ready 13s v1.30.0 10.0.70.77 Ubuntu 22.04.4 LTS 5.15.0-105-generic cri-o://1.30.0 90 | ``` 91 | 92 | ### HA (Production Ready решение, рекомендуется) 93 | 94 | Мы разместим **haproxy** и **keepalived** на **control-plane** узлах. Конфигурации налиты при помощи Ansible на этапе подготовки. 95 | Наш виртуальный IP: **10.0.70.85:8888** для взаимодействия с **Kube Api Server** 96 | 97 | Обратите внимание, что мы не устанавливаем **kube-proxy**, для этого мы указали ключ **--skip-phases=addon/kube-proxy**, 98 | этот вариант подходит при использовании **Kubernetes** без **kube-proxy** и позволяет использовать **Cilium** для его полной замены. 99 | 100 | Если вы планируете использовать **Flannel**, нужно оставить **kube-proxy**. 101 | 102 | ```sh 103 | sudo kubeadm init \ 104 | --pod-network-cidr=10.244.0.0/16 \ 105 | --control-plane-endpoint=10.0.70.85:8888 \ 106 | --upload-certs \ 107 | --skip-phases=addon/kube-proxy 108 | ``` 109 | 110 | При успешном выполнении мы увидим примерно следующее 111 | 112 | ```output 113 | Your Kubernetes control-plane has initialized successfully! 114 | 115 | To start using your cluster, you need to run the following as a regular user: 116 | 117 | mkdir -p $HOME/.kube 118 | sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 119 | sudo chown $(id -u):$(id -g) $HOME/.kube/config 120 | 121 | Alternatively, if you are the root user, you can run: 122 | 123 | export KUBECONFIG=/etc/kubernetes/admin.conf 124 | 125 | You should now deploy a pod network to the cluster. 126 | Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: 127 | https://kubernetes.io/docs/concepts/cluster-administration/addons/ 128 | 129 | You can now join any number of the control-plane node running the following command on each as root: 130 | 131 | kubeadm join 10.0.70.85:8888 --token r120zn.za3vq0au6kzgoepu \ 132 | --discovery-token-ca-cert-hash sha256:cacb3c674f63d7f261c4fed403f59ce6e7d4c869c3748e301c98b2b9f17f7786 \ 133 | --control-plane --certificate-key 31ba08487b6899c2ebe43dd3857e168840a98d1077a7998c79f6f4838b4c08f7 134 | 135 | Please note that the certificate-key gives access to cluster sensitive data, keep it secret! 136 | As a safeguard, uploaded-certs will be deleted in two hours; If necessary, you can use 137 | "kubeadm init phase upload-certs --upload-certs" to reload certs afterward. 138 | 139 | Then you can join any number of worker nodes by running the following on each as root: 140 | 141 | kubeadm join 10.0.70.85:8888 --token r120zn.za3vq0au6kzgoepu \ 142 | --discovery-token-ca-cert-hash sha256:cacb3c674f63d7f261c4fed403f59ce6e7d4c869c3748e301c98b2b9f17f7786 143 | ``` 144 | 145 | Добавляем узлы и проверяем. 146 | 147 | В результате мы должны получить что-то подобное 148 | 149 | ```sh 150 | kubectl get nodes -o wide 151 | ``` 152 | 153 | ```output 154 | NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME 155 | kubeadm-cp-01 Ready control-plane 46m v1.30.0 10.0.70.70 Ubuntu 24.04 LTS 6.8.0-31-generic cri-o://1.31.0 156 | kubeadm-cp-02 Ready control-plane 45m v1.30.0 10.0.70.78 Ubuntu 24.04 LTS 6.8.0-31-generic cri-o://1.31.0 157 | kubeadm-cp-03 Ready control-plane 45m v1.30.0 10.0.70.79 Ubuntu 24.04 LTS 6.8.0-31-generic cri-o://1.31.0 158 | kubeadm-node-01 Ready 45m v1.30.0 10.0.70.71 Ubuntu 24.04 LTS 6.8.0-31-generic cri-o://1.31.0 159 | kubeadm-node-02 Ready 45m v1.30.0 10.0.70.77 Ubuntu 24.04 LTS 6.8.0-31-generic cri-o://1.31.0 160 | kubeadm-node-03 Ready 45m v1.30.0 10.0.70.74 Ubuntu 24.04 LTS 6.8.0-31-generic cri-o://1.31.0 161 | ``` 162 | 163 | ## Настройка CNI 164 | 165 | **CNI (Container Network Interface)** - это спецификация, которая определяет, как контейнеры в сети взаимодействуют друг с другом и с внешним миром. 166 | Она позволяет плагинам сети в Kubernetes управлять сетевыми настройками контейнеров. 167 | 168 | ### Cilium 169 | 170 | **Cilium** - это проект с открытым исходным кодом для обеспечения сетевого взаимодействия, безопасности и наблюдаемости для облачных сред, 171 | таких как кластеры **Kubernetes** и другие платформы для оркестровки контейнеров. 172 | 173 | В основе **Cilium** лежит новая технология ядра **Linux** под названием **eBPF**, 174 | которая позволяет динамически вставлять в ядро Linux мощную логику управления безопасностью, видимостью и сетями. 175 | **eBPF** используется для обеспечения высокопроизводительных сетей, мультикластерных и мультиоблачных возможностей, 176 | расширенной балансировки нагрузки, прозрачного шифрования, широких возможностей сетевой безопасности, прозрачной наблюдаемости и многого другого. 177 | 178 | Есть несколько вариантов установки **Cilium** 179 | 180 | #### **Cilium CLI** 181 | 182 | Установите последнюю версию **Cilium CLI**. **Cilium CLI** можно использовать для установки **Cilium**, проверки состояния установки **Cilium**, 183 | а также для включения/выключения различных функций (например, кластерной сетки, Hubble). 184 | 185 | ```sh 186 | brew install cilium-cli 187 | ``` 188 | 189 | Установите Cilium в кластер Kubernetes, на который указывает ваш текущий контекст kubectl: 190 | 191 | ```sh 192 | cilium install --version 1.16.0 193 | ``` 194 | 195 | #### **Helm Chart** 196 | 197 | Установка Cilium на кластер без kube-proxy 198 | 199 | ```sh 200 | helm upgrade cilium cilium/cilium --version 1.16.0 \ 201 | --namespace kube-system \ 202 | --install \ 203 | --values cilium/values.yaml 204 | ``` 205 | 206 | После развертывания **Cilium** с помощью приведенного выше руководства мы можем сначала убедиться, что агент **Cilium** работает в нужном режиме: 207 | 208 | ```sh 209 | kubectl -n kube-system exec ds/cilium -- cilium-dbg status | grep KubeProxyReplacement 210 | ``` 211 | 212 | Для получения подробной информации используйте --verbose: 213 | 214 | ```sh 215 | kubectl -n kube-system exec ds/cilium -- cilium-dbg status --verbose 216 | ``` 217 | 218 | ```output 219 | KubeProxyReplacement Details: 220 | Status: True 221 | Socket LB: Enabled 222 | Socket LB Tracing: Enabled 223 | Socket LB Coverage: Full 224 | Devices: eth0 10.0.75.83 fe80::be24:11ff:fee9:819e (Direct Routing) 225 | Mode: SNAT 226 | Backend Selection: Random 227 | Session Affinity: Enabled 228 | Graceful Termination: Enabled 229 | NAT46/64 Support: Disabled 230 | XDP Acceleration: Disabled 231 | Services: 232 | - ClusterIP: Enabled 233 | - NodePort: Enabled (Range: 30000-32767) 234 | - LoadBalancer: Enabled 235 | - externalIPs: Enabled 236 | - HostPort: Enabled 237 | ``` 238 | 239 | Больше информации о работе без **kube-proxy** в [официальной документации](https://docs.cilium.io/en/stable/network/kubernetes/kubeproxy-free/). 240 | 241 | Проверяем статус установки 242 | 243 | ```sh 244 | cilium status --wait 245 | ``` 246 | 247 | Мы должны увидеть что-то подобное 248 | 249 | ```output 250 | /¯¯\ 251 | /¯¯\__/¯¯\ Cilium: OK 252 | \__/¯¯\__/ Operator: OK 253 | /¯¯\__/¯¯\ Envoy DaemonSet: disabled (using embedded mode) 254 | \__/¯¯\__/ Hubble Relay: disabled 255 | \__/ ClusterMesh: disabled 256 | 257 | DaemonSet cilium Desired: 6, Ready: 6/6, Available: 6/6 258 | Deployment cilium-operator Desired: 2, Ready: 2/2, Available: 2/2 259 | Containers: cilium Running: 6 260 | cilium-operator Running: 2 261 | Cluster Pods: 2/2 managed by Cilium 262 | Helm chart version: 263 | Image versions cilium quay.io/cilium/cilium:v1.15.6@sha256:6aa840986a3a9722cd967ef63248d675a87add7e1704740902d5d3162f0c0def: 6 264 | cilium-operator quay.io/cilium/operator-generic:v1.15.6@sha256:5789f0935eef96ad571e4f5565a8800d3a8fbb05265cf6909300cd82fd513c3d: 2 265 | ``` 266 | 267 | Выполните следующую команду, чтобы убедиться, что ваш кластер имеет правильное сетевое подключение (опционально): 268 | 269 | ```sh 270 | cilium connectivity test 271 | ``` 272 | 273 | Пример вывода 274 | 275 | ```output 276 | ✅ [cilium-test] All 52 tests (600 actions) successful, 28 tests skipped, 0 scenarios skipped. 277 | ``` 278 | 279 | Можно удалить неймспейс **cilium-test** 280 | 281 | ```sh 282 | kubectl delete namespaces cilium-test 283 | ``` 284 | 285 | ## Установка тестового приложения 286 | 287 | Для проверки работоспособности кластера установим **kube-prometheus-stack** при помощи **helm** 288 | 289 | ```sh 290 | helm upgrade \ 291 | --install \ 292 | --namespace monitoring \ 293 | --create-namespace \ 294 | kube-prometheus-stack \ 295 | prometheus-community/kube-prometheus-stack \ 296 | --values ./kube-prometheus-stack/values.yaml \ 297 | --version 61.3.2 298 | ``` 299 | 300 | Проверяем, что все запустилось 301 | 302 | ```sh 303 | kubectl get pods -o wide -n monitoring 304 | ``` 305 | 306 | Должны увидеть 307 | 308 | ```output 309 | NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES 310 | alertmanager-kube-prometheus-stack-alertmanager-0 2/2 Running 0 34s 10.244.2.5 kubeadm-node-02 311 | kube-prometheus-stack-grafana-67997d8bd4-7s2fq 3/3 Running 0 50s 10.244.1.2 kubeadm-node-01 312 | kube-prometheus-stack-kube-state-metrics-6fb5dddbdb-48l45 1/1 Running 0 50s 10.244.2.3 kubeadm-node-02 313 | kube-prometheus-stack-operator-775b5fb784-gpn94 1/1 Running 0 50s 10.244.1.3 kubeadm-node-01 314 | kube-prometheus-stack-prometheus-node-exporter-9d6ks 1/1 Running 0 50s 10.0.70.71 kubeadm-node-01 315 | kube-prometheus-stack-prometheus-node-exporter-d74z8 1/1 Running 0 50s 10.0.70.77 kubeadm-node-02 316 | kube-prometheus-stack-prometheus-node-exporter-fzgq2 1/1 Running 0 50s 10.0.70.70 kubeadm-cp-01 317 | prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 34s 10.244.2.6 kubeadm-node-02 318 | ``` 319 | 320 | Зайдем на веб интерфейс графаны 321 | 322 | ```sh 323 | kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 8000:80 324 | ``` 325 | 326 | Заходим на [localhost:8000](http://localhost:8000/login) и вводим 327 | 328 | ```grafana 329 | Логин: admin 330 | Пароль: prom-operator 331 | ``` 332 | 333 | ## Настройка CSI 334 | 335 | **CSI (Container Storage Interface)** - это спецификация, которая позволяет контейнерам в **Kubernetes** взаимодействовать с различными хранилищами данных, 336 | такими как блочные и файловые системы, через стандартизированный интерфейс. Это позволяет управлять хранилищем данных более гибко и эффективно в среде контейнеров. 337 | 338 | ### **Longhorn** 339 | 340 | Для организации системы хранения в кластере, мы будем использовать **[Longhorn](https://longhorn.io/)**. 341 | 342 | **Longhorn** - это легкая, надежная и мощная система хранения распределенных блоков для **Kubernetes**. 343 | 344 | **Longhorn** реализует распределенное блочное хранилище с помощью **контейнеров** и **микросервисов**. 345 | **Longhorn** создает выделенный контроллер хранения для каждого тома блочного устройства и синхронно реплицирует том на несколько реплик, 346 | хранящихся на нескольких узлах. Контроллер хранилища и реплики сами оркеструются с помощью **Kubernetes**. 347 | 348 | #### Характеристики 349 | 350 | - **Распределенное блочное хранилище** корпоративного класса без единой точки отказа 351 | - **Инкрементный снимок** блочного хранилища 352 | - Резервное копирование на вторичное хранилище (**NFS** или **S3-совместимое** объектное хранилище), основанное на эффективном обнаружении блоков изменений 353 | - **Повторяющиеся снимки** и **резервное копирование** 354 | - **Автоматизированное обновление без сбоев**. Вы можете обновить весь стек программного обеспечения **Longhorn**, не нарушая работу томов хранения. 355 | - Интуитивно понятная приборная панель с **графическим интерфейсом** 356 | 357 | #### Пререквизиты 358 | 359 | - Кластер Kubernetes: Убедитесь, что каждый узел соответствует требованиям к установке. 360 | - Ваша рабочая станция: Установите Helm версии 3.0 или более поздней. 361 | 362 | #### Установка Longhorn 363 | 364 | Примечание: 365 | 366 | - Начальные настройки для **Longhorn** можно найти в пользовательских опциях **Helm** или отредактировав файл конфигурации развертывания. 367 | - Для **Kubernetes** \< v1.25, если в вашем кластере все еще используется контроллер допуска Pod Security Policy, установите значение helm enablePSP в true, 368 | чтобы установить ресурс longhorn-psp PodSecurityPolicy, который позволит запускать привилегированные поды **Longhorn**. 369 | 370 | #### Требования к установке 371 | 372 | Каждый узел в кластере **Kubernetes**, на котором установлен **Longhorn**, должен отвечать следующим требованиям: 373 | 374 | - Контейнерная среда выполнения, совместимая с Kubernetes (Docker v1.13+, containerd v1.3.7+ и т. д.) 375 | - Kubernetes >= v1.21 376 | - Установлен open-iscsi, и на всех узлах запущен демон iscsid. 377 | Это необходимо, поскольку Longhorn полагается на iscsiadm на узле для предоставления постоянных томов Kubernetes. Помощь в установке open-iscsi см. в [этом разделе](https://longhorn.io/docs/1.6.2/deploy/install/#installing-open-iscsi). 378 | - Поддержка RWX требует, чтобы на каждом узле был установлен клиент NFSv4. 379 | Об установке клиента NFSv4 читайте в [этом разделе](https://longhorn.io/docs/1.6.2/deploy/install/#installing-nfsv4-client). 380 | - Файловая система узла поддерживает функцию расширения файлов для хранения данных. В настоящее время мы поддерживаем: 381 | ext4 382 | XFS 383 | - Должны быть установлены bash, curl, findmnt, grep, awk, blkid, lsblk. 384 | - Распространение монтирования должно быть включено. 385 | 386 | Рабочие нагрузки Longhorn должны иметь возможность запускаться от имени root, чтобы Longhorn был развернут и работал должным образом. 387 | 388 | Этот сценарий можно использовать для проверки среды Longhorn на наличие потенциальных проблем. 389 | 390 | Минимальное рекомендуемое аппаратное обеспечение см. в руководстве по передовому опыту. 391 | 392 | 1. Добавьте репозиторий Longhorn Helm: 393 | 394 | ```sh 395 | helm repo add longhorn https://charts.longhorn.io 396 | ``` 397 | 398 | 1. Получите список последних чартов из репозитория: 399 | 400 | ```sh 401 | helm repo update 402 | ``` 403 | 404 | 1. Установите Longhorn в пространстве имен longhorn-system. 405 | 406 | ```sh 407 | helm upgrade \ 408 | --install \ 409 | longhorn longhorn/longhorn \ 410 | --namespace longhorn-system \ 411 | --create-namespace \ 412 | --version 1.6.2 413 | ``` 414 | 415 | 1. Чтобы убедиться, что развертывание прошло успешно, выполните команду: 416 | 417 | ```sh 418 | kubectl -n longhorn-system get pod 419 | ``` 420 | 421 | Результат должен выглядеть следующим образом: 422 | 423 | ```output 424 | NAME READY STATUS RESTARTS AGE 425 | csi-attacher-799967d9c-nx4f9 1/1 Running 0 3m10s 426 | csi-attacher-799967d9c-vsz7g 1/1 Running 0 3m10s 427 | csi-attacher-799967d9c-zh6vq 1/1 Running 0 3m10s 428 | csi-provisioner-58f97759c-676jf 1/1 Running 0 3m10s 429 | csi-provisioner-58f97759c-fxsb5 1/1 Running 0 3m10s 430 | csi-provisioner-58f97759c-krfs2 1/1 Running 0 3m10s 431 | csi-resizer-6c9b8598f4-dzxwp 1/1 Running 0 3m10s 432 | csi-resizer-6c9b8598f4-kkfn4 1/1 Running 0 3m10s 433 | csi-resizer-6c9b8598f4-wq47f 1/1 Running 0 3m10s 434 | csi-snapshotter-5c5f9b754d-4pdbg 1/1 Running 0 3m10s 435 | csi-snapshotter-5c5f9b754d-7n7wf 1/1 Running 0 3m10s 436 | csi-snapshotter-5c5f9b754d-q4rzx 1/1 Running 0 3m10s 437 | engine-image-ei-5cefaf2b-8j4j9 1/1 Running 0 3m14s 438 | engine-image-ei-5cefaf2b-94g2f 1/1 Running 0 3m14s 439 | engine-image-ei-5cefaf2b-g5bg5 1/1 Running 0 3m14s 440 | instance-manager-3af1ba7167264c2020df4d36d77d3905 1/1 Running 0 3m14s 441 | instance-manager-8452580cb8e2bc9ad134bb6a1c2806cc 1/1 Running 0 3m12s 442 | instance-manager-a006e8fdef719ff0b5aee753abbe1dd8 1/1 Running 0 3m14s 443 | longhorn-csi-plugin-bswvc 3/3 Running 0 3m10s 444 | longhorn-csi-plugin-fmlrd 3/3 Running 0 3m10s 445 | longhorn-csi-plugin-tpnqm 3/3 Running 0 3m10s 446 | longhorn-driver-deployer-68b5879955-7tkrs 1/1 Running 0 3m31s 447 | longhorn-manager-5psrc 1/1 Running 0 3m31s 448 | longhorn-manager-5qjbd 1/1 Running 0 3m31s 449 | longhorn-manager-rwzbb 1/1 Running 0 3m31s 450 | longhorn-ui-9ccf5c989-bxdv2 1/1 Running 0 3m31s 451 | longhorn-ui-9ccf5c989-dsvz6 1/1 Running 0 3m31s 452 | ``` 453 | 454 | 1. Чтобы включить доступ к пользовательскому интерфейсу Longhorn, необходимо настроить контроллер Ingress. 455 | 456 | По умолчанию аутентификация в пользовательском интерфейсе Longhorn не включена. 457 | Информацию о создании контроллера NGINX Ingress с базовой аутентификацией см. в [этом разделе](https://longhorn.io/docs/1.6.2/deploy/accessing-the-ui/longhorn-ingress). 458 | 459 | 1. Войдите в пользовательский интерфейс Longhorn, выполнив [следующие действия](https://longhorn.io/docs/1.6.2/deploy/accessing-the-ui). 460 | 461 | Посмотреть на веб интерфейс можно так: 462 | 463 | ```sh 464 | kubectl port-forward -n longhorn-system svc/longhorn-frontend 8000:80 465 | ``` 466 | 467 | ## Ingress Controller 468 | 469 | Ingress Controller - это компонент в Kubernetes, который управляет входящими HTTP и HTTPS запросами в кластер. 470 | Он обеспечивает балансировку нагрузки, маршрутизацию трафика и обеспечивает доступ к службам внутри кластера извне. 471 | 472 | Ingress Controller работает на уровне приложения и использует информацию из объекта Ingress, который определяет правила маршрутизации для входящего трафика. 473 | Это позволяет настраивать маршрутизацию трафика без изменения конфигурации службы или приложения. 474 | 475 | ### Установка при помощи helm 476 | 477 | Добавьте репозиторий Traefik Labs в Helm: 478 | 479 | ```sh 480 | helm repo add traefik https://traefik.github.io/charts 481 | ``` 482 | 483 | Вы можете обновить хранилище, выполнив команду: 484 | 485 | ```sh 486 | helm repo update 487 | ``` 488 | 489 | И установите его с помощью командной строки Helm: 490 | 491 | ```sh 492 | helm upgrade \ 493 | --install \ 494 | --namespace traefik \ 495 | --create-namespace \ 496 | traefik traefik/traefik \ 497 | --values traefik/values.yaml 498 | ``` 499 | 500 | ```sh 501 | helm upgrade \ 502 | --install ingress-nginx ingress-nginx \ 503 | --repo https://kubernetes.github.io/ingress-nginx \ 504 | --namespace ingress-nginx \ 505 | --create-namespace \ 506 | --set controller.kind=DaemonSet \ 507 | --set controller.hostNetwork=false \ 508 | --set controller.hostPort.enabled=false \ 509 | --set controller.service.loadBalancerIP=10.0.70.199 510 | ``` 511 | 512 | ### Проброс дашборда Traefik 513 | 514 | ```sh 515 | kubectl port-forward -n traefik $(kubectl get pods -n traefik --selector "app.kubernetes.io/name=traefik" --output=name) 9000:9000 516 | ``` 517 | 518 | Его ним можно увидеть по адресу: [127.0.0.1:9000/dashboard/](http://127.0.0.1:9000/dashboard/) 519 | 520 | ## Удаляем узел из кластера 521 | 522 | В Kubernetes, для удаления узла (node) из кластера, вы можете использовать следующую команду kubectl 523 | 524 | ```sh 525 | kubectl get nodes -o wide 526 | ``` 527 | 528 | Эти команды выполняют следующие действия: 529 | 530 | - kubectl drain - этот шаг убеждается, что все поды, которые могут быть перезапущены в другом месте, были перезапущены. 531 | Он также удаляет все пустые директории, которые были созданы подами на узле, 532 | и удаляет все поды, которые не могут быть перезапущены в другом месте (например, поды, которые имеют локальные данные). 533 | 534 | ```sh 535 | kubectl drain kubeadm-node-01 --delete-local-data --force --ignore-daemonsets 536 | ``` 537 | 538 | - kubectl delete node - этот шаг удаляет узел из кластера. 539 | Обратите внимание, что эти команды могут привести к потере данных, 540 | поэтому убедитесь, что вы понимаете, что делаете, и что у вас есть резервное копирование данных, если это необходимо. 541 | 542 | ```sh 543 | kubectl delete node kubeadm-node-01 544 | ``` 545 | 546 | ## Просмотр списка join ссылок в Kubernetes 547 | 548 | Для просмотра списка join ссылок в Kubernetes, вы можете использовать команду kubeadm token list. 549 | Эта команда отобразит список всех активных join токенов, которые могут быть использованы для присоединения новых узлов к кластеру. 550 | 551 | Вот пример использования: 552 | 553 | ```sh 554 | kubeadm token list 555 | ``` 556 | 557 | Для получения join ссылки в Kubernetes, вы можете использовать команду kubeadm token create --print-join-command. 558 | Эта команда создаст новый join токен и напечатает команду, которую вы можете использовать для присоединения новых узлов к кластеру. 559 | 560 | Вот пример использования: 561 | 562 | ```sh 563 | kubeadm token create --print-join-command 564 | ``` 565 | -------------------------------------------------------------------------------- /kubeadm/cilium/ippool.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: "cilium.io/v2alpha1" 3 | kind: CiliumLoadBalancerIPPool 4 | metadata: 5 | name: "pool" 6 | spec: 7 | blocks: 8 | - cidr: "10.0.75.199/32" 9 | -------------------------------------------------------------------------------- /kubeadm/cilium/l2-announcement-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "cilium.io/v2alpha1" 2 | kind: CiliumL2AnnouncementPolicy 3 | metadata: 4 | name: policy1 5 | spec: 6 | serviceSelector: 7 | matchLabels: 8 | color: blue 9 | nodeSelector: 10 | matchExpressions: 11 | - key: node-role.kubernetes.io/control-plane 12 | operator: DoesNotExist 13 | interfaces: 14 | - ^eth[0-9]+ 15 | externalIPs: true 16 | loadBalancerIPs: true 17 | -------------------------------------------------------------------------------- /kubeadm/cilium/values.yaml: -------------------------------------------------------------------------------- 1 | kubeProxyReplacement: true 2 | k8sServiceHost: 10.0.75.80 3 | k8sServicePort: 8888 4 | l2announcements: 5 | enabled: true 6 | devices: eth+ 7 | -------------------------------------------------------------------------------- /kubeadm/kube-prometheus-stack/values.yaml: -------------------------------------------------------------------------------- 1 | grafana: 2 | ingress: 3 | enabled: true 4 | hosts: 5 | - grafana.example.local 6 | tls: 7 | - secretName: grafana-general-tls 8 | hosts: 9 | - grafana.example.local 10 | -------------------------------------------------------------------------------- /kubeadm/test-app/deployment.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: frontend 6 | namespace: development 7 | labels: 8 | app: frontend 9 | spec: 10 | revisionHistoryLimit: 10 11 | strategy: 12 | type: RollingUpdate 13 | rollingUpdate: 14 | maxUnavailable: 1 15 | maxSurge: 1 16 | replicas: 3 17 | selector: 18 | matchLabels: 19 | app: frontend 20 | template: 21 | metadata: 22 | labels: 23 | app: frontend 24 | spec: 25 | containers: 26 | - name: frontend 27 | image: python:3.12.4-alpine 28 | command: ["python3", "-m", "http.server", "80"] 29 | imagePullPolicy: IfNotPresent 30 | resources: 31 | requests: 32 | memory: "16Mi" 33 | cpu: "10m" 34 | limits: 35 | memory: "1Gi" 36 | cpu: "1000m" 37 | -------------------------------------------------------------------------------- /kubeadm/test-app/ingress.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.k8s.io/v1 3 | kind: Ingress 4 | metadata: 5 | name: frontend 6 | namespace: development 7 | spec: 8 | # В кластере может быть несколько Ingress Controllers, мы используем NGINX 9 | ingressClassName: "traefik" 10 | tls: 11 | - hosts: 12 | - "frontend.example.local" 13 | rules: 14 | # Хост определяет правило направления траффика по доменному имени 15 | - host: "frontend.example.local" 16 | http: 17 | # Для различных путей в URL можно указать различные бэкенд-сервисы 18 | paths: 19 | - path: / 20 | pathType: Prefix 21 | backend: 22 | service: 23 | # Заранее создан сервис типа ClusterIP 24 | # Он выступает в качестве бэкенда нашего Ingress 25 | name: frontend 26 | port: 27 | # У сервиса может быть несколько портов, указываем нужный нам 28 | number: 80 29 | -------------------------------------------------------------------------------- /kubeadm/test-app/namespace.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Namespace 4 | metadata: 5 | name: development 6 | labels: 7 | name: development 8 | -------------------------------------------------------------------------------- /kubeadm/test-app/service.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: frontend 6 | namespace: development 7 | labels: 8 | app: frontend 9 | spec: 10 | type: ClusterIP 11 | ports: 12 | - port: 80 13 | protocol: TCP 14 | targetPort: 80 15 | selector: 16 | app: frontend 17 | -------------------------------------------------------------------------------- /kubeadm/traefik/values.yaml: -------------------------------------------------------------------------------- 1 | deployment: 2 | kind: DaemonSet 3 | 4 | service: 5 | labels: 6 | color: blue 7 | spec: 8 | externalTrafficPolicy: Local 9 | -------------------------------------------------------------------------------- /opentofu/README.md: -------------------------------------------------------------------------------- 1 | # **Infrastructure as Code** и системы управления конфигурацией 2 | 3 | Для развёртывания инфраструктуры согласно подходу **Infrastructure as Code**, используя **OpenTofu/Terraform** и создания необходимых ресурсов необходимо выполнить следующие шаги. 4 | 5 | --- 6 | 7 | ## Что такое OpenTofu/Terraform 8 | 9 | **Terraform** — это инструмент для создания и управления инфраструктурой. 10 | С его помощью можно создавать, обновлять и удалять любые интересующие вас ресурсы в различных облачных и не только облачных сервисах. 11 | 12 | **Terraform** позволяет описывать инфраструктуру в виде кода, что делает процесс её создания более эффективным и управляемым. 13 | Код на языке **HCL** (HashiCorp Configuration Language) описывает требуемую инфраструктуру, а **Terraform** автоматически создаёт или обновляет её. 14 | 15 | Информация об **OpenTofu** взята с [habr.com](https://habr.com/ru/companies/flant/news/762356/). 16 | 17 | 20 сентября, на сайте **Linux Foundation** появилась новость о том, что фонд принял **OpenTofu** в число своих проектов. 18 | Теперь свободный форк **Terraform** будет развиваться под управлением **Linux Foundation**, что дает ряд преимуществ: 19 | 20 | - Он будет всегда **Open Source** — то есть соответствовать **Open Source Definition**, а не размытому определению «открытые исходники». 21 | - Он будет управляться открытым сообществом, а значит, прозрачно реализовывать и отражать видение разных разработчиков, а не единственного вендора. 22 | - Он будет беспристрастным — то есть не зависящим от прихотей одной компании. 23 | 24 | Кроме того, сами создатели открытого форка **Terraform** отмечают еще две особенности, которые повлечет за собой принятие проекта в **Linux Foundation**: 25 | обратная совместимость и хорошо проработанная модульная архитектура. 26 | 27 | Официальный сайт проекта [OpenTofu](https://opentofu.org/). 28 | Репозиторий провайдера [bpg/terraform-provider-proxmox](https://github.com/bpg/terraform-provider-proxmox). 29 | 30 | --- 31 | 32 | ## Подготовка Proxmox 33 | 34 | ### Создание шаблона Ubuntu 22.04 35 | 36 | На узле Proxmox создаем шаблон **Cloud Init** **Ubuntu 22.04** 37 | 38 | ```sh 39 | export PROXMOX_STORAGE=proxmox-data-01 40 | apt update && apt install libguestfs-tools -y && \ 41 | wget --backups=1 https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img && \ 42 | virt-customize -a jammy-server-cloudimg-amd64.img --install qemu-guest-agent && \ 43 | qm create 2204 --name "ubuntu-22.04-cloudinit-template" --cores 2 --memory 2048 --net0 virtio,bridge=vmbr0 --scsihw virtio-scsi-pci && \ 44 | qm set 2204 --virtio0 ${PROXMOX_STORAGE}:0,import-from=/root/jammy-server-cloudimg-amd64.img && \ 45 | qm set 2204 --ide2 ${PROXMOX_STORAGE}:cloudinit && \ 46 | qm set 2204 --boot order=virtio0 && \ 47 | qm set 2204 --serial0 socket --vga serial0 && \ 48 | qm template 2204 49 | ``` 50 | 51 | --- 52 | 53 | ## Последовательность действий при работе с OpenTofu 54 | 55 | ### Инициализируем рабочий каталог, содержащий файлы конфигурации OpenTofu/Terraform 56 | 57 | Команда **tofu init** инициализирует рабочий каталог, содержащий файлы конфигурации **OpenTofu**. Это первая команда, которую следует выполнить после записи новой конфигурации **OpenTofu** или клонирования существующей из системы управления версиями. Эту команду безопасно запускать несколько раз. 58 | 59 | ```bash 60 | tofu init 61 | ``` 62 | 63 | ### Проверяем файлы конфигурации в каталоге 64 | 65 | Команда **tofu validate** проверяет файлы конфигурации в каталоге, ссылаясь только на конфигурацию и не обращаясь к каким-либо удаленным службам, таким как удаленное состояние, API-интерфейсы провайдеров и т.д. 66 | 67 | ```bash 68 | tofu validate 69 | ``` 70 | 71 | ### Создаем план 72 | 73 | Команда **tofu plan** позволяет предварительно просмотреть действия, которые **OpenTofu** предпримет для изменения вашей инфраструктуры, или сохранить предполагаемый план, который вы сможете применить позже. 74 | 75 | ```bash 76 | tofu plan 77 | ``` 78 | 79 | ### Выполняем изменения, определенные конфигурацией **OpenTofu** 80 | 81 | Команда **tofu apply** является более распространенным рабочим процессом вне автоматизации. Если вы не передадите сохраненный план команде применения, она выполнит все функции плана и предложит вам утвердить его перед внесением изменений. 82 | 83 | ```bash 84 | tofu apply 85 | ``` 86 | 87 | ### Удаляем ресурсы 88 | 89 | Команда **tofu destroy** создает план выполнения для удаления всех ресурсов, управляемых в этом проекте. 90 | 91 | ```bash 92 | tofu destroy 93 | ``` 94 | 95 | --- 96 | 97 | ## Как задавать переменные 98 | 99 | Значения переменных можно задать несколькими способами: 100 | 101 | 1. Через файл с расширением **.tfvars** (пачками) 102 | 103 | ```bash 104 | instance_zone=ru-central-b 105 | ``` 106 | 107 | По умолчанию загружаем значения из **terraform.tfvars**, но можно явно обозначить файл для загрузки: 108 | 109 | ```bash 110 | tofu apply -var-file="testing.tfvars" 111 | ``` 112 | 113 | 2. Через переменные окружения. Переменная должна начинаться с **TF*VAR***, а дальше уже имя переменной 114 | 115 | ### Для простого типа 116 | 117 | ```bash 118 | export TF_VAR_instance_zone=ru-central-d 119 | ``` 120 | 121 | ### Для составного типа 122 | 123 | ```bash 124 | export TF_VAR_instance_zone='["ru-central-a","ru-central-b"]' 125 | ``` 126 | -------------------------------------------------------------------------------- /opentofu/kubeadm/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "tofu init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.opentofu.org/bpg/proxmox" { 5 | version = "0.66.3" 6 | constraints = ">= 0.61.1" 7 | hashes = [ 8 | "h1:lOAzNYVZvq5svjoikkV0ma2jS6cW9+JBku3kgwOPtfo=", 9 | "zh:372c7e42af71ea4be52fd61a9b29caa8cff913c38c2e639d84797060f0e78f8a", 10 | "zh:45b15873f78b13051fa8eaf59bc1d480ad1feaba7074ea97fb3775787a9bdadb", 11 | "zh:50792893b1d7441e39433b10ad706a14468fb43326842b06e2bc95fb3c9801fb", 12 | "zh:591ad7b8d2d4f12d617201caf5bacddca69e68ba396e6ff60d9d1ca0ee59a6f5", 13 | "zh:8d63f1eaf8a1731abffed0ef1ce15423bd56faebb1819743884841f7f9ab4126", 14 | "zh:90400a0beb68c99e262f9a6bc93daf9dfaeefdb3af673c2a86c17853c73fa868", 15 | "zh:9c0ff725d5a0c2095144a6eeb8c98fb9a3dc5f36c80e526ad63b51ce4094973a", 16 | "zh:a099fea3db1a858fc8688bf9e711a2962ab83fbb94d6507a773239aba8985834", 17 | "zh:a2a4d184e923e5d2ad92ebc414cba87c82b3c38e4183a825fbac573f7f8f5076", 18 | "zh:be762328a2608a2bb0a0a265964af57efe403bb3b11aa0fc2863355855fc4b9f", 19 | "zh:c84c8e17dc739132f85c2041a2493f7caa1f08850c4ee427462c98552a114371", 20 | "zh:d3daa7e19371fbedc3f4ddab47feb099205c6141ebc2fa1236b36aad52173723", 21 | "zh:d64ad91e29a6291ababd9ca86b32e6a36f50b806ca1079e74005a7ca2d037a8b", 22 | "zh:dc7eb38a771762570523f01cf6ae8def5b5f8acd5e173ca06b48f4f8511b7227", 23 | "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /opentofu/kubeadm/control-plane.tf: -------------------------------------------------------------------------------- 1 | variable "cp_vms" { 2 | type = list(object({ 3 | name = string 4 | address = string 5 | node_name = string 6 | })) 7 | default = [ 8 | { 9 | name = "kubeadm-cp-01" 10 | address = "10.0.75.81/24" 11 | node_name = "pve-01" 12 | }, 13 | { 14 | name = "kubeadm-cp-02" 15 | address = "10.0.75.82/24" 16 | node_name = "pve-02" 17 | }, 18 | { 19 | name = "kubeadm-cp-03" 20 | address = "10.0.75.83/24" 21 | node_name = "pve-02" 22 | } 23 | ] 24 | } 25 | 26 | # Создание виртуальных машин 27 | resource "proxmox_virtual_environment_vm" "control-plane" { 28 | for_each = { for vm in var.cp_vms : vm.name => vm } 29 | 30 | name = each.value.name 31 | migrate = true 32 | # protection = true 33 | description = "Managed by OpenTofu" 34 | tags = ["kubeadm", "kubernetes"] 35 | on_boot = true 36 | node_name = each.value.node_name 37 | 38 | clone { 39 | vm_id = "2404" 40 | node_name = "pve-01" 41 | retries = 3 42 | } 43 | 44 | agent { 45 | enabled = true 46 | } 47 | 48 | operating_system { 49 | type = "l26" 50 | } 51 | 52 | cpu { 53 | cores = 4 54 | type = "host" 55 | numa = true 56 | } 57 | 58 | memory { 59 | dedicated = 4096 60 | } 61 | 62 | vga { 63 | memory = 4 64 | type = "serial0" 65 | } 66 | 67 | disk { 68 | size = "20" 69 | interface = "virtio0" 70 | datastore_id = "proxmox-data-01" 71 | file_format = "raw" 72 | } 73 | 74 | network_device { 75 | bridge = "vmbr0" 76 | model = "virtio" 77 | } 78 | 79 | initialization { 80 | datastore_id = "proxmox-data-01" 81 | ip_config { 82 | ipv4 { 83 | address = each.value.address 84 | gateway = "10.0.75.1" 85 | } 86 | } 87 | dns { 88 | servers = [ 89 | "10.0.75.65", 90 | "10.0.75.66" 91 | ] 92 | } 93 | user_account { 94 | username = "infra" 95 | keys = [ 96 | var.ssh_public_key 97 | ] 98 | } 99 | } 100 | } 101 | 102 | # Создание ресурсов высокой доступности 103 | # resource "proxmox_virtual_environment_haresource" "patroni" { 104 | # for_each = { for vm in var.cp_vms : vm.name => vm } 105 | 106 | # resource_id = "vm:${proxmox_virtual_environment_vm.patroni[each.key].vm_id}" 107 | # state = "started" 108 | # group = "prod" 109 | # comment = "Managed by OpenTofu" 110 | # } 111 | -------------------------------------------------------------------------------- /opentofu/kubeadm/node.tf: -------------------------------------------------------------------------------- 1 | variable "node_vms" { 2 | type = list(object({ 3 | name = string 4 | address = string 5 | node_name = string 6 | })) 7 | default = [ 8 | { 9 | name = "kubeadm-node-01" 10 | address = "10.0.75.84/24" 11 | node_name = "pve-01" 12 | }, 13 | { 14 | name = "kubeadm-node-02" 15 | address = "10.0.75.85/24" 16 | node_name = "pve-02" 17 | }, 18 | { 19 | name = "kubeadm-node-03" 20 | address = "10.0.75.86/24" 21 | node_name = "pve-02" 22 | } 23 | ] 24 | } 25 | 26 | # Создание виртуальных машин 27 | resource "proxmox_virtual_environment_vm" "node" { 28 | for_each = { for vm in var.node_vms : vm.name => vm } 29 | 30 | name = each.value.name 31 | migrate = true 32 | # protection = true 33 | description = "Managed by OpenTofu" 34 | tags = ["kubeadm", "kubernetes"] 35 | on_boot = true 36 | node_name = each.value.node_name 37 | 38 | clone { 39 | vm_id = "2404" 40 | node_name = "pve-01" 41 | retries = 3 42 | } 43 | 44 | agent { 45 | enabled = true 46 | } 47 | 48 | operating_system { 49 | type = "l26" 50 | } 51 | 52 | cpu { 53 | cores = 16 54 | type = "host" 55 | numa = true 56 | } 57 | 58 | memory { 59 | dedicated = 32768 60 | } 61 | 62 | vga { 63 | memory = 4 64 | type = "serial0" 65 | } 66 | 67 | disk { 68 | size = "20" 69 | interface = "virtio0" 70 | datastore_id = "proxmox-data-01" 71 | file_format = "raw" 72 | } 73 | 74 | network_device { 75 | bridge = "vmbr0" 76 | model = "virtio" 77 | } 78 | 79 | initialization { 80 | datastore_id = "proxmox-data-01" 81 | ip_config { 82 | ipv4 { 83 | address = each.value.address 84 | gateway = "10.0.75.1" 85 | } 86 | } 87 | dns { 88 | servers = [ 89 | "10.0.75.65", 90 | "10.0.75.66" 91 | ] 92 | } 93 | user_account { 94 | username = "infra" 95 | keys = [ 96 | var.ssh_public_key 97 | ] 98 | } 99 | } 100 | } 101 | 102 | # Создание ресурсов высокой доступности 103 | # resource "proxmox_virtual_environment_haresource" "patroni" { 104 | # for_each = { for vm in var.node_vms : vm.name => vm } 105 | 106 | # resource_id = "vm:${proxmox_virtual_environment_vm.patroni[each.key].vm_id}" 107 | # state = "started" 108 | # group = "prod" 109 | # comment = "Managed by OpenTofu" 110 | # } 111 | -------------------------------------------------------------------------------- /opentofu/kubeadm/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "bpg/proxmox" 5 | version = ">= 0.61.1" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | endpoint = var.virtual_environment_endpoint 12 | api_token = var.virtual_environment_api_token 13 | insecure = true 14 | ssh { 15 | agent = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /opentofu/kubeadm/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | virtual_environment_api_token = "root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 2 | virtual_environment_endpoint = "https://x.x.x.x:8006/" 3 | ssh_public_key = "ssh-rsa ..." 4 | -------------------------------------------------------------------------------- /opentofu/kubeadm/variables.tf: -------------------------------------------------------------------------------- 1 | variable "virtual_environment_endpoint" { 2 | type = string 3 | description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" 4 | } 5 | 6 | variable "virtual_environment_api_token" { 7 | type = string 8 | description = "The api roken the Proxmox Virtual Environment API (example: root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)" 9 | } 10 | 11 | variable "ssh_public_key" { 12 | type = string 13 | description = "SSH Puclic key for VMs (example: ssh-rsa ...)" 14 | } 15 | -------------------------------------------------------------------------------- /opentofu/minio/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "tofu init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.opentofu.org/bpg/proxmox" { 5 | version = "0.66.3" 6 | constraints = ">= 0.57.0" 7 | hashes = [ 8 | "h1:lOAzNYVZvq5svjoikkV0ma2jS6cW9+JBku3kgwOPtfo=", 9 | "zh:372c7e42af71ea4be52fd61a9b29caa8cff913c38c2e639d84797060f0e78f8a", 10 | "zh:45b15873f78b13051fa8eaf59bc1d480ad1feaba7074ea97fb3775787a9bdadb", 11 | "zh:50792893b1d7441e39433b10ad706a14468fb43326842b06e2bc95fb3c9801fb", 12 | "zh:591ad7b8d2d4f12d617201caf5bacddca69e68ba396e6ff60d9d1ca0ee59a6f5", 13 | "zh:8d63f1eaf8a1731abffed0ef1ce15423bd56faebb1819743884841f7f9ab4126", 14 | "zh:90400a0beb68c99e262f9a6bc93daf9dfaeefdb3af673c2a86c17853c73fa868", 15 | "zh:9c0ff725d5a0c2095144a6eeb8c98fb9a3dc5f36c80e526ad63b51ce4094973a", 16 | "zh:a099fea3db1a858fc8688bf9e711a2962ab83fbb94d6507a773239aba8985834", 17 | "zh:a2a4d184e923e5d2ad92ebc414cba87c82b3c38e4183a825fbac573f7f8f5076", 18 | "zh:be762328a2608a2bb0a0a265964af57efe403bb3b11aa0fc2863355855fc4b9f", 19 | "zh:c84c8e17dc739132f85c2041a2493f7caa1f08850c4ee427462c98552a114371", 20 | "zh:d3daa7e19371fbedc3f4ddab47feb099205c6141ebc2fa1236b36aad52173723", 21 | "zh:d64ad91e29a6291ababd9ca86b32e6a36f50b806ca1079e74005a7ca2d037a8b", 22 | "zh:dc7eb38a771762570523f01cf6ae8def5b5f8acd5e173ca06b48f4f8511b7227", 23 | "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /opentofu/minio/minio.tf: -------------------------------------------------------------------------------- 1 | variable "minio_vms" { 2 | type = list(object({ 3 | name = string 4 | address = string 5 | node_name = string 6 | })) 7 | default = [ 8 | { 9 | name = "minio1" 10 | address = "10.0.75.55/24" 11 | node_name = "pve-01" 12 | }, 13 | { 14 | name = "minio2" 15 | address = "10.0.75.56/24" 16 | node_name = "pve-02" 17 | }, 18 | { 19 | name = "minio3" 20 | address = "10.0.75.57/24" 21 | node_name = "pve-01" 22 | }, 23 | { 24 | name = "minio4" 25 | address = "10.0.75.58/24" 26 | node_name = "pve-02" 27 | } 28 | ] 29 | } 30 | 31 | # Создание виртуальных машин 32 | resource "proxmox_virtual_environment_vm" "minio" { 33 | for_each = { for vm in var.minio_vms : vm.name => vm } 34 | 35 | name = each.value.name 36 | migrate = true 37 | # protection = true 38 | description = "Managed by OpenTofu" 39 | tags = ["minio", "opentofu"] 40 | on_boot = true 41 | node_name = each.value.node_name 42 | 43 | clone { 44 | vm_id = "2404" 45 | node_name = "pve-01" 46 | retries = 3 47 | } 48 | 49 | agent { 50 | enabled = true 51 | } 52 | 53 | operating_system { 54 | type = "l26" 55 | } 56 | 57 | cpu { 58 | cores = 2 59 | type = "host" 60 | numa = true 61 | } 62 | 63 | memory { 64 | dedicated = 2048 65 | } 66 | 67 | vga { 68 | memory = 4 69 | type = "serial0" 70 | } 71 | 72 | disk { 73 | size = "20" 74 | interface = "virtio0" 75 | datastore_id = "proxmox-data-02" 76 | file_format = "raw" 77 | } 78 | 79 | disk { 80 | size = "450" 81 | interface = "virtio1" 82 | datastore_id = "minio-data-01" 83 | file_format = "raw" 84 | } 85 | 86 | network_device { 87 | bridge = "vmbr0" 88 | model = "virtio" 89 | } 90 | 91 | initialization { 92 | datastore_id = "proxmox-data-02" 93 | ip_config { 94 | ipv4 { 95 | address = each.value.address 96 | gateway = "10.0.75.1" 97 | } 98 | } 99 | dns { 100 | servers = [ 101 | "10.0.75.65", 102 | "10.0.75.66" 103 | ] 104 | } 105 | user_account { 106 | username = "infra" 107 | keys = [ 108 | var.ssh_public_key 109 | ] 110 | } 111 | } 112 | } 113 | 114 | # Создание ресурсов высокой доступности 115 | # resource "proxmox_virtual_environment_haresource" "patroni" { 116 | # for_each = { for vm in var.minio_vms : vm.name => vm } 117 | 118 | # resource_id = "vm:${proxmox_virtual_environment_vm.patroni[each.key].vm_id}" 119 | # state = "started" 120 | # group = "prod" 121 | # comment = "Managed by OpenTofu" 122 | # } 123 | -------------------------------------------------------------------------------- /opentofu/minio/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "bpg/proxmox" 5 | version = ">= 0.57.0" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | endpoint = var.virtual_environment_endpoint 12 | api_token = var.virtual_environment_api_token 13 | insecure = true 14 | ssh { 15 | agent = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /opentofu/minio/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | virtual_environment_api_token = "fedor@pve!opentofu=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 2 | virtual_environment_endpoint = "https://x.x.x.x:8006/" 3 | ssh_public_key = "ssh-rsa ..." 4 | -------------------------------------------------------------------------------- /opentofu/minio/variables.tf: -------------------------------------------------------------------------------- 1 | variable "virtual_environment_endpoint" { 2 | type = string 3 | description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" 4 | } 5 | 6 | variable "virtual_environment_api_token" { 7 | type = string 8 | description = "The api roken the Proxmox Virtual Environment API (example: root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)" 9 | } 10 | 11 | variable "ssh_public_key" { 12 | type = string 13 | description = "SSH Puclic key for VMs (example: ssh-rsa ...)" 14 | } 15 | -------------------------------------------------------------------------------- /opentofu/nginx/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "tofu init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.opentofu.org/bpg/proxmox" { 5 | version = "0.66.3" 6 | constraints = ">= 0.57.0" 7 | hashes = [ 8 | "h1:lOAzNYVZvq5svjoikkV0ma2jS6cW9+JBku3kgwOPtfo=", 9 | "zh:372c7e42af71ea4be52fd61a9b29caa8cff913c38c2e639d84797060f0e78f8a", 10 | "zh:45b15873f78b13051fa8eaf59bc1d480ad1feaba7074ea97fb3775787a9bdadb", 11 | "zh:50792893b1d7441e39433b10ad706a14468fb43326842b06e2bc95fb3c9801fb", 12 | "zh:591ad7b8d2d4f12d617201caf5bacddca69e68ba396e6ff60d9d1ca0ee59a6f5", 13 | "zh:8d63f1eaf8a1731abffed0ef1ce15423bd56faebb1819743884841f7f9ab4126", 14 | "zh:90400a0beb68c99e262f9a6bc93daf9dfaeefdb3af673c2a86c17853c73fa868", 15 | "zh:9c0ff725d5a0c2095144a6eeb8c98fb9a3dc5f36c80e526ad63b51ce4094973a", 16 | "zh:a099fea3db1a858fc8688bf9e711a2962ab83fbb94d6507a773239aba8985834", 17 | "zh:a2a4d184e923e5d2ad92ebc414cba87c82b3c38e4183a825fbac573f7f8f5076", 18 | "zh:be762328a2608a2bb0a0a265964af57efe403bb3b11aa0fc2863355855fc4b9f", 19 | "zh:c84c8e17dc739132f85c2041a2493f7caa1f08850c4ee427462c98552a114371", 20 | "zh:d3daa7e19371fbedc3f4ddab47feb099205c6141ebc2fa1236b36aad52173723", 21 | "zh:d64ad91e29a6291ababd9ca86b32e6a36f50b806ca1079e74005a7ca2d037a8b", 22 | "zh:dc7eb38a771762570523f01cf6ae8def5b5f8acd5e173ca06b48f4f8511b7227", 23 | "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /opentofu/nginx/nginx.tf: -------------------------------------------------------------------------------- 1 | variable "minio_vms" { 2 | type = list(object({ 3 | name = string 4 | address = string 5 | node_name = string 6 | })) 7 | default = [ 8 | { 9 | name = "nginx-01" 10 | address = "10.0.75.91/24" 11 | node_name = "pve-01" 12 | }, 13 | { 14 | name = "nginx-02" 15 | address = "10.0.75.92/24" 16 | node_name = "pve-02" 17 | } 18 | ] 19 | } 20 | 21 | # Создание виртуальных машин 22 | resource "proxmox_virtual_environment_vm" "minio" { 23 | for_each = { for vm in var.minio_vms : vm.name => vm } 24 | 25 | name = each.value.name 26 | migrate = true 27 | # protection = true 28 | description = "Managed by OpenTofu" 29 | tags = ["nignx", "opentofu"] 30 | on_boot = true 31 | node_name = each.value.node_name 32 | 33 | clone { 34 | vm_id = "2404" 35 | node_name = "pve-01" 36 | retries = 3 37 | } 38 | 39 | agent { 40 | enabled = true 41 | } 42 | 43 | operating_system { 44 | type = "l26" 45 | } 46 | 47 | cpu { 48 | cores = 1 49 | type = "host" 50 | numa = true 51 | } 52 | 53 | memory { 54 | dedicated = 1024 55 | } 56 | 57 | vga { 58 | memory = 4 59 | type = "serial0" 60 | } 61 | 62 | disk { 63 | size = "20" 64 | interface = "virtio0" 65 | datastore_id = "proxmox-data-02" 66 | file_format = "raw" 67 | } 68 | 69 | network_device { 70 | bridge = "vmbr0" 71 | model = "virtio" 72 | } 73 | 74 | initialization { 75 | datastore_id = "proxmox-data-02" 76 | ip_config { 77 | ipv4 { 78 | address = each.value.address 79 | gateway = "10.0.75.1" 80 | } 81 | } 82 | dns { 83 | servers = [ 84 | "10.0.75.65", 85 | "10.0.75.66" 86 | ] 87 | } 88 | user_account { 89 | username = "infra" 90 | keys = [ 91 | var.ssh_public_key 92 | ] 93 | } 94 | } 95 | } 96 | 97 | # Создание ресурсов высокой доступности 98 | # resource "proxmox_virtual_environment_haresource" "patroni" { 99 | # for_each = { for vm in var.minio_vms : vm.name => vm } 100 | 101 | # resource_id = "vm:${proxmox_virtual_environment_vm.patroni[each.key].vm_id}" 102 | # state = "started" 103 | # group = "prod" 104 | # comment = "Managed by OpenTofu" 105 | # } 106 | -------------------------------------------------------------------------------- /opentofu/nginx/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "bpg/proxmox" 5 | version = ">= 0.57.0" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | endpoint = var.virtual_environment_endpoint 12 | api_token = var.virtual_environment_api_token 13 | insecure = true 14 | ssh { 15 | agent = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /opentofu/nginx/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | virtual_environment_api_token = "fedor@pve!opentofu=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 2 | virtual_environment_endpoint = "https://x.x.x.x:8006/" 3 | ssh_public_key = "ssh-rsa ..." 4 | -------------------------------------------------------------------------------- /opentofu/nginx/variables.tf: -------------------------------------------------------------------------------- 1 | variable "virtual_environment_endpoint" { 2 | type = string 3 | description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" 4 | } 5 | 6 | variable "virtual_environment_api_token" { 7 | type = string 8 | description = "The api roken the Proxmox Virtual Environment API (example: root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)" 9 | } 10 | 11 | variable "ssh_public_key" { 12 | type = string 13 | description = "SSH Puclic key for VMs (example: ssh-rsa ...)" 14 | } 15 | -------------------------------------------------------------------------------- /opentofu/patroni-postgresql/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "tofu init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.opentofu.org/bpg/proxmox" { 5 | version = "0.66.3" 6 | constraints = ">= 0.60.0" 7 | hashes = [ 8 | "h1:lOAzNYVZvq5svjoikkV0ma2jS6cW9+JBku3kgwOPtfo=", 9 | "zh:372c7e42af71ea4be52fd61a9b29caa8cff913c38c2e639d84797060f0e78f8a", 10 | "zh:45b15873f78b13051fa8eaf59bc1d480ad1feaba7074ea97fb3775787a9bdadb", 11 | "zh:50792893b1d7441e39433b10ad706a14468fb43326842b06e2bc95fb3c9801fb", 12 | "zh:591ad7b8d2d4f12d617201caf5bacddca69e68ba396e6ff60d9d1ca0ee59a6f5", 13 | "zh:8d63f1eaf8a1731abffed0ef1ce15423bd56faebb1819743884841f7f9ab4126", 14 | "zh:90400a0beb68c99e262f9a6bc93daf9dfaeefdb3af673c2a86c17853c73fa868", 15 | "zh:9c0ff725d5a0c2095144a6eeb8c98fb9a3dc5f36c80e526ad63b51ce4094973a", 16 | "zh:a099fea3db1a858fc8688bf9e711a2962ab83fbb94d6507a773239aba8985834", 17 | "zh:a2a4d184e923e5d2ad92ebc414cba87c82b3c38e4183a825fbac573f7f8f5076", 18 | "zh:be762328a2608a2bb0a0a265964af57efe403bb3b11aa0fc2863355855fc4b9f", 19 | "zh:c84c8e17dc739132f85c2041a2493f7caa1f08850c4ee427462c98552a114371", 20 | "zh:d3daa7e19371fbedc3f4ddab47feb099205c6141ebc2fa1236b36aad52173723", 21 | "zh:d64ad91e29a6291ababd9ca86b32e6a36f50b806ca1079e74005a7ca2d037a8b", 22 | "zh:dc7eb38a771762570523f01cf6ae8def5b5f8acd5e173ca06b48f4f8511b7227", 23 | "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /opentofu/patroni-postgresql/etcd.tf: -------------------------------------------------------------------------------- 1 | variable "etcd_vms" { 2 | type = list(object({ 3 | name = string 4 | address = string 5 | node_name = string 6 | })) 7 | default = [ 8 | { 9 | name = "etcd-01" 10 | address = "10.0.75.114/24" 11 | node_name = "pve-01" 12 | }, 13 | { 14 | name = "etcd-02" 15 | address = "10.0.75.115/24" 16 | node_name = "pve-02" 17 | } 18 | ] 19 | } 20 | 21 | # Создание виртуальных машин 22 | resource "proxmox_virtual_environment_vm" "etcd" { 23 | for_each = { for vm in var.etcd_vms : vm.name => vm } 24 | 25 | name = each.value.name 26 | migrate = true 27 | description = "Managed by OpenTofu" 28 | tags = ["etcd"] 29 | on_boot = true 30 | node_name = each.value.node_name 31 | 32 | clone { 33 | vm_id = "2404" 34 | node_name = "pve-01" 35 | retries = 3 36 | } 37 | 38 | agent { 39 | enabled = true 40 | } 41 | 42 | operating_system { 43 | type = "l26" 44 | } 45 | 46 | cpu { 47 | cores = 2 48 | type = "host" 49 | numa = true 50 | } 51 | 52 | memory { 53 | dedicated = 2048 54 | } 55 | 56 | vga { 57 | memory = 4 58 | type = "serial0" 59 | } 60 | 61 | disk { 62 | size = "20" 63 | interface = "virtio0" 64 | datastore_id = "proxmox-data-01" 65 | file_format = "raw" 66 | } 67 | 68 | network_device { 69 | bridge = "vmbr0" 70 | model = "virtio" 71 | } 72 | 73 | initialization { 74 | datastore_id = "proxmox-data-01" 75 | ip_config { 76 | ipv4 { 77 | address = each.value.address 78 | gateway = "10.0.75.1" 79 | } 80 | } 81 | dns { 82 | servers = [ 83 | "10.0.75.65", 84 | "10.0.75.66" 85 | ] 86 | } 87 | user_account { 88 | username = "infra" 89 | keys = [ 90 | var.ssh_public_key 91 | ] 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /opentofu/patroni-postgresql/patroni-postgresql.tf: -------------------------------------------------------------------------------- 1 | variable "patroni_vms" { 2 | type = list(object({ 3 | name = string 4 | address = string 5 | node_name = string 6 | })) 7 | default = [ 8 | { 9 | name = "patroni-postgresql-01" 10 | address = "10.0.75.111/24" 11 | node_name = "pve-01" 12 | }, 13 | { 14 | name = "patroni-postgresql-02" 15 | address = "10.0.75.112/24" 16 | node_name = "pve-02" 17 | }, 18 | { 19 | name = "patroni-postgresql-03" # Исправлено имя 20 | address = "10.0.75.113/24" 21 | node_name = "pve-02" 22 | } 23 | ] 24 | } 25 | 26 | # Создание виртуальных машин 27 | resource "proxmox_virtual_environment_vm" "patroni" { 28 | for_each = { for vm in var.patroni_vms : vm.name => vm } 29 | 30 | name = each.value.name 31 | migrate = true 32 | description = "Managed by OpenTofu" 33 | tags = ["patroni", "postgresql"] 34 | on_boot = true 35 | node_name = each.value.node_name 36 | 37 | clone { 38 | vm_id = "2404" 39 | node_name = "pve-01" 40 | retries = 3 41 | } 42 | 43 | agent { 44 | enabled = true 45 | } 46 | 47 | operating_system { 48 | type = "l26" 49 | } 50 | 51 | cpu { 52 | cores = 2 53 | type = "host" 54 | numa = true 55 | } 56 | 57 | memory { 58 | dedicated = 2048 59 | } 60 | 61 | vga { 62 | memory = 4 63 | type = "serial0" 64 | } 65 | 66 | disk { 67 | size = "20" 68 | interface = "virtio0" 69 | datastore_id = "proxmox-data-01" 70 | file_format = "raw" 71 | } 72 | 73 | network_device { 74 | bridge = "vmbr0" 75 | model = "virtio" 76 | } 77 | 78 | initialization { 79 | datastore_id = "proxmox-data-01" 80 | ip_config { 81 | ipv4 { 82 | address = each.value.address 83 | gateway = "10.0.75.1" 84 | } 85 | } 86 | dns { 87 | servers = [ 88 | "10.0.75.65", 89 | "10.0.75.66" 90 | ] 91 | } 92 | user_account { 93 | username = "infra" 94 | keys = [ 95 | var.ssh_public_key 96 | ] 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /opentofu/patroni-postgresql/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "bpg/proxmox" 5 | version = ">= 0.60.0" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | endpoint = var.virtual_environment_endpoint 12 | api_token = var.virtual_environment_api_token 13 | insecure = true 14 | ssh { 15 | agent = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /opentofu/patroni-postgresql/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | virtual_environment_api_token = "root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 2 | virtual_environment_endpoint = "https://x.x.x.x:8006/" 3 | ssh_public_key = "ssh-rsa ..." 4 | -------------------------------------------------------------------------------- /opentofu/patroni-postgresql/variables.tf: -------------------------------------------------------------------------------- 1 | variable "virtual_environment_endpoint" { 2 | type = string 3 | description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" 4 | } 5 | 6 | variable "virtual_environment_api_token" { 7 | type = string 8 | description = "The api roken the Proxmox Virtual Environment API (example: root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)" 9 | } 10 | 11 | variable "ssh_public_key" { 12 | type = string 13 | description = "SSH Puclic key for VMs (example: ssh-rsa ...)" 14 | } 15 | -------------------------------------------------------------------------------- /opentofu/test_vm/.terraform.lock.hcl: -------------------------------------------------------------------------------- 1 | # This file is maintained automatically by "tofu init". 2 | # Manual edits may be lost in future updates. 3 | 4 | provider "registry.opentofu.org/bpg/proxmox" { 5 | version = "0.66.3" 6 | constraints = ">= 0.53.1" 7 | hashes = [ 8 | "h1:lOAzNYVZvq5svjoikkV0ma2jS6cW9+JBku3kgwOPtfo=", 9 | "zh:372c7e42af71ea4be52fd61a9b29caa8cff913c38c2e639d84797060f0e78f8a", 10 | "zh:45b15873f78b13051fa8eaf59bc1d480ad1feaba7074ea97fb3775787a9bdadb", 11 | "zh:50792893b1d7441e39433b10ad706a14468fb43326842b06e2bc95fb3c9801fb", 12 | "zh:591ad7b8d2d4f12d617201caf5bacddca69e68ba396e6ff60d9d1ca0ee59a6f5", 13 | "zh:8d63f1eaf8a1731abffed0ef1ce15423bd56faebb1819743884841f7f9ab4126", 14 | "zh:90400a0beb68c99e262f9a6bc93daf9dfaeefdb3af673c2a86c17853c73fa868", 15 | "zh:9c0ff725d5a0c2095144a6eeb8c98fb9a3dc5f36c80e526ad63b51ce4094973a", 16 | "zh:a099fea3db1a858fc8688bf9e711a2962ab83fbb94d6507a773239aba8985834", 17 | "zh:a2a4d184e923e5d2ad92ebc414cba87c82b3c38e4183a825fbac573f7f8f5076", 18 | "zh:be762328a2608a2bb0a0a265964af57efe403bb3b11aa0fc2863355855fc4b9f", 19 | "zh:c84c8e17dc739132f85c2041a2493f7caa1f08850c4ee427462c98552a114371", 20 | "zh:d3daa7e19371fbedc3f4ddab47feb099205c6141ebc2fa1236b36aad52173723", 21 | "zh:d64ad91e29a6291ababd9ca86b32e6a36f50b806ca1079e74005a7ca2d037a8b", 22 | "zh:dc7eb38a771762570523f01cf6ae8def5b5f8acd5e173ca06b48f4f8511b7227", 23 | "zh:f26e0763dbe6a6b2195c94b44696f2110f7f55433dc142839be16b9697fa5597", 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /opentofu/test_vm/output.tf: -------------------------------------------------------------------------------- 1 | output "vm_info" { 2 | description = "Names and IP addresses of the created VMs" 3 | value = { 4 | for i in range(var.vm_count) : 5 | format("vm-%02d", i + 1) => proxmox_virtual_environment_vm.vm[i].ipv4_addresses[1] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /opentofu/test_vm/provider.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | proxmox = { 4 | source = "bpg/proxmox" 5 | version = ">= 0.53.1" 6 | } 7 | } 8 | } 9 | 10 | provider "proxmox" { 11 | endpoint = var.virtual_environment_endpoint 12 | api_token = var.virtual_environment_api_token 13 | insecure = true 14 | ssh { 15 | agent = false 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /opentofu/test_vm/terraform.tfvars.example: -------------------------------------------------------------------------------- 1 | virtual_environment_api_token = "root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" 2 | virtual_environment_endpoint = "https://x.x.x.x:8006/" 3 | ssh_public_key = "ssh-rsa ..." 4 | -------------------------------------------------------------------------------- /opentofu/test_vm/variables.tf: -------------------------------------------------------------------------------- 1 | variable "virtual_environment_endpoint" { 2 | type = string 3 | description = "The endpoint for the Proxmox Virtual Environment API (example: https://host:port)" 4 | } 5 | 6 | variable "virtual_environment_api_token" { 7 | type = string 8 | description = "The api roken the Proxmox Virtual Environment API (example: root@pam!for-terraform-provider=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)" 9 | } 10 | 11 | variable "ssh_public_key" { 12 | type = string 13 | description = "SSH Puclic key for VMs (example: ssh-rsa ...)" 14 | } 15 | -------------------------------------------------------------------------------- /opentofu/test_vm/vm.tf: -------------------------------------------------------------------------------- 1 | variable "vm_count" { 2 | description = "Number of VMs to create" 3 | default = 1 4 | } 5 | 6 | resource "proxmox_virtual_environment_vm" "vm" { 7 | count = var.vm_count 8 | name = format("vm-%02d", count.index + 1) 9 | migrate = true 10 | description = "Managed by OpenTofu" 11 | tags = ["opentofu", "test"] 12 | on_boot = true 13 | 14 | node_name = format("pve-%02d", count.index + 1) 15 | 16 | clone { 17 | vm_id = "2404" 18 | node_name = "pve-01" 19 | retries = 2 20 | } 21 | 22 | agent { 23 | enabled = true 24 | } 25 | 26 | operating_system { 27 | type = "l26" 28 | } 29 | 30 | cpu { 31 | cores = 2 32 | type = "host" 33 | numa = true 34 | } 35 | 36 | memory { 37 | dedicated = 2048 38 | } 39 | 40 | vga { 41 | memory = 4 42 | type = "serial0" 43 | } 44 | 45 | disk { 46 | size = "40" 47 | interface = "virtio0" 48 | datastore_id = "proxmox-data-01" 49 | file_format = "raw" 50 | } 51 | 52 | network_device { 53 | bridge = "vmbr0" 54 | model = "virtio" 55 | } 56 | 57 | initialization { 58 | datastore_id = "proxmox-data-01" 59 | ip_config { 60 | ipv4 { 61 | address = "dhcp" 62 | } 63 | } 64 | dns { 65 | servers = [ 66 | "77.88.8.8", 67 | "8.8.8.8" 68 | ] 69 | } 70 | user_account { 71 | username = "infra" 72 | keys = [ 73 | var.ssh_public_key 74 | ] 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pre-commit==4.0.1 2 | -------------------------------------------------------------------------------- /talos/README.md: -------------------------------------------------------------------------------- 1 | # Talos 2 | 3 | ## Что такое Talos? 4 | 5 | [Talos Linux](https://www.talos.dev/) — это **Linux**, разработанный для [Kubernetes](https://kubernetes.io/): безопасный, неизменяемый и минималистичный. 6 | 7 | - Поддерживает **облачные платформы, «голое железо» и платформы виртуализации** 8 | - Все **управление системой осуществляется через API**. Нет SSH, оболочки или консоли 9 | - Готовность к работе: **поддерживает одни из крупнейших Kubernetes-кластеров в мире** 10 | - Проект с открытым исходным кодом от команды [Sidero Labs](https://www.siderolabs.com/) 11 | 12 | ## Почему Talos 13 | 14 | Имея меньше, Talos предлагает больше: безопасность, эффективность, устойчивость, согласованность. 15 | 16 | Все эти аспекты улучшаются благодаря простоте и минимализму. 17 | 18 | ## Загрузка Talos на физический сервер с использованием ISO 19 | 20 | Talos может быть установлен на физическую машину с использованием ISO-образа. ISO-образы для архитектур amd64 и arm64 доступны на [странице релизов Talos](https://github.com/siderolabs/talos/releases/). 21 | 22 | Talos не устанавливается на диск при загрузке с ISO до тех пор, пока не будет применена конфигурация машины. 23 | 24 | Пожалуйста, следуйте [руководству по началу работы](https://www.talos.dev/v1.9/introduction/getting-started/) для выполнения общих шагов по установке Talos. 25 | 26 | Примечание: Если на диске уже установлена Talos, при загрузке с ISO Talos загрузится с этой установленной версии. 27 | Порядок загрузки должен отдавать предпочтение диску перед ISO, либо ISO должно быть удалено после установки, чтобы Talos загружался с диска. 28 | 29 | Ознакомьтесь со справочником по [параметрам ядра](https://www.talos.dev/v1.9/reference/kernel/) для списка поддерживаемых Talos параметров ядра. 30 | 31 | Доступны два типа ISO-образов: 32 | 33 | - metal-arch.iso: поддерживает загрузку на системах с BIOS и UEFI (для x86), только UEFI для arm64. 34 | - metal-arch-secureboot.iso: поддерживает загрузку только на системах с UEFI в режиме SecureBoot (создано с помощью [Image Factory](https://www.talos.dev/v1.9/learn-more/image-factory/). 35 | 36 | ## Начало работы 37 | 38 | Руководство по настройке кластера Talos Linux. 39 | 40 | Этот документ проведет вас через процесс установки простого кластера Talos, а также объяснит некоторые ключевые концепции. 41 | 42 | Независимо от того, где вы запускаете Talos, этапы создания Kubernetes-кластера включают: 43 | 44 | 1. Загрузка машин с образа Talos Linux. 45 | 2. Определение конечной точки API Kubernetes и создание конфигурации машин. 46 | 3. Настройка Talos Linux путем применения конфигураций машин. 47 | 4. Настройка `talosctl`. 48 | 5. Запуск Kubernetes. 49 | 50 | ## Производственный кластер 51 | 52 | Для высокодоступного Kubernetes-кластера в продакшене рекомендуется использовать три узла плоскости управления. 53 | Использование пяти узлов может обеспечить большую устойчивость к сбоям, 54 | но также приводит к большему количеству операций репликации и может отрицательно сказаться на производительности. 55 | 56 | ### Layer 2 VIP (общий IP-адрес) 57 | 58 | Talos имеет встроенную поддержку работы Kubernetes через общий/виртуальный IP-адрес. 59 | Для этого требуется соединение уровня 2 (Layer 2) между узлами управляющей плоскости. 60 | 61 | Выберите неиспользуемый IP-адрес в той же подсети, что и узлы плоскости управления, для использования в качестве VIP. 62 | (Убедитесь, что выделенный адрес не используется другими устройствами и исключен из диапазона DHCP.) 63 | 64 | ### Выделение секретов 65 | 66 | При создании конфигурационных файлов для кластера Talos Linux рекомендуется начать с генерации файлы с секретами, 67 | который должен быть сохранен в безопасном месте. 68 | Этот файл можно использовать для генерации конфигураций машин или клиентов в любое время: 69 | 70 | ```sh 71 | talosctl gen secrets -o secrets.yaml 72 | ``` 73 | 74 | ### Запускаем Talos 75 | 76 | Теперь мы можем создать общую конфигурацию машин: 77 | 78 | ```sh 79 | talosctl gen config --kubernetes-version 1.31.4 --with-secrets secrets.yaml my-cluster https://172.16.61.10:6443 --config-patch @patch.yaml 80 | ``` 81 | 82 | Создаем конфигурацию для каждого узла: 83 | 84 | ```sh 85 | talosctl machineconfig patch controlplane.yaml --patch @cp0.patch --output cp0.yaml 86 | talosctl machineconfig patch controlplane.yaml --patch @cp1.patch --output cp1.yaml 87 | talosctl machineconfig patch controlplane.yaml --patch @cp2.patch --output cp2.yaml 88 | talosctl machineconfig patch worker.yaml --patch @worker0.patch --output worker0.yaml 89 | talosctl machineconfig patch worker.yaml --patch @worker1.patch --output worker1.yaml 90 | talosctl machineconfig patch worker.yaml --patch @worker2.patch --output worker2.yaml 91 | ``` 92 | 93 | ```sh 94 | talosctl apply-config --insecure -n 172.16.61.144 --file ./cp0.yaml 95 | talosctl apply-config --insecure -n 172.16.61.145 --file ./cp1.yaml 96 | talosctl apply-config --insecure -n 172.16.61.146 --file ./cp2.yaml 97 | talosctl apply-config --insecure -n 172.16.61.147 --file ./worker0.yaml 98 | talosctl apply-config --insecure -n 172.16.61.149 --file ./worker1.yaml 99 | talosctl apply-config --insecure -n 172.16.61.148 --file ./worker2.yaml 100 | ``` 101 | 102 | После перезапуска Controls Plane узла необходимо инициализировать etcd: 103 | 104 | ```sh 105 | talosctl bootstrap --nodes 172.16.61.11 --endpoints 172.16.61.11 --talosconfig=./talosconfig 106 | ``` 107 | 108 | Получаем kube config: 109 | 110 | ```sh 111 | talosctl kubeconfig ~/.kube/config --nodes 172.16.61.10 --endpoints 172.16.61.10 --talosconfig ./talosconfig 112 | ``` 113 | 114 | ## Настройка Cilium 115 | 116 | Добавляем репозиторий: 117 | 118 | ```sh 119 | helm repo add cilium https://helm.cilium.io/ 120 | helm repo update 121 | ``` 122 | 123 | Устанавливаем Cilium: 124 | 125 | ```sh 126 | helm upgrade \ 127 | --install \ 128 | cilium \ 129 | cilium/cilium \ 130 | --version 1.16.5 \ 131 | --namespace kube-system \ 132 | --values cilium/values.yaml 133 | ``` 134 | 135 | ## Metrics Server 136 | 137 | Устанавливаем Metrics Server 138 | 139 | ```sh 140 | helm upgrade \ 141 | --namespace kube-system \ 142 | --install metrics-server metrics-server/metrics-server \ 143 | --set args={--kubelet-insecure-tls} 144 | ``` 145 | 146 | Поднимаем **Traefik Kubernetes Ingress** 147 | 148 | ```sh 149 | helm upgrade \ 150 | --install \ 151 | --namespace traefik \ 152 | --create-namespace \ 153 | traefik traefik/traefik \ 154 | --values traefik/values.yaml 155 | ``` 156 | 157 | Обновление кластера 158 | 159 | ```sh 160 | talosctl --nodes 172.16.61.10 upgrade-k8s --to 1.32.0 161 | ``` 162 | -------------------------------------------------------------------------------- /talos/cilium/ippool.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cilium.io/v2alpha1 2 | kind: CiliumLoadBalancerIPPool 3 | metadata: 4 | name: pool 5 | spec: 6 | blocks: 7 | - cidr: 172.16.61.20/32 8 | -------------------------------------------------------------------------------- /talos/cilium/l2-announcement-policy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cilium.io/v2alpha1 2 | kind: CiliumL2AnnouncementPolicy 3 | metadata: 4 | name: policy1 5 | spec: 6 | serviceSelector: 7 | matchLabels: 8 | color: blue 9 | nodeSelector: 10 | matchExpressions: 11 | - key: node-role.kubernetes.io/control-plane 12 | operator: DoesNotExist 13 | interfaces: 14 | - ^ens[160-161]+ 15 | externalIPs: true 16 | loadBalancerIPs: true 17 | -------------------------------------------------------------------------------- /talos/cilium/values.yaml: -------------------------------------------------------------------------------- 1 | ipam: 2 | mode: kubernetes 3 | kubeProxyReplacement: true 4 | securityContext: 5 | capabilities: 6 | ciliumAgent: 7 | - CHOWN 8 | - KILL 9 | - NET_ADMIN 10 | - NET_RAW 11 | - IPC_LOCK 12 | - SYS_ADMIN 13 | - SYS_RESOURCE 14 | - DAC_OVERRIDE 15 | - FOWNER 16 | - SETGID 17 | - SETUID 18 | cleanCiliumState: 19 | - NET_ADMIN 20 | - SYS_ADMIN 21 | - SYS_RESOURCE 22 | cgroup: 23 | autoMount: 24 | enabled: false 25 | hostRoot: /sys/fs/cgroup 26 | k8sServiceHost: 172.16.61.10 27 | k8sServicePort: 6443 28 | l2announcements: 29 | enabled: true 30 | devices: ens+ 31 | hubble: 32 | relay: 33 | enabled: true 34 | ui: 35 | enabled: true 36 | ingress: 37 | enabled: true 38 | hosts: 39 | - hubble.test 40 | -------------------------------------------------------------------------------- /talos/cp0.patch: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | hostname: cp0 4 | interfaces: 5 | - interface: ens160 6 | dhcp: false 7 | addresses: 8 | - 172.16.61.11/24 9 | vip: 10 | ip: 172.16.61.10 11 | routes: 12 | - network: 0.0.0.0/0 13 | gateway: 172.16.61.2 14 | -------------------------------------------------------------------------------- /talos/cp1.patch: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | hostname: cp1 4 | interfaces: 5 | - interface: ens160 6 | dhcp: false 7 | addresses: 8 | - 172.16.61.12/24 9 | vip: 10 | ip: 172.16.61.10 11 | routes: 12 | - network: 0.0.0.0/0 13 | gateway: 172.16.61.2 14 | -------------------------------------------------------------------------------- /talos/cp2.patch: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | hostname: cp2 4 | interfaces: 5 | - interface: ens160 6 | dhcp: false 7 | addresses: 8 | - 172.16.61.13/24 9 | vip: 10 | ip: 172.16.61.10 11 | routes: 12 | - network: 0.0.0.0/0 13 | gateway: 172.16.61.2 14 | -------------------------------------------------------------------------------- /talos/patch.yaml: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | nameservers: 4 | - 77.88.8.8 5 | - 77.88.8.1 6 | install: 7 | disk: /dev/nvme0n1 8 | time: 9 | servers: 10 | - 1.ru.pool.ntp.org 11 | - 2.ru.pool.ntp.org 12 | - 3.ru.pool.ntp.org 13 | cluster: 14 | network: 15 | cni: 16 | name: none 17 | proxy: 18 | disabled: true 19 | -------------------------------------------------------------------------------- /talos/traefik/values.yaml: -------------------------------------------------------------------------------- 1 | deployment: 2 | kind: DaemonSet 3 | 4 | service: 5 | labels: 6 | color: blue 7 | spec: 8 | externalTrafficPolicy: Local 9 | 10 | additionalArguments: 11 | - --serversTransport.insecureSkipVerify=true 12 | -------------------------------------------------------------------------------- /talos/worker0.patch: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | hostname: worker0 4 | interfaces: 5 | - interface: ens160 6 | dhcp: false 7 | addresses: 8 | - 172.16.61.14/24 9 | routes: 10 | - network: 0.0.0.0/0 11 | gateway: 172.16.61.2 12 | -------------------------------------------------------------------------------- /talos/worker1.patch: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | hostname: worker1 4 | interfaces: 5 | - interface: ens160 6 | dhcp: false 7 | addresses: 8 | - 172.16.61.15/24 9 | routes: 10 | - network: 0.0.0.0/0 11 | gateway: 172.16.61.2 12 | -------------------------------------------------------------------------------- /talos/worker2.patch: -------------------------------------------------------------------------------- 1 | machine: 2 | network: 3 | hostname: worker2 4 | interfaces: 5 | - interface: ens160 6 | dhcp: false 7 | addresses: 8 | - 172.16.61.16/24 9 | routes: 10 | - network: 0.0.0.0/0 11 | gateway: 172.16.61.2 12 | --------------------------------------------------------------------------------