Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

稼働時間の監視には、Webサイト、API、およびサーバーの可用性のチェックが含まれます。モニターは、指定された間隔内で特定のエンドポイントをプローブして、それが使用可能かどうかを判別します。目標は、システムのSLAで指定されているように、契約レベルの可用性を達成し、契約が満たされていない場合の違いを判断することです。

この記事では、Prometheusblackbox_exporterに基づく稼働時間監視システムを構築します。カスタムHTTP監視システムを構築するのは簡単かもしれませんが、エクスポーターの周りにラッパーを構築することで、他の多くのプローブ手法にアクセスし、システムの他の要素をすばやく監視できます。

この記事では、いくつかのテクノロジーの使用について説明します。稼働時間システムの詳細に入る前に、各コンポーネントについて説明します。

Google Compute Engine(GCE)とは何ですか?

Compute Engineは、AWSのEC2コンピューティングサービスに似たGoogleのクラウドコンピューティングサービスです。 GCEは安全でカスタマイズ可能であり、小規模なマシン(最大32個のvCPUと128 GBのメモリをサポート)から標準的なマシン(最大224個のvCPUと896 GBのメモリをサポート)やその他の集中的なワークロード用のハイエンドマシンまで、さまざまなワークロードに適合します。コンピューターをオンデマンドで利用して、時間ごとのニーズに合わせて拡張します。

GCEは、コンテナー、インスタンステンプレート、マネージドインスタンスグループなど、アプリのデプロイにさまざまなデプロイメカニズムをサポートしています。この記事では、Ruby稼働時間モニターをDockerコンテナーにバンドルしてデプロイします。

クラウドストレージとは何ですか?

Google Cloud Storageは、AWSのS3サービスと同様の高可用性オブジェクトストレージサービスです。 Cloud Storageは、最新のアプリのいくつかのユースケースを可能にする多くのストレージ機能を提供します。 RubyでCloudStorageを使い始めるには、google-cloud-storageを使用します クラウドストレージからファイルをアップロードおよびダウンロードするだけでなく、認証するためのgem:

require 'google/cloud/storage'

def upload_file bucket_name:, file_path:, file_name: nil
 storage = Google::Cloud::Storage.new
 bucket = storage.bucket bucket_name

 file = bucket.create_file file_path, file_name
end

def download_file bucket_name: file_path, file_name: nil
 storage = Google::Cloud::Storage.new
 bucket = storage.bucket bucket_name
 file = bucket.file file_name

 file.download file_path
end

GOOGLE_APPLICATION_CREDENTIALSを設定する必要があります ご使用の環境で、適切なサービスアカウントキーをポイントします。すべてのGoogleクライアントgemは、承認のためにこの環境変数を検索します。それ以外の場合は、認証固有のパラメータをGoogle::Cloud::Storage.newに渡す必要があります 。ただし、アプリがGCE VMで実行されている場合、これはすでに環境に設定されています。

Cloud PubSubとは何ですか?

Cloud PubSubは、GoogleCloudが提供するパブリッシュ/サブスクライブメッセージングサービスです。この形式の通信は、AWSのSNSと同様に、非同期のサービス間通信を容易にするために使用されます。非同期通信を使用してシステムを構築すると、システムのパフォーマンス、スケーラビリティ、および信頼性を向上させることができます。 RubyでCloudPubSubの使用を開始するには、google-cloud-pubsubを使用します イベントを認証、公開、リッスンするgem:

require 'google/cloud/pubsub'

def publish_message topic_id:, message: nil
 pubsub = Google::Cloud::Pubsub.new
 topic = pubsub.topic topic_id

 topic.publish_async message do |result|
  raise "Failed to publish message" unless result.succeeded?
  puts "Message published asynchronously"
 end

 topic.async_publisher.stop.wait!
rescue StandardError => e
 puts "Received error while publishing: #{e.message}"
end

def receive_message subscription_id: nil, wait_time: 200.seconds
 pubsub = Google::Cloud::Pubsub.new

 subscription = pubsub.subscription subscription_id
 subscriber = subscription.listen do |received_message|
  puts "Received message: #{received_message.data}"
  received_message.acknowledge!
 end

 subscriber.start
 sleep wait_time
end

:クラウドストレージについて説明した認証もここに適用されます。

