sonatype nexus3 の s3 blobstore は rubygems では遅くなった話

タイトルがすべてを物語っていますが、

sonatype nexus3 とは、パッケージマネージャです。

maven, rubygem, pypi, npm, docker など、各種言語などのパッケージマネージャを主に内部向けに公開するのに使えるものです。

最近だとgithubなどもパッケージを置けるようになって存在意義が薄れてきているのかな?というきもしますが。

で、各種パッケージを保管する場所が必要になりますが、通常、サーバ上のファイルシステムがその保管場所として利用されます。

nexus3では、サーバ上のファイルシステムのほか、AWS S3 も保管場所として利用できます。オフィシャルサイトにも、EC2でnexus3を動かすのでないかぎり、推奨しないと書かれていますが。

なにはともあれ、EC2でnexus3を動かしていたので、S3をストレージとして利用していたところ、

「bundle install すると前は10秒くらいで終わっていたのが2-3分かかるようになったんだけど」

などと連絡を受け、ストレージ変更のほかに構成変更を行っていたのでそこが原因かも、と疑い、いろいろ設定変更したりしたのですが、ファイルシステムをストレージとして利用したところ、高速化し、原因が判明したという流れです。

ではなぜS3にすると遅くなるのでしょうか。それはおそらく、依存関係解決のためです。

Gemfileには、まず、直接依存しているパッケージ群が記載されています。

まず、それらのパッケージについて、どのパッケージに依存しているのかをnexus3に問い合わせます。

すると、nexus3は、各パッケージの依存関係が記載されたファイルを取得します。結果、s3からのファイルダウンロードがパッケージの数だけ走ります。

nexus3から、依存先のパッケージ一覧が返ってきます。

新しく見つかったパッケージについて、またどのパッケージに依存しているのかをnexus3に問い合わせます。

というのを、全パッケージについて行いますので、s3からファイルをダウンロードする回数がそこそこ多くなり、そのぶん時間が余計にかかる。という話です。

EC2で運用してこれなので、AWSからネットワーク的に遠いところでS3をストレージとして利用すると、目も当てられないような遅さになるのではないかと推測できます。

AWS Cloudwatch agent (prometheus-beta) でEC2上のexporterからメトリクスを送る

Amazon CloudWatch が Prometheus メトリクスのモニタリングを開始 (ベータ版)

ということで、ベータですがAWS EKSとKubernetes向けに Prometheusメトリクスのモニタリングができるようになりました。

しかし、私はEC2でProemtheusメトリクスを出すExporterを動かしていて、これをモニタリングしたい。

そこで、いろいろ設定を見ながら、EC2上のProemtheusメトリクスをCloudwatchへ送る方法を探した記録です。

まず、セットアップページから githubのページ を見つけます。

そして、prometheus-k8s.yaml をみると、必要な設定ファイルがみつかります。ここから、設定ファイルを作成していきます。

私は fluentdのモニタリングがしたかったので、fluentdのための設定になっています。

一つ目は、cloudwatch agentのための設定

/etc/cwagent.json

    {
      "agent": {
        "region": "ap-northeast-1"
      },
      "logs": {
        "metrics_collected": {
          "prometheus": {
            "cluster_name": "sample",
            "prometheus_config_path": "/etc/prometheusconfig/prometheus.yaml",
            "emf_processor": {
              "metric_declaration_dedup": true,
              "metric_declaration": [
                {
                  "source_labels": ["Service"],
                  "label_matcher": "Fluentd",
                  "dimensions": [["Service","plugin_id"]],
                  "metric_selectors": ["^fluentd_output_"]
                }
              ]
            }
          }
        },
        "force_flush_interval": 5
      }
    }

cluster_name は k8sじゃないので適当です。 prometheus_config_path は読み込むファイルパスです。これは、 docker run するときに prometheus.yamlをマウントする場所なので、どこでもいいです。

emf_processor というところで、どのメトリクスを収集するのか、どの単位で区別するのか、といった設定をします。

source_labels で指定したラベルが label_macherと一致するものを対象として設定されます。ここではServiceラベルが Fluentdであるものを対象としています。今回はprometheus.yaml側で付与したラベルを利用します。

dimensions というところで、メトリクスを区別するタグの組を設定します。ここでは、 Serviceとplugin_idの組で区別するようにしています。複数台で動かす場合は、インスタンス名などを含めるとよいでしょう。

metric _selectorsというところで収集するメトリクスを決めます。 正規表現がつかえます。今回は fluentd_output で始まるメトリクスのみを収集するよう設定しています。

二つ目は、prometheusの設定。

fluentdのprometheus plugin は 24231 ポートでメトリクスを公開するよう設定するサンプルが公式ドキュメントにあります。

