4W - 이스티오 메트릭 확인

개요

이번 주차에서는 이스티오 관측 가능성에 알아본다.
로깅은 상대적으로 간단하니 크게 다루지 않고, 대신 메트릭과 트레이싱을 중점적으로 파헤쳐본다.

미리 알아야 할 것은, 책에서 나오는 버전과 25년 5월 현재 내가 사용하는 버전 간에 가장 극심하게 차이가 발생하는 부분 중 하나가 바로 이 관측 가능성 부분이라는 것이다.
1.25버전을 기준으로 Telemetry API는 GA가 됐으며 이전 방식의 세팅들 중 몇 가지는 아예 막혀버렸다.
나는 최신 버전을 기준으로 하기에 순전히 책 내용을 따라가는 사람들에게는 이 내용이 맞지 않을 수 있다.

사전 지식

관측 가능성이란

The ability to collect data about programs' execution, modules' internal states, and the communication among components

관측가능성(Observability)은 프로그램 실행, 모듈의 내부 상태, 컴포넌트 간 통신에 대한 데이터를 수집하는 기능, 능력으로 정의된다.[1]
관측가능성은 관리자가 잘 볼 수 있도록 필요한 메트릭과 각종 정보를 수집해서 내어주는 시스템의 기능이다.
그래서 이 개념은 모니터링과 대비되는 개념이 아닌데, 자세한 내용은 관측 가능성#사견 - 관측가능성과 모니터링의 차이에 담는다.

관측 가능성의배경

분산된 거대한 시스템 내부에서 일어나는 상황은 제대로 알기 어렵다.
이때 시스템의 내부 상태를 좀 더 가시적으로 만들어야 한다는 수요가 생겨났고, 자연스레 관측가능성이란 개념이 떠오르게 됐다.
현대의 운영 담론에서는 모든 고장이나 문제를 사전에 알 수 없고, 고정된 기준을 두고 파악할 수 없다는 것을 가정한다.
대신 시스템으로부터 최대한 다양한 방법으로, 다양한 지표를 수집하여 정제된 형태로 시각화하는 것을 목표로 시스템에 각종 설정을 한다.
이를 통해 가급적 발생한 문제를 빠르게 탐색하고, 아울러 발생할 수도 있는 문제들을 투명하고 직관적으로 볼 수 있게 환경을 구성하는 방향으로 나아가고 있는 것이다.

관측 가능성의 대상 - 시그널

시스템에 관측 가능성을 부여하기 위해 측정(telemetry)할 만한 데이터, 혹은 요소가 무엇이 있을까?
오픈텔레메트리에서는 이러한 데이터들을 신호(signal)라고 표현한다.
현대에는 수집할 신호를 크게 3가지로 분류하고 있다.
image.png
간단하게는 지피티가 이렇게 요약하는데, 잘 설명한 것 같다.[2]

메트릭(Metric)

시스템의 특정 시간을 나타내는 수치값이다.
보통 통계(statistics)라고도 표현한다.
메모리 사용량, 초당 http 요청량, 데이터베이스 사이즈 등이 여기에 해당한다.

로그(Log)

자유롭게 생성되는 각종 텍스트 데이터를 말한다.
이 개념은 사실 굉장히 포괄적인데, 어떤 데이터를 담고 있는 흔적들이라고만 이해해도 무방하다.
이 데이터들은 그 자체로 사용하기 매우 어렵고 방대하다.
그래서 유의미한 로그만 따로 수집하거나, 정제하여 새로운 메트릭으로 만들어내는 등의 작업을 하는 것이 보통이다.

트레이스(Trace)

트래픽, 혹은 프로세스 등의 어떤 실제나 논리적 작업의 흐름을 말한다.
이건 이후 문서에서 조금 더 다루게 될 것이다.

이스티오의 메트릭

이제 이스티오에 관측 가능성 시그널 중 첫번째로 메트릭에 대해서 알아본다.
이스티오의 메트릭은 몇 가지 층위가 존재한다.
(다른 방식도 있긴 하나, 보기 쉽게 프로메테우스 메트릭 방식의 예시를 든다.)

프록시 레벨 메트릭

데이터플레인의 사이드카, 엔보이로부터 오는 메트릭을 말한다.

