- Visual Studio Code / vim,
- Git cli,
- GitHub,
- DockerHub,
- Nest.js cli,
- Docker Desktop,
- Kubernetes (minikube),
- Chrome / curl,
- Prometheus.
Write a node.js application which displays in http on / "hello world".
1. Installation
$ mkdir -p ~/workspace/prestashop
$ cd ~/workspace/prestashop
$ nest new test-node
-Replace string by hello world in method getHello of AppService
2. Test with Jest
$ npm run test
PASS src/app.controller.spec.ts
AppController
root
✓ should return "hello world" (35ms)
console.log src/app.service.ts:6
hello world
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.604s
Ran all test suites.
3. Run application
$ cd test-node && npm start
$ curl -w '\n' localhost:3000
hello world
4. Save code in Github
$ git init
$ git add .
$ git commit -m "exercice 1"
$ git remote add origin https://github.com/vleclerc/test-node-prestashop.git
$ git push -u origin master
Run it on a container Docker.
1. Make the Dockerfile
$ touch Dockerfile
2. Check last version LTS of nodejs ex: 12.16.1
3. Ignore useless files to build our Docker image
$ touch .dockerignore
4. Build an image of our application
$ docker build -t vleclerc/test-node-prestashop:0.0.1 .
5. Show images
$ docker images
Result:
REPOSITORY TAG IMAGE ID CREATED SIZE
vleclerc/test-node-prestashop 0.0.1 45752af18474 13 seconds ago 344MB
node 12.16.1-slim 26932a190e66 3 weeks ago 140MB
...
6. Run a container of our application with this image
$ docker run --name test-node-prestashop --rm -p 8080:3000 -d vleclerc/test-node-prestashop:0.0.1
7. Show containers
$ docker ps
Result:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a032f056de60 vleclerc/test-node-prestashop:0.0.1 "docker-entrypoint.s…" 3 seconds ago Up 2 seconds 8080/tcp, 0.0.0.0:8080->3000/tcp test-node-prestashop
8. Follow application logs in the container In a new terminal:
$ docker logs -f test-node-prestashop
Result:
> [email protected] start /usr/src/app
> nest start
[Nest] 30 - 03/16/2020, 12:39:25 PM [NestFactory] Starting Nest application...
[Nest] 30 - 03/16/2020, 12:39:25 PM [InstanceLoader] AppModule dependencies initialized 36ms
[Nest] 30 - 03/16/2020, 12:39:25 PM [RoutesResolver] AppController {/}: 12ms
[Nest] 30 - 03/16/2020, 12:39:25 PM [RouterExplorer] Mapped {/, GET} route 8ms
[Nest] 30 - 03/16/2020, 12:39:25 PM [NestApplication] Nest application successfully started 5ms
9. Test our container application
$ curl -w '\n' localhost:8080
Result:
hello world
Logs:
hello world
10. Stop the container
$ docker stop test-node-prestashop
Updates the application to display : "hello PrestaShop"
1. Update code
-Replace string world by PrestaShop in method getHello of AppService
2. Test with Jest
$ npm run test
FAIL src/app.controller.spec.ts
AppController
root
✕ should return "hello world" (36ms)
● AppController › root › should return "hello world"
expect(received).toBe(expected) // Object.is equality
Expected: "hello world"
Received: "hello PrestaShop"
17 | describe('root', () => {
18 | it('should return "hello world"', () => {
> 19 | expect(appController.getHello()).toBe('hello world');
| ^
20 | });
21 | });
22 | });
at Object.<anonymous> (app.controller.spec.ts:19:40)
console.log src/app.service.ts:6
hello PrestaShop
3. Update Test
Replace string world by PrestaShop in file app.controller.spec.ts
4. Run test again
$ npm run test
PASS src/app.controller.spec.ts
AppController
root
✓ should return "hello PrestaShop" (30ms)
console.log src/app.service.ts:6
hello PrestaShop
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 3.692s
Ran all test suites.
5. Build a new image version of our application
$ docker build -t vleclerc/test-node-prestashop:0.0.2 .
6. Show images
$ docker images
Result:
REPOSITORY TAG IMAGE ID CREATED SIZE
vleclerc/test-node-prestashop 0.0.2 f64abb651979 10 seconds ago 344MB
vleclerc/test-node-prestashop 0.0.1 45752af18474 2 days ago 344MB
node 12.16.1-slim 26932a190e66 3 weeks ago 140MB
...
7. Run a container of our application with this image
$ docker stop test-node-prestashop
$ docker run --name test-node-prestashop --rm -p 8080:3000 -d vleclerc/test-node-prestashop:0.0.2
8. Show containers
$ docker ps
Result:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
672de4d069e4 vleclerc/test-node-prestashop:0.0.2 "docker-entrypoint.s…" 15 seconds ago Up 14 seconds 8080/tcp, 0.0.0.0:8080->3000/tcp test-node-prestashop
9. Follow application logs in the container In a new terminal:
$ docker logs -f test-node-prestashop
Result:
> [email protected] start /usr/src/app
> nest start
[Nest] 30 - 03/16/2020, 12:39:25 PM [NestFactory] Starting Nest application...
[Nest] 30 - 03/16/2020, 12:39:25 PM [InstanceLoader] AppModule dependencies initialized 36ms
[Nest] 30 - 03/16/2020, 12:39:25 PM [RoutesResolver] AppController {/}: 12ms
[Nest] 30 - 03/16/2020, 12:39:25 PM [RouterExplorer] Mapped {/, GET} route 8ms
[Nest] 30 - 03/16/2020, 12:39:25 PM [NestApplication] Nest application successfully started 5ms
10. Test our container application
$ curl -w '\n' localhost:8080
Result:
hello PrestaShop
Logs:
hello PrestaShop
10. Stop the container
$ docker stop test-node-prestashop
11. Save local images in DockerHub
$ docker tag vleclerc/test-node-prestashop:0.0.1 vleclercdev/test-node-prestashop:0.0.1
$ docker push vleclercdev/test-node-prestashop:0.0.1
$ docker tag vleclerc/test-node-prestashop:0.0.2 vleclercdev/test-node-prestashop:0.0.2
$ docker push vleclercdev/test-node-prestashop:0.0.2
1. Start minikube to deploy containers
$ minikube start
$ kubectl cluster-info
Kubernetes master is running at https://192.168.64.3:8443
KubeDNS is running at https://192.168.64.3:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
2. Start Kubernetes Dashboard cf. https://github.com/vleclerc/k8s-dashboard
3. Configure and Start Prometheus cf. https://github.com/vleclerc/prometheus
4. Add monitoring, liveness and readness endpoints on your app
$ npm i prometheus-api-metrics && npm prom-client
$ npm i @cloudnative/health-connect
Add global middleware in main.ts and test metrics endpoint.
$ npm start
Test endpoints in your favorite browser: http://localhost:3000/metrics http://localhost:3000/live http://localhost:3000/ready http://localhost:3000/health
5. Recreate tag and push test-node-prestashop:0.0.1 and 0.0.2 with endpoints
$ docker rmi vleclerc/test-node-prestashop:0.0.1
$ docker build -t vleclerc/test-node-prestashop:0.0.1 .
$ docker tag vleclerc/test-node-prestashop:0.0.1 vleclercdev/test-node-prestashop:0.0.1
$ docker push vleclercdev/test-node-prestashop:0.0.1
$
$ docker rmi vleclerc/test-node-prestashop:0.0.2
$ docker build -t vleclerc/test-node-prestashop:0.0.2 .
$ docker tag vleclerc/test-node-prestashop:0.0.2 vleclercdev/test-node-prestashop:0.0.2
$ docker push vleclercdev/test-node-prestashop:0.0.2
6. Make a deployment of the test-node-prestashop:0.0.1 (stable) In a kube directory create app-deployment.yml Reference dockerhub image vleclercdev/test-node-prestashop:0.0.1 Configure number of replica and blue/green deployment strategy partial ex:
...
replicas: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
...
$ kubectl apply -f kube
7. Prometheus Graph update Prometheus target with cluste_rip:service_port
get cluster ip
$ kubectl cluster-info
get service port
$ kubectl describe service app-lb
go to http://localhost:9090/graph Filter by up expression Look at the graph when you deploy there is no downtime.
8. Make a deployment of 25% the test-node-prestashop:0.0.2 (canary strategy) There is 3 replicas on stable version if you deploy 1 replica on carany the total of replicas is 4 stable app : 3/4 = 75% canary app : 1/4 = 25%
$ kubectl apply -f kubectl apply -f kube/app-deployment-canary.yaml
9. Test deployment of canary app
On 30 calls we have 7 HelloPrestaShop 7*100/30=23.333... approximatly 25%
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello world
➜ test-node git:(master) ✗ curl -w '\n' http://192.168.64.3:31534/
hello PrestaShop