Kubernetes Networking을 담당하는 컴포넌트는 cni plugin(weavnet, flannel 등), kube-proxy, coredns가 있다. 

먼저 CNI 플러그인에 대해서 살펴보도록 하자. 

 

CNI 플러그인

기본적으로 쿠버네티스는 kubenet 이라는 기본적인 네트워크 플러그인을 제공하지만 크로스 노드 네트워킹이나 네트워크 정책 설정과 같은 고급 기능은 구현되어 있지 않다. 따라서 Pod 네트워킹 인터페이스로 CNI 스펙을 준수하는(Kubernete Networking Model 구현하는) 네트워크 플러그인을 사용해야한다.

Kubernetes Networking Model
- Every Pod should have an IP Address
- Every Pod should be able to communicate with every other POD in the same node.
- Every Pod should be able to communicate with every other POD on other nodes without NAT.

 

CNI 플러그인의 주요 기능

1. Container Runtime must create network namespace

2. Identify network the container must attach to

3. Container Runtime to invoke Network Plugin(bridge) when container is Added

- create veth pair

- attach veth pair

- assign ip address

- bring up interface

4. Container Runtime to invoke Network Plugin(bridge) when container is Deleted

- delete veth pair

5. JSON format of the Network Configuration

 

CNI 플러그인의 주요 책임

  • Must support arguments ADD/DEL/CHECK
  • Must support parameters container id, network ns etc..
  • Must manage IP Address assignment to PODS
  • Must Retrun results in a specific format

 

참고. CNI 설정

kubelet 실행시 CNI 설정을 함께한다.

> ps -aux | grep kubelet

# kubelet.service
ExecStart=/usr/local/bin/kubelet \\
--config=/var/lib/kubelet/kubelet-config.yaml \\
--container-runtime=remote \\
--container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
--image-pull-progress-deadline=2m \\
--kubeconfig=/var/lib/kubelet/kubeconfig \\
--network-plugin=cni \\
--cni-bin-dir=/opt/cni/bin \\
--cni-conf-dir=/etc/cni/net.d \\
--register-node=true \\
--v=2

 

참고. CNI IPAM

ip 할당에 대한 관리(DHCP, host-local, ...)

cat /etc/cni/net.d/net-script.conf
{
	"cniVersion": "0.2.0",
	"name": "mynet",
	"type": “net-script",
	"bridge": "cni0",
	"isGateway": true,
	"ipMasq": true,
	"ipam": {
		"type": "host-local",
		"subnet": "10.244.0.0/16",
		"routes": [
					{ "dst": "0.0.0.0/0" }
				]
	}
}
# cat /etc/cni/net.d/10-bridge.conf
{
	"cniVersion": "0.2.0",
	"name": "mynet",
	"type": "bridge",
	"bridge": "cni0",
	"isGateway": true,
	"ipMasq": true,
	"ipam": {
		"type": "host-local",
		"subnet": "10.22.0.0/16",
		"routes": [
			{ "dst": "0.0.0.0/0" }
        ]
	}
}

Pod Networking with CNI 플러그인(flannel)

Pod 내 컨테이너 들은 가상 네트워크 인터페이스(veth)를 통해 서로 통신할 수 있고, 고유한 IP를 갖게된다. 각 Pod는 위에서 설명한 CNI로 구성된 네트워크 인터페이스를 통하여 고유한 IP 주소로 서로 통신할 수 있다. 추가로 각기 다른 노드에 존재하는 Pod들은 서로 통신하기 위해 라우터를 거처야 한다.

출처 :https://medium.com/finda-tech/kubernetes-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%95%EB%A6%AC-fccd4fd0ae6

 

Service Networking with Kube-proxy

service는 selector를 통해 전달받을 트래픽을 특정 Pod로 전달한다. Pod 네트워크와 동일하게 가상 IP 주소를 갖지만 ip addr, router로 조회할 수 없는 특징을 가지고 있다. 대신 iptable 명령을 통해 NAT 테이블을 조회해보면 관련 설정이 있음을 확인할 수 있다. 

 

쿠버네티스는 Service Networking을 위해 Kube-proxy를 이용한다. IP 네트워크(Layer 3)는 기본적으로 자신의 호스트에서 목적지를 찾지 못하면 상위 게이트웨이로 패킷을 전달하고, 라우터에 트래픽이 도달하기전에 Kube-proxy를 통해 최종 목적지를 찾는 경우가 있다.

 

Kube-proxy는 현재(2021.11.28) iptables 모드가 기본 프록시 모드로 설정되어 있어 있고, 쿠버네티스에는 데몬셋으로 배포되기 때문에 모든 노드에 존재한다. 이때 kube-proxy는 직접 proxy 역할을 수행하지 않고, 그 역할을 전부 netfilter(service ip 발견 & Pod로 전달)에게 맡긴다. kube-proxy는 단순히 iptables를 통해 netfilter의 규칙을 수정한다. 

 

--proxy-mode ProxyMode
사용할 프록시 모드: 'userspace' (이전) or 'iptables' (빠름) or 'ipvs' or 'kernelspace' (윈도우). 공백인 경우 가장 잘 사용할 수 있는 프록시(현재는 iptables)를 사용한다. iptables 프록시를 선택했지만, 시스템의 커널 또는 iptables 버전이 맞지 않으면, 항상 userspace 프록시로 변경된다.

 

참고. iptables, netfilter

- iptables : 유저 스페이스에 존재하는 인터페이스로 패킷 흐름을 제어. netfilter를 이용하여 규칙을 지정하여 패킷을 포워딩한다.

- netfilter : 커널 스페이스에 위치하여 모든 패킷의 생명주기를 관찰하고, 규칙에 매칭되는 패킷이 발생되면 정의된 액션을 수행한다.

출처 : proxying(https://medium.com/finda-tech/kubernetes-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%95%EB%A6%AC-fccd4fd0ae6)

 

출처 : Nodeport(https://medium.com/finda-tech/kubernetes-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%95%EB%A6%AC-fccd4fd0ae6)

 

sudo iptables -S or -L -t nat 명령을 통해 노드에 설정되어 있는 NAT 테이블을 조회할 수 있다.

# netfilter의 체인룰
KUBE-SVC-XXX
KUBE-SERVICES
KUBE-SEP-XXX
KUBE-POSTROUTING
KUBE-NODEPORTS
KUBE-MARK-DROP
KUBE-MARK-MASQ
DOCKER
POSTROUTING
PREROUTING
OUTPUT
KUBE-PROXY-CANARY
KUBE-KUBELET-CANRY
등
# 특정 서비스의 체인룰 조회
iptables –L –t net | grep db-service
KUBE-SVC-XA5OGUC7YRHOS3PU tcp -- anywhere 10.103.132.104 /* default/db-service: cluster IP */ tcp dpt:3306
DNAT tcp -- anywhere anywhere /* default/db-service: */ tcp to:10.244.1.2:3306
KUBE-SEP-JBWCWHHQM57V2WN7 all -- anywhere anywhere /* default/db-service: */

 

kube-proxy는 마스터 노드의 API server에서 정보를 수신하여 이러한 체인 룰들을 추가하거나 삭제한다. 이렇게 지속적으로 iptables를 업데이트하여 netfilter 규칙을 최신화하며 Service 네트워크를 관리하는 것이다.

 


Ingress

Ingress는 리버스 프록시를 통해 클러스터 내부 Service로 패킷을 포워딩 시키는 방법을 명시한다. 대표적으로 많이 사용하는 nginx ingress controller는 ingress 리소스를 읽어서 그에 맞는 리버스 프록시를 구성한다.

 

Ingress 특징

  • using a single externally accessible URL that you can configure to route to different services Based on URL path and implementing SSL security as well
  • layer 7 load balancer
  • Ingress controller(Deploy) required - NOT deployed by default

Inginx Ingress Controller(Deployment) 예시

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-ingress-controller
spec:
  replicas: 1
  selector:
    matchLabels:
      name: nginx-ingress
  template:
    metadata:
      labels:
        name: nginx-ingress
    spec:
      containers:
        - name: nginx-ginress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
      args:
        - /nginx-ingress-controler
        - --configmap=$(POD_NAMESPACE)/nginx-configuration
      env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
      ports:
        - name: http
          containerPort: 80
        - name: https
          containerPort: 443

 

Ingress Resources(Configure)

Path types

Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation. There are three supported path types:

  • ImplementationSpecific: With this path type, matching is up to the IngressClass. Implementations can treat this as a separate pathType or treat it identically to Prefix or Exact path types.
  • Exact: Matches the URL path exactly and with case sensitivity.
  • Prefix: Matches based on a URL path prefix split by /. Matching is case sensitive and done on a path element by element basis. A path element refers to the list of labels in the path split by the / separator. A request is a match for path p if every p is an element-wise prefix of p of the request path.
Note: If the last element of the path is a substring of the last element in request path, it is not a match (for example: /foo/bar matches/foo/bar/baz, but does not match /foo/barbaz).

 

Rule example 1.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-wear
spec:
  backend:
    serviceName: wear-service
    servicePort: 80

 

Rule example 2(splitting traffic by URL).

  • no host is specified. The rule applies to all inbound HTTP traffic through the IP address specified.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-wear-watch
spec:
 rules:
 - http:
     paths:
     - path: /wear
       backend:
          serviceName: wear-service
          servicePort: 80
     - path: /watch
       backend:
          serviceName: watch-service
          servicePort: 80

 

Rule example 3(spliting by hostname).

  • If a host is provided (for example, foo.bar.com), the rules apply to that host
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-wear-watch
spec:
 rules:
 - host: wear.my-online-store.com
   http:
     paths:
     - backend:
         serviceName: wear-service
         servicePort: 80
 - host: watch.my-online-store.com
   http:
     paths:
     - backend:
         serviceName: watch-service
         servicePort: 80

 

참고) rewrite-target

Without the rewrite-target option, this is what would happen:

http://<ingress-service>:<ingress-port>/watch --> http://<watch-service>:<port>/watch

http://<ingress-service>:<ingress-port>/wear --> http://<wear-service>:<port>/wear

 

참고) replace("/something(/|$)(.*)", "/$2")

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
  name: rewrite
  namespace: default
