Масштабирование Scala: как докеризировать с помощью Kubernetes
Опубликовано: 2022-03-11Kubernetes — новинка, обещающая помочь развертывать приложения в облаке и быстрее масштабировать их. Сегодня при разработке архитектуры микросервисов довольно стандартно выбирать Scala для создания серверов API.
Если в ваших планах есть приложение Scala и вы хотите масштабировать его в облако, то вы попали по адресу. В этой статье я собираюсь шаг за шагом показать, как взять стандартное приложение Scala и внедрить Kubernetes с Docker для запуска нескольких экземпляров приложения. Конечным результатом будет одно приложение, развернутое как несколько экземпляров, с балансировкой нагрузки Kubernetes.
Все это будет реализовано путем простого импорта исходного кода Kubernetes в ваше приложение Scala. Обратите внимание, комплект скрывает множество сложных деталей, связанных с установкой и настройкой, но он достаточно мал, чтобы его можно было прочитать и понять, если вы хотите проанализировать, что он делает. Для простоты мы развернем все на вашем локальном компьютере. Однако эта же конфигурация подходит для реального облачного развертывания Kubernetes.
Что такое Кубернетес?
Прежде чем углубляться в подробности реализации, давайте обсудим, что такое Kubernetes и почему это важно.
Возможно, вы уже слышали о Docker. В некотором смысле это легкая виртуальная машина.
По этим причинам он уже является одним из наиболее широко используемых инструментов для развертывания приложений в облаках. Образ Docker довольно легко и быстро создается и дублируется, что намного проще, чем традиционная виртуальная машина, такая как VMWare, VirtualBox или XEN.
Kubernetes дополняет Docker, предлагая полную среду для управления докеризованными приложениями. Используя Kubernetes, вы можете легко развертывать, настраивать, организовывать, управлять и отслеживать сотни и даже тысячи приложений Docker.
Kubernetes — это инструмент с открытым исходным кодом, разработанный Google и принятый многими другими поставщиками. Kubernetes изначально доступен на облачной платформе Google, но другие поставщики также адаптировали его для своих облачных сервисов OpenShift. Его можно найти в Amazon AWS, Microsoft Azure, RedHat OpenShift и других облачных технологиях. Можно сказать, что он имеет все шансы стать стандартом для развертывания облачных приложений.
Предпосылки
Теперь, когда мы рассмотрели основы, давайте проверим, установлено ли у вас все необходимое программное обеспечение. Прежде всего, вам нужен Docker. Если вы используете Windows или Mac, вам понадобится Docker Toolbox. Если вы используете Linux, вам необходимо установить конкретный пакет, предоставленный вашим дистрибутивом, или просто следовать официальным указаниям.
Мы собираемся кодировать на Scala, который является языком JVM. Вам, конечно, нужно, чтобы Java Development Kit и инструмент scala SBT были установлены и доступны по глобальному пути. Если вы уже являетесь программистом на Scala, скорее всего, у вас уже установлены эти инструменты.
Если вы используете Windows или Mac, Docker по умолчанию создаст виртуальную машину с именем default
только с 1 ГБ памяти, что может быть слишком мало для запуска Kubernetes. По моему опыту, у меня были проблемы с настройками по умолчанию. Я рекомендую вам открыть графический интерфейс VirtualBox, выбрать виртуальную машину default
и изменить объем памяти как минимум на 2048 МБ.
Приложение для кластеризации
Инструкции в этом руководстве могут применяться к любому приложению или проекту Scala. Чтобы в этой статье было немного «мяса» для работы, я выбрал пример, очень часто используемый для демонстрации простого микросервиса REST в Scala, который называется Akka HTTP. Я рекомендую вам попробовать применить исходный код к предложенному примеру, прежде чем пытаться использовать его в своем приложении. Я протестировал комплект на демо-приложении, но не могу гарантировать, что не будет конфликтов с вашим кодом.
Итак, сначала мы начнем с клонирования демонстрационного приложения:
git clone https://github.com/theiterators/akka-http-microservice
Далее проверьте, все ли работает правильно:
cd akka-http-microservice sbt run
Затем перейдите к http://localhost:9000/ip/8.8.8.8
, и вы должны увидеть что-то вроде следующего изображения:
Добавление исходного комплекта
Теперь мы можем добавить к исходному коду немного магии Git:
git remote add ScalaGoodies https://github.com/sciabarra/ScalaGoodies git fetch --all git merge ScalaGoodies/kubernetes
После этого у вас есть демоверсия, включая исходный код, и вы готовы попробовать. Или вы даже можете скопировать и вставить код оттуда в свое приложение.
После того, как вы объединили или скопировали файлы в свои проекты, вы готовы начать.
Запуск Kubernetes
После того, как вы загрузили комплект, нам нужно загрузить необходимый двоичный файл kubectl
, запустив:
bin/install.sh
Этот установщик достаточно умен (надеюсь), чтобы загрузить правильный двоичный файл kubectl
для OSX, Linux или Windows, в зависимости от вашей системы. Обратите внимание, установщик работал на системах, которыми я владею. Пожалуйста, сообщайте о любых проблемах, чтобы я мог исправить комплект.
После того, как вы установили бинарный файл kubectl
, вы можете запустить весь Kubernetes в локальном Docker. Просто беги:
bin/start-local-kube.sh
При первом запуске эта команда загрузит образы всего стека Kubernetes и локальный реестр, необходимый для хранения ваших образов. Это может занять некоторое время, поэтому проявите терпение. Также обратите внимание, что ему нужен прямой доступ к Интернету. Если вы находитесь за прокси, это будет проблемой, так как комплект не поддерживает прокси. Чтобы решить эту проблему, вам нужно настроить такие инструменты, как Docker, curl и т. д., для использования прокси. Это достаточно сложно, поэтому я рекомендую получить временный неограниченный доступ.
Предполагая, что вы смогли все успешно загрузить, чтобы проверить, нормально ли работает Kubernetes, вы можете ввести следующую команду:
bin/kubectl get nodes
Ожидаемый ответ:
NAME STATUS AGE 127.0.0.1 Ready 2m
Обратите внимание, что возраст может варьироваться, конечно. Кроме того, поскольку запуск Kubernetes может занять некоторое время, вам, возможно, придется несколько раз вызвать команду, прежде чем вы увидите ответ. Если вы не получаете здесь ошибок, поздравляем, Kubernetes запущен и работает на вашем локальном компьютере.
Докеризация вашего приложения Scala
Теперь, когда вы запустили и запустили Kubernetes, вы можете развернуть в нем свое приложение. Раньше, до Docker, вам приходилось развертывать целый сервер для запуска вашего приложения. В Kubernetes все, что вам нужно сделать для развертывания приложения, это:
- Создайте образ Docker.
- Вставьте его в реестр, откуда его можно будет запустить.
- Запустите инстанс с Kubernetes, который возьмет образ из реестра.
К счастью, это намного проще, чем кажется, особенно если вы используете инструмент сборки SBT, как это делают многие.
В комплект я включил два файла, содержащих все необходимые определения для создания образа, способного запускать приложения Scala, или, по крайней мере, то, что необходимо для запуска демонстрационной версии Akka HTTP. Я не могу гарантировать, что он будет работать с любыми другими приложениями Scala, но это хорошая отправная точка, и он должен работать во многих различных конфигурациях. Файлы для создания образа Docker:
docker.sbt project/docker.sbt
Давайте посмотрим, что в них. Файл project/docker.sbt
содержит команду для импорта sbt-docker
:
addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0")
Этот плагин управляет созданием образа Docker с помощью SBT. Определение Docker находится в файле docker.sbt
и выглядит следующим образом:
imageNames in docker := Seq(ImageName("localhost:5000/akkahttp:latest")) dockerfile in docker := { val jarFile: File = sbt.Keys.`package`.in(Compile, packageBin).value val classpath = (managedClasspath in Compile).value val mainclass = mainClass.in(Compile, packageBin).value.getOrElse(sys.error("Expected exactly one main class")) val jarTarget = s"/app/${jarFile.getName}" val classpathString = classpath.files.map("/app/" + _.getName) .mkString(":") + ":" + jarTarget new Dockerfile { from("anapsix/alpine-java:8") add(classpath.files, "/app/") add(jarFile, jarTarget) entryPoint("java", "-cp", classpathString, mainclass) } }
Чтобы полностью понять значение этого файла, вам нужно знать Docker достаточно хорошо, чтобы понять этот файл определения. Однако мы не будем вдаваться в подробности файла определения Docker, потому что вам не нужно досконально разбираться в нем для создания образа.

