Docker

Docker是一个能够把开发的应用程序自动部署到container的开源container engine(容器引擎)。
Docker官网对它的定义是:Simplify modern application development and delivery by bundling distributed services into a single Docker application
Docker背后的想法是创建软件程序可移植的轻量容器,让其可以在任何安装了Docker的机器上运行,而不用关心底层操作系统。
 
Docker版本选择
Docker有社区版(Community Edition)、企业版(Enterprise Edition)两种。企业版主要是在安全方面进行了加强。
从2017年初的1.13版本以后,Docker使用了新的<年>.<月>形式的版本号规则。
目前发布通道分为Stable、Test、Nightly三种。
如今,Docker运行容器已经不是简单的通过Docker Daemon来启动,Docker Daemon被分成了多个模块以适应OCI标准。
 
Docker CE这个产品,是由Moby组织下的Moby项目以及其他项目构建和编译出来的。因此,并不会存在一个开源项目叫Docker CE。因为Docker CE是一个产品,只能从Docker公司官网上来下载使用。
 
64位Centos下Docker-CE部署
首先清除当前docker
$ sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
docker默认存储目录是/var/lib/docker/,包括 images、containers、volumes、networks等,是一个被保护的目录。
1、设置Docker's repositories并从中安装
(1)安装需要的包
$ sudo yum install -y yum-utils
yum-utils提供了yum-config-manager
device-mapper-persistent-data和lvm2是deviceapper存储驱动所必须的
(2)使用如下命令设置stable仓库
$ sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
(3)可选:启用/禁用edge和test仓库
$ sudo yum-config-manager --enable/disable docker-ce-nightly
$ sudo yum-config-manager --enable/disable docker-ce-test
这些仓库被包括在docker.repo文件中但默认是disable的
(4)更新yum软件包目录
$ sudo yum makecache fast
(5)安装最新版Docker CE
$ sudo yum install docker-ce docker-ce-cli containerd.io
或者搜索选择版本
$ yum list docker-ce --showduplicates | sort -r
docker-ce.x86_64  3:18.09.1-3.el7                     docker-ce-stable
docker-ce.x86_64  3:18.09.0-3.el7                     docker-ce-stable
docker-ce.x86_64  18.06.1.ce-3.el7                    docker-ce-stable
docker-ce.x86_64  18.06.0.ce-3.el7                    docker-ce-stable
选择需要安装的版本
$ sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
(6)启动Docker CE
$ sudo systemctl start docker
2、安装RPM包并手工安装、手工升级
(1)前往这两个网页下载rpm文件:
(2)安装Docker CE,把下面的路径改为下载包的路径
$ sudo yum install /path/to/package.rpm
(3)启动Docker CE
$ sudo systemctl start docker
(4)更新Docker CE
下载新的包文件重复安装过程
$ sudo yum -y update /path/to/package.rpm
 
检查版本和基本信息
docker -v
docker info
 
运行Container
开启运行一个Container:
docker run <image>
相当于:
docker create <image>
docker start <container>
image参数就是指定在这个Container里运行哪个镜像。如果未指定具体标签,将默认选择tag为latest的镜像。
一个image可以同时开启并运行多个Container,同时运行的多个Container也可以同时运行同一个image。
 
命令示例:
docker run -i -t ubuntu /bin/echo 'Hello world'
-p 8080:80   将容器的80端口和主机的8080映射。一个主机端口只能绑定一个容器端口。如果要映射的端口比较多,冒号前后的端口号可以是一个范围
-i                   打开容器的标准输入
-t                  为容器打开一个命令行终端
-d                 表示该容器为后台容器,
--name         为容器指定名
--rm              一旦运行的进程退出就删除容器
-e k="v"       创建环境变量 
注意点:
(1)运行在后台的容器,创建成功后就与当前终端无关了。要停止容器只能调用docter stop或者docker kill等命令
(2)使用-d参数启动的容器,启动命令不能是bash,否则执行后会直接退出。可以搭配-i和-t参数为容器提供一个伪tty n
(3)-i和-t参数一般搭配使用,使容器运行在前台,有指定的交互控制台。可以给容器输入和输出。但只要从所在终端exit了,容器就会变成停止状态。 
 
