<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Thomas Kim</title>
    <description>The latest articles on DEV Community by Thomas Kim (@airoasis).</description>
    <link>https://dev.to/airoasis</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F711060%2F0f74fc19-392f-4bb3-912f-8a29cc05b000.jpeg</url>
      <title>DEV Community: Thomas Kim</title>
      <link>https://dev.to/airoasis</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/airoasis"/>
    <language>en</language>
    <item>
      <title>Kubernetes 의 Autoscaling</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Tue, 15 Mar 2022 08:10:33 +0000</pubDate>
      <link>https://dev.to/airoasis/kubernetes-autoscaler-6d7</link>
      <guid>https://dev.to/airoasis/kubernetes-autoscaler-6d7</guid>
      <description>&lt;h2&gt;
  
  
  들어가며
&lt;/h2&gt;

&lt;p&gt;Kubernetes를 사용하는 큰 이유중 하나가 autoscaling 때문이다. 하지만 autoscaling을 제대로 사용하기 위해서는 resource request와 limit 설정 및 Horizontal Pod Autoscaler (HPA), Cluster Autoscaler (CA) 의 설정이 반드시 필요하다. &lt;br&gt;
최근 서비스 오픈을 위해 performance testing을 하며 Kubernetes의 Autoscaling을 사용하면서 알게 된 내용을 정리하였다.  &lt;/p&gt;
&lt;h2&gt;
  
  
  1. Kubernetes 의 Autoscaling
&lt;/h2&gt;

&lt;p&gt;Kubernetes 에서 autoscaling 을 할 수 있는 방법은 세가지가 있다. VPA (Vertical Pod Autoscaler), HPA, (Horizontal Pod Autoscaler), CA (Cluster Autoscaler)를 사용하여 변화하는 request 에 따라 autoscale 이 가능하다. 보통 운영에서는 VPA를 이용하여 적절한 resource request / limit 값을 찾는데 활용하고, 실제로 사용하는 방법은 HPA 와 CA이다. (VPA 관련 내용은 &lt;a href="https://dev.to/airoasis/kubernetes-yi-resources-seoljeongeul-wihan-vpa-mic-goldilocks-3503"&gt;Kubernetes 의 Resources 설정을 위한 VPA 및 Goldilocks&lt;/a&gt; 참고).&lt;/p&gt;

&lt;p&gt;여기서 HPA의 역할은 CPU나 Memory 의 사용량에 따라 Pod를 scale-out 하는 용도로 사용하고, HPA로 인해 새로운 Pod들이 생겨나서 더이상 available 한 node 로는 scheduling을 못하게 되는 상황에 Node를 scale-out 하기 위해 CA가 사용된다. &lt;/p&gt;
&lt;h2&gt;
  
  
  2. Kubernetes Resouce Request 와 Limit 설정
&lt;/h2&gt;

&lt;p&gt;사실 request, limit 설정의 정답은 없다. 하지만 cpu 와 memory 의 특성에 의해 &lt;strong&gt;cpu 는 request만 설정하고, memory는 request와 limit을 동일하게 설정하는것을 추천한다&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;이렇게 설정하는 이유는 다음과 같다. cpu는 compressible resource라 여러 pod가 cpu를 서로 사용하려고 할 때에 서비스는 단지 throttling 되어 처리시간이 좀 더 걸리지만 memory는 incompressible resource라 memory 가 부족하면 OOM(out of memory)으로 Pod가 종료되고 새롭게 scheduling 되어야 한다. 따라서 memory 의 request를 낮게 설정하여 여유롭지 않은 node 에 배치되었는데 Java와 같이 올라갈때 memory 가 많이 필요한 서비스의 경우 해당 node 에서 memory가 부족하여 올라가지도 못하고 바로 종료되는 상황이 발생할 수 있다. &lt;/p&gt;

&lt;p&gt;그리고 중요한 점은 운영환경의 &lt;strong&gt;Kubernetes에 배포되는 모든 서비스의 request / limit 설정을 해주어야 한다&lt;/strong&gt;는 것이다. (개발중에는 설정하지 않아도 문제가 되지는 않는다) 이를 설정하지 않으면 Pod는 Node의 자원 전체를 사용할 수 있다는 의미와 동일하다. 결국 kubernetes는 정확한 scheduling 을 할 수가 없고, 특정 node 에 많은 pod가 배치될 수 있고, resource 부족이 생기는 경우 설정이 없는 pod들을 eviction 대상으로 보고 많은 pod들이 재배치되는 문제가 생기게 된다. &lt;/p&gt;

&lt;p&gt;결국 autoscaling 은 request / limit 설정 없이는 무용지물이다. &lt;/p&gt;
&lt;h2&gt;
  
  
  3. HPA 설정
&lt;/h2&gt;

&lt;p&gt;HPA 설정에서 중요한건 다음 두가지 이다&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;🛑 HPA를 사용하면 deployment 의 replicas 설정은 반드시 반드시 제거해야 한다.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🛑 HPA 가 scale-out 하는 기준은 resource limit 이 아닌 request 이다&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Replicas 설정 제거
&lt;/h3&gt;

&lt;p&gt;관련 내용은 &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#migrating-deployments-and-statefulsets-to-horizontal-autoscaling"&gt;Migrating Deployments and StatefulSets to horizontal autoscaling&lt;/a&gt; 에 자세히 설명되어 있다. 하지만 페이지의 거의 끝부분에 있어서 놓치고 지나치기 쉽다...&lt;/p&gt;

&lt;p&gt;HPA 도입 이전에는 deployment 의 &lt;code&gt;replicas&lt;/code&gt; 설정으로 pod의 갯수를 설정한다. 하지만 HPA를 도입하면 반드시 &lt;strong&gt;deployment 의 &lt;code&gt;replicas&lt;/code&gt; 설정을 제거하고 HPA의 &lt;code&gt;mixReplicas&lt;/code&gt;, &lt;code&gt;maxReplicas&lt;/code&gt; 설정으로 대신하여야 한다&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;만약 두군데 모두 설정이 있으면 kubernetes 는 예를 들어 배포와 같이 &lt;code&gt;kubectl apply -f deployment.yaml&lt;/code&gt; command 를 사용하게 되는 경우 무조건 deployment 의 replicas 설정으로 회귀하도록 한다. &lt;/p&gt;

&lt;p&gt;예를 들어 deployment의 replicas 설정이 2이고 HPA로 인해 pod가 10개까지 늘어난 상황에서 서비스가 되고 있는데 이때 배포가 일어나면 갑자기 pod는 2개로 줄어들었다가 다시 HPA로 인해 필요한 pod들이 생기는 상황이 발생한다. &lt;/p&gt;

&lt;p&gt;또한 Argo CD를 사용하는 경우 pod가 2개가 아닌 경우 계속 &lt;code&gt;OutOfSync&lt;/code&gt;를 보게 될 것이다... &lt;/p&gt;
&lt;h3&gt;
  
  
  HPA 의 scale-out 기준
&lt;/h3&gt;

&lt;p&gt;많은 경우 HPA의 scale-out 기준이 resouce limit으로 착각하는 경우가 있다. 하지만 HPA의 동작은 &lt;strong&gt;resource request&lt;/strong&gt;기준이다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server:latest&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5Gi&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5Gi&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling/v2beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HorizontalPodAutoscaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server-hpa&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
        &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;예를 들어 위와 같은 설정에서 HPA는 CPU 가 900밀리코어일때 scale-out을 시작한다. 참고로 여기서 &lt;code&gt;averageUtilization&lt;/code&gt; 값은 100을 넘어선 값도 설정이 가능하다. &lt;/p&gt;

&lt;h2&gt;
  
  
  4. Pod Disruption Budget (PDB)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PodDisruptionBudget&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server-pdb&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;minAvailable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PDB는 운영에서 반드시 필요한 설정이다. Pod는 항상 설정된 replica의 수 만큼 유지되지만 시스템 관리로 인해 특정 node를 다운 시켜야 하는 경우, 또는 cluster autoscaler 가 node의 수를 줄이는 경우 등과 같은 이유로 pod의 수가 줄어들어야 하는 경우가 있다.&lt;/p&gt;

&lt;p&gt;이런 경우 PDB를 통해 최소한 운영 가능한 pod의 비율/개수를 정하거나 최대 서비스 가능하지 않은 pod의 비율/개수를 정하여 서비스의 안정성을 보장한다. 여기에서는 최소 1개의 pod가 항상 보장되게 설정하였다. 결국 node가 scale down 되어야 하는 상황에서 최소 보장되어야 하는 PDB 설정을 만족하지 못하는 해당 node는 다운되지 않고 기다리다가 만족하는 상황이 되면 그때 다운되어진다.&lt;/p&gt;

&lt;h2&gt;
  
  
  마치며
&lt;/h2&gt;

&lt;p&gt;운영환경에서 제대로 autoscaling을 사용하기는 쉽지않다... 각 서비스에 따라 resource request/limit 설정을 달리 하여야 하고, HPA가 동작할때 새로 생기는 pod 가 ready 될 때까지 견딜 수 있도록 적절한 target 을 정하는 것, 그리고 새로운 pod 가 생성되고 termination 될 때 request의 유실이 없도록 application 및 kuernetes 의 설정이 필요하다. &lt;/p&gt;

&lt;p&gt;한번에 이 모든걸 완벽하게 해결하기 보다는 운영을 하면서 문제점을 하나씩 해결해 나가는 방법이 적절한거 같다. &lt;/p&gt;

&lt;p&gt;Java 서비스의 경우 &lt;a href="https://dev.to/airoasis/kubernetes-reul-wihan-spring-boot-gaebal-feat-mujungdan-baepounyeong-2k0o"&gt;Kubernetes 를 위한 Spring Boot 개발 (feat. 무중단 배포/운영)&lt;/a&gt; 와 &lt;a href="https://dev.to/airoasis/spring-boot-seobiseureul-wihan-kubernetes-seoljeong-3d72"&gt;Spring Boot 서비스를 위한 Kubernetes 설정&lt;/a&gt; 를 참고하면 좋을거 같다. &lt;/p&gt;

</description>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Kubernetes 에서 Datadog Java (Spring Boot) APM 적용하기</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 08:49:44 +0000</pubDate>
      <link>https://dev.to/airoasis/kubernetes-datadog-spring-boot-3g34</link>
      <guid>https://dev.to/airoasis/kubernetes-datadog-spring-boot-3g34</guid>
      <description>&lt;h2&gt;
  
  
  들어가며
&lt;/h2&gt;

&lt;p&gt;현재 진행중인 프로젝트는 Kubernetes (AWS EKS) 에서 Istio 를 사용하면서 Java, Go, Python 등으로 개발 된 서비스를 개발/운영 중이다. 로깅, 모니터링, APM 을 위해서는 Datadog을 사용중이고, 여기서는 Datadog의 Java (Spring Boot) APM 적용을 정리하려고 한다. &lt;/p&gt;

&lt;h2&gt;
  
  
  Kubernetes 에서 Datadog 설치
&lt;/h2&gt;

&lt;p&gt;Kubernetes 에서 Datadog Agent 를 설치하고 설정하는 내용은 &lt;a href="https://dev.to/airoasis/kubernetesistio-reul-wihan-datadog-seoljeong-471k"&gt;Kubernetes/Istio 를 위한 Datadog 설정&lt;/a&gt; 에 설명하였다. 해당 Post의 내용대로 Datadog 을 설치하면 Kubernetes/Istio 모니터링, 모든 Pod (컨테이너)의 로그 수집, APM 을 받을 수 있는 상태로 Datadog Agent 및 Datadog Cluster Agent가 설정 완료된다. 만약 Istio를 사용하지 않는다면 "Istio 설정 변경" 부분만 적용하지 않으면 된다. &lt;/p&gt;

