> ## Content Index
> Fetch the complete content index at: https://www.richarvin.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Docker学习笔记（高阶篇）一
- URL: https://www.richarvin.com/docker-xue-xi-bi-ji-gao-jie-pian-yi/
- Published: 2024-03-12T03:50:41.000Z
- Updated: 2024-05-22T01:23:05.000Z
- Author: Richarvin
- Tags: 编程技术

## 一、Docker Dockerfile

---

Docker Dockerfile是一个文本文件，包含了一系列的指令和参数，用于自动化构建Docker镜像。每条指令都构建镜像的一层，每一层都代表了镜像构建过程中的一个步骤。该层包含了该指令执行后的所有更改，这些层最终会叠加起来形成最终的镜像。

### 镜像层的工作原理

假设我们有一个非常基础的Dockerfile：

```bash
FROM ubuntu:18.04
RUN apt-get update && apt-get install -y nginx
COPY . /var/www/html

```

这个Dockerfile包含了三条指令，每条指令对应镜像的一层：

- FROM ubuntu:18.04：这是基础镜像层，所有后续的操作都基于这个Ubuntu 18.04的镜像。
- RUN apt-get update && apt-get install -y nginx：这一条指令创建了第二层。这层在Ubuntu 18.04的基础上，更新了软件包列表并安装了nginx。这一层只包含了由于安装nginx而产生的改变。
- COPY . /var/www/html：这一条指令创建了第三层。它把宿主机当前目录下的文件复制到了镜像的/var/www/html目录中。这层包含了由于复制文件操作产生的所有改变。

### 层的重用和缓存

当Dockerfile被重新构建时，如果FROM指令指定的基础镜像没有变化，且RUN指令执行的安装命令没有变化，那么Docker会使用缓存的镜像层，而不是重新创建。这大大加快了构建过程。

但是如果你修改了COPY指令复制的文件内容（哪怕是一个小小的改变），Docker需要重新执行这条指令以及所有后续的指令，因为这会影响到/var/www/html目录的内容，从而影响到这一层以及所有基于这一层的后续层。

### 层的效率

Docker镜像的这种分层结构，使得镜像的分发非常高效。如果多个镜像都是基于相同的Ubuntu 18.04基础镜像，那么这个基础镜像只需要在一个节点上存储和缓存一次，不同的镜像只需存储它们自己特有的层。

## 二、Dockerfile常用指令

---

截止到发文当天，Dockerfile支持18个常用字的保留指令：