spec:
  rules:
  - host: rewrite.bar.com
    http:
      paths:
      - backend:
          serviceName: http-svc
          servicePort: 80
        path: /something(/|$)(.*)

 

Nginx Ingress Service(NodePort) 예시

apiVersion: v1
kind: Service
metadata:
  name: nginx-ingress
spec:
  type: NodePort
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  - port: 443
    targetPort: 443
    protocol: TCP
    name: https
  selector:
    name: nginx-ingress

 


CoreDNS

쿠버네티스 클러스터의 DNS 역할을 수행할 수 있는 유연하고 확장 가능한 DNS 서버이고, 서비스 디스커버리에 사용된다.

 

FQDN(Fully Qualified Domain Name)

쿠버네티스에서 도메인으로 다양한 네임스페이스의 서비스 혹은 파드와 통신하고자 할 때 사용한다.

사용방법 : {service or host name}.{namespace name}.{svc or pod}.cluster.local

Hostname Namespace Type Root IP Address
web-service apps svc cluster.local 10.107.37.188
10-244-2-5 default pod cluster.local 10.244.2.5
# Service
curl http://web-service.apps.svc.cluster.local

# POD
curl http://10-244-2-5.apps.pod.cluster.local

 

coreDNS 설정

  • /etc/coredns/Corefile
$ cat /etc/coredns/Corefile
.:53 {
	errors
	health
	kubernetes cluster.local in-addr.arpa ip6.arpa {
		pods insecure
		upstream
		fallthrough in-addr.arpa ip6.arpa
	}
	prometheus :9153
	proxy . /etc/resolv.conf
	cache 30
	reload
}

coreDNS service

  • kube-dns
pi@master:~ $ kubectl get service -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   21d
  • kubelet config for coreDNS
root@master:/var/lib/kubelet# cat config.yaml 
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    cacheTTL: 0s
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
  webhook:
    cacheAuthorizedTTL: 0s
    cacheUnauthorizedTTL: 0s
cgroupDriver: cgroupfs
clusterDNS:
- 10.96.0.10
clusterDomain: cluster.local
cpuManagerReconcilePeriod: 0s
evictionPressureTransitionPeriod: 0s
fileCheckFrequency: 0s
healthzBindAddress: 127.0.0.1
healthzPort: 10248
httpCheckFrequency: 0s
imageMinimumGCAge: 0s
kind: KubeletConfiguration
logging: {}
nodeStatusReportFrequency: 0s
nodeStatusUpdateFrequency: 0s
rotateCertificates: true
runtimeRequestTimeout: 0s
shutdownGracePeriod: 0s
shutdownGracePeriodCriticalPods: 0s
staticPodPath: /etc/kubernetes/manifests
streamingConnectionIdleTimeout: 0s
syncFrequency: 0s
volumeStatsAggPeriod: 0s
  • external DNS server

forwarded to the nameserver specified in the coredns pods and /etc/resolv.conf file is set to use the nameserver from the kubernetes(POD에서 요청한 도메인이 DNS server에서 찾을 수 없는 경우)

pi@master:~ $ cat /etc/resolv.conf
# Generated by resolvconf
nameserver 192.168.123.254
nameserver 8.8.8.8
nameserver fd51:42f8:caae:d92e::1
nameserver 61.41.153.2
nameserver 1.214.68.2

 


Quiz 1.

1. 네트워크 인터페이스/맥주소 조회

  • ifconfig -a : 전체 네트워크 인터페이스 조회
  • cat /etc/network/interfaces : 시스템의 네트워크 기본 정보 설정
  • ifconfig eth0 : 네트워크 인터페이스/맥주소 조회
  • ip link show eth0 : 네트워크 인터페이스/맥주소 조회
  • ip a | grep -B2 10.3.116.12 on controlplane

네트워크 인터페이스 : eth0, MAC address(ether) : 02:42:0a:08:0b:06

root@controlplane:~# ip a | grep -B2 10.8.11.6
4890: eth0@if4891: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default 
    link/ether 02:42:0a:08:0b:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.8.11.6/24 brd 10.8.11.255 scope global eth0

 

worker 노드의 네트워크 인터페이스 on controlplane

  • ssh node01 ifconfig eth0 로도 확인 가능!
  • arp node01
root@controlplane:~# arp node01
Address                  HWtype  HWaddress           Flags Mask            Iface
10.8.11.8                ether   02:42:0a:08:0b:07   C                     eth0

 

2. What is the interface/bridge created by Docker on this host?

  • ifconfig -a : docker0
  • ip addr
root@controlplane:~# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 02:42:8e:70:63:21 brd ff:ff:ff:ff:ff:ff
3: flannel.1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UNKNOWN mode DEFAULT group default 
    link/ether ae:8e:7a:c3:ed:cd brd ff:ff:ff:ff:ff:ff
4: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether ee:04:24:06:10:7d brd ff:ff:ff:ff:ff:ff
5: veth0cbf7e57@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue master cni0 state UP mode DEFAULT group default 
    link/ether 4e:9d:38:c6:36:3f brd ff:ff:ff:ff:ff:ff link-netnsid 2
6: vethe11f72d7@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue master cni0 state UP mode DEFAULT group default 
    link/ether 26:8a:96:40:ad:fc brd ff:ff:ff:ff:ff:ff link-netnsid 3
4890: eth0@if4891: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:0a:08:0b:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0
4892: eth1@if4893: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:ac:11:00:39 brd ff:ff:ff:ff:ff:ff link-netnsid 1

what is the state of the interface docker0

  • state DOWN
root@controlplane:~# ip link show docker0
2: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 02:42:8e:70:63:21 brd ff:ff:ff:ff:ff:f

3. What is the default gateway

  • ip route show default
root@controlplane:~# ip route show default
default via 172.17.0.1 dev eth1

4. What is the port the kube-scheduler is listening on in the controlplane node?

  • netstat -nplt | grep scheduler
  • netstat -natulp | grep kube-scheduler
root@controlplane:~# netstat -nplt
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      735/ttyd            
tcp        0      0 127.0.0.1:10257         0.0.0.0:*               LISTEN      3716/kube-controlle 
tcp        0      0 127.0.0.1:10259         0.0.0.0:*               LISTEN      3776/kube-scheduler 
tcp        0      0 127.0.0.53:53           0.0.0.0:*               LISTEN      636/systemd-resolve 
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      758/sshd            
tcp        0      0 127.0.0.1:36697         0.0.0.0:*               LISTEN      4974/kubelet        
tcp        0      0 127.0.0.11:45217        0.0.0.0:*               LISTEN      -                   
tcp        0      0 127.0.0.1:10248         0.0.0.0:*               LISTEN      4974/kubelet        
tcp        0      0 127.0.0.1:10249         0.0.0.0:*               LISTEN      6178/kube-proxy     
tcp        0      0 127.0.0.1:2379          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 10.8.11.6:2379          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 10.8.11.6:2380          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 127.0.0.1:2381          0.0.0.0:*               LISTEN      3893/etcd           
tcp6       0      0 :::10256                :::*                    LISTEN      6178/kube-proxy     
tcp6       0      0 :::22                   :::*                    LISTEN      758/sshd            
tcp6       0      0 :::8888                 :::*                    LISTEN      5342/kubectl        
tcp6       0      0 :::10250                :::*                    LISTEN      4974/kubelet        
tcp6       0      0 :::6443                 :::*                    LISTEN      4003/kube-apiserver

 

5. Notice that ETCD is listening on two ports. Which of these have more client connections established?

  • netstat -natulp | grep etcd | grep LISTEN
  • netstat -anp | grep etcd | grep 2379 | wc -l
root@controlplane:~# netstat -anp | grep etcd
tcp        0      0 127.0.0.1:2379          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 10.8.11.6:2379          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 10.8.11.6:2380          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 127.0.0.1:2381          0.0.0.0:*               LISTEN      3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35234         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:33034         127.0.0.1:2379          ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34830         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35498         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35394         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35458         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35546         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35378         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35418         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35450         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35130         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:33034         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35148         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35250         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34916         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34812         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34758         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35232         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34846         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35060         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35566         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34872         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35368         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35442         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35356         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34724         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34972         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34794         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34618         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35528         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35208         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34862         ESTABLISHED 3893/etcd           
tcp        0      0 10.8.11.6:2379          10.8.11.6:37174         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35468         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35310         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35220         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35254         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35472         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35040         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34744         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35506         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35036         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34908         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35512         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34638         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35228         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35300         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35106         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35170         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35486         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35434         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35284         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34628         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35196         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35412         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35336         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34964         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35184         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35422         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34942         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34924         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35540         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35294         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35246         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35428         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34822         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35384         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35108         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34746         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35072         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34752         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35086         ESTABLISHED 3893/etcd           
tcp        0      0 10.8.11.6:37174         10.8.11.6:2379          ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35714         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35584         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:34730         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35324         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35268         ESTABLISHED 3893/etcd           
tcp        0      0 127.0.0.1:2379          127.0.0.1:35706         ESTABLISHED 3893/etcd

 


