之前我们通过了Namespace,Cgroup,rootfs文件系统去学习的容器原理。
通过例子进行学习:
from flask import Flask
import socket
import os
app = Flask(__name__)
@app.route('/')
def hello():
html = "Hello {name}!
" \
"Hostname: {hostname}
"
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
先编写一个dockerfile用于制作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"]
CMD内容就是entrypoint的参数,且就是容器的启动进程。
Dckerfile并且是全是对容器内部操作,例如ADD,当前目录复制到容器目录之中。
关于写完的路径
1.制作docker镜像 -t 起名字:这个命令等于起一个容器执行Dockerfile文件。
docker build -t helloworld .
在Dockerfile中每个原句执行后,都会生成一个对应的镜像层,即使原语句本身并无明显修改文件操作,例如env语句,也会生成。在外界看来这个层为空的。
2.关于docker 命令的使用
查看构建结果
docker image is
下面是查看机器商的镜像。
启动镜像 :映射docke80端口到宿主机4000
docker run -p 4000:80 helloworld python app.py
$ curl http://localhost:4000
Hello World!
Hostname: 4ddf4638572d
查看当前运行的镜像
docker ps
docker commit指令:把正在运行的程序直接提交为一个镜像。就是把我们在运行的容器所做的操作也保存在镜像之中。
$ docker exec -it 4ddf4638572d /bin/sh
# 在容器内部新建了一个文件
root@4ddf4638572d:/app# touch test.txt
root@4ddf4638572d:/app# exit
# 将这个新建的文件提交到镜像中保存
$ docker commit 4ddf4638572d geektime/helloworld:v2
docker exec指令:进入容器之中:本身docker的Namespace的信息在宿主机是以文件的形式存在的。
获取docker的id,并获取容器pid
我们可以看到真实的Namespace文件。 如果我们可以加入到这个linux Namespace文件中是否可以进入该容器中呢?
该操作依赖于一个系统调用:通过open()打开文件把描述符fd交给setns()使用,执行后当前进程就加入了该Namespace。 相当于当前执行的进程与容器进程共享一个Namespace。
docker 提供了参数可以让你启动一个容器加入到另一个容器的Namespace中 -net
$ docker run -it --net container:4ddf4638572d busybox ifconfig
以上就是关于docker exec的原理。
而docker commit :就是把可读写层加上原来的容器镜像的只读层打包一个新的镜像。只读层在宿主机上是共享的不会占用额外的空间。由于使用的联合文件系统,对rootfs的任何修改,都会复制到可读写层上进行修改:Copy-on-Write
图中lnit层存在,可以防止在conmmit时候,把docker自己对/etc/hosts的修改也一起提交掉。
Volume(技术卷):
一个机器上完全隔离开的文件系统环境由1.rootfs机制+2.Mount Namespace构建的。
而容器内外的文件/目录挂载就需要Volume了。它允许宿主机上指定目录或者文件挂载到容器中进行修改与读取。
两种声明方式:
docker run -v /test ...
docker run -v /home:/test ...
第一种没有写明确的目录:会自动创建 /var/lib/docker/volumes/【volume_id】/_data
在容器内部修改后外部也会进行变化。
这里使用的挂载技术就是bind mount机制,可以将一个文件或者目录挂载到另一个目录。(不是整个设备)原挂载点将会被隐藏起来,而不受影响。
如图就是inode重定向。
注意:在commit的时候
docker commit是发生在宿主机空间的,由于Mount Namespce的隔离作用
,它并不知道这个绑定挂载的存在只能看到有个test的空目录。同理,我们在宿主机上去看这个docker的读写层可以看到这个test是空的。