読者です 読者をやめる 読者になる 読者になる

macosでJVMのスレッドダンプを取得する

JVMのこと、詳しくなりたくって、でも何もできずにいる自分がただそこにいるだけで、悔しくて泣きたくて。
そんな気持ちを最近持ったので、勉強しはじめた。

とりあえずjavaをインストールする

ORACLEのインストールガイドを読むのが一番。
MacでのJavaのインストール方法

インストール完了後、こんな感じでバージョンを確認できたらよさげ。

$java -version
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

javaのプログラムを起動する

とりあえず簡単なプログラムを起動させて、立ち上がっているプロセスを確認したい。
ただ、ずっと起動しぱなっしのプロセスにしてあげる必要があるので、BufferedReader というやつを使って、入力完了するまでプロセスが死なないようにした。

import java.io.*;
public class HelloWorld {
  public static void main (String[] args) throws IOException
    {
        InputStreamReader is = new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(is);
        System.out.println("何か入力してください.");
        String str = br.readLine();
        System.out.println(str + "が入力されました.");
    }
}

HelloWorld.java として保存して、 javac HelloWorld.java , java HelloWorld を実行すると実行される。

javaプロセス起動中にスレッドダンプを取得

プログラムを実行すると、 何か入力してください. と出たまま入力せずに別のコンソールから起動中のプロセスを確認する。

$ps aux | grep java
user           34577   0.0  0.0  2432804    776 s021  S+    2:27AM   0:00.00 grep java
user           34573   0.0  0.3  6026488  22852 s019  S+    2:27AM   0:00.16 /usr/bin/java HelloWorld

ちゃんと立ち上がったままになってて良い感じ。

次にスレッドダンプを取得。

$jps
34582 Jps
34573 HelloWorld

jpsコマンドで、pidに紐づいたjavaの実行プログラムがリストで取れるっぽい。
さらに jstack コマンドでpidを指定するとそのプロセスのスレッドダンプを出力できる。

$jstack 34420
2017-05-17 02:13:46
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.131-b11 mixed mode):

"Attach Listener" #9 daemon prio=9 os_prio=31 tid=0x00007fc815083800 nid=0x1307 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Service Thread" #8 daemon prio=9 os_prio=31 tid=0x00007fc815025800 nid=0x4903 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fc816008800 nid=0x4703 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fc81582c800 nid=0x4503 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fc815871800 nid=0x4303 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fc815871000 nid=0x4103 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fc815857800 nid=0x3103 in Object.wait() [0x000070000dd8e000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x0000000795588ec8> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
...

あとはよさげに

jstack 34420 > hoge.log

とかしてあげるとよい。

なお、スレッドダンプの読み方も調べてみたけど語るには知識がなさすぎるので、参考になった記事を共有するに留めておくことにします。

enk.hatenablog.com

Java書かないのでまったくここらへんのことわからないけど、少しずつこうやって知見を貯めていきたい。

Infrastructure as Codeの価値・原則・プラクティス・評価軸

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

Infrastructure as Code ―クラウドにおけるサーバ管理の原則とプラクティス

Infrastructure as Code本の読書まとめ。
1周目は理解仕切れていないところ多くて、2周目でなんとなーくわかってきて、3周目の今、自分なりにまとめていこうと思いまして、記事にしてます。
ほとんどの項目がまんま書かれていますが、いまいちわかっていなかったとこは自分なりのメモを残すことにした。

価値

  • 変化を支えられるインフラをつくれる
  • システムの更新を日常のものにできる
  • プロダクト開発者地震がリソースのメンテナンスを行える
  • 障害が起きてもすぐに対応できる

原則

  • 簡単に再現できるシステムであること:ほとんどの作業が自動化されている状態であること
  • 使い捨てにできるシステムであること:変化に合わせて今あるものを除却/追加しやすい状態であること
  • 統一的なシステムであること:同じ役割をもつサーバがほとんど同じ状態であること
  • 反復できるプロセスであること:誰がいつやっても同じ状態をつくれる状態であること

プラクティス

  • 定義ファイルを使いましょう
  • 自己記述システムでプロセスを担保しましょう
  • VCSを使いましょう

