目录

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的。

查看镜像分层、元数据

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

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

# 查看指定镜像的摘要信息
[root@localhost ~]# docker image ls --digests alpine
REPOSITORY          TAG                 DIGEST                                                                    IMAGE ID            CREATED             SIZE
alpine              latest              sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb   961769676411        4 days ago          5.58MB

# 根据摘要信息拉取镜像(镜像内容的变更一定会导致摘要散列值的变化)
[root@localhost ~]# docker image pull alpine@sha256:72c42ed48c3a2db31b7dafe17d275b634664a708d901ec9fd57b1529280f01fb

获取Image的方式

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

镜像仓库服务

镜像仓库服务

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

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

镜像仓库的分类

  • Offiical Repository
  • Unofficial Repository

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

# 拉取ubuntu 版本号为 14.04
docker image pull ubuntu:14.04

# 如果不指定标签,则默认拉取latest最新的镜像。
docker image pull alpine

# 从非官方仓库拉取镜像:从tu-demo仓库中拉取v2镜像,其中镜像的拥有者是Docker Hub账户nigelpoulton(一个不应该被信任的账户)
docker image pull nigelpoulton/tu-demo:v2 
docker image pull microsoft/dotnet:latest

# 从第三方镜像仓库服务获取镜像(非Docker Hub),则需要在镜像仓库名称前面加上第三方镜像仓库服务的DNS名称。假设镜像位于Google容器镜像仓库服务(GCR)中,则需要在仓库名称前面加上gcr.io,可能需要在拉取镜像前完成登录。
docker image pull gcr.io/nigelpoulton/tu-demo:v2 

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

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

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

# 返回没有标签的镜像
[root@localhost ~]# docker image ls --filter dangling=true
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              4dbbb3b1e7a9        6 weeks ago         914MB

# 返回带标签的镜像
[root@localhost ~]# docker image ls --filter dangling=false
REPOSITORY                                                 TAG                 IMAGE ID            CREATED             SIZE
alpine                                                     latest              961769676411        4 days ago          5.58MB

# 过滤并显示tag=latest的镜像
[root@localhost ~]# docker image ls --filter=reference="*:latest"
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
alpine              latest              961769676411        4 days ago          5.58MB
lb-scale_web        latest              6a539fb0d274        6 weeks ago         918MB

# 格式化显示
[root@localhost ~]# docker image ls --format "{{.Repository}}: {{.Tag}}: {{.Size}}"
alpine: latest: 5.58MB

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

# 查找所有NAME为nigelpoulton的仓库,NAME代表仓库名称,包含了Docker ID,或非官方仓库的组织名称。
[root@localhost ~]# docker search nigelpoulton
NAME                                 DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
nigelpoulton/pluralsight-docker-ci   Simple web app used in my Pluralsight videos…   17                                      [OK]
nigelpoulton/tu-demo                                                                 11  

# 只显示官方镜像(默认显示的官方和非官方的)
[root@localhost ~]# docker search alpine --filter "is-official=true"
NAME                DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
alpine              A minimal Docker image based on Alpine Linux…   5585                [OK]    

# Docker 默认只返回25行结果,使用--limit参数可以增加返回内容行数,最多为100行。
[root@localhost ~]# docker search microsoft --limit "100" | wc -l
101

删除镜像

# 删除指定镜像
[root@localhost ~]# docker image rm IMAGE ID

# 删除所有镜像
[root@localhost ~]# docker image rm ${docker image ls -q
} -f

通过docker file构建BaseImage

自定义dockfile创建本地image

[root@localhost hello-world]# yum install gcc glibc-static
[root@localhost hello-world]# mkdir hello-world
[root@localhost hello-world]# cd hello-world/
[root@localhost hello-world]# touch hello.c
[root@localhost hello-world]# vim hello.c
#include<stdio.h>
int main()
{
        printf("hello docker\n");
}
[root@localhost hello-world]# gcc -static hello.c -o hello
[root@localhost hello-world]# ./hello
[root@localhost hello-world]# vim Dockerfile
FROM scratch
ADD hello /
CMD ["/hello"]
[root@localhost hello-world]# docker build -t soulboy/hello-wrld .
Sending build context to Docker daemon  860.7kB
Step 1/3 : FROM scratch
 ---> 
Step 2/3 : ADD hello /
 ---> d476563481ef
Step 3/3 : CMD ["/hello"]
 ---> Running in ce031e741831
