기본적인 프로젝트를 생성하면 다음과 같은 web.xml이 생성되어 있을 것이다.

기본적으로 "/"경로를 호출할 때, 쓰일 수 있는 페이지의 형식을 <welcome-file>로 정의해 놓았다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>servlettest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
</web-app>

 

 

앱을 외부로 디플로이 하기 위해 자신의 프로젝트를 톰캣에 추가 하였을 것이다.

web.xml에 매핑을 하지 않았을 경우, Servlet을 호출 할 때

http://<YOUR_IP_ADDRESS>/<ROOT_CONTEXT>/<패키지 이름이 포함된 클래스 이름>

과 같이 매우 긴 주소로 호출 할 것이다.

 

 

하지만 이러한 형식은 클래스 이름이 그대로 노출되기 때문에 보안상 좋지 않습니다. 따라서 이런 방식으로 사용하지 않고, 서블릿 클래스에 대해 대응하는 매핑된 이름으로 실제 서블릿을 요청한다.

 

매핑을 web.xml에서 정의 할 수 있는데 정의하는 방식은 다음과 같다.

  <servlet>
  	<servlet-name>mappingname</servlet-name>
  	<servlet-class>package.YourServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>mappingname</servlet-name>
  	<url-pattern>/name</url-pattern>
  </servlet-mapping>

 

1. <servlet> 태그

이 태그는 프로젝트 내부의 servlet를 외부에 드러낼 수 있도록 servlet에 이름을 지어주는 역할을 한다.

<servlet-name>태그를 이용하여 서블릿의 이름을 지어주고,

<servlet-class>태그를 이용하여 이름을 지어 줄 서블릿을 선언한다.

 

 

2. <servlet-mapping> 태그

이 태그는 내부의 servlet와 외부 요청에 대한 논리적인 경로를 설정해준다.

<servlet-name>태그를 이용하여 외부에 드러낼 서블릿의 이름을 설정한 다음,

<url-pattern>태그를 이용하여 외부에서 접근할 url 경로를 설정해준다.

 

 

 

 

위를 테스트 하기 위해 나는 test패키지를 생성하여 testServlet 클래스를 생성하였다.

package test;

import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class testServlet extends HttpServlet{

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		System.out.println("doGet method is called");
		super.doGet(req, resp);
	} // get 요청에 대한 override class이며, get방식에 대해 요청을 처리하는 클래스이다.

	@Override
	public void destroy() {
		System.out.println("destroy method is called");
		super.destroy();
	} // servlet이 종료될 때 호출되는 class

	@Override
	public void init() throws ServletException {
		System.out.println("init method is called");
		super.init();
	} //최초 servlet call을 다루는 override class
	
	

}

 

 

이 Servlet 클래스를 논리적인 주소로 매핑시키기 위해 다음과 같이 web.xml을 수정하였다.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>servlettest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
  	<servlet-name>mappingname</servlet-name>
  	<servlet-class>test.testServlet</servlet-class>
  </servlet>
  
  <servlet-mapping>
  	<servlet-name>mappingname</servlet-name>
  	<url-pattern>/name</url-pattern>
  </servlet-mapping>
</web-app>

mappingname이라는 이름으로 test 패키지의 testServlet클래스에 대해 이름을 지어준 뒤,

"/name" 이라는 논리적인 경로와 매핑시켜주었다.

 

 

 

다음은 테스트 결과 캡쳐 화면이다.

 

위와 같이 잘 호출 되었음을 알 수 있다.

일반적인 리눅스 터미널에서 awscli를 설치하였을 때, 보통은 제대로 작동하지만, git bash는 윈도우 상에서 리눅스 시스템을 사용하도록 에뮬레이터로 “흉내” 낸 것이기 때문에 경로가 잘 매칭 되지 않는 문제가 빈번히 일어난다. 그 결과로 다음과 같이 제대로 명령어가 작동하지 않는 경우가 종종 일어난다.

리눅스 환경에서도 경로 해석이 잘못 될 경우, 일어나는 에러이다.

 

위 문제를 해결하기 위하여 가장 먼저 awscli가 어떤 언어 기반으로 실행되는지 찾아보았다. 공식 aws github를 찾아본 결과, python을 사용하는 것을 알 수 있었다.

 

Python기반으로 실행되는 스크립트 임을 알게 되었으니, 이제 실행파일이 어디 있는지 확인해보았다. 일반적으로 pip uninstall 명령어를 사용하면 다음과 같이 패키지가 설치 된 경로를 출력한다.

 

위와 같이 내 컴퓨터에서 awscli가 설치된 경로는

c:\users\it1903004\appdata\roaming\python\python37\scripts\aws

 임을 알 수 있었다.

 

awscli를 사용할 때 마다 위 경로를 계속하여 타이핑 하는 것은 매우 비효율적이므로 위 경로의 스크립트를 python으로 실행시키는 것을 “aws“명령어로 alias시켜 사용하기로 결정하였다. alias를 등록하기 위해서 사용자 root 디렉토리 경로에서 ./bashrc파일을 생성하였다.

vi ~/.bashrc
//vi editor 상으로 aws script가 있는 경로에 있는 파일을 python으로 실행시키는 코드를 입력한다.

alias aws='python "c:\users\it1903004\appdata\roaming\python\python37\scripts\aws"'
//입력 후 :wq로 저장한다.


source ./bashrc 
//source 명령어로 ./bashrc 파일을 시스템 상에 적용한다.

 

 

 

 

 

다음과 같은 절차를 거치게 되면 최종적으로 시스템 상에서 aws명령어를 정상적으로 사용할 수 있게 된다.

 

 

1. 의문점

자주 사용하는 AWS Resource에 대해 조사하는 도중 홈페이지에 기술된 EC2 Default Limit과 AWS Service Quota의 내용이 상이하였습니다. 이에 대한 자료가 없어 상이한 내용에 대한 캡처본과 함께 AWS Support Center에 Case Open을 하였습니다.

상이한 내용은 다음과 같습니다.

 

  • AWS 공식 홈페이지의 EC2 Default Limit

 

  • AWS Service Quota에서의 EC2 Default Limit (개인 실습 계정 현황)

 

 

 

 

2. case 답변 본문

답변의 본문은 다음과 같습니다.

Hello,

I would like to thank you for your patience while we waited for the EC2 Service Team to get back to me.

 

When they were reviewing your questions as well as the screenshots provided, they have informed me of the following:

 

There is a slight difference between the new change vCPU's and the old from EC2 classic platform. The difference is that vCPUs uses a quota that allows you to lunch resources dependant on what is the factor as per the documentation below:

 

"Q: What are vCPU-based limits?" https://aws.amazon.com/ec2/faqs/?nc1=h_ls#EC2_On-Demand_Instance_limits

 

Example if your quota is 1,308 you can launch 1,308 t2.micro instances and/or 654 t2.nano's with 654 t2.mirco's.

 

