티스토리 뷰

728x90
반응형

서론

복잡한 마이크로서비스 아키텍처에서 서비스 간의 흐름을 관리하고, 호출에 걸리는 시간을 알려주는 Telemetry 서비스를 구성하는 것은 반드시 필요하다. 특히 폴리그랏을 지향하는 마이크로서비스 아키텍처에서 서비스간 호출 관계를 정의하는 것은 무엇보다 중요하다고 할 수 있다. EKS가 제공하는 K8S 기반 네트워크 정책을 수립하는 것도 중요하지만 이는 쉽지않은 구성이며 이를 App Mesh로 구성할 경우 보다 유연한 트래픽 관리가 가능해진다.

바로 이러한 기술을 제공하는 것이 서비스 메시이다. Mesh란 서비스들 사이의 Network Traffic에 대한 논리적인 바운더리라고 볼 수 있다.

보다 자세한 Service Mesh에 대해 알아보고 싶을 경우 다음을 참고하자.

[MSA] Internal LoadBalancer Service Mesh

AWS는 AWS App Mesh를 Service Mesh로 제공한다.

그럼 본격적으로 AWS App Mesh를 구성해 보자.


AWS App Mesh 구성과정

1) AWS App Mesh 아키텍처

2) 통합 구성요소 설치

3) AWS App Mesh 리소스 배포

4) EKS 서비스 생성 및 업데이트

5) AWS App Mesh 테스트


AWS App Mesh 아키텍처

App Mesh는 VM과 컨테이너 모두 (Amazon EC2, Amazon ECS, AWS Fargate, Amazon EKS 및 AWS Outposts)에 대한 서비스 메시 역할을 할 수 있으며, Amazon은 가상 서비스, 가상 노드, 가상 라우터 및 가상 경로를 기반으로 추상화 계층을 만들어 실제 서비스에 접근한다.

다음은 AWS App Mesh의 아키텍처 흐름을 이해하기 위한 하나의 호출 흐름도 이다.

 

 

Client Service 또는 Front-End UI에서 마이크로서비스 A를 호출하고 마이크로서비스 A는 마이크로서비스 B를 호출하는 경우 어떻게 Service Mesh가 동작하는지 알아보자.

AWS App Mesh는 다음과 같은 특징을 기반으로 설계된다.

 

 

AWS App Mesh

a. App Mesh 개체는 모든 관련 엔티티 및 서비스에 대한 논리적 경계 역할을 한다.

Virtual Gateway

a. Mesh 외부에 있는 리소스가 Mesh 내부에 있는 리소스와 통신하기 위한 Gateway이다.

b. EKS에는 Ingress Controller가 존재하지만, 외부 유입 대상의 정보도 함께 Telemetry Service에 수집하여 관리하기 위한 External Gateway 역할을 겸할 수 있다.

c. Virtual Gateway는 다르게 Envoy Proxy라고 부르며, 하나의 Pod로 기동된다.

d. Gateway Route(가상 경로)를 통해 요청 경로와 일치할 경우 Virtual Service로 라우팅한다. 

Virtual Service

a. VM 또는 컨테이너에 배포 된 실제 서비스의 추상화된 형태이다.

b. VirtualServiceName으로 Virtual Service를 호출하면, 해당 요청은 Provider로 지정된 Virtual Node 또는 Virtual Router로 라우팅된다.

c. 가상 서비스와 가상 노드 사이에는 일대 다 관계가 있다.

Virtual Node

a. 새 버전의 마이크로서비스가 배포되면 가상 노드로 구성된다.

b. Virtual Node는 Listener, Backend, Service Discovery가 항상 구성되어야 한다.

c. Virtual Node가 생성될 때 이를 탐색할 수 있는 방법 즉 Service Discovery가 구성되어야 한다.

d. Vitual Node의 인바운드 트래픽은 Listener로 아웃바운드 트래픽은 Backend로 구성된다.

Virtual Router

a. 네트워크 라우터와 유사하게 Virtual Router는 Virtual Node의 엔드포인트 역할을 한다.

b. Virtual Router는 인바운드 요청에 대해 Virtual Node로 전달하는 Virtual Route를 생성한다.

c. 가상 라우터에는 트래픽 정책 및 재시도 정책을 준수하는 하나 이상의 Virtual Route가 있다.

d. Virtual Route는 하나 이상의 Virtual Node에 분산하여 요청을 전달할 수 있으며, 이에 대한 가중치를 지정할 수 있다.

Common

a. AWS App Mesh의 Control Plane은 AWS 컴퓨팅 서비스에 따라 다르게 CRD를 통해 구성된다.

b. AWS App Mesh의 Data Plane은 Envoy 프록시로 구성된다.

c. Amazon EKS와 함께 AWS App Mesh를 사용하면 자동화 된 사이드카 주입을 정의하여 App Mesh를 활용하기 용이하다.

d. Amazon은 표준 Kubernetes 객체로 App Mesh의 구성을 단순화하기 위해 EKS 용 CRD를 제공한다.


통합 구성요소 설치

1) App Mesh 설치 가능 여부 검증

