容器数据持久化的两种方法 + Node 侧 SSH 客户端 ssh2
容器数据持久化的问题
Docker 容器是临时的运行环境。当容器被删除或重新创建后,容器内部的所有数据都会丢失。对于数据库备份文件这类需要持久保存的数据,必须将其同步到宿主机。
方法一:Docker Volumes(卷映射)
在 docker-compose.yml 中使用 volumes 字段,将容器内部路径映射到宿主机:
services:
mongo:
image: mongo:latest
volumes:
- ./data/mongo:/data/db # 宿主机路径:容器路径
- ./backups:/tmp/backups # 备份文件映射
yaml
特点:
- 实时双向同步,容器内外文件自动保持一致
- 适合需要持续读写的数据(如数据库文件)
- 在容器创建时就需要配置,不能动态添加
方法二:docker cp(手动拷贝)
使用 docker cp 命令将容器内文件拷贝到宿主机:
# 从容器拷贝到宿主机
docker cp <container_name>:/tmp/2024-01-01-log ./backups/
# 从宿主机拷贝到容器
docker cp ./backups/2024-01-01-log <container_name>:/tmp/
bash
特点:
- 按需手动执行,不需要预先配置
- 适合一次性操作(如导出备份文件)
- 支持双向拷贝
容器内文件管理
# 查看容器内目录
docker exec -it <container_name> ls -la /tmp
# 删除容器内指定目录
docker exec -it <container_name> rm -rf /tmp/2024-01-01-log
# 验证删除结果
docker exec -it <container_name> ls -la /tmp
bash
由于 Docker 镜像大多基于 Linux,可以直接使用 Linux 命令(ls、rm、cp 等)操作容器内的文件系统。
从本地操作到远程操作
上述操作都是在本地 Docker 环境中执行的。在实际生产中,Docker 通常部署在远程服务器或虚拟机上。要在远程服务器上执行这些操作,需要通过 SSH 连接到服务器。
ssh2:Node.js SSH 客户端
ssh2 是 Node.js 中最流行的 SSH 客户端库,下载量超过 5000 万次,提供了完整的 SSH2 协议支持:
- 远程命令执行(exec)
- 文件传输(SFTP)
- 端口转发
- Shell 交互
安装
pnpm add ssh2
pnpm add -D @types/ssh2
bash
当前版本为 1.15.0。如果大版本号变更,建议锁定版本以保持兼容性。
基本用法
根据官方文档,ssh2 的基本使用模式如下:
const { Client } = require('ssh2')
const conn = new Client()
conn.on('ready', () => {
console.log('Client :: ready')
conn.exec('uptime', (err, stream) => {
if (err) throw err
stream
.on('close', (code, signal) => {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal)
conn.end()
})
.on('data', (data) => {
console.log('STDOUT: ' + data)
})
.stderr.on('data', (data) => {
console.log('STDERR: ' + data)
})
})
}).connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
privateKey: require('fs').readFileSync('/path/to/my/key'),
})
javascript
核心流程:
- 创建
Client实例 - 监听
ready事件(连接成功后触发) - 调用
connect()方法传入连接配置 - 在
ready回调中使用exec()执行远程命令 - 通过
stream的data/stderr/close事件获取执行结果
创建 NestJS SSH 模块
为了在 NestJS 中方便地使用 SSH 功能,我们需要将其封装为一个可配置的模块。
模块目录结构
src/
├── utils/ # 工具模块目录
│ └── ssh/
│ ├── ssh.module.ts # 外层模块(固定接口)
│ ├── ssh-core.module.ts # 核心模块(实现逻辑)
│ ├── ssh.service.ts # SSH 服务
│ └── ssh.interfaces.ts # 类型定义
text
注意:SSH 属于通用工具模块,应放在
utils/目录下,而不是conditional/目录(后者用于可选的第三方模块集成)。
模块设计
NestJS 自定义模块支持两种注册方式:
forRoot(options):同步注册,直接传入配置对象forRootAsync(options):异步注册,通过工厂方法获取配置(支持依赖注入)
// ssh.module.ts
import { DynamicModule, Module } from '@nestjs/common'
import { SshCoreModule } from './ssh-core.module'
@Module({})
export class SshModule {
static forRoot(options: SshModuleOptions): DynamicModule {
return SshCoreModule.forRoot(options)
}
static forRootAsync(options: SshModuleAsyncOptions): DynamicModule {
return SshCoreModule.forRootAsync(options)
}
}
typescript
外层 SshModule 只做接口透传,核心逻辑全部放在 SshCoreModule 中。这种双层结构的好处是外层接口稳定,内部实现可以自由重构。
小结
| 持久化方法 | 适用场景 | 特点 |
|---|---|---|
| Docker Volumes | 需要持续同步的数据 | 实时同步、配置时声明 |
| docker cp | 一次性文件导出/导入 | 手动执行、按需操作 |
- 生产环境中,Docker 通常部署在远程服务器,需要通过 SSH 执行操作
ssh2是 Node.js 中最成熟的 SSH 客户端库,支持远程命令执行和文件传输- SSH 模块采用双层结构设计(外层固定接口 + 内层核心实现),符合 NestJS 动态模块的最佳实践
- 下一节将完成 SSH 模块的核心逻辑实现
↑