Lチカ開発ブログ

https://l-chika.com/の開発ブログ

1年ぶりにbrew updateした時のメモ

どんな時のため

  • MacのOSをバージョンアップしたりすると brew update がうまくいかない
  • 環境: macOs Sierra バージョン:10.12.2

きっかけ

開発はVagrantでしたりするけど、ローカルでサクッとrubyの動作をチェックしたいときに、rubyのバージョンを変更しようと、 brew upgrade rbenv ruby-build をしようと思ったら、エラーとなった

手順概要

  1. brew updateとupgrade
  2. rbenvのupdate
  3. bundler のupdate

brew updateとupgrade

$ brew update
Error: The /usr/local directory is not writable.
Even if this directory was writable when you installed Homebrew, other
software may change permissions on this directory. Some versions of the
"InstantOn" component of Airfoil are known to do this.

You should probably change the ownership and permissions of /usr/local
back to your user account.
  sudo chown -R $(whoami):admin /usr/local
Kernel.exit

$ sudo chown -R $(whoami):admin /usr/local
  • パーミッションを変更しても、さらにソース管理でエラーとなる場合は、git statsh
  • 必要なパッケージをupgrate
  • パーミッションは元に戻しても、戻さなくても
$ brew update

Could not restore untracked files from stash
Error: Failure while executing: git stash pop

$ cd /usr/local && git stash pop --quiet
$ brew update
$ brew upgrade rbenv ruby-build
$ sudo chown root:wheel /usr/local

rbenvのupdate

  • ruby 2.3.3をインストールする場合
$ rbenv install --list
$ rbenv install 2.3.3
$ rbenv versions
$ rbenv global 2.3.3
$ rbenv rehash
$ ruby -v

bundler のupdate

  • 特定バージョン以上のrubyをサポートしているgemをインストールする際に、なぜかbundle install 時にエラーになった
  • bundlerが利用しているバージョンが違ったためのよう

rubyとbundlerのパスを確認

$ which ruby
$ which bundle

違っている場合には以下を実行

$ rbenv exec gem install bundler
$ rbenv rehash
$ which bundle

ELB->ALBに変更した際の確認メモ

ELBのtypeを ClassicApplication に変更した際のメモ。

概要

  1. 従来のELBとは別にALBを増設
  2. ALBのDNS name(A Record)からIPを取得する
  3. ローカルの/etc/hostsを変更して、ブラウザで確認する

ALBのDNS name(A Record)からIPを取得する

$ dig xxxx.ap-northeast-1.elb.amazonaws.com

/etc/hostsを変更

$ sudo vim /etc/hosts

/etc/hosts

xx.xx.xx.xx hoge.com

ブラウザで確認する

hoge.com をブラウザで確認して、ALBにトラフィックが流入していることを確認する

素人が電子工作始めるならやっぱりArduino?

電子工作の素人である自分が、その入門として始めたのが「Arduino」。

なぜArduino

Arduinoに関する情報はたくさんあるのでここでは簡単に、自分が選んだ理由を。

  • 電子回路の前提知識がなくともプログラミングが少しできれば制御できる
  • 簡単な事ならハンダづけ不要
  • キットが安く手に入る

と色々あるが、一番のきっかけは、電子工作をするにあたって ArduinoRaspberry Pi かで迷っていると、ちょっと詳しい人から

「簡単に始めるならArduinoがオススメ。Raspberry Pi は小さいLinuxなので小難しい事をするならオススメ」

という言葉で決めた。

流行ってる?

  • ネットや本でArduinoに関する情報は多くある
  • 多種多様な専用の拡張部品(シールド)がある

Googleトレンド

Raspberry Piと比較すると同じくらい検索されている

懸念

分裂、そして統合

Arduino LLC(Arduino.cc)とArduino SRL(Arduino.org)に分裂し、そのせいで - Arduino IDE 1.6.Xは、arduino.ccのみが配布 - Arduino IDE 1.7.Xは、arduino.orgのみが配布 Arduino Uno等の従来機種は、どちらのArduino IDEでも利用できますが、分裂後のそれぞれの機種を使う場合はArduino IDEを使い分ける必要が発生

分裂に関して

Arduinoの内部分裂について | スイッチサイエンス マガジン

Make: Japan | Arduino戦争:グループ分裂、そして新製品の登場

Arduino創始者グループの分裂がユーザに与える影響について - しなぷすのハード製作記

統合に関して

分裂していたArduinoが和解して1つに - ITmedia ニュース

という事で利用者にとっては迷惑な話であったが、ひとまず収束しそうな感じ。

