반응형

Ingress Controller 를 활용한 웹서비스에 TLS 를 적용하는 방법 중 가장 쉬운 방법은 Ingress Controller 에 TLS 를 적용하는 방법입니다. 이 방법은 client 와 Ingress Controller 까지 https 로 통신하고 Ingress Controller 와 web server 와는 http 로 통신하게 됩니다.

아래의 방법은 Ingress Controller 는 미리 설치되어 있다고 가정합니다.

 

먼저, letsencrypt 로 접속하여 3개월간 무료로 인증서를 받는 방법을 알아봅니다.

 

1. 필요 패키지 설치 (Ubuntu 16.04 기준)

# apt-get update
# apt-get install software-properties-common
# add-apt-repository universe
# sudo add-apt-repository ppa:certbot/certbot
# sudo apt-get update
# sudo apt-get install certbot python-certbot-nginx

 

2. 인증서 다운로드

중요한 것은 인터넷에서 해당 도메인으로 로컬 서버에 접속이 되어야 하며 (Inbound 가능) standalone 을 사용할 때 로컬에 80 포트는 unbind 되어 있어야 합니다. 아래와 같은 경우는 외부에서 cloudnativeday.kr, www.cloudnativeday.kr, test.cloudnativeday.kr 도메인이 갖는 ip address 가 내 로컬 서버여야 한다는 의미입니다. 정확히는 해당 ip address 로 외부에서 내 서버로 연결이 가능해야 합니다.

# certbot certonly --standalone --cert-name cloudnativeday.kr -d cloudnativeday.kr,www.cloudnativeday.kr,test.cloudnativeday.kr

 

인증서 발급이 완료되면 다음과 같은 디렉토리에 인증서가 생성되어 보입니다. archive 디렉토리와 심볼릭 링크가 있는 live 디렉토리가 있는데 live 디렉토리를 활용합니다. 그리고, crt 는 fullchain.pem 을, key 는 private.pem 을 사용합니다.

 

# ls -al /etc/letsencrypt/archive/cloudnativeday.kr/
total 24
drwxr-xr-x 2 root root 4096 Apr  7 21:31 .
drwx------ 3 root root 4096 Apr  7 21:31 ..
-rw-r--r-- 1 root root 1931 Apr  7 21:31 cert1.pem
-rw-r--r-- 1 root root 1647 Apr  7 21:31 chain1.pem
-rw-r--r-- 1 root root 3578 Apr  7 21:31 fullchain1.pem
-rw------- 1 root root 1704 Apr  7 21:31 privkey1.pem


# ls -al /etc/letsencrypt/live/cloudnativeday.kr/
total 12
drwxr-xr-x 2 root root 4096 Apr  7 21:31 .
drwx------ 3 root root 4096 Apr  7 21:31 ..
lrwxrwxrwx 1 root root   41 Apr  7 21:31 cert.pem -> ../../archive/cloudnativeday.kr/cert1.pem
lrwxrwxrwx 1 root root   42 Apr  7 21:31 chain.pem -> ../../archive/cloudnativeday.kr/chain1.pem
lrwxrwxrwx 1 root root   46 Apr  7 21:31 fullchain.pem -> ../../archive/cloudnativeday.kr/fullchain1.pem
lrwxrwxrwx 1 root root   44 Apr  7 21:31 privkey.pem -> ../../archive/cloudnativeday.kr/privkey1.pem
-rw-r--r-- 1 root root  692 Apr  7 21:31 README

 

 

인증서를 성공적으로 생성하고 나면, Ingress Controller 에서 사용하기위해서 인증서를 secret tls 타입으로 생성합니다.

 

3. secret tls 생성

# kubectl create secret tls cloudnativeday-certs --key /etc/letsencrypt/live/cloudnativeday.kr/privkey.pem --cert /etc/letsencrypt/live/cloudnativeday.kr/fullchain.pem --namespace cloudnative

타입은 secret tls 로 하고, key 는 private.pem 을, cert 는  fullchain.pem 을 사용합니다.

 

 

4. tls ingress 생성

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: cloudnative-kr
  namespace: cloudnative
