Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从 Apache2.0 协议开源。

Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。

容器完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。

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

DockerHub:https://hub.docker.com/

Docker 为什么会出现

  • 项目开发、测试、生产环境的不同,导致环境配置十分繁琐
  • 发布一个项目,项目需要与环境一并打包
  • 在 Windows 下配置的应用环境( JDK, MySQL, Redis 等等),不能跨平台,无法与其他平台(例如 Linux)兼容

Docker 为以上种种问题,给出了解决方案

Docker 能做什么

虚拟机

image-20211007191658093

Docker

image-20211007191744301

两者比较

  • 传统虚拟机技术,虚拟一套硬件然后运行特定的操作系统,在此系统上运行与安装软件

  • Docker 容器内的应用直接运行在宿主机上,没有虚拟硬件和内核,更加轻便

    每个容器间相互隔离,其内都有一个属于自身的文件系统,互不干扰

总之,Docker 具有许多优点:

  • 更快速的交付和部署
  • 更便捷的升级和扩展
  • 更简单的运维
  • 更高效的资源利用

Docker 基本概念

  • 镜像(Image)

    相当于一个 root 文件系统。

  • 容器(Container)

    通过镜像创建的一个或一组应用。

    镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是类,容器是对应的实例。

  • 仓库(Repository)

    存放镜像的地方,分为公开仓库和私有仓库。

    DockerHub、阿里云等都有 Docker 仓库。

Read More:Docker架构

Docker 安装

https://docs.docker.com/get-docker/

https://www.runoob.com/docker/centos-docker-install.html

注意:docker-ce 是社区版,ee 是企业版

配置阿里云镜像加速

https://help.aliyun.com/document_detail/60750.html

Docker 运行流程

image-20211007225658636

Docker 运行原理

Docker 工作方式

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

DockerServer 收到 DockerClient 的指令,就会执行该指令

image-20211007225955965

Docker 为什么比虚拟机快

  • Docker 拥有比虚拟机更少的抽象层

  • Docker 利用的是宿主机的内核,虚拟机需要 GuestOS

    GuestOS:VM(虚拟机)系统(OS)

    HostOS:物理机系统(OS)

    新建容器时,Docker 不需要重新加载一个操作系统内核,省去了引导过程

Docker 常用命令

https://docs.docker.com/reference/

帮助命令

# docker 版本信息
docker version
# docker 系统信息,包括镜像和容器的数量
docker info
# docker 帮助信息
docker 命令 --help

镜像命令

  • docker images

    查看所有本地主机上的镜像

    [root@localhost ~]# docker images
    REPOSITORY            TAG          IMAGE ID       CREATED         SIZE
    tomcat                latest       c662ee449a7e   8 days ago      680MB
    nginx                 latest       f8f4ffc8092c   9 days ago      133MB
    mysql                 latest       2fe463762680   9 days ago      514MB
    hello-world           latest       feb5d9fea6a5   13 days ago     13.3kB
    centos                latest       5d0da3dc9764   3 weeks ago     231MB
    redis                 latest       02c7f2054405   4 weeks ago     105MB
    rabbitmq              management   3e83da0dc938   5 weeks ago     253MB
    portainer/portainer   latest       580c0e4e98b0   6 months ago    79.1MB
    elasticsearch         7.9.2        caa7a21ca06e   12 months ago   763MB
    
    # 解释
    REPOSITORY  镜像的仓库源
    TAG 		镜像的标签
    IMAGE ID 	镜像的id
    CREATED 	镜像的创建时间
    SIZE 		镜像的大小
    
    # 可选项
    -a, --all 		# 列出所有镜像
    -q, --quiet 	# 只显示镜像的id
  • docker search

    搜索镜像

    [root@localhost ~]# docker search mysql
    NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
    mysql                             MySQL is a widely used, open-source relation…   11511     [OK]       
    mariadb                           MariaDB Server is a high performing open sou…   4368      [OK]       
    mysql/mysql-server                Optimized MySQL Server Docker images. Create…   850                  [OK]
    centos/mysql-57-centos7           MySQL 5.7 SQL database server                   91                   
    mysql/mysql-cluster               Experimental MySQL Cluster Docker images. Cr…   88                   
    centurylink/mysql                 Image containing mysql. Optimized to be link…   59                   [OK]
    
    # 可选项
    --filter=STARS=3000 	# 筛选STARS大于3000的镜像
  • docker pull

    下载镜像

  • docker rmi

    删除镜像

    docker rmi -f 镜像id					# 删除指定镜像
    docker rmi -f 镜像id,镜像id,镜像id	 # 删除多个镜像
    docker rmi -f ${docker images -aq}   # 删除全部镜像

