    本次实验源代码来自：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 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)

集群部署有点问题被墙了。

### 五、总结与反思
通过本次大作业，我们学习网站前后端的知识，学习了云计算的一些基本知识，亲自部署了云上的应用，学习了docker、k8s等云计算工具的使用，更好的理解了的docker和k8s的用途，和它们在真实云计算环境下对项目部署的帮助。但我们的部署和开发并不完善，一些应用的细节问题没有解决，关于docker、k8s和其他云计算工具的许多知识还需要学习
分工：项目部署、README书写、版本控制、前后端修改：郭腾、梁辉
      前后端开发修改、版本控制、项目部署：沈小奇