Pod
pada Kubernetes bersifat mortal.
Artinya apabila pod-pod tersebut dibuat dan kemudian mati, pod-pod tersebut
tidak akan dihidupkan kembali. ReplicaSets
secara
khusus bertugas membuat dan menghapus Pod
secara dinamsi (misalnya, pada proses scaling out atau scaling in).
Meskipun setiap Pod
memiliki alamat IP-nya masing-masing, kamu tidak dapat mengandalkan alamat IP
yang diberikan pada pod-pod tersebut, karena alamat IP yang diberikan tidak stabil.
Hal ini kemudian menimbulkan pertanyaan baru: apabila sebuah sekumpulan Pod
(yang selanjutnya kita sebut backend)
menyediakan service bagi sebuah sekumpulan Pod
lain (yang selanjutnya kita sebut frontend) di dalam
kluster Kubernetes, bagaimana cara frontend menemukan backend mana yang digunakan?
Inilah alasan kenapa Service
ada.
Sebuah Service
pada Kubernetes adalah sebuah abstraksi yang memberikan definisi
set logis yang terdiri beberapa Pod
serta policy bagaimana cara kamu mengakses sekumpulan Pod
tadi - seringkali disebut sebagai microservices.
Set Pod
yang dirujuk oleh suatu Service
(biasanya) ditentukan oleh sebuah Label Selector
(lihat penjelasan di bawah untuk mengetahui alasan kenapa kamu mungkin saja membutuhkan Service
tanpa
sebuah selector).
Sebagai contoh, misalnya terdapat sebuah backend yang menyediakan fungsionalitas image-processing
yang memiliki 3 buah replica. Replica-replica tadi sifatnya sepadan - dengan kata lain frontend
tidak peduli backend manakah yang digunakan. Meskipun Pod
penyusun sekumpulan backend bisa berubah,
frontend tidak perlu peduli bagaimana proses ini dijalankan atau menyimpan list dari backend-backend
yang ada saat itu. Service
memiliki tujuan untuk decouple mekanisme ini.
Untuk aplikasi yang dijalankan di atas Kubernetes, Kubernetes menyediakan API endpoint sederhana
yang terus diubah apabila state sebuah sekumpulan Pod
di dalam suatu Service
berubah. Untuk
aplikasi non-native, Kubernetes menyediakan bridge yang berbasis virtual-IP bagi Service
yang diarahkan pada Pod
backend.
Service
Service
Service
headlessService
- jenis-jenis Service
Service
Sebuah Service
di Kubernetes adalah sebuah objek REST, layaknya sebuah Pod
. Seperti semua
objek REST, definisi Service
dapat dikirim dengan method POST pada apiserver untuk membuat
sebuah instans baru. Sebagai contoh, misalnya saja kamu memiliki satu sekumpulan Pod
yang mengekspos port
9376 dan memiliki label "app=MyApp"
.
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
Spesifikasi ini akan ditranslasikan sebagai sebuah objek Service
baru dengan nama "my-service"
dengan target port 9376 pada setiap Pod
yang memiliki label "app=MyApp"
. Service
ini
juga akan memiliki alamat IP tersendiri (yang terkadang disebut sebagai “cluster IP”), yang nantinya
akan digunakan oleh service proxy (lihat di bagian bawah). Selector pada Service
akan selalu dievaluasi
dan hasilnya akan kembali dikirim dengan menggunakan method POST ke objek Endpoints
yang juga disebut "my-service"
.
Perhatikan bahwa sebuah Service
dapat melakukan pemetaan setiap incoming port pada targetPort
mana pun. Secara default, field targetPort
akan memiliki value yang sama dengan value dari field port
.
Hal menarik lainnya adalah value dari targetPort
bisa saja berupa string yang merujuk pada nama
dari port yang didefinisikan pada Pod
backend. Nomor port yang diberikan pada port dengan nama
tadi bisa saja memiliki nilai yang berbeda di setiap Pod
backend. Hal ini memberikan fleksibilitas
pada saat kamu melakukan deploy atau melakukan perubahan terhadap Service
. Misalnya saja suatu saat
kamu ingin mengubah nomor port yang ada pada Pod
backend pada rilis selanjutnya tanpa menyebabkan
permasalahan pada sisi klien.
Secara default, protokol yang digunakan pada service adalah TCP
, tapi kamu bisa saja menggunakan
protokol yang tersedia. Karena banyak Service
memiliki kebutuhan untuk
mengekspos lebih dari sebuah port, Kubernetes menawarkan definisi multiple port pada sebuah objek
Service. Setiap definisi port dapat memiliki protokol yang berbeda.
Service
tanpa selectorSecara umum, Service
memberikan abstraksi mekanisme yang dilakukan untuk mengakses Pod
, tapi
mereka juga melakukan abstraksi bagi backend lainnya. Misalnya saja:
Berdasarkan skenario-skenario di atas, kamu dapat membuat sebuah Service
tanpa selector:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
Karena Service
ini tidak memiliki selector, objek Endpoints
bagi Service
ini tidak akan dibuat.
Dengan demikian, kamu bisa membuat Endpoints
yang kamu inginkan:
kind: Endpoints
apiVersion: v1
metadata:
name: my-service
subsets:
- addresses:
- ip: 1.2.3.4
ports:
- port: 9376
Catatan: Perhatikan bahwa alamat IP yang kamu buat untukEndpoints
tidak boleh berupa loopback (127.0.0.0/8), link-local (169.254.0.0/16), atau link-local multicast (224.0.0.0/24). Alamat IP tersebut juga tidak boleh berupa cluster IP dariService
Kubernetes lainnya, karenakube-proxy
belum menyediakan dukungan IP virtual sebagai destination.
Cara mengakses suatu Service
tanpa selector sama saja dengan mengakses suatu Service
dengan selector. Trafik yang ada akan di-route ke Endpoints
yang dispesifikasikan oleh
pengguna (dalam contoh kali ini adalah 1.2.3.4:9376
).
Sebuah ExternalName
Service
merupakan kasus spesial dari Service
dimana Service
tidak memiliki selector dan menggunakan penamaan DNS. Untuk
informasi lebih lanjut silahkan baca bagian ExternalName.
Service
Setiap node di kluster Kubernetes menjalankan kube-proxy
. kube-proxy
bertanggung jawab terhadap implementasi IP virtual bagi Services dengan tipe
selain ExternalName
.
Pada Kubernetes versi v1.0, Services adalah “layer 4” (TCP/UDP pada IP), proxy
yang digunakan murni berada pada userspace. Pada Kubernetes v1.1, API Ingress
ditambahkan untuk merepresentasikan “layer 7”(HTTP), proxy iptables
juga ditambahkan
dan menjadi mode operasi default sejak Kubernetes v1.2. Pada Kubernetes v1.8.0-beta.0,
proxy ipvs juga ditambahkan.
Pada mode ini, kube-proxy
mengamati master Kubernetes apabila terjadi penambahan
atau penghapusan objek Service
dan Endpoints
. Untuk setiap Service
, kube-proxy
akan membuka sebuah port (yang dipilih secara acak) pada node lokal. Koneksi
pada “proxy port” ini akan dihubungkan pada salah satu Pod
backend dari Service
(yang tercatat pada Endpoints
). Pod
backend yang akan digunakan akan diputuskan berdasarkan
SessionAffinity
pada Service
. Langkah terakhir yang dilakukan oleh kube-proxy
adalah melakukan instalasi rules iptables
yang akan mengarahkan trafik yang ada pada
clusterIP
(IP virtual) dan port dari Service
serta melakukan redirect trafik ke proxy
yang memproksikan Pod
backend. Secara default, mekanisme routing yang dipakai adalah
round robin.
Pada mode ini, kube-proxy
mengamati master Kubernetes apabila terjadi penambahan
atau penghapusan objek Service
dan Endpoints
. Untuk setiap Service
,
kube-proxy
akan melakukan instalasi rules iptables
yang akan mengarahkan
trafik ke clusterIP
(IP virtual) dan port dari Service
. Untuk setiap objek Endpoints
,
kube-proxy
akan melakukan instalasi rules iptables
yang akan memilih satu buah Pod
backend. Secara default, pemilihan backend ini dilakukan secara acak.
Tentu saja, iptables
yang digunakan tidak boleh melakukan switching
antara userspace dan kernelspace, mekanisme ini harus lebih kokoh dan lebih cepat
dibandingkan dengan userspace proxy. Meskipun begitu, berbeda dengan mekanisme
proxy userspace, proxy iptables
tidak bisa secara langsung menjalankan mekanisme
retry ke Pod
lain apabila Pod
yang sudah dipilih sebelumnya tidak memberikan respons,
dengan kata lain hal ini akan sangat bergantung pada
readiness probes.
Kubernetes v1.9
beta
beta
(misalnya v2beta3
).Pada mode ini, kube-proxy
mengamati Services dan Endpoints
, kemudian memanggil
interface netlink untuk membuat rules ipvs yang sesuai serta melakukan sinkronisasi
rules ipvs dengan Services dan Endpoints
Kubernetes secara periodik, untuk memastikan
status ipvs konsisten dengan apa yang diharapkan. Ketika sebuah Services diakses,
trafik yang ada akan diarahkan ke salah satu Pod
backend.
Sama halnya dengan iptables
, ipvs juga berdasarkan pada fungsi hook netfilter,
bedanya adalah ipvs menggunakan struktur data hash table dan bekerja di kernelspace.
Dengan kata lain ipvs melakukan redirect trafik dengan lebih cepat dan dengan performa yang lebih
baik ketika melakukan sinkronisasi rules proxy. Selain itu, ipvs juga menyediakan
lebih banyak opsi algoritma load balancing:
rr
: round-robinlc
: least connectiondh
: destination hashingsh
: source hashingsed
: shortest expected delaynq
: never queueCatatan: Mode ipvs menggunakan module IPVS kernel yang diinstal pada node sebelumkube-proxy
dijalankan. Ketikakube-proxy
dijalankan dengan mode proxy ipvs,kube-proxy
akan melakukan proses validasi, apakah module IPVS sudah diinstal di node, jika module tersebut belum diinstal, makakube-proxy
akan menggunakan modeiptables
.
Dari sekian model proxy yang ada, trafik inbound apa pun yang ada diterima oleh IP:Port pada Service
akan dilanjutkan melalui proxy pada backend yang sesuai, dan klien tidak perlu mengetahui
apa informasi mendetail soal Kubernetes, Service
, atau Pod
. afinitas session (session affinity) berbasis
Client-IP dapat dipilih dengan cara menerapkan nilai “ClientIP” pada service.spec.sessionAffinity
(nilai default untuk hal ini adalah “None”), kamu juga dapat mengatur nilai maximum session
timeout yang ada dengan mengatur opsi service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
jika
sebelumnya kamu sudah menerapkan nilai “ClusterIP” pada service.spec.sessionAffinity
(nilai default untuk opsi ini adalah “10800”).
Banyak Services dengan kebutuhan untuk mengekspos lebih dari satu port.
Untuk kebutuhan inilah, Kubernetes mendukung multiple port definitions pada objek Service
.
Ketika menggunakan multiple port, kamu harus memberikan nama pada setiap port yang didefinisikan,
sehingga Endpoint yang dibentuk tidak ambigu. Contoh:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
Perhatikan bahwa penamaan port hanya boleh terdiri dari karakter alphanumeric lowercase
dan -, serta harus dimulai dan diakhiri dengan karakter alphanumeric, misalnya saja 123-abc
dan web
merupakan penamaan yang valid, tapi 123_abc
dan -web
bukan merupakan penamaan yang valid.
Kamu dapat memberikan spesifikasi alamat cluster IP yang kamu inginkan
sebagai bagian dari request pembuatan objek Service
. Untuk melakukan hal ini,
kamu harus mengisi fields .spec.clusterIP
field. Contoh penggunaannya adalah sebagai berikut,
misalnya saja kamu sudah memiliki entry DNS yang ingin kamu gunakan kembali,
atau sebuah sistem legacy yang sudah diatur pada alamat IP spesifik
dan sulit untuk diubah. Alamat IP yang ingin digunakan pengguna haruslah merupakan alamat IP
yang valid dan berada di dalam range CIDR service-cluster-ip-range
yang dispesifikasikan di dalam
penanda yang diberikan apiserver. Jika value yang diberikan tidak valid, apiserver akan
mengembalikan response code HTTP 422 yang mengindikasikan value yang diberikan tidak valid.
Pertanyaan yang selalu muncul adalah kenapa kita menggunakan IP virtual dan bukan DNS round-robin standar? Terdapat beberapa alasan dibalik semua itu:
Kami berusaha untuk mengurangi ketertarikan pengguna untuk melakukan yang mungkin akan menyusahkan pengguna. Dengan demikian, apabila terdapat justifikasi yang cukup kuat, kami mungkin saja memberikan implementasi alternatif yang ada.
Kubernetes mendukung 2 buah mode primer untuk melakukan Service
- variabel environment dan DNS.
Ketika sebuah Pod
dijalankan pada node, kubelet menambahkan seperangkat variabel environment
untuk setiap Service
yang aktif. Environment yang didukung adalah Docker links compatible variabel (perhatikan
makeLinkVariables)
dan variabel {SVCNAME}_SERVICE_HOST
dan {SVCNAME}_SERVICE_PORT
, dinama nama Service
akan diubah
menjadi huruf kapital dan tanda minus akan diubah menjadi underscore.
Sebagai contoh, Service
"redis-master"
yang mengekspos port TCP 6379 serta alamat
cluster IP 10.0.0.11 akan memiliki environment sebagai berikut:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
Hal ini merupakan kebutuhan yang urutannya harus diperhatikan - Service
apa pun yang
akan diakses oleh sebuah Pod
harus dibuat sebelum Pod
tersebut dibuat,
jika tidak variabel environment tidak akan diinisiasi.
Meskipun begitu, DNS tidak memiliki keterbatasan ini.
Salah satu add-on opsional
(meskipun sangat dianjurkan) adalah server DNS. Server DNS bertugas untuk mengamati apakah
terdapat objek Service
baru yang dibuat dan kemudian bertugas menyediakan DNS baru untuk
Service tersebut. Jika DNS ini diaktifkan untuk seluruh kluster, maka semua Pod
akan secara otomatis
dapat melakukan resolusi DNS.
Sebagai contoh, apabila kamu memiliki sebuah Service
dengan nama "my-service"
pada Namespace
“my-ns”, maka record DNS "my-service.my-ns"
akan dibuat. Pod
yang berada di dalam
Namespace “my-ns” dapat langsung melakukan lookup dengan hanya menggunakan "my-service"
.
Sedangkan Pod
yang berada di luar Namespace my-ns” harus menggunakan "my-service.my-ns"
.
Hasil dari resolusi ini menrupakan cluster IP.
Kubernetes juga menyediakan record DNS SRV (service) untuk named ports. Jika
Service "my-service.my-ns"
memiliki port dengan nama "http"
dengan protokol TCP
,
kamu dapat melakukan query DNS SRV untuk "_http._tcp.my-service.my-ns"
untuk mengetahui
nomor port yang digunakan oleh http.
Server DNS Kubernetes adalah satu-satunya cara untuk mengakses
Service dengan tipe ExternalName
. Informasi lebih lanjut tersedia di
DNS Pods dan Services.
Service
headlessTerkadang kamu tidak membutuhkan mekanisme load-balancing dan sebuah single IP Sevice.
Dalam kasus ini, kamu dapat membuat “headless” Service
dengan cara memberikan spesifikasi
None pada cluster IP (.spec.clusterIP
).
Opsi ini memungkinkan pengguna mengurangi ketergantungan terhadap sistem Kubernetes dengan cara memberikan kebebasan untuk mekanisme service discovery. Aplikasi akan tetap membutuhkan mekanisme self-registration dan adapter service discovery lain yang dapat digunakan berdasarkan API ini.
Untuk Service
“headless” alokasi cluster IP tidak dilakukan dan kube-proxy
tidak me-manage Service-Service, serta tidak terdapat mekanisme load balancing
yang dilakukan. Bagaimana konfigurasi otomatis bagi DNS dilakukan bergantung pada
apakah Service
tersebut memiliki selector yang dispesifikasikan.
Untuk Service
“headless” dengan selector, kontroler Endpoints
akan membuat suatu
record Endpoints
di API, serta melakukan modifikasi konfigurasi DNS untuk mengembalikan
A records (alamat) yang merujuk secara langsung pada Pod
backend.
Untuk Service
“headless” tanpa selector, kontroler Endpoints
tidak akan membuat record Enpoints. Meskipun demikian,
sistem DNS tetap melakukan konfigurasi salah satu dari:
ExternalName
-tipe services.Endpoints
yang memiliki nama Service
yang sama, untuk
tipe lainnya.Service
- jenis-jenis Service
Untuk beberapa bagian dari aplikasi yang kamu miliki (misalnya saja, frontend),
bisa saja kamu memiliki kebutuhan untuk mengekspos Service
yang kamu miliki
ke alamat IP eksternal (di luar kluster Kubernetes).
ServiceTypes
yang ada pada Kubernetes memungkinkan kamu untuk menentukan
jenis Service
apakah yang kamu butuhkan. Secara default, jenis Service
yang diberikan adalah ClusterIP
.
Value dan perilaku dari tipe Service
dijelaskan sebagai berikut:
ClusterIP
: Mengekspos Service
ke range alamat IP di dalam kluster. Apabila kamu memilih value ini
Service
yang kamu miliki hanya dapat diakses secara internal. tipe ini adalah
default value dari ServiceType.NodePort
: Mengekspos Service
pada setiap IP node pada port statis
atau port yang sama. Sebuah Service
ClusterIP
, yang mana Service
NodePort
akan di-route
, dibuat secara otomatis. Kamu dapat mengakses Service
dengan tipe ini,
dari luar kluster melalui <NodeIP>:<NodePort>
.LoadBalancer
: Mengekspos Service
secara eksternal dengan menggunakan LoadBalancer
yang disediakan oleh penyedia layanan cloud. Service
dengan tipe NodePort
dan ClusterIP
,
dimana trafik akan di-route, akan dibuat secara otomatis.ExternalName
: Melakukan pemetaan Service
ke konten
dari field externalName
(misalnya: foo.bar.example.com
), dengan cara mengembalikan
catatan CNAME
beserta value-nya. Tidak ada metode proxy apa pun yang diaktifkan. Mekanisme ini
setidaknya membutuhkan kube-dns
versi 1.7.Jika kamu menerapkan value NodePort
pada field type, master Kubernetes akan mengalokasikan
port dari range yang dispesifikasikan oleh penanda --service-node-port-range
(secara default, 30000-32767)
dan setiap Node akan memproksikan port tersebut (setiap Node akan memiliki nomor port yang sama) ke Service
yang kamu miliki. Port
tersebut akan dilaporkan pada field .spec.ports[*].nodePort
di Service
kamu.
Jika kamu ingin memberikan spesifikasi IP tertentu untuk melakukan poxy pada port.
kamu dapat mengatur penanda --nodeport-addresses
pada kube-proxy
untuk range alamat IP
tertentu (mekanisme ini didukung sejak v1.10). Sebuah daftar yang dipisahkan koma (misalnya, 10.0.0.0/8, 1.2.3.4/32)
digunakan untuk mem-filter alamat IP lokal ke node ini. Misalnya saja kamu memulai kube-proxy
dengan penanda
--nodeport-addresses=127.0.0.0/8
, maka kube-proxy
hanya akan memilih interface loopback untuk Service
dengan tipe
NodePort
. Penanda --nodeport-addresses
memiliki nilai default kosong ([]
), yang artinya akan memilih semua interface yang ada
dan sesuai dengan perilaku NodePort
default.
Jika kamu menginginkan nomor port yang berbeda, kamu dapat memberikan spesifikasi value dari field nodePort, dan sistem yang ada akan mengalokasikan port tersebut untuk kamu, jika port tersebut belum digunakan (perhatikan bahwa jika kamu menggunakan teknik ini, kamu perlu mempertimbangkan collision yang mungkin terjadi dan bagaimana cara mengatasi hal tersebut) atau transaksi API yang dilakukan akan gagal.
Hal ini memberikan kebebasan bagi pengembang untuk memilih load balancer yang akan digunakan, terutama apabila load balancer yang ingin digunakan belum didukung sepenuhnya oleh Kubernetes.
Perhatikan bahwa Service
dapat diakses baik dengan menggunakan <NodeIP>:spec.ports[*].nodePort
atau .spec.clusterIP:spec.ports[*].port
. (Jika penanda --nodeport-addresses
diterapkan,
Pada penyedia layanan cloud yang menyediakan pilihan load balancer eksternal, pengaturan field type
ke LoadBalancer
akan secara otomatis melakukan proses provision load balancer untuk Service
yang kamu buat.
Informasi mengenai load balancer yang dibuat akan ditampilkan pada field .status.loadBalancer
pada Service
kamu. Contohnya:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155
Trafik dari load balancer eksternal akan diarahkan pada Pod
backend, meskipun mekanisme
bagaimana hal ini dilakukan bergantung pada penyedia layanan cloud. Beberapa penyedia layanan
cloud mengizinkan konfigurasi untuk value loadBalancerIP
. Dalam kasus tersebut, load balancer akan dibuat
dengan loadbalancerIP yang dispesifikasikan. Jika value dari loadBalancerIP
tidak dispesifikasikan.
sebuah IP sementara akan diberikan pada loadBalancer. Jika loadBalancerIP
dispesifikasikan,
tetapi penyedia layanan cloud tidak mendukung hal ini, maka field yang ada akan diabaikan.
Catatan Khusus untuk Azure: Untuk spesifikasi loadBalancerIP
publik yang didefinisikan oleh pengguna,
sebuah alamat IP statis publik akan disediakan terlebih dahulu, dan alamat IP tersebut harus berada di
resource group dari resource yang secara otomatis dibuat oleh kluster. Misalnya saja, MC_myResourceGroup_myAKSCluster_eastus
.
Berikan spesifikasi alamat IP sebagai loadBalancerIP
. Pastikan kamu sudah melakukan update pada
securityGroupName pada file konfigurasi penyedia layanan cloud.
Untuk informasi lebih lanjut mengenai permission untuk CreatingLoadBalancerFailed
kamu dapat membaca troubleshooting untuk
Penggunaan alamat IP statis pada load balancer Azure Kubernetes Service (AKS) atau
CreatingLoadBalancerFailed pada kluster AKS dengan advanced networking.
Catatan: Dukungan untuk SCTP load balancer dari penyedia layanan cloud bergantung pada implementasi load balancer yang disediakan oleh penyedia layanan cloud tersebut. Jika SCTP tidak didukung oleh load balancer penyedia layanan publik maka request pembuatanService
akan tetap diterima, meskipun proses pembuatan load balancer itu sendiri gagal.
Di dalam environment, terkadang terdapat kebutuhan untuk melakukan route trafik antar Service yang berada di dalam satu VPC.
Di dalam environment split-horizon DNS kamu akan membutuhkan dua service yang mampu melakukan mekanisme route trafik eskternal maupun internal ke endpoints yang kamu miliki.
Hal ini dapat diraih dengan cara menambahkan anotasi berikut untuk service yang disediakan oleh penyedia layanan cloud.
Pilih salah satu tab.
[...]
metadata:
name: my-service
annotations:
cloud.google.com/load-balancer-type: "Internal"
[...]
Gunakan cloud.google.com/load-balancer-type: “internal” untuk master dengan versi 1.7.0 to 1.7.3. Untuk informasi lebih lanjut, dilahkan baca dokumentasi.
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-internal: 0.0.0.0/0
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/azure-load-balancer-internal: "true"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/openstack-internal-load-balancer: "true"
[...]
[...]
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/cce-load-balancer-internal-vpc: "true"
[...]
Dukungan parsial untuk SSL bagi kluster yang dijalankan di AWS mulai diterapkan,
mulai versi 1.3 terdapat 3 anotasi yang dapat ditambahkan pada Service
dengan tipe
LoadBalancer
:
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-ssl-cert: arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012
Anotasi pertama memberikan spesifikasi ARN dari sertifikat yang akan digunakan. Sertifikat yang digunakan bisa saja berasal dari third party yang diunggah ke IAM atau sertifikat yang dibuat secara langsung dengan menggunakan sertifikat manajer AWS.
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: (https|http|ssl|tcp)
Anotasi kedua memberikan spesifikasi bagi protokol yang digunakan oleh Pod
untuk saling berkomunikasi.
Untuk HTTPS dan SSL, ELB membutuhkan Pod
untuk melakukan autentikasi terhadap dirinya sendiri melalui
koneksi yang dienkripsi.
Protokol HTTP dan HTTPS akan memilih mekanisme proxy di tingkatan ke-7:
ELB akan melakukan terminasi koneksi dengan pengguna, melakukan proses parsing headers, serta
memasukkan value bagi header X-Forwarded-For
dengan alamat IP pengguna (Pod hanya dapat melihat
alamat IP dari ELB pada akhir koneksi yang diberikan) ketika melakukan forwarding suatu request.
Protokol TCP dan SSL akan memilih mekanisme proxy pada tingkatan 4: ELB akan melakukan forwarding trafik tanpa melakukan modifikasi headers.
Pada environment campuran dimana beberapa port diamankan sementara port lainnya dalam kondisi tidak dienkripsi, anotasi-anotasi berikut dapat digunakan:
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-backend-protocol: http
service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443,8443"
Pada contoh di atas, jika Service
memiliki 3 buah port, yaitu: 80
, 443
, dan
8443
, maka 443
adan 8443
akan menggunakan sertifikat SSL, tetapi 80
hanya akan
di-proxy menggunakan protokol HTTP.
Mulai versi 1.9, Service
juga dapat menggunakan predefined policy
untuk HTTPS atau listener SSL. Untuk melihat policy apa saja yang dapat digunakan, kamu dapat menjalankan perintah awscli:
aws elb describe-load-balancer-policies --query 'PolicyDescriptions[].PolicyName'
Policy ini kemudian dapat dispesifikasikan menggunakan anotasi “service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy”, contohnya:
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-ssl-negotiation-policy: "ELBSecurityPolicy-TLS-1-2-2017-01"
Untuk mengaktifkan dukungan protokol PROXY untuk kluster yang dijalankan di AWS, kamu dapat menggunakan anotasi di bawah ini:
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
Sejak versi 1.3.0, penggunaan anotasi berlaku untuk semua port yang diproksi oleh ELB dan tidak dapat diatur sebaliknya.
Terdapat beberapa anotasi yang digunakan untuk melakukan manajemen akses log untuk ELB pada AWS.
Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-enabled
mengatur akses log mana sajakah yang diaktifkan.
Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval
mengatur interval (dalam menit) publikasi akses log. Kamu dapat memberikan spesifikasi interval
diantara range 5-60 menit.
Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name
mengatur nama bucket Amazon S3 dimana akses log load balancer disimpan.
Anotasi service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix
memberikan spesifikasi hierarki logis yang kamu buat untuk bucket Amazon S3 yang kamu buat.
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-access-log-enabled: "true"
# Specifies whether access logs are enabled for the load balancer
service.beta.kubernetes.io/aws-load-balancer-access-log-emit-interval: "60"
# The interval for publishing the access logs. You can specify an interval of either 5 or 60 (minutes).
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-name: "my-bucket"
# The name of the Amazon S3 bucket where the access logs are stored
service.beta.kubernetes.io/aws-load-balancer-access-log-s3-bucket-prefix: "my-bucket-prefix/prod"
# The logical hierarchy you created for your Amazon S3 bucket, for example _my-bucket-prefix/prod_
Mekanisme draining untuk ELB klasik dapat dilakukan dengan menggunakan anotasi
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled
serta mengatur
value-nya menjadi "true"
. Anotasi
service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout
juga
dapat digunakan untuk mengatur maximum time (dalam detik), untuk menjaga koneksi yang ada
agar selalu terbuka sebelum melakukan deregistering instance.
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-connection-draining-enabled: "true"
service.beta.kubernetes.io/aws-load-balancer-connection-draining-timeout: "60"
Terdapat beberapa anotasi lain yang dapat digunakan untuk mengatur ELB klasik sebagaimana dijelaskan seperti di bawah ini:
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout: "60"
# The time, in seconds, that the connection is allowed to be idle (no data has been sent over the connection) before it is closed by the load balancer
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
# Specifies whether cross-zone load balancing is enabled for the load balancer
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: "environment=prod,owner=devops"
# A comma-separated list of key-value pairs which will be recorded as
# additional tags in the ELB.
service.beta.kubernetes.io/aws-load-balancer-healthcheck-healthy-threshold: ""
# The number of successive successful health checks required for a backend to
# be considered healthy for traffic. Defaults to 2, must be between 2 and 10
service.beta.kubernetes.io/aws-load-balancer-healthcheck-unhealthy-threshold: "3"
# The number of unsuccessful health checks required for a backend to be
# considered unhealthy for traffic. Defaults to 6, must be between 2 and 10
service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval: "20"
# The approximate interval, in seconds, between health checks of an
# individual instance. Defaults to 10, must be between 5 and 300
service.beta.kubernetes.io/aws-load-balancer-healthcheck-timeout: "5"
# The amount of time, in seconds, during which no response means a failed
# health check. This value must be less than the service.beta.kubernetes.io/aws-load-balancer-healthcheck-interval
# value. Defaults to 5, must be between 2 and 60
service.beta.kubernetes.io/aws-load-balancer-extra-security-groups: "sg-53fae93f,sg-42efd82e"
# A list of additional security groups to be added to ELB
Peringatan: Ini merupakan tingkatan alpha dan tidak direkomendasikan untuk digunakan pada environment production.
Sejak versi 1.9.0, Kubernetes mendukung Network Load Balancer (NLB). Untuk
menggunakan NLB pada AWS, gunakan anotasi service.beta.kubernetes.io/aws-load-balancer-type
dan atur value-nya dengan nlb
.
metadata:
name: my-service
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
Tidak seperti ELB klasik, NLB, melakukan forwarding IP klien melalui node.
Jika field .spec.externalTrafficPolicy
diatur value-nya menjadi Cluster
, maka
alamat IP klien tidak akan diteruskan pada Pod
.
Dengan mengatur value dari field .spec.externalTrafficPolicy
ke Local
,
alamat IP klien akan diteruskan ke Pod
, tapi hal ini bisa menyebabkan distribusi trafik
yang tidak merata. Node yang tidak memiliki Pod
untuk Service
dengan tipe LoadBalancer
akan menyebabkan kegagalan health check NLB Target pada tahapan auto-assigned .spec.healthCheckNodePort
dan tidak akan menerima trafik apa pun.
Untuk menghasilkan distribusi trafik yang merata, kamu dapat menggunakan
DaemonSet atau melakukan spesifikasi
pod anti-affinity
agar Pod
tidak di-assign ke node yang sama.
NLB juga dapat digunakan dengan anotasi internal load balancer.
Agar trafik klien berhasil mencapai instances dibelakang ELB, security group dari node akan diberikan rules IP sebagai berikut:
Rule | Protokol | Port |
IpRange(s) | Deskripsi IpRange |
---|---|---|---|---|
Health Check | TCP | NodePort(s) (.spec.healthCheckNodePort for .spec.externalTrafficPolicy = Local) |
VPC CIDR | kubernetes.io/rule/nlb/health=<loadBalancerName> |
Client Traffic | TCP | NodePort(s) | .spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0 ) |
kubernetes.io/rule/nlb/client=<loadBalancerName> |
MTU Discovery | ICMP | 3,4 | .spec.loadBalancerSourceRanges (defaults to 0.0.0.0/0 ) |
kubernetes.io/rule/nlb/mtu=<loadBalancerName> |
Perhatikan bahwa jika .spec.loadBalancerSourceRanges
tidak dispesifikasikan,
Kubernetes akan mengizinkan trafik dari 0.0.0.0/0
ke Node Security Group.
Jika node memiliki akses publik, maka kamu harus memperhatikan tersebut karena trafik yang tidak berasal
dari NLB juga dapat mengakses semua instance di security group tersebut.
Untuk membatasi klien IP mana yang dapat mengakses NLB, kamu harus memberikan spesifikasi loadBalancerSourceRanges.
spec:
loadBalancerSourceRanges:
- "143.231.0.0/16"
Catatan: NLB hanya dapat digunakan dengan beberapa kelas instance tertentu baca dokumentasi AWS untuk mengetahui lebih lanjut intance apa saja yang didukung.
Service dengan tipe ExternalName
melakukan pemetaan antara Service
dan DNS, dan bukan
ke selector seperti my-service
atau cassandra
. Kamu memberikan spesifikasi spec.externalName
pada Service
tersebut.
Definisi Service
ini, sebagai contoh, melaukan pemetaan
Service
my-service
pada namespace prod
ke DNS my.database.example.com
:
kind: Service
apiVersion: v1
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
Catatan:ExternalName
menerima alamat IPv4 dalam bentuk string, tapi karena DNS tersusun atas angka dan bukan sebagai alamat IP.ExternalName
yang menyerupai alamat IPv4 tidak bisa di-resolve oleh CoreDNS atau ingress-nginx karenaExternalName
memang ditujukan bagi penamaan canonical DNS. Untuk melakukan hardcode alamat IP, kamu dapat menggunakan headlessService
sebagai alternatif.
Ketika melakukan pencarian host my-service.prod.svc.cluster.local
,
servis DNS kluster akan mengembalikan record CNAME
dengan value my.database.example.com
.
Mekanisme akses pada my-service
bekerja dengan cara yang sama dengan
Service
pada umumnya, perbedaan yang krusial untuk hal ini adalah mekanisme redirection
terjadi pada tingkatan DNS dan bukan melalui proxy forward. Apabila kamu berniat memindahkan basis data
yang kamu pakai ke dalam kluster, kamu hanya perlu mengganti instans basis data kamu dan menjalankannya
di dalam Pod
, menambahkan selector atau endpoint yang sesuai, serta mengupah type dari
Service yang kamu gunakan.
Catatan: Bagian ini berasal dari tulisan Tips Kubernetes - Bagian 1 oleh Alen Komljen.
Jika terdapat sebuah alamat IP eksternal yang melakukan mekanisme route ke satu atau lebih node yang ada di kluster, Service
Kubernetes dapat diekspos
dengan menggunakan externalIP
. Trafik yang diarahkan ke kluster dengan IP eksternal
(sebagai destinasi IP), pada port Service
akan di-route ke salah satu endpoint Service
.
Value dari externalIP
tidak diatur oleh Kubernetes dan merupakan tanggung jawab
dari administrator kluster.
Pada ServiceSpec, kamu dapat memberikan spesifikasi externalIP
dan ServiceTypes
.
Pada contoh di bawah ini. "my-service"
dapat diakses oleh klien pada “80.11.12.10:80
” (externalIP:port
).
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
Penggunaan proxy userspace untuk VIP dapat digunakan untuk skala kecil hingga menengah,
meski begitu hal ini tidak scalable untuk kluster yang sangat besar dan memiliki ribuan Service
.
Perhatikan Desain proposal orisinil untuk portal untuk informasi
lebih lanjut.
Penggunaan proxy userspace menghilangkan source-IP dari packet yang mengakses
sebuah Service
. Hal ini membuat mekanisme firewall menjadi sulit untuk diterapkan.
Proxy iptables
tidak menghilangkan source IP yang berasal dari dalam kluster,
meski begitu, hal ini masih berimbas pada klien yang berasal dari Service
dengan tipe
load-balancer atau node-port.
Field tipe didesain sebagai fungsionalitas yang berantai - setiap tingkatan
menambahkan tambahan pada tingkatansebelumnya. Hal ini tidak selalu berlaku bagi
semua penyedia layanan cloud (misalnya saja Google Compute Engine tidak perlu
melakukan alokasi NodePort
untuk membuat LoadBalancer
bekerja sebagaimana mestinya,
hal ini berbeda dengan AWS yang memerlukan hal ini, setidaknya untuk API yang mereka miliki
saat ini).
Di masa mendatang, kami berencana untuk membuat policy proxy menjadi lebih
bervariasi dan bukan hanya round robin, misalnya saja master-elected atau sharded.
Kami juga berharap bahwa beberapa Service
bisa saja memiliki load balancer yang sebenarnya,
suatu kasus dimana VIP akan secara langsung mengantarkan paket.
Kami ingin meningkatkan dukungan lebih lanjut untuk Service
dengan tingkatan Service
L7(HTTP).
Kami ingin memiliki mode ingress yang lebih fleksibel untuk Service
yang
mencakup mode ClusterIP
, NodePort
, dan LoadBalancer
dan banyak lagi.
Informasi sebelumnya sudah cukup bagi sebagian orang yang hanya ingin menggunakan Service. Meskipun begitu, terdapat banyak hal yang sebenarnya terjadi dan akan sangat bermanfaat untuk dipelajari lebih lanjut.
Salah satu filosofi Kubernetes adalah pengguna tidak mungkin menghadapi situasi dimana apa yang mereka mengalami kegagalan tanpa adanya alasan yang jelas. Dalam kasus ini, kita akan coba memahami lebih lanjut mengenai network port - pengguna tidak seharusnya memilih nomor port jika hal itu memungkinkan terjadinya collision dengan pengguna lainnya. Hal ini merupakan mekanisme isolasi kegagalan.
Agar pengguna dapat menentukan nomor port bagi Service
mereka, kita harus
memastikan bahwa tidak ada dua Service
yang mengalami collision. Kita melakukan
hal tersebut dengan cara melakukan alokasi alamat IP pada setiap Service
.
Untuk memastikan setiap Service
memiliki alamat IP yang unik, sebuah allocator
internal akan secara atomik melakukan pemetaan alokasi global di dalam etcd ketika
membuat sebuah Service
baru. Pemetaan objek harus tersedia pada registry Service
dimana Service
akan diberikan sebuah IP, jika tidak, proses pembuatan Service
akan gagal
dan sebuah pesan akan memberikan informasi bahwa alamat IP tidak dapat dialokasikan.
Sebuah backgroud controller bertanggung jawab terhadap mekanisme pemetaan tersebut (migrasi
dari versi Kubernetes yang digunakan dalam memory locking) sekaligus melakukan pengecekan
terhadap assignment yang tidak valid yang terjadi akibat intervensi administrator dan melakukan
penghapusan daftar IP yang dialokasikan tapi tidak digunakan oleh Service
mana pun.
Tidak seperti alamat IP Pod
, yang akan di route ke destinasi yang “pasti”,
IP Service
tidak mengarahkan request hanya pada satu host. Sebagai gantinya,
kita mneggunakan iptables
(logika pemrosesan paket pada Linux) untuk melakukan definisi
alamat IP virtual yang secara transparan akan diarahkan sesuai kebutuhan. Ketika klien
dihubungkan pada VIP, trafik yang ada akan secara otomatis dialihkan pada endpoint yang sesuai.
Variabel environment dan DNS untuk Service
terdiri dalam bentuk VIP dan port.
Kami mendukung tiga jenis mode proxy - userspace, iptables
, dan ipvs yang memiliki
perbedaan cara kerja satu sama lainnya.
Sebagai contoh, anggaplah kita memiliki aplikasi image processing seperti yang sudah
disebutkan di atas. Ketika Service
backend dibuat, master Kubernetes akan mengalokasikan
sebuah alamat IP virtual, misalnya 10.0.0.1. Dengan asumsi port dari Service
tersebut adalah 1234,
maka Service
tersebut akan diamati oleh semua instance kube-proxy
yang ada di kluster.
Ketika sebuah proxy mendapati sebuah Service
baru, proxy tersebut akan membuka sebuah port
acak, menyediakan iptables
yang mengarahkan VIP pada port yang baru saja dibuat, dan mulai
koneksi pada port tersebut.
Ketika sebuah klien terhubung ke VIP dan terdapat rules iptables
yang diterapkan, paket akan diarahkan ke port dari proxy Service
itu sendiri.
Proxy Service
akan memilih sebuah backend, dan mulai melakukan mekanisme proxy
trafik dari klien ke backend.
Dengan demikian, pemilik Service
dapat memilih port mana pun yang dia inginkan
tanpa adanya kemungkinan terjadinya collision. Klien dapat dengan mudah mengakses IP dan port,
tanpa harus mengetahui Pod
mana yang sebenarnya diakses.
Kembali, bayangkan apabila kita memiliki aplikasi image processing seperti yang sudah
disebutkan di atas. Ketika Service
backend dibuat, master Kubernetes akan mengalokasikan
sebuah alamat IP virtual, misalnya 10.0.0.1. Dengan asumsi port dari Service
tersebut adalah 1234,
maka Service
tersebut akan diamati oleh semua instance kube-proxy
yang ada di kluster.
Ketika sebuah proxy mendapati sebuah Service
baru, proxy tersebut akan melakukan instalasi
serangkaian rules iptables
yang akan melakukan redirect VIP ke rules tiap Service
. Rules
untuk tiap Service
ini terkait dengan rules tiap Endpoints
yang mengarahkan (destinasi NAT)
ke backend.
Ketika sebuah klien terhubung ke VIP dan terdapat _rules _iptables
yang diterapkan. Sebuah backend akan dipilih (hal ini dapat dilakukan berdasarkan session affinity
maupun secara acak) dan paket-paket yang ada akan diarahkan ke backend. Tidak seperti mekanisme
yang terjadi di userspace, paket-paket yang ada tidak pernah disalin ke userspace, kube-proxy
tidak harus aktif untuk menjamin kerja VIP, serta IP klien juga tidak perlu diubah.
Tahapan yang dijalankan sama dengan tahapan yang dijalankan ketika trafik masuk melalui sebuah node-port atau load-balancer, meskipun pada dua kasus di atas klien IP tidak akan mengalami perubahan.
Operasi iptables
berlangsung secara lambat pada kluster dengan skala besar (lebih dari 10.000 Service
).
IPVS didesain untuk mekanisme load balance dan berbasis pada hash tables yang berada di dalam kernel.
Dengan demikian kita dapat mendapatkan performa yang konsisten pada jumlah Service
yang cukup besar dengan
menggunakan kube-proxy
berbasis ipvs. Sementara itu, kube-proxy
berbasis ipvs memiliki algoritma
load balance yang lebih bervariasi (misalnya saja least conns, locality, weighted, persistence).
Service merupakan resource top-level pada API Kubernetes.
Penjelasan lebih lanjut mengenai objek API dapat ditemukan pada:
objek API Service
.
Kubernetes v1.0
stable
Kamu dapat menggunakan TCP untuk Service
dengan type apa pun, dan protokol ini merupakan
protokol default yang digunakan.
Kubernetes v1.0
stable
Kamu dapat menggunakan UDP untuk sebagian besar Service
.
Untuk Service
dengan type=LoadBalancer, dukungan terhadap UDP
bergantung pada penyedia layanan cloud yang kamu gunakan.
Kubernetes v1.1
stable
Apabila penyedia layanan cloud yang kamu gunakan mendukung, kamu dapat menggunakan
Service dengan type LoadBalancer
untuk melakukan mekanisme reverse proxy
bagi HTTP/HTTPS, dan melakukan forwarding ke Endpoints
dari _Service.
Catatan: Kamu juga dapat menggunakan IngressSebuah obyek API yang mengatur akses eksternal terhadap Service yang ada di dalam kluster, biasanya dalam bentuk request HTTP. sebagai salah satu alternatif penggunaanService
untuk HTTP/HTTPS.
Kubernetes v1.1
stable
Apabila penyedia layanan cloud yang kamu gunakan mendukung, (misalnya saja, AWS),
Service dengan type LoadBalancer
untuk melakukan konfigurasi load balancer
di luar Kubernetes sendiri, serta akan melakukan forwarding koneksi yang memiliki prefiks
protokol PROXY.
Load balancer akan melakukan serangkaian inisiasi octet yang memberikan deskripsi koneksi yang datang, dengan bentuk yang menyerupai:
PROXY TCP4 192.0.2.202 10.0.42.7 12345 7\r\n
yang kemudian diikuti data dari klien.
Kubernetes v1.12
alpha
alpha
(misalnya, v1alpha1
).Kubernetes memberikan dukungan bagi SCTP sebagai value dari definition yang ada pada
Service, Endpoints
, NetworkPolicy
dan Pod
sebagai fitur alpha. Untuk mengaktifkan fitur ini,
administrator kluster harus mengaktifkan feature gate SCTPSupport pada apiserver, contohnya
“--feature-gates=SCTPSupport=true,...”
. Ketika fature gate ini diaktifkan, pengguna dapat
memberikan value SCTP pada field protocol Service
, Endpoints
, NetworkPolicy
dan Pod
.
Kubernetes kemudian akan melakukan pengaturan agar jaringan yang digunakan agar jaringan tersebut menggunakan SCTP,
seperti halnya Kubernetes mengatur jaringan agar menggunakan TCP.
Dukungan untuk asosiasi multihomed SCTP membutuhkan plugin CNI yang dapat memberikan
pengalokasian multiple interface serta alamat IP pada sebuah Pod
.
NAT untuk asosiasi multihomed SCTP membutuhkan logika khusus pada modul kernel terkait.
Service
dengan type=LoadBalancerSebuah Service
dengan type LoadBalancer
dan protokol SCTP dapat dibuat
hanya jika implementasi load balancer penyedia layanan cloud menyediakan dukungan
bagi protokol SCTP. Apabila hal ini tidak terpenuhi, maka request pembuatan Servixe ini akan ditolak.
Load balancer yang disediakan oleh penyedia layanan cloud yang ada saat ini (Azure, AWS, CloudStack, GCE, OpenStack) tidak mendukung SCTP.
SCTP tidak didukung pada node berbasis Windows.
Kube-proxy tidak mendukung manajemen asosiasi SCTP ketika hal ini dilakukan pada mode userspace
Baca Bagaimana cara menghubungkan Front End ke Back End menggunakan sebuah Service
.
Apakah halaman ini berguna?
Thanks for the feedback. If you have a specific, answerable question about how to use Kubernetes, ask it on Stack Overflow. Open an issue in the GitHub repo if you want to report a problem or suggest an improvement.