K8S 3: Using eksctl on Amazon Linux EC2

cách 2 là dùng `eksctl` là CLI của AWS phát triển, nhiệm vụ tương tự như Service EKS, nhưng ta làm việc với nó trên CLI/terminal

Table of Contents

Giới thiệu

Để vọc Kubernetes trên AWS, có nhiều cách:

cách 1 là dùng Service EKS của AWS, làm việc trên Console luôn, rất trực quan

cách 2 là dùng eksctl là CLI của AWS phát triển, nhiệm vụ tương tự như Service EKS, nhưng ta làm việc với nó trên CLI/terminal

cách 3 là tạo 1 EC2 Ubuntu 18.04 LTS (t2.medium trở lên), cài minikube lên nó, dựng 1 cluster

=> cách 1 và 2 khá tốn kém, nhưng bạn có thể dùng full service, gần với môi trường production nhất,
cách 3 thì rẻ hơn nhiều, các bạn chỉ tốn phí duy trì con EC2 Ubuntu thôi, tuy nhiên cách này chỉ nên dùng để vọc vạch, dùng “cho biết” thế nào là k8s thôi 😆

Bài này mình sẽ hướng dẫn cách 2, dùng eksctl, các bài trước đã nói về cách 3 rồi

Chuẩn bị

Launch 1 EC2 Amazon Linux, t2.micro là đủ, ssh vào rồi làm việc

install kubectl

curl -o kubectl https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/linux/amd64/kubectl
chmod +x ./kubectl
mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$HOME/bin:$PATH
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
kubectl version --short --client

install aws-iam-authenticator on Linux

curl -o aws-iam-authenticator https://amazon-eks.s3-us-west-2.amazonaws.com/1.14.6/2019-08-22/bin/linux/amd64/aws-iam-authenticator
chmod +x ./aws-iam-authenticator
mkdir -p $HOME/bin && cp ./aws-iam-authenticator $HOME/bin/aws-iam-authenticator && export PATH=$HOME/bin:$PATH
echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
aws-iam-authenticator help

install eksctl

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

set default region, và set account Docker Hub

export AWS_DEFAULT_REGION=us-east-1
export DOCKER_USERNAME=AAAAAAA
export DOCKER_PASSWORD=BBBBBBB
export DOCKER_USER_ID=CCCCCC

Use AWS Console, create IAM Role and attach to EC2 with this policy:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "iam:CreateInstanceProfile",
                "iam:DeleteInstanceProfile",
                "iam:GetRole",
                "iam:GetInstanceProfile",
                "iam:ListRoleTags",
                "iam:UntagRole",
                "iam:TagRole",
                "iam:RemoveRoleFromInstanceProfile",
                "iam:CreateRole",
                "iam:DeleteRole",
                "iam:AttachRolePolicy",
                "iam:PutRolePolicy",
                "iam:ListInstanceProfiles",
                "iam:AddRoleToInstanceProfile",
                "iam:ListInstanceProfilesForRole",
                "iam:PassRole",
                "iam:CreateServiceLinkedRole",
                "iam:DetachRolePolicy",
                "iam:DeleteRolePolicy",
                "iam:DeleteServiceLinkedRole",
                "ec2:DeleteInternetGateway",
                "iam:GetOpenIDConnectProvider",
                "iam:GetRolePolicy"
            ],
            "Resource": [
                "arn:aws:iam::793459850633:instance-profile/eksctl-*",
                "arn:aws:iam::*:oidc-provider/*",
                "arn:aws:iam::793459850633:role/eksctl-*",
                "arn:aws:ec2:*:*:internet-gateway/*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:DeleteSubnet",
                "ec2:AttachInternetGateway",
                "ec2:DeleteRouteTable",
                "ec2:AssociateRouteTable",
                "ec2:DescribeInternetGateways",
                "elasticloadbalancing:DescribeLoadBalancers",
                "autoscaling:DescribeAutoScalingGroups",
                "ec2:CreateRoute",
                "ec2:CreateInternetGateway",
                "ec2:RevokeSecurityGroupEgress",
                "autoscaling:UpdateAutoScalingGroup",
                "ec2:DeleteInternetGateway",
                "ec2:DescribeKeyPairs",
                "ec2:DescribeRouteTables",
                "ec2:ImportKeyPair",
                "ec2:DescribeLaunchTemplates",
                "ec2:CreateTags",
                "ec2:CreateRouteTable",
                "cloudformation:*",
                "ec2:RunInstances",
                "ec2:DetachInternetGateway",
                "ec2:DisassociateRouteTable",
                "ec2:RevokeSecurityGroupIngress",
                "ec2:DescribeImageAttribute",
                "ec2:DeleteNatGateway",
                "autoscaling:DeleteAutoScalingGroup",
                "ec2:DeleteVpc",
                "ec2:CreateSubnet",
                "ec2:DescribeSubnets",
                "eks:*",
                "autoscaling:CreateAutoScalingGroup",
                "ec2:DescribeAddresses",
                "ec2:DeleteTags",
                "ec2:CreateNatGateway",
                "autoscaling:DescribeLaunchConfigurations",
                "ec2:CreateVpc",
                "ec2:DescribeVpcAttribute",
                "autoscaling:DescribeScalingActivities",
                "ec2:DescribeAvailabilityZones",
                "ec2:CreateSecurityGroup",
                "ec2:ModifyVpcAttribute",
                "ec2:ReleaseAddress",
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:DeleteLaunchTemplate",
                "ec2:DescribeTags",
                "ec2:DeleteRoute",
                "ec2:DescribeLaunchTemplateVersions",
                "ec2:DescribeNatGateways",
                "ec2:AllocateAddress",
                "ec2:DescribeSecurityGroups",
                "autoscaling:CreateLaunchConfiguration",
                "ec2:DescribeImages",
                "ec2:CreateLaunchTemplate",
                "autoscaling:DeleteLaunchConfiguration",
                "ec2:DescribeVpcs",
                "ec2:DeleteSecurityGroup"
            ],
            "Resource": "*"
        }
    ]
}