容器命令

新建容器并启动
docker run [可选参数] image

# 参数说明
--name="xxxx" 	容器名字,tomcat01,tomcat02,tomcat114514,用于区分容器
-d 				后台方式运行
-it 			交互方式运行
-p 				指定容器端口
	-p 主机端口:容器端口(常用)
	-p 主机ip:主机端口:容器端口
	-p 容器端口
# 测试,启动并进入容器
[root@localhost ~]# docker run -it centos /bin/bash
[root@3726acf7506d /]# ls
bin  dev  etc  home  lib  lib64  lost+found  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
# 退出容器,返回主机
[root@3726acf7506d /]# exit
exit
[root@localhost ~]# ls
anaconda-ks.cfg  initial-setup-ks.cfg
列出所有运行的容器
docker ps 	#列出当前正在运行的容器

# 可选参数
-a 			# 列出所有容器,包括正在运行和历史运行的容器
-n=? 		# 限制显示的容器个数
-q 			# 只显示容器编号

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED       STATUS          PORTS                                                                                                                                                 NAMES
673143eb13f9   portainer/portainer   "/portainer"             7 hours ago   Up 24 minutes   0.0.0.0:8088->9000/tcp, :::8088->9000/tcp                                                                                                             youthful_sutherland
edaaf8cdd660   rabbitmq:management   "docker-entrypoint.s…"   2 weeks ago   Up 24 minutes   4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, :::5672->5672/tcp, 15671/tcp, 15691-15692/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp, :::15672->15672/tcp   rabbit
20e0d6112f64   redis                 "docker-entrypoint.s…"   2 weeks ago   Up 24 minutes   0.0.0.0:6379->6379/tcp, :::6379->6379/tcp                                                                                                             redis
73569253a92c   0716d6ebcc1a          "docker-entrypoint.s…"   2 weeks ago   Up 24 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp                                                                                                  mysql
退出容器
exit 			# 停止容器并退出
ctrl + p + q 	# 不停止容器并退出
删除容器
docker rm 容器id 					# 删除指定容器
docker rm -f ${docker ps -aq} 	 # 删除所有容器
docker ps -a -q|xargs docker rm  # 删除所有容器
启动和停止容器的相关操作
docker start 容器id 		# 启动容器
docker restart 容器id 	# 重启容器
docker stop 容器id 		# 停止当前正在运行的容器
docker kill 容器id 		# 强制停止容器

留坑:

docker run -d xxxx

使用该命令后台启动容器,过一会发现容器自动停止

Docker 容器后台运行,必须要有一个前台进程

其他命令

日志命令
docker logs

# 可选参数
-tf 		# 显示日志
--tail num  # 要显示的日志条数
查看容器进程信息
docker top 容器id
查看镜像元数据
docker inspect 容器id
进入当前正在运行的容器
方式一
# 容器通常是后台运行的方式,有时候需要进入容器,修改一些配置
docker exec -it 容器id [bash或shell]
方式二
docker attach 容器id
区别

docker exec

进入容器后开启一个新的终端

docker attach

进入容器正在执行的终端,不会启动新进程

从容器拷贝文件到主机
docker cp 容器id:容器内路径 目的主机路径

命令总结

image-20211008003026312

Docker 可视化面板

可视化面板工具日常很少使用,仅供测试

  • portainer

    docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
  • Rancher

Docker 镜像加载原理

UnionFS (联合文件系统)

UnionFS 是一种分层、轻量并且高性能的文件系统,它支持对文件系统的修改作为一次提交,进行一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

UnionFS 是 Docker 镜像的基础,镜像可以通过分层进行继承,基于基础镜像,可以制作具体的各种镜像。

特性:

一次同时加载多个文件系统,从外界看,只有一个文件系统,联合加载会把各层系统文件叠加起来,最终的系统文件会包含所有底层文件和目录。

加载原理

Docker 镜像实际上由一层一层的文件系统组成,即 UnionFS。

image-20211008004849954

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

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

平时安装的 CentOS 都是按 GB 计算,为什么 Docker 才几百 MB?

[root@localhost ~]# docker images centos
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
centos       latest    5d0da3dc9764   3 weeks ago   231MB

对于一个精简版 OS,rootfs 很小,只包含最基础的命令、工具和程序库,底层直接用的是 Host 的 Kernel,本身只提供 rootfs。

由此可见,对于不同的 Linux 发行版本,bootfs 基本是一致的,rootfs 会有所差别,即不同发行版可以共用 bootfs。

