目录结构

  1. $ tree identidock/
  2. identidock/
  3. ├── app
  4. └── identidock.py
  5. ├── cmd.sh
  6. ├── docker-compose.yml
  7. ├── Dockerfile
  8. └── Dockerfile.bak

identidock.py

  1. from flask import Flask
  2. app = Flask(__name__)
  3. @app.route('/')
  4. def hello_world():
  5. return 'Hello Docker!\n'
  6. if __name__ == '__main__':
  7. app.run(debug=True, host='0.0.0.0')

构建

  1. $ docker build -t identidock .

Digest: sha256:7a7e47efdfd6503afcf56a57f981bcc23d54211ad3b3c21cbe4bd597873fa6e1Status: Downloaded newer image for python:3.4
—-> 8c62b065252f
Step 2/5 : RUN pip install Flask==0.10.1
—-> Running in fa9fb047fee1
Collecting Flask==0.10.1
Removing intermediate container fa9fb047fee1
—-> 37fc7a1155ec
Step 3/5 : WORKDIR /app
—-> Running in 819ed30c1c35
Removing intermediate container 819ed30c1c35
—-> fe27c7494c03
Step 4/5 : COPY app /app
—-> 1855871f890a
Step 5/5 : CMD [“python”,”identidock.py”]
—-> Running in 7577e033d7bd
Removing intermediate container 7577e033d7bd
—-> 52a82f0b0f2a
Successfully built 52a82f0b0f2a
Successfully tagged identidock:latest

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. identidock latest 52a82f0b0f2a 56 seconds ago 935MB
  4. python 3.4 8c62b065252f 2 years ago 924MB

运行

  1. $ docker run -d -p 5000:5000 identidock
  2. 74103e65731ce6ee4713430964e618a2dca8cdc77fb7e03d08c8933706a6cca7

测试

  1. $ curl localhost:5000
  2. Hello Docker!

绑定挂载

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 74103e65731c identidock "python identidock.py" 27 minutes ago Up 27 minutes 0.0.0.0:5000->5000/tcp unruffled_perlman
  1. $ docker stop $(docker ps -lq)
  2. 74103e65731c
  3. $ docker rm `docker ps -lq`
  4. 74103e65731c

停止并删除上次运行的容器

docker run -d -p 5000:5000 -v "$PWD"/app:/app identidock

uWSGI:可立刻用于生产环境的应用服务器

这里出现报错,详情请见👇
容器pip安装第三方库报错PY
Dockerfile

  1. FROM python:3.4
  2. RUN pip install Flask==0.10.1 uWSGI
  3. WORKDIR /app
  4. COPY app /app
  5. CMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py", \
  6. "--callable","app","--stats","0.0.0.0:9191"]

CMD中启动一个监听9090端口的HTTP服务器,在9191端口启动一个数据统计服务器,其实我们还可以在运行docker run的时候,重新定义CMD内容

image.png

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. identidock latest 9ee42844f0a2 2 minutes ago 938MB
  4. python 3.7 9337bc3e7477 2 weeks ago 877MB
  5. python 3.4 8c62b065252f 2 years ago 924MB
  6. $ docker run -d -p 9090:9090 -p 9191:9191 identidock
  7. a4a5d38590c458ce2520983eb1c4fffcd34afee20e9bdb6e8deb8a63845724bb
  8. $ curl localhost:9090
  9. Hello Docker!

细分权限,端口,用户提升安全性

  1. FROM python:3.4
  2. RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi #创建uwsgi用户和用户组
  3. RUN pip install Flask==0.10.1 uWSGI
  4. WORKDIR /app
  5. COPY app /app
  6. EXPOSE 9090 9191 #声明主机和其他容器可以访问的端口
  7. USER uwsgi #以uwsgi用户执行这一行后面的所有的命令(包括CMD和ENTRYPOINT)

