それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Debian/ubuntu のapt自動アップデートのUnattendedUpgradesを設定する

Debian の apt 更新がめんどくさいのでなんとかする。

昔は cron-apt でやってたけど、今時はどうするのかなーと思って調べてたらUnattendedUpgradesがあったのでコレを設定することに。

Debian Wikiに従ってインストール

sudo apt install unattended-upgrades apt-listchanges

apt-listchanges は無くても良さそう。apt-listchangesはメールで更新の通知をするツール。

アップデートを有効にする

sudo dpkg-reconfigure -plow unattended-upgrades

アップグレードを有効にする。

sudo -e /etc/apt/apt.conf.d/20auto-upgrades

定期的にアップグレードとパッケージリストの更新を有効にする。

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

自動更新の確認

sudo unattended-upgrades --dry-run

dry-run で自動更新の設定と動作をチェックできる。

更新するパッケージの指定と除外はここで設定できる

takuya@orangepizero:~$ cat  /etc/apt/apt.conf.d/50unattended-upgrades | head
// Unattended-Upgrade::Origins-Pattern controls which packages are
// upgraded.
//
// Lines below have the format format is "keyword=value,...".  A
// package will be upgraded only if the values in its metadata match
// all the supplied keywords in a line.  (In other words, omitted
// keywords are wild cards.) The keywords originate from the Release
// file, but several aliases are accepted.  The accepted keywords are:
//   a,archive,suite (eg, "stable")
//   c,component     (eg, "main", "crontrib", "non-free")

設定が不安なら dpkg-recongfigure でも出来る

sudo dpkg-reconfigure -plow unattended-upgrades

no を選ぶと、/etc などのファイル更新でどっちを優先するか決められる。

設定ファイル更新されるの嫌だから、ローカル優先しておいた。

もし更新するパッケージを指定するなら

以下のファイルにアレコレ書いて、更新するパッケージを指定する。 デフォルトで大丈夫だと思うのですが。更新したら動かなくなる野良ビルドドライバとか入ってたら linux-header とかカーネルの更新は避けたいところだったりする。

sudo -e /etc/apt/apt.conf.d/50unattended-upgrades

たとえば、gitlab-ce の パッケージを指定した例。

gitlab-ce をapt に行を追加していて、ominibus パッケージとして使っている場合

