vague memory

うろ覚えを無くしていこうともがき苦しむ人の備忘録

Mackerel事始め mkr(4) dashboards

f:id:htnosm:20161012181150p:plain

mkr v0.11.1 で dashboardsサブコマンドが追加されています。

公式に詳細説明があります。

が、どういう風に見えるのかは載っていないので、説明にある内容でのダッシュボード表示を試してみます。

dashboards

mkr v0.11.1 以上で dashboards コマンドを使用できます。

$ mkr help | grep -A 1 -e VERSION -e dashboards
VERSION:
   0.11.3
--
     dashboards
     help, h     Shows a list of commands or help for one command

dashboards help

機能は今の所 generate のみです。
-p 付与で標準出力へ、オプション無しだと Mackerel への反映となります。 同一名称(url_path)のダッシュボードが既に存在する場合でも、上書きされます。

$ mkr dashboards generate -h
NAME:
    generate - Generate custom dashboard

USAGE:
    mkr generate

DESCRIPTION:
    A custom dashboard is registered from a yaml file.
    Requests "POST /api/v0/dashboards". See https://mackerel.io/ja/api-docs/entry/dashboards#create.


OPTIONS:
    --print, -p  markdown is output in standard output.

ホストグラフ指定

ホストとメトリックを指定するホストグラフの一覧

ホストグラフの例を表示します。 複数ホスト・メトリックを縦横に並べたダッシュボードの生成になります。 f:id:htnosm:20161012175529p:plain

個別グラフ指定

サービスメトリックや式によるグラフなどを個別に指定するグラフの一覧
- service_name のみの指定でサービスグラフ
- service_name, role_name の指定でロールグラフ
- host_id の指定でホストグラフ
- query の指定で式によるグラフ

ここで言うサービスグラフは サービスメトリック であり、サービス名指定でロールを跨いでの表示ができるわけでは無いようです。
個別グラフの例を表示します。手頃なメトリックがなかったので指定しているメトリックは変えています。 f:id:htnosm:20161012175530p:plain


yamlを定義しておくことで履歴管理、ある程度の使い回しができるようになります。 WebのUIでURLコピー&ペーストで作成するよりは作成しやすいかなと思いました。

Mackerel事始め AWSインテグレーション(EC2)

Mackerel の AWS インテグレーションに EC2 が追加されました。

とある環境で mackerel-agent のバージョンアップの機会があったので導入してみました。
AWSインテグレーションの導入は公式の手順で特に躓くこと無く導入できました。

導入時の注意点

適用する際の注意点としてはこの辺りかと思います。

  • mackerel-agent 未導入でも EC2 の監視が可能
  • mackerel-agent 0.34.0 以上 導入済みの場合、ホスト情報は統合される(重複カウントされない)
    • mackerel-agent 0.34.0 未満 は統合されない(重複カウントされる)
  • Free プランでは利用不可

取得内容の確認

当方環境では下記項目のグラフが表示されました。 ()内はメトリック名です。

  • CPU (custom.ec2.cpu)
  • CPU Credit Balance/Usage (custom.ec2.cpu_credit.*)
  • Disk IO Read/Write (custom.ec2.disk_io.*)
  • Disk OPS Read/Write (custom.ec2.disk_ops.*)
  • Network Packets (custom.ec2.network_packets.*)
  • Network Traffic (custom.ec2.network.*)
  • Status Check Failed (custom.ec2.status_check_failed.*)

取得間隔は 5分と記載がありますが、 EC2側で Detailed Monitoring が有効となっている場合は 1分のグラフ表示となっていました。

mackerel-agent 無し

  • mackerel-agent 未導入、もしくはバージョンが 0.34.0 未満の場合
    • mackerel-agent 導入済みホストとは別のホストとして登録される

f:id:htnosm:20160831064933p:plain

mackerel-agent 有り

  • mackerel-agent バージョン 0.34.0 以上が導入済みの場合
    • 同一ホストとして登録される

