EKS-prometheus-grafana

https://docs.aws.amazon.com/ko_kr/eks/latest/userguide/prometheus.html

먼저 프로메테우스를 설치한다.

cat << EOF | k apply -f -
 ---
 apiVersion: v1
 kind: PersistentVolumeClaim
 metadata:
   name: grafana-pvc
 spec:
   accessModes:
     - ReadWriteOnce
   resources:
     requests:
       storage: 1Gi
 ---
 apiVersion: apps/v1
 kind: Deployment
 metadata:
   labels:
     app: grafana
   name: grafana
 spec:
   selector:
     matchLabels:
       app: grafana
   template:
     metadata:
       labels:
         app: grafana
     spec:
       securityContext:
         fsGroup: 472
         supplementalGroups:
           - 0
       containers:
         - name: grafana
           image: grafana/grafana:7.5.2
           imagePullPolicy: IfNotPresent
           ports:
             - containerPort: 3000
               name: http-grafana
               protocol: TCP
           readinessProbe:
             failureThreshold: 3
             httpGet:
               path: /robots.txt
               port: 3000
               scheme: HTTP
             initialDelaySeconds: 10
             periodSeconds: 30
             successThreshold: 1
             timeoutSeconds: 2
           livenessProbe:
             failureThreshold: 3
             initialDelaySeconds: 30
             periodSeconds: 10
             successThreshold: 1
             tcpSocket:
               port: 3000
             timeoutSeconds: 1
           resources:
             requests:
               cpu: 250m
               memory: 750Mi
           volumeMounts:
             - mountPath: /var/lib/grafana
               name: grafana-pv
       volumes:
         - name: grafana-pv
           persistentVolumeClaim:
             claimName: grafana-pvc
 ---
 apiVersion: v1
 kind: Service
 metadata:
   name: grafana
 spec:
   ports:
     - port: 3000
       protocol: TCP
       targetPort: http-grafana
   selector:
     app: grafana
   sessionAffinity: None
   type: LoadBalancer
 EOF

https://grafana.com/docs/grafana/latest/installation/kubernetes/

설치는 위링크를 참조하고 grafana svc type 만 LoadBalancer 로 변경한다.

k get svc
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP                                                                   PORT(S)          AGE
grafana      LoadBalancer   172.20.237.228   af7fa7486f6eb4ad4a6bde897210f4a9-206885623.ap-northeast-2.elb.amazonaws.com   3000:32317/TCP   32m

그라파나의 서비스가 다만들어지면 URL로 접근이 가능하다.

패스워드는 admin / admin 이다.

로그인후 할일은 data source 를 지정하는것이다. 우리는 prometheus 를 이용할것이다.

서비스이름/네임스페이스/svc:port 로 지정한다.

save & test 눌러서 잘되는지 확인하자.

그리고 dashboard를 import 하자.

https://grafana.com/grafana/dashboards/11074

많은 사람이 애용하는 dashboard를 사용할것이다. import 는 ID로 넣으면된다 이경우엔 11074 를 입력하자

VictoriaMetrics 를 프로메테우스로 지정하자. 그리고 Import 하면 대시보드가 뜬다.

대략 이런 대시보드가 자동으로 수집된다.

https://grafana.com/grafana/dashboards/13770

그라파나는 사람들이 만들어놓은 대시보드를 이용하기 쉽다.

그리고 node-exporter 로 만들어내는 매트릭리스트를 파악하여 원하는 지표를 사용할수 있다.

https://prometheus.io/docs/guides/node-exporter/

위URL을 참고해서 매트릭을 확인하여 보자.

예를 들어서 Dropped packet를 확인하려 한다면 다음 매트릭을 확인할수 있다.

읽어주셔서 감사하다!

올해의 가시다님 과의 스터디가 마무리되었다. 같이 EKS 스터디에 참여해주신분들께 감사를 드리며, 평안한 하루되시라!

Web-Performance-test

 k run test -it --rm --image=dos65/httperf --restart=Never --  httperf --server default-productpage-84356-8135672-aff6ae07c7cc.kr.lb.naverncp.com --port 9080 --uri /productpage?u=normal --rate 100 --num-conn 1000 --num-call 10 --timeout 10
If you don't see a command prompt, try pressing enter.
Maximum connect burst length: 1

Total: connections 1000 requests 3319 replies 2766 test-duration 37.384 s

Connection rate: 26.7 conn/s (37.4 ms/conn, <=1000 concurrent connections)
Connection time [ms]: min 10017.5 avg 27192.2 max 34318.0 median 28058.5 stddev 4668.2
Connection time [ms]: connect 497.2
Connection length [replies/conn]: 8.563

Request rate: 88.8 req/s (11.3 ms/req)
Request size [B]: 138.0

Reply rate [replies/s]: min 7.2 avg 61.3 max 150.2 stddev 44.8 (7 samples)
Reply time [ms]: response 2895.1 transfer 0.3
Reply size [B]: header 250.0 content 4474.0 footer 0.0 (total 4724.0)
Reply status: 1xx=0 2xx=2766 3xx=0 4xx=0 5xx=0

CPU time [s]: user 17.72 system 19.55 (user 47.4% system 52.3% total 99.7%)
Net I/O: 353.3 KB/s (2.9*10^6 bps)

Errors: total 737 client-timo 737 socket-timo 0 connrefused 0 connreset 0
Errors: fd-unavail 0 addrunavail 0 ftab-full 0 other 0
pod "test" deleted

httperf 를 이용한 부하테스트

AKOS-Study-Manual-EKS-istio

클러스터를 먼저 프로비저닝 했다. 30분이상이 걸리는 작업이므로 시작해놓고 기다린다.

