我尝试使用WSGI + EC2 + NGINX部署Django
概述
我已经在EC2上部署了用Django开发的应用程序,但遇到了各种问题,所以我将其整理成一篇文章作为备忘录。首先,我将描述如何设置EC2并使其在自定义域名下运行。
环境
-
- Macbook pro (M1 2020)
-
- Python 3.10.8
-
- Django 3.2
-
- EC2 (Ubuntu 22.04)
- Nginx 1.18
步骤
-
- 创建一个Django应用程序
-
- 启动EC2实例
-
- 确认可以从浏览器访问Django应用程序
-
- 使用弹性IP固定全局IP地址
-
- 配置Nginx
-
- 获取自定义域名并在Route53上设置
-
- 获取SSL证书
- 将WSGI进程守护化
创建Django应用程序
在这里我们只会创建一个简化版的Django项目。
关于Django的详细开发方法,请参考相关的书籍或其他文章。
A. 建立虛擬環境
B. 創建Django專案和應用
C. 將機密信息集中於.env檔案
D. 分割設定檔案
E. 將已安裝的程式庫輸出至requirements.txt檔案
F. 將代碼推送至Github
A. 建立虚拟环境
我們可以使用Python內置的venv模塊來創建環境。
$ python -m venv django_env
我进入虚拟环境。
$ source django_env/bin/activate
请安装所需的库。
这里只有两个,如果还有其他需要的库,请适时进行安装。
$ pip install django==3.2
$ pip install uwsgi==2.0.21
生成Django项目和应用程序
创建Django项目,并在其下创建应用程序。
$ django-admin startproject sample_project .
$ django-admin startapp sample_app
如果忘记了末尾的句号,只需移动到项目目录即可。
$ django-admin startproject sample_project
$ cd sample_project
$ django-admin startapp sample_app
将机密信息汇集到.env文件中。
安装图书馆。
$ pip install django-environ
请在与 manage.py 文件相同的目录中创建一个 .env 文件。
在这里存储密钥等敏感信息。
如果有数据库密码等,请在这里写上。
还可以在这里写下根据环境变化的变量(也可以使用环境变量)。请务必与 manage.py 文件放在相同的目录中。
SECRET_KEY=django-insecure-xxxxxxxxxxxxxxxxxxxxxxx
DEBUG=True
ALLOWED_HOSTS=*
将settings.py文件中的相关部分进行替换。
import os
import environ
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# env系の設定
env = environ.Env()
env.read_env(os.path.join(BASE_DIR, '.env'))
SECRET_KEY = env('SECRET_KEY')
DEBUG = env('DEBUG') == 'True'
ALLOWED_HOSTS = [env('ALLOWED_HOSTS')]
...
为了避免上传到Github上,我会将其列入.gitignore。
.env
将settings文件分割
创建一个名为settings的文件夹,并按以下结构进行配置。
将settings.py更名为common.py。
├── settings
│ ├── common.py
│ ├── development.py
│ └── production.py
将共通的配置写在common.py中,将日志配置和其他环境相关的配置写在development.py或production.py等文件中。
根据不同的环境加载不同的文件,即可反映各自的配置。
from .common import *
...
from .common import *
...
由于在 settings 目录中增加了层级,因此需要修改 BASE_DIR 的层级。
将 parent 对象增加一个。
BASE_DIR = Path(__file__).resolve().parent.parent.parent
我将修正调用了settings.py的地方。
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings.development')
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'sample_project.settings.production')
将安装的库输出到requirements.txt文件中
我会将相同的库安装在EC2上以便后续使用。
$ pip freeze > requirements.txt
将代码推送到 Github。
我会将到目前为止的更改内容推送到Github上。请在与manage.py相同的目录中执行。请提前在Github上创建好要推送的存储库。
$ git init
$ git remote add origin git@github.com:xxxxxxxxxxxxxxxxx.git
$ git add .
$ git commit -m 'create django project'
$ git push origin master
2. 设置EC2
A. 在Ubuntu 22.04上创建EC2实例
B. 通过SSH连接到EC2
C. 在EC2上使用pyenv设置Python版本与开发环境一致
D. 在EC2上从GitHub上进行代码拉取
E. 在EC2上搭建虚拟环境
A. 在ubuntu22.04上创建EC2。
使用SSH连接到EC2
请从EC2控制台中选择刚才创建的实例。
在实例概览页面中选择”连接”,将显示SSH客户端的连接信息。
.pem是之前生成的密钥对,请包括保存的路径。
$ ssh -i "/to/your/pem_path/xxxxx.pem" ubuntu@ec2-IPアドレス.ap-northeast-1.compute.amazonaws.com
使用EC2平台,并使用pyenv工具将Python版本与开发环境保持一致。
我将检查本地环境的Python版本。
$ python -V
Python 3.10.8
登录EC2实例并安装所需的库。
$ sudo apt update; sudo apt install build-essential libssl-dev zlib1g-dev \
libbz2-dev libreadline-dev libsqlite3-dev curl \
libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
我将使用pyenv拉取更新代码。
$ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
我们将开辟一条道路。
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
$ echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
$ echo 'eval "$(pyenv init -)"' >> ~/.bashrc
将更改内容应用。
$ source .bashrc
我会确认Pyenv已安装好。
$ pyenv --version
我要检查本地环境是否可以安装与Python相同的版本。
$ pyenv install -list
安装与本地Python版本相同的版本。
pyenv install 3.10.8
确认已安装指定的版本。
$ pyenv versions
更改Python的版本。
$ pyenv global 3.10.8
确认已更改为指定的版本。
$ python -V
Python 3.10.8
在EC2上从Github上进行pull操作
我将进入SSH目录。
$ cd ~/.ssh
我会制作钥匙。
$ ssh-keygen -t rsa
将id_rsa.pub文件的内容复制,并在下方注册为新的SSH密钥。
如果输出为“接続確認をします。You’ve successfully authenticated”,那就表示一切正常。
$ ssh -T git@github.com
我要拉取Django应用程序。
$ cd ~
$ git clone git@github.com:xxxxxxxxxxxxx.git
E. EC2上に仮想環境を構築する
以与当地制造时相同的方式进行建立。
or
按照在当地制作时的方式进行搭建。
$ python -m venv django_env
$ source django_env/bin/activate
ライブラリをインストールします。
$ pip install -r requirements.txt
3. 确认可以从浏览器访问Django应用程序。
A. とりあえずmanage.pyで動かす
B. wsgiで動かす
A. とりあえずmanage.pyで動かす
切换到含有manage.py文件的目录,并执行下一步操作。
$ python manage.py runserver 0.0.0.0:8000
我会检查您是否可以通过浏览器连接到EC2实例的全局IP地址。
http://グローバルIP:8000
以wsgi作为运行平台
停止manage.py并在同一目录下执行。
$ uwsgi --http :8000 --module sample_project.wsgi
我会通过浏览器检查是否可以使用全局IP地址进行连接。
http://グローバルIP:8000
4. 通过弹性IP在全球范围固定IP地址
A. 获取弹性IP
B. 关联EC2和弹性IP
C. 在EC2的.env配置文件中添加固定的全局IP地址
给分配弹性 IP
将弹性IP与EC2关联。
当完成关联后,EC2的主机名将发生变化,因此在进行ssh连接时,请务必从EC2控制台重新确认连接信息。
将弹性 IP 添加到 EC2 的 .env 文件中,以实现固定的全球 IP 地址。
ALLOWED_HOSTS=固定したグローバルIPアドレス
5. 配置 Nginx
A. 编辑nginx.conf文件
B. 复制uwsgi文件
C. 将静态文件放置在Nginx中
D. 启动nginx服务
A. 编辑nginx.conf
创建一个编写设置的文件。
$ sudo touch /etc/nginx/sites-available/sample_project
内容如下所示。
upstream django {
server unix:///home/ubuntu/sample_project/sample_project.sock;
}
server {
listen 80;
server_name グローバルIPアドレス;
root /usr/share/nginx/html;
location /static {
alias /usr/share/nginx/html/static;
}
location /media {
alias /usr/share/nginx/html/media;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
uwsgi_pass django;
include /home/ubuntu/sample_project/uwsgi_params;
}
}
创建一个符号链接,将其加载到nginx.conf文件中。
$ sudo ln -s /etc/nginx/sites-available/sample_project /etc/nginx/sites-enabled/sample_project
将uwsgi文件复制。
将 uwsgi_params 文件从 Nginx 复制到项目的 manage.py 所在的相同级目录。
$ sudo cp /etc/nginx/uwsgi_params /home/ubuntu/sample_project/
将静态文件放置在Nginx中。
将Django项目中的静态文件(如CSS和JavaScript等)部署到Nginx上。
创建配置目录。
$ sudo mkdir /usr/share/nginx/html/static
$ sudo mkdir /usr/share/nginx/html/media
更改目录的所有者。
$ sudo chown ubuntu:ubuntu /usr/share/nginx/html/static
$ sudo chown ubuntu:ubuntu /usr/share/nginx/html/media
更改设置中的设置。
from .common import *
STATIC_ROOT = BASE_DIR / "static"
...
from .common import *
STATIC_ROOT = '/usr/share/nginx/html/static'
MEDIA_ROOT = '/usr/share/nginx/html/media'
...
前往manage.py的文件夹,执行以下命令。
$ python manage.py collectstatic
我会确认是否放置了静态文件。
$ ls -lrt /usr/share/nginx/html/static
D. 运行Nginx
激活并启动Nginx。
$ sudo systemctl enable nginx
$ sudo systemctl start nginx
在manage.py所在的目录中执行以下命令,确认可以通过浏览器使用全局IP进行访问。
$ uwsgi --socket /home/ubuntu/sample_project/sample_project.sock --module sample_project.wsgi --chmod-socket=666
6. 在Route53上获取一个独特的域名。
A. 選擇並購買域名。
B. 將固定的全球IP關聯起來。
C. 將域名反映在Nginx和Django上。
A. 选择一个域名并进行购买
请在下一页确认要购买的域名,并在后续页面上完成购买手续。
如果是个人使用,请通过whois进行查询,但个人信息的联系方式将全部显示为AWS的联系方式。
筆者购买完成后大约20分钟内域名就被激活了。
将固定的全球IP地址与其关联
从Route53仪表板中选择主机区域。
输入以下内容,并点击创建记录。
域名已经与固定的全球IP地址绑定成功。
将域名反映到Nginx和Django中。
在Nginx中添加域名
server_name グローバルIPアドレス 取得したドメイン名;
...
ALLOWED_HOSTS = ['取得したドメイン名', env('ALLOWED_HOSTS')]
...
通过这个域名就可以访问了。
http://取得したドメイン名
7. 获得SSL证书
A. 安装 Let’s Encrypt
B. 在 Nginx 中接受对 443 的访问
A. 我们来安装Let’s Encrypt
停止nginx,并安装Let’s Encrypt。
$ sudo systemctl stop nginx
$ sudo apt-get install letsencrypt
获取进行SSL化所需的证书。
$ sudo certbot certonly –standalone -d 取得したドメイン名
更改证书的执行权限。
$ sudo chmod 700 /etc/letsencrypt/live/
B. 使用 Nginx 接受对于 443 端口的访问。
upstream django {
server unix:///home/ubuntu/sample_project/sample_project.sock;
}
server {
listen 80;
server_name ドメイン名;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name ドメイン名;
root /usr/share/nginx/html;
ssl_certificate /etc/letsencrypt/live/ドメイン名/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ドメイン名/privkey.pem;
location /static {
alias /usr/share/nginx/html/static;
}
location /media {
alias /usr/share/nginx/html/media;
}
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarder-Proto $scheme;
uwsgi_pass django;
include /home/ubuntu/sample_project/uwsgi_params;
}
}
重启Nginx。
$ sudo systemctl start nginx
通过这个,可以使用HTTPS访问。
https://ドメイン名
8. 将wsgi守护化
A. 创建wsgi.ini文件
B. 在后台运行
创建wsgi.ini文件
我将现在正在运行的Django命令复制到文件中。
$ uwsgi --socket /home/ubuntu/sample_project/sample_project.sock --module sample_project.wsgi --chmod-socket=666
[uwsgi]
socket = /home/ubuntu/sample_project/sample_project.sock
module = sample_project.wsgi
chmod-socket = 666
进行操作确认。
$ uwsgi --ini uwsgi.ini
在后台运行
在后台运行时,无论是否断开SSH连接,应用程序都会持续运行。
$ uwsgi --ini uwsgi.ini &