Miscellaneous

[삽질의 추억] 쿠버네티스나 도커 안에서 WOL(Wake On Lan) 안될 때

[혜안] 2022. 8. 23. 14:11
728x90

쿠버네티스나 도커와 같이 컨테이너 안으로 어플리케이션을 넣으면

WOL 처럼 내부 네트워크에서만 동작되는 기능은 동작하지 않습니다.

 

처음부터 알고 있었던건 아니구요.

잡다한 홈서버를 라즈베리파이에 Nodejs 로 운용하다가 쿠버네티스에 넣었더니

2가지 기능이 안되더라구요.

첫번째는 GPIO 컨트롤,

두번째는 WOL 이었습니다.

 

GPIO 컨트롤은 아래와 같이 해결하였구요.

2022.07.31 - [Raspberry pi] - 라즈베리파이 쿨링팬 제어 nodejs 도커로 실행하기 :GPIO 접근권한

 

라즈베리파이 쿨링팬 제어 nodejs 도커로 실행하기 :GPIO 접근권한

쿨링팬 제어하는 nodejs 모듈을 열심히 만들었고, 이제 상시 돌아가도록 쿠버네티스 daemon으로 등록해야 합니다. 그 전에 일단 도커 이미지화를 해야 쿠버네티스에 올릴 수 있으니 도커 이미지부

viewise.tistory.com

 

이번에 할 이야기가 WOL 입니다.

아시겠지만 컨테이너환경에서 어플리케이션을 구동하게 되면,

이 어플리케이션은 비록 호스트의 자원을 빌려쓰는 신세지만 그럼에도 마치 별개의 또다른 호스트처럼 취급됩니다.

특히 네트워크의 경우 가상의 별도 네트워크를 생성하기 때문에,

원칙상 호스트의 네트워크와 통신이 되지 않습니다.

그러니까 내부 네트워크에서만 호출 할 수 있는 WOL 같은 기능은 동작하지 않았던 것입니다.

컨테이너를 외부 네트워크라고 인식하는 거죠.

 

저의 경우도 그러했습니다.

집에 내부적으로 꾸며놓은 네트워크 대역은 192.168.x.x 대역입니다.

그리고 쿠버네티스에 flannel을 통해 구성한 네트워크 대역은 10.244.x.x 이구요.

아마 쿠버네티스 컨테이너를 생성한 상태라면,

호스트에서 ifconfig를 쳤을 때에 아래와 같이 보일껍니다.

eht0는 호스트의 IP입니다.

그리고 맨 아래에 vethxxxxxxxx 가 있습니다.

아마도 virtual ethernet 의 약자일텐데요.

이렇게 호스트 입장에서 컨테이너는 본인의 네트워크대역이 아닌 별도의 네트워크 IP 대역으로 취급되니,

당연히 WOL의 매직패킷은 전달이 안됐던 겁니다.

그럼 방법은?

 

docker나 kubernetes나 호스트의 네트워크로 컨테이너를 생성할 수 있는 기능은 있습니다.

docker의경우 --net=host 옵션을 주고 run하면 됩니다.

kubernetes의 경우는 아래와 같이 yaml파일에 기술해주면 됩니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: wol
  name: wol
spec:
  selector:
    matchLabels:
      app: wol
  template:
    metadata:
      labels:
        app: wol
    spec:
      hostNetwork: true
.
.
.

hostNetwork: true는 생성할 Pod를 가상네트워크가 아닌 호스트의 네트워크를 이용하겠다는 설정입니다.

실행하면 아래와 같이 보입니다.

$ k get pod -o wide
NAME                               READY   STATUS    RESTARTS   AGE     IP             NODE           NOMINATED NODE   READINESS GATES
coin-gather-675dfd9486-cj4q7       1/1     Running   0          18h     10.244.0.120   raspberrypi4   <none>           <none>
coin-trader-7cbd7d447-6mfjt        1/1     Running   0          18h     10.244.0.121   raspberrypi4   <none>           <none>
fan-controller-chftl               1/1     Running   0          7d12h   10.244.1.3     raspberrypi2   <none>           <none>
fan-controller-spgm8               1/1     Running   0          12d     10.244.0.55    raspberrypi4   <none>           <none>
home-server-54959467d4-zrtcf       1/1     Running   0          10h     10.244.0.122   raspberrypi4   <none>           <none>
mariadb-custom-5dbf65bc5b-65bhz    1/1     Running   0          5d10h   10.244.0.117   raspberrypi4   <none>           <none>
plug-controller-6764f8c8b4-db7wb   1/1     Running   0          5d10h   10.244.1.10    raspberrypi2   <none>           <none>
testshell                          1/1     Running   0          16h     10.244.1.18    raspberrypi2   <none>           <none>
wol-597cd6f6b7-c94rk               1/1     Running   0          10h     192.168.0.20   raspberrypi2   <none>           <none>

IP 열을 보시면 맨 밑에 wol Pod만 192.168.x.x 대역입니다. 호스트의 IP 대역을 할당 받았습니다.