f:id:htnosm:20160831064956p:plain

重複登録後に統合を行いたい場合

重複登録された後に mackerel-agent のアップデートを行いましたが、 EC2上で mackerel-agent をアップデート だけでは反映されませんでした。
アップデート時に mackerel-agent の再起動が掛かっており、 コマンドで確認しても正常に更新されている状態です。

  • EC2 上の mackerel-agent Version
    • 最新版(0.34.0) へ更新済み
$ /usr/bin/mackerel-agent version
mackerel-agent version 0.34.0 (rev 694915a) [linux 386 go1.6.2]
  • Mackerel 上のホスト情報
    • 未更新(0.32.2) のまま

f:id:htnosm:20160902210154p:plain

解消方法

ホストIDファイルを削除し新規登録することで、 ホスト情報のMackerelエージェントのバージョンが最新版になり、 AWSインテグレーションでの取得情報との統合が成されました。

# ホストID削除
$ sudo rm /var/lib/mackerel-agent/id
# mackerel-agent 再起動
$ sudo /etc/init.d/mackerel-agent restart

尚、ホストの新規登録のため、既存情報への影響があります。

  • 既存のメトリックスは引き継がれない
  • 既存のホストは Poweroff となる
    • 退役忘れに注意

これまで別の方法で監視していた Status Check Failed が取得できるようになったのは嬉しいです。
mackerel-agent の更新で新規ホスト登録となる点は改善して欲しいと思います。

AWS EC2 インスタンスの復旧(Auto Recovery)設定の落とし穴

AWS EC2 インスタンスには、 Status Checked Failed 発生時に自動的に復旧を試みる機能があります。
設定自体は正しく行えているように見えるのに、いざ異常発生時に復旧に失敗するケースに当たりました。

Action failed. Encountered error calling EC2 recover. (401: AWS was not able to validate the provided access credentials)

復旧に失敗した RecoveryAlarm は IAM Role を使用して CloudFormation で作成していましたが、作成(設定)自体は正常に行えていました。
結論としてはドキュメントに記載があるのですが、IAM Role で復旧アラームを作成している場合、復旧アクションは動作しません。

IAM ロール (たとえば、Amazon EC2 インスタンスプロファイル) を使用している場合は、アラームアクションを使用してインスタンスを停止、終了、または再起動することはできません。

尚、復旧(recover)アクションと同様に、 停止(stop)、終了(terminate)、再起動(reboot) を実行するアラームを作成できますが、 復旧(recover)とその他のアクションでは使用される実行権限が異なります。 (アラーム作成時の権限が使用されるのは 復旧(revocer) の場合のみ)

IAM ロールに基づいて、アラームアクションを使用してインスタンスを停止、終了、または再起動する場合は、EC2ActionsAccess ロールしか使用できません。他の IAM ロールはサポートされていません。別の IAM ロールを使用している場合は、インスタンスを停止、終了、または再起動できません。

AWS Security Token Service (AWS STS) を用いて許可された一時的な認証情報を使用している場合は、アラームアクションを用いて Amazon EC2 インスタンスを復旧することはできません。

ドキュメントの内容だけではわかり難かったので、実際に復旧アクションの挙動を確認してみました。

IAM 準備

IAM Role と IAM User での比較を行います。

IAM Role / IAM User

IAM Role と IAM User を準備します。今回はいずれも PowerUserAccess を付与しています。

  • IAM Role

f:id:htnosm:20160816230225p:plain

  • IAM User

f:id:htnosm:20160816230226p:plain

aws configure

デフォルトで IAM Role を使用し、profile=cfn で IAM User を使用できるよう設定します。

$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************EFEA         iam-role
secret_key     ****************dE0o         iam-role
    region           ap-northeast-1      config-file    ~/.aws/config
$ cat ~/.aws/credentials
[default]
region = ap-northeast-1

