Raspberry pi

라즈베리파이 쿠버네티스 ConfigMap으로 변수 전달하기 : 동적으로 적용하기

[혜안] 2022. 8. 5. 00:49
728x90

라즈베리파이4에 쿨링팬 제어 모듈을 nodejs로 만들고, 이걸 쿠버네티스 데몬셋으로 상시 동작하도록 꾸미고 있습니다.

 

이전 글 참조.

2022.07.29 - [Raspberry pi] - 라즈베리파이 온도에 따라 냉각팬 조절하기 : nodejs

 

라즈베리파이 온도에 따라 냉각팬 조절하기 : nodejs

 라즈베리파이4를 중고로 영입했는데, 케이스와 냉각팬을 같이 주셨네요. 원래는 생각이 없었다가 꽁짜로 생겼으니 일단 달아보았는데요. 냉각팬이 생각보다 소음이 크더라구요. 온도가 높을

viewise.tistory.com

2022.08.04 - [Raspberry pi] - 라즈베리파이 쿨링팬 제어 쿠버네티스 데몬셋으로 실행하기 : nodejs, GPIO 접근권한

 

라즈베리파이 쿨링팬 제어 쿠버네티스 데몬셋으로 실행하기 : nodejs, GPIO 접근권한

라즈베리파이에 달린 쿨링팬을 CPU 온도에 따라 제어하기 위한 모듈을 nodejs 기반으로 제작했습니다. 2022.07.29 - [분류 전체보기] - 라즈베리파이 온도에 따라 냉각팬 조절하기 : nodejs 라즈베리파이

viewise.tistory.com

 

이어서, 데몬셋으로 동작중인 쿨링팬 제어모듈의 쿨링팬 동작 조건을 변수로 분리하여 환경변수에서 처리하도록 개선했습니다.

10초 간격으로 체크하게 했더니, 임계온도인 55도에서 10초 간격으로 쿨링팬이 돌다 말다를 반복하여 별로 효율적으로 보이지 않았기 때문인데요.

어떤게 적정값인지 찾기는 어려우니 일단 변수로 빼서 설정이 가능하게 하는게 1차 목표였고,

그렇게 분리된 설정파일을 쿠버네티스에서 환경변수로 전달하는게 그다음 목표입니다.

 

일단 설정해야할 변수값은 3개입니다.

1. 임계 온도 (cri_temp_cels)

2. 온도 체크 시간간격 (interval_sec)

3. 임계 온도를 넘었을 때에 쿨링팬이 돌아가는 동작시간 (running_sec)

 

그리고 2가지 조건이 있습니다.

1. ConfigMap이 존재하지 않아도 기본 값으로 동작 할 것

2. 데몬이 동작 중에도 변수를 바꾸면 재기동 없이 바로 반영될 것

 

1번 조건을 위해서는 config 파일을 기본으로 넣어놓으면 될 것 같습니다.

2번 조건을 위해서는 ConfigMap을 바꾸면 위 config 파일을 바꿔주도록 하면 될 것 같네요.

그리고 바뀐 config 파일을 nodejs에서 감지해서 설정값들을 reload 하면 될 것 같습니다.

 

먼저 nodejs 소스를 수정합니다.

config파일 경로는 현재 동작중인 경로 기준 conf/fan-config 로 정했습니다.

const config_file = __dirname + '/conf/fan-config'

별도 폴더로 분리한 이유는,

아래 나오겠지만 ConfigMap을 볼륨마운트하여 파일로 떨굴 계획인데, 볼륨 마운트 시 별도 폴더를 지정해야 하기 때문입니다.

 

그다음으로 config 파일을 읽어오는 function을 작성합니다.

var cri_temp_cels = 0;
var running_sec = 0;
var interval_sec = 0;

function read_config(event) {
    i('read config file!!');
    var JSON_config = fs.readFileSync(config_file, 'utf8');

    var config = JSON.parse(JSON_config);

    cri_temp_cels = parseInt(config.cri_temp_cels);
    running_sec = parseInt(config.running_sec);
    interval_sec = parseInt(config.interval_sec);

    i('cri_temp_cels', cri_temp_cels);
    i('running_sec', running_sec);
    i('interval_sec', interval_sec);
}

 

임계온도, 동작시간, 체크주기 변수값들을 반영해야 하므로 메인 소스 수정도 필요합니다.

async function get_temp() {

    try {
        const temp = await exe_shell("cat /sys/class/thermal/thermal_zone0/temp");

        if(temp/1000 > cri_temp_cels){
            fan.writeSync(1);
            setTimeout(get_temp, 1000*running_sec);
        }
        else{
            fan.writeSync(0);
            setTimeout(get_temp, 1000*interval_sec);
        }
        i("cpu temperture", temp/1000, fan.readSync());

    }catch(error) {
        e(error.toString());
    }

}

 