The previous quota limits were based on what is provisioned to your account, which means if the limit was 5 for t2.micro you would only be able to lunch 5 t2.micro's and nothing else.

 

Also the reason why your default limits differ from the documentation is due to account specific information such as when your AWS Account was created, billing history and utilization of the account. The documentation was based on an average of all our customers.

 

Should you at any time have any additional questions or concerns in this regard, please do let us know by responding back to this case. I wish you a wonderful day further!

 

 

 

3. 정리 및 결론

위 본문을 정리하면 다음 두문장으로 설명이 가능합니다.

 

  • 공식 홈페이지 EC2 Default Limit : AWS 전체 사용자가 사용하고 있는 자원에 대에 대해 프로비저닝 된 Limit.

  • AWS Service Quota Default Limit : 계정의 생성날자, 지불 이력, 자원 사용 이력 등에 따라 프로비저닝 된 한도.

 

 

즉, 모든 계정은 새로 만들게 되면 지불 이력과 자원 사용 이력이 없기 때문에 default Limit을 매우 낮게 잡습니다. 그러나 사용이력이 생기면 이를 AWS에서 프로비저닝 하여 새로운 Limit을 적용합니다. 이에 대해 실제로 적용이 되는지 주변에서 Service Quota 현황을 동의를 구하고 수집하였습니다.

 

 

 

  • t2.micro를 5회 미만 생성한 계정의 Service Quota 현황

 

 

  • t2.medium을 10회 이상 생성한 계정의 Service Quota 현황

 

 

 

  • 프리티어를 한도 내에서 다양한 실험을 한 계정의 Service Quota 현황

 

 

 

위 경우와 같이 한도 증가 신청을 하지 않고도 자동으로 프로비저닝 되어 할당된 한도가 증가하는 것을 확인 할 수 있었습니다.

목차

1. 사전 준비

2. EKS로 클러스터 구성하기

3. Kubectl을 이용해 노드 구성하기

    3-1. 노드 마이그레이션

4. helm 설치

5. Auto Scaling Application and Cluster

    5-1. HPA

    5-2. CA

6. Prometheus

7. Grafana

 

 


1. 사전 준비

 

AWS 가입 후, EKS Hands on Lab을 따라할 IAM계정을 생성하겠습니다.

이 testeks라는 이름을 가진 IAM계정에는 administrator access권한을 주었습니다.

(아직 학습이 부족하여 구체적으로 eks를 사용하는데 얼마나 많은 요소가 들어가는지 파악중입니다. 파악되면, 해당 요소만 컨트롤 하는 권한만 정리하여 수록하겠습니다.)

 

 

생성한 IAM계정을 통하여 c9를 workspace를 생성하겠습니다.

일반 인스턴스 터미널로 해도 상관 없지만 코드를 읽으면서 가독성 있는 실습을 위해 c9를 선택하였습니다.

설정은 다음과 같이 진행하였습니다.

 

 

 

설치 후, 다음과 같이 kubectl  jq(Json command-line processor), gettext(쉘 환경변수를 대체하는 envsubst)를 설치합니다.

설치 후, kubectl을 사용해야 하므로, kubectl에 실행 권한을 chmod에 추가합니다

 


sudo curl --silent --location -o /usr/local/bin/kubectl https://storage.googleapis.com/kubernetes-release/release/v1.13.7/bin/linux/amd64/kubectl
sudo chmod +x /usr/local/bin/kubectl
sudo yum install -y jq gettext

 

 

 

 

 

 

 

cloud9에서 eks를 구성하므로, cloud9으로 인해 생성된 instance에 그에 상응하는 권한을 할당해야 합니다. 

다음과 같이 admin권한을 가진 ekstest-admin role을 생성하여 cloud 9 인스턴스에 역할을 부여합니다.

또한, eks에서도 ec2에 대한 자원을 자동으로 management하기 때문에 이에 상응하는 역할도 부여해야합니다.

 

 

 

역할을 부여한 후, c9 내부에서 다음과 같은 설정을 해주어야 합니다.

c9는 생성자의 권한을 따라가기 때문에, 이 권한을 해제하고 직접 권한을 세팅해주기 위해 다음과 같은 설정이 필요하다.

 

다음 명령어를 통해 cloud 9에 있는 credential에 대한 정보를 삭제한 다음,


rm -vf ${HOME}/.aws/credentials

 

 

account id 와 region 정보는 계속해서 사용할 것이므로, 해당 workspace에 global 설정을 하겠습니다.


export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account)
export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region')
echo "export ACCOUNT_ID=${ACCOUNT_ID}" >> ~/.bash_profile
echo "export AWS_REGION=${AWS_REGION}" >> ~/.bash_profile
aws configure set default.region ${AWS_REGION}
aws configure get default.region

 

 

role이 제대로 적용되고, c9설정 변경이 제대로 되었다면, aws 명렁어를 통해 현재 할당된 role를 확인한다.

다음과 같이 ARN 항목에 우리가 작성한 role name이 뜨면 성공적으로 작업이 완료 된 것이다.

aws sts get-caller-identity

 

 

 

 

2. EKS로 클러스터 구성하기

 

다음 명령어를 이용하여, eks를 구성할 eksctl를 설치하고, 리눅스 시스템의 사용자 명령어로 쓰일 수 있도록 /usr/local/bin 폴더로 패키지를 옮긴다.


curl --silent --location "https://github.com/weaveworks/eksctl/releases/download/latest_release/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
sudo mv -v /tmp/eksctl /usr/local/bin

 

 

설치 후 , eksctl를 터미널에 입력하여 다음과 같은 메시지가 나오면 정상이다.


The official CLI for Amazon EKS
Usage: eksctl [command] [flags]
Commands:
eksctl create Create resource(s)
eksctl get Get resource(s)
eksctl update Update resource(s)
eksctl delete Delete resource(s)
eksctl scale Scale resources(s)
eksctl drain Drain resource(s)
eksctl utils Various utils
eksctl completion Generates shell completion scripts
eksctl version Output the version of eksctl
eksctl help Help about any command
Common flags:
-C, --color string toggle colorized logs (valid options: true, false, fabulous) (default "true")
-h, --help help for this command
-v, --verbose int set log level, use 0 to silence, 4 for debugging and 5 for debugging with AWS debug logging (default 3)
Use 'eksctl [command] --help' for more information about a command.

 

 

이제 클러스터를 구성할 것이다. 다음 명령어를 입력하여 node가 3개고, alb ingress filter(alb를 쿠버네티스 상에서 프로비저닝하는 역할)가 적용된 application load balancer가 포함된 vpc로 구성된 eksworkshop 클러스터를 구성한다.


+20191010 추가내용
클러스터 생성시, 입력하는 노드 갯수와 같은 수의 인스턴스가 생성되며, 서브넷은 가용영역 당 하나씩 생성된다. 싱가포르 리전의 경우, 가용영역이 a,b,c이므로, 이에 상응하는 서브넷이 생성된다. 인스턴스 생성 순서는 가용영역 알파벳 순서이다.
eksctl에는 많은 파라미터 들이 있으니 eksctl 사용법을 참고바란다.

