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のデプロイと一緒にデプロイする必要があります。完全なセットアップは次のようになります:
ブラックボックスラッパーは構成されたすべてのエンドポイントをプローブし、Prometheusは他のターゲットと同様にエクスポーターをスクレイプします。次に、Grafanaはグラフ化するPrometheusからデータを取得します。 blackbox_exporter --config.file blackbox.yml
のようなエクスポーターバイナリを実行します 。 Blackbox Exporterを使用すると、バイナリをシャットダウンして再起動することなく、新しい構成でエクスポーターをライブリロードすることもできます。これは、秒単位で測定された間隔でエンドポイントをスクレイピングする場合に非常に役立ちます。
BlackboxWrapperサービスの仕様
コードを深く掘り下げる前に、サービスの仕様を強調しましょう:
-
BlackboxWrapper
サービスは2つのプロセスを実行します。- 最初のプロセスは
blackbox_exporter
を実行します バイナリ。 - 2番目のプロセスはGCPからのバケットの変更をリッスンし、最初のプロセスを再開します。
- 最初のプロセスは
- サービスはDockerイメージとしてデプロイされます。これにより、
blackbox_exporter
と一緒にサービスをパッケージ化できるようになります。 バイナリ。
まず、アプリディレクトリを作成してから、ディレクトリに入ります。
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アクションのシークレットを設定します。
--container-env
を使用してコンテナの環境変数を設定します 国旗。 Githubアクションから設定しているため、機密データにはシークレットを使用するか、非機密データには環境変数を使用できます。
GCPリソースの作成
GCPコンソールでバケットを作成しましょう。
また、GCPコンソールでPubSubトピックを作成します。
クラウドストレージバケットのサービスエージェント(IAMロール)を設定します-pubsub.publisher
コンソールで。各プロジェクトには、PubSub通知などのバックグラウンドアクションを担当するクラウドストレージサービスアカウントが関連付けられています。見つける方法については、ここをクリックしてください。
最後に、GCPコンソールでサブスクリプションを作成します。
出来上がり! 🎉クラウド機能は正常にデプロイされました。
ここまで進んだら、クッキーに値します🍪。これは、複数の最適化を実現できる、優れたソリューションの最初のバージョンだと思います。たとえば、次のことを実現できます。
- blackbox_exporterをサーバーレス関数としてデプロイして、稼働時間の監視に最適な複数のリージョンをサポートし、クラウドストレージのバケット構成の更新を担当するマスターサーバーをデプロイします。
- 潜在的に、前のポイントから、これを一般的なクラウドプロバイダーに統合して同じ機能を実現するアプリに抽象化できるため、クラウドに依存しなくなります。追伸:人気のあるクラウドプロバイダー(GCP、AWS、Azure)は、サービス全体で同じ機能を提供します。
-
次の記事では、このソリューションに基づいて、クラウドストレージオブジェクトのバージョン管理によるロールバックを提供します。これにより、誤った更新による構成の更新から回復できるようになります。
-
Dockerを使用してデプロイすると、パッケージ化の問題が解決されますが、ご存知かもしれませんが、サービスをパッケージ化するにはさまざまな方法があります。この記事では、わかりやすくするためにDockerを選択しました。
- プロメテウス は、オープンソースのシステム監視およびアラートツールキットです。これには、時系列データをスクレイプして保存するサーバー、アプリケーションコードをインストルメント化するためのクライアントライブラリ、アラートを処理するためのアラートマネージャーが含まれます。
- Grafana は、メトリックが保存されている場所に関係なく、メトリックのクエリ、視覚化、アラート、および理解を可能にする視覚化システムです。
- ブラックボックスエクスポーター は、HTTP、HTTPS、DNS、TCP、およびICMPを介してエンドポイントをプローブするためにPrometheusチームによって構築されたオープンソースツールです。
-
Cloud Tuneup Pro でインターネット Cookie とシステム レジストリをスキャンする
最適化とは、最適なパフォーマンスと速度を得るためにコンピューターをクリーニングして維持することを意味します。定期的な最適化により、PC は常に完璧に機能します。コンピューターの最適化は、手動で行うことも、サードパーティのツールを使用して行うこともできます。前者の方法を使用すると時間がかかる場合があるため、特に初心者の場合は後者の方法をお勧めします。 Advanced PC Cleanup などの最適化アプリケーションをインストールして、コンピューターのパフォーマンスを維持できます。 ただし、上級ユーザーの場合は、次のような質問があるかもしれません: 質問 :物理的に異なる場所にあるコンピュ
-
クラウド ストレージ:データの保存と転送におけるイノベーション
人間は常に、自分の脳細胞以外の外部媒体に依存して、将来の世代のために情報を保存および伝達したり、自分の記憶を保存したりしています。洞窟の壁に湿った粘土と泥と彫刻を使用した初期のネアンデルタール人から、情報の保存と伝達に象形文字を使用したより進歩的なエジプト人まで.おそらく、情報技術の世界でストレージ メディアの進歩に (より良い方向への) 変化をもたらしたのは、この人類の生来の特徴です。 1725 年のパンチ カードの時代から、フロッピー ディスク、CD、DVD、ハードディスク、そして最終的にクラウドのような形のないものまで、私たちは長い道のりを歩んできました。その大きな理由は、メディア ス