[Python]AWSを使ってec2+RDS環境を構築したい

前回の続き↓
[[Python]AWS + Tornadoの環境構築](https://qiita.com/tkap/items/4e84e51d13014e8d9e40)
・AWSのEC2とRDSを使ってインフラ構築
・EC2のOSはAmazon Linux
・RDSのDBMSにはMySQL
・サーバ側はPython3
・DBのORMにはsqlalchemy
・マイグレーションにはalembic
・コーディング規約はpycodestyle(pep8)
こんな感じのチャットアプリを作ろうと思います。

参考
[Python の Alembic を使って RDB のスキーマを管理する](http://momijiame.tumblr.com/post/45191790683/python-%E3%81%AE-alembic-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6-rdb-%E3%81%AE%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%9E%E3%82%92%E7%AE%A1%E7%90%86%E3%81%99%E3%82%8B)
↓やることはほぼこれです。
[Alembicでマイグレーションスクリプトを自動生成する最小限の設定](https://qiita.com/furi/items/f34c400f76179430e6b4)
#環境設定
Alembicを使います
モデルを別で書きたかったので、meta_dataを使う形式にしてみます。

```
pip install alembic
pip freeze | grep alembic
>alembic==0.9.6
```

#migrationの下準備
```
alembic init migrations #migrationsフォルダが作成される
vi alembic.ini 
```

```text:alembic.ini
sql_alchemy.url=DBへのパス
#パスの書き方は後述
```
今回はモデルを別ファイルで定義することにしたので、models.pyをmigrationsディレクトリと同じ階層に作る。

```python:models.py
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class User(Base):
    __tablename__ = 'users'

    id = Column(Integer, primary_key=True)
    name = Column(String(255), nullable=False)

    def __repr__(self):
        return "<User(name='%s')>" % (self.name)
```
migrations/env.pyでこのmodelsをインポートする。

```python:env.py
import models
target_metadata = models.Base.metadata
#target_metadata = None
```
これで下準備は完了なのでmigrationすればいいはず…

```
alembic revision --autogenerate -m "Add account table"
```
するとエラー

```
ERROR [alembic.util.messaging] Target database is not up to date.
  FAILED: Target database is not up to date.
```
stackflowを見て(ここには書いてないけど)、どうやらalembic.iniのscript_locationが間違ってたみたい。エラーですぎていろいろいじっていたからずれてしまっていた。
一応参考↓
[Error when autogenerate migration scripts with alembic](https://groups.google.com/forum/#!topic/sqlalchemy-alembic/6dJSNwkN7Zo)

```text:alembic.ini
script_location = migrations #←作ったフォルダ名
```
これでmigrationしてみる。

```
alembic revision --autogenerate -m "Init tables"
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'users'
```
うまくいったみたい。migrations/versions直下にマイグレーションファイルができている。
あとはalembic upgrade headすればよい！

さっそくするとDBのパスが間違っていることが判明。

```
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2003, "Can't connect to MySQL server on 'localhost' ([Errno 111] Connection refused)")
```
のようなエラーが出てきた。
localhost(この場合ec2インスタンス)からDBに繋げないらしい。
mysql -u root -p -h エンドポイント名　　なら繋げてるのに…

下のURLを見てみたけれどRDSを使わずにMySQLを使っているので関係なさそう。（一応やった）
[EC2にMySQLインストールと設定確認](https://qiita.com/toshihirock/items/c5933d3628c7d4e81ffa)
後から見返したら全く不要な作業だったと思う。

調べてみると、alembic.iniのsqlalchemy.urlが理解できていなかったみたい。
構造は sqlalchemy.url = driver://user:pass@host/dbname

driver: DBMS名+ライブラリ名、何を使ってDBにアクセスするか
user: DBのユーザー名、RDSの設定をしていればインスタンスページに書いてある
pass: RDS設定時に決めたパスワード
host: DBのホスト名、今回はec2内でmysqlを稼働させるのではなく、RDSを使いたいのでRDSのホスト名(つまりエンドポイント)
dbname: 接続するDBの名前、わからなければRDSに直接接続してshow databasesすれば確認できると思う

自分の場合はMySQLを触るためのライブラリを入れていなかったことが判明したのでpymysqlを導入。

```
#v_env下だとpymysqlがなかったので導入
pip install pymysql
```
するとURLは以下のようになる。
sqlalchemy.url = mysql + pymysql://root:(password)@example-rds-mysql-server.fmi3ou2hr9.us-east-2.rds.amazonaws.com/exampledb
これでうまくいった。
もう一度やると、env.pyで使っているmodelモジュールがないとのこと。パスが通ってなかったのでenv.pyを修正。

```python:env.py
#target_metadata = Noneとなっているところを以下に修正
### modelsをインポートするためにパスを追加 ###
import os
import sys
root_dir = os.getcwd()
sys.path.insert(0, root_dir)
### metadata を設定 ### 
import models
target_metadata = models.Base.metadata
```
うまくいくか…

```
alembic upgrade head
INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> e76a42708e85, Init tables
```
ようやくRDSにテーブルを作ることができた！migrations/versionsにあるmigrationスクリプトも自動生成されててalembicすごい。

#まとめ
やったこととしては、
①alembicの導入、pymysqlの導入
②alembic initでフォルダ作成
③alembic.iniでDBのパスとscript_locationを修正
④env.pyでmodelsモジュールをインポート
⑤migrationファイルをautogenerateして、upgrade head
になります。


長くなってしまったので続きは別記事で。