PONCOTSU

インフラ領域を主にあれしてます

Dartの変数

変数を宣言

基本形はこう。 型 変数名 = 値; という形をとる。

int number = 1;
String name = 'hoge';

さらにDart型推論機能を持っているので、var記法 を使って、型を指定せずに変数を宣言することも可能。

var number = 1;
var name = 'hoge';

dynamic型

dynamic を使うと、動的に変数宣言できる。
他の言語にもあまりない名前の型ですが、 TypeScriptだと Any に近い振る舞いをする型っぽい?(詳しい方、教えてくださいmm)
dynamic型 の変数にはどんな型のオブジェクトでも入れることが可能となるため、型安全が保証されなくなるので、使う時は本当に必要な時だけにしたほうがよさそう。
ってことで、軽く検証してみる。
dynamic型 を使えば変数宣言時は何も考えず宣言ができるが、その変数を利用する際には、呼び出し側が変数の型を知っている必要があることがわかる。

void main() {
  void foo(String name) {
      print(name);
  }

  void bar(int name) {
      print(name);
  }

  dynamic name = "hoge";
  foo(name); // hoge
  bar(name); // type 'String' is not a subtype of type 'int'
}

bar(name) を呼び出すと、bar(name)int型 の引数を期待しているのに、Stringが入ってきたのでエラーにしたよ、という結果になります。
この場合、開発者は bar() を使う時に 変数name の実際の型が String になっていることを知っていなければなりません。

RubyスクリプトからMySQLにログインする

ActiveRecord上からDBに接続すると実際にどんなふうにDB操作をプログラムからやれるものかのかイメージしづらくなる。そこで今回はRubyのプログラムを書いて、MySQLにログインするためのスクリプトを書いてみる。

対象読者

  • ある程度Rubyを書いたことがある
  • サーバ(又はローカル)にMySQLをインストールしたことがある
  • 簡単なクエリを書いたことがある
  • Dockerを使ったことがある

構成

  • ローカル:Rubyコードをつくる・実行する
  • MySQLコンテナ:Dockerを使ってMySQLサーバとしてコンテナをつくる

こんな感じ。 RDSとか使ってもよいのだけども、検証のためなので、擬似外部サーバとしてDockerを採用した。

STEP0: 前提とする環境

  • ローカル
    • mysqlインストール済
    • rubyが動く状態
    • gem install ruby-mysql しておく
    • dockerが動く状態

STEP1: MySQLコンテナをつくる

Docker使っているならわりと簡単。

docker pull mysql
docker run --name mysql-server -e MYSQL_ROOT_PASSWORD=root -d -p 33066:3306 mysql

これでMySQLサーバが作成された。 これでコンテナ上にもmysqlのプロセスも立ち上がっているはず。

STEP2: ローカルからコンテナ上のMySQLにログインしてみる

mysql -h 0.0.0.0 -uroot -p
# パスワードは root
mysql >

無事、mysqlにログインできたらOK.

STEP3: コンテナ上で少し下準備

コンテナ上のmysqlにログインしたまま、下準備をする。 具体的には、データベース・テーブルの作成を行ないます。

CREATE DATABASE test;
mysql> CREATE TABLE users(id int, name varchar(50));
mysql> use test;
mysql> show tables;
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| users          |
+----------------+
1 row in set (0.00 sec)

mysql> DESC users;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id    | int(11)     | YES  |     | NULL    |       |
| name  | varchar(50) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

こんな感じのデータベースになればOK.

STEP4: Rubyスクリプトを書く

test.rb

require 'mysql'
connection = Mysql::connect("0.0.0.0", "root", "mysql", "test", 33066)

# 文字コードをUTF8に設定
connection.query("set character set utf8")

# DBに問い合わせ

# 挿入するデータ
data= {
1: 'hoge',
2: 'fuga'
}

# 用意したデータの分だけINSERT文を実行
data.each do |id, name|
  connection.query("INSERT INTO test.users(id, name) VALUES(#{id}, '#{name}')");
end

# SELECT文の結果を取得
rs = connection.query("SELECT * FROM users")

# 検索結果を表示
rs.each do |r|
  puts r.join ", "
end

# コネクションを閉じる
connection.close

実行してみる

書いたスクリプトを実行してみる。

[localhost]$ ruby test.rb
1, hoge
2, fuga

無事、RubyスクリプトからMySQLにログインし、クエリを叩くことができるようになりました。

利用できるデバイス情報を把握する lsblkコマンド

ハードデバイスについて勉強してみた。

Linuxはデバイスファイルを通してデバイスを利用することができる

すべてをファイルとして扱う思想のLinuxでは、接続されたデバイスもファイルを通して利用することになっている。 そのホストとデバイスの入出力を扱うための特殊なファイルをデバイスファイルとよぶ。 IDEなのかSCSI系なのか、ハードディスクによって多少仕様は変わるが、ここでは割愛する。

