美文网首页
【创建一个简单的web应用】

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

作者: 傻白甜心 | 来源:发表于2018-02-26 20:06 被阅读0次
《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.总结

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

相关文章

网友评论

      本文标题:【创建一个简单的web应用】

      本文链接:https://www.haomeiwen.com/subject/cgiyxftx.html