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 则分配一个伪终端。

  • 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\]:构建镜像时可以指定的一系列选项。

常见选项:

  • -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 这三种机制。

相关推荐
裴嘉靖2 分钟前
Vue 生成 PDF 完整教程
前端·vue.js·pdf
毕设小屋vx ylw2824264 分钟前
Java开发、Java Web应用、前端技术及Vue项目
java·前端·vue.js
间彧24 分钟前
Kubernetes的Pod与Docker Compose中的服务在概念上有何异同?
后端
间彧28 分钟前
从开发到生产,如何将Docker Compose项目平滑迁移到Kubernetes?
后端
间彧33 分钟前
如何结合CI/CD流水线自动选择正确的Docker Compose配置?
后端
间彧34 分钟前
在多环境(开发、测试、生产)下,如何管理不同的Docker Compose配置?
后端
间彧36 分钟前
如何为Docker Compose中的服务配置健康检查,确保服务真正可用?
后端
间彧40 分钟前
Docker Compose和Kubernetes在编排服务时有哪些核心区别?
后端
间彧1 小时前
如何在实际项目中集成Arthas Tunnel Server实现Kubernetes集群的远程诊断?
后端
冴羽1 小时前
今日苹果 App Store 前端源码泄露,赶紧 fork 一份看看
前端·javascript·typescript