spec:
  tls:
  - secretName: cloudnativeday-certs
    hosts:
    - cloudnativeday.kr
    - www.cloudnativeday.kr
  rules:
  - host: cloudnativeday.kr
    http:
      paths:
      - backend:
          serviceName: nginx-cloudnative
          servicePort: 80
        path: /
  - host: www.cloudnativeday.kr
    http:
      paths:
      - backend:
          serviceName: nginx-cloudnative
          servicePort: 80
        path: /

http 로 들어오면 자동으로 https 로 redirect 됩니다. 왜냐하면 기본 값이 nginx.ingress.kubernetes.io/ssl-redirect: "true" 이기 때문입니다. 일반 http 와 다른 부분은 tls 부분입니다. secret 과 인증서가 적용될 도메인명을 적으면 됩니다.

 

반응형
Posted by seungkyua@gmail.com
,
반응형

Kubernetes 는 RBAC 이 디폴트 접근 제어 권한으로 User account 와 RBAC 을 적절히 사용하면 효과적인 접근 제어를 할 수 있습니다.

예를 들어, Kubernetes Cluster 에 접근할 수 있는 사용자 seungkyua 를 새로 만들고 해당 사용자는 seungkyua 네임스페이스에만 접근 가능하게 설정하고 싶은 경우가 있을 수 있습니다.

또한 seungkyua 네임스페이스 접근 안에서도 pods, replicasets, deployments, services 만 생성, 조회, 수정, 삭제권한만 주고 싶을 때도 있습니다.

 

이러한 세팅은 아래와 같은 절차를 거쳐야 합니다. 물론 Kubernetes 는 RBAC 사용으로 세팅되어 있다고 가정합니다.

  1. 신규 namespace 생성
  2. User account 생성 (client key 와 client crt 생성)
  3. 새로운 namespace 에 제한적으로 접근할 수 있는 권한 생성 (role 생성)
  4. 3번의 새로운 User account 와 3 번의 role 을 연결 (rolebinding 생성)
  5. kubectl 이 사용할 새로운 config context 를 만들고 연결

 

1. 신규 naemspace 생성

# kubectl create namespace seungkyua

 

2. 새로운 User account 생성

  • client private key 생성 
# openssl genrsa -out seungkyua.key 2048

 

  • crt 생성을 위한 csr 생성
# openssl req -new -key seungkyua.key -subj "/CN=seungkyua/O=Kubernetes Korea Group" -out seungkyua.csr

 

  • client cert 파일 생성
# openssl x509 -req -in seungkyua.csr -CA /etc/kubernetes/ssl/ca.crt -CAkey /etc/kubernetes/ssl/ca.key -CAcreateserial -out seungkyua.crt -days 10000

 

3. 새로운 namespace 로 접근 role 설정

# vi role.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: seungkyua-manager
  namespace: seungkyua
rules:
  - apiGroups:
      - ""            # core api
      - "extensions"
      - "apps"
    resources:
      - "deployments"
      - "replicasets"
      - "pods"
      - "services"
    verbs:
      - "get"
      - "list"
      - "watch"
      - "create"
      - "update"
      - "patch"
      - "delete"


# kubectl create -f role.yaml

 

4. User 와 Role 을 연결 (rolebinding 생성)

# vi rolebinding.yaml

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: seungkyua-manager-rolebinding
  namespace: seungkyua
subjects:
  - kind: User         # User or ServiceAccount
    name: seungkyua
roleRef:
  kind: Role
  name: seungkyua-manager
  apiGroup: rbac.authorization.k8s.io
  

# kubectl create -f rolebinding.yaml

 

5. Kubectl config context 생성

  • 인증을 위한 사용자 정보 등록 (key, crt 로 접속)
# kubectl config set-credentials seungkyua --client-certificate=/root/ahnsk/private-certs/seungkyua.crt --client-key=/root/ahnsk/private-certs/seungkyua.key --embed-certs=true

 

  • context 생성
# kubectl config get-clusters NAME cluster.local # kubectl config set-context seungkyua@k1 --cluster=cluster.local --user=seungkyua --namespace=seungkyua

 

  • kubernetes 접속
# kubectl config get-contexts
CURRENT   NAME                             CLUSTER         AUTHINFO           NAMESPACE
*         kubernetes-admin@cluster.local   cluster.local   kubernetes-admin   
          seungkyua@k1                     cluster.local   seungkyua 


# kubectl config use-context seungkyua@k1


