Docker精讲:基本安装,简单命令及核心概念

docker服务部署

docker是一个容器管理工具,其内部容器才是具体服务,所以我们在安装docker时不需要有太多定制内容,只需要通过yum安装即可

1. 更新系统包
复制代码
#更新现有依赖包,防止现有依赖包版本过低影响docker安装
yum update
2. 安装依赖包
复制代码
yum install -y yum-utils device-mapper-persistent-data lvm2
3. 添加Docker的yum源
复制代码
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
#如果上面的官方docker源速度过慢,可以下载下面的阿里云docker源
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
4. 安装Docker
复制代码
yum install docker-ce docker-ce-cli containerd.io
5. 配置docker镜像源
复制代码
vim /etc/docker/daemon.json
#修改为如下内容
{
  "registry-mirrors": [
    "https://docker.registry.cyou",
    "https://docker-cf.registry.cyou",
    "https://dockercf.jsdelivr.fyi",
    "https://docker.jsdelivr.fyi",
    "https://dockertest.jsdelivr.fyi",
    "https://mirror.aliyuncs.com",
    "https://dockerproxy.com",
    "https://mirror.baidubce.com",
    "https://docker.m.daocloud.io",
    "https://docker.nju.edu.cn",
    "https://docker.mirrors.sjtug.sjtu.edu.cn",
    "https://docker.mirrors.ustc.edu.cn",
    "https://mirror.iscas.ac.cn",
    "https://docker.rainbond.cc"
  ]
}

如果上面的镜像源不可用,可以进入如下文章查看最新的更新

docker镜像源来源

yum配置的镜像源是让yum具有下载docker的能力,不会影响docker拉取镜像的速度,加速docker下载镜像的过程需要配置docker镜像源。

6. 启动Docker并设置开机启动
复制代码
systemctl enable docker
7. 验证Docker是否安装成功
复制代码
docker --version

容器化介绍

容器化通过将被部署的应用程序,放入包含其依赖的所有环境的容器中。通过操作系统提供的某些能力,让容器环境同外部实现环境隔离,可以在一台主机中,独立于其他服务,部署在一个独立的虚拟环境中。

什么是容器?

我们可以把容器想象成一个虚拟机,其外在表现和虚拟机极其相似,比如说,容器通常由其自己的文件系统,有其自己的虚拟网卡,虚拟网卡和真实网卡之间通过网桥连接(类似于虚拟机NAT模式)。其部署环境也独立于真实环境之外。

容器和虚拟机的区别

虽然在外在表现和虚拟机类似,不过其底层实现和虚拟机却是天差地别。

虚拟机通过分配真实的硬件资源,包括CPU,内存,磁盘等,打造一个真实的硬件环境。这样虽然能够实现环境隔离,不过对于资源分配不好把控,是对资源的浪费。

比如说,如果我想部署一个Kafka和一个redis在两个主机中,如果通过虚拟机来进行环境隔离,Kafka内存波动较大,我就需要给Kafka极大的内存资源,不过当Kafka内存占用较小时,由于分配的是固定的内存资源。剩余的资源也无法给redis使用。

并且虚拟机是模仿真实的主机环境,真实主机具有的环境虚拟机一应俱全,这就导致会引入大量的当前服务无需的依赖,这也是对资源的浪费。

虚拟机镜像虽然也可以在不同平台之间保持一致运行环境,但在虚拟机镜像的迁移和管理方面会较为复杂。

那么容器化如何解决了这些问题呢?

首先,容器的隔离实现是通过直接调用linux内核中的核心方法,不使用linux各种发行版的方法(如CentOS,Ubuntu等等)。所以只要是基于Linux的操作系统,容器都可以提供相同的环境,并且迁移方便。

并且容器不分配实际硬件资源,其运行环境和普通的程序运行时一样,都是在真实主机环境中运行。不同的的是其会调用linux操作系统的功能来实现容器之间的隔离。

容器的隔离实现

一个容器的实现主要通过LInux操作系统,提供的命名空间和控制组来实现和外界环境的隔离,生成一个独立的虚拟环境。

