티스토리 뷰
서론
복잡한 마이크로서비스 아키텍처에서 서비스 간의 흐름을 관리하고, 호출에 걸리는 시간을 알려주는 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
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를 사용하면 마이크로서비스 간의 트래픽 요청을 통합하여 관리할 수 있다. 사용자 지정 트래픽 라우팅 규칙을 손쉽게 구현해 배포 중이나 오류 발생 시 또는 애플리케이션의 규모가 커져도 각 마이크로서비스의 고가용성을 보장할 수 있다. |
'③ 클라우드 > ⓐ AWS' 카테고리의 다른 글
복잡한 마이크로서비스 관계 AWS X-Ray로 정리하자. (0) | 2020.10.26 |
---|---|
Lambda로 잊기 쉬운일을 예약하자! (EC2 자동 기동 종료 방법) (0) | 2020.10.20 |
Amazon API Gateway API 배포 / 인증 10분 완성 (1) | 2020.10.17 |
Amazon EKS 30분만에 구성하기 (CloudFormation) (7) | 2020.10.10 |
Micro Service와 Serverless (0) | 2019.07.09 |
- Total
- Today
- Yesterday
- JEUS7
- 아키텍처
- JEUS6
- Docker
- API Gateway
- openstack tenant
- SWA
- node.js
- kubernetes
- OpenStack
- jeus
- apache
- Architecture
- aa
- aws
- openstack token issue
- k8s
- 마이크로서비스 아키텍처
- webtob
- TA
- Da
- git
- 쿠버네티스
- 마이크로서비스
- MSA
- nodejs
- SA
- 오픈스택
- JBoss
- wildfly
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |