├── .gitignore ├── README.md ├── app ├── .dockerignore ├── Dockerfile ├── README.md ├── app.py ├── requirements.txt ├── static │ └── main.css └── templates │ └── index.html ├── k8s-files ├── lab-demo.yaml.tpl └── load-balancer.yaml └── terraform ├── README.md ├── database.tf ├── main.tf ├── output.tf └── variables.tf /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.idea 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Практикум: развертывание отказоустойчивого приложения в Managed Service for Kubernetes® и MDB 2 | 3 | # Требуемое окружение 4 | 5 | В рамках практической работы окружение подготовлено в виде образа ВМ. Из этого образа будет создана виртуальная машина, 6 | на которой и будет выполняться работа. Для самостоятельного выполнения работы придется настроить окружение самостоятельно. 7 | 8 | Для выполнения практической работы: 9 | 1. Установите следующие утилиты: 10 | * yc (https://cloud.yandex.ru/docs/cli/quickstart); 11 | * Terraform (https://learn.hashicorp.com/tutorials/terraform/install-cli); 12 | * Docker (https://docs.docker.com/get-docker/); 13 | * Kubectl (https://kubernetes.io/docs/tasks/tools/install-kubectl/); 14 | * envsubst; 15 | * git; 16 | * ssh; 17 | * curl. 18 | 19 | 2. Зарегистрироваться в Яндекс.Облаке (https://cloud.yandex.ru/); 20 | 3. Добавить в облако каталог, в котором будете создавать ресурсы. 21 | 22 | # Подготовка окружения 23 | ## Шаг 0. Вход в облако 24 | Видео: https://youtu.be/M0fXvr8h8bE 25 | * Проверяем предсозданные ресурсы в облаке. 26 | 27 | ## Шаг 1. Создание VM + Настройка консольной утилиты yc 28 | Видео: https://youtu.be/_jKRNy0SJ5E 29 | * Настраиваем доступ по протоколу SSH. 30 | * Настраиваем консольную утилиту yc. 31 | 32 | ### Настройка доступа по протоколу SSH 33 | 1. Убедитесь, что у вас есть SSH-ключ или сгенерируйте новый (подробная [инструкция](https://cloud.yandex.ru/docs/compute/operations/vm-connect/ssh#creating-ssh-keys)). 34 | ``` 35 | ssh-keygen -t rsa -b 2048 # генерация нового ssh-ключа 36 | cat ~/.ssh/id_rsa.pub 37 | ``` 38 | 39 | 2. Создайте в Консоли виртуальную машину из образа `base-image`: 40 | * В каталоге нажмите кнопку `Создать ресурс`, выберите ресурс `Виртуальная машина`, введите имя `lab-vm`. 41 | * В секции `Выбор образа/загрузочного диска` выберете вкладку `Пользовательские`, выберете загрузочный диск 42 | из образа `base-image`. 43 | * В секции `Диски` у созданного диска выберите `Тип диска` SSD. 44 | * В разделе `Доступ` выберите аккаунт `sa-admin-`, введите логин для входа на ВМ. 45 | Скопируйте SSH-ключ из файла `~/.ssh/id_rsa.pub`. 46 | 47 | 3. Дождитесь создания ВМ (статуса `Running`); 48 | 4. Зайдите на страницу ВМ, убедитесь что привязан сервисный аккаунт `sa-admin-`, скопируйте публичный IPv4 адрес; 49 | 5. Войдите на созданную ВМ по SSH; 50 | 6. Дальнейшие команды выполняйте на созданной ВМ. 51 | 52 | ### Настройка консольной утилиты yc для работы с Яндекс Облаком 53 | 1. В браузере вернитесь на страницу облака и скопируйте id каталога: 54 | ``` 55 | echo "export FOLDER_ID=folder-id-here" >> ~/.bashrc && . ~/.bashrc 56 | echo $FOLDER_ID 57 | ``` 58 | 3. Выполните в терминале команду `yc config set folder-id $FOLDER_ID`; 59 | 2. Выполните в терминале команду `yc config set instance-service-account true`, чтобы использовать yc от привязанного к ВМ 60 | сервисного аккаунта; 61 | 62 | 4. Чтобы проверить корректность настройки yc, выведите список виртуальных машин в каталоге с помощью команды: 63 | ``` 64 | yc compute instance list 65 | ``` 66 | 67 | # Развертывание кластера PostgreSQL с помощью Terraform 68 | ## Шаг 2. Используем Terraform 69 | Видео: https://youtu.be/ZDYVsVMuZeQ 70 | * Знакомимся с конфигурацией Terraform 71 | * Инициализация Terraform и создание кластера PostgreSQL 72 | 73 | 74 | ### Спецификация Terraform 75 | 76 | 1. На ВМ загружен репозиторий с файлами для практической работы. Обновите версию репозитория и сохраните 77 | путь к репозиторию в переменной окружения `REPO`: 78 | ``` 79 | echo "export REPO=/home/common/yandex-scale-2020-lab-k8s" >> ~/.bashrc && . ~/.bashrc 80 | echo $REPO 81 | cd $REPO 82 | git pull 83 | ``` 84 | 85 | ### Инициализация Terraform и создание кластера PostgreSQL 86 | 87 | 1. Перейдите в каталог с описанием инфраструктуры приложения и инициализируйте Terraform: 88 | ``` 89 | cd $REPO/terraform/ && ls 90 | terraform init 91 | ``` 92 | 2. Разверните инфраструктуру с помощью Terraform: 93 | ``` 94 | terraform apply -var yc_folder=$FOLDER_ID -var user=$USER 95 | ``` 96 | 97 | 3. На время развертывания кластера оставьте открытым терминал с запущенным Terraform 98 | и перейдите к следующему шагу. Позже мы проверим, что кластер создался. 99 | 100 | # Обзор кластера Managed K8S и создание группы узлов 101 | ## Шаг 3. Создаем группу узлов 102 | Видео: https://youtu.be/PfjaBv7X4o8 103 | * Обзор созданного кластера 104 | * Создание группы узлов 105 | 106 | ### Обзор созданного кластера 107 | 1. Перейдите в раздел Консоли `Managed Service for Kubernetes`. В нем вы увидете созданный кластер с названием `lab-k8s`. 108 | 2. Зайдите на страницу кластера. Обратите внимание на подключенные сервисные аккаунты: 109 | * Сервисный аккаунт `k8s-cluster-manager-` используется для ресурсов, необходимых кластеру. 110 | * Сервисный аккаунт `k8s-image-puller-` используется в качестве сервисного аккаунта для аутентификации 111 | в Container Registry на нодах кластера. 112 | 3. Обратите внимание, что создан зональный кластер, а значит создан один экземпляр мастера Kubernetes. 113 | 114 | ### Создание группы узлов 115 | 1. Перейдите на вкладку `Группы узлов` и создайте группу узлов с именем `lab-k8s-group` и одним узлом: 116 | * В разделе `Доступ` введите логин для входа на ВМ. Скопируйте SSH-ключ из файла `~/.ssh/id_rsa.pub`. 117 | 2. Пока создается группа узлов, проверьте результаты работы Terraform и подготовьте докер образ с приложением. 118 | Оставьте окно браузера открытым и перейдите в консоль. 119 | 120 | # Проверка кластера PostgreSQL 121 | ## Шаг 4. Проверяем кластер 122 | Видео: https://youtu.be/NWNnuzBxFD8 123 | * Сохраните переменные 124 | * Проверьте кластер PostgreSQL 125 | 126 | ### Проверка кластера PostgreSQL 127 | 1. Вернитесь к терминалу с запущенным Terraform. По завершении работы в секции `Outputs` отобразятся данные о кластере. 128 | 129 | 2. Сохраните данные о кластере в переменные окружения: 130 | ``` 131 | echo "export DATABASE_URI=database_uri-here" >> ~/.bashrc 132 | echo "export DATABASE_HOSTS=database_hosts-here" >> ~/.bashrc 133 | . ~/.bashrc 134 | echo $DATABASE_URI 135 | echo $DATABASE_HOSTS 136 | ``` 137 | 138 | 3. Проверьте результат работы Terraform: 139 | ``` 140 | yc managed-postgresql cluster list 141 | yc managed-postgresql cluster get 142 | ``` 143 | 144 | # Проверка кластера K8S 145 | ## Шаг 5. Проверяем кластер 146 | Видео: https://youtu.be/z_mvuhNOhzw 147 | * Добавить переменные окружения 148 | * Добавить учетные данные 149 | 150 | ### Добавление переменных окружения 151 | 1. На странице браузера, где создавалась группа узлов, проверьте, что создание группы завершено. 152 | 2. В терминале проверьте, что кластер доступен к использованию: 153 | ``` 154 | yc managed-kubernetes cluster list 155 | echo "export K8S_ID=k8s-id-here" >> ~/.bashrc && . ~/.bashrc 156 | echo $K8S_ID 157 | yc managed-kubernetes cluster --id=$K8S_ID get 158 | yc managed-kubernetes cluster --id=$K8S_ID list-node-groups 159 | ``` 160 | 161 | ### Добавление учетных данных в конфигурационный файл Kubectl 162 | ``` 163 | yc managed-kubernetes cluster get-credentials --id=$K8S_ID --external 164 | ``` 165 | Конфигурация будет записана в файл `~/.kube/config`, проверим его содержимое: 166 | ``` 167 | cat ~/.kube/config 168 | ``` 169 | 170 | # Подготовка Docker-образов 171 | ## Шаг 6. Работаем с Docker 172 | Видео: https://youtu.be/dArqkeLksQk 173 | * Создать Container Registry 174 | * Собрать Docker-образ 175 | 176 | ### Создание Container Registry 177 | 1. Создайте реестр: `yc container registry create --name lab-registry`; 178 | 2. Сохраните id реестра: 179 | ``` 180 | echo "export REGISTRY_ID=registry-id-here" >> ~/.bashrc && . ~/.bashrc 181 | ``` 182 | 2. Проверьте наличие реестра через терминал: 183 | ``` 184 | yc container registry list 185 | yc container registry get $REGISTRY_ID 186 | ``` 187 | 188 | 3. Настройте аутентификацию в docker: 189 | ``` 190 | yc container registry configure-docker 191 | cat ~/.docker/config.json 192 | ``` 193 | Файл `config.json` содержит вашу конфигурацию после авторизации: 194 | ``` 195 | { 196 | "credHelpers": { 197 | "container-registry.cloud.yandex.net": "yc", 198 | "cr.cloud.yandex.net": "yc", 199 | "cr.yandex": "yc" 200 | } 201 | } 202 | ``` 203 | 204 | ### Сборка и загрузка Docker-образов в Container Registry 205 | 1. Перейдите к исходникам приложения: 206 | ``` 207 | cd $REPO/app && ls 208 | ``` 209 | 210 | 2. Соберите образ приложения: 211 | ``` 212 | sudo docker build . --tag cr.yandex/$REGISTRY_ID/lab-demo:v1 213 | sudo docker images 214 | ``` 215 | 216 | 3. Загрузите образ 217 | ``` 218 | sudo docker push cr.yandex/$REGISTRY_ID/lab-demo:v1 219 | ``` 220 | 221 | ### Просмотр списка Docker-образов 222 | 223 | 1. Получите список Docker-образов в реестре командой `yc container image list`. 224 | * В результате в таблице отборазятся ID образа, дата его создания и другие данные. 225 | 226 | # Развертывание приложения и балансировщика нагрузки в k8s 227 | ## Шаг 7. Работаем с kubectl 228 | Видео: https://youtu.be/q5NR5wJwPpQ 229 | 230 | 1. Просмотрите файлы конфигурации: 231 | ``` 232 | cd $REPO/k8s-files && ls 233 | cat lab-demo.yaml.tpl 234 | cat load-balancer.yaml 235 | ``` 236 | 237 | 2. В файле `lab-demo.yaml.tpl` замените значение переменных: 238 | * Переменные `DATABASE_URI` и `DATABASE_HOSTS` получены в результате работы terraform на четвертом шаге. 239 | * `REGISTRY_ID` получали ранее с помощью команды `yc container registry list`. 240 | ``` 241 | envsubst \$REGISTRY_ID,\$DATABASE_URI,\$DATABASE_HOSTS lab-demo.yaml 242 | cat lab-demo.yaml 243 | ``` 244 | 245 | 3. Убедитесь что все переменные окружения из файла `lab-demo.yaml.tpl` заменились на реальные значения 246 | в файле `lab-demo.yaml` 247 | 248 | 4. Разверните ресурсы: 249 | ``` 250 | kubectl apply -f lab-demo.yaml 251 | kubectl describe deployment lab-demo 252 | ``` 253 | 254 | ``` 255 | kubectl apply -f load-balancer.yaml 256 | kubectl describe service lab-demo 257 | ``` 258 | 259 | 5. Как только балансировщик нагрузки полноценно развернется и получит внешний URL (поле `LoadBalancer Ingress`), 260 | проверим работоспособность сервиса в браузере. 261 | 262 | # Изменения в архитектуре сервиса для обеспечения отказоустойчивости 263 | ## Шаг 8. Изменения в архитектуре сервиса 264 | Видео: https://youtu.be/c9yXNJPfeuo 265 | 266 | После выполнения всех шагов имеем: 267 | * MDB PostgreSQL с одним хостом. 268 | * Managed Kubernetes с зональным мастером (1 хост) и группой узлов из одного узла. 269 | * одну реплику приложения, запущенную в k8s. 270 | 271 | ### Отказоустойчивый MDB PostgreSQL 272 | 273 | В MDB можно увеличить количество хостов в кластере без остановки работы. 274 | 1. Перейдите во вкладку `Managed Service for PostgreSQL` в UI, выберите созданный кластер, во вкладке `Хосты` добавьте хост. 275 | 2. Выберите зону доступности, отличную от той, которая используется для первого хоста. 276 | 3. Убедитесь, что у хостов в кластере достаточно ресурсов, чтобы обрабатывать возвросшую нагрузку при отказе одного 277 | или нескольких хостов. 278 | 279 | ### Отказоустойчивый Managed Kubernetes 280 | 281 | Необходимо создать региональный мастер, состоящий из трех хостов. Тип мастера уже созданного кластера 282 | поменять нельзя, поэтому придется создать новый кластер. 283 | 1. Перейдите во вкладку `Managed Service for Kubernetes` и создайте новый кластер. 284 | 2. Выберите тип мастера `Региональный`. 285 | 3. Создайте группу узлов с тремя узлами. 286 | 4. Убедитесь, что у хостов в группе узлов достаточно ресурсов, чтобы обрабатывать возвросшую нагрузку при отказе одного 287 | или нескольких хостов. 288 | 289 | ### Отказоустойчивое приложение 290 | 291 | При развертывании приложения в k8s вы указывали одну реплику, теперь надо увеличить количество реплик до трех. Созданный 292 | балансировщик нагрузки будет распределять нагрузку по всем трем репликам. 293 | 294 | # Удаление ресурсов 295 | ## Шаг 9. Удаление ресурсов 296 | Видео: https://youtu.be/CaDWyC0Mpvo 297 | * Удаление ресурсов из кластера k8s. 298 | * Удаление кластера k8s. 299 | * Удаление базы данных в Terraform. 300 | * Удаление реестра и Docker-образов. 301 | * Удаление ВМ с настроенным окружением. 302 | 303 | ### Удаление ресурсов из кластера k8s 304 | ``` 305 | kubectl delete -f load-balancer.yaml 306 | kubectl delete -f lab-demo.yaml 307 | ``` 308 | 309 | ### Удаление кластера k8s 310 | ``` 311 | yc managed-kubernetes cluster list 312 | yc managed-kubernetes cluster delete lab-k8s 313 | yc managed-kubernetes cluster list 314 | ``` 315 | 316 | ### Удаление базы данных в Terraform 317 | ``` 318 | cd $REPO/terraform 319 | terraform destroy -var yc_folder=$FOLDER_ID -var user=$USER 320 | yc managed-postgresql cluster list 321 | ``` 322 | 323 | ### Удаление реестра и Docker-образов 324 | Перейдите во вкладку `Container Registry` в Консоли, выберите реестр `lab-registry` и удалите в нем все образы. 325 | После этого вернитесь к списку реестров, нажмите раскрывающееся меню для реестра `lab-registry` и удалите реестр. 326 | 327 | ### Удаление ВМ с настроенным окружением 328 | В консоли перейдите во вкладку `Compute`, нажмите раскрывающееся меню для ВМ `lab-vm` и удалите эту ВМ. -------------------------------------------------------------------------------- /app/.dockerignore: -------------------------------------------------------------------------------- 1 | ** 2 | !app.py 3 | !requirements.txt 4 | !static 5 | !templates 6 | -------------------------------------------------------------------------------- /app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7-slim-buster 2 | 3 | WORKDIR /usr/src/app 4 | COPY requirements.txt ./ 5 | RUN pip install --no-cache-dir -r requirements.txt 6 | 7 | COPY . . 8 | 9 | ARG COLOR_SCHEME=light 10 | ENV COLOR_SCHEME "$COLOR_SCHEME" 11 | CMD ["python", "./app.py"] 12 | -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # TodoList — демо-приложение для вебинара 2 | 3 | Как подготовить приложение для запуска в Яндекс.Облаке: 4 | 1. Аутентифицироваться в Container Registry 5 | ``` 6 | yc container registry configure-docker 7 | ``` 8 | 9 | 1. Создать Container Registry 10 | ``` 11 | yc container registry create --name todo-registry 12 | ``` 13 | 14 | 1. Собрать docker-образ с тегом v1 15 | ``` 16 | sudo docker build . --tag cr.yandex//todo-demo:v1 17 | ``` 18 | 19 | 1. Собрать docker-образ с тегом v2 (для проверки сценария обновления приложения) 20 | ``` 21 | sudo docker build . --build-arg COLOR_SCHEME=dark --tag cr.yandex//todo-demo:v2 22 | ``` 23 | 24 | 1. Загрузить docker-образы в Container Registry 25 | ``` 26 | sudo docker push cr.yandex//todo-demo:v1 27 | sudo docker push cr.yandex//todo-demo:v2 28 | ``` 29 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, render_template, request, redirect, url_for 2 | from flask_sqlalchemy import SQLAlchemy 3 | import socket 4 | import os 5 | 6 | app = Flask(__name__, template_folder="templates") 7 | 8 | # Database 9 | app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URI'] 10 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # silence the deprecation warning 11 | 12 | db = SQLAlchemy(app, engine_options={'connect_args': { 13 | 'sslmode': 'require', 14 | 'host': os.environ['DATABASE_HOSTS'], 15 | 'port': 6432, 16 | 'target_session_attrs': 'read-write', 17 | }}) 18 | 19 | 20 | class Todo(db.Model): 21 | id = db.Column(db.Integer, primary_key=True) 22 | text = db.Column(db.String(200)) 23 | complete = db.Column(db.Boolean) 24 | 25 | 26 | UNAVAILABLE = ("unavailable", 503) 27 | LIGHT_MODE = "light" 28 | DARK_MODE = "dark" 29 | 30 | # Defines Load Balancer health check result. if False, /alive handler will return 503 31 | alive = True 32 | # Defines Instance Group health check result. if False, /healthy handler will return 503 33 | healthy = True 34 | # Defines color scheme of the app 35 | mode = LIGHT_MODE 36 | if 'COLOR_SCHEME' in os.environ: 37 | mode = os.environ['COLOR_SCHEME'] 38 | 39 | 40 | # Main page 41 | @app.route('/') 42 | def index(): 43 | if not healthy: 44 | return UNAVAILABLE 45 | 46 | incomplete = Todo.query.filter_by(complete=False).all() 47 | complete = Todo.query.filter_by(complete=True).all() 48 | 49 | return render_template('index.html', 50 | incomplete=incomplete, complete=complete, hostname=socket.gethostname(), mode=mode) 51 | 52 | 53 | # List all incomplete items 54 | @app.route('/list') 55 | def list_incomplete(): 56 | if not healthy: 57 | return UNAVAILABLE 58 | incomplete = Todo.query.filter_by(complete=False).all() 59 | return str([todo.text for todo in incomplete]) 60 | 61 | 62 | # List all completed items 63 | @app.route('/completed') 64 | def list_completed(): 65 | if not healthy: 66 | return UNAVAILABLE 67 | completed = Todo.query.filter_by(complete=True).all() 68 | return str([todo.text for todo in completed]) 69 | 70 | 71 | # Add new item 72 | @app.route('/add', methods=['POST']) 73 | def add(): 74 | if not healthy: 75 | return UNAVAILABLE 76 | todo = Todo(text=request.form['todoitem'], complete=False) 77 | db.session.add(todo) 78 | db.session.commit() 79 | 80 | # Makes to stay on the same home page###### 81 | return redirect(url_for('index')) 82 | 83 | 84 | # Complete item 85 | @app.route('/complete/') 86 | def complete(id): 87 | if not healthy: 88 | return UNAVAILABLE 89 | todo = Todo.query.filter_by(id=int(id)).first() 90 | todo.complete = True 91 | db.session.commit() 92 | 93 | # Makes to stay on the same home page 94 | return redirect(url_for('index')) 95 | 96 | 97 | # Load Balancer health check handler 98 | @app.route('/alive') 99 | def hc_alive(): 100 | if alive: 101 | return "alive", 200 102 | else: 103 | return UNAVAILABLE 104 | 105 | 106 | @app.route('/switch_alive', methods=['POST']) 107 | def switch_alive(): 108 | global alive 109 | alive = not alive 110 | return "now alive = {}".format(alive) 111 | 112 | 113 | # Instance Group health check handler 114 | @app.route('/healthy') 115 | def hc_healthy(): 116 | if healthy: 117 | return "healthy", 200 118 | else: 119 | return UNAVAILABLE 120 | 121 | 122 | @app.route('/switch_healthy', methods=['POST']) 123 | def switch_healthy(): 124 | global healthy 125 | healthy = not healthy 126 | return "now healthy = {}".format(healthy) 127 | 128 | 129 | if __name__ == '__main__': 130 | db.create_all() 131 | app.run(debug=True, host='0.0.0.0', port=80) 132 | -------------------------------------------------------------------------------- /app/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask_SQLAlchemy==2.4.1 2 | Flask==1.1.1 3 | psycopg2-binary==2.8.4 4 | -------------------------------------------------------------------------------- /app/static/main.css: -------------------------------------------------------------------------------- 1 | body{ 2 | margin-top: 5px; 3 | font-size: 15pt; 4 | } 5 | h2 { 6 | font-size: 30pt; 7 | } 8 | li { 9 | font-size: 20pt; 10 | } 11 | 12 | body.light{ 13 | background: #ffffff; 14 | color: #0077ff; 15 | } 16 | .button.light{ 17 | background: #ffffff; 18 | color: #0077ff; 19 | } 20 | .light a:link{ 21 | color: #0077ff; 22 | } 23 | 24 | body.dark{ 25 | background: #1b1a30; 26 | color: #ffffff; 27 | } 28 | .button.dark { 29 | background: #1b1a30; 30 | color: #ffffff; 31 | } 32 | .dark a:link { 33 | color: #ffffff; 34 | } 35 | -------------------------------------------------------------------------------- /app/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Todo App 6 | 7 | 8 | 9 | 10 |

