레이블이 kubectl인 게시물을 표시합니다. 모든 게시물 표시
레이블이 kubectl인 게시물을 표시합니다. 모든 게시물 표시

k8s service account secret

# pod(앱)가 k8s api 로 인증할때 service account(sa) 를 사용하는데
# 모든 pod 가 디폴트로 사용하는 sa default 외 별도로 앱용 sa 가 있다.
kubectl get sa
NAME      SECRETS   AGE
default   0         16h
ysoftman1 0         16h

# ysoftman1 pod 에 보면 spec > template > spec > ServiceAccountName: ysoftman1 을 사용하고 있다.
# 그런데 secrects 0 이라 확인해보면 not found 가 발생한다.
kubectl describe secret
Error from server (NotFound): secrets "ysoftman1" not found

# sa 를 새로 만들어 봐도 nout found 가 발생한다.
kubectl create serviceaccount ysoftman
kubectl describe secret ysoftman
Error from server (NotFound): secrets "ysoftman" not found

# 찾아보니 1.24 부터(현재 1.26 사용하고 있음) sa 생성시 secret 를 자동 생성해주지 않도록 변경됐다고 한다. 

# token 타입의 secret 를 생성해서 ysoftman sa 에서 사용
cat << zzz | kubectl apply -f -
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
  name: ysoftman
  annotations:
    kubernetes.io/service-account.name: "ysoftman"
zzz

# 이제 ysoftman sa 의 secret 이 설정된 것을 볼 수 있다.
kubectl describe secret ysoftman

# 참고로 새로운 토큰 값만 필요한 경우 다음 명령으로 얻을 수 있다.
kubectl create token ysoftman --duration=999999h

vector certificate verify failed

# k8s 내부 인증서(apiserver,apiserver-etc-client...)를 업데이트 했다.

# vector daemonset 재시작했는데 vector pod error log 가 다음과 같이 발생한다.
# 에러 로그가 많아서 dump 로도 확인이 된다. 
kubectl cluster-info dump | rg -i error

2024-05-17T02:38:08.766363Z  WARN vector::kubernetes::reflector: Watcher Stream received an error. Retrying. error=InitialListFailed(HyperError(hyper::Error(Connect, ConnectError { error: Error { code: ErrorCode(1), cause: Some(Ssl(ErrorStack([Error { code: 167772294, library: "SSL routines", function: "(unknown function)", reason: "certificate verify failed", file: "ssl/statem/statem_clnt.c", line: 2092 }]))) }, verify_result: X509VerifyResult { code: 26, error: "unsuitable certificate purpose" } })))
2024-05-17T02:38:35.158930Z ERROR kube_client::client::builder: failed with error error trying to connect: error:0A000086:SSL routines:(unknown function):certificate verify failed:ssl/statem/statem_clnt.c:2092:: unsuitable certificate purpose

# vector 는 kubernetes_logs 를 소스로 하고 있고
# k8s 접근하기 위해서 인증과정을 거치게 되는것 같다.
# kube_config_file 로 kube config 파일을 명시하는 옵션이 있는데 사용하지 않아
# 디폴트로 in-cluster configuration 로 설정된다.

# vector 에서 사용하는 kube client rust 소스(go 소스도 같다.)에 다음과 같은 경로의 인증서를 참고 하는것 같다.
// Mounted credential files
const SERVICE_TOKENFILE: &str = "/var/run/secrets/kubernetes.io/serviceaccount/token";
const SERVICE_CERTFILE: &str = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt";

# vector pod 에도 다음과 같이 mount 설정이 있다.
spec:
  containers:
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-l82p5
      readOnly: true
  volumes:
  - name: kube-api-access-l82p5
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace

# kube-root-ca.crt 는 모든 namespace 의 configmap 에 등록되어 있다.

use rqlite

# rqlite(분산 sqlite database)
https://rqlite.io/docs/cli/

# k8s 로 운영중인 경우 local로 port-forwarding
kubectl port-forward service/rqlite 4001:4001 -n rqlite

# 접속
rqlite -H localhost -p 4001

# usage hints
.help

# 분산 노드 확인
.nodes

# 테이블 리스트 
.tables

# 테이블 조회
# 쿼리는 일반적인 sql 사용하면 된다.
select * from 테이블;

# sql 문으로 dump
.dump ysoftman.dump

# db 백업(sqlite format 3)
.backup ysoftman.db

# exit
.exit or .quit

# mysql 쿼리문에서 사용하려면 
# 필드명을 " 대신 `로 감싸야 한다.
# primary 에 있는 AUTOINCREMENT 는 제거하고 fileld 선언시 auto_increment 로 선언해야 한다.
# cat ysoftman.dump | sed -e "s/\"/\`/g" -e "s/AUTOINCREMENT//g" | pbcopy
# 이렇게 일일히 변경하는것 보다 툴을 사용하면 편하다.
brew tap techouse/sqlite3-to-mysql
brew install sqlite3-to-mysql
sqlite3mysql --help

# sqlite3 db 파일을 mysql 특정 db 에 import
sqlite3mysql -f ysoftman.db -h localhost -P 3306 -u root -d ysoftmantest --mysql-password aaa111

argocd image-updater and notification

# argocd-image-updater 설치
# 이미지가 업데이트되면 해당 이미지를 사용하는 argocd app 에 반영할 수 있다.
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj-labs/argocd-image-updater/stable/manifests/install.yaml

# image 확인 간격은 기본 2m 인데 변경하려면 deployment 에 아래처럼 --interval 옵션으로 변경할 수 있다.
spec:
  template:
    spec:
      containers:
      - command:
        - /usr/local/bin/argocd-image-updater
        - run
        - --interval
        - 30s

# docker.io quay.io, ghcr.io 등의 image registry 외
# 커스텀 image 저장소인 경우 configmap 에 추가
data:
  log.level: debug
  registries.conf: |
    registries:
    - name: ysoftman_images
      api_url: https://ysoftman.image.io
      prefix: ysoftman.image.io

# argocd-image-updater 재시작
kubectl -n argocd rollout restart deployment argocd-image-updater

# k8s > argocd-image-updater pod 에 ssh 접속해서 다음 명령으로 작동여부를 확인할 수 있다.
argocd-image-updater test <이미지명>

# argocd app 에 annotation 추가
# latest: 최근 이미지 태그가 생성된것으로 업데이트
# digest: 이미 이미지 태그가 있는 상태에서 태그의 이미지가 변경되면 업데이트(dev, stage, prod, latest 태그 처럼 계속 이미지가 변경되는 경우)
# helm 설정에서 image.tag 가 아닌 ysoftmanApp.image.tag 필드를 사용중인 경우
kubectl annotate app <argocd 앱이름> -n argocd \
argocd-image-updater.argoproj.io/image-list="myapp=<이미지경로:태그>" \
argocd-image-updater.argoproj.io/myapp.update-strategy=digest \
argocd-image-updater.argoproj.io/myapp.helm.image-tag=ysoftmnaApp.image.tag

# 이제 태그 이미지가 변경되면 변경된 이미지 layer(digest)를 받고
# deployment > image > 이미지경로:태그@sha256:xxxxx 로 변경돼 pod 가 재시작된다.
 