// Automatically upgrade packages from these (origin:archive) pairs
Unattended-Upgrade::Allowed-Origins {
    "${distro_id}:${distro_codename}-security";
    "${distro_id}:${distro_codename}-updates";
    // この行を追加
    "o=*packages.gitlab.com/gitlab/gitlab-ce,codename=${distro_codename}";

このようにすると apt に追加した gitlab のパッケージが自動更新の対象になる。

もし自身でapt に追加していたらこのようにする。通常は debian の updates / security を自動インストールしてくれる。

除外したい場合はブラックリストにパッケージ名を書く

アップデートの実行ログ

アップデートの実行ログは /var/log/unattended-upgrades/unattended-upgrades.log に書かれる。

また、dpkg のログが /var/log/dpkg.log に記載されるので、自動更新のヒストリ(履歴・実行記録)をたまに見てやるといい。

参考資料

2021-11-23

ログについて記述を追加

web.archive.org のURLを考える

web.archive.org のURLはどうなってるのかを見てみる

web.archive.org のインターネットアーカイブがとても便利。

著作権云々だとかで魚拓サービスはとても使いにくいので、アーカイブから取るのが良い。

基本的なURL

基本的はURLは日付+取得したいURLになってる

たとえばヤフオクの 20170225084900 のデータを見たい場合

http://web.archive.org/web/20170225084900/http://auctions.yahoo.co.jp/

日付の指定を省略して、どの日付のアーカイブがあるか調べる

http://web.archive.org/web/*/http://auctions.yahoo.co.jp/
http://web.archive.org/web/http://auctions.yahoo.co.jp/ # または

特定月の最新版を取りたい

日付を省略すれば、その月の最新版の付近が取れる。

http://web.archive.org/web/201303/http://auctions.yahoo.co.jp/

特定年の最新版を取りたい

月日を省略すれば、その年の最新版の付近が取れる。

http://web.archive.org/web/2013/http://auctions.yahoo.co.jp/

取り敢えずこの辺ください

存在しなくてもその前後の日付で、アーカイブが存在する一番近い日付にリダイレクトしてくれる。

## あるかないか知らないけど、20130315 のデータくださいってリクエスト
http://web.archive.org/web/20130315/http://auctions.yahoo.co.jp/

画像があるの?

画像も同じようにURLを指定すれば取れると思う。

http://web.archive.org/web/20040610050213/http://example.com/sample.jpg

ただし、ページに埋まってる画像は次のようなURLで取得されることになる。

## 20040610050213のhttp://example.comが存在する場合で、その中に埋まってる画像のURL
http://web.archive.org/web/20040610050213_im/http://example.com/sample.jpg

なんなら、FLVとかもあったりする。

InternetArchive は大切にしたいプロジェクト。

InternetArchiveのwayback machine はいざという時に貴重。

WebArchiveプロジェクトは、ストレージや転送量が大変なプロジェクトだとおもう。でもとても救われることが多い。

なので少しばかり応援してさせていただいた。

バックエンドへのssh のユーザー名で接続先を振り分ける(SSHプロキシ的な踏み台サーバーの作成

ssh でフロントとバックエンドへの接続を簡単にしたい。

ssh でIP浪費するのもめんどくさい話だし。

バックエンドへの接続をもう少し楽にできたら良いんだけど。

ユーザー名でバックエンドへの接続を切り分ける

発想としていくつかある。

フロント┬────── Backend 0
    ├────── Backend 1
    └────── Backend 2

フロント側にアレコレする

フロントにSSHログインする場合:踏み台経由の多段ログイン

  • authorized_keys をつかう
  • /etc/sshd/sshd_config を使う
  • LDAPなどを使う

sshd_config を使う場合

sshd_config の Match を使ってフォワードする。

Match user takuya
    ForceCommand ssh -t backend-host

authorized_keys を使う場合

authorized_keys に登録する公開鍵に次のように書く。

Command ssh -t backend-host ssh-rsa AAAAAAAAAAAA

LDAPを使う。

LDAPの認証エントリに sshd_config/authorized_keys と同等のこと記述したらいい。

2回の認証を省略したい。

そもそも認証状態を転送できないのか? LDAPを使えばワンちゃん。Backend側がフロント側のPAMに聞きに行けばいいんだけど、依存関係が増えてもうめんどくさい。

ポート転送をかける:転送分だけポートが必要

  • フロント:2201 を Backend01 22
  • フロント:2202 を Backend02 22

すぐに思いつくのはポートフォワード。でもVhostに振り分けたいって要望がある時点で、ポートを開けたりIPを触りたくない前提があるわけだから、此の解決策は手軽なゆえに、多くの人は規制により使えないことが多いと思う。

NFSでマウント:ファイルを編集するだけなら

  1. NFSでフロント側からBackendをつなぐ
  2. Backendにはログインしない。
  3. 主にファイル編集だけなら有用
  4. ChrootDirectory と組み合わせて戦える
  5. 同一ホスト内のLXCならなおさら有効?

NFS マウント/Chroot 

BackendがLXCで同一ホスト内なら、SSHログイン後の Chroot Directory や Home をアレコレすることでなんとかなる。

実はこれは意外とオススメ。NFSでマウントして触らせておくのはバックエンドサーバーのプロセスをそのまま触られないので結構バランスする。

Chroot設定。

Match Group sftpuser
  ChrootDirectory /var/backend/01

NFS マウント

sudo mount Backend01:/var/www /var/backend/01

もしくは、これらのマウントを sshfs で起動するスクリプトをログイン(bashrc/sshd_config/authorize_keys)に仕込めばいい。

リバースプロキシ

sshpiperd が使えるらしい

クライアント側で操作する。

クライアント側で -t オプションを付ける

クライアント側でバックエンドへの転送を、フロント側のSSHサーバーに指定をする。

ssh front-host -t ssh  backend-host

クライアント側のconfig で -W オプションをつける

.ssh/config

Host backend
  Hostname backend
  Port 22
  ProxyCommand ssh -W %h:%p frontend

ポート転送を掛ける

ポート転送をしてフロント側とバックエンドへの接続用に、フロント側にポートを開ける。

などで指定すればいけそう。

その他使える設定ファイル。

  • ForceCommand
  • ~/.ssh/environment
  •  ~/.ssh/rc
  • /etc/ssh/sshrc

その他の解決方法

今回はネットワークの3レイヤあたりは余り考慮せず7レイヤをメインで考慮したのでこれらの解決策は余り検討しなかった。

  • SSL で CONNECT でSocksプロキシを使う
  • stunnel を使う
  • openVPNを使う
  • ssh/tun-tap を使う。

SNI出来ないの?

OpenSSHの此のスレッドのこの辺り(Name based SSH proxy )で議論されてる。

I hope we do not introduce a cleartext SNI into the SSH protocol. This leaks far too much sensitive metadata for passive monitors. TLS has cleartext SNI, and it is quite difficult to figure out how to protect it from passive monitors

やろうと思えば出来るけど、SSHプロトコルにないもんを作るのはSSHプロトコルの定義からの見直しになりそうだし、そもそもSNIはクリアテキストになってんじゃないの?そんなの実装したくねーわって丁寧な言葉で書かれてる。たぶんコレが全て答え。そのため、どうしてもやりたいなら stunnel を使う必要がある。

参考資料

https://www.infiniteloop.co.jp/blog/2016/05/user-namebase-ssh-reverse-proxy/

http://hogem.hatenablog.com/entry/2015/06/08/233000

https://github.com/tg123/sshpiper

http://yudai.arielworks.com/memo/2010/12/15/225715

http://tsuchinoko.dmmlabs.com/?p=1387

Name based SSH proxy

php で終了しないプログラムを書く

phpホスティングなどのリソースを上手に扱う関係上、実行時間が限られてる。

なので、終了しない無限ループをキックしようと思うとこんな感じなる。

#!/usr/bin/env php
<?php

set_time_limit(0); # forever program!

どこで使うかというと、WEBから起動する場合に、apacheが起動させちゃうもので、ずっと動画配信したいものなど。

動画の音声・動画ストリーム情報をJSONで受け取るffprobe json オプション

事の発端:ファイル名変換をミスった

この動画なんだっけ?wmv?avi? mpegts ?

ファイル名の変換をミスった。拡張子がトンだ。もうしにたい。仕方ないのでコーデック情報見ながら各種データの拡張子を探ることに

動画のストリーム情報の解析がめんどくさい。

ffmpeg で動画の情報を見るには ffprobe を使う。

ffprobe -i movie.mov 

まぁこれだけでも見るだけなら 標準エラーにffmpeg と同じ情報が出てきてログ見たらわかるんだけど。もっと手軽にほしいところだけ見たいな的な。

ネット見てたら、ffmpeg/ffprobe のエラーストリームへログをgrep してたりしてたいへんだ。

JSON で取り出す

-print_format json

ffprobe には show_format があってJSONでストリーム情報を取り出せる。

ffprobe -i movie.mov -show_streams -print_format json

これで、ストリーム情報を取り出せる。

うるさいログを黙らせる。

サポートしてるフォーマットとかの情報がうるさいので黙らせる。-loglevel quietで指定する。

ffprobe -i movie.mov -loglevel quiet -show_streams -print_format json

-print_format json jq を組み合わせる。

ここまで来たら後は楽ちん。 jq と組合あせて戦う。

takuya@Desktop$ ffprobe  -loglevel quiet -show_streams -print_format json "$f" | jq '.streams[] | {"index":.index,"codec_type":.codec_type,"codec_name":.codec_name}  '  -r
{
  "index": 0,
  "codec_type": "audio",
  "codec_name": "aac"
}
{
  "index": 1,
  "codec_type": "video",
  "codec_name": "h264"
}
{
  "index": 2,
  "codec_type": "data",
  "codec_name": null
}
{
  "index": 3,
  "codec_type": "data",
  "codec_name": null
}
takuya@Desktop$

動画のコーデック情報を取り出す。

ffprobe  -loglevel quiet -show_streams -print_format json "$i" | jq .streams[0].codec_name  -r

最後にこれを関数にする。

function ffmpeg_show_video_codec()
    f=$1
    ffprobe  -loglevel quiet -show_streams -print_format json "$f" | jq .streams[0].codec_name  -r
}

コレで行けそうですね。

追記1 XML + XPATHの方が簡単かも

json と jq はどうしても煩雑なので、 XML+XPATH のほうが楽だった。

XML で出力する -print_format xml

XML で出力してXPATHの方が // の省略記法があるので圧倒的に楽

ffprobe /Users/takuya/.Desktops/2017-02-10/IMG_4046.MOV-1.mov -loglevel quiet -show_streams -print_format xml | xpath '//stream/@codec_name'

やっぱりJSONめんどくさいんだよ。読みやすいけど、コードが煩雑。廃れてほしい。*1

追記 XML / JSON 以外のフォーマット

などがある

追記 他に見れるもの

formats 以外に見れるものもある。

ffprobe の man を眺めているとアレコレあるみたい

       -show_data
       -show_data_hash algorithm
       -show_error
       -show_format
       -show_format_entry name
       -show_entries section_entries
       -show_packets
       -show_frames
       -show_streams
       -show_programs
       -show_chapters

時間無くて調べきれない。-show_programs 番組のprogram_id などが見れる TS のストリーム情報などはコッチ見た方がいいのかな。

追記

動画の拡張子を wmv / mp4 にわける。

#!/usr/bin/env bash
for i in ./movies/* ; do
  name=$(ffprobe  -loglevel quiet -show_streams -print_format json "$i" | jq .streams[0].codec_name  -r)
  echo $name $i
  case "$name" in
    wmv*)
      :
      echo mv "$i" "${i/.*/.wmv}"
      # mv "$i" "${i/.*/.wmv}"
      ;;
    h264|aac|mpeg4)
      :
      echo mv "$i" "${i/.*/mp4}"
      ;;
  esac

done


覚えにくいので、bash alias や function にしてコマンドにした。

alias ffmpeg_info_json ='$ ffprobe -loglevel quiet -show_streams  -print_format json -i '

*1: 配列とオブジェクトがどちらも入るのがより煩雑さを加速してる感じがある。 XMLの子ノードと属性問題はあるけど、XPATHではあまり区別なく使いやすい。

qpdfでPDFにパスワードをつけて、編集不可にしたり、閲覧不可にする。

qpdf でパスワードをつける。

qpdf があればコピーを不可、編集不可、閲覧不可などのパスワードをつけることが出来る

