一个完整的 Web 应用包含前端页面、数据库、后台逻辑等,按照一般流程去构建需要配置 Nginx、MySQL,以及后台服务器,运维涉及到的部分十分复杂。而 Docker 可以将这些东西(数据+服务)封装起来,虽然有些场合不建议数据和服务放在一起。本文就在一个 Docker 容器中完整部署整个 Web 应用的需求作详细的介绍。
本文举例的 Web 应用为 『top-topic-Zhihu』(换个 timeline 看知乎),抓取「知乎」网站每天新提出的热门 top10 问题聚合显示,提供另一种看知乎的姿势,不被纷繁的 timeline 所迷惑。
项目地址:https://github.com/Huangtuzhi/top-topic-Zhihu Dockerfile 和依赖文件:https://github.com/Huangtuzhi/docker-toptopic 云服务商选择『灵雀云』提供容器服务:http://www.alauda.cn/ 镜像地址:https://hub.alauda.cn/repos/huangtuzhi/docker-toptopic Web页面展示:http://toptopic-huangtuzhi.myalauda.cn:19991/ 『top-topic-Zhihu』应用结构
需要这几步来完成目标:
“ 抓取——> 存储( 数据持久化) ——> 分析——> 展示
抓取:抓取部分主要是爬虫,先手动输入验证码获取登录 Cookie。然后带着该 Cookie 模拟发出 Get 请求来获得网页数据。思路是从自己的个人主页开始爬,先爬出现在主页 timeline 上的所有人,再爬这些人主页上的其他人…,直到数据量足够大。把人的 ID 存储在 people 中。接着继续爬 people 中所有人主页上提出的问题,并获得问题的关注人数和提问时间。把抓取到的问题存储在 question 中。
存储:存储可以把上面的 people,question 写入文本或者 MySQL 数据库。中间数据也应该放到数据库中,不然内存会被无穷多的递归生成的中间数据填满。本项目使用带主键的 MySQL 表模拟内存 set 来存储 people。
分析:网站目的是获取每天或者一个时间段内新提出的 top10 热门问题,所以需要对时间过滤,对关注人数排序。这都可以在 SQL 查询中完成。
展示:展示包括后台和前端两部分,后台需要在 DB 中取得数据构造成 JSON 格式以 CGI 的形式提供给前端调用。这里使用 Python Flask 框架提供 CGI 后台服务。前端页面主要是跨域 AJax 请求后台 CGI 来获得数据,结合定义的模板来展示页面。在版本 V1 中使用 AngularJS 来简单的编写模板及 AJax 请求的逻辑部分,在版本 V2 中使用 artTemplate 和封装原生的 JS 来满足需求。
文件目录
整个 Web 应用的目录结构如下所示:
Dockerfile
Dockerfile 描述了容器的依赖和进行构建的步骤,下面会逐步解释语句的含义。
MySQL 安装和配置
MySQL 服务器只需要用包管理器安装 mysql-server-5.6,因为后台使用 Python 作为服务器语言,还需要安装 MySQL 对 Python 语言的支持。需要使用 apt 安装 python ,libmysqlclient-dev 和 python-dev,然后使用 pip 管理器安装 MySQL-python。
MySQL 的默认字符集为 latin1,而网页显示一般是 utf8 字符集,需要将 MySQL 的配置文件的字符集置为 utf8。
使用命令
将本地已修改好的配置文件覆盖 Docker 中的 MySQL 配置文件。查看字符集
若字符集如上所示,则说明已经修改成功。
MySQL 的坑
在上面的 Dockerfile 中看到分别给'root'@'127.0.0.1'和'root'@'localhost'都加了权限,'root'@'localhost'的权限在 SQL 语句最后才加上。这是因为
使用'root'@'localhost'没权限建数据库和表,报错
Access denied for user ‘root’@’localhost’ (using password: No)
使用 'root'@'127.0.0.1'进入 Docker 后没权限连接 Mysql,报错
Access denied for user ‘root’@’localhost’ (using password: YES)
于是这里用'root'@'127.0.0.1'来建数据库和表,最后再用'root'@'localhost'来连接数据库。
Nginx 安装和配置
Nginx 在这里作为静态页面的服务器,安装只需要用 apt 管理器安装即可。
Nginx 需要配置 root 目录来指定网站的文件位置,把本地的 global.conf和nginx.conf文件覆盖到 Docker 中。
在 global.conf 中我们指明服务器根目录为/usr/share/nginx/html/www
在 Docker 中,我们将网站文件放到新建的 /home/toptopic/web/www目录。这里建立一个软链接将它们关联起来,便于修改和维护。
EXPOSE 两个端口
EXPOSE 在 Docker 中用来限制开放的端口。我们使用 Nginx 来提供静态页面访问,使用 Flask 框架来提供动态页面数据的获取,所以需要开放两个端口。
查询端口状态,可以看到宿主机 2333 端口被映射到 Docker 的 2333 端口,宿主机 5000 端口被映射到 Docker 的 5000 端口。
2223 端口与上节中的 Nginx 中设定的端口必须保持一致,因为 Nginx 使用 2223 端口提供服务,Docker 刚好必须把这个端口开放出去。
在基于 Flask 框架写的后台服务 dataCGI.py 中,服务器对应的监听地址为
host 必须设置为 0.0.0.0,表示监听所有的 IP 地址。如果 host 使用 127.0.0.1,在容器外将无法访问服务。同时,这里的端口 5000 和 Dockerfile 中开放的另一个端口一致。
启动脚本
在 Dockerfile 中的 CMD 中可以指定 Docker 运行时执行一些命令。
这三行分别启动 MySQL,Nginx 和后台服务。
构建命令
构建 Docker 容器
运行容器
需要注意的是若使用
无法启动 CMD 中的脚本命令,这是因为在 docker run 后指定了 /bin/bash 后会覆盖 CMD 中的命令。
在云平台上部署
在「灵雀云」上部署一个 Docker 应用需要两步:构建——创建服务。
点击「构建」——「创建镜像构建仓库」,然后选择 Github 仓库源。需要把预先写好的 Dockerfile 放在 Github中。
构建好仓库之后,点击「创建服务」。
进行服务的设置,高级设置中服务地址类型选为 tcp-endpoint 即可(外部用户可以直接通过 TCP 方式访问这个服务地址,服务地址的端口是随机分配的,一般会大于 10000 小于 65535)。
最后点击最下方的「创建服务」完成部署。新建的服务如下所示:
在浏览器中输入http://toptopic-huangtuzhi.myalauda.cn:19991/,或点击『阅读原文』即可访问网站。
作者简介:外号夫子,长于长江之上「梨花岛」,总喜欢一个江湖的传说,如果你偶然记起关于「桃花岛」黄药师的传说,记得划船来找我。个人博客:fuzhii.com。其他博文《换个timeline看知乎》,《用机器学习的方法鉴别红楼梦作者》。