如何理解分层

观察 Docker 下载镜像的过程,可以看到一层一层的下载:

image-20211008005538015

为什么 Docker 要采用分层的结构?

最大的好处是资源共享,例如,多个镜像由相同的 base 镜像构建,那么宿主机只需在磁盘上保留一份 base 镜像,内存中也只需加载一份 base 镜像,就可以为所有容器服务,镜像的每一层都可以进行共享。

docker image inspect xxxx

查看镜像分层信息

[root@localhost ~]# docker image inspect mysql:5.7
[
    {
        "Id": "sha256:9f35042c6a98363147ae456147643a3d14c484640f13f4ab207cf2c296839baf",
        "RepoTags": [
            "mysql:5.7"
        ],
        "RepoDigests": [
            "mysql@sha256:360c7488c2b5d112804a74cd272d1070d264eef4812d9a9cc6b8ed68c3546189"
        ],
        "Parent": "",
        "Comment": "",
        "Created": "2021-09-28T08:18:07.951097511Z",
        "Container": "3c0f8c1100d9abdbe629b2617727a637847c69e7a46fd142596b78e28087fb3f",
        "ContainerConfig": {
            "Hostname": "3c0f8c1100d9",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "3306/tcp": {},
                "33060/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.12",
                "MYSQL_MAJOR=5.7",
                "MYSQL_VERSION=5.7.35-1debian10"
            ],
            "Cmd": [
                "/bin/sh",
                "-c",
                "#(nop) ",
                "CMD [\"mysqld\"]"
            ],
            "Image": "sha256:9d2d1742b119869ea812b372c42f9187d3d08c53ca2af103cd93e2f415789f53",
            "Volumes": {
                "/var/lib/mysql": {}
            },
            "WorkingDir": "",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": {}
        },
        "DockerVersion": "20.10.7",
        "Author": "",
        "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {
                "3306/tcp": {},
                "33060/tcp": {}
            },
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                "GOSU_VERSION=1.12",
                "MYSQL_MAJOR=5.7",
                "MYSQL_VERSION=5.7.35-1debian10"
            ],
            "Cmd": [
                "mysqld"
            ],
            "Image": "sha256:9d2d1742b119869ea812b372c42f9187d3d08c53ca2af103cd93e2f415789f53",
            "Volumes": {
                "/var/lib/mysql": {}
            },
            "WorkingDir": "",
            "Entrypoint": [
                "docker-entrypoint.sh"
            ],
            "OnBuild": null,
            "Labels": null
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 448231647,
        "VirtualSize": 448231647,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/ada332e1d9bf25db7e8f7cf83db2c4a3f8426eb7bdfe0be3664fb3ffb403a6a1/diff:/var/lib/docker/overlay2/2ec01820c2d54238f6ea0bccfe4824430554050bec04fa4a4777d077036428e3/diff:/var/lib/docker/overlay2/be77c514734d30c85acc52166dd3b6265785b1917fe36f98c726dc35941b310e/diff:/var/lib/docker/overlay2/191ed40243b6aea8b7cca5c4030d07be5b08880d62f4a62022f4d4097e27c366/diff:/var/lib/docker/overlay2/ad1625acde0a506677de7e116085d2cf2f3d7914970229be050a26f2fac8c425/diff:/var/lib/docker/overlay2/761fde36efc5dd18f45f028a74f5d552fe881cb082aa1977d8705cf339089f72/diff:/var/lib/docker/overlay2/331c558c501038e534ba76367e0f63663733d96da930496306997e37b7bd7b45/diff:/var/lib/docker/overlay2/f845ddda6d164fa72587002cdd7244f533d5e87d8d07fe32af9185a69573a43f/diff:/var/lib/docker/overlay2/2bb1bf94a07e00abcb648e69d9dfdba79d04919e6510503f0915197f39cbc05f/diff:/var/lib/docker/overlay2/8d06674bf384b552980456abebc1c9b28602cbf6119a919510575cb7e6870d95/diff",
                "MergedDir": "/var/lib/docker/overlay2/56c6d33bf549a5de2cef76caee3a0b7d622b71ca150c97aec98fd65bc0daa20f/merged",
                "UpperDir": "/var/lib/docker/overlay2/56c6d33bf549a5de2cef76caee3a0b7d622b71ca150c97aec98fd65bc0daa20f/diff",
                "WorkDir": "/var/lib/docker/overlay2/56c6d33bf549a5de2cef76caee3a0b7d622b71ca150c97aec98fd65bc0daa20f/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:476baebdfbf7a68c50e979971fcd47d799d1b194bcf1f03c1c979e9262bcd364",
                "sha256:f2f5bad82361770ad8ec94444e6e0499d79dddf8100f21ca24e9cddec85009d4",
                "sha256:96fe563c6126ed009136798afd7d88f82205232e93d7df5873e57beecc450249",
                "sha256:44bc6574c36fc508f365a3f9313bd129074115cd3fd247b7de741d2b88d455af",
                "sha256:e333ff907af7d9103e9cb74f3ee450be324333a1b4db08fea9bb8c6f6bf1846d",
                "sha256:4cffbf4e4fe3490ea3df6d9567dc2cef46c242be61673da9ec2e0cffc4994229",
                "sha256:42417c6d26fccb39f58ad78542e033654b1b62ef4f768ff702596a8d4fe702b4",
                "sha256:3622b32457db96d80047fe5fd536a7942d48f10e72d76a3827a0c124f86e8fac",
                "sha256:9955f67940c9f8b2d3c51225fb646e5b704f83af9634ef5e7046e9d0374a9b44",
                "sha256:b53fd933647793b71756e69426dc8e435e8e283560ba14539d9746dbeb9d8927",
                "sha256:b2cd93f4f068d080f1d4759a651a6c39b817f8f4c8a02be538292c4674309410"
            ]
        },
        "Metadata": {
            "LastTagTime": "0001-01-01T00:00:00Z"
        }
    }
]

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