eksctl create cluster --name=eksworkshop-eksctl --nodes=3 --alb-ingress-access --region=${AWS_REGION}

 

 

 

 

다음과 같이 로그가 보이면 성공적으로 구성이 된 것이다.


[ℹ] using region ap-southeast-1
[ℹ] setting availability zones to [ap-southeast-1a ap-southeast-1b ap-southeast-1c]
[ℹ] subnets for ap-southeast-1a - public:192.168.0.0/19 private:192.168.96.0/19
[ℹ] subnets for ap-southeast-1b - public:192.168.32.0/19 private:192.168.128.0/19
[ℹ] subnets for ap-southeast-1c - public:192.168.64.0/19 private:192.168.160.0/19
[ℹ] nodegroup "ng-abc5205f" will use "ami-06206d907abb34bbc" [AmazonLinux2/1.13]
[ℹ] using Kubernetes version 1.13
[ℹ] creating EKS cluster "eksworkshop-eksctl" in "ap-southeast-1" region
[ℹ] will create 2 separate CloudFormation stacks for cluster itself and the initial nodegroup
[ℹ] if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-southeast-1 --name=eksworkshop-eksctl'
[ℹ] CloudWatch logging will not be enabled for cluster "eksworkshop-eksctl" in "ap-southeast-1"
[ℹ] you can enable it with 'eksctl utils update-cluster-logging --region=ap-southeast-1 --name=eksworkshop-eksctl'
[ℹ] 2 sequential tasks: { create cluster control plane "eksworkshop-eksctl", create nodegroup "ng-abc5205f" }
[ℹ] building cluster stack "eksctl-eksworkshop-eksctl-cluster"
[ℹ] deploying stack "eksctl-eksworkshop-eksctl-cluster"
[ℹ] building nodegroup stack "eksctl-eksworkshop-eksctl-nodegroup-ng-abc5205f"
[ℹ] --nodes-min=3 was set automatically for nodegroup ng-abc5205f
[ℹ] --nodes-max=3 was set automatically for nodegroup ng-abc5205f
[ℹ] deploying stack "eksctl-eksworkshop-eksctl-nodegroup-ng-abc5205f"
[✔] all EKS cluster resource for "eksworkshop-eksctl" had been created
[✔] saved kubeconfig as "/home/ec2-user/.kube/config"
[ℹ] adding role "arn:aws:iam::511105578551:role/eksctl-eksworkshop-eksctl-nodegro-NodeInstanceRole-ECWQU3TC1J17" to auth ConfigMap
[ℹ] nodegroup "ng-abc5205f" has 0 node(s)
[ℹ] waiting for at least 3 node(s) to become ready in "ng-abc5205f"
[ℹ] nodegroup "ng-abc5205f" has 3 node(s)
[ℹ] node "ip-192-168-0-173.ap-southeast-1.compute.internal" is ready
[ℹ] node "ip-192-168-49-93.ap-southeast-1.compute.internal" is ready
[ℹ] node "ip-192-168-72-198.ap-southeast-1.compute.internal" is ready
[ℹ] kubectl command should work with "/home/ec2-user/.kube/config", try 'kubectl get nodes'
[✔] EKS cluster "eksworkshop-eksctl" in "ap-southeast-1" region is ready

 


+20191009 추가내용

workshop때는 admin권한을 주고 cluster를 구성하였으나, admin권한은 보안적으로 취약하므로, 위 로그를 참고하여 생성되는 리소스에 대한 권한은 모두 포함되는 role를 생성하여 할당 하는 것이 바람직합니다.
sg, cluster, internet gateway, elastic ip, subnet route table, iam policy, subnet, vpc, vpc gateway

 

 

 

위와 같이 성공 하였다면, 지금 우리는 다음과 같은 구성으로 Architecture를 구축한 것입니다.

 

 

 

 

3. Kubectl을 이용해 노드 구성하기

 

이제 각 노드에 대해 디플로이먼트와 서비스를 배포해보겠다. (현재는 퍼블릭 서브넷에다 배포를 하지만, 추후 private subnet으로 노드를 구성하여 배포를 하는 방법도 위키에 올리겠습니다.)

앱을 배포하기 위해서는 이전에 설치한 kubectl을 사용해야 하며 사용법은 링크를 참고한다.

 

배포하기 앞서 다음의 깃허브 레포지토리에서 샘플 앱을 다운받는다.


cd ~/environment git clone https://github.com/brentley/ecsdemo-frontend.git
git clone https://github.com/brentley/ecsdemo-nodejs.git
git clone https://github.com/brentley/ecsdemo-crystal.git

 

 

 

다운로드 후, 다음과 같이 kubernetes cluster안에 service와 deployment를 생성해보자.


kubectl apply -f [your project directory path]/kubernetes/deployments.yaml
kubectl apply -f [your project directory path]/kubernetes/service.yaml
kubectl get pods
kubectl get service
kubectl get deployment

 

이 작업을 통해 백앤드 샘플에 대해 pod을 생성하고 이를 deployment로 묶어 service를 통해 아이피를 할당 받아 app 서비스를 할 준비를 마쳤다. pod과 deployment와 service간의 관계를 잘 모른다면, 이전 페이지를 참고한다.

 

 

이와 같은 방식으로 crystal 백앤드와 프론트앤드의 deployment와 service를 생성한다. 모두 생성하고

kubectl get all 을 실행하면 다음과 같은 리소스들이 출력된다.

Load Balancer생성 전 생성 권한이 없으면 에러가 난다. 에러 출력시, 다음 코드로 권한을 부여한다.


aws iam create-service-linked-role --aws-service-name "elasticloadbalancing.amazonaws.com"

 

 

 

 

 

 

 

deployment, service가 정상적으로 이루어졌다면, 다음과 같은 화면이 계속 새로고침 될 것이다.

 

 

이제 deployment의 replica를 늘려보도록 하자. 명령어를 다음과 같이 입력한다.


kubectl scale deployment ecs-demo-nodejs --replicas=3
kubectl scale deployment ecs-crystal --replicas=3
kubectl scale deployment ecs-frontend --replicas=3

 

 

입력 후 kubectl get deployment의 상태가 다음과 같이 바뀐다.

 

 

3-1. 노드 마이그레이션

 

그러나 크레딧을 아끼기 위해 노드의 인스턴스 타입을 t2.nano로 한 탓에 cpu가 감당이 안되어 deployment scaling이 제대로 일어나지 않았다. 이 참에 nodegroup 마이그레이션을 배우지 않았지만, 스스로 문서에서 찾아서 해보기로 하였다.

 

 

가장 먼저 노드 그룹을 하나 더 만들었다. 다음 코드를 입력하면 캡쳐한 화면과 같이 결과가 출력된다.


eksctl create nodegroup 
--cluster eksworkshop-eksctl 
--name migratedgroup 
--node-type t3.medium 
--nodes 3

 

 

 

 

