elasticbeanstalkのebextensionsでOracleのクライアントをインストールする方法

# tl;dr
* 本記事は検証が十分ではなく、また一つのやり方でありbest wayというわけではないことに留意する
* 本記事はあくまでもクライアントのインストールだけに留めているのでその他必要な設定に関しては省略していることに留意する
* あらかじめS3にrpmファイルを配置しておく
* ebのインスタンスプロファイル（プロフィール）には該当のS3へのRead権限があるIAMロールを指定する
* ebextensionsでS3からダウンロードしてrpm -iの記述をする

---
本記事は[LIFULL Advent Calendar 2017](https://qiita.com/advent-calendar/2017/lifull)の9日目の記事です。
[LIFULL Advent Calendar 2017 その2](https://qiita.com/advent-calendar/2017/lifull2)もよろしくお願いします。

ElasticBeanstalk使ってますか？
（本記事内ではElasticBeanstalkをebと称します)
ebのインスタンスからOracleに接続したい場面ってありませんか？
ただOracleに繋ぐ場合、クライアントをインストールする必要がありますよね。でもそのクライアントってOracleのサイトでログインしてからじゃないとダウンロードできなくてなかなか機械的にはやりづらいところですよね。
ebにはせっかくebextensionsという環境をカスタマイズするための仕組みが導入されているのに、サクッとはできないのが悲しいところ。

本記事ではサクッとできないところを解決する一つの方法を提示します。

# 事前にrpmファイルをS3に配置する
Oracleのサイトから以下のrpmファイルをダウンロードします。
[Linux x86-64](http://www.oracle.com/technetwork/jp/topics/linuxx86-64soft-085244-ja.html)用のページに行きます。デッドリンクになってたらググってください。
ダウンロードページに行ったら以下の3つのファイルをダウンロードします。

- oracle-instantclient12.2-basic-12.2.0.1.0-1.x86_64.rpm
- oracle-instantclient12.2-sqlplus-12.2.0.1.0-1.x86_64.rpm 
- oracle-instantclient12.2-devel-12.2.0.1.0-1.x86_64.rpm

途中のバージョン番号などは変わってる可能性があります。
とにかくbasicとsqlplusとdevelを抑えておけば良いです。
※ガーって書いたけどsqlplus要らなかった説、あります。

ダウンロードできたら上記の3つのファイルをアップロードします。
おそらく規約的に誰でもアクセスできるようなところにアップロードするのはNGなので上げる先としてS3を選択します。

<img width="469" alt="スクリーンショット 2017-12-09 21.51.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/46392/61e9b292-f1a7-3421-525c-de50ed5ad8e6.png">
適当にこんな感じに。（ファイル名が長ったらしいのでリネームしてます）

# IAM Roleの作成
先程アップロードしたファイルのGet*権限を持つIAM Roleを作成します。
面倒なのでここではAmazonS3ReadOnlyAccessポリシーを流用します。
またebのデフォルトで使われる各種ポリシーもアタッチします。
<img width="725" alt="スクリーンショット 2017-12-09 21.56.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/46392/6a90d55a-ff1a-b22e-e73e-9cd5fbc27dd4.png">

# ebextensionsの設定
ebextensionsはebのアプリケーションルートディレクトリに「.ebextensions」ディレクトリを作成し、その下に*.configファイルを置くことでebのデプロイ/インスタンス起動時などに実行してくれる各種設定・コマンド群を記述できる仕組みです。

[ドキュメントにも](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/customize-containers-ec2.html)記載されているとおり以下の設定が使えます。

- packages
  - rpm, yum, rubygems, pythonの順にパッケージ管理ツールを実行できる。Oracleクライアントもyumでインストールできるならこの設定が使えるのに
- groups
  - グループの設定が可能
- users
  - ユーザの設定が可能
- sources
  - 公開URLからアーカイブファイルをダウンロードして任意のディレクトリに配置する。Oracleのzipファイルも認証いらずならこの設定が使えるのに
- files
  - インスタンス内にファイルを作成
- commands
  - アプリケーションとウェブサーバの設定が終わったら実行される。今回はこれを使用
- services
  - インスタンス起動時に開始/終了したいサービスの設定が可能
- container_commands
  - アプリケーションのソースコードが抽出される前に実行される。なのでこの設定の中では一番最後に実行される

ということでcommandsの記述をしていきます。

```01_oracle_install.config
commands:
  01_get_ora_basic:
    command: aws s3 cp s3://sample.oracle-client/oracle-instantclient-basic.rpm /tmp/ora-basic.rpm
  02_get_ora_sqlplus:
    command: aws s3 cp s3://sample.oracle-client/oracle-instantclient-sqlplus.rpm /tmp/ora-sqlplus.rpm
  03_get_ora_devel:
    command: aws s3 cp s3://sample.oracle-client/oracle-instantclient-devel.rpm /tmp/ora-devel.rpm
  04_install_basic:
    command: rpm -i --replacepkgs /tmp/ora-basic.rpm
  05_install_sqlplus:
    command: rpm -i --replacepkgs /tmp/ora-sqlplus.rpm
  06_install_devel:
    command: rpm -i --replacepkgs /tmp/ora-devel.rpm
```

awsコマンドが利用可能なので``aws s3 cp``でアップロードしたファイル群を/tmp以下にダウンロードします。
そしてそれが終わったら``rpm -i --replacepgs``でインストールします。
``--replacepkgs``オプションがない場合、再デプロイ時に「すでにインストールされています」みたいなエラーになってデプロイができなくなったから指定したような気がします。

```02_setupfiles.config
commands:
  01_make_ld_conf:
    command: 'echo "/usr/lib/oracle/12.2/client64/lib" > /etc/ld.so.conf.d/oracle.conf'
  02_ld_config:
    command: 'ldconfig'
  03_make_symlink:
    command: 'sudo ln -sf /usr/include/oracle/12.2/client64 /usr/lib/oracle/12.2/client64/include'
```

commandsではなくfilesでいいのでは？と思うかもしれませんが、filesでやってしまうと先述の通り実行順序はcommandsが後なので``/usr/lib/oracle/``などがまだ存在せずエラーになります。

ちなみに01,02とファイルを分けた理由は単なる可読性のためなので一緒くたでもいいと思います。

```03_setenv.config
option_settings:
  - option_name: NLS_LANG
    value: Japanese_Japan.UTF8
  - option_name: ORACLE_HOME
    value: /usr/lib/oracle/12.2/client64
  - option_name: PATH
    value: "$PATH:/usr/lib/oracle/12.2/client64"
  - option_name: NLS_DATE_FORMAT
    value: "YYYY/MM/DD HH24:MI:SS"
  - option_name: TNS_ADMIN
    value: /usr/lib/oracle/12.2/client64
```
環境変数各種設定です。[options_settings](http://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/environment-configuration-methods-before.html)を使わなくともAWSコンソールの環境プロパティ画面でも設定できたかもしれないです。上記のとfilesと同様にディレクトリが存在しないから、だったかもしれないです。（なのでディレクトリを指定していないものに関してはコンソールの方で良いかも）

ebextensionsの設定はここまで。

## アプリケーションを作る（Rubyで）
サンプルなのでザクっと作ります。

```Gemfile
source 'https://rubygems.org'

gem 'ruby-oci8'
gem 'sequel'
gem 'sinatra', '~> 2.0.0'
```
SQLの便利ライブラリであるsequelとOracleのクライアントであるruby-oci8を指定します。

```config.ru
require './app.rb'
run Sinatra::Application
```
sinatraでアプリケーションを動かす際のお決まりです。

```app.rb
require 'sinatra'

post '/' do
  begin
    status 200
    'success'
  end
end
```
アプリケーションです。

最終的なファイル構成はこのようになります。

```tree
$ tree -a
.
├── .ebextensions
│   ├── 01_oracle_install.config
│   ├── 02_setupfiles.config
│   └── 03_setenv.config
├── Gemfile
├── app.rb
└── config.ru
```

# ebを作成する
抑えるポイントはIAMインスタンスプロファイル（スクショ上ではプロフィール：新しいUIだと名前が変わったみたいですね）にて先ほど作成しておいたIAMロールを選択することです。
<img width="463" alt="スクリーンショット 2017-12-09 22.18.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/46392/d2492b5b-5136-862d-ec13-2492b2be5e3a.png">
※キーペアの名前がちょっとアレな名前だったので黒塗りにしてます。

あとはRubyアプリケーション、テスト用なので単一インスタンス、RDSなし、t2.microなどの設定でアプリケーションを作成します。
t2.microより弱いインスタンスだとgemインストール時にメモリが足りなくてコケた記憶があるので検証時でもt2.micro以上をおすすめします。

数分待つと以下の通り、オールグリーンで作成が完了します。
<img width="991" alt="スクリーンショット 2017-12-09 22.39.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/46392/744b4cb9-7327-9ec3-41b4-2337e6728453.png">

postメソッドで受けるように作っちゃったのでcurlしてみます。
<img width="554" alt="スクリーンショット 2017-12-09 23.06.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/46392/451660c6-1e5d-b03a-e335-ba1ebf4cd498.png">
無事"success"が返ってきてアプリケーションが動作していることが確認できます。

え？Oracleの接続・動作確認してないじゃないかって？
それはまた別の話、ということで…。
クライアントがインストールされていなかったらそもそもruby-oci8のgemのインストールがコケます。
正常に作成できる＝インストールはできている、ということで一つよろしくお願いします。

余談ですがLambdaの場合は@ikeisukeさんの記事が参考になるかもしれません。
[AWS Lambda (Node.js-v4.3.2)からOracleに接続する(ORA-21561への対応)](https://qiita.com/ikeisuke/items/45ba4d88b887d793c444)