どうせRailsの環境構築するなら本番環境にも応用できるようにする

f:id:l-chika:20161218190851p:plain

Railsに限らず、Webサービスを開発する場合には開発する環境の整備が必要。 開発は普段自分のmacやWindowなどのローカルPCでしていると思いますが、最終的には本番環境で利用できるものでなければならない。 自分は以下の理由から、開発段階から本番環境に近しい環境での開発をしている。

  • OSが異なると、その環境固有の設定が必要になることがあり、それがかなり手間がかかる
  • 本番環境で問題が発生した場合に、ローカルでも再現できるようにしたい

では、どうしているかというと普通に「Vagrant」。 開発環境と本番環境(AWS EC2)で、ほぼ同じ設定で再利用することで、環境の差異をなくし、環境構築の手間を軽減。

前提

  • Virtualbox 5.0.16
  • Vagrant 1.8.6以上
  • ChefDK を利用(ローカルにbundle install でberksをインストールすると、ものすごく重かったので)。
  • Vagrantの環境。※適宜、必要な環境に設定を変更する

開発環境の構築手順

$ vagrant -v
Vagrant 1.8.6
$ vagrant plugin list
vagrant-omnibus (1.4.1)
vagrant-share (1.1.4, system)
vagrant-vbguest (0.11.0)
$ chef -v
Chef Development Kit Version: 0.18.26
chef-client version: 12.14.89
delivery version: master (d86679335580be3de22996ef294b20d525889d8d)
berks version: 5.1.0
kitchen version: 1.13.0

構築するディレクトリ用意とknife初期化

$ mkdir {VAGRANT_PATH} && cd $_
$ knife solo init .
$ vim Berksfile
Berksfile
source "https://api.berkshelf.com"

cookbook 'git', '~>4.1.0'
cookbook 'build-essential', '~>2.1.3'
cookbook 'ruby_build'
cookbook 'ruby_rbenv'
cookbook 'nginx'
cookbook 'mysql', '~> 5.3.6'
cookbook 'memcached-cookbook', '~> 0.3.2'
cookbook 'postfix', '~> 3.7.0'
cookbook 'nodejs', '~> 2.4.4'

cookbook取得

$ berks vendor

Vagrant

$ vagrant init
$ vim Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure(2) do |config|
  config.vm.box = "phusion/ubuntu-14.04-amd64"
  config.vm.network "forwarded_port", guest: 3000, host: 3000
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.synced_folder "./applications", '/home/vagrant/applications', type: "nfs"
  config.omnibus.chef_version=:latest
  config.vm.provision :chef_solo do |chef|
    # Vagrant 1.8.1の場合にはコメントイン
    # chef.channel = 'stable'
    # chef.version = '12.10.24'
    chef.cookbooks_path = ["./berks-cookbooks", "./site-cookbooks" ]

    chef.add_recipe "apt"
    chef.add_recipe "ruby_build"
    chef.add_recipe "ruby_rbenv::system"
    chef.add_recipe "ruby_rbenv::user"
    chef.add_recipe "mysql::server"
    chef.add_recipe 'nginx'
    chef.add_recipe 'memcached-cookbook'
    chef.add_recipe 'postfix'
    chef.add_recipe 'postfix::sasl_auth'
    chef.add_recipe 'nodejs'
    # chef.add_recipe 'myapp' # 独自のレシピを入れる場合

    chef.json = {
      myapp: {
        name: 'fabric',
        server_name: '192.168.33.10'
      },
      rbenv: {
        user_installs: [{
          user: 'vagrant',
          rubies: ["2.2.3"],
          global: "2.2.3",
          gems: {
            "2.2.3" => [
              { name: "bundler" }
            ]
          }
        }]
      },
      mysql: {
        version: '5.6',
        port: '3306',
        server_root_password: ''
      },
      postfix: {
        main: {
          relayhost: '[smtp.gmail.com]:587',
          smtp_sasl_auth_enable: 'yes'
        },
        sasl: {
          smtp_sasl_passwd: 'your_password',
          smtp_sasl_user_name: 'your_username'
        }
      },
      nodejs: {
        install_method: 'binary',
        binary: { url: 'https://nodejs.org/dist/v6.6.0/node-v6.6.0-linux-x64.tar.xz',
                  # curl -L -s https://nodejs.org/dist/v6.6.0/node-v6.6.0-linux-x64.tar.xz | shasum -a 256
                  checksum: 'fdf4377ea4dc9ba2f09d81d9ad1eae42e7eb870c4b1b69f2761f22f28cb5ba31',
                },
        version: '6.6.0'
      }
    }

  end

  config.vm.provider "virtualbox" do |vb|
    # vb.gui = true
    vb.memory = "1024"
  end