마지막으로,

config 파일에 변경이 발생하면 파일을 다시 읽어오도록 read_config function을 걸어놓습니다.

fs.watchFile(config_file, read_config);

 

이제 소스는 완성했고,

config 파일을 생성합니다.

$ mkdir conf
$vi conf/fan-config

{
"cri_temp_cels":"55",
"running_sec":"300",
"interval_sec":"40"
}

JSON 포멧으로 작성했습니다.

 

그다음은 ConfigMap을 생성해줍니다.

편의상 fan-config 파일을 읽어서 만들어줍니다.

$ k create configmap fan-config --from-file conf/fan-config --dry-run=client -o yaml > fan-config.yaml

생성된 yaml 파일을 열어봅니다.

$ cat fan-config.yaml
apiVersion: v1
data:
  fan-config: |
    {
    "cri_temp_cels":"55",
    "running_sec":"300",
    "interval_sec":"40"
    }
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: fan-config

구동했을 때에 ConfigMap값을 conf 경로에 파일로 썼는지 구분을 위해 interval_sec 값을 30으로 바꿔줍니다.

$ cat fan-config.yaml
apiVersion: v1
data:
  fan-config: |
    {
    "cri_temp_cels":"55",
    "running_sec":"300",
    "interval_sec":"30"
    }
kind: ConfigMap
metadata:
  creationTimestamp: null
  name: fan-config

그리고 일단 ConfigMap을 생성합니다.

$ k apply -f fan-config.yaml

$ k get cm
NAME               DATA   AGE
fan-config         1      21m

$ k describe cm fan-config
Name:         fan-config
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
fan-config:
----
{
"cri_temp_cels":"55",
"running_sec":"300",
"interval_sec":"30"
}


BinaryData
====

Events:  <none>

잘 생성되었네요.

 

마지막입니다.

DaemonSet yaml 파일을 수정합니다.

ConfigMap을 conf 경로에 볼륨마운트해서 fan-config 파일로 쓰도록 설정합니다.

$ vi fan_controller_daemon.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  creationTimestamp: null
  labels:
    app: fan-controller
  name: fan-controller
spec:
  selector:
    matchLabels:
      app: fan-controller
  template:
    metadata:
      creationTimestamp: null
      labels:
        app: fan-controller
    spec:
      containers:
      - name: fan-controller
        image: becans/fan-controller:arm7.0.6
        securityContext:
          privileged: true
        volumeMounts:
          - name: gpiopath
            mountPath: /sys
          - name: fanconfig
            mountPath: /usr/src/app/conf
      volumes:
      - name: gpiopath
        hostPath:
          path: /sys
          type: Directory
      - name: fanconfig
        configMap:
          name: fan-config

mountPath가 /usr/src/app/conf 인 이유는,

Dockerfile의 WORKDIR이 /usr/src/app 이기 때문입니다.

이제 수정한 모듈을 도커로 빌드해서 올리고, 쿠버네티스에서 내려받아 구동하겠습니다.

$ sudo docker build -t becans/fan-controller:arm7.0.6 .
Sending build context to Docker daemon  47.62kB
Step 1/6 : FROM node:12
 ---> 89df41ef0cd7
Step 2/6 : WORKDIR /usr/src/app
 ---> Using cache
 ---> 9352facdeab0
Step 3/6 : COPY package*.json ./
 ---> Using cache
 ---> 37b4bb72ae02
Step 4/6 : RUN npm install
 ---> Using cache
 ---> fdb3f3a065dd
Step 5/6 : COPY . .
 ---> 671c45afc7f3
Step 6/6 : CMD [ "node", "fan_controller.js" ]
 ---> Running in 07fa9d887383
Removing intermediate container 07fa9d887383
 ---> 8caee1f06a38
Successfully built 8caee1f06a38
Successfully tagged becans/fan-controller:arm7.0.6

$ sudo docker push becans/fan-controller:arm7.0.6
The push refers to repository [docker.io/becans/fan-controller]
e9c2c3221e58: Pushed
2cb8d67e3877: Layer already exists
f1298c154221: Layer already exists
13ae017bd412: Layer already exists
793fbcea9e2a: Layer already exists
4945a11d336c: Layer already exists
ac0e08eec46a: Layer already exists
b711f0c07b7c: Layer already exists
3c3c4870574d: Layer already exists
bcf9ec9b83fa: Layer already exists
952036bb3ee8: Layer already exists
e52411dadecd: Layer already exists
37b5ba013286: Layer already exists

