Herkese merhaba!

Uzun yıllardır bol miktarda kişisel zaman ve enerji harcayarak bilgimizi hepinizle paylaşıyoruz. Ancak şu andan itibaren bu blogu çalışır durumda tutabilmek için yardımınıza ihtiyacımız var. Yapmanız gereken tek şey, sitedeki reklamlardan birine tıklamak olacaktır, aksi takdirde hosting vb. masraflar nedeniyle maalesef yayından kaldırılacaktır. Teşekkürler.

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.


Yapı


├── Makefile
├── .env.dist
├── build
│ ├── deploy
│ │ └── k8s
│ │ ├── deployment.yaml
│ │ ├── secret.yaml
│ │ └── service.yaml
│ └── docker
│ └── dev
│ └── Dockerfile
└── main.go

Dosyalar


deployment.yaml


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

service.yaml


apiVersion: v1
kind: Service

metadata:
name: sport-service

spec:
type: NodePort
selector:
app: sport
ports:
- protocol: TCP
port: 80
targetPort: 8888

secret.yaml


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

Dockerfile


#
# 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"]

main.go


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"))
}

Makefile


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

.env.dist


HTTP_PORT=8888

DB_HOST=localhost
DB_PORT=3306
DB_NAME=football
DB_USER=user
DB_PASS=pass

Test


Localhost


$ 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


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.


İletim


$ 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