image-20230907143010010

1.容器化衍生

虚拟化时代KVM

虚拟化产品

  • VMware
    • 单机产品:VMware Workstation
    • 集群产品:
    • 服务端:VCenterServer
    • 客户端:VspareClient
  • VirtualBox
  • Xen
  • ESXI
  • KVM
    • 磁盘:
    • raw:标准分区
    • qcow2:LVM

OpenStack编排KVM的工具

Nova:资源调度、资源计算
Keystone:各个组件之间的认证
glance:镜像存储
neutron:网络(flat扁平网络模式)
horizon:Dashboard仪表盘,图形化界面

一张图理解IAAS、PAAS、SAAS

image-20230907143854642

2.什么是Docker

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux 机器上。使用Docker可以让每个应用彼此相互隔离,在同一台机器上同时运行多个容器,他们彼此之间共享同一个操作系统。Docker的优势在于,它可以在更细的粒度上进行资源的管理,比虚拟化技术性能更高,更加节约资源。下面是普通虚拟化技术和Docker的对比。

LXC -> libcontainer
当谈论 LXC 转向 libcontainer 时,以下是相关容器技术和项目

  1. LXC(Linux Containers):LXC 是一种用于容器虚拟化的技术,提供在单个 Linux 主机上隔离运行多个容器的方法。
  2. libcontainer:libcontainer 是用于管理容器的开源库,最初由 Docker 公司开发,并成为 Open Container Initiative(OCI)标准的一部分。
  3. runC:runC 是一个用于创建和运行 OCI 容器的开源工具,它使用 libcontainer 来管理容器的生命周期。
  4. Docker:Docker 最初使用了 LXC 技术来实现容器化,后来发展了自己的容器运行时(containerd)和相关工具,成为容器技术的先驱。
  5. CRI-O:CRI-O 是一个专为 Kubernetes 设计的容器运行时,它遵循 OCI 容器规范,与 Kubernetes 集成紧密。
  6. Containerd:Containerd 是一个独立的容器运行时,用于管理容器的生命周期,最初是 Docker 的一部分,后来成为独立项目。
  7. runV:runV 是一个实验性项目,使用虚拟化技术(如 QEMU)来运行容器,提供更高的隔离性。

编排工具:
Docker-swarm:原生公司
K8S:Google公司

image-20230907143102704

2.1容器化和虚拟化对比

image-20230907144241669

特性 Docker VM
启动速度 秒级 分钟级
硬盘使用 一般为MB 一般为GB
性能 接近原生 若于原生
系统支持量 单机支持上千个容器 一般几十个
隔离性 安全隔离 完全隔离

2.2.Namespace资源隔离

Namespace:是将内核的全局资源做封装,使得每个Namespace都有一份独立的资源,因此不同的进程在各自的Namespace内对同一种资源的使用不会互相干扰。
Cgroup(control group):是内核提供的一种资源隔离的机制,可以实现对进程所使用的cpu、内存物理资源、及网络带宽等进行限制。还可以通过分配的CPU时间片数量及磁盘IO宽带大小控制任务运行的优先级

2.2.1 Namespace解析

目前Linux内核总共支持以下6种Namespace:

  • IPC:隔离System V IPC和POSIX消息队列。
  • Network:隔离网络资源。
  • Mount:隔离文件系统挂载点。
  • PID:隔离进程ID。
  • UTS:隔离主机名和域名。
  • User:隔离用户ID和组ID。

    Linux对Namespace的操作,主要是通过 clone、setns 和 unshare 这3个系统调用来完成的,clone创建新进程时,接收一个叫flags的参数,这些flag包括 CLONE_NEWNS、CLONE_NEWIPC、CLONE_NEWUTS、CLONE_NEWNET(Mount namespace)、CLONE_NEWPID和CLONE_NEWUSER ,用于创建新的namespace,这样clone创建出来新进程之后就属于新的namespace了,后续新进程创建的进程默认属于同一namespace。
    如果想要给已存在进程设置新的namespace,可通过unshare函数(long unshare(unsigned long flags))完成设置,其入参flags表示新的namespace。当想要给已存在进程设置已存在的namespace,可通过setns函数(int setns(int fd, int nstype))来完成设置,每个进程在procfs目录下存储其相关的namespace信息,可找到已存在的namesapce,然后通过setns设置即可:

image-20230907145135432

上述每个虚拟文件对应该进程所处的namespace,如果其他进程想进入该namespace,open该虚拟文件获取到fd,然后传给setns函数的fd入参即可,注意虚拟文件type和nstype要对应上

2.2.2 六种namespace的简单介绍:

1、IPC

​ IPC也就是进程间通信,Linux下有多种进程间通信,比如socket、共享内存、Posix消息队列和SystemV IPC等,这里的IPC namespace针对的是SystemV IPC和Posix消息队列,其会用标识符表示不同的消息队列,进程间通过找到标识符对应的消息队列来完成通信,IPC namespace做的事情就是相同的标识符在不同namespace上对应不同的消息队列,这样不同namespace的进程无法完成进程间通信。

2、Network

​ Network Namespace隔离网络资源,每个Network Namespace都有自己的网络设备、IP地址、路由表、/proc/net目录、端口号等。每个Network Namespace会有一个loopback设备(除此之外不会有任何其他网络设备)。因此用户需要在这里面做自己的网络配置。IP工具已经支持Network Namespace,可以通过它来为新的Network Namespace配置网络功能。

3、Mount

​ Mount namesapce用户隔离文件系统挂载点,每个进程能看到的文件系统都记录在/proc/$$/mounts里。在创建了一个新的Mount Namespace后,进程系统对文件系统挂载/卸载的动作就不会影响到其他Namespace。

4、PID

​ PID Namespace用于隔离进程PID号,这样一来,不同的Namespace里的进程PID号就可以是一样的了。当创建一个PID Namespace时,第一个进程的PID号是1,也就是init进程。init进程有一些特殊之处,例如init进程需要负责回收所有孤儿进程的资源。另外,发送给init进程的任何信号都会被屏蔽,即使发送的是SIGKILL信号,也就是说,在容器内无法“杀死”init进程。

5、UTS

​ UTS Namespace用于对主机名和域名进行隔离,也就是uname系统调用使用的结构体structutsname里的nodename和domainname这两个字段,UTS这个名字也是由此而来的。为什么需要uts namespace呢,因为为主机名可以用来代替IP地址,比如局域网通过主机名访问机器。

6、User

​ User Namespace用来隔离用户资源,比如一个进程在Namespace里的用户和组ID与它在host里的ID可以不一样,这样可以做到,一个host的普通用户可以在该容器(user namespace)下拥有root权限,但是它的特权被限定在容器内。(容器内的这类root用户,实际上还是有很多特权操作不能执行,基本上如果这个特权操作会影响到其他容器或者host,就不会被允许)

2.3.cgroup:资源限制

2.3.1什么是cgroup?

cgroups是Linux下控制一个(或一组)进程的资源限制机制,全称是control groups,可以对cpu、内存等资源做精细化控制,比如目前很多的Docker在Linux下就是基于cgroups提供的资源限制机制来实现资源控制的;除此之外,开发者也可以指直接基于cgroups来进行进程资源控制,比如8核的机器上部署了一个web服务和一个计算服务,可以让web服务仅可使用其中6个核,把剩下的两个核留给计算服务。cgroups cpu限制除了可以限制使用多少/哪几个核心之外,还可以设置cpu占用比(注意占用比是各自都跑满情况下的使用比例,如果一个cgroup空闲而另一个繁忙,那么繁忙的cgroup是有可能占满整个cpu核心的)。

2.3.2.cgroups概念

从实现角度来看,cgroups实现了一个通用的进程分组框架,不同资源的具体管理工作由各cgroup子系统来实现,当需要多个限制策略比如同时针对cpu和内存进行限制,则同时关联多个cgroup子系统即可。

cgroups子系统

cgroups为每种资源定义了一个子系统,典型的子系统如下:

  • cpu 子系统,主要限制进程的 cpu 使用率。
  • cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
  • cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  • memory 子系统,可以限制进程的 memory 使用量。
  • blkio 子系统,可以限制进程的块设备 io。
  • devices 子系统,可以控制进程能够访问某些设备。
  • net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  • freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
  • ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。

