目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

容器与镜像

Docke Architecture

Docker_Architecture

Docker 客户端
 Docker Client 的主要目的是提供一种方法来指导从注册中心里提取镜像并使其在 Docker 主机上运行。

DockerHost
 Docker 主机提供了一个完整的环境来执行和运行应用程序。它包括 Docker 守护程序,镜像,容器,网络和存储。如前所述,守护进程负责所有与容器相关的操作,并通过 CLI 或 REST API 接收命令。它还可以与其他守护进程通信,以管理其服务。Docker 守护程序根据客户端的请求提取和构建容器镜像。一旦它提取了请求的镜像,它就会利用一组称为构建文件的指令为容器构建一个工作模型。构建文件还可以包括守护程序在运行容器之前预加载其他组件的指令,或者在构建容器之后将指令发送到本地命令行的指令。

Docker 对象
 各种对象用于组装应用,docker 的主要对象如下:

  • Images:用于构建容器的只读二进制模版,可以通过私有或公共注册中心获取。
  • Containers:容器是运行应用程序的封装环境。容器由映像和启动容器时提供的任何其他配置选项定义,包括但不限于网络连接和存储选项。容器只能访问映像中定义的资源,除非在将映像构建到容器中时定义了其他访问权限。
  • Networking:Docker 以应用程序驱动的方式实现网络,并提供各种选项,同时为应用程序开发人员保留足够的抽象。
  • Storage:用于持久化应用数据。

Linux 底层技术的支持

  • Namespaces:让多个容器之间相互隔离,每个容器有独立的 pid、net、ipc、mnt、uts。
  • Control groups:对容器做资源限制,为不同的容器分配不同的硬件资源。
  • Union file systems:Container 和 image 的分层。

Image 简介

Images
分层概念

  • 文件和 meta data 的集合(root filesystem)。
  • 分层的,并且每一层都可以添加改变删除文件,成为一个新的 image。
  • 不同的 image 可以共享相同的 layer。
  • Image 本身是 read-only 的。

查看镜像分层、元数据

1[root@localhost ~]# docker image inspect redis:latest
2            "Layers": [
3                "sha256:cf5b3c6798f77b1f78bf4e297b27cfa5b6caa982f04caeb5de7d13c255fd7a1e",
4                "sha256:1f34edf2a6aacf60086a6caecb2ceacfb6e1f7b24d659ed91e0bbdca47e13686",
5                "sha256:3ba3251341dc82ef8984b687dca38b85d41210a18ad8afaf637d7f9c22199780",
6                "sha256:633cc0abe0bf7866b76409e5b6299da578b406239405e3c2f8d48f02fb410925",
7                "sha256:5f58a275fe5e91edaa7bb070b4c582dd2c2acb200ca9cea00beedb620cdbe2d1",
8                "sha256:f37c36c3db455fbd01876554875293088798eb31f159d862a8f9593c32ffe15d"
9            ]

根据摘要拉取镜像(防止不同镜像使用同一 tag)

1# 查看指定镜像的摘要信息
2[root@localhost ~]# docker image ls --digests alpine
3REPOSITORY          TAG                 DIGEST                                                                    IMAGE ID            CREATED             SIZE
4alpine              latest              sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb   961769676411        4 days ago          5.58MB
5
6# 根据摘要信息拉取镜像(镜像内容的变更一定会导致摘要散列值的变化)
7[root@localhost ~]# docker image pull alpine@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb

获取 Image 的方式

  • 通过镜像仓库服务获取。
  • 通过 Dockerfile
    构建。

镜像仓库服务

镜像仓库服务

 拉取的镜像位于 Docker 主机本地仓库中,Docker 镜像存储在镜像仓库服务(Image Registry)当中。Docker 客户端镜像仓库服务是可配置的,默认是 Docker Hub。
 镜像仓库服务包含多个镜像仓库,一个镜像仓库中又可以包含多个镜像。

1# 拉取的镜像位于Docker主机本地仓库中,通过以下命令可以查看本地仓库中的所有镜像。
2docker image ls
3docker images

镜像仓库的分类

  • Offiical Repository
  • Unofficial Repository

