AWSのKubernetesで オートスケール~EC2のオートスケールもやっちゃう~

AWSのKubernetesで オートスケール~EC2のオートスケールもやっちゃう~:


はじめに

今回はEKSでオートスケールに挑戦します。

サービスを保守・運用していく中で、リソースが枯渇してサービスの継続が困難になることは避けたいです。

KubernetesではPod(コンテナ)にリソースを割り当てることができ、リソースが不足すると自動でPodを増やす機能があります。
Horizontal Pod Autoscaler

ただEC2インスタンスのリソースが不足した場合は、Podのオートスケールは機能しません。(リソースがないから。。。)

なので、EC2インスタンスのオートスケールも必要になってきます。

cluster-autoscalerという機能を使ってEC2インスタンスのオートスケールも設定します。

これを設定することで、リソースが必要なときのみEC2インスタンスを作成し、リソースが不要になった場合はEC2インスタンスを自動で削除します。


Architecture

  • cluster autoscaler

    EC2インスタンスのオートスケールを担当する機能です。
  • kube2iam

    PodからAWSリソースを操作するためのIAM権限の管理をするツール。

    cluster autoscalerを動かすためのIAM Roleをkube2iamを使って管理します。

    kube2iamのデプロイは本記事では説明しません。AWSのKubernetesでサービスを公開する最高の方法~ALB,ACM,Route53の自動作成~こちらを参考にしてください。
  • Metrics Server

    クラスタのリソース使用量を集計してくれるツール。Podがオートスケールするために必要。前まではheapsterというツールだったが、HeapsterはKubernetes 1.11でDeprecateされた。
  • Helm

    Kubernetesのパッケージマネージャーです。

    Metrics Serverをインストールするのに使用します。


やっていく


前提

  • EKS Clusterが構築済みであること
    構築されていない方はeksctlコマンドを使ってみてください!


Install Helm

README通りにやっていきます。

$ curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get > get_helm.sh 
$ chmod +x get_helm.sh 
$./get_helm.sh 
RBACマニフェストを作成します。

cat <<EoF > ./rbac.yaml 
--- 
apiVersion: v1 
kind: ServiceAccount 
metadata: 
  name: tiller 
  namespace: kube-system 
--- 
apiVersion: rbac.authorization.k8s.io/v1beta1 
kind: ClusterRoleBinding 
metadata: 
  name: tiller 
roleRef: 
  apiGroup: rbac.authorization.k8s.io 
  kind: ClusterRole 
  name: cluster-admin 
subjects: 
  - kind: ServiceAccount 
    name: tiller 
    namespace: kube-system 
EoF 
マニフェストファイルをデプロイしてinitコマンドを実行します。

$ kubectl apply -f ~/environment/rbac.yaml 
$ helm init --service-account tiller 
これでHelmのインストールは完了です。


Install Metrics Server

Metrics Serverのインストールはhelmコマンドで一発でできます。

$ helm install stable/metrics-server \ 
    --name metrics-server \ 
    --version 2.0.4 \ 
    --namespace kube-system 
OK!

https://github.com/kubernetes-incubator/metrics-server

https://eksworkshop.com/scaling/deploy_hpa/

https://kubernetes.io/docs/tasks/debug-application-cluster/core-metrics-pipeline/#metrics-server


cluster autoscaler


Create IAM Role

まずはcluster autoscalerに割り当てるポリシーを記載したclusterAutoscale-iam-policy.jsonファイルを作成します。

{ 
  "Version": "2012-10-17", 
  "Statement": [ 
      { 
          "Effect": "Allow", 
          "Action": [ 
              "autoscaling:DescribeAutoScalingGroups", 
              "autoscaling:DescribeAutoScalingInstances", 
              "autoscaling:DescribeTags", 
              "autoscaling:DescribeLaunchConfigurations", 
              "autoscaling:SetDesiredCapacity", 
              "autoscaling:TerminateInstanceInAutoScalingGroup" 
          ], 
          "Resource": "*" 
      } 
  ] 
} 
次にcluster-autoscalerという名前のRoleを作成します。

export NODE_INSTANCE_ROLE_ARN=$(aws iam list-roles --query "Roles[?contains(RoleName,\`NodeInstanceRole\`)].[Arn][]" --output text) 
 