命名空间(NameSpace)

命名空间是 Linux 提供的一种机制,它将系统的全局资源划分为独立的组,使每个进程只能看到和访问它自己的资源。Docker 使用命名空间来实现容器之间的隔离,使得每个容器都认为自己是独立运行的。常用的命名空间类型包括:

  • PID 命名空间(PID Namespace):隔离进程 ID,允许进程在容器中拥有自己的进程树和 PID 1。
  • 网络命名空间(Network Namespace):隔离网络设备、IP 地址、路由表等,使容器有自己的网络栈。
  • 挂载命名空间(Mount Namespace):隔离文件系统挂载点,使进程有自己独立的文件系统视图。
  • UTS 命名空间(UTS Namespace):隔离主机名和域名,使容器有自己的主机名。
  • IPC 命名空间(IPC Namespace):隔离信号量、消息队列等进程间通信机制。
  • 用户命名空间(User Namespace):允许进程有自己的用户 ID 和组 ID 映射,提供更细粒度的权限隔离。
  • 时间命名空间(Time Namespace):隔离系统时钟,允许不同命名空间内的进程看到不同的时间。

说了这么多,命名空间到底是什么啊?

实际上命名空间就是linux操作系统内核中的一个结构体,我们上面提到的每个命名空间都有一个自己的结构体,其中保存了命名空间的信息。而我们将一个主机内容添加进命名空间,本质上就是将记录主机内容信息的一个结构体的一个属性,保存对应命名空间结构体的地址。我们以最常见的进程举例:

在操作系统中,保存进程信息的结构叫PCB(进程控制块),当我们在PID命名空间中创建进程时,操作系统会创建两个PID,分别是真实PID和容器PID,保存进程信息的PCB也会将一个属性保存当前命名空间结构体的地址,这证明当前进程数据当前结构体,当多个进程都保存相同的命名空间结构体时,他们就是一个命名空间的进程,可以互相访问。

PCB中还保存了另外一个结构体,用于保存当前进程的所有容器层级和容器PID以及其他信息等等。在命名空间中还可以创建另一个命名空间,一个进程可以保存在多个互相嵌套的命名空间中。所以进程会保存多个容器PID以及层级信息。

通过这两个结构体,进程可以完成通过命名空间实现环境隔离,以及真实PID和容器PID的映射。

再比如网络命名空间本质上也是一个结构体,并且真实网卡和容器内虚拟网卡由虚拟交换机和veth pair来实现真实网课和虚拟网卡的连接。其他的不一一细讲了,大概理解命名空间本质概念就好。

控制组(cgroups)

我们仍然使用Kafka和redis的部署案例。通过容器化部署,可以防止在Kafka低内存消耗时,分配给其的内存资源被浪费。不过在Kafka接收消息量较大时又会出现另一个问题,如果放任其无限制扩展内容,势必会导致redis没有生存空间,所以我们在让其可以弹性利用内存的同时,我们也要对其最大值进程限制。

控制组(cgroups) 是 Linux 内核的一种功能,允许系统管理员或用户对一组进程的资源使用情况进行限制、隔离和监控。它在容器技术、虚拟化、云计算以及多用户系统中非常有用,提供了对 CPU、内存、磁盘 I/O、网络带宽等系统资源的精细控制。

cgroups 的主要功能和用途:

1. 资源限制(Resource Limiting)

cgroups 允许你对进程组的资源使用进行限制,以防止某些进程消耗过多的系统资源,影响其他进程或系统的性能。例如:

  • CPU 限制:可以为一组进程分配 CPU 资源的最大使用比例,从而避免某个进程独占 CPU。
  • 内存限制:可以限制一组进程能够使用的内存量,防止某些进程消耗过多的内存导致系统崩溃。
  • I/O 限制:可以限制进程访问磁盘或其他存储设备的速率,确保 I/O 操作的公平性,防止某些进程造成 I/O 瓶颈。

2. 资源隔离(Resource Isolation)

