1-Django - 部署Django纯后端项目
- about
- before
- 物料准备
- 线上部署步骤
- 常见报错
- settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
- Error 10061 connecting to 82.157.21.45:6379. 由于目标计算机积极拒绝,无法连接。
- ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2
- Error: That port is already in use
- DisallowedHost as /
- supervisor启动uwsgi失败
- 静态文件加载成功,但页面中不生效
- 静态文件加载不上
- 静态文件配置无误,浏览器访问nginx,静态文件加载报403 forbidden
- NGINX + uwsgi配置之后,登录过程中找不到session
about
本篇博客主要演示了纯Django项目的部署,只要你不是前后端分离的项目部署,都可以参考这个博客。
技术栈:
腾讯云centos7.6 + ssl + 备案好的域名 + python3.9 + django4.2 + mysql8.0 + redis5.0.7 + uwsgi2.0.23 + nginx1.24.0
before
部署项目是一个复杂且麻烦的过程:
- 平台不同,ubuntu、Windows iis、centos7;
- 项目不同,它依赖的三方软件也不同,要求也不同;
- 大家的水平也不同,小白的话,有点难;
- 对于云服务器也不太了解,设置安全组/防火墙;
- 对于MySQL软件的用户、权限设置不清楚;
- 对于Linux、云服务器、nginx,都要有了解。
- 对于查看各种日志不了解;500 ERROR
- 项目本身在本地都跑不通的这些bug,没有解决完,就上线;
- 项目开发中,在某些具体功能中把代码写死的,比如连接
127.0.0.1
的本地开发环境的配置;
务必要记住
- 本地开发环境,在把项目代码上传到云服务器之前;要在本地进行足够的测试;而且,要把所有连接本地的一些配置,都要改为线上环境的;在项目中,写死的路径、连接,一定要统一的放到配置文件中。
- 尽量避免出现中文路径和中文文件名。包括一些特殊字符。
物料准备
所以需要准备:
- 一个云服务器,配置要求:2核2G内存,带宽,阿里1M;腾讯300M/400M都行;硬盘40G+;系统是cnetos7.x(7.3/7.5/7.6)。
- 云服务器这块,一般新用户优惠一年100元左右;但如果续费可就不是这个价了。
- 申请并购买域名,域名和云服务器厂家选择一致就行。
- 本地要安装xshell、xftp,个人免费版本,连接:https://www.xshell.com/zh/xshell/
- 本地准备并调试好你的项目。
本地项目准备
项目下载与测试
由于项目是在gitee上,所以,我需要把它拉到本地之后,然后跑起来看看有没有问题。
将项目下载到本地,然后创建虚拟环境,并且下载项目依赖包。
PS:如果本地有了项目,就不需要了拉代码这一步骤了。
# 拉代码到本地
git clone https://gitee.com/wupeiqi/day13
# 终端命令
pip install virtualenv
virtualenv venv
.\venv\Scripts\activate
pip -V
cd day13
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
pip install --no-cache-dir -r requirements.txt
在本地让项目跑起来
把项目中所有的数据库连接、或者其他连接本地的,都需要调整为线上环境的。
settings.py
# 这种写法适用于多种不同的环境,你可以一套环境一个配置文件,根据ENV_的值的不同,导入不同的配置项
# 项目如果是线上环境是,那么ENV_的值可以是pro,如果是测试环境,那么ENV_的值可以是test,这个你可以自己约定,主要是用于灵活的加载各种配置
ENVS_ = 'local'
# ENVS_ = 'test'
# ENVS_ = 'pro'
try:
if ENVS_ == 'local':
# 放到最后,是为了将前面的覆盖
from .local_settings import *
elif ENVS_ == 'pro':
from .pro_settings import *
except ImportError as e:
print(f'------------> 项目导入{ENVS_}环境配置报错 <------------>', e)
线上环境配置pro_settings.py
:
# MySQL配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'bms', #你的数据库名称
'USER': 'root', #你的数据库用户名
'PASSWORD': '666666', #你的数据库密码
'HOST': '', #你的数据库主机,留空默认为localhost
'PORT': '3306', #你的数据库端口
}
}
本地开发配置local_settings.py
:
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# sqlite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
可以看到,将来部署到线上,用的数据库是MySQL。我的本地开发用的sqlite3数据库。
访问http://127.0.0.1:8000/login/登录,用户名是root
,密码是qwe123
,角色是管理员。
在本地经过充分测试并把发现的bug排一排,保证没有明显的bug。
sqlite3数据迁移到MySQL中
由于本地开发是sqlite3,线上是MySQL,如果线上的MySQL需要一些初始数据,那么这里在演示下如何从sqlite3中将数据导入到MySQL中。
- 先根据配置sqlite3的配置文件,生成
data.json
文件:
# -*- coding = utf-8 -*-
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# # sqlite3配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# # MySQL配置
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.mysql',
# 'NAME': 'day13', # 你的数据库名称
# 'USER': 'root', # 你的数据库用户名
# 'PASSWORD': '123', # 你的数据库密码
# 'HOST': "127.0.0.1", # 你的数据库主机,留空默认为localhost
# 'PORT': '3306', # 你的数据库端口
# }
# }
然后执行导出命令:
python -Xutf8 manage.py dumpdata -o data.json
- 有了
data.json
文件,把sqlite3的配置文件注释掉,配置上MySQL的配置选项,在local_settings.py
中操作:
# -*- coding = utf-8 -*-
import os
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# # sqlite3配置
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# }
# MySQL配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day13', # 你的数据库名称
'USER': 'root', # 你的数据库用户名
'PASSWORD': '123', # 你的数据库密码
'HOST': "127.0.0.1", # 你的数据库主机,留空默认为localhost
'PORT': '3306', # 你的数据库端口
}
}
- 在终端中登录MySQL,然后创建好数据库,再下载pymysql模块,用于和MySQL连接:
# 终端登录到MySQL中
create database day13 CHARSET utf8mb4 COLLATE utf8mb4_bin;
# 下载模块
pip install pymysql
# 然后在settings.py同级目录的__init__.py文件中添加下面两行代码
import pymysql
pymysql.install_as_MySQLdb()
关于连接MySQL数据库,用两个模块:
-
mysqlclient,这个模块优先选择,直接
pip install mysqlclient
下载安装,然后不需要其它任何配置,即可直接运行项目,就可以在项目中连接使用MySQL了,但如果报任何和mysqlclient的错误、连接MySQL的错误。一律不处理。直接把mysqlclient删掉pip uninstall mysqlclient
,改用pymysql。 -
pymysql,
pip install pymysql
下载,然后在settings.py
文件的同级目录找到__init__.py
文件,添加如下代码。import pymysql pymysql.install_as_MySQLdb()
-
mysqlclient和pymysql用法一致,只不过pymysql多了一步配置而已,就上面的两行代码。
- 执行数据库迁移命令,并根据
data.json
文件,将数据导入到MySQL的数据库中:
python manage.py makemigrations
python manage.py migrate
python -Xutf8 manage.py loaddata data.json # 执行这个命令肯定报错
报错:django.db.utils.IntegrityError: Problem installing fixture 'D:\tmp\od\day13\data.json': Could not load www.Administrator(pk=1): (1048, "Column 'create_date' cannot be null")
(venv) D:\tmp\od\day13>python manage.py loaddata data.json
Traceback (most recent call last):
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\mysql\base.py", line 75, in execute
return self.cursor.execute(query, args)
File "D:\tmp\od\venv\lib\site-packages\pymysql\cursors.py", line 153, in execute
result = self._query(query)
File "D:\tmp\od\venv\lib\site-packages\pymysql\cursors.py", line 322, in _query
conn.query(q)
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 558, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 822, in _read_query_result
result.read()
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 1200, in read
first_packet = self.connection._read_packet()
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 772, in _read_packet
packet.raise_for_error()
File "D:\tmp\od\venv\lib\site-packages\pymysql\protocol.py", line 221, in raise_for_error
err.raise_mysql_exception(self._data)
File "D:\tmp\od\venv\lib\site-packages\pymysql\err.py", line 143, in raise_mysql_exception
raise errorclass(errno, errval)
pymysql.err.IntegrityError: (1048, "Column 'create_date' cannot be null")
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "D:\tmp\od\day13\manage.py", line 22, in <module>
main()
File "D:\tmp\od\day13\manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "D:\tmp\od\venv\lib\site-packages\django\core\management\__init__.py", line 442, in execute_from_command_line
utility.execute()
File "D:\tmp\od\venv\lib\site-packages\django\core\management\__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "D:\tmp\od\venv\lib\site-packages\django\core\management\base.py", line 412, in run_from_argv
self.execute(*args, **cmd_options)
File "D:\tmp\od\venv\lib\site-packages\django\core\management\base.py", line 458, in execute
output = self.handle(*args, **options)
File "D:\tmp\od\venv\lib\site-packages\django\core\management\commands\loaddata.py", line 102, in handle
self.loaddata(fixture_labels)
File "D:\tmp\od\venv\lib\site-packages\django\core\management\commands\loaddata.py", line 163, in loaddata
self.load_label(fixture_label)
File "D:\tmp\od\venv\lib\site-packages\django\core\management\commands\loaddata.py", line 253, in load_label
if self.save_obj(obj):
File "D:\tmp\od\venv\lib\site-packages\django\core\management\commands\loaddata.py", line 209, in save_obj
obj.save(using=self.using)
File "D:\tmp\od\venv\lib\site-packages\django\core\serializers\base.py", line 288, in save
models.Model.save_base(self.object, using=using, raw=True, **kwargs)
File "D:\tmp\od\venv\lib\site-packages\django\db\models\base.py", line 877, in save_base
updated = self._save_table(
File "D:\tmp\od\venv\lib\site-packages\django\db\models\base.py", line 1020, in _save_table
results = self._do_insert(
File "D:\tmp\od\venv\lib\site-packages\django\db\models\base.py", line 1061, in _do_insert
return manager._insert(
File "D:\tmp\od\venv\lib\site-packages\django\db\models\manager.py", line 87, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "D:\tmp\od\venv\lib\site-packages\django\db\models\query.py", line 1805, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "D:\tmp\od\venv\lib\site-packages\django\db\models\sql\compiler.py", line 1822, in execute_sql
cursor.execute(sql, params)
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\utils.py", line 102, in execute
return super().execute(sql, params)
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\utils.py", line 67, in execute
return self._execute_with_wrappers(
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\utils.py", line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\utils.py", line 84, in _execute
with self.db.wrap_database_errors:
File "D:\tmp\od\venv\lib\site-packages\django\db\utils.py", line 91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
File "D:\tmp\od\venv\lib\site-packages\django\db\backends\mysql\base.py", line 75, in execute
return self.cursor.execute(query, args)
File "D:\tmp\od\venv\lib\site-packages\pymysql\cursors.py", line 153, in execute
result = self._query(query)
File "D:\tmp\od\venv\lib\site-packages\pymysql\cursors.py", line 322, in _query
conn.query(q)
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 558, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 822, in _read_query_result
result.read()
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 1200, in read
first_packet = self.connection._read_packet()
File "D:\tmp\od\venv\lib\site-packages\pymysql\connections.py", line 772, in _read_packet
packet.raise_for_error()
File "D:\tmp\od\venv\lib\site-packages\pymysql\protocol.py", line 221, in raise_for_error
err.raise_mysql_exception(self._data)
File "D:\tmp\od\venv\lib\site-packages\pymysql\err.py", line 143, in raise_mysql_exception
raise errorclass(errno, errval)
django.db.utils.IntegrityError: Problem installing fixture 'D:\tmp\od\day13\data.json': Could not load www.Administrator(pk=1): (1048, "Column 'create_date' cannot be null")
问题原因,是Administrator
模型类中,对于create_date
的约束没写全,MySQL的表设计中,create_date
字段值不允许为null
,但模型类中,没有声明,所以需要调整模型类的字段设置。
class Administrator(ActiveBaseModel):
""" 管理员表 """
username = models.CharField(verbose_name="用户名", max_length=32, db_index=True)
password = models.CharField(verbose_name="密码", max_length=64)
mobile = models.CharField(verbose_name="手机号", max_length=11, db_index=True)
# 未调整前
# create_date = models.DateTimeField(verbose_name="创建日期", auto_now_add=True)
# 调整之后
create_date = models.DateTimeField(verbose_name="创建日期", auto_now_add=True, null=True, blank=True) # 添加null=True, blank=True
def __str__(self):
return self.username
然后终端从新执行:
# 执行数据库迁移
python manage.py makemigrations
python manage.py migrate
# 导入数据到MySQL中
python manage.py loaddata data.json
报错:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xba in position 347: invalid start byte
这是在执行loaddata导入json数据时报的错,原因是编码问题,解决办法使用 Python 的 UTF-8 模式导出数据就没问题:
python -Xutf8 manage.py dumpdata -o data.json
python -Xutf8 manage.py loaddata data.json
从MySQL中备份数据库
有兴趣可以参考:https://www.cnblogs.com/Neeo/articles/11303149.html#普通参数
mysqldump -uroot -p --default-character-set=utf8 -B 要备份的数据库 >要保存的数据文件名.sql
# 我的项目用到的数据库是day13,所以,我只需要将这一个数据库备份出来即可。
mysqldump -uroot -p --default-character-set=utf8 -B day13 >data.sql
备份数据,我不推荐用navicat,因为sql不全,推荐mysqldump命令。
Redis配置
检查Redis配置是否合理。
下载相关模块:
# 我这里以5.3.0版本为例,不同的版本之间,可能略有差异,所以要版本问题要注意
pip install django-redis==5.3.0
在local_settings.py
:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379", # 安装redis的主机的 IP 和 端口
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 1000,
"encoding": 'utf-8'
},
"PASSWORD": "123456" # redis密码
}
}
}
本地测下Redis是否好用。
我的示例项目并没有用到Redis。所以,我临时添加一些代码,做个测试,如果你项目已经用了Redis,你可以不做下面的动作。
account.py
:
from django.conf import settings
from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from www.forms.account import LoginForm, SmsLoginForm, SendSmsForm
from www import models
from django.shortcuts import HttpResponse
from django_redis import get_redis_connection
def login(request):
# -------------- 新增的代码 -------------------
conn = get_redis_connection('default') # 或者显示的使用某个别名
# 最基本的用法,获取连接对象,并设置值,同时指定过期时间,单位: 秒
conn.set('18211101111', '8842', ex=10)
# 在需要的地方在通过连接对象获取值
print(conn.get("18211101111"))
# -------------- 新增的代码 -------------------
# 1.GET请求看到登录页面
if request.method == "GET":
form = LoginForm()
return render(request, "login.html", {"form": form})
# 2.用户提交
# 2.1 是否为空
form = LoginForm(data=request.POST)
if not form.is_valid():
return render(request, "login.html", {"form": form})
# 2.2 去数据库校验:客户表?管理员表?
data_dict = form.cleaned_data # {role:1,username:11,passwrod:123}
role = data_dict.pop("role")
if role == "1":
user_object = models.Administrator.objects.filter(**data_dict).filter(active=1).first()
else:
user_object = models.Customer.objects.filter(**data_dict).filter(active=1).first()
# 2.3 数据不存在
if not user_object:
form.add_error("password", "用户名或密码错误")
return render(request, "login.html", {"form": form})
# 2.4 数据存在,将用户信息存储session
mapping = {"1": "ADMIN", "2": "CUSTOMER"}
request.session[settings.NB_SESSION_KEY] = {
"role": mapping[role], # "ADMIN" "CUSTOMER"
"id": user_object.id,
"name": user_object.username,
}
# 2.5 成功,跳转后台
return redirect(settings.HOME_URL)
浏览器访问login页面,后端打印出来验证码就说明Redis没有问题。
更新requirements.txt
更新requirements.txt
文件,如果没有就是生成,项目根目录终端执行命令:
pip freeze > requirements.txt
PS:如果你的项目没有requirements.txt
和readme.md
说明文件,基本算是不太成熟的开发人员。相当于卖电脑,不附带说明书。
借用别的项目的截图额外的说下可能遇到的情况,如下图,生成的requirements.txt
中,看看是不是有不合法的格式,比如有些时候用的是conda下载的包,有可能就不太合法,如果我们不注意的话,线上pip下载时就报错了,我们这里要提前避开这个问题。
# 合法的
async-timeout==4.0.2
# 不合法的,我们可以终端执行下pip list,然后定位到这个不合法的包,看它的版本,然后手动调整下就好了
# certifi @ file:///C:/b/abs_85o_6fm0se/croot/certifi_1671487778835/work/certifi # 把原来的不合法的注释掉就完了
# 根据pip list 结果,手动修改之后如下所示
certifi==2022.12.7
OK,到这一步,本地要做的操作,基本上完事了。
线上环境准备
云服务器准备
硬件要求:一个云服务器,配置要求:2核2G内存,将来根据访问量可以调整,带宽,阿里1M;腾讯1M带宽以上,300M/400M月流量都行,将来根据访问量可以调整;硬盘40G+,将来根据访问量可以调整;系统是cnetos7.x(7.3/7.5/7.6)。
选择阿里、华为、腾讯、都可以。我这里以腾讯云为例,演示线上部署。
如果你是刚入门接触这块内容,我建议你去看看腾讯云的最新的活动,连接:https://cloud.tencent.com/act,选个轻量应用服务器,配置不用那么高,价位100元左右的即可。
买完之后,进入控制台,创建远程root账号的密码:
我个人喜欢通过xshell连接云服务器操作。所以这里讲下xshell如何连接云服务器,xshell官网下载:https://www.xshell.com/zh/xshell/,你留个姓名和邮箱即可使用正版的个人免费版。安装就默认就行了。连接参考下图:
云服务器控制台:放开云服务器的安全组
这个步骤很重要,好多同学都会忘记这个,搞的项目部署好了没法访问。
安全组或者叫做防火墙是云服务器厂家在你买的服务器之外套的安全壳。
云服务器控制台中,找到你的实例对应的安全组。
线上:关闭防火墙
服务器有了,执行下面的命令,关闭防火墙,然后安装一些必要的工具插件。
systemctl stop firewalld.service
systemctl disable firewalld.service
systemctl status firewalld.service
sed -i.ori 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
yum update -y
yum -y install gcc automake autoconf libtool make
yum -y install net-tools
yum -y install vim
yum -y install wget
yum install lrzsz
# ........解释........
# 查看防火墙状态
systemctl status firewalld.service
# 关闭防火墙
systemctl stop firewalld.service
# 禁止开机启动防火墙
systemctl disable firewalld.service
# 启动防火墙
systemctl start firewalld.service
# 防火墙随系统开启启动
systemctl enable firewalld.service
# 关闭selinux,提高了系统的安全性,但关闭它可以释放系统资源,提高服务器的性能,避免一些程序的兼容性问题等等
[root@r ~]# sed -i.ori 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
线上:Python3.9.9解释器安装
Python3.6/Python3.11参考:https://www.cnblogs.com/Neeo/articles/12829625.html#编译安装python36
演示cenots7中安装Python3.9,参考:https://www.cnblogs.com/Neeo/articles/12829625.html#编译安装python311
yum install -y epel-release
yum update -y
yum install gcc patch libffi-devel python-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel -y
cd /opt/
wget https://cdn.npmmirror.com/binaries/python/3.9.9/Python-3.9.9.tgz
tar -zxvf Python-3.9.9.tgz
cd /opt/Python-3.9.9
./configure --prefix=/opt/python399/ && make && make install
配置环境变量:
echo "export PATH=/opt/python399/bin:\$PATH" >> /etc/profile
cat /etc/profile
source /etc/profile
# 添加软连接
ln -s /opt/python399/bin/python3.9 /usr/bin/python3.9
ln -s /opt/python399/bin/pip3.9 /usr/bin/pip3.9
任意路径测试:
[root@cs opt]# python3.9 -V
Python 3.9.9
[root@cs opt]# pip3.9 -V
pip 21.2.4 from /opt/python399/lib/python3.9/site-packages/pip (python 3.9)
线上:MySQL8.0安装
https://www.cnblogs.com/Neeo/articles/17135006.html#for-centos7x
把本地的初始sql文件,导入到线上MySQL中:
# 云服务器中执行
# 首先把本地项目根目录下data.sql文件手动上传到云服务器的/od目录
cd /od/
# 然后本地直接将data.sql文件退拽到xshell窗口就可以了,你也可以选择xftp上传
# 导入数据库
mysql -uroot -p <data.sql
# 去数据库中验证下
[root@cs tmp]# mysql -uroot -p123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 808
Server version: 8.0.33 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use day13;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-----------------------+
| Tables_in_day13 |
+-----------------------+
| django_migrations |
| www_administrator |
| www_customer |
| www_customer_hbs |
| www_depart |
| www_hobby |
| www_info |
| www_level |
| www_order |
| www_pricepolicy |
| www_transactionrecord |
+-----------------------+
11 rows in set (0.00 sec)
mysql> select * from www_administrator;
+----+--------+----------+----------------------------------+-------------+-------------+
| id | active | username | password | mobile | create_date |
+----+--------+----------+----------------------------------+-------------+-------------+
| 1 | 1 | root | 5bcf250b089cfc734f39f95900e859b8 | 18630087660 | NULL |
+----+--------+----------+----------------------------------+-------------+-------------+
1 row in set (0.00 sec)
没问题!!
线上:nginx安装
下载依赖
yum update -y
yum -y install gcc gcc-c++ pcre pcre-devel zlib zlib-devel openssl openssl-devel libxml2-devel libxslt-devel gd-devel GeoIP-devel jemalloc-devel libatomic_ops-devel perl-devel perl-ExtUtils-Embed
#安装Nginx需要先将官网下载的源码进行编译,依赖gcc环境
# PCRE是一个perl库,包括perl兼容的正则表达式库。Nginx的http模块使用pcre库来解析正则表达式
# zlib库提供很多种压缩解压缩方式,Nginx使用zlib对http包的内容进行gzip
# OpenSSL是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的秘钥和证书封装管理功能及
#SSL协议,并提供丰富的应用程序供测试或其它目的使用。Nginx不仅支持http协议,还支持HTTPS协议(即在SSL协议上传输http)。
下载
去这个链接:https://nginx.org/en/download.html
cd /opt
wget https://nginx.org/download/nginx-1.24.0.tar.gz
ls
[root@cs opt]# ls
nginx-1.24.0.tar.gz
解压
cd /opt
tar -zxvf nginx-1.24.0.tar.gz
编译安装,我按照方式3走的
注意:nginx的解压目录和编译目录不能是同一文件夹。
cd /opt
mkdir my_nginx
cd /opt/nginx-1.24.0
./configure --prefix=/opt/my_nginx \
--with-threads \
--with-file-aio \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_addition_module \
--with-http_xslt_module=dynamic \
--with-http_image_filter_module=dynamic \
--with-http_geoip_module=dynamic \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gunzip_module \
--with-http_gzip_static_module \
--with-http_auth_request_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_degradation_module \
--with-http_slice_module \
--with-http_stub_status_module \
--with-stream=dynamic \
--with-stream_ssl_module \
--with-stream_realip_module \
--with-stream_geoip_module=dynamic \
--with-stream_ssl_preread_module \
--with-compat \
--with-pcre-jit
没有报错的情况:
Configuration summary
+ using threads
+ using system PCRE library
+ using system OpenSSL library
+ using system zlib library
nginx path prefix: "/opt/my_nginx"
nginx binary file: "/opt/my_nginx/sbin/nginx"
nginx modules path: "/opt/my_nginx/modules"
nginx configuration prefix: "/opt/my_nginx/conf"
nginx configuration file: "/opt/my_nginx/conf/nginx.conf"
nginx pid file: "/opt/my_nginx/logs/nginx.pid"
nginx error log file: "/opt/my_nginx/logs/error.log"
nginx http access log file: "/opt/my_nginx/logs/access.log"
nginx http client request body temporary files: "client_body_temp"
nginx http proxy temporary files: "proxy_temp"
nginx http fastcgi temporary files: "fastcgi_temp"
nginx http uwsgi temporary files: "uwsgi_temp"
nginx http scgi temporary files: "scgi_temp"
接下来进行编译安装:
make -j$(nproc) && make install -j$(nproc)
看下安装目录:
cd /opt/my_nginx
ls
[root@cs my_nginx]# ls
client_body_temp fastcgi_temp logs proxy_temp scgi_temp
conf html modules sbin uwsgi_temp
在nginx的安装目录中:
- conf:存放nginx配置文件目录。
- logs:存放nginx日志目录。
- sbin:存放nginx可执行脚本目录。
- html:存放nginx的网站站点,静态资源的目录。
知道主要的目录作用,我们也就可以启动nginx了。
cd /opt/my_nginx/sbin
./nginx
ps -ef|grep nginx
[root@cs sbin]# ps -ef|grep nginx
root 39441 1 0 22:37 ? 00:00:00 nginx: master process ./nginx
nobody 39442 39441 0 22:37 ? 00:00:00 nginx: worker process
root 39444 73894 0 22:37 pts/1 00:00:00 grep --color=auto nginx
浏览器直接访问你的ip地址就可以看到了:
如果想要在任意目录输入nginx
即可启动,那还需要配置nginx的环境变量。
配置nginx环境变量
echo "export PATH=/opt/my_nginx/sbin:\$PATH" >> /etc/profile
source /etc/profile
此时,就可以在任意位置启动nginx了。
配置启动方式
直接nginx命令启动
# 直接输入nginx来启动,但只能首次启动nginx使用,因为重复启动的话,会提示80端口已被占用
nginx
# 查看nginx相关进程
ps -ef | grep nginx
# 查看NGINX监听的端口
netstat -tunlp | grep nginx
# 平滑重启nginx,也就是重新读取nginx的配置文件,而不是重启进程
nginx -s reload
# 确认nginx配置文件中配置项是否正确
nginx -t
# 停止nginx, 杀死nginx进程
nginx -s stop
配置systemctl管理nginx
systemd 配置文件说明:
- 每一个 Unit 都需要有一个配置文件用于告知 systemd 对于服务的管理方式。
- 配置文件存放于 /usr/lib/systemd/system/,设置开机启动后会在 /etc/systemd/system 目录建立软链接文件。
- 每个Unit的配置文件配置默认后缀名为.service。
- 在 /usr/lib/systemd/system/ 目录中分为 system 和 user 两个目录,一般将开机不登陆就能运行的程序存在系统服务里,也就是 /usr/lib/systemd/system。
- 配置文件使用方括号分成了多个部分,并且区分大小写。
我们来配置下:
cat >/lib/systemd/system/nginx.service<<EOF
[Unit]
Description=nginx
After=network.target
[Service]
Type=forking
ExecStartPre=/opt/my_nginx/sbin/nginx -t -c /opt/my_nginx/conf/nginx.conf
ExecStart=/opt/my_nginx/sbin/nginx -c /opt/my_nginx/conf/nginx.conf
ExecReload=/opt/my_nginx/sbin/nginx -s reload
ExecStop=/opt/my_nginx/sbin/nginx -s stop
PrivateTmp=true
[Install]
WantedBy=multi-user.target
EOF
cat /lib/systemd/system/nginx.service
解释版:
cat >/lib/systemd/system/nginx.service<<EOF
[Unit] # 记录service文件的通用信息
Description=nginx # Nginx服务描述信息
After=network.target # Nginx服务启动依赖,在指定服务之后启动
[Service] # 记录service文件的service信息
Type=forking # 标准UNIX Daemon使用的启动方式
ExecStartPre=/opt/my_nginx/sbin/nginx -t -c /opt/my_nginx/conf/nginx.conf
ExecStart=/opt/my_nginx/sbin/nginx -c /opt/my_nginx/conf/nginx.conf
ExecReload=/opt/my_nginx/sbin/nginx -s reload
ExecStop=/opt/my_nginx/sbin/nginx -s stop
PrivateTmp=true
[Install] # 记录service文件的安装信息
WantedBy=multi-user.target # 多用户环境下启用
EOF
cat /lib/systemd/system/nginx.service
然后执行如下命令:
pkill nginx
systemctl daemon-reload
systemctl start nginx
systemctl status nginx
systemctl stop nginx
线上:Redis安装
redis安装:https://www.cnblogs.com/Neeo/articles/17609004.html#redis507-for-centos79
我的配置,注意,生产中, 千万不要bind 0.0.0.0,不要将Redis暴露到外网环境,防止被人攻击,我这里先这么配置上,先测试,最终再改为127.0.0.1。
daemonize yes
bind 0.0.0.0
port 6379
pidfile /opt/redis6379/pid/redis6379.pid
logfile /opt/redis6379/logs/redis6379.log
dir "/data/redis6379"
save 900 1
save 300 10
save 60 10000
dbfilename "redis.rdb"
appendonly yes
appendfilename "redis.aof"
appendfsync everysec
requirepass 1234
想要配置持久化,参考:https://www.cnblogs.com/Neeo/articles/17609006.html
线上部署步骤
开始上正菜了!!!
线上:目录规划
# 所有项目部署相关的东西都在/od 目录内,二软件都安装到了/opt目录内
mkdir -p /od
cd /od
[root@cs od]# ls
allstatic # 静态文件收集的目录
neeo.cc_nginx # ssl证书文件所在目录
venv # 项目所用的虚拟环境目录
day13 # 项目根目录
logs # 日志相关目录
script # 各种脚本所在的目录
data.sql # 之前从本地上传过来的MySQL初始数据文件
本地:本地项目中配置文件修改为线上的配置并上传项目到云服务器
本地电脑上操作配置文件。
settings.py
,注意,这个settings.py
配置文件中,不要有包含任何关于数据库的配置项,而是写在对应的配置文件中。
# ENVS_ = 'local'
# ENVS_ = 'test'
ENVS_ = 'pro' # 加载线上配置
try:
if ENVS_ == 'local':
from .local_settings import *
elif ENVS_ == 'pro':
from .pro_settings import *
except ImportError as e:
# 这一样打印很重要,线上项目调试时,注意观察,如果有打印这一行内容,说明你加载的其它文件的配置有问题了,需要检查
print(f'------------> 项目导入{ENVS_}环境配置报错 <------------>', e)
pro_settings.py
:
# 先开着调试模式
DEBUG = True
ALLOWED_HOSTS = ["*"]
# 静态文件收集目录
STATIC_ROOT = "/od/allstatic/"
# 云服务器中的MySQL配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day13', #你的数据库名称
'USER': 'zhangkai', #你的数据库用户名
'PASSWORD': '123', #你的数据库密码
'HOST': '82.157.21.45', #你的数据库主机,留空默认为localhost
'PORT': '3306', #你的数据库端口
}
}
# 云服务器中的Redis配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://82.157.21.45:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 1000,
"encoding": 'utf-8',
"decode_responses": True,
},
"PASSWORD": "1234" # redis密码
}
}
}
手动咋本地将项目打包,并且上传到云服务器的/od
目录下:
cd /od
unzip day13.zip
[root@cs od]# ls
day13 # 项目目录
day13.zip
线上:数据库迁移
其实在之前安装MySQL那个步骤中,已经做完了:
# 云服务器中执行
# cd到从本地上传到服务器的data.sql文件所在的目录,执行下面的命令
mysql -uroot -p <data.sql
# 完事登进去可以查看下有没有表和记录
[root@cs opt]# mysql -uzhangkai -p123
mysql> use day13;
mysql> show tables;
+-----------------------+
| Tables_in_day13 |
+-----------------------+
| django_migrations |
| www_administrator |
| www_customer |
| www_customer_hbs |
| www_depart |
| www_hobby |
| www_info |
| www_level |
| www_order |
| www_pricepolicy |
| www_transactionrecord |
+-----------------------+
11 rows in set (0.00 sec)
mysql> select * from www_administrator;
+----+--------+----------+----------------------------------+-------------+-------------+
| id | active | username | password | mobile | create_date |
+----+--------+----------+----------------------------------+-------------+-------------+
| 1 | 1 | root | 5bcf250b089cfc734f39f95900e859b8 | 18630087660 | NULL |
+----+--------+----------+----------------------------------+-------------+-------------+
1 row in set (0.00 sec)
OK,没问题。
线上:创建虚拟环境并测试项目能否运行(重要的一步)
创建虚拟环境并且下载依赖包:
cd /od
# 解释器安装好了之后,可以直接下载virtualenv模块
pip3 install virtualenv
# 创建虚拟机换
virtualenv venv
# 激活虚拟环境
source /od/venv/bin/activate
# 注意观察 pip -V的结果,返回的必须是你虚拟环境的pip所在目录才算激活
pip -V
# 升级pip和下载相关模块
pip install --upgrade pip
pip install uwsgi
# 因为执行pip在/od 目录,而requirements.txt在/od/day13目录,所以我用了相对路径
pip install -r day13/requirements.txt
在线上服务器上,先把项目跑起来。
激活虚拟环境之后,通过下面的命令运行Django项目:
cd /od/day13
python3 manage.py runserver 0.0.0.0:8000
浏览器访问http://82.157.21.45:8000/login
,进行登录操作,然后你可以任意的访问项目各个页面做一些交互动作,目的是再次确认,项目访问都是正常的。
# 账号信息
用户角色: 管理员
用户名: root
密码: qwe123
线上直接跑项目,测完没问题,再往下走。
线上:配置uwsgi
创建uwsig.ini文件:
# 确认虚拟环境已经激活,且下载好了uwsgi模块
cd /od
source /od/venv/bin/activate
pip install uwsgi
# 创建脚本所在目录和uwsgi日志所在目录
mkdir script
mkdir logs
touch script/uwsgi.ini
文件内容,注意,uwsgi.ini
文件可以写注释内容,但注释内容应该写在配置项的上面或者下面,而不是和配置项在一行,即禁止这么写chdir=/od/day13/ # 填写订单项目的根目录
。
cat >/od/script/uwsgi.ini<<EOF
[uwsgi]
# 填写订单项目的根目录
chdir=/od/day13/
# 填写与项目同名的目录,这是个相对路径,主要就是找到其内的wsgi.py这个文件
module=day13.wsgi
# 虚拟环境的根目录,也就是工作目录
home=/od/venv/
# uwsgi的主进程,其他的uwsgi的进程都是这个主进程的子进程,当你kill时,杀掉的也是这个master主进程
master=true
# uwsgi并发时的工作进程的数量,官网的建议是:2 * cup核数 + 1
# 由这几个进程来分摊并发请求
processes=3
# 临时使用http,实际部署时,通过nginx反向代理,就要把http换成socket,这点别忘了改
http=0.0.0.0:8000
# socket=0.0.0.0:8000
# 当服务器退出时,自动删除unix socket文件和pid文件
vacuum=true
# 默认的请求的大小为4096,如果你接收到了一个更大的请求 (例如,带有大cookies或者查询字符串),那么超过4096的限制就会报错invalid request block size: 4547 (max 4096)...skip,所以我们这里提前调整下
# https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Options.html#buffer-size
buffer-size=32768
# uwsgi的日志文件,开始我也是先把日志配置注释掉,任其先直接输出到控制台,方便观察是否有报错,等整个项目各项部署都么问题了,在启用这个配置也不晚。
# logto=/od/logs/uwsgi.log
EOF
将项目的线上配置pro_settings.py
中的debug模式调整为Flase:
# -*- coding = utf-8 -*-
ALLOWED_HOSTS = ["*"]
DEBUG = False
# 云服务器中的MySQL配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day13', # 你的数据库名称
'USER': 'zhangkai', # 你的数据库用户名
'PASSWORD': '123', # 你的数据库密码
'HOST': '82.157.21.45', # 你的数据库主机,留空默认为localhost
'PORT': '3306', # 你的数据库端口
}
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://82.157.21.45:6379", # 安装redis的主机的 IP 和 端口
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 1000,
"encoding": 'utf-8'
},
"PASSWORD": "1234" # redis密码
}
}
}
运行uwsgi测试:
# 确认虚拟环境已经激活,且下载好了uwsgi模块
cd /od
source /od/venv/bin/activate
pip install uwsgi
# 执行下面的命令
/od/venv/bin/uwsgi --ini /od/script/uwsgi.ini &
然后你浏览器访问http://82.157.21.45:8000/login/
,进行登录操作,然后可以交互,但没有样式,这就是正常的。
为啥没有样式是正常的,因为当DEBUG=False
时,Django就不再帮我们代理静态文件了,那么uwsgi也不会帮我们代理静态文件,所以页面呈现的效果才是如下图这样的。
确认了uwsgi这一步没问题,那么就把uwsig.ini文件的参数调整一下,这个参数取消注释socket=0.0.0.0:8000
:
cat >/od/script/uwsgi.ini<<EOF
[uwsgi]
# 填写订单项目的根目录
chdir=/od/day13/
# 填写与项目同名的目录,这是个相对路径,主要就是找到其内的wsgi.py这个文件
module=day13.wsgi
# 虚拟环境的根目录,也就是工作目录
home=/od/venv/
# uwsgi的主进程,其他的uwsgi的进程都是这个主进程的子进程,当你kill时,杀掉的也是这个master主进程
master=true
# uwsgi并发时的工作进程的数量,官网的建议是:2 * cup核数 + 1
# 由这几个进程来分摊并发请求
processes=3
# 将http注释改为通过socket对外提供通信
# http=0.0.0.0:8000
socket=0.0.0.0:8000
# 当服务器退出时,自动删除unix socket文件和pid文件
vacuum=true
# 默认的请求的大小为4096,如果你接收到了一个更大的请求 (例如,带有大cookies或者查询字符串),那么超过4096的限制就会报错invalid request block size: 4547 (max 4096)...skip,所以我们这里提前调整下
# https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Options.html#buffer-size
buffer-size=32768
# uwsgi的日志文件,开始我也是先把日志配置注释掉,任其先直接输出到控制台,方便观察是否有报错,等整个项目各项部署都么问题了,在启用这个配置也不晚。
# logto=/od/logs/uwsgi.log
EOF
uwsgi相关命令:
# 相对路径启动,指定配置文件启动后端
uwsgi --ini ./uwsgi.ini # 启动
# 启动时会生成两个文件,分别为:
# PID文件 标识这个程序所处的状态
# SOCK文件 用来和其他程序通信的
uwsgi --stop uwsgi.pid # 停止
uwsgi --reload uwsgi.ini # 重置
# 绝对路径启动,就是要找到uwsgi的绝对路径和uwsig.ini的绝对路径
(venv) [root@cs od]# which uwsgi
/od/venv/bin/uwsgi
# uwsig.ini的绝对路径是 /od/venv/bin/uwsgi
# 所以,绝对路径启动就是下面的样子
/od/venv/bin/uwsgi --ini /od/script/uwsgi.ini
# 关闭uwsgi进程,一键杀死所有与uwsgi相关的进程
pkill -9 uwsgi
# 你也可以先过滤出来,在根据pid进行kill
ps -ef |grep uwsgi # 查看进程,找到pid
pkill -9 uwsgi # -9强制杀死叫做uwsgi的进程
线上:静态文件收集
将项目的线上配置pro_settings.py
中确认是否有如下参数:
STATIC_ROOT = "/od/allstatic/"
执行下面的命令:
# 确认虚拟环境已经激活
cd /od
source /od/venv/bin/activate
# 免交互收集静态文件
echo "yes"|python3 ./day13/manage.py collectstatic
(venv) [root@cs od]# echo "yes"|python3 ./day13/manage.py collectstatic
You have requested to collect static files at the destination
location as specified in your settings:
/od/allstatic
This will overwrite existing files!
Are you sure you want to do this?
Type 'yes' to continue, or 'no' to cancel:
0 static files copied to '/od/allstatic', 61 unmodified.
OK,没问题。
线上:配置nginx
首先保证你的uwsgi程序在运行着。
编辑nginx配置文件:
cd /od
vim /opt/my_nginx/conf/nginx.conf
文件内容:
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
charset utf-8;
gzip on;
gzip_static on;
gzip_min_length 1k;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 9;
gzip_buffers 4 16k;
gzip_http_version 1.0;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/jpeg image/gif image/png image/svg+xml;
# 所有访问80端口的请求,都转发给8000端口的服务器,即uwsgi服务
location / {
include uwsgi_params;
uwsgi_pass 0.0.0.0:8000;
}
# 静态文件代理,location /static 最后不加 /
location /static {
# 路径最后要加上 /
alias /od/allstatic/;
}
}
}
然后平滑重启nginx:
nginx -t
nginx -s reload
同时保证uwsgi在运行,然后你就可以浏览器访问了,交互没问题,样式也都有了。
线上:ssl配置
腾讯云ssl配置参考:https://cloud.tencent.com/document/product/400/35244?from_cn_redirect=1
域名备案和解析
注意
- 域名可以随时进行备案,但是试用服务器不能进行和备案通过的域名进行域名解析,下图是来自阿里云的截图, 你必须要花钱购买正式服务器才可以。
网站部署,最好的是要有个备案好的域名,然后让其域名和你的云服务器进行域名解析,这样我们可以通过域名访问我们的云服务器了。
1. 购买域名
购买域名,一般原则是你买个哪个厂家的云服务器,就也买哪个厂家提供的域名,后续用起来也比较方便。
我之前从阿里云迁移到腾讯云的时候,在阿里那边备案好的域名要迁移到腾讯云,阿里云官网做域名迁出动作,腾讯云官网做域名迁入动作,麻烦!
迁入腾讯云的域名要从新备案,更麻烦。费事,所以,你要整域名的话,要考虑好你用哪个厂家的云服务器,尽量买个久点服务器。
参考:https://buy.cloud.tencent.com/domain
你可以输入你心仪的域名,至于想要什么后缀,你可以参考https://baike.baidu.com/item/域名后缀/667641?fr=ge_ala,看下不同的后缀都是啥意思。
输入好域名,且没有被别人注册,你就可以付钱了。
2. 域名备案
备案的话,有分ICP和公安备案:
- ICP备案是指互联网信息服务提供者备案,是指在中国大陆地区从事互联网信息服务的单位或个人必须提交的备案手续,目的是为了规范互联网信息服务的管理。ICP备案是由国家互联网信息办公室负责管理的,包括主要内容、网站备案号等信息。
- 网站公安备案是指在中国大陆地区,网站必须向当地公安部门提交备案申请,以便公安部门能够监管网站的内容和活动,防止网络违法犯罪活动的发生。网站公安备案需要提供的信息包括网站的基本情况、负责人信息、服务器信息、网站域名等。
总的来说,ICP备案是针对互联网信息服务提供者的备案,而网站公安备案则是针对网站内容的备案。两者的目的和管理机构也不同。
我这里的域名主要用于平常教学用,没有特定的网站要跑,所以只进行了ICP备案,没有进行公安备案,如果你将来要跑某个个人或者公司的项目,你可以自行去这个地址完成公安备案:https://beian.mps.gov.cn/#/
接着说ICP备案。
买完了域名,然后就要去其官网找域名备案功能,备案这个流程顺利的话需要十天半个月的。
你可以在手机上下载云服务器的微信小程序,在小程序上搜索"腾讯云备案小程序",然后开始按提示操作即可。
微信搜索备案小程序:
然后按照指引进行备案。
下面是腾讯云的域名备案流程示例。
整个备案过程也是很麻烦的,大概的流程就是填写备案申请,让你提交啥材料你就提交啥材料,然后提交到云服务器厂家审核,有问题的话会有小姐姐电话联系你说你哪里需要修改,然后你就修改之后,继续提交申请,继续审核,然后这个云服务器厂家审核通过了,会给你发短信提醒:
【腾讯云】尊敬的用户,恭喜您提交的(主办单位名称: xxx,备案订单号:3016xxxxxxxxxxxx)备案订单通过腾讯云备案初审,您的备案信息已提交管局审核。管局受理后,备案负责人(xxx)将会收到管局下发短信核验通知,请收到短信后在24小时内按照短信提示登录工信部网站进行短信核验。超时未验证导致备案订单被退回:https://mc.tencent.com/ldERfhrH。审核期间,请勿调整网站内容和域名信息,并保持电话畅通。登录控制台:https://mc.tencent.com/rExpTtQF 。您的账号(账号ID:10001xxxxxx,昵称:xxxxxx)。
这个时候,你按照短信提醒去对应的网址上登录短信中的账号和昵称,进行短信验证,这一步很重要。
然后继续等待,因为要把你的备案申请提交到国家工业和信息化部进行审核。这个阶段可能会有电话垂询,问你为啥备案之类的,不过一般良民应该接不到这个电话。
国家工业和信息化部审核通过之后,会给你发短信,由于我在阿里备过案,迁移到腾讯云之后,从新备的案,所以短信提示是下面这样的。
【工业和信息化部】尊敬的用户xxx,您的备案信息已被变更,详情请咨询您的接入服务提供商。【工信部ICP备案】
总之,现在域名备案搞定了.....
3. 域名解析
这一步相当于让域名和你云服务器的公网IP做个映射,让浏览器通过访问域名的方式就能解析到你的云服务器公网IP上去。
ICP备案成功之后,登录云服务器控制台,找到域名解析,将备案好的域名和你的服务器进行解析,腾讯云是这个地址:https://console.cloud.tencent.com/cns。
添加域名解析:
解析成功之后,你可以查看一下:
# 我备案好的域名是www.neeo.cc
# 公网IP是82.157.21.45
# 那么腾讯云这边直接给我提供好了两个域名都可以解析到公网IP
# 即www开头的和api开头的都可以
www.neeo.cc
api.neeo.cc
# 如果是阿里云的,如果只有一个www的,那么你可以手动添加一个二级域名,也就是添加一个api的,这个不懂可以百度了
总之,域名解析完事了,我们后面步骤再用这个域名。
ssl证书申请并上传到服务器
我这里还是以腾讯云服务器为例,去腾讯云官网申请免费证书,这个地址:https://console.cloud.tencent.com/ssl
我这里申请免费的,有钱大佬请上正式证书。
填写相关信息申请信息,这个ssl证书对应的域名是www.neeo.cc
,由于我们现在部署的纯后端的项目,所以暂时用不到api.neeo.cc
这个域名。
等审核,很快的就通过的,会有短信提示:
【腾讯云】尊敬的腾讯云用户,您购买的域名为 neeo.cc 的 TrustAsia TLS RSA CA 证书(年限:1年,证书ID:9lEHOfy8)现已进入审核验证阶段。请您耐心等待系统自动完成DNS验证,验证通过后证书将会颁发。域名验证方式可参考:https://mc.tencent.com/GhB9OLKA 。登录证书控制台:https://mc.tencent.com/hmpuWwkI 。您的腾讯云账号(账号ID:10xxxxx,昵称:听xxxx)
审核通过,也会收到短信通知:
【腾讯云】尊敬的用户,您的域名 neeo.cc 的 TrustAsia TLS RSA CA(1年)证书已审核通过并成功颁发。登录控制台查看证书:https://mc.tencent.com/g9algEWM 。证书部署可参考文档:https://mc.tencent.com/ZKGAc9go 。您的腾讯云账号(账号ID:10xxxxx,昵称:听xxxx)。
下载证书:
下载到本地后,直接将压缩包上传到云服务器:
cd /od
unzip neeo.cc_nginx.zip
ls neeo.cc_nginx
(venv) [root@cs od]# ls neeo.cc_nginx
neeo.cc_bundle.crt # 证书文件,我们用得上
neeo.cc_bundle.pem # 证书文件(可忽略该文件)
neeo.cc.csr # CSR 文件是申请证书时由您上传或系统在线生成的,提供给 CA 机构。安装时可忽略该文件。
neeo.cc.key # 私钥文件,我们用得上
nginx中配置ssl
配置参考腾讯云官网:https://cloud.tencent.com/document/product/400/35244?from_cn_redirect=1
注意,云服务器中的安全组注意放开443端口。
首先要拿到ssl两个文件的绝对路径:
cd /od/neeo.cc_nginx/
ls
# 接下来手动拼接出来这俩绝对路径
/od/neeo.cc_nginx/neeo.cc_bundle.crt
/od/neeo.cc_nginx/neeo.cc.key
编辑nginx的配置文件:
mv /opt/my_nginx/conf/nginx.conf /opt/my_nginx/conf/nginx.conf.bak
vim /opt/my_nginx/conf/nginx.conf
文件内容。
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
#SSL 默认访问端口号为 443
listen 443 ssl;
#请填写绑定证书的域名,即www.neeo.cc
server_name www.neeo.cc;
#请填写证书文件的相对路径或绝对路径
ssl_certificate /od/neeo.cc_nginx/neeo.cc_bundle.crt;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key /od/neeo.cc_nginx/neeo.cc.key;
ssl_session_timeout 5m;
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
gzip on;
gzip_static on;
gzip_min_length 1k;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 9;
gzip_buffers 4 16k;
gzip_http_version 1.0;
# 进行压缩的文件类型
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/jpeg image/gif image/png image/svg+xml;
# 访问https://www.nee.cc的请求转发给uwsgi
location / {
include uwsgi_params;
uwsgi_pass 0.0.0.0:8000;
}
location /static {
alias /od/allstatic/;
}
}
server {
listen 80;
#请填写绑定证书的域名,即www.neeo.cc
server_name www.neeo.cc;
#把http的域名请求转成https
return 301 https://$host$request_uri;
}
}
完事之后:
[root@cs neeo.cc_nginx]# nginx -t
nginx: the configuration file /opt/my_nginx/conf/nginx.conf syntax is ok
nginx: configuration file /opt/my_nginx/conf/nginx.conf test is successful
[root@cs neeo.cc_nginx]# nginx -s reload
你的浏览器访问就是https的了。
线上:让程序实现后台运行
方式1,通过nohup命令
每一次项目代码迭代更新,都可以直接覆盖掉/od
目录下面的day13
项目,然后重新执行下面的命令即可。
cd /od
source /od/venv/bin/activate
pip install -r day13/requirements.txt
pkill -9 uwsgi
nohup /od/venv/bin/uwsgi --ini /od/script/uwsgi.ini >/dev/null 2>&1 &
主要用的是nohup
命令。
方式2,通过三方监控软件
其原理就是,通过三方软件监控uwsgi的进程,发现uwsgi的进程挂掉了,监控软件自动重启uwsgi进程。
直接使用使用yum命令即可直接安装即可:
yum install -y supervisor
生成supervisor的配置文件:
echo_supervisord_conf > /etc/supervisord.conf
这里贴个supervisor的示例,编辑supervisord.conf配置文件vim /etc/supervisord.conf
,追加如下内容:
[program:od]
command=/od/venv/bin/uwsgi --ini /od/script/uwsgi.ini
autostart=true ; 在supervisord启动的时候也自动启动
startsecs=10 ; 启动10秒后没有异常退出,就表示进程正常启动了,默认为1秒
autorestart=true ; 程序退出后自动重启,可选值:[unexpected,true,false],默认为unexpected,表示进程意外杀死后才重启
stopasgroup=true ;默认为false,进程被杀死时,是否向这个进程组发送stop信号,包括子进程
killasgroup=true ;默认为false,向进程组发送kill信号,包括子进程
然后就可以尝试通过supervisor命令启动uwsgi,注意,要先杀掉之前启动uwsgi相关进程:
cd /od
pkill -9 uwsgi
pkill -9 supervisord
# 根据配置文件启动supervisord进程,同时也会启动监控的uwsgi进程
supervisord -c /etc/supervisord.conf
# 通过supervisorctl终端可以方便查询到监控的所有的进程,然后你可以指定启动/停止/重启某个进程
supervisorctl -c /etc/supervisord.conf
[root@cs od]# pkill -9 uwsgi
[root@cs od]# pkill -9 supervisord
[root@cs od]# supervisorctl -c /etc/supervisord.conf
od RUNNING pid 19594, uptime 0:00:11
supervisor> stop od
od: stopped
supervisor> start od
od: started
supervisor> restart od
od: stopped
od: started
supervisor> stop od
od: stopped
supervisor> start od
od: started
更多参考:https://www.cnblogs.com/Neeo/p/8651136.html
线上:项目优化调整
mysql和Redis都能支持远程访问,肯定是不行的,容易被黑,所以,我们需要设置一下,仅能本地访问,所以,直接调整如下几个文件。
项目的配置文件pro_settings.py
:
ALLOWED_HOSTS = ["*"]
STATIC_ROOT = "/od/allstatic/"
DEBUG = False
# 云服务器中的MySQL配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'day13',
'USER': 'zhangkai',
'PASSWORD': '123', # 注意你的密码应该足够复杂,不要学我
'HOST': '127.0.0.1', # 仅支持云服务器本地访问
'PORT': '3306',
}
}
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379", # 仅支持云服务器本地访问
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {
"max_connections": 1000,
"encoding": 'utf-8'
},
"PASSWORD": "1234" # 注意你的密码应该足够复杂,不要学我,而且要和Redis配置文件中的requirepass值保持一致
}
}
}
redis的配置文件vim /opt/redis6379/conf/redis6379.conf
:
daemonize yes
# 注意,生产中, 千万不要bind 0.0.0.0,不要将Redis暴露到外网环境,防止被人攻击
bind 127.0.0.1
port 6379
pidfile /opt/redis6379/pid/redis6379.pid
logfile /opt/redis6379/logs/redis6379.log
dir "/data/redis6379"
save 900 1
save 300 10
save 60 10000
dbfilename "redis.rdb"
appendonly yes
appendfilename "redis.aof"
appendfsync everysec
requirepass 1234
重启Redis服务:
redis-cli -a 1234 shutdown
redis-server /opt/redis6379/conf/redis6379.conf
在云服务器中的安全组中把uwsgi、mysql和Redis这两个对外的端口禁用掉。
修改uwsgi.ini文件,vim /od/scrit/uwsgi.ini
:
cat >/od/script/uwsgi.ini<<EOF
[uwsgi]
# 填写订单项目的根目录
chdir=/od/day13/
# 填写与项目同名的目录,这是个相对路径,主要就是找到其内的wsgi.py这个文件
module=day13.wsgi
# 虚拟环境的根目录,也就是工作目录
home=/od/venv/
# uwsgi的主进程,其他的uwsgi的进程都是这个主进程的子进程,当你kill时,杀掉的也是这个master主进程
master=true
# uwsgi并发时的工作进程的数量,官网的建议是:2 * cup核数 + 1
# 由这几个进程来分摊并发请求
processes=3
# 临时使用http,实际部署时,通过nginx反向代理,就要把http换成socket,这点别忘了改
# http=0.0.0.0:8000
socket=127.0.0.1:8000
# 当服务器退出时,自动删除unix socket文件和pid文件
vacuum=true
# 默认的请求的大小为4096,如果你接收到了一个更大的请求 (例如,带有大cookies或者查询字符串),那么超过4096的限制就会报错invalid request block size: 4547 (max 4096)...skip,所以我们这里提前调整下
# https://uwsgi-docs-zh.readthedocs.io/zh_CN/latest/Options.html#buffer-size
buffer-size=32768
# uwsgi的日志文件
logto=/od/logs/uwsgi.log
EOF
修改nginx配置文件,主要是uwsgi_pass 127.0.0.1:8000;
:
(venv) [root@cs od]# vim /opt/my_nginx/conf/nginx.conf
(venv) [root@cs od]# cat /opt/my_nginx/conf/nginx.conf
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
#SSL 默认访问端口号为 443
listen 443 ssl;
#请填写绑定证书的域名
server_name www.neeo.cc;
#请填写证书文件的相对路径或绝对路径
ssl_certificate /od/neeo.cc_nginx/neeo.cc_bundle.crt;
#请填写私钥文件的相对路径或绝对路径
ssl_certificate_key /od/neeo.cc_nginx/neeo.cc.key;
ssl_session_timeout 5m;
#请按照以下协议配置
ssl_protocols TLSv1.2 TLSv1.3;
#请按照以下套件配置,配置加密套件,写法遵循 openssl 标准。
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
gzip on;
gzip_static on;
gzip_min_length 1k;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 9;
gzip_buffers 4 16k;
gzip_http_version 1.0;
# 进行压缩的文件类型
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/jpeg image/gif image/png image/svg+xml;
# 访问https://www.nee.cc的请求转发给uwsgi
location / {
include uwsgi_params;
uwsgi_pass 127.0.0.1:8000;
}
location /static {
alias /od/allstatic/;
}
}
server {
listen 80;
#请填写绑定证书的域名
server_name www.neeo.cc;
#把http的域名请求转成https
return 301 https://$host$request_uri;
}
}
然后将nginx和uwsgi都重新启动下:
cd /od
pkill -9 uwsgi
nohup /od/venv/bin/uwsgi --ini ./script/uwsgi.ini >/dev/null 2>&1 &
nginx -t
nginx -s reload
这么着一搞,MySQL、Redis、uwsgi都只能云服务器本地访问,然后云服务器的安全组也禁止外部访问8000、3306、6379端口,所有的攻击你只能通过NGINX的443和80端口了。
常见报错
settings.DATABASES is improperly configured. Please supply the ENGINE value. Check settings documentation for more details.
windows11 + python3.10 + pycharm2023
配置文件中,数据库的配置参数有误,甚至没有配置,检查settins.py中的数据库连接配置。
Error 10061 connecting to 82.157.21.45:6379. 由于目标计算机积极拒绝,无法连接。
本地无法连接远程的Redis服务。
排错思路:
- 检查云服务器安全组是否放开6379端口。
- 检查你项目的Redis配置,是否有误。
- 检查云服务器上的Redis配置文件的bind命令是否监听的是
bind 0.0.0.0
。
注意,以上都动作有调整之后,都要想着重启Redis服务,相关命令。
pkill redis
redis-server /opt/redis6379/conf/redis6379.conf
vim /opt/redis6379/conf/redis6379.conf
ImportError: urllib3 v2.0 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2
这个问题是urllib3的版本高,依赖openssl的版是1.1.1+
解决方式有两个:
- 升级openssl,这个暂时不推荐,如果想了解参考:https://www.cnblogs.com/Neeo/articles/12829625.html#ssl的问题
- 降级urllib3,选择这个。
原来的urllib3的版本是2.0.3,现在降到1.26.15
pip install urllib3==1.26.15
Error: That port is already in use
这个报错,可能就是你运行django的端口被占用了,解决办法就是过滤出来这个端口,杀掉他,或者换个端口运行。
这里来看怎么过滤出来这个端口,进而杀掉进程:
[root@cs ~]# netstat -tunlp|grep 8888
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 13793/uwsgi
[root@cs ~]# pkill -9 uwsgi
[root@cs ~]# netstat -tunlp|grep 8888
看到这个端口被之前的uwsgi在占用,你可以把uwsgi相关进程杀掉。
如果你的uwsgi是supervisor启动的,那么你要先关掉supervisor,再关掉uwsgi:
pkill -9 supervisor
pkill -9 uwsgi
ps:
- kill是根据pid杀死进程。
- pkill是根据进程名杀死进程。
DisallowedHost as /
报错长这样:
这个报基本上就是你的django settings中的ALLOW_HOSTS列表配置的有问题:
ALLOWED_HOSTS = [] # 你可能写成了空列表
直接修改成下面这样的:
ALLOWED_HOSTS = ["*"]
然后重启uwsgi的进程,注意,如果你是supervisor管理的uwsgi,那么可以直接通过supervisor进行重启:
(crmenv) [root@cs newcrm]# vim newcrm/settings.py
(crmenv) [root@cs newcrm]# supervisorctl -c /etc/supervisord.conf
newcrm RUNNING pid 26464, uptime 2 days, 6:19:26
supervisor> restart newcrm
newcrm: stopped
newcrm: started
supervisor>
supervisor启动uwsgi失败
检查supervisord.conf文件是否配置的有问题。
也要检查uwsgi.ini文件的配置,其中uwsgi.ini中的daemonize
参数和supervisor有冲突,不能打开:
# daemonize = /var/log/uwsgi.log
即删除"uwsgi.ini"文件中的"daemonize"项即可。
静态文件加载成功,但页面中不生效
这个问题困扰了我很久,具体效果就是js、css都收集成功,前端也能正常的加载,但页面渲染时就是没有效果!!
搞得我一直去检查我的静态文件收集相关的配置,没注意到这两个参数:
http {
# 主要是声明include参数
include mime.types;
# 这个参数注释掉也不影响,但也给它放开吧
default_type application/octet-stream;
}
参考:https://www.365seal.com/y/75pAow1avo.html
静态文件加载不上
这是另一个静态文件加载不上的案例和解决办法。
就是nginx配置的问题:
静态文件配置无误,浏览器访问nginx,静态文件加载报403 forbidden
python3.10 + nginx + uwsgi + django3.2 + mysql5.7 + centos7
报错环境:uwsgi配置、nginx静态文件代理都正常,前端页面访问,静态文件包403 forbidden。
原因,可能是由于启动用户和nginx工作用户不一致所致。
来看nginx工作进程启动的用户是谁:
[root@VM-0-12-centos day03]# ps aux|grep nginx
root 18318 0.0 0.1 39440 2056 ? Ss Aug31 0:00 nginx: master process /usr/sbin/nginx
root 21927 0.0 0.1 41964 2524 ? S 11:47 0:00 nginx: worker process
root 21928 0.0 0.1 41964 2532 ? S 11:47 0:00 nginx: worker process
root 23845 0.0 0.0 112812 968 pts/5 S+ 11:47 0:00 grep --color=auto nginx
可以看到是nginx进程是以root账号启动的。
来看NGINX的配置文件中,NGINX的工作用户是谁:
[root@VM-0-12-centos day03]# head -n 5 /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
可以看到user nginx;
是NGINX用户,所以和工作进程的root用户不一致导致的。
具体的解决办法就是修改NGINX的配置文件的工作用户:
[root@VM-0-12-centos day03]# vim /etc/nginx/nginx.conf
# 就是修改第一行内容如下
user root;
# 完事之后,平滑重启NGINX
[root@VM-0-12-centos day03]# nginx -s reload
前端访问就好了:
参考:https://blog.csdn.net/m0_67403188/article/details/126113702
NGINX + uwsgi配置之后,登录过程中找不到session
NGINX + uwsgi + Django3.2
无论是在本地还是在云服务器上,通过python manage.py runserver
运行测试项目,session都没有问题。但只要结合上uwsgi就,从session中就拿不到数据了。
经过排查,发现是settings中的关于session的配置是有问题:
# session配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7
这么配置任谁看了也说不出毛病,将session缓存到内存中,过期时间7天!
但问题肯定是有的,如果不是结合Redis作为缓存,那么这里指定cache则用的是内存缓存,本地开发没问题,因为本地开发单进程,独享内存资源,也能访问到内存中的session缓存。但通过uwsgi部署就不行了,多进程中,就找不到这个session缓存了,我亲测发现的问题。所以,如果想要使用cache作为session的内存缓存,则搭配Redis使用,否则,就老老实实的用那个db缓存就好了,uwsgi中使用也正常。
最终你可以有两种方案,如果有Redis的话,settings中这么配置:
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:@127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "123456",
"CONNECTION_POOL_KWARGS": {
"max_connections": 100,
},
}
},
# 提供存储短信验证码
"sms_code": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:@127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"PASSWORD": "123456",
}
}
}
# session配置
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7
如果你没有Redis,那么你就别用内存缓存了,用db吧,settings中这么配置:
# session配置
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
SESSION_CACHE_ALIAS = 'default'
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7