メールアドレスをマスクしてpostfixのログをfluentdに流す

前回の記事fluent-plugin-filter-parse-postfixはデフォルトでメールアドレスをマスクするようにしている。

<source>
  @type tail
  path /var/log/maillog
  pos_file /var/log/td-agent/postfix-maillog.pos
  tag postfix.maillog
  format none
</source>

<filter postfix.maillog>
  @type grep
  regexp1 message status=
</filter>

<filter postfix.maillog>
  @type parse_postfix
  mask true # デフォルト値
  #include_hash ture
</filter>
{
  "time":"Feb 27 09:02:38",
  "hostname":"MyHOSTNAME",
  "process":"postfix/smtp[26490]",
  "queue_id":"5E31727A35D",
  "to":"*********@myemail.net",
  "domain":"myemail.net",
  "relay":"gateway-f1.isp.att.net[204.127.217.17]:25",
  "conn_use":2, // 一応数値型…
  "delay":0.58, // 一応数値型…
  "delays":"0.11/0.03/0.23/0.20",
  "dsn":"2.0.0",
  "status":"sent",
  "status_detail":"(250 ok ; id=en4req0070M63004172202102)"
}

メールアドレスはstatus_detailなどにも入っているが、それらについてもマスクするようにしている。(spec) あと、 include_hash tureというオプションを追加するとsha512のハッシュ値も付加されて、マスクされたデータを検索することもできる*1

なんでまあ、一応便利に使ってます。

*1:要openssl-devel/libssl-dev

postfixのログをfluentdに入れる a.k.a. ぅゎrubyっょぃ

Postfixのログの「status=...」の行をfluentdに流そうと思ったんですよ。

要件としては

  • メアドはマスクしたい
  • メアドからドメインとりたい
  • statusは値と詳細に分けたい
  • 大量にメールが流れるので早くしたい

それで何も考えずにCの拡張ライブラリを作って、それでflunetdのプラグインを書いたんですよ。

github.com

github.com

以下のような感じで、fluentdにログを流せます。

<source>
  @type tail
  path /var/log/mail.log
  pos_file /var/log/td-agent/postfix-mail.log.pos
  tag postfix.mail
  format none
</source>

<filter postfix.mail>
  @type grep
  regexp1 message status=
</filter>

<filter postfix.mail>
  @type parse_postfix
</filter>

<match postfix.mail>
  @type stdout
</match>

ただ、そもそも既存の処理のベンチマークを取っていなかったので、EC2/t2.microで速度を測ったんですよ。

gist.github.com

ちなみに、正規表現は以下の記事を参考にしました。

SIOS ビッグデータ技術ブログ: Postfixのログをfluentdを使ってTreasureDataに送る

で、結果が次の通り。

                           user     system      total        real
07:09:55 psl(1):       2.450000   0.000000   2.450000 (  2.450314)
07:09:58 psl(2):       2.460000   0.000000   2.460000 (  2.464074)
07:10:00 psl(3):       2.830000   0.000000   2.830000 (  2.834007)
07:10:03 psl(4):       2.500000   0.000000   2.500000 (  2.505221)
07:10:05 psl(5):       2.570000   0.000000   2.570000 (  2.565137)
>sec/prs:              0.000005   0.000000   0.000005 (  0.000005)
>prs/sec:            195160.031226        Inf        Inf (195026.773633)
                           user     system      total        real
07:10:11 psl+m(1):     2.620000   0.000000   2.620000 (  2.623226)
07:10:14 psl+m(2):     2.610000   0.000000   2.610000 (  2.600343)
07:10:16 psl+m(3):     2.590000   0.000000   2.590000 (  2.594375)
07:10:19 psl+m(4):     2.560000   0.000000   2.560000 (  2.557504)
07:10:21 psl+m(5):     2.560000   0.000000   2.560000 (  2.563402)
>sec/prs:              0.000005   0.000000   0.000005 (  0.000005)
>prs/sec:            193199.381762        Inf        Inf (193216.547290)
                           user     system      total        real