使うオプション --encrypt

qpdf  --encrypt パスワード1 パスワード2 キー フラグ -- in.pdf out.pdf

最後にいれる -- はオプションフラグの識別のために必要

-- in.pdf out.pdf

パスワードを付ける例

USER_PASSWD='ユーザーパスワード'
OWNER_PASSWD='オーナパスワード'
qpdf  --encrypt $USER_PASSWD $OWNER_PASSWD 40 --print=n --modify=n --extract=n --annotate=n -- 2.pdf 3.pdf

Mac の場合アイコンも変わる。

f:id:takuya_1st:20170313190523p:plain:w120

プレビューでも開けなくなってる。

f:id:takuya_1st:20170313190526p:plain:w300

オーナパスワードだけ付ける場合

誰でも閲覧は出来るが、編集不可で印刷不可にする場合

ユーザーパスワードには「空文字」が許可されるので、空文字を入れることで、オーナパスワードだけをつけることが出来る。

OWNER_PASSWD='オーナパスワード'
qpdf  --encrypt '' $OWNER_PASSWD  40 --print=n --modify=n --extract=n --annotate=n -- 2.pdf 3.pdf

オーナパスワードで印刷不可にした場合

印刷するには、このパスワードダイアログ出てくる。

f:id:takuya_1st:20170313190243p:plain:w200

でもqpdf で閲覧状態のPDFをdecrypt して解除できちゃうんだけどね。知らない人にはちょっとプレッシャーをかけることが出来る。

参考資料

http://qpdf.sourceforge.net/files/qpdf-manual.html

networksetup 困った。。。ダイアログが何度も出る

networksetup 困った。。。

networksetup が便利なんだけど。これを使うと毎回毎回にダイアログが出る。

/usr/sbin/networksetup

f:id:takuya_1st:20170313174928p:plain:w200

sudo すると解決する

sudo したら解決するらしい。なんでや。。。

sudo /usr/sbin/networksetup

sudo で解決するので、sudo つけるか、sudoers に コマンド追加したら解決しますね。

ダイアログが。。。

f:id:takuya_1st:20170313174940p:plain

パーミッションを確認する。

takuya@Desktop$ ll /usr/sbin/networksetup
-rwxr-xr-x 1 root wheel 199K 2016-07-09 12:03 /usr/sbin/networksetup

これだから、suid を付加しても解決する。ただしSIP(system integrity protection) があるのでオフにしないと解決できない。

chmod u+s /usr/sbin/networksetup

rubyのSequelをパパと使うサンプル(select/update/create/delete/unique/constrains)

DB にアクセスするなら Sequel が便利

データベースにアクセスしてデータを扱うには Sequel が便利。ActiveRecordもいいんだけどアレはめんどくさい。

SQLに極めて近い構造で余計なことはなにもなく便利でいい。

インストール

gem install sequel

DBに接続

DB = Sequel.connect('sqlite://./sample.db')
DB = Sequel.connect('mysql2://takuya:**pass**@192.168.100.1/takuya_db',:loggers => [Logger.new($stdout)])

create table をサンプル

DB.create_table!(:table) do
    primary_key :id
    String :user_name , :unique=>true
    Char :role, default: 'u'
    check(:role=>%w[a o u])
    unique [:user_id, :group_id, :role]
end

select

シンボルで、ハッシュキーでアクセスする。面倒なときはクラスを定義する(後述)

res =  db[:tablename]
res.all.each{|e|
     puts res[:id]
     puts res[:name]

}

insert のサンプル

obj = { name: "takuya"}
table = DB[:tablename]
table.insert(obj)

select のサンプル

table = DB[:tablename]
table  = table.where( 'id > 1000' );
table.each{|r|
 puts r[:id]
}

Where 句を使う

res =  db[:tablename].where('id > 100')
res =  db[:tablename].where('id > 100').where(' id   < 1000 ')

SQL を確認する。

res =  db[:tablename].where('id > 100').where(' id   < 1000 ')
res.sql

delete のサンプル

table = DB[:tablename]
table  = table.where( 'id =  1000' ).imit(1);
table.delete

update のサンプル

table = DB[:tablename]
table  = table.where( 'id =  1000' ).imit(1);
obj = { name: "takuya"}
table.update(obj)

unique キーのエラーを検出

  obj = { name: str1 , path: str2 }
  begin
    table = DB[:user_file_info]
    table.insert(obj)
  rescue Sequel::UniqueConstraintViolation => e
     puts e.backtrace
     raise e
  end

prepare のサンプル

prepare = DB[:users].where('name like :n').prepare(:select,:select_by_name)
prepare.call(:n=>"%#{e}%")

create table のさらなる詳細。

DB.create_table! # テーブルを削除して作る(データ消消える
DB.create_table? # テーブルがなければ作る
DB.create_table  # テーブルがあればエラーになる

create table でのforeign key の指定

DB.create_table(:table) do
    primary_key :id
    foreign_key :user_id, :user
end

次のようにカスケードを指定する場合もあり。

foreign_key :user_id, :user, :on_update=>true
foreign_key :user_id, :user, :on_update=>true , :on_delte=>true
foreign_key :user_id, :user, :on_update=>true , :on_delte=>true,:deferrable=>true

外部キーのカラム名を別名にする時

DB.create_table(:table) do
    primary_key :id
    Integer :user_id
    String  :user_name

    foreign_key [:user_id, :user_name],   :user
end

各種制約をつける

DB.create_table(:table) do
    primary_key :id
    Integer :user_id
    Integer :group_id
    String : role
    # 複数カラムの場合
    unique [:user_id, :group_id, :role]

デフォルト制約 role を [‘r’,‘o’,‘u’,] に制限する場合。

check(:role=>%w[a o u])

オブジェクトを使う。

クラス名を指定して扱える。テーブル名をハッシュで指定する。

class ImageCache < Sequel::Model(:image_cache)

end

r = ImageCache.all.first
r.url # ドットでアクセス可能になる。

テーブル名を省略するとクラス名の複数名になる。

class User < Sequel::Model(:users); end
class User < Sequel::Model; end # 省略可能

ActiveRecordじゃないとだめな人には此の方が便利かもしれない。

irb/pry を使ってるとき

irb でいっかいテーブルのクラス定義をしてしまうと、再定義がめんどくさいので。一旦消す方がいい。

pry を再起動せずに、クラスを消すには remove_const するのが楽

class UserInfoTable < Sequel::Model(:user_info_table); end
Object.class_eval { remove_const :UserInfoTable }

BLOB を突っ込む

バイナリを突っ込もうとしたらエラーになったことがあるので明示的にblob を指定する必要があった ( sqlite のとき )

obj = { image_url: image_url, image_data:Sequel.blob(bin) }
table = DB[:image_cache]
table.insert(obj)

sql を書くのをサボるのに便利。

ちょっとしたデータ処理でSQLでデータを格納したり、処理したりするのに便利。

ActiveRecordのようなモデルベースだと「アプリ」を使うには便利なんだけど、テーブルデータを触ったり増やしたりするにはとても不便なので、SequelはちょっとめんどくさいSQLをサボるのに便利だと思う。

参考資料

http://stackoverflow.com/questions/25657286/ruby-sequel-error-table-already-exists http://stackoverflow.com/questions/10196624/how-to-check-constraints-in-sequel

http://takuya-1st.hatenablog.jp/entry/2015/12/28/204019

