docker部署python web应用_使用docker一键部署Web应用

前几天借着部署一个小系统的机会尝试了一把docker,把Web应用,数据库,nginx统统都用docker管理。把打包好的代码扔到服务器上一键就部署好了,不需要考虑服务器的环境问题,简直不要太爽。但感觉还是有蛮多坑的,于是做了一个小demo熟悉了一遍。demo在这里,是一个包含了Flask应用,Nginx,MongoDB数据库的Web应用。clone下来直接执行./depoly.sh就能运行了。

接下来总结下其中需要注意的地方。

整体结构

├── db

│ └── dump

├── docker-compose.yml

├── deploy.sh

├── destroy.sh

├── nginx

│ ├── default.conf

│ ├── Dockerfile

│ └── html

│ └── index.html

└── web

├── app

│ ├── app.py

│ ├── __init__.py

├── Dockerfile

├── requirements.txt

└── start.py

MongoDB的镜像不需要做任何修改,db目录下存放的只是需要导入的初始化数据。

nginx目录下存放的是nginx的配置文件、前端代码和对应的Dockerfile文件。

web目录下是Flask app的代码和对应的Dockerfile文件。

docker-compose.yml统一管理需要用到的三个容器。

Dockerfile VS docker-compose

整个项目中就是通过这两个文件操作docker的,刚开始接触可能不是很容易理解他们的差别。比如有时只需要一个Dockerfile,而又有时候只需要一个docker-compose.yml。

Dockerfile 负责构建镜像,因为很多时候从hub上拉去下来的镜像不够符合我们的要求,需要一些定制化的修改。比如用于运行flask应用的是一个python镜像,我们需要给他安装一些python包。

# ./app/Dockerfile

FROMpython:2.7

ADD. /app

WORKDIR/app

RUN pip install -r requirements.txt

在这里我们做的只是把文件导入到docker中,然后安装python依赖包。

docker-compose 有两个比较重要的作用,一个是操作容器,比如暴露端口、挂载目录实现与宿主机文件同步、管理容器网络、执行命令等。这些功能都可以通过docker run后面跟一堆参数实现,但是通过docker-compose实现会清晰和方便很多。另一个作用是统一管理多个容器,一个项目往往需要同时使用多个容器,你肯定希望用一个docker-compose up启动他们而不是一个一个地去docker run。

其实这两者的区别也不是那么清晰,比如上面Dockerfile中导入文件的操作,可以放到compose中变成目录挂载,让宿主机和docker中的文件同步,这样利用Flask的debug模式的热启动,修改宿主机的文件就能立即看到docker中的运行效果了。

docker中的网络

刚开始接触docker的时候可能并不会太注意docker的网络,完全使用默认配置也没什么问题。但是在一个需要多个容器的应用中,不了解docker的网络可能就不知道如何把多个容器串联起来了。

docker服务器启动后会自动创建三个网络:bridge、host、none。

# docker network ls

NETWORK ID NAME DRIVER SCOPE

f3193c939ac5 bridge bridge local

560e7d180738 host host local

e2f3c776cd7c none null local

默认情况下容器启动后都会加入使用bridge模式的bridge网络。在这种模式中,容器使用一个内网IP,并把网关指向docker服务器创建的虚拟网关docker0。

# docker inspect

...

"Gateway": "172.17.0.1",

"IPAddress": "172.17.0.4",

...

通过docker inspect可以看到我们运行的容器IP和它的网关docker0的IP,在宿主机上可以直接ping这个地址。同一个网络中的容器可以相互通信,而不同网络中的容器由于默认不在同一个子网,不能相互通信。

compose file 版本差异

docker compose file已经发布到第三版,第三版主要增加了docker swarm相关的内容。第二版与第三版比较兼容,第一版与第二版的差别比较大,网上的很多示例都是基于第一版,然而第一版在接下来的release中已经准备废弃了,所以在这里记录下几个第一版与第二版差别较大的地方。

容器互联

在前面对网络的介绍中我们知道,每一个容器都有一个IP,容器之间通过IP地址相互通信。但是通常情况下IP地址都是新建容器的时候自动分配,所以用硬编码的方式将其他容器的地址写到代码里是不行的。

在第一版中,通常使用links环境变量注入。使用links命令会将被连接容器的所有环境变量都传递给连接的容器。比如在web应用中连接数据库,就是用link连接数据库容器:

web:

build: .

command: python -u app.py

ports:

- "5000:5000"

volumes:

- .:/todo

links:

- db

db:

image: mongo:3.0.2

然后在web代码中从环境变量中获取数据库地址:

client = MongoClient(

os.environ['DB_PORT_27017_TCP_ADDR'],

27017)

此时web容器中可以获取到所有db容器的环境变量。

在第二版以后,links就是一个即将被弃用的命令了。因为这种注入所有环境变量的方式不太可控,所以建议使用卷共享这种更可控的方式实现环境变量共享。如果容器在同一个网络中,直接使用容器名,就可以实现容器互联。

services:

web:

build: ./web

ports:

- "5678"

volumes:

- /tmp/app

command: /usr/local/bin/gunicorn -w 2 -b :5678 start:app

db:

image: mongo:3.4

container_name: demo_db

from mongoengine import *

connect(db='demo', host='demo_db')

这里直接使用容器名demo_db就可以连接到MongoDB数据库了。

默认网络

在第一版中,使用docker-compose启动的容器都会默认被加入到bridge网络中,与其他使用docker run启动的容器共用一个网络。但在第二版以后,使用docker-compose启动容器时,会创建一个以docker-compose.yml所在文件夹名为前缀的网络。

docker_deploy_demo# docker network ls

NETWORK ID NAME DRIVER SCOPE

4a2bd496dc63 bridge bridge local

c3bc371f694d host host local

10d54b17f5ab none null local

8beddd847aad dockerdeploydemo_default bridge local

比如这里文件夹名是docker_deploy_demo,所以新建的bridge网络是dockerdeploydemo_default。所以使用其他容器连接docker-compose所管理的容器时,需要指定他们所在的网络。比如操作数据库容器导入数据:

docker run --rm -v $('pwd')/db/dump:/backup --net dockerdeploydemo_default mongo bash -c 'mongorestore /backup --host demo_db:27017'

镜像标签

为镜像打个标签可以方便管理自己创建的镜像,在第二版中,如果镜像是由dockerfile生成,那么可以用image为镜像自定义标签。

nginx:

build: ./nginx

image: demo_nginx

ports:

- "80:80"

volumes:

- ./nginx/html:/usr/share/nginx/html

container_name: demo_nginx

但是在第一版中,build和image是不能共存的。

HTTPS

现在使用https已经是Web应用的标配了,在docker中配置https跟真机中并没有太大差别:先获取证书和密钥,再在nginx中配置好证书和密钥。我另外写了一个小demo演示了在docker中配置https,包含了使用letsencrypt的CA认证证书和自签证书。地址在这里。

你可能感兴趣的:(docker部署python,web应用)