Cloud StorageとPubSubを活用すると、非常に興味深いソリューションを構築できます。多くの場合、オブジェクトをアップロードし、更新(ライフサイクル)を追跡して、特定のイベントに基づいて特定のアクションを作成、更新、削除、および実行します。それでも抽象的であると思われる場合は、2つのユースケースを調べてみましょう。

  • イメージサービス:イメージサービスの構築。画像と動画のストレージを提供し、これらのデータの変換を実行するCloudinaryに似たものを作成したいとします。 Cloud Storageはデータの保存とバージョン管理に役立ちますが、PubSubを使用すると、顧客が前処理されたバージョンを要求する前であっても、バケットからのイベントをリッスンし、データに対して特定のタイプの前処理を実行できます。
  • 構成ファイルを配布します。インフラストラクチャエンジニアリングの一般的な問題は、構成を複数のサーバーに展開し、簡単にロールバックできるようにすることです。サーバー構成を担当する中央サーバーが必要であり、構成を1回更新して、構成をサーバーのフリートに配布したいとします。 CloudStorageとCloudPubSubを使用することで、PubSubを介してリッスンするエージェントをサーバー上に構築し、オブジェクト通知を取得して、これらのイベントに基づいてアクションを実行できます。さらに、それが悪い変更であった場合(間違った構成変更がダウンタイムの一般的な理由です😩)、オブジェクトのバージョン管理を使用してロールバックを実行できます。

この記事では、上記の2番目のユースケースを使用して、BlackboxExporter用のRubyラッパーを構築します。ラッパーは、あるプロセスでエクスポーターを実行し、別のプロセスを実行してGCPのバケットからの構成の変更を監視してから、エクスポーターをライブリロードします。準備はできたか?楽しみましょう!

ブラックボックスエクスポーターとは何ですか?

Blackbox Exporterは、HTTP、HTTPS、DNS、TCP、およびICMPを介してエンドポイントをプローブするためにPrometheusチームによって構築されたオープンソースツールです。エクスポーターは、GrafanaおよびPrometheusのデプロイと一緒にデプロイする必要があります。完全なセットアップは次のようになります:

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

ブラックボックスラッパーは構成されたすべてのエンドポイントをプローブし、Prometheusは他のターゲットと同様にエクスポーターをスクレイプします。次に、Grafanaはグラフ化するPrometheusからデータを取得します。 blackbox_exporter --config.file blackbox.ymlのようなエクスポーターバイナリを実行します 。 Blackbox Exporterを使用すると、バイナリをシャットダウンして再起動することなく、新しい構成でエクスポーターをライブリロードすることもできます。これは、秒単位で測定された間隔でエンドポイントをスクレイピングする場合に非常に役立ちます。

BlackboxWrapperサービスの仕様

コードを深く掘り下げる前に、サービスの仕様を強調しましょう:

  • BlackboxWrapper サービスは2つのプロセスを実行します。
    • 最初のプロセスはblackbox_exporterを実行します バイナリ。
    • 2番目のプロセスはGCPからのバケットの変更をリッスンし、最初のプロセスを再開します。
  • サービスはDockerイメージとしてデプロイされます。これにより、blackbox_exporterと一緒にサービスをパッケージ化できるようになります。 バイナリ。

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

構築を始めましょう

まず、アプリディレクトリを作成してから、ディレクトリに入ります。

mkdir blackbox-wrapper && cd blackbox-wrapper

標準のRubyアプリケーションと同様に、bundlerを使用します ラッパーの依存関係を管理します。 Gemfileを作成する:

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

ruby '2.7.2'

gem 'google-cloud-storage'
gem 'google-cloud-pubsub'
gem 'rake'
gem 'pry'

次に、bundle installを実行します 。

次に、コードを保持するファイルを作成します:app.rb

このファイルは、当社のサービスへのエントリポイントとして機能します。アプリをコンテナにデプロイするため、このファイルはCMDで指定されます Dockerfileのコマンド 後で。

touch app.rb

Dockerfileの作成

一部の項目は意図的にこのファイルから省略されていますが。以下のコードは、この記事に必要な重要なコンポーネントを強調しています。

FROM ruby:2.7.2

RUN mkdir /app
WORKDIR /app
COPY . .

# Install other dependencies
...

# Download & Install blackbox exporter
RUN curl -SL \
    https://github.com/prometheus/blackbox_exporter/releases/download/v0.18.0/blackbox_exporter-0.18.0.linux-386.tar.gz | \
    tar xvz -C /tmp && \
    mv /tmp/blackbox_exporter-0.18.0.linux-386/blackbox_exporter /usr/local/bin && \
    mkdir /etc/blackbox && \
    mv /tmp/blackbox_exporter-0.18.0.linux-386/blackbox.yml /etc/blackbox/

