반응형
작성일: 2024년 11월 17일

 

내가 eBPF, XDP의 네트워크 패킷 처리 성능을 측정하다가 목표한 성능 값보다 낮게 나와서 다른 사람들은 얼마나 성능이 나오는지 궁금해서 찾아본 자료이다.

다른 사람들이 테스트한 eBPF 성능 측정 값을 보면, 꽤 좋은 수치가 나온다.

내가 너무 구형 장비에서 테스트한 것이 성능 저하의 원인인 듯하다.

(아래 cilium이 구성한 HW 장비보다 7년 정도 더 오래된 CPU를 사용했음 ㅠㅠ)

 

 

https://cilium.io/blog/2021/05/11/cni-benchmark/

 

CNI Benchmark: Understanding Cilium Network Performance

As more crucial workloads are being migrated to Kubernetes, network performance benchmarks are becoming an important selection criter...

cilium.io

 

네트워크 처리 성능을 끌어올리기 위해 eBPF, XDP를 사용하는 것에 대해 의심할 필요는 없어 보인다.

 


 

반응형
작성일: 2024년 11월 13일

 

 

Intel Xeon CPU의 다양한 제품 라이업을 보면, 어느 제품을 구입해서 써야 할지 막막할 때가 있다.

이럴 때, 각 CPU 모델의 성능 측정 값을 알면 딱 좋은데, 다행스럽게도 Intel이 제공하는 CPU 모델별 성능 정보 테이블이 있다. 

 

https://www.intel.com/content/dam/support/us/en/documents/processors/APP-for-Intel-Xeon-Processors.pdf

 

 

참고 정보

Intel은 MIPS 정보는 제공하지 않고 GFLOPS, APP 성능 지표만 제공한다고 한다. (아래 웹 사이트에 자세한 정보가 있다)

https://www.intel.com/content/www/us/en/support/articles/000057052/processors/intel-xeon-processors.html

 

What Is MIPS (Million Instructions Per Second) Number for Intel®...

Performance information available for Intel® Xeon® Processors

www.intel.com

 

반응형
작성일: 2024년 8월 14일

 

 

CPU 온도 모니터링

$ apt install lm-sensors

$ sensors-detect --auto

$ systemctl restart module-init-tools

$ watch sensors

 

 

프로세스 별 Network traffic 모니터링

$ apt install nethogs

$ nethogs eth0

$ apt install iftop

$ iftop -i eth0

 

 

 

 

 

 

 

 

 


 

반응형
작성일: 2024년 8월 14일

 

SSD, HDD 등 저장 장치에 대해서 Read, Write 성능을 측정하기 위해서는 dd 같은 명령어를 사용하면 성능 수치까지 나오지만

실제로 File copy를 하면서 실사용 환경과 같이 Read, Write 성능을 측정해보고 싶다면 아래 shell script를 활용해보는 것을 추천한다.

 

#!/bin/bash

fallocate -l 1G sample-big.file

CNT=0
while true; do
        CUR_DATE=`date +%4Y-%m-%d-%H-%M-%S`
        CNT=$(( ${CNT}+1 ))
        echo ""
        echo ""
        echo "============= TEST START ================"
        echo "Test time:  ${CUR_DATE}"
        echo "Test count: $CNT"
        START_TIME=`date +%s%N`
        cp sample-big.file sample-big-1.file
        sync
        END_TIME=`date +%s%N`
        INTERVAL_TIME_NANO=$(( ${END_TIME}-${START_TIME} ))
        INTERVAL_TIME_NANO_COMMA=$(numfmt --grouping ${INTERVAL_TIME_NANO})
        INTERVAL_TIME_MILI=$(( ${INTERVAL_TIME_NANO}/1000000 ))
        INTERVAL_TIME_MILI_COMMA=$(numfmt --grouping ${INTERVAL_TIME_MILI})
        echo "Total execution time: ${INTERVAL_TIME_NANO_COMMA} (nanosec)   ${INTERVAL_TIME_MILI_COMMA} (milisec)"
        BPS=$(( 1073741824*1000000/${INTERVAL_TIME_NANO} ))
        BPS_COMMA=$(numfmt --grouping ${BPS})
	echo "IO BPS: ${BPS_COMMA} (KBytes / Sec)"
        rm sample-big-1.file
        sync
        echo "============= TEST END =================="
        echo ""
        echo ""
        sleep 2
done

 

만약, Read, Write 부하를 더 많이 주고 싶다면 `fallocate` 명령으로 더 큰 사이즈의 file을 테스트하도록 하면 된다.
(위 코드에서 1G를 10G 또는 100G로 수정하면 됨)

 

위 스크립트를 실행하면, 아래와 같이 테스트가 되면서 출력물이 나온다.

 

$ my-run.sh