#####

# argocd-notification 로 argocd 상태를 slack, github, webhook 등으로 노티를 보내 보자.
# argocd-notification 은 argocd 설치시 기본으로 포함되어 있다.

# 우선 현재 설정된 configmap 을 가져오자.
kubectl get cm argocd-notifications-cm -o yaml -n argocd > argocd-notification-cm.yaml

# app sync 성공시 특정 URL 로 노티 보내기 위해 다음을 내용을 추가한다.
# 참고 https://argocd-notifications.readthedocs.io/en/stable/services/webhook/ 가 문서가 있는데 데 subscriptions.recipients 부분 설명이 없어 아래 예시에 추가했다.
data:
  subscriptions: |
    - recipients:
      - ysoftman

  # x-www-form-urlencoded 인 경우
  service.webhook.ysoftman: |
    url: https://ysoftman.test.com
    headers:
    - name: Content-Type
      value: application/x-www-form-urlencoded
  template.app-sync-succeeded: |
    webhook:
      ysoftman:
        method: POST
        body: key1=value1&key2=value2

  # json 인 경우
  service.webhook.ysoftman: |
    url: https://ysoftman.test.com
    headers:
    - name: Content-Type
      value: application/json
  template.app-sync-succeeded: |
    webhook:
      ysoftman:
        method: POST
        body: |
          {
            "key1":123,
            "key2":"aaa"
          }

# (kubectl.kubernetes.io/last-applied-configuration 는 삭제후) 적용
kubectl apply -f ./argocd-notification-cm.yaml

# 노티를 사용할 application 에 다음 annotations 를 추가한다.
# 또는 arogcd ui > app > summary > edit > notification subscriptions 
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  annotations:
    notifications.argoproj.io/subscribe.on-deployed.ysoftman: ""
    notifications.argoproj.io/subscribe.on-sync-succeeded.ysoftman: ""
    notifications.argoproj.io/subscribe.on-sync-failed.ysoftman: ""

kubectl top node error

# 설치
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# node 를 조회하면 다음 에러가 발생한다.(kubectl top pods 는 정상동작)
kubectl top node
error: metrics not available yet

# kube-system > metrics-server deployment 가 제대로 동작하고 있지 않았다.
kubectl get deployment metrics-server -n kube-system
NAME             READY   UP-TO-DATE   AVAILABLE   AGE
metrics-server   0/1     1            0           26h

# 이슈1 - 이미지 다운로드 실패
# 원인은 registry.k8s.io 에 대해서 방화벽 설정으로 이미지를 받아오지 못해서였다.
Pulling image "registry.k8s.io/metrics-server/metrics-server:v0.6.3"

# 우선 로컬에 이미지를 다운로드 받자.
docker pull registry.k8s.io/metrics-server/metrics-server:v0.6.3

# 이미지를 tar 로 만든다.
docker save -o metrics-server-v0.6.3.tar registry.k8s.io/metrics-server/metrics-server:v0.6.3

# 워커 노드에 tar 파일 전송
rsync -avz ./metrics-server-v0.6.3.tar ysoftman@worker1/home/ysoftman/
rsync -avz ./metrics-server-v0.6.3.tar ysoftman@worker2/home/ysoftman/
rsync -avz ./metrics-server-v0.6.3.tar ysoftman@worker3/home/ysoftman/

# 노드의 tar 이미지 파일을 로딩한다.
ssh ysoftman@worker1 "sudo docker load -i /home/ysoftman/metrics-server-v0.6.3.tar"
ssh ysoftman@worker2 "sudo docker load -i /home/ysoftman/metrics-server-v0.6.3.tar"
ssh ysoftman@worker3 "sudo docker load -i /home/ysoftman/metrics-server-v0.6.3.tar"

# metrics-server deployment 명세에 다음이 설정되어 있기 때문에
# imagePullPolicy: IfNotPresent
# pod 가 running 된다.

# 이슈2 - tls 비활성화
# metrics 로그를 보면 아래와같이 노드(kubelet)의 메트릭 수집시 실패한다고 나온다.
scraper.go:140] "Failed to scrape node" err="Get \"https://10.10.10.100:10250/metrics/resource\": read tcp 10.10.1.10:40752->10.10.10.100:10250: read: connection reset by peer" node="ysoftman-cpu1"

# metrics-server deployment> containers args 에 다음을 추가하면 위 에러는 발행하지 않고 정상 동작(scraping node ... scrap finished)한다.
- --secure-port=4443
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --kubelet-use-node-status-port
- --metric-resolution=15s
- --kubelet-insecure-tls  # 추가옵션
- --v=6 # 추가옵션 (Scraping node "node=xxxx" 노드 수집 성공 메시지 확인용)

# 이슈3 - apiservice 적용 안되는 이슈
# 아직도 error:metrics not available yet 이 발생한다.
# 찾아보니 kubectl 에서 top 커맨드 사용시 발생하는 에러 메시지였다.

# k8s api 로 node 메트릭을 다음과 같이 실행하면 결과가 나온다.
NODE_NAME="ysoftman-cpu1"
kubectl get --raw /api/v1/nodes/$NODE_NAME/proxy/metrics/resource | grep -i -E "node_cpu|node_mem"

# metrics api 에서 pods 응답은 나온다.
kubectl get --raw /apis/metrics.k8s.io/v1beta1/pods | jq 

# 하지만 nodes 응답을 보면 items: [] 로 빈값으로 나온다.
kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | jq 

# metrics-server pod log 는 다음과 같이 노드로부터 정보를 받아 저장했다고 나온다.
round_trippers.go:553] GET https://10.10.10.10:10250/metrics/resource 200 OK in 2 millisecond
server.go:139] "Storing metrics"
server.go:144] "Scraping cycle complete"

# metrics-server 를 삭제했는데, k top nodes/pods 에러가 발행하지 않는다.
kubectl delete -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/downl
oad/components.yaml

# top 명령을 받고 있는 별도의 pod 가 있는것으로 의심된다.
# v1beta1.metrics.k8s.io 를 사용하는 api 서비를 조회하면�
kubectl get apiservices | grep -i v1beta1.metrics.k8s.io

# 다음과 같이 prometheus-adapter 서비스가 나온다.
v1beta1.metrics.k8s.io     prometheus-adapter/prometheus-adapter

# kubectl top nodes 동작하는 클러스터는 다음과 같다.
v1beta1.metrics.k8s.io     kube-system/metrics-server

# metrics-server (kind:APIService) 부분이 반영이 안되는게 문제였다.
# prometheus-adapter > v1beta1.metrics.k8s.io 를 사용하고 있고, 이를 argo-cd 에서 항상 sync 하고 있어 삭제해도 다시 생성된다.

k8s PersistentVolume 값 변경

# k8s PersistentVolume(pv) > nfs > ip 를 변경하고자 한다.
# patch 로 변경하면 다음과 같이 생성 후에는 변경할 수 없다고 나온다.
kubectl patch pv ysoftmanPV -p '{"spec":{"nfs":{"server":"10.10.10.10"}}}'
Forbidden: spec.persistentvolumesource is immutable after creation