此命令使用Ubuntu镜像创建并启动一个容器 ,在容器里执行 /bin/echo 'Hello world’命令。本地不存在此镜像时会自动从配置的镜像仓库拉取。
容器的文件系统是在只读的镜像文件上加一层可读写的文件层,这样可以保证镜像不变只记录改变的数据。
接着会配置容器的网络,docker会为容器分配一个虚拟网络接口,并通过桥接的方式将该网络接口桥接到宿主主机上去,然后为该虚拟网络接口分配一个ip地址。
最后,docker在新容器中运行指定的命令。启动的运行程序称为initial进程。这个initial进程启动的时候,容器也会随之启动。
容器内不一定只有一个进程,initial进程本身也可以产生其他的子进程,或者通过docker exec等运维操作产生子进程。
当initial进程退出的时候,容器中的所有子进程也会随之退出(防止资源的泄漏)。因此,可以认为容器的生命周期和initial进程的生命周期是一致的。
 
交互式进入容器中
docker attch <container name | id>
docker exec -it <container name | id> /bin/bash
docker attch可以使本机的输入直接输到容器中,容器的输出会直接显示在本机的屏幕上。因此,可以attach到一个已经运行的容器的stdin,然后进行命令执行的动作。 但是需要注意的是,如果从这个stdin中exit,会导致容器的停止。
 
查看Container和image
查看容器列表:
docker ps
加上-a参数以包含非运行中的容器
查看镜像列表:
docker images
此命令只会显示顶层镜像,加上-a参数可以显示隐藏的中间层镜像
查看镜像和容器的元数据,包括制作者、适应架构、各层的数字摘要等:
docker inspect <image name | id>
docker inspect <container name | id>
例如查看一个容器的挂载信息:
docker inspect <container name | id> | grep Mounts -A 9
 
停止/重启/一个 Container
docker stop <container name | id>
docker restart <container name | id>
docker kill <container name | id>
stop容器时会向容器中PID为1的initial进程(主进程)发送SIGTERM信号,默认在10秒后才会发送SIGKILL信号;kill容器时会直接向initial进程发送SIGKILL信号
使用参数-t指定等待的时间
如果容器启动命令是/bin/sh(主进程是shell),则不会将信号转发给用户进程
 
例如停止所有容器:
docker stop $(docker ps -a -q)

 

删除一个 image/Container
docker rmi <image name>
docker rm <container name | id>
已经创建容器的镜像无法删除,必须先删除容器
-f  强制删除
 
Container与主机间数据拷贝
docker cp CONTAINER:SRC_PATH DEST_PATH
docker cp SRC_PATH|- CONTAINER:DEST_PATH
 
docker system-全新的命令集合
查看docker整体磁盘使用率的概况,包括镜像、容器和(本地)volume:
docker system df
查看docker系统实时事件,不包括容器内的(等同于docker events命令):
docker system events
查看整个docker系统的信息(等同于docker info命令):
docker system info
清理资源,包括stop的容器、未使用的数据卷、未使用的网络、dangling的镜像(无名称悬停镜像):
docker system prune
-a 强制清理,包括所有未使用的镜像
 
开启Remote API 访问 2375端口
在docker的启动参数中加上-H tcp://0.0.0.0:2375
就可以用如下命令访问远程主机的docker
docker -H tcp://ip:2375 version
此时任何人都可以不经过认证远程访问此docker,因此需要加上授权的方式,在启动参数中加上:
--tlsverify
--tlscacert=/data/hps/certificate/ca.pem
--tlscert=/data/hps/certificate/server-cert.pem
--tlskey=/data/hps/certificate/server-key.pem
 
刚刚的命令就变成了:
docker --tlsverify --tlscacert=/data/hps/certificate/ip/ca.pem --tlscert=/data/hps/certificate/ip/client-cert.pem --tlskey=/data/hps/certificate/ip/client-key.pem -H ip:2375 info

 