이 화면이 뜨면, 다음과 같이 인스턴스가 만들어져 있을 것이다. (저는 t3.medium으로 하였습니다.)

 

이제 노드를 마이그레이션 해보겠습니다. 기존 사용하던 node를 삭제하면 자동으로 다른 nodegroup에 마이그레이션 된다고 한다.


eksctl delete nodegroup --cluster eksworksup-eksctl --name [nodegroup name]

 

 

 

위 명령어를 입력하면 기존 사용하던 노드가 삭제되고, kubectl get pods를 입력해보면 다음과 같이 새로 컨테이너들이 만들어지고 있는 것을 확인 할 수 있다.

테스트 하면서 공부할 땐, 과금을 무서워 하지말고 팍팍쓰는게 나을거 같다는 생각이 들었다.

 

 

이제 진짜로 scaling을 해보겠다. 다음 명령어를 입력해서 replica를 3개 까지 늘려보도록 하자.


kubectl scale deployment ecsdemo-nodejs --replicas=3
kubectl scale deployment ecsdemo-frontend --replicas=3
kubectl scale deployment ecsdemo-crystal --replicas=3

 

 

위와 같은 터미널 화면을 마주한다면, 정상적으로 scaling이 된 것이다.

 

 

이제 로드밸런서 타입의 서비스를 통해 우리가 배포한 앱을 확인 할 것이다.

deployment를 복제하여 앱을 배포한 결과, 다음과 같이 고가용성을 만족한 배포가 이루어 졌음을 눈으로 볼 수 있다.

 

 

 

 

 

 

 

 

 

4. helm 설치

이 부분에 대해서는 설치만 진행하였습니다. 자세한 내용은 개인적으로 더 스터디 하여 공유하겠습니다.

Helm은 여러 Kubernetes 리소스를 Chart라는 단일 논리적 배포 단위(쿠버네티스를 관리하면서 나오는 yaml로 구성된 파일의 집합)로 패키지화하는 Kubernetes 용 패키지 관리자 및 응용 프로그램 관리 도구이다. Helm은 tiller라는 서버를 통하여 Kubernetes api server과 통신을 하여 Kubernetes를 관리할 수 있다. 이후 챕터에 필요하므로, 건너뛰지 말고 꼭 설치하도록 하자.

Helm의 기능을 정리하면 다음과 같다.

  • 단일 또는 반복이 가능한 배포

  • 3rd party서비스를 사용하여 응용 프로그램의 종속성 관리

  • 환경에 알맞는 배포의 구성 관리 (ex. test, stagging, production)

  • 사후 / 사전 배포 작업 실행

  • 응용 프로그램 배포 업데이트 / 롤백 / 테스트

 

  1. Helm을 설치하는 script를 curl로 받아와서 해당 파일을 실행해야하므로, 실행권한까지 부여한다.


cd ~/environment
https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh
chmod +x get_helm.sh
sh get_helm.sh

 

 

helm 설치 쉘스크립트를 실행하면 helm init을 입력하라고 메시지가 뜬다. 이를 입력 하지 말고, 다음 안내를 따라서 tiller에 최적화된 helm을 설치한다. 만약 입력 하였을시, helm reset -force를 입력하여 helm을 초기화하고 다시 진행한다.

 

 

2. 다음 스크립트를 입력하여 tiller(helm 클라이언트의 서버)를 운영할 service account를 만들고 tiller까지 설치한다.


cat <<EoF > ~/environment/rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: tiller
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: tiller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
  - kind: ServiceAccount
    name: tiller
    namespace: kube-system
EoF

 

yaml 파일을 다음과 같이 입력 후,


kubectl apply -f ~/environment/rbac.yaml
helm init --service-account tiller

까지 입력해주면 helm설치가 완료되게 된다.

 

 

정상적으로 입력이 되었을 때,


Creating /home/ec2-user/.helm 
Creating /home/ec2-user/.helm/repository 
Creating /home/ec2-user/.helm/repository/cache 
Creating /home/ec2-user/.helm/repository/local 
Creating /home/ec2-user/.helm/plugins 
Creating /home/ec2-user/.helm/starters 
Creating /home/ec2-user/.helm/cache/archive 
Creating /home/ec2-user/.helm/repository/repositories.yaml 
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com 
Adding local repo with URL: http://127.0.0.1:8879/charts 
$HELM_HOME has been configured at /home/ec2-user/.helm.

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

Please note: by default, Tiller is deployed with an insecure 'allow unauthenticated users' policy.
To prevent this, run `helm init` with the --tiller-tls-verify flag.
For more information on securing your installation see: https://docs.helm.sh/using_helm/#securing-your-helm-installation

다음과 같이 출력되면 정상이다.

 

 

 

 

 

 

5. Autoscaling Application and Clusters

 

이전 튜토리얼에서는 수동으로 스케일링을 하였다. 그러나 이번 튜토리얼은 자동으로 scaling하는 방법에 대해 알아 볼 것이다.

먼저 Scaling의 유형에는 2가지 유형이 존재한다.

  • Horizontal Pod Autoscaler (HPA) 는 deployment안에 있는 pod의 replica set을 scaling한다. HPA는 Kubernetes API 리소스와 컨트롤러에 대해서 상속되어있다. 컨트롤러 관리자는 HorzontalPodAutoScaler 정의에 대해 명세된 지표에 따라 이때 resource utilization을 쿼리한다.

  • Cluster Autoscaler(CA)는 Kubernetes의 pod과 node에 대해서 모두 scaling을 할 수 있닌 기본 컴포넌트이다. CA는 자동으로 pod이 자리를 잡아 실행 될 수 있도록 Auto Scaling Group의 크기를 늘릴 수 있으며, 유휴상태일 시 자동으로 node를 줄일 수도 있다.

 

 

5-1. HPA

 

Metric서버는 리소스 사용량에 관한 데이터를 수집하는 서버다. 이 지표는 배포의 확장 및 축소에 관한 동작을 주관한다. 이제 이 metric서버를 설치를 할 것이다. helm 글로벌 chart list에서 metric 서버를 설치해보자. 다음 코드로 설치를 진행한다.


helm install stable/metrics-server \
    --name metrics-server \
    --version 2.0.4 \
    --namespace metrics

 

설치가 정상적으로 진행되면 다음과 같은 로그를 볼 수 있다.


NAME:   metrics-server
LAST DEPLOYED: Sat Oct 19 08:03:30 2019
NAMESPACE: metrics
STATUS: DEPLOYED

RESOURCES:
==> v1/ClusterRole
NAME                   AGE
system:metrics-server  0s

==> v1/ClusterRoleBinding
NAME                                  AGE
metrics-server:system:auth-delegator  0s
system:metrics-server                 0s

==> v1/Pod(related)
NAME                             READY  STATUS             RESTARTS  AGE
metrics-server-5b5bfd85cf-crbn5  0/1    ContainerCreating  0         0s

==> v1/Service
NAME            TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)  AGE
metrics-server  ClusterIP  10.100.132.85  <none>       443/TCP  0s

