
本章目录:
0x02 Docker 核心概念
1.镜像 [image]
2.容器 [Container]
3.仓库 [Repository]
描述:Docker的三大核心概念镜像/容器和仓库, 通过三大对象核心概念所构建的高效工作流程;
描述:images 类似于虚拟机镜像,借鉴了Git利用分成分层优点,通过文件系统分层的概念实现了分层复用,极大的节约了磁盘空间;简单的您可以将它理解成为一个面向Docker引擎的只读模板包含文件系统;
镜像是创建Docker容器的基础,然后通过版本管理和增量的文件系统;
用户基于镜像来运行自己的容器,镜像是基于 Union 文件系统的层式结构;
Docker运行容器前本地必须存在对应的镜像,如果不指定镜像名将会默认自动采用Docker Hub公共注册服务器仓库中该名称的镜像; Docker 镜像使用帮助:https://lug.ustc.edu.cn/wiki/mirrors/help/docker
基础知识:
1.镜像名称格式:Image hub address/Namespace/Repository:tag即hub.weiyigeek.top/Test//alpine-apps:1.0
2.典型Linux启动需要运行两个FS即:Kernel > Bootfs > Rootfs
3.镜像的分层结构: 新镜像是从Base镜像一层一层叠加的,每安装一个软件就在现有镜像基础上增加一层;
镜像操作常用命令:
docker search --no-trunc=false [镜像名称] #搜索关于Archlinux镜像,输出信息不截断显示
#仓库名(Repository) 或者 标签名[不指定着默认latest,即最新]
docker pull name/[repository[:tag]] #获取Hub镜像如果不指定TAG将默认选择仓库中最新颁布的镜像
docker push DockerHubUser用户/test:latest #上传到docker仓库
docker images #列出本机已有镜像
docker tag 原仓库[:标签] 新仓库名[:标签] #为本地镜像添加一个新标签 [注意仓库名称必须小写]
docker tag <image id> username/name:devel #修改镜像的标签
docker inspect [image id] #获取镜像的详细信息
docker inspect -f {{".Architecture"}} 550(images Id 前面3位) #-f 获取单个属性 返回JSON
docker rmi [<image id>|<repository> ] #删除镜像 镜像id|仓库名称
docker rmi -f <images id> #不建议强行删除正在容器中运行的镜像文件
docker save -o 保存文件名.tar [repository[:tag]] #将镜像文件打包存出到磁盘
docker save [repository[:tag]] > 保存文件名 #将镜像文件打包存出到磁盘
docker load --input 保存文件名.tar #将打包的镜像文件进行载人
docker load < 保存文件名.tar
运行案例:
$ sudo docker pull ubuntu:16.04 #下载14.04 tag的镜像
Using default tag: latest
latest: Pulling from library/ubuntu
#从下面可以看出镜像文件一般是由若干组成,行首f476d66f5408代表了各层的ID,下载过程中会获取并输出镜像各层信息
#层(Layer)其实是AUFS(Advanced Union File System联合文件系统)重要概率是实现增量保存于更新的基础
f476d66f5408: Pulling fs layer
8882c27f669e: Pulling fs layer
d9af21273955: Pulling fs layer
f5029279ec12: Waiting
$ sudo docker pull registry.hub.docker.com:5000/ubuntu #从指定长仓库进行下载
$ sudo docker run -t -i Ubuntu /bin/bash #利用dockerubuntu镜像创建一个容器 仓库后面是接的命令
root@634e7ed26d76:/# w
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
03:34:47 up 1:59, 0 users, load average: 0.00, 0.05, 0.06
root@634e7ed26d76:/# cat /etc/os-release
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
$ sudo docker images #列出镜像
#其中Image id的信息十分重要,它唯一标示了镜像,Tag信息来区分发行版本(如14.04,16.04)
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest d131e0fa2585 9 days ago 102MB
centos latest 9f38484d220f 7 weeks ago 202MB
#REPOSITORY (来自哪个仓库) TAG (镜像的标签信息) IMAGE ID (镜像的ID号,唯一) CREATED [Created:建立时间] VIRTUAL SIZE (镜像大小)
$ sudo docker tag ubuntu:latest ubuntu18.04:latest #修改镜像名称使用该方式进行添加新标签原来的不会被删除
$ sudo docker tag d131e0fa2585 ubuntutls:latest
$ sudo docker images
#更改后镜像的ID是完全一致的,其实都指向了同一个镜像文件相当于别名而已;
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntutls latest d131e0fa2585 9 days ago 102MB
ubuntu18.04 latest d131e0fa2585 9 days ago 102MB
$ sudo docker inspect d131e0fa2585 #获取该镜像的详细信息
$ sudo docker inspect -f {{".Architecture"}} d131 #获取系统架构 通常使用镜像ID的前三位
amd64

