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

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

AWS ingress-nginx controller https 사용하기

# 참고로 로드 밸런서 타입에 따라 각각 가능한 프로토콜이 정해져 있다.
# Application Load Balancer(ALB) - HTTP, HTTPS
# CLASSIC(Elastic) LOAD BALANCER (CLB/ELB) - HTTP, HTTPS
# Network Load Balancer(NLB) - TCP, TLS, UDP

# ALB + ALB ingress controller 를 사용한 경우
# ingress annotation 으로 인증서 AWS Certificate Manager(ACM)에 등록된
# 인증서를 arn 으로 사용할 수 있다.
kubectl edit ingress my-test-ingress -n my-test
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS":443}]'
    alb.ingress.kubernetes.io/actions.ssl-redirect: '{"Type": "redirect", "RedirectConfig": { "Protocol": "HTTPS", "Port": "443", "StatusCode": "HTTP_301"}}'
    alb.ingress.kubernetes.io/certificate-arn: arn:aws:acm:ap-northeast-2:11111:certificate/11111

# NLB + ingress-nginx controller 를 사용하는 경우
# ingress tls -> secretName 때문에 인증서를 k8s secret 리소스로 등록해야 된다.
# 발급된 인증서를 사용하는 경우
# aws private CA(인증서) 를 사용하는 경우 ACM 콘솔이나 다음 명령으로 내보낼 수 있다.
# 참고로 public CA 는 내보내기가 안된다.
aws acm export-certificate \
--certificate-arn arn:aws:acm:ap-northeast-2:11111:certificate/11111 \
--passphrase fileb://path-to-passphrase-file  \
| jq -r '"\(.Certificate)\(.CertificateChain)\(.PrivateKey)"'

# 테스트로 사용하는 경우
# self-signed certificate 인증서 생성해 사용
# cert 파일 생성
rm -rf tls.crt tls.key
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=*.dev.ysoftman-aws.io/O=*.dev.ysoftman-aws.io"

# 이제 secret 리소스 생성하자
kubectl create secret tls dev-ysoftman-aws --key tls.key --cert tls.crt -n my-test

# ingress -> tls -> secretName 명시
kubectl edit ingress my-test-ingress -n my-test
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
... 생략 ...
spec:
  tls:
  - hosts:
    - my-test-api.dev.ysoftman-aws.io
    secretName: dev-ysoftman-aws
... 생략 ...

# ELB + ingress-nginx controller 를 설치하면 alb 처럼 annotation 으로 public CA 를 사용할 수 있다.
# 참고 https://kubernetes.github.io/ingress-nginx/deploy/#aws -> deploy-tls-termination.yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.46.0/deploy/static/provider/aws/deploy-tls-termination.yaml

# 다운로드된 파일에서 XXX 부분을 변경 한다.
proxy-real-ip-cidr: XXX.XXX.XXX/XX  -> VPC CIDR 명시

# ingress-nginx-controller service 에서 public CA arn 을 사용할 수 있다.
arn:aws:acm:us-west-2:XXXXXXXX:certificate/XXXXXX-XXXXXXX-XXXXXXX-XXXXXXXX -> 발급받은 인증서 명시
# 적용
kubectl apply -f deploy-tls-termination.yaml

# external-ip 가 새로 만들어지기 때문에 route53 에서 기존 레코드 바인딩을 수정하자.
kubectl get svc --namespace ingress-nginx

# 이제 NLB 처럼 ingress tls,secret 설정 없이, https 를 사용할 수 있다.

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 service account 생성

# 외부 drone ci 등에 신규 k8s 클러스터에 접근이 필요한 경우
# service account 와 clusterrolebinding(Role-based access control (RBAC)) 리소스를 생성하고
# service account 의 token을 사용하면 된다.

# 다음과 같이 service account 명세를 작성한다.
cat << zzz > service_account_for_droneci.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: drone-ci
  namespace: default

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: drone-ci-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: drone-ci
  namespace: default
zzz

# 이제 적용하면 serviceAccount 가 secret 이 생성된다.
kubectl apply -f service_account_for_droneci.yaml

# drone-ci-token-xxx 를 파악해서 ~/.kube/config 에서 사용하면 된다.
kubectl get secret $(kubectl get secret -n default | grep drone | awk '{print $1}') -n default -o jsonpath="{.data.token}"

# 인증서 확인
kubectl get secrets

# 참고로 A 네임스페이스의 인증서 B 네임스페이스에 적용하기
kubectl get secret -n A ysoftman-test-cert -o yaml > cert.yaml
# cert.yaml 의 namespace=B 로 수정해서 적용한다.
kubectl apply -f cert.yaml