Removing intermediate container ce031e741831
 ---> 6a68cb96d615
Successfully built 6a68cb96d615
Successfully tagged soulboy/hello-wrld:latest
[root@localhost hello-world]# docker image ls
[root@localhost hello-world]# docker history 6a68cb96d615

IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT

6a68cb96d615        3 minutes ago       /bin/sh -c #(nop)  CMD ["/hello"]               0B                  
d476563481ef        3 minutes ago       /bin/sh -c #(nop) ADD file:feb1dec246efa459c…   857kB
[root@localhost hello-world]# docker run soulboy/hello-wrld
hello docker

Container简介

Container

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

容器管理命令

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

镜像常用命令

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

构建BaseImage两种方式

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

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

#进入容器
[root@localhost ~]# docker exec -it cocky_merkle /bin/bash
#安装Vim
[root@060ef71c6a99 /]# yum install vim -y
#退出容器
[root@060ef71c6a99 /]# exit  
#基于容器创建新的BaseImage
[root@localhost ~]# docker commit cocky_merkle soulboy/centos-vim
sha256:9601a7182c7b2b497869e3ce32d5dd800c069d34ae5db4e98a8d75ccf87ed132
[root@localhost ~]# docker images | grep soulboy
soulboy/centos-vim                latest              9601a7182c7b        28 seconds ago      362MB

#对比原始Centos镜像和新的Centos镜像区别
[root@localhost ~]# docker history centos
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
9f38484d220f        3 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
<missing>           3 months ago        /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B                  
<missing>           3 months ago        /bin/sh -c #(nop) ADD file:074f2c974463ab38c…   202MB               

[root@localhost ~]# docker history soulboy/centos-vim
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
9601a7182c7b        4 minutes ago       /bin/bash                                       160MB               
9f38484d220f        3 months ago        /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B                  
<missing>           3 months ago        /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B                  
<missing>           3 months ago        /bin/sh -c #(nop) ADD file:074f2c974463ab38c…   202MB   

#提倡通过Dockerfile的方式创建Image,因为Dockerfile可以查看构建流程,更加安全,基于容器创建Image并不推荐,因为用户使用Image不知道Image中到底加入了那哪东西,不安全。
[root@localhost test]# mkdir docker-centos-vim
[root@localhost test]# cd docker-centos-vim/
[root@localhost docker-centos-vim]# vim Dockerfile
FROM centos
RUN yum install -y vim
[root@localhost docker-centos-vim]# docker build -t soulboy/centos-vim .
Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM centos
 ---> 9f38484d220f
Step 2/2 : RUN yum install -y vim
 ---> Running in 1db6f9fb291a
Loaded plugins: fastestmirror, ovl
Determining fastest mirrors
 * base: mirrors.nju.edu.cn
 * extras: mirrors.nju.edu.cn
 * updates: mirrors.cn99.com
Resolving Dependencies
--> Running transaction check
---> Package vim-enhanced.x86_64 2:7.4.160-6.el7_6 will be installed
--> Processing Dependency: vim-common = 2:7.4.160-6.el7_6 for package: 2:vim-enhanced-7.4.160-6.el7_6.x86_64
Removing intermediate container 1db6f9fb291a
 ---> 2de975c65628
Successfully built 2de975c65628
Successfully tagged soulboy/centos-vim:latest

[root@localhost docker-centos-vim]# docker images
REPOSITORY                        TAG                 IMAGE ID            CREATED              SIZE
soulboy/centos-vim                latest              2de975c65628        About a minute ago   362MB

#基于新Image创建并运行容器
[root@localhost docker-centos-vim]# docker run -it soulboy/centos-vim
[root@060ef71c6a99 /]# exit
[root@localhost docker-centos-vim]# docker exec -it cocky_merkle /bin/bash
[root@060ef71c6a99 /]# vim --help

Dockerfile简介

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

传送门

Dockerfile语法解析

# 制作Base Image(不依赖于任何Base Image)
FROM scratch

# 使用Base Image(尽量使用官方的Image作为Base Image,为了安全考虑。)
FROM centos

# 注释
LABEL maintainer="xxx@gmail.com"
LABEL version="1.0"
LABEL description="This is description"

# 运行命令(每运行一次RUN都会生成新的一层,因此复杂的RUN应该使用反斜线换行,以避免无用的分享,合并多条命令成一行!)
RUN yum update && yum install -y vim\ 
python-dev

