hamayuzinの日記

エンジニアとかデータサイエンティストとかやってます。あの時 あれやってたな的な備忘録にできれば。

【AWS Auto Scaling / new relic】railsで オートスケール時の new relicのアラートポリシーを変更したい

場面

  • オートスケールだぜー サーバーを9時に1台増やして、19時に1台減らすぜー
  • new relicでメトリクス監視したいし、みたいぜー

こまったこ

  • 19時に予定通りにサーバーが死んだときに、new relicのアラートポリシーに引っかかって 「サーバー死んでるぞ 大丈夫か?」と通知がきて、社内が騒然とする

やったこ

  • アラートポリシーを落ちる寸前に、new relic apiを叩いて、サーバーが落ちてもアラートを飛ばさないalert pollicyに変更する。
  • メトリクスは残るけど、通知はこない

手順

  1. new relic の api keyを取得 2.後は以下コード 後は whenever とかでcron設定して、
ChangeAlertPolicy.new.change_alert_policy

を叩くだけ

class ChangeAlertPolicy
  def initialize
    
    @target_server_ips = in_service_auto_scaled_ip_addresses
    @target_new_relic_ids = get_new_relic_server_id
    @new_relic_policy_id = get_new_relic_policy_id
  end

  def change_alert_policy
    return if @target_new_relic_ids.blank? || @new_relic_policy_id.blank?
    change_server_policy
  end

  # 稼働中のauto scaleで増えたサーバーのipaddressを取得する
  def in_service_auto_scaled_ip_addresses
    instance_ips = []

 # 特定のALBにひっつけてあるので、その下のものを全て取得
    arn = Rails.application.secrets.aws_call_load_balancer_arn

    instance_healths = init_alb.describe_target_health(target_group_arn: arn)
    instance_ids = instance_healths.target_health_descriptions.map { |t| t.target.id }

    ec2_instances = init_ec2.describe_instances(instance_ids: instance_ids)
    instance_ips << ec2_instances.reservations.map do |e|
      # 対象外としたサーバーがあれば、tag名とかではじける
      e.instances[0].private_ip_address if e.instances[0].tags.any? { |tag| tag.value == 'hogehoge' }
    end
    instance_ips.flatten
  end

  # オートスケールで増えたサーバーが、new relic側で何のIDとして保持されているか取得
  # 今回は new relic側で ip-172-11-11-11みたいになっていたので、"172.11.11.11".tr('.', '-')して 名前で検索している
  def get_new_relic_server_id
    new_relic_server_ids = []
    @target_server_ips.each do |target_server_ip|
      http = create_http
      req = Net::HTTP::Get.new(URI.parse("#{host_uri}/servers.json?filter[name]=ip_#{target_server_ip.tr('.', '-')}"))
      req['X-Api-Key'] = Rails.application.secrets.new_relic[:api_key]
      res = http.request(req)
      target_server = JSON.parse(res.body)['servers']
      next if target_server.blank?
      new_relic_server_ids << target_server[0]['id']
    end
    new_relic_server_ids
  end

  # サーバーにつけたいalert policyのIDを取得する 名前でOK
  def get_new_relic_policy_id
    http = create_http
    req = Net::HTTP::Get.new(URI.parse("#{host_uri}/alert_policies.json?filter[name]=hoge hoge"))
    req['X-Api-Key'] = Rails.application.secrets.new_relic[:api_key]
    res = http.request(req)
    target_alert_policy = JSON.parse(res.body)['alert_policies']
    return nil if target_alert_policy.blank?
    target_alert_policy[0]['id']
  end

  # 該当のアラートポリシーに、対象となるサーバーのnew relic側のIDを渡すことで、変更している
  def change_server_policy
    http = create_http
    req = Net::HTTP::Put.new(URI.parse("#{host_uri}/alert_policies/#{@new_relic_policy_id}.json"))
    req['X-Api-Key'] = Rails.application.secrets.new_relic[:api_key]
    req['Content-Type'] = 'application/json'
    req.body = { "alert_policy": { "links": { "servers": @target_new_relic_ids } } }.to_json
    http.request(req)
  end

  private

  def init_alb
    Aws::ElasticLoadBalancingV2::Client.new(
      region: 'ap-northeast-1',
      access_key_id: Rails.application.secrets.aws_access_key_id,
      secret_access_key: Rails.application.secrets.aws_secret_access_key
    )
  end

  def init_ec2
    Aws::EC2::Client.new(
      region: 'ap-northeast-1',
      access_key_id: Rails.application.secrets.aws_access_key_id,
      secret_access_key: Rails.application.secrets.aws_secret_access_key
    )
  end

  def host_uri
    URI.parse('https://api.newrelic.com/v2')
  end

  def create_http
    @http ||= begin
      http = Net::HTTP.new(host_uri.host, host_uri.port)
      http.use_ssl = true
      http
    end
  end
end

問題

new relic側で、オートスケールで削除された サーバーを削除していいか迷い中