end

アプリケーション用ディレクト

$ cd {VAGRANT_PATH}
$ mkdir applications && cd $_
$ git clone git@github.com:xxxx

vagrantとアプリケーションの起動

$ vagrant up --provider=virtualbox
※ 初回起動は10~15分くらいかかる
$ vagrant ssh
$ cd applications/{APP_PATH}
$ bundle install --path=vender/bundle
$ ./bin/rails s -b 0.0.0.0

本番環境(AWS)の構築手順

前提

  • AWSのアカウントはある

vagrant plugin

$ vagrant plugin list
dotenv (2.1.1)
vagrant-aws (0.7.2)
vagrant-omnibus (1.5.0)
vagrant-share (1.1.5, system)
vagrant-vbguest (0.13.0)

手順

box追加

$ vagrant box add dummy https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box
$ mkdir {VAGRANT_PATH} && cd $_
$ knife solo init .
$ vim Berksfile
$ berks vendor
$ vim .env
$ vagrant init
$ vim Vagrantfile

※ Berksfileは開発環境と同じ

.envの設定

AWS_SSH_USERNAME="ec2-user" # AWSにSSHアクセスする際のユーザ名(ec2-user or ubuntu)
AWS_SSH_KEY="~/.ssh/vagrant.pem" # 作成した秘密鍵のパス
AWS_ACCESS_KEY_ID="" # ユーザ作成時のAccess Key Id
AWS_SECRET_ACCESS_KEY="" # ユーザ作成時のSecret Access Key
AWS_KEYPAIR_NAME="vagrant" # 作成したキーペア名
AWS_SECURITY_GROUP="vagrant" # セキュリティグループ名
# -*- mode: ruby -*-
# vi: set ft=ruby :

Dotenv.load

Vagrant.configure(2) do |config|
  config.vm.box = 'dummy'
  # config.vm.box_url = 'https://github.com/mitchellh/vagrant-aws/raw/master/dummy.box'
  config.omnibus.chef_version = :latest

  config.vm.provider :aws do |provider, override|
    # AWS認証情報
    provider.access_key_id = ENV['AWS_ACCESS_KEY_ID']
    provider.secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
    provider.keypair_name = ENV['AWS_KEYPAIR_NAME']

    # EC2インスタンスの設定
    provider.region = 'ap-northeast-1'
    provider.availability_zone = 'ap-northeast-1c'

    provider.ami = 'ami-a21529cc' # Ubuntu Server 14.04 LTS
    # provider.ami = 'ami-0919cd68' # Ubuntu Server 16.04 LTS

    provider.instance_type = 't2.micro'
    provider.instance_ready_timeout = 120
    provider.terminate_on_shutdown = false
    # provider.subnet_id = "xxxxxx"

    # セキュリティグループ
    provider.security_groups = [ENV['AWS_SECURITY_GROUP']]
    # タグ(任意)。インスタンスに分かりやすい目印を付けたい場合。
    provider.tags = { Name: 'vagrant-aws', Description: 'Boot from vagrant-aws' }

    # ログイン後、sudo できるようにする
    provider.user_data = <<-USER_DATA
    #!/bin/sh
    sed -i -e 's/^\\(Defaults.*requiretty\\)/#\\1/' /etc/sudoers
    USER_DATA

    # AWSへの接続ユーザおよび秘密鍵の指定
    override.ssh.username = ENV['AWS_SSH_USERNAME']
    override.ssh.private_key_path = ENV['AWS_SSH_KEY']
    override.ssh.pty = true

    # Ubuntuの場合
    override.nfs.functional = false
    # override.vm.synced_folder ".", "/home/ec2-user/vagrant", disabled: true
  end

  config.vm.provision :chef_solo do |chef|
    # Vagrant 1.8.1の場合にはコメントイン
    # chef.channel = 'stable'
    # chef.version = '12.10.24'
    chef.cookbooks_path = ['./berks-cookbooks', './site-cookbooks']

    chef.add_recipe 'apt'
    chef.add_recipe 'ruby_build'
    chef.add_recipe 'ruby_rbenv::system'
    chef.add_recipe 'ruby_rbenv::user'
    chef.add_recipe 'mysql::server'
    chef.add_recipe 'nginx'
    chef.add_recipe 'memcached-cookbook'
    chef.add_recipe 'postfix'
    chef.add_recipe 'postfix::sasl_auth'
    chef.add_recipe 'nodejs'
    chef.add_recipe 'myapp'

    chef.json = {
      myapp: {
        name: 'fabric',
        server_name: '192.168.33.10',
        wwww_dir: { owner: 'ubuntu', group: 'ubuntu' }
      },
      rbenv: {
        user_installs: [{
          user: 'ubuntu',
          rubies: ["2.2.3"],
          global: "2.2.3",
          gems: {
            "2.2.3" => [
              { name: "bundler" }
            ]
          }
        }]
      },
      mysql: {
        version: '5.6',
        port: '3306',
        server_root_password: ''
      },
      postfix: {
        main: {
          relayhost: '[smtp.gmail.com]:587',
          smtp_sasl_auth_enable: 'yes'
        },
        sasl: {
          smtp_sasl_passwd: 'your_password',
          smtp_sasl_user_name: 'your_username'
        }
      },
      nodejs: {
        install_method: 'binary',
        binary: { url: 'https://nodejs.org/dist/v6.6.0/node-v6.6.0-linux-x64.tar.xz',
                  # curl -L -s https://nodejs.org/dist/v6.6.0/node-v6.6.0-linux-x64.tar.xz | shasum -a 256
                  checksum: 'fdf4377ea4dc9ba2f09d81d9ad1eae42e7eb870c4b1b69f2761f22f28cb5ba31',
                },
        version: '6.6.0'
      }
    }
  end
