40分钟的Docker实战攻略

发布于:2025-09-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

本文在参考 Bilibili UP主“技术爬爬虾”相关视频的基础上,补充并扩展了一些内容。

一:什么是Docker

(1)基本概念

Docker 是一种开源的 容器化平台,用于快速构建、部署和运行应用程序。它通过将应用程序及其依赖项打包到轻量级的、可移植的容器中,实现了环境一致性,解决了“在我机器上能运行,但在其他环境不行”的问题。其最核心思想是

  • 标准化 (Standardization):Docker 通过引入镜像 (Image) 的概念,将应用程序及其所有依赖项(代码、运行时环境、系统工具、库等)打包成一个独立的、可执行的单元。这个镜像在任何支持 Docker 的环境中都能以相同的方式运行,确保了“一次构建,随处运行 (Build once, run anywhere)”的承诺。这解决了传统环境中“在我机器上能跑”但在其他环境就出问题(“依赖地狱”或“环境差异”)的痛点。

  • 隔离 (Isolation):Docker 利用 Linux 内核的特性(主要是 cgroupsnamespaces),为每个应用程序创建了一个独立的、轻量级的运行环境,称为容器 (Container)。容器之间相互隔离,互不影响,即使一个容器出现问题,也不会影响到其他容器或宿主机。这种隔离性使得应用程序的部署更加安全可靠,也简化了依赖管理。

image.png

Docker解决的问题

  • 环境一致性问题:通过镜像和容器,保证了开发、测试、生产环境的一致性,消除了“在我机器上能跑”的问题。

  • 依赖管理复杂性:将应用程序及其所有依赖打包在容器内,简化了依赖管理,避免了版本冲突。

  • 部署效率低下:容器的快速启动和轻量级特性使得应用程序的部署和扩展变得非常高效。

  • 资源利用率低:相比于虚拟机,容器共享宿主机的操作系统内核,更加轻量级,启动速度更快,占用资源更少,提高了服务器的资源利用率。

  • 应用隔离性不足:容器提供了进程级别的隔离,增强了应用的安全性,避免了应用间的相互影响。

  • 微服务架构的流行:Docker 是微服务架构的理想载体,每个微服务都可以独立地打包成一个容器,便于部署、扩展和管理。

(2)关键术语

镜像(Image):

  • 定义:镜像是一个只读的模板,包含了运行应用程序所需的所有文件系统内容、环境变量、以及启动应用程序的命令。可以理解为应用程序及其运行环境的“快照”或“蓝图”。

  • 分层存储

    :镜像是通过

    联合文件系统 (Union File System)

    技术分层构建的。每一层都代表了对文件系统的一次修改。这种分层存储的优势在于:

    • 复用性:多给镜像可以共享相同的基础层,节省存储空间。

    • 快速构建:当修改镜像时,只需要重新构建修改的层,而不是整个镜像。

    • 版本控制:每一层都可以看作是文件系统的历史记录,便于回溯和管理。

  • Registry (仓库)Docker Hub 是官方的公共镜像仓库,用户可以在其中查找、下载和分享镜像。企业也可以搭建私有仓库来管理内部镜像。Docker Hub Container Image Library | App Containerization

容器(Container):

  • 定义:容器是镜像的一个运行实例。当镜像被启动时,就会创建一个容器。容器是轻量级、可移植、自给自足的,并且与其他容器和宿主机隔离。

  • 生命周期:容器拥有自己的生命周期,可以启动、停止、暂停、重启和删除。

  • 读写层:容器在运行时会在镜像的顶层添加一个可写的读写层。所有对容器内文件系统的修改都会记录在这个读写层上,而不会修改底层镜像。这也是容器可以被删除而不会影响镜像的原因。

Dockerfile:

  • 定义:Dockerfile 是一个文本文件,包含了构建 Docker 镜像所需的所有指令。它是一种自动化构建镜像的方式。

  • 作用:通过编写 Dockerfile,开发者可以清晰地定义应用程序的环境、依赖和启动方式,从而实现镜像构建的自动化、可重复和版本化。

Docker Engine:

  • 定义:Docker Engine 是 Docker 平台的核心组件,负责构建和运行容器。它是一个客户端-服务器 (Client-Server) 架构的应用程序。

  • 组成

    • Docker Daemon (守护进程):运行在宿主机上的后台服务,负责管理 Docker 对象(镜像、容器、网络、数据卷)。

    • Docker CLI (命令行接口):用户与 Docker Daemon 交互的工具,通过命令行发送指令给 Daemon。

    • REST API:Docker Daemon 提供 REST API 接口,允许其他程序或工具与 Docker 进行交互。

二:Docker安装

注意

  • Docker 容器技术最初就是为 Linux 设计的,因此在 Linux 系统上运行 Docker 具有最高的原生性能和最少的抽象层。这意味着容器的启动速度更快,资源占用更少,效率更高。

  • 由于 Docker 容器的核心技术依赖于 Linux 内核特性,Windows 上的 Docker(Docker Desktop)并不是直接在 Windows 内核上运行 Linux 容器。它主要通过WSL 2(Windows Subsystem for Linux 2) 实现的