当该镜像在容器运行存在的时候,镜像文件默认是无法被删除的;必须停止/删除容器ID才能删除镜像文件;
#当同一个镜像有多个标签的时候rmi命令只是删错了该进行的标签而且,并不影响镜像文件
#但当只剩下一个标签的时候就要小心了,如果在停止的状态下再次使用rmi命令则会删除该镜像
$ sudo docker rmi ubuntu18.04
Untagged: ubuntu18.04:latest
$ sudo docker rmi ubuntu ubuntutls
Untagged: ubuntu:latest
Untagged: #删除了这个镜像文件的所有AUFS层
ubuntu@sha256:d26d529daa4d8567167181d9d569f2a85da3c5ecaf539cace2c6223355d69981
$ sudo docker rmi -f ubuntutls #强制删除
Untagged: ubuntutls:latest
Deleted: sha256:d131e0fa2585a7efbfb187f70d648aa50e251d9d3b7031edf4730ca6154e221e
创建镜像有三种办法:
1.从已有镜像的容器创建
2.基于本地模板导入:使用操作系统模板导入一个镜像文件;这里推荐使用OpenVZ提供的模板来常见
3.基于Dockerfile导入
######### 从已有镜像创建 ############
$sudo docker commit [option-选项] ContainerId [Repository[:Tag]]
$sudo docker commit -m "xx" -a "oo" ContainerID(被修改过的ID) [Repository[:Tag]]
# -a,--author="作者"
# -m,--message="更改信息"
# -p,--pause=ture 提交时暂停容器Container运行
$sudo docker run -it centos:latest /bin/bash
[root@32a481e170c6 ~]$ touch {1..10}.txt #上面这个容器ID非常重要在进行修改之后
$sudo docker container ls -a #查看容器记录
#创建一个新的镜像
$sudo docker commit -m "Zabbix base in Centos7" -a "Weiyigeek" 32a481e170c6 centoszabbix:latest
sha256:680ddb57c4b80c625ef68e113f553ee932a06f25d4685d25a0b6464cf5d60982 #成功会给出一个镜像ID
######### 从本地模板导入 ############
$sudo cat ubuntu-14.04.tar.gz | docker import - ubuntu:14.04 #本地导入镜像命令
存出和载人镜像并上传镜像到DockerHub中实例:
$docker save -o zabbixcentos.tar centoszabbix:latest
ls -alh zabbixcentos.tar
-rw------- 1 root root 200M 5月 6 13:16 zabbixcentos.tar
$docker load --input zabbixcentos.tar
Loaded image: centoszabbix:latest
$docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centoszabbix (上面从已有镜像文件中创建) latest 622b7661c06b 13 minutes ago 202MB
#用户需要在DockerHub网站注册后才能进行上传镜像
$docker login #在及其上进行登录
# Login with your Docker ID to push and pull images from Docker Hub. If you don have a Docker ID, head over to https://hub.docker.com to create one.
# Username: weiyigeek
# Password: 输入hub密码
# WARNING! 账号密码base64编码存放于 /root/.docker/config.json 文件中
# echo "d2VpeW********VrO***ldlaXllMjAxOQ==" | base64 -d
# weiyigeek:Docker
# https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded #表示登录成功
$docker push weiyigeek/czabbix:latest
The push refers to repository [docker.io/weiyigeek/czabbix]
484404f26982: Pushed
d69483a6face: Mounted from library/centos
latest: digest: sha256:c1d891c2d56a0677ff28639189fa7252790d3b8ef417816570fb3a7654bc4e05 size: 736 #表示上传成功返回的票据