https://github.com/jeremyevans/sequel/blob/master/doc/schema_modification.rdoc

GNU mc(:midnight commander)でファイラ使うと楽しい。

ようやく名前がわかったので使ってみた。

昔、教授が使ってて、なんやこれ!楽しそうと思ったファイラ。「これDOSですか?」「いいやちがうよ、古い頃から使ってるMS-DOSみたいなもんだな」「なんで?Finderあるじゃないですか?」「コレがいいんだよコマンドからはなれるとコレが使いやすいんだよ」

当時は何のことを指しているのか全く理解できませんでした。

MS-DOS時代を彷彿とさせるユーザーインターフェース。楽しそう。でも名前を忘れてて何かわからなかった。試すこともしなかった。

今日ふとした瞬間に見つけて懐かしい感じなにった。

Midnight Commander とは

Midnight Commander はビジュアルファイルマネージャです。ファイルやディレクトリー全体のコピー・移動・削除や、ファイルの検索、サブシェルでのコマンドの実行ができます。ビューアやエディタも内蔵しています。 Ncurses や S-Lang のような汎用のテキストインターフェイスを使っているため、通常のコンソール、X Window ターミナル、または SSH 接続を介してリモートシェルでも動作します。

Macにもインストール

takuya@~$ brew install  midnight-commander

Linuxにインストール

sudo apt install mc

起動

(Terminal.appの方が自然に動く気がする)

takuya@~$ mc

操作方法

マウスが使える(!)ので操作は最初はマウスからなれる

ファンクションキーを楽しむ。

ファンクションキーで操作するあたりが、もうね、もうね、DOS時代です。

ファンクションキーの存在意義が曖昧にな現代のコンピュータで、ガンガンファンクションキーを使いまくる。

ファンクションキーに気づくことが出来る。F1-10が昔は如何様に使われいたか、そして現代ははどうなったのかを一瞬で体験できる。

f:id:takuya_1st:20170308152321p:plain f:id:takuya_1st:20170308153953p:plain

ssh 経由で強さを発揮する。

SSH経由でターミナルだと、ファイル名を補完したり入力するのすらめんどくさいことが多いので、コレは最高だ。

NASのリモートサーバーにMC入れたら捗る。 ファイルの管理がすごく楽になった。コマンドだと ls 叩きまくりだしなぁ

とくに日本語ファイルが多くて補完が大変なときなどに重宝する。

ログインしたら bashの代わりに mc 起動する公開鍵設定

もう、bash起動する必要なかったら、mc 直接起動しちゃえばいいんじゃないかな。

.ssh/authorized_keys

command="mc"  ssh-rsa  AABIwAAAQEA4pJgjWmRfoAZ6Ippi

楽しかった

まぁEmacsVim NERDTree使えって話なんだろうけどね。

最初はとっつきにくいかもしれないけど、VNCやRDP上げてファイル操作するより、インストールも設定も楽だと思います。 なによりパケット量気にしない使えるのも嬉しい。

欠点といえば、画像をプレビューできないところくらいか。

昔からの人には当たり前なことだと思う。教授も日常で使ってたし。

昔からのCLI経由のインターフェースって完成されてるよね。。

関連資料

http://takuya-1st.hatenablog.jp/entry/20110813/1313252992

参考資料

SOAレコードとdig

DNSSOAレコードについて

takuya@:sites-available$ dig soa google.co.jp +short
ns2.google.com. dns-admin.google.com. 149484063 900 900 1800 60
                                       ^^^       ^^  ^^  ^^  ^^                                                  
                                        │        │    │  │   └ minimun
                                        │        │    │  └─  expire
                                        │        │    └─   retry
                                        │        └─   refresh
                                        └─  serial
  • serial
    • シリアル値。データのID。一般的にはタイム・シリアル値を入れることが多い
  • refresh
    • セカンダリがプライマリに問合せする間隔
  • retry
    • セカンダリがプライマリ問合わせ(上記 refresh ) 失敗時に再取得する間隔
  • expire
    • セカンダリがrefresh に失敗後に現在のキャッシュを保持して返すまでの時間
  • minimun
    • ネガティブキャッシュの存在可能時間を指定する。
    • ネガティブキャッシュとは問合せた結果<該当なし>を保存すること

ネガティブキャッシュと新規追加の関係性。

レコードを新規追加すると、通常だと次のような流れになります。

  1. NS(primary)に新規追加する。
  2. NS(secondary)に同期される
  3. NSに問い合わせが来る
  4. TTL分だけキャッシュされる

先に問い合わせが来てしまった場合。

  1. NS(primary)に新規追加する。
  2. NS(secondary)問い合わせが来る
  3. ネガティブキャッシュされる(「存在しない」がキャッシュされる)
  4. NS(secondary)に同期される

プライマリ→セカンダリの同期前にセカンダリに問い合わせると、セカンダリにはレコードがなく問い合わせ結果がネガティブになり、その情報をSOAに従ってネガティブキャッシュしてしまう。なのでますます反映が遅くなることがある。

SOAで見るべき

一般の人(NS運営してない)がSOAで見る情報はなにより、ネガティブキャッシュ設定だと思います。

なんでこんなことを調べてるのかというとV◯lue-d◯mainの更新の遅さに苦労しててちょっと調べてたわけです。

python3 urllib でPOST/GET/PUT と JSON 送信

python3 の urllib を使う。

ひさしくpython2.7 だし全然不満がないのですが。python3.6 の文字列展開とjinja2 が便利そうだしそろそろ python3 使ってみようと思い立って、まずは基本的なリクエストを投げるところから。

urllib.request で各種リクエストを作る

urllib.request は 公式ドキュメントにすら Requests を使ったら?と書かれてたりする。 まぁ、そうなんだけど。py3から使い方も変わったしおさらいしておくことにする。

基本的なリクエストの流れ

import urllib,urllib.request
url = 'https://t.co'
req = urllib.request.Request(url=url)
f = urllib.request.urlopen(req)
print(f.read().decode('utf-8'))

Request を作って urlopen(request) するのが基本的な流れ。

もちろんGETするだけならもっとシンプルに使える

urllib.request.urlopen(url)

でも、getして終わりってわけじゃないよね。プログラム言語を使い込んでいくならかならず POST/GET/PUT と JSON送信なんかが出て来る。

各種サンプルを試してみた

#!/usr/bin/env python3

import http.client
import urllib
import urllib.request


## phpinfo の内容をjson で返してくれるURL
url = 'http://[::1]/~takuya/info_json.php'

## GET SAMPLE
def sample_get():
  req = urllib.request.Request(url=url)
  f = urllib.request.urlopen(req)
  print(f.read().decode('utf-8'))

## POST SAMPLE
def sample_post():
  req = urllib.request.Request(url=url, data=b'var=1')
  f = urllib.request.urlopen(req)
  print(f.read().decode('utf-8'))

## POST with Custom HEADER
def sample_post_with_header():
  headers={
    'Content-type':'application/json'
  }
  req = urllib.request.Request(url=url,headers=headers, data=b'{ "a" : 1  }')
  f = urllib.request.urlopen(req)
  print(f.read().decode('utf-8'))

## POST json with Custom HEADER
def sample_post_with_header_and_json():
  headers={
    'Content-type':'application/json'
  }
  json_str='{"a":1}'
  req = urllib.request.Request(url=url,headers=headers, data=json_str.encode('utf-8'))
  f = urllib.request.urlopen(req)
  print(f.read().decode('utf-8'))