# 참고로 pvc 용량 패치는 되는데, 용량을 줄이면 안된다.
kubectl patch pvc prometheus-1 -p '{"spec":{"resources":{"requests":{"storage":"10Gi"}}}}' -n monitoring
... spec.resources.requests.storage: Forbidden: field can not be less than previous value

# Available 아직 클레임에 바인딩되지 않은 사용할 수 있는 리소스
# Bound 볼륨이 클레임에 바인딩됨
# Released 클레임이 삭제되었지만 클러스터에서 아직 리소스를 반환하지 않음
# Failed 볼륨이 자동 반환에 실패함
status:
  phase: Bound

# claimRef 부분을 삭제하해 Available 상태로 만들 수 있다.
kubectl patch pv ysoftmanPV -p '{"spec":{"claimRef:"null}}'

# 하지만 pv 가 terminating 상태에서 삭제가 안된다.
# finalizers: 오브젝트 삭제 시 충족해야될 조건을 명시하는 곳
# kubernetes.io/pv-protection: pv, pvc 등에서 실수로 오브젝트 삭제를 막기위해 기본적으로 명시되어 있다.
kind: PersistentVolume
metadata:
  finalizers:
  - kubernetes.io/pv-protection

# 다음과 같이 finalizers 조건을 패치(또는 kubectl edit.. 로 해당 부분 삭제)하면, pv 가 삭제된다.
kubectl patch pv ysoftmanPV -p '{"metadata":{"finalizers":null}}'

#####

# 위 내용을 바탕으로 많은 PV 값을 수정해 반영하는 스크립트를 다음과 같이 작성한다.
# 우선 변경할 pv 들을 yaml 로 로컬에 백업해두자.
mkdir -p pv
for name in $(kubectl get pv -A | grep -i aaa | awk '{print $1}'); do 
    echo "backup pv manifest(yaml)... ./pv/$name.yaml"
    kubectl get pv $name -o yaml > ./pv/$name.yaml
done

# pv 삭제
for name in $(kubectl get pv -A | grep -i aaa | awk '{print $1}'); do 
    echo "delete pv manifest(yaml)"
    # delete 하면 terminating 상태가 유지되는데, 이때 finalizers > kubernetes.io/pv-protection 를 삭제해야 완전히 제거된다.
    kubectl delete pv $name & kubectl patch pv $name -p '{"metadata":{"finalizers":null}}'
done

# 백업해둔 pv yaml 에서 ip 만 변경해서 적용
for f in $(ls -1 ./pv); do 
    cat ./pv/$f | sed -e 's/server: 10.10.10.11/server: 10.10.10.12/'g | kubectl apply -f -
done

ssh 로그인 후 바로 종료 문제

# k8s pod 내에 sshd -D 를 기동 후
# 다음과 같이 ssh 연결을 하면 바로 연결이 close 된다.
ssh root@localhost -vvv

... 생략 ...
Last login: Sat Apr 29 07:38:26 2023 from 127.0.0.1
debug3: receive packet: type 96
debug2: channel 0: rcvd eof
... 생략 ...
Connection to localhost closed.
debug1: Exit status 255

# receive packet : type 96(SSH_MSG_CHANNEL_EOF)으로
# 보통 연결된 ssh 에서 exit/logout 하면 받는 패킷이다.

# lastlog(/var/log/lastlog) 로 마지막 로그를 봐도 로그인 된 흔적이 있다.
Username         Port     From             Latest
root             pts/3    127.0.0.1        Sat Apr 29 07:38:28 +0000 2023
... 생략 ...

# lastb(/var/log/btmp)로 실패한 로그인 기록은 보면 없다고 나오고
# last(/var/log/wtmp) | head -2 로 성공한 로그인 기록을 보면 내역에도 기록 되어 있다.
# 그런데 로그인 후 바로 로그아웃되었다.
root     pts/3        127.0.0.1        Sat Apr 29 07:38 - 07:38  (00:00)
root     pts/3        127.0.0.1        Sat Apr 29 07:38 - 07:38  (00:00)

# 혹시 root 기본 쉘을 확인해보면 bash 정상
cat /etc/passwd | grep groot
root:x:0:0:root:/root:/bin/bash

# ssh closed 시 verbose 내용에 255 종료상태가 있는것을 보고 
# 체크1
# 로그인시 ~/.profile, ~/.bashrc 를 rename 으로 로딩을 하지 않아도 closed 된다.

# 체크2
# root/.ssh/ 에 있는 environment 파일이 다른 정상 container 와 달리 꽤 커서 살펴봤다.
# pod(container)의 파일을 로컬로 복사
kubectl cp {namespace}/{pod_name_ssh_접속정상}:/root/.ssh/environment ./environment1
kubectl cp {namespace}/{pod_name_ssh_접속문제}:/root/.ssh/environment ./environment2

# environment1 파일은 500라인인데, environment2 파일은 1000라인이다.
# 문제가 있는 container /root/.ssh/environment 의 내용을 라인을 지워가면서 접속 여부를 확인해봤다.
# 확인결과 environment 내용에 상관없이 라인수를 991로 줄이면 접속이 성공한다.
# 접속시 너무 많은 environment 설정(현재 client 와 중복도 많이 되고 있었음) 문제였음...

linux disk or memory /tmp directory

# go 서버(k8s pod)에 multipart 형식으로 파일을 업로드하면 context.multipartfrom() 에서 
# /tmp 위치에 multipart-3000453554 등의 이름으로 임시파일을 생성하게 된다.
# 여러 linux 배포판에서 disk IO 를 줄이기 위해 /tmp 는 디폴트로 tmpfs(RAM based) 파일시스템을 사용한다고 한다.
# 사용한 리눅스 배포판은 Debian GNU/Linux 9 (stretch) 이다.
# 그런데 df, mount, findmnt 명령에는 /tmp 마운트(파일시스템) 정보가 보이지 않는다.

# 파일 업로드를 진행하면
# /tmp/multipart... 파일이 점점 커지면서
watch -n 1 ls -ahl /tmp

# free 는 점점 줄어 들고 있는것 처럼 보인다.
# -s: repeat printing every N seconds
# -w: wide output
# -m: show output in mebibytes
free -s 1 -w -m

# /tmp 사용량을 보면 mounted on / 정보가 나온다.
# 이는 disk 를 사용하고 있다는 얘기다.
# 메모리 뿐만 아니라  / 의 disk 용량도 줄어들고 있는것이 확인된다.
watch -n 1 df -m /tmp

# 다시 free 결과를 보면 cache(pages with actual contents of files or block devices) 용량이 증가한다.
# 예전 free 관련에서 정리한 포스팅을 다시 참고해보면
# cache 의 목적은 최근 자주 사용되는 데이터를 메모리에 보관하여 재사용시 디스크까지 가서 않고 메모리에서 빠르게 액세스/로딩을 위해 사용된다.
# cache 사용량이 증가했지만 
# 가용메모리 = free + buffers + cached 로 사용할 수 있는 메모리가 줄어든것이 아니였다.
# 실제로 kubectl top 로 보면 메모리 사용량은 증가하지 않았다.
# kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
kubectl top pod -n my-namespace