==> v1/ServiceAccount
NAME            SECRETS  AGE
metrics-server  1        0s

==> v1beta1/APIService
NAME                    AGE
v1beta1.metrics.k8s.io  0s

==> v1beta1/RoleBinding
NAME                        AGE
metrics-server-auth-reader  0s

==> v1beta2/Deployment
NAME            READY  UP-TO-DATE  AVAILABLE  AGE
metrics-server  0/1    1           0          0s


NOTES:
The metric server has been deployed. 

In a few minutes you should be able to list metrics using the following
command:

  kubectl get --raw "/apis/metrics.k8s.io/v1beta1/nodes"

 

api service가 제대로 실행되고 있는지 확인을 해보면,


apiVersion: apiregistration.k8s.io/v1
kind: APIService
metadata:
  creationTimestamp: "2019-10-19T08:03:30Z"
  labels:
    app: metrics-server
    chart: metrics-server-2.0.4
    heritage: Tiller
    release: metrics-server
  name: v1beta1.metrics.k8s.io
  resourceVersion: "14654"
  selfLink: /apis/apiregistration.k8s.io/v1/apiservices/v1beta1.metrics.k8s.io
  uid: f03b0180-f246-11e9-b522-0245eea98ecc
spec:
  group: metrics.k8s.io
  groupPriorityMinimum: 100
  insecureSkipTLSVerify: true
  service:
    name: metrics-server
    namespace: metrics
  version: v1beta1
  versionPriority: 100
status:
  conditions:
  - lastTransitionTime: "2019-10-19T08:03:34Z"
    message: all checks passed
    reason: Passed
    status: "True"
    type: Available
    
ec2-user:~/environment $ kubectl get apiservice
NAME                                   SERVICE                  AVAILABLE   AGE
v1.                                    Local                    True        6h
v1.apps                                Local                    True        6h
v1.authentication.k8s.io               Local                    True        6h
v1.authorization.k8s.io                Local                    True        6h
v1.autoscaling                         Local                    True        6h
v1.batch                               Local                    True        6h
v1.networking.k8s.io                   Local                    True        6h
v1.rbac.authorization.k8s.io           Local                    True        6h
v1.storage.k8s.io                      Local                    True        6h
v1alpha1.crd.k8s.amazonaws.com         Local                    True        6h
v1beta1.admissionregistration.k8s.io   Local                    True        6h
v1beta1.apiextensions.k8s.io           Local                    True        6h
v1beta1.apps                           Local                    True        6h
v1beta1.authentication.k8s.io          Local                    True        6h
v1beta1.authorization.k8s.io           Local                    True        6h
v1beta1.batch                          Local                    True        6h
v1beta1.certificates.k8s.io            Local                    True        6h
v1beta1.coordination.k8s.io            Local                    True        6h
v1beta1.events.k8s.io                  Local                    True        6h
v1beta1.extensions                     Local                    True        6h
v1beta1.metrics.k8s.io                 metrics/metrics-server   True        3h
v1beta1.policy                         Local                    True        6h
v1beta1.rbac.authorization.k8s.io      Local                    True        6h
v1beta1.scheduling.k8s.io              Local                    True        6h
v1beta1.storage.k8s.io                 Local                    True        6h
v1beta2.apps                           Local                    True        6h
v2beta1.autoscaling                    Local                    True        6h
v2beta2.autoscaling                    Local                    True        6h

다음과 같이 available하고, vlbetal.metrics.k8s.io가 실행되고 있는 것을 알 수 있다.

 

 

 

이제 Kubernetes에서 제공하는 기본 앱 샘플을 생성할 것이다. 생성하면 그에 대한 deployment와 service가 생성된다.


c2-user:~/environment $ kubectl run php-apache --image=k8s.gcr.io/hpa-example --requests=cpu=200m --expose --port=80
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
service/php-apache created
deployment.apps/php-apache created
ec2-user:~/environment $ kubectl get service
kuNAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.100.0.1       <none>        443/TCP   8h
php-apache   ClusterIP   10.100.207.222   <none>        80/TCP    42s
ec2-user:~/environment $ kubectl get deployment
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
php-apache   1/1     1            1           48s

 

 

php-apache 앱에 대해서 hpa 오토 스케일링을 적용하기 위해서 다음 명령어를 입력하자. 입력 후, 오토 스케일링이 어떤 상태인지 까지 조회해보자.


ec2-user:~/environment $ kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled
ec2-user:~/environment $ kubectl get hpa
NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   0%/50%    1         10        1          38m

 

 

wget 요청을 지속적으로 앱에 보내어 부하를 가한다.


ec2-user:~/environment $ kubectl run -i --tty load-generator --image=busybox /bin/sh
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
If you don't see a command prompt, try pressing enter.
/ # while true; do wget -q -O - https://php-apache; done
OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK

 

 

그에 따라 지속적으로 hpa의 상태를 조회하면 다음과 같이 auto scaling이 적용이 되어 replica의 수가 늘어나는 것을 관찰 할 수 있다.


ec2-user:~/environment $ kubectl get hpa -w
NAME         REFERENCE               TARGETS   MINPODS   MAXPODS   REPLICAS   AGE
php-apache   Deployment/php-apache   0%/50%    1         10        1          76m
php-apache   Deployment/php-apache   209%/50%   1     10    1     76m
php-apache   Deployment/php-apache   209%/50%   1     10    4     76m
php-apache   Deployment/php-apache   209%/50%   1     10    5     77m

 

 

 

5-2. CA

 

먼저 다음을 입력하여 CA를 하기 위한 yml파일을 불러온다.


mkdir ~/environment/cluster-autoscaler
cd ~/environment/cluster-autoscaler
wget https://eksworkshop.com/scaling/deploy_ca.files/cluster_autoscaler.yml

 

 

다음, 노드를 생성할 때 같이 생성되었던 auto-scaling그룹의 최소 - 최대 용량을 2 - 8로 조정한다.

 

다음과 같이 구성하였으면, 이제 CA를 구성하겠다. 방금 다운받은 yml파일을 열어 132행에 있는 <AUTOSCALING GROUP NAME>을 자신의 auto-scaling group name으로 바꾼다. 필자는 이렇게 바꾸었다.

 


command:
            - ./cluster-autoscaler
            - --v=4
            - --stderrthreshold=info
            - --cloud-provider=aws
            - --skip-nodes-with-local-storage=false
            - --nodes=2:8:eksctl-eksworkshop-eksctl-nodegroup-ng-004e02c5-NodeGroup-1L9BVBKLDPZWY
          env:
            - name: AWS_REGION
              value: ap-southeast

 

 

다음, auto-scaling을 다룰 수 있는 권한을 다음 코드를 통해 추가한다.