SBT позаботится о сборе всех файлов для вас.
Обратите внимание, что путь к классам автоматически создается следующей командой:
val classpath = (managedClasspath in Compile).value
В общем, довольно сложно собрать все файлы JAR для запуска приложения. Используя SBT, файл Docker будет создан с помощью add(classpath.files, "/app/")
. Таким образом, SBT собирает для вас все файлы JAR и создает Dockerfile для запуска вашего приложения.
Другие команды собирают недостающие части для создания образа Docker. Образ будет создан с использованием существующего образа APT для запуска программ Java ( anapsix/alpine-java:8
, доступный в Интернете в Docker Hub). Другие инструкции добавляют другие файлы для запуска вашего приложения. Наконец, указав точку входа, мы можем запустить его. Также обратите внимание, что имя начинается с localhost:5000
, потому что localhost:5000
— это место, где я установил реестр в сценарии start-kube-local.sh
.
Создание образа Docker с помощью SBT
Чтобы создать образ Docker, вы можете игнорировать все детали Dockerfile. Вам просто нужно ввести:
sbt dockerBuildAndPush
Затем плагин sbt-docker
создаст для вас образ Docker, загрузив из Интернета все необходимые части, а затем отправит его в ранее запущенный реестр Docker вместе с приложением Kubernetes на localhost
хосте. Итак, все, что вам нужно, это подождать еще немного, чтобы ваше изображение было приготовлено и готово.
Обратите внимание: если у вас возникли проблемы, лучше всего сбросить все до известного состояния, выполнив следующие команды:
bin/stop-kube-local.sh bin/start-kube-local.sh
Эти команды должны остановить все контейнеры и перезапустить их правильно, чтобы ваш реестр был готов к получению образа, созданного и sbt
.
Запуск службы в Kubernetes
Теперь, когда приложение упаковано в контейнер и помещено в реестр, мы готовы его использовать. Kubernetes использует командную строку и файлы конфигурации для управления кластером. Поскольку командные строки могут быть очень длинными, а также иметь возможность повторять шаги, я использую здесь файлы конфигурации. Все сэмплы исходного набора находятся в папке kube
.
Наш следующий шаг — запустить один экземпляр образа. Работающий образ на языке Kubernetes называется pod . Итак, давайте создадим модуль, вызвав следующую команду:
bin/kubectl create -f kube/akkahttp-pod.yml
Теперь вы можете проверить ситуацию с помощью команды:
bin/kubectl get pods
Тебе следует увидеть:
NAME READY STATUS RESTARTS AGE akkahttp 1/1 Running 0 33s k8s-etcd-127.0.0.1 1/1 Running 0 7d k8s-master-127.0.0.1 4/4 Running 0 7d k8s-proxy-127.0.0.1 1/1 Running 0 7d
Статус на самом деле может быть другим, например, «ContainerCreating», может пройти несколько секунд, прежде чем он станет «Выполняется». Также вы можете получить еще один статус типа «Ошибка», если, например, забыли создать образ раньше.
Вы также можете проверить, работает ли ваш модуль, с помощью команды:
bin/kubectl logs akkahttp
Вы должны увидеть вывод, заканчивающийся чем-то вроде этого:
[DEBUG] [05/30/2016 12:19:53.133] [default-akka.actor.default-dispatcher-5] [akka://default/system/IO-TCP/selectors/$a/0] Successfully bound to /0:0:0:0:0:0:0:0:9000
Теперь у вас есть сервис, работающий внутри контейнера. Однако сервис пока недоступен. Такое поведение является частью дизайна Kubernetes. Ваш модуль работает, но вы должны предоставить его явным образом. В противном случае служба должна быть внутренней.
Создание службы
Создание сервиса и проверка результата сводится к выполнению:
bin/kubectl create -f kube/akkahttp-service.yaml bin/kubectl get svc
Вы должны увидеть что-то вроде этого:
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE akkahttp-service 10.0.0.54 9000/TCP 44s kubernetes 10.0.0.1 <none> 443/TCP 3m
Обратите внимание, что порт может быть другим. Kubernetes выделил сервису порт и запустил его. Если вы используете Linux, вы можете напрямую открыть браузер и ввести http://10.0.0.54:9000/ip/8.8.8.8
, чтобы увидеть результат. Если вы используете Windows или Mac с Docker Toolbox, IP-адрес является локальным для виртуальной машины, на которой работает Docker, и, к сожалению, он по-прежнему недоступен.
Я хочу подчеркнуть здесь, что это не проблема Kubernetes, а скорее ограничение Docker Toolbox, которое, в свою очередь, зависит от ограничений, налагаемых виртуальными машинами, такими как VirtualBox, которые действуют как компьютер внутри другого компьютера. Чтобы преодолеть это ограничение, нам нужно создать туннель. Чтобы упростить задачу, я включил еще один скрипт, который открывает туннель на произвольном порту для доступа к любой развернутой нами службе. Вы можете ввести следующую команду:
bin/forward-kube-local.sh akkahttp-service 9000
Обратите внимание, что туннель не будет работать в фоновом режиме, вы должны держать окно терминала открытым столько, сколько вам нужно, и закрывать, когда туннель вам больше не нужен. Пока туннель работает, вы можете открыть: http://localhost:9000/ip/8.8.8.8
и, наконец, увидеть приложение, работающее в Kubernetes.
Последний штрих: Масштаб
До сих пор мы «просто» помещали наше приложение в Kubernetes. Хотя это захватывающее достижение, оно не добавляет слишком большой ценности нашему развертыванию. Мы избавлены от необходимости загружать и устанавливать на сервер и настраивать для него прокси-сервер.
Где Kubernetes сияет, так это в масштабировании. Вы можете развернуть два, десять или сто экземпляров нашего приложения, изменив только количество реплик в файле конфигурации. Итак, давайте сделаем это.
Мы собираемся остановить один модуль и вместо этого начать развертывание. Итак, давайте выполним следующие команды:
bin/kubectl delete -f kube/akkahttp-pod.yml bin/kubectl create -f kube/akkahttp-deploy.yaml
Далее проверьте статус. Опять же, вы можете попробовать пару раз, потому что развертывание может занять некоторое время:
NAME READY STATUS RESTARTS AGE akkahttp-deployment-4229989632-mjp6u 1/1 Running 0 16s akkahttp-deployment-4229989632-s822x 1/1 Running 0 16s k8s-etcd-127.0.0.1 1/1 Running 0 6d k8s-master-127.0.0.1 4/4 Running 0 6d k8s-proxy-127.0.0.1 1/1 Running 0 6d
Теперь у нас две капсулы, а не одна. Это связано с тем, что в предоставленном мной файле конфигурации есть значение replica: 2
с двумя разными именами, сгенерированными системой. Я не буду вдаваться в детали конфигурационных файлов, потому что цель статьи — просто введение для программистов Scala, чтобы быстро начать работу с Kubernetes.
Во всяком случае, сейчас активны два модуля. Что интересно, сервис такой же, как и раньше. Мы настроили сервис для балансировки нагрузки между всеми модулями с akkahttp
. Это означает, что нам не нужно повторно развертывать службу, но мы можем заменить один экземпляр реплицированным.
Убедиться в этом мы можем, снова запустив прокси (если у вас Windows и вы его закрыли):
bin/forward-kube-local.sh akkahttp-service 9000
Затем мы можем попытаться открыть два окна терминала и просмотреть журналы для каждого модуля. Например, в первом типе:
bin/kubectl logs -f akkahttp-deployment-4229989632-mjp6u
И во втором типе:
bin/kubectl logs -f akkahttp-deployment-4229989632-s822x
Конечно, отредактируйте командную строку в соответствии со значениями, которые есть в вашей системе.
Теперь попробуйте получить доступ к сервису с помощью двух разных браузеров. Вы должны ожидать, что запросы будут разделены между несколькими доступными серверами, как показано на следующем изображении:
Заключение
Сегодня мы едва поцарапали поверхность. Kubernetes предлагает гораздо больше возможностей, включая автоматическое масштабирование и перезапуск, добавочные развертывания и тома. Кроме того, приложение, которое мы использовали в качестве примера, очень простое, без сохранения состояния, а различные экземпляры не должны знать друг друга. В реальном мире распределенным приложениям необходимо знать друг друга и изменять конфигурации в соответствии с доступностью других серверов. Действительно, Kubernetes предлагает распределенное хранилище ключей ( etcd
), позволяющее различным приложениям взаимодействовать друг с другом при развертывании новых экземпляров. Тем не менее, этот пример преднамеренно сделан достаточно маленьким и упрощенным, чтобы помочь вам приступить к работе, сосредоточив внимание на основных функциях. Если вы будете следовать руководству, вы сможете получить рабочую среду для своего приложения Scala на своем компьютере, не запутавшись в большом количестве деталей и не потерявшись в сложности.
- Разработка для облака в облаке: разработка больших данных с помощью Docker в AWS
- K8s/Kubernetes: AWS, GCP и Azure
- Сравнение сервисной сетки Kubernetes