[cfn]
aws_access_key_id = ****************VO2A
aws_secret_access_key = ************************************QPgI
region = ap-northeast-1

Cloud Formation テンプレート準備

EC2 + RecoveryAlarm のテンプレートを用意します。

{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Outputs": {
    "InstanceID" : {
      "Value" : { "Ref" : "Ec2Instance" }
    },
    "RecoveryAlarm" : {
      "Value" : { "Ref" : "RecoveryAlarm" }
    }
  },
  "Resources": {
    "Ec2Instance" : {
      "Type" : "AWS::EC2::Instance",
      "Properties" : {
        "ImageId" : "ami-374db956",
        "InstanceType" : "t2.micro",
        "BlockDeviceMappings" : [
          {
            "DeviceName": "/dev/xvda",
            "Ebs": {
              "VolumeType": "gp2",
              "VolumeSize": "8"
            }
          }
        ]
      }
    },
    "RecoveryAlarm": {
      "Type": "AWS::CloudWatch::Alarm",
      "Properties": {
        "Namespace": "AWS/EC2" ,
        "MetricName": "StatusCheckFailed",
        "Statistic": "Minimum",
        "Period": "60",
        "EvaluationPeriods": "5",
        "ComparisonOperator": "GreaterThanThreshold",
        "Threshold": "0",
        "AlarmActions": [ {"Fn::Join" : ["", ["arn:aws:automate:", { "Ref" : "AWS::Region" }, ":ec2:recover" ]]} ],
        "Dimensions": [{"Name": "InstanceId","Value": {"Ref": "Ec2Instance"}}]
      }
    }
  }
}

RecoveryAlarm の作成

IAM Role と IAM User それぞれで CreateStack を実行します。

# IAM Role
aws cloudformation create-stack \
  --stack-name ec2-recoveryalarm-role \
  --template-body file://ec2.json
# IAM User
aws cloudformation create-stack \
  --stack-name ec2-recoveryalarm-user \
  --template-body file://ec2.json \
  --profile cfn
  • IAM Role で作成したStack

f:id:htnosm:20160816230228p:plain

  • IAM User で作成したStack

f:id:htnosm:20160816230227p:plain

f:id:htnosm:20160816230229p:plain

  • 作成したRecoveryAlarm

f:id:htnosm:20160816230230p:plain

RecoveryAlarm の実行確認

作成した RecoveryAlarm の状態を ALARM に更新し、挙動を確認します。 (システムステータスチェックのエラーを意図的に起こすことはできません。)

IAM Role で作成した RecoveryAlarm

# IAM Role で作成したRecoveryAlarm
$ aws cloudwatch set-alarm-state \
  --alarm-name "ec2-recoveryalarm-role-RecoveryAlarm-1SXN4O9QLTKH" \
  --state-value ALARM \
  --state-reason "Manual Update to ALARM"

401 エラーが発生し、Action が失敗します。

f:id:htnosm:20160816230231p:plain

IAM User で作成した RecoveryAlarm

# IAM User で作成したRecoveryAlarm
$ aws cloudwatch set-alarm-state \
  --alarm-name "ec2-recoveryalarm-user-RecoveryAlarm-1HG4YDX2YZ90H" \
  --state-value ALARM \
  --state-reason "Manual Update to ALARM"

IAM User で作成した場合は問題無く、 Action が正常に終了します。

f:id:htnosm:20160816230232p:plain

RecoveryAlarm を再作成

IAM Role で作成し、401エラーとなった RecoveryAlarm を IAM User からの実行で作成し直します。

$ diff ec2.json ec2_update.json
30a31
>         "AlarmName": "ec2-recoveryalarm-role-RecoveryAlarm-renew",
$ aws cloudformation update-stack --stack-name ec2-recoveryalarm-role --template-body file://ec2_update.json --profile cfn
  • RecoveryAlarm の再作成(Delete/Update)

f:id:htnosm:20160816230233p:plain

