实验目标: 用 Docker 部署一个Python 编写的 Web 应用。
实验步骤: 基础镜像(python)–>flask–>部署python应用–>前端(html css js)–>flask–>服务(数据库)
web框架 flask django
应用代码部分
代码功能:
如果当前环境中有"NAME"这个环境变量,就把它打印在"Hello"后,否则就打印"Hello world",最后再打印出当前环境的 hostname。
从系统中获取主机变量获取主机名称等信息。
本实验是后端往前端传内容。
#ipython
#ipython os
#mkdir python_app
#cd python_app
#vim app.py:
from flask import Flask #from模块调用
import socket #为了获取主机名称用
import os #获取环境变量用的
app = Flask(__name__) #
@app.route('/') #装饰器
def hello():
html = "<h3>Hello {name}!</h3>" \ #html字符串定义了网页的源代码,里面由有变量name等进行传值
"<b>Hostname:</b> {hostname}<br/>"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname()) #这里return给出返回值,一旦NAME没有值就会把world值返回
if __name__ == "__main__": #主函数
app.run(host='0.0.0.0', port=80) #监控本地所有的网卡、监控80端口
应用依赖:
定义在同目录下的 requirements.txt 文件里,内容如下:
#vim requirements.txt
Flask
Dockerfile制作容器镜像:
#vim Dockerfile
FROM python:2.7-slim #基础镜像
WORKDIR /app #进入目录后切换到/app目录下
ADD . /app #把当前目录下的所有文件都拷到/app目录下去
RUN pip install --trusted-host pypi.python.org -r requirements.txt #pip -r 相当于python的yum,pip可以安装任何python官方的模块,指定了网站。这条命令就是为了装一个flask的软件
EXPOSE 80 #暴露一个80端口
ENV NAME World #设置一个环境变量叫world
CMD ["python", "app.py"] #用cmd去执行一条命令,【python app.py】
Dockerfile文件说明:
FROM python:2.7-slim
# 使用官方提供的 Python 开发镜像作为基础镜像
# 指定"python:2.7-slim"这个官方维护的基础镜像,从而免去安装 Python 等语言环境的操作。否则,这一段就得这么写了:
##FROM ubuntu:latest
##RUN apt-get update -yRUN apt-get install -y python-pip python-dev build-essential
WORKDIR /app
# 将工作目录切换为 /app
# 意思是在这一句之后,Dockerfile 后面的操作都以这一句指定的 /app 目录作为当前目录。
ADD . /app
# 将当前目录下的所有内容复制到 /app 下
# Dockerfile 里的原语并不都是指对容器内部的操作。比如 ADD,指的是把当前目录(即 Dockerfile 所在的目录)里的文件,复制到指定容器内的目录当中。
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 使用 pip 命令安装这个应用所需要的依赖
EXPOSE 80
# 允许外界访问容器的 80 端口
ENV NAME World
# 设置环境变量
CMD ["python", "app.py"]
# 设置容器进程为:python app.py,即:这个 Python 应用的启动命令
# 这里app.py 的实际路径是 /app/app.py。CMD ["python", "app.py"] 等价于 "docker run python app.py"。
# 在使用 Dockerfile 时,可能还会看到一个叫作 ENTRYPOINT 的原语。它和 CMD 都是 Docker 容器进程启动所必需的参数,完整执行格式是:"ENTRYPOINT CMD"。
# 但是,默认,Docker 会提供一个隐含的 ENTRYPOINT,即:/bin/sh -c。所以,在不指定 ENTRYPOINT 时,比如在这个例子里,实际上运行在容器里的完整进程是:/bin/sh -c "python app.py",即 CMD 的内容就是 ENTRYPOINT 的参数。
# 基于以上原因,后面会统一称 Docker 容器的启动进程为 ENTRYPOINT,而不是 CMD。
现在目录结构:
# ls
Dockerfile app.py requirements.txt
构建镜像:
# docker build -t helloworld .
-t 给这个镜像加一个 Tag
Dockerfile 中的每个原语执行后,都会生成一个对应的镜像层。即使原语本身并没有明显地修改文件的操作(比如,ENV 原语),它对应的层也会存在。只不过在外界看来,这个层是空的。
查看结果:
# docker image ls
REPOSITORY TAG IMAGE ID
helloworld latest 653287cdf998
启动容器:
# docker run -p 4000:80 helloworld
镜像名 helloworld 后面,什么都不用写,因为在 Dockerfile 中已经指定了 CMD。否则,就得把进程的启动命令加在后面:
# docker run -p 4000:80 helloworld python app.py
查看容器:
# docker ps
CONTAINER ID IMAGE COMMAND CREATED
4ddf4638572d helloworld "python app.py" 10 seconds ago
进入容器:
# docker exec -it b69 /bin/bash
访问容器内应用:
# curl http://localhost:4000
<h3>Hello World!</h3><b>Hostname:</b> 4ddf4638572d<br/>
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
至此,已经使用容器完成了一个应用的开发与测试,如果现在想要把这个容器的镜像上传到 DockerHub 上分享给更多的人,首先要注册一个 Docker Hub 账号,然后使用 docker login 命令登录。
给容器镜像打tag起一个完整的名字:
# docker tag helloworld yanqiang20072008/helloworld:v1
yanqiang20072008为我在docker hub的用户名
推送镜像到docker hub:
# docker push yanqiang20072008/helloworld:v1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~