Docker入门——保姆级

Docker概述

​ ------ Notes from WAX through KuangShen

准确来说,这是一篇学习笔记!!!

Docker为什么出现

一款产品:开发---上线 两套环境!应用环境如何铜鼓?

开发 -- 运维。避免"在我的电脑米问题呀!!!"

环境配置十分麻烦,没一个机器都要部署环境(集群Redis、ES...)!费时费力。

发布一个项目(jar + (Redis、Mysql、jdk、ES))能否带上安装环境进行打包发布。

Windows开发,最后发布到Linux:

传统:开发项目,运维来做。

现在:开发打包部署上线,一套流程做完。

例:

java -- apk -- 发布(应用商店) -- 张三使用apk -- 安装即可使用!

java -- jar(环境) -- 打包项目带上环境(镜像)-- (Docker仓库:商店)-- 下载发布的镜像 -- 直接运行即可!

Docker给出以上问题的解决方案!

Docker的思想来自于集装箱!

隔离:Docker核心思想,打包装箱!每个箱子是互相隔离的

Docker通过隔离机制,可以将服务器利用到极致!

Docker的历史

2010年,几个搞IT的年轻人,就在没美国成立了一家公司dotCloud

做一些pass的云计算服务,LXC有关的容器技术!

他们将自己的技术(容器话技术)命名 就是Docker!

Docker刚刚诞生的时候,没有引起行业注意!公司举步维艰,于是决定开源!!!

2013,Docker开源!

Docker越来越多的人发现Docker的有点!火了,每个月都会更新一个版本!

2014年4月9日,Docker1.0发布!

Docker为什么这么火?极其轻巧

在容器技术出来之前,我们都是使用虚拟技术!

虚拟机:在Windows中装一个Vmware,通过这个软件可以虚拟化出来一个或多个电脑,但是十分笨重。

虚拟机也是属于虚拟化技术,Docker容器技术,也是一种虚拟化技术

shell 复制代码
vm:linux centos原生镜像(一个电脑) 隔离,需要开启多个虚拟机! 几个G大小
docker:隔离,镜像(最核心的环境4m + jdk + mysql)十分轻巧,运行镜像就可以了,几 M Kb  秒级启动

到现在,所有开发人员都必须会的工具 Docker。

聊聊docker

Docker是基于 Go 语言开发的,开源项目!

官网:https://www.docker.com

文档:https://docs.docker.com 超详细

仓库地址:https://hub.docker.com

Docker的作用

虚拟机技术缺点:

1、资源占用多

2、冗余步骤多

3、启动慢

Docker和虚拟技术的不同:

  • 统虚拟机,虚拟出一切硬件,运行一个完整的操作系统,然后在这个系统上安装和运行软件

  • 容器内的应用直接运行再宿主机的内核,容器是没有自己的内核的,也没有虚拟我们的硬件,所以轻便

  • 每个容器内是相互隔离的,每个容器内都有一个属于自己的文件系统,互不影响

DevOps(开发/运维)

应用更快速的交付和部署

  • 传统方式:一堆帮助文档,安装程序

  • Docker:打包镜像发布测试,意见运行

更快捷的升级和扩缩容

  • 使用Docker以后,我们不熟应用就类似搭积木简单

  • 项目打包为一个镜像,扩容 服务器A 服务器B

更简单的系统运维

  • 在容器化之后,开发、测试测试环境都是高度一致的。

更高效的计算资源利用:

  • Docker是内核级的虚拟化,可以在一个物理机上运行很多个容器实例。服务器的性能能利用到极致。

docker的安装

docker的基本组成

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cXdU4l9R-1691285233022)(C:\Users\wax\AppData\Roaming\Typora\typora-user-images\image-20210424153809962.png)]

镜像(image):

Docker镜像好比一个模板,可以通过这个模板来创建容器服务。

通过这个镜像可以创建多个容器(最终服务运行或项目运行就是在容器中的)

容器(container):

Docker利用容器技术,独立运行一个或者一组应用,通过镜像来创建。

虽然不是很准确,但是可以粗略的连接容器就是一个简单的LInux系统

仓库(repository):

仓库就是存放镜像的地方

仓库分为公有仓库和私有仓库

Docker Hub

阿里云...都有容器服务器

安装docker

安装环境

  1. 需要会一点Linux的基础
  2. CentOS(本次用例)

环境查看

shell 复制代码
#系统版本是3.10以上的
[root@sa ~]# uname -r
3.10.0-957.el7.x86_64
[root@sa ~]# 
shell 复制代码
#系统版本
[root@sa ~]# cat /etc/os-release 
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

安装docker

帮助文档:https://docs.docker.com/engine/install/

shell 复制代码
# 1、删除旧的版本
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine
# 2、依赖包
sudo yum install -y yum-utils

