项目部署 nginx + uwsgi

Posted addit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了项目部署 nginx + uwsgi相关的知识,希望对你有一定的参考价值。

1.项目部署的相关概念

关于部署流程,主要包含以下两个方面:部署方案、部署环境:

1.1部署方案

1. 分析项目的产品需求文档,定好部署方案的方向
2. 分析项目开发文档,按照功能边界,设计部署的结点
3. 分析项目功能软件,合理的取舍,选符合当前业务场景的
4. 梳理项目部署涉及到的部署软件实现方案,根据上面第2点确定的结点,确定初版部署方案
5. 根据项目实际情况,调整优化并确定项目部署方案

1.2部署环境

1.2.1个人开发环境:

工作人员:自己
工作平台:个人笔记本、公司配的电脑
平台特点:环境是自己配的,团队中不同的个人开发环境可以不一样
工作内容:项目的子模块,子功能
完成标准:完成领导安排的内容[项目的功能子模块开发]

1.2.2公司开发环境:

工作人员:开发团队
工作平台:公司内部服务器
平台特点:服务器环境和线上的服务器环境完全一致
工作内容:项目子模块间的功能联调
完成标准:项目阶段开发、调试完成

1.2.3项目测试环境:

工作人员:测试团队
工作平台:公司内部服务器
平台特点:服务器环境和线上的服务器环境完全一致
工作内容:项目功能/非功能/探索等测试
完成标准:项目阶段功能正常运行

1.2.4项目预发布环境:

工作人员:运维团队
工作平台:公司线上服务器组中的一台
平台特点:服务器环境和线上的服务器环境完全一致
工作内容:特殊功能测试(比如支付)、数据压力测试、其他安全测试等
完成标准:项目阶段功能正常运行,最后一道防线

1.2.5项目生产环境:

工作人员:运维团队
工作平台:公司线上服务器组
平台特点:标准线上的服务器环境
工作内容:代码部署和维护、记录内部架构文档
完成标准:项目正常运行

项目整体架构

技术图片

 

 

服务器部署

服务器数量: 1000QPS 每秒请求数

服务器类型:物理服务器,云服务器

并发瓶颈:

1. 带宽:200M/10K
2. 处理并发的能力
3. 数据库查询能力:mysql 1000次/S

可以使用apache ab工具进行压测

 

2.前端项目部署

将前后端项目上传码云

技术图片

 

 2.1第一步:编译。在本机电脑上找到前端项目位置

 自动化程序会将打包的文件自动生成到项目的dist文件夹中。

npm run build

注意,编译的文件,必须要通过http协议才能访问到,直接点击index.html,是无法访问的。

因为根据我们上面的部署方案,我们需要安装nginx来运行这个项目。

 nginx部署有两种:

  1).部署到云服务器中

  2).部署到晕服务中的docker容器里面

此项目直接将nginx部署到服务器中

2.2部署nginx

2.2.1 .安装nginx

sudo apt-get install nginx

2.2.2 修改nginx配置文件  default

技术图片

 upstream luffy                  # 反向代理池
    server 106.52.122.145:8000;   

# nginx 反向代理uwsgi server listen
80; server_name 106.52.122.145 api.additwujiahua.com; # location /
     # 通过uwsgi_pass 设置服务器地址和协议,将动态请求转发给uwsgi处理 include uwsgi_params; uwsgi_pass luffy; location
~ .*\\.(css|js)$ root /home/luffy/; server listen 80; server_name additwujiahua.com www.additwujiahua.com; location / #root /opt/luffyweb/dist/; root /home/luffy-cli/dist; index index.html; try_files $uri $uri /index.html;

# nginx 处理静态叶页面资源
  # 当用户请求的是additwujiahua.com/static这个页面时,会进入这个location匹配
# alias 参数进行路径别名 让nginx去/opt/staic底下去找静态资源
  # location / static
  #  alias /opt/static;
  #

重启nginx

service nginx restart
nignx -s reload

启动nginx服务器

service nginx start

停止nginx服务器

service nginx stop

 

3.后端部署

将mysql和redis部署到 docker容易里面

 3.1 安装docker

更新ubuntu的apt源索引

sudo apt-get update

安装包允许apt通过HTTPS使用仓库

sudo dpkg --configure -a
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common

添加Docker官方GPG key

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