&lt;p&gt;여기에서 중요한건 &lt;code&gt;DD_DOGSTATSD_NON_LOCAL_TRAFFIC&lt;/code&gt; 를 설정해야 JVM Metrics 까지 모니터링 할 수 있다. &lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot 서비스를 위한 Kubernetes deployment 리소스 생성
&lt;/h2&gt;

&lt;p&gt;아래와 같이 deployment를 생성하면 Datadog 에서 해당 서비스의 APM 및 JVM Metrics, Profiling Metrics, Kubernetes Metrics 등 모니터링이 가능하다. Datadog java agent는 Spring Boot 서비스에 Java Agent 로 올라가고 일반 Tracing 데이터는 Spring Boot 이 올라간 Pod가 속한 Node에 DaemonSet으로 deploy된 Datadog Agent의 tracing-agent서비스 (Port: 8126)로 수집한 데이터를 보내고 JVM Metrics 정보는 agent 서비스 (Post: 8125)로 보내게 된다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-apm-agent&lt;/span&gt; 
        &lt;span class="na"&gt;emptyDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mwp/main-server:latest&lt;/span&gt;
        &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-apm-agent&lt;/span&gt; 
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/datadog/apm/agent&lt;/span&gt; 
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_AGENT_HOST&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;status.hostIP&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_SERVICE&lt;/span&gt;
            &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;fieldRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;fieldPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;metadata.labels['app']&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_ENV&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;develop"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_VERSION&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.0"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_TRACE_ENABLED&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_TRACE_SAMPLE_RATE&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_PROFILING_ENABLED&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_LOGS_INJECTION&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_JMXFETCH_ENABLED&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
      &lt;span class="na"&gt;initContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-apm-agent&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;busybox&lt;/span&gt;
        &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wget&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-O&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/datadog/apm/agent/dd-java-agent.jar&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;https://dtdg.co/latest-java-tracer&lt;/span&gt;
        &lt;span class="na"&gt;volumeMounts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-apm-agent&lt;/span&gt;
          &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/datadog/apm/agent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Datadog 문서에는 java agent를 다운받아서 사용하라고 되어있고, 직접 소스코드에서 다운 받아서 사용할 수도 있다. 하지만 좀 더 Kubernets스럽게? 하는 방법은 위에서와 같이 Pod 에서 사용할 volume을 만들고 &lt;code&gt;initContainers&lt;/code&gt;를 사용하여 Pod가 올라가기 전에 &lt;code&gt;wget&lt;/code&gt;으로 java agent를 다운 받아서 mount한 volume 에 저장하고, 실제 Spring Boot 이 올라가는 container 에서 해당 volume 에 저장된 java agent 를 사용하는 방법이다. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DD_AGENT_HOST&lt;/code&gt;: Pod가 속한 Node에 DeamonSet으로 deploy된 Datadog Agent로 APM 데이터를 보내기 위해 &lt;code&gt;status.hostIP&lt;/code&gt; 를 사용하여 node의 IP를 설정한다. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DD_SERVICE&lt;/code&gt;: Datadog APM 에서 보여지는 서비스명으로 metadata의 app 이름으로 설정하였다. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DD_TRACE_ENABLED&lt;/code&gt;: 다양한 환경, 예를 들어 dev/stg/prd 와 같이 다양한 환경에서 APM 의 사용 여부를 정할 때 유용하게 사용 가능&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DD_TRACE_SAMPLE_RATE&lt;/code&gt;: 수집하는 데이터의 sampling rate을 설정한다. 1은 100%를 의미한다. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DD_PROFILING_ENABLED&lt;/code&gt;: Datadog Continuous Profiler 를 활성화 하는 것으로 매우 매우 낮은 overhead로 code issue나 performance regression을 확인할 수 있다. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DD_JMXFETCH_ENABLED&lt;/code&gt;: JVM Metrics를 활성화 하기 위해 필요하다. 문서에는 default 값이 true라고 되어 있지만 명시적으로 이렇게 하지 않으면 JVM Metrics 가 나오지 않는다... (버그인거 같다)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;🤚 위에 있는 Deployment 는 Datadog의 Java agent를 설명하기 위한 최소한의 설정이다. 실제 운영환경에서 필요한 Spring Boot, Kubernetes 관련 개발/설정은 &lt;a href="https://dev.to/airoasis/series/16894"&gt;Kubernetes 에서 Spring Boot 서비스 개발하기 시리즈&lt;/a&gt;에 정리하였다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  마치며
&lt;/h2&gt;

&lt;p&gt;이번에 상용을 앞둔 서비스를 LoadRunner를 통해 테스팅을 하였다. Kubernetes의 HPA, CA를 사용하면서 문제점을 확인하고 troubleshooting을 하는데 Datadog의 monitoring 과 APM 은 정말 정말 정말 유용하게 사용되었다. 이와 관련된 글은 다른 Post 에 정리할 예정이다. &lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.datadoghq.com/tracing/setup_overview/setup/java/?tab=springboot"&gt;https://docs.datadoghq.com/tracing/setup_overview/setup/java/?tab=springboot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.datadoghq.com/tracing/runtime_metrics/java/"&gt;https://docs.datadoghq.com/tracing/runtime_metrics/java/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#setup"&gt;https://docs.datadoghq.com/developers/dogstatsd/?tab=kubernetes#setup&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>java</category>
      <category>datadog</category>
    </item>
    <item>
      <title>Argo CD 설치 및 설정</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:52:19 +0000</pubDate>
      <link>https://dev.to/airoasis/argo-cd-seolci-mic-seoljeong-45bd</link>
      <guid>https://dev.to/airoasis/argo-cd-seolci-mic-seoljeong-45bd</guid>
      <description>&lt;p&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;ArgoCD&lt;/a&gt;는 &lt;a href="https://www.weave.works/technologies/gitops/" rel="noopener noreferrer"&gt;GitOps&lt;/a&gt; 로 관리되는 Kubernetes manifests의 변경사항을 Monitoring 하며 실제 Cluster 에 배포된 형태를 이와 동일하게 계속 유지시키는 역할을 한다. &lt;/p&gt;

&lt;h2&gt;
  
  
  Argo CD 설치
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/getting_started/" rel="noopener noreferrer"&gt;https://argo-cd.readthedocs.io/en/stable/getting_started/&lt;/a&gt;&lt;br&gt;
Argo CD를 kubernetes cluster 에 설치&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create namespace argocd
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-f&lt;/span&gt; https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/ha/install.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;운영에서는 HA 버전의 Argo CD를 설치한다. &lt;/p&gt;




&lt;h2&gt;
  
  
  Argo CD CLI 설치
&lt;/h2&gt;

&lt;p&gt;Argo CD CLI 설치&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; ~/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/bin/argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Argo CD 서비스 노출
&lt;/h2&gt;

&lt;p&gt;Argo CD는 default로 서버를 외부로 노출시키지 않는다. 아래와 같이 서비스타입을 LoadBalancer로 변경하여 외부로 노출시킨다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl patch svc argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec": {"type": "LoadBalancer"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  admin password 변경
&lt;/h2&gt;

&lt;p&gt;Argo CD는 최초 &lt;code&gt;admin&lt;/code&gt; account의 초기 password를 kubernetes 의 secret 으로 저장해 놓는다. 아래와 같이 password를 얻는다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; argocd get secret argocd-initial-admin-secret &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Argo CD CLI를 이용하여 Argo CD에 로그인한다. 우선 생성된 Load Balancer의 주소를 얻는다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc argocd-server &lt;span class="nt"&gt;-n&lt;/span&gt; argocd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;그리고 Login 한다. Username은 &lt;code&gt;admin&lt;/code&gt; 이다&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;argocd login &amp;lt;ARGOCD_SERVER_DOMAIN&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;admin 유저의 password를 업데이트 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;argocd account update-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;blockquote&gt;
&lt;p&gt;Argo CD는 Git repository를 3분에 한번씩 pollin 하면서 실제 kubernetes cluster 와 다른점을 확인한다. 따라서 배포시에 운이 없다면 최대 3분을 기다려야 Argo CD 가 변경된 image를 배포하게 된다. 이렇게 Polling 으로 인한 delay 를 없애고 싶다면 Git repository 에 Argo CD로 webhook을 만들어 놓으면 된다. 아래 link 참고&lt;br&gt;
&lt;a href="https://argo-cd.readthedocs.io/en/stable/operator-manual/webhook/" rel="noopener noreferrer"&gt;https://argo-cd.readthedocs.io/en/stable/operator-manual/webhook/&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;그리고 보통 Argo CD는 내부 개발자나 운영자 외에는 접근을 막기 위해 Security group 에서 특정 IP 만 inbound 로 열어 놓는 경우가 있다. 이 때 위와 같이 webhook 을 만들어 놓았다면 반드시 Argo CD의 load balancer에 Github 의 webhook 관련 API 도 inbound로 열어줘야 한다. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;아래 링크에 GitHub 의 IP 주소 관련 내용이 있고, &lt;br&gt;
&lt;a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses" rel="noopener noreferrer"&gt;https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/about-githubs-ip-addresses&lt;/a&gt;&lt;br&gt;
실제 inbound 에 넣줘야 하는 IP는 아래에서 확인할 수 있다.hooks 에 있는 리스트를 넣어주면 된다.&lt;br&gt;
&lt;a href="https://api.github.com/meta" rel="noopener noreferrer"&gt;https://api.github.com/meta&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://argocd-notifications.readthedocs.io/en/stable/" rel="noopener noreferrer"&gt;Argo CD Notification&lt;/a&gt; 을 사용하여 Slack 에 notification 을 보내는 설정도 하면 개발팀에서 배포가 잘 안된 경우, 알림을 받을 수 있다. &lt;/p&gt;

&lt;p&gt;Argo CD의 &lt;a href="https://github.com/argoproj-labs/applicationset" rel="noopener noreferrer"&gt;ApplicationSet&lt;/a&gt; 을 활용하여 프로젝트를 셋업하는 방법도 있다. 예전에는 App of Apps 를 사용했다면 여기서 좀 더 발전된 개념이 Application Sets 이다.&lt;/p&gt;

&lt;p&gt;GitOps는 현재 1.0이 공식 버전이고 곧 업데이트 된 개념이 나올 예정이다.&lt;br&gt;
&lt;a href="https://opengitops.dev/" rel="noopener noreferrer"&gt;https://opengitops.dev/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/open-gitops/documents" rel="noopener noreferrer"&gt;https://github.com/open-gitops/documents&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;GitOps 1.0 의 문제점은 &lt;a href="https://codefresh.io/about-gitops/pains-gitops-1-0/" rel="noopener noreferrer"&gt;The pains of GitOps 1.0&lt;/a&gt; 참고. &lt;br&gt;
현재 Kubernetes 의 configuration 관리를 위해 kustomize를 사용중이고 Argo CD 에서 kustomize를 이용한 배포를 한다. 만약 kustomize나 helm에서 branching을 사용한다면 &lt;a href="https://codefresh.io/about-gitops/branches-gitops-environments/" rel="noopener noreferrer"&gt;Stop Using Branches for Deploying to Different GitOps Environments&lt;/a&gt; 를 읽어보면 도움이 될거 같다. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;실제 Argo CD를 사용하고 있는 스크린샷&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7ni1swapvf8kvtupr0v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff7ni1swapvf8kvtupr0v.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>argocd</category>
      <category>gitops</category>
    </item>
    <item>
      <title>Kubernetes/Istio 를 위한 Datadog 설정</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:50:35 +0000</pubDate>
      <link>https://dev.to/airoasis/kubernetesistio-reul-wihan-datadog-seoljeong-471k</link>
      <guid>https://dev.to/airoasis/kubernetesistio-reul-wihan-datadog-seoljeong-471k</guid>
      <description>&lt;p&gt;Datadog은 Helm 또는 Operator를 통해 설정 가능하다. 여기에도 역시 Operator를 사용한다. Datadog을 통해 Logging, APM, Kubernetes, Istio Monitoring, Alerting 등 많은 기능을 한번에 해결 할 수 있다. (&lt;del&gt;하지만 매우 비싸다&lt;/del&gt;) 예전에 Grafana, Prometheus, AlertManager 등을 사용하여 Monitoring을 구축하였는데... Datadog은 Agent만 설치하면 모든게 가능해진다. 정말 간편하다&lt;/p&gt;

