乐闻世界logo
搜索文章和话题

What are the best practices for Deno deployment and operations?

2月21日 16:06

Deployment and operations of Deno are important aspects of building production-grade applications. Understanding how to properly deploy and operate Deno applications can ensure application stability and maintainability.

Deployment Overview

Deno applications can be deployed to various environments, including traditional servers, containerized platforms, cloud services, and edge computing platforms.

Docker Deployment

1. Basic Dockerfile

dockerfile
# Use official Deno image FROM denoland/deno:1.38.0 # Set working directory WORKDIR /app # Copy dependency files COPY deno.json ./ # Cache dependencies RUN deno cache src/main.ts # Copy source code COPY . . # Expose port EXPOSE 8000 # Run application CMD ["deno", "run", "--allow-net", "--allow-env", "src/main.ts"]

2. Multi-stage Build

dockerfile
# Build stage FROM denoland/deno:1.38.0 AS builder WORKDIR /app COPY . . # Compile to executable RUN deno compile --allow-net --allow-env --output=app src/main.ts # Run stage FROM debian:bullseye-slim WORKDIR /app # Copy executable from build stage COPY --from=builder /app/app . # Expose port EXPOSE 8000 # Run application CMD ["./app"]

3. Production Dockerfile

dockerfile
# Build stage FROM denoland/deno:1.38.0 AS builder WORKDIR /app # Install dependencies COPY deno.json ./ RUN deno cache src/main.ts # Copy source code COPY . . # Run tests RUN deno test --allow-all # Compile to executable RUN deno compile \ --allow-net \ --allow-env \ --allow-read \ --output=app \ src/main.ts # Run stage FROM debian:bullseye-slim WORKDIR /app # Install necessary runtime dependencies RUN apt-get update && \ apt-get install -y ca-certificates && \ rm -rf /var/lib/apt/lists/* # Create non-root user RUN useradd -m -u 1000 deno # Copy executable from build stage COPY --from=builder /app/app . # Change owner RUN chown -R deno:deno /app # Switch to non-root user USER deno # Expose port EXPOSE 8000 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 # Run application CMD ["./app"]

Kubernetes Deployment

1. Deployment Configuration

yaml
apiVersion: apps/v1 kind: Deployment metadata: name: deno-app labels: app: deno-app spec: replicas: 3 selector: matchLabels: app: deno-app template: metadata: labels: app: deno-app spec: containers: - name: deno-app image: your-registry/deno-app:latest ports: - containerPort: 8000 env: - name: PORT value: "8000" - name: DATABASE_URL valueFrom: secretKeyRef: name: app-secrets key: database-url resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health port: 8000 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8000 initialDelaySeconds: 5 periodSeconds: 5

2. Service Configuration

yaml
apiVersion: v1 kind: Service metadata: name: deno-app-service spec: selector: app: deno-app ports: - protocol: TCP port: 80 targetPort: 8000 type: LoadBalancer

3. ConfigMap and Secret

yaml
# ConfigMap apiVersion: v1 kind: ConfigMap metadata: name: app-config data: PORT: "8000" LOG_LEVEL: "info" --- # Secret apiVersion: v1 kind: Secret metadata: name: app-secrets type: Opaque data: database-url: <base64-encoded-url> api-key: <base64-encoded-key>

Cloud Platform Deployment

1. Deno Deploy

Deno Deploy is Deno's official edge computing platform.

typescript
// main.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/") { return new Response("Hello from Deno Deploy!", { headers: { "Content-Type": "text/plain" }, }); } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 });

Deployment steps:

  1. Create Deno Deploy account
  2. Connect GitHub repository
  3. Configure deployment settings
  4. Automatic deployment

2. Vercel Deployment

json
// vercel.json { "version": 2, "builds": [ { "src": "src/main.ts", "use": "@vercel/deno" } ], "routes": [ { "src": "/(.*)", "dest": "/src/main.ts" } ] }

