Shuffle Algorithm - Fisher-Yates

대표적인 Shuffle(썩기) 알고리즘인 Fisher-Yates 알고리즘에 대해서 알아보자.

Python의 random 모듈에 구현되어 있기 때문에 굳이 별도의 함수로 구현할 필요없아 가져다 쓰기만 하면된다.

from random import shuffle

a = [x for x in range(100)]
b = shuffle(a)
print(b)

CPython에 구현된 shuffle() 함수가 현대적인 Fisher-Yates 알고리즘의 구현이다.

Python으로 굳이구현해 보자면 아래와 같다.

from math import floor
from random import random

def shuffle(x):
    for i in reversed(range(1, len(x))):
        j = floor(random() * (i + 1))
        x[i], x[j] = x[j], x[i]
    return x

0에서 99까지의 리스트를 썩어보자.

a = [x for x in range(100)]
b = shuffle(a)
print(b)
[68, 10, 73, 26, 67, 20, 43, 58, 6, 33, 27, 11, 59, 91, 65, 84, 51, 53, 87, 39, 93, 72, 85, 34, 78, 30, 63, 15, 82, 48, 98, 86, 16, 89, 95, 83, 47, 77, 24, 25, 40, 61, 69, 64, 22, 17, 29, 5, 3, 4, 74, 92, 42, 57, 88, 13, 23, 31, 94, 12, 8, 0, 35, 81, 28, 52, 45, 9, 49, 79, 90, 50, 76, 75, 66, 18, 46, 38, 14, 2, 96, 32, 99, 55, 36, 7, 44, 60, 21, 54, 1, 37, 41, 97, 19, 71, 56, 70, 62, 80]

CPython에 구현된 shuffle() 함수를 참고 하였다.

확률 계산

  1. $n$개의 슬롯을 가지는 배열을 가정하자

  2. 배열의 첫 번째 요소가 $n$ 번째 슬롯에 위치할 확률을 계산해 본다.

    • $n$ 번째 슬롯는 첫 번째 iteration에서 요소가 결정 되기 때문에 확률은 $\frac{1}{n}$ 이 된다.
  3. 같은 방법으로 배열의 첫 번째 요소가 $n-1$ 번째 슬롯에 위치할 확률을 계산해 보자. 다음 확률의 곱과 같다.

    • 첫 번째 요소가 $n$ 번째 슬롯에 위치하지 않을 확률: $\frac{n-1}{n}$
    • 첫 번째 요소가 $n-1$ 번째 슬롯에 위치할 확률: $\frac{1}{n-1}$

    $$\frac{n-1}{n} \times \frac{1}{n-1} = \frac{1}{n}$$

  4. 마찬가지로 배열의 첫 번째 요소가 $n-2$ 번째 슬롯에 위치할 확률은 아래 확률의 곱과 같다.

    • 첫 번째 요소가 $n$ 번째 슬롯에 위치하지 않을 확률: $\frac{n - 1}{n}$
    • 첫 번째 요소가 $n-1$ 번째 슬롯에 위치하지 않을 확률: $\frac{n-2}{n-1}$
    • 첫 번째 요소가 $n-1$ 번째 슬롯에 위치할 확률: $\frac{1}{n-2}$

    $$\frac{n - 1}{n} \times \frac{n-2}{n-1} \times \frac{1}{n-2} = \frac{1}{n}$$

  5. 위와 같이 모든 요소에 대하여 첫 번째 슬롯에 위치할 확률을 구해보면 모두 $\frac{1}{n}$ 인 것을 알 수 있다.

  6. 같은 방법으로 임이의 $x$ 번째 요소($0 \le x \le n-1$)가 임이의 슬롯 $s$ ($0 \le s \le n-1$)에 위치할 확률을 구해보면 모두 $\frac{1}{n}$ 인 것을 알 수 있다.

제비뽑기순서에 따른 당첨 확률 문제과 같다.

SWAP 구조이기 때문에 구조적으로 사다리티기와 비슷한 구조다. 사다리타기에서 사다리의 디딤대의 형태random 함수가 되는 것이다.

검증

만든 프로그램을 검증 해보자.

elements = 10
times = 1000000

