diff --git a/.dockerignore.txt b/.dockerignore.txt new file mode 100644 index 0000000..2aed98c --- /dev/null +++ b/.dockerignore.txt @@ -0,0 +1,7 @@ +.env +.vs +npm_debug.log +.git +.gitignore +node_module +README.md \ No newline at end of file diff --git a/.vs/ShopingKaro/FileContentIndex/1d9d3582-b81e-4263-a3a7-1625fdc57943.vsidx b/.vs/ShopingKaro/FileContentIndex/1d9d3582-b81e-4263-a3a7-1625fdc57943.vsidx new file mode 100644 index 0000000..90e0d02 Binary files /dev/null and b/.vs/ShopingKaro/FileContentIndex/1d9d3582-b81e-4263-a3a7-1625fdc57943.vsidx differ diff --git a/.vs/ShopingKaro/FileContentIndex/32d614f3-f1c9-4ab4-8ae4-e42923bac192.vsidx b/.vs/ShopingKaro/FileContentIndex/32d614f3-f1c9-4ab4-8ae4-e42923bac192.vsidx new file mode 100644 index 0000000..e33dd19 Binary files /dev/null and b/.vs/ShopingKaro/FileContentIndex/32d614f3-f1c9-4ab4-8ae4-e42923bac192.vsidx differ diff --git a/.vs/ShopingKaro/FileContentIndex/a859d0e4-01da-4d36-95ef-e9caf165e6ba.vsidx b/.vs/ShopingKaro/FileContentIndex/a859d0e4-01da-4d36-95ef-e9caf165e6ba.vsidx new file mode 100644 index 0000000..70aef67 Binary files /dev/null and b/.vs/ShopingKaro/FileContentIndex/a859d0e4-01da-4d36-95ef-e9caf165e6ba.vsidx differ diff --git a/.vs/ShopingKaro/v17/.wsuo b/.vs/ShopingKaro/v17/.wsuo new file mode 100644 index 0000000..ffe2ea5 Binary files /dev/null and b/.vs/ShopingKaro/v17/.wsuo differ diff --git a/.vs/ShopingKaro/v17/DocumentLayout.backup.json b/.vs/ShopingKaro/v17/DocumentLayout.backup.json new file mode 100644 index 0000000..f64eff3 --- /dev/null +++ b/.vs/ShopingKaro/v17/DocumentLayout.backup.json @@ -0,0 +1,12 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\Poornima\\Documents\\Poornima\\Interview\\ShopingKaro\\", + "Documents": [], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [] + } + ] +} \ No newline at end of file diff --git a/.vs/ShopingKaro/v17/DocumentLayout.json b/.vs/ShopingKaro/v17/DocumentLayout.json new file mode 100644 index 0000000..eadcae0 --- /dev/null +++ b/.vs/ShopingKaro/v17/DocumentLayout.json @@ -0,0 +1,37 @@ +{ + "Version": 1, + "WorkspaceRootPath": "C:\\Users\\Poornima\\Documents\\Poornima\\Interview\\ShopingKaro\\", + "Documents": [ + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\Poornima\\Documents\\Poornima\\Interview\\ShopingKaro\\Dockerfile.txt||{8B382828-6202-11D1-8870-0000F87579D2}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Dockerfile.txt||{8B382828-6202-11D1-8870-0000F87579D2}" + } + ], + "DocumentGroupContainers": [ + { + "Orientation": 0, + "VerticalTabListWidth": 256, + "DocumentGroups": [ + { + "DockedWidth": 200, + "SelectedChildIndex": 0, + "Children": [ + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "Dockerfile.txt", + "DocumentMoniker": "C:\\Users\\Poornima\\Documents\\Poornima\\Interview\\ShopingKaro\\Dockerfile.txt", + "RelativeDocumentMoniker": "Dockerfile.txt", + "ToolTip": "C:\\Users\\Poornima\\Documents\\Poornima\\Interview\\ShopingKaro\\Dockerfile.txt", + "RelativeToolTip": "Dockerfile.txt", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAA8AAAAYAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003109|", + "WhenOpened": "2025-05-23T10:51:30.993Z", + "EditorCaption": "" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 0000000..5635e8e --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,7 @@ +{ + "ExpandedNodes": [ + "" + ], + "SelectedNode": "\\package-lock.json", + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000..0774708 Binary files /dev/null and b/.vs/slnx.sqlite differ diff --git a/Dockerfile.txt b/Dockerfile.txt new file mode 100644 index 0000000..99a618f --- /dev/null +++ b/Dockerfile.txt @@ -0,0 +1,16 @@ +#Stage 1: Build +FROM node:18-slim As builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci --omit=dev +COPY .. +#Stage 2: Production +FROM node:18-slim +#Non root user +RUN groupadd -r appuser && useradd -r appuser +WORKDIR /app +COPY --from=builder /app . +RUN chown -R appuser:appuser /app +USER appuser +EXPOSE 3000 +CMD ["node", "index.js"] \ No newline at end of file diff --git a/app-deployment.yaml b/app-deployment.yaml new file mode 100644 index 0000000..6f00ec1 --- /dev/null +++ b/app-deployment.yaml @@ -0,0 +1,36 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: shopingkaro-app +spec: + replicas: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + selector: + matchLabels: + app: shopingkaro + template: + metadata: + labels: + app: shopingkaro + spec: + containers: + - name: shopingkaro + image: shopingkaro-backend:latest + ports: + - containerPort: 3000 + env: + - name: DB_URI + valueFrom: + secretKeyRef: + name: mongodb-secret + key: uri + - name: NODE_ENV + valueFrom: + configMapKeyRef: + name: app-config + key: NODE_ENV + diff --git a/app-ingress.yaml b/app-ingress.yaml new file mode 100644 index 0000000..630406c --- /dev/null +++ b/app-ingress.yaml @@ -0,0 +1,18 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: shopingkaro-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + rules: + - host: shopingkaro.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: shopingkaro-service + port: + number: 80 diff --git a/app-service.yaml b/app-service.yaml new file mode 100644 index 0000000..552b435 --- /dev/null +++ b/app-service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: shopingkaro-service +spec: + selector: + app: shopingkaro + ports: + - port: 80 + targetPort: 3000 diff --git a/configmap.yaml b/configmap.yaml new file mode 100644 index 0000000..4ab6e1a --- /dev/null +++ b/configmap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: app-config +data: + NODE_ENV: production + JWT_SECRET: supersecurejwtkey diff --git a/hpa.yaml b/hpa.yaml new file mode 100644 index 0000000..24ca6ac --- /dev/null +++ b/hpa.yaml @@ -0,0 +1,18 @@ +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: shopingkaro-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: shopingkaro-app + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 60 diff --git a/mongo-pvc.yaml b/mongo-pvc.yaml new file mode 100644 index 0000000..893bfe6 --- /dev/null +++ b/mongo-pvc.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: mongo-pvc +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 5Gi diff --git a/mongo-service.yaml b/mongo-service.yaml new file mode 100644 index 0000000..9b8d9e8 --- /dev/null +++ b/mongo-service.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: Service +metadata: + name: mongo-service +spec: + ports: + - port: 27017 + selector: + app: mongo + clusterIP: None diff --git a/mongo-statefulset.yaml b/mongo-statefulset.yaml new file mode 100644 index 0000000..075a4ab --- /dev/null +++ b/mongo-statefulset.yaml @@ -0,0 +1,42 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mongo +spec: + serviceName: "mongo" + replicas: 1 + selector: + matchLabels: + app: mongo + template: + metadata: + labels: + app: mongo + spec: + containers: + - name: mongo + image: mongo:6 + ports: + - containerPort: 27017 + env: + - name: MONGO_INITDB_ROOT_USERNAME + valueFrom: + secretKeyRef: + name: mongodb-secret + key: username + - name: MONGO_INITDB_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mongodb-secret + key: password + volumeMounts: + - name: mongo-storage + mountPath: /data/db + volumeClaimTemplates: + - metadata: + name: mongo-storage + spec: + accessModes: ["ReadWriteOnce"] + resources: + requests: + storage: 5Gi diff --git a/readme.md b/readme.md index 8d6183f..8d98311 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,47 @@ +#KhushiBaby Docker Optimization challenges +1. Key Optimization: +Multi-Stage Builds: Separates build and production stages to ensure only necessary files are included in the final image. + +Slim Base Image: Utilizes node:18-slim to reduce image size. + +Non-Root User: Runs the application as a non-root user (appuser) to enhance security. + +Efficient Dependency Installation: Uses npm ci --omit=dev to install only production dependencies, ensuring faster and more reliable builds. + +.dockerignore Usage: Excludes unnecessary files and directories from the Docker context to minimize image size. + + + +2. Optimization reduced image size, leading to faster deployment and reduced resource consumption. + + +3.Security Checklist + +Non-Root Execution: Application runs as a non-root user to limit potential vulnerabilities. + +Minimal Base Image: Reduces attack surface by using a slim base image. + +Exclusion of Dev Dependencies: Ensures only necessary packages are included in the production image. + +.dockerignore: Prevents sensitive files (e.g., .env, .git) from being added to the image. + + +4. Building and running docker image locally +1. Clone the Repository: + +2. Build the Docker Image: + +docker build -t shopingkaro-app . + + +3. Run the Docker Container: + +docker run -p 3000:3000 shopingkaro-app + +The application will be accessible at http://localhost:3000. + + + # ShopingKaro ShopingKaro is a web application developed in Node.js that allows users to easily browse and shop for various products. With a user-friendly interface and a variety of features, ShopingKaro aims to provide a seamless online shopping experience. diff --git a/secret.yaml b/secret.yaml new file mode 100644 index 0000000..a6f1ab4 --- /dev/null +++ b/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mongodb-secret +type: Opaque +data: + username: bW9uZ291c2Vy + password: c2VjcmV0cGFzcw== + uri: bW9uZ29kYjovL21vbmdvdXNlcjpzZWNyZXRwYXNzQG1vbmdvLXNlcnZpY2U6MjcwMTcvc2hvcGluZ2thcm8=