07:10:27 rgx(1):       6.420000   0.000000   6.420000 (  6.423937)
07:10:33 rgx(2):       6.410000   0.000000   6.410000 (  6.402610)
07:10:40 rgx(3):       6.410000   0.000000   6.410000 (  6.415075)
07:10:46 rgx(4):       6.470000   0.000000   6.470000 (  6.465956)
07:10:53 rgx(5):       6.560000   0.000000   6.560000 (  6.564861)
>sec/prs:              0.000013   0.000000   0.000013 (  0.000013)
>prs/sec:            77471.335606        Inf        Inf (77465.479252)
                           user     system      total        real
07:11:02 rgx+m(1):     7.110000   0.000000   7.110000 (  7.110184)
07:11:09 rgx+m(2):     6.940000   0.000000   6.940000 (  6.938133)
07:11:16 rgx+m(3):     6.970000   0.000000   6.970000 (  6.968497)
07:11:23 rgx+m(4):     7.150000   0.000000   7.150000 (  7.157125)
07:11:30 rgx+m(5):     6.950000   0.000000   6.950000 (  6.945317)
>sec/prs:              0.000014   0.000000   0.000014 (  0.000014)
>prs/sec:            71184.510251        Inf        Inf (71186.016988)

秒間19万行をパースする速度が必要だったかといえばそんなわけはなく。

Rubyは十分速いなぁ…先に速度測れよなぁ…という教訓が得られました。

ridgepole+alter .. , lock=none

Ridgepole v0.6.4で、ALTER文にLOCK=NONEとか付けられるようにしました。

$ ridgepole -a -c '{adapter: mysql2, database: employees}' --alter-extra="LOCK=NONE" --dry-run
Apply `Schemafile` (dry-run)
add_column("dept_manager", "to_date2", :date, {:null=>false, :after=>"from_date"})
remove_column("dept_manager", "to_date")

# ALTER TABLE `dept_manager` ADD `to_date2` date NOT NULL AFTER `from_date`,
# LOCK=NONE
# ALTER TABLE `dept_manager` DROP `to_date`,
# LOCK=NONE

$ ridgepole -a -c '{adapter: mysql2, database: employees}' --alter-extra="LOCK=NONE" --debug
Apply `Schemafile`
...
-- add_column("dept_manager", "to_date2", :date, {:null=>false, :after=>"from_date"})
   (42.2ms)  ALTER TABLE `dept_manager` ADD `to_date2` date NOT NULL AFTER `from_date`,LOCK=NONE
   -> 0.0428s
-- remove_column("dept_manager", "to_date")
   (46.9ms)  ALTER TABLE `dept_manager` DROP `to_date`,LOCK=NONE
   -> 0.0471s

MySQLCREATE INDEXDROP INDEXについては、ALTER文を使うように指示するオプションを追加しました。

$ ridgepole -a -c '{adapter: mysql2, database: employees}' --alter-extra="LOCK=NONE" --mysql-use-alter --dry-run
Apply `Schemafile` (dry-run)
remove_index("dept_manager", {:name=>"emp_no"})
add_index("dept_manager", ["emp_no"], {:name=>"emp_no2", :using=>:btree})

# ALTER TABLE `dept_manager` DROP INDEX `emp_no`,
# LOCK=NONE
# ALTER TABLE `dept_manager` ADD  INDEX `emp_no2` USING btree (
# `emp_no`)
# ,LOCK=NONE

$ ridgepole -a -c '{adapter: mysql2, database: employees}' --alter-extra="LOCK=NONE" --mysql-use-alter --debug
Apply `Schemafile`
...
-- remove_index("dept_manager", {:name=>"emp_no"})
   (19.2ms)  ALTER TABLE `dept_manager` DROP INDEX `emp_no`,LOCK=NONE
   -> 0.0200s
-- add_index("dept_manager", ["emp_no"], {:name=>"emp_no2", :using=>:btree})
   (23.4ms)  ALTER TABLE `dept_manager` ADD  INDEX `emp_no2` USING btree (`emp_no`),LOCK=NONE
   -> 0.0243s

