kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

yumリポジトリにEPELを追加する手順

ダウンロード

ここから対象バージョンのepelをダウンロードする

最新のCentOSならば

wget http://ftp.riken.jp/Linux/fedora/epel/epel-release-latest-7.noarch.rpm

インストール

sudo rpm -ivh epel-release-latest-7.noarch.rpm

yum repolist すると、 epel が増えている

オプション解説

  • i : install
  • v : verbose 詳細情報の表示
  • h : hash 進捗状況をhash(#)で表示する

必要な時だけ使う

基本的にはバンドルされているリポジトリを使って、存在しない時だけepelを使う

デフォルト無効にする

[epel]
...
enabled=0
  • /etc/yum.repos.d/epel.repo

検索、インストール

enablerepoオプションを使う

sudo yum search fail2ban --enablerepo=epel
sudo yum install fail2ban --enablerepo=epel

他の方法

yum-plugin-priorities を使ってリポジトリの優先順位をつける方法もある

yumについて理解が進んでいないのでメモ

CentOSでのパッケージ管理についてのお話

用語

RHEL : Red Hat Enterprise Linux

yum

yum : Yellowdog Updater Modified

RPM Package Managerのパッケージを管理するメタパッケージ管理システム

RPM?

RPM : RPM Package Manager

RedHat系OSが使うパッケージ管理システム

  • 再帰的命名だ!!
  • 昔は、Red Hat Package Manager だったらしい
  • パッケージ : ソフトウェアのバイナリやそれが必要とするファイルをひとまとめにしたもの

なんでRPMから更に1枚噛ませているの?

  • yumは、内部でRPMを呼び出している
  • RPMにない機能をyumは持っている
    • ファイル名より抽象的なパッケージ名
    • インストールされていないパッケージの検索
    • パッケージ同士の依存関係の管理

リポジトリ

  • パッケージを集めて保管している場所
  • CentOSにデフォルトでバンドルされているのは、base, updates, extras, centosplus, contrib の5つ
    • ただし、centosplus, contrib はデフォルト無効
    • base : OSリリース時の状態
    • updates : baseから更新があったパッケージが格納
    • extras : baseに含まれない便利なソフトウェア群

コマンド yum repolist で確認可能

また、/etc/yum.repos.d/CentOS-Base.repo に、デフォルトのリポジトリ設定が書かれている

リポジトリの追加

バンドルされているリポジトリの他に、サードパーティリポジトリを追加できる

  • 利点 : デフォルトにない/新しいパッケージを追加できる
  • 欠点 : CentOS開発チームがテストをしていない

RedHatエンタープライズ向けなので、提供されるパッケージは古めの(枯れている)バージョンが多い

その為、最新のパッケージが欲しい場合、サードパーティリポジトリに頼ることが多い

centOSでよく使われるサードパーティリポジトリ

基本的にはどれか1択。複数サードパーティリポジトリからパッケージを入れると、依存関係がわけわからんことになりがち

EPEL

EPEL : Extra Packages for Enterprise Linux

RPMfusion

Fedora プロジェクトに収録されていないパッケージを提供するレポジトリ

Remi

RHELFedoraに最新のPHPと他のソフトウェアを供給するのを目的としたリポジトリ

RepoForge

  • 旧RPMforge
  • RHEL6で更新が止まってる様子なのであえて使う必要はなさげ?
    • 2015/10現在、RHEL7が最新

参考

AWS Elastic Beanstalkの環境のSWAPをやってみる

環境の作成

dev-env は環境名

eb create dev-env

環境変数

設定

eb setenv SECRET_KEY_BASE=****** -e dev-env

確認

eb printenv dev-env
 Environment Variables:
     RACK_ENV = production
     SECRET_KEY_BASE = ******
     RAILS_SKIP_MIGRATIONS = false
     RAILS_SKIP_ASSET_COMPILATION = false
     BUNDLE_WITHOUT = test:development

アプリで環境変数を使用する

普通に ENV でアクセスできる

参考

デプロイ

ローカルの最新のcommitがデプロイされる

  • master意外のブランチをcheckoutしているならば、それがデプロイされる
eb deploy dev-env

ブラウザで開く

eb open dev-env

環境設定のwebページを開く

eb console dev-env

環境の一覧をチェック

$ eb list
dev-env
* prod-env

より詳しく

$ eb list -v
Region: us-west-2
Application: elastic_beanstalk_demo
    Environments: 2
        dev-env : ['i-96f6da50']
        prod-env : ['i-29c9e5ef']

環境をswapする

環境が使用しているURLを交換する

  • elasticbeanstalkがデプロイするときにはダウンタイムが発生する
  • そのため、別の環境にデプロイして、問題なければ、本番とステージの環境のCNAMEをswapして、ダウンタイムなしに本番環境を最新にする。というのが、elasticbeanstalk Way っぽい
  • Blue-Green Deployment と言うらしい
    • 環境をまるっと切り替える
    • 問題があったら元に戻せば良いだけなので安心
    • ダウンタイムが(ほぼ)発生しない
    • トランザクションの取り扱いについて考慮する必要がある
$ eb swap prod-env -n dev-env

なんかエラー

$ eb swap prod-env -n dev-env

ERROR: InvalidShapeReferenceError :: Invalid model, missing shape reference: OrderedDict([(u'type', u'structure'), (u'members', OrderedDict([(u'User', OrderedDict([(u'shape', u'Double')])), (u'Nice', OrderedDict([(u'shape', u'Double')])), (u'System', OrderedDict([(u'shape', u'Double')])), (u'Idle', OrderedDict([(u'shape', u'Double')])), (u'IOWait', OrderedDict([(u'shape', u'Double')])), (u'IRQ', OrderedDict([(u'shape', u'Double')])), (u'SoftIRQ', OrderedDict([(u'shape', u'Double')]))]))])

色んな所でコマンドラインがうまく動かないという報告が上がってるので、ブラウザでやることにする

参考

その他コマンドメモ

デフォルトの環境を設定

eb use ${環境名}
  • 各コマンドで環境を指定しない場合、デフォルトの環境が指定される
  • デプロイコマンドを実行すると、内部でこれが設定される

環境の停止

お金が勿体無いのでさっさと停止

eb terminate dev-env

TODO

iptables について理解する

iptablesってなんぞ?

Linuxに実装されたパケットフィルタ兼NAT

NAT?

NAT(Network Address Translation)

ネットワークアドレス変換

ローカルIPと、グローバルIPの変換を行う

  • ローカルから外部に出るときに、グローバルIPの割当を行う
  • 外部からローカル内のマシンにアクセスする時に、IPの変換を行う

パケットフィルタ?

送信されてきたパケットを検査して通過/遮断を判断する機能

ファイアウォールとは違うの?

ファイアウォール : 外部との通信制御して、内部のコンピューターネットワークの安全を維持するソフトウェア

有効化する

$ sudo /sbin/chkconfig iptables on

設定の確認

iptables --list を実行

$ sudo /sbin/iptables --list
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

これは何も設定されていない状態

  • target prot opt source destination はヘッダ。

ヘッダの読み方

policy

チェイン(後述)全体に適用されるルール

  • 上記の場合、 policy ACCEPT で、基本的には通信を許可

用語

  • Chain : パケット群にマッチするルールのリスト
  • ルール : マッチしたパケットに対する処理を規定する
  • target : パケットに対する処理(主に以下の3つ)
    • ACCEPT : パケットをそのまま通す
    • DROP : パケットを破棄
    • RETURN : 同じルールが上位Chainにある場合、そちらを適用

Chain

デフォルトでは INPUT FORWARD OUTPUT の Chain がある

  • INPUT : 受信したデータの扱い
  • OUTPUT : 送信するデータの扱い
  • FORWARD : 他のマシンに渡すデータの扱い

他に自作のChainを作って、上記3つのチェインに連鎖させることができる

Fail2ban であるIPをBANした状態のとき(一部)

Chain INPUT (policy ACCEPT)
target     prot opt source               destination
fail2ban-HTTP  tcp  --  anywhere             anywhere            tcp dpt:http

Chain fail2ban-HTTP (1 references)
target     prot opt source               destination
REJECT     all  --  192.0.2.0  anywhere            reject-with icmp-port-unreachable
RETURN     all  --  anywhere             anywhere

空白の取り方がおかしいので見づらい...

  • INPUTfail2ban-HTTP が Chain
  • fail2ban-HTTPでは、192.0.2.0 をREJECT`
  • それ以外は、RETURN
  • INPUT ではその他に何も指定がないので、policy ACCEPT なので、そのまま通す

TODO

  • フィルタの追加方法
  • テーブルについて
  • 設定の定石(普通のwebサーバの場合、どんな設定をすべきか?)

参考

AWS Elastic Beanstalk まずは動かしてみる

AWS CLIツールのインストール

$ pip install awsebcli

Rails環境の用意

$ rails g controller hello index

Rails環境上で、EBS初期化

$ eb init

リージョンどこにするか聞かれる

Select a default region
1) us-east-1 : US East (N. Virginia)
2) us-west-1 : US West (N. California)
3) us-west-2 : US West (Oregon)
4) eu-west-1 : EU (Ireland)
5) eu-central-1 : EU (Frankfurt)
6) ap-southeast-1 : Asia Pacific (Singapore)
7) ap-southeast-2 : Asia Pacific (Sydney)
8) ap-northeast-1 : Asia Pacific (Tokyo)
9) sa-east-1 : South America (Sao Paulo)
(default is 3):

AWSのアクセスキーとシークレットトークンを聞かれる

You have not yet set up your credentials or your credentials are incorrect
You must provide your credentials.
(aws-access-id): ********
(aws-secret-key): ********

どのアプリケーションを使うか聞かれる

既存のアプリが残っているので、それを使うか、別のを作るか聞かれた

  • 新しく作ることにした
Select an application to use
1) elastic_beanstalk_demo
2) [ Create new Application ]
(default is 1): 2
Enter Application Name
(default is "elastic_beanstalk_demo2"):

Rubyアプリで良いよな? と聞かれた

It appears you are using Ruby. Is this correct?
(y/n): y

Rubyのバージョン聞かれた

Select a platform version.
1) Ruby 2.2 (Puma)
2) Ruby 2.1 (Puma)
3) Ruby 2.0 (Puma)
4) Ruby 2.2 (Passenger Standalone)
5) Ruby 2.1 (Passenger Standalone)
6) Ruby 2.0 (Passenger Standalone)
7) Ruby 1.9.3
(default is 1): 1

SSH使う? と聞かれる

Do you want to set up SSH for your instances?
(y/n): y

キーペアを聞かれる

Select a keypair.
1) test1234
2) [ Create new KeyPair ]
(default is 2): 1

/.elasticbeanstalk が作られ、.gitignore に追加されるので commit する

$ git diff
diff --git a/.gitignore b/.gitignore
index 050c9d9..964c20f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,8 @@
 /log/*
 !/log/.keep
 /tmp
+
+# Elastic Beanstalk Files
+.elasticbeanstalk/*
+!.elasticbeanstalk/*.cfg.yml
+!.elasticbeanstalk/*.global.yml

デプロイ

$ eb create elastic-beanstalk-demo2

サイトを開く

$ eb open

なんかエラー

A really lowlevel plumbing error occured. Please contact your local Maytag(tm) repair person.

エラーログをチェック

$ eb logs
-------------------------------------
/var/log/puma/puma.log
-------------------------------------
=== puma startup: 2015-09-22 07:37:26 +0000 ===
=== puma startup: 2015-09-22 07:37:26 +0000 ===
[20209] + Gemfile in context: /var/app/current/Gemfile
[20205] - Worker 0 (pid: 20209) booted, phase: 0
2015-09-22 07:42:23 +0000: Rack app error: #<RuntimeError: Missing `secret_token` and `secret_key_base` for 'production' environment, set these values in `config/secrets.yml`>

puma?

EBS環境で使われているデフォルトのwebサーバ → A Modern, Concurrent Web Server for Ruby - Puma

  • 最近はHerokuも採用しているらしい
  • 非I/Oブロッキングな作りの為、スロークエリクライアントアタックに強いとの評判
    • 遅いクライアントが大量に接続されて、全てのワーカープロセスが待ち状態のままにさせられる攻撃
  • なんでまたこんなぐぐりにくい名前を...

この辺が詳しい。 → 最近の Rack サーバ事情について - おもしろwebサービス開発日記

シークレットキーを設定する必要があるらしいので作る

$ rake secret
$ eb setenv SECRET_KEY_BASE=*******

見れた!!!

まずはここまで

TODO

  • puma と、Passenger どちらがよいのじゃろ?
  • リポジトリを更新してから再度デプロイ
    • githubのmasterブランチ更新で自動的にデプロイ
  • デプロイ完了したらslackで通知
  • RDSとの連携
  • stage, product環境をそれぞれ作る
  • CloudWatch使う
  • 独自ドメイン使う

参考

Railsスタイルガイドななめ読み

ruby読んだついでにこっちも読んどく

Routing

RESTfulなresourceにアクションを追加する場合、member と collection を利用する

#config/routes.rb
resources :subscriptions do
  get 'unsubscribe', on: :member
end

resources :photos do
  get 'search', on: :collection
end

resources :subscriptions do # 複数ある場合
  member do
    get 'unsubscribe'
    # more routes
  end
end
  • member: id付きのルート
  • collection: idなしのルート

resourcesの入れ子で、ActiveRecordのモデル間の関連を表現できる

resources :posts do #Post has meny Comments
  resources :comments
end
resources :posts, shallow: true do
  resources :comments
end

match を利用しない

  • 利用する場合、アクション毎に :via オプションで[:get, :post, :patch, :put, :delete]を指定する

Controllers

  • ビジネスロジックは、modelに書く
    • controller の役割は、view層にデータを渡すこと、またはview層からデータを受け取ることのいずれかのみ
  • (理想論ですが)すべての controller は find と new 以外のメソッドはひとつ程度に
  • controller と view の間でやりとりするインスタンス変数は2つまで

Models

  • ActiveRecord を継承しないモデルも導入する
  • Sexy Validationを利用する
  • 独自のヴァリデーションが2回以上呼び出される場合、もしくは独自のヴァリデーションが正規表現を含む場合は、ファイルとして切り出す
    • 独自のヴァリデーション・ファイルはapp/validatorsを作成し、その配下に配置
  • 名前付きスコープがラムダ式で定義されており、かつ引数が複雑になってきた際には、スコープ名と同名のActiveRecord::Relationオブジェクトを返すクラスメソッドを作成する
class User < ActiveRecord::Base
  def self.with_orders
    joins(:orders).select('distinct(users.id)')
  end
end
  • ActiveRecordのオブジェクトに対して繰り返し処理を行う場合は、find_each
    • all だと、全てのデータをDBから取り出してメモリに突っ込むので危険

Migrations

Assets

  • 可能ならば、gem化されたAssetsを使う

Bundler

  • 有名で利用者の多い gem を利用する
  • OS固有のgemは、グルーピングしよう
# Gemfile
group :darwin do
  gem 'rb-fsevent'
  gem 'growl'
end

group :linux do
  gem 'rb-inotify'
end

config/application.rb に以下を追記して、環境変数RUBY_PLATFORMで使用するgemを決定する

#config/application.rb
platform = RUBY_PLATFORM.match(/(linux|darwin)/)[0].to_sym
Bundler.require(platform)

TODO

rubyスタイルガイドななめ読み

rubocopを使うに当たって把握しておく

  • 知らなかったこと/よさ気なところをメモ

ソース

レイアウト

{の前後、}の前にはスペース

[1, 2, 3].each { |e| puts e }

メソッドの引数に初期値を割り当てるとき、 =演算子の周りにはスペース

def some_method(arg1 = :default, arg2 = nil, arg3 = [])

メソッド呼び出しが複数行に及ぶときは、引数は揃える

  • ")"の置き場に悩むことがあったので、このようにする
# 良い例 (普通のインデントです)
def send_mail(source)
  Mailer.deliver(
    to: 'bob@example.com',
    from: 'us@example.com',
    subject: 'Important message',
    body: source.text
  )
end

可読性のため、大きな数値にはアンダースコア

num = 1_000_000

構文

複数行にまたがるif/unlessでは、条件式は常にif/unlessと同じ行に

  • 悪い例をやりがち
# 悪い例
if
  some_condition
  do_something
  do_something_else
end

# 良い例
if some_condition
  do_something
  do_something_else
end

1行のcase文ではwhen x then ...を使う

  • ";" でやりがちだけど、英文的にはthenの方がしっくりくるのかしら
case x
when 100; p "100"     # NG
when 200 then p "200" # OK
end

andとorの使用は禁止

  • andを使いがち

代入部分を()で囲まずに、=の返り値を条件式に用いてはいけません

if (v = array.grep(/foo/))
  do_something(v)
  ...
end
  • そもそも条件式内で代入するなよ的な気持ちが...

値が入っているかわからない変数の前処理のは&&=。 &&=を使えば変数が存在するときのみ値を変更するので、 存在確認に用いている不要なifを除去できる

  • 知らなかった...
if something # NG
  something = something.downcase
end

something = something ? something.downcase : nil # NG
something = something.downcase if something      # OK
something = something && something.downcase      # OK
something &&= something.downcase                 # More better

Perlスタイルの($:や$;などのような)特別な変数の使用は避ける。 Englishライブラリを使う

1行の本文を持つラムダには新しいリテラルを使う。 lambdaは複数行にまたがるときのみ

l = ->(a, b) { a + b }

Proc.newよりproc

p = proc { |n| puts n }

使わないブロック引数やローカル変数の先頭にはを付ける。 単にを用いてもOK

  • rubocopなどで使用されていない変数エラーを回避できる
result = hash.map { |_, v| v + 1 }

def something(x)
  _, used_var = something_else(x)
end

複雑な比較ロジックの代わりに、 可能な限りRangeやComparable#between?を使う

  • わすれがち
do_something if x >= 1000 && x <= 2000   # 悪い例
do_something if (1000..2000).include?(x) # 良い例
do_something if x.between?(1000, 2000)   # 良い例

ループ内では条件判定ブロックよりもnext

  • nextあんまり使ってないなぁ
# 悪い例
[0, 1, 2, 3].each do |item|
  if item > 1
    puts item
  end
end

# 良い例
[0, 1, 2, 3].each do |item|
  next unless item > 1
  puts item
end

mapとflattenの組み合わせの代わりに、flat_map

  • 深さが2以上の配列には適用できないので注意
  • flat_map知らなかった...

reverse.eachの代わりにreverse_each

  • reverse_each知らなかった...

注釈

  • TODO : あとで追加されるべき、今はない機能や関数
  • FIXME : 直す必要がある
  • OPTIMIZE* : パフォーマンスに問題を及ぼすような、遅い、または非効率な
  • HACK : 疑わしくリファクタリングで除去すべきコード
  • REVIEW : 意図したとおりに動くか確認する必要がある箇所
    • 例: REVIEW: Are we sure this is how the client does X currently?

クラス

Struct.newの使用を考えましょう、 それは、単純なアクセサ、コンストラクタや比較演算子を定義してくれます

# 良い例
class Person
  attr_accessor :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
end

# より良い例
Person = Struct.new(:first_name, :last_name) do
end
  • truct.newで初期化されたインスタンスを拡張してはいけません。 それは余分なクラスレベルをもたらし、 複数回requireされた時に、奇妙なエラーの原因にもなります
  • 継承での振る舞いが"扱いづらい"ので、クラス変数(@@)の使用は避けましょう

例外

例外発生にはfail。 raiseは例外をキャッチして、再度発生させるときにのみ

不確実性のあるメソッド(Avdi Grimmによって作られた言葉です) を用いてbeginの蔓延を和らげる

# 悪い例
begin
  something_that_might_fail
rescue IOError
  # handle IOError
end

# 良い例
def with_io_error_handling
  yield
rescue IOError
  # handle IOError
end

with_io_error_handling { something_that_might_fail }

with_io_error_handling { something_else_that_might_fail }
  • これ便利そう!!

Collections

  • シンボルの配列が必要なときは%iが好まれます
  • 要素が一意のものを扱うときは、Arrayの代わりにSet
  • Hash#has_key?よりHash#key?を、 Hash#has_value?よりHash#value?。Matzが述べているように、長い記法は廃止が検討されています

コレクションに対するアクセサを提供するとき、 コレクション内の要素にアクセスする前に、 nilでアクセスするのを防ぐための代替のアクセス方法を提供しましょう

# 悪い例
def awesome_things
  @awesome_things
end

# 良い例
def awesome_things(index = nil)
  if index && @awesome_things
    @awesome_things[index]
  else
    @awesome_things
  end
end

正規表現

速い代替手段がある場合、String#gsubは使わない

  • subやtrで代替する

捕捉した結果を使う必要のないとき、捕捉しないグループを用いる

/(first|second)/   # 悪い例
/(?:first|second)/ # 良い例

Perlレガシーの暗号的な変数は使わない

Regexp.last_match(1)

/(?<meaningful_var>regexp)/ =~ string
puts meaningful_var

複雑な正規表現にはx識別子を用いる

  • スペースが無視されることに注意
regexp = /
  start         # some text
  \s            # white space char
  (group)       # first group
  (?:alt1|alt2) # some alternation
  end
/x