设置Docker稳定版仓库

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

添加仓库后,更新apt源索引

sudo apt-get update

安装最新版Docker CE(社区版)

sudo apt-get install docker-ce

检查Docker CE是否安装正确

sudo docker run hello-world

3.2 docker的相关命令

 安装完成Docker后,默认已经启动了docker服务,如需手动控制docker服务的启停,可执行如下命令

# 启动docker
sudo service docker start

# 停止docker
sudo service docker stop

# 重启docker
sudo service docker restart

# 列出镜像
docker image ls

# 拉取镜像
docker image pull library/hello-world

# 删除镜像
docker image rm 镜像id/镜像ID

# 创建容器
docker run [选项参数] 镜像名 [命令]

# 停止一个已经在运行的容器
docker container stop 容器名或容器id

# 启动一个已经停止的容器
docker container start 容器名或容器id

# kill掉一个已经在运行的容器
docker container kill 容器名或容器id

# 删除容器
docker container rm 容器名或容器id

 

 3.3 接下来尝试将nginx部署到docker容器里

# 在docker中下载nginx镜像
docker image pull nginx

# 使用git把码云上面的编译过的vue项目克隆到服务器/home
# 阿里云服务器中,我们从码云克隆下来的前端项目: /home/luffyproject_pc/dist
git clone 项目git地址

# 创建nginx容器,并以80端口对外提供服务
docker run -itd -p 80:80 -v /home/luffyproject_pc/dist:/usr/share/nginx/html nginx

# 启动nginx容器中的nginx【容器ID通过docker container ls --all可以查看到】
docker container exec -itd [容器ID] nginx

"""
/etc/nginx              # nginx容器中nginx的配置目录
/usr/share/nginx/html   # nginx容器中www目录
"""

 

3.4 将后端项目部署到服务器

3.4.1 

在项目中复制开发配置文件dev.py 到生产配置prod.py

修改配置中的地址相关即可。蓝色部分为修改部分

DEBUG = False

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

import sys

sys.path.insert(0, os.path.join(BASE_DIR, "apps"))

# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = !!t0^(#[email protected][email protected]^!0u5lyq+4ctyui)sxmsc4*[email protected]([email protected]

# SECURITY WARNING: don‘t run with debug turned on in production!
DEBUG = False

ALLOWED_HOSTS = [
    # "api.luffycity.cn",
    # "additwujiahua.com",
    ‘api.additwujiahua.com‘,
]

# CORS组的配置信息
CORS_ORIGIN_WHITELIST = (
    ‘www.additwujiahua.com‘
    ‘additwujiahua.com:80‘
)
CORS_ALLOW_CREDENTIALS = True  # 允许ajax跨域请求时携带cookie

# Application definition

INSTALLED_APPS = [
    django.contrib.admin,
    django.contrib.auth,
    django.contrib.contenttypes,
    django.contrib.sessions,
    django.contrib.messages,
    django.contrib.staticfiles,
    # 跨域
    corsheaders,

    # drf
    rest_framework,

    # xadmin
    xadmin,
    crispy_forms,
    reversion,

    # 富文本编辑器
    ckeditor,  # 富文本编辑器
    ckeditor_uploader,  # 富文本编辑器上传图片模块

    # 子应用
    home,
    "users",
    "courses",
    "cart",
    orders,
    "coupon",
    "payments"
]

MIDDLEWARE = [
    corsheaders.middleware.CorsMiddleware,
    django.middleware.security.SecurityMiddleware,
    django.contrib.sessions.middleware.SessionMiddleware,
    django.middleware.common.CommonMiddleware,
    django.middleware.csrf.CsrfViewMiddleware,
    django.contrib.auth.middleware.AuthenticationMiddleware,
    django.contrib.messages.middleware.MessageMiddleware,
    django.middleware.clickjacking.XFrameOptionsMiddleware,
]

ROOT_URLCONF = luffy.urls

TEMPLATES = [
    
        BACKEND: django.template.backends.django.DjangoTemplates,
        DIRS: [],
        APP_DIRS: True,
        OPTIONS: 
            context_processors: [
                django.template.context_processors.debug,
                django.template.context_processors.request,
                django.contrib.auth.context_processors.auth,
                django.contrib.messages.context_processors.messages,
            ],
        ,
    ,
]