## PUT json with Custom HEADER
def sample_put_with_header_and_json():
  headers={
    'Content-type':'application/json'
  }
  json_str='{"a":1}'
  req = urllib.request.Request(url=url,headers=headers, data=json_str.encode('utf-8'), method='put')
  f = urllib.request.urlopen(req)
  print(f.read().decode('utf-8'))

まとめ

HTTP(s)のリクエストを送信するときにアレコレする場合は、すべてurllib.request.Request を作って、それを open することになるとわかった。

引数増やすだけで対応できるので楽ですねぇ。

urllib.request.Request(url=url) # simple GET
urllib.request.Request(url=url, data=b'var=1') # POSTする
urllib.request.Request(url=url,headers=headers, data=b'var=1') # HEADERつける
urllib.request.Request(url=url,headers=headers, data=json_str.encode('utf-8'), method='put')

ハマりどころは、POSTデータ byte 型になるところ

data=b'var=1'
# または
data=str.encode('utf-8')

などとやっておかないとエラーになる。油断してると忘れそうになる。

これで各種APIを叩くのが楽になりますね。

参考資料

https://docs.python.org/3.4/library/urllib.request.html#module-urllib.request

curl で get/post/put/delete などメソッドを指定したりJSONをPOSTしたり

curl でHTTPのMethod指定する

GETはすぐ覚えらるけど、GETと同等に使うPOSTはちょっとコツが居る。

curl で POST などの HTTP のメソッドを指定するには、 -X HTTP_METHOD をつけて method を指定する。

curl -X GET
curl -X DELETE
curl -X POST --data 'name=value&id=1'
curl -X PUT   --data 'name=value&id=1'

ファイルを送信する場合にもPOSTをつかう

POSTと ファイル名指定の -F と合わせ技

curl -X POST -F name=@path/to/name
curl -X PUT   -F name=@path/to/name

JSON をPOST送信する場合には

JSONを送る場合は、大抵の場合がPOSTなので、POSTと合わせて、--data で指定する。

curl -X POST --data '{"id":1}' 
curl -X PUT   --data '{"id":1}' 

content-typeを状況により追加で付ける必要がある。

curl -X POST --data '{"id":1}'  -H "Content-Type: application/json"
curl -X PUT   --data '{"id":1}'  -H "Content-Type: application/json"

ちなみに、 --data つけるとPOST

わたしはPOSTやPUTであることを明示することで自分自身への確認としていますが。

--data をつけるだけでPOSTになる・

つまり、次の2つは同じと言える。

curl -X POST --data '{"id":1}' 
curl --data '{"id":1}' 

REST なAPI好き

RESTなAPI好きなんだけど、HTTPメソッドで切り分けるとすると、リクエスト側でメソッド指定するのが不便ですねぇ。

2019-02-06

自分で探すときに検索でマッチしないのでキーワードを追加。

curl でAPIアクセスのヘッダ毎回指定を設定ファイルを使って省略する

API にアクセスするときHEADER多すぎ

URLとPOST/GETのデータ以外に指定する項目が多すぎて嫌になる。

curl "https://api.example.com/api/v4/settings" \
     -H "X-Auth-Email: takuya@example.com" \
     -H "X-Auth-Key: gfhfdertgfdsertgf" \
     -H "X-Auth-SID: xxxxxxxxxxxxxxxx" \
     -H "Content-Type: application/json"

おおくが、HTTPに沿った認証してないない。認証API叩いて、セッションキーでCookie発行や、Digestで認証してくれたらいいのにな。。。

curlrc でヘッダを省略する

毎回指定しなきゃいけないヘッダは、省略する。

server.com.curlrc

header ="X-Auth-Email: takuya@example.com" \
header ="X-Auth-Key: gfhfdertgfdsertgf" \
header ="X-Auth-SID: xxxxxxxxxxxxxxxx" \
header ="Content-Type: application/json"

curlrc を指定してリクエストする。

curl -K server.com.curlrc  "https://api.example.com/api/v4/settings" 

かなりシンプルになる。

さらにaliasする

curlrc を指定してリクエストする。

alias curl.server.com='curl -K server.com.curlrc  '

これで、大事なことに集中できる。

curl.server.com "https://api.example.com/api/v4/settings" 
curl.server.com "https://api.example.com/api/v4/update" -X PUT --data '{}'

余計なことを考えずにURIとPOSTデータだけに集中できる。

怠惰ですね。もう

f:id:takuya_1st:20170307205131j:plain:w300

API って使いにくいよね。

個人的には、APIが HTTPの jsonで返す意味があんまりないと思うんだけよね。、ぶっちゃけ、GETクエリで検索条件を絞り込めないほうが不便なんだよね。マルっと一括して取ってくるプログラムと、ちまちま実行するUNIXパイプ志向との発想の違いなんだろうけど。欲しいデータだけをGETクエリで指定しちゃうほうが楽だよね。

api もっとシンプルにしてくれたらいいのに、無駄な労力書けて複雑なAPI作って、使う方も複雑になって大変です。

参考資料

curl でよく使う設定をまとめる ~/.curlrc - それマグで!

画像元

https://clubt.jp/product/445475_42465623.html

文字列の長さ順にソートする(awk/sortコマンド組み合わせ)

文字列の長さ順にソートすると美しい。

なんでなんだろうね。アルファベット順よりも文字列長さ順でソートすると、出力がきれいに見えるんだよね。

awk/sort で文字列の長さ順にソートする。

command | awk '{print length() ,$0}' | sort -nr

例えばPATHをソートしてみる

