SHOYAN BLOG

I am a pragmatic programmer.

CircleCIでお手軽にCI環境をつくる

最近はCIプラットフォームが充実していて、Travice CI、Wercker、Droneなど様々なプラットフォームが開発されています。
最近はCircleCIを使っているのですが、なかなか便利です。

CircleCIの便利な点

CircleCIの便利な点は特別な設定をせずにテストコードさえあればCIができてしまうことです。
CircleCIにアカウントを作って、CIを行うリポジトリを設定すればOKです。
リポジトリにpushするとCIが実行されます。
ソースコードからbundle installなどの必要な前処理をしてくれるので、CIを行うための前準備的な設定が必要ありません。

私はgit_find_commiterphp-syntax-checkでCircleCIを使っています。

注意点

デフォルトのRubyのバージョンが古いので(マニュアルによるとUbuntu 12.04 and Ubuntu 14.04 build imagesのもの)、Ruby1.9から導入されたキーワード引数など比較的新しく導入された機構を使うと構文エラーが発生することがあります。
その場合はcircle.ymlファイルにRubyのバージョンを設定できます。

1
2
3
machine:
  ruby:
    version: 2.3.0

また、bundlerのバージョンでエラーが発生することがありました。
その場合は、以下のようにバージョンを指定することができます。

1
2
3
dependencies:
  pre:
    - gem install bundler -v 1.12.5

circle.ymlの構文についてはドキュメントを参考にしてください。

3年経ってスクラムをふりかえる

一時期のスクラムムーブメントは一体なんだったのか。
最近はスクラムマスターやファシリテーター、振り返りなどのスクラム用語をめっきりと聞かなくなった。

私のチームではスクラムの最後のプラクティスである朝会さえもなくなった。
結果、それで困っているかというとそんなことはない。

スクラムムーブメントを振り返ってみたいと思う。

スクラム黎明期

スクラムが流行り始めたのは2013年頃だったと思う。
次々と新しいスクラム本が出版され、私もSCRUM BOOT CAMP THE BOOKやアジャイルサムライを読んでスクラムの手法について勉強した。
会社ではスクラムの講習が開かれ、スクラムマスターやプロダクトオーナーを決めたりもした。

私のチームでは朝会と夕会を行い、カンバンでタスクを見える化し、プロジェクトの前には見積もりをチームで行い、定期的にふりかえりを行った。
スクラムマスターとプロダクトオーナーを決めて、スクラムのプラクティスを実践していた。

スクラム暗黒時代

うまくいっているように思われたスクラムだが、雲行きが怪しくなってきた。
スクラムのやり方に縛られ、チームの雰囲気がギクシャクしたものになってきたのだ。
定例のふりかえりでは発言が出ず、議論も深まることなく、時間の無駄に思えた。

スクラムとはなんだったのか

わずか3年でスクラムは姿を消してしまった。
誤解のないように言っておくが、スクラム自体がダメではないということである。
実際にスクラムを導入して成果をあげているチームもあるだろう。
実際によい時期もあったと思う。

スクラムとは手法であり、その内容は主に人同士のコミュニケーションを増やすためのものだ。
よって、人同士のコミュニケーションがうまくいかないと当然スクラムもうまくいかない。
また、コミュニケーションが増えるにあたりMTGなどの時間が増えてしまう問題がある。
そのコストは無視できるほど小さくない。

スクラムは悪くない。
しかし、スクラムは難しい。

[Ruby]オブジェクトの設定情報を保持するパターンの紹介

オブジェクトの設定情報を保持するパターンを紹介します。
このパターンを使えば設定情報を管理するクラスに集約することができ、コードの見通しがよくなります。また、初期設定の定義ができるようになります。

名前空間で区切ってConfigurationモデルに設定を持たせるようにします。
そして、トップにconfigurationとconfigureメソッドを定義します。
クラスメソッドで定義するのがポイントです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Hoge
  class Configuration
    def initialize
      @name = nil
    end
    attr_accessor :name

  end

  def self.configuration
    @configuration ||= Hoge::Configuration.new
  end

  def self.configure
    yield configuration if block_given?
  end
end

configureメソッドは以下のように初期設定したいパラメーターを設定するために使います。

1
2
3
4
5
6
7
Hope.configure do |config|
  config.name = 'shoyan'
end

config = Hoge.configuration
config.name
=> "shoyan"