3. Railway Deployment

toml
# railway.toml [build] builder = "NIXPACKS" [deploy] startCommand = "deno run --allow-net --allow-env src/main.ts" [env] PORT = "8000"

Process Management

1. Using PM2

javascript
// ecosystem.config.js module.exports = { apps: [{ name: 'deno-app', script: 'deno', args: 'run --allow-net --allow-env src/main.ts', instances: 'max', exec_mode: 'cluster', autorestart: true, watch: false, max_memory_restart: '1G', env: { NODE_ENV: 'production', PORT: 8000 } }] };
bash
# Install PM2 npm install -g pm2 # Start application pm2 start ecosystem.config.js # View status pm2 status # View logs pm2 logs # Restart application pm2 restart deno-app # Stop application pm2 stop deno-app

2. Using Systemd

ini
# /etc/systemd/system/deno-app.service [Unit] Description=Deno Application After=network.target [Service] Type=simple User=deno WorkingDirectory=/app Environment="PORT=8000" Environment="DATABASE_URL=postgres://..." ExecStart=/usr/local/bin/deno run --allow-net --allow-env /app/src/main.ts Restart=always RestartSec=10 [Install] WantedBy=multi-user.target
bash
# Enable service sudo systemctl enable deno-app # Start service sudo systemctl start deno-app # View status sudo systemctl status deno-app # View logs sudo journalctl -u deno-app -f # Restart service sudo systemctl restart deno-app

Monitoring and Logging

1. Log Management

typescript
// logger.ts import { getLogger, setup, handlers } from "https://deno.land/std@0.208.0/log/mod.ts"; await setup({ handlers: { console: new handlers.ConsoleHandler("INFO"), file: new handlers.FileHandler("INFO", { filename: "./logs/app.log", formatter: "{levelName} {datetime} {msg}", }), }, loggers: { default: { level: "INFO", handlers: ["console", "file"], }, }, }); export const logger = getLogger();

2. Health Check

typescript
// health.ts import { serve } from "https://deno.land/std@0.208.0/http/server.ts"; let isHealthy = true; let isReady = false; // Simulate startup time setTimeout(() => { isReady = true; }, 5000); const handler = async (req: Request): Promise<Response> => { const url = new URL(req.url); if (url.pathname === "/health") { return new Response(JSON.stringify({ status: isHealthy ? "ok" : "error" }), { headers: { "Content-Type": "application/json" }, status: isHealthy ? 200 : 503, }); } if (url.pathname === "/ready") { return new Response(JSON.stringify({ ready: isReady }), { headers: { "Content-Type": "application/json" }, status: isReady ? 200 : 503, }); } return new Response("Not Found", { status: 404 }); }; await serve(handler, { port: 8000 });

3. Metrics Collection

typescript
// metrics.ts class MetricsCollector { private metrics: Map<string, number> = new Map(); private counters: Map<string, number> = new Map(); increment(name: string, value: number = 1) { const current = this.counters.get(name) || 0; this.counters.set(name, current + value); } gauge(name: string, value: number) { this.metrics.set(name, value); } timing(name: string, duration: number) { const timings = this.metrics.get(`${name}_timings`) || []; timings.push(duration); this.metrics.set(`${name}_timings`, timings); } getMetrics(): Record<string, any> { return { counters: Object.fromEntries(this.counters), gauges: Object.fromEntries(this.metrics), }; } } export const metrics = new MetricsCollector();

Performance Optimization

1. Connection Pool

typescript
// connection-pool.ts class ConnectionPool<T> { private pool: T[] = []; private maxConnections: number; private factory: () => Promise<T>; constructor(maxConnections: number, factory: () => Promise<T>) { this.maxConnections = maxConnections; this.factory = factory; } async acquire(): Promise<T> { if (this.pool.length > 0) { return this.pool.pop()!; } return await this.factory(); } release(connection: T) { if (this.pool.length < this.maxConnections) { this.pool.push(connection); } } }