# 3、设置镜像的仓库,当然也可以设置阿里云的
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo              官方
sudo yum-config-manager \
    --add-repo \
    https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo     阿里云

#更新yum软件包索引
yum makecache fast

# 4、安装docekr  -ce表示社区版    -ee表示企业版
yum install docker-ce docker-ce-cli containerd.io
  # Ubuntu 安装
  sudo apt install docker.io

# 5、运行docker
sudo systemctl start docker

# 6、查看版本信息,有则是安装成功
docker version

# 7、查看启动是否成功
docker run hello-world   # 会打印"Hello from Docker!" 则成功
	

卸载docker

shell 复制代码
# 1、卸载依赖
sudo yum remove docker-ce docker-ce-cli containerd.io

# 2、删除资源
sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

hello world流程

此处就不画图了:

docker run 开始,docker就会在本地查找镜像,

有,则直接运行该镜像。没有,就会在docker Hub(官方镜像仓库)上去下载,下载到本地后直接运行,没有下载到则返回错误

底层原理

docker是怎么工作的

Docker是一个 Client - Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问!

DockerServer 接收到Docekr Client的指令执行!

Docker 为什么比 VM 快

  1. Docker有着比虚拟机更少的抽象层
  2. Docker利用的是宿主机的内核,VM需要的是 Guest OS。

所以说,新建一容器的时候,Docker不需要像虚拟机一样重新加载一个操作系统内核,避免引导。虚拟机是加载Guest OS,分钟级别,而Docker是利用宿主机的操作系统,省略了这个复杂的过程,秒级启动。

当然,安全性方面,肯定还是VM虚拟机更安全,毕竟是一个完整的虚拟系统。

Docker的常用命令

帮助命令

shell 复制代码
docker version				# 显示docker版本信息
docker info					# 显示docker的系统信息,包括镜像和容器的数量
docekr --help				# 帮助

帮助文档 https://docs.docker.com/reference/

镜像命令

Docker images 查看所有本地镜像

shell 复制代码
[root@sa ~]# docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
hello-world             latest              d1165f221234        2 months ago        13.3kB
vue-docker-demo-image   latest              4835e3101950        8 months ago        135MB
docker-demo-image       latest              831b48a0447e        8 months ago        133MB

# 含义
REPOSITORY		镜像的仓库源
TAG				镜像的标签
IMAGE ID		镜像的id
REPOSITORY		镜像创建时间
SIZE			镜像大小

# 可选项
  -a, --all             Show all images (default hides intermediate images)		# 显示所有镜像
      --digests         Show digests 											# 显示摘要
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print images using a Go template
      --no-trunc        Don't truncate output
  -q, --quiet           Only show numeric IDs									# 只显示id

docekr search 搜索镜像

shell 复制代码
[root@sa ~]# docker search mysql
NAME           DESCRIPTION 			         STARS  OFFICIAL            AUTOMATED
mysql                MySQL is a widely used, open-source relation...   10876  	 [OK] 
mariadb       		 MariaDB Server is a high performing open sou...   4102   	 [OK] 
mysql/mysql-server   Optimized MySQL Server Docker images. Create...   808       					 [OK]
...
[root@sa ~]# 

# 可选项,通过收藏来过滤
 -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print search using a Go template
      --limit int       Max number of search results (default 25)
      --no-trunc        Don't truncate output
[root@sa ~]# docker search mysql --filter=STARS=3000
NAME                DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
mysql               MySQL is a widely used, open-source relation...   10876               [OK]                
mariadb             MariaDB Server is a high performing open sou...   4102                [OK]     
[root@sa ~]#

**docker pull ** 下载镜像

shell 复制代码
# 下载镜像 docker pull 镜像名[:tag]
[root@sa ~]# docker pull mysql
Using default tag: latest				# 如果没有指定tag,默认就是latest
latest: Pulling from library/mysql
69692152171a: Pull complete 			# 分层下载,docker image的核心--联合文件系统
1651b0be3df3: Pull complete 
951da7386bc8: Pull complete 
0f86c95aa242: Pull complete 
37ba2d8bd4fe: Pull complete 
6d278bb05e94: Pull complete 
497efbd93a3e: Pull complete 
f7fddf10c2c2: Pull complete 
16415d159dfb: Pull complete 
0e530ffc6b73: Pull complete 
b0a4a1a77178: Pull complete 
cd90f92aa9ef: Pull complete 
Digest: sha256:d50098d7fcb25b1fcb24e2d3247cae3fc55815d64fec640dc395840f8fa80969		# 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest		# 真实地址
[root@sa ~]# 

# 以下两个命令等价
docker pull mysql
docker pull docekr.io/library/mysql:latest

docker images 查看镜像

