おふくろさまより愛をこめて

mmmpa ふとしです。誠実なプログラミングを心がけたい。

Recent entries

IBM Bluemix (IBM Cloud - Cloud Foundry) をローカルで動作するように環境を準備する

Bluemix は IBM が提供する PaaS で、Heroku に似た仕組みを持つ Cloud Foundry をベースとして動作しています。専用の cli を使ってコードを push すると、コードにあわせて buildpack が選択され、コードに合わせた環境が準備されて自動的にアプリが起動します。

push から動作までが非常に速やかで簡潔な反面、環境がすべて自動で準備されているため、動作における不具合の調査とその調整が少ししづらい印象があります。幸い、Croud Foundry にはローカルで動作させるための PCF Dev (P は開発元であった Pivotal 社の頭文字) という plugin が用意されていますので、それを今回はインストールしました。

OS は Debian stretch で、手順は Introduction | Try PCF on your Local Workstation | Pivotal に従っています。

(なお windows と mac では cloudfoundry-incubator/cfdev: A fast and easy local Cloud Foundry experience on native hypervisors, powered by LinuxKit with VPNKit で簡単に準備できようです)

インストール

cf-cli のインストール

PCF Dev plugin は cf-cli 上で動作します。また、動作している Cloud Foundry へアクセスするためにも必要になるので、いずれにしてもインストールが必要になります。

# apt-get の https アクセスなどに必要なパッケージをインストール
sudo apt-get update
sudo apt-get install -y wget gnupg gnupg1 gnupg2 apt-transport-https

wget -q -O - https://packages.cloudfoundry.org/debian/cli.cloudfoundry.org.key | sudo apt-key add -
echo "deb https://packages.cloudfoundry.org/debian stable main" | sudo tee /etc/apt/sources.list.d/cloudfoundry-cli.list
sudo apt-get update
sudo apt-get install cf-cli

VirtualBox をインストール

仮想環境として内部的に VirtualBox を使用しています。VirtualBox から環境に合わせたパッケージをダウンロードしてインストールします。

素の Debian stretch だと多くの依存パッケージが足らず virtualbox-6.0 depends on Foo; however: Package Foo is not installed. といったエラーメッセージが複数行にわたって表示されます。したがって依存パッケージを先にインストールしなければなりません。

# 依存パッケージのインストール
sudo apt-get install -y libcurl3 libdevmapper1.02.1 libgl1-mesa-glx libgl1 libopus0 libpng16-16 libqt5core5a libqt5gui5 libqt5opengl5 libqt5printsupport5 libqt5widgets5 libqt5x11extras5 libsdl1.2debian libvpx4 libx11-6 libxcb1 libxcursor1 libxext6 libxml2 libxmu6 libxt6 psmisc 

sudo dpkg -i virtualbox-6.0_6.0.0-127566~Debian~stretch_amd64.deb

PCF Dev をインストール

Download PCF Dev — Pivotal Network からダウンロードします。ダウンロードにはアカウントが必要なので作成します。

plugin のインストールは cf-cli に対して行うので以下のようになります。

cf install-plugin ./pcfdev-v0.30.0+PCF1.11.0-linux

補記

チュートリアルのように $ ./pcfdev-v0.30.0+PCF1.11.0-linux とした場合は Your cf CLI version is too old. Please install the latest cf CLI. とエラーになりインストールできませんでした。

起動

以下のコマンドを実行するだけです。

cf dev start

ただし初回はまず仮想環境イメージのダウンロードで時間がかかり、2 回目以降も起動自体にものすごい時間がかかります。それを知らないと固まったような印象を受けますが、気長に待つのが肝要です。

起動時ログ例

$ cf dev start

Less than 4096 MB of free memory detected, continue (y/N): > y
Please sign in with your Pivotal Network account.
Need an account? Join Pivotal Network: https://network.pivotal.io

Email> 

Password> 
Downloading VM...
Progress: |====================>| 100%  # まず仮想環境イメージのダウンロードが長い
VM downloaded.
Allocating 4096 MB out of 32140 MB total system memory (1371 MB free).
Importing VM...
Starting VM...
Provisioning VM...
Waiting for services to start... # 2 回目以降もここからの起動が長い
7 out of 58 running
7 out of 58 running
7 out of 58 running
7 out of 58 running
40 out of 58 running
56 out of 58 running
58 out of 58 running            # 気長に待ちましょう

 _______  _______  _______    ______   _______  __   __
