本記事でのデプロイ環境の完成図
EC2を使用する際の注意点
- 2022/5/2現在 1〜4までの工程が簡略化できるようになっている
作成するリソース | 名前タグの自動生成 |
---|---|
VPCなど | 任意(ここではfp) |
他の設定は任意。
本記事では上記の2点のみ変更して作成
- 2021/9/30現在 EC2インスタンス(8GB)を4つ作成した時点で無料枠(30GB)を超えるので課金対象になる
- 2つのインスタンスが1ヶ月フル稼働をした時点で課金対象になる
- また稼働していないインスタンスに対してElasticIPが有効になっている場合も課金対象になる
- 2021年10月26日時点ではRDSの無料枠にPostgresQLがなかったのでMySQLを使用しています。(DjangoはPostgresQLの使用を推奨しています。)
firebaseでプロジェクトを作成し、Firebase Hosting の設定通りに進める
フロントエンド側
buildする
$ npm run build
firebase CLIのインストール
$ npm install -g firebase-tools
$ firebase login
$ firebase projects:list
上記のコマンドでログインとプロジェクト一覧を確認できればOK
firebase公式ドキュメント
firebaseプロジェクトの初期化
$ firebase init hosting
以降は下記の内容で選択
Use an existing project
firebaseで作成したプロジェクトを選択
? What do you want to use as your public directory? (public)build
? Configure as a single-page app (rewrite all urls to /index.html)? (y/N) y
? Set up automatic builds and deploys with GitHub? Y
? File build/index.html already exists. Overwrite? (y/N) N
? For which GitHub repository would you like to set up a GitHub workflow? (fo
rmat: user/repository) enuii3/portfolder_api
? Set up the workflow to run a build script before every deploy? (y/N) y
? What script should be run before every deploy? npm ci && npm run build
? Set up automatic deployment to your site's live channel when a PR is merged
? (Y/n) Y
? What is the name of the GitHub branch associated with your site's live chan
nel? master
環境変数を再設定する
後日作成内容
create-react-appで独自の環境変数を読み込む
deployする
$ firebase deploy
バックエンド側
デプロイ用ライブラリの導入
$ pip install django-environ
$ pip instlal dj-database-url
- 環境変数自体は.env(jira_api直下に新規作成する必要がある)に記述し、django-environは環境変数を簡単に扱える様にする
- dj-dabase-urlはデーターベースを簡単に扱える様にする
# 下記の2行を追加
import os
import environ
# BASE_DIRの定義箇所以下に以下の2行を追加
env = environ.Env()
env.read_env(os.path.join(BASE_DIR, '.env'))
# 以下省略
# 以下の3項目をそれぞれ編集
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG')
DATABASES = {
'default': env.db(),
}
CORS_ORIGIN_WHITELIST = env.list('CORS_ORIGIN_WHITELIST')
STATIC_URL = '/static/'
# 下記の1行を追加
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
- .envファイルの読み込ませるためにsettings.pyに編集
- djangoには複数のstaticファイル(adminダッシュボードなど)があり、 djangoアプリのデプロイ時にはcollect staticコマンドを実行して、散らばったstaticファイルを一つのstaticフォルダ(新規作成する必要がある)に統合する必要があるのでプロジェクトディレクトリ直下に空のstaticディレクトリを作成する
SECRET_KEY=settings.pyで設定されていたシングルクォートの中身
DEBUG=False
DATABASE_URL=sqlite:///db.sqlite3
CORS_ORIGIN_WHITELIST=http://localhost:3000
$ pip freeze > requirements-dev.txt
- 開発時にimportしたライブラリを
pip freeze > requirements-dev.txt
コマンドで読み取って依存関係を吐き出す
-r requirements-dev.txt
gunicorn
psycopg2
- 本番環境用にrequirements.txtを作成し、
requirements-dev.txt
を読み込むのと、本番環境でgunicornとPostgreSQLを使用するので上記の2行を追加
以下AWS操作
1. VPCの作成
名前タグ | CIDRブロック |
---|---|
portfolder-vpc | 10.0.0.0/16 |
2. サブネットの作成
VPC ID | サブネット名 | アベイラビリティーゾーン | CIDRブロック |
---|---|---|---|
portfolder-vpc | pf-public-sn1 | ap-notheast-1a | 10.0.1.0/24 |
portfolder-vpc | pf-public-sn2 | ap-notheast-1d | 10.0.2.0/24 |
portfolder-vpc | pf-private-sn1 | ap-notheast-1a | 10.0.3.0/24 |
portfolder-vpc | pf-private-sn1 | ap-notheast-1d | 10.0.4.0/24 |
3. インターネットゲートウェイを作成し、アタッチする
名前タグ |
---|
pf-igw |
4 ルートテーブルの作成
4.1 ルートテーブルに下記のルートの追加
送信先 | ターゲット |
---|---|
0.0.0.0/0 | pf-igw |
4.2 サブネットの関連付けタブから2つのパブリックサブネットの関連付け
5. EC2インスタンスの作成
異なるアベイラビリティーゾーンのEC2インスタンスを2つ作成
名前 | アプリケーションおよびOSイメージ | キーペア | VPC | サブネット |
---|---|---|---|---|
任意(本記事ではfp-ec2-1a) | Ubuntu | 任意(推奨) | fp-vpc | アベイラビリティーゾーン1a(もしくは1c)のpublicサブネット |
パブリックIPの自動割り当て | セキュリティーグループ | セキュリティーグループ名 | インバウンドセキュリティールール |
---|---|---|---|
有効化 | 作成 | 任意(本記事ではfp-public-sg) | ルールは以下のスクショを参照 |
6. ElasticIPアドレスを紐付け
ネットワークボーダーグループ |
---|
任意(本記事ではデフォルトのap-northeast-1) |
作成完了後は一覧画面に戻り、アクション内のElastic IPアドレスの関連付けを選択し、先ほど作成したEC2インスタンスに紐付け(紐付け完了後は)
デフォルトでは起動する度にインスタンスのIPアドレスは変更されるが、こうする事によって、IPアドレスを固定化できる
6. RDSの作成
6.1 データベース(MySQL)の作成
データベースの作成時の選択項目
DB作成方法 | エンジンオプション | テンプレート | マスターユーザー | マスターパスワード |
---|---|---|---|---|
標準作成 | MySQL | 無料利用枠 | admin | 任意 |
コンピューティングリソース | EC2インスタンス | ネットワークタイプ | VPC | サブネットグループ |
---|---|---|---|---|
EC2 コンピューティングリソースに接続 | 作成済のもの | IPv4 | 自動選択 | 自動選択 |
VPC SG | 新しいSG名 | AZ |
---|---|---|
一旦defaultを選択し、後に自動生成されたec2-rds-1に変更 | pf-db-sg | 自動選択 |
作成が完了すると、データベースとサブネットグループ(1a, 1c, 1dのそれぞれ異なるアベイラビリティーゾーン毎のサブネットと付随するルートテーブルなど)が作成される
6.2 サブネットグループの作成
(RDS作成時に自動生成されるようになってました。 一応下記は残しておきます。)
名前 | 説明 | VPC | アベイラビリティーゾーン | マスターユーザー | サブネット |
---|---|---|---|---|---|
fp-db-sngroup(任意) | 何かしらの入力が必須 | friends-phrase-vpc | ap-notheast-1aと1c | admin | 1aと1c一つずつ |
7. Route53にてホストゾーンの作成
8. お名前.comにてネームサーバーの設定
設定ができたかの確認
#Linux or Mac
$ dig NS ドメイン名
# windows
$ nslookup
> set type=ns
> ドメイン名
9. ACMにてSSL証明書を発行
ドメイン名 |
---|
任意のドメイン名 |
9.1 証明書を選択し、Route53でレコードを作成ボタンを押下し、レコード作成
9.2 Route53にて作成したホストゾーンにCNAMEレコードの登録
10. ターゲットグループを作成
インスタンスを選択し、Include as pending belowボタンを押下
ターゲットタイプ | ターゲットグループ名 | VPC |
---|---|---|
Instance | pf-target-group | porfolder-vpc |
11. Application LoadBalancerの作成
ロードバランサー名 | VPC | マッピング | セキュリティーグループ | HTTPリスナーのデフォルトアクション |
---|---|---|---|---|
pf-loadbalancer | portfolder-vpc | pf-public-sn1, pf-public-sn2 | pf-db-sg | pf-targetgroup |
11.1 既存のHTTPリスナーのルールを編集
11.2 HTTPSリスナーの追加
プロトコル | ポート | デフォルトアクション | デフォルトSSL |
---|---|---|---|
HTTPS | 443 | Foward to(pf-target-group) | 作成したACM |
12. route53にて、トラフィック先をALBにしたAレコードの作成
13. 本番環境を構築
14.1 sshでEC2にアクセス
コマンドは作成したEC2インスタンスの接続ボタンから確認できる
ちなみに
Host *
ServerAliveInterval 60
TCPKeepAlive yes
IPQoS=throughput
上記の設定はubuntuに接続時に強制ログアウトになるのを防ぐ
13.2 各ライブラリ(データベースクライアント、gunicorn, nginxなど)をインストール
//EC2インスタンスに各ライブラリを導入
ubuntu@ip-00-0-0-000:~$ sudo apt-get update
ubuntu@ip-00-0-0-000:~$ sudo apt-get install python3-pip python3-dev mysql-server libmysqlclient-dev nginx
13.3 仮想環境の構築
ubuntu@ip-00-0-0-000:~$ sudo -H pip3 install --upgrade pip
ubuntu@ip-00-0-0-000:~$ sudo -H pip3 install virtualenv
ubuntu@ip-00-0-0-000:~$ virtualenv 仮想環境名
ubuntu@ip-00-0-0-000:~$ source 仮想環境名/bin/activate
(仮想環境名) ubuntu@ip-00-0-0-000:~$ git clone レポジトリ
(仮想環境名) ubuntu@ip-00-0-0-000:~$ cd リポジトリ名
(仮想環境名) ubuntu@ip-00-0-0-000:~/リポジトリ名$ pip install -r requirements.txt
ちなみに私用
-r requirements-dev.txt
gunicorn
mysqlclient
13.4 本番環境用の環境変数を作成
SECRET_KEY=ローカル環境で初めに設定されていたシークレットキー
DEBUG=False
DATABASE_URL=mysql://ユーザー名:パスワード@RDSのエンドポイント:/データベース名
ALLOWED_HOSTS=EC2インスタンスの独自ドメイン
CORS_ORIGIN_WHITELIST=firebase側のURLを設定する
AWS_ACCESS_KEY_ID=各自設定が必要
AWS_SECRET_ACCESS_KEY=各自設定が必要
AWS_STORAGE_BUCKET_NAME=各自設定が必要
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS')
13.5 RDSにアクセスし、データベースを作成
//データベースの作成
$ mysql -h RDSのエンドポイント -u admin -p
mysql> CREATE DATABASE データベース名;
(仮想環境名) ubuntu@ip-00-0-0-000:~/リポジトリ名$ python3 manage.py migrate
(仮想環境名) ubuntu@ip-00-0-0-000:~/リポジトリ名$ python3 manage.py collectstatic
(仮想環境名) ubuntu@ip-00-0-0-000:~/リポジトリ名$ python3 manage.py createsuperuser
- 本番環境用にpostgresqlのデーターベースを作成したので、models.pyのデータベース構造を再度展開するためにmigrateする必要がある
- プロジェクト内に散らばったstaticファイルをstaticフォルダに統合する
14.6 gunicornの設定
$ sudo vi /etc/systemd/system/gunicorn.service
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/プロジェクト名
ExecStart=/home/ubuntu/仮想環境名/bin/gunicorn --access-logfile - --workers 3 --bind unix:/home/ubuntu/プロジェクト名/アプリ名.sock アプリ名.wsgi:application
[Install]
WantedBy=multi-user.target
- WorkingDirectoryは
[/ から Django Project Root までパス]/[Django Project Root(git cloneならリポジトリ名)]
- ExecStartは
[/ から gunicorn コマンドまでのパス]/gunicorn --access-logfile - --workers 3 --bind unix: (wsgiが入ってるディレクトリ).wsgi:application
- gunicornの設定ファイルは公式ドキュメントを参考にしております。
gunicornの起動
$ sudo systemctl restart gunicorn
$ sudo systemctl enable gunicorn
13.7 nginxの設定
$ sudo vi /etc/nginx/sites-available/プロジェクト名
server {
listen 80;
server_name EC2のパブリックIPアドレス;
location = /favicon.ico {access_log off; log_not_found off;}
location /static/ {
root /home/ubuntu/プロジェクト名;
}
location / {
include proxy_params;
proxy_pass http://unix:/home/ubuntu/プロジェクト名/アプリ名.sock;
}
}
- プロジェクト名(git clone)はリポジトリ名
nginxの起動
//シンボリックリンクを作成
$ sudo ln -s /etc/nginx/sites-available/プロジェクト名 /etc/nginx/sites-enabled/
//nginxの起動
$ sudo systemctl restart nginx
//80ポートを解放
$ sudo ufw allow 'Nginx Full'
- 上記で作成した設定ファイルに対してシンボリックリンクを作成する(こうすることによって実態のファイルを削除しなくてもリンクを切ることで、新しくリンクさせることができる)
- ec2コンソールに戻り、先ほど作成したセキュリティーグループのインバウンドルールに
httpタイプ
で0.0.0.0/0ソース
を追加した後にgunicornを起動
ちなみにnginx status確認コマンド
$ sudo systemctl status nginx.service
14. 本番環境用の設定
REACT_APP_API_URL=EC2のパブリックIPアドレスもしくは独自ドメイン
CORS_ORIGIN_WHITELIST = [
"firebase側のURLを設定する"
]
Route53とACMについての参考記事
おまけ
ssh: connect to host ec2-00-00-000-00.ap-northeast-1.compute.amazonaws.com port 22: Connection refusedのチェックポイント
- EC2インスタンスに付随しているセキュリティーグループのインバウンドルールにsshが追加されているか
- EC2インスタンスに付随しているサブネットグループは適正かどうか(publicのサブネットグループになっているかなど)
- publicルートテーブルの関連付けに上記のサブネットグループが追加されているかどうか
削除したくなった場合のRDSの削除手順
- RDSインスタンスを削除
- RDS用のサブネットグループの削除
- RDS用セキュリティーグループのインバウンドとアウトバウンドを空にしてから削除(空にしないと削除出来ない)もしくはセキュリティーグループ関連づけを削除
ERR_CONNECTION_TIMED_OUTのエラー
ALBのアクセスログに関してはこちら
ちなみに私用
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::バケット名/*"
},
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::東京リージョンの場合h582318560864:root"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::バケット名/*"
},
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::バケット名/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
},
{
"Effect": "Allow",
"Principal": {
"Service": "delivery.logs.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::バケット名"
}
]
}