당연히 WOL 기능은 정상적으로 동작하게 되었구요.

원래는 wol이 home-server 내에 있었지만, 호스트 네트워크 자원을 사용하는게 여러모로 부담이기 때문에 wol 기능만 잘라서 별도 Pod로 만들었습니다.

home-server가 wol을 호출하는 구조입니다.

wol 메인소스는 아래와 같습니다.

const WOL = 1;

const server = net.createServer(function(client){
    logger.info('Client connected');
    client.on('data', function(data){

        var msg = JSON.parse(data);
        if(msg.type = WOL){

            logger.info('call wol:'+ msg.value);
            wol.wake(msg.value,{}, function(err){
                if(err) {
                    logger.error(err);
                }
            });
            var res = {'type':WOL,'value':'WOL Sent'};
            client.write(JSON.stringify(res));
        }
    });
    client.on('end', function(){
        logger.info('Client disconnected');
    });
});

server.listen(8107, function(){
    logger.info('Server listening for connections');
});

 

yaml 은 아래와 같이 기술되어 있습니다.

apiVersion: v1
kind: Service
metadata:
  name: wol-svc
spec:
  selector:
    app: wol
  ports:
    - port: 8107
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: wol
  name: wol
spec:
  selector:
    matchLabels:
      app: wol
  template:
    metadata:
      labels:
        app: wol
    spec:
      hostNetwork: true
      containers:
      - name: wol
        image: private.repo:8888/wol:0.1
        env:
        - name: TZ
          value: Asia/Seoul

여기서 궁금한 점이 있을 수 있는데요.

home-server는 가상네트워크에 있습니다.

그리고 wol은 호스트 네트워크를 사용하죠.

서로 네트워크 망이 다른데 과연 호출이 될까? 싶습니다.

결론만 말씀드리면, home-server에서는 wol-svc:8107로 호출을 하면, 정상적으로 호출이 됩니다.

어찌되었든, 코딱지만한 어플리케이션들도 시간이 지나면서 자연스럽게 MSA로 가고있네요.

.

.

.

좀더 심오한 삽질을 해봅니다.

Kubernetes의 컨테이너 네트워크 구성이 궁금합니다.

우선 컨테이너 내부의 네트워크 인터페이스를 확인해봅니다.

$ k exec -it testshell -- 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
3: eth0@if23: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group default
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.1.18/24 brd 10.244.1.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::ac44:97ff:fe6d:810c/64 scope link
       valid_lft forever preferred_lft forever

eth0@if23 이라는 인터페이스가 보입니다.

그냥  eth0가 아니라 뒤에 @가 붙는 것이 뭔가 링크를 뜻하는것 같네요.

 

다시 해당 컨테이너가 있는 호스트에서 네트워크 인터페이스를 확인해봅니다.

$ ip addr
    .
    .
    .
23: vethd78c2f43@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue master cni0 state UP group default
    link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff link-netns cni-63fab09e-02e2-8dd9-7369-04562e9c3ab7
    inet 169.254.189.81/16 brd 169.254.255.255 scope global noprefixroute vethd78c2f43
       valid_lft forever preferred_lft forever
    .
    .
    .

23번 index에 vethd78c2f43@if3 이라고 있네요.

아하~

컨테이너에서는 3번 인터페이스에 eth0@if23

호스트에서는 23번 인터페이스에 vethd78c2f43@if3 

서로 링크되어 있네요.

즉, testshell 이라는 pod는 호스트에 vethd78c2f43 라는 가상 인터페이스를 생성해서, 컨테이너 안에서 그걸 eth0으로 사용하고 있습니다.

그냥 서로 한 쌍으로 링크되어 있다. 라고 얘기하더라구요.

 

그럼 또 의문점이 있습니다.

Pod 하나당 이렇게 가상 인터페이스 하나씩이 할당되어 있으면,

Pod 끼리의 통신은 어떻게 하는걸까?

 

분명 home-server 에서 wol-svc 라는 서비스이름으로 바로 호출이 됩니다.

이것 역시 cni 인터페이스가 bridge하여 서로를 연결시켜준다고 합니다.

bridge-utils를 설치하면, 쉽게 확인이 됩니다.

$ apt install bridge-utils

$ brctl show cni0
bridge name     bridge id               STP enabled     interfaces
cni0            8000.325c2abceef8       no              veth120d9d2c
                                                        veth19ef9a07
                                                        veth816e63b2
                                                        veth841caf25
                                                        vetha561f213
                                                        vethab01d4d5
                                                        vethb6cd6ed0
                                                        vethedbdc1e5

 

cni0 인터페이스가 가상 인터페이스 8개를 브릿지 시켜주고 있네요.

 

뭐 아주 깊이 파보지는 않았지만, 대략 어떤 느낌으로 네트워크를 구성하고 있는지는 감이 좀 옵니다.

 

컨테이너 안에서 wol 한번 해보겠다고 멀리도 왔네요.

 

728x90