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

 

Go 언어를 2015~2020년 사이에 사용하고, 그 뒤로 거의 쓰지 않다가 오늘 go-pktgen 소스 코드를 보다가

몇년 사이 golang에 많은 변화가 있다는 것을 알았다.

내가 2015년에 책으로 배운 Go 언어에 대한 지식만으로 프로그래밍하면 큰코 다치겠다 ㅋㅋ

(회사 동료들이 같이 Go 언어를 사용하면, 같이 성장할텐데... 회사 동료들은 C, C++ 찬양론자들이라서... ㅠㅠ)

 

새로운 Golang Feature도 파악하고, 다시 기억도 좀 살리기 위해 Golang 학습 문서를 봐야겠다

오늘 기준으로 볼만한 온라인 문서를 찾아보니... 아래와 같다.

 

 

Tucker의 Go 언어 프로그래밍 (2021년, 공봉식)

2024년 5월 8일 기준으로 이 책을 eBook으로 구입해서 보고 있는데...

내가 9년 전에 구입해서 봤던 책보다는 3배 정도 설명이 자세하고 친절하다.

9년 전에 구입한 Go 언어 책이 미울 정도이다. (그 책을 읽은 시간이 아깝다 ㅠㅠ)

종이 책(또는 eBook) 중에서는 이 책이 가장 괜찮은 것 같다. (나의 주관적 느낌)

 

Tucker의 Go 언어 프로그래밍 (책 표지)

 

 

아래 화면은 내가 구입한 eBook이고 macOS용 Crema 앱으로 보고 있는 중이다.

컴퓨터학(Compute Science) 전공하고 있는 대학생, 또는 컴퓨터학을 수료한 졸업생이라면 쉽게 읽힐 수 있는 책이다.

왜냐고? 책 중간 중간에 Memory 구조와 Go source code를 설명하는 부분이 자주 등장하는데,

약간이라도 CPU, Memory 구조 및 동작 방식을 알고 보는 것이 재미있다.

예를 들어, 아래 빨간 동그라미로 표시한 부분과 같은 표현이 익숙하고 잘 이해가 되는 분이라면 이 책을 재미있게 읽을 수 있다.

 

Tucker의 Go 언어 프로그래밍 (eBook으로 구입해서 macOS용 Crema로 보고 있음)

참고: 저작권 침해가 될 것 같아서 위와 같이 책 내용을 가렸습니다.

 

Tucker의 Go 언어 프로그래밍 (eBook으로 구입해서 macOS용 Crema로 보고 있음)

 

아무튼 이 책은 적극 추천~~~
(이 책의 저자, 출판사로부터 받은 거 없음. 그냥 개인적 소신임)

 

 

Tucker의 Go 언어 프로그래밍 / 동영상 강의

YouTube 채널, Playlist: https://www.youtube.com/playlist?list=PLy-g2fnSzUTBHwuXkWQ834QHDZwLx6v6j

 

 

 

Effective Go (Go Official Site)

영어 문서: https://go.dev/doc/effective_go 

한국어 문서: https://gosudaweb.gitbooks.io/effective-go-in-korean/content/

 

 

 

 

Facebook 그룹

가끔 Facebook을 통해서 공식 행사를 알려준다. (예를 들어, GopherCon Korea 2024 같은 이벤트)

https://www.facebook.com/groups/golangko

 

 

 

 


 

반응형

 

작성일: 2024년 4월 25일

 

양수리 성당 ~ 봉주르 스퀘어 카페 ~ 팔당대교

  • 왕복 2시간 코스이고, 모두 평지라서 쉽게 다녀올 수 있을 듯.
  • 중간에 자동차 도로를 만나지 않으니까 안전함.

자전거 라이딩 코스: 양수리 성당 ~ 봉주르 스퀘어 카페 ~ 팔당대교

 

 

 

 

봉주르 스퀘어 카페 / 3층 전망대에서 보는 풍경

 

봉주르 스퀘어 카페 / 마당 풍경

반응형
작성일: 2024년 4월 22일

 

 

종이 사용 설명서를 항상 책상 위에 올려 놓고 보면, 책상이 깔끔하지 않아서 종이 사용 설명서를 치우고 공식 홈피 문서를 보려고 했다.
그런데 이 Rainy75 키보드의 공식 홈피인 wobkey.com 에는 사용 설명서(Use Manual)을 제공하지 않는 것 같다. ㅠㅠ
(어쩌면 내가 못 찾는 것일 수도... ㅠㅠ)
그래서 가끔 키보드 설정이 필요할 때, 블로그에서 보려고 사진으로 박재했다.

 

 

Rainy75 User Manual (English)

RAINY75 User Manual (Windows / MACOS 전환)

 

 

 

 

RAINY75 User Manual / Multimedia combination shortcuts

 

 

 

 

RAINY75 User Manual / Lighting effect control / 키보드 백라이트 제어

 

 

 

RAINY75 User Manual / Check Battery Level (배터리 확인)

 

 

 

 

 


 

 

 

 

레이니75 키보드 한글 사용 설명서

아래 한글로 작성된 매뉴얼은 [ KypeKBD 카페 ]에서 제작한 것이다.

원문(PDF 파일)은 TypeKBD 카페 에서 열람할 수 있다.

 

 