configureメソッドで設定した情報が保持されています。
初期設定はできるようになりました。

次はこの設定情報を内部で参照するようにします。

Hopeクラスのinitializeメソッドで設定情報をインスタンス変数に保存します。

1
2
3
4
5
6
7
8
9
class Hoge
  def initialize
    @config = Hoge.configuration
  end

  def greeting
    puts "Hello #{@config.name}"
  end
end

実行してみます。
Hope::Configurationの値が出力されていますね。

1
2
3
hoge = Hoge.new
hope.greeting
=> Hello shoyan

Hoge.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Hoge
  class Configuration
    def initialize
      @name = nil
    end
    attr_accessor :name
  end

  def initialize
    @config = Hoge.configuration
  end

  def greeting
    puts "Hello #{@config.name}"
  end

  def self.configuration
    @configuration ||= Hoge::Configuration.new
  end

  def self.configure
    yield configuration if block_given?
  end
end

Rubyのロード機構について

Rubyはロード機構として、以下を備えています。

  • require
  • load
  • autoload
  • require_relative

require

requireは以下の特徴を備えています。

  • ロードパスからファイルを探してくる
  • 拡張ライブラリもロードできる
  • 拡張子 .rb / .so を省略できる
  • 同じファイルは2度以上ロードしない

Rubyのロードパスは $:に入っています。
私の環境では以下のように表示されました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[1] pry(main)> puts $:
/Users/shoyan/.rbenv/rbenv.d/exec/gem-rehash
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/did_you_mean-1.0.0/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/coderay-1.1.1/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/slop-3.6.0/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/method_source-0.8.2/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/pry-0.10.3/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/x86_64-darwin14
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/site_ruby
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/vendor_ruby/2.3.0
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/vendor_ruby/2.3.0/x86_64-darwin14
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/vendor_ruby
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/2.3.0
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/2.3.0/x86_64-darwin14

load

loadもrequireと同じくロードパスからファイルを探してロードします。
しかし、requireのように拡張ライブラリはロードできません。
また、ロードできるファイルもRubyプログラムだけです。
requireのように2回以上の読み込みを制御する機構もなく、callされた回数分ファイルは読み込まれます。

autoload

autoloadはrequireと似ていますが、すぐには読み込まれず、必要になった段階でrequrieされます。以下、サンプルコードです。

autoload.rb

1
2
autoload :Outer, './outer'
puts Outer.new

outer.rb

1
2
3
4
class Outer
end

puts 'Outer loaded!'

実行すると、Outerがloadされているのが確認できます。

1
2
3
  ruby autoload.rb
Outer loaded!
#<Outer:0x007fcc14040e60>

autoloadはネストされたクラスやモジュールもloadすることができます。

autoload.rb

1
2
3
autoload :Outer, './outer'
puts Outer.new
puts Outer::Inner.new

outer.rb

1
2
3
4
5
class Outer
  autoload :Inner, './inner'
end

puts 'Outer loaded!'

inner.rb

1
2
3
4
5
6
class Outer
  class Inner
  end
end

puts "Outer::Inner loaded!"

実行すると、Outer::Innerがloadされているのが確認できます。

1
2
3
4
5
  ruby autoload.rb
Outer loaded!
#<Outer:0x007f9fd4054df0>
Outer::Inner loaded!
#<Outer::Inner:0x007f9fd404f990>

require_relative

あまり見かけることがありませんが、require_relative というメソッドがRuby1.9.2から追加されています。
例えばrequireで同じ階層のファイルをロードする場合は、require './filename' のように明示的に./を指定する必要があります。
require_relativeを使えば、require_relative 'filename' のように定義することができます。

Iptablesことはじめ

iptablesの概念と設定方法を調べました。

iptablesとは

iptablesとはパケットをフィルタリングするためのツールです。
また、NATとしても使えます。

iptablesの用語

iptablesを理解するにあたって必要な用語をまとめました。

TARGETS

iptablesのルールを設定する際に、targetを指定します。
targetはユーザー定義のものと特別なtargetである、ACCEPT, DROP, QUEUE or RETURNがあります。
パケットに対してどんな操作をするかを定義します。

代表的なtargetです。