mkdir ~/environment/asg_policy
cat <<EoF > ~/environment/asg_policy/k8s-asg-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "autoscaling:DescribeAutoScalingGroups",
        "autoscaling:DescribeAutoScalingInstances",
        "autoscaling:SetDesiredCapacity",
        "autoscaling:TerminateInstanceInAutoScalingGroup",
        "autoscaling:DescribeTags"
      ],
      "Resource": "*"
    }
  ]
}
EoF
aws iam put-role-policy --role-name $ROLE_NAME --policy-name ASG-Policy-For-Worker --policy-document file://~/environment/asg_policy/k8s-asg-policy.json

 

 

 

get-role하였을 때 다음과 같이 출력되면 정상적으로 적용이 된 것이다.

 

 

최종적으로 auto-scaler의 yml를 apply하게 되면, 다음과 같은 자원들이 생성된다.


kubectl apply -f ~/environment/cluster-autoscaler/cluster_autoscaler.yml
kubectl logs -f deployment/cluster-autoscaler -n kube-system

 

 

CA가 제대로 작동하는지 확인 하기 위해 샘플 앱의 deployment를 생성한다.


cat <<EoF> ~/environment/cluster-autoscaler/nginx.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-to-scaleout
spec:
  replicas: 1
  template:
    metadata:
      labels:
        service: nginx
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx-to-scaleout
        resources:
          limits:
            cpu: 500m
            memory: 512Mi
          requests:
            cpu: 500m
            memory: 512Mi
EoF
kubectl apply -f ~/environment/cluster-autoscaler/nginx.yaml
kubectl get deployment/nginx-to-scaleout

 

 

다음과 같이 deployment의 scale을 증가시킨 다음, 실제로 콘솔을 확인하면 node가 추가된 것을 알 수 있다. (저는 실패해서 원인 분석중입니다.)

 

 

6. Prometheus

SoundCloud에서 제작한 시스템 모니터링 및 알람 툴킷이다. 현재는 독립형 오픈소스 프로젝트가 되었으며, 모든 사용자가 독립적으로 유지보수한다. 이 프로젝트는 2016년 두 번째 호스팅 프로젝트로 Cloud Native Computing Foundation에 합류하였다.

프로메테우스의 기능을 정리해보았다.

  • metric 이름과 키-값으로 구분되는 다양한 시계열를 가진 다차원 데이터 모델을 지원한다.

  • 다차원 데이터를 처리하기 위해 PromQL이라는 질의어를 사용한다.

  • 분산 스토리지에 의존하지 않으며, 단일 서버 노드는 자율적이다.

  • HTTP의 pull model을 통해 시계열 수집이 일어난다.

  • 중개 게이트워이를 통해 시계열의 push를 지원한다.

  • 다양한 형태의 그래프 및 대시보드를 지원한다.

 

프로메테우스의 구조는 다음과 같다.

 

 

프로메테우스 설치

 

먼저 다음 명령어로 prometheus라는 이름을 가진 namespace를 만들고, helm을 통해서 prometheus를 설치한다.


kubectl create namespace prometheus
helm install stable/prometheus 
--name prometheus --namespace prometheus 
--set alertmanager.persistentVolume.storageClass="gp2" 
--set server.persistentVolume.storageClass="gp2"

 

 

설치가 완료되면, prometheus를 설치하며 생긴 리소스가 다음과 같이 출력된다.

 

 

서비스에 연결하기 위해 8080포트를 9090으로 포워딩시킨 후, /targets를 c9의 주소 뒤에 붙여서 앱의 preview를 확인한다. 정상적으로 이루어 졌다면 다음과 같은 화면을 볼 수 있다.

 

 

 

7. Grafana

 

grafana를 설치해보자. 다음 명령어로 helm으로 grafana를 불러오자.


kubectl create namespace grafana
helm install stable/grafana \
    --name grafana \
    --namespace grafana \
    --set persistence.storageClassName="gp2" \
    --set adminPassword="EKS!sAWSome" \
    --set datasources."datasources\.yaml".apiVersion=1 \
    --set datasources."datasources\.yaml".datasources[0].name=Prometheus \
    --set datasources."datasources\.yaml".datasources[0].type=prometheus \
    --set datasources."datasources\.yaml".datasources[0].url=http://prometheus-server.prometheus.svc.cluster.local \
    --set datasources."datasources\.yaml".datasources[0].access=proxy \
    --set datasources."datasources\.yaml".datasources[0].isDefault=true \
    --set service.type=LoadBalancer

 

 

설치 후, 다음과 같이 리소스들이 출력되면 정상이다.

 

 

 

kubectl get all -n grafana로 외부에 공개된 로드밸런서 주소를 확인 한다. 그리고, admin의 암호를 발급받는다.

해당 로드 밸런서 주소로 들어가면 로그인 화면이 노출된다.

 

접속이 되면, 다음과 같은 grafana 대시보드가 뜬다.

 

 

 

+를 눌러서 나오는 드롭박스의 import를 눌러서 3146을 다음과 같이 입력한다.

 

 

잠시 기다리면 다음과 같은 화면이 노출되며, Prometheus탭의 값을 Prometheus로 설정하고 Import를 클릭한다.

클릭하면 다음과 같은 화면이 노출된다.

1. Introduction

 

1.1 What is Kubernetes

쿠버네티스는 컨테이너를 쉽고 빠르게 배포/확장하고 관리를 자동화해주는 오픈소스 플랫폼이다. 

1주일에 수십억 개의 컨테이너를 생성하는 구글이 내부 배포시스템으로 사용하던 borg를 기반으로 오픈소스 프로젝트를 통해 빠르게 발전하고 있다.

단순한 컨테이너 플랫폼이 아닌 마이크로서비스, 클라우드 플랫폼을 지향하고 컨테이너로 이루어진 것들을 손쉽게 담고 관리할 수 있는 그릇 역할을 합니다. 서버리스, CI/CD, 머신러닝 등 다양한 기능을 쿠버네티스 상에서 이용할 수 있다.

 

 

 

Kubernetes가 제공하는 구체적인 항목은 다음과 같다.

1. Service Discovery and Load Balancing
Kubernetes는 DNS나 자체 ip를 이용하여 container를 외부에 노출 시킬 수 있다. 컨테이너에 트래픽이 몰리게 되면, kubernetes는 배포된 컨테이너가 안정적이도록 네트워크 트래픽을 LoadBalancing하고, 분산시킨다.

2. Storage Orchestration
Kubernetes는 local storage나 public cloud등 원하는 storage를 mount할 수 있다.

3. Automated Rollouts and Rollbacks
Kubernetes를 이용하여 컨테이너의 상태를 사용자가 지정한 속도에 맞추어 설정 할 수 있다.

4. Automatic bin packing
Kubernetes를 이용하여 컨테이너에 필요한 자원의 양을 지정할 수 있다.

5. Self-healing
오류가 난 컨테이너를 다시 시작하고, 다른 컨테이너로 대체 할 수 있으며, 이러한 과정을 클라이언트에 노출시키지 않는다.

 

 



1.2. Kubernetes Architecture

Kubernetes Cluster는 다음과 같이 구성되어 있다

 

 

 

