@@ -148,36 +148,146 @@ Install Terraform and ensure the Scaleway Terraform provider is set up with `ter
148148### Setting-up Terraform files
149149
1501501 . Create a new directory and set up your files:
151-
152151 ```
153152 mkdir scaleway-kube-db
154153 cd scaleway-kube-db
155154 ```
156155
1571562 . Create a ` providers.tf ` file:
158-
159157 ```
160- ADD CONTENT FROM PROVIDERS.TF FILE
158+ terraform {
159+ required_providers {
160+ scaleway = {
161+ source = "scaleway/scaleway"
162+ version = "~> 2.40"
163+ }
164+ }
165+ }
166+
167+ provider "scaleway" {
168+ access_key = var.scaleway_access_key
169+ secret_key = var.scaleway_secret_key
170+ project_id = var.project_id
171+ region = var.region
172+ zone = var.zone
173+ }
161174 ```
162175
1631763 . Create a ` variables.tf ` file:
164-
165177 ```
166- ADD CONTENT FROM VARIABLES.TF FILE
178+ variable "scaleway_access_key" {
179+ description = "Scaleway Access Key"
180+ type = string
181+ sensitive = true
182+ }
183+
184+ variable "scaleway_secret_key" {
185+ description = "Scaleway Secret Key"
186+ type = string
187+ sensitive = true
188+ }
189+
190+ variable "project_id" {
191+ description = "Scaleway Project ID"
192+ type = string
193+ }
194+
195+ variable "region" {
196+ description = "Scaleway region (e.g., fr-par)"
197+ type = string
198+ default = "fr-par"
199+ }
200+
201+ variable "zone" {
202+ description = "Scaleway zone (e.g., fr-par-1)"
203+ type = string
204+ default = "fr-par-1"
205+ }
206+
207+ variable "db_password" {
208+ description = "Password for database user"
209+ type = string
210+ sensitive = true
211+ }
212+
213+ variable "db_user" {
214+ description = "Database username"
215+ type = string
216+ default = "admin"
217+ }
167218 ```
168219
1692204 . Create a ` main.tf ` file for the infrastructure:
170-
171221 ```
172- ADD CONTENT FROM MAIN.TF FILE
222+ # Create Private Network
223+ resource "scaleway_vpc_private_network" "private_net" {
224+ name = "kube-db-network"
225+ region = var.region
226+ }
227+
228+ # Create Managed PostgreSQL Database
229+ resource "scaleway_rdb_instance" "database" {
230+ name = "my-kube-database"
231+ node_type = "db-dev-s"
232+ engine = "PostgreSQL-15"
233+ is_ha_cluster = true
234+ user_name = var.db_user
235+ password = var.db_password
236+ private_network {
237+ pn_id = scaleway_vpc_private_network.private_net.id
238+ }
239+ }
240+
241+ # Kubernetes Cluster (Kapsule)
242+ resource "scaleway_k8s_cluster" "kapsule" {
243+ name = "my-kube-cluster"
244+ version = "1.28.2"
245+ cni = "cilium"
246+ private_network_id = scaleway_vpc_private_network.private_net.id
247+
248+ autoscaler_config {
249+ disable_scale_down = false
250+ scale_down_delay_after_add = "10m"
251+ scale_down_unneeded_time = "10m"
252+ estimator = "binpacking"
253+ expander = "random"
254+ ignore_daemonsets_utilization = true
255+ }
256+
257+ pool {
258+ name = "default-pool"
259+ node_type = "DEV1-M"
260+ size = 2
261+ autoscaling = true
262+ min_size = 2
263+ max_size = 5
264+ autohealing = true
265+ container_runtime = "containerd"
266+ }
267+ }
268+
269+ # Output Database Connection Information
270+ output "db_host" {
271+ value = scaleway_rdb_instance.database.private_network[0].ip
272+ }
273+
274+ output "db_port" {
275+ value = scaleway_rdb_instance.database.endpoint[0].port
276+ }
277+
278+ output "kubeconfig" {
279+ value = scaleway_k8s_cluster.kapsule.kubeconfig
280+ sensitive = true
281+ }
173282 ```
174-
175283### Creating a terraform.tfvars file
176284
177- Create a file to store your variables securely:
178-
285+ Create a ` terraform.tfvars ` file to store your variables securely:
179286 ```
180- ADD CONTENT FROM TERRAFORM.TFVARS FILE
287+ scaleway_access_key = "<your-scaleway-access-key >"
288+ scaleway_secret_key = "<your-scaleway-secret-key >"
289+ project_id = "<your-scaleway-project-id >"
290+ db_password = "<your-strong-db-password >"
181291 ```
182292
183293### Applying the Terraform configuration
@@ -197,10 +307,136 @@ Now let's deploy a more realistic application that uses the database. Here's a s
197307
198308### Creating a Dockerfile for the application
199309
310+ The Dockerfile is used to create a Docker image for your application. Here's a simple example:
311+
312+ ``` dockerfile
313+ # Use the official Node.js 14 image.
314+ # https://hub.docker.com/_/node
315+ FROM node:14
316+
317+ # Create and change to the app directory.
318+ WORKDIR /usr/src/app
319+
320+ # Copy application dependency manifests to the container image.
321+ # A wildcard is used to ensure both package.json AND package-lock.json are copied.
322+ # Copying this separately prevents re-running npm install on every code change.
323+ COPY package*.json ./
324+
325+ # Install production dependencies.
326+ RUN npm install --only=production
327+
328+ # Copy local code to the container image.
329+ COPY . .
330+
331+ # Run the web service on container startup.
332+ CMD [ "node" , "app.js" ]
333+
334+ # Expose the port the app runs on
335+ EXPOSE 8080
336+ ```
337+
200338### Creating the application files
201339
340+ You need to create the necessary files for your Node.js application. Here’s a simple ` app.js ` and a ` package.json ` file as an example:
341+
342+ ** ` app.js ` ** :
343+ ```javascript
344+ const express = require('express');
345+ const { Pool } = require('pg');
346+ const app = express();
347+
348+ const pool = new Pool({
349+ user : ' postgres' ,
350+ host : ' node-postgres-db' , // This matches the service name in Kubernetes
351+ database : ' postgres' ,
352+ password : ' password' , // Ensure this matches the password set in the Kubernetes secret
353+ port : 5432 ,
354+ } );
355+
356+ app.get('/', async (req, res) => {
357+ try {
358+ const result = await pool .query (' SELECT NOW() as now' );
359+ res.send(result.rows);
360+ } catch (err ) {
361+ console.error(err);
362+ res.status(500 ).send(err.toString());
363+ }
364+ } );
365+
366+ const PORT = process.env.PORT || 8080;
367+ app.listen(PORT, () => {
368+ console .log (` Server running on port ${PORT } ` );
369+ } );
370+ ```
371+
372+ ** ` package.json ` ** :
373+ ```json
374+ {
375+ " name" : " node-postgres-app" ,
376+ " version" : " 1.0.0" ,
377+ " main" : " app.js" ,
378+ " dependencies" : {
379+ " express" : " ^4.17.1" ,
380+ " pg" : " ^8.6.0"
381+ }
382+ }
383+ ```
384+
202385### Creating Kubernetes manifests for the application
203386
387+ You need to create two main Kubernetes manifests: one for the deployment and one for the service.
388+
389+ ** ` deployment.yaml ` ** :
390+ ```yaml
391+ apiVersion: apps/v1
392+ kind: Deployment
393+ metadata:
394+ name: node-postgres-app
395+ spec:
396+ replicas: 1
397+ selector:
398+ matchLabels:
399+ app: node-postgres-app
400+ template:
401+ metadata:
402+ labels:
403+ app: node-postgres-app
404+ spec:
405+ containers:
406+ - name: node-postgres-app
407+ image: ${ YOUR_DOCKER_REGISTRY } /node-postgres-app:latest
408+ ports:
409+ - containerPort: 8080
410+ env:
411+ - name: POSTGRES_PASSWORD
412+ valueFrom:
413+ secretKeyRef:
414+ name: postgres-secret
415+ key: password
416+ ---
417+ apiVersion: v1
418+ kind: Secret
419+ metadata:
420+ name: postgres-secret
421+ type: Opaque
422+ data:
423+ password: cGFzc3dvcmQ= # base64 encoded password, 'password' in this case
424+ ```
425+
426+ ** ` service.yaml ` ** :
427+ ```yaml
428+ apiVersion: v1
429+ kind: Service
430+ metadata:
431+ name: node-postgres-app
432+ spec:
433+ type: LoadBalancer
434+ ports:
435+ - port: 80
436+ targetPort: 8080
437+ selector:
438+ app: node-postgres-app
439+ ```
204440### Building and pushing the Docker image
205441
206442 <Message type = " note" >
@@ -227,7 +463,7 @@ Now let's deploy a more realistic application that uses the database. Here's a s
227463 kubectl get service node-postgres-app
228464 ```
229465
230- 3 . Visit the application at the external IP to see it in action.
466+ 3 . Visit the application at the external IP to see it in action. If everything is set up correctly, you should see the current PostgreSQL time displayed when you access the application URL.
231467
232468## Security best practices
233469
0 commit comments