end

vagrant起動

$ vagrant up --provider=aws

最後に

簡単にではあるば、開発環境とAWSで利用できる設定を記述。本番ではDBやメール、ファイルサーバー、Memcache等のミドルウェアが異なるがWebサーバーはほぼ同じ設定を利用できることで、何か障害が発生しても問題の切り分けがしやすくなる。

関連・参考本

Chef実践入門 ~コードによるインフラ構成の自動化 (WEB+DB PRESS plus)

Chef実践入門 ~コードによるインフラ構成の自動化 (WEB+DB PRESS plus)

実践 Vagrant

実践 Vagrant

Application Load Balancer(ALB)に登録されているTarget GroupのEC2からprivate ipを一括取得する

Capistranoなどで、ロードバランサー(ELB)に紐付いているWebサーバー(EC2)にデプロイする際にIPアドレスを直接記述するのではなく、 ロードバランサーのARNから動的に取得する方法。

f:id:l-chika:20161215101854p:plain

なにがうれしい

インスタンスのIPが変更になった時や、台数の増減があった場合に、deploy.rb をその度に変更する手間が省ける。

前提

AWS-SDK を利用してrubyで操作する

これまで

ALBにする前はELB(Elastic Load Blancer)のclassicを利用していて、ELBに紐づくインスタンスのIPを以下で取得していた。 (しかもSDKのバージョンがかなり古い)

Gemfile

gem 'aws-sdk', '1.26.0'

deploy.rbから抜粋

require 'aws-sdk'

AWS.config({
  :access_key_id => '******',
  :secret_access_key => '******',
  :ec2_endpoint => '******',
  :elb_endpoint =>'******'
})

elb = AWS::ELB.new.load_balancers['hoge']
ips = elb.instances.select {|i| i.exists? && i.status == :running}.map(&:private_ip_address)

ALBの場合

ELBのtypeをapplication(ALB)にした場合はこれまでのやり方ではIPアドレスが取得できないので、以下に変更 ※ aws-sdkは(2.5.11)を利用

Gemfile

gem 'aws-sdk', '~> 2.5.3'

新deploy.rbから抜粋

require 'aws-sdk'

# 環境にあわせて設定
access_key = '******'
secret_key = '******'

load_balancer_arn =  'arn:aws:elasticloadbalancing:******'
region = '******'
credentials = Aws::Credentials.new(access_key, secret_key)

alb_cleint = Aws::ElasticLoadBalancingV2::Client.new(credentials: credentials, region: region)


alb_resp = alb_cleint.describe_target_groups(load_balancer_arn: load_balancer_arn)
target_group_arns = alb_resp.target_groups.map(&:target_group_arn)

instance_ids = []
target_group_arns.each do |target_group_arn|
  alb_resp = alb_cleint.describe_target_health(target_group_arn: target_group_arn)
  instance_ids += alb_resp.target_health_descriptions.map(&:target).map(&:id)
end

ec2_clinet = Aws::EC2::Client.new(region: region, credentials: credentials)
ec2_resp = ec2_clinet.describe_instances(filters:[{ name: 'instance-id', values: instance_ids }])

ips = []
ec2_resp.reservations.each do |reservation|
  reservation.instances.each do |instance|
    ips << instance.private_ip_address
  end
end

参考

