Docker 浅谈

Docker 是什么

你可以把 Docker 理解为软件世界的集装箱。就如 Docker logo 所展示的,是一头鲸鱼驮着若干集装箱。Docker 可以允许开发者将应用以及所有依赖项(库、环境等)打包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 或 Windows 机器上。

诞生的背景

应用在开发、测试、生产环境中,由于环境依赖、配置差异等等问题,常导致 "在我这能跑" 的难题。传统虚拟机虽能隔离环境,但资源占用高、启动慢、笨重,无法满足轻量、快速部署的需求。 Docker 便在这种背景下诞生。

Docker 与虚拟机的区别

虚拟机能实现环境隔离,解决部署环境一致性的问题。那么 Docker 又是怎么解决的,有什么优势呢?

一言以蔽之:虚拟机虚拟化硬件,依赖完整的操作系统;而容器虚拟化操作系统,共享主机内核,因此更轻量、更快速。

通俗化的理解就是:

  • 虚拟机就像一栋独栋别墅。拥有自己的地基、墙体、水电系统(虚拟硬件)和完整的家庭空间(操作系统)。私密性好(隔离强),但建造和启动慢,占用土地多(资源消耗大)。

  • Docker 容器就像一栋公寓楼里的一个单间。所有房间共享大楼的地基和主体结构(主机内核),但每个房间有自己独立的墙壁(隔离)、家具(应用与依赖)。启动快(拎包入住),非常节省资源。

核心概念

Dockerfile

文本文件,描述如何构建镜像(例如指定基础镜像、安装软件、复制文件等)。

镜像 Image

Dockerfile 构建后的产物即镜像(Image)。镜像是只读的,类似于安装系统用的 .iso 文件,它包含了运行应用所需的所有东西(代码、库、环境变量、配置文件)。

层 Layer

镜像由层构成,每个层表示文件系统的变更------增加、修改、删除。

容器 Container

容器就是实际运行中的实例,基于镜像创建而来,与其他容器隔离。类似于一个轻量级的、隔离的虚拟机(但本质是进程)。

仓库 Registry

存储和分发镜像的平台,如 Docker Hub(Docker 官方平台)。

Docker Compose

容器的一个最佳实践是每个容器应该只做一件事。但实际项目可能需要同时运行过个容器,并且同时管理容器间的网络连接等。使用 Docker Compose,可以实现在一个 YAML 文件中定义所有容器及其配置。然后使用一条命令便可启动运行。

例如一个项目包含前端、后端、数据库,分别对应三个镜像,这一个项目可以用一个 docker-compose.yml 文件描述,包含三个镜像,用一条命令就能启动。

Docker 架构

Docker 采用客户端-服务器架构。Docker 客户端和守护进程可以运行在同一系统上,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程通过 REST API 进行通信,通信方式可以是 UNIX 套接字或网络接口。

  • Docker 守护进程(dockerd)监听 Docker API 请求,并管理 Docker 对象,如镜像、容器、网络和卷。守护进程还可以与其他守护进程通信,以管理 Docker 服务。

  • Docker 客户端(docker)是许多 Docker 用户与 Docker 交互的主要方式。当你使用 docker run 等命令时,客户端会将这些命令发送给 dockerd,由其执行。docker 命令使用 Docker API。Docker 客户端可以与多个守护进程通信。另一个 Docker 客户端是 Docker Compose,它允许你操作由一组容器组成的应用程序。

Docker 的基础使用场景

用 Dockerfile 构建镜像

以下是一个简单示例,描述了一个前端项目的 Dockfile

dockerfile 复制代码
# 指定基础镜像,使用官方 nginx 镜像
FROM nginx:alpine

