├── .gitignore
├── README
├── chapter01-basic-knowledges
├── 1.1-basic-golang
│ ├── 01-install-golang
│ │ └── README
│ ├── 02-first-program
│ │ ├── README
│ │ ├── formatter
│ │ │ └── index.go
│ │ └── main.go
│ ├── 03-variable-types
│ │ ├── README
│ │ └── main.go
│ ├── 04-if-and-loop
│ │ ├── README
│ │ └── main.go
│ ├── 05-interface-struct
│ │ ├── README
│ │ └── main.go
│ └── 06-go-routine
│ │ ├── README
│ │ └── main.go
├── 1.2-basic-docker
│ ├── 01-install-docker
│ │ └── README
│ ├── 02-run-nginx-with-docker
│ │ ├── README
│ │ └── docker-compose.yml
│ ├── 03-build-docker-image
│ │ ├── Dockerfile
│ │ ├── README
│ │ ├── docker-compose.yml
│ │ └── html
│ │ │ └── index.html
│ ├── 04-push-docker-image
│ │ └── README
│ ├── 05-run-docker-compose
│ │ ├── README
│ │ ├── docker-compose.yml
│ │ └── dockers
│ │ │ └── lb
│ │ │ ├── logs
│ │ │ ├── access.log
│ │ │ └── error.log
│ │ │ └── nginx
│ │ │ └── conf
│ │ │ ├── bitnami
│ │ │ └── certs
│ │ │ │ ├── server.crt
│ │ │ │ └── server.key
│ │ │ ├── fastcgi.conf
│ │ │ ├── fastcgi.conf.default
│ │ │ ├── fastcgi_params
│ │ │ ├── fastcgi_params.default
│ │ │ ├── koi-utf
│ │ │ ├── koi-win
│ │ │ ├── mime.types
│ │ │ ├── mime.types.default
│ │ │ ├── nginx.conf
│ │ │ ├── nginx.conf.default
│ │ │ ├── scgi_params
│ │ │ ├── scgi_params.default
│ │ │ ├── uwsgi_params
│ │ │ ├── uwsgi_params.default
│ │ │ ├── vhosts
│ │ │ └── app.conf
│ │ │ └── win-utf
│ └── extra-image-nginx-1.12
│ │ ├── Dockerfile
│ │ └── rootfs
│ │ ├── app-entrypoint.sh
│ │ └── nginx-inputs.json
├── 1.3-basic-kubernetes
│ ├── 01-install-k8s
│ │ └── README
│ └── 02-run-nginx-with-k8s
│ │ ├── 00-namespace.yml
│ │ ├── 01-deployment.yml
│ │ ├── 02-service.yml
│ │ ├── 03-ingress.yml
│ │ └── README
├── 1.4-basic-redis
│ ├── 01-run-redis
│ │ ├── 00-namespace.yml
│ │ ├── 01-deployment.yml
│ │ ├── 02-service.yml
│ │ ├── 03-client-util.yml
│ │ └── README
│ ├── 02-use-redis-cli
│ │ └── README
│ └── extra-redis-image
│ │ ├── client-util-1.0
│ │ └── Dockerfile
│ │ └── redis-5.0
│ │ ├── Dockerfile
│ │ └── rootfs
│ │ ├── entrypoint.sh
│ │ ├── libredis.sh
│ │ ├── postunpack.sh
│ │ ├── redis-inputs.json
│ │ ├── run.sh
│ │ └── setup.sh
└── 1.5-basic-kafka
│ ├── 01-run-kafka
│ ├── 00-namespace.yml
│ ├── 01-deployment-zk.yml
│ ├── 02-service-zk.yml
│ ├── 03-deployment-kfk.yml
│ ├── 04-service-kfk.yml
│ ├── 05-client-util.yml
│ └── README
│ ├── 02-play-consumer-producer
│ └── README
│ ├── 03-play-consumer-group
│ └── README
│ └── extra-docker-images
│ ├── kfk-2.0
│ ├── Dockerfile
│ └── rootfs
│ │ ├── app-entrypoint.sh
│ │ ├── init.sh
│ │ ├── kafka-inputs.json
│ │ └── run.sh
│ └── zk-3.0
│ ├── Dockerfile
│ └── rootfs
│ ├── app-entrypoint.sh
│ ├── init.sh
│ ├── run.sh
│ └── zookeeper-inputs.json
├── chapter02-microservices
├── 2.2-microservices-types
│ ├── 01-http-service
│ │ ├── README
│ │ ├── context.go
│ │ ├── context_http.go
│ │ ├── main.go
│ │ └── microservice.go
│ ├── 02-consumer-service
│ │ ├── README
│ │ ├── context.go
│ │ ├── context_consumer.go
│ │ ├── docker-compose.yml
│ │ ├── main.go
│ │ ├── microservice.go
│ │ ├── producer.go
│ │ └── util.go
│ ├── 03-batch-consumer-service
│ │ ├── README
│ │ ├── batch_event.go
│ │ ├── context.go
│ │ ├── context_consumer_batch.go
│ │ ├── docker-compose.yml
│ │ ├── main.go
│ │ ├── microservice.go
│ │ ├── producer.go
│ │ └── util.go
│ ├── 04-scheduler-service
│ │ ├── README
│ │ ├── context.go
│ │ ├── context_scheduler.go
│ │ ├── main.go
│ │ └── microservice.go
│ ├── 05-asynctask-service
│ │ ├── README
│ │ ├── cacher.go
│ │ ├── context.go
│ │ ├── context_asynctask.go
│ │ ├── context_consumer.go
│ │ ├── context_http.go
│ │ ├── docker-compose.yml
│ │ ├── main.go
│ │ ├── microservice.go
│ │ ├── mq.go
│ │ ├── producer.go
│ │ └── util.go
│ └── 06-paralleltask-service
│ │ ├── README
│ │ ├── cacher.go
│ │ ├── context.go
│ │ ├── context_asynctask.go
│ │ ├── context_consumer.go
│ │ ├── context_http.go
│ │ ├── context_ptask.go
│ │ ├── docker-compose.yml
│ │ ├── main.go
│ │ ├── microservice.go
│ │ ├── mq.go
│ │ ├── producer.go
│ │ └── util.go
└── 2.3-service-startup-teardown
│ ├── README
│ ├── batch_event.go
│ ├── cacher.go
│ ├── context.go
│ ├── context_asynctask.go
│ ├── context_consumer.go
│ ├── context_consumer_batch.go
│ ├── context_http.go
│ ├── context_ptask.go
│ ├── context_scheduler.go
│ ├── docker-compose.yml
│ ├── main.go
│ ├── microservice.go
│ ├── mq.go
│ ├── producer.go
│ └── util.go
├── chapter03-deploy-scale-services
├── 3.1-deploy-services
│ ├── Dockerfile
│ ├── README
│ ├── batch_event.go
│ ├── cacher.go
│ ├── context.go
│ ├── context_asynctask.go
│ ├── context_consumer.go
│ ├── context_consumer_batch.go
│ ├── context_http.go
│ ├── context_ptask.go
│ ├── context_scheduler.go
│ ├── deploy.sh
│ ├── entrypoint.sh
│ ├── k8s
│ │ ├── 00-namespace.yml
│ │ ├── 01-register-api.yml
│ │ └── 02-ingress.yml
│ ├── main.go
│ ├── microservice.go
│ ├── mq.go
│ ├── producer.go
│ └── util.go
├── 3.2-healthcheck-readiness
│ ├── Dockerfile
│ ├── README
│ ├── batch_event.go
│ ├── cacher.go
│ ├── context.go
│ ├── context_asynctask.go
│ ├── context_consumer.go
│ ├── context_consumer_batch.go
│ ├── context_http.go
│ ├── context_ptask.go
│ ├── context_scheduler.go
│ ├── deploy.sh
│ ├── entrypoint.sh
│ ├── k8s
│ │ ├── 00-databases
│ │ │ ├── 00-namespace.yml
│ │ │ └── 01-redis.yml
│ │ └── 01-application
│ │ │ ├── 02-register-api.yml
│ │ │ └── 03-ingress.yml
│ ├── main.go
│ ├── microservice.go
│ ├── microservice_asynctask.go
│ ├── microservice_batch_consumer.go
│ ├── microservice_consumer.go
│ ├── microservice_http.go
│ ├── microservice_liveness.go
│ ├── microservice_paralleltask.go
│ ├── microservice_scheduler.go
│ ├── mq.go
│ ├── producer.go
│ └── util.go
└── 3.3-scale-services
│ ├── Dockerfile
│ ├── README
│ ├── batch_event.go
│ ├── cacher.go
│ ├── config.go
│ ├── context.go
│ ├── context_asynctask.go
│ ├── context_consumer.go
│ ├── context_consumer_batch.go
│ ├── context_http.go
│ ├── context_ptask.go
│ ├── context_scheduler.go
│ ├── deploy.sh
│ ├── entrypoint.sh
│ ├── k8s
│ ├── 00-databases
│ │ ├── 00-namespace.yml
│ │ ├── 01-redis.yml
│ │ ├── 02-zk.yml
│ │ ├── 03-kfk.yml
│ │ └── 04-client-util.yml
│ └── 01-application
│ │ ├── 05-register-api.yml
│ │ ├── 06-mail-consumer.yml
│ │ └── 07-ingress.yml
│ ├── main.go
│ ├── microservice.go
│ ├── microservice_asyncttask.go
│ ├── microservice_batch_consumer.go
│ ├── microservice_consumer.go
│ ├── microservice_http.go
│ ├── microservice_liveness.go
│ ├── microservice_paralleltask.go
│ ├── microservice_scheduler.go
│ ├── mq.go
│ ├── producer.go
│ └── util.go
└── chapter04-implement-reallife-app
└── 4.2-tcir-application
├── Dockerfile
├── README
├── batch_event.go
├── cacher.go
├── config.go
├── context.go
├── context_asynctask.go
├── context_consumer.go
├── context_consumer_batch.go
├── context_http.go
├── context_ptask.go
├── context_scheduler.go
├── deploy.sh
├── entrypoint.sh
├── k8s
├── 00-databases
│ ├── 00-namespace.yml
│ ├── 01-redis.yml
│ ├── 02-zk.yml
│ ├── 03-kfk.yml
│ └── 04-client-util.yml
└── 01-application
│ ├── 05-register-api.yml
│ ├── 06-mail-consumer.yml
│ ├── 07-batch-scheduler.yml
│ ├── 08-batch-ptask-api.yml
│ ├── 09-batch-ptask-worker.yml
│ ├── 10-external-mock-api.yml
│ └── 11-ingress.yml
├── main.go
├── microservice.go
├── microservice_asynctask.go
├── microservice_batch_consumer.go
├── microservice_consumer.go
├── microservice_http.go
├── microservice_liveness.go
├── microservice_paralleltask.go
├── microservice_scheduler.go
├── models.go
├── mq.go
├── producer.go
├── requester.go
└── util.go
/.gitignore:
--------------------------------------------------------------------------------
1 | go.mod
2 | go.sum
3 | dockers
4 | main
5 | vendor
6 | .DS_Store
7 | access.log
8 | error.log
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | ## The workshop content for Automation and scalable technology using Golang and K8S workshop
2 |
3 | If you are interested in the workshop, register and join here https://bit.ly/automationtech1-web
4 |
5 | ### Workshop Outline
6 |
7 | Chapter 1. Basic Knowledges
8 | Lesson 1.1 Basic Golang
9 | Workshop 1 - Install Golang
10 | Workshop 2 - First Program
11 | Workshop 3 - Variable and Types
12 | Workshop 4 - Conditional and Loop
13 | Workshop 5 - Interface and Struct
14 | Workshop 6 - Go routine and Channel
15 |
16 | Lesson 1.2 Basic Docker
17 | Workshop 7 - Install Docker
18 | Workshop 8 - Run Program with Docker
19 | Workshop 9 - Build Docker image
20 | Workshop 10 - Push Docker Image
21 | Workshop 11 - Run Docker compose
22 |
23 | Lesson 1.3 Basic Kubernetes
24 | Workshop 12 - Install Kubernetes
25 | Workshop 13 - Run Program with Kubernetes
26 |
27 |
28 | Lesson 1.4 Basic Redis
29 | Workshop 14 - Run Redis in Kubernetes
30 | Workshop 15 - Use redis-cli
31 |
32 |
33 | Lesson 1.5 Basic Kafka
34 | Workshop 16 - Run Kafka in Kubernetes
35 | Workshop 17 - Play with Consumer and Producer
36 | Workshop 18 - Play with Consumer Group
37 |
38 | Chapter 2. Microservices
39 | Lesson 2.1 What is Services
40 |
41 |
42 | Lesson 2.2 Service Types
43 | Workshop 19 - Coding on HTTP Service
44 | Workshop 20 - Coding on Consumer Service
45 | Workshop 21 - Coding on Batch Consumer Service
46 | Workshop 22 - Coding on Scheduler Service
47 | Workshop 23 - Coding on Async Task Service
48 | Workshop 24 - Coding on Parallel Task Service
49 |
50 |
51 | Lesson 2.3 Service Startup and Teardown
52 | Workshop 25 - Composing service & start service & teardown service
53 |
54 | Chapter 3. Service Deployment and Scale
55 | Lesson 3.1 Service Deployment
56 | Workshop 26 - Deploy Service in Kubernetes
57 | Workshop 27 - Setup Service Health check
58 | Workshop 28 - Scale Service
59 |
60 | Chapter 4. Implement Real-life high load application
61 | Lesson 4.1 Thai Citizen ID Card Register (TCIR) Application architecture
62 |
63 |
64 | Lesson 4.2 Thai Citizen ID Card Register (TCIR) Application
65 | Workshop 29 - Coding, Building and Deploy TCIR Application in K8S
66 | Workshop 30 - Scale TCIR Application in K8S
67 |
68 | By Chaiyapong Lapliengtrakul
69 | ไชยพงศ์ ลาภเลี้ยงตระกูล
70 | All right reserved
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/01-install-golang/README:
--------------------------------------------------------------------------------
1 | Install Golang
2 |
3 | 1. Install Golang by Google "install golang"
4 |
5 | 2. Download installer according to your platform (Mac, Windows, Ubuntu, etc)
6 |
7 | 3. After install open terminal and run command
8 | $ go version
9 | go version go1.15.7 darwin/amd64
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/02-first-program/README:
--------------------------------------------------------------------------------
1 | First Program
2 |
3 | 0. git clone git@github.com:3dsinteractive/automation-scalable-technology-with-golang-and-k8s-part-1.git
4 | Directory structure will follow each lesson respectively
5 |
6 | 1. Download and install VSCode (Use Google to search for "install vscode")
7 |
8 | 2. Open vscode at directory chapter01-basic-knowledges/1.1-basic-golang/02-first-program
9 |
10 | 3. Open terminal in VSCode using (Ctrl + ~)
11 |
12 | 4. Run command
13 | $ go mod init automationworkshop/main
14 | go: creating new go.mod: module automationworkshop/main
15 |
16 | 5. Run command
17 | $ go run main.go
18 | My First Program
19 | This is from MyPrintln
20 |
21 | 6. Program are described by the comment in sourcecode
22 |
23 |
24 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/02-first-program/formatter/index.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import "fmt"
4 |
5 | // MyPrintln will print input string
6 | func MyPrintln(input string) {
7 | myPrintln(input)
8 | }
9 |
10 | // myPrintln will not export
11 | func myPrintln(input string) {
12 | fmt.Println(input)
13 | }
14 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/02-first-program/main.go:
--------------------------------------------------------------------------------
1 | // 1. Package name must be main for main package
2 | package main
3 |
4 | // 2. Import section is the list of package dependency
5 | import (
6 | "automationworkshop/main/formatter"
7 | "fmt"
8 | )
9 |
10 | // 3. main() is the function where application start
11 | func main() {
12 | fmt.Println("My First Program")
13 |
14 | formatter.MyPrintln("This is from MyPrintln")
15 | // This function is not export
16 | // formatter.myPrintln("This is from MyPrintln")
17 | }
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/03-variable-types/README:
--------------------------------------------------------------------------------
1 | Variables and Types
2 |
3 | 1. Open vscode at directory chapter01-basic-knowledges/1.1-basic-golang/03-variable-types
4 |
5 | 2. Open terminal in VSCode using (Shortcut Ctrl + ~)
6 |
7 | 3. Run command
8 | $ go mod init automationworkshop/main
9 | go: creating new go.mod: module automationworkshop/main
10 |
11 | 4. Run command
12 | $ go run main.go
13 | string = This variable type string
14 | int = 305
15 | bool = true
16 | ---
17 | interface = This variable type string
18 | interface = 305
19 | interface = true
20 | ---
21 | map = map[citizen_id:1234 firstname:Chaiyapong lastname:Lapliengtrakul]
22 | map JSON = {"citizen_id":"1234","firstname":"Chaiyapong","lastname":"Lapliengtrakul"}
23 | No Gender specify
24 | ---
25 | slice = [item 1 item 2 item 3]
26 | slice JSON = ["item 1","item 2","item 3"]
27 | ---
28 | Citizen = &{Chaiyapong Lapliengtrakul 1234}
29 | Citizen JSON = {"firstname":"Chaiyapong","lastname":"Lapliengtrakul","citizen_id":"1234"}
30 | ---
31 | theMap is nil
32 | theCitizen is nil
33 | ---
34 | Gender is Unspecify
35 | Gender = UNSPECIFY
36 | ---
37 |
38 | 5. Sourcecode are described by the comments
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/04-if-and-loop/README:
--------------------------------------------------------------------------------
1 | If and Loop
2 |
3 | 1. Open vscode in chapter01-basic-knowledges/1.1-basic-golang/04-if-and-loop
4 |
5 | 2. Run command
6 | $ go mod init automationworkshop/main
7 | go: creating new go.mod: module automationworkshop/main
8 |
9 | 3. Run Program
10 | $ go run main.go
11 | CitizenID 1234, you are logged in
12 | ---
13 | You are Female
14 | ---
15 | Loop i=0
16 | Loop i=1
17 | Loop i=2
18 | Loop i=3
19 | Loop i=4
20 | Loop i=5
21 | Loop i=6
22 | Loop i=7
23 | Loop i=8
24 | Loop i=9
25 | ---
26 | Country 0=Thailand
27 | Country 1=Japan
28 | Country 2=China
29 | Country 3=Korea
30 | Country 4=Vietnam
31 | ---
32 | For i=1
33 | For i=2
34 | For i=3
35 | For i=4
36 | For i=5
37 | For i=6
38 | For i=7
39 | For i=8
40 | For i=9
41 | For i=10
42 | ---
43 | For n=1
44 | For n=2
45 | For n=3
46 | For n=4
47 | For n=5
48 | For n=6
49 | For n=7
50 | For n=8
51 | For n=9
52 | For n=10
53 | For n=11
54 |
55 | 4. Explain are in the source code comment
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/05-interface-struct/README:
--------------------------------------------------------------------------------
1 | Interface and struct
2 |
3 | 1. Open vscode at chapter01-basic-knowledges/1.1-basic-golang/05-interface-struct
4 |
5 | 2. Run command
6 | $ go mod init automationworkshop/main
7 |
8 | 3. Run program
9 | $ go run main.go
10 | Successfully create citizen card for ID=1122334455
11 | ---
12 |
13 | 4. Code explain in comments
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.1-basic-golang/06-go-routine/README:
--------------------------------------------------------------------------------
1 | Go routine and Go Channel
2 |
3 | 0. See what is Go Routine and Go Channel in Slides
4 |
5 | 1. Open vscode at chapter01-basic-knowledges/1.1-basic-golang/06-go-routine
6 |
7 | 2. Run command
8 | $ go mod init automationworkshop/main
9 |
10 | 3. Run program
11 | $ go run main.go
12 |
13 | 4. Code are explained in comments.
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/01-install-docker/README:
--------------------------------------------------------------------------------
1 | Install Docker
2 |
3 | 1. Install docker from https://docs.docker.com/get-docker/
4 |
5 | 2. Double click Docker icon in Application directory, wait until docker is running in menu bar
6 |
7 | 3. After install open terminal and run command
8 | $ docker version
9 | Client: Docker Engine - Community
10 | Version: 20.10.2
11 | API version: 1.41
12 | Go version: go1.13.15
13 | Git commit: 2291f61
14 | Built: Mon Dec 28 16:12:42 2020
15 | OS/Arch: darwin/amd64
16 | Context: default
17 | Experimental: true
18 |
19 | Server: Docker Engine - Community
20 | Engine:
21 | Version: 20.10.2
22 | API version: 1.41 (minimum version 1.12)
23 | Go version: go1.13.15
24 | Git commit: 8891c58
25 | Built: Mon Dec 28 16:15:28 2020
26 | OS/Arch: linux/amd64
27 | Experimental: false
28 | containerd:
29 | Version: 1.4.3
30 | GitCommit: 269548fa27e0089a8b8278fc4fc781d7f65a939b
31 | runc:
32 | Version: 1.0.0-rc92
33 | GitCommit: ff819c7e9184c13b7c2607fe6c30ae19403a7aff
34 | docker-init:
35 | Version: 0.19.0
36 | GitCommit: de40ad0
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/02-run-nginx-with-docker/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | nginx:
4 | image: 3dsinteractive/nginx:1.12
5 | ports:
6 | - 8080:8080
7 | - 8443:8443
8 | restart: always
9 | volumes:
10 | - ./dockers/nginx/nginx:/bitnami/nginx
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/03-build-docker-image/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM 3dsinteractive/nginx:1.12
2 |
3 | COPY ./html /app
4 |
5 | USER 1001
6 |
7 | EXPOSE 8080
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/03-build-docker-image/README:
--------------------------------------------------------------------------------
1 | Build docker image
2 |
3 | 1. Open vscode at chapter01-basic-knowledges/1.2-basic-docker/03-build-docker-image
4 |
5 | 2. Run command
6 | $ docker build -t 3dsinteractive/mynginx:1.0 .
7 |
8 | 3. Run command
9 | $ docker-compose up -d
10 | Creating network "03-build-docker-image_default" with the default driver
11 | Creating 03-build-docker-image_nginx_1 ... done
12 |
13 | 4. Run command
14 | $ curl -X GET localhost:8080/index.html
15 |
16 |
17 |
18 | Hello world
19 |
20 |
21 |
22 | 5. Explain Dockerfile
23 |
24 |
25 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/03-build-docker-image/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | nginx:
4 | image: 3dsinteractive/mynginx:1.0
5 | ports:
6 | - 8080:8080
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/03-build-docker-image/html/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Hello world
5 |
6 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/04-push-docker-image/README:
--------------------------------------------------------------------------------
1 | Push docker image
2 |
3 | 0. Look at slides 1.2 Basic Docker (Docker images and containers)
4 |
5 | 1. Register for https://hub.docker.com
6 |
7 | 2. Create public repository call [your-repository-name]/mynginx:1.0
8 |
9 | 3. docker login
10 | [username] : [your-repository-name]
11 | [password] : xxxxxxxxx
12 |
13 | 4. Run command (Change [your-repository-name] to your registered docker name)
14 | $ docker push [your-repository-name]/mynginx:1.0
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/README:
--------------------------------------------------------------------------------
1 | Run Docker compose
2 |
3 | 1. Open vscode at chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose
4 |
5 | 2. Run command
6 | $ docker-compose up -d
7 | $ docker ps
8 |
9 | 3. Run command
10 | $ curl -X GET "localhost:8080/index.html"
11 |
12 | 4. Explain section in docker-compose.yml
13 |
14 | 5. Run command
15 | $ docker-compose down
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | lb:
4 | image: 3dsinteractive/nginx:1.12
5 | depends_on:
6 | - mynginx1
7 | - mynginx2
8 | ports:
9 | - 8080:8080
10 | - 8443:8443
11 | restart: always
12 | volumes:
13 | - ./dockers/lb/logs:/opt/bitnami/nginx/logs
14 | - ./dockers/lb/nginx:/bitnami/nginx
15 | mynginx1:
16 | image: 3dsinteractive/mynginx:1.0
17 | mynginx2:
18 | image: 3dsinteractive/mynginx:1.0
19 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/logs/access.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3dsinteractive/automation-scalable-technology-with-golang-and-k8s-part-1/2ce397b7fb04caf3f2cf5516a7e6c4fab2d45a70/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/logs/access.log
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/logs/error.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3dsinteractive/automation-scalable-technology-with-golang-and-k8s-part-1/2ce397b7fb04caf3f2cf5516a7e6c4fab2d45a70/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/logs/error.log
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/bitnami/certs/server.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIICqDCCAZACCQCz8T3726LYsjANBgkqhkiG9w0BAQUFADAWMRQwEgYDVQQDDAtl
3 | eGFtcGxlLmNvbTAeFw0xMjExMTQxMTE4MjdaFw0yMjExMTIxMTE4MjdaMBYxFDAS
4 | BgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
5 | AQEA5NHl5TfZtO6zugau2tp5mWIcQYJhuwKTmYeXDLYAGJpoD2SixwPL5c8glneI
6 | Rz1N2EQIZVeaWGbS0FLFlPdOkCkplpW9isYVC4XqKrk5b4HW4+YC+Cup0k+Kd4NM
7 | eZOTUvWr5N6dIpdibkVumBc/pao8VtdwywlCL/PwGRsQtkXrRICzdtRa3MXqTmEF
8 | foyVCGgBRtronlB9x4Plfb8Psk4GrPkjrWYgO8peKrl0O5+F+sYg7Gj95zCH73BQ
9 | ANzCVNrgD9fs9cyx3ru9CUdEoIxAAJwQFkjm7xr6xqhIlSgnQ7B0uOSTNRcXY6rw
10 | s+PxGneec/kRPRgzjC/QHY6n8QIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBbyMqF
11 | RDsX8zX1EW5qA8AQ8Jb2XqWrVeSO8blMV3WagJ2airMm3+c/82FCwsd/cZ08UXhA
12 | /Kou0gi/F16tV26PiiUdp590Qao3d8H2qxc1rzzULimZPgxH4iA4vRyMHtyZN6h4
13 | 7Fdn7O9xNMPu8siOz8rrzsEdEX5URbOMkDLCZsbTIUWVv2XmqrR0K10d5VuLWeLi
14 | r+4G6c6jpa244WmqT9ClqceJ12G1Wnmezy7ybiW0l5M2iuIKFEiRP5Hj0J15o1I2
15 | pXAbKysAdWRHsJSQOtcgO8Vh9k0wo3tKg4HDp1hbrEzoGzOv92Vjg3lG8X+hzbMJ
16 | MQURotHkD4Gk57wL
17 | -----END CERTIFICATE-----
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/bitnami/certs/server.key:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIIEpAIBAAKCAQEA5NHl5TfZtO6zugau2tp5mWIcQYJhuwKTmYeXDLYAGJpoD2Si
3 | xwPL5c8glneIRz1N2EQIZVeaWGbS0FLFlPdOkCkplpW9isYVC4XqKrk5b4HW4+YC
4 | +Cup0k+Kd4NMeZOTUvWr5N6dIpdibkVumBc/pao8VtdwywlCL/PwGRsQtkXrRICz
5 | dtRa3MXqTmEFfoyVCGgBRtronlB9x4Plfb8Psk4GrPkjrWYgO8peKrl0O5+F+sYg
6 | 7Gj95zCH73BQANzCVNrgD9fs9cyx3ru9CUdEoIxAAJwQFkjm7xr6xqhIlSgnQ7B0
7 | uOSTNRcXY6rws+PxGneec/kRPRgzjC/QHY6n8QIDAQABAoIBACo3G131tuGtpFTu
8 | xLW11vdYZXQklNlGuWp63IBI162yVv54B5wF9Ek6tH1uIiNaiREcRBxGVEB4/+3V
9 | R4SbN9Ba98RDbgu7TcipdTFaqOEMqFO1bNjSXWtip14zSBmqA2Ur1AHOnFj0awGD
10 | J8tBhsmOpcEz0Ch1VdO5ApPvLV8jH9wQiMI/Q6yYQMtmzTMCUMYdMqe+LOziIOzL
11 | oqN/WXnKL5E5TiO1bIxSpWPbT+IVn1c3/PShmvmRrLWsFUQlkwXJKMYZPO+rCCfe
12 | b+Q9lMLMnj+vOnM3z16WC3aiiJGCZjVTvQ+x22YrBTRPxZmHO2eZ4H/cUQM7Y/tw
13 | I7RjEM0CgYEA9Kxt1t8bWonzBii3P0rwyx0IECvg63k+pp4BpxpeWQKL7NVdSzk3
14 | AyJVcNjUoZgi2kVPdxzZGLrnZfuZ691xQB3oZF0LwBzQ4GFHkTRCB0s8ZA5lcJaI
15 | 9pBu91bhz2VOZSTeQWpdMMURjXVyTXZInU1mwzmjVOIAYmO33shH9gcCgYEA72mX
16 | UoIrFPLkOTSZOb7UbjYH01vf6ThQiYCEWg7mD3CbY7n9oobIcQMzNnt7xN4wOl/V
17 | eKfZ7G56q8enfqm45Dyo9aCBCENVzmwO8wLe5UnvJBNL20KjvtwG8w5A6UZQzC7p
18 | 3QS+U2zxVQNEeaE6a8Wrq2d1PlhVAHYw8odgNEcCgYBN38+58xrmrz99d1oTuAt5
19 | 6kyVsRGOgPGS4HmQMRFUbT4R7DscZSKASd4945WRtTVqmWLYe4MRnvNlfzYXX0zb
20 | ZmmAAClsRP+qWuwHaEWXwrd+9SIOOqtvJrta1/lZJFpWUOy4j10H18Flb7sosnwc
21 | LPWHL4Iv0xriNfDg5Iga4wKBgQDLJBU59SkJBW+Q+oho7vrg6QeK15IOGbJ8eYfT
22 | woCC6VFwNQh5N1QsUELMH8rNKJpTba18SzAl5ThBOY9tciVnw/C5Og9CK6BLHnUw
23 | zWbDtxAq1BSxXsIB2EAtTBLX3MoB9myJFNVJhE7hi3w2mA8yEu+u6IIa/Ghjk+XE
24 | ZAnFUQKBgQDjMinRZrK5wA09jcetI+dNiLnKHoQG6OaXDDsNCatex0O2F36BvVXE
25 | P78qDz/i5aBMWsLx6VDvWJAkBIpZoNS5UsOn17tFaocGUSkcm48bs8Dn6VvsE8Bd
26 | XMPAHyKuILlKYifBvNq5T22KhqKX7yGmk/AeOOiKr2KeMnh27JYrCA==
27 | -----END RSA PRIVATE KEY-----
28 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/fastcgi.conf:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
3 | fastcgi_param QUERY_STRING $query_string;
4 | fastcgi_param REQUEST_METHOD $request_method;
5 | fastcgi_param CONTENT_TYPE $content_type;
6 | fastcgi_param CONTENT_LENGTH $content_length;
7 |
8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
9 | fastcgi_param REQUEST_URI $request_uri;
10 | fastcgi_param DOCUMENT_URI $document_uri;
11 | fastcgi_param DOCUMENT_ROOT $document_root;
12 | fastcgi_param SERVER_PROTOCOL $server_protocol;
13 | fastcgi_param REQUEST_SCHEME $scheme;
14 | fastcgi_param HTTPS $https if_not_empty;
15 |
16 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
17 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
18 |
19 | fastcgi_param REMOTE_ADDR $remote_addr;
20 | fastcgi_param REMOTE_PORT $remote_port;
21 | fastcgi_param SERVER_ADDR $server_addr;
22 | fastcgi_param SERVER_PORT $server_port;
23 | fastcgi_param SERVER_NAME $server_name;
24 |
25 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
26 | fastcgi_param REDIRECT_STATUS 200;
27 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/fastcgi.conf.default:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
3 | fastcgi_param QUERY_STRING $query_string;
4 | fastcgi_param REQUEST_METHOD $request_method;
5 | fastcgi_param CONTENT_TYPE $content_type;
6 | fastcgi_param CONTENT_LENGTH $content_length;
7 |
8 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
9 | fastcgi_param REQUEST_URI $request_uri;
10 | fastcgi_param DOCUMENT_URI $document_uri;
11 | fastcgi_param DOCUMENT_ROOT $document_root;
12 | fastcgi_param SERVER_PROTOCOL $server_protocol;
13 | fastcgi_param REQUEST_SCHEME $scheme;
14 | fastcgi_param HTTPS $https if_not_empty;
15 |
16 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
17 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
18 |
19 | fastcgi_param REMOTE_ADDR $remote_addr;
20 | fastcgi_param REMOTE_PORT $remote_port;
21 | fastcgi_param SERVER_ADDR $server_addr;
22 | fastcgi_param SERVER_PORT $server_port;
23 | fastcgi_param SERVER_NAME $server_name;
24 |
25 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
26 | fastcgi_param REDIRECT_STATUS 200;
27 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/fastcgi_params:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param QUERY_STRING $query_string;
3 | fastcgi_param REQUEST_METHOD $request_method;
4 | fastcgi_param CONTENT_TYPE $content_type;
5 | fastcgi_param CONTENT_LENGTH $content_length;
6 |
7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
8 | fastcgi_param REQUEST_URI $request_uri;
9 | fastcgi_param DOCUMENT_URI $document_uri;
10 | fastcgi_param DOCUMENT_ROOT $document_root;
11 | fastcgi_param SERVER_PROTOCOL $server_protocol;
12 | fastcgi_param REQUEST_SCHEME $scheme;
13 | fastcgi_param HTTPS $https if_not_empty;
14 |
15 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
16 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
17 |
18 | fastcgi_param REMOTE_ADDR $remote_addr;
19 | fastcgi_param REMOTE_PORT $remote_port;
20 | fastcgi_param SERVER_ADDR $server_addr;
21 | fastcgi_param SERVER_PORT $server_port;
22 | fastcgi_param SERVER_NAME $server_name;
23 |
24 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
25 | fastcgi_param REDIRECT_STATUS 200;
26 | fastcgi_param HTTP_PROXY "";
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/fastcgi_params.default:
--------------------------------------------------------------------------------
1 |
2 | fastcgi_param QUERY_STRING $query_string;
3 | fastcgi_param REQUEST_METHOD $request_method;
4 | fastcgi_param CONTENT_TYPE $content_type;
5 | fastcgi_param CONTENT_LENGTH $content_length;
6 |
7 | fastcgi_param SCRIPT_NAME $fastcgi_script_name;
8 | fastcgi_param REQUEST_URI $request_uri;
9 | fastcgi_param DOCUMENT_URI $document_uri;
10 | fastcgi_param DOCUMENT_ROOT $document_root;
11 | fastcgi_param SERVER_PROTOCOL $server_protocol;
12 | fastcgi_param REQUEST_SCHEME $scheme;
13 | fastcgi_param HTTPS $https if_not_empty;
14 |
15 | fastcgi_param GATEWAY_INTERFACE CGI/1.1;
16 | fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
17 |
18 | fastcgi_param REMOTE_ADDR $remote_addr;
19 | fastcgi_param REMOTE_PORT $remote_port;
20 | fastcgi_param SERVER_ADDR $server_addr;
21 | fastcgi_param SERVER_PORT $server_port;
22 | fastcgi_param SERVER_NAME $server_name;
23 |
24 | # PHP only, required if PHP was built with --enable-force-cgi-redirect
25 | fastcgi_param REDIRECT_STATUS 200;
26 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/scgi_params:
--------------------------------------------------------------------------------
1 |
2 | scgi_param REQUEST_METHOD $request_method;
3 | scgi_param REQUEST_URI $request_uri;
4 | scgi_param QUERY_STRING $query_string;
5 | scgi_param CONTENT_TYPE $content_type;
6 |
7 | scgi_param DOCUMENT_URI $document_uri;
8 | scgi_param DOCUMENT_ROOT $document_root;
9 | scgi_param SCGI 1;
10 | scgi_param SERVER_PROTOCOL $server_protocol;
11 | scgi_param REQUEST_SCHEME $scheme;
12 | scgi_param HTTPS $https if_not_empty;
13 |
14 | scgi_param REMOTE_ADDR $remote_addr;
15 | scgi_param REMOTE_PORT $remote_port;
16 | scgi_param SERVER_PORT $server_port;
17 | scgi_param SERVER_NAME $server_name;
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/scgi_params.default:
--------------------------------------------------------------------------------
1 |
2 | scgi_param REQUEST_METHOD $request_method;
3 | scgi_param REQUEST_URI $request_uri;
4 | scgi_param QUERY_STRING $query_string;
5 | scgi_param CONTENT_TYPE $content_type;
6 |
7 | scgi_param DOCUMENT_URI $document_uri;
8 | scgi_param DOCUMENT_ROOT $document_root;
9 | scgi_param SCGI 1;
10 | scgi_param SERVER_PROTOCOL $server_protocol;
11 | scgi_param REQUEST_SCHEME $scheme;
12 | scgi_param HTTPS $https if_not_empty;
13 |
14 | scgi_param REMOTE_ADDR $remote_addr;
15 | scgi_param REMOTE_PORT $remote_port;
16 | scgi_param SERVER_PORT $server_port;
17 | scgi_param SERVER_NAME $server_name;
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/uwsgi_params:
--------------------------------------------------------------------------------
1 |
2 | uwsgi_param QUERY_STRING $query_string;
3 | uwsgi_param REQUEST_METHOD $request_method;
4 | uwsgi_param CONTENT_TYPE $content_type;
5 | uwsgi_param CONTENT_LENGTH $content_length;
6 |
7 | uwsgi_param REQUEST_URI $request_uri;
8 | uwsgi_param PATH_INFO $document_uri;
9 | uwsgi_param DOCUMENT_ROOT $document_root;
10 | uwsgi_param SERVER_PROTOCOL $server_protocol;
11 | uwsgi_param REQUEST_SCHEME $scheme;
12 | uwsgi_param HTTPS $https if_not_empty;
13 |
14 | uwsgi_param REMOTE_ADDR $remote_addr;
15 | uwsgi_param REMOTE_PORT $remote_port;
16 | uwsgi_param SERVER_PORT $server_port;
17 | uwsgi_param SERVER_NAME $server_name;
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/uwsgi_params.default:
--------------------------------------------------------------------------------
1 |
2 | uwsgi_param QUERY_STRING $query_string;
3 | uwsgi_param REQUEST_METHOD $request_method;
4 | uwsgi_param CONTENT_TYPE $content_type;
5 | uwsgi_param CONTENT_LENGTH $content_length;
6 |
7 | uwsgi_param REQUEST_URI $request_uri;
8 | uwsgi_param PATH_INFO $document_uri;
9 | uwsgi_param DOCUMENT_ROOT $document_root;
10 | uwsgi_param SERVER_PROTOCOL $server_protocol;
11 | uwsgi_param REQUEST_SCHEME $scheme;
12 | uwsgi_param HTTPS $https if_not_empty;
13 |
14 | uwsgi_param REMOTE_ADDR $remote_addr;
15 | uwsgi_param REMOTE_PORT $remote_port;
16 | uwsgi_param SERVER_PORT $server_port;
17 | uwsgi_param SERVER_NAME $server_name;
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/05-run-docker-compose/dockers/lb/nginx/conf/vhosts/app.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 8080;
3 | index index.html index.htm;
4 | add_header 'X-Frame-Options' 'AllowAll';
5 | location / {
6 | resolver 127.0.0.11 valid=30s;
7 | proxy_redirect off;
8 | proxy_set_header Host $host;
9 | proxy_set_header X-Forwarded-Proto $scheme;
10 | proxy_set_header X-Real-IP $remote_addr;
11 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
12 | set $goto mynginx;
13 | proxy_pass http://$goto;
14 | }
15 | }
16 | upstream mynginx {
17 | server mynginx1:8080;
18 | server mynginx2:8080;
19 | }
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/extra-image-nginx-1.12/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM bitnami/minideb-extras:jessie-r20
2 | LABEL maintainer "Bitnami "
3 |
4 | ENV BITNAMI_PKG_CHMOD="-R g+rwX" \
5 | BITNAMI_PKG_EXTRA_DIRS="/bitnami/nginx/conf" \
6 | HOME="/"
7 |
8 | # Install required system packages and dependencies
9 | RUN install_packages libc6 libpcre3 libssl1.0.0 zlib1g
10 | RUN bitnami-pkg unpack nginx-1.12.1-2 --checksum 4ed9d05706a333d9d7480ef510025cc550d09d25f321632eb1cf7d037a507deb
11 | RUN ln -sf /opt/bitnami/nginx/html /app
12 | RUN ln -sf /dev/stdout /opt/bitnami/nginx/logs/access.log
13 | RUN ln -sf /dev/stdout /opt/bitnami/nginx/logs/error.log
14 |
15 | COPY rootfs /
16 |
17 | ENV BITNAMI_APP_NAME="nginx" \
18 | BITNAMI_IMAGE_VERSION="1.12.1-r2" \
19 | NGINX_DAEMON_GROUP="" \
20 | NGINX_DAEMON_USER="" \
21 | NGINX_HTTPS_PORT_NUMBER="8443" \
22 | NGINX_HTTP_PORT_NUMBER="8080" \
23 | PATH="/opt/bitnami/nginx/sbin:$PATH"
24 |
25 | EXPOSE 8080 8443
26 |
27 | WORKDIR /app
28 | USER 1001
29 | ENTRYPOINT ["/app-entrypoint.sh"]
30 | CMD ["nginx","-g","daemon off;"]
31 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/extra-image-nginx-1.12/rootfs/app-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | . /opt/bitnami/base/functions
4 | . /opt/bitnami/base/helpers
5 |
6 | print_welcome_page
7 | check_for_updates &
8 |
9 | if [[ "$1" == "nami" && "$2" == "start" ]] || [[ "$1" == "nginx" ]]; then
10 | nami_initialize nginx
11 | info "Starting nginx... "
12 | fi
13 |
14 | exec tini -- "$@"
15 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.2-basic-docker/extra-image-nginx-1.12/rootfs/nginx-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "httpPort": "{{$global.env.NGINX_HTTP_PORT_NUMBER}}",
3 | "httpsPort": "{{$global.env.NGINX_HTTPS_PORT_NUMBER}}",
4 | "systemGroup": "{{$global.env.NGINX_DAEMON_GROUP}}",
5 | "systemUser": "{{$global.env.NGINX_DAEMON_USER}}"
6 | }
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.3-basic-kubernetes/02-run-nginx-with-k8s/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: basic-k8s
5 | labels:
6 | name: basic-k8s
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.3-basic-kubernetes/02-run-nginx-with-k8s/01-deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: nginx
5 | namespace: basic-k8s
6 | labels:
7 | name: nginx
8 | spec:
9 | replicas: 1
10 | strategy:
11 | type: Recreate
12 | selector:
13 | matchLabels:
14 | name: nginx
15 | template:
16 | metadata:
17 | labels:
18 | name: nginx
19 | spec:
20 | containers:
21 | - name: nginx
22 | image: 3dsinteractive/nginx:1.12
23 | imagePullPolicy: Always
24 | resources:
25 | requests:
26 | memory: 500Mi
27 | cpu: 200m
28 | limits:
29 | memory: 1Gi
30 | cpu: 500m
31 | livenessProbe:
32 | tcpSocket:
33 | port: 8080
34 | initialDelaySeconds: 5
35 | timeoutSeconds: 1
36 | periodSeconds: 300
37 | readinessProbe:
38 | tcpSocket:
39 | port: 8080
40 | initialDelaySeconds: 5
41 | timeoutSeconds: 1
42 | periodSeconds: 30
43 | failureThreshold: 5
44 | ports:
45 | - containerPort: 8080
46 | name: ngx8080
47 | - containerPort: 8443
48 | name: ngx8443
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.3-basic-kubernetes/02-run-nginx-with-k8s/02-service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: nginx
5 | namespace: basic-k8s
6 | labels:
7 | name: nginx
8 | spec:
9 | selector:
10 | name: nginx
11 | ports:
12 | - name: ngx8080
13 | port: 8080
14 | targetPort: 8080
15 | protocol: TCP
16 | - name: ngx8443
17 | port: 8443
18 | targetPort: 8443
19 | protocol: TCP
20 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.3-basic-kubernetes/02-run-nginx-with-k8s/03-ingress.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Ingress
3 | metadata:
4 | name: ingress
5 | namespace: basic-k8s
6 | labels:
7 | name: ingress
8 | spec:
9 | rules:
10 | - host: kubernetes.docker.internal
11 | http:
12 | paths:
13 | - path: /
14 | backend:
15 | serviceName: nginx
16 | servicePort: 8080
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/01-run-redis/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: basic-redis
5 | labels:
6 | name: basic-redis
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/01-run-redis/01-deployment.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: redis
5 | namespace: basic-redis
6 | labels:
7 | name: redis
8 | spec:
9 | replicas: 1
10 | strategy:
11 | type: Recreate
12 | selector:
13 | matchLabels:
14 | name: redis
15 | template:
16 | metadata:
17 | labels:
18 | name: redis
19 | spec:
20 | containers:
21 | - name: redis
22 | image: 3dsinteractive/redis:5.0
23 | imagePullPolicy: Always
24 | ports:
25 | - name: redis6379
26 | containerPort: 6379
27 | env:
28 | - name: ALLOW_EMPTY_PASSWORD
29 | value: "yes"
30 | resources:
31 | requests:
32 | memory: 500Mi
33 | cpu: 200m
34 | limits:
35 | memory: 1Gi
36 | cpu: 500m
37 | livenessProbe:
38 | tcpSocket:
39 | port: 6379
40 | initialDelaySeconds: 30
41 | timeoutSeconds: 1
42 | periodSeconds: 300
43 | readinessProbe:
44 | tcpSocket:
45 | port: 6379
46 | initialDelaySeconds: 30
47 | timeoutSeconds: 1
48 | periodSeconds: 30
49 | failureThreshold: 5
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/01-run-redis/02-service.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: redis
5 | namespace: basic-redis
6 | labels:
7 | name: redis
8 | spec:
9 | selector:
10 | name: redis
11 | ports:
12 | - name: redis6379
13 | port: 6379
14 | targetPort: 6379
15 | protocol: TCP
16 | clusterIP: None
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/01-run-redis/03-client-util.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: client-util
5 | namespace: basic-redis
6 | labels:
7 | name: client-util
8 | spec:
9 | containers:
10 | - name: client-util
11 | image: opcellent/util:2.0
12 | stdin: true
13 | tty: true
14 | imagePullPolicy: Always
15 | resources:
16 | requests:
17 | memory: 500Mi
18 | cpu: 200m
19 | limits:
20 | memory: 1Gi
21 | cpu: 500m
22 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/01-run-redis/README:
--------------------------------------------------------------------------------
1 | Run Redis
2 | ===
3 |
4 | 1. Enter workshop dir
5 | $ cd chapter01-basic-knowledges/1.4-basic-redis/01-run-redis
6 |
7 | 2. Create k8s namespace
8 | $ kubectl apply -f 00-namespace.yml
9 | namespace/basic-redis created
10 |
11 | 3. Check if namespace has created
12 | $ kubectl get ns
13 | NAME STATUS AGE
14 | basic-redis Active 20s
15 | default Active 7h59m
16 | ingress-nginx Active 46m
17 | kube-node-lease Active 7h59m
18 | kube-public Active 7h59m
19 | kube-system Active 7h59m
20 |
21 | 4. Create redis deployment
22 | $ kubectl apply -f 01-deployment.yml
23 | deployment.apps/redis created
24 |
25 | 5. Check redis deployment has created
26 | $ kubectl get po -n basic-redis
27 | NAME READY STATUS RESTARTS AGE
28 | redis-577d58dd6c-g7brv 0/1 Running 0 26s
29 |
30 | ** Wait until the STATUS is Running
31 |
32 | 6. Create redis service
33 | $ kubectl apply -f 02-service.yml
34 | service/redis created
35 |
36 | 7. Check redis service has created
37 | $ kubectl get svc -n basic-redis
38 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
39 | redis ClusterIP None 6379/TCP 19s
40 |
41 | 8. Create client-util pod
42 | $ kubectl apply -f 03-client-util.yml
43 | pod/client-util created
44 |
45 | 9. Check if client-util pod has created
46 | $ kubectl get po -n basic-redis
47 | NAME READY STATUS RESTARTS AGE
48 | client-util 1/1 Running 0 96s
49 | redis-577d58dd6c-g7brv 1/1 Running 0 3m54s
50 |
51 | ** Wait until the STATUS is Running
52 |
53 | 10. Exec into client-util pod
54 | $ kubectl exec -it client-util -n basic-redis -- bash
55 | root@client-util:/#
56 |
57 | 11. Run redis-cli to connect to redis
58 | # redis-cli -h redis
59 | redis:6379>
60 |
61 | 12. Exit from redis-cli
62 | # exit
63 | root@client-util:/#
64 |
65 | 13. Exit from client-util
66 | # exit
67 |
68 | 14. Do not cleanup workshop, we will use it in next workshop
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/02-use-redis-cli/README:
--------------------------------------------------------------------------------
1 | Use Redis cli
2 | ===
3 |
4 | 1. Exec into client-util pod
5 | $ kubectl exec -it client-util -n basic-redis -- bash
6 |
7 | 2. Start redis-cli
8 | $ redis-cli -h redis
9 | redis:6379>
10 |
11 | 3. Use SET command to set mykey = myvalue
12 | $ SET "mykey" "myvalue"
13 | OK
14 |
15 | 4. Use GET to get value from key
16 | $ GET "mykey"
17 | "myvalue"
18 |
19 | 5. Use EXPIRE command to expire mykey in 10 seconds
20 | $ EXPIRE "mykey" 10
21 | (integer) 1
22 |
23 | ** Wait 10 seconds
24 |
25 | 6. Test get mykey when it is expired
26 | $ GET "mykey"
27 | (nil)
28 |
29 | 7. SET mykey2
30 | $ SET "mykey2" "myvalue2"
31 | OK
32 |
33 | 8. GET mykey2
34 | $ GET "mykey2"
35 | "myvalue2"
36 |
37 | 9. Use DEL command to delete mykey2
38 | $ DEL "mykey2"
39 | (integer) 1
40 |
41 | 10. GET mykey2 to see how it is deleted
42 | $ GET "mykey2"
43 | (nil)
44 |
45 | 11. SET mykey1 and mykey2
46 | $ SET "mykey1" "value1"
47 | $ SET "mykey2" "value2"
48 |
49 | 12. Use KEYS to list all keys using wildcard
50 | $ KEYS "mykey*"
51 | 1) "mykey2"
52 | 2) "mykey1"
53 |
54 | 13. Exit from redis
55 | $ exit
56 |
57 | 14. Exit from client-util
58 | $ exit
59 |
60 | 15. Cleanup workshop
61 | $ kubectl delete ns basic-redis
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/extra-redis-image/redis-5.0/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM bitnami/minideb-extras-base:stretch-r266
2 | LABEL maintainer "Bitnami "
3 |
4 | ENV BITNAMI_PKG_CHMOD="-R g+rwX" \
5 | HOME="/" \
6 | OS_ARCH="amd64" \
7 | OS_FLAVOUR="debian-9" \
8 | OS_NAME="linux"
9 |
10 | # Install required system packages and dependencies
11 | RUN install_packages libc6
12 | RUN . ./libcomponent.sh && component_unpack "redis" "5.0.5-1" --checksum f43c5625691360ae2c5a43aebb772b9ae05a3268efabe5c0474e0804505f0a9a
13 |
14 | COPY rootfs /
15 | RUN /postunpack.sh
16 | ENV ALLOW_EMPTY_PASSWORD="no" \
17 | BITNAMI_APP_NAME="redis" \
18 | BITNAMI_IMAGE_VERSION="5.0.5-debian-9-r30" \
19 | NAMI_PREFIX="/.nami" \
20 | PATH="/opt/bitnami/redis/bin:$PATH" \
21 | REDIS_DISABLE_COMMANDS="" \
22 | REDIS_MASTER_HOST="" \
23 | REDIS_MASTER_PASSWORD="" \
24 | REDIS_MASTER_PASSWORD_FILE="" \
25 | REDIS_MASTER_PORT_NUMBER="6379" \
26 | REDIS_PASSWORD="" \
27 | REDIS_PASSWORD_FILE="" \
28 | REDIS_REPLICATION_MODE=""
29 |
30 | EXPOSE 6379
31 |
32 | USER 1001
33 | ENTRYPOINT [ "/entrypoint.sh" ]
34 | CMD [ "/run.sh" ]
35 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/extra-redis-image/redis-5.0/rootfs/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | set -o nounset
5 | set -o pipefail
6 | #set -o xtrace
7 | # shellcheck disable=SC1091
8 |
9 | # Load libraries
10 | . /libbitnami.sh
11 | . /libredis.sh
12 |
13 | # Load Redis environment variables
14 | eval "$(redis_env)"
15 |
16 | print_welcome_page
17 |
18 | if [[ "$*" = *"/run.sh"* ]]; then
19 | info "** Starting Redis setup **"
20 | /setup.sh
21 | info "** Redis setup finished! **"
22 | fi
23 |
24 | echo ""
25 | exec "$@"
26 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/extra-redis-image/redis-5.0/rootfs/postunpack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # shellcheck disable=SC1091
4 |
5 | # Load libraries
6 | . /libredis.sh
7 | . /libfs.sh
8 |
9 | # Load Redis environment variables
10 | eval "$(redis_env)"
11 |
12 | for dir in "$REDIS_VOLUME" "${REDIS_VOLUME}/data" ; do
13 | ensure_dir_exists "$dir"
14 | done
15 | chmod -R g+rwX /bitnami "$REDIS_VOLUME" "$REDIS_BASEDIR"
16 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/extra-redis-image/redis-5.0/rootfs/redis-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "allowEmptyPassword": "{{$global.env.ALLOW_EMPTY_PASSWORD}}",
3 | "disableCommands": "{{$global.env.REDIS_DISABLE_COMMANDS}}",
4 | "masterHost": "{{$global.env.REDIS_MASTER_HOST}}",
5 | "masterPassword": "{{$global.env.REDIS_MASTER_PASSWORD}}",
6 | "masterPasswordFileLocation": "{{$global.env.REDIS_MASTER_PASSWORD_FILE}}",
7 | "masterPort": "{{$global.env.REDIS_MASTER_PORT_NUMBER}}",
8 | "password": "{{$global.env.REDIS_PASSWORD}}",
9 | "passwordFileLocation": "{{$global.env.REDIS_PASSWORD_FILE}}",
10 | "replicationMode": "{{$global.env.REDIS_REPLICATION_MODE}}"
11 | }
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/extra-redis-image/redis-5.0/rootfs/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | set -o nounset
5 | set -o pipefail
6 | # set -o xtrace
7 | # shellcheck disable=SC1091
8 |
9 | # Load libraries
10 | . /libos.sh
11 | . /libredis.sh
12 |
13 | # Load Redis environment variables
14 | eval "$(redis_env)"
15 |
16 | # Constants
17 | REDIS_EXTRA_FLAGS=${REDIS_EXTRA_FLAGS:-}
18 | EXEC=$(command -v redis-server)
19 |
20 | args=("$REDIS_BASEDIR/etc/redis.conf" "--daemonize" "no" "$@")
21 | # configure extra command line flags
22 | if [[ -n "$REDIS_EXTRA_FLAGS" ]]; then
23 | warn "REDIS_EXTRA_FLAGS is deprecated. Please specify any extra-flag using '/run.sh $REDIS_EXTRA_FLAGS' as command instead"
24 | read -r -a envExtraFlags <<< "$REDIS_EXTRA_FLAGS"
25 | args+=("${envExtraFlags[@]}")
26 | fi
27 |
28 | info "** Starting Redis **"
29 | if am_i_root; then
30 | exec gosu "$REDIS_DAEMON_USER" "$EXEC" "${args[@]}"
31 | else
32 | exec "$EXEC" "${args[@]}"
33 | fi
34 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.4-basic-redis/extra-redis-image/redis-5.0/rootfs/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -o errexit
4 | set -o nounset
5 | set -o pipefail
6 | #set -o xtrace
7 | # shellcheck disable=SC1091
8 |
9 | # Load libraries
10 | . /libos.sh
11 | . /libfs.sh
12 | . /libredis.sh
13 |
14 | # Load Redis environment variables
15 | eval "$(redis_env)"
16 |
17 | # Ensure Redis environment variables settings are valid
18 | redis_validate
19 | # Ensure Redis is stopped when this script ends
20 | trap "redis_stop" EXIT
21 | am_i_root && ensure_user_exists "$REDIS_DAEMON_USER" "$REDIS_DAEMON_GROUP"
22 | # Ensure Redis is initialized
23 | redis_initialize
24 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/01-run-kafka/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: basic-kafka
5 | labels:
6 | name: basic-kafka
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/01-run-kafka/02-service-zk.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: zk1
5 | namespace: basic-kafka
6 | labels:
7 | name: zk1
8 | spec:
9 | selector:
10 | name: zk1
11 | ports:
12 | - name: zk2181
13 | port: 2181
14 | protocol: TCP
15 | - name: zk2888
16 | port: 2888
17 | protocol: TCP
18 | - name: zk3888
19 | port: 3888
20 | protocol: TCP
21 | clusterIP: None
22 | ---
23 | apiVersion: v1
24 | kind: Service
25 | metadata:
26 | name: zk2
27 | namespace: basic-kafka
28 | labels:
29 | name: zk2
30 | spec:
31 | selector:
32 | name: zk2
33 | ports:
34 | - name: zk2181
35 | port: 2181
36 | protocol: TCP
37 | - name: zk2888
38 | port: 2888
39 | protocol: TCP
40 | - name: zk3888
41 | port: 3888
42 | protocol: TCP
43 | clusterIP: None
44 | ---
45 | apiVersion: v1
46 | kind: Service
47 | metadata:
48 | name: zk3
49 | namespace: basic-kafka
50 | labels:
51 | name: zk3
52 | spec:
53 | selector:
54 | name: zk3
55 | ports:
56 | - name: zk2181
57 | port: 2181
58 | protocol: TCP
59 | - name: zk2888
60 | port: 2888
61 | protocol: TCP
62 | - name: zk3888
63 | port: 3888
64 | protocol: TCP
65 | clusterIP: None
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/01-run-kafka/04-service-kfk.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: kfk1
5 | namespace: basic-kafka
6 | labels:
7 | name: kfk1
8 | spec:
9 | selector:
10 | name: kfk1
11 | ports:
12 | - name: kfk9092
13 | port: 9092
14 | protocol: TCP
15 | clusterIP: None
16 | ---
17 | apiVersion: v1
18 | kind: Service
19 | metadata:
20 | name: kfk2
21 | namespace: basic-kafka
22 | labels:
23 | name: kfk2
24 | spec:
25 | selector:
26 | name: kfk2
27 | ports:
28 | - name: kfk9092
29 | port: 9092
30 | protocol: TCP
31 | clusterIP: None
32 | ---
33 | apiVersion: v1
34 | kind: Service
35 | metadata:
36 | name: kfk3
37 | namespace: basic-kafka
38 | labels:
39 | name: kfk3
40 | spec:
41 | selector:
42 | name: kfk3
43 | ports:
44 | - name: kfk9092
45 | port: 9092
46 | protocol: TCP
47 | clusterIP: None
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/01-run-kafka/05-client-util.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: client-util
5 | namespace: basic-kafka
6 | labels:
7 | name: client-util
8 | spec:
9 | containers:
10 | - name: client-util
11 | image: opcellent/util:2.0
12 | stdin: true
13 | tty: true
14 | imagePullPolicy: Always
15 | resources:
16 | requests:
17 | memory: 500Mi
18 | cpu: 200m
19 | limits:
20 | memory: 1Gi
21 | cpu: 500m
22 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/02-play-consumer-producer/README:
--------------------------------------------------------------------------------
1 | Play Consumer-Producer
2 | ===
3 |
4 | Kafkacat URL
5 | https://github.com/edenhill/kafkacat
6 |
7 | 1. Start all k8s object if not created
8 | $ kubectl apply -f .
9 |
10 | 2. Check if kafka and zookeeper pods is running
11 | $ kubectl get po -n basic-kafka
12 | NAME READY STATUS RESTARTS AGE
13 | client-util 1/1 Running 0 5m28s
14 | kfk1-86886b6b84-75p7p 1/1 Running 2 5m29s
15 | kfk2-5b69dfcdb4-wk9lt 1/1 Running 2 5m29s
16 | kfk3-6d4c8874c6-47dxp 1/1 Running 2 5m29s
17 | zk1-76cc547698-8865g 1/1 Running 0 5m29s
18 | zk2-7bb59d6788-5dfxx 1/1 Running 0 5m29s
19 | zk3-566db54d6b-xqjzh 1/1 Running 0 5m29s
20 |
21 | 3. Exec into client-util pod
22 | $ kubectl exec -it client-util -n basic-kafka -- bash
23 | root@client-util:/#
24 |
25 | 4. Start Producer using kafkacat, and send some messages
26 | $ kafkacat -P -b "kfk1,kfk2,kfk3" -t "mytopic"
27 |
28 | $ message1
29 | $ message2
30 | $ message3
31 |
32 | 5. Open another tab in terminal, and exec into client-util
33 | $ kubectl exec -it client-util -n basic-kafka -- bash
34 | root@client-util:/#
35 |
36 | 6. Start Consumer in the open terminal
37 | $ kafkacat -C -b "kfk1,kfk2,kfk3" -t "mytopic"
38 |
39 | 7. Exit Consumer by (Ctrl + c)
40 |
41 | 8. Exit client-util
42 | $ exit
43 |
44 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/03-play-consumer-group/README:
--------------------------------------------------------------------------------
1 | Play Consumer Group
2 | ===
3 |
4 | Kafkacat URL
5 | https://github.com/edenhill/kafkacat
6 |
7 | 1. Start all k8s objects
8 | $ kubectl apply -f .
9 |
10 | 2. Check if kafka and zookeeper deployment is Running
11 | $ kubectl get po -n basic-kafka
12 | NAME READY STATUS RESTARTS AGE
13 | client-util 1/1 Running 0 5m28s
14 | kfk1-86886b6b84-75p7p 1/1 Running 2 5m29s
15 | kfk2-5b69dfcdb4-wk9lt 1/1 Running 2 5m29s
16 | kfk3-6d4c8874c6-47dxp 1/1 Running 2 5m29s
17 | zk1-76cc547698-8865g 1/1 Running 0 5m29s
18 | zk2-7bb59d6788-5dfxx 1/1 Running 0 5m29s
19 | zk3-566db54d6b-xqjzh 1/1 Running 0 5m29s
20 |
21 | 3. Exec into client-util
22 | $ kubectl exec -it client-util -n basic-kafka -- bash
23 | root@client-util:/#
24 |
25 | 4. Start Producer and send some messages
26 | $ kafkacat -P -b "kfk1,kfk2,kfk3" -t "mytopic1"
27 |
28 | $ message1
29 | $ message2
30 | $ message3
31 |
32 | 5. Open another tab in terminal, then exec into client-util shell
33 | $ kubectl exec -it client-util -n basic-kafka -- bash
34 | root@client-util:/#
35 |
36 | 6. Start Consumer 1 in new terminal
37 | $ kafkacat -C -b "kfk1,kfk2,kfk3" -G mygroup mytopic1
38 |
39 | 7. Open another tab in terminal, then exec into client-util shell
40 | $ kubectl exec -it client-util -n basic-kafka -- bash
41 | root@client-util:/#
42 |
43 | 8. Start Consumer 2 in new terminal
44 | $ kafkacat -C -b "kfk1,kfk2,kfk3" -G mygroup mytopic1
45 |
46 | 9. Send some more messages in Producer
47 | $ message4
48 | $ message5
49 | $ message6
50 | $ message7
51 | $ message8
52 |
53 | 10. Check each Consumers will receive new messages
54 |
55 | 12. Exit from Consumer using (Ctrl + c)
56 |
57 | 13. When Consumer exit, see the rebalance process happen
58 |
59 | 14. Exit from all consumer using (Ctrl + c)
60 |
61 | 15. Exit from all client-util
62 | $ exit
63 |
64 | 16. Cleanup workshop
65 | $ kubectl delete ns basic-kafka
66 |
67 |
68 |
69 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/kfk-2.0/rootfs/app-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | . /opt/bitnami/base/functions
4 | . /opt/bitnami/base/helpers
5 |
6 | print_welcome_page
7 |
8 | if [[ "$1" == "nami" && "$2" == "start" ]] || [[ "$1" == "/run.sh" ]]; then
9 | . /init.sh
10 | nami_initialize kafka
11 | info "Starting kafka... "
12 | fi
13 |
14 | exec tini -- "$@"
15 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/kfk-2.0/rootfs/init.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ##
4 | ## @brief Helper function to show an error when KAFKA_LISTENERS does not configure a secure listener
5 | ## param $1 Input name
6 | ##
7 | plaintext_listener_error() {
8 | error "The $1 environment variable does not set a secure listener. Set the environment variable ALLOW_PLAINTEXT_LISTENER=yes to allow the container to be started with a plaintext listener. This is recommended only for development."
9 | exit 1
10 | }
11 |
12 | ##
13 | ## @brief Helper function to show a warning when the ALLOW_PLAINTEXT_LISTENER flag is enabled
14 | ##
15 | plaintext_listener_enabled_warn() {
16 | warn "You set the environment variable ALLOW_PLAINTEXT_LISTENER=${ALLOW_PLAINTEXT_LISTENER}. For safety reasons, do not use this flag in a production environment."
17 | }
18 |
19 |
20 | # Validate passwords
21 | if [[ "$ALLOW_PLAINTEXT_LISTENER" =~ ^(yes|Yes|YES)$ ]]; then
22 | plaintext_listener_enabled_warn
23 | elif [[ ! "$KAFKA_LISTENERS" =~ SASL_SSL ]]; then
24 | plaintext_listener_error KAFKA_LISTENERS
25 | fi
26 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/kfk-2.0/rootfs/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | . /opt/bitnami/base/functions
3 | . /opt/bitnami/base/helpers
4 |
5 |
6 | USER=kafka
7 | KAFKA_HOME="/opt/bitnami/kafka"
8 | START_COMMAND="${KAFKA_HOME}/bin/kafka-server-start.sh ${KAFKA_HOME}/config/server.properties --override connections.max.idle.ms=-1 --override group.initial.rebalance.delay.ms=3000"
9 |
10 | if [[ -z "$KAFKA_BROKER_ID" ]]; then
11 | if [[ -n "$BROKER_ID_COMMAND" ]]; then
12 | KAFKA_BROKER_ID="$(eval "$BROKER_ID_COMMAND")"
13 | export KAFKA_BROKER_ID
14 | else
15 | # By default auto allocate broker ID
16 | export KAFKA_BROKER_ID=-1
17 | fi
18 | fi
19 |
20 | if [[ "$KAFKA_LISTENERS" =~ SASL ]]; then
21 | export KAFKA_OPTS="-Djava.security.auth.login.config=${KAFKA_HOME}/conf/kafka_jaas.conf"
22 | fi
23 |
24 | # If container is started as `root` user
25 | if [[ $EUID -eq 0 ]]; then
26 | exec gosu ${USER} bash -c "${START_COMMAND[@]}"
27 | else
28 | exec bash -c "${START_COMMAND[@]}"
29 | fi
30 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/zk-3.0/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM bitnami/minideb-extras:stretch-r163
2 | LABEL maintainer "Bitnami "
3 |
4 | ENV BITNAMI_PKG_CHMOD="-R g+rwX" \
5 | HOME="/"
6 |
7 | # Install required system packages and dependencies
8 | RUN install_packages libblkid1 libbsd0 libc6 libffi6 libgcc1 libglib2.0-0 libmount1 libpcre3 libselinux1 libstdc++6 libuuid1 libx11-6 libxau6 libxcb1 libxdmcp6 libxext6 zlib1g
9 | RUN bitnami-pkg install java-1.8.181-1 --checksum 66bba4b4a2647f981339d306da796905c222057c4277a5ef045e079981a404f4
10 | RUN bitnami-pkg unpack zookeeper-3.4.12-14 --checksum 06a1205a7955d26bdbd8f72cfabc39d10724c5a4ec9da39df20de4012bf5abb3
11 |
12 | COPY rootfs /
13 | ENV ALLOW_ANONYMOUS_LOGIN="no" \
14 | BITNAMI_APP_NAME="zookeeper" \
15 | BITNAMI_IMAGE_VERSION="3.4.12-debian-9-r75" \
16 | JVMFLAGS="" \
17 | PATH="/opt/bitnami/java/bin:/opt/bitnami/zookeeper/bin:$PATH" \
18 | ZOO_CLIENT_PASSWORD="" \
19 | ZOO_CLIENT_USER="" \
20 | ZOO_ENABLE_AUTH="no" \
21 | ZOO_HEAP_SIZE="1024" \
22 | ZOO_INIT_LIMIT="10" \
23 | ZOO_MAX_CLIENT_CNXNS="60" \
24 | ZOO_PORT_NUMBER="2181" \
25 | ZOO_SERVERS="" \
26 | ZOO_SERVER_ID="1" \
27 | ZOO_SERVER_PASSWORDS="" \
28 | ZOO_SERVER_USERS="" \
29 | ZOO_SYNC_LIMIT="5" \
30 | ZOO_TICK_TIME="2000"
31 |
32 | EXPOSE 2181 2888 3888
33 |
34 | USER 1001
35 | ENTRYPOINT [ "/app-entrypoint.sh" ]
36 | CMD [ "/run.sh" ]
37 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/zk-3.0/rootfs/app-entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash -e
2 |
3 | . /opt/bitnami/base/functions
4 | . /opt/bitnami/base/helpers
5 |
6 | print_welcome_page
7 |
8 | if [[ "$1" == "nami" && "$2" == "start" ]] || [[ "$1" == "/run.sh" ]]; then
9 | . /init.sh
10 | nami_initialize zookeeper
11 | info "Starting zookeeper... "
12 | fi
13 |
14 | exec tini -- "$@"
15 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/zk-3.0/rootfs/init.sh:
--------------------------------------------------------------------------------
1 | ##
2 | ## @brief Helper function to show an error when ZOO_ENABLE_AUTH is set to no
3 | ## param $1 Input name
4 | ##
5 | anonymous_login_error() {
6 | error "The $1 environment variable does not configure authentication. Set the environment variable ALLOW_ANONYMOUS_LOGIN=yes to allow unauthenticated users to connect to ZooKeeper. This is recommended only for development."
7 | exit 1
8 | }
9 |
10 | ##
11 | ## @brief Helper function to show a warning when the ALLOW_ANONYMOUS_LOGIN flag is enabled
12 | ##
13 | anonymous_login_enabled_warn() {
14 | warn "You set the environment variable ALLOW_ANONYMOUS_LOGIN=${ALLOW_ANONYMOUS_LOGIN}. For safety reasons, do not use this flag in a production environment."
15 | }
16 |
17 |
18 | # Validate passwords
19 | if [[ "$ALLOW_ANONYMOUS_LOGIN" =~ ^(yes|Yes|YES)$ ]]; then
20 | anonymous_login_enabled_warn
21 | elif [[ ! "$ZOO_ENABLE_AUTH" =~ ^(yes|Yes|YES)$ ]]; then
22 | anonymous_login_error ZOO_ENABLE_AUTH
23 | fi
24 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/zk-3.0/rootfs/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | . /opt/bitnami/base/functions
3 | . /opt/bitnami/base/helpers
4 |
5 |
6 | export ZOO_LOG_DIR=/opt/bitnami/zookeeper/logs
7 | export ZOOPIDFILE=/opt/bitnami/zookeeper/tmp/zookeeper.pid
8 |
9 | USER=zookeeper
10 | START_COMMAND="/opt/bitnami/zookeeper/bin/zkServer.sh start && tail -f ${ZOO_LOG_DIR}/zookeeper.out"
11 |
12 | # If container is started as `root` user
13 | if [ $EUID -eq 0 ]; then
14 | exec gosu ${USER} bash -c "${START_COMMAND}"
15 | else
16 | exec bash -c "${START_COMMAND}"
17 | fi
18 |
--------------------------------------------------------------------------------
/chapter01-basic-knowledges/1.5-basic-kafka/extra-docker-images/zk-3.0/rootfs/zookeeper-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "allowAnonymousLogin": "{{$global.env.ALLOW_ANONYMOUS_LOGIN}}",
3 | "zookeeperClientPassword": "{{$global.env.ZOO_CLIENT_PASSWORD}}",
4 | "zookeeperClientUser": "{{$global.env.ZOO_CLIENT_USER}}",
5 | "zookeeperEnableAuth": "{{$global.env.ZOO_ENABLE_AUTH}}",
6 | "zookeeperHeapSize": "{{$global.env.ZOO_HEAP_SIZE}}",
7 | "zookeeperId": "{{$global.env.ZOO_SERVER_ID}}",
8 | "zookeeperInitLimit": "{{$global.env.ZOO_INIT_LIMIT}}",
9 | "zookeeperJvmFlags": "{{$global.env.JVMFLAGS}}",
10 | "zookeeperMaxClientCnxns": "{{$global.env.ZOO_MAX_CLIENT_CNXNS}}",
11 | "zookeeperPort": "{{$global.env.ZOO_PORT_NUMBER}}",
12 | "zookeeperServerPasswords": "{{$global.env.ZOO_SERVER_PASSWORDS}}",
13 | "zookeeperServerUsers": "{{$global.env.ZOO_SERVER_USERS}}",
14 | "zookeeperServers": "{{$global.env.ZOO_SERVERS}}",
15 | "zookeeperSyncLimit": "{{$global.env.ZOO_SYNC_LIMIT}}",
16 | "zookeeperTickTime": "{{$global.env.ZOO_TICK_TIME}}"
17 | }
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/01-http-service/README:
--------------------------------------------------------------------------------
1 | HTTP service
2 |
3 | 1. Open chapter02-microservices/2.2-microservices-types/01-http-service
4 |
5 | 2. Run command
6 | $ go mod init automationworkshop/main
7 | go: creating new go.mod: module automationworkshop/main
8 |
9 | 3. Run command
10 | $ go build .
11 | $ ./main
12 |
13 | 4. Run command
14 | $ curl -X POST "localhost:8080/citizen"
15 | {"status":"success"}
16 |
17 | 5. Run command
18 | $ curl -X PUT "localhost:8080/citizen/123"
19 | {"id":"123"}
20 |
21 | 6. Run command
22 | $ curl -X GET "localhost:8080/citizen/123?page=2"
23 | {"id":"123","page":"2"}
24 |
25 | 7. Run command
26 | $ curl -X DELETE "localhost:8080/citizen/123"
27 | {"status":"success"}
28 |
29 | 8. Explain the service in source code
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/01-http-service/context.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // IContext is the context for service
4 | type IContext interface {
5 | Log(message string)
6 | Param(name string) string
7 | QueryParam(name string) string
8 | ReadInput() string
9 | Response(responseCode int, responseData interface{})
10 | }
11 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/01-http-service/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 |
7 | "github.com/labstack/echo"
8 | )
9 |
10 | // HTTPContext implement IContext it is context for HTTP
11 | type HTTPContext struct {
12 | ms *Microservice
13 | c echo.Context
14 | }
15 |
16 | // NewHTTPContext is the constructor function for HTTPContext
17 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
18 | return &HTTPContext{
19 | ms: ms,
20 | c: c,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *HTTPContext) Log(message string) {
26 | fmt.Println("HTTP: ", message)
27 | }
28 |
29 | // Param return parameter by name
30 | func (ctx *HTTPContext) Param(name string) string {
31 | return ctx.c.Param(name)
32 | }
33 |
34 | // QueryParam return query param
35 | func (ctx *HTTPContext) QueryParam(name string) string {
36 | return ctx.c.QueryParam(name)
37 | }
38 |
39 | // ReadInput read the request body and return it as string
40 | func (ctx *HTTPContext) ReadInput() string {
41 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
42 | if err != nil {
43 | return ""
44 | }
45 | return string(body)
46 | }
47 |
48 | // Response return response to client
49 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
50 | ctx.c.JSON(responseCode, responseData)
51 | }
52 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/01-http-service/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | )
6 |
7 | func main() {
8 |
9 | ms := NewMicroservice()
10 |
11 | ms.POST("/citizen", func(ctx IContext) error {
12 | ctx.Log("POST: /citizen")
13 | status := map[string]interface{}{
14 | "status": "success",
15 | }
16 | ctx.Response(http.StatusOK, status)
17 | return nil
18 | })
19 |
20 | ms.GET("/citizen/:id", func(ctx IContext) error {
21 | id := ctx.Param("id")
22 | page := ctx.QueryParam("page")
23 | ctx.Log("GET: /citizen/" + id)
24 | citizen := map[string]interface{}{
25 | "id": id,
26 | "page": page,
27 | }
28 | ctx.Response(http.StatusOK, citizen)
29 | return nil
30 | })
31 |
32 | ms.PUT("/citizen/:id", func(ctx IContext) error {
33 | id := ctx.Param("id")
34 | ctx.Log("PUT: /citizen/" + id)
35 | citizen := map[string]interface{}{
36 | "id": id,
37 | }
38 | ctx.Response(http.StatusOK, citizen)
39 | return nil
40 | })
41 |
42 | ms.DELETE("/citizen/:id", func(ctx IContext) error {
43 | id := ctx.Param("id")
44 | ctx.Log("DELETE: /citizen/" + id)
45 | status := map[string]interface{}{
46 | "status": "success",
47 | }
48 | ctx.Response(http.StatusOK, status)
49 | return nil
50 | })
51 |
52 | defer ms.Cleanup()
53 | ms.Start()
54 | }
55 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/02-consumer-service/README:
--------------------------------------------------------------------------------
1 | Consumer Service
2 |
3 | 0. Look at slides Consumer service
4 |
5 | 1. Open vscode at chapter02-microservices/2.2-microservices-types/02-consumer-service
6 |
7 | 2. Start kafka and zookeeper locally via docker
8 | $ docker-compose up -d
9 | * Wait until container is ready
10 |
11 | $ docker ps
12 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13 | eecc315cc549 3dsinteractive/kafka:2.0 "/app-entrypoint.sh …" 2 minutes ago Up 2 minutes 9092/tcp, 0.0.0.0:9094->9094/tcp 02-consumer-service_kafka_1
14 | 67b5f2efaf0a 3dsinteractive/zookeeper:3.0 "/app-entrypoint.sh …" 2 minutes ago Up 2 minutes 2888/tcp, 0.0.0.0:2181->2181/tcp, 3888/tcp 02-consumer-service_zookeeper_1
15 |
16 | 3. Run command
17 | $ go mod init automationworkshop/main
18 | go: creating new go.mod: module automationworkshop/main
19 |
20 | 4. Run command
21 | $ go build .
22 | $ ./main
23 |
24 | Consumer: {"message_id":0}
25 | Consumer: {"message_id":1}
26 | Consumer: {"message_id":2}
27 | Consumer: {"message_id":3}
28 | Consumer: {"message_id":4}
29 | Consumer: {"message_id":5}
30 | Consumer: {"message_id":6}
31 | Consumer: {"message_id":7}
32 | Consumer: {"message_id":8}
33 | Consumer: {"message_id":9}
34 |
35 | 5. Run command for cleanup
36 | $ docker-compose down
37 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/02-consumer-service/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | Response(responseCode int, responseData interface{})
9 | ReadInput() string
10 | }
11 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/02-consumer-service/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "fmt"
5 |
6 | // ConsumerContext implement IContext it is context for Consumer
7 | type ConsumerContext struct {
8 | ms *Microservice
9 | message string
10 | }
11 |
12 | // NewConsumerContext is the constructor function for ConsumerContext
13 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
14 | return &ConsumerContext{
15 | ms: ms,
16 | message: message,
17 | }
18 | }
19 |
20 | // Log will log a message
21 | func (ctx *ConsumerContext) Log(message string) {
22 | fmt.Println("Consumer: ", message)
23 | }
24 |
25 | // Param return parameter by name (empty in case of Consumer)
26 | func (ctx *ConsumerContext) Param(name string) string {
27 | return ""
28 | }
29 |
30 | // ReadInput return message
31 | func (ctx *ConsumerContext) ReadInput() string {
32 | return ctx.message
33 | }
34 |
35 | // Response return response to client
36 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
37 | return
38 | }
39 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/02-consumer-service/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | zookeeper:
4 | image: 3dsinteractive/zookeeper:3.0
5 | ports:
6 | - 2181:2181
7 | environment:
8 | - ALLOW_ANONYMOUS_LOGIN=yes
9 | kafka:
10 | image: 3dsinteractive/kafka:2.0-custom
11 | ports:
12 | - 9094:9094
13 | environment:
14 | - ALLOW_PLAINTEXT_LISTENER=yes
15 | - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
16 | - KAFKA_ADVERTISED_LISTENERS=INSIDE://:9092,OUTSIDE://localhost:9094
17 | - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:9094
18 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
19 | - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
20 | - KAFKA_DELETE_TOPIC_ENABLE=true
21 | - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true
22 | - KAFKA_NUM_NETWORK_THREADS=8
23 | - KAFKA_NUM_IO_THREADS=16
24 | depends_on:
25 | - zookeeper
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/02-consumer-service/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "time"
6 | )
7 |
8 | func main() {
9 | ms := NewMicroservice()
10 |
11 | servers := "localhost:9094"
12 | topic := "when-citizen-has-registered-" + randString()
13 | groupID := "validation-consumer"
14 | timeout := time.Duration(-1)
15 |
16 | ms.Consume(servers, topic, groupID, timeout, func(ctx IContext) error {
17 | msg := ctx.ReadInput()
18 | ctx.Log(msg)
19 | return nil
20 | })
21 |
22 | prod := NewProducer(servers, ms)
23 | go func() {
24 | for i := 0; i < 10; i++ {
25 | prod.SendMessage(topic, "", map[string]interface{}{"message_id": i})
26 | time.Sleep(time.Second)
27 | }
28 |
29 | // Exit program
30 | ms.Stop()
31 | }()
32 |
33 | defer ms.Cleanup()
34 | ms.Start()
35 | }
36 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/02-consumer-service/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "math/rand"
7 | )
8 |
9 | func randString() string {
10 | i := rand.Int()
11 | return fmt.Sprintf("%d", i)
12 | }
13 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/03-batch-consumer-service/README:
--------------------------------------------------------------------------------
1 | Batch Consumer
2 |
3 | 0. Look at slide Batch Consumer Service
4 |
5 | 1. open vscode at chapter02-microservices/2.2-microservices-types/03-batch-consumer-service
6 |
7 | 2. Start kafka and zookeeper locally via docker
8 | $ docker-compose up -d
9 | * Wait until container is ready
10 |
11 | $ docker ps
12 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13 | eecc315cc549 3dsinteractive/kafka:2.0 "/app-entrypoint.sh …" 2 minutes ago Up 2 minutes 9092/tcp, 0.0.0.0:9094->9094/tcp 02-consumer-service_kafka_1
14 | 67b5f2efaf0a 3dsinteractive/zookeeper:3.0 "/app-entrypoint.sh …" 2 minutes ago Up 2 minutes 2888/tcp, 0.0.0.0:2181->2181/tcp, 3888/tcp 02-consumer-service_zookeeper_1
15 |
16 | 3. Run command
17 | $ go mod init automationworkshop/main
18 | go: creating new go.mod: module automationworkshop/main
19 |
20 | 4. Run command
21 | $ go build .
22 | $ ./main
23 |
24 | Batch Consumer: Begin Batch
25 | Batch Consumer: {"message_id":0}
26 | Batch Consumer: {"message_id":1}
27 | Batch Consumer: {"message_id":2}
28 | Batch Consumer: End Batch
29 | Batch Consumer: Begin Batch
30 | Batch Consumer: {"message_id":3}
31 | Batch Consumer: End Batch
32 | Batch Consumer: Begin Batch
33 | Batch Consumer: {"message_id":4}
34 | Batch Consumer: {"message_id":5}
35 | Batch Consumer: {"message_id":6}
36 | Batch Consumer: End Batch
37 | Batch Consumer: Begin Batch
38 | Batch Consumer: {"message_id":7}
39 | Batch Consumer: {"message_id":8}
40 | Batch Consumer: End Batch
41 | Batch Consumer: Begin Batch
42 | Batch Consumer: {"message_id":9}
43 | Batch Consumer: End Batch
44 |
45 | 5. Run command to cleanup
46 | $ docker-compose down
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/03-batch-consumer-service/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | Response(responseCode int, responseData interface{})
9 | ReadInput() string
10 | ReadInputs() []string
11 | }
12 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/03-batch-consumer-service/context_consumer_batch.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "fmt"
5 |
6 | // BatchConsumerContext implement IContext it is context for Consumer
7 | type BatchConsumerContext struct {
8 | ms *Microservice
9 | messages []string
10 | }
11 |
12 | // NewBatchConsumerContext is the constructor function for BatchConsumerContext
13 | func NewBatchConsumerContext(ms *Microservice, messages []string) *BatchConsumerContext {
14 | return &BatchConsumerContext{
15 | ms: ms,
16 | messages: messages,
17 | }
18 | }
19 |
20 | // Log will log a message
21 | func (ctx *BatchConsumerContext) Log(message string) {
22 | fmt.Println("Batch Consumer: ", message)
23 | }
24 |
25 | // Param return parameter by name (empty in case of Consumer)
26 | func (ctx *BatchConsumerContext) Param(name string) string {
27 | return ""
28 | }
29 |
30 | // ReadInput return message (return empty in batch consumer)
31 | func (ctx *BatchConsumerContext) ReadInput() string {
32 | return ""
33 | }
34 |
35 | // ReadInputs return messages in batch
36 | func (ctx *BatchConsumerContext) ReadInputs() []string {
37 | return ctx.messages
38 | }
39 |
40 | // Response return response to client
41 | func (ctx *BatchConsumerContext) Response(responseCode int, responseData interface{}) {
42 | return
43 | }
44 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/03-batch-consumer-service/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | zookeeper:
4 | image: 3dsinteractive/zookeeper:3.0
5 | ports:
6 | - 2181:2181
7 | environment:
8 | - ALLOW_ANONYMOUS_LOGIN=yes
9 | kafka:
10 | image: 3dsinteractive/kafka:2.0-custom
11 | ports:
12 | - 9094:9094
13 | environment:
14 | - ALLOW_PLAINTEXT_LISTENER=yes
15 | - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
16 | - KAFKA_ADVERTISED_LISTENERS=INSIDE://:9092,OUTSIDE://localhost:9094
17 | - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:9094
18 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
19 | - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
20 | - KAFKA_DELETE_TOPIC_ENABLE=true
21 | - KAFKA_CFG_AUTO_CREATE_TOPICS_ENABLE=true
22 | - KAFKA_NUM_NETWORK_THREADS=8
23 | - KAFKA_NUM_IO_THREADS=16
24 | depends_on:
25 | - zookeeper
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/03-batch-consumer-service/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "time"
6 | )
7 |
8 | func main() {
9 | ms := NewMicroservice()
10 |
11 | servers := "localhost:9094"
12 | topic := "when-citizen-has-registered-batch-" + randString()
13 | groupID := "validation-consumer"
14 | timeout := time.Duration(-1)
15 | batchSize := 3
16 | batchTimeout := time.Second * 5
17 |
18 | ms.ConsumeBatch(servers, topic, groupID, timeout, batchSize, batchTimeout,
19 | func(ctx IContext) error {
20 | msgs := ctx.ReadInputs()
21 | ctx.Log("Begin Batch")
22 | for _, msg := range msgs {
23 | ctx.Log(msg)
24 | }
25 | ctx.Log("End Batch")
26 | return nil
27 | })
28 |
29 | prod := NewProducer(servers, ms)
30 | go func() {
31 | for i := 0; i < 10; i++ {
32 | prod.SendMessage(topic, "", map[string]interface{}{"message_id": i})
33 | time.Sleep(time.Second)
34 | }
35 |
36 | // Wait for last batch then exit program
37 | time.Sleep(5 * time.Second)
38 | ms.Stop()
39 | }()
40 |
41 | defer ms.Cleanup()
42 | ms.Start()
43 | }
44 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/03-batch-consumer-service/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "math/rand"
7 | )
8 |
9 | func randString() string {
10 | i := rand.Int()
11 | return fmt.Sprintf("%d", i)
12 | }
13 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/04-scheduler-service/README:
--------------------------------------------------------------------------------
1 | Scheduler Service
2 |
3 | 0. Look at slide Scheduler Service
4 |
5 | 1. Open vscode at chapter02-microservices/2.2-microservices-types/04-scheduler-service
6 |
7 | 2. Run command
8 | $ go mod init automationworkshop/main
9 | go: creating new go.mod: module automationworkshop/main
10 |
11 | 3. Run command
12 | $ go build .
13 | $ ./main
14 |
15 | Scheduler: Tick at 21:40:45
16 | Scheduler: Tick at 21:40:46
17 | Scheduler: Tick at 21:40:47
18 | Scheduler: Tick at 21:40:48
19 | Scheduler: Tick at 21:40:49
20 | Scheduler: Tick at 21:40:50
21 | Scheduler: Tick at 21:40:51
22 | Scheduler: Tick at 21:40:52
23 | Scheduler: Tick at 21:40:53
24 | Scheduler: Tick at 21:40:54
25 | Scheduler: Tick at 21:40:55
26 | Scheduler: Tick at 21:40:56
27 | Scheduler: Tick at 21:40:57
28 |
29 | 4. Run command for cleanup
30 | $ docker-compose down
31 |
32 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/04-scheduler-service/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "time"
5 |
6 | // IContext is the context for service
7 | type IContext interface {
8 | Log(message string)
9 | Param(name string) string
10 | Response(responseCode int, responseData interface{})
11 | ReadInput() string
12 | ReadInputs() []string
13 |
14 | Now() time.Time
15 | }
16 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/04-scheduler-service/context_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "time"
7 | )
8 |
9 | // SchedulerContext implement IContext it is context for Consumer
10 | type SchedulerContext struct {
11 | ms *Microservice
12 | }
13 |
14 | // NewSchedulerContext is the constructor function for SchedulerContext
15 | func NewSchedulerContext(ms *Microservice) *SchedulerContext {
16 | return &SchedulerContext{
17 | ms: ms,
18 | }
19 | }
20 |
21 | // Now return time.Now
22 | func (ctx *SchedulerContext) Now() time.Time {
23 | return time.Now()
24 | }
25 |
26 | // Log will log a message
27 | func (ctx *SchedulerContext) Log(message string) {
28 | fmt.Println("Scheduler: ", message)
29 | }
30 |
31 | // Param return parameter by name (empty in scheduler)
32 | func (ctx *SchedulerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // ReadInput return message (return empty in scheduler)
37 | func (ctx *SchedulerContext) ReadInput() string {
38 | return ""
39 | }
40 |
41 | // ReadInputs return messages in batch (return nil in scheduler)
42 | func (ctx *SchedulerContext) ReadInputs() []string {
43 | return nil
44 | }
45 |
46 | // Response return response to client
47 | func (ctx *SchedulerContext) Response(responseCode int, responseData interface{}) {
48 | return
49 | }
50 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/04-scheduler-service/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "time"
7 | )
8 |
9 | func main() {
10 | ms := NewMicroservice()
11 |
12 | timer := 1 * time.Second
13 | exitScheduler := ms.Schedule(timer, func(ctx IContext) error {
14 | now := ctx.Now()
15 | ctx.Log(fmt.Sprintf("Tick at %s", now.Format("15:04:05")))
16 | return nil
17 | })
18 |
19 | defer func() { exitScheduler <- true }()
20 | defer ms.Cleanup()
21 |
22 | ms.Start()
23 | }
24 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/README:
--------------------------------------------------------------------------------
1 | AsyncTask Service
2 |
3 | 0. Look at slide AsyncTask Service
4 |
5 | 1. Open vscode at chapter02-microservices/2.2-microservices-types/05-asynctask-service
6 |
7 | 2. Start kafka and zookeeper locally via docker
8 | $ docker-compose up -d
9 | * Wait until container is ready
10 |
11 | $ docker ps
12 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
13 | eecc315cc549 3dsinteractive/kafka:2.0 "/app-entrypoint.sh …" 2 minutes ago Up 2 minutes 9092/tcp, 0.0.0.0:9094->9094/tcp 02-consumer-service_kafka_1
14 | 67b5f2efaf0a 3dsinteractive/zookeeper:3.0 "/app-entrypoint.sh …" 2 minutes ago Up 2 minutes 2888/tcp, 0.0.0.0:2181->2181/tcp, 3888/tcp 02-consumer-service_zookeeper_1
15 |
16 | 3. Run command
17 | $ go mod init automationworkshop/main
18 | go: creating new go.mod: module automationworkshop/main
19 |
20 | 4. Run command
21 | $ go build .
22 | $ ./main
23 |
24 | 5. Open another terminal and run command
25 | curl -X POST "localhost:8080/citizen/register" -d '{"firstname":"chaiyapong"}'
26 | {"ref":"atask-xxxxxxxxxxx"}
27 |
28 | 6. Run command
29 | curl -X GET "localhost:8080/citizen/register?ref=atask-xxxxxxxxxxx"
30 | {"code":200,"data":{"id":"123"},"status":"success"}
31 |
32 | 7. Run command for cleanup
33 | $ docker-compose down
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | QueryParam(name string) string
9 | Response(responseCode int, responseData interface{})
10 | ReadInput() string
11 | ReadInputs() []string
12 |
13 | // Dependency
14 | Cacher(server string) ICacher
15 | Producer(servers string) IProducer
16 | MQ(servers string) IMQ
17 | }
18 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "fmt"
5 |
6 | // ConsumerContext implement IContext it is context for Consumer
7 | type ConsumerContext struct {
8 | ms *Microservice
9 | message string
10 | }
11 |
12 | // NewConsumerContext is the constructor function for ConsumerContext
13 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
14 | return &ConsumerContext{
15 | ms: ms,
16 | message: message,
17 | }
18 | }
19 |
20 | // Log will log a message
21 | func (ctx *ConsumerContext) Log(message string) {
22 | fmt.Println("Consumer: ", message)
23 | }
24 |
25 | // Param return parameter by name (empty in case of Consumer)
26 | func (ctx *ConsumerContext) Param(name string) string {
27 | return ""
28 | }
29 |
30 | // QueryParam return empty in consumer
31 | func (ctx *ConsumerContext) QueryParam(name string) string {
32 | return ""
33 | }
34 |
35 | // ReadInput return message
36 | func (ctx *ConsumerContext) ReadInput() string {
37 | return ctx.message
38 | }
39 |
40 | // ReadInputs return nil in case Consumer
41 | func (ctx *ConsumerContext) ReadInputs() []string {
42 | return nil
43 | }
44 |
45 | // Response return response to client
46 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
47 | return
48 | }
49 |
50 | // Cacher return cacher
51 | func (ctx *ConsumerContext) Cacher(server string) ICacher {
52 | return NewCacher(server)
53 | }
54 |
55 | // Producer return producer
56 | func (ctx *ConsumerContext) Producer(servers string) IProducer {
57 | return NewProducer(servers, ctx.ms)
58 | }
59 |
60 | // MQ return MQ
61 | func (ctx *ConsumerContext) MQ(servers string) IMQ {
62 | return NewMQ(servers, ctx.ms)
63 | }
64 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 |
7 | "github.com/labstack/echo"
8 | )
9 |
10 | // HTTPContext implement IContext it is context for HTTP
11 | type HTTPContext struct {
12 | ms *Microservice
13 | c echo.Context
14 | }
15 |
16 | // NewHTTPContext is the constructor function for HTTPContext
17 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
18 | return &HTTPContext{
19 | ms: ms,
20 | c: c,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *HTTPContext) Log(message string) {
26 | fmt.Println("HTTP: ", message)
27 | }
28 |
29 | // Param return parameter by name
30 | func (ctx *HTTPContext) Param(name string) string {
31 | return ctx.c.Param(name)
32 | }
33 |
34 | // QueryParam return query param
35 | func (ctx *HTTPContext) QueryParam(name string) string {
36 | return ctx.c.QueryParam(name)
37 | }
38 |
39 | // ReadInput read the request body and return it as string
40 | func (ctx *HTTPContext) ReadInput() string {
41 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
42 | if err != nil {
43 | return ""
44 | }
45 | return string(body)
46 | }
47 |
48 | // ReadInputs return nil in HTTP Context
49 | func (ctx *HTTPContext) ReadInputs() []string {
50 | return nil
51 | }
52 |
53 | // Response return response to client
54 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
55 | ctx.c.JSON(responseCode, responseData)
56 | }
57 |
58 | // Cacher return cacher
59 | func (ctx *HTTPContext) Cacher(server string) ICacher {
60 | return NewCacher(server)
61 | }
62 |
63 | // Producer return producer
64 | func (ctx *HTTPContext) Producer(servers string) IProducer {
65 | return NewProducer(servers, ctx.ms)
66 | }
67 |
68 | // MQ return MQ
69 | func (ctx *HTTPContext) MQ(servers string) IMQ {
70 | return NewMQ(servers, ctx.ms)
71 | }
72 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | zookeeper:
4 | image: 3dsinteractive/zookeeper:3.0
5 | ports:
6 | - 2181:2181
7 | environment:
8 | - ALLOW_ANONYMOUS_LOGIN=yes
9 | kafka:
10 | image: 3dsinteractive/kafka:2.0-custom
11 | ports:
12 | - 9094:9094
13 | environment:
14 | - ALLOW_PLAINTEXT_LISTENER=yes
15 | - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
16 | - KAFKA_ADVERTISED_LISTENERS=INSIDE://:9092,OUTSIDE://localhost:9094
17 | - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:9094
18 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
19 | - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
20 | - KAFKA_DELETE_TOPIC_ENABLE=true
21 | - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
22 | - KAFKA_NUM_NETWORK_THREADS=8
23 | - KAFKA_NUM_IO_THREADS=16
24 | depends_on:
25 | - zookeeper
26 | redis:
27 | image: 3dsinteractive/redis:4.0
28 | environment:
29 | - ALLOW_EMPTY_PASSWORD=yes
30 | ports:
31 | - 6379:6379
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "net/http"
5 |
6 | func main() {
7 | ms := NewMicroservice()
8 |
9 | cacheServer := "localhost:6379"
10 | mqServers := "localhost:9094"
11 |
12 | ms.AsyncPOST("/citizen/register", cacheServer, mqServers, func(ctx IContext) error {
13 | ctx.Log(ctx.ReadInput())
14 | res := map[string]interface{}{
15 | "id": "123",
16 | }
17 | ctx.Response(http.StatusOK, res)
18 | return nil
19 | })
20 |
21 | defer ms.Cleanup()
22 | ms.Start()
23 | }
24 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/05-asynctask-service/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/06-paralleltask-service/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | QueryParam(name string) string
9 | Response(responseCode int, responseData interface{})
10 | ReadInput() string
11 | ReadInputs() []string
12 |
13 | // Dependency
14 | Cacher(server string) ICacher
15 | Producer(servers string) IProducer
16 | MQ(servers string) IMQ
17 | }
18 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/06-paralleltask-service/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // ConsumerContext implement IContext it is context for Consumer
11 | type ConsumerContext struct {
12 | ms *Microservice
13 | message string
14 | }
15 |
16 | // NewConsumerContext is the constructor function for ConsumerContext
17 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
18 | return &ConsumerContext{
19 | ms: ms,
20 | message: message,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *ConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *ConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer
37 | func (ctx *ConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message
42 | func (ctx *ConsumerContext) ReadInput() string {
43 | return ctx.message
44 | }
45 |
46 | // ReadInputs return nil in case Consumer
47 | func (ctx *ConsumerContext) ReadInputs() []string {
48 | return nil
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *ConsumerContext) Cacher(server string) ICacher {
58 | return NewCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *ConsumerContext) Producer(servers string) IProducer {
63 | return NewProducer(servers, ctx.ms)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *ConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/06-paralleltask-service/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "runtime"
7 | "strings"
8 |
9 | "github.com/labstack/echo"
10 | )
11 |
12 | // HTTPContext implement IContext it is context for HTTP
13 | type HTTPContext struct {
14 | ms *Microservice
15 | c echo.Context
16 | }
17 |
18 | // NewHTTPContext is the constructor function for HTTPContext
19 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
20 | return &HTTPContext{
21 | ms: ms,
22 | c: c,
23 | }
24 | }
25 |
26 | // Log will log a message
27 | func (ctx *HTTPContext) Log(message string) {
28 | _, fn, line, _ := runtime.Caller(1)
29 | fns := strings.Split(fn, "/")
30 | fmt.Println("HTTP:", fns[len(fns)-1], line, message)
31 | }
32 |
33 | // Param return parameter by name
34 | func (ctx *HTTPContext) Param(name string) string {
35 | return ctx.c.Param(name)
36 | }
37 |
38 | // QueryParam return query param
39 | func (ctx *HTTPContext) QueryParam(name string) string {
40 | return ctx.c.QueryParam(name)
41 | }
42 |
43 | // ReadInput read the request body and return it as string
44 | func (ctx *HTTPContext) ReadInput() string {
45 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
46 | if err != nil {
47 | return ""
48 | }
49 | return string(body)
50 | }
51 |
52 | // ReadInputs return nil in HTTP Context
53 | func (ctx *HTTPContext) ReadInputs() []string {
54 | return nil
55 | }
56 |
57 | // Response return response to client
58 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
59 | ctx.c.JSON(responseCode, responseData)
60 | }
61 |
62 | // Cacher return cacher
63 | func (ctx *HTTPContext) Cacher(server string) ICacher {
64 | return NewCacher(server)
65 | }
66 |
67 | // Producer return producer
68 | func (ctx *HTTPContext) Producer(servers string) IProducer {
69 | return NewProducer(servers, ctx.ms)
70 | }
71 |
72 | // MQ return MQ
73 | func (ctx *HTTPContext) MQ(servers string) IMQ {
74 | return NewMQ(servers, ctx.ms)
75 | }
76 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/06-paralleltask-service/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | zookeeper:
4 | image: 3dsinteractive/zookeeper:3.0
5 | ports:
6 | - 2181:2181
7 | environment:
8 | - ALLOW_ANONYMOUS_LOGIN=yes
9 | kafka:
10 | image: 3dsinteractive/kafka:2.0-custom
11 | ports:
12 | - 9094:9094
13 | environment:
14 | - ALLOW_PLAINTEXT_LISTENER=yes
15 | - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
16 | - KAFKA_ADVERTISED_LISTENERS=INSIDE://:9092,OUTSIDE://localhost:9094
17 | - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:9094
18 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
19 | - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
20 | - KAFKA_DELETE_TOPIC_ENABLE=true
21 | - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
22 | - KAFKA_NUM_NETWORK_THREADS=8
23 | - KAFKA_NUM_IO_THREADS=16
24 | depends_on:
25 | - zookeeper
26 | redis:
27 | image: 3dsinteractive/redis:4.0
28 | environment:
29 | - ALLOW_EMPTY_PASSWORD=yes
30 | ports:
31 | - 6379:6379
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/06-paralleltask-service/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "math/rand"
6 | "net/http"
7 | "time"
8 | )
9 |
10 | func main() {
11 | ms := NewMicroservice()
12 |
13 | cacheServer := "localhost:6379"
14 | mqServers := "localhost:9094"
15 |
16 | // 1. Start PTask endpoint
17 | ms.PTaskEndpoint("/citizen/batch", cacheServer, mqServers)
18 |
19 | // 2. Start 2 worker nodes
20 | for i := 0; i < 2; i++ {
21 | ms.PTaskWorkerNode("/citizen/batch", cacheServer, mqServers, func(ctx IContext) error {
22 | ctx.Log(ctx.ReadInput())
23 | resStr := randString()
24 | res := map[string]interface{}{
25 | "result": resStr,
26 | }
27 | n := rand.Intn(5)
28 | time.Sleep(time.Duration(n) * time.Second)
29 | ctx.Response(http.StatusOK, res)
30 | return nil
31 | })
32 | }
33 |
34 | defer ms.Cleanup()
35 | ms.Start()
36 | }
37 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.2-microservices-types/06-paralleltask-service/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | QueryParam(name string) string
9 | Response(responseCode int, responseData interface{})
10 | ReadInput() string
11 | ReadInputs() []string
12 |
13 | // Dependency
14 | Cacher(server string) ICacher
15 | Producer(servers string) IProducer
16 | MQ(servers string) IMQ
17 | }
18 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // ConsumerContext implement IContext it is context for Consumer
11 | type ConsumerContext struct {
12 | ms *Microservice
13 | message string
14 | }
15 |
16 | // NewConsumerContext is the constructor function for ConsumerContext
17 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
18 | return &ConsumerContext{
19 | ms: ms,
20 | message: message,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *ConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *ConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer
37 | func (ctx *ConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message
42 | func (ctx *ConsumerContext) ReadInput() string {
43 | return ctx.message
44 | }
45 |
46 | // ReadInputs return nil in case Consumer
47 | func (ctx *ConsumerContext) ReadInputs() []string {
48 | return nil
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *ConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *ConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *ConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/context_consumer_batch.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // BatchConsumerContext implement IContext it is context for Consumer
11 | type BatchConsumerContext struct {
12 | ms *Microservice
13 | messages []string
14 | }
15 |
16 | // NewBatchConsumerContext is the constructor function for BatchConsumerContext
17 | func NewBatchConsumerContext(ms *Microservice, messages []string) *BatchConsumerContext {
18 | return &BatchConsumerContext{
19 | ms: ms,
20 | messages: messages,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *BatchConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Batch Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *BatchConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer batch
37 | func (ctx *BatchConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message (return empty in batch consumer)
42 | func (ctx *BatchConsumerContext) ReadInput() string {
43 | return ""
44 | }
45 |
46 | // ReadInputs return messages in batch
47 | func (ctx *BatchConsumerContext) ReadInputs() []string {
48 | return ctx.messages
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *BatchConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *BatchConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *BatchConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *BatchConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "runtime"
7 | "strings"
8 |
9 | "github.com/labstack/echo"
10 | )
11 |
12 | // HTTPContext implement IContext it is context for HTTP
13 | type HTTPContext struct {
14 | ms *Microservice
15 | c echo.Context
16 | }
17 |
18 | // NewHTTPContext is the constructor function for HTTPContext
19 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
20 | return &HTTPContext{
21 | ms: ms,
22 | c: c,
23 | }
24 | }
25 |
26 | // Log will log a message
27 | func (ctx *HTTPContext) Log(message string) {
28 | _, fn, line, _ := runtime.Caller(1)
29 | fns := strings.Split(fn, "/")
30 | fmt.Println("HTTP:", fns[len(fns)-1], line, message)
31 | }
32 |
33 | // Param return parameter by name
34 | func (ctx *HTTPContext) Param(name string) string {
35 | return ctx.c.Param(name)
36 | }
37 |
38 | // QueryParam return query param
39 | func (ctx *HTTPContext) QueryParam(name string) string {
40 | return ctx.c.QueryParam(name)
41 | }
42 |
43 | // ReadInput read the request body and return it as string
44 | func (ctx *HTTPContext) ReadInput() string {
45 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
46 | if err != nil {
47 | return ""
48 | }
49 | return string(body)
50 | }
51 |
52 | // ReadInputs return nil in HTTP Context
53 | func (ctx *HTTPContext) ReadInputs() []string {
54 | return nil
55 | }
56 |
57 | // Response return response to client
58 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
59 | ctx.c.JSON(responseCode, responseData)
60 | }
61 |
62 | // Cacher return cacher
63 | func (ctx *HTTPContext) Cacher(server string) ICacher {
64 | return ctx.ms.getCacher(server)
65 | }
66 |
67 | // Producer return producer
68 | func (ctx *HTTPContext) Producer(servers string) IProducer {
69 | return ctx.ms.getProducer(servers)
70 | }
71 |
72 | // MQ return MQ
73 | func (ctx *HTTPContext) MQ(servers string) IMQ {
74 | return NewMQ(servers, ctx.ms)
75 | }
76 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/context_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // SchedulerContext implement IContext it is context for Consumer
11 | type SchedulerContext struct {
12 | ms *Microservice
13 | }
14 |
15 | // NewSchedulerContext is the constructor function for SchedulerContext
16 | func NewSchedulerContext(ms *Microservice) *SchedulerContext {
17 | return &SchedulerContext{
18 | ms: ms,
19 | }
20 | }
21 |
22 | // Log will log a message
23 | func (ctx *SchedulerContext) Log(message string) {
24 | _, fn, line, _ := runtime.Caller(1)
25 | fns := strings.Split(fn, "/")
26 | fmt.Println("Scheduler:", fns[len(fns)-1], line, message)
27 | }
28 |
29 | // Param return parameter by name (empty in scheduler)
30 | func (ctx *SchedulerContext) Param(name string) string {
31 | return ""
32 | }
33 |
34 | // QueryParam return empty in scheduler
35 | func (ctx *SchedulerContext) QueryParam(name string) string {
36 | return ""
37 | }
38 |
39 | // ReadInput return message (return empty in scheduler)
40 | func (ctx *SchedulerContext) ReadInput() string {
41 | return ""
42 | }
43 |
44 | // ReadInputs return messages in batch (return nil in scheduler)
45 | func (ctx *SchedulerContext) ReadInputs() []string {
46 | return nil
47 | }
48 |
49 | // Response return response to client
50 | func (ctx *SchedulerContext) Response(responseCode int, responseData interface{}) {
51 | return
52 | }
53 |
54 | // Cacher return cacher
55 | func (ctx *SchedulerContext) Cacher(server string) ICacher {
56 | return ctx.ms.getCacher(server)
57 | }
58 |
59 | // Producer return producer
60 | func (ctx *SchedulerContext) Producer(servers string) IProducer {
61 | return ctx.ms.getProducer(servers)
62 | }
63 |
64 | // MQ return MQ
65 | func (ctx *SchedulerContext) MQ(servers string) IMQ {
66 | return NewMQ(servers, ctx.ms)
67 | }
68 |
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | zookeeper:
4 | image: 3dsinteractive/zookeeper:3.0
5 | ports:
6 | - 2181:2181
7 | environment:
8 | - ALLOW_ANONYMOUS_LOGIN=yes
9 | kafka:
10 | image: 3dsinteractive/kafka:2.0-custom
11 | ports:
12 | - 9094:9094
13 | environment:
14 | - ALLOW_PLAINTEXT_LISTENER=yes
15 | - KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181
16 | - KAFKA_ADVERTISED_LISTENERS=INSIDE://:9092,OUTSIDE://localhost:9094
17 | - KAFKA_LISTENERS=INSIDE://:9092,OUTSIDE://:9094
18 | - KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT
19 | - KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE
20 | - KAFKA_DELETE_TOPIC_ENABLE=true
21 | - KAFKA_AUTO_CREATE_TOPICS_ENABLE=true
22 | - KAFKA_NUM_NETWORK_THREADS=8
23 | - KAFKA_NUM_IO_THREADS=16
24 | depends_on:
25 | - zookeeper
26 | redis:
27 | image: 3dsinteractive/redis:4.0
28 | environment:
29 | - ALLOW_EMPTY_PASSWORD=yes
30 | ports:
31 | - 6379:6379
--------------------------------------------------------------------------------
/chapter02-microservices/2.3-service-startup-teardown/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM 3dsinteractive/golang:1.14-alpine3.9-librdfkafka1.4.0
2 |
3 | # 1. Add all go files and build
4 | WORKDIR /go/src/bitbucket.org/automationworkshop/main
5 | ADD . /go/src/bitbucket.org/automationworkshop/main
6 | RUN go build -mod vendor -i -tags "musl static_all" .
7 |
8 | # ================
9 | FROM 3dsinteractive/alpine:3.9
10 |
11 | # 2. Use multi stage docker file, and copy executable file from previous stage
12 | COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
13 | COPY --from=0 /go/src/bitbucket.org/automationworkshop/main/main /main
14 |
15 | # 3. Add entrypoint.sh (The file that will use as entrypoint when run go program)
16 | ADD ./entrypoint.sh /entrypoint.sh
17 |
18 | # 4. Create user 1001 (It is my practice to always use user with id 1001)
19 | RUN adduser -u 1001 -D -s /bin/sh -G ping 1001
20 | RUN chown 1001:1001 /entrypoint.sh
21 | RUN chown 1001:1001 /main
22 |
23 | # 5. Make entrypoint.sh and main executable
24 | RUN chmod +x /entrypoint.sh
25 | RUN chmod +x /main
26 |
27 | # 6. Set default user
28 | USER 1001
29 |
30 | # 7. Expose port 8080
31 | EXPOSE 8080
32 |
33 | # 8. Start program by run entrypoint.sh
34 | ENTRYPOINT ["/entrypoint.sh"]
35 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/README:
--------------------------------------------------------------------------------
1 | Deploy Service
2 |
3 | 0. Look into the slides about Deploy Service
4 |
5 | 1. Open vscode at chapter03-deploy-deploy-services/3.1-deploy-services
6 |
7 | 2. Register for https://hub.docker.com
8 |
9 | 3. Create public repository call [your-docker-repository-name]/automation-technology
10 |
11 | 4. Make sure docker is running (Check the docker icon in task bar)
12 |
13 | 5. Make sure you are logged in with your docker account
14 | $ docker login
15 | [username]
16 | [password]
17 |
18 | 6. Update file deploy.sh change
19 | DOCKER_REPOSITORY=
20 | to
21 | DOCKER_REPOSITORY=[your-docker-repository-name]
22 |
23 | 7. Run command (deploy.sh will be the file used to build your project, especially when it integrate with ci/cd)
24 | $ ./deploy.sh
25 | This will build the Dockerfile and push image to [your-docker-repository-name]/automation-technology
26 |
27 | 8. Explain deploy.sh (Each comments will explain itself)
28 |
29 | 9. Explain Dockerfile
30 |
31 | 10. Explain entrypoint.sh
32 |
33 | 11. Run command
34 | $ cd k8s
35 |
36 | 12. Run command
37 | $ kubectl apply -f .
38 |
39 | 13. Run command
40 | $ kubectl get po -n deploy-service
41 | register-api-758f494ccd-7xqmx 1/1 Running 0 109s
42 | register-api-758f494ccd-gbtdg 1/1 Running 0 109s
43 |
44 | 14. Run command
45 | $ kubectl get svc -n deploy-service
46 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
47 | register-api ClusterIP 10.103.76.235 8080/TCP 2m38s
48 |
49 | 15. Run command
50 | $ kubectl get ing -n deploy-service
51 | NAME CLASS HOSTS ADDRESS PORTS AGE
52 | ingress kubernetes.docker.internal localhost 80 91s
53 |
54 | 16. Run command
55 | $ curl -X POST "http://kubernetes.docker.internal/citizen"
56 | {"status":"success"}
57 |
58 | 17. Run command
59 | $ curl -X PUT "http://kubernetes.docker.internal/citizen/123"
60 | {"id":"123"}
61 |
62 | 18. Run command
63 | $ curl -X GET "http://kubernetes.docker.internal/citizen/123?page=2"
64 | {"id":"123","page":"2"}
65 |
66 | 19. Run command
67 | $ curl -X DELETE "http://kubernetes.docker.internal/citizen/123"
68 | {"status":"success"}
69 |
70 | 20. Run command
71 | $ kubectl delete ns deploy-service
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | QueryParam(name string) string
9 | Response(responseCode int, responseData interface{})
10 | ReadInput() string
11 | ReadInputs() []string
12 |
13 | // Dependency
14 | Cacher(server string) ICacher
15 | Producer(servers string) IProducer
16 | MQ(servers string) IMQ
17 | }
18 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // ConsumerContext implement IContext it is context for Consumer
11 | type ConsumerContext struct {
12 | ms *Microservice
13 | message string
14 | }
15 |
16 | // NewConsumerContext is the constructor function for ConsumerContext
17 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
18 | return &ConsumerContext{
19 | ms: ms,
20 | message: message,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *ConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *ConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer
37 | func (ctx *ConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message
42 | func (ctx *ConsumerContext) ReadInput() string {
43 | return ctx.message
44 | }
45 |
46 | // ReadInputs return nil in case Consumer
47 | func (ctx *ConsumerContext) ReadInputs() []string {
48 | return nil
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *ConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *ConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *ConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/context_consumer_batch.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // BatchConsumerContext implement IContext it is context for Consumer
11 | type BatchConsumerContext struct {
12 | ms *Microservice
13 | messages []string
14 | }
15 |
16 | // NewBatchConsumerContext is the constructor function for BatchConsumerContext
17 | func NewBatchConsumerContext(ms *Microservice, messages []string) *BatchConsumerContext {
18 | return &BatchConsumerContext{
19 | ms: ms,
20 | messages: messages,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *BatchConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Batch Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *BatchConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer batch
37 | func (ctx *BatchConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message (return empty in batch consumer)
42 | func (ctx *BatchConsumerContext) ReadInput() string {
43 | return ""
44 | }
45 |
46 | // ReadInputs return messages in batch
47 | func (ctx *BatchConsumerContext) ReadInputs() []string {
48 | return ctx.messages
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *BatchConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *BatchConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *BatchConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *BatchConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "runtime"
7 | "strings"
8 |
9 | "github.com/labstack/echo"
10 | )
11 |
12 | // HTTPContext implement IContext it is context for HTTP
13 | type HTTPContext struct {
14 | ms *Microservice
15 | c echo.Context
16 | }
17 |
18 | // NewHTTPContext is the constructor function for HTTPContext
19 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
20 | return &HTTPContext{
21 | ms: ms,
22 | c: c,
23 | }
24 | }
25 |
26 | // Log will log a message
27 | func (ctx *HTTPContext) Log(message string) {
28 | _, fn, line, _ := runtime.Caller(1)
29 | fns := strings.Split(fn, "/")
30 | fmt.Println("HTTP:", fns[len(fns)-1], line, message)
31 | }
32 |
33 | // Param return parameter by name
34 | func (ctx *HTTPContext) Param(name string) string {
35 | return ctx.c.Param(name)
36 | }
37 |
38 | // QueryParam return query param
39 | func (ctx *HTTPContext) QueryParam(name string) string {
40 | return ctx.c.QueryParam(name)
41 | }
42 |
43 | // ReadInput read the request body and return it as string
44 | func (ctx *HTTPContext) ReadInput() string {
45 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
46 | if err != nil {
47 | return ""
48 | }
49 | return string(body)
50 | }
51 |
52 | // ReadInputs return nil in HTTP Context
53 | func (ctx *HTTPContext) ReadInputs() []string {
54 | return nil
55 | }
56 |
57 | // Response return response to client
58 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
59 | ctx.c.JSON(responseCode, responseData)
60 | }
61 |
62 | // Cacher return cacher
63 | func (ctx *HTTPContext) Cacher(server string) ICacher {
64 | return ctx.ms.getCacher(server)
65 | }
66 |
67 | // Producer return producer
68 | func (ctx *HTTPContext) Producer(servers string) IProducer {
69 | return ctx.ms.getProducer(servers)
70 | }
71 |
72 | // MQ return MQ
73 | func (ctx *HTTPContext) MQ(servers string) IMQ {
74 | return NewMQ(servers, ctx.ms)
75 | }
76 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/context_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // SchedulerContext implement IContext it is context for Consumer
11 | type SchedulerContext struct {
12 | ms *Microservice
13 | }
14 |
15 | // NewSchedulerContext is the constructor function for SchedulerContext
16 | func NewSchedulerContext(ms *Microservice) *SchedulerContext {
17 | return &SchedulerContext{
18 | ms: ms,
19 | }
20 | }
21 |
22 | // Log will log a message
23 | func (ctx *SchedulerContext) Log(message string) {
24 | _, fn, line, _ := runtime.Caller(1)
25 | fns := strings.Split(fn, "/")
26 | fmt.Println("Scheduler:", fns[len(fns)-1], line, message)
27 | }
28 |
29 | // Param return parameter by name (empty in scheduler)
30 | func (ctx *SchedulerContext) Param(name string) string {
31 | return ""
32 | }
33 |
34 | // QueryParam return empty in scheduler
35 | func (ctx *SchedulerContext) QueryParam(name string) string {
36 | return ""
37 | }
38 |
39 | // ReadInput return message (return empty in scheduler)
40 | func (ctx *SchedulerContext) ReadInput() string {
41 | return ""
42 | }
43 |
44 | // ReadInputs return messages in batch (return nil in scheduler)
45 | func (ctx *SchedulerContext) ReadInputs() []string {
46 | return nil
47 | }
48 |
49 | // Response return response to client
50 | func (ctx *SchedulerContext) Response(responseCode int, responseData interface{}) {
51 | return
52 | }
53 |
54 | // Cacher return cacher
55 | func (ctx *SchedulerContext) Cacher(server string) ICacher {
56 | return ctx.ms.getCacher(server)
57 | }
58 |
59 | // Producer return producer
60 | func (ctx *SchedulerContext) Producer(servers string) IProducer {
61 | return ctx.ms.getProducer(servers)
62 | }
63 |
64 | // MQ return MQ
65 | func (ctx *SchedulerContext) MQ(servers string) IMQ {
66 | return NewMQ(servers, ctx.ms)
67 | }
68 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 1. Format of docker image is $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
4 | # for example 3dsinteractive/automation-technology:prd-1.0.20210117185518
5 | APP_VERSION=1.0
6 | TIMESTAMP=20210117185518
7 | DEPLOY_ENV=prd
8 | DOCKER_REPOSITORY=
9 |
10 | # 2. commit will push docker image to repository
11 | function commit() {
12 | local IMAGE=$1
13 | echo "docker push image : $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP"
14 | docker push $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
15 | }
16 |
17 | # 3. build_api is the main function to build Dockerfile
18 | function build_api() {
19 | local IMAGE=automation-technology
20 |
21 | # If found go in default path, it will use go from default path
22 | GO=/usr/local/go/bin/go
23 | if [ -f "$GO" ]; then
24 | /usr/local/go/bin/go mod init automationworkshop/main
25 | /usr/local/go/bin/go get
26 | /usr/local/go/bin/go mod vendor
27 | else
28 | go mod init automationworkshop/main
29 | go get
30 | go mod vendor
31 | fi
32 |
33 | # Build the Dockerfile
34 | docker build -f Dockerfile -t $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP .
35 | commit $IMAGE
36 | }
37 |
38 | # 4. Validate APP_VERSION must not empty
39 | if [ "$APP_VERSION" = "" ]; then
40 | echo -e "APP_VERSION cannot be blank"
41 | exit 1
42 | fi
43 |
44 | # 5. Validate TIMESTAMP must not empty
45 | if [ "$TIMESTAMP" = "" ]; then
46 | echo -e "TIMESTAMP cannot be blank"
47 | exit 1
48 | fi
49 |
50 | # 6. Validate DEPLOY_ENV must not empty
51 | if [ "$DEPLOY_ENV" == "" ]; then
52 | echo -e "DEPLOY_ENV cannot be blank"
53 | exit 1
54 | fi
55 |
56 | # 7. Validate DOCKER_REPOSITORY must not empty
57 | if [ "$DOCKER_REPOSITORY" == "" ]; then
58 | echo -e "DOCKER_REPOSITORY cannot be blank"
59 | exit 1
60 | fi
61 |
62 | # 8. Run main build process
63 | build_api
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # exec env file if exists, .env might be in /.env or /config/.env
4 | FILE=/.env ; [ -f $FILE ] && . $FILE
5 | FILE=/config/.env ; [ -f $FILE ] && . $FILE
6 |
7 | # start main program
8 | exec /main
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/k8s/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: deploy-service
5 | labels:
6 | name: deploy-service
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/k8s/01-register-api.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: register-api
5 | namespace: deploy-service
6 | labels:
7 | name: register-api
8 | spec:
9 | replicas: 2
10 | strategy:
11 | type: RollingUpdate
12 | rollingUpdate:
13 | maxUnavailable: 1
14 | maxSurge: 1
15 | selector:
16 | matchLabels:
17 | name: register-api
18 | template:
19 | metadata:
20 | labels:
21 | name: register-api
22 | spec:
23 | containers:
24 | - name: register-api
25 | image: 3dsinteractive/automation-technology:prd-1.0.20210117185518
26 | imagePullPolicy: Always
27 | ports:
28 | - name: api8080
29 | containerPort: 8080
30 | resources:
31 | requests:
32 | memory: 500Mi
33 | cpu: 200m
34 | limits:
35 | memory: 1Gi
36 | cpu: 500m
37 | ---
38 | apiVersion: v1
39 | kind: Service
40 | metadata:
41 | name: register-api
42 | namespace: deploy-service
43 | labels:
44 | name: register-api
45 | spec:
46 | selector:
47 | name: register-api
48 | ports:
49 | - name: "api8080"
50 | port: 8080
51 | targetPort: 8080
52 | protocol: TCP
53 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/k8s/02-ingress.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Ingress
3 | metadata:
4 | name: ingress
5 | namespace: deploy-service
6 | labels:
7 | name: ingress
8 | spec:
9 | rules:
10 | - host: kubernetes.docker.internal
11 | http:
12 | paths:
13 | - path: /
14 | backend:
15 | serviceName: register-api
16 | servicePort: 8080
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "net/http"
6 | )
7 |
8 | func main() {
9 | ms := NewMicroservice()
10 |
11 | startHTTP(ms)
12 |
13 | ms.Start()
14 | }
15 |
16 | func startHTTP(ms *Microservice) {
17 | ms.POST("/citizen", func(ctx IContext) error {
18 | ctx.Log("POST: /citizen")
19 | status := map[string]interface{}{
20 | "status": "success",
21 | }
22 | ctx.Response(http.StatusOK, status)
23 | return nil
24 | })
25 |
26 | ms.GET("/citizen/:id", func(ctx IContext) error {
27 | id := ctx.Param("id")
28 | page := ctx.QueryParam("page")
29 | ctx.Log("GET: /citizen/" + id)
30 | citizen := map[string]interface{}{
31 | "id": id,
32 | "page": page,
33 | }
34 | ctx.Response(http.StatusOK, citizen)
35 | return nil
36 | })
37 |
38 | ms.PUT("/citizen/:id", func(ctx IContext) error {
39 | id := ctx.Param("id")
40 | ctx.Log("PUT: /citizen/" + id)
41 | citizen := map[string]interface{}{
42 | "id": id,
43 | }
44 | ctx.Response(http.StatusOK, citizen)
45 | return nil
46 | })
47 |
48 | ms.DELETE("/citizen/:id", func(ctx IContext) error {
49 | id := ctx.Param("id")
50 | ctx.Log("DELETE: /citizen/" + id)
51 | status := map[string]interface{}{
52 | "status": "success",
53 | }
54 | ctx.Response(http.StatusOK, status)
55 | return nil
56 | })
57 | }
58 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.1-deploy-services/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM 3dsinteractive/golang:1.14-alpine3.9-librdfkafka1.4.0
2 |
3 | # 1. Add all go files and build
4 | WORKDIR /go/src/bitbucket.org/automationworkshop/main
5 | ADD . /go/src/bitbucket.org/automationworkshop/main
6 | RUN go build -mod vendor -i -tags "musl static_all" .
7 |
8 | # ================
9 | FROM 3dsinteractive/alpine:3.9
10 |
11 | # 2. Use multi stage docker file, and copy executable file from previous stage
12 | COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
13 | COPY --from=0 /go/src/bitbucket.org/automationworkshop/main/main /main
14 |
15 | # 3. Add entrypoint.sh (The file that will use as entrypoint when run go program)
16 | ADD ./entrypoint.sh /entrypoint.sh
17 |
18 | # 4. Create user 1001 (It is my practice to always use user with id 1001)
19 | RUN adduser -u 1001 -D -s /bin/sh -G ping 1001
20 | RUN chown 1001:1001 /entrypoint.sh
21 | RUN chown 1001:1001 /main
22 |
23 | # 5. Make entrypoint.sh and main executable
24 | RUN chmod +x /entrypoint.sh
25 | RUN chmod +x /main
26 |
27 | # 6. Set default user
28 | USER 1001
29 |
30 | # 7. Expose port 8080
31 | EXPOSE 8080
32 |
33 | # 8. Start program by run entrypoint.sh
34 | ENTRYPOINT ["/entrypoint.sh"]
35 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/README:
--------------------------------------------------------------------------------
1 | Healthcheck Readiness
2 |
3 | 0. Look into the slides about service healthcheck
4 |
5 | 1. Open vscode at chapter03-deploy-healthchecks/3.2-healthcheck-readiness
6 |
7 | 2. Register for https://hub.docker.com
8 |
9 | 3. Create public repository call [your-docker-repository-name]/automation-technology
10 |
11 | 4. Make sure docker is running (Check the docker icon in task bar)
12 |
13 | 5. Make sure you are logged in with your docker account
14 | $ docker login
15 | [username]
16 | [password]
17 |
18 | 6. Update file deploy.sh change
19 | DOCKER_REPOSITORY=
20 | to
21 | DOCKER_REPOSITORY=[your-docker-repository-name]
22 |
23 | 7. Run command (deploy.sh will be the file used to build your project, especially when it integrate with ci/cd)
24 | $ ./deploy.sh
25 | This will build the Dockerfile and push image to [your-docker-repository-name]/automation-technology
26 |
27 | 8. Run command
28 | $ cd k8s
29 |
30 | 9. Run command to start databases
31 | $ kubectl apply -f 00-databases/.
32 |
33 | ** Wait until redis is OK
34 |
35 | 10. Run command
36 | $ kubectl get po -n healthcheck
37 | NAME READY STATUS RESTARTS AGE
38 | redis-577d58dd6c-c6595 1/1 Running 0 3m55s
39 |
40 | 11. Run command to start application
41 | $ kubectl apply -f 01-application/.
42 |
43 | 12. Run command to make sure all services is OK
44 | $ kubectl get po -n healthcheck
45 | register-api-758f494ccd-7xqmx 1/1 Running 0 109s
46 | register-api-758f494ccd-gbtdg 1/1 Running 0 109s
47 |
48 | 13. Run command
49 | $ curl -X POST "http://kubernetes.docker.internal/citizen"
50 | {"status":"success"}
51 |
52 | 14. Run command
53 | $ watch "kubectl get po -n healthcheck"
54 |
55 | 15. Open other terminal and Run command to kill redis
56 | $ kubectl delete -f 01-redis.yml
57 |
58 | 16. Wait to see service healthcheck fail
59 | ** Notice RESTARTS will increase
60 |
61 | 17. Run command to get redis back
62 | $ kubectl apply -f 01-redis.yml
63 |
64 | 18. Wait to see service come back
65 |
66 | 19. Explain healthcheck endpoint in sourcecode
67 |
68 | 20. Run command
69 | $ kubectl delete ns healthcheck
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | QueryParam(name string) string
9 | Response(responseCode int, responseData interface{})
10 | ReadInput() string
11 | ReadInputs() []string
12 |
13 | // Dependency
14 | Cacher(server string) ICacher
15 | Producer(servers string) IProducer
16 | MQ(servers string) IMQ
17 | }
18 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // ConsumerContext implement IContext it is context for Consumer
11 | type ConsumerContext struct {
12 | ms *Microservice
13 | message string
14 | }
15 |
16 | // NewConsumerContext is the constructor function for ConsumerContext
17 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
18 | return &ConsumerContext{
19 | ms: ms,
20 | message: message,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *ConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *ConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer
37 | func (ctx *ConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message
42 | func (ctx *ConsumerContext) ReadInput() string {
43 | return ctx.message
44 | }
45 |
46 | // ReadInputs return nil in case Consumer
47 | func (ctx *ConsumerContext) ReadInputs() []string {
48 | return nil
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *ConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *ConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *ConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/context_consumer_batch.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // BatchConsumerContext implement IContext it is context for Consumer
11 | type BatchConsumerContext struct {
12 | ms *Microservice
13 | messages []string
14 | }
15 |
16 | // NewBatchConsumerContext is the constructor function for BatchConsumerContext
17 | func NewBatchConsumerContext(ms *Microservice, messages []string) *BatchConsumerContext {
18 | return &BatchConsumerContext{
19 | ms: ms,
20 | messages: messages,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *BatchConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Batch Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *BatchConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer batch
37 | func (ctx *BatchConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message (return empty in batch consumer)
42 | func (ctx *BatchConsumerContext) ReadInput() string {
43 | return ""
44 | }
45 |
46 | // ReadInputs return messages in batch
47 | func (ctx *BatchConsumerContext) ReadInputs() []string {
48 | return ctx.messages
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *BatchConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *BatchConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *BatchConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *BatchConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "runtime"
7 | "strings"
8 |
9 | "github.com/labstack/echo"
10 | )
11 |
12 | // HTTPContext implement IContext it is context for HTTP
13 | type HTTPContext struct {
14 | ms *Microservice
15 | c echo.Context
16 | }
17 |
18 | // NewHTTPContext is the constructor function for HTTPContext
19 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
20 | return &HTTPContext{
21 | ms: ms,
22 | c: c,
23 | }
24 | }
25 |
26 | // Log will log a message
27 | func (ctx *HTTPContext) Log(message string) {
28 | _, fn, line, _ := runtime.Caller(1)
29 | fns := strings.Split(fn, "/")
30 | fmt.Println("HTTP:", fns[len(fns)-1], line, message)
31 | }
32 |
33 | // Param return parameter by name
34 | func (ctx *HTTPContext) Param(name string) string {
35 | return ctx.c.Param(name)
36 | }
37 |
38 | // QueryParam return query param
39 | func (ctx *HTTPContext) QueryParam(name string) string {
40 | return ctx.c.QueryParam(name)
41 | }
42 |
43 | // ReadInput read the request body and return it as string
44 | func (ctx *HTTPContext) ReadInput() string {
45 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
46 | if err != nil {
47 | return ""
48 | }
49 | return string(body)
50 | }
51 |
52 | // ReadInputs return nil in HTTP Context
53 | func (ctx *HTTPContext) ReadInputs() []string {
54 | return nil
55 | }
56 |
57 | // Response return response to client
58 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
59 | ctx.c.JSON(responseCode, responseData)
60 | }
61 |
62 | // Cacher return cacher
63 | func (ctx *HTTPContext) Cacher(server string) ICacher {
64 | return ctx.ms.getCacher(server)
65 | }
66 |
67 | // Producer return producer
68 | func (ctx *HTTPContext) Producer(servers string) IProducer {
69 | return ctx.ms.getProducer(servers)
70 | }
71 |
72 | // MQ return MQ
73 | func (ctx *HTTPContext) MQ(servers string) IMQ {
74 | return NewMQ(servers, ctx.ms)
75 | }
76 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/context_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // SchedulerContext implement IContext it is context for Consumer
11 | type SchedulerContext struct {
12 | ms *Microservice
13 | }
14 |
15 | // NewSchedulerContext is the constructor function for SchedulerContext
16 | func NewSchedulerContext(ms *Microservice) *SchedulerContext {
17 | return &SchedulerContext{
18 | ms: ms,
19 | }
20 | }
21 |
22 | // Log will log a message
23 | func (ctx *SchedulerContext) Log(message string) {
24 | _, fn, line, _ := runtime.Caller(1)
25 | fns := strings.Split(fn, "/")
26 | fmt.Println("Scheduler:", fns[len(fns)-1], line, message)
27 | }
28 |
29 | // Param return parameter by name (empty in scheduler)
30 | func (ctx *SchedulerContext) Param(name string) string {
31 | return ""
32 | }
33 |
34 | // QueryParam return empty in scheduler
35 | func (ctx *SchedulerContext) QueryParam(name string) string {
36 | return ""
37 | }
38 |
39 | // ReadInput return message (return empty in scheduler)
40 | func (ctx *SchedulerContext) ReadInput() string {
41 | return ""
42 | }
43 |
44 | // ReadInputs return messages in batch (return nil in scheduler)
45 | func (ctx *SchedulerContext) ReadInputs() []string {
46 | return nil
47 | }
48 |
49 | // Response return response to client
50 | func (ctx *SchedulerContext) Response(responseCode int, responseData interface{}) {
51 | return
52 | }
53 |
54 | // Cacher return cacher
55 | func (ctx *SchedulerContext) Cacher(server string) ICacher {
56 | return ctx.ms.getCacher(server)
57 | }
58 |
59 | // Producer return producer
60 | func (ctx *SchedulerContext) Producer(servers string) IProducer {
61 | return ctx.ms.getProducer(servers)
62 | }
63 |
64 | // MQ return MQ
65 | func (ctx *SchedulerContext) MQ(servers string) IMQ {
66 | return NewMQ(servers, ctx.ms)
67 | }
68 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 1. Format of docker image is $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
4 | # for example 3dsinteractive/automation-technology:prd-1.0.20210117185519
5 | APP_VERSION=1.0
6 | TIMESTAMP=20210117185519
7 | DEPLOY_ENV=prd
8 | DOCKER_REPOSITORY=
9 |
10 | # 2. commit will push docker image to repository
11 | function commit() {
12 | local IMAGE=$1
13 | echo "docker push image : $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP"
14 | docker push $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
15 | }
16 |
17 | # 3. build_api is the main function to build Dockerfile
18 | function build_api() {
19 | local IMAGE=automation-technology
20 |
21 | # If found go in default path, it will use go from default path
22 | GO=/usr/local/go/bin/go
23 | if [ -f "$GO" ]; then
24 | /usr/local/go/bin/go mod init automationworkshop/main
25 | /usr/local/go/bin/go get
26 | /usr/local/go/bin/go mod vendor
27 | else
28 | go mod init automationworkshop/main
29 | go get
30 | go mod vendor
31 | fi
32 |
33 | # Build the Dockerfile
34 | docker build -f Dockerfile -t $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP .
35 | commit $IMAGE
36 | }
37 |
38 | # 4. Validate APP_VERSION must not empty
39 | if [ "$APP_VERSION" = "" ]; then
40 | echo -e "APP_VERSION cannot be blank"
41 | exit 1
42 | fi
43 |
44 | # 5. Validate TIMESTAMP must not empty
45 | if [ "$TIMESTAMP" = "" ]; then
46 | echo -e "TIMESTAMP cannot be blank"
47 | exit 1
48 | fi
49 |
50 | # 6. Validate DEPLOY_ENV must not empty
51 | if [ "$DEPLOY_ENV" == "" ]; then
52 | echo -e "DEPLOY_ENV cannot be blank"
53 | exit 1
54 | fi
55 |
56 | # 7. Validate DOCKER_REPOSITORY must not empty
57 | if [ "$DOCKER_REPOSITORY" == "" ]; then
58 | echo -e "DOCKER_REPOSITORY cannot be blank"
59 | exit 1
60 | fi
61 |
62 | # 8. Run main build process
63 | build_api
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # exec env file if exists, .env might be in /.env or /config/.env
4 | FILE=/.env ; [ -f $FILE ] && . $FILE
5 | FILE=/config/.env ; [ -f $FILE ] && . $FILE
6 |
7 | # start main program
8 | exec /main
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/k8s/00-databases/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: healthcheck
5 | labels:
6 | name: healthcheck
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/k8s/00-databases/01-redis.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: redis
5 | namespace: healthcheck
6 | labels:
7 | name: redis
8 | spec:
9 | replicas: 1
10 | strategy:
11 | type: Recreate
12 | selector:
13 | matchLabels:
14 | name: redis
15 | template:
16 | metadata:
17 | labels:
18 | name: redis
19 | spec:
20 | containers:
21 | - name: redis
22 | image: 3dsinteractive/redis:5.0
23 | imagePullPolicy: Always
24 | ports:
25 | - name: redis6379
26 | containerPort: 6379
27 | env:
28 | - name: ALLOW_EMPTY_PASSWORD
29 | value: "yes"
30 | resources:
31 | requests:
32 | memory: 500Mi
33 | cpu: 200m
34 | limits:
35 | memory: 1Gi
36 | cpu: 500m
37 | livenessProbe:
38 | tcpSocket:
39 | port: 6379
40 | initialDelaySeconds: 30
41 | timeoutSeconds: 1
42 | periodSeconds: 300
43 | readinessProbe:
44 | tcpSocket:
45 | port: 6379
46 | initialDelaySeconds: 30
47 | timeoutSeconds: 1
48 | periodSeconds: 30
49 | failureThreshold: 5
50 | ---
51 | apiVersion: v1
52 | kind: Service
53 | metadata:
54 | name: redis
55 | namespace: healthcheck
56 | labels:
57 | name: redis
58 | spec:
59 | selector:
60 | name: redis
61 | ports:
62 | - name: redis6379
63 | port: 6379
64 | targetPort: 6379
65 | protocol: TCP
66 | clusterIP: None
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/k8s/01-application/02-register-api.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: register-api
5 | namespace: healthcheck
6 | labels:
7 | name: register-api
8 | spec:
9 | replicas: 2
10 | strategy:
11 | type: RollingUpdate
12 | rollingUpdate:
13 | maxUnavailable: 1
14 | maxSurge: 1
15 | selector:
16 | matchLabels:
17 | name: register-api
18 | template:
19 | metadata:
20 | labels:
21 | name: register-api
22 | spec:
23 | containers:
24 | - name: register-api
25 | image: 3dsinteractive/automation-technology:prd-1.0.20210117185519
26 | imagePullPolicy: Always
27 | readinessProbe:
28 | httpGet:
29 | path: /healthz
30 | port: 8080
31 | initialDelaySeconds: 10
32 | periodSeconds: 10
33 | timeoutSeconds: 3
34 | failureThreshold: 1
35 | livenessProbe:
36 | httpGet:
37 | path: /healthz
38 | port: 8080
39 | initialDelaySeconds: 30
40 | periodSeconds: 10
41 | timeoutSeconds: 5
42 | failureThreshold: 1
43 | ports:
44 | - name: api8080
45 | containerPort: 8080
46 | resources:
47 | requests:
48 | memory: 500Mi
49 | cpu: 200m
50 | limits:
51 | memory: 1Gi
52 | cpu: 500m
53 | ---
54 | apiVersion: v1
55 | kind: Service
56 | metadata:
57 | name: register-api
58 | namespace: healthcheck
59 | labels:
60 | name: register-api
61 | spec:
62 | selector:
63 | name: register-api
64 | ports:
65 | - name: "api8080"
66 | port: 8080
67 | targetPort: 8080
68 | protocol: TCP
69 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/k8s/01-application/03-ingress.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Ingress
3 | metadata:
4 | name: ingress
5 | namespace: healthcheck
6 | labels:
7 | name: ingress
8 | spec:
9 | rules:
10 | - host: kubernetes.docker.internal
11 | http:
12 | paths:
13 | - path: /
14 | backend:
15 | serviceName: register-api
16 | servicePort: 8080
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "net/http"
6 | "time"
7 | )
8 |
9 | func main() {
10 | ms := NewMicroservice()
11 |
12 | cacheServer := "redis:6379"
13 | cacher := ms.getCacher(cacheServer)
14 | cacher.Set("key1", "value1", time.Duration(-1))
15 |
16 | // 1. Healthcheck endpoint will register to /healthz
17 | ms.RegisterLivenessProbeEndpoint("/healthz")
18 |
19 | // 2. Start application services
20 | startHTTP(ms)
21 |
22 | ms.Start()
23 | }
24 |
25 | func startHTTP(ms *Microservice) {
26 | ms.POST("/citizen", func(ctx IContext) error {
27 | ctx.Log("POST: /citizen")
28 | status := map[string]interface{}{
29 | "status": "success",
30 | }
31 | ctx.Response(http.StatusOK, status)
32 | return nil
33 | })
34 |
35 | ms.GET("/citizen/:id", func(ctx IContext) error {
36 | id := ctx.Param("id")
37 | page := ctx.QueryParam("page")
38 | ctx.Log("GET: /citizen/" + id)
39 | citizen := map[string]interface{}{
40 | "id": id,
41 | "page": page,
42 | }
43 | ctx.Response(http.StatusOK, citizen)
44 | return nil
45 | })
46 |
47 | ms.PUT("/citizen/:id", func(ctx IContext) error {
48 | id := ctx.Param("id")
49 | ctx.Log("PUT: /citizen/" + id)
50 | citizen := map[string]interface{}{
51 | "id": id,
52 | }
53 | ctx.Response(http.StatusOK, citizen)
54 | return nil
55 | })
56 |
57 | ms.DELETE("/citizen/:id", func(ctx IContext) error {
58 | id := ctx.Param("id")
59 | ctx.Log("DELETE: /citizen/" + id)
60 | status := map[string]interface{}{
61 | "status": "success",
62 | }
63 | ctx.Response(http.StatusOK, status)
64 | return nil
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/microservice_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/confluentinc/confluent-kafka-go/kafka"
8 | )
9 |
10 | func (ms *Microservice) consumeSingle(servers string, topic string, groupID string, readTimeout time.Duration, h ServiceHandleFunc) {
11 | c, err := ms.newKafkaConsumer(servers, groupID)
12 | if err != nil {
13 | return
14 | }
15 |
16 | defer c.Close()
17 |
18 | c.Subscribe(topic, nil)
19 |
20 | for {
21 | if readTimeout <= 0 {
22 | // readtimeout -1 indicates no timeout
23 | readTimeout = -1
24 | }
25 |
26 | msg, err := c.ReadMessage(readTimeout)
27 | if err != nil {
28 | kafkaErr, ok := err.(kafka.Error)
29 | if ok {
30 | if kafkaErr.Code() == kafka.ErrTimedOut {
31 | if readTimeout == -1 {
32 | // No timeout just continue to read message again
33 | continue
34 | }
35 | }
36 | }
37 | ms.Log("Consumer", err.Error())
38 | return
39 | }
40 |
41 | // Execute Handler
42 | h(NewConsumerContext(ms, string(msg.Value)))
43 | }
44 | }
45 |
46 | // Consume register service endpoint for Consumer service
47 | func (ms *Microservice) Consume(servers string, topic string, groupID string, readTimeout time.Duration, h ServiceHandleFunc) error {
48 | go ms.consumeSingle(servers, topic, groupID, readTimeout, h)
49 | return nil
50 | }
51 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/microservice_http.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "context"
6 | "time"
7 |
8 | "github.com/labstack/echo"
9 | )
10 |
11 | // GET register service endpoint for HTTP GET
12 | func (ms *Microservice) GET(path string, h ServiceHandleFunc) {
13 | ms.echo.GET(path, func(c echo.Context) error {
14 | return h(NewHTTPContext(ms, c))
15 | })
16 | }
17 |
18 | // POST register service endpoint for HTTP POST
19 | func (ms *Microservice) POST(path string, h ServiceHandleFunc) {
20 | ms.echo.POST(path, func(c echo.Context) error {
21 | return h(NewHTTPContext(ms, c))
22 | })
23 | }
24 |
25 | // PUT register service endpoint for HTTP PUT
26 | func (ms *Microservice) PUT(path string, h ServiceHandleFunc) {
27 | ms.echo.PUT(path, func(c echo.Context) error {
28 | return h(NewHTTPContext(ms, c))
29 | })
30 | }
31 |
32 | // PATCH register service endpoint for HTTP PATCH
33 | func (ms *Microservice) PATCH(path string, h ServiceHandleFunc) {
34 | ms.echo.PATCH(path, func(c echo.Context) error {
35 | return h(NewHTTPContext(ms, c))
36 | })
37 | }
38 |
39 | // DELETE register service endpoint for HTTP DELETE
40 | func (ms *Microservice) DELETE(path string, h ServiceHandleFunc) {
41 | ms.echo.DELETE(path, func(c echo.Context) error {
42 | return h(NewHTTPContext(ms, c))
43 | })
44 | }
45 |
46 | // startHTTP will start HTTP service, this function will block thread
47 | func (ms *Microservice) startHTTP(exitChannel chan bool) error {
48 | // Caller can exit by sending value to exitChannel
49 | go func() {
50 | <-exitChannel
51 | ms.stopHTTP()
52 | }()
53 | return ms.echo.Start(":8080")
54 | }
55 |
56 | func (ms *Microservice) stopHTTP() {
57 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
58 | defer cancel()
59 | ms.echo.Shutdown(ctx)
60 | }
61 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/microservice_liveness.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "net/http"
6 |
7 | "github.com/labstack/echo"
8 | )
9 |
10 | func (ms *Microservice) isCacherAlive() bool {
11 | if ms.cacher == nil {
12 | return true
13 | }
14 |
15 | ms.Log("MS", "Perform healthcheck on Cacher")
16 | err := ms.cacher.Healthcheck()
17 | if err != nil {
18 | return false
19 | }
20 | return true
21 | }
22 |
23 | func (ms *Microservice) isAlive() (bool, string) {
24 | // 1. We will check dependency if it is OK, in this case the dependency is Redis
25 | isAlive := ms.isCacherAlive()
26 | if !isAlive {
27 | return false, "Cacher healthcheck failed"
28 | }
29 |
30 | // 2. If we have other dependency, we will add them here such as
31 | // isAlive = ms.isMariaDBAlive()
32 | // if !isAlive {
33 | // return false, "MariaDB healthcheck failed"
34 | // }
35 |
36 | return true, ""
37 | }
38 |
39 | func (ms *Microservice) responseProbeOK(resp *echo.Response) {
40 | resp.WriteHeader(http.StatusOK)
41 | resp.Write([]byte("ok"))
42 | }
43 |
44 | func (ms *Microservice) responseProbeFailed(resp *echo.Response, reason string) {
45 | errMsg := "Healthcheck failed because of " + reason
46 | resp.WriteHeader(http.StatusInternalServerError)
47 | resp.Write([]byte(errMsg))
48 | }
49 |
50 | // RegisterLivenessProbeEndpoint register endpoint for liveness probe
51 | func (ms *Microservice) RegisterLivenessProbeEndpoint(path string) {
52 | ms.echo.GET(path, func(c echo.Context) error {
53 | // If Microservice isAlive return !ok, it is because some dependency is failed
54 | ok, reason := ms.isAlive()
55 | if !ok {
56 | // If !ok we will response status 500 error
57 | ms.responseProbeFailed(c.Response(), reason)
58 | return nil
59 | }
60 | // If ok we will response 200 OK
61 | ms.responseProbeOK(c.Response())
62 | return nil
63 | })
64 | }
65 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/microservice_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "sync"
6 | "time"
7 | )
8 |
9 | // Schedule will run handler at timer period
10 | func (ms *Microservice) Schedule(timer time.Duration, h ServiceHandleFunc) chan bool /*exit channel*/ {
11 |
12 | // exitChan must be call exitChan <- true from caller to exit scheduler
13 | exitChan := make(chan bool, 1)
14 | go func() {
15 | t := time.NewTicker(timer)
16 | done := make(chan bool, 1)
17 | isExit := false
18 | isExitMutex := sync.Mutex{}
19 |
20 | go func() {
21 | <-exitChan
22 | isExitMutex.Lock()
23 | isExit = true
24 | isExitMutex.Unlock()
25 | // Stop Tick() and send done message to exit for loop below
26 | // Ref: From the documentation http://golang.org/pkg/time/#Ticker.Stop
27 | // Stop turns off a ticker. After Stop, no more ticks will be sent.
28 | // Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
29 | t.Stop()
30 | done <- true
31 | }()
32 |
33 | for {
34 | select {
35 | case execTime := <-t.C:
36 | isExitMutex.Lock()
37 | if isExit {
38 | isExitMutex.Unlock()
39 | // Done in the next round
40 | continue
41 | }
42 | isExitMutex.Unlock()
43 |
44 | now := time.Now()
45 | // The schedule that older than 10s, will be skip, because t.C is buffer at size 1
46 | diff := now.Sub(execTime).Seconds()
47 | if diff > 10 {
48 | continue
49 | }
50 | h(NewSchedulerContext(ms))
51 | case <-done:
52 | return
53 | }
54 | }
55 | }()
56 |
57 | return exitChan
58 | }
59 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.2-healthcheck-readiness/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM 3dsinteractive/golang:1.14-alpine3.9-librdfkafka1.4.0
2 |
3 | WORKDIR /go/src/bitbucket.org/automationworkshop/main
4 | ADD . /go/src/bitbucket.org/automationworkshop/main
5 | RUN go build -mod vendor -i -tags "musl static_all" .
6 |
7 | # ================
8 | FROM 3dsinteractive/alpine:3.9
9 |
10 | COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
11 | COPY --from=0 /go/src/bitbucket.org/automationworkshop/main/main /main
12 |
13 | ADD ./entrypoint.sh /entrypoint.sh
14 |
15 | RUN adduser -u 1001 -D -s /bin/sh -G ping 1001
16 | RUN chown 1001:1001 /entrypoint.sh
17 | RUN chown 1001:1001 /main
18 |
19 | RUN chmod +x /entrypoint.sh
20 | RUN chmod +x /main
21 |
22 | USER 1001
23 |
24 | EXPOSE 8080
25 |
26 | ENTRYPOINT ["/entrypoint.sh"]
27 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/config.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "os"
5 |
6 | // IConfig is interface for application config
7 | type IConfig interface {
8 | ServiceID() string
9 | CacheServer() string
10 | MQServers() string
11 | }
12 |
13 | // Config implement IConfig
14 | type Config struct{}
15 |
16 | // NewConfig return new config instance
17 | func NewConfig() *Config {
18 | return &Config{}
19 | }
20 |
21 | // ServiceID return ID of service
22 | func (cfg *Config) ServiceID() string {
23 | return os.Getenv("SERVICE_ID")
24 | }
25 |
26 | // CacheServer return redis server
27 | func (cfg *Config) CacheServer() string {
28 | return os.Getenv("CACHE_SERVER")
29 | }
30 |
31 | // MQServers return Kafka servers
32 | func (cfg *Config) MQServers() string {
33 | return os.Getenv("MQ_SERVERS")
34 | }
35 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // IContext is the context for service
5 | type IContext interface {
6 | Log(message string)
7 | Param(name string) string
8 | QueryParam(name string) string
9 | Response(responseCode int, responseData interface{})
10 | ReadInput() string
11 | ReadInputs() []string
12 |
13 | // Dependency
14 | Cacher(server string) ICacher
15 | Producer(servers string) IProducer
16 | MQ(servers string) IMQ
17 | }
18 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/context_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // ConsumerContext implement IContext it is context for Consumer
11 | type ConsumerContext struct {
12 | ms *Microservice
13 | message string
14 | }
15 |
16 | // NewConsumerContext is the constructor function for ConsumerContext
17 | func NewConsumerContext(ms *Microservice, message string) *ConsumerContext {
18 | return &ConsumerContext{
19 | ms: ms,
20 | message: message,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *ConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *ConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer
37 | func (ctx *ConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message
42 | func (ctx *ConsumerContext) ReadInput() string {
43 | return ctx.message
44 | }
45 |
46 | // ReadInputs return nil in case Consumer
47 | func (ctx *ConsumerContext) ReadInputs() []string {
48 | return nil
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *ConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *ConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *ConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *ConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/context_consumer_batch.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // BatchConsumerContext implement IContext it is context for Consumer
11 | type BatchConsumerContext struct {
12 | ms *Microservice
13 | messages []string
14 | }
15 |
16 | // NewBatchConsumerContext is the constructor function for BatchConsumerContext
17 | func NewBatchConsumerContext(ms *Microservice, messages []string) *BatchConsumerContext {
18 | return &BatchConsumerContext{
19 | ms: ms,
20 | messages: messages,
21 | }
22 | }
23 |
24 | // Log will log a message
25 | func (ctx *BatchConsumerContext) Log(message string) {
26 | _, fn, line, _ := runtime.Caller(1)
27 | fns := strings.Split(fn, "/")
28 | fmt.Println("Batch Consumer:", fns[len(fns)-1], line, message)
29 | }
30 |
31 | // Param return parameter by name (empty in case of Consumer)
32 | func (ctx *BatchConsumerContext) Param(name string) string {
33 | return ""
34 | }
35 |
36 | // QueryParam return empty in consumer batch
37 | func (ctx *BatchConsumerContext) QueryParam(name string) string {
38 | return ""
39 | }
40 |
41 | // ReadInput return message (return empty in batch consumer)
42 | func (ctx *BatchConsumerContext) ReadInput() string {
43 | return ""
44 | }
45 |
46 | // ReadInputs return messages in batch
47 | func (ctx *BatchConsumerContext) ReadInputs() []string {
48 | return ctx.messages
49 | }
50 |
51 | // Response return response to client
52 | func (ctx *BatchConsumerContext) Response(responseCode int, responseData interface{}) {
53 | return
54 | }
55 |
56 | // Cacher return cacher
57 | func (ctx *BatchConsumerContext) Cacher(server string) ICacher {
58 | return ctx.ms.getCacher(server)
59 | }
60 |
61 | // Producer return producer
62 | func (ctx *BatchConsumerContext) Producer(servers string) IProducer {
63 | return ctx.ms.getProducer(servers)
64 | }
65 |
66 | // MQ return MQ
67 | func (ctx *BatchConsumerContext) MQ(servers string) IMQ {
68 | return NewMQ(servers, ctx.ms)
69 | }
70 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/context_http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 | "runtime"
7 | "strings"
8 |
9 | "github.com/labstack/echo"
10 | )
11 |
12 | // HTTPContext implement IContext it is context for HTTP
13 | type HTTPContext struct {
14 | ms *Microservice
15 | c echo.Context
16 | }
17 |
18 | // NewHTTPContext is the constructor function for HTTPContext
19 | func NewHTTPContext(ms *Microservice, c echo.Context) *HTTPContext {
20 | return &HTTPContext{
21 | ms: ms,
22 | c: c,
23 | }
24 | }
25 |
26 | // Log will log a message
27 | func (ctx *HTTPContext) Log(message string) {
28 | _, fn, line, _ := runtime.Caller(1)
29 | fns := strings.Split(fn, "/")
30 | fmt.Println("HTTP:", fns[len(fns)-1], line, message)
31 | }
32 |
33 | // Param return parameter by name
34 | func (ctx *HTTPContext) Param(name string) string {
35 | return ctx.c.Param(name)
36 | }
37 |
38 | // QueryParam return query param
39 | func (ctx *HTTPContext) QueryParam(name string) string {
40 | return ctx.c.QueryParam(name)
41 | }
42 |
43 | // ReadInput read the request body and return it as string
44 | func (ctx *HTTPContext) ReadInput() string {
45 | body, err := ioutil.ReadAll(ctx.c.Request().Body)
46 | if err != nil {
47 | return ""
48 | }
49 | return string(body)
50 | }
51 |
52 | // ReadInputs return nil in HTTP Context
53 | func (ctx *HTTPContext) ReadInputs() []string {
54 | return nil
55 | }
56 |
57 | // Response return response to client
58 | func (ctx *HTTPContext) Response(responseCode int, responseData interface{}) {
59 | ctx.c.JSON(responseCode, responseData)
60 | }
61 |
62 | // Cacher return cacher
63 | func (ctx *HTTPContext) Cacher(server string) ICacher {
64 | return ctx.ms.getCacher(server)
65 | }
66 |
67 | // Producer return producer
68 | func (ctx *HTTPContext) Producer(servers string) IProducer {
69 | return ctx.ms.getProducer(servers)
70 | }
71 |
72 | // MQ return MQ
73 | func (ctx *HTTPContext) MQ(servers string) IMQ {
74 | return NewMQ(servers, ctx.ms)
75 | }
76 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/context_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | )
9 |
10 | // SchedulerContext implement IContext it is context for Consumer
11 | type SchedulerContext struct {
12 | ms *Microservice
13 | }
14 |
15 | // NewSchedulerContext is the constructor function for SchedulerContext
16 | func NewSchedulerContext(ms *Microservice) *SchedulerContext {
17 | return &SchedulerContext{
18 | ms: ms,
19 | }
20 | }
21 |
22 | // Log will log a message
23 | func (ctx *SchedulerContext) Log(message string) {
24 | _, fn, line, _ := runtime.Caller(1)
25 | fns := strings.Split(fn, "/")
26 | fmt.Println("Scheduler:", fns[len(fns)-1], line, message)
27 | }
28 |
29 | // Param return parameter by name (empty in scheduler)
30 | func (ctx *SchedulerContext) Param(name string) string {
31 | return ""
32 | }
33 |
34 | // QueryParam return empty in scheduler
35 | func (ctx *SchedulerContext) QueryParam(name string) string {
36 | return ""
37 | }
38 |
39 | // ReadInput return message (return empty in scheduler)
40 | func (ctx *SchedulerContext) ReadInput() string {
41 | return ""
42 | }
43 |
44 | // ReadInputs return messages in batch (return nil in scheduler)
45 | func (ctx *SchedulerContext) ReadInputs() []string {
46 | return nil
47 | }
48 |
49 | // Response return response to client
50 | func (ctx *SchedulerContext) Response(responseCode int, responseData interface{}) {
51 | return
52 | }
53 |
54 | // Cacher return cacher
55 | func (ctx *SchedulerContext) Cacher(server string) ICacher {
56 | return ctx.ms.getCacher(server)
57 | }
58 |
59 | // Producer return producer
60 | func (ctx *SchedulerContext) Producer(servers string) IProducer {
61 | return ctx.ms.getProducer(servers)
62 | }
63 |
64 | // MQ return MQ
65 | func (ctx *SchedulerContext) MQ(servers string) IMQ {
66 | return NewMQ(servers, ctx.ms)
67 | }
68 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 1. Format of docker image is $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
4 | # for example 3dsinteractive/automation-technology:prd-1.0.20210117202336
5 | APP_VERSION=1.0
6 | TIMESTAMP=20210117202336
7 | DEPLOY_ENV=prd
8 | DOCKER_REPOSITORY=
9 |
10 | # 2. commit will push docker image to repository
11 | function commit() {
12 | local IMAGE=$1
13 | echo "docker push image : $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP"
14 | docker push $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
15 | }
16 |
17 | # 3. build_api is the main function to build Dockerfile
18 | function build_api() {
19 | local IMAGE=automation-technology
20 |
21 | # If found go in default path, it will use go from default path
22 | GO=/usr/local/go/bin/go
23 | if [ -f "$GO" ]; then
24 | /usr/local/go/bin/go mod init automationworkshop/main
25 | /usr/local/go/bin/go get
26 | /usr/local/go/bin/go mod vendor
27 | else
28 | go mod init automationworkshop/main
29 | go get
30 | go mod vendor
31 | fi
32 |
33 | # Build the Dockerfile
34 | docker build -f Dockerfile -t $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP .
35 | commit $IMAGE
36 | }
37 |
38 | # 4. Validate APP_VERSION must not empty
39 | if [ "$APP_VERSION" = "" ]; then
40 | echo -e "APP_VERSION cannot be blank"
41 | exit 1
42 | fi
43 |
44 | # 5. Validate TIMESTAMP must not empty
45 | if [ "$TIMESTAMP" = "" ]; then
46 | echo -e "TIMESTAMP cannot be blank"
47 | exit 1
48 | fi
49 |
50 | # 6. Validate DEPLOY_ENV must not empty
51 | if [ "$DEPLOY_ENV" == "" ]; then
52 | echo -e "DEPLOY_ENV cannot be blank"
53 | exit 1
54 | fi
55 |
56 | # 7. Validate DOCKER_REPOSITORY must not empty
57 | if [ "$DOCKER_REPOSITORY" == "" ]; then
58 | echo -e "DOCKER_REPOSITORY cannot be blank"
59 | exit 1
60 | fi
61 |
62 | # 8. Run main build process
63 | build_api
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # exec env file if exists, .env might be in /.env or /config/.env
4 | FILE=/.env ; [ -f $FILE ] && . $FILE
5 | FILE=/config/.env ; [ -f $FILE ] && . $FILE
6 |
7 | # start main program
8 | exec /main
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/k8s/00-databases/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: scale-service
5 | labels:
6 | name: scale-service
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/k8s/00-databases/01-redis.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: redis
5 | namespace: scale-service
6 | labels:
7 | name: redis
8 | spec:
9 | replicas: 1
10 | strategy:
11 | type: Recreate
12 | selector:
13 | matchLabels:
14 | name: redis
15 | template:
16 | metadata:
17 | labels:
18 | name: redis
19 | spec:
20 | containers:
21 | - name: redis
22 | image: 3dsinteractive/redis:5.0
23 | imagePullPolicy: Always
24 | ports:
25 | - name: redis6379
26 | containerPort: 6379
27 | env:
28 | - name: ALLOW_EMPTY_PASSWORD
29 | value: "yes"
30 | resources:
31 | requests:
32 | memory: 500Mi
33 | cpu: 200m
34 | limits:
35 | memory: 1Gi
36 | cpu: 500m
37 | livenessProbe:
38 | tcpSocket:
39 | port: 6379
40 | initialDelaySeconds: 30
41 | timeoutSeconds: 1
42 | periodSeconds: 300
43 | readinessProbe:
44 | tcpSocket:
45 | port: 6379
46 | initialDelaySeconds: 30
47 | timeoutSeconds: 1
48 | periodSeconds: 30
49 | failureThreshold: 5
50 | ---
51 | apiVersion: v1
52 | kind: Service
53 | metadata:
54 | name: redis
55 | namespace: scale-service
56 | labels:
57 | name: redis
58 | spec:
59 | selector:
60 | name: redis
61 | ports:
62 | - name: redis6379
63 | port: 6379
64 | targetPort: 6379
65 | protocol: TCP
66 | clusterIP: None
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/k8s/00-databases/04-client-util.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: client-util
5 | namespace: scale-service
6 | labels:
7 | name: client-util
8 | spec:
9 | containers:
10 | - name: client-util
11 | image: opcellent/util:2.0
12 | stdin: true
13 | tty: true
14 | imagePullPolicy: Always
15 | resources:
16 | requests:
17 | memory: 500Mi
18 | cpu: 200m
19 | limits:
20 | memory: 1Gi
21 | cpu: 500m
22 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/k8s/01-application/05-register-api.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: register-api
5 | namespace: scale-service
6 | labels:
7 | name: register-api
8 | spec:
9 | replicas: 2
10 | strategy:
11 | type: RollingUpdate
12 | rollingUpdate:
13 | maxUnavailable: 1
14 | maxSurge: 1
15 | selector:
16 | matchLabels:
17 | name: register-api
18 | template:
19 | metadata:
20 | labels:
21 | name: register-api
22 | spec:
23 | containers:
24 | - name: register-api
25 | image: 3dsinteractive/automation-technology:prd-1.0.20210117202336
26 | imagePullPolicy: Always
27 | readinessProbe:
28 | httpGet:
29 | path: /healthz
30 | port: 8080
31 | initialDelaySeconds: 10
32 | periodSeconds: 10
33 | timeoutSeconds: 3
34 | failureThreshold: 3
35 | livenessProbe:
36 | httpGet:
37 | path: /healthz
38 | port: 8080
39 | initialDelaySeconds: 60
40 | periodSeconds: 30
41 | timeoutSeconds: 30
42 | failureThreshold: 2
43 | env:
44 | - name: SERVICE_ID
45 | value: register-api
46 | - name: CACHE_SERVER
47 | value: redis:6379
48 | - name: MQ_SERVERS
49 | value: kfk1:9092,kfk2:9092,kfk3:9092
50 | ports:
51 | - name: api8080
52 | containerPort: 8080
53 | resources:
54 | requests:
55 | memory: 500Mi
56 | cpu: 200m
57 | limits:
58 | memory: 1Gi
59 | cpu: 500m
60 | ---
61 | apiVersion: v1
62 | kind: Service
63 | metadata:
64 | name: register-api
65 | namespace: scale-service
66 | labels:
67 | name: register-api
68 | spec:
69 | selector:
70 | name: register-api
71 | ports:
72 | - name: "api8080"
73 | port: 8080
74 | targetPort: 8080
75 | protocol: TCP
76 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/k8s/01-application/06-mail-consumer.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: mail-consumer
5 | namespace: scale-service
6 | labels:
7 | name: mail-consumer
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | name: mail-consumer
13 | template:
14 | metadata:
15 | labels:
16 | name: mail-consumer
17 | spec:
18 | containers:
19 | - name: mail-consumer
20 | image: 3dsinteractive/automation-technology:prd-1.0.20210117202336
21 | imagePullPolicy: Always
22 | readinessProbe:
23 | httpGet:
24 | path: /healthz
25 | port: 8080
26 | initialDelaySeconds: 10
27 | periodSeconds: 10
28 | timeoutSeconds: 3
29 | failureThreshold: 3
30 | livenessProbe:
31 | httpGet:
32 | path: /healthz
33 | port: 8080
34 | initialDelaySeconds: 60
35 | periodSeconds: 30
36 | timeoutSeconds: 30
37 | failureThreshold: 2
38 | env:
39 | - name: SERVICE_ID
40 | value: mail-consumer
41 | - name: CACHE_SERVER
42 | value: redis:6379
43 | - name: MQ_SERVERS
44 | value: kfk1:9092,kfk2:9092,kfk3:9092
45 | ports:
46 | - name: api8080
47 | containerPort: 8080
48 | resources:
49 | requests:
50 | memory: 500Mi
51 | cpu: 200m
52 | limits:
53 | memory: 1Gi
54 | cpu: 500m
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/k8s/01-application/07-ingress.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Ingress
3 | metadata:
4 | name: ingress
5 | namespace: scale-service
6 | labels:
7 | name: ingress
8 | spec:
9 | rules:
10 | - host: kubernetes.docker.internal
11 | http:
12 | paths:
13 | - path: /
14 | backend:
15 | serviceName: register-api
16 | servicePort: 8080
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/main.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "net/http"
6 | "time"
7 | )
8 |
9 | func main() {
10 | cfg := NewConfig()
11 |
12 | ms := NewMicroservice()
13 | ms.RegisterLivenessProbeEndpoint("/healthz")
14 |
15 | // 1. Read SERVICE_ID via environment variable to run each service in separate deployments
16 | serviceID := cfg.ServiceID()
17 |
18 | // 2. Finally, these 2 services will run in separate deployments,
19 | // so we could control how many replicas each services will be
20 | switch serviceID {
21 | case "register-api":
22 | startHTTP(ms, cfg)
23 | case "mail-consumer":
24 | startConsumer(ms, cfg)
25 | }
26 |
27 | ms.Start()
28 | }
29 |
30 | func startHTTP(ms *Microservice, cfg IConfig) {
31 | ms.POST("/citizen", func(ctx IContext) error {
32 |
33 | // 1. Read Input (Not using it right now, just for example)
34 | input := ctx.ReadInput()
35 | ctx.Log("POST: /citizen " + input)
36 |
37 | // 2. Generate citizenID and send it to MQ
38 | citizenID := randString()
39 | citizen := map[string]interface{}{
40 | "citizen_id": citizenID,
41 | }
42 | prod := ctx.Producer(cfg.MQServers())
43 | err := prod.SendMessage("when-citizen-has-registered", "", citizen)
44 | if err != nil {
45 | ctx.Log(err.Error())
46 | return err
47 | }
48 |
49 | // 3. Response citizenID
50 | status := map[string]interface{}{
51 | "status": "success",
52 | "citizen_id": citizenID,
53 | }
54 | ctx.Response(http.StatusOK, status)
55 | return nil
56 | })
57 | }
58 |
59 | func startConsumer(ms *Microservice, cfg IConfig) {
60 | topic := "when-citizen-has-registered"
61 | groupID := "mail-consumer"
62 | timeout := time.Duration(-1)
63 |
64 | mq := NewMQ(cfg.MQServers(), ms)
65 | mq.CreateTopicR(topic, 5, 1, time.Hour*24*30)
66 | ms.Consume(cfg.MQServers(), topic, groupID, timeout, func(ctx IContext) error {
67 | msg := ctx.ReadInput()
68 | ctx.Log("Mail has sent to " + msg)
69 | return nil
70 | })
71 | }
72 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/microservice_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/confluentinc/confluent-kafka-go/kafka"
8 | )
9 |
10 | func (ms *Microservice) consumeSingle(servers string, topic string, groupID string, readTimeout time.Duration, h ServiceHandleFunc) {
11 | c, err := ms.newKafkaConsumer(servers, groupID)
12 | if err != nil {
13 | return
14 | }
15 |
16 | defer c.Close()
17 |
18 | c.Subscribe(topic, nil)
19 |
20 | for {
21 | if readTimeout <= 0 {
22 | // readtimeout -1 indicates no timeout
23 | readTimeout = -1
24 | }
25 |
26 | msg, err := c.ReadMessage(readTimeout)
27 | if err != nil {
28 | kafkaErr, ok := err.(kafka.Error)
29 | if ok {
30 | if kafkaErr.Code() == kafka.ErrTimedOut {
31 | if readTimeout == -1 {
32 | // No timeout just continue to read message again
33 | continue
34 | }
35 | }
36 | }
37 | ms.Log("Consumer", err.Error())
38 | ms.Stop()
39 | return
40 | }
41 |
42 | // Execute Handler
43 | h(NewConsumerContext(ms, string(msg.Value)))
44 | }
45 | }
46 |
47 | // Consume register service endpoint for Consumer service
48 | func (ms *Microservice) Consume(servers string, topic string, groupID string, readTimeout time.Duration, h ServiceHandleFunc) error {
49 | go ms.consumeSingle(servers, topic, groupID, readTimeout, h)
50 | return nil
51 | }
52 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/microservice_http.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "context"
6 | "time"
7 |
8 | "github.com/labstack/echo"
9 | )
10 |
11 | // GET register service endpoint for HTTP GET
12 | func (ms *Microservice) GET(path string, h ServiceHandleFunc) {
13 | ms.echo.GET(path, func(c echo.Context) error {
14 | return h(NewHTTPContext(ms, c))
15 | })
16 | }
17 |
18 | // POST register service endpoint for HTTP POST
19 | func (ms *Microservice) POST(path string, h ServiceHandleFunc) {
20 | ms.echo.POST(path, func(c echo.Context) error {
21 | return h(NewHTTPContext(ms, c))
22 | })
23 | }
24 |
25 | // PUT register service endpoint for HTTP PUT
26 | func (ms *Microservice) PUT(path string, h ServiceHandleFunc) {
27 | ms.echo.PUT(path, func(c echo.Context) error {
28 | return h(NewHTTPContext(ms, c))
29 | })
30 | }
31 |
32 | // PATCH register service endpoint for HTTP PATCH
33 | func (ms *Microservice) PATCH(path string, h ServiceHandleFunc) {
34 | ms.echo.PATCH(path, func(c echo.Context) error {
35 | return h(NewHTTPContext(ms, c))
36 | })
37 | }
38 |
39 | // DELETE register service endpoint for HTTP DELETE
40 | func (ms *Microservice) DELETE(path string, h ServiceHandleFunc) {
41 | ms.echo.DELETE(path, func(c echo.Context) error {
42 | return h(NewHTTPContext(ms, c))
43 | })
44 | }
45 |
46 | // startHTTP will start HTTP service, this function will block thread
47 | func (ms *Microservice) startHTTP(exitChannel chan bool) error {
48 | // Caller can exit by sending value to exitChannel
49 | go func() {
50 | <-exitChannel
51 | ms.stopHTTP()
52 | }()
53 | return ms.echo.Start(":8080")
54 | }
55 |
56 | func (ms *Microservice) stopHTTP() {
57 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
58 | defer cancel()
59 | ms.echo.Shutdown(ctx)
60 | }
61 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/microservice_liveness.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "net/http"
6 |
7 | "github.com/labstack/echo"
8 | )
9 |
10 | func (ms *Microservice) isCacherAlive() bool {
11 | if ms.cacher == nil {
12 | return true
13 | }
14 |
15 | ms.Log("MS", "Perform healthcheck on Cacher")
16 | err := ms.cacher.Healthcheck()
17 | if err != nil {
18 | return false
19 | }
20 | return true
21 | }
22 |
23 | func (ms *Microservice) isAlive() (bool, string) {
24 | isAlive := ms.isCacherAlive()
25 | if !isAlive {
26 | return false, "Cacher healthcheck failed"
27 | }
28 |
29 | return true, ""
30 | }
31 |
32 | func (ms *Microservice) responseProbeOK(resp *echo.Response) {
33 | resp.WriteHeader(http.StatusOK)
34 | resp.Write([]byte("ok"))
35 | }
36 |
37 | func (ms *Microservice) responseProbeFailed(resp *echo.Response, reason string) {
38 | errMsg := "Healthcheck failed because of " + reason
39 | resp.WriteHeader(http.StatusInternalServerError)
40 | resp.Write([]byte(errMsg))
41 | }
42 |
43 | // RegisterLivenessProbeEndpoint register endpoint for liveness probe
44 | func (ms *Microservice) RegisterLivenessProbeEndpoint(path string) {
45 | ms.echo.GET(path, func(c echo.Context) error {
46 | ok, reason := ms.isAlive()
47 | if !ok {
48 | ms.responseProbeFailed(c.Response(), reason)
49 | return nil
50 | }
51 | ms.responseProbeOK(c.Response())
52 | return nil
53 | })
54 | }
55 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/microservice_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "sync"
6 | "time"
7 | )
8 |
9 | // Schedule will run handler at timer period
10 | func (ms *Microservice) Schedule(timer time.Duration, h ServiceHandleFunc) chan bool /*exit channel*/ {
11 |
12 | // exitChan must be call exitChan <- true from caller to exit scheduler
13 | exitChan := make(chan bool, 1)
14 | go func() {
15 | t := time.NewTicker(timer)
16 | done := make(chan bool, 1)
17 | isExit := false
18 | isExitMutex := sync.Mutex{}
19 |
20 | go func() {
21 | <-exitChan
22 | isExitMutex.Lock()
23 | isExit = true
24 | isExitMutex.Unlock()
25 | // Stop Tick() and send done message to exit for loop below
26 | // Ref: From the documentation http://golang.org/pkg/time/#Ticker.Stop
27 | // Stop turns off a ticker. After Stop, no more ticks will be sent.
28 | // Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
29 | t.Stop()
30 | done <- true
31 | }()
32 |
33 | for {
34 | select {
35 | case execTime := <-t.C:
36 | isExitMutex.Lock()
37 | if isExit {
38 | isExitMutex.Unlock()
39 | // Done in the next round
40 | continue
41 | }
42 | isExitMutex.Unlock()
43 |
44 | now := time.Now()
45 | // The schedule that older than 10s, will be skip, because t.C is buffer at size 1
46 | diff := now.Sub(execTime).Seconds()
47 | if diff > 10 {
48 | continue
49 | }
50 | h(NewSchedulerContext(ms))
51 | case <-done:
52 | return
53 | }
54 | }
55 | }()
56 |
57 | return exitChan
58 | }
59 |
--------------------------------------------------------------------------------
/chapter03-deploy-scale-services/3.3-scale-services/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM 3dsinteractive/golang:1.14-alpine3.9-librdfkafka1.4.0
2 |
3 | WORKDIR /go/src/bitbucket.org/automationworkshop/main
4 | ADD . /go/src/bitbucket.org/automationworkshop/main
5 | RUN go build -mod vendor -i -tags "musl static_all" .
6 |
7 | # ================
8 | FROM 3dsinteractive/alpine:3.9
9 |
10 | COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
11 | COPY --from=0 /go/src/bitbucket.org/automationworkshop/main/main /main
12 |
13 | ADD ./entrypoint.sh /entrypoint.sh
14 |
15 | RUN adduser -u 1001 -D -s /bin/sh -G ping 1001
16 | RUN chown 1001:1001 /entrypoint.sh
17 | RUN chown 1001:1001 /main
18 |
19 | RUN chmod +x /entrypoint.sh
20 | RUN chmod +x /main
21 |
22 | USER 1001
23 |
24 | EXPOSE 8080
25 |
26 | ENTRYPOINT ["/entrypoint.sh"]
27 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/config.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "os"
5 |
6 | // IConfig is interface for application config
7 | type IConfig interface {
8 | ServiceID() string
9 | CacheServer() string
10 | MQServers() string
11 | CitizenRegisteredTopic() string
12 | CitizenConfirmedTopic() string
13 | CitizenValidationAPI() string
14 | CitizenDeliveryAPI() string
15 | BatchDeliverAPI() string
16 | }
17 |
18 | // Config implement IConfig
19 | type Config struct{}
20 |
21 | // NewConfig return new config instance
22 | func NewConfig() *Config {
23 | return &Config{}
24 | }
25 |
26 | // ServiceID return ID of service
27 | func (cfg *Config) ServiceID() string {
28 | return os.Getenv("SERVICE_ID")
29 | }
30 |
31 | // CacheServer return redis server
32 | func (cfg *Config) CacheServer() string {
33 | return os.Getenv("CACHE_SERVER")
34 | }
35 |
36 | // MQServers return Kafka servers
37 | func (cfg *Config) MQServers() string {
38 | return os.Getenv("MQ_SERVERS")
39 | }
40 |
41 | // CitizenRegisteredTopic return topic name for registered event
42 | func (cfg *Config) CitizenRegisteredTopic() string {
43 | return "when-citizen-has-registered"
44 | }
45 |
46 | // CitizenConfirmedTopic return topic name for confirmed event
47 | func (cfg *Config) CitizenConfirmedTopic() string {
48 | return "when-citizen-has-confirmed"
49 | }
50 |
51 | // CitizenValidationAPI return API to validate citizen information
52 | func (cfg *Config) CitizenValidationAPI() string {
53 | return "http://external-api:8080/3rd-party/validate"
54 | }
55 |
56 | // CitizenDeliveryAPI return API to request delivery citizen ID card
57 | func (cfg *Config) CitizenDeliveryAPI() string {
58 | return "http://external-api:8080/3rd-party/delivery"
59 | }
60 |
61 | // BatchDeliverAPI return API to batch delivery citizen ID card
62 | func (cfg *Config) BatchDeliverAPI() string {
63 | return "http://batch-ptask-api:8080/ptask/delivery"
64 | }
65 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/context.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import "time"
5 |
6 | // IContext is the context for service
7 | type IContext interface {
8 | Log(message string)
9 | Param(name string) string
10 | QueryParam(name string) string
11 | Response(responseCode int, responseData interface{})
12 | ReadInput() string
13 | ReadInputs() []string
14 |
15 | // Time
16 | Now() time.Time
17 |
18 | // Dependency
19 | Cacher(server string) ICacher
20 | Producer(servers string) IProducer
21 | MQ(servers string) IMQ
22 | Requester(baseURL string, timeout time.Duration) IRequester
23 | }
24 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/context_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "fmt"
6 | "runtime"
7 | "strings"
8 | "time"
9 | )
10 |
11 | // SchedulerContext implement IContext it is context for Consumer
12 | type SchedulerContext struct {
13 | ms *Microservice
14 | }
15 |
16 | // NewSchedulerContext is the constructor function for SchedulerContext
17 | func NewSchedulerContext(ms *Microservice) *SchedulerContext {
18 | return &SchedulerContext{
19 | ms: ms,
20 | }
21 | }
22 |
23 | // Log will log a message
24 | func (ctx *SchedulerContext) Log(message string) {
25 | _, fn, line, _ := runtime.Caller(1)
26 | fns := strings.Split(fn, "/")
27 | fmt.Println("Scheduler:", fns[len(fns)-1], line, message)
28 | }
29 |
30 | // Param return parameter by name (empty in scheduler)
31 | func (ctx *SchedulerContext) Param(name string) string {
32 | return ""
33 | }
34 |
35 | // QueryParam return empty in scheduler
36 | func (ctx *SchedulerContext) QueryParam(name string) string {
37 | return ""
38 | }
39 |
40 | // ReadInput return message (return empty in scheduler)
41 | func (ctx *SchedulerContext) ReadInput() string {
42 | return ""
43 | }
44 |
45 | // ReadInputs return messages in batch (return nil in scheduler)
46 | func (ctx *SchedulerContext) ReadInputs() []string {
47 | return nil
48 | }
49 |
50 | // Response return response to client
51 | func (ctx *SchedulerContext) Response(responseCode int, responseData interface{}) {
52 | return
53 | }
54 |
55 | // Now return now
56 | func (ctx *SchedulerContext) Now() time.Time {
57 | return time.Now()
58 | }
59 |
60 | // Cacher return cacher
61 | func (ctx *SchedulerContext) Cacher(server string) ICacher {
62 | return ctx.ms.getCacher(server)
63 | }
64 |
65 | // Producer return producer
66 | func (ctx *SchedulerContext) Producer(servers string) IProducer {
67 | return ctx.ms.getProducer(servers)
68 | }
69 |
70 | // MQ return MQ
71 | func (ctx *SchedulerContext) MQ(servers string) IMQ {
72 | return NewMQ(servers, ctx.ms)
73 | }
74 |
75 | // Requester return Requester
76 | func (ctx *SchedulerContext) Requester(baseURL string, timeout time.Duration) IRequester {
77 | return NewRequester(baseURL, timeout, ctx.ms)
78 | }
79 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/deploy.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # 1. Format of docker image is $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
4 | # for example 3dsinteractive/automation-technology:prd-1.0.20210118001006
5 | APP_VERSION=1.0
6 | TIMESTAMP=20210118001006
7 | DEPLOY_ENV=prd
8 | DOCKER_REPOSITORY=3dsinteractive
9 |
10 | # 2. commit will push docker image to repository
11 | function commit() {
12 | local IMAGE=$1
13 | echo "docker push image : $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP"
14 | docker push $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP
15 | }
16 |
17 | # 3. build_api is the main function to build Dockerfile
18 | function build_api() {
19 | local IMAGE=automation-technology
20 |
21 | # If found go in default path, it will use go from default path
22 | GO=/usr/local/go/bin/go
23 | if [ -f "$GO" ]; then
24 | /usr/local/go/bin/go mod init automationworkshop/main
25 | /usr/local/go/bin/go get
26 | /usr/local/go/bin/go mod vendor
27 | else
28 | go mod init automationworkshop/main
29 | go get
30 | go mod vendor
31 | fi
32 |
33 | # Build the Dockerfile
34 | docker build -f Dockerfile -t $DOCKER_REPOSITORY/$IMAGE:$DEPLOY_ENV-$APP_VERSION.$TIMESTAMP .
35 | commit $IMAGE
36 | }
37 |
38 | # 4. Validate APP_VERSION must not empty
39 | if [ "$APP_VERSION" = "" ]; then
40 | echo -e "APP_VERSION cannot be blank"
41 | exit 1
42 | fi
43 |
44 | # 5. Validate TIMESTAMP must not empty
45 | if [ "$TIMESTAMP" = "" ]; then
46 | echo -e "TIMESTAMP cannot be blank"
47 | exit 1
48 | fi
49 |
50 | # 6. Validate DEPLOY_ENV must not empty
51 | if [ "$DEPLOY_ENV" == "" ]; then
52 | echo -e "DEPLOY_ENV cannot be blank"
53 | exit 1
54 | fi
55 |
56 | # 7. Validate DOCKER_REPOSITORY must not empty
57 | if [ "$DOCKER_REPOSITORY" == "" ]; then
58 | echo -e "DOCKER_REPOSITORY cannot be blank"
59 | exit 1
60 | fi
61 |
62 | # 8. Run main build process
63 | build_api
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh -e
2 |
3 | # exec env file if exists, .env might be in /.env or /config/.env
4 | FILE=/.env ; [ -f $FILE ] && . $FILE
5 | FILE=/config/.env ; [ -f $FILE ] && . $FILE
6 |
7 | # start main program
8 | exec /main -profiler=true
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/00-databases/00-namespace.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: tcir-app
5 | labels:
6 | name: tcir-app
7 | module: Namespace
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/00-databases/01-redis.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: redis
5 | namespace: tcir-app
6 | labels:
7 | name: redis
8 | spec:
9 | replicas: 1
10 | strategy:
11 | type: Recreate
12 | selector:
13 | matchLabels:
14 | name: redis
15 | template:
16 | metadata:
17 | labels:
18 | name: redis
19 | spec:
20 | containers:
21 | - name: redis
22 | image: 3dsinteractive/redis:5.0
23 | imagePullPolicy: Always
24 | ports:
25 | - name: redis6379
26 | containerPort: 6379
27 | env:
28 | - name: ALLOW_EMPTY_PASSWORD
29 | value: "yes"
30 | resources:
31 | requests:
32 | memory: 500Mi
33 | cpu: 200m
34 | limits:
35 | memory: 1Gi
36 | cpu: 500m
37 | livenessProbe:
38 | tcpSocket:
39 | port: 6379
40 | initialDelaySeconds: 30
41 | timeoutSeconds: 1
42 | periodSeconds: 300
43 | readinessProbe:
44 | tcpSocket:
45 | port: 6379
46 | initialDelaySeconds: 30
47 | timeoutSeconds: 1
48 | periodSeconds: 30
49 | failureThreshold: 5
50 | ---
51 | apiVersion: v1
52 | kind: Service
53 | metadata:
54 | name: redis
55 | namespace: tcir-app
56 | labels:
57 | name: redis
58 | spec:
59 | selector:
60 | name: redis
61 | ports:
62 | - name: redis6379
63 | port: 6379
64 | targetPort: 6379
65 | protocol: TCP
66 | clusterIP: None
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/00-databases/04-client-util.yml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | name: client-util
5 | namespace: tcir-app
6 | labels:
7 | name: client-util
8 | spec:
9 | containers:
10 | - name: client-util
11 | image: opcellent/util:2.0
12 | stdin: true
13 | tty: true
14 | imagePullPolicy: Always
15 | resources:
16 | requests:
17 | memory: 500Mi
18 | cpu: 200m
19 | limits:
20 | memory: 1Gi
21 | cpu: 500m
22 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/05-register-api.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: register-api
5 | namespace: tcir-app
6 | labels:
7 | name: register-api
8 | spec:
9 | replicas: 2
10 | strategy:
11 | type: RollingUpdate
12 | rollingUpdate:
13 | maxUnavailable: 1
14 | maxSurge: 1
15 | selector:
16 | matchLabels:
17 | name: register-api
18 | template:
19 | metadata:
20 | labels:
21 | name: register-api
22 | spec:
23 | containers:
24 | - name: register-api
25 | image: 3dsinteractive/automation-technology:prd-1.0.20210118001006
26 | imagePullPolicy: Always
27 | readinessProbe:
28 | httpGet:
29 | path: /healthz
30 | port: 8080
31 | initialDelaySeconds: 10
32 | periodSeconds: 10
33 | timeoutSeconds: 3
34 | failureThreshold: 3
35 | livenessProbe:
36 | httpGet:
37 | path: /healthz
38 | port: 8080
39 | initialDelaySeconds: 60
40 | periodSeconds: 30
41 | timeoutSeconds: 30
42 | failureThreshold: 2
43 | env:
44 | - name: SERVICE_ID
45 | value: register-api
46 | - name: CACHE_SERVER
47 | value: redis:6379
48 | - name: MQ_SERVERS
49 | value: kfk1:9092,kfk2:9092,kfk3:9092
50 | ports:
51 | - name: api8080
52 | containerPort: 8080
53 | resources:
54 | requests:
55 | memory: 500Mi
56 | cpu: 200m
57 | limits:
58 | memory: 1Gi
59 | cpu: 500m
60 | ---
61 | apiVersion: v1
62 | kind: Service
63 | metadata:
64 | name: register-api
65 | namespace: tcir-app
66 | labels:
67 | name: register-api
68 | spec:
69 | selector:
70 | name: register-api
71 | ports:
72 | - name: "api8080"
73 | port: 8080
74 | targetPort: 8080
75 | protocol: TCP
76 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/06-mail-consumer.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: mail-consumer
5 | namespace: tcir-app
6 | labels:
7 | name: mail-consumer
8 | spec:
9 | replicas: 3
10 | selector:
11 | matchLabels:
12 | name: mail-consumer
13 | template:
14 | metadata:
15 | labels:
16 | name: mail-consumer
17 | spec:
18 | containers:
19 | - name: mail-consumer
20 | image: 3dsinteractive/automation-technology:prd-1.0.20210118001006
21 | imagePullPolicy: Always
22 | readinessProbe:
23 | httpGet:
24 | path: /healthz
25 | port: 8080
26 | initialDelaySeconds: 10
27 | periodSeconds: 10
28 | timeoutSeconds: 3
29 | failureThreshold: 3
30 | livenessProbe:
31 | httpGet:
32 | path: /healthz
33 | port: 8080
34 | initialDelaySeconds: 60
35 | periodSeconds: 30
36 | timeoutSeconds: 30
37 | failureThreshold: 2
38 | env:
39 | - name: SERVICE_ID
40 | value: mail-consumer
41 | - name: CACHE_SERVER
42 | value: redis:6379
43 | - name: MQ_SERVERS
44 | value: kfk1:9092,kfk2:9092,kfk3:9092
45 | ports:
46 | - name: api8080
47 | containerPort: 8080
48 | resources:
49 | requests:
50 | memory: 500Mi
51 | cpu: 200m
52 | limits:
53 | memory: 1Gi
54 | cpu: 500m
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/07-batch-scheduler.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: batch-scheduler
5 | namespace: tcir-app
6 | labels:
7 | name: batch-scheduler
8 | spec:
9 | replicas: 1
10 | selector:
11 | matchLabels:
12 | name: batch-scheduler
13 | template:
14 | metadata:
15 | labels:
16 | name: batch-scheduler
17 | spec:
18 | containers:
19 | - name: batch-scheduler
20 | image: 3dsinteractive/automation-technology:prd-1.0.20210118001006
21 | imagePullPolicy: Always
22 | readinessProbe:
23 | httpGet:
24 | path: /healthz
25 | port: 8080
26 | initialDelaySeconds: 10
27 | periodSeconds: 10
28 | timeoutSeconds: 3
29 | failureThreshold: 3
30 | livenessProbe:
31 | httpGet:
32 | path: /healthz
33 | port: 8080
34 | initialDelaySeconds: 60
35 | periodSeconds: 30
36 | timeoutSeconds: 30
37 | failureThreshold: 2
38 | env:
39 | - name: SERVICE_ID
40 | value: batch-scheduler
41 | - name: CACHE_SERVER
42 | value: redis:6379
43 | - name: MQ_SERVERS
44 | value: kfk1:9092,kfk2:9092,kfk3:9092
45 | ports:
46 | - name: api8080
47 | containerPort: 8080
48 | resources:
49 | requests:
50 | memory: 500Mi
51 | cpu: 200m
52 | limits:
53 | memory: 1Gi
54 | cpu: 500m
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/08-batch-ptask-api.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: batch-ptask-api
5 | namespace: tcir-app
6 | labels:
7 | name: batch-ptask-api
8 | spec:
9 | replicas: 2
10 | strategy:
11 | type: RollingUpdate
12 | rollingUpdate:
13 | maxUnavailable: 1
14 | maxSurge: 1
15 | selector:
16 | matchLabels:
17 | name: batch-ptask-api
18 | template:
19 | metadata:
20 | labels:
21 | name: batch-ptask-api
22 | spec:
23 | containers:
24 | - name: batch-ptask-api
25 | image: 3dsinteractive/automation-technology:prd-1.0.20210118001006
26 | imagePullPolicy: Always
27 | readinessProbe:
28 | httpGet:
29 | path: /healthz
30 | port: 8080
31 | initialDelaySeconds: 10
32 | periodSeconds: 10
33 | timeoutSeconds: 3
34 | failureThreshold: 3
35 | livenessProbe:
36 | httpGet:
37 | path: /healthz
38 | port: 8080
39 | initialDelaySeconds: 60
40 | periodSeconds: 30
41 | timeoutSeconds: 30
42 | failureThreshold: 2
43 | env:
44 | - name: SERVICE_ID
45 | value: batch-ptask-api
46 | - name: CACHE_SERVER
47 | value: redis:6379
48 | - name: MQ_SERVERS
49 | value: kfk1:9092,kfk2:9092,kfk3:9092
50 | ports:
51 | - name: api8080
52 | containerPort: 8080
53 | resources:
54 | requests:
55 | memory: 500Mi
56 | cpu: 200m
57 | limits:
58 | memory: 1Gi
59 | cpu: 500m
60 | ---
61 | apiVersion: v1
62 | kind: Service
63 | metadata:
64 | name: batch-ptask-api
65 | namespace: tcir-app
66 | labels:
67 | name: batch-ptask-api
68 | spec:
69 | selector:
70 | name: batch-ptask-api
71 | ports:
72 | - name: "api8080"
73 | port: 8080
74 | targetPort: 8080
75 | protocol: TCP
76 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/09-batch-ptask-worker.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: batch-ptask-worker
5 | namespace: tcir-app
6 | labels:
7 | name: batch-ptask-worker
8 | spec:
9 | replicas: 3
10 | selector:
11 | matchLabels:
12 | name: batch-ptask-worker
13 | template:
14 | metadata:
15 | labels:
16 | name: batch-ptask-worker
17 | spec:
18 | containers:
19 | - name: batch-ptask-worker
20 | image: 3dsinteractive/automation-technology:prd-1.0.20210118001006
21 | imagePullPolicy: Always
22 | readinessProbe:
23 | httpGet:
24 | path: /healthz
25 | port: 8080
26 | initialDelaySeconds: 10
27 | periodSeconds: 10
28 | timeoutSeconds: 3
29 | failureThreshold: 3
30 | livenessProbe:
31 | httpGet:
32 | path: /healthz
33 | port: 8080
34 | initialDelaySeconds: 60
35 | periodSeconds: 30
36 | timeoutSeconds: 30
37 | failureThreshold: 2
38 | env:
39 | - name: SERVICE_ID
40 | value: batch-ptask-worker
41 | - name: CACHE_SERVER
42 | value: redis:6379
43 | - name: MQ_SERVERS
44 | value: kfk1:9092,kfk2:9092,kfk3:9092
45 | ports:
46 | - name: api8080
47 | containerPort: 8080
48 | resources:
49 | requests:
50 | memory: 500Mi
51 | cpu: 200m
52 | limits:
53 | memory: 1Gi
54 | cpu: 500m
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/10-external-mock-api.yml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: external-api
5 | namespace: tcir-app
6 | labels:
7 | name: external-api
8 | spec:
9 | replicas: 1
10 | strategy:
11 | type: RollingUpdate
12 | rollingUpdate:
13 | maxUnavailable: 1
14 | maxSurge: 1
15 | selector:
16 | matchLabels:
17 | name: external-api
18 | template:
19 | metadata:
20 | labels:
21 | name: external-api
22 | spec:
23 | containers:
24 | - name: external-api
25 | image: 3dsinteractive/automation-technology:prd-1.0.20210118001006
26 | imagePullPolicy: Always
27 | readinessProbe:
28 | httpGet:
29 | path: /healthz
30 | port: 8080
31 | initialDelaySeconds: 10
32 | periodSeconds: 10
33 | timeoutSeconds: 3
34 | failureThreshold: 3
35 | livenessProbe:
36 | httpGet:
37 | path: /healthz
38 | port: 8080
39 | initialDelaySeconds: 60
40 | periodSeconds: 30
41 | timeoutSeconds: 30
42 | failureThreshold: 2
43 | env:
44 | - name: SERVICE_ID
45 | value: external-api
46 | - name: CACHE_SERVER
47 | value: redis:6379
48 | - name: MQ_SERVERS
49 | value: kfk1:9092,kfk2:9092,kfk3:9092
50 | ports:
51 | - name: api8080
52 | containerPort: 8080
53 | resources:
54 | requests:
55 | memory: 500Mi
56 | cpu: 200m
57 | limits:
58 | memory: 1Gi
59 | cpu: 500m
60 | ---
61 | apiVersion: v1
62 | kind: Service
63 | metadata:
64 | name: external-api
65 | namespace: tcir-app
66 | labels:
67 | name: external-api
68 | spec:
69 | selector:
70 | name: external-api
71 | ports:
72 | - name: "api8080"
73 | port: 8080
74 | targetPort: 8080
75 | protocol: TCP
76 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/k8s/01-application/11-ingress.yml:
--------------------------------------------------------------------------------
1 | apiVersion: extensions/v1beta1
2 | kind: Ingress
3 | metadata:
4 | name: ingress
5 | namespace: tcir-app
6 | labels:
7 | name: ingress
8 | spec:
9 | rules:
10 | - host: kubernetes.docker.internal
11 | http:
12 | paths:
13 | - path: /api
14 | backend:
15 | serviceName: register-api
16 | servicePort: 8080
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/microservice_consumer.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "time"
6 |
7 | "github.com/confluentinc/confluent-kafka-go/kafka"
8 | )
9 |
10 | func (ms *Microservice) consumeSingle(servers string, topic string, groupID string, readTimeout time.Duration, h ServiceHandleFunc) {
11 | c, err := ms.newKafkaConsumer(servers, groupID)
12 | if err != nil {
13 | return
14 | }
15 |
16 | defer c.Close()
17 |
18 | c.Subscribe(topic, nil)
19 |
20 | for {
21 | if readTimeout <= 0 {
22 | // readtimeout -1 indicates no timeout
23 | readTimeout = -1
24 | }
25 |
26 | msg, err := c.ReadMessage(readTimeout)
27 | if err != nil {
28 | kafkaErr, ok := err.(kafka.Error)
29 | if ok {
30 | if kafkaErr.Code() == kafka.ErrTimedOut {
31 | if readTimeout == -1 {
32 | // No timeout just continue to read message again
33 | continue
34 | }
35 | }
36 | }
37 | ms.Log("Consumer", err.Error())
38 | ms.Stop()
39 | return
40 | }
41 |
42 | // Execute Handler
43 | h(NewConsumerContext(ms, string(msg.Value)))
44 | }
45 | }
46 |
47 | // Consume register service endpoint for Consumer service
48 | func (ms *Microservice) Consume(servers string, topic string, groupID string, readTimeout time.Duration, h ServiceHandleFunc) error {
49 | go ms.consumeSingle(servers, topic, groupID, readTimeout, h)
50 | return nil
51 | }
52 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/microservice_http.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "context"
6 | "time"
7 |
8 | "github.com/labstack/echo"
9 | )
10 |
11 | // GET register service endpoint for HTTP GET
12 | func (ms *Microservice) GET(path string, h ServiceHandleFunc) {
13 | ms.echo.GET(path, func(c echo.Context) error {
14 | return h(NewHTTPContext(ms, c))
15 | })
16 | }
17 |
18 | // POST register service endpoint for HTTP POST
19 | func (ms *Microservice) POST(path string, h ServiceHandleFunc) {
20 | ms.echo.POST(path, func(c echo.Context) error {
21 | return h(NewHTTPContext(ms, c))
22 | })
23 | }
24 |
25 | // PUT register service endpoint for HTTP PUT
26 | func (ms *Microservice) PUT(path string, h ServiceHandleFunc) {
27 | ms.echo.PUT(path, func(c echo.Context) error {
28 | return h(NewHTTPContext(ms, c))
29 | })
30 | }
31 |
32 | // PATCH register service endpoint for HTTP PATCH
33 | func (ms *Microservice) PATCH(path string, h ServiceHandleFunc) {
34 | ms.echo.PATCH(path, func(c echo.Context) error {
35 | return h(NewHTTPContext(ms, c))
36 | })
37 | }
38 |
39 | // DELETE register service endpoint for HTTP DELETE
40 | func (ms *Microservice) DELETE(path string, h ServiceHandleFunc) {
41 | ms.echo.DELETE(path, func(c echo.Context) error {
42 | return h(NewHTTPContext(ms, c))
43 | })
44 | }
45 |
46 | // startHTTP will start HTTP service, this function will block thread
47 | func (ms *Microservice) startHTTP(exitChannel chan bool) error {
48 | // Caller can exit by sending value to exitChannel
49 | go func() {
50 | <-exitChannel
51 | ms.stopHTTP()
52 | }()
53 | return ms.echo.Start(":8080")
54 | }
55 |
56 | func (ms *Microservice) stopHTTP() {
57 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
58 | defer cancel()
59 | ms.echo.Shutdown(ctx)
60 | }
61 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/microservice_liveness.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "net/http"
6 |
7 | "github.com/labstack/echo"
8 | )
9 |
10 | func (ms *Microservice) isCacherAlive() bool {
11 | if ms.cacher == nil {
12 | return true
13 | }
14 |
15 | ms.Log("MS", "Perform healthcheck on Cacher")
16 | err := ms.cacher.Healthcheck()
17 | if err != nil {
18 | return false
19 | }
20 | return true
21 | }
22 |
23 | func (ms *Microservice) isAlive() (bool, string) {
24 | isAlive := ms.isCacherAlive()
25 | if !isAlive {
26 | return false, "Cacher healthcheck failed"
27 | }
28 |
29 | return true, ""
30 | }
31 |
32 | func (ms *Microservice) responseProbeOK(resp *echo.Response) {
33 | resp.WriteHeader(http.StatusOK)
34 | resp.Write([]byte("ok"))
35 | }
36 |
37 | func (ms *Microservice) responseProbeFailed(resp *echo.Response, reason string) {
38 | errMsg := "Healthcheck failed because of " + reason
39 | resp.WriteHeader(http.StatusInternalServerError)
40 | resp.Write([]byte(errMsg))
41 | }
42 |
43 | // RegisterLivenessProbeEndpoint register endpoint for liveness probe
44 | func (ms *Microservice) RegisterLivenessProbeEndpoint(path string) {
45 | ms.echo.GET(path, func(c echo.Context) error {
46 | ok, reason := ms.isAlive()
47 | if !ok {
48 | ms.responseProbeFailed(c.Response(), reason)
49 | return nil
50 | }
51 | ms.responseProbeOK(c.Response())
52 | return nil
53 | })
54 | }
55 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/microservice_scheduler.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "sync"
6 | "time"
7 | )
8 |
9 | // Schedule will run handler at timer period
10 | func (ms *Microservice) Schedule(timer time.Duration, h ServiceHandleFunc) chan bool /*exit channel*/ {
11 |
12 | // exitChan must be call exitChan <- true from caller to exit scheduler
13 | exitChan := make(chan bool, 1)
14 | go func() {
15 | t := time.NewTicker(timer)
16 | done := make(chan bool, 1)
17 | isExit := false
18 | isExitMutex := sync.Mutex{}
19 |
20 | go func() {
21 | <-exitChan
22 | isExitMutex.Lock()
23 | isExit = true
24 | isExitMutex.Unlock()
25 | // Stop Tick() and send done message to exit for loop below
26 | // Ref: From the documentation http://golang.org/pkg/time/#Ticker.Stop
27 | // Stop turns off a ticker. After Stop, no more ticks will be sent.
28 | // Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.
29 | t.Stop()
30 | done <- true
31 | }()
32 |
33 | for {
34 | select {
35 | case execTime := <-t.C:
36 | isExitMutex.Lock()
37 | if isExit {
38 | isExitMutex.Unlock()
39 | // Done in the next round
40 | continue
41 | }
42 | isExitMutex.Unlock()
43 |
44 | now := time.Now()
45 | // The schedule that older than 10s, will be skip, because t.C is buffer at size 1
46 | diff := now.Sub(execTime).Seconds()
47 | if diff > 10 {
48 | continue
49 | }
50 | h(NewSchedulerContext(ms))
51 | case <-done:
52 | return
53 | }
54 | }
55 | }()
56 |
57 | return exitChan
58 | }
59 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/models.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | // Citizen is model for citizen
5 | type Citizen struct {
6 | CitizenID string `json:"citizen_id"`
7 | }
8 |
--------------------------------------------------------------------------------
/chapter04-implement-reallife-app/4.2-tcir-application/util.go:
--------------------------------------------------------------------------------
1 | // Create and maintain by Chaiyapong Lapliengtrakul (chaiyapong@3dsinteractive.com), All right reserved (2021 - Present)
2 | package main
3 |
4 | import (
5 | "bytes"
6 | "fmt"
7 | "math/rand"
8 | "regexp"
9 | "strings"
10 | )
11 |
12 | var specialCharsRegex = regexp.MustCompile("[^a-zA-Z0-9]+")
13 |
14 | func randString() string {
15 | i := rand.Int()
16 | return fmt.Sprintf("%d", i)
17 | }
18 |
19 | func escapeName(tokens ...string) string {
20 | // Any name rules
21 | // - Lowercase only (for consistency)
22 | // - . (dot), _ (underscore), - (minus) can be used
23 | // - Max length = 250
24 | var b bytes.Buffer
25 |
26 | // Name result must be token1-token2-token3-token4 without special characters
27 | for i, token := range tokens {
28 | if len(token) == 0 {
29 | continue
30 | }
31 |
32 | token = strings.ToLower(token)
33 |
34 | cleanToken := specialCharsRegex.ReplaceAllString(token, "-")
35 | if i != 0 {
36 | b.WriteString("-")
37 | }
38 | b.WriteString(cleanToken)
39 | }
40 |
41 | name := b.String()
42 | // - Cannot start with -, _, +
43 | for true {
44 | if len(name) == 0 || name[0] != '-' {
45 | break
46 | }
47 | name = name[1:]
48 | }
49 |
50 | // - Cannot be longer than 250 characters (max len)
51 | if len(name) > 250 {
52 | name = name[0:250]
53 | }
54 |
55 | return name
56 | }
57 |
--------------------------------------------------------------------------------