10/11/2020 - GO, KUBERNETES, MYSQL
Bu örnekte, Kubernetes'e çok basit bir veritabanı güdümlü Golang RESTful uygulamasını ileteceğiz. Burada önemli olan nokta, veritabanının bir Pod olmamasıdır. Bulutta olduğu için sırlara güvenerek İnternet üzerinden ona erişeceğiz. Uygulama her zamanki gibi kendi HTTP sunucusunu çalıştıracak ve gelen talepleri sunacaktır.
├── Makefile
├── .env.dist
├── build
│ ├── deploy
│ │ └── k8s
│ │ ├── deployment.yaml
│ │ ├── secret.yaml
│ │ └── service.yaml
│ └── docker
│ └── dev
│ └── Dockerfile
└── main.go
apiVersion: apps/v1
kind: Deployment
metadata:
name: sport-deployment
labels:
app: sport
spec:
replicas: 2
selector:
matchLabels:
app: sport
template:
metadata:
labels:
app: sport
spec:
containers:
- name: golang
image: you/sport:latest
ports:
- containerPort: 8888
env:
- name: HTTP_PORT
value: "8888"
- name: DB_PORT
value: "15209" # This comes from ngrok. You will see that later.
- name: DB_NAME
value: "football"
- name: DB_HOST
valueFrom:
secretKeyRef:
name: sport-secret
key: db-host
- name: DB_USER
valueFrom:
secretKeyRef:
name: sport-secret
key: db-user
- name: DB_PASS
valueFrom:
secretKeyRef:
name: sport-secret
key: db-pass
apiVersion: v1
kind: Service
metadata:
name: sport-service
spec:
type: NodePort
selector:
app: sport
ports:
- protocol: TCP
port: 80
targetPort: 8888
Sırlar içerdiği için bunu deponuzda tutmamanız gerekiyor.
apiVersion: v1
kind: Secret
metadata:
name: sport-secret
type: Opaque
data:
# This db-host comes from ngrok. You will see that later.
db-host: NC50Y3Aubmdyb2suaW8= # echo -n '4.tcp.ngrok.io' | base64
db-user: dXNlcg== # echo -n 'user' | base64
db-pass: cGFzcw== # echo -n 'pass' | base64
#
# STAGE 1: build
#
FROM golang:1.15-alpine3.12 as build
WORKDIR /source
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/main main.go
#
# STAGE 2: run
#
FROM alpine:3.12 as run
COPY --from=build /source/bin/main /main
ENTRYPOINT ["/main"]
package main
import (
"database/sql"
"fmt"
"log"
"net/http"
"os"
"github.com/joho/godotenv"
_ "github.com/go-sql-driver/mysql"
)
func main() {
_ = godotenv.Load(".env.dist")
dsn := fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s",
os.Getenv("DB_USER"),
os.Getenv("DB_PASS"),
os.Getenv("DB_HOST"),
os.Getenv("DB_PORT"),
os.Getenv("DB_NAME"),
)
fmt.Println("db dsn:", dsn)
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatalln("db conn:", err)
}
defer db.Close()
if err := db.Ping(); err != nil {
log.Fatalln("db ping:", err)
}
fmt.Println("db conn: up!")
rtr := http.NewServeMux()
rtr.HandleFunc("/api/v1/team", team)
if err := http.ListenAndServe(":" + os.Getenv("HTTP_PORT"), rtr); err != nil {
log.Fatalln("app start:", err)
}
}
func team(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("my team\n"))
}
build
, run
ve local-test
komutları local ortam içindir. Uygulamanızı yerel ortamda test etmek için bunları kullanabilirsiniz.
## Build application binary.
.PHONY: build
build:
go build -race -ldflags "-s -w" -o bin/main main.go
## Build application binary and run it.
.PHONY: run
run: build
bin/main
## Send a dummy request to the local server.
.PHONY: local-test
local-test:
curl --request GET http://localhost:8888/api/v1/team
## -----------------------------------------------------------------------------------
## Build, tag and push application image to registry then clean up.
.PHONY: push
push:
docker build -t you/sport:latest -f ./build/docker/dev/Dockerfile .
docker push you/sport:latest
docker rmi you/sport:latest
docker system prune --volumes --force
## Apply secret and deploy application to kubernetes cluster.
.PHONY: deploy
deploy: secret
kubectl apply -f build/deploy/k8s/secret.yaml
kubectl apply -f build/deploy/k8s/deployment.yaml
kubectl apply -f build/deploy/k8s/service.yaml
## Send a dummy request to the exposed pod on kubernetes.
.PHONY: k8s-test
k8s-test:
curl --request GET $(shell minikube service sport-service --url)/api/v1/team
HTTP_PORT=8888
DB_HOST=localhost
DB_PORT=3306
DB_NAME=football
DB_USER=user
DB_PASS=pass
$ make run
go build -race -ldflags "-s -w" -o bin/main main.go
bin/main
db dsn: user:pass@tcp(localhost:3306)/football
db conn: up!
$ make local-test
curl --request GET http://localhost:8888/api/v1/team
my team
Şimdi Docker imajımızı derleyip Docker Hub'a aktaralım. Bu imaj, dağıtımımızı çalıştırdığımızda çekilecek, bu yüzden önemlidir.
$ make push
docker build -t you/sport:latest -f ./build/docker/dev/Dockerfile .
Sending build context to Docker daemon 7.762MB
.......
Ngrok ücretsiz bir tünel açma hizmetidir. Yerel veritabanı sunucumuzu İnternet üzerinden erişebilmemiz ve İnternete maruz bırakmak için kullanacağız. ngrok adresine gidip indirin ve kaydolun.
MySQL veritabanı sunucum bir docker konteyneridir ve aşağıdaki gibi görünür. Sizin AWS hizmetiniz de kullanılabilir.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8a7937a05baa mysql:5.7.24 "docker-entrypoint.s…" 4 hours ago Up 4 hours 0.0.0.0:3306->3306/tcp, 33060/tcp football-db
Ngrok ile İnternet'e açın.
$ ./ngrok authtoken {your-token-goes-here}
Authtoken saved to configuration file: /Users/you/.ngrok2/ngrok.yml
$ ./ngrok tcp 3306
Session Status online
Account ngrok-test (Plan: Free)
Version 2.3.35
Region United States (us)
Web Interface http://127.0.0.1:4040
Forwarding tcp://4.tcp.ngrok.io:15209 -> localhost:3306
Aşağıda görebileceğiniz gibi k8s YAML dosyalarımızdaki alan adı ve bağlantı noktası kullanılmaktadır.
$ make deploy
kubectl apply -f build/deploy/k8s/secret.yaml
secret/sport-secret created
kubectl apply -f build/deploy/k8s/deployment.yaml
deployment.apps/sport-deployment created
kubectl apply -f build/deploy/k8s/service.yaml
service/sport-service created
Pod ortamındaki değişkenleri kontrol edelim.
$ kubectl exec sport-deployment-66944b6d95-plswg -- printenv
HOSTNAME=sport-deployment-66944b6d95-plswg
DB_PASS=pass
HTTP_PORT=8888
DB_PORT=15209
DB_NAME=football
DB_HOST=4.tcp.ngrok.io
DB_USER=user
.....
Şimdi uygulamanın iyi çalışıp çalışmadığını test edelim. Bu, veritabanına başarıyla bağlandığı anlamına gelir.
$ make k8s-test
curl --request GET http://192.168.99.100:32586/api/v1/team
my team