# 设定当前工作目录 类似于cd(用WORKDIR,不要用RUN cd,尽量使用绝对目录!)
WORKDIR /test    #如果没有test目录会自动创建test目录
WORKDIR demo
RUN pwd   #输出结果为: /test/demo

# ADD 添加文件到到Image的指定目录
ADD hello /	#拷贝hello文件到Image的根目录中
ADD test.tar.gz /  #添加到根目录并解压

# COPY 复制文件到指定目录(多数情况下,COPY优于ADD! ADD除了COPY还有额外功能解压,添加远程文件/目录可以使用curl或者wget!)
WORKDIR /root
COPY hello test/     #/root/test/hello

# ENV 宏定义,方便引用
ENV MYSQL_VERSION 5.6 #设置常量
RUN yum install -y mysql-server="${MYSQL_VERSION}"\
 && echo 123 #引用常量

RUN、CMD、ENTRYPOINT对比

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

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

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

发布镜像

# 本地登录dockerhub用户
[root@localhost ~]# docker login
Login Succeeded

# pull的格式: dockerhub_count/Image_name
[root@localhost hello-world]# docker push rtsfan1024/hello-world:latest

Dockerfile构建Image(Flask的APP)

 部署一个基于flask的python镜像

# 安装pip
[root@localhost test]# yum install python-pip
[root@localhost test]# 
python -m pip install –upgrade pip

#安装flask
[root@localhost Python-3.7.3]# pip install --trusted-host pypi.python.org --trusted-host py --trusted-host files.pythonhosted.org flask

# 运行app.py 并访问 http://192.168.31.210:5000/
[root@localhost test]# python app.py 
 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: This is a development server. Do not use it in a production deployment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
192.168.31.230 - - [23/Jun/2019 22:24:16] "GET / HTTP/1.1" 200 -
192.168.31.230 - - [23/Jun/2019 22:24:20] "GET /favicon.ico HTTP/1.1" 404 -

#  创建目录编写Dockerfile
[root@localhost test]# cd /test
[root@localhost test]# mkdir flask-hello-world

# 编写Dockerfile文件
[root@localhost test]# cat /test/flask-hello-world/Dockerfile 
FROM python:2.7
LABEL maintainer="Soul Boy<rtsfan1024@gmail.com>"
RUN pip install flask -i http://pypi.douban.com/simple --trusted-host pypi.douban.com
COPY app.py /app/
WORKDIR /app
EXPOSE 5000
CMD ["python", "app.py"]

# 基于Flask框架的python应用代码
[root@localhost test]# cat /test/flask-hello-world/app.py 
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
    return "hello docker"
if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

# 基于Dockerfile构建Image
[root@localhost flask-hello-world]# cd /test/flask-hello-world/
[root@localhost flask-hello-world]# docker build -t rtsfan1024/flask-hello-world .
[root@localhost flask-hello-world]# docker run -d -p 5000:5000 rtsfan1024/flask-hello-world:latest

# 访问http://192.168.31.210:5000/
`hello docker`

容器操作

# 进入已存在的容器
docker exec -it container_id /bin/bash 

# 停止容器
docker stop container_id

# 删除容器(退出状态的容器)
docker rm container_id

# 指定创建容器时指定名称(名字也具有唯一性)
docker run -d --name=demo Image

# 分析容器
docker inspect redis

# 容器运行日志
docker logs container_id

通过Dockerfile构建工具

[root@localhost flask-hello-world]# mkdir -pv /test/stress
[root@localhost flask-hello-world]# cat /test/stress/Dockerfile
FROM centos
RUN yum install -y epel-release && yum install -y stress
ENTRYPOINT ["/usr/bin/stress"]
CMD []
[root@localhost flask-hello-world]# cd /test/stress/
[root@localhost stress]# docker build -t rtsfan1024/stress .

# ENTRYPOINT + CMD : 命令 + 接收参数
[root@localhost stress]# docker run -it rtsfan1024/stress:latest --vm 1 --verbose
stress: info: [1] dispatching hogs: 0 cpu, 0 io, 1 vm, 0 hdd
stress: dbug: [1] using backoff sleep of 3000us
stress: dbug: [1] --> hogvm worker 1 [6] forked
stress: dbug: [6] allocating 268435456 bytes ...
stress: dbug: [6] touching bytes in strides of 4096 bytes ...
stress: dbug: [6] freed 268435456 bytes

容器的资源限制

容器的内存限定

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

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

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

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


作者:Soulboy