envoy_cluster_internal_upstream_rq{response_code_class="2xx",cluster_name="xds-grpc"} 7163
envoy_cluster_upstream_rq_completed{cluster_name="xds-grpc"} 7164
envoy_cluster_ssl_connection_error{cluster_name="xds-grpc"} 0
envoy_cluster_lb_subsets_removed{cluster_name="xds-grpc"} 0
envoy_cluster_internal_upstream_rq{response_code="503",cluster_name="xds-grpc"} 1

이스티오와는 연관 없는, 엔보이가 자체적으로 노출하는 메트릭이라 앞단에 envoy_가 붙는다.
(이 메트릭들로부터 아래 서비스 레벨 메트릭을 만들기에 아예 연관 없다고 말할 순 없다.)
이 레벨에서는 엔보이의 구조와 개념에 따라 리스너, 클러스터 등의 정보가 노출된다.
이스티오를 통해 이 메트릭들을 조절하는 것도 가능하다.

서비스 레벨 메트릭

istio_requests_total{
  connection_security_policy="mutual_tls",
  # 이 메트릭은 details라는 서비스로 향하는 트래픽에 대한 정보를 담는다.
  destination_app="details",
  destination_canonical_service="details",
  destination_canonical_revision="v1",
  destination_principal="cluster.local/ns/default/sa/default",
  destination_service="details.default.svc.cluster.local",
  destination_service_name="details",
  destination_service_namespace="default",
  destination_version="v1",
  destination_workload="details-v1",
  destination_workload_namespace="default",
  # 목적지(details) 기준에서 측정한 메트릭이다..
  reporter="destination",
  request_protocol="http",
  response_code="200",
  response_flags="-",
  # 이 메트릭은 productpage라는 서비스로부터 출발한 트래픽에 대한 정보를 담는다.
  source_app="productpage",
  source_canonical_service="productpage",
  source_canonical_revision="v1",
  source_principal="cluster.local/ns/default/sa/default",
  source_version="v1",
  source_workload="productpage-v1",
  source_workload_namespace="default"
} 214

각 엔보이의 메트릭을 종합하여, 서비스 메시의 서비스들끼리 이뤄지는 통신에 대한 메트릭도 제공한다.
이 메트릭은 이스티오를 기본 세팅하고 모니터링했을 때 가장 먼저 마주하게 되는 메트릭으로, 현재 서비스 메시에서 일어나고 있는 트래픽 전반에 대한 정보를 알 수 있게 해준다.
이 메트릭은 다음 문서에서 조금 더 자세하게 다룬다.
위 예시의 reporter 라벨을 보면 알 수 있듯이, 서비스 간 통신에 두 엔보이가 관여하다보니 하나의 트래픽에 대해서도 2개의 메트릭이 발생하게 된다.

컨트롤 플레인 레벨 메트릭

말 그대로 컨트롤 플레인 레벨에서 설정 동기화가 잘 됐는지, 설정 간 에러는 없었는지, 컨트롤 플레인이 사용한 자원은 얼마인지 등의 정보를 나타내는 메트릭이다.
기본으로 제공되는 모든 메트릭은 여기에 나와있다.[3]
컨트롤 플레인의 기본 컴포넌트 이름들을 접두사로 나와있는 것들이 많다.

이스티오(엔보이) 로깅

로깅 부분에 대해서는 실습을 해보지는 않을 것이고, 어떤 식으로 생겼는지만 보자.

[%START_TIME%] "%REQ(:METHOD)% %REQ(X-ENVOY-ORIGINAL-PATH?:PATH)% %PROTOCOL%"
%RESPONSE_CODE% %RESPONSE_FLAGS% %BYTES_RECEIVED% %BYTES_SENT% %DURATION%
%RESP(X-ENVOY-UPSTREAM-SERVICE-TIME)% "%REQ(X-FORWARDED-FOR)%" "%REQ(USER-AGENT)%"
"%REQ(X-REQUEST-ID)%" "%REQ(:AUTHORITY)%" "%UPSTREAM_HOST%"\n

기본 로깅 설정을 하면 엔보이의 로그를 볼 수 있게 되는데, 정확하게 엔보이에서 기본으로 제공하는 형식과 똑같다.[4]
image.png
로그에서 각 부분은 다음의 의미를 가진다.