# 결론... /tmp 는 디스크를 사용하고 있었다.

aws bastion 접속없이 로컬에서 kubectl 사용하기

# 보안상의 이유로 다음과 같이 aws bastion 을 사용한다.
# aws private <-> aws bastion <-> 외부(local...)
# 그래서 기본적으로는 다음과 같이 aws eks 클러스터에 kubectl 명령은 bastion 에 접속 후 사용해야 한다.
ssh -i aws-ysoftman.pem ec2-user@xxxx.ap-northeast-2.compute.amazonaws.com
[ec2-user@ip-xxxxxx ~]$ kubectl get ns

# ssh tunneling 으로 로컬에서 bastion 통해 바로 kubectl 을 사용(연결)할 수 있다.
# -f 백그라운드 실행, 추후 필요없으면 ssh 프로세스 kill 필요, -f 없이 사용해도 되고,이러면 ctrl+c 로 종료
# -N 원격 명령을 실행하지 않는다. 포트 포워딩할때 같이 사용
# -L 로컬포트->특정호스트:포트로 바인딩
# alias 로 만들어 사용하자.
alias aws-prod-start-tunnel='ssh -fNL 29443:xxx.ap-northeast-2.eks.amazonaws.com:443 -i ~/.ssh/aws-ysoftman.pem ec2-user@xxx.ap-northeast-2.compute.amazonaws.com'
alias aws-prod-stop-tunnel="ps -ef | grep 'ssh -fNL' | grep -v grep | awk '{print \$2}' | xargs kill -9"


# 로컬 호스트에 도메인 추가한다.
sudo vi /etc/hosts
127.0.0.1     kubernetes.default

# 참고로 k8s 허용하지 않는 도메인을 사용하면 다음과 같은 에러가 발생한다.
Unable to connect to the server: x509: certificate is valid for xxx.ap-northeast-2.eks.amazonaws.com, xxx.ap-northeast-2.compute.internal, kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster.local, not aws-ysoftman

# 이 에러를 무시하려면 다음과 같이 매번 옵션을 명시해 실행할 순 있다.
# kubectl get ns --insecure-skip-tls-verify

# aws cli 로 kubectl 접속 설정 파일을 생성한다.
aws eks update-kubeconfig --region ap-northeast-2 --name ysoftman-k8s --kubeconfig ~/.kube/kubeconfig-aws-ysoftman.yaml

# 접속 설정 파일 중 server 부분을 다음과 같이 변경한다.
vi ~/.kube/kubeconfig-aws-ysoftman.yaml
apiVersion: v1
clusters:
  - cluster:
      server: https://kubernetes.default:29443

# 이제 (ssh 연결된 상태니) 로컬에서 kubectl 응답을 받을 수 있다.
kubectl get ns

ingress-nginx-controller not found error

# ingress nginx controller(k8s.gcr.io/ingress-nginx/controller:v0.44.0) log 를 확인해 보면
kubectl logs -f $(kubectl get pod -n ingress-nginx | rg -N ingress-nginx-controller --color never  | awk '{print $1}')

# 다음과 같은 에러가 무수히 발생하고 있다.
E1227 03:37:10.227828       7 queue.go:130] "requeuing" err="services \"ingress-nginx-controller\" not found" key="&ObjectMeta{Name:sync status,GenerateName:,Namespace:,SelfLink:,UID:,ResourceVersion:,Generation:0,CreationTimestamp:0001-01-01 00:00:00 +0000 UTC,DeletionTimestamp:<nil>,DeletionGracePeriodSeconds:nil,Labels:map[string]string{},Annotations:map[string]string{},OwnerReferences:[]OwnerReference{},Finalizers:[],ClusterName:,ManagedFields:[]ManagedFieldsEntry{},}"

# nginx 설정에 다음과 같이 --publish-service 를 ingress-nginx-controller 로 설정했는데 ingress-nginx-controller 서비스가 존재하지 않았다.
--publish-service=$(POD_NAMESPACE)/ingress-nginx-controller

# ingress-nginx-controller 를 사용하는 ingress-nginx-controller 이름의 서비스 리소스를 생성하면 더이상 에러가 발생하지 않는다.
cat << zzz | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: 80
    protocol: TCP
  - name: https
    port: 443
    targetPort: 443
    protocol: TCP
  selector:
    app.kubernetes.io/component: controller
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/name: ingress-nginx
zzz

k8s ValidatingWebhookConfiguration 으로 문법 에러 ingress 리소스 생성 방지

# kubectl 과 같은 api 요청을 받는 k8s 서버는 kube-api handler 이후 요청한 리소스에 대해 mutating(리소스 변경),validating(리소스 검증) admission(허용 여부 판단 webhook) 처리를 하여 실제 리소스 설정(etcd 에 저장)될지 말지를 처리한다.

# nginx 에 적용될 server-snippet 등에 문법이 에러가 있는 ingress (리소스)를 적용 요청을 하면 에러 없이 리소스가 생성(등록)되는게 문제다.
kubectl apply -f syntax_error_ingress.yaml
ingress.extensions/ysoftman-test-ingress created

# nginx log 확인해 보면 에러 ingress 리소스 로드 시도가 계속 실패
kubectl logs -f $(kubectl get pod -n ingress-nginx | rg -N ingress-nginx-controller --color never  | awk '{print $1}')
 -------------------------------------------------------------------------------
Error: exit status 1
2021/12/22 17:19:34 [emerg] 15035#15035: invalid number of arguments in "proxy_set_header" directive in /tmp/nginx-cfg062794518:1076
nginx: [emerg] invalid number of arguments in "proxy_set_header" directive in /tmp/nginx-cfg062794518:1076
nginx: configuration file /tmp/nginx-cfg062794518 test failed
-------------------------------------------------------------------------------
W1222 17:19:34.507544       7 queue.go:130] requeuing ysoftman-test/ysoftman-test-ingress, err
-------------------------------------------------------------------------------

# 잘못된 ingress 리소스가 등록되어 계속 nginx 가 리로딩 실패해 문제가 되니 바로 지우자.
kubectl delete -f syntax_error_ingress.yaml

# 잘못된 설정으로 ingress-nginx-controller 가 전체에 영향 주는것을 막기 위해
# validating admission webhook server 를 옵션으로 노출할 수 있다.
# ValidatingWebhookConfiguration 리소스를 등록한다.


#####


# ValidatingWebhookConfiguration 으로 문법 에러 ingress 리소스 생성 방지하기

# (minikube 기준) kube-apiserver enable-admission-plugins 옵션에 ValidatingAdmissionWebhook 가 있는지 확인
kubectl get pod kube-apiserver-minikube -o=json -n kube-system | jq '.spec.containers[0].command' | rg -N "enable-admission-plugins"
  "--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota",

# ValidatingAdmissionWebhook 보이지 않아도 디폴트로 추가되어 있어 별도로 추가하지 않아도 된다.
# master 노드(장비) 마다 접속해 다음 파일에서
# --enable-admission-plugins 값을 추가하면
# kubelet(cluster의 모든 노드에 떠있는 agent)이 변경을 감지해 kube-apiserver(pod)가 자동으로 재시작 된다.
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml


# 방법1 - helm 으로 설치하면 ValidatingWebhookConfiguration 관련 리소스들이 자동 설치된다.


# 방법2 - ValidatingWebhookConfiguration 수동 등록
# 다음 명령 결과가 있다면 admission controller 를 사용할 수 있다.
# k8s 버전에 따라 
# k8s 1.6 이후는 admissionregistration.k8s.io/v1
# k8s 1.9 이후는 admissionregistration.k8s.io/v1beta1
kubectl api-versions | grep admissionregistration

# ingress-nginx-controller 버전 확인
kubectl get daemonset ingress-nginx-controller -n ingress-nginx -o=json | jq '.spec.template.spec.containers[0].image'
"quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.25.1"

# ValidatingWebhookConfiguration 은 TLS 로 통신해야 한다.
# 다음과 같이 self-singed 로 키를 생성한다.
# service 에서 사용할 이름이 매칭될 수 있도록 CN을 다음과 같이 설정한다. (CN=${SERVICE_NAME}.${NAMESPACE}.svc)
openssl req -x509 -newkey rsa:2048 -keyout validating-webhook-key.pem -out validating-webhook-cert.pem -days 100000 -nodes -subj "/CN=ingress-nginx-controller-admission.ingress-nginx.svc"

# secret 리소스를 등록하자.(base64 인코딩돼 등록되기 때문에 secret 리소스를 보면 LS0... 으로 시작하는 문자열이 된다.)
kubectl create secret tls ingress-validation-tls -n ingress-nginx \
--key validating-webhook-key.pem \
--cert validating-webhook-cert.pem

# ingress-nginx-controller 옵션 --validating-webhook 옵션들 추가
# secret 는 volumes, volumeMounts 로 pod 에서 파일로 접근하도록 한다.
kubectl edit daemonset ingress-nginx-controller -n ingress-nginx
... 생략 ...
      containers:
      - args:
        - /nginx-ingress-controller
        - --enable-ssl-chain-completion=false
        - --configmap=$(POD_NAMESPACE)/ingress-nginx
        - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
        - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
        - --annotations-prefix=nginx.ingress.kubernetes.io
        - --default-backend-service=default/default-backend-service
        - --report-node-internal-ip-address
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/tls.crt
        - --validating-webhook-key=/usr/local/certificates/tls.key
... 생략 ...
        ports:
        - containerPort: 8443
          name: webhook
          protocol: tcp
... 생략 ...
          volumeMounts:
          - name: webhook-cert
            mountPath: /usr/local/certificates/
            readOnly: true
... 생략 ...
      volumes:
      - name: webhook-cert
        secret:
          secretName: ingress-validation-tls

# ValidatingWebhookConfiguration 과 ingress-nginx-controller-admission service 리소스 등록은 아래 URL에 정리


#####


# 참고 이슈(삽질 엄청함ㅠ)
# 위의 모든 설정을 했는데 invalid ingress 가 아무런 제약없이 created 된다.
# ingrss nginx controller pod 로그를 보면 validationwebhook 은 동작되지만
kubectl logs -f $(kubectl get pod -n ingress-nginx | rg -N ingress-nginx-controller --color never  | awk '{print $1}') | rg "admission" -C 2

# 다음 로그 처럼 accepting 되는 문제가 있었다.
server.go:61] handling admission controller request /extensions/v1beta1/ingress?timeout=10s
main.go:87] accepting non ingress  in namespace ysoftman-test-namespace extensions/v1beta1, Resource=ingresses

# 나와 같이 nginx-ingress-controller:0.25.1 에서
# extensions/v1beta1 를 사용하지 못하는 문제가 있었다.

# syntax_error_ingress apiVersion 을 다음과 같이 변경
extensions/v1beta1 --> networking.k8s.io/v1beta1

# ValidatingWebhookConfiguration 에 networking.k8s.io/v1beta api 추가
  - apiGroups:
    - networking.k8s.io
    - extensions
    apiVersions:
    - v1
    - v1beta1
... 생략 ...
      path: /networking.k8s.io/v1beta1/ingresses

# 이제 server_snippnet 오타가 있는 ingress 등록시 에러가 발생하고 생성되지 않는다.
kubectl apply -f syntax_error_ingress.yaml
namespace/ysoftman-test-namespace unchanged
Error from server: error when creating "syntax_error_ingress.yaml": admission webhook "validate.nginx.ingress.kubernetes.io" denied the request:
-------------------------------------------------------------------------------
Error: exit status 1
2021/12/28 19:11:31 [emerg] 1385#1385: unexpected "}" in /tmp/nginx-cfg650933801:19967
nginx: [emerg] unexpected "}" in /tmp/nginx-cfg650933801:19967
nginx: configuration file /tmp/nginx-cfg650933801 test failed

kubectl config 파일 통합

# kubectl 은 기본 ~/.kube/config 설정 파일을 사용한다.
# (참고 config 설정은 k8s 노드의 /etc/kubernetes/admin.conf 에 있다.)
# 특정 클러스터 접속 정보를 yaml 파일로 제공하는 경우
# 다음과 같이 KUBECONFIG 환경변수에 명시해 사용한다.
export KUBECONFIG=~/.kube/config:~/.kube/test-config.yaml 

# 그런데 kubectx (https://github.com/ahmetb/kubectx) 사용시 
# 멀티 yaml 설정이 지원되지 않아 다음과 같은 에러가 발생했다.
error: kubeconfig error: failed to load: cannot determine kubeconfig path: multiple files in KUBECONFIG are currently not supported

# 다음과 같이 ~/.kube/config 하나로 통합할 수 있다.
mv -v ~/.kube/config ~/.kube/config.bak
KUBECONFIG=${HOME}/.kube/config.bak:${HOME}/.kube/test-config.yaml kubectl config view --flatten > ${HOME}/.kube/config

# 참고
# ~/.kube 에 아래 파일들이 있고 파일하나가 클러스터 정보 하나를 가지고 있는 상태에서 KUBECONFIG 에 설정
config
lemon.yaml
apple.yml
banana.yml
- kubectl config use-context lemon-context 수행시
  - touch config.lock 만들고 config.lock: file exists 에러 발생
  - touch lemon.yaml.lock 만들면 lemon.yaml.lock: file exists 에러 발생
- KUBECONFIG 에서 config 를 제외하면 config.lock 파일이 있어도 상관없음 
- kubectl config use-context lemon-context 로 변경하면 config 파일이 변경됨(KUBECONFIG 에 첫번째로 설정된 파일의 current-context 가 변경됨)


#####


# fzf 가 설치되어 있다면 kubectx 실행시 fzf 선택 메뉴가 나타난다.
# fzf 메뉴 없이 kubectx 로 리스트만 보려면
KUBECTX_IGNORE_FZF=1 kubectx
# 또는
kubectx | cat

# kubectx -d 로 context 를 삭제할 수 있지만
# 해당 context 에서 설정된 user, cluster 정보는 남아 있다.
kubectx -d ysoftman_test_context

k8s dashboard 사용하기

# k8s dashboard 사용하기
# 참고로 oauth proxy + github 인증으로 로컬 proxy 서버없이 접근하는 방법도 있다.

# k8s dashboard 설치
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.5/aio/deploy/recommended.yaml

# 로컬에서 k8s api 연결 할 수 있는 프록시 서버 띄우기
kubectl proxy

# 로컬 URL 로 dashboard 접근
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/

#####

# 위의 로컬 프록시는 매번 로컬에 프록시 서버를 띄워 접속해야 돼서 불편하다.
# 고정 도메인으로 접속 하기

# 방법1 - ingress -> service 80 -> pod 9090 으로 연결하는 방식
# kubernetes-dashboard deployment 설정 변경
# 모든 8443 -> 9090 으로 변경
# scheme HTTPS -> HTTP 로 변경
# container arg 기존 설정은 다 삭제하고 다음으로 변경
- --namespace=kubernetes-dashboard
- --insecure-bind-address=0.0.0.0
- --insecure-port=9090
- --enable-insecure-login

# kubernetes-dashboard service 설정 변경
# 80 -> pod 9090 으로 연결되도록 변경
ports:
- port: 80
  protocol: TCP
  targetPort: 9090

# kubernetes-dashboard ingress 추가
# host 는 ingress-nginx controller 설치시 생성되는 aws loadbalaner 사용
cat << EOF | kubectl apply -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  namespace: kubernetes-dashboard
  name: kubernetes-dashboard-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/backend-protocol: HTTP
    nginx.ingress.kubernetes.io/cors-allow-origin: '*'
    nginx.ingress.kubernetes.io/enable-cors: "false"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
    - host: aaa111.elb.ap-northeast-2.amazonaws.com
      http:
        paths:
          - path: /dashboard/(.*)
            backend:
              serviceName: kubernetes-dashboard
              servicePort: 80
EOF

# 접속 확인
https://aaa111.elb.ap-northeast-2.amazonaws.com/dashboard/#/login

# 구입한 도메인이 있다면 호스트명 변경해서 사용하자
kubectl edit ingress kubernetes-dashboard-ingress -n kubernetes-dashboard
    # 구입한 도메인을 사용
    - host: lemon-apple.ysoftman.com

# 접속 확인
https://lemon-apple.ysoftman.com/dashboard/#/login

# 참고로 위 설정에서 http 를 사용했으니
# 로컬 kubectl proxy 사용시 다음과 같이 http 를 사용해야 한다.
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/#/login

# 방법2
# 별도 서버가 있다면 aws 클러스터 접속 설정하고 kubectl proxy 실행한다
nohup kubectl proxy

#####

# dashboard service(admin) account 토큰을 파악해 대시보드 로그인에 사용
kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | rg admin | awk '{print $1}') | rg -i token: | awk '{print $2}' | pbcopy

k8s ingress-controller model

k8s 에서 설정된 특정(ysoftman.lemon.com) 호스트에 대해 여러개의 인그레스가 있고
각 인그레스는 default backend (path rule 에 없는 경우 처리할 백엔드 서비스 설정) 가 설정되어 있다.
이 중 A 인그레스의 default backend 의 설정 또는 삭제 변경에 대해서만 ingress-nginx-controller 에 반영되어 처리되는 현상이 있어 찾아봤다.

참고

위 링크를 보면 클러스터 전반에 대한 셋팅으로 nginx model(설정이 메모리에 로딩된 상태를 의미하는것 같다.)이라는 것이 있다. 이 모델은 생성(빌드)에 비용이 많이 들어 특정 작업이 있을때만 빌드(로드된다.)
그리고 모델 생성시 규칙이 있는데 여러 인그레스 중 같은 host, path 등을 가지고 있다면 가장 오래된(최초로 만들어진) 인그레스의 설정이 우선한다.

모델 생성시 오래된 rule 우선 규칙으로 인해 가장 오랜된 A 인그레스의 설정만
ingress-nginx-controller 의 default backend 설정에 반영된다.

# 참고로 생성된지 오래된 순으로 ingress 파악
kubectl get ingress --sort-by=.metadata.creationTimestamp 