容器日志
查看某个容器的日志
docker logs <container name | id>
-f:跟踪日志输出
--since="xxxx-xx-xx":显示某个开始时间的日志 
--tail=10:显示最后10条日志
-t:显示时间戳
 
docker支持的日志引擎如下:
  • none
关闭docker的回显日志,无法查看任何容器输出的日志,docker logs看不到任何输出。
  • syslog
把所有容器的回显日志打到系统的syslog中。
  • journald
把所有容器的回显日志打到系统的journald服务中。
  • fluentd
把所有容器的回显日志打到fluentd服务中
  • gelf
把所有容器的回显日志打到支持Graylog Extended Log Format格式的服务(比如Graylog或Logstash)中
  • json-file
把每个容器的回显日志打到每个容器的内部,形式为json文件。
在实际使用中,有些容器在启动后有大量的回显日志,尤其在程序内部报错时打出的日志信息尤其巨大,因此要配置存储目录、存储上限:
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "100m",
    "max-file": "3"    # 意味着一个容器有三个日志,分别是id+.json、id+1.json、id+2.json
    }
  "date-root": "xxx"   # 存储目录

 

数据卷
默认情况下,当用户退出容器而容器中又没有非守护进程在运行时,容器会进入关闭状态,数据的修改会保留在层级的可写文件系统内。当用户需要重新开启一个容器时,是无法访问原来所做的修改的,而是恢复到镜像的初始化状态。
为了解决数据持久化的问题,Docker提供了卷(Volume)机制。就是将宿主机中的某个目录,mount到容器中
这样,在容器中此目录下的修改,即便容器关闭,数据也会保留下来,供宿主机和其他容器访问。
卷是受控存储,是由Docker引擎进行管理维护的。因此使用卷,不必处理 uid、SELinux 等各种权限问题,Docker引擎在建立卷时会自动添加安全规则,以及根据挂载点调整权限。并且可以统一列表、添加、删除。
  • 在运行容器的时候,在Docker中创建一个匿名数据卷
docker run -dti -v /data centos
在docker容器中会有/data目录,这个目录不归属于层级文件系统
创建命名数据卷:
docker volume create --name xxx
由于数据卷有名字,下次挂载的时候还可以用
数据卷默认可能会保存于/var/lib/docker/volumes,不过一般不需要、也不应该访问这个位置
  • 将宿主机的一个目录,挂在到容器里
docker run -tdi -v /var/data:/data:ro centos
冒号前为宿主机目录,必须为绝对路径,冒号后为镜像内挂载的路径在冒号后指定权限
直接挂载目录,可能会遇到Permission Denied问题
  • 挂载单个文件到容器中
docker run -tdi ~/dbback.tar.gz:/dbback.tar.gz centos
  • 数据卷容器
数据卷其实就是一个正常的容器,专门用来提供数据卷供其它容器挂载共同使用的。其它容器启动可以直接挂载数据卷容器中定义的挂载信息。
创建一个包含数据卷的容器供其他容器使用,这个容器并不需要一直开启
docker run -tdi -v /data --name data_s centos
如果需要将数据同步到宿主机的目录中,则创建数据卷容器的时候,选择挂载宿主机的目录,如:
docker run -tdi -v /data:/data --name data_s centos
创建两个容器,使用这个数据卷容器
docker run -ti --volumes-from data_s  --name web1 centos
docker run -ti --volumes-from data_s  --name web2 centos
此时这两个容器,都可以共同读写/data目录了
其实就是挂载了匿名卷的容器,早就过时了。
 
监控
docker自带的docker stats命令,可以显示容器使用的系统资源。数据主要来自cgroup。
 
