feat(blog): add blog posts and typography plugin

Add new blog posts covering React Hooks, Docker with Node.js, Tailwind CSS, and TypeScript best practices. Implement Tailwind Typography plugin for better blog post styling. Create blog post listing pages in both English and Chinese. Update BlogLayout to use prose styling for improved readability.

The changes include:
- Added 4 new blog posts in English and Chinese versions
- Integrated @tailwindcss/typography plugin
- Created blog post index pages
- Updated BlogLayout with typography-focused styling
- Added related dependencies to package.json
This commit is contained in:
joyzhao
2025-06-16 22:24:20 +08:00
parent b4b2153bde
commit 70b6a27021
14 changed files with 4699 additions and 1 deletions

View File

@@ -0,0 +1,400 @@
---
title: "Scaling Node.js Apps with Docker"
description: "Learn how to containerize Node.js applications using Docker for seamless deployment and scalability in production environments."
image: "https://images.unsplash.com/photo-1605745341112-85968b19335b?w=400&h=250&fit=crop&crop=center"
date: "April 25, 2025"
readTime: "7 min read"
tags: ["Node.js", "Docker", "DevOps"]
slug: "scaling-nodejs-docker"
layout: "../../../layouts/BlogLayout.astro"
---
# Scaling Node.js Apps with Docker
![Docker and Node.js](https://images.unsplash.com/photo-1605745341112-85968b19335b?w=800&h=400&fit=crop&crop=center)
Docker has revolutionized how we deploy and scale applications. When combined with Node.js, it provides a powerful platform for building scalable, maintainable applications. In this guide, we'll explore how to containerize Node.js applications and scale them effectively.
## Why Docker for Node.js?
Docker offers several advantages for Node.js applications:
- **Consistency**: Same environment across development, testing, and production
- **Isolation**: Applications run in isolated containers
- **Scalability**: Easy horizontal scaling with container orchestration
- **Portability**: Run anywhere Docker is supported
- **Resource efficiency**: Lightweight compared to virtual machines
## Creating a Dockerfile for Node.js
Let's start with a basic Node.js application and create a Dockerfile:
```dockerfile
# Use the official Node.js runtime as the base image
FROM node:18-alpine
# Set the working directory inside the container
WORKDIR /usr/src/app
# Copy package.json and package-lock.json (if available)
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production
# Copy the rest of the application code
COPY . .
# Create a non-root user to run the application
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Change ownership of the app directory to the nodejs user
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
# Expose the port the app runs on
EXPOSE 3000
# Define the command to run the application
CMD ["node", "server.js"]
```
## Multi-Stage Builds for Optimization
For production applications, use multi-stage builds to reduce image size:
```dockerfile
# Build stage
FROM node:18-alpine AS builder
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install all dependencies (including devDependencies)
RUN npm ci
# Copy source code
COPY . .
# Build the application (if you have a build step)
RUN npm run build
# Production stage
FROM node:18-alpine AS production
WORKDIR /usr/src/app
# Copy package files
COPY package*.json ./
# Install only production dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy built application from builder stage
COPY --from=builder /usr/src/app/dist ./dist
COPY --from=builder /usr/src/app/server.js ./
# Create non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
RUN chown -R nextjs:nodejs /usr/src/app
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
```
## Docker Compose for Development
Use Docker Compose to manage your development environment:
```yaml
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
volumes:
- .:/usr/src/app
- /usr/src/app/node_modules
environment:
- NODE_ENV=development
- DATABASE_URL=mongodb://mongo:27017/myapp
depends_on:
- mongo
- redis
command: npm run dev
mongo:
image: mongo:5.0
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mongo_data:
redis_data:
```
## Production Deployment with Docker Swarm
For production scaling, use Docker Swarm or Kubernetes. Here's a Docker Swarm example:
```yaml
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: myapp:latest
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=mongodb://mongo:27017/myapp
networks:
- app-network
depends_on:
- mongo
mongo:
image: mongo:5.0
deploy:
replicas: 1
restart_policy:
condition: on-failure
volumes:
- mongo_data:/data/db
networks:
- app-network
environment:
- MONGO_INITDB_ROOT_USERNAME=admin
- MONGO_INITDB_ROOT_PASSWORD=password
nginx:
image: nginx:alpine
deploy:
replicas: 1
restart_policy:
condition: on-failure
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
networks:
- app-network
depends_on:
- app
volumes:
mongo_data:
external: true
networks:
app-network:
driver: overlay
```
## Health Checks and Monitoring
Add health checks to your Dockerfile:
```dockerfile
# Add to your Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD node healthcheck.js
```
Create a simple health check script:
```javascript
// healthcheck.js
const http = require('http');
const options = {
host: 'localhost',
port: 3000,
path: '/health',
timeout: 2000
};
const request = http.request(options, (res) => {
console.log(`STATUS: ${res.statusCode}`);
if (res.statusCode === 200) {
process.exit(0);
} else {
process.exit(1);
}
});
request.on('error', (err) => {
console.log('ERROR:', err);
process.exit(1);
});
request.end();
```
## Performance Optimization Tips
### 1. Use .dockerignore
Create a `.dockerignore` file to exclude unnecessary files:
```
node_modules
npm-debug.log
.git
.gitignore
README.md
.env
.nyc_output
coverage
.cache
```
### 2. Optimize Layer Caching
Order your Dockerfile commands to maximize cache efficiency:
```dockerfile
# Copy package files first (changes less frequently)
COPY package*.json ./
RUN npm ci --only=production
# Copy source code last (changes more frequently)
COPY . .
```
### 3. Use Alpine Images
Alpine Linux images are much smaller:
```dockerfile
FROM node:18-alpine # ~40MB
# vs
FROM node:18 # ~350MB
```
### 4. Implement Graceful Shutdown
```javascript
// server.js
const express = require('express');
const app = express();
const server = require('http').createServer(app);
// Your app routes here
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.get('/health', (req, res) => {
res.status(200).send('OK');
});
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
// Graceful shutdown
process.on('SIGTERM', () => {
console.log('SIGTERM received, shutting down gracefully');
server.close(() => {
console.log('Process terminated');
process.exit(0);
});
});
process.on('SIGINT', () => {
console.log('SIGINT received, shutting down gracefully');
server.close(() => {
console.log('Process terminated');
process.exit(0);
});
});
```
## Monitoring and Logging
Use structured logging and monitoring:
```javascript
// logger.js
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.simple()
)
})
]
});
module.exports = logger;
```
## Conclusion
Docker provides a powerful platform for scaling Node.js applications. By following these best practices:
- Use multi-stage builds for optimized production images
- Implement proper health checks and graceful shutdown
- Use Docker Compose for development environments
- Leverage orchestration tools like Docker Swarm or Kubernetes for production
- Monitor and log your applications properly
You'll be able to build robust, scalable Node.js applications that can handle production workloads efficiently.
Remember that containerization is just one part of a scalable architecture. Consider implementing load balancing, caching strategies, and database optimization for complete scalability.
---
*Ready to deploy? Check out our guide on Kubernetes deployment strategies for even more advanced scaling techniques.*