This is the multi-page printable view of this section. Click here to print.
Services, Load Balancing, dan Jaringan
- 1: Service
- 2: Topologi Service (Service Topology)
- 3: EndpointSlice
- 4: DNS untuk Service dan Pod
- 5: Menghubungkan aplikasi dengan Service
- 6: Ingress
- 7: Kontroler Ingress
- 8: NetworkPolicy
- 9: Menambahkan Entry pada /etc/hosts Pod dengan HostAliases
- 10: Dual-stack IPv4/IPv6
1 - Service
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 dinamis (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
klaster 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.
Mendefinisikan sebuah 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 selector
Secara umum, Service
memberikan abstraksi mekanisme yang dilakukan untuk mengakses Pod
, tapi
mereka juga melakukan abstraksi bagi backend lainnya. Misalnya saja:
- Kamu ingin memiliki sebuah basis data eksternal di environment production tapi pada tahap test, kamu ingin menggunakan basis datamu sendiri.
- Kamu ingin merujuk service kamu pada service lainnya yang berada pada Namespace yang berbeda atau bahkan klaster yang berbeda.
- Kamu melakukan migrasi workloads ke Kubernetes dan beberapa backend yang kamu miliki masih berada di luar klaster Kubernetes.
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
Endpoints
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 dari Service
Kubernetes lainnya,
karena kube-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.
IP Virtual dan proxy Service
Setiap node di klaster 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.
Mode Proxy: userspace
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.
Mode Proxy: iptables
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.
Mode Proxy: ipvs
Kubernetes v1.9 [beta]
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 queue
kube-proxy
dijalankan. Ketika kube-proxy
dijalankan dengan mode proxy ipvs,
kube-proxy
akan melakukan proses validasi, apakah module IPVS sudah diinstal di node,
jika module tersebut belum diinstal, maka kube-proxy
akan menggunakan mode iptables
.
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").
Multi-Port Services
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.
Memilih sendiri alamat IP yang kamu inginkan
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.
Mengapa tidak menggunakan DNS round-robin?
Pertanyaan yang selalu muncul adalah kenapa kita menggunakan IP virtual dan bukan DNS round-robin standar? Terdapat beberapa alasan dibalik semua itu:
- Terdapat sejarah panjang dimana library DNS tidak mengikuti TTL DNS dan melakukan caching hasil dari lookup yang dilakukan.
- Banyak aplikasi yang melakukan lookup DNS hanya sekali dan kemudian melakukan cache hasil yang diperoleh.
- Bahkan apabila aplikasi dan library melakukan resolusi ulang yang proper, load dari setiap klien yang melakukan resolusi ulang DNS akan sulit untuk di manage.
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.
Discovering services
Kubernetes mendukung 2 buah mode primer untuk melakukan Service
- variabel environment dan DNS.
Variabel Environment
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.
DNS
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 klaster, 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
headless
Terkadang 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.
Dengan selector
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.
Tanpa selector
Untuk Service
"headless" tanpa selector, kontroler Endpoints
tidak akan membuat record Enpoints. Meskipun demikian,
sistem DNS tetap melakukan konfigurasi salah satu dari:
- record CNAME untuk
ExternalName
-tipe services. - record untuk semua
Endpoints
yang memiliki namaService
yang sama, untuk tipe lainnya.
Mekanisme publish 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 klaster 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
: MengeksposService
ke range alamat IP di dalam klaster. Apabila kamu memilih value iniService
yang kamu miliki hanya dapat diakses secara internal. tipe ini adalah default value dari ServiceType.NodePort
: MengeksposService
pada setiap IP node pada port statis atau port yang sama. SebuahService
ClusterIP
, yang manaService
NodePort
akan di-route , dibuat secara otomatis. Kamu dapat mengaksesService
dengan tipe ini, dari luar klaster melalui<NodeIP>:<NodePort>
.LoadBalancer
: MengeksposService
secara eksternal dengan menggunakanLoadBalancer
yang disediakan oleh penyedia layanan cloud.Service
dengan tipeNodePort
danClusterIP
, dimana trafik akan di-route, akan dibuat secara otomatis.ExternalName
: Melakukan pemetaanService
ke konten dari fieldexternalName
(misalnya:foo.bar.example.com
), dengan cara mengembalikan catatanCNAME
beserta value-nya. Tidak ada metode proxy apa pun yang diaktifkan. Mekanisme ini setidaknya membutuhkankube-dns
versi 1.7.
Type NodePort
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,
Type LoadBalancer
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 klaster. 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 klaster AKS dengan advanced networking.
Service
akan tetap diterima, meskipun proses pembuatan load balancer itu sendiri gagal.
Load balancer internal
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 untuk SSL di AWS
Dukungan parsial untuk SSL bagi klaster 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"
Protokol PROXY pada AWS
Untuk mengaktifkan dukungan protokol PROXY untuk klaster 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.
Akses Log ELB pada AWS
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 Koneksi pada AWS
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"
Anotasi ELB lainnya
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
Dukungan Network Load Balancer (NLB) pada AWS [alpha]
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"
Tipe ExternalName
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
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 karena ExternalName
memang ditujukan bagi penamaan canonical DNS.
Untuk melakukan hardcode alamat IP, kamu dapat menggunakan headless Service
sebagai alternatif.
Ketika melakukan pencarian host my-service.prod.svc.cluster.local
,
servis DNS klaster 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 klaster, 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.
IP Eksternal
Jika terdapat sebuah alamat IP eksternal yang melakukan mekanisme route ke satu atau lebih node yang ada di klaster, Service
Kubernetes dapat diekspos
dengan menggunakan externalIP
. Trafik yang diarahkan ke klaster 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 klaster.
Pada ServiceSpec, kamu dapat memberikan spesifikasi externalIP
dan ServiceTypes
.
Pada contoh di bawah ini. "my-service"
dapat diakses oleh klien pada "198.51.100.32: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
Kekurangan
Penggunaan proxy userspace untuk VIP dapat digunakan untuk skala kecil hingga menengah,
meski begitu hal ini tidak scalable untuk klaster 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 klaster,
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).
Pengerjaan lebih lanjut
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.
Detail mendalam mengenai IP virtual
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.
Menghindari collison
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.
IP dan VIP
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.
Userspace
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 klaster.
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.
Iptables
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 klaster.
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.
Ipvs
Operasi iptables
berlangsung secara lambat pada klaster 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).
Objek API
Service merupakan resource top-level pada API Kubernetes.
Penjelasan lebih lanjut mengenai objek API dapat ditemukan pada:
objek API Service
.
Protokol yang didukung
TCP
Kubernetes v1.0 [stable]
Kamu dapat menggunakan TCP untuk Service
dengan type apa pun, dan protokol ini merupakan
protokol default yang digunakan.
UDP
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.
HTTP
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.
Service
untuk HTTP/HTTPS.
Protokol PROXY
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.
SCTP
Kubernetes v1.12 [alpha]
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 klaster 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.
Perhatian
Dukungan untuk asoasiasi multihomed SCTP
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=LoadBalancer
Sebuah 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.
Windows
SCTP tidak didukung pada node berbasis Windows.
Kube-proxy userspace
Kube-proxy tidak mendukung manajemen asosiasi SCTP ketika hal ini dilakukan pada mode userspace
Selanjutnya
Baca Bagaimana cara menghubungkan Front End ke Back End menggunakan sebuah Service
.
2 - Topologi Service (Service Topology)
Kubernetes v1.17 [alpha]
Topologi Service memungkinkan Service untuk merutekan lalu lintas jaringan berdasarkan topologi Node dalam klaster. Misalnya, suatu layanan dapat menentukan lalu lintas jaringan yang lebih diutamakan untuk dirutekan ke beberapa endpoint yang berada pada Node yang sama dengan klien, atau pada availability zone yang sama.
Pengantar
Secara bawaan lalu lintas jaringan yang dikirim ke ClusterIP
atau NodePort
dari Service
dapat dialihkan ke alamat backend untuk Service tersebut. Sejak Kubernetes 1.7
dimungkinkan untuk merutekan lalu lintas jaringan "eksternal" ke Pod yang berjalan di
Node yang menerima lalu lintas jaringan, tetapi fitur ini tidak didukung untuk ClusterIP
dari
Service, dan topologi yang lebih kompleks — seperti rute zonasi —
belum memungkinkan. Fitur topologi Service mengatasi kekurangan ini dengan
mengizinkan pembuat layanan untuk mendefinisikan kebijakan dalam merutekan lalu lintas jaringan
berdasarkan label Node untuk Node-Node asal dan tujuan.
Dengan menggunakan label Node yang sesuai antara asal dan tujuan, operator dapat menunjuk kelompok Node mana yang "lebih dekat" dan mana yang "lebih jauh" antara satu sama lain, dengan menggunakan metrik apa pun yang masuk akal untuk memenuhi persyaratan dari operator itu. Untuk sebagian besar operator di publik cloud, misalnya, ada preferensi untuk menjaga layanan lalu lintas jaringan dalam zona yang sama, karena lalu lintas jaringan antar zona memiliki biaya yang dibebankan, sementara lalu lintas jaringan dalam zona yang sama tidak ada biaya. Kebutuhan umum lainnya termasuk kemampuan untuk merutekan lalu lintas jaringan ke Pod lokal yang dikelola oleh sebuah DaemonSet, atau menjaga lalu lintas jaringan ke Node yang terhubung ke top-of-rack switch yang sama untuk mendapatkan latensi yang terendah.
Menggunakan Topologi Service
Jika klaster kamu mengaktifkan topologi Service kamu dapat mengontrol rute lalu lintas jaringan Service
dengan mengatur bagian topologyKeys
pada spesifikasi Service. Bagian ini
adalah daftar urutan label-label Node yang akan digunakan untuk mengurutkan endpoint
saat mengakses Service ini. Lalu lintas jaringan akan diarahkan ke Node yang nilai
label pertamanya cocok dengan nilai dari Node asal untuk label yang sama. Jika
tidak ada backend untuk Service pada Node yang sesuai, maka label kedua akan
dipertimbangkan, dan seterusnya, sampai tidak ada label yang tersisa.
Jika tidak ditemukan kecocokan, lalu lintas jaringan akan ditolak, sama seperti jika tidak ada
sama sekali backend untuk Service tersebut. Artinya, endpoint dipilih
berdasarkan kunci topologi yang pertama yang tersedia pada backend. Jika dalam
bagian ini ditentukan dan semua entri tidak memiliki backend yang sesuai dengan
topologi klien, maka Service tidak memiliki backend untuk klien dan koneksi harus
digagalkan. Nilai khusus "*"
dapat digunakan untuk mengartikan "topologi
apa saja". Nilai catch-all ini, jika digunakan, maka hanya sebagai
nilai terakhir dalam daftar.
Jika topologyKeys
tidak ditentukan atau kosong, tidak ada batasan topologi
yang akan diterapkan.
Seandainya sebuah klaster dengan Node yang dilabeli dengan nama host ,
nama zona, dan nama wilayah mereka, maka kamu dapat mengatur nilai
topologyKeys
dari sebuah Service untuk mengarahkan lalu lintas jaringan seperti berikut ini.
- Hanya ke endpoint dalam Node yang sama, gagal jika tidak ada endpoint pada Node:
["kubernetes.io/hostname"]
. - Lebih memilih ke endpoint dalam Node yang sama, jika tidak ditemukan maka ke endpoint pada zona yang sama, diikuti oleh wilayah yang sama, dan selain itu akan gagal:
["kubernetes.io/hostname ", "topology.kubernetes.io/zone", "topology.kubernetes.io/region"]
. Ini mungkin berguna, misalnya, dalam kasus di mana lokalitas data sangatlah penting. - Lebih memilih ke endpoint dalam zona yang sama, tetapi memilih endpoint mana saja yang tersedia apabila tidak ada yang tersedia dalam zona ini:
["topology.kubernetes.io/zone ","*"]
.
Batasan
- Topologi Service tidak kompatibel dengan
externalTrafficPolicy=Local
, dan karena itu Service tidak dapat menggunakan kedua fitur ini sekaligus. Dimungkinkan untuk menggunakan kedua fitur pada klaster yang sama untuk Service yang berbeda, bukan untuk Service yang sama. - Untuk saat ini kunci topologi yang valid hanya terbatas pada
kubernetes.io/hostname
,topology.kubernetes.io/zone
, dantopology.kubernetes.io/region
, tetapi akan digeneralisasikan ke label Node yang lain di masa depan. - Kunci topologi harus merupakan kunci label yang valid dan paling banyak hanya 16 kunci yang dapat ditentukan.
- Nilai catch-all,
"*"
, harus menjadi nilai terakhir pada kunci topologi, jika nilai itu digunakan.
Contoh
Berikut ini adalah contoh umum penggunaan fitur topologi Service.
Hanya pada endpoint pada Node lokal
Service yang hanya merutekan ke endpoint pada Node lokal. Jika tidak ada endpoint pada Node, lalu lintas jaringan akan dihentikan:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "kubernetes.io/hostname"
Lebih memilih endpoint pada Node lokal
Service yang lebih memilih endpoint pada Node lokal, namun akan memilih ke endpoint dalam klaster jika endpoint pada Node lokal tidak ada:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "kubernetes.io/hostname"
- "*"
Hanya untuk endpoint pada zona atau wilayah yang sama
Service yang lebih memilih endpoint dalam zona yang sama daripada wilayah yang sama. Jika tidak ada endpoint pada
keduanya, maka lalu lintas jaringan akan dihentikan.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "topology.kubernetes.io/zone"
- "topology.kubernetes.io/region"
Lebih memilih endpoint pada Node lokal, zona yang sama, dan kemudian wilayah yang sama
Service yang lebih memilih endpoint pada Node lokal, zona yang sama, dan kemudian baru wilayah yang sama, namun jika tetap tidak ditemukan maka akan memilih endpoint diseluruh klaster.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 9376
topologyKeys:
- "kubernetes.io/hostname"
- "topology.kubernetes.io/zone"
- "topology.kubernetes.io/region"
- "*"
Selanjutnya
- Baca tentang mengaktifkan topologi Service
- Baca menghubungkan aplikasi dengan Service
3 - EndpointSlice
Kubernetes v1.17 [beta]
EndpointSlice menyediakan sebuah cara yang mudah untuk melacak endpoint jaringan dalam sebuah klaster Kubernetes. EndpointSlice memberikan alternatif yang lebih scalable dan lebih dapat diperluas dibandingkan dengan Endpoints.
Motivasi
Endpoints API telah menyediakan sebuah cara yang mudah dan sederhana untuk melacak endpoint jaringan pada Kubernetes. Sayangnya, seiring dengan besarnya klaster Kubernetes dan Service, batasan-batasan yang dimiliki API tersebut semakin terlihat. Terutama, hal tersebut termasuk kendala-kendala mengenai proses scaling endpoint jaringan dalam jumlah yang besar.
Karena semua endpoint jaringan untuk sebuah Service disimpan dalam satu sumber daya Endpoints, sumber daya tersebut dapat menjadi cukup besar. Hal itu dapat mempengaruhi kinerja dari komponen-komponen Kubernetes (terutama master control plane) dan menyebabkan lalu lintas jaringan dan pemrosesan yang cukup besar ketika Endpoints berubah. EndpointSlice membantu kamu menghindari masalah-masalah tersebut dan juga menyediakan platform yang dapat diperluas untuk fitur-fitur tambahan seperti topological routing.
Sumber daya EndpointSlice
Pada Kubernetes, sebuah EndpointSlice memiliki referensi-referensi terhadap sekumpulan endpoint jaringan. Controller EndpointSlice secara otomatis membuat EndpointSlice untuk sebuah Service Kubernetes ketika sebuah selektor dituliskan. EndpointSlice tersebut akan memiliki referensi-referensi menuju Pod manapun yang cocok dengan selektor pada Service tersebut. EndpointSlice mengelompokkan endpoint jaringan berdasarkan kombinasi Service dan Port yang unik. Nama dari sebuah objek EndpointSlice haruslah berupa nama subdomain DNS yang sah.
Sebagai contoh, berikut merupakan sampel sumber daya EndpointSlice untuk sebuah Service Kubernetes
yang bernama example
.
apiVersion: discovery.k8s.io/v1beta1
kind: EndpointSlice
metadata:
name: example-abc
labels:
kubernetes.io/service-name: example
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.1.2.3"
conditions:
ready: true
hostname: pod-1
topology:
kubernetes.io/hostname: node-1
topology.kubernetes.io/zone: us-west2-a
Secara bawaan, setiap EndpointSlice yang dikelola oleh controller EndpointSlice tidak akan memiliki lebih dari 100 endpoint. Di bawah skala tersebut, EndpointSlice akan memetakan 1:1 dengan Endpoints dan Service dan akan memiliki kinerja yang sama.
EndpointSlice dapat bertindak sebagai sumber kebenaran untuk kube-proxy sebagai acuan mengenai bagaimana cara untuk merutekan lalu lintas jaringan internal. Ketika diaktifkan, EndpointSlice semestinya memberikan peningkatan kinerja untuk Service yang memiliki Endpoints dalam jumlah besar.
Tipe-tipe Alamat
EndpointSlice mendukung tiga tipe alamat:
- IPv4
- IPv6
- FQDN (Fully Qualified Domain Name)
Topologi
Setiap endpoint pada EndpointSlice dapat memiliki informasi topologi yang relevan. Hal ini digunakan untuk mengindikasikan di mana endpoint berada, berisi informasi mengenai Node yang bersangkutan, zona, dan wilayah. Ketika nilai-nilai tersebut tersedia, label-label Topology berikut akan ditambahkan oleh controller EndpointSlice:
kubernetes.io/hostname
- Nama dari Node tempat endpoint berada.topology.kubernetes.io/zone
- Zona tempat endpoint berada.topology.kubernetes.io/region
- Region tempat endpoint berada.
Nilai-nilai dari label-label berikut berasal dari sumber daya yang diasosiasikan dengan tiap endpoint pada sebuah slice. Label hostname merepresentasikan nilai dari kolom NodeName pada Pod yang bersangkutan. Label zona dan wilayah merepresentasikan nilai dari label-label dengan nama yang sama pada Node yang bersangkutan.
Pengelolaan
Secara bawaan, EndpointSlice dibuat dan dikelola oleh controller
EndpointSlice. Ada berbagai macam kasus lain untuk EndpointSlice, seperti
implementasi service mesh, yang memungkinkan adanya entitas atau controller lain
yang dapat mengelola beberapa EndpointSlice sekaligus. Untuk memastikan beberapa entitas dapat
mengelola EndpointSlice tanpa mengganggu satu sama lain, sebuah
label endpointslice.kubernetes.io/managed-by
digunakan untuk mengindikasikan entitas
yang mengelola sebuah EndpointSlice. Controller EndpointSlice akan menambahkan
endpointslice-controller.k8s.io
sebagai nilai dari label tersebut pada seluruh
EndpointSlice yang dikelolanya. Entitas lain yang mengelola EndpointSlice juga diharuskan untuk
menambahkan nilai yang unik untuk label tersebut.
Kepemilikan
Pada kebanyakan kasus, EndpointSlice akan dimiliki oleh Service yang diikutinya. Hal ini diindikasikan dengan referensi pemilik pada tiap EndpointSlice dan
juga label kubernetes.io/service-name
yang memudahkan pencarian seluruh
EndpointSlice yang dimiliki oleh sebuah Service.
Controller EndpointSlice
Controller EndpointSlice mengamati Service dan Pod untuk memastikan EndpointSlice yang bersangkutan berada dalam kondisi terkini. Controller EndpointSlice akan mengelola EndpointSlice untuk setiap Service yang memiliki selektor. Ini akan merepresentasikan IP dari Pod yang cocok dengan selektor dari Service tersebut.
Ukuran EndpointSlice
Secara bawaan, jumlah endpoint yang dapat dimiliki tiap EndpointSlice dibatasi sebanyak 100 endpoint. Kamu dapat
mengaturnya melalui opsi --max-endpoints-per-slice
kube-controller-manager sampai dengan
jumlah maksimum sebanyak 1000 endpoint.
Distribusi EndpointSlice
Tiap EndpointSlice memiliki sekumpulan port yang berlaku untuk seluruh endpoint dalam sebuah sumber daya. Ketika nama port digunakan untuk sebuah Service, Pod mungkin mendapatkan nomor target port yang berbeda-beda untuk nama port yang sama, sehingga membutuhkan EndpointSlice yang berbeda. Hal ini mirip dengan logika mengenai bagaimana subset dikelompokkan dengan Endpoints.
Controller EndpointSlice akan mencoba untuk mengisi EndpointSlice sebanyak mungkin, tetapi tidak secara aktif melakukan rebalance terhadap EndpointSlice tersebut. Logika dari controller cukup sederhana:
- Melakukan iterasi terhadap EndpointSlice yang sudah ada, menghapus endpoint yang sudah tidak lagi dibutuhkan dan memperbarui endpoint yang sesuai yang mungkin telah berubah.
- Melakukan iterasi terhadap EndpointSlice yang sudah dimodifikasi pada langkah pertama dan mengisinya dengan endpoint baru yang dibutuhkan.
- Jika masih tersisa endpoint baru untuk ditambahkan, mencoba untuk menambahkannya pada slice yang tidak berubah sebelumnya dan/atau membuat slice yang baru.
Terlebih penting, langkah ketiga memprioritaskan untuk membatasi pembaruan EndpointSlice terhadap distribusi dari EndpointSlice yang benar-benar penuh. Sebagai contoh, jika ada 10 endpoint baru untuk ditambahkan dan ada 2 EndpointSlice yang masing-masing memiliki ruang untuk 5 endpoint baru, pendekatan ini akan membuat sebuah EndpointSlice baru daripada mengisi 2 EndpointSlice yang sudah ada. Dengan kata lain, pembuatan sebuah EndpointSlice lebih diutamakan daripada pembaruan beberapa EndpointSlice.
Dengan kube-proxy yang berjalan pada tiap Node dan mengamati EndpointSlice, setiap perubahan pada sebuah EndpointSlice menjadi sangat mahal karena hal tersebut akan dikirimkan ke setiap Node dalam klaster. Pendekatan ini ditujukan untuk membatasi jumlah perubahan yang perlu dikirimkan ke setiap Node, meskipun hal tersebut berdampak pada banyaknya EndpointSlice yang tidak penuh.
Pada praktiknya, distribusi yang kurang ideal seperti ini akan jarang ditemukan. Kebanyakan perubahan yang diproses oleh controller EndpointSlice akan cukup kecil untuk dapat masuk pada EndpointSlice yang sudah ada, dan jika tidak, cepat atau lambat sebuah EndpointSlice baru akan segera dibutuhkan. Pembaruan bertahap (rolling update) dari Deployment juga menyediakan sebuah proses pengemasan ulang EndpointSlice yang natural seiring dengan digantikannya seluruh Pod dan endpoint yang bersangkutan.
Selanjutnya
4 - DNS untuk Service dan Pod
Laman ini menyediakan ikhtisar dari dukungan DNS oleh Kubernetes.
Pendahuluan
Kubernetes DNS melakukan scheduling DNS Pod dan Service yang ada pada klaster, serta melakukan konfigurasi kubelet untuk memberikan informasi bagi setiap Container untuk menggunakan DNS Service IP untuk melakukan resolusi DNS.
Apa Sajakah yang Mendapatkan Nama DNS?
Setiap Service yang didefinisikan di dalam klaster (termasuk server DNS itu sendiri) memiliki nama DNS. Secara default, sebuah list pencarian DNS pada Pod klien akan mencantumkan namespace Pod itu sendiri serta domain default klaster. Hal ini dapat diilustrasikan dengan contoh berikut:
Asumsikan sebuah Service dengan nama foo
pada Kubernetes dengan namespace bar
.
Sebuah Pod yang dijalankan di namespace bar
dapat melakukan resolusi
terhadap Service ini dengan melakukan query DNS
untuk foo
. Sebuah Pod yang dijalankan pada namespace quux
dapat melakukan
resolusi Service ini dengan melakukan query DNS untuk foo.bar
.
Bagian di bawah ini akan menampilkan detail tipe rekaman serta layout yang didukung. Layout atau nama query lain yang dapat digunakan dianggap sebagai detail implementasi yang bisa saja berubah tanpa adanya pemberitahuan sebelumnya. Untuk informasi spesifikasi terbaru kamu dapat membaca Service Discovery pada Kubernetes berbasis DNS.
Service
A record
Service "Normal" (bukan headless) akan diberikan sebuah A record untuk sebuah nama dalam bentuk
my-svc.my-namespace.svc.cluster-domain.example
. Inilah yang kemudian digunakan untuk melakukan
resolusi IP klaster dari Service tersebut.
Service "Headless" (tanpa IP klaster) juga memiliki sebuah A record DNS dengan format
my-svc.my-namespace.svc.cluster-domain.example
. Tidak seperti halnya Service normal,
DNS ini akan melakukan resolusi pada serangkauan IP dari Pod yang dipilih oleh Service tadi.
Klien diharapkan untuk mengkonsumsi serangkaian IP ini atau cara lain yang digunakan adalah pemilihan
menggunakan penjadwalan Round-Robin dari set yang ada.
SRV record
SRV record dibuat untuk port bernama yang merupakan bagian dari Service normal maupun Headless
Services.
Untuk setiap port bernama, SRV record akan memiliki format
_my-port-name._my-port-protocol.my-svc.my-namespace.svc.cluster-domain.example
.
Untuk sebuah Service normal, ini akan melakukan resolusi pada nomor port dan
nama domain: my-svc.my-namespace.svc.cluster-domain.example
.
Untuk Service headless, ini akan melakukan resolusi pada serangkaian Pod yang merupakan backend dari Service
tersebut yang memiliki format: auto-generated-name.my-svc.my-namespace.svc.cluster-domain.example
.
Pod
Hostname Pod dan Field Subdomain
Saat ini ketika sebuah Pod dibuat, hostname-nya adalah nilai dari metadata.name
.
Spek Pod memiliki field opsional hostname
, yang dapat digunakan untuk menspesifikasikan
hostname Pod. Ketika dispesifikasikan, maka nama ini akan didahulukan di atas nama Pod .
Misalnya, sebuah Pod dengan hostname
yang diberikan nilai "my-host
", maka hostname Pod tersebut akan menjadi "my-host
".
Spek Pod juga memiliki field opsional subdomain
yang dapat digunakan untuk menspesifikasikan
subdomain Pod tersebut. Misalnya saja sebuah Pod dengan hostname
yang diberi nilai "foo
", dan subdomain
yang diberi nilai "bar
", pada namespace "my-namespace
", akan memiliki fully qualified
domain name (FQDN) "foo.bar.my-namespace.svc.cluster-domain.example
".
Contoh:
apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo # Actually, no port is needed.
port: 1234
targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
---
apiVersion: v1
kind: Pod
metadata:
name: busybox2
labels:
name: busybox
spec:
hostname: busybox-2
subdomain: default-subdomain
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
name: busybox
Jika terdapat sebuah Service headless memiliki nama yang sama dengan
subdomain dari suatu Pod pada namespace yang sama, server KubeDNS klaster akan mengembalikan
A record untuk FQDN Pod.
Sebagai contoh, misalnya terdapat sebuah Pod dengan hostname "busybox-1
" dan
subdomain "default-subdomain
", serta sebuah Service headless dengan nama "default-subdomain
"
berada pada suatu namespace yang sama, maka Pod tersebut akan menerima FQDN dirinya sendiri
sebagai "busybox-1.default-subdomain.my-namespace.svc.cluster-domain.example
". DNS mengembalikan
A record pada nama tersebut dan mengarahkannya pada IP Pod. Baik Pod "busybox1
" dan
"busybox2
" bisa saja memiliki A record yang berbeda.
Objek Endpoint dapat menspesifikasikan hostname
untuk alamat endpoint manapun
beserta dengan alamat IP-nya.
hostname
diperlukan
agar sebuah Pod memiliki A record. Sebuah Pod yang tidak memiliki hostname
tetapi memiliki subdomain
hanya akan membuat sebuah A record untuk Service headless
(default-subdomain.my-namespace.svc.cluster-domain.example
), yang merujuk pada IP dari
Pod tersebut. Pod juga harus dalam status ready agar dapat memiliki A record kecuali
field publishNotReadyAddresses=True
diaktifkan pada Service.
Kebijakan DNS Pod
Kebijakan DNS dapat diaktifkan untuk setiap Pod. Kubernetes saat ini mendukung
kebijakan DNS spesifik Pod (pod-specific DNS policies). Kebijakan ini
dispesifikasikan pada field dnsPolicy
yang ada pada spek Pod.
- "
Default
": Pod akan mewarisi konfigurasi resolusi yang berasal dari Node dimana Pod tersebut dijalankan. Silakan baca diskusi terkait untuk detailnya. - "
ClusterFirst
": Query DNS apa pun yang tidak sesuai dengan sufiks domain klaster yang sudah dikonfigurasi misalnya "www.kubernetes.io
", akan di-forward ke nameserver upstream yang diwarisi dari Node. Administrator klaster bisa saja memiliki stub-domain atau DNS usptream lain yang sudah dikonfigurasi. Silakan lihat diskusi terkait untuk detail lebih lanjut mengenai bagaimana query DNS melakukan hal tersebut. - "
ClusterFirstWithHostNet
": Untuk Pod yang dijalankan dengan menggunakanhostNetwork
, kamu harus secara eksplisit mengaktifkan kebijakan DNS-nya menjadi "ClusterFirstWithHostNet
". - "
None
": Hal ini mengisikan sebuah Pod untuk mengabaikan konfigurasi DNS dari environment Kubernetes Semua pengaturan DNS disediakan menngunakan fielddnsConfig
yang ada pada spek Pod. Silakan lihat konfigurasi DNS Pod di bawah.
dnsPolicy
tidak secara eksplisit dispesifikasikan, maka “ClusterFirst” akan digunakan.
Contoh di bawah ini menunjukkan sebuah Pod dengan kebijakan
DNS yang diubah menjadi "ClusterFirstWithHostNet
" karena field hostNetwork
diubah menjadi true
.
apiVersion: v1
kind: Pod
metadata:
name: busybox
namespace: default
spec:
containers:
- image: busybox:1.28
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
name: busybox
restartPolicy: Always
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
Konfigurasi DNS Pod
Konfigurasi DNS Pod mengizinkan pengguna untuk memiliki lebih banyak kontrol terhadap pengaturan DNS pada Pod.
Field dnsConfig
bersifat opsional dan dapat digunakan dengan
pengaturan dnsPolicy
apa pun.
Meskipun begitu, ketika field dnsPolicy
pada sebuah Pod diubah menjadi "None
",
maka field dnsConfig
harus dispesifikasikan.
Berikut merupakan properti yang dapat dispesifikasikan oleh pengguna
pada field dnsConfig
:
nameservers
: serangkaian alamat IP yang akan digunakan sebagai server DNS bagi Pod. Jumlah maksimum dari IP yang dapat didaftarkan pada field ini adalah tiga buah IP. Ketika sebuahdnsPolicy
pada Pod diubah menjadi "None
", maka list ini setidaknya harus mengandung sebuah alamat IP, selain kasus tersebut properti ini bersifat opsional. Server yang didaftarkan akan digabungkan di dalam nameserver dasar yang dihasilkan dari kebijakan DNS yang dispesifikasikan, apabila terdapat duplikat terhadap alamat yang didaftarkan maka alamat tersebut akan dihapus.searches
: merupakan serangkaian domain pencarian DNS yang digunakan untuk proses lookup pada Pod. Properti ini bersifat opsional. Ketika dispesifikasikan, list yang disediakan akan digabungkan dengan nama domain pencarian dasar yang dihasilkan dari kebijakan DNS yang dipilih. Alamat yang duplikat akan dihapus. Nilai maksimum domain pencarian yang dapat didaftarkan adalah 6 domain.options
: merupakan sebuah list opsional yang berisikan objek dimana setiap objek bisa saja memiliki propertiname
(yang bersifat wajib). Isi dari properti ini akan digabungkan dengan opsi yang dihasilkan kebijakan DNS yang digunakan. Alamat yang duplikat akan dihapus.
Di bawah ini merupakan contoh sebuah Pod dengan pengaturan DNS kustom:
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: dns-example
spec:
containers:
- name: test
image: nginx
dnsPolicy: "None"
dnsConfig:
nameservers:
- 1.2.3.4
searches:
- ns1.svc.cluster-domain.example
- my.dns.search.suffix
options:
- name: ndots
value: "2"
- name: edns0
Ketika Pod diatas dibuat, maka Container test
memiliki isi berkas /etc/resolv.conf
sebagai berikut:
nameserver 1.2.3.4
search ns1.svc.cluster-domain.example my.dns.search.suffix
options ndots:2 edns0
Untuk pengaturan IPv6, path pencarian dan name server harus dispesifikasikan sebagai berikut:
kubectl exec -it dns-example -- cat /etc/resolv.conf
Keluaran yang dihasilkan akan menyerupai:
nameserver fd00:79:30::a
search default.svc.cluster-domain.example svc.cluster-domain.example cluster-domain.example
options ndots:5
Keberadaan Fitur (Feature Availability)
Keberadaan Pod DNS Config dan DNS Policy "None
"" diilustrasikan pada tabel di bawah ini.
versi k8s | Dukungan Fitur |
---|---|
1.14 | Stable |
1.10 | Beta (aktif secara default) |
1.9 | Alpha |
Selanjutnya
Untuk petunjuk lebih lanjut mengenai administrasi konfigurasi DNS, kamu dapat membaca Cara Melakukan Konfigurasi Service DNS
5 - Menghubungkan aplikasi dengan Service
Model Kubernetes untuk menghubungkan kontainer
Sekarang kamu memiliki aplikasi yang telah direplikasi, kamu dapat mengeksposnya di jaringan. Sebelum membahas pendekatan jaringan di Kubernetes, akan lebih baik jika kamu paham bagaimana jaringan bekerja di dalam Docker.
Secara default, Docker menggunakan jaringan host, jadi kontainer dapat berkomunikasi dengan kontainer lainnya jika mereka berada di dalam node yang sama. Agar kontainer Docker dapat berkomunikasi antar node, masing-masing kontainer tersebut harus diberikan port yang berbeda di alamat IP node tersebut, yang akan diteruskan (proxied) ke dalam kontainer. Artinya adalah para kontainer di dalam sebuah node harus berkoordinasi port mana yang akan digunakan atau dialokasikan secara otomatis.
Akan sulit untuk mengkoordinasikan port yang digunakan oleh banyak pengembang. Kubernetes mengasumsikan bahwa Pod dapat berkomunikasi dengan Pod lain, terlepas di Node mana Pod tersebut di deploy. Kubernetes memberikan setiap Pod alamat ClusterIP sehingga kamu tidak perlu secara explisit membuat jalur antara Pod ataupun memetakan port kontainer ke dalam port di dalam Node tersebut. Ini berarti kontainer di dalam sebuah Pod dapat berkomunikasi dengan localhost via port, dan setiap Pod di dalam klaster dapat berkomunikasi tanpa NAT. Panduan ini akan membahas bagaimana kamu dapat menjalankan sebuah layanan atau aplikasi di dalam model jaringan di atas.
Panduan ini menggunakan server nginx sederhana untuk mendemonstrasikan konsepnya. Konsep yang sama juga ditulis lebih lengkap di Aplikasi Jenkins CI.
Mengekspos Pod ke dalam klaster
Kita melakukan ini di beberapa contoh sebelumnya, tetapi mari kita lakukan sekali lagi dan berfokus pada prespektif jaringannya. Buat sebuah nginx Pod, dan perhatikan bahwa templat tersebut mempunyai spesifikasi port kontainer:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
ports:
- containerPort: 80
Ini membuat aplikasi tersebut dapat diakses dari node manapun di dalam klaster kamu. Cek lokasi node dimana Pod tersebut berjalan:
kubectl apply -f ./run-my-nginx.yaml
kubectl get pods -l run=my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-3800858182-jr4a2 1/1 Running 0 13s 10.244.3.4 kubernetes-minion-905m
my-nginx-3800858182-kna2y 1/1 Running 0 13s 10.244.2.5 kubernetes-minion-ljyd
Cek IP dari Pod kamu:
kubectl get pods -l run=my-nginx -o yaml | grep podIP
podIP: 10.244.3.4
podIP: 10.244.2.5
Kamu dapat melakukan akses dengan ssh ke dalam node di dalam klaster dan mengakses IP Pod tersebut menggunakan curl. Perlu dicatat bahwa kontainer tersebut tidak menggunakan port 80 di dalam node, atau aturan NAT khusus untuk merutekan trafik ke dalam Pod. Ini berarti kamu dapat menjalankan banyak nginx Pod di node yang sama dimana setiap Pod dapat menggunakan containerPort yang sama, kamu dapat mengakses semua itu dari Pod lain ataupun dari node di dalam klaster menggunakan IP. Seperti Docker, port masih dapat di publikasi ke dalam * interface node*, tetapi kebutuhan seperti ini sudah berkurang karena model jaringannya.
Kamu dapat membaca lebih detail bagaimana kita melakukan ini jika kamu penasaran.
Membuat Service
Kita mempunyai Pod yang menjalankan nginx di dalam klaster. Teorinya, kamu dapat berkomunikasi ke Pod tersebut secara langsung, tapi apa yang terjadi jika sebuah node mati? Pod di dalam node tersebut ikut mati, dan Deployment akan membuat Pod baru, dengan IP yang berbeda. Ini adalah masalah yang Service selesaikan.
Service Kubernetes adalah sebuah abstraksi yang mendefinisikan sekumpulan Pod yang menyediakan fungsi yang sama dan berjalan di dalam klaster. Saat dibuat, setiap Service diberikan sebuah alamat IP (disebut juga ClusterIP). Alamat ini akan terus ada, dan tidak akan pernah berubah selama Service hidup. Pod dapat berkomunikasi dengan Service dan trafik yang menuju Service tersebut akan otomatis dilakukan mekanisme load balancing ke Pod yang merupakan anggota dari Service tersebut.
Kamu dapat membuat Service untuk replika 2 nginx dengan kubectl explose
:
kubectl expose deployment/my-nginx
service/my-nginx exposed
Perintah di atas sama dengan kubectl apply -f
dengan yaml sebagai berikut:
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
Spesifikasi ini akan membuat Service yang membuka TCP port 80 di setiap Pod dengan label run: my-nginx
dan mengeksposnya ke dalam port Service (targetPort
: adalah port kontainer yang menerima trafik, port
adalah service port yang dapat berupa port apapun yang digunakan Pod lain untuk mengakses Service).
Lihat Service objek API untuk melihat daftar field apa saja yang didukung di definisi Service. Cek Service kamu:
kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.0.162.149 <none> 80/TCP 21s
Seperti yang disebutkan sebelumnya, sebuah Service berisi sekumpulan Pod. Pod diekspos melalui endpoints
. Service selector akan mengecek Pod secara terus-menerus dan hasilnya akan dikirim (POSTed) ke objek endpoint yang bernama my-nginx
. Saat sebuah Pod mati, IP Pod di dalam endpoint tersebut akan otomatis dihapus, dan Pod baru yang sesuai dengan Service selector akan otomatis ditambahkan ke dalam endpoint. Cek endpoint dan perhatikan bahwa IP sama dengan Pod yang dibuat di langkah pertama:
kubectl describe svc my-nginx
Name: my-nginx
Namespace: default
Labels: run=my-nginx
Annotations: <none>
Selector: run=my-nginx
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.0.162.149
IPs: 10.0.162.149
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.2.5:80,10.244.3.4:80
Session Affinity: None
Events: <none>
kubectl get ep my-nginx
NAME ENDPOINTS AGE
my-nginx 10.244.2.5:80,10.244.3.4:80 1m
Kamu sekarang dapat melakukan curl ke dalam nginx Service di <CLUSTER-IP>:<PORT>
dari node manapun di klaster. Perlu dicatat bahwa Service IP adalah IP virtual, IP tersebut tidak pernah ada di interface node manapun. Jika kamu penasaran bagaimana konsep ini bekerja, kamu dapat membaca lebih lanjut tentang service proxy.
Mengakses Service
Kubernetes mendukung 2 mode utama untuk menemukan sebuah Service - variabel environment dan DNS. DNS membutuhkan tambahan CoreDNS di dalam klaster.
Variabel Environment
Saat sebuah Pod berjalan di Node, kubelet akan menambahkan variabel environment untuk setiap Service yang aktif ke dalam Pod. Ini menimbulkan beberapa masalah. Untuk melihatnya, periksa environment dari Pod nginx yang telah kamu buat (nama Pod-mu akan berbeda-beda):
kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443
Perlu dicatat tidak ada variabel environment yang menunjukan Service yang kamu buat. Ini terjadi karena kamu membuat replika terlebih dahulu sebelum membuat Service. Kerugian lain ditimbulkan adalah bahwa komponen scheduler mungkin saja bisa menempatkan semua Pod di dalam satu Node, yang akan membuat keseluruhan Service mati jika Node tersebut mati. Kita dapat menyelesaikan masalah ini dengan menghapus 2 Pod tersebut dan menunggu Deployment untuk membuat Pod kembali. Kali ini Service ada sebelum replika Pod tersebut ada. Ini akan memberikan kamu scheduler-level Service (jika semua Node kamu mempunyai kapasitas yang sama), serta variabel environment yang benar:
kubectl scale deployment my-nginx --replicas=0; kubectl scale deployment my-nginx --replicas=2;
kubectl get pods -l run=my-nginx -o wide
NAME READY STATUS RESTARTS AGE IP NODE
my-nginx-3800858182-e9ihh 1/1 Running 0 5s 10.244.2.7 kubernetes-minion-ljyd
my-nginx-3800858182-j4rm4 1/1 Running 0 5s 10.244.3.8 kubernetes-minion-905m
Kamu mungkin saja melihat Pod dengan nama yang berbeda, hal ini terjadi karena Pod-Pod itu dihapus dan dibuat ulang.
kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443
DNS
Kubernetes menawarkan sebuah layanan DNS klaster tambahan yang secara otomatis memberikan sebuah nama dns pada Service. Kamu dapat mengecek jika DNS berjalan di dalam klaster Kubernetes:
kubectl get services kube-dns --namespace=kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.0.0.10 <none> 53/UDP,53/TCP 8m
Jika DNS belum berjalan, kamu dapat mengaktifkannya.
Sisa panduan ini mengasumsikan kamu mempunyai Service dengan IP (my-nginx), dan sebuah server DNS yang memberikan nama ke dalam IP tersebut (CoreDNS klaster), jadi kamu dapat berkomunikasi dengan Service dari Pod lain di dalam klaster menggunakan metode standar (contohnya gethostbyname). Jalankan aplikasi curl lain untuk melakukan pengujian ini:
kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm
Waiting for pod default/curl-131556218-9fnch to be running, status is Pending, pod ready: false
Hit enter for command prompt
Lalu tekan enter dan jalankan nslookup my-nginx
:
[ root@curl-131556218-9fnch:/ ]$ nslookup my-nginx
Server: 10.0.0.10
Address 1: 10.0.0.10
Name: my-nginx
Address 1: 10.0.162.149
Mengamankan Service
Hingga sekarang kita hanya mengakses nginx server dari dalam klaster. Sebelum mengekspos Service ke internet, kamu harus memastikan bahwa kanal komunikasi aman. Untuk melakukan hal tersebut, kamu membutuhkan:
- Self signed certificates untuk https (kecuali jika kamu sudah mempunyai identity certificate)
- Sebuah server nginx yang terkonfigurasi untuk menggunakan certificate tersebut
- Sebuah secret yang membuat setifikat tersebut dapat diakses oleh pod
Kamu dapat melihat semua itu di contoh nginx https. Contoh ini mengaharuskan kamu melakukan instalasi go dan make. Jika kamu tidak ingin melakukan instalasi tersebut, ikuti langkah-langkah manualnya nanti, singkatnya:
make keys KEY=/tmp/nginx.key CERT=/tmp/nginx.crt
kubectl create secret tls nginxsecret --key /tmp/nginx.key --cert /tmp/nginx.crt
secret/nginxsecret created
kubectl get secrets
NAME TYPE DATA AGE
default-token-il9rc kubernetes.io/service-account-token 1 1d
nginxsecret Opaque 2 1m
Berikut ini adalah langkah-langkah manual yang harus diikuti jika kamu mengalami masalah menjalankan make (pada windows contohnya):
#membuat sebuah key-pair public private
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /d/tmp/nginx.key -out /d/tmp/nginx.crt -subj "/CN=my-nginx/O=my-nginx"
#rubah key tersebut ke dalam pengkodean base64
cat /d/tmp/nginx.crt | base64
cat /d/tmp/nginx.key | base64
Gunakan hasil keluaran dari perintah sebelumnya untuk membuat sebuah file yaml seperti berikut. Nilai yang dikodekan base64 harus berada di dalam satu baris.
apiVersion: "v1"
kind: "Secret"
metadata:
name: "nginxsecret"
namespace: "default"
data:
nginx.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURIekNDQWdlZ0F3SUJBZ0lKQUp5M3lQK0pzMlpJTUEwR0NTcUdTSWIzRFFFQkJRVUFNQ1l4RVRBUEJnTlYKQkFNVENHNW5hVzU0YzNaak1SRXdEd1lEVlFRS0V3aHVaMmx1ZUhOMll6QWVGdzB4TnpFd01qWXdOekEzTVRKYQpGdzB4T0RFd01qWXdOekEzTVRKYU1DWXhFVEFQQmdOVkJBTVRDRzVuYVc1NGMzWmpNUkV3RHdZRFZRUUtFd2h1CloybHVlSE4yWXpDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBSjFxSU1SOVdWM0IKMlZIQlRMRmtobDRONXljMEJxYUhIQktMSnJMcy8vdzZhU3hRS29GbHlJSU94NGUrMlN5ajBFcndCLzlYTnBwbQppeW1CL3JkRldkOXg5UWhBQUxCZkVaTmNiV3NsTVFVcnhBZW50VWt1dk1vLzgvMHRpbGhjc3paenJEYVJ4NEo5Ci82UVRtVVI3a0ZTWUpOWTVQZkR3cGc3dlVvaDZmZ1Voam92VG42eHNVR0M2QURVODBpNXFlZWhNeVI1N2lmU2YKNHZpaXdIY3hnL3lZR1JBRS9mRTRqakxCdmdONjc2SU90S01rZXV3R0ljNDFhd05tNnNTSzRqYUNGeGpYSnZaZQp2by9kTlEybHhHWCtKT2l3SEhXbXNhdGp4WTRaNVk3R1ZoK0QrWnYvcW1mMFgvbVY0Rmo1NzV3ajFMWVBocWtsCmdhSXZYRyt4U1FVQ0F3RUFBYU5RTUU0d0hRWURWUjBPQkJZRUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjcKTUI4R0ExVWRJd1FZTUJhQUZPNG9OWkI3YXc1OUlsYkROMzhIYkduYnhFVjdNQXdHQTFVZEV3UUZNQU1CQWY4dwpEUVlKS29aSWh2Y05BUUVGQlFBRGdnRUJBRVhTMW9FU0lFaXdyMDhWcVA0K2NwTHI3TW5FMTducDBvMm14alFvCjRGb0RvRjdRZnZqeE04Tzd2TjB0clcxb2pGSW0vWDE4ZnZaL3k4ZzVaWG40Vm8zc3hKVmRBcStNZC9jTStzUGEKNmJjTkNUekZqeFpUV0UrKzE5NS9zb2dmOUZ3VDVDK3U2Q3B5N0M3MTZvUXRUakViV05VdEt4cXI0Nk1OZWNCMApwRFhWZmdWQTRadkR4NFo3S2RiZDY5eXM3OVFHYmg5ZW1PZ05NZFlsSUswSGt0ejF5WU4vbVpmK3FqTkJqbWZjCkNnMnlwbGQ0Wi8rUUNQZjl3SkoybFIrY2FnT0R4elBWcGxNSEcybzgvTHFDdnh6elZPUDUxeXdLZEtxaUMwSVEKQ0I5T2wwWW5scE9UNEh1b2hSUzBPOStlMm9KdFZsNUIyczRpbDlhZ3RTVXFxUlU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K"
nginx.key: "LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ2RhaURFZlZsZHdkbFIKd1V5eFpJWmVEZWNuTkFhbWh4d1NpeWF5N1AvOE9ta3NVQ3FCWmNpQ0RzZUh2dGtzbzlCSzhBZi9WemFhWm9zcApnZjYzUlZuZmNmVUlRQUN3WHhHVFhHMXJKVEVGSzhRSHA3VkpMcnpLUC9QOUxZcFlYTE0yYzZ3MmtjZUNmZitrCkU1bEVlNUJVbUNUV09UM3c4S1lPNzFLSWVuNEZJWTZMMDUrc2JGQmd1Z0ExUE5JdWFubm9UTWtlZTRuMG4rTDQKb3NCM01ZUDhtQmtRQlAzeE9JNHl3YjREZXUraURyU2pKSHJzQmlIT05Xc0RadXJFaXVJMmdoY1kxeWIyWHI2UAozVFVOcGNSbC9pVG9zQngxcHJHclk4V09HZVdPeGxZZmcvbWIvNnBuOUYvNWxlQlkrZStjSTlTMkQ0YXBKWUdpCkwxeHZzVWtGQWdNQkFBRUNnZ0VBZFhCK0xkbk8ySElOTGo5bWRsb25IUGlHWWVzZ294RGQwci9hQ1Zkank4dlEKTjIwL3FQWkUxek1yall6Ry9kVGhTMmMwc0QxaTBXSjdwR1lGb0xtdXlWTjltY0FXUTM5SjM0VHZaU2FFSWZWNgo5TE1jUHhNTmFsNjRLMFRVbUFQZytGam9QSFlhUUxLOERLOUtnNXNrSE5pOWNzMlY5ckd6VWlVZWtBL0RBUlBTClI3L2ZjUFBacDRuRWVBZmI3WTk1R1llb1p5V21SU3VKdlNyblBESGtUdW1vVlVWdkxMRHRzaG9reUxiTWVtN3oKMmJzVmpwSW1GTHJqbGtmQXlpNHg0WjJrV3YyMFRrdWtsZU1jaVlMbjk4QWxiRi9DSmRLM3QraTRoMTVlR2ZQegpoTnh3bk9QdlVTaDR2Q0o3c2Q5TmtEUGJvS2JneVVHOXBYamZhRGR2UVFLQmdRRFFLM01nUkhkQ1pKNVFqZWFKClFGdXF4cHdnNzhZTjQyL1NwenlUYmtGcVFoQWtyczJxWGx1MDZBRzhrZzIzQkswaHkzaE9zSGgxcXRVK3NHZVAKOWRERHBsUWV0ODZsY2FlR3hoc0V0L1R6cEdtNGFKSm5oNzVVaTVGZk9QTDhPTm1FZ3MxMVRhUldhNzZxelRyMgphRlpjQ2pWV1g0YnRSTHVwSkgrMjZnY0FhUUtCZ1FEQmxVSUUzTnNVOFBBZEYvL25sQVB5VWs1T3lDdWc3dmVyClUycXlrdXFzYnBkSi9hODViT1JhM05IVmpVM25uRGpHVHBWaE9JeXg5TEFrc2RwZEFjVmxvcG9HODhXYk9lMTAKMUdqbnkySmdDK3JVWUZiRGtpUGx1K09IYnRnOXFYcGJMSHBzUVpsMGhucDBYSFNYVm9CMUliQndnMGEyOFVadApCbFBtWmc2d1BRS0JnRHVIUVV2SDZHYTNDVUsxNFdmOFhIcFFnMU16M2VvWTBPQm5iSDRvZUZKZmcraEppSXlnCm9RN3hqWldVR3BIc3AyblRtcHErQWlSNzdyRVhsdlhtOElVU2FsbkNiRGlKY01Pc29RdFBZNS9NczJMRm5LQTQKaENmL0pWb2FtZm1nZEN0ZGtFMXNINE9MR2lJVHdEbTRpb0dWZGIwMllnbzFyb2htNUpLMUI3MkpBb0dBUW01UQpHNDhXOTVhL0w1eSt5dCsyZ3YvUHM2VnBvMjZlTzRNQ3lJazJVem9ZWE9IYnNkODJkaC8xT2sybGdHZlI2K3VuCnc1YytZUXRSTHlhQmd3MUtpbGhFZDBKTWU3cGpUSVpnQWJ0LzVPbnlDak9OVXN2aDJjS2lrQ1Z2dTZsZlBjNkQKckliT2ZIaHhxV0RZK2Q1TGN1YSt2NzJ0RkxhenJsSlBsRzlOZHhrQ2dZRUF5elIzT3UyMDNRVVV6bUlCRkwzZAp4Wm5XZ0JLSEo3TnNxcGFWb2RjL0d5aGVycjFDZzE2MmJaSjJDV2RsZkI0VEdtUjZZdmxTZEFOOFRwUWhFbUtKCnFBLzVzdHdxNWd0WGVLOVJmMWxXK29xNThRNTBxMmk1NVdUTThoSDZhTjlaMTltZ0FGdE5VdGNqQUx2dFYxdEYKWSs4WFJkSHJaRnBIWll2NWkwVW1VbGc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K"
Sekarang buat secrets menggunakan file tersebut:
kubectl apply -f nginxsecrets.yaml
kubectl get secrets
NAME TYPE DATA AGE
default-token-il9rc kubernetes.io/service-account-token 1 1d
nginxsecret Opaque 2 1m
Sekarang modifikasi replika nginx untuk menjalankan server https menggunakan certificate di dalam secret dan Service untuk mengekspos semua port (80 dan 443):
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: NodePort
ports:
- port: 8080
targetPort: 80
protocol: TCP
name: http
- port: 443
protocol: TCP
name: https
selector:
run: my-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 1
template:
metadata:
labels:
run: my-nginx
spec:
volumes:
- name: secret-volume
secret:
secretName: nginxsecret
containers:
- name: nginxhttps
image: bprashanth/nginxhttps:1.0
ports:
- containerPort: 443
- containerPort: 80
volumeMounts:
- mountPath: /etc/nginx/ssl
name: secret-volume
Berikut catatan penting tentang manifes nginx-secure-app:
- di dalam file tersebut, ada spesifikasi Deployment dan Service
- ada server nginx yang melayani trafik HTTP di port 80 dan trafik HTTPS di port 443, dan Service nginx yang mengekspos kedua port tersebut.
- Setiap kontainer mempunyai akses ke key melalui volume yang di mount pada
/etc/nginx/ssl
. Ini adalah konfigurasi sebelum server nginx dijalankan.
kubectl delete deployments,svc my-nginx; kubectl create -f ./nginx-secure-app.yaml
Pada tahapan ini, kamu dapat berkomunikasi dengan server nginx dari node manapun.
kubectl get pods -l run=my-nginx -o custom-columns=POD_IP:.status.podIPs
POD_IP
[map[ip:10.244.3.5]]
node $ curl -k https://10.244.3.5
...
<h1>Welcome to nginx!</h1>
Perlu dicatat bahwa kita menggunakan parameter -k
saat menggunakan curl, ini karena kita tidak tau apapun tentang Pod yang menjalankan nginx saat pembuatan seritifikat, jadi kita harus memberitahu curl untuk mengabaikan ketidakcocokan CName. Dengan membuat Service, kita menghubungkan CName yang digunakan pada certificate dengan nama pada DNS yang digunakan Pod. Lakukan pengujian dari sebuah Pod (secret yang sama digunakan untuk agar mudah, Pod tersebut hanya membutuhkan nginx.crt untuk mengakses Service)
apiVersion: apps/v1
kind: Deployment
metadata:
name: curl-deployment
spec:
selector:
matchLabels:
app: curlpod
replicas: 1
template:
metadata:
labels:
app: curlpod
spec:
volumes:
- name: secret-volume
secret:
secretName: nginxsecret
containers:
- name: curlpod
command:
- sh
- -c
- while true; do sleep 1; done
image: radial/busyboxplus:curl
volumeMounts:
- mountPath: /etc/nginx/ssl
name: secret-volume
kubectl apply -f ./curlpod.yaml
kubectl get pods -l app=curlpod
NAME READY STATUS RESTARTS AGE
curl-deployment-1515033274-1410r 1/1 Running 0 1m
kubectl exec curl-deployment-1515033274-1410r -- curl https://my-nginx --cacert /etc/nginx/ssl/nginx.crt
...
<title>Welcome to nginx!</title>
...
Mengekspos Service
Kamu mungkin ingin mengekspos Service ke alamat IP eksternal. Kubernetes mendukung dua cara untuk melakukan ini: NodePort dan LoadBalancer. Service yang dibuat tadi sudah menggunakan NodePort
, jadi replika nginx sudah siap untuk menerima trafik dari internet jika Node kamu mempunyai IP publik.
kubectl get svc my-nginx -o yaml | grep nodePort -C 5
uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
clusterIP: 10.0.162.149
ports:
- name: http
nodePort: 31704
port: 8080
protocol: TCP
targetPort: 80
- name: https
nodePort: 32453
port: 443
protocol: TCP
targetPort: 443
selector:
run: my-nginx
kubectl get nodes -o yaml | grep ExternalIP -C 1
- address: 104.197.41.11
type: ExternalIP
allocatable:
--
- address: 23.251.152.56
type: ExternalIP
allocatable:
...
$ curl https://<EXTERNAL-IP>:<NODE-PORT> -k
...
<h1>Welcome to nginx!</h1>
Mari coba membuat ulang Service menggunakan cloud load balancer, ubah saja type
Service my-nginx
dari NodePort
ke LoadBalancer
:
kubectl edit svc my-nginx
kubectl get svc my-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-nginx ClusterIP 10.0.162.149 162.222.184.144 80/TCP,81/TCP,82/TCP 21s
curl https://<EXTERNAL-IP> -k
...
<title>Welcome to nginx!</title>
IP address pada kolom EXTERNAL-IP
menunjukan IP yang tersedia di internet. Sedangkan kolom CLUSTER-IP
merupakan IP yang hanya tersedia di dalam klaster kamu (IP private).
Perhatikan pada AWS, tipe LoadBalancer
membuat sebuah ELB, yang menggunakan hostname yang panjang, bukan IP. Karena tidak semua keluar pada standar keluaran kubectl get svc
. Jadi kamu harus menggunakan kubectl describe service my-nginx
untuk melihatnya. Kamu akan melihat seperti ini:
kubectl describe service my-nginx
...
LoadBalancer Ingress: a320587ffd19711e5a37606cf4a74574-1142138393.us-east-1.elb.amazonaws.com
...
Selanjutnya
Kubernetes juga mendukung Federated Service, yang bisa mempengaruhi banyak klaster dan penyedia layanan cloud, untuk meningkatkan ketersediaan, peningkatan toleransi kesalahan, dan pengembangan dari Service kamu. Lihat Panduan Federated Service untuk informasi lebih lanjut.
6 - Ingress
Sebuah obyek API yang mengatur akses eksternal terhadap Service yang ada di dalam klaster, biasanya dalam bentuk request HTTP.
Ingress juga menyediakan load balancing, terminasi SSL, serta name-based virtual hosting.
Terminologi
Untuk memudahkan, di awal akan dijelaskan beberapa terminologi yang sering dipakai:
- Node: Sebuah mesin fisik atau virtual yang berada di dalam klaster Kubernetes.
- Klaster: Sekelompok node yang merupakan resource komputasi primer yang diatur oleh Kubernetes, biasanya diproteksi dari internet dengan menggunakan firewall.
- Edge router: Sebuah router mengatur policy firewall pada klaster kamu. Router ini bisa saja berupa gateway yang diatur oleh penyedia layanan cloud maupun perangkat keras.
- Jaringan klaster: Seperangkat links baik logis maupus fisik, yang memfasilitasi komunikasi di dalam klaster berdasarkan model jaringan Kubernetes.
- Service: Sebuah Service yang mengidentifikasi beberapa Pod dengan menggunakan selector label. Secara umum, semua Service diasumsikan hanya memiliki IP virtual yang hanya dapat diakses dari dalam jaringan klaster.
Apakah Ingress itu?
Ingress ditambahkan sejak Kubernetes v1.1, mengekspos rute HTTP dan HTTPS ke berbagai services di dalam klaster. Mekanisme routing trafik dikendalikan oleh aturan-aturan yang didefinisikan pada Ingress.
internet
|
[ Ingress ]
--|-----|--
[ Services ]
Sebuah Ingress dapat dikonfigurasi agar berbagai Service memiliki URL yang dapat diakses dari eksternal (luar klaster), melakukan load balance pada trafik, terminasi SSL, serta Virtual Host berbasis Nama. Sebuah kontroler Ingress bertanggung jawab untuk menjalankan fungsi Ingress yaitu sebagai loadbalancer, meskipun dapat juga digunakan untuk mengatur edge router atau frontend tambahan untuk menerima trafik.
Sebuah Ingress tidak mengekspos sembarang port atau protokol. Mengekspos Service untuk protokol selain HTTP ke HTTPS internet biasanya dilakukan dengan menggunakan service dengan tipe Service.Type=NodePort atau Service.Type=LoadBalancer.
Prasyarat
Kubernetes v1.1 [beta]
Sebelum kamu mulai menggunakan Ingress, ada beberapa hal yang perlu kamu ketahui sebelumnya. Ingress merupakan resource dengan tipe beta.
GCE/Google Kubernetes Engine melakukan deploy kontroler Ingress pada master. Perhatikan laman berikut keterbatasan versi beta kontroler ini jika kamu menggunakan GCE/GKE.
Jika kamu menggunakan environment selain GCE/Google Kubernetes Engine, kemungkinan besar kamu harus melakukan proses deploy kontroler ingress kamu sendiri. Terdapat beberapa jenis kontroler Ingress yang bisa kamu pilih.
Sebelum kamu memulai
Secara ideal, semua kontroler Ingress harus memenuhi spesifikasi ini, tetapi beberapa kontroler beroperasi sedikit berbeda satu sama lain.
Resource Ingress
Berikut ini merupakan salah satu contoh konfigurasi Ingress yang minimum:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
backend:
serviceName: test
servicePort: 80
Seperti layaknya resource Kubernetes yang lain, sebuah Ingress membutuhkan field apiVersion
, kind
, dan metadata
.
Untuk informasi umum soal bagaimana cara bekerja dengan menggunakan berkas konfigurasi, silahkan merujuk pada melakukan deploy aplikasi, konfigurasi kontainer, mengatur resource.
Ingress seringkali menggunakan anotasi untuk melakukan konfigurasi beberapa opsi yang ada bergantung pada kontroler Ingress yang digunakan, sebagai contohnya
adalah anotasi rewrite-target.
Kontroler Ingress yang berbeda memiliki jenis anotasi yang berbeda. Pastikan kamu sudah terlebih dahulu memahami dokumentasi
kontroler Ingress yang akan kamu pakai untuk mengetahui jenis anotasi apa sajakah yang disediakan.
Spesifikasi Ingress memiliki segala informasi yang dibutuhkan untuk melakukan proses konfigurasi loadbalancer atau server proxy. Hal yang terpenting adalah bagian inilah yang mengandung semua rules yang nantinya akan digunakan untuk menyesuaikan trafik yang masuk. Resource Ingress hanya menyediakan fitur rules untuk mengarahkan trafik dengan protokol HTTP.
Rule Ingress
Setiap rule HTTP mengandung informasi berikut:
- Host opsional. Di dalam contoh ini, tidak ada host yang diberikan, dengan kata lain, semua rules berlaku untuk inbound trafik HTTP bagi alamat IP yang dispesifikasikan. JIka sebuah host dispesifikasikan (misalnya saja, foo.bar.com), maka rules yang ada akan berlaku bagi host tersebut.
- Sederetan path (misalnya, /testpath), setiap path ini akan memiliki pasangan berupa sebuah backend yang didefinisikan dengan
serviceName
danservicePort
. Baik host dan path harus sesuai dengan konten dari request yang masuk sebelum loadbalancer akan mengarahkan trafik pada service yang sesuai. - Suatu backend adalah kombinasi service dan port seperti yang dideskripsikan di dokumentasi Service. Request HTTP (dan HTTPS) yang sesuai dengan host dan path yang ada pada rule akan diteruskan pada backend terkait.
Backend default seringkali dikonfigurasi pada kontroler kontroler Ingress, tugas backend default ini adalah mengarahkan request yang tidak sesuai dengan path yang tersedia pada spesifikasi.
Backend Default
Sebuah Ingress yang tidak memiliki rules akan mengarahkan semua trafik pada sebuah backend default. Backend default inilah yang biasanya bisa dimasukkan sebagai salah satu opsi konfigurasi dari kontroler Ingress dan tidak dimasukkan dalam spesifikasi resource Ingress.
Jika tidak ada host atau path yang sesuai dengan request HTTP pada objek Ingress, maka trafik tersebut akan diarahkan pada backend default.
Jenis Ingress
Ingress dengan satu Service
Terdapat konsep Kubernetes yang memungkinkan kamu untuk mengekspos sebuah Service, lihat alternatif lain. Kamu juga bisa membuat spesifikasi Ingress dengan backend default yang tidak memiliki rules.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: test-ingress
spec:
backend:
serviceName: testsvc
servicePort: 80
Jika kamu menggunakan kubectl apply -f
kamu dapat melihat:
kubectl get ingress test-ingress
NAME HOSTS ADDRESS PORTS AGE
test-ingress * 107.178.254.228 80 59s
Dimana 107.178.254.228
merupakan alamat IP yang dialokasikan oleh kontroler Ingress untuk
memenuhi Ingress ini.
ADDRESS
sebagai <pending>
.
Fanout sederhana
Sebuah konfigurasi fanout akan melakukan route trafik dari sebuah alamat IP ke banyak Service, berdasarkan URI HTTP yang diberikan. Sebuah Ingress memungkinkan kamu untuk memiliki jumlah loadbalancer minimum. Contohnya, konfigurasi seperti di bawah ini:
foo.bar.com -> 178.91.123.132 -> / foo service1:4200
/ bar service2:8080
akan memerlukan konfigurasi Ingress seperti:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: simple-fanout-example
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: foo.bar.com
http:
paths:
- path: /foo
backend:
serviceName: service1
servicePort: 4200
- path: /bar
backend:
serviceName: service2
servicePort: 8080
Ketika kamu membuat Ingress dengan perintah kubectl apply -f
:
kubectl describe ingress simple-fanout-example
Name: simple-fanout-example
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo service1:4200 (10.8.0.90:4200)
/bar service2:8080 (10.8.0.91:8080)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 22s loadbalancer-controller default/test
Kontroler Ingress akan menyediakan loadbalancer (implementasinya tergantung dari jenis Ingress yang digunakan), selama service-service yang didefinisikan (s1
, s2
) ada.
Apabila Ingress selesai dibuat, maka kamu dapat melihat alamat IP dari berbagai loadbalancer
pada kolom address
.
Virtual Host berbasis Nama
Virtual Host berbasis Nama memungkinkan mekanisme routing berdasarkan trafik HTTP ke beberapa host name dengan alamat IP yang sama.
foo.bar.com --| |-> foo.bar.com s1:80
| 178.91.123.132 |
bar.foo.com --| |-> bar.foo.com s2:80
Ingress di bawah ini memberikan perintah pada loadbalancer untuk melakukan mekanisme routing berdasarkan header host.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: service1
servicePort: 80
- host: bar.foo.com
http:
paths:
- backend:
serviceName: service2
servicePort: 80
Jika kamu membuat sebuah Ingress tanpa mendefinisikan host apa pun, maka
trafik web ke alamat IP dari kontroler Ingress tetap dapat dilakukan tanpa harus
menyesuaikan aturan name based virtual host. Sebagai contoh,
resource Ingress di bawah ini akan melakukan pemetaan trafik
dari first.bar.com
ke service1
, second.foo.com
ke service2
, dan trafik lain
ke alamat IP tanpa host name yang didefinisikan di dalam request (yang tidak memiliki request header) ke service3
.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: name-virtual-host-ingress
spec:
rules:
- host: first.bar.com
http:
paths:
- backend:
serviceName: service1
servicePort: 80
- host: second.foo.com
http:
paths:
- backend:
serviceName: service2
servicePort: 80
- http:
paths:
- backend:
serviceName: service3
servicePort: 80
TLS
Kamu dapat mengamankan Ingress yang kamu miliki dengan memberikan spesifikasi secret
yang mengandung private key dan sertifikat TLS. Saat ini, Ingress hanya
memiliki fitur untuk melakukan konfigurasi single TLS port, yaitu 443, serta melakukan terminasi TLS.
Jika section TLS pada Ingress memiliki spesifikasi host yang berbeda,
rules yang ada akan dimultiplekskan pada port yang sama berdasarkan
hostname yang dispesifikasikan melalui ekstensi TLS SNI. Secret TLS harus memiliki
key
bernama tls.crt
dan tls.key
yang mengandung private key dan sertifikat TLS, contohnya:
apiVersion: v1
data:
tls.crt: base64 encoded cert
tls.key: base64 encoded key
kind: Secret
metadata:
name: testsecret-tls
namespace: default
type: kubernetes.io/tls
Ketika kamu menambahkan secret pada Ingress maka kontroler Ingress akan memberikan perintah untuk
memproteksi channel dari klien ke loadbalancer menggunakan TLS.
Kamu harus memastikan secret TLS yang digunakan memiliki sertifikat yang mengandung
CN untuk sslexample.foo.com
.
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: tls-example-ingress
spec:
tls:
- hosts:
- sslexample.foo.com
secretName: testsecret-tls
rules:
- host: sslexample.foo.com
http:
paths:
- path: /
backend:
serviceName: service1
servicePort: 80
Loadbalancing
Sebuah kontroler Ingress sudah dibekali dengan beberapa policy terkait mekanisme load balance yang nantinya akan diterapkan pada semua Ingress, misalnya saja algoritma load balancing, backend weight scheme, dan lain sebagainya. Beberapa konsep load balance yang lebih advance (misalnya saja persistent sessions, dynamic weights) belum diekspos melalui Ingress. Meskipun begitu, kamu masih bisa menggunakan fitur ini melalui loadbalancer service.
Perlu diketahui bahwa meskipun health check tidak diekspos secara langsung melalui Ingress, terdapat beberapa konsep di Kubernetes yang sejalan dengan hal ini, misalnya readiness probes yang memungkinkan kamu untuk memperoleh hasil yang sama. Silahkan pelajari lebih lanjut dokumentasi kontroler yang kamu pakai untuk mengetahui bagaimana implementasi health checks pada kontroler yang kamu pilih (nginx, GCE).
Mengubah Ingress
Untuk mengubah Ingress yang sudah ada dan menambahkan host baru, kamu dapat mengubahnya dengan mode edit:
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo s1:80 (10.8.0.90:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 35s loadbalancer-controller default/test
kubectl edit ingress test
Sebuah editor akan muncul dan menampilkan konfigurasi Ingress kamu dalam format YAML apabila kamu telah menjalankan perintah di atas. Ubah untuk menambahkan host:
spec:
rules:
- host: foo.bar.com
http:
paths:
- backend:
serviceName: s1
servicePort: 80
path: /foo
- host: bar.baz.com
http:
paths:
- backend:
serviceName: s2
servicePort: 80
path: /foo
..
Menyimpan konfigurasi dalam bentuk YAML ini akan mengubah resource pada API server, yang kemudian akan memberi tahu kontroler Ingress untuk mengubah konfigurasi loadbalancer.
kubectl describe ingress test
Name: test
Namespace: default
Address: 178.91.123.132
Default backend: default-http-backend:80 (10.8.2.3:8080)
Rules:
Host Path Backends
---- ---- --------
foo.bar.com
/foo s1:80 (10.8.0.90:80)
bar.baz.com
/foo s2:80 (10.8.0.91:80)
Annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ADD 45s loadbalancer-controller default/test
Kamu juga dapat mengubah Ingress dengan menggunakan perintah kubectl replace -f
pada berkas konfigurasi
Ingress yang ingin diubah.
Mekanisme failing pada beberapa zona availability
Teknik untuk menyeimbangkan persebaran trafik pada failure domain berbeda antar penyedia layanan cloud. Kamu dapat mempelajari dokumentasi yang relevan bagi kontoler Ingress untuk informasi yang lebih detail. Kamu juga dapat mempelajari dokumentasi federasi untuk informasi lebih detail soal bagaimana melakukan deploy untuk federasi klaster.
Pengembangan selanjutnya
Silahkan amati SIG Network untuk detail lebih lanjut mengenai perubahan Ingress dan resource terkait lainnya. Kamu juga bisa melihat repositori Ingress untuk informasi yang lebih detail soal perubahan berbagai kontroler.
Alternatif lain
Kamu dapat mengekspos sebuah Service dalam berbagai cara, tanpa harus menggunakan resource Ingress, dengan menggunakan:
Selanjutnya
7 - Kontroler Ingress
Agar Ingress dapat bekerja sebagaimana mestinya, sebuah klaster harus memiliki paling tidak sebuah kontroler Ingress.
Berbeda dengan kontroler-kontroler lainnya yang dijalankan
sebagai bagian dari binary kube-controller-manager
, kontroler Ingress
tidak secara otomatis dijalankan di dalam klaster. Kamu bisa menggunakan
laman ini untuk memilih implementasi kontroler Ingress yang kamu pikir
paling sesuai dengan kebutuhan kamu.
Kubernetes sebagai sebuah proyek, saat ini, mendukung dan memaintain kontroler-kontroler GCE dan nginx.
Kontroler-kontroler lainnya
- Ambassador API Gateway merupakan ingress berbasis Envoy kontroler dengan dukungan komunitas atau komersial dari Datawire.
- AppsCode Inc. menawarkan dukungan dan pemeliharaan untuk ingress berbasis HAProxy, Voyager.
- Contour merupakan ingress berbasis Envoy yang disediakan dan didukung oleh VMware.
- Citrix menyediakan sebuah kontroler Ingress untuk perangkat keras (MPX), virtualisasi (VPX) dan kontainerisasi cuma-cuma (CPX) ADC untuk mesin baremetal dan penyedia layanan cloud deployments.
- F5 Networks menyediakan dukungan dan pemeliharaan untuk kontroler F5 BIG-IP bagi Kubernetes.
- Gloo adalah sebuah proyek kontroler Ingress open source berbasis Envoy yang menawarkan fungsionalitas API Gateway dengan dukungan enterprise dari solo.io.
- Kontroler Ingress berbasis HAProxy jcmoraisjr/haproxy-ingress yang disebutkan di dalam artikel HAProxy Ingress Controller for Kubernetes. HAProxy Technologies menawarkan dukungan dan pemeliharaan bagi HAProxy Enterprise dan Ingress kontroler jcmoraisjr/haproxy-ingress.
- Kontroler Ingress berbasis Istio Control Ingress Traffic.
- Kong menawarkan dukungan dan pemeliharaan komunitas atau komersial Kontroler Ingress untuk Kubernetes.
- NGINX, Inc. menawarkan dukungan dan pemeliharaan Kontroler Ingress NGINX untuk Kubernetes.
- Traefik adalah sebuah kontroler Ingress yang menyediakan semua fitur secara lengkap (fully featured) (Let's Encrypt, secrets, http2, websocket), dengan tambahan dukungan komersial oleh Containous.
Menggunakan beberapa jenis kontroler Ingress sekaligus
Kamu dapat melakukan deploy berapa pun banyaknya kontroler Ingress
dalam sebuah klaster. Jika kamu ingin membuat Ingress, kamu tinggal memberikan anotasi setiap Ingress sesuai dengan
ingress.class
yang sesuai untuk menandai kontroler Ingress mana yang digunakan jika terdapat lebih dari satu kontroler Ingress yang ada di
klaster kamu.
Apabila kamu tidak mendefinisikan class
yang dipakai, penyedia layanan cloud kamu akan menggunakan kontroler Ingress default yang mereka miliki.
Idealnya, semua ingress harus memenuhi spesifikasi ini, tetapi berbagai jenis kontroler Ingress bisa saja memiliki sedikit perbedaan cara kerja.
Selanjutnya
- Pelajari Ingress lebih lanjut.
- Melakukan konfigurasi Ingress pada Minikube dengan kontroler NGINX
8 - NetworkPolicy
Sebuah NetworkPolicy adalah spesifikasi dari sekelompok Pod atau endpoint yang diizinkan untuk saling berkomunikasi.
NetworkPolicy
menggunakan label untuk memilih Pod serta mendefinisikan serangkaian rule yang digunakan
untuk mendefinisikan trafik yang diizinkan untuk suatu Pod tertentu.
Prasyarat
NetworkPolicy diimplementasikan dengan menggunakan plugin jaringan,
dengan demikian kamu harus memiliki penyedia jaringan yang mendukung NetworkPolicy
-
membuat resource tanpa adanya controller tidak akan berdampak apa pun.
Pod yang terisolasi dan tidak terisolasi
Secara default, Pod bersifat tidak terisolasi; Pod-Pod tersebut menerima trafik dari resource apa pun.
Pod menjadi terisolasi apabila terdapat NetworkPolicy
yang dikenakan pada Pod-Pod tersebut.
Apabila terdapat NetworkPolicy
di dalam namespace yang dikenakan pada suatu Pod, Pod tersebut
akan menolak koneksi yang tidak diizinkan NetworkPolicy
. (Pod lain dalam namespace
yang tidak dikenakan NetworkPolicy
akan tetap menerima trafik dari semua resource.)
Resource NetworkPolicy
Lihat NetworkPolicy
untuk definisi lengkap resource.
Sebuah contoh NetworkPolicy
akan terlihat seperti berikut:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
Mengirimkan ini ke API server dengan metode POST tidak akan berdampak apa pun kecuali penyedia jaringan mendukung network policy.
Field-field yang bersifat wajib: Sama dengan seluruh config Kubernetes lainnya, sebuah NetworkPolicy
membutuhkan field-field apiVersion
, kind
, dan metadata
. Informasi generik mengenai
bagaimana bekerja dengan file config
, dapat dilihat di
Konfigurasi Kontainer menggunakan ConfigMap
,
serta Manajemen Objek.
spec: NetworkPolicy
spec memiliki semua informasi yang harus diberikan untuk memberikan definisi network policy yang ada pada namespace tertentu.
podSelector: Setiap NetworkPolicy
memiliki sebuah podSelector
yang bertugas memfilter Pod-Pod yang dikenai policy tersebut. Contoh yang ada memfilter Pod dengan label "role=db"
. Sebuah podSelector
yang empty akan memilih semua Pod yang ada di dalam namespace.
policyTypes: Setiap NetworkPolicy
memiliki sebuah daftar policyTypes
yang dapat berupa Ingress
, Egress
, atau keduanya. Field policyTypes
mengindikasikan apakah suatu policy diberikan pada trafik ingress, egress, atau camputan ingress dan egress pada Pod tertentu. Jika tidak ada policyTypes
tyang diberikan pada NetworkPolicy
maka Ingress
default akan diterapkan dan Egress
akan diterapkan apabila policy tersebut memberikan spesifikasi egress.
ingress: Setiap NetworkPolicy
bisa saja memberikan serangkaian whitelist rule-rule ingress
. Setiap rule mengizinkan trafik yang sesuai dengan section from
dan ports
. Contoh policy yang diberikan memiliki sebuah rule, yang sesuai dengan trafik pada sebuah port
single, bagian pertama dispesifikasikan melalui ipBlock
, yang kedua melalui namespaceSelector
dan yang ketiga melalui podSelector
.
egress: Setiap NetworkPolicy
bisa saja meliputi serangkaian whitelist rule-rule egress
. Setiap rule mengizinkan trafik yang sesuai dengan section to
dan ports
. Contoh policy yang diberikan memiliki sebuah rule, yang sesuai dengan port
single pada destinasi 10.0.0.0/24
.
Pada contoh, NetworkPolicy
melakukan hal berikut:
-
Mengisolasi Pod-Pod dengan label
"role=db"
pada namespace"default"
baik untukingress
atauegress
. -
(Rule
Ingress
) mengizinkan koneksi ke semua Pod pada namespace“default”
dengan label“role=db”
untuk protokol TCPport
6379
dari:- semua Pod pada namespace
"default"
dengan label"role=frontend"
- semua Pod dalam sebuah namespace dengan label
"project=myproject"
- alamat IP pada range
172.17.0.0–172.17.0.255
dan172.17.2.0–172.17.255.255
(yaitu, semua172.17.0.0/16
kecuali172.17.1.0/24
)
- semua Pod pada namespace
-
(Rule Egress) mengizinkan koneksi dari semua Pod pada namespace
"default"
dengan label"role=db"
ke CIDR10.0.0.0/24
untuk protokol TCP padaport
5978
Lihat mekanisme Deklarasi Network Policy untuk penjelasan lebih mendalam.
Perilaku selektor to
dan from
Terdapat empat jenis selektor yang dapat dispesifikasikan dalam section
ingress
from
atau section
egress
to
:
podSelector: Ini digunakan untuk memfilter Pod tertentu pada namespace dimana NetworkPolicy
berada yang akan mengatur destinasi ingress atau egress.
namespaceSelector: Ini digunakan untuk memfilter namespace tertentu dimana semua Pod diperbolehkan sebagai source ingress
atau destinasi egress
.
namespaceSelector and podSelector: Sebuah entri to
/from
yang memberikan spesifikasi namespaceSelector
dan podSelector
serta memilih Pod-Pod tertentu yang ada di dalam namespace. Pastikan kamu menggunakan sintaks YAML yang tepat; policy
ini:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
...
mengandung sebuah elemen from
yang mengizinkan koneksi dari Pod-Pod dengan label role=client
di namespace dengan label user=alice
. Akan tetapi, policy ini:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
...
mengandung dua elemen pada array from
, dan mengizinkan koneksi dari Pod pada Namespace lokal dengan label
role=client
, atau dari Pod di namespace apa pun dengan label user=alice
.
Ketika kamu merasa ragu, gunakan kubectl describe
untuk melihat bagaimana Kubernetes
menginterpretasikan policy tersebut.
ipBlock: Ini digunakan untuk memilih range IP CIDR tertentu untuk berperan sebagai source ingress atau destinasi egress. Alamat yang digunakan harus merupakan alamat IP eksternal klaster, karena alamat IP Pod bersifat ephemeral dan tidak dapat ditebak.
Mekanisme ingress dan egress klaster seringkali membutuhkan mekanisme rewrite alamat IP source dan destinasi
paket. Pada kasus-kasus dimana hal ini, tidak dapat dipastikan bahwa apakah hal ini
terjadi sebelum atau setelah pemrosesan NetworkPolicy
, dan perilaku yang ada mungkin saja berbeda
untuk kombinasi plugin jaringan, penyedia layanan cloud, serta implementasi Service
yang berbeda.
Pada ingress, artinya bisa saja kamu melakukan filter paket yang masuk berdasarkan source IP
,
sementara di kasus lain "source IP" yang digunakan oleh Network Policy adalah alamat IP LoadBalancer
,
node dimana Pod berada, dsb.
Pada egress, bisa saja sebuah koneksi dari Pod ke IP Service
di-rewrite ke IP eksternal klaster
atau bahkan tidak termasuk di dalam ipBlock
policy.
Policy Default
Secara default, jika tidak ada policy yang ada dalam suatu namespace, maka semua trafik ingress dan egress yang diizinkan ke atau dari Pod dalam namespace. Contoh di bawah ini akan memberikan gambaran bagaimana kamu dapat mengubah perilaku default pada sebuah namespace.
Default: tolak semua trafik ingress
Kamu dapat membuat policy isolasi "default"
untuk sebuah namespace
dengan membuat sebuah NetworkPolicy
yang memilih semua Pod tapi tidak mengizinkan
trafik ingress masuk ke Pod-Pod tersebut.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
Hal ini menjamin bahwa bahkan Pod yang tidak dipilih oleh NetworkPolicy
lain masih terisolasi.
Policy ini tidak mengubah perilaku default dari egress.
Default: izinkan semua trafik ingress
Jika kamu ingin mengizinkan semua trafik ingress pada semua Pod dalam sebuah namespace (bahkan jika policy ditambahkan dan menyebabkan beberapa Pod menjadi terisolasi), kamu dapat secara eksplisit mengizinkan semua trafik bagi namespace tersebut.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
Default: tolak semua trafik egress
Kamu dapat membuat policy isolasi "default"
untuk sebuah namespace
dengan membuat sebuah NetworkPolicy
yang memilih semua Pod tapi tidak mengizinkan
trafik egress keluar dari Pod-Pod tersebut.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Egress
Hal ini menjamin bahwa bahkan Pod yang tidak dipilih oleh NetworkPolicy
lain masih terisolasi.
Policy ini tidak mengubah perilaku default dari ingress.
Default: izinkan semua trafik egress
Jika kamu ingin mengizinkan semua trafik egress pada semua Pod dalam sebuah namespace (bahkan jika policy ditambahkan dan menyebabkan beberapa Pod menjadi terisolasi), kamu dapat secara eksplisit mengizinkan semua trafik bagi namespace tersebut.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
Default: tolak semua trafik ingress dan egress
Kamu dapat membuat sebuah policy "default" jika kamu ingin menolak semua trafik ingress maupun egress pada semua Pod dalam sebuah namespace.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Hal ini menjamin bahwa bahkan Pod yang tidak dipilih oleh NetworkPolicy
tidak akan mengizinkan trafik ingress atau egress.
Dukungan terhadap SCTP
Kubernetes v1.12 [alpha]
Kubernetes mendukung SCTP sebagai value protocol
pada definisi NetworkPolicy
sebagai fitur alpha. Untuk mengaktifkan fitur ini, administrator klaster harus mengaktifkan gerbang fitur SCTPSupport
pada apiserver
, contohnya “--feature-gates=SCTPSupport=true,...”
. Ketika gerbang fitur ini diaktifkan, pengguna dapat menerapkan value
dari field protocol
pada NetworkPolicy
menjadi SCTP
. Kubernetes akan mengatur jaringan sesuai dengan SCTP, seperti halnya koneksi TCP.
Plugin CNI harus mendukung SCTP sebagai value dari protocol
pada NetworkPolicy
.
Selanjutnya
- Lihat Deklarasi Network Policy untuk melihat lebih banyak contoh penggunaan.
- Baca lebih lanjut soal panduan bagi skenario generik resource
NetworkPolicy
.
9 - Menambahkan Entry pada /etc/hosts Pod dengan HostAliases
Menambahkan entri pada berkas /etc/hosts Pod akan melakukan override resolusi hostname pada level Pod ketika DNS dan opsi lainnya tidak tersedia. Pada versi 1.7, pengguna dapat menambahkan entri yang diinginkan beserta field HostAliases pada PodSpec.
Modifikasi yang dilakukan tanpa menggunakan HostAliases tidaklah disarankan karena berkas ini diatur oleh Kubelet dan dapat di-override ketika Pod dibuat/di-restart.
Isi Default pada Berkas Hosts
Misalnya saja kamu mempunyai sebuah Pod Nginx yang memiliki sebuah IP Pod:
kubectl run nginx --image nginx --generator=run-pod/v1
pod/nginx created
Perhatikan IP Pod tersebut:
kubectl get pods --output=wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx 1/1 Running 0 13s 10.200.0.4 worker0
File hosts
yang ada akan tampak sebagai berikut:
kubectl exec nginx -- cat /etc/hosts
# Berkas hosts yang dikelola Kubernetes
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.200.0.4 nginx
Secara default, berkas hosts
hanya berisikan boilerplate alamat IP IPv4 and IPv6 seperti
localhost
dan hostname dari Pod itu sendiri.
Menambahkan Entri Tambahan dengan HostAliases
Selain boilerplate default, kita dapat menambahkan entri pada berkas
hosts
untuk melakukan resolusi foo.local
, bar.local
pada 127.0.0.1
dan foo.remote
,
bar.remote
pada 10.1.2.3
, kita dapat melakukannya dengan cara menambahkan
HostAliases pada Pod di bawah field .spec.hostAliases
:
apiVersion: v1
kind: Pod
metadata:
name: hostaliases-pod
spec:
restartPolicy: Never
hostAliases:
- ip: "127.0.0.1"
hostnames:
- "foo.local"
- "bar.local"
- ip: "10.1.2.3"
hostnames:
- "foo.remote"
- "bar.remote"
containers:
- name: cat-hosts
image: busybox
command:
- cat
args:
- "/etc/hosts"
Pod ini kemudian dapat dihidupkan dengan perintah berikut:
kubectl apply -f hostaliases-pod.yaml
pod/hostaliases-pod created
Perhatikan IP dan status Pod tersebut:
kubectl get pod --output=wide
NAME READY STATUS RESTARTS AGE IP NODE
hostaliases-pod 0/1 Completed 0 6s 10.200.0.5 worker0
File hosts
yang ada akan tampak sebagai berikut:
kubectl logs hostaliases-pod
# Berkas hosts yang dikelola Kubernetes
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
fe00::0 ip6-mcastprefix
fe00::1 ip6-allnodes
fe00::2 ip6-allrouters
10.200.0.5 hostaliases-pod
# Entries added by HostAliases.
127.0.0.1 foo.local bar.local
10.1.2.3 foo.remote bar.remote
Dengan tambahan entri yang telah dispesifikasikan sebelumnya.
Kenapa Kubelet Melakukan Mekanisme Manajemen Berkas Hosts
?
Kubelet melakukan proses manajemen
berkas hosts
untuk setiap container yang ada pada Pod untuk mencegah Docker melakukan
modifikasi pada berkas tersebut
setelah kontainer dihidupkan.
Karena sifat dari berkas tersebut yang secara otomatis di-manage,
semua hal yang didefinisikan oleh pengguna akan ditimpa (overwrite) ketika berkas
hosts
di-mount kembali oleh Kubelet ketika ada kontainer yang di-restart
atau Pod di-schedule ulang. Dengan demikian tidak dianjurkan untuk
memodifikasi berkas tersebut secara langsung.
10 - Dual-stack IPv4/IPv6
Kubernetes v1.16 [alpha]
Dual-stack IPv4/IPv6 memungkinkan pengalokasian alamat IPv4 dan IPv6 untuk Pod dan Service.
Jika kamu mengaktifkan jaringan dual-stack IPv4/IPv6 untuk klaster Kubernetes kamu, klaster akan mendukung pengalokasian kedua alamat IPv4 dan IPv6 secara bersamaan.
Fitur-fitur yang didukung
Mengaktifkan dual-stack IPv4 / IPv6 pada klaster Kubernetes kamu untuk menyediakan fitur-fitur berikut ini:
- Jaringan Pod dual-stack (pengalokasian sebuah alamat IPv4 dan IPv6 untuk setiap Pod)
- Service yang mendukung IPv4 dan IPv6 (setiap Service hanya untuk satu keluarga alamat)
- Perutean Pod ke luar klaster (misalnya Internet) melalui antarmuka IPv4 dan IPv6
Prasyarat
Prasyarat berikut diperlukan untuk menggunakan dual-stack IPv4/IPv6 pada klaster Kubernetes :
- Kubernetes versi 1.16 atau yang lebih baru
- Dukungan dari penyedia layanan untuk jaringan dual-stack (Penyedia layanan cloud atau yang lainnya harus dapat menyediakan antarmuka jaringan IPv4/IPv6 yang dapat dirutekan) untuk Node Kubernetes
- Sebuah plugin jaringan yang mendukung dual-stack (seperti Kubenet atau Calico)
- Kube-proxy yang berjalan dalam mode IPVS
Mengaktifkan dual-stack IPv4/IPv6
Untuk mengaktifkan dual-stack IPv4/IPv6, aktifkan gerbang fitur (feature gate) IPv6DualStack
untuk komponen-komponen yang relevan dari klaster kamu, dan tetapkan jaringan
dual-stack pada klaster:
- kube-controller-manager:
--feature-gates="IPv6DualStack=true"
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
misalnya--cluster-cidr=10.244.0.0/16,fc00::/24
--service-cluster-ip-range=<IPv4 CIDR>,<IPv6 CIDR>
--node-cidr-mask-size-ipv4|--node-cidr-mask-size-ipv6
nilai bawaannya adalah /24 untuk IPv4 dan /64 untuk IPv6
- kubelet:
--feature-gates="IPv6DualStack=true"
- kube-proxy:
--proxy-mode=ipvs
--cluster-cidr=<IPv4 CIDR>,<IPv6 CIDR>
--feature-gates="IPv6DualStack=true"
--cluster-cidr
pada baris perintah, maka penetapan tersebut akan gagal.
Service
Jika klaster kamu mengaktifkan jaringan dual-stack IPv4/IPv6, maka kamu dapat
membuat Service dengan
alamat IPv4 atau IPv6. Kamu dapat memilih keluarga alamat untuk clusterIP
Service kamu dengan mengatur bagian, .spec.ipFamily
, pada Service tersebut.
Kamu hanya dapat mengatur bagian ini saat membuat Service baru. Mengatur bagian
.spec.ipFamily
bersifat opsional dan hanya boleh digunakan jika kamu berencana
untuk mengaktifkan Service
dan Ingress IPv4 dan IPv6
pada klaster kamu. Konfigurasi bagian ini bukanlah syarat untuk lalu lintas
[egress] (#lalu-lintas-egress).
--service-cluster-ip-range
pada kube-controller-manager.
Kamu dapat mengatur .spec.ipFamily
menjadi salah satu dari:
IPv4
: Dimana server API akan mengalokasikan IP dariservice-cluster-ip-range
yaituipv4
IPv6
: Dimana server API akan mengalokasikan IP dariservice-cluster-ip-range
yaituipv6
Spesifikasi Service berikut ini tidak memasukkan bagian ipFamily
.
Kubernetes akan mengalokasikan alamat IP (atau yang dikenal juga sebagai
"cluster IP") dari service-cluster-ip-range
yang dikonfigurasi pertama kali
untuk Service ini.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
Spesifikasi Service berikut memasukkan bagian ipFamily
. Sehingga Kubernetes
akan mengalokasikan alamat IPv6 (atau yang dikenal juga sebagai "cluster IP")
dari service-cluster-ip-range
yang dikonfigurasi untuk Service ini.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ipFamily: IPv6
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
Sebagai perbandingan, spesifikasi Service berikut ini akan dialokasikan sebuah alamat
IPv4 (atau yang dikenal juga sebagai "cluster IP") dari service-cluster-ip-range
yang dikonfigurasi untuk Service ini.
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ipFamily: IPv4
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
Tipe LoadBalancer
Penyedia layanan cloud yang mendukung IPv6 untuk pengaturan beban eksternal,
Mengatur bagian type
menjadi LoadBalancer
sebagai tambahan terhadap mengatur bagian
ipFamily
menjadi IPv6
menyediakan sebuah cloud load balancer untuk Service kamu.
Lalu lintas egress
Penggunaan blok alamat IPv6 yang dapat dirutekan dan yang tidak dapat dirutekan secara publik diperbolehkan selama CNI dari penyedia layanan dapat mengimplementasikan transportasinya. Jika kamu memiliki Pod yang menggunakan IPv6 yang dapat dirutekan secara publik dan ingin agar Pod mencapai tujuan di luar klaster (misalnya Internet publik), kamu harus mengatur IP samaran untuk lalu lintas keluar dan balasannya. ip-masq-agent bersifat dual-stack aware, jadi kamu bisa menggunakan ip-masq-agent untuk masquerading IP dari klaster dual-stack.
Masalah-masalah yang diketahui
- Kubenet memaksa pelaporan posisi IP untuk IPv4,IPv6 IP (--cluster-cidr)