Docker简介

什么是Docker?

Docker–> “码头工人”;Docker基于go语言开发,遵循apache2.0协议(开源协议,允许软件的商业使用)。

Docker 是一个开源的容器化平台,用于打包、分发和运行应用程序,使得应用程序和它的所有依赖(依赖库、配置文件、环境等)可以被打包成一个轻量级、可移植的容器,这样就能确保应用程序在任何环境中都能一致地运行,而不受操作系统和硬件的限制。Docker的宗旨是“一次封装,到处运行”。

容器(container)使用沙盒机制,容器之间不会存在任何接口。几乎没有性能开销,可以很容易的在计算机和数据中心运行。最重要的是,他们不依赖于任何语言、框架或者系统。

Docker有什么特性?

  1. 环境隔离: 使用 Docker,可以为每个项目或服务创建独立的容器,这些容器在相同的物理机器上运行时不会相互干扰。使得不同的应用程序、服务可以在完全隔离的环境中运行,避免环境冲突(如同时安装python3.8和python3.9,或者相同服务的不同版本)。
  2. 微服务架构: Docker 是微服务架构的理想选择,因为每个微服务可以运行在独立的容器中,容器间可以通过网络进行通信。Docker 使得微服务的部署、扩展和管理变得更加容易。
  3. 持续集成和持续部署(CI/CD): Docker 可以帮助开发人员创建一致的开发和测试环境,避免了传统开发过程中“在A电脑上能运行,在B电脑上无法运行”的问题。同时,Docker 容器可以在不同环境之间快速迁移,简化了持续集成和持续部署的流程。
  4. 跨平台开发: Docker 容器不依赖于具体的操作系统或硬件环境,因此开发者可以将容器打包并在任何平台(如开发环境、测试环境、生产环境)上运行,保证应用程序的跨平台兼容性。
  5. 轻量化和资源节省: 相比虚拟机,Docker 容器的开销更小,因为容器与宿主操作系统共享内核,没有冗余的操作系统开销。多个容器可以在同一台机器上高效地共享资源,提升资源利用率。

Docker在不同操作系统上的表现(Linux为主)

  1. Linux: Docker 最初是基于 Linux 容器技术(LXC)开发的,在 Linux 上运行 Docker 时,容器直接使用 Linux 内核(不同的Linux操作系统共享 Linux 内核的基本功能和特点,如进程管理、内存管理、硬件支持等)提供的技术(如 namespaces 和 cgroups)来实现隔离。因此,Docker 在 Linux 环境下是原生运行的,能够利用系统的资源和特性来提供高效的容器化服务。
  2. Windows: 在 Windows 上,Docker 本身不能直接利用 Windows 内核的容器功能,因为 Windows 的容器与 Linux 容器是不同的。为了支持在 Windows 上运行 Docker,Docker 使用了 WSL (Windows Subsystem for Linux) 来模拟一个 Linux 环境,让 Docker 能在这个虚拟环境中运行 Linux 容器。因此,尽管 Docker 在 Windows 上能运行,但它仍然依赖于 WSL 提供的虚拟化层,来模拟一个 Linux 环境。
  3. macOS: 在 macOS 上,Docker 的运行方式类似于 Windows,通过一个虚拟化层来提供 Linux 环境。由于 macOS 本身不支持 Linux 容器,Docker 会在后台启动一个轻量级的虚拟机,通常使用 HyperKitLinuxKit 来实现对 Linux 内核的模拟,允许 Docker 容器在 macOS 上运行。这个虚拟机提供了一个 Linux 环境,容器在其中运行。因此,虽然 Docker 能在 macOS 上运行,但本质上它是在一个虚拟化的 Linux 环境中运行。

Docker 使用 Linux 内核的特性(如 cgroupsnamespaces)来实现容器化,提供隔离和资源管理:

  • Namespaces:用于为每个容器提供独立的网络、进程、文件系统等环境,从而实现容器之间的隔离。

  • **Control Groups (cgroups)**:用于限制、记录和隔离容器使用的资源,如 CPU、内存和磁盘 I/O,确保容器的资源使用不会影响到主机或其他容器。