2. Caching Strategy

typescript
// cache.ts class Cache { private cache: Map<string, { value: any; expires: number }> = new Map(); private ttl: number; constructor(ttl: number = 60000) { this.ttl = ttl; } set(key: string, value: any, ttl?: number) { const expires = Date.now() + (ttl || this.ttl); this.cache.set(key, { value, expires }); } get(key: string): any | null { const item = this.cache.get(key); if (!item) { return null; } if (Date.now() > item.expires) { this.cache.delete(key); return null; } return item.value; } clear() { this.cache.clear(); } cleanup() { const now = Date.now(); for (const [key, item] of this.cache.entries()) { if (now > item.expires) { this.cache.delete(key); } } } }

Security Best Practices

1. Environment Variable Management

typescript
// config.ts interface Config { port: number; databaseUrl: string; apiKey: string; logLevel: string; } function loadConfig(): Config { const port = parseInt(Deno.env.get("PORT") || "8000"); const databaseUrl = Deno.env.get("DATABASE_URL"); const apiKey = Deno.env.get("API_KEY"); const logLevel = Deno.env.get("LOG_LEVEL") || "info"; if (!databaseUrl) { throw new Error("DATABASE_URL environment variable is required"); } if (!apiKey) { throw new Error("API_KEY environment variable is required"); } return { port, databaseUrl, apiKey, logLevel, }; } export const config = loadConfig();

2. Principle of Least Privilege

bash
# Grant only necessary permissions deno run --allow-net --allow-env src/main.ts # Limit network access deno run --allow-net=api.example.com src/main.ts # Limit file access deno run --allow-read=/app/data src/main.ts

CI/CD Integration

1. GitHub Actions

yaml
# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Deno uses: denoland/setup-deno@v1 with: deno-version: v1.38.0 - name: Run tests run: deno test --allow-all - name: Lint run: deno lint - name: Format check run: deno fmt --check deploy: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build Docker image run: docker build -t deno-app:${{ github.sha }} . - name: Push to registry run: | echo ${{ secrets.REGISTRY_PASSWORD }} | docker login -u ${{ secrets.REGISTRY_USER }} --password-stdin docker push deno-app:${{ github.sha }}

2. GitLab CI

yaml
# .gitlab-ci.yml stages: - test - build - deploy test: stage: test image: denoland/deno:1.38.0 script: - deno test --allow-all - deno lint - deno fmt --check build: stage: build image: docker:latest services: - docker:dind script: - docker build -t deno-app:$CI_COMMIT_SHA . - docker push deno-app:$CI_COMMIT_SHA deploy: stage: deploy image: alpine:latest script: - kubectl set image deployment/deno-app deno-app=deno-app:$CI_COMMIT_SHA only: - main

Troubleshooting

1. Common Issues

Issue: Application fails to start

bash
# Check logs deno run --log-level=debug src/main.ts # Check permissions deno info src/main.ts

Issue: Memory leak

typescript
# Periodically check memory usage setInterval(() => { const usage = Deno.memoryUsage(); console.log("Memory usage:", usage); }, 60000);

Issue: Performance degradation

typescript
# Use performance profiling import { performance } from "https://deno.land/std@0.208.0/node/performance.ts"; const start = performance.now(); // Execute operation const duration = performance.now() - start; console.log(`Operation took ${duration}ms`);

Best Practices

  1. Containerized deployment: Use Docker to ensure environment consistency
  2. Health checks: Implement health check endpoints
  3. Logging: Record key operations and errors
  4. Monitoring metrics: Collect and monitor application metrics
  5. Automated deployment: Use CI/CD for automated deployment
  6. Principle of least privilege: Grant only necessary permissions
  7. Resource limits: Set reasonable resource limits
  8. Backup strategy: Regularly backup important data

Deployment and operations of Deno require considering multiple aspects. Through proper planning and implementation, stable and reliable production-grade applications can be built.

标签:Deno