Quiz 2.

1. Inspect the kubelet service and identify the network plugin configured for Kubernetes.

  • ps -aux | grep kubelet | grep --color network-plugin=
root@controlplane:~# ps -aux | grep kubelet | grep --color network-plugin=   
root      4819  0.0  0.0 4003604 104604 ?      Ssl  04:51   1:12 /usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --config=/var/lib/kubelet/config.yaml --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.2

 

2. What is the path configured with all binaries of CNI supported plugins?

  • The CNI binaries are located under /opt/cni/bin by default.

 

Identify which of the below plugins is not available in the list of available CNI plugins on this host?

  • Run the command: ls /opt/cni/bin and identify the one not present at that directory.
$ls /opt/cni/bin/
bandwidth  bridge  dhcp  firewall  flannel  host-device  host-local  ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tuning  vlan

 

3. What is the CNI plugin configured to be used on this kubernetes cluster?

  • ls /etc/cni/net.d/
  • 포드를 조회해보고 이름을 통해 알 수도 있음.
controlplane $ cat /etc/cni/net.d/10-weave.conflist 
{
    "cniVersion": "0.3.0",
    "name": "weave",
    "plugins": [
        {
            "name": "weave",
            "type": "weave-net",
            "hairpinMode": true
        },
        {
            "type": "portmap",
            "capabilities": {"portMappings": true},
            "snat": true
        }
    ]
}

 

4. What binary executable file will be run by kubelet after a container and its associated namespace are created.

  • Look at the type field in file /etc/cni/net.d/10-flannel.conflist. (flannel)
root@controlplane:~# cat /etc/cni/net.d/10-flannel.conflist
{
  "name": "cbr0",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "flannel",
      "delegate": {
        "hairpinMode": true,
        "isDefaultGateway": true
      }
    },
    {
      "type": "portmap",
      "capabilities": {
        "portMappings": true
      }
    }
  ]
}

 

참고) cni plugin 설치 안되었을시 Pod 에러(No Network configured)

Events:
  Type     Reason                  Age               From               Message
  ----     ------                  ----              ----               -------
  Normal   Scheduled               46s               default-scheduler  Successfully assigned default/app to node01
  Warning  FailedCreatePodSandBox  44s               kubelet, node01    Failed to create pod sandbox: rpc error: code = Unknown desc = [failed to set up sandbox container "6b3b648ea4a547e46b96ca4d23841e9acc0edc0d43f0e879af8f45ebb498c74e" network for pod "app": networkPlugin cni failed to set up pod "app_default" network: unable to allocate IP address: Post "http://127.0.0.1:6784/ip/6b3b648ea4a547e46b96ca4d23841e9acc0edc0d43f0e879af8f45ebb498c74e": dial tcp 127.0.0.1:6784: connect: connection refused, failed to clean up sandbox container "6b3b648ea4a547e46b96ca4d23841e9acc0edc0d43f0e879af8f45ebb498c74e" network for pod "app": networkPlugin cni failed to teardown pod "app_default" network: Delete "http://127.0.0.1:6784/ip/6b3b648ea4a547e46b96ca4d23841e9acc0edc0d43f0e879af8f45ebb498c74e": dial tcp 127.0.0.1:6784: connect: connection refused]

 

5. Identify the name of the bridge network/interface created by weave on each node

  • ifconfig : weave
  • ip link : weave
controlplane $ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 02:42:ac:11:00:10 brd ff:ff:ff:ff:ff:ff
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default 
    link/ether 02:42:da:b0:4f:0c brd ff:ff:ff:ff:ff:ff
6: datapath: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether fe:f9:14:7b:9e:e8 brd ff:ff:ff:ff:ff:ff
8: weave: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 06:a9:f6:0f:2d:d6 brd ff:ff:ff:ff:ff:ff
10: vethwe-datapath@vethwe-bridge: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue master datapath state UP mode DEFAULT group default 
    link/ether fa:69:cd:fe:bd:65 brd ff:ff:ff:ff:ff:ff
11: vethwe-bridge@vethwe-datapath: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue master weave state UP mode DEFAULT group default 
    link/ether ea:09:2e:7d:f1:74 brd ff:ff:ff:ff:ff:ff
12: vxlan-6784: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 65535 qdisc noqueue master datapath state UNKNOWN mode DEFAULT group default qlen 1000
    link/ether 0a:8f:c0:a8:02:9f brd ff:ff:ff:ff:ff:ff

 

6. What is the POD IP address range configured by weave?

  • ip addr show weave
controlplane $ ip addr show weave
8: weave: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1376 qdisc noqueue state UP group default qlen 1000
    link/ether 06:a9:f6:0f:2d:d6 brd ff:ff:ff:ff:ff:ff
    inet 10.32.0.1/12 brd 10.47.255.255 scope global weave
       valid_lft forever preferred_lft forever
    inet6 fe80::4a9:f6ff:fe0f:2dd6/64 scope link 
       valid_lft forever preferred_lft forever

 

7. What is the default gateway configured on the PODs scheduled on node03?

  • kubectl run busybox --imag=busybox --command sleep 1000 --dry-run=client -o yaml > pod.yaml
apiVersion: v1
kind: Pod
metadata:
  labels:
    run: busybox
  name: busybox
spec:
  nodeName: node03 # node3!
  containers:
  - command:
    - sleep
    - "1000"
    image: busybox
    name: busybox
  • kubectl exec -it busybox -- sh
  • ip route : 10.38.0.0
# ip r
default via 10.38.0.0 dev eth0
10.32.0.0/12 dev eth0 scope link src 10.38.0.1
  • ssh node03 ip route : 10.46.0.0
controlplane $ ssh node03 ip route
default via 172.17.0.1 dev ens3 
10.32.0.0/12 dev weave proto kernel scope link src 10.46.0.0 
172.17.0.0/16 dev ens3 proto kernel scope link src 172.17.0.22 
172.18.0.0/24 dev docker0 proto kernel scope link src 172.18.0.1 linkdown

 


Quiz 3.

1. What network range are the nodes in the cluster part of?

  • ip addr
controlplane $ ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 02:42:ac:11:00:0f brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.15/16 brd 172.17.255.255 scope global ens3
       valid_lft forever preferred_lft forever
    inet6 fe80::42:acff:fe11:f/64 scope link 
       valid_lft forever preferred_lft forever

2. What is the range of IP addresses configured for PODs on this cluster?

  • kubectl logs weave-net-xxx -c weave -n kube-system | grep ipalloc-range
  • weave configuration으로도 확인가능.
controlplane $ kubectl logs weave-net-4p55r -c weave -n kube-system | grep ipalloc-range
INFO: 2021/07/17 06:50:50.095797 Command line options: map[conn-limit:200 datapath:datapath db-prefix:/weavedb/weave-net docker-api: expect-npc:true http-addr:127.0.0.1:6784 ipalloc-init:consensus=1 ipalloc-range:10.32.0.0/12 metrics-addr:0.0.0.0:6782 name:06:a9:f6:0f:2d:d6 nickname:controlplane no-dns:true no-masq-local:true port:6783]

 

3. What is the IP Range configured for the services within the cluster?

  • cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep cluster-ip-range
controlplane $ cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep cluster-ip-range
    - --service-cluster-ip-range=10.96.0.0/12

 

4. What type of proxy is the kube-proxy configured to use?

  • kubectl logs kube-proxy-xxx -n kube-system
  • assuming iptables proxy
controlplane $ k logs kube-proxy-fsn4s -n kube-system
I0717 06:51:30.922706       1 node.go:136] Successfully retrieved node IP: 172.17.0.17
I0717 06:51:30.922791       1 server_others.go:111] kube-proxy node IP is an IPv4 address (172.17.0.17), assume IPv4 operation
W0717 06:51:30.977974       1 server_others.go:579] Unknown proxy mode "", assuming iptables proxy
I0717 06:51:30.978240       1 server_others.go:186] Using iptables Proxier.
I0717 06:51:30.978587       1 server.go:650] Version: v1.19.0
I0717 06:51:30.979181       1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0717 06:51:30.979710       1 config.go:315] Starting service config controller
I0717 06:51:30.979723       1 shared_informer.go:240] Waiting for caches to sync for service config
I0717 06:51:30.979739       1 config.go:224] Starting endpoint slice config controller
I0717 06:51:30.979742       1 shared_informer.go:240] Waiting for caches to sync for endpoint slice config
I0717 06:51:31.079918       1 shared_informer.go:247] Caches are synced for service config 
I0717 06:51:31.079925       1 shared_informer.go:247] Caches are synced for endpoint slice config

 

 

참고) kube-proxy as daemonset

kubectl -n kube-system get ds 을 통해 kube-proxy가 데몬셋이라는 것을 알 수 있다. 따라서 kube-proxy는 모든 노드에 걸쳐 실행되고 있다.

pi@master:~ $ kubectl -n kube-system get ds
NAME         DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
kube-proxy   4         4         4       4            4           kubernetes.io/os=linux   21d
weave-net    4         4         4       4            4           <none>                   21d

 


Quiz 4.

1. What is the IP of the CoreDNS server that should be configured on PODs to resolve services?

  • kube-dns