[root@ip-192-168-114-198 appmesh_test]# curl -o pre_upgrade_check.sh https://raw.githubusercontent.com/aws/eks-charts/master/stable/appmesh-controller/upgrade/pre_upgrade_check.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2128  100  2128    0     0   4836      0 --:--:-- --:--:-- --:--:--  4836
[root@ip-192-168-114-198 appmesh_test]# chmod +x pre_upgrade_check.sh 
[root@ip-192-168-114-198 appmesh_test]# sh pre_upgrade_check.sh 
App Mesh CRD check: PASSED!
Controller version check: PASSED!
Injector check for namespace appmesh-inject: PASSED!
Injector check for namespace appmesh-system: PASSED!

Your cluster is ready for upgrade. Please proceed to the installation instructions
[root@ip-192-168-114-198 appmesh_test]# 

위와 같이 pre_upgrade_check.sh을 다운로드 받아 결과가

"Your cluster is ready for upgrade. Please proceed to the installation instructions"

로 출력되는지 확인 후 구성을 진행한다.

2) Helm 설치

[root@ip-192-168-114-198 appmesh_test]# curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3
[root@ip-192-168-114-198 appmesh_test]# chmod 700 get_helm.sh 
[root@ip-192-168-114-198 appmesh_test]# sh get_helm.sh 
Downloading https://get.helm.sh/helm-v3.3.4-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm
[root@ip-192-168-114-198 appmesh_test]# helm repo add eks https://aws.github.io/eks-charts
"eks" has been added to your repositories
[root@ip-192-168-114-198 appmesh_test]# helm repo list
NAME    URL                             
eks     https://aws.github.io/eks-charts
[root@ip-192-168-114-198 appmesh_test]# 

App Mesh는 Helm 기반으로 구성한다.

helm을 설치하기 eks helm repository를 추가한다.

자세한 helm에 대해 학습을 원할 경우 다음을 참고한다.

Helm3 기본 명령어 확인 및 Kubernetes deploy

Helm3 Chart 커스터마이징

Helm3 Best Practices

3) CRD 설치 (CustomerResourceDefinition)

[root@ip-192-168-114-198 appmesh_test]# kubectl apply -k "https://github.com/aws/eks-charts/stable/appmesh-controller/crds?ref=master"
customresourcedefinition.apiextensions.k8s.io/gatewayroutes.appmesh.k8s.aws created
customresourcedefinition.apiextensions.k8s.io/meshes.appmesh.k8s.aws created
customresourcedefinition.apiextensions.k8s.io/virtualgateways.appmesh.k8s.aws created
customresourcedefinition.apiextensions.k8s.io/virtualnodes.appmesh.k8s.aws created
customresourcedefinition.apiextensions.k8s.io/virtualrouters.appmesh.k8s.aws created
customresourcedefinition.apiextensions.k8s.io/virtualservices.appmesh.k8s.aws created
[root@ip-192-168-114-198 appmesh_test]# 

4) appmesh namespace 생성

[root@ip-192-168-114-198 appmesh_test]# kubectl create ns appmesh-system
namespace/appmesh-system created
[root@ip-192-168-114-198 appmesh_test]# 

5) EKS OIDC(OpenID Connect) 자격 증명 공급자 생성