shell 复制代码
[root@sa ~]# docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
mysql                   latest              c0cdc95609f1        3 days ago          556MB
hello-world             latest              d1165f221234        2 months ago        13.3kB
vue-docker-demo-image   latest              4835e3101950        8 months ago        135MB
[root@sa ~]# 

docker rmi 删除镜像

shell 复制代码
# docker rmi 容器id  -f  强制删除(运行中的)
# docker rmi 容器id 容器id 容器id  删除多个容器
[root@sa ~]# docker rmi d11
Untagged: hello-world:latest
Untagged: hello-world@sha256:5122f6204b6a3596e048758cabba3c46b1c937a46b5be6225b835d091b90e46c
Deleted: sha256:d1165f2212346b2bab48cb01c1e39ee8ad1be46b87873d9ca7a4e434980a7726
Deleted: sha256:f22b99068db93900abe17f7f5e09ec775c2826ecfe9db961fea68293744144bd
[root@sa ~]# 

# 批量删除
# -f 强制删除,-aq 仅显示所有的镜像id
[root@sa ~]# docker rmi -f $(docker images -aq)

容器命令

有了镜像才可以创建容器

以centOS镜像为例:

shell 复制代码
docker pull centos

新建容器并启动

shell 复制代码
docekr run [可选参数] image

# 参数说明
--name="name"			# 容器名称 name01  name02,用来区分容器
-d 						# 后台方式运行
-it						# 使用交互方式运行,进入容器查看内容
-p						# 指定容器的端口
	-p ip:主机端口:容器端口  映射
	-p 主机端口:容器端口(常用)
	-p 容器端口
	
-P(大写p)				# 随机指定端口

# 启动并进入容器,你会发现,现在的用户名就是容器的id
[root@sa ~]# 
[root@sa ~]# docker run -it centos /bin/bash
[root@74457211455c /]# 
[root@74457211455c /]# 
# 退出就是exit
...

列出所有运行的容器

shell 复制代码
docker ps	# 列出正在运行的容器
   -a 		# 列出所有容器,包含已经停止的
   -n=? 	# 列出最近创建的容器,带数字
   -q		# 只列出容器的id

# 列出所有容器,包含已经停止的
[root@sa ~]# docker ps -a
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS                    PORTS               NAMES
74457211455c        centos                  "/bin/bash"              2 minutes ago       Up 2 minutes                                  happy_wilson

退出容器

shell 复制代码
exit		# 直接停止容器并退出
Ctrl +p +q  # 容器不停止退出

删除容器

shell 复制代码
docker rm 						# 容器id
	-f 	# 强制删除正在运行的容器
	
docker rm -f $(docker ps -aq)	# 删除所有的容器
docker ps -aq |xargs docker rm	# 也可以删除所有容器

启动容器

shell 复制代码
docker start	容器id		# 启动容器
docker restart	容器id		# 重启容器
docker stop		容器id		# 停止当前正在运行的容器
docker kill		容器id		# 轻质停止当前容器

其他常用命令

后台启动

shell 复制代码
# 命令 docker run -d 镜像名
[root@sa ~]# docker run -d centos
26e18764513a7ebf03f73dda143d5b1bb73054cbf74bae10d8f22ba16d4c4ea9
[root@sa ~]# 
# 会产生的问题:ps发现centos 直接停了
# 原因:docke容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止

查看日志

shell 复制代码
docker logs -tf --tail

# 显示日志
-tf				# 显示日志 -t时间戳 -f Follow log output(不断输出日志)
--tail number	# 要显示的日志条数,后接数字

查看容器中的进程信息

shell 复制代码
# 命令 docker top 容器id
[root@sa ~]# docker top 8b
UID    PID        PPID    C   STIME        TTY       TIME        CMD
root   21913     21895    0     04:28     pts/0    00:00:00     /bin/bash
[root@sa ~]# 

查看镜像的元数据

shell 复制代码
# 命令 docker inspect 容器id