AWS SDK for Ruby

SDKの使い方とか

Amazon Web Services完全ソリューションガイド

Amazon Web Services完全ソリューションガイド

自分が考える最低限の構成でWebサービスを作ったときのAWS費用

l-chika.hatenablog.com

一念発起してWebサービスの作成に挑戦し、Lチカ を作成!(現在は閉じてます)

AWSで1ヶ月ほど稼働させてみると意外と費用がかかったので、色々迷ったが一旦 全てを削除

で、何にいくらくらいかかったかを整理。

どんな構成だったか?

サービスの要件について

サーバーサイドエンジニアがなぜ一人でWebサービスを作ろうと思ったか - Lチカ開発ブログ

インフラの構成

以下を参考に大枠の構成を作成。

【AWS 再入門】VPC 環境に踏み台サーバーを構築して SSH 接続してみよう – NET BIZ DIV. TECH BLOG

利用したサービス

  • ELB
  • EC2
    • Webサーバー:t2.micro
    • 踏み台兼デプロイサーバー:t2.micro
  • ElasticIP
  • RDS:t2.micro
  • S3
  • SMS
  • Route 53

思ってたより高かった

当初は月3000円くらいかなーって「予算」を作成。 (AWSでは利用料金の上限を設定してアラートをメール通知仕組みがある) ところが、思ったよりも早くアラートが届いた。 確認してみると、このまま稼働すると1ヶ月で6000円ほどになる事が判明。 しばらくそのまま稼働させていたが、構成とかアプリケーションをもう一度作成しなおしたい事もあったので、 停止を決定。

以下は、11/1~25日までの経過

日別グラフ

f:id:l-chika:20161128233913p:plain

サービスごとの構成比

f:id:l-chika:20161128234100p:plain

ムダな構成だった?

グラフを見るとわかるが、毎日$2ほどの費用がかかっている。 ジュース2本分と考えると安いかもしれないが、チリも積もればでそれなりの金額に。 今の状態で月6000円をかけるのはもったいない。

で、何がかかったかというと、やはりEC2で常に安定的にかかる。踏み台サーバーは利用しない時はstopしていたがElasticIPとは紐づいていたので、それなりにかかっていた。 S3やRDSはデータがまだほとんどなく、アクセスもなかったのでそこまでにはならなかった。

小さく始めるにはELBとか過剰かもしれなかった(Webサーバーは1台)が、SSLACMで利用してみたかのもあるので良い経験になった。

削除するときに気をつけること

それぞれのサービスは削除すれば良いけど、削除を忘れがちなのが - ElasticIPの解放 - AMIを作った場合にはイメージも - EBSのボリューム、スナップショット

今度どうせ作るなら

  • ELBでなくALB・・・http2.0に自動的に対応できるとか熱い
  • MySQL5.7・・・InnoDB全文検索とかJSONカラムとか利用してみる
  • Ruby2.3.3・・・このサービスをつくっているときに安定版が更新された

やってみて

最低限の構成と自分ひとりで作成できた経験は非常に大きかった。 日々の仕事では断片的にしかAWSを触らなかったで、0からつくることで会社で利用している構成への理解も深まった。 やはり自分ひとりで、かつ自分のお金で作成すると頼るところもないので自分事になり、普段の業務とは違う感覚になり真剣度が上がったような気がした。

一旦、 l-chika.com 自体は停止するが、技術的・サービス的な構成を見直して再度チャレンジ。 このブログでは随時、その経過を投稿する。

PPAPのインターフェース-Rubyの場合-

PPAP(ロングバージョン)をRubyで表すと、どうなるか考えてみた。

www.youtube.com

# Pen-Pineapple-Apple-Pen

piko = PikoTaro.new
piko.push(Pen.new)
piko.have?(:pen) # => true

piko.push(Apple.new)
piko.have?(:apple) # => true
ap = piko.pull
ap.done! # => "Apple Pen !"

piko.push(Pen.new)
piko.push(Pineapple.new)
pp = piko.pull
pp.done! # => "Pineapple Pen !"

piko.push(ap)
piko.push(pp)
ppap = piko.pull
ppap.done! # => "Pen-Pineapple-Apple-Pen !"

piko.push(Pen.new)
piko.push(Pen.new)
l_pen = piko.pull
l_pen.done! # => "Long pen !"

piko.push(Apple.new)
piko.push(Pineapple.new)
app = piko.pull
app.done! # => "Apple-Pineapple !"

piko.push(l_pen)
piko.push(app)
ppap = piko.pull
ppap.done! # => "Pen-Pineapple-Apple-Pen !"