容器基础4:重识docker容器

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 容器基础4:重识docker容器 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原理)

容器基础4:重识docker容器

上一篇:mysql 8 设置允许远程连接 You are not allowed to create a user with GRANT


下一篇:Oracle第五周测验