[root@sa ~]# docker inspect 8b
[
    {
        "Id": "8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05",
        "Created": "2021-05-18T20:28:55.704722287Z",
        "Path": "/bin/bash",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 21913,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2021-05-18T20:28:56.270071675Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:300e315adb2f96afe5f0b2780b87f28ae95231fe3bdd1e16b9ba606307728f55",
        "ResolvConfPath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/hostname",
        "HostsPath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/hosts",
        "LogPath": "/var/lib/docker/containers/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05/8bdbd34f37a64afa4e80eaafdaa517bd98a8ed1b3be178b2fb386865d4df7f05-json.log",
        "Name": "/interesting_black",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Capabilities": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": [],
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": [],
            "DeviceCgroupRules": null,
            "DeviceRequests": null,
            "KernelMemory": 0,
            "KernelMemoryTCP": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95-init/diff:/var/lib/docker/overlay2/85c0301341e0be53f9854cc3148968bb232564ca6cac942efebc0b9328ec192c/diff",
                "MergedDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95/merged",
                "UpperDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95/diff",
                "WorkDir": "/var/lib/docker/overlay2/1664d1df131ceec26e30d5f1288e4a76104ba149a4e2ed91df49cbae1d5b5c95/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "8bdbd34f37a6",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "centos",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "org.label-schema.build-date": "20201204",
                "org.label-schema.license": "GPLv2",
                "org.label-schema.name": "CentOS Base Image",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.vendor": "CentOS"
            }
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "f90c37e119e8231aee5aed42652c64393959cd87f061092347b98b21a5764f8e",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/f90c37e119e8",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "d3f5369c06fc9c93af1b5bfbc45617ee24b7900e88a33ac66793b80712912f88",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "d29efb6707572fecd8ccaadfb2f011525f39fa0a9fb0a7732be8af61baf34003",
                    "EndpointID": "d3f5369c06fc9c93af1b5bfbc45617ee24b7900e88a33ac66793b80712912f88",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]
[root@sa ~]# 

进入正在运行的容器

shell 复制代码
# 进入正在运行的容器

# 方式一:
docker exec -it 容器id bashShell

# 方式二:
docker attch 容器id

# docker exec	进入容器后开启一个新的终端
# docker attch	进入容器正在执行的终端,不会开启新的进程

容器和主机之间的拷贝

shell 复制代码
# 从容器内拷贝文件到主机
docker cp 容器id:/路径 /主机路径  # 建议使用绝对路径

# 进入容器
[root@sa ~]# docker exec -it f2 /bin/sh
sh-4.4# 
sh-4.4# touch 123.txt
sh-4.4# ls
123.txt  ks-script-esd4my7v  ks-script-eusq_sc5
sh-4.4# exit
[root@sa ~]# 

# 将容器内的文件拷贝至主机
[root@sa ~]# docker cp  f2:/tmp/123.txt ./
[root@sa ~]# 
[root@sa ~]# docker ps 					 # 说明只要容器还在,启不启动数据都在
CONTAINER ID   IMAGE    COMMAND    CREATED      STATUS      PORTS     NAMES
[root@sa ~]# 
[root@sa ~]# docker ps -a
CONTAINER ID  IMAGE COMMAND   				 CREATED  	STATUS  PORTS   NAMES
f24dcc07e0e5 centos "/bin/sh -c 'while t..."   15 ago     Exited      vigilant_hypatia
[root@sa ~]# 

Docker 镜像讲解

镜像是什么

镜像是一种轻量级、可执行的独立软件包(类似于免安装),用来打包软件运行环境和基于运行环境开发的软件,他包含运行某个软件所需的所有内容,包括代码、运行是所需的库、环境变量及配置文件等等。

Docker镜像加载原理

UnionFS(联合文件系统)

UnionFS:Union文件系统是一种分层、轻量级并且高性能的文件系统,他支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂在到同一个虚拟文件系统(unite several directories into a single virtual filesystem)下。Union文件系统时Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。

特性:以此同时加载多个文件系统,单对外表现,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录

Docker镜像加载原理

Docker的镜像实际上有一层一层的文件系统组成,这种层级的文件系统叫联合文件系统

Bootfs(boot file system)主要包含bootloader 和kernel,BootLoader主要是引导加载kerner,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们经典的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。

rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev, /proc, /etc等标准目录文件。rootfs就是各种不同的操作系统发行版,例如:Ubuntu、CentOS等。

平时安装的虚拟机CentOS都是好几个G,但是Docker这里的才几百兆,为什么?

因为Docker镜像一般都是非常精简的,不要忘了,他用的宿主机的内核,自己只需要提供rootfs就可以了。所以镜像只需提供最基本的命令、工具和程序即可。由此,对于不同的Linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。

分层理解

分层的镜像

​ 在下载镜像时,可以注意到下载日志输出的是一层层下载的

分层的好处:

最大的好处莫过于资源共享!例如,有多个镜像都是相同的Base镜像构建而来,那么宿主机只需要在磁盘上保留一份Base镜像,同时,内存中也只需要加载一份Base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。

查看镜像分层的方式可以通过 docker inspect 命令查看

理解:

所有的Docker镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上创建一个新的镜像层。

例如:基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层,如果继续添加,则依次往上新建。

(这里其实忽略了一个重要的概念,那就是层,怎样的添加才能构成所谓的 "层")

Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层。

分层理解2(理论更清晰)

比较好的理解:

Docker的镜像分层

接下来,我们来看看 Docker的镜像分层机制。

