Docker,可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

Docker“真香定律”:不会用的时候觉得复杂,用顺手了就再也放不下了。

今天我们就带大家从 0 到 1 构建一个自己的 Docker 应用。

使用的语言和应用的版本如下:Python 3.8.1,Flask库 1.1.1,Redis库 3.4.1。

1. web 应用

这次我们使用 Python 来编写我们的 web 应用,上一次我们使用的是 Go 语言。

由于 Go 语言部署直接使用二进制文件,Dockerfile 会极其简单,为了让大家熟悉一下 Dockerfile 的应用,所以这里我们使用 Python 语言里编写我们的 web 应用。

1.1 Flask 安装

Flask 安装很简单,和其他 Python 依赖安装基本没有区别。

pipinstallflask

1.2 Flask demo

我们前面说了 Flask 是一个非常轻量的 web 框架.

那么有多轻量呢?

轻量到我们使用下面几行代码就可以构建出来一个简单的 web 应用。

fromflaskimportFlask

app=Flask(__name__)

@app.route('/')
defhello():
return'Hello,Flask'

启动应用。

$envFLASK_APP=hello.pyflaskrun
*ServingFlaskapp"hello"
*Runningonhttp://127.0.0.1:5000/(PressCTRL+Ctoquit)

应用默认启动在 5000 端口,我们可以通过 -p 参数指定引用的启动端口。

当然 Flask 还支持其他参数,我们可以通过 flask run --help 进行查看。

root@a36d1df88169:/#flaskrun--help
Usage:flaskrun[OPTIONS]

Runalocaldevelopmentserver.

Thisserverisfordevelopmentpurposesonly.Itdoesnotprovidethe
stability,security,orperformanceofproductionWSGIservers.

ThereloaderanddebuggerareenabledbydefaultifFLASK_ENV=development
orFLASK_DEBUG=1.

Options:
-h,--hostTEXTTheinterfacetobindto.
-p,--portINTEGERTheporttobindto.
--certPATHSpecifyacertificatefiletouseHTTPS.
--keyFILEThekeyfiletousewhenspecifyinga
certificate.
--reload/--no-reloadEnableordisablethereloader.Bydefault
thereloaderisactiveifdebugisenabled.
--debugger/--no-debuggerEnableordisablethedebugger.Bydefault
thedebuggerisactiveifdebugisenabled.
--eager-loading/--lazy-loader
Enableordisableeagerloading.Bydefault
eagerloadingisenabledifthereloaderis
disabled.
--with-threads/--without-threads
Enableordisablemultithreading.
--extra-filesPATHExtrafilesthattriggerareloadonchange.
Multiplepathsareseparatedby':'.
--helpShowthismessageandexit.

应用启动了之后,我们可以访问 5000 端口来验证应用是不是正常的。

[root@docker~]#curllocalhost:5000
Hello,Flask

1.3 Flask 使用

上面介绍了 Flask 最简单的使用 demo,下面我们使用 Flask 来编写我们应用和 Redis 进行交互。

首先我们也要先安装 Python 依赖库:redis。

pip install redis 我们主要要实现三个功能:

  1. redis 连接
  2. 提供一个 route set 实现对 redis 中的值进行设置
  3. 提供一个 route get 实现对 redis 中的值进行查询

redis 连接

redis 连接,我们直接使用 Python 的依赖库 Redis。

importredis

redis_client=redis.Redis(host=redis_host,port=redis_port,db=0)

其中连接 Redis 需要使用三个参数:

  • host: redis 的 host
  • port: redis 的端口
  • db:redis 中的数据库,我们使用 db = 0 即可。

这里的一个核心问题是 redis 运行在另外一个 Docker 中,那我们在应用的 Docker 中如何连接 redis 实例呢?也就是如何发现redis 的 host 和 port 呢?

在 Docker 技术中我们可以在启动 Docker 的时候指定参数 –link 将两个 Docker 的网络进行打通。在下面部署的时候我们再细说。

set route

编写一个 route,可以对 redis 进行写入。

@app.route('/set')
defset():
key=request.args.get("key")
value=request.args.get("value")
redis_client.set(key,value)
return'OK.Wehaveset'+key+'tobe'+value

其中 request.args 中可以获取到 url 中的参数。但是上面的代码没有做参数校验,key 和 value 可能是空,我们加一个参数校验的逻辑。


@app.route('/set')
defset():
key=request.args.get("key")
value=request.args.get("value")
ifkeyisNoneorvalueisNone:
return'OOps,thekeyorvalueisNULL'
redis_client.set(key,value)
return'OK.Wehaveset'+key+'tobe'+value

get route

编写一个 route 对 redis 中的值查询

@app.route('/get')
defget():
key=request.args.get('key')
ifkeyisNone:
return'OOps,thekeyisnull'
value=redis_client.get(key)
returnvalue

至此,我们的 web 应用代码编写完成,完整的代码如下,其中 redis-host 现在还是一个占位符,我们部署的时候会把这个变量注入进来。

fromflaskimportFlask,request
importredis

redis_client=redis.Redis(host='redis-host',port=6379,db=0)
app=Flask(__name__)

@app.route('/set')
defset():
key=request.args.get('key')
value=request.args.get('value')
ifkeyisNoneorvalueisNone:
return'OOps,thekeyorvalueisNULL'
redis_client.set(key,value)
return'OK.Wehaveset'+key+'tobe'+value