評価軸

  • 労力をかけずにインフラ開発・運用ができているか
  • 常にサーバは最新の状態かつ統一性が担保されているか
  • CI/CDの仕組みが整っており、開発者がそれを容易に実行できるか
  • 営業中にデプロイできるような状態か
  • MTTRをKPIとして改善に取り組めているか

nginxの負荷分散手法(1) 重み付け

nginxでは、リバースプロキシ機能を使って、負荷分散を行なうことができる。
主なやり方は3つ。

  1. 重み付けによる負荷分散
  2. 稼働系と待機系
  3. 接続数による負荷分散

今回は、 1. 重み付けによる負荷分散 について。
設定方法は簡単。
upstreamコンテキストでまとめた複数のバックエンドサーバにweightオプションと重みパラメータをつけるだけ。

upstream backend {
  server xxx.xxx.xxx.xxx weight=3;
  server yyy.yyy.yyy.yyy weight=2;
  server zzz.zzz.zzz.zzz weight=1;
}

この設定で、

  • xxx.xxx.xxx.xxx サーバは 全アクセスの3/6 の割合を捌くこととなる
  • yyy.yyy.yyy.yyy サーバは 全アクセスの2/6 の割合を捌くこととなる
  • zzz.zzz.zzz.zzz サーバは 全アクセスの1/6 の割合を捌くこととなる

サーキットブレイカーパターン:連続して起こる同様の障害への対応策

障害が起こりにくい・万が一、障害が起きた時にシステム側で早急な対応をするような仕組みを勉強しております。そこでサーキットブレイカー。

サーキットブレイカーパターンとはなにか

電子回路にも使われている遮断機が主な概念になっているパターン。何かしらのReq/Resに対して、良くない事象(つまり障害)が頻発すると「あ、これはやばいから一旦この導線をオフにしていこう」という仕組みのことを指します。リモートコールはインメモリコールと比べ、いくつか障害になりえる要素が多く、考慮が必要です。例えば、リモートコールはそもそも受信失敗しやすかったり、サプライヤー側からのレスポンスが頻繁にタイムアウトしてしまったりすることがあります。原因がリモートにある以上、対処がしにくく、かつ一度起きてしまったら、連続的に起こりうるような障害対応のために生まれたのがサーキットブレイカーパターンです。

仕組みと処理の流れ

  1. 中間層にサーキットブレイカーを設けます(処理の流れの中に組み込めばOK)
  2. クライアントがリクエストを投げると、そのリクエストはサーキットブレイカーを介してサプライヤー(リモートにあるサーバ側)に向かいます
  3. サプライヤー側になんの問題もなければサーキットブレイカーもそのまんまクライアントにレスポンスを渡します(正常系)
  4. もしサプライヤー側で処理が滞っていたり、そもそも何かしらの問題でサプライヤー側にリクエストが届いていなかったりしてレスポンスがなかったらサーキットブレイカーはその障害発生数を記録
  5. サーキットブレイカーで設定している閾値に達してしまうと、サーキットブレイカーは自身の処理を切り替え(これをトリップというらしい)、クライアント側へ早急にエラーメッセージを配信するようにします(この時点で、サプライヤーへの通信をしなくなる)。
  6. 妥当だと思われる障害復旧時間を設定しておけば、サーキットブレイカーは自己復旧を行ない、サプライヤー側へ疎通を図ります。問題がなさそうであれば、2の状態に戻ります。

f:id:tacmac:20160919212741p:plain

なお、サーキットブレイカーはこのような状態を切り替えながら(トリップしながら)、障害検知・切り替え・復旧を行なっております。 f:id:tacmac:20160919213752p:plain
Closedは電子回路でいうと電流が流れている状態なので正常系処理をしている状態。Openは導線を断ち切り、電流が止まっている状態なので「今サプライヤー側でなんか知らないけどよくない事態が起きてるのでそのリクエスト受け付けられないんです」とエラーメッセージを返している状態。HalfOpenがプログラム内で設定した時間間隔のもとサプライヤーへヘルスチェックしに行き、Closedに戻せるか確認している状態を意味します。

サーキットブレイカーの長所

自分らでは障害が起きても、根本的な対応が取れない外部サービスと連携している場合、導入する意味があるように思えます。一度Openになると、サプライヤーまでの通信は切られるので、ユーザーに時間的なストレスを感じさせないという点で、早急にエラーを返すことができるのがメリットだと思います。