controlplane $ k get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   60m

 

2. Where is the configuration file located for configuring the CoreDNS service?

  • Deployment configuration : /etc/coredns/Corefile
controlplane $ kubectl -n kube-system describe deployments.apps coredns | grep -A2 Args | grep Corefile
      /etc/coredns/Corefile

 

The Corefile is passed in to the CoreDNS POD by a ConfigMap object : mount 정보 확인!

pi@master:~ $ kubectl describe deploy coredns -n kube-system
Name:                   coredns
Namespace:              kube-system
CreationTimestamp:      Sat, 26 Jun 2021 18:21:35 +0900
Labels:                 k8s-app=kube-dns
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               k8s-app=kube-dns
Replicas:               2 desired | 2 updated | 2 total | 0 available | 2 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 25% max surge
Pod Template:
  Labels:           k8s-app=kube-dns
  Service Account:  coredns
  Containers:
   coredns:
    Image:       k8s.gcr.io/coredns/coredns:v1.8.0
    Ports:       53/UDP, 53/TCP, 9153/TCP
    Host Ports:  0/UDP, 0/TCP, 0/TCP
    Args:
      -conf
      /etc/coredns/Corefile
    Limits:
      memory:  170Mi
    Requests:
      cpu:        100m
      memory:     70Mi
    Liveness:     http-get http://:8080/health delay=60s timeout=5s period=10s #success=1 #failure=5
    Readiness:    http-get http://:8181/ready delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:  <none>
    Mounts:
      /etc/coredns from config-volume (ro)
  Volumes:
   config-volume:
    Type:               ConfigMap (a volume populated by a ConfigMap)
    Name:               coredns
    Optional:           false
  Priority Class Name:  system-cluster-critical

 

3. From the hr pod nslookup the mysql service and redirect the output to a file /root/CKA/nslookup.out

  • nslookup mysql.payroll
kubectl exec -it hr -- nslookup mysql.payroll > /root/CKA/nslookup.out


Server: 10.96.0.10
Address: 10.96.0.10

Name: mysql.payroll.svc.cluster.local
Address: 10.111.253.233

-> nslookup을 통해 fqdn 정보와 address를 알 수 있음.

 

참고) 서비스의 Selector는 Pod의 라벨 내용을 참고한다. kubectl get po xx --show-labels

 


Quiz 5.

1. 네임스페이스를 지정하여 ingress resource를 추가할 수 있다.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test-ingress
  namespace: critical-space
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /pay
        backend:
          serviceName: pay-service
          servicePort: 8282

 

2. deploy ingress controller

2-0. namespace

  • kubectl create namespace(ns) ingress-space

2-1. configmap

  • kubectl create configmap(cm) nginx-configuration --namespace ingress-space
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx-configuration
  namespace: ingress-space

2-2. service account

  • kubectl create serviceaccount(sa) ingress-serviceaccount --namespace ingress-space
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ingress-serviceaccount
  namespace: ingress-space

2.3. Role과 RoleBinding은 자동 생성

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  creationTimestamp: "2021-07-25T07:23:17Z"
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  name: ingress-role-binding
  namespace: ingress-space
  resourceVersion: "1409"
  uid: abd25bb3-4ae5-45c0-b1aa-c5c98b49c351
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: ingress-role
subjects:
- kind: ServiceAccount
  name: ingress-serviceaccount
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  creationTimestamp: "2021-07-25T07:23:17Z"
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  name: ingress-role
  namespace: ingress-space
  resourceVersion: "1408"
  uid: 535a7ee6-aabd-4a9f-9144-2799ab1327c6
rules:
- apiGroups:
  - ""
  resources:
  - configmaps
  - pods
  - secrets
  - namespaces
  verbs:
  - get
- apiGroups:
  - ""
  resourceNames:
  - ingress-controller-leader-nginx
  resources:
  - configmaps
  verbs:
  - get
  - update
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - endpoints
  verbs:
  - get

 

2-4. Ingress Controller 자동 생성

kind: Deployment
metadata:
  name: ingress-controller
  namespace: ingress-space
spec:
  replicas: 1
  selector:
    matchLabels:
      name: nginx-ingress
  template:
    metadata:
      labels:
        name: nginx-ingress
    spec:
      serviceAccountName: ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.21.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --default-backend-service=app-space/default-http-backend
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443

 

2-5. create service as nodePort

  • kubectl -n ingress-space expose deployment ingress-controller --name ingress --port 80 --target-port 80 --type NodePort --dry-run=client -o yaml > ingress-svc.yaml
apiVersion: v1
kind: Service
metadata:
  name: ingress
  namespace: ingress-space
spec:
  type: NodePort
  selector:
    name: nginx-ingress
  ports:
    - port: 80
      targetPort: 80
      nodePort: 30080

 

2-6. create ingress resource at namespace :: app-space 

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tt
  namespace: app-space
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /wear
        pathType: Prefix
        backend:
          service:
            name: wear-service
            port:
              number: 8080
      - path: /watch
        pathType: Prefix
        backend:
          service:
            name: video-service
            port:
              number: 8080

출처

- https://kubernetes.github.io/ingress-nginx/examples/

- https://kubernetes.github.io/ingress-nginx/examples/rewrite/

- https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/high-availability/#steps-for-the-first-control-plane-node

- https://kubernetes.io/docs/concepts/cluster-administration/networking/#how-to-implement-the-kubernetes-networking-model

- https://kubernetes.io/docs/concepts/cluster-administration/addons/

- https://kubernetes.io/docs/setup/independent/install-kubeadm/#check-required-ports 

- https://github.com/kubernetes/dns/blob/master/docs/specification.md

- https://coredns.io/plugins/kubernetes/

- https://medium.com/finda-tech/kubernetes-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%A0%95%EB%A6%AC-fccd4fd0ae6

Docker Network를 살펴보기전에 네트워크 기본 개념인 Switching, Routing, DNS에 대해서 먼저 살펴보고, 네트워크 환경 격리를 위한 네트워크 네임스페이스와 격리된 네트워크와의 연결를 위해 브리지 네트워크에 대해서 살펴 볼 것이다.

Network Basic

 

출처 :&amp;amp;amp;nbsp;https://circuitglobe.com/difference-between-router-and-switch.html

Switching

같은 네트워크 대역의 여러 장치들이 필요할 때 스위치를 통해 연결되어 통신 할 수 있도록 한다.

예를들면, 192.168.1.10 <-> 192.168.1.0(switch) <-> 192.168.1.11

 

참고) 네트워크 디바이스에 ip를 설정하는 방법

# eth0 네트워크 디바이스에 ip 설정
ip addr add 192.168.1.10/24 dev eth0
ip addr add 192.168.1.11/24 dev eth0
$ ip link

 

Routing

둘 이상의 다른 대역의 네트워크 간 데이터 전송을 위한 경로를 설정해 주고 데이터가 해당 경로에 따라 통신할 수 있도록 한다.

(gateway : door to the outside)

 

예를들면,

192.168.1.10 <-> 192.168.1.0(switch) <-> 192.168.1.11

                              192.168.1.1

                                 (gateway)

                                 192.168.2.1

192.168.2.10 <-> 192.168.2.0(switch) <-> 192.168.2.11

 

참고) 라우팅을 추가하는 방법

ip route add 192.168.2.0/24 via 192.168.1.1
$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
192.168.2.0    192.168.1.1     255.255.255.0   UG     0      0        0 eth0

 

Default Gateway

default는 따로 라우팅 규칙을 적용받지 않을 때 나머지 모든 ip에 대한 라우트를 처리합니다.

 

예를들면,

192.168.1.10 <-> 192.168.1.0(switch) <-> 192.168.1.11

                              192.168.1.1

                              (gateway)  -----------------------------------Internet

                              192.168.2.1

192.168.2.10 <-> 192.168.2.0(switch) <-> 192.168.2.11

 

참고) default gateway를 추가하는 방법

ip route add default via 192.168.2.1
= ip route add 0.0.0.0/0 via 192.168.2.1

$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.2.1     255.255.255.0   UG     0      0        0 eth0
192.168.1.0     192.168.2.1     255.255.255.0   UG     0      0        0 eth0
192.168.2.0     0.0.0.0         255.255.255.0   UG     0      0        0 eth0

# 일반 서버를 라우터처럼 사용할 때
cat /proc/sys/net/ipv4/ip_forward
# /etc/sysctl.conf
net.ipv4.ip_forward = 1(0 : not forward)

 

DNS

각 서버의 별칭을 /etc/hosts에 작성할 수 있지만 관리해야하는 별칭이 많아지거나 IP가 변경됬을 시에 관리해야하는 포인트가 많아 지기 때문에 한 곳에서 관리하고자하는 필요성이 제기된다. 이렇게 DNS Server가 탄생했다.

 

일반적인 사람들은 도메인 이름을 통해 온라인으로 정보에 엑세스한다. 이 도메인이름을 IP 주소로 변환해주는 것이 DNS 서버의 역할이다.

* Name Resolution : Translating Hostname to IP address

 

출처 :&amp;amp;amp;nbsp;http://dailusia.blog.fc2.com/blog-entry-362.html

 

DNS 설정

모르는 host name인 경우 DNS Server에 요청(기본 설정 : /etc/hosts가 우선순위가 높다)

리눅스의 경우 /etc/resolv.conf에 DNS 서버를 여러개 정의할 수 있다.

#/etc/resolv.conf
nameserver 192.168.1.100 8.8.8.8

