From b5268a1370e72bbedf77d7c30ae24527294fa6bc Mon Sep 17 00:00:00 2001 From: dannc Date: Wed, 21 Dec 2022 10:57:52 +0700 Subject: [PATCH] homework 13.5: complete all tasks --- readme.md | 1 + .../13.5/project/components/backend.jsonnet | 74 ++++++++ .../13.5/project/components/database.jsonnet | 162 ++++++++++++++++++ .../13.5/project/components/frontend.jsonnet | 74 ++++++++ .../project/components/local_google.jsonnet | 23 +++ .../13.5/project/environments/base.libsonnet | 16 ++ .../project/environments/production.libsonnet | 18 ++ .../13.5/project/environments/stage.libsonnet | 12 ++ .../13.5/project/params.libsonnet | 10 ++ .../13.5/project/qbec.yaml | 15 ++ .../13-kubernates-config/13.5/readme.md | 132 ++++++++++++++ 11 files changed, 537 insertions(+) create mode 100644 src/homework/13-kubernates-config/13.5/project/components/backend.jsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/components/database.jsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/components/frontend.jsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/components/local_google.jsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/environments/base.libsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/environments/production.libsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/environments/stage.libsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/params.libsonnet create mode 100644 src/homework/13-kubernates-config/13.5/project/qbec.yaml create mode 100644 src/homework/13-kubernates-config/13.5/readme.md diff --git a/readme.md b/readme.md index e2faef9..2e3d74c 100644 --- a/readme.md +++ b/readme.md @@ -66,3 +66,4 @@ * [13.2. разделы и монтирование](/src/homework/13-kubernates-config/13.2) * [13.3. работа с kubectl](/src/homework/13-kubernates-config/13.3) * [13.4. инструменты для упрощения написания конфигурационных файлов. Helm и Jsonnet](/src/homework/13-kubernates-config/13.4) +* [13.5. поддержка нескольких окружений на примере Qbec](/src/homework/13-kubernates-config/13.5) diff --git a/src/homework/13-kubernates-config/13.5/project/components/backend.jsonnet b/src/homework/13-kubernates-config/13.5/project/components/backend.jsonnet new file mode 100644 index 0000000..c6a74ec --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/components/backend.jsonnet @@ -0,0 +1,74 @@ +local p = import '../params.libsonnet'; +local params = p.components.backend; + +[ + { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "labels": { + "app": "app", + "service": "backend" + }, + "name": "backend", + }, + "spec": { + "replicas": params.replicas, + "selector": { + "matchLabels": { + "app": "app", + "service": "backend" + } + }, + "template": { + "metadata": { + "labels": { + "app": "app", + "service": "backend" + } + }, + "spec": { + "containers": [ + { + "image": "dannecron/netology-devops-k8s-app:backend-latest", + "imagePullPolicy": "Always", + "name": "netology-backend", + "env": [ + { + "name": "DATABASE_URL", + "value": "postgresql://db_user:db_passwd@postgres:5432/news" + } + ], + "ports": [ + { + "name": "web", + "containerPort": 9000 + } + ] + } + ], + "terminationGracePeriodSeconds": 30 + } + } + } + }, + { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "name": "backend", + }, + "spec": { + "ports": [ + { + "name": "web", + "port": 9000 + } + ], + "selector": { + "app": "app", + "service": "backend" + } + } + } +] diff --git a/src/homework/13-kubernates-config/13.5/project/components/database.jsonnet b/src/homework/13-kubernates-config/13.5/project/components/database.jsonnet new file mode 100644 index 0000000..d9a151b --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/components/database.jsonnet @@ -0,0 +1,162 @@ +local p = import '../params.libsonnet'; +local params = p.components.database; + +[ + { + "apiVersion": "v1", + "kind": "ConfigMap", + "metadata": { + "name": "postgres-config", + "labels": { + "app": "postgres" + } + }, + "data": { + "POSTGRES_DB": "news", + "POSTGRES_USER": "db_user", + "POSTGRES_PASSWORD": "db_passwd", + "PGDATA": "/var/lib/postgresql/data" + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolume", + "metadata": { + "name": params.pvName, + "labels": { + "type": "local", + "app": "postgres" + } + }, + "spec": { + "storageClassName": "manual", + "capacity": { + "storage": "1Gi" + }, + "accessModes": [ + "ReadWriteMany" + ], + "hostPath": { + "path": params.pvPath + } + } + }, + { + "apiVersion": "v1", + "kind": "PersistentVolumeClaim", + "metadata": { + "name": "postgres-pv-claim", + "labels": { + "app": "postgres" + } + }, + "spec": { + "storageClassName": "manual", + "accessModes": [ + "ReadWriteMany" + ], + "resources": { + "requests": { + "storage": "1Gi" + } + } + } + }, + { + "apiVersion": "apps/v1", + "kind": "StatefulSet", + "metadata": { + "labels": { + "app": "app", + "service": "database", + "db-kind": "postgresql" + }, + "name": "db", + }, + "spec": { + "selector": { + "matchLabels": { + "app": "app", + "service": "database", + "db-kind": "postgresql" + } + }, + "serviceName": "postgres", + "replicas": 1, + "podManagementPolicy": "Parallel", + "updateStrategy": { + "type": "RollingUpdate" + }, + "template": { + "metadata": { + "labels": { + "app": "app", + "service": "database", + "db-kind": "postgresql" + } + }, + "spec": { + "terminationGracePeriodSeconds": 60, + "containers": [ + { + "name": "postgres", + "image": "postgres:13-alpine", + "imagePullPolicy": "IfNotPresent", + "ports": [ + { + "name": "postgresql", + "containerPort": 5432, + "protocol": "TCP" + } + ], + "envFrom": [ + { + "configMapRef": { + "name": "postgres-config" + } + } + ], + "volumeMounts": [ + { + "mountPath": "/var/lib/postgresql/data", + "name": "postgredb" + } + ] + } + ], + "volumes": [ + { + "name": "postgredb", + "persistentVolumeClaim": { + "claimName": "postgres-pv-claim" + } + } + ] + } + } + } + }, + { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "name": "postgres" + }, + "spec": { + "type": "ClusterIP", + "clusterIP": "None", + "ports": [ + { + "name": "postgresql", + "port": 5432, + "targetPort": "postgresql", + "protocol": "TCP" + } + ], + "selector": { + "service": "database", + "db-kind": "postgresql" + } + } + } +] diff --git a/src/homework/13-kubernates-config/13.5/project/components/frontend.jsonnet b/src/homework/13-kubernates-config/13.5/project/components/frontend.jsonnet new file mode 100644 index 0000000..f3df2e9 --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/components/frontend.jsonnet @@ -0,0 +1,74 @@ +local p = import '../params.libsonnet'; +local params = p.components.frontend; + +[ + { + "apiVersion": "apps/v1", + "kind": "Deployment", + "metadata": { + "labels": { + "app": "app", + "service": "frontend" + }, + "name": "frontend", + }, + "spec": { + "replicas": params.replicas, + "selector": { + "matchLabels": { + "app": "app", + "service": "frontend" + } + }, + "template": { + "metadata": { + "labels": { + "app": "app", + "service": "frontend" + } + }, + "spec": { + "containers": [ + { + "image": "dannecron/netology-devops-k8s-app:frontend-latest", + "imagePullPolicy": "Always", + "name": "netology-frontend", + "env": [ + { + "name": "BASE_URL", + "value": "http://backend:9000" + } + ], + "ports": [ + { + "name": "web", + "containerPort": 80 + } + ] + } + ], + "terminationGracePeriodSeconds": 30 + } + } + } + }, + { + "apiVersion": "v1", + "kind": "Service", + "metadata": { + "name": "frontend" + }, + "spec": { + "ports": [ + { + "name": "web", + "port": 80 + } + ], + "selector": { + "app": "app", + "service": "frontend" + } + } + } +] diff --git a/src/homework/13-kubernates-config/13.5/project/components/local_google.jsonnet b/src/homework/13-kubernates-config/13.5/project/components/local_google.jsonnet new file mode 100644 index 0000000..ea57ff6 --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/components/local_google.jsonnet @@ -0,0 +1,23 @@ +[ + { + "apiVersion": "v1", + "kind": "Endpoints", + "metadata": { + "name": "local-google", + }, + "subsets": [ + { + "addresses": [ + { + "ip": "173.194.220.100" + } + ], + "ports": [ + { + "port": 80 + } + ] + } + ] + } +] diff --git a/src/homework/13-kubernates-config/13.5/project/environments/base.libsonnet b/src/homework/13-kubernates-config/13.5/project/environments/base.libsonnet new file mode 100644 index 0000000..b27a6ac --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/environments/base.libsonnet @@ -0,0 +1,16 @@ + +// this file has the baseline default parameters +{ + components: { + backend: { + replicas: 1 + }, + frontend: { + replicas: 1 + }, + database: { + "pvName": "postgres-pv", + "pvPath": "/mnt/postgres/data" + }, + }, +} diff --git a/src/homework/13-kubernates-config/13.5/project/environments/production.libsonnet b/src/homework/13-kubernates-config/13.5/project/environments/production.libsonnet new file mode 100644 index 0000000..c370246 --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/environments/production.libsonnet @@ -0,0 +1,18 @@ + +// this file has the param overrides for the default environment +local base = import './base.libsonnet'; + +base { + components +: { + backend: { + replicas: 3, + }, + frontend: { + replicas: 3, + }, + database: { + "pvName": "postgres-pv-production", + "pvPath": "/mnt/postgres/production/data" + }, + } +} diff --git a/src/homework/13-kubernates-config/13.5/project/environments/stage.libsonnet b/src/homework/13-kubernates-config/13.5/project/environments/stage.libsonnet new file mode 100644 index 0000000..9a8ca9b --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/environments/stage.libsonnet @@ -0,0 +1,12 @@ + +// this file has the param overrides for the default environment +local base = import './base.libsonnet'; + +base { + components +: { + database: { + "pvName": "postgres-pv-stage", + "pvPath": "/mnt/postgres/stage/data" + } + } +} diff --git a/src/homework/13-kubernates-config/13.5/project/params.libsonnet b/src/homework/13-kubernates-config/13.5/project/params.libsonnet new file mode 100644 index 0000000..7614b12 --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/params.libsonnet @@ -0,0 +1,10 @@ + +// this file returns the params for the current qbec environment +local env = std.extVar('qbec.io/env'); +local paramsMap = import 'glob-import:environments/*.libsonnet'; +local baseFile = if env == '_' then 'base' else env; +local key = 'environments/%s.libsonnet' % baseFile; + +if std.objectHas(paramsMap, key) +then paramsMap[key] +else error 'no param file %s found for environment %s' % [key, env] diff --git a/src/homework/13-kubernates-config/13.5/project/qbec.yaml b/src/homework/13-kubernates-config/13.5/project/qbec.yaml new file mode 100644 index 0000000..40623c2 --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/project/qbec.yaml @@ -0,0 +1,15 @@ +apiVersion: qbec.io/v1alpha1 +kind: App +metadata: + name: project +spec: + environments: + stage: + defaultNamespace: "default" + server: https://51.250.85.245:6443 + excludes: + - local_google + production: + defaultNamespace: "production" + server: https://51.250.85.245:6443 + vars: {} diff --git a/src/homework/13-kubernates-config/13.5/readme.md b/src/homework/13-kubernates-config/13.5/readme.md new file mode 100644 index 0000000..0823cc8 --- /dev/null +++ b/src/homework/13-kubernates-config/13.5/readme.md @@ -0,0 +1,132 @@ +Выполнение [домашнего задания](https://github.com/netology-code/devkub-homeworks/blob/main/13-kubernetes-config-05-qbec.md) +по теме "13.5. поддержка нескольких окружений на примере Qbec" + +## Q/A + +> Приложение обычно существует в нескольких окружениях. Для удобства работы следует использовать соответствующие инструменты, например, Qbec. + +### Задание 1 + +Подготовить приложение для работы через qbec. + +> Приложение следует упаковать в qbec. Окружения должно быть 2: stage и production. + +> Требования: +> * stage окружение должно поднимать каждый компонент приложения в одном экземпляре; +> * production окружение — каждый компонент в трёх экземплярах; +> * для production окружения нужно добавить endpoint на внешний адрес. + +Для начала необходимо установить две утилиты: `jsonnet` и `qbec`. + +Для простоты оригинальная утилита [jsonnet](https://github.com/google/jsonnet) будет заменена на её [официальную реализацию на языке go](https://github.com/google/go-jsonnet). +Установка: + +* Скачать последний релиз с `github` + + ```shell + curl -LO https://github.com/google/go-jsonnet/releases/download/v0.19.1/go-jsonnet_0.19.1_Linux_x86_64.tar.gz + ``` + +* Распаковать архив + + ```shell + tar -zxf go-jsonnet_0.19.1_Linux_x86_64.tar.gz + ``` + +* Переместить файлы `jsonnet` и `jsonnetfmt` в директорию с исполняемыми файлами (н-р, `~/.local/bin`) + + ```shell + mv jsonnet ~/.local/bin && mv jsonnetfmt ~/.local/bin + ``` + +* Не забыть удалить оставшиеся файлы + + ```shell + rm -f LICENSE README.md go-jsonnet_0.19.1_Linux_x86_64.tar.gz + ``` + +Для установки `qbec`: + +* Скачать последний релиз с `github` + + ```shell + curl -OL https://github.com/splunk/qbec/releases/download/v0.15.2/qbec-linux-amd64.tar.gz + ``` + +* Распаковать архив + + ```shell + tar -zxf qbec-linux-amd64.tar.gz + ``` + +* Переместить файл `qbec` в директорию с исполняемыми файлами (н-р, `~/.local/bin`) + + ```shell + mv qbec ~/.local/bin + ``` + +* Не забыть удалить оставшиеся файлы + + ```shell + rm -f CHANGELOG.md LICENSE README.md jsonnet-qbec licenselint.sh qbec-linux-amd64.tar.gz + ``` + +Конфигурация `qbec` расположена в директории [project](./project). Конфигурация состоит из 4-х компонентов: + +* [backend](./project/components/backend.jsonnet): Deployment и Service для backend части приложения +* [frontend](./project/components/frontend.jsonnet): Deployment и Service для frontend части приложения +* [database](./project/components/database.jsonnet): StatefulSet базы данных вместе с некоторыми дополнительными компонентами (ConfigMap, PV, PVC) +* [local_google](./project/components/local_google.jsonnet): Endpoint для одного из ip-адресов `google.com` + +Чтобы убедится, что конфигурация в порядке, можно провести валидацию: + +```shell +qbec validate stage +qbec validate production +``` + +Затем нужно приступить к деплою приложения на окружение `stage`: + +```shell +qbec apply stage --show-details +``` + +Затем нужно убедиться, что деплой прошёл успешно: + +```shell +kubectl get po +``` + +```text +NAME READY STATUS RESTARTS AGE +backend-5b6584cddb-2sbvw 1/1 Running 0 60s +db-0 1/1 Running 0 59s +frontend-7b79b7d798-l6khv 1/1 Running 0 60s +``` + +Аналогично необходимо развернуть приложение на окружение `production`, но перед этим необходимо создать namespace с именем `production`: + +```shell +kubectl create namespace production +``` + +```shell +qbec apply production +``` + +Проверка, что все поды запущены штатно: + +```shell + kubectl --namespace=production get po +``` + +```text +NAME READY STATUS RESTARTS AGE +backend-5b6584cddb-lpp4p 1/1 Running 0 42s +backend-5b6584cddb-t8nzc 1/1 Running 0 42s +backend-5b6584cddb-w79xl 1/1 Running 0 42s +db-0 1/1 Running 0 41s +frontend-7b79b7d798-b4ct4 1/1 Running 0 42s +frontend-7b79b7d798-blnft 1/1 Running 0 42s +frontend-7b79b7d798-rbssh 1/1 Running 0 42s +```