Docker入门知识的个人笔记总结,不适合0基础的同学作为入门资料学习。
Docker为什么比虚拟化省资源?
Docker利用了Linux核心中的资源分离机制,如cgroups和linux的命名空间namespace,来建立独立的容器。它和宿主机共用内核,避免了虚拟机使用自己独立操作系统所带来的开销。
Docker如何实现资源隔离
cgroups提供硬件计算资源的隔离,包括cpu,内存,存储,io与网络。
命名空间隔离了工作环境,包括进程数、网络、用户ID、挂载系统等等
命名空间分为6种,不同版本的linux内核对其支持程度是不同的,具体如下:
namespace | 系统调用参数 | 隔离内容 | 最终完成的内核版本 |
---|---|---|---|
UTS | CLONE_NEWUTS | 主机名和域名 | 2.6.19 |
IPC | CLONE_NEWIPC | 信号量,消息队列与共享内存 | 2.6.19 |
PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 | 2.6.29 |
Mount | CLONE_NEWNS | 文件系统 | 2.4.19 |
User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
国内大多爱用Centos和Ubuntu,部署docker服务最好使用Centos7以上或Ubuntu,Centos6的原始内核太老对Docker的支持不太好。
docker架构的组成部分
Docker 的核心组件包括:
- Docker 客户端 - Client,使用docker命令实现对容器的管理
- Docker 服务端/引擎 - Docker daemon 管理和docker客户端的交互,管理容器进程
- Docker 镜像 - Image
- Registry - 镜像仓库
- docker 容器 - Container
容器与镜像的区别
-
镜像(Images)
- 只读的硬盘文件,用来创建容器
- 一个镜像经常是基于另一个镜像,加了一些定制化内容
- 你可以构建自己的镜像,你也可以使用别人放在公共镜像仓库的镜像
-
容器(Containers)
- 容器是一个镜像运行时的实例
- 可以通过docker的api或者命令行命令对container进行创建、运行、移动,删除等操作
- 一个容器可以被连接进多个网络、添加存储或者根据目前的容器状态创建一个新的镜像
docker的安装
Centos7的Extra仓库提供了docker-ce的安装包,但是版本可能相对老一些。
官方网站的是最新版本但是源站的yum仓库在海外,很慢。可以使用国内的镜像仓库
比如使用清华大学的镜像:
sudo yum remove docker docker-common docker-selinux docker-engine
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
sudo sed -i 's+download.docker.com+mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
sudo yum makecache fast
sudo yum install docker-ce
其他系统版本可参考:
https://mirror.tuna.tsinghua.edu.cn/help/docker-ce/
安装后可以进行docker环境的检查:
docker version可以查看当前docker及其内部组件的具体版本
[root@master ~]# docker version
Client: Docker Engine - Community
Version: 19.03.12
API version: 1.40
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:46:54 2020
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.12
API version: 1.40 (minimum version 1.12)
Go version: go1.13.10
Git commit: 48a66213fe
Built: Mon Jun 22 15:45:28 2020
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.13
GitCommit: 7ad184331fa3e55e52b890ea95e65ba581ae3429
runc:
Version: 1.0.0-rc10
GitCommit: dc9208a3303feef5b3839f4323d9beb36df0a9dd
docker-init:
Version: 0.18.0
GitCommit: fec3683
Docker常用操作
docker search
在docker hub上搜索该images
docker pull
从某个repository或者registry拉取镜像
docker images
列出images
docker create
创建一个新的container
docker start
启动一个或多个容器
docker run
在一个新的容器中运行一个命令
docker attach
挂载至一个运行的容器
docker ps
列出容器列表
docker logs
获得某个容器的日志;容器的日志默认是打印至容器的tty终端的,因此需要使用该命令才能打印至宿主机的tty终端
docker restart
重启一个容器
docker stop
停止一个或多个容器
docker kill
杀死一个或多个容器
docker rm
移除一个或多个容器
容器的生命周期
Docker Image
Docker镜像含有启动容器所需要的的文件系统及其内容,因此,其用于创建并启动docker 容器。
- 采用分层构建机制,最底层为bootfs,其次为rootfs
- bootfs:用于系统引导的文件系统,包括bootloader和kernel,容器启动完成后会被卸载以节约内存资源
- rootfs:位于bootfs之上,表现为docker容器根文件系统。docker中,rootfs由内核挂载为只读模式,然后通过联合挂载技术额外挂载一个可写层。
docker的镜像层次
位于下层的镜像被称为父镜像(parentn image),最底层的称为基础镜像(base image)
最上层为可写层,其下均为只读层。
联合挂载文件系统将这些层次组织起来,常见的联合挂载系统有aufs(ubuntu默认),devicemapper(centos7默认),overlayfs等
Docker registry
启动容器时,docker daemon会试图从本地获取相关镜像,本地镜像不存在时,其将从Registry中下载该镜像并保存到本地
registry:registry是一个无状态的,高度可扩展的服务器应用程序,负责存储和分发docker images
Docker Registry
Registry 用于保存docker镜像,包括镜像的层次结构和元数据。用户可以自建Registry,也可以使用官方的Docker Hub
分类:
- Sponsor Registry:第三方的registry,供客户和Docker社区使用
- Mirror Registry:第三方的registry,只让客户使用
- Vendor Registry:由发布Dokcer镜像的供应商提供的registry
- Private Registry:通过设有防火墙和额外的安全层的私有实体提供的registry
Repository
由某个特定的docker镜像的所有迭代版本组成的镜像仓库
- 一个Registry中可以存在多个Repository
- Repositry可以分为"顶层仓库"和"用户仓库"
- 用户仓库名称格式为"用户名/仓库名"
- 每个仓库可以包含多个Tag,每个Tag对应一个镜像
registry可以理解为Repository的仓库,repository是images的仓库
registry中的镜像通常由开发人员制作,而后推送至"公共"或"私有"Registry上保存,供其他人员使用,例如部署到生产环境。
Docker Hub
docker默认的云端仓库服务。主要提供了以下功能。
- Image Repositories(镜像仓库)
- 从社区和官方库中寻找、拉取、管理、上传镜像
- Automated Builds(自动构建)
- 当你改变镜像构建源码时,自动创建新的镜像
- Webhooks
- 当你成功push之后可以绑定一个钩子去触发一些动作
- Organizations
- 创建工作组来管理镜像仓库
- GitHub and Bitbucket Integration
- 将docker hub加入至持续集成工作流中
可以使用如下格式的命令从指定仓库获取镜像:
docker pull [:]/[/]:
镜像制作途径
- 通过Dockerfile制作镜像
- 从一个运行中的容器制作镜像(运行中容器改动后制作成镜像)
基于容器制作镜像
docker commit [OPTIONS] CONTAINER [REPOSITORY:[TAG]
选项 | 默认值(如果有) | Description |
---|---|---|
–author, -a | 作者 | |
–change,-c | 提交同时改变Dockerfile中的内容 | |
–message,-m | 提交message | |
–pause,-p | true | 提交期间挂起容器 |
例子:
docker commit --change=‘CMD [“httpd”, “-h /data/httpd/htdocs”, “-f”]’ -c “EXPOSE 80” bbox1 suny/busybox/httpd:v0.1
为镜像打标签
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
可以基于ID打标,或者基于名称和标签打标,还额可以基于私有Registry打标。
将镜像push到Docker仓库
推倒Docker Hub时首先需要使用docker login登录,然后使用docker push
docker push USERNAME/IMAGE
镜像导入和导出
-
docker save
- 保存一个或多个镜像到一个tar包(默认STDOUT)
- docker save [OPTIONS] IMAGE [IMAGE…]
- –output,-o:写到一个文件,而不是STDOUT
-
docker load
- Load一个image从一个tar或者STDIN
- docker load [OPTIONS]
- –input,-i:从一个TAR文件而不是STDIN
- –quiet,-q:抑制输出
Docker 数据卷
docker镜像由多个只读层叠加而成,启动容器时,Docker会加载只读镜像层并在镜像栈顶部添加一个读写层
如果运行中的容器修改了现有的一个已经存在的文件,那该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏,这就是“写时复制(COW)”机制
关闭并重启容器,其数据不受影响,但是删除容器后,更改会全部丢失。
docker的数据读写机制带来如下问题:
- 存储于联合文件系统中,不易于宿主机访问
- 容器间的共享不变
- 删除容器后数据会丢失
解决方案:Data volumes
Docker volumes
卷是容器上的一个或多个目录,此类目录可绕过联合文件系统,与宿主机的某目录关联。
Data volumes提供了如下一些特性以便于容器间的数据共享与持久化:
- volume与容器初始化之时就会创建,由base image提供的卷中的数据会在此期间完成复制
- 容器间的volumes可以被共享或重用
- 可以直接对volume中的数据进行修改
- volume中的数据不影响image更新升级
- 即使容器自身被删除,容器卷也会存留。即使volume没有被任何容器引用,也不会主动做垃圾回收操作。volume的数据持久化独立于容器的生命周期之外。
镜像好比源码,容器好比运行源码后的进程,卷好比数据。于是镜像可以重用,卷可以共享。
卷实现了源代码(镜像)和数据(卷)的分离,用户制作镜像时无须考虑镜像运行的容器所在的主机的环境。
数据卷类型
Docker有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不同。
- bind mount volume
- 和宿主机文件系统中的某一目录绑定的卷,数据存储于与卷绑定的宿主机目录中
- docker managed volume
- 由docker daemon创建的卷,在宿主机中表现为docker所属的一个文件
docker run命令使用-v选项即可使用Volume
- Docker-managed volume
- docker run -it -name bbox1 -v /data busybox
- docker inspect -f {{.Mounts}} bbox1
- Bind-mount Volume
- docker run -it -v HOSTDIR:VOLUMEDIR --name bbox2 busybox
- docker inspect -f {{.Mounts}} bbox2
共享数据卷
多个容器之间可以通过如下两种方式共享数据卷。
- 多个容器的卷使用同一个主机目录。如:
- docker run -it --name c1 -v /docker/volumes/v1:/data busybox
- docker run -it --name c2 -v /docker/volumes/v1:/data busybox
- 复制使用其他容器的卷,docker run命令使用–volumes-from选项
- docker run -it --name bbox1 -v /docker/volumes/v1:/data busybox
- docker run -it --name bbox2 --volumes-from bbox1 busybox
docker 网络
单主机虚拟网络:容器之间可互通,但是和外界隔离
多主机网络
桥接式容器(Bridged containers)
桥接式容器一般拥有两个接口,一个环回接口,一个连接至主机上某个桥设备的以太网接口。
docker daemon 启动时默认会创建一个名为docker0的网络桥,并且创建的容器为桥接式容器,其以太网接口桥接至docker0
- –net bridge即为将容器接口添加至 docker0桥
docker0桥为NAT桥,因此桥接式容器可通过此桥接口访问外部网络,但防火墙规则阻止了一切从外部网络访问桥接式容器的请求。
docker run --rm --net bridge busybox:latest ifconfig -a
参数补充:
- --hostname HOSTNAME : 为容器指定主机名
- --dns DNS_SERVER_IP 为容器指定所使用的的dns服务器地址
- --add-host HOSTNAME:IP为容器指定本地主机名解析
封闭式容器(Closed containers)
- 不参与网络通信,运行于此类容器中的进程仅能访问本地环回接口
- 仅适用于进程无需网络通信的场景中,如备份、进程诊断及各种离线任务等。
- docker run --rm --net none busybox:latest ifconfig -a
Opening inbound communication
Docker0为NAT桥,因此容器一般获得的是私有网络地址
可以把容器想象为宿主机NAT服务器背后的主机
如果开放容器或骑上的服务为可被外部网络访问,需要在宿主机上为其定义DNAT规则。如:
- 对宿主机某IP地址的某端口的访问映射给某容器地址的某端口
-A PREROUTING -d 主机IP -j DNAT --to-destination 容器IP - 对宿主机某IP地址的某端口的访问映射给某容器地址的某端口
- 主机IP:PORT 容器IP:PORT
- -A PREROUTING -d 主机IP -p {tcp|udp} --dport 主机端口 -j DNAT --to-destination 容器IP:容器端口
- 主机IP:PORT 容器IP:PORT
为docker run 命令使用-p选项即可实现端口映射,无需手动添加规则:
- -p
- 将指定的容器端口映射至主机所有地址的一个动态端口
- -p :
- 将指定的容器端口映射至主机指定的主机端口
- -p ::
- 将指定的容器端口映射到主机指定ip的动态端口
- -p ::
- 将指定的容器端口映射至主机指定ip的端口hostPort
“动态端口”指的是随机端口,具体的映射结果可使用docker port命令查看。
可以使用-P或–publish-all将容器所有计划要暴露的端口全部映射至主机端口,如:
docker run -d -P --expose 2222 --expose 3333 --name web busybox:latest /bin/httpd -p 2222 -f
docker port web
如果不想使用默认的docker0桥接口,或者需要修改此桥接口的网络属性,可以通过docker daemon命令使用-b,–bip,–fixed-cidr,–default-gateway,–dns和–mtu等选项进行设定。
联盟式容器(Joined containers)
联盟式容器是指使用某个已存在容器的网络接口的容器,接口被联盟内的各个容器共享使用,因此联盟式容器彼此间的网络名称空间是共用的。
- 创建一个监听与2222端口的http服务器
- docker run -d -it --rm -p 2222 busybox:latest /bin/httpd -p 2222 -f
- 创建一个联盟式容器,并查看其监听的端口
- docker run -it --rm --net container:web --name joined busybox:latest netstat -tan
联盟式容器间网络名称空间虽然共用,但是其他名称空间比如User,Mount等还是隔离的
联盟式容器彼此间存在端口冲突的可能性,因此,通常只会在多个容器上的程序lookback互相通信,或对某个已存在的容器的网络属性进行监控时才是用这种模式的网络模型。
开放式容器(open containers)
开放式容器共享主机网络名称空间的容器,他们对主机的网络名称空间拥有全部的访问全向,包括访问哪些关键性服务,这对宿主机安全性有很大的潜在威胁。
为docker run命令使用–net host选项即可创建开放式容器,例如:
docker run -it --rm --net host busybox:latest /bin/sh
Dockerfile
前文提到过,镜像的生成途径包括Dockerfile和基于容器制作。本节介绍Dockerfile。
Dockerfile是构建docker image的源代码。
Docker可以通过阅读Dockerfile的指令来自动构建docker images。一个Dockerfile文件只是一个包含了所有配置镜像命令的文本文件。
Dockerfile语法
#开头为注释
每行为一个命令,基础格式为:指令+参数。指令是大小写不敏感的。但是将指令大写是约定俗成的惯例,它可以很好地将指令和参数区分开
docker运行dockerfile时,从上到下依次执行
第一条指令必须是FROM,以确定你需要构建镜像的基础镜像。
常用指令
FROM
必须为Dockerfile文件开篇的第一个非注释行,用于为映像文件构建过程指定基准镜像。后续的指令运行于此基准镜像所提供的运行环境。
基准镜像可以是任何可用的镜像文件。默认情况下docker build会在docker主机上查找指定的镜像文件,其不存在时,则会从Docker Hub Registry上拉取所需的镜像文件。
语法:
- FROM [:] 或者
- FROM @
MAINTANIER
用于让Dockerfile制作者提供本人的详细信息。Dockerfile并不限制MAINTAINER指令出现的位置,但是建议将其放置于FROM指令之后。
语法:
- MAINTAINER <author’s info> 如:
- MAINTAINER “suny suny@eusc.com”
COPY
用于从Docker主机复制文件至创建的新映像文件
语法:
- COPY … 或者
- COPY ["",…""]
- 要复制的源文件或目录,支持使用通配符
- 目标路径,镜像中文件系统路径,建议使用绝对路径,否则以WORKDIR为其起始路径。
文件复制准则:
- 必须是build上下文中的路径,不能是其父目录中的文件
- 如果是目录,则其内部文件或子目录会被递归复制,而src目录自身不会被复制
- 如果指定了多个,或者在中使用了通配符,则必须是一个目录,且必须以/结尾。
- 如果事先不存在,他将会被自动创建,这包括父目录路径。
ADD
ADD指令类似于COPY指令,ADD支持使用TAR文件和URL路径
语法:
- ADD … 或者
- ADD ["",…""]
操作准则:
- 同COPY指令
- 如果为URL且dest不以/结尾,则被下载并直接创建为dest,若以/结尾,则文件被下载至dest目录,原文件名保留
- 如果src是tar文件,则被展开为一个目录,行为类似tar -x命令;通过URL获取的tar文件不会自动展开
WORKDIR
用于为Dockerfile中所有的RUN,CMD,ENTRYPOINT,COPY和ADD指定设定工作目录
语法:
- WORKDIR
- 在Dockerfile文件中,WORKDIR指令可出现多次,其路径也可以为相对路径,不过是相对于前一个WORKDIR指令指定的路径
- WORKDIR也可以调用由ENV指定定义的变量
如:
- WORKDIR /var/log
- WORKDIR $STATEPATH
VOLUME
用于在image中创建一个挂载点目录,以挂载Docker host上的卷或其他容器上的卷。
- VOLUME
- VOLUME [""]
EXPOSE
用于为容器打开指定要监听的端口以实现与外部通信。
- EXPOSE [/][[/]]
- protocol用于指定传输协议,tcp或udp,默认tcp
如:
- EXPOSE 指令可以一次指定多个端口
- EXPOSE 11211/udp 11211/tcp
ENV
ENV用于指明容器环境中的环境变量。
引用环境变量时可以用 v a r i a b l e n a m e 或 者 variable_name或者 variablename或者{variable_name}
${variable_name}这种语法也被一些标准的bash解释器支持
- ${variable:-word} 如果变量已被赋值值,那么最终值将为此前设定的值,如果此前未赋值,则为此时赋的值
- ${variable:+word} 如果此前变量有值则覆盖,如果变量无值,则赋给它空字符串
ENV =
RUN
指定docker build过程中运行的程序
-
RUN
-
RUN ["","",""]
-
第一种格式中,通常是一个shell命令,且以"/bin/sh -c"来运行它,这意味着此进程在容器中的PID不是1,不能接受Unix信号,因此当使用docker stop 命令停止容器的时候,此进程收不到SIGTERM信号;
-
第二种语法格式中的参数是一个JSON格式的数组,executable是要运行的命令,是传递给命令的选项或参数。然而,此种格式指定的命令不会以/bin/sh -c来发起,因此常见的shell操作如变量替换及通配符替换不会进行,不过要运行的命令想依赖于shell特性的话,可以将其替换为类似下面的格式。
-
RUN ["/bin/bash","-c","",“param1”]
CMD
类似于RUN指令,CMD指令也可以用于运行任何命令或者应用程序,但是两者的运行时间点不同。
- RUN指令运行于映像文件构建过程中,而CMD指令运行于基于Dockerfile构建出的新映像文件启动第一个容器时
语法:
- CMD
- CMD [“executable”,"",""]
- CMD ["",""]
前两种语法格式的意义同RUN
第三种则用于ENTRYPOINT指令提供默认参数
ENTRYPOINT
类似CMD指令的功能,用于为容器指定默认的运行程序,从而使得容器像是一个单独的可执行程序。
与CMD不同的是,由ENTRYPOINT启动的程序不会被docker run 命令行指定的参数所覆盖,而且这些命令行参数会被当做参数传递给ENTRYPOINT指定的程序。docker run命令的–entrypoint选项的参数可以覆盖ENTRYPOINT指令指定的程序。
语法:
- ENTRYPOINT
- ENTRYPOINT [“executable”,"",""]
docker run 命令传入的命令参数会覆盖CMD指令的内容并附加到ENTRYPOINT命令最后作为其参数使用。
Dockerfile文件中可以存在多个ENTRYPOINT指令,但是仅有最后一个会生效。
RUN,CMD,ENTRYPOINT之间的区别:
- RUN:执行于docker build阶段,每个都有效
- CMD:执行于docker run阶段,仅最后一个有效
- ENTRYPOINT:执行于docker run阶段,仅最后一个有效
USER
用于指定运行image时的或运行Dockerfile中任何RUN、CMD、或ENTRYPOINT指令指定的程序时的用户名或者UID
默认情况下,container的运行身份是root用户。
语法:
- USER |
uid需要是在/etc/passwd中有效的用户UID
ONBUILD
用于在Dockerfile中定义一个触发器。Dockerfile用来build映像文件,这个映像也可以作为base image被另外一个Dockerfile用作FROM指令的参数,并以之构建新的映像文件,此时将会触发base image的Dockerfile文件中的ONBUILD指令定义的触发器。
ONBUILD不能自我嵌套,且不会触发FROM和MAINTAINER指令
使用包含ONBUILD指令的Dockerfile构建的镜像应该使用特殊的标签,如ruby:2.0-onbuild
在ONBUILD指令中使用ADD或COPY指令应该格外小心,因为新构建过程的上下文在缺少指定的源文件时会失败。
.dockerignore file
在docker cli发送内容给docker daemon时,他会先查找.dockerignore文件中的内容。如果存在则将.dockerignore文件中的内容排除在外。
Docker资源限制
docker提供了三种flag来管理内存、cpu、和设备资源,可以在docker run和dockeer create命令中使用:
-
内存限制
- -m或者–memory
-
CPU限制
- –cpu-shares
- –cpuset-cpus
-
devices
- –device
-
–ipc
-
–privileged
Docker Private Registry
可以通过docker-distribution创建私有仓库
yum install docker-distribution
更改docker-daemon服务,使其能够默认从私有仓库拉取而不是docker hub