例如,/cgroup/memory/docker/xxxx/目录下,cgroup中的memory子系统为hierarchy提供了大量文件,与docker监控相关的主要有:
    memory.usage_in_bytes:已使用的内存量(包含cache和buffer)字节数,相当于linux的used_mem
    memory.limit_in_bytes:限制的内存字节数,相当于linux的total_mem
    memory.failcnt:申请内存失败次数计数
    memory.memsw.usage_in_bytes:已使用的内存和swap字节数
    memory.memsw.limit_in_bytes:限制的内存和swap字节数
    memory.memsw.failcnt:申请内存和swap失败次数计数
    memory.stat:内存相关状态
 
docker stats命令会将容器的内存监控分为cache、usage、swap usage、kernel usage、kernel tcp usage。
其中cache是从memory.stat中的cache中获取;usage是使用memory.usage_in_bytes和memory.limit_in_bytes进行相除。
这一方式有一个弊端,就是不够细化,没有区分出cache部分,不能真正反映内存使用率。因为一般来说cache是可以复用的内存部分,因此一般将其计入到可使用的部分。
 
在统计内存使用量时可以考虑将cache计算排除出去。类似于linux中计算real_used时将buffer和cache排除。
但计算cache并不能直接应用memory.stat中的cache,因为其中包括了tmpfs,而tmpfs(即share memory)算是实际使用的内存部分。
因为在memory.stat中存在:active_file + inactive_file = cache - size of tmpfs
因此可以计算实际使用的内存量为:real_used = memory.usage_in_bytes - (rss + active_file + inactive_file)
 

 
Docker是client-server架构,docker daemon(dockerd)监听API请求,负责管理容器、镜像、容器网络、卷等;用户使用CLI通过API接口来控制dockerd。
Docker服务启动之后,可以看见系统上启动了dockerd进程,其子进程为containerd,containerd的子进程为若干个containerd-shim进程,每个containerd-shim进程有1个子进程,就是容器内的主进程,该进程启动命令就是容器的启动命令
 
moby daemon是实际管理容器的engine。
 
containerd是容器运行时管理引擎
    向上为moby daemon提供容器、镜像的相关管理的gRPC接口,使得moby daemon屏蔽下面的结构变化,确保原有接口向下兼容。
    向下通过containerd-shim结合runC,使得引擎可以独立升级,避免之前Docker升级会导致所有容器不可用的问题。
上图从左右两部分看,可以认为containerd提供了两大功能:对容器生命周期(runtime)的管理;对镜像拉取、存储的管理。
按照水平层次来看:
    第一层是GRPC,containerd通过GRPC的形式来对上层提供服务的。Metrics主要是提供cgroup Metrics的一些内容。
    第二层的左边是容器镜像的存储;中间images、containers对应的Matadata是通过bootfs存储在磁盘上的;右边的Tasks是管理容器的容器结构,容器的一些操作会有Event向上层发出,上层订阅Event以知道容器状态发生什么变化。
    第三层是Runtimes层。
 
shim类似于containerd底层的守护进程模块,这样设计的原因有几点:
1、containerd需要管理容器生命周期,而容器可能是由不同的容器运行时所创建出来的,因此需要提供一个灵活的插件化管理。而 shim就是针对于不同的容器运行时所开发的,这样就能够从 containerd中脱离出来,通过插件的形式进行管理。
2、因为shim插件化的实现,使其能够被containerd动态接管。如果不具备这样的能力,当moby daemon或者containerd daemon意外退出的时候,容器就没人管理了,那么它也会随之消失、退出,这样就会影响到应用的运行。
3、因为随时可能会对moby或者containerd进行升级,如果不提供shim机制,那么就无法做到原地升级,也无法做到不影响业务的升级,因此containerd shim非常重要,它实现了动态接管的能力。
 
runC是Docker开发的容器runtime,符合oci规范,也是现在docker的默认runtime。
它提供了一个命令行小工具,没有镜像管理功能,只能根据准备好的文件系统创建容器。具体底层实现基于libcontainer库。
启动Docker Daemon时增加--add-runtime参数可以选择其他的runtime实现
posted @ 2020-12-24 13:41  扬羽流风  阅读(204)  评论(0编辑  收藏  举报