이스티오 데이터 플레인, 컨트롤 플레인 포트 정보

데이터 플레인, 컨트롤 플레인에 접근해서 메트릭을 뜯어볼 예정이니 간단하게 포트 정보도 알아본다.
데이터 플레인으로서 엔보이가 프록시로 배치될 때, 실제 컨테이너 프로세스를 구체적으로 따져보면 한 단계의 층위가 더 있다.

사실 사이드카로 주입되는 컨테이너의 메인 프로세스는 엔보이가 아니라 pilot-agent이다.
이 에이전트가 각종 환경 설정과 컨트롤 플레인과의 통신을 담당하면서 엔보이를 기동시켜주는 것이다.
image.png

컨트롤 플레인 istiod은를 포트 기준으로 위와 같은 모양을 하고 있다. - 9876 - ControlZ 인터페이스 포트 - ControlZ는 istiod를 직접적으로 모니터링할 때만 여는 웹 대시보드이다.[^54] - 8080 - 서비스 메시의 상태를 노출하는 디버그용 포트이나, deprecated됨 - 근데 아직도 `pilot-discovery request GET`을 하면 이곳으로 요청을 날리게 된다. - 15010 - 데이터 플레인에서 각종 설정을 받기 위해 사용되는 포트 - 즉, 이 통로를 통해 istiod는 데이터 플레인을 관리한다. - 15012 - 위 포트와 같지만 TLS를 통해 통신 간 암호화를 하는 포트로, 기본으로 쓰인다. - 15014 - 컨트롤 플레인의 프로메테우스 메트릭 노출 포트 - 15017 - 데이터 플레인에 사이드카를 주입하는 어드미션 웹훅 포트로, 앞단의 서비스가 443 트래픽을 이쪽으로 포워딩한다.

image.png
문서에도 나와있으니 참고.

실습 진행

이제 본격적으로 실습에 들어가본다.
이번 목표는 일단 이스티오에서 기본으로 제공하는 메트릭들을 관찰하는 것이다.

환경 세팅

이번에도 환경 세팅 방식은 다르지 않으나, 대신 각 관측가능성을 위한 샘플용 툴을 사용하지 않고 헬름을 통해 직접 구축하게 될 것이다.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: myk8s
nodes:
- role: control-plane
  image: kindest/node:v1.32.2
  extraPortMappings:
  - containerPort: 30000 # Sample Application (istio-ingrssgateway) http
    hostPort: 30000
  - containerPort: 30001 # Prometheus
    hostPort: 30001
  - containerPort: 30002 # Grafana
    hostPort: 30002
  - containerPort: 30003 # Kiali
    hostPort: 30003
  - containerPort: 30004 # Tracing
    hostPort: 30004
  - containerPort: 30005 # Sample Application (istio-ingrssgateway) HTTPS
    hostPort: 30005
  - containerPort: 30006 # TCP Route
    hostPort: 30006
  - containerPort: 30007 # New Gateway 
    hostPort: 30007
  extraMounts:
  - hostPath: ../book-source-code-master
    containerPath: /istiobook
  kubeadmConfigPatches:
    - |
      kind: ClusterConfiguration
      controllerManager:
        extraArgs:
          bind-address: 0.0.0.0
networking:
  podSubnet: 10.10.0.0/16
  serviceSubnet: 10.200.1.0/24

KIND를 이용하는 것은 여전하다.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: default
  components:
    pilot:
      k8s:
        env:
          - name: PILOT_FILTER_GATEWAY_CLUSTER_CONFIG
            value: "true"
    ingressGateways:
    - name: istio-ingressgateway
      enabled: true
      k8s:
        service:
          type: NodePort
          ports:
          - name: http
            port: 80
            targetPort: 8080
            nodePort: 30000
          - name: https
            port: 443
            targetPort: 8443
            nodePort: 30005
          externalTrafficPolicy: Local
  meshConfig: {}
  values:
	global:
	  variant: distroless
    pilot:
      env:
        ENABLE_NATIVE_SIDECARS: true

이스티오 오퍼레이터 양식에서는 meshConfig에 아무 내용도 넣지 않는다.
그리고 이번 세팅에서는 세팅하는 프록시를 distroless 이미지 기반으로 넣는다.
이로 인해 프록시 컨테이너에서는 curl, cd 등의 간단한 명령도 넣을 수 없게 된다.