원인 파악하느라 너무 삽질을 많이 했다. 
이제 속이 후련해서 개비스콘 짤 생성~ㅋ (https://gvsc.rajephon.dev/)

k8s ingress-nginx-controller 설정(nginx.conf) 파악

# ingress-nginx(controller) 에서 ysoftman.lemon.com / 에 대한 처리가
# 어떻게 되는지 보기 위해 --v=3 옵션을 주고 ingress-nginx-controller 를
# 다시 시작(deployment 리소스가 없으면 daemonset 리소스를 수정)
# 다음과 같이 실제 nginx 설정이 어떻게 되어 있는지 볼 수 있지만 diff 만 보인다.ㅠㅠ
kubectl logs -f $(kubectl get pod -n ingress-nginx  | rg -v NAME | awk '{print$1}' | head -1) -n ingress-nginx | rg "server_name ysoftman.lemon.com" -C 50

# 실제 running 중인 ingress-nginx-controller 의 nginx.conf 을 덤프해보자.
# nginx 동작하는 노드 접속
ssh ysoftman@인그레스서버

# nginx-controller 도커 컨테이너에 접속
sudo docker exec -it --user=0 --privileged $(sudo docker ps | grep ingress-nginx-controller | awk '{print $1}') bash

# nginx 가 --with-debug 옵션으로 동작 중인지 확인
nginx -V 2>&1 | grep -- '--with-debug'

# nginx master PID 파악해서 gdb 로 열기
gdb -p $(ps -ef | grep "nginx: master" | grep -v grep | awk '{print $2}')

# (gdb) 에 아래 명령 복붙
set $cd = ngx_cycle->config_dump
set $nelts = $cd.nelts
set $elts = (ngx_conf_dump_t*)($cd.elts)
while ($nelts-- > 0)
set $name = $elts[$nelts]->name.data
printf "Dumping %s to nginx_conf.txt\n", $name
append memory nginx_conf.txt \
        $elts[$nelts]->buffer.start $elts[$nelts]->buffer.end
end

# (gdb) 종료
quit

# 덤프된 nginx_conf.txt 확인
cat nginx_conf.txt

# 호스트로 빠져 나온다.
exit

# 컨테이너 -> 호스트(노드)로 nginx_conf.txt 복사
sudo docker cp $(sudo docker ps | grep ingress-nginx-controller | awk '{print $1}'):/etc/nginx/nginx_conf.txt .

# 로컬로 빠져 나온다.
exit

# 노드 -> 로컬로 nginx_conf.txt 복사
rsync ysoftman@인그레스서버:/home/ysoftman/nginx_conf.txt .

# nginx_conf.txt 파일이 너무 크니
# ## start server ysoftman.lemon.com  ~  ## end server ysoftman.lemon.com 만 남기고 지운다.

# 문제가 있는 location 의 라인번호를 보고 다시 nginx_conf.txt 에서 찾아 보자
rg -n "location " nginx_conf.txt


#####


# 좀더 편하게 kubectl ingress-nginx 플러그인을 사용해 파악할 수도 있다.
# nginx-controller 가 daemonset 으로 동작하는 경우 pod 를 찾을 수 없다고 나온다.
# deployment 로 동작하는 경우만 작동하는것으로 보인다.
# 설치
brew install krew
kubectl krew install ingress-nginx
export PATH="${PATH}:${HOME}/.krew/bin"

# 백엔드 정보
kubectl ingress-nginx backends -n nginx-ingress

# 특정 호스트 관련 설정 정보
kubectl ingress-nginx conf -n ingress-nginx --host testaddr.local


k8s configmap 내용 즉시 적용

# kubernetes(k8s) configmap 으로 값을 쓰면 실제 pod 에 전달(적용)까지
# 시간이 좀 걸리는 것 같아 찾아보니

`
As a result, the total delay from the moment when the ConfigMap is updated to the moment when new keys are projected to the pod can be as long as kubelet sync period (1 minute by default) + ttl of ConfigMaps cache (1 minute by default) in kubelet. You can trigger an immediate refresh by updating one of the pod's annotations.
`

# 적용까지 1분+1분(캐시) 걸리 수 있는데,... pod annotation 업데이트하면 pod 에 바로 반영되는것 같다.

# 실제 configmap 적용 후 바로 다음과 같이 refresh 를 위해 pod annotation 을 업데이트하니 바로 pod 에 적용된다.
# 계속 사용해야 하니 유닉스타임(초)와 같은 타임스탬프값을 사용하면 좋을 것 같다.
kubectl annotate --overwrite pods $(kubectl get pods | grep ysoftman | awk '{print $1}') updated="$(date +%s)"



로컬에서 k8s redis cluster 접속

# k8s 에 redis cluster 를 구성하였다.
# redis 노드1개는 1개의 pod 로 구성되고, 이 pod 를 서비스로 묶었다.

# 다음과 클라이언트가 k8s 안의 pod(redis-cluster-0) 라면 접속이 잘된다.
kubectl --namespace redis exec -it redis-cluster-0 -- redis-cli -h {레디스 노드 중 하나} -p 6379 -c

# 실제 서비스 서버도 k8s 배포되어 운영시는 문제가 없지만
# 개발시에는 로컬에서 k8s redis 에 접근이 필요하다.
# 그래서 redis 의 k8s service 에 nodeport (32000) 설정하고 
# k8s nodeIP:nodeport 로 접속을 시도 하였다.
redis-cli -h {k8s 노드 중 하나} -p 32000 -c
# 접속은 되지만 set 수행시 연결되지 않고,
set aaa bbb
# 이미 저장된 값을 get 수행하면 moved 로 
# redis pod 로 redirect 시도하고 연결되지 않는다.
get aaa
moved xxxx

# 로컬에서 redis set,get 등 수행시 해당 값을 redis node(slot)으로 분배를 시도하고
# 결국 redis-cluster 의 pod(redis node) ip 에 접근할 수 밖에 없다.

# 해결 방법은 redis-cluster 를 프록시 해주는 redis-cluster-proxy 프로그램을
# k8s service(+pod)에 두고, 이 redis-cluster-proxy 로 요청을 하도록 한다.

# 먼저 redis-cluster-proxy 도커 이미지를 생성한다.
# os 버전이 낮아 gcc 버전이 낮으면 빌드 에러가 발생하니 최신 os 기반에서 빌드하자.
# 자세한 내용은 dockerfile 참고

# 이미 빌드된 redis-cluster-proxy 도커 이미지 사용시 참고


# 이제 redis-cluster-proxy 를 k8s service(+pod)로 다음과 같이 구성한다.
# redis-cluster-proxy.yaml
apiVersion: v1
kind: Pod
metadata:
  name: redis-cluster-proxy
  labels:
    app: redis-cluster-proxy
spec:
  containers:
    - name: redis-cluster-proxy
      image: ysoftman/redis-cluster-proxy:latest
      imagePullPolicy: Always
      command:
        ["/redis-cluster-proxy/src/redis-cluster-proxy", "10.123.123.123:6379"]
---
apiVersion: v1
kind: Service
metadata:
  name: redis-cluster-proxy
  labels:
    app: redis-cluster-proxy
spec:
  # type: ClusterIP
  type: NodePort
  ports:
    - port: 7777
      targetPort: 7777
      nodePort: 30777 # The range of valid ports is 30000-32767
      name: redis-cluster-proxy
  selector:
    app: redis-cluster-proxy

# k8s service(+pod) 반영
kubectl apply -f redis-cluster-proxy.yaml --namespace redis

# 다음과 같은 요청 흐름이 된다.
로컬 redis-cli k8s 노드 중 하나:30777 --> redis-cluster-proxy(service) --> redis-cluster-proxy(pod):7777 --> redis cluster nodes(pods):6379

# 이제 로컬 개발시에는 redis-cluster-proxy 를 통해 k8s redis 를 사용할 수 있다.
redis-cli -h {k8s 노드 중 하나} -p 30777 -c


#####


# helm 으로 외부에서 접근 가능한 redis-cluster 으로 구성할 경우
# 다음과 같이 실행하면 service 3개에 각각 redis pod 1개를 띄우고, 
# 서비스별 external ip 를 생성한다.(시간이 좀 걸린다.)
helm install redis-cluster bitnami/redis-cluster \
--namespace redis-cluster \
--set cluster.node=3 \
--set cluster.externalAccess.enabled=true

# 이후 helm upgrade 설치 가이드 명령 참고해서 
helm upgrade redis-cluster bitnami/redis-cluster \
--namespace redis-cluster \
--set cluster.node=3 \
--set cluster.externalAccess.enabled=true \
--set cluster.externalAccess.service.type=LoadBalancer \
--set "cluster.externalAccess.service.loadBalancerIP[0]=10.10.10.1,cluster.externalAccess.service.loadBalancerIP[1]=10.10.10.2,cluster.externalAccess.service.loadBalancerIP[2]=10.10.10.3"


# 참고


kubernetes RevisionHistoryLimit

# kubernetes(k8s) deployment 적용(재시작)시 replicaset 이 새로 생성되는데
# 이전 replica 가 삭제되 않고 유지되는 경우가 있다.
kubectl get replicaset | grep -E "NAME|ysoftman"
NAME                       DESIRED   CURRENT     READY     AGE
ysoftman-deployment-11111  0         0           0         1d
ysoftman-deployment-22222  0         0           0         2d
ysoftman-deployment-33333  1         1           1         1d

# deployment 설정에서 revisionHistoryLimit 을 0 으로 주면 이전 replica 를 유지 하지 않고 삭제한다.
# revisionHistoryLimit 디폴트 값은 10이다.
https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy
spec:
  revisionHistoryLimit:0

# replicatset 에 patch 로 바로 적용할 수 있다.
kubectl patch deployment/ysoftman-deployment -p '{"spec":{"revisionHistoryLimit":0}}'

# deployment 전체 재시작 후
kubectl rollout restart deployment

# deployment 전체 replica 이전 revision 삭제
kubectl patch deployment $(kubectl get deployment | awk '{print $1}') -p '{"spec":{"revisionHistoryLimit":0}}'

# 참고
# patch 명령시 --type='strategic'가 디폴트다.
# [] 부분 변경시 다음과 같이 해당 원소(오브젝트) 전체를 명시한다.
# 예) ingress host 변경시 host 가 포함된 오브젝트 전체 명시
kubectl patch ingress ysoftman_ing -n ysoftman_ns -p \
'{
  "spec": {
    "rules": [
      {
        "host": "ysoftman.host",
        "http": {
          "paths": [
            {
              "backend": {
                "service": {
                  "name": "ysoftman_service",
                  "port": {
                    "name": "http"
                  }
                }
              },
              "path": "/abc",
              "pathType": "Prefix"
            }
          ]
        }
      }
    ]
  }
}'