Domain Names

top level domain : 웹사이트의 목적에 따라  com, .net, .edu, .org, .io 등을 사용한다.

출처 :&amp;amp;amp;nbsp;https://ko.wikipedia.org/wiki/%EB%8F%84%EB%A9%94%EC%9D%B8_%EB%84%A4%EC%9E%84

Search domain 

Organization DNS Server -> RootDNS -> .com DNS -> google DNS server

 

#/etc/resolv.conf
nameserver 192.168.1.100

# web 검색시 web.mycompany.com, web.prod.mycompany.com 이 검색됨
search mycompany.com prod.mycompany.com

 

Main Record Types

Type   example
A 주어진 호스트에 해당하는 IPv4를 알려준다. 192.168.1.1
AAAA 주어진 호스트에 해당하는 IPv6를 알려준다. 2001:0db8:85a3:0000:0000:8a2e:0370:7334
CNAME 도메인 이름의 별칭을 만드는데 사용한다. eat.web-server, hungry.web-server

 

Resolution Tool

ping, nslookup(only DNS), dig(only DNS) 

 


Network Namespaces / Bridge network

네트워크 네임스페이스는 프로세스간에 네트워크 환경을 격리할 수 있는 매우 강력한 기능을 제공한다. 

ip 명령은 네트워크 네임스페이스를 다루는 기능이 기본적으로 내장되어 있고, 네트워크 상태를 확인하고 제어하는 표준적인 명령어이다. ip 명령을 통해 네트워크 네임스페이스와 브리지 네트워크를 실습해 볼 것이다.

 

네트워크 디바이스 조회

  • ip -n {network namespace name} link
root@71bef26e2a21:/# ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0
3: ip6tnl0@NONE: <NOARP> mtu 1452 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/tunnel6 :: brd ::
9: eth0@if10: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default 
    link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0

 

네트워크 네임스페이스 관리

  • ip netns : 네트워크 네임스페이스 조회
  • ip netns add {name} : 네트워크 네임스페이스 생성
  • ip -n {network namespace} link set {vitual interface name} netns {name} : 가상 네트워크 인터페이스에 네임스페이스 지정
  • ip netns exec {name} {command} : 네임워크 네임스페이스에서 명령 실행
  • ip netns exec ip --br link : 네트워크 네임스페이스 내에 네트워크 디바이스 조회
  • ip netns exec {name} ip link set dev {device name} up : 네트워크 네임스페이스 내에 네트워크 인터페이스 실행
root@71bef26e2a21:/# ip netns add red
root@71bef26e2a21:/# ip netns add blue
root@71bef26e2a21:/# ip netns
blue
red

# 특정 네트워크 네임스페이스의 네트워크 디바이스 조희
root@71bef26e2a21:/# ip netns exec red ip --br link
lo               DOWN           00:00:00:00:00:00 <LOOPBACK> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP> 

# 루프백 인터페이스 실행
$ ip netns exec red ip link set dev lo up
$ ip netns exec red ip -br link

root@71bef26e2a21:/# ip netns exec red ip --br link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP>

 

Virtual Network Interface

  • ip link add {name1} type veth peer name {name2} : 가상 네트워크 인터페이스 생성
  • ip link del {pare 중 1개의 이름} : 하나만 삭제해도 연결된 것 같이 삭제됨.
  • ip a add xx.xx.xx.xx/xx dev {name} : 가상 네트워크 인터페이스에 ip 지정
  • = ip -n {network namespace} addr add xx.xx.xx.xx dev {name} (optional, not recommended)
  • ip -n {network namespace} link set dev {name} up : 가상 네트워크 인터페이스 실행
# veth0, veth1 (virtual interface)
$ ip link add veth0 type veth peer name veth1
$ root@71bef26e2a21:/# ip -br link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP> 
veth1@veth0      DOWN           0e:7d:e8:e3:60:fd <BROADCAST,MULTICAST,M-DOWN>  <----
veth0@veth1      DOWN           36:e2:ec:f0:95:23 <BROADCAST,MULTICAST,M-DOWN>  <----
eth0@if10        UP             02:42:ac:11:00:03 <BROADCAST,MULTICAST,UP,LOWER_UP>

 

실습 1. 가상 네트워크 인터페이스를 이용하여 네트워크 네임스페이스와 호스트를 연결

첫째, 기존 가상 네트워크 인터페이스(veth1)에 네트워크 네임스페이스(red) 지정을 한다.

$ ip link set veth1 netns red
root@71bef26e2a21:/# ip netns exec red ip --br link 
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP> 
veth1@if5        DOWN           0e:7d:e8:e3:60:fd <BROADCAST,MULTICAST> <------ 네트워크 네임스페이스 virtual interface


root@71bef26e2a21:/# ip --br link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP> 
veth0@if4        DOWN           36:e2:ec:f0:95:23 <BROADCAST,MULTICAST> <------- 호스트 virtual interface
eth0@if10        UP             02:42:ac:11:00:03 <BROADCAST,MULTICAST,UP,LOWER_UP>

둘째, 가상 네트워크 인터페이스(veth0, veth1) IP 지정 및 Up 상태로 변경 후 ping 테스트

root@71bef26e2a21:/# ip a add 10.200.0.2/24 dev veth0
root@71bef26e2a21:/# ip netns exec red ip a add 10.200.0.3/24 dev veth1
root@71bef26e2a21:/# ip link set dev veth0 up
root@71bef26e2a21:/# ip netns exec red ip link set dev veth1 up
root@71bef26e2a21:/# ip -br link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP> 
veth0@if4        UP             36:e2:ec:f0:95:23 <BROADCAST,MULTICAST,UP,LOWER_UP> 
eth0@if10        UP             02:42:ac:11:00:03 <BROADCAST,MULTICAST,UP,LOWER_UP> 
root@71bef26e2a21:/# ip netns exec red ip -br link
lo               UNKNOWN        00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> 
tunl0@NONE       DOWN           0.0.0.0 <NOARP> 
ip6tnl0@NONE     DOWN           :: <NOARP> 
veth1@if5        UP             0e:7d:e8:e3:60:fd <BROADCAST,MULTICAST,UP,LOWER_UP>


root@71bef26e2a21:/# ping 10.200.0.3
PING 10.200.0.3 (10.200.0.3) 56(84) bytes of data.
64 bytes from 10.200.0.3: icmp_seq=1 ttl=64 time=0.077 ms

root@71bef26e2a21:/# ip netns exec red ping 10.200.0.2
PING 10.200.0.2 (10.200.0.2) 56(84) bytes of data.
64 bytes from 10.200.0.2: icmp_seq=1 ttl=64 time=0.036 ms
64 bytes from 10.200.0.2: icmp_seq=2 ttl=64 time=0.057 ms

 

Virtual Network Switch(Bridge)

브리지는 데이터링크(L2) 계층의 장비로 네트워크 세그먼트를 연결해주는 역할을 한다. 브리지는 물리 장비나 소프트웨어로 구성할 수 있다. ip 명령어를 사용하면 veth 가상 인터페이스 뿐만 아니라, 가상 브리지를 만드는 것도 가능하다. 

  • ip link add {name} type bridge : 브리지 생성
  • ip link set {name} up : 브리지 실행
  • ip link set {virtual interface name} master {name} : 브리지에 가상 네트워크 인터페이스를 연결
  • ip link set dev {name} up : 가상 네트워크 인터페이스 활성화
  • ip addr add 10.201.0.1/24 brd 10.201.0.255 dev {name} : 브리지에 ip와 브로드캐스트 ip 셋업(10.201.0.0/24 IP 대역이 bridge로 연결됨)
  • ip addr add 10.201.0.1/24 dev {name} 
  • ip a show {name}

 

실습 2. 브리지를 통해 서로 다른 네트워크 네임스페이스(컨테이너)를 연결

 

첫째, 브리지 생성 및 활성화

# 브리지 생성
root@71bef26e2a21:/# ip link add br0 type bridge
root@71bef26e2a21:/# ip link set br0 up

둘째, 가상 네트워크 인터페이스 생성 및 네트워크 네임스페이스 지정

# 네트워크 네임스페이스, virtual interface 1
root@71bef26e2a21:/# ip netns add container10 
root@71bef26e2a21:/# ip link add brid10 type veth peer name veth10
root@71bef26e2a21:/# ip link set veth10 netns container10


# 네트워크 네임스페이스, virtual interface 2
root@71bef26e2a21:/# ip netns add container11
root@71bef26e2a21:/# ip link add brid11 type veth peer name veth11
root@71bef26e2a21:/# ip link set veth11 netns container11

셋째, 가상 네트워크 인터페이스 ip 할당 및 활성화

# 네트워크 네임스페이스, IP 할당 및 실행
root@71bef26e2a21:/# ip netns exec container10 ip a add 10.201.0.4/24 dev veth10
root@71bef26e2a21:/# ip netns exec container10 ip link set dev veth10 up

root@71bef26e2a21:/# ip netns exec container11 ip a add 10.201.0.5/24 dev veth11
root@71bef26e2a21:/# ip netns exec container11 ip link set dev veth11 up

넷째, 가상 네트워크 인터페이스를 브리지에 연결 및 활성화

# virtual interface를 브리지에 연결 1
root@71bef26e2a21:/# ip link set brid10 master br0
root@71bef26e2a21:/# ip link set dev brid10 up

# virtual interface를 브리지에 연결 2
root@71bef26e2a21:/# ip link set brid11 master br0
root@71bef26e2a21:/# ip link set dev brid11 up

