StatefulSet 리소스로 프로젝트에 필요한 컴포넌트들을 설치하고, 테스트 해보는 것까지 기록하는 목적이다.
각 컴포넌트간에 ClusterIP 방식으로 통신하고자 설계했다.
default로 설정된 ClusterIP은 k8s cluster 내부에서만 접근이 가능한 서비스로 외부로 노출되지 않는 특징이 있다.
그래서 Pod끼리 해당 서비스의 DNS 이름이나 ClusterIP를 사용해서 주로 접근한다.
ConfigMap과 Secret 리소스를 활용해서 컴포넌트들을 결합한 서버를 배포하는게 이번 게시글의 목적이다.
우선 먼저, 항상 eks를 종료하기 전에 모든 리소스를 삭제(terraform destroy)했기에 다시 생성해줘야 한다.
1. *.tf 폴더 경로에서 terraform apply로 재생성
2. k8s에 접속하기 위한 config 설정
aws eks --region ap-northeast-2 update-kubeconfig --name <cluster>
3. k8s cluster에 load-balancer-controller 설정
Helm install command for clusters with IRSA
helm install aws-load-balancer-controller eks/aws-load-balancer-controller -n kube-system --set clusterName=<cluster-name> --set serviceAccount.create=false --set serviceAccount.name=aws-load-balancer-controller
4. 초기 설정 완료
argo의 Ingress도 실행해서 Address가 잘 뜨는걸 확인하면 끝
프로젝트의 Github Action Workflow를 새로 업데이트 했다.
자 이제 commit을 하면 helm 레포지토리에 dev 브랜치 생성, 빌드될때의 id와 동일하게 values-dev.yaml의 tag가 바뀐다.
ECR에 반영된 컨테이너 이미지의 tag와 yaml의 tag가 동일해야한다.
ECR의 이미지 tag와 동일하게 Helm 레포지토리의 tag가 업데이트 되는 모습.
이제 ArgoCD를 통해서 Image를 Manifest하기만 하면 성공이당
1. MySQL 컴포넌트 설치
Kubernetes 환경에서 MySQL를 구성하고 서비스에 연결하자!
1-1. StatefulSet 리소스를 만들기 위한 사전 작업
PersistantVolume와 Claim 파일을 생성해서 describe로 확인해줬다.
얘네는 Pod가 생성된 노드의 host local에 생성된다.
DB의 민감한 데이터를 Secret 리소스로 담아두자.
ubuntu:~/environment/k8s-resource $ echo -n 'password' | base64
Secret 리소스는 평문으로 저장하지 말도록 base64로 변환해서 작성해준다.
$vi mysql-secret.yaml
$kubectl create -f mysql-secret.yaml
$kubectl get secret mysql-secret
service와 deployment도 작성해서 다음 명령어로 확인해주자
ubuntu:~/environment/k8s-resource/mysql $ kubectl get pods -l app=mysql
NAME READY STATUS RESTARTS AGE
mysql-58f67b88d8-br48f 0/1 Pending 0 22s
pending인거 좀만 기다려보자. k9s에서보니 뭐.. 된거같다?
접속 테스트
kubectl exec -it <mysql> -- bash
# mysql -u root -p
다른 Pod에서 MySQL 접속하기
$ kubectl run -it --rm --image=<mysql> --restart=Never <pod name> -- mysql -h <IP> -p '<password>'
2. Redis 컴포넌트 설치
Kubernetes 환경에서 Redis(stand alone)를 구성해보자
사실 처음에는 statefulset 리소스가 아닌 Pod로 생성할려 했다.
그러나 식별성, 구성, 저장소가 중요한 애플리케이션은 statefulset 리소스로 생성해야 한다고 해서 바꿈
- 순차적, 정교한 롤링 업데이트가 안됨
- 복제 및 자동 복구 관리의 어려움
- 각 인스턴스별 식별하기 어려움
- Persistent 저장소를 할당할 수 없음 → 영속성,일관성 보장못함
근데.. 캐싱 용도로 stand-alone으로 쓸거라 굳이.? 라는 느낌
캐싱에 영속성이 무슨 상관이냐 싶기도 하고, 추후 고도화를 진행하면 그때 cluster로 만들어서 sharding이나 scaling을 고려하면 되지
근데 그냥 통일성 있게 statefulSet 리소스로 진행했다ㅋ
이유는 장애 발생시 비즈니스 로직까지 멈춰버리기에 self-healing이 없으면 곤란하기 때문이다.
ConfigMap과 Secret 리소스를 사용해서 하나의 redis.yaml에 작성하니 편하다.
---로 구분해서 apiVersion... 부터 적으니 파일 관리도 용이했음
수정/// 코드는 지웠습니다.
BusyBox를 이용해서 서버 작동을 확인하자
BusyBox는 1MiB 조금 넘는 정도로, 임베디드처럼 제한된 환경에서 쓰기 위해 옵션을 최소화한 녀석인데, 400 여 개의 Linux 커맨드라인 명령어를 모아놓은 단일 실행 파일이다.
BusyBox
25 March 2016 -- Building on an Android tablet. Android is based on Linux kernel, but sadly and unexplicably, Android userspace is not Unix-friendly: in many cases, things are done differently than in "usual" Unix systems. For example, there is no /bin dir
www.busybox.net
Pod로 생성해주자
apiVersion: v1
kind: Pod
metadata:
name: busybox
spec:
containers:
- name: busybox
image: busybox
command:
- sleep
- "3600"
imagePullPolicy: IfNotPresent
restartPolicy: Always
$ kubectl apply -f ncutils.yaml 설치
Cluster 내부에서 redis 접속 확인
$ kubectl exec -it busybox sh -- nc redis 6379
ping
busybox의 sh 명령어는 Bash가 아닌 경량 리눅스 셸로 알려진 Almquist 셸을 실행한다
네트워크 유틸리티인 netcat 명령어(nc)로 Redis에 요청을 보낸다.
PING 명령어를 실행하면 +PONG 으로 응답한다.
Redis 서버가 정상적으로 작동하고 있음을 나타낸다.
(해결) k8s Ingress에 해당하는주소로 접속시, 503 Service Temporarily Unavailable 오류를 출력
ArgoCD에 해당하는 namespace의 모든 Pod들이 Pending에 머무는 현상이 발생했다.
kubectl describe pod <argocd-server> -n argocd
명령어를 통해서 이벤트 로그를 확인해서 왜 스케줄링하지 못하는지 확인해봤다.
Warning FailedScheduling 3m10s (x3 over 13m) default-scheduler 0/2 nodes are available: 2 Too many pods. preemption: 0/2 nodes are available: 2 No preemption victims found for incoming pod.
해당 로그를 통해 노드가 충분한 리소스를 가지지 않기 때문으로 알 수 있다.
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.16"
cluster_name = local.name
cluster_version = "1.27"
cluster_endpoint_public_access = true
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
default_node_group = {
instance_types = ["t3.small"]
min_size = 1
max_size = 3
desired_size = 2
}
}
tags = local.tags
}
*.tf 파일에서 terraform으로 생성한 eks cluster 노드의 인스턴스 유형을 t2.micro로 지정 했었다.
웃기네ㅋㅋㅋ 왜 리소스 딸릴거를 생각못하고 유형을 줄여버렸지
이런 시행착오가 쌓일수록 클라우드 환경에 익숙해지고 손에 익어 실력으로 이어질 거라 믿는다
(해결) k8s 환경에서 mySQL 구동시 pvc를 찾을 수 없는 오류
Warning FailedScheduling 63s (x2 over 6m23s) default-scheduler 0/2 nodes are available: persist
entvolumeclaim "mysql-pv-claim" not found. preemption: 0/2 nodes are available: 2 Preemption is not helpful for scheduling..
안만들었으니까 못찾지.
pvc 리소스를 생성하는걸 까먹어서 생겼던 오류.
금방 해결했다.
(해결) k8s 환경에서 mySQL 구동시 FailedScheduling 오류
Warning FailedScheduling 34s default-scheduler running PreFilter plugin "VolumeBinding":
error getting PVC "default/mysql-pv-claim": could not find v1.PersistentVolumeClaim "default/mysql-pv-claim"
Warning Failed 11s (x2 over 12s) kubelet Error: failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: error during container init: error mounting "/run/containerd/io.containerd.runtime.v2.task/k8s.io/mysql/db/initdb.d" to rootfs at "/docker-entrypoint-initdb.d": stat /run/containerd/io.containerd.runtime.v2.task/k8s.io/mysql/db/initdb.d: no such file or directory: unknown
보통 k8s 환경에서 오류 찾는 과정에 대해서 느낀 바가 있다.
k9s - Pod 로 시각화했을때 적색에 가까울 수록 장애 상황에 있는데, 해당 파드의 이름을 복사하거나 $ kubectl get po -A
로 확인할 수 있다.
$ kubectl describe <pod> -n <namespace>
(ns없으면 default) 를 통해 해당 Pod의 로그를 확인할 수 있고, 여기서 대부분의 오류를 읽어 해결할 수 있었다.
위의 오류들은 해당 노드의 디렉토리 경로가 잘못된 경우이다.
출처 : https://kubernetes.io/ko/docs/tasks/run-application/run-single-instance-stateful-application/
'kubernetes' 카테고리의 다른 글
ArgoCD를 이용한 무중단 배포하기(Canary 방식, argo-rollout) (0) | 2024.11.19 |
---|---|
Spring Boot Actuator를 이용한 Kubernetes Pod의 Health check (0) | 2024.11.19 |
kubernetes Pod의 Graceful Shutdown (feat. Kafka) (0) | 2024.11.19 |
Service Mesh - Kubernetes가 있는데 왜 Istio가 필요한가요? (0) | 2024.11.19 |
kubernetes 환경에서 Apache Kafka cluster를 구성해보자 (Operator 패턴) (0) | 2024.11.19 |