|       ||       ||       |  |      | |       ||  | |  |
|    _  ||       ||    ___|  |  _    ||    ___||  |_|  |
|   |_| ||       ||   |___   | | |   ||   |___ |       |
|    ___||      _||    ___|  | |_|   ||    ___||       |
|   |    |     |_ |   |      |       ||   |___  |     |
|___|    |_______||___|      |______| |_______|  |___|
is now running.
To begin using PCF Dev, please run:
   cf login -a https://api.local.pcfdev.io --skip-ssl-validation
Apps Manager URL: https://apps.local.pcfdev.io
Admin user => Email: admin / Password: admin
Regular user => Email: user / Password: pass

アクセス

cf-cli

以下のコマンドで、cf-cli のアクセス先を立ち上がった PCF Dev に設定します。なお、*.local.pcfdev.io192.168.11.11 にポイントされているので、きちんとローカルの Cloud Foundry にアクセスすることになります。

cf login -a api.local.pcfdev.io --skip-ssl-validation

EmailPassword が要求されるので、起動時のログに表示されているものを使用します。

Regular user => Email: user / Password: pass

GUI

ブラウザからは https://apps.local.pcfdev.io でコンソールにアクセスできます。EmailPassword はおなじく起動時ログで出てきたものを使用できます。

PCF Dev の動作確認

単純な ruby を起動してみましょう。Cloud Foundry ではなく Bluemix から配給されているものですが IBM-Cloud/ruby-sinatra-helloworld: Sample Ruby application for Bluemix which uses the Sinatra framework. が簡単に起動します。

git clone https://github.com/IBM-Cloud/ruby-sinatra-helloworld
cd ruby-sinatra-helloworld
cf push

問題がなければ以下のようなログが最終的に出力されます。経路 に示された sample-ruby-sinatra-brave-okapi.local.pcfdev.io にアクセスして動作を確認しましょう。

$ cf push
user としてマニフェストから組織 pcfdev-org / スペース pcfdev-space にプッシュしています...

# build のログが続く

名前:                   sample-ruby-sinatra
要求された状態:         started
インスタンス:           1/1
使用:                   256M x 1 インスタンス
経路:                   sample-ruby-sinatra-brave-okapi.local.pcfdev.io
最終アップロード日時:   Mon 31 Dec 07:39:34 JST 2018
スタック:               cflinuxfs2
ビルドパック:           ruby 1.6.37
開始コマンド:           bundle exec rackup config.ru -p $PORT

     状態   開始日時               cpu    メモリー       ディスク       詳細
#0   実行   2018-12-30T22:39:58Z   0.0%   456K of 256M   6.7M of 512M 

まとめ

これで Cloud Foundry をローカルで動作できるようになりました。

実は IBM Bluemix に乗せる平易なアプリケーションを作成していて、メモリを 64MB に設定すると落ちてしまう事象がありました。128MB にすると落ちるときと落ちない時があり、それのチューニングをする or チューニングの問題ではないのか調査するにあたって都度 push したのでは追いつかなくなってしまいました。

幸いローカル環境構築はきちんと提供されていたので大いに助かりました。 Pivotal 社及び「ローカル環境あるよ」 と教えてくださった Toshiaki Maki(@making)さん には感謝の言葉しかありません。

hosts でサブドメインをワイルドカードで指定したいができないので dnsmasq で対応する。

サブドメインをなんらかの key としたサービスがあります。その開発においてはローカルの開発環境においてもサブドメインを付与したアクセスが必要になります。しかしいわゆる /etc/hosts にあたるファイルではワイルドカードを指定できないため、key ごとに書き加えていく必要があります。

127.0.0.1       foo.example.com
127.0.0.1       bar.example.com
127.0.0.1       *.example.com # 機能しない

しかし、これは明らかに手間です。そこでローカルマシン上で DNS としての機能を提供する dnsmasq を用いて *.example.com へのアクセスを 127.0.0.1 にリダイレクトさせることで対応できます。

/etc/dnsmasq.conf

インストールは各環境に合わせて行い、設定ファイルにリダイレクト設定を書きます。

記法は単純で address=/a/b で a にマッチするアクセスがあった場合に b へリダイレクトします。これは必要なリダイレクト分だけ書けます。

address=/.example.com/192.168.0.1
address=/.o296.com/192.168.0.1

詰まりどころ

以下はわたしが dnsmasq を設定するにあたって詰まった部分です。比較的長い時間を溶かしたので、同じような状況に陥ったどなたかの助けになれれば幸いです。

Operation not permitted

起動時に以下のエラーが出る場合は /etc/dnsmasq.confuser=root を書き加えます。

dnsmasq: setting capabilities failed: Operation not permitted

リダイレクトされない

dnsmasq は DNS として振る舞います。どの DNS でドメインを解決するかというのは /etc/resolv.conf 書かれていて、大体の場合以下のようになっているようです。

nameserver 192.168.0.1
nameserver 2001:268:fd07:4::1
nameserver 2001:268:fd08:4::1

192.168.0.1 は自分自身に向いていることも多いのですが、わたしの場合はマシンの ip は 192.168.0.6 でした。これは確実に自分に向くように 127.0.0.1 に変更します。

nameserver 127.0.0.1
nameserver 2001:268:fd07:4::1
nameserver 2001:268:fd08:4::1

書きかえた /etc/resolv.conf がもとに戻ってしまう

NetworkManager が動作している場合 recolv.conf は再起動のたびに上書きされてしまいます。書き換えを防止するために、以下のように追記します。

[main]
plugins=ifupdown,keyfile
dns=none  # 追加 

まとめ

これで無限にサブドメインアクセスができるようになりました。

業務でちょうどサブドメインを key とするサービスの作業を担当していたのでもっと早く dnsmasq を知れれば、都度 /etc/hosts を編集することもなかったのにな……と思いました。(その業務からは年内で担当をはずれました)

その他の手段

適当なドメインを所有している場合は *.local.o296.com などを 127.0.0.1 にポイントしておくという手もあります。サブドメインを使用するサービスでローカル開発環境を提供しているような場合、その会社がそういうローカル用のドメインを用意している場合もあるようです。

今年の洗濯回数は 8 回でした。

過去録

1 回目 2 月

2 回目 3 月

3 回目 5 月

4 回目 6 月

5 回目 7 月

6 回目 9 月

7 回目 11 月

8 回目 12 月

まとめ

今年は大規模な入れ替えもなかったので安定した洗濯生活でした。ただ、あたらしいボクブリを結構おろしたせいか、後半戦にはボクブリはあるのに T シャツの在庫がきれるという逆転現象が起こっていました。

来年は 6 億円を当てて自宅に 30kg 級の乾燥機を設置したいですね。

Ansible で rbenv をインストールする role を書いた

インフラ構成をできるだけ属人性を低くしつつ再現性を高くしたくて Ansible を触っています。Ansible は知識がない状態だと設定ファイルについて全くもって全景がとらえにくい === とてもとっかかりづらいという問題があります。しかし、そこを過ぎた処理設定自体はものすごく平易に書けるので素敵なツールだと感じています。

そこで、Ansible を使い慣れるのも兼ねて、rbenv がすでにある場合や ruby-version がすでにインストールされている場合はスキップ、ない場合は rbenv の update や ruby-version のインストールを行う role を書いてみました。

わたしの従来の rbenv インストール

rbenv について、従来は以下のような粗雑な shell ファイルを用意しインストールを行っていました。そしてバージョンの update が必要な場合は ssh で接続し、ruby-build のバージョンを上げ、ビルドしなおすという運用をしていました。

sudo yum -y install bzip2 gcc* openssl-devel readline-devel zlib-devel

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv && src/configure && make -C src

echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

~/.rbenv/bin/rbenv install 2.5.3

rbenv/tasks/main.yml

上記 shell ファイルを単純にそのまま Ansible role に書き下すのは、shell モジュールを用いて置き換えればいいだけなので非常に簡単です。しかしこれを何度も行った場合は少し問題があります。rbenv は都度ビルドされ、export PATH="$HOME/.rbenv/bin:$PATH" がどんどん書き足されていってしまいます。

そこで Check rbenv Check installed ruby version といった tasks で状態を把握して処理を切り替えます。

- name: Check rbenv
  shell: ~/.rbenv/bin/rbenv --version
  register: rbenv_exists
  changed_when: False
  ignore_errors: yes

- name: Check installed ruby version
  shell: ~/.rbenv/bin/rbenv versions | grep {{ ruby_version }}
  register: has_ruby_version
  changed_when: False
  ignore_errors: yes