通过 cgroups,可以对不同的进程组进行资源隔离,确保一个进程组的资源使用不会影响到其他进程组。这对容器化环境特别有用:

  • 每个容器可以被分配固定数量的资源,如 CPU 核心、内存、I/O 速率等,保证不同容器之间的资源隔离和公平使用。
  • 资源隔离还可以用于虚拟机或多用户系统,确保不同的用户或服务不会相互干扰。

3. 资源计费与统计(Resource Accounting)

cgroups 可以实时跟踪和统计某个进程组使用的资源量,包括 CPU 时间、内存使用、网络带宽消耗、I/O 操作等。这对系统监控、调优以及计费系统非常有用。例如:

  • 可以统计某个容器或进程组使用了多少 CPU 时间和内存。
  • 在云服务中,可以用 cgroups 的资源统计功能来进行按量计费,记录每个虚拟机或容器的资源消耗。

4. 优先级控制(Priority Control)

cgroups 可以为不同的进程组设置资源使用的优先级,确保高优先级的任务能够优先获得资源。例如:

  • 在 CPU 竞争时,可以优先让某些关键任务使用更多的 CPU 时间,确保关键任务的响应时间。
  • 对于内存和 I/O 资源,也可以设置不同的权重,以保证系统中的重要进程获得优先资源。

优先级控制通过控制进程权重比例,CPU会根据权重比例分配时间片。不过我认为这个功能实际上是多余的,高并发应用会通过创建线程来抢占CPU资源,在我看来无需CPU通过分配时间片来进行控制,这样控制反而会造成不好的结果,比如说某些进程偶尔需要进行高并发,创建多个线程以提高性能,但由于控制组对其权重进行了限制,导致其虽然占用了更多的内存,但性能却没有明显提升。(个人思考,有别的想法也可以说出来一起讨论)

5. 进程管理和分组(Process Grouping and Management)

cgroups 提供了将多个进程分组管理的机制。每个进程组可以有不同的资源限制和策略,可以随时动态调整:

  • 在容器化环境中,cgroups 管理着每个容器中的进程,确保容器内部的所有进程都受到相同的资源限制。
  • cgroups 还允许系统管理员动态向某个进程组添加或移除进程,便于灵活管理系统资源。

docker容器相关命令

  1. 启动容器

    docker run -d -p <主机端口>:<容器端口> --name <容器名> <镜像>
    #-d为后台启动,对于一些有自己的启动命令的服务。如nginx,我们可以直接-d,而对于一些没有启动命令的服务,我们还要给他一个持续运行的命令防止其在后台关闭进程
    #-p为端口映射,如果外部需要访问服务,那么我们则需要将容器内端口映射到真实环境的端口
    #--name 给运行的容器起名字 最后的镜像是运行容器的镜像
    #我们也可以直接 docker run <服务名:版本> 这样会自动去远程仓库拉取镜像

  2. 停止容器

    docker stop <容器名/容器ID>

  3. 启动已停止的容器

    docker start <容器名/容器ID>

  4. 查看运行中的容器

    docker ps

  5. 查看所有容器(包括停止的)

    docker ps -a

  6. 删除容器

    docker rm <容器名/容器ID>

  7. 进入正在运行的容器

    docker exec -it <容器名/容器ID> /bin/bash

  8. 查看容器日志

    docker logs <容器名/容器ID>

  9. 重启容器

    docker restart <容器名/容器ID>

联合文件系统

多个容器内部的服务所依赖的环境中,必定有重合的部分,不如java开发的服务,底层一定是要依赖于jdk,对于js开发的后端服务,必须依赖node环境,部分服务依赖于数据库如mysql来存储服务数据,docker容器环境直接调用操作系统底层系统调用,对于部分依赖于某个操作系统发行版的服务,其还需要centos,等等等等。

大量服务之间依赖相同环境,如果我们对其进行完全隔离,势必要在一个主机上安装多个相同依赖环境的服务,这是对于主机内存的极大浪费。然而如果我们想要对其进行复用时,多个服务对于同一个依赖的差异性又是一个问题,比如说我可能依赖mysql,但是我的mysql的配置可能和你不同。那么对于这种需求,操作系统采用了联合文件系统的方式对多个相同依赖环境进行复用,并完善差异化。