&lt;h2&gt;
  
  
  Datadog Operator 설치
&lt;/h2&gt;

&lt;p&gt;우선 Datadog Operator를 Helm을 통해 설치한다. &lt;br&gt;
&lt;a href="https://github.com/DataDog/datadog-operator/blob/main/docs/getting_started.md" rel="noopener noreferrer"&gt;https://github.com/DataDog/datadog-operator/blob/main/docs/getting_started.md&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo add datadog https://helm.datadoghq.com
&lt;span class="nv"&gt;$ &lt;/span&gt;helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; datadog &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;fullnameOverride&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dd-op"&lt;/span&gt; mwp-datadog-operator datadog/datadog-operator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이렇게 설치하면 &lt;code&gt;datadog&lt;/code&gt; namespace에 datadog operator 가 설치된다. &lt;/p&gt;




&lt;h2&gt;
  
  
  Datadog Configuration 설정
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Datadog credential 을 Kubernetes Secret으로 생성
&lt;/h3&gt;

&lt;p&gt;Datadog API, APP Key를 이용하여 Kubernetes secret을 생성&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl create secret generic datadog-secrets &lt;span class="nt"&gt;--from-literal&lt;/span&gt; api-key&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;DATADOG_API_KEY&amp;gt; &lt;span class="nt"&gt;--from-literal&lt;/span&gt; app-key&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;DATADOG_APP_KEY&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;생성된 kubernetes secret을 아래에서 사용&lt;/p&gt;

&lt;h3&gt;
  
  
  Datadog Agent 및 Cluster Agent 설치 및 설정
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;datadog-operator.yaml&lt;/code&gt; 파일을 생성한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadoghq.com/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DatadogAgent&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secrets&lt;/span&gt;
      &lt;span class="na"&gt;keyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api-key&lt;/span&gt;
    &lt;span class="na"&gt;appSecret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datadog-secrets&lt;/span&gt;
      &lt;span class="na"&gt;keyName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app-key&lt;/span&gt;
  &lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/datadoghq/agent:latest"&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;hostPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8125&lt;/span&gt;
      &lt;span class="na"&gt;collectEvents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;tolerations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Exists&lt;/span&gt;
      &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DD_DOGSTATSD_NON_LOCAL_TRAFFIC&lt;/span&gt; &lt;span class="c1"&gt;# Java JVM Metrics를 받기 위해 필요&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
    &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;logsConfigContainerCollectAll&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;apm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;hostPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8126&lt;/span&gt;
    &lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;processCollectionEnabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;systemProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;bpfDebugEnabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;features&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubeStateMetricsCore&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;networkMonitoring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;clusterAgent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/datadoghq/cluster-agent:latest"&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;clusterChecksEnabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;clusterChecksRunner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gcr.io/datadoghq/agent:latest&lt;/span&gt;
    &lt;span class="s"&gt;replicas:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;2&lt;/span&gt;
  &lt;span class="s"&gt;clusterName:&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;eks-demo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Datadog agent가 DaemonSet 형태로 각 node 설치된다. &lt;/li&gt;
&lt;li&gt;Datadog cluster agent도 설치되어 효율적인 운영이 가능하다&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;설정을 적용한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; datadog-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이렇게 적용하고 나면 Datadog agent는 auto discovery를 통해 가능한 모든 metrics를 가져온다. 설정 파일을 보면 알겠지만, kubernetes 로깅, 모니터링, 그리고 application 에 Datadog APM 을 적용하면 port 8126으로 받을 수 있도록 설정을 한다. &lt;/p&gt;

&lt;p&gt;Agent는 DaemonSet 형태로 각 노드에 하나씩 배포된다. 직접 해당 Pod를 &lt;code&gt;kubectl describe pod datadog-agent-~~~ -n datadog&lt;/code&gt; 확인해보면 &lt;code&gt;agent&lt;/code&gt;, &lt;code&gt;trace-agent&lt;/code&gt;, &lt;code&gt;process-agent&lt;/code&gt;, &lt;code&gt;system-probe&lt;/code&gt; 이렇게 4개의 container가 올라가 있는걸 볼 수 있다. &lt;/p&gt;




&lt;h2&gt;
  
  
  Istio 설정 변경
&lt;/h2&gt;

&lt;p&gt;이렇게 하면 Kubernets monitoring 및 Logging 이 모두 가능해진다. 하지만 Istio 는 제대로 monitoring 되지 않고, Envoy proxy tracing 도 불가능하다. Istio 를 위해 Istio operator 설정을 변경해 주어야 한다. &lt;br&gt;
&lt;a href="https://www.datadoghq.com/blog/how-to-monitor-istiod/#monitoring-istiod-with-datadog" rel="noopener noreferrer"&gt;https://www.datadoghq.com/blog/how-to-monitor-istiod/#monitoring-istiod-with-datadog&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Istio monitoring을 위한 변경
&lt;/h3&gt;

&lt;p&gt;아래와 같이 Datadog 이 Istio 관련 지표를 모니터링 할 수 있게 수정&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system patch service istiod &lt;span class="nt"&gt;--patch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
metadata:
  annotations:
    ad.datadoghq.com/endpoints.check_names: '["istio"]'
    ad.datadoghq.com/endpoints.init_configs: '[{}]'
    ad.datadoghq.com/endpoints.instances: |
      [
        {
          "istiod_endpoint": "http://%%host%%:15014/metrics",
          "send_histograms_buckets": true
        }
      ]
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Istio tracing 설정
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;istio-operator.yaml&lt;/code&gt; 파일을 아래와 같이 수정하여 APM 에서 Envoy proxy tracing 을 가능하게 한다. (Istio 관련 설치 및 설정은 &lt;a href="https://velog.io/@airoasis/eks-istio-install" rel="noopener noreferrer"&gt;EKS 에 Istio 설치 및 설정&lt;/a&gt; 참고)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install.istio.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IstioOperator&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-system&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istiocontrolplane&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;egressGateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-egressgateway&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;ingressGateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-ingressgateway&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
        &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt;
        &lt;span class="na"&gt;serviceAnnotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/healthz/ready&lt;/span&gt;
          &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;32197"&lt;/span&gt;
    &lt;span class="na"&gt;pilot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;meshConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enableTracing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;defaultConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;holdApplicationUntilProxyStarts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;tracing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Enable Datadog Tracing&lt;/span&gt;
        &lt;span class="na"&gt;datadog&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(HOST_IP):8126&lt;/span&gt;
        &lt;span class="na"&gt;sampling&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100.0&lt;/span&gt;  &lt;span class="c1"&gt;# 모든 request를 tracing 한다&lt;/span&gt;
    &lt;span class="na"&gt;accessLogFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stdout&lt;/span&gt;
    &lt;span class="na"&gt;outboundTrafficPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REGISTRY_ONLY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;meshConfig&lt;/code&gt; 에 위와 같이 Tracing 관련 설정을 추가한다. 이렇게 하면 Istio는 tracing 정보를 각 node 에 DaemonSet 으로 올라가 있는 Datadog Agent로 Tracing 정보를 보내게 된다. 그리고 Agent는 Datadog으로 모든 data를 전송한다. 이제 해당 설정을 반영한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;istioctl &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; istio-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;sampling rate을 100%로 하였기 때문에 모든 request에 대해 tracing 이 가능해야 한다. 하지만 Istio 버전에 따라 해당 설정을 적용하기 위해 istio-proxy가 올라가 있는 Pod들을 restart 해줘야 하는 경우가 있다. 1.11, 1.12 에서는 바로 적용 되는거 같다. 하지만 1.10 버전에서는 다시 시작해줘야 한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Istio ServiceEntry 설정
&lt;/h3&gt;

&lt;p&gt;만약, Istio 의 mesh 설정 중에 &lt;code&gt;outboundTrafficPolicy&lt;/code&gt; 의 &lt;code&gt;mode&lt;/code&gt; 가 &lt;code&gt;REGISTRY_ONLY&lt;/code&gt; 이고 (default 설정은 &lt;code&gt;ALLOW_ANY&lt;/code&gt; 이다), Datadog APM 을 사용하여 Java, Go, Python 등과 같은 Application 의 APM 을 사용하려면 반드시 &lt;code&gt;ServiceEntry&lt;/code&gt;를 추가해서 application pod에서 각 node에 daemonset으로 올라가 있는 datadog agent 의 container 중 하나인 &lt;code&gt;tracing-agent&lt;/code&gt;로 outbound 가 설정되어야 한다. (이 내용은 Datadog 의 문서에도 없고, Istio 에서도 특별히 명시되어 있지 않다) 이렇게 하지 않으면 절대 APM 을 확인 할수 없다. EKS 기준 손쉽게 할 수 있는 방법은 아래와 같다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1alpha3&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceEntry&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internal-ext&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ignored.com&lt;/span&gt;
  &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;192.168.0.0/16&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8126&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp-datadog&lt;/span&gt;
      &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TCP&lt;/span&gt;
  &lt;span class="na"&gt;resolution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NONE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🤚 팀에서 특별한 이유로 네이버 클라우드를 사용하는 프로젝트가 있는데, 여기에서는 이렇게 모든 설정을 하여도 APM 을 확인할 수 없었다. 그래서 네이버 클라우드와 연락하여 확인해본 결과 네이버 클라우드는 AWS EKS 와 달리 Kubernetes Service의 CNI인 Cilium 을 사용하고 있어서 동작하지 않았던 것이다. 결국 Cilium의 설정을 변경 (&lt;code&gt;cilium-config&lt;/code&gt;의 &lt;code&gt;kube-proxy-replacement&lt;/code&gt;를 &lt;code&gt;disabled&lt;/code&gt; 에서 &lt;code&gt;probe&lt;/code&gt; 로 변경) 하거나 Datadog의 설정 중 &lt;code&gt;hostNetwork&lt;/code&gt;를 'true' 로 변경하는 방법이 있다.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;여기 까지 하면 Datadog 의 APM 에서 Envoy proxy의 tracing 정보를 모두 볼 수 있다. 하지만 이건 istio-proxy의 tracing 정보이고 application 의 APM은 아니다... 물론 Istio는 모든 request가 istio-proxy를 통해 들어가고 나가기 때문에 이에 대한 tracing 은 가능하지만 (Istio 공식문서의 Jaeger tracing과 같은) aplication 자체에 대한 APM을 위해서는 각 language에 맞는 Datadog APM 을 사용해야 한다. &lt;/p&gt;