다섯째, ping 테스트

# 테스트
root@71bef26e2a21:/# ip netns exec container10 ping 10.201.0.5
PING 10.201.0.5 (10.201.0.5) 56(84) bytes of data.
64 bytes from 10.201.0.5: icmp_seq=1 ttl=64 time=0.142 ms

root@71bef26e2a21:/# ip netns exec container11 ping 10.201.0.4
PING 10.201.0.4 (10.201.0.4) 56(84) bytes of data.
64 bytes from 10.201.0.4: icmp_seq=1 ttl=64 time=0.260 ms

 

실습 3. 호스트와 네임스페이스를 연결하고 인터넷과 DNS를 사용할 수 있도록 셋업

호스트에서는 위의 container10과 container11 네임스페이스와 연결되어 있지 않기 때문에 통신을 할 수가 없는 상태이다. 

root@71bef26e2a21:/# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
10.200.0.0      0.0.0.0         255.255.255.0   U     0      0        0 veth0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

 

첫째, 브리지(br0)에 ip와 브로드캐스트 ip를 셋업하자.(10.201.0.0/24 IP 대역이 br0로 연결됨)

root@71bef26e2a21:/# ip addr add 10.201.0.1/24 brd 10.201.0.255 dev br0
root@71bef26e2a21:/# ip a show br0
6: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 62:92:6c:c8:b5:28 brd ff:ff:ff:ff:ff:ff
    inet 10.201.0.1/24 brd 10.201.0.255 scope global br0
       valid_lft forever preferred_lft forever
       
       
root@71bef26e2a21:/# route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         172.17.0.1      0.0.0.0         UG    0      0        0 eth0
10.200.0.0      0.0.0.0         255.255.255.0   U     0      0        0 veth0
10.201.0.0      0.0.0.0         255.255.255.0   U     0      0        0 br0
172.17.0.0      0.0.0.0         255.255.0.0     U     0      0        0 eth0

root@71bef26e2a21:/# ping 10.201.0.4
PING 10.201.0.4 (10.201.0.4) 56(84) bytes of data.
64 bytes from 10.201.0.4: icmp_seq=1 ttl=64 time=0.072 ms

 

둘째, 네트워크 네임스페이스에서 인터넷을 사용할 수 있도록 default, NAT, DNS 셋업

  • ip route add default via xx.xx.xx.xx
  • /etc/netns/{namespace name}/resolv.conf : DNS 서버 지정
root@71bef26e2a21:/# ip netns exec container10 ip route add default via 10.201.0.1
root@71bef26e2a21:/# ip netns exec container11 ip route add default via 10.201.0.1
root@71bef26e2a21:/# ip netns exec container10 route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         10.201.0.1      0.0.0.0         UG    0      0        0 veth10
10.201.0.0      0.0.0.0         255.255.255.0   U     0      0        0 veth10

# NAT 셋업 - linux IP 포워드 기능 활성화
$ sysctl -w net.ipv4.ip_forward=1
$ iptables -t nat -A POSTROUTING -s 10.201.0.0/24 -j MASQUERADE

root@71bef26e2a21:/# ip netns exec container10 ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=36 time=92.6 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=36 time=86.7 ms

# DNS 셋업 - 네트워크 네임스페이스 도메인 요청
root@71bef26e2a21:/# mkdir -p /etc/netns/container10/
root@71bef26e2a21:/# echo 'nameserver 8.8.8.8' > /etc/netns/container10/resolv.conf
root@71bef26e2a21:/# ip netns exec container10 curl www.naver.com
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center> NWS </center>
</body>
</html>
root@71bef26e2a21:/# ip netns exec container10 curl google.com   
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>

 

실습 4. 네트워크 인터페이스에서 인터넷에 연결된 호스트를 통해 다른 네트워크로 통신할 수 있도록 셋업

호스트를 게이트웨이로 활용(192.168.15.2 &amp;gt; 192.168.1.3)

첫째, 네트워크 네임스페이스 셋업

$ ip netns exec {network_namespace} ip route add 192.168.1.0/24 via 192.168.15.5(bridge network ip address)

둘째, 인터넷 접근을 위한 네트워크 네임스페이스 default 셋업

# 인터넷 접근 from internal network
ip netns exec {network_namespace} ip route add default via 192.168.15.5
ip netns exec {network_namespace} ping 8.8.8.8

셋째, NAT 설정

# packet의 source(출처) 정보를 게이트웨이로 변경하는 작업이 필요함. 그래야 외부입장에서는 게이트웨이에서 보낸 것으로 간주하고
# 통신이 가능하게된다.
iptables -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE

넷째, 테스트

ip netns exec {network_namespace} ping 192.168.1.3

 

실습 5. 외부 네트워크에서 Virtual network interface 로의 접근

- 첫번째 방법. 위의 방법과 동일하게 호스트를 게이트웨이로 사용한다.

- 두번째 방법. 호스트에 포워딩 룰을 추가한다

iptables -t nat -A PREROUTING --dport 80 --to-destination 192.168.15.2:80 -j DNAT

 

참고

1. While testing the Network Namespaces, if you come across issues where you can't ping one namespace from the other, make sure you set the NETMASK while setting IP Address. ie: 192.168.1.10/24

 

첫째, ip -n red addr add 192.168.1.10/24 dev veth-red

 

둘째, Another thing to check is FirewallD/IP Table rules. Either add rules to IP Tables to allow traffic from one namespace to another. Or disable IP Tables all together (Only in a learning environment).

 

2. Common command

- arp

- netstat -plnt

 


Docker Networking

도커 네트워크 종류

- None

- Host network : docker container isolation 적용 안됨. docker container 끼리 같은 포트를 사용할 수 없음

- Bridge(default) : internal private network

pi@master:~ $ sudo docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
dd74c3b7034f   bridge    bridge    local
87b3579ecb45   host      host      local
ae52f9cf2344   none      null      local

 

Docker0

Linux는 Docker를 설치하면 서버의 물리 NIC(Network Interface Card)가 docker0이라는 가상 브리지 네트워크로 연결된다. 이 docker0은 Docker를 실행시킨 후에 디폴트로 만들어진다. 앞서 배웠던 네임스페이스 개념이 각 컨테이너에게 적용되고, Bridge와 연결 또한 그렇다.

Docker 컨테이너가 실행되면 컨테이너에 172.17.0.0/16 이라는 서브넷 마스크를 가진 프라이빗 IP주소가 eth0으로 자동으로 할당된다. 이 가상 NIC는 OSI 참조 모델의 레이어 2인 가상 네트워크 인터페이스로, 페어인 NIC와 터널링 통신을 한다.

https://jonnung.dev/images/docker_network.png - veth(가상 NIC)

 

NAPT(Network Address Port Translation)

Docker 컨테이너와 외부 네트워크가 통신을 할 때는 가상 브리지 docker0과 호스트 OS의 물리 NIC에서 패킷을 전송하는 장치가 필요하다. Docker에서는 NAPT 기능을 사용하여 연결한다.

NAPT(Network Address Port Translation)란 하나의 IP 주소를 여러 컴퓨터가 공유하는 기술로, IP 주소와 포트 번호를 변환하는 기능이다. 프라이빗 IP 주소와 글로벌 IP 주소를 투과적으로 상호 변환하는 기술로, TCP/IP의 포트 번호까지 동적으로 변환하기 때문에 하나의 글로벌 IP 주소로 여러 대의 머신이 동시에 연결할 수 있다. Docker에서는 NAPT에 Linux의 iptables를 사용하고 있다.

 

예를들면, 컨테이너 시작 시에 컨테이너 안의 웹 서버가 사용하는 80번 포트를 호스트 OS의 8080번 포트로 전송하도록 설정한다. 그러면 외부 네트워크에서 호스트 OS의 8080번 포트에 엑세스하면 컨테이너 안의 80번 포트로 연결된다.

(docker0과 물리 NIC 사이에서 컨테이너의 포트와 호스트 OS 포트를 IP 마스커레이드(NAPT)를 사용하여 변환하고 있다.)

iptables \
–t nat \
-A DOCKER \
-j DNAT \
--dport 8080 \
--to-destination 172.17.0.3:80

 

참고

NAT와 IP 마스커레이드의 차이

프라이빗 IP 주소와 글로벌 IP 주소를 변환하여 프라이빗 IP 주소가 할당된 컴퓨터에 대해 인터넷 엑세스를 가능하게 할때 사용하는 기술로는 NAT과 NAPT(IP 마스커레이드)가 있다.

 

- NAT(Network Address Translation)

프라이빗 IP 주소가 할당된 클라이언트가 인터넷상에 있는 서버에 엑세스할 때 NAT 라우터는 클라이언트의 프라이빗 IP 주소를 NAT가 갖고 있는 글로벌 IP 주소로 변환하여 요청을 송신한다. 응답은 NAT 라우터가 송신처를 클라이언트의 프라이빗 IP 주소로 변환하여 송신한다.

이러한 주소 변환에 의해 프라이빗 네트워크상의 컴퓨터와 인터넷상의 서버 간의 통신이 성립된다. 그런데 NAT의 경우 글로벌 IP 주소와 프라이빗 IP 주소를 1:1로 변환하기 때문에 동시에 여러 클라이언트가 엑세스할 수가 없다.

 

- NAPT(Network Address Port Translation)