[root@ip-192-168-114-198 appmesh_test]# export CLUSTER_NAME=NRSON-EKS-CLUSTER
[root@ip-192-168-114-198 appmesh_test]# export AWS_REGION=ap-northeast-2
[root@ip-192-168-114-198 appmesh_test]# eksctl utils associate-iam-oidc-provider \
>     --region=$AWS_REGION \
>     --cluster $CLUSTER_NAME \
>     --approve
[  eksctl version 0.29.2
[  using region ap-northeast-2
[  IAM Open ID Connect provider is already associated with cluster "NRSON-EKS-CLUSTER" in "ap-northeast-2"
[root@ip-192-168-114-198 appmesh_test]# 

# eksctl utils associate-iam-oidc-provider --region=$AWS_REGION --cluster $CLUSTER_NAME --approve

6) IAM 역할 생성, 정책 연결 및 사용자 바인딩

[root@ip-192-168-114-198 appmesh_test]# eksctl create iamserviceaccount \
>     --cluster $CLUSTER_NAME \
>     --namespace appmesh-system \
>     --name appmesh-controller \
>     --attach-policy-arn  arn:aws:iam::aws:policy/AWSCloudMapFullAccess,arn:aws:iam::aws:policy/AWSAppMeshFullAccess \
>     --override-existing-serviceaccounts \
>     --approve
[  eksctl version 0.29.2
[  using region ap-northeast-2
[  2 existing iamserviceaccount(s) (kube-system/alb-ingress-controller,kube-system/aws-node) will be excluded
[  1 iamserviceaccount (appmesh-system/appmesh-controller) was included (based on the include/exclude rules)
[  1 iamserviceaccount (kube-system/aws-node) was excluded (based on the include/exclude rules)
[!]  metadata of serviceaccounts that exist in Kubernetes will be updated, as --override-existing-serviceaccounts was set
[  1 task: { 2 sequential sub-tasks: { create IAM role for serviceaccount "appmesh-system/appmesh-controller", create serviceaccount "appmesh-system/appmesh-controller" } }
[  building iamserviceaccount stack "eksctl-NRSON-EKS-CLUSTER-addon-iamserviceaccount-appmesh-system-appmesh-controller"
[  deploying stack "eksctl-NRSON-EKS-CLUSTER-addon-iamserviceaccount-appmesh-system-appmesh-controller"
[  created serviceaccount "appmesh-system/appmesh-controller"
[root@ip-192-168-114-198 appmesh_test]#

# eksctl create iamserviceaccount --cluster $CLUSTER_NAME --namespace appmesh-system --name appmesh-controller --attach-policy-arn  arn:aws:iam::aws:policy/AWSCloudMapFullAccess,arn:aws:iam::aws:policy/AWSAppMeshFullAccess --override-existing-serviceaccounts --approve

7) appmesh-controller 설치

[root@ip-192-168-114-198 appmesh_test]# helm upgrade -i appmesh-controller eks/appmesh-controller \
>     --namespace appmesh-system \
>     --set region=$AWS_REGION \
>     --set serviceAccount.create=false \
>     --set serviceAccount.name=appmesh-controller
Release "appmesh-controller" does not exist. Installing it now.
NAME: appmesh-controller
LAST DEPLOYED: Fri Oct 16 23:47:31 2020
NAMESPACE: appmesh-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
AWS App Mesh controller installed!
[root@ip-192-168-114-198 appmesh_test]# kubectl get deployment appmesh-controller \
>     -n appmesh-system \
>     -o json  | jq -r ".spec.template.spec.containers[].image" | cut -f2 -d ':'
v1.1.1
[root@ip-192-168-114-198 appmesh_test]#

# helm upgrade -i appmesh-controller eks/appmesh-controller --namespace appmesh-system --set region=$AWS_REGION --set serviceAccount.create=false --set serviceAccount.name=appmesh-controller

Helm Chart를 통해 eks/appmesh-controller를 설치한다.

설치가 완료되면 appmesh-controller의 버전을 확인한다. AWS에서 권고하는 버전은 v1.0.0 이상이다.


AWS App Mesh 리소스 배포

1) namespace 생성

먼저 app mesh 리소스를 배치할 namespace를 생성한다.

[root@ip-192-168-114-198 appmesh_test]# cat namespace.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: my-apps
  labels:
    mesh: my-mesh
    appmesh.k8s.aws/sidecarInjectorWebhook: enabled
[root@ip-192-168-114-198 appmesh_test]# kubectl apply -f namespace.yaml 
namespace/my-apps created
[root@ip-192-168-114-198 appmesh_test]# 

2) mesh 생성

다음으로 mesh를 생성한다.

[root@ip-192-168-114-198 appmesh_test]# cat mesh.yaml 
apiVersion: appmesh.k8s.aws/v1beta2
kind: Mesh
metadata:
  name: my-mesh
spec:
  namespaceSelector:
    matchLabels:
      mesh: my-mesh
[root@ip-192-168-114-198 appmesh_test]# kubectl apply -f mesh.yaml 
mesh.appmesh.k8s.aws/my-mesh created
[root@ip-192-168-114-198 appmesh_test]# kubectl describe mesh my-mesh
Name:         my-mesh
Namespace:    
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"appmesh.k8s.aws/v1beta2","kind":"Mesh","metadata":{"annotations":{},"name":"my-mesh"},"spec":{"namespaceSelector":{"matchLa...
API Version:  appmesh.k8s.aws/v1beta2
Kind:         Mesh
Metadata:
  Creation Timestamp:  2020-10-17T03:49:48Z
  Finalizers:
    finalizers.appmesh.k8s.aws/mesh-members
    finalizers.appmesh.k8s.aws/aws-appmesh-resources
  Generation:        1
  Resource Version:  344134
  Self Link:         /apis/appmesh.k8s.aws/v1beta2/meshes/my-mesh
  UID:               c89bf51b-83ac-4c82-87e4-0558e05e38b1
Spec:
  Aws Name:  my-mesh
  Namespace Selector:
    Match Labels:
      Mesh:  my-mesh
Status:
  Conditions:
    Last Transition Time:  2020-10-17T03:49:52Z
    Status:                True
    Type:                  MeshActive
  Mesh ARN:                arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh
  Observed Generation:     1
Events:                    <none>
[root@ip-192-168-114-198 appmesh_test]# 

위와 같이 mesh를 생성하고 mesh 정보를 확인한다. mesh 생성 시 spec.egressFilter.type: ALLOW_ALL 등록 시 아웃바운드 트래픽을 모두 허용하여 Virtual Node에 Backend를 정의하지 않아도 되지만 이는 기본 화이트리스트 정책에 어긋나 권고하지 않는다.

3) Virtual Node 생성

다음으로 Virtual Node를 생성한다. Virtual Node는 Kubernetes 배포에 대한 논리적인 연결을 담당하는 요소이다.

apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
  name: my-service-a
  namespace: my-apps
spec:
  podSelector:
    matchLabels:
      app: my-app-1
  listeners:
    - portMapping:
        port: 80
        protocol: http
  serviceDiscovery:
    dns:
      hostname: my-service-a.my-apps.svc.cluster.local

virtual-node.yaml 파일은 앞서 생성한 my-apps namespace에 my-server-a라는 App Mesh Virtual Node를 만드는데 사용된다. Virtual Node는 이후 Kubernetes Server를 나타낸다. serviceDiscovery.dns.hostname은 Virtual Node가 나타내는 실제 서비스 DNS Hostname이다.

[root@ip-192-168-114-198 appmesh_test]# kubectl apply -f virtual-node.yaml 
virtualnode.appmesh.k8s.aws/my-service-a created
[root@ip-192-168-114-198 appmesh_test]# kubectl describe virtualnode my-service-a -n my-apps
Name:         my-service-a
Namespace:    my-apps
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"appmesh.k8s.aws/v1beta2","kind":"VirtualNode","metadata":{"annotations":{},"name":"my-service-a","namespace":"my-apps"},"sp...
API Version:  appmesh.k8s.aws/v1beta2
Kind:         VirtualNode
Metadata:
  Creation Timestamp:  2020-10-17T03:56:56Z
  Finalizers:
    finalizers.appmesh.k8s.aws/aws-appmesh-resources
  Generation:        1
  Resource Version:  345869
  Self Link:         /apis/appmesh.k8s.aws/v1beta2/namespaces/my-apps/virtualnodes/my-service-a
  UID:               da8d25bb-be19-4c62-9b3d-431e04905d83
Spec:
  Aws Name:  my-service-a_my-apps
  Listeners:
    Port Mapping:
      Port:      80
      Protocol:  http
  Mesh Ref:
    Name:  my-mesh
    UID:   c89bf51b-83ac-4c82-87e4-0558e05e38b1
  Pod Selector:
    Match Labels:
      App:  my-app-1
  Service Discovery:
    Dns:
      Hostname:  my-service-a.my-apps.svc.cluster.local
Status:
  Conditions:
    Last Transition Time:  2020-10-17T03:56:56Z
    Status:                True
    Type:                  VirtualNodeActive
  Observed Generation:     1
  Virtual Node ARN:        arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualNode/my-service-a_my-apps
Events:                    <none>
[root@ip-192-168-114-198 appmesh_test]# 

4) Virtual Router 생성

다음으로 Virtual Router를 생성한다. Virtual Router는 Mesh 내에 하나 이상의 Virtual Service에 대한 트래픽을 처리한다.

apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualRouter
metadata:
  namespace: my-apps
  name: my-service-a-virtual-router
spec:
  listeners:
    - portMapping:
        port: 80
        protocol: http
  routes:
    - name: my-service-a-route
      httpRoute:
        match:
          prefix: /
        action:
          weightedTargets:
            - virtualNodeRef:
                name: my-service-a
              weight: 1

이는 이전 단계에서 생성한 Virtual Node로 트래픽을 라우팅하는 Virtual Router를 생성한다.

Listeners에는 PortMaping 정보를 기입하여 Routing 대상의 Port와 Protocol을 지정할 수 있다. 이때 http 이외의 다른 프로토콜을 사용할 수 있다.

또한 routers는 route를 정의할 수 있어 경로기반 라우팅 처리가 가능하며, virtualNodeRed.name은 App Mesh에서 생성한 Virtual Node가 아닌 Kubernetes의 Virtual Node를 참조한다.
또한 weightedTargets를 통해 가상노드 또는 가상 라우터 쪽으로 가중치 기반 트래픽 전송이 가능하다. 이를 활용하면 BLUE/GREEN 배포 등 다양한 배포정책을 가져갈 수 있다.

[root@ip-192-168-114-198 appmesh_test]# kubectl apply -f virtual-router.yaml 
virtualrouter.appmesh.k8s.aws/my-service-a-virtual-router created
[root@ip-192-168-114-198 appmesh_test]# kubectl describe virtualrouter my-service-a-virtual-router -n my-apps
Name:         my-service-a-virtual-router
Namespace:    my-apps
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"appmesh.k8s.aws/v1beta2","kind":"VirtualRouter","metadata":{"annotations":{},"name":"my-service-a-virtual-router","namespac...
API Version:  appmesh.k8s.aws/v1beta2
Kind:         VirtualRouter
Metadata:
  Creation Timestamp:  2020-10-17T08:49:10Z
  Finalizers:
    finalizers.appmesh.k8s.aws/aws-appmesh-resources
  Generation:        1
  Resource Version:  417523
  Self Link:         /apis/appmesh.k8s.aws/v1beta2/namespaces/my-apps/virtualrouters/my-service-a-virtual-router
  UID:               0bc2e7c0-a643-43fb-9efc-5d6edfd66edf
Spec:
  Aws Name:  my-service-a-virtual-router_my-apps
  Listeners:
    Port Mapping:
      Port:      80
      Protocol:  http
  Mesh Ref:
    Name:  my-mesh
    UID:   c89bf51b-83ac-4c82-87e4-0558e05e38b1
  Routes:
    Http Route:
      Action:
        Weighted Targets:
          Virtual Node Ref:
            Name:  my-service-a
          Weight:  1
      Match:
        Prefix:  /
    Name:        my-service-a-route
Status:
  Conditions:
    Last Transition Time:  2020-10-17T08:49:12Z
    Status:                True
    Type:                  VirtualRouterActive
  Observed Generation:     1
  Route AR Ns:
    My - Service - A - Route:  arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualRouter/my-service-a-virtual-router_my-apps/route/my-service-a-route
  Virtual Router ARN:          arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualRouter/my-service-a-virtual-router_my-apps
Events:                        <none>
[root@ip-192-168-114-198 appmesh_test]# 

5) Virtual Service 생성

다음으로 Virtual Service를 생성한다. Virtual Service는 Virtual Node가 Virtual Reouter를 통해 직접 또는 간접적으로 제공하는 실제 Service의 추상된 형태이다. Virtual Service의 이름을 지정하고 애플리케이션에서 이 이름을 참조하도록 지정하면 이후 요청은 Virtual Service의 Provider로써 지정된 Virtual Node 또는 Virtual Router로 라우팅된다.

apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualService
metadata:
  name: my-service-a
  namespace: my-apps
spec:
  awsName: my-service-a.my-apps.svc.cluster.local
  provider:
    virtualRouter:
      virtualRouterRef:
        name: my-service-a-virtual-router

이는 Virtual Router Provider를 사용하여 이전 단계에서 만든 my-service-a라는 virtual Node로 트래픽을 라우팅하는 Virtual Service를 만든다.

- awsName은 Virtual Service가 추상화 되는 실제 Kubernetes의 서비스 FQDN(Fully Qualified Domain Nmae)이다.

- 실제 생성될 Service는 아래에서 참고한다.

[root@ip-192-168-114-198 appmesh_test]# kubectl apply -f virtual-service.yaml 
virtualservice.appmesh.k8s.aws/my-service-a created
[root@ip-192-168-114-198 appmesh_test]# kubectl describe virtualservice my-service-a -n my-apps
Name:         my-service-a
Namespace:    my-apps
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration:
                {"apiVersion":"appmesh.k8s.aws/v1beta2","kind":"VirtualService","metadata":{"annotations":{},"name":"my-service-a","namespace":"my-apps"},...
API Version:  appmesh.k8s.aws/v1beta2
Kind:         VirtualService
Metadata:
  Creation Timestamp:  2020-10-17T09:19:05Z
  Finalizers:
    finalizers.appmesh.k8s.aws/aws-appmesh-resources
  Generation:        1
  Resource Version:  424846
  Self Link:         /apis/appmesh.k8s.aws/v1beta2/namespaces/my-apps/virtualservices/my-service-a
  UID:               f31d4d29-b686-4b29-8e48-da0c22aa3f5b
Spec:
  Aws Name:  my-service-a.my-apps.svc.cluster.local
  Mesh Ref:
    Name:  my-mesh
    UID:   c89bf51b-83ac-4c82-87e4-0558e05e38b1
  Provider:
    Virtual Router:
      Virtual Router Ref:
        Name:  my-service-a-virtual-router
Status:
  Conditions:
    Last Transition Time:  2020-10-17T09:19:05Z
    Status:                True
    Type:                  VirtualServiceActive
  Observed Generation:     1
  Virtual Service ARN:     arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualService/my-service-a.my-apps.svc.cluster.local
Events:                    <none>
[root@ip-192-168-114-198 appmesh_test]#

다음은 현재까지 생성된 App Mesh와 각 요소를 다음에서 확인할 수 있다.

AWS App Mesh > 메시 > my-mesh

 

 

해당 화면에는 가상 게이트웨이, 가상 서비스, 가상 라우터, 가상 노드를 확인할 수 있다.

구성 간 연관관계는 다음과 같다.

- 가상 노드의 name은 가상 라우터의 routes.httpRoute.action.weightedTargets.virtualNodeRef.name과 동일해야 한다.

- 가상 노드의 serviceDiscovery.dns.hostname은 가상 서비스의 awsName과 동일해야 한다.

- 가상 라우터의 name은 가상 서비스의 provider.virtualRouter.virtualRouterRef.name과 동일해야 한다.


EKS 서비스 생성 및 업데이트

이제 App Mesh 구성이 완료되고 실제 서비스를 배포해 보도록 하자.

App Mesh를 사용하기 위해서 모든 포드에는 App Mesh SideCar Container가 추가되어 있어야 한다. Annotation sidecarInjectorWebhook은 해당 Label로 배포 된 모든 포드에 SideCar Container를 자동으로 추가하게 한다.

해당 구성은 AWS App Mesh 리소스 배포 > namespace 생성을 참고한다.

1) proxy 인증 활성화

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "appmesh:StreamAggregatedResources",
            "Resource": [
                "arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualNode/my-service-a_my-apps"
            ]
        }
    ]
}

위와 같이 본인이 속한 region과 aws acountid를 변경한 후 my-policy라는 이름으로 정책을 생성한다.

 

 

2) IAM 생성

IAM 역할을 생성하고 앞서 생성한 정책을 연결한다. 또한 IAMSERVICEACCOUNT를 생성하고 이 정책을 바인딩한다. 이 역할을 통해 App Mesh에 리소스를 추가, 제거 및 변경할 수 있게 된다.

[root@ip-192-168-114-198 appmesh_test]# eksctl create iamserviceaccount \
>     --cluster $CLUSTER_NAME \
>     --namespace my-apps \
>     --name my-service-a \
>     --attach-policy-arn  arn:aws:iam::104818303680:policy/my-policy \
>     --override-existing-serviceaccounts \
>     --approve
[  eksctl version 0.29.2
[  using region ap-northeast-2
[  2 existing iamserviceaccount(s) (appmesh-system/appmesh-controller,kube-system/alb-ingress-controller) will be excluded
[  2 iamserviceaccounts (kube-system/aws-node, my-apps/my-service-a) were included (based on the include/exclude rules)
[!]  serviceaccounts that exists in Kubernetes will be excluded, use --override-existing-serviceaccounts to override
[  2 parallel tasks: { 2 sequential sub-tasks: { create IAM role for serviceaccount "my-apps/my-service-a", create serviceaccount "my-apps/my-service-a" }, 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/aws-node", create serviceaccount "kube-system/aws-node" } }
[  building iamserviceaccount stack "eksctl-NRSON-EKS-CLUSTER-addon-iamserviceaccount-kube-system-aws-node"
[  building iamserviceaccount stack "eksctl-NRSON-EKS-CLUSTER-addon-iamserviceaccount-my-apps-my-service-a"
[  deploying stack "eksctl-NRSON-EKS-CLUSTER-addon-iamserviceaccount-my-apps-my-service-a"
[  deploying stack "eksctl-NRSON-EKS-CLUSTER-addon-iamserviceaccount-kube-system-aws-node"
[  created serviceaccount "kube-system/aws-node"
[  created serviceaccount "my-apps/my-service-a"
[root@ip-192-168-114-198 appmesh_test]#

3) Kubernetes 서비스 생성

apiVersion: v1
kind: Service
metadata:
  name: my-service-a
  namespace: my-apps
  labels:
    app: my-app-1
spec:
  selector:
    app: my-app-1
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-service-a
  namespace: my-apps
  labels:
    app: my-app-1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app-1
  template:
    metadata:
      labels:
        app: my-app-1
    spec:
      serviceAccountName: my-service-a
      containers:
      - name: nginx
        image: nginx:1.19.0
        ports:
        - containerPort: 80

Kubernetes Service 및 Deployment를 생성한다. 이때 앞서 생성한 sidecarInjectorWebhook에 의해 Virtual Node에 설정한 Label과 일치하여 SideCar Container가 자동으로 Pod에 추가된다.

[root@ip-192-168-114-198 appmesh_test]# kubectl apply -f kubernetes-service.yaml 
service/my-service-a created
deployment.apps/my-service-a created
[root@ip-192-168-114-198 appmesh_test]# kubectl get pod -n my-apps
NAME                            READY   STATUS    RESTARTS   AGE
my-service-a-57c6985655-277mz   2/2     Running   0          25s
my-service-a-57c6985655-6tfqp   2/2     Running   0          25s
my-service-a-57c6985655-c45jk   2/2     Running   0          25s
[root@ip-192-168-114-198 appmesh_test]# kubectl describe pod my-service-a-57c6985655-277mz -n my-apps
Name:         my-service-a-57c6985655-277mz
Namespace:    my-apps
Priority:     0
Node:         ip-192-168-114-1.ap-northeast-2.compute.internal/192.168.114.1
Start Time:   Sat, 17 Oct 2020 11:43:37 +0000
Labels:       app=my-app-1
              pod-template-hash=57c6985655
Annotations:  kubernetes.io/psp: eks.privileged
Status:       Running
IP:           192.168.113.235
IPs:
  IP:           192.168.113.235
Controlled By:  ReplicaSet/my-service-a-57c6985655
Init Containers:
  proxyinit:
    Container ID:   docker://eeeebadf5ada53615ca02df8dfb0213b462ed30a0350d2eb1bd5dcbf2929c8aa
    Image:          840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v3-prod
    Image ID:       docker-pullable://840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager@sha256:0aa17530c0e741e2053bd9a575ba2a7efdb16251716e58915b07f9a8ea0929d0
    Port:           <none>
    Host Port:      <none>
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Sat, 17 Oct 2020 11:43:38 +0000
      Finished:     Sat, 17 Oct 2020 11:43:39 +0000
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:     10m
      memory:  32Mi
    Environment:
      APPMESH_START_ENABLED:         1
      APPMESH_IGNORE_UID:            1337
      APPMESH_ENVOY_INGRESS_PORT:    15000
      APPMESH_ENVOY_EGRESS_PORT:     15001
      APPMESH_APP_PORTS:             80
      APPMESH_EGRESS_IGNORED_IP:     169.254.169.254
      APPMESH_EGRESS_IGNORED_PORTS:  22
      AWS_ROLE_ARN:                  arn:aws:iam::104818303680:role/eksctl-NRSON-EKS-CLUSTER-addon-iamserviceacc-Role1-HHDXGKALX6G2
      AWS_WEB_IDENTITY_TOKEN_FILE:   /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    Mounts:
      /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from my-service-a-token-j9l5x (ro)
Containers:
  nginx:
    Container ID:   docker://a3d19c6b75d2b1a362eb785bcbb4262889b80188190c2c1da19ef63fa06934da
    Image:          nginx:1.19.0
    Image ID:       docker-pullable://nginx@sha256:21f32f6c08406306d822a0e6e8b7dc81f53f336570e852e25fbe1e3e3d0d0133
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 17 Oct 2020 11:43:39 +0000
    Ready:          True
    Restart Count:  0
    Environment:
      AWS_ROLE_ARN:                 arn:aws:iam::104818303680:role/eksctl-NRSON-EKS-CLUSTER-addon-iamserviceacc-Role1-HHDXGKALX6G2
      AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    Mounts:
      /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from my-service-a-token-j9l5x (ro)
  envoy:
    Container ID:   docker://ae39283c82b86651844a896519b53554907d154d43fe2e5f3d6ea869442d88f8
    Image:          840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.15.0.0-prod
    Image ID:       docker-pullable://840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy@sha256:655da25f07c7fc218bc66268719b142b7f81b513d46184cdecf403de26e2d12e
    Port:           9901/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sat, 17 Oct 2020 11:43:39 +0000
    Ready:          True
    Restart Count:  0
    Requests:
      cpu:      10m
      memory:   32Mi
    Readiness:  exec [sh -c curl -s http://localhost:9901/server_info | grep state | grep -q LIVE] delay=1s timeout=1s period=10s #success=1 #failure=3
    Environment:
      APPMESH_VIRTUAL_NODE_NAME:    mesh/my-mesh/virtualNode/my-service-a_my-apps
      APPMESH_PREVIEW:              0
      ENVOY_LOG_LEVEL:              info
      AWS_REGION:                   ap-northeast-2
      AWS_ROLE_ARN:                 arn:aws:iam::104818303680:role/eksctl-NRSON-EKS-CLUSTER-addon-iamserviceacc-Role1-HHDXGKALX6G2
      AWS_WEB_IDENTITY_TOKEN_FILE:  /var/run/secrets/eks.amazonaws.com/serviceaccount/token
    Mounts:
      /var/run/secrets/eks.amazonaws.com/serviceaccount from aws-iam-token (ro)
      /var/run/secrets/kubernetes.io/serviceaccount from my-service-a-token-j9l5x (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  aws-iam-token:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  86400
  my-service-a-token-j9l5x:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  my-service-a-token-j9l5x
    Optional:    false
QoS Class:       Burstable
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From                                                       Message
  ----    ------     ----  ----                                                       -------
  Normal  Scheduled  65s   default-scheduler                                          Successfully assigned my-apps/my-service-a-57c6985655-277mz to ip-192-168-114-1.ap-northeast-2.compute.internal
  Normal  Pulled     64s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Container image "840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-proxy-route-manager:v3-prod" already present on machine
  Normal  Created    64s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Created container proxyinit
  Normal  Started    64s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Started container proxyinit
  Normal  Pulled     63s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Container image "nginx:1.19.0" already present on machine
  Normal  Created    63s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Created container nginx
  Normal  Started    63s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Started container nginx
  Normal  Pulled     63s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Container image "840364872350.dkr.ecr.us-west-2.amazonaws.com/aws-appmesh-envoy:v1.15.0.0-prod" already present on machine
  Normal  Created    63s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Created container envoy
  Normal  Started    63s   kubelet, ip-192-168-114-1.ap-northeast-2.compute.internal  Started container envoy
[root@ip-192-168-114-198 appmesh_test]#

describe 중간쯤 envoy proxy가 container로 추가된 것을 확인할 수 있다.

4) my-apps namespace 내 리소스 확인

[root@ip-192-168-114-198 appmesh_test]# kubectl get all -n my-apps
NAME                                READY   STATUS    RESTARTS   AGE
pod/my-service-a-57c6985655-d6s2t   2/2     Running   0          7m23s
pod/my-service-a-57c6985655-rwpvx   2/2     Running   0          7m23s
pod/my-service-a-57c6985655-tgx68   2/2     Running   0          7m23s

NAME                   TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/my-service-a   ClusterIP   10.100.9.142   <none>        80/TCP    95m

NAME                           READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-service-a   3/3     3            3           95m

NAME                                      DESIRED   CURRENT   READY   AGE
replicaset.apps/my-service-a-57c6985655   3         3         3       95m

NAME                                                        ARN                                                                                                          AGE
virtualrouter.appmesh.k8s.aws/my-service-a-virtual-router   arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualRouter/my-service-a-virtual-router_my-apps   171m

NAME                                          ARN                                                                                                              AGE
virtualservice.appmesh.k8s.aws/my-service-a   arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualService/my-service-a.my-apps.svc.cluster.local   141m

NAME                                       ARN                                                                                         AGE
virtualnode.appmesh.k8s.aws/my-service-a   arn:aws:appmesh:ap-northeast-2:104818303680:mesh/my-mesh/virtualNode/my-service-a_my-apps   7h43m
[root@ip-192-168-114-198 appmesh_test]#

현재까지 생성된 전제 리소스를 확인해 보면

- VirtualNode

- VirtualRouter

- VirtualService

- Deployment (Pod, Replicaset)

- Service

가 생성되어 있는 것을 확인할 수 있다.


AWS App Mesh 테스트

AWS App Mesh 테스트는 기본 EKS 호출구조의 변화로 확인할 수 있다.

EKS는 CNI(Container Network Interface) 네트워크를 통해 통신하며, 각 포트 내 /etc/resolv.conf를 통해 호출 도메인을 확인할 수 있다.

예를 들어 default namespace 내 authorservice를 사용하는 Service 내 Pod의 /etc/resolv.conf를 확인해 보면 다음과 같이 확인할 수 있다.

root@my-nginx-75897978cd-7gszz:/# cat /etc/resolv.conf
nameserver 172.20.0.10
search default.svc.cluster.local svc.cluster.local cluster.local ap-northeast-2.compute.internal
options ndots:5
root@my-nginx-75897978cd-7gszz:/# 

위는 동일 Namespace 내에 존재하는 또 다른 Pod가 호출할 경우 servicename 또는 servicename.namespacename.svc.cluster.local로 호출할 수 있으며, 다른 Namespace 내에 존재하는 경우 servicename.namespacename.svc.cluster.local로 호출할 수 있다.

이는 기본 namespace가 다르지만 namespace가 network 통신이 가능하여 때로는 엄격한 보안 규칙을 지정해야 하기도 하다.

이때 Service Mesh를 사용하면, 바로 이러한 엄격한 규칙에 따른 보안그룹을 정의할 수 있다.

앞서 살펴본 AWS App Mesh 아키텍처에서 Virtual Node를 다시한번 상기해 보기바란다.

Virtual Node는 Service Discovery, Listener, Backend로 구분되는 세부 구성요소를 갖고 있다.

Service Discovery는 탐색 조건, Listener는 인바운드 트래픽, Backend는 아웃바운드 트래픽이다.

Service Mesh가 구성되면 기본으로 모든 트래픽은 차단된 상태에서 화이트리스트 방식을 사용하고 있다.

다음은 Virtual Node의 한 예시이다.

apiVersion: appmesh.k8s.aws/v1beta2
kind: VirtualNode
metadata:
  name: my-service-a
  namespace: my-apps
spec:
  podSelector:
    matchLabels:
      app: my-app-1
  listeners:
    - portMapping:
        port: 80
        protocol: http
  serviceDiscovery:
    dns:
      hostname: my-service-a.my-apps.svc.cluster.local
  backends:
    - virtualService:
        virtualServiceRef:
          name: your-service-a
          namespace: your-apps

- serviceDiscovery : service를 탐색하기 위한 dns.hostname을 등록한다.

- listeners : 80 port / protocol http를 사용하는 요청만 인바운트 트래픽을 허용한다.

- backends : 본인 또는 다른 virtualService 현재 구성에는 your-apps namespace에 존재하는 your-service-a라는 virtualService로의 아웃바운드 트래픽을 허용한다.

 

 

위와 같은 상호 연결 관계를 virtual node에 정의함으로써 노드간 호출 구조를 코드가 아닌 envoy config를 통해 정의할 수 있다.


결론

Service Mesh는 Kubernetes 환경 내부에서 엄격한 서비스간 통신을 관리하는 중계 Proxy 역할을 한다. 물론 Mesh 외부에 존재하는 Namespace는 해당 네트워크가 적용되지 않는다. 따라서 Mesh 외부에서의 접근 제어 역시 중요하게 관리해야 할 부분이기도 하다.

Service Mesh는 그 외에도 Blue/Green, 카나리 배포 등을 가중치 기반으로 처리하기에 용이하며, Virtual Gateway를 통해 외부로부터의 요청을 처리하는데에도 Service Mesh가 사용된다.

또한, Backend를 통해 마이크로서비스 간 통신을 구성할 수 있으며, 서비스간 TLS 인증을 통해 요청의 신뢰성을 높일 수도 있다.

특히 마이크로서비스에서 가장 중요한 복잡한 서비스 추적을 위해 반드시 필요한 요소이기도 하다. Amazon의 경우 X-Ray / CloudWatch와 연계하여 복잡한 마이크로서비스의 장애를 추적하고 메트릭을 수집하여 표출할 수 있다.

# Service Mesh의 이점

- App Mesh는 모든 마이크로서비스로부터 Metric, Log, TraceID를 지속적으로 수집한다. 이러한 데이터를 통합하여 Amazon CloudWatch, AWS X-Ray 및 여러 호환 도구와 연동하여 모니터링하고 추적할 수 있다. 따라서 마이크로서비스의 문제를 빠르게 식별하고 파악해 전체 애플리케이션을 최적화할 수 있다.

- App Mesh를 사용하면 마이크로서비스 간의 트래픽 요청을 통합하여 관리할 수 있다. 사용자 지정 트래픽 라우팅 규칙을 손쉽게 구현해 배포 중이나 오류 발생 시 또는 애플리케이션의 규모가 커져도 각 마이크로서비스의 고가용성을 보장할 수 있다. 
- App Mesh는 컨테이너로 들어오고 나가는 모든 통신 트래픽을 관리하는 프록시를 배포하고 구성한다. 따라서 애플리케이션 운용을 위해 각 마이크로서비스에 대해 통신 프로토콜을 구성하거나 사용자 지정 코드를 작성하거나 라이브러리를 구현할 필요가 없다. 
- AWS Fargate에서 실행되는 기존 또는 새로운 마이크로서비스, Amazon ECS, Amazon EKS 등과 함께 App Mesh를 사용할 수 있다. App Mesh를 사용하면 코드 변경 없이 여러 클러스터, 오케스트레이션 시스템 또는 VPC에서 실행되는 마이크로서비스의 통신을 단일 애플리케이션으로 모니터링하고 제어할 수 있다.

 

728x90
반응형