1.2.1. Master Node (Control Plain)

Cluster의 뇌와 같은 역할을 하는 노드이다. Worker Node에 있는 컨테이너의 Orchestration을 담당한다.

  • API Server : Worker node와 통신하기 위한 api 서버.

  • etcd :클러스터의 상태를 key-value로 저장하는 내부 데이터베이스.

  • Scheduler : Node가 배정되지 않은 새로운 Pod를 감지하고, 이를 노드에 배정하는 기능을 담당한다.

  • Controller Manager : API Server를 통해 클러스터의 상태를 공유하는 역할을 한다. 컨트롤러는 다음과 같은 역할을 수행한다.

    • Node Control : 노드가 다운되었을 때, 통지와 대응에 관한 책임을 가짐.
    • Replication Control ; 시스템의 모든 오브젝트에 대해 적절한 수의 pod를 유지시키는 책임을 가짐.
    • Endpoint Control : service와 pod를 연결시키는 책임을 가짐
    • Service Account & Token Control : 새로운 namespace에 대한 기본 계정과 API 접근 토큰을 생성한다.

 

 

 

1.2.2. Worker Node

실제 컨테이너 이미지를 실행시키는 역할을 하며, 각 노드에는 여러개의 컨테이너가 올 수 있다.

  • Kube Proxy : node의 네트워킹 서비스를 반영하는 프록시이다. 사용자는 이 proxy를 통해 각 node에 접근 가능하다.

  • kubelet : Master Node와 통신을 담당하는 구성요소

  • Controller Runtime(Pods) : 실제 기능을 담당하는 여러개의 Pod로 구성되어있다.

 

 

 

1.2.3. Pod

하나 이상의 스토리지 및 네트워크를 공유하고 해당 컨테이너를 구동하는 방식에 대한 명세를 가지는 컨테이너 그룹이다. 이를 통해 어플리케이션에 대한 헬퍼 프로그램을 지원할 수 있으며, 그 예는 다음과 같다.

  • 컨텐츠 관리 시스템, 파일과 데이터 로더, 로컬 캐시 관리 등.

  • 로그와 백업 체크포인트, 압축, 로테이션, 스냅샷 등.

  • 데이터 변동 감시자, 로그 추적자, 로깅 및 모니터링 어댑터, 이벤트 관리 등.

  • 프록시, 브릿지, 어댑터

  • 컨트롤러, 매니저, 설정, 업데이트

 

 

1.2.4. Service

여러개의 pod로 이루어진 고정 ip를 할당받은 논리적인 단위이다. Pod이 어디있든 service를 통해 pod으로 접근 할 수 있다.

 

다음은 Service의 종류에 대해서 서술해보았다.

  • Cluster IP : 클러스터 내부에서 할당받은 ip이다. Kubectl을 이용하여 service를 생성하게 되면 기본으로 구성되게 되어있다.
  • Node Port : 클러스터 내부에서 할당받은 서비스 포트. 이 옵션으로 생성 시, Cluster IP가 자동으로 함께 만들어지며, 이 포트를 통해 외부에서 <Note IP>:<Node Port>의 형태로 접근이 가능하다.
  • Load Balancer : 서비스를 외부적으로 노출 시킬 수 있다. 이 옵션으로 생성시, Node Port와 Cluster IP가 자동으로 생성된다.
  • External Name : 서비스를 외부 CNAME레코드와 매핑시켜준다.

 

 


1.2.5 Deployment

pod는 쿠버네티스의 가장 기본적인 단위이지만, cluster에서 직접 시작되지 않는다. 대신, 추상화 계층인 deployment로 관리된다. 배포의 기본적인 목적은 pod의 복제본과 구체적인 spec에 대해 명세하는 것이며, deployment가 클러스터에 추가되면 그에 해당하는 spec에 맞게 app이 실행되고, 모니터링 됩니다. pod에 장애 발생 시, deployment에 명시된 spec에 따라 자동으로 다시 pod를 생성하고 관리한다.

 

다음 그림은 pod-service-deployment에 대한 모식도를 나타낸 것입니다. 

출처 : https://medium.com/google-cloud/kubernetes-101-pods-nodes-containers-and-clusters-c1509e409e16

 

 

 

2. Elastic Kubernetes Service

 

 

2.1. EKS Architecture Best Practice

AWS가 권장하는 EKS Architecture best practice는 다음과 같다.

출처 : AWS EKS QuickStart

 

위 아키텍처와 같이 Kubernetes 노드를 여러 AZ에 private subnet에 배포하여 보안성과 가용성, 자동프로비저닝을 잡고, public subnet를 각 private subnet의 전방에 배치함으로써 proxy 및 bastion host역할을 부여하게 할 수 있다.

 

 

2.2. EKS Data Flow Architecture

위 best practice를 설명하기 위해 data I/O를 포함하여 아키텍처를 다시 그려보았다.

위 아키텍처에서 우리가 주목해야 할 부분은 AWS ALB Ingress Controller이다.

ALB Ingress Controller를 한마디로 말하자면, EKS와 클러스터 간 변동 명세를에 api server로 전달하는 provider이다. 이러한 방식을 통해 AWS에서 적용 할 수 있는 서비스를 cluster내부의 node 및 pod에 대해 적용 가능하게 된다.

ex) Resource Scaling, Container Management, Versioning, traffic control, state monitoring ….

위 아키텍처의 데이터 흐름을 포함한 작동 방식은 다음의 순서를 따른다.

 

  1. 지속적으로 EKS APi Seerver로부터 ALB Ingress Controller는 모니터링은 받는다.
  2. yaml 파일 업데이트 또는 터미널 입력 등으로 부터 AWS 자원의 변동사항 명세를 입력 받는다.
  3. 이러한 입력을 ALB Ingress Controller가 모니터링을 한다.
  4. ALB Ingress Controller는 EKS API Server로 모니터링 도중 발생한 변경사항에 대해 전달을 한다.
  5. 변동사항 명세를 받은 EKS(Control Plain)는 AWS 리소스(Worker Group)에 대해 변동사항을 적용한다.

 

 

 

3. Conclusion

다음과 같은 요소를 종합하였을 때, 우리는 쿠버네티스를 다음과 같은 이유로 사용 한다고 할 수 있다.

  • 많은 호스트에서 운영되는 컨테이너들을 쉽게 조작하고 모니터링 한다.

  • 컨테이너가 언제 시작되고 멈추는지 결정 할 수 있다.

  • 새로운 코드를 빠르게 호스트 안의 컨테이너에 적용 할 수 있다.

  • 컨테이너의 트레픽과 라우팅을 관리할 수 있다.

Virtualization

기존에는 하나의 app이나 task에 하나의 전용서버를 할당하였다. 이렇게 함으로써 app에서 발생하는 문제의 원인을 쉽게 발견 할 수 있었으며, 네트워크 구성이 용이했다.