下文以Linux安装为例,Windows可自行查阅

 # 下载安装脚本
 curl -fsSL https://get.docker.com -o install-docker.sh
 ​
 # 执行脚本
 sudo sh install-docker.sh
 ​
 # 验证是否安装成功
 docker --version
 docker run hello-world

image.png

然后安装docker desktop或者安装portainer管理docker资源,以portainer为例

 # 拉取
 docker pull portainer/portainer-ce:latest
 # 或
 docker pull registry.cn-hangzhou.aliyuncs.com/portainer/portainer-ce:latest
 ---
 ​
 # Portainer数据目录
 mkdir -p /opt/portainer/data
 ---
 ​
 # 运行 Portainer(绑定 9000 端口)
 docker run -d \
   --name portainer \
   -p 9000:9000 \
   -v /var/run/docker.sock:/var/run/docker.sock \
   -v /opt/portainer/data:/data \
   --restart=always \
   portainer/portainer-ce:latest
 ---
 ​
 # 访问
 http://IP:9000

image.png

三:使用Docker

(1)镜像

A:拉取镜像

官网仓库

image.png

国内某些原因无法正常访问,所以需要设置镜像源(Linux)

 # 编辑配置文件
 sudo vi /etc/docker/daemon.json
 ​
 # 输入源
 {
     "registry-mirrors": [
         "https://docker.m.daocloud.io",
         "https://docker.1panel.live",
         "https://hub.rat.dev"
     ]
 }
 ​
 # 重启docker
 sudo service docker restart

docker pull:当你在本地需要使用一个 Docker 镜像来创建容器时,如果本地没有这个镜像,Docker 会自动尝试拉取。但你也可以提前使用g该命令手动下载镜像,以便后续使用