# 删除默认 nginx 静态资源
RUN rm -rf /usr/share/nginx/html/*

# 拷贝 web 项目的 build  文件到 nginx 静态目录下
COPY dist/ /usr/share/nginx/html/

# 拷贝自定义 nginx 配置
COPY default.conf /etc/nginx/conf.d/default.conf

# 暴露端口 80
EXPOSE 80

# 让 NGINX 在前台运行,防止 Docker 容器启动后立即退出
CMD [ "nginx" , "-g" , "daemon off;" ]

如上 FROM/RUN/CMD 等是 Dockerfile 的指令

指令 说明
FROM 定制的镜像都是基于某个基础镜像,FROM 就是用来指定基础镜像
RUN 在构建过程中的执行命令
CMD 创建容器时要执行的默认命令
COPY 复制指令,从上下文目录中复制文件或者目录到容器内指定路径
EXPOSE 声明容器运行时监听的网络端口
ENV 在容器内部设置环境变量

构建指令

sh 复制代码
# 以当前目录为上下文构建名称为 frontend 的镜像,即当前目录需要有 Dockerfile
docker build -t frontend .

frontend 为镜像名称,也可以指定标签如 frontend:v1,frontend:latest,

镜像推送和拉取

如果是为了在其他机器上使用镜像,那我们需要用到镜像仓库。

需要登陆才可使用进行推送操作

sh 复制代码
# docker 登陆命令
docker login [hub URL]

将本地镜像推送到远程的镜像仓库

sh 复制代码
docker push my-image:latest

从镜像仓库拉取指定镜像

sh 复制代码
docker pull my-image:latest

关于拉取镜像:拉取的镜像会存在本地,然后当基于镜像创建容器时,如果指定的镜像本地没有,则会默认从仓库拉取镜像,有则使用本地镜像。
本地可以安装使用 Docker Desktop,查看镜像、容器等,非常方便。

基于镜像创建容器

创建容器使用 docker run 命令,如启动一个基于 ubuntu 的镜像,启动后在前台运行(即命令行不会退出)

sh 复制代码
docker run ubuntu

一般有几种场景:

后台运行容器

在后台运行 ubuntu 容器并返回容器 ID

sh 复制代码
docker run -d ubuntu 

交互式运行并分配终端

以交互模式运行 ubuntu 容器,并启动一个 Bash shell

sh 复制代码
docker run -it ubuntu /bin/bash

临时容器

启动一个 ubuntu 容器,执行 xxxx 命令,完成后自动删除容器

sh 复制代码
docker run --rm ubuntu sh -c "xxxxx"

另外一下是一些常用的参数:

参数 示例 含义
--name --name my_container 指定容器名称,不指定的话 docker 会随机生成一个名称
-p -p 8080:80 端口映射,主机的 8080 端口映射为容器内的 80 端口
-v -v /host/data:/container/data 挂载卷,将主机的 /host/data 目录挂载到容器内的 /container/data 目录
-e, --env -e MY_ENV=my_value 设置环境变量 MY_ENV=my_value
--network --network host 指定网络模式

容器管理

容器启动、停止、重启

sh 复制代码
docker start my_container
docker stop my_container
docker restart my_container

删除一个或多个已经停止容器

sh 复制代码
docker rm my_container

在运行中的容器中执行命令

sh 复制代码
# 在 my_container 容器中执行 ls /app 命令
docker exec my_container ls /app 

查看当前运行的容器

sh 复制代码
# 查看所有运行的容器
docker ps

# 查看所有容器,包含停止的
docker ps -a

# 显示所有容器 ID
docker ps -aq

# 输出示例
CONTAINER ID   IMAGE               COMMAND                  CREATED       STATUS       PORTS                                     NAMES
09b4274e4356   project__frontend   "/docker-entrypoint...."   3 hours ago   Up 3 hours   0.0.0.0:8081->80/tcp, [::]:8081->80/tcp   frontend
30486b3d96d0   project__backend    "./wait-for-mysql.sh"    3 hours ago   Up 3 hours   1024/tcp                                  backend
929f7bfcf316   project__mysql      "docker-entrypoint.s..."   3 hours ago   Up 3 hours   3306/tcp, 33060/tcp                       mysql
6d78b99b0e3b   redis:alpine        "docker-entrypoint.s..."   3 hours ago   Up 3 hours   6379/tcp                                  redis

docker ps 常用来查看所有容器的运行状态。容器状态有 7 种:

  • created,已创建,从未启动过的容器

  • running,运行中,通过docker startdocker run启动。

  • paused,已暂停,通过docker pause暂停。

  • restarting,重启中,由于容器指定的重启策略而正在启动。

  • exited,已停止,一个不再运行的容器。例如,容器内的进程已完成,或者容器已通过docker stop命令停止。

  • removing,删除中,执行docker rm,容器正在被移除

  • dead,已失效,例如,由于外部进程占用资源而仅被部分移除的容器。dead的容器无法(重新)启动,只能被移除

查看容器运行的日志

sh 复制代码
docker logs my_container

# 跟随日志,类似于 tail -f
docker logs -f my_container 

查看容器端口映射信息

sh 复制代码
docker port my_container

# 输出示例
80/tcp -> 0.0.0.0:8081  # ipv4
80/tcp -> [::]:8081     # ipv6

Docker Compose 的使用场景

实际一个项目,可能会有多个服务组成,即完整运行一个服务可能会基于多个镜像运行多个容器,包含若干配置,以及多个容器协作需要创建网络等。docker compose 就是针对这种情况的解决方案,通过 docker-compose.yml 文件可以配置多个容器组成的服务,以及它们依赖的网络配置等。然后可以用一个命令就能启动这个多容器的项目。

docker-compose.yml

如下所示,docker-compose.yml 定义了 5 个服务,包含数据库服务、后端服务、前端服务

yaml 复制代码
services:  # 服务配置
  mysql:
    container_name: mysql # 容器名称
    build: ./mysql             # 构建目录
    platform: linux/amd64      # 架构类型
    image: project__mysql  # 镜像名称(本地构建可不需要)
    restart: always   # 重启策略
    volumes:          # 依赖卷
      - mysql-data:/var/lib/mysql

  redis:
    container_name: redis
    image: redis:alpine
    restart: always
    volumes:
      - redis-data:/data

  backend:
    container_name: backend
    build: ./backend
    platform: linux/amd64
    image: project__backend
    depends_on:     # 启动顺序依赖
      mysql:
        condition: service_healthy
      redis:
        condition: service_started

  frontend:
    container_name: frontend
    build: ./frontend
    platform: linux/amd64
    image: project__frontend
    ports:     # 端口映射
      - "8081:80"
    depends_on:
      - backend

  frontend-admin:
    container_name: frontend_admin
    build: ./frontend-admin
    platform: linux/amd64
    image: project__frontend-admin
    ports:
      - "8082:80"
    depends_on:
      - backend

volumes:   # 卷配置
  mysql-data:
  redis-data:

docker-compose.yml 所处目录的上下文示意:

scss 复制代码
.
|-mysql
|  |-Dockerfile
|  `-(other files)
|-backend
|  |-Dockerfile
|  `-(other files)
|-frontend
|  |-Dockerfile
|  `-(other files)
|-frontend-admin
|  |-Dockerfile
|  `-(other files)
`-docker-compose.yml

构建与推送镜像

构建

含有 build 配置的服务,会自动依赖其所指定的目录进行构建,指定目录下需包含 Dockerfile

sh 复制代码
docker compose build

推送

构建完成后,可以将构建完成的镜像自动推送到镜像仓库,即含有 build 配置的项会自动推送到 image 定义的镜像位置(当然镜像仓库是有权限控制的)

sh 复制代码
docker compose push

启动命令与过程

启动命令

sh 复制代码
# 启动服务在前台运行,一旦退出则服务停止
docker compose up 

# 启动服务在后台运行
docker compose up -d

启动的具体过程

  1. 前期准备与验证

    1. 读取 Compose 文件
    2. 验证配置,检查 YAML 语法、服务定义、网络、卷等配置是否正确。
    3. 项目环境,确定项目名称(默认是当前目录名,可通过 -p 指定)。
  2. 构建与拉取镜像

    1. 如果配置了 build,会构建镜像,如果本地构建过,则直接使用缓存,否则会进行构建,如果配置了 --build 则会使用进行构建。
    2. 如果配置了 image,会拉取镜像,如果本地不存在就会从镜像仓库拉取,如果配置了 --pull=always 会始终从镜像仓库拉取。
  3. 创建网络和卷

    1. 创建网络:根据 networks 部分创建自定义网络。默认会为整个项目创建一个名为 {project-name}_default 的网络,所有服务都会连接到这个网络,以便通过服务名进行通信。
    2. 创建卷:根据 volumes 部分创建命名的持久化卷。如果卷已存在,则直接使用。
  4. 启动服务(容器)

    1. 按依赖顺序启动,会根据 depends_on 配置来确定启动顺序。

      1. 例如,一个 web 服务依赖 db 服务,那么 Compose 会先启动 db,然后再启动 web。但注意 depends_on 只能决定启动顺序,并不能保证 web 启动时 db 已经正常运行
    2. 容器创建:为每个服务创建并启动容器,这相当于对每个服务执行 docker run

    3. 重启策略:会根据 restart 策略来管理容器的生命周期

  5. 服务健康检查(如果配置了 healthcheck

    1. 等待健康状态:如果服务配置了健康检查(healthcheck),Compose 会等待容器变为 healthy 状态,然后再启动依赖于它的下一个服务。

    2. depends_on 的区别:

      • depends_on 只控制启动顺序(容器进程运行)。
      • healthcheck + condition: service_healthy 控制就绪顺序(容器内应用真正准备好接收请求)。这是更可靠的方式
  6. 后续

    1. 前台模式:会用一个彩色的输出流输出所有服务的日志,按下 Ctrl+C 会停止所有正在运行的服务

    2. 后台模式(-d 参数):启动命令会立即返回,只打印出创建了哪些容器。之后可以用docker compose logs 来查看日志,使用 docker compose ps 查看状态。

使用启动命令时常用的一些参数

参数 示例 含义
-f docker compose -f xxx.yml up 用于指定某个 docker compose yml 配置启动,默认是当前目录下的 docker-compose.yml 文件
-p docker compose -p app up 指定启动项目名称,默认是以当前的目录名
--build docker compose up --build 启动时强制重新构建镜像,即使镜像已存在
--pull docker compose up --pull= 指定启动前拉镜像的策略, - always,总是从远程拉镜像,即使本地已经有了,保证运行时是最新镜像 - missing,(默认值)如果本地不存在指定的镜像,则从仓库拉取。如果本地已存在,则直接使用本地镜像 - never,从不拉取镜像。如果本地不存在该镜像,则命令失败

运维:查看、停止

查看日志

sh 复制代码
docker compose logs 

停止并删除 docker compose 所创建的容器

sh 复制代码
docker compose down

重启 docker compose 所创建的容器

sh 复制代码
docker compose restart

网络 Network

网络模式

安装 Docker 时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host。

运行容器时,可以指定容器运行的网络模式。

  • Host

    • 通过 --network=host 指定
    • 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口
  • Bridge

    • 通过 --network=bridge 指定,如果不指定,会默认以 brdge 模式
    • 为每一个容器分配、设置IP等,并将容器连接到一个 docker0 虚拟网桥,通过 docker0 网桥以及 Iptables nat 表配置与宿主机通信
  • None

    • 通过 --network=none 指定
    • 关闭容器的网络功能
  • Container

    • 通过 --network=container:NAME_or_ID 指定
    • 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围。
  • 指定自定义网络

相关命令

sh 复制代码
# 列出所有网络
docker network ls
# 创建一个新的网络
docker network create <network>
# 删除指定的网络
docker network rm <network>
# 连接容器到网络
docker network connect <network> <container>
# 断开容器与网络的连接
docker network disconnect <network> <container>

卷 Volume

卷是容器的持久性数据存储,由 Docker 创建和管理。可以使用docker volume create命令显式创建卷,Docker 也可以在创建容器或服务时创建卷。

创建卷时,它会存储在 Docker 主机上的一个目录中。将卷挂载到容器时,挂载到容器中的就是这个目录。这与绑定挂载的工作方式类似,不同之处在于卷由 Docker 管理,并且与主机的核心功能隔离开来。

卷相关的命令

sh 复制代码
# 列出所有卷
docker volume ls
# 创建一个新的卷
docker volume create <volume>
# 删除指定的卷
docker volume rm <volume>
# 显示卷的详细信息
docker volume inspect <volume>

Docker in Docker (DinD)

Docker in Docker(简称 DinD)指的是在一个 Docker 容器内部运行一个完整的 Docker 守护进程(Docker Daemon)和客户端(Docker CLI)。

简单来说,就是"容器里的容器"。你可以在一个容器中执行 docker builddocker run 等命令,就像在宿主机上一样。

可以使用官方的 docker:dind 镜像进行构建。这个镜像经过特殊配置,可以在容器内启动并运行 Docker 守护进程。运行此类容器通常需要 --privileged(特权)模式。

优点

  • 环境隔离:构建环境与宿主机完全隔离,非常干净,不会相互污染。

  • 安全性相对较高:与 DoD 相比,它对宿主机的影响更小,进程和文件系统都是隔离的。

缺点

  • 性能开销大:在容器内运行完整的 Docker 守护进程和存储驱动(通常使用效率较低的 vfs),资源占用高。

  • 无法共享缓存:每次启动一个 DinD 容器,都是一个全新的环境,之前构建的镜像缓存会丢失,导致构建速度慢。

业务场景

业务中需要通过 docker compose 启动多个容器,而部署环境的容器中使用了 host 的网络模式,内部再使用 docker compose 启动会默认创建网络,由于要部署的环境众多,这会导致 host 网络无法分配的问题。此时需要使用 Docker in Docker 的方式进行部署,隔绝项目网络与部署环境的网络。

结语

以上是业务中使用到的 Docker 知识的简单总结,本文对 Docker 相关知识的多个方面均有涉及,但受限于篇幅和定位,各部分内容的探讨尚属浅显。若希望深入理解 Docker 的底层原理、高级应用及最佳实践,可进一步阅读专业技术文档如 Docker 官方网站等,以获取更系统、全面的知识体系。

相关推荐
wok15718 小时前
Windows 上使用 Docker Desktop 教程
windows·docker·容器
MUTA️19 小时前
x86 架构下运行 ARM-ROS2 Docker 镜像操作指南
arm开发·docker·架构
hqzing19 小时前
低成本玩转鸿蒙容器的丐版方案
docker·harmonyos
李少兄19 小时前
Kubernetes 日志管理
docker·容器·kubernetes
suamt20 小时前
记录windows下如何运行docker程序
运维·docker·容器
特立独行的猫a20 小时前
低成本搭建鸿蒙PC运行环境:基于 Docker 的 x86_64 服务器
docker·容器·harmonyos·鸿蒙pc
鋆雨无欢丶20 小时前
docker证书认证问题
运维·docker·容器
阿杰 AJie20 小时前
Docker 容器启动的全方位方法汇总
运维·docker·容器
我的golang之路果然有问题21 小时前
Docker 之常用操作(实习中的)
java·运维·笔记·docker·容器·eureka