摘要:基于網(wǎng),分享項(xiàng)目的組網(wǎng)架構(gòu)和部署。項(xiàng)目組網(wǎng)架構(gòu)架構(gòu)說(shuō)明流項(xiàng)目訪問(wèn)分為兩個(gè)流,通過(guò)分兩個(gè)端口暴露給外部使用數(shù)據(jù)流用戶(hù)訪問(wèn)網(wǎng)站。通過(guò)進(jìn)行配置,使用作為異步隊(duì)列來(lái)存儲(chǔ)任務(wù),并將處理結(jié)果存儲(chǔ)在中。
基于Raindrop網(wǎng),分享項(xiàng)目的組網(wǎng)架構(gòu)和部署。
項(xiàng)目組網(wǎng)架構(gòu) 架構(gòu)說(shuō)明 1.流項(xiàng)目訪問(wèn)分為兩個(gè)流,通過(guò)nginx分兩個(gè)端口暴露給外部使用:
數(shù)據(jù)流:用戶(hù)訪問(wèn)Raindrop網(wǎng)站。
控制流:管理人員通過(guò)supervisor監(jiān)控、管理服務(wù)器進(jìn)程。
2.nginx圖中除了將程序部署在ECS上外,還使用了OSS(對(duì)象存儲(chǔ)服務(wù),可以理解成一個(gè)nosql數(shù)據(jù)庫(kù)),主要是為了存放一些靜態(tài)文件,提高訪問(wèn)速度。阿里的OSS還可以與CDN一起使用,同樣可以提高訪問(wèn)速度。
通過(guò)nginx對(duì)外暴露兩個(gè)端口,如上所述,80端口供用戶(hù)訪問(wèn)網(wǎng)站,另一個(gè)端口供管理人員使用。
80端口:根據(jù)請(qǐng)求的url配置了方向代理,分別導(dǎo)向client(angular)和server(flask).
其中server通過(guò)gunicorn部署在[socket]localhost:10000上
配置如下:
server { listen 80 default_server; # set client body size to 4M (add by dh) # client_max_body_size 4M; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; # product root /home/raindrop/www/client/dist; # Add index.php to the list if you are using PHP index index.html index.htm index.nginx-debian.html; server_name _; location / { # First attempt to serve request as file, then # as directory, then fall back to displaying a 404. try_files $uri $uri/ =404; } # access flask static folder location /static/ { # product root /home/raindrop/www/server/app; } location /api/ { proxy_pass http://localhost:10000/api/; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # Error pages error_page 413 @413_json; location @413_json { default_type application/json; return 200 "{"msg": "Request Entity Too Large(max=4M)"}"; } }3.gunicorn
gunicorn作為wsgi容器,用來(lái)執(zhí)行flask server。
gunicorn可以使用異步socket:gevent,其本質(zhì)是基于greenlet實(shí)現(xiàn)協(xié)程的第三方庫(kù),改善io阻塞問(wèn)題,通過(guò)簡(jiǎn)單的配置就能使程序獲得極高的并發(fā)處理能力。
注意:使用gunicorn != 使用gevent,如要開(kāi)啟gevent socket,啟動(dòng)gunicorn時(shí)需要增加--work-class參數(shù),如下:
gunicorn --workers=4 --worker-class socketio.sgunicorn.GeventSocketIOWorker -b localhost:10000 wsgi:app
除了gunicorn,也可以選用uwsgi,但是有兩點(diǎn)限制需要注意:
1.如果使用了Flask-socketio,請(qǐng)不要使用uwsgi,原因
Note regarding uWSGI: While this server has support for gevent and WebSocket, there is no way to use the custom event loop needed by gevent-socketio, so there is no directly available method for hosting Flask-SocketIO applications on it. If you figure out how to do this please let me know!
2.如果使用異步WSGI Server,請(qǐng)勿使用uwsgi,原因:慎用異步 WSGI Server 運(yùn)行 Flask 應(yīng)用
Flask(Werkzeug)的Local Thread無(wú)法與uwsgi基于uGreen的微線程兼容,引起Local Thread工作混亂。4.celery
在程序運(yùn)行過(guò)程中會(huì)有一些比較耗時(shí)但并非緊急的工作,這些任務(wù)可以使用異步任務(wù)來(lái)處理,提高server的響應(yīng)速度。
celery - Distributed Task Queue是一個(gè)第三方庫(kù),提供異步隊(duì)列和任務(wù)處理功能。
Raindrop使用celery配合實(shí)現(xiàn)feed功能。通過(guò)flask進(jìn)行配置,使用redis作為異步隊(duì)列來(lái)存儲(chǔ)任務(wù),并將處理結(jié)果(feed activity)存儲(chǔ)在redis中。
如上所述,我們需要在服務(wù)器上運(yùn)行g(shù)unicorn和celery兩個(gè)進(jìn)程。
顯然,我們需要一個(gè)monitor來(lái)幫我們管理這兩個(gè)進(jìn)程,避免進(jìn)程崩潰不能及時(shí)拉起,阻塞業(yè)務(wù)。
supervisor:Supervisor process control system for UNIX是一個(gè)開(kāi)源monitor程序,并且內(nèi)置了web管理功能,可以遠(yuǎn)程監(jiān)控、重啟進(jìn)程。配置如下:
[inet_http_server] # web 管理端口,通過(guò)nginx暴露給遠(yuǎn)端用戶(hù) port=127.0.0.1:51000 [program:raindrop] # 程序啟動(dòng)命令 command = gunicorn --workers=4 --worker-class socketio.sgunicorn.GeventSocketIOWorker -b localhost:10000 wsgi:app directory = /home/raindrop/www/server user = dh stopwaitsecs=60 stdout_logfile = /var/log/raindrop/supervisor-raindrop.log redirect_stderr = true [program:celery] command = celery -P gevent -A wsgi.celery worker directory = /home/raindrop/www/server user = dh stopwaitsecs=60 stdout_logfile = /var/log/raindrop/supervisor-celery.log redirect_stderr = true6.數(shù)據(jù)庫(kù)
mysql:網(wǎng)站主要數(shù)據(jù)存儲(chǔ)在關(guān)系型數(shù)據(jù)庫(kù)中
redis:緩存mysql數(shù)據(jù) + celery異步隊(duì)列
使用阿里云的服務(wù)器
ECS:部署服務(wù)端程序
OSS:存儲(chǔ)前端靜態(tài)文件(速度很快,對(duì)前端體驗(yàn)改善很大,采用angular框架強(qiáng)烈推薦使用)
nginx: HTTP Server
angularjs: client框架
flask:server框架
gunicorn: web 容器
celery: 異步任務(wù)處理
supervisor: monitor
redis: 數(shù)據(jù)緩存 + 任務(wù)隊(duì)列
mysql: 數(shù)據(jù)庫(kù)
ubuntu12.04 64: 操作系統(tǒng)
virtualenv: python虛擬環(huán)境(隔離項(xiàng)目開(kāi)發(fā)環(huán)境,不用擔(dān)心包沖突了)
vagrant: 基于virtualbox的本地虛擬環(huán)境(環(huán)境可以導(dǎo)入導(dǎo)出,團(tuán)隊(duì)開(kāi)發(fā)必備)
gulp: angular工程打包工具
pip: python包管理工具
fabric: 基于python的遠(yuǎn)程腳本(自動(dòng)化部署神器)
繁瑣VS自動(dòng)化基于Ubuntu 12.0.4,其它系統(tǒng)安裝命令(apt-get 等)請(qǐng)自行修改。
如果是第一次部署,需要初始化ECS服務(wù)器,安裝基本工具:
nginx, supervisor, redis, mysql, pip, virtualenv
打包項(xiàng)目代碼
發(fā)布靜態(tài)文件到OSS服務(wù)器
發(fā)布項(xiàng)目代碼到ECS服務(wù)器,安裝server依賴(lài)的包
修改mysql, nginx, supervisor配置文件
拉起所需進(jìn)程
然后各種apt-get install, scp, tar, cp, mv,不得不說(shuō),這是一個(gè)煩人且毫無(wú)技術(shù)含量的工作,干過(guò)幾次后基本就可以摔鍵盤(pán)了。
不過(guò),有繁瑣的地方,一定有自動(dòng)化。其實(shí)完成上面這些工作,三條命令足以:
fab init_env fab build fab deploy
這要感謝Fabric:Simple, Pythonic remote execution and deployment項(xiàng)目,封裝了非常簡(jiǎn)潔的遠(yuǎn)程操作命令。
Fabric使用Fabric,只需要編寫(xiě)一個(gè)fabile.py腳本,在啟動(dòng)定義init_env, build, deploy三個(gè)任務(wù):
# -*- coding: utf-8 -*- import os, re, hashlib from termcolor import colored from datetime import datetime from fabric.api import * from fabric.contrib.files import exists class FabricException(Exception): pass env.abort_exception = FabricException # 服務(wù)器地址,可以有多個(gè),依次部署: env.hosts = [ "[email protected]" ] env.passwords = { "[email protected]:22":"123456" } # sudo用戶(hù)為root: env.sudo_user = "root" # mysql db_user = "root" db_password = "123456" _TAR_FILE = "raindrop.tar.gz" _REMOTE_TMP_DIR = "/tmp" _REMOTE_BASE_DIR = "/home/raindrop" _ALIYUN_OSS = { "endpoint" : "oss-cn-qingdao.aliyuncs.com", "bucket" : "yourbucketname", "accessKeyId" : "youraccessKeyId" , "accessKeySecret": "youraccessKeySecret" } def build(): """ 必須先打包編譯client,再打包整個(gè)項(xiàng)目 """ with lcd(os.path.join(os.path.abspath("."), "client")): local("gulp build") # 上傳靜態(tài)文件到oss服務(wù)器,并修改index.html中對(duì)靜態(tài)文件的引用 with lcd(os.path.join(os.path.abspath("."), "client/dist")): with lcd("scripts"): for file in _list_dir("./"): if oss_put_object_from_file(file, local("pwd", capture=True) + "/" + file): _cdnify("../index.html", file) with lcd("styles"): for file in _list_dir("./"): if oss_put_object_from_file(file, local("pwd", capture=True) + "/" + file): _cdnify("../index.html", file) # 注意在oss上配置跨域規(guī)則,否則fonts文件無(wú)法加載 # ??!修改fonts文件夾請(qǐng)放開(kāi)此段程序??! # with lcd("fonts"): # for file in _list_dir("./"): # oss_put_object_from_file("fonts/%s" % file, local("pwd", capture=True) + "/" + file) with lcd(os.path.join(os.path.abspath("."), "server")): local("pip freeze > requirements/common.txt") excludes = ["oss", "distribute"] [local("sed -i -r -e "/^.*" + exclude + ".*$/d" "requirements/common.txt"") for exclude in excludes] local("python setup.py sdist") # e.g command: fab deploy:"",Fasle # 注意命令兩個(gè)參數(shù)間不要加空格 def deploy(archive="", needPut="True"): if archive is "": filename = "%s.tar.gz" % local("python setup.py --fullname", capture=True).strip() archive = "dist/%s" % filename else: filename = archive.split("/")[-1] tmp_tar = "%s/%s" % (_REMOTE_TMP_DIR, filename) if eval(needPut): # 刪除已有的tar文件: run("rm -f %s" % tmp_tar) # 上傳新的tar文件: put(archive, _REMOTE_TMP_DIR) # 創(chuàng)建新目錄: newdir = "raindrop-%s" % datetime.now().strftime("%y-%m-%d_%H.%M.%S") with cd(_REMOTE_BASE_DIR): sudo("mkdir %s" % newdir) # 重置項(xiàng)目軟鏈接: with cd(_REMOTE_BASE_DIR): # 解壓到新目錄: with cd(newdir): sudo("tar -xzvf %s --strip-components=1" % tmp_tar) # 保存上傳文件 if exists("www/server/app/static/upload/images/", use_sudo=True): sudo("cp www/server/app/static/upload/images/ %s/server/app/static/upload/ -r" % newdir) sudo("rm -f www") sudo("ln -s %s www" % newdir) with cd(_REMOTE_BASE_DIR): with prefix("source %s/env/local/bin/activate" % _REMOTE_BASE_DIR): sudo("pip install -r www/server/requirements/common.txt") # 啟動(dòng)服務(wù) with cd("www"): # mysql sudo("cp etc/my.cnf /etc/mysql/") sudo("restart mysql") # monitor sudo("cp etc/rd_super.conf /etc/supervisor/conf.d/") sudo("supervisorctl stop celery") sudo("supervisorctl stop raindrop") sudo("supervisorctl reload") sudo("supervisorctl start celery") sudo("supervisorctl start raindrop") # nginx sudo("cp etc/rd_nginx.conf /etc/nginx/sites-available/") # ln -f —-如果要建立的鏈接名已經(jīng)存在,則刪除之 sudo("ln -sf /etc/nginx/sites-available/rd_nginx.conf /etc/nginx/sites-enabled/default") sudo("nginx -s reload") def init_env(): sudo("aptitude update") sudo("aptitude safe-upgrade") # sudo("apt-get install nginx") sudo("aptitude install python-software-properties") sudo("add-apt-repository ppa:nginx/stable") sudo("aptitude update") sudo("apt-get install nginx") sudo("apt-get install supervisor") sudo("apt-get install redis-server") sudo("apt-get install mysql-server") sudo("apt-get install python-pip python-dev build-essential") sudo("pip install virtualenv") run("mkdir /var/log/raindrop -p") with cd ("/home/raindrop"): sudo("virtualenv env") def oss_put_object_from_file(key, file_path): from oss.oss_api import * oss = OssAPI(_ALIYUN_OSS["endpoint"], _ALIYUN_OSS["accessKeyId"], _ALIYUN_OSS["accessKeySecret"]) res = oss.put_object_from_file(_ALIYUN_OSS["bucket"], key, file_path) return res.status == 200 and True or False def _expand_path(path): print path return ""$(echo %s)"" % path def sed(filename, before, after, limit="", backup=".bak", flags=""): # Characters to be escaped in both for char in "/"": before = before.replace(char, r"\%s" % char) after = after.replace(char, r"\%s" % char) # Characters to be escaped in replacement only (they"re useful in regexen # in the "before" part) for char in "()": after = after.replace(char, r"\%s" % char) if limit: limit = r"/%s/ " % limit context = { "script": r""%ss/%s/%s/%sg"" % (limit, before, after, flags), "filename": _expand_path(filename), "backup": backup } # Test the OS because of differences between sed versions with hide("running", "stdout"): platform = local("uname") if platform in ("NetBSD", "OpenBSD", "QNX"): # Attempt to protect against failures/collisions hasher = hashlib.sha1() hasher.update(env.host_string) hasher.update(filename) context["tmp"] = "/tmp/%s" % hasher.hexdigest() # Use temp file to work around lack of -i expr = r"""cp -p %(filename)s %(tmp)s && sed -r -e %(script)s %(filename)s > %(tmp)s && cp -p %(filename)s %(filename)s%(backup)s && mv %(tmp)s %(filename)s""" else: context["extended_regex"] = "-E" if platform == "Darwin" else "-r" expr = r"sed -i%(backup)s %(extended_regex)s -e %(script)s %(filename)s" command = expr % context return local(command) def _cdnify(index_file, cdn_file): sed(index_file, "([^<]*(src|href)=")[^<]*" + cdn_file + """, "1http://" + _ALIYUN_OSS["bucket"] + "." + _ALIYUN_OSS["endpoint"] + "/" + cdn_file + """) def _list_dir(dir=None, access=lcd, excute=local): """docstring for list_dir""" if dir is None: return [] with access(dir): string = excute("for i in *; do echo $i; done", capture=True) files = string.replace(" ","").split(" ") return files
通過(guò)fabric自動(dòng)化部署有兩點(diǎn)需要注意:
1.安裝mysql時(shí),設(shè)置的密碼不能生效,需要登到服務(wù)器上手動(dòng)設(shè)置一下:
mysql -u root use mysql; update user set password=PASSWORD("123456") where User="root"; flush privileges; quit;
2.服務(wù)器上的用戶(hù)需要自己手動(dòng)創(chuàng)建。
gulp在build任務(wù)中,使用gulp build打包c(diǎn)lient的angular代碼。
這里先不多說(shuō),有需要請(qǐng)參考github項(xiàng)目generator-gulp-angular
setup是python自己的打包工具。
build任務(wù)中,最后使用python setup.py sdist命令把整個(gè)工程打包成一個(gè)tar.gz文件,上傳到服務(wù)器。
使用這個(gè)工具需要編寫(xiě)如下兩個(gè)文件:
setup.py:打包腳本
#!/usr/bin/env python from setuptools import setup, find_packages import server try: long_description = open("README.md").read() except: long_description = server.__description__ REQUIREMENTS = [] exclude_lib = ["oss", "distribute"] for lib in open("server/requirements/common.txt").readlines(): for exclude in exclude_lib: if lib.lower() not in exclude: REQUIREMENTS.append(lib) setup( name="raindrop", url="https://www.yudianer.com", version=server.__version__, author=server.__author__, author_email=server.__email__, description=server.__description__, long_description=long_description, license=server.__license__, packages=find_packages(), zip_safe=False, platforms="any", install_requires=REQUIREMENTS )
MANIFEST.in:指定打包哪些文件夾
recursive-include etc * recursive-include client/dist * recursive-include server/app * recursive-include server/requirements * recursive-exclude server *.pyc prune server/app/static/upload/images prune server/env prune server/tests
本文由raindrop網(wǎng)碼農(nóng)撰寫(xiě)。歡迎轉(zhuǎn)載,但請(qǐng)注明出處。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/11719.html
摘要:為了解決骨干網(wǎng)當(dāng)前的問(wèn)題,基礎(chǔ)網(wǎng)絡(luò)團(tuán)隊(duì)在年下半年開(kāi)始,對(duì)新一代骨干網(wǎng)架構(gòu)進(jìn)行重新設(shè)計(jì)硬件選型,在新一代骨干網(wǎng)架構(gòu)設(shè)計(jì)中采用了當(dāng)前比較流行的源路由即技術(shù)以下簡(jiǎn)稱(chēng),在介紹新一代骨干網(wǎng)架構(gòu)之前先給大家簡(jiǎn)單介紹一下技術(shù)的基本概念。前言隨著網(wǎng)絡(luò)技術(shù)的發(fā)展和云計(jì)算業(yè)務(wù)的快速普及,當(dāng)前各行各業(yè)的客戶(hù)都有迫切上云的需求,越來(lái)越多的關(guān)鍵業(yè)務(wù),如:web前端、視頻會(huì)議、辦公應(yīng)用、數(shù)據(jù)存儲(chǔ)等開(kāi)始部署在云端;新的網(wǎng)...
摘要:立即咨詢(xún)產(chǎn)品文檔優(yōu)刻得上線了混合云自建機(jī)房火爆預(yù)售官方補(bǔ)貼活動(dòng)中針對(duì)混合云作了介紹,老劉博客本篇文章分享給大家有關(guān)產(chǎn)品優(yōu)勢(shì)架構(gòu)及與傳統(tǒng)自建私有云的對(duì)比。UCloud混合云UHybrid可提供豐富的IaaS和PaaS產(chǎn)品和專(zhuān)業(yè)的服務(wù),整合UCloud公有云、托管云、私有云和客戶(hù)自有托管IDC等資源,重點(diǎn)解決存量IT資源合理利用,實(shí)現(xiàn)多云互聯(lián)互通,多區(qū)域靈活組網(wǎng);滿足各個(gè)行業(yè)上云業(yè)務(wù)穩(wěn)定,平滑過(guò)...
摘要:架構(gòu)優(yōu)點(diǎn)無(wú)需架線挖槽,快速部署。只有電信部分有權(quán)利在公共場(chǎng)所布設(shè)電纜,而無(wú)線橋接方式則可根據(jù)客戶(hù)需求使用和免許可的頻段靈活定制專(zhuān)網(wǎng)。組網(wǎng)快,支持緊急通信保障。模式作為葉子節(jié)點(diǎn)以型網(wǎng)橋向上連接型網(wǎng)橋。 ...
摘要:在上篇基于技術(shù)構(gòu)建新一代骨干網(wǎng)智能可靠可調(diào)度一中提到了數(shù)據(jù)中心野蠻式增長(zhǎng)給網(wǎng)絡(luò)和骨干網(wǎng)帶來(lái)了極大挑戰(zhàn)以及骨干網(wǎng)的演進(jìn)路線技術(shù)部分原理介紹。介紹完整個(gè)骨干網(wǎng)的架構(gòu)設(shè)計(jì)后,我們將分別針對(duì)骨干網(wǎng)的智能可靠可調(diào)度三大特性進(jìn)行剖析。在上篇《基于Segment Routing技術(shù)構(gòu)建新一代骨干網(wǎng):智能、可靠、可調(diào)度(一)》中提到了UCloud數(shù)據(jù)中心野蠻式增長(zhǎng)給MAN網(wǎng)絡(luò)和骨干網(wǎng)帶來(lái)了極大挑戰(zhàn)以及UCl...
閱讀 942·2021-09-07 09:58
閱讀 1493·2021-09-07 09:58
閱讀 2888·2021-09-04 16:40
閱讀 2508·2019-08-30 15:55
閱讀 2416·2019-08-30 15:54
閱讀 1374·2019-08-30 15:52
閱讀 438·2019-08-30 10:49
閱讀 2610·2019-08-29 13:21