k8s go client 클러스터 커넥션

# 처음 kubectl 로 kubernetes(k8s) 클러스터에 접속하려면 다음의 과정이 필요하다.
# 사용자인 설정
kubectl config set-credentials ysoftman --token=abc123

# ysoftman1-cluster 라는 클러스터 설정
kubectl config set-cluster ysoftman1-cluster --insecure-skip-tls-verify=true --server https://ysoftman1:6443

# ysoftman2-cluster 라는 클러스터 설정
kubectl config set-cluster ysoftman2-cluster --insecure-skip-tls-verify=true --server https://ysoftman2:6443

# 컨텍스트는 cluster + user 를 조합해서 접속 정보를 나타낸다.
# ysoftman1-context 컨텍스트 설정
kubectl config set-context ysoftman1-context --cluster=ysoftman1-cluster --user=ysoftman

# ysoftman2-context 컨텍스트 설정
kubectl config set-context ysoftman2-context --cluster=ysoftman2-cluster --user=ysoftman

# 위 2개의 클러스터 중 ysoftman1-context 를 사용
# ysoftman 계정을 이용해 ysoftman1-cluster 에 접속(요청)하게 된다.
kubectl config use-context ysoftman1-context

# 위와 같이 설정하면 ${HOME}/.kube/config 파일이 다음과 같이 생성된다.
apiVersion: v1
clusters:
- cluster:
    insecure-skip-tls-verify: true
    server: https://ysoftman1:6443
  name: ysoftman1-cluster
- cluster:
    insecure-skip-tls-verify: true
    server: https://ysoftman2:6443
  name: ysoftman2-cluster
contexts:
- context:
    cluster: ysoftman1-cluster
    user: ysoftman
  name: ysoftman1-context
- context:
    cluster: ysoftman2-cluster
    user: ysoftman
  name: ysoftman2-context
current-context: ysoftman1-context
kind: Config
preferences: {}
users:
- name: ysoftman
  user:
    token:
    abc123

# 설정된 클러스터 확인
kubectl config get-contexts

# 현재 사용중인 클러스터 확인
kubectl config current-context

# 클러스터를 변경해서 작업(요청)하려면 컨텍스트를 바꿔야 한다.
kubectl config use-context 사용할_컨텍스트

# golang 등의 client 에서는 kubectl config 파일을 그대로 사용할 수 있다.
https://kubernetes.io/docs/tasks/administer-cluster/access-cluster-api/#go-client

# 참고로 golang client 로 N 개의 클러스터에 동시 접속이 필요한 경우
# current-context 로 구분된 N 개의 config(.yaml) 파일을 만들어
# N 개의 커넥터를 만들어 사용했다.

helm command

# helm 은 redis, jenkins, couchbase 등 널리 알려진 패키지(k8s에서는 리소스)을
# kubernetes(k8s) 환경에 쉽게 배포 운영해주는 일종의 패키지(리소스) 매니져다.
# helm 은 charts(k8s 리소스 생성을 위한 설정들 모아놓은 패키지)를 관리하는 방식으로 사용된다.
# kubectl 설치하고
wget https://storage.googleapis.com/kubernetes-release/release/v1.16.0/bin/linux/amd64/kubectl
chmod +x kubectl
sudo cp kubectl /usr/local/bin

# kubectl config 로 context 를 설정해야 한다.
kubectl config set-credentials xxx
kubectl config set-cluster xxx
kubectl config set-context xxx
kubectl config use-context xxx

# 설치 - mac
brew install kubernetes-helm

# 설치 - linux
# github 에 이미 빌드된 helm binary 사용한다.
# https://github.com/helm/helm/releases
# 참고로 helm 서버와 버전이 호환되는 helm client 를 사용해야 한다.
wget https://get.helm.sh/helm-v2.16.1-linux-amd64.tar.gz
tar zxvf helm-v2.16.1-linux-amd64.tar.gz
sudo cp linux-amd64 /usr/local/helm

# Error: incompatible versions 로 클라,서버 버전이 다른 경우
# helm 클라이언트를 서버 버전(2.14.1)으로 설치한다.
curl -L https://git.io/get_helm.sh | bash -s -- --version v2.14.1

# 초기화
# ~/.helm 을 셋팅한다.
helm init

# helm 으로 설치된 리스트
helm list -A

# 패키시 상태(Deployment, Service, Ingress...등)
helm status 이름

# helm chart 저장소 업데이트
helm update repo

# helm redis 설치
# 사용 가능한 redis chart 검색
helm search redis

# chart 상세보기
helm inspect stable/redis

# syntax 체크, -f value.yaml
helm lint stable/redis -f stable/values-dev.yaml

# 템플릿 렌더링 결과
helm template stable/redis -f stable/values-dev.yaml

# 설치 시뮬레이션, 디버그 메시지 출력
helm install stable/redis --name ysoftman-redis -n ysoftman-ns --dry-run --debug

# 설치(redis 클러스터 구성시 다음 명령 실행 후 출력되는 NOTES 부분 참고)
helm install stable/redis --name ysoftman-redis -n ysoftman-ns

# 업데이트
# upgrade 에서는 --history-max 로 헬름으로 배포된 히스토리 내역 최대 개수 제한 할 수있다. 기본(256개)
helm upgrade ysoftman-redis stable/redis -n ysoftman-ns --history-max 3

# values.yaml 의 특정값을 변경해서 업데이트
helm upgrade ysoftman-redis stable/redis -n ysoftman-ns --set ysoftman.key1=aaa,ysoftman.key2=bbb

# NOTES 부분은 status 로 다시 볼 수 있다.
helm status ysoftman-redis

# history 보기
helm history ysoftman-redis

# 롤백
helm rollback ysoftman-redis 1

# 삭제, aliases: uninstall, del, delete, un
helm delete ysoftman-redis

# 커스텀 chart 만들기
# 참고 https://docs.bitnami.com/kubernetes/how-to/create-your-first-helm-chart/
# 기본 디렉토리와 파일들을 생성해 준다.
# 참고로 Go template 엔진을 사용한다.
helm create mychart

# mychart 내의 파일을 수정후 상위 디렉토리로 이동후 커스텀 chart 로 설치할 수 있다.
helm install ./mychart --name ysoftman-app

# helm 으로 관리되는(설치된) 리소스 보기
# k9s > helm 리소스로도 볼 수 있다.
kubectl get all -A -l='app.kubernetes.io/managed-by=Helm'