&lt;h2&gt;
  
  
  Datadog Integration 추가
&lt;/h2&gt;

&lt;p&gt;이제 Datadog 에 로그인 하여 Integration tab으로 가서 Kubernetes 및 Istio를 추가해 주면 관련 모니터링을 위한 Dashboard가 추가된다. &lt;/p&gt;

&lt;p&gt;Kubernetes Overview&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F066n2v114y6jsufpk23b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F066n2v114y6jsufpk23b.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qcmkrt6ezz3uhqhpt4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3qcmkrt6ezz3uhqhpt4i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>istio</category>
      <category>datadog</category>
    </item>
    <item>
      <title>Loki를 이용한 손쉬운 Kubernetes Logging</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:48:35 +0000</pubDate>
      <link>https://dev.to/airoasis/lokireul-iyonghan-sonswiun-kubernetes-logging-1iho</link>
      <guid>https://dev.to/airoasis/lokireul-iyonghan-sonswiun-kubernetes-logging-1iho</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/grafana/loki" rel="noopener noreferrer"&gt;Loki&lt;/a&gt;는 Prometheus 와 같은 철학으로 탄생한 cloud native infra를 위한 logging 서비스이다. 개인적으로 EFK 도 사용해보고 Datadog의 logging 도 사용해 봤지만 Loki가 제일 마음에 든다. (Datadog 이나 New Relic 과 같은 서비스를 사용하지 않는다면 Loki를 사용하여 Logging을 하면 가벼운 Kubernetes logging 서비스를 구축 가능한다.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Loki 설치
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo add grafana https://grafana.github.io/helm-charts
&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo update
&lt;span class="nv"&gt;$ &lt;/span&gt;helm upgrade &lt;span class="nt"&gt;--install&lt;/span&gt; loki grafana/loki-stack &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;loki &lt;span class="nt"&gt;--set&lt;/span&gt; grafana.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,grafana.service.type&lt;span class="o"&gt;=&lt;/span&gt;LoadBalancer,loki.config.table_manager.retention_deletes_enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,loki.config.table_manager.retention_period&lt;span class="o"&gt;=&lt;/span&gt;336h,loki.persistence.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,loki.persistence.size&lt;span class="o"&gt;=&lt;/span&gt;5Gi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;loki namespace를 생성&lt;/li&gt;
&lt;li&gt;상용에 사용할 수 있도록 데이터를 persist 한다. &lt;/li&gt;
&lt;li&gt;Grafana를 외부로 노출한다 &lt;/li&gt;
&lt;li&gt;최근 2주동안의 로그를 저장하고 오래된 순서대로 삭제&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Loki 확인
&lt;/h2&gt;

&lt;p&gt;접속 주소를 확인&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get svc &lt;span class="nt"&gt;-n&lt;/span&gt; loki
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grafana의 admin password를 확인한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get secret &lt;span class="nt"&gt;--namespace&lt;/span&gt; loki loki-grafana &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.data.admin-password}"&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;login ID는 admin 이고 password는 위에서 얻은 값이다. &lt;/p&gt;

&lt;p&gt;결국 Grafana를 통해 서비스 되고 Explore tab 에서 Loki를 선택하여 Kubernetes의 모든 log를 확인할 수 있다. &lt;/p&gt;

&lt;p&gt;실제 화면은 아래와 같다. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpplpejqdqebu531yviys.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpplpejqdqebu531yviys.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>logging</category>
      <category>loki</category>
    </item>
    <item>
      <title>AWS EKS 의 Cluster Autoscaler 설정</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:43:59 +0000</pubDate>
      <link>https://dev.to/airoasis/eks-yi-cluster-autoscaler-seoljeong-6ho</link>
      <guid>https://dev.to/airoasis/eks-yi-cluster-autoscaler-seoljeong-6ho</guid>
      <description>&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html"&gt;https://docs.aws.amazon.com/eks/latest/userguide/autoscaling.html&lt;/a&gt; 를 참고하여 설정&lt;/p&gt;

&lt;p&gt;VPA 및 Goldilocks를 통해 Kubernetes 의 resource request, limit 설정을 완료하고 HPA를 통해 Pod를 늘리고 결국 Node의 자원이 부족하면 Cluster Autoscaler 를 통해 Node를 추가로 생성하게 된다. Cluster Autoscaler 는 Cloud Vendor 마다 설정 방법이 다르다&lt;/p&gt;

&lt;p&gt;AWS 에서 &lt;code&gt;eksctl&lt;/code&gt; 을 이용하여 kubernetes cluster 를 만들었다면 EKS documentation 의 Prerequisites 에 있는 내용은 모두 완료 된 상태이다. 그 아래부터 차례대로 direction을 따라 설정하면 쉽게 Cluster Autoscaler 를 설치할 수 있다. &lt;/p&gt;

&lt;h2&gt;
  
  
  IAM Policy 및 role 생성
&lt;/h2&gt;

&lt;p&gt;Cluster Autoscaler 가 IAM role 을 사용하기 위해 필요한 권한을 주기 위해 IAM Policy 생성이 필요하다.&lt;/p&gt;

&lt;h3&gt;
  
  
  IAM Policy 생성
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;아래와 같이 &lt;code&gt;cluster-autoscaler-policy.json&lt;/code&gt; 파일을 생성한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"Version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"Statement"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"autoscaling:DescribeAutoScalingGroups"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"autoscaling:DescribeAutoScalingInstances"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"autoscaling:DescribeLaunchConfigurations"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"autoscaling:DescribeTags"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"autoscaling:SetDesiredCapacity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"autoscaling:TerminateInstanceInAutoScalingGroup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"ec2:DescribeLaunchTemplateVersions"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Resource"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Effect"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Allow"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;아래와 같이 policy 를 생성한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam create-policy &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--policy-name&lt;/span&gt; AmazonEKSClusterAutoscalerPolicy &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://cluster-autoscaler-policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;위 command 를 실행하고 나오는 output 에서 Amazon Resource Name (ARN)을 아래에서 사용해야 한다. &lt;/p&gt;

&lt;h3&gt;
  
  
  IAM role 생성
&lt;/h3&gt;

&lt;p&gt;아래와 같이 &lt;code&gt;eksctl&lt;/code&gt;을 사용하여 IAM role을 생성할 수 있다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eks-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cluster-autoscaler &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach-policy-arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;위에서 생성한 policy의 arn&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--override-existing-serviceaccounts&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy Cluster Autoscaler
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;우선 Cluster Autoscaler YALM 파일을 다운로드 한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; cluster-autoscaler-autodiscover.yaml https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-autodiscover.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;YAML 파일을 열어서 &lt;code&gt;&amp;lt;YOUR CLUSTER NAME&amp;gt;&lt;/code&gt; 을 찾아서 &lt;code&gt;eks-demo&lt;/code&gt; 로 replace 한다. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그리고 적용한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; cluster-autoscaler-autodiscover.yaml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
ℹ️ 문서에는 이 다음에 service account을 annotate 하는 부분이 있는데 확인해보면 이미 되어있다&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cluster-autoscaler.kubernetes.io.safe-to-evict&lt;/code&gt;를 추가하기 위해 아래와 같이 patch 한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl patch deployment cluster-autoscaler &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s1"&gt;'{"spec":{"template":{"metadata":{"annotations":{"cluster-autoscaler.kubernetes.io/safe-to-evict": "false"}}}}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;그리고 &lt;code&gt;cluster-autoscaler&lt;/code&gt; deployment를 수정한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system edit deployment.apps/cluster-autoscaler
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br&gt;
&lt;code&gt;&amp;lt;YOUR CLUSTER NAME&amp;gt;&lt;/code&gt; 을 &lt;code&gt;eks-demo&lt;/code&gt; 로 수정하고 container command 에 &lt;code&gt;--balance-similar-node-groups&lt;/code&gt; 와 &lt;code&gt;--skip-nodes-with-system-pods=false&lt;/code&gt; 를 추가하여 아래와 같이 수정한다.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;command&lt;/span&gt;
    &lt;span class="s"&gt;- ./cluster-autoscaler&lt;/span&gt;
    &lt;span class="s"&gt;- --v=4&lt;/span&gt;
    &lt;span class="s"&gt;- --stderrthreshold=info&lt;/span&gt;
    &lt;span class="s"&gt;- --cloud-provider=aws&lt;/span&gt;
    &lt;span class="s"&gt;- --skip-nodes-with-local-storage=false&lt;/span&gt;
    &lt;span class="s"&gt;- --expander=least-waste&lt;/span&gt;
    &lt;span class="s"&gt;- --node-group-auto-discovery=asg:tag=k8s.io/cluster-autoscaler/enabled,k8s.io/cluster-autoscaler/&amp;lt;YOUR CLUSTER NAME&amp;gt;&lt;/span&gt;
    &lt;span class="s"&gt;- --balance-similar-node-groups&lt;/span&gt;
    &lt;span class="s"&gt;- --skip-nodes-with-system-pods=false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Cluster Autoscaler &lt;a href="https://github.com/kubernetes/autoscaler/releases"&gt;release&lt;/a&gt; 페이지를 열어서 설치한 kubernetes 버전의 최신 cluster autoscaler 를 확인한다. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;위에서 확인한 버전을 아래와 같이 적용한다. (v1.21.2를 가정한다.)&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl set image deployment cluster-autoscaler \
-n kube-system \
cluster-autoscaler=k8s.gcr.io/autoscaling/cluster-autoscaler:v1.21.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cluster Autoscaler 로그 확인
&lt;/h2&gt;

&lt;p&gt;Cluster Autoscaler 를 deploy 한 후에 로그를 확인하여 제대로 동작하는지 확인한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;kubectl -n kube-system logs -f deployment.apps/cluster-autoscaler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✋ 최근에는 &lt;a href="https://karpenter.sh/"&gt;Karpenter&lt;/a&gt;가 나와서 Cluster Autoscaler 대신 이걸 활용 할 수도 있다.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>autoscale</category>
    </item>
    <item>
      <title>Kubernetes 의 Resources 설정을 위한 VPA 및 Goldilocks</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:41:57 +0000</pubDate>
      <link>https://dev.to/airoasis/kubernetes-yi-resources-seoljeongeul-wihan-vpa-mic-goldilocks-3503</link>
      <guid>https://dev.to/airoasis/kubernetes-yi-resources-seoljeongeul-wihan-vpa-mic-goldilocks-3503</guid>
      <description>&lt;h2&gt;
  
  
  Kubernetes (AWS EKS) 의 Autoscaler
&lt;/h2&gt;

&lt;p&gt;Kubernetes 의 Autoscaler 를 사용하기 위해서는 우선 &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Deployment 의 Resource request, limit을 모두 설정해야 한다. &lt;/li&gt;
&lt;li&gt;Horizontal Pod Autoscaler (HPA) 를 설정한다.&lt;/li&gt;
&lt;li&gt;AWS 의 Cluster Autoscaler (CA) 를 설정한다. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;여기서 Resource의 request, limit 설정을 위해 &lt;a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler"&gt;Vertical Pod Autoscaler&lt;/a&gt; 및 &lt;a href="https://goldilocks.docs.fairwinds.com/#how-can-this-help-with-my-resource-settings"&gt;Goldilocks&lt;/a&gt;를 활용하고 이를 통해 request, limit 을 설정하는데 참고 할 수 있다. &lt;/p&gt;




&lt;h2&gt;
  
  
  VPA 및 Goldilocks의 역할
&lt;/h2&gt;

