Elastic BeanstalkでDjango - チュートリアル

# 注意点

※awsアカウントはある前提です。 rootじゃ危ないので、eb用のiamアカウントでやろう

# eb cliをインストール

いろいろ方法がある。

※aws cliはいらない。俺も最初はいると思ったけど、いらない。むしろ、普通にいれたら競合すると思う。

### EB CLI セットアップスクリプト

[https://github.com/aws/aws-elastic-beanstalk-cli-setup](https://github.com/aws/aws-elastic-beanstalk-cli-setup)

aws cliとeb cliの競合を回避できるのと、管理が楽らしい。　[原文](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.html)　[AWS推奨](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/eb-cli3-install.html)  
aws cliとeb cliが競合するって変な話だよね。

> _5.1. 経験豊富な Python 開発者にとって、virtualenv 内での通常の pip でのインストールではなく、このモードでのインストールの利点は何でしょうか？_  
> _virtualenv内であっても、開発者は依存関係が衝突する複数のパッケージをインストールする必要があるかもしれません。例えば、AWS CLI と EB CLI が競合するバージョンの botocore を使用していることがあります。そのような例は特にひどかった。コンフリクトがある場合、ユーザーはコンフリクトしているパッケージごとに別々のvirtualenvを管理するか、コンフリクトしていないパッケージの組み合わせを見つけなければならない。_
> 
> _これらの回避策はどちらも、時間が経つにつれて、またコンフリクトしているパッケージの数が増えるにつれて、管理できなくなる。 -Deepl翻訳_

### pip

俺はこっちを使った。セットアップスクリプトはむずそうだったから。 aws cliを使ってなかったから。競合もしないし。  
  
もし、セットアップスクリプトを使いたくなくて、かつaws cliと競合してる場合、競合してる奴を一回全部アンインストールして、eb cliを入れたら一応いける。

```python
pip install awsebcli --upgrade --user

```

# セットアップ

プロジェクトのディレクトリでこうする。

```plaintext
eb init
```

と、いろいろ聞かれる。

*   たしか、最初はアカウントの認証。キーを求められるから、入力しよう。シークレットキーは発行しなきゃいけない。
*   リージョン　場所
*   アプリ名　僕の場合、infolinkです。アプリと環境っていうのがあるんですけど、環境はinfolink-dev(devは開発環境みたいな意味)みたいな開発環境とか本番環境を分けるための概念。
*   python使ってる？
*   プラットフォームを選んで。Python 3.11 running on 64bit Amazon Linux 2023でいいと思う。最新
*   codecommitを使う？　軽く調べたところ、codecommitリポジトリを使ってデプロイすると差分のみになるから通信コスト削減になるらしい。今のところ、無料枠のおかげでコストかかってないから、俺はいいかな。
*   sshをインスタンスにセットアップする？　どっちでもいい。俺は、使ってもあまり解決にならなかった。でも、一応作っておいたらいいかも。あと、キーペアとパスフレーズ(パスワードみたいなやつ。覚えておこう)を設定

話変わるけど、プログラミングするならDeeplはマジで入れといたほうがいい。選択してctrl+ccですぐ翻訳できるし。デスクトップアプリのほうが、ブラウザ以外も使えて良い。

### ユーザー権限

*   AdministratorAccess-AWSElasticBeanstalk これだけでいけると思ったけど、無理だった。なので、下のも追加した。
*   AmazonEC2FullAccess
*   AmazonRDSFullAccess
*   AmazonS3FullAccess

### 設定

プロジェクトディレクトリの下にrequirements.txtを置くとインストールされる。postgreを使うから、psycopg2-binaryは必須。gunicornはデフォルトで入るからいらない

同じように.ebextensionsディレクトリを作る。そして、.configファイルを作って置く。あと、たしか、.の前に好きな文字を入れれる。俺もdjango.configになってるし。

.configファイル内

```plaintext
option_settings:
  aws:elasticbeanstalk:container:python:
    WSGIPath: プロジェクトのディレクトリ名.wsgi:application #　ほかの場所に置いてたら変えなきゃかも。まあ、変えんか

  aws:elbv2:listener:443:
    ListenerEnabled: 'true'
    Protocol: HTTPS
    SSLCertificateArns: arn:aws:acm:....あなたの証明書のid。acmに証明書を登録しよう。

  aws:elasticbeanstalk:environment:proxy:staticfiles:
    /static: static

container_commands:
  01_collectstatic:
    command: "source /var/app/venv/*/bin/activate && python3 manage.py collectstatic --noinput"
  02_migrate:
    command: "source /var/app/venv/*/bin/activate && python3 manage.py migrate --noinput"
    leader_only: true

```

leader\_onlyはその名の通り、リーダーだけが実行する。migrateは一回でデータベースに適応されるのでいい。  
collectstaticはそれぞれのインスタンスでしなきゃいけないからfalse。

俺がcloudflareでhttps化した時の方法は、ドメインを取得して(移行でもいい) -> ssl/tlsに行き、エッジ証明書に行き、常にhttpsやhstsなどをオンにする。(ここら辺はヘルプを読んで、自分で判断して)　そして、概要でフル(厳密)を選択。

ちなみに、フレキシブルにすれば、クライアントとcloudflare間のみtls化される。フルにするとend2end。厳密はヘルプを見て。正直ヘルプが便利すぎるので、それを見たほうがいい。

end2endを使う場合、オリジンサーバに行って証明書を作らなきゃいけない。そして、作る時に出てくる二つの値(プライベートキーともう一つあったけど、忘れた)を使って、acmにインポートすれば終わり。acmのidみたいなのは探してください。

cnameを設定するのは必要だけど、これは後で解説。本番ドメインは完成してから使えるようにしよう

# 環境を作る

```plaintext
eb create
```

また色々聞かれる

*   環境名 -devとかで
*   dns cname prefix **ここの名前**.region-name.elasticbeanstalk.com　環境名でいいかも
*   load balancer アプリケーションで
*   Spot Fleet requests　スポットインスタンスという未使用のリソースを使うかを聞いてる。安い代わりに可用性が低い。止まる可能性がある。価格は需要と供給らしい。Yを押すと、インスタンスのサイズを聞かれた気がする。

インスタンスのタイプ聞かれたかもしれん。 2つ以上書いたほうがいい(どっちかがもう使えなくなったときようだと思う)から、t3.micro,t3.smallとかで。

# djangoの設定

```plaintext
DEFAULT_HTTP_PROTOCOL = "https" #これ、allauthの設定かdjangoの設定かわからん。一応入れといて。

#こうすると、DEBUG=Trueでデプロイしちゃったときエラーになって削除される。immutableデプロイの場合
if not DEBUG:
    ALLOWED_HOSTS = ["infolink.dev"]

    CSRF_TRUSTED_ORIGINS = [
        "https://infolink.dev",
    ]
DEBUG = env.bool('DEBUG') #これはdjango-environをつかっているだけです

if not DEBUG:
    if 'RDS_DB_NAME' in os.environ:
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.postgresql_psycopg2',
                'NAME': os.environ['RDS_DB_NAME'],
                'USER': os.environ['RDS_USERNAME'],
                'PASSWORD': os.environ['RDS_PASSWORD'],
                'HOST': os.environ['RDS_HOSTNAME'],
                'PORT': os.environ['RDS_PORT'],
            }
        }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
```

ebが返すドメインから見たい場合、そのドメインも追加。たしか、createした時のログにある。なければ、コンソールから。当たり前だけど、そのドメインの場合、前の証明書は使えないよ。

# デプロイ

```plaintext
eb deploy 環境名
```

エラーが出たら、コンソールからログをダウンロードして読む。多分コマンドのエラーが原因だと思う(cmdがついてるファイルを見よう)。

ヘルスチェック応じたデプロイ方法を使う場合は、先に、コンソールから設定にいって一番下の更新、モニタリング、ログの編集を押して、ヘルスチェックの合格ラインを重大に下げないとデプロイできないかも。俺の場合そうだった。

なぜか、4xx 100%になってsevereになるんだよね。[わかりやすい、デプロイ方法の解説記事](https://dev.classmethod.jp/articles/elastic-beanstalk-deploy-policy/)

# RDS

コンソールからrdsを追加。以上。postgreを選ぼう。　djangoの設定の奴を使えば環境変数から勝手に取得してくれるからok

# プライベートサブネットはいらないと思う

natゲートウェイが高いからプライベートサブネットは使いたくない。

それに、デフォルトのvpc構成で問題ないと思う。デフォルトでも、セキュリティグループを使ってec2はロードバランサーのセキュリティグループからのみ受けつけたり、rdsはec2インスタンスのセキュリティグループからのみ受け付けるようになってる。

「セキュリティグループ」と、awsで検索したら、その項目が出てくるから見てみて。

スタックオーバーフローの回答にもセキュリティグループがしっかりしてればいいと書いてあったし。仕組み的に問題はないと自分でも思った。

# CNAMEを設定

cloudflareでdnsの項目を探して、レコードの追加をする。見ればわかる。 cloudflareのナビゲーション、複雑だから忘れた。とりあえず、cnameレコードを追加して、ebの環境のドメインと紐づければいい。

ついでだけど、ドメインのルートは普通、CNAMEにできないらしい。フラット化というcloudflareの技術？を使ってできるらしい。まあ、調べれば公式の出てくるから見てみて。

# 終わり

たぶん、成功してる？書き忘れがなければ成功してるだろう。infolinkも同じ方法でやったから。

結構調べるの大変だったんだよなぁー。英語ばっかだし、謎のエラー出るし。Elastic Beanstalkの日本語記事では、トップレベルなのではないか？笑　初めて、自己満じゃない記事書けたかも。

参考にしたサイト。

[https://testdriven.io/blog/django-elastic-beanstalk/](https://testdriven.io/blog/django-elastic-beanstalk/#project-setup)　  
英語だけど、すごくいいです。この記事で失敗した時は、これが役に立つと思う。でも、

```plaintext
aws:elasticbeanstalk:environment:proxy:staticfiles:
   /static: static
```

これがないんだよなあ。俺はこれがないとstaticファイルがうまくいかなかった。ほかが完全に同じってわけじゃないし、今ちょっと見ただけだから間違ってるかはわからないけど。