Service Mesh란?
서비스 메시(Service Mesh)는 마이크로서비스 간의 통신을 관리하고 최적화하는 네트워크 계층이다.
이를 통해 트래픽 제어, 서비스 탐색, 로드 밸런싱, 보안 정책 적용, 모니터링 등을 수행할 수 있다.
특히 서비스 간 통신의 복잡성을 해결하는 데 중점을 두며, 주요 아키텍처 구성 요소로는 사이드카 패턴(Sidecar Pattern)이 자주 사용된다.
Gateway API 와 같은 K8S의 관문 역할을 하는 곳에서는 외부에서 들어오는 트래픽에 대해서 다양한 설정을 수행할 수 있지만, 내부의 Pod나 서비스 간의 모니터링이나 트래픽 제어는 어렵기 때문에 이런 서비스 메시를 사용하게 된다.
Service Mesh 종류
사용해본 Service Mesh는 SideCar Pattern의 Istio/envoy 말고는 없었는데, 검색해보니 상당히 많은 Service Mesh 가 있었다.
Service mesh는 현재 크게 세가지 유형으로 구분할 수 있다고 한다.
1) PaaS (Platform as a Service)의 일부로 서비스 코드에 포함되는 유형
Microsoft Azure Service fabric, lagom, SENECA 등이 이 유형에 해당되며, 프레임워크 기반의 프로그래밍 모델이기 때문에, 서비스메쉬를 구현하는데에 특화된 코드가 필요합니다. ( Mesh-native Code )
2) 라이브러리로 구현되어 API 호출을 통해 Service mesh에 결합되는 유형
Spring Cloud, Netflix OSS(Ribbon/Hystrix/Eureka/Archaius), finagle 등이 이 유형에 해당되며, 프레임워크 라이브러리를 사용하는 형태입니다. 이중 Netfilix의 Prana는 sidecar 형태로 동작합니다. 서비스 메시를 이해하고 코드를 작성해야합니다. (Mesh Aware Code)
3) Side car proxy를 이용하여 Service mesh를 마이크로서비스에 주입하는 유형
Istio/Envoy, Consul, Linkerd 등이 이 유형에 해당되며, sidecar proxy 형태로 동작됩니다. 따라서 서비스메시와 무관하게 코드를 작성할 수 있습니다.
출처 : https://velog.io/@tedigom/MSA-제대로-이해하기-4Service-Mesh-f8k317qn1b
Linkerd 구성
Istio는 이전에 테스트해본적이 있기도하고, 스터디에서 다뤘기 때문에 Linkerd를 테스트해보기로 했다.
컨트롤 플레인(Control Plane)
컨트롤 플레인은 Linkerd 클러스터 전반의 네트워크 정책과 트래픽 흐름을 관리하는 중앙 제어 구성 요소라고 한다.
여기서 클러스터 내부의 프록시들과 통신하고 아래의 기능을 제공한다.
- 서비스 디스커버리(Service Discovery): 네트워크 상에서 서비스가 위치하는 지점과 관련 메타데이터를 추적하여 트래픽이 올바른 대상 서비스로 전달되도록 한다.
- 메트릭(Metric) 모니터링: 각 프록시에서 전달한 트래픽 관련 메트릭을 수집하고 대시보드에 표시하여 실시간 모니터링이 가능한다.
- 정책 적용: 네트워크 보안 및 서비스 간 트래픽 제어 정책을 컨트롤 플레인에서 설정하고, 이를 각 프록시에 배포한다.
데이터 플레인(Data Plane)
Linkerd의 데이터 플레인은 각 서비스 인스턴스에 배포된 사이드카 프록시로 구성된다.
이 프록시는 각 서비스의 트래픽을 처리하며, 컨트롤 플레인과 협력하여 로드 밸런싱, 트래픽 라우팅, TLS 암호화 등 여러 네트워크 관리 기능을 제공하게 된다.
Istio에 비해 상당히 가벼운 프록시를 사용해 성능 부담을 최소화했다고 하며,
해당 프록시가 각 서비스 인스턴스에 붙어서 모든 네트워크 트래픽을 가로채고 처리하게된다.
아래의 링크에서 해당 프록시에서 제공하는 기능들을 좀 더 살펴볼 수 있다.
https://linkerd.io/2.16/reference/architecture/#proxy
Linkerd 설치 테스트
- 공식문서 : https://linkerd.io/2.16/getting-started/
- 스터디에서 실습한 과정 : https://gasidaseo.notion.site/Service-Mesh-Linkerd-af5ab1c832214ed5ad3f60e1cb9fe459
- 현재 2.16버전과 조금 다른 명령어나 과정이 존재한다.
- 특히 2.12 버전 이후부터는 그라파나 라이선스 문제로 Linkerd에 포함되어 설치되지 않는다.
kind를 통한 k8s 기본 환경 배포
cat <<EOF > kind-ersia-7w.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
labels:
mynode: control-plane
topology.kubernetes.io/zone: ap-northeast-2a
extraPortMappings:
- containerPort: 30000
hostPort: 30000
protocol: TCP
- containerPort: 30001
hostPort: 30001
protocol: TCP
- containerPort: 30002
hostPort: 30002
protocol: TCP
- role: worker
labels:
mynode: worker1
topology.kubernetes.io/zone: ap-northeast-2a
- role: worker
labels:
mynode: worker2
topology.kubernetes.io/zone: ap-northeast-2b
networking:
podSubnet: 10.10.0.0/16
serviceSubnet: 10.200.1.0/24
EOF
기존의 kind 구성에서 불필요한 설정을 빼고 간소화 하였다.
# kind k8s cluster 생성
kind create cluster --config kind-ersia-7w.yaml --name ersia --image kindest/node:v1.30.4
# 실습 완료 후 kind k8s cluster 삭제
kind delete cluster --name ersia
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get nodes -A
NAME STATUS ROLES AGE VERSION
ersia-control-plane Ready control-plane 42s v1.30.4
ersia-worker Ready <none> 19s v1.30.4
ersia-worker2 Ready <none> 19s v1.30.4
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-7db6d8ff4d-26ldx 1/1 Running 0 33s
kube-system coredns-7db6d8ff4d-fbzgw 1/1 Running 0 33s
kube-system etcd-ersia-control-plane 1/1 Running 0 48s
kube-system kindnet-gv8dn 1/1 Running 0 33s
kube-system kindnet-q6qkd 1/1 Running 0 28s
kube-system kindnet-ts7b8 1/1 Running 0 28s
kube-system kube-apiserver-ersia-control-plane 1/1 Running 0 48s
kube-system kube-controller-manager-ersia-control-plane 1/1 Running 0 48s
kube-system kube-proxy-n7dfv 1/1 Running 0 28s
kube-system kube-proxy-v9xqt 1/1 Running 0 33s
kube-system kube-proxy-vrxs4 1/1 Running 0 28s
kube-system kube-scheduler-ersia-control-plane 1/1 Running 0 48s
local-path-storage local-path-provisioner-7d4d9bdcc5-xbvhh 1/1 Running 0 33s
Linkerd CLI 설치
https://linkerd.io/2.16/tasks/install-helm/ 를 통해 heml으로 설치해도 된다.
여기서는 CLI를 사용해 설치해보았다.
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/install-edge | sh
# 설치한 linkerd 명령어의 환경변수 등록 후 버전 확인
(⎈|kind-ersia:N/A) root@Ersia:~# export PATH=$HOME/.linkerd2/bin:$PATH
(⎈|kind-ersia:N/A) root@Ersia:~# linkerd version
Client version: edge-24.10.3
Server version: unavailable
Linkerd 리소스 설치
현재 cluster에 설치 가능한지 사전 확인명령을 수행한다.
linkerd check --pre
(⎈|kind-ersia:N/A) root@Ersia:~# linkerd check --pre
kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API
...
...
linkerd-version
---------------
√ can determine the latest version
√ cli is up-to-date
Status check results are √
Linkerd의 CRD 먼저 설치
linkerd install --crds | kubectl apply -f -
(⎈|kind-ersia:N/A) root@Ersia:~# linkerd install --crds | kubectl apply -f -
Rendering Linkerd CRDs...
Next, run `linkerd install | kubectl apply -f -` to install the control plane.
customresourcedefinition.apiextensions.k8s.io/authorizationpolicies.policy.linkerd.io created
...
customresourcedefinition.apiextensions.k8s.io/externalworkloads.workload.linkerd.io created
Linkerd의 실제 서비스와 Pod 등을 설치
linkerd install | kubectl apply -f -
(⎈|kind-ersia:N/A) root@Ersia:~# linkerd install | kubectl apply -f -
namespace/linkerd created
clusterrole.rbac.authorization.k8s.io/linkerd-linkerd-identity created
clusterrolebinding.rbac.authorization.k8s.io/linkerd-linkerd-identity created
serviceaccount/linkerd-identity created
...
...
secret/linkerd-config-overrides created
설치 완료 후 정상적으로 설치되었는지 확인한다.
# 설치를 확인하는 공식 명령어
# linkerd-control-plane-proxy 을 확인할 수 있다.
linkerd check
kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API
...
...
linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match
linkerd-extension-checks
------------------------
√ namespace configuration for extensions
Status check results are √
# 설치된 리소스 확인
kubectl get all,sa,cm,secret -n linkerd
NAME READY STATUS RESTARTS AGE
pod/linkerd-destination-5fc7cbddb4-jzbxd 4/4 Running 0 13m # 서비스 디스커버리와 로드 밸런싱 담당
pod/linkerd-identity-566d5d54cc-llt82 2/2 Running 0 13m # 워크로드 신원 관리 및 mTLS 인증서 발급
pod/linkerd-proxy-injector-64cdc9fd99-pgb47 2/2 Running 0 13m # 새 파드에 Linkerd 프록시 자동 주입
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/linkerd-dst ClusterIP 10.200.1.128 <none> 8086/TCP 13m # destination 컴포넌트 내부 통신
service/linkerd-dst-headless ClusterIP None <none> 8086/TCP 13m # destination 컴포넌트 헤드리스 서비스
service/linkerd-identity ClusterIP 10.200.1.166 <none> 8080/TCP 13m # identity 컴포넌트 내부 통신
service/linkerd-identity-headless ClusterIP None <none> 8080/TCP 13m # identity 컴포넌트 헤드리스 서비스
service/linkerd-policy ClusterIP None <none> 8090/TCP 13m # 정책 관련 서비스
service/linkerd-policy-validator ClusterIP 10.200.1.85 <none> 443/TCP 13m # 네트워크 정책 검증
service/linkerd-proxy-injector ClusterIP 10.200.1.21 <none> 443/TCP 13m # 프록시 주입 웹훅 엔드포인트
service/linkerd-sp-validator ClusterIP 10.200.1.35 <none> 443/TCP 13m # 서비스 프로필 검증
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/linkerd-destination 1/1 1 1 13m # destination 컴포넌트 배포 관리
deployment.apps/linkerd-identity 1/1 1 1 13m # identity 컴포넌트 배포 관리
deployment.apps/linkerd-proxy-injector 1/1 1 1 13m # proxy-injector 컴포넌트 배포 관리
NAME DESIRED CURRENT READY AGE
replicaset.apps/linkerd-destination-5fc7cbddb4 1 1 1 13m # destination 배포의 레플리카셋
replicaset.apps/linkerd-identity-566d5d54cc 1 1 1 13m # identity 배포의 레플리카셋
replicaset.apps/linkerd-proxy-injector-64cdc9fd99 1 1 1 13m # proxy-injector 배포의 레플리카셋
NAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/linkerd-heartbeat 55 15 * * * <none> False 0 4m31s 13m # Linkerd 상태 주기적 체크
NAME SECRETS AGE
serviceaccount/default 0 13m # 기본 서비스 계정
serviceaccount/linkerd-destination 0 13m # destination 컴포넌트 권한 관리
serviceaccount/linkerd-heartbeat 0 13m # heartbeat 크론잡 권한 관리
serviceaccount/linkerd-identity 0 13m # identity 컴포넌트 권한 관리
serviceaccount/linkerd-proxy-injector 0 13m # proxy-injector 컴포넌트 권한 관리
NAME DATA AGE
configmap/kube-root-ca.crt 1 13m # 클러스터 루트 CA 인증서
configmap/linkerd-config 2 13m # Linkerd 전반적인 설정 저장
configmap/linkerd-identity-trust-roots 1 13m # 신원 인증용 루트 인증서 저장
NAME TYPE DATA AGE
secret/linkerd-config-overrides Opaque 1 13m # Linkerd 설정 오버라이드
secret/linkerd-identity-issuer Opaque 2 13m # 신원 인증서 발급용 비밀 키 저장
secret/linkerd-policy-validator-k8s-tls kubernetes.io/tls 2 13m # policy-validator TLS 인증서
secret/linkerd-proxy-injector-k8s-tls kubernetes.io/tls 2 13m # proxy-injector TLS 인증서
secret/linkerd-sp-validator-k8s-tls kubernetes.io/tls 2 13m # sp-validator TLS 인증서
위의 Architecture에서 간단하게 확인했듯이, 각 구성요소는 아래와 같은 역할을 수행한다.
- Pods
- linkerd-destination: 서비스 디스커버리와 로드 밸런싱을 담당
- linkerd-identity: 워크로드의 신원을 관리하고 mTLS를 위한 인증서를 발급
- linkerd-proxy-injector: 새로운 파드에 Linkerd 프록시를 자동으로 주입
- Services
- linkerd-dst, linkerd-identity: 각 컴포넌트의 내부 통신을 위한 서비스
- linkerd-policy, linkerd-policy-validator: 네트워크 정책을 관리하고 검증
- linkerd-proxy-injector: 프록시 주입을 위한 웹훅 엔드포인트를 제공
- linkerd-sp-validator: 서비스 프로필 검증을 위한 서비스
- CronJob
- linkerd-heartbeat: Linkerd의 상태를 주기적으로 체크
- ConfigMaps:
- linkerd-config: Linkerd의 설정을 저장
- linkerd-identity-trust-roots: 인증서를 저장
테스트용 App 배포
공식 사이트에서 제공하는 emoji 투표를 수행하는 간단한 웹 서비스이다.
여기서 주의해서 볼 부분은 Pod의 개수이다. (현재 각 Pod별로 1개씩 구동)
curl --proto '=https' --tlsv1.2 -sSfL https://run.linkerd.io/emojivoto.yml | kubectl apply -f -
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get pod -n emojivoto
NAME READY STATUS RESTARTS AGE
emoji-97f8b9d7b-btxc8 1/1 Running 0 31s
vote-bot-6988f549dc-vk76h 1/1 Running 0 31s
voting-589cdb687-2hfdl 1/1 Running 0 31s
web-6dcb9b6479-glc5r 1/1 Running 0 31s
접속하기 위해 NordPort를 할당한다.
# NordPort 30000 Port로 패치
kubectl patch svc web-svc -n emojivoto -p '{"spec": {"type": "NodePort", "ports": [{"port": 80, "nodePort": 30000}]}}'
# 접속할 URL 추출
kubectl get svc -n emojivoto web-svc -o jsonpath={.spec.ports[0].nodePort} | awk '{ print "App URL = http://localhost:"$1 }'
App URL = http://localhost:30000
해당 URL로 Host의 웹브라우저에서 접속하면 아래와 같은 화면을 확인할 수 있다.
Linkerd Injection 수행
injection을 수행하면 Pod가 2개로 변경된다.
kubectl get -n emojivoto deploy -o yaml | linkerd inject - | kubectl apply -f -
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get -n emojivoto deploy -o yaml | linkerd inject - | kubectl apply -f -
deployment "emoji" injected
deployment "vote-bot" injected
deployment "voting" injected
deployment "web" injected
deployment.apps/emoji configured
deployment.apps/vote-bot configured
deployment.apps/voting configured
deployment.apps/web configured
# 수행 후 Pod 모니터링
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get pod -n emojivoto
NAME READY STATUS RESTARTS AGE
emoji-788f84699-zp6s7 2/2 Running 0 13s
emoji-97f8b9d7b-5b9bd 1/1 Terminating 0 118s
vote-bot-555c556d76-47qv8 2/2 Running 0 13s
voting-589cdb687-wmtf7 1/1 Terminating 0 118s
voting-7479ff64b6-tpzqs 2/2 Running 0 13s
web-6dcb9b6479-8g5v2 1/1 Terminating 0 118s
web-85f6fb8564-kfz9w 2/2 Running 0 13s
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get pod -n emojivoto
NAME READY STATUS RESTARTS AGE
emoji-788f84699-zp6s7 2/2 Running 0 49s
vote-bot-555c556d76-47qv8 2/2 Running 0 49s
voting-7479ff64b6-tpzqs 2/2 Running 0 49s
web-85f6fb8564-kfz9w 2/2 Running 0 49s
추가된 Pod는 linkerd-proxy container로 해당 proxy를 통해서 Service Mesh 핵심 역할을 수행한다.
injection이 정상적으로 수행되었는지 확인해보자.
(⎈|kind-ersia:N/A) root@Ersia:~# linkerd -n emojivoto check --proxy
kubernetes-api
--------------
√ can initialize the client
√ can query the Kubernetes API
...
...
linkerd-control-plane-proxy
---------------------------
√ control plane proxies are healthy
√ control plane proxies are up-to-date
√ control plane proxies and cli versions match
linkerd-data-plane
------------------
√ data plane namespace exists
√ data plane proxies are ready
√ data plane is up-to-date
√ data plane and cli versions match
√ data plane pod labels are configured correctly
√ data plane service labels are configured correctly
√ data plane service annotations are configured correctly
√ opaque ports are properly annotated
확인해보면 아까 check 명령어와 달리 data plane 부분을 확인하는 것을 볼 수 있다.
이제 배포한 App에 linkerd가 정상 작동되는 것을 확인했으니, 대시보드를 통해 메트릭을 확인해보자.
viz 대시보드 설치
해당 대시보드는 Linkerd의 Extension으로 공식 사이트에서는 Viz 대시보드 말고도 아래의 기본 Extension을 제공한다.
- viz: Metrics and visibility features
- jaeger: Distributed tracing
- multicluster: Cross-cluster routing
# install the on-cluster metrics stack
linkerd viz install | kubectl apply -f -
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get all,sa,cm,secret -n linkerd-viz
NAME READY STATUS RESTARTS AGE
pod/metrics-api-76bbcc9cc8-lj2q8 0/2 PodInitializing 0 13s # 메트릭 API 서비스 제공
pod/prometheus-97867d867-m7r8q 0/2 PodInitializing 0 13s # 메트릭 수집 및 저장
pod/tap-58988b674d-v6w5r 0/2 PodInitializing 0 13s # 실시간 트래픽 분석
pod/tap-injector-5b9bc54677-6tv89 0/2 PodInitializing 0 12s # Tap 기능을 위한 사이드카 주입
pod/web-f6fbcd8d6-wkhzr 0/2 PodInitializing 0 13s # Linkerd 대시보드 웹 인터페이스
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/metrics-api ClusterIP 10.200.1.52 <none> 8085/TCP 13s # 메트릭 API 서비스 엔드포인트
service/prometheus ClusterIP 10.200.1.250 <none> 9090/TCP 13s # Prometheus 서비스 엔드포인트
service/tap ClusterIP 10.200.1.96 <none> 8088/TCP,443/TCP 13s # Tap 서비스 엔드포인트
service/tap-injector ClusterIP 10.200.1.135 <none> 443/TCP 13s # Tap 인젝터 서비스 엔드포인트
service/web ClusterIP 10.200.1.175 <none> 8084/TCP,9994/TCP 13s # 웹 대시보드 서비스 엔드포인트
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/metrics-api 0/1 1 0 13s # 메트릭 API 배포 관리
deployment.apps/prometheus 0/1 1 0 13s # Prometheus 배포 관리
deployment.apps/tap 0/1 1 0 13s # Tap 배포 관리
deployment.apps/tap-injector 0/1 1 0 13s # Tap 인젝터 배포 관리
deployment.apps/web 0/1 1 0 13s # 웹 대시보드 배포 관리
NAME DESIRED CURRENT READY AGE
replicaset.apps/metrics-api-76bbcc9cc8 1 1 0 13s # 메트릭 API 레플리카셋
replicaset.apps/prometheus-97867d867 1 1 0 13s # Prometheus 레플리카셋
replicaset.apps/tap-58988b674d 1 1 0 13s # Tap 레플리카셋
replicaset.apps/tap-injector-5b9bc54677 1 1 0 13s # Tap 인젝터 레플리카셋
replicaset.apps/web-f6fbcd8d6 1 1 0 13s # 웹 대시보드 레플리카셋
NAME SECRETS AGE
serviceaccount/default 0 14s # 기본 서비스 계정
serviceaccount/metrics-api 0 14s # 메트릭 API 서비스 계정
serviceaccount/prometheus 0 14s # Prometheus 서비스 계정
serviceaccount/tap 0 14s # Tap 서비스 계정
serviceaccount/tap-injector 0 13s # Tap 인젝터 서비스 계정
serviceaccount/web 0 13s # 웹 대시보드 서비스 계정
NAME DATA AGE
configmap/kube-root-ca.crt 1 14s # 클러스터 루트 CA 인증서
configmap/prometheus-config 1 13s # Prometheus 설정
NAME TYPE DATA AGE
secret/tap-injector-k8s-tls kubernetes.io/tls 2 13s # Tap 인젝터 TLS 인증서
secret/tap-k8s-tls kubernetes.io/tls 2 14s # Tap TLS 인증서
설치된 리소스를 보면 Prometeus가 기본 내장되어서 설치되는 것을 확인할 수 있다.
설치 후 check 명령으로 정상 동작 여부 등을 확인할 수 있다.
(⎈|kind-ersia:N/A) root@Ersia:~# linkerd check
...
...
linkerd-viz
-----------
√ linkerd-viz Namespace exists
√ can initialize the client
√ linkerd-viz ClusterRoles exist
√ linkerd-viz ClusterRoleBindings exist
√ tap API server has valid cert
√ tap API server cert is valid for at least 60 days
√ tap API service is running
√ linkerd-viz pods are injected
√ viz extension pods are running
√ viz extension proxies are healthy
√ viz extension proxies are up-to-date
√ viz extension proxies and cli versions match
√ prometheus is installed and configured correctly
√ viz extension self-check
아래의 명령으로 대시보드를 기동할 수 있으며, & 옵션을 넣어서 백그라운드로 수행해도 무방하다.
linkerd viz dashboard --address localhost --port 80
또는
linkerd viz dashboard --address localhost --port 80 &
웹브라우저를 통해서 접속하면 아래와 같은 화면을 확인할 수 있다.
(참고) viz 대시보드 경고창 해소
viz 대시보드를 확인하다보면 아래와 같은 경고창을 확인할 수 있다.
해당 경고창은 Pod들이 Linkerd의 tap 기능을 사용하도록 구성되지 않았다는 것을 의미한다.
tap은 Linkerd의 트래픽 감시 기능 중 하나로 이를 사용하기 위해서는
- Service Mesh에 추가된 Pod들이 injection이 되어있어야 한다.
- (추가)수동으로 injection을 수행해서 그런지 viz 설치 후 해당 tab을 사용할 수 있도록 rollout 해줘야 했다.
# injection을 확인
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl -n emojivoto get deploy web -o yaml | grep linkerd.io/inject | grep -v annotations
linkerd.io/inject: enabled
# 만약 injection 되어있지 않다면 injection 수행
kubectl annotate deploy web -n emojivoto linkerd.io/inject=enabled
# rollout 수행
kubectl rollout restart deploy web -n emojivoto
# 실제로 모두 injection을 수행했으므로 전체 deploy를 rollout
kubectl rollout restart deployment -n emojivoto
viz 대시보드를 통한 배포 디버깅
제일 먼저 viz 대시보드를 통해 emoijvoto App을 보면 성공률이 100%가 아니란 점을 확인할 수 있다.
(우리가 잘못 배포한게 아니라 공식사이트에서 설명하는 것을 보면 일부러 이렇게 배포한 것으로 보인다.)
이 중에 web 쪽을 눌러보자.
이런식으로 web에 대한 전체 배포 흐름을 확인할 수 있으며 어디 배포에서 문제인지 한눈에 확인이 가능하다.
이어서 화면 아래쪽으로 스크롤해보자.
그러면 각 api 호출이 발생하는 내역과 성공률을 볼 수 있다.
여기서 트래픽 흐름이 어디로 가는지 확인할 수 있는데 /api/vote에서 성공률이 80%이고 vote-bot이라는 배포가 접근을 하면서 간간히 에러가 발생하는 것을 확인할 수 있다.
이 중에 좀 더 아래로 내려가면 web에서 vote-bot을 통해 특정 호출 하나가 성공률 0%인 것을 볼 수 있다.
/emojivoto.v1.VotingService/VoteDoughnut 해당 path로의 호출이 문제인 것을 알 수 있다.
그럼 실제 웹페이지에서 도넛에 투표를 해서 서비스에 문제가 있는지 확인해보자.
해당 도넛 emoji를 클릭하면 아래와 같이 404에러가 발생하는 것을 볼 수 있다.
For the sake of this demo, voting for 🍩
always returns an error.
친절하게 해당 데모는 도넛에 투표하면 항상 에러를 발생시킨다고 설명해주고 있다.
이런 식으로 각 Pod와 서비스 내부의 트래픽을 모니터링하고 디버깅할 수 있다.
그 외 추가사항
App 배포 시 자동 injection 수행
매번 배포하고 해당 injection을 수행할 수는 없기 때문에 당연하게도 자동 injection을 지원하는데, annotations을 추가해줘서 간단하게 injection을 수행할 수 있다.
cat <<EOF > echoserver-linkerd.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: echoserver
spec:
replicas: 1
selector:
matchLabels:
app: echoserver
template:
metadata:
labels:
app: echoserver
annotations:
linkerd.io/inject: enabled # injection 수행을 위한 annotations 추가
spec:
containers:
- name: echoserver
image: ealen/echo-server:latest
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: echoserver
spec:
type: NodePort
selector:
app: echoserver
ports:
- port: 80
targetPort: 80
nodePort: 30001
EOF
kubectl apply -f echoserver-linkerd.yaml
테스트용 해당 echoserver App을 배포하면, 별도의 injection을 수행하지 않아도 자동으로 injection이 수행되고 Pod가 2개 수행되는 것을 볼 수 있다.
(⎈|kind-ersia:N/A) root@Ersia:~# kubectl get pod -A | grep echo
default echoserver-8d796dd48-hzrm5 2/2 Running 0 24m
Grafana 설치
위에서 언급했듯이 2.12 버전 이후부터는 그라파나 라이선스 문제로 Linkerd에 포함되어 설치되지 않는다.
In earlier versions of Linkerd, the viz extension also pre-installed a Grafana dashboard. As of Linkerd 2.12, due to licensing changes in Grafana, this is no longer the case. However, you can still install Grafana on your own—see the Grafana docs for instructions on how to create the Grafana dashboards.
https://linkerd.io/2.16/features/dashboard/#grafana
따라서 아래의 과정을 통해 별도로 설치해줘야 한다.
# helm chart를 통한 grafana 설치
helm repo add grafana https://grafana.github.io/helm-charts
helm install grafana -n grafana --create-namespace grafana/grafana \
-f https://raw.githubusercontent.com/linkerd/linkerd2/main/grafana/values.yaml
# viz의 promethus에 grafana가 접근할 수 있는 권한을 부여
# https://github.com/linkerd/linkerd2/blob/release/stable-2.13/grafana/authzpolicy-grafana.yaml
cat <<EOF > authzpolicy-grafana.yaml
apiVersion: policy.linkerd.io/v1alpha1
kind: AuthorizationPolicy
metadata:
namespace: linkerd-viz
name: grafana
spec:
targetRef:
group: policy.linkerd.io
kind: Server
name: prometheus-admin
requiredAuthenticationRefs:
- kind: ServiceAccount
name: grafana
namespace: grafana
EOF
kubectl apply -f authzpolicy-grafana.yaml
# viz 대시보드의 grafana url을 업데이트
linkerd viz install --set grafana.url=grafana.grafana:3000 | kubectl apply -f -
# 이후 다시 대시보드 기동
linkerd viz dashboard --address localhost --port 80
이렇게 설정해주면, viz 대시보드에서 grafana 연동을 확인할 수 있다.
해당 아이콘을 클릭하면 바로 grafana 대시보드로 연동된다.
- 참고1 : https://linkerd.io/2.16/tasks/grafana/#in-cluster-grafana-instances
- 참고2 : https://github.com/linkerd/linkerd2/tree/main/grafana#using-grafana-with-linkerd
Istio vs Linkerd
이 부분은 몇가지 테스트를 해보고 구글링도 해보면서 여러 글들을 읽어보니 아직 Istio가 더 주류이고, 앞으로도 더 빠른 개선이 있을 것이라는 생각이 들었다.
실제로 기본 설정으로 Istio와 Linkerd를 로컬에서 테스트해보면 Linkerd가 상대적으로 정말 가볍고 사용하기 편하다는 느낌을 받긴했는데, Istio에서도 이런 점을 이미 잘 알고 있고 다양한 모드를 지원하는 등 더 많은 개선을 해나가고 있었다.
- Sidecar-less mode로도 불리는 Istio Ambient mode 배포 : https://istio.io/latest/news/releases/1.22.x/announcing-1.22/#ambient-mode-now-in-beta
- eBPF를 사용하는 kmesh : https://jimmysong.io/en/blog/introducing-kmesh-kernel-native-service-mesh/
실제 2개의 github 프로젝트의 Contributor를 보면 Istio가 압도적인 것을 볼 수 있다.
다만, 개인적으로는 Linkerd를 좀 더 응원하고 싶은데, envoy proxy가 C++로 작성되어있다는 점과 경량화된 코드라는 점 때문이다.
C++로 개발을 하면 메모리와 관련된 많은 버그나 취약점 등을 고려하고 노력을 기울여야하고, 이런 큰 프로젝트를 Rust나 다른 언어로 전환하는 것도 쉽지 않은일이다.
- 미국 백악관의 Memory Safe 언어 사용에 관한 문서 : https://www.whitehouse.gov/wp-content/uploads/2024/02/Final-ONCD-Technical-Report.pdf
- 실무자를 위한 서비스 메시 - 이스티오가 해답인가 : https://www.samsungsds.com/kr/insights/service_mesh_istio.html
또한 개인적으로는 WireGuard처럼 가볍고 빠른 프로젝트를 더 선호하는데, 이는 코드가 많아질 수록 리뷰해야하는 양, 유지보수해야하는 양이 점점 많아지기 때문이다.
아래는 Linkerd에서 Envoy를 왜 사용하지 않았는지에 대한 블로그 글인데, Envoy가 별로라서가 아니라 Linkerd가 추구하는 가볍고 빠른 Service Mesh 컨셉과 맞지 않기 때문이라고 설명하고 있다.
In short:
Linkerd doesn’t use Envoy because using Envoy wouldn’t allow us to build the lightest, simplest, and most secure Kubernetes service mesh in the world.
https://linkerd.io/2020/12/03/why-linkerd-doesnt-use-envoy/