![](https://picture.richarvin.com/202403/xnip2024-03-12_10-07-43.webp)

指令列表

**1.FROM**：指定基础镜像。所有Dockerfile都必须以FROM指令开始，指定一个已存在的镜像作为构建新镜像的基础：

```dockerfile
FROM ubuntu:18.04
```

**2.RUN**：在镜像中运行命令。用于安装软件包或执行其它配置任务:

```dockerfile
RUN apt-get update && apt-get install -y python3
```

**3.CMD**：提供容器默认执行的命令。Dockerfile中只能有一个CMD指令。如果定义了多个，只有最后一个会被执行:

```dockerfile
CMD ["python3", "./app.py"]
```

4.**COPY**：将主机的文件或目录复制到镜像中。常用于复制源代码到镜像内:

```dockerfile
COPY . /app
```

5.**ADD**：与COPY类似，但是它还支持远程URL和自动解压压缩文件的功能:

```dockerfile
ADD https://example.com/big.tar.gz /var/www/html/
```

6.**EXPOSE**：声明容器运行时监听的端口:

```dockerfile
EXPOSE 80
```

7.**ENV**：设置环境变量:

```dockerfile
ENV MYSQL_ROOT_PASSWORD password
ENV MYSQL_DATABASE my_database
ENV MYSQL_USER my_user
ENV MYSQL_PASSWORD my_password
```

**8.ENTRYPOINT**：配置容器启动时运行的命令，与CMD配合使用，可以让容器以应用程序或服务的形式运行：

```dockerfile
ENTRYPOINT ["python3"]
CMD ["app.py"]
```

<!DOCTYPE html> 

[官方配置参数说明](https://docs.docker.com/reference/dockerfile/?ref=richarvin.com#arg) 

当你的dockerfile文件编辑好后，切换到dockerfile文件所在目录下构建镜像：

```bash
docker build -t your-image-name:tag .
```

使用`docker run -d`命令来运行该镜像即可。

## 二、Docker Compose

---

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose，您可以使用 YML 文件来配置应用程序需要的所有服务。然后，使用一个命令，就可以从 YML 文件配置中创建并启动所有服务。

### 使用Compose的步骤

1. **编写Dockerfile**：这是创建自定义Docker镜像的步骤，你会在这个文件中定义基础镜像、安装必要的软件包、配置环境、复制应用程序文件等步骤。这个步骤是为了创建一个能够满足你特定需求的镜像。
2. **编写`docker-compose.yml`文件**：在这个文件中，定义你的应用所需的服务、网络和卷。这包括使用哪些镜像（无论是自定义镜像还是直接从Docker Hub拉取的镜像）、如何配置容器之间的网络、容器需要挂载哪些卷等。
3. **运行`docker-compose up -d`命令**：这一步会根据你在`docker-compose.yml`文件中定义的配置启动和运行你的应用。`-d`参数表示在后台运行容器。
4. **通过`docker-compose down`进行清理**：用于停止并删除根据`docker-compose.yml`文件创建的所有容器，网络和默认创建的卷。

在Docker中，使用`docker-compose up -d`命令时并不一定需要有一个自己创建的`Dockerfile`文件。例如下面是一个基于`nginx`官方镜像启动一个简单web服务器的`docker-compose.yml`示例，这里并不需要`Dockerfile`：

```yml
version: '3'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    volumes:
      - ./html:/usr/share/nginx/html
```

在这个示例中，`image: nginx:latest`指示Docker使用已有的最新的`nginx`官方镜像。因此只要你有这个`docker-compose.yml`文件，就可以使用`docker-compose up -d`命令来启动服务，无需自己编写`Dockerfile`。

但是如果你想要构建一个自定义的镜像（例如，你有特定的环境配置或者应用程序需要包含在镜像中），那么你就需要自行创建一个`Dockerfile`来指定如何构建这个镜像，并在`docker-compose.yml`文件中使用`build:`指令指向包含`Dockerfile`的目录。

### Docker Compose文件格式（`docker-compose.yml`）

Compose 文件是一个YAML文件，其中定义了所有相关的服务设置。一个服务通常表示运行一个容器。服务可以包括应用的数据库、web服务、前端服务等。

Compose文件的结构大致如下：

```yaml
version: '3'

services:
  db:
    image: postgres
    volumes:
      - db-data:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: exampledb
      POSTGRES_USER: exampleuser
      POSTGRES_PASSWORD: examplepass
  
  web:
    build: .
    command: python app.py
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

volumes:
  db-data:

```

在这个例子中：

- `db`服务使用了`postgres`镜像，并定义了一个卷`db-data`来持久化数据库数据。
- `web`服务通过`build: .`指令构建当前目录下的Dockerfile，并且映射了端口8000到容器的端口8000。
- `depends_on`指令确保在启动`web`服务之前数据库`db`服务已经在运行。

这样定义了服务后，使用`docker-compose up -d`命令就可以一起启动这两个服务。它们会在同一个网络中创建，`web`服务能够通过数据库服务名称`db`来访问数据库服务。

<!DOCTYPE html> 

[docker compose版本查询](https://docs.docker.com/compose/intro/history?ref=richarvin.com) 

![](https://picture.richarvin.com/202403/xnip2024-03-12_11-48-16.webp)

docker-compose 指令列表

## 三、总结

---

- Dockerfile：它的作用是定义如何从一个基础镜像开始，通过一系列步骤构建出一个新的镜像。这些步骤可能包括安装软件包、设置环境变量、复制应用程序代码到镜像中等。Dockerfile关注的是镜像的创建，是构建过程的一张清单说明。
- docker-compose.yml：这个文件则用于定义和运行基于Docker的应用。它允许你用YAML格式配置应用的服务、网络和卷，这些服务可以基于你通过Dockerfile构建的镜像，也可以是直接从Docker Hub或其他Docker镜像仓库拉取的现成镜像。docker-compose.yml关注的是如何将一个或多个镜像组织成完整的应用，处理服务之间的关系、网络连接和数据存储。

所以**`Dockerfile`** 是关于**构建**镜像的，“怎样做”这个镜像；而 **`docker-compose.yml`** 是关于**部署和管理**这些镜像作为服务的，即如何把这些镜像“运作起来”，使它们共同作为一个整体服务运行。

docker Compose 虽然是官方提供的容器编排工具，但是实际生产环境是不用的，因为其局限性很大，这只适合单机多容器部署进行本地开发环境、测试环境以及小型的生产部署。但并不直接支持跨多个主机或管理一个容器集群。对于生产环境中的容器编排和集群管理，更倾向于使用像Kubernetes这样的系统。因为Kubernetes提供了更强大的功能，能够处理跨主机的容器编排、自动扩展、服务发现、负载均衡、自我修复等复杂功能，这些是Docker Compose所不具备的。