WSGI_APPLICATION = luffy.wsgi.application

# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = 
    "default": 
        "ENGINE": "django.db.backends.mysql",
        "HOST": "127.0.0.1",
        "PORT": 3306,
        "USER": "luffy_user",
        "PASSWORD": "luffy",
        "NAME": "luffycity",
    


# Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    
        NAME: django.contrib.auth.password_validation.UserAttributeSimilarityValidator,
    ,
    
        NAME: django.contrib.auth.password_validation.MinimumLengthValidator,
    ,
    
        NAME: django.contrib.auth.password_validation.CommonPasswordValidator,
    ,
    
        NAME: django.contrib.auth.password_validation.NumericPasswordValidator,
    ,
]

# Internationalization
# https://docs.djangoproject.com/en/2.2/topics/i18n/

LANGUAGE_CODE = zh-hans

TIME_ZONE = Asia/Shanghai

USE_I18N = True

USE_L10N = True

USE_TZ = False

# Static files (CSS, javascript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

# 访问静态文件的url地址前缀
STATIC_URL = /static/
# 设置django的静态文件目录
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "luffy/statics")
]


# 项目中存储上传文件的根目录[暂时配置],注意,static目录需要手动创建否则上传文件时报错
MEDIA_ROOT = os.path.join(BASE_DIR, "luffy/statics")
# 访问上传文件的url地址前缀
MEDIA_URL = "/media/"

# 缓存配置
CACHES = 
    "default": 
        "BACKEND": "django_redis.cache.RedisCache",
        # 项目上线时,需要调整这里的路径
        "LOCATION": "redis://127.0.0.1:6379/0",

        "OPTIONS": 
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        
    ,

    "session": 
        "BACKEND": "django_redis.cache.RedisCache",
        # 项目上线时,需要调整这里的路径
        "LOCATION": "redis://127.0.0.1:6379/1",

        "OPTIONS": 
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        
    ,

    # 提供存储短信验证码
    "sms_code": 
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/2",
        "OPTIONS": 
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        
    ,

    # 储存购物车信息
    "cart": 
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379/3",
        "OPTIONS": 
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
        
    ,



# 设置xadmin用户登录时,登录信息session保存到redis
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"

