Docker+Flask构建后端的踩坑记录
今天第一次尝试用flask构建自己的后端项目,其实一切都挺顺利的。直到部署这个环节,因为我对于Flask和Docker不了解,踩了不少坑。
docker这个东西,对于我这个初学者来说,dockerfile是好文明,但是docker本身不是。
在一开始,我总是尝试pull ubuntu镜像,然后再dockerfile里面写上apt-get install和pip,其实何必呢,完全就有官方的python环境镜像。而且,手动更改系统镜像,不一定能达到效果,就好比今天Python环境搞了半天,发现跑不起来,因为期间的软链接创建不成功,无法执行python app.py
这个命令(但是它自带的python3没有问题,但是我要的是python2)
写了个自动搭建dockerfile,仅供参考
1 | //FlaskApp |
Dockerfile的基本指令有十三个,上面用到了部分;
FROM - 所有Dockerfile的第一个指令都必须是
FROM
,用于指定一个构建镜像的基础源镜像,如果本地没有就会从公共库中拉取,没有指定镜像的标签会使用默认的latest标签,如果需要在一个Dockerfile中构建多个镜像,可以使用多次。MAINTAINER - 描述镜像的创建者,名称和邮箱。
RUN - RUN命令是一个常用的命令,执行完成之后会成为一个新的镜像,通常用于运行安装任务从而向映像中添加额外的内容。在这里,我们需更新包,安装
python3
和pip
。在第二个RUN
命令中使用pip
来安装requirements.txt
文件中的所有包。COPY - 复制本机文件或目录,添加到指定的容器目录, 本例中将
requirements.txt
复制到镜像中。WORKDIR - 为RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续参数如果是相对路径,则会基于之前命令指定的路径。
ENTRYPOINT - 在启动容器的时候提供一个默认的命令项。
现在 Dockerfile
已经准备好了,而且也了解了Docker的构建过程,接下来为我们的应用程序创建Docker映像:
1 | docker build -t docker-flask:0.1 . |
容器化的优点,就是开发的应用程序通过容器部署,从一开始就确保了应用程序构建的环境是干净的,从而消除了交付过程中的意外情况。
但是呢,在开发应用程序的过程中,更重要的是要快速重新构建和测试,以检查验证过程中的每个中间步骤。为此,web应用程序的开发人员需要依赖于Flask等框架提供的自动重启功能(Debug模式下,修改代码自动重启)。而这一功能也可以在容器中使用。
为了启用自动重启,在启动Docker容器时将主机中的开发目录映射到容器中的app目录。这样Flask就可以监听主机中的文件变化(通过映射)来发现代码更改,并在检测到更改时自动重启应用程序。
此外,还需要将应用程序的端口从容器转发到主机。这是为了能够让主机上的浏览器访问应用程序。
因此,启动Dokcer容器时需要使用 volume-mapping 和 port-forwarding 选项:
1 | docker run --name flask_app -v $PWD/app:/app -p 1000:5000 docker-flask:0.1 |
该命令将会执行以下操作:
基于之前构建的
docker-flask
镜像启动一个容器;这个容器的名称被设置为
flask_app
。如果没有——name
选项,Docker将为容器生成一个名称。显式指定名称可以帮助我们定位容器(用来停止等操作);-v
选项将主机的app目录挂载到容器;-p
选项将容器的端口映射到主机。
现在可以通过http://localhost:5000
或者 http://0.0.0.0:5000/
访问到应用
然而,然而,第二个坑就是我今天对Flask掌握不足的问题了(,在成功做好端口映射后,我在宿主机环境一直一直curl localhost:10000
失败,返回的字符是curl: (52) Empty reply from server
。很难绷得住,因为这端口好像转发成功了,但是啥也没有,其他的没有服务的端口都是refuse,压根没开,所以这是咋回事呢?我百思不得其解。
终于找到了一篇讲Flask的博客(https://blog.csdn.net/qq_26591517/article/details/86423838),里面有这么提到:
“用python开启flask web服务时:(1)你只需要本机访问,那ip只要不设置为0.0.0.0就可以,正常访问就好。(2)如果你需要外网访问,ip需要设置为0.0.0.0,此时,在本机上访问需要使用默认的127.0.0.1(也就是你不设置ip时默认的ip),在外网上访问则需要使用你本机的ip,不要使用0.0.0.0”
原来如此,我立马把
1 | if __name__ == "__main__": |
改为:
1 | if __name__ == "__main__": |
成功了!