# 빈도수를 저장하기 위한 변수를 초기화 한다.
freqs = [[0 for x in range(elements)] for x in range(elements)]

# times 번 반복하면서
for n in range(times):
    # elements개의 요소를 가지는 리스트르 만든다.
    values = list(range(elements))

    # 리스트를 썩은 다음
    for i, x in enumerate(shuffle(values)):
        # 빈도수를 기록한다. 
        freqs[i][x] += 1

# 이쁘게 출력 하자
print("        |", end="")
for x in range(elements):
    print(f'{x:7} |', end="")
print("")
print("-"*99)

for i, freq in enumerate(freqs):
    print(f" slot {i} |", end="")
    for x in freq:
        print(f'{x:7} |', end="")
    print("")

결과는 다음과 같다.

        |      0 |      1 |      2 |      3 |      4 |      5 |      6 |      7 |      8 |      9 |
---------------------------------------------------------------------------------------------------
 slot 0 | 100024 | 100224 |  99795 |  99715 |  99240 | 100132 |  99944 |  99976 | 101189 |  99761 |
 slot 1 | 100363 | 100020 |  99848 | 100309 |  99758 |  99989 | 100217 |  99855 |  99750 |  99891 |
 slot 2 |  99745 | 100285 |  99743 | 100022 |  99959 | 100102 | 100179 | 100006 | 100182 |  99777 |
 slot 3 | 100094 | 100119 |  99648 | 100087 |  99581 | 100774 | 100027 | 100250 |  99891 |  99529 |
 slot 4 | 100572 | 100177 | 100148 |  99262 |  99783 |  99912 |  99750 | 100074 |  99780 | 100542 |
 slot 5 |  99870 |  99904 | 100456 | 100140 | 100646 |  99648 |  99582 |  99584 | 100136 | 100034 |
 slot 6 | 100250 |  99615 |  99958 |  99859 | 100889 |  99716 | 100071 |  99947 |  99687 | 100008 |
 slot 7 |  99396 | 100119 | 100087 | 100373 | 100388 |  99841 |  99873 | 100080 |  99679 | 100164 |
 slot 8 |  99694 |  99856 | 100316 | 100036 | 100028 |  99627 |  99903 | 100097 | 100238 | 100205 |
 slot 9 |  99992 |  99681 | 100001 | 100197 |  99728 | 100259 | 100454 | 100131 |  99468 | 100089 |
  • 10개의 요소를 가지를 리스트를 섞었으니 첫 번째 슬롯에 1이 나올 확률은 실행 휫수의 $\frac{1}{10}$ 이다.
  • 1000000번 시도하였으니 기대값은 $\frac{1}{10}$ 인 100000이지만 24 많은 100024이 나왔다.
  • 결과값이 대체적으로 100000 언저리에서 놀고 있으니 잘 동작 한다고 보도록 하자.

라즈베리파이(RPi, Raspberry Pi) 기본계정 pi 변경하기

How to change default account username for RPi(Raspberry Pi)

Raspberry Pi 공식 이미지의 username 과 password는 다음과 같다.

username: pi
password : raspberry

쓸때마다 느끼는 거지만 pi 와 raspberry는 정말 손에 익지 않는다. 키보드로 칠때마다 오타가 나서 한번에 로그인 할때가 거의 없다. pi 계정을 계속 쓰면 보안 문제도 생길 수 있으니 자신만의 손에 익은 username과 password로 변경 해 보자.

다른 리눅스 시스템에도 적용할 수 있다.

먼저 root 계정으로 로그인 해야 한다. 먼저 root 로 로그인 하기 위하여 root 계정의 Password를 설정한다.

sudo passwd root

pi 계정 에서 로그아웃 후 root 계정으로 로그인 한다.

logout

pi 계정을 oneuon로 변경한다.

usermod -l oneuon pi

이제 /home/pi 디렉터리를 /home/oneuon로 변경하자.

usermod -m -d /home/oneuon oneuon

필요하다면 변경한 oneuon계정의 Password를 변경한다.

passwd oneuon

필요 하다면 pi group도 변경 한다.

groupmod -n oneuon pi