k create ns istioinaction
k label ns istioinaction istio-injection=enabled
k ns istioinaction
kaf services/catalog/kubernetes/catalog.yaml -n istioinaction
kaf services/webapp/kubernetes/webapp.yaml -n istioinaction
kaf services/webapp/istio/webapp-catalog-gw-vs.yaml -n istioinaction


SIMPLEWEB="webapp.istioinaction.io"
curl -s http://$SIMPLEWEB:30000/api/catalog --resolve "$SIMPLEWEB:30000:127.0.0.1"
while true; do curl -s http://$SIMPLEWEB:30000 --resolve "$SIMPLEWEB:30000:127.0.0.1" | jq ".upstream_calls[0].body" ; date "+%Y-%m-%d %H:%M:%S" ; sleep 1; echo; done

image.png
기본 세팅도 해준다.
반복문도 써두긴 했지만 이번에는 트레이싱을 하거나 시각화를 할 것은 아니기 때문에 사실 안 해도 상관 없다.
image.png
간단하게 파드 양식을 꺼내보면, 위와 같이 distroless로 배포된 것을 확인할 수 있다.

프록시, 서비스 레벨 메트릭

프록시 레벨의 메트릭은 그냥 각 엔보이에 요청을 날려보면 된다.

keti catalog-5899bbc954-mtq4g -- curl localhost:15000/stats

image.png
아무 파드나 붙잡고 15000으로 요청을 날리면 관리자 api에 접근할 수 있다.
참고로 위 프록시 컨테이너와는 별개로 기본 앱 컨테이너가 curl을 가지고 있기 때문에 이렇게 요청을 날릴 수 있는 것이다.
이 데이터들은 별다른 세팅을 하지 않더라도 엔보이에서 자체적으로 드러내는 메트릭이다.
image.png
다만 엔보이에서만 드러내는 것만 있는 것은 아니고 이스티오에서 표준으로 드러내는 메트릭 정보도 확인할 수 있다.
단순하게 프록시에 요청하는 것만으로 프록시 레벨, 서비스 레벨의 트래픽을 볼 수 있는 것이다.
image.png
위에서 distroless 이미지를 세팅했기 때문에 이스티오 프록시에 바로 요청을 날리면 curl 명령이 없어서 요청이 수행되지 않는다.

keti catalog-5899bbc954-wnprn --container istio-proxy -- pilot-agent request GET help

그러나 엔보이를 실행하는 기본 명령툴로 pilot-agent를 이용해서 메트릭을 보는 것도 가능하다.
image.png
나오는 내용은 이전에 엔보이 어드민 웹 ui에서 봤던 정보와 완벽히 일치한다.

keti catalog-5899bbc954-wnprn --container istio-proxy -- pilot-agent request GET /stats

image.png
마찬가지로 여기에 stats 엔드포인트로 요청을 날려도 같은 결과가 반환된다.

keti catalog-5899bbc954-wnprn --container istio-proxy -- pilot-agent request GET /stats/prometheus

길어서 찍지는 않았지만 이렇게 하면 프로메테우스 형태로 메트릭을 받아볼 수 있다.

프록시 레벨 메트릭 커스텀하기

    metadata:
      annotations:
        proxy.istio.io/config: |-
          proxyStatsMatcher:
            inclusionPrefixes:
            - "cluster.outbound|80||catalog.istioinaction"

엔보이에서 드러내는 메트릭 정보를 커스텀하려면 위와 같이 프록시 설정을 건드린다.
구체적으로는 엔보이에서 내부적으로 필터링해서 보여주는 정보들을 조금 더 노출시키는 설정이다.
위 방법은 파드에 어노테이션을 다는 방법으로 원하는 프록시에만 설정이 걸리도록 할 수 있다.

  meshConfig:
    defaultConfig:
      proxyStatsMatcher:
        inclusionPrefixes:
        - "cluster.outbound|80||catalog.istioinaction"

