基本概念
镜像的概念
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
镜像的分层存储结构
Docker 设计时,就充分利用 Union FS 的技术,将镜像设计为分层存储的架构。镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。下图可以和明显的看到镜像的分层结构
分层结构的特点
<1>共享宿主机的kernel
<2>base镜像提供的是最小的Linux发行版
<3>同一docker主机支持运行多种Linux发行版
<4>采用分层结构的最大好处是:共享资源
<5>Copy-on-Write 可写容器层
<6>容器层以下所有镜像层都是只读的
<7>docker从上往下依次查找文件
<8>容器层保存镜像变化的部分,并不 会对镜像本身进行任何修改 l
<9>一个镜像最多127层
在传统的Linux操作系统内核启动时,首先挂载一个只读的rootfs,当系统检测其完整性后,将其切换成读写(read-write)模式。
docker 沿用了传统操作系统的rootfs的挂载方式,在docker daemon启动之后,rootfs处于docker文件系统的最顶层,并且只是只读模式;挂载完毕后利用联合挂载技术在已有的rootfs文件系统上再挂载一个读写层,这样对于用户来说可读可写的文件系统就可以完全展示出来。
docker镜像系统的分布:
namespace
读写层
初始化层
只读层
镜像的构建
docker commit 构建新镜像三部曲
(1)运行容器 ——docker run -it --name test busybox
(2)修改容器 —— echo helloworld > testfile
(3)将容器保存为新的镜像——docker commit test test:v1
缺点:
效率低、可重复性弱、容易出错
使用者无法对镜像进行审计,存在安全隐患
一、搭建简单的2048游戏
1.安装docker
[root@server1 docker]# ls
[root@server1 docker]# yum install -y *
[root@server1 docker]# yum install -y bash-*
[root@server1 docker]# systemctl start docker
[root@server1 docker]# docker info
[root@server1 ~]# ls
docker game2048.tar
[root@server1 ~]# docker load -i game2048.tar
[root@server1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game2048 latest 19299002fdbe 2 years ago 55.5MB
[root@server1 ~]# docker run -d -p 80:80 --name vm1 game2048
d2d783749852a70cab902cb2fa44d4de32188dd25e38f422cfa566da433481d7
[root@server1 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d2d783749852 game2048 "/bin/sh -c 'sed -i …" 9 seconds ago Up 8 seconds 0.0.0.0:80->80/tcp, 443/tcp vm1
二、搭建ubuntu(具有交互功能的容器)
1.导入镜像
[root@server1 ~]# ls
docker game2048.tar nginx.tar ubuntu.tar58
[root@server1 ~]# docker load -i ubuntu.tar
56abdd66ba31: Loading layer 196.8MB/196.8MB
9468150a390c: Loading layer 208.9kB/208.9kB
11083b444c90: Loading layer 4.608kB/4.608kB
5f70bf18a086: Loading layer 1.024kB/1.024kB
Loaded image: ubuntu:latest
[root@server1 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
game2048 latest 19299002fdbe 2 years ago 55.5MB
ubuntu latest 07c86167cdc4 3 years ago 188MB
[root@server1 ~]# ifconfig
[root@server1 ~]# docker run -it --name vm2 ubuntu
root@3800b84ec229:/# ls
root@3800b84ec229:/# ip addr #可以查看到ip
crtl pq退出
知识点:
1.ubuntu有外部yum仓库,不需要搭建yum源,需要软件可以直接下载
2.-it 表示获取一个交互页面 ,给容器起一个名字,这一个容器不需要端口影射
3.Docker和物理机共享一个内核,可以看到容器和宿主机使用的是同一个内核
4.Docker不能做到完全隔离,他的安全性不如kvm,但性能接近物理机
注意:有些应用不能改变内核
5.crtl + pq可以退出容器但进程还存活,也就是说退出之后docker ps可以查看到这个容器的进程,crtl + D 退出并关闭容器,下次进入容器是需要开启
6.docker inspect containername 可以查看到一个容器的具体信息
7.容器的ip是变更的,不是静态分配的,他是单调递增的ip
8.容器的网络默认使用桥接
9.容器之间使用docker来做桥接的,来做虚拟网络的配置
10.容器的ip能出来但是外部网络进不来,进来的话要做端口转发DNATS
验证:4.查看到桥接的状态
[root@server1 ~]# yum install -y bridge-utils
[root@server1 ~]# brctl show
[root@server1 ~]# sysctl -a | grep ip_forward
##内核路由开启
[root@server1 ~]# iptables -t nat -nL
[root@server1 ~]# docker inspect vm2
[root@server1 ~]# docker inspect vm1
1.启动并连接vm2,创建10个文件后退出(不停止后台运行)
[root@server1 ~]# docker start vm2
vm2
[root@server1 ~]# docker attach vm2
root@3800b84ec229:/# ls
root@3800b84ec229:/# touch file{1..10}
root@3800b84ec229:/# ls
[root@server1 ~]# docker ps
2.再次连接,可以查看到刚刚创建的文件,这次直接结束容器(crtl+D)
[root@server1 ~]# docker start vm2
vm2
[root@server1 ~]# docker attach vm2
root@3800b84ec229:/#
root@3800b84ec229:/# ls
[root@server1 ~]# docker ps
3.删除容器vm2,可以看到镜像还在
[root@server1 ~]# docker rm vm2
vm2
[root@server1 ~]# docker images
[root@server1 ~]# docker run -it --name vm2 ubuntu
root@beede5849ddb:/# ls
[root@server1 ~]# docker history ubuntu
[root@server1 ~]# docker attach vm2
root@beede5849ddb:/#
root@beede5849ddb:/# ls
root@beede5849ddb:/# touch file{1..10}
root@beede5849ddb:/# ls
root@beede5849ddb:/# exit
6.查看进程,保存当前容器的镜像,查看到多了一个镜像,查看新镜像的层数
[root@server1 ~]# docker ps -a
[root@server1 ~]# docker commit -m "add files" vm2 ubuntu:v1
sha256:bcf24cdac0e119f7e388967b870abfb08fc7de507bd7a83eaf6a37bdcd394715
[root@server1 ~]# docker images
[root@server1 ~]# docker history ubuntu:v1
7.删除容器vm2,运行刚刚保存的镜像为新的容器vm2,可以查看到文件
[root@server1 ~]# docker rm vm2
vm2
[root@server1 ~]# docker run -it --name vm2 ubuntu:v1
root@15f61e51dbb9:/# ls
root@15f61e51dbb9:/# rm -f file*
root@15f61e51dbb9:/# ls
root@15f61e51dbb9:/# exit
删除文件后强制退出
8.保存当前容器为新的镜像Ubuntu:v2,查看镜像的信息,删除刚刚的容器vm2,运行Ubuntu:v2镜像为新的容器vm2,查看镜像的层数
[root@server1 ~]# docker commit vm2 ubuntu:v2
[root@server1 ~]# docker images
[root@server1 ~]# docker rm vm2
vm2
[root@server1 ~]# docker run -it --name vm2 ubuntu:v2
root@0ea79df79767:/# ls
root@0ea79df79767:/# exit
[root@server1 ~]# docker history ubuntu:v2
1.查看镜像
[root@server1 ~]# docker images
2.删除镜像
[root@server1 ~]# docker rmi ubuntu:v1
Untagged: ubuntu:v1
[root@server1 ~]# docker rmi ubuntu:v2
[root@server1 ~]# docker rm vm2
vm2
[root@server1 ~]# docker rmi ubuntu:v2
[root@server1 ~]# docker images
总结:
(1)容器在Crtl+pq退出后会保存当前的状态
(2)docker ps -a比docker ps 查看到的进程更全面,-a可以显示不活跃的容器,也就是说他既能显示运行中的容器又能显示已经关闭的容器
(3)在容器中搭建好一个环境之后可以将容器 直接保存为一个镜像方便以后直接使用
(4)两个镜像的内容相同不代表两者的结构相同,容器的镜像是以层来划分的,层数越少性能越优,他会逐层从上往下叠加
(5)容器的名字仅仅是一个别名,两个名字一样的容器可以是完全不同的容器
(6)容器的运行相当一个进程,消耗的资源少,但是创建虚拟机使用的是宿主机资源
(7)镜像保存的方式镜像名称:版本
(8)必须把容器提交成镜像才能保存数据
(9)镜像是从上往下读,最新的修改会有一个最新的配置
(10)层数不能过多 在127层以内
(11)尽可能的减少镜像的层数,容器就相当于是一个进程在运行,对资源消耗比较少
(12)分层的好处 :共享机制;
v2中包含了v1和原始的操作系统,只保留一个也可以使用;
代码重用 不便于审计;