root 계정에서 로그 아웃 후 oneuon으로 로그인 한다.

root 계정을 비활성화 하기전 sudo 명령이 잘 동작 하는지 확인한다.

sudo ls

password를 Lock 하여 root 계정을 비활성화 한다.

sudo passwd -l root

History 파일에서 특정 엔트리 삭제하기

Remove specific history entries from history file

History 파일에서 특정 엔트리 삭제하기

History 파일에서 특정 엔트리 삭제하기

  작업을 하다 보면 민감한 정보가 History에 남아 있는 경우가 있다. 다음과 같이 명령 앞에 white space를 추가 하여 해당 명령을 history 파일에 남기지 않는 방법이 있다.

mysql -u root -ppassword -h localhost 

명령앞 빈칸(space)에 주목.

mysql -u root -ppassword -h localhost

아차 하는 순간에 민감한 정보가 History에 남아 있는 경우

history
1234 mysql -u root -pPASSWORD -h localhost 
1235 ls

-d_ 옵션을 사용하여 해당  엔트리를 삭제한다. _ _ 다음과이 사용한다. _

history -d OFFSET
history -d 1234

지워야 되는 엔트리의 수가 많을 경우 다음과 명령으로 특정 문자열이 이 들어 있는 History를 지울 수 있다.

while history -d $(history | grep 'SEARCH-STRING-TO-REMOVE'| head -n 1 | awk {'print $1'}) ; do :; history -w; done

예를 들어 다음과 같이 실행 하면 **ls**가 들어간 모든 History가 삭제된다.

while history -d $(history | grep 'mysql'| head -n 1 | awk {'print $1'}) ; do :; history -w; done
history
1234 cd

Synergy - Software KVM

Synergy - Software KVM

작업을 하다 보면 한 책상위에서 여러대의 PC로 작업을 해야 하는 경우가 생긴다. 나의 경의 예전에 개발은 Linux 머신에서 문서 작업 및 기타 업무는 Windows PC에서 진행을 하고는 했었는데 키보드와 마우스가 항상 문제였다. 모니터야 source를 변경 하면 (귀찮기는 하지만) 쉽게 되지만 Keyboard와 마우스를 공유 하려면 별도의 KVM을 구입 하야야 한다. KVM 없이 공유 할 수 있는 솔루션을 찾다가 Synergy라는 S/W KVM을 찾았다. 키보드와 마우스만을 공유 할 수 있으니 엄밀히 말하면 KVM이 아니라 KM 이다. Synergy를 사용한지는 Open source 일때 부터이니 10년도 더 지났다. 그때 이 소프트웨어가 너무 마음에 들어 기부하고 받은 1.8 버젼용 Pro License를 아직 사용가능 하다. 몇년전 Symless라는 회사를 만들고 본격적으로 판매를 하고 있더니만 작년에 Synergy 2가 나왔고 2017년 말에 2를 12달러에 판매하는 프로모션이 있어 잽싸게 구매를 하였다. 현재 Basic, Pro 버전이 있으며 각각 29, 39 달러에 판매 되고 있다.

Symless

Installation

Configuration

개발자와 시스템 관리자의 친구 tmux, GNU screen Alternative - 01

Usages of tmux, GNU screen Alternative

개발자와 시스템 관리자의 친구 tmux, GNU screen Alternative - 01

개발자와 시스템 관리자의 친구, tmux 에 대하여 알아보자

tmux는 GNU screen을 대체 할 수 있는 가장 매력적인 대안이다. Terminal Multiplex 로 매뉴얼은 다음에서 볼 수 있다.

tmux man page

tmux의 주요 기능에 대하여 알아본다. 먼저 세션 유지 기능과 화면 공유 기능에 대하여 알아보자.

세션 유지 기능

PuTTY등으로 SSH 세션을 연결 한 후 프로그램을 실행하여 작업 도중 네트워크 문제 등으로 세션이 끊겼기면 Logout이 되면서 해당 쉘에서 작업 중이던 모든 작업이 Hangup Signal (SIGHUP)을 받아 Termination  된다. 자칫 작업한 내용을 잃어 버릴 수도 있다.

