近期有两个flask小项目需要部署在阿里云服务器上,最终成功使用docker完成部署,中间踩了不少坑,特此记录下来,希望帮助别的同学少踩坑。

服务器是阿里云Ubuntu 14.04.5 LTS,使用nginx+gunicory部署,supervisor监控程序。

这里重点说下docker部分,关于docker教程,推荐这个:Docker-从入门到实践

不熟悉docker的同学务必看下相关教程,docker相关安装及如何加速这里我不赘述了,只对我用到的即部署环节所需要注意的进行说明。

我的Dockerfile如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
FROM ubuntu:16.04

LABEL maintainer="BlackRun"
ENV PYTHONIOENCODING=utf-8


RUN  sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
&& sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list \
&& apt-get clean \
&& apt-get update \
&& apt-get install -y python3-pip python3-dev nginx supervisor\
&& rm -rf /var/lib/apt/lists/*
ADD pip.conf /etc/pip.conf
COPY supervisord.conf /etc/supervisord.conf
COPY supervisor.conf /etc/supervisor/
RUN pip3 install --upgrade pip 


COPY . /app
WORKDIR /app
RUN  pip3 install -r requirements.txt \
&& ln -s /app/nginx.conf /etc/nginx/conf.d \
&& rm -rf /etc/nginx/sites-enabled/default \
&& rm -rf /etc/supervisor/supervisord.conf \
&& sed -i 's/nodaemon=false/nodaemon=true/g' /etc/supervisord.conf
EXPOSE 9001
EXPOSE 9002
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

下面一一作出说明: 前三行,说明了基础镜像使用官方的ubuntu:16.04、作者名、设置PYTHONIOENCODING变量名

这里需要注意的是,根据教程,Docker每有一个RUN就会多一层镜像,所以应该避免使用RUN命令。具体请查看前面给出的教程。

1
2
sed -i s@/archive.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list
sed -i s@/security.ubuntu.com/@/mirrors.aliyun.com/@g /etc/apt/sources.list

两行命令,设定ubuntu的apt源为阿里源,加快下面下载所需依赖的速度。其中sed是linux下一种在线编辑器。

1
2
3
4
 apt-get clean \
 apt-get update \
 apt-get install -y python3-pip python3-dev nginx supervisor\
 rm -rf /var/lib/apt/lists/*

这四行对apt进行更新,安装依赖,最后删除相关包,节省空间,熟悉ubuntu的同学应该都理解

1
2
3
4
ADD pip.conf /etc/pip.conf
COPY supervisord.conf /etc/supervisord.conf
COPY supervisor.conf /etc/supervisor/
RUN pip3 install --upgrade pip 

我习惯使用阿里源从python库中下载文件,所以使用pip.conf添加阿里源。2、3行是复制supervisor相关配置文件进image文件里,最后一行更新pip.

pip.conf

1
2
3
[global]
trusted-host=mirrors.aliyun.com
index-url=http://mirrors.aliyun.com/pypi/simple

supervisor.conf

[program:nginx-app]
command = /usr/sbin/nginx -g "daemon off;"
stdout_logfile = /var/log/supervisor/nginx_stdout.log
stdout_logfile_maxbytes = 10MB
stderr_logfile = /var/log/supervisor/nginx_error.log
stderr_logfile_maxbytes = 10MB

[program:student_filter_gunicorn]
command = /usr/local/bin/gunicorn -w 4 -b 127.0.0.1:8002 app:app
directory = /app/student_filter
stdout_logfile = /var/log/supervisor/student_filter_gunicorn_out.log
stdout_logfile_maxbytes = 10MB
stderr_logfile = /var/log/supervisor/student_filter_gunicorn_error.log
stderr_logfile_maxbytes = 10MB

[program:xueji_gunicorn]
command = /usr/local/bin/gunicorn -w 4 -b 127.0.0.1:8003 app_mac:app
directory = /app/xueji
stdout_logfile = /var/log/supervisor/xueji_gunicorn_out.log
stdout_logfile_maxbytes = 10MB
stderr_logfile = /var/log/supervisor/xueji_gunicorn_error.log
stderr_logfile_maxbytes = 10MB

这里使用gunicorn监听8002和8003两个端口运行python应用,然后用nginx作反向代理。 添加日志文件,是为了方便调试,不加也行。

具体supervisor使用教程可以查看我以前的文章supervisor使用小计

1
2
3
4
5
6
7
COPY . /app
WORKDIR /app
RUN  pip3 install -r requirements.txt \
&& ln -s /app/nginx.conf /etc/nginx/conf.d \
&& rm -rf /etc/nginx/sites-enabled/default \
&& rm -rf /etc/supervisor/supervisord.conf \
&& sed -i 's/nodaemon=false/nodaemon=true/g' /etc/

再回到Dockerfile中,上面命令第一行将当前目录下所有文件复制到容器的/app目录下。然后用WORKDIR命令说明工作目录(相当于运行 cd /app)

下面3-7行,分别是安装我的项目用到的python库,建立nginx配置文件的软链接,然后删除nginx和supervisor默认配置文件,将supervisor配置为后台运行。

1
rm -rf /etc/nginx/sites-enabled/default

其中有个大坑就是上面这句一定要有,因为默认配置文件监听了80端口,如果你在配置文件里也监听80端口,导致你访问80端口时候只会显示nginx的欢迎界面。

1
2
3
EXPOSE 9001
EXPOSE 9002
CMD ["supervisord", "-c", "/etc/supervisord.conf"]

最后三行,声明运行容器时提供的服务端口以及运行supervisord命令,将整个应用拉起来运行。

因为项目很小所以我的nginx.conf很简单,需要其他功能的同学请自行添加 nginx.conf

server {
   listen  9002;
   server_name 127.0.0.1;

   location  /  {
      proxy_pass   http://127.0.0.1:8002;
   }
  }

  server {
   listen  9003;
   server_name 127.0.0.1;

   location  /  {
      proxy_pass   http://127.0.0.1:8003;
   }
  }

最后运行在Dockerfile目录下运行下面命令即可生成容器,一切顺利的话就可以在浏览器里访问到你的应用了。

1
2
docker image build -t flask .
docker container run --name flask -d -p 9002:9002 -p 9003:9003 flask

我还用到了阿里云提供的镜像服务,建立了自己的镜像仓库,将本地生成好的image推送到阿里云镜像仓库,然后在云服务器上再拉取下来,方便快捷。具体可以看:阿里云容器镜像服务,目前是免费的。当然你也可以推送到docker官方的仓库。

下面简单说下踩的坑和一些排错技巧:

一、上面提到的一定要删除nginx的默认配置文件。 二、在ubuntu上安装docker不要使用apt-get安装,使用下面命令安装

1
sudo curl -sSL https://get.docker.com/ | sh

三、docker官方的ubuntu镜像,少了很多东西,不方便调试,所以可以在容器中使用下面命令安装一些常用的工具:

1
2
apt-get update
apt-get install -y lsof net-tools curl vim

然后结合相关的日志就可以排错。

我最后在阿里云服务器上拉取镜像后,发现通过浏览器仍然无法访问程序,这时候可以用docker exec 命令进入到容器里,然后运行下面命令:

1
2
curl http://127.0.0.1:8002
curl http://127.0.0.1:9002

我的返回了正确的html代码,说明我的部署没有问题,看了下阿里云服务器,我的是专有网络,在安全组里打开9002和9003即可正常访问。