ソース元: martinfowler.com

RailsでURLの末尾にスラッシュ(trailing_slash)をつける方法

SEO要件のためにURLの末尾に/(スラッシュ)をつけることがあります。静的ファイルや少ないページ数のサイトであれば愚直にスラッシュつける設定をやっていけばよさげですが、Railsのようなフレームワークで実現するにはどうしたらよいでしょうか。機会があったのでやってみました。

trailing_slash: を使う

# routes.rb

Rails.application.routes.draw do
  get '/items/:id', to: 'items#show', trailing_slash: true, as: 'item_detail'
end

とすると、 item_detail_pathをクリックした先のURLがしっかりとhttp:example.com/items/22/といった感じで末尾にスラッシュが付加されるようになります。 ちなみに上記は各ルーティングへの個別設定なのですが、アプリケーション全体で、末尾スラッシュ(これをtrailing_slashという)をつけたい場合は

# config/application.rb
class Application < Rails::Application
    config.action_controller.default_url_options = { :trailing_slash => true }
end

とすると、アプリケーションで発行するURL全体にtrailing_slashが付加される。

ルーティングオプションのtrailing_slashで賄えないこと

それは、直接URLを叩きにいった場合、末尾スラッシュ追加処理が行なわれないということ。

# routes.rb

Rails.application.routes.draw do
  get '/items/:id', to: 'items#show', trailing_slash: true, as: 'item_detail'
end

と設定した上で、例えばhttp://example.com/items/45を叩くとhttp://example.com/items/45のままです。http://example.com/items/45/とはなりません。カノニカルでtrailing_slashされたURLをセットしていても検索エンジンによっては処理が変わりうるので、せっかくSEO対策のためにtrailing_slashしたにもかかわらず不安が残ります。

結論:末尾スラッシュがなければ、末尾スラッシュ付きのURLリダイレクトさせればよい

先にコードを載せておきます。

# ApplicationController
def trailing_slash
    redirect_to "#{request.original_url}/" unless request.original_url.match(/\/$/)
end
# ItemsController
class ItemsController < ApplicationController
    before_filter :trailing_slash, only: [:show]
...
end

routes.rbの中で完結させるのは難しいので、コントローラ層で処理を書いてあげる。ApplicationControllerを読み込むタイミングでもしURLの末尾にスラッシュがないのであれば、スラッシュ付きにURLを変えてリダイレクトさせるとよい。これでroutes.rbを汚さずとも末尾スラッシュ追加ができるようになる。また、SEO要件としては追加したいアクション(path)が決まっている場合もありますね。そのときは上記コード例のようにbefore_filterで末尾スラッシュを付加させたいアクションを指定してあげるとよいです。

go run と go buildの違い

f:id:tacmac:20160919223213p:plain:h200

Ruby書いているとパッケージだとか、コンパイルだとかにはほとんど無縁なので、golangの最初の最初、としてpackage・import踏まえつつ、go run と go buildの違いを整理していきます。

パッケージ(package)とは

Goでは、すべてのプログラムが何かしらのパッケージに所属している。パッケージはいわばスコープのようなもの。 原則として以下のルールが存在する。

  • 1つのファイルに複数のパッケージを設定してはならない
  • 1つのディレクトリに複数のパッケージをおいてはならない↲
  • package mainにあるmain関数がエンドポイントとされている(ここから処理が始まる)
package main

import "fmt"

func main() {
  fmt.Printf("hello world!\n")
}

コード1: hello.go

import とは

読み込む必要のあるパッケージを宣言するところ。なお、プログラムの中で使われないパッケージは自動で検出され、除いてくれと叱られる。そこらへんの厳密性をスキップしたい場合は、パッケージの左に _ (アンスコ)をつけるとスキップしてくれる。パッケージ1つだけ読み込むのであればコード1のような表記で良いが、複数ある場合は以下のようにしたほうがよい。

import (
  "fmt"
  "testing"
)

コード2

プログラムを実行する

基本的には、go run hoge.goもしくはgo build hoge hoge.go | ./hogeとすることで実行することができる。

go build

goはコンパイル型言語なので、CやJavaのように基本的にはコンパイルする必要がある。

