Browse Source

commit1

master
梁辉 4 years ago
parent
commit
893b68db47
20 changed files with 1266 additions and 2 deletions
  1. +1
    -0
      COMMIT_EDITMSG
  2. +19
    -0
      Dockerfile
  3. +3
    -0
      FETCH_HEAD
  4. +1
    -0
      HEAD
  5. +21
    -0
      LICENSE
  6. +1
    -0
      ORIG_HEAD
  7. +22
    -0
      Pipfile
  8. +269
    -0
      Pipfile.lock
  9. +609
    -2
      README.md
  10. +16
    -0
      albumy-master.iml
  11. +21
    -0
      config
  12. +1
    -0
      description
  13. +11
    -0
      gconfig.py
  14. BIN
      index
  15. +16
    -0
      main.py
  16. +15
    -0
      nginx_flask.conf
  17. +198
    -0
      nohup.out
  18. +4
    -0
      packed-refs
  19. +31
    -0
      requirements.txt
  20. +7
    -0
      run.py

+ 1
- 0
COMMIT_EDITMSG View File

@ -0,0 +1 @@
README CHANGE

+ 19
- 0
Dockerfile View File

@ -0,0 +1,19 @@
FROM python:3.6
RUN apt-get update
RUN apt install -y nginx
WORKDIR /Project/demo
COPY requirements.txt ./
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY . .
RUN rm /etc/nginx/sites-enabled/default
COPY nginx_flask.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/nginx_flask.conf /etc/nginx/sites-enabled/nginx_flask.conf
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["nginx","-c","/etc/nginx/nginx.conf"]
CMD ["nginx","-t"]
CMD ["nginx","-s","reload"]
CMD ["gunicorn", "run:app", "-c", "./gconfig.py"]

+ 3
- 0
FETCH_HEAD View File

@ -0,0 +1,3 @@
941b27bc0531c5401948df3564c5b5da371ee9e4 branch 'develop' of 106.75.225.141:guoteng/albumy
42295b43602cb2dd7896c365a821f6d6afba4672 not-for-merge branch 'feature' of 106.75.225.141:guoteng/albumy
549e8bab6eb6120bd84ff14324503868ec124b4e not-for-merge branch 'master' of 106.75.225.141:guoteng/albumy

+ 1
- 0
HEAD View File

@ -0,0 +1 @@
ref: refs/heads/develop

+ 21
- 0
LICENSE View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Grey Li
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

+ 1
- 0
ORIG_HEAD View File

@ -0,0 +1 @@
c4729ee28f6cff91fd35196a61d75723434b9c5a

+ 22
- 0
Pipfile View File

@ -0,0 +1,22 @@
[[source]]
url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
[dev-packages]
faker = "*"
watchdog = "*"
[packages]
flask-sqlalchemy = "*"
flask-login = "*"
flask-dropzone = "*"
flask-mail = "*"
flask-moment = "*"
flask-wtf = "*"
python-dotenv = "*"
pillow = "*"
bootstrap-flask = "*"
flask = "*"
flask-whooshee = "*"
flask-avatars = "*"

+ 269
- 0
Pipfile.lock View File

