我尝试使用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

步骤

    1. 创建一个Django应用程序

 

    1. 启动EC2实例

 

    1. 确认可以从浏览器访问Django应用程序

 

    1. 使用弹性IP固定全局IP地址

 

    1. 配置Nginx

 

    1. 获取自定义域名并在Route53上设置

 

    1. 获取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。

ec2画面.png
image.png
image.png
image.png
image.png
スクリーンショット 2023-03-22 1.59.04.png
image.png

使用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で動かす

image.png

切换到含有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

スクリーンショット 2023-03-21 23.18.42.png
image.png
image.png

将弹性IP与EC2关联。

スクリーンショット 2023-03-21 23.20.41.png
スクリーンショット 2023-03-21 23.23.41.png
ElasticIP関連付け.png

当完成关联后,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
image.png

在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. 选择一个域名并进行购买

image.png

请在下一页确认要购买的域名,并在后续页面上完成购买手续。
如果是个人使用,请通过whois进行查询,但个人信息的联系方式将全部显示为AWS的联系方式。
筆者购买完成后大约20分钟内域名就被激活了。

将固定的全球IP地址与其关联

从Route53仪表板中选择主机区域。

スクリーンショット 2023-03-22 0.11.05.png
スクリーンショット 2023-03-22 0.13.08.png

输入以下内容,并点击创建记录。

スクリーンショット 2023-03-22 1.55.23.png

域名已经与固定的全球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;
    }
}
image.png

重启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 &
广告
将在 10 秒后关闭
bannerAds