대용량 스토리지 서버 이전 작업

 

회사가 운영하고 있는 서비스에 많은 기능들이 추가되고 이용 고객들이 날로 늘어나다 보니 서버 업그레이드에 대한 소요가 지속적으로 제기되었습니다.

기존에 운영하고 있던 스토리지 서버도 훌륭한 스펙을 갖추고 있었지만 상기의 문제들로 점점 한계에 부딪혔습니다.

그래서 몇 달 전쯤 새로운 스토리지 서버를 추가적으로 도입하였고, 이전까지 성공적으로 마무리하였습니다.

회사 보안상 자세하게 밝힐 수는 없지만 대략적인 과정을 블로그를 통해 공유하고자 합니다. ^^

 

먼저 기존에 운영하고 있던 스토리지 서버는 다음과 같은 문제를 내포하고 있었습니다.

 

  • 디스크 I/O 과부하
  • 디스크 공간 부족
  • 비SSD (하드디스크 드라이브)
  • OS 지원 중단

 

회사가 운영중인 서비스에는 상품 판매를 위한 이미지, 사이즈별 썸네일, 첨부파일 등 자잘한 파일이 수십TB를 이루고 있었습니다.

그리고 모든 서버가 해당 스토리지에 연결되어 하루에도 수십GB 분량의 파일을 썼다, 지웠다 등의 작업이 발생하고 있는 상황이었습니다.

그 상황에서 대용량 SSD가 탑재된 신규 스토리지로 이전 작업을 해야 하는데, 요구조건은 아래와 같습니다.

 

  • 서버 이전 도중 서비스 부하를 최소한으로 한다.
  • 서버 이전 도중 서비스 중단이 없도록 한다.


서버 이전을 위해 실제로 서비스중인 서버를 종료하고 수 시간에 걸쳐 데이터를 신규 서버로 복사하고 서버를 부팅하는 방식으로 작업을 진행한다면, 이는 서비스의 중단으로 이어지고 곧 매출에 부정적인 영향을 미칠 수밖에 없습니다.

따라서, 대규모 서비스에서 서버를 업그레이드하거나 이전하는 작업은 무중단 작업이 필수불가결한 부분일 수밖에 없습니다.

 

절차는 아래와 같습니다.

 

1. 먼저, 기존 서버에 동작하고 있던 Batch Job이 있다면 먼저 내리도록 합니다.

기존 스토리지 서버에는 더 이상 필요가 없어진 파일을 자동으로 삭제하거나, 자동으로 백업하는 batch job이 있었습니다. 이러한 부분이 있다면 사전에 중단하여 신규 서버 이전 도중 데이터 불일치가 발생할 여지를 최소화 하도록 합니다.

 

2. 신규 서버로 전체 복제를 1회 실시합니다.

1차적으로 기존 스토리지에 있던 파일을 신규 스토리지 서버로 복사합니다. 복사는 리눅스의 rsync를 사용하였습니다.

$ rsync -aPvz --delete --bwlimit=4096 /복제할소스 사용자명@신규서버주소:/복제할목적지

bwlimit 옵션을 주면 일정 대역폭 이내에서 파일을 전송하게 됩니다. 기존 스토리지가 하드디스크 드라이브였기 때문에 부하를 최소화하기 위함입니다.

만약 rsync 복제가 실패한다면 873번 포트가 허용되어 있는지 확인하고, 차단되어 있다면 이를 허용해주도록 합니다.

$ firewall-cmd --permanent --zone=dmz --add-port 873/tcp
$ firewall-cmd --reload

 

3. 신규 서버로 증분 / 실시간 동기화를 실시합니다.

rsync로 파일 전체 복제를 1차적으로 마무리 하였더라도 기존 스토리지에는 지속적으로 파일 쓰기/삭제가 발생하고 있기 때문에 결과적으로 기존 스토리지와 신규 스토리지의 데이터는 달라질 수밖에 없습니다.

불일치한 데이터를 맞추기 위해 lsyncd를 활용할 것입니다.

 

