美文网首页
【创建一个简单的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