如何定位镜像
 通过名字加标签的组合定位一个镜像。

 1# 拉取ubuntu 版本号为 14.04
 2docker image pull ubuntu:14.04
 3
 4# 如果不指定标签,则默认拉取latest最新的镜像。
 5docker image pull alpine
 6
 7# 从非官方仓库拉取镜像:从tu-demo仓库中拉取v2镜像,其中镜像的拥有者是Docker Hub账户nigelpoulton(一个不应该被信任的账户)
 8docker image pull nigelpoulton/tu-demo:v2 
 9docker image pull microsoft/dotnet:latest
10
11# 从第三方镜像仓库服务获取镜像(非Docker Hub),则需要在镜像仓库名称前面加上第三方镜像仓库服务的DNS名称。假设镜像位于Google容器镜像仓库服务(GCR)中,则需要在仓库名称前面加上gcr.io,可能需要在拉取镜像前完成登录。
12docker image pull gcr.io/nigelpoulton/tu-demo:v2 

为镜像打多个标签
 一个镜像可以根据用户需要设置多个标签,标签仅仅是存放在镜像元数据中的任意数字或字符串。

1# 拉取仓库中的全部镜像,忽略tag
2docker image pull -a nigelpoulton/tu-demo

过滤 docker image ls 的输出内容
 Docker 提供 --filter 参数来过滤 docker image ls 命令返回的镜像列表内容。

 1# 返回没有标签的镜像
 2[root@localhost ~]# docker image ls --filter dangling=true
 3REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
 4<none>              <none>              4dbbb3b1e7a9        6 weeks ago         914MB
 5
 6# 返回带标签的镜像
 7[root@localhost ~]# docker image ls --filter dangling=false
 8REPOSITORY                                                 TAG                 IMAGE ID            CREATED             SIZE
 9alpine                                                     latest              961769676411        4 days ago          5.58MB
10
11# 过滤并显示tag=latest的镜像
12[root@localhost ~]# docker image ls --filter=reference="*:latest"
13REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
14alpine              latest              961769676411        4 days ago          5.58MB
15lb-scale_web        latest              6a539fb0d274        6 weeks ago         918MB
16
17# 格式化显示
18[root@localhost ~]# docker image ls --format "{{.Repository}}: {{.Tag}}: {{.Size}}"
19alpine: latest: 5.58MB

通过 CLI 方式搜索 Docker Hub
 docker search 命令允许通过该 CLI 的方式搜索 Docker Hub。

 1# 查找所有NAME为nigelpoulton的仓库,NAME代表仓库名称,包含了Docker ID,或非官方仓库的组织名称。
 2[root@localhost ~]# docker search nigelpoulton
 3NAME                                 DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
 4nigelpoulton/pluralsight-docker-ci   Simple web app used in my Pluralsight videos…   17                                      [OK]
 5nigelpoulton/tu-demo                                                                 11  
 6
 7# 只显示官方镜像(默认显示的官方和非官方的)
 8[root@localhost ~]# docker search alpine --filter "is-official=true"
 9NAME                DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
10alpine              A minimal Docker image based on Alpine Linux…   5585                [OK]    
11
12# Docker 默认只返回25行结果,使用--limit参数可以增加返回内容行数,最多为100行。
13[root@localhost ~]# docker search microsoft --limit "100" | wc -l
14101

删除镜像

1# 删除指定镜像
2[root@localhost ~]# docker image rm IMAGE ID
3
4# 删除所有镜像
5[root@localhost ~]# docker image rm ${docker image ls -q
6} -f
7

通过 docker file 构建 BaseImage

自定义 dockfile 创建本地 image

 1[root@localhost hello-world]# yum install gcc glibc-static
 2[root@localhost hello-world]# mkdir hello-world
 3[root@localhost hello-world]# cd hello-world/
 4[root@localhost hello-world]# touch hello.c
 5[root@localhost hello-world]# vim hello.c
 6#include<stdio.h>
 7int main()
 8{
 9        printf("hello docker\n");
10}
11[root@localhost hello-world]# gcc -static hello.c -o hello
12[root@localhost hello-world]# ./hello
13[root@localhost hello-world]# vim Dockerfile
14FROM scratch
15ADD hello /
16CMD ["/hello"]
17[root@localhost hello-world]# docker build -t soulboy/hello-wrld .
18Sending build context to Docker daemon  860.7kB
19Step 1/3 : FROM scratch
20 ---> 
21Step 2/3 : ADD hello /
22 ---> d476563481ef
23Step 3/3 : CMD ["/hello"]
24 ---> Running in ce031e741831
25Removing intermediate container ce031e741831
26 ---> 6a68cb96d615
27Successfully built 6a68cb96d615
28Successfully tagged soulboy/hello-wrld:latest
29[root@localhost hello-world]# docker image ls
30[root@localhost hello-world]# docker history 6a68cb96d615
31
32IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
33
346a68cb96d615        3 minutes ago       /bin/sh -c #(nop)  CMD ["/hello"]               0B                  
35d476563481ef        3 minutes ago       /bin/sh -c #(nop) ADD file:feb1dec246efa459c…   857kB
36[root@localhost hello-world]# docker run soulboy/hello-wrld
37hello docker