Cách tạo 1 cluster bằng eksctl

Create a file cluster.yaml

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: base-project
  region: us-east-1

availabilityZones: ["us-east-1a", "us-east-1d"]

nodeGroups:
  - name: nodegrp-1
    instanceType: t2.medium
    desiredCapacity: 1

Apply above config file to create cluster:

eksctl create cluster -f cluster.yaml

chờ khoảng 10 phút vì eks sẽ provision ra network rất tốn time:

more sample template https://github.com/weaveworks/eksctl/blob/master/examples

Get pods, service, nodes, is running

kubectl get pods,svc,node -A
[ec2-user@ip-172-31-84-250 ~]$ kubectl get pods,svc,node -A
NAMESPACE     NAME                              READY   STATUS    RESTARTS   AGE
kube-system   pod/aws-node-6k2qq                1/1     Running   0          3h28m
kube-system   pod/coredns-8455f84f99-rzn44      1/1     Running   0          3h34m
kube-system   pod/coredns-8455f84f99-xw2rv      1/1     Running   0          3h34m
kube-system   pod/kube-proxy-w2t7v              1/1     Running   0          3h28m


NAMESPACE     NAME                     TYPE           CLUSTER-IP       EXTERNAL-IP
              PORT(S)         AGE
default       service/kubernetes       ClusterIP      10.100.0.1       <none>
              443/TCP         3h34m
kube-system   service/kube-dns         ClusterIP      10.100.0.10      <none>
              53/UDP,53/TCP   3h34m  


NAMESPACE   NAME                                  STATUS   ROLES    AGE     VERSION
            node/ip-192-168-62-230.ec2.internal   Ready    <none>   3h28m   v1.14.7-eks-1861c5

Run app micro-service on k8s

clone source về

cd ~
git clone https://github.com/hoangmnsd/k8s-mastery

tạo pod frontend:

cd /home/ec2-user/k8s-mastery/resource-manifests
kubectl apply -f sa-frontend-deployment.yaml

sau khi apply thì pods và services, node như sau:

ip-172-31-84-250 ~]$ kubectl get pods,svc,node -A
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
default       pod/sa-frontend-54789d8b7d-2whc4   1/1     Running   0          2m36s
default       pod/sa-frontend-54789d8b7d-dbh4p   1/1     Running   0          2m36s
kube-system   pod/aws-node-7t2df                 1/1     Running   0          9m5s
kube-system   pod/coredns-8455f84f99-w8dlf       1/1     Running   0          15m
kube-system   pod/coredns-8455f84f99-xqk2f       1/1     Running   0          15m
kube-system   pod/kube-proxy-xzhwd               1/1     Running   0          9m5s

NAMESPACE     NAME                 TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)         AGE
default       service/kubernetes   ClusterIP   10.100.0.1    <none>        443/TCP         15m
kube-system   service/kube-dns     ClusterIP   10.100.0.10   <none>        53/UDP,53/TCP   15m

NAMESPACE   NAME                                  STATUS   ROLES    AGE    VERSION
            node/ip-192-168-34-208.ec2.internal   Ready    <none>   9m5s   v1.14.7-eks-1861c5

