docker容器实际案例
1.使用docker部署python写的web应用
from flask import Flask
import socket
import os
app = Flask(__name__)
@app.route(‘/‘)
def hello():
html = "<h3>Hello {name} </h3><br>Hostname:</b> {hostname}<br/>"
return html.format(name=os.getenv("NAME","world"),hostname=socket.gethostname())
if __name__ == "__main__":
app.run(host=‘0.0.0.0‘,port=80)
$ cat requirements.txt
Flask
2.制作容器镜像
使用Dockerfile制作docker镜像,也就是rootfs
# 使用官方提供的Python开发镜像作为基础镜像
FROM python:2.7-slim
# 将工作目录切换为/app
WORKDIR /app
# 将当前目录下的所有内容复制到/app下
ADD ./app
# 使用pip命令安装这个应用所需要的依赖
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 允许外接访问容器的80端口
EXPOSE 80
# 设置环境变量
ENV NAME world
# 设置容器进程为:python app.py 即:这个Python应用程序的启动命令
CMD ["python","app.py"]
3.Dockerfile设计思想
使用标准原语,(大写高亮的词语),描述我们要构建的Docker镜像。并且这些原语,都是按顺序处理的
FROM原语:指定"python:2.7-slim"这个官方维护的镜像,从而免去安装Python等语言环境的操作
RUN原语: 容器里执行shell命令的意思
WORKDIR:dockerfile后面的操作都以这一句指定的/app目录作为当前目录
CMD: dockerfile指定python app.py为这个容器的进程,这里app.py的实际路径是/app/app.py
所以CMD["python","app.py"]等价于 docker run python app.py
ENTRYPOINT:它和CMD都是docker容器里进程启动所必须的参数,完整执行格式"ENTRYPOINT CMD"
(不写,默认是/bin/sh -c,所以实际执行的是/bin/sh -c "python ap.py",cmd的内容就是ENTRYPOINT 参数)
4.dockerfile 存放位置
Dockerfile app.py requirements.txt
5.制作docker镜像
$ docker build -t helloworld .
-t:镜像加Tag,docker build 会自动加载当前目录下的Dockerfile文件,按顺序指定原语
过程:docker使用基础镜像启动了一个容器,在容器中依次执行Dockerfile中的原语
注意事项:
docker每个原语执行后,都会生成一个对应的镜像层,即使原语本身没有明显的修改文件操作(env),对应的层也会存在,只不过外界看这个层是空的
6.查看docker镜像
$ docker image ls
RESPOSITORY TAG IMAGE ID
helloworld latest 653287cdf998
7.启动容器
docker run -p 4000:80 helloworld
-p 4000:80告诉docker,把容器内的80端口映射在宿主机的4000端口上
镜像名helloworld,什么都没写,dockerfile中已经指定了CMDB。否组就需要些进程的启动命令
docker run -p 4000:80 helloworld python app.py
8.查看容器
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED
4ddf4638572d helloworld "python app.py" 10 seconds ago
发现容器启动了
验证一下
$ curl http://localhost:4000
<h3>Hello World!</h3><b>Hostname:</b> 4ddf4638572d<br/>
9.镜像上传DockerHub
注册Docker Hub账号,docker login登录
给镜像起一个完整的名字geektime/helloworld:v1
geektime是账户名
$ docker tag helloworld geektime/helloworld:v1
$ docker push geektime/helloworld:v1
10.将正在运行的容器,直接转为一个镜像
这个容器运行起来后,我又在里面做了一些操作,并且要把操作结果保存到镜像里
将容器4ddf4638572d 提交为镜像geektime/helloworld:v2
$ docker commit 4ddf4638572d geektime/helloworld:v2
11.docker exec 怎么进入容器里的呢?
进程的namespace信息在宿主机上是真实存在的,以文件方式存在
如下命令,可以看到docker容器在宿主机上的进程号是25686
$ docker inspect --format ‘{{ .State.Pid }}‘ 4ddf4638572d
25686
可以查看宿主机的proc文件,看到进程25686的所有namespace对应文件(ns是namespace的简写)
$ ls -l /proc/25686/ns
total 0
lrwxrwxrwx 1 root root 0 Aug 13 14:05 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid_for_children -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 uts -> uts:[4026532277]
每种namespace,在ns目录下都对应一个虚拟文件,并连接到真实的namespace文件上
这样就可以实现,将一个进程加入到一个已经存在的namespace中。(就实现了进入到进程所在容器的目的,这就是docker exec原理)