总结: Docker 是基于 Linux 的技术,虽然它可以在多种操作系统上运行,但其核心功能是建立在 Linux 上的。

容器间通信

通过 Docker 网络进行通信(最常见)

Docker 提供了强大的网络功能来让容器之间能够互相通信。容器可以在同一个网络上进行通信。Docker 默认提供了多种不同的网络模式,可以根据需求选择合适的网络模式。默认网卡是docker0。

docker默认网络环境

a. 桥接网络(Bridge Network)

  • 这是 Docker 默认的网络模式。每个容器都连接到一个虚拟的桥接网络(获取一个独立的IP地址),容器可以通过IP地址进行通信,适用于单主机上多容器之间通信。
  • 例如 WordPress 和 MySQL 容器都在同一个桥接网络中,它们可以通过容器的 IP 地址或容器名来互相访问。

b. 主机网络(Host Network)

  • 主机网络模式让容器共享宿主机的网络接口。容器会直接使用宿主机的 IP 地址进行通信,这意味着容器不再有独立的 IP 地址。容器间通过 localhost 或主机 IP 通信。
1
2
3
4
5
6
# 使用主机网络运行容器
docker run -d --name container1 --network host my-image
docker run -d --name container2 --network host my-image

# 在 container1 中访问 container2 的服务
curl http://localhost:<port>

c. 无网络(None Network)

  • 容器不连接任何网络,适用于容器不需要网络通信的情况。

d. 自定义网络(Custom Network)

  • 可以为容器创建一个自定义的网络。在自定义网络中,所有的容器都可以通过容器名进行通信(DNS 解析),而不需要知道容器的 IP 地址。
  • 例如,可以创建一个名为 wordpress-network 的自定义网络,将 WordPress 和 MySQL 容器都连接到这个网络中。然后,WordPress 可以通过 mysql 来连接 MySQL。

通过共享存储通信

容器也可以通过共享卷(Volume)或绑定挂载(Bind Mount)共享数据。适用于需要共享文件或数据的场景。

共享卷(Volume)

  • 特点
    • 数据持久化,独立于容器生命周期。
    • 多个容器可以挂载同一个卷。

绑定挂载(Bind Mount)

  • 特点
    • 将主机目录挂载到容器中。
    • 多个容器可以挂载同一个主机目录。
  • 使用场景
    适用于开发环境或需要与主机共享数据的场景。

通过 Docker Compose 简化通信

Docker Compose 是一个用于定义和运行多容器 Docker 应用的工具,支持定义网络、卷和环境变量。通过 docker-compose.yml 文件,可以方便地定义多个服务并让它们在同一网络下进行通信。在 Compose 中定义的服务名实际上就是容器名,可以直接通过服务名进行访问。

例如,WORDPRESS_DB_HOST: mysql:3306 中的 mysql 就是 MySQL 服务的名称。Compose 会自动将这个服务解析为相应的容器。

端口映射(Port Mapping)

当容器需要从外部访问时,Docker 允许将容器的端口映射到宿主机的端口。通常用于容器之间的外部通信,但对于容器内部通信,不需要进行端口映射。我现在使用的openwrt系统内安装的docker容器就是通过这种方式进行访问的。

示例:

-p 3000:8080: 这个用于端口映射(port mapping)。将宿主机的 3000 端口映射到Docker 容器内部的 8080 端口。当访问宿主机的 3000 端口时,Docker 会将这个请求转发到运行在容器内部监听 8080 端口的应用程序。

docker配置端口注意事项

在使用docker run -p 3000:3000创建容器时,一定要注意容器内部使用的端口号(一般官方会给出),如果在运行镜像时将端口映射写成 -p 3000:false port,很可能会导致无法通过宿主机的 3000 端口访问到容器(或者容器提供的web页面)。例如Open WebUI 应用程序在 Docker 容器内部监听的是 8080 端口(这是其默认配置)。端口映射 -p 3000:3000 则是将宿主机的 3000 端口映射到容器内部的3000 端口,导致端口不匹配,由于 Open WebUI 内部的 Web 服务监听的是 8080 端口,而宿主机的 3000 端口映射到了容器内部的3000端口,Docker 就无法正确地将外部对宿主机 3000 端口的访问转发到 Open WebUI 的 Web 服务上(配置云服务器openwebUI时遇到的问题,一个傻逼教程给的命令)。