# kubectl config get-contexts
CURRENT   NAME                             CLUSTER         AUTHINFO           NAMESPACE
          kubernetes-admin@cluster.local   cluster.local   kubernetes-admin   
*         seungkyua@k1                     cluster.local   seungkyua          seungkyua

 

Kubernetes 에 접속하여 해당 계정이 해당 네임스페이스에만 접속 권한이 있는지 확인

# kubectl get pods

No resources found.



# kubectl get pods -n defaults

Error from server (Forbidden): pods is forbidden: User "seungkyua" cannot list resource "pods" in API group "" in the namespace "defaults"

 

 

반응형
Posted by seungkyua@gmail.com
,
반응형


https://github.com/seungkyua/my-kubespray

my-kubespray

You can easily add your custom logic without modifying kubespray.

The my-kubespray has serveral parameters to override and can deploy ceph rbd provisioner into namespace.

Prerequisites

  • All servers must have the same user, and the user must set root permissions to NOPASSWD in sudo.

  • In the deployment node (or master 1 node), you need to enable ssh access to all other servers (including localhost) without a password to the corresponding account.

  • Register all cluster servers in the /etc/hosts file.

  • Time synchronization between servers with ntp.

  • Enable network restart with systemctl restart network.service .

  • Disable firewall such as selinux or ufw.

  • swapoff.

  • Install python 2.7.x in ansible runner node.

Quick Start

To deploy the cluster you can use :

# Download kubespray and my-kubespray
$ git clone https://github.com/kubernetes-sigs/kubespray.git
$ git clone https://github.com/seungkyua/my-kubespray.git

# Change directory into my-kubespray
$ cd my-kubespray

# Install dependencies from ``requirements.txt``
$ sudo pip install -r ../kubespray/requirements.txt

# Copy ``inventory/k2-seungkyua`` as ``inventory/mycluster``
$ cp -rfp inventory/k2-seungkyua inventory/mycluster

# Update Ansible inventory file
$ vi inventory/mycluster/hosts.ini
[all]
k2-master01 ip=192.168.30.151
k2-master02 ip=192.168.30.152
k2-master03 ip=192.168.30.153
k2-ctrl01 ip=192.168.30.154
k2-ctrl02 ip=192.168.30.155
k2-ctrl03 ip=192.168.30.156
k2-cn01 ip=192.168.30.157

[etcd]
k2-master01
k2-master02
k2-master03

[kube-master]
k2-master01
k2-master02
k2-master03

[kube-node]
k2-ctrl01
k2-ctrl02
k2-ctrl03
k2-cn01

[k8s-cluster:children]
kube-node
kube-master

# Review and change parameters under ``inventory/mycluster/group_vars/k8s-cluster.yml``
$ cat inventory/mycluster/group_vars/k8s-cluster.yml
populate_inventory_to_hosts_file: false
override_system_hostname: false
helm_enabled: true
etcd_memory_limit: 8192M
kubeconfig_localhost: true
kubectl_localhost: true
ipip_mode: Never
calico_ip_auto_method: "can-reach=8.8.8.8"
kubeadm_enabled: true
docker_insecure_registries:
- seungkyua:5000
dashboard_enabled: true
local_volume_provisioner_enabled: true
ingress_nginx_enabled: true
ingress_nginx_host_network: true
ingress_nginx_nodeselector:
node-role.kubernetes.io/ingress: true
ceph_version: mimic
storageclass_name: rbd
monitors: 192.168.30.23:6789,192.168.30.24:6789,192.168.30.25:6789
admin_token: QBTEBmRVVlh5UmZoTlJBQTMyZTh6Qk5uajV1VElrMDJEbWFwWmc9WA==
user_secret_namespace: default
pool_name: kubes
user_id: kube
user_token: QEFESG9CcFlpZ0o3TVJBQTV2eStjbDM5RXNLcFkzQyt0WEVHckE9WA==

# Run Ansible Playbook to deploy kubernetes cluster
$ ansible-playbook -b -f 30 -i inventory/mycluster/hosts.ini ../kubespray/cluster.yml

# Run Ansible Playbook to deploy ceph rbd provisioner
$ ansible-playbook -b -f 30 -i inventory/mycluster/hosts.ini storage.yml

Miscellaneous