go build [コンパイル後のファイル名] [コンパイルしたいファイル名]

コンパイルし、コンパイル後のバイナリファイルを実行するには

./[コンパイル後のファイル名]

と叩くだけで良い。 なお、オプション無しでgo buildを叩くと、自動的にmainパッケージを読み込み、main関数があるファイルを特定し、そのファイル名を使ったバイナリファイルが生成される。go buildをすると、基本的にはカレントディレクトリ以下をスコープにして読み込んでコンパイルするファイルを決めるので、カレントディレクトリ以下にあるファイルが読み込まれない、といったことはない。

go run

コンパイルして実行するのがだるい、というニーズ?に答えた命令。自動的にコンパイル・実行してくれる便利なコマンド。go run hoge.goとするだけでプログラムを実行することができる。ただ、ここで注意しておきたいのがgo buildのようにカレントディレクトリ以下の全ファイルを読み込むわけではないということです。go run hoge.goと打つと、基本的にはimportされたパッケージだけを読み 同階層にmainパッケージに所属する別の関数などがあるとそれは無視されてしまう。

Site Reliability Engineeringとは何か?より具体的に考えてみる

結論から先にまとめると、安心安全なシステム開発・時代の変化に沿った運用ができるエンジニアリングが、つまりはSREだということ。この点、要素分解して、もっと現場に落とし込む方法について書いていきます。

SRE本でベンジャミン・トレナー・ソロスが伝えてること

SRE本の中では、SREって深いコンピュータサイエンスの知識が必要だし、ソフトウェアエンジニアリングの経験も必要だから、すんごい求められるものが多くかつ重要なんです、といった話を延々としているのだが、要するにSREってなにか?というところでいうと、可用性・レイテンシ・パフォーマンス・効率・変化への対応・監視・緊急対応・キャパシティコントロールに責任を持ち、対応していうことだということがわかる。すごく観点が多いし、実に教科書的な回答のような印象をぼくは受けました。

基本情報処理技術者試験にも載っているRASISを軸にするとわかりやすいかも

RASIS(ラシス)という用語があります。これは基本情報技術者試験でも出てくる基本的な用語です。これはなにかといいますと、

用語(英語) 用語(日本語) 意味 主な指標となるもの
Reliability 信頼性 システムが正常に動作していること MTBF
Availability 可用性 必要に応じて、いつでもシステムを利用できること 稼働率
Serviceability 保守性 障害の早期発見・迅速な復旧ができること MTTR
Integrity 保全 誤作動がなく、システムが壊れにくいこと 障害発生数
Security 安全性 不正アクセスなどでシステムが壊されないこと・データの漏洩がないこと 発生件数

を指します。
これに勝手な解釈を交えて自分なりのまとめ方をすると、SREとはRASISに運用という観点が追加されたものではないかと思います。 では運用の観点とはなにかと申しますと、DevOpsともよく言われる、運用フロー効率化のことです。なので、RASISに一つ、Operation(運用)という観点を付け加えるとよいのではないか、そう思いました。ROASISとかになるのかな...?(小声)

SREを現場に落とし込む

SREでぐぐると、各企業がSREをどんなふうに解釈して、どんな取り組みをしているのか、なんとなくざっくりわかります。わりと広義の意味では共通の部分が多いが、具体的な話でいうとバラバラだったりして、一体どういうことやねん?となりがちです。そこでこんな表をつくってみました。これで現場に落とせるはず。

観点 ゴール 自社の課題 打ち手 指標
Reliability システムが正常に動作していること     MTBF
Availability 必要に応じて、いつでもシステムを利用できること   稼働率
Serviceability 障害の早期発見・迅速な復旧ができること   MTTR
Integrity 誤作動がなく、システムが壊れにくいこと 障害発生数
Security 不正アクセスなどでシステムが壊されないこと・データの漏洩がないこと 漏洩件数
Operation (1) 頻度が高く、ストレスのないリリースができること (2) 手作業である必要がないものを自動化 リリース数・手作業/自動の割合

空白になっているところを埋めていくと、うまく出来ているところ、まだまだ改善すべきところがみえてくるはず。そこにビジネス観点・長期運用観点を持ってして優先度を定めると、自然とやるべきことが決まってくるのではないでしょうか。