基本语法:

 docker pull [OPTIONS] NAME[:TAG|@DIGEST]
  • NAME: 镜像的名称。通常包含注册表地址(默认为 docker.io/library/,即 Docker Hub 的官方镜像)、用户名/组织名(对于非官方镜像)和仓库名。例如,ubuntu (等同于 docker.io/library/ubuntu

  • TAG (可选): 镜像的标签,通常代表镜像的版本。如果未指定,默认会拉取 latest 标签的镜像。ubuntu:22.04 (拉取 Ubuntu 22.04 版本)

  • DIGEST (可选): 镜像的 SHA256 摘要。通过摘要拉取可以确保你拉取到的是一个精确的、不可变的镜像版本,这对于确保环境一致性非常有用。ubuntu@sha256:12345abcdef... (拉取指定摘要的 Ubuntu 镜像)

常用选项:

  • -a, --all-tags: 下载指定仓库中的所有标签的镜像。如果你想下载一个镜像仓库(Repository)下的所有版本,这个选项非常有用。例如 docker pull --all-tags alpine

  • --disable-content-trust: 跳过镜像的签名验证。默认情况下,Docker 会进行内容信任验证,以确保下载的镜像没有被篡改。但在某些情况下,你可能需要禁用此验证(请注意潜在的安全风险)。

  • --platform: 设置平台。当镜像支持多种架构时(例如 amd64arm64),你可以指定拉取特定架构的镜像。例如 docker pull --platform=arm64 ubuntu

  • -q, --quiet: 静默模式。只显示镜像 ID,不显示详细的拉取过程信息。

使用示例:

 # 拉取最新版 Ubuntu 镜像(默认 latest 标签)
 docker pull ubuntu
 ​
 # 拉取特定版本的 Nginx 镜像
 docker pull nginx:1.23.0
 ​
 # 从非默认注册表拉取镜像
 docker pull my.private.registry.com/myimage:v1.0
 ​
 # 拉取某个仓库的所有标签镜像
 docker pull --all-tags busybox

B:关于镜像的其他命令

推荐在portainer管理

image.png

相关命令介绍如下

 # 查看镜像
 docker images # 查看所有镜像
 docker images -q # 只显示镜像id
 ​
 # 构建镜像(后面介绍)
 docker build -t my-app:latest . # 需要有Dockerfile
 ​
 # 标记镜像
 docker tag my-app:latest username/my-app:v1.0
 ​
 # 删除镜像
 docker rmi my-app:latest
 ​
 # 推送镜像(后面介绍,需登录)
 docker push username/my-app:v1.0
 ​
 # 离线保存和加载镜像
 docker save -o my-app.tar my-app:latest # 保存镜像到文件
 docker load -i my-app.tar # 手动加载镜像
 ​
 # 查看镜像详细信息
 docker inspect my-app:latest

(2)容器

A:从镜像构建容器

docker run:它的主要作用是从一个 Docker 镜像创建并启动一个新的容器

基本语法:

 docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
  • IMAGE: 你想用来创建容器的 Docker 镜像的名称或 ID。如果本地没有这个镜像,Docker 会首先尝试从镜像仓库(如 Docker Hub)拉取 (pull) 它。

  • COMMAND (可选): 在容器内部要执行的命令。如果省略,Docker 会使用镜像在 Dockerfile 中定义的默认命令。

  • ARG... (可选): 传递给 COMMAND 的参数。

常用选项:

docker run 有非常多的选项来控制容器的行为。这里列举一些最常用且重要的:

  • -d, --detach

    :

    在后台(分离)模式运行容器

    。容器会启动,然后 Docker 会打印出容器 ID 并退出,让你能继续使用终端。这对于运行长时间运行的服务至关重要。

    • 示例: docker run -d nginx (在后台启动一个 Nginx Web 服务器)。

  • -p, --publish host_port:container_port

    :

    将容器的端口发布到宿主机

    。这会将你宿主机上的一个端口映射到容器内部的一个端口,从而使容器中的服务可以通过宿主机访问。

    • 示例: docker run -p 8080:80 nginx (将宿主机 8080 端口映射到容器 80 端口,这样你就可以通过 http://localhost:8080 访问 Nginx)。

  • -it

    :

    交互式和伪终端

    。这是两个选项的组合:

    • -i, --interactive: 即使没有附加到容器,也保持 STDIN 开放。对于需要用户输入的交互式进程至关重要。

    • -t, --tty: 分配一个伪终端。这能让你在容器内部获得一个命令行界面。

    • 示例: docker run -it ubuntu bash (启动一个 Ubuntu 容器并进入其 Bash shell)。

  • --name NAME

    :

    为容器指定一个特定的名称

    。如果你不指定名称,Docker 会生成一个随机名称。使用名称可以方便地后续引用容器(例如,用

     docker stop NAME

    ,

     docker logs NAME

    )。

    • 示例: docker run --name my-web-server -p 8080:80 nginx

  • -v, --volume host_path:container_path

    :

    将宿主机上的路径(目录或文件)挂载到容器内部

    。这对于

    数据持久化

    (数据在容器删除后仍然保留)和轻松地向容器提供配置或应用程序代码至关重要。

    • 示例: docker run -v /my/app/data:/var/lib/mysql mysql (将本地目录挂载到 MySQL 容器中用于数据存储)。

  • --env KEY=VALUE, -e KEY=VALUE

    :

    在容器内部设置环境变量

    。这对于向应用程序传递配置值非常有用。

    • 示例: docker run -e MY_ENV_VAR=hello my-app

  • --rm

    :

    容器退出时自动删除容器

    。这对于临时容器或批处理任务非常有用,可以防止你的系统积累大量已退出的容器。

    • 示例: docker run --rm ubuntu bash -c "echo Hello World"

  • --network NAME

    :

    将容器连接到特定的 Docker 网络

    。这使得同一网络中的容器可以通过名称相互通信。

    • 示例: docker run --network my-app-network my-app

使用示例: 我们希望 Nginx 在后台运行,并且能通过宿主机的 80 端口访问到容器内部的 80 端口(Nginx 默认监听 80 端口)。

首先准备宿主机目录和文件

 # 创建 Nginx 配置和网页内容的根目录
 mkdir -p ~/nginx-data/conf.d
 mkdir -p ~/nginx-data/html
 ​
 # 创建自定义的 Nginx 配置文件
 # 注意:确保文件名为 default.conf,因为 Nginx 通常会加载这个目录下的 .conf 文件
 cat <<EOF > ~/nginx-data/conf.d/default.conf
 server {
     listen       80;
     server_name  localhost;
 ​
     location / {
         root   /usr/share/nginx/html; # 这是容器内部的路径
         index  index.html index.htm;
     }
 }
 EOF
 # 创建自定义的网页内容
 cat <<EOF > ~/nginx-data/html/index.html
 <!DOCTYPE html>
 <html>
 <head>
     <title>我的自定义Nginx页面</title>
     <style>
         body { font-family: Arial, sans-serif; text-align: center; margin-top: 50px; background-color: #f0f0f0; }
         h1 { color: #333; }
         p { color: #666; }
     </style>
 </head>
 <body>
     <h1>欢迎来到我的Docker Nginx!</h1>
     <p>这个页面来自宿主机的自定义文件。</p>
 </body>
 </html>
 EOF

运行命令

 docker run -d \
 -p 80:80 \
 --name my-custom-nginx \
 -v ~/nginx-data/html:/usr/share/nginx/html \
 -v ~/nginx-data/conf.d:/etc/nginx/conf.d \
 --restart always \
 nginx:1.23.0

解释

  • docker run: 启动一个新的 Docker 容器。

  • -d: 让容器在后台运行。这样它就不会霸占你的终端。

  • -p 80:80: 将宿主机的 80 端口映射到容器的 80 端口。这样你就能通过 http://localhost 访问到 Nginx 服务。

  • --name my-custom-nginx: 给容器起一个易于识别的名字,方便后续管理(如 docker stop my-custom-nginx)。

  • -v ~/nginx-data/html:/usr/share/nginx/html: 这是第一个卷挂载。它将你宿主机家目录下的 nginx-data/html 文件夹,映射到容器内部 Nginx 默认的网页文件目录。这意味着你可以把你的 HTML 文件放在宿主机的 ~/nginx-data/html 里,容器会自动使用它。

  • -v ~/nginx-data/conf.d:/etc/nginx/conf.d: 这是第二个卷挂载。它将你宿主机家目录下的 nginx-data/conf.d 文件夹,映射到容器内部 Nginx 加载额外配置文件的目录。这样你可以在宿主机编辑 Nginx 的配置文件。

  • --restart always: 设置重启策略。容器退出或 Docker 服务重启时,Docker 会自动尝试重新启动这个容器。这在生产环境中非常有用。

  • nginx:1.23.0: 使用上面步骤中拉取的镜像

查看运行中的容器

image.png

验证服务(编码问题)

image.png

停止与删除容器(可选)

  • 即使你删除了容器,你宿主机上的 ~/nginx-data 目录及其内容(你的自定义配置和网页文件)仍然会保留,下次你可以用相同的命令再次启动一个 Nginx 容器,它会加载你之前的数据。

 # 停止容器
 docker stop my-custom-nginx
 ​
 # 删除容器
 docker rm my-custom-nginx
 ​
 # 删除正在运行中的容器(强制删除)
 docker rm -f my-custom-nginx

B:Docker挂载卷

之前使用的方式挂载方式为绑定挂载。实际上,Docker 还提供了一种更抽象、更易管理的持久化数据方式,那就是 Docker 卷 (Docker Volume)

Docker卷Docker 卷是 Docker 管理的数据存储区域。它不直接暴露在宿主机文件系统的某个特定路径下(尽管它最终还是存储在宿主机上),而是由 Docker 引擎来创建、管理和维护。

 # 创建一个名为 `nginx_html_data` 的卷,用于存放 Nginx 的网页文件
 docker volume create nginx_html_data 
 docker volume create nginx_conf_data
 ​
 # 查看挂载卷
 docker volume ls
 ​
 # 使用挂载卷
 docker run -d \
 -p 80:80 \
 --name my-nginx-with-volume \
 -v nginx_html_data:/usr/share/nginx/html \  # 使用命名卷挂载网页内容
 -v nginx_conf_data:/etc/nginx/conf.d \     # 使用命名卷挂载配置
 --restart always \
 nginx:latest

注意:当一个空卷挂载到一个非空目录的容器时,容器目录中的内容会被复制到卷中。这意味着,如果你第一次运行 Nginx 容器时,/usr/share/nginx/html 目录里有默认的 Nginx 欢迎页面,这些页面就会被复制到 nginx_html_data 卷中。之后,你对卷的修改都会持久化。

另外,在portainer中也可以管理这部分内容

image.png

C:其他参数介绍

--it:用于进行容器内部进行调试。这里的 -i是指保持 STDIN 打开,即使没有附加到容器,-t是指分配一个伪终端。

 docker run -it --rm ubuntu:latest bash
  • -it: 让你能够进入容器的命令行界面。

  • --rm: 这个参数非常有用!它表示容器退出时自动删除容器。这对于这种一次性交互式任务非常方便,避免留下大量已退出的容器。

  • bash: 这是在容器启动后要执行的命令。它会打开一个 Bash Shell。

--memory--cpus:生产环境中,为容器分配合适的资源非常重要,可以防止单个容器耗尽宿主机资源或相互影响。

  • --memory [SIZE]: 限制容器可以使用的内存大小。支持的单位有 b, k, m, g

  • --cpus [COUNT]: 限制容器可以使用的 CPU 核心数(或核心的份额)。

 docker run -d \
 --name limited-nginx \
 -p 8080:80 \
 --memory 512m \ # 容器最多只能使用 512 兆字节的内存。
 --cpus 0.5 \ # 容器最多只能使用 0.5 个 CPU 核心(即一个核心的一半计算能力)。
 nginx:latest

---env:注入环境变量,环境变量是向容器内的应用程序传递配置信息的一种非常常用和灵活的方式。-e KEY=VALUE, --env KEY=VALUE: 设置容器内的环境变量。

假设你有一个 Node.js 应用程序,它根据 NODE_ENV 环境变量来决定是开发模式还是生产模式。

 docker run --rm -e NODE_ENV=production my-nodejs-app:latest

--network将容器连接到特定的 Docker 网络。这使得同一网络中的容器可以通过名称相互通信。

  • --network host: 容器直接使用宿主机的网络栈,不进行网络隔离。容器会共享宿主机的 IP 地址和端口空间。这在某些高性能场景下有用,但会牺牲隔离性。

 docker run --network my-app-network my-app

--rm:适用于临时运行、测试或一次性任务的容器。容器退出时自动删除

例如,运行一个完成任务后自动清理的容器

 docker run --rm alpine:latest echo "This container will be gone after printing this message!"

D:其他关于容器的命令

 # 查看运行中或所有容器
 docker ps
 docker ps -a
 ​
 # 停止容器
 docker stop [容器名或容器ID]
 ​
 # 开启容器
 docker start [容器名或容器ID]
 ​
 # 重启容器
 docker restart [容器名或容器ID]
 ​
 # 查看容器详细信息
 docker inspect [容器名或容器ID]
 ​
 # 创建容器但不运行
 docker create [容器名或容器ID] # 和docker run参数差不多
 ​
 # 进入容器内部
 docker exec -it [容器名或容器ID] bash

(3)Dockerfile

A:介绍

Dockerfile 是一个文本文件,其中包含了一系列命令和指令,Docker 引擎可以读取这些指令并自动构建 Docker 镜像。其核心作用如下

  • 自动化构建: 无需手动一步步操作,只要有 Dockerfile,Docker 就能自动重复构建出完全相同的镜像。

  • 版本控制: Dockerfile 本身是文本文件,可以像代码一样进行版本控制(例如,使用 Git),方便追踪镜像的变更历史。

  • 可移植性: Dockerfile 定义了镜像的构建过程,确保了在不同环境中构建出一致的镜像。

  • 透明性: 任何查看 Dockerfile 的人都能清楚地知道镜像中包含了什么,以及它是如何构建的。

B:常用指令

Dockerfile 由一行行指令组成,每条指令都构建镜像的一“层”(layer)。当 Dockerfile 中的指令执行完毕后,所有这些层组合起来就形成了一个完整的 Docker 镜像。

FROM(基础镜像): 指定基础镜像,你的新镜像将基于它来构建。这是 Dockerfile 的第一个非注释指令。

 FROM <image>[:<tag>]

例如

 FROM ubuntu:22.04  # 基于 Ubuntu 22.04 构建
 FROM alpine:latest # 基于轻量级的 Alpine Linux 构建

RUN(执行命令): 在镜像构建过程中执行命令行命令。每个 RUN 指令都会在镜像中创建一个新层。

 RUN <command>
 或
 RUN ["executable", "param1", "param2"]

例如

 RUN apt-get update && apt-get install -y \
 nginx \
 curl \
 && rm -rf /var/lib/apt/lists/* # 更新并安装 Nginx 和 curl,然后清理 APT 缓存

COPY(复制文件/目录):构建上下文(通常是 Dockerfile 所在的目录)复制文件或目录到镜像中。

 COPY <src>... <dest>

例如

 COPY . /app      # 将构建上下文中的所有文件和目录复制到镜像的 /app 目录
 COPY myapp.conf /etc/nginx/conf.d/ # 复制单个配置文件

ADD(复制文件/目录,并支持 URL 和解压): 类似于 COPY,但功能更强大。src 可以是一个 URL,Docker 会自动下载文件。如果 src 是一个压缩文件(tar, gzip, bzip2 等),并且 dest 是一个目录,ADD 会自动解压。

  • 尽管 ADD 功能多,但通常推荐使用 COPY,因为它的行为更可预测,不易出错。只有在需要自动解压或从 URL 下载时才考虑 ADD

 ADD <src>... <dest>

例如

 ADD https://example.com/latest.tar.gz /app/ # 下载并解压
 ADD myapp.tar.gz /app/ # 解压本地压缩包

WROKDIR(设置工作目录): 后续的 RUN, CMD, ENTRYPOINT 命令设置工作目录。如果该目录不存在,WORKDIR 会自动创建它。

 WORKDIR /path/to/workdir

例如

 WORKDIR /app # 将 /app 设为工作目录,后续命令都在 /app 下执行
 RUN npm install

EXPOSE(声明暴露端口): 声明容器运行时会监听的端口。这只是一个文档化的声明,并不会自动发布端口。实际的端口映射需要在 docker run 命令中使用 -p 参数。

 EXPOSE <port> [<port>/<protocol>...]

例如

 EXPOSE 80 # 声明容器的 80 端口将会被监听
 EXPOSE 80/tcp 443/tcp # 同时声明 TCP 的 80 和 443 端口

ENV(设置环境变量): 这些变量会在镜像构建过程中以及容器运行时对所有后续指令和应用程序生效。

 ENV <key> <value>
 或
 ENV <key>=<value> ...

例如

 ENV NODE_ENV production # 设置一个环境变量
 ENV PORT 8080

CMD(容器启动时默认执行命令): 指定容器启动时将要执行的默认命令。一个 Dockerfile 中只能有一个 CMD 指令,如果有多条,只有最后一条会生效。

  • 注意:如果在 docker run 命令中指定了命令,CMD 的指令会被覆盖。

 CMD command param1 param2
 或
 CMD ["executable", "param1", "param2"]

例如

 CMD ["nginx", "-g", "daemon off;"] # 容器启动时运行 Nginx
 CMD ["node", "app.js"]             # 容器启动时运行 Node.js 应用

ENTRYPOINT (容器启动时执行的入口点): 供一个容器的入口点ENTRYPOINT 后的命令不会被 docker run 命令行中的命令覆盖,而是将 docker run 命令中的参数作为自身的参数。常用于构建可执行的应用程序镜像,或者将容器作为一个命令来使用。

 ENTRYPOINT ["executable", "param1", "param2"]

需要注意CMD 与 ENTRYPOINT 的组合使用

  • 如果同时存在,CMD 的内容会作为 ENTRYPOINT 的默认参数。

  • 这使得镜像既有一个固定的执行入口,又可以通过 docker run 提供灵活的参数。

例如

 ENTRYPOINT ["/usr/bin/super_app"]
 CMD ["--help"] # 默认执行 /usr/bin/super_app --help
 ​
 # 当运行 docker run my_image --version 时,实际执行的是 /usr/bin/super_app --version

ARG(构建参数): 定义在镜像构建时可以传递的变量。这些变量只在构建过程中可用,不会保留在最终的镜像中作为环境变量(除非你用 ENV 指令将其显式地设为环境变量)。在 docker build 命令中使用 --build-arg 来设置其值

 ARG <name>[=<default value>]

例如

 ARG APP_VERSION=1.0.0 # 定义一个构建参数,并设置默认值
 RUN echo "Building version $APP_VERSION"
 ​
 # 构建时可以这样传递参数:
 # docker build --build-arg APP_VERSION=2.0.0 -t myapp .

C:构建

**创建一个简单的 Dockerfile 来构建一个包含自定义网页内容的 Nginx 镜像

 # 1. 指定基础镜像:使用官方 Nginx 镜像的最新稳定版本
 FROM nginx:stable-alpine
 ​
 # 2. 设置作者信息 (可选,但推荐)
 LABEL maintainer="Your Name <your.email@example.com>"
 ​
 # 3. 将宿主机上的自定义 HTML 文件复制到 Nginx 容器的默认网页目录
 # 假设在 Dockerfile 同级目录下有一个名为 'html' 的文件夹,里面放着你的 index.html 等文件
 COPY html/ /usr/share/nginx/html/
 ​
 # 4. (可选)如果你有自定义的 Nginx 配置文件,也可以复制进去
 # 假设在 Dockerfile 同级目录下有一个名为 'config' 的文件夹,里面放着你的 default.conf 文件
 # COPY config/default.conf /etc/nginx/conf.d/default.conf
 ​
 # 5. 声明容器会监听的端口 (最佳实践,不实际发布端口)
 EXPOSE 80
 ​
 # 6. 设置容器启动时执行的命令 (Nginx 镜像通常自带,这里只是示例)
 # CMD ["nginx", "-g", "daemon off;"]

构建过程

  • 将上述 Dockerfile 内容保存为名为 Dockerfile 的文件。

  • Dockerfile 同级目录下创建一个 html 文件夹,并在其中放入你的 index.html 文件。

  • 打开终端,进入 Dockerfile 所在的目录。

  • 执行构建命令:

    • -t my-custom-nginx-image:1.0: 为构建的镜像指定名称和标签。

    • .: 表示 Dockerfile 和构建上下文在当前目录。

 docker build -t my-custom-nginx-image:1.0 .

D:推送

1:登录Docker镜像仓库。如果你要推送到 Docker Hub,就登录 Docker Hub;如果你要推送到私有仓库,就登录你的私有仓库。

 # 输入后登录Docker Hub
 docker login 
 ​
 # 推送私有仓库
 docker login your.private.registry.com

2:Docker 镜像的标签 (tag) 非常重要,它决定了镜像推送时会去哪个仓库,以及在仓库中的名称和版本。一个完整的镜像标签通常包含 [仓库地址/][用户名或组织名/]镜像名称[:标签]

  • 仓库地址/ (可选):如果你要推送到 Docker Hub 的个人仓库,通常可以省略。但如果推送到私有仓库或 Docker Hub 的组织仓库,就需要包含。

  • 用户名或组织名/ (必需):对于推送到 Docker Hub 上的个人或组织仓库,这是必需的,它决定了镜像归属于谁。

  • 镜像名称:镜像的名称。

  • ::分隔符。

  • 标签:通常是版本号(例如 1.0, latest, dev)。

使用 docker tag打标签,语法如下

 docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

例如

 docker tag my-custom-nginx-image:1.0 yourusername/my-custom-nginx-image:v1.0

3:使用 docker push推送镜像

 docker push NAME[:TAG]

例如

 docker push yourusername/my-custom-nginx-image:v1.0

(4)Docker网络

A:介绍

Docker 通过网络驱动来实现不同的网络功能。当你运行一个容器时,如果没有明确指定网络,它通常会连接到 Docker 默认创建的某个网络。

  • 网络隔离:每个 Docker 容器都拥有自己的独立网络栈,包括 IP 地址、网络接口、路由表等。这意味着容器在网络层面是相互隔离的,除非你明确地将它们连接起来或进行端口映射。

  • DOS服务发现:在同一个 Docker 网络中的容器,可以通过容器名服务名进行相互通信,而不是必须使用 IP 地址。Docker 内置了 DNS 服务,可以自动解析这些名称到对应的 IP 地址。这极大地简化了多容器应用的配置。

image.png

image.png

B:网络类型

bridge (桥接网络 - 默认和最常用): 当你安装 Docker 时,它会默认创建一个名为 bridge(或 docker0)的虚拟网桥。所有未指定网络的容器默认都会连接到这个桥接网络。每个连接到桥接网络的容器都会获得一个私有 IP 地址,它们可以相互通信。

image.png

容器可以通过端口映射 (-p 参数) 将其内部端口暴露给宿主机。宿主机以及外部网络可以通过宿主机的 IP 地址和映射的端口访问容器内的服务。

自定义 bridge 网络 (推荐): 虽然 Docker 有一个默认的 bridge 网络,但强烈建议你创建自己的自定义桥接网络。这是因为

  • 更好的隔离: 将你的应用程序容器放在一个独立的网络中,与默认网络中的其他无关容器隔离。

  • 自动 DNS 服务发现(非常好用): 在自定义桥接网络中,容器可以通过容器名称互相解析和通信。这是默认桥接网络不具备的功能(需要使用 --link,但 --link 已被弃用)。

  • 更易管理: 清晰地组织你的容器网络。

命令如下

 # 新建
 docker network create my-app-network
 ​
 # 删除
 docker network rm my-app-network

使用方法如下

  • web 容器可以直接通过 db 这个名字访问到 db 容器,反之亦然。

 docker run -d --name web --network my-app-network -p 80:80 my-web-image
 docker run -d --name db --network my-app-network my-db-image

host模式: 容器直接使用宿主机的网络栈,不进行任何网络隔离。容器将共享宿主机的 IP 地址和端口空间。这种方式对网络性能有极高要求,且容器隔离性不那么重要的特殊场景,例如一些网络监控工具或高性能代理。

  • 优点:提供了最好的网络性能,因为没有额外的网络虚拟化层。

  • 缺点

    • 安全性低: 容器不再与宿主机网络隔离,可能带来安全风险。

    • 端口冲突: 如果宿主机上已经有服务使用了某个端口,容器就不能再使用相同的端口。

使用方法如下

  • 此时,如果 my-app-image 在容器内部监听 8000 端口,那么你直接访问宿主机的 8000 端口就能访问到它。

 docker run -d --name my-app-host-mode --network host my-app-image

none(无网络): 容器内部没有网络接口,除了 localhost。当你不需要容器有任何网络连接时,或者你想完全手动配置容器网络时可以使用它,通常用于安全性极高或特殊测试的场景。

使用方法如下

  • 进入容器后,你无法 ping 任何外部地址,也无法访问互联网。

 docker run -it --network none ubuntu bash

(5)docker compose

Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。简单来说,如果你需要同时运行多个相互关联的 Docker 容器(比如一个 Web 服务器、一个应用服务器和一个数据库),那么 Docker Compose 就能派上用场了。它让你能用一个 YAML 文件来配置你的应用服务,然后通过一个命令来启动、停止和管理所有这些服务

注意:从 Docker Engine v20.10.0 开始,Docker 官方提供了 docker compose(注意没有 - 短横线)作为 CLI 插件,直接集成在 Docker 里,无需额外安装

A:介绍

docker-compose.yaml文件: 这是 Docker Compose 的核心。它是一个 YAML 格式的配置文件,用于定义应用程序的服务、网络和数据卷。在该文件中

  • services (服务):应用程序的各个组件,例如 webappdb 等。每个服务都对应一个 Docker 容器。你可以指定每个服务使用的镜像、构建方式、端口映射、数据卷、环境变量等。

  • networks (网络):定义容器之间用于通信的自定义网络。Compose 会为你的应用创建一个默认网络,但你也可以定义自己的网络以便更好地隔离或组织服务。

  • volumes (数据卷):定义用于持久化数据的命名卷。

命令:

  • 启动所有服务: docker-compose up-d后台运行,-f指定配置文件)

  • 停止所有服务: docker-compose down

  • 构建服务镜像: docker-compose build

  • 查看服务状态: docker-compose ps

  • 查看服务日志: docker-compose logs

B:案例分析

项目介绍:构建一个PHP-Mysql项目,该项目结构如下

image.png

1:编写文件

docker-compose.yaml

 services:
   nginx:
     # 直接使用官方Nginx镜像,不再需要单独的 Dockerfile-nginx
     image: nginx:latest
     container_name: php-nginx
     ports:
       - "80:80" # 将宿主机80端口映射到容器80端口
     volumes:
       - ./public:/var/www/html # 挂载PHP应用代码
       - ./conf/nginx.conf:/etc/nginx/nginx.conf # 挂载自定义 Nginx 配置
     depends_on:
       - php # 依赖php服务,确保php-fpm启动后nginx才启动
     networks:
       - app-network
 ​
   php:
     build:
       context: .
       dockerfile: Dockerfile-php # 自定义的PHP Dockerfile
     container_name: php-fpm
     volumes:
       - ./public:/var/www/html # 挂载PHP应用代码
     networks:
       - app-network
 ​
   mysql:
     image: mysql:5.7 # 使用官方的 MySQL 5.7 镜像
     container_name: php-mysql-db
     environment:
       MYSQL_ROOT_PASSWORD: mysecretpassword # 设置root用户密码
       MYSQL_DATABASE: mydatabase            # 默认数据库名称
       MYSQL_USER: myuser                    # 默认用户
       MYSQL_PASSWORD: mypassword            # 默认用户密码
     volumes:
       - mysql_data:/var/lib/mysql # 挂载数据卷实现MySQL数据持久化
     networks:
       - app-network
 ​
 networks:
   app-network: # 定义一个bridge网络,让所有服务能互相通信
     driver: bridge
 ​
 volumes:
   mysql_data: # 定义一个命名卷用于MySQL数据持久化

Dockerfile-php(PHP-FPM 容器的 Dockerfile)

 FROM php:7.4-fpm-alpine 
 ​
 # 安装 PHP 扩展和常用工具 (使用 Alpine 的 apk 包管理器)
 # --no-cache 选项可以在安装后移除apk缓存,进一步减小镜像大小
 RUN apk add --no-cache \
     php-mysqli \      # MySQLi 扩展用于数据库连接
     php-pdo_mysql \   # PDO MySQL 驱动
     php-gd \          # 图像处理,如果你的应用需要
     php-mbstring \    # 多字节字符串支持
     php-xml \         # XML 处理
     # 如果需要其他工具,比如 git 或常用的调试工具,可以在这里安装
     # bash \
     # git \
     # nano \
     && rm -rf /var/cache/apk/* # 清理 apk 缓存
 ​
 # 配置 PHP-FPM 用户和组,与 Nginx 容器内用户匹配
 RUN sed -i 's/listen = 127.0.0.1:9000/listen = 9000/' /usr/local/etc/php-fpm.d/www.conf \
 && sed -i 's/;listen.owner = nobody/listen.owner = www-data/' /usr/local/etc/php-fpm.d/www.conf \
 && sed -i 's/;listen.group = nobody/listen.group = www-data/' /usr/local/etc/php-fpm.d/www.conf
 ​
 # 暴露 PHP-FPM 端口 (Docker Compose 内部通信会用到)
 EXPOSE 9003
 ​
 # 定义工作目录
 WORKDIR /var/www/html
 ​
 # 容器启动时运行的命令 (php-fpm 镜像通常已内置,如果需要自定义行为再写)
 CMD ["php-fpm", "-F"]

conf/nginx.conf

 # conf/nginx.conf
 user www-data; # 确保与 PHP-FPM 容器中 user = www-data 匹配
 worker_processes  1;
 ​
 error_log  /var/log/nginx/error.log warn;
 pid        /var/run/nginx.pid;
 ​
 events {
     worker_connections  1024;
 }
 ​
 http {
     include       /etc/nginx/mime.types;
     default_type  application/octet-stream;
 ​
     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                       '$status $body_bytes_sent "$http_referer" '
                       '"$http_user_agent" "$http_x_forwarded_for"';
 ​
     access_log  /var/log/nginx/access.log  main;
 ​
     sendfile        on;
     #tcp_nopush     on;
 ​
     keepalive_timeout  65;
 ​
     #gzip  on;
 ​
     server {
         listen       80;
         server_name  localhost;
         root   /var/www/html; # Nginx 默认的网页文件根目录
 ​
         # 配置 PHP-FPM
         location ~ \.php$ {
             try_files $uri =404; # 如果PHP文件不存在,返回404
             fastcgi_pass   php:9003; # 关键:通过php服务名和9003端口连接php-fpm
             fastcgi_index  index.php;
             fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
             include        fastcgi_params;
         }
 ​
         # 任何非 PHP 文件直接提供服务
         location / {
             try_files $uri $uri/ /index.php?$query_string;
         }
 ​
         error_page   500 502 503 504  /50x.html;
         location = /50x.html {
             root   /usr/share/nginx/html;
         }
     }
 }

public/index.php(PHP 应用程序入口文件)

 <?php
 // public/index.php
 ​
 echo "<h1>Welcome to PHP in Docker!</h1>";
 echo "<p>PHP Version: " . phpversion() . "</p>";
 ​
 // 数据库连接配置
 $servername = "mysql"; // 在 Docker Compose 网络中,服务名即为主机名
 $username = getenv('MYSQL_USER') ?: "myuser"; // 从环境变量获取,或使用默认值
 $password = getenv('MYSQL_PASSWORD') ?: "mypassword";
 $dbname = getenv('MYSQL_DATABASE') ?: "mydatabase";
 ​
 // 创建数据库连接
 $conn = new mysqli($servername, $username, $password, $dbname);
 ​
 // 检查连接
 if ($conn->connect_error) {
     echo "<p style='color: red;'>Database Connection Failed: " . $conn->connect_error . "</p>";
 } else {
     echo "<p style='color: green;'>Successfully connected to MySQL database!</p>";
 ​
     // 尝试执行一个简单的查询
     $sql = "SELECT 1 as test_col";
     $result = $conn->query($sql);
 ​
     if ($result) {
         $row = $result->fetch_assoc();
         echo "<p>Test Query Result: " . $row['test_col'] . "</p>";
     } else {
         echo "<p style='color: orange;'>Test Query Failed: " . $conn->error . "</p>";
     }
 ​
     $conn->close();
 }
 ​
 ?>

2:构建运行

构建

 docker-compose up --build -d

image.png

本文全面概述了 Docker 这一 容器化平台 的核心概念与实践。本文首先介绍了Docker如何通过 镜像 实现应用程序及其依赖的标准化封装,以及如何利用 容器 提供轻量级、隔离的运行环境,从而解决环境一致性、依赖管理和部署效率等痛点。接着,资料详细阐述了 镜像、容器、Dockerfile 和 Docker Engine 等关键术语的定义与作用,并提供了在Linux环境下安装和使用Docker的指导。此外,资料还深入探讨了 Docker网络 的类型及其重要性,特别是自定义桥接网络的优势,最后介绍了 Docker Compose 作为管理多容器应用的强大工具。