Scalaのスケーリング:Kubernetesを使用してDocker化する方法
公開: 2022-03-11Kubernetesはブロックの新しい子供であり、アプリケーションをクラウドにデプロイし、それらをより迅速にスケーリングするのに役立つことを約束しています。 今日、マイクロサービスアーキテクチャ用に開発する場合、APIサーバーの作成にScalaを選択するのはかなり標準的です。
計画にScalaアプリケーションがあり、それをクラウドにスケーリングしたい場合は、適切な場所にいます。 この記事では、汎用のScalaアプリケーションを取得し、DockerでKubernetesを実装して、アプリケーションの複数のインスタンスを起動する方法を段階的に説明します。 最終的には、単一のアプリケーションが複数のインスタンスとしてデプロイされ、Kubernetesによって負荷分散されます。
これらはすべて、ScalaアプリケーションにKubernetesソースキットをインポートするだけで実装されます。 キットには、インストールと構成に関連する多くの複雑な詳細が隠されていることに注意してください。ただし、キットの機能を分析する場合は、読みやすく、理解しやすいほど小さいです。 簡単にするために、すべてをローカルマシンにデプロイします。 ただし、同じ構成がKubernetesの実際のクラウドデプロイメントに適しています。
Kubernetesとは何ですか?
実装の厄介な詳細に入る前に、Kubernetesとは何か、なぜそれが重要なのかについて説明しましょう。
Dockerについてはすでに聞いたことがあるかもしれません。 ある意味、軽量の仮想マシンです。
これらの理由から、これはすでにクラウドにアプリケーションをデプロイするために広く使用されているツールの1つです。 Dockerイメージは、VMWare、VirtualBox、XENなどの従来の仮想マシンよりもはるかに簡単に構築および複製できます。
KubernetesはDockerを補完し、Docker化されたアプリケーションを管理するための完全な環境を提供します。 Kubernetesを使用すると、数百または数千ものDockerアプリケーションを簡単にデプロイ、構成、オーケストレーション、管理、監視できます。
KubernetesはGoogleが開発したオープンソースツールであり、他の多くのベンダーに採用されています。 KubernetesはGoogleクラウドプラットフォームでネイティブに利用できますが、他のベンダーもOpenShiftクラウドサービスに採用しています。 これは、Amazon AWS、Microsoft Azure、RedHat OpenShift、およびさらに多くのクラウドテクノロジーで見つけることができます。 クラウドアプリケーションを展開するための標準になるのに適した位置にあると言えます。
前提条件
基本を説明したので、前提条件のソフトウェアがすべてインストールされているかどうかを確認しましょう。 まず、Dockerが必要です。 WindowsまたはMacを使用している場合は、DockerToolboxが必要です。 Linuxを使用している場合は、ディストリビューションが提供する特定のパッケージをインストールするか、公式の指示に従う必要があります。
JVM言語であるScalaでコーディングします。 もちろん、Java DevelopmentKitとscalaSBTツールがインストールされ、グローバルパスで利用可能である必要があります。 すでにScalaプログラマーである場合は、それらのツールがすでにインストールされている可能性があります。
WindowsまたはMacを使用している場合、Dockerはdefault
でデフォルトという名前の仮想マシンを作成します。メモリは1 GBのみで、Kubernetesを実行するには小さすぎる可能性があります。 私の経験では、デフォルト設定に問題がありました。 VirtualBox GUIを開き、仮想マシンのdefault
を選択して、メモリを少なくとも2048MBに変更することをお勧めします。
クラスター化するアプリケーション
このチュートリアルの手順は、すべてのScalaアプリケーションまたはプロジェクトに適用できます。 この記事で取り組むべき「肉」を持たせるために、AkkaHTTPと呼ばれるScalaの単純なRESTマイクロサービスを示すために頻繁に使用される例を選択しました。 アプリケーションで使用する前に、提案された例にソースキットを適用することをお勧めします。 デモアプリケーションに対してキットをテストしましたが、コードとの競合がないことを保証することはできません。
まず、デモアプリケーションのクローンを作成することから始めます。
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
このインストーラーは、システムに応じて、OSX、Linux、またはWindows用の正しいkubectl
バイナリをダウンロードするのに十分賢いです(うまくいけば)。 インストーラーは私が所有するシステムで動作したことに注意してください。 キットを修正できるように、問題があれば報告してください。
kubectl
バイナリをインストールすると、ローカルDockerでKubernetes全体を起動できます。 ただ走れ:
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アプリをDocker化する
Kubernetesが稼働しているので、アプリケーションをデプロイできます。 昔は、Dockerが登場する前は、アプリケーションを実行するためにサーバー全体をデプロイする必要がありました。 Kubernetesを使用すると、アプリケーションをデプロイするために必要なことは次のとおりです。
- Dockerイメージを作成します。
- 起動できるレジストリにプッシュします。
- Kubernetesを使用してインスタンスを起動すると、レジストリからイメージが取得されます。
幸いなことに、特に多くの場合のようにSBTビルドツールを使用している場合は、見た目ほど複雑ではありません。
キットには、Scalaアプリケーションを実行できるイメージを作成するために必要なすべての定義、または少なくともAkkaHTTPデモを実行するために必要なものをすべて含む2つのファイルが含まれています。 他のScalaアプリケーションで動作することを保証することはできませんが、これは良い出発点であり、多くの異なる構成で動作するはずです。 Dockerイメージのビルドを探すためのファイルは次のとおりです。
docker.sbt project/docker.sbt
それらの中にあるものを見てみましょう。 ファイルproject/docker.sbt
には、 sbt-docker
プラグインをインポートするコマンドが含まれています。
addSbtPlugin("se.marcuslonnberg" % "sbt-docker" % "1.4.0")
このプラグインは、SBTを使用したDockerイメージの構築を管理します。 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定義ファイルを完全に理解する必要がないため、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
スクリプトでレジストリをインストールした場所であるためです。
SBTを使用したDockerイメージの構築
Dockerイメージをビルドするには、Dockerfileのすべての詳細を無視できます。 次のように入力するだけです。
sbt dockerBuildAndPush
sbt-docker
プラグインは、必要なすべての部分をインターネットからダウンロードしてDockerイメージを構築し、 localhost
のKubernetesアプリケーションとともに以前に開始されたDockerレジストリにプッシュします。 したがって、必要なのは、画像が調理されて準備ができるまでもう少し待つことです。
問題が発生した場合は、次のコマンドを実行して、すべてを既知の状態にリセットするのが最善の方法です。
bin/stop-kube-local.sh bin/start-kube-local.sh
これらのコマンドは、すべてのコンテナーを停止して正しく再起動し、レジストリがsbt
によってビルドおよびプッシュされたイメージを受信できるようにする必要があります。
Kubernetesでサービスを開始する
これで、アプリケーションがコンテナーにパッケージ化され、レジストリーにプッシュされたので、使用する準備が整いました。 Kubernetesは、コマンドラインファイルと構成ファイルを使用してクラスターを管理します。 コマンドラインが非常に長くなる可能性があり、手順を複製することもできるため、ここでは構成ファイルを使用しています。 ソースキットのすべてのサンプルは、 kube
フォルダーにあります。
次のステップは、イメージの単一インスタンスを起動することです。 実行中のイメージは、Kubernetes言語ではポッドと呼ばれます。 それでは、次のコマンドを呼び出してポッドを作成しましょう。
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」の場合、「Running」になるまでに数秒かかる場合があります。 また、前に画像を作成し忘れた場合など、「エラー」などの別のステータスを取得することもできます。
次のコマンドを使用して、ポッドが実行されているかどうかを確認することもできます。
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/ http://10.0.0.54:9000/ip/8.8.8.8
と入力して結果を確認できます。 Docker ToolboxでWindowsまたはMacを使用している場合、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が優れているのはスケーリングです。 構成ファイル内のレプリカの数を変更するだけで、アプリケーションの2つ、10つ、または100のインスタンスをデプロイできます。 それではやってみましょう。
代わりに、単一のポッドを停止して展開を開始します。 それでは、次のコマンドを実行してみましょう。
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
これで、ポッドが1つではなく2つになりました。 これは、私が提供した構成ファイルに、システムによって生成された2つの異なる名前を持つ値replica: 2
があるためです。 記事の範囲は、ScalaプログラマーがKubernetesにジャンプスタートするための単なる紹介であるため、構成ファイルの詳細については説明しません。
とにかく、現在2つのポッドがアクティブになっています。 面白いのは、サービスが以前と同じだということです。 akkahttp
というラベルの付いたすべてのポッド間で負荷分散するようにサービスを構成しました。 つまり、サービスを再デプロイする必要はありませんが、単一のインスタンスを複製されたインスタンスに置き換えることができます。
プロキシを再度起動することでこれを確認できます(Windowsを使用していて、プロキシを閉じている場合)。
bin/forward-kube-local.sh akkahttp-service 9000
次に、2つのターミナルウィンドウを開いて、各ポッドのログを確認します。 たとえば、最初のタイプでは次のようになります。
bin/kubectl logs -f akkahttp-deployment-4229989632-mjp6u
そして2番目のタイプでは:
bin/kubectl logs -f akkahttp-deployment-4229989632-s822x
もちろん、システムにある値に応じてコマンドラインを編集します。
次に、2つの異なるブラウザでサービスにアクセスしてみます。 次の画像のように、リクエストが複数の使用可能なサーバー間で分割されることを期待する必要があります。
結論
今日、私たちは表面をかろうじて引っかいた。 Kubernetesは、自動スケーリングと再起動、増分デプロイ、ボリュームなど、さらに多くの可能性を提供します。 さらに、例として使用したアプリケーションは非常に単純で、ステートレスであり、さまざまなインスタンスが相互に知る必要はありません。 現実の世界では、分散アプリケーションはお互いを知る必要があり、他のサーバーの可用性に応じて構成を変更する必要があります。 実際、Kubernetesは分散キーストア( etcd
)を提供しており、新しいインスタンスがデプロイされたときにさまざまなアプリケーションが相互に通信できるようにします。 ただし、この例は意図的に十分に小さく、コア機能に焦点を当てて作業を開始できるように簡略化されています。 チュートリアルに従うと、多数の詳細に混乱したり、複雑さに迷ったりすることなく、マシン上でScalaアプリケーションの作業環境を取得できるはずです。
- クラウドでのクラウド向けの開発:AWSでのDockerを使用したビッグデータ開発
- K8s / Kubernetes:AWS vs. GCP vs. Azure
- Kubernetesサービスメッシュの比較