cat << EOF  | jq > config/pod-role-trust-policy.json 
{ 
    "Version": "2012-10-17", 
    "Statement": [ 
      { 
        "Effect": "Allow", 
        "Principal": { 
          "Service": "ec2.amazonaws.com" 
        }, 
        "Action": "sts:AssumeRole" 
      }, 
      { 
        "Effect": "Allow", 
        "Principal": { 
          "AWS": "${NODE_INSTANCE_ROLE_ARN}" 
        }, 
        "Action": "sts:AssumeRole" 
      } 
    ] 
  } 
EOF 
 
$ aws iam create-role \ 
  --role-name cluster-autoscaler \ 
  --assume-role-policy-document file://pod-role-trust-policy.json 
 
$ CLUSTER_AUTOSCALER_ARN=$(aws iam create-policy --policy-name clusterAutoscaler-iam-policy --policy-document file://clusterAutoscale-iam-policy.json --query "Policy.[Arn]" --output text) 
 
$ aws iam attach-role-policy --role-name cluster-autoscaler --policy-arn $CLUSTER_AUTOSCALER_ARN 


AutoScalingGroupにタグを付与

cluster autoscalerが自動でAutoScalingGroupを見つけるために、AutoScalingGroupにタグを付与します。

Nameがk8s.io/cluster-autoscaler/enabledでValueはtrueにします。

もう1つ設定します。をご自身の環境のクラスタ名に読み替えて設定してください。Valueは不要です。
k8s.io/cluster-autoscaler/<Your Cluster Name>


cluster autoscalerのデプロイ

まずはymlファイルをダウンロードします。

$ wget https://raw.githubusercontent.com/kubernetes/autoscaler/master/cluster-autoscaler/cloudprovider/aws/examples/cluster-autoscaler-one-asg.yaml 
cluster-autoscaler-one-asg.yamlファイルの中を3箇所変更します。

Deploymentのspecのmetadataにannotationsを追加します。これを追加することでAutoScalintGroupの操作をする権限をPodに付与します。kube2iamの機能になります。
<Arn of cluster-autoscaler Role>IAMロールの作成で作成したcluster-autoscalerロールのArnです。

spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      app: cluster-autoscaler 
  template: 
    metadata: 
      labels: 
        app: cluster-autoscaler 
      annotations: 
        iam.amazonaws.com/role: <Arn of cluster-autoscaler Role> 
commandの引数の--nodesをコメントアウトします。

これはAutoScalingGroupのミニマムとマックスを手動で設定する場合です。今回はAutoScalingGroupにタグを付与したため、自動で設定されます。

command: 
            - ./cluster-autoscaler 
            - --v=4 
            - --stderrthreshold=info 
            - --cloud-provider=aws 
            - --skip-nodes-with-local-storage=false 
            # - --nodes=1:5:NODEGROUP_NAME 
EC2インスタンスとKubernetes APIが通信するときに使用するcrtファイルを変更します。OSにAmazon Linux 2を使っている方は、/etc/ssl/certs/ca-bundle.crtを記述してください。

めちゃめちゃハマりましたが、ちゃんとドキュメントには書いてました。ドキュメント大事。

volumes: 
  - name: ssl-certs 
    hostPath: 
      # path: "/etc/ssl/certs/ca-certificates.crt" 
      path: "/etc/ssl/certs/ca-bundle.crt" 
これで準備できました!

デプロイしましょう!

$ kubectl apply -f cluster-autoscaler-one-asg.yaml 
エラーログが出ていなかったら成功です!

$ kubectl logs -n kube-system cluster-autoscaler-XXXXX 


試してみる

それではサンプルアプリをデプロイしてオートスケールを試してみましょう!

下記のymlファイルをデプロイするとDeployment, Service, HorizontalPodAutoscalerが作られます。

apiVersion: extensions/v1beta1 
kind: Deployment 
metadata: 
  name: nginx-test 
  labels: 
    name: nginx-test 
  namespace: default 
spec: 
  replicas: 1 
  selector: 
    matchLabels: 
      name: nginx-test 
  template: 
    metadata: 
      labels: 
        name: nginx-test 
    spec: 
      containers: 
      - name: nginx-test 
        image: nginx:stable-alpine 
        ports: 
        - name: nginx-port 
          containerPort: 80 
        resources: 
          requests: 
            cpu: 100m 
            memory: 30Mi 
          limits: 
            cpu: 100m 
            memory: 30Mi 
      dnsConfig: 
        nameservers: 
          - 8.8.8.8 
--- 
kind: Service 
apiVersion: v1 
metadata: 
  name: nginx-test 
  labels: 
    name: nginx-test 
  namespace: default 
spec: 
  selector: 
    name: nginx-test 
  ports: 
  - name: nginx-port 
    port: 80 
    targetPort: 80 
    protocol: TCP 
  type: "NodePort" 
--- 
apiVersion: autoscaling/v2beta1 
kind: HorizontalPodAutoscaler 
metadata: 
  name: nginx-test-scale 
  namespace: default 
spec: 
  scaleTargetRef: # ここでautoscale対象となるscaled resource objectを指定 
    apiVersion: extensions/v1beta1 
    kind: Deployment 
    name: nginx-test 
  minReplicas: 1 # 最小レプリカ数 
  maxReplicas: 10 # 最大レプリカ数 
  metrics: 
  - type: Resource 
    resource: 
      name: cpu 
      targetAverageUtilization: 5 # CPU使用率が常に5%になるように指定 
作ったPodに対してGolangで負荷をかけてみます!

(Golangよく分かんないよという方はkubectl execでPodに接続し て yes > /dev/null コマンドでも負荷かけれます。)

package main 
 
import ( 
"net/http" 
"sync" 
"log" 
"time" 
) 
 
func main() { 
url := "http://192.168.100.100:30534/" // アクセスするURLだよ! 
 
maxConnection := make(chan bool,1000) // 同時に並列する数をしていできるよ!(第二引数) 
wg := &sync.WaitGroup{} // 並列処理が終わるまでSleepしてくれる便利なやつだよ! 
 
count := 0 // いくつアクセスが成功したかをアカウントするよ! 
start := time.Now() // 処理にかかった時間を測定するよ! 
for maxRequest := 0; maxRequest < 50000; maxRequest ++{ // 10000回リクエストを送るよ! 
wg.Add(1) // wg.add(1)とすると並列処理が一つ動いていることを便利な奴に教えるよ! 
maxConnection <- true // ここは並列する数を抑制する奴だよ!詳しくはググって! 
go func() { // go func(){/*処理*/}とやると並列処理を開始してくれるよ! 
defer wg.Done() // wg.Done()を呼ぶと並列処理が一つ終わったことを便利な奴に教えるよ! 
 
resp, err := http.Get(url) // GETリクエストでアクセスするよ! 
if err != nil { // err ってのはエラーの時にエラーの内容が入ってくるよ! 
return // 回線が狭かったりするとここでエラーが帰ってくるよ! 
} 
defer resp.Body.Close() // 関数が終了するとなんかクローズするよ!(おまじない的な) 
 
count++ // アクセスが成功したことをカウントするよ! 
<-maxConnection // ここは並列する数を抑制する奴だよ!詳しくはググって! 
}() 
} 
wg.Wait() // ここは便利な奴が並列処理が終わるのを待つよ! 
end := time.Now() // 処理にかかった時間を測定するよ! 
log.Printf("%d 回のリクエストに成功しました!\n", count) // 成功したリクエストの数を表示してくれるよ! 
log.Printf("%f 秒処理に時間がかかりました!\n",(end.Sub(start)).Seconds()) //何秒かかったかを表示するよ! 
} 
では負荷をかけます。

$ go run stress.go 


k8s-as-st2


素晴らしい。。。

スクショ忘れましたが、EC2インスタンスの数もAutoScalingGroupのMaxまで増えました!


まとめ

EKS最高!

EKSで何でもできるので、お仕事ください!w


References

https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler
https://medium.com/@alejandro.millan.frias/cluster-autoscaler-in-amazon-eks-d9f787176519
https://qiita.com/os1ma/items/7b48decb71d0c1f9ea3e#%E3%83%AF%E3%83%BC%E3%82%AB%E3%83%BC%E3%82%B0%E3%83%AB%E3%83%BC%E3%83%97%E3%81%AE%E7%99%BB%E9%8C%B2

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)