每个子系统都是定义了一套限制策略,它们需要与内核的其他模块配合来完成资源限制功能,比如对 cpu 资源的限制是通过进程调度模块根据 cpu 子系统的配置来完成的;对内存资源的限制则是内存模块根据 memory 子系统的配置来完成的,而对网络数据包的控制则需要 Traffic Control 子系统来配合完成。

2.3.3.cgroups原理

关于cgroups原理,可以从进程角度来剖析相关数据结构之间关系,Linux 下管理进程的数据结构是 task_struct,其中与cgrups相关属性如下:

image-20230907150930485

每个进程对应一个css_set结构,css_set存储了与进程相关的cgropus信息。cg_list是一个嵌入的 list_head 结构,用于将连到同一个 css_set 的进程组织成一个链表。进程和css_set的关系是多对一关系,tasks表示关联的多个进程。

image-20230907150946511

subsys 是一个指针数组,存储一组指向 cgroup_subsys_state 的指针,通过这个指针进程可以获取到对应的cgroups信息,一个 cgroup_subsys_state 就是进程与一个特定子系统相关的信息,cgroup_subsys_state结构体如下:

image-20230907150956604

cgroup 指针指向了一个 cgroup 结构,也就是进程属于的 cgroup,进程受到子系统控制就是加入到特定的cgroup来实现的,就是对应这里的cgroup,由此看出进程和cgroup的关系是多对多关系。

image-20230907151007384

sibling、children 和 parent 三个嵌入的 list_head 负责将统一层级的 cgroup 连接成一棵 cgroup 树。subsys 是一个指针数组,存储一组指向 cgroup_subsys_state 的指针。这组指针指向了此 cgroup 跟各个子系统相关的信息,也就是说一个cgroup可以关联多个子系统,二者关系是多对多关系。

Linux下的cgroups的数据结构图示如下:

image-20230907151021130

2.3.4 cgroups 层级结构

在cgrups中一个task任务就是一个进程,一个进程可以加入到某个cgroup,也从一个进程组迁移到另一个cgroup。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制。多个cgroup形成一个层级结构(树形结构),cgroup树上的子节点cgroup是父节点cgroup的孩子,继承父cgroup的特定的属性。注意:cgroups层级只会关联某个子系统之后才能进行对应的资源控制,一个子系统附加到某个层级以后,这个层级上的所有cgroup都受到这个子系统的控制。cgroup典型应用架构图如下:

image-20230907151250251

2.3.5 cgroups文件系统

Linux 使用了多种数据结构在内核中实现了 cgroups 的配置,关联了进程和 cgroups 节点,那么 Linux 又是如何让用户态的进程使用到 cgroups 的功能呢? Linux内核有一个很强大的模块叫 VFS (Virtual File System)。 VFS 能够把具体文件系统的细节隐藏起来,给用户态进程提供一个统一的文件系统 API 接口。 cgroups 也是通过 VFS 把功能暴露给用户态的,cgroups 与 VFS 之间的衔接部分称之为 cgroups 文件系统。通过cgroups适配VFS,用户可以使用VFS接口来操作cgroup功能。

VFS 是一个内核抽象层(通用文件模型),能够隐藏具体文件系统的实现细节,从而给用户态进程提供一套统一的 API 接口。VFS 使用了一种通用文件系统的设计,具体的文件系统只要实现了 VFS 的设计接口,就能够注册到 VFS 中,从而使内核可以读写这种文件系统。 这很像面向对象设计中的抽象类与子类之间的关系,抽象类负责对外接口的设计,子类负责具体的实现。其实,VFS本身就是用 c 语言实现的一套面向对象的接口。

2.3.6 小结

cgroups是Linux下控制一个(或一组)进程的资源限制机制,cgroup通过关联单个/多个子系统定义了一套限制策略来限制CPU/内存等资源,子系统上限制策略相当于一套配置,需要内核中对应模块配合来完成资源限制功能,比如对 cpu 资源的限制是通过进程调度模块根据 cpu 子系统的配置来完成的等。

注意cgroups资源限制是针对cgroup为单位的,不管cgroup下是有一个进程还是多个进程。Docker下的容器内所有进程就是属于同一组cgroup管理之下的,比如限制CPU使用,限制内存大小等。

