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

相关推荐
_.Switch26 分钟前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
2401_8504108327 分钟前
文件系统和日志管理
linux·运维·服务器
JokerSZ.31 分钟前
【基于LSM的ELF文件安全模块设计】参考
运维·网络·安全
芯盾时代1 小时前
数字身份发展趋势前瞻:身份韧性与安全
运维·安全·网络安全·密码学·信息与通信
心灵彼岸-诗和远方2 小时前
DevOps业务价值流:架构设计最佳实践
运维·产品经理·devops
一只哒布刘2 小时前
NFS服务器
运维·服务器
南猿北者3 小时前
docker容器
docker·容器
苹果醋33 小时前
Java8->Java19的初步探索
java·运维·spring boot·mysql·nginx
二十雨辰3 小时前
[linux]docker基础
linux·运维·docker
time never ceases4 小时前
使用docker方式进行Oracle数据库的物理迁移(helowin/oracle_11g)
数据库·docker·oracle