Istio di WarungPintar
Setelah berhasil mengimplementasikan kubernetes di environment staging dan production warungpintar dengan menggunakan ingress bawaan dari GKE kita mempertimbangkan untuk menggunakan istio sebagai ingress controller traffic kita sebelum masuk ke dalam cluster.
dan ini adalah alasan mengapa kita memilih menggunakan istio:
- Automatic load balancing for HTTP, gRPC, WebSocket, and TCP traffic.
- Fine-grained control of traffic behavior with rich routing rules, retries, failovers, and fault injection.
- A pluggable policy layer and configuration API supporting access controls, rate limits and quotas.
- Automatic metrics, logs, and traces for all traffic within a cluster, including cluster ingress and egress.
- Secure service-to-service communication in a cluster with strong identity-based authentication and authorization.
walaupun pada saat kita menggunakan istio masih dalam beta version kita tetap memilih menggunakan istio di production.
it’s so crazy :D
oke sebelum kita mulai explore tentang istio ada baiknya kita baca terlebih dahulu dokumentasi dari istio agar memahami lebih dalam tentang istio, setelah itu kita implementasikan di cluster staging terlebih dahulu di artikel ini kita akan mencoba untuk mengimplementasikan di service oauth (oauth-staging.xxx.com)
untuk mengimplementasikan istio ke dalam cluster, minimal ada 4 langkah yang harus kita lakukan yaitu
- install istio di GKE, buat atau edit cluster di bagian Additional features pilih Istio (beta) Enabled lalu pada bagian Istio mTLS (beta) pilih permissive atau install manual dengan helm di https://git.io/getLatestIstio.
pastikan Kubernetes Services sudah terdeploy dengan baik : istio-citadel
, istio-pilot
, istio-ingressgateway
, istio-policy
, and istio-telemetry
kubectl get svc -n istio-system
-n adalah singkatan dari namespace, karena semua services istio di deploy di dalam namespace istio-system
istio-citadel ClusterIP 10.141.8.73 <none> 8060/TCP,15014/TCP 38d
istio-galley ClusterIP 10.141.13.253 <none> 443/TCP,15014/TCP,9901/TCP 38d
istio-ingressgateway LoadBalancer 10.141.14.251 35.247.155.81 15020:32356/TCP,80:32202/TCP,443:32269/TCP,31400:30066/TCP,15029:31155/TCP,15030:32173/TCP,15031:30356/TCP,15032:32362/TCP,15443:31310/TCP 38d
istio-pilot ClusterIP 10.141.12.127 <none> 15010/TCP,15011/TCP,8080/TCP,15014/TCP 38d
istio-policy ClusterIP 10.141.1.53 <none> 9091/TCP,15004/TCP,15014/TCP 38d
istio-sidecar-injector ClusterIP 10.141.15.170 <none> 443/TCP 38d
istio-telemetry ClusterIP 10.141.3.41 <none> 9091/TCP,15004/TCP,15014/TCP,42422/TCP 38d
disini istio-ingressgateway memiliki ip publik 35.247.155.81 jadi kita bisa mengarahkan dns domain server kita (oauth-staging.xxx.com ) ke ip ini.
2. inject namespace kita
kubectl label namespace NAMESPACE istio-injection=enabled
3. buat gateway.yml
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: oauth-gateway
namespace: istio-system
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "oauth-staging.xxx.com"
tls:
httpsRedirect: true
- port:
number: 443
name: https
protocol: HTTPS
tls:
mode: SIMPLE
serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
privateKey: /etc/istio/ingressgateway-certs/tls.key
hosts:
- "oauth-staging.xxx.com"
- httpsRedirect = true pada bagian ini kita meminta istio untuk redirect port 80 ke port 443 https
- jika kita perhatikan pada baigan tls-serverCertificate secara default istio akan mencari tls domain kita ke path ini /etc/istio/ingressgateway-certs/tls.crt maka kita harus membuat kubernetes secret untuk tls kita
3.1 buat tls-secret-istio.yml
apiVersion: v1
data:
tls.crt: <isi dengan tls.crt domain>
tls.key: <isi dengan tls.key domain>
kind: Secret
metadata:
name: istio-ingressgateway-certs
namespace: istio-system
type: kubernetes.io/tls
lalu apply 2 file di atas ke dalam cluster kita
kubectl apply -f tls-secret-istio.yml
kubectl apply -f gateway.yml
4. buat virtual-service.yml
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: oauth-istio-vc
namespace: istio-system
spec:
hosts:
- oauth-staging.xxx.com
gateways:
- oauth-gateway.istio-system.svc.cluster.local
http:
- match:
- uri:
prefix: "/oauth/"
timeout: 5s
route:
- destination:
host: oauth-istio.core.svc.cluster.local
subset: v1
port:
number: 50088
retries:
attempts: 3
perTryTimeout: 2s
retryOn: gateway-error,connect-failure,refused-stream
fault:
delay:
percentage:
value: 0.1
fixdDelay: 5s
disini kita sudah mempunya service oauth-istio yang berada di dalam namespace core sehinga untuk memanggilnya dengan dns local kita harus memanggil dengan [nama-service].[namespace].svc.cluster.local yang menariknya adalah istio sudah memiliki feature retries jadi kita tidak perlu lagi melakukan logika retry di setiap code aplikasi kita. disini kita melakukan pengaturan attempts = 3 , perTryTimeout: 2s dan retryOn yang artinya ini service ini akan melakukan request sebanyak 3 (jarak antara request by default adalah 25ms) dan jarak antara request adalah 2 detik dan mekanisme request ulang terjadi ketika kondisi gateway-error,connect-failure,refused-stream. untuk fault injection disini kita menggunakan fixDelay 5s ini artinya istio akan memberikan delay selama 5 detik dalam 1 dari 1000 request terhadap “oauth” ini berguna agar aplikasi kita bisa “bernafas” terlebih dahulu.
4.1 untuk circuit breaker sendiri kita harus membuat DestionationRule virtual service yang telah kita buat.
circuit-breaker.yml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: oauth-rule
namespace: istio-system
spec:
hosts:
- oauth-staging.xxx.com
trafficPolicy:
connectionPool:
http:
http1MaxPendingRequests: 1
maxRequestsPerConnection: 1
tcp:
maxConnections: 1
outlierDetection:
baseEjectionTime: 180.000s
consecutiveErrors: 1
interval: 1.000s
maxEjectionPercent: 100
setelah mekanisme retry masih gagal mekanisme circuit-breaker akan mulai di lakukan dengan begini service tidak akan menerima request.
kubectl apply -f virtual-service.yml
kubectl apply -f circuit-breaker.yml
test
curl https://oauth-staging.xxx.com -k -v* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
< HTTP/2 200
< content-type: application/json
< date: Fri, 10 Jan 2020 04:18:20 GMT
< content-length: 65
< x-envoy-upstream-service-time: 3
< server: istio-envoy
jika berhasil kita akan melihat istio-envoy yang telah menjadi servernya dan sudah otomatis menggunakan http2
jadi request flow nya menjadi seperti ini
Tracing & Monitoring
setelah berhasil mengganti ingress kita dengan istio, lalu pertanyaan selanjutnya adalah bagimana melihat traffic yang sedang terjadi dalam cluster, melihat service apa saja yang sudah masuk ke dalam service mesh dan bagaimana performance mereka ? untuk memecahkan masalah ini kita memutuskan untuk menggunakan kiali dan jaeger
disini kita kan menginstall kiali dan jaeger dengan menggunakan istio command line istioctl disini https://istio.io/docs/setup/install/istioctl/
buat kubernetes secret terlebih dahulu untuk credential masuk ke dalam kiali dashboard
kiali-config.yml
apiVersion: v1
kind: Secret
metadata:
name: kiali
namespace: istio-system
labels:
app: kiali
type: Opaque
data:
username: <username kamu yang sudah encode base64>
passphrase: <password kamu yang sudah encode base64>
apply config ke dalam cluster
kubectl apply -f kiali-config.yml
install kiali dan jaeger ke dalam cluster istio-system
istioctl manifest apply --set values.kiali.enabled=true --set values.tracing.enabled=true
lalu check service nya di dalam cluster istio-system
kubectl get svc -n istio-system>jaeger-agent ClusterIP None <none> 5775/UDP,6831/UDP,6832/UDP 36d
jaeger-collector ClusterIP 10.88.7.34 <none> 14267/TCP,14268/TCP,14250/TCP 36d
jaeger-query ClusterIP 10.88.13.29 <none> 16686/TCP 36d
kiali ClusterIP 10.88.15.239 <none> 20001/TCP
untuk mengakses kiali dashboard yang jalan dengan menggunakan ip cluster jadi istioctl akan menggunakan port forwading untuk mengakses dashboard dengan menggunakan command
istioctl dashboard kiali
> http://localhost:38819/kiali
pada gambar di atas kita bisa memonitor traffic yang sedang terjadi di dalam cluster kita dan kita juga bisa melihat alur traffic dari service satu ke service lainya dan melihat antrian traffic terjadi pada service mana.
selanjutnya jager, jika di lihat sebelumnya service jaeger-query, jaeger-collector dan jaeger-agent jalan di namespace istio-system sedangkan aplikasi kita oauth berada di namespace berbeda yaitu core, jadi kita butuh untuk menjalankan jager-agent ini di namespace core sebagai DaemonSet yang terhubung dengan jaeger-collector di namespace istio-system.
configmap-jaeger-agent.yml
apiVersion: v1
kind: ConfigMap
metadata:
name: jaeger-configuration
namespace: core
labels:
app: jaeger
app.kubernetes.io/name: jaeger
data:
agent: |
collector:
host-port: "jaeger-collector.istio-system.svc.cluster.local:14267"
apply configmap ke dalam cluster core
kubectl apply -f configmap-jaeger-agent.yml
jaeger-agent-istio.yml
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
name: jaeger-agent
namespace: merchant
labels:
app: jaeger
app.kubernetes.io/name: jaeger
app.kubernetes.io/component: agent
spec:
template:
metadata:
labels:
app: jaeger
app.kubernetes.io/name: jaeger
app.kubernetes.io/component: agent
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "5778"
spec:
containers:
- name: jaeger-agent
image: jaegertracing/jaeger-agent:1.9.0
args: ["--config-file=/conf/agent.yaml"]
volumeMounts:
- name: jaeger-configuration-volume
mountPath: /conf
ports:
- containerPort: 5775
protocol: UDP
- containerPort: 6831
protocol: UDP
- containerPort: 6832
protocol: UDP
- containerPort: 5778
protocol: TCP
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
volumes:
- configMap:
name: jaeger-configuration
items:
- key: agent
path: agent.yaml
name: jaeger-configuration-volume
apply jager agent
kubectl apply -f jaeger-agent-istio.yml
buka jaeger dengan istioctl
istioctl dashboard jaeger
> http://localhost:41057
untuk melihat metrics kita bisa juga menggunakan prometheus dan grafana, untuk install nya sama saja ketika kita menginstall kiali dan jaeger bisa menggunakan istioctl command.
istioctl manifest apply --set values.prometheus.enabled=true --set values.grafana.enabled=true
Masalah
Sampai saat artikel ini di tulis masalah yang kita hadapi saat mengimplementasikan istio ada 3 yaitu :
- ketika kita mencoba migrasi kubernetes cronjob kita ke dalam istio, karena by default semua pod yang telah di inject istio akan menjalankan istio-proxy dan istio proxy ini tidak akan mati ketika cronjob telah berasil menjalankan tugasnya sedangakan cronjob setelah di eksekusi podnya akan langsung di terminate ini lah yang akan menyebabkan kubernetes cronjob kita hang. solusinya ada 3 yaitu 1. karena cronjob tidak membutuhkan feature dari istio jadi kita pisahkan namespace untuk cronjob itu sendiri 2. kita bisa menambahkan neverInjectSelector di configmap istio-sidecar-injector 3. kita bisa membahkan annotations sidecar.istio.io/inject: “false” di spec metadata cronjob kita.
- ingress di GKE, by default menggunakan GCLB (google cloud load balancer), karena ini server staging jadi kita ingin membatasi ip mana saja yang berhak untuk mengakses aplikasi tersebut awalnya kita mencoba menggunakan firewall rules tapi ini tidak bisa karena firewall rules hanya bisa di implementasi di level instance sedangkan aplikasi kita berjalan di GKE, jadi kalo kita butuh untuk block sesuatu seperti staging server ini kita bisa menggunakan Cloud Armor untuk blok ingress salah satu produk dari GCP.
- 404 muncul ketika aplikasi melakukan redirect dari oauth (oauth-staging.xxx.com) ke aplikasi lain yaitu aplikasi-2 (aplikasi-2.xxx.com) hal ini terjadi karena kita membuat gateway yang berbeda untuk setiap subdomain dengan tls yang sama dan selector yang sama (ingressgateway) jadi ketika oauth (gateway1) melakukan redirect ke aplikasi-2.xxx.com (gateway-2) browser masih mendeteksi dengan koneksi yang sama yaitu gateway1 sehingga menyebabkan 404. untuk solusi ini kita membuat 1 gateway yang dapat melayani 2 virtual service ini
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: gateway-oauth-aplikasi2
namespace: istio-system
spec:
selector:
istio: ingressgateway # use istio default controller
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "oauth-staging.xxx.com"
- "aplikasi-2.xxx.com"
#atau kita bisa menggunakan - *.xxx.com
*di warung pintar kami ingin merevolusi warung di seluruh indonesia, masih banyak orang yang berdagang dengan cara yang sama seperti 50 tahun lalu. banyak masalah yang harus kita selesaikan untuk itu bagi yang ingin memajukan warung di seluruh indonesia mari bergabung bersama kami di https://warungpintar.co.id/karir