虚拟化系统:
1. Type-I: 此种虚拟化是Hypervisor直接运行在硬件之上,来创建虚拟机.
2. Type-II: 这种虚拟化类似与VMware Workstations。
IPC: 在相同的名称空间中的进程才能通过IPC实现进程间通信。
PID: 在每个名称空间中必须有一个进程ID为1的进程,它是所有进程的父进程,即init进程,所以要实现用户空间隔离,就需要让每个用户空间中进程,以为自己是运行在init进程下的.
user: 要让每个用户空间中的进程以为自己是运行在独立的主机中,那就必须实现每个用户空间中都一个UID为0的用户, 但该用户在真实系统中,却是一个普通用户。
容器虚拟化:
资源限制:
通常有两种,一种是弹性百分比限制能使用全部资源的百分比.另一种是决对资源限制.
实现资源限制的是Cgroups(Control Groups):
1. blkio: 块设备IO
2. cpu: CPU
3. cpuacct: CPU资源使用报告
4. cpuset: 多处理器平台上的CPU集合
5. devices: 设备访问
6. freezer: 挂起 或 恢复任务
7. memory: 内存用量及报告
8. perf_event: 对cgroup中的任务进行统一性能测试
9. net_cls: cgroup中的任务创建的数据报文的类别标识符.
Docker、LXC、CNCF:
Docker是一家商业公司开发的容器技术,早期是容器内核是基于LXC(LinuX Container),来实现容器的创建和管理的。
LXC: 早期有一种技术叫 jail(监狱,或叫沙盒),这种技术可让一个应用程序运行在一个沙箱中,该应用程序无论做出任何对系统的破坏都不会影响到真实的系统,仅仅只是沙箱中的虚拟环境。后来这种技术在Linux也引入了,叫vserver,即现在我们比较熟悉的chroot功能。但是在Linux系统中早期内核支持名称空间,是需要通过自己编写代码,调用系统调用来创建出UTS,Network,IPC,等不同的名称空间,结合chroot实现在一个操作系统上创建出一个用户空间,但这对普通用户来说,是很困难的,而LXC则提供了一组工具,将这些功能整合成工具,用户若需要创建容器,只需要使用lxc-create等命令行工具,就可以很轻松的创建容器,但是LXC在创建容器时,它需要借助于template(模板),当允许lxc-create来创建一个容器时,你可以在CentOS的系统上,创建一个Ubuntu的容器,或FreeBSD的容器等,都可以只有你提供相应的template和这些系统的软件包安装源即可; 但LXC这种方式带来的问题是使用上依然非常困难,应用模板的创建并不容易,而且又产生了很多冗余数据;如:我想创建两个Ubuntu的容器,我就需要安装两份相同的数据; 而且若我需要将一个容器
迁移到另一台主机上,怎么做?LXC没有提供相应的工具。所以它并不适合大规模使用。Docker:它早期创建容器的内核是LXC,但它使用了比LXC更高效,更精巧的方式来创建容器,它不在使用模板来安装容器,而是将容器制作成镜像,当需要使用容器时,直接从镜像仓库中下载之前制作好的镜像到本地,直接启动容器即可。并且它的镜像采用一种叫做叠加镜像的方式解决数据冗余的问题,实际上它采用一种叫三层叠加镜像的方式来实现数据的高效利用 和 容器私有数据的保存。借助于共享存储来将容器的私有数据保存在宿主机的外部,当宿主机宕机时,只要在新的宿主机上下载镜像,启动容器,挂载数据即可恢复服务。具体如下图:
注:
Docker为了管理容器方便,它定义一个容器中只能运行一个进程, 但实际上容器跟虚拟机类似,它是可运行多个进程的,只是为了管理方便,限制容器中只能运行一个进程。
容器这种技术带来好处:
对于开发人员来说,是天大的好处,因为Docker的出现真正解决代码一次编写到处运行,无论底层是什么系统,只要能运行docker,将镜像做好后,直接编排好,然后在宿主机上启动容器即可。对于运维人员来说,带来的问题是,系统构架更加复杂,原本的调试进程的方式,在容器时代变的异常困难,因为容器中很可能没有各种调试工具等。
容器部署上的问题:
如NMP环境的构建上, 通常我们需要先部署MySQL容器,接着是PHP容器,最后是Nginx容器,这样的顺序部署才能让环境都正常工作,否则可能出现Nginx代理PHP服务器,结果找不到PHP服务器,因为容器启动后,它的IP地址会改变,那通过DHCP根据MAC不是可以固定分配IP吗?但其实是不可以的,因为容器是没有固定MAC的,它的网卡是虚拟的,是通过Linux上的"虚拟网线”创建的两个虚拟网卡,一端接入容器,一端接入Docker0(默认桥)桥上,这样就实现了容器能接入网络中,因此每次启动容器时,是无法知道这一次创建的虚拟网卡的MAC地址是多少的,因此必须使用其它方法来获取启动完成后的容器的IP地址,这也就是为啥容器需要编排,对于LNMP环境,Nginx要依赖PHP,因为它要代理PHP,而PHP又依赖MySQL,因为PHP应用程序要从MySQL中获取数据,因此必须先启动MySQL容器,这样才能获取MySQL容器当前使用的IP,接着启动PHP容器,将MySQL容器的IP传入到PHP容器中,这样PHP容器就可以知道MySQL的IP,而Nginx也是一样,这样编排后,LNMP环境中每个容器就都能获取它所依赖的容器的IP,就可以正常通信了。
为了解决部署容器的顺序问题,容器技术,必须结合编排工具一起使用,才能实现大规模应用。
目前比较知名的编排工具:
Docker: machine+swarm+compose,这三个工具配合使用来实现编排。
第三方提供的编排工具: mesos+marathon
Google: kubernetes-->简称k8s
libcontainer:
又称为RunC,它是Docker官方提供的新Docker构建容器的核心程序,由于LXC在功能上,没有与时俱进,因此Docker官方,最终决定自己开发一个新的容器构建核心,它就是runC。
Docker,Moby,CNCF的历史故事:
Docker我们知道它是一家商业公司,早期Docker刚出现时,以下轰动世界,为了让Docker技术,能够有更大的发展,Docker将自己的产品分成商业版和社区开源版,但是Docker的商业版在商业领域上始终没能活动 大佬们的信赖,没能得到太多融资,但是社区开源版确始终火热的不行,Docker为了能获得更多商业利益,它最终决定将社区版的Docker更名为Moby,希望能吸引更多关注的目光到它的商业版上,但事与愿违。 而此时Google发现自己秘密使用了十几年的容器技术竟然被Docker给开源了,而且还发展的如此红火,尽人皆知,但Google不希望自己在容器技术上的话语权落到Docker手中,它决定扶持一家小容器公司,但这家公司的产品在Google大力扶植下,依然是名落孙山,不是Docker的对手,最后,Google发现Docker在编排工具的开发上,好无建树,而自己拥有十几年的容器使用经验(Google内部的容器叫Borg(博格)),因此就组织了工程师开发了Kubernetes,又鉴于Docker一家公司为了商业目的,随意更改Docker社区版,改变Docker的性质,因此Google将Kubernetes捐给了CNCF,而CNCF是容器标准化基金会组织,它是Google联合多家
知名公司,如:IBM,微软,Facebook等联合成立的组织。向世人表态为不会左右Kubernetes的发展进程,加上Kubernetes从其诞生就是站在Bong的肩膀上成立的,所以它一经发布就成为世人聚焦的目标,短短3年,就已经从0.1发展到1.1版本,以每年4个版本迭代,2018年为止,其市场占有率达到80%以上.
OCI(Open Container Initiative):
它由Linux基金会主导于2015年6月创立,皆在围绕容器格式和运行时制定了一个开放的工业标准.
它制定了两个规范标准:
1. 运行时标准(The Runtime Specifications (runtime-spec))
2. 镜像格式标准(The Image Specification (image-spec))
OCF(Open Container Format):
OCF开放容器格式就是OCI的倡导下产生的标准。
Docker的基本架构:
注:
Docker的镜像:
Registry:在Docker中称为镜像仓库,Docker的镜像仓库有点类似与yum源仓库,但它的组织方式是这样的:
官方提供的Docker镜像仓库地址: hup.docker.org,当然国内也有很多docker镜像站.
Docker镜像仓库的组织格式为:
应用程序名:版本号
其中: 应用程序名为仓库名.
版本号: 为仓库中镜像名.也是它的tag标签号.
以Nginx为例:
Nginx:为仓库名.
nginx:1.15 这就是一个镜像名.同时一个镜像可能有多个名字,
比如: 1.15是Nginx的最新发行版,则还会给它提供一个名字叫: nginx:lasted 即最新版,
这个名字会随着Nginx发布的最新版而改变。
nginx:1.14 另外我们知道,Nginx的发行版中,次号码为偶数表示,此版本为稳定版,
为奇数则为开发版,所以这种版本通常也会有一个名字: nginx:stated 即稳定版的nginx镜像.
docker的下载站有很多:
若使用清华大学的repo仓库,下载其repo文件后,还需要修改其repo镜像的站点为清华大学的镜像站.
yum install docker-ce
安装完docker后,要下载其docker镜像,可使用国内的镜像加速器来下载:使用方式:
mkdir /etc/docker
vim /etc/docker/daemon.json
{ "registry-mirrors": ["https://registry.docker-cn.com"] }
查看docker版本信息:
docker version
docker info
docker的常用操作:
docker search :搜索docker镜像仓库中指定的镜像名.
docker pull : 下载指定镜像到本地.
docker images : 列出本地已有的镜像
下载docker镜像:
docker image pull nginx:1.14-alpine
注: alpine:这是一个小发行版的nginx,大小很小. 它仅集成了nginx运行所必须的环境,其它什么都没有,若你需要一些调试工具,它就不太适合了。
另外,一般官方发行的镜像,一般都很少会带调试工具。
另注: 我们搜索docker镜像时,会看到类似 tom/nginx 这类的镜像,它们是表示 tom这个人,在docker官方注册了账号,上传了自己制作的nginx镜像.
docker pull busybox
docker imgle ls --help #查看ls子命令的帮助.
docker imgle --help
创建容器:
docker container create #仅创建容器,但不运行.
docker container run #创建容器,并运行。
docker container start #启动一个或多个已经创建好的容器。
docker container stop |kill #都可以停在一个或多个容器.
docker container top #查看那些容器最消耗资源.
docker container run [options] Image [Command] [参数...]
options:
-t :启动镜像时,给其添加一个终端访问接口,以便能够启动后,连接登录.
-i :启动为交互模式.
--rm :停止后,直接删除该容器.
-d : 运行到后台.
--name <ImangeName> :为启动的容器名一个名字.
--network <NetName> :将容器加入一个指定的网络中.
docker network ls #可列出当前本机有多少网络可用.
ifconfig 可查看到,当docker安装完成后,它会自动创建一个docker0的桥,这个桥是NAT桥.
它是直接桥接在物理网卡上的,可直接和物理网络通信。
示例:
docker pull busybox #下载一个busybox的镜像.
docker container run -it --name b1 busybox:latest #这样就启动了一个叫b1的容器.
/ #ps #这样就启动了busybox,当前显示的是它的sh终端.
/ #mkdir /data/httpd
/ #vim /data/httpd/index.html
/ #httpd -f -h /data/httpd #在busybox中启动一个httpd进程,并指明其web根目录.
/ #exit #执行exit就退出了sh终端,也就终止了容器.
终端2:
docker ps
docker inspect b1 #可查看b1这个容器的详细信息.
curl http://1.1.1.1/ #这样就可以访问容器中的httpd服务了。
docker ps -a #可查看停止的容器.
示例2:
终端1:
docker start -a -i b1 #重新启动停止的容器b1
终端2:
docker ps
docker kill b1 #可强制关闭b1容器. 正常情况下应该使用stop,可保障数据不丢失.
docker rm b1 #删除停止的容器b1
示例3:
docker run --name nginx1 -d nginx:1.14-alpine #若本机能访问互联网,这里可直接运行,docker会自动下载镜像.
注:
在容器中运行应用程序,它必须工作在前台,否则容器已启动就会关闭,因为在容器中只有一个进程,该进程是支撑容器的骨架
docker ps
docker run --name redis1 -d redis:4-alpine #启动一个redis容器,它将自动下载该镜像.并启动.
示例4:
docker exec -it redis1 /bin/sh #在指定容器redis1上运行一个命令,/bin/sh,并使用交互式登录.
/data # ps #这样就可以登录到redis1这个容器上了.
docker logs nginx1 #查看容器nginx1的日志信息.
docker 所有状态转化示意
Docker的镜像:
Docker镜像包含了启动容器所需的文件系统及其内容,因此它用于创建并启动docker容器.
docker镜像采用分层构建的机制,最底层为bootfs,其上为rootfs.
bootfs: 用于系统引导文件系统,包括bootloader和kernel,容器启动完成后,bootfs会被卸载,以节省内存空间.
rootfs: 它在运行中的docker容器中变现为根文件系统.
如: docker exec -it nginx1 /bin/sh #登录后, ls / 可以看到全部的根文件系统目录.
传统OS:
系统启动时,内核挂载rootfs时,为确保不会误删除文件,通常会先以只读模式挂载,完整性自检完成后,再重新以读写模式挂载rootfs.
docker: 它在启动内核,挂载rootfs时,始终是以只读模式挂载, 而后在通过联合挂载技术额外挂载一个可写层.实现读写操作。
Docker Image Layer
此镜像显示了Docker的镜像层级,从上到下分别为:
可写层
add Apache 和 add emacs(类似于vim的编辑器): 这两层为自己通过yum安装在镜像中的工具.
Debian: 这是为安装的工具提供的基本的最小安装的根文件系统已共Apache能运行起来.这里使用的是Debian.还可以使用CentOS等.
bootfs: 这一层则是启动Debian这个rootfs所需的bootloader和kernel,通常在将Debian启动起来后,它将自动被卸载.
Docker实现叠加挂载镜像的文件系统:
overlayfs: 从kernel3.18以后被整合到Linux内核中了. 它叫 叠加文件系统, 再早期Docker实现叠加挂载是使用Aufs(Advanced multi-layered unification filesystem:高级多层统一文件系统), 而此文件系统是2006年开发,但因代码质量问题,一直未被整合到kernel中; 据说它的代码有3万行,而ext4这种文件系统总代码量4千多行。所以早期要使用docker有两种方式 一种手动给内核打aufs的补丁,另一种就是使用Ubuntu系统,因为它比较早的将Aufs整合到它的kernel中了。不过现在目前 CentOS系统已经能支持overlayfs的第2版了,所以可以直接使用docker即可.
docker info
#输出中Storage Driver: overlay2 ,overlay2是一种虚拟抽象层的文件系统,它是建构在Backing Filesystem: xfs ,即后端文件系统xfs之上的文件系统。
docker pull quay.io/coreos/flannel:v0.10.0-amd64
#下载quay.io 这个网站的coreos名称空间下的flannel仓库中的tag为v0.10.0-amd64 的镜像。默认docker使用https去下载. quay.io是docker镜像很有名的第三方提供者.
示例1:
#基于busybox创建一个测试镜像:
docker run --name bx1 -it busybox
/ #mkdir -p /data/html
/ #vi /data/html/index.html
<h1>Hello busybox HTTP Server</h1>
终端2:
docker commit -p bx1 #-p是让bx1容器先暂停,避免有人访问镜像,导致镜像创建不完成.
docker image ls #可看到刚创建的镜像.
docker tag <IMAGE ID> tom/bbox:v0.1 #这样就给指定的镜像打上了tom/bbox的标签,并且tag为v0.1s
docker tag tom/bbox:v0.1 tom/bbox:latest #在tom/bbox:v0.1上再给其创建一个标签名.
docker image rm tom/bbox:latest #删除一个标签名, 它只是删除标签,就像删除硬链接,文件是不会被删除的。
示例2:
docker inspect nginx:v1.14 #查看其CMD,可以看到的其被docker启动后,容器中将启动的第一个进程为nginx
docker inspect tom/bbox:latest
示例3:
#基于busybox制作一个启动后运行busybox中的httpd的镜像.
终端1:
docker run --name bx1 -it tom/bbox:latest #注:这是基于示例1中的内容创建的.
终端2:
docker commit -a "tom <tom@test.com>" -c ‘CMD ["/bin/httpd","-f","-h","/data/html"]’ -p bx1 tom/bbox:v0.2
注:
CMD: 这是仅修改启动容器后,执行什么命令, 注意CMD必须大写!! 其次参数的顺序不能乱.
-p : 制作时让容器处于暂停状态.
制作完成后,直接给它加一个标签名为tom/bbox:v0.2
docker image ls #查看制作好的镜像
docker run --name bx2 -d tom/bbox:v02
终端3:
docker inspect bx2 #查看bx2的定义,主要看CMD定义
curl http://1.1.1.3/ #这样就可以直接访问busybox启动的微型httpd了。
示例4:
#上传镜像到docker hup中的仓库中.
1. 要创建docker hup的登录账户,并登录到后台,创建一个仓库.
如: 上面创建的镜像名为: tom/bbox:v0.1
那创建的仓库就叫 tom/bbox
2. 上传镜像前,要先登录
docker login -u tom #然后要求输入密码,Login Succeeded表示登录成功.
3. 开始上传镜像:
docker push tom/bbox #不加tag则表示将本地所有镜像都推送上去。
示例5:
#导出已有镜像,并在其它主机上导入镜像:
docker image ls
docker save -o myimages.gz tom/bbox:v0.1 tom/bbox:v0.2 #这样即可将两个镜像打包成myimages.gz。
#登录到另一台docker上就可以执行:
docker load -i myimages.gz #就可以导入镜像了
docker image ls
示例6:
docker exec -it --name bx1
/ #wget -O - -q http://1.1.1.2/ # 假如Nginx容器启动后的地址为1.1.1.2,则通过wget测试访问.
-O :加载完成页面后,不保存,直接输出当标准输出."-"
Docker的四种网络模型:
第一种是Closed Container(封闭式容器): 它主要是用来处理一些Docker计算任务的,完成后就自动退出了,因此它无需网络协议栈.
第二种是Bridge Container(桥接式容器): 它是通过创建一根虚拟网线,一头接入容器中,也就是Container Virtual interface,另一头接在Docker bridge Virtual Interface上.
第三种是Joined Container(联合容器): 它是让A和B两个容器都共享一组Net,UTS,IPC名称空间,这样的好处是A和B可以直接通过lo接口通信,并且还可以桥接到Docker桥上,共享网络接口。
第四种是Open Container(开放容器): 它是第三种的扩展,它直接与物理机共享了Net,UTS,IPC名称空间,因此物理机上的网络接口它都可以看到,并且可以直接管理。
示例1:
#inspect 查看docker组件的详细信息.
docker network ls
docker network inspect bridge #查看bridge网络的详细信息。
示例2:
#创建网络模型3
终端1:
docker run --name bx1 -it --rm busybox
/ # wget -O - -q http://127.0.0.0.1/
终端2:
docker run --name bx2 -it --network container:bx1 --rm busybox
/ #echo "hello world." > /tmp/index.html
/ #httpd -f -h /tmp
示例2:
vim /etc/docker/daemon.json #以下仅供参考,新版本有些参数以不支持!!
{
"bip": "1.1.1.1/24", #设置docker0的IP
“fixed-cidr”:"10.20.0.0/16",
"fixed-cidr-v6":"2001:db8::/64",
"mtu":"1500",
"default-gateway":"10.20.1.1",
"default-gateway-v6":"2001:db8:dbcd::89",
"dns":["10.20.1.2","8.8.8.8"],
"hosts":["tcp://0.0.0.0:2375","unix:///var/run/docker.sock"] #启动docker监听在2375端口上.
}
systemctl restart docker #新版本建议直接修改启动脚本,已参数形式传入
在主机2:
docker -H 1.1.1.1:2375 image ls #这样就可以查看1.1.1.1这台主机上docker的是否有镜像了。
示例3:
#创建docker网络
docker network create -d bridge --subnet "1.1.1.0/16" --gateway "1.1.1.1" mybr0
docker run -it --name bx1 --net mybr0 busybox:v0.1
示例4:
#启动容器时传递参数:
docker run --name t1 -it --network bridge -h t1.test.com --rm busybox:latest
docker run --name t1 -it --network bridge -h t1.test.com --dns 114.114.114.114 --rm busybox:latest
docker run --name t1 -it --network bridge -h t1.test.com --dns 114.114.114.114 --add-host www.test.com:1.1.1.1 --rm busybox:latest
示例5:
docker run --name myweb --rm -p 80 tom/bbox:v0.2 #创建一个地址映射,允许外部访问此容器.
#注: -p 80 ,实际是创建了一条 iptables DNAT规则.
docker port myweb #查看docker创建的端口映射
docker run --name myweb --rm -p 1.1.1.1::80 tom/bbox:v0.2
docker run --name myweb --rm -p 80:80 tom/bbox:v0.2
docker run --name myweb --rm -p 1.1.1.1:80:80 tom/bbox:v0.2
Docker的数据卷(Data Volume):
为避免关闭再重启容器,其数据不受影响,但删除Docker容器,则其更改将会全部丢失.
存在的问题:
1. 存储于联合文件系统中的数据,宿主机不易访问.
2. 容器间数据共享不方便.
3. 删除容器其数据会丢失.
解决方案:"卷(Volume)":
卷是容器上的一个或多个目录(即:多个卷),此类目录可绕过联合文件系统,与宿主机上的某目录绑定(即关联)
Docker有两种类型的卷:
1. Bind mount volume(绑定挂载卷):这是有用户自定义指定docker中的某目录挂载到宿主机的某个指定目录.
2. Docker-managed volume(docker管理的卷): 这是由docker自己维护,用户需要在/etc/docker/daemon.json中指定启用docker自动启动容器时,关联宿主机上由docker默认自动创建一个目录,并与容器中指定目录关联的方式。
示例:
#由docker管理的卷:
docker run --rm --name b2 -it -v /data busybox:latest
终端2:
docker inspect b2 #可查看其“Mounts”段,其中有说明容器中/data 目录与宿主机上的那个目录建立的关联.
接着可测试在宿主机上目录中创建文件,在容器中编辑,是否可行。
示例2:
docker run --name b2 --rm -it -v /data/volumes/b2:/data busybox:latest
终端2:
echo “Hello Container busybox.” >> /data/volumes/b2/index.html
#接着在容器中查看.
# 再尝试删除容器,再关联相同目录,查看数据是否依然可访问。
docker inspect -f {{.Mounts}} b2 #简单过滤.
#注:
JSON中使用"[]": 表示这是一个列表, {}: 表示这是一个关联数组(也叫Hash映射),它可嵌套.
在关联数组中的格式:
{
"Key":"Value", #每个键 值对要使用 逗号分隔.
"Key":"Value",
"key0": {
"key1":"Value1", #key1的上层是“key0”, 而key0上层是 根(通常用"."表示.)
..
},
..
}
docker inspect -f {{.Mounts}} b2
#最外层{} 为引用格式, 内层{.Mounts} 为引用数组中.Mounts
. :表示根,.Mounts:表示根下的Mounts.
docker inspect -f {{.NetworkSettings.IPAddress}} b2 #这是简单的Go模板,go模板还支持循环,判断等.
示例3:
#创建一个基础容器,让其它容器共享它的UTS,IPC,Network名称空间 已经它的卷映射.
终端1:
docker run --name infraCon -it -v /data/volumes/b2:/data/web/html busybox
/ #ifconfig
终端2:
docker run --name nginx --network container:infraCon --volumes-from infraCon -it busybox
/ #ifconfig
终端3:
docker inspect infraCon
docker inspect nginx #查看“Mounts” 和 Networks是否与上面的一样。
Dockerfile:
格式:
# : #号开头为注释.
FROM : 第一个指令必须是FROM,它用来指明,依据那个基础镜像做镜像。
一般指令建议使用大写以区分参数, 但指令不区分大小写.
格式:
FROM <仓库名>[:<tag>] 或 FROM <仓库名>@<Hash摘要值>
使用dockerfile制作镜像的注意事项:
1. 必须把镜像文件都放到一个指定目录中, docker build在打包时,只打包该目录中的文件.超出此目录的文件将不会被打包到镜像中.
2. .dockeringore : 这个隐藏文件在指定目录的中,用于设定打包时排除的文件列表,可使用通配符。
3. docker build 在进行镜像打包时,它实际上类似于通过启动一个容器,然后,在容器中运行一些命令,创建一些文件,这些数据都保存在可写层中, 再将可写层打包到镜像中一样,不过,docker build是隐藏式启动基础镜像来自动完成这些操作,所以,需要知道你要使用某些命令,这些命令必须在基础镜像中存在才能使用。
4. 变量替换:
变量格式: $VarName 或 ${VarName}
变量替换: ${VarName:-String} #若VarName为空或未定义,则使用String做为值. 否则使用VarName的值.
${VarName:+String} #若VarName有值,则使用String替换该值.
5. dockerfile中的指令:
1. copy <Src> <Dest> 或 COPY ["<Src>",..."<Dest>"] #注:Src若为目录,复制时,是仅复制目录中文件,而不复制目录本身的.
示例:
mkdir /dockerfile/
cd /dockerfile
vim Dockerfile #注: 文件名首字母必须大写.
#注: Dockerfile中的每条指令都是一层,层数越多挂载效率越差!!
#Description: test image
FROM busybox:latest
MAINTAINER "Tom <tom@test.com>"
# LABEL maintainer="Tom <tom@test.com>" #功能和上面一样.
COPY index.html /data/web/html/ #需要将index.html放到/dockerfile中
COPY yum.repos.d /etc/yum.repos.d/ #注: 先将yum.repos.d 复制党/dcokerfile中, 然后在写此命令.
ADD http://nginx.org/download/nginx-1.15.2.tar.gz /usr/local/src/ #它会自动在打包时下载该URL指定的文件.
ADD nginx-1.15.2.tar.gz /usr/local/src #打包时,会自动将tar.gz展开放入/usr/local/src/的文件.
WORKDIR /usr/local/src #将它做为工作目录.
ADD test.tar.gz ./ #这里引用的相对路径就是WORRDIR所设定的目录.
WORKDIR /etc/yum.repos.d/
ADD yum.repos.d.tar.gz ./ #这里的相对路径就是/etc/yum.repos.d/了。
#VOLUME ["/dir_1", "/dir_2" ..] #设置容器挂载主机目录
VOLUME /data/mysql/ #指定Docker自动管理的卷。
#EXPOSE <Port>/<Protocol>
EXPOSE 80/tcp
#指定要对外暴露TCP的80端口,但这里指定暴露,实际并不会直接暴露,而是需要在启动容器时,使用 “-P” 才会真正暴露此端口给外部用户.
# docker port 容器名 #在不加"-P"时,可使用它来验证是否暴露了80端口.
#ENV <KeyName> <Value> #此格式定义的变量,会将空格后面的所有字符串都当做keyname的值.
#ENV <Key>=<Value> <Key2>=<V2> ... #此格式可创建多个环节变量. 若值中有空格,需要用""转义, 或用引号引起来.
ENV DOC_ROOT /data/web/html
COPY *.html $DOC_ROOT
COPY *.jsp ${JSP_ROOT:-/data/web/jsp/} #注:这些环境变量,是可以在启动容器时,指定"-e JSP_ROOT=/data/jsp" 来修改的.
#RUN <Command1> && <Command2> ... #RUN执行命令是在docker build时执行命令.
WORKDIR /usr/local/src
ADD http://test.org/test.tar.gz ./
RUN cd ./ &&
tar xf test.tar.gz #仅做多行写命令的测试
#RUN ["<Executable>","<Param1>",....]
#RUN的第二种语法格式.它使用JSON数组,Executable为要运行的命令,后面为命令的参数,可有多个.此格式启动的命令不会以/bin/sh -c 的方式启动,因此它不能使用sh的命令语法,如:变量替换,通配符等. 若想使用shell特性的话,可用以下方式:
RUN ["/bin/sh","-c","<Executable>","Param1",...]
注:
RUN 和 CMD 及 ENTRYPOINT 若使用中括号来表示数组时,其中不能使用单引号.必须使用双引号.
docker build --tag tinyhttpd:v0.1 ./
测试:
docker run --name tinyweb1 --rm tinyhttpd:v0.1 cat /data/web/html/index.html
docker run --name tinyweb1 --rm tinyhttpd:v0.1 ls /etc/yum.repos.d/
#启动镜像时,传递参数:
Dockerfile:
FROM java:8
ADD microsoft.jar /root
ENV PARAMS=""
ENTRYPOINT ["sh","-c","java $PARAMS -jar /root/microsoft.jar"]
测试:
docker run -d -e PARAMS="-Dserver.port=8080" -p 2000:8080 镜像名称
Docker私有仓库的构建:
1. 安装docker的仓库构建程序包.
yum install docker-distribution
2. vim /etc/docker-distribution/registry/config.yml
services: registry #表示运行的服务为registry
layerinfo: inmemory #表示缓存在内存中.
addr: :5000 #没有指定地址,表示使用0.0.0.0:5000
3.启动服务:
systemctl start docker-distribution
4. 给自己做的镜像打上自己的仓库地址的标签.
docker tag myweb:v0.3.11 node2.test.com:5000/myweb:v0.3.11
5. 将自己打好标签的镜像push上去.
docker push node2.test.com:5000/myweb:v0.3.11 #这里会报错,说我们使用了不安全的http.
6. 若我们是内网使用, 不想使用HTTPS,可修改配置文件为:
vim /etc/docker/daemon.conf
{
...
"insecure-registries": ["node2.test.com:5000"]
}
systemctl restart docker
7. 再次push
8. 可到其它节点上测试下载.
docker pull node2.test.com:5000/myweb:v0.3.11
Docker的资源限制:
Linux Capabilities: Linux Kernel相关的知识
Memory:
OOM: 对于比较重要的容器,在启动时就要调整其OOM_adj, 其值调整越小, OOM_score的评分就越低,当内核发现内存不足,发生OOM时,就优先将OOM_score得分高的优先kill掉.
默认: docker daemon会自动将自己的OOM_adj调整很低,避免内核发生OOM时,被内核kill掉.
可调整容器使用内存的限制:
-m | --memory= #限制容器能使用的物理内存大小.单位:k, m, g; 若设置一个容器可用内存为4m,若容器使用超过,将被kill掉.
--memory-swap= #要使用它,必须先使用 -m 选项,否则它不生效.
若设置: -m 7G --memory-swap=10G, 则表示Swap+Ram=10G, Swap=10-7=3G,即可使用3G的Swap内存.
若设置: -m 7G --memory-swap=7G, 则表示不使用Swap内存.即 若设置这两个参数值相同,表示禁用SWAP.
若设置: -m 7G --memory-swap=0, 则表示SWAP未设置, 则若DockerHost启用了SWAP,则表示容器可用的SWAP大小=2*RAM的大小.
若设置: -m 7G --memory-swap=-1, 则表示DockerHost上启用了Swap,则容器可使用的全部的SWAP大小.
注:
free : 在容器中使用free看到的swap空间使用情况并非上面这些参数计算出来的实际情况, 也就是说free查看容器使用的内存空间是非常不准确的。
--memory-swappiness: 设置容器使用SWAP内存的倾向性. 值的范围: 0~100, 0:能不用就不用, 100:只要能需要就使用.
--memory-reservation: 设置要预留多少内存空间。
--oom-kill-disable 设置当发生OOM时,是否禁止此容器被kill掉,它要可-m一起使用.
CPU:
默认所有容器使用CPU资源都没有限制。
--cpu-shares #CPU是可压缩性资源,若当前有A,B,C三个容器, 假如分配给他们CPU占用的比例为:1024:512:2048.
则表示,将CPU资源分2+1+4,即分成7份, 三个容器都需要CPU,则A使用2份CPU时间, B使用1份
CPU时间, C使用4份CPU时间, 若B和C当前都空闲,则A可使用全部CPU资源; 若此时A和B都需要
用CPU资源,则按照A使用2份CPU资源, B使用1份CPU时间, 若又新加了D,则再重新分割CPU时间片.
--cpus= #限制单个容器最多可使用几核CPU, 即: 假如有4个核心, 还是A,B,C, 若此时B和C都不使用CPU
在不限制的情况下,A可使用4个核心,即最大可使用400%的计算能力.若设置了单个容器的
最多可使用CPU核心数,则基本当前所有容器都不用CPU,你也只能使用限制个数的核心数.
--cpus=1.5 #表示最多可用150%,注意容器最多可使用150%的CPU计算能力,指的是
只有能限制它最多使用这么多计算能力即可,至于容器在那个CPU核心上
运行无所谓,随机分配,分到那个核心上就在那个核心上运行。但总量是不会超过150%的。
--cpuset-cpus= #这是限制容器能够运行在那个CPU核心上的, 假如:有4核心, 则用0~3来表示4个核心的编号.
--cpuset-cpus=0,1 # 则表示容器只能运行在核心0和1上,其它上面不行。
--cpu-period= #限制最多可用CPU的时间.
--cpu-quota=
以上这些选项在docker run 时可以设置。
docker压测工具:
docker pull lorel/docker-stress-ng
docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress --help #可查看帮助.
docker run --name stress -it --rm -m 256m lorel/docker-stress-ng:latest stress --vm 2 #限制其最多使用256M内存.
docker run --name stress -it --rm --cpus 2 lorel/docker-stress-ng:latest stress --cpu 8 #限制最多使用2个CPU核心的计算资源.
终端1:
docker run --name stress -it --rm --cpu-shares 1024 lorel/docker-stress-ng:latest stress --cpu 8
终端3:
docker run --name stress -it --rm --cpu-shares 512 lorel/docker-stress-ng:latest stress --cpu 8
终端2:
docker top stress #查看容器进程数
docker stats #查看docker资源的消耗情况。