- name: Check ruby version
  shell: ~/.rbenv/bin/rbenv version | grep {{ ruby_version }}
  register: is_ruby_version
  changed_when: False
  ignore_errors: yes

- name: Install ruby requiring packages
  yum:
    name:
      - bzip2
      - gcc*
      - openssl-devel
      - readline-devel
      - zlib-devel
  become: yes
  become_method: sudo
  when: has_ruby_version is failed

# install rbenv

- git:
    repo: https://github.com/rbenv/rbenv.git
    dest: ~/.rbenv
  when: rbenv_exists is failed

- git:
    repo: https://github.com/rbenv/ruby-build.git
    dest: ~/.rbenv/plugins/ruby-build
  when: rbenv_exists is failed

- name: Update rbenv
  shell: git pull
  args:
    chdir: ~/.rbenv
  when: has_ruby_version is failed

- name: Update ruby-build
  shell: git pull
  args:
    chdir: ~/.rbenv/plugins/ruby-build
  when: has_ruby_version is failed

- name: Make rbenv
  shell: src/configure && make -C src
  args:
    chdir: ~/.rbenv
  when: has_ruby_version is failed

- name: Register rbenv
  shell: echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
  when: rbenv_exists is failed

- name: Register rbenv
  shell: echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
  when: rbenv_exists is failed

# install ruby

- name: Install Ruby
  command: ~/.rbenv/bin/rbenv install {{ ruby_version }}
  when: has_ruby_version is failed

- name: Set default ruby version
  shell: ~/.rbenv/bin/rbenv global {{ ruby_version }}
  when: is_ruby_version is failed

まとめ

register changed_when ignore_errors でフラグを設定し when で切り替えるという単純な組み合わせで十分な role を書くことができました。ruby-version のダウングレードについても一瞬で終わるので便利ですね。

Amazon linux 2 docker image を ec2 で使われているものと似た感じにする

Amazon linux は AWS で使われている Cent OS をベースとした Amazon 謹製の linux ディストリビューションです。おおむね Cent OS と同じですが、Amazon 専用パッケージがインストールされていたり、Amazon linux 特有のコマンドでしかインストールできないパッケージなどがあり、微妙な差異があります。Ansible や Capistrano などの構成ツールの設定をする際には、その差異が問題になります。

とはいえ、構成ツールの初期調整の段階で EC2 インスタンスを立ち上げるのもおおげさです。そこで Docker の出番となるわけですが、配給されている Amazon linux イメージ amazonlinux:2 は EC2 では使用できる基本的なパッケージも含まれておらず、ものすごくミニマルな構成になっています。(これはおそらく ECS での使用を想定されているせいです)

そこで、EC2 のイメージには入っていると思われるパッケージをインストールし、Ansible でのインフラ構成のテスト環境とします。と言っても、Dockerfile で必要なパッケージをインストールし、普通の SSH でアクセスできるようにするだけなのでむずかしいことはありません。

公開鍵と秘密鍵

公開鍵と秘密鍵は前もって準備しておきます。ローカルでしか使わないので雑でかまわないでしょう。

ssh-keygen -t rsa

Dockerfile

FROM amazonlinux:2

# `shadow-utils` `procps` には基本的なコマンドが多く含まれています
RUN yum install -y sudo shadow-utils procps wget
RUN yum install -y openssh-server openssh-clients

# easy_install を使うためです
RUN wget https://bootstrap.pypa.io/ez_setup.py -O - | sudo python

# SSH の設定
RUN systemctl enable sshd.service
RUN echo "PasswordAuthentication no" >> /etc/ssh/sshd_config
COPY ./id_rsa.pub /home/ec2-user/.ssh/id_rsa.pub
RUN cat /home/ec2-user/.ssh/id_rsa.pub >> /home/ec2-user/.ssh/authorized_keys

# no password で sudo を可能にします
RUN useradd ec2-user
RUN echo "ec2-user ALL=NOPASSWD: ALL" >> /etc/sudoers

CMD /sbin/init

足りないパッケージがある場合は一つ一つ調べるのではなく、EC2 で立ちあげたインスタンスで yum list installed を行い、出たパッケージをすべてインストールしてしまうという手もあります。

docker-compose.yml

version: '3'
services:
  web:
    build: .
    tty: true
    privileged: true
    ports:
      - "2222:22"

起動と接続

docker-compose up --build -d
ssh ec2-user@localhost -p 2222 -i id_rsa_for_docker