예를 들어 아래와 같은 상황들이 있을 수 있다.

  • 장시간 미사용으로 인항 세션 해제
  • Windows 자동 업데이트로 인한 시스템 재시작으로 인한 세션 해제
  • 네트워크 불안으로 인한 세션 해제
  • 정전등 전원 불안으로 인한 세션 해제

TMUX 세션을 생성 한다.

tmux
tmux new-session -s test

또는 간단히

tmux new -s test

tmux 세션 내에서 vi 명령을 사용하여 테스트 해본다.

vi test.txt

필요한 작업을 진행한다.

터미널을 닫거나 네트워크를 끊어 SSH 세션을 종료시킨다.

다시 SSH 세션을 연결 후 tmux list-sessions 명령으로 현제 열려 있는 세션을 확인한다.

tmux list-session
test: 1 windows (created Tue Mar 6 15:20:04 2018) \[159x92\] (attached)

이전에 생성한 test 세션이 보인다. test 세션에 attach 한다.

세션이 하나이거나 마지막 생성된 세션으로 attach 하기 위해서 는 간단히 다음을 입력한다.

tmux attach-session

세션 이름으로 attach,  test 세션에 attach

tmux attach-session -t test
    

_**attach-session**_ command는 _**attach**_ 나 _**a**_ 로 대체 가능하다. 
    