一个镜像创建成容器时,镜像使用的端口是固定的(容器端口是镜像开发者预先配置好的,应用程序在容器内部监听这些端口),不能随便更改,宿主机端口在运行容器时通过 -p 选项指定,用于将外部流量导向容器内部的相应端口。可以根据需要自由选择宿主机端口(只要不冲突),但要确保它映射到容器内部应用程序实际监听的端口(别填错内部端口)。

1
2
3
4
5
# 错误命令,映射了错误的端口
docker run -d --name open-webui -p 3000:3000 -v open-webui-data:/app/data --pull always ghcr.io/open-webui/open-webui:main

# 正确命令,使用了容器的正确端口
docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.nju.edu.cn/open-webui/open-webui:main

docker与虚拟机的区别

特性 Docker 虚拟机 (VM)
资源占用 轻量级,资源消耗较少,多个容器共享操作系统内核 重,每个独立的操作系统需要更多的资源
启动速度 秒级 分钟级
操作系统 共享宿主机操作系统的内核 每个虚拟机都有自己的完整操作系统
资源隔离 容器之间共享宿主机内核,但相互隔离 完全隔离,每个虚拟机运行独立的操作系统
性能开销 低,几乎没有虚拟化开销 高,虚拟化带来额外的性能损失
计算能力损耗 几乎无 损耗 50%左右
迁移 容器可以非常容易地在不同主机间迁移 迁移虚拟机较复杂,涉及整个操作系统的复制
硬件虚拟化支持 不需要硬件虚拟化支持 需要硬件虚拟化支持(如 Intel VT-x 或 AMD-V)
可移植性 高,容器化应用能在不同的环境中轻松运行 较低,虚拟机需要匹配目标环境的硬件和OS
文件系统 容器通过共享宿主机文件系统来运行应用 每个虚拟机有自己的独立文件系统
操作系统兼容性 只能运行与宿主机操作系统内核兼容的操作系统 可以运行不同操作系统(Windows、Linux、macOS等)
存储 通常使用卷(Volumes)与宿主机共享数据 虚拟机有自己的虚拟硬盘
应用部署 适用于微服务架构,可以快速部署和扩展 适用于运行完整的操作系统和应用,适合更大规模的应用
隔离性 进程隔离 系统隔离

docker与虚拟机的区别

docker运行流程

docker运行流程1

docker流程图2

容器化技术

docker相关概念

1. 镜像(Image)

Docker将应用程序及其所需的依赖、函数库、环境、配置等文件打包在一起,里面包含了要部署的应用程序以及它所关联的所有库,称为镜像(类似手机.apk安装包 )。镜像是一个只读的模板 ,包含了运行应用程序所需的所有依赖项(代码、运行环境、库等)。类似于虚拟机的快照,容器是基于镜像启动的。镜像中的应用程序运行后形成的进程就是容器

Docker会给容器进程做隔离。一个镜像可以启动多个容器,多个容器之间互不相关,但都基于一个镜像模版。