After installation, you can find the artifacts which are kubectl and admin.conf in inventory/mycluster/artifacts directorin on deploy node(ansible runner).

$ ls -al inventory/mycluster/artifacts
total 173620
drwxr-x--- 2 seungkyua seungkyua     4096 Feb 22 05:25 .
drwxrwxr-x 4 seungkyua seungkyua     4096 Feb 22 02:38 ..
-rw-r----- 1 seungkyua seungkyua     5449 Feb 22 05:25 admin.conf
-rwxr-xr-x 1 seungkyua seungkyua 177766296 Feb 22 05:25 kubectl
-rwxr-xr-x 1 seungkyua seungkyua       65 Feb 22 05:25 kubectl.sh

If desired, copy admin.conf to ~/.kube/config and kubectl to /usr/local/bin/kubectl

$ mkdir -p ~/.kube
$ cp inventory/mycluster/artifacts/admin.conf ~/.kube/config
$ sudo cp inventory/mycluster/artifacts/kubectl /usr/local/bin/kubectl



반응형
Posted by seungkyua@gmail.com
,
반응형

Kubernetes Node 가 NotReady 인 경우 해결을 위해서 제일 먼저 해당 노드의 syslog 를 확인 하는 것이 좋다.


## Node 의 syslog 확인 (ubuntu 인 경우)
# vi /etc/log/syslog

Feb 20 12:14:49 k1-node01 kubelet[31100]: E0220 12:14:49.401890   31100 raw.go:146] Failed to watch directory "/sys/fs/cgroup/devices/system.slice": inotify_add_watch /sys/fs/cgroup/devices/system.slice/run-ref5f878051f440b98dfd9bd843a01b58.scope: no space left on device
Feb 20 12:14:49 k1-node01 kubelet[31100]: F0220 12:14:49.401909   31100 kubelet.go:1369] Failed to start cAdvisor inotify_add_watch /sys/fs/cgroup/devices/system.slice/run-ref5f878051f440b98dfd9bd843a01b58.scope: no space left on device
Feb 20 12:14:49 k1-node01 kubelet[31100]: E0220 12:14:49.640153   31100 kubelet.go:2266] node "k1-node01" not found
Feb 20 12:14:49 k1-node01 systemd[1]: kubelet.service: Main process exited, code=exited, status=255/n/a
Feb 20 12:14:49 k1-node01 systemd[1]: kubelet.service: Unit entered failed state.
Feb 20 12:14:49 k1-node01 systemd[1]: kubelet.service: Failed with result 'exit-code'.


위와 같이 inotify_add_watch 에서 no space left on device 로 에러가 날 경우에 sysctl 로 값을 늘려줘야 한다.

## fs.inotify.max_user_watches 값을 늘려줌
# vi /etc/sysctl.d/99-sysctl.conf

net.ipv4.ip_forward=1
net.ipv4.ip_local_reserved_ports=30000-32767
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-arptables=1
net.bridge.bridge-nf-call-ip6tables=1
fs.inotify.max_user_watches=1048576


해당 값을 적용하고 나서 kubelet 이 activating 상태이거나 failed 상태인 경우가 많으므로 아래와 같이 kubelet 을 재 시작해 준다.

## sysctl 로 적용 및 kubelet 재실행

# sysctl -p
# sudo systemctl restart kubelet 






반응형
Posted by seungkyua@gmail.com
,
반응형

kubernetes 에서 ceph rbd provisioner 활용 방법입니다.