@ -0,0 +1,269 @@
{
"_meta": {
"hash": {
"sha256": "8f77fa130f0a03afa0d2967bb6b7811e356ad34aef7a0aa88199fc29de15e1b5"
},
"pipfile-spec": 6,
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
}
]
},
"default": {
"blinker": {
"hashes": [
"sha256:471aee25f3992bd325afa3772f1063dbdbbca947a041b8b89466dc00d606f8b6"
],
"version": "==1.4"
},
"bootstrap-flask": {
"hashes": [
"sha256:d03c738f5377dbe7ecce010e28afcc0fb8373c4c9d01f8590af7657b53342c1d",
"sha256:ed817e82acadac4c8b3fc2d1a310325b6ed395d13b0f6ca9d14e1742f880e685"
],
"index": "pypi",
"version": "==1.2.0"
},
"click": {
"hashes": [
"sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc",
"sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a"
],
"version": "==7.1.1"
},
"flask": {
"hashes": [
"sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
"sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"
],
"index": "pypi",
"version": "==1.1.2"
},
"flask-avatars": {
"hashes": [
"sha256:c83081641ee834063416d9437be7d947cdebac51125c5f561d75e7e60f14b21b",
"sha256:e49e9a333b76fb131f4b66c1221b505ab9b3dd0e47a27d6a23e5d24dd2fcb7b2"
],
"index": "pypi",
"version": "==0.2.2"
},
"flask-dropzone": {
"hashes": [
"sha256:e5e3d4740d407807aa99d7b6438aad812a1ce01e1b07b0f409462ff078386709",
"sha256:fddeb963aef31da81e7bc39cad740e8778a8c59d96ef76c7d5ed362fc626a73a"
],
"index": "pypi",
"version": "==1.5.4"
},
"flask-login": {
"hashes": [
"sha256:6d33aef15b5bcead780acc339464aae8a6e28f13c90d8b1cf9de8b549d1c0b4b",
"sha256:7451b5001e17837ba58945aead261ba425fdf7b4f0448777e597ddab39f4fba0"
],
"index": "pypi",
"version": "==0.5.0"
},
"flask-mail": {
"hashes": [
"sha256:22e5eb9a940bf407bcf30410ecc3708f3c56cc44b29c34e1726fe85006935f41"
],
"index": "pypi",
"version": "==0.9.1"
},
"flask-moment": {
"hashes": [
"sha256:3c509afa25fd77459c9d799f292dcea384b1cd588ed1fd68f97f6fda6131299e",
"sha256:3e8b88f99af7cf75f2f29ef9d8c158eb92ca6f3c1ba6456ad70f715efa6eb7f7"
],
"index": "pypi",
"version": "==0.9.0"
},
"flask-sqlalchemy": {
"hashes": [
"sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327",
"sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d"
],
"index": "pypi",
"version": "==2.4.1"
},
"flask-whooshee": {
"hashes": [
"sha256:9ee837503eaead2a81554f0e01c2647f0240f247b0b5b01b431c8cb67617ff31"
],
"index": "pypi",
"version": "==0.7.0"
},
"flask-wtf": {
"hashes": [
"sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2",
"sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720"
],
"index": "pypi",
"version": "==0.14.3"
},
"itsdangerous": {
"hashes": [
"sha256:321b033d07f2a4136d3ec762eac9f16a10ccd60f53c0c91af90217ace7ba1f19",
"sha256:b12271b2047cb23eeb98c8b5622e2e5c5e9abd9784a153e9d8ef9cb4dd09d749"
],
"version": "==1.1.0"
},
"jinja2": {
"hashes": [
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
],
"version": "==2.11.1"
},
"markupsafe": {
"hashes": [
"sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473",
"sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161",
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
"sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66",
"sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b",
"sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1",
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
"sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d",
"sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e",
"sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d",
"sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c",
"sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21",
"sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2",
"sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5",
"sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b",
"sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6",
"sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f",
"sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f",
"sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2",
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
"version": "==1.1.1"
},
"pillow": {
"hashes": [
"sha256:04a10558320eba9137d6a78ca6fc8f4a5801f1b971152938851dc4629d903579",
"sha256:0f89ddc77cf421b8cd34ae852309501458942bf370831b4a9b406156b599a14e",
"sha256:251e5618125ec12ac800265d7048f5857a8f8f1979db9ea3e11382e159d17f68",
"sha256:291bad7097b06d648222b769bbfcd61e40d0abdfe10df686d20ede36eb8162b6",
"sha256:2f0b52a08d175f10c8ea36685115681a484c55d24d0933f9fd911e4111c04144",
"sha256:3713386d1e9e79cea1c5e6aaac042841d7eef838cc577a3ca153c8bedf570287",
"sha256:433bbc2469a2351bea53666d97bb1eb30f0d56461735be02ea6b27654569f80f",
"sha256:4510c6b33277970b1af83c987277f9a08ec2b02cc20ac0f9234e4026136bb137",
"sha256:50a10b048f4dd81c092adad99fa5f7ba941edaf2f9590510109ac2a15e706695",
"sha256:670e58d3643971f4afd79191abd21623761c2ebe61db1c2cb4797d817c4ba1a7",
"sha256:6c1924ed7dbc6ad0636907693bbbdd3fdae1d73072963e71f5644b864bb10b4d",
"sha256:721c04d3c77c38086f1f95d1cd8df87f2f9a505a780acf8575912b3206479da1",
"sha256:8d5799243050c2833c2662b824dfb16aa98e408d2092805edea4300a408490e7",
"sha256:90cd441a1638ae176eab4d8b6b94ab4ec24b212ed4c3fbee2a6e74672481d4f8",
"sha256:a5dc9f28c0239ec2742d4273bd85b2aa84655be2564db7ad1eb8f64b1efcdc4c",
"sha256:b2f3e8cc52ecd259b94ca880fea0d15f4ebc6da2cd3db515389bb878d800270f",
"sha256:b7453750cf911785009423789d2e4e5393aae9cbb8b3f471dab854b85a26cb89",
"sha256:b99b2607b6cd58396f363b448cbe71d3c35e28f03e442ab00806463439629c2c",
"sha256:cd47793f7bc9285a88c2b5551d3f16a2ddd005789614a34c5f4a598c2a162383",
"sha256:d6bf085f6f9ec6a1724c187083b37b58a8048f86036d42d21802ed5d1fae4853",
"sha256:da737ab273f4d60ae552f82ad83f7cbd0e173ca30ca20b160f708c92742ee212",
"sha256:eb84e7e5b07ff3725ab05977ac56d5eeb0c510795aeb48e8b691491be3c5745b"
],
"index": "pypi",
"version": "==7.1.1"
},
"python-dotenv": {
"hashes": [
"sha256:81822227f771e0cab235a2939f0f265954ac4763cafd806d845801c863bf372f",
"sha256:92b3123fb2d58a284f76cc92bfe4ee6c502c32ded73e8b051c4f6afc8b6751ed"
],
"index": "pypi",
"version": "==0.12.0"
},
"sqlalchemy": {
"hashes": [
"sha256:c4cca4aed606297afbe90d4306b49ad3a4cd36feb3f87e4bfd655c57fd9ef445"
],
"version": "==1.3.15"
},
"werkzeug": {
"hashes": [
"sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
"sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"
],
"version": "==1.0.1"
},
"whoosh": {
"hashes": [
"sha256:7ca5633dbfa9e0e0fa400d3151a8a0c4bec53bd2ecedc0a67705b17565c31a83",
"sha256:aa39c3c3426e3fd107dcb4bde64ca1e276a65a889d9085a6e4b54ba82420a852",
"sha256:e0857375f63e9041e03fedd5b7541f97cf78917ac1b6b06c1fcc9b45375dda69"
],
"version": "==2.7.4"
},
"wtforms": {
"hashes": [
"sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61",
"sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1"
],
"version": "==2.2.1"
}
},
"develop": {
"faker": {
"hashes": [
"sha256:2d3f866ef25e1a5af80e7b0ceeacc3c92dec5d0fdbad3e2cb6adf6e60b22188f",
"sha256:b89aa33837498498e15c709eb40c31386408a901a53c7a5e12a425737a767976"
],
"index": "pypi",
"version": "==4.0.2"
},
"pathtools": {
"hashes": [
"sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"
],
"version": "==0.1.2"
},
"python-dateutil": {
"hashes": [
"sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
"sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
],
"version": "==2.8.1"
},
"six": {
"hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
],
"version": "==1.14.0"
},
"text-unidecode": {
"hashes": [
"sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8",
"sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"
],
"version": "==1.3"
},
"watchdog": {
"hashes": [
"sha256:c560efb643faed5ef28784b2245cf8874f939569717a4a12826a173ac644456b"
],
"index": "pypi",
"version": "==0.10.2"
}
}
}

