알아둬야 할 Linux Networking Prerequisite 내용.
Basics : Linux Networking
A가 B에게 통신하려면?
- Switch에 연결한다. -> switch는 두 개의 시스템을 연결하는 네트워크를 생성한다.
- switch에 연결하기 위해서는, 각 호스트가 사용해야 할 인터페이스가 있다.
ip link
명령어로 해당 PM / VM의 인터페이스를 확인할 수 있다.- 예컨대 예시의 경우 eth0 이라는 이름의 인터페이스가 확인됨.
- switch 내부의 네트워크가 192.168.1.0 이라고 가정한다면, 두 개의 컴퓨터 A와 B는 아래의 명령어를 사용해서 switch 내부의 네트워크에 자신의 정보를 등록할 수 있다.
ip addr add 192.168.1.10/24 dev eth0
ip addr add 192.168.1.11/24 dev eth0
- 명령어로 ip주소가 등록이 완료되었다면, A와 B는 서로 통신할 수 있다.
ping 192.169.1.11
로 요청을 보내면 응답을 받을 수 있다.
- switch의 경우, 네트워크에 등록된 컴퓨터에 한해 통신이 가능하다.
그렇다면 '각 네트워크 간 통신'은 어떻게 하나? 서로 다른 스위치 네트워크에 등록되어 있는 B와 C는 통신을 어떻게 하나?
- Router의 역할. 예시에서는 두 개의 네트워크를 연결한다.
- 두 개의 네트워크를 연결해야 하므로, router는 각 네트워크마다 ip주소를 가지고 있다. 예컨대 A / B 네트워크에서는 192.168.1.1 주소를, C / D 네트워크에서는 192.168.2.1 을 가지고 있다.
- router도 일종의 디바이스. B가 C와 통신하고 싶으면, C는 내부 네트워크에 없으므로 Gateway 역할을 하는 Router에 요청을 보내야 한다.
route 명령어를 처음 입력해보면 아무것도 나오지 않는다. 외부와 연결하기 위한 mapping table이 없는 상태.
ip route add 192.168.2.0/24 via 192.168.1.1
명령어로 A / B 스위치 네트워크에 192.168.2.0 네트워크를 새로 추가한다.- 다시
route
를 입력해보면 gateway가 생성된 것을 볼 수 있다. 이제 A / B 라우터는 C / D 네트워크에 연결되었다. - 정반대의 경우에도 동일한 작업을 해줘야 한다. C/D에서 A/B로 연결하려면
ip route add 192.168.1.0/24 via 192.168.2.1
명령어를 입력해줘야 한다.
만약 외부 네트워크 중 인터넷에 연결해야 할 경우... 인터넷은 수많은 ip주소를 가지고 있음.
- 이걸 일일이 mapping table로 괸리해서 요청을 보내려는 컴퓨터가 직접 인터넷의 ip와 연결되도록 관리할 수가 없으니,
ip route add default via 192.168.2.1
같은 형태로 Default Gateway를 생성한다.- 어디로 라우팅해야 할지 모르겠으면 전부 default gateway로 보내는 것.
- 따라서 이렇게 세팅할 경우 simple routing table + default gateway만 있으면 외부로 통신이 가능하다.
- gateway 필드에 0.0.0.0 을 붙여도 동일한 효과가 적용된다.
- internal network를 위한 라우터가 필요할 경우, multi-router 세팅을 적용하면 된다.
- 외부 인터넷 / internal network를 위한 entry를 별도로 세팅하는 것.
ip route add 192.168.1.0/24 via 192.168.2.2
형태로 internal network를 세팅한다.
- 외부 인터넷 / internal network를 위한 entry를 별도로 세팅하는 것.
리눅스 자체를 simple Router처럼 사용하는 방법
A - B, B - C가 각각의 네트워크로 연결된 상황. 따라서 B는 위 예시처럼 192.168.1.6, 192.168.2.6 두 개의 ip주소를 가지고 있다.
- A와 C가 통신하려면? A와 C 양측에서 라우터로 B 주소를 지정하면 된다.
ip route add 192.168.2.0/24 via 192.168.1.6
: A의 명령어ip route add 192.168.1.0/24 via 192.168.2.6
: C의 명령어
- 하지만 route 연결한 것만으로는 통신이 되지 않는다.
- 리눅스 패킷은 기본적으로 한 인터페이스에서 다른 인터페이스로 forward되지 않기 때문. Security 이슈 때문이다.
- ex) B에서 eth0 인터페이스로 받은 패킷은 eth1 인터페이스로 전달되지 않는다. 만약 eth0가 private network / eth1이 public network라고 가정한다면, 외부에서 private network로 메시지를 보낼 수도 있게 되기 때문.
- network 간 패킷 통신이 가능하도록 하는 설정파일은
/proc/sys/net/ipv4/ip_forward
파일에서 확인할 수 있음.- 디폴트 값은 0. NO FORWARD를 의미함.
- 1로 변경하면 ping이 통과되는 걸 볼 수 있다.
/etc/sysctl.conf
값의 net.ipv4.ip_forward 값도 1로 설정해줘야 한다.
자주 쓰이는 명령어 모음
ip link
: list & modify interface on the hostip addr
: see the ip addresses assigned to those interfaces.ip addr add 192.168.1.10/24 dev eth0
: 인터페이스에 ip주소를 추가한다. eth0 인터페이스에 ip주소를 추가한 것.
위의 설정은 재시작하면 사라지는 옵션들이다. persist하도록 만드려면 /etc/network/interfaces 파일을 수정해야 한다.
ip route
: routing table 조회. route로 줄여 쓸 수도 있다.ip route add 192.168.1.0/24 via 192.168.2.1
: add entries into routing table.
cat /proc/sys/net/ipv4/ip_forward
: 네트워크 인터페이스 간 패킷 교환여부를 설정하는 옵션값. 0이 디폴트.
Basics - DNS
A와 B 두 개의 컴퓨터가 네트워크로 연결되어 있고, B에는 데이터베이스가 있는 상태라고 가정한다.
- B의 ip주소를 입력하는 대신, db라는 이름을 입력하면 B를 지칭하도록 만들고 싶다.
cat >> /etc/hosts
에서 192.168.1.11 db 라는 매핑 값이 있으면 된다. (ip주소 - 이름)- 실제 B의 host명이 무엇이건 상관없이, A는 /etc/hosts에 정의된 ip - 이름 매핑값을 사용한다.
- 이런 방식의 DNS 설정을 Name Resolution 이라고 한다.
소규모 네트워크에서는 각 호스트별 /etc/hosts 파일을 관리해주는 것만으로도 해결됨.
- 서버가 많아지면, 각 서버마다 /etc/hosts 파일로 DNS를 관리하기가 점점 더 어려워진다.
- 그래서 아예 DNS 정보를 저장하는 서버를 하나 두고, 그 서버에 접근해서 etc/hosts 파일을 받아오는 식으로 구조 변경이 진행됨.
- 이 정보를 저장하는 서버를 DNS Server 라고 부른다.
/etc/resolv.conf
파일에서 DNS 서버 경로를 확인할 수 있다. host값을 받았는데 ip주소를 모르겠다면, 바로 DNS 서버에 요청을 보내는 식으로 동작함.- /etc/hosts를 직접 관리할 필요는 없어졌지만, DNS 서버와 별개로 접근하고 싶은 private server가 있을 경우
/etc/hosts
에 지정해서 사용할 수 있음.
- 로컬의 hosts 파일과 DNS 서버의 값이 서로 다를 경우?
- 디폴트 설정으로는 /etc/hosts에서 값을 찾을 수 없을 때 DNS 서버로 요청을 보내므로, 로컬의
/etc/hosts
가 DNS 서버보다 우선한다. /etc/nsswitch.conf
옵션으로 조회 순서를 조정할 수 있다. 예컨대hosts: files dns
로 설정할 경우 local hosts file -> DNS server 순으로 조회한다.
- 디폴트 설정으로는 /etc/hosts에서 값을 찾을 수 없을 때 DNS 서버로 요청을 보내므로, 로컬의
- 만약 hosts / DNS server 둘다 값이 없을 경우?
- fail in resolution 에러가 발생함.
- google이 호스팅하는 Nameserver로 유명한 8.8.8.8 ... 인터넷으로 연결할 수 있음.
/etc/resolv.conf
파일에 여러 개의 nameserver (8.8.8.8 같은) 를 등록하거나, DNS server에서Forward All to 8.8.8.8
을 넣어서 구글의 namespace server로 값을 포워딩할 수 있음.
그러면 www.facebook.com 과 같은 '도메인'은 정확히 무엇을 의미하나?
- ip주소를 인간이 이해하고 외울 수 있도록 translated한 것.
- dot(.) 으로 분절된 이유는 '그룹화'를 위해서.
- .com, .edu, .org, .io 등 맨 뒤에 붙는 구분자는 top level domains represent the intent of the website.
- .com의 경우 commercial / general purpose
- .net의 경우 network
- .edu는 교육
- .org는 NGO단체
- .com, .edu, .org, .io 등 맨 뒤에 붙는 구분자는 top level domains represent the intent of the website.
www.google.com을 예로 들어보면
- 맨 처음 시작점인 dot(.) : Root. 모든 것의 시작.
- .com -> top level domain
- google -> 구글에 할당된 Domain Name
- www -> subDomain. Further Group을 의미함. 예컨대 map 서비스라면 map.google, 스토리지라면 drive.google 등등.
도메인을 요청했을 때의 절차
- 로컬 DNS서버 또는 /etc/hosts 파일에서 매핑되는 값을 찾아본다. 없으면 인터넷 DNS Server로 요청을 보낸다.
- 인터넷에 있는 Root DNS server에 의해 검색이 시작된다.
- .com DNS -> google DNS -> apps subdomain DNS.... 순서로 tree 타고 내려가듯 ip주소를 찾는다.
- 최종적으로 ip주소를 찾아서, 요청을 보낸 서버로 ip주소를 전달한다.
- 리턴값을 전달한 뒤, 로컬 DNS 서버에서 이 값을 캐싱한다. 캐시 시간은 몇 초 ~ 몇 분 정도로 조금씩 다를 수 있음.
로컬 DNS에서 이 값을 캐시하면, 같은 도메인을 다시 조회할 때 인터넷 DNS를 전부 타고 내려가서 가져올 필요가 없음.
사내 DNS 서비스를 예로 들어보자.
- nameserver로 DNS 서버를 생성했고, 해당 DNS 서버에는 여러 개의 subdomain을 포함한 주소가 나열되어 있다.
- web.mycompany.com / pay.mycompany.com ....
- 이렇게 DNS가 설정되어 있을 경우,
ping web
하면 name resolution에 실패하므로 에러가 난다.- 외부에서는 web.mycompany.com처럼 풀네임을 써서 접근해야 하니까 문제가 없지만, 사내에서는 굳이 매번 풀네임을 입력할 필요 없이 서브도메인 이름만으로 접근하도록 만들고 싶다.
ping web
명령어만으로ping web.mycompany.com
과 동일한 효과를 내게 하려면?
- DNS 서버에
search mycompany.com
를 추가로 등록해주면 된다. 이렇게 할 경우ping web
을 입력할 경우, 알아서 도메인을 찾아 실행해준다.- search에 여러 개의 도메인을 설정할 경우 - 위 예시처럼
search mycompany.com prod.mycompany.com
두 개의 도메인을 추가할 경우,ping web
을 하면 알아서 둘 중 어떤 도메인인지 찾아준다.
- search에 여러 개의 도메인을 설정할 경우 - 위 예시처럼
ping web.mycompany.com
풀네임을 입력한다 해도 문제없이 동작한다.
Record Types : DNS 서버에 저장되는 Record 종류를 의미함.
- A : ipv4 방식
- AAAA : ipv6 방식
- CNAME : Mapping One name to Other Name을 위한 용도로 사용함.
ping으로 DNS Resolution을 찾을 수 있지만, nslookup이나 dig을 사용할 수도 있다.
- nslookup이나 dig의 경우 local의 /etc/hosts 파일을 참조하지 않는다. 무조건 DNS server부터 찾아감.
Basics - Network Namespaces
컨테이너는 Namespace라는 underlying host에 의해 구별된다.
- host가 하나의 집이라고 비유한다면, namespace는 집 안의 여러 방을 의미한다.
- 방에서는 privacy가 보장되고, 그 안에서만 존재를 확인할 수 있는 것들이 있다. 반대로, 방 안에서는 바깥에서 무슨 일이 일어나고 있는지 알 수 없다.
- admin은 이 namspace 간 연결을 활성화하는 식의 작업이 가능하다.
컨테이너 프로세스를 생성할 때, 해당 프로세스는 완전히 독립적으로 실행되어야 한다. (다른 프로세스의 존재를 알아서는 안 된다.)
- 따라서 프로세스를 실행할 때 namespace로 프로세스를 감싼다.
하지만 host 입장에서는 여러 namespace의 프로세스를 확인해야 한다.
ps aux
명령어를 컨테이너에서 입력하면 프로세스가 하나만 보이지만ps aux
를 host에서 실행하면 여러 개의 프로세스를 확인할 수 있다.
host는 local area Network와 통신하기 위한 인터페이스를 가지고 있다.
- Routing table / ARP Table을 보유하고 있음.
컨테이너를 생성하고 namespace로 논리적 분리를 해냈어도, networking 연결은 필요하다.
- 그래서 virtual 인터페이스가 구현되어 있고, Routing Table과 ART Table이 만들어져 있다.
리눅스에서 새 namespace를 생성하는 명령어는 ip netns add <이름>
ip link
: 호스트에서 사용하는 network interface를 확인하기 위한 명령어.
- 특정 namespace 내부에서 명령어를 실행하려고 할 때에는
ip netns exec <ns> ip link
또는ip -n red link
명령어를 사용한다.
- namespace 내부에서 명령어를 실행할 경우, eth0 인터페이스가 확인되지 않는다.
ARP table이나 Routing table도 마찬가지.
- host에서 실행할 경우 entries가 보이지만
- container에서 실행할 경우 아무런 entity가 보이지 않는다.
다시말해 지금 namespace는 network connectivity가 없는 상태. network interface도 없고, host interface에 접근하지도 못한다.
- 우선 namespace 간 연결 = virtual Ethernet을 생성해서 virtual cable로 연결되게끔 세팅할 수 있다.
ip link add veth-red type veth peer name veth-blue
명령어로, 서로 연결된 두 개의 virtual ethernet을 생성한다.ip link set veth-red netns red
,ip link set veth-blue netns blue
명령어로 ethernet을 namespace에 연결한다.ip -n red addr add 192.168.15.1 dev veth-red
,ip -n blue addr add 192.168.15.2 dev veth-blue
명령어로 각각의 ethernet에 ip주소를 할당한다.ip -n red link set veth-red up
,ip -n red link set veth-blue up
명령어로 두 개의 ethernet을 활성화한다.
- 연결이 완료되면, 두 개의 namespace에서는 ARP table에 서로의 정보가 저장되어 있다. 통신할 수 있음.
- 두 namespace 간 연결 정보는 host에서는 알 수 없다. veth-red / blue에 할당한 ip주소는 host의 ARP table에 등록되어 있지 않기 때문.
여러 개의 network를 연결하려면?
- 물리서버 연결하는 것과 마찬가지다. Virtual Switch를 생성한 뒤, namespace를 연결하는 것.
- virtual switch를 생성하는 솔루션들 - linux bridge, Open vSwitch 등등.. 강의에서는 Linux Bridge 사용함.
ip link add v-net-0 type bridge
명령어로 switch interface를 생성한다.- host 입장에서는 인터페이스, namespace 입장에서는 switch 역할을 한다.
ip link set dev v-net-0 up
으로 인터페이스를 활성화한다.- namespace를 직접 연결하는 대신 switch를 사용해 통신할 예정이므로, direct connection이었던 cable을 삭제한다.
ip -n red link del veth-red
- 각각의 virtual ethernet을 생성한 뒤, 다른 namespace ethernet에 직접 연결하는 게 아니라 brigde에 연결하는 cable을 생성한다.
- 위 작업은 전부 virutal ethernet / switch로 이루어졌기 때문에, host에서 그냥 조회하려고 하면 존재가 확인되지 않는다.
- 따라서 host와 virtual Switch를 연결해야 한다. 정확히는 host에 virtual switch ip주소를 등록한다.
- 연결한 뒤 각 namespace로 ping 요청을 보내면, 정상적으로 응답값이 오는 것을 확인할 수 있다.
이상의 작업은 전부 Private Network를 설정하는 작업이었다. host 내부의 리소스 간 통신, host와 내부 리소스의 통신을 설정하는 작업임.
그럼 namespace에서 host 외부의 네트워크 (인터넷)와 연결하려면?
- Logical Schema를 그려보면, 외부 네트워크와 연결되어 있으면서 namespace와도 연결할 수 있는 영역은 host machine. 이 host machine이 gateway 역할을 해야 한다.
- blue namespace에서 host의 gateway에 접근 가능하도록 하려면
ip netns exec blue ip route add 192.168.1.0/24 via 192.168.15.5
명령어를 실행한다.- virtual switch의 ip주소를 host의 routing table에 추가한 것.
하지만 이 설정만으로 요청을 보내면, 요청은 가는데 응답이 오지 않는다. 요청을 보냈는데, 응답을 받은 뒤 gateway가 어디로 값을 보내야 할지 모르기 때문.
- NAT를 세팅해 준다.
iptable -t nat -A POSTROUTING -s 192.168.15.0/24 -j MASQUERADE
명령어로 NAT을 세팅해준다.
인터넷에 직접 연결하고 싶다면, route에 default gateway를 host machine과 연결하고 있는 virtual switch로 등록하면 된다.
- 현재 namespace는 host가 접근할 수 있는 모든 영역에 접근이 가능한 상태
- host의 default gateway 설정이 인터넷과 연결되어 있는 상태라면, host의 default gateway로 접근할 수 있게 namespace 내부 요청이 virtual switch를 통과하도록 하면 된다.
외부에서 해당 namespace로 요청을 보내고 싶은 경우 (또는 응답을 전송해야 하는 경우): 두 가지 방법이 있다.
- 해당 private network의 정보를 알려준다. 192.168.15.2로 요청을 보내려면, 192.168.1.2 라는 eth0 인터페이스 ip주소로 요청을 보내면 된다고 routing table에 추가해준다.
- 직접 포트포워딩. 해당 ip주소의 80포트에서 오는 메시지는 blue namespace의 80포트로 포트포워딩하는 것.
iptables -t nat -A PREROUTING --dport 80 --to-destination 192.168.15.2:80 -j DNAT
Basics - Networking in Docker
도커가 설치된 host 머신이 있다고 가정하자.
- 네트워크 인터페이스 eth0 : 192.168.1.10 ip주소.
docker run 옵션에서 네트워크를 선택할 수 있다.
- None : 어디에도 연결하지 않은 채 컨테이너가 실행된다.
- Host : host 네트워크에 연결된다. 예시의 경우 host의 80번 포트로 접근하면 컨테이너로 바로 연결됨. 대신, 80번 포트를 사용하고 있으므로 다른 프로세스가 포트 80번을 사용할 수 없다.
- bridge : docker 컨테이너 전용 private network를 생성하고, private network에서 전용 ip를 발급받아 사용하는 구조.
도커가 설치될 때 기본적으로 internal private network인 bridge 가 자동으로 생성된다.
- 도커는 bridge로 인식하고, host에서는 docker0 로 인식한다.
- 즉, 리눅스에서 bridge 타입의 namespace 생성하는 것과 유사함
- docker를 설치한 상황에서 host의 docker0 네트워크 인터페이스는 down 상태다.
- bridge는 내부적으로는 switch 역할을 하므로, container가 생성될 경우 컨테이너와 연결된다.
- 예시의 경우 bridge는 172.17.0.1 ip주소를 가지고 있고, namespace로 docker id값을 부여한 컨테이너를 생성한다.
- 컨테이너와 bridge 연결 방식은 위에서 리눅스 기준으로 설명한 것과 동일하다.
- container와 연결할 수 있는 virtual Cable을 생성한다.
ip link
로 호스트에서 확인해보면 vethbb1c343@i7 이라는 인터페이스를 확인할 수 있고ip -n b3165... link
로 컨테이너 namespace에서 확인해보면 eth0@if8 이라는 인터페이스를 확인할 수 있다.
- container id를 namespace로 해서
ip -n b3165... addr
로 해당 namespace에 할당된 ip주소도 볼 수 있는데, 예시에서는 172.17.0.3 으로 할당된 것을 볼 수 있다.
- 위와 같은 방식으로, docker는 컨테이너를 생성할 때마다 virtual cable을 생성하고 연결한다.
- 이렇게 되면, 모든 container끼리는 서로 통신할 수 있는 구조가 된다.
- virtual cable Pair는 odd / even number로 확인 가능하다. 위 예시의 경우 7-8, 11-12, 9-10 형태로 관계가 있으며, container 측이 짝수, switch 측이 홀수다.
지금까지의 설정은 'host + docker container 간 통신은 가능하지만, 외부에서 docker container 프로세스에는 접근 불가' 구조.
- docker의 port-forwarding 기능: host의 특정 포트를 container의 특정 포트로 매핑하는 기능.
docker run -p 8080:80 nginx
로 컨테이너를 생성한 뒤curl http://192.168.1.10:8080
으로 접근하면 nginx가 실행된다.
내부적으로는 iptable 명령어의 -Dport와 --to-the-destination 옵션을 사용해서 포트 매핑을 지원하고 있다.
iptables -nvL -t nat
명령어로 확인해보면, docker에서 생성한 매핑을 확인할 수 있음.
Basics - CNI (Container Networking Interface)
Linux에서 namespace 생성하고, host와 연결하고, 외부 통신이 가능하게 하는 작업.
- container를 지원하는 여러 솔루션들이 사소한 차이만 있을 뿐, 로직의 순서는 거의 동일하다.
- docker를 대표적으로 소개했지만, rkt나 mesos, k8s까지도 절차는 거의 비슷함.
그럼 차라리 Standard를 지정해서
- 프로그램이 따라야 하는 규칙은 무엇이며
- Container runtime이 어떻게 invoke할 것인지
single set of solution을 사용하면 어떨까?
-> CNI의 등장
CNI : Set of Standards that define how programs should be developed to solve networking challenges in a container runtime environment
- 이 인터페이스를 지원하는 솔루션 (bridge 같은 프로그램)을 plugin이라고 부른다.
- 따라서 plugin은 어떤 기능을 지원해야 하는지, container runtime은 어떻게 해당 기능을 호출해야 하는지를 지정한 규약.
- 위쪽: Container Runtime에서 지원해야 하는 기능
- 아래쪽: plugin에서 지원해야 하는 기능
- Bridge 같은 Supported Plugins도 있고, cilium 같은 3rd party program도 있다.
- 특이점: Docker는 CNI Implementation이 없다. CNM이라는 자체 container network model을 사용하고 있음. CNI와는 다른 방식으로 networking challenges를 해결하는 방식이라고 보면 됨.
물론 docker를 아예 못 쓰는 건 아니다. 우회해서 사용하는 방법은 얼마든지 있음
docker run --network=none nginx
로 이미지를 생성한 뒤,bridge add 2e34dcf34 /var/run/netns/2e34dcf34
같은 명령어를 쓰는 식으로.- k8s가 한동안 이 방식을 썼었다. container runtime 자체는 docker를 써서 생성하고, CNI를 따로 붙여서 네트워크에 활용하는 식.
- -> k8s에서는 어떻게 networking 이슈를 해결하는지 알아볼 예정.
'학습일지 > kubernetes' 카테고리의 다른 글
if kakao 2021 - k8s Cluster 확장, 어디까지 알아보고 오셨어요? (0) | 2022.06.02 |
---|---|
CKA 대비 kubernetes 스터디 - 8. Networking (2) (0) | 2022.04.08 |
CKA 대비 kubernetes 스터디 - 7. Storage (0) | 2022.04.02 |
CKA 대비 kubernetes 스터디 - 6. Security (2) (0) | 2022.03.30 |
CKA 대비 kubernetes 스터디 - 6. Security (1) (1) | 2022.03.25 |