docker 概述
docker 官网:http://www.docker.com
官网文档: https://docs.docker.com/get-docker/
Docker Hub官网:https://hub.docker.com (仓库)
什么是 docker
docker 是一个开源的容器化平台,可以将应用程序及其所有依赖项打包在一个独立、可移植的容器中。它通过利用容器技术来实现应用程序的快速部署、可扩展性和跨平台性。
docker 的核心概念是容器,它是一个完全独立、可执行的软件包,包含了应用程序的代码、运行时环境、系统工具和系统库。与虚拟机相比,容器更加轻量级,因为它们共享主机操作系统的内核,并且没有额外的操作系统负担。
使用 docker,开发人员可以将应用程序及其所有依赖项打包在一个容器中,然后将该容器部署到任何支持 docker 的主机上,无论是开发环境、测试环境还是生产环境。容器提供了一致的运行环境,确保应用程序在不同环境中以相同的方式运行。
docker 还提供了一套强大的命令行工具和 API,使得容器的创建、部署、启动、停止和管理变得简单和可靠。它支持容器的版本控制和快速部署,方便应用程序的更新和回滚。
使用 docker,开发人员可以更快速地构建和交付应用程序,运维人员可以更轻松地管理和扩展应用程序的部署。docker 已经成为现代化软件开发和部署的重要工具,被广泛应用于云计算、微服务架构、持续集成和持续部署等领域。
docker 的发展历程
问题:
一款产品从开发到上线,从操作系统,到运行环境,再到应用配置。作为开发+运维之间的协作我们需要关心很多东西,这也是很多互联网公司都不得不面对的问题,特别是各种版本的迭代之后,不同版本环境的兼容,对运维人员是极大的考验!
环境配置如此麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。解决开发人员说的“ 在我的机器上可正常工作”的问题。
之前在服务器配置一个应用的运行环境,要安装各种软件,就拿一个基本的工程项目的环境来说吧,Java/Tomcat/MySQL/JDBC 驱动包等。安装和配置这些东西有多麻烦就不说了,它还不能跨平台。假如我们是在 Windows 上安装的这些环境,到了 Linux 又得重新装。况且就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。
传统上认为,软件编码开发/测试结束后,所产出的成果即是程序或是能够编译执行的二进制字节码文件等(Java 为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让维运团队得以部署应用程式,开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。
解决方案:
docker 之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案。
docker 镜像的设计,使得 docker 得以打破过去「程序即应用」的观念。通过 docker 镜像 ( images ) 将应用程序所需要的系统环境,由下而上打包,达到应用程序跨平台间的无缝接轨运作。
第一个概念:docker 里面所有的应用,打包为镜像。版本迭代管理…
docker 的思想来自于集装箱,集装箱解决了什么问题?在一艘大船上,可以把货物规整的摆放起来。并且各种各样的(docker 镜像容器等都是标准化)货物被集装箱标准化了,集装箱和集装箱(docker 容器)之间不会互相影响。那么我就不需要专门运送水果的船和专门运送化学品的船了。只要这些货物在集装箱里封装的好好的,那我就可以用一艘大船把他们都运走。
docker 就是类似的理念。
- 镜像标准化
- 容器化隔离
docker 历史
2010年,几个搞IT的年轻人,在美国旧金山成立了一家名叫“dotCloud”的公司。
这家公司主要提供基于PaaS的云计算技术服务。具体来说,是和LXC有关的容器技术。
后来,dotCloud 公司将自己的容器技术进行了简化和标准化,并命名为—— docker。
docker 技术诞生之后,并没有引起行业的关注。而 dotCloud 公司,作为一家小型创业企业,在激烈的竞争之下,也步履维艰。
正当他们快要坚持不下去的时候,脑子里蹦出了“开源”的想法。
什么是“开源”?开源,就是开放源代码。也就是将原来内部保密的程序源代码开放给所有人,然后让大家一起参与进来,贡献代码和意见。
有的软件是一开始就开源的。也有的软件,是混不下去,创造者又不想放弃,所以选择开源。自己养不活,就吃“百家饭”嘛。
2013年3月,dotCloud 公司的创始人之一,docker 之父,28岁的 Solomon Hykes 正式决定,将 docker 项目开源。
不开则已,一开惊人。
越来越多的 IT工程师发现了 docker 的优点,然后蜂拥而至,加入 docker 开源社区。
docker 的人气迅速攀升,速度之快,令人瞠目结舌。
开源当月,docker 0.1 版本发布。此后的每一个月,docker 都会发布一个版本。到2014年6月9日,docker 1.0 版本正式发布。
此时的 docker,已经成为行业里人气最火爆的开源技术,没有之一。甚至像 Google、微软、Amazon、VMware 这样的巨头,都对它青睐有加,表示将全力支持。
软件技术革命从来都是懒人推动的。
docker 和容器技术为什么会这么火爆?说白了,就是因为它“轻”。
重量级的环境隔离:虚拟机
在容器技术之前,业界的网红是虚拟机。虚拟机技术的代表,是 VMWare和 OpenStack。
相信很多人都用过虚拟机。虚拟机,就是在你的操作系统里面,装一个软件,然后通过这个软件,再模拟一台甚至多台“子电脑”出来。
在“子电脑”里,你可以和正常电脑一样运行程序,例如开QQ。如果你愿意,你可以变出好几个“子电脑”,里面都开上QQ。“子电脑”和“子电脑”之间,是相互隔离的,互不影响。
虚拟机属于虚拟化技术。而 docker 这样的容器技术,也是虚拟化技术,属于轻量级的虚拟化。
虚拟机虽然可以隔离出很多“子电脑”,但占用空间更大,启动更慢,虚拟机软件可能还要花钱(例如 VMWare)。
而容器技术恰好没有这些缺点。它不需要虚拟出整个操作系统,只需要虚拟一个小规模的环境(类似“沙箱”)。
它启动时间很快,几秒钟就能完成。而且,它对资源的利用率很高(一台主机可以同时运行几千个 docker 容器)。此外,它占的空间很小,虚拟机一般要几GB到几十GB的空间,而容器只需要MB级甚至KB级。
正因为如此,容器技术受到了热烈的欢迎和追捧,发展迅速。
docker 理念
docker 是基于 Go 语言实现的云开源项目。
docker 的主要目标是 “Build,Ship and Run Any App , Anywhere”,也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个 WEB 应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。
Linux 容器技术的出现就解决了这样一个问题,而 docker 就是在它的基础上发展过来的。将应用运行在 docker 容器上面,而 docker 容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。只需要一次配置好环境,换到别的机子上就可以一键部署好,大大简化了操作。
之前的虚拟机技术
虚拟机(virtual machine)就是带环境安装的一种解决方案。
它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。这类虚拟机完美的运行了另一套系统,能够使应用程序,操作系统和硬件三者之间的逻辑不变。
虚拟机的缺点:
-
资源占用多
-
冗余步骤多
-
启动慢
容器虚拟化技术
由于前面虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境中的软件都能始终如一地运行。
比较了 docker 和传统虚拟化方式的不同之处:
- 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;
- 而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。
- 每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。
虚拟机与容器的对比
docker是怎么工作的
docker 是一个 Client-Server 结构的系统,docker 守护进程运行在主机上, 然后通过 socket 连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们前面说到的集装箱。
为什么Docker比较 VM 快
- docker 有着比虚拟机更少的抽象层。由于 docker 不需要 Hypervisor 实现硬件资源虚拟化,运行在 docker 容器上的程序直接使用的都是实际物理机的硬件资源。因此在 CPU、内存利用率上 docker 将会在效率上有明显优势。
- docker 利用的是宿主机的内核,而不需要 Guest OS。因此,当新建一个容器时,docker 不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引寻、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载 Guest OS,返个新建过程是分钟级别的。而docker 由于直接利用宿主机的操作系统,则省略了返个过程,因此新建一个 docker 容器只需要几秒钟。
docker 对于开发/运维(DevOps)的帮助
更快速的应用交付和部署:
传统的应用开发完成后,需要提供一堆安装程序和配置说明文档,安装部署后需根据配置文档进行繁杂的配置才能正常运行。docker 化之后只需要交付少量容器镜像文件,在正式生产环境加载镜像并运行即可,应用安装配置在镜像里已经内置好,大大节省部署配置和测试验证时间。
更便捷的升级和扩缩容:
随着微服务架构和 docker 的发展,大量的应用会通过微服务方式架构,应用的开发构建将变成搭乐高积木一样,每个 docker 容器将变成一块“积木”,应用的升级将变得非常容易。当现有的容器不足以支撑业务处理时,可通过镜像运行新的容器进行快速扩容,使应用系统的扩容从原先的天级变成分钟级甚至秒级。
更简单的系统运维:
应用容器化运行后,生产环境运行的应用可与开发、测试环境的应用高度一致,容器会将应用程序相关的环境和状态完全封装起来,不会因为底层基础架构和操作系统的不一致性给应用带来影响,产生新的 BUG。当出现程序异常时,也可以通过测试环境的相同容器进行快速定位和修复。
更高效的计算资源利用:
docker 是内核级虚拟化,其不像传统的虚拟化技术一样需要额外的 Hypervisor [管理程序] 支持,所以在一台物理机上可以运行很多个容器实例,可大大提升物理服务器的 CPU 和内存的利用率。
docker 的优点
docker 具有以下几个主要的优点:
- 轻量级和快速:相比传统虚拟化技术,docker 容器更为轻量级,因为它们共享主机操作系统的内核,避免了额外的操作系统负担。这使得容器的启动、停止和迁移更加快速,也节约了硬件资源和时间成本。
- 可移植性和一致性:docker 容器提供了一种统一的部署环境,可以在不同的操作系统和硬件平台上运行。开发人员可以在其本地开发环境中构建容器,然后将其部署到测试环境、生产环境或云平台上,而无需担心环境差异导致的应用程序问题。这种可移植性确保了应用程序在不同环境中以相同的方式运行。
- 高效的资源利用:docker 容器可以在同一主机上运行多个相互隔离的容器,共享主机的操作系统和硬件资源。这使得容器可以更好地利用主机资源,提高了资源利用率。
- 简化部署和管理:docker 提供了一套强大的命令行工具和 API,使得容器的创建、部署、启动、停止和管理变得简单和可靠。容器可以通过复制和共享镜像来快速创建多个相同的实例,实现快速部署和扩展。docker 还支持容器的版本控制和快速部署,方便应用程序的更新和回滚。
- 提高开发效率:docker 容器通过将应用程序及其所有依赖项打包在一个容器中,简化了开发环境的搭建和配置过程。开发人员可以在容器中构建、测试和调试应用程序,减少了开发环境之间的不一致性和冲突。
总的来说,docker 的优点包括轻量级、快速、可移植性、一致性、高效的资源利用和简化的部署和管理。它提供了一种现代化的应用程序开发、交付和运行方式,已经被广泛应用于云计算、微服务架构、持续集成和持续部署等领域。
docker 的应用场景
docker 在许多不同的应用场景中都有广泛的应用,下面列举了一些主要的应用场景:
- 应用程序的快速交付和部署:docker 可以将应用程序及其所有依赖项打包在一个容器中,实现应用程序的快速交付和部署。开发人员可以在其本地开发环境中构建容器,然后将其部署到测试环境、生产环境或云平台上,无需担心环境差异导致的应用程序问题。
- 微服务架构:docker 容器非常适合构建和管理微服务架构。每个微服务可以被打包在一个独立的容器中,并且可以独立部署、扩展和管理。这样可以实现服务之间的解耦和灵活的水平扩展,同时简化了部署和维护的复杂性。
- 持续集成和持续部署:docker 可以与持续集成和持续部署工具集成,实现自动化的构建、测试和部署流程。开发人员可以使用 docker 容器来创建一致的开发和测试环境,并且可以在不同的环境中快速部署和测试应用程序。
- 多租户环境:docker 的容器技术可以实现资源的隔离和安全性,使得在多租户环境下更容易管理和部署应用程序。每个租户可以被分配一个独立的容器,可以在同一主机上运行多个容器实例,提高了资源利用率。
- 快速搭建开发环境:docker 可以帮助开发人员快速搭建开发环境,而无需手动配置和安装依赖项。开发人员可以使用预先配置的 docker 镜像,快速创建包含所需开发工具和库的开发环境。
- 跨平台开发和测试:docker 容器可以在不同的操作系统和硬件平台上运行,提供了一种统一的部署环境。这使得开发人员可以在本地开发环境中构建和测试应用程序,并将其部署到不同的目标平台上,而无需进行额外的修改和配置。
总的来说,docker 的应用场景非常广泛,可以用于快速交付和部署应用程序、构建微服务架构、实现持续集成和持续部署、管理多租户环境,以及快速搭建开发环境和跨平台开发和测试等。它已经成为现代化软件开发和部署的重要工具。
docker 的三大必要概念
docker 的三大必要概念——镜像、容器、仓库
docker 架构图
镜像(image):模版。(web项目:1、环境 2、配置变量 3、上线项目 4、配置项目需要的静态文件)打包成镜像
docker 镜像(image)就是一个只读的模板。镜像可以用来创建 docker 容器,一个镜像可以创建很多容器。
容器(container):通过镜像来启动运行时容器,就是我们自己封装部署的东西(项目、环境、日志…)
docker 利用容器(container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。可以把容器看做是一个简易版的 Linux 环境(包括 root 用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。。
仓库(repository):私有仓库(公司内部)、公有仓库(docker hub,需要配置镜像加速)
仓库(repository)是集中存放镜像文件的场所。仓库(repository)和仓库注册服务器(registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。仓库分为公开仓库(public)和私有仓库(private)两种形式。最大的公开仓库是 docker hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。
国内的公开仓库包括阿里云、网易云等
小结:
需要正确的理解仓库/镜像/容器这几个概念 :
- image:文件生成的容器实例,本身也是一个文件,称为镜像文件。
- 一个容器运行一种服务,当我们需要的时候,就可以通过 docker 客户端用镜像创建一个对应的运行实例,也就是我们的容器。
- 至于仓库,就是放了一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候从仓库中拉下来就可以了。那么我就不需要专门运送水果的船和专门运送化学品的船了。只要这些货物在集装箱里封装的好好的,那我就可以用一艘大船把他们都运走。
docker就是类似的理念。
docker 安装
环境要求
本次使用的是云服务器,版本是 centos,要求版本在3.10以上
[root@iZbp15293q8kgzhur7n6kvZ /]# uname -r
3.10.0-1160.108.1.el7.x86_64
[root@iZbp15293q8kgzhur7n6kvZ /]# 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/centos/
- 保证环境是满足要求的
- 卸载以前旧的 docker
yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logrotate \docker-logrotate \docker-engine
# 安装 gcc 环境
yum -y install gcc
yum -y install gcc-c++
- 安装 docker 需要的仓库地址配置
yum install -y yum-utils# download.docker.com 很卡,不推荐使用
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo# 在这里就要使用国内的镜像。
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
- 安装 docker
# 更新 yum 索引
yum makecache fast# 安装 docker
yum install -y docker-ce docker-ce-cli containerd.io
- 查看 docker 是否安装成功
# 查看 docker 版本
[root@iZbp15293q8kgzhur7n6kvZ /]# docker -v
Docker version 25.0.4, build 1a576c5
- 启动docker
systemctl start docker
- 下载并运行 hello-world 镜像
docker run hello-world
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:6352af1ab4ba4b138648f8ee88e63331aae519946d3b67dae50c313c6fc8200f
Status: Downloaded newer image for hello-world:latestHello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:1. The Docker client contacted the Docker daemon.2. The Docker daemon pulled the "hello-world" image from the Docker Hub.(amd64)3. The Docker daemon created a new container from that image which runs theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare images, automate workflows, and more with a free Docker ID:https://hub.docker.com/For more examples and ideas, visit:https://docs.docker.com/get-started/
出现这个证明我们 docker 已经启动成功了
卸载 docker
如果以后想要卸载 docker,执行以下命令
# 停止docker服务
systemctl stop docker
# 删除docker
yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin docker-ce-rootless-extras
# 删除docker产生的文件夹
rm -rf /var/lib/docker
rm -rf /var/lib/containerd
阿里云镜像加速
在阿里云工作台中搜索容器镜像服务 ACR,每个人的镜像加速器都是不同的,执行以下命令即可
了解 hello-world 做了什么
docker run 镜像名称
- 寻找镜像(如果本地存在,则通过镜像启动容器,不存在,去公开仓库 dockerhub 寻找镜像)
- 下载镜像(如果公开仓库 dockerhub 存在这个镜像,拉取到本地,然后执行,不存在,就直接报错)
下载一个不存在的镜像
# 随便输入一个不存在的镜像,找不到就报错
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run asjfdhakjshfdajshfakjsfhd
Unable to find image 'asjfdhakjshfdajshfakjsfhd:latest' locally
docker: Error response from daemon: pull access denied for asjfdhakjshfdajshfakjsfhd, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.
查看已经本地下载好的镜像
docker images
[root@iZbp15293q8kgzhur7n6kvZ /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 10 months ago 13.3kB
docker 常用命令
基本命令
# 查看 docker 版本
docker version
# 查看一些 docker 的详细信息
docker info
帮助命令(–help),linux必须要会看帮助文档
docker --help
[root@iZbp15293q8kgzhur7n6kvZ /]# docker --helpUsage: docker [OPTIONS] COMMANDA self-sufficient runtime for containersCommon Commands:run Create and run a new container from an imageexec Execute a command in a running containerps List containersbuild Build an image from a Dockerfilepull Download an image from a registrypush Upload an image to a registryimages List imageslogin Log in to a registrylogout Log out from a registrysearch Search Docker Hub for imagesversion Show the Docker version informationinfo Display system-wide informationManagement Commands:builder Manage buildsbuildx* Docker Buildx (Docker Inc., v0.13.0)compose* Docker Compose (Docker Inc., v2.24.7)container Manage containerscontext Manage contextsimage Manage imagesmanifest Manage Docker image manifests and manifest listsnetwork Manage networksplugin Manage pluginssystem Manage Dockertrust Manage trust on Docker imagesvolume Manage volumesSwarm Commands:swarm Manage SwarmCommands:attach Attach local standard input, output, and error streams to a running containercommit Create a new image from a container's changescp Copy files/folders between a container and the local filesystemcreate Create a new containerdiff Inspect changes to files or directories on a container's filesystemevents Get real time events from the serverexport Export a container's filesystem as a tar archivehistory Show the history of an imageimport Import the contents from a tarball to create a filesystem imageinspect Return low-level information on Docker objectskill Kill one or more running containersload Load an image from a tar archive or STDINlogs Fetch the logs of a containerpause Pause all processes within one or more containersport List port mappings or a specific mapping for the containerrename Rename a containerrestart Restart one or more containersrm Remove one or more containersrmi Remove one or more imagessave Save one or more images to a tar archive (streamed to STDOUT by default)start Start one or more stopped containersstats Display a live stream of container(s) resource usage statisticsstop Stop one or more running containerstag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGEtop Display the running processes of a containerunpause Unpause all processes within one or more containersupdate Update configuration of one or more containerswait Block until one or more containers stop, then print their exit codesGlobal Options:--config string Location of client config files (default "/root/.docker")-c, --context string Name of the context to use to connect to the daemon (overrides DOCKER_HOST env var and defaultcontext set with "docker context use")-D, --debug Enable debug mode-H, --host list Daemon socket to connect to-l, --log-level string Set the logging level ("debug", "info", "warn", "error", "fatal") (default "info")--tls Use TLS; implied by --tlsverify--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")--tlskey string Path to TLS key file (default "/root/.docker/key.pem")--tlsverify Use TLS and verify the remote-v, --version Print version information and quitRun 'docker COMMAND --help' for more information on a command.For more help on how to use Docker, head to https://docs.docker.com/go/guides/
docker COMMAND --help
可以继续查看其它命令的帮助文档
镜像命令
展示所有的镜像
docker images
[root@iZbp15293q8kgzhur7n6kvZ /]# docker images
# REPOSITORY:镜像仓库
# TAG:标签(版本号),latest 代表最新版
# IMAGE ID:镜像ID,是简化后的
# CREATED:创建时间
# SIZE:镜像大小
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 10 months ago 13.3kB
参数:
-a
:等价于--all
,显示所有的镜像,包括中间层镜像
默认情况下,docker images
命令只显示顶层镜像,即那些没有被其他镜像所依赖的镜像。而使用-a
参数,会显示所有的镜像,包括中间层镜像。中间层镜像是构建镜像过程中生成的临时镜像,它们可能被其他镜像所依赖。
-q
:等价于--quiet
,只展示镜像的 id
搜索镜像
docker search 镜像名称
[root@iZbp15293q8kgzhur7n6kvZ /]# docker search mysql
# STARS:星数,最多的通常为我们要找的官方镜像
NAME DESCRIPTION STARS OFFICIAL
mysql MySQL is a widely used, open-source relation… 14944 [OK]
mariadb MariaDB Server is a high performing open sou… 5703 [OK]
phpmyadmin phpMyAdmin - A web interface for MySQL and M… 960 [OK]
percona Percona Server is a fork of the MySQL relati… 626 [OK]
bitnami/mysql Bitnami MySQL Docker Image 110
bitnami/mysqld-exporter 6
cimg/mysql 3
ubuntu/mysql MySQL open source fast, stable, multi-thread… 61
rapidfort/mysql RapidFort optimized, hardened image for MySQL 25
rapidfort/mysql8-ib RapidFort optimized, hardened image for MySQ… 9
google/mysql MySQL server for Google Compute Engine 25
elestio/mysql Mysql, verified and packaged by Elestio 0
rapidfort/mysql-official RapidFort optimized, hardened image for MySQ… 9
bitnamicharts/mysql 0
hashicorp/mysql-portworx-demo 0
databack/mysql-backup Back up mysql databases to... anywhere! 111
linuxserver/mysql A Mysql container, brought to you by LinuxSe… 41
mirantis/mysql 0
docksal/mysql MySQL service images for Docksal - https://d… 0
linuxserver/mysql-workbench 55
vitess/mysqlctld vitess/mysqlctld 1
eclipse/mysql Mysql 5.7, curl, rsync 1
drupalci/mysql-5.5 https://www.drupal.org/project/drupalci 3
drupalci/mysql-5.7 https://www.drupal.org/project/drupalci 0
datajoint/mysql MySQL image pre-configured to work smoothly … 2
参数:
-f
:等价于--filter
,根据条件搜索想要的镜像
# 过滤出星数不小于500的镜像
docker search mysql -f=STARS=500
[root@iZbp15293q8kgzhur7n6kvZ /]# docker search mysql -f=STARS=500
NAME DESCRIPTION STARS OFFICIAL
mysql MySQL is a widely used, open-source relation… 14944 [OK]
mariadb MariaDB Server is a high performing open sou… 5703 [OK]
phpmyadmin phpMyAdmin - A web interface for MySQL and M… 960 [OK]
percona Percona Server is a fork of the MySQL relati… 626 [OK]
拉取镜像
docker pull 镜像名称:版本号
# 如果不加版本号,则拉取最新版本的镜像
[root@iZbp15293q8kgzhur7n6kvZ /]# docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
72a69066d2fe: Pull complete
93619dbc5b36: Pull complete
99da31dd6142: Pull complete
626033c43d70: Pull complete
37d5d7efb64e: Pull complete
ac563158d721: Pull complete
d2ba16033dad: Pull complete
688ba7d5c01a: Pull complete
00e060b6d11d: Pull complete
1c04857f594f: Pull complete
4d7cfa90e6ea: Pull complete
e0431212d27d: Pull complete
# 签名校验id,主要是保证不会被掉包
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest# 下载指定版本
[root@iZbp15293q8kgzhur7n6kvZ /]# docker pull mysql:5.7
5.7: Pulling from library/mysql
72a69066d2fe: Already exists
93619dbc5b36: Already exists
99da31dd6142: Already exists
626033c43d70: Already exists
37d5d7efb64e: Already exists
ac563158d721: Already exists
d2ba16033dad: Already exists
0ceb82207cd7: Pull complete
37f2405cae96: Pull complete
e2482e017e53: Pull complete
70deed891d42: Pull complete
Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7
删除镜像
docker rmi 镜像名称或镜像ID
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rmi hello-world
Error response from daemon: conflict: unable to remove repository reference "hello-world" (must force) - container 578ad7d53c37 is using its referenced image d2c94e258dcb
这里删除失败是因为 hello-world 镜像有容器正在运行,如果要删除只能强制删除
参数:
-f
:等价于--force
,强制删除镜像,即使有容器正在运行
# 强制删除
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rmi -f hello-world
Untagged: hello-world:latest
Untagged: hello-world@sha256:6352af1ab4ba4b138648f8ee88e63331aae519946d3b67dae50c313c6fc8200f
Deleted: sha256:d2c94e258dcb3c5ac2798d32e1249e42ef01cba4841c2234249495f87264ac5a
# 根据镜像ID删除镜像
[root@iZbp15293q8kgzhur7n6kvZ /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mysql 5.7 c20987f18b13 2 years ago 448MB
mysql latest 3218b38490ce 2 years ago 516MB
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rmi -f 3218b38490ce
Untagged: mysql:latest
Untagged: mysql@sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709
Deleted: sha256:3218b38490cec8d31976a40b92e09d61377359eab878db49f025e5d464367f3b
Deleted: sha256:aa81ca46575069829fe1b3c654d9e8feb43b4373932159fe2cad1ac13524a2f5
Deleted: sha256:0558823b9fbe967ea6d7174999be3cc9250b3423036370dc1a6888168cbd224d
Deleted: sha256:a46013db1d31231a0e1bac7eeda5ad4786dea0b1773927b45f92ea352a6d7ff9
Deleted: sha256:af161a47bb22852e9e3caf39f1dcd590b64bb8fae54315f9c2e7dc35b025e4e3
Deleted: sha256:feff1495e6982a7e91edc59b96ea74fd80e03674d92c7ec8a502b417268822ff
这里镜像ID不一定要是完整的,只要有唯一的一个镜像ID开头满足你输入的ID即可
# 只输入开头的c20即可删除
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rmi -f c20
Untagged: mysql:5.7
Untagged: mysql@sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
Deleted: sha256:c20987f18b130f9d144c9828df630417e2a9523148930dc3963e9d0dab302a76
Deleted: sha256:6567396b065ee734fb2dbb80c8923324a778426dfd01969f091f1ab2d52c7989
Deleted: sha256:0910f12649d514b471f1583a16f672ab67e3d29d9833a15dc2df50dd5536e40f
Deleted: sha256:6682af2fb40555c448b84711c7302d0f86fc716bbe9c7dc7dbd739ef9d757150
Deleted: sha256:5c062c3ac20f576d24454e74781511a5f96739f289edaadf2de934d06e910b92
Deleted: sha256:8805862fcb6ef9deb32d4218e9e6377f35fb351a8be7abafdf1da358b2b287ba
Deleted: sha256:872d2f24c4c64a6795e86958fde075a273c35c82815f0a5025cce41edfef50c7
Deleted: sha256:6fdb3143b79e1be7181d32748dd9d4a845056dfe16ee4c827410e0edef5ad3da
Deleted: sha256:b0527c827c82a8f8f37f706fcb86c420819bb7d707a8de7b664b9ca491c96838
Deleted: sha256:75147f61f29796d6528486d8b1f9fb5d122709ea35620f8ffcea0e0ad2ab0cd0
Deleted: sha256:2938c71ddf01643685879bf182b626f0a53b1356138ef73c40496182e84548aa
Deleted: sha256:ad6b69b549193f81b039a1d478bc896f6e460c77c1849a4374ab95f9a3d2cea2
删除所有镜像,慎用
重新拉取几个镜像进行测试
删除命令如下:
docker rmi -f $(docker images -aq)
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rmi -f $(docker images -aq)
Untagged: hello-world:latest
Untagged: hello-world@sha256:2498fce14358aa50ead0cc6c19990fc6ff866ce72aeb5546e1d59caac3d0d60f
Deleted: sha256:feb5d9fea6a5e9606aa995e879d862b825965ba48de054caab5ef356dc6b3412
Deleted: sha256:e07ee1baac5fae6a26f30cabfe54a36d3402f96afda318fe0a96cec4ca393359
容器命令
有镜像才能创建容器,我们这里使用 centos 的镜像来测试
根据镜像创建并运行一个容器
docker run 镜像名称
[root@iZbp15293q8kgzhur7n6kvZ /]# docker pull centos
Using default tag: latest
latest: Pulling from library/centos
a1d0c7532777: Pull complete
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
docker.io/library/centos:latest
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run centos
# docker run 执行时自带了 docker pull命令
参数:
--name
:为容器指定一个名称,不指定则随机一个名字-d
:等价于--detach
,后台启动容器并打印容器id-i
:等价于--interactive
,让容器用交互的方式启动-t
:等价于--tty
,给容器分配一个伪终端,例如/bin/bash
-p
:等价于--publish
,指定端口映射(主机访问的端口:容器端口)-P
:大写P,等价于--publish-all
,将所有公开的端口发布到随机端口
用交互的方式启动容器,并进入容器内部
docker run -it 镜像名称
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -it centos
[root@c98e20511eb4 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
退出容器
# 命令,退出后容器停止运行
exit
# 快捷键,退出后容器不会停止
Ctrl + P + Q
查看正在运行中的容器
docker ps
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c98e20511eb4 centos "/bin/bash" 2 hours ago Up 2 hours vibrant_rosalind
参数:
-a
:等价于--all
,显示所有容器,包括已经停止的-q
:等价于--quiet
,只展示容器ID
容器的启动、重启、停止命令
# 启动一个已经停止的容器
docker start 容器名或容器ID
# 重启一个容器,包括已经停止的容器
docker restart 容器名或容器ID
# 停止一个容器
docker stop 容器名或容器ID
# 强制停止一个容器
docker kill 容器名或容器ID
docker stop
和docker kill
区别:
docker stop
命令会向容器发送一个优雅的停止信号(SIGTERM),让容器有机会进行清理和停止。容器将会接收到该信号并尝试优雅地停止运行中的进程。如果容器在一定时间内没有停止,docker 将会发送一个强制停止信号(SIGKILL)来强制终止容器。这个命令给容器一个正常停止的机会,并且容器可以执行一些清理操作,如保存数据、关闭连接等。
docker kill
命令会立即向容器发送一个强制终止信号(SIGKILL),不给容器进行任何清理操作的机会。容器将会立即停止,正在运行的进程将会被强制中断。这个命令是一种强制终止容器的方式,适用于需要立即停止容器的情况。
删除容器
docker rm 容器ID或容器名
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7d1b6cececcc centos "/bin/bash" 11 minutes ago Up 11 minutes happy_ardinghelli
962ead7ba991 centos "/bin/bash" 11 minutes ago Up 11 minutes nifty_lovelace
c98e20511eb4 centos "/bin/bash" 2 hours ago Up 2 hours vibrant_rosalind
d6afbc8233e8 centos "/bin/bash" 3 hours ago Exited (0) 3 hours ago admiring_galois
ba50251e5f7e centos "/bin/bash" 3 hours ago Exited (0) 3 hours ago nervous_euler
578ad7d53c37 d2c94e258dcb "/hello" 5 hours ago Exited (0) 12 minutes ago gallant_bhaskara
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rm 578a
578a
参数:
-f
:等价于--force
,强制删除容器
# 删除所有容器,包括正在运行和已经停止的,慎用
docker rm -f $(docker ps -aq)
[root@iZbp15293q8kgzhur7n6kvZ /]# docker rm -f $(docker ps -aq)
7d1b6cececcc
962ead7ba991
c98e20511eb4
d6afbc8233e8
ba50251e5f7e
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
以后台方式启动容器 -d
docker run -d centos
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -d centos
de7eb928d553c5df52f94e27acf604745fa15eb24c40fee60e4059e2bb60b5dd
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
以后台方式启动容器,查看正在运行时的容器发现没有刚刚启动的容器,这是因为这个镜像 centos 启动的容器里并没有前台进程,所以刚启动一下子就停止了,如果想查询到这个容器,需要加上-a
参数
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
de7eb928d553 centos "/bin/bash" 2 minutes ago Exited (0) 2 minutes ago happy_ride
可以使用-it
参数以交互方式启动 centos,这样就有了一个前台进程,容器就不会停止了
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd centos
b82d98613fd44437b1aa4a1bbff63075604a547ce878e0df9ff76ee13d1b56aa
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b82d98613fd4 centos "/bin/bash" 2 seconds ago Up 2 seconds beautiful_ramanujan
查看日志
docker logs 容器名或容器id
参数:
-t
:等价于--timestamps
,打印时间戳-f
:等价于--follow
,打印最新日志并继续输出-n
:等价于--tail
,从日志末尾输出的行数
这里我们启动一个可以输出日志的容器
# shell 脚本(一个死循环,每隔1s,输出akuya)
docker run -d centos /bin/sh -c "while true;do echo akuya;sleep 1;done"
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -d centos /bin/sh -c "while true;do echo akuya;sleep 1;done"
5cbf1cd1a21bee0de30fdf53f2bbfc0b690b90b06ce0cd90d4a3e0962de32eab
[root@iZbp15293q8kgzhur7n6kvZ /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5cbf1cd1a21b centos "/bin/sh -c 'while t…" 4 seconds ago Up 4 seconds romantic_villani
b82d98613fd4 centos "/bin/bash" 5 minutes ago Up 5 minutes beautiful_ramanujan
# 先打印最新5行日志,然后继续输出新的日志,使用快捷键 ctrl+c 停止
[root@iZbp15293q8kgzhur7n6kvZ /]# docker logs -ft -n 5 5cbf1cd1a21b
2024-03-19T13:15:50.993510891Z akuya
2024-03-19T13:15:51.995897632Z akuya
2024-03-19T13:15:52.998156257Z akuya
2024-03-19T13:15:54.000528678Z akuya
2024-03-19T13:15:55.002960756Z akuya
2024-03-19T13:15:56.005416926Z akuya
2024-03-19T13:15:57.007909978Z akuya
2024-03-19T13:15:58.010417478Z akuya
查看容器相关的进程(top)
docker top 容器名或容器id
[root@iZbp15293q8kgzhur7n6kvZ /]# docker top 5cbf1cd1a21b
UID PID PPID C STIME TTY TIME CMD
root 15397 15376 0 21:11 ? 00:00:00 /bin/sh -c while true;do echo akuya;sleep 1;done
root 15975 15397 0 21:20 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
查看容器的元数据(inspect)
docker inspect 容器名或容器id
[root@iZbp15293q8kgzhur7n6kvZ /]# docker inspect 5cbf1cd1a21b
[{"Id": "5cbf1cd1a21bee0de30fdf53f2bbfc0b690b90b06ce0cd90d4a3e0962de32eab","Created": "2024-03-19T13:11:32.102190519Z","Path": "/bin/sh",......
]
进入一个正在进行的容器
docker exec -it 容器名或容器id shell脚本程序(例如/bin/sh或/bin/bash,如果不加默认进入正在执行的终端)
docker attach 容器名或容器id
# 不加/bin/bash则会进入到正在运行的/bin/sh中,也就是那个死循环的程序
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it 5cbf1cd1a21b /bin/bash
[root@5cbf1cd1a21b /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 13:11 ? 00:00:00 /bin/sh -c while true;do echo akuya;sleep 1;done
root 1138 0 0 13:30 pts/0 00:00:00 /bin/bash
root 1161 1 0 13:30 ? 00:00:00 /usr/bin/coreutils --coreutils-prog-shebang=sleep /usr/bin/sleep 1
root 1162 1138 0 13:30 pts/0 00:00:00 ps -ef
# 进入到正在执行的程序中,也就是死循环,需要重新启动一下服务器
[root@iZbp15293q8kgzhur7n6kvZ /]# docker attach 5cbf1cd1a21b
akuya
akuya
akuya
akuya
akuya
区别:
exec
:docker exec
命令用于在正在运行的容器中执行一个新的命令。它会创建一个新的进程,并在容器中执行指定的命令。该命令会在容器的新进程中执行,不会影响容器原有的主进程。使用docker exec
可以在容器内部执行命令、进入容器的交互式终端,并且可以在后台运行的容器中执行任务。attach
:docker attach
命令用于连接到正在运行的容器的标准输入(stdin)、输出(stdout)和错误输出(stderr)。该命令会将你的终端会话附加到容器的主进程上,并将容器的输出直接显示在你的终端上。使用docker attach
可以实时查看和参与到容器内部正在运行的进程中,但是当你退出终端会话时,容器将会停止运行。
拷贝容器内的文件到主机上
docker cp 容器名或容器id:容器内文件地址 主机地址
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5cbf1cd1a21b centos "/bin/sh -c 'while t…" 23 minutes ago Up 23 minutes romantic_villani
b82d98613fd4 centos "/bin/bash" 28 minutes ago Up 28 minutes beautiful_ramanujan
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker attach b82d98613fd4
[root@b82d98613fd4 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@b82d98613fd4 /]# cd /home
[root@b82d98613fd4 home]# touch a.txt
# 快捷键退出容器
[root@b82d98613fd4 home]# read escape sequence
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker cp b82d98613fd4:/home/a.txt /home
Successfully copied 1.54kB to /home
[root@iZbp15293q8kgzhur7n6kvZ ~]# cd /home
[root@iZbp15293q8kgzhur7n6kvZ home]# ll
total 0
-rw-r--r-- 1 root root 0 Mar 19 21:40 a.txt
常用命令总结
常用命令:
docker --help attach Attach to a running container # 当前 shell 下 attach 连接指定运行镜像
build Build an image from a Dockerfile # 通过 Dockerfile 定制镜像
commit Create a new image from a container changes # 提交当前容器为新的镜像
cp Copy files/folders from the containers filesystem to the host path #从容器中拷贝指定文件或者目录到宿主机中
create Create a new container # 创建一个新的容器,同 run,但不启动容器
diff Inspect changes on a container's filesystem # 查看 docker 容器变化
events Get real time events from the server # 从 docker 服务获取容器实时事件
exec Run a command in an existing container # 在已存在的容器上运行命令
export Stream the contents of a container as a tar archive # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history Show the history of an image # 展示一个镜像形成历史
images List images # 列出系统当前镜像
import Create a new filesystem image from the contents of a tarball # 从tar包中的内容创建一个新的文件系统映像[对应export]
info Display system-wide information # 显示系统相关信息
inspect Return low-level information on a container # 查看容器详细信息
kill Kill a running container # kill 指定 docker 容器
load Load an image from a tar archive # 从一个 tar 包中加载一个镜像[对应 save]
login Register or Login to the docker registry server # 注册或者登陆一个 docker 源服务器
logout Log out from a Docker registry server # 从当前 Docker registry 退出
logs Fetch the logs of a container # 输出当前容器日志信息
port Lookup the public-facing port which is NAT-ed to PRIVATE_PORT # 查看映射端口对应的容器内部源端口
pause Pause all processes within a container # 暂停容器
ps List containers # 列出容器列表
pull Pull an image or a repository from the docker registry server # 从docker镜像源服务器拉取指定镜像或者库镜像
push Push an image or a repository to the docker registry server # 推送指定镜像或者库镜像至docker源服务器
restart Restart a running container # 重启运行的容器
rm Remove one or more containers # 移除一个或者多个容器
rmi Remove one or more images # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run Run a command in a new container # 创建一个新的容器并运行一个命令
save Save an image to a tar archive # 保存一个镜像为一个 tar 包[对应 load]
search Search for an image on the Docker Hub # 在 docker hub 中搜索镜像
start Start a stopped containers # 启动容器
stop Stop a running containers # 停止容器
tag Tag an image into a repository # 给源中镜像打标签
top Lookup the running processes of a container # 查看容器中运行的进程信息
unpause Unpause a paused container # 取消暂停容器
version Show the docker version information # 查看 docker 版本号
wait Block until a container stops, then print its exit code # 截取容器停止时的退出状态值
练习
docker 安装 nginx
# 搜素镜像
[root@iZbp15293q8kgzhur7n6kvZ home]# docker search nginx
NAME DESCRIPTION STARS OFFICIAL
nginx Official build of Nginx. 19711 [OK]
......
# 拉取镜像
[root@iZbp15293q8kgzhur7n6kvZ home]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
# 通过镜像启动容器
# --name 给容器命名
# -p 3500<->80 端口映射(本机端口:容器内端口),外部通过3500端口访问
[root@iZbp15293q8kgzhur7n6kvZ home]# docker run -d --name mynginx -p 3500:80 nginx
8d54bf57c5577257227c90db0d8fdfe3ba769b294e4a49e5500c7fe6e6a904d4
[root@iZbp15293q8kgzhur7n6kvZ home]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8d54bf57c557 nginx "/docker-entrypoint.…" 13 seconds ago Up 12 seconds 0.0.0.0:3500->80/tcp, :::3500->80/tcp mynginx
[root@iZbp15293q8kgzhur7n6kvZ home]# curl localhost:3500
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
这样就完成 nginx 的安装
也可以在外部浏览器访问,输入地址服务器公网ip:3500
即可访问,如果无法访问说明没有配置安全组,需要在安全组中把3500端口开放
docker 安装 elasticsearch
# -p 可以同时暴露多个端口
# -e 环境配置,一般在镜像的文档中查看 hub.docker.com
[root@iZbp15293q8kgzhur7n6kvZ home]# docker run -d --name myelastaicsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.8.0
Unable to find image 'elasticsearch:7.8.0' locally
7.8.0: Pulling from library/elasticsearch
524b0c1e57f8: Pull complete
7a096b8f20be: Pull complete
9dd8117fbfec: Pull complete
335891dbdd0e: Pull complete
dfce820717b4: Pull complete
82d3459719f7: Pull complete
2e79822fece3: Pull complete
2f80b981dd6a: Pull complete
05f8a08da0ba: Pull complete
Digest: sha256:945f80960f2ad1bd4b88bd07a9ba160d22d4285bbc8a720d052379006d5d57a6
Status: Downloaded newer image for elasticsearch:7.8.0
bad6ece441159ee80c53aa5e30403e7f82ea96ab4ce8315a3e2c6fb81f0bbbca
[root@iZbp15293q8kgzhur7n6kvZ home]# curl localhost:9200
{"name" : "bad6ece44115","cluster_name" : "docker-cluster","cluster_uuid" : "t4WJEW-oQ-CiSiI1n1YtHQ","version" : {"number" : "7.8.0","build_flavor" : "default","build_type" : "docker","build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65","build_date" : "2020-06-14T19:35:50.234439Z","build_snapshot" : false,"lucene_version" : "8.5.1","minimum_wire_compatibility_version" : "6.8.0","minimum_index_compatibility_version" : "6.0.0-beta1"},"tagline" : "You Know, for Search"
}
可视化工具(了解即可)
最常用的工具是 portainer
portainer 是一个开源的容器管理平台,它提供了一个简单易用的用户界面,用于管理和监控 docker 容器集群。通过 portainer,用户可以轻松地进行容器的部署、启动、停止、监控和管理等操作,而无需使用复杂的命令行工具。
portainer 支持多种操作系统和容器平台,包括 docker、kubernetes、swarm等,使用户能够在不同的环境中管理和操作容器。它提供了直观的用户界面和可视化的操作方式,使用户能够更加方便地管理和监控容器的状态、日志和资源使用情况。
总之,portainer 是一个功能强大的容器管理平台,可以帮助用户更好地管理和操作 docker 容器集群。
安装命令如下:
# 外部访问端口可以随意设置,这里设置成8088
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
安装完成后,浏览器访问:ip地址:8088
,注意设置阿里云安全组
得到这个页面,随意设置密码即可
选择本地连接即可
在这里就可以查看到当前 docker 的所有信息啦
docker 镜像详解
镜像本质是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
别人给我们生成好的一个环境,或者项目都可以打包成一个镜像
分层下载
这里我们下载一个 redis 镜像
这里我们可以看到,下载镜像并不是下载一个完整的包,而是分层进行下载,这些层最后组合成了完整的 redis 镜像
我们可以使用docker inspect redis
来查看镜像分层的详细信息
在最后几行可以看到:
"RootFS": {"Type": "layers",# 镜像分层下载的片段"Layers": ["sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f","sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb","sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1","sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372","sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed","sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952"]
},
理解:
所有的 docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 python 包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
一步步逐层叠加,最后形成完整的镜像
该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。
在添加额外的镜像层的同时,镜像始终保持是当前所有镜像的组合,理解这一点非常重要。下图中举了一个简单的例子,每个镜像层包含 3 个文件,而镜像包含了来自两个镜像层的 6 个文件。
上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。
下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有 6 个文件,这是因为最上层中的文件 7 是文件 5 的一个更新版本。
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
docker 在 Windows 上仅支持 windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层和 CoW[1]。
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
docker 镜像下载原理
UnionFS (联合文件系统)
UnionFS(联合文件系统):Union 文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
docker 镜像加载原理
docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS。
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 都是好几个G,为什么 docker 这里才200M?
这是因为对于一个精简的 OS,rootfs 可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用 Host 的 kernel,自己只需要提供 rootfs 就可以了。由此可见对于不同的 linux 发行版,bootfs 基本是一致的,rootfs 会有差别, 因此不同的发行版可以公用 bootfs。
容器打包镜像(commit)
提交容器的副本,让这个容器产生一个新的镜像
docker commit 容器名或容器id 镜像名:版本号
参数:
-
-a
:等价于--author
,作者信息(例如:John Hannibal Smith hannibal@a-team.com) -
-m
:等价于--message
,提交的信息
这里我们根据 redis 镜像创建一个容器,进行修改,然后再用修改后的容器打包成一个新的镜像
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker run -it redis /bin/bash
# 对容器进行修改
root@ed467c2e1bd1:/data# cp /bin/* /home
# 快捷键ctrl+p+q退出
root@ed467c2e1bd1:/data# read escape sequence
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed467c2e1bd1 redis "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp clever_kilby
68ef570f524e portainer/portainer "/portainer" 49 minutes ago Up 49 minutes 0.0.0.0:8088->9000/tcp, :::8088->9000/tcp heuristic_hopper
bad6ece44115 elasticsearch:7.8.0 "/tini -- /usr/local…" 18 hours ago Up 18 hours 0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 0.0.0.0:9300->9300/tcp, :::9300->9300/tcp myelastaicsearch
8d54bf57c557 nginx "/docker-entrypoint.…" 18 hours ago Up 18 hours 0.0.0.0:3500->80/tcp, :::3500->80/tcp mynginx
# 重启一下容器
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker restart ed467c2e1bd1
ed467c2e1bd1
# 打包成一个新的镜像newredis:1.0
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker commit -a="akuya" -m="test" ed467c2e1bd1 newredis:1.0
sha256:d243afadb3b1457422129ddb1e32a338ef1d4742b9d1e9a9b069cea04696b242
这里我们可以看到,新打包的镜像多了一层sha256:d243afadb3b1457422129ddb1e32a338ef1d4742b9d1e9a9b069cea04696b242
对比原来 redis 镜像的分层
# 新newredis:1.0的镜像分层
"RootFS": {"Type": "layers","Layers": ["sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f","sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb","sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1","sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372","sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed","sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952","sha256:9d3a71caf59342bb545421fd890b80016a4dd6ffc960a8fb11b566396530a95e"]
},"RootFS": {"Type": "layers",# 镜像分层下载的片段"Layers": ["sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f","sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb","sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1","sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372","sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed","sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952"]
},
可以很明显的看到新的镜像分层多了一层sha256:9d3a71caf59342bb545421fd890b80016a4dd6ffc960a8fb11b566396530a95e
所以我们可以了解到当创建一个新的镜像时,docker 会基于已有的镜像进行修改和扩展。每个修改操作都会生成一个新的层,并且这些层是按照顺序进行组织的,形成一个层次结构。每个层都只包含了与前一层的差异,这样可以节省存储空间,并且提高镜像的构建效率。
docker 镜像分层的优点
docker 镜像的分层机制带来了以下几个优点:
- 空间效率:由于 docker 镜像的每个层都只包含与前一层的差异,相同的层可以在多个镜像之间共享和重用。这样可以节省存储空间,尤其在使用大量镜像时非常显著。只需存储每个层的差异,而不是整个镜像,大大减少了磁盘占用。
- 构建效率:在构建镜像时,只需要对修改的部分创建新的层,而不需要重新复制整个镜像。这样可以大大加快镜像的构建速度,尤其当镜像层次结构复杂且包含大量文件时,效果更为明显。
- 可维护性:通过分层机制,docker 镜像的每个层都是只读的,不可修改。这意味着镜像的不同版本可以共享相同的底层层,只需在最上层添加新的层来表示不同的版本或修改。这样可以简化镜像的维护和更新过程,提高可维护性。
- 可重用性:由于镜像的每个层都可以在多个镜像之间共享和重用,可以更好地利用已有的层来构建新的镜像。这样可以提高镜像的可重用性,减少重复工作,同时也方便了镜像的分发和共享。
- 容器隔离性:通过每个容器都有自己的可写层,docker 可以实现容器之间的隔离性。每个容器可以独立地修改和管理自己的文件系统,而不会对其他容器或原始镜像的层产生影响。这样可以确保容器的独立性和安全性。
综上所述,docker 镜像的分层机制带来了空间效率、构建效率、可维护性、可重用性和容器隔离性等多个方面的优点,使得 docker 在容器化应用的开发和部署中具有高效性和灵活性。
容器数据卷
什么是容器数据卷
docker的理念回顾:
将应用和运行的环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能够持久化的!
就好比,你安装一个 MySQL,结果你把容器删了,就相当于删库跑路了,这TM也太扯了吧!
所以我们希望容器之间有可能可以共享数据,docker 容器产生的数据,如果不通过 docker commit 生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了!这样是行不通的!
为了能保存数据在 docker 中我们就可以使用卷!让数据挂载到我们本地!这样数据就不会因为容器删除而丢失了!
作用:
卷就是目录或者文件,存在一个或者多个容器中,由 docker 挂载到容器,但不属于联合文件系统,因此能够绕过 Union File System , 提供一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此 docker不会在容器删除时删除其挂载的数据卷。
特点:
-
持久性:容器数据卷可以在容器的生命周期中保留数据。即使容器被删除或重新创建,数据卷仍然存在,可以被其他容器继续使用。
-
共享性:容器数据卷可以被多个容器同时挂载和使用,实现容器之间的数据共享。这样可以方便地实现容器间的数据传递和共享。
-
独立性:容器数据卷是独立于容器的,它可以存在于容器之外。这意味着即使容器被删除,数据卷仍然保留在主机上,可以被其他容器或主机访问和使用。
所以:总结一句话: 就是容器的持久化,以及容器间的继承和数据共享!
使用数据卷
可以使用docker volume create
创建一个容器数据卷,也可以在执行docker run 容器
时添加-v
(等价于--volume
)参数使用数据卷(推荐)
docker run -it -v 服务器目录:容器目录 镜像名或镜像id
首先删除所有镜像和容器进行测试
执行以下命令
docker run -it -v /home/test:/root/test centos /bin/bash
下载完毕后我们进入服务器的 home 目录下
[root@iZbp15293q8kgzhur7n6kvZ ~]# cd /home
[root@iZbp15293q8kgzhur7n6kvZ home]# ls
test
可以看到, home 目录下多了一个 test 目录
我们再进入到容器的 root 目录下
[root@e9667f583715 /]# cd /root
[root@e9667f583715 ~]# ls
anaconda-ks.cfg anaconda-post.log original-ks.cfg test
可以看到同样多了一个 test 目录,这说明挂载了数据卷,自动创建对应的文件夹
接下来,在容器的 test 目录下创建一个文件
[root@e9667f583715 ~]# cd test/
[root@e9667f583715 test]# touch a.txt
[root@e9667f583715 test]# ls
a.txt
看一看服务器的 test 目录
[root@iZbp15293q8kgzhur7n6kvZ home]# cd test/
[root@iZbp15293q8kgzhur7n6kvZ test]# ls
a.txt
可以看到 test 目录下多了个 a.txt
文件
再在服务器的 test 目录下创建一个文件
[root@iZbp15293q8kgzhur7n6kvZ test]# touch b.txt
[root@iZbp15293q8kgzhur7n6kvZ test]# ls
a.txt b.txt
看一看容器的 test 目录
[root@e9667f583715 test]# ls
a.txt b.txt
可以看到 test 目录下多了 b.txt
文件,这说明两边的目录完全共通,都可以进行读写操作
可以查看一下容器的元数据,里面有一段:
"Mounts": [{"Type": "bind","Source": "/home/test","Destination": "/root/test","Mode": "","RW": true,"Propagation": "rprivate"}
],
删除容器
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker rm -f e9667f583715
查看服务器的 test 目录下的数据还在不在?
[root@iZbp15293q8kgzhur7n6kvZ test]# ls
a.txt b.txt
可以看到数据依然存在
重新运行一个容器进行挂载数据卷
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker run -it -v /home/test:/root/test centos /bin/bash
[root@ba07c0614e65 /]# cd /root
[root@ba07c0614e65 ~]# ls
anaconda-ks.cfg anaconda-post.log original-ks.cfg test
[root@ba07c0614e65 ~]# cd test/
[root@ba07c0614e65 test]# ls
a.txt b.txt
可以看到数据没有丢失
安装 mysql 挂载数据卷
容器数据卷是非常适合于 mysql 的,通过将 mysql 的数据挂载到本地服务器上,即时 mysql 宕机了或者删除了,数据也不会丢失
使用以下命令安装 mysql
docker run -d -p 3306:3306 \
-v /home/mysql/conf:/etc/mysql/conf.d \
-v /home/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name mymysql \
mysql:5.7
安装完成后,进入服务器的 /home/mysql
目录下
[root@iZbp15293q8kgzhur7n6kvZ ~]# cd /home
[root@iZbp15293q8kgzhur7n6kvZ home]# cd mysql/
[root@iZbp15293q8kgzhur7n6kvZ mysql]# ls
conf data
可以看到有 conf、data 两个目录,进入到 data 目录下
[root@iZbp15293q8kgzhur7n6kvZ mysql]# cd data/
[root@iZbp15293q8kgzhur7n6kvZ data]# ls
auto.cnf ca.pem client-key.pem ibdata1 ib_logfile1 mysql private_key.pem server-cert.pem sys
ca-key.pem client-cert.pem ib_buffer_pool ib_logfile0 ibtmp1 performance_schema public_key.pem server-key.pem
可以看到跟我们在 windows 系统下安装的 mysql 目录一致
我们在外部使用 navicat 连接一下这个数据库
主机就是服务器的 ip 地址,密码为123456
接下来我们创建一个数据库
可以看到数据库的数据已经保存在了本地上
这样即使你的 mysql 容器宕机导致数据丢失了,本地的数据也不会消失
匿名挂载和具名挂载
匿名挂载
匿名挂载,顾名思义,在挂载数据卷时没有指定主机的挂载目录,-v
后只有容器内路径
首先我们使用docker volume ls
来查看已经拥有的数据卷
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume ls
DRIVER VOLUME NAME
local 50e59a468588177437d3ab4d1a1831c74708d277495c96708d2b10735f6a9c9b
local 99f858f4eb9b980bc322c38e57124062fae4a7f0996e562b1fba62f651feea2d
local bc66eca0b45126f5c4ebed6f5225825e2ded2877a1e3310b45f3333dce8a74c4
接下来我们使用匿名挂载创建一个容器
[root@iZbp15293q8kgzhur7n6kvZ test]# docker run -d --name newcentos -v /root/test centos
aba914f3fcb394acd6307eaff1a498c5e819d09c2910d6e4ad4649be14527704
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume ls
DRIVER VOLUME NAME
local 50e59a468588177437d3ab4d1a1831c74708d277495c96708d2b10735f6a9c9b
local 99f858f4eb9b980bc322c38e57124062fae4a7f0996e562b1fba62f651feea2d
local bc66eca0b45126f5c4ebed6f5225825e2ded2877a1e3310b45f3333dce8a74c4
local d903842c735ce7543fbb8441cccdad41aff96943cfb2151a867dedbc8f13cad1
可以看到数据卷多了一个d903842c735ce7543fbb8441cccdad41aff96943cfb2151a867dedbc8f13cad1
我们可以查看一下这个数据卷的信息
可以看到默认情况,会挂载到本地的这个目录下
匿名挂载适用于临时存储或者不需要在多个容器之间共享的数据。例如,可以将日志文件、临时文件或其他容器产生的数据挂载到主机上的特定目录,以便于查看和处理。
需要注意的是,由于匿名挂载是直接指定主机上的路径,所以在不同主机上挂载相同路径的容器可能会导致冲突或数据不一致。因此,在使用匿名挂载时,需要确保主机路径的唯一性和正确性。
在使用 dockerfile 构建镜像时,通过 VOLUME 保留字挂载数据卷就是使用匿名挂载,不会出现忘记挂载目录而导致数据丢失的情况
具名挂载
具名挂载,顾名思义,就是在挂载数据卷时指定了数据卷的名字,为了可以在多个容器中共享重要的数据
[root@iZbp15293q8kgzhur7n6kvZ test]# docker run -d --name newcentos01 -v centosdir:/root/test centos
ee3c1fa72526f6d8e3983fc280389ff55cb53ecc3c7329cfcba347b03fa2768b
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume ls
DRIVER VOLUME NAME
local 50e59a468588177437d3ab4d1a1831c74708d277495c96708d2b10735f6a9c9b
local 99f858f4eb9b980bc322c38e57124062fae4a7f0996e562b1fba62f651feea2d
local bc66eca0b45126f5c4ebed6f5225825e2ded2877a1e3310b45f3333dce8a74c4
local centosdir
local d903842c735ce7543fbb8441cccdad41aff96943cfb2151a867dedbc8f13cad1
可以看到数据卷中多了一个名字为 centosdir 的数据卷,查看一下它的信息
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume inspect centosdir
[{"CreatedAt": "2024-03-20T18:17:12+08:00","Driver": "local","Labels": null,"Mountpoint": "/var/lib/docker/volumes/centosdir/_data","Name": "centosdir","Options": null,"Scope": "local"}
]
具名挂载提供了一种方便和灵活的方式来管理容器的数据。它可以在多个容器之间共享和重用数据,使容器之间的数据交互更加简单和高效。同时,具名挂载由 docker 自动管理,减轻了手动管理数据的负担。
数据卷容器
容器数据卷:容器的持久化
数据卷容器:容器,当做一个持久化的卷。
数据卷容器(Data Volume Container)是一种在 docker 中用于管理和共享数据的特殊容器。它是一种轻量级的容器,主要用于创建和管理数据卷,而不运行实际的应用程序。
数据卷容器的工作原理如下:
- 创建数据卷容器:首先,创建一个专门用于管理数据的容器,称为数据卷容器。可以使用
docker create
命令创建一个数据卷容器。 - 挂载数据卷:然后,在其他容器中使用
--volumes-from
参数来挂载数据卷容器中的数据卷。这样,其他容器就可以访问和共享数据卷容器中的数据。 - 管理数据卷:数据卷容器可以使用
docker run
命令启动和停止,或者使用docker start
和docker stop
命令进行管理。通过管理数据卷容器,可以管理和保持数据卷的持久性。
数据卷容器的主要优点如下:
- 数据共享:数据卷容器允许多个容器访问和共享相同的数据卷。它提供了一种简单且可靠的方式来管理和共享数据。
- 数据持久性:通过使用数据卷容器,数据卷的生命周期不再依赖于任何特定的容器。即使容器被删除或重新创建,数据卷仍然存在。
- 简化管理:通过将数据卷的管理和持久性交给数据卷容器,可以简化数据的管理和维护。容器的启动和停止不会影响数据卷的存在。
- 数据版本控制:数据卷容器可以用于管理和保存不同版本的数据。通过创建不同的数据卷容器,可以保存不同时间点的数据快照。
需要注意的是,数据卷容器是一种较早的数据管理方法,现在也有更灵活的数据管理方式,如使用具名挂载(Named Volume)或使用 docker 卷插件。这些方法提供了更多的功能和性能,因此在选择数据管理方式时需要根据实际需求进行选择。
接下来进行测试:
首先删除所有的数据卷和容器
# 删除所有数据卷命令
docker volume rm $(docker volume ls -q)
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume rm $(docker volume ls -q)
50e59a468588177437d3ab4d1a1831c74708d277495c96708d2b10735f6a9c9b
99f858f4eb9b980bc322c38e57124062fae4a7f0996e562b1fba62f651feea2d
bc66eca0b45126f5c4ebed6f5225825e2ded2877a1e3310b45f3333dce8a74c4
centosdir
d903842c735ce7543fbb8441cccdad41aff96943cfb2151a867dedbc8f13cad1
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume ls
DRIVER VOLUME NAME
运行容器 centos01,使用匿名挂载
[root@iZbp15293q8kgzhur7n6kvZ data]# docker run -it --name centos01 -v /root/test centos
[root@5ee3e5785b46 /]# cd /root
[root@5ee3e5785b46 ~]# ls -l
total 16
-rw------- 1 root root 2361 Sep 15 2021 anaconda-ks.cfg
-rw-r--r-- 1 root root 608 Sep 15 2021 anaconda-post.log
-rw------- 1 root root 2059 Sep 15 2021 original-ks.cfg
drwxr-xr-x 2 root root 4096 Mar 20 10:36 test
查看所有数据卷
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume ls
DRIVER VOLUME NAME
local f844ece6524ca50f313ac5065f70f24900a624a22ae167edb3b23934f476e0c2
接下来,运行容器 centos02,使用 --volumes-from
挂载 centos01 的数据卷
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker run -it --name centos02 --volumes-from centos01 centos
[root@3a0633d18652 /]# cd /root
[root@3a0633d18652 ~]# ls -l
total 16
-rw------- 1 root root 2361 Sep 15 2021 anaconda-ks.cfg
-rw-r--r-- 1 root root 608 Sep 15 2021 anaconda-post.log
-rw------- 1 root root 2059 Sep 15 2021 original-ks.cfg
drwxr-xr-x 2 root root 4096 Mar 20 10:36 test
可以看到 centos02 中也存在 test 目录
再次查看所有数据卷
[root@iZbp15293q8kgzhur7n6kvZ test]# docker volume ls
DRIVER VOLUME NAME
local f844ece6524ca50f313ac5065f70f24900a624a22ae167edb3b23934f476e0c2
发现数据卷没有增加
这时候在 centos01 和 centos02 的 test 目录下进行测试,发现二者是完全共通的,这就是数据卷容器,类似于 java 中的继承,这里是 centos02 继承了 centos01 的数据卷,二者达成共通
那么在多个相同的容器中,如果想要共享数据,使用数据卷容器,将容器作为一个父数据卷,其他容器来挂载到我这个父卷下,就可以实现共享了
可以再多增加几个容器进行测试,删除父容器查看其余子容器是否共通,自行测试即可,最后可以得出结论:
容器之间配置信息的传递,数据卷的生命周期会一直持续到没有容器使用它为止。
存储在本机的文件则会一直保留!
dockerfile(重点)
大家想想,Nginx,tomcat,mysql 这些镜像都是哪里来的?官方能写,我们不能写吗?
我们要研究自己如何做一个镜像,而且我们写的微服务项目打包上云部署,docker 就是最方便的。
微服务打包成镜像,任何装了 docker 的地方,都可以下载使用,极其方便。
流程:开发应用 => 编写 dockerfile => 打包为镜像 => 上传到仓库(私有仓库公司内部,公有仓库 dockerhub)=> 下载镜像 => 启动运行。
还可以方便移植!
什么是 dockerfile
dockerfile 是一种用于定义和构建 docker 镜像的文本文件。它包含一系列的指令和参数,用于描述镜像的构建过程,包括基础映像、软件包安装、文件拷贝、环境变量设置等。
通过编写 dockerfile,可以将应用程序、环境和依赖项打包成一个独立的容器镜像,使其可以在不同的环境和平台上运行,实现应用程序的可移植性和可扩展性。
dockerfile 的基本结构包括以下几个部分:
- 基础映像(Base Image):使用 FROM 指令指定基础映像,作为构建镜像的起点。基础映像通常包含了操作系统和一些预装的软件和工具。
- 构建过程指令:使用一系列指令来描述构建过程,例如 RUN 用于执行命令和安装软件包,COPY 用于拷贝文件和目录,ADD 用于拷贝和提取文件,WORKDIR 用于设置工作目录,等等。
- 容器启动指令:使用 CMD 或 ENTRYPOINT 指令来定义容器启动时要执行的命令,也就是默认的容器执行命令。
通过编写 dockerfile,可以自定义构建过程,选择所需的软件和配置,以及设置环境变量、暴露端口等。dockerfile 的语法简单且易于理解,使得镜像的构建过程变得可重复和可维护。
总的来说,dockerfile 是定义和构建 docker 镜像的文本文件,通过编写指令和参数来描述镜像的构建过程和配置,以实现应用程序的打包和部署。它是使用 docker 进行容器化开发和部署的重要工具。
构建步骤:
- 编写 dockerfile 文件
- docker build 构建镜像
- docker run 镜像
dockerfile 构建过程
基础知识:
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
#
表示注释- 每条指令都会创建一个新的镜像层,并对镜像进行提交
流程:
- docker 从基础镜像运行一个容器
- 执行一条指令并对容器做出修改
- 执行类似 docker commit 的操作提交一个新的镜像层
- docker 再基于刚提交的镜像运行一个新容器
- 执行 dockerfile 中的下一条指令直到所有指令都执行完成!
说明:
从应用软件的角度来看,dockerfile,docker 镜像与 docker 容器分别代表软件的三个不同阶段。
- dockerfile 是软件的原材料(代码)
- docker 镜像则是软件的交付品(.apk)
- docker 容器则是软件的运行状态(客户下载安装执行)
dockerfile 面向开发,docker 镜像成为交付标准,docker 容器则涉及部署与运维,三者缺一不可!
- dockerfile:需要定义一个 dockerfile,dockerfile 定义了进程需要的一切东西。dockerfile 涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当引用进行需要和系统服务和内核进程打交道,这时需要考虑如何设计 namespace 的权限控制)等等。
- docker镜像:在 dockerfile 定义了一个文件之后,docker build 时会产生一个 docker 镜像,当运行 docker 镜像时,会真正开始提供服务;
- docker容器:容器是直接提供服务的。
dockerfile 指令
关键字:
FROM # 基础镜像,当前新镜像是基于哪个镜像的
MAINTAINER # 镜像维护者的姓名混合邮箱地址
RUN # 容器构建时需要运行的命令
EXPOSE # 当前容器对外保留出的端口
WORKDIR # 指定在创建容器后,终端默认登录的进来工作目录,一个落脚点
ENV # 用来在构建镜像过程中设置环境变量
ADD # 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包
COPY # 类似ADD,拷贝文件和目录到镜像中!
VOLUME # 容器数据卷,用于数据保存和持久化工作
CMD # 指定一个容器启动时要运行的命令,dockerFile中可以有多个CMD指令,但只有最后一个生效!
ENTRYPOINT # 指定一个容器启动时要运行的命令!和CMD一样
ONBUILD # 当构建一个被继承的DockerFile时运行命令,父镜像在被子镜像继承后,父镜像的ONBUILD被触发
编写 dockerfile
dockerhub 中99%的镜像都是通过在 base 镜像(Scratch)中安装和配置需要的软件构建出来的
Scratch 镜像很赞,它简洁、小巧而且快速,它没有 bug、安全漏洞、延缓的代码或技术债务。除了被 docker 添加了 metadata 之外,它基本上是空的。
我们在使用 dockerfile 构建 docker 镜像时,一种方式是使用官方预先配置好的容器镜像。优点是我们不用从头开始构建,节省了很多工作量,但付出的代价是需要下载很大的镜像包。
如果我们的需求是在构建一个符合我们实际业务需求的 docker 镜像的前提下,确保镜像尺寸尽可能的小,应该怎么做呢?
思路是使用空镜像 scratch,可以说是真正的从零开始构建属于自己的镜像,镜像的第一层
发布一个自己编写的 centos
由于阿里云官方的 centos 是不完整的,很多命令都没有(例如 vim、ipconfig 等),我们自定义一个 centos 镜像,,让它能够拥有这些命令
# dockerfile文件名字可以任意取,最好为Dockerfile
vim Dockerfile
编写 dockerfile 如下:
FROM centos7.9.2009
MAINTAINER akuya<123456@qq.com># 配置环境以及工作目录
ENV MYPATH /usr/local
WORKDIR $MYPATH# 安装vim、ipconfig等命令
RUN yum -y install vim
RUN yum -y install net-tools# 暴露端口
EXPOSE 80CMD echo $MYPATH
CMD echo "---akuya---"
CMD /bin/bash
由于最新版 centos8 有 bug,故使用 centos7.9版本
根据 dockerfile 构建镜像
docker build .
参数:
-f
:等价于--file
,指定 dockerfile 文件-t
:等价于--tag
,指定输出的镜像文件名:版本号
命令最后一定要加一个.
# 构建镜像命令
docker build -f Dockerfile -t mycentos:1.0 .
[root@iZbp15293q8kgzhur7n6kvZ home]# docker build -f Dockerfile -t mycentos:1.0 .
[+] Building 54.3s (8/8) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 282B 0.0s=> [internal] load metadata for docker.io/library/centos:7.9.2009 15.7s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/4] FROM docker.io/library/centos:7.9.2009@sha256:9d4bcbbb213dfd745b58be38b13b996ebb5ac315fe75711bd618426a630e0987 10.0s=> => resolve docker.io/library/centos:7.9.2009@sha256:9d4bcbbb213dfd745b58be38b13b996ebb5ac315fe75711bd618426a630e0987 0.0s=> => sha256:9d4bcbbb213dfd745b58be38b13b996ebb5ac315fe75711bd618426a630e0987 1.20kB / 1.20kB 0.0s=> => sha256:dead07b4d8ed7e29e98de0f4504d87e8880d4347859d839686a31da35a3b532f 529B / 529B 0.0s=> => sha256:eeb6ee3f44bd0b5103bb561b4c16bcb82328cfe5809ab675bb17ab3a16c517c9 2.75kB / 2.75kB 0.0s=> => sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc 76.10MB / 76.10MB 6.1s=> => extracting sha256:2d473b07cdd5f0912cd6f1a703352c82b512407db6b05b43f2553732b55df3bc 3.4s=> [2/4] WORKDIR /usr/local 2.2s=> [3/4] RUN yum -y install vim 21.2s=> [4/4] RUN yum -y install net-tools 3.4s => exporting to image 1.7s => => exporting layers 1.7s => => writing image sha256:1e7fb59b8a162a54a01bc41930d654d7d474cbd7ade3d110f12d95727e1d193f 0.0s => => naming to docker.io/library/mycentos:1.0 0.0s
[root@iZbp15293q8kgzhur7n6kvZ home]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
mycentos 1.0 1e7fb59b8a16 About a minute ago 708MB
centos latest 5d0da3dc9764 2 years ago 231MB
根据这个镜像运行容器后发现并没有打印出目录/usr/local
和---akuya---
,这是因为 CMD 指令如果存在多个,只有最后一个会被执行
docker history
查看镜像的变更历史
如果你下载了一个镜像,报错了或者你想查看一些构建逻辑,使用 docker history
[root@iZbp15293q8kgzhur7n6kvZ home]# docker history mycentos:1.0
IMAGE CREATED CREATED BY SIZE COMMENT
1e7fb59b8a16 7 minutes ago CMD ["/bin/sh" "-c" "/bin/bash"] 0B buildkit.dockerfile.v0
<missing> 7 minutes ago CMD ["/bin/sh" "-c" "echo \"---akuya---\""] 0B buildkit.dockerfile.v0
<missing> 7 minutes ago CMD ["/bin/sh" "-c" "echo $MYPATH"] 0B buildkit.dockerfile.v0
<missing> 7 minutes ago EXPOSE map[80/tcp:{}] 0B buildkit.dockerfile.v0
<missing> 7 minutes ago RUN /bin/sh -c yum -y install net-tools # bu… 208MB buildkit.dockerfile.v0
<missing> 7 minutes ago RUN /bin/sh -c yum -y install vim # buildkit 296MB buildkit.dockerfile.v0
<missing> 7 minutes ago WORKDIR /usr/local 0B buildkit.dockerfile.v0
<missing> 7 minutes ago ENV MYPATH=/usr/local 0B buildkit.dockerfile.v0
<missing> 7 minutes ago MAINTAINER akuya<123456@qq.com> 0B buildkit.dockerfile.v0
<missing> 2 years ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 years ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 2 years ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
项目中编写 dockerfile 思路
- 基于一个空的镜像
- 下载需要的环境 ADD
- 执行环境变量的配置 ENV
- 执行一些Linux命令 RUN
- 日志 CMD
- 端口暴露 EXPOSE
- 挂载数据卷 VOLUMES
这个过程就是你手动部署项目的过程,你通过 docker 可以再未来实现自动化构建。
CMD 与 ENTRYPOINT 区别
两个命令都是指定一个容器启动时要运行的命令,但二者有很大的区别
-
CMD
:容器内有多个 CMD 指令时,只有最后一个 CMD 指令会生效,而如果在执行docker run
命令时携带了其它命令,将会覆盖掉所有 dockerfile 的 CMD 指令 -
ENTRYPOINT
:ENTRYPOINT 的命令不容易被覆盖。在docker run
命令中提供的任何参数都会作为 ENTRYPOINT 命令的参数传递。
当两者组合使用时:那么 CMD 将作为 ENTRYPOINT 的默认参数,如果在docker run
命令中提供了参数,它将覆盖 CMD 并作为 ENTRYPOINT 的参数传递。
例如:
FROM centos
CMD ["echo", "hello world"]
运行容器时,可以覆盖默认的 CMD:
docker build -f Dockerfile -t cmdtest .
docker run cmdtest /bin/bash
结果不会打印出hello world
使用 ENTRYPOINT 指令:
FROM centos
ENTRYPOINT ["echo", "hello "]
CMD ["world"]
运行容器时,可以传递参数给 ENTRYPOINT:
docker build -f Dockerfile -t entrytest .
docker run entrytest # 输出hello world
docker run entrytest akuya # 输出hello akuya
练习:构建一个 tomcat,编写首页
使用空镜像从零构建一个完整的 tomcat
一般一个项目都在一个文件夹中,我们只需要在项目目录下编辑一个 Dockerfile 文件即可
当执行docker bulid
指定时如果没有-f
参数指定 dockerfile 文件,会默认寻找项目目录下的 Dockerfile
来构建镜像,所以我们名字通常设定为Dockerfile
准备工作:准备 tomcat 和 jdk 的 jar 包,创建Dockerfile
文件,以及一个任意内容的readme.md
即可
编辑Dockerfile
,内容如下:
FROM centos:7.9.2009
MAINTAINER akuya<123456@qq.com># 宿主机目录下文件拷贝到容器内,文件如果不是绝对路径会默认寻找 dockerfile 文件的同级目录下,所以最好所有文件都在同一个目录下
COPY readme.md /usr/local/readme.md# 添加我们自己的安装包
ADD jdk-8u11-linux-x64.tar.gz /usr/local
ADD apache-tomcat-9.0.22.tar.gz /usr/local#安装vim编辑器
RUN yum -y install vim# 配置工作目录
ENV MYPATH /usr/local
WORKDIR $MYPATH# 配置环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_11
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.22
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.22
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin# 暴露端口
EXPOSE 8080# 启动的时候自动运行tomcat,打印日志
CMD /usr/local/apache-tomcat-9.0.22/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.22/bin/logs/catalina.out
构建并运行:
# 构建,不指定 dockerfile 文件会自动寻找该目录下的 Dockerfile
docker build -t mytomcat .
# 运行
docker run -d -p 8080:8080 --name mytomcat \
-v /root/mytomcat/test:/usr/local/apache-tomcat-9.0.22/webapps/test \
-v /root/mytomcat/logs:/usr/local/apache-tomcat-9.0.22/logs \
--privileged=true \
mytomcat
运行成功后打开浏览器,输入ip地址:8080
,即可访问到 tomcat 主页
在我们自定义的 tomcat 服务器中上传一个项目,在本地挂载目录,丢一个项目上去。
首先进入到我们挂载的宿主机目录下/root/mytomcat
下
[root@iZbp15293q8kgzhur7n6kvZ mytomcat]# cd /root/mytomcat/
[root@iZbp15293q8kgzhur7n6kvZ mytomcat]# ll
total 8
drwxr-xr-x 2 root root 4096 Mar 21 14:45 logs
drwxr-xr-x 2 root root 4096 Mar 21 14:45 test
在 test 目录下丢项目即可
示例:编写一个index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>docker test</title>
</head>
<body><h1>----------hello akuya----------------</h1><br><h2>my docker tomcattest</h2>
</body>
<script>console.log("my docker tomcat log")
</script>
</html>
浏览器访问ip地址:8080/test/
发布镜像
公有仓库:DockerHub
目前 DockerHub 网址无法访问,了解即可
注册dockerhub https://hub.docker.com/signup,需要有一个账号
发布镜像遵循以下几个步骤:
- 登录用户:
docker login
参数:
-u
:等价于--username
,登录所需的用户名-p
:等价于--password
,密码
- 给要上传的镜像打上标签
docker tag 镜像id 镜像名:版本号
- 上传镜像
docker push 镜像名:版本号
私有仓库,一般都是公司内部自行搭建的,步骤跟上述相同
docker 网络
准备工作:清空所有镜像和容器
docker rm -f $(docker ps -aq)
docker rmi -f $(docker images -aq)
docker0 网络
查看本地网络
ip addr
[root@iZbp15293q8kgzhur7n6kvZ /]# ip addr# 本地回环网络
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever# 网卡地址 wifi
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:16:3e:2a:98:75 brd ff:ff:ff:ff:ff:ffinet 192.168.0.176/24 brd 192.168.0.255 scope global dynamic eth0valid_lft 315187556sec preferred_lft 315187556secinet6 fe80::216:3eff:fe2a:9875/64 scope link valid_lft forever preferred_lft forever# docker 0 ,docker创建的网络
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:c0:01:1f:34 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c0ff:fe01:1f34/64 scope link valid_lft forever preferred_lft forever
-
大型项目微服务(一个服务对应一个容器)这么多,要怎么互相访问?如果通过 ip 来访问,但是当容器重新启动的时候 ip 都会被重新分配,所以显然不能用 ip 来访问容器,所以最好的办法是通过容器名来访问
-
docker 每启动一个容器,就会给他分配一个 ip。docker0 就是 docker 默认给的网络,在我们不指定网络的情况下,创建的容器都在 docker0 网络中,未来开发,我们要自定义网络。
这里我们启动一个容器,查看一下它的 ip
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd --name web01 centos
Unable to find image 'centos:latest' locally
latest: Pulling from library/centos
a1d0c7532777: Already exists
Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177
Status: Downloaded newer image for centos:latest
8708f9efa52cb166be8c9904edd95c2d37d51475c1daf78213fcb8d55c402d53# docker每启动一个容器,就会给他分配一个ip。这个ip就是归docker0 管理
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
116: eth0@if117: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever# 并且容器外的主机是可以 ping 通这个容器的
[root@iZbp15293q8kgzhur7n6kvZ /]# ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.097 ms
我们对比一下宿主机的网络和容器的网络
可以发现,在启动容器后,容器内会分配一个网络eth0@if117
,而宿主机也多了一个网络117: veth9f88f58@if116
,它俩是互相配对的
再添加一个容器 web02,进行对比
web02 ip:
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd --name web02 centos
de6c34801c3db4a14f49a81d7532c0fb1461e989e2d7a19eaf1b587685b01ead
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web02 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft forever
118: eth0@if119: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever
宿主机 ip:
[root@iZbp15293q8kgzhur7n6kvZ /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:16:3e:2a:98:75 brd ff:ff:ff:ff:ff:ffinet 192.168.0.176/24 brd 192.168.0.255 scope global dynamic eth0valid_lft 315186479sec preferred_lft 315186479secinet6 fe80::216:3eff:fe2a:9875/64 scope link valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:c0:01:1f:34 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c0ff:fe01:1f34/64 scope link valid_lft forever preferred_lft forever
117: veth9f88f58@if116: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 7a:cb:75:a2:a4:38 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet6 fe80::78cb:75ff:fea2:a438/64 scope link valid_lft forever preferred_lft forever
119: veth5e85c8c@if118: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default link/ether 62:fd:a5:83:48:4b brd ff:ff:ff:ff:ff:ff link-netnsid 1inet6 fe80::60fd:a5ff:fe83:484b/64 scope link valid_lft forever preferred_lft forever
总结:
web01 --- linux 116:eth0@if117 --- 117:veth9f88f58@if116
web02 --- linux 118:eth0@if119 --- 119:veth5e85c8c@if118
- 只要启动一个容器,默认就会分配一对网卡
- 虚拟接口 # veth-pair 就是一对的虚拟设备接口,它都是成对出现的。一端连着协议栈,一端彼此相连着。
- 就好比一个桥梁,可以连通容器内外。
下面测试,两个容器之间通过 ip 访问情况
# web01 ping web02
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web01 ping 172.17.0.3
PING 172.17.0.3 (172.17.0.3) 56(84) bytes of data.
64 bytes from 172.17.0.3: icmp_seq=1 ttl=64 time=0.113 ms
64 bytes from 172.17.0.3: icmp_seq=2 ttl=64 time=0.054 ms
64 bytes from 172.17.0.3: icmp_seq=3 ttl=64 time=0.050 ms
^C
--- 172.17.0.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.050/0.072/0.113/0.029 ms# web02 ping web01
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web02 ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.078 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.065 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.079 ms
^C
--- 172.17.0.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.065/0.074/0.079/0.006 ms
发现两个容器是可以通过 ip 来相互 ping 通的
总结:
docker 使用 Linux 桥接,在宿主机虚拟一个 docker 容器网桥(docker0),docker 启动一个容器时会根据 docker 网桥的网段分配给容器一个 IP 地址,称为 Container-IP,同时 docker 网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP 直接通信。
docker 容器网络就很好的利用了 Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫 veth pair);
docker 中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为 Linux 是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。
--link
参数
前面我们发现容器之间是可以通过 ip 相互 ping 通的,但是一旦容器删除后再次启动一个新的容器,ip 发生了变化怎么办?
所以最好通过容器名来相互访问,我们测试两个容器之间能否通过容器名相互 ping 通
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web01 ping web02
ping: web02: Name or service not known
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web02 ping web01
ping: web01: Name or service not known
发现两个容器是无法相互 ping 通
怎么解决这个问题?我们可以在容器运行时使用--link
参数指定它可以通过容器名去 ping 通的容器
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd --name web03 --link web02 centos
c50590fbd3880afbaeddf1d5f67274170328d4b40d63f109c84d2754f5ecca39
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web03 ping web02
PING web02 (172.17.0.3) 56(84) bytes of data.
64 bytes from web02 (172.17.0.3): icmp_seq=1 ttl=64 time=0.103 ms
64 bytes from web02 (172.17.0.3): icmp_seq=2 ttl=64 time=0.055 ms
64 bytes from web02 (172.17.0.3): icmp_seq=3 ttl=64 time=0.054 ms
^C
--- web02 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.054/0.070/0.103/0.024 ms
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web02 ping web03
ping: web03: Name or service not known
发现 web03 可以 ping web02,但是 web02 不能 ping web03
我们了解一下它为什么可以 ping 通?
其实它的原理和我们熟知的域名映射原理相同
我们在访问www.baidu.com
时,流程是先去本机的 hosts 中查找有没有映射,如果没有则去 DNS 找。那么 web02 就相当于www.baidu.com
,--link
就是在 web03 的 hosts 中配置了 web02 的映射
我们可以通过以下命令证实:
--link
过于麻烦,现在已经不常用了,我们一般都是使用自定义网络
自定义网络
有关 docker 网络命令
docker network COMMOND
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network --helpUsage: docker network COMMANDManage networksCommands:connect Connect a container to a networkcreate Create a networkdisconnect Disconnect a container from a networkinspect Display detailed information on one or more networksls List networksprune Remove all unused networksrm Remove one or more networksRun 'docker network COMMAND --help' for more information on a command.
列出所有的网络
docker network ls
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network ls
NETWORK ID NAME DRIVER SCOPE
4b2c8b4a7052 bridge bridge local
9377180a376d host host local
bc9bbface74e none null local
所有网络模式
网络模式 | 配置 | 说明 |
---|---|---|
bridge 模式 | –net=bridge | 默认值,在 docker 网桥 docker0 上为容器创建新的网络栈 |
none 模式 | –net=none | 不配置网络,用户可以稍后进入容器,自行配置 |
container 模式 | –net=container:name/id | 容器和另外一个容器共享 Network namespace。 kubernetes 中的 pod 就是多个容器共享一个Network namespace。 |
host 模式 | –net=host | 容器和宿主机共享 Network namespace |
用户自定义 | –net=自定义网络 | 用户自己使用 network 相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
我们看一下 docker0 网络
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network inspect bridge
[{"Name": "bridge","Id": "4b2c8b4a70520b4b20808abcade35c2c4758bb404d54c57667f6b22c8f2bdd2f","Created": "2024-03-19T16:00:30.274778926+08:00","Scope": "local","Driver": "bridge","EnableIPv6": false,"IPAM": {"Driver": "default","Options": null,"Config": [{# 网络配置: config配置,后面子网网段 255*255. 65534个地址(docker0网关)"Subnet": "172.17.0.0/16",# docker0 网关地址"Gateway": "172.17.0.1"}]},......# 在这个网络下的容器地址。Name 就是容器的名字"Containers": {"8708f9efa52cb166be8c9904edd95c2d37d51475c1daf78213fcb8d55c402d53": {"Name": "web01","EndpointID": "87460cc4bf26e390bcfd0b48308b1439155244d6127d1f52d10cf244ac7b0efa","MacAddress": "02:42:ac:11:00:02","IPv4Address": "172.17.0.2/16","IPv6Address": ""},"c50590fbd3880afbaeddf1d5f67274170328d4b40d63f109c84d2754f5ecca39": {"Name": "web03","EndpointID": "385f598cf212878438c827bb0bc07131e32227cc0485d373f238a4f518016628","MacAddress": "02:42:ac:11:00:04","IPv4Address": "172.17.0.4/16","IPv6Address": ""},"de6c34801c3db4a14f49a81d7532c0fb1461e989e2d7a19eaf1b587685b01ead": {"Name": "web02","EndpointID": "f73e8417c9d5b32f5ccc69de44730af018f471f9b85a6ea9ece74b61b6e99b73","MacAddress": "02:42:ac:11:00:03","IPv4Address": "172.17.0.3/16","IPv6Address": ""}},......}
]
创建容器时指定网络(–net)
docker run -itd --name web01 --net bridge centos
- 它是默认的
- 域名访问不通
- –link 域名通了,但是删了又不行
示例:自己定义一个网络
准备:删除所有容器
创建网络命令:
docker network create 网络名
参数:
-d
:等价于--driver
,指定网络模式(默认为 bridge)--subnet
:指定创建的网络的子网 IP 范围--gateway
:指定网络的默认网关
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network create \
> -d bridge \
> --subnet 192.169.0.0/16 \
> --gateway 192.169.0.1 \
> mynet
12f2b8c1ebf8b17230ab8ad427f43607a68f434c71d66d6473d3e2748f252cb7
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network ls
NETWORK ID NAME DRIVER SCOPE
4b2c8b4a7052 bridge bridge local
9377180a376d host host local
12f2b8c1ebf8 mynet bridge local
bc9bbface74e none null local
未来可以通过网络来隔离项目,包括集群环境也可以自定义网络去配置
我们通过我们自定义的网络去创建两个容器
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd --name web01 --net mynet centos
64eae3b402fca58e8e3d1442d2d094b09d165f74c89ec8562886bbae9d8857cc
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd --name web02 --net mynet centos
f444f053dee9c75edd6cc091dd62cee52b4b8637d39837e6f902e6dfa9b38be5
创建完成后,查看宿主机的网络:
[root@iZbp15293q8kgzhur7n6kvZ /]# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000link/ether 00:16:3e:2a:98:75 brd ff:ff:ff:ff:ff:ffinet 192.168.0.176/24 brd 192.168.0.255 scope global dynamic eth0valid_lft 315182855sec preferred_lft 315182855secinet6 fe80::216:3eff:fe2a:9875/64 scope link valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether 02:42:c0:01:1f:34 brd ff:ff:ff:ff:ff:ffinet 172.17.0.1/16 brd 172.17.255.255 scope global docker0valid_lft forever preferred_lft foreverinet6 fe80::42:c0ff:fe01:1f34/64 scope link valid_lft forever preferred_lft forever
122: br-12f2b8c1ebf8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:31:db:89:21 brd ff:ff:ff:ff:ff:ffinet 192.169.0.1/16 brd 192.169.255.255 scope global br-12f2b8c1ebf8valid_lft forever preferred_lft foreverinet6 fe80::42:31ff:fedb:8921/64 scope link valid_lft forever preferred_lft forever
124: veth18c6e58@if123: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-12f2b8c1ebf8 state UP group default link/ether 16:aa:84:ed:1b:99 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet6 fe80::14aa:84ff:feed:1b99/64 scope link valid_lft forever preferred_lft forever
126: vethb0f6cb2@if125: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master br-12f2b8c1ebf8 state UP group default link/ether 92:7a:5a:50:6a:94 brd ff:ff:ff:ff:ff:ff link-netnsid 1inet6 fe80::907a:5aff:fe50:6a94/64 scope link valid_lft forever preferred_lft forever
发现跟默认的 docker0 网络有很大不同
我们测试两个容器能否通过容器名 ping 通?
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web01 ping web02
PING web02 (192.169.0.3) 56(84) bytes of data.
64 bytes from web02.mynet (192.169.0.3): icmp_seq=1 ttl=64 time=0.071 ms
64 bytes from web02.mynet (192.169.0.3): icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from web02.mynet (192.169.0.3): icmp_seq=3 ttl=64 time=0.070 ms
^C
--- web02 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.061/0.067/0.071/0.008 ms
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web02 ping web01
PING web01 (192.169.0.2) 56(84) bytes of data.
64 bytes from web01.mynet (192.169.0.2): icmp_seq=1 ttl=64 time=0.067 ms
64 bytes from web01.mynet (192.169.0.2): icmp_seq=2 ttl=64 time=0.063 ms
64 bytes from web01.mynet (192.169.0.2): icmp_seq=3 ttl=64 time=0.067 ms
^C
--- web01 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.063/0.065/0.067/0.009 ms
诶!,我们惊喜地发现两个容器之间可以通过容器名 ping 通了
原理:
当你创建一个自定义网络并将容器连接到该网络时,docker 会为每个容器分配一个 DNS 名称。这个 DNS 名称是容器名,它在网络中是唯一的。docker 内部的 DNS 服务器会将这些容器名映射到相应的容器 IP 地址上。
因此,当一个容器想要访问另一个容器时,它可以使用目标容器的容器名作为主机名进行访问。docker 内部的 DNS 服务器会解析容器名并将其映射到相应的 IP 地址,从而实现容器之间的通信。
这种通过容器名进行访问的机制使得容器之间的通信更加方便和灵活。无论容器的 IP 地址如何变化,容器名始终是唯一且稳定的,可以方便地进行容器之间的通信和访问。
两个不同网络下的容器相互访问
如果两个容器在不同的网络下,无论是通过 ip 还是容器名都无法相互 ping 通,那么怎样能够让两个容器相互访问呢?
我们可以将一个容器连接到另一个网络
docker network connect 网络名 容器名
我们在 docker0 网络下创建一个容器
# 不指定网络下,默认就是 docker0 网络
[root@iZbp15293q8kgzhur7n6kvZ /]# docker run -itd --name akuya centos
f45124e9a4c2f466ec160159f05eb44de0f4e090c284fe7553d8cb8c4a5ea5ef# 发现是不能 ping 通的
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it akuya ping web01
ping: web01: Name or service not known
接下来我们将akuya
容器连接到我们自定义的mynet
网络下
# 连接网络
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network connect mynet akuya# 测试相互能否 ping 通
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it akuya ping web01
PING web01 (192.169.0.2) 56(84) bytes of data.
64 bytes from web01.mynet (192.169.0.2): icmp_seq=1 ttl=64 time=0.074 ms
64 bytes from web01.mynet (192.169.0.2): icmp_seq=2 ttl=64 time=0.069 ms
64 bytes from web01.mynet (192.169.0.2): icmp_seq=3 ttl=64 time=0.066 ms
^C
--- web01 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.066/0.069/0.074/0.010 ms
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it akuya ping web02
PING web02 (192.169.0.3) 56(84) bytes of data.
64 bytes from web02.mynet (192.169.0.3): icmp_seq=1 ttl=64 time=0.079 ms
64 bytes from web02.mynet (192.169.0.3): icmp_seq=2 ttl=64 time=0.056 ms
64 bytes from web02.mynet (192.169.0.3): icmp_seq=3 ttl=64 time=0.063 ms
^C
--- web02 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.056/0.066/0.079/0.009 ms
[root@iZbp15293q8kgzhur7n6kvZ /]# docker exec -it web01 ping akuya
PING akuya (192.169.0.4) 56(84) bytes of data.
64 bytes from akuya.mynet (192.169.0.4): icmp_seq=1 ttl=64 time=0.060 ms
64 bytes from akuya.mynet (192.169.0.4): icmp_seq=2 ttl=64 time=0.057 ms
64 bytes from akuya.mynet (192.169.0.4): icmp_seq=3 ttl=64 time=0.102 ms
^C
--- akuya ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.057/0.073/0.102/0.020 ms
经过测试我们发现,它们都可以相互 ping 通了
我们查看一下 docker0 网络和自定义的 mynet 网络信息
docker0 网络:
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network inspect bridge
[{......"Containers": {"f45124e9a4c2f466ec160159f05eb44de0f4e090c284fe7553d8cb8c4a5ea5ef": {"Name": "akuya","EndpointID": "b3b277ce1dba8c8a6e7cd7f74361c26c9261c2f2c7d3777d68fe89443dc39150","MacAddress": "02:42:ac:11:00:02","IPv4Address": "172.17.0.2/16","IPv6Address": ""}},......}
]
里面存在akuya
容器,IP地址为 172.17.0.2
mynet 网络:
[root@iZbp15293q8kgzhur7n6kvZ /]# docker network inspect mynet
[{......"Containers": {"64eae3b402fca58e8e3d1442d2d094b09d165f74c89ec8562886bbae9d8857cc": {"Name": "web01","EndpointID": "aa68171e585ec3846874531d5201019b2e412554bdffa389876f3fc1f5078ca4","MacAddress": "02:42:c0:a9:00:02","IPv4Address": "192.169.0.2/16","IPv6Address": ""},"f444f053dee9c75edd6cc091dd62cee52b4b8637d39837e6f902e6dfa9b38be5": {"Name": "web02","EndpointID": "d69c67cda750727d844b3412d95f0441fbdd7f005ade3cb4d34394d8d1254734","MacAddress": "02:42:c0:a9:00:03","IPv4Address": "192.169.0.3/16","IPv6Address": ""},"f45124e9a4c2f466ec160159f05eb44de0f4e090c284fe7553d8cb8c4a5ea5ef": {"Name": "akuya","EndpointID": "4b8e56d6d8ec037e88b250bf6be76ae494c645584bbf6332ddd5c82a9ea5fc10","MacAddress": "02:42:c0:a9:00:04","IPv4Address": "192.169.0.4/16","IPv6Address": ""}},......}
]
里面同样存在akuya
容器,ip 地址为 192.169.0.4
结论:连接之后,akuya
这个容器拥有了两个 ip,一个容器是可以有多个 ip 的。
如果要跨网络访问别的服务,就需要使用 docker network connect [OPTIONS] NETWORK CONTAINER
连接。
Docker Compose
dockercompose官网:https://docs.docker.com/compose/
什么是 docker compose
Docker Compose 是用于定义和运行多容器应用程序的工具。 这是解锁简化和高效的开发和部署体验的关键。
Compose 简化了对整个应用程序堆栈的控制,让您能够在单个易于理解的 yaml 配置文件中轻松管理服务、网络和卷。然后,只需一个命令,即可创建并启动所有服务 从您的配置文件中。
Compose 适用于所有环境;生产、暂存、开发、测试,以及 以及 CI 工作流程。它还具有用于管理应用程序整个生命周期的命令:
- 启动、停止和重新生成服务
- 查看正在运行的服务状态
- 流式传输正在运行的服务的日志输出
- 在服务上运行一次性命令
为什么使用 docker compose
问题:
前面我们使用 docker 的时候,定义 Dockerfile 文件,然后使用 docker build、docker run 等命令操作容器,然而微服务架构的应用系统一般包含若干个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启停,那么效率之低,维护量之大可想而之。
使用 docker compose 可以轻松,高效的管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具。
docker compose 优势
使用 docker compose 可简化容器化应用程序的开发、部署和管理:
- 简化控制:Docker Compose 允许您在单个 YAML 文件中定义和管理多容器应用程序。这简化了编排和协调各种服务的复杂任务,使管理和复制应用程序环境变得更加容易。
- 高效协作:Docker Compose 配置文件易于共享,便于开发人员、运营团队和其他利益相关者之间的协作。这种协作方法可以实现更顺畅的工作流程、更快的问题解决速度和更高的整体效率。
- 快速应用程序开发:Compose 缓存用于创建容器的配置。当您重新启动未更改的服务时,Compose 会重新使用现有容器。重用容器意味着您可以非常快速地对环境进行更改。
- 跨环境的可移植性:Compose 支持 Compose 文件中的变量。您可以使用这些变量为不同的环境或不同的用户自定义您的组合。
- 广泛的社区和支持:Docker Compose 受益于充满活力和活跃的社区,这意味着丰富的资源、教程和支持。这个社区驱动的生态系统有助于 Docker Compose 的持续改进,并帮助用户有效地解决问题。
小结
compose 项目是 docker 官方的开源项目,负责实现 docker 容器集群的快速编排
开源代码:https://github.com/docker/compose
我们知道使用 dockerfile 模板文件可以让用户很方便的定义一个单独的应用容器,其实在工作中,经常会碰到需要多个容器相互配合来完成的某项任务情况,例如工作中的 web 服务容器本身,往往会在后端加上数据库容器,甚至会有负责均衡器,比如 LNMP 服务
Compose 就是来做这个事情的,它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML格式)来定义一组相关联的应用容器为一个项目(project)
Compose 中有两个重要的概念:
- 服务(service):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例,很多的镜像启动
- 订单服务 image
- 物流服务 image
- 用户服务 image
- 支付服务 image
- 4个容器后面构成一个服务 service
- 项目(project):由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 中定义。
Compose 项目是由 Python 编写的,实际上就是调用了 Docker 服务提供的 API 来对容器进行管理,因此,只要所在的操作系统的平台支持 Docker API,就可以在其上利用 Compose 来进行编排管理。
安装 docker compose
- yum 安装
yum -y update
yum install -y docker-compose-plugin
- 一般情况下,linux 要安装 python 环境
# pip 是 python 包管理工具
yum install -y python-pip
- 输入命令
docker compose version
查看是否安装成功
[root@iZbp15293q8kgzhur7n6kvZ ~]# docker compose version
Docker Compose version v2.25.0
快速开始
安装官方演示的案例:https://docs.docker.com/compose/gettingstarted/
- 为项目创建目录
mkdir composetest
cd composetest
- 创建一个一会要运行的文件
app.py
vim app.py
代码如下:
import timeimport redis
from flask import Flaskapp = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)def get_hit_count():retries = 5while True:try:return cache.incr('hits')except redis.exceptions.ConnectionError as exc:if retries == 0:raise excretries -= 1time.sleep(0.5)@app.route('/')
def hello():count = get_hit_count()return 'Hello World! I have been seen {} times.\n'.format(count)
在此示例中,是 redis 容器的主机名应用程序的网络,我们使用 Redis 的默认端口。
- 创建另一个在项目目录中调用的文件
requirements.txt
,内容如下:
flask
redis
- 创建
Dockerfile
,内容如下:
# syntax=docker/dockerfile:1
FROM python:3.10-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
这会告诉 docker :
- 从 Python 3.10 映像开始构建映像。
- 将工作目录设置为
/code
- 设置命令使用的环境变量
flask
- 安装 gcc 和其他依赖
- 复制并安装 Python 依赖项
requirements.txt
- 将元数据添加到映像,以描述容器正在侦听端口 5000
- 将项目中的当前目录复制到映像中的 workdir,
.
代表当前目录 - 将容器的默认命令设置为
flask run
- 在 compose 文件中定义服务,创建文件
compose.yaml
# 定义版本
version: '3.8'
services:web:build: .ports:- "8000:5000"# 挂载数据卷,将容器工作目录/code挂载到当前目录下volumes:- .:/coderedis:image: "redis:alpine"
此 Compose 文件定义了两个服务:web 和 redis
该服务使用从当前目录中生成的映像。 然后,它将容器和主机绑定到公开的端口,此示例服务使用 Flask Web 服务器的默认端口
该服务使用redis
从 Docker Hub 注册表中提取的映像
- 使用 compose 构建并运行应用
[root@iZbp15293q8kgzhur7n6kvZ composetest]# ll
total 16
-rw-r--r-- 1 root root 514 Mar 21 19:49 app.py
-rw-r--r-- 1 root root 230 Mar 21 19:58 compose.yaml
-rw-r--r-- 1 root root 282 Mar 21 19:52 Dockerfile
-rw-r--r-- 1 root root 12 Mar 21 19:51 requirements.txt
注意文件不要有缺失
在这个目录下执行以下命令构建:
docker compose up
安装完成后打开浏览器输入ip地址:8000
,会看到以下信息
Hello World! I have been seen 1 times.
每刷新一次,数字都会增加
项目完成后,我们查看所有的镜像
会发现多了一个镜像,这个镜像是由项目名-web
(composetest-web)来构成的
查看容器服务:
# 由于是单机版的docker,这个服务无法查看,集群环境中才可以看到。
[root@iZj6c1wvscj5y05fyq1yiqZ ~]# docker service ls
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.
# 发现一次拉起了两个容器,这个就是 compose.yaml 定义的服务
# 容器名规律: 项目名-容器服务名-1编号
[root@iZj6c1wvscj5y05fyq1yiqZ ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
292c625ce219 redis:alpine "docker-entrypoint.s…" 6 minutes ago Up 3 minutes 6379/tcp composetest-redis-1
21f4f7a63283 composetest-web "python app.py" 6 minutes ago Up 3 minutes 0.0.0.0:8000->5000/tcp composetest-web-1
停止容器服务:
[root@iZj6c1wvscj5y05fyq1yiqZ composetest]# docker compose down
[+] Running 3/2✔ Container composetest-redis-1 Removed 0.2s ✔ Container composetest-web-1 Removed 0.3s # compose 会自动创建一个服务的网络✔ Network composetest_default Removed
发现多了一个自定义的网络,我们再次启动这个 compose,查看这个自定义网络信息
这里面就有我们刚刚看到的两个容器服务
总结
- 使用 compose 文件构建运行的镜像名字是
项目目录名-web
- compose 中每一个服务都会生成一个容器,名字是
项目目录名-服务名-web
- 构建时会生成一个自定义网络,所有的容器服务都会在这一个网络下
compose 常用命令
# Compose 大部分命令的对象即可以是项目的本身,也可以是指定为项目中的服务或者容器
执行docker compose [COMMAND] --help 或者docker compose help [COMMAND]可以查看命令的帮助信息# 具体的使用格式
docker compose [-f=<arg>...] [options] [COMMAND] [ARGS]# 参数选项
-f,--file file指定模板文件,默认是docker-compose.yml模板文件,可以多次指定
-p,--project-name name指定项目名称,默认使用所在目录名称作为项目名称
-v,--version 输出版本信息# Compose所支持的命令
build Build or rebuild services (构建项目中的服务容器)
config Validate and view the Compose file (验证并查看Compose文件)
create Create services (为服务创建容器)
down Stop and remove containers, networks, images, and volumes (停止容器并删除由其创建的容器,网络,卷和图像up)
events Receive real time events from containers (为项目中的每个容器流式传输容器事件)
exec Execute a command in a running container (这相当于docker exec。使用此子命令,您可以在服务中运行任意命令。默认情况下,命令分配TTY,因此您可以使用命令docker compose exec web sh来获取交互式提示。)
help Get help on a command (获得一个命令的帮助)
images List images ()
kill Kill containers (通过发送SIGKILL信号来强制停止服务容器)
logs View output from containers (查看服务容器的输出)
pause Pause services (暂停一个容器)
port Print the public port for a port binding (打印某个容器端口所映射的公共端口)
ps List containers (列出项目中目前所有的容器)
pull Pull service images (拉取服务依赖镜像)
push Push service images (推送服务镜像)
restart Restart services (重启项目中的服务)
rm Remove stopped containers (删除所有停止状态的服务容器)
run Run a one-off command (在指定服务上执行一个命令)
start Start services (启动已存在的服务容器)
stop Stop services (停止已存在的服务容器)
top Display the running processes (显示容器正在运行的进程)
unpause Unpause services (恢复处于暂停状态的容器)
up Create and start containers (自动完成包括构建镜像、创建服务、启动服务并关联服务相关容器的一系列操作)
version Show the Docker-Compose version information (输出版本)
docker run
启动一个容器,docker compose
启动多个容器(服务网络问题-自定一个网络。所有服务按照项目名来定义,编号)
说明:
一个普通的工作流程以docker compose up -d
启动应用程序开始。
docker compose logs
和ps
命令可以用来验证应用程序的状态,还能帮助调试。
修改代码后,先执行docker compose build
构建新的镜像,然后执行docker compose up -d
取代运行中的容器。
注意,compose 会保留原来容器中所有旧的数据卷,这意味着即使容器更新后,数据库和缓存也依旧在容器内(这很可能造成混淆,因此要特别小心)。
如果你修改了 compose 的 yaml 文件,但不需要构建新的镜像,可以通过up -d
参数使 compose 以新的配置替换容器。
如果想要强制停止 compose 并重新创建所有容器,docker compose stop 容器名
和 docker compose rm 容器名
简单使用:
- 执行命令运行容器:
docker compose up -d
- 查看镜像:
docker images
- 停止和删除容器:
docker compose stop 容器名
和docker compose rm 容器名
compose.yaml
模板文件是 compose 的核心,涉及的指令关键字比较多,但是大部分的指令与 docker run 相关的参数的含义是类似的
默认的模板名是docker-compose.yml
# 语法-3层
version: '3.8'
services: # 定义很多服务服务1:# 当前的服务配置服务2:# 当前服务配置
#服务要用的网络、卷、等其他全局规则
volumes:
networks:
configs:
.....
compose 和 docker兼容性:compose 文件格式有3个版本,分别为1, 2.x 和 3.x目前主流的为 3.x 其支持 docker 1.13.0 及其以上的版本
# 常用参数:
version # 指定 compose 文件的版本
services # 定义所有的 service 信息, services 下面的第一级别的 key 既是一个 service 的名称服务build # 指定包含构建上下文的路径, 或作为一个对象,该对象具有 context 和指定的 dockerfile context # context: 指定 Dockerfile 文件所在的路径dockerfile # dockerfile: 指定 context 指定的目录下面的 Dockerfile 的名称(默认为 Dockerfile)args # args: Dockerfile 在 build 过程中需要的参数 command # 覆盖容器启动后默认执行的命令, 支持 shell 格式和 [] 格式container_name # 指定容器的名称 (等同于 docker run --name 的作用)deploy # v3 版本以上, 指定与部署和运行服务相关的配置# deploy 部分是 docker stack 使用的, docker stack 依赖 docker swarmdepends_on # 定义容器启动顺序 (此选项解决了容器之间的依赖关系),一个项目需要依赖另外一个,启动顺序很重要,一定要先去加载启动依赖项的容器dns # 设置 DNS 地址(等同于 docker run --dns 的作用)entrypoint # 覆盖容器的默认 entrypoint 指令env_file # 从指定文件中读取变量设置为容器中的环境变量,可以是单个值或者一个文件列表environment # 设置环境变量, environment 的值可以覆盖 env_file 的值expose # 暴露端口, 但是不能和宿主机建立映射关系, 类似于 Dockerfile 的 EXPOSE 指令external_links # 连接不在 docker-compose.yml 中定义的容器或者不在 compose 管理的容器extra_hosts # 添加 host 记录到容器中的 /etc/hosts 中healthcheck # v2.1 以上版本, 定义容器健康状态检查image # 指定 docker 镜像, 可以是远程仓库镜像、本地镜像labels # 使用 Docker 标签将元数据添加到容器logging # 设置容器日志服务network_mode # 指定网络模式 (等同于 docker run --net 的作用, 在使用 swarm 部署时将忽略该选项) networks # 将容器加入指定网络 (等同于 docker network connect 的作用)# networks 可以位于 compose 文件顶级键和 services 键的二级键pid: 'host' # 共享宿主机的 进程空间(PID)ports # 建立宿主机和容器之间的端口映射关系, ports 支持两种语法格式- "8000:8000" # 容器的 8000 端口和宿主机的 8000 端口建立映射关系volumes # 定义容器和宿主机的卷映射关系- /var/lib/mysql # 映射容器内的 /var/lib/mysql 到宿主机的一个随机目录中- /opt/data:/var/lib/mysql # 映射容器内的 /var/lib/mysql 到宿主机的 /opt/data- ./cache:/tmp/cache # 映射容器内的 /var/lib/mysql 到宿主机 compose 文件所在的位置- ~/configs:/etc/configs/:ro # 映射容器宿主机的目录到容器中去, 权限只读- datavolume:/var/lib/mysql # datavolume 为 volumes 顶级键定义的目录, 在此处直接调用.....# 对于值为时间的可接受的值:2.5s10s1m30s2h32m5h34m56s时间单位: us, ms, s, m, h
# 对于值为大小的可接受的值:2b1024kb2048k300m1gb单位: b, k, m, g 或者 kb, mb, gb
# depends_on示例
version: '3'
services:web:build: .depends_on:- db - redis redis:image: redisports:- "6379:6379"environment:- root_pwd: "123456"db:image: postgres # docker compose up 以依赖顺序启动服务,下面例子中 redis 和 db 服务在 web 启动前启动
# 默认情况下使用 docker compose up web 这样的方式启动 web 服务时,也会启动 redis 和 db 两个服务,因为在配置文件中定义了依赖关系
# network示例
version: '3.7'services:test:image: nginx:1.14-alpinecontainer_name: mynginxcommand: ifconfignetworks:app_net: # 调用下面 networks 定义的 app_net 网络ipv4_address: 172.16.238.10networks:app_net:driver: bridgeipam:driver: defaultconfig:- subnet: 172.16.238.0/24- getaway: 172.16.238.1
小结
以后我们的微服务项目中,如果有多个微服务,则按照他们的启动顺序,配置对应的规则文件即可!
可以去看看你们的公司项目,有没有 docker-compose.yml 文件,有没有做自动编排!
如果下载的网上开源项目,有 dockerfile + docker-compose ,那我们可以一键搞定!
网上的项目拉下来,mysql 的 sql 文件导入数据库。 docker-compose up 即可!
dockerfile + docker-compose。
工程、服务、容器
- docker compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container)
- docker compose 运行目录下的所有文件(docker-compose.yml)组成一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例!
至此,dokcer compose 搞定!
实战:WordPress
WordPress 是一款个人博客系统
在目录下编辑docker-compose.yml
文件:
version: '3.8'services:db:image: mysql:5.7volumes:- db_data:/var/lib/mysqlrestart: alwaysports:- "3306:3306"environment:MYSQL_ROOT_PASSWORD: somewordpressMYSQL_DATABASE: wordpressMYSQL_USER: wordpressMYSQL_PASSWORD: wordpresswordpress:depends_on:- dbimage: wordpress:latestports:- "8000:80"restart: alwaysenvironment:WORDPRESS_DB_HOST: db:3306WORDPRESS_DB_USER: wordpressWORDPRESS_DB_PASSWORD: wordpressWORDPRESS_DB_NAME: wordpress
volumes:db_data: {}
执行以下命令:
docker compose up -d
启动成功打开浏览器,输入ip地址:8000
访问
出现这个证明安装成功
进入之后随便发布一篇文章,查看 mysql 中有哪些数据
mysql 用户名是wordpress
,密码是wordpress
,上面 yaml 文件中有配置
总结
docker 卷 db_data
将 WordPress 所做的所有更新持久化到数据库。
WordPress Multisite 仅适用于 port 80
和443
docker compose up -d
从你的项目目录运行。
测试访问!
未来只需要一键启动就可以安装好环境了!无论多复杂都是一键。