镜像的F&Q相关问题
Q:Docker 镜像是怎么实现增量的修改和维护的? 答:每个镜像都由很多层次构成,Docker 使用 Union FS 将这些不同的层结合到一个镜像中去。 通常 Union FS 有两个用途:(Docker 在 AUFS 上构建的容器也是利用了类似的原理)
一方面可以实现不借助 LVM、RAID 将多个 disk 挂到同一个目录下
就是将一个只读的分支和一个可写的分支联合在一起,Live CD 正是基于此方法可以允许在镜像不变的基础上允许用户在其上进行一些写操作
Q:什么是镜像写时复制? 答: 比如典型的Linux启动首先会将rootfs置为readonly再进行一系列检查后将其切换为readwrite供用户使用,当在Docker中起初也是将rootfs置为readonly,然后利用union mount将一个readwrite文件系统挂载只读的rootfs之上,并且运行再次将下层的文件系统设置为readonly并且向上叠加,我们把这样一组readonly和一个writeable的结构构成一个容器的运行目录,每一个被称为一个Layer;
Writable : Container (可以向上叠加同时它变成一层Image)
add Apache : Image (Reference parent-引用父)
add Emacs : Image (Reference parent-引用父)
Debian : Base Image
> Bootfs
lxc,aufs,brtfs : Kernel
Q:Docker 中 save 和 export 命令的区别? 答: 我们先简单的说一下其区别,在随后的的例子中使您更快的理解;
1.save 与 load 命令对应即导出与导入镜像,而export与import命令对应即导出导入容器;
2.save 保存后 load 加载的镜像没有丢失历史和层(Layer),而容器export导出然后import导入时所有的提交历史将会丢失,这意味着您无法回滚到之前的层;
3.补充:通过import导入的方式镜像只有一层,而通过commit的方式生成的镜像实际是在原有的Base Image(即复写层)上又生成了一层;
# 1.在一个正在运行的容器上创建一个目录然后commit
docker exec -it test1 mkdir /tmp/demo
docker commit -a "WeiyiGeek" -m "create /tmp/demo directory" test1 test:1.2 #加上tag镜像名称:版本防止玄虚镜像
sha256:30a048249000dd36561bffaa9fecc7690a45ef12096849c77afd4543b0d2d9b0
# 2.采用save命令进行导出提交的镜像
docker save -o save.tar 30a048
# 3.采用export进行导出正则运行的容器
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
25d2d645bfc9 test1 "top -b -d 2" 2 weeks ago Up 3 days test1
docker export test1 > export.tar # 或者 docker export 25d > export.tar
# 4.在另外一台机器上进行分别导入 save.tar 与 export.tar
docker load -i save.tar #此时默认导入会导致虚悬镜像
Loaded image ID: sha256:30a048249000dd36561bffaa9fecc7690a45ef12096849c77afd4543b0d2d9b0
cat export.tar | docker import - test1:1.0
sha256:cdd65e7054be9e1928fe8133380817e81ff12b8493b40e2f9106efedac5ee572
# 5.查看导入情况
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 30a048249000 2 minutes ago 7.37MB
test1 1.0 cdd65e7054be 3 minutes ago 7.35MB
# 6.查看异同
[root@master-01 ~]$ docker history 30a # save 方式 存在历史层信息
# IMAGE CREATED CREATED BY SIZE COMMENT
# 30a048249000 22 minutes ago -d 2 193B create /tmp/demo directory
# <missing> 2 weeks ago /bin/sh -c #(nop) CMD ["-d" "2"] 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) ENTRYPOINT ["top" "-b"] 0B
# <missing> 2 weeks ago /bin/sh -c echo "http://mirrors.huaweicloud".… 1.8MB
# <missing> 2 weeks ago /bin/sh -c #(nop) MAINTAINER WeiyiGeek mast… 0B
# <missing> 2 weeks ago /bin/sh -c #(nop) LABEL Author=WeiyiGeek De… 0B
# <missing> 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
# <missing> 5 weeks ago /bin/sh -c #(nop) ADD file:c92c248239f8c7b9b… 5.57MB
[root@master-01 ~]$ docker history cdd # export 方式无历史层信息且只有一层
# IMAGE CREATED CREATED BY SIZE COMMENT
# cdd65e7054be 23 minutes ago 7.35MB Imported from -
描述:Docker容器Container类似于一个轻量级的沙箱,也可以看做一个简易版的Linux系统环境;因为容器是运行应用程序的,所以必须得先有一个操作系统为基础
Docker利用容器来运行和隔离应用;
容器是从镜像创建的应用独立运行的实例;
可以进行启动/开始/停止/删除容器,而这些容器都是相互隔离,互不可见的;
镜像自身只读的,容器从镜像启动的时候会在镜像的最上层创建一个可写层,镜像本身将保持不变;
创建容器、启动容器、终止容器、进入容器、删除容器、导入导出容器实现容器迁移;
当创建并运行容器时候Docker在后台运行的标准操作包括:
检查本地是否存在指定镜像,不存在就从公有仓库下载
利用镜像创建并启动一个容器
分配一个文件系统,并在只读的镜像层外面挂载一个可读写层
从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
从地址池配置一个IP地址给容器
执行用户指定的应用程序
执行完毕后容器被终止
启动容器有两种方式:
一种是基于镜像新建一个容器并启动
另外一个是将在终止状态(stopped)的容器重新启动。
容器操作常用命令:
docker create -it repository[:tag] #创建容器但处于停止状态
docker start -a -i <container id> #启动创建的容器
# -a 参数 连接容器并打印输出或错误 -
# -i 参数 启动容器并进入交互模式
docker run -t -i repository[:tag] /bin/bash #创建并启动容器 等同于上面两条命令
# -t:让Docker分配一个伪终端(pseudo-tty)并绑定在容器的标准输入上.
# -i:则让容器的标准输入保持打开.
# CTRL+Z 和 Exit 则退出容器Container
docker run -d repository[:tag] /bin/sh -C "echo hello word" #守护态运行
# -d 参数即可后台运行,用户无法看到容器中的信息
# -c 执行的Command
# --rm 添加这个标记,容器会在停止后立即删除自身 (注意:与-d不能同时使用)
# --name 使用--name web 标记可以为容器重新命名
docker logs <Container-id> #获取容器的输出信息
docker attach [names] #采用ps -a NAMES 进入容器
docker exec -it <Container-id> /bin/bash #docker exec 至1.3版本起可以在容器中运行命令
docker ps -aq #显示本机上的所有容器ID运行的容器ID信息
docker restart <container id> #重启容器
docker stop <container id> #停止容器
docker kill <cantainer id> #强行终止容器 可以直接发送SIGKILL信号来终止容器
docker rm <container id> #删除容器删除依赖该镜像的容器ID,前3位即可
# -f,--force=false 强制终止并删除一个运行中的容器[默认会发生SIGKILL信号]
# -l,--link=false 删除容器连接但保留容器
# -v,--volumes=false 删除容器挂载的数据卷
docker export <container id> >导出文件.tar #导出容器
docker import - repository[:tag] #导入容器