그러나 이는 서버의 능력 중 극히 적은 능력만을 사용하여 효율성이 떨어지고, 네트워크 규모가 커지고 복잡해짐에 따라 서버가 점유하는 물리적 공간이 커지고 그에 따라 전력 소모도 심해진다.

이를 해결하고 전략을 세우기 위해 물리서버의 최대 처리능력만큼 여러개의 가상머신으로 분할하여 물리서버의 처리 능력을 최대한 활용 할 수 있게 한 것이 (서버) 가상화이다.

가상화를 하게 되면, 원래 os와 가상화한 파티션의 os를 다르게 설정 할 수 있다. 이 때, 상이한 os의 명령 또는 동작을 해석하여 하드웨어에 전달해줌으로써 인터페이스를 제공하는 것이 hypervisor이다.

즉, 위와 같은 역할을 hypervisor가 해줌으로써 widnow상에서 CentOS같은 운영체제를 사용 할 수 있다. VMWare, Parallels같은 것도 hypervisor이다.

hypervisor는 bare-metal형 hypervisor(type 1), os위에 올려져서 쓰이는 hypervisor(type 2)으로 나뉜다.

 

 

Type 1 (Native or Bare-Metal Hypervisor)

하이퍼바이저가 OS에 종속되지 않고, 하드웨어에 직접 설치된다.
보통 OS가 하드웨어를 컨트롤 하지만, type 1방식의 Hypervisor를 절치하게 되면, Hypervisor가 직접 하드웨어를 제어 할 수 있기 때문에 OS의 Overhead가 적어지고, 자원 관리가 유연해진다.

그러나 Hypervisor 자체에는 별도의 관리기능이 없기 때문에 이 방식에서는 구동 시 DOM0(Hypervisor 구동시 Hypervisor가 실행시키는 도메인)라는 관리 머신이 함께 구동된다. DOM0에서 얼마나 일을 부담하는가에 따라 전가상화, 반가상화로 나뉜다.

 

 

Full-Virtualization (전가상화)

로마에 가면 로마법을 따르는 것 처럼, 각 OS는 고유의 규칙을 가지고 있고 같은 것을 표현하는 방식도 OS마다 틀리다. 각기 다른 표현을 Hypervisor(엄밀히 말하면 DOM0)가 핸들링하여 Hardware가 알아 먹을 수 있는 표현으로 변환 한 다음, Hardware에게 전달한다.


https://blog.naver.com/alice_k106/220218878967 -> 사진 출처 (Alice님 Blog)

 

 

Para-Virtualization (반가상화)

전가상화에서는 Hypervisor(엄밀히 말하면 DOM0)가 각기 다른 표현을 번역하여 Hardware에 전달하였지만, 반가상화에서는 표현하는 사람이 직접 하드웨어가 알아먹게 전달하는 방식(Hyper-Call)이다. 즉, 우리가 영어권으로 여행가면 한국어를 안쓰고 영어를 사용하여 대화하는 것과 마찬가지다.
하지만, 기본적으로 커널에는 Hyper-Call에 대한 정보가 없다. 그래서 가상 OS의 커널을 직접 수정해야하는 단점이 있다. 그러나 DOM0를 거치는 횟수가 줄기 때문에 전가상화에 대비하여 좋은 퍼포먼스를 보인다.


https://blog.naver.com/alice_k106/220218878967 -> 사진 출처 (Alice님 Blog)

Type 2

일반적으로 우리가 쓰는 VMWare같은 것과 같다.
Hardware에 OS를 설치하고, 그 위에 Hypervisor를 설치한다.
OS가 Hardware를 관리하기 때문에 DOM0가 필요없다. 

그러나 OS위에 Hypervisor가 구동되고 그 위에 가상화가 이루어 지는 방식이기 때문에 Overhead가 크다.

1. Timeout Error 상황의 리소스 현황 및 Data Flow

 

 

 

1-1. 리소스 현황

  • 활성화된 로드밸런서 가용영역 : private-2a, private-2c

  • 대상 그룹 : private-2a와 private-2c subnet에 있는 인스턴스 ip address

  • 리스너 : HTTP 80

  • 목적지 : Tomcat 8080

  • Public ip는 오직 public subnet의 인스턴스만 할당.

  • IGW 및 NAT 서브넷 연결현황은 다음과 같다.

    • IGW : public-2a, public-2c
    • NAT : private-2a, private-2c, db subnet

 

 

1-2. Data Flow

1. 사용자는 Public DNS를 통해 ALB에 접근한다.

2. ALB의 트래픽은 IGW를 통해 VPC내로 들어온다.

3. 트래픽은 활성화된 로드밸런서 가용영역 subnet만 참조하게 된다.

3-1. 활성화 된 로드밸런서 가용영역 subnet은 private subnet이다.

3-2. private subnet안에 있는 로드밸런싱 대상의 인스턴스는 public 주소 또는 탄력적 ip가 없다.

3-3. public ip 또는 탄력적 ip없이는 외부와 통신을 할 수 없다.

3-4. 로드밸런서는 계속해서 활성화된 로드밸런서 가용영역 서브넷으로 브로드캐스트 주소로 패킷을 전송하다 유휴제한시간을 초과한다.

3-5. 최종적으로 사용자에게 timeout error을 출력한다.

 

 

 

2. Issue 해결 솔루션 및 Data Flow

 

 

 

2-1. 문제점 분석 및 해결법

먼저 로드밸런서 가용영역에 대해 알아보자. 로드밸런서 가용영역이란, 일반적인 우리가 알고 있는 AZ가 아닌 로드밸런서가 접근할 수 있는 subnet을 의미 한다. 이에 대해서는 다음 캡쳐본을 참고한다.

 

현재 로드밸런서는 활성화된 가용 영역(서브넷)이 private subnet이다. private subnet은 igw와 연결되어있지 않으며, 로드밸런싱 대상은 public ip 또는 탄력적 ip가 할당되어 있지 않기 때문에 외부 트래픽을 다룰 수 없게 된다.

이를 해결하기 위해서는 로드밸런서의 활성화된 가용영역(서브넷)을 public subnet으로 설정해준다. public 서브넷은 외부와는 물론 내부 서브넷과 통신이 가능하므로 public subnet을 거쳐 로드밸런싱 대상으로 트래픽을 전달하면 쌍방으로 통신이 가능해지기 때문에 위와 같은 이슈를 해결 할 수 있다.

 

 

2-2. Data Flow Diagram

다음과 같이 public subnet을 통해서 private subnet의 인스턴스로 접근하게 설정해준다.

 

 

3. 참고문헌

http://thebluenode.com/exposing-private-ec2-instances-behind-public-elastic-load-balancer-elb-aws

https://aws.amazon.com/ko/premiumsupport/knowledge-center/public-load-balancer-private-ec2/?nc1=h_ls

https://docs.aws.amazon.com/ko_kr/elasticloadbalancing/latest/userguide/how-elastic-load-balancing-works.html

https://docs.cloud.oracle.com/iaas/Content/Balance/Concepts/balanceoverview.htm

+ Recent posts