ip-172-31-84-250 ~]$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        483M   60K  483M   1% /dev
tmpfs           493M     0  493M   0% /dev/shm
/dev/xvda1      7.9G  2.8G  5.0G  36% /

tạo service frondend:

kubectl create -f service-sa-frontend-lb.yaml

get services:

kubectl get svc -A
NAMESPACE     NAME             TYPE           CLUSTER-IP     EXTERNAL-IP                                                              PORT(S)         AGE
default       kubernetes       ClusterIP      10.100.0.1     <none>                                                                   443/TCP         21m
default       sa-frontend-lb   LoadBalancer   10.100.64.73   a40ec71f8061811ea91b70a830ca64b1-169405131.us-east-1.elb.amazonaws.com   80:31360/TCP    102s
kube-system   kube-dns         ClusterIP      10.100.0.10    <none>                                                                   53/UDP,53/TCP   21m

check frontend app trên LB URL: a40ec71f8061811ea91b70a830ca64b1-169405131.us-east-1.elb.amazonaws.com

Tạo pod và service của backend logic:

cd /home/ec2-user/k8s-mastery/resource-manifests
kubectl apply -f sa-logic-deployment.yaml --record
kubectl apply -f service-sa-logic.yaml

get pods, svc:

ip-172-31-84-250 resource-manifests]$ kubectl get pods,svc -A
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
default       pod/sa-frontend-54789d8b7d-2whc4   1/1     Running   0          19m
default       pod/sa-frontend-54789d8b7d-dbh4p   1/1     Running   0          19m
default       pod/sa-logic-7d7ff8f6dc-r9tcz      1/1     Running   0          118s
default       pod/sa-logic-7d7ff8f6dc-zhwv2      1/1     Running   0          118s
kube-system   pod/aws-node-7t2df                 1/1     Running   0          25m
kube-system   pod/coredns-8455f84f99-w8dlf       1/1     Running   0          31m
kube-system   pod/coredns-8455f84f99-xqk2f       1/1     Running   0          31m
kube-system   pod/kube-proxy-xzhwd               1/1     Running   0          25m

NAMESPACE     NAME                     TYPE           CLUSTER-IP     EXTERNAL-IP                                                              PORT(S)         AGE
default       service/kubernetes       ClusterIP      10.100.0.1     <none>                                                                   443/TCP         31m
default       service/sa-frontend-lb   LoadBalancer   10.100.64.73   a40ec71f8061811ea91b70a830ca64b1-169405131.us-east-1.elb.amazonaws.com   80:31360/TCP    11m
default       service/sa-logic         ClusterIP      10.100.97.45   <none>                                                                   80/TCP          4s
kube-system   service/kube-dns         ClusterIP      10.100.0.10    <none>                                                                   53/UDP,53/TCP   31m

Tạo pod và service của backend webapp:

cd /home/ec2-user/k8s-mastery/resource-manifests
kubectl apply -f sa-web-app-deployment.yaml --record
kubectl apply -f service-sa-web-app-lb.yaml

get pods,svc:

ip-172-31-84-250 resource-manifests]$ kubectl get pods,svc -A
NAMESPACE     NAME                               READY   STATUS    RESTARTS   AGE
default       pod/sa-frontend-54789d8b7d-2whc4   1/1     Running   0          21m
default       pod/sa-frontend-54789d8b7d-dbh4p   1/1     Running   0          21m
default       pod/sa-logic-7d7ff8f6dc-r9tcz      1/1     Running   0          4m8s
default       pod/sa-logic-7d7ff8f6dc-zhwv2      1/1     Running   0          4m8s
default       pod/sa-web-app-5f7d8fd94d-lbzj8    1/1     Running   0          27s
default       pod/sa-web-app-5f7d8fd94d-ql5k2    1/1     Running   0          27s
kube-system   pod/aws-node-7t2df                 1/1     Running   0          27m
kube-system   pod/coredns-8455f84f99-w8dlf       1/1     Running   0          33m
kube-system   pod/coredns-8455f84f99-xqk2f       1/1     Running   0          33m
kube-system   pod/kube-proxy-xzhwd               1/1     Running   0          27m

NAMESPACE     NAME                     TYPE           CLUSTER-IP      EXTERNAL-IP                                                              PORT(S)         AGE
default       service/kubernetes       ClusterIP      10.100.0.1      <none>                                                                   443/TCP         33m
default       service/sa-frontend-lb   LoadBalancer   10.100.64.73    a40ec71f8061811ea91b70a830ca64b1-169405131.us-east-1.elb.amazonaws.com   80:31360/TCP    13m
default       service/sa-logic         ClusterIP      10.100.97.45    <none>                                                                   80/TCP          2m14s
default       service/sa-web-app-lb    LoadBalancer   10.100.34.198   a289bb56f061a11ea86df120e903ee59-714828784.us-east-1.elb.amazonaws.com   80:32321/TCP    5s
kube-system   service/kube-dns         ClusterIP      10.100.0.10     <none>                                                                   53/UDP,53/TCP   33m