============= TEST START ================
Test time:  2024-08-14-10-58-10
Test count: 1
Total execution time: 1,400,259,865 (nanosec)   1,400 (milisec)
IO BPS: 766,816 (KBytes / Sec)
============= TEST END ==================




============= TEST START ================
Test time:  2024-08-14-10-58-14
Test count: 2
Total execution time: 758,501,406 (nanosec)   758 (milisec)
IO BPS: 1,415,609 (KBytes / Sec)
============= TEST END ==================

... 중간 생략 ...

 

 

여담:
내가 일하고 있는 연구소에서 사용중인 Ubuntu 18.04 장비 중에서 Linux 4.15.0-143-generic 커널 버전을 사용하는 모델만
SSD I/O와 HDD I/O 성능이 100분의 1 수준으로 떨어지는 증상이 있었다.
이를 쉽게 재현하기 위해서 위와 같은 Script를 만들었다.
위 IO 과부하 테스트를 해본 결과, 아래와 같은 Linux kernel version 들이 모두 IO 성능이 급격하게 떨어지는 현상이 있다.

4.15.0-136-generic   -> 정상
4.15.0-137-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-138-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-139-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-140-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-141-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-142-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-143-generic   -> SSD HDD IO 성능 저하 이슈 발생
4.15.0-144-generic   -> 정상

 

 


 

반응형
작성일: 2024년 8월 13일

 

 

특정 버전의 Linux Kernel 설치하기

apt 명령으로 아래와 같은 특정 kernel version을 설치한다.

예를 들어, linux kernel version 4.15.0-137 을 설치한다면, 아래 예제 명령과 같이 실행한다.

$ apt-get install linux-image-4.15.0-137-generic \
                  linux-headers-4.15.0-137-generic \
                  linux-modules-4.15.0-137-generic \
                  linux-modules-extra-4.15.0-137-generic

 

 

GRUB 설정하기

새로운 Linux kernel version이 설치되었다고 해서, 새로 설치한 Kernel version으로 부팅되는 것이 아니다.

GRUB 설정을 수정해야 내가 원하는 linux kernel version으로 부팅된다.

아래 예제 GRUB 파일과 같이 수정하면 된다.

$ vi /etc/default/grub

... 중간 생략 ...

## 방금 새로 설치한 linux kernel version에 대한 이름을 설정한다.
GRUB_DEFAULT='Advanced options for Ubuntu>Ubuntu, with Linux 4.15.0-137-generic'

## 아래와 같이 설정하면, 3초 동안 countdown하는 화면이 나온다.
## countdown하는 동안에 'ESC or F4 or SHIFT' 키를 누르면 GRUB 부팅 설정이 나온다.
## 이때 Default kernel version이 아닌 다른 kernel version을 고를 수 있다.
GRUB_TIMEOUT_STYLE=countdown
GRUB_TIMEOUT=3

... 중간 생략 ...

 

 

grub 설정 파일을 저장하고, 아래와 같이 update-grub 명령을 수행한다.

$ update-grub