Todo List

11 |
Add a new todo item: 12 |
13 | 14 | 15 |
16 |
17 |
18 |

Incomplete Items

19 |
    20 | {% for todo in incomplete %} 21 |
  • {{ todo.text }}
  • 22 | {% endfor %} 23 |
24 |

Completed Items

25 |
    26 | {% for todo in complete %} 27 |
  • {{ todo.text }}
  • 28 | {% endfor %} 29 |
30 |
31 |
32 | Also, actual hostname is {{ hostname }} 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /k8s-files/lab-demo.yaml.tpl: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: lab-demo 5 | labels: 6 | app-label: lab-demo-label 7 | namespace: default 8 | spec: 9 | replicas: 1 10 | selector: 11 | matchLabels: 12 | app-label: lab-demo-label 13 | template: 14 | metadata: 15 | labels: 16 | app-label: lab-demo-label 17 | spec: 18 | containers: 19 | - name: lab-demo-app 20 | image: cr.yandex/$REGISTRY_ID/lab-demo:v1 21 | env: 22 | - name: DATABASE_URI 23 | value: "$DATABASE_URI" 24 | - name: DATABASE_HOSTS 25 | value: "$DATABASE_HOSTS" 26 | -------------------------------------------------------------------------------- /k8s-files/load-balancer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | namespace: default 5 | name: lab-demo 6 | labels: 7 | app-label: lab-demo-label 8 | spec: 9 | ports: 10 | - port: 80 11 | name: plaintext 12 | targetPort: 80 13 | selector: 14 | app-label: lab-demo-label 15 | type: LoadBalancer 16 | -------------------------------------------------------------------------------- /terraform/README.md: -------------------------------------------------------------------------------- 1 | # Terraform-спецификация окружения для TodoList 2 | 3 | ## Деплой приложения 4 | 1. Установить terraform: `https://www.terraform.io` 5 | 1. Инициализировать terraform в директории со спецификацией: 6 | ``` 7 | terraform init 8 | ``` 9 | 1. Если реестр, в котором лежат docker-образы приложения, называется не `registry-lab`, измените переменную `registry_name` 10 | в файле [main.tf](main.tf). 11 | 1. Развернуть и запустить приложение. 12 | * folder_id - каталог, в котором будет развернуто приложение, 13 | * yc_token - OAuth токен пользователя, от имени которого будет развернуто приложение 14 | ``` 15 | terraform apply -var yc_folder= -var yc_token= -var user=$USER 16 | ``` 17 | 18 | ## Создаваемые ресурсы 19 | * VPC Network с тремя подсетями во всех зонах доступности; 20 | * Кластер Managed PostgreSQL с одним хостом в зоне доступности `ru-central1-a`; 21 | 22 | ## Удаление приложения 23 | ``` 24 | terraform destroy -var yc_folder= -var yc_token= -var user=$USER 25 | ``` 26 | -------------------------------------------------------------------------------- /terraform/database.tf: -------------------------------------------------------------------------------- 1 | locals { 2 | dbuser = tolist(yandex_mdb_postgresql_cluster.lab-postgresql.user.*.name)[0] 3 | dbpassword = tolist(yandex_mdb_postgresql_cluster.lab-postgresql.user.*.password)[0] 4 | dbhosts = yandex_mdb_postgresql_cluster.lab-postgresql.host.*.fqdn 5 | dbname = tolist(yandex_mdb_postgresql_cluster.lab-postgresql.database.*.name)[0] 6 | dburi = "postgresql://${local.dbuser}:${local.dbpassword}@:1/${local.dbname}" 7 | } 8 | 9 | data "yandex_vpc_network" "lab-network" { 10 | name = "lab-network" 11 | } 12 | 13 | data "yandex_vpc_subnet" "lab-subnet-b" { 14 | name = "lab-subnet-b" 15 | } 16 | 17 | resource "yandex_mdb_postgresql_cluster" "lab-postgresql" { 18 | name = "lab-postgresql" 19 | folder_id = var.yc_folder 20 | environment = "PRODUCTION" 21 | network_id = data.yandex_vpc_network.lab-network.id 22 | 23 | config { 24 | version = 12 25 | resources { 26 | resource_preset_id = "s2.micro" 27 | disk_type_id = "network-ssd" 28 | disk_size = 32 29 | } 30 | } 31 | 32 | database { 33 | name = "db" 34 | owner = "user" 35 | } 36 | 37 | user { 38 | name = "user" 39 | password = "password" 40 | permission { 41 | database_name = "db" 42 | } 43 | } 44 | 45 | host { 46 | zone = "ru-central1-b" 47 | subnet_id = data.yandex_vpc_subnet.lab-subnet-b.id 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /terraform/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_providers { 3 | yandex = { 4 | source = "terraform-providers/yandex" 5 | } 6 | } 7 | } 8 | 9 | provider "yandex" { 10 | endpoint = "api.cloud.yandex.net:443" 11 | folder_id = var.yc_folder 12 | } 13 | -------------------------------------------------------------------------------- /terraform/output.tf: -------------------------------------------------------------------------------- 1 | output "cluster_id" { 2 | value = yandex_mdb_postgresql_cluster.lab-postgresql.id 3 | } 4 | 5 | output "database_uri" { 6 | value = local.dburi 7 | } 8 | 9 | output "database_hosts" { 10 | value = join(",", local.dbhosts) 11 | } 12 | 13 | -------------------------------------------------------------------------------- /terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "yc_folder" { 2 | type = string 3 | description = "Yandex Cloud folder" 4 | } 5 | 6 | variable "user" { 7 | type = string 8 | description = "$USER" 9 | } 10 | --------------------------------------------------------------------------------