IAM User で "再作成" した RecoveryAlarm

作成し直した RecoveryAlarm の実行確認をします。

$ aws cloudwatch set-alarm-state \
  --alarm-name "ec2-recoveryalarm-role-RecoveryAlarm-renew" \
  --state-value ALARM \
  --state-reason "Manual Update to ALARM"

IAM User で再作成した場合も、Action が正常に終了しました。

f:id:htnosm:20160816230234p:plain



マネジメントコンソール上と、 describe-alarms で RecoveryAlarm を確認してみましたが、どの権限(IAM)で作成されたかの情報は保持していませんでした。
RecoveryAlarm を作成した際は実行確認を行い、権限エラーが発生しない事を確認しておいた方が良さそうです。

Mackerel事始め 将来予測機能

Mackerel の将来予測機能を試してみます。

f:id:htnosm:20160808180208p:plain

機能としては式による監視で特定の関数(線形回帰)を使って実現するようです。

将来予測機能

式による監視の仕様

公式サイトの記載より

  • 監視間隔は5分ごと
  • 設定可能な式は、グラフの系列が1本になるものだけ
  • 式が複雑過ぎる等で値が取れない場合はステータス Unknown としてアラートが発生
  • 監視項目上限数20
  • Free プランでは使用不可

線形回帰関数

使用できる関数は カスタマイズしたグラフを表示する - Mackerel ヘルプ で確認できます。

  • linearRegression(metrics, duration)
    • 現在時刻から duration 前 までのメトリック値をもとに線形回帰したメトリックを返します
  • timeLeftForecast(metrics, duration, threshold)
    • 現在時刻から duration 前 までのメトリック値をもとに線形回帰した値が threshold になるまでの残り秒数を返します

公式にある例では timeLeftForecast を使用しており、日数で閾値設定するため scale 関数を使用しています。

  • scale(metrics, factor)
    • 定数倍したメトリックを返します

1ホストのメモリ使用量について設定例

あまり良い例(グラフ)が無かったのですが、memory.usedに対して設定をしてみます。

対象グラフ

監視対象となる あるホストの1ヶ月のメモリ使用量のグラフ は以下になります。

f:id:htnosm:20160808180202p:plain

監視設定

1ヶ月分のメトリックを使用して、メモリ使用量が 1.5GB となる日数を取得する式を設定してみます。

scale(timeLeftForecast(host('2KcXXXXXXXX', 'memory.used'),'1mo', 1500000000), 1/86400)

12.81日で到達するという予測となりました。

f:id:htnosm:20160808180203p:plain

Excelの線形予測結果との比較

同metricsから値を取得し、Excelで線形予測を出した結果が以下になります。
取得したDatapoint、丸めなどで誤差が出てますが、概ね同じような予測となりました。

f:id:htnosm:20160808180204p:plain

複数ホスト(role)に対しての設定例

hostIdに対しての設定だけだとホストが増えてきた場合に設定が追いつかなくなるので、role に対して設定します。
グラフの系列を1本だけにする という点に注意すれば他は1ホストの時と同じです。

f:id:htnosm:20160808180205p:plain

ディスク使用量の予測設定例

予測機能の用途としては、やはり公式に記載されているディスク使用量に使用する事が多くなりそうです。

  • 公式記載の式の例
scale(timeLeftForecast(host('host_id', 'filesystem.drive.used'),'3mo', 2000000000000), 1/86400)

式の例

  • 特定ホスト/ドライブを対象
    • hostId = 2KcXXXXXXXX 、 ドライブ = xvda3 の場合
host('2KcXXXXXXXX', 'filesystem.xvda3.used')
  • 特定ホスト/全ドライブを対象
    • ホストに紐付く全ドライブの中の最大値
    • ドライブ指定を「*
max(host('2KcXXXXXXXX', 'filesystem.*.used'))
  • 特定ロール/全ドライブを対象
    • ロールに紐付く全ドライブの中の最大値
    • role = 'service:role' の場合
    • ドライブ指定を「*
