简介
- docker相关的理论基础
理解Docker镜像和容器的存储路径
执行以下命令可以查看Docker的配置信息
1
docker info
输出内容包含了存储驱动和docker根目录的信息
docker镜像和容器的存储路径
- Docker容器由网络文件,卷和镜像组成。Docker文件的存储路径取决于你的操作系统。
- 常用操作系统中的路径如下
- Ubuntu: /var/lib/docker/
- Fedora: /var/lib/docker
- Debian: /var/lib/docker
- Windows: C:\ProgramData\DockerDesktop
- MacOS:
/Library/Containers/com.docker.docker/Data/vms/0/
- 在MacOS和Windows系统中,Docker在一个虚拟机中运行Linux容器
查看常规镜像的信息,会得到linux系统中的路径
1
2
3
4docker insepct nginx
"UpperDir":"/var/lib/docker/overlay2/585...9eb/diff"Docker镜像。最大的文件通常是镜像。如果使用默认的overlay2存储驱动,Docker镜像会保存在/var/lib/docker/overlay2目录
LowerDir包含镜像的只读层,表示变更的读写层包含在UpperDir中。
MergeDir表示UppeDir和LowerDir合并的结果,Docker用它来运行容器
WorkDir是overlay2的内部目录,应该是空的
docker 一般使用什么方式设置自启动服务
在 Docker 中,通常使用以下两种方式之一来设置容器的自启动服务:
使用 Docker Compose:
Docker Compose 是一个用于定义和管理多容器应用程序的工具。在 Docker Compose 配置文件中,你可以定义容器的启动顺序和依赖关系。当使用docker-compose up
启动容器组时,它们将按照定义的顺序自动启动,并在 Docker 守护进程启动时自动启动。在 Docker Compose 配置文件中,可以使用
restart
字段来设置容器的自启动行为。以下是一个示例 Docker Compose 配置文件的部分内容:1
2
3
4
5version: '3'
services:
web:
image: nginx:latest
restart: always在上述示例中,
restart: always
指示 Docker 在容器退出时自动重新启动该容器。使用 systemd 或其他宿主机的服务管理工具:
如果你的 Docker 容器运行在一个宿主机上,并且宿主机使用 systemd 或其他服务管理工具(如 Upstart 或 SysV init),你可以在宿主机上创建一个服务单元文件来管理 Docker 容器的启动和自启动行为。在宿主机上,你可以创建一个自定义的 systemd 服务单元文件(通常位于
/etc/systemd/system/
目录下),并使用docker run
命令来启动容器。然后,通过配置 systemd 服务单元文件,将 Docker 容器与宿主机的服务管理工具集成在一起,以实现容器的自启动。以下是一个示例 systemd 服务单元文件的部分内容:
1
2
3
4
5
6
7
8
9
10
11[Unit]
Description=Docker Container: My Service
Requires=docker.service
After=docker.service
[Service]
ExecStart=/usr/bin/docker run --name my-container my-image
Restart=always
[Install]
WantedBy=default.target在上述示例中,
ExecStart
字段指定了启动容器的命令。Restart=always
字段指示 systemd 在容器退出时自动重新启动。
无论你选择使用 Docker Compose 还是宿主机的服务管理工具,都可以根据具体需求来设置容器的自启动服务。请注意,在设置自启动服务时,要确保 Docker 服务已经正确安装和启动,并且容器的镜像已经准备好。
docker systemctl 无效
在 Docker 容器中,systemctl
命令无效,因为容器中并没有运行 Systemd 进程,而 systemctl
命令是 Systemd 的服务管理工具。
Docker 容器是基于轻量级的容器化技术构建的,它们通常以单个进程运行,并不包含完整的操作系统。因此,与在主机操作系统上一样使用 systemctl
命令是不可行的。
在 Docker 中,你可以通过以下方式来管理容器内的服务或进程:
启动和停止容器:使用
docker start
和docker stop
命令来启动和停止容器,例如:1
2docker start container_name
docker stop container_name运行服务进程:在容器中直接运行服务进程,可以通过 Dockerfile 或者启动容器时指定要运行的命令。
使用 Docker Compose:如果你在 Docker Compose 中定义了服务,可以使用
docker-compose up
和docker-compose down
命令来启动和停止整个服务组。
请记住,Docker 容器是设计用于运行单个进程或服务的,而不是一个完整的操作系统环境。因此,与在传统的虚拟机或物理机上管理服务的方式有所不同。你可以通过容器的启动命令和配置来管理容器内的服务和进程。
目录映射
-p
:指定端口映射,格式为:主机(宿主)端口:容器端口--privileged=true
:关闭安全权限,否则容器操作文件没有权限-v
:挂在目录为:主机目录:容器目录,在创建前容器是没有指定目录时,docker容器会自己创建- 端口映射:
-p 8083:8083 -p 5678:5678
- 目录映射以宿主机为主,换句话说,容器中原本存在的文件一旦进过挂载,就一定会被宿主机覆盖
如何处理overlay2
- 真正要处理的不是这两个文件,是要检查正在跑的容器内的服务,有没有往容器本地写文件的行为,如果有,则要把写文件的路径挂载出来,或者直接在容器中删除文件
docker 语言支持
- node.js
- python
- java
- go
- c#
docker中的overlay2
- 在安装的docker系统中,删除了容器和镜像,但是docker/overlay2还是占用很大的磁盘空间
docker/overlay2下的文件都是些什么
- docker/overlay2目录下的文件名基本都是md5编码
- overlay2是docker使用的文件存储驱动,也就是说,该目录下的文件都是docker使用的存储
- overlay2是分层存储,每一层通过本层的md5作为文件夹名来命名,如果要存储的两个东西,例如两个镜像的底层中的几层内容是一样的,那么它们的md5也就是一样的,通过md5检验,确认它们这几层是一样的,那么在overlay2实际存储的时候,这几层就可以只存储一份,然后由这两个镜像共同使用,来达到节省空间的目的
docker底层原理
第一次接触docker概念,都会见到或者听见一句话:docker技术比虚拟技术更为方便,快捷,docker容器本质上是进程
所有容器共享宿主机的cpu,磁盘,网络,内存等:
- 实现了进程隔离,每个服务独立运行
- 文件系统隔离,容器目录修改不影响主机目录
- 资源隔离,CPU内存,磁盘,网络资源相互独立
Docker容器的实现原理就是通过Namespace命名空间进行进程隔离,Unionfilesystem联合文件系统实现文件系统隔离,ControlGroups控制组实现资源隔离。
其底层原理涉及到
linux namespace
,Linux Namespace 是Linux提供的一种内核级别环境隔离的方法。 Unix有chroot
,提供了一种简单的模式:chroot
内部的文件系统无法访问外部的内容。Linux Namespace
在此基础上,提供了对UTS, IPC, mount, PID, network, User
等的隔离机制https://lwn.net/Articles/531114/
Linux Namespace, 有几个种类:
Mount namespaces
,UTS namespaces
,IPC namespaces
,PID namespaces
,Network namespaces
,User namespaces
主要是三个系统调用:
clone()
, 实现线程的系统调用,用来创建一个新的进程,并且可以通过设计上述参数达到隔离unshare()
, 使某个进程脱离某个namespace
setns()
, 把某进程加入到某个namespace
Docker exec 的实现原理
Linux Namespace
创建的隔离空间是虚拟的,一个进程的Namespace
信息在宿主机上是真实存在的,并且是以一个文件的方式存在。- 一个进程,可以选择加入到某个进程已有的
Namespace
当中,从而达到“进入”这个进程所在容器的目的,这正是docker exec
的实现原理 - 通过
setns()
可以将当前进程加入到已有的namespace
中。
镜像启动容器的本质
- 首先,需要明确Docker内的文件系统是如何工作的,Docker镜像被存储在一系列的只读层。
- Docker镜像是由多个文件系统(只读层)叠加而成,当启动一个容器的时候,Docker会加载只读层并在其上(镜像栈顶部)添加一个读写层
- 如果运行中的容器修改了现有的一个已经存在的文件,那么该文件将会从读写层下面的只读层复制到读写层,该文件的只读版本仍然存在,只是已经被读写层中该文件的副本所隐藏。当删除Docker容器,并通过该镜像重启的时候,之前的修改将会丢失
- 在Docker中,只读层和顶部的读写层的组合被称为联合文件系统(Union File System)
- Docker镜像可以理解成多个只读文件叠加而成,因此镜像是只读的,当镜像运行起来时,就相当于在只读的镜像外面包裹了一层读写层变成了容器。
- 当删除容器之后,使用镜像重新创建一个容器时,此时的镜像的只读层和原来一样,只是在读写层的修改会全部丢失
- 所以,docker的数据持久化说的就是:数据不随容器的删除而消失。
数据卷Volume
- Docker的数据卷Volume能够让容器从宿主机中读取文件或持久化数据到宿主机内,让容器与容器产生的数据分离开来。
- 可以简单的把Volume理解为Linux服务器上的挂载点,一个容器可以挂载多个不同的目录
- Volume的生命周期是独立于容器的声明周期之外的,即使容器删除,Volume也会保留下来,Docker也不会因为这个Volume没有被容器使用而回收。在容器中,添加或修改这个文件夹中的文件也不会影响容器的联合文件系统。
- Volume数据卷不是用分层文件系统,这对经常读取和写入的数据很有用。在开发过程中,可以将代码目录挂载到容器中,这样如果更改代码容器会实时地得到文件修改的返回文件。
Docker数据管理-数据库容器化并持久化:数据卷概念,创建数据卷的两种方式,docker volume用法
Docker数据管理
- 在生产环境中使用Docker的过程,往往需要对数据进行持久化,或者需要在多个容器之间进行数据共享,这必然涉及容器的数据管理操作
- 所谓Docker的数据持久化,即:数据不随着容器的结束而结束。在Docker中,要想实现数据的持久化,需要将数据从宿主机挂载到容器中
- 容器中管理数据主要有两种方式:
- 数据卷(Data Volumes):容器内数据直接映射到本地主机环境
- 数据卷容器(Data Volume Containers):使用特定容器维护数据卷
Docker核心概念:镜像,容器,仓库,架构核心设计理念
Docker核心概念
镜像,通俗地讲,它是一个只读的文件和文件夹组合。
它包含了容器运行时所需要的所有基础文件和配置信息,是容器启动的基础。所以想启动一个容器,那首先必须要有一个镜像。镜像是Docker容器启动的先决条件
如果想要使用一个镜像,一般有两种方式:
- 自己创建镜像。通常情况下,一个镜像是基于一个基础镜像构建的,可以在基础镜像上添加一些用户自定义的内容。形成业务镜像。
- 从功能镜像仓库拉取别人制作好的镜像
容器,是Docker的另一个核心概念。
通俗地讲,容器是镜像的运行实体。镜像是静态的只读文件,而容器带有运行时需要的可写文件层,并且容器中的进程属于运行状态。即容器运行着真正的应用进程
虽然容器的本质上是主机上运行的一个进程,但是容器有自己独立的命名空间隔离和资源限制。也就是说,在容器内部,无法看到主机上的进程,环境变量,网络等信息,这是容器于直接运行在主机上进程的本质区别。
仓库,Docker的镜像仓库类似于代码仓库,用来存储和分发Docker镜像。
镜像仓库分为公有镜像仓库和私有镜像仓库
Docker架构
相关背景知识—-容器的发展史
- 容器技术随着Docker的出现变得炙手可热,所有公司都在积极拥抱容器技术。此时市场上除了有Docker容器,还有很多其他的容器技术,例如:CoresOS的rkt, lxc等。容器技术百花齐放是好事,但是也出现了很多问题,比如容器技术的标准到底是什么?
- 可能会说,Docker已经成为了事实标准,把Docker作为容器技术的标准不可以吗?事实并没有想象的那么简单。因为那个时候不仅有容器标准之争,编排技术之争也十分激烈。当时的编排技术有三大主力,分别是:
Docker Swarm, Kubernetes, Mesos
。在这样的背景下,为了形成统一的标准,OCI应运而生。 - OCI全称为开放容器标准(
Open Container Initiative
),它是一个轻量级,开放的治理结构。OCI组织在Linux基金会的大力支持下,于2015年6月分正式注册成立。基金会旨在为用户围绕工业化容器的格式和镜像运行时,制定一个开放的容器标准。目前主要有两个标准文档:**容器运行时标准(runtime spec
)和容器镜像标准(image spec
) - 正是由于容器的战争,才导致Docker不得不改变了一些技术架构。最终,Docker整体架构采用C/S(客户端/服务器)模式,主要由客户端和服务端两大部分组成。客户端负责发送操作指令,服务端负责接受和处理指令。客户端和服务端通信有多种方式,既可以在通一台机器上通过UNIX套接字通信,也可以通过网络连接远程通信。
Docker客户端
- Docker客户端其实是一种泛称。其中docker命令是Docker用户与Docker服务端交互的主要方式。
- 除了使用docker命令的方式,还可以使用直接请求REST API的方式与Docker服务端交互,甚至还可以使用各种语言的SDK与Docker服务端交互
Docker服务端
- Docker服务端是Docker所有后台服务的统称。
- 其中
dockerd
是一个非常重要的后台管理进程,它负责相应和处理来自Docker客户端的请求,然后将客户端的请求转换为Docker的具体操作。例如镜像,容器,网络和挂载卷等具体对象的管理和操作。 - Docker从诞生到现在,服务端经历了多次架构重构。起初,服务端的组件是全部集成在docker二进制里,但是从1.11版本开始,
dockerd
已经成了独立的二进制,此时的容器也不是直接由dockerd
来启动了,而是继承了containerd, runC
等多个组件 - 虽然Docker的架构在不停重构,但是各个模块的基本功能和定位并没有变化。它和一般的C/S架构系统一样,Docker服务端模块负责和Docker客户端交互,并管理Docker的容器,镜像,网络等资源。
- Docker重要组件
- Docker目前已经有了非常多的组件和工具。Docker的两个至关重要的组件:**
runC
和containerd
**runC
是Docker官方按照OCI容器运行时标准的一个实现。通俗地讲,**runC
是一个用来运行容器的轻量级工具,是真正用来运行容器的。**containerd
是Docker服务端的一个核心组件,它是从dockerd
中剥离出来的,它的诞生完全遵循OCI标准,是容器标准化后的产物。**containerd
通过containerd-shim
启动并管理runC
,可以说containerd
真正管理了容器的生命周期**。
dockerd
通过gRPC
与containerd
通信,由于dockerd
与真正的容器运行时,runC
中间有了containerd
这一OCI标准层,使的dockerd
可以确保接口向下兼容gRPC
是一种远程服务调用
containerd-shim
的意思是垫片,类似于拧螺丝时夹在螺丝和螺母之间的垫片。containerd-shim
的主要作用是:**将containerd
和真正的容器进程解耦,使用containerd-shim
作为容器进程的父进程,从而实现重启dockerd
不影响已经启动的容器进程- 事实上,
dockerd
启动的时候,containerd
就随之启动了,dockerd
与containerd
一直存在。当执行docker run
命令时,containerd
会创建containerd-shim
充当”垫片”进程,然后启动容器的真正进程。(**containerd-shim
是真正容器的进程的父进程,这么做为了不让真正的容器进程作为containerd
的子进程,从而可以实现重启containerd
而不影响已经运行的容器**)
- Docker目前已经有了非常多的组件和工具。Docker的两个至关重要的组件:**