&lt;p&gt;사실 &lt;a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler"&gt;VPA&lt;/a&gt;는 Kubernetes 의 Autoscaler 중 하나로 Horizontal 하게 Pod/ Node를 늘리는 HPA와 달리 Vertical 하게 스펙을 높이는 방법이다. 그런데 VPA controller stack은 recommendation engine 을 포함하고 있고, 가이드라인을 제공하기 위해 현재 resource 사용량을 계속 수집한다. 결국 보통 실제 VPA를 사용한 Autoscale을 사용 하지는 않고, recommendation engine 만 사용하도록 설정하여 request, limit을 정하고 HPA, CA를 통해 Autoscale을 한다. &lt;/p&gt;

&lt;p&gt;여기서 &lt;a href="https://goldilocks.docs.fairwinds.com/#how-can-this-help-with-my-resource-settings"&gt;Goldilocks&lt;/a&gt; 의 역할은 불편하게 &lt;code&gt;kubectl&lt;/code&gt; 을 통해 필요한 서비스의 VPA recommendation 을 확인하는 대신 쉽게 Web UI를 통해 이를 확인 할 수 있게 도와준다. &lt;/p&gt;




&lt;h2&gt;
  
  
  Kube Metrics Server 설치
&lt;/h2&gt;

&lt;p&gt;우선 지표 수집을 위해 Kubernetes 의 metrics-server를 설치해야 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;제대로 적용되었는지 확인한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get deployment metrics-server &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  VPA 및 Goldilocks 설치
&lt;/h2&gt;

&lt;p&gt;우리는 full VPA install 이 필요하지 않고 recommender 만 필요하다. 아래와 같이 설치하면 VPA recommender와 Goldilocks를 설치한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo add fairwinds-stable https://charts.fairwinds.com/stable
&lt;span class="nv"&gt;$ &lt;/span&gt;helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; updater.enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false &lt;/span&gt;vpa fairwinds-stable/vpa &lt;span class="nt"&gt;--namespace&lt;/span&gt; vpa &lt;span class="nt"&gt;--create-namespace&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;goldilocks fairwinds-stable/goldilocks &lt;span class="nt"&gt;--namespace&lt;/span&gt; goldilocks &lt;span class="nt"&gt;--create-namespace&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Goldilocks 의 chart를 통해 VPA를 설치하면 쉽게 원하는 recommendation engine 만을 설치할 수 있다. &lt;a href="https://github.com/FairwindsOps/charts/tree/master/stable/vpa#components"&gt;https://github.com/FairwindsOps/charts/tree/master/stable/vpa#components&lt;/a&gt; 참고&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;VPA recommender를 사용할 namespace를 지정한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl label ns default goldilocks.fairwinds.com/enabled&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Goldilocks 확인
&lt;/h2&gt;

&lt;p&gt;kubernetes port forwarding 을 통해 Goldilocks 에 접속한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl &lt;span class="nt"&gt;-n&lt;/span&gt; goldilocks port-forward svc/goldilocks-dashboard 8080:80
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;kubernetes port forwarding을 remote 에서 하는 경우 localhost를 통해 Goldilocks를 접근하기 위해 local 에서 Local Port Forwarding을 해야한다.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-L&lt;/span&gt; 8080:127.0.0.1:8080 ec2-~~.compute.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="http://localhost:8080"&gt;http://localhost:8080&lt;/a&gt; 을 열어본다. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;서비스가 배포되지 않은 상황에서는 아무것도 없을것이다. 서비스를 올리고 사용하면서 확인하면 각 서비스의 설정과 recommendation 을 확인할 수 있다. &lt;br&gt;
하지만... 실제 사용해본 결과 참고는 할 수 있지만 이 수치가 절대적인 값이 될 수는 없다. Language와 서비스의 성격에 따라 실제 운영하면서 맞추어 나가는 방법이 좋은거 같다. &lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>kubernetes</category>
      <category>autoscale</category>
      <category>vpa</category>
      <category>goldilocks</category>
    </item>
    <item>
      <title>AWS EKS 에서 Istio 와 Application Load Balancer 연결</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:36:17 +0000</pubDate>
      <link>https://dev.to/airoasis/eks-eseo-istio-wa-application-load-balancer-yeongyeol-2k2j</link>
      <guid>https://dev.to/airoasis/eks-eseo-istio-wa-application-load-balancer-yeongyeol-2k2j</guid>
      <description>&lt;p&gt;EKS 에서 Istio 를 설치하면 기본적으로 Classic Load Balancer (CLB) 를 사용하여 설치가 된다. 공식적으로 CLB가 discontinued 된다는 notice는 없지만 CLB는 권장되지 않는다. 결국 L4 Network Load Balancer(NLB) 또는 L7 Application Load Balancer(ALB)로 변경해야 한다. 하지만 &lt;a href="https://aws.amazon.com/waf/"&gt;AWS WAF&lt;/a&gt; 와 같은 서비스를 사용하기 위해서는 반드시 ALB를 사용해야 한다. 여기서는 Istio 와 ALB를 연결해 본다. (NLB를 사용하는 방법은 많이 나와있지만 ALB를 사용하는 방법은 찾기 힘들다...)&lt;br&gt;
&lt;a href="https://rtfm.co.ua/en/istio-external-aws-application-loadbalancer-and-istio-ingress-gateway/"&gt;https://rtfm.co.ua/en/istio-external-aws-application-loadbalancer-and-istio-ingress-gateway/&lt;/a&gt; 를 참고하였다.&lt;/p&gt;
&lt;h2&gt;
  
  
  변경되는 Architecture
&lt;/h2&gt;

&lt;p&gt;전체적인 architecture는 이렇게 변경된다. 기존 istio ingress gateway의 service type 은 LoadBalancer 에서 NodePort로 변경하여 기존 CLB가 삭제되고, 대신 Kubernetes 의 Ingress resource를 통해 ALB를 생성한다. 그리고 ALB에서 TLS termination 을 하고 모든 request를 istio ingress gateway pod로 보내고 istio ingress gateway가 application service로 routing 을 하게 된다. &lt;/p&gt;


&lt;h2&gt;
  
  
  ACM 을 통한 TLS 인증서 설치
&lt;/h2&gt;

&lt;p&gt;첫째, TLS 인증서를 AWS ACM 에 import 한다. &lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/acm/latest/userguide/import-certificate-api-cli.html"&gt;https://docs.aws.amazon.com/acm/latest/userguide/import-certificate-api-cli.html&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  AWS Load Balancer Controller 설치
&lt;/h2&gt;

&lt;p&gt;ALB를 사용하기 위해서는 AWS Load Balancer Controller가 필요하다. Bastion 서버에서 아래 순서대로 설치를 진행한다. &lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html"&gt;https://docs.aws.amazon.com/eks/latest/userguide/aws-load-balancer-controller.html&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  IAM Policy 생성
&lt;/h3&gt;

&lt;p&gt;AWS Load Balancer Controller를 위한 IAM Policy를 다운로드한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.3.1/docs/install/iam_policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;IAM policy를 생성한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws iam create-policy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-name&lt;/span&gt; AWSLoadBalancerControllerIAMPolicy &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--policy-document&lt;/span&gt; file://iam_policy.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IAM Role 및 Kubernetes의 Service Account 생성
&lt;/h3&gt;

&lt;p&gt;위에서 생성된 IAM Policy를 이용하여 아래와 같이 생성한다. 여기서 &lt;code&gt;attach-policy-arn&lt;/code&gt; 을 위에서 생성한 policy를 넣는다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;eksctl create iamserviceaccount &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eks-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--namespace&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;aws-load-balancer-controller &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--attach-policy-arn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arn:aws:iam::931639357206:policy/AWSLoadBalancerControllerIAMPolicy &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--override-existing-serviceaccounts&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Helm 설치 및 repository 추가
&lt;/h3&gt;

&lt;p&gt;AWS Load Balancer Controller를 Helm을 통해 설치하기 때문에 Helm도 설치하고 &lt;code&gt;eks-charts&lt;/code&gt; repository도 추가한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;snap &lt;span class="nb"&gt;install &lt;/span&gt;helm &lt;span class="nt"&gt;--classic&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo add eks https://aws.github.io/eks-charts
&lt;span class="nv"&gt;$ &lt;/span&gt;helm repo update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AWS Load Balancer Controller 설치
&lt;/h3&gt;

&lt;p&gt;이제 AWS Load Balancer Controller 를 설치한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;helm &lt;span class="nb"&gt;install &lt;/span&gt;aws-load-balancer-controller eks/aws-load-balancer-controller &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;clusterName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eks-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; serviceAccount.create&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; serviceAccount.name&lt;span class="o"&gt;=&lt;/span&gt;aws-load-balancer-controller &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--set&lt;/span&gt; image.repository&lt;span class="o"&gt;=&lt;/span&gt;602401143452.dkr.ecr.ap-northeast-2.amazonaws.com/amazon/aws-load-balancer-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;controller 가 제대로 설치되었는지 확인한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get deployment &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system aws-load-balancer-controller
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Istio 설정 변경
&lt;/h2&gt;

&lt;p&gt;이제 Istio 의 ingress gateway의 service type을 NodePort로 변경하고 ALB가 istio ingreas gateway로 health check 을 할 수 있도록 관련 정보를 넣어준다. &lt;/p&gt;

&lt;h3&gt;
  
  
  Health check 을 위한 Port number 확인
&lt;/h3&gt;

&lt;p&gt;우선 ALB 가 health check을 위해 사용할 istio ingress gateway 의 health check 관련 nodePort number를 알아본다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get service istio-ingressgateway &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.spec.ports[?(@.name=="status-port")].nodePort}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;얻어진 Port number를 아래에서 사용한다. &lt;/p&gt;

&lt;h3&gt;
  
  
  Istio 설정 업데이트
&lt;/h3&gt;

&lt;p&gt;아래와 같이 &lt;code&gt;istio-operator.yaml&lt;/code&gt; 를 업데이트 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install.istio.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IstioOperator&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-system&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istiocontrolplane&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;egressGateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-egressgateway&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;ingressGateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-ingressgateway&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
        &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NodePort&lt;/span&gt; &lt;span class="c1"&gt;# ingress gateway 의 NodePort 사용&lt;/span&gt;
        &lt;span class="na"&gt;serviceAnnotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Health check 관련 정보&lt;/span&gt;
          &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/healthz/ready&lt;/span&gt;
          &lt;span class="na"&gt;alb.ingress.kubernetes.io/healthcheck-port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;32197"&lt;/span&gt; &lt;span class="c1"&gt;# 위에서 얻은 port number를 사용&lt;/span&gt;
    &lt;span class="na"&gt;pilot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;meshConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enableTracing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;defaultConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;holdApplicationUntilProxyStarts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;accessLogFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stdout&lt;/span&gt;
    &lt;span class="na"&gt;outboundTrafficPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REGISTRY_ONLY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Istio ingress gateway 의 service type을 NodePort로 설정한다.&lt;/li&gt;
&lt;li&gt;전 단계에서 얻은 port number를 healthcheck-port 에 넣어 health check 관련 정보를 istio ingress gateway 에 annotation 으로 저장한다.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;업데이트된 정보를 Istio 에 적용한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;istioctl &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; istio-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;여기까지 하면 기존 ingress gateway의 service type을 기존 &lt;code&gt;LoadBalancer&lt;/code&gt; 에서 &lt;code&gt;NodePort&lt;/code&gt;로 변경하였기 때문에 CLB (Classic Load Balancer)는 삭제된다. &lt;/p&gt;




