【创建一个简单的web应用】

《Docker开发指南中文版》

此章节为《Docker开发指南》的第六章—— 创建一个简单的web应用 的个人读书笔记

本章节内容如下:

  1. 创建一个基本网页
  2. 利用现有镜像
  3. 实现缓存功能
  4. 微服务
  5. 总结

想要创建一个简单的identicon独一无二的图像的Web应用程序
案例可以从书的github获取代码

git clone -b v0 https://github.com/using-docker/creating-a-simple-web-app/

1. 创建一个基本网页

为了简单起见,用字符串返回html,把identidock.py换成以下内容:

from flask import Flask

app = Flask(__name__)
default_name = 'Joe Bloggs'

@app.route('/')
def mainpage():

    name = default_name
  
    header = '<html><head><title>Identidock</title></head><body>'
    body = '''<form method="POST">
              Hello <input type="text" name="name" value="{}">
              <input type="submit" value="submit">
              </form>
              <p>You look like a:
              <img src="/monster/monster.png"/>
              '''.format(name)
    footer = '</body></html>'

    return header + body + footer

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')

format的{}替换成了name变量的值,然后执行

docker-compose up -d

打开浏览器访问http://localhost:5000,得到简单的网页

2. 利用现有镜像

这个实例中,我们会有用到一个现有的Docker镜像dnmonster,它可以将字符串变成独一无二的图像,还提供了差不多的RESTful API供我们使用,我们还可以将另一个的identicon服务将dnmonster替换掉。我们做如下修改

from flask import Flask, Response, request
import requests

app = Flask(__name__)
default_name = 'Joe Bloggs'

@app.route('/')
def mainpage():

    name = default_name

    header = '<html><head><title>Identidock</title></head><body>'
    body = '''<form method="POST">
              Hello <input type="text" name="name" value="{}">
              <input type="submit" value="submit">
              </form>
              <p>You look like a:
              <img src="/monster/monster.png"/>
              '''.format(name)
    footer = '</body></html>'

    return header + body + footer

@app.route('/monster/<name>')
def get_identicon(name):

    r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')
    image = r.content

    return Response(image, mimetype='image/png')

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')
  1. 导入Flask的Response返回图像
  2. 导入Requests库,用于与dnmonster服务通讯
  3. 发送一个http get请求到dnmonster,对应name变量值的identicon图像,大小为80像素
  4. return一个png图片

接下来对Dockerfile做一个小修改,使得代码能够使用正确的程序库

FROM python:3.4

RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask==0.10.1 uWSGI==2.0.8 requests==2.5.1
WORKDIR /app
COPY app /app
COPY cmd.sh /

EXPOSE 9090 9191
USER uwsgi

CMD ["/cmd.sh"]

准备就绪,从docker hub拉下dnmonster镜像

docker build -t indenidock .

docker run -d --name dnmonster amouat/dnmonster:1.0

或者指定在8080访问dnmonster

docker run -d -p 5000:5000 -e "ENV=DEV" --link dnmonster:dnmonster identidock

当你再次打开htttp://localhost:5000,可以获得一个输入字符串,得到identicon的画面

但是。。。。。提交按钮还没有生效,我们将compose重拾起来,还要记住docker run,按照下列内容更新docker-compose.yml:

identidock:
  build: .
  ports:
   - "5000:5000"
  environment:
    ENV: DEV 
  volumes:
    - ./app:/app
  links:
    - dnmonster
    - redis

dnmonster:
  image: amouat/dnmonster:1.0

redis:
  image: redis:3.0
  1. 声明从identidock到dnmonster容器的连接,Compose会负责容器的正确启动顺序
  2. 定义新的dnmonster容器。来源于amouat/dnmonster:1.0
    移除之前运行的所有容器,接着使用compose来重建和运行应用:
docker rm $(docker stop $(docker ps -q))

docker-compose build 
docker compose up -d

更新identicon.py内容如下:

from flask import Flask, Response, request
import requests
import hashlib


app = Flask(__name__)
salt = "UNIQUE_SALT"
default_name = 'Joe Bloggs'