NAPT는 프라이빗 IP 주소와 함께 포트 번호도 같이 변환하는 기술이다. 프라이빗 IP 주소를 글로벌 IP 주소로 변환할 때 프라이빗 IP 주소별로 서로 다른 포트 번호로 변환한다. NAPT는 포트 번호를 바탕으로 프라이빗 IP 주소로 변환할 수 있다. 이로써 하나의 글로벌 IP 주소와 여러 개의 프라이빗 IP 주소를 변환할 수 있는 것이다.

Linux에서 NAPT를 구축하는 것을 IP 마스커레이드라고 부른다.

 

 

Container Network Interface(CNI)

Network Namespaces, Docker ... 모두 비슷하게 브리지 네트워크 구성을 함. 그러므로 CNI 표준이 등장.

- docker does not implement CNI. Docker has its own set of standards known as CNM

- 쿠버네티스에서 docker container를 생성할 때 none 네트워크로 실행하고, 나머지 설정은 CNI plugins에게 맡긴다.

 

* 브리지 네트워크 셋업 절차

1. Create Network Namespace

2. Create Bridge Network/Interface

3. Create VETH Pairs(Pipe, Virtual Cable)

4. Attach vEth to Namespace

5. Attach Other vEth to Bridge

6. Assign IP Addr

7. Bring the tinerfaces up

8. Enable NAT - IP Masquerade

 


출처

- [책] 완벽한 IT 인프라 구축을 위한 Docker 2판

- 네트워크 네임스페이스와 브리지 네트워크 : https://www.44bits.io/ko/post/container-network-2-ip-command-and-network-namespace

Volume

미리 준비된 사용 가능한 볼륨(호스트 볼륨/nfs 등) 등을 매니페스트에서 직접 지정하여 사용할 수 있게 하는 것이다. 사용자가 설정된 볼륨을 사용할 수 있지만, 쿠버네티스에서 신규 볼륨을 생성하거나 기존 볼륨을 삭제하는 작업은 할 수 없다. 또한, 매니페스트에서 볼륨 리소스를 생성하는 것도 불가능하다.

 

- emptyDir, hostPath, downwardAPI, projected, nfs, iscsi, cephfs

 

emptyDir

파드용 임시 디스크 영역으로 사용할 수 있다. 그리고 파드가 종료되면 삭제된다. 호스트의 임의 영역을 마운트할 수 없으며 호스트에 있는 파일을 참조할 수도 없다.

apiVersion: v1
kind: Pod
metadata:
  name: sample-emptydir-memory-with-memory-limits
spec:
  containers:
  - image: nginx:1.16
    name: nginx-container
    resources:
      limits:
        memory: 64Mi
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir:
      medium: Memory #고속 tmpfs 메모리 영역, 삭제시 호스트 볼륨(컨테이너용 임시 디스크 영역) 사용
      sizeLimit: 128Mi

 

hostPath

쿠버네티스 노드상의 영역을 컨테이너에 매핑한다. 호스트의 임의 영역을 마운트할 수 있기 때문에 사용할 때는 호스트의 어떤 영역을 사용할지 지정해야한다. 보안상의 이유로 안전하지 않은 컨테이너가 업로드될 수 있으므로 사용하지 말 것을 권장한다.

Multi node 를 사용할 경우에는 hostPath를 사용하지말아야 한다. 그 이유는 Pod가 모든 노드에 동일한 데이터가 존재할 것으로 기대하기 때문이다. 대신에 , NFS 등 external replicated cluster storage를 사용해야한다. 

apiVersion: v1
kind: Pod
metadata:
  name: sample-hostpath
spec:
  containers:
  - image: nginx:1.16
    name: nginx-container
    volumeMounts:
    - mountPath: /srv
      name: hostpath-sample
  volumes:
  - name: hostpath-sample
    hostPath:
      path: /etc
      type: DirectoryOrCreate

Persistent Volume

외부 영구 볼륨을 제공하는 시스템과 연계하여 신규 볼륨을 생성하거나 기존 볼륨을 삭제하는 등의 작업이 가능하다. 

볼륨은 파드 정의 안에 직접 지정하는 형태로 연결하지만, 영구 볼륨은 개별 리소스로 생성한 후 사용한다. 즉 메니페스트를 사용하여 영구 볼륨 리소스를 생성해야한다. 또한, 영구 볼륨은 클러스터 리소스로 분류된다.

 

- GCE Persistent Disk, AWS Elastic Block Store, nfs, Container Storage Interface ...

 

접근 모드

- ReadWriteOnce(RWO) : 단일 노드에서 Read/Write 가능

- ReadOnlyMany(ROW) : 여러 노드에서 Read 가능

- ReadWriteMany(RWX) : 여러 노드에서 Read/Write 가능

 

Reclaim Policy

영구 볼륨을 사용한 후 처리 방법(삭제 또는 재사용)을 제어하는 정책이다. 즉, 영구 볼륨 클레임에서 사용된 후 그 볼륨 클레임이 삭제되었을 때 영구 볼륨 자체의 동작을 설정한다.

- Delete(default) : 영구 볼륨 자체가 삭제된다.

- Retain : 영구 볼륨 자체를 삭제하지 않고 유지한다. 또 다른 영구 볼륨 클레임에 의해 이 영구 볼륨이 다시 마운트되지 않는다.

- Recycle : 영구 볼륨 데이터를 삭제하고 재사용 가능한 상태로 만든다. 다른 영구 볼륨 클레임에서 다시 마운트 가능.

 

apiVersion: v1
kind: PersistentVolume
metadata:
  name: foo-pv
spec:
  storageClassName: ""
  claimRef:
    name: foo-pvc
    namespace: foo
  ...

Persistent Volume Claim

생성된 영구 볼륨 리소스를 할당하는 리소스다. 영구 볼륨은 클러스터에 볼륨을 등록만 하기 때문에 실제 파드에서 사용하려면 영구 볼륨 클레임을 정의하고 사용해야한다. 또한, 동적 프로비저닝 기능을 사용한 경우에는 영구 볼륨 클레임이 사용된 시점에 영구 볼륨을 동적으로 생성할 수 있다.

 

영구 볼륨 클레임에서 지정된 조건(용량, 레이블)을 기반으로 영구 볼륨에 대한 요청이 들어오면 스케줄러는 현재 가지고 있는 영구 볼륨에서 적당한 볼륨을 할당한다. 이때 주의해야할 것은 영구 볼륨 클레임 용량이 영구 볼륨 용량보다 작으면 할당된다는 점이다.

 

참고) The result of PVC deletion can be terminating because pvc is being used by a pod

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pvc
spec:
  selector:
    matchLabels:
      type: pv
    matchExpressions:
    - key: environment
      operator: In
      values:
      - stg
  resources:
    requests:
      storage: 3Gi
  accessModes:
  - ReadWriteOnce
  storageClassName: manual

파드에서 사용하는 예제는 아래와 같다.

 

apiVersion: v1
kind: Pod
metadata:
  name: sample-pvc-pod
spec:
  containers:
  - name: nginx-container
    image: nginx:1.16
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
      name: nginx-pvc
  volumes:
  - name: nginx-pvc
    persistentVolumeClaim:
      claimName: sample-pvc

참고)

- The same is true for ReplicaSets or Deployments. Add this to the pod template section of a Deployment on ReplicaSet.

volumeMounts를 하지 않는다면 Pod 재시작시 기존내용을 복구할 수 없다.

동적 프로비저닝

static provisioning : cloud provider에 디스크 요청 -> pv 생성 -> pvc 생성 -> pod definition 

영구 볼륨 클레임에서는 영구 볼륨을 미리 생성해 두고 영구 볼륨 클레임 요청에 따라 볼륨을 할당하는 순서로 진행되었다. 그래서 사전에 영구 볼륨을 생성해야 하는 번거로움과 영구 볼륨 클레임이 요청하는 용량 이상으로 영구 볼륨이 할당되어 낭비가 발생하는 문제가 있다.

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vol1
spec:
  accessModes:
    - ReadWriteOnce
  capacity:
    storage: 500Mi
  gcePersistentDisk:
    pdName: pd-disk
    fsType: ext4

 

이런 문제를 해결하는 것이 동적 프로비저닝(Dynamic Provisioning)이다.

 

동적 프로비저닝을 사용한 영구 볼륨 클레임의 경우 영구 볼륨 클레임이 생성되는 타이밍에 동적으로 영구 볼륨을 생성하고 할당한다. 동적 프로비저닝을 사용하려면 사전에 어떤 영구 볼륨을 생성할지 정의한 StorageClass를 생성해야한다.

 

참고) storage class의 VolumeBindingMode(waitForFirstConsumer) -> pvc만 생성시 해당 pvc를 사용하는 pod를 기다림(pvc status : waitForFirstConsumer)

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: sample-storageclass
parameters:
  type: pd-ssd
provisioner: kubernetes.io/gce-pd
reclaimPolicy: Delete
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: sample-pvc-dynamic
spec:
  storageClassName: sample-storageclass
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 3Gi
apiVersion: v1
kind: Pod
metadata:
  name: sample-pvc-wait-pod
spec:
  containers:
  - name: nginx-container
    image: nginx:1.16
    volumeMounts:
    - mountPath: "/usr/share/nginx/html"
      name: nginx-pvc
  volumes:
  - name: nginx-pvc
    persistentVolumeClaim:
      claimName: sample-pvc-dynamic

 