Docker镜像是分层构建的,Dockerfile 中每条指令都会新建一层。例如以下 Dockerfile:

docker 复制代码
FROM ubuntu:18.04
COPY . /app
RUN make /app
CMD python /app/app.py

以上四条指令会创建四层,分别对应基础镜像、复制文件、编译文件以及入口文件,每层只记录本层所做的更改,而这些层都是只读层。当你启动一个容器,Docker 会在最顶部添加读写层,你在容器内做的所有更改,如写日志、修改、删除文件等,都保存到了读写层内,一般称该层为容器层,如下图所示:

事实上,容器(container)和镜像(image)的最主要区别就是容器加上了顶层的读写层。所有对容器的修改都发生在此层,镜像并不会被修改,也即前面说的 COW(copy-on-write)技术。容器需要读取某个文件时,直接从底部只读层去读即可,而如果需要修改某文件,则将该文件拷贝到顶部读写层进行修改,只读层保持不变。

每个容器都有自己的读写层,因此多个容器可以使用同一个镜像,另外容器被删除时,其对应的读写层也会被删除(如果你希望多个容器共享或者持久化数据,可以使用 Docker volume)。

最后,执行命令 docker ps -s,可以看到最后有两列 size 和 virtual size。其中 size就是容器读写层占用的磁盘空间,而 virtual size 就是读写层加上对应只读层所占用的磁盘空间。如果两个容器是从同一个镜像创建,那么只读层就是 100%共享,即使不是从同一镜像创建,其镜像仍然可能共享部分只读层(如一个镜像是基于另一个创建)。因此,docker 实际占用的磁盘空间远远小于 virtual size 的总和。

以上就是 Docker 镜像分层的主要内容,至于这些层的交互、管理就需要存储驱动程序,也即联合文件系统(UnionFS)。Docker 可使用多种驱动,如目前已经合并入 Linux 内核、官方推荐的overlay, 曾在 Ubuntu、Debian等发行版中得到广泛使用的 AUFS,以及devicemapper、zfs等等,需要根据 Docker以及宿主机系统的版本,进行合适的选择。

commit 镜像

shell 复制代码
docker commit 提交容器成为一个新的副本(镜像)

# 命令和git类似
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]

容器数据卷

什么是容器数据卷

数据如果只保存在容器中,那么容器删除,则数据丢失。

有没有什么方式可以将容器中的数据同步或者说映射至本地/宿主机的长期保存所想要的数据。

容器之间也可以共享数据。

于是,有了卷技术。简单理解就是目录的挂载,将容器内的目录挂载到宿主机上,并且容器间也是可以数据共享的。

使用数据卷

方式一: 直接使用命令挂载 -v

shell 复制代码
# docker run -it -v 宿主机目录:容器内目录,宿主机目录可以不存在,会自动创建,最好使用绝对路径
	-it # 交互式启动
	-v  # 卷volume  挂载

[root@sa ~]# docker run  -it -v ~/mont_volume:/home centos /bin/bash

# 启动之后可根据 docker inspect 容器id 查看挂载结果
[root@sa ~]# docker inspect 0601e8ee0ec6
shell 复制代码
# 挂载以后基本上就理解成容器内核宿主机共用这个目录。
# 测试可发现,当容器停止后,在宿主机向mont_volume目录写入文件,然后重新启动容器,容器home目录可直接看到写入文件
# 即使,容器被删除,然后重新起一个容器,挂载宿主机目录还是mont_volume,那么,新容器也是可以直接看到写入文件。这也说明了,容器删除不会影响挂载的目录
# 总结,可用该方法将配置类的文件挂载出来,当我们需要修改容器配置时,无需进入容器即可修改

安装MySQL

shell 复制代码
# 1、获取镜像
[root@sa ~]# docker pull mysql

# 2、安装mysql一定要注意配置密码
# Starting a MySQL instance is simple:
#
[root@sa ~] # docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# ... where some-mysql is the name you want to assign to your container, my-secret-pw # is the password to be set for the MySQL root user and tag is the tag specifying the # MySQL version you want. See the list above for relevant tags.
# from  https://hub.docker.com/_/mysql

# 3、启动镜像
[root@sa ~]# docker run -d -p 3310:3306 -v /home/myspl/conf:/etc/mysql/conf.d -v /home/mysql/date:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql ac4ca43eb68bc7bfadbd6da4cef7fa81f83d79eb10b9639cf8b7cbc016e50c9a
	-d 后台运行
	-p 端口映射
	-v 卷挂载
	-e 环境配置
	--name 容器名字(容器名字mysql01,而mysql是镜像名)
[root@sa ~]# 

具名和匿名挂载

shell 复制代码
# 匿名挂载
-v 容器内目录
[root@sa ~]# docker run -d -P --name nginx -v /etc/nginx nginx