Docker 镜像都是只读的,当容器启动时,就将新一层(可写)加载到镜像之上。

这一层就是通常所说的容器层,容器层之下的都是镜像层。

容器数据卷

容器数据卷是什么

如果数据都存储在容器内部,一旦删除容器,数据就会丢失

另外,我们需要有一个容器间数据共享的技术,并将容器中产生的数据同步到本地

这就是卷技术,可以将容器内的目录,挂载到 Linux 本机上

数据卷使用

docker run -it -v 主机目录:容器目录

# 测试
[root@localhost home]# docker run -it -v /home/test:/home centos /bin/bash

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS         PORTS                                                                                                                                                 NAMES
850915c6c1e6   centos                "/bin/bash"              4 minutes ago   Up 4 minutes   

# 查看容器信息,找到 Mounts ,即挂载属性
[root@localhost ~]# docker inspect 850915c6c1e6

image-20211012203527910

具名挂载和匿名挂载

# 匿名挂载
docker run -d -P --name nginx01 -v /etc/nginx nginx

# 具名挂载
docker run -d -P --name nginx02 -v named-nginx:/etc/nginx nginx

具名挂载的特点是:-v 卷名:容器内路径

通过具名挂载可以方便的找到卷

且大多数情况下使用具名挂载

# 区分具名、匿名、指定路径挂载

-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径::容器内路径 # 指定路径挂载
# 通过 -v 容器内路径: ro rw 改变读写权限

# ro readonly 只读,即只能通过宿主机操作,容器内部无法操作
# rw readwrite 可读写

docker run -d -P --name nginx02 -v jumping-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v jumping-nginx:/etc/nginx:rw nginx

DockerFile

DockerFile 就是用来构建 Docker 镜像的文件,也就是命令脚本

通过这个脚本可以生成镜像

一个简单的演示

# 编写一个脚本文件 dockerfile01
FROM centos

VOLUME ["volume01","volume02"] #匿名挂载

CMD echo "eeeeeeeeeeend"

CMD /bin/bash
# 运行脚本
docker build -f dockerfile01 -t rayucan/centos:1.0 .

指令详解

https://www.runoob.com/docker/docker-dockerfile.html

构建步骤

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

DockerHub CentOS 官方的 DockerFile

FROM scratch
ADD centos-7-x86_64-docker.tar.xz /

LABEL \
    org.label-schema.schema-version="1.0" \
    org.label-schema.name="CentOS Base Image" \
    org.label-schema.vendor="CentOS" \
    org.label-schema.license="GPLv2" \
    org.label-schema.build-date="20201113" \
    org.opencontainers.image.title="CentOS Base Image" \
    org.opencontainers.image.vendor="CentOS" \
    org.opencontainers.image.licenses="GPL-2.0-only" \
    org.opencontainers.image.created="2020-11-13 00:00:00+00:00"

CMD ["/bin/bash"]

总结

DockerFile 是面向开发的,发布项目或发布镜像都需要编写 DockerFile,这个文件并不复杂。

DockerFile:构建文件,定义了一切步骤

DockerImages:通过 DockerFile 构建生成的镜像,即最终发布和运行的产品

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