【Docker】浅析架构和底层技术

966 阅读4分钟

1 概览

1.1 架构

Docker提供了一个开发、打包、运行的APP平台,把APP和底层的基础架构隔离开。

Docker Engine是一个后台进程,提供了Rest API的Server和CLI接口。

1.2 底层技术支持

Namespaces:隔离用户空间、网络空间、进程(pid、net、ipc、mnt、uts)

Control groups:做资源限制

Union file systems:Container和Image的分层

2 Image

2.1 Image的概念

Image是文件和meta data的集合,Image是分层的,每一层都可以添加、修改、删除文件,成为一个新的image,不同的image可以共享相同的layer,Image本身是只读的。

2.2 获取Image

可以在Docker Hub获取官方提供的Image

docker pull + image name

例如:docker pull hello-world

[root@localhost ~]# docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete 
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest
[root@localhost ~]# docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
hello-world   latest    d1165f221234   3 weeks ago   13.3kB

2.3 自定义Base Image

编译得到可执行C程序gcc -static hello.c -o hello

创建Dockerfile

FROM scratch
ADD hello /
CMD ["/hello"]

执行命令docker build -t xd1998/hello-docker .

("."表示当前目录找docker文件)

执行Dockerfile中指定的三个步骤

Sending build context to Docker daemon  368.8MB
Step 1/3 : FROM scratch
 ---> 
Step 2/3 : ADD hello /
 ---> d9b08486b846
Step 3/3 : CMD ["/hello"]
 ---> Running in 74e47a7e6df1
Removing intermediate container 74e47a7e6df1
 ---> 6f933eff0af6
Successfully built 6f933eff0af6
Successfully tagged xd1998/hello-docker:latest

查看镜像docker image ls

[root@localhost ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE 
xd1998/hello-docker latest 6f933eff0af6 2 minutes ago 865kB 
hello-world latest d1165f221234 3 weeks ago 13.3kB 

查看Image历史:docker history 6f933eff0af6

[root@localhost ~]# docker history 6f933eff0af6
IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
6f933eff0af6   5 minutes ago   /bin/sh -c #(nop)  CMD ["/hello"]               0B        
d9b08486b846   5 minutes ago   /bin/sh -c #(nop) ADD file:c6c2ab724dad6ae96…   865kB     

运行镜像docker run xd1998/hello-docker

得到打印的结果,说明创建的Base Image可以作为一个Container去执行

[root@localhost ~]# docker run xd1998/hello-docker
hello docker!

3 Container

3.1 Container的概念

Container是通过Image创建的,是在Image layer之上简历一个可读写的Container layer。Container和Image的关系类似于面向对象中实例和类的关系。Image负责应用的存储和分发,Container负责运行应用。

3.2 相关操作

查看正在运行的容器:docker container ls

查看所有容器:docker container ls -a

[root@localhost ~]# docker container ls -a
CONTAINER ID   IMAGE                 COMMAND    CREATED             STATUS                         PORTS     NAMES
6e2d332e348f   xd1998/hello-docke    "/hello"   17 minutes ago      Exited (14) 17 minutes ago               bold_albattani
80850a9eb600   hello-world           "/hello"   About an hour ago   Exited (0) About an hour ago             wonderful_pasteur

在运行一个容器的时候,默认会执行CMD中定义的命令,因为自定义的CMD中不是一个常驻内存的命令,所以执行完命令后容器的状态就变为了Exited

交互式运行容器:docker run -it imageName

删除容器:docker container rm containerID

删除镜像:docker image rm imageID

打印输出所有容器的ID:docker container ls -aq

清理所有容器:docker rm $(docker container ls -aq)

清除状态为exited的容器:docker container ls -f "status=exited" -q

4 Dockerfile语法与最佳实践

4.1 FROM

最佳实践:使用官方的image作为base image

FROM scratch # 制作base image
FROM centos # 使用base image
FROM ubuntu:14.04

4.2 LABEL

最佳实践:应该写明metadata

LABEL maintainer="xxx@163.com"
LABEL version="1.0"
LABEL description="this is description"

4.3 RUN

最佳实践:每执行依次RUN得到新的一层,所以最好合并多条命令为一行,为了命令的易读性,可以使用反斜线换行。

RUN yum update && yum install -y vim \
    python-dev

4.4 WORKDIR

最佳实践:WORKDIR可以设定当前工作目录,使用WORKDIR,不要用RUN cd,尽量使用绝对目录。

WORKDIR /test # 创建test目录
WORKDIR demo 
RUN pwd # 输出/test/demo

4.5 ADD/COPY

最佳实践:COPY优先于ADD,ADD除了可以添加文件到指定目录,还可以解压缩。添加远程文件/目录使用curl或者wget

ADD hello /
ADD test.tar.gz / # 解压缩到根目录
WORKDIR /root
ADD hello test/ # 将hello添加到/root/test
WORKDIR /root
COPY hello test/ # 将hello添加到/root/test

4.6 ENV

最佳实践:最好使用ENV定义常量

ENV MYSQL_VERSION 5.6 # 设置常量
RUN apt-get install -y mysql-server="${MYSQL_VERSION}" \
    && rm -rf /var/lib/apt/lists/* # 可以用${MYSQL_VERSION}使用常量4,

4.7 VOLUME / EXPOSE

VOLUME仅支持指定容器的挂载点目录,不支持宿主机的挂载点目录。如果挂载点目录路径下之前后文件存在,docker run命令会在卷挂在完成之后将所有文件实现复制共享。

EXPOSE可以指定镜像暴露的端口,在启动后去动态绑定至宿主机的随机端口和所有地址。

4.8 CMD / ENTRYPOINT

RUN是执行命令并创建一个新的Image Layer,CMD是设置容器启动后默认执行的命令和参数,ENTRYPOINT是设置容器启动时运行的命令。

Shell格式和Exec格式的区别:shell可以识别ENV变量,exec不能识别ENV变量,需要指明通过shell执行:ENTRYPOINT ["/bin/bash", "-c", "echo hello $name"]

# shell格式
RUN apt-get install -y vim 
CMD echo "hello docker"
ENRTYPOINT echo "hello docker"
# exec格式
RUN ["apt-get", "install", "-y", "vim"]
CMD ["/bin/echo", "hello docker"]
ENTRYPOINT ["/bin/echo", "hello docker"]

CMD是容器启动时默认执行的命令,如果docker run指定了其他命令,CMD命令会被忽略,如果定义了多个CMD,只有最后一个CMD会被执行。

ENTRYPOINT让容器以应用程序或服务的形式运行,不会被忽略,一定被执行

最佳实践:写一个shell脚本作为ENTRYPOINT

COPY docker-entrypoint.sh /usr/local/bin/
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 27017
CMD ["mongod"]

4.9 发布自己的镜像

登录:docker login

推送:docker push tag:latest

5 Dockfile实践

创建/hello-docker/app.py:

from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
    return "hello docker"
if __name__ == '__main__':
    app.run()

创建/hello-docker/Dockerfile:

FROM python:2.7
LABEL "maintainer=XD1998<xxx@163.com>"
RUN pip install flask
COPY app.py /app
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]

执行build命令:docker build -t xd1998/hello-docker .

参考资料

  1. 官方提供的Image对应的Dockerfile
  2. Dockerfile语法官方文档