&lt;h2&gt;
  
  
  ALB 생성
&lt;/h2&gt;

&lt;p&gt;이제 마지막으로 Kubernetes 의 Ingress를 통해 ALB를 생성한다. (AWS Load Balancer Controller는 Kubernetes 의 Ingress 를 통해 ALB 를 생성한다) &lt;br&gt;
아래와 같이 &lt;code&gt;kube-ingress.yaml&lt;/code&gt; 파일을 생성한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;extensions/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-alb&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-system&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;alb&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/scheme&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;internet-facing&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/certificate-arn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:acm:ap-northeast-2:93163~~~~"&lt;/span&gt;
    &lt;span class="na"&gt;alb.ingress.kubernetes.io/actions.ssl-redirect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{"Type":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"redirect",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"RedirectConfig":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Protocol":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"HTTPS",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Port":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"443",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"StatusCode":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"HTTP_301"}}'&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/*&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ssl-redirect&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;use-annotation&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/*&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-ingressgateway&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;certificate-arn 에는 AWS ACM 에 생성한 TLS certificate의 ARN을 입력한다. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;아래와 같이 Ingress를 생성하여 ALB 생성을 완료한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; kube-ingress.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이렇게 기존 CLB를 삭제하고 ALB 로 대체하면서 TLS Termination 은 ALB 에서 일어나고 ALB는 &lt;code&gt;istio-system&lt;/code&gt; namespace 에 배포되어 있는 &lt;code&gt;istio-ingressgateway&lt;/code&gt; 로 request를 보내게 되고 &lt;code&gt;istio-ingressgateway&lt;/code&gt;는 비로서 서비스로 라우팅을 하게 된다. &lt;/p&gt;




&lt;p&gt;여기까지 하면 EKS 에 상용에서 사용가능한 Kubernetes Cluster 생성 및 ALB를 통한 TLS Termination, istio ingress gateway의 라우팅이 가능해 진다. 결국 AWS의 &lt;a href="https://aws.amazon.com/waf/"&gt;WAF&lt;/a&gt;  와 같이 ALB가 필수인 서비스를 사용할 수 있다. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>istio</category>
    </item>
    <item>
      <title>AWS EKS에 Istio 설치 및 설정</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:34:38 +0000</pubDate>
      <link>https://dev.to/airoasis/ekse-istio-seolci-mic-seoljeong-2fpc</link>
      <guid>https://dev.to/airoasis/ekse-istio-seolci-mic-seoljeong-2fpc</guid>
      <description>&lt;p&gt;Istio를 설치하는 방법은 istioctl, helm, Istio Operator 등 다양한 방법이 있다. 이 중 현재 운영환경에서 권장되는 방법은 모든 configuration을 &lt;a href="https://istio.io/latest/docs/reference/config/istio.operator.v1alpha1/"&gt;IstioOperator CR&lt;/a&gt; 로 작성하여 이를 &lt;code&gt;istioctl install&lt;/code&gt; command 로 적용하는 방법이다. &lt;/p&gt;

&lt;h2&gt;
  
  
  istioctl 설치
&lt;/h2&gt;

&lt;p&gt;우선 최신버전의 &lt;code&gt;istioctl&lt;/code&gt;을 다운로드 받고 path 에 넣어준다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://istio.io/downloadIstioctl | sh -
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; ~/.istioctl/bin/istioctl ~/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;만약 특정 버전의 Istio 를 다운로드 받고 싶다면&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-sL&lt;/span&gt; https://istio.io/downloadIstioctl | &lt;span class="nv"&gt;ISTIO_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.10.3 &lt;span class="nv"&gt;TARGET_ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;x86_64 sh -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Istio 설치
&lt;/h2&gt;

&lt;p&gt;이제 아래와 같이 &lt;code&gt;istio-operator.yaml&lt;/code&gt; 파일을 생성한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;install.istio.io/v1alpha1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IstioOperator&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-system&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istiocontrolplane&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;egressGateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-egressgateway&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;ingressGateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;istio-ingressgateway&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;pilot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;k8s&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;hpaSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;meshConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enableTracing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;defaultConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;holdApplicationUntilProxyStarts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;accessLogFile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stdout&lt;/span&gt;
    &lt;span class="na"&gt;outboundTrafficPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;REGISTRY_ONLY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;가장 기본적인 &lt;code&gt;default&lt;/code&gt; profile 을 사용한다. &lt;/li&gt;
&lt;li&gt;모든 component의 &lt;code&gt;minReplicas&lt;/code&gt;를 2로 설정하여 HA를 보장하고 &lt;code&gt;PodDisruptionBudget&lt;/code&gt; 으로 인해 istio 버전 upgrade 가 실패하는걸 막는다&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enableTracing&lt;/code&gt; 을 설정하여 추후에 Datadog 이나 Jaeger 를 통해 distributed tracing 이 가능하게 한다.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;holdApplicationUntilProxyStarts&lt;/code&gt; 설정으로 istio-proxy 가 완전히 올라오면 서비스가 되게 한다. (Java 같은 경우는 서비스가 올라가는게 한참 걸려서 상관없지만 Go, Python 같은 경우 먼저 올라가서 서비스가 가능한 상태가 먼저 되고 request를 받으려고 하지만 istio-proxy가 준비되지 않아 request가 실패하는 경우가 있다. 물론 deployment 의 readiness/liveness 설정으로 대부분 istio-proxy 가 먼저 뜨지만 로컬에서 개발할때는 빠르게 테스트 하기 위해 readiness/liveness 없이 하기도 한다)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;accessLogFile&lt;/code&gt; 설정으로 Envoy proxy 의 access log를 콘솔로 남긴다.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;outboundTrafficPolicy&lt;/code&gt; 설정을 REGISTRY_ONLY 로 하여 오직 &lt;code&gt;ServiceEntry&lt;/code&gt; 를 통해 허가된 IP/Domain 만 outbound를 허용한다. (이 설정은 개발중에 좀 성가실 수 있다... 하지만 개발을 완료한 후에 적용하려 하면 더욱 힘들고 결국 해당 설정 없이 서비스 하게 되는 경우도 있다) 이를 통해 더욱 secure 한 서비스를 만들 수 있다. (default 설정은 ALLOW_ANY 이다)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;아래와 같이 kubernetes 에 istio를 deploy 한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;istioctl &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; istio-operator.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;이제 Kubernetes 에 Istio 가 배포 되었다. 아래와 같이 확인한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Namespace lable 을 통한 auto injection 설정
&lt;/h2&gt;

&lt;p&gt;마지막으로 원하는 namespace, 보통 &lt;code&gt;default&lt;/code&gt; namespace 에 아래와 같이 label을 추가해서 Istio 가 자동으로 application 을 배포할 때 Envoy sidecar proxy를 주입하도록 설정한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl label namespace default istio-injection&lt;span class="o"&gt;=&lt;/span&gt;enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;이제 &lt;code&gt;default&lt;/code&gt; namespace 에 배포하는 서비스에는 &lt;code&gt;istio-proxy&lt;/code&gt;가 sidecar 로 같이 올라가서 traffic management, security 등 다양한 Istio 의 기능을 사용할 수 있게 되었다. &lt;/p&gt;

</description>
      <category>aws</category>
      <category>kubernetes</category>
      <category>istio</category>
    </item>
    <item>
      <title>AWS EKS with eksctl</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Mon, 21 Feb 2022 04:32:25 +0000</pubDate>
      <link>https://dev.to/airoasis/aws-eks-with-eksctl-1clp</link>
      <guid>https://dev.to/airoasis/aws-eks-with-eksctl-1clp</guid>
      <description>&lt;p&gt;AWS 에 EKS 생성을 위한 가장 손쉬운 방법은 eksctl 을 사용하는 것이다. 그리고 eksctl cli를 실행시킬 환경은 laptop 보다는 AWS 에 bastion 서버를 만들어서 그곳에서 실행하는걸 추천한다. (laptop 에 이런 저런 환경을 옮겨가면서 작업하다보면 실수한다. 그리고 관리도 힘들다)&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS User 생성
&lt;/h2&gt;

&lt;p&gt;우선 AWS IAM 에서 programatically 사용가능하도록 admin user를 만들고 해당 user 의 AWS Access Key ID 및 AWS Secret Access Key를 획득한다. &lt;/p&gt;




&lt;h2&gt;
  
  
  Bastion 서버 생성
&lt;/h2&gt;

&lt;p&gt;그리고 AWS에 bastion 서버를 만들자. instance type은 &lt;code&gt;t3.small&lt;/code&gt; 이어도 충분하다. (&lt;del&gt;free tier 도 상관없다&lt;/del&gt;) 그리고 적절히 security group 을 만들어서 서버로 들어간다.&lt;/p&gt;




&lt;h2&gt;
  
  
  kubectl 설치
&lt;/h2&gt;

&lt;p&gt;원하는 kubernetes 버전의 kubectl 설치&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html"&gt;https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.21.2/2021-07-05/bin/linux/amd64/kubectl
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ./kubectl
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; ./kubectl &lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin/kubectl &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:&lt;span class="nv"&gt;$HOME&lt;/span&gt;/bin
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH=$PATH:$HOME/bin'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl version &lt;span class="nt"&gt;--short&lt;/span&gt; &lt;span class="nt"&gt;--client&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  aws cli 설치
&lt;/h2&gt;

&lt;p&gt;eksctl 을 사용하려면 AWS user의 credentil 설정이 필요하다. aws cli 를 설치한다. &lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;unzip
&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="s2"&gt;"https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s2"&gt;"awscliv2.zip"&lt;/span&gt;
unzip awscliv2.zip
&lt;span class="nb"&gt;sudo&lt;/span&gt; ./aws/install
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  aws cli configuration 설정
&lt;/h2&gt;

&lt;p&gt;이제 aws cli 에 configuration 을 설정한다. 이때 위에서 획득한 credential을 사용한다. region 은 Seoul(&lt;code&gt;ap-northeast-2&lt;/code&gt;)을 사용&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html"&gt;https://docs.aws.amazon.com/cli/latest/userguide/getting-started-quickstart.html&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws configure
AWS Access Key ID &lt;span class="o"&gt;[&lt;/span&gt;None]: ~~~
AWS Secret Access Key &lt;span class="o"&gt;[&lt;/span&gt;None]: ~~~
Default region name &lt;span class="o"&gt;[&lt;/span&gt;None]: ap-northeast-2
Default output format &lt;span class="o"&gt;[&lt;/span&gt;None]: json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  eksctl cli 설치
&lt;/h2&gt;

&lt;p&gt;eksctl cli 를 설치한다. &lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html"&gt;https://docs.aws.amazon.com/eks/latest/userguide/eksctl.html&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl &lt;span class="nt"&gt;--silent&lt;/span&gt; &lt;span class="nt"&gt;--location&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;_amd64.tar.gz"&lt;/span&gt; | &lt;span class="nb"&gt;tar &lt;/span&gt;xz &lt;span class="nt"&gt;-C&lt;/span&gt; /tmp
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mv&lt;/span&gt; /tmp/eksctl /usr/local/bin
&lt;span class="nv"&gt;$ &lt;/span&gt;eksctl version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  EKS Cluster 생성
&lt;/h2&gt;