Docker 镜像中应该包含应用运行所需的所有内容

  • 运行环境(如 Python、Node.js、Ubuntu、Redis等)

  • 源代码(Flask、Vue.js 项目等)

  • 依赖(pip installnpm install 安装的库)

  • 配置文件(环境变量、数据库连接)

  • 容器启动后的执行命令(CMDENTRYPOINT

  • 当项目包含多个组件时(如:Vue + Flask + MySQL + Redis)应使用多个 Dockerfile,并用 docker-compose.yml 统一管理。

2. 容器(Container)

容器是镜像的运行实例,本质上是一个轻量级的隔离进程,可以在宿主机上运行,具有自己的文件系统、进程空间和网络环境。容器可以被创建、启动、停止、删除,每个容器之间默认是相互隔离的。

3. 仓库(Repository)

仓库是一个存储和分享 Docker 镜像的地方。Docker Hub 是官方提供的公共仓库,用户可以将自己的镜像推送到仓库中,也可以从仓库中拉取镜像。官方仓库是所有人都可以访问和使用的仓库。还有其他源站等。国内也有类似DockerHub的镜像托管平台,像网易云镜像服务、阿里云镜像服务、私有云(公司自建)等。

Dockerfile和Docker Compose有什么区别?

在实际应用中Docker Compose是非常非常重要的!!!可以通过一个简单的compose.yaml文件方便地管理多个容器及它们的依赖关系,做到一键启动服务。

Dockerfile 是一个文本文件,类似一个自动化脚本,用于构建 Docker 镜像,类似于在虚拟机中安装操作系统和软件。包含一系列指令和步骤,这些步骤指导 Docker 从基础镜像开始,如何一步一步安装软件、复制文件、配置环境等,最终构建出一个用户自定义的镜像。

映射关系

常见的 Dockerfile 指令:

  • FROM: 指定基础镜像(必须是 Dockerfile 的第一条指令)
  • LABEL:添加元数据(如维护者信息)
  • RUN: 执行命令(如pip安装软件包), RUN允许在创建镜像时运行任意的 shelI命令,如ls、cat、pwd等
  • COPY: 将文件从宿主机复制到镜像
  • ADD: 添加文件和目录,并支持从 URL 下载
  • WORKDIR:用于设置工作目录,指定了接下来所有的命令(如 RUNCMDENTRYPOINTCOPYADD)都将在该目录下执行。如果指定的目录不存在,WORKDIR 会自动创建该目录。
  • CMD: 设置容器启动时执行的默认命令
  • EXPOSE: 声明容器监听的端口
  • ENV: 设置环境变量

dockerfile示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 使用官方的 Python 3.9 作为基础镜像
FROM python:3.9

# 设置维护者信息
LABEL maintainer="email@example.com"

# 设置环境变量,避免 Python 生成 .pyc 文件,提高容器效率
ENV PYTHONUNBUFFERED=1

# 设置工作目录(如果不存在会自动创建)
WORKDIR /app

# 复制当前目录(宿主机的项目文件)到容器的 /app 目录
COPY . /app/

# 安装系统依赖(如 curl、Redis 客户端等)
RUN apt-get update && apt-get install -y \
curl \
redis-tools \
&& rm -rf /var/lib/apt/lists/* # 清理缓存,减少镜像体积

# 安装 Python 依赖
RUN pip install --no-cache-dir -r requirements.txt

# 暴露 Flask 端口
EXPOSE 5000

# 运行 Flask 服务器(假设 app.py 是 Flask 入口文件)
CMD ["python", "app.py"]

Docker Compose 是一个定义和管理多容器应用的工具,通过 docker-compose.yml 文件来定义多个容器之间的关系、网络、卷挂载等信息,能够通过一个命令启动一个多容器应用。Docker Compose 主要用来协调和管理多个服务的生命周期,而不仅仅是构建一个镜像。

Docker会为每一个compose文件都自动创建一个子网,同一个compose文件里面定义的所有容器都会自动加入同一个子网,这个子网内的所有容器都能相互通信;Docker Compose可以通过depends_on(依赖于哪一个容器,先把它启动)参数自定义容器的启动顺序。

docker compose启动示例

Docker Compose 适用场景

  • 管理多个容器:如 Flask + PostgreSQL + Redis 组合。
  • 简化本地开发:开发环境可一键启动、停止,而不需要手动执行 docker run
  • 配置管理:所有容器配置集中管理,环境变量、端口映射、数据卷等一目了然。

Docker Compose示例:

docker-compose.yml 文件中,buildimage 用于定义容器如何获取镜像,它们的使用方式有所不同。build: 用于构建自定义镜像,通常和 Dockerfile 搭配使用,需要提供 Dockerfile 所在目录,Docker Compose 会自动执行 docker build 生成镜像。image: 用于直接拉取现成的镜像(通常来自 Docker Hub 或私有仓库)。适用于使用官方镜像或已有的镜像,而不需要自己构建Dockerfile。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 目录结构
my_project/
│── app/ # Flask 应用代码
│ ├── app.py # Flask 主程序
│ ├── requirements.txt # 依赖文件
│ ├── Dockerfile # Flask 应用的 Dockerfile
│── docker-compose.yml # Docker Compose 配置文件


# docker-compose.yml 文件
version: "3.8" # 指定 Docker Compose 版本

services:
web: # 定义 Flask Web 服务
build: ./app # 使用 app 目录中的 Dockerfile 构建镜像
ports:
- "5000:5000" # 绑定宿主机的 5000 端口到容器的 5000 端口
depends_on:
- db # 依赖数据库服务,确保数据库先启动
- redis # 依赖 Redis 缓存服务
environment: # 设置环境变量
DATABASE_URL: "postgresql://user:password@db:5432/mydatabase"
REDIS_URL: "redis://redis:6379/0"

db: # PostgreSQL 数据库服务
image: postgres:13 # 直接使用官方 PostgreSQL 13 镜像
restart: always # 容器异常退出时自动重启
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydatabase
volumes:
- pgdata:/var/lib/postgresql/data # 数据存储到 Docker 卷

redis: # Redis 缓存服务
image: redis:latest # 使用官方 Redis 最新版镜像
restart: always # 确保 Redis 服务一直运行

volumes:
pgdata: # 定义数据库存储卷,防止容器删除后数据丢失


# app/Dockerfile 文件
# 使用 Python 3.9 作为基础镜像
FROM python:3.9

# 设置工作目录
WORKDIR /app

# 复制 Flask 应用的所有文件到容器
COPY . /app/

# 安装 Flask 依赖
RUN pip install --no-cache-dir -r requirements.txt

# 运行 Flask 应用
CMD ["python", "app.py"]

# app/app.py
from flask import Flask
import psycopg2
import redis
import os

app = Flask(__name__)

# 连接 PostgreSQL 数据库
db_url = os.getenv("DATABASE_URL")
conn = psycopg2.connect(db_url)

# 连接 Redis
redis_url = os.getenv("REDIS_URL")
cache = redis.Redis.from_url(redis_url)

@app.route("/")
def hello():
return "Hello, Docker Compose!"

if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)

使用dockerfile的docker run 只启动一个单独的容器,通常需要手动指定镜像、端口、环境变量等;docker-compose up 可以同时启动多个容器,并且自动管理它们的依赖关系(如 Web 服务依赖数据库),所有服务都在 docker-compose.yml 文件中定义,不需要手动运行多个 docker run 命令。

Docker常用命令

常用参数:

  • -d:后台运行
  • -it:交互模式,让用户可以与容器内的进程交互,比如运行 bash 进入容器并执行命令
  • --name:给容器指定名称
  • -p 主机端口:容器端口:映射端口
  • -v 宿主机目录:容器目录:挂载目录
  • /bin/bash: /bin/bash 是进入 Ubuntu 镜像中的 Shell 解释器,允许用户在容器内执行命令。

版本与帮助命令

命令 作用
docker -v 查看docker版本
docker version 查看详细的 Docker 版本信息
docker info 查看 Docker 运行状态、存储驱动等信息
docker help 显示 Docker 帮助信息
docker <command> --help 查看某个具体命令的帮助

镜像管理

命令 作用
docker images 查看本地镜像
docker build -t <镜像名>:<标签> . 根据 Dockerfile 构建 Docker 镜像,”.”表示根据当前目录作为构建上下文
docker search <镜像名> 在 Docker Hub 搜索镜像
docker pull <镜像名>:<标签> 下载镜像(默认 latest)
docker rmi <镜像ID> 删除本地镜像
docker tag <本地镜像ID> <仓库名>:<标签> 给本地镜像打标签
docker push <仓库名>:<标签> 上传镜像到 Docker Hub
docker history <镜像名> 查看镜像的构建历史
docker inspect <镜像名> 查看镜像详细信息

容器管理

命令 作用
docker ps 查看运行中的容器
docker ps -a 查看所有容器(包括已停止)
docker run <镜像名> 运行一个容器
docker run -d <镜像名> 后台运行容器
docker run -it <镜像名> /bin/bash 以交互模式运行容器
docker start <容器ID> 启动已停止的容器
docker stop <容器ID> 停止容器
docker restart <容器ID> 重启容器
docker rm <容器ID> 删除容器
docker logs <容器ID> 查看容器日志
docker inspect <容器ID> 查看容器详细信息
docker top <容器ID> 查看容器内运行的进程
docker stats <容器ID> 查看容器资源占用情况
docker commit <容器ID> <新镜像名> 将容器保存为新镜像
1
2
3
4
5
6
# 示例
docker run -it ubuntu:20.04 /bin/bash # 运行 Ubuntu 并进入 shell
docker run -d --name my_app -p 8080:80 nginx # 运行 Nginx 并映射端口
docker ps -a # 查看所有容器(包括已停止)
docker stop my_app # 停止名为 my_app 的容器
docker rm my_app # 删除容器

进入容器

命令 作用
docker exec -it <容器ID> /bin/bash 进入正在运行的容器
docker attach <容器ID> 连接到容器的终端
docker cp <容器ID>:<容器内路径> <宿主机路径> 从容器拷贝文件到宿主机
docker cp <宿主机路径> <容器ID>:<容器内路径> 从宿主机拷贝文件到容器

数据卷(持久化存储)

命令 作用
docker volume ls 查看所有数据卷
docker volume create <卷名> 创建数据卷
docker volume rm <卷名> 删除数据卷
docker volume inspect <卷名> 查看数据卷详细信息
docker run -v <卷名>:<容器路径> <镜像名> 绑定数据卷到容器

网络

命令 作用
docker network ls 查看所有网络
docker network create <网络名> 创建网络
docker network rm <网络名> 删除网络
docker network inspect <网络名> 查看网络信息
docker network connect <网络名> <容器名> 连接容器到指定网络
docker network disconnect <网络名> <容器名> 断开容器的网络连接

docker compose

命令 作用
docker-compose up 启动所有服务
docker-compose up -d 以后台方式启动
docker-compose down 停止并删除所有容器
docker-compose ps 查看 Compose 管理的容器
docker-compose logs 查看日志
docker-compose build 构建镜像
docker-compose restart 重启服务
docker-compose stop 只停止不删除容器
docker-compose start stop命令停止的容器再重新启动

牛角尖

  1. 使用dockerfile构造镜像时,引用的一些基础镜像(如 MySQL、Redis、Python 等)已经包含了它们所依赖的操作系统环境,不需要再单独指定操作系统,基础镜像会提供它们所需要的基础的操作系统。每个基础镜像都已经包含了其需要的操作系统环境和相关的依赖库。比如 python:3.9-slim 会包含一个精简的操作系统和 Python,mysql:8.0 会包含一个操作系统和 MySQL。当选择一个基础镜像时,实际上已经选择了操作系统环境,这样你就不需要手动指定操作系统了。后续操作中即便没有明确指定操作系统,容器内部仍然是有文件系统的(由基础镜像提供),并且可以在这个文件系统中创建和操作目录。WORKDIR 是用来设置在容器内的文件操作目录的。

  2. 使用COPY将文件从主机复制到镜像工作目录WORKDIR时,COPY . /app 表示将宿主机(构建 Docker 镜像的机器)当前目录下的文件(例如 app.pyrequirements.txt 等)复制到容器的 /app 目录;当用户构建镜像时,COPY 指令已经将宿主机上的文件复制到镜像的指定工作目录上了。在构建镜像的过程中,这些文件会成为镜像的一部分。因此,其他人在使用这个镜像时,镜像中已经包含了这些文件,不需要再手动复制。当我构建好一个镜像时,镜像就相当于一个“只读”模板。镜像内部包含了操作系统(如果使用了基础镜像的话)、所有应用、依赖、环境变量以及通过 COPY 指令复制的文件。

  3. 容器是镜像的运行实例。当我启动容器时,它会基于镜像创建一个“可写”的文件系统,并执行我在 Dockerfile 中指定的命令(如 CMDENTRYPOINT)。所以容器内部已经包含了所有在镜像构建过程中复制进去的文件。如CMD ["python", "app.py"],当容器启动时,它会在 /app 目录下执行 python app.py