# 查看所有的 volume 的情况
[root@sa ~]# docker volume ls
DRIVER              VOLUME NAME
local               ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27
[root@sa ~]#

# VOLIME NAME 实际上是一些具体的目录

# 现在为了对比,起一个具名的挂在容器
[root@sa ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
42ca9c1cd1447c2d6f79caa9c9197cd7083a597ce01abec60c0109018cc69b06
[root@sa ~]# 
[root@sa ~]# docker volume ls
DRIVER              VOLUME NAME
local               ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27
local               juming-nginx

# 通过 -v 卷名:容器内路径
# 查看这个卷
[root@sa ~]# docker volume inspect ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27
[
    {
        "CreatedAt": "2021-05-29T23:21:35+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27/_data",
        "Name": "ed11f4eedfbbdff94704282111ffe0d6a8bad21171f4c6af0be255f8a649ab27",
        "Options": null,
        "Scope": "local"
    }
]
[root@sa ~]#  
[root@sa ~]# docker volume inspect juming-nginx
[
    {
        "CreatedAt": "2021-06-26T21:55:36+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
        "Name": "juming-nginx",
        "Options": null,
        "Scope": "local"
    }
]
[root@sa ~]# 
[root@sa juming-nginx]# cd _data/
[root@sa _data]# ls
conf.d  fastcgi_params  koi-utf  koi-win  mime.types  modules  nginx.conf  scgi_params  uwsgi_params  win-utf

总结:

所有的Docker容器的卷,在没有指定挂在目录的情况下都在 /var/lib/docker/volumes/xxxxx/_data

我们通过具名挂载可以方便的找到我们的一个卷,大多数情况下使用 具名挂载

shell 复制代码
# 如何确定是具名还是匿名挂载
-v	容器内路径				# 匿名挂载
-v  卷名:容器内路径		  # 具名挂载
-v  宿主机路径:容器内路径		# 指定路径挂载

拓展: (下面所说的权限都是针对容器而言,也就是如果在容器上是只读的,但,在宿主机上是可以进行写操作的)

shell 复制代码
# 通过 -v 容器内路径  :ro  :rw 来改变读写权限
# ro 只读
# rw 可读可写

# 凡是设置了这个容器权限,容器对挂载的内容的权限就被限定了
[root@sa ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
[root@sa ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx

# 上面所说的权限都是针对容器而言,也就是如果在容器上是只读的,但是可以在宿主机上进行写操作(当然,宿主机本身对该路径可写)

数据卷之DockerFile

Dockerfile是用来构建docker镜像的构建文件。命令脚本。

通过这个脚本可以生成镜像,镜像是一层一层的,脚本时一个个的命令,每个命令就是一层

shell 复制代码
# 创建一个dockerfile文件,名字可以任意,最好还是dockerfile
# 一般格式:指令(全大写)  参数
# 文件内容如下:
[root@sa Dockerfile-test]# cat dockerfile1  # 以下的每个指令就是镜像的一层
FROM centos							# 将centos作为基础镜像				

VOLUME ["volume01", "volume02"]		# 挂载数据卷

CMD echo "----is wax ---- first --- Dockerfile----"
CMD /bin/bash

[root@sa Dockerfile-test]# 

# 查看镜像生成过程
[root@sa Dockerfile-test]# docker build -f dockerfile1  -t wax-centos:0.1 .
							-f 指定dockerfile文件
							-t tag 目标文件
							一定不要忘了生成的文件存放路径------ 末尾的那个 .(当前目录)

Sending build context to Docker daemon  2.048kB
Step 1/4 : FROM centos
 ---> 300e315adb2f
Step 2/4 : VOLUME ["volume01", "volume02"]
 ---> Running in 669152823e0d
Removing intermediate container 669152823e0d
 ---> 5e192b1bef6d
Step 3/4 : CMD echo "----is wax ---- first --- Dockerfile----"
 ---> Running in b65e28cc25db
Removing intermediate container b65e28cc25db
 ---> cf5be1a83272
Step 4/4 : CMD /bin/bash
 ---> Running in 8cb48405b199
Removing intermediate container 8cb48405b199
 ---> 09081e56f27e
Successfully built 09081e56f27e
Successfully tagged wax-centos:0.1
[root@sa Dockerfile-test]# 

# 查看镜像
[root@sa Dockerfile-test]# docker images
REPOSITORY      TAG      IMAGE ID            CREATED             SIZE
wax-centos     0.1       09081e56f27e        5 minutes ago       209MB
redis          latest    fad0ee7e917a        3 weeks ago         105MB

# 启动镜像
[root@sa Dockerfile-test]# docker run  -it 09081e56f27e /bin/bash
[root@93d0f80f1c21 /]# 
[root@93d0f80f1c21 /]#   可以看到已经成功进入
[root@93d0f80f1c21 /]# ls -l
total 0
...... # 篇幅问题省略
drwxr-xr-x.   2 root root   6 Nov  3  2020 srv
dr-xr-xr-x.  13 root root   0 May 15 10:53 sys
drwxrwxrwt.   7 root root 145 Dec  4  2020 tmp
drwxr-xr-x.  12 root root 144 Dec  4  2020 usr
drwxr-xr-x.  20 root root 262 Dec  4  2020 var
drwxr-xr-x.   2 root root   6 Jun 28 11:13 volume01    # 两个volume** 目录就是构建镜像时,
drwxr-xr-x.   2 root root   6 Jun 28 11:13 volume02	   # 自动挂载的数据卷目录

# 可以看到两个volume实际上是匿名挂载,那么,查看其挂载的具体位置
[root@sa ~]# docker inspect 93d0f80f1c21
.....
 "Mounts": [
            {
                "Type": "volume",
                "Name": "c716f0d4f9*****bddf",
                "Source": "/var/lib/docker/volumes/c716f0d4f9*****bddf/_data",
                "Destination": "volume01",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            },
            {
                "Type": "volume",
                "Name": "14e0ac65b55b5******d156e",
                "Source": "/var/lib/docker/volumes/14e0ac65b55b5*****d156e/_data",
                "Destination": "volume02",
                "Driver": "local",
                "Mode": "",
                "RW": true,
                "Propagation": ""
            }
        ],
......

实际上,这种方式为十分常用,因为我们的目的大多需要自己构建镜像。

假如,构建镜像没有自动挂载卷,那就要自己手动挂载了, -v 卷名:容器内路径 (手动当然 具名挂载 啦)

数据卷容器

作用:实现多个容器间的数据同步,共享。

命令: - -volumes - from container_name

一般把被挂载的容器也就是父容器 称之为数据卷容器

shell 复制代码
# 启动数据卷容器
[root@sa ~]# docker run  -it --name docker-father wax-centos:0.1 /bin/bash

# 启动要和父容器共享的容器(为了简单,使用同一个镜像)
[root@sa ~]# docker run  -it --name docker-child01  --volumes-from docker-father wax-centos:0.1 /bin/bash

# 在启动2个容器
[root@sa ~]# docker run  -it --name docker-child02  --volumes-from docker-father wax-centos:0.1

[root@sa ~]# docker run  -it --name docker-child03  --volumes-from docker-child02 centos

# 查看父容器 (123. 456是03创建的)
[root@8eb24755d5fd volume01]# ls
123.txt  456.txt  child01-create  child02-create

# 查看03容器
[root@sa ~]# docker attach docker-child03
[root@f2eb5b35ae88 /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var  volume01  volume02
[root@f2eb5b35ae88 /]# 

直接给结论:

当我们删除任何一个容器时,其他容器的挂载目录不受影响。

可以看到,child03的镜像是基础的centos镜像,挂载到02上的,其自身并没有挂载volume01 、volume02,但是,使用了

--volumes-from 指令之后依然会挂载这两个目录。

另外,虽然03容器挂载的是02容器,但是停止并删除了02容器后,再在03中的volume01目录下创建文件在父容器、01中依然可以看到。另外,再把父容器删除,03中volume*目录下创建的文件,在01中还是可以看到。所以应该不是简单的拷贝。(原理我也不知奥)

另外,通过inspect可以看到,4个容器的挂载目录都是同一个地方,是不是突然明白了?也就是,即使,你把所有容器都删了,数据应该是还在的,都在宿主机上,需要注意的是,删除容器不影响数据(文件),但是在容器内删除文件是同步的,包括会同步到宿主机。其实这个比较好理解。

再另外,在宿主机上,删除宿主机上的被挂载目录,比如 /var/lib/docker/volumes/***/_data 的_data目录,那么已经启动的容器将无法在其挂载目录下进行操作------无法新建文件。就算手动将_data目录再创建出来,依然不能新建文件。需要停止后重启容器才可以新建。

Dockerfile

Dockerfile 介绍

Dockerfile 是用来构建docker镜像的文件,就是参数脚本!

构建步骤:

  1. 编写一个dockefile文件
  2. docker build 构建成一个镜像
  3. docker run 就可以运行镜像
  4. docker push 可以发布镜像

Dockerfile 构建过程

  1. 每个保留关键字(指令)都是大写
  2. 从上至下依次执行
  3. 使用 '#' 注释
  4. 每一个指令都会创建提交一层新的镜像层

Dockerfile是面向开发的!

Dockerfile:构建文件,定义了所有步骤,源代码

Dockerimages:通过Dockerfile构建的镜像,做种发布和运行

Docker容器:容器就是镜像运行起来提供的服务

Dockerfile的指令

shell 复制代码
FROM 					# 基础镜像,一切从这里构建
MAINTAINER				# 镜像的作者、维护者
RUN						# 镜像构建的时候需要运行的命令
ADD						# 步骤:Ubuntu镜像,可以给他加vim配置文件啥的
WORKDIR					# 镜像的工作目录
VOLUME					# 挂载的目录
EXPOSE					# 保留端口配置
CMD						# 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT				# 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD					# 当构建一个被继承 Dockerfile,这个时候就会运行 ONBUILD 的指令
COPY					# 类似ADD,将文件拷贝至镜像中
ENV						# 构建的时候设置环境变量

实例

以ubuntu基础镜像为例

shell 复制代码
# 先通过 docker pull ubuntu  下载一个官方的最新的ubuntu基础镜像
#这里可以先尝试一下,刚下载的镜像是不支持vim 的,等下通过dockerFile制作一个带有vim的镜像


#下面开始写dockerFile
root@wu:~/my_dockerfile# cat Dockerfile 
FROM ubuntu

MAINTAINER wax<1123@qq.com>

ENV MYPATH /usr/local
WORKDIR $MYPATH

RUN apt-get update
RUN apt-get install -y vim
RUN apt-get install -y net-tools

EXPOSE 80

CMD echo $MYPATH

CMD echo "------end!-------"
CMD /bin/bash

这里需要注意一点,如果是在centos系统上安装的docker,那么大概率执行以上dockerFile将会是失败的。据说是,容器使用的资源库的源是来自宿主机的,也就是centos上不能执行atp-get,而是 yum install命令

接着,我们就来构建自己的镜像(删除了安装vim过程中的打印)

shell 复制代码
# dockerFile的构建命令 可以通过 docker build --help查看
	# -f 指定dockerFile文件  -t 指定目标镜像
	
root@wu:~/my_dockerfile# docker build -f Dockerfile -t my_ubuntu:1.0 ./
Sending build context to Docker daemon  2.048kB
Step 1/11 : FROM ubuntu
 ---> ff0fea8310f3
Step 2/11 : MAINTAINER wax<1123@qq.com>
 ---> Using cache
 ---> 350bceb3effd
Step 3/11 : ENV MYPATH /usr/local
 ---> Using cache
 ---> 792e5469839a
Step 4/11 : WORKDIR $MYPATH
 ---> Using cache
 ---> 1b2933c2de8f
Step 5/11 : RUN apt-get update
 ---> Using cache
 ---> cee70ef91575
Step 6/11 : RUN apt-get  install -y vim
 ---> Using cache
 ---> 628163e35047
Step 7/11 : RUN apt-get  install -y net-tools
 ---> Using cache
 ---> 66ceeb0d5a36
Step 8/11 : EXPOSE 80
 ---> Using cache
 ---> 1a3c9ff3333b
Step 9/11 : CMD echo $MYPATH
 ---> Using cache
 ---> b9007091be62
Step 10/11 : CMD echo "------end!--------"
 ---> Using cache
 ---> 1c85c73c79fe
Step 11/11 : CMD /bin/bash
 ---> Using cache
 ---> 66c8af1a3f90
Successfully built 66c8af1a3f90
Successfully tagged my_ubuntu:1.0
root@wu:~/my_dockerfile# 

完了之后就可以运行my_ubuntu镜像,并且可以直接执行vim 以及 ifconfg等命令。这里就不做演示了。

相关推荐
A陈雷14 分钟前
springboot整合elasticsearch,并使用docker desktop运行elasticsearch镜像容器遇到的问题。
spring boot·elasticsearch·docker
小han的日常17 分钟前
接口自动化环境搭建
运维·自动化
小扳19 分钟前
Docker 篇-Docker 详细安装、了解和使用 Docker 核心功能(数据卷、自定义镜像 Dockerfile、网络)
运维·spring boot·后端·mysql·spring cloud·docker·容器
运维小文23 分钟前
服务器硬件介绍
运维·服务器·计算机网络·缓存·硬件架构
是安迪吖33 分钟前
nfs服务器
运维·服务器
鱼骨不是鱼翅35 分钟前
模拟回显服务器
运维·服务器
运维佬36 分钟前
CentOS 9 配置网卡
linux·centos
politeboy1 小时前
关于k8s中镜像的服务端口被拒绝的问题
云原生·容器·kubernetes
weixin_438197381 小时前
K8S创建云主机配置docker仓库
linux·云原生·容器·eureka·kubernetes
Elastic 中国社区官方博客1 小时前
使用真实 Elasticsearch 进行更快的集成测试
大数据·运维·服务器·数据库·elasticsearch·搜索引擎·集成测试