영구 볼륨 클레임 조정을 사용한 볼륨 확장을 하고 싶을 경우 allowVolumeExpansion:true를 설정하자. 다만 영구 볼륨 클레임 조정은 디스크 크기를 확장할 수 있지만 축소할 수는 없다는 점에 주의하자. 또한 파드에 어태치된 볼륨 자체는 확장되는데, 그 위에 있는 파일 시스템 확장은 경우에 따라 잘라질 수 있다. 파일 시스템의 자동 확장은 파일 시스템으로 EXT3/EXT4/XFS를 사용하는 경우만 해당된다.

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: sample-storageclass-resize
parameters:
  type: pd-ssd
provisioner: kubernetes.io/gce-pd
reclaimPolicy: Delete
allowVolumeExpansion: true

스테이트풀셋에서 영구 볼륨 클레임

스테이트풀셋의 워크로드에서는 영구 데이터 영역을 사용하는 경우가 많다. spec.volumeClaimTemplate을 사용하면 영구 볼륨 클레임을 별도로 정의하지 않아도 자동으로 영구 볼륨 클레임을 생성할 수 있다. 또 컨테이너 내부의 volumeMounts에 volumeClaimTemplate 이름을 지정하는 것으로 완료된다.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sample-statefulset-with-pvc
spec:
  serviceName: stateful-with-pvc
  replicas: 2
  selector:
    matchLabels:
      app: sample-pvc
  template:
    metadata:
      labels:
        app: sample-pvc
    spec:
      containers:
      - name: sample-pvc
        image: nginx:1.16
        volumeMounts:
        - name: pvc-template-volume
          mountPath: /tmp
  volumeClaimTemplates:
  - metadata:
      name: pvc-template-volume
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: 10Gi
      storageClassName: sample-storageclass

 

참고)

how much capacity is now available to the PVC?

--> kubectl get pvc -> capacity

 

Debugging?

kubectl explain persistentvolume --recursive | less

kubectl exec webapp -- cat /log/app.log


출처

- [책] 쿠버네티스 완벽가이드 마사오 아오야마 지음, 박상욱 옮김

- https://kubernetes.io/docs/concepts/storage/storage-classes/

- https://kubernetes.io/docs/concepts/storage/persistent-volumes/#claims-as-volumes

- https://github.com/container-storage-interface/spec

- https://docs.docker.com/engine/extend/legacy_plugins/

'클라우드 컴퓨팅 > 쿠버네티스' 카테고리의 다른 글

Kubernetes Networking  (0) 2021.12.05
Docker Network  (0) 2021.12.05
Kubernetes Security - Authentication/Authorization  (0) 2021.11.10
Kubernetes TLS/PKI 살펴보기  (0) 2021.11.08
Kubernetes Monitoring with Prometheus  (0) 2021.10.27
알고리즘 종류 : 백트랙킹

Dessert

제한시간 메모리 제한
1000ms 32 MB

 

농부 존은 소들의 저녁식사 줄 세우는 새로운 방법을 개발 했다.

N(1~15)마리의 소들을 순서대로 세워놓은 후, 

각 소들 사이에 +, - , . 셋 중 1가지가 써져있는 냅킨을 배치해서

최종 결과가 0 이 되게 해야 하는 것이다. 

 

점(.)이 써져있는 냅킨을 통해 더 큰 수를 만들 수 있게 된다. 

아래와 같은 경우를 보자. (ps .이 써져있는 냅킨은 '공백'이라고 생각하면 된다.)

 

1 - 2 . 3 - 4 . 5 + 6 . 7

 

이와 같은 배치는 1-23-45+67 을 나타낸다. 

결과는 0 이다. 

10.11은 1011 로 해석된다.

 

입력형식

첫 번째 줄에는 소들의 수 N이 입력된다.

 

출력형식

처음 20줄에 대해 가능한 20가지 답을 출력하는데, 사전 순으로 앞선 것을 출력한다.

순서는 +가 가장 앞서고 -와 .이 순서대로 뒤따른다.
답이 20개 미만이면 가능한 답을 모두 출력한다. 

마지막 줄에는 가능한 답의 총 가지 수를 출력한다.

 

입력 예

7

 

출력 예

1 + 2 - 3 + 4 - 5 - 6 + 7 
1 + 2 - 3 - 4 + 5 + 6 - 7 
1 - 2 + 3 + 4 - 5 + 6 - 7 
1 - 2 - 3 - 4 - 5 + 6 + 7 
1 - 2 . 3 + 4 + 5 + 6 + 7 
1 - 2 . 3 - 4 . 5 + 6 . 7 
6

 


public class Main {
	private int totalCnt = 0;

	public static void main(String[] args) throws Exception {
		Main main = new Main();
		main.run();
		
	}
	
	private void run() throws Exception {
		System.setIn(new FileInputStream("src/test/input.txt"));
		Scanner sc = new Scanner(System.in);
		int N = sc.nextInt();
		String[] numbersAndOpts = new String[2 * N - 1];
		numbersAndOpts[0] = "1";
		fullSearch(numbersAndOpts, 2, 1, 2 * N - 1);
		System.out.println(totalCnt);
	}


	private void fullSearch(String[] numbersAndOpts, int cur, int idx, int maxIdx) {
		if (idx >= maxIdx) {
			if (checkEquationIsZero(numbersAndOpts)) {
				totalCnt++;
				if(totalCnt <= 30) printEquation(numbersAndOpts);
			}
			return;
		}

		numbersAndOpts[idx] = "+";
		numbersAndOpts[idx + 1] = Integer.toString(cur);
		;
		fullSearch(numbersAndOpts, cur + 1, idx + 2, maxIdx);

		numbersAndOpts[idx] = "-";
		numbersAndOpts[idx + 1] = Integer.toString(cur);
		;
		fullSearch(numbersAndOpts, cur + 1, idx + 2, maxIdx);

		numbersAndOpts[idx] = ".";
		numbersAndOpts[idx + 1] = Integer.toString(cur);
		;
		fullSearch(numbersAndOpts, cur + 1, idx + 2, maxIdx);
		
	}

	private Boolean checkEquationIsZero(String[] numbersAndOpts) {
		StringBuilder sb = new StringBuilder();
		
		int nextIdx = getNumberStr(numbersAndOpts, sb, 0);
		Double sum = Double.parseDouble(sb.toString());
		emptyStringBuilder(sb);
		while (nextIdx < numbersAndOpts.length) {
			
			if (numbersAndOpts[nextIdx].equals("+")) {
				nextIdx = getNumberStr(numbersAndOpts, sb, nextIdx + 1);
				sum += Double.parseDouble(sb.toString());
				emptyStringBuilder(sb);
					
			} else if (numbersAndOpts[nextIdx].equals("-")) {
				nextIdx = getNumberStr(numbersAndOpts, sb, nextIdx + 1);
				sum -= Double.parseDouble(sb.toString());
				emptyStringBuilder(sb);
			}
		}

		return sum.equals(0.0) ? Boolean.TRUE : Boolean.FALSE;
		// Double.parseDouble();
	}
	private void emptyStringBuilder(StringBuilder sb) {
		sb.setLength(0);
	}

	private int getNumberStr(String[] numbersAndOpts, StringBuilder sb, int cur) {
		sb.append(numbersAndOpts[cur]);
		int i = cur + 1;
		while(i < numbersAndOpts.length - 1 && numbersAndOpts[i].equals(".")) {
			sb.append(numbersAndOpts[i + 1]);
			i += 2;
		}
		return i;
	}
	
	private void printEquation(String[] numbersAndOpts) {
		for (int i = 0; i < numbersAndOpts.length; i++) {
			System.out.print(numbersAndOpts[i] + " ");
		}
		System.out.println("");
	}

	private void test() {
		String[] numbersAndOpts = {"1",".","2",".","3",".","4"};
		StringBuilder sb = new StringBuilder();
		System.out.println(getNumberStr(numbersAndOpts, sb, 0));
	}
	
	private void testCheckEquationIsZero() {
		String[] numbersAndOpts = {"1",".","2","-","1",".","2"};
		System.out.println(checkEquationIsZero(numbersAndOpts));
	}
	
	private String[] deepClone(String[] numbersAndOpts) {
		String[] copy = new String[numbersAndOpts.length];
		for (int i = 0; i < numbersAndOpts.length; i++) {
			copy[i] = numbersAndOpts[i];
		}
		return copy;
	}

	

	private void start() {
		String[] my = new String[10];
		my[0] = "hi";

		System.out.println("=====Array========");
		System.out.println(my[0]);

		changeContents(my);

		System.out.println(my[0]);

		System.out.println("=====Wrapper========");

		Integer num = new Integer(3);

		System.out.println(num);
		changeNumber(num);
		System.out.println(num);

		System.out.println("========Object===========");
		Obj obj = new Obj();
		obj.num = 3;

		System.out.println(obj.num);
		changeObj(obj);
		System.out.println(obj.num);

		System.out.println("==========Collection - List ==============");
		List<Integer> li = new ArrayList<>();
		System.out.println(li.size());
		changeList(li);
		System.out.println(li.size());

	}

	class Obj {
		public int num;
	}

	private void changeContents(String[] args) {
		args[0] = "test1";
		args[1] = "test2";
	}

	private void changeNumber(Integer num) {
		num = 13;
	}

	private void changeObj(Obj obj) {
		obj.num = 13;
	}

	private void changeList(List<Integer> li) {
		li.add(3);
	}
}

 


출처

http://www.jungol.co.kr/bbs/board.php?bo_table=pbank&wr_id=463&sca=9040

+ Recent posts