Эцсийн төсөл
Курсын туршид үзсэн бүх зүйлийг нэг дор ашиглана — Node.js апп + PostgreSQL + Nginx stack-ийг Docker Compose-оор ажиллуулах бүрэн төсөл.
Төслийн бүтэц
final-project/
├── docker-compose.yml
├── .env
├── .env.example
├── nginx/
│ └── nginx.conf
├── app/
│ ├── Dockerfile
│ ├── .dockerignore
│ ├── package.json
│ ├── tsconfig.json
│ └── src/
│ └── index.ts
└── db/
└── init.sql
Апп бэлдэх
app/src/index.ts
import express from "express";
import { Pool } from "pg";
const app = express();
app.use(express.json());
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
});
app.get("/health", (_req, res) => {
res.json({ status: "ok", timestamp: new Date().toISOString() });
});
app.get("/users", async (_req, res) => {
try {
const result = await pool.query(
"SELECT id, username, created_at FROM users ORDER BY created_at DESC",
);
res.json(result.rows);
} catch (err) {
res
.status(500)
.json({ error: "Өгөгдлийн сантай холбогдоход алдаа гарлаа" });
}
});
app.post("/users", async (req, res) => {
const { username } = req.body as { username: string };
if (!username) {
res.status(400).json({ error: "username шаардлагатай" });
return;
}
try {
const result = await pool.query(
"INSERT INTO users (username) VALUES ($1) RETURNING id, username, created_at",
[username],
);
res.status(201).json(result.rows[0]);
} catch (err) {
res.status(500).json({ error: "Хэрэглэгч нэмэхэд алдаа гарлаа" });
}
});
const PORT = Number(process.env.PORT ?? 3000);
app.listen(PORT, () => {
console.log(`Сервер ${PORT} порт дээр ажиллаж байна`);
});
app/package.json
{
"name": "final-project-app",
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "ts-node-dev src/index.ts"
},
"dependencies": {
"express": "^4.18.2",
"pg": "^8.11.3"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/pg": "^8.11.0",
"typescript": "^5.3.3",
"ts-node-dev": "^2.0.0"
}
}
app/Dockerfile
# ── Stage 1: builder ──────────────────────────
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build
# ── Stage 2: production ───────────────────────
FROM node:20-alpine AS production
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
COPY --from=builder /app/dist ./dist
RUN chown -R appuser:appgroup /app
USER appuser
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
app/.dockerignore
node_modules
dist
.env
*.md
db/init.sql
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username TEXT UNIQUE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
INSERT INTO users (username) VALUES
('boldbaatar'),
('munkhjargal')
ON CONFLICT (username) DO NOTHING;
nginx/nginx.conf
events {
worker_connections 1024;
}
http {
upstream app {
server app:3000;
}
server {
listen 80;
location / {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /health {
proxy_pass http://app;
access_log off;
}
}
}
.env.example
DB_USER=myuser
DB_PASSWORD=changeme
DB_NAME=myapp
DATABASE_URL=postgresql://myuser:changeme@db:5432/myapp
.env файл:
cp .env.example .env
# .env дотор нууц үгийг өөрчилнө
docker-compose.yml
services:
db:
image: postgres:16-alpine
restart: unless-stopped
mem_limit: 512m
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
volumes:
- pgdata:/var/lib/postgresql/data
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"]
interval: 10s
timeout: 5s
start_period: 10s
retries: 5
networks:
- backend
app:
build:
context: ./app
dockerfile: Dockerfile
restart: unless-stopped
mem_limit: 256m
cpus: 0.5
environment:
NODE_ENV: production
DATABASE_URL: ${DATABASE_URL}
PORT: 3000
depends_on:
db:
condition: service_healthy
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:3000/health"]
interval: 30s
timeout: 5s
start_period: 20s
retries: 3
networks:
- backend
- frontend
nginx:
image: nginx:alpine
restart: unless-stopped
mem_limit: 64m
ports:
- "80:80"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
app:
condition: service_healthy
networks:
- frontend
volumes:
pgdata:
networks:
backend:
driver: bridge
frontend:
driver: bridge
Network тусгаарлалт:
backendnetwork:db↔app— Nginx энэ networkт байхгүй тул database-д шууд хандаж чадахгүйfrontendnetwork:app↔nginx— Database энэ networkт байхгүй
Ажиллуулах
# 1. .env файл үүсгэх
cp .env.example .env
# 2. Бүгдийг build хийж ажиллуулах
docker compose up --build
# 3. Background-д ажиллуулах
docker compose up --build -d
# 4. Шалгах
curl http://localhost/health
curl http://localhost/users
curl -X POST http://localhost/users \
-H "Content-Type: application/json" \
-d '{"username": "tsetseg"}'
Ажиллаж байгааг шалгах
# Бүх container-ийн төлөв
docker compose ps
# Log харах
docker compose logs -f
# Нөөцийн хэрэглээ
docker stats
# Database-д шууд холбогдох
docker compose exec db psql -U myuser -d myapp -c "SELECT * FROM users;"
Зогсоох ба цэвэрлэх
# Зогсоох
docker compose down
# Volume-тай хамт бүгдийг устгах
docker compose down -v
# Image-тай хамт
docker compose down -v --rmi all
Та юу сурсан бэ?
Энэ курсын туршид:
Үндэс — Container, image, registry-ийн ойлголт. Docker-ийн үндсэн команд.
Dockerfile — Layer, cache, multi-stage build. Node.js, Python аппликейшн containerize хийх.
Compose — Олон сервис хамтад нь ажиллуулах. Volume, network, healthcheck, depends_on.
Production бэлдэлт — Non-root user, resource limits, log удирдлага, аюулгүй байдал, image scan.
CI/CD — GitHub Actions-аар автоматаар build ба push.
Docker нь орчин үеийн backend хөгжүүлэлтийн суурь хэрэгсэл болсон. Энэ мэдлэгийн дээр Kubernetes, AWS ECS, Google Cloud Run зэрэг container orchestration платформуудыг судалж болно.