LoginSignup
5
2

More than 3 years have passed since last update.

Skaffoldを使ってGROWI on K8s用のElasticsearchとHackMDをビルド&デプロイ

Last updated at Posted at 2020-12-07

本記事は「Kubernetes2 Advent Calendar 2020」の8日目のエントリになります。よろしくお願いします。

内容としては、「Rook/Cephのブロックストレージとオブジェクトストレージを使ってGROWI WikiをKubernetesで動かす」の続き(機能追加)になります。

TL;DR

GROWI with "Elasticsearch and HackMD" on KubernetesをSkaffoldを使ってビルド&デプロイしてみました。

zaki-lknr/growi-on-k8s: GROWI Wiki on Kubernetes

前置き

先日、Rookと仲間たち、クラウドネイティブなストレージの Advent Calendar 2020の3日目のエントリで以下の記事を作成しました。

Rook/Cephのブロックストレージとオブジェクトストレージを使ってGROWI WikiをKubernetesで動かす - Qiita

この記事でのGROWIは、Elasticsearch連携は(vm.max_map_countのエラーを解消することができずにオブジェクトストレージの記事としては蛇足かと思い)オプションということもあり省略していたので、改めてElasticsearchも設定して検索が使えるGROWI on Kubernetesをデプロイしてみよう、という内容になります。

GROWI with Elasticsearch

Docker Compose版のGROWIで使用されるElasticsearchの定義ベースにすすめます。

日本語プラグイン導入

GROWIで使うために、以下の日本語プラグインを導入します。

  • analysis-kuromoji
  • analysis-icu

Helmチャートの設定でどうにかできたらベストだったんですが、READMEのプラグインインストールの項を見る限り、イメージをビルドしないといけないみたいなので、ビルドします。

bitnamiのチャートだと、プラグインを設定で入れられるっぽいのでビルド不要で使えるかもしれないけど未確認

Dockerfile

ベースはGROWIのリポジトリにあるDocker Composeで使っているDockerfileを使用します。
ただし、この内容でビルドしたイメージをそのままKubernetesにデプロイしても下記エラーで起動に失敗します。

ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

ポイントは、コンテナ起動時のボリューム設定で挿入しているelasticsearch.ymlファイルによる設定。
Kubernetesではコンテナ起動時にこの芸当は(NFSとかで事前に用意したファイルをpvとしてマウントしない限りはたぶん)できないので、イメージに組み込んでビルドします。

よって、Dockerfileはこんな感じ。

Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:6.8.10

RUN bin/elasticsearch-plugin install analysis-kuromoji
RUN bin/elasticsearch-plugin install analysis-icu

COPY elasticsearch.yml /usr/share/elasticsearch/config/elasticsearch.yml

イメージにCOPYするelasticsearch.ymlはこちら

elasticsearch.yml
http.host: 0.0.0.0

# for elasticsearch-head
http.cors.enabled: true
http.cors.allow-origin: "*"

Skaffoldを使ってbuild & push & deploy (devモード)

Dockerイメージを手元でビルドし、Docker Hubへpushし、Kubernetesクラスタでpullします。
手作業でこれを繰り返すとまぁまぁ面倒なので、今回はこれらを全部一気にやってくれるSkaffoldというビルドツールを使ってみました。
(ずーーっと前から「Skaffoldを試す」というタスクに積んだままだったので、ついでにここで試し見ようというやつです)

Google発のコンテナアプリケーション開発支援ツール「Skaffold」や「Kaniko」を使ってみる | さくらのナレッジ

Skaffold設定

Quickstart | Skaffold

インストールは(Linuxなら)バイナリをダウンロードしてパスの通ったパス(/usr/local/binなど)に置けばOKです。

Skaffoldコマンドを使うための準備としては

  1. docker loginしておき、skaffold実行ユーザアカウントでコンテナレジストリへpushができる状態にする
  2. $HOME/.kube/configを設定しておき、skaffold実行ユーザアカウントでkubectlを使ったクラスタへのマニフェストのデプロイができる状態にする

の2点です。(2についてはhelmistioctlと同じ要領。たぶん)

docker pushを行うので、コンテナレジストリにDocker Hubを使用する場合はアカウント作成と、push先のリポジトリを用意しておく必要があります。

image.png

Skaffoldビルド・デプロイ設定

