SRE(Site Reliability Engineering) 이라고 부르는게 요새 자주 들린다. 모든 내용을 다 아는건 아니지만, 이것저것 살펴보고 난 후 웃음이 났다. 그동안 내가 바라보는 관점이 그대로 같았기 때문이다. 나는 오래전부터 인프라 시스템, 좁게는 리눅스를 개발자 관점에서 다룰 수 있어야 한다고 생각해왔다.
나는 오랫동안 인프라 분야에 몸 담았다. 이렇게 말을 하면 대부분 서버, WAS 등을 다루는 것으로 생각할지 모른다. 실제로 내가 지원하는 분야는 TA 인데, 각 프로젝트마다 다르지만 그래도 TA 하면 인프라적인 요소를 많이 생각한다.
하지만 나는 적어도 인프라를 그야말로 전통적인 운영(Traditional Organizations) 을 지향하지 않았다. 대학교때에는 리눅스(Linux) 에 미쳐살았지만 그것을 벗어나 PHP 웹 개발하면서 취업도 개발자로 했다. 그러던 것이 인프라 팀으로 적을 옮겼는데, 정확하게는 인프라 개발팀이였다. Python 을 그때 배웠다. 그리고 인프라 백엔드 개발과 서버운영을 같이하는 일을 하게되면서 자연스럽게 OSS 와 인프라 개발을 위한 각가지 프로그래밍을 익혔다.
SRE 을 한국에 적용한다면 “인프라 개발팀” 이 가장 현실적이다. SRE 의 정의는 대략 다음과 같다.
Google defines an SRE as an operationally minded software engineer, but what does that mean? At Google, SRE teams are responsible for both capacity planning and provisioning. The teams are different from purely operational teams in that they seek software engineering solutions to problems.
소프트웨어 엔지니어 마인드로 운영을 다룬다는 것인데, 오래전부터 개발자 관점에서 인프라 시스템을 바라보고 다루어야 한다고 생각했었다.
문제는 현실이 그렇지 않다는 것이다. 더군다나 프리랜서 직업에서 프로젝트에 투입되보면 대부분 OS, WAS, 최근에는 Cloud 까지 구축과 운영을 하는 경우가 많았다. 이런 경우에 소프트웨어 엔지니어, 더나가 개발자적 시각으로 일을 할만큼 여건이 되지 않았다. 그리고 그런걸 원하지도 않았다. 전통적인 운영과 구축이였다.
한국의 상황이 안타까움이 있다. TA 직군에서 계약을하고 프로젝트를 하게되면 기술적인 부분에 있어서 그런대로 최근의 트랜드를 따라가긴 한다. 자동화, IaC, 데이터 분석등이 그런 것이다. 문제는 기술적인 부분만 강조할 뿐, 이것을 기반으로 SRE 까지는 가지 못한다는데 있다.
TA 들도 문제다. TA 들은 전통적인 구축, 운영에 적어 있다. 이들에게 Python 정도는 할 줄 알아야 하지 않느냐는 질문에 “그걸 왜 우리가 알아야 하냐?” 식의 답변이 대부분이다. OS 를 명령어를 통해서 세팅하고, WAS 서버를 세팅하고 로그 분석하고 JVM 설정과 덤프 분석등이 그들이 하는 일이라고 생각한다. 그러다보니 Python 은 고사하고 Cloud 에서 많이 쓰이는 Terraform 과 같은 툴을 익히는데 게을리 한다. 어떤 문제가 발생하면 서버에 직접 접속해서 봐야 속이 시원하고 JVM 덤프를 떠서 분석을 해야지만 뭔가 일을 제대로 하는 것마냥 생각한다.
이제 TA 도 소프트웨어 엔지니어를 넘어서 개발자가 되어야 한다. 진정으로 개발을 하는 건 아니지만 적어도 자신이 자신있게 하나 정도의 언어는 능숙하게 다룰 수 있어야 한다.
구글의 SRE 는 팀의 Role 이다. 정확하게는 운영에 대한 Role. 그래서 작금의 TA 직업군에게 적용되어지는 대상이다.
To enforce this, Google caps the amount of time SREs spend on purely operational work at 50%. This means that, at a minimum, 50% of a Google SRE’s time should be allocated to engineering tasks, such as automation and improvements to the service.
문제는 편견과 돈…
지금까지 TA 경력으로 일해오면서 한 마디 했다가 바보 된 경우가 있었다.
100% 장애 안나는 시스템은 없습니다. 대충 해봐도 90%정도 가동률을 보이면 진짜 잘한 겁니다. 장애 안나게 어떻게 시스템을 만듭니까?
특히나 금융 프로젝트에 투입된 상황에서 저런말을 하면 십중팔구 고객으로부터 짐 쌓라는 말을 들을 수 있다. 공공 프로젝트는 더 하지 않나? 100% 장애 안나는 시스템을 구축해야 하는거지, 금융 시스템 운영하다가 장애 날껄 생각하면서 구축하냐?
하지만 SRE 는 100% 장애를 커버하지 않는다. 이것도 나의 생각과 동일 했었는데, 장애 발생하더라도 서비스에 영향이 없다면 어떨까? 장애라는 것이 특정 서버가 다운되거나 네트워크 장비가 이상이 생길 수 있다. 이런 상황에서도 고객 서비스에 아무런 영향을 주지 못했다면 그 시스템은 신뢰성이 높다고 할 수 있지 않을까? 신뢰성(Reliability) 라는 말이 가지는 함의가 이와 같다고 생각한다.
돈.. 기업은 항상 돈을 먼저 생각한다. 기업이 힘들면 IT 부서가 먼저 짤린다는 슬픈 이야기는 더 이상 슬픈 이야기도 아니다. 100% 장애가 나지 않도록 해야지만 고객에 대한 보상도 없을 것이니 장애 발생이 가능할 수도 있다는 말을 꺼내는 순간 돈이 나갈 수 있다는 생각을 먼저 하게 된다. 장애가 나면 그것을 분석하고 장애를 예방하기 위한 일련의 활동이 필요한데, 그렇게 되면 전문가가 항시 있어야 한다. 언제 생길지도 모르는 장애 때문에 전문가를 고용한다는 건 기업에서는 있을 수 없는 일이다.
구축 프로젝트가 끝나면 대부분의 인력이 철수를 한다. 운영으로 넘어가게 되면 안타갑게도 인프라 관련 전문 인력은 뽑지도 않는다. 인프라 담당자를 뽑아 놨다고 하지만 그냥 용어만 잘 알고 있을뿐 실무적인 익숙함도 없는 사람들인 경우가 많다. 어짜피 구축된 시스템은 100% 장애가 발생하지 않도록 구축되었다는 신념이 있기 때문에 운영은 그냥 모니터링 요원정도로만 생각하는 것이다.
만약 이렇게 자동으로 디스크가 인식되지 않는다면 SCSI 디스크 ReScan 기능을 이용해야 한다. Hot Swap 은 SCSI 의 host 번호를 이용해 작동됨으로 SCSI host 번호를 알아야 한다. 하지만 리눅스에서는 /dev/sdc 형식인데 SCSI host 번호와는 다른데, 다음과 같이 알아내야 한다.
프로젝트를 하다보니 윈도우즈 시스템을 사용하고 있는 가운데, 리눅스 머신이 필요하게 되어서 vmplayer 를 이용해 가상머신으로서 리눅스를 테스트 용도로 설치해 사용하고 있었다. 이제 프로젝트도 전부 끝나게 되어서 노트북에 설치한 이 VMPlayer 의 OS 를 KVM 시스템으로 옮기 싶었다.
어떻게 VMWare, VirtualBox VM 이미지를 KVM 이미지로 바꿀 수 있을까 싶어 자료 조사를 하고 KVM 시스템으로 옮길 수 있었다. 이에 대한 내용을 정리한다.
qemu-img
VMPlayer 는 무료 소프트웨어이기 때문에 이미지 변경에 대한 툴을 제공하지 않는다. 이 경우에 KVM 리눅스 머신이 존재한다면 VMPlayer 의 이미지를 KVM 리눅스 머신으로 옮긴다.
KVM 리눅스 머신에는 qemu-img 를 사용할 수 있다. 이 명령어는 이미지를 변경할 수 있게 해준다. 만일 이 명령어가 존재하지 않는다면 다음과 같이 설치 해준다.
qemu-img 명령어 설치
ZSH
1
2
3
4
# Ubuntu
]$sudo apt install qemu-utils
# RedHat
]$sudo dnf install qemu-img
VMPlayer 이미지 옮기기 및 정보
여기서 한가지 조건이 존재한다. VMPlayer 를 이용해 VM 을 생성할때에 스토리지를 하나의 통 파일로 만들건지 아니면 여러조각의 파일로 조각해서 만들건지를 물어본다. 이 경우에는 스토리지 파일을 하나의 통 파일로 작성한 경우에 한 한다.
하나의 통파일로 작성할 경우에 VMPlayer 는 확장자 .vmdk 파일이 존재하게 된다. 이것을 KVM 리눅스 머신으로 옮겨준다. 옮겨준 파일은 qemu-img 명령어를 이용해 파일의 정보를 조회해 볼 수 있다.
VMPlayer 이미지의 정보
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$qemu-img info RHEL9.0.vmdk
image:RHEL9.0.vmdk
file format:vmdk
virtual size:30GiB(32212254720bytes)
disk size:5.06GiB
cluster_size:65536
Format specific information:
cid:2869090990
parentcid:4294967295
create type:monolithicSparse
extents:
[0]:
virtual size:32212254720
filename:RHEL9.0.vmdk
cluster size:65536
format:
VMPlayer 에서 스토리지를 생성할때에 30GiB 크기로 생성을 했으며, 사용하는 만큼만 실체적인 용량을 쓰도록 했기 때문에 5.06GiB 의 파일 크기를 가진다.
qcow2, raw 변경
qemu-img 를 사용해 KVM 이미지로 변경할 이미지 포맷을 지정할 수 있다. KVM 의 경우에 qcow2 나 raw 의 이미지를 지원한다. qcow2 의 경우에는 앞서 VMPlayer 파일 형식과 같이 총 용량은 정해져 있지만 사용하는 만큼만 실제 파일시스템에 반영이 된다. raw 는 그냥 전체 생성할때 지정한 용량이 파일 시스템에 반영이 된다.
Rocky Linux 9 는 RHEL 9 (RedHat Enterprise Linux 9) 의 크론 버전이다. RHEL9는 상용인 반면에 Rokcy Linux 는 무료다.
RHEL9나 Rokcy Linux 9 로 넘어오면서 변화한 것중에 하나가 ifcfg-eth0 파일이다. 이 파일은 /etc/sysconfig/network-scripts 디렉토리에 존재했었고 eth0 네트워크 장치에 대한 네트워크 설정 정보가 저장되었었다. 부팅을하면서 Network-Manager 데몬이 이 파일을 읽어 실행했었다. 하지만 RHEL9과 Rokcy Linux 에서는 이 파일을 더 이상 사용하지 않고 nmcli 명령어를 통해서 세팅을 하도록 변경 되었다.
이러한 변화는 Open vSwitch 세팅에서도 영향을 준다. 우분투와 다르게 RHEL9, Rocky Linux 9 에서는 nmcli 명령어를 통해서 Open vSwitch 를 설정하게 된다.
주의
절대로 원격에서 작업을 해서는 안된다. 외부와 연결하는 네트워크 작업이기 때문에 원격에서 작업을 했을 경우 다시 접속이 안될 수 있다.
Open vSwitch 설치
Rocky Linux 9 에서 Open vSwitch 설치는 패키지로 제공한다. 다음과 같이 설치가 가능하다.
Open vSwitch 설치.
ZSH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
]# dnf install centos-release-nfv-openvswitch
]# dnf search openvswitch
Last metadata expiration check:0:34:49ago on Sun Jul1615:48:152023.
inet6 fe80::47b0:302c:15bb:ad30/64scope link noprefixroute
valid_lft forever preferred_lft forever
RHEL9, Rocky Linux 9 에서 어떻게 Open vSwitch 를 설정하는지 알아 봤다.
WOL 설정
Wake On Lan 은 Ethernet 선과 연결된 상태에서 컴퓨터에 전원을 켜지는 기능을 말한다. 이 기능을 사용하기 위해서는 먼저 메인보드에서 지원을 해줘야 한다. 한가지 더 주의해야 하는 것은 메인보드마다 설정 방법이 다 다르다.
이 작업을 했던 컴퓨터는 ASRock B550M Pro4 였는데, BIOS 설정에 Boot 메뉴에 Power On Lan 설정이 있어서 Enable 을 해줬지만 되지 않았다. Advanced 설정에서 ACPI 메뉴가 존재하는데 여기서 PCIE 를 이용한 Power On Lan 설정이 존재하는데 이것을 Enable 을 해줘야 한다.(정확한 메뉴는 기억이 나지 않는다. 메뉴얼을 찾아보면 쉽게 찾을 수 있다.)
이렇게 했는데도 WOL 기능이 되지 않는다면 이것은 리눅스의 설정을 해줘야 함을 의미 한다. 앞서 Open vSwitch 설정을 해준 관계로 외부 접속을 위한 디바이스의 연결 이름이 일반적으로 다르다.
]# nmcli conn show "ovs-if-enp4s0" | grep wake-on-lan
802-3-ethernet.wake-on-lan:magic
802-3-ethernet.wake-on-lan-password:--
이렇게 한 다음에 가능하면 두번 재부팅하라고 하지만.. 글쎄… 한번만 해줘도 잘됐었다. 여러번 테스트를 해봤는데 문제없이 잘되었다.
한가지 덧붙이자면 WOL 기능을 사용하기 위해서는 절대로 전원 플러그를 뽑아서는 안된다. 멀티탭에 각 구에 전원이 On/Off 스위치가 있을 경우에 전원을 차단해서도 안된다. 전원 플러그를 뽑았다가 다시 꼽아도 안된다. 반드시 전원 플러그를 꼽고 한번은 리눅스로 부팅을 해주고 난 후에 리눅스를 Shutdown 해주고 그리고 전원 플러그는 그대로 연결이 되어 있어야 한다.
WOL 기능은 공유기에 내장되어 있는 경우가 많다. 공유기를 이용하면 원격에서도 컴퓨터를 켤수 있고 공유기의 포워드 기능을 이용하면 켜진 컴퓨터로 외부에서 접속이 가능하다. 프로젝트 할때마다 자주 써먹는 유용한 방법이다.
KVM 가상화를 운영하고 있는데, 운영중인 VM 하나가 용량이 부족해지는 상황이 발생했다. KVM 가상화 VM 의 용량은 결국 이미지 파일 한개임으로 이 이미지 파일의 용량을 늘려주면 VM 의 용량이 사실상 늘어나는 것으로 생각했다.
하지만 현시점(2023) 에서 검색을 해보니 다양한 방법들이 존재했다. 그 중에는 VM 이미지를 로컬에 마운트해서 늘려주는 방법도 존재했지만 너무나 복잡해 보였다. 좀 더 쉬운 방법이 없을까 해서 검색한 결과 가장 쉬워보이는 것을 발견했고 이 방법으로 손쉽게 VM 용량을 늘리는데 성공했다.
VM 상태
용량을 늘리려는 VM 의 상태는 다음과 같다.
VM 용량 상태
ZSH
1
2
3
4
5
6
7
8
# df -h
Filesystem Size Used Avail Use%Mounted on
tmpfs393M8.0M385M3%/run
/dev/vda148G44G794M99%/
tmpfs2.0G52K2.0G1%/dev/shm
tmpfs5.0M05.0M0%/run/lock
tmpfs2.0G02.0G0%/run/qemu
tmpfs393M0393M0%/run/user/1000
/dev/vda1 으로 파티션 하나로 보이지만 사실 SWAP 파티션도 존재한다. 이 SWAP 파티션의 /dev/vda2 디바이스이며 약 2G 용량을 차지한다.
VM 이미지 경로
VM 을 운영하는 Host 서버에서 VM 의 이미지가 어디에 있는지를 다음과 같이 살펴볼 수 있다.
VM 디스크 이미지 정
ZSH
1
2
3
4
5
]# virsh domblklist --domain Ubuntu20.04
대상소스
----------------------------------
vda/opt/kvm/ubuntu20.04.img
sda-
VM 이미지 정보
VM 이미지의 정보를 아는 것은 매우 중요하다. VM 이미지의 타입이 존재하는데 raw, qemu 타입이다. 어떤 타입인지에 따라서 VM 이미지 용량을 늘리는 방법이 달라진다.
이 작업은 매우 중요하다. truncate 명령어를 이용하는 것인데, -r 옵션으로 용량 증설을 위한 이미지를 만들었다. 그리고 그 이미지에 용량 증설을 설정한다. 여기서는 10G 용량을 늘린다
루트(/) 피티션 /dev/sda1 크기 변경
VM 이미지 내의 파티션 정보는 /dev/sda1 으로 보인다. 여기서 주의해야 할 것은 VM 이 가동된 후에 이미지 정보의 디바이스 이름은 /dev/vda1 으로 다르다. virt-df 혹은 virt-filesystems 나온 내용을 기반으로 디바스 이름을 정해야 한다.
[941.1]Expanding/dev/sda1 using the‘resize2fs’method
virt-resize:Resize operation completed with no errors.Before deleting
the old disk,carefully check that the resized disk boots andworks
correctly.
변경 요약을 보면 /dev/sda1 의 용량이 증설될 것인데, 이 파티션의 파일시스템이 ext4 임으로 resize2fs 방법을 이용해서 크기가 확장될 것임을 알려주고 있다.
진행 상태를 보면 Copying /dev/sda1 으로 나오고 실행 명령어에서 변경전 이미지와 truncate 명령어로 새롭게 생성한 이미지를 인자로 줬는데, 아마도 변경전 이미지를 truncate 명령어로 새롭게 생성한 이미지에 순차적인 복사를 하는 것으로 보인다.
이미지 용량에 따라서 작업 시간는 차이를 보일 것이다.
새로운 이미지로 VM 시작한 후 파티션 확인
/mnt7/ubuntu20.04-new.img 이미지로 VM 을 부팅한다. 그리고 난 후 다음과 같이 파티션 용량이 늘었는지 체크한다.
디스크 확인
ZSH
1
2
3
4
5
6
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
sr011:011024M0rom
vda252:0060G0disk
├─vda1252:1058.1G0part/
└─vda2252:201.8G0part[SWAP]
용량이 10G 늘었다.
파일시스템 용량을 봐 본다.
파일 시스템 용량 확인
ZSH
1
2
3
4
5
6
7
8
# df -h
Filesystem Size Used Avail Use%Mounted on
tmpfs393M6.8M386M2%/run
/dev/vda157G44G11G82%/
tmpfs2.0G52K2.0G1%/dev/shm
tmpfs5.0M05.0M0%/run/lock
tmpfs2.0G02.0G0%/run/qemu
tmpfs393M0393M0%/run/user/1000
앞에서 변경 요약을 보면 파일시스템이 뭔지를 파악하고 거기에 맞게 파티션 Resize 작업도 해주는 것으로 보인다. ext4 의 경우 resize2fs, XFS 의 경우에는 xfs_growfs 을 사용하는데 이런 작업은 파티션의 크기를 변경하는 것으로 VM 이미지 용량 증설과 함께 해준다.
G1 GC 의 기본적인 목표는 짧은 STW 시간을 가지고 가는 것이다. 어짜피 STW 를 피하지 못할 바에야 이 시간을 짧게 가지고 가는게 유리하다.
여기서 구분해야할는게 있는데 STW 는 Young GC 일때도 발생한다. 하지만 그 시간이 매우 짧아서 못 느낄 정도일 뿐이다. Old GC 의 경우에는 Young GC 대비 STW 시간이 매우 길다. 그 이유는 Heap 메모리 전체를 청소해야하기 때문이며 청소해야할 공간이 클 수록 Garbage Collector 작동을 위한 자원 소모가 많아진다.
다음과 같은 목표를 갖는다.
짧은 STW 시간을 갖도록 한다.
Full GC 가 발생되지 않도록 한다.
여기서 주목해야하는 것이 Full GC 발생하지 않도록 이다.
Full GC 발생 않도록…
MaxTenuringThreshold
이 말은 결국에는 Young GC 만 만들어야 한다는 것을 의미한다. 문제는 live Object 는 결국 최종적으로는 Old Generation 영역으로 이동할 수밖에 없다는데 있다. 그래서 G1 에서는 최대한 Old Generation 영역으로 이동하는 것을 늦출려고 한다. 이에 대한 파라메터가 -XX:MaxTenuringThreshold 다.
-XX:MaxTenuringThreshold: Sets the maximum amount of iterations to keep live objects in the new generation. This defaults to 15.
live 객체를 new generation 에 머물게하기 위한 최대 반복횟수로 번역된다. new generation 에서 Young GC 가 발생하면 오래 살아남을 live 객체를 선별되게 된다. 오래 살아남을 live 객체는 당연히 Old generation 영역으로 이동해야 하는데, 이것을 한번에 판단하지 않겠다는 뜻이다.
적어도 여러번.. 기본값으로 15번 정도는 같은 live 객체가 new generation 에서 생존해 있어야 한다. 그래야 ‘아~ 이놈은 오래 살아남을 놈이구나.. Old Generation 으로 옮기자’ 가 된다.
이러한 메커니즘은 Full GC 를 피하기 위한 것이다. Old Generation 에 live 객체가 많아지면 많아질 수록 Full GC 발생가능성은 커진다. 그래서 왠만하면 Young Generation 에 객체를 머물게하고 Garbage 가 되길 기다린다.
MaxGCPauseMillis – Do not set the new generation size unless required.
이 값을 꼭 지정하도록 하고 있다. G1 은 a pause time-target 을 갖는다. 이값은 MaxGCPauseMillis 로 지정하게 되는데 문제는 이 값을 어떻게든 충족되도록 동작하기 뒤해서 G1은 Young Generation 크기를 자동으로 조정하게 된다.
따라서 반드시 Young Generation 크기를 지정해서는 안된다. 오로지 MaxGCPauseMillis 값을 지정해주고 이를 통해서 자동으로 조정되도록 해야 한다. 200 ~ 500 사이에 값을 지정하면 되는데 될수있으면 그냥 기본값으로 둔다.
G1 에서 추천하는 JVM Options
다음은 G1 에서 추천하는 JVM Options 이다.
JVM Option
Details
-XX:+DisableExplicitGC
기본 추천 옵션으로 명시적 GC 를 사용하지 못하게 한다.
-XX:+UseStringDeduplication
기본값이 disabled 인데, 켜준다. String deduplication 에 대한 메모리 사용을 줄여준다.
-XX:MaxMetaspaceSize
최대 사용가능한 클래스 메타데이터 크기. 네이티브 메모리를 사용한다. 256MB 를 권장하지만 값은 유동적이다.
-XX:MaxTenuringThreshold
기본값: 15. new generation 에 live 객체를 유지하기 위한 최대 반복 횟수. 오랫동안 살아남아야할 live 객체가 많다면 값을 낮추고 그렇지않다면 기본값 사용 권장.