&lt;p&gt;eksctl cli로 EKS Cluster 를 생성한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;eksctl create cluster &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--version&lt;/span&gt; 1.21 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--name&lt;/span&gt; eks-demo &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--vpc-nat-mode&lt;/span&gt; HighlyAvailable &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--node-private-networking&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--region&lt;/span&gt; ap-northeast-2 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--node-type&lt;/span&gt; t3.medium &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--nodes&lt;/span&gt; 2 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--with-oidc&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--ssh-access&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--ssh-public-key&lt;/span&gt; thomas &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--managed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: 사용할 Kubernetes 버전&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vpc-nat-mode&lt;/code&gt;: kubernetes의 모든 outbound는 nat gateway를 통해 나가게 되는게 default option 은 single 이라 하나만 생성된다. 개발 환경에서는 상관없을거 같지만 운영에서는 각 subnet 마다 하나씩 만드는 &lt;code&gt;HighAvailable&lt;/code&gt; 옵션을 사용한다. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node-private-networking&lt;/code&gt;: 해당 option 이 없으면 node group 이 public subnet 에 만들어진다. 보안을 위해 private subnet 에 만들어지도록 이 옵션을 사용한다. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;node-type&lt;/code&gt;: 생성될 node의 instance 타입&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nodes&lt;/code&gt;: 생성 될 node의 갯수&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;cluster 가 잘 생성되었는지 확인한다&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;kubectl get nodes &lt;span class="nt"&gt;-o&lt;/span&gt; wide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  EKS 보안 관련 설정
&lt;/h2&gt;

&lt;p&gt;여기까지 하면 상용에서 사용할 수 있도록 HA를 고려한 secure 한 방법으로 Kubernets cluster 가 생성된다. 하지만 보안을 위해 한가지를 더 해주어야 한다. 현재 상태는 cluster endpoint가 public 하게 허용되어 있다. &lt;br&gt;
&lt;a href="https://aws.amazon.com/blogs/containers/de-mystifying-cluster-networking-for-amazon-eks-worker-nodes/"&gt;https://aws.amazon.com/blogs/containers/de-mystifying-cluster-networking-for-amazon-eks-worker-nodes/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html"&gt;https://docs.aws.amazon.com/eks/latest/userguide/cluster-endpoint.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;이를 제약하기 위해 private access 를 허용하고 CIDR 블록 제한으로 bastion 서버에서만 kubernetes 에 명령을 내릴 수 있도록 수정한다. bastion server의 Public IPv4 address 를 입력한다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;eksctl utils update-cluster-endpoints &lt;span class="nt"&gt;--cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eks-demo &lt;span class="nt"&gt;--private-access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--public-access&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;eksctl utils set-public-access-cidrs &lt;span class="nt"&gt;--cluster&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;eks-demo 1.1.1.1/32 &lt;span class="nt"&gt;--approve&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1.1.1.1/32 는 bastion 서버의 주소를 넣는다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;사실 EKS Cluster 생성 작업은 eksctl의 --dry-run 옵션으로 yaml 파일을 만들어서 생성부터 EKS 보안 관련 설정까지 모두 옵션을 주고 한번에 만들 수도 있다.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;이제 이렇게 생성된 EKS Cluster 에 Istio를 설치하고 서비스를 deploy하면 바로 사용 가능하다. &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>aws</category>
    </item>
    <item>
      <title>Spring Boot 서비스를 위한 Kubernetes 설정</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Fri, 18 Feb 2022 16:12:22 +0000</pubDate>
      <link>https://dev.to/airoasis/spring-boot-seobiseureul-wihan-kubernetes-seoljeong-3d72</link>
      <guid>https://dev.to/airoasis/spring-boot-seobiseureul-wihan-kubernetes-seoljeong-3d72</guid>
      <description>&lt;h2&gt;
  
  
  들어가며
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/airoasis/kubernetes-reul-wihan-spring-boot-gaebal-feat-mujungdan-baepounyeong-2k0o"&gt;Part 1&lt;/a&gt;에서 Kubernetes를 위한 Spring Boot Application 개발/설정에 대해서 정리하였다. 여기서는 운영환경에서 Spring Boot Application 을 위한 Kubernetes 설정에 대해 정리를 한다. &lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;아래는 deployment 설정의 예시이다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;affinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;podAntiAffinity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;preferredDuringSchedulingIgnoredDuringExecution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;podAffinityTerm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;labelSelector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;matchExpressions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
                  &lt;span class="na"&gt;operator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;In&lt;/span&gt;
                  &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
              &lt;span class="na"&gt;topologyKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kubernetes.io/hostname&lt;/span&gt;
            &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server:latest&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;develop&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JAVA_TOOL_OPTIONS&lt;/span&gt;
            &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:5005&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-Duser.timezone=Asia/Seoul"&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/actuator/health/readiness&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
        &lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/actuator/health/liveness&lt;/span&gt;
            &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
          &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5Gi&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.5Gi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Pod Anti-affinity 설정
&lt;/h3&gt;

&lt;p&gt;이 설정은 pod가 여러 node 에 균일하게 배포되는것을 보장한다. 만약 replicas를 3으로 설정하였는데 모두 하나의 node에 배포되고 해당 node가 장애로 다운된다면 해당 서비스 또한 당분간 아예 서비스가 되지 않는다. 하지만 Pod Anti-affinity 설정으로 최대한 동일한 pod가 같은 node에 배포되는것을 방지하여 장애에 강한 서비스를 만들 수 있다. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;여기서 &lt;code&gt;preferredDuringSchedulingIgnoredDuringExecution&lt;/code&gt; 대신 &lt;code&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/code&gt; 를 사용하면 node에는 해당 pod가 하나밖에 생성 될수 없고, 추가로 scheduling 되어야 하는 pod는 pending 상태가 되어 node가 cluster 에 추가되면 그제서야 배치될 수 있다. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Spring Profile 설정
&lt;/h3&gt;

&lt;p&gt;Spring profile을 kubernetes의 environment variable을 통해 설정한다. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. JVM 설정
&lt;/h3&gt;

&lt;p&gt;Kubernets의 environment variable인 &lt;code&gt;JAVA_TOOL_OPTIONS&lt;/code&gt;를 통해 JVM 설정을 한다. &lt;/p&gt;

&lt;h4&gt;
  
  
  Remote debugging 설정
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=localhost:5005&lt;/code&gt; 를 통해 container 안에서 동작중인 spring boot application 을 debugging 할 수 있다. IntelliJ 에서의 설정은 &lt;a href="https://www.jetbrains.com/help/idea/tutorial-remote-debug.html"&gt;Tutorial: Remote debug&lt;/a&gt; 참고&lt;/p&gt;

&lt;h4&gt;
  
  
  Timezone 설정
&lt;/h4&gt;

&lt;p&gt;Docker image의 default timezone은 UTC이다. 결국 log도 UTC로 남아 로그를 분석할 때 좋지 않다. 따라서 &lt;code&gt;-Duser.timezone=Asia/Seoul&lt;/code&gt; 설정으로 JVM의 timezone을 한국시간으로 변경하면 log 보기가 수월하다. (참고로 timezone 설정은 application code &lt;code&gt;TimeZone.setDefault(TimeZone.getTimeZone("Asia/Seoul"));&lt;/code&gt; 로도 가능하다)&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Readiness &amp;amp; Liveness 설정
&lt;/h3&gt;

&lt;p&gt;1부에서 Spring Boot Actuator를 포함하여 Spring Boot Application 을 개발하여 위와 같이 &lt;code&gt;/health&lt;/code&gt; endpoint 를 활용하여 kubernetes 의 readiness 설정 및 liveness 설정을 할 수가 있다. &lt;/p&gt;

&lt;p&gt;Readiness 설정으로 새로 시작하는 Spring Boot 서비스가 완전히 start 된 후에 request가 들어가도록 하여 무중단 배포를 위해 반드시 필요한 설정이다. 그리고 Liveness 설정으로 더이상 서비스가 불가능한 경우 해당 pod로의 request 유입을 막고 restart하게 하여 다시 서비스가 가능하게 한다. &lt;/p&gt;

&lt;h3&gt;
  
  
  5. Resource request/limit 설정
&lt;/h3&gt;

&lt;p&gt;Request / Limit 설정은 좀... tricky 하다... application 의 성격에 따라 달리해야 하고, 실제 서비스를 운영하면서 적절한 설정을 찾아야 한다. Java의 일반적인 특징으로 처음 시작할때 CPU와 Memory의 사용량이 급격히 증가한다. 이를 감안하여 request/limit 설정이 필요하다.&lt;/p&gt;

&lt;p&gt;단, request/limit 의 best practice는 memory의 request 와 limit은 동일한 값으로 설정하고 cpu는 상대적으로 큰 limit이나 아예 설정을 하지않아 unbounded limit으로 설정하는 것이다. cpu는 compressible resource라 여러 pod가 cpu를 서로 사용하려고 할 때에 서비스는 단지 쓰로틀링되어 처리시간이 좀 더 걸리지만 memory는 incompressible resource라 memory 가 부족하면 Pod가 종료되고 새롭게 scheduling 되어야 한다. 따라서 이렇게 종료되어 서비스의 불안정을 막기 위해 memory 는 request와 limit을 동일하게 설정하여 되도록 이런 상황을 방지한다.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler"&gt;VPA&lt;/a&gt; (Vertical Pod Autoscaler)의 recommendation engine 을 활용하는 방법이 있지만 그렇게 도움은 되지 않는다... (&lt;a href="https://goldilocks.docs.fairwinds.com/#how-can-this-help-with-my-resource-settings"&gt;Goldilocks&lt;/a&gt; 를 이용하면 VPA의 추천값을 Web을 통해 확인가능하다.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Autoscale &amp;amp; Fault tolerance
&lt;/h2&gt;

&lt;p&gt;아래는 HPA 및 PDB 예시이다&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;autoscaling/v2beta2&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HorizontalPodAutoscaler&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server-hpa&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;scaleTargetRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
  &lt;span class="na"&gt;minReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;maxReplicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="na"&gt;metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Resource&lt;/span&gt;
    &lt;span class="na"&gt;resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cpu&lt;/span&gt;
      &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Utilization&lt;/span&gt;
        &lt;span class="na"&gt;averageUtilization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
  &lt;span class="na"&gt;behavior&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;scaleDown&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;stabilizationWindowSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
      &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Percent&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
    &lt;span class="na"&gt;scaleUp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;stabilizationWindowSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Percent&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pods&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
        &lt;span class="na"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;15&lt;/span&gt;
      &lt;span class="na"&gt;selectPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Max&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;policy/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PodDisruptionBudget&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server-pdb&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;minAvailable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main-server&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Horizontal Pod Autoscale (HPA)
&lt;/h3&gt;

&lt;p&gt;❗HPA를 사용할때는 deployment에서 replicas 설정은 절대 하면 안된다. &lt;br&gt;
예리한 reader라면 이미 알고 있을지 모르겠지만 위의 deployment 설정에 replicas 설정이 없는것을 이상하게 생각할 수도 있다. 하지만 이는 HPA 를 사용하기 때문이다. 관련 설명은 &lt;a href="https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#migrating-deployments-and-statefulsets-to-horizontal-autoscaling"&gt;여기&lt;/a&gt; 참고. 다른글을 통해 해당 내용을 설명하려고 한다. &lt;/p&gt;

&lt;p&gt;HPA를 통해 Autoscale을 할 수 있다. ✋여기서 주의할 점은 averageUtilization 의 기준은 resource request라는 점이다. 결국 지금 설정은 배포된 pod의 평균 CPU 사용량이 0.9 코어일 때 scale out을 한다. &lt;/p&gt;