ホストからどんなデバイスを利用することができるのか確認してみる

lsblkコマンド を使えば、対象ホストから利用できるデバイスの詳細情報を確認することができる。

root@ip-xxx-xxx-xxx-xxx:/# lsblk -l
NAME  MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvda  202:0    0   8G  0 disk
xvda1 202:1    0   8G  0 part /

上記の出力について、少し説明。

項目 説明
NAME バイス
MAJ:MIN バイス番号(メジャー:マイナー)
RM リムーバブルディスクかどうか
SIZE 容量
RO 読み取り(ReadOnly)専用か否か
TYPE バイスの種類
MOUTPOINT マウント先

さらに詳細を説明

気になった項目に絞って調べてみた

TYPE

主なデバイスの種類として以下の3つがあるようです。

バイスの種類 説明
disk 物理ドライブ
part パーティション
lvm Logical Volume Manager。複数のハード・ディスクやパーティションにまたがった記憶領域を一つの論理的なディスクとして扱うことのできるディスク管理機能で作られた論理ボリューム

なぜRMの欄が存在するか?

そもそもRMとは、記録メディアを取り出して交換できる記憶装置のことを指す。 ストレージ運用は、内臓ハードディスクか取り外し可能なハードディスクなのかによって大きく変わるため、表示されるものだと推測される。

結局のところ、lsblkコマンドでどんなことができるのか?

使い方:
 lsblk [オプション] [<デバイス> ...]

オプション:
 -a, --all            すべてのデバイスを表示します
 -b, --bytes          可読性の高い形式ではなく、バイト単位でサイズを表示します
 -d, --nodeps         スレーブデバイスやホルダーを表示しません
 -D, --discard        discard 関連の機能を表示します
 -e, --exclude <一覧> メジャー番号を利用して、除外するデバイスを指定します (既定値: RAM ディスク)
 -f, --fs             ファイルシステムに関する情報を出力します
 -i, --ascii          ASCII 文字のみを使用します
 -I, --include <一覧> 指定したメジャー番号のデバイスのみを表示します
 -l, --list           一覧形式で出力します
 -m, --perms          パーミッションに関する情報を出力します
 -n, --noheadings     ヘッダを表示しないようにします
 -o, --output <list>  出力する列を指定します
 -O, --output-all     output all columns
 -p, --paths          完全なデバイスパスを表示します
 -P, --pairs          キー="値" の出力形式を使用します
 -r, --raw            加工を行なわない出力形式を使用します
 -s, --inverse        依存関係を逆転します
 -S, --scsi           SCSI デバイスに関する情報を出力します
 -t, --topology       トポロジに関する情報を出力します
 -x, --sort <column>  sort output by <column>

 -h, --help     このヘルプを表示して終了します
 -V, --version  バージョン情報を表示して終了します

利用可能な列 (--output で指定します):
        NAME  デバイス名
       KNAME  カーネル内部デバイス名
     MAJ:MIN  メジャー:マイナーデバイス番号
      FSTYPE  ファイルシステムの種類
  MOUNTPOINT  マウントされている場所
       LABEL  ファイルシステムのラベル
        UUID  ファイルシステムの UUID
    PARTTYPE  partition type UUID
   PARTLABEL  パーティションのラベル
    PARTUUID  パーティション UUID
   PARTFLAGS  パーティションフラグ
          RA  デバイスの先読み
          RO  読み込み専用デバイス
          RM  リムーバブルデバイス
       MODEL  デバイス識別子
      SERIAL  ディスクのシリアル番号
        SIZE  デバイスのサイズ
       STATE  デバイスの状態
       OWNER  ユーザ名
       GROUP  グループ名
        MODE  デバイスノードのパーミッション
   ALIGNMENT  アライメントオフセット
      MIN-IO  最小 I/O サイズ
      OPT-IO  最適 I/O サイズ
     PHY-SEC  物理セクタサイズ
     LOG-SEC  論理セクタサイズ
        ROTA  ローテーションデバイス
       SCHED  I/O スケジューラ名
     RQ-SIZE  要求キューサイズ
        TYPE  デバイスの種類
    DISC-ALN  discard アライメントオフセット
   DISC-GRAN  discard 粒度
    DISC-MAX  discard 最大バイト
   DISC-ZERO  discard ゼロデータ
       WSAME  write-same 最大バイト
         WWN  ユニークなストレージ識別子
        RAND  乱数シードへの追加
      PKNAME  親のカーネル内部デバイス名
        HCTL  SCSI 向けのホスト:チャンネル:ターゲット:LUN
        TRAN  デバイス伝送タイプ
         REV  デバイスのリビジョン
      VENDOR  デバイスの製造元
````

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