Файлы, упомянутые в главе
- .helm/templates/deployment.yaml
- .helm/requirements.yaml
- .helm/values.yaml
- .helm/secret-values.yaml
- app.js
- .gitlab-ci.yml
В этой главе мы настроим в нашем базовом приложении работу с простейшей базой данных типа in-memory — Redis (другим популярным примером является memcached). Это означает, что база данных будет stateless.
В простейшем случае нет необходимости вносить изменения в сборку: уже собранные образы есть на DockerHub. Надо просто выбрать правильный образ, корректно сконфигурировать его в своей инфраструктуре, а потом подключиться к базе данных из Node.js-приложения.
Сконфигурировать Redis в Kubernetes
Для того, чтобы сконфигурировать Redis в кластере, необходимо определить объекты с помощью Helm. Мы можем сделать это самостоятельно, но рассмотрим вариант с подключением внешнего чарта. В любом случае потребуется указать: имя сервиса, порт, логин и пароль — и разобраться, как эти параметры пробросить в подключенный внешний чарт.
Итак, нам понадобится:
- Указать Redis как зависимый сабчарт в
requirements.yaml
; - Сконфигурировать в werf работу с зависимостями;
- Сконфигурировать подключённый сабчарт;
- Убедиться, что создаётся кластер Redis в конфигурации master-slave.
Пропишем сабчарт с Redis:
dependencies:
<...>
- name: redis
version: 9.3.2
repository: https://kubernetes-charts.storage.googleapis.com/
condition: redis.enabled
Для того, чтобы werf при деплое загрузил необходимые нам сабчарты, нужно прописать в .gitlab-ci.yml
работу с зависимостями:
.base_deploy: &base_deploy
stage: deploy
script:
- werf helm repo init
- werf helm dependency update
- werf deploy --set "global.ci_url=$(cut -d / -f 3 <<< $CI_ENVIRONMENT_URL)"
… а также сконфигурировать имя сервиса, порт, логин и пароль, согласно документации сабчарта:
redis:
fullnameOverride: redis
nameOverride: redis
redis:
password: "LYcj6c09D9M4htgGh64vXLxn95P4Wt"
Сконфигурировать логин и порт для подключения у этого сабчарта невозможно, но если изучить исходный код — можно найти значения, использующиеся в сабчарте. Пропишем нужные значения с понятными нам ключами — они понадобятся позже, когда будем конфигурировать приложение.
redis:
<...>
_port:
_default: 6379
Если посмотреть на рендер (werf helm render
) нашего приложения с включенным сабчартом для Redis, то можем увидеть, какие будут созданы объекты типа Service:
# Source: example_4/charts/redis/templates/redis-master-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: guided-redis-master
# Source: example_4/charts/redis/templates/redis-slave-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: guided-redis-slave
Знание этих Services требуется, чтобы потом к ним подключаться.
Подключение Node.js-приложения к базе Redis
В нашем приложении мы будем подключаться к master-узлу Redis. Необходимо, чтобы приложение при выкате в любое окружение подключалось к правильному Redis.
В нашем случае Redis будет использоваться как хранилище сессий.
// Get configuration from envs
const REDIS_URI = "redis://"+ "root"+":" + process.env.REDIS_PASSWORD +"@"+process.env.REDIS_HOST+":"+ process.env.REDIS_PORT || "redis://127.0.0.1:6379";
const SESSION_TTL = process.env.SESSION_TTL || 3600;
// Init ExpressJS
var express = require('express');
var app = express();
// Redis
const redis = require("redis");
let redisClient = redis.createClient(REDIS_URI);
// Session
const expSession = require("express-session");
// Connect sessions to Redis & configure
let redisStore = require("connect-redis")(expSession);
var session = expSession({
secret: "keyboard cat", // TODO: CHANGE ME! 'keyboard cat' is insecure!
resave: false,
saveUninitialized: false,
store: new redisStore({ client: redisClient, ttl: SESSION_TTL }),
});
app.use(session);
console.log(`Connect to REDIS ${REDIS_URI}`);
app.get('/', function (req, res) {
if (req.session.views) {
req.session.views++
res.setHeader('Content-Type', 'text/html')
res.write('<p>views: ' + req.session.views + '</p>')
res.write('<p>expires in: ' + (req.session.cookie.maxAge / 1000) + 's</p>')
res.end()
} else {
req.session.views = 1
res.end('welcome to the session demo. refresh!')
}
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
Для подключения к базе данных нам, очевидно, нужно знать: хост, порт, логин, пароль. В коде приложения мы используем несколько переменных окружения: REDIS_HOST
, REDIS_PORT
, REDIS_LOGIN
, REDIS_PASSWORD
,SESSION_TTL
, COOKIE_SECRET
. Часть значений уже определена в values.yaml
для подключаемого сабчарта. Можно воспользоваться теми же значениями и дополнить их.
Будем конфигурировать хост через values.yaml
:
- name: REDIS_HOST
value: {{ pluck .Values.global.env .Values.redis.host | first | default .Values.redis.host._default | quote }}
redis:
<...>
host:
_default: redis-master
Конфигурируем логин и порт через values.yaml
, просто прописывая значения:
- name: REDIS_PORT
value: {{ pluck .Values.global.env .Values.redis._port | first | default .Values.redis._port._default | quote }}
Мы уже сконфигурировали пароль — используем прописанное ранее значение:
- name: REDIS_PASSWORD
value: {{ .Values.redis.password | quote }}
Также нам нужно сконфигурировать переменные, необходимые приложению для работы с Redis:
- name: SESSION_TTL
value: {{ pluck .Values.global.env .Values.app.redis.session_ttl | first | default .Values.app.redis.session_ttl._default | quote }}
redis:
session_ttl:
_default: "3600"
cookie_secret:
_default: "supersecret"