$ k apply -f fan_controller_daemon.yaml
daemonset.apps/fan-controller configured

$ k get pod -w
NAME                   READY   STATUS        RESTARTS   AGE
fan-controller-47zp5   1/1     Terminating   0          15m
fan-controller-47zp5   0/1     Terminating   0          15m
fan-controller-47zp5   0/1     Terminating   0          15m
fan-controller-47zp5   0/1     Terminating   0          15m
fan-controller-gbmkx   0/1     Pending       0          0s
fan-controller-gbmkx   0/1     Pending       0          0s
fan-controller-gbmkx   0/1     ContainerCreating   0          0s
fan-controller-gbmkx   1/1     Running             0          11s

잘 올라갔습니다.

(사실 여기까지 한방에 됐을 리 없고 수많은 시행착오 끝에 성공한 것만 나열하고 있습니다. ㅜㅜ)

 

이제 pod 안으로 들어가서 ConfigMap 값으로 설정이 먹었는지 확인해보겠습니다.

$ k exec -it fan-controller-gbmkx -- bash
root@fan-controller-gbmkx:/usr/src/app# cat conf/fan-config
{
"cri_temp_cels","55",
"running_sec":"300",
"interval_sec":"30"
}

잘 바뀌어 있네요.

사실 ConfigMap으로 볼륨마운트를 하면 숨어있는 파일이 더 있습니다.

root@fan-controller-gbmkx:/usr/src/app# ls -al conf/
total 12
drwxrwxrwx 3 root root 4096 Aug  4 14:52 .
drwxr-xr-x 1 root root 4096 Aug  4 14:49 ..
drwxr-xr-x 2 root root 4096 Aug  4 14:52 ..2022_08_04_14_52_59.3663318380
lrwxrwxrwx 1 root root   32 Aug  4 14:52 ..data -> ..2022_08_04_14_52_59.3663318380
lrwxrwxrwx 1 root root   17 Aug  4 14:49 fan-config -> ..data/fan-config

이런 모양으로요..

 

이제 pod가 러닝 중에 변수값이 실시간 변경되는지 확인이 필요합니다.

임계온도를 50도로 바꾸고 반영되는지 보겠습니다.

$ vi fan-config.yaml
apiVersion: v1
data:
  fan-config: |
    {
    "cri_temp_cels":"50",
    "running_sec":"300",
    "interval_sec":"30"
    }
kind: ConfigMap
metadata:  
  name: fan-config
  namespace: default
  
$ k apply -f fan-config.yaml
configmap/fan-config configured

ConfigMap을 변경했습니다.

root@fan-controller-gbmkx:/usr/src/app# cat conf/fan-config
{
"cri_temp_cels":"55",
"running_sec":"300",
"interval_sec":"30"
}

바로 pod 안으로 들어가보면 값이 안 변해 있습니다.

root@fan-controller-gbmkx:/usr/src/app# cat conf/fan-config
{
"cri_temp_cels":"50",
"running_sec":"300",
"interval_sec":"30"
}

약 2분정도 경과되니 변경되어 있네요.

모듈에도 반영되었는지 로그를 확인해보겠습니다.

root@fan-controller-gbmkx:/usr/src/app# cat logs/fan_controller.2022-08-04.log
{"level":"info","message":{"contents":[["cpu temperture",54.043,0]],"timestamp":"2022-08-04 15:30:14"}}
{"level":"info","message":{"contents":[["cpu temperture",55.504,1]],"timestamp":"2022-08-04 15:30:44"}}
{"level":"info","message":{"contents":[["read config file!!"]],"timestamp":"2022-08-04 15:32:39"}}
{"level":"info","message":{"contents":[["cri_temp_cels",50]],"timestamp":"2022-08-04 15:32:39"}}
{"level":"info","message":{"contents":[["running_sec",300]],"timestamp":"2022-08-04 15:32:39"}}
{"level":"info","message":{"contents":[["interval_sec",30]],"timestamp":"2022-08-04 15:32:39"}}
{"level":"info","message":{"contents":[["cpu temperture",52.095,1]],"timestamp":"2022-08-04 15:35:44"}}

config파일 변경을 감지하고 변수값을 다시 읽었습니다.

그리고 쿨링팬 동작기준이 50도로 바뀌어서 52도인데도 팬을 돌리고 있네요.

 

끝입니다.

작업했던 소스입니다.

https://github.com/becans/raspberry_fan_controller.git

 

GitHub - becans/raspberry_fan_controller: nodejs

nodejs. Contribute to becans/raspberry_fan_controller development by creating an account on GitHub.

github.com

 

도커 이미지입니다.

becans/fan-controller Tags | Docker Hub

 

Docker Hub

 

hub.docker.com

 

728x90