前述のDockerfileDockerfileから参照(イメージへコピー)する外部ファイルelasticsearch.ymlと同じディレクトリ(←構成を簡単にするため)に以下の「デプロイ用Kubernetesマニフェストファイル」と「Skaffold定義ファイル」を作成します。

デプロイ用Kubernetesマニフェストファイルは特に通常通りなので省略。
例えばDeploymentを使ってこんな感じ(あとでStatefulSetに変更しました)

skaffold.yamlについては、一つのイメージをbuild/push/deployする最小限な記述としてはこんな感じ。

skaffold.yaml
apiVersion: skaffold/v1
kind: Config
build:
  artifacts:
  - image: zakihmkc/growi-elasticsearch
    context: ./
    docker:
      dockerfile: ./Dockerfile
deploy:
  kubectl:
    manifests:
    - ./k8s-deploy.yaml

詳細はリファレンスを確認してください。

skaffold.yaml | Skaffold

ポイントはbuildセクションのartifactsで、ビルドするイメージの名前==push先のリポジトリ名を設定しておくことで、docker buildからdocker pushまで一気に行われます。
ビルドのcontextdocker.dockerfileはDocker Composeでビルドするときと同じでディレクトリの指定とDockerfileの指定。「開発」「ステージング」「本番」のようにファイルを分けるときに使うと良いです。

また、deployセクションでpushされたイメージをクラスターへデプロイするためのマニフェストファイルを指定することで、pushに成功した後にデプロイまで処理されます。

devモードで実行するには、サブコマンドdevを指定して実行します。
同じdevという名前にして分かりにくいけど、ビルドするイメージにテンポラリのタグを付与するために-t devを追加。

[zaki@cloud-dev elasticsearch]$ ls
Dockerfile  elasticsearch.yml  k8s-deploy.yaml  skaffold.yaml
[zaki@cloud-dev elasticsearch]$ skaffold dev -t dev 
Listing files to watch...
 - zakihmkc/growi-elasticsearch
Generating tags...
 - zakihmkc/growi-elasticsearch -> zakihmkc/growi-elasticsearch:dev
Checking cache...
 - zakihmkc/growi-elasticsearch: Not found. Building
Building [zakihmkc/growi-elasticsearch]...
Sending build context to Docker daemon  3.072kB
Step 1/4 : FROM docker.elastic.co/elasticsearch/elasticsearch:6.8.10
 ---> ffa00077159c
Step 2/4 : RUN bin/elasticsearch-plugin install analysis-kuromoji
 ---> Using cache
 ---> 6966b4d5e5bf
Step 3/4 : RUN bin/elasticsearch-plugin install analysis-icu
 ---> Using cache
 ---> 7e8711478ef2
Step 4/4 : COPY elasticsearch.yml /usr/share/elasticsearch/config/elasticsearch.yml
 ---> 11556383ec5f
Successfully built 11556383ec5f
Successfully tagged zakihmkc/growi-elasticsearch:dev
The push refers to repository [docker.io/zakihmkc/growi-elasticsearch]
06e6281e2c86: Preparing
c2255a3188da: Preparing

[... snip ...]

edf3aa290fb3: Layer already exists
06e6281e2c86: Pushed
dev: digest: sha256:c260d4c6f595b15163c9f7a3d88fcb6f790995f9f63ac519baaa94af49bc5bf2 size: 2421
Tags used in deployment:
 - zakihmkc/growi-elasticsearch -> zakihmkc/growi-elasticsearch:dev@sha256:c260d4c6f595b15163c9f7a3d88fcb6f790995f9f63ac519baaa94af49bc5bf2
Starting deploy...
 - deployment.apps/elasticsearch created
 - service/elasticsearch created
Waiting for deployments to stabilize...
 - deployment/elasticsearch: creating container elasticsearch
    - pod/elasticsearch-76d9c6d6f6-nqtjs: creating container elasticsearch
 - deployment/elasticsearch is ready.