Container 简介

Container

  • 基于 Image 创建(copy)
  • 在 Image layer 之上建立一个 container layer(可读写)
  • 类比面向对象:类(Image)和实例(Container)
  • Image 负责 app 的存储和分发,Container 负责运行 app。

容器管理命令

 1#查看所有容器
 2[root@localhost hello-world]# docker container ls -a
 3[root@localhost hello-world]# docker ps -a
 4#非交互式运行容器方法:运行完CMD就会退出。
 5[root@localhost hello-world]# docker run centos
 6#交互式运行容器方法
 7[root@localhost hello-world]# docker run -it centos
 8#删除指定容器:id可以是开头可以唯一区分的位数[root@localhost hello-world]# docker container rm CONTAINER_ID
 9[root@localhost hello-world]# docker rm CONTAINER_ID
10#批量删除容器
11[root@localhost hello-world]# docker rm $(docker container ls -aq)
12#批量删除已经退出状态的容器
13[root@localhost hello-world]# docker rm $(docker container ls -f "status=exited" -q)

镜像常用命令

1[root@localhost hello-world]# docker images
2[root@localhost hello-world]# docker image ls
3[root@localhost hello-world]# docker image rm IMAGE_ID
4[root@localhost hello-world]# docker rmi  IMAGE_ID

构建 BaseImage 两种方式

  • 提倡通过 Dockerfile 的方式创建 Image,因为 Dockerfile 可以查看构建流程,更加安全,基于容器创建 Image 并不推荐,因为用户使用 Image 不知道 Image 中到底加入了那哪东西,不安全。
  • Dockerfile 便于分享,只需分享 Dockerfile 对方就可以根据 Dockerfile 在本地自行构建。

 在 CentOS 容器中安装 VIM,然后将此容器变为新的 Image。

 1#进入容器
 2[root@localhost ~]# docker exec -it cocky_merkle /bin/bash
 3#安装Vim
 4[root@060ef71c6a99 /]# yum install vim -y
 5#退出容器
 6[root@060ef71c6a99 /]# exit  
 7#基于容器创建新的BaseImage
 8[root@localhost ~]# docker commit cocky_merkle soulboy/centos-vim
 9sha256:9601a7182c7b2b497869e3ce32d5dd800c069d34ae5db4e98a8d75ccf87ed132
10[root@localhost ~]# docker images | grep soulboy
11soulboy/centos-vim                latest              9601a7182c7b        28 seconds ago      362MB
12
13#对比原始Centos镜像和新的Centos镜像区别
14[root@localhost ~]# docker history centos
15IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
169f38484d220f        3 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
17<missing>           3 months ago        /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B                  
18<missing>           3 months ago        /bin/sh -c #(nop) ADD file:074f2c974463ab38c…   202MB               
19
20[root@localhost ~]# docker history soulboy/centos-vim
21IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
229601a7182c7b        4 minutes ago       /bin/bash                                       160MB               
239f38484d220f        3 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
24<missing>           3 months ago        /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B                  
25<missing>           3 months ago        /bin/sh -c #(nop) ADD file:074f2c974463ab38c…   202MB   
26
27#提倡通过Dockerfile的方式创建Image,因为Dockerfile可以查看构建流程,更加安全,基于容器创建Image并不推荐,因为用户使用Image不知道Image中到底加入了那哪东西,不安全。
28[root@localhost test]# mkdir docker-centos-vim
29[root@localhost test]# cd docker-centos-vim/
30[root@localhost docker-centos-vim]# vim Dockerfile
31FROM centos
32RUN yum install -y vim
33[root@localhost docker-centos-vim]# docker build -t soulboy/centos-vim .
34Sending build context to Docker daemon  2.048kB
35Step 1/2 : FROM centos
36 ---> 9f38484d220f
37Step 2/2 : RUN yum install -y vim
38 ---> Running in 1db6f9fb291a
39Loaded plugins: fastestmirror, ovl
40Determining fastest mirrors
41 * base: mirrors.nju.edu.cn
42 * extras: mirrors.nju.edu.cn
43 * updates: mirrors.cn99.com
44Resolving Dependencies
45--> Running transaction check
46---> Package vim-enhanced.x86_64 2:7.4.160-6.el7_6 will be installed
47--> Processing Dependency: vim-common = 2:7.4.160-6.el7_6 for package: 2:vim-enhanced-7.4.160-6.el7_6.x86_64
48Removing intermediate container 1db6f9fb291a
49 ---> 2de975c65628
50Successfully built 2de975c65628
51Successfully tagged soulboy/centos-vim:latest
52
53[root@localhost docker-centos-vim]# docker images
54REPOSITORY                        TAG                 IMAGE ID            CREATED              SIZE
55soulboy/centos-vim                latest              2de975c65628        About a minute ago   362MB
56
57#基于新Image创建并运行容器
58[root@localhost docker-centos-vim]# docker run -it soulboy/centos-vim
59[root@060ef71c6a99 /]# exit
60[root@localhost docker-centos-vim]# docker exec -it cocky_merkle /bin/bash
61[root@060ef71c6a99 /]# vim --help