1. 먼저 rbd provisioner 가 사용할 rbac 권한 yaml 파일을 만듭니다.
# vi rbd-rbac.yaml
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: rbd-provisioner
rules:
  - apiGroups: [""]
    resources: ["persistentvolumes"]
    verbs: ["get", "list", "watch", "create", "delete"]
  - apiGroups: [""]
    resources: ["persistentvolumeclaims"]
    verbs: ["get", "list", "watch", "update"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["events"]
    verbs: ["create", "update", "patch"]
  - apiGroups: [""]
    resources: ["services"]
    resourceNames: ["kube-dns","coredns"]
    verbs: ["list", "get"]
  - apiGroups: [""]
    resources: ["endpoints"]
    verbs: ["get", "list", "watch", "create", "update", "patch"]

---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: rbd-provisioner
subjects:
  - kind: ServiceAccount
    name: rbd-provisioner
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: rbd-provisioner
  apiGroup: rbac.authorization.k8s.io

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: rbd-provisioner
  namespace: kube-system

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: rbd-provisioner
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
- apiGroups: [""]
  resources: ["endpoints"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]

---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rbd-provisioner
  namespace: kube-system
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: rbd-provisioner
subjects:
- kind: ServiceAccount
  name: rbd-provisioner
  namespace: kube-system 

# kubectl create -f rbd-rbac.yaml 



2. kube-system 네임스페이스로 admin secret 과 user secret 을 생성한다.
# ceph auth get client.admin 2>&1 |grep "key = " |awk '{print  $3'} |xargs echo -n > /tmp/secret.admin
# kubectl create secret generic ceph-secret-admin --type=kubernetes.io/rbd --from-file=/tmp/secret.admin --namespace=kube-system

# ceph auth get-key client.kube > /tmp/secret.user
# kubectl create secret generic ceph-secret-user --type=kubernetes.io/rbd --from-file=/tmp/secret.user --namespace=kube-system 




3. rbd provisioner 가 사용할 storage class yaml 파일을 만듭니다.
pool 이름과 userId 를  정확히 입력해야 합니다.

# vi rbd-storageclass.yaml

---
apiVersion: storage.k8s.io/v1beta1
kind: StorageClass
metadata:
  name: "rbd"
  annotations:
    storageclass.beta.kubernetes.io/is-default-class: "true"
provisioner: ceph.com/rbd
reclaimPolicy: Delete
parameters:
  monitors: "192.168.30.23:6789,192.168.30.24:6789,192.168.30.25:6789"
  adminId: "admin"
  adminSecretName: "ceph-secret-admin"
  adminSecretNamespace: "kube-system"
  pool: "kubes"
  userId: "kube"
  userSecretName: "ceph-secret-user"
  userSecretNamespace: "kube-system"
  imageFormat: "2"
  imageFeatures: "layering" 


# kubectl create -f rbd-storageclass.yaml 



4. rbd provisioner 를 deployment 타입 yaml 로 생성합니다.
# vi deployment-rbd-provisioner.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rbd-provisioner
  namespace: kube-system
  labels:
    app: rbd-provisioner
    version: v2.1.1-k8s1.11
spec:
  replicas: 2
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: rbd-provisioner
      version: v2.1.1-k8s1.11
  template:
    metadata:
      labels:
        app: rbd-provisioner
        version: v2.1.1-k8s1.11
    spec:
      priorityClassName: system-cluster-critical
      serviceAccount: rbd-provisioner
      containers:
        - name: rbd-provisioner
          image: quay.io/external_storage/rbd-provisioner:v2.1.1-k8s1.11
          imagePullPolicy: IfNotPresent
          env:
            - name: PROVISIONER_NAME
              value: ceph.com/rbd
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
          command:
            - "/usr/local/bin/rbd-provisioner"
          args:
            - "-id=${POD_NAME}" 


# kubectl create -f deployment-rbd-provisioner.yaml 




5. 이제 default 네임스페이스에 테스트를 해 보겠습니다.
먼저 default 네임스페이스에 user secret 을 생성합니다. (rbd provisioner 를 사용하고자 하는 네임스페이스에는 user secret 을 만들어 줘야 합니다.)
# ceph auth get-key client.kube > /tmp/secret.user
# kubectl create secret generic ceph-secret-user --type=kubernetes.io/rbd --from-file=/tmp/secret.user --namespace=default


6. 테스트용 pod 생성.

# vi rbd-test.yaml

---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: rbd-test
  namespace: default
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: rbd
  resources:
    requests:
      storage: 1Gi

---
apiVersion: v1
kind: Pod
metadata:
  name: rbd-test
  namespace: default
spec:
  containers:
  - name: pod-test
    image: gcr.io/google_containers/busybox:1.24
    command:
    - "/bin/sh"
    args:
    - "-c"
    - "touch /mnt/SUCCESS && exit 0 || exit 1"
    volumeMounts:
    - name: pvc
      mountPath: "/mnt"
  restartPolicy: "Never"
  volumes:
  - name: pvc
    persistentVolumeClaim:
      claimName: rbd-test 



# kubectl create -f rbd-test.yaml 





반응형
Posted by seungkyua@gmail.com
,
반응형

CVE-2018-1002105: proxy request handling in kube-apiserver can leave vulnerable TCP connections




[ Enable API Aggregation ]


## kube-apiserver 에 옵션에 다음과 같이 설정

--requestheader-client-ca-file=/etc/kubernetes/ssl/front-proxy-ca.crt
--requestheader-allowed-names=front-proxy-client
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt
--proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key



[ Metrics API 서버 설치 ]

$ mkdir -p ~/ahnsk/metric-server && cd ~/ahnsk
$ cd metrics-server

$ kubectl create -f deploy/1.8+/

$ kubectl -n kube-system get pods -l k8s-app=metrics-server


## metric-server 로그 보기
kubectl -n kube-system logs $(kubectl get pods --namespace=kube-system -l k8s-app=metrics-server -o name)


## metric-server 가 hostname 을 알지 못한다는 로그가 나오면 metric-server 를 변경

$ kubectl edit deployment metrics-server -n kube-system
image: k8s.gcr.io/metrics-server-amd64:v0.3.0
command:
- /metrics-server
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP



[ 취약점 확인 ]

## 1.12.2 버전에서는 metric-server 가 node 의 stats 을 가져오고 있음
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.2", GitCommit:"17c77c7898218073f14c8d573582e8d2313dc740", GitTreeState:"clean", BuildDate:"2018-10-24T06:43:59Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"12", GitVersion:"v1.12.2", GitCommit:"17c77c7898218073f14c8d573582e8d2313dc740", GitTreeState:"clean", BuildDate:"2018-10-24T06:43:59Z", GoVersion:"go1.10.4", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
{"kind":"NodeMetricsList","apiVersion":"metrics.k8s.io/v1beta1","metadata":{"selfLink":"/apis/metrics.k8s.io/v1beta1/nodes"},"items":[{"metadata":{"name":"k1-master01","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-master01","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:09Z","window":"30s","usage":{"cpu":"425054529n","memory":"13992336Ki"}},{"metadata":{"name":"k1-master02","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-master02","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:17Z","window":"30s","usage":{"cpu":"174426537n","memory":"10275528Ki"}},{"metadata":{"name":"k1-master03","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-master03","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:15Z","window":"30s","usage":{"cpu":"322881470n","memory":"10741952Ki"}},{"metadata":{"name":"k1-node01","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-node01","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:14Z","window":"30s","usage":{"cpu":"309956501n","memory":"14327216Ki"}},{"metadata":{"name":"k1-node02","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-node02","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:12Z","window":"30s","usage":{"cpu":"363826460n","memory":"17359508Ki"}},{"metadata":{"name":"k1-node03","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-node03","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:07Z","window":"30s","usage":{"cpu":"256312214n","memory":"15248572Ki"}},{"metadata":{"name":"k1-node04","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-node04","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:13Z","window":"30s","usage":{"cpu":"124689837n","memory":"11814000Ki"}},{"metadata":{"name":"k1-node05","selfLink":"/apis/metrics.k8s.io/v1beta1/nodes/k1-node05","creationTimestamp":"2018-12-05T08:38:13Z"},"timestamp":"2018-12-05T08:37:15Z","window":"30s","usage":{"cpu":"114905912n","memory":"8340284Ki"}}]}



## 1.11.5 패치 적용된 버전에서는 metric-server 가 system:anonymous 계정으로 node 의 stats 을 가져오지 못함
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.3", GitCommit:"a4529464e4629c21224b3d52edfe0ea91b072862", GitTreeState:"clean", BuildDate:"2018-09-09T18:02:47Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.5", GitCommit:"753b2dbc622f5cc417845f0ff8a77f539a4213ea", GitTreeState:"clean", BuildDate:"2018-11-26T14:31:35Z", GoVersion:"go1.10.3", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes
{"kind":"NodeMetricsList","apiVersion":"metrics.k8s.io/v1beta1","metadata":{"selfLink":"/apis/metrics.k8s.io/v1beta1/nodes"},"items":[]}

$ kubectl logs metrics-server-695bdf6996-sg429 -n kube-system
E1205 08:14:30.157105       1 manager.go:102] unable to fully collect metrics: unable to fully scrape metrics from source kubelet_summary:node1: unable to fetch metrics from Kubelet node1 (192.168.0.93): request failed - "403 Forbidden", response:"Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=stats)"
E1205 08:15:30.148565       1 manager.go:102] unable to fully collect metrics: unable to fully scrape metrics from source kubelet_summary:node1: unable to fetch metrics from Kubelet node1 (192.168.0.93): request failed - "403 Forbidden", response:"Forbidden (user=system:anonymous, verb=get, resource=nodes, subresource=stats)"



[ work around 로 해결하는 방법 ]


1. kube-apiserver 옵션에 --anonymous-auth=false 를 넣는 방법 (default 는 true 임)

2. 아래 clusterrolebinding 의 system:basic-user 와 system:discovery 에 들어있는 system:unauthenticated Group 을 삭제

$ kubectl get clusterrolebindings system:basic-user -o yaml
kubectl get clusterrolebindings system:basic-user -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: 2018-11-21T08:44:59Z
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:basic-user
  resourceVersion: "113"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/system%3Abasic-user
  uid: bae78dd9-ed69-11e8-b322-3ca82a1c0c40
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:basic-user
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:authenticated
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:unauthenticated


그리고 clusterrole 의 system:basic-user 와 system:discovery 에 세팅된 auto repaired 를 false 로 변경


$ kubectl get clusterroles system:basic-user -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: 2018-11-21T08:44:59Z
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:basic-user
  resourceVersion: "58"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Abasic-user
  uid: baae6ff5-ed69-11e8-b322-3ca82a1c0c40
rules:
- apiGroups:
  - authorization.k8s.io
  resources:
  - selfsubjectaccessreviews
  - selfsubjectrulesreviews
  verbs:
  - create







반응형
Posted by seungkyua@gmail.com
,
반응형

일반적으로 RBAC 으로 Kubernetes Dashboard 를 설치하면 service account 의 token 을 가지고 접속해야 합니다.


admin clusterrole 을 가진 secret 을 찾아서 해당 token 으로 접속합니다.

$ kubectl -n kube-system describe secret clusterrole-aggregation-controller-token-fmnfg


Name:         clusterrole-aggregation-controller-token-fmnfg

Namespace:    kube-system

Labels:       <none>

Annotations:  kubernetes.io/service-account.name=clusterrole-aggregation-controller

              kubernetes.io/service-account.uid=ccbaf065-8f01-11e8-80c8-3ca82a1ccfd4


Type:  kubernetes.io/service-account-token


Data

====

ca.crt:     1025 bytes

namespace:  11 bytes

token:      xxxxxxxxxxxxxxxxxxxxxxxxxxxx


매번 이런 token 방식이 귀찮으면 아래와 같이 dashboard service account 에 admin clusterrole 을 지정하고 로그인 화면에서는 skip 을 클릭하여 넘어가면 됩니다.

$ kubectl get serviceaccount -n kube-system | grep kubernetes-dashboard

kubernetes-dashboard                 1         8d 


$ kubectl create clusterrolebinding kubernetes-dashboard-admin \

--clusterrole=cluster-admin \

--serviceaccount=kube-system:kubernetes-dashboard





반응형
Posted by seungkyua@gmail.com
,
반응형


Kubernetes dashboard 를 Ingress Controller 와 연결할 때 SSL 을 적용하고 싶으면 다음과 같이 Ingress 를 생성하면 된다.



Kubernetes dashboard ingress 


apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/secure-backends: "true"
ingress.kubernetes.io/ssl-passthrough: "true"
nginx.org/ssl-backend: "kubernetes-dashboard"
kubernetes.io/ingress.allow-http: "false"
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-body-size: "100M"
name: kubernetes-dashboard
namespace: kube-system
spec:
tls:
- secretName: kubernetes-dashboard-certs
rules:
- host: dashboard.k8s.stage
http:
paths:
- path: /
backend:
serviceName: kubernetes-dashboard
servicePort: 443



반응형
Posted by seungkyua@gmail.com
,
반응형

Kubernetes pause infra container 는 기존의 golang 에서 c 로 변경되었다.


main 함수를 보면 다음과 같다.

int main(int argc, char **argv) {
int i;
for (i = 1; i < argc; ++i) {
if (!strcasecmp(argv[i], "-v")) {
printf("pause.c %s\n", VERSION_STRING(VERSION));
return 0;
}
}

if (getpid() != 1)
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, "Warning: pause should be the first process\n");

if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
.sa_flags = SA_NOCLDSTOP},
NULL) < 0)
return 3;

for (;;)
pause();
fprintf(stderr, "Error: infinite loop terminated\n");
return 42;
}


pause 는 infra container 이므로 getpid() 가 1 인 최상의 프로세스가 되는 것이 Security 측면에서 바람직하다.


sigaction 은 signal 이 SIGINT (키보드 ctrl + c 로 종료), SIGTERM (종료) 가 들어오면 sigdown 함수를 handler 로 등록해서 수행한다.

자식 프로세스가 종료되거나 정지/재시작 될 때 SIGCHLD signal 이 발생하는데, SA_NOCLDSTOP flag 는 자식 프로세스가 정지되는 4가지의 signal - SIGSTOP(프로세스 정지), SIGTSTP(키보드 ctrl + z 로 발생한 프로세스 정지), SIGTTIN(백그라운드에서 제어터미널 읽기를 시도해서 정지), SIGTTOU(백그라운드에서 제어터미널 쓰기를 시도해서 정지) 등을 받아서 정지되거나 CONTINUE signal 을 받아서 재시작되어도 이를 통지 받지 않겠다는 의미이다. 즉, pause 가 부모 프로세스가 이지만 SIGCHLD signal 을 통보받을 필요가 없다고 생각하는 것이다.  하지만 그 나머지인 경우인 자식 프로세스가 종료되는 경우에는 signal 을 받을 수 밖에 없다. 이 때는 waitpid 함수를 통해서 혹시라도 자식 프로세스가 좀비가 되었을 때 좀비 프로세스를 제거할 수 있다.


sigaction 함수는 에러가 발생하면 -1 을 정상 처리되면 0을 리턴한다.


static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal");
exit(0);
}

psignal 함수는 두번째 인자로 들어온 string 을 stderr 로 출력한다.



static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0)
;
}