이스티오 오퍼레이터 설정에 넣어서 전역적으로 세팅하는 방법도 존재한다.
구체적으로 이 설정은 엔보이가 드러내지 않고 있는 메트릭 중에서 위의 접두사를 가진 메트릭들을 보이게 하는 설정이다.
추가적인 엔보이의 메트릭은 여기에서 자세하게 확인할 수 있다.[1:1]
이스티오 리소스 중 ProxyConfig가 있기에 이걸로 적용하는 것이 가능한가 찾아봤지만 아직 이것을 지원하지 않는 건지 아니면 앞으로도 지원할 생각이 없는 건지 메트릭에 대해 커스텀을 할 수 있는 필드를 제공하지는 않고 있다.[2:1]

컨트롤 플레인 메트릭 - 프로메테우스 메트릭

k -n istio-system debug istiod-7bc4cd85bc-8l7ls -ti --image=nicolaka/netshoot -c debug --profile general -- netstat -ntl 

image.png
나는 모든 이미지를 distroless로 했기 때문에 컨트롤 플레인에서도 다른 명령어를 쓰는 것이 불가능하다.
이럴 때 ephemeral container를 써주면 유용하게 디버깅을 할 수 있다.

k -n istio-system exec istiod-55fd8f655d-xs6lm -- pilot-discovery -h

image.png
아예 실제 컨트롤 플레인에 돌아가는 프로세스를 이용해서 각종 조작을 하는 것도 가능하다.

k -n istio-system exec istiod-55fd8f655d-xs6lm -- pilot-discovery request GET /metrics

image.png
위 경로로 요청을 보내면 프로메테우스 메트릭을 확인할 수 있다.
image.png
citadel 관련 메트릭으로는 인증서 발급과 만료 등에 대한 정보를 볼 수 있다.
image.png
pilot 접두사로는 데이터 플레인에 설정을 적용할 때 발생하는 각종 메트릭 정보를 확인할 수 있다.
image.png
이때 pilot_proxy_convergence 관련 메트릭은 상당히 중요하다.
왜냐하면 컨트롤 플레인의 설정이 프록시에 전달되기까지 걸린 시간을 나타내는 메트릭이기 때문이다.
이 값은 사용자가 이스티오 설정에 영향을 미치는 리소스를 업데이트하거나 설정을 할 때마다 카운트된다.
(사이드카 주입이 일어나는 네임스페이스 워크로드 배포, 이스티오 리소스 업데이트 등)
내 설정에서는 여태 6번의 설정이 발생했는데, 전부 0.1초 내외로 동기화가 완료됐다.
만약 0.4초 정도만에 설정이 적용되는 케이스가 있었다면 le(less equal) 0.5부분부터 한 값이 올라간 상태로 기록돼있었을 것이다.
이런 값이 많다면 이스티오의 설정이 제때 제때 서비스 메시 전반에 반영되지 않는다는 것을 뜻하기 때문에 최적화 요소 중 중요하게 고려해야 한다.
image.png
각 xds 설정이 날아간 횟수, 시간 등의 정보는 pilot_xds 쪽에서 확인할 수 있다.

번외 - 컨트롤 플레인 디버깅할 때 유용한 방법

k -n istio-system exec istiod-55fd8f655d-xs6lm -- pilot-discovery request GET /debug

image.png
debug 경로에는 기본적인 컨트롤 플레인의 상태를 모니터링할 수 있는 각종 추가 경로를 담고 있다.

istioctl dashboard controlz deployment/istiod.istio-system

이걸 웹 ui로 보려면 controlz를 사용하면 된다.
이건 컨트롤플레인을 모니터링하기 위해 내부적으로 제공되는 ui이다.
image.png
다만 위 /debug 경로로 요청해서 보는 것만큼 많은 설정을 보여주지는 않으니 참고하자.

결론

다음 문서에서 메트릭을 커스텀하는 방법을 알아볼 텐데, 여기에서 각 컴포넌트에 직접 접근해서 데이터를 뜯어보는 식으로 디버깅을 해봐야 그나마 커스텀을 하는데 있어 감을 잡기 편하다.
그래서 많이 다루게 되진 않을 지라도 구체적으로 어떻게 메트릭을 찾을 수 있는지 알아두면 유용할 것이다.

컨트롤 플레인 메트릭은 전체 서비스 메시의 상태를 조망하기에 유용한 정보들이 많다.
설정이 제대로 동기화됐는지, 시간을 얼마나 걸렸는지 등의 정보를 통해 서비스 메쉬를 사용하면서 발생할 수 있는 비효율을 최적화할 수 있으니 컨트롤 플레인의 메트릭도 유의깊게 봐두자.