takuya@~$ echo -e ${PATH//:/'\n'}  |  awk '{print length() ,$0}' | sort -nr
26 /Users/takuya/.rbenv/shims
26 /Users/takuya/.pyenv/shims
24 /usr/local/share/npm/bin
24 /Users/takuya/.pyenv/bin
18 /Users/takuya/.bin
15 /usr/local/sbin
14 /usr/local/bin
12 /opt/X11/bin
9 /usr/sbin
8 /usr/bin
5 /sbin
4 /bin

ああ、美しい。

ソートした結果が欲しい時

takuya@~$ echo -e ${PATH//:/'\n'}  |  awk '{print length() ,$0}' | sort -nr | awk '{ print  $2 }'
/Users/takuya/.rbenv/shims
/Users/takuya/.pyenv/shims
/usr/local/share/npm/bin
/Users/takuya/.pyenv/bin
/Users/takuya/.bin
/usr/local/sbin
/usr/local/bin
/opt/X11/bin
/usr/sbin
/usr/bin
/sbin
/bin

うん、美しい。

とくにPATHは文字列が長いほど、優先度が高く、/binなどの共有コマンドを上書きする側になるので、コレが便利。

bcacheでSSDをキャッシュに使ってHDDアクセスを早くしてSSHD的なことをやる

ssd を書込み読み込みキャッシュに使いたい

HDDのアクセスを早くしたい。Writeおせーんだわ。

あれこれ、実装があるけど lvm cache / dm-cache より、bcacheの方が圧倒的に簡単で速かった。

もちろん、こんなことをしなくても、よく使うデータはHDDからメモリにキャッシュされるので、実運用ではメモリに空き容量を増やすのがとにかく先決です。bcacheは趣味ですね。

apt からインストールできる場合

sudo apt install bcache-tools

bcacheのヘッドからインストール

git から取得しインストール

git clone https://github.com/g2p/bcache-tools.git
cd bcache-tools
make
sudo checkinstall    \
--pkgname=bcache-head    \
--pkgversion="1:$(date +%Y%m%d%H%M)" \
--backup=no    \
--deldoc=yes \
--fstrans=no \
--default

bcacheが有効か調べる

bcacheが使えるかどうか確認。

takuya@:~$ sudo modprobe bcache
takuya@:~$ sudo lsmod | grep cache
bcache                193649  0
dm_cache               41114  0
dm_persistent_data     49347  1 dm_cache
dm_bio_prison          13056  1 dm_cache
fscache                45542  1 nfs
mbcache                17171  1 ext4
dm_mod                 89405  17 dm_persistent_data,dm_cache,dm_bufio

使い始める前に用語の整理

  • bcache 此のソフト
  • バッキング・デバイスbacking device キャッシュされる側(通常はHDD)
  • キャッシング・デバイス chacing device キャッシュ側(通常はSSD
  • make-bcache 作成コマンド

バッキングデバイスの指定

今回は、LVM上のVGから100GB のストレージを切り出してブロックデバイスを作成して/dev/sda などの代わりに使う。

これをバッキングデバイスに指定した。

takuya@:~$ sudo make-bcache -B /dev/mapper/data-storage
UUID:           a057b697-795d-450c-8e68-454d6f244b93
Set UUID:       9778b366-5f4c-4947-a180-c66442f2a934
version:        1
block_size:     1
data_offset:        16

キャッシュデバイスの指定

今回はSSD上のLVMから20GB ほど切り出して/dev/sdb などの代わりに使う。

これをキャッシュデバイスに指定した

takuya@:~$ sudo make-bcache -C /dev/mapper/acid-ssd
UUID:           9ac55038-1b95-42fb-bd0f-3b4638476e5d
Set UUID:       aad94dbd-5ded-4d23-bccb-1f3c1f475c48
version:        0
nbuckets:       40960
block_size:     1
bucket_size:        1024
nr_in_set:      1
nr_this_dev:        0
first_bucket:       1

状態を確認する。

バイスが登録されたか調べる。

bacheデバイスが見えるようになったか確認する。

takuya@:~$ ls  -l -R /dev/bcache**
brw-rw---- 1 root disk 253, 0 2017-03-06 18:21 /dev/bcache0

/dev/bcache:
合計 0
drwxr-xr-x 2 root root 60 2017-03-06 18:21 by-uuid

/dev/bcache/by-uuid:
合計 0
lrwxrwxrwx 1 root root 13 2017-03-06 18:21 a057b697-795d-450c-8e68-454d6f244b93 -> ../../bcache0

ここで作成された /dev/bcache0/ が bcacheでキャッシュされた、バッキングデバイス(/dev/sda)などを指している。

バッキングデバイスの状態を確認する

takuya@:~$ sudo bcache-super-show /dev/mapper/data-storage
sb.magic        ok
sb.first_sector     8 [match]
sb.csum         D1AA8DCE13E9B35C [match]
sb.version      1 [backing device]

dev.label       (empty)
dev.uuid        a057b697-795d-450c-8e68-454d6f244b93
dev.sectors_per_block   1
dev.sectors_per_bucket  1024
dev.data.first_sector   16
dev.data.cache_mode 0 [writethrough]
dev.data.cache_state    0 [detached]

cset.uuid       9778b366-5f4c-4947-a180-c66442f2a934

キャッシュデバイスの状態を確認する

takuya@:~$ sudo bcache-super-show /dev/mapper/acid-ssd
sb.magic        ok
sb.first_sector     8 [match]
sb.csum         455541E08344F7F6 [match]
sb.version      3 [cache device]

dev.label       (empty)
dev.uuid        9ac55038-1b95-42fb-bd0f-3b4638476e5d
dev.sectors_per_block   1
dev.sectors_per_bucket  1024
dev.cache.first_sector  1024
dev.cache.cache_sectors 41942016
dev.cache.total_sectors 41943040
dev.cache.ordered   yes
dev.cache.discard   no
dev.cache.pos       0
dev.cache.replacement   0 [lru]

cset.uuid       aad94dbd-5ded-4d23-bccb-1f3c1f475c48

bcache0 にキャッシュデバイスを紐付ける。

sudo bcache-super-show /dev/mapper/acid-ssd  \ 
| grep cset.uuid | awk '{ print $2 }' \
| sudo tee /sys/block/bcache0/bcache/attach

ファイルシステムを作る

ここまでで、bcacheの準備ができたので、後はターゲット(/dev/bcache0)に対して操作をする。

takuya@:~$ sudo mkfs.ext4 /dev/bcache0
mke2fs 1.42.12 (29-Aug-2014)
Discarding device blocks: done
Creating filesystem with 26214398 4k blocks and 6553600 inodes
Filesystem UUID: 5848c161-fd7c-4031-807a-01c56dc7c967
Superblock backups stored on blocks:
    32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
    4096000, 7962624, 11239424, 20480000, 23887872

Allocating group tables: done
Writing inode tables: done
Creating journal (32768 blocks): done
Writing superblocks and filesystem accounting information: done

マウントする

sudo mount /dev/bcache0 mnt

測定してみる。

細かいテスト条件を考える暇がなかったのでかなり適当な条件ですが1GB程度の書込みで測定してみました。

単純にbw だけを見てみる。

時間がなくて、細かいテスト条件を考慮できてないので、単純にbw だけを見てみる。 読み込みは今回測定してない

ランダム書込みの測定

バイス 速度(bw)
ssdのみ 242557KB/s
hddのみ 910KB/s
bcache/writethrough 1314KB/s
bcache/writeback 144015KB/s

1GB程度のランダムデータは差が・・・でもまぁ少しは早くなる?

writebackで ssd にだけ書いて hdd にsync 待ちしないならそりゃ圧倒的に早いな

何かに使えそうですよね。

bcacheを使ったときの速度測定

とりあえず、fio でぱぱっと測定。測定条件を全く考慮せず使いまわした・・・次やるときはちゃんと測定条件を考えないと。

[global]
bs=4k
ioengine=libaio
iodepth=4
size=1g
direct=1
runtime=60
directory=/home/takuya/mnt
filename=ssd.test.file

[rand-write]
rw=randwrite
stonewall

fio の結果

takuya@:~$ sudo fio myjob.fio
rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4
fio-2.1.11
Starting 1 process
rand-write: Laying out IO file(s) (1 file(s) / 1024MB)
Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/1576KB/0KB /s] [0/394/0 iops] [eta 00m:00s]
rand-write: (groupid=0, jobs=1): err= 0: pid=2016: Mon Mar  6 18:44:02 2017
  write: io=78864KB, bw=1314.3KB/s, iops=328, runt= 60008msec
    slat (usec): min=6, max=757302, avg=104.79, stdev=5956.73
    clat (usec): min=78, max=2122.7K, avg=12067.09, stdev=45932.99
     lat (usec): min=89, max=2122.7K, avg=12172.05, stdev=46560.28
    clat percentiles (usec):
     |  1.00th=[  169],  5.00th=[  207], 10.00th=[  251], 20.00th=[ 5600],
     | 30.00th=[ 6688], 40.00th=[ 7136], 50.00th=[ 7392], 60.00th=[ 7648],
     | 70.00th=[ 7968], 80.00th=[ 8768], 90.00th=[11584], 95.00th=[20864],
     | 99.00th=[130560], 99.50th=[144384], 99.90th=[602112], 99.95th=[839680],
     | 99.99th=[2113536]
    bw (KB  /s): min=   14, max= 3093, per=100.00%, avg=1438.70, stdev=699.48
    lat (usec) : 100=0.07%, 250=9.92%, 500=1.09%, 750=0.29%, 1000=0.27%
    lat (msec) : 2=0.31%, 4=1.37%, 10=74.64%, 20=6.77%, 50=2.80%
    lat (msec) : 100=0.22%, 250=1.94%, 500=0.12%, 750=0.14%, 1000=0.04%
    lat (msec) : >=2000=0.02%
  cpu          : usr=0.22%, sys=1.58%, ctx=17090, majf=0, minf=8
  IO depths    : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=0/w=19716/d=0, short=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=4

Run status group 0 (all jobs):
  WRITE: io=78864KB, aggrb=1314KB/s, minb=1314KB/s, maxb=1314KB/s, mint=60008msec, maxt=60008msec

Disk stats (read/write):
    bcache0: ios=0/21318, merge=0/0, ticks=0/419156, in_queue=0, util=0.00%, aggrios=0/21324, aggrmerge=0/0, aggrticks=0/260408, aggrin_queue=260408, aggrutil=98.78%
    dm-4: ios=0/21434, merge=0/0, ticks=0/510812, in_queue=510812, util=98.78%, aggrios=0/19972, aggrmerge=0/1474, aggrticks=0/446100, aggrin_queue=446092, aggrutil=98.78%
  sda: ios=0/19972, merge=0/1474, ticks=0/446100, in_queue=446092, util=98.78%
    dm-5: ios=0/21215, merge=0/0, ticks=0/10004, in_queue=10004, util=5.14%, aggrios=0/26443, aggrmerge=0/2553, aggrticks=0/79524, aggrin_queue=79516, aggrutil=7.90%
  sdb: ios=0/26443, merge=0/2553, ticks=0/79524, in_queue=79516, util=7.90%

SSDだけの場合

キャッシュデバイスに利用したSSDからLVMを切り出して使うと次の速度だった。

takuya@:~$ fio myjob.fio
rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4
fio-2.1.11
Starting 1 process
Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/237.4MB/0KB /s] [0/60.8K/0 iops] [eta 00m:00s]
rand-write: (groupid=0, jobs=1): err= 0: pid=2146: Mon Mar  6 18:47:09 2017
  write: io=1024.0MB, bw=242557KB/s, iops=60639, runt=  4323msec
    slat (usec): min=2, max=1436, avg= 7.19, stdev=13.71
    clat (usec): min=12, max=48210, avg=57.96, stdev=215.17
     lat (usec): min=26, max=48214, avg=65.23, stdev=216.07
    clat percentiles (usec):
     |  1.00th=[   33],  5.00th=[   36], 10.00th=[   37], 20.00th=[   39],
     | 30.00th=[   40], 40.00th=[   41], 50.00th=[   41], 60.00th=[   42],
     | 70.00th=[   43], 80.00th=[   46], 90.00th=[   58], 95.00th=[  231],
     | 99.00th=[  237], 99.50th=[  241], 99.90th=[  684], 99.95th=[ 1160],
     | 99.99th=[ 5856]
    bw (KB  /s): min=169536, max=258520, per=99.67%, avg=241758.00, stdev=30286.36
    lat (usec) : 20=0.01%, 50=83.98%, 100=9.08%, 250=6.55%, 500=0.24%
    lat (usec) : 750=0.05%, 1000=0.02%
    lat (msec) : 2=0.06%, 4=0.01%, 10=0.01%, 20=0.01%, 50=0.01%
  cpu          : usr=11.29%, sys=44.42%, ctx=154698, majf=0, minf=8
  IO depths    : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=0/w=262144/d=0, short=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=4

Run status group 0 (all jobs):
  WRITE: io=1024.0MB, aggrb=242557KB/s, minb=242557KB/s, maxb=242557KB/s, mint=4323msec, maxt=4323msec

Disk stats (read/write):
    dm-1: ios=644/251352, merge=0/0, ticks=144/14056, in_queue=14208, util=97.55%, aggrios=644/262180, aggrmerge=0/15, aggrticks=144/18640, aggrin_queue=18752, aggrutil=96.11%
  sdb: ios=644/262180, merge=0/15, ticks=144/18640, in_queue=18752, util=96.11%

HDDだけの場合

takuya@:~$ sudo fio myjob.fio
rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4
fio-2.1.11
Starting 1 process
rand-write: Laying out IO file(s) (1 file(s) / 1024MB)
Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/1420KB/0KB /s] [0/355/0 iops] [eta 00m:00s]
rand-write: (groupid=0, jobs=1): err= 0: pid=2249: Mon Mar  6 18:50:09 2017
  write: io=54776KB, bw=931985B/s, iops=227, runt= 60184msec
    slat (usec): min=5, max=371161, avg=137.76, stdev=5134.81
    clat (usec): min=101, max=883763, avg=17428.75, stdev=51339.16
     lat (usec): min=126, max=883769, avg=17566.73, stdev=51900.52
    clat percentiles (usec):
     |  1.00th=[  171],  5.00th=[  209], 10.00th=[  223], 20.00th=[ 5600],
     | 30.00th=[ 6752], 40.00th=[ 7200], 50.00th=[ 7456], 60.00th=[ 7712],
     | 70.00th=[ 8160], 80.00th=[ 9280], 90.00th=[17792], 95.00th=[125440],
     | 99.00th=[189440], 99.50th=[261120], 99.90th=[684032], 99.95th=[880640],
     | 99.99th=[880640]
    bw (KB  /s): min=   17, max= 3032, per=100.00%, avg=991.52, stdev=724.89
    lat (usec) : 250=11.17%, 500=0.91%, 750=0.04%, 1000=0.01%
    lat (msec) : 2=0.24%, 4=1.22%, 10=71.24%, 20=7.80%, 50=1.29%
    lat (msec) : 100=0.06%, 250=5.26%, 500=0.48%, 750=0.19%, 1000=0.09%
  cpu          : usr=0.19%, sys=1.14%, ctx=11227, majf=0, minf=8
  IO depths    : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=0/w=13694/d=0, short=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=4