Sourcing file `/etc/default/grub'
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.15.0-213-generic
Found initrd image: /boot/initrd.img-4.15.0-213-generic
Found linux image: /boot/vmlinuz-4.15.0-143-generic
Found initrd image: /boot/initrd.img-4.15.0-143-generic
Found linux image: /boot/vmlinuz-4.15.0-142-generic
Found initrd image: /boot/initrd.img-4.15.0-142-generic
Found linux image: /boot/vmlinuz-4.15.0-137-generic
Found initrd image: /boot/initrd.img-4.15.0-137-generic
done

$

 

 

Linux OS를 reboot

아래와 같이 reboot 명령을 수행하고, 부팅 초기에 "countdown" 화면이 나오면

[ESC] 또는 [SHIFT] 또는 [F4] 키를 눌러서 원하는 Linux kernel version을 선택한다.

$ reboot

 

 

GNU GRUB 화면 (Linux kernel version 선택)

 

GNU GRUB 화면 (Linux kernel version 선택)

 

 

 

Linux kernel version의 적용 여부 확인

아래와 같이 `uname -sr` 명령으로 새로 설치한 linux kernel version으로 부팅되었는지 확인한다.

$ uname -sr
Linux 4.15.0-137-generic
$

 

 

 

 


 

반응형
작성일: 2024년 8월 10일

 

차박 여행을 한지 1년 5개월이 지났다.

차박 용품 중에서 제일 중요한 아이템은 "매트"가 아닐까 생각한다.

비록 차에서 잠을 자더라도 잘 자야 다음 날 여행이 즐겁다.

 

1년 전에 구입해서 사용했던 차박용 자충매트는 사용해보니 몇 가지 불편함이 있었다.

  • 자충 매트가 너무 컸다. 매트가 길어서 트렁크를 닫을 때 간섭이 있었다. 그래서 트렁크 문을 닫을 때마다 조심스러웠다.
  • 매트가 크니까 접을 때 불편했다.
  • 자충매트가 부풀 때는 빠르게 잘 부풀었는데, 반대로 접을 때는 엄청난 힘을 줘야 바람이 빠졌다.
  • 매트 접는 것이 쉽지 않다보니 그냥 대충 둘둘 말아서 방 구석에 세워놓게 된다. (공간을 많이 차지함)
  • 꿀잠을 자겠다고 두꺼운 자충 매트를 샀더니, 아무리 잘 접어도 부피가 크다.

 

위와 같은 불편함을 겪고 나서 좀더 나에게 잘 맞는 매트를 구입하고 싶었다.

그래서 구입한 것인 "네이터하이크 자동 충전 에어매트(Naturehike Automatic Inflatable Sleeping Pad)"라는 자충 매트이다.

Naturehike 홈페이지를 보니 등산 캠핑 용품을 판매하는 브랜드처럼 보인다.

(즉, 자동차 차박 용품 브랜드는 아니다)

 

일단, 차박 용품이 아니니까 차박에 어울릴까 걱정하면서 구입했다.

걱정은 했지만, 구입 후 만족하고 있다.

어떤 점이 좋았냐면,

  • 접었을 때 작고 가볍다. 그리고 보관, 운반이 쉽다.
  • 에어 패드(에어 매트) 스스로 잘 부풀어 오른다. (어느 정도 자동으로 부풀면 마지막에 약간 바람을 불어넣어주면 끝)
  • 접기가 정말 편하다. 작은 힘으로 둘둘 말면, 잘 말린다.

 

https://youtu.be/ygA4SjrkOpA?si=YBebqpHog4noXj0y

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

에어 매트 사이즈: 185(길이) x 55(폭) x 3.5(두께)    단위: cm

 

 

 

 

 

 

 


 

반응형
작성일: 4월 15일

 

 

Kubernetes를 사용하면서 만나는 문제 상황

Kubernetes service resource를 삭제하고 다시 생성하면, Public IP(또는 EXTERNAL-IP) 값이 변경된다.

AWS, Oracle Cloud, Azure, GCP(Google Cloud Platform) 같은 Public Cloud Infra에서 제공하는 

Reserved Public IP 기능과 Service YAML에 'externalIPs' 값을 설정하면 Public IP Address를 고정시킬 수 있지만,

Helm Chart를 작성하거나 Kubernetes Service Manifest 파일을 작성할 때마다 

  1. Reserved Public IP 생성 요청
  2. 위에서 생성된 Public IP 값을 확인 후 Service Manifest 작성(또는 Helm chart) 작성

한다는 것은 참 불편한 작업이다.

 

 

그래서 아래의 기능을 수행하는 Python script를 만들었다.

 

  1. my-conf.yaml 파일에서 Public IP address 정보를 갱신할 'Kubernetes service' 정보와 'Internet domain name' 목록을 설정
  2. DNS 서버에서 Kubernetes API server에게 Service resource의 public IP address를 조회 요청
  3. 이렇게 얻은 public IP address(즉, EXTERNAL-IP)를 자동으로 DNS 서버의 Zone file에 반영
  4. BIND9 서비스 데몬을 재기동

 

아래 예제 설정과 Python 소스 코드를 이용하면 잘 동작함 ^^

 

설정 파일 작성  [ 파일명: my-conf.yaml ]

 

$ cat my-conf.yaml

zone_file: /var/cache/bind/my-domain.kr.zone
json_file: /var/cache/bind/my-domain.kr.json

domain_name: my-domain.kr

services:
  - kube_svc_name: almighty
    host_name: almighty
  - kube_svc_name: myhappyserver
    host_name: myhappynacserver
  - kube_svc_name: my-ingress-nginx-controller
    host_name: myproxyserver

 

 

 

BIND9 Zone File 생성을 위한 Jinja2 Template 파일 작성  [ 파일명: zone.j2 ]

 

$ cat zone.j2

; ------------------------------------------------------------------------------------------
; NOTE:
;   이 파일은 대한민국 서울에 사는 Andrew Jeong이 만든 "DNS Auto Configuration Application"이
;   자동으로 생성한 파일입니다.
;   사용자가 이 파일을 직접 편집해도 일정 시간 이후에 Application에 의해서 재작성될 것입니다.
;   따라서 이 Zone file을 직접 편집하는 것을 금합니다.
;   만약, Zone file에 반영할 내용이 있다면 같은 폴더에 있는 JSON 파일을 수정해야 합니다.
; ------------------------------------------------------------------------------------------

; Internet Domain Zone :  {{ origin }}
; Exported date        :  {{ now }}

$ORIGIN {{ origin }}
$TTL {{ ttl }}

;
; SOA Record
;
@    IN    SOA    {{ soa['mname'] }}    {{ soa['rname'] }} (
    {{ soa['serial'] }} 	; serial
    {{ soa['refresh'] }} 	; refresh
    {{ soa['retry'] }} 	; retry
    {{ soa['expire'] }} 	; expire
    {{ soa['minimum'] }} 	; minimum ttl
)


{% if ns is defined -%}
;
; NS Records
;
{% for entry in ns -%}
@   IN  NS  {{ entry['host'] }}
{% endfor %}
{% endif %}


{%- if mx is defined -%}
;
; MX Records
;
{% for entry in mx -%}
@	IN	MX	{{ entry['preference'] }}    {{ entry['host'] }}
{% endfor %}
{% endif %}


{%- if spf is defined -%}

; SPF Records
{% for entry in spf -%}
{{ entry['domain'] }}		IN	TXT	"{{ entry['record'] }}"
{% endfor %}
{% endif %}


{%- if a is defined -%}
;
; A Records
;
{% for entry in a -%}
	{{ "{:<17}".format(entry['name']) }}  IN  A      {{ entry['ip'] }}
{% endfor %}
{% endif %}


{%- if aaaa is defined -%}

; AAAA Records
{%- for entry in aaaa -%}
{{ entry['name'] }}		IN	AAAA	{{ entry['ip'] }}
{% endfor %}
{% endif %}


{%- if cname is defined -%}
;
; CNAME Records
;
{% for entry in cname -%}
	{{ "{:<17}".format(entry['name']) }}  IN  CNAME  {{ entry['alias'] }}
{% endfor %}
{% endif %}


{%- if ptr is defined -%}
;
; PTR Records
;
{% for entry in ptr -%}
{{ entry['name'] }}		IN	PTR	"{{ entry['host'] }}"
{% endfor %}
{% endif %}


{%- if txt is defined -%}
;
; TXT Records
;
{% for entry in txt -%}
{{ entry['name'] }}		IN	TXT	"{{ entry['txt'] }}"
{% endfor %}
{% endif %}


{%- if srv is defined -%}
;
; SRV Records
;
{% for entry in srv -%}
{{ entry['name'] }}		IN	SRV	{{ entry['priority'] }}   {{ entry['weight'] }} {{ entry['port'] }}   {{ entry['target'] }}
{% endfor %}
{%- endif %}

; End of Zone

 

 

 

BIND9 Zone File 생성을 위한 JSON 파일 작성  [ 파일명: /var/cache/bind/my-domain.kr.json ]

$ cat /var/cache/bind/my-domain.kr.json

{
    "origin": "my-domain.kr.",
    "ttl": 600,
    "soa": {
        "mname": "ns.my-domain.kr.",
        "rname": "sejong.my-domain.kr.",
        "serial": "2024041500",
        "refresh": 3600,
        "retry": 600,
        "expire": 604800,
        "minimum": 86400
    },
    "ns": [
        {
            "host": "ns.my-domain.kr."
        },
        {
            "host": "ns1.my-domain.kr."
        }
    ],
    "a": [
        {
            "name": "@",
            "ip": "16.6.80.3"
        },
        {
            "name": "ns",
            "ip": "16.6.80.3"
        },
        {
            "name": "ns1",
            "ip": "13.2.88.15"
        },
        {
            "name": "myhappyserver",
            "ip": "13.93.28.9"
        },
        {
            "name": "my-ingress-nginx-controller",
            "ip": "10.1.7.6"
        },
        
        ... 중간 생략 ...
        
        {
            "name": "almighty",
            "ip": "129.154.195.186"
        }
    ],
    "cname": [
        {
            "name": "my-nicname-server",
            "alias": "almighty"
        },
        {
            "name": "toy",
            "alias": "my-ingress-nginx-controller"
        },
        
        ... 중간 생략 ...
        
        {
            "name": "my-supersvc",
            "alias": "mail.sogang.ac.kr"
        }
    ]
}

 

 

로그 파일 생성을 위한 Python Script 작성  [ 파일명: my_log.py ]

$ cat my_log.py

#!/usr/bin/python3

import logging
import logging.handlers
import os
import datetime
import threading
import time


class MyLog():
    def __init__(self, dir, logname) -> None:
        self.logname = logname
        self.dir = os.path.join(dir,logname)
        self.InitLogger()

    def InitLogger(self):
        ## Log format 설정하기
        formatter = logging.Formatter("[%(asctime)s] %(levelname)s %(filename)s:%(lineno)d - %(message)s")
        if os.path.exists(self.dir) == False :
            os.makedirs(self.dir)

        log_File = os.path.join(self.dir,  self.logname + ".log")
        timedfilehandler = logging.handlers.TimedRotatingFileHandler(filename=log_File, when='midnight', interval=1, encoding='utf-8')
        timedfilehandler.setFormatter(formatter)
        timedfilehandler.suffix = "%Y%m%d.log"

        self.logger = logging.getLogger(self.logname)
        self.logger.addHandler(timedfilehandler)
        self.logger.setLevel(logging.INFO)

        self.delete_old_log(self.dir, 3)

        now = datetime.datetime.now()
        self.toDay = "%04d-%02d-%02d" % (now.year, now.month, now.day)
        self.th_auto_delete = threading.Thread(target=self.auto_delete, daemon=True)
        self.th_auto_delete.start()

    '''
    함수 인자 설명:
      - fpath:삭제할 파일이 있는 디렉토리,
      - age:경과일수
    '''
    def delete_old_log(self, fpath, age):
        for fp in os.listdir(fpath):
            fp = os.path.join(fpath, fp)
            if os.path.isfile(fp):
                timestamp_now = datetime.datetime.now().timestamp()
                if os.stat(fp).st_mtime < timestamp_now - (age * 24 * 60 * 60):
                    try:
                        os.remove(fp)
                    except OSError:
                        print(fp, 'this log file is not deleted')

    def auto_delete(self):
        while True:
            now = datetime.datetime.now()
            day = "%04d-%02d-%02d" % (now.year, now.month, now.day)
            if self.toDay != day:
                self.toDay = day
                self.delete_old_log(self.dir, 3)
            time.sleep(600)


## This is test code.
if __name__ == '__main__':
    log = MyLog("my_log_test_dir", logname="sejong")
    for idx in range(3):
        log.logger.info(f"로그 메시지가 잘 기록되는지 테스트 중 {idx}")

 

 

Kubernetes External-IP를 조회 및 DNS 자동 설정을 위한 Python Script 작성 [  파일명: kube-dns-auto-config.py ]

 

#!/usr/bin/python3

import os
import time
import socket
import shutil
import subprocess
import yaml
import json
import datetime
#from datetime import datetime
from kubernetes import client, config
from jinja2 import Environment, FileSystemLoader

import my_log


###############################################################
## NOTE: You have to fix the following variable 'my_run_dir'
###############################################################
my_run_dir = "/opt/kube-dns-auto-config/"


def search_host_from_json_data(host_name):
    for item in json_data['a']:
        if item['name'] == host_name:
            return item
    return None


def chk_ip_addr_changed(conf_svc_item):
    kube_svc_name = conf_svc_item["kube_svc_name"]
    host_name = conf_svc_item["host_name"]

    log.logger.info(f"\n")
    log.logger.info(f"[ Checking configuration '{kube_svc_name}' / '{host_name}' ]")

    for item in kube_svc_info.items:
        if item.metadata.name == kube_svc_name:
            ip_addr_k8s_svc = item.status.load_balancer.ingress[0].ip
            log.logger.info(f"  {kube_svc_name:<11} from K8S SVC EXT-IP : {ip_addr_k8s_svc:>15}")

            dns_record = search_host_from_json_data(host_name)
            if dns_record == None:
                log.logger.info("  host_name {host_name} is not found.")
                return 0
            log.logger.info(f"  {host_name:<11} from JSON file      : {dns_record['ip']:>15}")
            if ip_addr_k8s_svc == dns_record['ip']:
                log.logger.info("  IP address of domain name is not changed. Nothing to do.")
                return 0
            else:
                log.logger.info(f"  IP address of domain name is changed;   {dns_record['ip']} -> {ip_addr_k8s_svc}")
                dns_record['ip'] = ip_addr_k8s_svc
                return 1
    return 0



def get_new_serial(old_serial):
    curr_date = datetime.datetime.today().strftime("%Y%m%d")
    bool_today = old_serial.startswith(curr_date)
    if bool_today is True:
        ## Serial 값이 오늘 한번이라도 Update된 경우
        int_old_serial = int(old_serial)
        new_serial = str(int_old_serial + 1)
    else:
        ## Serial 값이 처음 Update되는 경우
        new_serial = f"{curr_date}00"

    log.logger.info(f"old_serial: [{old_serial}]")
    log.logger.info(f"new_serial: [{new_serial}]")

    return new_serial


def make_bkup_file(serial):
    zone_file = my_conf_info['zone_file']
    bkup_zone_file = f"{zone_file}_{serial}"
    shutil.copy(zone_file, bkup_zone_file)

    bkup_json_file = f"{json_file}_{serial}"
    shutil.copy(json_file, bkup_json_file)

    return None


def make_zone_file():
    ## Jinja2 environment object and refer to templates directory
    env = Environment(loader=FileSystemLoader(my_run_dir))
    template = env.get_template('zone.j2')

    template.globals['now'] = datetime.datetime.now().isoformat()
    rendered_data = template.render(json_data)

    log.logger.info(f"rendered_data: \n{rendered_data}")

    fp = open(my_conf_info['zone_file'], "w")
    fp.write(rendered_data)
    fp.close()

    return None


## ------------------------------------------------------------------------
##     Begin of Main
## ------------------------------------------------------------------------

if __name__ == '__main__':
    log = my_log.MyLog("log_kube_dns", logname="kb_log")
    log.logger.info("#####  Start program  #####")

    ## Load configration from YAML file.
    conf_path = my_run_dir + "my-conf.yaml"
    with open(conf_path) as fp:
        my_conf_info = yaml.load(fp, Loader = yaml.FullLoader)

    json_file = my_conf_info['json_file']

    ## Configs can be set in Configuration class directly or using helper utility
    config.load_kube_config()
    v1 = client.CoreV1Api()

    ##
    ## !!! Main service logic !!!
    ##
    while True:
        svc_ip_changed = 0

        with open(json_file) as json_fp:
            json_data = json.load(json_fp)

        kube_svc_info = v1.list_service_for_all_namespaces(watch=False)
        for conf_svc_item in my_conf_info['services']:
            svc_ip_changed += chk_ip_addr_changed(conf_svc_item)

        if svc_ip_changed > 0:
            log.logger.info(f"IP address is changed")
            log.logger.info(f"json_data: \n\n{json_data}\n")

            new_serial = get_new_serial(json_data['soa']['serial'])
            json_data['soa']['serial'] = new_serial

            ## BKUP file 만들기
            make_bkup_file(new_serial)

            ## JSON 파일에 변경된 내용을 Update한다.
            json_str = json.dumps(json_data, indent=4)
            with open(json_file, "w") as json_outfile:
                json_outfile.write(json_str)

            ## JSON Data를 Zone DB 포맷으로 변환하여 Zone file에 저장
            make_zone_file()

            ## Restart BIND9 service
            ret_value = subprocess.call("systemctl restart bind9", shell=True)
            log.logger.info(f"\n\n")
            log.logger.info(f"[ BIND9 is restarted ] 'systemctl' return: {ret_value}\n\n")
        time.sleep(60)

    ## ------------------------------------------------------------------------
    ##     End of Main
    ## ------------------------------------------------------------------------

 

 

 

실행하기 (테스트하기)

$ /opt/kube-dns-auto-config/kube-dns-auto-config.py

 

 

위와 같이 Python script를 실행하고, 로그 파일을 열람한다.

$ tail -f /opt/kube-dns-auto-config/log_kube_dns/kb_log/kb_log.log

 

 

 

 

 

주의 사항

위 Python script를 실행하는 DNS 서버 장비에 $KUBE_CONFIG 파일이 존재해야 한다.

예를 들어, /home/sejong/.kube/config 같은 파일이 있어야 이 Python 프로그램이 Kubernetes API server에 접근이 가능하다.

 

 

 


 

 

##
## 채용 관련 글
##
제가 일하고 있는 기업 부설연구소에서 저와 같이 연구/개발할 동료를 찾고 있습니다.
(이곳은 개인 블로그라서 기업 이름은 기재하지 않겠습니다. E-mail로 문의주시면 자세한 정보를 공유하겠습니다.)

근무지 위치:
  서울시 서초구 서초동, 3호선 남부터미널역 근처 (전철역 출구에서 회사 입구까지 도보로 328m)
필요한 지식 (아래 내용 중에서 70% 정도를 미리 알고 있다면 빠르게 협업할 수 있음):
  - 운영체제 (학부 3~4학년 때, 컴퓨터공학 운영체제 과목에서 배운 지식 수준):
    예를 들어, Processor, Process 생성(Fork)/종료, Memory, 동시성, 병렬처리, OS kernel driver  
  - Linux OS에서 IPC 구현이 가능
    예를 들어, MSGQ, SHM, Named PIPE 등 활용하여 Process간 Comm.하는 기능 구현이 가능하면 됨. 
  - Algorithm(C언어, C++ 언어로 구현 가능해야 함)
    예를 들어, Hashtable, B-Tree, Qsort 정도를 C 또는 C++로 구현할 수 있을 정도 
  - Network 패킷 처리 지식(Layer 2 ~ 4, Layer 7)
    예를 들어, DHCP Server/Client의 주요 Feature를 구현할 정도의 능력이 있으면 됨.
  - Netfilter, eBPF 등 (IP packet hooking, ethernet packet 처리, UDP/TCP packet 처리)
  - IETF RFC 문서를 잘 읽고 이해하는 능력 ^^
  # 위에 열거한 내용 외에도 제가 여기 블로그에 적은 내용들이 대부분 업무하면서 관련이 있는 주제를 기록한 것이라서
  # 이 블로그에 있는 내용들을 잘 알고 있다면, 저희 연구소에 와서 연구/개발 업무를 수행함에 있어서 어려움이 없을 겁니다.
회사에서 사용하는 프로그래밍 언어:
  - 프로그래밍 언어: C, C++, Go
    (참고: 아직 연구소 동료들이 Rust를 사용하진 않습니다만, 새 언어로써 Rust를 사용하는 것을 고려하는 중)
근무 시간:
  - 출근: 8~10시 사이에서 자유롭게 선택
  - 퇴근: 8시간 근무 후 퇴근 (오후 5시 ~ 7시 사이)
  - 야근 여부: 거의 없음 (내 경우, 올해 상반기 6개월간 7시 이후에 퇴근한 경우가 2회 있었음)
  - 회식 여부: 자유 (1년에 2회 정도 회식하는데, 본인이 집에 가고 싶으면 회식에 안 감. 왜 참석 안 하는지 묻지도 않음)
외근 여부:
  - 신규 프로젝트 멤버 -> 외근 전혀 하지 않음 (나는 신규 프로젝트만 참여해서 지난 1년 동안 한번도 외근 없었음)
  - 상용 프로젝트 멤버 -> 1년에 5회 미만 정도로 외근
팀 워크샵 여부:
  - 팀 워크샵 자체를 진행하지 않음. (워크샵 참석하는 거 싫어하는 개발자 환영 ^^)
연락처:
  - "sejong.jeonjo@gmail.com"  # 궁금한 점은 이 연락처로 문의주세요.
  - 블로그 비밀 댓글 (제가 하루에 한번씩 댓글 확인하고 있음)
원하는 인재상:
  - 우리 부설연구소는 "긴 호흡으로 프로젝트를 진행"하기 때문에 최소 2년간 한 가지 주제를 꾸준하게 연구/개발할 수 있는 개발자를 원함.
  - 우리 부설연구소는 자주적으로 연구 주제를 찾아서 업무를 하기 때문에 능동적으로 생각하고 행동하는 동료를 원함.
  - 차분하게 연구 주제에 몰입하고, 해법을 찾는 것을 즐기는 사람.
내가 느끼는 우리 연구소의 장점:
  - 갑/을 관계가 없음. (제가 근무하고 있는 연구소는 SI업종이 아니라서 갑/을 회사 개념이 없음)
  - 연구소 자체적으로 연구 주제를 발굴하고 시스템을 개발하기 때문에 개발 일정에 대한 스트레스가 적음
  - 빌딩 전체를 우리 회사가 사용하므로 분위기가 산만하지 않음.
  - 근처에 예술의전당, 우면산 둘레길이 있어서 점심 시간에 산책하기 좋음 ^^
  - 연구소 동료들 매너가 Good (2년간 일하면서 한번도 감정에 스크레치 생기거나 얼굴 붉히며 싸운 적 없음 ^^)

 

 

반응형

 


작성한 날: 2024년 8월 7일
테스트했던 Pi 모델: Pi 4, Pi 5

 

터미널을 열어서 아래 명령(CLI)를 입력한다.

$ sudo  apt install -y fonts-unfonts-core

 

 

Raspbian Desktop 시작 메뉴에서 [ Raspberry Pi Configuration ] 창을 연다.

메뉴를 찾아가는 순서는 아래와 같다.

  [ 시작 메뉴 ]  ->  [ 기본 설정 ]  ->  [ Raspberry Pi Configuration ]  ->  [ Localisation ]

 

[ Locale ] 설정 항목에서 아래와 같이 설정한다.

 - Language : ko (Korean)

 - Character Set : UTF-8

 

[ Raspberry Pi Configuration ] 창을 닫는다.

 

아래와 같이 명령을 실행하여 한글 패키지를 설치한다.

 

$ sudo  apt install -y fcitx fcitx-hangul
... 출력 화면 생략 ...


$ vim  /etc/default/im-config
... 중간 생략 ...
IM_CONFIG_DEFAULT_MODE=fcitx     # <-- "auto" 값을 "fcitx" 값으로 수정 
... 중간 생략 ...


$ reboot

 

 

 

Raspberry Pi를 Reboot하고 나면, [한/영] 전환하는 키 조합을 설정한다. 메뉴를 찾아가는 순서는 아래와 같다.

 [ 시작 메뉴 ]  ->  [ 기본 설정 ]  ->  [ Fcitx 구성 ]  ->  [ 전역 설정 ] 탭 

 

[ 전역 설정 ] 탭에서 [ 트리거 입력기 ]를 설정한다.

나는 [ Shift + Space ] 키 조합과 [ Right Alt ] 키를 설정했다.  (아래아한글 프로그램의 한영 전환 키처럼 설정)

 

 


 

 

 

##
## 채용 관련 글
##
제가 일하고 있는 기업 부설연구소에서 저와 같이 연구/개발할 동료를 찾고 있습니다.
(이곳은 개인 블로그라서 기업 이름은 기재하지 않겠습니다. E-mail로 문의주시면 자세한 정보를 공유하겠습니다.)

근무지 위치:
  서울시 서초구 서초동, 3호선 남부터미널역 근처 (전철역 출구에서 회사 입구까지 도보로 328m)
필요한 지식 (아래 내용 중에서 70% 정도를 미리 알고 있다면 빠르게 협업할 수 있음):
  - 운영체제 (학부 3~4학년 때, 컴퓨터공학 운영체제 과목에서 배운 지식 수준):
    예를 들어, Processor, Process 생성(Fork)/종료, Memory, 동시성, 병렬처리, OS kernel driver  
  - Linux OS에서 IPC 구현이 가능
    예를 들어, MSGQ, SHM, Named PIPE 등 활용하여 Process간 Comm.하는 기능 구현이 가능하면 됨. 
  - Algorithm(C언어, C++ 언어로 구현 가능해야 함)
    예를 들어, Hashtable, B-Tree, Qsort 정도를 C 또는 C++로 구현할 수 있을 정도 
  - Network 패킷 처리 지식(Layer 2 ~ 4, Layer 7)
    예를 들어, DHCP Server/Client의 주요 Feature를 구현할 정도의 능력이 있으면 됨.
  - Netfilter, eBPF 등 (IP packet hooking, ethernet packet 처리, UDP/TCP packet 처리)
  - IETF RFC 문서를 잘 읽고 이해하는 능력 ^^
  # 위에 열거한 내용 외에도 제가 여기 블로그에 적은 내용들이 대부분 업무하면서 관련이 있는 주제를 기록한 것이라서
  # 이 블로그에 있는 내용들을 잘 알고 있다면, 저희 연구소에 와서 연구/개발 업무를 수행함에 있어서 어려움이 없을 겁니다.
회사에서 사용하는 프로그래밍 언어:
  - 프로그래밍 언어: C, C++, Go
    (참고: 아직 연구소 동료들이 Rust를 사용하진 않습니다만, 새 언어로써 Rust를 사용하는 것을 고려하는 중)
근무 시간:
  - 출근: 8~10시 사이에서 자유롭게 선택
  - 퇴근: 8시간 근무 후 퇴근 (오후 5시 ~ 7시 사이)
  - 야근 여부: 거의 없음 (내 경우, 올해 상반기 6개월간 7시 이후에 퇴근한 경우가 2회 있었음)
  - 회식 여부: 자유 (1년에 2회 정도 회식하는데, 본인이 집에 가고 싶으면 회식에 안 감. 왜 참석 안 하는지 묻지도 않음)
외근 여부:
  - 신규 프로젝트 멤버 -> 외근 전혀 하지 않음 (나는 신규 프로젝트만 참여해서 지난 1년 동안 한번도 외근 없었음)
  - 상용 프로젝트 멤버 -> 1년에 5회 미만 정도로 외근
팀 워크샵 여부:
  - 팀 워크샵 자체를 진행하지 않음. (워크샵 참석하는 거 싫어하는 개발자 환영 ^^)
연락처:
  - "sejong.jeonjo@gmail.com"  # 궁금한 점은 이 연락처로 문의주세요.
  - 블로그 비밀 댓글 (제가 하루에 한번씩 댓글 확인하고 있음)
원하는 인재상:
  - 우리 부설연구소는 "긴 호흡으로 프로젝트를 진행"하기 때문에 최소 2년간 한 가지 주제를 꾸준하게 연구/개발할 수 있는 개발자를 원함.
  - 우리 부설연구소는 자주적으로 연구 주제를 찾아서 업무를 하기 때문에 능동적으로 생각하고 행동하는 동료를 원함.
  - 차분하게 연구 주제에 몰입하고, 해법을 찾는 것을 즐기는 사람.
내가 느끼는 우리 연구소의 장점:
  - 갑/을 관계가 없음. (제가 근무하고 있는 연구소는 SI업종이 아니라서 갑/을 회사 개념이 없음)
  - 연구소 자체적으로 연구 주제를 발굴하고 시스템을 개발하기 때문에 개발 일정에 대한 스트레스가 적음
  - 빌딩 전체를 우리 회사가 사용하므로 분위기가 산만하지 않음.
  - 근처에 예술의전당, 우면산 둘레길이 있어서 점심 시간에 산책하기 좋음 ^^
  - 연구소 동료들 매너가 Good (2년간 일하면서 한번도 감정에 스크레치 생기거나 얼굴 붉히며 싸운 적 없음 ^^)

 

+ Recent posts