启动docker
DockerFile
# 使用官方提供的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"]
- FROM 原语,指定了“python:2.7-slim”这个官方维护的基础镜像
- 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",默认情况下,Docker 会为你提供一个隐含的 ENTRYPOINT,即:/bin/sh -c。所以,在不指定 ENTRYPOINT 时,比如在我们这个例子里,实际上运行在容器里的完整进程是:/bin/sh -c “python app.py”
- 需要注意的是,Dockerfile 中的每个原语执行后,都会生成一个对应的镜像层。即使原语本身并没有明显地修改文件的操作(比如,ENV 原语)
docker 命令
-
docker build -t helloworld .
,-t表示给镜像起名字,docker build 会自动加载当前目录下的 Dockerfile 文件,然后按照顺序,执行文件中的原语。 -
docker images
命令查看结果:
[root@lywane app]# docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
<none> <none> 6640ccb33e3b 36 minutes ago 158.3 MB
helloworld latest 8891ec087562 About an hour ago 158 MB
busybox latest 5ab5e1c8a2f0 6 months ago 1.24 MB
python 2.7-slim 7d87e03a3906 2 years ago 147.8 MB`
-
docker run -p 4000:80 helloworld
,容器80端口映射到4000端口,启动docker(执行DockerFile中的CMD)。 -
docker ps
命令查看运行中的容器:
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED
4ddf4638572d helloworld "python app.py" 10 seconds ago
-
docker tag
给镜像起名字 -
docker push
将镜像上传到Docker Hub -
docker commit
将变更提交到镜像中保存
docker exec
-
docker exec -it 4ddf4638572d /bin/sh
可以进入容器,是怎么做到的呢? - 查看容器pid
$ docker inspect --format '{{ .State.Pid }}' 4ddf4638572d
25686
- 查看该进程所有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]
- 一个进程的每种 Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上
- 有了这样一个可以“hold 住”所有 Linux Namespace 的文件,我们就可以对 Namespace 做一些很有意义事情了,比如:加入到一个已经存在的 Namespace 当中。
- 这也就意味着:一个进程,可以选择加入到某个进程已有的 Namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理
- 这而这个操作所依赖的,乃是一个名叫
setns()
的 Linux 系统调用,sentns接收两个参数,第一个参数是当前进程要加入的Namespace路径,比如 /proc/25686/ns/net;而第二个参数,则是你要在这个 Namespace 里运行的进程,比如 /bin/bash。 -
docker run -it --net container:4ddf4638572d busybox ifconfig
docker专门提供了-net选项,表示启动一个容器并加入到另一个容器的Network Namespace。 - 指定–net=host,就意味着这个容器不会为进程启用 Network Namespace。这就意味着,这个容器拆除了 Network Namespace 的“隔离墙”,所以,它会和宿主机上的其他普通进程一样,直接共享宿主机的网络栈。这就为容器直接操作和使用宿主机网络提供了一个渠道
- docker commit,实际上就是在容器运行起来后,把最上层的“可读写层”,加上原先容器镜像的只读层,打包组成了一个新的镜像。
- 当然,下面这些只读层在宿主机上是共享的,不会占用额外的空间。而由于使用了联合文件系统,你在容器里对镜像 rootfs 所做的任何修改,都会被操作系统先复制到这个可读写层,然后再修改。
- 这就是所谓的:Copy-on-Write。,Init 层的存在,就是为了避免你执行 docker commit 时,把 Docker 自己对 /etc/hosts 等文件做的修改,也一起提交掉