자식 프로세스가 왜 종료 되었는지는 관심이 없고 단순히 자식 프로세스가 종료될 때 좀비 프로세스를 막고자 한다면 waitpid 함수를 위와 같이 사용한다.


첫번재 인자로 전달된 pid 값이 -1 이면 모든 자식 프로세스가 종료될 때 까지 기다린다. 하지만 마지막 인자로 WNOHANG 이 입력되면 부모 프로세스는 기다리지 않고 바로 리턴된다. 하나의 SIGCHLD 에 대해서 while 문으로 처리한 이유는 여러 자식 프로세스가 종료될 수 있는 가능성이 있기 때문이다. 즉, SIGCHLD signal 이전에 좀비 프로세스가 있으면 그것을 처리한다. 


그럼 결과적으로는 SIGCHLD signal 이 발생할 때 pause 프로세스는 아무것도 기다리지 않고 좀비 프로세스가 있으면 처리하고 바로 리턴한다.

그리고 마지막에 pause() 함수에 의해서 잠시 정지 상태가 된다. 



* 좀비 프로세스 : 자식 프로세스가 종료되어 사용하는 리소스는 모두 해제된 상태지만, 부모 프로세스가 자식 프로세스의 종료를 확인하지 못한 상태로 커널의 프로세스 테이블에는 관리되고 있는 상태

* 고아 프로세스 : 자식 프로세스 보다 부모 프로세스가 죽었을 경우 자식 프로세스가 pid 1 인 init 프로세스에 속하게 된 경우




 



 
















반응형
Posted by seungkyua@gmail.com
,
반응형


2018년 3월 26일 CNC & Kubernetes Meetup 발표 자료


 facebook group : https://www.facebook.com/groups/k8skr/


• 일시: 2018년 3월 26일(월) 오후 7시 30분 - 9시 30분

• 장소: 역삼역 7번 출구 GS타워 12층 AWS코리아 (강남구 논현로 508)

• 세션 내용:


19:30-20:10 Kubernetes 를 이용한 Cloud native platform 개발 (어형부형)



20:10-20:50 Helm chart 를 활용한 App 배포 방법 (안승규)








반응형
Posted by seungkyua@gmail.com
,