# Specify entry point.
CMD ["bundle", "exec", "ruby", "app.rb" ]

上記から、次の点に注意する必要があります。

  • Rubyイメージを使用しました-ruby:2.7.2 --Rubyがインストールされたベースイメージとして。
  • blackbox_exporterをインストールしました バイナリで、PATHからアクセスできるディレクトリに移動しました 。
  • app.rbを実行するコンテナのエントリポイントを指定しました コンテナの起動時。
ラッパーサービスの構築

これは、すべてを接着するRubyサービスです。 main.rb内 、次のように配置します:

require 'rubygems'
require 'bundler/setup'
require "google/cloud/pubsub"
require "google/cloud/storage"

CONFIG_BUCKET = ENV['BUCKET_NAME']
TOPIC = ENV['PUBSUB_TOPIC']
TOPIC_SUBSCRIPTION = ENV['TOPIC_SUBSCRIPTION']

class ProcessNotification

  def initialize(file, attr, blackbox_exporter)
    @file = file
    @attr = attr
    @blackbox_exporter = blackbox_exporter
  end

  def call
    return if @attr['eventType'] == 'OBJECT_DELETE'

    @blackbox_exporter.write @file
    @blackbox_exporter.reload
  end
end

class BlackBoxExporter
  CONFIG_FILE = '/etc/blackbox/blackbox.yml'

  def initialize
    @blackbox_pid = nil
  end

  def start
    return unless @blackbox_pid.nil?

    @blackbox_pid = fork do
      exec('blackbox_exporter', '--config.file', CONFIG_FILE)
    end
  end

  def write(file)
    file.download CONFIG_FILE
  end

  def reload
    # Send SIGHUP signal
    Process.kill('HUP', @blackbox_pid)
  end

  def shutdown
    Process.kill('KILL', @blackbox_pid)
  end
end

class Subscriber
  class NotificationConfigError < StandardError
  end

  SUPPORTED_FILE_TYPES = ['blackbox.yml']

  def initialize(blackbox_exporter)
    @pubsub = Google::Cloud::Pubsub.new
    @storage = Google::Cloud::Storage.new
    @subscription_name = ENV['TOPIC_SUBSCRIPTION']  # Retrieve a subscription
    @bucket = @storage.bucket CONFIG_BUCKET
    @subscription = @pubsub.subscription @subscription_name
    @blackbox_exporter = blackbox_exporter
  end

  def listen
    create_notification_config

    puts "Starting subscriber"

    @subscriber = @subscription.listen do |received_message|
      process_notification(received_message)
    end

    @subscriber.on_error do |exception|
      process_exception(exception)
    end

    @subscriber.start
  end

  def process_notification(received_message)
    data = received_message.message.data
    published_at = received_message.message.published_at
    attributes = received_message.message.attributes

    puts "Data: #{data}, published at #{published_at}, Attr: #{attributes}"
    received_message.acknowledge!

    parsed_data = JSON.parse(data)
    file_name = parsed_data['name']
    return unless SUPPORTED_FILE_TYPES.include?(file_name)

    file = @bucket.file file_name
    process_notification = ProcessNotification.new(file, attributes, @blackbox_exporter)
    process_notification.call
  end

  def process_exception(exception)
    puts "Exception: #{exception.class} #{exception.message}"
  end

  def shutdown
    @subscriber.stop!(10)
  end

  def create_notification_config
    topic = @pubsub.topic TOPIC

    notification_exists = @bucket.notifications.count == 1
    unless notification_exists
      @bucket.notifications.each do |notification|
        notification.delete
      end
    end

    @bucket.create_notification topic.name

  rescue StandardError => e
    raise NotificationConfigError, e.message
  end
end

class BlackboxWrapper
  def initialize
    @blackbox_exporter = BlackBoxExporter.new
    @subscriber = Subscriber.new(@blackbox_exporter)
  end

  def start
    @blackbox_exporter.start
    @subscriber.listen

    at_exit do
      @blackbox_exporter.shutdown
      @subscriber.shutdown
    end

    # Block, letting processing threads continue in the background
    sleep
  end
end

blackbox_wrapper = BlackboxWrapper.new
blackbox_wrapper.start