Run status group 0 (all jobs):
  WRITE: io=54776KB, aggrb=910KB/s, minb=910KB/s, maxb=910KB/s, mint=60184msec, maxt=60184msec

Disk stats (read/write):
    dm-6: ios=0/14883, merge=0/0, ticks=0/462800, in_queue=473700, util=99.95%, aggrios=0/13821, aggrmerge=0/1075, aggrticks=0/443268, aggrin_queue=450992, aggrutil=99.95%
  sda: ios=0/13821, merge=0/1075, ticks=0/443268, in_queue=450992, util=99.95%
takuya@:~$

writeback にして測定してみる。

witebackを有効にする

takuya@:~$ echo writeback | sudo tee  /sys/block/bcache0/bcache/cache_mode
writeback
takuya@:~$ sudo fio myjob.fio
rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4
fio-2.1.11
Starting 1 process
Jobs: 1 (f=1): [w(1)] [100.0% done] [0KB/151.5MB/0KB /s] [0/38.8K/0 iops] [eta 00m:00s]
rand-write: (groupid=0, jobs=1): err= 0: pid=2341: Mon Mar  6 18:54:19 2017
  write: io=1024.0MB, bw=144015KB/s, iops=36003, runt=  7281msec
    slat (usec): min=1, max=6871, avg=12.32, stdev=16.83
    clat (usec): min=19, max=14755, avg=98.10, stdev=368.31
     lat (usec): min=28, max=14760, avg=110.48, stdev=368.54
    clat percentiles (usec):
     |  1.00th=[   34],  5.00th=[   38], 10.00th=[   39], 20.00th=[   40],
     | 30.00th=[   41], 40.00th=[   43], 50.00th=[   43], 60.00th=[   44],
     | 70.00th=[   46], 80.00th=[   50], 90.00th=[   63], 95.00th=[  157],
     | 99.00th=[ 1864], 99.50th=[ 2480], 99.90th=[ 3152], 99.95th=[ 5024],
     | 99.99th=[13504]
    bw (KB  /s): min=136680, max=161360, per=99.65%, avg=143503.93, stdev=6617.05
    lat (usec) : 20=0.01%, 50=79.82%, 100=14.01%, 250=2.88%, 500=0.19%
    lat (usec) : 750=0.67%, 1000=0.42%
    lat (msec) : 2=1.12%, 4=0.81%, 10=0.06%, 20=0.03%
  cpu          : usr=5.00%, sys=44.67%, ctx=67203, majf=0, minf=7
  IO depths    : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=0/w=262144/d=0, short=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=4