$ docker build -t identidock:1 .
$ docker run -d -P --name port-test identidock:1
0eafeb8f7195faf1919f680c830f64a9a035e025a50d5e3f3c715e301df3f327
-P 参数 随机选择一些高端口映射到容器的每一个已经EXPOSE声明的端口
dropann@dropann:~/using_docker_in_dev/identidock$ docker port port-test
9090/tcp -> 0.0.0.0:32769
9191/tcp -> 0.0.0.0:32768
通过环境变量和一个简单的脚本,按实际的运行环境切换不同功能
cmd.sh

  1. #!/bin/bash
  2. set -e
  3. if [ "$ENV" = 'DEV' ]; then #如果ENV变量设定为DEV,运行调试的web服务器
  4. echo "Running Development Server"
  5. exec python "identidock.py"
  6. else
  7. echo "Running Production Server" #否则运行生产服务器
  8. exec uwsgi --http 0.0.0.0:9090 --wsgi-file /app/identidock.py \
  9. --callable app --stats 0.0.0.0:9191
  10. fi

其中exec命令的目的是为了避免创建一个新进程,以确保uwsgi进程能够收到所有信号,而不是被父进程拦截

  1. FROM python:3.4
  2. RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
  3. RUN pip install Flask==0.10.1 uWSGI
  4. WORKDIR /app
  5. COPY app /app
  6. COPY cmd.sh /
  7. EXPOSE 9090 9191
  8. USER uwsgi
  9. CMD ["/cmd.sh"]
  1. chmod -x cmd.sh
  2. docker build -t identidock .
  3. docker run -e "ENV=DEV" -p 5000:5000 identidock

一个简单的web应用

目录结构

  1. $ tree creating-a-simple-web-app/
  2. creating-a-simple-web-app/
  3. ├── identidock
  4. ├── app
  5. └── identidock.py
  6. ├── cmd.sh
  7. ├── docker-compose.yml
  8. ├── Dockerfile
  9. └── Dockerfile.bak
  10. ├── LICENSE
  11. └── README.md

Dockerfile

  1. FROM python:3.4
  2. RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
  3. RUN pip install Flask==0.10.1 uWSGI requests==2.5.1 redis==2.10.3
  4. WORKDIR /app
  5. COPY app /app
  6. COPY cmd.sh /
  7. EXPOSE 9090 9191
  8. USER uwsgi
  9. CMD ["/cmd.sh"]

docker-compose.yml

  1. identidock:
  2. build: .
  3. ports:
  4. - "5000:5000"
  5. environment:
  6. ENV: DEV
  7. volumes:
  8. - ./app:/app
  9. links:
  10. - dnmonster
  11. - redis
  12. dnmonster:
  13. image: amouat/dnmonster:1.0
  14. redis:
  15. image: redis:3.0

identicon.py

  1. from flask import Flask, Response, request
  2. import requests
  3. import hashlib #导入对用户输入进行散列处理的程序库
  4. import redis
  5. app = Flask(__name__)
  6. cache = redis.StrictRedis(host='redis', port=6379, db=0) #建立redis缓存
  7. salt = "UNIQUE_SALT" #定义散列函数salt值
  8. default_name = 'Joe Bloggs'
  9. @app.route('/', methods=['GET', 'POST']) #Flask默认只相应get请求,必须给route声明加入methods参数,宣告route能处理post请求
  10. def mainpage():
  11. name = default_name
  12. if request.method == 'POST':
  13. name = request.form['name']
  14. salted_name = salt + name
  15. name_hash = hashlib.sha256(salted_name.encode()).hexdigest() #执行SHA256算法,以获取散列值
  16. header = '<html><head><title>Identidock</title></head><body>'
  17. body = '''<form method="POST">
  18. Hello <input type="text" name="name" value="{0}">
  19. <input type="submit" value="submit">
  20. </form>
  21. <p>You look like a:
  22. <img src="/monster/{1}"/>
  23. '''.format(name, name_hash)
  24. footer = '</body></html>'
  25. return header + body + footer
  26. @app.route('/monster/<name>')
  27. def get_identicon(name):
  28. image = cache.get(name) #检查名字是否在缓存中
  29. if image is None: #如果返回None,继续调用idention
  30. print ("Cache miss", flush=True) #输出调试信息,表示没有在缓存中找到图像
  31. r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')
  32. image = r.content
  33. cache.set(name, image)# 把图像加入缓存,与名字关联
  34. return Response(image, mimetype='image/png')
  35. if __name__ == '__main__':
  36. app.run(debug=True, host='0.0.0.0')