ridgepole+pt-osc

Ridgepole--external-scriptというオプションを付けました。 更新SQLの実行を外部スクリプトで行うオプションです。

これで、一応pt-oscを使って、DDLを実行できるようにはなりました。あとは、MySQLのALTER文で末尾にLOCK=NONEを付けるとか。

ただ、MySQLのインデックス追加・削除に関してはCREATE INDEXDROP INDEXが渡されるため、ALTER文への書き換えが必要です。 また、ALTER文についても、pt-oscで実行する場合はテーブル名と以降のSQLへの分解が必要です。

Example

pt-osc.rb

#!/usr/bin/env ruby
require 'open3'
require 'shellwords'

def parse_sql(sql)
  case sql
  when /\AALTER +TABLE +`([^`]+)`(.+)\z/i
    [$1, $2]
  when /\ACREATE +INDEX +`([^`]+)`(.+)ON +`([^`]+)`(.+)\z/
    [$3, "ADD INDEX `#{$1}` #{$2} #{$4}"]
  when /\ADROP +INDEX +`([^`]+)` +ON +`([^`]+)`\z/i
    [$2, "DROP INDEX `#{$1}`"]
  else
    raise 'Unsuppored SQL'
  end
end

table, alter = parse_sql(ARGV[0])

dsn = "h=localhost,u=root,p=root,D=employees,t=#{table},A=utf8"

cmd = ['pt-online-schema-change']
cmd << '--alter' << alter
#cmd << '--dry-run'
cmd << '--execute'
cmd << dsn

cmd = Shellwords.join(cmd)
out, err, status = Open3.capture3(cmd)

puts out
$stderr.puts err
exit status.to_i

実行してみる

まずはdry-run。

$ ridgepole -c database.yml -a --external-script ./pt-osc.rb --dry-run
pply `Schemafile` (dry-run)
add_column("titles", "to_date2", :date, {:after=>"from_date"})
remove_column("titles", "to_date")
remove_index("titles", {:name=>"emp_no"})
add_index("titles", ["emp_no"], {:name=>"emp_no2", :using=>:btree})

# ALTER TABLE `titles` ADD `to_date2` date AFTER `from_date`
# ALTER TABLE `titles` DROP `to_date`
# DROP INDEX `emp_no` ON `titles`
# CREATE  INDEX `emp_no2` USING btree ON `titles` (
# `emp_no`)

問題なさそうなので適用します。

$ ridgepole -c database.yml -a --external-script ./pt-osc.rb
Apply `Schemafile`
-- add_column("titles", "to_date2", :date, {:after=>"from_date"})
Execute ./pt-osc.rb ALTER\ TABLE\ \`titles\`\ ADD\ \`to_date2\`\ date\ AFTER\ \`from_date\`
./pt-osc.rb: pt-online-schema-change --alter \ ADD\ \`to_date2\`\ date\ AFTER\ \`from_date\` --execute h\=localhost,u\=root,p\=root,D\=employees,t\=titles,A\=utf8
No slaves found.  See --recursion-method if host vagrant-ubuntu-trusty-64 has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
Altering `employees`.`titles`...
Creating new table...
Created new table employees._titles_new OK.
Altering new table...
Altered `employees`.`_titles_new` OK.
2015-11-20T16:09:42 Creating triggers...
2015-11-20T16:09:42 Created triggers OK.
2015-11-20T16:09:42 Copying approximately 442453 rows...
2015-11-20T16:09:45 Copied rows OK.
2015-11-20T16:09:45 Swapping tables...
2015-11-20T16:09:45 Swapped original and new tables OK.
2015-11-20T16:09:45 Dropping old table...
2015-11-20T16:09:45 Dropped old table `employees`.`_titles_old` OK.
2015-11-20T16:09:45 Dropping triggers...
2015-11-20T16:09:45 Dropped triggers OK.
Successfully altered `employees`.`titles`.
   -> 3.6346s
-- remove_column("titles", "to_date")
Execute ./pt-osc.rb ALTER\ TABLE\ \`titles\`\ DROP\ \`to_date\`
./pt-osc.rb: pt-online-schema-change --alter \ DROP\ \`to_date\` --execute h\=localhost,u\=root,p\=root,D\=employees,t\=titles,A\=utf8
No slaves found.  See --recursion-method if host vagrant-ubuntu-trusty-64 has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
Altering `employees`.`titles`...
Creating new table...
Created new table employees._titles_new OK.
Altering new table...
Altered `employees`.`_titles_new` OK.
2015-11-20T16:09:46 Creating triggers...
2015-11-20T16:09:46 Created triggers OK.
2015-11-20T16:09:46 Copying approximately 1 rows...
2015-11-20T16:09:49 Copied rows OK.
2015-11-20T16:09:49 Swapping tables...
2015-11-20T16:09:49 Swapped original and new tables OK.
2015-11-20T16:09:49 Dropping old table...
2015-11-20T16:09:49 Dropped old table `employees`.`_titles_old` OK.
2015-11-20T16:09:49 Dropping triggers...
2015-11-20T16:09:49 Dropped triggers OK.
Successfully altered `employees`.`titles`.
   -> 3.1043s
-- remove_index("titles", {:name=>"emp_no"})
Execute ./pt-osc.rb DROP\ INDEX\ \`emp_no\`\ ON\ \`titles\`
./pt-osc.rb: pt-online-schema-change --alter DROP\ INDEX\ \`emp_no\` --execute h\=localhost,u\=root,p\=root,D\=employees,t\=titles,A\=utf8
No slaves found.  See --recursion-method if host vagrant-ubuntu-trusty-64 has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
Altering `employees`.`titles`...
Creating new table...
Created new table employees._titles_new OK.
Altering new table...
Altered `employees`.`_titles_new` OK.
2015-11-20T16:09:49 Creating triggers...
2015-11-20T16:09:49 Created triggers OK.
2015-11-20T16:09:49 Copying approximately 1 rows...
2015-11-20T16:09:51 Copied rows OK.
2015-11-20T16:09:51 Swapping tables...
2015-11-20T16:09:51 Swapped original and new tables OK.
2015-11-20T16:09:51 Dropping old table...
2015-11-20T16:09:51 Dropped old table `employees`.`_titles_old` OK.
2015-11-20T16:09:51 Dropping triggers...
2015-11-20T16:09:51 Dropped triggers OK.
Successfully altered `employees`.`titles`.
   -> 2.3925s
-- add_index("titles", ["emp_no"], {:name=>"emp_no2", :using=>:btree})
Execute ./pt-osc.rb CREATE\ \ INDEX\ \`emp_no2\`\ USING\ btree\ ON\ \`titles\`\ \(\`emp_no\`\)\
./pt-osc.rb: pt-online-schema-change --alter ADD\ INDEX\ \`emp_no2\`\ \ USING\ btree\ \ \ \(\`emp_no\`\)\  --execute h\=localhost,u\=root,p\=root,D\=employees,t\=titles,A\=utf8
No slaves found.  See --recursion-method if host vagrant-ubuntu-trusty-64 has slaves.
Not checking slave lag because no slaves were found and --check-slave-lag was not specified.
Operation, tries, wait:
  copy_rows, 10, 0.25
  create_triggers, 10, 1
  drop_triggers, 10, 1
  swap_tables, 10, 1
  update_foreign_keys, 10, 1
Altering `employees`.`titles`...
Creating new table...
Created new table employees._titles_new OK.
Altering new table...
Altered `employees`.`_titles_new` OK.
2015-11-20T16:09:51 Creating triggers...
2015-11-20T16:09:51 Created triggers OK.
2015-11-20T16:09:51 Copying approximately 1 rows...
2015-11-20T16:09:54 Copied rows OK.
2015-11-20T16:09:54 Swapping tables...
2015-11-20T16:09:54 Swapped original and new tables OK.
2015-11-20T16:09:54 Dropping old table...
2015-11-20T16:09:54 Dropped old table `employees`.`_titles_old` OK.
2015-11-20T16:09:54 Dropping triggers...
2015-11-20T16:09:54 Dropped triggers OK.
Successfully altered `employees`.`titles`.
   -> 3.1088s

正常にDBの更新が行われました。再度、適用しても変更はありません。

$ ridgepole -c database.yml -a --external-script ./pt-osc.rb
Apply `Schemafile`
No change

その他

pt-online-schema-changeがSQLの間違いなどで失敗しても、終了コードが0になって胃るっぽいんですが、どうしたもんでしょうね…

CloudWatch Logsを見るやつ作った

LogsFlowというCloudWtach LogsのビューアWebアプリ作りました。

github.com

検索とかは全然なくて、だた見れるだけ。

URLが/groups/my-group/streams/my-streamsのようにREST的なので、適当なツールからぱっとログを見るのにはいいかも。

あと、/groups/my-group/streams/my-streams.jsonとか/groups.jsonとか開くと、JSONがふってきます。

f:id:winebarrel:20151120135403p:plain

ログ表示画面はTerminalっぽいかっちょいい画面にしたかったけど、参考になるcssとかが見つからず。残念。

どうぞご利用ください。

Barkdog v0.1.3

DatadogのアラートDSL管理ツール Barkdog v0.1.3をリリースしました。

github.com

Libratoで監視まわりを全部まかなおうとしてたとき、アラート管理にはテンプレート必須だなー、と思ったのでつけました。

以下のように、いろんな箇所でテンプレートつかえます。

template "cpu template" do
  query "avg(last_5m):avg:#{context.target}.load_avg.1m{host:i-XXXXXXXX} > 1"
  message context.message
  options do
    notify_no_data true
    no_data_timeframe 2
    notify_audit true
    silenced({})
  end
end

monitor "Check load avg", :type=>"metric alert" do
  context.message = "@winebarrel@example.net"
  include_template "cpu template", :target=>"ddstat"
end

template "basic monitor" do
  monitor "#{target} cpu" do
    query "avg(last_5m):avg:#{context.target}.load_avg.1m{host:i-XXXXXXXX} > 1"
    ...
  end

  # any other monitor
  monitor ...
end

"myhost".tap do |host|
  include_template "basic monitor", :target=>host
  include_template "mysql monitor", :target=>host
  ...
end

これで、ベーシックな監視(CPUとかI/Oとか)をテンプレート化して、すべてのホストにひもづけて、MySQLをインストールしているホストにはMySQLのテンプレートをひもづけて。。。とかがやりやすくなったと思います。

ところで、Datadogで監視やってる人はそのへんどうしているんだろう?すでにテンプレートの仕組みとかあるんだろうか?

どうぞご利用ください。

Miam v0.2.2.beta3

Miam v0.2.2.beta3をリリースしました。

github.com

主な変更点は以下の通り。

  • Improve update (show diff)
  • Support Template
  • Add --ignore-login-profile option
  • Sort policy array

Improve update (show diff)

いままでdry-runではアップデート後の値しか表示していなかったんですが、diffを表示するようにしました。

f:id:winebarrel:20151010004721p:plain

Support Template

テンプレート機能をサポートしました。

emplate "common-policy" do
  policy "my-policy" do
    {"Version"=>context.version,
     "Statement"=>
      [{"Action"=>
         ["s3:Get*",
          "s3:List*"],
        "Effect"=>"Allow",
        "Resource"=>"*"}]}
  end
end

user "bob", :path => "/developer/" do
  login_profile :password_reset_required=>true

  groups(
    "Admin"
  )

  include_template "common-policy", version: "2012-10-17"
end

Add --ignore-login-profile option

login profileのpassword_reset_requiredの変更を無視できるようになりました。

f:id:winebarrel:20151010005029p:plain

Sort policy array

各ポリシー内のArrayの順序が差分として出てしまうことがあったので、無視するようにしました。(問題はないはず)

どうぞご利用ください。