lsyncd는 우리가 흔히 사용하는 원드라이브, 구글드라이브 등의 동기화 프로그램을 PC에 설치하면 PC의 내용을 클라우드 서버로 자동을 전송하는 것처럼 양 서버간의 불일치한 데이터를 동기화 시켜주는 강력한 도구입니다.

lsyncd는 양방향/단방향 동기화가 가능하며, 여기서는 단방향 동기화 설정을 하여 서버 이전작업을 진행할 것입니다.

 

먼저 기존 스토리지 서버에 lsyncd가 설치되어 있지 않다면 면저 설치해 주도록 합니다. (기존 스토리지 서버가 동기화 서버가 될 것입니다)

$ yum install lsyncd

 그리고 /etc/lsyncd.conf 설정파일을 편집하여 아래와 같이 설정해줍니다.

...
sync {
   default.rsync,
   delete=true,     # 원본(소스) 파일이 삭제될 경우 목적지 서버의 파일도 함께 삭제됩니다.
   source="복제할소스디렉토리",
   target="복제할타겟디렉토리::타겟서버rsync공유이름",
   exclude={"제외할디렉토리"},
   rsync = {
    archive = true,
    compress = false,
    whole_file = false
   },
   delay=0
}

 그리고 lsyncd 서비스를 시작해 줍니다.

$ systemctl start lsyncd

동기화 서버 설정이 끝났다면 동기화 클라이언트 (복제 대상 목적지) 설정을 할 차례입니다.

신규 스토리지 서버에서  /etc/rsyncd.conf 를 아래와 같이 설정합니다.

...
[rsync공유이름]
# destination directory
path = /공유할디렉토리
# Hosts you allow to copy (specify source Host)
hosts allow = 허용할IP
use chroot = yes
uid = root
gid = root
read only = false

설정이 완료 되었다면 서비스를 기동합니다.

$ systemctl start rsyncd

이제 lsyncd는 기존 스토리지 서버의 파일과 신규 스토리지 서버의 파일을 서로 비교하여 불일치한 부분만 자동으로 동기화 할 것입니다.

동기화 진행 상황은 로그파일 /var/log/lsyncd.log 에서 확인할 수 있습니다.

(최초에는 양 서버간 불일치 파일을 확인하기 때문에 시간이 오래 걸립니다)

 

4. 동기화가 맞추어지면, 신규 스토리지 서버로 최종 변경합니다.

3번까지 진행했다면 대부분의 이전 작업은 마무리 된 것입니다.

이제 가장 서비스 이용량이 적은 시간대 (주로 새벽)에 서비스를 잠시 내리고 (1분 이내) mount point 등 기존 서버에 연결되어 있던 경로를 신규 서버로 맞추어주면 되겠습니다.

이 때, 각 서버들의 서비스 설정파일, 서비스 코드 내 경로를 콘솔이나 에디터로 미리 변경해놓고 동시다발적으로 배포 및 서비스 재기동을 실시하여 다운타임을 최소화 할 수 있도록 합니다.

그리고 기존 서버에 동작하고 있던 Batch job도 신규 서버에 맞게 수정하여 다시 등록하면 되겠습니다.

 

이상으로 대용량 스토리지 서버 이전 작업에 필요한 사항을 간략하게나마 적어 보았습니다.

대부분 위와 같은 시나리오대로 진행하면 되지만, 실무에서는 예기치 못한 상황이 언제든지 발생할 수 있습니다.

예를 들면 이전한 서버가 예상대로 동작하지 않는 경우가 있을 수 있는데, 이를 대비해서 언제든지 이전을 중단하고 기존 서버로 복구할 수 있는 절차도 마련해 두시는 것을 권장합니다.

 

물론 작업에 필요한 사항은 보고서, 계획서로 정리하여 사전에 충분한 검토를 거친 후 시행하는 것이 좋겠습니다.

감사합니다.

서버이전 리눅스 무중단

Ubuntu 18.04에서 네트워크 카드 추가 및 IP 설정하기

Ubuntu 18.04로 버전이 업데이트 되면서 네트워크 카드의 설정을 변경하는 방법도 바뀌었다.

기존에는 /etc/network/interfaces 파일에서 설정을 하였지만, 18.04 버전 부터는 netplan을 통한 설정 방식으로 바뀌었다.

 