/etc/prometheus.yaml

    global:
      scrape_interval: 1m
      scrape_timeout: 10s
    scrape_configs:
      - job_name: fluentd
        static_configs:
          - targets:
            - localhost:24231
            labels:
             - Service: Fluentd

こちらは prometheusのドキュメントを見るのが早いでしょう。

設定ができたら、あとは docker run するだけです

利用すべきイメージはdockerhub で見つけることができます。 prometheus-beta とついているものです

docker run -d --rm  --net host --name cwagent -v /etc/cwagent.json:/etc/cwagentconfig:ro -v /etc/prometheus.yaml;/etc/prometheusconfig/prometheus.yaml:ro  amazon/cloudwatch-agent:latest-prometheus-beta

docker logs cwagent にて、変なエラーが出てないことを確認したら数分待って cloudwatch metricsを見てみましょう。

cloudwatch の設定ファイルを見るとわかるのですが、 logsというセクションが使われています。

実際、cloudwatch logsを見に行くと、ロググループが作成され、メトリクスがログストリームにたまっているのを見ることができます。自動作成されるロググループは、自動では失効しないので、ほっておくとひたすらログがたまり、どんどんお金がかかるようになるので注意しましょう。

また、収集するメトリクスをきちんと絞らないと、大量のメトリクスが収集され、やはりお金がかかるので気を付けましょう。

オンプレのElasticsearchをAWS ElasticsearchSerivceへ移設した話。

オンプレで動いていたElasticsearchが遅い(HDDだったため)、負荷が高まると取りこぼす。などの事情があり、いっそマネージドなサービスを利用しようとAWS ESへの移設を行うことになりました。

fluentdあたりの話

もともとの構成は、各ノードにfluentdが存在し、aggregatorとなるfluentdを経由したり経由しなかったりして Elasticsearchにデータを送るようになっていました。

そのおかげで、どこからElasticsearchにデータを送っているのか。というのを探すだけで大変な思いをしたため、aggregatorとなるfluentdを経由しないルートをまず、なくすところから始めました。

移行中は、古いデータはオンプレのElasticsearchにしかなく、それをAWS ESに移す積極的な理由もなかったのと、正常動作することを確認する意味もあり、aggregatorから2つのElasticsearchへデータを送るよう設定しました。 一通り、データの整合性が確認でき段階で、オンプレElasticsearchへの送信を止めて、AWS ESを利用するようにする計画で。

ここで問題が一つおきました。AWS ESのほうがデータが多いのです。しかも、ドキュメント数が多いindexであるほどその割合が大きいという状態。

これについては調べた結果、fluentdからAWS ESに送る際に、時間がかかりすぎてリトライ処理をした際、二重にデータが挿入されていることがわかりました。

fluentd側でidを付与していないため、同じデータを何度も送ると毎回別のドキュメントとして認識され、取り込まれてしまうのが原因です。ログ側に一意となるidを付与すれば解決しそうだけれど、さしあたってダブっているだけであれば問題ないということで、足りないドキュメントがないことを確認し、いったん放置することになりました。

次の問題は、aggregatorのfluentdが0.12系で、一部Elasticsearchに直接送っていたfluentdの中に1系のものがあったことです。

これについては 公式ドキュメント に記載がある通り、 1系からaggregatorに送るところに time_as_integer を設定して解決しました。

あとは特に問題もなく、すすみ、kibanaにもcognito認証を追加して会社のIdPからのSAML連携を利用することで、ついでにセキュリティ的にもよくなりました。

AWS ESのドメイン名がひどい

問題が一つあって、AWS ESでは、独自ドメインの証明書が利用できず、とても分かりにくいドメイン名になってしまうというところです。

こちらについては、AWS ELBのApplicationLoadbalancerを利用して解決しました。

といってもApplicationLoadbalancerの後段には、割り当てたSubnet内のIPアドレスにしかforwardできないので、単純に redirectさせる形にしてます。

具体的には

  • /plugin/kibana/* は AWS ES の plugin/kibana/* へ転送
  • /app/kibanaAWS ES の _plugin/kibana/app/kibana へ、
  • それ以外は AWS ES の _plugin/kibana/ へ

という具合です。

こうすることで、オンプレElasticsearchで動かしていたURLのドメイン部分だけ、新たにApplicationLoadbalancer向けに設定したドメインに変更することで、同じダッシュボードが見られるようになり、URL変更されてダッシュボードを探しなおす。みたいな手間からは解放されています。

kibanaではなくElasticsearchそのものにアクセスしたいときは、redirectされたあと、URLを削って色々やる必要がでてしまいますが、直アクセスするようなケースであれば、わかりにくいドメイン名でも1度設定するだけでしょ。ということであきらめてもらってます。