프로메테우스, 그라파나를 이용한 시각화는 다음 문서에서 다룬다.

이전 글, 다음 글

다른 글 보기

이름 index noteType created
1W - 서비스 메시와 이스티오 1 published 2025-04-10
1W - 간단한 장애 상황 구현 후 대응 실습 2 published 2025-04-10
1W - Gateway API를 활용한 설정 3 published 2025-04-10
1W - 네이티브 사이드카 컨테이너 이용 4 published 2025-04-10
2W - 엔보이 5 published 2025-04-19
2W - 인그레스 게이트웨이 실습 6 published 2025-04-17
3W - 버츄얼 서비스를 활용한 기본 트래픽 관리 7 published 2025-04-22
3W - 트래픽 가중치 - flagger와 argo rollout을 이용한 점진적 배포 8 published 2025-04-22
3W - 트래픽 미러링 패킷 캡쳐 9 published 2025-04-22
3W - 서비스 엔트리와 이그레스 게이트웨이 10 published 2025-04-22
3W - 데스티네이션 룰을 활용한 네트워크 복원력 11 published 2025-04-26
3W - 타임아웃, 재시도를 활용한 네트워크 복원력 12 published 2025-04-26
4W - 이스티오 메트릭 확인 13 published 2025-05-03
4W - 이스티오 메트릭 커스텀, 프로메테우스와 그라파나 14 published 2025-05-03
4W - 오픈텔레메트리 기반 트레이싱 예거 시각화, 키알리 시각화 15 published 2025-05-03
4W - 번외 - 트레이싱용 심플 메시 서버 개발 16 published 2025-05-03
5W - 이스티오 mTLS와 SPIFFE 17 published 2025-05-11
5W - 이스티오 JWT 인증 18 published 2025-05-11
5W - 이스티오 인가 정책 설정 19 published 2025-05-11
6W - 이스티오 설정 트러블슈팅 20 published 2025-05-18
6W - 이스티오 컨트롤 플레인 성능 최적화 21 published 2025-05-18
8W - 가상머신 통합하기 22 published 2025-06-01
8W - 엔보이와 iptables 뜯어먹기 23 published 2025-06-01
9W - 앰비언트 모드 구조, 원리 24 published 2025-06-07
9W - 앰비언트 헬름 설치, 각종 리소스 실습 25 published 2025-06-07
7W - 이스티오 메시 스케일링 26 published 2025-06-09
7W - 엔보이 필터를 통한 기능 확장 27 published 2025-06-09

관련 문서

이름 noteType created
관찰 가능성 패턴 - 2024-06-20
Loki knowledge 2024-06-13
Thanos knowledge 2025-02-26
kubestr knowledge 2025-02-19
Grafana knowledge 2024-06-13
OpenTelemetry knowledge 2025-02-28
Prometheus Operator knowledge 2025-03-30
Prometheus knowledge 2025-02-26
관측 가능성 knowledge 2025-02-24
Istio Observability knowledge 2025-04-28
Istio Telemetry knowledge 2025-04-08
Kiali knowledge 2025-04-28
Cilium knowledge 2025-06-15
Kube-State-Metrics knowledge 2025-02-28
Prometheus-Adapter knowledge 2025-03-04
Jaeger knowledge 2025-04-29
OpenTelemtry Operator knowledge 2025-04-29
황금 신호 knowledge 2025-05-16
4주차 - opentelemetry 데모 project 2025-03-01
4주차 - 관측 가능성 project 2025-02-23
4W - EKS 모니터링과 관측 가능성 published 2025-02-28
4W - 프로메테우스 스택을 통한 EKS 모니터링 published 2025-02-28
E-이스티오 컨트롤 플레인 성능 최적화 topic/explain 2025-05-18
E-이스티오 컨트롤 플레인 메트릭 topic/explain 2025-05-18

참고


  1. https://en.wikipedia.org/wiki/Observability_(software) ↩︎ ↩︎

  2. 가시다님 자료 ↩︎ ↩︎

  3. https://istio.io/latest/docs/reference/commands/pilot-discovery/#metrics ↩︎

  4. https://www.envoyproxy.io/docs/envoy/latest/configuration/observability/access_log/usage ↩︎