Организация стенда для локальной разработки с помощью werf: автоматизируем пересборку приложения с фронтендом и бэкендом

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Вносить изменения в код приложения и тут же автоматически получать задеплоенные изменения, чтобы быстро тестировать его, — мечта разработчика. В этой статье мы посмотрим, как реализовать такой подход для небольшого приложения с фронтендом и бэкендом: организуем два варианта локального стенда на базе minikube или Docker с автоматическим развертыванием всех изменений или только закоммиченых в Git. Бэкенд приложения напишем на Go, а фронтенд — на Vue.js. Все это позволит быстро запускать проект для тестирования прямо во время разработки, что, несомненно, повысит удобство работы с приложением.

На старте вам понадобится установленная утилита werf (инструкция по установке есть на официальном сайте) — с ее помощью мы и будем максимально удобно организовывать автоматический деплой всех изменений. Всё остальное поставим в процессе. 

В предыдущей статье мы рассмотрели, как собрать и развернуть приложение в кластере под управлением платформы Deckhouse с помощью утилиты werf. Сегодня в качестве следующего шага мы организуем локальный стенд для разработки, позволяющий с помощью werf настроить автоматическую сборку приложения на машине разработчика. Вам необязательно читать и выполнять инструкции из предыдущей статьи — мы всё построили так, чтобы эта статья была самодостаточной.

Разработка приложения

У нас уже есть шаблон небольшого приложения — это простой веб-сервис, в котором можно написать сообщение, сохранить его в базу данных и отобразить сохраненные ранее сообщения. Пока оно представляет из себя монолитный бэкенд на Go с подготовленными для деплоя в кластер Kubernetes Helm-чартами. В приложении реализована как логика работы с БД для сохранения и получения собщений, так и шаблонизация HTML-страниц и их отображение пользователю по соответствующим эндпоинтам — то есть нет жесткого разделения на бэкенд и фронтенд.

Далее мы немного доработаем приложение, разбив его на отдельные сервисы фронтенда и бэкенда: вынесем весь интерфейс во фронтенд, а в бэкенде оставим только возможность отвечать на REST-запросы. Кроме того, мы добавим чарты для новых сервисов и настроим запуск приложения в Docker Compose на локальной машине разработчика. Это позволит сделать наше приложение более интерактивным с точки зрения пользователя и более «сложным» для развертывания — ведь фронтенд и бэкенд теперь будут развертываться как отдельные сервисы, работающие параллельно и отвечающие за запросы по одному доменному имени. В итоге наша задача по развертыванию получится максимально приближенной к реальной жизни. 

Реорганизация проекта

Создадим новый каталог, в котором будем разрабатывать приложение. Для начала скопируем содержимое оригинального проекта в подкаталог backend нового проекта. В нем будет храниться только то, что отвечает за работу с БД и организацию REST API. 

Фронтенд приложения расположится в подкаталоге frontend корневого каталога, но создавать его пока не нужно: мы сделаем это на стадии подготовки нового приложения на Vue.js. 

В итоге у приложения должна получиться следующая файловая структура:

$ tree -a .
.
└── backend
   ├── .helm
   │   └── templates
   │       ├── database.yaml
   │       ├── deployment.yaml
   │       ├── ingress.yaml
   │       ├── job-db-setup-and-migrate.yaml
   │       └── service.yaml
   ├── Dockerfile
   ├── cmd
   │   └── main.go
   ├── db
   │   └── migrations
   │       ├── 000001_create_talkers_table.down.sql
   │       └── 000001_create_talkers_table.up.sql
   ├── go.mod
   ├── go.sum
   ├── internal
   │   ├── app
   │   │   └── app.go
   │   ├── common
   │   │   └── json_logger_filter.go
   │   ├── controllers
   │   │   └── db_controllers.go
   │   └── services
   │       └── db_service.go
   ├── templates
   │   ├── index.html
   │   ├── remember.html
   │   └── say.html
   └── werf.yaml

Разработка бэкенда приложения

В нашем исходном приложении реализованы генерация страниц и передача их браузеру пользователя. В новой версии за веб-представление приложения будет отвечать отдельный сервис на Vue.js, поэтому давайте уберем из бэкенда все, что связано с обработкой шаблонов, а ответы будем передавать не в HTML, а в формате JSON — как взрослые программисты :)

Для начала удалим в файле internal/app/app.go эндпоинт /, отвечающий за главную страницу приложения, так как за него теперь будет отвечать фронтенд. К эндпоинотам remember и say, которые отвечают за сохранение и отображение сообщений из БД, добавим подпуть /api (должно получиться вот так: remember/api, say/api), который пригодится для настройки распределения запросов по нужным путям при развертывании в кластере и Docker Compose в процессе организации локального стенда. После этого у нас получится следующий код функции инициализации сервера:

func Run() {
	route := gin.New()
	route.Use(gin.Recovery())
	route.Use(common.JsonLogger())


	route.GET("/api/remember", controllers.RememberController)
	route.GET("/api/say", controllers.SayController)


	err := route.Run()
	if err != nil {
		return
	}
}

Примечание

Лучше заранее продумать, как будут группироваться эндпоинты приложения. Такая предусмотрительность позволит сразу организовать версионность и обратную совместимость API. Например, при изменении мажорной версии приложения все новые методы API будут доступны по пути /api/v2, а старые останутся на /api/v1.

Заменим в контроллерах тип ответа с HTML на JSON, оставив его содержимое в неизменном виде.