Deployments stabilized in 5.595258289s
Press Ctrl+C to exit
Watching for changes...
[elasticsearch] [2020-12-04T14:10:06,213][INFO ][o.e.e.NodeEnvironment    ] [TQOy5aT] using [1] data paths, mounts [[/ (rootfs)]], net usable_space [42.1gb], net total_space [58.9gb], types [rootfs]
[elasticsearch] [2020-12-04T14:10:06,215][INFO ][o.e.e.NodeEnvironment    ] [TQOy5aT] heap size [1gb], compressed ordinary object pointers [true]
[elasticsearch] [2020-12-04T14:10:06,218][INFO ][o.e.n.Node               ] [TQOy5aT] node name derived from node ID [TQOy5aTYQuKGZ35Crwr6Iw]; set [node.name] to override
[elasticsearch] [2020-12-04T14:10:06,218][INFO ][o.e.n.Node               ] [TQOy5aT] version[6.8.10], pid[1], build[default/docker/537cb22/2020-05-28T14:47:19.882936Z], OS[Linux/3.10.0-1127.13.1.el7.x86_64/amd64], JVM[AdoptOpenJDK/OpenJDK 64-Bit Server VM/14.0.1/14.0.1+7]
[elasticsearch] [2020-12-04T14:10:06,218][INFO ][o.e.n.Node               ] [TQOy5aT] JVM arguments [-Xms1g, -Xmx1g, -XX:+UseG1GC, -XX:G1ReservePercent=25, -XX:InitiatingHeapOccupancyPercent=30, -Des.networkaddress.cache.ttl=60, -Des.networkaddress.cache.negative.ttl=10, -XX:+AlwaysPreTouch, -Xss1m, -Djava.awt.headless=true, -Dfile.encoding=UTF-8, -Djna.nosys=true, -XX:-OmitStackTraceInFastThrow, -XX:+ShowCodeDetailsInExceptionMessages, -Dio.netty.noUnsafe=true, -Dio.netty.noKeySetOptimization=true, -Dio.netty.recycler.maxCapacityPerThread=0, -Dlog4j.shutdownHookEnabled=false, -Dlog4j2.disable.jmx=true, -Djava.io.tmpdir=/tmp/elasticsearch-16478887452689113828, -XX:+HeapDumpOnOutOfMemoryError, -XX:HeapDumpPath=data, -XX:ErrorFile=logs/hs_err_pid%p.log, -Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=32,filesize=64m, -Djava.locale.providers=COMPAT, -XX:UseAVX=2, -Des.cgroups.hierarchy.override=/, -Des.path.home=/usr/share/elasticsearch, -Des.path.conf=/usr/share/elasticsearch/config, -Des.distribution.flavor=default, -Des.distribution.type=docker]
[elasticsearch] [2020-12-04T14:10:09,582][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [aggs-matrix-stats]
[elasticsearch] [2020-12-04T14:10:09,582][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [analysis-common]
[elasticsearch] [2020-12-04T14:10:09,582][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [ingest-common]
[elasticsearch] [2020-12-04T14:10:09,582][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [ingest-geoip]
[elasticsearch] [2020-12-04T14:10:09,582][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [ingest-user-agent]
[elasticsearch] [2020-12-04T14:10:09,583][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [lang-expression]
[elasticsearch] [2020-12-04T14:10:09,583][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [lang-mustache]
[elasticsearch] [2020-12-04T14:10:09,583][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [lang-painless]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [mapper-extras]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [parent-join]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [percolator]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [rank-eval]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [reindex]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [repository-url]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [transport-netty4]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [tribe]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-ccr]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-core]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-deprecation]
[elasticsearch] [2020-12-04T14:10:09,584][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-graph]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-ilm]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-logstash]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-ml]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-monitoring]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-rollup]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-security]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-sql]
[elasticsearch] [2020-12-04T14:10:09,585][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-upgrade]
[elasticsearch] [2020-12-04T14:10:09,586][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded module [x-pack-watcher]
[elasticsearch] [2020-12-04T14:10:09,595][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded plugin [analysis-icu]
[elasticsearch] [2020-12-04T14:10:09,595][INFO ][o.e.p.PluginsService     ] [TQOy5aT] loaded plugin [analysis-kuromoji]
[elasticsearch] [2020-12-04T14:10:16,226][INFO ][o.e.x.s.a.s.FileRolesStore] [TQOy5aT] parsed [0] roles from file [/usr/share/elasticsearch/config/roles.yml]
[elasticsearch] [2020-12-04T14:10:17,251][INFO ][o.e.x.m.p.l.CppLogMessageHandler] [TQOy5aT] [controller/79] [Main.cc@109] controller (64 bit): Version 6.8.10 (Build a5f7163bca0250) Copyright (c) 2020 Elasticsearch BV
[elasticsearch] [2020-12-04T14:10:17,994][INFO ][o.e.d.DiscoveryModule    ] [TQOy5aT] using discovery type [zen] and host providers [settings]
[elasticsearch] [2020-12-04T14:10:18,943][INFO ][o.e.n.Node               ] [TQOy5aT] initialized
[elasticsearch] [2020-12-04T14:10:18,943][INFO ][o.e.n.Node               ] [TQOy5aT] starting ...
[elasticsearch] [2020-12-04T14:10:19,069][INFO ][o.e.t.TransportService   ] [TQOy5aT] publish_address {127.0.0.1:9300}, bound_addresses {127.0.0.1:9300}
[elasticsearch] [2020-12-04T14:10:19,086][WARN ][o.e.b.BootstrapChecks    ] [TQOy5aT] max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
[elasticsearch] [2020-12-04T14:10:22,134][INFO ][o.e.c.s.MasterService    ] [TQOy5aT] zen-disco-elected-as-master ([0] nodes joined), reason: new_master {TQOy5aT}{TQOy5aTYQuKGZ35Crwr6Iw}{mDuQTY1KS1q1Es7e0DDjLA}{127.0.0.1}{127.0.0.1:9300}{ml.machine_memory=8181829632, xpack.installed=true, ml.max_open_jobs=20, ml.enabled=true}
[elasticsearch] [2020-12-04T14:10:22,138][INFO ][o.e.c.s.ClusterApplierService] [TQOy5aT] new_master {TQOy5aT}{TQOy5aTYQuKGZ35Crwr6Iw}{mDuQTY1KS1q1Es7e0DDjLA}{127.0.0.1}{127.0.0.1:9300}{ml.machine_memory=8181829632, xpack.installed=true, ml.max_open_jobs=20, ml.enabled=true}, reason: apply cluster state (from master [master {TQOy5aT}{TQOy5aTYQuKGZ35Crwr6Iw}{mDuQTY1KS1q1Es7e0DDjLA}{127.0.0.1}{127.0.0.1:9300}{ml.machine_memory=8181829632, xpack.installed=true, ml.max_open_jobs=20, ml.enabled=true} committed version [1] source [zen-disco-elected-as-master ([0] nodes joined)]])
[elasticsearch] [2020-12-04T14:10:22,197][INFO ][o.e.h.n.Netty4HttpServerTransport] [TQOy5aT] publish_address {10.244.127.102:9200}, bound_addresses {0.0.0.0:9200}
[elasticsearch] [2020-12-04T14:10:22,197][INFO ][o.e.n.Node               ] [TQOy5aT] started
[elasticsearch] [2020-12-04T14:10:22,223][WARN ][o.e.x.s.a.s.m.NativeRoleMappingStore] [TQOy5aT] Failed to clear cache for realms [[]]
[elasticsearch] [2020-12-04T14:10:22,262][INFO ][o.e.g.GatewayService     ] [TQOy5aT] recovered [0] indices into cluster_state
[elasticsearch] [2020-12-04T14:10:22,874][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.watches] for index patterns [.watches*]
[elasticsearch] [2020-12-04T14:10:22,941][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.watch-history-9] for index patterns [.watcher-history-9*]
[elasticsearch] [2020-12-04T14:10:22,964][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.triggered_watches] for index patterns [.triggered_watches*]
[elasticsearch] [2020-12-04T14:10:22,992][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.monitoring-logstash] for index patterns [.monitoring-logstash-6-*]
[elasticsearch] [2020-12-04T14:10:23,044][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.monitoring-es] for index patterns [.monitoring-es-6-*]
[elasticsearch] [2020-12-04T14:10:23,092][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.monitoring-alerts] for index patterns [.monitoring-alerts-6]
[elasticsearch] [2020-12-04T14:10:23,255][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.monitoring-beats] for index patterns [.monitoring-beats-6-*]
[elasticsearch] [2020-12-04T14:10:23,310][INFO ][o.e.c.m.MetaDataIndexTemplateService] [TQOy5aT] adding template [.monitoring-kibana] for index patterns [.monitoring-kibana-6-*]
[elasticsearch] [2020-12-04T14:10:23,479][INFO ][o.e.l.LicenseService     ] [TQOy5aT] license [93d6e9a7-978d-4035-8052-e26c27e49afe] mode [basic] - valid

ここで表示が止まってますが、これ、kubectl logs -fのログが出ています。
別のターミナルでkubectl get podすれば以下の通り。

[zaki@cloud-dev elasticsearch]$ kubectl get pod
NAME                             READY   STATUS    RESTARTS   AGE
elasticsearch-76d9c6d6f6-nqtjs   1/1     Running   0          5m41s

ちゃんと動いています。

ログの最後じゃないので分かりづらいけど、多分このログが起動成功の目印だと思う。(多分)

[elasticsearch] [2020-12-04T14:10:22,197][INFO ][o.e.h.n.Netty4HttpServerTransport] [TQOy5aT] publish_address {10.244.127.102:9200}, bound_addresses {0.0.0.0:9200}
[elasticsearch] [2020-12-04T14:10:22,197][INFO ][o.e.n.Node               ] [TQOy5aT] started

実行中のイメージのtagはこの通りだけど、ハッシュ値が付与されている状態。

[zaki@cloud-dev elasticsearch]$ kubectl get pod elasticsearch-76d9c6d6f6-nqtjs -o jsonpath='{.spec.containers[].image}'; echo
zakihmkc/growi-elasticsearch:dev@sha256:c260d4c6f595b15163c9f7a3d88fcb6f790995f9f63ac519baaa94af49bc5bf2

ビルドしたイメージが正常に動作するのが確認できたのでCtrl-cで停止します。

[elasticsearch] [2020-12-04T14:10:23,479][INFO ][o.e.l.LicenseService     ] [TQOy5aT] license [93d6e9a7-978d-4035-8052-e26c27e49afe] mode [basic] - valid
^CCleaning up...
 - deployment.apps "elasticsearch" deleted
 - service "elasticsearch" deleted
There is a new version (1.17.1) of Skaffold available. Download it from:
  https://github.com/GoogleContainerTools/skaffold/releases/tag/v1.17.1

するとpodも停止します。
便利ですね。

[zaki@cloud-dev elasticsearch]$ kubectl get pod
NAME                             READY   STATUS        RESTARTS   AGE
elasticsearch-76d9c6d6f6-nqtjs   1/1     Terminating   0          11m

pushされたイメージはこの通り。

image.png

そして、Skaffoldのdevモードが更に便利なところは、Ctrl-cで停止するまの待機状態の間は、Dockerfileやデプロイ用マニフェストを変更すると、ファイルの変更を検知してリビルドから再デプロイまで自動でやってくれるため、開発中のトライアルアンドエラーの最中はデプロイ作業に時間を割かずに済むため、開発に集中できます。

runモードでbuild & push & deploy

引数をdevからrunに変更します。
ついでに、tagもリリースバージョンっぽくしてみます。

[zaki@cloud-dev elasticsearch]$ skaffold run -t 6.8.10-ja
Generating tags...
 - zakihmkc/growi-elasticsearch -> zakihmkc/growi-elasticsearch:6.8.10-ja
Checking cache...
 - zakihmkc/growi-elasticsearch: Found. Tagging
Tags used in deployment:
 - zakihmkc/growi-elasticsearch -> zakihmkc/growi-elasticsearch:6.8.10-ja@sha256:c260d4c6f595b15163c9f7a3d88fcb6f790995f9f63ac519baaa94af49bc5bf2
Starting deploy...
 - deployment.apps/elasticsearch created
 - service/elasticsearch created
Waiting for deployments to stabilize...
 - deployment/elasticsearch is ready.
Deployments stabilized in 3.40209576s
You can also run [skaffold run --tail] to get the logs
There is a new version (1.17.1) of Skaffold available. Download it from:
  https://github.com/GoogleContainerTools/skaffold/releases/tag/v1.17.1

[zaki@cloud-dev elasticsearch]$ 

今度はバックグラウンドで処理が行われるため、プロンプトが返ってきます。

[zaki@cloud-dev elasticsearch]$ kubectl get pod
NAME                            READY   STATUS    RESTARTS   AGE
elasticsearch-89688ff58-tw969   1/1     Running   0          13s

新しいtagもpushされています。(中身は変わっていないのでdevと同じ)

image.png

runモードでデプロイしたpod類の削除

停止・削除するには、skaffold deleteを実行します。

[zaki@cloud-dev elasticsearch]$ skaffold delete 
Cleaning up...
 - deployment.apps "elasticsearch" deleted
 - service "elasticsearch" deleted

StatefulSetバージョン

最初からそうすればよかったのにと思いつつ、ワークロードをStatefulSetに変更してデプロイ。

また、実行時に-n <namespace>を付与して、デプロイ先をGROWI本体と同じネームスペースを指定しています。

[zaki@cloud-dev elasticsearch (features/elasticsearch)]$ skaffold run -t 6.8.10-ja -n growi-on-k8s
Generating tags...
 - zakihmkc/growi-elasticsearch -> zakihmkc/growi-elasticsearch:6.8.10-ja
Checking cache...
 - zakihmkc/growi-elasticsearch: Found Remotely
Tags used in deployment:
 - zakihmkc/growi-elasticsearch -> zakihmkc/growi-elasticsearch:6.8.10-ja@sha256:c260d4c6f595b15163c9f7a3d88fcb6f790995f9f63ac519baaa94af49bc5bf2
Starting deploy...
 - statefulset.apps/elasticsearch created
 - service/elasticsearch created
Waiting for deployments to stabilize...
Deployments stabilized in 10.508539ms
You can also run [skaffold run --tail] to get the logs
There is a new version (1.17.1) of Skaffold available. Download it from:
  https://github.com/GoogleContainerTools/skaffold/releases/tag/v1.17.1

[zaki@cloud-dev elasticsearch (features/elasticsearch)]$ kc get pod,svc,pvc -n growi-on-k8s 
NAME                            READY   STATUS              RESTARTS   AGE
pod/elasticsearch-0             0/1     ContainerCreating   0          9s
pod/growi-app-fd6fcdd9d-vsdjj   1/1     Running             14         7d
pod/growi-mongo-0               1/1     Running             3          7d

NAME                    TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)          AGE
service/elasticsearch   ClusterIP      10.99.155.247    <none>          9200/TCP         9s
service/growi-app       LoadBalancer   10.96.23.173     192.168.0.185   3000:32644/TCP   7d
service/growi-mongo     ClusterIP      10.106.230.143   <none>          27017/TCP        7d

NAME                                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS      AGE
persistentvolumeclaim/es-data-elasticsearch-0      Bound    pvc-f052985f-b3a9-408f-8343-d09b1cbf8ff7   8Gi        RWO            rook-ceph-block   9s
persistentvolumeclaim/mongo-config-growi-mongo-0   Bound    pvc-6b29acb4-bad6-4b01-9135-4302bf1f35d3   256Mi      RWO            rook-ceph-block   7d1h
persistentvolumeclaim/mongo-db-growi-mongo-0       Bound    pvc-21ea3319-5487-4b10-83d3-a9c076d30b28   4Gi        RWO            rook-ceph-block   7d1h

GROWI WikiのElasticsearch設定

Elasticsearchの設定が無い場合はこの通り。

image.png

GROWIにElasticsearchを組み込むには、Docker Compose版のdocker-compose.ymlの通り、環境変数ELASTICSEARCH_URIにElasticsearchのURLを設定します。
Kubernetes環境ではService経由のpod間通信になるので、Service名(elasticsearch)を記述します。

マニフェストはこんな感じ

growi-on-k8s.yaml抜粋
      containers:
      - image: weseek/growi:4
        name: growi-app
        env:
          - name: MONGO_URI
            value: mongodb://growi-mongo:27017/growi
          - name: ELASTICSEARCH_URI
            value: http://elasticsearch:9200/growi
          - name: PASSWORD_SEED
            value: changeme

このマニフェストでデプロイし直せば、この通り画面上部に検索フィールドが表示されます。

image.png

設定のメニューの「全文検索管理」を表示すると、この通りElasticsearchが有効になっています。

image.png

これで、GROWIとElasticsearchが連携設定され、検索が使えるようになりました。

GROWI with HackMD

GROWIのWikiはデフォルトでは同じページを複数ユーザーで同時編集は出来ません。
(やってしまった場合は、後から保存しようとしたユーザー側でconflictとなり、手動でマージする必要があります)

ただし、HackMDと連携することで、同時編集ができるようにする機能が用意されているので、(これはイメージのビルドが必要だけど日本語プラグイン入りElasticsearchでイメージビルドしてるのでそのままのノリで)GROWI on Kubernetesでもやってみます。

HackMD連携も、公式のDocker Composeが公開されています。
現在のdocker-compose.ymlの内容を整理すると、以下の通り。

  1. MariaDBをデプロイ
  2. GROWI用HackMDイメージをビルド
  3. GROWI用HackMDをデプロイ
    • 環境変数は以下の通り
      • GROWI本体のブラウザからアクセスする際のURL
      • MariaDB接続先
    • HackMD単体でブラウザからアクセスできるようにしておく
  4. GROWI本体の環境変数追加
    • GROWI本体(pod)から見たHackMDのURL
    • ブラウザからアクセスする際のHackMDのURL

MariaDB

Helmとかで別途入れてもいい気もするけど、GROWIのdocker-compose.ymlだとcharactersetの引数設定があるので、これベースにマニフェスト作成。

docker-compose.override.yml抜粋
  mariadb:
    image: mariadb:10.3
    command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
    environment:
      - MYSQL_USER=hackmd
      - MYSQL_PASSWORD=hackmdpass
      - MYSQL_DATABASE=hackmd
      - MYSQL_RANDOM_ROOT_PASSWORD=true

ただしこれをそのままKubernetes用マニフェストに書き換えると、以下のエラーでデプロイに失敗します。

2020-12-05  0:45:49 0 [Note] mysqld (mysqld 10.3.27-MariaDB-1:10.3.27+maria~focal) starting as process 1 ...
mysqld: Please consult the Knowledge Base to find out how to run mysqld as root!
2020-12-05  0:45:49 0 [ERROR] Aborting

ポイントはcommandの部分。

Kubernetesのマニフェストではcommandでなくargsを使うとうまく動作します。

mariadbマニフェスト抜粋
    spec:
      containers:
      - image: mariadb:10.3
        name: hackmd
        args:
          - mysqld
          - --character-set-server=utf8
          - --collation-server=utf8_general_ci
        env:
          - name: MYSQL_USER
            value: hackmd
          - name: MYSQL_PASSWORD

GROWI用HackMDイメージビルドとデプロイ

これもSkaffoldを使います。
Dockerfileや、ビルド時にイメージに組み込む設定ファイル類は公式で公開されているものをそのまま使用できます。

このファイル類に加えて、Elasticsearchのビルドと同様に以下のファイルを準備。

環境変数にGROWI本体の(ブラウザからアクセスする際の)URL設定が必要なので、設定しておきます。

build/push/deployの手順・内容はElasticsearchと同じなので省略します。
作成したファイル一式はGitHub参照してください。

デプロイに成功しtype:LoadBalancerなどを使って外部公開すると、以下のようにブラウザでアクセスできます。

image.png

GROWIの設定時に、ブラウザからアクセスするためのURLが必要なので確認しておきます。

GROWI WikiのHackMD設定

前述の通り、GROWI側にもHackMDがどこにいるかの設定が必要なので、環境変数を追加します。
HACKMD_URI_FOR_SERVERはElasticsearchと同じく、GROWIのpodからHackMDのpodへのpod間通信のためService名を記述します。
HACKMD_URIはブラウザからアクセスする際のHackMDのURLを設定します。

環境変数設定抜粋
      containers:
      - image: weseek/growi:4
        name: growi-app
        env:
          - name: MONGO_URI
            value: mongodb://growi-mongo:27017/growi
          - name: ELASTICSEARCH_URI
            value: http://elasticsearch:9200/growi
          - name: PASSWORD_SEED
            value: changeme
          - name: HACKMD_URI_FOR_SERVER
            value: http://hackmd:3000
          - name: HACKMD_URI
            value: http://192.168.0.186:3000

この設定でGROWI本体を再デプロイしてブラウザでアクセス、ページ編集画面の隣にある「HackMD」ボタンを押下、

image.png

すると以下のように、HackMDを使って編集開始するためのボタンが表示されます。

image.png

ボタン押下すると、

image.png

あれ……?

残念ながらこれ原因が分かりませんでしたが、ChromeとEdgeだとダメでしたが、Firefoxだとアクセスできました。

image.png

もう一つブラウザを起動してアクセスすると、画面右上のオンライン数が2になり、それぞれのブラウザでの編集状態(カーソルなど)が表示されます。

image.png

ちなみにDocker Compose版だとChromeでもアクセスできるので、今回のtype:LoadBalancerを使用したKubernetes環境だと、GROWI本体とHackMDがブラウザから見ると別のIPアドレス(ホスト)にデプロイされていてかつHTTPSでもないので、Chromeのセキュリティ機能的な制限かなと思ってますが、このあたりで力尽きました。
(GROWIもHackMDもkubectl logsで確認する限りそれっぽいエラーログは何も出ていない)

Chromeのデベロッパーツールで見るとこんな感じで、AUTH failed: No cookie transmitted.というエラーが出ています。

image.png

まとめと課題

Skaffoldを使うことで、コンテナイメージのbuild/push/deployを一度に出来るので、大変効率的になります。
特にdevモードの場合は、ファイルの変更を検知して自動でイメージのリビルドからクラスタへのデプロイまで行われるので、コードの作成に集中できます。

GROWI on Kubernetesについても、ElasticsearchおよびHackMDをKubernetesへデプロイすることで、検索および同時編集が可能になりました。
今回はtype:LoadBalancerを使ってGROWI本体とHackMDを外部公開したため、Chromeアクセス時に期待する動作を確認できませんでしたが、Ingressなど別の方法で実装すると改善されるかもしれません。

またtype:LoadBalancerの欠点として、デプロイするまで外部アクセス用のアドレスが分からないため、GROWI本体とHackMDでお互いの外部アクセス用アドレスを初回デプロイ前は設定することができません。
これも改善の余地があり、Ingressでできるかもしれないし、ExternalDNSも使えるかもしれないので検討してみたいです。

自宅 k8s クラスタのサービスに FQDN で繋がるようにした - ののし.log


(参考) エラーパターン

Elasticsearch

[elasticsearch] ERROR: [1] bootstrap checks failed
[elasticsearch] [1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

このエラーは

Dockerfile
FROM docker.elastic.co/elasticsearch/elasticsearch:6.8.10

RUN bin/elasticsearch-plugin install analysis-kuromoji
RUN bin/elasticsearch-plugin install analysis-icu

この内容のDockerfileで日本語プラグインがインストールされたカスタムイメージをKubernetesにデプロイすると発生しました。

以下のファイルを/usr/share/elasticsearch/config/elasticsearch.yml置いておくと回避できます。

/usr/share/elasticsearch/config/elasticsearch.yml
http.host: 0.0.0.0

# for elasticsearch-head
http.cors.enabled: true
http.cors.allow-origin: "*"

MariaDB

Docker Composeでcommandを使ってデプロイすると、元々のイメージで指定されていたdocker-entrypoint.shcommand指定の引数がセットで適用されるけれど、Kubernetesのpodのパラメタでcommandを使うと、どうやらdocker-entrypoint.shの情報が上書きされてmysqldを直接実行して起動に失敗します。

用意されているDocker Composeで正常動作しているMariaDBのコンテナをdocker inspectでパラメタ類を確認すると以下の通り。

docker-composeでデプロイしたmariadbコンテナのdocker-inspect抜粋
{
            "Cmd": [
                "mysqld",
                "--character-set-server=utf8",
                "--collation-server=utf8_general_ci"
            ],
            "Image": "mariadb:10.3",
            "Volumes": {
                "/var/lib/mysql": {}
            },
            "WorkingDir": "",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
}

Kubernetes上でcommand指定でcharset等の指定を行った場合のエラーメッセージ

2020-12-05  0:45:49 0 [Note] mysqld (mysqld 10.3.27-MariaDB-1:10.3.27+maria~focal) starting as process 1 ...
mysqld: Please consult the Knowledge Base to find out how to run mysqld as root!
2020-12-05  0:45:49 0 [ERROR] Aborting

上記のメッセージでググるとヒットする(基本的に非コンテナ環境だと思われる)回避策の--user=rootを追加すると

2020-12-05  0:46:37 0 [ERROR] Could not open mysql.plugin table. Some plugins may be not loaded
2020-12-05  0:46:37 0 [ERROR] Can't open and lock privilege tables: Table 'mysql.servers' doesn't exist
2020-12-05  0:46:37 0 [Note] Server socket created on IP: '::'.
2020-12-05  0:46:37 0 [ERROR] Fatal error: Can't open and lock privilege tables: Table 'mysql.user' doesn't exist

となり、同様に回避策をググって

          - --user=mysql
          - --datadir=/var/lib/mysql

に設定変更しても改善せず。

Docker Composeでの動作状態と比較してみるとcommandを使うとdocker-entrypoint.shが使われずに起動しようとするので失敗していた、という状態でした。

これは、Docker単体でも、mysqldを単体で実行すると同じエラーで起動失敗します。

$ docker run -it --rm --name mariadb mariadb:10.3 bash
root@b8a277c49a9d:/# mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
2020-12-05 11:27:44 0 [Note] mysqld (mysqld 10.3.27-MariaDB-1:10.3.27+maria~focal) starting as process 8 ...
mysqld: Please consult the Knowledge Base to find out how to run mysqld as root!
2020-12-05 11:27:44 0 [ERROR] Aborting

参考情報

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2