# ============================================================ # 1. Namespace # ============================================================ apiVersion: v1 kind: Namespace metadata: name: web-portal --- # ============================================================ # 2. PostgreSQL - PersistentVolumeClaim # ============================================================ apiVersion: v1 kind: PersistentVolumeClaim metadata: name: postgres-pvc namespace: web-portal spec: accessModes: [ReadWriteOnce] resources: requests: storage: 2Gi --- # ============================================================ # 3. PostgreSQL - Secret # ============================================================ apiVersion: v1 kind: Secret metadata: name: postgres-secret namespace: web-portal type: Opaque stringData: POSTGRES_DB: portaldb POSTGRES_USER: portaluser POSTGRES_PASSWORD: portalpass --- # ============================================================ # 4. PostgreSQL - ConfigMap (init SQL) # ============================================================ apiVersion: v1 kind: ConfigMap metadata: name: postgres-init-sql namespace: web-portal data: init.sql: | CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username VARCHAR(100) UNIQUE NOT NULL, password_hash TEXT NOT NULL, is_admin BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS webpages ( id SERIAL PRIMARY KEY, name VARCHAR(200) NOT NULL, url TEXT NOT NULL, description TEXT DEFAULT '', icon VARCHAR(10) DEFAULT '🌐', created_at TIMESTAMP DEFAULT NOW() ); CREATE TABLE IF NOT EXISTS user_webpages ( user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, webpage_id INTEGER REFERENCES webpages(id) ON DELETE CASCADE, PRIMARY KEY (user_id, webpage_id) ); -- admin / admin1234 INSERT INTO users (username, password_hash, is_admin) VALUES ('admin', '$2b$12$LQv3c1yqBWVHxkd0LHAkCOYz6TtxMQJqhN8/LewKyNiAYMxRmfvym', TRUE) ON CONFLICT (username) DO NOTHING; -- user1 / user1234 INSERT INTO users (username, password_hash, is_admin) VALUES ('user1', '$2b$12$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', FALSE) ON CONFLICT (username) DO NOTHING; INSERT INTO webpages (name, url, description, icon) VALUES ('Google', 'https://www.google.com', 'ꡬ글 검색 μ—”μ§„', 'πŸ”'), ('GitHub', 'https://github.com', 'μ†ŒμŠ€μ½”λ“œ μ €μž₯μ†Œ', 'πŸ™'), ('Notion', 'https://notion.so', 'λ¬Έμ„œ ν˜‘μ—… 툴', 'πŸ“') ON CONFLICT DO NOTHING; --- # ============================================================ # 5. PostgreSQL - Deployment # ============================================================ apiVersion: apps/v1 kind: Deployment metadata: name: postgres namespace: web-portal spec: replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: postgres:15-alpine ports: - containerPort: 5432 envFrom: - secretRef: name: postgres-secret volumeMounts: - name: data mountPath: /var/lib/postgresql/data - name: init-sql mountPath: /docker-entrypoint-initdb.d resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m" volumes: - name: data persistentVolumeClaim: claimName: postgres-pvc - name: init-sql configMap: name: postgres-init-sql --- # ============================================================ # 6. PostgreSQL - Service # ============================================================ apiVersion: v1 kind: Service metadata: name: postgres-service namespace: web-portal spec: selector: app: postgres ports: - port: 5432 targetPort: 5432 --- # ============================================================ # 7. Backend - Secret (JWT) # ============================================================ apiVersion: v1 kind: Secret metadata: name: backend-secret namespace: web-portal type: Opaque stringData: JWT_SECRET: "change-this-to-a-secure-random-string-in-production" --- # ============================================================ # 8. Backend - Deployment # ============================================================ apiVersion: apps/v1 kind: Deployment metadata: name: backend namespace: web-portal spec: replicas: 2 selector: matchLabels: app: backend template: metadata: labels: app: backend spec: initContainers: - name: wait-for-db image: busybox command: ['sh', '-c', 'until nc -z postgres-service 5432; do echo waiting for postgres; sleep 2; done'] containers: - name: backend image: web-portal-backend:latest imagePullPolicy: Never # Docker Desktop 둜컬 이미지 μ‚¬μš© ports: - containerPort: 8000 env: - name: DB_HOST value: postgres-service - name: DB_PORT value: "5432" - name: DB_NAME valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_DB - name: DB_USER valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_USER - name: DB_PASSWORD valueFrom: secretKeyRef: name: postgres-secret key: POSTGRES_PASSWORD - name: JWT_SECRET valueFrom: secretKeyRef: name: backend-secret key: JWT_SECRET livenessProbe: httpGet: path: /api/health port: 8000 initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: httpGet: path: /api/health port: 8000 initialDelaySeconds: 5 periodSeconds: 5 resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "500m" --- # ============================================================ # 9. Backend - Service # ============================================================ apiVersion: v1 kind: Service metadata: name: backend-service namespace: web-portal spec: selector: app: backend ports: - port: 8000 targetPort: 8000 --- # ============================================================ # 10. Frontend (Nginx) - Deployment # ============================================================ apiVersion: apps/v1 kind: Deployment metadata: name: frontend namespace: web-portal spec: replicas: 2 selector: matchLabels: app: frontend template: metadata: labels: app: frontend spec: containers: - name: frontend image: web-portal-frontend:latest imagePullPolicy: Never # Docker Desktop 둜컬 이미지 μ‚¬μš© ports: - containerPort: 80 resources: requests: memory: "64Mi" cpu: "50m" limits: memory: "128Mi" cpu: "200m" --- # ============================================================ # 11. Frontend - Service (NodePort - μ™ΈλΆ€ μ ‘μ†μš©) # ============================================================ apiVersion: v1 kind: Service metadata: name: frontend-service namespace: web-portal spec: type: NodePort selector: app: frontend ports: - port: 80 targetPort: 80 nodePort: 30080 # http://localhost:30080 으둜 접속