Ниже представлен контроллер сохранения данных, расположенный в файле internal/controllers/db_controllers.go:

func RememberController(c *gin.Context) {
	dbType, dbPath := services.GetDBCredentials()


	db, err := sql.Open(dbType, dbPath)
	if err != nil {
		panic(err)
	}


	message := c.Query("message")
	name := c.Query("name")


	_, err = db.Exec("INSERT INTO talkers (message, name) VALUES (?, ?)",
		message, name)
	if err != nil {
		panic(err)
	}


	c.JSON(http.StatusOK, gin.H{
		"Name":    name,
		"Message": message,
	})


	defer db.Close()
}

В нем все осталось как прежде: получение данных от формы на сайте, переданных по GET-запросу, и их сохранение в БД. Изменен только ответ с вызова шаблона HTML на генерацию JSON (второй блок кода, если считать снизу, который теперь начинается с c.JSON).

Код контроллера чтения данных из БД расположен в том же файле и выглядит теперь так:

func SayController(c *gin.Context) {
	dbType, dbPath := services.GetDBCredentials()


	db, err := sql.Open(dbType, dbPath)
	if err != nil {
		panic(err)
	}


	result, err := db.Query("SELECT * FROM talkers")
	if err != nil {
		panic(err)
	}


	count := 0
	var data []map[string]string


	for result.Next() {
		count++
		var id int
		var message string
		var name string


		err = result.Scan(&id, &message, &name)
		if err != nil {
			panic(err)
		}


		data = append(data, map[string]string{
			"Name":    name,
			"Message": message})
	}
	if count == 0 {
		c.JSON(http.StatusOK, gin.H{
			"Error": "There are no messages from talkers!",
		})
	} else {
		c.JSON(http.StatusOK, gin.H{
			"Messages": data,
		})
	}
}

Здесь все также осталось практически без изменений — мы лишь заменили HTML на JSON (два последних блока кода, теперь они начинаются с c.JSON).

Ура! Отделение бэкенда завершено.

Можно проверить, что все работает, перейдя в терминале в каталог backend и развернув приложение в кластере:

werf converge --repo <ИМЯ ПОЛЬЗОВАТЕЛЯ DOCKER HUB>/habr-app

После развертывания проверим, что по эндпоинту /say данные возвращаются из БД в виде JSON-файла следующего содержания:

$ curl https://habrapp.example.com/say | jq

{
  "Messages": [
    {
      "Message": "Привет, Хабр!",
      "Name": "Zhbert"
    }
  ]
}

Если вы разворачивали кластер с этим приложением по инструкции из предыдущей статьи этого цикла, то данные уже должны быть в базе — после тестирования первой версии приложения. В противном случае вам также вернется JSON, но с ошибкой, которая будет ругаться на отсутствие данных — это нормально, не пугайтесь.

Заметим, что, так как мы удалили из приложения генерацию главной страницы, при запросе на / возвращается 404-я ошибка, как в примере ниже:

$ curl https://habrapp.example.com

404 page not found

В дальнейшем мы это исправим, так что не обращайте внимания.

Перенесем из каталога backend файл werf.yaml на уровень выше — в корневой каталог проекта, так как теперь он будет отвечать за сборку сразу двух контейнеров вместо одного.

Ну что ж, можно приступать к созданию фронтенда.

Разработка фронтенда приложения

В этой части мы разработаем веб-приложение на Vue.js, которое будет отвечать за фронтенд нашего проекта: отображать страницы, а также отправлять и получать данные от бэкенда.

Установите Vue CLI на вашу машину. Лучше сразу делать это через npm, так как в дальнейшем он нам еще понадобится.

Теперь инициализируем новое Vue-приложение в корневом каталоге нашего проекта, выполнив соответствующую команду:

vue create frontend

В ответ на предложение определиться с версией фреймворка выберем вариант по умолчанию:

> Default ([Vue 3] babel, eslint

Процесс подготовки займет некоторое время в зависимости от скорости интернет-соединения. В результате отобразятся статус созданного проекта и подсказка о том, как его запустить локально:

Vue CLI v5.0.8
✨  Creating project in /Users/zhbert/Programming/habr-werf-local-stend/frontend.
⚙️  Installing CLI plugins. This might take a while...

added 862 packages, and audited 863 packages in 24s

packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities
						
Источник: https://habr.com/ru/companies/flant/articles/771678/


Интересные статьи

Интересные статьи

В данной статье мы хотим осветить особенности организации безопасности в облаках и рассказать, как CSPM продукты помогают автоматизировать процесс обеспечения безопасности в рамках методологии DevSecO...
Установка удалённо управляемой браузерной системы (ReCoBS) — это один из способов создания безопасной среды веб-сёрфинга для ваших пользователей, использующих Windows Server с бесшовным окном браузе...
Искусственный интеллект и связанные с ним технологии плотно вошли в нашу жизнь: с его помощью музыкальные сервисы рекомендуют нам музыку, черно-белые фильмы становятся цветными, а на просторах интерне...
Привет, Хабр! На связи Гончик, любитель APM (application performance monitoring) инструментов, в частности Glowroot. Сегодня расскажу о том, как за кратчайшее время найти узкие места в Confl...
Задумывались ли вы о том, как повысить CLV (показатель пожизненной ценности клиента) в своем проекте? Сегодня мы на примерах кейсов расскажем, как формирование эффективного канала коммуникации с ауди...