```bash    
tmux attach -t test
tmux a -t test

화면 공유 기능

화면을 다른 사람과 공유 할 수 있다.

동일 유저와 화면 공유

같은 계정로 시스템에 로그인 한 경우

Terminal 1

SSH Client를 이용하여 시스템에 로그인 한 후 tmux 세션을 생성한다.

tmux new -s test-01

Terminal 2

다른 SSH Client를 이용하여 시스템에 로그인 한 후 tmux list-sessions 명령으로 현재 열려 있는 세션 리스트를 확인한다.

tmux list-sessions
test-01: 1 windows (created Tue Mar  6 15:52:45 2018) [159x92] (attached)

tmux attach-session 명령으로 test-01 세션에 Attach 한다.

tmux attach -t test-01

터미널 1과 터미널 2에 같은 화면이 보이며 한쪽에서 키보드를 입력 하면 다른쪽 터미널에서도 입력된다.

다른 유저와 화면 공유

tmux_가 실행되면 /tmp/tmux-${UID}_ 디렉터리에 default 라는 이름의 UNIX 소켓이 생성되는데 이 UNIX 소켓을 통해 화면 공유 기능이 수행된다. -S 옵션을 통해 명시적으로 UNIX Domain Socket 파일을 지정 할 수 있이며 해당 UNIX Domain Socket 의 경로를  다른 유저에게 알려 줌으로써 다른 유저가 내가 만든 tmux 세션에 접속 할 수 있다.

먼저 공유하고자 하는 User 와 같이 속하는 Group을 생성한다.

sudo addgroup --gid 888 tmux
sudo usermod -aG tmux myaccount
sudo usermod -aG tmux otheraccount

Terminal 1

Unix Domain Socket을 지정 하여 세션을 만든다.

tmux -S /tmp/tmux-shared new -s test-01

Terminal 2

다른 유저로 로그인 하여 attach 해본다.

tmux -S /tmp/tmux-shared a -t test-01
error connecting to /tmp/tmux-shared (Permission denied)

/tmp/tmux-shared 파일에 권한이 없어 Unix Domain Socket에 접속할 수 없다.

해당 파일의 권한을 확인해보자

ls -l /tmp/tmux-shared 
srwxrwx--- 1 myaccount myaccount 0 Mar  6 16:25 /tmp/tmux-shared

other경우 해당 파일에 대하여 R/W Permission이 없으므로 Unix Domain Socket을 Open 할 수 없다.

Terminal 1

세션을 생성한 유저로 로그인 하여 해당 파일의 Owner Group을 앞서 생성한 tmux로 변경한다.

ls -l /tmp/tmux-shared 
srwxrwx--- 1 myaccount myaccount 0 Mar  6 16:25 /tmp/tmux-shared
chgrp tmux tmux-shared
ls -l /tmp/tmux-shared 
srwxrwx--- 1 myaccount tmux 0 Mar  6 16:25 /tmp/tmux-shared

Terminal 2

Permission을 변경 하였으면 이제 세션에 Attach 해보자.

tmux -S /tmp/tmux-shared attach -t test-01

접속이 되고 하면 공유역시 잘된다.

살짝 아쉬운점.

아쉽게도 아직 screen과 같이 ACL을 통해 read-only 모드로 화면을 공유 하는 방법은 없다. Attach 하려는 사용자가 -r 옵션을 통해 명시적으로 read-only 모드로 Attach 하여야 한다.

tmux -S /tmp/tmux-shared attach -t test-01 -r

요약

세션 생성

tmux new-session -s test

new-sessionnew로 대체 가능하다.

세션에 attach

tmux attach-session -t test

attach-sessionattacha로 대체 가능하다.

접속할 Unix Domain Socket을 지정 하려면 -S 옵션을 사용한다.

tmux attach-session -S /tmp/test-socket -t test

다른 유저에게 화면을 공유 하기 위해서는 해당 유저가 내가 만든 Unix Domain Socket에 접근 권한이 있어야 한다.

다음 포스트에서는 tmux의 가장 편리한 기능인 화면 분할 기능에 대하여 알아보자

왜 디렉터리 Hard link를 만들 수 없나요?

왜 디렉터리 Hard link를 만들 수 없나요?

디렉터리 Hard Link를 만들고 싶지만 만들어 지지 않는다.

예전에는 root 권한으로 -d 옵셥을 주면 디렉터리에 대한 Hard Link를 만들 수 있었던것 같은데 지금  Ubuntu 에서 테스트 해보니 root 권한으로도 디렉터리 Hard Link가 만들어 지지 않는다.

mkdir a
ln a b
ln: a: hard link not allowed for directory

왜 디렉터리에 대한 Hard Link를 만들수 없는지 알아보자.

디렉터리 Hard Link는 리눅스 시스템에 여러 문제점을 야기 시키기 때문이다.

그렇다면 디렉터리 Hard Link에 의해 발생할 수 있는 문제점들은 어떤 것들이 있을까?

파일시스템에 루프를 만든다.

mkdir -p ~/a/b
cd ~/a/b
ln ~/a c

무한대의 깊이(depth)를 가지는 디렉터리 루프가 만들어 졌다.

cd ~/a/b/c/b/c/b/c/b/c/b/c/b/c/b/c/...

-maxdepth 옵션 없이 find 명력을 이 루프가 포함된 디렉터리에서 실행 하면 find명령은 무한대의 깊이를 검색하려고 할 것이다. 이는 사용자가 디렉터리 (Hard Link에 의해)무한대의 루프를 포함하는 디렉터리에 대하여 주요 명령어인 find 명령을 사용 할 수 없음을 의미 한다. 이는 또다른 주요 명령인 locate도 같이 적용 된다.

개념적으로 보면 파일시스템은 기본적으로 Tree 구조를 가지지만 Tree의 정의에는 Loop가 없기 때문에 Loop가 생긴 파일시스템은 더이상 파일시스템이라고 볼 수 없다.

상위디렉터리의 명료성을 깨트린다.

위의 Loop 예제에서 여러개의 상위 디렉터리가 존재하게 된다.

cd ~/a/b
cd ~/a/b/c/b

첫번째 케이스에서 ~/a는 ~/a/b의 상위 디렉터리이다. 두번째 케이스에서 ~/a/b/c 는 ~/a/b/c/b의 상위 디렉터리이며 이는 ~/a/b와 동일하다.

따라서 ~/a/b는 2개의 상위 디렉터리를 가진다.

하나의 파일이 여러 파일로 표현된다.

리눅스/유닉스 시스템에서 파일은 경로에 의해 식별된다.

심볼릭 링크(Symbolic Link)의 경우 실제 경로를 resolving 한 후 실별된다.

예을 들어,

~/a/b/foo.txt
~/a/b/c/b/foo.txt

위의 두 파일은 다른 파일로 식별된다. 위 예제에서 각 path의 foo.txt는 하나의 inode 번호를 가지지만 이를 식별 하기 위한 Path는 무한대로 존제 한다. 사용자는 자신이 명시적으로 Loop를 생성하였을 경우를 제외하고는 이를 체크하지 않는다.

디렉터리 하드링크는 자신의 하위 디렉터리 또는 하위 디렉터리나 상위 디렉터리가 아닌 어떤 디렉터리도 가리킬 수 있다. 이 경우 해당 디렉터리의 하위 항목들은 2개의 파일로 복제되며, 2개의 경로로 식별된다.

ln /one /one-hard 
echo foo > /one-hard/foobar.txt 
diff -s /one/foobar.txt /one-hard/foobar.txt
echo bar >> /one/foobar.txt 
diff -s /one/foobar.txt /one-hard/foobar.txt 
cat /one/foobar.txt 
foo 
bar

그렇다면 디렉터리에 소프트링크가 어떻게 동작 하는가?

소프트링크나 소프트링크가 포함된 디렉터리 루프를 포하는 경로는 종종 파일을 식별하거나 오픈하는 용도로만 사용된다. 이것은 정상적인 경로나 선형 경로 처럼 사용될 수 있다.

하지만경로가 파일 비교에 사용되는 경우, Symbolic Link를 먼저 디코딩한 다음 원본 파일의 경로로 변환하여 Canonical Path(정식 경로)로 만들 수 있다.

이는 모든 소프트링크가 링크 없이 확장될 수 있기 때문에 가능하다. 경로에 있는 모든 소프트링크를 사용하여 이 작업을 수행하면 남아 있는 경로는 항상 경로가 명확한 트리의 일부가 된다.

readlink 명령으로 정식 경로를 확인 할 수 있다.

readlink -f /some/symlinked/path

소프트 링트는 Filesystem 내부의 링크와는 다르기 때문에 소프트 링크로 인해 문제가 발생할 소지가 없다. 이는 Hard Link와 구분되며 필요한 경우 Symbolic Link가 없은 경로로 해석된다.

어떤의미에서(운영제체의 관점에서) Symbolic Link를 추가 하는것은 기존의 파일시스템 구조를 변경하는것이 아니라 유지하는 것이지만 응용 프로그램 측면에서는 더 많은 구조를 추가하는 것이다.

디렉터리 하드링크의 대안

-al 옵션을 주고 copy 한다. 디렉터리는 복사(생성)이 되고 파일은 Hard Link가 된다.

cp -al src dst

이는 증분백업에서 사용되는 주요 매커니즘이다.

directory를 -o bind 옵션을 주고 mount 할 수 도 있는데 이건 hard link가 아니라 soft link와 같이 동작 하니 넘어가기로 한다.

Insync - Linux에서 Google Drive Desktop Client 사용하기

이 글에서는 몇 없는 Google Drive의 리눅스 동기화 어플리케이션인 Insync 에 대하여 알아본다.

Insync: Google Drive Syncing Application for Linux

Insync: 리눅스를 위한 Google Drive 동기화 어플리케이션

Linux를 메인 OS로 사용한지도 벌써 10년 가까이 되어 간다 넘었다.. 처음 메인 OS를 Linux로 변경하고 불편했던 인터넷 뱅킹등의 문제는 스마트폰에서 은행 업무를 볼 수 있게 되면서 해결되었다. 그외 민원업무등 반드시 Windows가 필요한 일은 VirtualBox를 통해 해결한다.

그외 어플리케이션들은 Google Docs같은 웹 기반 어플리케이션으로 대체 가능하다.  하지만 데이터 백업 및 파일 관리용으로 사용하는 Google Drive는 신뢰할 수 있는 Alternate가 없어 고민 했하던 차에 2015년 즈음에 insync라는 Thrid-Party Application을 알게 되었다 유료지만 요즘 트렌트인 정기 결제(구독)가 아닌 One Time 결제로 사용 가능하고 각 배포판용 Repository도 별도로 제공 하기때문에 설치 역시 간편하다.headless application도 지원(3.x 버젼에 잠시 지원이 중단 되었다가 다시 지원하기 시작함) 하기 때문에 UI 없이 콘솔 상에서 실행도 가능하다. https://www.insynchq.com   다른 Agent들과 비교한 자료를 원하면 아래 사이트를 참조 하기 바란다.

use google drive linux

Insync 설치

Add APT Repository

sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys ACCAF35C

/etc/apt/sources.list.d/insync.list 을 생성 한다. 내용은 다음과 같다.

deb http://apt.insynchq.com/[DISTRIBUTION] [CODENAME] non-free contrib

Ubuntu 16.04의 경우 [DISTRIBUTION]ubuntu [CODENAME]xenial을 입력한다. Ubuntu 18.04의 경우 [DISTRIBUTION]ubuntu [CODENAME]bionic을 입력한다. Ubuntu 20.04의 경우 [DISTRIBUTION]ubuntu [CODENAME]focal을 입력한다.

Ubuntu 20.04의 경우 아래 명령을 실행하여

echo deb http://apt.insynchq.com/ubuntu focal non-free contrib | sudo tee /etc/apt/sources.list.d/insync.list

패키지 리스트를 업데이트 한다.

sudo apt update

Installation

UI Version

UI로 로그인 하였을 Insync가 실행되기를 원한다면 UI 버젼을 설치 하자. 대부분 이 버젼을 설치 하면 된다.

sudo apt install insync

Headless Version

GUI가 없는 서버나 UI 로그인 없이 Insync를 실행 하고 싶으면 headless version을 설치 한다.

sudo apt-get install insync

UI 버전에서 Headless 버젼으로 변경(또는 반대의 경우) 시 유의 사항

각 설정 과 데이터베이스 파일의 구조는 크게 변하지 않은 것 같으나 위치가 변경 되었다.

  • UI Version의 경우 ~/.config/Insync
  • Headless 버젼의 경우 ~/.config/Insync-headless

따라서 UI 버젼에서 Headless 버젼으로 변경하고 Insync를 실행하면 UI 버전에서 사용하던 계정 등의 설정 정보를 같이 로드 되지 않는다.

~/.config/Insync 디렉터리를 ~/.config/Insync-headless로 복사한다.

cp -ap ~/.config/Insync ~/.config/Insync-headless

UI 버전과 Headless 버젼의 설정 파일 구조가 언제 바뀔지 모르기 때문에 Symbolic Link 보다는 Hard Link링크로 복사하여 Headless 버젼을 실행 해 보고 문제가 발생하면 해당 디렉터리를 삭제 후 다시 설정하자.

Headless 버전에서 UI 버젼으로 바꾸는 경우에는 반대로 수행하면 된다.

RSYNC+SSH 를 이용한 증분 백업 설정 및 Cron을 이용한 자동화

RSYNC+SSH 를 이용한 증분 백업 및 Cron을 이용한 자동화

관리 하는 서버가 많아지고 서버에 저장되는 데이터의 양이 늘어 날 수록 백업을 어떻게 하여야 하는지 고민이 생긴다.

이 포스트에서는 RSYNC와 SSH를 이용하여 여러 서버의 데이터를 백업하고 이를 자동화 하는 방법에 대하여 기술 한다. 테스트를 위한 서버 구성은 다음과 같다.  

아래 예제 에서는 apple, banana, coconut/home 디렉터리를 backupbot으로 백업 하고자 한다.

Backup Environment

공통 준비 사항

Backup User 생성

백업 서버와 백업을 할 서버 모두 백업을 위한 사용자 계정 backupbot을 생성한다.

sudo adduser --disabled-password --gecos 'BackupBot' backupbot

/etc/hosts 파일을 편집하여 아래 와 같이 host 정보를 추가 한다.

SSH 키 생성 및 배포

Backup 서버에서 SSH Key Pair를 생성하고 각 서버로 배포 한다.

ssh-keygen 명령으로 SSH Key pair를 생성한다.

SSH KEY 배포

Private Key를 backupbot 서버의 backupbot 계정으로 복사한다.

공개 키(backupbot.pub)를 각 서버의 backupbot 계정으로 복사한다.

backupbot 계정은 password를 지정하지 않았기 때문에 직업 복사할 수 없으므로 개인 개정(user)으로 먼저 복사한 다음 backupbot 계정으로 복사한다.

Apple, Banana, Coconut

apple, banana, coconut 서버에 접속 하여 다음과 같이 설정 한다.

필요 패키지 설치

권한 설정

/etc/sudoers 파일을 열어 권한을 설정 한다.

backup 계정은 다른 계정에서 생성한 파일들에 대한 접근 권한이 없으므로 backupbot 계정으로 /home 디렉터리를 백업 하기 위해서는 sudo명령으로 root permission을 획득 하여야 한다. backupbot 계정이 password없이 rsync 명령을 수행 할 수 있도록 아래 내용을 추가 한다.

Backupbot

필요 패키지 설치

SSH Client 설정

~/.ssh/config 파일을 열어

다음과 같이 설정한다. (해당 파일이 없을 경우 생성한다.)

다음 명령으로 배포된 KEY로 SSH접속이 정상적으로 되는지 확인한다. Password를 물어보지 않고 접속되어 명령쉘이 뜨면 성공이다.

RSYNC명령을 이용한 수동 백업

위 명령을 실행 하면 apple 서버의 /home 디렉터리를 backupbot 서버의 /home/backupbot/apple/latest 에 동기화 한다.

증분 백업

rsync 명령으로 동기화를 하면 수정된 파일은 덮어 써지거나 삭제 되기 때문에 기록이 남지 않는다. 기록을 남기 기 위해서는 동기화전 기존 백업을 다른 이름으로 복사하고 동기화 하면 되지만 생으로 복사를 하면 디스크 용량이 두배로 늘기 때문에 효율적이지 않다. 리눅스에서 제공하는 hard link를 사용하면 될것 같지만 Hard Link는 디렉터리에 적용할 수 없다.

cp 명령의 -l 옵션을 사용하면 디렉터리는 카피가 되고 파일은 hard link로 생성된다.

위 명령으로 복사 후 rsync명령으로 동기화 하면 최근 동기화 데이터를 남기면서 최신 데이터를 백업 할 수 있다.

rsync 명령에서 --link-dest 옵션으로 cp명령의 -l 옵션과 같은 효과를 볼 수 있다.

먼저 최신 백업을 다른이름으로 저장한다.

rsync 명령행에 --link-dest="/home/backupbot/apple/previous" 옵션을 추가 한다.

/home/backupbot/apple/previous/home/backupbot/apple/$(date +%Y%m%D%H%M%S)로 바꾸면 날짜 + 시간으로 백업 데이터를 관리 할 수 있다.

Backupman 스크립트를 이용한 자동화

앞서 설명한 백업 절차를 자동화 하는 Python 스크립트를 만들었다.

사용방법은 다음과 같다.

설치

아래와 같이 pip 명령으로 설치 할 수 있다.

권한 설정 (backupbot 에서)

/etc/sudoers 파일을 열어 권한을 설정 한다.

아래 내용을 추가한다.

백업 테스트

백업테스트를 위해 backupman 계정으로 로그인 한다.

다음 명령을 통해 백업 스크립트가 정상적으로 동작 하는지 확인한다.

apple

banana

coconut

Cron에 Job 등록

위 명령이 정상적으로 수행되어 백업이 완료 되었으면 cron에 등록 하여 주기적으로 백업이 이루어 지도록 한다.

아래 예제는 apple, banana, coconut 서버에 대하여 각각 매일 오전 3시, 4시 5시에 백업을 수행 하도록 job을 등록한것이다.

Backupman 스크립트는 아래 Repository에서 확인 할 수 있다.

python-backupman

Update Ubuntu Mirror Site in sources.list

Update Ubuntu Mirror Site in sources.list

sudo sed -i 's|http://archive.ubuntu.com|http://ftp.daumkakao.com|g' /etc/apt/sources.list

Change NodePort range local kubernetes

Change NodePort range local kubernetes

Add following line to add following line to /etc/kubernetes/manifests/kube-apiserver.yaml

- --service-node-port-range=80-32767

Examples

sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
- --advertise-address=172.168.2.11
- --service-cluster-ip-range=10.96.0.0/12
- --service-node-port-range=80-32767
- --client-ca-file=/etc/kubernetes/pki/ca.crt