Run status group 0 (all jobs):
  WRITE: io=1024.0MB, aggrb=144015KB/s, minb=144015KB/s, maxb=144015KB/s, mint=7281msec, maxt=7281msec

Disk stats (read/write):
    bcache0: ios=223/257488, merge=0/0, ticks=28/26512, in_queue=0, util=0.00%, aggrios=111/132361, aggrmerge=0/0, aggrticks=14/13326, aggrin_queue=13352, aggrutil=95.97%
    dm-4: ios=0/1, merge=0/0, ticks=0/0, in_queue=0, util=0.00%, aggrios=0/1, aggrmerge=0/0, aggrticks=0/0, aggrin_queue=0, aggrutil=0.00%
  sda: ios=0/1, merge=0/0, ticks=0/0, in_queue=0, util=0.00%
    dm-5: ios=223/264721, merge=0/0, ticks=28/26652, in_queue=26704, util=95.97%, aggrios=223/258582, aggrmerge=0/6444, aggrticks=28/15168, aggrin_queue=15116, aggrutil=95.91%
  sdb: ios=223/258582, merge=0/6444, ticks=28/15168, in_queue=15116, util=95.91%
takuya@:~$

writethrough で限界までパフォーマンス上げてみる

シーケンシャルIOの検出・バイパスオフ

takuya@:~$ echo writethrough | sudo tee  /sys/block/bcache0/bcache/cache_mode
writethrough
takuya@:~$ echo 0 | sudo tee /sys/block/bcache0/bcache/sequential_cutoff
0
takuya@:~$ echo 0 | sudo tee /sys/fs/
bcache/ btrfs/  cgroup/ ext4/   fuse/   pstore/

レイテンシ検出オフ

takuya@:~$ echo 0 | sudo tee /sys/fs/bcache/aad94dbd-5ded-4d23-bccb-1f3c1f475c48/congested_read_threshold_us
0
takuya@:~$ echo 0 | sudo tee /sys/fs/bcache/aad94dbd-5ded-4d23-bccb-1f3c1f475c48/congested_write_threshold_us
0

この条件だと、測定結果は次のとおりになった。5%くらいはやい?あんまり変わらないね。

takuya@:~$ sudo fio myjob.fio
rand-write: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=4
fio-2.1.11
Starting 1 process
Jobs: 1 (f=0): [w(1)] [8.1% done] [0KB/1810KB/0KB /s] [0/452/0 iops] [eta 11m:35s]
rand-write: (groupid=0, jobs=1): err= 0: pid=4873: Mon Mar  6 19:12:02 2017
  write: io=84528KB, bw=1408.7KB/s, iops=352, runt= 60007msec
    slat (usec): min=5, max=16313, avg=86.45, stdev=167.83
    clat (usec): min=77, max=726096, avg=11262.20, stdev=31084.99
     lat (usec): min=85, max=730582, avg=11349.47, stdev=31109.02
    clat percentiles (usec):
     |  1.00th=[  163],  5.00th=[  209], 10.00th=[ 4576], 20.00th=[ 5792],
     | 30.00th=[ 6816], 40.00th=[ 7136], 50.00th=[ 7392], 60.00th=[ 7648],
     | 70.00th=[ 7968], 80.00th=[ 8768], 90.00th=[10944], 95.00th=[18816],
     | 99.00th=[129536], 99.50th=[140288], 99.90th=[544768], 99.95th=[634880],
     | 99.99th=[724992]
    bw (KB  /s): min=   71, max= 3040, per=100.00%, avg=1479.37, stdev=684.76
    lat (usec) : 100=0.06%, 250=6.80%, 500=0.60%, 750=0.18%, 1000=0.13%
    lat (msec) : 2=0.11%, 4=1.35%, 10=79.16%, 20=7.32%, 50=2.14%
    lat (msec) : 100=0.01%, 250=1.91%, 500=0.11%, 750=0.13%
  cpu          : usr=0.73%, sys=3.81%, ctx=17268, majf=0, minf=8
  IO depths    : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=0/w=21132/d=0, short=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=4

Run status group 0 (all jobs):
  WRITE: io=84528KB, aggrb=1408KB/s, minb=1408KB/s, maxb=1408KB/s, mint=60007msec, maxt=60007msec

Disk stats (read/write):
    bcache0: ios=0/21070, merge=0/0, ticks=0/242964, in_queue=0, util=0.00%, aggrios=30/21496, aggrmerge=0/0, aggrticks=0/178762, aggrin_queue=178812, aggrutil=99.98%
    dm-4: ios=0/21224, merge=0/0, ticks=0/355520, in_queue=355620, util=99.98%, aggrios=0/21184, aggrmerge=0/51, aggrticks=0/296676, aggrin_queue=296624, aggrutil=99.97%
  sda: ios=0/21184, merge=0/51, ticks=0/296676, in_queue=296624, util=99.97%
    dm-5: ios=60/21768, merge=0/0, ticks=0/2004, in_queue=2004, util=2.88%, aggrios=60/22260, aggrmerge=0/71, aggrticks=0/1788, aggrin_queue=1752, aggrutil=2.54%
  sdb: ios=60/22260, merge=0/71, ticks=0/1788, in_queue=1752, util=2.54%

停止する

stop に 1 を書き込んだらデバイスを停止できる。

takuya@:~$ echo 1 | sudo tee /sys/block/bcache0/bcache/stop
1

もしまた使いたいときは、wipefs でフラッシュする

takuya@:~$ sudo wipefs -a /dev/mapper/acid-cache

主な操作方法

主な操作は /sys/fs 経由で行う。

参考資料

http://www.slideshare.net/nobuto_m/bcachessd-hdd

http://unix.stackexchange.com/questions/225017/how-to-remove-bcache0-volume

http://www.tech-g.com/2015/05/10/bcache-linux-ssd-caching-for-hard-drives-on-debian-jessie/

https://pommi.nethuis.nl/ssd-caching-using-linux-and-bcache/

https://jp.linux.com/news/linuxcom-exclusive/413722-lco20140225