Dockerfile 简介

 Docker 可以通过阅读来 Dockerfile 的指令以自动构建 Image。Dockerfile 是一个文本文档,其中包含用户可以在命令行上调用的所有命令以组合 Image。基于 docker build 命令可以在 Dockerfile 中定义一个连续执行多条命令行指令的自动构建镜像的过程。

传送门

Dockerfile 语法解析

 1# 制作Base Image(不依赖于任何Base Image)
 2FROM scratch
 3
 4# 使用Base Image(尽量使用官方的Image作为Base Image,为了安全考虑。)
 5FROM centos
 6
 7# 注释
 8LABEL maintainer="xxx@gmail.com"
 9LABEL version="1.0"
10LABEL description="This is description"
11
12# 运行命令(每运行一次RUN都会生成新的一层,因此复杂的RUN应该使用反斜线换行,以避免无用的分享,合并多条命令成一行!)
13RUN yum update && yum install -y vim\ 
14python-dev
15
16# 设定当前工作目录 类似于cd(用WORKDIR,不要用RUN cd,尽量使用绝对目录!)
17WORKDIR /test    #如果没有test目录会自动创建test目录
18WORKDIR demo
19RUN pwd   #输出结果为: /test/demo
20
21# ADD 添加文件到到Image的指定目录
22ADD hello /	#拷贝hello文件到Image的根目录中
23ADD test.tar.gz /  #添加到根目录并解压
24
25# COPY 复制文件到指定目录(多数情况下,COPY优于ADD! ADD除了COPY还有额外功能解压,添加远程文件/目录可以使用curl或者wget!)
26WORKDIR /root
27COPY hello test/     #/root/test/hello
28
29# ENV 宏定义,方便引用
30ENV MYSQL_VERSION 5.6 #设置常量
31RUN yum install -y mysql-server="${MYSQL_VERSION}"\
32 && echo 123 #引用常量

RUN、CMD、ENTRYPOINT 对比

  • RUN:执行命令并创建新的 Image Layer。容器启动时默认执行的命令,如果 docker run 指定了其他命令(Docker run -it [image] /bin/bash),CMD 命令会被忽略。如果定义了多个 CMD,只有最后一个会执行。

  • CMD: 设置容器启动后默认执行的命令和参数.

  • ENTRYPOINT:设置容器启动时运行的命令.让容器以应用程序或者服务的形式运行。不会被忽略,一定会执行。

发布镜像

1# 本地登录dockerhub用户
2[root@localhost ~]# docker login
3Login Succeeded
4
5# pull的格式: dockerhub_count/Image_name
6[root@localhost hello-world]# docker push rtsfan1024/hello-world:latest

Dockerfile 构建 Image(Flask 的 APP)

 部署一个基于 flask 的 python 镜像

 1# 安装pip
 2[root@localhost test]# yum install python-pip
 3[root@localhost test]# 
 4python -m pip install –upgrade pip
 5
 6#安装flask
 7[root@localhost Python-3.7.3]# pip install --trusted-host pypi.python.org --trusted-host py --trusted-host files.pythonhosted.org flask
 8
 9# 运行app.py 并访问 http://192.168.31.210:5000/