ACCEPT
パケットを通す
DROP
パケットを破棄する
QUEUE
パケットをキューにためる
RETURN
チェインを辿るのをストップして、評価を行う
REJECT
パケットを破棄し、エラーパケットを返す
DNAT
送信先IPアドレスを変更する
SNAT
送信元IPアドレスを変更する
MASQUERADE
複数の送信元IPアドレスを変更(マスカレード)する
LOG
ログを出力する

Chain

iptablesを設定する際にchainを指定します。
chainにはビルトインchainとユーザー定義chainがあります。
chainはパケットに対する実行条件を定義します。

ビルトインchainは以下です。

INPUT
FORWARD
OUTPUT
PREROUTING
POSTROUTING

TABLES

一般的にはフィルタリングとして使われることが多いiptablesですが、他にも様々な機能があります。
iptablesにはそれをtableという概念で扱っており、5つのtableがあります。

filter
パケットのフィルタリングを設定するためのテーブルです。
デフォルトのテーブルです。tableオプションを明示的に指定しない場合は、filterが指定されたことになります。
INPUT、FORWARD、OUTPUTのChainを含みます。

nat
IPアドレスの変換を設定するためのテーブルです。
PREROUTING、POSTROUTING のChainを含みます。

mangle
パケットの書き換えを設定するためのテーブルです。

raw
接続の追跡の除外の設定をするためのテーブルです。

security
アクセスコントロールの設定をするためのテーブルです。

OPTIONS

iptablesのコマンドにオプションを指定できます。
オプションはいくつかのグループに区別されます。

COMMANDS

iptablesに実行してほしいコマンドを指定します。
以下のようなコマンドがあります。

-A, –append
選択したチェインにルールを追加します。

-C, –check
指定したチェインにルールが存在するかをチェックします。

-D, –delete
チェインからルールを消します。

詳細はiptablesのmanをご覧ください。

PARAMETERS

ルールを作成する際に指定するパラメーターです。

-p, –protocol
ルールの対象となるプロトコルを指定します。

-j, –jump
ルールの対象となるtargetを指定します。

-s, –source
ルールの対象となるアドレスを指定します。
例えば、IPアドレス、ホスト名、ネットワーク名などを指定します。

詳細はiptablesのmanをご覧ください。

OTHER OPTIONS

追加オプションです。

-v, –verbose
詳細表示のオプションです。

詳細はiptablesのmanを参照ください。

iptablesの基本的な構文

基本的には以下のように指定します。

1
iptables [-t table] command chain options target

例えば、ローカルホストからのping(icmp)を許可する場合は以下となります。

1
iptables -A INPUT -p icmp -j ACCEPT

iptablesの基本的な読み方

現在のiptablesの設定を表示するには、iptables -L コマンドを使います。

1
2
3
4
5
6
7
8
9
10
11
12
Example: Input Chain Rule Table Listing
Chain INPUT (policy DROP)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
DROP       all  --  anywhere             anywhere             ctstate INVALID
UDP        udp  --  anywhere             anywhere             ctstate NEW
TCP        tcp  --  anywhere             anywhere             tcp flags:FIN,SYN,RST,ACK/SYN ctstate NEW
ICMP       icmp --  anywhere             anywhere             ctstate NEW
REJECT     udp  --  anywhere             anywhere             reject-with icmp-port-unreachable
REJECT     tcp  --  anywhere             anywhere             reject-with tcp-reset
REJECT     all  --  anywhere             anywhere             reject-with icmp-proto-unreachable

1行目がChainの名前で、次にデフォルトポリシーが表示されています(DROP)。
2行目がカラム名です。

target: target名
prot: プロトコル名。例えば、tcp, udp, icmp, or all
opt: めったに使われません。
source: 接続元のIPアドレス or サブネット or anywhere
destination: 接続先のIPアドレス or サブネット or anywhere

iptablesコマンドの使い方

いくつかの例を紹介します。

特定のサーバーからのhttp通信を許可する

1
2
3
# iptables -A INPUT -p tcp -s 192.168.1.1 --dport 80 -j ACCEPT
# 設定を保存
# service iptables save

ルールを消す

iptables -D ルール で消すことができます。
iptables -S でルール一覧がでるので、そのルールを指定すれば簡単です。

1
2
# iptables -S
-A INPUT -s 157.7.105.69/32 -p tcp -m tcp --dport 80 -j ACCEPT

以下のコマンドで消します。

1
iptables -D INPUT -s 157.7.105.69/32 -p tcp -m tcp --dport 80 -j ACCEPT

参考文献