&lt;p&gt;그리고 behavior 설정을 통해 scale up은 즉각 반응하도록 하고 scale down은 서서히 하도록 설정하였다. 이것 또한 application 이나 서비스의 특성에 따라 조절이 필요하다. &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Pod Disruption Budget (PDB)
&lt;/h3&gt;

&lt;p&gt;PDB는 운영에서 반드시 필요한 설정이다. Pod는 항상 설정된 replica의 수 만큼 유지되지만 시스템 관리로 인해 특정 node를 다운 시켜야 하는 경우, 또는 cluster autoscaler 가 node의 수를 줄이는 경우 등과 같은 이유로 pod의 수가 줄어들어야 하는 경우가 있다. &lt;/p&gt;

&lt;p&gt;이런 경우 PDB를 통해 최소한 운영 가능한 pod의 비율/개수를 정하거나 최대 서비스 가능하지 않은 pod의 비율/개수를 정하여 서비스의 안정성을 보장한다. 여기에서는 최소 1개의 pod가 항상 보장되게 설정하였다. 결국 node가 scale down 되어야 하는 상황에서 최소 보장되어야 하는 PDB 설정을 만족하지 못하는 해당 node는 다운되지 않고 기다리다가 만족하는 상황이 되면 그때 다운되어진다. &lt;/p&gt;

&lt;h2&gt;
  
  
  마치며
&lt;/h2&gt;

&lt;p&gt;여러가지 Kubernetes 설정을 통해 좀 더 안정적인 서비스 운영이 가능하다. JVM 옵션과 같이 Java 에만 국한된 설정도 있었지만 대부분 모든 서비스에서 필요한 general 한 설정이었던거 같다. 이 외에도 많은 설정이 운영에서 필요하겠지만 상황에 따라 필요한 설정을 추가하면서 운영하면 좋을거 같다. &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>java</category>
    </item>
    <item>
      <title>Kubernetes 를 위한 Spring Boot 개발 (feat. 무중단 배포/운영)</title>
      <dc:creator>Thomas Kim</dc:creator>
      <pubDate>Fri, 18 Feb 2022 11:55:36 +0000</pubDate>
      <link>https://dev.to/airoasis/kubernetes-reul-wihan-spring-boot-gaebal-feat-mujungdan-baepounyeong-2k0o</link>
      <guid>https://dev.to/airoasis/kubernetes-reul-wihan-spring-boot-gaebal-feat-mujungdan-baepounyeong-2k0o</guid>
      <description>&lt;h2&gt;
  
  
  들어가며
&lt;/h2&gt;

&lt;p&gt;현재 Kubernetes, Istio를 사용하여 Java, Vue, Python, Go 등으로 개발된 서비스를 개발/운영하고 있다. 우선 여기에서는 (Part 1) Spring Boot 설정 및 개발에 관련된 내용을 정리하고 Part 2에서는 kubernetes 에서 필요한 설정을 정리한다. &lt;/p&gt;

&lt;p&gt;Spring Boot 을 Kubernetes 에 넣기 위해서는 아래와 같은 설정 및 개발을 기존 코드에 추가해야 한다. &lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerize
&lt;/h2&gt;

&lt;p&gt;Spring Boot Application 을 dockerize 하는 방법은 &lt;a href="https://github.com/GoogleContainerTools/jib"&gt;Jib&lt;/a&gt;, Buildpacks, Dockerfile 이렇게 세가지가 있다. 이 중 가장? 간단하고 build time이 빠르고 (즉, 효율적인 layering), image 사이즈가 가장 작아지는 Jib 을 사용한다. Jib 의 사용법은 간단하다. 아래와 같이 &lt;code&gt;build.gradle&lt;/code&gt; 에 &lt;a href="https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin"&gt;jib gradle plugin&lt;/a&gt;을 추가하면 된다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'com.google.cloud.tools.jib'&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s1"&gt;'3.2.0'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;그리고 &lt;code&gt;./gradlew jib&lt;/code&gt;을 통해 image를 빌드 할 수 있다.&lt;/p&gt;

&lt;p&gt;참고로 로컬 kubernetes 개발 환경에서 &lt;a href="https://skaffold.dev/"&gt;skaffold&lt;/a&gt; 를 사용한다면 아래와 같이 &lt;code&gt;jib: {}&lt;/code&gt; 만 추가하면 알아서 로컬 kubernetes 에 빌드/배포해준다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;skaffold/v2beta27&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Config&lt;/span&gt;
&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;example/image-name&lt;/span&gt;
      &lt;span class="na"&gt;context&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./example-app&lt;/span&gt;
      &lt;span class="na"&gt;jib&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;아래는 Github Actions를 사용하여 이미지를 build 하고 AWS ECR에 image를 push 하는 코드의 일부이다&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up JDK &lt;/span&gt;&lt;span class="m"&gt;11&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-java@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;java-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;11&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Grant execute permission for gradlew&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chmod +x gradlew&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Push with Gradle&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build-and-push-to-ecr&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./gradlew jib -x test --image $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Java 11 이상 사용을 권장한다. Java 8 은 containerize 된 환경에 최적화되지 않아 JVM이 효율적으로 운영되지 못한다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Health Check APIs
&lt;/h2&gt;

&lt;p&gt;Kubernetes 의 readiness, liveness 설정을 위해 Spring Boot application 의 health check API 가 필요하다. 다행히 &lt;a href="https://github.com/spring-projects/spring-boot/tree/main/spring-boot-project/spring-boot-actuator"&gt;Spring Boot Actuator&lt;/a&gt; 에서 이 기능을 제공한다. 아래와 같이 &lt;code&gt;build.gradle&lt;/code&gt; 에 dependency로 추가만 하면 된다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="k"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'org.springframework.boot:spring-boot-starter-actuator'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Spring Boot 2 부터는 Actuator의 &lt;code&gt;/health&lt;/code&gt; 와 &lt;code&gt;/info&lt;/code&gt; 를 제외한 모든 endpoint가 disable 되어 있다. 만약 다른 endpoint 도 사용하고 싶다면 &lt;code&gt;application.properties&lt;/code&gt; 에 추가적인 설정이 필요하다. 그렇지 않다면 그냥 dependenty 만 추가하고 그 외 추가적인 설정이나 개발은 필요없다. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;kubernetes 의 readiness 설정을 하지 않으면 rolling update 시 또는 autoscale을 통해 새로 pod 가 생길때 spring boot application이 완전히 뜨기 전에 request가 들어가고 이렇게 들어온 request는 ingress가 &lt;code&gt;503&lt;/code&gt;을 리턴하게 된다. 또한 liveness 설정이 없으면 예기치못한 상황으로 spring boot application 이 죽었을 때 서비스가 새로 시작하지 않고 계속 죽어있게 된다.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graceful shutdown
&lt;/h2&gt;

&lt;p&gt;운영환경에서 Pod가 termination 되는 상황은 rolling update로 배포를 하거나 kubernetes autoscale 을 통해 늘어난 Pod가 줄어들 때 등이 있을수 있다. 이때 kubernetes는 SIGTERM 시그널을 보내고 Pod안의 Spring Boot Application 은 종료가 된다. &lt;/p&gt;

&lt;p&gt;하지만 Spring Boot의 default 설정은 시그널을 받자마자 종료되도록 되어 있고, 만약 종료될때 들어온 request가 완료되기 전에 Spring Boot application 이 내려가면 해당 request를 보낸쪽에서는 HTTP STATUS &lt;code&gt;503&lt;/code&gt;을 받게 된다.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;실제 운영환경 (또는 load testing 시)에서 이 문제는 application log 에서는 확인하지 못하고 kubernetes ingress 에서 &lt;code&gt;503&lt;/code&gt; 확인이 가능하다.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;이를 graceful 하게 처리하기 위해서는 graceful shutdown 설정을 해야 한다. 이것 또한 간단하다. 아래와 같이 &lt;code&gt;application.properties&lt;/code&gt; 파일에 아래와 같이 추가하면 된다. (단, Spring Boot 2.3 부터 가능한 option 이다.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;server.shutdown=graceful&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;해당 설정이 추가되면 tomcat 이 종료 시그널을 받았을때 처리중인 request가 있다면 이를 모두 처리하고 application 이 종료된다. 하지만 들어온 request가 종료될 때까지 무한정 기다리는것은 아니다. default 설정은 30초간 기다리고 그때까지 종료하지 못한다면 강제 종료된다. (일반적인 경우는 default 설정이면 충분하다) 이 설정은 아래와 같이 변경 가능하다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;spring.lifecycle.timeout-per-shutdown-phase=1m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Loading HikariCP
&lt;/h2&gt;

&lt;p&gt;Spring Boot 2부터 Hikari 가 default DataSouce 구현체이다. &lt;code&gt;spring-boot-starter-data-jpa&lt;/code&gt; 나 &lt;code&gt;spring-boot-starter-jdbc&lt;/code&gt;를 사용한다면 별도의 설정없이 Hikari를 사용하게 된다. Hikari는 Connection Pool (HikariCP)을 사용하여 DB connection 을 관리하는데 Spring Boot application 이 설정/개발에 따라 Hikari connection pool을 Spring Boot 이 시작할 때 바로 만들지 않고, DB 관련 request가 처음 들어와서 처리 할 때 그제서야 Hikari를 initialize 하면서 connection pool을 생성하기도 한다. (경험으로는 Hibernate를 사용하면 application 이 올라갈때 바로 connection pool을 생성하고 MyBatis는 그렇지 않았다)&lt;/p&gt;

&lt;p&gt;사실 이러한 과정은 일반적인 상황에서는 문제가 되지 않는다. 하지만 요청이 폭발하는 상황에서 kubernetes 가 autoscale을 통해 새로운 pod를 생성하고 이렇게 생성된 pod가 바로 많은 요청을 받는 상황에서는 몇초간 latency가 매우 높아진다. &lt;/p&gt;

&lt;p&gt;우선 현재 Spring Boot application 이 언제 connection pool을 생성하는지 확인하려면 &lt;code&gt;application.properties&lt;/code&gt; 에 아래와 같이 설정을 추가하여 hikari log를 남기고, application을 실행해 보자. (테스트 후 반드시 해당 설정을 제거하자. 특히 운영환경에서는...)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;logging.level.com.zaxxer.hikari.HikariConfig=DEBUG&lt;/span&gt; 
&lt;span class="s"&gt;logging.level.com.zaxxer.hikari=TRACE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;만약 application 이 시작하면서 Hikari 설정 관련 log 가 나오면서 connection pool 이 생성된다면 문제가 되지 않는다. 하지만 그렇지 않다면 아래와 같이 connection pool을 application 이 시작할 때 만들어주어 몇초동안 latency가 급격히 높아지는 현상을 줄일 수 있다.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HikariLoader&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRunner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;HikariDataSource&lt;/span&gt; &lt;span class="n"&gt;hikariDataSource&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;HikariLoader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HikariDataSource&lt;/span&gt; &lt;span class="n"&gt;hikariDataSource&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hikariDataSource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hikariDataSource&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApplicationArguments&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;SQLException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;hikariDataSource&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConnection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  마치며
&lt;/h2&gt;

&lt;p&gt;Kubernets가 운영에 필요한 많은 일들을 해주지만 이렇게 추가적인 개발/설정 없이는 제대로 운영할 수가 없다. 그래도 Java는 다른 언어에 비해 굉장히 간단하게 이러한 설정들을 추가 할 수가 있다. Part 2에서는 실제 운영환경에서 필요한 Kubernetes 관련 설정들을 정리하려고 한다.  &lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>java</category>
    </item>
  </channel>
</rss>