sửa cái App.js để nó fetch URL của sa-webapp

nano ~/k8s-mastery/sa-frontend/src/App.js

sửa như sau (dùng ELB URL của sa-webapp):

fetch('http://a7cf3208e067911ea90de1247d6da376-1135942870.us-east-1.elb.amazonaws.com/sentiment', {

build docker images và push lại lên Docker Hub:

npm run build
sudo docker build -f Dockerfile -t $DOCKER_USER_ID/sentiment-analysis-frontend:28 .
sudo docker push $DOCKER_USER_ID/sentiment-analysis-frontend:28

sửa deployment config để dùng image mới tag:28

nano ~/k8s-mastery/resource-manifests/sa-frontend-deployment-update.yaml

sửa chỗ image thành dùng bản tag :28,
cần chú ý chỗ hoangmnsd đó là account Docker Hub của mình, bạn cần dùng account Docker Hub của bạn, ví dụ là CCCCCC mà bạn đã setting ở trên

image: hoangmnsd/sentiment-analysis-frontend:28

apply lại:

kubectl apply -f sa-frontend-deployment-update.yaml  --record

check lại app để thông luồng từ frontend -> backend:
a40ec71f8061811ea91b70a830ca64b1-169405131.us-east-1.elb.amazonaws.com

SSH vào Node trong Cluster

Nếu muốn có thể ssh vào Node, trước tiên cần tạo ssh key ssh-keygen:

ip-172-31-84-250 ~]$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ec2-user/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ec2-user/.ssh/id_rsa.
Your public key has been saved in /home/ec2-user/.ssh/id_rsa.pub.

khi tạo cluster cần tạo với file config như sau:

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: base-project
  region: us-east-1

availabilityZones: ["us-east-1a", "us-east-1d"]

nodeGroups:
  - name: nodegrp-1
    instanceType: t2.medium
    desiredCapacity: 1
    ssh: # import public key from file
      publicKeyPath: /home/ec2-user/.ssh/id_rsa.pub

Khi ssh vào thì dùng command sau:

ssh -i ~/.ssh/id_rsa ec2-user@<EC2-PUBLIC-IP>

Tạo k8s dashboard

Do mình dùng bản recommend bị ko vào dc từ windows browser, nên dùng bản alternative:

kubectl create -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta5/aio/deploy/alternative.yaml

tạo serviceaccount:

kubectl create serviceaccount my-dashboard-sa -n kubernetes-dashboard

tạo clusterrolebinding:

kubectl create clusterrolebinding my-dashboard-sa \
--clusterrole=cluster-admin \
--serviceaccount=kubernetes-dashboard:my-dashboard-sa

get secret:

kubectl get secrets -n kubernetes-dashboard

Lấy token:

kubectl describe secret -n kubernetes-dashboard <TOKEN_NAME>

dùng extension REQUESTLY của Chrome, link cài:
https://chrome.google.com/webstore/detail/requestly-redirect-url-mo/mdnleldcmiljblolnjhpnblkcekpdkpa config như hình sau, mỗi khi vào cái IP kia thì nó sẽ tự động modify Header và add thêm TOKEN vào cho mình

port-forward để bên ngoài có thể access:

kubectl port-forward -n kubernetes-dashboard service/kubernetes-dashboard 9090:80 --address 0.0.0.0

Done! Bạn sẽ vào được k8s Dashboard từ Chrome browser trên windows với link http://<EC-PUBLIC-IP>:9090

Lỗi có thể gặp

Sau khi install helm và app spring postgres,
sau đó thêm k8s dashboard thì bị lỗi POD mới tạo luôn ở trạng thái ContainerCreating

xem logs:

kubectl describe pod <pod_name> 

thì phát hiện lỗi network: add cmd: failed to assign an IP address to container

google 1 lúc thì thấy (https://github.com/aws/amazon-vpc-cni-k8s/issues/59),
nguyên nhân có thể do thiếu IP trong ENI

“After checking AWS documentation, it seems that there is an IP limit per instance: 18 for t3.medium, 36 for t3.large”

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html

Thank You!

Your comment has been submitted. It will appear on this page shortly! OK

Yikes, Sorry!

Error occured. Couldn't submit your comment. Please try again. Thank You! OK

Leave a comment