Развертывание и масштабирование микросервисов Kubernetes
Kubernetes – открытый инструмент для оркестровки и управления контейнерными приложениями. В предыдущем мануале этой серии – Основы работы с Kubernetes – вы узнали об основных компонентах Кубернетеса.
В этом мануале вы сможете применить полученные знания из предыдущих руководств для создания, развертывания и управления комплексного микросервиса в Kubernetes. Тестовое веб-приложение, которое вы будете использовать в этом мануале, представляет собой простой список дел, написанный в Node.js, который использует БД MongoDB. Ранее это же приложение вы использовали в мануале Создание контейнеризированного приложения Docker.
Требования
- Кластер Kubernetes (как в мануале Начало работы с Kubernetes).
- Активный аккаунт Docker Hub для хранения образов.
- Система контроля версий Git (установить и настроить ее вам поможет этот мануал).
1: Создание образа с помощью Dockerfile
Для начала нужно создать контейнер для приложения, упаковав его в образ Docker.
Перейдите в домашний каталог, а затем используйте Git, чтобы клонировать тестовое приложение из официального репозитория GitHub:
cd ~
git clone https://github.com/janakiramm/todo-app.git
Создайте образ контейнера из Dockerfile. Используйте флаг -t, чтобы добавить к образу метку, которая включает имя пользователя реестра, имя образа и (опционально) описательный тег.
docker build -t 8host/todo .
Результат сообщает, что образ было успешно собран и помечен соответствующим образом.
Sending build context to Docker daemon 8.238MB
Step 1/7 : FROM node:slim
---> 286b1e0e7d3f
Step 2/7 : LABEL maintainer = "jani@janakiram.com"
---> Using cache
---> ab0e049cf6f8
Step 3/7 : RUN mkdir -p /usr/src/app
---> Using cache
---> 897176832f4d
Step 4/7 : WORKDIR /usr/src/app
---> Using cache
---> 3670f0147bed
Step 5/7 : COPY ./app/ ./
---> Using cache
---> e28c7c1be1a0
Step 6/7 : RUN npm install
---> Using cache
---> 7ce5b1d0aa65
Step 7/7 : CMD node app.js
---> Using cache
---> 2cef2238de24
Successfully built 2cef2238de24
Successfully tagged 8host/todo-app:latest
Убедитесь, что образ был собран правильно.
docker images
Вы увидите размер образа и время его создания.
REPOSITORY TAG IMAGE ID CREATED SIZE
8host/todo-app latest 81f5f605d1ca 9 minutes ago 236MB
Затем загрузите образ в публичный реестр на Docker Hub. Для этого войдите в учетную запись Docker Hub:
docker login
После того, как вы предоставите свои учетные данные, пометьте образ, указав имя пользователя Docker Hub:
docker tag your_docker_hub_username/todo-app
Теперь загрузите образ:
docker push
Вы можете убедиться, что новый образ появился в реестре, выполнив поиск по Docker Hub в веб-браузере.
Когда образ Docker переместится в реестр, упакуйте приложение для Kubernetes.
2: Развертывание пода MongoDB в Kubernetes
Приложение использует MongoDB для хранения списков дел, созданных пользователем. Чтобы запустить MongoDB в Kubernetes, нужно упаковать его как под. Когда вы запустите этот под, он запустит один экземпляр MongoDB.
Создайте новый файл YAML db-pod.yaml:
nano db-pod.yaml
Добавьте следующий код, который определяет под с одним контейнером на основе MongoDB. Откройте порт 27017, стандартный порт MongoDB. Обратите внимание, определение содержит метки name и app. Вы сможете использовать эти метки для определения и настройки определенных подов.
apiVersion: v1
kind: Pod
metadata:
name: db
labels:
name: mongo
app: todoapp
spec:
containers:
- image: mongo
name: mongo
ports:
- name: mongo
containerPort: 27017
volumeMounts:
- name: mongo-storage
mountPath: /data/db
volumes:
- name: mongo-storage
hostPath:
path: /data/db
Данные хранятся в томе под названием mongo-storage, который связан с каталогом /data/db ноды. Дополнительную информацию о томах можно найти в документации Kubernetes.
Выполните следующую команду для создания пода.
kubectl create -f db-pod.yml
pod "db" created
Проверьте под:
kubectl get pods
В выводе вы увидите, что под существует и работает.
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 2m
Сделайте этот под доступным для внутренних потребителей кластера.
Создайте новый файл db-service.yaml, который содержит такой код сервиса для MongoDB:
apiVersion: v1
kind: Service
metadata:
name: db
labels:
name: mongo
app: todoapp
spec:
selector:
name: mongo
type: ClusterIP
ports:
- name: db
port: 27017
targetPort: 27017
Сервис обнаруживает в пространстве имен все поды, которые соответствуют метке name: db. Раздел selector явно определяет эту ассоциацию.
Сервис виден внутри кластера через декларацию type: ClusterIP.
Сохраните файл и закройте редактор. Затем добавьте файл в кластер:
kubectl create -f db-service.yml
Если сервис успешно добавлен в кластер, вы увидите:
service "db" created
Узнайте порт, по которому доступен под.
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db ClusterIP 10.109.114.243 <none> 27017/TCP 14s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 47m
Согласно выводу сервис использует порт 27017. Веб-приложение может получить доступ к MongoDB через этот сервис. Когда он использует имя хоста db, DNS-сервис Kubernetes будет разрешать адрес ClusterIP, связанный с сервисом. Этот механизм позволяет подам обнаруживать друг друга и взаимодействовать.
3: Развертывание приложения Node.JS как пода
Упакуйте созданный ранее образ Docker в качестве пода и разверните его в кластере. Он будет работать как фронт-энд приложения, доступный конечным пользователям.
Создайте новый файл YAML по имени web-pod.yaml:
nano web-pod.yaml
Добавьте следующий код, который определяет опд с одним контейнером на основе образа Docker 8host/todo-app. Он работает по порту 3000 и протоколу TCP.
apiVersion: v1
kind: Pod
metadata:
name: web
labels:
name: web
app: todoapp
spec:
containers:
- image: 8host/todo-app
name: myweb
ports:
- containerPort: 3000
Обратите внимание, определение содержит метки name и app. Сервис будет использовать эти метки для маршрутизации входящего трафика на соответствующие порты.
Выполните следующую команду для создания пода:
kubectl create -f web-pod.yaml
pod "web" created
Проверьте новый под:
kubectl get pods
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 8m
web 1/1 Running 0 9s
Обратите внимание, что в списке есть и под базы данных MongoDB, и под веб-приложения.
Теперь нужно сделать под приложения web доступным в интернете.
Сервисы позволяют открывать к подам как внешний, так и внутренний доступ. Определите сервис, который сделает под web доступным. Откройте его через NodePort, схему, которая сделает поды доступными через произвольный порт, открытый на каждой ноде кластера.
Создайте новый файл web-service.yaml и добавьте в него определение сервиса:
apiVersion: v1
kind: Service
metadata:
name: web
labels:
name: web
app: todoapp
spec:
selector:
name: web
type: NodePort
ports:
- name: http
port: 3000
targetPort: 3000
protocol: TCP
Сервис обнаруживает в пространстве имен все поды, которые соответствуют метке web. Раздел selector явно определяет эту ассоциацию.
Сервис имеет тип NodePort, который объявляется строкой type: NodePort.
Передайте сервис в кластер:
kubectl create -f web-service.yml
service "web" created
Теперь узнайте, какой порт использует под:
kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
db ClusterIP 10.109.114.243 <none> 27017/TCP 12m
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 59m
web NodePort 10.107.206.92 <none> 3000:30770/TCP 12s
Сервис доступен по порту 30770. Попробуйте подключиться к одной из рабочих нод.
Получите открытый IP-адрес рабочей ноды, связанной с кластером Kubernetes, используя консоль хостинг-провайдера.
Узнав IP-адрес, используйте команду curl, чтобы отправить HTTP-запрос одной из нод на порт 30770:
curl http://your_worker_ip_address:30770
Вы получите примерно такой вывод:
<!DOCTYPE html>
<html>
<head>
<title>Containers Todo Example</title>
<link rel='stylesheet' href='/stylesheets/screen.css' />
<!--[if lt IE 9]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<div id="layout">
<h1 id="page-title">Containers Todo Example</h1>
<div id="list">
<form action="/create" method="post" accept-charset="utf-8">
<div class="item-new">
<input class="input" type="text" name="content" />
</div>
</form>
</div>
<div id="layout-footer"></div>
</div>
<script src="/javascripts/ga.js"></script>
</body>
</html>
4: Масштабирование веб-приложения
Набор реплик гарантирует, что в кластере будет работать минимальное количество подов. Если под упакован как набор реплик, Kubernetes всегда будет поддерживать минимальное количество подов, определенное в спецификации.
Удалите текущий под и воссоздайте два пода через набор реплик. Если вы оставите этот под в работе, он не будет частью набора реплик. Потому нужно запускать поды через наборы реплик, даже если в наборе будет только один под.
Удалите существующий под:
kubectl delete pod web
pod "web" deleted
Теперь создайте новую декларацию набора реплик. Определение набора реплик идентично определению пода. Ключевое отличие состоит в том, что оно содержит элемент replica, который определяет количество подов, которое нужно запустить. Как и под, набор также содержит метки в качестве метаданных, которые помогают в обнаружении сервисов.
Создайте файл web-rs.yaml и вставьте в него код:
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
name: web
labels:
name: web
app: todoapp
spec:
replicas: 2
template:
metadata:
labels:
name: web
spec:
containers:
- name: web
image: 8host/todo-app
ports:
- containerPort: 3000
Сохраните и закройте файл.
Создайте набор реплик:
kubectl create -f web-rs.yaml
replicaset "web" created
Проверьте количество подов:
kubectl get pods
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 18m
web-n5l5h 1/1 Running 0 25s
web-wh6nf 1/1 Running 0 25s
Когда вы обращаетесь к сервису через NodePort, запрос отправляется на один из подов, управляемых набором реплик.
Давайте проверим функциональность набора реплик, удалив один из подов, и посмотрим, что произойдет:
kubectl delete pod web-wh6nf
pod "web-wh6nf" deleted
Снова проверьте поды:
kubectl get pods
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 19m
web-n5l5h 1/1 Running 0 1m
web-wh6nf 1/1 Terminating 0 1m
web-ws59m 0/1 ContainerCreating 0 2s
Как только под удаляется, Kubernetes создает новый под, чтобы поддерживать заданное минимальное количество подов.
Вы можете масштабировать набор реплик для запуска дополнительных подов.
Выполните следующую команду, чтобы масштабировать веб-приложение до 10 подов.
kubectl scale rs/web --replicas=10
replicaset "web" scaled
Проверьте количество подов:
kubectl get pods
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 22m
web-4nh4g 1/1 Running 0 21s
web-7vbb5 1/1 Running 0 21s
web-8zd55 1/1 Running 0 21s
web-f8hvq 0/1 ContainerCreating 0 21s
web-ffrt6 1/1 Running 0 21s
web-k6zv7 0/1 ContainerCreating 0 21s
web-n5l5h 1/1 Running 0 3m
web-qmdxn 1/1 Running 0 21s
web-vc45m 1/1 Running 0 21s
web-ws59m 1/1 Running 0 2m
Kubernetes инициировал процесс масштабирования пода web. Когда запрос приходит на сервис через NodePort, он маршрутизируется на один из подов в наборе реплик.
Когда трафик и загрузка снижаются, вы можете вернуться к исходной конфигурации из двух подов.
kubectl scale rs/web --replicas=2
replicaset "web" scaled
Эта команда прерывает работу всех подов, оставляя только два из них:
kubectl get pods
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 24m
web-4nh4g 1/1 Terminating 0 2m
web-7vbb5 1/1 Terminating 0 2m
web-8zd55 1/1 Terminating 0 2m
web-f8hvq 1/1 Terminating 0 2m
web-ffrt6 1/1 Terminating 0 2m
web-k6zv7 1/1 Terminating 0 2m
web-n5l5h 1/1 Running 0 5m
web-qmdxn 1/1 Terminating 0 2m
web-vc45m 1/1 Terminating 0 2m
web-ws59m 1/1 Running 0 4m
Чтобы проверить доступность набора реплик, попробуйте удалить один из подов и проверить их количество.
kubectl delete pod web-ws59m
pod "web-ws59m" deleted
kubectl get pods
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 25m
web-n5l5h 1/1 Running 0 7m
web-ws59m 1/1 Terminating 0 5m
web-z6r2g 0/1 ContainerCreating 0 5s
Как только количество подов уменьшилось, Kubernetes восстановил его, чтобы соответствовать требованиям файла YAML. Когда один из подов в наборе реплик удаляется, создается другой под. Это обеспечивает высокую доступность приложения, гарантируя работу установленного в параметрах количества подов.
Удалите все объекты, созданные в этом мануале:
kubectl delete -f db-pod.yaml -f db-service.yaml -f web-rs.yaml -f web-service.yaml
pod "db" deleted
service "db" deleted
replicaset "web" deleted
service "web" deleted
Заключение
Теперь вы умеете работать с микросервисами.
В следующем мануале этой серии вы узнаете, как настроить высокую доступность MongoDB с помощью StatefulSet.
Tags: Docker, Kubernetes, MongoDB, Node.js