命令实践演示:
#创建容器
$sudo docker create -it weiyigeek/czabbix
2b72a3410be576aeb9023ef2c41488e7b2630cf2282b8a0b1dddce771f22231f
#启动容器ID执行交换登录,start可以进入到任何在容器中运行
$sudo docker start -i 2b7
# [root@2b72a3410be5 /]# whoami
# root
# [root@2b72a3410be5 /]# exit
#对于创建后的容器采用exit命令退出后该容器自动处于终止状态;
#提示:正常退出不关闭容器请按Ctrl+P+Q进行退出容器
$sudo docker ps -a #显示本机上存在的所有容器
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 634e7ed26d76 d131e0fa2585 "/bin/bash" About an hour ago Exited (130) About an hour ago recursing_goodall
# b72a3410be5 weiyigeek/czabbix "/bin/bash" 41 seconds ago Up 21 seconds priceless_chaplygin
$docker run -t -i centos /bin/bash #创建容器并且运行
# [root@0b9aef4876d3 /]# hostname
# 0b9aef4876d3
$ddocker run -d centoszabbix /bin/sh -c "echo Hello World,Docker Container"
4e62768b1d9196fc79fd740c103df79c8e3bb09d9c6810aa43976abeda036a26
$ddocker logs 4e62 #获取容器输出的信息
Hello World,Docker Container
$docker attach priceless_chaplygin #通过上面的ps -a 中names进入想对应的容器
#当多个窗口同时 attach 到同一个容器的时候 所有窗口都会同步显示 。当某个窗口因命令阻塞时,其他窗口也无法执行操作了
# [root@b72a3410be5 /]$ hostname
# b72a3410be5
$docker exec -ti b72 /bin/echo "whoami" #可以通过exec直接执行命令和进入shell
# whoami
#停止容器
#当需要删除一个在容器中运行的镜像的时候,会导致不能删除镜像[Image]这时候必须先停止再删除容器[container]:
$sudo docker stop 634e7ed26d76
#镜像的容器ID,前3位即可,然后在删除镜像
$sudo docker rm 634 #此时再用镜像ID来删除进行,成功则会打印出删除各层信息
$sudo docker rmi -f d131e0fa2585 #镜像的容器ID,前3位即可,然后在删除镜像
#导出镜像
$sudo docker export 092 > czabbix.tar
#导入镜像
$cat czabbix.tar | docker import - test:latest
sha256:bbb9dbcaa5fff9c5b1b99c978df4dcaeeca3d2200d82fc466f5064931bd3bba2
[root@zabbix ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test latest bbb9dbcaa5ff 14 seconds ago 202MB
补充:使用nsenter工具进入Docker容器
#获取容器的PID
$PID=$(docker inspect --format "{{ .State.Pid}}" <container id>)
#通过得到的这个PID,就可以连接到这个容器:
$nsenter --target $PID --mount --uts --ipc --net --pid
#手动获取docker container 运行的PID
[root@zabbix ~]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED
092d1b82c6a0 weiyigeek/czabbix "/bin/bash" 17 minutes ago ck
[root@zabbix ~]$ docker inspect --format "{{ .State.Pid}}" 092
9030
[root@zabbix ~]$ nsenter --target 9030 --mount --uts --ipc --net --pid
[root@092d1b82c6a0 /]$ cat /etc/redhat-release
CentOS Linux release 7.6.1810 (Core)
#成功进入该容器,9030就是容器092d1b82c6a0的进程PID
Docker load 与 Docker import 的比较 描述:导入容器和导入镜像是差不多的但是实际上又是有所区别的
load:导入镜像存储文件到本地镜像库 而 import:导入一个容器快照到本地镜像库
容器快照:文件将丢失所有的历史记录和元数据信息(即保留容器当时的快照状态),导入的时候还需要重新制定标签等元数据信息;
镜像存储:文件将保存完整的记录,并且体积也要大;
总结:
容器的名称是唯一性的如果不指定名称,将会自动生成一个容器名称;
容器是直接提供应用服务的组件,也是Docker实现快速启停和高效服务器性能的基础
在生产环境中因为容器自身的轻量性,建议在容器前段引入HA(高可靠性)机制,当出现错误的时候能快速切换到其他容器之中,还能自动重启故障容器;
描述:Docker 仓库(Repository)类似于代码仓库,是Docker集中存放镜像文件的场所;该概念的引入为Docker镜像文件的分发和管理提供了便捷的途径。 仓库注册地址:https://hub.docker.com/ 安装帮助文档:http://www.widuu.com/docker/installation/ubuntu.html
注册服务器是存放仓库的地方,其上往往存放着多个仓库,每个仓库集中存放某一类镜像,往往包括多个镜像文件,通过不同的标签[TAG]来进行区分;
目前最大的公开仓库是 Docker Hub 存放了数量庞大的镜像提供用户下载
国内的公开仓库包括Docker Pool等等可以提供稳定的国内访问。
例如:对于仓库地址dl.dockerpool.com/ubuntu来说,dl.dockerpool.com是注册服务器,ubuntu是仓库名;

Docker 仓库分类:
公开仓库(Public)
私有仓库(Private)
官方与用户提供的镜像比较:
官方:centos 由Docker维护的基础或根镜像;
个人:user/centos 表示由user用户维护的centos基础镜像;
镜像仓库操作常用命令:
docker login #连接并初始化hub仓库 需要输入用户名和密码认证,然后存放于/root/.docker/config.json 文件中
docker pull [repository] #下载指定仓库到本地
docker push [imges]
#实例
$docker pull dl.dockerpool.com:5000/ubuntu:12.04 #连接其他的注册服务器
$docker images
$docker tag dl.dockerpool.com:5000/ubuntu:12.04 ubuntu:12.04 #更新标签与官方标签保持一致
$docker push dl.dockerpool.com:5000/ubuntu:12.04 #上传到私有镜像
(1)创建和使用私有仓库: 最快捷还是搭建本地的仓库服务器(注意:配置不当会导致漏洞)
$ docker run -d -p 5000:5000 registry
#默认情况下仓库创建在容器的/tmp/register目录下,-v 通过镜像文件存放在宿主机本地的指定路径/opt/data/registry上;
$ docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry
# -d:容器启动后会进入后台,用户无法看到容器中的信息.
# -p:指定仓库镜像的端口.
# -v:将镜像的存放位置放在本地指定的路径上.
此时会在本地启动一个私有仓库服务,监听端口为5000; 更新标签本地仓库地址并上传到私有仓库之中:
$docker tag ubuntu:14.04 10.0.2.2:5000/test #更改镜像的标签
$docker pull 10.0.2.2:5000/test #下载镜像
#可以以这个来测试是不是存在docker 5000未授权访问漏洞
$ curl http://10.0.2.2:5000/v1/search #上传成功后可以查看到返回的json字符串
(2)自动创建[Automated Builds] 该功能是对于需要经常升级镜像内程序来说十分的方便,用户创建镜像发生更改的时候进行手动更新镜像,并能通过自动创建功能跟踪目标网络,一旦项目发现新的提交则自动执行创建;配置自动创建步骤如下:
创建登录Docker Hub 绑定Github;
在Docker Hub中配置一个自动创建
选取一个目标网站中的项目(需要Dockerfile和分支)
指定Dockerfile的位置并提交创建,可以在"自动创建页面"跟踪每次创建的状态;
总结:仓库管理镜像的设计理论与Git差不多,工作流程为文件分发和合作带来的众多优势。
至此本节完毕,敬请期待下一小节内容。
Docker容器技术入门实践系列历史已发布文章(点击即可进入):
欢迎各位志同道合的朋友一起学习交流,如文章有误请在下方留下您宝贵的经验知识,个人邮箱地址【master#weiyigeek.top】或者 个人公众号【WeiyiGeek】联系我。
更多文章来源于【WeiyiGeek Blog - 为了能到远方,脚下的每一步都不能少】 个人博客。
博客地址: https://weiyigeek.top

人活着,最根本的价值不是吃饭,而是人生的意义。
专栏书写不易,如果您觉得这个专栏还不错的,请给这篇专栏【点个赞、投个币、收个藏、关个注,转个发】,这将对我的肯定,谢谢!。
echo "【点个赞】,动动你那粗壮的拇指或者芊芊玉手,亲!"
printf("%s", "【投个币】,万水千山总是情,投个硬币行不行,亲!")
fmt.Printf("【收个藏】,阅后即焚不吃灰,亲!")
System.out.println("【关个注】,后续浏览查看不迷路哟,亲!")
console.info("【转个发】,让更多的志同道合的朋友一起学习交流,亲!")