参考资料:
1、https://www.ibm.com/developerworks/cn/linux/1506_cgroup/index.html
2、https://tech.meituan.com/2015/03/31/cgroups.html

3.Docker能做什么,解决了什么问题

简化配置

​ 虚拟机的最大好处是能在你的硬件设施上运行各种配置不一样的平台(软件、系统),Docker在降低额外开销的情况下提供了同样的功能。它能让你将运行环境和配置放在代码中然后部署,同一个Docker的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。

提高开发和布署效率

在软件开发过程中,我们都想把两件事做好:
开发环境和和投产环境一至
快速布署

​ 为了达到第一个目标我们要把程序跑在不同系统的服务器上,用大量的时间去测试功能是否完整。Docker可以轻松的为任何应用创建一个轻量级的、可移植的、自给自足的镜像,同一个镜像可以在不同的环境中使用,从而统一环境。
​ Docker构建好镜像后,可以快速布署到任何流行的 Linux 机器上,不用再次配置和安装应用所需的依赖。最重要的是启动一个容器是秒级别的!

隔离应用

​ 一台服务器上可能会跑不同的应用,每个应用都有自己的依赖。容易因为依赖的不两只造成冲突,Docker能把每个应用和他的依赖打包成独立的单元,做到资源隔离。

4.Docker三个重要概念

镜像 - Docker images
容器 - Docker containers
仓库 - Docker repository

image-20230907151522599

**理论上来说,镜像是不可写的,只读,不可以修改***

4.1 Docker-文件存储overlayfs

概述:

Docker镜像是一层层构建的,其文件系统是一种联合文件系统,这种文件系统不是传统意义上的linux文件系统(如ext4,xfs,direct-lvm,btrfs,zfs等),并不直接参与磁盘空间的划分,而是基于底层的linux文件系统,联合文件系统中各目录所呈现。因此对于容器来讲,其根目录是挂载了多个目录的合集(merged,df即可查看)。
Docker不同文件系统对应不同的存储驱动。通过存储驱动来管理镜像层和可写容器层的内容。每个存储驱动程序对实现的处理方式不同,但是基本所有驱动程序都使用可堆叠的镜像层和写时复制(CoW)策略(除VFS外)。

Docker 使用 OverlayFS(Overlay File System)来管理容器文件系统,这个文件系统由三个主要层组成:lower层、upper层和merge层。下面我会详细解释它们的作用:

  1. Lower层(底层)
    • Lower层是底层文件系统,通常是一个只读的镜像文件系统。这个层包含了容器的基础文件系统,包括操作系统和应用程序的文件。多个容器可以共享相同的底层,因为它是只读的,从而节省磁盘空间。
  2. Upper层(顶层)
    • Upper层是容器的可写层,也称为写时复制层。这个层包含了容器运行时创建或修改的文件。当容器需要修改底层文件系统时,它会在上面创建一个新文件或目录,而不是直接修改底层。这确保了多个容器可以同时使用相同的底层文件系统,而不会相互干扰。
  3. Merge层(合并层)
    • Merge层是 OverlayFS 的核心,它用于将底层和上层文件系统合并成一个容器的可见文件系统。当容器运行时,操作系统会根据需要从底层和上层文件系统中读取文件,并将它们合并成一个虚拟的文件系统,供容器进程使用。这个合并过程是透明的,容器看到的是一个完整的文件系统。
  4. Worker(工作器)
    • Worker 是 OverlayFS 的工作线程,负责管理文件系统的读取和写入操作。它们负责处理文件的复制、删除、重命名等操作,以确保容器文件系统的一致性和隔离性。Worker 在后台运行,与容器的生命周期并不直接相关,它们由 Docker 引擎管理。

4.2 Docker images

Docker镜像可以看做是一个特殊的文件系统,除了提供容器运行时所需要的程序、库、资源、配置文件以外,

还包含了一些为运行时,准备的配置参数(匿名卷,环境变量,用户等),镜像是不可更改的

4.3 Docker containers

容器的定义和镜像,几乎是一模一样,唯一区别在于容器的最上面那一层是可读可写的。

4.4 Docker repository

仓库是Docker用来存放镜像的地方,类似于我们之前常用的代码仓库。
通常一个仓库会包含,同一个软件,不同版本的镜像
我们可以通过<仓库名>:<标签>格式来指定具体使用哪个版本的镜像,如果不给标签,那么默认以Latest
作为默认标签

