惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
G
GRAHAM CLULEY
T
Threat Research - Cisco Blogs
Spread Privacy
Spread Privacy
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
NISL@THU
NISL@THU
Latest news
Latest news
N
News and Events Feed by Topic
量子位
爱范儿
爱范儿
M
MIT News - Artificial intelligence
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
GbyAI
GbyAI
A
Arctic Wolf
Google Online Security Blog
Google Online Security Blog
W
WeLiveSecurity
S
Security Affairs
Webroot Blog
Webroot Blog
B
Blog
N
Netflix TechBlog - Medium
Hugging Face - Blog
Hugging Face - Blog
I
InfoQ
博客园 - 司徒正美
H
Heimdal Security Blog
罗磊的独立博客
L
LINUX DO - 最新话题
Security Archives - TechRepublic
Security Archives - TechRepublic
博客园_首页
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
The Hacker News
The Hacker News
Y
Y Combinator Blog
C
CERT Recently Published Vulnerability Notes
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
C
Cisco Blogs
V
Vulnerabilities – Threatpost
T
The Exploit Database - CXSecurity.com
博客园 - 聂微东
Microsoft Azure Blog
Microsoft Azure Blog
人人都是产品经理
人人都是产品经理
The Register - Security
The Register - Security
PCI Perspectives
PCI Perspectives
The GitHub Blog
The GitHub Blog
Jina AI
Jina AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
O
OpenAI News
S
Security @ Cisco Blogs
Google DeepMind News
Google DeepMind News

博客园 - 豆豆の爸爸

IDEA 2024的零卡死配置 10分钟揭秘大模型的原理 苹果容器Apple container是做什么用的? pnpm 10.14 支持JavaScript运行时的安装了 用 rake 合并多个 JS 文件,并且用 Google Closure Compiler 压缩代码 HTML 5 就是 Web Application JS程序员的一天 Google Map 类实例在类式继承中的实现 写入 cookie 的过期时间时在GMT或UTC时间格式上的兼容问题 “当 HTML 5 来敲门”专题沙龙(上海)活动 PHP 的 Smarty 模板页中分离JS并避开literal标签的解决方法 Google Maps(Google 地图) V3 在 IE7 浏览器中拖放其容器时图块被覆盖的 bug 2010年我的个人总结 [译]用 Closure Compiler 编写更好的 OO 的 JavaScript 使用 IronScheme 进入 Scheme 编程语言的世界 - 豆豆の爸爸 《JS高级程序设计(第2版)》书评 [译]在 Firebug 中的表格化日志 在 Notepad++ 运行 Closure Linter 来校验JS代码 在 Notepad++ 运行 Closure Compiler 工具来解析并压缩JS
白话Docker:用Web应用实例深入容器
豆豆の爸爸 · 2025-07-29 · via 博客园 - 豆豆の爸爸

我会通过用Docker部署一个Node.js编写的web应用程序,来深入地理解Docker容器的本质。

一、准备工作

在开始前,需要在你的Mac电脑中安装Docker。在官方网站上有各种环境下的安装指南,用Mac电脑的就看Install Docker Desktop on Mac

安装Docker

安装步骤非常简单:

  1. 如果是M1芯片的arm64架构,就下载Docker for Mac with Apple silicon

  2. 双击Docker.dmg文件,然后把鲸鱼图标拖放到Applications文件夹即可。
    image

运行Docker

在应用中找到Docker图标,点击运行。
image

运行后,在菜单栏会看到多了一个鲸鱼图标:
image

然后,在终端用命令查下Docker的版本:

docker --version

再查下docker info是不是也正常。如果都正常,就可以用Node.js编写web应用了。

在这里,我是用Nestjs写的应用程序,代码非常简单。也就是用Nestjs的cli工具生成的,创建应用的这一步就直接跳过了,直接上代码:

// app.controller.ts部分
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

// app.module.ts部分
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}


// app.service.ts部分
import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

// main.ts部分
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT ?? 3000);
}
bootstrap();

在上面的代码中,就是用Nest框架启动了一个Web服务器,只有一个简单的功能是:打印出“Hello World!”。