@app.route('/get')
defget():
key=request.args.get('key')
ifkeyisNone:
return'OOps,thekeyisnull'
value=redis_client.get(key)
returnvalue

2. Dockerfile

下面开始编写我们的 Dockerfile。

回忆一下我们上面编写 web 应用过程中,主要安装了依赖 flask 和 redis 依赖。我们可以很简单写出来我们的 Dockerfile 如下,并命名为 Dockerfile。

frompython:3

RUNpipinstallflask
RUNpipinstallredis
RUNmkdir/data

COPYhello.py/data/
WORKDIR/data

EXPOSE5000
ENVFLASK_APP=/data/hello.py
ENTRYPOINT["flask","run","-h","0.0.0.0"]

我们对这个 Dockerfile 进行一个简单解释:

  • from:表示基础镜像是 python:3;
  • RUN:表示在 docker build 的时候会执行后面的几个命令;
  • COPY:拷贝文件或者目录都可以;
  • WORKDIR:表示启动容器之后,当前的工作目录;
  • EXPOSE:表示容器要暴露 5000 端口;
  • ENV:环境变量;
  • ENTRYPOINT:表示 Docker 容器的启动进程。这里 entrypoint 中的 flask run 我们增加了参数 -h 0.0.0.0。如果不加这个参数的话,进程默认绑定到 127.0.0.1,外面是没有办法访问的。

通过该 dockerfile 来构建镜像。基本每一个命令都会对应一个 step,如下。

[root@dockerweb]#dockerbuild-tweb:v1.
SendingbuildcontexttoDockerdaemon3.584kB
Step1/8:frompython:3
--->efdecc2e377a
Step2/8:RUNpipinstallflask
--->Runninginc4dfe7b3e466
Collectingflask
DownloadingFlask-1.1.1-py2.py3-none-any.whl(94kB)
Collectingitsdangerous>=0.24
Downloadingitsdangerous-1.1.0-py2.py3-none-any.whl(16kB)
CollectingJinja2>=2.10.1
DownloadingJinja2-2.11.1-py2.py3-none-any.whl(126kB)
CollectingWerkzeug>=0.15
DownloadingWerkzeug-1.0.0-py2.py3-none-any.whl(298kB)
.......
Step8/8:ENTRYPOINT["flask","run"]
--->Runningin25594e1de72f
Removingintermediatecontainer25594e1de72f
--->d18b55e4d1fd
Successfullybuiltd18b55e4d1fd
Successfullytaggedweb:v1

构建成功之后,我们可以通过 docker images 查看到我们刚才 build 出来的镜像 web。

[root@dockerdemo]#dockerimages|grepweb
webv102cc264143dc6minutesago943MB

3. 部署

我们先来部署一个 Redis Docker,-d 参数表示以 daemon 的方式运行。-p 表示端口映射。-name 表示 Docker 容器的名字叫 redis-test。

dockerrun--nameredis-test-p6379:6379-dredis:latest

下面部署我们的 web 应用。

[root@docker~]#dockerrun-p5000:5000--linkredis-test:redis-host-d--namewebweb:v1
64eef1f67c3934b6257510f47b587c59cee635188a4043b749966e71d2bc8c08
[root@docker~]#dockerps
CONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES
64eef1f67c39web:v1"flaskrun-h0.0.0.0"2secondsagoUp1second0.0.0.0:5000->5000/tcpweb

其中有一个运行参数需要进行简单说明,也就是 –link。

link 后面跟一对映射的值,左侧的为已经存在的 Docker 容器,右侧的为该容器映射到我们启动的 Docker 应用中的 host 名字,这里也就是 web 这个 Docker 容器。

我们下面通过 docker exec 进入到容器中看一下 link 是怎么做的。

[root@docker~]#dockerexec-ti64eef1f67c39/bin/bash
root@64eef1f67c39:/data#

我们查看一下 hosts 文件。

root@64eef1f67c39:/data#cat/etc/hosts
127.0.0.1localhost
::1localhostip6-localhostip6-loopback
fe00::0ip6-localnet
ff00::0ip6-mcastprefix
ff02::1ip6-allnodes
ff02::2ip6-allrouters
172.17.0.5redis-host0d748e8ce766redis-test
172.17.0.664eef1f67c39

我们可以看到 redis-host 已经被写到 hosts 中,所以我们在 web 这个 Docker 容器中就可以通过 redis-host 这个主机名访问到 Redis 容器了,这也是我们的应用代码的写法。

4. 验证

部署完成,我们下面进行一个简单的验证。在宿主机上执行下面命令去设置一对 kv: 写入到 Redis 中。

[root@docker~]#curl"localhost:5000/set?key=imooc&value=imooc.com"
OK.Wehavesetimooctobeimooc.com

第二个请求去读取该值,如下。

[root@docker~]#curl"localhost:5000/get?key=imooc"
imooc.com

5. 总结

至此,我们第一个动手实践的 Docker 应用已经完成。本来想弄一个更复杂的应用,但是限于篇幅,只能做了一下取舍。虽然简单,还是建议各位同学进行动手实践。毕竟纸上得来终觉浅,绝知此事要躬行。

作者| legendtkl

– END –

本文由 mdnice 多平台发布