Docker Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。
它允许用户通过一个 YAML 文件来配置应用程序的服务,这个文件称为 docker-compose.yml。
使用 Docker Compose,您可以轻松地管理多个 Docker 容器的部署,包括启动、停止和重启服务,以及查看运行状态等。
基本组成
- Services:在 docker-compose.yml 文件中,可以定义多个服务,每个服务都将运行在一个或多个容器中。
- Networks:可以为服务指定网络,以控制容器之间的通信方式。
- Volumes:可以定义数据卷,以便在容器之间共享数据或持久化数据。
创建 Nest 引入 mysql 服务
创建 Nest 项目试试:
bash
nest new docker-compose-test -p npm
安装 tyeporm、mysql2:
bash
npm install @nestjs/typeorm typeorm mysql2
在 mysql workbench 里创建个 database:
bash
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4;
在 AppModule 引入 TypeOrmModule:
typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'xxx',
database: 'test',
synchronize: true,
logging: true,
entities: [],
poolSize: 10,
connectorPackage: 'mysql2',
extra: {
authPlugin: 'sha256_password',
},
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
src 目录下创建 test.entity.ts:
typescript
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class Test {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
}
在 entities 里注册下:
启动 Nest 服务:
bash
npm run start:dev
mysql 服务没问题。
引入 redis 服务
bash
npm install redis
AppModule 添加一个 redis:
typescript
{
provide: 'REDIS_CLIENT',
async useFactory() {
const client = createClient({
socket: {
host: 'localhost',
port: 6379,
},
});
await client.connect();
return client;
},
},
在 AppControll 里注入下:
然后访问下 http://localhost:3000 后。
服务端打印了 redis 里的 key:
这就说明 redis 服务也连接成功了。
没有 docker compose
假设我们 nest 服务开发完了,想部署,那就要写这样的 dockerfile:
dockerfile
# Step 1: 使用带有 Node.js 的基础镜像
FROM node:18-alpine as builder
# 设置工作目录
WORKDIR /usr/src/app
# 复制 package.json 和 package-lock.json(如果可用)
COPY package*.json ./
# 安装项目依赖
RUN npm install --only=production
# 安装 nest CLI 工具(确保它作为项目依赖被安装)
RUN npm install @nestjs/cli -g
# 复制所有文件到容器中
COPY . .
# 构建应用程序
RUN npm run build
# Step 2: 运行时使用更精简的基础镜像
FROM node:18-alpine
# 创建 runc 的符号链接
RUN ln -s /sbin/runc /usr/bin/runc
WORKDIR /usr/src/app
# 从 builder 阶段复制构建好的文件
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/node_modules ./node_modules
# 暴露 3000 端口
EXPOSE 3000
# 运行 Nest.js 应用程序
CMD ["node", "dist/main"]
因为我们项目依赖 mysql,redis,所以我们要先运行 mysql、redis 镜像,最后才能运行 nest 镜像。
我们运行之后还会报错:
说是 127.0.0.1 的 6379 端口连不上。
注意 nest 容器里需要使用宿主机 ip 来访问 mysql、redis 服务,所以我们在 nest 应用里面要把 mysql 和 redis 的 host 全改写宿主机 ip。
终端输入 ifconfig,在 en0 找到自己的 ip。
之后 dockerfile build 一个 nest 镜像,分别按顺序运行 mysql、redis 和 nest 镜像即可。
引入 docker compose
**编写 docker-compose.yml 文件:**根目录添加一个 docker-compose.yml,定义服务、网络和数据卷等:
yaml
version: '1.0'
# 定义服务,即需要运行的容器集合
services:
# 定义一个名为'nest-app'的服务
nest-app:
# 构建配置,指定Dockerfile的路径和上下文
build:
# 指定Docker构建上下文的路径,通常是Dockerfile所在的目录
context: ./
# 指定Dockerfile的路径,相对于构建上下文
dockerfile: ./Dockerfile
# 定义该服务所依赖的其他服务,它们将按照依赖顺序启动
depends_on:
# 依赖名为'mysql-container'的服务
- mysql-container
# 依赖名为'redis-container'的服务
- redis-container
# 端口映射,将宿主机的3000端口映射到容器的3000端口
ports:
- '3000:3000'
# 定义一个名为'mysql-container'的服务,使用mysql镜像
mysql-container:
# 指定使用mysql官方Docker镜像
image: mysql
# 端口映射,将宿主机的3306端口映射到容器的3306端口
ports:
- '3306:3306'
# 数据卷配置,用于持久化存储
volumes:
# 将宿主机的/Users/yunmu/Desktop/mysql目录映射到容器的/var/lib/mysql目录
- /Users/yunmu/Desktop/mysql:/var/lib/mysql
environment:
MYSQL_DATABASE: test
MYSQL_ROOT_PASSWORD: xxx
# 定义一个名为'redis-container'的服务,使用redis镜像
redis-container:
# 指定使用redis官方Docker镜像
image: redis
# 端口映射,将宿主机的6379端口映射到容器的6379端口
ports:
- '6379:6379'
# 数据卷配置,用于持久化存储
volumes:
# 将宿主机的/Users/yunmu/Desktop/redis目录映射到容器的/data目录
- /Users/yunmu/Desktop/redis:/data
每个 services 都是一个 docker 容器,名字随便指定。
启动服务 :运行以下命令来启动所有定义的服务:
bash
docker-compose up
它会把所有容器的日志一起输出,先跑的 mysql、redis,再跑的 nest。
也可以运行这个命令:
bash
docker-compose up -d --build --force-recreate
-d
:Detached mode,意味着 Docker Compose 会在后台运行容器。- -
-build
:在启动服务之前强制重构建服务关联的镜像。 --force-recreate
:即使容器的配置没有改变,也强制重新创建容器。
成功创建。
浏览器访问下 http://localhost:3000/:
nest 容器内打印了 redis 的 key。
停止移除 Docker Compose 启动的容器:
bash
docker-compose down
如果还想移除对应的镜像,请使用:
bash
docker-compose down --rmi all
桥接网络
Docker 桥接网络是一种网络模式,允许容器在同一个宿主机上进行通信,同时与宿主机以及其他未连接到该桥接网络的容器隔离。
在 Docker 桥接网络中,每个容器都会被分配一个独立的IP地址,并且这些IP地址都是与宿主机不同的。
Docker 通过桥接器(bridge)将这些容器连接起来,形成一个虚拟的网络环境。
容器之间可以通过这个网络环境进行通信,而宿主机也可以通过桥接器与容器进行通信。
Docker桥接网络适用于在同一个宿主机上运行的容器之间的通信。如果需要在不同宿主机上的容器之间进行通信,则可以使用Docker的覆盖网络(overlay network)。
当启动一个Docker容器时,如果没有指定网络模式,那么默认会使用桥接网络。可以通过Docker命令或Docker Compose文件来配置容器的网络模式。
通过 docker network 来创建叫桥接网络:
bash
docker network create common-network
通过 networks 指定创建的 common-network 桥接网络,网络驱动程序指定为 bridge。
其实我们一直用的网络驱动程序都是 bridge,它的含义是容器的网络和宿主机网络是隔离开的,但是可以做端口映射。比如 -p 3000:3000、-p 3306:3306 这样。
修改 AppModule 的代码,改成用容器名来访问:
先删除之前的容器镜像:
bash
docker-compose down --rmi all
运行 compose:
成功运行。
其实不指定 networks 也可以,docker-compose 会创建个默认的:
运行 docker-compose up , 你会发现它创建了一个默认的 network:
所以,不手动指定 networks,也是可以用桥接网络的。
我们如果不用 docker compose,可以在 docker run 的时候指定 --network,这样 3 个容器通过容器名也能互相访问。
比如:
bash
docker run -d --network common-network -v /Users/yunmu/Desktop/mysql:/var/lib/mysql --name mysql-container mysql
docker run -d --network common-network -v /Users/yunmu/Desktop/redis:/data --name redis-container redis
docker run -d --network common-network -p 3000:3000 --name nest-container nest-image
其实本质就是对 Network Namespace 的处理,本来是 3 个独立的 Namespace,当指定了 network 桥接网络,就可以在自己 Namespace 下访问别的 Namespace 了。