10[root@localhost test]# python app.py 
11 * Serving Flask app "app" (lazy loading)
12 * Environment: production
13   WARNING: This is a development server. Do not use it in a production deployment.
14   Use a production WSGI server instead.
15 * Debug mode: off
16 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
17192.168.31.230 - - [23/Jun/2019 22:24:16] "GET / HTTP/1.1" 200 -
18192.168.31.230 - - [23/Jun/2019 22:24:20] "GET /favicon.ico HTTP/1.1" 404 -
19
20#  创建目录编写Dockerfile
21[root@localhost test]# cd /test
22[root@localhost test]# mkdir flask-hello-world
23
24# 编写Dockerfile文件
25[root@localhost test]# cat /test/flask-hello-world/Dockerfile 
26FROM python:2.7
27LABEL maintainer="Soul Boy<rtsfan1024@gmail.com>"
28RUN pip install flask -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
29COPY app.py /app/
30WORKDIR /app
31EXPOSE 5000
32CMD ["python", "app.py"]
33
34# 基于Flask框架的python应用代码
35[root@localhost test]# cat /test/flask-hello-world/app.py 
36from flask import Flask
37app = Flask(__name__)
38@app.route('/')
39def hello():
40    return "hello docker"
41if __name__ == '__main__':
42    app.run(host="0.0.0.0", port=5000)
43
44# 基于Dockerfile构建Image
45[root@localhost flask-hello-world]# cd /test/flask-hello-world/
46[root@localhost flask-hello-world]# docker build -t rtsfan1024/flask-hello-world .
47[root@localhost flask-hello-world]# docker run -d -p 5000:5000 rtsfan1024/flask-hello-world:latest
48
49# 访问http://192.168.31.210:5000/
50`hello docker`

容器操作

 1# 进入已存在的容器
 2docker exec -it container_id /bin/bash 
 3
 4# 停止容器
 5docker stop container_id
 6
 7# 删除容器(退出状态的容器)
 8docker rm container_id
 9
10# 指定创建容器时指定名称(名字也具有唯一性)
11docker run -d --name=demo Image
12
13# 分析容器
14docker inspect redis
15
16# 容器运行日志
17docker logs container_id

通过 Dockerfile 构建工具

 1[root@localhost flask-hello-world]# mkdir -pv /test/stress
 2[root@localhost flask-hello-world]# cat /test/stress/Dockerfile
 3FROM centos
 4RUN yum install -y epel-release && yum install -y stress
 5ENTRYPOINT ["/usr/bin/stress"]
 6CMD []
 7[root@localhost flask-hello-world]# cd /test/stress/
 8[root@localhost stress]# docker build -t rtsfan1024/stress .
 9
10# ENTRYPOINT + CMD : 命令 + 接收参数
11[root@localhost stress]# docker run -it rtsfan1024/stress:latest --vm 1 --verbose
12stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
13stress: dbug: [1] using backoff sleep of 3000us
14stress: dbug: [1] --> hogvm worker 1 [6] forked
15stress: dbug: [6] allocating 268435456 bytes ...
16stress: dbug: [6] touching bytes in strides of 4096 bytes ...
17stress: dbug: [6] freed 268435456 bytes

容器的资源限制

容器的内存限定

1#swap默认不设置与memory一致,也就是 200M + 200M = 400M,而stress每次分配和回收的内存为256M。 如果添加选项 --vm-bytes 500M ,则会报错,因为宿主机分配给容器的内容总量为400M。
2[root@localhost ~]# docker run --memory=200M rtsfan1024/stress:latest --vm 1 --verbose 
3stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
4stress: dbug: [1] using backoff sleep of 3000us
5stress: dbug: [1] --> hogvm worker 1 [6] forked
6stress: dbug: [6] allocating 268435456 bytes ...
7stress: dbug: [6] touching bytes in strides of 4096 bytes ...

容器的 CPU 限定(relative weight 相对权重)

1# 第一个容器
2[root@localhost ~]# docker run --cpu-shares=10 --name=test1 rtsfan1024/stress:latest --cpu 1
3# 第二个容器
4[root@localhost ~]# docker run --cpu-shares=5 --name=test2 rtsfan1024/stress:latest --cpu 1

使用 top 命令查看 CPU 占占用率(test1 的占用率是 test2 的两倍)
资源占用


作者:Soulboy