目录结构
$ tree identidock/identidock/├── app│ └── identidock.py├── cmd.sh├── docker-compose.yml├── Dockerfile└── Dockerfile.bak
identidock.py
from flask import Flaskapp = Flask(__name__)@app.route('/')def hello_world():return 'Hello Docker!\n'if __name__ == '__main__':app.run(debug=True, host='0.0.0.0')
构建
$ 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
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEidentidock latest 52a82f0b0f2a 56 seconds ago 935MBpython 3.4 8c62b065252f 2 years ago 924MB
运行
$ docker run -d -p 5000:5000 identidock74103e65731ce6ee4713430964e618a2dca8cdc77fb7e03d08c8933706a6cca7
测试
$ curl localhost:5000Hello Docker!
绑定挂载
$ docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES74103e65731c identidock "python identidock.py" 27 minutes ago Up 27 minutes 0.0.0.0:5000->5000/tcp unruffled_perlman
$ docker stop $(docker ps -lq)74103e65731c$ docker rm `docker ps -lq`74103e65731c
停止并删除上次运行的容器
docker run -d -p 5000:5000 -v "$PWD"/app:/app identidock
uWSGI:可立刻用于生产环境的应用服务器
这里出现报错,详情请见👇
容器pip安装第三方库报错PY
Dockerfile
FROM python:3.4RUN pip install Flask==0.10.1 uWSGIWORKDIR /appCOPY app /appCMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py", \"--callable","app","--stats","0.0.0.0:9191"]
CMD中启动一个监听9090端口的HTTP服务器,在9191端口启动一个数据统计服务器,其实我们还可以在运行docker run的时候,重新定义CMD内容

$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEidentidock latest 9ee42844f0a2 2 minutes ago 938MBpython 3.7 9337bc3e7477 2 weeks ago 877MBpython 3.4 8c62b065252f 2 years ago 924MB$ docker run -d -p 9090:9090 -p 9191:9191 identidocka4a5d38590c458ce2520983eb1c4fffcd34afee20e9bdb6e8deb8a63845724bb$ curl localhost:9090Hello Docker!
细分权限,端口,用户提升安全性
FROM python:3.4RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi #创建uwsgi用户和用户组RUN pip install Flask==0.10.1 uWSGIWORKDIR /appCOPY app /appEXPOSE 9090 9191 #声明主机和其他容器可以访问的端口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-test9090/tcp -> 0.0.0.0:327699191/tcp -> 0.0.0.0:32768
通过环境变量和一个简单的脚本,按实际的运行环境切换不同功能
cmd.sh
#!/bin/bashset -eif [ "$ENV" = 'DEV' ]; then #如果ENV变量设定为DEV,运行调试的web服务器echo "Running Development Server"exec python "identidock.py"elseecho "Running Production Server" #否则运行生产服务器exec uwsgi --http 0.0.0.0:9090 --wsgi-file /app/identidock.py \--callable app --stats 0.0.0.0:9191fi
其中exec命令的目的是为了避免创建一个新进程,以确保uwsgi进程能够收到所有信号,而不是被父进程拦截
FROM python:3.4RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgiRUN pip install Flask==0.10.1 uWSGIWORKDIR /appCOPY app /appCOPY cmd.sh /EXPOSE 9090 9191USER uwsgiCMD ["/cmd.sh"]
chmod -x cmd.shdocker build -t identidock .docker run -e "ENV=DEV" -p 5000:5000 identidock
一个简单的web应用
目录结构
$ tree creating-a-simple-web-app/creating-a-simple-web-app/├── identidock│ ├── app│ │ └── identidock.py│ ├── cmd.sh│ ├── docker-compose.yml│ ├── Dockerfile│ └── Dockerfile.bak├── LICENSE└── README.md
Dockerfile
FROM python:3.4RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgiRUN pip install Flask==0.10.1 uWSGI requests==2.5.1 redis==2.10.3WORKDIR /appCOPY app /appCOPY cmd.sh /EXPOSE 9090 9191USER uwsgiCMD ["/cmd.sh"]
docker-compose.yml
identidock:build: .ports:- "5000:5000"environment:ENV: DEVvolumes:- ./app:/applinks:- dnmonster- redisdnmonster:image: amouat/dnmonster:1.0redis:image: redis:3.0
identicon.py
from flask import Flask, Response, requestimport requestsimport hashlib #导入对用户输入进行散列处理的程序库import redisapp = Flask(__name__)cache = redis.StrictRedis(host='redis', port=6379, db=0) #建立redis缓存salt = "UNIQUE_SALT" #定义散列函数salt值default_name = 'Joe Bloggs'@app.route('/', methods=['GET', 'POST']) #Flask默认只相应get请求,必须给route声明加入methods参数,宣告route能处理post请求def mainpage():name = default_nameif request.method == 'POST':name = request.form['name']salted_name = salt + namename_hash = hashlib.sha256(salted_name.encode()).hexdigest() #执行SHA256算法,以获取散列值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: #如果返回None,继续调用identionprint ("Cache miss", flush=True) #输出调试信息,表示没有在缓存中找到图像r = requests.get('http://dnmonster:8080/monster/' + name + '?size=80')image = r.contentcache.set(name, image)# 把图像加入缓存,与名字关联return Response(image, mimetype='image/png')if __name__ == '__main__':app.run(debug=True, host='0.0.0.0')
