为了避免被设为VIP导致用户无法浏览,该文章迁移到
https://gitee.com/silent_missile/technique_note
在Debian服务器上部署一个自己开发的网站,网站比较复杂,用到了很多组件,这些组件如标题所示,需要一步步进行部署。这里把我摸索的步骤记录下来,以供未来参考
数据库服务
首先是数据库服务,因为数据库服务不依赖其他服务。
我的站点因功能原因,写和读的量差不多,而且访问量也不大,所以选择了postgresql
首先是安装,Debian自带了postgresql的安装包,虽然不是最新版本,但是绝大多数情况足够用了,直接apt安装就可以。
Debian安装的postgresql默认是没有密码的,但是没有密码却无法登录,所以直接用数据库管理员权限强行登陆后再修改设定密码。在此之前,必须要让postgresql初始化生成基本的数据目录和配置文件
sudo service postgresql start
新系统首次运行之后,基本的数据目录和配置文件就生成了。
postgresql安装好之后默认的密码验证机制是peer验证,除非有另外的id服务器,否则我们一般都是本地验证的。所以打开
/etc/postgresql/pg_hba.conf
拉到最下面,把几处peer和scram-sha-256更改为trust,就可以无密码登录
sudo service postgresql restart
在Debian中,postgresql服务是由postgres用户发起的daemon,而postgresql数据库初始化后默认的管理员账户就是postgres
sudo -u postgres psql
然后用命令
\password
修改postgresql服务器中存储的管理员账户postgres的密码。修改好以后
\q
退出。
然后再次打开
/etc/postgresql/pg_hba.conf
拉到最下面,把几处trust更改为scram-sha-256,然后重新启动postgresql服务器,把认证替换为本地sha-256认证
sudo service postgresql restart
重启postgresql服务,再在bash中
psql -U postgres
按照提示输入刚刚设定的密码,以数据库管理员身份登录数据库服务器。
在Linux中,postgres这个账户是操作系统的账户,一般被设定为不可登陆,所以仅能通过
su -u postgres somecommand
用root密码来通过root获得postgres用户的权限。
以postgres用户登陆到数据库服务中,然后以这个状态直接来进行后续操作,比如说创建新的数据库用户。当然,可以把新创建的这个用户赋予数据库管理员权限。甚至还可以在操作系统里创建用户,并用操作系统中的这个用户来创建数据库系统的用户,再把这个用户赋予数据库管理员的权限,以此屏蔽默认的postgres用户,一定程度上可以避免黑客猜出来数据库管理员的用户名。
然后创建普通用户和对应的数据库,这里为了凸显通用性,用户名和数据库名是不同的,不过实践中,用户名和数据库名一般是相同的。
postgres=# create user username with password '****';
CREATE ROLE
postgres=# create database dbtest owner username; -- 创建数据库指定所属者
CREATE DATABASE
postgres=# grant all on database dbtest to username; -- 将dbtest所有权限赋值给username
GRANT
postgres=#
需要注意:
1. 要以英文分号结尾
2.密码需要引号包裹
然后\q退出,再以刚刚创建的用户登录上来
psql -U username
看看用户是否已经生效。
redis缓存服务器
和数据库一样redis缓存服务器也不依赖其他的服务。Debian也有redis的包,直接apt安装即可
默认情况下,redis服务是开机启动的,而且没有密码,所以如果没有特别的安全性要求,可以直接用,不过既然我把所有的服务都放到一台服务器上,用户既然能够访问网关,自然也就能够访问redis服务。所以肯定不能这样暴露,密码一定要设置。打开编辑redis的配置文件
sudo vi /etc/redis/redis.conf
找到requirepass的那一行,默认是注释掉的,把注释取消掉,然后修改为
requirepass redispassword
另外阿里云的Debian在申请时可以设定是否ipv6,如果仅仅是ipv4,没有v6,那么需要在配置文件中找到bind行修改
# bind 127.0.0.1 ::1
bind 127.0.0.1
然后重启redis服务器
sudo service redis-service restart
如果还有问题,那就需要把bind这一行注释掉,启动redis服务之后可以再改回来127.0.0.1,然后再重启redis
注意,redis服务的名字不是redis,而是redis-server
想查看系统中到底安装了哪些服务,或者说想知道那个服务到底叫什么名字
sudo ls /etc/init.d/
virtualenv设置
因为Debian比较保守,自带的软件包都比较古老,所以如果需要较好的开发、运行环境,还是要自己手动安装一些新的软件,最好就是创建virtualenv,然后在这个虚拟幻境里,通过pip安装各种新版本的软件
通过命令
mkdir your_virtualenv_dir # 创建目录,一般我会把所有的虚拟环境放在这个目录里
cd your_virtualenv_dir
virtualenv your_env_name -p /usr/bin/python3 # 新的virtualenv会存放在your_virtualenv_dir/your_env_name,也可以指定其他的python
就可以创建一个虚拟环境
LD_LIBRARY_PATH设置
因为Debian自带的包比较老,所以我会自己编译一些新的包,这些包因为不是用deb安装的,所以一般我会把它们安装在your_virtualenv_dir/your_env_name。这会导致一些新的问题,虽然PATH在activate中设置了,但是LD_LIBRARY_PATH并没有在activate中设置。
我会自己编译安装opencascade和pythonocc-core,而且都是安装在your_virtualenv_dir/your_env_name目录,如果不设置LD_LIBRARY_PATH,pythonocc-core就无法找到opencascade的so
所以需要调整LD_LIBRARY_PATH设置
根据
打开
nano your_virtualenv_dir/your_env_name/bin/activate
然后在deactivate ()函数里面,加入如下代码
if ! [ -z ${_OLD_LD_LIBRARY_PATH+_} ] ; then
LD_LIBRARY_PATH="$_OLD_LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
unset _OLD_LD_LIBRARY_PATH
fi
在deactivate ()函数结束以后,加入如下代码
_OLD_LD_LIBRARY_PATH="$LD_LIBRARY_PATH"
LD_LIBRARY_PATH="$VIRTUAL_ENV/lib:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH
这样,每次进入虚拟环境的时候
source your_virtualenv_dir/your_env_name/bin/activate
就会自动设置LD_LIBRARY_PATH,这样通过编译安装的软件也可以被识别。
pip源设置
官方pip源在国外,访问比较慢,所以采用国内的镜像,比如说南京大学的镜像
source your_virtualenv_dir/your_env_name/bin/activate
pip config set global.index-url https://mirror.nju.edu.cn/pypi/web/simple
pip install --upgrade pip
安装一些python包
科学计算包
对于numpy、scipy这种科学计算的python包,使用更高效的blas库可以有效提高运行速度,所以我一般会从源代码安装,在编译的时候可以让它去找高速blas,从而加快运行速度
首先安装高速blas,可以用命令行
sudo apt-get install libopenblas-dev
也可以用图形化的synaptic,如果synaptic运行出了问题
sudo rm /root/.synaptic/ -rf
然后重新启动synaptic就可以了
安装好高速blas以后把这个高速blas设置为默认的,替代系统原本的默认的比较慢的
sudo update-alternatives --all
有很多可以选的,遇到了blas就选openblas就切换过来了
然后从源代码编译安装科学计算包
pip install numpy --no-binary numpy
pip install scipy --no-binary scipy
过程中会自动编译、自动安装
如果提示
Did not find CMake 'cmake'
Found CMake: NO
Run-time dependency xsimd found: NO (tried pkgconfig and cmake)
Run-time dependency threads found: YES
Library npymath found: YES
pybind11-config found: YES (/tmp/pip-build-env-r96k30hw/overlay/bin/pybind11-config) 2.12.1
Run-time dependency pybind11 found: YES 2.12.1
Program f2py found: YES (/tmp/pip-build-env-r96k30hw/overlay/bin/f2py)
Run-time dependency scipy-openblas found: NO (tried pkgconfig)
Run-time dependency openblas found: NO (tried pkgconfig and cmake)
Run-time dependency openblas found: NO../scipy/meson.build:216:9: ERROR: Dependency lookup for OpenBLAS with method 'pkgconfig' failed: Pkg-config for machine host machine not found. Giving up.
安装cmake就可以
安装psycopg
Installation - psycopg 3.2.3.dev1 documentation
psycopg前两年升级到了3.x,从3.x开始可以从源代码安装
pip install psycopg[C]
如果非要从二进制安装也可以
pip install psycopg[binary]
其实编译安装也花不了多少时间,编译时间也就是几分钟而已。如果编译过程提示找不到postgresql的头文件,就把postgresql-server-dev包用apt装上
其他包
除了上面介绍的包需要从源代码安装,要额外注意,其他的包基本上直接安装就可以,对于Django开发运行而言,大体上常用的包如下
pip install django redis pylint pylint-django autopep8 gunicorn gevent greenlet supervisor
django设置
redis设置
在Django-4.x之后,redis访问已经被集成到Django里面了,不需要import
不过需要用户自己安装一个包
pip install redis
在用户开发的Django站点程序中,把redis的密码加入settings.py
Django 缓存框架 | Django 文档 | Django
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": ["redis://:你的密码@服务器地址:6379/0"]
}
}
“你的密码”自然就是前面设置的redis密码,“服务器地址”这个选项,则是因为redis可以通过网络远程访问,如果所有的服务都设置在一台服务器上,那么直接写localhost即可
静态资源设置
根据django的官方文档
管理静态文件(比如图片、JavaScript、CSS) | Django 文档 | Django
尽管django自带的开发服务器程序支持静态文件,但是官方不鼓励在生产环境通过django来访问静态文件
通过STATIC_URL这个变量可以用于在程序中为涉及到静态文件的地方做相关配置,这是用于生成访问静态文件的url的,浏览器得到这个url后,会再访问nginx来请求静态文件,此时就不需要django来处理了
通过django.contrib.staticfiles这个类提供了一些处理静态文件的工具,这个工具通过命令
python manage.py collectstatic
可以把项目相关的静态文件全部收集到STATIC_ROOT这个变量指定的目录里,以供生产环境部署,也就是复制到nginx配置文件中指定的那个"/static"所alias的目录下,其中"/static"对应STATIC_URL,alias目录对应STATIC_ROOT,这样所有的对静态文件的访问就都通过nginx来处理,不需要django来运算
根据官方文档
The staticfiles app | Django 文档 | Django
在urls.py中增加
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
# ... the rest of your URLconf here ...
urlpatterns += staticfiles_urlpatterns()
可以让django提供访问静态文件的能力,不过官方还是不推荐这么做。经过实践检验,gunicorn搭配这种配置不能提供静态文件的访问功能,至少不能提供django-admin模块所需的静态文件如css之类的访问功能。
所以没必要调整django来支持静态文件的访问,而是在nginx中配置,"/static"对应STATIC_URL,alias目录对应STATIC_ROOT就好。
不过对于前后端分离的项目,前后端通过json交换信息,前端通过js进行呈现,一般不需要访问Django的静态资源,静态资源都是前端开发时就处理掉了。
gunicorn服务
gunicorn使用非常简单,cd进入django程序目录后
gunicorn -w 进程数量 -b ip:端口 -k 运行模式 项目名.wsgi:application
# 样例
gunicorn -w 2 -b localhost:8080 -k gevent teacher.wsgi:application
gevent模式响应比较快,而gevent又依赖greenlet
老版的gunicorn要求用户在最后给的运行程序一定要加上application类名,不过新版已经不需要了,只要写到wsgi就可以了
当然也可以创建配置文件,然后用-c参数读入配置文件来启动
gunicorn mysite:wsgi -c gunicorn_config.py
supervisor监控程序
如果gunicorn进程意外中断,会导致网站访问受阻。为了避免这个问题,可以使用supervisor监控,一旦gunicorn进程终端,supervisor会自动再次启动gunicorn
https://www.jianshu.com/p/bbd0b4cfcac9
针对自己的网站程序编辑一个配置文件
[program:web]
command=gunicorn -w 4 -b 127.0.0.1:8000 -k gevent webProgram.wsgi:application
directory=/home/user/webProgram
autostart=true
再把这个配置文件放置到
/home/username/anaconda3/envs/yourenv/etc/supervisord/conf.d/
目录下,这样在运行anaconda安装的supervisor时自然就会载入了
我试过编辑一个supervisor的配置文件,然后通过
supervisord -c custome.conf
来借助自定义的配置文件启动,但是实践表明,这个anaconda自带的supervisor程序还是会读取pyenv环境下的配置文件,而不是读取自定义的配置文件。所以只能通过这种方式来增加配置。另外即便采用
[include]
file=/home/username/anaconda3/envs/yourenv/etc/supervisord/supervisord.conf
那么在采用supervisor -c命令来载入,会提示没有supervisord段,启动出错
经过实践检验,使用anaconda自带的supervisor,把网站个性配置文件放到conf.d目录下是唯一成功的启动方法
进入anaconda环境后,直接使用命令
supervisord
即可启动,还可以使用
supervisorctl restart webProgram
或者其他supervisorctl命令来对supervisor服务进行调用
nginx转发配置和django路由配置的协调
外网用户访问时正常访问到80端口,nginx会监听这个端口,然后根据用户请求分别转发给gunincorn的django程序或者直接从硬盘上读取静态文件,也可以转发给其他服务程序比如php-fpm。作为网关nginx性能很好,为后面的其他服务程序提供了不错的屏蔽,提高了服务器的安全性。
/etc/nginx/nginx.conf文件是nginx的主配置文件,其中通过include引用了/etc/nginx/esites-enabled这个目录内的文件,而在/etc/nginx/sites-available目录,则存放着各个不同站点的配置文件。通过在/etc/nginx/sites-enabled创建指向/etc/nginx/sites-available目录内的文件,来调整nginx实际运行中的站点。
所以在/etc/nginx/sites-available目录创建配置文件,一般为了方便管理都是把文件名设定成域名
server {
listen 80;
server_name domainname.com;
root /home/developer/mysite/public;
index index.html;
location /api {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Referer $http_referer;
proxy_set_header X-Forwarded-Proto $scheme;
#proxy_redirect localhost/ /api/;
#proxy_redirect ~^/(.+)$ /api/$1;
}
location /static {
alias /home/developer/mysite/static;
}
}
这个配置就是说遇到“/static/...”访问路径时会直接从硬盘上读取/home/developer/mysite/static目录下的文件;而如果遇到了"/api/..."访问路径则会转发给http://localhost:8080也就是gunicorn监听的端口
关于转发规则的匹配,有些地方需要注意
nginx的proxy_pass路径转发规则浅析(末尾/问题)_东372550723的博客-CSDN博客_nginx配置转发路径
location后面的路径是否以“/”结尾,以及proxy_pass是否以“/”结尾都对匹配模式有影响
另外后台的django+gunicorn返回的内容中也要注意,如果仅仅是返回json,那不需要担心,浏览器直接处理即可
如果返回的是html页面内容,其中还包含了链接,尤其是涉及到css、js、图片等自动加载的链接,浏览器还会再次访问这个链接,如果这个链接拼接的有问题,就会在载入的时候出错。
在django中,通过配置STATIC_URL可以控制其生成的html页面中包含的静态资源的路径,如果这个路径和nginx的配置一致,不论是访问到nginx server root配置的目录下的静态文件,还是访问到location /static目录下的文件,都可以正常使用。但如果这个链接是访问到其他目录就会出问题。
如果django返回的是HTTP 302 redirect重定向,nginx对此提供了proxy_redirect配置参数来控制,可以通过字符串匹配来处理,也可以使用正则表达式来匹配。具体可以参考
实际上nginx仅仅会修改响应的Location字段,不会修改response body的内容。所以如果重定向以后返回的内容又包含了html页面内容,那还是要注意内部包含的链接的问题。
我遇到的问题是django-admin页面不能正常工作,因为里面的链接都需要额外处理,而这个处理只能在django完成,nginx是不能处理的。而django-admin又十分封闭
所以最后为了保证访问index时,不会和django的访问api/url冲突,我还是修改了django的url路由配置,尤其是django-admin的部分,给它们统统加上了"/api"前缀。
对于django而言,其url为
http://domainname/url/param/
红色的部分。这部分却不会直接和urls.py中的urlpatterns匹配,而是要去掉最前面的'/',用剩下的url/param/来匹配urlpatterns
所以可以在django中添加一个BASE_URL为'api/',这个变量用于匹配urlpatterns,所以没有起始的'/',然后在根目录的urls.py中的urlpatterns里都加上BASE_URL前缀,其他的url通过include作为根目录下url的子集,就可以确保从nginx传递过来的url和django返回的带有链接的html内容都没有问题。如果仅考虑静态文件,通过django的STATIC_URL来设置也可以,不过对于django-admin却会出问题,为了保证django-admin不出问题,还是用这个方法比较稳妥。
另外在其他代码——最常见的是权限控制——中涉及到url访问权限的判别,一定要用红色的带有起始'/'的url——也就是/url/param/——来判别,因为这是常规字符串处理,不是django自己的路由匹配会自动删除起始的'/'
如果需要使用vue中的history模式,那么nginx还要再调整配置,不过我不希望服务器的负载增大,所以关闭了vue的history模式。虽然这样会在浏览器地址栏留下一个难看的#符,不过毕竟减轻了服务器的负担。
如果nginx配置中把django的转发url设定了前缀,那么vue里也要加上这个前缀。vue中涉及到django后台交互的url必须要保留起始的'/',所以前缀是'/api',和django的BASE_URL不同。
把nginx的站点配置文件编辑好后,把/etc/nginx/enabled-site目录下默认的default软连接删除,再创建一个指向刚刚这个配置文件的软连接,否则default可能会拦截请求导致设定的网站访问不到,具体原则则是按照字母顺序,如果新设定的站点字母顺序比default靠前就会被正常访问,否则就会被default拦截。
sudo rm /etc/nginx/sites-enabled/default
sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/example
为了让nginx能够读取静态文件,需要对文件系统权限做些调整。nginx默认的运行用户是www-data,可以修改/etc/nginx/nginx.conf文件,修改user行,修改为静态文件目录的所有人。应该说把静态文件存放目录的访问权限调整为www-data也可访问也是可以的,不过未来如果再有新的文件或者目录,调整起来会非常困难,所以简便的方法还是替换nginx的启动用户。ubuntu没有selinux,如果是红帽系的带了selinux,那修改目录的用户权限会更难,还是替换nginx的启动用户简便一些。不过安全性上稍微差一些,主要差异就在于目录所有人是可以登录的,但是www-data用户是不可以登录的,所以一旦用户登录信息被窃取,损失比www-data用户要大。
然后测试一下nginx的配置文件
nginx -T # 检查nginx语法问题
如果有问题会提示具体在哪一行,没问题就通过。如果提示的问题是访问权限,可以用sudo临时用户测试一下
sudo -u correctuser nginx -T
因为nginx测试的时候是不会用配置文件里的user行的内容来测试的,而是用当前用户来测试的。
通过以后重新启动nginx,使新配置生效
sudo systemctl restart nginx.service
此时通过浏览器访问
http://localhost/api
就能看到用户请求通过nginx转发给gunicorn运行的django网站程序,然后程序返回的数据通过gunicorn和nginx返回浏览器。
本文详述在Ubuntu服务器上部署复杂网站的全过程,包括PostgreSQL数据库、Redis缓存、Gunicorn应用服务器、Supervisor进程监控及Nginx反向代理的配置,解决跨服务依赖、权限设置与静态资源管理等问题。

2898

被折叠的 条评论
为什么被折叠?