上記は多くのコーディングですが、下から始めて分解してみましょう:

  • BlackboxWrapper :このクラスは、当社のサービスへの入り口です。 -.start メソッドは次のことを行います:
    • blackbox_exporterを開始します エンドポイントのプロービングを開始するための別のプロセスのバイナリ。
    • subscriberを開始します バケットの変更をリッスンする別のプロセスで。
    • 次にsleepを呼び出します メインプロセスで、アプリが無限に実行されるようにします。
  • BlackboxExporterはどのように機能しますか 動作しますか?
    • .start メソッドはexecを使用します blackbox_exporterを実行するカーネルメソッド 別のプロセスのバイナリ。
    • .reload メソッドはSIGHUPを送信します blackbox_exporterをライブリロードするように通知します 新しい構成のバイナリ。 ProcessNotificationからお気づきかもしれませんが クラスの場合、エクスポーターがリロードされる前に、新しい構成ファイルが構成ファイルの場所に書き込まれます。
  • subscriberはどのように機能しますか 動作しますか?
    • .listen メソッドは、NotificationConfiguationの作成から始まります 。 NotificationConfiguration 次の3つを指定するルールです。
      • 通知を受信するためのpub/subのトピック。
      • 通知の送信をトリガーするイベント。通知をトリガーできるさまざまなイベントタイプを表示するには、ここをクリックしてください。
      • 通知に含まれる情報。
    • #create_notification_config このメソッドは、NotificationConfigurationが1つだけであることも保証します;それ以外の場合は、すべてを削除して作成します。これにより、通知が1回だけ送信されます。
    • .listen メソッドは@subscription.listenも呼び出します サブスクライブしているバケットの通知変更のリッスンを開始します。説明したように、これは別のプロセスで無限に実行されることに注意してください。
    • #process_notification メソッドは、通知の更新が送信されるたびに呼び出されます。 SUPPORTED_FILE_TYPESがあることに注意してください 、これを使用して、気になるバケット内のファイルを識別し、残りのファイルについては何もしません。
  • ProcessNotification :これは、通知の処理、更新された構成のダウンロード、ファイルへの書き込み、およびblackbox_exporterの再読み込みを担当します。 バイナリ。
サービスをローカルで実行する

サービスをローカルで実行してテストするには、アプリディレクトリのルートで次のコマンドを実行します。

export BUCKET_NAME='{insert-bucket-name}'
export PUBSUB_TOPIC='{insert-pubsub-topic}'
export TOPIC_SUBSCRIPTION='{insert-subscription-name}'
export GOOGLE_APPLICATION_CREDENTIALS='{insert-path-to-service-key-json}'

bundle exec ruby app.rb

Googleコンピューティングエンジンへのサービスの導入

クラウドの多くの側面と同様に、同じ結果を達成する方法はたくさんありますが、最新のソフトウェアエンジニアリングでは、いくつかの理由からCI/CDプロセスが推奨されています。そのため、setup-gcloud

を使用してGithubActionsからサービスをデプロイすることに焦点を当てます。

デプロイファイル(.github / Workflows / deploy.yml)を設定しましょう。

name: Build and Deploy to Google Compute Engine

on:
  push:
    branches:
    - main

env:
  PROJECT_ID: ${{ secrets.GCE_PROJECT }}
  GCE_INSTANCE: ${{ secrets.GCE_INSTANCE }}
  GCE_INSTANCE_ZONE: us-central1-a
  BUCKET_NAME: demo-configurations
  PUBSUB_TOPIC: demo-configurations-bucket-notifications
  TOPIC_SUBSCRIPTION: demo-bucket-changes-subscription

jobs:
  setup-build-publish-deploy:
    name: Setup, Build, Publish, and Deploy
    runs-on: ubuntu-latest

    steps:
    - name: Checkout
      uses: actions/checkout@v2

    # Setup gcloud CLI
    - uses: google-github-actions/setup-gcloud@master
      with:
        version: '290.0.1'
        service_account_key: ${{ secrets.GCE_SA_KEY }}
        project_id: ${{ secrets.GCE_PROJECT }}

    # Configure Docker to use the gcloud command-line tool as a credential
    # helper for authentication
    - run: |-
        gcloud --quiet auth configure-docker

    # Build the Docker image
    - name: Build
      run: |-
        docker build --tag "gcr.io/$PROJECT_ID/$GCE_INSTANCE-image:$GITHUB_SHA" .

    # Push the Docker image to Google Container Registry
    - name: Publish
      run: |-
        docker push "gcr.io/$PROJECT_ID/$GCE_INSTANCE-image:$GITHUB_SHA"

    - name: Deploy
      run: |-
        gcloud compute instances update-container "$GCE_INSTANCE" \
          --zone "$GCE_INSTANCE_ZONE" \
          --container-image "gcr.io/$PROJECT_ID/$GCE_INSTANCE-image:$GITHUB_SHA" \
          --container-env "BUCKET_NAME=$BUCKET_NAME,PUBSUB_TOPIC=$PUBSUB_TOPIC,TOPIC_SUBSCRIPTION=$TOPIC_SUBSCRIPTION"