联合文件系统,将多个文件抽象成层的概念,其中我们所依赖的服务被一层一层的罗列起来,而容器内的服务则是要一层一层的访问这些服务,这些层全部都是只读层,无法进行更改。对于这些只读层的变更内容,则是放在第一层作为可写层,其中可写层的内容会对这些只读层的内容进行覆盖,最终的结构如下图:

这是对联合文件系统的抽象理解,其实本质上来说,就是多个容器对同一个环境进行使用,然后自己对差异的那日容进行维护,并对原环境覆盖,其中复用环境是相对简单的事情,最具意义的就是可写层的实现,可以对环境内容进行修改并覆盖,这是实现联合文件系统的关键。

镜像(image)

想要了解容器化,那就一定少不了镜像的概念。在了解完联合文件系统后,镜像的概念也变得非常简单了。对于容器内部所依赖的环境信息,以及对于环境信息修改的内容,以及容器内服务本身等关于服务容器化运行相关的信息记录,就是镜像,我们可以把镜像理解为容器的模板,对于一个服务的容器化运行,由docker按照镜像的模板来进行部署。

镜像相关命令

  1. 列出本地镜像

    docker images

  2. 拉取镜像

    docker pull <镜像名>:<标签>

  3. 构建镜像

    docker build -t <镜像名>:<标签> .

  4. 删除镜像

    docker rmi <镜像ID/镜像名>

  5. 查看镜像的历史记录

    docker history <镜像ID/镜像名>

数据卷挂载

对于容器内产生的需要持久化存储的数据,一旦删除容器后,数据也将不复存在,虽然容器并没有显示的将磁盘内数据进行删除,不过这片磁盘数据只被容器内部的文件系统所引用,一旦这个容器消失,文件系统也将不再,那么这个数据即使存储在磁盘中,相较于操作系统而言,也是没有这个数据,无法访问。

为了能够持久化存储数据,docker采用了一种数据卷的方式来实现。数据卷将容器需要持久化存储的数据不再保存在其内部文件中,而是保存在外部的主机文件中,而原容器内的文件则是保存了外部主机文件的一个引用,实现数据卷挂载,这样不仅实现了数据的持久化存储,对于一些配置的修改也可以直接修改主机文件,而不是进入容器内部,更加方便。

数据卷相关命令

  1. 列出所有数据卷
bash 复制代码
docker volume ls
  1. 创建数据卷
bash 复制代码
docker volume create <卷名>
  1. 查看数据卷的详细信息
bash 复制代码
docker volume inspect <卷名>
  1. 挂载数据卷到容器
bash 复制代码
docker run -d -v <卷名>:<容器内路径> <镜像>
  1. 删除数据卷
bash 复制代码
docker volume rm <卷名>

删除指定的数据卷。需要确保数据卷没有被任何正在运行的容器使用。

  1. 清理未使用的数据卷
bash 复制代码
docker volume prune

注意 :这个操作是不可逆的,请谨慎使用。

  1. 挂载本地目录(绑定挂载)

    docker run -d -v /宿主机路径:/容器内路径 <镜像>

  2. 列出特定数据卷

    docker volume ls -f name=<关键词>

端口映射

同虚拟机一样,虚拟机在桥接模式下,其端口只能被当前主机访问。之前我们说过,docker和虚拟机这种桥接模式比较类似,所以在默认情况下,只能被当前主机访问。

不过既然是部署服务,自然不能局限于当前主机,所以我们可以通过一些命令,来讲端口映射到主机端口中,实现外部客户端访问此服务的目的。

端口映射相关命令

  1. 基本端口映射

    docker run -d -p <宿主机端口>:<容器端口> <镜像>

  • -d:后台运行容器(可选)。
  • -p <宿主机端口>:<容器端口>:将宿主机端口映射到容器的端口。

例子:

复制代码
docker run -d -p 8080:80 nginx