레이니75 키보드 매뉴얼 (한글) / 윈도우 & macOS 간 전환

위 이미지 출처: KypeKBD 카페 

 

 

 

레이니75 키보드 매뉴얼 (한글) / 멀티미디어 조합 단축키

 

위 이미지 출처: KypeKBD 카페 

 

 

레이니75 키보드 매뉴얼 (한글) / 조명 효과 제어

 

위 이미지 출처: KypeKBD 카페 

 

 

레이니75 키보드 매뉴얼 (한글) / RGB 라이트 설정

 

위 이미지 출처: KypeKBD 카페 

 

 

 

레이니75 키보드 매뉴얼 (한글) / 배터리 상태 확인 및 무선 연결 & 블루투스 연결 & 유선 연결 설정 방법

위 이미지 출처: KypeKBD 카페 

 

 

레이니75 키보드 매뉴얼 (한글) / Q&A

위 이미지 출처: KypeKBD 카페 

 

 

 

 

 

 

 

 


 

 

 

 

 

 

반응형
작성일: 2024년 4월 19일

 

 

Rust Programming 책을 구입해서 읽기 어려운 상황이라면, 아래의 온라인 문서를 추천.

 

https://doc.rust-kr.org/

 

The Rust Programming Language - The Rust Programming Language

Steve Klabnik, Carol Nichols 지음. 기여해주신 러스트 커뮤니티 여러분과 한국어 번역에 참여해주신 분들께 감사드립니다. 이 텍스트 버전은 여러분이 (2023년 2월 9일에 출시된) 러스트 1.67.1 혹은 이후

doc.rust-kr.org

 

 

 


 

반응형
작성일: 2024년 4월 18일

 

 

example.com에 대해서 새로 변경된 DNS Record 정보를 가져오게 하려면

기존에 내 Local Host의 DNS Cache에 저장된 DNS Record를 삭제해야 한다.

 

macOS를 사용하는 경우라면, 아래와 같이 명령을 수행하면 DNS Cache를 삭제할 수 있다. (또는 DNS 초기화, Flush, Reset)

 

$ sudo dscacheutil -flushcache

$ sudo killall -HUP mDNSResponder

 

 

## DNS Cache를 flush하기 전에는 CURL 같은 Application이 
## 예전에 조회해서 얻었던 Old IP address를 사용한다.
$ curl -v my-example.kr:8080
  Trying 12.15.20.38:8080...
  ... 중간 생략 ...


## DNS Cache를 flush
$ sudo dscacheutil -flushcache

$ sudo killall -HUP mDNSResponder

## DNS Cache를 flush한 후에는 CURL 같은 Application이
## 새 DNS Record를 조회하고, 새로 얻은 IP Address를 사용한다.
$ curl -v my-example.kr:8080
  Trying 19.12.25.113:8080...
  ... 중간 생략 ...

 

 

 

 

 

 

 


 

반응형
작성일: 2024년 4월 16일

 

 

Network(특히 ONOS, VPN), Linux OS, Kubernetes, GNS3, Raspberry Pi 등 관련 내용을 구글링하다보면, 자주 '톨티의 공작소'가 검색 결과로 뜬다.

그래서 의도하지 않게 '톨티의 공작소' 블로그를 보게 된다.

 

인터넷 문서(블로그)에 이렇게 정성과 공을 들여서 컨텐츠를 만들어주니, 나 같은 열람자(Reader) 입장에서는 고맙다.

문서 중간에 그림, 표 등이 많이 있어서 바쁠 때는 쓱 훑어보기에도 좋다.

 

https://m.blog.naver.com/love_tolty

 

톨티의 공작소 : 네이버 블로그

Tolty의 하드웨어 소프트웨어 공작소 입니다.^^

m.blog.naver.com

 

 

 

 

 

 

반응형
작성일: 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에 접근이 가능하다.

 

 

 


 

반응형
작성일: 2024년 4월 12일

 

Jinja2를 사용해서 리스트(목록)이 있는 문서를 만들다보면 리스트의 각 컬럼의 폭을 맞춰야 할 때가 있다.

 

예를 들어, 아래와 같은 경우.

## 컬럼이 들쑥 날쑥한 리스트
redmine  IN  A  10.1.1.8
git  IN  A  10.1.1.7
jenkins  IN  A  10.1.1.11
chat-server  IN  A  10.1.1.12

 

 

Jinja2 파일(즉, example.j2 파일)에 아래와 같이 기술하면, 왼쪽 또는 오른쪽으로 정렬할 수 있다.

 

## 문자열의 오른쪽으로 공백 문자를 채우기 (Right-side Whitespace Padding)
{{ "{:<17}".format("test string") }}

## 문자열의 왼쪽으로 공백 문자를 채우기 (Left-side Whitespace Padding)
{{ "{:>17}".format("test string") }}

 

 

아래 출력 예시는 가장 왼쪽 컬럼(예: redmine, git, jenkins, ...)에 대해서 오른쪽 공백 문자를 채운 것이다.

## 컬럼 폭(너비)를 맞춘 리스트
redmine      IN  A  10.1.1.8
git          IN  A  10.1.1.7
jenkins      IN  A  10.1.1.11
chat-server  IN  A  10.1.1.12

 

 


 

+ Recent posts