公有仓库
docker官方仓库
私有仓库

5.Docker的组成

docker主要有以下几部分组成:

  • Docker Client 客户端
  • Docker daemon 守护进程
  • Docker Image 镜像
  • Docker Container 容器
  • Docker Registry 仓库

客户端和守护进程:

  • Docker是C/S(客户端client-服务器server)架构模式。
  • docker通过客户端连接守护进程,通过命令向守护进程发出请求,守护进程通过一系列的操作返回结果。
  • docker客户端可以连接本地或者远程的守护进程。
  • docker客户端和服务器通过socket或RESTful API进行通信。

如图:

image-20230907153421748

image-20230907153503470

6.Docker安装和部署

环境准备

主机名 外网IP 内网IP 配置
Docker01 10.0.0.101 172.16.1.101 1C/1G
Docker02 10.0.0.102 172.16.1.102 1C/1G

安装Docker

TP

# 1.下载docker 官方源
wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
# 2.修改成清华源
sed -i 's+https://download.docker.com+https://mirrors.tuna.tsinghua.edu.cn/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# 3.安装docker
yum install -y docker-ce docker-ce-cli containerd.io
# 4.查看版本
docker version
# 5.启动docker服务端
systemctl start docker
# 6.查看docker详细信息
docker info

[root@docker ~]# docker info  # 运行docker info命令,显示Docker系统信息
Client: Docker Engine - Community   # Docker客户端信息
 Version: 24.0.6     # 客户端版本24.0.6
 Context: default    # 默认上下文
 Debug Mode: false   # 调试模式关闭
 Plugins:            # 插件列表
  buildx: Docker Buildx (Docker Inc.)   # Docker Buildx插件信息
    Version:  v0.11.2   # 插件版本v0.11.2
    Path:     /usr/libexec/docker/cli-plugins/docker-buildx   # 插件路径
  compose: Docker Compose (Docker Inc.)  # Docker Compose插件信息
    Version:  v2.21.0   # 插件版本v2.21.0
    Path:     /usr/libexec/docker/cli-plugins/docker-compose   # 插件路径

Server:              # Docker服务器信息
 Containers: 1       # 容器数量:1个
  Running: 0         # 运行中的容器:0个
  Paused: 0          # 暂停的容器:0个
  Stopped: 1         # 停止的容器:1个
 Images: 2           # 镜像数量:2个
 Server Version: 24.0.6   # 服务器版本:24.0.6
 Storage Driver: overlay2   # 存储驱动程序:overlay2
  Backing Filesystem: xfs   # 支持的文件系统:xfs
  Supports d_type: true    # 支持d_type特性:是
  Using metacopy: false    # 使用metacopy:否
  Native Overlay Diff: true   # 使用本地Overlay Diff:是
  userxattr: false     # userxattr:否
 Logging Driver: json-file   # 日志驱动程序:json-file
 Cgroup Driver: cgroupfs     # Cgroup驱动程序:cgroupfs
 Cgroup Version: 1           # Cgroup版本:1
 Plugins:                     # 插件列表
  Volume: local               # 卷插件:local
  Network: bridge host ipvlan macvlan null overlay   # 网络插件列表
  Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog   # 日志插件列表
 Swarm: inactive              # Swarm状态:非活动
 Runtimes: io.containerd.runc.v2 runc   # 容器运行时列表
 Default Runtime: runc        # 默认容器运行时:runc
 Init Binary: docker-init     # 初始化二进制文件:docker-init
 containerd version: 8165feabfdfe38c65b599c

Docker配置文件

位置:/etc/docker
文件名:damon.json

6.1 Docker镜像加速

可网上自行寻找配置,也可去阿里源免费申请一个

image-20230907160038349

image-20230907160145460

image-20230907160241952

7.容器运行测试

docker创建容器的流程:
1)执行docker run命令创建容器
2)会检查本地是否有镜像,没有则去dockerhub拉镜像
3)调用docker api连接服务端
4)服务端调用底层的runc启动容器
5)使用namespace进行资源隔离
6)使用cgroup资源限制

#运行alpine    //因为没有这个镜像,所有会优先去hub.docker拉取镜像
docker run alpine /bin/echo "Hello World"

