Docker 完全入门指南到原理浅析(全栈必备)

docker 介绍和优势

Docker 是一个轻量级容器化工具,可以将应用程序及其运行环境打包成镜像,确保在不同环境中的一致性。这些镜像由多层组成,每层记录一个变更,便于快速构建和分发。

后端会部署很多服务,比如 mysql 和 redis 等中间件的服务,部署他们需要安装一系列的依赖和进行环境变量设置。

如果我们部署多台机器,同样的操作需要重复多次,服务才能正常启动,环境配置很麻烦。

从这些镜像中会创建容器,每个容器都在隔离的环境中运行,有自己的文件系统和网络空间,不会互相干扰。虽然容器共享主机操作系统内核,但它们的运行进程是隔离的,保证了安全性和稳定性。

这种设计非常适合微服务架构,其中应用程序被拆分成多个小型、独立的服务。每个服务都可以打包成一个 Docker 容器,独立部署和扩展。

例如,用户认证、数据库处理和前端展示等服务可以各自运行在自己的容器中。这种模块化方法简化了管理,提高了系统的可伸缩性和可用性。

相比之下虚拟机也是一种带环境安装的解决方案,它可以在一种操作系统内运行另一种操作系统。然而,虚拟机存在资源占用多、冗余步骤多和启动慢等缺点。

安装 docker

首先需要安装 Docker,直接从官网下载 docker desktop 就行。

安装完成命令行输入 docker -h 看下 docker 命令是否可用,如果不可用,在 Settings > Advanced 设置下环境变量即可。

docker 的界面 images 是本地的所有镜像,containers 是镜像跑起来的容器,volumes 是数据卷:

docker 核心概念

  1. 镜像(Image):Docker 镜像是一个只读的模板,例如一个操作系统的镜像,或者带有应用程序及其依赖的镜像。镜像用来创建 Docker 容器。Docker 用户可以创建自己的镜像或者使用别人发布的镜像。
  2. 容器(Container):容器是镜像的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。你可以把容器看作是一个简易版的 Linux 环境(包括根用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
  3. 仓库(Repository):Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。Docker Hub 和 Docker Cloud 是最著名的公共仓库,用户也可以搭建自己的私有仓库。
  4. 数据卷(Volume):卷用于数据持久化和数据共享。当 Docker 容器被删除时,保存在容器内的数据也会被删除。而卷则存在于一个或多个容器之外,即使容器被删除,卷里的数据也仍然存在。
  5. **Dockerfile:**Dockerfile 是一个文本文件,包含了一系列的指令,每一条指令构建一层,用于自动化构建 Docker 镜像。Dockerfile 从基础镜像开始,执行一系列命令,最终构成你想要的镜像。
  6. 网络(Network):Docker 网络允许容器间相互通信,以及容器与外部通信。Docker 提供了多种网络模式,包括桥接模式、主机模式、覆盖模式等,以支持不同的使用场景。

拉取运行镜像

搜索镜像,点击 pull(搜索这步需要科学上网,不然搜不到):

pull 下来之后,就可以在本地 images 看到了:

点击 run 填写一些参数:

我们来看下这些选项

  • 名字:如果不填,docker desktop 会给你生成随机的容器名字。
  • 端口:容器内跑的 nginx 服务是在 80 端口,我们需要将容器的 80 端口映射到宿主机的某个端口。
  • 数据卷 volumes:通过将宿主机的目录挂载到容器内部,可以确保容器内的数据持久化存储。即使容器被删除,只要再次创建容器时将同一个宿主机目录挂载到新容器,之前保存的数据仍然可以被访问。这样做的目的是为了防止数据随容器的消失而丢失。
  • 环境变量:设置后,可以被容器内运行的应用程序读取。

这里我们挂载本地的 /tmp/test(window:D://tmp/test) 到容器内的 /usr/share/nginx/html 目录。

点击 run:

这样容器内的 nginx 服务跑起来了。

挂载卷

我们进入/tmp/test 目录下:

typescript 复制代码
cd /tmp/aaa

添加一个 index.html:

typescript 复制代码
echo "hello" >./index.html

浏览器访问 http://localhost 就可以访问到:

这就说明数据卷挂载成功了。

点击 files 标签就可以看到容器内的文件。可以看到 /usr/share/nginx/html 被标识为 mounted,就是挂载目录的意思,这意味着我们本地相应牡蛎添加 index.html, 这里面也会实时添加:

如果你挂载某些目录报错,需在 Settings > Resources > File Sharing 配置对应挂载目录:

至于挂载到的目录,在镜像搜索结果页有写:

通过命令行 docker run 来跑镜像, -v 是指定挂载的数据卷,后面的 :ro 代表 readonly,也就是容器内这个目录只读,:rw 表示容器内可以读写这个目录。

这就是数据卷的作用。

我们还可以进入容器内执行各种命令:

docker 命令

拉取镜像

服务器上没有 Docker Desktop 这种可视化操作,就需要敲命令。

比如我们点击 pull 按钮,就相当于执行了 docker pull:

typescript 复制代码
docker pull nginx:latest

latest 是标签,也就是这个:

运行镜像

然后我们点击 run 按钮,填完表单,就相当于执行了 docker run:

bash 复制代码
docker run --name nginx-test2 -p 81:80 -v /tmp/test:/usr/share/nginx/html -e KEY1=VALUE1 -d nginx:latest
  • --name 取名:给新启动的容器命名为 nginx-test2。
  • -p 端口映射:容器内的 80 端口映射到宿主机的 81 端口上
  • -v 指定数据卷挂载目录:将宿主机上的 /tmp/test 目录挂载到容器内的 /usr/share/nginx/html 目录。
  • -e 指定环境变量:设置了一个环境变量 KEY1,其值为 VALUE1。容器内部的应用程序可以读取这个环境变量。
  • -d 后台运行
  • nginx:latest 指定使用的 Docker 镜像的名称和标签

docker run 会返回一个容器的 hash:

就是这里的 id:

获取容器列表

上面容器列表界面可以用 docker ps 来获取:

默认显示运行中的容器。

如果想显示全部,加个 -a

获取镜像列表

image 镜像列表可以通过 docker images 命令获取:

进入退出容器 terminal

我们在容器的 terminal 里执行命令:

对应的是 docker exec 命令:

bash 复制代码
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
  • [OPTIONS]:可选的参数,可以用来修改命令的行为。例如,使用 -d 或 --detach 选项可以在后台运行命令,而 -i 或 --interactive 让命令保持交互性,-t 或 --tty 则分配一个伪终端。
  • CONTAINER:这是你想要执行命令的容器的名称或 ID。
  • COMMAND:这是你想在容器内执行的命令。
  • [ARG...]:这是传递给命令的额外参数。

上面命令会启动一个交互式的 bash shell 在容器内部,允许你直接运行容器内命令。

退出容器 terminal:

查看日志

对应 docker logs 命令:

查看容器的详情

对应 docker inspect 命令:

推送镜像

Docker 提供了 Docker Hub 镜像仓库,可以把本地镜像 push 到仓库,或从仓库 pull 镜像到本地。

首先去 docker hub 注册一个账户,登录 docker hub,输入用户名密码:

首先对对镜像进行标记(tag):

bash 复制代码
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
  • SOURCE_IMAGE[:TAG] 是本地已有的镜像名称和标签。
  • TARGET_IMAGE[:TAG] 是要推送到仓库的目标镜像名称和标签。

发布镜像:

bash 复制代码
docker push TARGET_IMAGE[:TAG]
  • TARGET_IMAGE[:TAG] 是已经标记的目标镜像名称和标签。

发布后,其他人可拉取并运行你的镜像。

管理数据卷

使用 docker volume 命令:

启动停止删除容器

  • docker start container_name_or_id 启动一个已经停止的容器
  • docker stop container_name_or_id 停止一个容器
  • docker rm container_name_or_id 删除一个容器,如果正在运行,且你想强制删除:docker rm --force container_name_or_id

制作 dockerfile

Dockerfile 是一个文本文件,包含了一系列指令和配置,用来定义如何自动化构建 Docker 镜像。例如,可以指定基础镜像、工作目录、需要复制的文件、运行命令等。

Dockerfile 的基本结构非常简单,通常是从基础镜像开始,然后添加一系列的层,每一层都代表一个指令。

常用指令

  1. **FROM:**指定基础镜像,后续的指令都是基于这个镜像进行的,所有的 Dockerfile 都必须以一个 FROM 指令开始。例如FROM ubuntu:latest 表示使用最新的 Ubuntu 镜像作为基础。
  2. RUN:用于在镜像构建过程中运行命令。这些命令会在当前镜像的顶层添加一个新的层。
  3. CMD :提供容器启动时的默认命令。一个 Dockerfile 中只能有一个 CMD 指令。 例如 CMD ["nginx", "-g", "daemon off;"] 会启动 nginx 服务器。
  4. LABEL :添加元数据到镜像,比如作者、版本、描述等。例如 LABEL version="1.0"
  5. EXPOSE :声明容器运行时监听的端口。 例如:EXPOSE 80 表示容器将在运行时监听 80 端口。
  6. ENV :设置环境变量。例如 ENV NGINX_VERSION 1.15.8 会设置一个名为 NGINX_VERSION 的环境变量,其值为 1.15.8
  7. COPY :复制本地文件或目录到镜像中,例如:COPY . /app 会将当前目录下的所有文件和目录复制到镜像的 /app 目录中。
  8. ADD:与 COPY 类似,但可以自动解压压缩文件,并可以从 URL 添加文件。不过,推荐使用 COPY 指令,因为它更明确且易于理解。
  9. ENTRYPOINT:配置容器启动时运行的命令,可以与 CMD 配合使用。

VOLUME:创建一个挂载点,用于容器与宿主机之间的数据共享。例如,VOLUME /data 会创建一个名为 /data 的挂载点,会生成一个临时目录关联容器 app 目录,这样就算后面 docker run 没 -v 指定数据卷,将来也可以找回数据。

  1. USER :指定运行容器时的用户名或UID。例如 USER nginx 会以 nginx 用户的身份运行容器。
  2. WORKDIR :设置工作目录,后续的 RUN、CMD、ENTRYPOINT、COPY 和 ADD 指令都会在这个目录下执行。例如 WORKDIR /app

ENTRYPOINT 与 CMD 的区别

在 Dockerfile 中,ENTRYPOINTCMD 都可以用来指定容器启动时执行的命令,但还是有差别的:

ENTRYPOINT

ENTRYPOINT 的主要目的是指定容器启动时需要执行的命令和参数,它可以确保容器作为一个特定的应用或服务运行。

当使用 ENTRYPOINT 时,容器启动的命令会被固定下来,用户在运行容器时传递的任何额外参数都会被追加到 ENTRYPOINT 指定的命令之后。

例如,如果 Dockerfile 包含如下 ENTRYPOINT

dockerfile 复制代码
ENTRYPOINT ["executable", "param1", "param2"]

那么在运行容器时传递的任何参数都会追加到这个命令之后。假设运行容器时传递了 param3param4,那么容器实际执行的命令会变成:

shell 复制代码
executable param1 param2 param3 param4

CMD

CMD 也可以用来指定容器启动时执行的命令,但它更灵活。

如果 Dockerfile 中同时指定了 CMDENTRYPOINT,那么 CMD 中的内容会作为默认参数传递给 ENTRYPOINT

如果只指定了 CMD 而没有指定 ENTRYPOINT,那么 CMD 中的命令和参数会在容器启动时执行。

用户在运行容器时传递的任何参数会覆盖 CMD 中的默认参数。

例如,Dockerfile 如下:

dockerfile 复制代码
CMD ["executable", "param1", "param2"]

如果用户在运行容器时没有传递任何参数,那么容器启动时执行的命令就是 executable param1 param2

但如果用户传递了 param3param4,那么这些将覆盖 CMD 中的默认参数,容器启动时执行的命令将变为 executable param3 param4

结合使用

在实践中,ENTRYPOINTCMD 可以结合使用来提供更大的灵活性。
ENTRYPOINT 定义了容器的主要执行命令,而 CMD 提供了该命令的默认参数。

用户在运行容器时传递的任何参数都会追加到 ENTRYPOINT 指定的命令之后,这允许用户覆盖 CMD 中指定的默认参数。

例如:

dockerfile 复制代码
ENTRYPOINT ["executable"]
CMD ["param1", "param2"]

这样,如果用户在运行容器时没有传递任何参数,那么执行的命令会是 executable param1 param2

如果用户传递了 param3,那么执行的命令将变为 executable param3,这里的 param3 覆盖了 CMD 中的默认参数。

docker build 命令

docker build 用于创建 Docker 镜像:

bash 复制代码
docker build [OPTIONS] PATH | URL | -
  • [OPTIONS]:构建镜像时可以指定的一系列选项。
  • PATH | URL | -:指定 Dockerfile 所在的路径、URL 或者从标准输入读取 Dockerfile 的内容。

常见选项:

  • -t, --tag:给创建的镜像打上标签,格式为 name:tag。
  • --build-arg:设置构建镜像时的变量。
  • --file, -f:指定要使用的 Dockerfile 路径。
  • --no-cache:构建镜像时不使用缓存。
  • --rm:设置为在构建过程失败时不移除中间容器,默认开启。
  • --squash:将构建过程中产生的所有层合并成一层,减小镜像大小。

使用 dockerfile 构建一个镜像

dockerfile 复制代码
# 使用官方nginx镜像作为基础镜像  
FROM nginx:stable-alpine  
  
# 设置工作目录为/usr/share/nginx/html  
WORKDIR /usr/share/nginx/html  
  
# 将当前目录下的index.html文件复制到镜像的/usr/share/nginx/html目录中  
COPY index.html .  
  
# 暴露80端口供容器运行时使用  
EXPOSE 80  
  
# 启动nginx服务器并以前台模式运行(注意这里使用了CMD指令而不是ENTRYPOINT指令)  
CMD ["nginx", "-g", "daemon off;"]

后续项目目录执行:

bash 复制代码
docker build -t my-nginx-image:latest .

其中,-t 选项用于指定镜像的名称和标签,. 表示 Dockerfile 所在的当前目录。

Docker 默认使用名为 Dockerfile 的文件作为构建指令的来源,但也可以通过 -f 参数指定其他文件名。

我们使用 docker build 命令构建一个名为 my-app、标签为 v1 的镜像:

点击 run 填写参数

点击 Run 之后,访问页面 http://localhost:9999/

进入容器 files:

可以发现这个 index.html 文件就是我们之前项目目录下 index.html 文件。

有没有办法,我们项目 index.html 改动容器内跟着改?

运行镜像:

bash 复制代码
docker run --name my-app-test2 -d -v ./:/usr/share/nginx/html -p 8888:80 my-app:v1
  • -v ./:/usr/share/nginx/htm:当前目录(./)被映射到容器内的 /usr/share/nginx/html 目录。这意味着对宿主机当前目录的任何更改都会反映在容器的 /usr/share/nginx/html 目录中,反之亦然。

修改 index.html 文件:

访问 http://localhost:8888/

没问题。

这样一套流程就打通了。

docker 原理

  • Namespace 机制:实现资源的隔离
    • PID namespace:独立的进程 ID
    • IPC namespace:进程间通信限制
    • Mount namespace:文件系统隔离
    • Network namespace:网络资源隔离
    • User namespace:用户和用户组隔离
    • UTS namespace:主机名和域名隔离

通过这些 Namespace,Docker 确保了每个容器的独立性,使得在容器内运行的代码就像在一个独立的系统中运行一样。

  • Control Group:资源访问限制
    • 通过设定 CPU、内存、磁盘使用参数,限制容器资源占用
  • UnionFS:文件系统分层存储
    • 分层镜像设计,通过 UnionFS 合并形成完整文件系统
    • 镜像层的共享减少磁盘空间占用

Docker 的实现原理依赖于 Linux 的 Namespace、Control Group 和 UnionFS 这三种机制。

相关推荐
码农飞飞4 分钟前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举
一个小坑货6 分钟前
Rust 的简介
开发语言·后端·rust
吃杠碰小鸡17 分钟前
commitlint校验git提交信息
前端
小袁搬码18 分钟前
Windows中指定路径安装DockerDesktop
windows·docker·容器·docker desktop
monkey_meng36 分钟前
【遵守孤儿规则的External trait pattern】
开发语言·后端·rust
qq_3129201143 分钟前
docker 部署 kvm 图形化管理工具 WebVirtMgr
运维·docker·容器
踏雪Vernon43 分钟前
[OpenHarmony5.0][Docker][环境]OpenHarmony5.0 Docker编译环境镜像下载以及使用方式
linux·docker·容器·harmonyos
虾球xz1 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
Estar.Lee1 小时前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
我爱李星璇1 小时前
HTML常用表格与标签
前端·html