本次实验源代码来自:https://github.com/greyli/albumy demo网站:http://59.110.70.81:5000 或者http://106.75.223.107:5000 gitlab项目地址(注意是develop分支下的代码) http://106.75.225.141/guoteng/albumy/tree/develop (主机的带宽最好大一点,我的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 http://gitea.shuishan.net.cn/10185501411/cloud_computing_1.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) 集群部署有点问题被墙了。 ### 五、总结与反思 通过本次大作业,我们学习网站前后端的知识,学习了云计算的一些基本知识,亲自部署了云上的应用,学习了docker、k8s等云计算工具的使用,更好的理解了的docker和k8s的用途,和它们在真实云计算环境下对项目部署的帮助。但我们的部署和开发并不完善,一些应用的细节问题没有解决,关于docker、k8s和其他云计算工具的许多知识还需要学习 分工:项目部署、README书写、版本控制、前后端修改:郭腾、梁辉 前后端开发修改、版本控制、项目部署:沈小奇