max(role('service:role', 'filesystem.*.used'))

パーセンテージ設定

上記例だと絶対値で閾値設定を行う必要があるため、異なるディスクサイズの場合にそれぞれ設定する必要が出てきます。 となるとやはりパーセンテージで設定したくなるので、式を調整します。

  • 対象ドライブ構成

f:id:htnosm:20160808180207p:plain

ドライブ毎にディスクサイズが異なるので、ドライブ毎に設定が必要になるかと思います。 ロール内のディスク構成は同一である場合は以下のように定義可能です。

  • ディスク使用率取得例
divide(
   max(role('service:role','filesystem.xvda3.used'))
  ,max(role('service:role','filesystem.xvda3.size'))
)
  • ディスク使用率の将来予測例
    • 使用率が100%に到達する日数
scale(timeLeftForecast(divide(
   max(role('service:role','filesystem.xvda3.used'))
  ,max(role('service:role','filesystem.xvda3.size'))
),'1mo',1), 1/86400)

通知例

通知メッセージには式の内容がそのまま入ります。 (少々見難いので、将来的には通知メッセージをカスタマイズできるようになると嬉しいですね。)

f:id:htnosm:20160808180206p:plain


通常の閾値設定によるアラート発生前に傾向を掴めるというのは中々面白いのではないでしょうか。
長期間のメトリクスを対象にした設定を行えていないので、ディスク使用量の予測設定をして観察してみたいと思います。

Mackerel事始め mkrで残アラートのクローズ

Mackerelを使用しているとアラート発生後に復旧せずに残り続けるアラートが出てきます。
AWS インテグレーションで CloudWatch 上に値が出力されない物(ELB の ERROR 等、CloudWatch上 0 ではなく、値が無い物)が良く残ります。
Web上から一つずつクリックしてCloseするのは数が多くなってくると厳しいので mkr を使用して一括Closeを行います。

今回の判定条件はアラート発生後 n秒経過した物を対象とします。
(Close実行部分はコメントアウトしています)

export MACKEREL_APIKEY='XXXXXXXXXX'
# 経過秒数を指定
_SEC=86400

# epoch秒生成
_EPOCH=$(expr $(date +'%s') - ${_SEC})
echo "Target: $(date --date "@${_EPOCH}" +'%Y-%m-%d %H:%M:%S')"
#BSD
#echo "Target: $(date -r "${_EPOCH}" +'%Y-%m-%d %H:%M:%S')"

# アラートをクローズ
for _ID in $(mkr alerts | jq -r '.[] | select(.openedAt < '${_EPOCH}').id')
do
  echo "${_ID}"
  # mkr alerts close ${_ID}
done
  • Closed by API f:id:htnosm:20160805023259p:plain

Mackerel事始め mkrでホストの退役

Mackerel で mkr と WebAPI を使用して poweroff にしたホストを退役します。
今回の判定条件は n日前のメトリクスが無いホストとします。(poweroff 直後のホストは非対象としています)

f:id:htnosm:20160429183205p:plain

ApiKey/URL/Metricsを設定

検索対象のメトリクスは loadavg5 等必ず存在する物を指定します。

export MACKEREL_APIKEY='XXXXXXXXXX'
_MACKEREL_URL="https://mackerel.io/api/v0"
_METRICS="loadavg5"
# 処理実行当日を含む日数
_KEEP_DAY=1

日数(epoch秒)生成

_KEEP_TO=$(date --date ''${_KEEP_DAY}' days ago' +'%Y-%m-%d 15:00:00')
_KEEP_TO=$(TZ=UTC date --date "${_KEEP_TO}" +'%s')
_KEEP_FROM=$(expr ${_KEEP_TO} - 86400)
_KEEP_TO=$(expr ${_KEEP_TO} - 1)
echo "From: $(date --date "@${_KEEP_FROM}" +'%Y-%m-%d %H:%M:%S')"
echo "  To: $(date --date "@${_KEEP_TO}" +'%Y-%m-%d %H:%M:%S')"

