ํด๋ผ์ฐ๋ํ์
์จ๋น๋ #03
ํด๋ผ์ฐ๋ํ์
, AWS Elastic Kubernetes Service๋ฅผ ํ์ฉํ
๊ฐ๋ฐ์ ํ๋ซํผ ๊ตฌ์ถํ๊ธฐ
- ๐๏ธ ์ค์ต ์์ ์ฌ์
- ๐๏ธ ์ค๋น์ฌํญ
- ๐งฐ ํ์ ๋๊ตฌ ์ค์น
- ๐ ๏ธ EKS ํด๋ฌ์คํฐ ์์ฑํ๊ธฐ
- โ๏ธ EKS ํด๋ฌ์คํฐ ์ธํ ํ๊ธฐ
- โ๏ธ ํด๋ผ์ฐ๋ํ์ ์ฐ๋ํ๊ธฐ
- ๐ References
- ๐ฌ Contact
- Kubernetes(AWS EKS)
- Engine: v1.25
- Node: Amazon Linux 2, t3.medium x 2
- Helm: v3.12.1
- Calico: v3.25.1
- Cert Manager: v1.12.0
- Nginx Ingress Controller: v1.8.1
- ํด๋ผ์ฐ๋ํ์ ๊ณ์
- AWS ๊ณ์
- ๋ฃจํธ ๊ณ์ ์ด ์๋ IAM ์ฌ์ฉ์ ์์ฑ ๊ณ์ ์ฌ์ฉ
- ์ก์ธ์คํค ๋ฐ๊ธ
- Cloudflare ๊ณ์
- ๋๋ฉ์ธ ์ฌ์ ๊ตฌ๋งค ํ์
- GitHub ๊ณ์
-
AWS CLI ์ค์น
$ curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg" $ sudo installer -pkg AWSCLIV2.pkg -target /
-
AWS ๊ณ์ ์ค์
$ aws configure
$ curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.25.9/2023-05-11/bin/darwin/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' >> ~/.zshrc
$ source ~/.zshrc-
macOS
$ brew tap weaveworks/tap $ brew install weaveworks/tap/eksctl $ eksctl version
-
Unix
$ ARCH=amd64 # Apple Silicon(M1/M2) ๋ฑ ARM ์ํคํ ์ฒ์ธ ๊ฒฝ์ฐ `ARCH=arm64` ์ ๋ ฅ $ PLATFORM=$(uname -s)_$ARCH $ curl -sLO "https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_$PLATFORM.tar.gz" $ tar -xzf eksctl_$PLATFORM.tar.gz -C /tmp && rm eksctl_$PLATFORM.tar.gz $ sudo mv /tmp/eksctl /usr/local/bin
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
$ chmod 700 get_helm.sh
$ ./get_helm.sh$ eksctl create cluster \
--name=cloudtype-test \
--region=ap-northeast-2 \
--without-nodegroup- EC2 > Network & Security > Key Pairs ๋ฉ๋ด๋ก ์ง์
- Create key pair ๋ฒํผ์ ๋๋ฅด๊ณ ๋ค์์ ํญ๋ชฉ์ ํ์ธ ํ ํค๋ฅผ ์์ฑ
- Name: ํคํ์ด๋ช
- Key pair type: RSA
- Private key file format: .pem
- ํคํ์ด ์์ฑ์ด ์ ์์ ์ผ๋ก ์๋ฃ๋๋ฉด
.pemํ์ฅ์์ ํ์ผ์ด ๋ค์ด๋ก๋ ๋๋ฉฐ, ๋ณด์์ ์ ์ํ์ฌ ์์ ํ ์์น์ ํ์ผ ๋ณด๊ด
$ eksctl create nodegroup \
--cluster=cloudtype-test \
--region=ap-northeast-2 \
--name=ng-eks-01 \
--node-type=t3.medium \
--nodes=2 \
--nodes-min=2 \
--nodes-max=4 \
--node-volume-size=20 \
--ssh-access \
--ssh-public-key=[ํคํ์ผ๋ช
] \
--managed \
--asg-access \
--external-dns-access \
--full-ecr-access \
--appmesh-access \
--alb-ingress-access - ECR ๊ด๋ จ ํ์ ๊ถํ ํ์ธ ํ์
- ์ญํ ๋ช
- eksctl-[ํด๋ฌ์คํฐ๋ช ]-nodegroup-n-NodeInstanceRole-xxxxxxxxxx
- ๊ถํ
- AmazonEC2ContainerRegistryReadOnly
- AmazonEC2ContainerRegistryPowerUser
- ์ญํ ๋ช
-
Calico ์ค์น
$ helm repo add projectcalico https://docs.tigera.io/calico/charts $ helm repo update $ helm install calico projectcalico/tigera-operator \ --version v3.25.1 \ --set installation.kubernetesProvider=EKS \ --namespace tigera-operator \ --create-namespace $ helm -n tigera-operator get values calico -
Network Policy Engine add-on ์ ์ฉ
$ kubectl patch clusterrole aws-node \ --type='json' \ -p='[{"op": "add", "path": "/rules/-1", "value":{ "apiGroups": [""], "resources": ["pods"], "verbs": ["patch"]}}]' \ -o yaml $ kubectl set env daemonset aws-node -n kube-system ANNOTATE_POD_IP=true $ kubectl get po -n calico-system | grep calico-kube-controllers- # pod ์ด๋ฆ์ ๋์ ํํ๋ก ํ ๋น๋์ด ๊ฐ๋ณ์ ์ผ๋ก ํ์ธ ํ์ $ kubectl get pods -n calico-system --no-headers=true \ | awk '/calico-kube-controllers-/{print $1}' \ | xargs kubectl delete -n calico-system pod $ kubectl get po -n calico-system | grep calico-kube-controllers- # ์ญ์ ํ ์ฌ์์ฑ๋ pod ์ ์ ์ํ ํ์ธ
$ helm repo add jetstack https://charts.jetstack.io
$ helm repo update
$ helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.12.0 \
--set installCRDs=true$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.1/deploy/static/provider/aws/deploy.yaml-
Cloudflare API KEY ๋ฐ๊ธ
- Cloudflare ๋ด ํ๋กํ > API ํ ํฐ ํ์ด์ง ์ด๋
- ํ ํฐ ์์ฑ ๋ฒํผ ํด๋ฆญ
- ์์ญ DNS ํธ์ง ํ ํ๋ฆฟ ์ฌ์ฉ ๋ฒํผ ํด๋ฆญ
- ๋ค์ ์ด๋ฏธ์ง์ ๊ฐ์ด ์ธํ ํ, ์์ฝ ๊ณ์ ๋ฒํผ ํด๋ฆญ(์์ญ ๋ฆฌ์์ค ํญ๋ชฉ์ ์ฌ์ฉํ ๋๋ฉ์ธ ์ ํ)
- ํด๋ผ์ฐ๋ํ์ ๊ณผ ์ฐ๋ํ ๋๋ฉ์ธ ํ์ธ ํ, ํ ํฐ ์์ฑ ๋ฒํผ ํด๋ฆญ
-
ingress-nginx-controllerLoadBalancer ์ธ๋ถ IP CNAME ๋ ์ฝ๋ ๋ฑ๋ก-
ingress-nginx-controllerLoadBalancer ์ธ๋ถ IP(Hostname) ํ์ธ$ kubectl get svc \ -n ingress-nginx \ ingress-nginx-controller \ -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' # EKS์ ๊ฒฝ์ฐ LoadBalancer์ ์ธ๋ถ IP๋ฅผ URL ํ์์ผ๋ก ํ ๋น -
Cloudflare์์ ์ฐ๋ํ ๋๋ฉ์ธ์ ๋์๋ณด๋์์, DNS > ๋ ์ฝ๋ ํ์ด์ง ์ด๋
-
๋ ์ฝ๋ ์ถ๊ฐ ๋ฒํผ ํด๋ฆญ ํ, ๋ค์ ๋ ๊ฐ์ ๋ ์ฝ๋ ์ถ๊ฐ
- ์ ํ: CNAME, ์ด๋ฆ: *, IPv4 ์ฃผ์: ์์์ ์กฐํํ LoadBalancer ์ธ๋ถ IP
- ์ ํ: CNAME, ์ด๋ฆ: ํ์ฌ ๋๋ฉ์ธ(example.com์ธ ๊ฒฝ์ฐ example.com), IPv4 ์ฃผ์: ์์์ ์กฐํํ LoadBalancer ์ธ๋ถ IP
-
-
Cloudflare API KEY ํ๊ฒฝ๋ณ์ ๋ฑ๋ก
$ export CLOUDFLARE_ACME_EMAIL=<Cloudflare ๊ณ์ ID> $ export CLOUDFLARE_API_TOKEN=<Cloudflare API KEY>
-
Cluster Issuer / Certificate ์์ฑ
$ cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Namespace metadata: name: cloudtype --- apiVersion: v1 kind: Secret metadata: name: cloudflare-api-token-secret namespace: cert-manager type: Opaque stringData: api-token: "${CLOUDFLARE_API_TOKEN}" --- apiVersion: cert-manager.io/v1 kind: ClusterIssuer metadata: name: cloudtype-crt namespace: cert-manager spec: acme: email: "${CLOUDFLARE_ACME_EMAIL}" server: https://acme-v02.api.letsencrypt.org/directory privateKeySecretRef: name: cloudtype-crt solvers: - http01: ingress: class: nginx - dns01: cloudflare: email: "${CLOUDFLARE_ACME_EMAIL}" apiTokenSecretRef: name: cloudflare-api-token-secret key: api-token selector: dnsZones: - [Cloudflare์ ๋ฑ๋ก๋ ๋๋ฉ์ธ] --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: cloudtype-tls namespace: cloudtype spec: dnsNames: - "[Cloudflare์ ๋ฑ๋ก๋ ๋๋ฉ์ธ]" - "*.[Cloudflare์ ๋ฑ๋ก๋ ๋๋ฉ์ธ]" issuerRef: kind: ClusterIssuer name: cloudtype-crt secretName: cloudtype-tls EOF
-
Cert Manager Order ์ํ ํ์ธ
$ kubectl get order -n cloudtype \ | awk '/cloudtype-tls-/{print $1}' \ | xargs kubectl get order -n cloudtype
- ์ ์์ ์ผ๋ก TLS ์ธ์ฆ์๋ฅผ ๋ฐ๊ธํ ์ ์๋ ์ํ์ธ์ง ํ์ธ ํ์
- Order์ STATE ํญ๋ชฉ ๊ฐ์ด valid์ฌ์ผ ํด๋ผ์ฐ๋ํ์ ์์ ๋ฐฐํฌํ ์๋น์ค์ ๋ํ์ฌ HTTPS ์ธ์ฆ์ด ์ ์์ ์ผ๋ก ์งํ
- Cloudflare DNS์ ๋๋ฉ์ธ์ ์ธ์ฆ์๊ฐ ๋ฐ๊ธ๋๊ธฐ ์ํ ์ํ๊ฐ ๋๊ธฐ๊น์ง ์ฝ 30๋ถ~1์๊ฐ ์์
- ์ ์์ ์ผ๋ก TLS ์ธ์ฆ์๋ฅผ ๋ฐ๊ธํ ์ ์๋ ์ํ์ธ์ง ํ์ธ ํ์
-
ํด๋ฌ์คํฐ IAM OIDC ์ ๊ณต์ ์ฒด ์์ฑ
$ export CLUSTER_NAME=cloudtype-test $ OIDC_ID=$(aws eks describe-cluster --name $CLUSTER_NAME --query "cluster.identity.oidc.issuer" --output text | cut -d '/' -f 5) $ aws iam list-open-id-connect-providers | grep $OIDC_ID | cut -d "/" -f4 $ eksctl utils associate-iam-oidc-provider --cluster $CLUSTER_NAME --approve
-
EBS CSI ๋๋ผ์ด๋ฒ IAM ์ญํ ์์ฑ
$ eksctl create iamserviceaccount \ --name ebs-csi-controller-sa \ --namespace kube-system \ --cluster ${CLUSTER_NAME} \ --role-name AmazonEKS_EBS_CSI_DriverRole \ --role-only \ --attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \ --approve -
EKS add-on EBS CSI ๋๋ผ์ด๋ฒ ์ ์ฉ
$ export ACCOUNT_ID=$(aws sts get-caller-identity --query "Account" --output text | xargs -L 1) $ eksctl create addon \ --name aws-ebs-csi-driver \ --cluster ${CLUSTER_NAME} \ --service-account-role-arn arn:aws:iam::${ACCOUNT_ID}:role/AmazonEKS_EBS_CSI_DriverRole \ --force
-
๊ธฐ์กด Storage Class dafault ํด์
$ kubectl patch storageclass gp2 -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"false"}}}' -
์ ๊ท Storage Class dafault ์ค์
$ cat <<EOF | kubectl apply -f - apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: gp3 annotations: storageclass.kubernetes.io/is-default-class: "true" allowVolumeExpansion: true provisioner: ebs.csi.aws.com volumeBindingMode: Immediate parameters: type: gp3 allowAutoIOPSPerGBIncrease: 'true' encrypted: 'true' EOF
-
PVC ์์ฑ ํ ์คํธ
$ cat <<EOF | kubectl apply -f - apiVersion: v1 kind: PersistentVolumeClaim metadata: name: pvc-test spec: accessModes: - ReadWriteOnce storageClassName: gp3 resources: requests: storage: 1Gi EOF
-
PVC ์ํ ํ์ธ
$ kubectl get pvc pvc-test
- PVC์ STATUS๊ฐ Bound์ธ์ง ํ์ธ
-
์์ด์ ํธ ์ค์น
$ kubectl apply -f https://raw.githubusercontent.com/cloudtype/agent/master/k8s/v1.0.0/agent.yaml
-
EKS ํด๋ฌ์คํฐ ์์ด์ ํธ ์ ์ ์ฃผ์ ํ์ธ
$ kubectl get svc \ -n cloudtype \ agent \ -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' \ | xargs -I{} echo "https://{}" -
์์ด์ ํธ ํ ํฐ ๊ฐ ์กฐํ
$ kubectl get secrets agent-secret -n cloudtype -o jsonpath='{.data.agent-token}' | base64 --decode
-
ํด๋ผ์ฐ๋ํ์ ์์ ํด๋ฌ์คํฐ ์ฐ๊ฒฐ
-
๋ ์ง์คํธ๋ฆฌ ์ ๋ณด ํ์ธ
- AWS ECR ์ฝ์์์ Create repository ๋ฒํผ ๋๋ฅธ ํ Repository name ํญ๋ชฉ์์ ECR ์๋ฒ ์ฃผ์ ํ์ธ
-
๋ ์ง์คํธ๋ฆฌ ์ค์ ์ฐฝ ์ง์
- ์ข์ธก ๋ค๋น๋ฐ์์ ํด๋ฌ์คํฐ ์ ํ ํ ์ค์ ํญ์ผ๋ก ์ด๋ํ์ฌ ์ปจํ ์ด๋ ์ ์ฅ์ ์ฐ๊ฒฐํ๊ธฐ ๋ฒํผ ํด๋ฆญ
-
๋ ์ง์คํธ๋ฆฌ ์ ๋ณด ์ค์
- ์ ์ฅ์ ์ ๊ณต์: AWS Elastic Container Registry
- ์ ์ ์ฃผ์: 1๋ฒ ๊ณผ์ ์์ ํ์ธํ ECR ์ ์ ์ฃผ์
- AWS Access Key Id: ECR ๊ด๋ จ ๊ถํ์ด ๋ถ์ฌ๋ ์ฌ์ฉ์์ Access Key
- AWS Secret Access Key: ์๋
- Region: ECR ์ด์ฉ ๋์ ๋ฆฌ์
- ์ธ์ฆ์ ์ํฌ๋ฆฟ ์ด๋ฆ
- cloudtype-tls
- ์ธ์ฆ์ ์ํฌ๋ฆฟ ๋ค์์คํ์ด์ค
- cloudtype
- ์ธ์ฆ์ ๋ฐ๊ธ๊ธฐ
- cloudtype-crt
- ๊ธฐ๋ณธ ๋๋ฉ์ธ
- ์ด์ ๋จ๊ณ์์ Nginx Ingress Controller ์ธํ ์ ์ฌ์ฉํ๋ Cloudflare ๋๋ฉ์ธ
- ์ธ๊ทธ๋ ์ค ์ ๋ณด
- ์ธ๊ทธ๋ ์ค ํด๋์ค: nginx
- ์ธ๊ทธ๋ ์ค IP: Nginx Ingress Controller LoadBalancer ์ธ๋ถ IP/Hostname
- ๋ก๋๋ฐธ๋ฐ์ ์ ํ
- LoadBalancer
- ์คํ ๋ฆฌ์ง ํด๋์ค
- gp3
- ์ ๋ ฅํ์ง ์์ผ๋ฉด ํด๋ฌ์คํฐ์ default ์คํ ๋ฆฌ์ง ํด๋์ค ์ฌ์ฉ
- ๋ณผ๋ฅจ ๋ชจ๋
- RWO









