Istio는 크게 네트워크 정책을 설정하고 데이터플레인에 정책을 전달하는 중앙 관리 시스템인 컨트롤플레인, 실제로 서비스 간의 통신을 제어하며 실제 트래픽을 처리하는 데이터플레인으로 이루어진다. 각각에 대해서 자세히 알아본다.
Istiod는 컨트롤플레인의 관리 시스템이 동작하기 위한 핵심 컴포넌트로 크게는 아래 기능을 담당한다.
위 기능들은 기존에는 Pilot, Gally, Citadel 독립적인 컴포넌트들이 각각 담당을 하여 구현되었으나, Istio 1.5부터 독립적인 컴포넌트들의 기능이 모두 Istiod 단일 프로세스로 통합되었다. 각 독립적인 컴포넌트들이 Istiod 내에서 어떤 식으로 구현되었는지 확인해보자.
코드가 방대하여 Istiod의 전반적인 기능을 이해할 용도로 구현된 기능의 일부만 분석하였다.
Pilot은 서비스 디스커버리, 트래픽 컨트롤, Envoy 구성 전달을 하는 기능이다.
서비스 디스커버리
1) 서비스 디스커버리를 수행할 컨트롤러 생성 및 ServiceEvent를 감지할 이벤트 핸들러 생성
// NewController creates a new Kubernetes controller
// Created by bootstrap and multicluster (see multicluster.Controller).
func NewController(kubeClient kubelib.Client, options Options) *Controller {
c := &Controller{
opts: options,
client: kubeClient,
queue: queue.NewQueueWithID(1*time.Second, string(options.ClusterID)),
servicesMap: make(map[host.Name]*model.Service),
nodeSelectorsForServices: make(map[host.Name]labels.Instance),
nodeInfoMap: make(map[string]kubernetesNode),
workloadInstancesIndex: workloadinstances.NewIndex(),
initialSyncTimedout: atomic.NewBool(false),
configCluster: options.ConfigCluster,
}
...
c.services = kclient.NewFiltered[*v1.Service](kubeClient, kclient.Filter{ObjectFilter: kubeClient.ObjectFilter()})
//이벤트 핸들러 생성
registerHandlers[*v1.Service](c, c.services, "Services", c.onServiceEvent, nil)
c.endpoints = newEndpointSliceController(c)
}
2) 서비스에 이벤트가 발생했을 때 함수 실행
func (c *Controller) onServiceEvent(pre, curr *v1.Service, event model.Event) error {
log.Debugf("Handle event %s for service %s in namespace %s", event, curr.Name, curr.Namespace)
// Create the standard (cluster.local) service.
svcConv := kube.ConvertService(*curr, c.opts.DomainSuffix, c.Cluster(), c.meshWatcher.Mesh())
switch event {
//서비스 삭제 이벤트 시 deleteService 함수 호출
case model.EventDelete:
c.deleteService(svcConv)
default:
//그 외(생성/업데이트) 이벤트 시 addOrUpdateService 함수 호출
c.addOrUpdateService(pre, curr, svcConv, event, false)
}
return nil
}
func (c *Controller) addOrUpdateService(pre, curr *v1.Service, currConv *model.Service, event model.Event, updateEDSCache bool) {
...
c.Lock()
//prevConv == servicesMap에서 현재 확인하려는 서비스와 동일한 이름을 가진 이전 서비스 상태
prevConv := c.servicesMap[currConv.Hostname]
c.servicesMap[currConv.Hostname] = currConv
c.Unlock()
...
//서비스가 Gateway로 기능하는 경우 모든 EDS 업데이트
needsFullPush = c.extractGatewaysFromService(currConv)
...
//서비스가 NodePort일 경우 모든 EDS 업데이트
needsFullPush = c.updateServiceNodePortAddresses(currConv)
...
// This full push needed to update ALL ends endpoints, even though we do a full push on service add/update
// as that full push is only triggered for the specific service.
if needsFullPush {
// networks are different, we need to update all eds endpoints
c.opts.XDSUpdater.ConfigUpdate(&model.PushRequest{Full: true, Reason: model.NewReasonStats(model.NetworksTrigger)})
}
// We also need to update when the Service changes. For Kubernetes, a service change will result in Endpoint updates,
// but workload entries will also need to be updated.
// TODO(nmittler): Build different sets of endpoints for cluster.local and clusterset.local.
if updateEDSCache || features.EnableK8SServiceSelectWorkloadEntries {
endpoints := c.buildEndpointsForService(currConv, updateEDSCache)
if len(endpoints) > 0 {
c.opts.XDSUpdater.EDSCacheUpdate(shard, string(currConv.Hostname), ns, endpoints)
}
}
// filter out same service event
//prevConv와 currConv가 동일할 경우 Envoy에 Push하지 않음.
if event == model.EventUpdate && !serviceUpdateNeedsPush(pre, curr, prevConv, currConv) {
return
}
c.opts.XDSUpdater.SvcUpdate(shard, string(currConv.Hostname), ns, event)
c.handlers.NotifyServiceHandlers(prevConv, currConv, event)
}
트래픽 컨트롤
변경된 엔드포인트를 캐시하고, VirtualService 및 DestinationRule에 따라 라우팅 정보를 재생성한다.
VirtualService를 통해 Traffic shifting, Fault Injection을 구현할 수 있으며, DestinationRule을 통해 Circuit Breaker를 설정하고,
EnvoyFilter를 통해 Rate Limit을 설정할 수 있다.
대표적으로 VirtualService를 처리하는 코드를 확인해본다.
1) VirtualService 처리 - 라우팅 규칙 생성
//istio/pilot/pkg/networking/core/route/route.go
func BuildHTTPRoutesForVirtualService(
node *model.Proxy,
virtualService config.Config,
serviceRegistry map[host.Name]*model.Service,
hashByDestination DestinationHashMap,
listenPort int,
gatewayNames sets.String,
opts RouteOptions,
) ([]*route.Route, error) {
vs, ok := virtualService.Spec.(*networking.VirtualService)
...
out := make([]*route.Route, 0, len(vs.Http))
catchall := false
for _, http := range vs.Http {
//match가 0일 경우 라우팅 규칙 생성
if len(http.Match) == 0 {
if r := translateRoute(node, http, nil, listenPort, virtualService, serviceRegistry,
hashByDestination, gatewayNames, opts); r != nil {
out = append(out, r)
}
catchall = true
} else {
//match설정이 있을 경우 라우팅 규칙 생성
for _, match := range http.Match {
if r := translateRoute(node, http, match, listenPort, virtualService, serviceRegistry,
hashByDestination, gatewayNames, opts); r != nil {
out = append(out, r)
...
return out, nil
}
2) RDS Push가 필요할 경우 BuildHTTPRoute를 통해 라우팅 규칙을 생성하고, 생성된 규칙을 xDS를 통해 Envoy에 전달한다.
//istio/pilot/pkg/xds/xdsgen.go
func (s *DiscoveryServer) pushXds(con *Connection, w *model.WatchedResource, req *model.PushRequest) error {
...
res, logdata, err := gen.Generate(con.proxy, w, req)
...
}
//istio/pilot/pkg/xds/rds.go
func (c RdsGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource, req *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
...
//라우팅 규칙을 생성하는 BuildHTTPRoutes 함수 호출
resources, logDetails := c.ConfigGenerator.BuildHTTPRoutes(proxy, req, w.ResourceNames)
return resources, logDetails, nil
}
//istio/pilot/pkg/networking/core/httproute.go
func (configgen *ConfigGeneratorImpl) BuildHTTPRoutes(
node *model.Proxy,
req *model.PushRequest,
routeNames []string,
) ([]*discovery.Resource, model.XdsLogDetails) {
...
// 함수를 타고 들어가다 보면 위에서 언급한 BuildHTTPRoutesForVirtualService 함수 등을 호출하여 라우팅 규칙을 생성함.
}
Envoy 구성 전달
istiod는 xDS Sync API를 통해 Envoy에 수집한 구성을 전달한다.
코드 정보 : istio/pilot/pkg/xds/ads.go
코드 정보 : istio/pkg/xds/server.go
코드 정보 : istio/pkg/xds/discovery.go
1) Envoy와의 연결 수립
//istio/pilot/pkg/xds/ads.go
// StreamAggregatedResources implements the ADS interface.
func (s *DiscoveryServer) StreamAggregatedResources(stream DiscoveryStream) error {
return s.Stream(stream)
}
func (s *DiscoveryServer) Stream(stream DiscoveryStream) error {
...
ctx := stream.Context()
peerAddr := "0.0.0.0"
if peerInfo, ok := peer.FromContext(ctx); ok {
peerAddr = peerInfo.Addr.String()
}
//istio/pilot/pkg/xds/ads.go
ids, err := s.authenticate(ctx)
if err != nil {
return status.Error(codes.Unauthenticated, err.Error())
}
if ids != nil {
log.Debugf("Authenticated XDS: %v with identity %v", peerAddr, ids)
} else {
log.Debugf("Unauthenticated XDS: %s", peerAddr)
}
//istio/pilot/pkg/xds/ads.go
// InitContext returns immediately if the context was already initialized.
if err = s.globalPushContext().InitContext(s.Env, nil, nil); err != nil {
// Error accessing the data - log and close, maybe a different pilot replica
// has more luck
log.Warnf("Error reading config %v", err)
return status.Error(codes.Unavailable, "error reading config")
}
con := newConnection(peerAddr, stream)
con.ids = ids
con.s = s
return xds.Stream(con)
}
3) LDS, RDS, CDS, EDS 관련 요청이 있을 경우 ConfigUpdate 함수를 통해 pushChannel에 request를 넣는다.
대표적 예시로 EDS함수를 확인해보자.
//EDS
//istio/pilot/pkg/xds/eds.go
func (s *DiscoveryServer) EDSUpdate(shard model.ShardKey, serviceName string, namespace string,
istioEndpoints []*model.IstioEndpoint,
) {
inboundEDSUpdates.Increment()
// Update the endpoint shards
pushType := s.Env.EndpointIndex.UpdateServiceEndpoints(shard, serviceName, namespace, istioEndpoints)
if pushType == model.IncrementalPush || pushType == model.FullPush {
// Trigger a push
//EDSUpdate가 되었을 경우 ConfigUpdate 함수 호출
s.ConfigUpdate(&model.PushRequest{
Full: pushType == model.FullPush,
ConfigsUpdated: sets.New(model.ConfigKey{Kind: kind.ServiceEntry, Name: serviceName, Namespace: namespace}),
Reason: model.NewReasonStats(model.EndpointUpdate),
})
}
}
//istio/pkg/xds/discovery.go
func (s *DiscoveryServer) ConfigUpdate(req *model.PushRequest) {
...
//요청을 pushChannel에 넣음.
s.pushChannel <- req
}
4) Istiod에서 Envoy에 Push할 이벤트가 있는 경우(pushChannel) 시작된 xDS 스트림에서 해당 이벤트 및 구성(xDS)을 전달한다.
//istio/pkg/xds/server.go
func Stream(ctx ConnectionContext) error {
...
select {
...
//앞에서 EDS 변화가 발생하여 생긴 요청을 pushChannel에 넣었음. 해당 요청을 Push함수를 통해 최종적으로 xDS로 Envoy에 전달함.
case pushEv := <-con.pushChannel:
err := ctx.Push(pushEv)
if err != nil {
return err
}
case <-con.stop:
return nil
}
//istio/pkg/xds/ads.go
func (conn *Connection) Push(ev any) error {
pushEv := ev.(*Event)
err := conn.s.pushConnection(conn, pushEv)
pushEv.done()
return err
}
func (s *DiscoveryServer) pushConnection(con *Connection, pushEv *Event) error {
pushRequest := pushEv.pushRequest
...
wrl := con.watchedResourcesByOrder()
for _, w := range wrl {
if err := s.pushXds(con, w, pushRequest); err != nil {
return err
}
}
...
}
Galley는 Istio의 구성 데이터를 검증하고 변환하는 기능을 제공하는 기능이다.
Istiod에서는 해당 기능을 아래 코드에서 제공하고 있다. 대표적으로 HTTPHeaderName을 검증하는 코드를 확인해본다.
1) HTTP header name이 비어있거나, validHeaderRegex에 해당하지 않는 name이라면 error를 반환한다.
//istio/pkg/config/validation
var validHeaderRegex = regexp.MustCompile("^[-_A-Za-z0-9]+$")
// ValidateHTTPHeaderName validates a header name
func ValidateHTTPHeaderName(name string) error {
if name == "" {
return fmt.Errorf("header name cannot be empty")
}
if !validHeaderRegex.MatchString(name) {
return fmt.Errorf("header name %s is not a valid header name", name)
}
return nil
}
이 외에도 Metadata, Wight, Percent, Gateway, Server, Port 등 다양한 항목들에 대한 검증 기능을 제공한다.
Citadel은 Istio의 보안 관리 및 인증을 제공하는 기능이다.
Istiod에서는 해당 기능을 아래 코드에서 제공하고 있다. 대표적으로 자체 서명 CA 인증서를 주기적으로 생성/갱신하고, 이를 Kubernetes Secret으로 저장하는 코드를 확인해본다.
1) 기존 CA인증서를 로드하고, 기존 인증서가 없을 경우, 새로운 인증서를 생성한다. 이 후 인증서를 Kubernetes Secret으로 저장한다.
//isito/security/pkg/pki/ca/ca.go
func NewSelfSignedIstioCAOptions(ctx context.Context,
rootCertGracePeriodPercentile int, caCertTTL, rootCertCheckInverval, defaultCertTTL,
maxCertTTL time.Duration, org string, useCacertsSecretName, dualUse bool, namespace string, client corev1.CoreV1Interface,
rootCertFile string, enableJitter bool, caRSAKeySize int,
) (caOpts *IstioCAOptions, err error) {
...
//기존 인증서 로드
err := loadSelfSignedCaSecret(client, namespace, caCertName, rootCertFile, caOpts)
...
pkiCaLog.Infof("CASecret %s not found, will create one", caCertName)
...
//없을 경우 새로운 인증서 생성
pemCert, pemKey, ckErr := util.GenCertKeyFromOptions(options)
...
if caOpts.KeyCertBundle, err = util.NewVerifiedKeyCertBundleFromPem(pemCert, pemKey, nil, rootCerts); err != nil {
pkiCaLog.Warnf("failed to create CA KeyCertBundle (%v)", err)
return fmt.Errorf("failed to create CA KeyCertBundle (%v)", err)
}
...
//생성한 인증서로 Kubernetes Secret 생성
secret := BuildSecret(caCertName, namespace, nil, nil, pemCert, pemCert, pemKey, istioCASecretType)
_, err = client.Secrets(namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
}
Envoy Proxy는 각 애플리케이션에 사이드카로 배포되어 서비스 간의 통신을 제어하고 처리한다. 앞서 살펴본 Istiod로 부터 설정을 받아와 동작하며, Istiod와 Envoy는 xDS프로토콜을 사용해 통신한다.
자세한 기능은 Envoy 설명에서 확인해본다.
Envoy에는 아래와 같은 구성요소들이 있다.
envoy를 설치하고 간단한 envoy proxy 실습을 진행한다.
# wget -O- https://apt.envoyproxy.io/signing.key | sudo gpg --dearmor -o /etc/apt/keyrings/envoy-keyring.gpg
# echo "deb [signed-by=/etc/apt/keyrings/envoy-keyring.gpg] https://apt.envoyproxy.io jammy main" | sudo tee /etc/apt/sources.list.d/envoy.list
# sudo apt-get update && sudo apt-get install envoy -y
# envoy --version
envoy version: e3b4a6e9570da15ac1caffdded17a8bebdc7dfc9/1.32.0/Clean/RELEASE/BoringSSL
static_resources:
listeners:
- name: listener_0
address:
socket_address:
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
access_log:
- name: envoy.access_loggers.stdout
typed_config:
"@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog
http_filters:
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
routes:
- match:
prefix: "/"
route:
host_rewrite_literal: www.envoyproxy.io
cluster: service_envoyproxy_io
clusters:
- name: service_envoyproxy_io
type: LOGICAL_DNS
# Comment out the following line to test on v6 networks
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: service_envoyproxy_io
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: www.envoyproxy.io
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext
sni: www.envoyproxy.io
root@testpc:~# envoy -c envoy-demo.yaml
root@testpc:~# ss -tnlp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 0.0.0.0:10000 0.0.0.0:* users:(("envoy",pid=3407,fd=25))
root@testpc:~# curl -s http://127.0.0.1:10000 | grep -o "<title>.*</title>"
<title>Envoy proxy - home</title>
(⎈|default:N/A) root@k3s-s:~# curl -s http://192.168.10.200:10000 | grep -o "<title>.*</title>"
<title>Envoy proxy - home</title>
root@testpc:~#
...
[2024-10-18T23:24:30.719Z] "GET / HTTP/1.1" 200 - 0 15795 355 284 "-" "curl/7.81.0" "6af88bbd-742c-4eb2-be25-af8c364b23c0" "www.envoyproxy.io" "46.137.195.11:443"
cat <<EOT> envoy-override.yaml
admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9902
EOT
envoy -c envoy-demo.yaml --config-yaml "$(cat envoy-override.yaml)"
# envoy 관리페이지 외부 접속 정보 출력
echo -e "http://$(curl -s ipinfo.io/ip):9902"
아래 yaml을 배포하여 Istio Base Component와 Istiod를 설치한다.
복잡성을 줄이기 위해 ingressgateway는 활성화하고 egressgateway는 비활성화 한다.
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
components:
base:
enabled: true
egressGateways:
- enabled: true
name: istio-egressgateway
ingressGateways:
- enabled: true
name: istio-ingressgateway
pilot:
enabled: true
hub: docker.io/istio
profile: demo
tag: 1.23.2
values:
defaultRevision: ""
gateways:
istio-egressgateway: {}
istio-ingressgateway: {}
global:
configValidation: true
istioNamespace: istio-system
profile: demo
istio를 배포하게 되면 아래와 같은 리소스들이 생성된다.
*istio-ingressgateway는 NodePort로 변경함.
Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway NodePort 10.10.200.37 <none> 15021:32488/TCP,80:32102/TCP,443:31354/TCP,31400:32683/TCP,15443:30575/TCP 17m
istiod ClusterIP 10.10.200.238 <none> 15010/TCP,15012/TCP,443/TCP,15014/TCP
pod
NAME READY STATUS RESTARTS AGE
pod/istio-ingressgateway-5f9f654d46-w87wh 1/1 Running 0 4m35s
pod/istiod-7f8b586864-jl9fd 1/1 Running 0 4m51s
crd
#kubectl get crd | grep istio.io | sort
NAME MIN AVAILABLE MAX UNAVAILABLE ALLOWED DISRUPTIONS AGE
poddisruptionbudget.policy/istio-ingressgateway 1 N/A 0 4m35s
poddisruptionbudget.policy/istiod 1 N/A 0 4m51s
authorizationpolicies.security.istio.io 2024-10-19T15:15:33Z
destinationrules.networking.istio.io 2024-10-19T15:15:33Z
envoyfilters.networking.istio.io 2024-10-19T15:15:34Z
gateways.networking.istio.io 2024-10-19T15:15:34Z
peerauthentications.security.istio.io 2024-10-19T15:15:34Z
proxyconfigs.networking.istio.io 2024-10-19T15:15:34Z
requestauthentications.security.istio.io 2024-10-19T15:15:34Z
serviceentries.networking.istio.io 2024-10-19T15:15:34Z
sidecars.networking.istio.io 2024-10-19T15:15:34Z
telemetries.telemetry.istio.io 2024-10-19T15:15:34Z
virtualservices.networking.istio.io 2024-10-19T15:15:34Z
wasmplugins.extensions.istio.io 2024-10-19T15:15:34Z
workloadentries.networking.istio.io 2024-10-19T15:15:34Z
workloadgroups.networking.istio.io 2024-10-19T15:15:34Z
이름 | 설명 |
---|---|
poddisruptionbudget.policy/istio-ingressgateway | Istio Ingress Gateway의 안정성을 보장하기 위해 최소 가용 Pod 수를 설정한다. |
poddisruptionbudget.policy/istiod | Istio Control Plane(istiod )이 일정 수 이상의 Pod을 유지하도록 설정한다. |
authorizationpolicies.security.istio.io | 서비스 간 통신 및 외부 요청에 대한 접근 제어 규칙을 정의한다. |
destinationrules.networking.istio.io | 서비스 호출 시 적용할 로드 밸런싱, 연결 설정 등의 정책을 정의한다. |
envoyfilters.networking.istio.io | Envoy 프록시의 동작을 커스터마이징하기 위해 필터를 추가한다. |
gateways.networking.istio.io | 외부 트래픽을 내부 서비스로 라우팅하기 위한 Gateway를 정의한다. |
peerauthentications.security.istio.io | 서비스 간 통신에 대한 인증 정책을 설정한다. (mTLS 등) |
proxyconfigs.networking.istio.io | 프록시 설정을 제어하며, 사이드카 및 인그레스 동작을 커스터마이징한다. |
requestauthentications.security.istio.io | 들어오는 요청에 대한 인증을 처리하는 규칙을 정의한다. |
serviceentries.networking.istio.io | 외부 서비스에 대한 접근을 허용하기 위해 내부에 가상 서비스 항목을 만든다. |
sidecars.networking.istio.io | 특정 네임스페이스나 서비스에 대해 사이드카 프록시 동작을 정의한다. |
telemetries.telemetry.istio.io | 모니터링 및 메트릭 수집을 위한 텔레메트리 설정을 정의한다. |
virtualservices.networking.istio.io | 요청을 특정 서비스로 라우팅하기 위한 규칙을 정의한다. |
wasmplugins.extensions.istio.io | WebAssembly(WasM) 플러그인을 사용하여 Istio 프록시를 확장한다. |
workloadentries.networking.istio.io | 클러스터 외부의 워크로드를 Istio 메쉬에 포함시킨니다. |
workloadgroups.networking.istio.io | 비슷한 특성을 가진 외부 워크로드를 그룹으로 정의한다. |
istio-ingressgateway는 Istio의 외부 트래픽을 내부 서비스로 라우팅 하는 역할이다. deployment에서 ps -ef를 확인하면 다음과 같다.
(⎈|default:N/A) root@k3s-s:~# kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- ps -ef
UID PID PPID C STIME TTY TIME CMD
istio-p+ 1 0 0 15:16 ? 00:00:00 /usr/local/bin/pilot-agent proxy router --domain istio-system.svc.cluster.local -
istio-p+ 12 1 0 15:16 ? 00:00:02 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev.json --drain-time-s 45 --drain-
istio-p+ 61 0 0 15:36 pts/0 00:00:00 ps -ef
envoy 프로세스에서 사용하는 envoy 구성 파일을 확인해본다.
(⎈|default:N/A) root@k3s-s:~# kubectl exec -it deployment.apps/istio-ingressgateway -n istio-system -- cat /etc/istio/proxy/envoy-rev.json
구성 요소 | 설명 | 주요 값/설정 |
---|---|---|
application_log_config | 애플리케이션 로그 포맷을 정의 | %Y-%m-%dT%T.%fZ\t%l\tenvoy %n %g:%#\t%v\tthread=%t |
node 정보 | Envoy 노드 및 메타데이터 관련 정보 | id: router~172.16.1.3~... cluster: istio-ingressgateway.istio-system instance_ip: 172.16.1.3 |
annotations | Istio 및 Kubernetes 메타데이터 정보 | istio.io/rev: default , prometheus.io/scrape: true |
layered_runtime | Envoy 런타임 설정을 정의 | - overload.global_downstream_max_connections : 2147483647- re2.max_program_size.error_level : 32768 |
bootstrap_extensions | 내부 리스너 구성. | buffer_size_kb : 64 |
admin 설정 | 관리 인터페이스 관련 설정 | address: 127.0.0.1:15000 profile_path: /var/lib/istio/data/envoy.prof |
dynamic_resources | 동적 리소스 관리 설정(LDS, CDS, ADS) | api_type: DELTA_GRPC discovery_address: istiod.istio-system.svc:15012 |
static_resources | 정적 리소스(클러스터, 리스너) 설정 | 클러스터: prometheus_stats , agent , xds-grpc 리스너: 0.0.0.0:15090 , 0.0.0.0:15021 |
클러스터 설정 | xDS, Prometheus 등과의 연결을 위한 클러스터 정보 | Circuit Breakers: Max 100,000 connections/requests |
리스너 설정 | 네트워크 리스너 구성 | 리스너 포트: 15090, 15021 HTTP 필터: Router, Health Check |
proxy_config | 프록시 동작을 위한 세부 설정 | binaryPath: /usr/local/bin/envoy concurrency: 2 statusPort: 15020 |
메타데이터 | Pod 및 서비스와 관련된 정보. | Pod 이름: istio-ingressgateway-5f9f654d46-w87wh 서비스 계정: istio-ingressgateway-service-account |
현재는 istio ingress gateway 외부 노출 설정이 없기 때문에 nodePort를 기반으로 접속을 시도하였을 때 접속 실패가 된다.
(⎈|default:N/A) root@k3s-s:~# export IGWHTTP=$(kubectl get service -n istio-system istio-ingressgateway -o jsonpath='{.spec.ports[1].nodePort}')
echo $IGWHTTP
32102
(⎈|default:N/A) root@k3s-s:~# export MYDOMAIN=www.gyuri.dev
(⎈|default:N/A) root@k3s-s:~# echo -e "192.168.10.10 $MYDOMAIN" >> /etc/hosts
echo -e "export MYDOMAIN=$MYDOMAIN" >> /etc/profile
(⎈|default:N/A) root@k3s-s:~# curl -v -s $MYDOMAIN:$IGWHTTP
* Trying 192.168.10.10:32102...
* connect to 192.168.10.10 port 32102 failed: Connection refused
* Failed to connect to www.gyuri.dev port 32102 after 1 ms: Connection refused
* Closing connection 0
nginx Deployment 배포
nginx Deployment를 배포한다. 이 때, default namespace에 istio-injection=enabled설정을 통해 해당 네임스페이스에 배포된 파드들에 istio사이드카가 붙도록 설정한다.
cat <<EOF | kubectl create -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: kans-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-websrv
spec:
replicas: 1
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
serviceAccountName: kans-nginx
terminationGracePeriodSeconds: 0
containers:
- name: deploy-websrv
image: nginx:alpine
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-clusterip
spec:
ports:
- name: svc-webport
port: 80
targetPort: 80
selector:
app: deploy-websrv
type: ClusterIP
EOF
(⎈|default:N/A) root@k3s-s:~# kubectl label namespace default istio-injection=enabled
namespace/default labeled
(⎈|default:N/A) root@k3s-s:~# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/deploy-websrv-778ffd6947-zdgdf 2/2 Running 0 15s 172.16.1.4 k3s-w1 <none> <none>
Gateway, VirtualService 배포
지정한 ingress gateway로부터 인입된 트래픽을 관리하기 위해 Gateway를 배포하고, 인입 처리할 hosts 설정 및 목적지 라우팅 정책을 설정하기 위해 VirtualService를 배포한다.
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
name: test-gateway
spec:
selector:
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: nginx-service
spec:
hosts:
- "$MYDOMAIN"
gateways:
- test-gateway
http:
- route:
- destination:
host: svc-clusterip
port:
number: 80
EOF
istio proxy들이 정상적으로 작동하는지 점검한다.
(⎈|default:N/A) root@k3s-s:~# kubectl get gw,vs
NAME AGE
gateway.networking.istio.io/test-gateway 2s
NAME GATEWAYS HOSTS AGE
virtualservice.networking.istio.io/nginx-service ["test-gateway"] ["www.gyuri.dev"] 2s
(⎈|default:N/A) root@k3s-s:~# istioctl proxy-status
NAME CLUSTER CDS LDS EDS RDS ECDS ISTIOD VERSION
deploy-websrv-778ffd6947-zdgdf.default Kubernetes SYNCED (4m18s) SYNCED (4m18s) SYNCED (4m18s) SYNCED (4m18s) IGNORED istiod-7f8b586864-jl9fd 1.23.2
istio-ingressgateway-5f9f654d46-w87wh.istio-system Kubernetes SYNCED (46s) SYNCED (46s) SYNCED (4m18s) SYNCED (46s) IGNORED istiod-7f8b586864-jl9fd 1.23.2
접속 테스트
(⎈|default:N/A) root@k3s-s:~# curl -s $MYDOMAIN:$IGWHTTP | grep -o "<title>.*</title>"
<title>Welcome to nginx!</title>
(⎈|default:N/A) root@k3s-s:~# kubetail -n istio-system -l app=istio-ingressgateway -f
Will tail 1 logs...
istio-ingressgateway-5f9f654d46-w87wh
[istio-ingressgateway-5f9f654d46-w87wh] [2024-10-19T16:11:05.545Z] "GET / HTTP/1.1" 200 - via_upstream - "-" 0 615 8 8 "172.16.0.0" "curl/7.81.0" "2ee7d856-eb06-94a5-8a08-d2f6ab77c9fd" "www.gyuri.dev:32102" "172.16.1.4:80" outbound|80||svc-clusterip.default.svc.cluster.local 172.16.1.3:33970 172.16.1.3:8080 172.16.0.0:52692 - -
[istio-ingressgateway-5f9f654d46-w87wh] [2024-10-19T16:11:14.050Z] "GET / HTTP/1.1" 200 - via_upstream - "-" 0 615 2 1 "172.16.0.0" "curl/7.81.0" "7eff26f0-f971-96c1-8365-9de15f2cfac0" "www.gyuri.dev:32102" "172.16.1.4:80" outbound|80||svc-clusterip.default.svc.cluster.local 172.16.1.3:33970 172.16.1.3:8080 172.16.0.0:4801 - -