那么,将这样的一个应用程序容器化,第一步是制作容器镜像。

二、Docker的配置文件:Dockerfile

首先,我们在这个Nestjs的应用程序的根目录下创建一个文本文件,文件名是:Dockerfile,没有扩展名的。

# 使用官方提供的Node.js开发镜像作为基础镜像
FROM node:22-slim

# 工作目录切换为 /usr/src/app
WORKDIR /usr/src/app

# 将 package.json复制到当前目录下
COPY package.json ./

# 使用corepack安装 pnpm
RUN corepack enable && corepack prepare pnpm --activate

# 使用pnpm命令安装这个应用所需要的依赖
RUN pnpm install

COPY . .

# 暴露给外界访问容器3000端口
EXPOSE 3000

# 设置容器进程为:pnpm run start:dev,即:这个Node应用开发环境的启动命令
CMD ["pnpm", "run", "start:dev"]

通过Dockerfile的内容,可以看到是用一些标准的大写词语来描述Docker镜像。这些大写的词语就是按上面内容顺序来处理的。

三、制作Docker镜像

接下来,可以用Docker制作这个应用的镜像了,在当前目录执行:

docker build -t nestjs-app .

这个-t是给这个镜像加tag,也就是起个名儿。docker build会自动加载当前目录中的Dockerfile,然后按照里面的顺序一个个的执行其中的大写词语。在这个过程里面,实际上就等同于Docker使用基础镜像启动了一个容器,然后在这个容器中一个个执行Dockerfile中的大写词语。

在每次执行后,都会生成一个对应的镜像层。

Docker build操作完成后,可以用 docker image命令查下结果:

docker image ls 

REPOSITORY           TAG                     IMAGE ID       CREATED         SIZE
nestjs-app          latest                  2badda7aef21   2 hours ago     1.11GB

四、使用Docker镜像启动容器

使用这个镜像,用docker run命令来启动容器:

docker run nestjs-app

在镜像名的后面,什么都不用写,这是因为在Dockerfile中已经指定CMD了,不然,就得把进程的开发环境启动命令加在后面:

docker run -p 3000:8080 nestjs-app pnpm run start:dev

在容器启动了之后,可以用docker ps命令看一下:

docker ps

CONTAINER ID   IMAGE               COMMAND                   CREATED 
8015b4eac7eb   nestjs-app   "docker-entrypoint.s…"   23 seconds ago

在这个命令中,我通过-p 3000:8080告诉Docker,把容器中8080端口映射到宿主机的3000端口。

这样,访问宿主机的3000端口,就可以看到容器中应用程序返回的结果:
image

这是VSCode中的插件 Thunder client,类似用curl http://localhost:3000

这样,已经使用容器完成了一个web应用的开发与测试。

五、总结

这里我用了一个Node.js应用作为实例,讲解了Docker容器使用的主要场景。熟悉了这些操作,也就基本摸清了Docker容器核心功能。

下面来深入理解Docker容器镜像。

在这个容器进程“pnpm run start:dev”,是运行在由Linux Namespace和Cgroups所构成的隔离环境中;运行它所需要的各种文件,是由多个挂载在一块的rootfs层提供的。

rootfs(根文件系统)也就是容器镜像,这是容器的实现机制。它只是一个操作系统所有文件、配置和目录,不包括操作系统内核。而虚拟机的镜像是一个磁盘的“快照”,如果磁盘有几个GB的大小,这个镜像就是几个GB这么大。

在rootfs中,由于有“分层镜像”的设计,因此Docker容器镜像的是增量式的,这样每次拉取、推送镜像内容要比多个完整的操作系统的大小要小得多。还有共享层可以让所有这些容器镜像需要的总空间,比每个镜像的总和要小。

把Docker镜像发布后,下载这个镜像得到的内容肯定是完全一致的,可以完全复现制作这个镜像的完整环境。这就是Docker容器技术“一致性”的重要特性。

容器镜像把每一个环节都打通了:“开发-测试-部署”,现在已经成为软件的主流发布方式。

六、最后

这个项目对应的github地址是:https://github.com/georgewing/nestjs-docker-app,也就是这篇文章的代码和示例。