+ 609
- 2
README.md View File

@ -1,2 +1,609 @@
# cloud_computing_1
本次实验源代码来自:https://github.com/greyli/albumy
demo网站:http://59.110.70.81:5000
(主机的带宽最好大一点,我的1M小水管打开网站测试极慢)
tomcat需要8080端口,nginx需要5000,80端口。
不必要的端口不要开,提前提示,谨慎reboot,update,upgrade,高并发优化不是必要的,谨慎使用。
系统:Centos 8
先创建一个有root权限的用户(这里用Rivenatte)
切换到Rivenatte:
su Rivenatte
更新:
sudo yum update
sudo yum upgrade
检查有没有gcc:
gcc -v
没有就:
sudo yum install gcc
有时需安装git:
sudo yum install git
拉取项目(这里用原项目的):
git clone https://github.com/greyli/albumy.git
cd albumy
配置虚拟环境:
python -m venv env(or python3 -m venv env)
source env/bin/activate
pip install -r requirements.txt
python -m pip install --upgrade pip
生成测试数据并测试:
flask forge
flask run --host 0.0.0.0 --port 5000
用浏览器打开http://云主机ip:5000
测试的用户账号:
username:admin@helloflask.com
password:helloflask
下面的pip可以换成pip3,如果你在创建虚拟环境时用python3的话
然而flask自带的wsgi容器性能较差,我采用另一个服务器,gunicorn。
先将我git上的python文件放在albumy中,
同时还需在项目目录下:
mkdir log
cd log
touch debug.log
touch gunicorn.pid
cd ..
这里的albumy指项目目录,不是albumy模块。
gunicorn需要在虚拟环境下安装:
(没进虚拟环境的先:
source env/bin/activate)
pip install gunicorn
同时还需安装gevent:
pip install gevent
推荐参考这个格式,指定源:
pip install 包名 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
可以解决大多问题。
然后:
cd albumy
修改__init__.py的create_app中的
app = Flask('albumy')
改为
app = Flask(__name__)
输入
gunicorn -c albumy项目的绝对路径/gconfig.py run:app
像上面一样,打开浏览器测试
继续用nginx来做反向代理。
先安装nginx:
sudo yum install nginx
接下来的配置,不同的linux发行版修改的文件可能不同,但大体类似。
实在不行就用whereis nginx找。
centos就先:
cd /etc/nginx
ls -a
发现conf.d这个文件夹,这个是放配置文件的
先对nginx.conf修改。
一般第二个https的设置是被注释的,不用管。
看http的设置,在http部分的最下面加入:
include conf.d/*.conf;(;不能少了)
这样我们就可以通过在conf.d文件夹里修改文件来进行配置
配置文件必须是.conf格式
比如 albumy.conf
server {
listen 80;
root albumy项目的绝对路径;
server_name 公网ip;
location / {
proxy_set_header x-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://localhost:5000/; # gunicorn绑定的端口号
}
# 配置static的静态文件:
location ~ ^\/static\/.*$ {
root 项目的绝对路径;
}
}
添加后
sudo nginx -c /etc/nginx/nginx.conf
sudo nginx -t
sudo nginx -s reload
用浏览器打开80,5000端口,看下有没有成功。
80端口的仅仅是静态页面,比较简陋。
接着我们需要用nohup守护进程:
进入项目文件夹的虚拟环境:
cd xxx/albumy/
source env/bin/activate
nohup gunicorn -c gconfig.py run:app &
然后看下有没有成功:
ps -e | grep gunicorn
nohub不能让主机重启后自动运行进程,我们需要设置开机启动:
sudo vim /etc/rc.local
在最下面添加命令行,这样就可以在重启后自动执行命令行了。
最后别忘了加:
chmod +x /etc/rc.d/rc.local
然后可以重启检查下。
接下来进行高并发优化
我们需要安装ab压测工具
这个在任何服务器上部署都可以,只要在最后把ip改下就行。
这里申请一台centos8主机,部署ab。
同样不使用root用户。
先提醒下,deb系的安装与以下有点区别。
先更新:
sudo yum update
sudo yum upgrade
安装依赖:
sudo yum install apr-util
sudo yum install yum-utils
sudo mkdir -p /appdata/apache/ab && cd /appdata/apache/ab
安装ab:
sudo yum install yum-utils.noarch
sudo yumdownloader httpd-tools*
下一步需要root用户:
su root
rpm2cpio httpd-tools*.rpm | cpio -idmv
su Rivenatte
sudo cp /appdata/apache/ab/usr/bin/ab /usr/bin
执行ab:
ab -n 1000 -c 200 http://ip:5000/
如果不与项目在同一主机,写公网ip
在就localhost
1000指共访问了1000次,200指同时200并发
进行了5次
出现apr_pollset_poll: The timeout specified has expired (70007)
就改为ab -n 1000 -c 200 -k http://ip:5000/
看主机配置,量力而行。
先测试下,与优化后比较。
回到部署nginx的主机。
先获取cpu核心数:
grep processor /proc/cpuinfo | wc -l
一般在申请时就知道了
修改配置文件中的http模块:
sudo vim /etc/nginx/nginx.conf
具体参数参考以下文章:
https://www.jianshu.com/p/0fa77899913a
单核2g的主机可以参考以下配置:
worker_rlimit_nofile 10000;
events {
worker_connections 10000;
}
keepalive_timeout 0;(高并发的网站需要设置的参数)
su root
(不用root用户无法进行下列命令)
对系统配置:
ulimit -n 一个较大的数字
echo 50000 > /proc/sys/net/core/somaxconn
more /proc/sys/net/core/somaxconn
有50000则正常
下列同理:
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle(注意,现在的centos取消了这个参数,没有可以不管)
cat /proc/sys/net/ipv4/tcp_tw_recycle
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
cat /proc/sys/net/ipv4/tcp_tw_reuse
echo 0 > /proc/sys/net/ipv4/tcp_syncookies
cat /proc/sys/net/ipv4/tcp_syncookies
dmesg|tail
(可能需vim /etc/sysctl.conf
设置:
net.ipv4.tcp_syncookies = 0)
对测试主机也需:
echo 50000 > /proc/sys/net/core/somaxconn
ulimit -n 一个较大的数字
(可能需设置net.ipv4.tcp_syncookies = 0,同上)
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/压测albumy.png)
不重启主机,对测试主机配置后开始测试:
sudo vim /etc/sysctl.conf
加入:
net.nf_conntrack_max = 655360
net.netfilter.nf_conntrack_tcp_timeout_established = 1200)
ab -c 100000 -n 100000 -r http://主机ip:5000/
(可以适当改下,但还是要看主机配置,我这台是2gRam 1核)
之后可以将以上系统设置加入自启动项(服务器重启可能会回到修改前设置)
下面添加邮箱:
先去注册个邮箱,并以stmp服务器发送。(这个需要网上查下如何开放邮箱stmp,不同邮箱的可能不同)
像之前一样,修改启动项:
source env/bin/activate
export MAIL_SERVER='smtp.163.com' //示例
export MAIL_USERNAME='XXXXXX@163.com' //填邮箱XXXXXX
export MAIL_PASSWORD='XXXXXX' //密码,注意是邮箱开启第三方smtp的认证码
别忘了chmod +x /etc/rc.d/rc.local
最后reboot。
(注:这里需要邮箱发送验证邮件,才可以输入验证码注册)
## 采用docker部署与k8s集群负载均衡
### 一、docker部署上传镜像
配置docker容器并push到远端,重新在别的服务器上部署时只需将镜像pull下来即可。
写好Dockerfile,在本地build好镜像后,将容器push到远端。
在服务器上部署时只需将远端的容器pull到服务器上,再run即可
```python
我们首先要将上面的run.py中的
app.run()改为app.run(host='0.0.0.0',port=3389)
```
部署时先下载docker
```bash
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
```
若报错则先执行
```bash
yum install -y https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-1.2.6-3.3.el7.x86_64.rpm
```
接着启动docker服务
```bash
service docker start
```
写入我们的Dockerfile
```bash
vim Dockerfile
```
```
FROM python:3.6
RUN apt-get update
RUN apt install -y nginx
WORKDIR /Project/demo
COPY requirements.txt ./
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY . .
RUN rm /etc/nginx/sites-enabled/default
COPY nginx_flask.conf /etc/nginx/sites-available/
RUN ln -s /etc/nginx/sites-available/nginx_flask.conf /etc/nginx/sites-enabled/nginx_flask.conf
RUN echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["nginx","-c","/etc/nginx/nginx.conf"]
CMD ["nginx","-t"]
CMD ["nginx","-s","reload"]
CMD ["gunicorn", "run:app", "-c", "./gconfig.py"]
```
其中需要我们之前部署时写的gconfig.py文件和我们的nginx配置文件
除此以外我们需要在根目录创建一个新文件**nginx_flask.conf**
```
server {
listen 80;
location / {
proxy_pass http://localhost:5000/;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ^~ /static/ {
root /Project/demo;
}
}
```
之后在本地建立镜像
```bash
docker build -t image1 .
```
建立成功后可以尝试启动docker容器
```
docker run -d -p 3389:3389 --name test1 image1
```
打开http://云主机ip:3389查看是否显示界面
之后将我们的镜像push到远端
没有注册docker账号的话先注册一个docker账号
https://registry.hub.docker.com/
接着登录docker账号
```
docker login
输入账号
输入密码
```
显示login successful就成功了,之后push到远端
首先对镜像打标签
```bash
docker tag image1 lh123/albumy_nginx_mysql_image:v2
```
之后将镜像推送到仓库
```bash
docker push lh123/albumy_nginx_mysql_image:v2
```
以后需要创建应用时只需要从docker中拉取镜像运行即可。
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/docker_image.png)
新建云主机,安装docker之后将上述镜像pull到本地,之后执行
```
docker run -d -p 3389:3389 --name test1 lh123/albumy_nginx_mysql_image:v2
```
打开http:云主机ip:3389即可访问网页。
### 二、采用负载均衡
之后在ucloud中使用负载均衡ULB,创建负载均衡ULB。
接着在建立三台云主机,使用原来申请的有外网的云主机,分别通过内网连接三台新建的云主机。
在云主机上pull下来刚才上传的docker镜像
```bash
docker pull lh123/albumy_nginx_mysql_image:v2
```
在云主机上启动镜像
```bash
docker run -d -p 3389:3389 --name test1 albumy_nginx_mysql_image:v2
```
将我们启动好的三台云主机啊加到我们的负载均衡当中
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/负载均衡.png)
使用负载均衡之后的压测效果更好
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/压测albumy.png)
### 三、连接云数据库
为了保证负载均衡的服务器的数据一致性,我们将数据库挂在到云数据库mysql上
我们将项目的.flask.env文件中的设置改为
```bash
FLASK_APP=albumy
FLASK_ENV=development
```
再将setting.py中的设置改为
```bash
class ProductionConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = 'mysql://root:lianghui123@10.23.56.255:3306/flask_test'
```
再打包到docker并运行我们的项目
发现报错No module named MYSQLdb发现是因为
```bash
按照 Flask-SQLAlchemy 文档的说明,配置好 SQLALCHEMY_DATABASE_URI = 'mysql://username:password@server/db' 后操作 MySQL 报错 ImportError: No module named 'MySQLdb'。既然缺少 MySQLdb 这个模块,按照常规方法缺啥补啥吧,执行 pip install MySQL-python 却报错 ImportError: No module named 'ConfigParser'。查了一下,这是由于 MySQL-python 不支持 Python 3(MySQL-3.23 through 5.5 and Python-2.4 through 2.7 are currently supported)。
```
将MySQLdb换为pymsql,安装pymsql(记得先启动虚拟环境)
```absh
pip install pymsql
```
并在__init__.py中加入
```python
import pymysql
pymysql.install_as_MySQLdb()
```
再将setting.py中的设置改为
```bash
class ProductionConfig(BaseConfig):
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:lianghui123@10.23.56.255:3306/flask_test'
```
即可访问我们的云主机数据库。
最后将完成的项目打包好镜像push到远端仓库。
### 四、k8s集群部署(有点问题)
建立好docker容器之后就可以使用k8s集群部署网页,采用k8s部署可以实现容器集群的自动化部署、自动扩缩容、维护等功能。
首先在ucloud中创建一个UK8S,之后申请一个云主机,
登录云主机安装docker拉取镜像
```
docker pull lh123/albumy_nginx_mysql_image:v2
```
安装k8s
```bash
wget https://storage.googleapis.com/kubernetes-release/release/v1.19.0/bin/linux/amd64/kubectl
chmod +x kubectl
sudo mv kubectl /usr/local/bin/ (root用户:mv kubectl /usr/bin/)
kubectl version -o json
```
将K8S的集群凭证添加到kubectl配置文件中,从而能够操控集群。
打开K8S的外网集凭证复制,在云主机中创建~/.kube文件夹,并在其中创建config文件,将外网集凭证复制到config文件中。
为了能在k8s上部署我们的docker镜像,我们还需要在项目文件中添加两个文件**Deployment.yaml** 和 **service.yaml**
**deployment.yaml**中:
```bash
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: albumy-node-deployment
spec:
replicas: 1
selector:
matchLabels:
app: albumynode
template:
metadata:
labels:
app: albumynode
spec:
containers:
- name: albumynode
image: lh123/albumy_nginx_mysql_image:v2
imagePullPolicy: Always
ports:
- containerPort: 3389
```
**service.yaml**中:
```
apiVersion: v1
kind: Service
metadata:
name: albumy-node-deployment
spec:
ports:
- port: 3389
targetPort: 3389
selector:
app: albumynode
```
之后运行一下命令部署项目
```bash
kubectl create -f deployment.yaml
kubectl create -f service.yaml
```
查看pod是否处于ready状态,若pod不处于ready状态且一直是containercreating状态则
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/错误1.png)
查看创建日志,发现是容器创建失败,谷歌不能访问,uhub-edu.service.ucloud.cn/google_containers/pause-amd64:3.0"镜像无法获取,这应该是ucloud端的问题。(我们采用平时实验作业第八次的部署方法部署实验2里的静态网页也还是报这个错误,真的是很无语)。
(即使用
```bash
kubectl create deployment webapp --image=lh123/albumy_nginx_mysql_image:v2
```
我们发现这样子pod的状态还是一直处于containercreating的状态,而且也是报下图的错误,我觉得者应该是ucloud端的问题,被墙了。我怀疑是ucloud的教育云出问题了之前第八次实验的时候还可以用,现在就不可以用了。
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/错误2.png)
解决方法如下,从docker.io把pause-amd64镜像取下来,然后做个标签。这样就可以解决问题。
```
docker pull googlecontainer/pause-amd64:3.0
docker tag googlecontainer/pause-amd64:3.0 uhub-edu.service.ucloud.cn/google_containers/pause-amd64:3.0
```
之后新建连接,登录云主机执行
```bash
echo -e "\n\e[92mStarting Proxy. After starting it will not output a response. Please click the first Terminal Tab\n"; kubectl proxy
```
之后访问我们的应用
```
kubectl get pods#获取我们pod的名称
export POD_NAME=$刚才得到的pod名称
curl http://localhost:8001/api/v1/namespaces/default/pods/$POD_NAME/proxy/
```
查看我们创建的POD的详细信息
```
kubectl get pods -o wide
```
之后将副本scale到10个
```bash
kubectl scale deployments/albumy-node-deployment --replicas=10
```
将我们的网页暴露给公网,并加上负载均衡
创建一个负载均衡
```bash
kubectl expose deployment albumy-node-deployment --type=LoadBalancer --port=80
```
查看service,可以看到负载均衡服务的外网ip地址
```bash
kubectl get services
```
之后访问http:localhost:负载均衡ip就可以看到网页
本次作业镜像:
账号:1491337293@qq.com
密码:18830095812zxc
docker账户
账号:lh123
密码:18830095812zxc
docker镜像:lh123/albumy_nginx_mysql_image:v2
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/docker镜像.png)
ucloud上docker部署镜像名:docker_nginx_mysql
![](http://106.75.225.141/guoteng/albumy/raw/develop/readme_image/镜像.png)
集群部署有点问题被墙了。

+ 16
- 0
albumy-master.iml View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://MODULEDIR" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="MODULEDIR/albumy/templates" />
</list>
</option>
</component>
</module>

+ 21
- 0
config View File

@ -0,0 +1,21 @@
[core]
repositoryformatversion = 0
filemode = false
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = git@106.75.225.141:guoteng/albumy.git
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
remote = origin
merge = refs/heads/master
rebase = true
[branch "develop"]
remote = origin
merge = refs/heads/develop
rebase = true
[gui]
wmstate = normal
geometry = 1061x563+160+160 233 255

+ 1
- 0
description View File

@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

+ 11
- 0
gconfig.py View File

@ -0,0 +1,11 @@
from gevent import monkey
monkey.patch_all()
import multiprocessing
debug = True
loglevel = 'debug'
bind = '0.0.0.0:3389'
pidfile = 'log/gunicorn.pid'
logfile = 'log/debug.log'
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'gevent'

BIN
index View File


+ 16
- 0
main.py View File

@ -0,0 +1,16 @@
# This is a sample Python script.
# Press Shift+F10 to execute it or replace it with your code.
# Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings.
def print_hi(name):
# Use a breakpoint in the code line below to debug your script.
print(f'Hi, {name}') # Press Ctrl+F8 to toggle the breakpoint.
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
print_hi('PyCharm')
# See PyCharm help at https://www.jetbrains.com/help/pycharm/

+ 15
- 0
nginx_flask.conf View File

@ -0,0 +1,15 @@
server {
listen 80;
location / {
proxy_set_header x-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://localhost:3389/; # gunicorn绑定的端口号
}
# 配置static的静态文件:
location ~ ^\/static\/.*$ {
root /Project/demo;
}
}

+ 198
- 0
nohup.out View File

@ -0,0 +1,198 @@
[2021-01-14 08:37:45 +0800] [14269] [DEBUG] Current configuration:
config: gconfig.py
bind: ['0.0.0.0:3389']
backlog: 2048
workers: 5
worker_class: gevent
threads: 1
worker_connections: 1000
max_requests: 0
max_requests_jitter: 0
timeout: 30
graceful_timeout: 30
keepalive: 2
limit_request_line: 4094
limit_request_fields: 100
limit_request_field_size: 8190
reload: False
reload_engine: auto
reload_extra_files: []
spew: False
check_config: False
preload_app: False
sendfile: None
reuse_port: False
chdir: /root/albumy
daemon: False
raw_env: []
pidfile: log/gunicorn.pid
worker_tmp_dir: None
user: 0
group: 0
umask: 0
initgroups: False
tmp_upload_dir: None
secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
forwarded_allow_ips: ['127.0.0.1']
accesslog: None
disable_redirect_access_to_syslog: False
access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
errorlog: -
loglevel: debug
capture_output: False
logger_class: gunicorn.glogging.Logger
logconfig: None
logconfig_dict: {}
syslog_addr: udp://localhost:514
syslog: False
syslog_prefix: None
syslog_facility: user
enable_stdio_inheritance: False
statsd_host: None
dogstatsd_tags:
statsd_prefix:
proc_name: None
default_proc_name: run:app
pythonpath: None
paste: None
on_starting: <function OnStarting.on_starting at 0x7f4348074a60>
on_reload: <function OnReload.on_reload at 0x7f4348074b70>
when_ready: <function WhenReady.when_ready at 0x7f4348074c80>
pre_fork: <function Prefork.pre_fork at 0x7f4348074d90>
post_fork: <function Postfork.post_fork at 0x7f4348074ea0>
post_worker_init: <function PostWorkerInit.post_worker_init at 0x7f4348094048>
worker_int: <function WorkerInt.worker_int at 0x7f4348094158>
worker_abort: <function WorkerAbort.worker_abort at 0x7f4348094268>
pre_exec: <function PreExec.pre_exec at 0x7f4348094378>
pre_request: <function PreRequest.pre_request at 0x7f4348094488>
post_request: <function PostRequest.post_request at 0x7f4348094510>
child_exit: <function ChildExit.child_exit at 0x7f4348094620>
worker_exit: <function WorkerExit.worker_exit at 0x7f4348094730>
nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x7f4348094840>
on_exit: <function OnExit.on_exit at 0x7f4348094950>
proxy_protocol: False
proxy_allow_ips: ['127.0.0.1']
keyfile: None
certfile: None
ssl_version: 2
cert_reqs: 0
ca_certs: None
suppress_ragged_eofs: True
do_handshake_on_connect: False
ciphers: None
raw_paste_global_conf: []
strip_header_spaces: False
[2021-01-14 08:37:45 +0800] [14269] [INFO] Starting gunicorn 20.0.4
[2021-01-14 08:37:45 +0800] [14269] [DEBUG] Arbiter booted
[2021-01-14 08:37:45 +0800] [14269] [INFO] Listening at: http://0.0.0.0:3389 (14269)
[2021-01-14 08:37:45 +0800] [14269] [INFO] Using worker: gevent
[2021-01-14 08:37:45 +0800] [14272] [INFO] Booting worker with pid: 14272
[2021-01-14 08:37:45 +0800] [14273] [INFO] Booting worker with pid: 14273
[2021-01-14 08:37:45 +0800] [14274] [INFO] Booting worker with pid: 14274
[2021-01-14 08:37:45 +0800] [14276] [INFO] Booting worker with pid: 14276
[2021-01-14 08:37:45 +0800] [14269] [DEBUG] 5 workers
[2021-01-14 08:37:45 +0800] [14277] [INFO] Booting worker with pid: 14277
[2021-01-14 09:06:33 +0800] [17476] [INFO] Booting worker with pid: 17476
[2021-01-14 09:06:33 +0800] [17481] [INFO] Booting worker with pid: 17481
[2021-01-14 09:06:33 +0800] [14269] [DEBUG] 2 workers
[2021-01-14 09:06:33 +0800] [14269] [INFO] Handling signal: hup
[2021-01-14 09:06:33 +0800] [14269] [INFO] Hang up: Master
[2021-01-14 09:06:33 +0800] [14269] [DEBUG] Current configuration:
config: gconfig.py
bind: ['0.0.0.0:3389']
backlog: 2048
workers: 5
worker_class: gevent
threads: 1
worker_connections: 1000
max_requests: 0
max_requests_jitter: 0
timeout: 30
graceful_timeout: 30
keepalive: 2
limit_request_line: 4094
limit_request_fields: 100
limit_request_field_size: 8190
reload: False
reload_engine: auto
reload_extra_files: []
spew: False
check_config: False
preload_app: False
sendfile: None
reuse_port: False
chdir: /root/albumy
daemon: False
raw_env: []
pidfile: log/gunicorn.pid
worker_tmp_dir: None
user: 0
group: 0
umask: 0
initgroups: False
tmp_upload_dir: None
secure_scheme_headers: {'X-FORWARDED-PROTOCOL': 'ssl', 'X-FORWARDED-PROTO': 'https', 'X-FORWARDED-SSL': 'on'}
forwarded_allow_ips: ['127.0.0.1']
accesslog: None
disable_redirect_access_to_syslog: False
access_log_format: %(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"
errorlog: -
loglevel: debug
capture_output: False
logger_class: gunicorn.glogging.Logger
logconfig: None
logconfig_dict: {}
syslog_addr: udp://localhost:514
syslog: False
syslog_prefix: None
syslog_facility: user
enable_stdio_inheritance: False
statsd_host: None
dogstatsd_tags:
statsd_prefix:
proc_name: None
default_proc_name: run:app
pythonpath: None
paste: None
on_starting: <function OnStarting.on_starting at 0x7f4348074a60>
on_reload: <function OnReload.on_reload at 0x7f4348074b70>
when_ready: <function WhenReady.when_ready at 0x7f4348074c80>
pre_fork: <function Prefork.pre_fork at 0x7f4348074d90>
post_fork: <function Postfork.post_fork at 0x7f4348074ea0>
post_worker_init: <function PostWorkerInit.post_worker_init at 0x7f4348094048>
worker_int: <function WorkerInt.worker_int at 0x7f4348094158>
worker_abort: <function WorkerAbort.worker_abort at 0x7f4348094268>
pre_exec: <function PreExec.pre_exec at 0x7f4348094378>
pre_request: <function PreRequest.pre_request at 0x7f4348094488>
post_request: <function PostRequest.post_request at 0x7f4348094510>
child_exit: <function ChildExit.child_exit at 0x7f4348094620>
worker_exit: <function WorkerExit.worker_exit at 0x7f4348094730>
nworkers_changed: <function NumWorkersChanged.nworkers_changed at 0x7f4348094840>
on_exit: <function OnExit.on_exit at 0x7f4348094950>
proxy_protocol: False
proxy_allow_ips: ['127.0.0.1']
keyfile: None
certfile: None
ssl_version: 2
cert_reqs: 0
ca_certs: None
suppress_ragged_eofs: True
do_handshake_on_connect: False
ciphers: None
raw_paste_global_conf: []
strip_header_spaces: False
[2021-01-14 09:06:33 +0800] [17510] [INFO] Booting worker with pid: 17510
[2021-01-14 09:06:33 +0800] [17512] [INFO] Booting worker with pid: 17512
[2021-01-14 09:06:33 +0800] [17509] [INFO] Booting worker with pid: 17509
[2021-01-14 09:06:33 +0800] [17511] [INFO] Booting worker with pid: 17511
[2021-01-14 09:06:33 +0800] [17518] [INFO] Booting worker with pid: 17518
[2021-01-14 09:06:33 +0800] [14269] [DEBUG] 5 workers
[2021-01-14 09:06:33 +0800] [14269] [INFO] Handling signal: term
[2021-01-14 09:06:35 +0800] [17476] [INFO] Worker exiting (pid: 17476)
[2021-01-14 09:06:35 +0800] [17481] [INFO] Worker exiting (pid: 17481)
[2021-01-14 09:06:35 +0800] [17511] [INFO] Worker exiting (pid: 17511)
[2021-01-14 09:06:35 +0800] [17518] [INFO] Worker exiting (pid: 17518)
[2021-01-14 09:06:35 +0800] [17510] [INFO] Worker exiting (pid: 17510)
[2021-01-14 09:06:35 +0800] [17512] [INFO] Worker exiting (pid: 17512)
[2021-01-14 09:06:35 +0800] [17509] [INFO] Worker exiting (pid: 17509)
[2021-01-14 09:06:35 +0800] [14269] [INFO] Shutting down: Master

+ 4
- 0
packed-refs View File

@ -0,0 +1,4 @@
# pack-refs with: peeled fully-peeled sorted
326f368eeaac6faa708e788440893c1d39acf864 refs/remotes/origin/develop
42295b43602cb2dd7896c365a821f6d6afba4672 refs/remotes/origin/feature
549e8bab6eb6120bd84ff14324503868ec124b4e refs/remotes/origin/master

+ 31
- 0
requirements.txt View File

@ -0,0 +1,31 @@
blinker==1.4
bootstrap-flask==1.2.0
click==7.1.1
flask-avatars==0.2.2
flask-dropzone==1.5.4
flask-login==0.5.0
flask-mail==0.9.1
flask-moment==0.9.0
flask-sqlalchemy==2.4.1
flask-whooshee==0.7.0
flask-wtf==0.14.3
flask==1.1.2
itsdangerous==1.1.0
jinja2==2.11.1
markupsafe==1.1.1
pillow==7.1.1
python-dotenv==0.12.0
sqlalchemy==1.3.15
werkzeug==1.0.1
whoosh==2.7.4
wtforms==2.2.1
# dev
faker==4.0.2
pathtools==0.1.2
python-dateutil==2.8.1
six==1.14.0
text-unidecode==1.3
watchdog==0.10.2
gunicorn
gevent==1.4
pymysql

+ 7
- 0
run.py View File

@ -0,0 +1,7 @@
from albumy.__init__ import create_app
from flask import Flask
app = create_app()
if __name__ == "__main__":
app.run()

||||||
x
 
000:0
Loading…
Cancel
Save