'전체'에 해당되는 글 224건



2회(#2/3) 에서 이어지는 내용이다 


  • GitHub 를 사용한 개발과정 맛보기 #2 - WordPress Sample & MySQL(PHP)

이번에는 OpenShift GUI Console 이 아닌 명령어 방식으로 oc(OpenShift Client)를 사용해 보겠다. Master Node로 ssh 로그인을 하고 새로운 프로젝트를 생성한다. OpenShift Console에서 실시간으로 확인 가능하다


GitHub.com의 Wordpress project URL(https://github.com/DragOnMe/WordPress.git)을 복사한다


oc 명령으로 php:latest 도커이미지와 WordPress 소스 repository를 지정하여 새로운 App 서비스를 프로젝트에 추가한다. 진행 상황은 텍스트에 나온 설명대로 'oc logs -f bc/어플리케이션이름' 을 수행해 보거나, 앞서와 마찬가지로 GUI를 통해 Build Log 상태를 확인할 수도 있다 


OpenShift Console 화면에서 새로 만든 프로젝트 클릭


WordPress 실행을 위한 PHP 웹서버 부분은 Build & Deploy 가 완료되었고 하나의 Pod가 기동되어 있다


GUI console에서와 달리, 명령어 방식에서는 서비스에 접속 가능하도록 expose 명령으로 주소를 할당해야 한다. 이제 MySQL DB를 준비해 보자


상단의 프로젝트 타이틀 우측 'Add to project' 클릭


Technologies 부분의 'Data Stores' 클릭


'MySQL (Ephemeral)' 선택


계정, 암호 등은 자동 생성되도록 비워 두고 'Create' 클릭(기억할 수 있는 암호와 계정을 정하고 직접 입력해도 된다)


계정, 암호 등은 MySQL 컨테이너 기동 시에 정확히 입력해야 하므로 잘 기억 또는 메모해 둔다(긁어서 복사해 두자). 'Continue to overview' 클릭


위쪽에 새로 만든 MYSQL EPHEMERAL 컨테이너 서비스가 기동되어 pod 1개가 만들어져 있다. 이제 PHP 웹을 실행해 보기 위해 Route(접속 URL)을 클릭


WordPress 초기 셋업(설치) 화면이 연결되었다. 'Let's go!' 클릭


Database Host 에는 OpenShift 프로젝트에 존재하는 서비스명(mysql)을 입력한다. 'Submit' 클릭


'Run the install' 클릭


제목 등을 적당히 입력하고 'Install WordPress' 클릭. 다음 화면에서 'Log In' 클릭, 입력한 로그인 계정으로 로그인


WordPress 대쉬보드 화면 접속 성공!



  • Auto-Scaling 의 사용(Hawkular Metrics)

#1. 수동 scaling - 새로운 프로젝트 생성(Create) 


Autoscale 테스트를 위한 PHP web 을 구동할 예정이다. PHP 선택


언어는 크게 상관이 없다. PHP 5.6 으로 선택하고 'Select'


github.com 프로젝트로 간단한 PHP 페이지 프로젝트를 준비한다. 단순히 phpinfo() 를 수행하는 웹사이트이다. Clone or download 를 통해 URL(https://github.com/DragOnMe/php_for_autoscale.git) 복사


Git Repository URL에 위에서 복사한 URL을 paste(Create)


Continue to overview 로 Overview 화면으로 이동


서비스 내에 Pod 가 하나 만들어지고 Web 서비스 준비 완료. 상단 Routes 주소 클릭


웹서비스 정상 접속 완료


Pod 를 수동으로 Scaling 해보자. Overview 로 돌아와서 Pod 를 클릭


현재 기동되어 있는 단 하나의 Pod 가 보일 것이다. Pod 이름 클릭


동일한 Pod가 여러 개 생길 수 있으므로 Pod 이름은 '서비스명-빌드번호-b9t2h' 와 같은 형태로 자동으로 생성된다. Pod의 상태를 확인해 보면 내부 사설 IP 10.130.0.27 이며 node02 에서 동작하고 있음을 볼수 있다


Pod 우측의 위아래 버튼을 누르면 Pod의 갯수가 증가/감소하는 것을 볼 수 있다


Pod를 2개로 만든 상태에서 Pod 버튼을 클릭하면 2개의 Pod 가 기동되어 있음을 볼 수 있다


새로 만들어진 Pod 를 클릭해 보면, IP 10.129.0.13 이며 node01 에서 동작하고 있다



#2. 자동 scaling, HPA(Horizontal Pod autoscale) 라고 하며, 본 글 작성시 사용중인 Origin 1.4 버전에서는 Hawkular Metrics 가 비교적 안정적으로 통합되어 오토스케일을 위한 추가 설정의 부담이 많이 줄어들었다 - 새로운 프로젝트 생성(Create), 앞서 #1 과 마찬가지로  https://github.com/DragOnMe/php_for_autoscale.git 저장소 주소를 사용하는데, 이번에는 프로젝트 이름만 Auto-2 로 구분가능하도록 진행하자


* 여기서 중요하게 확인해야 할 것은 위의 터미널에 나와 있는 oc get pods 명령어의 결과에서처럼, metric 모니터링을 위한 3개의 기본 pod(hawkular metrics/cassandra, heapster) 가 반드시 Running 상태가 되어 있어야 한다는 것이다 


서비스를 추가하는 화면에서 아래로 스크롤해 내려간다


Scaling 타이틀 아래에 Strategy 부분을 Automatic 으로 바꾸고, 아래의 설정 값들을 적당히 입력한다


조금 더 아래로 내려 가면 Resource Limits 부분이 나온다. 여기에 세부 Metric 설정 값들을 입력해야만(자세한 설정 방법은 여기 참고), 비로소 해당 서비스를 자동으로 Scale-out/in 할 수 있게 된다(Create 클릭)


이전 프로젝트들의 Summary 화면과 다른 부분을 볼 수 있다. 우측 상단의 서비스 URL을 클릭해서 해당 서비스 페이지를 접속해보고, 화면 refresh 를 연속으로 몇 차례 시도한 후, 이 화면으로 돌아오면 Pod 왼쪽의 그래프 부분이 바뀌어 부하 발생에 바뀌어 나타나는 것을 볼 수 있다


osmaster 내부가 아닌 외부 시스템의 터미널 창에서 Apache ab 를 실행하여, 서비스 URL 페이지에 대해 다량의 접속 부하를 발생시키면, OpenShift Summary 화면의 그래프가 바뀌며, Pod가 최대 갯수인 4개까지 자동으로 늘어나는(scaled-out) 것을 볼 수 있다


웹페이지 부하를 멈춘 상태에서 몇 분 정도 지나면 자동으로 최소 Pod 갯수인 1개까지 줄어(scaled-in) 있는 것을 볼 수 있다



  • Persistent-Volume 의 사용

#1. github.com 에서 simple-file-manager 리포리터리의 URL 복사


새로운 프로젝트를 만들고 PHP 언어를 선택. Git URL에 위에서 복사한 주소 paste


빌드 / 배포가 완료 되면 오픈소스로 공개된 간단한 파일 업다운로드 서비스가 기동될 것이다. 새로운 파일을 업로드해 보자


Overview를 통해 Pod를 선택하고 Terminal 로 들어가 보면 새로 업로드한 파일이 보인다. 그러나 이 파일은 Pod(또는 컨테이너)가 없어지면 같이 사라지게 된다(/tmp 아래의 일회용 스토리지 공간에 저장)


* Overview 화면에서 Pod를 1개 늘리고, Terminal 로 들어가서 ls 를 해 보면 새로 만들어진 Pod에는 위에서 업로드한 파일이 보이지 않을 것이다. Pod 별로 각각 일회용 스토리지 영역이 할당되기 때문이다


 

#2 이번에는 OpenShift 내에서 미리 설정된 Persistent Volume(NFS 볼륨)을 사용하여 컨테이너에 직접 마운트해서 사용해 본다. 위 #1과 마찬가지로 새로운 프로젝트를 만들고 https://github.com/DragOnMe/simple-file-manager.git 리포지토리를 연결한다


Deployments 화면 아래의 Add storage 클릭


"프로젝트의 배포 설정에 추가할 persistent volume 요청이 없으니 추가" 하라고 한다(Create storage 클릭)


[Volume Claim 단계] 앞서 Persistent Volume을 ReadWriteMany 모드로 만들었으므로 Access Mode는 RWX 를 선택하고 볼륨 크기를 적당히 지정(미리 만든 각 PV들은 400M 이므로 그 이하의 크기)하고 Create


[Add Storage: 스토리지 할당 단계] 앞의 Volume Claim 에 대해 pv010 볼륨이 자동으로 할당되었다. Mount Path 는 컨테이너 Application 내부에서 사용하는 Mount Point의 Path(simple-file-manager에서 사용하는 data path는 /opt/app-root/src 이다) 를 입력한다


다시 위로 스크롤 해서 Deploy 를 수행한다


Pod 에 터미널로 접속해서 새로운 파일을 하나 만들자. 파일 업로드를 흉내 내 보는 것


Overview 로 돌아가서 Pod 를 하나 늘려두자


새로이 만들어진 Pod 에서 터미널 접속 후 ls 로 확인해 보면 앞에 만들어져 있던 Pod 에서 생성한 파일이 보인다. 두개의 Pod 가 OpenShift 가 제공하는  PV를 공유하고 있음을 알 수 있다


좌측 Storage 메뉴를 통해, 현재 프로젝트에 할당된 스토리지 정보를 확인할 수 있느. 명령어 방식으로 system:admin 계정으로 oc get pv 명령어를 통해서 전체 시스템에서 사용 가능한 PV 정보를 확인힐 수도 있다



이로써 3회에 걸친 OpenShift Origin v3 에 대한 설치와 소프트웨어 엔지니어 입장에서의 간단한 설정 & 사용 방법에 대해 알아 보았습니다. 되도록 간단히 요점 위주로 작성해 두려 했으나, 직접 수행해 본 내용을 꼼꼼히 빠짐 없이 기재하고 설명을 달다 보니 분량이 제법 늘어나 있어서 다소 읽기에 부담이 될지도 모르겠습니다. 불필요한 부분은 알아서 스킵하시고 참고해 주시길~ ^^


혹시 궁금한 점이 있거나, 지적할 부분이 있다면 언제든 댓글로 컨택해 주시기 부탁 드립니다. - Barracuda -





[연관되는 글]

[OpenShift v3 #1] Origin all-in-one, CentOS 7 기반 단일서버 설치 사용법(1/3)

[OpenShift v3 #2] Origin 3-node, CentOS 7 기반 3노드 설치, 사용 방법(2/3)


- Barracuda -



저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


노트북에 서로 다른 사설IP 대역(노트북의 랜카드와 동일한 브리징 네트워크의 IP가 아닌, 내부 사설 네트워크)로 가상네트워크를 구성해서 VM을 만들 경우가 있다. 이 경우 대개 고정IP와 함께 고정된 default gateway를 설정하게 되는데, 사무실과 집을 오가다 보면 매번 VM들의 default gateway를 변경해 주어야 한다.


이런 번거로움을 피하기 위해 VirtualBox 내에 Vyatta Router VM을 만들고, 각 VM들의 사설 IP대역에 맞는 interface(eth1, eth2, eth3, ...)를 각각 설정한 다음, VM에서는 Vyatta Router의 interface IP를 default gateway 로 설정해 둔다. 물론 Vyatta Router의 eth0는 인터넷이 가능한 IP 대역으로 Bridged Network로 구성하고, eth1/eth2/eth3 으로부터 eth0로 라우팅을 해 주어야 각 VM에서 Vyatta Router를 통해 외부로 접속이 가능할 것이다.


이 방법을 응용한다면 VMWare용 Vyatta Router Virtual Appliance나 다른 가상화 솔루션들을 사용할 경우나, Vyatta Router를 사용한 간단한 내부 네트워크를 동적으로 구성할 때도 도움이 될 만 하겠다.


아래에 그 방법에 대해 정리해 둔다.




Vyatta Router VM의 설치


  • Vyos.io 사이트에서 http://packages.vyos.net/iso/release/1.1.7/ 로 접속하여 iso 파일(vyos-1.1.7-amd64.iso)을 다운로드
    - 필요에 따라 다른 iso를 골라서 설치 사용하거나 ova 파일 자체를 appliance로 올려서 쓸수도 있다 
  • Virtual Box에서 linux 머신(CPU1, Ram 512, Disk 2GB)을 생성하고 ISO파일을 CD에 장착하고 Vyatta OS를 설치
    - VirtualBox에서의 VM 생성과 설치 방법에 대해서는 따로 설명하지 않는다
  • 사용할 네트워크에 맞게 네트워크 장치(NIC)를 생성한다. 위 그림에서와 같이 작업을 하려면 다음의 구성정보와 그림을 참고한다
    - 어댑터1: 브리지 어댑터, 무작위 모드: 모두 허용
    - 어댑터2, 3, 4: 호스트 전용 어댑터(사전에 만든 호스트 전용 네트워크 이름 지정, vboxnet0), 무작위 모드: 모두 또는 가상머신에 허용, 어댑터종류:아무거나...반가상네트워크도 가능)


어댑터2,3,4는 모두 동일하게 설정



Vyatta Router Interface 및 라우팅 설정



  • Vyos 설치가 완료되고 리부트한 후, 기본 설정된 관리자 계정 vyos(암호: vyos) 로 로그인한다
  • 초기 설정에 대해 configure, set 등의 명령으로 각각 인터페이스 생성과 설정을 수행하고 확인한다. configure 이후의 설정을 저장하려면 commit 다음에 save 를 반드시 하여야 한다

vyos@vyos:~$ configure

vyos@vyos:~$ set interfaces ethernet eth0 description OUTSIDE

vyos@vyos:~$ set interfaces ethernet eth0 address 192.168.0.201/16

vyos@vyos:~$ set system gateway-address 192.168.0.1

vyos@vyos:~$ set system domain-name 8.8.8.8

vyos@vyos:~$ set service ssh port '22'

vyos@vyos:~$ commit

vyos@vyos:~$ save

Saving configuration to '/config/config.boot'...

vyos@vyos:~$ exit

vyos@vyos:~$ show interfaces
Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
Interface        IP Address                        S/L  Description
---------        ----------                        ---  -----------
eth0             192.168.0.201/16                  u/u  OUTSIDE
lo               127.0.0.1/8                       u/u  
                 ::1/128
vyos@vyos:~$ 
vyos@vyos:~$ configure
vyos@vyos:~$ set interfaces ethernet eth1 description 10-NETWORK
vyos@vyos:~$ set interfaces ethernet eth1 10.255.10.1/24
vyos@vyos:~$ set interfaces ethernet eth1 description 20-NETWORK
vyos@vyos:~$ set interfaces ethernet eth1 10.255.20.1/24
vyos@vyos:~$ set interfaces ethernet eth1 description 30-NETWORK
vyos@vyos:~$ set interfaces ethernet eth1 10.255.30.1/24

vyos@vyos:~$ save

Saving configuration to '/config/config.boot'...

vyos@vyos:~$ exit

vyos@vyos:~$ show interfaces
Codes: S - State, L - Link, u - Up, D - Down, A - Admin Down
Interface        IP Address                        S/L  Description
---------        ----------                        ---  -----------
eth0             192.168.0.201/16                  u/u  OUTSIDE 
eth1             10.255.10.1/24                    u/u  10-NETWORK 
eth2             10.255.20.1/24                    u/u  20-NETWORK 
eth3             10.255.30.1/24                    u/u  30-NETWORK 
lo               127.0.0.1/8                       u/u  
                 ::1/128


  • 내부 사설망과 연결되는 개별 router 포트(eth1, eth2, eth3) 에 대해 다음과 같이 설정하고 저장한다

내부 사설망과 연결되는 개별 router port(eth1, 2, 3)에 대해 다음과 같이 설정한다

vyos@vyos:~$ configure

vyos@vyos:~$ set nat source rule 10 source address 10.255.10.0/24

vyos@vyos:~$ set nat source rule 10 outbound-interface eth0

vyos@vyos:~$ set nat source rule 10 protocol 'all'

vyos@vyos:~$ set nat source rule 10 translation address masquerade

vyos@vyos:~$ set nat source rule 20 source address 10.10.255.0/24

vyos@vyos:~$ set nat source rule 20 outbound-interface eth0

vyos@vyos:~$ set nat source rule 20 protocol 'all'

vyos@vyos:~$ set nat source rule 20 translation address masquerade

vyos@vyos:~$ set nat source rule 30 source address 10.255.30.0/24

vyos@vyos:~$ set nat source rule 30 outbound-interface eth0

vyos@vyos:~$ set nat source rule 30 protocol 'all'

vyos@vyos:~$ set nat source rule 30 translation address masquerade

vyos@vyos:~$ commit

vyos@vyos:~$ save

Saving configuration to '/config/config.boot'...

vyos@vyos:~$ show nat source rule 10

 outbound-interface eth0

 protocol all

 source {

     address 10.255.10.0/24

 }

 translation {

     address masquerade

 }

[edit]

vyos@vyos:~$ exit


  • Router의 정상작동은, 내부 사설망을 사용할 VM(네트워크 어댑터는 호스트 전용, Vyatta Router 의 eth1 등과 동일한 방식의 어댑터)을 생성하고 default gateway를 10.255.10.1  등 Router port의 IP로 설정하여 외부 인터넷으로 ping 이 도달되는지 확인해 보면 된다
  • Vyatta Router를 재시작하기 위해서는 명령어 상태에서 reboot 명령어를, 끄기 위해서는 단순히 poweroff 명령어를 실행한다


사설망 외부에서 내부로의 직접 접근(Port Forwarding)

  • 공유기를 통한 외부에서 내부로의 접근 방식과 동일한 개념으로 Port fordwarding을 설정하면 된다
  • 설정 방법과 과정을 예로 들어보면 아래와 같다(192.168.0.201:65022 -> 10.255.10.100:22, TCP/UDP DNAT 포워딩)
vyos@vyos:~$ configure
vyos@vyos:~$ set nat destination rule 100 description PORT_FORWARD_TEST
vyos@vyos:~$ set nat destination rule 100 destination address '192.168.0.201'
vyos@vyos:~$ set nat destination rule 100 destination port 65022
vyos@vyos:~$ set nat destination rule 100 inbound-interface 'eth0'
vyos@vyos:~$ set nat destination rule 100 protocol tcp_udp
vyos@vyos:~$ set nat destination rule 100 translation address '10.255.10.100'
vyos@vyos:~$ set nat destination rule 100 translation port '22'
vyos@vyos:~$ commit
vyos@vyos:~$ save
Saving configuration to '/config/config.boot'...
vyos@vyos:~$ show nat destination rule 100
 description SIP_FORWARD_TEST
 destination {
     address 192.168.0.201
     port 65022
 }
 inbound-interface eth0
 protocol tcp_udp
 translation {
     address 10.255.10.100
     port 22
 }
[edit]
vyos@vyos:~$ exit


한 걸음 더...

  • 노트북을 사용하는 네트워크 환경이 바뀌었는데 Vyatta Router의 eth0 주소와 각 포트의 라우팅 정보를 수시로 바꿔 주어야 하는 것은 상당히 불편하고 귀찮은 일일 것이다. 사실 본 글의 라우팅 구조를 생각해 낼 떄부터 이 내용을 결론으로 쓰기 위한 것이었는데 ...... 
    - VirtualBox VM관리 화면에서 Vyatta Router VM을 끈 후 복제(Vyatta Router Home 등의  다른 이름으로)
    - 복제할 때 Mac 주소를 자동생성하게(default이지만, 확인 필요) 설정, 단 다음 단계에서 링크가 아닌 '완전한 복제' 실행


     - 새로 만들어진 Vyatta Router VM에 로그인하여, 앞서 수행했던 configure 과정과 비슷한 내용을 다시 수행하는 것이 아니라 vi등의 편집기로 /config/config.boot 내용 중 원하는 부분(외부와 연결되는 eth0 네트워크 정보, default gateway, 4개의 NIC에 대한 MAC 주소-VirtualBox에서 자동할당한 MAC 주소)를 수정하고 reboot 하면 된다. 참고로 아래에 config.boot 파일의 수정해야 할 부분을 붉은 글씨로 따로 표시하였다


vyos@vyos:~$ cat /config/config.boot 

interfaces {

    ethernet eth0 {

        address 192.168.219.201/24

        description OUTSIDE

        duplex auto

        hw-id 08:00:27:8e:73:7a

        smp_affinity auto

        speed auto

    }

    ethernet eth1 {

        address 10.255.10.1/24

        description 10-NETWORK

        duplex auto

        hw-id 08:00:27:b5:91:0e

        smp_affinity auto

        speed auto

    }

    ethernet eth2 {

        address 10.255.20.1/24

        description 20-NETWORK

        duplex auto

        hw-id 08:00:27:6b:38:52

        smp_affinity auto

        speed auto

    }

    ethernet eth3 {

        address 10.255.30.1/24

        description 30-NETWORK

        duplex auto

        hw-id 08:00:27:60:d7:dd

        smp_affinity auto

        speed auto

    }

    loopback lo

...

}

nat {

    destination {

        rule 100 {

            description PORT_FORWARD_TEST

            destination {

                address 192.168.219.201

                port 65022

            }

            inbound-interface eth0

            protocol tcp_udp

            translation {

                address 10.255.10.100

                port 22

            }

        }

        rule 101 {

            description FORWARD_OSMASTER

            destination {

                address 192.168.219.201

                port 60160

            }

            inbound-interface eth0

            protocol tcp_udp

            translation {

                address 10.255.10.160

                port 22

            }

        }

...

    }

    source {

        rule 10 {

            outbound-interface eth0

            protocol all

            source {

                address 10.255.10.0/24

            }

            translation {

                address masquerade

            }

        }

        rule 20 {

            outbound-interface eth0

            protocol all

            source {

                address 10.255.20.0/24

            }

            translation {

                address masquerade

            }

        }

        rule 30 {

            outbound-interface eth0

            protocol all

            source {

                address 10.255.30.0/24

            }

            translation {

                address masquerade

            }

        }

    }

}

service {

    ssh {

        port 22

    }

}

system {

    config-management {

        commit-revisions 20

    }

    domain-name test.local

    gateway-address 192.168.219.1

    host-name vyos

...

vyos@vyos:~$ 



  • 이렇게 한다면, 업무 환경의 네트워크에 따라 Vyatta Router만 바꿔서 사용하므로, 수시로 업무 환경에 따라 VM의 네트워크 정보를 바꿔 주어야 했던 불편함은 깔끔하게 해결되지 않을까 한다



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


지난 회에서 OpenShift Origin v3의 단일서버 구성 설치와 간단한 사용법을 정리하였다, 이번 회에서는 3-노드 구성의 OpenShift v3 설치 과정과와 Kubernetes, OpenShift console 및 CLI를 통한 Orchestration과 몇가지의 Use Case를 정리해 두고자 한다(이 글을 마지막으로 업데이트하는 시점에 OpenShift 릴리즈가 1.5.0으로 바뀌었다. 아래 설정 파일에서 v1.5.0으로 표시된 부분 참고).



사전 설정(Pre-requisites)

  • 서버 구성(최소 사양)
    - OS는 CentOS 7.3, Minimal Server 버전
    - Master: 2 CPU, 4 G memory, 20G+40G(추가디스크) Disk, 추가디스크는 /mnt/persistent-volumes 에 마운트
    - Node01: 2 CPU, 4 G memory, 20G
    - Node02: 2 CPU, 4 G memory, 20G 
  •  내부 테스트 또는 외부 서비스용 별도 DNS 서버 필요(DNS 설정 방식은 이전 포스팅(#1) 참조
    - 단일 노드가 아닌 3-노드 구성에 대한 DNS 설정 부분의 차이점 확인 필요
  • 도메인, IP 주소에 대한 부분은 설치 환경과 요구 사항에 맞게 꼼꼼하게 대조하여 오류가 없도록 진행하는 것이 중요



  • DNS forwarding 을 위한 설정 내용(192.168.0.0/16 네트워크 사용시).


[root@dns ~]# cat /etc/named.conf 

options {

listen-on port 53 { any; };

directory  "/var/named";

dump-file  "/var/named/data/cache_dump.db";

statistics-file "/var/named/data/named_stats.txt";

memstatistics-file "/var/named/data/named_mem_stats.txt";


allow-query     { any; };

recursion yes;


// For non-internal domains

forwarders {

168.126.63.1;

8.8.8.8;

};

auth-nxdomain no; # conform to RFC1035

dnssec-enable yes;

// dnssec-validation yes;

dnssec-lookaside auto;


/* Path to ISC DLV key */

bindkeys-file "/etc/named.iscdlv.key";


managed-keys-directory "/var/named/dynamic";


pid-file "/run/named/named.pid";

session-keyfile "/run/named/session.key";

};


logging {

        channel default_debug {

                file "data/named.run";

                severity dynamic;

        };

};


zone "." IN {

type hint;

file "named.ca";

};


include "/etc/named.rfc1912.zones";

include "/etc/named.root.key";


[root@dns ~]# cat /etc/named.rfc1912.zones

...(생략)...

zone "0.in-addr.arpa" IN {

type master;

file "named.empty";

allow-update { none; };

};


zone "test.local" IN {

        type master; // server type

        file "test.local.zone"; // zone file

        allow-update {none;}; // none: no slave to sync

};


zone "168.192.in-addr.arpa" IN { // Reverse

        type master;

        file "/var/named/rev.168.192.in-addr.arpa";

};


[root@dns ~]# cat /var/named/test.local.zone

$ORIGIN .

$TTL 10


test.local.    IN    SOA    dns.test.local.    root.test.local. (

2017031402    ; serial

1D       ; refresh

1H       ; retry

1W      ; expire

3H )    ; min(Negative cache TTL)

test.local.          IN    NS    dns.test.local.

dns.test.local.    IN    A    192.168.0.3


$ORIGIN    test.local.


osmaster        IN      A       192.168.10.160

osnode01        IN      A       192.168.10.161

osnode02        IN      A       192.168.10.162

*.apps          IN      CNAME   osmaster.test.local.


[root@dns ~]# cat /var/named/rev.168.192.in-addr.arpa 

$TTL 10


@    IN    SOA    dns.test.local.    root.test.local. (

2017031403 ; serial

1D ; refresh

1H ; retry

1W ; expire

3H ) ; min(Negative cache TTL)

test.local.          IN    NS    dns.test.local.

dns.test.local.    IN    A    192.168.0.3


@      IN NS      dns.test.local.

3.0    IN PTR    dns.test.local.


160.10    IN PTR    osmaster.test.local.

161.10    IN PTR    osnode01.test.local.

162.10    IN PTR    osnode02.test.local.



  • 준비된 각각의 노드에서 다음과 같은 사전설치 과정을 따른다
[Master노드: osmaster]

# vi /etc/sysconfig/network-scripts/ifcfg-eth0

...

NETMASK=255.255.0.0

DNS1=192.168.0.3

DNS2=8.8.8.8

# hostnamectl set-hostname osmaster

# nslookup osmaster.test.local

# dig yahoo.com a


# cat /etc/selinux/config 

...

SELINUX=enforcing

...

SELINUXTYPE=targeted


# vi /etc/hosts

...

192.168.10.160 osmaster osmaster.test.local

192.168.10.161 osnode01 osnode01.test.local

192.168.10.162 osnode02 osnode02.test.local


# ssh-keygen

# ssh-copy-id root@osmaster.test.local

# ssh-copy-id root@osnode01.test.local

# ssh-copy-id root@osnode02.test.local


[root@osmaster ~]# domain="test.local"


[root@osmaster ~]# for node in {osmaster.$domain,osnode01.$domain,osnode02.$domain}; do ssh root@$node "yum install -y git bind-utils iptables-services net-tools wget bash-completion chrony; yum update -y"; done


[root@osmaster ~]# systemctl enable iptables

[root@osmaster ~]# systemctl start iptables

[root@osmaster ~]# systemctl enable firewalld

[root@osmaster ~]# systemctl start firewalld

[root@osmaster ~]# firewall-cmd --add-service=ntp --permanent

[root@osmaster ~]# firewall-cmd --reload


[root@osmaster ~]# for node in {osnode01.$domain,osnode02.$domain}; do scp /etc/hosts root@$node:/etc/hosts; done

hosts                                                                                               100%  230     0.2KB/s   00:00    

hosts                                                                                               100%  230     0.2KB/s   00:00    

* 만약 iptables 설치시 "Failed to start iptables.service: Unit is masked." 와 같은 오류가 발생하면 'systemctl unmask iptables" 로 해결한다



[root@osmaster ~]# vi /etc/chrony.conf

server time.bora.net iburst

...

# Allow NTP client access from local network.

allow 192.168.0.0/16

...

# Serve time even if not synchronized to any NTP server.

local stratum 10

...


[root@osmaster ~]# systemctl enable chronyd

[root@osmaster ~]# systemctl restart chronyd

[root@osmaster ~]# chronyc tracking

[root@osmaster ~]# chronyc sources -v


[Node01노드: osnode01]

[root@osnode01 ~]# hostnamectl set-hostname osnode01

[root@osnode01 ~]# vi /etc/chrony.conf

server 192.168.10.160 iburst

...

# Serve time even if not synchronized to any NTP server.

local stratum 10

...


[root@osnode01 ~]# systemctl enable chronyd

[root@osnode01 ~]# systemctl restart chronyd

[root@osnode01 ~]# chronyc tracking

[root@osnode01 ~]# chronyc sources -v



[Node02노드: osnode02]

[root@osnode01 ~]# hostnamectl set-hostname osnode02

[root@osnode02 ~]# vi /etc/chrony.conf

server 192.168.10.160 iburst

...

# Serve time even if not synchronized to any NTP server.

local stratum 10

...


[root@osnode02 ~]# systemctl enable chronyd

[root@osnode02 ~]# systemctl restart chronyd

[root@osnode02 ~]# chronyc tracking

[root@osnode02 ~]# chronyc sources -v



OpenShift v3 설치 시작


  • EPEL repo를 설정하고 필요한 패키지 설치(여기서부터는 모든 설정 작업을 Master node 에서만 수행)
[Master노드: osmaster]

# yum install -y epel-release

# yum install -y docker ansible 

# yum install -y python-cryptography pyOpenSSL.x86_64 java-1.8.0-openjdk-headless python-passlib


  • NFS를 persistent storage 로 설치하기 위해 nfs-server 설치, 설정
    - Persistent Volume은 iSCSI, Ceph RBD, GlusterFS, OpenStack Cinder, AWS EBS, Google Cloud Persistent Disk, Fibre Channel 등을 모두 지원
    - 여기서는 여러 인스턴스(컨테이너)들의 공유 사용이 가능함을 확인하기 위해 대표적인 Shared File System인 NFS를 사용

[root@osmaster ~]# yum groupinstall -y file-server

[root@osmaster ~]# systemctl enable rpcbind

[root@osmaster ~]# systemctl enable nfs-server

[root@osmaster ~]# systemctl start rpcbind

[root@osmaster ~]# systemctl start nfs-server

[root@osmaster ~]# iptables-save > pre-nfs-firewall-rules-server

[root@osmaster ~]# iptables -I INPUT -m state --state NEW -p tcp -m multiport --dport 111,892,2049,32803 -s 0.0.0.0/0 -j ACCEPT

[root@osmaster ~]# iptables -I INPUT -m state --state NEW -p udp -m multiport --dport 111,892,2049,32769 -s 0.0.0.0/0 -j ACCEPT

[root@osmaster ~]# service iptables save

[root@osmaster ~]# setsebool -P virt_use_nfs 1


  • Persistent Metrics Volume for Hawkular Metrics
    - Volume 설정 후 nfs-server 서비스가 정상 작동하는지 확인 필요

[root@osmaster ~]# DIR=/mnt/persistent-volumes/metrics

[root@osmaster ~]# mkdir -p $DIR; chmod 777 $DIR

[root@osmaster ~]# chown nfsnobody:nfsnobody $DIR

[root@osmaster ~]# echo "$DIR 192.168.0.0/16(rw,all_squash)" >> /etc/exports

[root@osmaster ~]# systemctl restart nfs-server

[root@osmaster ~]# systemctl status nfs-server


  • OpenShift Ansible 패키지와 configuration 소스 설치, 설정
    - Auto-scale을 위한 hawkular metrics 설치/설정 포함

# git clone https://github.com/DragOnMe/openshift-ansible.git

# git clone https://github.com/DragOnMe/playbook-allinone-cfg.git

# vi playbook-allinone-cfg/centos7-3node-playbook.cfg

[OSEv3:children]

masters

nodes


[OSEv3:vars]

ansible_ssh_user=root

deployment_type=origin

containerized=true


# Examples

openshift_install_examples=true


# Persistent metrics over NFS

openshift_hosted_metrics_deploy=true

openshift_hosted_metrics_storage_kind=nfs

openshift_hosted_metrics_storage_access_modes=['ReadWriteMany']

openshift_hosted_metrics_storage_nfs_directory=/mnt/persistent-volumes

openshift_hosted_metrics_storage_nfs_options='*(rw,root_squash)'

openshift_hosted_metrics_storage_volume_name=metrics

openshift_hosted_metrics_storage_volume_size=5Gi

openshift_hosted_metrics_storage_host=osmaster.test.local

openshift_hosted_metrics_public_url=https://hawkular-metrics.apps.test.local/hawkular/metrics


# htpasswd authentication

openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', 'challenge': 'true', 'kind': 'HTPasswdPasswordIdentityProvider', 'filename': '/etc/origin/master/htpasswd'}]


# release and image tag

openshift_release=v1.5.0

openshift_image_tag=v1.5.0


# router node

openshift_hosted_router_selector='region=infra'


# subdomain

openshift_master_default_subdomain="apps.test.local"


# pods placement

osm_default_node_selector='region=primary'


[masters]

osmaster.test.local openshift_public_hostname="osmaster.test.local"


[nodes]

osmaster.test.local openshift_node_labels="{'region': 'infra', 'zone': 'default'}" openshift_schedulable=true

osnode01.test.local openshift_node_labels="{'region': 'primary', 'zone': 'datacenter1'}" openshift_schedulable=true

osnode02.test.local openshift_node_labels="{'region': 'primary', 'zone': 'datacenter1'}" openshift_schedulable=true


[root@osmaster ~]# ansible-playbook -i ./playbook-allinone-cfg/centos7-3node-playbook.cfg ./openshift-ansible/playbooks/byo/config.yml 


PLAY [Create initial host groups for localhost] **************************************************************************************


TASK [include_vars] ******************************************************************************************************************

ok: [localhost]


TASK [Evaluate group l_oo_all_hosts] *************************************************************************************************

ok: [localhost] => (item=osmaster.test.local)

ok: [localhost] => (item=osnode01.test.local)

ok: [localhost] => (item=osnode02.test.local)


PLAY [Create initial host groups for all hosts] **************************************************************************************


TASK [include_vars] ******************************************************************************************************************

ok: [osmaster.test.local]

ok: [osnode01.test.local]

ok: [osnode02.test.local]


PLAY [Populate config host groups] ***************************************************************************************************


TASK [fail] **************************************************************************************************************************

 [WARNING]: when statements should not include jinja2 templating delimiters such as {{ }} or {% %}. Found: {{ g_etcd_hosts is not

defined }}


skipping: [localhost]

...

...

...

TASK [openshift_excluder : Enable docker excluder] ***********************************************************************************

changed: [osmaster.test.local]

changed: [osnode02.test.local]

changed: [osnode01.test.local]


TASK [openshift_excluder : Check for openshift excluder] *****************************************************************************

ok: [osmaster.test.local]

ok: [osnode01.test.local]

ok: [osnode02.test.local]


TASK [openshift_excluder : Enable openshift excluder] ********************************************************************************

changed: [osnode01.test.local]

changed: [osnode02.test.local]

changed: [osmaster.test.local]


PLAY RECAP ***************************************************************************************************************************

localhost                  : ok=9    changed=0    unreachable=0    failed=0   

osmaster.test.local      : ok=617  changed=104  unreachable=0    failed=0   

osnode01.test.local      : ok=275  changed=41   unreachable=0    failed=0   

osnode02.test.local      : ok=272  changed=39   unreachable=0    failed=0   



  • Containerized 방식의 ansible-playbook을 통한 자동 설치 과정은 하드웨어 성능에 따라 다르겠지만, 대략 10~20분 정도 소요된다
  • 오류 없이 성공적으로 수행 된 경우, 최종 출력되는 라인들에서 각 노드 별로 failed=0 값이 출력되어야 한다.
  • 실행 도중 오류가 나는 경우, 진행되었던 설치 과정의 흔적을 다음과 같은 명령으로 Clear 한 후, 각종 문제(주로 selinux 설정, firewalld-iptables, nfs 마운트, URL 오류 관련 문제 등)을 해결하고, 위의 ansible-playbook 명령으로 다시 설치를 시도해야 한다
  • 진행된 모든 과정을 이전 상태로 되돌리기 위해서는 아래 과정을 참고한다

[root@osmaster ~]# ansible-playbook -i ./playbook-allinone-cfg/centos7-3node-playbook.cfg ./openshift-ansible/playbooks/adhoc/uninstall.yml

[root@osmaster ~]# iptables -t nat -F; iptables -F

[root@osmaster ~]# rm -rf /mnt/persistent-volumes/*

[root@osmaster ~]# rm -f /etc/exports


  • 최초 OpenShift 계정으로 guest 계정 생성, 접속 테스트

[root@osmaster ~]# cd /etc/origin/master/

[root@osmaster ~]# cat htpasswd

[root@osmaster ~]# htpasswd -b /etc/origin/master/htpasswd guest openshift

[root@osmaster ~]# oc login

Authentication required for https://osmaster:8443 (openshift)

Username: guest

Password: 

Login successful.


You don't have any projects. You can try to create a new project, by running


    oc new-project <projectname>


[root@osmaster ~]# oc status

Error from server: User "guest" cannot get projects in project "default"

[root@osmaster ~]# oc logout

Logged "guest" out on "https://osmaster:8443"


  • OpenShift용 Persistent Volume 생성(2GB * 13)
    - 사전에 만들어 두고 인스턴스들이 가져다 쓰는 개념으로, 개별 볼륨의 크기와 갯수는 스토리지의 크기와 상황에 맞게 조정
    - Persistent(지속적인) 스토리지에 반대되는 개념은 Ephemeral(일회용의, 단명하는) 스토리지로, Docker가 기본적으로 사용하는 스토리지는 /tmp/ 영역을 사용하는 AUFS(Union 파일시스템) 방식이며, Docker 컨테이너가 삭제되면 이 영역의 데이터도 같이 사라짐
    - 볼륨의 크기 지정 방법과 옵션에 대해서는 Kubernetes Resource Model 을 참고

[root@osmaster ~]# vi playbook-allinone-cfg/pv.json

{

 "apiVersion": "v1",

 "kind": "PersistentVolume",

 "metadata": {

 "name": "pv001"

},

"spec": {

   "capacity": {

   "storage": "2Gi"

   },

   "accessModes": [ "ReadWriteMany" ],

   "nfs": {

      "path": "/mnt/persistent-volumes/pv001",

      "server": "192.168.10.160"

   },

   "persistentVolumeReclaimPolicy": "Recycle"

   }

}


[root@osmaster ~]# oc login -u system:admin

[root@osmaster ~]# for i in `seq -w 001 014`; do \

SHARE=/mnt/persistent-volumes/pv$i; \

mkdir -p $SHARE; chmod 777 $SHARE; \

chown nfsnobody:nfsnobody $SHARE; \

echo "$SHARE 192.168.0.0/16(rw,all_squash)" >>/etc/exports; \

sed s/pv001/pv$i/g /root/playbook-allinone-cfg/pv.json | oc create -f -; \

done

persistentvolume "pv001" created

persistentvolume "pv002" created

persistentvolume "pv003" created

persistentvolume "pv004" created

persistentvolume "pv005" created

persistentvolume "pv006" created

persistentvolume "pv007" created

persistentvolume "pv008" created

persistentvolume "pv009" created

persistentvolume "pv010" created

persistentvolume "pv011" created

persistentvolume "pv012" created

persistentvolume "pv013" created

[root@osmaster ~]# oc get pv

NAME      CAPACITY   ACCESSMODES   RECLAIMPOLICY   STATUS      CLAIM     REASON    AGE

pv001     2Gi        RWX           Recycle         Available                       9s

pv002     2Gi        RWX           Recycle         Available                       8s

pv003     2Gi        RWX           Recycle         Available                       8s

pv004     2Gi        RWX           Recycle         Available                       8s

pv005     2Gi        RWX           Recycle         Available                       8s

pv006     2Gi        RWX           Recycle         Available                       7s

pv007     2Gi        RWX           Recycle         Available                       7s

pv008     2Gi        RWX           Recycle         Available                       7s

pv009     2Gi        RWX           Recycle         Available                       7s

pv010     2Gi        RWX           Recycle         Available                       6s

pv011     2Gi        RWX           Recycle         Available                       6s

pv012     2Gi        RWX           Recycle         Available                       6s

pv013     2Gi        RWX           Recycle         Available                       6s

pv014     2Gi        RWX           Recycle         Available                       5s

pv015     2Gi        RWX           Recycle         Available                       5s

pv016     2Gi        RWX           Recycle         Available                       5s

pv017     2Gi        RWX           Recycle         Available                       5s

pv018     2Gi        RWX           Recycle         Available                       4s

pv019     2Gi        RWX           Recycle         Available                       4s


## Openshift 재설치 등을 위한 삭제시에는 oc delete pv <pv-name> 으로 개별 볼륨을 삭제하거나 다음과 같이 한번에 삭제할 수도 있다(데이터 유실에 유의해야 함)
[root@osmaster ~]# for i in `seq -w 001 014`; do oc delete pv pv$i; done
[root@osmaster ~]# rm -rf /mnt/persistent-volumes/metrics

  • Openshift 재시작, Guest 및 관리자 계정(admin) 생성

[root@osmaster ~]# systemctl restart origin-master

[root@osmaster ~]# htpasswd -b /etc/origin/master/htpasswd admin openshift

[root@osmaster ~]# oc login -u system:admin

[root@osmaster ~]# oadm policy add-cluster-role-to-user cluster-admin admin

[root@osmaster ~]# oc get all 



기능 테스트 단계


  • 웹브라우저를 통해 https://osmaster.test.local:8443 으로 접속하면 Origin 로그인 화면이 나타나고 guest 계정으로 로그인이 정상적인지 확인해 보자


이게 전부라면 얼마나 좋을까만, 기쁘게도 확인하고 알아둬야 할 내용들이 더 있다(실은 아주 더 많다). 참고로 OpenShift 를 익숙하게 사용하기 위해서는 OpenShift Console Web(GUI)를 통한 기본 사용법, 프로젝트의 구성 현황/상태 확인 방법과 함께 CLI(oc, oadm 등 명령. → 참고) 사용과 빌드/배포 자동화를 위한 yaml 파일 수정/편집과 OpenShift 의 Architecture에 대한 이해가 필요할 듯 싶다. 



  • OpenShift 사용과 활용에 앞서


여기서는 

  • 먼저, Googling으로 쉽게 접해볼 수 있는 오픈소스 프로젝트 2가지를 GibHub 를 통해 연동하여 환경 구축을 한 후, OpenShift로 개발/운영을 수행하는 과정(프로젝트 생성, GitHub연동, Build & Deploy, 테스트 과정)을 살펴본 뒤, 
  • 두 번 째로 Auto-scaling metric 설정을 통한 부하(cpu/memory)에 따른 자동 scale-up/down  을 테스트 해 보고 
  • 세 번 째로 persistent volume 이 없을 때(docker의 ephemeral 특성)와 있을 때(persistent 기능 확보) 각각에 대해 어떤 차이가 보이는지 확인해 본 다음, 위에서 생성한 NFS server를 통한 persistent volume을 공유 스토리지로 사용해 볼 것이다, 



4가지 대표적인 Use Case 경험하기


  • GibHub 를 사용한 개발과정 맛보기 #1 - Spring MVC Showcase(Java)
  • GibHub 를 사용한 개발과정 맛보기 #2 - WordPress Sample & MySQL(PHP)
  • Auto-Scaling 의 사용(Hawkular Metrics)
  • Persistent-Volume 의 사용



  • GibHub 를 사용한 개발과정 맛보기 #1 - Spring MVC Showcase(Java)


spring-mvc-showcase 프로젝트를 포크하고 URL을 복사해 둔다. pom.xml 파일이 있는 Maven 프로젝트(Clone or download 클릭 & URL 복사)


OpenShift 콘솔에 로그인(설치 직후에 생성한 guest 계정 정보 guest / openshift 로 로그인)


New Project 를 선택하고 새로운 프로젝트를 생성(Create)


프로젝트를 생성하면 자동적으로 Add to Project 과정이 실행되고 프로젝트에 추가할 컴포넌트를 선택할 수 있게 된다. 여기서는 Java 선택


OpenShift 는 Java 를 구동할 수 있는 컴포넌트로 WildFly(WAS인 Jboss의 Open 버전) Docker Container를 기본으로 제공한다(Select)


Resource 이름을 적당히 지정하고, 위에서 복사해 둔 GitHub 프로젝트의 URL을 복사(Create)


"Application created." 라는 메시지와 함께 Continue to overview 링크를 누르고 싶게 만드는 화면이 나타난다. 아래에 Command Line Tool인 oc(OpenShift Client) 의 간단한 사용법이 보이는데, 현재 GUI상에서 수행하는 모든 작업들은 oc 명령어로 가능하며, GUI방식보다 디테일한 작업을 위해서는 oc 명령어를 익혀두어야 할 때가 올 수도 있다(일단 클릭)


다음 단계인 Build 과정으로, Summary 탭 내에서 Application의 빌드가 수행되고 있다는 표시로 동적아이콘이 빙빙 돌아가고 있다(View Log 클릭) 


터미널 창에서 oc logs 를 수행한 결과와 동일한 로그 내용이 실시간으로 확인 가능하다. 'Status: ' 우측에 역시 동적아이콘이 빙빙 돌아가고 있으며, Build가 완료되면 'Complete' 로 바뀐다.


Build가 완료되었다. 콘솔 창을 가장 아래로 스크롤해 보면 "Push successful" 이라고 메시지가 나올 것이다. Build가 완료된 직후, OpenShift는 Build된 바이너리 파일들이 담긴 Docker Image를 새로 생성된 Pod에 밀어 넣고 Application 이 실행 가능한 상태로 Deploy를 완료한다


왼쪽 메인 아이콘 목록에서 'Overview' 를 클릭, 프로젝트내 리소스를 확인한다. 하단 직사각형 Box 부분이 하나의 Service 이며, 'sample-01-mvc' 라는 서비스 내에 pod가 1개 위치하고, 해당 컨테이너의 Build 이미지 정보가 왼쪽에 표시된다. 상단에 서비스에 접속할 수 있는 URL(서비스 Routes)이 자동으로 생성되어 있음을 볼수 있다(URL 클릭) 


마지막으로, 현재까지 빌드&배포된 Application의 정상 동작을 확인해 본다. 앞에서 URL을 클릭하면 새로운 탭에서 OpenShift 가 만들어준 Application이 동작하는 웹페이지가 나타난다. 주소 끝에 'spring-mvc-showcase/' 경로를 입력하고 엔터 


Java 개발자에게 친숙한 MVC Showcase 페이지가 성공적으로 수행되었다. 각 탭을 클릭해서 정상 작동하는지 테스트 해보자


     GitHub 프로젝트의 소스를 개발자가 수정하는 경우를 따라가 보자. 일반적으로는 개발자가  git client 로 프로그램을 수정하고, 최종적으로 Origin의 Master Branch 에 수정이 가해지겠지만, 여기서는 단순히 테스트를 위한 것이므로 GitHub에서 Master를 직접 수정하는 것으로 진행한다.  'home.jsp' 파일을 위와 같이 수정하자


수정된 내용을 commit


메인 아이콘을 통해 'Builds > Builds' 클릭


Build 리스트의 빌드명 클릭


'Start Build' 클릭, 아래에 Build 진행 상태가 표시되며, 나머지는 앞서 최초 빌드 진행 과정과 거의 동일하며, 앞서와 마찬가지로 'View Log' 를 통해 진행 상황을 확인할 수 있다. 최종 수행 결과에 대한 확인 과정은 위와 반복되는 내용이므로 생략한다



아래에 이어지는 나머지 3가지 Case는, 페이지 분량이 많은 관계로 3편으로 이어서 연재한다. 


  • GibHub 를 사용한 개발과정 맛보기 #2 - WordPress Sample & MySQL(PHP)
  • Auto-Scaling 의 사용(Hawkular Metrics)
  • Persistent-Volume 의 사용



[연관되는 글]

[OpenShift v3 #1] Origin all-in-one, CentOS 7 기반 단일서버 설치 사용법(1/3)

[OpenShift v3 #3] Origin 3-node, CentOS 7 기반 3노드 설치, 사용 방법(3/3)


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


OpenShift 를 설치 하기위한 구성은 싱글 서버(All-In-One)부터 10대 이상의 구성까지 다양하다. 이번 포스팅에서는 단일서버에 OpenShift 를 설치하는 과정을 기록하고 관리하도록 한다. OpenShift 는 v3로 넘어오면서 Kubernetes 의 Container, Storage, SDN 오케스트레이션을 완성도 있게 구현하여, 한층 단순하고 안정적으로 다룰 수 있게 되었다. 즉 기존의 Gear/Broker/Cartridges 의 생소한 개념들을 걷어내고 Docker/Kubernetes/Docker Images 개념으로 재정립하여 구조적으로 간단해지고, 널리 알려진 인프라 기술이 적용되었으며, 더 나은 UX를 제공하게 된 것이다. 


최근에 들어서 기존의 rpm 설치 방식을 간결하게 만든 dockerized 설치 방식이 일반화되고 있으며, 여기서 설치할 OpenShift는 v3 Origin 버전의 경우도, Dockerized 설치 방식으로 Ansible-playbook 을 통해  로컬서버에 호스팅되는 형식으로 구현되어 있다.


참고로 본 글은, OpenShift 설치와 사용법에 관련한 총 2개의 시리즈 중 첫 번째로, 다음 회에서는 1 Master 2 Node 설치 과정과 Kubernetes를 통한 Docker/Container, Storage, Network의 Orchestration과 Build/Deploy 에 대해 실제 사용 사례(Use Case)별 구현과 체험을 간략히 진행해 보는 것으로 예정하고 있다. 



  • OpenShift v3 Concepts



사전 설정(Pre-requisites)

  • 서버 구성(최소 사양) : 2 CPU, 8 G memory, 20G+20G(추가디스크) Disk
    - OS는 CentOS 7.3, Minimal Server 버전
    - 추가디스크는 Persistent Storage를 위한 NFS 용 스토리지로, /mnt/persistent-volumes 에 마운트
  •  내부 테스트 또는 외부 서비스용 별도 DNS 서버 필요
    - 여기서는 내부 테스트용 test.local 도메인을 named 를 통해서 존파일 설정(DNS forwarding 사용)



  • DNS forwarding 을 위한 설정 내용을 일단 정리해두고 넘어가자(192.168.0.0/16 네트워크 사용시).


[root@dns ~]# cat /etc/named.conf 

options {

listen-on port 53 { any; };

directory "/var/named";

dump-file "/var/named/data/cache_dump.db";

statistics-file "/var/named/data/named_stats.txt";

memstatistics-file "/var/named/data/named_mem_stats.txt";


allow-query     { any; };

recursion yes;


// For non-internal domains

forwarders {

168.126.63.1;

8.8.8.8;

};

auth-nxdomain no; # conform to RFC1035

dnssec-enable yes;

// dnssec-validation yes;

dnssec-lookaside auto;


/* Path to ISC DLV key */

bindkeys-file "/etc/named.iscdlv.key";


managed-keys-directory "/var/named/dynamic";


pid-file "/run/named/named.pid";

session-keyfile "/run/named/session.key";

};


logging {

        channel default_debug {

                file "data/named.run";

                severity dynamic;

        };

};


zone "." IN {

type hint;

file "named.ca";

};


include "/etc/named.rfc1912.zones";

include "/etc/named.root.key";


[root@dns ~]# cat /etc/named.rfc1912.zones

...(생략)...

zone "0.in-addr.arpa" IN {

type master;

file "named.empty";

allow-update { none; };

};


zone "test.local" IN {

        type master; // server type

        file "test.local.zone"; // zone file

        allow-update {none;}; // none: no slave to sync

};


zone "168.192.in-addr.arpa" IN { // Reverse

        type master;

        file "/var/named/rev.168.192.in-addr.arpa";

};


[root@dns ~]# cat /var/named/test.local.zone

$ORIGIN .

$TTL 10


test.local.    IN    SOA    dns.test.local.    root.test.local. (

2017031402    ; serial

1D       ; refresh

1H       ; retry

1W      ; expire

3H )    ; min(Negative cache TTL)

test.local.          IN    NS    dns.test.local.

dns.test.local.    IN    A    192.168.0.3


$ORIGIN    test.local.


openshift3    IN    A    192.168.10.145

*.openshift3  IN    A    192.168.10.145


[root@dns ~]# cat /var/named/rev.168.192.in-addr.arpa 

$TTL 10


@    IN    SOA    dns.test.local.    root.test.local. (

2017031403 ; serial

1D ; refresh

1H ; retry

1W ; expire

3H ) ; min(Negative cache TTL)

test.local.          IN    NS    dns.test.local.

dns.test.local.    IN    A    192.168.0.3


@      IN NS      dns.test.local.

3.0    IN PTR    dns.test.local.


145.10    IN PTR    openshift3.test.local.



  • 준비된 노드에서 다음과 같은 사전설치 과정을 따른다

# vi /etc/sysconfig/network-scripts/ifcfg-eth0

...

NETMASK=255.255.0.0

DNS1=192.168.0.3

DNS2=8.8.8.8

# hostnamectl set-hostname openshift14-allinone

# yum install -y bind-utils git iptables-services

# systemctl enable iptables

# systemctl start iptables

# nslookup openshift3.test.local

# dig yahoo.com a


# ssh-keygen

# ssh-copy-id root@openshift3.test.local

# ssh root@openshift3.test.local



OpenShift v3 설치 시작


  • EPEL repo를 설정하고 필요한 패키지 설치

# yum install -y epel-release

# yum install -y docker wget ansible 

# yum install -y python-cryptography pyOpenSSL.x86_64


  • OpenShift Ansible 패키지와 configuration 소스 설치, 설정

# git clone https://github.com/DragOnMe/openshift-ansible.git

# git clone https://github.com/DragOnMe/playbook-allinone-cfg.git

# vi playbook-allinone-cfg/centos7-1node-playbook.cfg

[OSEv3:children]

masters

nodes


[OSEv3:vars]

ansible_ssh_user=root

deployment_type=origin

openshift_master_identity_providers=[{'name': 'htpasswd_auth', 'login': 'true', 'challenge': 'true', 'kind': 'HTPasswdPasswordIdentityProvider', 'filename': '/etc/origin/master/htpasswd'}]

containerized=true

openshift_release=v1.4.1

openshift_image_tag=v1.4.1

openshift_public_hostname=openshift3.test.local

openshift_master_default_subdomain=apps.openshift3.test.local

openshift_hosted_metrics_deploy=false



[masters]

openshift3.test.local openshift_schedulable=true


[nodes]

openshift3.test.local openshift_node_labels="{'region': 'infra', 'zone': 'default'}" openshift_schedulable=true


# ansible-playbook -i ./playbook-allinone-cfg/centos7-1node-playbook.cfg ./openshift-ansible/playbooks/byo/config.yml


  • 실행 도중 오류가 나는 경우 진행되었던 설치 과정의 흔적을 Clear 한 후, 각종 문제(주로 selinux 설정, firewalld-iptables 관련 문제 등)을 해결하고 다시 설치를 시도해야 한다

# ansible-playbook -i ./playbook-allinone-cfg/centos7-1node-playbook.cfg ./openshift-ansible/playbooks/adhoc/uninstall.yml


  • 최초 OpenShift 계정으로 guest 계정 생성, 접속 테스트

# cd /etc/origin/master/

# cat htpasswd

# htpasswd -b /etc/origin/master/htpasswd guest openshift

# oc login

# oc status

# oc logout


  • NFS를 persistent storage 로 설치하기 위해 nfs-server 설치, 설정

# yum groupinstall -y file-server

# systemctl enable rpcbind

# systemctl enable nfs-server

# systemctl start rpcbind

# systemctl start nfs-server

# iptables-save > pre-nfs-firewall-rules-server

# iptables -I INPUT -m state --state NEW -p tcp -m multiport --dport 111,892,2049,32803 -s 0.0.0.0/0 -j ACCEPT

# iptables -I INPUT -m state --state NEW -p udp -m multiport --dport 111,892,2049,32769 -s 0.0.0.0/0 -j ACCEPT

# service iptables save

# setsebool -P virt_use_nfs 1


  • OpenShift용 Persistent Volume 생성(1GB * 15)

# vi playbook-allinone-cfg/pv.json

{

 "apiVersion": "v1",

 "kind": "PersistentVolume",

 "metadata": {

 "name": "pv001"

},

"spec": {

   "capacity": {

   "storage": "1Gi"

   },

   "accessModes": [ "ReadWriteOnce" ],

   "nfs": {

      "path": "/mnt/persistent-volumes/pv0001",

      "server": "192.168.10.145"

   },

   "persistentVolumeReclaimPolicy": "Recycle"

   }

}


# oc login -u system:admin

# for i in `seq -w 001 015`; do \

SHARE=/mnt/persistent-volumes/pv$i; \

mkdir -p $SHARE; chmod 777 $SHARE; \

chown nfsnobody:nfsnobody $SHARE; \

echo "$SHARE 192.168.0.0/16(rw,all_squash)" >>/etc/exports; \

sed s/pv001/pv$i/g /root/playbook-allinone-cfg/pv.json | oc create -f -; \

done

# oc get pv

# oc version

# oc get user

# oc config view

# oc get all


  • Guest 및 관리자 계정(admin) 생성

# htpasswd -b /etc/origin/master/htpasswd admin openshift

# oc login -u system:admin

# oadm policy add-cluster-role-to-user cluster-admin admin



마무리 & 테스트 단계


  • 웹브라우저를 통해 https://openshift3.test.local:8443 으로 접속하면 Origin 로그인 화면이 나타난다




다음 회를 기약하며


이번 회에서는 단일 서버 구성의 설치 과정에 대해서만 간단히 정리하고, 다음 #2 회에서 3-노드 설치, 설정과 함께, 3가지의 OpenShift 사용 Use Case를 직접 다뤄 보면서 조금 더 사용법과 개념에 익숙해 지는 기회를 만들고자 한다.


참고로 OpenShift Origin을 VirtualBox 등의 단일 VM내에서 작동시킬 수 있는 MiniShift 도 있으니 한 번 설치, 사용해 볼 만하다. DNS를 통하지 않는 IP 주소 기반의 사용 방식이지만 OpenShift의 대다수 기능들을 그대로 사용할 수 있게 만들어져 있다. 참조 주소는 다음과 같다.




[연관되는 글]

[OpenShift v3 #2] Origin 3-node, CentOS 7 기반 3노드 설치, 사용 방법(2/3)

[OpenShift v3 #3] Origin 3-node, CentOS 7 기반 3노드 설치, 사용 방법(3/3)



- Barracuda -



저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


Openstack Mitaka 최종본(v6) 설치 및 설정 종합본



본 편의 도입 및 준비 부분은, 위의 지난 2016년 9월에 #1 편에서 다뤘던 Mitaka v5 버전 내용을 그 근간으로 한다. Mitaka v5 는 지난 2016년 6월에 출시된 이후 거의 몇 일 ~ 몇 주 단위로 활발하게 수정 변경되어 11월 9일에 최종 버전인 v6 가 릴리즈되어 있다. 참고로 CentOS 계열(Redhat, CentOS, Fedora)을 위한 디플로이 도구인 Packstack 의 Mitaka 를 위한 버전은 다음 그림에서 보다시피 v5, v6 만 존재하고 있다.



지난 9월의 계획에서는 2016년 10월 릴리즈 예정이었던 Newton 버전을 설치하고 검증하는 과정을 다루려고 했으나, 릴리즈 직후에 한 번 시험삼아 설치해 보니 지나치게 불안정한 면이 커서, 현 시점의 안정적인 Mitaka 최종 버전을 따로 정리해 두는 편이 나을 듯 하여 급하게 본 포스팅을 구성해 보려 한다.


  • 설치 준비 과정 이후의 상세 내용


전 편의 1~5 섹션까지 내용은 그대로 사용하는 것으로 하고, 이후의 섹션 6부터 전 편의 보충 형식으로 써내려 간다. 결론적으로는, 오픈스택 Mitaka 버전을 설치, 설정하기 위해서는 지난 #1편(1~5섹션)과 본 #2편(6섹션 이후)를 연이어서 참고하면 되지 않을까 하는 생각이다.



6. 오픈스택 설치를 위한 각 노드들의 패키지 설정


  • 각 노드의 리포지토리를 변경하고 필수 패키지 설정


이미 위에서 언급했다시피, 다수의 의존성 패키지를 따로 설치하지 않고 yum update -y 를 통해 최신 패키지 업데이트를 진행한 다음 Openstack v6 를 따로 다운로드 설치하는 과정을 아래에 써내려 간다. 역시 Mitaka의 최종 버전이니만큼 비교적 안정적이고 오류 없이 잘 진행됨을 확인하였다(9월버전 v5에서 오류가 발생하였던 lbaas 포함 설치도 v6에서는 오류 없이 잘 진행됨).


# yum update -y

# yum install -y net-tools

# yum install -y wget


  • 각 노드의 /etc/hosts 파일에 다음의 3개 라인을 추가

10.10.10.6 openstack-ctrl

10.10.10.7 openstack-net

10.10.10.8 openstack-cmp1


  • 마찬가지로 각 3개 노드에서 보안 등 각종 관리패키지를 오픈스택 설치가 가능해 지도록 구성

# systemctl disable firewalld

# systemctl stop firewalld

# systemctl disable NetworkManager

# systemctl stop NetworkManager

# yum remove -y NetworkManager

# systemctl enable network

# systemctl start network



7. Packstack 으로 Openstack 자동 설치하기

Packstack은 Controller 노드에서 answer 파일을 읽어 들여서 자신을 포함하여 하위 노드(Network 노드, Compute 노드들)에서 각종 소프트웨어들을 설치/설정하는 스크립트를 단계적으로 실행해 주는 역할을 한다(Redhat 제공 RDO 프로젝트 참고: www.rdoproject.org). 이를 위해 각 하위 노드에 암호 없이 접속이 가능해야 한다. 


  • Controller 노드에서 각 노드로 Passwordless ssh 설정

[root@openstack-ctrl ~]# ssh-keygen 

[root@openstack-ctrl ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.7

[root@openstack-ctrl ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.8


#--> 암호 없이 정상적으로 접속되는지 확인

[root@openstack-ctrl ~]# ssh openstack-net

[root@openstack-ctrl ~]# ssh openstack-cmp1


  • Packstack repo 설정 및 설치 시작
이전에 진행했던 방식으로 다음과 같이 rdo-release.rpm 을 다운로드하고 설치하면, 현재 시점의 가장 최근 버전인 newton 버전이 설치되어 버리므로,

[root@openstack-ctrl ~]# yum install -y https://www.rdoproject.org/repos/rdo-release.rpm

[root@openstack-ctrl ~]# yum install -y openstack-packstack

...


특정 버전의 Openstack을 명시적으로 다운로드 & 설치하기 위해서는 https://repos.fedorapeople.org/repos/openstack/ 에서 해당 버전을 찾아서 설치하는 과정을 밟아 가면 된다. 여기서는 생성된 answer.txt 파일 항목 중 특별히 편집 수정해야 하는 부분을 표시하여 간략한 설명을 달아 두도록 한다.

[root@openstack-ctrl ~]# yum install -y https://repos.fedorapeople.org/repos/openstack/openstack-mitaka/rdo-release-mitaka-6.noarch.rpm

[root@openstack-ctrl ~]# yum update -y

[root@openstack-ctrl ~]# yum install -y openstack-packstack

[root@openstack-ctrl ~]# su - stack

[stack@openstack-ctrl ~]# sudo packstack --gen-answer-file=answer.txt

[stack@openstack-ctrl ~]# sudo vi answer.txt

[general]

...

# 개별 항목에 자동 적용 되는 공통 암호

CONFIG_DEFAULT_PASSWORD=openstack


# 여기서는 사용하지 않음

CONFIG_MANILA_INSTALL=n


# 여기서는 사용하지 않음

CONFIG_SWIFT_INSTALL=n


# 미터링 패키지 설치

CONFIG_CEILOMETER_INSTALL=y


# 여기서는 사용하지 않음

CONFIG_AODH_INSTALL=n


# 여기서는 사용하지 않음

CONFIG_GNOCCHI_INSTALL=n


# 여기서는 사용하지 않음

CONFIG_SAHARA_INSTALL=n


# 오케스트레이션을 위한 패키지 설치

CONFIG_HEAT_INSTALL=y


# 여기서는 사용하지 않음

CONFIG_IRONIC_INSTALL=n


# ntp 시간동기화는 수동 설치 및 설정

CONFIG_NTP_SERVERS=


# 여기서는 사용하지 않음

CONFIG_NAGIOS_INSTALL=n


# API 서비스 패키지 등이 설치되는 Controller 노드

CONFIG_CONTROLLER_HOST=10.10.10.6


# Compute 서비스 패키지들이 설치되는 노드

CONFIG_COMPUTE_HOSTS=10.10.10.8


# Compute networking (nova network) 또는 OpenStack Networking (neutron) 이 설치 되는 Network 노드

CONFIG_NETWORK_HOSTS=10.10.10.7


# AMQP 서비스 설치 노드(여기서는 Controller 노드)

CONFIG_AMQP_HOST=10.10.10.6


# 기본 DB인 MariaDB 설치 노드(여기서는 Controller 노드)

CONFIG_MARIADB_HOST=10.10.10.6


# MariaDB 관리자 계정 암호

CONFIG_MARIADB_PW=openstack


# Keystone DB 암호

CONFIG_KEYSTONE_DB_PW=openstack


# Identity 서비스 admin 계정 암호

CONFIG_KEYSTONE_ADMIN_PW=openstack


# Identity service demo 계정 암호

CONFIG_KEYSTONE_DEMO_PW=openstack


# Glance DB 암호

CONFIG_GLANCE_DB_PW=openstack


# Glance Identity service 암호

CONFIG_GLANCE_KS_PW=openstack


# Nova DB 암호

CONFIG_NOVA_DB_PW=openstack


# Nova Identity service 암호

CONFIG_NOVA_KS_PW=openstack


# Neutron Identity service 암호

CONFIG_NEUTRON_KS_PW=openstack


# Neutron DB 암호

CONFIG_NEUTRON_DB_PW=openstack


# Neutron L3 의 External OVS bridge 지정(비워 두면 default로 linuxbridge)

CONFIG_NEUTRON_L3_EXT_BRIDGE=br-ex


# OpenStack Networking metadata agent 암호.

CONFIG_NEUTRON_METADATA_PW=openstack


# OpenStack Networking's Load-Balancing-as-a-Service (LBaaS) 설치 여부

CONFIG_LBAAS_INSTALL=y


# OpenStack Networking's L3 Metering agent 설치 여부

CONFIG_NEUTRON_METERING_AGENT_INSTALL=y


# neutron.ml2.type_drivers 로 사용할 드라이버 지정. comma 로 구분 ['local','flat', 'vlan', 'gre', 'vxlan']

CONFIG_NEUTRON_ML2_TYPE_DRIVERS=vxlan


# Tenant network로 사용할 network type을 순서대로 지정. comma 로 구분 ['local', 'vlan', 'gre', 'vxlan']

CONFIG_NEUTRON_ML2_TENANT_NETWORK_TYPES=vxlan


# neutron.ml2.mechanism_drivers 로 로딩할 메커니즘 드라이버 지정. comma 로 구분

# ['logger', 'test', 'linuxbridge', 'openvswitch', 'hyperv', 'ncs', 'arista', 'cisco_nexus', 'mlnx', 'l2population', 'sriovnicswitch']

CONFIG_NEUTRON_ML2_MECHANISM_DRIVERS=openvswitch


# OpenStack Networking으로 사용할 L2 agent 지정 ['linuxbridge', 'openvswitch']

CONFIG_NEUTRON_L2_AGENT=openvswitch


# HEAT DB 암호

CONFIG_HEAT_DB_PW=openstack


# HEAT Identity service 암호

CONFIG_HEAT_KS_PW=openstack


# HEAT Identity domain 관리자 암호

CONFIG_HEAT_DOMAIN_PASSWORD=openstack


# Provisioning 테스트를 위한 demo 설치 여부

CONFIG_PROVISION_DEMO=y


# Ceilometer Identity service 암호

CONFIG_CEILOMETER_KS_PW=openstack


# MongoDB 설치 서버(여기서는 Controller 노드)

CONFIG_MONGODB_HOST=10.10.10.6


# Redis master server

CONFIG_REDIS_MASTER_HOST=10.10.10.6


[stack@openstack-ctrl ~]# sudo packstack --answer-file=answer.txt

Welcome to the Packstack setup utility


The installation log file is available at: /var/tmp/packstack/20161209-052810-KZtyhp/openstack-setup.log


Installing:

Clean Up                                             [ DONE ]

Discovering ip protocol version                      [ DONE ]

root@10.10.10.6's password: 

Setting up ssh keys                                  [ DONE ]

Preparing servers                                    [ DONE ]

Pre installing Puppet and discovering hosts' details [ DONE ]

Adding pre install manifest entries                  [ DONE ]

Setting up CACERT                                    [ DONE ]

Adding AMQP manifest entries                         [ DONE ]

Adding MariaDB manifest entries                      [ DONE ]

Adding Apache manifest entries                       [ DONE ]

Fixing Keystone LDAP config parameters to be undef if empty[ DONE ]

Adding Keystone manifest entries                     [ DONE ]

Adding Glance Keystone manifest entries              [ DONE ]

Adding Glance manifest entries                       [ DONE ]

Adding Nova API manifest entries                     [ DONE ]

Adding Nova Keystone manifest entries                [ DONE ]

Adding Nova Cert manifest entries                    [ DONE ]

Adding Nova Conductor manifest entries               [ DONE ]

Creating ssh keys for Nova migration                 [ DONE ]

Gathering ssh host keys for Nova migration           [ DONE ]

Adding Nova Compute manifest entries                 [ DONE ]

Adding Nova Scheduler manifest entries               [ DONE ]

Adding Nova VNC Proxy manifest entries               [ DONE ]

Adding OpenStack Network-related Nova manifest entries[ DONE ]

Adding Nova Common manifest entries                  [ DONE ]

Adding Neutron VPNaaS Agent manifest entries         [ DONE ]

Adding Neutron FWaaS Agent manifest entries          [ DONE ]

Adding Neutron LBaaS Agent manifest entries          [ DONE ]

Adding Neutron API manifest entries                  [ DONE ]

Adding Neutron Keystone manifest entries             [ DONE ]

Adding Neutron L3 manifest entries                   [ DONE ]

Adding Neutron L2 Agent manifest entries             [ DONE ]

Adding Neutron DHCP Agent manifest entries           [ DONE ]

Adding Neutron Metering Agent manifest entries       [ DONE ]

Adding Neutron Metadata Agent manifest entries       [ DONE ]

Adding Neutron SR-IOV Switch Agent manifest entries  [ DONE ]

Checking if NetworkManager is enabled and running    [ DONE ]

Adding OpenStack Client manifest entries             [ DONE ]

Adding Horizon manifest entries                      [ DONE ]

Adding Heat manifest entries                         [ DONE ]

Adding Provisioning manifest entries                 [ DONE ]

Adding Provisioning Glance manifest entries          [ DONE ]

Adding Provisioning Demo bridge manifest entries     [ DONE ]

Adding MongoDB manifest entries                      [ DONE ]

Adding Redis manifest entries                        [ DONE ]

Adding Ceilometer manifest entries                   [ DONE ]

Adding Ceilometer Keystone manifest entries          [ DONE ]

Copying Puppet modules and manifests                 [ DONE ]

Applying 10.10.10.8_prescript.pp

Applying 10.10.10.7_prescript.pp

Applying 10.10.10.6_prescript.pp

10.10.10.7_prescript.pp:                             [ DONE ]      

10.10.10.8_prescript.pp:                             [ DONE ]      

10.10.10.6_prescript.pp:                             [ DONE ]      

Applying 10.10.10.6_amqp.pp

Applying 10.10.10.6_mariadb.pp

10.10.10.6_amqp.pp:                                  [ DONE ]    

10.10.10.6_mariadb.pp:                               [ DONE ]    

Applying 10.10.10.6_apache.pp

10.10.10.6_apache.pp:                                [ DONE ]   

Applying 10.10.10.6_keystone.pp

Applying 10.10.10.6_glance.pp

10.10.10.6_keystone.pp:                              [ DONE ]     

10.10.10.6_glance.pp:                                [ DONE ]     

Applying 10.10.10.6_api_nova.pp

10.10.10.6_api_nova.pp:                              [ DONE ]     

Applying 10.10.10.6_nova.pp

Applying 10.10.10.8_nova.pp

Applying 10.10.10.7_neutron.pp

10.10.10.6_nova.pp:                                  [ DONE ]    

10.10.10.7_neutron.pp:                               [ DONE ]    

10.10.10.8_nova.pp:                                  [ DONE ]    

Applying 10.10.10.8_neutron.pp

Applying 10.10.10.6_neutron.pp

10.10.10.8_neutron.pp:                               [ DONE ]    

10.10.10.6_neutron.pp:                               [ DONE ]    

Applying 10.10.10.6_osclient.pp

Applying 10.10.10.6_horizon.pp

10.10.10.6_osclient.pp:                              [ DONE ]     

10.10.10.6_horizon.pp:                               [ DONE ]     

Applying 10.10.10.6_heat.pp

10.10.10.6_heat.pp:                                  [ DONE ] 

Applying 10.10.10.6_provision.pp

Applying 10.10.10.6_provision_glance

10.10.10.6_provision.pp:                             [ DONE ]          

10.10.10.6_provision_glance:                         [ DONE ]          

Applying 10.10.10.7_provision_bridge.pp

10.10.10.7_provision_bridge.pp:                      [ DONE ]             

Applying 10.10.10.6_mongodb.pp

Applying 10.10.10.6_redis.pp

10.10.10.6_mongodb.pp:                               [ DONE ]    

10.10.10.6_redis.pp:                                 [ DONE ]    

Applying 10.10.10.6_ceilometer.pp

10.10.10.6_ceilometer.pp:                            [ DONE ]       

Applying Puppet manifests                            [ DONE ]

Finalizing                                           [ DONE ]


 **** Installation completed successfully ******


Additional information:

 * Time synchronization installation was skipped. Please note that unsynchronized time on server instances might be problem for some OpenStack components.

 * File /root/keystonerc_admin has been created on OpenStack client host 10.10.10.6. To use the command line tools you need to source the file.

 * To access the OpenStack Dashboard browse to http://10.10.10.6/dashboard .

Please, find your login credentials stored in the keystonerc_admin in your home directory.

 * Because of the kernel update the host 10.10.10.8 requires reboot.

 * Because of the kernel update the host 10.10.10.7 requires reboot.

 * Because of the kernel update the host 10.10.10.6 requires reboot.

 * The installation log file is available at: /var/tmp/packstack/20161209-052810-KZtyhp/openstack-setup.log

 * The generated manifests are available at: /var/tmp/packstack/20161209-052810-KZtyhp/manifests



8. Network 노드 - Neutron 네트워크(OVS bridge) 인터페이스 설정 확인


#1 편에서 수동으로 설정해 주어야 했던 Network 노드의 외부 bridge 인터페이스(br-ex)와 enp0s9 의 브리지 포트 연결이, Mitaka v6에서는 자동으로 이루어짐을 볼 수 있다. 다음의 결과를 확인해 보자 


  • Network 노드 브리지 인터페이스 설정 확인

[root@openstack-net ~]# ifconfig

br-ex: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet 192.168.0.7  netmask 255.255.255.0  broadcast 192.168.0.255

        inet6 fe80::8464:32ff:fe27:304f  prefixlen 64  scopeid 0x20<link>

        ether 08:00:27:bd:a3:bd  txqueuelen 0  (Ethernet)

        RX packets 138  bytes 21542 (21.0 KiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 59  bytes 6843 (6.6 KiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


enp0s9: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet6 fe80::a00:27ff:febd:a3bd  prefixlen 64  scopeid 0x20<link>

        ether 08:00:27:bd:a3:bd  txqueuelen 1000  (Ethernet)

        RX packets 412  bytes 84303 (82.3 KiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 71  bytes 9555 (9.3 KiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


...



이제 ifcfg-br-ex, ifcfg-enp0s9 인터페이스도 확인해 보자

[root@openstack-net ~]# cat /etc/sysconfig/network-scripts/ifcfg-br-ex

DEVICE=br-ex

DEVICETYPE=ovs

TYPE=OVSBridge

BOOTPROTO=static

IPADDR=192.168.0.7

NETMASK=255.255.255.0

BROADCAST=192.168.0.255

GATEWAY=192.168.0.1

DEFROUTE=yes

ONBOOT=yes

NM_CONTROLLED=no

IPV4_FAILURE_FATAL=yes

IPV6INIT=no


[root@openstack-net ~]# cat /etc/sysconfig/network-scripts/ifcfg-enp0s9

DEVICE=enp0s9

DEVICETYPE=ovs

TYPE=OVSPort

OVS_BRIDGE=br-ex

ONBOOT=yes

NM_CONTROLLED=no

IPV6INIT=no



[root@openstack-net ~]# ovs-vsctl show

fd89eff3-c539-4250-8922-854f550124bc

    Bridge br-ex

        Port br-ex

            Interface br-ex

                type: internal

        Port "qg-05c23c0b-11"

            Interface "qg-05c23c0b-11"

                type: internal

        Port "enp0s9"

            Interface "enp0s9"

    Bridge br-int

        fail_mode: secure

        Port patch-tun

            Interface patch-tun

                type: patch

                options: {peer=patch-int}

        Port "qr-02fc6bc6-04"

            tag: 1

            Interface "qr-02fc6bc6-04"

                type: internal

        Port br-int

            Interface br-int

                type: internal

        Port "tap44e6cf82-31"

            tag: 1

            Interface "tap44e6cf82-31"

                type: internal

    Bridge br-tun

        fail_mode: secure

        Port "vxlan-0a0a0a08"

            Interface "vxlan-0a0a0a08"

                type: vxlan

                options: {df_default="true", in_key=flow, local_ip="10.10.10.7", out_key=flow, remote_ip="10.10.10.8"}

        Port patch-int

            Interface patch-int

                type: patch

                options: {peer=patch-tun}

        Port br-tun

            Interface br-tun

                type: internal

    ovs_version: "2.5.0"

Open vSwitch 의 브리지 인터페이스를 조회해 보면, br-ex 브리지에 br-ex 포트와 enp0s9 포트가 연결되어 있음을 볼 수 있다.



Controller 노드의 환경변수 설정 스크립트 복사


  • Network 노드, Compute 노드에서 CLI 명령 실행이 필요할 경우에 대비하여 스크립트 복사


Controller 노드의 keystonerc 관련 파일들을 다음과 같이 Network 노드, Compute 노드에 복사해 둔다


[root@openstack-ctrl ~]# scp keystonerc_* root@openstack-net:~

[root@openstack-ctrl ~]# scp keystonerc_* root@openstack-cmp1:~



9. Horizon 웹을 통해 Cloud 플랫폼 사용 준비 과정


  • Horizon 웹 UI 사용 준비 - 클라우드 네트워크 설정


시스템 구성도의 Admin VM에 해당하는 VM에서, firefox 웹브라우저를 통해 http://10.10.10.6으로 접속, admin 계정과 answer.txt 에서 지정한 암호를 통해 로그인


tenant 서비스를 위해서 demo 프로젝트를 사용한다고 가정했을 때,  해당 프로젝트에서 사용할 네트워크를 생성, 설정하는 과정을 다음과 같이 진행한다.


System->Routers 페인, 라우터 목록에서 router1 삭제 


System->Networks 페인, 사용하지 않는 네트워크(public_subnet) 을 삭제



networks 패널에서, '+Create Network' 을 선택하고 admin 프로젝트에 대해 public 네트워크를 새로이 생성(본 케이스에서는 vxlan 방식, shared - External network).



Subnet Detail 에서는, 네트워크 내부에 DHCP 서버(iptime 공유기)가 존재하므로, Enable DHCP 는 해제하고, 할당 가능한 IP 범위를 입력


admin 계정을 로그아웃하고, tenant 서비스를 설정/사용하기 위한 demo 계정으로 로그인


앞에서 admin 을 통해 생성, 설정한 네트워크를 사용하기 위해, 왼쪽 메뉴에서 Project->Network->Router 패널을 선택하고 '+Create Router' 을 클릭하여 demo 프로젝트에서 사용할 라우터를 생성. External Network 항목에 대해, 앞서 생성했던 public 네트워크를 선택





'+Add Interface' 클릭을 통해, 앞서 생성한 사설 네트워크와의 연결을 위한 인터페이스를 추가



좌측 메뉴의 Network 패널에서 Network Topology 를 선택하여 현재 구성된 네트워크의 구조를 확인한다. 여기까지 성공적으로 진행하였다면 클라우드 환경을 본격적으로 사용하기 위한 전체 설정 과정은 완료한 것이며, 다음의 필수 준비과정 몇 가지를 거치면 모든 준비가 끝나고 VM을 생성, 관리할 수 있는 요건을 갖추게 되었다.


11. VM 인스턴스 생성 관리를 위한 준비 & 실행 단계


  • security Group 생성 확인

 Compute->Access & Security->Security Group


시스템에 디폴트로 적용되는 Security Group 이 이미 생성되어 있다. 필요시 별도로 생성하여 사용하면 됨.


  • Key Pair 생성



Compute->Access & Security->Key Pairs->Create Key Pair, pem 파일 다운로드 및 보관



  • Floating IP 생성



Floating IP를 할당 필요한 갯수만큼 할당 받는다



  • VM Image 확인


Openstack 에 포함된 테스트용 초소형 OS image(12MB) 외에도, Ubuntu Cloud image 사이트나 CentOS cloud image 사이트 등에서 다운로드 받아 두고, Openstack image로 업로드(admin 계정으로)해서 사용할 수 있다


  • VM instance 생성








  • Ubuntu VM instance 생성하고 접속하기

별도의 전용 Security Group 을 생성해 둔다


Ubuntu VM을 만들기 위한 Flavor 템플릿을 생성해 둔다(admin 계정)








새로이 Ubuntu VM Instance 를 생성하고, 앞에서 할당해 두었던 Floating IP 들 중에서 하나를 선택하여 Associate 를 클릭하면 VM의 내부 사설 port(10.0.0.*) 과 Floating IP가 연결된다



Ubuntu VM 에 연결된 Floating IP 로 ping 테스트와 ssh 접속을 수행해 본다



12. LBaas(LB As A Service) 의 설정과 테스트


  • Haproxy Load Balancer 사용을 위한 준비

새로운 VM과 LB에 할당할 Floating IP 를 추가한다


LB pool 에 할당할 새로운 Ubuntu VM 을 생성해 둔다




LB pool 에 포함될 VM 들에 접속하여 nginx 패키지를 설치한다(외부 접속을 위해 /etc/resolv.conf 에 nameserver 를 추가하고, apt-get update 를 한 이후, apt-get install nginx 로 패키지 설치). 설치 이후 curl http://localhost 로 자체 접속 테스트.


각 VM의 웹서버에 80 port 로 접속이 가능하도록, 기존의 Security Group에 새로운 Rule 을 추가한다


VM들의 웹서버에 접속이 가능한지 웹브라우저를 통해 각각 접속해 본다



Network->Load Balancer, +Add Pool 클릭 및 저장 후,


Add VIP 선택


IP address에는 VM의 사설 IP 대역에 해당하는 IP를 입력. Connection Limit 는 -1 또는 제한되는 연결 갯수 입력 & 저장


Associate Floating IP 선택.


미리 준비한 Floating IP를 선택하고 Associate 클릭 & 저장


Members 탭 선택, +Add Member 클릭


Add Member 클릭. Pool 선택 & 대상 VM, port 번호 등 설정



웹브라우저에서 LB VIP로 접속하여 확인 완료



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들

Gluster 파일시스템의 Geo-replication 환경을 구축하고 테스트 하는 과정을 정리해 보자. 작업 환경을 단순하게, 과정을 직관적으로 표현하기 위해 추가 디스크 없는 VM 2개만으로 모든 내용을 소화해 보기로 한다. 이를 위해 Linux 의 Sparse 파일을 가상 디스크로 매핑하여, 마치 추가 디스크가 장착된, 또는 별도 파티션 된 물리 디스크 볼륨이 있는 것처럼 흉내 내는 방법을 같이 다루어 보고자 한다(블록디바이스를 통한 비슷한 테스트를 진행해야 할 때 유용한 방법으로 써먹을 수 있을 것이다).



CentOS 7.2에 Gluster Filesystem 설치


먼저 위의 그림과 같이 네트워크가 연결된 2개의 가상머신을 준비한다(CentOS 7.2 또는 RHEL 7.2는 이미 설치되었다고 가정하자). Redhat 이나 CentOS의 경우 대개 EPEL 을 통해 Gluster 를 설치하게 된다.


* 주의: 2개의 서버 노드를 peer 로 연결하여 Cluster 에 투입하는 것은 아니며, 서로 독립된 Gluster 세트가 원격지에 각각 존재하는 경우에 대한 내용을 다룬다. 여러 peer 를 통하여 Brick을 구성하고 데이터가 분산(distributed) 또는 중복(replica)되는 Volume 구성에 대해서는 여기서 논외로 하자.


* 2개의 서버 각각에 다음과 같이 gluster 를 설치한다.

# yum install -y wget

# wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm

# rpm -ivh epel-release-7-8.noarch.rpm

# yum install -y centos-release-gluster38.noarch

# yum install -y glusterfs-server

yum install -y glusterfs-geo-replication

# systemctl start glusterd

# systemctl enable glusterd


* RHEL 7 에서는 약간 과정이 다르다. 다음과 같이 해 보자.

# yum install -y wget

# wget http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-8.noarch.rpm

# rpm -ivh epel-release-7-8.noarch.rpm

# vi /etc/yum.repos.d/Gluster.repo

[gluster38]

name=Gluster 3.8

baseurl=http://mirror.centos.org/centos/7/storage/$basearch/gluster-3.8/

gpgcheck=0

enabled=1

# yum install -y glusterfs-server

# yum install -y glusterfs-geo-replication

# systemctl start glusterd

# systemctl enable glusterd


* 커맨드를 단순하게 하기 위해 /etc/hosts 파일에 다음의 2 라인을 추가

10.10.10.10 gnode1

10.10.10.11 gnode2


* 호스트 명을 각각 gnode1, gnode 로 변경하고 재접속

hostnamectl set-hostname gnode1



준비1: 데이터를 동기화할 가상 블록디바이스 장착

서두에서 말한 것처럼 Sparse 파일(1 GByte 짜리 ^^;;)을 만들어 가상디스크로 붙이고, 이를 데이터 동기화용 블록디바이스로 활용할 것이다. VM 2개에 각각 다음과 같이 작업한다.


* losetup -f 로 Sparse 이미지 파일을 LO 디바이스에 매핑하면, 자동으로 다음의 빈 LO 디바이스(아래의 경우, /dev/loop2)가 찾아져서 매핑됨

* 블록디바이스가 준비되었으므로 xfs 로 파일시스템을 포맷하고 마운트포인트를 /mnt/gnode_disk1 디렉토리로 하여 시스템에 마운트하면 준비 끝

[root@gnode1 ~]# mkdir gluster_disk

[root@gnode1 ~]# cd gluster_disk/

[root@gnode1 ~]# dd if=/dev/zero of=./disk1.img bs=1M count=1024

[root@gnode1 ~]# losetup -f /root/gluster_disk/disk1.img

[root@gnode1 ~]# losetup

NAME       SIZELIMIT OFFSET AUTOCLEAR RO BACK-FILE

...

/dev/loop2         0      0         0  0 /root/gluster_disk/disk1.img 

[root@gnode1 ~]# mkfs.xfs /dev/loop2

[root@gnode1 ~]# mkdir /mnt/gnode_disk1

[root@gnode1 ~]# mount /dev/loop2 /mnt/gnode_disk1/



준비2: Password-less 로그인 설정

Geo-replication 이 가능하게 하기 위해서는, 2개의 서버에서 각각 상대방 서버로 패스워드 없이 root 계정으로 접속할 수 있도록 ssh 키를 교환해 두어야 한다. 다음과 같이 진행하자.


* Password-less ssh 접속을 위한 키 교환. 상대방 서버에 ssh 접속하고 암호를 입력해 두면 다음에는 암호 없이 로그인 가능

[root@gnode1 ~]# ssh-keygen 

[root@gnode1 ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.11

[root@gnode1 ~]# ssh gnode2



Gluster Volume 생성 및 기동

각각의 Gluster 서버에서 다음과 같이 Volume 을 생성하고 기동한다. 


* 주의: 마운트포인트 아래에 서브디렉토리를 생성하여 볼륨(여기서는 distvol)에 할당해야 함

[root@gnode1 ~]# mkdir -p /mnt/gnode_disk1/brick

[root@gnode1 ~]# gluster volume create distvol gnode1:/mnt/gnode_disk1/brick

[root@gnode1 ~]# gluster volume start distvol

[root@gnode1 ~]# gluster volume info

Volume Name: distvol

Type: Distribute

Volume ID: 9754b79a-d0e7-4823-9e85-38340d99e732

Status: Started

Snapshot Count: 0

Number of Bricks: 1

Transport-type: tcp

Bricks:

Brick1: gnode1:/mnt/gnode_disk1/brick

Options Reconfigured:

nfs.disable: on

performance.readdir-ahead: on

transport.address-family: inet


[root@gnode2 ~]# mkdir -p /mnt/gnode_disk1/brick

[root@gnode2 ~]# gluster volume create distvol gnode2:/mnt/gnode_disk1/brick

[root@gnode2 ~]# gluster volume start distvol

[root@gnode2 ~]# gluster volume info

Volume Name: distvol

Type: Distribute

Volume ID: e12db607-2e19-4a45-bc3b-eb6e922f59e5

Status: Started

Snapshot Count: 0

Number of Bricks: 1

Transport-type: tcp

Bricks:

Brick1: gnode2:/mnt/gnode_disk1/brick

Options Reconfigured:

nfs.disable: on

performance.readdir-ahead: on

transport.address-family: inet



Geo-replication 설정 및 테스트

* Geo-replication 설정은 Master(여기서는 gnode1) 에서만 수행

[root@gnode1 ~]# gluster system:: execute gsec_create

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol create push-pem

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol start

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol status

MASTER NODE    MASTER VOL    MASTER BRICK              SLAVE USER    SLAVE              SLAVE NODE    STATUS    CRAWL STATUS       LAST_SYNCED                  

-----------------------------------------------------------------------------------------------------------------------------------------------------

gnode1         distvol       /mnt/gnode_disk1/brick    root          gnode2::distvol    gnode2        Active    Changelog Crawl    2016-10-10 01:10:04

[root@gnode1 ~]# gluster volume info distvol

Volume Name: distvol

Type: Distribute

Volume ID: 9754b79a-d0e7-4823-9e85-38340d99e732

Status: Started

Snapshot Count: 0

Number of Bricks: 1

Transport-type: tcp

Bricks:

Brick1: gnode1:/mnt/gnode_disk1/brick

Options Reconfigured:

changelog.changelog: on

geo-replication.ignore-pid-check: on

geo-replication.indexing: on

nfs.disable: on

performance.readdir-ahead: on

transport.address-family: inet



동기화 테스트

동기화 테스트를 수행하기 위해 별도의 Gluster Client 를 사용해야 하지만, Gluster 서버 자신을 클라이언트로 사용해도 된다. 다시 말해 Gluster 서버에서 자신의 Volume 을 리모트 마운트하는 것이다. 다음과 같이 진행해 보자.


* gnode1: Gluster Fuse Client 로 distvol 볼륨을 로컬의 /mnt/gnode1_distvol/ 디렉토리로 마운트하고 해당 디렉토리로 이동

[root@gnode1 ~]# mkdir /mnt/gnode1_distvol

[root@gnode1 ~]# mount.glusterfs gnode1:/distvol /mnt/gnode1_distvol/

[root@gnode1 ~]# cd /mnt/gnode1_distvol/


* gnode2: Gluster Fuse Client 로 distvol 볼륨을 로컬의 /mnt/gnode2_distvol/ 디렉토리로 마운트하고 해당 디렉토리로 이동

[root@gnode2 ~]# mkdir /mnt/gnode2_distvol

[root@gnode2 ~]# mount.glusterfs gnode1:/distvol /mnt/gnode2_distvol/

[root@gnode2 ~]# cd /mnt/gnode2_distvol/


* gnode1: 파일 생성, 수정 및 삭제

[root@gnode1 ~]# echo abcdef----xyzxyz----xxxx > test.txt

[root@gnode1 ~]# cp test.txt s

[root@gnode1 ~]# cat test.txt >> s

[root@gnode1 ~]# cp s x

[root@gnode1 ~]# cat s >> x

[root@gnode1 ~]# vi x

[root@gnode1 ~]# rm tttt

...

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol status

MASTER NODE    MASTER VOL    MASTER BRICK              SLAVE USER    SLAVE              SLAVE NODE    STATUS    CRAWL STATUS       LAST_SYNCED                  

-----------------------------------------------------------------------------------------------------------------------------------------------------

gnode1         distvol       /mnt/gnode_disk1/brick    root          gnode2::distvol    gnode2        Active    Changelog Crawl    2016-10-10 01:15:22


* gnode2 디렉토리 내의 파일 변화 관찰, 확인

[root@gnode2 gnode2_distvol]# ls

s  test.txt  x

[root@gnode2 gnode2_distvol]# cat test.txt

abcdef----xyzxyz----xxxx

[root@gnode2 gnode2_distvol]#


몇 가지 팁들 ... port 변경, 작은 파일에 유리한 option 등

Gluster 의 geo-replication은 내부적으로 ssh를 통한 rsync 방식으로 동작한다. 일반적으로 ssh는 TCP 22 포트를 사용하는데, 임의의 포트번호로 설정하려면 다음과 같이 진행하면 된다(사실, "How to replicate to slave via non-standard ssh port..." 에 대한 답이다).


우선 양쪽 Gluster 서버에서 ssh port 를 변경한다


# vi /etc/ssh/sshd_config

...

Port 22222

...


# systemctl restart sshd

# netstat -anp | grep 22222

tcp   0   0   0.0.0.0:22222    0.0.0.0:*    LISTEN    8425/sshd

...


만약 netstat 결과에 해당 포트번호로 바인딩되어 있지 않고 ssh 접속이 안된다면 SELINUX가 ssh의 기본 Port인 22번으로 바인딩하게 고정해 둔 것일 게다. 그러면 다음과 같은 semanage 명령을 수행하고 조금 시간이 지난 뒤에 다시 접속해 본다.


# semanage port -a -t ssh_port_t -p tcp 22222


Linux 서버에서 파이어월(firewalld)이 사용되고 있다면 다음과 같이 해당 포트를 허용해 주는 것도 잊지 않도록 하자.


# firewall-cmd --permanent --zone=public --add-port=22222/tcp

# firewall-cmd --reload


성공적으로 ssh 포트 번호를 변경하였다면, 이번에는 Gluster 에서 새로운 포트로 Geo-replication 이 되도록 설정하면 된다.


[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol stop

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol delete

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol create ssh-port 22222 push-pem

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol config ssh-port 22222

[root@gnode1 ~]# gluster volume geo-replication distvol gnode2::distvol start



Rsync는 Block 방식의 전송이기 때문에 파일의 크기가 큰 경우에 좀 더 유리하다. 반대로 말하면 크기가 작은(대략 수 KB~수백KB 이내) 파일의 경우 전송 지연이 발생할 수 있게 되는데, 이 때에는 다음과 같은 옵션을 적용하여 테스트해 보기를 권한다. Geo-replication 이 작동되는 도중에도 실행해 볼 수 있다.


[root@gnode1 ~] gluster volume geo-replication distvol gnode2::distvol config use-tarssh true


참고로, 간단히 테스트 해 보니 50KB 크기의 몇 백 개 파일 전송에 1분 26초 정도 걸리던 것이, 옵션 변경 후에 약 10초 가량 전송 시간이 줄어든 것을 볼 수 있었다.


원래의 기본 전송방식인 Rsync 방식으로 돌아가려면 다음과 같이 하면 된다.

[root@gnode1 ~] gluster volume geo-replication distvol gnode2::distvol config \!use-tarssh



Good Luck !


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


9월 6일자 티스토리 공지사항에 보면 아래의 내용이 올라와 있다. 밥벌이에 바쁘다 보니 뒤늦게 읽게 됐는데, 보다 보니 좀 심각한 내용이다. 티스토리의 서버가 이전함에 따라 IP가 바뀌게 되어 2차 도메인 사용자를 위한 배려(?)로 CNAME으로 host.tistory.io 만을 사용하고 도메인에 대한 A 레코드(티스토리의 IP주소로 등록한 부분)을 삭제 해 달라는 내용이다(10월6일 이후에 좀 더 상세하게 바뀐 내용으로 업데이트된 공지 참고).


그런데, DNS 표준 권고사항에서는 루트도메인(Root Domain=Naked Domain=Bare Domain=Zone Apex ... 다 같은 말이다. 즉 필자의 경우를 예로 들면, 보유 중인 2차 개인 도메인인 'bryan.wiki'가 바로 루트도메인)에 대한 CNAME 은 허용하지 않게 되어 있다(참고: RFC1034, Serverfault 포스팅).




다시 풀어서 예를 들어 보면, www.bryan.wiki 에 대해서는 CNAME 으로 host.tistory.io 를 등록할 수 있지만, bryan.wiki 에 대한 CNAME 으로 host.tistory.io 를 등록하지 못하게 되어 있다. 표준을 준수하는 메이저 DNS 제공 업체들(Ghandi, GoDaddy 등)은 이러한 등록을 시도하지 못하도록 오류를 뱉어내게 되어 있다. Amazon의 Route53(Ghandi 의 DNS 서비스사용) 의 관리 화면에서 @.bryan.wiki(또는 그냥 bryan.wiki) 에 대해 CNAME을 등록할 경우에 붉은 글씨의 오류 메시지를 볼 수 있다.


티스토리가 CNAME 만의 사용을 계속 강요한다면...?


만약 티스토리가 A 레코드를 삭제하고 bryan.wiki 에 대한 주소 등록을 CNAME 만으로 등록하도록 입장을 고수한다면, 다음의 우회적인 방법으로 구현은 가능하다. 필자가 사용중인 Amazon의 Route53과 S3 Redirection에 의한 방법을 예로 들어 보겠다.


우선 AWS 웹사이트에서 Route53 의 도메인 관리화면으로 들어간다. 일단, Create Record Set에서 다음과 같이 www.bryan.wiki 에 대한 CNAME은 host.tistory.io. 으로 등록은 가능하다. 이 부분은 표준에 어긋나지 않으므로...




다음으로, 티스토리가 요구하는 대로 현재 등록된 A 레코드를 삭제해 보자.


이제 "http://www.bryan.wiki 로는 블로그 접속이 되지만, http://bryan.wiki 로는 접속이 되지 않는다. 티스토리에서 원하는 대로 bryan.wiki 에 대한 CNAME 등록을 시도해 본다.


Create 를 눌러 보지만 등록 불가라고 붉은 글씨의 오류가 나며, 다음 단계로 진행되지 않는다. 빈칸 자리에 @ 를 넣으면 등록은 가능하지만 여전히 http://bryan.wiki 로 블로그 접속은 불가한 상태!



S3 의 Redirect request 를 써 보자


S3 는 Amazon의 스토리지 공간(Bucket)에 파일을 올려 두고 인터넷을 통해 다운로드 등의 서비스가 가능한 서비스로 알려져 있지만, 버킷 접속을 특정 주소로 전환해 주는 기능도 가지고 있다. 이 기능을 사용하면 되지만, 사용량에 따른 비용을 내야 하는 유료 서비스이다. 접속 형태와 지역에 따라 구분이 되기는 하지만 서울 Region 기준으로 1만 건당 0.37 센트(무려!!!)를 지불해야 한다.


AWS 메인 화면에서 S3를 클릭하여 해당 화면에 진입한다. Create Bucket을 선택해서 Bucket Name에 2차도메인을 입력하고 Create 클릭.

  

Bucket 접속에 대한 Redirection End Point를 설정해 보자. Bucket 왼쪽의 돋보기 아이콘이나 오른쪽 위의 Properties를 선택하고 Redirect all requests to .... 를 선택하고 Save.




이제 http://bryan.wiki 에 접속했을 때 리디렉션할 주소가 생성되었으니, Route53 에서 Create Record Set으로 루드도메인에 대한 A 레코드를 선택하고 Alias를 선택하면, 아래에 S3 endpoint가 목록에 나타난다. 이를 선택하고 Create!


여기까지 성공하였다면 다음 그림과 같은 등록 상태를 확인할 수 있을 것이다. SOA, NS, CNAME, A 타입의 레코드가 각각 하나씩이다. 이제 http://bryan.wiki 와 같은 2차 도메인으로 접속하면, CNAME으로 등록한 www 서브도메인(http://www.bryan.wiki)으로 리디렉션되고 티스토리 블로그로 접속할 수 있게 된다.



10월 6일 전후로 티스토리에서 해당 공지를 변경하여 발표했다. A 레코드에 의한 DNS 등록을 병행하기로 했다는 내용이다. 처음 발표된 9월 6일 이후로 수많은 2차도메인 사용자들에게 혼란을 초래해 왔고, 또 많은 이들의 지적과 불만에 찬 댓글에 대한 리액션인 듯 하다.


티스토리가 서버를 이전하는 일에는 인터넷 서비스회사의 특성상 그럴 수도 있다고 하지만 앞으로 IP 주소가 얼마나 자주 바뀔 예정이기에 표준 준수를 어겨가면서까지 A 레코드 방식을 버리라고 무리수를 두었는지는 도대체 지금도 알 길이 없다. 그냥 단순한 사용자 편의만을 위한 배려뿐이었을까?


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


1. 오픈스택이란?

Openstack은 소규모부터 중대규모를 아우르는 IAAS(Infra As A Service)를 가능하게 하는 오픈소스 클라우드 플랫폼 솔루션입니다. 2010년, Rackspace사의 클라우드 파일 플랫폼과 NASA의 Nebula를 기반으로 통합, 협력하는 방식으로 진행되기 시작한 이 프로젝트의 첫 코드명은 Austin(2010). 이후 Diablo(2011), Essex(2012), Havana(2013), Icehouse, Juno(2014), Kilo(2015), Liverty, Mitaka(2016) 를 거져 2016년 10월에 Newton 버전이 출시될 예정으로 개발이 진행되고 있지요.


Python 언어를 기반으로 만들어진 이 클라우드 플랫폼은 Apache License 2.0 이 적용되어 있기 때문에 상용/비상용에 관계 없이 누구나 자유로이 채택하여 변경, 사용할 수 있을 뿐 아니라, 완성도 면에서 충실하고 잘 관리된 프로젝트의 퀄리티로 인해 수많은 전문가와 기업들이 관심을 가지고 개발에 참여하거나 자사의 제품에서 오픈스택을 지원하기 위해 연구개발을 진행하고 있는 Hot 한 기술 아이템이자 클라우드 컴퓨팅 플랫폼과 네트워킹 분야의 패러다임을 이끌고 있는 녀셕이기도 합니다.


원래는 Spec이 어느 정도 되는 서버 장비가 최소 2대 이상 있어야 설치, 운영을 해 볼 수 있지만, 가정 또는 소규모 연구실에서도 자체 Lab Test 정도는 해 볼 수 있도록 Triple-O(Openstack On Openstack) 구성에 대한 문서를 오픈스택 진영에서 직접 소개하고 있읍니다.

참고: ☞ https://wiki.openstack.org/wiki/TripleO).


2. 들어가며

본 포스팅에서는 필자의 맥북(MacBook Pro Sierra, Quadcore 16GB, SSD 256GB) 상에서 (1) VirtualBox 로 가상머신(CentOS 7.2)을 만들고, (2) Packstack을 이용해서 각각의 가상머신을 Openstack용 controller, Network, Compute 노드로 설치, 설정하여, 만들어진 오픈스택 클라우드 환경에서 (3) 실제로 VM을 생성하고 네트워크로 접속해보고, (4) LBaaS 서비스를 설치한 뒤 LB와 2개의 VM을 이용한 로드밸런싱 환경을 간단히 테스트해 보는 과정 전체를 기술해 둡니다.


중요한 점은, 본인이 포스팅하는 대다수의 블로깅에서 일관되게 지키려고 하는 원칙 중의 하나이긴 하지만, 과정 하나 하나를 직접 내 손으로 수행해 본 결과를 그대로 표현하고 기술함으로써 설치나 설정 과정에서 필요한 원리와 방식에 대해 더 구체적으로 들여다볼 수 있는 기회를 보는 이에게 제공해 주고자 한다는 점입니다. 내가 알지 못하고 무언가를 표현하는 것은, 보는 이에게 도움도 되지 않을 뿐 아니라 시간과 지면 낭비라고 생각하며, 플라스크를 만든 아르민 로나허가 말했듯이, "추상적인 내용보다 구체적인 경험을 나누는 것" 이 더 효과적인 소통과 전달의 수단이 될 수 있기 때문이지요.




본 포스팅의 작업과정에 가장 핵심적인 내용들이 위의 그림에 요약되어 표현되어 있으니, 잘 이해되도록 그림을 자세히 들여다 보기를 권하며, 아래 내용부터는 간략히, 다소 간결한 표현으로 써내려 갑니다.



3. 버추얼박스 가상머신 준비



우선 가상머신에 설치할 OS인 CentOS 7.2 를 다운로드(일반적으로 Ubuntu일 경우 Devstack 을 통해서, CentOS 나 Redhat일 경우 Packstack 을 통해서 Openstack 을 설치하게 됨).


  • VirtualBox 에서 VM 생성


위 그림에 해당하는 4개의 VM을 생성한다. Control 노드는 메모리를 4GB~8GB 정도 주는 것이 좋으며, Admin은 버추얼박스에서 GUI(웹브라우저 사용)이 가능하도록 데스크톱 형태로 설치해야 한다.


  • VM 네트워크 카드(NIC) 설정

그림을 잘 들여다 보면 기본적인 설정사항이 상세히 표시되어 있는데, 본 포스팅에서는 VirtualBox 에서 3가지의 네트워크(각각 외부, 데이터, 관리용)를 준비해야 한다. 이 중에서 외부(External) 네트워크는 인터넷 접속용, 데이터는 VM에 할당되는 서비스 네트워크, 관리용은 Openstack이 VM을 관리하기 위한 용도.

데이터 및 관리용 네트워크는 VirtualBox 의 호스트전용 어댑터 방식(NAT방식으로 해도 작동은 하지만, Virtualbox 내부에서만 동작하는 네트워크이므로 이 정도로 충분함)으로 생성하고, 외부용 네트워크는 맥북 호스트에서 VM에 접속도 가능하고, VM에서 외부 인터넷으로도 접속이 가능하도록 브리지모드로 생성.

호스트전용 어댑더 방식을 위해 아래 그림의 과정과 같이 vboxnet0 네트워크를 우선 생성한다(VirtualBox 환경설정 > 툴바의 네트워크 선택 > 호스트전용 네트워크 선택 > 우측 + 아이콘 클릭).


IPv4 주소는 변경할 필요 없이 그대로 둔다.


DHCP 서버를 눌러서 내용 확인 후 변경 없이 그대로 확인


마우스 커서를 들이대면 설정된 네트워크 정보가 보인다. 그냥 확인.


VM에서 사용할 호스트전용 네트워크가 준비되었으므로 다음 섹션의 VM별 네트워크 구성표에 따라 각각 어댑터를 할당하고 맨 위의 구성도를 보면서 스텍을 적당히 조절하며 4개의 VM을 생성한다. 앞의 3개의 노드들은 가급적 GUI가 없는 서버모드로 OS를 설치해야 하며, 마지막의 Admin VM은 웹브라우저를 통해 Controller VM의 Horizon 웹포탈에 접속해야 하므로 GUI 가 가능하도록 생성하되, 네트워크 정보는 Controller 와 같은 구성으로 하면 운영, 관리에 무리가 없을 것이다.


  • 각 VM 네트워크 카드(NIC) 구성

VM구분

(노드)

 NIC1

 NIC2

 NIC3

방식

(vbox)

장치명

용도

방식

(vbox)

장치명

용도

방식

(vbox)

장치명

용도

 Controller

bridge

enp0s9

API

(외부)

vboxnet0

eth0

control

(관리용)

 

 

 

 Network

bridge

enps09

API

(외부)

vboxnet0

eth0

control

(관리용)

vboxnet0

eth1

tenant

(데이터)

 Compute

 

 

 

vboxnet0

eth0

control

(관리용)

vboxnet0

eth1

tenant

(데이터)


   * 용도별 network segment(IP 대역)

     - API(외부용): 192.168.0.6~ ... Vbox VM외부, 즉 Virtualbox(즉 호스트의 하이퍼바이저 레벨) 에서는 bridge 장치로 인식되며, VM 내부에서는 ifcfg-enp0s9 장치로 관리됨(물리장치명 enp0s9 는 맥북의 경우이며, 일반 조립PC의 RTL 계열 등의 네트워크 카드는 단순히 eth0 와 같은 명칭이 될 수도 있다)

     - Control(관리용): 10.10.10.6~ ... Vbox VM 내부에서는 ifcfg-eth0 로 관리됨

     - Tenant(데이터): 10.10.20.6~ ... Vbox VM 내부에서는 ifcfg-eth1 으로 관리됨

   * 모든 Adapter 는 Virtualbox 에서 생성시 '무작위모드 허용' 으로 설정

   * vboxnet0 방식의 NIC은 모두 '반가상네트워크'로 설정


글 처음에 보이는 시스템 구성도와 아래의 네트워크 구성도는 Openstack 설치, 설정과정의 핵심 구조와 원리를 담은 설계도에 해당하므로 충분히 들여다 보고 잘 이해해 두도록 한다 그래야만 차후에 본인 스스로 실무적 요구사항을 담은 구성도를 직접 그려낼 수 있게 될 것이다.




4. 오픈스택 노드 들의 네트워크 개별 설정 - (1)


이제 위의 구성도와 표에 맞게 각 노드의 네트워크를 세부적으로 설정해 보자


  • 인터넷에서 패키지를 다운로드할 수 있도록 DNS 접속 주소를 설정

# vi /etc/resolv.conf # --> 자신의 인터넷 환경에 맞게 DNS 등록. 잘 모르면 8.8.8.8 하나만.

nameserver 168.126.63.1

nameserver 168.126.63.2


  • Controller 노드

  호스트명을 다음과 같이 변경하고 로그아웃 후, 다시 로그인


# hostnamectl set-hostname openstack-ctrl


  API용 외부 네트워크(enp0s9)

[root@openstack-ctrl ~]# vi /etc/sysconfig/network-scripts/ifcfg-enp0s9

DEVICE=enp0s9

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=192.168.0.6

NETMASK=255.255.255.0

[root@openstack-ctrl ~]# vi /etc/sysconfig/network-scripts/route-enp0s9

default via 192.168.0.1 dev enp0s9

 

  관리용 내부 네트워크(eth0)

[root@openstack-ctrl ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE=eth0

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=10.10.10.6

NETMASK=255.255.255.0


[root@openstack-ctrl ~]# systemctl restart network

[root@openstack-ctrl ~]# yum install -y net-tools


  Controller 노드는 Openstack 인프라 내에서 시간동기화(ntpd) 서버 기능을 해야 하므로 다음과 같이 ntpd 서버로 설정한다


[root@openstack-ctrl ~]# yum clean all; yum install -y ntp

[root@openstack-ctrl ~]# vi /etc/ntp.conf

restrict 10.10.10.0 mask 255.255.255.0 nomodify notrap

server kr.pool.ntp.org

server time.bora.net

server time.kornet.net

[root@openstack-ctrl ~]# systemctl enable ntpd

[root@openstack-ctrl ~]# systemctl start ntpd

[root@openstack-ctrl ~]# ntpq -pn


  • Network 노드

  호스트명을 다음과 같이 변경하고 로그아웃 후, 다시 로그인


# hostnamectl set-hostname openstack-net


  API용 외부 네트워크(enp0s9)

[root@openstack-net ~]# vi /etc/sysconfig/network-scripts/ifcfg-enp0s9

DEVICE=enp0s9

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=192.168.0.7

NETMASK=255.255.255.0

[root@openstack-net ~]# vi /etc/sysconfig/network-scripts/route-enp0s9

default via 192.168.0.1 dev enp0s9

 

  관리용 내부 네트워크(eth0)

[root@openstack-net ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0

DEVICE=eth0

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=10.10.10.7

NETMASK=255.255.255.0


  데이터용 내부 네트워크(eth1) 

[root@openstack-net ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth1

DEVICE=eth1

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=10.10.20.7

NETMASK=255.255.255.0


[root@openstack-net ~]# systemctl restart network

[root@openstack-net ~]# yum install -y net-tools


  Controller 노드에 맞추어 시간동기화(ntp) 기능을 설정


[root@openstack-net ~]# yum clean all; yum install -y ntp

[root@openstack-net ~]# vi /etc/ntp.conf

server  10.10.10.6

[root@openstack-net ~]# systemctl enable ntpd

[root@openstack-net ~]# systemctl start ntpd

[root@openstack-net ~]# ntpq -pn

     remote           refid      st t when poll reach   delay   offset  jitter

==============================================================================

 10.10.10.6      LOCAL(0)         6 u  92m   64    0   60.402   37.293   0.000


5. 오픈스택 설치를 위한 각 노드 계정 및 네트워크 설정 - (2)


  • Controller, Network, Compute 노드에서 각각 오픈스택 설치/운영을 위한 stack 이라는 계정을 생성하고 권한을 부여
# useradd stack
# passwd stack  # --> specify a password
# echo "stack ALL=(root) NOPASSWD:ALL" | sudo tee -a /etc/sudoers.d/stack
# sudo chmod 0440 /etc/sudoers.d/stack

  • Compute 노드는 외부 네트워크에 직접 연결할 수 있는 NIC이 없음(위의 표: VM NIC 구성표 참조)


시스템 구성도와는 다르게 Compute 노드에 Network 노드처럼 NIC을 하나 더 추가해 주면 되지만, 여기서는 원칙대로 진행한다. 하지만 이렇게 되면 Compute 노드에서 새로운 패키지를 설치할 수 없게 되므로, Network 노드(를 게이트웨이로 해서)를 통해 외부 접속이 가능하도록 해 보자. 우선, Network 노드에서 다음을 수행한다.


# yum install -y iptables-services

# systemctl start iptables

# systemctl enable iptables

# iptables -F

# iptables -t nat -A POSTROUTING -o enp0s9 -j MASQUERADE

# iptables -A FORWARD -i eth0 -j ACCEPT

# service iptables save

# vi /etc/sysctl.conf

net.ipv4.ip_forward=1

# sysctl -p

   ※ [주의1] 붉은 글씨로 표기된 enp0s9 장치명은 Openstack 설치가 완료된 이후에는 br-ex 로 바꿔서 사용되어야 한다. 아래에서 다시 설명함

  • Compute 노드(openstack-cmp1, openstack-cmp2, ...)

  호스트명을 다음과 같이 변경하고 로그아웃 후, 다시 로그인


# hostnamectl set-hostname openstack-cmp1



  데이터용 내부 네트워크(eth1) 

[root@openstack-cmp1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth1

DEVICE=eth1

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=10.10.20.8

NETMASK=255.255.255.0


     다음으로 Compute 노드에서 다음과 같이 네트워크와 게이트웨이를 설정한다.

# vi /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=10.10.10.8

NETMASK=255.255.255.0

# vi /etc/sysconfig/network-scripts/route-eth0

default via 10.10.10.7 dev eth0

# systemctl restart network
# yum install -y net-tools

# ping 8.8.8.8

PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.

64 bytes from 8.8.8.8: icmp_seq=1 ttl=53 time=45.7 ms

64 bytes from 8.8.8.8: icmp_seq=2 ttl=53 time=39.8 ms ...


  Controller 노드에 맞추어 시간동기화(ntp) 기능을 설정한다


[root@openstack-cmp1 ~]# yum clean all; yum install -y ntp

[root@openstack-cmp1 ~]# vi /etc/ntp.conf

server  10.10.10.6

[root@openstack-cmp1 ~]# systemctl enable ntpd

[root@openstack-cmp1 ~]# systemctl start ntpd

[root@openstack-cmp1 ~]# ntpq -pn

     remote           refid      st t when poll reach   delay   offset  jitter

==============================================================================

 10.10.10.6      LOCAL(0)         6 u  92m   64    0   60.402   37.293   0.000


6. 오픈스택 설치를 위한 각 노드들의 패키지 설정


  • 각 노드의 리포지토리를 변경하고 필수 패키지를 설치


이제 모든 3개 노드(Controller, Network, Compute)에서 BaseRepo 를 다음과 같이 변경하고, 필수 유틸리티와 종속성 패키지를 순서대로 빠짐 없이 설치한다(Openstack 버전에 따라 다소 차이가 있을 수 있으며, 아래는 2016.9월 시점 Mitaka v5 기준). 참고로 활발히 개발이 진행되고 있는 Openstack 의 버전에 따라, 본 포스팅의 경우처럼 다수의 의존성 패키지를 따로 설치하지 않고 yum update 를 통해 기본적인 패키지 업데이트를 진행한 다음, 최소한의 의존성 패키지를 따로 설치하는 경우도 생각해 볼 수 있다.


# vi /etc/yum.repos.d/CentOS-Base.repo

#mirrorlist=...모든 섹션에서 mirrorlist 줄을 comment

#baseurl=...모든 섹션에서 baseurl 줄을 comment, 그 줄 아래에 다음 줄을 각각 추가

baseurl=http://ftp.daumkakao.com/centos/$releasever/os/$basearch/

# yum install -y net-tools

# yum install -y wget


# --> 종속성 패키지 설치

# wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/python-markdown-2.4.1-1.el7.centos.noarch.rpm

# wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/python-cheetah-2.4.4-5.el7.centos.x86_64.rpm

# wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/python-werkzeug-0.9.1-2.el7.noarch.rpm

# wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/python-itsdangerous-0.23-2.el7.noarch.rpm

# wget http://mirror.centos.org/centos/7/extras/x86_64/Packages/python-flask-0.10.1-4.el7.noarch.rpm

# wget wget ftp://mirror.switch.ch/pool/4/mirror/centos/7.2.1511/os/x86_64/Packages/python-jinja2-2.7.2-2.el7.noarch.rpm

# wget ftp://mirror.switch.ch/pool/4/mirror/centos/7.2.1511/cloud/x86_64/openstack-liberty/common/python-babel-1.3-6.el7.noarch.rpm

# wget ftp://mirror.switch.ch/pool/4/mirror/centos/7.2.1511/os/x86_64/Packages/python-markupsafe-0.11-10.el7.x86_64.rpm

# sudo yum install -y python-pygments

# sudo rpm -ivh python-markdown-2.4.1-1.el7.centos.noarch.rpm

# sudo rpm -ivh python-cheetah-2.4.4-5.el7.centos.x86_64.rpm

# sudo rpm -ivh python-werkzeug-0.9.1-2.el7.noarch.rpm

# sudo rpm -ivh python-itsdangerous-0.23-2.el7.noarch.rpm

# sudo yum install -y  pytz

# sudo rpm -ivh python-babel-1.3-6.el7.noarch.rpm

# sudo rpm -ivh python-markupsafe-0.11-10.el7.x86_64.rpm

# sudo rpm -ivh python-jinja2-2.7.2-2.el7.noarch.rpm

# sudo rpm -ivh python-flask-0.10.1-4.el7.noarch.rpm


  • 각 노드의 /etc/hosts 파일에 다음의 3개 라인을 추가

10.10.10.6 openstack-ctrl

10.10.10.7 openstack-net

10.10.10.8 openstack-cmp1


  • 마찬가지로 각 3개 노드에서 보안 등 각종 관리패키지를 오픈스택 설치가 가능해 지도록 구성

# systemctl disable firewalld

# systemctl stop firewalld

# systemctl disable NetworkManager

# systemctl stop NetworkManager

# yum remove -y NetworkManager

# systemctl enable network

# systemctl start network


# vi /etc/selinux/config #--> liberty 부터는 불필요 해졌으므로 생략

...

SELINUX=permissive

...



7. Packstack 으로 Openstack 자동 설치하기

Packstack은 Controller 노드에서 answer 파일을 읽어 들여서 자신을 포함하여 하위 노드(Network 노드, Compute 노드들)에서 각종 소프트웨어들을 설치/설정하는 스크립트를 단계적으로 실행해 주는 역할을 한다(Redhat 제공 RDO 프로젝트 참고: www.rdoproject.org). 이를 위해 각 하위 노드에 암호 없이 접속이 가능해야 한다. 


  • Controller 노드에서 각 노드로 Passwordless ssh 설정

[root@openstack-ctrl ~]# ssh-keygen 

[root@openstack-ctrl ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.7

[root@openstack-ctrl ~]# ssh-copy-id -i /root/.ssh/id_rsa.pub root@10.10.10.8


#--> 암호 없이 정상적으로 접속되는지 확인

[root@openstack-ctrl ~]# ssh openstack-net

[root@openstack-ctrl ~]# ssh openstack-cmp1


  • Packstack repo 설정 및 설치 시작

[root@openstack-ctrl ~]# yum install -y https://www.rdoproject.org/repos/rdo-release.rpm

[root@openstack-ctrl ~]# yum install -y openstack-packstack

[root@openstack-ctrl ~]# su - stack

[root@openstack-ctrl ~]# sudo packstack --gen-answer-file=answer.txt

[root@openstack-ctrl ~]# sudo vi answer.txt

=== OVS(Openvswitch) 기반 샘플 answer 파일 다운로드 ===

=== answer.txt 로 복사후 편집, 사용 ===


[root@openstack-ctrl ~]# sudo packstack --answer-file=answer.txt

Welcome to the Packstack setup utility



The installation log file is available at: /var/tmp/packstack/20160828-041734-_nTMcZ/openstack-setup.log



Installing:

Clean Up                                             [ DONE ]

Discovering ip protocol version                      [ DONE ]

Setting up ssh keys                                  [ DONE ]

Preparing servers                                    [ DONE ]

Pre installing Puppet and discovering hosts' details [ DONE ]

Adding pre install manifest entries                  [ DONE ]

Installing time synchronization via NTP              [ DONE ]

Setting up CACERT                                    [ DONE ]

Adding AMQP manifest entries                         [ DONE ]

Adding MariaDB manifest entries                      [ DONE ]

Adding Apache manifest entries                       [ DONE ]

Fixing Keystone LDAP config parameters to be undef if empty[ DONE ]

Adding Keystone manifest entries                     [ DONE ]

...

Adding Nagios server manifest entries                [ DONE ]

Adding Nagios host manifest entries                  [ DONE ]

Copying Puppet modules and manifests                 [ DONE ]

Applying 10.10.10.8_prescript.pp

Applying 10.10.10.7_prescript.pp

Applying 10.10.10.6_prescript.pp

10.10.10.8_prescript.pp:                             [ DONE ]      

10.10.10.6_prescript.pp:                             [ DONE ]      

10.10.10.7_prescript.pp:                             [ DONE ]      

Applying 10.10.10.8_chrony.pp

Applying 10.10.10.7_chrony.pp

Applying 10.10.10.6_chrony.pp

10.10.10.8_chrony.pp:                                [ DONE ]   

10.10.10.6_chrony.pp:                                [ DONE ]   

10.10.10.7_chrony.pp:                                [ DONE ]   

Applying 10.10.10.6_amqp.pp

Applying 10.10.10.6_mariadb.pp

10.10.10.6_amqp.pp:                                  [ DONE ]    

10.10.10.6_mariadb.pp:                               [ DONE ]    

Applying 10.10.10.6_apache.pp

10.10.10.6_apache.pp:                                [ DONE ]   

Applying 10.10.10.6_keystone.pp

Applying 10.10.10.6_glance.pp

Applying 10.10.10.6_cinder.pp

10.10.10.6_keystone.pp:                              [ DONE ]     

10.10.10.6_glance.pp:                                [ DONE ]     

10.10.10.6_cinder.pp:                                [ DONE ]     

Applying 10.10.10.6_api_nova.pp

10.10.10.6_api_nova.pp:                              [ DONE ]     

Applying 10.10.10.6_nova.pp

Applying 10.10.10.8_nova.pp

10.10.10.6_nova.pp:                                  [ DONE ] 

10.10.10.8_nova.pp:                                  [ DONE ] 

Applying 10.10.10.8_neutron.pp

Applying 10.10.10.7_neutron.pp

Applying 10.10.10.6_neutron.pp

10.10.10.8_neutron.pp:                               [ DONE ]    

10.10.10.6_neutron.pp:                               [ DONE ]    

10.10.10.7_neutron.pp:                               [ DONE ]    

Applying 10.10.10.6_osclient.pp

Applying 10.10.10.6_horizon.pp

10.10.10.6_osclient.pp:                              [ DONE ]     

10.10.10.6_horizon.pp:                               [ DONE ]     

Applying 10.10.10.6_ring_swift.pp

10.10.10.6_ring_swift.pp:                            [ DONE ]       

Applying 10.10.10.6_swift.pp

10.10.10.6_swift.pp:                                 [ DONE ]  

Applying 10.10.10.6_heat.pp

10.10.10.6_heat.pp:                                  [ DONE ] 

Applying 10.10.10.6_provision.pp

Applying 10.10.10.6_provision_glance

10.10.10.6_provision.pp:                             [ DONE ]          

10.10.10.6_provision_glance:                         [ DONE ]          

Applying 10.10.10.7_provision_bridge.pp

10.10.10.7_provision_bridge.pp:                      [ DONE ]             

Applying 10.10.10.6_gnocchi.pp

10.10.10.6_gnocchi.pp:                               [ DONE ]    

Applying 10.10.10.6_mongodb.pp

Applying 10.10.10.6_redis.pp

10.10.10.6_mongodb.pp:                               [ DONE ]    

10.10.10.6_redis.pp:                                 [ DONE ]    

Applying 10.10.10.6_ceilometer.pp

10.10.10.6_ceilometer.pp:                            [ DONE ]       

Applying 10.10.10.6_aodh.pp

10.10.10.6_aodh.pp:                                  [ DONE ] 

Applying 10.10.10.6_nagios.pp

Applying 10.10.10.8_nagios_nrpe.pp

Applying 10.10.10.7_nagios_nrpe.pp

Applying 10.10.10.6_nagios_nrpe.pp

10.10.10.8_nagios_nrpe.pp:                           [ DONE ]        

10.10.10.7_nagios_nrpe.pp:                           [ DONE ]        

10.10.10.6_nagios.pp:                                [ DONE ]        

10.10.10.6_nagios_nrpe.pp:                           [ DONE ]        

Applying Puppet manifests                            [ DONE ]

Finalizing                                           [ DONE ]


 **** Installation completed successfully ******


Additional information:

 * Time synchronization installation was skipped. Please note that unsynchronized time on server instances might be problem for some OpenStack components.

 * File /root/keystonerc_admin has been created on OpenStack client host 10.10.10.6. To use the command line tools you need to source the file.

 * To access the OpenStack Dashboard browse to http://10.10.10.6/dashboard .

Please, find your login credentials stored in the keystonerc_admin in your home directory.

 * The installation log file is available at: /var/tmp/packstack/20160906-010456-B6ViYg/openstack-setup.log

 * The generated manifests are available at: /var/tmp/packstack/20160906-010456-B6ViYg/manifests



  • 유의해야 할 사항

본 글에서 제공되는 answer.txt 샘플파일은 2016.9월 시점 Mitaka 기준의 것으로, LBaas 설치 옵션(CONFIG_LBAAS_INSTALL)을 켜게 되면 설치 단계에서 "neutron server 실행 오류가 유발되고 있으므로 초기 설치시에는 일단 제외하고 LBaas는 글 후반에 별도로 다룸


Neutron L2 Agent와 Neutron ML2 Mechanism driver는 openvswitch만을 적용하였다.  메커니즘 드라이버는 필요에 따라 linuxbridge, arista, cisco nexus 등을 여건에 맞게 콤마로 구분하여 추가하면 됨


간혹 설치 중간 중간에 노드의 상태가 불안정하여 오류가 발생하거나 더 이상 진행되지 않는 등의 경우가 생기기도 하는데, 강제 종료 후 다시 시도해 보거나, 전체 노드를 리부트 후 재시도 또는 최악의 경우 VM을 다시 만들어 초기 상태에서 재시도를 해야 하는 경우가 있을 수도 있음


잦은 설치 오류로 인하여 /var/log 디렉토리의 서비스별 로그 디렉토리에 log 파일이 넘쳐서 Vbox VM의 디스크가 Full 될 수도 있음에 유의



8. Network 노드 - Neutron 네트워크(OVS bridge) 인터페이스 설정


이제 설치과정의 막바지. Network 노드의 브리지 인터페이스를 다음과 같이 설정해 주어야 한다 


  • Network 노드 브리지 인터페이스 설정

[root@openstack-net ~]# ifconfig

br-ex: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet6 fe80::b4c4:87ff:fe82:a949  prefixlen 64  scopeid 0x20<link>

        ether b6:c4:87:82:a9:49  txqueuelen 0  (Ethernet)

        RX packets 0  bytes 0 (0.0 B)

        RX errors 0  dropped 13  overruns 0  frame 0

        TX packets 8  bytes 648 (648.0 B)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


enp0s9: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet 192.168.0.7  netmask 255.255.255.0  broadcast 192.168.0.255

        inet6 fe80::a00:27ff:fe2f:5037  prefixlen 64  scopeid 0x20<link>

        ether 08:00:27:2f:50:37  txqueuelen 1000  (Ethernet)

        RX packets 27435  bytes 37349433 (35.6 MiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 8894  bytes 643839 (628.7 KiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet 10.10.10.7  netmask 255.255.255.0  broadcast 10.10.10.255

         ...

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0


eth1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet 10.10.20.7  netmask 255.255.255.0  broadcast 10.10.20.255

         ...

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ifconfig 로 네트워크 구성을 살펴 보면 br-ex(외부 연결 브리지)에는 IP 주소가 할당되어 있지 않고, enp0s9 에 외부 연결용 IP(192.168.0.7)이 할당되어 있는 것을 볼 수 있다. 


이제 enp0s9 에 할당된 외부 네트워크 정보를 br-ex(외부용 브리지 인터페이스) 로 옮기고, ifcfg-enp0s9를 br-ex와 연결하는 작업을 해야 한다. 다음의 과정을 따라가 보자.


[root@openstack-net ~]# cd /etc/sysconfig/network-scripts/

[root@openstack-net ~]# cp ifcfg-enp0s9 ifcfg-br-ex

[root@openstack-net ~]# vi ifcfg-br-ex

DEVICE=br-ex

DEVICETYPE=ovs

TYPE=OVSBridge

BOOTPROTO=static

IPADDR=192.168.0.7

NETMASK=255.255.255.0

BROADCAST=192.168.0.255

GATEWAY=192.168.0.1

DEFROUTE=yes

ONBOOT=yes

NM_CONTROLLED=no

IPV4_FAILURE_FATAL=yes

IPV6INIT=no


[root@openstack-net ~]# vi ifcfg-enp0s9

DEVICE=enp0s9

DEVICETYPE=ovs

TYPE=OVSPort

OVS_BRIDGE=br-ex

ONBOOT=yes

NM_CONTROLLED=no

IPV6INIT=no



[root@openstack-net ~]# systemctl restart network

[root@openstack-net ~]# ifconfig

br-ex: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet 192.168.0.7  netmask 255.255.255.0  broadcast 192.168.0.255

        inet6 fe80::b49c:2aff:fe19:7e4c  prefixlen 64  scopeid 0x20<link>

        ether 08:00:27:2f:50:37  txqueuelen 0  (Ethernet)

        RX packets 31966  bytes 5704088 (5.4 MiB)

        RX errors 0  dropped 1  overruns 0  frame 0

        TX packets 7268  bytes 1153521 (1.1 MiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0



enp0s9: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

        inet6 fe80::a00:27ff:fe2f:5037  prefixlen 64  scopeid 0x20<link>

        ether 08:00:27:2f:50:37  txqueuelen 1000  (Ethernet)

        RX packets 1191146  bytes 714542018 (681.4 MiB)

        RX errors 0  dropped 0  overruns 0  frame 0

        TX packets 17821  bytes 2222494 (2.1 MiB)

        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

네트워크 재시작 후 네트워크 구성을 ifconfig 로 확인해 보면, br-ex에 외부 연결용 네트워크 정보가 할당되어 있고, enp0s9의 ethernet(MAC) 주소가 br-ex 의 MAC 주소와 동일한 값을 가진 것을 확인할 수 있다.


9. Compute 노드 - 네트워크 게이트웨이 설정


  • Compute 노드에서 외부로 접속할 필요가 있을 경우에만 해당


앞서 설명했듯이 Compute 에는 외부로 직접 연결 가능한 네트워크가 없으므로, Network 노드를 통해서만 외부 접속이 가능하다.  위의 [주의1] 에서 설명한 부분의 파란 글씨에 해당하는 설정을 Network 노드에서 수행(위에서 설명한 대로 enp0s9는 br-ex 로 변경)한 다음 다음과 같이 설정해 두면 된다.


[root@openstack-cmp1 ~]# cd /etc/sysconfig/network-scripts/

[root@openstack-cmp1 ~]# vi ifcfg-eth0

DEVICE=eth0

TYPE=Ethernet

ONBOOT=yes

NM_CONTROLLED=no

IPADDR=10.10.10.8

NETMASK=255.255.255.0


[root@openstack-cmp1 ~]# vi route-eth0

default via 10.10.10.7 dev eth0


[root@openstack-cmp1 ~]# systemctl restart network



Controller 노드의 환경변수 설정 스크립트 복사


  • Network 노드, Compute 노드에서 CLI 명령 실행이 필요할 경우에 대비하여 스크립트 복사


Controller 노드의 keystonerc 관련 파일들을 다음과 같이 Network 노드, Compute 노드에 복사해 둔다


[root@openstack-ctrl ~]# scp keystonerc_* root@openstack-net:~

[root@openstack-ctrl ~]# scp keystonerc_* root@openstack-cmp1:~



10. Horizon 웹을 통해 Cloud 플랫폼 사용 준비 과정


  • Horizon 웹 UI 사용 준비 - 클라우드 네트워크 설정


시스템 구성도의 Admin VM에 해당하는 VM에서, firefox 웹브라우저를 통해 http://10.10.10.6으로 접속, admin 계정과 answer.txt 에서 지정한 암호를 통해 로그인


tenant 서비스를 위해서 demo 프로젝트를 사용한다고 가정했을 때,  해당 프로젝트에서 사용할 네트워크를 생성, 설정하는 과정을 다음과 같이 진행한다.



사용하지 않는 기본 Router 삭제(Admin->System->Router->demo 프로젝트-public라우터), Public 네트워크 정보(Admin->System->Network->admin 프로젝트-public subnet)을 삭제 



networks 패널에서, '+Create Network' 을 선택하고 admin 프로젝트에 대해 public 네트워크를 새로이 생성(본 케이스에서는 vxlan 방식, shared - External network).



생성된 public 네트워크 항목을 선택하여, 하단의 '+Create Subnet' 을 클릭,  실제로 사용할 외부 네트워크 정보(CIDR, Gateway, DNS 등)을 Subnet, Subnet Detail 항목 선택을 통해 각각 등록


admin 계정을 로그아웃하고, tenant 서비스를 설정/사용하기 위한 demo 계정으로 로그인

앞에서 admin 을 통해 생성, 설정한 네트워크를 사용하기 위해, 왼쪽 메뉴에서 Project->Network->Router 패널을 선택하고 '+Create Router' 을 클릭하려 demo 프로젝트에서 사용할 라우터를 생성. External Network 항목에 대해, 앞서 생성했던 public 네트워크를 선택



'+Create Gateway' 클릭을 통해 외부 네트워크와의 연결을 위한 게이트웨이를 설정




'+Add Interface' 클릭을 통해, 앞서 생성한 사설 네트워크와의 연결을 위한 인터페이스를 추가


좌측 메뉴의 Network 패널에서 Network Topology 를 선택하여 현재 구성된 네트워크의 구조를 확인한다. 여기까지 성공적으로 진행하였다면 클라우드 환경을 본격적으로 사용하기 위한 전체 설정 과정은 완료한 것이며, 다음의 필수 준비과정 몇 가지를 거치면 모든 준비가 끝나고 VM을 생성, 관리할 수 있게 됨.


11. VM 인스턴스 생성 관리를 위한 준비 & 실행 단계


  • security Group 생성


  • Key Pair 생성



  • Floating IP 생성



  • VM Image 확인


  • 생성된 VM 목록




12. LBaas(LB As A Service) 의 설정과 테스트


LBaas 의 설치 방법에 대해서는 여기를 주로 많이 참조하게 되는데(Kilo 버전 기준), 현재 시점의 Mitaka에 적용시에 적욥하지 않은 일부 오류가 숨어 있으므로 아래의 내용으로 진행하면 크게 무리 없이 성공적으로 수행 가능



  • Controller, Network 노드에서 각각 haproxy, lbaas 설치
[root@openstack-ctrl ~]# yum install -y haproxy openstack-neutron-lbaas


  • Controller 노드에 driver, plugin 설정
[root@openstack-ctrl ~]# vi /etc/neutron/neutron_lbaas.conf
service_provider = LOADBALANCER:Haproxy:neutron_lbaas.services.loadbalancer.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default
[root@openstack-ctrl ~]# vi /etc/neutron/neutron.conf
service_plugins 리스트에 lbaas 추가
[root@openstack-ctrl ~]# systemctl restart neutron-server.service

  • Network 노드에 lbaas agent 설정
[root@openstack-net ~]# vi /etc/neutron/lbaas_agent.ini
device_driver = neutron.services.loadbalancer.drivers.haproxy.namespace_driver.HaproxyNSDriver
user_group = haproxy
interface_driver = neutron.agent.linux.interface.OVSInterfaceDriver

[root@openstack-net ~]# systemctl restart neutron-lbaas-agent.service
[root@openstack-net ~]# systemctl enable neutron-lbaas-agent.service

  • Horizon(Controller 노드) 설정
[root@openstack-ctrl ~]# vi /etc/openstack-dashboard/local_settings
'enable_lb': True

[root@openstack-ctrl ~]# systemctl restart httpd.service


이제 Openstack Horizon 에 로그인하여 Network 패널 아래에 Load Balancer 라는 서브 패널이 나타나며, Load balancer 설정 및 사용이 가능해 짐을 볼 수 있다.


다음에 다룰 내용은?


2016년 10월에 Mitaka 후속 버전으로 Newton 버전에 릴리즈되었습니다. 다음에 다룰 내용은 최신의 Newton 버전이 되겠으며, 본 포스팅과 중복되는 부분은 제외하고, 기본적인 설치/설정 과정의 요약 정리와 함께 로드밸런싱을 위한 VM 이미지의 준비, Haproxy 기술을 활용한 LB의 대상이 되는 VM에 웹서버를 설정하고 부하 분산의 작동 테스트를 포함하여, answer 파일의 개별설정과 VM의 HOT(Heat Orchestration Template)에 의한 VM 자동증설(Auto-scale: Scale out/in)에 대해서 다루어 보도록 하겠습니다. 


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


2016년 3월 9일, 이세돌은 알파고와 ‘구글 딥마인드 챌린지 매치’ 5번기 제1국에서 186수 만에 흑으로 불계패를 당했다.  한마디로 충격적이다. 세계 최정상급 프로바둑 최정상급 기사 이세돌의 패배를 어떻게 받아들여야 할까?


약 십 수년 년 전에 필자는 "The Many Faces Of Go" 라는 PC용 바둑프로그램을 어렵사리 구해서 바둑을 두어 본 적이 있었다. 지금처럼  온라인 웹사이트도 없었고 관련 정보도 부족했지만 아마추어 4~7급 수준이라고 알려졌었고, 당시 필자의 수준은 대략 7~9급 수준이었다고 기억된다. 하지만 당시 나는 한마디로 프로그램을 "가지고 놀았"었다.


원래 하수들은 되지도 않은 꼼수를 좋아한다. 내 꼼수에 컴퓨터 프로그램은 아무렇게나(녀석은 나름 최선을 다했겠지만) 맞두다가 박살이 났었으니까. 심지어 초읽기 시간제한도 최대로 했었는데 말이다. 그래서 "아직은 아니구나" 생각했던 시절이 기억난다. 그런데..., 이런 일이 결국 일어나고 말았다. 경우의 수가 상대적으로 작은 장기나 체스도 아니고 바둑에서 말이다.


컴퓨터에 사람이 진 것이 처음은 아니다. 그러나...


지난 1997년 체스 챔피언 카스파로프를 무너뜨린 IBM의 딥블루는 일종의 버그[각주:1]로 인간을 물리쳤다는 말(연합뉴스 참조. Deep Blue 개발자의 고백)이 있다. 한편, 프로 바둑기사가 컴퓨터에 패배한 것이 처음은 아니다. 이미 2015년 10월에 알파고는 유럽챔피언 판후이 2단를 5대 0으로 대파한 적이 있었다. 


2015년 10월 판후이 2단: 알파고 기보 중 하나


이때만 해도, 바둑의 본고장도 아니고 유럽의 프로 2단 실력이니 그럴 수도 있겠다 싶었다. 그러나 세계 최정상급 이세돌 9단이 근소하게 진 것도 아니고 186수 만에 불계패를 당했다. 이제 바둑에서까지 사람이 컴퓨터에 도전해야 하는 시대가 온 것인가?



'알파고'의 수준은 어느 정도인가?


알파고는, 타이젬[각주:2] 기준으로 20초 속기 수준으로 보면 프로 50위권 밖이라고 평가되었었다(관련 기사). 하지만 이 평가는 알파고가 최정상급 프로기사들의 기보를 집중적으로 공부하기 전의 수준이 아니었을까 짐작된다. 알파고는 판후이와 대국하기 전에 유럽의 아마추어 대국 기보를 공부했었다고 한다(이러고도 판후이에게 압승). 그리고 그 이후에는 세계 최정상급 기사들의 기보를 집중적으로 연구했었을 것이라는 추측이다. 


알파고의 능력을 보자면, 이미 3000만 개 이상의 기보를 학습했다고 하며, 당연한 말이겠지만 쉬지도 자지도 않고 대국을 하며 바둑 수를 배웠다고 한다. 벌써 2016년 1월 동안 4주 만에 100만 대국을 익혔고 하루에 3만 대국을 소화할 수 있다고 딥마인드 측은 밝힌 바 있다(참고: 알파고 알고리즘 요약 - slideshare.net).


2016년 3월 9일 이세돌:알파고 제1국 기보. 타이젬 기보 캡처


대국 전 이세돌 기사는 호언장담했었다. 2시간 제한에 60초 초읽기 3번 정도면 5:0 내지 4:1 정도로 이기지 않을까 생각한다고..., 그리고 세계 최정상급 프로기사들의 기보는 최대 1만 5천 개 가량밖에 안 되고 저작권 문제도 있으므로 제아무리 최강의 "딥 러닝(Deep Learning)과 강화학습(Reinforcement Learning; RLAI)" 능력을 가진 알파고라도 사람에게는 무리라는 예측이 나돌았었다. 그러나 첫 대국이기는 하지만 그 결과는 참담하다. 


결국, 불계로 끝났지만, 형세 상으로 보면 덤 7집 반을 공제하고 반면으로 4집 반가량 알파고가 앞섰고, 프로 기사의 대국에서 4집 반이면 아마추어에서는 소위 '만방' 이라는 점이 더 무섭게 다가온다. 일반 뉴스나 신문들은 우세한 국면에서 이세돌이 실수 했기 때문이라고 하지만, 일부 전문가들은 대국 내내 단 한 번도 이세돌이 우세했던 적이 없었다고 분석하기도 한다(나무위키).



그러나 결국 인간이 승리할 것?!


총 5국 중 제 1국을 승리한 DeepMind 측 CEO인 데미스 하사비스(Demis Hassabis)는 뛸 듯이 기뻐하며 "우리는 달에 착륙했다." 라고 소감을 밝혔다고 한다. 한 편, 대국을 끝낸 알파고는 담담하게 다음과 같은 소감을 밝혔다고 전해진다. "0001011010111110101000010111101010...". 믿거나 말거나.


IT 관계자로서는 박수를 보내고 싶긴 하지만 ..., 어찌 보면 섬뜩한 결과에 무서운 생각마저 들기도 한다. 이미 인공지능으로 무장한 컴퓨터에 회계사, 변호사, 각종 자문역할 등의 전문직 일자리까지 뺏길지도 모른다는 불안감이 팽배한 이 시점에서 말이다. 그러나, 냉정하고 실수가 적은 '컴퓨터'라는 상대방을 몰라서 방심한 한순간의 실수일 뿐, 결국 바둑에서만큼은 인간이 승리할 것이라고 믿고 싶다.


이 대국 결과를 두고  에릭 슈미트 구글 회장은 "대국의 결과와 상관없이 최종 승자는 인류라"는 의미심장한 말을 남겼다. 나는 이 말이 진심이라고 믿고 싶다. 사람은 컴퓨터를 지배하고 통제하면서 활용할 수 있어야 한다. 그렇지 않다면 언젠가 영화 '터미네이터' 와 같은 암담한 종말이 올지도 모르기 때문이다.



번외1: 2국도 졌다, 이 난국을 어떻게 하나?


1국에 이어 3월 10일에 속행된 2국도 완패했다. 알파고의 "신의 한 수" 인 37수는 인간의 고정관념의 정곡을 찔러버린 수라고들 한다. 알파고가 1분 만에 한 수씩 둔다는 생각도 깨졌다. 국면이 복잡해지자 2분가량 소위 '장고'를 하는 모습도 보여 줬기 때문이다.


"생각외로 만만치 않다"가 아니라 이제 한 판이라도 이겨야겠다는 인간 이세돌의 절박함은 다음과 같은 대응책을 밤새 고안해 냈다. 그의 절친 홍민표 9단, 박정상 9단 등과 함께...(경향신문).


그런데 이 와중에 이세돌이 졌기 때문에 바둑의 부흥을 위해 새누리에 입당했다는 조훈현 씨는 도대체 무슨 생각일까? 그게 무슨 상관이 있는 건지 참으로 오묘신기 빵빵이다. 어쨌든...,


  • 알파고는 수많은 계산이 필요한 초중반에, 변수(경우의 수에 의한 변화)를 줄이려고 노력한다. 즉 오묘한 형세 변화를 주도하는 '패'를 유도하여 반상의 변화를 꾀한다(이 부분은 대국 후 이 9단과 함께 작전회의(?) 를 했던 동료 기사들의 주문이었고, 정작 이 9단 본인은 "자기의 바둑을 두면 되지!"라고 결연히 말했다는 후문이다 = 노컷뉴스)).
  • 마찬가지 전략에 의해, 알파고가 싫어하는 듯한 국면 즉, 판의 모양을 크게 만들어야 한다
  • 당연한 이야기지만, "실수 줄이기"이다. 상대방도 완벽한 인공지능은 아니므로 초중반에 알파고가 저지르는 실수를 파고들면서, 경우의 수가 줄어드는 후반에 실수를 줄여야 승산이 있다(사실 알파고의 37수는 일종의 실수가 아닐까 하는 생각이 드는 건, 내가 하수이기 때문일까? ^^;;).


과연 결과가 어떻게 될지 귀추가 주목되는 "진정한 세기의 대결"이다. 이세돌 파이팅!!!



번외2: 3국도 완패, 그런데 커제라면?


3국도 걱정했던 대로 완패하고 말았다. 이제 5국 중에서 단 한 판이라도 이겨 보는 게 목표로 바뀔 듯하다. 패에 의한 복잡한 양상은 피하는 듯했지만, 팻감을 염두에 둔 형세 판단에 의한 선택도 정확했다는 평이 지배적인 듯하다.


이제 본격적으로 인간이 인공지능 컴퓨터에 도전하는 형국이 되는 것이다. 돌아가는 분위기상, 최근 이세돌과 호각을 보이는 중국의 커제 9단이 알파고의 도전을 받아들일 듯한데, 그 결과가 주목되는 시점이다.


한편, 국내 뉴스들이 전한 바 있는 커제의 '망언'은 중국어에 일가견이 있는 한 네티즌에 의하면 오보라는 얘기가 있다. 다시 한 번 국내 기자들의 기레기적 속성이 고개를 내민 것이 아닌가 하는 생각을 지울 수가 없다(MLBPARK). 번역된 내용을 보면 커제가 했던 말 어디에도 "이세돌이 인류 대표 자격이 없다." 라는 말은 찾아볼 수가 없었다는 것이다.


사실 확인 없이 퍼 나르기만 하는 "기레기는 각성하라!" 제발~!!!



번외3: 이세돌의 첫 승리를 축하합니다


마음을 비운 듯한 이세돌이 드디어 최강의 인공지능 알파고(알사범, 알9단으로도 불리운다고...)에게 첫 승리를 거두었다. 이 9단의 힘겨운 승리를 지켜보며 2014년에 개봉했던 영화 "신의 한 수" 의 내용이 잠깐 떠올랐다. 주님(안성기 분)이 죽어가면서 태석(정우성 분)에게 한 말이다. "너무 유연해서 꺾을 수가 없어. 부러지지가 않아... 아이가 두는 거야, 순수한..."



영화 "신의 한수(2014, 조범구 감독)" 중에서



어쨌든, 이로써 인공지능도 상대방의 신의 한 수에 멘붕(?)을 겪고 '떡수' 를 두거나 치명적인 실수를 할 수도 있다는 것과, 알파고도 불계패 즉, "돌을 던질[각주:3]" 수도 있다는 것을 보여 주었다. 집념의 이세돌, 정신력과 집념의 승리를 축하합니다. 바둑계의 동료 기사들도 "아름다운 바둑", "멋진 대국"이라고 칭찬했다고 한다.



이세돌의 회심의 일격. 사진 출처: Yettie Studio 생중계 화면 캡처




AlphaGo, 돌을 던지다! 사진 출처: Yetti Studio 생중계 화면 캡처



2016년 3월 13일 이세돌:알파고 제 4국 기보. 타이젬 기보 캡처



- Barracuda -


  1. 더 이상 불리한 국면을 타개할 방법이 없자 랜덤하게 아무렇게나 두어 버리는 일종의 버그수. 정작 상대방인 카스파로프는 도대체 이게 뭔가 하고 깊은 고민을 하다 멘탈이 무너져서 악수를 두고 이후 게임들까지 망쳐버렸다는 후문 [본문으로]
  2. 타이젬바둑(Tygem)은, 동양온라인이 운영하고 있는 인터넷 바둑사이트. 2004년에 설립되었고 한국, 일본, 중국 각지에 홈페이지가 있다. 대국 시스템 이외에, 기전, 바둑강좌, 각종 이벤트를 운영하고 있다. 2004년에 조훈현, 이창호, 유창혁등의 바둑기사, 동양그룹, 중앙일보, DACOM 등에 의해 설립되었다. 2008년 7월에는 홈페이지 명칭이「동양바둑」이라 개칭되었다(위키백과) [본문으로]
  3. 전해지는 바로는 승리할 확률이 10% 이내이면 항복 즉, 돌을 던지게 되어 있다고 한다. [본문으로]
저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들

Mac OSX 에는 Xcode 라는 좋은 무료 개발도구가 있다. 물론 Mac 에서도 CodeLite, CodeBlocks 와 같은 잘 알려진 오픈소스 개발도구를 쓸 수 있지만, Codeblocks 는 Mac OSX 에서의 안정성이 떨어지는 감이 있고 CodeLite 는 Mac 에서 command-line 프로그램 개발 시 iostream 의 cin 을 제대로 처리 못하는 등 조금씩 문제를 안고 있기 때문에 쓰다 보면 불편함이 쌓이게 되고, 그러다 보니 Apple 에서 Mac 을 가장 잘 지원해 주(는 것으로 믿고 싶은...)는 Xcode 같은 Native app으로 다시 돌아가곤 한다.


이번 글에서는 Xcode 로 C++, Objective-C, Swift 와 같은 언어를 사용한 개발에 있어서, 협업 개발을 위한 git 저장소와 Xcode 가 자체적으로 제공하는 git client 기능을 사용하는 방법을 기록해 두려 한다.


개발자의 결벽증

물론 Remote 저장소에서 repository를 cloning 하거나 Local 저장소에서 작업하다가 그때 그때 push 하면서 "미친 년 널 뛰듯이" 아무렇게나 되는대로 작업할 수는 있다. 그러나 사람마다 기준이나 스타일은 다르겠지만, 하드디스크의 작업 영역에 온갖 너저분한 임시 checkout 디렉토리, 수정하다 만듯한 폴더와 파일 조각들이 여기저기 널려 있는 꼴은, 마치 소스의 indent 가 들쭉날쭉한 참상을 보는 것 만큼이나 속상하는 일이기 때문에 다음과 같은 원칙을 정해 둔다


  • 하나의 프로젝트에 대해 주로 작업하는 Local 저장소와 Remote 저장소의 대응은 1:1 일 것
  • 사용하는 Tool 이 Sublime Text 또는 Sourcetree 이건 Xcode 이건 간에 프로그램 소스가 저장되는 repository는 하나만 존재할 것
  • 프로그램 소스코드와 데이터파일을 제외한 프로젝트파일, 바이너리 실행 또는 오브젝트 파일들은 git 을 통한 버전관리에서 제외 될 것


필요한 것들(또는 지금 쓰고 있는 것들)

  • Remote 저장소(Github, Gitlab 계정 또는 본인만의 git server) - 필자는 Remote git 저장소를 위한 AWS 가상머신(doubleshot.io)
  • Git Client - OSX 에 내장된 자체 git & Sourcetree app



정리해 두려는 것들

  • 이미 존재하는 Remote 저장소를 내려 받고 Xcode 에서 작업하거나 또는,
  • 새로운 Local 저장소를 만들어 Xcode 에서 작업하고, Remote 저장소와 연동, 업로드(보관)



1. Remote 저장소를 내려 받고 Xcode 에서 작업하기


1.1 저장소 이름은 gittest2, Remote 저장소의 주소는 ssh://gituser@doubleshot.io/home/gituser/repos/gittest2 라 하자. 


1.2 가장 먼저, Sourcetree 에서 Remote 저장소를 cloning 해서 Local 로 다운로드 한다. 다음으로 Xcode 에서 작업할 새로운 프로젝트를 생성해 두고, Sourcetree 창을 나란히 배치한다.


해당 repository 에서 R-click, "Show in Finder" click


Finder 에서 저장소 폴더를 선택, Xcode 의 프로젝트 내부로 drag 한다(또는 Finder를 거치지 않고 Sourcetree 에서 Xcode 로 직접 drag 해도 된다)


Xcode 는 외부 폴더를 가져올 때의 옵션을 묻는다. 그림과 같이 설정하고 Finish!(Create Group을 하면 Xcode 내에 그룹이 만들어지고 그 내부에 git 폴더가 논리적으로 참조되고, Create Folder Reference 를 하면 프로젝트 내에 그룹이 만들어지지 않고 git 폴더 자체가 직접 참조된다)


Xcode 프로젝트 안으로 외부 git 저장소가 안전하게 import 되어 있다. 오른쪽 정보창에서 저장된 path 를 확인해 보자. Xcode 가 기본으로 자동 생성해 주는 main.cpp 는 휴지통으로...!



1.3 위의 과정을 거치면 Xcode 내에서 add, commit, push 등 모든 git 기능을 사용할 수 있다. Xcode 프로젝트 내에 Working copy 가 복사되어 만들어지는 것이 아니라, Sourcetree 가 Clone 해 둔 repository를 그대로 사용하는 것이다.



2. Local 저장소로 Xcode 에서 작업하고 Remote 저장소와 연동하기


2.1 Git server 에서 Remote 저장소를 생성해 둔다(이 과정은 아래 2.7 이후에 해도 된다). 저장소 이름은 xcodegit_repo, Remote 저장소의 주소는 ssh://gituser@doubleshot.io/home/gituser/repos/xcodegit_repo 라 하자. 


2.2 Sourcetree 에서 "Create a local repo..." 선택, Local 저장소를 생성한다(이 때, "Also create remote..." Check는 반드시 해제).



2.3 Xcode 에서 작업할 새로운 프로젝트를 생성한다.



Xcode 에서 프로젝트 생성 후, 자동 생성되는 main.cpp 는 휴지통으로... 만약 사용할 필요가 없다면 main.cpp 파일이 속한 그룹 전체를 삭제해도 무방함



2.5 Sourcetree 에서 Local 저장소를 R-click, "Show in finder" 선택. Finder 에서 Xcode 프로젝트 내부로 폴더 drag

Xcode 의 프로젝트 내부로 drag(Sourcetree 에서 직접 drag 해도 된다)


1.2와 마찬가지로 가져오기 옵션 설정



2.6 Xcode 로 가져온 Local 저장소 폴더 R-click, "New file" 을 선택(저장되는 path 확인), 작업할 소스 파일(cpp 등) 생성.



2.7 Xcode 에서 소스 파일을 편집, 저장 후 git add, commit 등 실행(로컬 저장소에 저장)


2.8 작업이 완료된 소스 파일을 Remote 저장소와 연동 저장하기 위해서는 소스 파일이 저장된 폴더 선택, Xcode 메뉴의 "Source Control" 선택, Remote 저장소 정보를 설정한다.


Add Remote 선택


Git server 에서 생성해 둔 Remote 저장소의 주소를 등록


코딩 및 테스트 작업이 끝나면 Xcode 메뉴에서 "Source Control" -> Push 를 통해 Remote 저장소에 업로드할 수 있다


<참고> Git server 와의 연결을 ssh 프로토콜을 통할 경우에는, Xcode 가 생성해 주는 ssh 인증키를 git server 측과 '키교환'을 해 두어야 한다  


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


소장하고 싶은 영화나 동영상 파일들을 모아서 디스크 한 켠에 폴더별로 잘 정리해서 보관하는 경우가 많다. 그렇다고 자주 들춰보기는 쉽지 않지만, 일종의 "혹시나 다음에 또 보고 싶어질지도 몰라...ㅋ" 하는 미련 같은 건 아닐까(그러다 결국 한 순간에 폴더째 지워버리게 되기도 하지만...ㅠㅠ). 아, 위에 나오는 짤방성 이미지의 context sensitive한 자막은 참 공감이 많이 가고, 무엇보다 푸근한 처자에게 고맙다(뭐가 ? ^^;;).


2016년 첫 포스팅 치고는 거의 솔플메모에 가까울 정도로 가볍다. 언제나 심각하고 거창할 순 없는 거다. 각설하고, 이제 본론인 메모 작성으로 넘어가자.


Handbrake로 동영상을 변환

다운로드 받은 영화를 보관 또는 거실TV에서 상영하기 전에 거실 PC의 Linux(Ubuntu 14.04 LTS)에서 무료 동영상변환기인 핸드브레이크(Handrake)로 인코딩 하게 된다(Mac book에도 Adapter라는 쓸만한 인코딩 앱이 있긴 하지만, 귀여운 내 맥프레에게 이런 격 떨어지는 일을 시키고 싶지는 않다).


그런데, TV(스마트 TV가 나오기 전 모델의 42인치 LG LED)의 미디어플레이어가 개후져서 조금 옛날 것이라 AC3 오디오를 인식 못하거나 새로운 코덱으로 인코딩 된 mp4 파일은 아예 화면도 띄우지 못하는 경우가 많다. 이 때 만날 수 있는 여러 문제 상황을 단순하게 넘기려면, 아예 Handbrake에서 동영상 변환을 할 때 영상의 화면에 자막을 심어 버리는 작업을 하는 쪽이 속 편하다.


한글 자막 .smi 파일은 대부분 EUC-KR 코드

인터넷에서 입수되는 대부분의 한글 자막은 smi 포맷으로 되어 있고, 한글코드는 EUC-KR 인 경우가 많다(불평은 하지 않습니다. 자막 제작자님들 고맙습니다 ^^). 그런데 문제는 Handbrake가 srt 포맷의 자막만 지원하기 때문에 2번의 변환을 거쳐야 한다. 한 번은 한글 코드 변환, 또 한 번은 자막파일 변환. 이과정이 귀찮아서 Bash shell 을 작성해 보았다. 사용 방법과 소스는 아래 참고.


자막 변환 스크립트 사용법

우선 smi -> srt 자막을 변환하는 Linux 용 프로그램 subs를 설치해야 한다(작성자 <Dmitry Karasik, dmitry@karasik.eu.org> 에게 감사). 좀 오래 된 거지만 지금도 쌩쌩 잘 돌아간다.  그 다음에 사용할 iconv 는 대다수 Linux 배포본에 포함된 코드 변환 도구이므로, 따로 설명은 생략한다.


다음과 같이 subs 를 우선 설치.

$ sudo apt-get install libsubtitles-perl

$ subs -> 테스트 삼아 한 번 실행해 보자

subs: convert, join, split, and re-time subtitles


format:

   subs [options] subfile [ subfile ... ]


options:

   -a coeff, - a and b coefficients in linear transformation

   -b time     u=at+b, where t and u are src and dest times

...


사용법은 간단하다. 스크립트(conv_eucsmi2utfsrt.sh ... 파일 명은 알아서 변경)를 Linux 계정의 $HOME/bin  정도에 복사해 두고(chmod +x 가 필요할지도...), 변환할 자막 파일이 있는 경로로 가서 "conv_eucsmi2utfsrt.sh 자막파일.smi" 같은 식으로 실행해 주면 된다. 변환에 성공했을 경우 다음과 같이 나오게 될 것이다.


명령어 인자로 전달하는 자막파일명 부분에 와일드카드를 쓴다든지, 여러개 파일의 일괄변환 등에 대해서는 따로 고려하지 않았다. 모든 smi 파일을 단숨에 변환할 일이 그다지 많지 않기 때문인데, 나중에 필요하면 한 번 생각해 보기로...


$ conv_eucsmi2utfsrt.sh tmp.smi

Converted tmp.smi(euc-kr) to tmp.srt(utf8)

Enjoy it! ;)


특별한 기술이 필요하거나 대단히 복잡한 것은 없다. 잘 보면 아래 부분 실행문들에서 iconv, subs, sed 를 적당한 옵션을 주어 각각 실행하는 것이 전부다. 그러나 귀찮은 작업을 스크립트 하나 실행해서 해 낼 수 있다는 점에서 자화자찬 중.


conv_eucsmi2utfsrt.sh (Bash shell script 소스)

#!/usr/bin/env bash
# Bryan(Barracuda), 2016.1.
# Usage: conv_eucsmi2utfsrt.sh filename.smi(with EUC-KR Kor. char.)
#
# *.smi 파일(euc-kr)을 iconv와 subs 를 써서 *.srt(utf8) 형식으로 변환
# 변환 후 srt 파일 내에 smi 파일에서 자주 보이는 흔적인   가 있으면 삭제함
# 자막 변환 도구 subs는 sudo apt-get install libsubtitles-perl 로 설치되어 있어야 함(ubuntu 기준)

args=1
param=$1

if [ "$#" -ne "$args" ]; then
    # $0 의 Path 부분 삭제: ${0##/*/}
    echo -e "\033[0;33m$# arguments ... use \"${0##/*/} filename.smi\"\033[0m"
    exit 1
fi

# 인자로 전달 된 파일명에서 확장자 부분을 제외한 앞 부분을 잘라냄(뒤에서부터 .로 시작하는 부분을 삭제)
# param이 . 으로 끝나면 basename은 "filename", extension은 ""
# param에 . 이 없으면 basename은 "filename", extension도 "filename", index는 "0"
basename=${param%*.*}
extension=${param##*.}
index=`expr index $param .`

# 파일명이 . 으로 끝나는 경우, 파일명에 . 이 없는 경우, 확장자가 smi 가 아닌 경우 오류
if [[ "$extension" = "" || "$index" = "0" || "$extension" != "smi" ]]; then
    echo -e "\033[0;33mBad file extension ... I want \"*.smi\" file.\033[0m"
    exit 1
else if [ ! -f $param ]; then
        echo -e "\033[0;33mFile $param missing.\033[0m"
        exit 1
    fi
fi

if [ -e "$basename.srt" ]; then
    echo -e "\033[0;33mFile $basename.srt already exists. Plz delete or rename it.\033[0m"
    exit 1
fi

# 한글 euc-kr 코드를 utf8 코드로 변환
iconv -f euc-kr -t utf8 $basename.smi > _$basename.smi
# smi 파일을 srt 파일로 변환
subs -c srt _$basename.smi -o _$basename.srt
# srt 파일 내의   를 제거
sed "s/ //g" _$basename.srt > $basename.srt
# 중간 작업파일 제거
rm -f _$basename.smi _$basename.srt

echo -e "\033[0;32mConverted $basename.smi(euc-kr) to $basename.srt(utf8)"
echo -e "\033[0;32mEnjoy it! ;)\033[0m"


<*주의*> 위 소스의 44~45 라인을 아래 내용으로 수정 바랍니다. syntaxhighlighter 로 bash 소스를 표시할 경우 html 요소(&nbsp;) 가 정상적으로 출력되지 못하는 군요. 이 점 양해 바랍니다.

# srt 파일 내의 &nbsp; 를 제거

sed "s/&nbsp;//g" _$basename.srt > $basename.srt



Mac 사용자라면 간단히 MinySubtitleConverter 를 써 보자


만약 Linux 가 아닌 Windows 나 Mac 사용자라면 http://blog.myhyuny.com 블로그를 방문하여 MinySubtitleConverter 를 다운로드 받아서 사용하면 위의 과정을 간편하게 한 번에 끝낼 수 있다. 직접 테스트 해보니 한글코드와 srt 변환이 아주 훌륭하게 작동한다.


2014년 3월이 마지막 업데이트이긴 하지만 OSX 10.11(El Capitan) 에서도 문제 없이 사용할 수 있는 것으로 확인되었다.




Input Encoding 을 따로 지정하지 않고 Auto 로 두어도 한글 코드를 자동으로 인식한다. File 메뉴에서 Open -> smi 파일을 선택하면 smi 파일이 있는 폴더에 srt 파일이 자동 생성된다.



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들



[스택오버플로우] 2줄 짜리 단순한 질문에 대한 명료한 분석과 이유를 달아준 명답 of 명답. 지난 5월에 올라온 이슈답변에 이 시간 현재 평점 38만 점, 황금배지 54개!

보러 가기


짧고 격하게 공감하고 오래 기억하라


단순한 따라하기 보다는. 왜 그래야 하는지 알려고 노력하는 것이 얼마나 중요한지 새삼 깨닫게 해 준다.

비단 C++코딩에서 뿐이랴. 특히 과학을 하는 이에게 원리와 이유의 탐구가 얼마나 중요한가 말이다. 그러니 늘, 기본에 충실하라. 이건 나 자신에게 하는 말.


* RAII 는 Resource Acquisition IS Initialization 이라는 표현의 약어로 C++을 창시한 Bjarne Stroustrup 이 주장하는 일종의 기술적 원칙이다.


[스택오버플로우] 2줄 짜리 단순한 질문에 대한 명료한 분석과 이유를 달아준 명답.지난 5월에 올라온 이슈답변에 이 시간 현재 평점 38만 점, 황금배지 54개!단순한 따라하기 보다는. 왜 그래야 하는지 알려고...

Posted by Bryan Lee on Wednesday, December 2, 2015


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들



지난 포스팅의 마지막에 Fizzbuzz 를 풀어 내는 희한한 예제를 게시한 바 있습니다. 좀 오래 되긴 했지만, 궁금해 하는 후배가 있어 한 번 같이 분석해 보았고 독특하고 엉뚱한 생각에 재미를 조금 느끼기도 했습니다.


실제 인터뷰시의 사례인지는 알 수 없지만, 이 해법을 소개한 페이지(Fizzbuzz 에 지겨워진 개발자들)을 잠깐 보면, "일단 똑똑하다", "뽑고 싶다" 거나 "생각이 한쪽으로 쏠린 사람", "팀웍을 해칠 것 같다" 는 등의 다양한 반응를 예로 들고 있네요. 글 쓴이(Samuel Tardieu) 자신은 일을 하면서 이런 식의 재미를 추구하는 방식을 좋아한다고 적고 있기도 합니다. 여러분은 어떠신가요?


우선, 소개된 원본 소스를 그대로 두고 한 번 훑어 보기로 합니다. 참, 한 번 실행시켜 보셨나요? 다른 잘 짜여진 소스와 마찬가지로 정확하게 결과를 출력해주고 있습니다. 희한하네? ^^;;

#include <stdio.h>

static const char *t[] = {"%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n"};

int main()
{
  unsigned int i;
  for(i = 1; i <= 100; i++) printf(t[3&19142723>>2*i%30], i);
  return 0;
}


C 프로그램이고, t 라는 문자열들을 초기화해서 가지고 있는 문자열 포인터 배열이 보입니다. t[0] 는 "%d\n", t[1] 은 "Fizz\n" ...이 되겠지요.


그 외에는 for loop 에서 printf 함수를 쓴게 답니다. 그럼 어디에서 저런 괴물같은 결과가 나온 걸까요?


가만히 보면 printf 에서 사용하는 파라미터의 t[3 & 19142723 >> 2 * i % 30] 부분에 뭔가 비밀이 숨어 있다는 얘긴데요. [] 내부의 값은 t[] 배열의 index 로 0~3 사이의 정수값을 가지게 되겠네요.


(1) 어떤 트릭일지 들여다봅시다. 3 & 19142723 >> 2 * i % 30 ... 부분을 보면 우리 눈을 현혹시키는 것이 한 가지 숨어 있습니다. 다름 아닌 연산자 우선순위! 이게 첫번째 포인트.


저 계산식을 우선 연산자 우선순위에 맞게 괄호를 써서 어떤 값을 어떻게 가지게 되는지 확인해 보아야겠습니다.


3 & (19142723 >> ( (2 * i) % 30) ) 이렇게 됩니다. &는 bit AND 연산으로 가장 우선순위가 낮고, 그다음 낮은 것이 >> 즉 RShift(오른 쪽 비트와이즈 쉬프트 연산) 이지요.


자 그러면 (2*i) 가 가장 먼저 계산됩니다. i는 1~100 사이의 카운터니까, (카운터 * 2) 를 계산한 값을 30으로 나눈 나머지를 취하는군요. 즉 2%30, 4%30, 6%30, ..., 28%30, 30%30 처럼 {2, 4, 6, ..., 28, 30, 0, 2, 4, ....} 와 같이 나열해 보면 15를 주기로 0부터 리셋되어 2씩 값이 증가하는 수들이 나열됩니다.


(2) 그럼 위의 식은 19142723 이라는 묘한 숫자값을 처음에는 2회 Rshift, 그 다음 4회 Rshift, 이런 식으로 반복해서 2칸씩 Rshift 하고 3(2진수로 11)과 bit AND 연산을 하는 겁니다. 그렇다면...


(3) 모든 비밀은 19142723 이라는 숫자에 숨어 있는 거군요. 이 숫자를 2진수로 출력해 보면(아래 예제에 2진수 출력 함수가 있으니 확인해보세요) 00 00 01 00 10 01 00 00 01 10 00 01 00 00 11 이렇게 됩니다. 이해하기 쉽게 2자리씩 끊어서 보자구요.


위의 (2)에서처럼 생각하면, 카운터 i 가 1일 때 00 00 01 00 10 01 00 00 01 10 00 01 00 00 11 을 오른쪽으로 2칸 밀면 끝 자리에 00이 나오고 2진수 11과 AND 연산을 하면 00 즉 0이 나옵니다. 카운터 i가 2일 때는 오른쪽으로 4칸 밀면 끝자리에 00, 2진수 11과 AND 연산을 하면 00, 카운터 i 가 3일때는 6칸 밀고 끝자리에 01 과 2진수 11 AND 연산하면 01 이 나오지요.


즉 주어진 숫자를 2번씩 Rshift 하면서 2비트씩 뽑아 먹는 일을 계속 반복하는 겁니다.


자, 여기서 다른 Correct한 예제 솔루션들에서 출력되는 결과를 다시 한 번 보면,


 1

 i 값 = t[0]

 2

 i 값 = t[0]

 Fizz

 t[1]

 4

 i 값 = t[0]

 buzz

 t[2]

 Fizz

 t[1] 

 7

 i 값 = t[0]

 8

 i 값 = t[0]

 ...

 

 15

 t[3]

 16

 i 값 = t[0]


이 패턴이 15개씩을 주기로 반복되고 있습니다(당연하겠지요 ^^;; 아마도 짖궂은 코더는 여기서 아이디어를 착안했겠지요).


이제, 위 표 오른쪽 칼럼의 t[]의 인덱스를 보니 {0, 0, 1, 0, 2, 1, 0, ...} 이렇게 되는데, 2자리의 2진수로 바꿔서 다시 나열해 보면 {00, 00, 01, 00, 10, 01, 00, ...} 이렇게 됩니다.


위의 (3)에서 보이는 2진수를, 오른쪽에서부터 11를 제외하고 숫자를 2개씩 묶어서 나열해 보면 정확히 맞아 떨어지지요.


이제 감이 조금씩 잡히시나요? 매직넘버로 보였던 19142723 이라는 숫자가, 사실은 미리 관찰된 패턴에 의해 나오는 t[]의 인덱스 값을 오른쪽(가장 낮은 값에는 11을 채우고) 에서부터 왼쪽으로 채워나가서 만들어지는 2진수를 10진수로 표현한 값이라는 얘기입니다. 한 마디로 줄이면, 솔루션이 아니라 '사기' 입니다.


이해를 돕기 위해 아래 C++ 프로그램을 작동시켜 보시기 바랍니다. 말로 설명하는 것보다 프로그램을 보고 실행해 보는 것이 더 직관적으로 이해가 잘 될 수 있을 것 같습니다.

#include <iostream>

using std::cout;
using std::endl;

void fizzbuzz7(int _n)
{
    static const char *t[] = {"%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n"};

    for(int i = 1 ; i <= _n; i++)
        printf(t[3 & (19142723 >> ((2 * i)%30))], i);
}

// 10진수를 2진수 값으로 출력
void binary_cout(int _n)
{
    if( _n <= 0)
        return;

    binary_cout(_n >> 1);

    cout << _n % 2;
}

int main()
{
    // Magic number 19142723는 2진수로 00 00 01 00 10 01 00 00 01 10 00 01 00 00 11 (2자리씩 끊어서 표기)
    int x, y;

    // fizzbuzz7 과 같은 로직에 해설을 추가하여 다시 써 봅니다
    for(int i = 1; i <= 100; i++) {
        cout << i << ": Magicnumber 를 " << (x = (2 * i) % 30) << "회 Rshift -> ";
        binary_cout(y = (19142723 >> x));
        cout << " 이 되고 이것을 0x11 과 bit and를 하면: " << (3 & y) << " 이 되며, 이것은 배열의 index." << endl;
    }

    return 0;
}



누가 작성했는지 모르지만 이 프로그램의 코더는 분명 일반인들의 허를 찌르는 센스를 가진 사람일 것 같습니다. 틀에 얽매이지 않는 자유로운 영혼, 저도 이런 분들을 좋아하고 또 본받으려고 노력합니다. 물론 프로그래머로서의 목표의식과 인성이 갖추어져 있다는 전제하에서.


발칙하다는 것은 기발하고 독창적이라는 말과 비슷합니다. 하지만 또 어떻게 보면 사회성이 떨어지고 규범을 지키지 않을 것 같은 부정적인 느낌을 주기도 합니다. 위의 솔루션은 사실 예상되는 답의 패턴에 문제해결 과정을 끼워 맞춘 것으로, 엄밀하게 말하자면 문제해결에 적합한 코딩은 아닙니다. 즉 나누는 수를 3과 5에다가 새로운 수인 7을 하나 더한다든지 하게 되면, 예상되는 결과로부터 패턴을 추출하여 프로그램 내의 매직넘버를 다시 계산하고 t[]의 index 를 계산하는 식도 다시 써야 하니까요.


또 이 솔루션은 문제를 출제한 사람을 사실은 조롱하고 있다고 생각할 수 있습니다. 하지만 그 기발한 방법으로 보아 좋은 직관력을 가졌다고 생각되며, 동적계획법이든 뭐든 적당한 훈련 과정을 거친 사람이고 문제해결을 위한 코딩의 감각이 어느 정도 갖추어져 있는 사람임은 분명해 보입니다.


이전 글: 

2015/12/07 - [Technical/Development] - [프로그래밍] Fizzbuzz 문제에 대하여(1)



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


Fizzbuzz(피즈버즈) 문제. 프로그래머라면 한 번 쯤 풀어 보거나 들어본 경험이 있을지도 모르겠다. 만약 프로그래머로서의 직업을 가지려고 하거나, 단순한 취미로라도 "나 프로그램 좀 짠다" 라는 말을 할 수 있으려면 꼭 접해 보았어야할 문제다.


만약 Fizzbuzz 문제를 처음 듣거나, 예전에 들었는데 가물가물한다...하는 분이라면 이참에, 다시 한 번 스스로를 돌아보는 계기를 마련해 보자. 이건 글을 쓰는 본인에게도 해당하는 말이 될게다. Solid programming이나 Grok coding는 수 많은 고민과 노력에 의해 충분히 만들어 질 수 있다고 나는 믿는다. 중요한 건 엔지니어로서의 동기, 자부심 또는 열정 아니겠는가?



Fizzbuzz 문제가 뭐임?


"Fizzbuzz questions"는 영국의 아이들이 학교에서 하게되는 일종의 놀이다(를 통한 동기 또는 흥미 유발을 위한). 해외의 학교에서는 놀이식으로 수업을 진행하는 경우가 많은 편인데, 우리 것과 궂이 비교하자면 일종의 369 게임과 비슷할까?


어쨌든 Fizzbuzz 문제를 프로그래머 채용 인터뷰시에 내 보았더니 대다수의 우수한 프로그래머의 경우 2분 이내로 답을 내어야 함에도 대부분의 컴퓨터과학 전공학과 졸업생들이 문제 자체를 제대로 풀지 못했고, 심지어 좀 한다는 시니어 프로그래머들도 솔루션을 내는데 10~15분이 걸렸다고 한다.


[조금 오래 된(2007~) 관련 페이지들]

프로그래머들이 왜 프로그램을 못짜?(CodingHorror)

Fizzbuzz 에 대해 너무 깊게 생각하지 말라(RaganWald)

Fizzbuzz로 코딩 잘하는 개발자 찾기(Imran)

Fizzbuzz still works(GlobalNerdy)



Fizzbuzz 문제를 활용하는 이유


"소프트웨어를 개발하다", "프로그램을 짜다" 는 말을 보통 "코딩한다" 라고 표현한다. 코딩을 잘한다는 건 무엇일까? "최소한의 시간에 주어진 문제를 정확히 해결하는 프로그램을 잘 만들 수 있는가" 라는 질문에서 어느 정도 답을 찾을 수 있겠지만 문제가 그리 간단하지만은 않다. 


결과가 빨리 나왔다고 해서 Sold 한 프로그램이라고 단정지을 수는 없으며, 코딩 이전의 설계와 코딩 이후의 추후 확장이 필요한 소프트웨어의 특성상, 전체적인 구조에 대한 설계에 충분한 시간과 공을 들여야 할 수도 있다. 또 결과에 오류가 없을 뿐 아니라, 요건 변경에 대비하여 확장성이 충분해야 하는 솔루션이 필요할 수도 있기 때문이다. 그렇다면 Fizzbuzz 문제 같은 간단한 코딩테스트를 전화 또는 대면인터뷰시에 사용하는 이유는 무엇일까?


위의 관련페이지들의 내용을 찬찬히 훑어 보면 답은 명확하다. 바로, 코딩을 잘하는 사람을 뽑기 보다는 코딩을 못하는 사람 즉, 최근 몇 개월간 코딩을 직접 경험해 보지 못한 사람, 코딩을 잘 하려는 노력이나 고민을 게을리 하는 사람을 가려내어, 채용 회사와 면접자의 불필요한 시간 손실을 일단 줄여보겠다는 단순한 의도라고 짐작할 수 있다.



Fizzbuzz 문제와 솔루션들


[문제(영어 원문)]

Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".

우리 말로 풀어서 쓰면, 1부터 100사이의 숫자를 프린트하는 프로그램을 작성하는데 3의 배수이면 "Fizz"를, 5의 배수이면 "Buzz"를, 둘 모두의 배수 즉 15의 배수이면 "FizzBuzz" 를 프린트하도록 하라.


문제는 한마디로 간단하고 명확하다. 인터뷰시에 해당되는 중요한 얘기인데, 만약 문제에 구체적인 제약사항이 필요하다면 적극적으로 면접관에게 질문하고 상의하라. 문제가 요구하는 답은 다음과 같은 모양일 것이다.


1

2

Fizz

4

Buzz

.

.

또는

1 2 Fizz 4 Buzz Fizz 7 ...


본인이 스스로 능력있는 코더라고 자부한다면 2분 내외로 실제로 동작하는 프로그램 코드가 완성되어야 한다. 실제로 면접시 제시되는 방법은 다양하다. 전화인터뷰시에 말로 설명해야 될 수도 있고, 화이트보드에 손으로 코딩하거나(손코딩 & 눈디버깅 & 뇌컴파일) 또는 실제로 코딩할 수 있는 노트북이나 작업 PC가 주어질 수도 있다. 그때 그때 상황에 따라 잘 대처해야 하겠다.


만약 한 번도 이런 퀴즈나 문제를 접해본 적이 없는 응시자하면 적잖이 당황하게 될 것이다. 학교나 학원의 교수나 선생님들은 이런 문제의 해결법을 따로 가르쳐 주지 않기 때문이고, 그 해법이 너무나 다양할 수 있기 때문일 것이다. 또 결과는 언뜻 단순해 보이지만 해결 방법이 그리 썩 단순하지는 않으며, 뭔가 트릭을 쓰면 '쌈빡'한 방법으로 해결될 듯도 하기 때문에 더 머리 속이 복잡해 질지도 모른다.


[참고]

오메가: 수학귀신들의 잡학사전 - FizzBuzz 테스트

c2.com - Fizz Buzz Test


위의 c2.com 과 같은 사이트의 페이지들에는 다양한 개발 언어로 된 샘플 소스들이 많이 올라와 있다. 또 구글링을 통해 검색해 보면 다양한 Fizzbuzz 솔루션들을 접해 볼 수 있다. 참고로 위의 관련페이지들에 링크로 소개된 Imran의 사이트의 페이지에는 최근까지도 새로운 댓글들이 올라오고 있는 실정이다.


printf("1\n");

printf("2\n");

printf("Fizz\n"); ...


이런 답들도 있기는 하다 ^^;; 심지어...


for(...) {

    if(if i == 1) printf("1\n");

    else if ...


이런 답도 있다 ㅠㅠ.


본 글에서는 가장 직관적이고 보기 쉬운(평범한 필자의 수준에서 직접 작성한 것이기에) 대표적인 샘플 몇 개와 장난기 섞인 것들 몇 개를 소개한다. 주로 C/C++과 javascript, Python 으로 작성된 것들을 소개해 둔다.


글 맨 마지막에는, 다음글에 연속으로 게재될 엽기적인 답안 하나를 제시해 놓으려 하니 잘 읽어두시고 한 번 실행해 보기를 권한다.


Example 1, 2, 3 은 C++ 이지만 cout 만 제외하면 C 에서도 그대로 적용 가능하며, Example 4, 5, 6은 C의 printf 함수와 삼항조건연산자등을 사용하는 트릭들이 들어 있다.



C++ Example 1 : 2분만에 풀기에 딱 좋은 단순한 구성

#include <iostream>

using std::cout;

void fizzbuzz1(int _n)
{
    for(int i = 1; i <= _n; i++) {
        if(i % 15 == 0)
            cout << "fizzbuzz";
        else if(i % 3 == 0)
            cout << "fizz";
        else if(i % 5 == 0)
            cout << "buzz";
        else
            cout << i;
        cout << endl;
    }
}

int main()
{
    fizzbuzz1(100);
    return 0;
}


(아래 부터는 main()을 제외한 처리 함수 부분만 표기한다)


C++ Example 2 : 첫 번째 솔루션은 뭔가 비교조건이 중복되게 느껴진다. 그렇다면...

void fizzbuzz2(int _n)
{
    int check_more = false;

    for(int i = 1; i <= _n; i++) {
        if(i % 3 == 0) {
            cout << "fizz";
            check_more = true;
        }
        if(i % 5 == 0) {
            cout << "buzz";
            check_more = true;
        }
        if(!check_more)
            cout << i;
        else
            check_more = false;
        cout << endl;
    }
}


C++ Example 3 : 역시 첫번째 솔루션에 대한 약간의 변형

void fizzbuzz3(int _n)
{
    for(int i = 1; i <= _n; i++) {
        if(i % 3 == 0)
            cout << "fizz";
        if(i % 5 == 0)
            cout << "buzz";
        else if(i % 3 != 0)
            cout << i;
        cout << endl;
    }
}


C++ Example 4, 5, 6 : 평범하게 풀기 싫다면 약간의 C 언어만의 트릭을 써 보자

void fizzbuzz4(int _n)
{
    for(int i = 1 ; i <= _n; i++) {
        if(i%3 && i%5) printf("%d", i);
        printf("%s%s\n", i%3 ? "" : "fizz", i%5 ? "" : "buzz");
    }
}
void fizzbuzz5(int _n)
{
    // printf 함수는 출력한 문자의 갯수를 return 한다
    // 연산자 우선순위에 주의한다
    int i = 0;

    while(i++<_n)
        (i%3 || !printf("Fizz")) * (i%5 || !printf("Buzz")) && printf("%d",i), printf("\n");
}
void fizzbuzz6(int _n)
{
    char i = 0, n[3];
    while(i++ < 100)
        printf("%s%s%s\n", i%3 ? "":"Fizz", i%5 ? "":"Buzz", (i%3&&i%5&&sprintf(n, "%d", i)) ? n : "");
}


Javascript Example 1 : 참 단순 명료한 javascript !

for (var i=1; i<=100; i++) {   
    console.log( ((i%3 ? '':'Fizz') + (i%5 ? '':'Buzz') || i) )
}


Python Example 1 : 위의 일반적인 경우보다는 좀 더 Pythonic 하게 작성해 보자

FizzBuzz 와 같은 2가지 경우가 아니라 n개의 확장된 case에 대해서도 쉽게 처리가 가능한 방식을 구현할 수 있다.

# -*- coding:utf-8 -*-

# 각각의 나누는 수는 고유의 이름을 가진다. 이를 divisor_pairs(divisor, name) 이라는 튜플로 설정한다.
# 1~n 까지의 각각의 수(i)에 따라,
#   ... 나누는 수(divisor) 의 name이 있으면 프린트하고
#   ... 없으면 숫자 자체를 프린트한다

divisor_pairs = [
    (3, "3으로나누어짐"),
    (5, "5로나누어짐"),
    (7, "7로나누어짐")
]

for i in range(1, 501):
    # i에 대해 나누어 떨어질 경우, name 으로 스트링을 만든다
    name_string = "".join(name for (divisor, name) in divisor_pairs if i % divisor == 0)
    # name_string 을 프린트한다. 만약 빈문자열이면 i 를 프린트한다
    print(name_string or i)



다음 2회에서 분석해 볼 엽기적 솔루션

[솔루션이 소개된 원문 페이지 보기FizzBuzz and bored programmers ...아우~ 이걸 채용해야 하나?

void fizzbuzz7(int _n)
{
    static const char *t[] = {"%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n"};

    for(int i = 1 ; i <= _n; i++) {
        printf(t[3 & 19142723 >> 2 * i % 30], i);
    }
}


다음 글: 2015/12/07 - [Technical/Development] - [프로그래밍] Fizzbuzz 문제에 대하여(2)



- Barracuda -

저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


자주 만나는 헷갈리는 우리말 표현들 모음 II

(지난 관련 글-2012/12/13-에 이어서 추가할 만한 것들을 별도 포스팅으로 정리)




[~ㅓ, ~ㅔ, ~ㅐ, ~ㅖ]


1. ~건대, ~컨대

동사 또는 형용사 '~하다'와 '~건대' 가 합해지는 과정에서 'ㅏ' 가 빠지고 'ㅎ'이 남는 경우에 'ㄱ'과 결합하여 거센소리로 '~컨대' 가 됩니다. 따라서 "단언컨데", "단연컨데" 아니고 "단언컨대" 가 맞습니다. 당연히 "요컨데" 아니고 "요컨대" 가 맞습니다.


2. 요새

"요즈음" 이라는 뜻으로 쓰일 때는 "요세" 아니고 "요사이" 즉 "요새"가 맞습니다.


3. 재작년

"2 년 전" 이라는 의미로 쓰일 때에는 "제작년" 아니고 "재작년" 이 맞습니다. 1 년 전은 "작년".


4. 도대체

"도데채", "도데체" 아니고 "도대체" 가 맞습니다.



받침


1. "오랜만이다"

"오랫만" 아니고 오랜만


2. "앳되다"

"애띤 모습" 아니고 "앳된 모습"


3. "무난하다"

"문안하다" 아니고 "무난하다", 어렵지 않다(한자어)는 뜻.


4. '빈털털이'

소리 나는대로 '빈털터리' 라고 씁니다.


5. "건드리다"

"건들이다" 아니고 "건드리다". "건들거리다" 와 헷갈리면 안됩니다.


6. "널찍하다"

"넓직하다", "넓찍하다" 아닙니다. "너르다", "너그럽고 크다" 와 연관지어 둡니다.


7. '뒤치다꺼리'

'뒤치닥거리' 아닙니다. 비슷하게 헷갈리는 말로 '푸닥거리', '일거리', '마수걸이' 가 있습니다. '푸닥거리' 는 '푸다' 의 어원이 명확하지 않기 때문에 '푸다꺼리' 로 소리나는 대로 써야 한다는 설도 있습니다만, 현재 표준어 사전에는 '푸닥거리' 만 인정하고 있습니다.


8. '굳이'

"그걸 구지 말해야 하나"에서 '굳이' 로 씁니다. 이건 소리 나는대로 아닙니다. 


9. '구시렁'

"궁시렁거리다" 아니고 "구시렁거리다"


10. '움큼'

"한 움큼 거머쥐다". '웅큼' 아닙니다.


11. "닦달하다"

"닥달하다" 아닙니다. "닦다" 와 연관지어서 기업합니다. "남을 단단히 윽박질러서 혼을 내다" 또는 "물건을 손질하고 매만지거나 다듬다"의 뜻입니다.


12. "더욱이", "일찍이"

부사에 '~이' 가 붙어서 역시 부사가 되는 경우에 어근이나 부사의 원형을 적는 맞춤법의 원칙이 있습니다. 따라서 "더우기" 아니고 "더욱이", "일찌기" 아니고 "일찍이". 마찬가지로 "곰곰이, 생긋이, 오뚝이, 히죽이" 를 연관 짓습니다.


13. '구레나룻'

'구렛나룻', '구렌나루'  아니고 '구레나룻'



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들



청와대가 보유한 조잡하고 치졸한 인터넷 도메인들


지난 2011년에 한겨레신문에 실린 웃픈 기사가 생각났다. 청와대가 "쥐박이.com , 쥐박이.kr, 쥐박이.net, 쥐박이.org. 명박이.kr..." 같은 한글키워드 도메인들을 2010년에 구매하여 보유하고 있다는 기사였다(기사 보기). 그렇다면 박근혜정부는 어떨까 싶어 몇 가지 키워드와 도메인들을 검색해 보았다. 다음을 보자.



우선 대표적인 한글키워드, '닭그네' 로 후이즈를 검색해 보았다. 예상했던 대로 닭그네.kr, 닭그네.com, 닭그네.net 3개의 도메인은 대통령비서실(postmaster@president.go.kr) 소유로 되어 있다. 나머지 "닭그네.한국" 한글도메인은 '미스터 jeon' 이라는 개인이 등록, 보유하고 있는 것으로 보인다.



도대체 뭐하러 이런 도메인을 세금을 들여서 보유하려 한 것일까? 내가 낸 세금으로 무언가를 차단하고 가리며 통제하려는 치졸한 수작을 보니, 참으로 답 없게 어리버리한 사람들이라는 생각이 든다. 조만간 소유자 정보가 비공개로 전환될지도 모르겠다.



대통령비서실이 보유한 국제인터넷 도메인들을 보니


다른 키워드 몇 개로 더 검색해 보려다 이게 뭐냐 싶어서 그만 두려던 차에, 리버스후이즈로 검색을 시도해 보기로 했다. 즉, 대통령비서실 명의로 등록된 국제 도메인을 찾아 보는 것이다. 방법은 간단하다. domainbigdata.com 에서 해당 이메일 소유자가 보유한 도메인 목록을 볼 수 있기 때문이다(검색해 보기).


등록 정보는 Office Of The President, Korea, Republic Of... 로 나오고, 결과를 보니 등록자(Registrant)가 "office of the president"로 되어 있는 인터넷 도메인이 총 89개로 보인다(목록 보기). 소유자명으로 검색을 했기 때문에 위에서 검색된 결과보다 더 많고, 이들 모두가 청와대 소유로 보이지는 않는다(대다수 나머지 잡다한 도메인들은 Private 처리가 되어 실 소유주를 확인할 수가 없다). 하지만 뭔가 의심스럽기는 하다.



예상했던 대로다. antibakgeunhye.com, antigeunhye-bak.com 등 "안티 박근혜" 의미로 조합할 수 있는 10개 가량의 국제 도메인 주소들이 보인다. 거참, 기왕에 보유할 거면 com, net 외에도 us, io, tv 같은 인기 있는 TLD들도 다 사버리지 그랬니?


쥐박이.com 이나 닭그네.com 이나 피같은 세금 엉뚱한 데 쓰는 작태는 여전하구나.



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들



개인용 wiki 사이트, git 서버용으로 쓰던 Amazon EC2 vm에 고정 ip인 소위 Elastic IP를 물리고 인터넷 도메인(doubleshot.io)을 할당해서 쓰고 있다. git 서버의 접속 주소가 바뀌었으니 당연히 그에 맞게 git 의 remote 설정을 바꿔야 해서, 그 과정을 샘플  노트 형태로 작성해 둔다.

* git repository 에 해당하는 디렉토리(MyCppProjects)로 이동하여 변경 이전의 접속 주소를 "git remote" 로 확인
* 접속 방식은 기존 pem 인증키를 이용한 ssh 방식이므로 .git/config 파일의 접속주소 중 도메인 부분을 새로운 도메인(doubleshot.io)으로 교체하고 저장
✔ ~/MyCppProjects [master L|✔] 
22:16 $ git remote -v
wttest ssh://gituser@ec2-52-69-153-237.ap-northeast-1.compute.amazonaws.com/home/gituser/repos/MyCppProjects.git (fetch)
wttest ssh://gituser@ec2-52-69-153-237.ap-northeast-1.compute.amazonaws.com/home/gituser/repos/MyCppProjects.git (push)
✔ ~/MyCppProjects [master L|✔]
22:16 $ vi .git/config
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "wttest"]
        url = ssh://gituser@doubleshot.io/home/gituser/repos/MyCppProjects.git
        fetch = +refs/heads/*:refs/remotes/wttest/*


* 변경된 주소를 확인하고 push 를 실행하여 git 서버와 동기화 진행

✔ ~/MyCppProjects [master L|✔] 

22:26 $ git remote -v

wttest ssh://gituser@doubleshot.io/home/gituser/repos/MyCppProjects.git (fetch)

wttest ssh://gituser@doubleshot.io/home/gituser/repos/MyCppProjects.git (push)

✔ ~/MyCppProjects [master L|✔] 

22:26 $ git push wttest master

Everything up-to-date

✔ ~/MyCppProjects [master L|✔] 

22:31 $


- Barracuda -



저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들



2013년 12월 우리의 고개를 끄덕이게 만들었던 '독재, 1.9' 는 수구꼴통들의 지배력 유지 전략에 해당하는 핵심을 다음과 같이 지적했다. 자신들의 권세와 노략질을 위해 무언가를 망치는데는 이골이 난 놈들에게 이 4가지 꼼수는 손오공이 여의봉 다루듯이 손쉽게 느껴졌을 것이다.


  • 반공주의=레드컴플렉스=매카시즘
  • 지역주의
  • 민주세력의 분열과 배신
  • 권언유착




그들이 노리는 5번째 꼼수는 '역사교과서 국정화'


이제 그로부터 2년이 지난 지금, 닭과 십상시 & 주변의 날파리들은 그들의 치명적인 약점이 될 수도 있는 친일/독재에 대한 흔적을 말소하기 위해 '올바른 역사'와 '애국' 이라는 미명하에 역사교과서 국정화를 비밀리에 진행해 왔고, 그 노골적인 대쉬가 시작됐다. 이 치졸한 짓의 성공은 그들이 원하는 '독재, 2.0'의 완성을 위한 방점이 될 것이다. 유신회귀를 그 바탕에 깔고......


역사교과서 국정화는 진정한 '독재, 2.0'의 시작이고, 이거야말로 민주주의의 패배다. 거창한 말 싹다 빼고, 국정화된 역사로 인해 미래에 당신의 아이 중 몇몇은 바보가 되거나 소시오패스로 자라날 지도 모를 일이다. 마치 '병든 닭'처럼 말이다.


전체 흐름을 다 보면 그런 기운이 온다. 꼭 찍어 먹어 봐야 똥이고, 마셔봐야 시안화칼륨인지 아는가?


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들




Buzzword 의 의미


Buzzword 는 buzzing(윙윙거리는, 소음), buzz(떠나다, 꺼지다)의 'buzz'와 'word' 합성어로, 단어게임의 일종이기도 하며 buzzing word, buzz word 라고 쓰기도 하지만, 현재는 거의 buzzword 로 붙여서 쓴다. Buzz 는 "Just buzz off!"(꺼져 줘) 같은 말에서처럼 부정적인 의미도 있고. "Buzz me(=Call me)" 와 같이 일상적인 뜻도 들어 있어서 한 마디로 정의 내리기 어려운 용어이다.


이해하기 쉽게 풀어서 쓴다면  (잘난체 하는 사람들이 즐겨 쓰는) 유행어 정도이지만 실제로는 유행어, 신조어, 시사용어, 전문용어, 화두 등의 의미를 지니는 일반화된 용어로 주로 쓰인다.  이 단어의 속성 자체가 "명확한 합의와 정의가 없는 용어이며, 명확하게 정의된 용어도 본래의 뜻을 벗어나서 사용되면 버드워드로 간주한다"(위키피디아:한글판)[각주:1]라고 정의되고 있다. 글을 쓰는 시점까지의 조사된 바로, 필자 본인이 간단히 설명한다면 "(언론 등을 통해) 주로 일시적으로 유행하는 기술적 용어" 정도가 아닐까 싶다. 일종의 레토릭(말의 기교, 미사여구)이라고 볼 수 있으며, 위의 그림에서 처럼 버즈워드는 그 자체가 버즈워드이기도 하지만 이 말 만큼은 쉽게 사라질 용어는 아닌 듯 하다.


Buzzword 의 유래


Dictionary.com 에서는 "1946년경 또는 그 이후, 하버드 대학생들이 강의나 세션에서의 키워드를 일컬어 buzz word 라는 slang 을 썼다" 라고 유래[각주:2]를 적고 있으며(학생들이 모여서 왁자지껄하게 떠드는 모임의 소음이 연상된다)용어의 범람에 질색하는 사람이 보기에는 위에 적은 것 처럼 부정적인 시각으로 비칠 수도 있고, 실제로 그러한 용어 범람에 의한 혼동을 우려하는 시각도 적지 않다.



Adobe 에서 만든 버즈워드 디텍터 코믹 영상. 찌릿~찌릿~



실제로 마케팅, 비즈니스 강연 등에서 화두가 된 키워드들이 언론을 타고 새로운 버즈워드로 탄생하는 경우가 많기 때문에 그 분야의 전문가들은 무시하는 경향이 많지만, 빅데이터(Big data) 처럼 구전이 반복되면서 갑자기 대세를 이루는 단어로 부각되는 경우도 허다하다. 심지어 한국정보통신기술협회의 용어사전에서는 "검색 엔진을 이용하여 정보를 찾을 때 검색에 도움을 주지 못하는 단어, 즉 쓸데 없는 말" 이라고 정의[각주:3]하기까지 한다(영문 wikipedia.org의 정의[각주:4]와 거의 유사).


The Wallstreet Journal 은 "Business Buzzwords Generator" 라는 버즈워드 생성기를 서비스하고 있으며, Patch Products 는 Buzzword bingo 라는 보드게임을 선보인 바 있다. Merriam-Webster 는 "Daily Buzzword' 섹션을 별도로 갖추어 서비스하고 있기도 하다(보기). 국내의 닐슨코리아는 각종 빅데이터로부터 키워드베이스의 관심도, 성향 분포, 순위 조사 등의 인터넷 분석 서비스를 하고 있다(보기).



Buzzword, 어떻게 받아 들여야 하나?


여기서, 과연 버즈워드를 누군가처럼 부정적이고 냉소적인 시각으로만 볼 것인가에 대해 한 번 쯤 생각해 볼 필요가 있다. 지나친 남용에 의한 의미의 혼동에 대해서는 지혜로운 현대인의 입장에서 경계해야 마땅하지만, 굳이 까탈스럽게 시대의 흐름에 역행하면서까지 과도한 경계를 하는 것은, 오히려 개인의 스트레스의 주범이 되거나 본인만의 생각의 우물에 빠져 허우적대게 될 수도 있을 것 같다.


차라리 버즈워드를 재미와 흥미를 유발하는 창의적 요소로 인식하는 것이 더 삶을 긍정적으로 이끌지 않을까? 이미 많은 스타트업 창업가들이 버즈워드를 이용해 기업의 이름을 작명하고 있고, 새로운 기술 서적들의 책 제목으로, 새로운 웹사이트들이 버즈워드를 화두로 지식을 정리하며 생각을 공유하고, 비즈니스모델을 만들어 미래를 펼쳐가고 있는 추세가 현실이 되었기 때문이라는 생각을 해 보게 된다.



- Barracuda -





  1. 한글 위키피디아 정의: https://ko.wikipedia.org/wiki/%EB%B2%84%EC%A6%88%EC%9B%8C%EB%93%9C [본문으로]
  2. buzzword의 유래: http://dictionary.reference.com/browse/buzzword [본문으로]
  3. 한국정보통신기술협회 정의: http://word.tta.or.kr/terms/termsView.jsp?gubun=1&terms_num=11446 [본문으로]
  4. wikipedia.org 의 정의: https://en.wikipedia.org/wiki/Buzzword [본문으로]
저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들



ungit은 git 사용자를 위한 유용한 GUI 브라우저이다. ungit 은 node.js 상에서 동작하므로 이참에 node.js 를 source로부터 build하고 ungit 을 설치하는 과정을 메모해 둔다.




1. tarball 소스로부터 node.js 빌드 & 설치


bryan@bryan-laptop1:~/Downloads$ wget https://nodejs.org/dist/v0.12.7/node-v0.12.7.tar.gz

bryan@bryan-laptop1:~/Downloadssudo apt-get install build-essential python-dev

bryan@bryan-laptop1:~/Downloadstar xvzf node-v0.12.7.tar.gz

bryan@bryan-laptop1:~/Downloadscd node-v0.12.7/

bryan@bryan-laptop1:~/Downloads/node-v0.12.7./configure

bryan@bryan-laptop1:~/Downloads/node-v0.12.7make

bryan@bryan-laptop1:~/Downloads/node-v0.12.7sudo make install

bryan@bryan-laptop1:~/Downloads/node-v0.12.7node --version

v0.12.7


2. ungit 설치 및 테스트


bryan@bryan-laptop1:~sudo npm install -g ungit

bryan@bryan-laptop1:~$ cd git/bumblebee-ui/

✔ ~/git/bumblebee-ui [master ↓·4|✔] 

10:36 $ ungit



<관련 글 보기>

2015/08/03 - [Git Tip] Git Branch와 상태를 보여주는 Linux Prompt(bash-git-prompt)

2015/07/31 - [Git Tip] Git 브랜치를 보여주는 Linux 프람프트(prompt) - Ubuntu 14.04, bash 기준

2015/07/24 - [Git Tip] Git에 대한 궁금증들

2015/07/23 - [Git Tip] AWS EC2 VM을 이용한 Git 서버설정과 git 기본 사용법


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


지난 번 Git 을 위한 Linux Prompt 변경 내용에 추가하여, Github.com 프로젝트 중에서 쓸만 한 것이 있어서 소개해 두도록 한다. 단순히 Branch 명을 보여 주는 것에서 벗어나서 브랜치의 자세한 상태까지 Prompt 에서 보여 주므로 아주 실속 있는 Git용 프람프트 유틸리티가 아닐까 한다. 설치 과정도 아주 간단하여 쉽게 적용해 볼 수 있다.


* 적용 대상: Bash를 사용하는 Linux 또는 Mac

* 설치 방법: https://github.com/magicmonty/bash-git-prompt 에 있는 내용 참조


* 설치 과정(Ubuntu 14.04, Bash 사용)

bryan@bryan-XenPC:~cd ~

bryan@bryan-XenPC:~git clone https://github.com/magicmonty/bash-git-prompt.git .bash-git-prompt

bryan@bryan-XenPC:~$ vi .bashrc <== .bashrc 의 case "$TERM" ... 문 다음의 적당한 위치에 추가

...

# If this is an xterm set the title to user@host:dir

case "$TERM" in

xterm*|rxvt*)

    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"

    ;;

*)

    ;;

esac


# gitprompt configuration

# Set config variables first

GIT_PROMPT_ONLY_IN_REPO=1

# GIT_PROMPT_FETCH_REMOTE_STATUS=0   # uncomment to avoid fetching remote status

# GIT_PROMPT_START=...    # uncomment for custom prompt start sequence

# GIT_PROMPT_END=...      # uncomment for custom prompt end sequence

# as last entry source the gitprompt script

# GIT_PROMPT_THEME=Custom # use custom .git-prompt-colors.sh

# GIT_PROMPT_THEME=Solarized # use theme optimized for solarized color scheme

source ~/.bash-git-prompt/gitprompt.sh


# enable color support of ls and also add handy aliases

...

bryan@bryan-XenPC:~$ source .bashrc


* ~/.bash-git-prompt/themes 내에 테마파일을 설정해 두고 커스텀 테마를 지정해서 사용할 수도 있다

* 다음은 bash-git-prompt 프로젝트 홈에 소개 된 프롬프트 심볼들에 대한 간단한 설명들이다


[Local Status Symbols] 로컬브랜치 상태 심볼들

    ✔: repo가 더 이상 건드릴 부분 없이 clear 함

    ●n: n개의 staging된(index 에 add되었지만 commit은 되지 않은) 파일들이 있음

    ✖n: n개의 merge 되지 않은 파일들이 있음

    ✚n: n개의 수정되었지만 staging되지 않은(add가 필요한) 파일들이 있음

    …n: n개의 트래킹 되지 않은 파일들이 있음

    ⚑n: n개의 스태쉬(임시 저장소)가 존재함

[Branch Tracking Symbols]리모트저장소와 비교된 트래킹 심볼들

    ↑n: 리모트저장소보다 n개의 commit 단계가 앞서 있음(push 가 필요한 상태 암시)

    ↓n: 리모트저장소보다 n개의 commit 단계가 거슬러 내려가 있음

    ↓m↑n: 리모트저장소에 비해 브랜치가 갈라져 있음, 위 2가지가 복합됨

     L: 로컬브랜치만 존재함


[관련 글]

2015/07/23 - [Git Tip] AWS EC2 VM을 이용한 Git 서버설정과 git 기본 사용법

2015/07/24 - [Git Tip] Git에 대한 궁금증들

2015/07/31 - [Git Tip] Git 브랜치를 보여주는 Linux 프람프트(prompt) - Ubuntu 14.04, bash 기준


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


Git을 사용하는 방식은 명령형(커맨드라인; Command line; 또는 터미널 방식) 이거나 GUI Client 형(Mac, Windows)이거나 둘 중 하나일 것이다. Git의 내부 메커니즘을 알기 위해서이기도 하지만 커맨드라인 방식이 익숙해 지면 훨씬 수월해 지는 경우가 많은 듯 하다. 이 때 어쩔 수 없이 git status 를 쳐서 현재 어떤 브랜치에서 작업중인지를 수시로 확인해야 하는데, Linux 의 프람프트를 개조해서 사용하면 편리한 점이 많으므로 그 방법을 정리한다.


"Git 은 브랜치로 시작해서 브랜치로 울고 웃다가 브랜치로 끝난다" - Barracuda -


* 준비물: github 에서 공개된 아래의 스크립트를 계정의 Home에 내려 받아 둔다

* git-prompt.sh 는 Bash, Zsh 를 지원한다

bryan@bryan-XenPC:~$ curl -o ~/.git-prompt.sh https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh


* 홈 디렉토리의 .bashrc를 아래와 같이 수정하고 적용

* 기존의 프람프트에 영향, 변형이 없어야 하며, Git 으로 관리되는 작업 디렉토리에서만 프람프트가 자동으로 변경

bryan@bryan-XenPC:~$ vi .bashrc

...

# For Git prompt # <== Homedir에 .git-prompt.sh 가 있는지 확인하는 if문 추가

if [ -f ~/.git-prompt.sh ]; then 

    source ~/.git-prompt.sh 

fi


if [ "$color_prompt" = yes ]; then

    if [ -f ~/.git-prompt.sh ]; then # <== 원래의 PS1 설정을 위한 if 블록에 __git_ps1 변수 값을 끼워넣는 if 문 추가

        PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$$(__git_ps1 "(Branch:%s)") '

    else

        PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '

    fi

else

    if [ -f ~/.git-prompt.sh ]; then # <== 위와 마찬가지

        PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$$(__git_ps1 "(Branch:%s)") '

    else

        PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '

    fi

fi

unset color_prompt force_color_prompt

...


* 스크립트가 정상 작동하는지 디렉토리를 이동하면서 확인해 본다

~/git-test/gittest 디렉토리와 ~/PycharmProjects/PyTestProjects 는 git 으로 트래킹되는 working 디렉토리이다

bryan@bryan-XenPC:~$ source .bashrc

bryan@bryan-XenPC:~$ cd git-test/gittest/

bryan@bryan-XenPC:~/git-test/gittest$(Branch:develop) cd ../../kernel_src/

bryan@bryan-XenPC:~/kernel_src$ cd ../PycharmProjects/PyTestProjects/

bryan@bryan-XenPC:~/PycharmProjects/PyTestProjects$(Branch:master)


[관련 글]

2015/07/23 - [Technical/Development] - [Git Tip] AWS EC2 VM을 이용한 Git 서버설정과 git 기본 사용법

2015/07/24 - [Technical/Development] - [Git Tip] Git에 대한 궁금증들

2015/08/03 - [Git Tip] Git Branch와 상태를 보여주는 BASH-GIT-PROMPT



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


Git 을 다루는 엔지니어들이 점점 늘고 있다. 한글 입력상태에서 자판으로 git을 치면 '햣' 이 된다, 햣~! 너도 나도 써야한다라고 하니, 이게 마치 무슨 대세가 된 건 아닌가 착각도 하게 되는데, 막상 써보려니 손에 익고 간편한 cvs, svn 과는 비슷하면서도 뭔가 좀 다르고 어렵기도 하다.


근데, 간편안내서 같은 곳을 보면 "어렵지 않아요 ;)" 하면서 사람을 막 꼬드긴다(가서 보면 더 너무 쉽게 써놔서 더 아리송하다). 그게 대체 뭐길래...하면서 약도 좀 오르고 궁금하기도 하다. 이제, 하나 하나 따져가면서 왜 그런가 고민하고 정리해 두는 버릇이 있는 필자가 git을 한 번 다루어 보려 한다.



Git을 써야 하는 이유


결론부터 간단히 말하자면, Git 은 약간의 개념공부와 실습이 필요한 Tool 이다. cvs, svn 과는 달리 Git은 Distributed+VCS(버전관리도구) 이고 cvs가 하지 못하는 일을 가능하게 하기 때문이다. Linux의 주인공 리누즈 토발츠는 자신이 만든 Git을 소개하면서 cvs를 'devil'(악마...라기 보다는 죄악에 가깝다고 본다) 라고 까지 한다(아래 유튜브 영상 참고). cvs나 svn을 쓰면서 branch-merge하기 전에 풀백업을 받아 두는 엔지니어의 심정이라면 Git을 써야만 하지 않을까?



왜 Git을 써야 하는지에 대해 인터넷에 수많은 문서들이 올라오고 있지만, 이 페이지를 보고 좀 더 명료해 지리라 생각된다.

http://pismute.github.io/whygitisbetter/



아마도, 이 정도 개념만 잡고 개발팀에 투입 돼도 "이 친구 쫌 하는데!" 라는 시선을 받아 볼 수 있을런지도 모르겠다. 칭찬은 엔지니어에게 스팀팩을 놔주는 거 아닐까...



Git 에 관한 필수 용어와 관행(Convention) 또는 약속



master, branch와 origin


* Repository(저장소=줄여서 repo)가 만들어지면(git init) 최초로 유일하게 존재하는 브랜치가 master branch 이고 전체 저장소의 본류가 된다. 이 브랜치는 그 자체로 'master' 라는 이름을 가진다

* 'origin' 은 약속된 이름으로, 현재 push(원격 서버에 업로드) 또는 clone/pull(원격서버로부터 내려받음), Fetch(원격 repo내용 확인) 하려는 원격저장소(서버의 repo)의 기본(default) 이름이다

  - git clone git://remote.blah/test.git 을 하게 되면 자동으로 현재 디렉토리에 test 가 만들어지고 원격저장소의 이름은 'origin' 이 된다(-o 옵션으로 마지막 인수로 새로운 디렉토리명(예: test_dev)을 덧붙여서 다른 이름을 부여할 수도 있다)

  - 굳이 원격저장소의 이름을 origin 으로 하지 않아도 무방하지만, 프로젝트의 최초에 생성되는 원격저장소는 오리지널 이라는 의미의 'origin' 이란 이름을 가지도록 한다

  - origin 이 아닌 원격저장소의 이름은 git remote rename oldname newname , git remote rm name1 ... 이런 식으로 변경 또는 삭제가 가능하다. 또는 다른 저장소이름을 origin 으로 바꾸는 것도 물론 가능하다



작업 디렉토리, 로컬/원격저장소(repository), Index, Head


*작업디렉토리는 버전을 관리할 소스 파일들이 있는 곳이며, git init 으로 초기화한 디렉토리와 그 하위 디렉토리 전체가 관리 대상이다. 이 곳의 전체 또는 일부의 파일들은 git add 로 index라는 가상공간에 등록(staging 단계) 되며, Git에 의한 관리 대상이 된다. 수정 또는 새로이 추가된 파일은 항상 git add 명령으로 스테이징단계를 거쳐야 한다

* Staging 단계의 파일들은 git commit 명령에 의해 repo라는 가상 공간에 기록되며, commit 되는 각 단계의 Head[각주:1] 내용이 기록된 시간 순서대로 꼬리를 물고 늘어서 있는 구조이다. commit 시에 저장되는 내용은 파일 전체 내용이 아닌, 각 파일들의 변경사항들이 묶음으로 기록됨으로써 빠르고 가벼운 동작이 잘 유지된다.

* Git에서 repository는 로컬(development)과 원격에 각각 존재하므로, 개념을 잘 구분해서 써야한다


Git protocol


* Git이 원격저장소와 통신하는 방식은 4가지가 있다

  - 로컬 프로토콜: 로컬 디스크나 NFS로 연결된 저장소일 경우 file:///디렉토리/xxx.git 과 같은 방식으로 연결

  - SSH프로토콜: ssh를 통한 암호화된 통신 방식으로 ssh를 이용할 로그인 계정이 있을 때 사용할 수 있다. 공개용 프로젝트에는 부적합하다

  - Git프로토콜: SSH와 유사하지만 전송 속도가 빠르며 TCP 9418port를 사용한다.  별도의 인증수단이 없어서 Gitosis 와 같은 별도의 인증패키지를 사용해야 한다

  - HTTP/HTTPS 프로토콜: 의미 그대로 HTTP 또는 HTTPS 프로토콜을 그대로 사용하며, 방화벽에 크게 구애받지 않는다


Log 와 Commit Identifier


* Git으로 수행한 모든 commit들은 빠짐 없이 저장소에 기록되며, 각 commit 단계별로 유일한 Key(해시 키) 값(예: f6f9e... 처럼 생긴 16진수들) 이 관리된다.

* git log --graph 와 같은 명령으로 이 Key 값을 조회한 후 revert(되돌리기), cherry-pick(브랜치 가져오기), Tag(꼬리표 달기) 등의 명령을 수행하게 된다


Tag 달기


* Git에서 수행한 commit 단계(의 Key 값)에 대해 의미를 가진 꼬리표를 달아서 버전관리의 이정표를 구분하도록 한다

  - 예: git tag 1.0.0 edbbca65716825e770d1509e9359612260c08c73



Git 과 전문가들이 권고하는 관행(Convention)


reponame.git 형태로 repository 디렉토리명을 쓴다


유일한 오리지널 저장소(repo)인 경우 원격 저장소에 "origin" 이라는 이름을 붙여 쓴다


Branch 의 종류와 네이밍 관행


Vincent Driessen 이 제시하는 브랜칭 모델


backlogtool.com/git-guide 에서 정리한 브랜칭 모델. 위와 모델은 같으나 단순화 되어 눈에 더 잘 들어온다


* Main 브랜치: Master, Develop

* Supporting(Topic) 브랜치: Feature, Release, Hotfix

 - Feature 브랜치: Develop 에서 분기하는 일종의 Topic 브랜치(들), 완료되면 Develop에 병합(merge)한 후 Push

 - Release 브랜치: Develop 에서 릴리즈를 위한 분기(release-브랜치명), 완료되면 Master에 병합한 후 release 태깅

    배포 후 Develop에 병합

 - Hotfix 브랜치: 긴급 수정 작업, Master 에서 긴급 수정작업을 위한 분기(hotfix-브랜치명), 완료되면 Master에 병합, 배포 후 Develop에 병합


[참고]  이 브랜칭 모델이 모든 경우에 정답일 수는 없다. 프로젝트나 사업의 성격상, 어떤 경우는 여러 개의 Release가 고객사별로 브랜칭되어 동시에 병행/교차되어 진행되는 복잡한 구조가 더 적합할 수도 있다. 단, 관리의 복잡성으로 인한 부담이 너무 크지 않다면 말이다. 결국 Git 라는 Tool 자체의 자유도로 인하여, 기존의 SVN등으로 해결 못하던 많은 한계점들을 피해갈 수 있는 가능성이 생겼다는 것은 분명해 보인다.


도움이 되는 온라인 실습/강의/설명 자료


[한글로 된 친절한 문서 페이지들]

https://git-scm.com/book/ko/v2

http://backlogtool.com/git-guide/kr/

http://www.slideshare.net/einsub/svn-git-17386752

http://sapeyes.blog.me/70118257910

https://opentutorials.org/module/217

http://www.creativeworksofknowledge.com/2014/09/03/git-intro-and-common-workflow/



[영문으로 된 실습, 설명, 강의 페이지들]

http://pcottle.github.io/learnGitBranching/ - 한글(일부만) 지원

http://gitref.org/

https://www.atlassian.com/git/tutorials/

http://gitimmersion.com/index.html

http://wildlyinaccurate.com/a-hackers-guide-to-git/


[관련 글]

2015/07/23 - [Git Tip] AWS EC2 VM을 이용한 Git 서버설정과 git 기본 사용법


- Barracuda -

  1. 마치 카세트테이프의 헤드(또는 구식 LP 레코드의 바늘) 처럼, 재생되는 현재의 위치를 가리키는 개념이다. 그래서 merge 옵션에 보면 --no-ff (패스트포워드 하지마삼...) 같은 것도 있다. [본문으로]
저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


git(깃) 서버를 Amazon EC2 인스턴스에 설치하고, Repo를 운영 관리하는 기초과정 정리


* 준비해야 할 것들

 - 서버: AWS EC2 t2.micro, ubuntu 14.4, 접속주소: ec2-xx.amazonaws.com

 - 클라이언트: Ubuntu 14.4 PC, EC2 vm ssh 접속을 위한 보안 키파일(여기서는 AWSKP_as1.pem)


EC2 vm측, git 서버 설치 과정


* 필수 패키지 설치

root@aws-ubt14-as01:~# apt-get install git-core

root@aws-ubt14-as01:~# apt-get install openssh-server


* linux 계정(=gituser) 추가, 권한 설정 및 key pair 생성

* 이 방법은 git 계정을 공용으로 사용하는 경우로 사용자 개별 설정은 불가(사용자별 권한 설정 등 세부적인 관리가 필요할 경우 gitolite 와 같은 관리 패키지 사용 필요)

root@aws-ubt14-as01:~# adduser --home /home/gituser --shell /bin/bash gituser

root@aws-ubt14-as01:~# visudo <== 아래 line 추가

...

gituser ALL=(ALL) ALL

...

root@aws-ubt14-as01:~# su - gituser


* 생성된 Key pair의 공용(public)키 부분을 .ssh/authorized_keys 에 추가

gituser@aws-ubt14-as01:~ssh-keygen -b 1024 -f gituser -t dsa

gituser@aws-ubt14-as01:~cat gituser.pub >> .ssh/authorized_keys


gituser@aws-ubt14-as01:~chmod 600 .ssh/authorized_keys

gituser@aws-ubt14-as01:~sudo chown -R gituser.gituser .ssh/

gituser@aws-ubt14-as01:~$ mkdir repos; cd repos


* Remote client에서 사용할 작업디렉토리 생성, repo 설정

* 새로운 repo 가 필요해질 때마다 별도로 작업해 주어야 함

gituser@aws-ubt14-as01:~/repos$ mkdir ec2new.git; cd ec2new.git

gituser@aws-ubt14-as01:~/repos/testprj.git$ git init --bare --shared



Remote client(개발자 PC)측 git 설정


* 기존 ec2new 프로젝트를 Local PC에서 git 서버에 push(업로드&동기화)하는 과정까지...

* EC2 vm에서 생성한 key pair의 개인(private)키 파일을 local 로 복사해 와서 저장

* ssh-add 로 키 파일이 자동 적용되게 한 후, EC2 vm 의 gituser 계정에 ssh 로그인이 가능한지 확인

bryan@bryan-XenPC:~$ scp -i AWSKP_as1.pem root@ec2-xxx.amazonaws.com:/home/gituser/gituser ./gituser.pem

bryan@bryan-XenPC:~$ ssh-add /home/bryan/gituser.pem

bryan@bryan-XenPC:~$ ssh gituser@ec2-xxx.amazonaws.com


* 작업 디렉토리(프로젝트: ec2new) 가 있다고 가정

bryan@bryan-XenPC:~/git-test/ec2new$ ls -l

합계 12

-rw-rw-r-- 1 bryan bryan  6  7월 22 20:39 README

-rw-rw-r-- 1 bryan bryan 31  7월 22 20:39 a.c

-rw-rw-r-- 1 bryan bryan 51  7월 22 22:33 b.cpp


* 현재 디렉토리를 git repo 등록(초기화)

bryan@bryan-XenPC:~/git-test/ec2new$ git init


* git repo에 현재 디렉토리의 모든 파일 추가(tracking 가능=staging상태=index에 등록)

bryan@bryan-XenPC:~/git-test/ec2new$ git  add .


* a.c 파일을 수정하였다면 수정 사실을 git에 알린다(관리 대상으로 등록 의미)

bryan@bryan-XenPC:~/git-test/ec2new$ git  add a.c


* Commit 이전까지는 Staging 단계, commit 이되면 HEAD에 기록(= local에 commit)됨

* 즉, git에서 commit 은 local 에서의 최종 저장 단계

bryan@bryan-XenPC:~/git-test/ec2new$ git commit -m "1st commit"

[master 346b586] Modified a.c

 1 file changed, 1 insertion(+)


* Remote repo 주소를 git에게 알림

bryan@bryan-XenPC:~/git-test/ec2new$ git remote add ec2new ssh://gituser@ec2-xxx.amazonaws.com/home/gituser/repos/ec2new.git


* Remote repo 확인

bryan@bryan-XenPC:~/git-test/ec2new$ git remote -v

ec2new ssh://gituser@ec2-xxx.amazonaws.com/home/gituser/repos/ec2new.git (fetch)

ec2new ssh://gituser@ec2-xxx.amazonaws.com/home/gituser/repos/ec2new.git (push)


* Remote repo 에 업로드(최종 기록)

bryan@bryan-XenPC:~/git-test/ec2new$ git push ec2new master

Counting objects: 5, done.

Delta compression using up to 4 threads.

Compressing objects: 100% (2/2), done.

Writing objects: 100% (3/3), 341 bytes | 0 bytes/s, done.

Total 3 (delta 0), reused 0 (delta 0)

To ssh://gituser@ec2-xxx.amazonaws.com/home/gituser/repos/ec2new.git

   eaacfc7..346b586  master -> master

[관련 글]

2015/07/24 - [Technical/Development] - [Git Tip] Git에 대한 궁금증들


- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


MySQL이 Oracle 에 인수될 즈음부터 MySQL을 기반으로 하면서 보다 향상된 개념으로 각자가 이름을 떨치며 꾸준히 진행되어 온 프로젝트가 바로 Percona와 MariaDB이다. 참고로 두 프로젝트의 연관성을 비교하는 내용은 이 곳의 포스팅을 보시면 되겠고, 이번 포스팅에서는 Codership이 만든 Synchronous Multimaster 방식의 Galera cluster 를 설치해 보고, 운영에 관련해서 고려할 점들을 정리해 두려 한다.


Multimaster, Synchronous 한 특징을 가지는 이러한 MM 솔루션이 나오기 전에는(물론 완성도가 떨어지고 운영상 불편했던 MMM 같은 것도 있기는 했다), Master-Slave 구조의 비동기 Replication 방식이 많이 쓰였다. 한 편으로는 Semi-sync(Master는 변경 사항을 Slave 로 전달하는 것 까지만 책임을 진다)라는 장점을 가지기는 했지만 태생적으로 비동기식에서 벗어날 수 없기에, 만약의 상황에서 데이터 손실을 감수해야 하는 한계를 지녔었다고 볼 수 있겠다.


다뤄 나가고자 하는 내용을 간단히 요약하면 다음과 같다


* MariaDB Galera Cluster 설치 및 설정 과정

* 노드의 추가(확장)과 Maintenance를 위한 제거 등 운영 방법



1. MariaDB Galera Cluster 설치 및 설정 과정


설치 방법은 소스 빌드, 바이너리 다운로드&설치, rpm 설치 등 여러 가지가 있지만, 여기서는 mariadb.org 에서 권고하는 distro별 링크를 통해 단계를 밟아 나가는 내용을 그대로 따르면서 진행해 보자.


본 글에서 선택한 설치, 운영 환경: Ubuntu 14.04 Trusty, MariaDB 10, Kaist archive


* 설치 & 테스트를 진행할 3개의 Ubuntu 14.4 머신을 준비한다

 - ubuntu14-pv1, 10.0.10.1

 - ubuntu14-pv2, 10.0.10.2

 - ubuntu14-pv3, 10.0.10.3

* Local network이 아닌 외부 망을 통하여 노드간 원격 접속이 필요한 경우(AWS AZ 도 포함) 또는 서버 자체 방화벽(ufw 등) 이 설정되어 있을 때는, 방화벽 설정에서 TCP 3306/4568/4444 port와 TCP, UDP 4567 을 개방해야 한다(☞참조)



첫 번째 Cluster, Doner 노드의 설치와 기동


* 대다수 작업이 root 권한을 필요로 하므로 super user 로 로그인하여 진행한다

* 설치 과정에서 mysql 관리자 계정인 root 암호를 2번 입력(여기서는 편의상 maria 로 정한다)

* 첫 번째로 설정되어 기동되는 MariaDB 머신 도너(Doner) 노드라고 부르며, 다음과 같이 설정

* 여러 머신간의 데이터 동기화가 중요한 환경에서는, DB 데몬이 머신 부팅 후 자동 실행되는 방식을 피하는 것이 바람직

ubuntu@ubuntu14-pv1:~$ su -

root@ubuntu14-pv1:~# apt-get install software-properties-common

root@ubuntu14-pv1:~# apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xcbcb082a1bb943db

root@ubuntu14-pv1:~# add-apt-repository 'deb http://ftp.kaist.ac.kr/mariadb/repo/10.0/ubuntu trusty main'

root@ubuntu14-pv1:~# apt-get update

root@ubuntu14-pv1:~# apt-get install mariadb-galera-server

root@ubuntu14-pv1:~# mysql_secure_installation

root@ubuntu14-pv1:~# mysql -uroot -pmaria

MariaDB [(none)]> grant all on *.* to 'root'@'%' identified by 'maria';

MariaDB [(none)]> grant usage on *.* to sst_user@'%' identified by 'maria';

MariaDB [(none)]> grant all privileges on *.* to sst_user@'%';

MariaDB [(none)]> flush privileges;

MariaDB [(none)]> quit

root@ubuntu14-pv1:~# service mysql stop

root@ubuntu14-pv1:~# apt-get install sysv-rc-conf <== mysqld 의 자동 실행을 중지하기 위함

root@ubuntu14-pv1:~# sysv-rc-conf <== runlevel 2~5 에 대해 space 를 눌러서 해제


* 복제 방법으로 Percona 의 xtrabackup 을 사용하기 위함(rsync 를 선택할 경우 설치하지 않아도 됨)

* 이 경우 데이터스트림 전송을 위한 다용도 relay 솔루션인 socat은 꼭 설치해야 함

root@ubuntu14-pv1:~# apt-get install xtrabackup socat


* 중요 설정, 고유 입력 항목은 붉은 글씨로 표시

* wsrep_node_address 에는 머신 자체 ip 를 등록

* 초기 설정시에는 wsrep_cluster_address에 머신 자체 ip 만 등록

* wsrep_sst_receive_address 에는 자체 ip:4569 를 등록

root@ubuntu14-pv1:~# vi /etc/mysql/conf.d/mariadb.cnf

# MariaDB-specific config file.

# Read by /etc/mysql/my.cnf


[client]

default-character-set = utf8

 

[mysqld]

character-set-server = utf8

collation-server = utf8_general_ci

character_set_server = utf8

collation_server = utf8_general_ci

 

autocommit = 0


# Load Galera Cluster

wsrep_provider = /usr/lib/galera/libgalera_smm.so

wsrep_cluster_name='galera_cluster'

wsrep_retry_autocommit = 0

wsrep_sst_auth=sst_user:maria

#wsrep_sst_method = rsync

wsrep_sst_method = xtrabackup

wsrep_provider_options = "evs.keepalive_period = PT3S; evs.suspect_timeout = PT30S; evs.inactive_timeout = PT1M; evs.install_timeout = PT1M"

 

# Galera Node Option

wsrep_node_name='galera1'

wsrep_node_address='10.0.10.1'

wsrep_cluster_address = 'gcomm://10.0.10.1'

wsrep_sst_receive_address=10.0.10.1:4569

 

# Other mysqld options

default-storage-engine=innodb

binlog_format = ROW

innodb_autoinc_lock_mode = 2

innodb_flush_log_at_trx_commit = 2

innodb_locks_unsafe_for_binlog = 1

innodb_log_file_size=100M

innodb_file_per_table

query_cache_size=0

query_cache_type=0

bind-address=0.0.0.0

datadir=/var/lib/mysql

tmpdir=/tmp

user=mysql

log-error=/var/log/mysql/mysql.err


* Cluster 내에서 최초로 기동되는 MariaDB doner 노드이기에 --wsrep-new-cluster 옵션으로 시작

* [주의] Checking for corrupt, not cleanly closed... 메시지는 DB가 정상 기동 되었음을 의미함

root@ubuntu14-pv1:~# service mysql start --wsrep-new_cluster

 * Starting MariaDB database server mysqld                               [ OK ] 

 * Checking for corrupt, not cleanly closed and upgrade needing tables.


여기까지가 Doner 노드 설정 과정이다.


* 데이터베이스가 정상 작동하는지 간단히 테스트해 보고, 다음 Cluster 확장 단계로 넘어가자

root@ubuntu14-pv1:~# mysql -uroot -pmaria

MariaDB [(none)]> create database cluster_test;

MariaDB [(none)]> use cluster_test;

Reading table information for completion of table and column names

You can turn off this feature to get a quicker startup with -A


Database changed

MariaDB [cluster_test]> create table tbl1 (id varchar(20));

Query OK, 0 rows affected (0.25 sec)

MariaDB [cluster_test]> insert into tbl1 values ('abcdefg');

Query OK, 1 row affected (0.00 sec)

MariaDB [cluster_test]> commit;

Query OK, 0 rows affected (0.00 sec)



2. 운영 요령: 노드의 추가(확장)과 머신 점검을 위한 제거 등


앞의 단계는 Cluster 에 1개의 머신만 등록한 상태이다. 이번에는 Cluster 내에 2개의 머신을 더 추가하여, Galera Cluster 에서 권장하는 최소 홀수 개인 3개를 완성하는 과정과 Failover 및 자동 복구, Cluster 내에서 노드의 제거(머신 점검 등의 상황일 때)와 재투입 과정에 대해서 정리해 보자.


Cluster 의 두 번째 노드, 첫 Joiner 노드의 추가


* 앞의 Doner 노드 설정 과정과 거의 동일하며, config(maria.cnf) 의 일부 내용과 기동 방법이 다르다


* wsrep_node_address 에는 머신 자체의 ip 를 등록

* wsrep_cluster_address 에 기존의 Doner 노드와 Joiner 노드 ip 를 등록

* wsrep_sst_receive_address 에는 자체 ip:4569 를 등록

root@ubuntu14-pv2:~# scp root@10.0.10.1:/etc/mysql/conf.d/mariadb.cnf /etc/mysql/conf.d/

root@ubuntu14-pv2:~# vi /etc/mysql/conf.d/mariadb.cnf

# MariaDB-specific config file.

...

# Galera Node Option

wsrep_node_name='galera2'

wsrep_node_address='10.0.10.2'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2'

wsrep_sst_receive_address=10.0.10.2:4569

...


* Debian, Ubuntu 계열의 경우 특별히 신경 써서 작업해 주어야 하는 부분(debian-sys-maint 계정을 동일하게)

* Doner 노드의 /etc/mysql/debian.cnf 를 복사(password 만 Doner 노드의 것을 가져와도 됨)

root@ubuntu14-pv2:~# scp root@10.0.10.1:/etc/mysql/debian.cnf /etc/mysql/


* Cluster 내에 추가 되는 Joiner 노드는 별도 옵션 없이 시작

root@ubuntu14-pv1:~# service mysql start

 * Starting MariaDB database server mysqld                                [ OK ] 

 * Checking for corrupt, not cleanly closed and upgrade needing tables.


* Doner 노드의 config 를 새로이 추가된 Joiner 를 반영하여 수정해 둔다.

root@ubuntu14-pv1:~# vi /etc/mysql/conf.d/mariadb.cnf

 # MariaDB-specific config file.

...

# Galera Node Option

wsrep_node_name='galera1'

wsrep_node_address='10.0.10.1'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2'

wsrep_sst_receive_address=10.0.10.1:4569

...


* Doner 노드의 Mariadb 를 재시작할 필요는 없다(일단 맞추어 놓기만 하고, 다음에 재시작할 때 읽어 들이면 될 것이다). Joiner 가 추가되면서 이미 내부적으로 서로의 존재가 인식되었기 때문인데, Doner 노드에서 아래의 방법으로 확인할 수 있다. 즉, wsrep_cluster_address 변수는 config 에서 읽어들인 값을 가지고 있지만, Cluster 의 현재 상태 값을 가진 wsrep_incoming_addresses 는 2개 노드 접속 주소를 모두 가지고 있다.

root@ubuntu14-pv1:~# mysql -uroot -pmaria

MariaDB [(none)]> show variables like 'wsrep_cluster_address';

+-----------------------+-------------------+

| Variable_name         | Value             |

+-----------------------+-------------------+

| wsrep_cluster_address | gcomm://10.0.10.1 |

+-----------------------+-------------------+

1 row in set (0.00 sec)


MariaDB [(none)]> show status like 'wsrep_incoming_addresses';

+--------------------------+-------------------------------+

| Variable_name            | Value                         |

+--------------------------+-------------------------------+

| wsrep_incoming_addresses | 10.0.10.2:3306,10.0.10.1:3306 |

+--------------------------+-------------------------------+

1 row in set (0.00 sec)



Cluster 의 세 번째 노드, 새로운 Joiner 노드의 추가


* wsrep_node_address 에는 머신 자체의 ip 를 등록

* wsrep_cluster_address 에 기존의 노드에 추가하여 새로운 Joiner 노드 ip 를 등록

* wsrep_sst_receive_address 에는 자체 ip:4569 를 등록

root@ubuntu14-pv3:~# scp root@10.0.10.2:/etc/mysql/conf.d/mariadb.cnf /etc/mysql/conf.d/

root@ubuntu14-pv3:~# vi /etc/mysql/conf.d/mariadb.cnf

# MariaDB-specific config file.

...

# Galera Node Option

wsrep_node_name='galera3'

wsrep_node_address='10.0.10.3'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2,10.0.10.3'

wsrep_sst_receive_address=10.0.10.3:4569

...


* Doner 노드의 /etc/mysql/debian.cnf 를 복사

root@ubuntu14-pv3:~# scp root@10.0.10.1:/etc/mysql/debian.cnf /etc/mysql/


* Cluster 내에 추가 되는 Joiner 노드이므로 별도 옵션 없이 시작

root@ubuntu14-pv3:~# service mysql start

 * Starting MariaDB database server mysqld                               [ OK ] 

 * Checking for corrupt, not cleanly closed and upgrade needing tables.


* 기존의 Doner 노드와 Joiner 노드의 config 를 새로이 추가된 Joiner 를 반영하여 수정해 둔다. 앞 선 과정과 같은 요령

* 위와 마찬가지로 기존 MaraiDB들을 재시작할 필요는 없다

root@ubuntu14-pv1:~# vi /etc/mysql/conf.d/mariadb.cnf

 # MariaDB-specific config file.

...

# Galera Node Option

wsrep_node_name='galera1'

wsrep_node_address='10.0.10.1'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2,10.0.10.3'

wsrep_sst_receive_address=10.0.10.1:4569

...

root@ubuntu14-pv2:~# vi /etc/mysql/conf.d/mariadb.cnf

 # MariaDB-specific config file.

...

# Galera Node Option

wsrep_node_name='galera2'

wsrep_node_address='10.0.10.2'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2,10.0.10.3'

wsrep_sst_receive_address=10.0.10.2:4569

...


여기까지가  두 번째 Joiner 노드 추가 과정이며, 목표로 했던 3대로 이루어진 Galera cluster 가 완성되었다.


* 3대로 구성된 Galera Cluster 가 정상 작동하는지 간단한 테스트를 해 보자

root@ubuntu14-pv1:~# mysql -uroot -pmaria

MariaDB [(none)]> show variables like 'wsrep_cluster_address';

+--------------------------+----------------------------------------------+

| Variable_name            | Value                                        |

+--------------------------+----------------------------------------------+

| wsrep_incoming_addresses | 10.0.10.3:3306,10.0.10.2:3306,10.0.10.1:3306 |

+--------------------------+----------------------------------------------+

1 row in set (0.01 sec)

MariaDB [(none)]> insert into cluster_test.tbl1 values ('xyz123');

1 row in set (0.01 sec)

MariaDB [(none)]> commit;

0 row in set (0.00 sec)


* 최초에 Doner 에서 insert 했던 데이터와 직전에 insert 했던 데이터가 모두 조회된다

root@ubuntu14-pv3:~# mysql -uroot -pmaria -e "select * from cluster_test.tbl1;"

+--------+

| id     |

+--------+

| abcdefg   |

| xyz123   |

+--------+


<여기서 퀴즈1> node1(doner), node2, node3 구성일 때, node1의 MySQL이 shutdown 되었다면 node2, node3 중 하나가 doner 가 될 것이다. 잠시 후 node1이 다시 Cluster에 참여하면 node은 원래대로 doner가 될까?정답은 아래로 Drag!
정답: node1은 원래의 Doner 지위를 자동으로 되찾게 됨


Cluster 내의 노드를 제거/복원(재투입)하려면?


서버 머신 점검을 위해 Doner 노드(ubuntu14-pv1)를 Cluster 에서 제거해야 하는 상황이다. 아래의 과정으로 밟도록 하자.


* config 에서 wsrep_cluster_address 설정을 gcomm:// 로 클리어하고 DB를 재시작하면 Cluster 에서 제거됨

root@ubuntu14-pv1:~# vi /etc/mysql/conf.d/mariadb.cnf

...

# Galera Node Option

wsrep_node_name='galera1'

wsrep_node_address='10.0.10.1'

#wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2,10.0.10.3' <== Comment out

wsrep_cluster_address = 'gcomm://' <== 추가

wsrep_sst_receive_address=10.0.10.1:4569

...

root@ubuntu14-pv1:~# service mysql restart

 * Stopping MariaDB database server mysqld                               [ OK ] 

 * Starting MariaDB database server mysqld                               [ OK ] 

 * Checking for corrupt, not cleanly closed and upgrade needing tables.


root@ubuntu14-pv2:~# mysql -uroot -pmaria -e "show status like  'wsrep_incoming_addresses';"

+--------------------------+-------------------------------+

| Variable_name            | Value                         |

+--------------------------+-------------------------------+

| wsrep_incoming_addresses | 10.0.10.3:3306,10.0.10.2:3306 |

+--------------------------+-------------------------------+

root@ubuntu14-pv3:~# mysql -uroot -pmaria -e "show status like  'wsrep_incoming_addresses';"

+--------------------------+-------------------------------+

| Variable_name            | Value                         |

+--------------------------+-------------------------------+

| wsrep_incoming_addresses | 10.0.10.3:3306,10.0.10.2:3306 |

+--------------------------+-------------------------------+



ubuntu14-pv1 머신의 점검/수리가 끝났다. 원래 대로 재투입하려면 다음 과정을 밟으면 된다.


* config 를 이전 상태로 되돌리고 단순히 restart 하면 끝(이미 Galera Cluster 내에 노드가 1개 이상 작동중일 때에는 --wsrep-new-cluster 옵션을 쓰지 않음)

root@ubuntu14-pv1:~# vi /etc/mysql/conf.d/mariadb.cnf

...

# Galera Node Option

wsrep_node_name='galera1'

wsrep_node_address='10.0.10.1'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2,10.0.10.3' <== Uncomment

# wsrep_cluster_address = 'gcomm://' <== Comment out

wsrep_sst_receive_address=10.0.10.1:4569

...

root@ubuntu14-pv1:~# service mysql restart

 * Stopping MariaDB database server mysqld                               [ OK ] 

 * Starting MariaDB database server mysqld                               [ OK ] 

 * Checking for corrupt, not cleanly closed and upgrade needing tables.


root@ubuntu14-pv2:~# mysql -uroot -pmaria -e "show status like  'wsrep_incoming_addresses';"

+--------------------------+----------------------------------------------+

| Variable_name            | Value                                        |

+--------------------------+----------------------------------------------+

| wsrep_incoming_addresses | 10.0.10.3:3306,10.0.10.2:3306,10.0.10.1:3306 |

+--------------------------+----------------------------------------------+

root@ubuntu14-pv3:~# mysql -uroot -pmaria -e "show status like  'wsrep_incoming_addresses';"

+--------------------------+----------------------------------------------+

| Variable_name            | Value                                        |

+--------------------------+----------------------------------------------+

| wsrep_incoming_addresses | 10.0.10.3:3306,10.0.10.2:3306,10.0.10.1:3306 |

+--------------------------+----------------------------------------------+




3. 노드간 데이터 복제가 잘 되지 않는 것 같다. 확인/조치 방법은?


Cluster 내의 모든 노드들은 자신이 Primary 노드라고 인식한다(참고). 그러나 특정한 상황, 즉 네트워크 일시적 단절(network glitch), 과반수 이상의 노드가 장애를 겪거나 또는 split-brain 상태에 빠질 수도 있다. 이렇게 되면 데이터의 동기화에 문제가 발생할 가능성이 커지게 된다.


[여기서 잠깐] split-brain 에 대해 정리해 둘 필요가 있다. 일반적인 split-brain 이란 Master-Slave 상황에서 각자가 Master(또는 primary) 라고 인식하게 되는 상황을 말한다. Galera Cluster 와 같은 Multimaster 의 경우에도 Doner와 Joiner 관계가 있는 것과 같이 Master라 하더라도 '급' 이 다른 구분이 필요하다(즉, "데이터의 오리지널 소스가 누구지?"에 대한 답이 필요하다). 잠시 후 2-node 구성일 때의 네트워크 단절 상황에 대해 테스트 해보기로 하자.


첫 번째 장애: 3-node 구성일 때 1대의 머신에 네트워크 장애 발생


* node2의 네트워크 단절(머신 자체는 동작하지만 node1, node3 과 네트워킹이 안되도록 iptables 로 장애를 흉내 냄)

* 일정 시간이 지나면 node2 는 '쓰기 불가' 상태에 빠지며 wsrep_local_index가 0으로 떨어짐

root@ubuntu14-pv2:~# iptables -A INPUT -d 10.0.10.2 -s 10.0.10.1 -j REJECT

root@ubuntu14-pv2:~# iptables -A INPUT -d 10.0.10.2 -s 10.0.10.3 -j REJECT

root@ubuntu14-pv2:~# mysql -uroot -pmaria

MariaDB [(none)]> show status like 'wsrep_local_index';

+-------------------+-------+

| Variable_name     | Value |

+-------------------+-------+

| wsrep_local_index | 1     |

+-------------------+-------+


MariaDB [(none)]> show status like 'wsrep_local_index';

+-------------------+-------+

| Variable_name     | Value |

+-------------------+-------+

| wsrep_local_index | 0     |   <== 값이 0인 노드는 Doner 또는 Standalone 노드

+-------------------+-------+


MariaDB [(none)]> show status like 'wsrep_incoming_addresses';

+--------------------------+----------------+

| Variable_name            | Value          |

+--------------------------+----------------+

| wsrep_incoming_addresses | 10.0.10.2:3306 |

+--------------------------+----------------+


MariaDB [(none)]> show status like 'wsrep_cluster_size';

+--------------------+-------+

| Variable_name      | Value |

+--------------------+-------+

| wsrep_cluster_size | 1     |

+--------------------+-------+


MariaDB [(none)]>  show status like 'wsrep_cluster_status';

+----------------------+-------------+

| Variable_name        | Value       |

+----------------------+-------------+

| wsrep_cluster_status | non-Primary |

+----------------------+-------------+


MariaDB [(none)]> insert into cluster_test.tbl1 values ('data1234');

ERROR 1047 (08S01): WSREP has not yet prepared node for application use


[주의사항] Cluster에서 제외된 노드(여기서는 node2)에서는 정상적인 쿼리가 수행되지 않는다

* 이 때 wsrep_provider_options 변수에 pc.bootstrap=1(YES) 값을 설정하면 자체가 Standalone 모드로 작동하게 할 수 있다. 단, 네트워크를 정상화한 이후에 다시 Cluster 내에 투입하려면 반드시 MySQL을 재시작해야 한다

* Cluster 내에서 하나의 노드에서만 수행해야 한다(galera Cluster 에서는 Automatic Bootstrap 이라고 함)

root@ubuntu14-pv2:~# mysql -uroot -pmaria

MariaDB [(none)]> SET GLOBAL wsrep_provider_options='pc.bootstrap=1';


* [주의사항] 또 다른 장애 발생 가능성: 이 상황에서 node2의 특정 테이블에 insert 후 PK 변경 DDL 수행시,  Cluster에 재투입하면 node1, node3 에서는 Deadlock 발생 가능성이 있음

* 따라서, Cluster 에서 제외된 노드에서는 더 이상의 DDL이나 insert/update 쿼리가 돌지 않도록 특별히 유의해야 함

root@ubuntu14-pv1:~# 

MariaDB [cluster_test]> select * from tbl1;

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

root@ubuntu14-pv3:~# 

MariaDB [cluster_test]> select * from tbl1;

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction


* [원상복귀] node1, node3 은 정상 작동 중인 상황, node2를 되살린다

* node2 의 네트워크를 되살리면 일정 시간이 지나 node1, node2가 있는 Cluster 로 자동 복귀

root@ubuntu14-pv2:~# iptables -F <== 네트워크 장애가 해결되었음을 시뮬레이션



[특정 노드의 전체 데이터 수동 동기화]

* node2 가 Cluster에서 분리된 이후에 Update되어 데이터가 node1, node3 에 비해 지나치게 상이한 경우의 조치(초기화)

* 아래 과정대로 하면 node1, node3로부터 전체 데이터에 대해 동기화(mysqldump, xtrabackup, rsync 등 방법도 있음)

root@ubuntu14-pv2:~# rm -rf /var/lib/mysql/*

root@ubuntu14-pv2:~# rm -rf /var/log/mysql/*

root@ubuntu14-pv2:~# mysql_install_db

root@ubuntu14-pv2:~# service mysql start --wsrep_cluster_address='gcomm://'

root@ubuntu14-pv2:~# mysql_secure_installation

root@ubuntu14-pv2:~# mysql -uroot -pmaria

MariaDB [(none)]> grant all on *.* to 'root'@'%' identified by 'maria';

MariaDB [(none)]> grant usage on *.* to sst_user@'%' identified by 'maria';

MariaDB [(none)]> grant all privileges on *.* to sst_user@'%';

MariaDB [(none)]> flush privileges;

MariaDB [(none)]> quit

root@ubuntu14-pv2:~# service mysql stop

root@ubuntu14-pv2:~# vi /etc/mysql/conf.d/mariadb.cnf

...

# Galera Node Option

wsrep_node_name='galera2'

wsrep_node_address='10.0.10.2'

wsrep_cluster_address = 'gcomm://10.0.10.1,10.0.10.2,10.0.10.3' <== Uncomment

# wsrep_cluster_address = 'gcomm://' <== Comment out

wsrep_sst_receive_address=10.0.10.2:4569

...

root@ubuntu14-pv1:~# service mysql start

 * Stopping MariaDB database server mysqld                               [ OK ] 

 * Starting MariaDB database server mysqld                               [ OK ] 

 * Checking for corrupt, not cleanly closed and upgrade needing tables.


두 번째 장애: 2-node 구성일 때 1대의 머신에 네트워크 장애 발생(split-brain 발생 위험성 존재)


* Cluster내에 2개의 노드(node1, node2) 만 남아 있는 상황

* node1-2 사이에 네트워크 장애 발생, 2개의 노드에서 동일하게 데이터베이스가 정상작동하지 않음(접속은 되나 use, select 등 기본 동작 불가)

* Cluster 내의 Master 노드 정족수가 부족(과반수를 초과해야 하나, quorom=1/2)하게 되기 때문(이러한 한계를 무시하는 설정도 있고, 노드를 흉내내 주는 대안적 방법으로 garbd(Galera Arbiter로 2개 노드일 때 quorom 값을 +1 증가시켜 줌, ☞참고) 를 쓰는 방법도 있으나, 썩 바람직하지 않으므로 Skip)

root@ubuntu14-pv2:~# iptables -A INPUT -d 10.0.10.2 -s 10.0.10.1 -j REJECT


root@ubuntu14-pv1:~# mysql -uroot -pmaria

MariaDB [(none)]> select * from cluster_test.tbl1;

ERROR 1047 (08S01): WSREP has not yet prepared node for application use


root@ubuntu14-pv2:~# mysql -uroot -pmaria

MariaDB [(none)]> use cluster_test;

ERROR 1047 (08S01): WSREP has not yet prepared node for application use


* [원상복귀] node2 의 네트워크를 되살리면 일정 시간이 지나 node1이 있는 Cluster 로 자동 복귀

root@ubuntu14-pv2:~# iptables -F


root@ubuntu14-pv1:~# mysql -uroot -pmaria
MariaDB [(none)]> show status like 'wsrep_local_index';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| wsrep_local_index | 0     |
+-------------------+-------+


root@ubuntu14-pv2:~# mysql -uroot -pmaria

MariaDB [(none)]> show status like 'wsrep_local_index';

+-------------------+-------+

| Variable_name     | Value |

+-------------------+-------+

| wsrep_local_index | 1     |

+-------------------+-------+



- Barracuda -


저작자 표시 비영리 변경 금지
신고
블로그 이미지

Barracuda

Bryan의 MemoLog. 쉽게 익혀 보는 IT 실습과 개념원리, 코딩 세계의 얕은 맛보기들, 평범한 삶 주변의 현상 그리고 進上, 眞想, 진상들


Redis(레디스; REmote DIctionary System)은 요즘 각광 받고 있는 In-memory Data(key-value) Store이다. 언제고 한 번 다뤄 봤으면 했는데, 마침 비슷한 기회가 주어져서 고가용성을 확보할 수 있는 중요한 설정 방법을 찾아보고 Redis 를 활용하는 아주 간단한 Python Client 예제를 정리하여 실전을 위해 기록해 두고자 한다.


"쓰다 보니 내용이 좀 많습니다. 2편 정도로 나누려 했으나, 다루려고 하는 내용을 한 편에 모아서 구성하는 편이 더 좋다고 생각했으니 스크롤 압박이 심하더라도 양해 바랍니다" - Barracuda -


"Redis 는 DBMS 인가?", "임시 데이터 저장용 캐시라고도 하던데..." 하는 잡다한 얘기는 여기서는 생략하자. 자주 쓰다 보면 아~ Redis 는 이래서 쓰는구나 하고 느끼면 되겠고, 기회가 된다면 왜 Naver Japan에서 LINE 서비스에 이를 적용했는지, Memcached와 왜 자주 비교 되는지, Twitter, Flickr, Github, Tumblr, Instagram 등의 구축 사례를 구글링 등으로 찾아 보면 어느 정도 감이 잡힐 것이라고 생각된다.


본 포스팅에서는 아래의 내용 위주로 정리해 두기로 한다.


* Redis 아키텍처 결정 및 설치/설정 방법

* Redis Sentinel 의 추가 설정과 HA 운영

* Redis Client example - python 버전

* Redis 를 서비스에 적용할 때의 주의사항


1. Redis 아키텍처 결정 및 설치/설정 방법


Redis 로 구성할 수 있는 아키텍처들은 그 용도, 규모에 따른 조합이 다양하게 있을 수 있으며, 응용/확장 형태로 Naver 등에서는 Zookeeper 와 함께 사용하기도 하고, 때로는 Haproxy/Keepalived 를 병행하여 실제 서비스에 사용되기도 한다. 여기서는 Redis 만을 사용한 구성 모델을 생각해 보자.


* Single(단일) 서버 구성 - 개발, 테스트 용도

* MS(Master_1-Slave_N) 구성 - 소규모 개발 및 테스트 용도

* Redis MS & Sentinel*2(Total 5) 구성 - 소규모 개발 및 서비스 용도

* Redis MS & Cluster & Sharding - 중규모 이상 서비스 용도



Redis 와 Sentinel 을 이해하기 위한 간단한 예


여기서는 비교적 단순하면서도 가용성(HA: High Availability)이 보장될 수 있는 3 번째 방법을 채택하고,실제로 구현해 보자. 우선 아래 첫 번째 그림을 보자. Redis 를 설명하는 웹페이지들이 예를 들어 자주 보여 주는 그림이다.


그림 1. Single Maser-Slave & Sentinel


그림1은 하나 씩의 Master-Slave 와, 이들을 감시하는 Sentinel이 Master down시 Slave를 Master 를 승격(Failover 과정을 통해)시켜 주게 되는 좋은 예라고 할 수 있다. 그러나 Redis와 Sentinel 의 작동 과정과 원리를 직관적으로 잘 보여 준다는 점 외에는 이 아키텍처로부터 얻을 수 있는 것은 거의 없다. 왜냐하면 "3대의 서버 중 Master 1대만 죽었을 때(quorom=1일때. quorom은 뒤에 설명)" 만 안정성이 보장된다는 한계를 지닌 아키텍처이기 때문이다.


부하를 분산하기 위해 Master 는 write-only, Slave 는 read-only 로 작동하게 된다고 가정하면, Redis에 접속하여 데이터를 처리하는 Client는 Sentinel로부터 Master와 Slave 의 주소를 알아내려 하기 때문에, Slave나 Sentinel이 죽거나 특정한 2대가 동시에 죽는 어떤 경우에라도 정상적인 서비스를 기대하지 못하게 된다는 사실을 잘 따져 보도록 하자(Sentinel이 왜 필요해 지는지는 아래 실제 구성에서 자연스럽게 정리 될 것이다).



서버 3대로 안정적인 Redis 서비스를 할 수 있는 방법은?


그렇다면, 위와 같은 3대의 서버로 어떻게 하면 비교적 안정적인 서비스 환경을 구성할 수 있을까? 아래 그림을 보자.


그림 2. Redis Master 1, Slave 2 & 3 Sentinels


그림 2는 2대의 서버가 동시에 죽더라도 최소한 Data의 읽기 또는 쓰기 중 한가지만은 가능한 구조이다. Master는 2대의 Slave 들에 각각 변경 데이터를 보내 주며, 3개의 Sentinel 들이 3대의 Redis 서버와 자신을 제외한 나머지 2대의 Sentinel 들을 감시하는 구조이다. Sentinel 을 3대 이상 홀수로 사용하는 이유는, Master 가 죽고 Failover 상황이 발생생했을 때, 홀수개의 Sentinel들이 각각 투표에 참여하여 과반수 이상이 선출되도록 하기 위함이다(redis.io 권고). 그러나 ...


* 이 아키텍처에는 결정적인 한계가 존재한다. 위 그림에서는 Master 서버 내의 Redis 서버(데몬, 서비스)가 Down 되었다면 3개의 Sentinel들이 투표에 참여하여 과반수인 2표(quorom >= 2) 이상을 얻은 Slave 가 Master 로 선출될 것이다. 그런데 뭐가 문제인가?


* 실제 상황에서는 서버(머신) 내의 특정 프로세스 또는 데몬이 죽어 버리는 경우도 있지만, 머신 자체의 하드웨어(전원, 네트워크) 고장/오동작 또는 네트워크 장비/포트 단절 등에 의해 서버 자체의 연결이 끊어지는 경우가 더 많을 수 있다. 그림 1에서와 같은 문제점과 유사하게, Master 서버 머신이 Down 되어 버리거나(왜 문제일까? 아래에서 Quiz 로 제시한다), 2대의 서버가 동시에 Down 되어 버린다면 마찬가지의 참담한 결과를 맞게 된다.


해결 방법은 무엇일까? 단순히 Redis-Sentinel 서버를 1대 더 추가하여 4대를 만들어서 사용하는 방법도 있고, 3대의 Sentinel을 모두 외부의 다른 서버에 설치하거나, Redis 에 접속하는 Client 머신 내에 설치를 권장하기도 한다. 또는 시스템 내의 그리 바쁘지 않은 서버 2대를 따로 선정해서 Sentinel 만 설치/설정해 두면 상대적으로 그림은 간단해지고 위의 문제도 어느 정도 해결 가능하다.



그림 3. Redis Master 1, Slave 2 & 5 Sentinels


그림 3에서는 2대의 서버 머신이 동시에 죽는 어떠한 상황이라도 Redis 서비스 자체는 훌륭하게 동작하게 될 수 있다. 특별히 2대의 Redis 서버가 동시에 다운되었더라도, Redis Client 측에서 Master 를 Write & Read 용으로 사용하도록 약간의 코딩을 해 둔다면 말이다.


* 투입되는 서버의 댓수가 많다면 충분히 안정적인 서비스가 가능하겠지만, 하드웨어나 VM이 무한정 사용될 수는 없는 것이고 보면, 외부 Sentinel 머신을 한 개 정도만 더 추가해서 Redis 3, Sentinel 5 구성이 비교적 합리적인 선택이 아닐까 생각된다(물론 운영의 부담이 되는 관리포인트가 늘어남은 감수해야 한다)


* 결국은 여건을 충분히 고려한 취사 선택이 어느 정도로 필요하다는 말이 되겠고, 본 포스팅에서는 마지막의 그림 3을 목표 아키텍처로 하여 시스템을 준비하고, 실제로 구현하고 검증까지 진행해 보도록 하자.



Redis 서버용 머신을 이용해 Master(1)-Slave(2) 를 구성한다


* 5대의 서버를 준비한다(본 테스트 환경에서 사용한 머신은 모두 ubuntu14.4 vm이며 10.0.0.0/16 네트워크에 연결)

 - ubuntu14-pv1: 10.0.10.1 (Redis Master, Sentinel)

 - ubuntu14-pv2: 10.0.10.2 (Redis Slave1, Sentinel)

 ubuntu14-pv3: 10.0.10.3 (Redis Slave2, Sentinel)

 - ubuntu14-pvha1: 10.0.0.1 (Sentinel)

 - ubuntu14-pvha2: 10.0.0.2 (Sentinel)


그림 4. Redis Master 1, Slave 2


* 1단계 작업으로 그림 4에서와 같이 3대의 서버로 Redis Master-Slave 구성부터 진행

ubuntu14-pv1~3, ubuntu14-pvha1,2 모든 5대의 서버에서 동일하게 작업

root@ubuntu14-pv1:~# apt-get install redis-server redis-tools

root@ubuntu14-pv1:~# apt-get install python-pip

root@ubuntu14-pv1:~# pip install redis

root@ubuntu14-pv1:~# vi /etc/sysctl.conf -> 아래 내용을 추가

# Redis M-S sync

vm.overcommit_memory=1


# Increase max open file limit

fs.file-max = 1048576

root@ubuntu14-pv1:~# sysctl -p

root@ubuntu14-pv1:~# ulimit -n 65535

root@ubuntu14-pv1:~# vi ~/.bashrc -> 스크립트 최 상단에 아래 내용을 삽입

ulimit -n 65535


* ubuntu14-pv1 서버를 Redis master 로 설정하기 위한 작업이다

* 설치 자체는 어렵지 않지만, config 설정시에 주의해야할 것들이 있으니, 붉은 글씨의 라인에 쓰인 설명을 잘 참고한다

* requirepass 는 자신이 Master 가 되었을 때 요구할 접속 암호, masterauth 는 자신이 Slave 일 때 Master 로 접속하기 위한 암호이다. 본 설정에서는 뒤에 Sentinel 에 의해서 Master <-> Slave 전환이 자유로워야 하므로 동일하게 맞추어야 한다.

root@ubuntu14-pv1:~# vi /etc/redis/redis.conf

daemonize yes

pidfile /var/run/redis/redis-server.pid

port 6379

timeout 0

tcp-keepalive 0


bind 10.0.10.1 # 서버 머신 자체의 IP 주소


# slaveof 10.0.10.3 6379 # Master 는 slaveof 설정이 없어야 한다

masterauth 1234

requirepass 1234


repl-ping-slave-period 10

repl-timeout 60


loglevel notice


logfile /var/log/redis/redis-server.log


databases 16


save 900 1

save 300 10

save 60 10000


stop-writes-on-bgsave-error yes


rdbcompression yes

rdbchecksum yes

dbfilename dump.rdb


dir /var/lib/redis


slave-read-only yes


# Yes for high bandwidth, No for high latency or high traffic

repl-disable-tcp-nodelay yes


slave-priority 100


# Mitigation for replication lag, only for Master

#min-slaves-to-write 1

#min-slaves-max-lag 10


appendonly no

appendfilename "appendonly.aof"

appendfsync everysec

no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb


lua-time-limit 5000


slowlog-log-slower-than 10000

slowlog-max-len 128


notify-keyspace-events ""


hash-max-ziplist-entries 512

hash-max-ziplist-value 64

list-max-ziplist-entries 512

list-max-ziplist-value 64

set-max-intset-entries 512

zset-max-ziplist-entries 128

zset-max-ziplist-value 64


activerehashing yes


client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 32mb 8mb 60


hz 10


aof-rewrite-incremental-fsync yes


* ubuntu14-pv2, 3 서버를 Redis slave 로 설정하기 위한 작업이다

* 붉은 글씨의 라인에 쓰인 설명을 잘 참고한다. 나머지 부분 내용은 Master와 동일하다

root@ubuntu14-pv2:~# vi /etc/redis/redis.conf

daemonize yes

pidfile /var/run/redis/redis-server.pid

port 6379

timeout 0

tcp-keepalive 0


bind 10.0.10.2 # 서버 머신 자체의 IP주소


slaveof 10.0.10.1 6379 # 초기에 Master 로 사용할 1번 서버의 IP주소

masterauth 1234

requirepass 1234


repl-ping-slave-period 10

repl-timeout 60


loglevel debug


logfile /var/log/redis/redis-server.log


databases 16


save 900 1

save 300 10

save 60 10000


stop-writes-on-bgsave-error yes


rdbcompression yes

rdbchecksum yes

dbfilename dump.rdb


dir /var/lib/redis


slave-read-only yes


# Yes for high bandwidth, No for high latency or high traffic

repl-disable-tcp-nodelay yes


slave-priority 100


# Mitigation for replication lag, Comment out for slaves

#min-slaves-to-write 1

#min-slaves-max-lag 10


appendonly no

appendfilename "appendonly.aof"

appendfsync everysec

no-appendfsync-on-rewrite no

auto-aof-rewrite-percentage 100

auto-aof-rewrite-min-size 64mb


lua-time-limit 5000


slowlog-log-slower-than 10000

slowlog-max-len 128


notify-keyspace-events ""


hash-max-ziplist-entries 512

hash-max-ziplist-value 64

list-max-ziplist-entries 512

list-max-ziplist-value 64

set-max-intset-entries 512

zset-max-ziplist-entries 128

zset-max-ziplist-value 64


activerehashing yes


client-output-buffer-limit normal 0 0 0

client-output-buffer-limit slave 256mb 64mb 60

client-output-buffer-limit pubsub 32mb 8mb 60


hz 10


aof-rewrite-incremental-fsync yes


* Master부터 두 번째 Slave 까지, 모두 위와 같이 작업을 했다면, Master 부터 순차적으로 Redis 를 재시작한다.

* 진행 과정을 관찰하기 위해 redis-server 기동 후 log에 tail 을 걸어 둔다

root@ubuntu14-pv1:~# service redis-server restart

root@ubuntu14-pv1:~# tail -f /var/log/redis/redis-server.log

                _._                                                  

           _.-``__ ''-._                                             

      _.-``    `.  `_.  ''-._           Redis 2.8.4 (00000000/0) 64 bit

  .-`` .-```.  ```\/    _.,_ ''-._                                   

 (    '      ,       .-`  | `,    )     Running in stand alone mode

 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379

 |    `-._   `._    /     _.-'    |     PID: 1140

  `-._    `-._  `-./  _.-'    _.-'                                   

 |`-._`-._    `-.__.-'    _.-'_.-'|                                  

 |    `-._`-._        _.-'_.-'    |           http://redis.io        

  `-._    `-._`-.__.-'_.-'    _.-'                                   

 |`-._`-._    `-.__.-'    _.-'_.-'|                                  

 |    `-._`-._        _.-'_.-'    |                                  

  `-._    `-._`-.__.-'_.-'    _.-'                                   

      `-._    `-.__.-'    _.-'                                       

          `-._        _.-'                                           

              `-.__.-'                                               


[1140] 09 Jul 01:13:08.762 # Server started, Redis version 2.8.4

[1140] 09 Jul 01:13:08.763 * DB loaded from disk: 0.001 seconds

[1140] 09 Jul 01:13:08.763 * The server is now ready to accept connections on port 6379

[1140] 09 Jul 01:13:08.763 - DB 0: 633 keys (0 volatile) in 1536 slots HT.

[1140] 09 Jul 01:13:08.763 - 0 clients connected (0 slaves), 834680 bytes in use

[1140] 09 Jul 01:13:09.189 * Starting BGSAVE for SYNC

[1140] 09 Jul 01:13:09.191 * Background saving started by pid 1143

[1143] 09 Jul 01:13:09.218 * DB saved on disk

[1143] 09 Jul 01:13:09.219 * RDB: 0 MB of memory used by copy-on-write



Redis Master에 데이터를 입력하고 Slave 로 복제 되는지 확인하자


* Sentinel 전용 머신(ubuntu14-pvha1,2)에서는 Sentinel만 작동하여야 하므로, redis-server 데몬을 내리고 자동실행을 해제한다(source build 가 아닌  apt-get 같은 방법으로 redis-server 를 설치하면 부팅후 자동실행까지 설정되므로)

root@ubuntu14-pvha2:~# service redis-server stop

root@ubuntu14-pvha2:~# rm /etc/init.d/redis-server

root@ubuntu14-pvha2:~# update-rc.d redis-server remove


* Sentinel 전용 머신(또는 redis-tools 가 설치된 외부 머신)의 redis client 를 이용해서 확인을 수행한다

* Master 에 접속하여 "testkey01":"data1234" Key-Value 를 저장하고 Slave 에서 get 으로 확인

root@ubuntu14-pvha2:~# redis-cli -a 1234 -h 10.0.10.1 -p 6379

10.0.10.1:6379> info replication

# Replication

role:master

connected_slaves:2

slave0:ip=10.0.10.2,port=6379,state=online,offset=1386,lag=0

slave1:ip=10.0.10.3,port=6379,state=online,offset=1386,lag=0

master_repl_offset:1386

repl_backlog_active:1

repl_backlog_size:1048576

repl_backlog_first_byte_offset:2

repl_backlog_histlen:1385

10.0.10.1:6379> set testkey01 'data1234'

OK

10.0.10.1:6379> 

root@ubuntu14-pvha1:~# redis-cli -a 1234 -h 10.0.10.2 -p 6379

10.0.10.2:6379> get testkey01

"data1234"

10.0.10.2:6379> 

root@ubuntu14-pvha1:~# redis-cli -a 1234 -h 10.0.10.3 -p 6379

10.0.10.3:6379> get testkey01

"data1234"


전체적인 동작이 정상적인 것을 확인했다. 자 이 상황에서 Quiz 하나 !(검토하신 아이디어를 공유하거나 질문 하실 분은 댓글로 기재해 주세요)


* <Quiz 1> Master 서버 머신이 Down(redis.io 문서에서는 partition 이라고 표현한다. 즉 shutdown, power down, network 단절 등) 된다면 어떤 일이 일어 나는지 생각해 보자. Master 는 선출될 수 있을까? Read 동작은 가능한가? 원래의 Master 가 다시 연결되면 전체 시스템이 정상 동작 할까?(전제조건: Master 1, Slave 2(read-only), Sentinel 3, quorom=2 일 경우)

[답은 아래로 drag 하세요. 중요한 것은 왜 그럴까를 따져 보는 것입니다]