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等命令。这里就不做演示了。

相关推荐
XIAOHEZIcode1 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户0328472220702 天前
如何搭建本地yum源(上)
运维
武子康2 天前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
大树885 天前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠5 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质5 天前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
Inhand陈工5 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
Alsn865 天前
等待学习-学习目录:Docker 容器安全攻防
学习·安全·docker
酣大智5 天前
ARP代理--工作原理
运维·网络·arp·arp代理
shushangyun_5 天前
2026年快消品B2B系统推荐:支持终端门店订货、促销政策自动化的工具?
java·运维·网络·数据库·人工智能·spring·自动化