--container-envに注意してください デプロイフェーズでフラグが設定されます。これにより、必要な環境変数がGithubActionsシークレットからコンテナーに安全に渡されます。

シークレットと環境変数

次に、githubアクションのシークレットを設定します。

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

--container-envを使用してコンテナの環境変数を設定します 国旗。 Githubアクションから設定しているため、機密データにはシークレットを使用するか、非機密データには環境変数を使用できます。

GCPリソースの作成

GCPコンソールでバケットを作成しましょう。

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

また、GCPコンソールでPubSubトピックを作成します。

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

クラウドストレージバケットのサービスエージェント(IAMロール)を設定します-pubsub.publisher コンソールで。各プロジェクトには、PubSub通知などのバックグラウンドアクションを担当するクラウドストレージサービスアカウントが関連付けられています。見つける方法については、ここをクリックしてください。

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

最後に、GCPコンソールでサブスクリプションを作成します。

GCE、クラウドストレージ、PubSubを使用してRubyで稼働時間監視システムを構築する

出来上がり! 🎉クラウド機能は正常にデプロイされました。

結論

ここまで進んだら、クッキーに値します🍪。これは、複数の最適化を実現できる、優れたソリューションの最初のバージョンだと思います。たとえば、次のことを実現できます。

  • blackbox_exporterをサーバーレス関数としてデプロイして、稼働時間の監視に最適な複数のリージョンをサポートし、クラウドストレージのバケット構成の更新を担当するマスターサーバーをデプロイします。
  • 潜在的に、前のポイントから、これを一般的なクラウドプロバイダーに統合して同じ機能を実現するアプリに抽象化できるため、クラウドに依存しなくなります。追伸:人気のあるクラウドプロバイダー(GCP、AWS、Azure)は、サービス全体で同じ機能を提供します。
  • 次の記事では、このソリューションに基づいて、クラウドストレージオブジェクトのバージョン管理によるロールバックを提供します。これにより、誤った更新による構成の更新から回復できるようになります。

  • Dockerを使用してデプロイすると、パッケージ化の問題が解決されますが、ご存知かもしれませんが、サービスをパッケージ化するにはさまざまな方法があります。この記事では、わかりやすくするためにDockerを選択しました。

用語集
  • プロメテウス は、オープンソースのシステム監視およびアラートツールキットです。これには、時系列データをスクレイプして保存するサーバー、アプリケーションコードをインストルメント化するためのクライアントライブラリ、アラートを処理するためのアラートマネージャーが含まれます。
  • Grafana は、メトリックが保存されている場所に関係なく、メトリックのクエリ、視覚化、アラート、および理解を可能にする視覚化システムです。
  • ブラックボックスエクスポーター は、HTTP、HTTPS、DNS、TCP、およびICMPを介してエンドポイントをプローブするためにPrometheusチームによって構築されたオープンソースツールです。

  1. Cloud Tuneup Pro でインターネット Cookie とシステム レジストリをスキャンする

    最適化とは、最適なパフォーマンスと速度を得るためにコンピューターをクリーニングして維持することを意味します。定期的な最適化により、PC は常に完璧に機能します。コンピューターの最適化は、手動で行うことも、サードパーティのツールを使用して行うこともできます。前者の方法を使用すると時間がかかる場合があるため、特に初心者の場合は後者の方法をお勧めします。 Advanced PC Cleanup などの最適化アプリケーションをインストールして、コンピューターのパフォーマンスを維持できます。 ただし、上級ユーザーの場合は、次のような質問があるかもしれません: 質問 :物理的に異なる場所にあるコンピュ

  2. クラウド ストレージ:データの保存と転送におけるイノベーション

    人間は常に、自分の脳細胞以外の外部媒体に依存して、将来の世代のために情報を保存および伝達したり、自分の記憶を保存したりしています。洞窟の壁に湿った粘土と泥と彫刻を使用した初期のネアンデルタール人から、情報の保存と伝達に象形文字を使用したより進歩的なエジプト人まで.おそらく、情報技術の世界でストレージ メディアの進歩に (より良い方向への) 変化をもたらしたのは、この人類の生来の特徴です。 1725 年のパンチ カードの時代から、フロッピー ディスク、CD、DVD、ハードディスク、そして最終的にクラウドのような形のないものまで、私たちは長い道のりを歩んできました。その大きな理由は、メディア ス