CloudFormation을 통해 VPC, EC2, EKS 등 인프라를 생성하고
EC2의 Shell Script를 통해 EKS 오브젝트(EKS Pod, Ingress)를 배포할 예정이다.
1. 사전 작업
IaC(Cloudformation) 기반으로 인프라를 생성한다.
Private EC2를 생성하여 EKS를 관리하고자 한다.
메인 작업을 위해 EC2에 필요한 솔루션 설치 및 IAM Role 설정하여 환경 구성을 한다.
모든 작업은 IaC 및 Script 기반으로 수행한다. (코드는 github 게시 예정)
1.1 EC2 생성
1.1.1 eksctl 설치
curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
mv /tmp/eksctl /usr/local/bin
eksctl version
> 0.200.0
1.1.2 kubectl 설치
https://docs.aws.amazon.com/eks/latest/userguide/install-kubectl.html
kubectl version --client
# Linux
# Kubernetes 1.31
curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.31.2/2024-11-15/bin/linux/amd64/kubectl
mkdir -p $HOME/bin
cp ./kubectl $HOME/bin/kubectl
export PATH=$HOME/bin:$PATH
1.1.3 helm 설치
# 1. 스크립트 다운로드
curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
# 2. 다운받은 스크립트에 실행 권한 부여
chmod 700 get_helm.sh
# 3. 스크립트 실행하여 Helm 설치
./get_helm.sh
# 4. 설치 확인
helm version
1.2 IAM Role 생성
Priavate EC2가 EKS Cluster 접근을 위한 IAM Role을 생성한다.
해당 IAM Role에 할당할 Policy 목록
- AmazonEKS_CNI_Policy
- AmazonEC2ContainerRegistryReadOnly
- AmazonEKSWorkerNodePolicy
- AmazonEC2RoleforSSM
AmazonEC2RoleforSSM은 Private EC2를 SSM을 통해 접근하기 위한 용도이다.
나머지 정책들은 Private EC2에서 EKS로의 접근을 위한 용도이다.
위의 화면처럼 IAM Role을 생성하면 된다.
1.3 Private EC2의 인스턴스 프로파일에 해당 IAM Role 할당
앞서 생성한 IAM Role을 Private EC2에 할당하고자 한다.
1.3.1 IAM 역할 수정
인스턴스 프로파일을 앞서 생성한 IAM Role로 연결한다.
앞으로 Private EC2는 해당 Role을 통해 EKS 접근하게 된다.
1.4 EKS Cluster의 IAM 액세스 항목에 해당 IAM Role 할당
EKS Cluster의 IAM 액세스 항목에서 앞서 생성한 EC2의 IAM Role을 할당해줘야 최종적으로 접근이 가능하다.
1.4.1 IAM 보안 주체에 해당 IAM Role 할당
1.4.2 액세스 정책 지정 - AmazonEKSAdminPolicy
단순 Pod 관리를 위해서는 해당 정책이 필요하다.
1.4.3 액세스 정책 지정 - AmazonEKSClusterAdminPolicy
Worker Node 관리를 위해서 해당 정책도 필요하다.
액세스 범위는 클러스터로 지정한다.
각각 정책 추가 버튼을 누르고 다음을 클릭한다.
1.5 SSM을 통해 Private EC2 접근
1.5.1 kubeconfig 생성
Private EC2를 SSM을 통해 접근하여 kubeconfig를 생성해야 한다.
EKS 클러스터가 생성되었다면 kubectl 명령어를 사용하여 쿠버네티스 클러스터에 접근하기 위해 config 파일을 생성해야 한다.
aws eks --region ap-northeast-2 update-kubeconfig --name [EKS cluster명]
이 이후, kubectl get pods -A 또는 kubectl get nodes 명령어 수행이 가능하다.
2. 메인 작업
Worker Node에 Label 추가 (Option)
# Node의 label 지정
kubectl label node <NODE_NAME> node-type=private
# Node의 label 확인
kubectl get node <NODE_NAME> --show-labels | grep private
# Node의 label 제거
kubectl label nodes <NODE_NAME> node-type=private
Label을 기반으로 Ingress 관련된 오브젝트만 Public Node에 두고 나머지 오브젝트들은 Private Node에 배포할 예정이다.
위의 명령어는 수동으로 Node에 Label을 지정하는 방식이다.
아래의 내용은 자동으로 Node에 Label을 할당하는 쉘 스크립트이다.
#!/bin/bash
# public nodegroup 노드에 라벨 추가
for node in $(kubectl get nodes -l 'eks.amazonaws.com/nodegroup=Practice2-public-nodegroup' -o name); do
echo "Labeling public node: $node"
kubectl label $node node-type=public --overwrite
done
# private nodegroup 노드에 라벨 추가
for node in $(kubectl get nodes -l 'eks.amazonaws.com/nodegroup=Practice2-private-nodegroup' -o name); do
echo "Labeling private node: $node"
kubectl label $node node-type=private --overwrite
done
# 결과 확인
echo -e "\nVerifying node labels:"
kubectl get nodes --show-labels | grep "node-type"
추가적으로 서비스별 Deployment에 nodeSelector / tolerations 설정을 해줘야 한다.
nodeSelector 만 있으면 안된다...tolerations와 함께 작성해야 원하는 노드 배치가 된다.
2.1 ArgoCD 배포
# 작업 디렉토리 이동
cd /k8s/argocd
# 설치 파일 다운로드
curl -O https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
2.1.1 설치 파일 수정 - ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
labels:
app.kubernetes.io/name: argocd-cmd-params-cm
app.kubernetes.io/part-of: argocd
name: argocd-cmd-params-cm
data:
server.insecure: "true"
설치 파일 중 ConfigMap의 data.server.insecure: "true" 설정을 추가하면 tempoarary redirect 에러 해결이 가능하다.
2.1.2 설치 파일 수정 - Deployment, Statefulset
모든 Deployment와 Statefulset에 아래의 nodeSelector, tolerations를 기입한다.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/component: applicationset-controller
app.kubernetes.io/name: argocd-applicationset-controller
app.kubernetes.io/part-of: argocd
name: argocd-applicationset-controller
spec:
selector:
matchLabels:
app.kubernetes.io/name: argocd-applicationset-controller
template:
metadata:
labels:
app.kubernetes.io/name: argocd-applicationset-controller
spec: # -------아래 코드 복사-------
nodeSelector:
node-type: "private"
tolerations:
- key: "node-type"
operator: "Equal"
value: "private"
effect: "NoSchedule"
# 배포
kubectl apply -n argocd -f install.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-ingress
namespace: argocd
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
alb.ingress.kubernetes.io/group.name: "cdscamp"
spec:
ingressClassName: alb
rules:
- host: argocd.biz.biz
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
배포 성공
초기 아이디: admin
초기 비밀번호는 아래의 명령어를 통해 확인 가능하다.
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d; echo
2.2 Sonarqube 배포
Sonarqube는 코드 정적 분석 도구이다.
필요한 오브젝트
- manifest
- Deployment (sonarqube-deployment.yaml)
- Service (sonarqube-service.yaml)
- Secret (sonarqube-secret.yaml)
- PVC (sonarqube-pvc.yaml)와 PV (sonarqube-pv.yaml)
- progreSQL DB
- Deployment (sonarqube-postgre-deployment.yaml)
- Service (sonarqube-postgre-service.yaml)
PostgreSQL 배포 순서
1. Namespace 생성
kubectl create namespace monitoring
2. PV (PersistentVolume) 구성
kubectl apply -f sonarqube-postgre-pv.yaml
3. PVC (PersistentVolumeClaim) 구성
kubectl apply -f sonarqube-postgre-pvc.yaml
4. Secret 구성
kubectl apply -f sonarqube-postgre-secret.yaml
5. PostgreSQL Service 구성
kubectl apply -f sonarqube-postgre-service.yaml
6. PostgreSQL Deployment 구성
kubectl apply -f sonarqube-postgre-deployment.yaml
Sonarqube 배포 순서
7. Sonarqube Service 구성
kubectl apply -f sonarqube-service.yaml
8. Sonarqube Deployment 구성
kubectl apply -f sonarqube-deployment.yaml
# Helm 저장소 추가
helm repo add sonarqube https://SonarSource.github.io/helm-chart-sonarqube
# Helm 저장소 업데이트
helm repo update
# 공식 Helm 리포지토리에서 sonarqube 차트의 values.yaml 다운로드
sudo touch values.yaml && sudo chmod 666 values.yaml
helm show values sonarqube/sonarqube > values.yaml
# 저장소 목록 확인
helm repo list
# Sonarqube chart 검색
helm search repo sonarqube
배포 방식은 value.yaml 파일을 받아서 수정하는 방식, yaml을 직접 작성하는 방식.. 다양하다.
나는 둘다 진행했지만 최종적으로 yaml을 직접 작성하는 방식으로 채택하였다.
2.2.1 value.yaml 파일 수정
controller.nodeSelector.eks.amazonaws.com/nodegroup: Practice2-private-nodegroup
2.2.1.1 namespace 지정
namespace: test
2.2.1.2 Port 지정 (Option)
Sonarqube는 기본 9000 포트지만 9010으로 변경하고자 한다.
9000 포트 -> 9010포트 변경
externalPort: 서비스가 외부에 노출하는 포트
internalPort: 컨테이너가 수신하는 포트
2.2.1.3 postgresql 확인
Sonarqube는 postgresql을 사용한다.
정상적으로 enabled: true 되어있는지 설정 확인한다.
2.2.1.4 PV 지정
2.2.1.5 Ingress / Ingress Controller 지정
ingress-nginx.enabled: true
controller.nodeSelector.eks.amazonaws.com/nodegroup: Practice2-public-nodegroup
ingress 관련 리소스는 public nodegroup에만 배포되도록 nodeSelector 설정한다.
기존 Ingress만 활용할 것이므로, httproute는 그대로 둔다.
httproute는 기존 Ingress보다 더 발전된 라우팅 규칙을 지원하는 아직 베타 단계의 기능이다.
ingress.enabled: true
ingress.hosts.name: 원하는 도메인 (ex. sonarqube.userid.biz.biz )
ingress.hosts.path: /
ingress.hosts.pathType: Prefix
ingress.annotations.kubernetes.io/ingress.class: nginx
ingress.annotations.nginx.ingress.kubernetes.io/ssl-redirect: "false"
ingress.ingressClassName: nginx
# helm 차트에서 받아온 파일과 내가 작성한 파일 병합(옵션 -f)
helm install sonarqube sonarqube/sonarqube -f values.yaml -n test
# 설치 확인
kubectl get pods -n test
kubectl get svc -n test
kubectl get ingress -n test
helm 릴리스 삭제
$ helm uninstall sonarqube -n test
배포 성공
2.2.2 직접 yaml 파일 작성
apiVersion: v1
kind: Namespace
metadata:
name: sonarqube
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: sonar-postgresql-pv
spec:
storageClassName: local-storage
accessModes:
- ReadWriteOnce
capacity:
storage: 5Gi
persistentVolumeReclaimPolicy: Retain
hostPath:
path: /k8s/sonarqube
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: sonarqube-postgresql-pvc
namespace: sonarqube
spec:
storageClassName: local-storage
volumeName: sonar-postgresql-pv
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
---
apiVersion: v1
kind: Secret
metadata:
name: postgres-pwd
namespace: sonarqube
type: kubernetes.io/basic-auth
stringData:
password: P@s\$w0rd
---
apiVersion: v1
kind: Service
metadata:
name: sonarqube-postgres-svc
namespace: sonarqube
spec:
selector:
app: sonarqube-postgres
ports:
- port: 5432
targetPort: 5432
protocol: TCP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarqube-postgres
namespace: sonarqube
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube-postgres
template:
metadata:
labels:
app: sonarqube-postgres
spec:
nodeSelector:
node-type: "private"
tolerations:
- key: "node-type"
operator: "Equal"
value: "private"
effect: "NoSchedule"
securityContext:
runAsUser: 0
fsGroup: 0
containers:
- image: postgres:14.5
name: sonarqube-postgres
resources:
requests:
cpu: "100m"
memory: "256Mi"
limits:
cpu: "300m"
memory: "512Mi"
env:
- name: POSTGRES_USER
value: sonar
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-pwd
key: password
ports:
- containerPort: 5432
name: postgresport
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
subPath: postgres-data
volumes:
- name: postgres-data
persistentVolumeClaim:
claimName: sonarqube-postgresql-pvc
---
apiVersion: v1
kind: Service
metadata:
name: sonarqube-svc
namespace: sonarqube
spec:
selector:
app: sonarqube
ports:
- name: http
port: 9000
protocol: TCP
targetPort: 9000
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sonarqube
namespace: sonarqube
spec:
replicas: 1
selector:
matchLabels:
app: sonarqube
template:
metadata:
labels:
app: sonarqube
spec:
nodeSelector:
node-type: "private"
tolerations:
- key: "node-type"
operator: "Equal"
value: "private"
effect: "NoSchedule"
initContainers:
- name: increase-the-vm-max-map-count
image: busybox:1.28
command:
- sysctl
- -w
- vm.max_map_count=262144
securityContext:
privileged: true
- name: increase-the-ulimit
image: busybox:1.28
command:
- sh
- -c
- ulimit -n 65536
securityContext:
privileged: true
containers:
- image: sonarqube:8.9.9-community
name: sonarqube
resources:
requests:
cpu: "300m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
env:
- name: SONARQUBE_JDBC_USERNAME
value: sonar
- name: SONARQUBE_JDBC_URL
value: jdbc:postgresql://sonarqube-postgres-svc:5432/sonar
- name: SONARQUBE_JDBC_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-pwd
key: password
ports:
- containerPort: 9000
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sonarqube-ingress
namespace: sonarqube
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
alb.ingress.kubernetes.io/group.name: "cdscamp"
spec:
ingressClassName: alb
rules:
- host: sonarqube.jerry.domain.domain
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sonarqube-svc
port:
number: 9000
배포 성공
최초 로그인 정보: admin / admin
2.3 Ingress 배포
2.3.1 정책 생성
# 정책 다운로드
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/v2.6.0/docs/install/iam_policy.json
# 정책 생성
aws iam create-policy --policy-name AWSLoadBalancerControllerIAMPolicy --policy-document file://iam_policy.json
해당 정책은 "elasticloadbalancing" 권한이 부족하다.
그래서 "elasticloadbalancing:*" 정책을 추가하였다.
2.3.2 IAM ODIC 제공자 연결
# EKS 클러스터에 IAM OIDC(OpenID Connect) 제공자 연결
eksctl utils associate-iam-oidc-provider --region=ap-northeast-2 --cluster=Practice2-eks-cluster --approve
IAM ODIC는 IAM Role과 EKS 클러스터의 ServiceAccount와 연결해주는 제공자 이다.
콘솔상 확인되지 않음
2.3.3 Service Account 생성 및 정책 할당
# sa 생성 및 정책 할당
eksctl create iamserviceaccount \
--cluster <EKS_CLUSTER_NAME> \
--namespace kube-system \
--name aws-load-balancer-controller \
--attach-policy-arn arn:aws:iam::<ACCOUNT_ID>:policy/AWSLoadBalancerControllerIAMPolicy \
--approve \
--region ap-northeast-2
2.3.4. Ingress Controller 생성
# helm 저장소 추가 및 업데이트
helm repo add eks https://aws.github.io/eks-charts
helm repo update
# helm chart 설치
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=<EKS_CLUSTER_NAME> \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set region=<REGION> \
--set vpcId=<VPC_ID>
# helm chart 제거
helm uninstall aws-load-balancer-controller -n kube-system
helm chart를 활용한 Ingress Controller를 생성한다.
2.3.5 Ingress 생성
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: sonarqube-ingress
namespace: sonarqube
annotations:
alb.ingress.kubernetes.io/scheme: internet-facing
alb.ingress.kubernetes.io/target-type: ip
alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'
alb.ingress.kubernetes.io/group.name: "cdscamp"
spec:
ingressClassName: alb
rules:
- host: sonarqube.userid.biz.biz
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: sonarqube-svc
port:
number: 9000
서비스별로 Ingress를 생성하고 namespace도 동일하게 구성하였다.
하나의 alb를 여러 서비스가 공유하며 배포를 위해 annotations에 group을 지정하였다.
리소스가 삭제되지 않는 문제
# ingress의 finalizer 삭제
kubectl patch ingress argocd-ingress -n argocd -p '{"metadata":{"finalizers":[]}}' --type=merge
finalizer가 삭제되지 않는 문제
리소스가 삭제되지 않는 주된 이유는 해당 리소스에 설정된 finalizer가 삭제 작업을 막고 있기 때문입니다. 예를 들어, 특정 리소스가 외부 시스템과 연동되어 있고, 이 연결을 종료하는 과정이 완료되지 않으면 리소스는 삭제되지 않습니다.
2.3.6 Route53 도메인 연결
배포 성공
2.4 GitLab Runner 배포
다음 글에 이어서 작성 예정..
'IaC' 카테고리의 다른 글
EKS 환경에서 오브젝트(GitLab-Runner) 배포 -2 (0) | 2025.01.03 |
---|---|
[Terraform] 배포 및 플래그 (1) | 2024.06.11 |
[Terraform] 디렉터리 구조 (0) | 2024.06.11 |
[Terraform] 용어 (0) | 2024.04.02 |
[Terraform] 환경 구성 (1) | 2024.04.02 |