# 日志配置
LOGGING = 
    version: 1,
    disable_existing_loggers: False,
    formatters: 
        verbose: 
            format: %(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s
        ,
        simple: 
            format: %(levelname)s %(module)s %(lineno)d %(message)s
        ,
    ,
    filters: 
        require_debug_true: 
            (): django.utils.log.RequireDebugTrue,
        ,
    ,
    handlers: 
        console: 
            level: DEBUG,
            filters: [require_debug_true],
            class: logging.StreamHandler,
            formatter: simple
        ,
        file: 
            level: DEBUG,
            class: logging.handlers.RotatingFileHandler,
            # 日志位置,日志文件名,日志保存目录必须手动创建
            filename: os.path.join(os.path.dirname(BASE_DIR), "logs/luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            maxBytes: 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            backupCount: 10,
            # 日志格式:详细格式
            formatter: verbose
        ,
    ,
    # 日志对象
    loggers: 
        django: 
            handlers: [console, file],
            propagate: True,  # 是否让日志信息继续冒泡给其他的日志处理系统
        ,
    


REST_FRAMEWORK = 
    # 异常处理
    EXCEPTION_HANDLER: luffy,utils.exceptions.custom_exception_handler,
    #  登录认证
    DEFAULT_AUTHENTICATION_CLASSES: (
        rest_framework_jwt.authentication.JSONWebTokenAuthentication,
        rest_framework.authentication.SessionAuthentication,
        rest_framework.authentication.BasicAuthentication,
    ),


# 保存时间1天
import datetime

JWT_AUTH = 
    JWT_EXPIRATION_DELTA: datetime.timedelta(days=1),
    # 设置jwt登录视图的返回值
    JWT_RESPONSE_PAYLOAD_HANDLER: users.utils.jwt_response_payload_handler,


# 告诉django我们使用自己的用户认证组件
AUTH_USER_MODEL = users.User

# 配置django的认证类功能,增加手机号登录
AUTHENTICATION_BACKENDS = [
    users.utils.UsernameMobileAuthBackend,
]

PC_GEETEST_ID = 81c4fd5a59bfd9da92796db7a45cce3e
PC_GEETEST_KEY = 1e4a137ed379d88ef171c0ac8d7b927f

# 短信配置
# 主账号
SMS_ACCOUNTSID = "8aaf07086ab0c082016ab3eda41a00bd"

# 主账号Token
SMS_ACCOUNTTOKEN = "29c414dd7446457d8f4a0678c6a2de86"

# 创建应用的APPID
SMS_APPID = "8aaf07086ab0c082016ab3eda47100c3"

# 说明:请求地址,生产环境配置成app.cloopen.com
SMS_SERVERIP = "sandboxapp.cloopen.com"

# 富文本编辑器ckeditor配置
CKEDITOR_CONFIGS = 
    default: 
        toolbar: full,  # 工具条功能
        height: 300,  # 编辑器高度
        # ‘width‘: 300,     # 编辑器宽
    ,

CKEDITOR_UPLOAD_PATH = ‘‘  # 上传图片保存路径,留空则调用django的文件上传功能

# 保利威视频加密服务
POLYV_CONFIG = 
    "userId": "00573fd073",
    "secretkey": "m2Xc6C6Fb0",
    "servicesUrl": "https://hls.videocc.net/service/v1/token",


# 支付宝
ALIPAY_APPID = "2016093000628031"  # 应用ID
APP_NOTIFY_URL = None  # 应用回调地址[支付成功以后,支付宝返回结果到哪一个地址下面]
ALIPAY_DEBUG = True
# APIPAY_GATEWAY="https://openapi.alipay.com/gateway.do"
APIPAY_GATEWAY = "https://openapi.alipaydev.com/gateway.do"
ALIPAY_RETURN_URL = "http://www.additwujiahua.com/success"
# 支付宝会根据API中商户传入的notify_url,通过POST请求的形式将支付结果作为参数通知到商户系统
ALIPAY_NOTIFY_URL = "http://api.additwujiahua.com:8000/payments/success"

 

3.4.2 上线前先收集静态文件static

当Django运行在生产环境中,我们会关闭debug调试,那么项目将不再提供静态文件的支持,需要将静态文件交给静态文件的nginx服务器来提供访问。

我们先收集所有静态文件。项目中的静态文件除了我们使用的上传文件之外,django本身还有自己的静态文件,如rest_framework、xadmin、admin、ckeditor等。我们需要收集这些静态文件,集中一起放到静态文件服务器中。

我们要将收集的静态文件放到项目的static目录中,所以先创建目录static。

Django提供了收集静态文件的方法。先在配置文件中配置收集之后存放的目录

settings/prop.py,代码:

STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR), static)

然后执行收集命令

python manage.py collectstatic

将 static文件 上传到码云

 

3.5 django的程序通常使用uwsgi服务器来运行

3.5.1修改wsgi.py文件

os.environ.setdefault(DJANGO_SETTINGS_MODULE, luffy.settings.prop)

我们可以在本地环境中先安装uwsgi模块进行使用,然后成功了再上传到服务器中。

pip install uwsgi
uwsgi --version    # 查看 uwsgi 版本

在项目根目录下创建uwsgi配置文件uwsgi.ini

[uwsgi]
#使用nginx连接时使用,Django程序所在服务器地址
;socket=127.0.0.1:8000
#直接做web服务器使用,Django程序所在服务器地址
http=127.0.0.1:8000
#项目目录
chdir=/home/moluo/Desktop/luffy
#项目中wsgi.py文件的目录,相对于项目目录
wsgi-file=luffy/wsgi.py
# 进程数
processes=2
# 线程数
threads=2
# uwsgi服务器的角色
master=True
# 存放进程编号的文件
pidfile=uwsgi.pid
# 日志文件,因为uwsgi可以脱离终端在后台运行,日志看不见。我们以前的runserver是依赖终端的
daemonize=uwsgi.log
# 指定依赖的虚拟环境
virtualenv=/home/moluo/.virtualenvs/luffy

注意:云服务器里面注意修改路径!!!!

启动uwsgi服务器,必须在uwsgi配置文件中运行下面的命令

注意,在本地环境中必须先把runserver关闭

uwsgi --ini uwsgi.ini

停止uwsgi服务器,还可以通过使用kill命令停止

uwsgi --stop uwsgi.pid
uwsgi -9 进程号

 