日数(epoch秒)生成 (BSD)

OSXの場合デフォルトのままだとこちらになります。(GNU date導入済みであれば上記)

_KEEP_TO=$(date -v-${_KEEP_DAY}d +'%Y-%m-%d 15:00:00')
_KEEP_TO=$(date -jf "%Y-%m-%d %H:%M:%S" "${_KEEP_TO}" +%s)
_KEEP_FROM=$(expr ${_KEEP_TO} - 86400)
_KEEP_TO=$(expr ${_KEEP_TO} - 1)
echo "From: $(date -r "${_KEEP_FROM}" +'%Y-%m-%d %H:%M:%S')"
echo "  To: $(date -r "${_KEEP_TO}" +'%Y-%m-%d %H:%M:%S')"

Metrics が存在しない host を退役

status が poweroff 且つ 実行日 - n日 のmetrics存在チェックをし、データが存在しないホストを退役します。
(実行すると有無を言わさず退役になるので、実行部分はコメントアウトしています)

for _ID in $(mkr hosts --status poweroff | jq -c -r '.[].id')
do
  _RES=$(curl -s -S -X GET -H "X-Api-Key: ${MACKEREL_APIKEY}" "${_MACKEREL_URL}/hosts/${_ID}/metrics?name=${_METRICS}&from=${_KEEP_FROM}&to=${_KEEP_TO}" | jq -r '.metrics | length')
  echo "HostId: ${_ID} / DataPoint: ${_RES}"
  #[ ${_RES} -eq 0 ] && mkr retire --force ${_ID}
done

cron等で定期的に実行すると退役漏れが無くせるかと思います。

Jenkins タイトル/ヘッダー文字の変更

Jenkinsを使っていて、複数環境を使い分ける必要が出てきた場合に、 見た目が全部一緒で判り難いので環境別に表示を変えたいと思いました。

f:id:htnosm:20160710205120p:plain

Jenkinsの標準機能では存在せず、Simple Theme Plugin を使用するのが定石のようなので、こちらを使用します。

変更点

ヘッダーの色と Jenkins の文字部分を変更します。ついでにジェンキンスおじさまにはご退場いただきます。

f:id:htnosm:20160710205121p:plain

おじさまとJenkinsロゴはトップページへのリンクが付与されていますが、無くなっても支障が無いので文字列に置き換えます。

環境

Jenkins Ver: 2.7.1

css と js の格納

${JENKINS_HOME}/userContentcss と js を格納します。

  • theme.css
    • backgroud に任意の色を設定
    • theme.js で使用する header-text のstyleを指定
#header{
    background: #99999;
    position: relative;
}
.header-text{
    font-size: 26px;
    line-height: 1.4em;
    color: white;
}
  • theme.js
    • titleStr に任意の文字列を設定
var titleStr = "XXXXX"

// Title Replace
window.addEventListener("load", function() {
  var title = document.querySelector('title')
  title.innerHTML = title.innerHTML.replace(/\[Jenkins\]/, titleStr)
}, false);

// HeaderLogo Replace
document.addEventListener("DOMContentLoaded", function(){
  var img = document.getElementsBySelector('[id="jenkins-home-link"]')[0];
  var alink = img.parentNode;
  alink.removeChild(img);
  alink.appendChild(document.createTextNode(titleStr));
  alink.setAttribute("class", "header-text");
}, false);

変更結果

Jenkinsの設定で URL of theme CSS/JS に格納した theme.css/theme.js を指定すると反映されます。
上からデフォルト、緑、青、赤と3パターンの変更例です。

f:id:htnosm:20160710205122p:plain f:id:htnosm:20160710205123p:plain f:id:htnosm:20160710205124p:plain f:id:htnosm:20160710205125p:plain