@app.route('/', methods=['GET', 'POST'])
def mainpage():

   name = default_name
   if request.method == 'POST':
       name = request.form['name']

   salted_name = salt + name
   name_hash = hashlib.sha256(salted_name.encode()).hexdigest()
   header = '<html><head><title>Identidock</title></head><body>'
   body = '''<form method="POST">
             Hello <input type="text" name="name" value="{0}">
             <input type="submit" value="submit">
             </form>
             <p>You look like a:
             <img src="/monster/{1}"/>
             '''.format(name, name_hash)
   footer = '</body></html>'

   return header + body + footer


@app.route('/monster/<name>')
def get_identicon(name):

   r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')
   image = r.content

   return Response(image, mimetype='image/png')

if __name__ == '__main__':
   app.run(debug=True, host='0.0.0.0')
  1. 导入对用户输入进行散列处理的程序库
  2. 定义散列函数的salt值,通过改变这个值,相同的输入在不同的网站可以生成不一样的identicon
  3. 表单提交是http post请求,所以必须给route声明加入methods命名的参数,明确宣告route可以处理post和get
  4. 如果method等同于post,并来自于点击提交按钮,需要用户赋值输入name
  5. 对输入执行SHA256算法,以获取散列值
  6. 用散列值改变图像url,在加载时候,调用get_identicon

3. 实现缓存功能

使用Redis实现缓存功能

from flask import Flask, Response, request
import requests
import hashlib
import redis

app = Flask(__name__)
cache = redis.StrictRedis(host='redis', port=6379, db=0)
salt = "UNIQUE_SALT"
default_name = 'Joe Bloggs'


@app.route('/', methods=['GET', 'POST'])
def mainpage():

    name = default_name
    if request.method == 'POST':
        name = request.form['name']

    salted_name = salt + name
    name_hash = hashlib.sha256(salted_name.encode()).hexdigest()
    header = '<html><head><title>Identidock</title></head><body>'
    body = '''<form method="POST">
              Hello <input type="text" name="name" value="{0}">
              <input type="submit" value="submit">
              </form>
              <p>You look like a:
              <img src="/monster/{1}"/>
              '''.format(name, name_hash)
    footer = '</body></html>'

    return header + body + footer


@app.route('/monster/<name>')
def get_identicon(name):

    image = cache.get(name)
    if image is None:
        print ("Cache miss", flush=True)
        r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')
        image = r.content
        cache.set(name, image)

    return Response(image, mimetype='image/png')

if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')
  1. 导入redis模块
  2. 建立redis缓存,通过docker连接的特性,让redis这个主机名能够被解析
  3. 检查名字中是否已经存在在缓存中
  4. 如果缓存未命中,返回None
  5. 输出一些调试信息,表示我们没有在缓存中找到图像
  6. 把图像添加到缓存中,并与名字关联

更新Dockerfile和docker-compose.yml的内容:

FROM python:3.4

RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask==0.10.1 uWSGI==2.0.8 requests==2.5.1 redis==2.10.3
WORKDIR /app
COPY app /app
COPY cmd.sh /

EXPOSE 9090 9191
USER uwsgi

CMD ["/cmd.sh"]
FROM python:3.4

RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask==0.10.1 uWSGI==2.0.8 requests==2.5.1 redis==2.10.3
WORKDIR /app
COPY app /app
COPY cmd.sh /

EXPOSE 9090 9191
USER uwsgi

CMD ["/cmd.sh"]
identidock:
  build: .
  ports:
   - "5000:5000"
  environment:
    ENV: DEV 
  volumes:
    - ./app:/app
  links:
    - dnmonster
    - redis

dnmonster:
  image: amouat/dnmonster:1.0

redis:
  image: redis:3.0

现在,如果你先用docker-compose stop停止identicon,可以用docker-compose build和docker-compose up来启动新的版本。

4.微服务

我们的identicon应用由3个容器组成,其中一个用python的web程序,它与一个用javascript写成的服务,以及一个用c语言实现的键值数据库通讯。后面的章节将介绍如何使用少量的工作来使得更多的微服务接到identicon,包括第九章的反向代理,第十章的监控和日志记录方案。

5.总结

现在我们的应用已经基本可用,虽然很简单,但是功能有了,我们已经了解如何重复使用现有的镜像。更重要的是,我们还看到了容器如何自然演变成一组具有明确功能的小服务,它们可以交互形成一个更大的系统。这就是微服务框架。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容