3.6 本地配置完后部署到服务器并使用uwsgi服务器运行

3.6.1.导出当前虚拟环境中的模块包列表

pip freeze > requirements.txt

3.6.2.在服务器中使用git拉取最新的项目内容。

cd /home/luffyproject
git pull

3.6.3 安装基本虚拟环境[直接把项目运行在服务器中,不在容器里面]

# 安装python3的pip工具
apt install python3-pip
# 使用pip安装虚拟环境
pip3 install virtualenv
pip3 install virtualenvwrapper

# 配置虚拟环境的环境变量
# 执行命令
mkdir $HOME/.virtualenvs

# 执行命令,打开并编辑 ~/.bashrc
vim  ~/.bashrc

# 文件末尾添加以下几行代码,:wq 保存退出。
export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh

# 刷新配置文件
source ~/.bashrc

# 创建虚拟环境
mkvirtualenv luffyproject -p python3

# 在虚拟环境中同步之前的个人开发环境中安装包列表 requirements.txt
cd /home/luffy/docs
pip install -r requirements.txt -i https://pypi.douban.com/simple

# 项目中
pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2

# 安装完成这些手动安装的包以后,我们再次执行上面的命令
pip install -r requirements.txt -i https://pypi.douban.com/simple
    
# 经过上面的步骤,我们直接切到uwsgi.ini所在目录下,运行项目
uwsgi --ini uwsgi.ini

# 我们会发现上面命令运行不起来,原因是我们的本地模块包是从新安装的,之前有修改过,所以需要调整。同时因为我们之前已经在项目中使用到了redis和mysql
# 因此我们需要在docker中下载redis和mysql的容器
docker pull redis
docker pull mysql
# 创建redis容器并运行redis
docker container run -itd -p6379:6379 redis
# 创建mysql容器并启动mysql
docker container run -itd -p3306:3306 -e MYSQL_ROOT_PASSWORD=luffy mysql:5.7

# 接下来远程在本地环境中导出当前项目的数据库,并导入到服务器中的数据库中
# 1. 本地导出数据
cd ~/Desktop/luffy/docs/
mysqldump -uroot -p123 luffycity>./luffycity.sql  # 本地数据库账号
# 2. 在服务器中创建数据和数据库用户
mysql -uroot -pluffy -h47.112.204.45  # 本地登陆线上的mysql数据库
> create database luffycity charset=utf8mb4;
> create user luffy_user identified by luffy;
> grant all privileges on luffycity.* to luffy_user@%;
> flush privileges;
> exit

# 3. 退出服务器数据库终端,使用以下命令导入数据库
cd ~/Desktop/luffy/docs/
mysql -uroot -pluffy -h47.112.204.45 luffycity < ./luffycity.sql

# 4. 继续使用manage.py运行django项目,修改数据库的decode错误以后,就正常运行。
  第一个错误:注释掉对应的错误
  第二个错误:加上decode() # 5. 使用uwsgi来运行django项目 uwsgi --ini uwsgi.ini # 新的配置信息 uwsgi.ini [uwsgi] #使用nginx连接时使用,Django程序所在服务器地址 socket=0.0.0.0:8000 #直接做web服务器使用,Django程序所在服务器地址 #http=127.0.0.1:8000 #http=0.0.0.0:8000 #项目目录 chdir=/home/luffy #项目中wsgi.py文件的目录,相对于项目目录 wsgi-file=luffy/wsgi.py # 进程数 processes=1 # 线程数 threads=1 # uwsgi服务器的角色 master=True # 存放进程编号的文件 pidfile=uwsgi.pid # 日志文件,因为uwsgi可以脱离终端在后台运行,日志看不见。我们以前的runserver是依赖终端的 daemonize=uwsgi.log # 指定依赖的虚拟环境 virtualenv=/root/.virtualenvs/luffyproject

 

配置好重启uwsgi即可

 

以上是关于项目部署 nginx + uwsgi的主要内容,如果未能解决你的问题,请参考以下文章

python3 + Django + uwsgi + nginx 配置部署笔记

Flask+uWSGI+Nginx项目部署

使用uwsgi+nginx部署项目

ubuntu18+uwsgi+nginx部署django项目

django 本地项目部署uwsgi+nginx

uwsgi+nginx项目部署