image-20230907160603551

#查看镜像
docker images

image-20230907160732241

7.1Docker镜像操作

hub.docker:记得使用梯子

# 拉镜像
镜像名称:镜像仓库名:标签
语法:docker pull alpine:latest

# 查看镜像 //两种方式都可查看
[root@Docker01 ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest c059bfaa849c 21 months ago 5.59MB
centos centos7 eeb6ee3f44bd 24 months ago 204MB
[root@Docker01 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest c059bfaa849c 21 months ago 5.59MB
centos centos7 eeb6ee3f44bd 24 months ago 204MB

# 删除镜像
#指定镜像仓库名:标签
[root@Docker01 ~]# docker rmi alpine:latest
#指定ID
[root@Docker01 ~]# docker rmi eeb6ee3f44bd

# 搜索镜像
[root@Docker01 ~]# docker search nginx

# 导出镜像(指定仓库名:标签)
-o :指定路径(同 > )
[root@Docker01 ~]# docker save alpine:latest -o /tmp/alpine_latest.tgz
[root@Docker01 ~]# docker save alpine:latest > /tmp/alpine_latest.tgz
# 导出镜像(指定imageID)
-i=<
[root@Docker01 ~]# docker load < /tmp/alpine.tgz
[root@Docker01 ~]# docker load -i /tmp/alpine.tgz

# 打标签
[root@Docker01 ~]# docker tag c059bfaa849c alpine:v1

7.2 容器操作

# 运行一个容器
docker run 镜像仓库:标签
[root@docker01 ~]# docker run alpine /bin/echo "Hello World"

# 查看正在运行的容器ID
[root@docker01 ~]# docker ps -q
f4591f3f6cb3

# 查看正在运行的docker容器
[root@docker01 ~]# docker ps
CONTAINER ID   IMAGE      COMMAND       CREATED          STATUS          PORTS     NAMES
ff3a7481aac0   centos:7   "/bin/bash"   20 seconds ago   Up 20 seconds             elegant_blackwell

# 查看所有docker容器
[root@docker01 ~]# docker ps -a
CONTAINER ID   IMAGE      COMMAND                  CREATED          STATUS                        PORTS     NAMES
ff3a7481aac0   centos:7   "/bin/bash"              3 minutes ago    Exited (137) 56 seconds ago             elegant_blackwell
f4591f3f6cb3   centos     "/bin/bash"              9 minutes ago    Exited (0) 7 minutes ago                laughing_chaplygin
dfd97ec530fa   alpine     "/bin/echo 'Hello Wo…"   13 minutes ago   Exited (0) 13 minutes ago               vigilant_hoover

# 查看正在运行的docker容器ID
[root@docker01 ~]# docker ps -q
f4591f3f6cb3

# 查看所有容器ID
[root@docker01 ~]# docker ps -qa
f4591f3f6cb3
dfd97ec530fa

# 停止正在运行的docker容器
docker stop 容器ID
[root@docker01 ~]# docker stop ff3a7481aac0

# 删除docker容器
docker rm 要删除的docker容器ID
[root@docker01 ~]# docker rm ff3a7481aac0
ff3a7481aac0

# 删除所有docker容器
[root@docker01 ~]# docker rm $(docker ps -qa)
f4591f3f6cb3
dfd97ec530fa

拓展

怎么使其容器一直在运行中?且使用docker ps能查看运行中的容器

#查看当前所有容器
[root@docker ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
nginx        latest    605c77e624dd   20 months ago   141MB
alpine       latest    c059bfaa849c   21 months ago   5.59MB
centos       centos7   eeb6ee3f44bd   24 months ago   204MB
#运行centos7
[root@docker ~]# docker run -it -d centos:centos7
e2b26aeb351a522b724f59a641689dcc04a4e145b995a9Docker7300b2c356586e50
#查看容器运行
[root@docker ~]# docker ps
CONTAINER ID   IMAGE            COMMAND       CREATED         STATUS         PORTS     NAMES
e2b26aeb351a   centos:centos7   "/bin/bash"   8 seconds ago   Up 7 seconds             reverent_bassi
#停止运行 stop+容器运行ID
[root@docker ~]# docker stop e2b26aeb351a