네트워크 카드 추가하기

VirutalBox의 경우 네트워크 카드 하나를 추가하고 다음에 연결됨을 [호스트 전용 어댑터]로 설정한다.

 

추가된 네트워크 인터페이스 확인

가상머신을 재부팅 한 후, ifconfig -a 명령을 통해 추가된 네트워크 인터페이스를 확인한다.

enp0s3: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fe80::a00:27ff:fe13:c615  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:13:c6:15  txqueuelen 1000  (Ethernet)
        RX packets 342  bytes 411198 (411.1 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 111  bytes 9792 (9.7 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.56.100  netmask 255.255.255.0  broadcast 192.168.56.255
        inet6 fe80::a00:27ff:fe9a:198e  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:9a:19:8e  txqueuelen 1000  (Ethernet)
        RX packets 136  bytes 11940 (11.9 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 394  bytes 39229 (39.2 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 120  bytes 8836 (8.8 KB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 120  bytes 8836 (8.8 KB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

추가된 인터페이스 카드 명이 enp0s8 임을 확인할 수 있다.

 

고정 IP 설정

Ubuntu 18.04 버전부터는 /etc/netplan/*.yaml 파일을 수정하여야 한다.

가상머신에서 다음 명령을 입력하여 해당 파일을 vi 에디터로 편집한다.

sudo vi /etc/netplan/*.yaml

다음과 같이 ipconfig -a 명령으로 찾은 신규 네트워크 카드 명을 기재하고, IP를 설정해준다.

# This file is generated from information provided by
# the datasource.  Changes to it will not persist across an instance.
# To disable cloud-init's network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
    ethernets:
        enp0s3:
            addresses: []
            dhcp4: true
        enp0s8:
            addresses: [192.168.56.100/24]
            gateway4: 192.168.56.1
            nameservers:
                    addresses: [8.8.8.8,8.8.4.4]
            dhcp4: no
            dhcp6: no
    version: 2

 

설정 적용

변경한 설정을 적용하려면 다음 명령을 입력한다.

sudo netplan apply

 

DevOps Ubuntu Network

Vue.js에서 Selectbox 컴포넌트 만들기

Vue.js에서 입력 폼을 만들 때 사용할 수 있는 Selectbox 컴포넌트를 만드는 방법을 소개하고자 한다.

SelectBox.vue (이름은 자유롭게 정한다) 파일을 하나 만들고 template를 아래와 같이 작성한다.

<template>
    <select
          v-model="this.value"
          class="custom-select"
          v-bind:id="input_id"
          v-on:input="updateValue($event.target.value)"
    >
        <option v-for="(item, index) in items" :value="index">{{ item }}</option>
    </select>
</template>

Vue.js 2.2 버전부터 v-model을 사용하는 컴포넌트는 value props를 가진다. (참고: #) 따라서 별도로 v-bind로 props를 내려줄 필요 없이 하위 컴포넌트에서 this.value라고만 명시해주면 상위 컴포넌트의 value 값을 하위 컴포넌트 값으로 가져올 수 있다.

그리고, v-on:input="updateValue($event.target.value)" 부분은 사용자가 selectbox의 값을 변경할 경우 변경된 값을 상위 컴포넌트로 전송하기 위한 코드이다. 이 코드가 없으면 상위 컴포넌트에 사용자가 변경한 값이 반영되지 않으니 주의하여야 한다.

script는 아래와 같이 작성한다.

<script>
    export default {
        name: "SelectBox",

        data() {
            return {
                //
            };
        },

        props: ['value', 'items', 'input_id'],

        methods: {
            updateValue: function (value) {
                this.$emit('input', value);
            }
        }

    }
</script>

updateValue 메서드는 사용자가 입력한 값을 input 이벤트로 내보내는 역할을 한다.

마지막으로, 컴포넌트를 가져다 쓰는 부분의 코드는 아래와 같다.

<SelectBox
       v-model="preselect_value"
       :items="somethings"
       :input_id="'my_selectbox'"
       @input="value => { preselect_value = value }"
></SelectBox>

 

Vue.js Component Develop