这条命令会将宿主机的 8080 端口映射到容器中的 80 端口(Nginx 的默认端口),这样你可以通过 http://localhost:8080 访问容器中的 Nginx 服务。

  1. 映射多个端口

    docker run -d -p <宿主机端口1>:<容器端口1> -p <宿主机端口2>:<容器端口2> <镜像>

可以使用 -p 多次来映射多个端口。

例子:

复制代码
docker run -d -p 8080:80 -p 8443:443 nginx

这会将宿主机的 8080 端口映射到容器的 80 端口,将宿主机的 8443 端口映射到容器的 443 端口(Nginx 的 HTTPS 端口)。

  1. 随机映射宿主机端口

    docker run -d -P <镜像>

使用大写的 -P(或 --publish-all),Docker 会将容器内暴露的所有端口随机映射到宿主机的端口上。

例子:

复制代码
docker run -d -P nginx

这会随机将容器中暴露的 80443 端口映射到宿主机的可用端口。

你可以使用 docker ps 来查看映射后的宿主机端口:

复制代码
docker ps
  1. 指定绑定的宿主机 IP 地址

    docker run -d -p <宿主机IP>:<宿主机端口>:<容器端口> <镜像>

默认情况下,Docker 将绑定到宿主机上的所有 IP 地址(0.0.0.0),你也可以指定要绑定的 IP 地址。

例子:

复制代码
docker run -d -p 127.0.0.1:8080:80 nginx

这会将宿主机的 127.0.0.1(即 localhost)的 8080 端口映射到容器的 80 端口。只有宿主机本地才能访问这个端口,而外部无法访问。

  1. 查看端口映射

    docker port <容器ID/容器名>

显示容器内端口与宿主机端口的映射关系。

例子:

复制代码
docker port my_nginx

这会列出名为 my_nginx 容器的所有端口映射信息。

  1. 修改正在运行的容器的端口映射

Docker 不支持在容器运行时 直接修改端口映射。如果需要更改端口映射,需要停止容器重新运行

复制代码
bashCopy codedocker stop <容器ID/容器名>
docker rm <容器ID/容器名>
docker run -d -p <新端口映射> <镜像>

其他命令

  1. 查看 Docker 信息

    docker info

  2. 查看 Docker 版本

    docker version

  3. 查看系统中的事件日志

    docker events

  4. 导出容器为镜像

    docker commit <容器ID> <镜像名>:<标签>

  5. 清理未使用的镜像、卷、网络和容器

    docker system prune

  1. 修改正在运行的容器的端口映射

Docker 不支持在容器运行时 直接修改端口映射。如果需要更改端口映射,需要停止容器重新运行

复制代码
bashCopy codedocker stop <容器ID/容器名>
docker rm <容器ID/容器名>
docker run -d -p <新端口映射> <镜像>

其他命令

  1. 查看 Docker 信息

    docker info

  2. 查看 Docker 版本

    docker version

  3. 查看系统中的事件日志

    docker events

  4. 导出容器为镜像

    docker commit <容器ID> <镜像名>:<标签>

  5. 清理未使用的镜像、卷、网络和容器

    docker system prune

相关推荐
程序猿小三38 分钟前
Linux下基于关键词文件搜索
linux·运维·服务器
虚拟指尖1 小时前
Ubuntu编译安装COLMAP【实测编译成功】
linux·运维·ubuntu
椎4952 小时前
苍穹外卖前端nginx错误之一解决
运维·前端·nginx
刘某的Cloud2 小时前
parted磁盘管理
linux·运维·系统·parted
极验2 小时前
iPhone17实体卡槽消失?eSIM 普及下的安全挑战与应对
大数据·运维·安全
爱倒腾的老唐3 小时前
24、Linux 路由管理
linux·运维·网络
yannan201903133 小时前
Docker容器
运维·docker·容器
_清浅3 小时前
计算机网络【第六章-应用层】
运维·服务器·计算机网络
正在努力的小河3 小时前
Linux 自带的 LED 灯驱动实验
linux·运维·服务器
李子圆圆3 小时前
电力专用多功能微气象监测装置在电网安全运维中的核心价值是什么?
运维·安全