eksctl create cluster --vpc-public-subnets $WKSubnets --name $CLUSTER_NAME --region $AWS_REGION --version 1.21 \
> --nodegroup-name $CLUSTER_NAME-nodegroup --node-type t3.medium --nodes 3 --nodes-min 3 --nodes-max 6 \
> --with-oidc --node-volume-size=20 --ssh-access --ssh-public-key $MySSHKeypair
2021-09-04 11:29:11 [ℹ]  eksctl version 0.63.0
2021-09-04 11:29:11 [ℹ]  using region ap-northeast-2
2021-09-04 11:29:12 [✔]  using existing VPC (vpc-094808933b68add7c) and subnets (private:map[] public:map[ap-northeast-2a:{subnet-0a603a222db0cce10 ap-northeast-2a 10.0.11.0/24} ap-northeast-2b:{subnet-007964ce4a003361a ap-northeast-2b 10.0.12.0/24} ap-northeast-2c:{subnet-007813cf58631ef3b ap-northeast-2c 10.0.13.0/24}])
2021-09-04 11:29:12 [!]  custom VPC/subnets will be used; if resulting cluster doesn't function as expected, make sure to review the configuration of VPC/subnets
2021-09-04 11:29:12 [ℹ]  nodegroup "first-eks-nodegroup" will use "" [AmazonLinux2/1.21]
2021-09-04 11:29:12 [ℹ]  using EC2 key pair %!q(*string=<nil>)
2021-09-04 11:29:12 [ℹ]  using Kubernetes version 1.21
2021-09-04 11:29:12 [ℹ]  creating EKS cluster "first-eks" in "ap-northeast-2" region with managed nodes
2021-09-04 11:29:12 [ℹ]  will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
2021-09-04 11:29:12 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-2 --cluster=first-eks'
2021-09-04 11:29:12 [ℹ]  CloudWatch logging will not be enabled for cluster "first-eks" in "ap-northeast-2"
2021-09-04 11:29:12 [ℹ]  you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=ap-northeast-2 --cluster=first-eks'
2021-09-04 11:29:12 [ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "first-eks" in "ap-northeast-2"
2021-09-04 11:29:12 [ℹ]  2 sequential tasks: { create cluster control plane "first-eks", 3 sequential sub-tasks: { 4 sequential sub-tasks: { wait for control plane to become ready, associate IAM OIDC provider, 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/aws-node", create serviceaccount "kube-system/aws-node" }, restart daemonset "kube-system/aws-node" }, 1 task: { create addons }, create managed nodegroup "first-eks-nodegroup" } }
2021-09-04 11:29:12 [ℹ]  building cluster stack "eksctl-first-eks-cluster"
2021-09-04 11:29:12 [ℹ]  deploying stack "eksctl-first-eks-cluster"
2021-09-04 11:29:42 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:30:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:31:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:32:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:33:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:34:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:35:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:36:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:37:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:38:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:39:12 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:40:13 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:41:13 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"
2021-09-04 11:45:14 [ℹ]  building iamserviceaccount stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-09-04 11:45:14 [ℹ]  deploying stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"

EKS를 Setup 하는 과정에 대해선 이전포스팅을 참고하기 바란다.

간단한 실습이 있지만..음 istio는 못참지.

가즈아!

먼저 istioctl을 설치하자.

curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.10.4 TARGET_ARCH=x86_64 sh -
tree istio-1.10.4/ -L 2
mv istio-1.10.4/bin/istioctl /usr/local/bin/istioctl
istioctl version

버전과 상황에 따라 설치 방법이 다를 수 있다.

istioctl install --set profile=demo -y

demo로 프로파일을 설정하게되면 istio에서 사용하는 모든 오브젝트를 설치해준다. 그러므로 만약 프로덕션에서 사용한다면 원하는 오브젝트만 따로 설치하자.

nginx pod에 istio inject 명령어로 yaml 에 istio를 주입하면 이렇게 된다.

#전
apiVersion: v1
kind: Pod
metadata:
  name: pod1
spec:
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
#후    
istioctl kube-inject -f pod1.yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    kubectl.kubernetes.io/default-container: nginx
    kubectl.kubernetes.io/default-logs-container: nginx
    prometheus.io/path: /stats/prometheus
    prometheus.io/port: "15020"
    prometheus.io/scrape: "true"
    sidecar.istio.io/status: '{"initContainers":["istio-init"],"containers":["istio-proxy"],"volumes":["istio-envoy","istio-data","istio-podinfo","istio-token","istiod-ca-cert"],"imagePullSecrets":null}'
  creationTimestamp: null
  labels:
    istio.io/rev: default
    security.istio.io/tlsMode: istio
    service.istio.io/canonical-name: pod1
    service.istio.io/canonical-revision: latest
  name: pod1
spec:
  containers:
  - image: nginx
    name: nginx
    ports:
    - containerPort: 80
    resources: {}
  - args:
    - proxy
    - sidecar
    - --domain
    - $(POD_NAMESPACE).svc.cluster.local
    - --serviceCluster
    - pod1.default
    - --proxyLogLevel=warning
    - --proxyComponentLogLevel=misc:error
    - --log_output_level=default:info
    - --concurrency
    - "2"
    env:
    - name: JWT_POLICY
      value: third-party-jwt
    - name: PILOT_CERT_PROVIDER
      value: istiod
    - name: CA_ADDR
      value: istiod.istio-system.svc:15012
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: INSTANCE_IP
      valueFrom:
        fieldRef:
          fieldPath: status.podIP
    - name: SERVICE_ACCOUNT
      valueFrom:
        fieldRef:
          fieldPath: spec.serviceAccountName
    - name: HOST_IP
      valueFrom:
        fieldRef:
          fieldPath: status.hostIP
    - name: CANONICAL_SERVICE
      valueFrom:
        fieldRef:
          fieldPath: metadata.labels['service.istio.io/canonical-name']
    - name: CANONICAL_REVISION
      valueFrom:
        fieldRef:
          fieldPath: metadata.labels['service.istio.io/canonical-revision']
    - name: PROXY_CONFIG
      value: |
        {}
    - name: ISTIO_META_POD_PORTS
      value: |-
        [
            {"containerPort":80}
        ]
    - name: ISTIO_META_APP_CONTAINERS
      value: nginx
    - name: ISTIO_META_CLUSTER_ID
      value: Kubernetes
    - name: ISTIO_META_INTERCEPTION_MODE
      value: REDIRECT
    - name: ISTIO_META_WORKLOAD_NAME
      value: pod1
    - name: ISTIO_META_OWNER
      value: kubernetes://apis/v1/namespaces/default/pods/pod1
    - name: ISTIO_META_MESH_ID
      value: cluster.local
    - name: TRUST_DOMAIN
      value: cluster.local
    image: docker.io/istio/proxyv2:1.10.4
    name: istio-proxy
    ports:
    - containerPort: 15090
      name: http-envoy-prom
      protocol: TCP
    readinessProbe:
      failureThreshold: 30
      httpGet:
        path: /healthz/ready
        port: 15021
      initialDelaySeconds: 1
      periodSeconds: 2
      timeoutSeconds: 3
    resources:
      limits:
        cpu: "2"
        memory: 1Gi
      requests:
        cpu: 10m
        memory: 40Mi
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        drop:
        - ALL
      privileged: false
      readOnlyRootFilesystem: true
      runAsGroup: 1337
      runAsNonRoot: true
      runAsUser: 1337
    volumeMounts:
    - mountPath: /var/run/secrets/istio
      name: istiod-ca-cert
    - mountPath: /var/lib/istio/data
      name: istio-data
    - mountPath: /etc/istio/proxy
      name: istio-envoy
    - mountPath: /var/run/secrets/tokens
      name: istio-token
    - mountPath: /etc/istio/pod
      name: istio-podinfo
  initContainers:
  - args:
    - istio-iptables
    - -p
    - "15001"
    - -z
    - "15006"
    - -u
    - "1337"
    - -m
    - REDIRECT
    - -i
    - '*'
    - -x
    - ""
    - -b
    - '*'
    - -d
    - 15090,15021,15020
    image: docker.io/istio/proxyv2:1.10.4
    name: istio-init
    resources:
      limits:
        cpu: "2"
        memory: 1Gi
      requests:
        cpu: 10m
        memory: 40Mi
    securityContext:
      allowPrivilegeEscalation: false
      capabilities:
        add:
        - NET_ADMIN
        - NET_RAW
        drop:
        - ALL
      privileged: false
      readOnlyRootFilesystem: false
      runAsGroup: 0
      runAsNonRoot: false
      runAsUser: 0
  volumes:
  - emptyDir:
      medium: Memory
    name: istio-envoy
  - emptyDir: {}
    name: istio-data
  - downwardAPI:
      items:
      - fieldRef:
          fieldPath: metadata.labels
        path: labels
      - fieldRef:
          fieldPath: metadata.annotations
        path: annotations
      - path: cpu-limit
        resourceFieldRef:
          containerName: istio-proxy
          divisor: 1m
          resource: limits.cpu
      - path: cpu-request
        resourceFieldRef:
          containerName: istio-proxy
          divisor: 1m
          resource: requests.cpu
    name: istio-podinfo
  - name: istio-token
    projected:
      sources:
      - serviceAccountToken:
          audience: istio-ca
          expirationSeconds: 43200
          path: istio-token
  - configMap:
      name: istio-ca-root-cert
    name: istiod-ca-cert
status: {}
---

istio의 sidecar가 nginx pod에 삽입되게 된다.

      limits:
        cpu: "2"
        memory: 1Gi
      requests:
        cpu: 10m
        memory: 40Mi

사용하는 자원의 제한은 위와같다. istio-init(initcontainer) proxy(envoy) 가 추가된다.

kubectl label namespace default istio-injection=enabled
namespace/default labeled
kubectl get ns -L istio-injection
NAME              STATUS   AGE   ISTIO-INJECTION
default           Active   46m   enabled

namespace 에 라벨을 붙이면 자동으로 그뒤론 NS 에 sidecar가 붙게된다.

k run nginx-istio --image=nginx --restart=Never
pod/nginx-istio created
k get pod
NAME          READY   STATUS            RESTARTS   AGE
nginx-istio   0/2     PodInitializing   0          4s
pod1          2/2     Running           0          5m11s

이제 sidecar를 본격적으로 확인해보자.

kubectl apply -f istio-1.10.4/samples/addons

아까 다운로드한 istio 에서 샘플로제공된 애드온을 설치한다. 위와같은 명령어를 치면 모든 애드온이 설치된다. 애드온내부에 있는 특정 애드온만도 설치가능하니 필요하면 특정 애드온만 설치해도 된다.

kiali.yaml 를 설치할때 kind 에 MonitoringDashboard 가 있어야 설치가 되는데 처음에 한꺼번에 다 배포를 하면 실패한다 그럼 그냥 쿨하게 명령어 한번더 입력해주자.

이제 애드온으로 접근하기위해선 애드온의 서비스를 퍼블릭하게 변경해줘야하는데, 나는 이전에는 yaml를 손수 수정했는데 이부분이 싱크빅하다.

k get svc -n istio-system grafana -o yaml | sed -e "s/type: ClusterIP/type: LoadBalancer/" | kubectl apply -f -
service/grafana configured
k get svc -n istio-system kiali -o yaml | sed -e "s/type: ClusterIP/type: LoadBalancer/" | kubectl apply -f -
service/kiali configured
k get svc -n istio-system tracing -o yaml | sed -e "s/type: ClusterIP/type: LoadBalancer/" | kubectl apply -f -
service/tracing configured

sed 로 수정해서 바로 적용한다. 와우..당연히 내가 못하는건 아닌데 관념의 차이로 인하여 이런 사용을 생각못했다. 다음엔 써먹어야지

ubectl get svc -n istio-system
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP                                                                    PORT(S)                                                AGE
grafana                LoadBalancer   172.20.162.75   a6d32baedc66b4633bb7fbb0875c6132-465014933.ap-northeast-2.elb.amazonaws.com    3000:30572/TCP                                                3m51s
istio-egressgateway    ClusterIP      172.20.129.21   <none>                                                                         80/TCP,443/TCP                                                21m
istio-ingressgateway   LoadBalancer   172.20.95.93    a0e6177dd9cb64884bd2893028c04328-781274984.ap-northeast-2.elb.amazonaws.com    15021:31227/TCP,80:30590/TCP,443:32395/TCP,31400:32264/TCP,15443:32750/TCP   21m
istiod                 ClusterIP      172.20.90.49    <none>                                                                         15010/TCP,15012/TCP,443/TCP,15014/TCP                                        21m
jaeger-collector       ClusterIP      172.20.99.248   <none>                                                                         14268/TCP,14250/TCP                                                3m51s
kiali                  LoadBalancer   172.20.96.205   a313dbdb158064d578d88c0a022bc845-1007771282.ap-northeast-2.elb.amazonaws.com   20001:30296/TCP,9090:30713/TCP                                               3m51s
prometheus             ClusterIP      172.20.50.6     <none>                                                                         9090/TCP                                                3m50s
tracing                LoadBalancer   172.20.58.118   a9da5b64099ed4fd3b5abdf3b1cd9ebe-68617878.ap-northeast-2.elb.amazonaws.com     80:30295/TCP                                                3m51s
zipkin                 ClusterIP      172.20.76.230   <none>                                                                         9411/TCP                                                3m51s

샘플 manifest 중에 bookinfo 가 있다.

샘플에서 보여주는것은 트래픽이 어떻게 흐르는지 시각화로 보여주는것이다.

문제가 생길경우 다음과같이 UI 와 로깅으로 확인이 가능하다.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: reviews
spec:
  hosts:
    - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v2
      weight: 50
    - destination:
        host: reviews
        subset: v3
      weight: 50

위의 에러는 기본적인 destination rule 을 설정하지 않은 상태로 review 에 대한 룰을 설정해서 그렇다.

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: productpage
spec:
  host: productpage
  subsets:
  - name: v1
    labels:
      version: v1
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: reviews
spec:
  host: reviews
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v3
    labels:
      version: v3
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: ratings
spec:
  host: ratings
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
  - name: v2-mysql
    labels:
      version: v2-mysql
  - name: v2-mysql-vm
    labels:
      version: v2-mysql-vm
---
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: details
spec:
  host: details
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
---

destination rule 을 설정하고 보면 reviews rule 이 정상적으로 작동하는것을 알수있다.

적용전
적용후

가중치에 의하여 v2/v3로만 라우팅 되는것을 확인할수 있다.

istio는 조만간 블로그에 적용후에 더 자세히 다뤄보도록 하겠다.

좋은 주말되시라!

NKS-Linuxer-Blog-trouble-shooting-lifecycle-not-working

블로그를 이전한지 얼마안됬기 때문에 집중모니터링 기간이다.
먼저 자원부터 본다.

k top node
NAME                  CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%   
nks-pool-1119-w-gzi   223m         5%     1265Mi          16%       
nks-pool-1119-w-kvi   172m         4%     1540Mi          20%       

k top pod
NAME                                             CPU(cores)   MEMORY(bytes)   
php-fpm-nginx-deployment-6bc7b6df77-fbdx9        9m           138Mi           
storage-nfs-client-provisioner-5b88c7c55-dvtlj   2m           8Mi             

자원은 얼마안쓰지만..혹시나 사용량이 늘어날까봐 scale을 늘렸다.

k scale deployment php-fpm-nginx-deployment --replicas=3
deployment.apps/php-fpm-nginx-deployment scaled

그리고 pod 를 확인했는데...

k get pod
NAME                                             READY   STATUS              RESTARTS   AGE
php-fpm-nginx-deployment-6bc7b6df77-bpf2g        2/2     Running             0          19s
php-fpm-nginx-deployment-6bc7b6df77-fbdx9        2/2     Running             3          32h
php-fpm-nginx-deployment-6bc7b6df77-rfpb2        0/2     ContainerCreating   0          19s
storage-nfs-client-provisioner-5b88c7c55-dvtlj   1/1     Running             0          10h

생성단계에서 멈춘 pod 가 있었다. 상태를 확인해보니

Events:
  Type     Reason               Age        From                          Message
  ----     ------               ----       ----                          -------
  Normal   Scheduled            <unknown>  default-scheduler             Successfully assigned default/php-fpm-nginx-deployment-6bc7b6df77-rfpb2 to nks-pool-1119-w-gzi
  Normal   Pulled               28s        kubelet, nks-pool-1119-w-gzi  Container image "linuxer-regi.kr.ncr.ntruss.com/php-fpm:12" already present on machine
  Normal   Created              28s        kubelet, nks-pool-1119-w-gzi  Created container php-fpm
  Normal   Started              28s        kubelet, nks-pool-1119-w-gzi  Started container php-fpm
  Normal   Pulled               28s        kubelet, nks-pool-1119-w-gzi  Container image "nginx:1.21" already present on machine
  Normal   Created              28s        kubelet, nks-pool-1119-w-gzi  Created container nginx
  Normal   Started              28s        kubelet, nks-pool-1119-w-gzi  Started container nginx
  Warning  FailedPostStartHook  28s        kubelet, nks-pool-1119-w-gzi  Exec lifecycle hook ([/bin/sh -c chmod 777 /run/php-fpm.sock]) for Container "nginx" in Pod "php-fpm-nginx-deployment-6bc7b6df77-rfpb2_default(6978da29-8045-49a0-9745-6be3cc48c364)" failed - error: command '/bin/sh -c chmod 777 /run/php-fpm.sock' exited with 1: chmod: cannot access '/run/php-fpm.sock': No such file or directory

Exec lifecycle hook ([/bin/sh -c chmod 777 /run/php-fpm.sock]) for Container "nginx" in Pod "php-fpm-nginx-deployment-6bc7b6df77-rfpb2_default(6978da29-8045-49a0-9745-6be3cc48c364)" failed - error: command '/bin/sh -c chmod 777 /run/php-fpm.sock' exited with 1: chmod: cannot access '/run/php-fpm.sock': No such file or directory

lifecycle hook 이 정상동작하지 않았다. 음...이벤트상으론 컨테이너가 생성전에 hook이 동작한건데 이건좀 확인해봐야겠다.

조금있다가 컨테이너가 자동으로 재시작되며 Runing 상태로 변경됬다.

k logs php-fpm-nginx-deployment-6bc7b6df77-rfpb2 -c nginx 
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: can not modify /etc/nginx/conf.d/default.conf (read-only file system?)
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2021/09/02 00:30:55 [notice] 1#1: using the "epoll" event method
2021/09/02 00:30:55 [notice] 1#1: nginx/1.21.1
2021/09/02 00:30:55 [notice] 1#1: built by gcc 8.3.0 (Debian 8.3.0-6) 
2021/09/02 00:30:55 [notice] 1#1: OS: Linux 5.4.8-050408-generic
2021/09/02 00:30:55 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2021/09/02 00:30:55 [notice] 1#1: start worker processes
2021/09/02 00:30:55 [notice] 1#1: start worker process 22
2021/09/02 00:30:55 [notice] 1#1: start worker process 23
2021/09/02 00:30:55 [notice] 1#1: start worker process 24
2021/09/02 00:30:55 [notice] 1#1: start worker process 25

logs 도 정상...음...일단 로깅을 모아보고 생각해야겠다.

NKS-Linuxer-Blog-Rebuilding

블로그를 새로 만들기로 했다.

https://linuxer.name/2020/02/aws-linuxer의-블로그-톺아보기

2020년 2월에 완성된 블로그의 구조이니..이걸 우려먹은지도 벌써 1년이 훌쩍넘어다는 이야기다. 블로그를 좀더 가볍고 편한구조로 변경하려고 고민했으나..나는 실패했다.ㅠㅠ

능력이나 뭐 그런 이야기가 아니라..게으름에 진거다. 게으름에 이기기 위해서 글을 시작했다.

목적은 K8S 에 새로 만들기고, K8S의 특성을 가져가고 싶었다.

제일먼저 작업한것은 Wordpess 의 근간이 되는 PHP 다.

PHP는 도커파일을 먼저 작성했다.

FROM php:7.4-fpm

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
                           libpng-dev \
                           libzip-dev \
                           libicu-dev \
                           libzip4 \
        && pecl install xdebug \
        && docker-php-ext-install opcache \
    && docker-php-ext-enable xdebug \
        && docker-php-ext-install pdo_mysql \
        && docker-php-ext-install exif \
        && docker-php-ext-install zip \
        && docker-php-ext-install gd \
        && docker-php-ext-install intl \
        && docker-php-ext-install mysqli

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

WORKDIR /srv/app
RUN cp /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini
RUN echo "date.timezone=Asia/Seoul" >> /usr/local/etc/php/php.ini
RUN sed -i --follow-symlinks 's|127.0.0.1:9000|/run/php-fpm.sock|g' /usr/local/etc/php-fpm.d/www.conf
RUN sed -i --follow-symlinks 's|short_open_tag = Off|short_open_tag = On|g' /usr/local/etc/php/php.ini
RUN sed -i --follow-symlinks 's|9000|/run/php-fpm.sock|g' /usr/local/etc/php-fpm.d/zz-docker.conf
CMD ["php-fpm"]

몇가지 수정사항이 있었는데 먼저 tcp socket를 사용하지 않고, unix socket을 사용했다. 흔하게 file socket이라고도 하는데 nginx <-> php-fpm 의 socket 통신의 속도가 상승한다. nginx와 php-fpm이 같은 서버내에 있을때 사용할수 있는 방법이다.
또 zz-docker.conf 는 php 이미지에서 ext를 설치할때 docker 패키지를 사용하면설치되는데 이 conf파일안에 unix 소켓을 사용할수 없도록 만드는 설정이 있다.

[global]
daemonize = no

[www]
listen = 9000

위설정이 바로 그 설정이다 listen = 9000 이 fix로 박히게 되는데 이걸 수정해주지 않으면 www.conf를 아무리 수정해도 unix socket을 사용할수 없다. 변경하고 빌드는 정상적으로 됬다.

빌드후 push는 NCP 의 Container Registry 서비스를 이용했다. docker login 할때 sub account 의 access key 와 secret key를 생성해서 사용했다.

docker build -t linuxer-cr/php-fpm:12 ./
docker push linuxer-cr/php-fpm:12

12번에 걸쳐서 빌드 테스트를 진행했다. centos 이미지였다면 쉬웠을껀데ㅠㅠ그냥 있는 이미지 써본다고 고생했다. 빌드가 완료된 php-fpm을 deployment 로 배포했다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: php-fpm-nginx-deployment
spec:
  selector:
    matchLabels:
      app: php-fpm-nginx
  template:
    metadata:
      labels:
        app: php-fpm-nginx
    spec:
      imagePullSecrets:
      - name: regcred
      containers:
      - name: php-fpm
        image: linuxer-rc/php-fpm:12
        volumeMounts:
        - name: vol-sock
          mountPath: /run
        - name: www
          mountPath: /usr/share/nginx/html
      - name: nginx
        image: nginx:1.21
        lifecycle:
          postStart:
            exec:
              command: ["/bin/sh", "-c", "chmod 777 /run/php-fpm.sock"]
        volumeMounts:
        - name: vol-sock
          mountPath: /run
        - name: nginx-config-volume
          mountPath: /etc/nginx/conf.d/default.conf
          subPath: default.conf
        - name: www
          mountPath: /usr/share/nginx/html

      volumes:
      - name: vol-sock
        emptyDir:
          medium: Memory
      - name: nginx-config-volume
        configMap:
          name: nginx-config
      - name: html
        emptyDir: {}
      - name: www
        persistentVolumeClaim:
          claimName: nfs-pvc

위의 manifest 는 완성된 버전이다. 특이한 부분을 말하자면 몇가지가 있는데,

첫번째로 nignx pod 와 php-fpm container의 unix socket 을 공유하는 부분이다.
emptyDir: medium:Memory 로 지정하면 메모리를 emptydir 로 사용한다 원래 컨셉은 shm 을 hostpath로 이용하여 마운트해서 사용하려했는데 편리한 방법으로 지원해서 사용해봤다. 일반 디스크에 unix socket를 사용하는것보다 속도가 빠를것이라 예상한다
벤치를 돌려보기엔 너무 귀찮았다.

두번째로 lifecycle: postStart다. nginx 프로세스가 시작하면서 소켓을 생성하기에 권한부족으로 정상적으로 php-fpm과 통신이 되지 않았다. 그래서 lifecycle hook을 이용하여 컨테이너가 모두 생성된 이후에 cmd 를 실행하도록 설정하였다.

세번째로 여러개의 파드에서 같은 데이터를 써야하므로 고민을 했다.
NFS-Server pod 를 생성하여 내부에서 NFS-Server를 이용한 데이터를 공유하느냐, 아니면 NAS서비스를 이용하여 NFS Client provisioner 를 이용할것인가. 고민은 금방 끝났다.
편한거 쓰자! NAS를 사용했다.

NAS 서비스를 확인하고,

#프로비저너 설치
helm --kubeconfig=$KUBE_CONFIG install storage stable/nfs-client-provisioner --set nfs.server=169.254.82.85 --set nfs.path=/n2638326_222222

#프로비저너 설치확인
k get pod storage-nfs-client-provisioner-5b88c7c55-dvtlj 
NAME                                             READY   STATUS    RESTARTS   AGE
storage-nfs-client-provisioner-5b88c7c55-dvtlj   1/1     Running   0          33m

#nfs-pvc 설치
cat << EOF | k apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nfs-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
  storageClassName: nfs-client
EOF

볼륨까지 프로비저닝했다.

그리고 네번째 nginx-config 다 configmap 으로 만들어져서 /etc/nginx/conf.d/default.conf 경로에 subpath 로 파일로 마운트된다.

cat << EOF | k apply -f -
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-config
data:
  default.conf: |
    server {
    root   /usr/share/nginx/html;
    listen       80;
    server_name  _;

    #access_log  /var/log/nginx/host.access.log  main;

    location / {
    index index.php;
    try_files \$uri \$uri/ /index.php?\$args;
    }

    #error_page  404              /404.html;

    # redirect server error pages to the static page /50x.html
    #
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }

    location ~ [^/]\.php(/|$) {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        fastcgi_pass unix:/run/php-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param REQUEST_METHOD \$request_method;
        fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
    }
    }
EOF

나는 대다수의 manifest를 shell 에서 그대로 적용해버리기때문에 $request_method 이런 변수가 있는 부분은 \$request_method 역슬러시를 넣어서 평문 처리를해줬다.

이 nginx conf configmaps 에서 특이점은 try_files \$uri \$uri/ /index.php?\$args; 부분이다. 이부분이 빠지면 wordpress 의 주소형식을 사용할수 없어 페이지 이동이 되지 않는다.

이제 이 모든과정이 php-fpm-nginx-deployment 를 정상적으로 동작하게 하기위한 과정이었다.

이제 데이터를 AWS 에 있는 EC2 에서 가져왔다.

그냥 귀찮아서 bastion host에서 rsync 로 sync 했다.

#NFS mount
mount -t nfs nasserverip/마운트정보 /mnt
#pod 가 마운트된 pvc로 다이렉트로 sync
rsync root@aws-ec2-ip:/wordpressdir /mnt/default-nfs-pvc-pvc-d04852d6-b138-40be-8fc3-150894a3daac

이렇게 하니 단순 expose 만으로도 1차적으로 사이트가 떴다.

NPLB(Network Proxy Load Balancer) -> nginx-php-fpm POD -> AWS RDS

이런구성으로 돌고있었기에 DB를 옮겨왔다.

#mysqldump
mysqldump -h rdsendpoint -u linxuer -p linuxer_blog > linuxerblog.sql
#sync
rsync root@aws-ec2-ip:/linuxerblog.sql /home/

테스트용도로 사용할 CDB

mysql -h cdb-endpoint -u -p linuxer_blog < linuxerblog.sql

디비 복구후 wp-config 에서 define('DB_HOST') 를 CDB로 변경했다. 기나긴 트러블 슈팅의 기간이 끝나가고 있었다.

잘될줄 알았는데, 그건 저 혼자만의 생각이었습니다.

처음부터 SSL은 절대 처리하지 않을것이라 생각했건만...이렇게 된거 Let's encrypt로 간다!

#certbot install
yum install certbot certbot-plagin-route53

#route53이용한 인증
certbot certonly \
  --dns-route53 \
  -d linuxer.name \
  -d *.linuxer.name

인증서에 root ca 가 포함되어있지 않기 때문에 root ca를 서버의 번들 인증서에서 삽입해 줘야한다.

openssl pkcs7 -inform der -in dstrootcax3.p7c -out dstrootcax3.pem -print_certs
cp fullchain.pem fullca.pem
cat dstrootcax3.pem >> fullca.pem
openssl verify -CAfile fullca.pem cert.pem
cert.pem: OK

이렇게 하면 이제 private key, public key, root ca chain 해서 Certificate Manager에 인증서가 등록이 가능하다. 여기에 잘 등록하면,

이렇게 인증서를 등록할수 있다. 인증서의 발급기관은 R3로 뜬다.

이제 드디어 ingress 를 만들 준비가 되었다. ingress 를 만들기 위해 먼저 svc가 필요하다.

k expose deployment php-fpm-nginx-deployment --type=NodePort --port=80 --target-port=80 --name=php-fpm-nginx-deployment

k get svc
NAME                           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
php-fpm-nginx-deployment-svc   NodePort    198.19.196.141   <none>        80:30051/TCP   24h

정상적으로 만들어 진게 확인되면,

cat << EOF | k apply -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80},{"HTTPS":443}]'
    alb.ingress.kubernetes.io/ssl-certificate-no: "----"
    alb.ingress.kubernetes.io/actions.ssl-redirect: |
      {"type":"redirection","redirection":{"port": "443","protocol":"HTTPS","statusCode":301}}
  labels:
    linuxer: blog
  name: slinuxer-blog-ingress
spec:
  backend:
    serviceName: php-fpm-nginx-deployment-svc
    servicePort: 80
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: ssl-redirect
          servicePort: use-annotation
      - path: /*
        backend:
          serviceName: my-service
          servicePort: 80
EOF

대망의 ingress다. ALB 컨트롤러를 이용해 ingress를 생성하고 컨트롤한다.alb.ingress.kubernetes.io/ssl-certificate-no: "----" 이부분은 Resource Manager에서 NRN을 확인하자.

이후 내블로그를 NKS로 완벽하게 이전을 마치고 앞으로의 K8S의 테스트 환경이 될 모르모트로 완성되었다.

이이후 Route53에서 DNS를 돌리고 자원을 하나씩 중지했다.

입사후 긴시간 동안 마음만 먹었던 프로젝트를 끝내서 너무 속이 시원하다.

이제 NKS위에서 전보다 나은 퍼포먼스를 보여줄 LINUXER BLOG를 응원해 주시라!

즐거운 밤이 되시길 빈다.

AKOS-Study-Manual-EKS-Setup

두번째로 진행한 가시다님과의 스터디! AKOS다.

가시다님은 편한 스터디를 위해서 항상 CloudFromation 을 제공해주시지만 청개구리인 나는 사실 그대로 따라해본적은 없다. 이번에도 그렇다. 먼저 VPC와 bastion-hsot를 생성하는 cloudformation 템플릿을 주셨지만 어찌..엔지니어가 된자의 도리로 그대로 따라만 하겠는가..

라고 생각하여 일단 새로 생성하는것이 아닌..내가 사용하던 EC2에 셋팅을 했다.

미리 사용하던 VPC가 있었기에 VPC도 그대로 쓴다.

aws cli,eksctl,kubectl,
실습에서 사용하는 postgresql cmd docker 등 실습에 필요한 것들을 설치했다.
해당 부분은 cloudformation 템플릿에 user-data 로 셋팅하는 부분을 참고했다.

bastion-host를 셋팅후 bastion-host에 이미 ssm 을 사용하기 위해 추가했던 역할에 실습에 사용하기위한 권한을 을 부여했다.

실습이 끝나면 administratoraccess 권한을 제거한다.

EC2에 부여된 역할도 확인했다.

나는 SSM을 사용하기 때문에 SSH로 인스턴스를 접근하지 않는다. 실습중에 SSH에 접근하기 위해 보안그룹을 여는과정이 있었는데, 그부분은 스킵한다.

이런식으로 웹에서 콘솔을 사용할수 있다. SSH 와는 다르다 SSH와는..

자쿠와는 다르다! 자쿠와는!"은 무슨 뜻일까?

키페어를 생성하는 중에 추가된게 보였다 pem 말고 이젠 ppk 방식의 키도 제공한다..
이전까지는 puttygen으로 제너레이터 하던부분이 개선된거 같다. 개꿀팁...와...

이제 EKS 구성!!

클러스터 구성은 다음과 같은 명령어를 사용한다.

eksctl create cluster --vpc-public-subnets $WKSubnets --name $CLUSTER_NAME --region $AWS_REGION --version 1.21 \
 --nodegroup-name $CLUSTER_NAME-nodegroup --node-type t3.medium --nodes 3 --nodes-min 3 --nodes-max 6 \
 --with-oidc --node-volume-size=20 --ssh-access --ssh-public-key $MySSHKeypair --dry-run

근데 또 이대로 생성은 못한다. 이유는 나는..수동으로 하기때문.......가자 매뉴얼의 길!!

echo "export WKVPC=`aws ec2 describe-vpcs --filters Name=tag:Name,Values=Linuxer-Blog-VPC | jq -r '.Vpcs[].VpcId'`" >> ~/.bashrc
echo $WKVPC
vpc-094808933b68add7c
echo "export AWS_REGION=$AWS_REGION" >> ~/.bashrc
echo "export CLUSTER_NAME=first-eks" >> ~/.bashrc
echo "export WKSubnets=subnet-0a603a222db0cce10,subnet-007964ce4a003361a,subnet-007813cf58631ef3b" >> ~/.bashrc
echo "export MySSHKeypair=eks-test-key" >> ~/.bashrc
source ~/.bashrc

그리고 클러스터 생성을 dry-run 으로 하면

ksctl create cluster --vpc-public-subnets $WKSubnets --name $CLUSTER_NAME --region $AWS_REGION --version 1.21 \
>  --nodegroup-name $CLUSTER_NAME-nodegroup --node-type t3.medium --nodes 3 --nodes-min 3 --nodes-max 6 \
>  --with-oidc --node-volume-size=20 --ssh-access --ssh-public-key $MySSHKeypair --dry-run
apiVersion: eksctl.io/v1alpha5
availabilityZones:
- ap-northeast-2c
- ap-northeast-2b
- ap-northeast-2a
cloudWatch:
  clusterLogging: {}
iam:
  vpcResourceControllerPolicy: true
  withOIDC: true
kind: ClusterConfig
managedNodeGroups:
- amiFamily: AmazonLinux2
  desiredCapacity: 3
  disableIMDSv1: false
  disablePodIMDS: false
  iam:
    withAddonPolicies:
      albIngress: false
      appMesh: false
      appMeshPreview: false
      autoScaler: false
      certManager: false
      cloudWatch: false
      ebs: false
      efs: false
      externalDNS: false
      fsx: false
      imageBuilder: false
      xRay: false
  instanceSelector: {}
  instanceType: t3.medium
  labels:
    alpha.eksctl.io/cluster-name: first-eks
    alpha.eksctl.io/nodegroup-name: first-eks-nodegroup
  maxSize: 6
  minSize: 3
  name: first-eks-nodegroup
  privateNetworking: false
  releaseVersion: ""
  securityGroups:
    withLocal: null
    withShared: null
  ssh:
    allow: true
    publicKeyPath: eks-test-key
  tags:
    alpha.eksctl.io/nodegroup-name: first-eks-nodegroup
    alpha.eksctl.io/nodegroup-type: managed
  volumeIOPS: 3000
  volumeSize: 20
  volumeThroughput: 125
  volumeType: gp3
metadata:
  name: first-eks
  region: ap-northeast-2
  version: "1.21"
privateCluster:
  enabled: false
vpc:
  autoAllocateIPv6: false
  cidr: 10.0.0.0/16
  clusterEndpoints:
    privateAccess: false
    publicAccess: true
  id: vpc-094808933b68add7c
  manageSharedNodeSecurityGroupRules: true
  nat:
    gateway: Disable
  subnets:
    public:
      ap-northeast-2a:
        az: ap-northeast-2a
        cidr: 10.0.11.0/24
        id: subnet-0a603a222db0cce10
      ap-northeast-2b:
        az: ap-northeast-2b
        cidr: 10.0.12.0/24
        id: subnet-007964ce4a003361a
      ap-northeast-2c:
        az: ap-northeast-2c
        cidr: 10.0.13.0/24
        id: subnet-007813cf58631ef3b

잘 동작한다.

eksctl create cluster --vpc-public-subnets $WKSubnets --name $CLUSTER_NAME --region $AWS_REGION --version 1.21 \
>  --nodegroup-name $CLUSTER_NAME-nodegroup --node-type t3.medium --nodes 3 --nodes-min 3 --nodes-max 6 \
>  --with-oidc --node-volume-size=20 --ssh-access --ssh-public-key $MySSHKeypair
2021-08-28 12:09:47 [ℹ]  eksctl version 0.63.0
2021-08-28 12:09:47 [ℹ]  using region ap-northeast-2
2021-08-28 12:09:47 [✔]  using existing VPC (vpc-094808933b68add7c) and subnets (private:map[] public:map[ap-northeast-2a:{subnet-0a603a222db0cce10 ap-northeast-2a 10.0.11.0/24} ap-northeast-2b:{subnet-007964ce4a003361a ap-northeast-2b 10.0.12.0/24} ap-northeast-2c:{subnet-007813cf58631ef3b ap-northeast-2c 10.0.13.0/24}])
2021-08-28 12:09:47 [!]  custom VPC/subnets will be used; if resulting cluster doesn't function as expected, make sure to review the configuration of VPC/subnets
2021-08-28 12:09:47 [ℹ]  nodegroup "first-eks-nodegroup" will use "" [AmazonLinux2/1.21]
2021-08-28 12:09:47 [ℹ]  using EC2 key pair %!q(*string=<nil>)
2021-08-28 12:09:47 [ℹ]  using Kubernetes version 1.21
2021-08-28 12:09:47 [ℹ]  creating EKS cluster "first-eks" in "ap-northeast-2" region with managed nodes
2021-08-28 12:09:47 [ℹ]  will create 2 separate CloudFormation stacks for cluster itself and the initial managed nodegroup
2021-08-28 12:09:47 [ℹ]  if you encounter any issues, check CloudFormation console or try 'eksctl utils describe-stacks --region=ap-northeast-2 --cluster=first-eks'
2021-08-28 12:09:47 [ℹ]  CloudWatch logging will not be enabled for cluster "first-eks" in "ap-northeast-2"
2021-08-28 12:09:47 [ℹ]  you can enable it with 'eksctl utils update-cluster-logging --enable-types={SPECIFY-YOUR-LOG-TYPES-HERE (e.g. all)} --region=ap-northeast-2 --cluster=first-eks'
2021-08-28 12:09:47 [ℹ]  Kubernetes API endpoint access will use default of {publicAccess=true, privateAccess=false} for cluster "first-eks" in "ap-northeast-2"
2021-08-28 12:09:47 [ℹ]  2 sequential tasks: { create cluster control plane "first-eks", 3 sequential sub-tasks: { 4 sequential sub-tasks: { wait for control plane to become ready, associate IAM OIDC provider, 2 sequential sub-tasks: { create IAM role for serviceaccount "kube-system/aws-node", create serviceaccount "kube-system/aws-node" }, restart daemonset "kube-system/aws-node" }, 1 task: { create addons }, create managed nodegroup "first-eks-nodegroup" } }
2021-08-28 12:09:47 [ℹ]  building cluster stack "eksctl-first-eks-cluster"
2021-08-28 12:09:48 [ℹ]  deploying stack "eksctl-first-eks-cluster"
2021-08-28 12:10:18 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-cluster"

eksctl 도 내부적으론 cloudformation 으로 동작하는것을 알수있다.
eksctl 을 안쓰고 수동으로 node 만들고 EKS-master 에 연결하고 했다면 아마 좀힘들지 않았을까..? 하고 생각하는 중에 에러가 발생했다.

021-08-28 12:30:36 [✖]  unexpected status "ROLLBACK_IN_PROGRESS" while waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 12:30:36 [ℹ]  fetching stack events in attempt to troubleshoot the root cause of the failure
2021-08-28 12:30:36 [!]  AWS::IAM::Role/NodeInstanceRole: DELETE_IN_PROGRESS
2021-08-28 12:30:36 [!]  AWS::EC2::LaunchTemplate/LaunchTemplate: DELETE_IN_PROGRESS
2021-08-28 12:30:36 [!]  AWS::EKS::Nodegroup/ManagedNodeGroup: DELETE_IN_PROGRESS
2021-08-28 12:30:36 [✖]  AWS::EKS::Nodegroup/ManagedNodeGroup: CREATE_FAILED – "Nodegroup first-eks-nodegroup failed to stabilize: [{Code: Ec2SubnetInvalidConfiguration,Message: One or more Amazon EC2 Subnets of [subnet-007964ce4a003361a, subnet-0a603a222db0cce10, subnet-007813cf58631ef3b] for node group first-eks-nodegroup does not automatically assign public IPaddresses to instances launched into it. If you want your instances to be assigned a public IP address, then you need to enable auto-assign public IP address for the subnet. See IP addressing in VPC guide: https://docs.aws.amazon.com/vpc/latest/userguide/vpc-ip-addressing.html#subnet-public-ip,ResourceIds: [subnet-007964ce4a003361a, subnet-0a603a222db0cce10, subnet-007813cf58631ef3b]}]"
2021-08-28 12:30:36 [!]  1 error(s) occurred and cluster hasn't been created properly, you may wish to check CloudFormation console
2021-08-28 12:30:36 [ℹ]  to cleanup resources, run 'eksctl delete cluster --region=ap-northeast-2 --name=first-eks'
2021-08-28 12:30:36 [✖]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup": ResourceNotReady: failed waiting for successful resource state
Error: failed to create cluster "first-eks"

으아니.....서브넷에서 Public IP를 자동으로 붙이도록 해야한다니ㅠㅠ AWS양반 이게 무슨말이요....

나는 기본으로 끄도록 설정한다. 켜주고..

또 보니까 eks 를 생성명령어 치고 에러가 발생하면

eksctl delete cluster --region=ap-northeast-2 --name=first-eks
2021-08-28 12:35:42 [ℹ]  eksctl version 0.63.0
2021-08-28 12:35:42 [ℹ]  using region ap-northeast-2
2021-08-28 12:35:42 [ℹ]  deleting EKS cluster "first-eks"
2021-08-28 12:35:42 [ℹ]  deleted 0 Fargate profile(s)
2021-08-28 12:35:42 [✔]  kubeconfig has been updated
2021-08-28 12:35:42 [ℹ]  cleaning up AWS load balancers created by Kubernetes objects of Kind Service or Ingress
2021-08-28 12:35:43 [!]  retryable error (Throttling: Rate exceeded
        status code: 400, request id: 3c52ae23-c941-435c-82ef-4df78b0f706e) from cloudformation/DescribeStacks - will retry after delay of 5.632676043s
2021-08-28 12:35:50 [ℹ]  3 sequential tasks: { delete nodegroup "first-eks-nodegroup", 2 sequential sub-tasks: { 2 sequential sub-tasks: { delete IAM role for serviceaccount "kube-system/aws-node", delete serviceaccount "kube-system/aws-node" }, delete IAM OIDC provider }, delete cluster control plane "first-eks" [async] }
2021-08-28 12:35:50 [ℹ]  will delete stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 12:35:50 [ℹ]  waiting for stack "eksctl-first-eks-nodegroup-first-eks-nodegroup" to get deleted
2021-08-28 12:35:50 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 12:36:06 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 12:36:06 [ℹ]  will delete stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-08-28 12:36:06 [ℹ]  waiting for stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node" to get deleted
2021-08-28 12:36:06 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-08-28 12:36:24 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-08-28 12:36:24 [ℹ]  deleted serviceaccount "kube-system/aws-node"
2021-08-28 12:36:25 [ℹ]  will delete stack "eksctl-first-eks-cluster"
2021-08-28 12:36:25 [✔]  all cluster resources were deleted

자동으로 삭제하진 않고 내가 수동으로 삭제해야한다. 삭제를 하면 다시 설치!! 설치가 완료됬다.

이제 pgsql rds와 secretmanager 셋팅을 할거다.

t2.micro로 생성하고

Secrets Manager 도 생성했다.

rds endpoint 도 변수로 추가하고 보안그룹 열어주고 pgsql 유저 패스워드를 지정한다

RDSEP=eksworkdb.cnnbttekipxl.ap-northeast-2.rds.amazonaws.com
createuser -d -U eksdbadmin -P -h $RDSEP mywork
Enter password for new role:
Enter it again:
Password:
#디비 생성
PGPASSWORD=$AppDbPw createdb -U mywork -h $RDSEP -E UTF8 myworkdb

이후로는 책의 진행을 따라서 backend-app 까지 배포했다.

ubectl logs -l app=backend-app -f --max-log-requests 8
2021-08-28 14:00:12.504 [http-nio-8080-exec-7] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:00:12.507 [http-nio-8080-exec-8] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:00:42.503 [http-nio-8080-exec-3] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:00:42.505 [http-nio-8080-exec-5] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:01:12.503 [http-nio-8080-exec-5] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:01:12.503 [http-nio-8080-exec-4] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:01:42.503 [http-nio-8080-exec-1] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:01:42.505 [http-nio-8080-exec-2] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:02:12.508 [http-nio-8080-exec-8] INFO  k.s.presentation.api.HealthApi - Health GET API called.
2021-08-28 14:02:12.513 [http-nio-8080-exec-9] INFO  k.s.presentation.api.HealthApi - Health GET API called.

정상적으로 들어오는거 확인하고~ 프론트를 빌드했다.

이후에 빌드한 것을 s3 로 정적호스팅 설정하고,

OAI 설정 및 연결해주고..대부분 기본설정이다.

이전에 만든 CF를 썼는데 원본이름이 이상하다...

일단 정적페이지 까지 띄웠다.

생각보다 긴 실습이었다.

eksctl delete cluster --region=ap-northeast-2 --name=first-eks
2021-08-28 14:28:19 [ℹ]  eksctl version 0.63.0
2021-08-28 14:28:19 [ℹ]  using region ap-northeast-2
2021-08-28 14:28:19 [ℹ]  deleting EKS cluster "first-eks"
2021-08-28 14:28:20 [ℹ]  deleted 0 Fargate profile(s)
2021-08-28 14:28:20 [✔]  kubeconfig has been updated
2021-08-28 14:28:20 [ℹ]  cleaning up AWS load balancers created by Kubernetes objects of Kind Service or Ingress
2021-08-28 14:29:57 [!]  retryable error (Throttling: Rate exceeded
        status code: 400, request id: 79716aac-f9df-40d9-871c-e473113fe5c1) from cloudformation/DescribeStacks - will retry after delay of 9.550890926s
2021-08-28 14:30:06 [ℹ]  3 sequential tasks: { delete nodegroup "first-eks-nodegroup", 2 sequential sub-tasks: { 2 sequential sub-tasks: { delete IAM role for serviceaccount "kube-system/aws-node", delete serviceaccount "kube-system/aws-node" }, delete IAM OIDC provider }, delete cluster control plane "first-eks" [async] }
2021-08-28 14:30:06 [ℹ]  will delete stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:30:06 [ℹ]  waiting for stack "eksctl-first-eks-nodegroup-first-eks-nodegroup" to get deleted
2021-08-28 14:30:06 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:30:23 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:30:40 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:30:59 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:31:16 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:31:36 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:31:55 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:32:14 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:32:31 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:32:49 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:33:05 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:33:21 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:33:40 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:33:56 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-nodegroup-first-eks-nodegroup"
2021-08-28 14:33:56 [ℹ]  will delete stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-08-28 14:33:56 [ℹ]  waiting for stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node" to get deleted
2021-08-28 14:33:56 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-08-28 14:34:13 [ℹ]  waiting for CloudFormation stack "eksctl-first-eks-addon-iamserviceaccount-kube-system-aws-node"
2021-08-28 14:34:13 [ℹ]  deleted serviceaccount "kube-system/aws-node"
2021-08-28 14:34:14 [ℹ]  will delete stack "eksctl-first-eks-cluster"
2021-08-28 14:34:14 [✔]  all cluster resources were deleted

클러스터를 삭제하며 포스팅을 마친다.

Certified Kubernetes Administrator-CKA-Review

CKA를 취득하기로 마음먹은지 어언 10개월.. 작년 7월부터 고민했던 종착점에 도착했다.

먼저 시험을 보기전의 나에 대해서 이야기해볼까 한다.

컨테이너는 그럭저럭 다루고, ECS기반의 아키텍처설계를 주로했다. EKS는 혼자서 사용하면서 대충~ 이야기할수있는 레벨이었다. 이직을 진행하면서 NKS에 대한 공부를 진행했고, 관리형 K8S는 어느정도 이해도가 높아졌다는 생각을 한 시점이었다.

그리고 DKOS-Docker Kubernetes online study를 진행하면서 나름의 공부를 한터라 자신이 있었다. 1차 시험에는 49점으로 탈락했다. 사실 다 풀었는데 왜이런 점수가 나왔는지 의아했다. 그래서 떨어지고나서 찾아보니..시험에서 원하는 답이 있다는 것을 알 수 있었다.

보통 나는 시험을 준비할 때 이런 프로세스를 따른다.

후기수집->언급빈도/공통키워드 분석-> 분석에 따른 시험공부->응시

그런데 이번엔 그렇지 않고 그냥 시험을 봤다. 평소와 다른 패턴으로 시험에서 원하는 답과 내가 생각하는 답의 거리. 그리고 K8S 클러스터에서 15번문제에서 작업한 것이 앞서 풀었던 문제에 영향을 끼쳤다. K8S context를 사용하는 문제들이 여러 문제였다.

명확하게 탈락 이유를 알게되고 본래 시험을 볼때 쓰던 방식을 따랐다.

경험자들의 후기를 수집했고 공통적으로 나오는 이야기가 있었다.

뭄샤드형. Mumshad Mannambeth 다. Udemy에 강의가 있다.

https://www.udemy.com/course/certified-kubernetes-administrator-with-practice-tests

원가는 좀 비싼데 유데미 특성상 시크릿모드로 여러번 반복해서 들어가면 할인된다. 나는 15000원에 구입했다.

먼저 스크립트를 보는 법을 알려준다.

CC에서 영어자막을 켜고, 그 옆에 Transcript 를 누르면 스크립트를 볼수있다.
축하한다. 이제 번역기를 쓸수있다. 강의 잘듣길 바란다.

크롬 자체의 번역기 기능을 켜면 애매한 번역일지언정 대충 알아 볼수있게 번역해준다.

그리고 여기부터 중요하다.

Udemy 강의의 24. Accessing the Labs 를 보면 kodekloud의 쿠폰을 준다.

강의+실습쿠폰이라니 이 얼마나 혜자 구성인가. kodekloud에선 Lab을 진행할수 있고 쿠버네티스를 실습하고 문제를 풀 수 있도록 환경을 제공해준다. 쿠폰으로 가입까지 마무리했다면 udemy 강의와 실습은 별개임을 알아야한다. 이론적인 지식은 별개로 채우고 실습으로 맞아가며 공부하는게 빠르다.

https://kodekloud.com/lessons/core-concepts-4/

로그인이후에 이 URL로 가면 Lab 만 진행할수 있다. 뭄샤드형의 컴퓨팅파워를 마음껏 사용하도록하자. - 좀 느린게 흠이지만 나는 너무 만족했다-

이 실습을 다 풀어보면서 시험에서 요구하는 정확한 답이 뭔지 알았다.

7월 31일 탈락 8월 7일 합격이었다. 딱 일주일만의 재 시험을 봐서 합격했다.

일주일간 뭄샤드형 실습완료- killer.sh 1번 정주행. 그리고 참고서로 쿠버네티스 완벽 가이드 책을 이용해서 애매한 개념들을 이해했다.

이제 공부하는 과정을 설명했다면 시험본 과정을 설명할까한다.

시험 준비는 먼저 구글번역/파파고번역/PSI /Innovative Exams Screensharing 이렇게 네가지의 플러그인을 설치했다. LinuxFoundation 시험은 번역기를 무료로 쓸수 있게 해준다. 감사. 매우감사.

그리고 뭄샤드형 Lab을 하면서 내가 잘못하는 도메인의 북마크(즐겨찾기)를 마구 만들어 뒀다.
- 실제 시험 들어가니까 그건 안보이고 검색해서 모두 해결했다.-

그리고 시험 15분전에 시험에 참가전 신원검사와 장소검사 등등을 하고 시험을 봤다.

시험을 시작하면 제일먼저 해야할것은

source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
alias k=kubectl
complete -F __start_kubectl k

이 네줄을 치트 시트에서 찾아서 입력하는것이다.

자동완성 없으면 진짜 손가락에 랙걸리는게 느껴진다.

내가 중점적으로 연습했던건 RBAC 였다. SA->Role->Rolebinding 과정이 번거로웠다.

그리고 나는 원래 VI를 사랑하는 유저지만 이번시험에선 VI를 봉인했다. VI로 들어가서 수정하는 과정이 매우 귀찮았다.

그래서

cat <<EOF | k apply -f -
YAML
EOF

cat <<EOF | k apply -f - enter! 다음에 원하는 YAML을 넣고 EOF하는 것이다. 메모장은 시험에서 제공된 메모장으로 모두 에디터했다.. VI로 열고 닫고 넘귀찮은것.. 물론 방법이야 있는데..

VIM 에서 :shell 혹은 :sh 혹은 Ctrl + z 를 누르면 쉘로 복귀한다. !w 하면 되는데 손이 바쁘니까 그냥 메모장서 복붙붙했다.

이런 과정을 거쳐서 시험은 2번째 시험에 합격했고, 후기를 적고있다.

대충 하고싶었던 말을 다썼고, 이제 감사할 일만 남았다.

첫번쨰로 DKOS 에 참여할수 있도록 허락해주신 가시다님.
두번째로 팁을 아낌없이 나눠주신 라온클
세번째로 영원한 동료 MVSC-manvscloud 님

글로 모두 적지는 못하지만 도움을 주신 모든 분들께 항상 감사를 드립니다.

맺음말은 같은방식으로 하겠습니다.

즐거운 저녁되시라!

K8s-one-line-Challenge

잔잔한 호수에 돌맹이는 내가던졌다.

K8s의 Service는 selector 에서 지정한 label로 pod에게 트래픽을 흘린다.

그런데 아이러니하게도 service 에서 연결된 pod를 한번에 조회할순 없다.

service 에서 selector 나 endpoint를 확인해서 labels 를 보고 확인해야 한다. 그 과정을 한번 보자.

my-service1 이라는 서비스에서 사용하는 pod를 조회할꺼다.

k get svc -o wide
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE     SELECTOR
kubernetes    ClusterIP   198.19.128.1     <none>        443/TCP        2d13h   <none>
my-service1   NodePort    198.19.231.233   <none>        80:30001/TCP   2d12h   app=my-nginx1
my-service2   NodePort    198.19.172.176   <none>        80:30002/TCP   2d12h   app=my-nginx2
my-service3   NodePort    198.19.200.20    <none>        80:30003/TCP   2d12h   app=my-nginx3

k get pods -l app=my-nginx1 --show-labels 
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
my-nginx1-67f499d79c-g7vr7   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running   1          26h   app=my-nginx1,pod-template-hash=67f499d79c

kubectl. 에서 svc 를 get하고 -o wide 명령어를 쓰면 selector 가보인다. 거기서 get pod -l app=my-nginx1 이라 일일이 지정해줘야지만 확인할수 있다. 명령어 두줄치면 되긴한데 귀찮다. 이렇게 된이상 한줄치기는 물러설수 없다.

미끼를 물어주신 iamai 님께 감사를 드린다. - 재차 감사! - 예아!

kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" |awk -F":" '{print $1}' |grep -f - <(kubectl get po -o wide)
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          26h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          26h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          26h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>

endpoint 에서 조회된 IP를 awk 로 떼어서 한줄씩으로 변환후 포트를 제거한다. 그리고 pod list 에서 IP가 grep 된 줄만 출력한다.

[root@linuxer-bastion ~]# kubectl get endpoints
NAME          ENDPOINTS                                         AGE
kubernetes    10.0.12.10:6443,10.0.12.11:6443,10.0.12.16:6443   2d13h
my-service1   198.18.0.74:80,198.18.1.208:80,198.18.2.250:80    2d13h
my-service2   198.18.0.173:80,198.18.1.155:80,198.18.2.120:80   2d13h
my-service3   198.18.0.6:80,198.18.1.139:80,198.18.2.70:80      2d13h
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 
my-service1   198.18.0.74:80,198.18.1.208:80,198.18.2.250:80    2d13h
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'
198.18.0.74:80,198.18.1.208:80,198.18.2.250:80
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" 
198.18.0.74:80
198.18.1.208:80
198.18.2.250:80
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" |awk -F":" '{print $1}' 
198.18.0.74
198.18.1.208
198.18.2.250
[root@linuxer-bastion ~]# kubectl get endpoints |grep my-service1 |awk '{print $2}'|tr "," "\n" |awk -F":" '{print $1}' |grep -f - <(kubectl get po -o wide)
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          26h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          26h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          26h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>
[root@linuxer-bastion ~]# kubectl get po -o wide
NAME                         READY   STATUS     RESTARTS   AGE   IP             NODE                  NOMINATED NODE   READINESS GATES
busybox                      0/1     Init:0/2   0          26h   198.18.2.60    nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          26h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          26h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          26h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>
my-nginx2-659945d9d8-2sggt   1/1     Running    1          26h   198.18.1.155   nks-pool-1119-w-gzi   <none>           <none>
my-nginx2-659945d9d8-cjkft   1/1     Running    0          26h   198.18.2.120   nks-pool-1119-w-gzh   <none>           <none>
my-nginx2-659945d9d8-szw59   1/1     Running    0          26h   198.18.0.173   nks-pool-1119-w-gzg   <none>           <none>
my-nginx3-694994cd8c-l4m6k   1/1     Running    1          26h   198.18.1.139   nks-pool-1119-w-gzi   <none>           <none>
my-nginx3-694994cd8c-lbqsd   1/1     Running    0          26h   198.18.2.70    nks-pool-1119-w-gzh   <none>           <none>
my-nginx3-694994cd8c-xjzkc   1/1     Running    0          26h   198.18.0.6     nks-pool-1119-w-gzg   <none>           <none>
nginx                        1/1     Running    0          26h   198.18.0.80    nks-pool-1119-w-gzg   <none>           <none>

이해를 돕기위해 결과를 한줄씩 쳐서 출력했다.

iamai 님의 shell에 대한 이해도를 볼수있었다.

나는 grep를 쓰지않고 출력하고 싶었다. 여러 엔지니어들을 보면 jsonpath로 예쁘게 깍는것이 부러웠다. 방법은 다양했다 json | jq 부터 jsonpath custom-columns 까지 방법이 많은데 나도 한번 써볼까 싶었다.

k get pod -l $(k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' | sed 's/:/=/' | tr -s '[[:space:]]' ' ') --show-labels
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
my-nginx1-67f499d79c-g7vr7   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running   1          26h   app=my-nginx1,pod-template-hash=67f499d79c

시작부터 iamai 님과의 다른접근을 볼수있다. 나는 label로 접근했고, iamai 님은 IP로 접근했다.

selector 는 label을 기반으로 pod와 매핑되기때문에 IP가 우선이 되서는 안된다 생각했다. IP는 고정된 값이 아니므로. 그래서 label 을 사용하기로 생각했다.

[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' 
map[app:my-nginx1]
[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' 
[app:my-nginx1]
[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' | sed 's/:/=/' 
[app=my-nginx1]
[root@linuxer-bastion ~]# k get svc my-service1 -o=jsonpath='{..selector}' | sed 's/map//' | sed 's/:/=/' | tr -s '[[:space:]]' ' '
 app=my-nginx1

먼저 jsonpath를 이용하여 service의 selector를 찾는다 여기서 sed 명령어로 map[app:my-nginx1] 이라는 문자열을 두번 파이프라인하여 [app=my-nginx1]로 변환된다. json으로 출력한 문자열은 = -> : 으로 치환되어 표기된다. 그래서 변경해줘야 했다. 괄호를 벗겼다. 괄호를 벗은 값은 내가 처음부터 원했던 service - selector - label 이다. 이제 이값을 이용해서 pod 를 리스팅 하고 label를 보면 완성이다.

k get pod -l $(k get svc my-service1 -o=jsonpath='{..selector}' /| sed 's/map//' | sed 's/:/=/' | tr -s '[[:space:]]' ' ') --show-labels
NAME                         READY   STATUS    RESTARTS   AGE   LABELS
my-nginx1-67f499d79c-g7vr7   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running   0          26h   app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running   1          26h   app=my-nginx1,pod-template-hash=67f499d79c

내가 작성한 스크립트는 폰트크기 15다

k get ep -o custom-columns=IP:.subsets[].addresses[].ip
IP
10.0.12.10
198.18.0.74
198.18.0.173
198.18.0.6

성주님께서 주신 IP 추출 팁

오랜만에 머리를 굴렸더니 재미있었다.

오늘도 같이 머리를 싸매서 고민을 해주신 봄님, iamai 님, 성주님께 감사를 드린다.

더좋은 아이디어나 생각이 있다면 얼른 결과를 공유해주시길 바란다!

즐거운 새벽되시라!

하고 누우려는데 성주님께서 주신 IP list 로 하나더 만들고 싶었다.

k get ep my-service1 -o custom-columns=IP:.subsets[].addresses[*].ip | tr "," "\n" | grep -v IP | grep -f - <(kubectl get po -o wide --show-labels) 
my-nginx1-67f499d79c-g7vr7   1/1     Running    0          27h   198.18.0.74    nks-pool-1119-w-gzg   <none>           <none>            app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-j4f9k   1/1     Running    0          27h   198.18.2.250   nks-pool-1119-w-gzh   <none>           <none>            app=my-nginx1,pod-template-hash=67f499d79c
my-nginx1-67f499d79c-mqxzs   1/1     Running    1          27h   198.18.1.208   nks-pool-1119-w-gzi   <none>           <none>            app=my-nginx1,pod-template-hash=67f499d79c

성주님+iamai님의 조언을 합쳤다 중간에 grep -v IP 는 내 생각이다.

시원하게 끝내고 잔다!

정말로 좋은새벽되시라.

Ping-MTU-test

MTU 9000 이상을 점보프레임이라 부른다.

점포프레임이 정상적으로 전송되는지 확인하는 방법이다.

ping -M do -s 1472 google.com
PING google.com (172.217.175.110) 1472(1500) bytes of data.
76 bytes from nrt20s21-in-f14.1e100.net (172.217.175.110): icmp_seq=1 ttl=114 (truncated)
76 bytes from nrt20s21-in-f14.1e100.net (172.217.175.110): icmp_seq=2 ttl=114 (truncated)
76 bytes from nrt20s21-in-f14.1e100.net (172.217.175.110): icmp_seq=3 ttl=114 (truncated)

1500에 맞춰서 google로 보내면 정상적으로 간다.

ping -M do -s 1473 google.com
PING google.com (172.217.175.110) 1473(1501) bytes of data.
^C
--- google.com ping statistics ---
45 packets transmitted, 0 received, 100% packet loss, time 43999ms

1501 은 가지 않는다.

대부분의 클라우드 내부의 이더넷은 점보프레임이 설정되어 있으며 9000이상이다.

ifconfig | grep -i MTU | grep eth
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 8950

외부망으론 1500까지만 전송됨을 확인하였으니 내부망에서 MTU를 확인한다.

ping -M do -s 8922 10.0.12.13
PING 10.0.12.13 (10.0.12.13) 8922(8950) bytes of data.
8930 bytes from 10.0.12.13: icmp_seq=1 ttl=64 time=0.491 ms
8930 bytes from 10.0.12.13: icmp_seq=2 ttl=64 time=0.431 ms
8930 bytes from 10.0.12.13: icmp_seq=3 ttl=64 time=0.483 ms

잘된다.

인터페이스에 지정된 MTU는 8950이다.
ping에선 IP Header(20 Bytes) + ICMP Header(8 Bytes) 28 Bytes 를 뺀 숫자가 ICMP Data 크기이다. 그래서 Ping 로 MTU 테스트할땐 실제 MTU-28을 하여 테스트하면된다.