Share
Embed
Transcript
35 slides
-
• Twitter: https://twitter.com/__Attsun__ • ブレインパッドという会社で自社サービス開発してます • VP of Engineeringとかテックリードとかやってます •
得意な言語はPythonです!家ではGoです!Scalaは仕事です! ◦ ScalaはBetter Javaとして使っているだけのガチじゃない勢です • Kubernetesが好きで! 自己紹介:渥美 達也 -
ブレインパッドのご紹介 • データ分析の会社と思われがちですが、 • エンジニアがいるんです! • デジタルマーケティング領域の自社製品を作っています • RtoasterはDMP業界シェアNo.1 •
広告周りは新規製品開発が盛んです。 • もちろん、機械学習やデータ分析の知見はサービス開発にも活きています -
アジェンダ 1. k8sを利用しているサービスのご紹介 2. k8sのおさらい 3. システム構成 4. Play2 on
k8sのプラクティス a. コンテナビルド b. k8sへのデプロイ c. 環境変数の設定 d. コンフィグの設定 e. ロギングの設定 -
システム構成ざっくり Cloud Load Balancing Cloud DNS Cloud SQL GKE 1.9
admin Deployment web Deployment batch CronJob Firebase Media (G, Y, …) BigQuery MediaHub Accounts batch CronJob -
Kubernetesとは? 公式によると、以下だそうです。 Kubernetes is an open-source system for automating deployment,
scaling, and management of containerized applications. 雑に表現すると、コンテナベースのアプリケーションを管理する仕組みですね。 -
Pod / Deployment / Service / kubectl • Pod ◦
Kubernetesで実行されるアプリケーションの最小単位です。 ◦ 1個以上のコンテナから構成されます。 • Deployment ◦ 個々のPodをどのように実行するかを管理する単位です ◦ Podのレプリカの数や、Podが利用するコンテナを管理しており、Deploymentを更新することでPod が更新されます。 • Service ◦ Podを論理的な単位でまとめ、それらへのアクセスポリシーを定義する仕組みです。 ◦ Podはコンテナアップデートなどにより頻繁に生成されたり破棄されたりを繰り返しますが、Service を見ている限り影響されません。 • kubectl ◦ kubernetesのコマンドラインクライアントです。 ◦ kubectl apply xxx.yaml で、YAMLに記載されたDeploymentなどの設定を反映するなど。 今日必要な最低限の知識 -
[node-2] [node-1] Pod / Deployment / Service [pod-nginx-1] nginx container
[pod-nginx-2] nginx container Podは特定のノード上で稼働し ます。 -
[node-2] [node-1] [deployment-nginx] container=nginx:1.7.9 replica=2 port=80 Pod / Deployment /
Service [pod-nginx-1] nginx container [pod-nginx-2] nginx container Podがどのように稼働するかは、 Deploymentにより管理される (ReplicaSetは割愛) -
[node-2] [node-1] [deployment-nginx] container=nginx:1.7.9 replica=2 port=80 Pod / Deployment /
Service [pod-nginx-1] nginx container [pod-nginx-2] nginx container [service-nginx] Serviceが、Podへのアクセスを管理 している。 このケースではLBとしても働いてい る。 -
Podの構成 • 同じPodの中に複数のコンテナが動いているパターン • Nginxコンテナ ◦ 静的コンテンツのホスト ◦ Play2 APIへのプロキシ
• Play2コンテナ ◦ Nginxから流れてきたAPIを実行する ◦ ベースはJava8コンテナ ◦ Scala 2.11 + Play 2.5 • Cloud SQL Proxyコンテナ ◦ Cloud SQLへの接続に必要なやつ webapp Pod Nginx Container Play2 Container CloudSQLProxy Container -
sbtでPlay2コンテナ作成 lazy val webapp = project.in(file(“webapp”)) .dependsOn(依存パッケージたち) .enablePlugin(PlayScala, JavaAppPackaging, sbtdocker.DockerPlugin)
.settings(Scalaバージョンなどの共通セッティング) .settings( buildInfoPackage := “パッケージ名” buildOptions in docker := BuildOption(cache = false), imageNames in docker := Seq(s“${gcrRegion.value}/${moduleName.value}:${version.value}”), dockerfile in docker := { val appDir = stage.value val targetDir = “/opt/docker” new Dockerfile { from(java8イメージ) expose(9000) copy(appDir, targetDir) entryPoint(s”$targetDir/bin/${executableName.value}”) } } build := docker.value ) -
sbtでPlay2コンテナ作成 lazy val webapp = project.in(file(“webapp”)) .dependsOn(依存パッケージたち) .enablePlugin(PlayScala, JavaAppPackaging, sbtdocker.DockerPlugin)
.settings(Scalaバージョンなどの共通セッティング) .settings( buildInfoPackage := “パッケージ名” buildOptions in docker := BuildOption(cache = false), imageNames in docker := Seq(s“${gcrRegion.value}/${moduleName.value}:${version.value}”), dockerfile in docker := { val appDir = stage.value val targetDir = “/opt/docker” new Dockerfile { from(java8イメージ) expose(9000) copy(appDir, targetDir) entryPoint(s”$targetDir/bin/${executableName.value}”) } } build := docker.value ) これらのプラグインを使って、 jar 生成からコンテナビルドまで一 気にやります。 -
sbtでPlay2コンテナ作成 lazy val webapp = project.in(file(“webapp”)) .dependsOn(依存パッケージたち) .enablePlugin(PlayScala, JavaAppPackaging, sbtdocker.DockerPlugin)
.settings(Scalaバージョンなどの共通セッティング) .settings( buildInfoPackage := “パッケージ名” buildOptions in docker := BuildOption(cache = false), imageNames in docker := Seq(s“${gcrRegion.value}/${moduleName.value}:${version.value}”), dockerfile in docker := { val appDir = stage.value val targetDir = “/opt/docker” new Dockerfile { from(java8イメージ) expose(9000) copy(appDir, targetDir) entryPoint(s”$targetDir/bin/${executableName.value}”) } } build := docker.value ) Dockerfileのようなものをここで 定義しています。 jarをコピってentrypointにしま す。 -
sbtでPlay2コンテナ作成 lazy val webapp = project.in(file(“webapp”)) .dependsOn(依存パッケージたち) .enablePlugin(PlayScala, JavaAppPackaging, sbtdocker.DockerPlugin)
.settings(Scalaバージョンなどの共通セッティング) .settings( buildInfoPackage := “パッケージ名” buildOptions in docker := BuildOption(cache = false), imageNames in docker := Seq(s“${gcrRegion.value}/${moduleName.value}:${version.value}”), dockerfile in docker := { val appDir = stage.value val targetDir = “/opt/docker” new Dockerfile { from(java8イメージ) expose(9000) copy(appDir, targetDir) entryPoint(s”$targetDir/bin/${executableName.value}”) } } build := docker.value ) このビルドファイルに対して sbt buildすればコンテナがローカル に作成されます。 -
deployment.yamlの設定 apiVersion: apps/v1 kind: Deployment metadata: … spec: selector: …
template: metadata: … spec: containers: - name: webapp-server image: 先ほど作ったイメージ ports: - containerPort: 9000 envFrom: - configMapRef: name: webapp-env-config // 後述 - secretRef name: cloudsql-db-credentials // 後述 - name: webapp-client image: JSが入ったnginxコンテナ ports: - containerPort: 80 ... - name: b.gcr.io/cloudsql-docker/gce-proxy:x.xx ... -
service.yamlの設定 apiVersion: apps/v1 kind: Service metadata: ... spec: type: NordPort
ports: - port: 80 selector: ... deployment / serviceのyamlをいつも 通りkubectl applyすれば完了! -
Javaオプションなどの環境変数の指定は ConfigMapに切り出しています。 (deployment.yamlがごちゃつくので) deployment.yamlでConfigMapをenvFromす ることで設定。 コンフィグファイルパスもここで指定。 Play2の環境変数指定 apiVersion: v1 kind:
ConfigMap metadata: name: webapp-env-config data: JAVA_OPTS: >- -server -Xms… -Xmx… -XX:MetaspaceSize=... -Dconfig.resource=application.conf -Dlogger.resource=logback.xml ... -
DBのパスワードなど、一部コンフィグに直 接書きたくなかったり、環境変数から取得し たい場合がある。 そういうときは、ConfigMap / Secretで設定 された環境変数を設定ファイル内に読み込 むことができます。 Play2のコンフィグレーション //
application.conf db { default.username=${?DB_USER} default.password=${?DB_PASSWORD} } -
Play2のロギング // logback.xml <configuration> <appender name=”STDOUT” class=”ch.qos.core.ConsoleAppender”> <target>System.out</target> -- filter略
-- <encoder class=”ch.qos.logback.core.encoder.LayoutWrappingEncoder”> <layout class="ch.qos.logback.contrib.json.classic.JsonLayout"> <jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter"> </jsonFormatter> <includeContextName>false</includeContextName> <appendLineSeparator>true</appendLineSeparator> </layout> <charset>UTF-8</charset> </encoder> </appender> -
Play2のロギング // logback.xml <configuration> <appender name=”STDOUT” class=”ch.qos.core.ConsoleAppender”> <target>System.out</target> -- filter略
-- <encoder class=”ch.qos.logback.core.encoder.LayoutWrappingEncoder”> <layout class="ch.qos.logback.contrib.json.classic.JsonLayout"> <jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter"> </jsonFormatter> <includeContextName>false</includeContextName> <appendLineSeparator>true</appendLineSeparator> </layout> <charset>UTF-8</charset> </encoder> </appender> GKEでは、標準出力、エラー出力 に垂れ流しておけば Stackdriverに 流してくれます。 -
Play2のロギング // logback.xml <configuration> <appender name=”STDOUT” class=”ch.qos.core.ConsoleAppender”> <target>System.out</target> -- filter略
-- <encoder class=”ch.qos.logback.core.encoder.LayoutWrappingEncoder”> <layout class="ch.qos.logback.contrib.json.classic.JsonLayout"> <jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter"> </jsonFormatter> <includeContextName>false</includeContextName> <appendLineSeparator>true</appendLineSeparator> </layout> <charset>UTF-8</charset> </encoder> </appender> 構造化されているほうが Stackdriverで扱いやすいので、 JSONでログ出力します。 -
Play2のロギング // 前ページから続き <appender name=”STDERR” class=”ch.qos.core.ConsoleAppender”> <target>System.err</target> -- STDOUTとほぼ同じなので省略 --
</appender> <root> <appender-ref ref="STDOUT" /> <appender-ref ref="STDERR" /> </root> </configuration> 標準エラー出力を捕まえる設定も 同じようにあります。 -
まとめ • Play2 + Kubernetesは簡単! • sbtでビルドからコンテナ作成まで完結できる • VMを直接意識しないアプリケーション管理&GAEやFaaSほど環境を制限されない ので、Kubernetesはちょうど良い
• (GKEの場合)ロギングは標準に垂れ流すだけでStackdriver行きになるのでロー テーションとかディスクの枯渇とかの悩みがなくなる ◦ もちろん、Stackdriver Loggingのお金がかかります • 調べきれていないところ ◦ JMX周りの話