Istio di WarungPintar

Agung Julisman
Grow at Warung Pintar
8 min readJan 10, 2020

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

  1. 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

flow istio

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
graph oauth di dashboard kiali
monitoring traffic di oauth

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 :

  1. 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.
  2. 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.
  3. 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

--

--

Agung Julisman
Grow at Warung Pintar

Agung Julisman, is a technology enthusiast who spends too much time getting excited about technology