Category: HowTo

Systemd 유닛, 부팅 후 5분 후에 실행시키기

간혹 이런게 필요할때가 있다. 부팅을 하고 난 후에, 그것도 한 10분 후 혹은 5분 후에 프로그램을 한번만 실행시키고 싶을때…. 보통 이런 것들은 시스템 모니터링을 위해 시스템의 정보를 원격 서버에 등록하는 것과 같은 것이 있다.

과거에는 /etc/rc.local 에 스크립트를 등록해서 해결했다. 하지만 rc.local 도 SystemV Init 의 유산이라, Systemd 로 변경되면서 사라졌다.

그러면 부팅 후 실행 혹은 부팅하고 5분, 수분, 그러니까 몇분이 지난 후에 한번 실행시키는 방법은 없을까?

Systemd Timer

Systemd 는 Timer 라는 게 있다. Systemd 유닛에 여러 타입이 있는데, 일반적으로 service 를 타입을 많이 쓴다. service 타입은 그야말로 데몬으로 등록하도록하는 것이다. Spring Boot 를 시작/중지 시키기등을 할때에 service 유닛을 만든다.

Timer 는 이름에서와 같이 시간에 의존해서 작동된다. 놀랍게도 timer 유닛은 오랫동안 그리고 현재도 리눅스에 사용되고 있는 cron 을 대체할 수도 있다.

특정 시점에서 실행시키기도 하고 몇분마다 반복적으로 실행되게 할 수도 있다. 아예 날짜를 지정해 특정 날짜가 되면 실행이되도록 할 수도 있다. 서론에서 말했던 부팅하고 난 후에 몇분 후에 실행되도록 할 수도 있다.

timer 유닛은 이름 그대로 시간만 잰다. timer 유닛은 유닛 이름과 동일한 service 유닛을 설정된 시간에 실행시켜주는 역할만 한다. 그래서 timer 유닛은 동일한 service 유닛과 함께 사용된다.

그러면 ‘부팅을 한 후 5분 후에 service 유닛을 실행시키기’ 를 한번 해보자.

‘한번만 실행된다’ 주목해서 service 유닛에서 실행 타입을 ‘oneshot’ 으로 해줘야 한다. 다음 예제와 같다.

이름은 간단하게 ‘a.service’ 로 했다. service 유닛의 위치는 /etc/systemd/system 디렉토리다. 이제 service 유닛을 부팅 후에 5분이 된 시점에 실행되도록 timer 유닛을 다음과 같이 작성한다. 파일 이름은 a.timer 다.

단순하다. 중요한건 유닛의 이름이 같아야 한다.

주의사항

한가지 주의사항이 있다. service 유닛을 timer 유닛이 실행시켜주는 구조로 되어 있다보니 service 유닛은 ‘enabled’ 상태여서는 안된다. enabled 상태란, 부팅과 동시에 자동 실행 설정이다. 그런데, timer 유닛은 특정 시점에서 service 유닛을 실행시켜야 하기 때문에 service 유닛이 부팅과 동시에 자동으로 실행되면 안된다.

따라서, service 유닛은 ‘disabled’ 상태여야 하고 timer 유닛은 ‘enabled’ 상태여야 한다.

Timer 유닛 리스트, 상태

timer 유닛에 어떤게 있는지를 다음과 같이 확인할 수 있다.

timer 유닛에 스케줄 상태를 다음과 같이 확인할 수있다.

몇가지 예제

다음과 같은 예제가 있다.

위 설정은 부팅 후 5분후에 처음 실행되고, 이부터는 1시간 간격으로 실행된다.

참고

create a systemd startup script that delays 30 minutescreate a systemd startup script that delays 30 minutes

유용한 Git 명령어

유용한 Git 명령어 정리.

Git 설정

대부분 Git 설정은 명령어로 한다.

하지만 Git bash 를 이용할 경우에 설정 파일을 직접 편집하도록 할 수도 있다.

브랜치 리스트

로컬 브랜치 삭제

원격 브랜치 삭제

원격 브랜치 삭제는 branch 명령어를 사용하지 않는다.

로컬에 원격 브랜치 목록 업데이트

원격 브랜치 목록 보기를 했을때에 실제 원격 브랜치와 차이가 있을 수 있다. 이것때문에 헷깔릴 수 있는데, 원격 브랜치 목록을 로컬에 반영하기 위해서는 다음과 같이 해준다.

prune 을 자동으로 되도록 설정할 수 있다.

git 설정 파일에서는 다음과 같이 설정할 수 있다.

node_exporter 설치하기

node_exporter 는 OS 에 대한 각종 지표를 수집해주는 exporter 다. Prometheus 가 읽어 갈수 있도록 작은 웹서버로 작동된다.

설치야 바이너리로 배포를 하기 때문에 아키텍쳐에 맞게 다운받아서 설치를 하면 된다. 압축 풀고 시작하면 그만일 정도로 아주 간단하다. Prometheus 에 exporter들은 대부분 간단하다. 복잡하게 설치하지는 않는다.

그런데… 고려해야하는 부분이 존재한다. 일단, 설치부터…

Download & Install

다운로드는 Github 저장소에 받으면 된다.

node_exporter 디렉토리가 보이고 그 안에 node_exporter 바이너파일이 있다. 이것을 적당한 곳으로 이동시키놓으면 끝난다.

어떻게 시작/중지 할건지…

여기서 이제 고민을 해야한다. 많은 사람들은 이것을 쉘 스크립트와 nohup 을 사용하는 사람들이 있다. 어느 시대에 살고 있는 사람들인지 의심이 될 정도인데, 이제는 init script 도 다 없어질 만큼 대부분의 배포판들이 systemd 로 다 전환이 완료된 상태다.

그러면 당연히 systemd 로 하면 되지! 하지만 여기서 문제가 된다.

현재 systemd 는 지속적으로 지금도 버전업이 되고 있다. 그러다보니 특정 버전을 기준으로 특정 기능이 지원이되고 안되고가 갈리게 된다.

systemd 버전 240 …. (대체 어떻게 버전 관리를.. 어떻게 기능을 많이 집어넣었으면 버전이 240 이여.. -_-;; ) 왠만하면 systemd 버전 240 이상을 사용할 것을 권한다. 그런데, 이게 말처럼,, 240버전을 써라~~ 한다고 되는게 아니다.

systemd 는 리눅스 시스템의 뼈대라고 보면된다. 핵심중에 핵심! 그러다보니 systemd 는 배포판과 함께 제공되고 묶여 있다. 240버전을 쓰고 싶다면 240버전을 가진 배포판을 써야 한다는 뜻이 된다.

  • Ubuntu 22.04: 249.11-0ubuntu3.12
  • RHEL 8.10: 239-82.el8_10.1

이런 저런 사유로 배포판을 선택하고 거기다 버전을 선택하게 된다. 내가 하고 있는 프로젝트에서는 CentOS, RHEL 7.9 가 대부분이고 RHEL 8 은 최신형으로 취급하는데.. systemd 만 놓고 보면 RHEL 8 도 그다지 마음에 들지 않는 부분이다.

개인적으로 RHEL 8 도 이제는 끝물이다. RHEL 9 의 버전이 이제는 벌써 9.3을 벗어나고 있기 때문에 이제는 RHEL 9 로가야 한다.

아무튼, 말이 길었는데, systemd 유닛으로 만들어 보자..

일단, node_exporter 에는 많은 옵션들이 있다. Prometheus exporter 들이 많은 옵션들을 제공한다. 이러한 옵션들은 별도의 파일로 작성하고 쉘 변수로 만들어 두고 systemd 유닛에서 읽어들이도록 하면 된다.

/etc/sysconfig/node_exporter 파일에 다음과 같이 내용을 적어준다.

/etc/sysconfig/node_exporter 는 RedHat 기반에 적합하다. Ubuntu 면 /etc/sysconfig 디렉토리가 없기 때문에 Ubuntu 레이아웃에 맞는 곳에 넣으면 된다.

systemd 유닛 파일은 별거 없다.

딱 보면 별거 없다….. 하지만,,, 240 이하에서는 출력되는 로그들… 일명 Standard Out 들을 어떻게 처리할까? 그냥 이대로 둬도 되나? 결론은 되긴 한다. 이렇게 그대로 두면 node_exporter 가 뭔가를 출력하면 stdout, stderr 로 내보낸다. 그러면 systemd 는 이것을 /var/log/syslog 파일에 기록하게 된다.

하지만, systemd 로 변경되면서 나온 journal 에 기록을 하고 싶을지도 모른다. 이왕이면 그렇게하는게 좋기도 하다. 그래서 다음과 같이 [Service] 세션에 StandardOutput 옵션을 준다.

콘솔에도 출력을…. 뭐.. 이건 옵션이다. 여기서 systemd 버전에 따라서 파일에 redirect 가 가능하기도 하고 불가능하기도 하다. StandardOutput 에 옵션이 inherit, null, tty, journal, kmsg, journal+console, kmsg+console, file:path, append:path, truncate:path 이렇게 되어 있다. 이게 다 가능한게 아니다. 버전에 따라서 가능하기도 하고 불가능하기도 하다.

Systemd 버전

새로운 배포판에 따라서 systemd 의 버전이 달라진다. 문제는 대부분 systemd 버전이 특정 시점까지만 업데이트가 된다. 시스템에 뼈대이다 보니 확 갈아 엎을 수 없는 것이여서 그럴거다.

이러다보니 특정 기능을 탐이 나는 때가 있는데, OS 를 다 갈아 엎어야하는 고충이 있다. 그래서 이왕이면 새로운 시스템을 구축할때에는 왠만하면 최신판을 쓰는게 좋다. Ubuntu 라면 24.04, RedHat 이면 9.0 을 사용하길 권한다.

[번역] Nginx $proxy_add_x_forwarded_for and real_ip_header

이글은 다음의 글을 번역한 것입니다.

나는 대략 다음과 같은 구조로 Nginx 와 또 다른 프론트 로드 밸런서 뒤에 webapp 을 가지고 있다.

Client(a.a.a.a) -> LB(b.b.b.b) -> NGX(c.c.c.c) -> WEBAPP(d.d.d.d)

이것이 내 Nginx 설정의 일부다.

1. 로드 밸런서는 X-Forwarded-For 필드에 Client IP 를 추가 한다.

    2. Nginx 는 X-Forwarded-For 헤더에서 LB IP(b.b.b.b) 를 생략하고 Client 에 Real IP를 찾는데, $remote_addr 은 b.b.b.b 에서 a.a.a.a 로 변경되고 따라서 proxy_set_header X-Real-IP $remote_addr 은 맞는 설정이다. (내가 원하는 설정대로다.)

    그런데, Nginx 는 X-Forwarded-For 헤더를 b.b.b.b 대신에 a.a.a.a IP 로 바꾼다.

    3. WEBAPP 은 다음과 같은 헤더를 수신한다.

    내가 필요한 것은 먼저 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_forwarded_for를 설정한 다음 실제 IP를 검색하고 $remote_addr 값을 바꾸는 기능입니다.

    이 문제를 어떻게 해결해야 하는지 알려줄 사람?

    답1

     $proxy_add_x_forwarded_for 는 $http_x_forwarded_for,$remote_addr 와 동일하고 $remote_addr 값은 http_realip_module 을 사용될때에 변경됩니다. 그래서 당신은 그 헤더에서 마지막 Proxy 주소를 얻을 수 없습니다. nginx 설정은 선언적이기 때문에 지시어의 순서 변경은 영향을 주지 않습니다.

    http_realip_module 를 사용하면,  $realip_remote_addr 값은 (nginx >= 1.9.7)  오리지널 $remote_addr 처럼 사용될 수 있습니다. (역, $realip_remote_addr 은 Nginx 앞에 있는 서버에 IP, 여기서는 LB(b.b.b.b) 를 가리킨다) 따라서 다음과 같이 X-Forwarded-For 를 세팅할 수 있습니다.

    답2

    나도 같은 문제로 여기까지 왔는데, 이러한 현상은 real_ip_recursive on; 으로 발생된다는 결론을 내렸습니다. Nginx 의 공식문서에 다음과 같이 나옵니다.

    If recursive search is enabled, an original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the request header field.

    당신은 b.b.b.b 를 신뢰하도록 만들었다. 그래서 기대했던 a.a.a.a, b.b.b.b 가 a.a.a.a, a.a.a.a 로 바뀐 것입니다. 다음의 소스가 나에게 확신을 주었다. https://serverfault.com/questions/314574/nginx-real-ip-header-and-x-forwarded-for-seems-wrong

    X-Forwared-For 에서 Client IP 얻기

    Spring Boot3 에서 Reactive Web 이 아닌 그냥 Web 을 한다고 하면 Servlet 기반이고, Spring Boot 앱을 실행했을 경우 기본적으로 Embedded Tomcat 이 구동된다.

    만일 다음과 같은 환경이라고 가정해 보자.

    Client —-> AWS ALB ——> Nginx —–> Spring Boot3 (Embedded Tomca)

    ‘어느 미친놈이 저런 구조로 아키를 설계하고 구현하냐?’ 라고 비아냥될 수도 있지만 실제로 이렇게 하는 곳이 있다. 이런 구조에서 Spring Boot3 개발자은 다음과 같은 요구사항을 인프라에 전달 한다.

    Real Ip 를 가지고 올 수 있게 해주세요.

    Spring Boot3 에서 Real Ip, 그러니까 Client 의 IP 를 가지고 오는 방법으로 다음과 같은 코드를 자주 사용한다.

    이렇게 해서 돌려보면 ip 에는 Nginx 의 ip를 가지고 오게 된다. 이를 해결하기 위한 다양한 방법이 존재한다.

    Nginx 설정

    Nginx 에서 Real IP 를 가지고 오기 위해서는 Nginx 에 자체 변수인 $remote_addr 에 Client IP 를 넣어줘야 한다. 여기서 ‘$remote_addr 변수에 값을 넣어줘야’ 말이 중요하다.

    Nginx 에서도 앞에 수많은 Proxy 가 있을 경우에 $remote_addr 은 이전 Proxy 의 IP 주소가 된다.

    Client —-> AWS ALB ——> Nginx

    만일 위와같은 구조일 경우에 Nginx 에 $remote_addr 값은 AWS ALB 의 값이 된다.

    이 문제를 해결하기 위해서 Nginx 에 ‘–with-http_realip_module’ 을 가지고 있을 경우에 특정 헤더에서 Real Ip 를 찾을 수 있도록 해준다. AWS ALB 의 경우에는 X-Forwarded-For 헤더에 Client IP 를 보존해 준다. 따라서 Nginx 에서는 다음과 같이 설정해줌으로써 $remote_addr 에 Client IP를 넣을 수 있다.

    X-Forwarded-For 라는 헤더에서 Client IP 를 찾아야 하는데, set_real_ip_from 에 있는 IP 는 그냥 신뢰할 수 있는 IP 로 정의하고 Client IP 에서 제외된다. 이렇게 하는 이유는 X-Forwarded-For 가 다음과 같은 구조를 가지기 때문이다.

    X-Forwarded-For: 213.13.24.10, 172.10.10.34

    신뢰할 수 있는 IP 라는 건 Proxied IP 를 말하는 것으로 중간에 있는 서버들을 말한다. 이러한 신뢰할 수 있는 IP 가 X-Forwarded-For 에 있기 때문에 이것은 Client IP가 될 수 없다. 이 신뢰할 수 있는 IP 를 제외하고 나면 Client IP 만 남게 되고 이것을 $remote_addr 변수에 담게 된다.

    이렇게하면 끝났으면 좋을련만, Nginx 뒤에 있는 Spring Boot3 에 req.getRemoteAdd() 함수에는 여전히 Client IP가 잡히지 않는다. Nginx 에 $remote_addr 은 어디까지나 Nginx 내에서만 활용되는 것이고 뒷단에 연결된 서버까지 연향을 주지 않는다.

    이 경우에는 별도의 Header 를 정의해서 그 Header 값을 가지고 옴으로써 Client IP를 얻을 수 있다. 먼저 Nginx 에서 Header 값을 지정해줘야 한다. 다음과 같이 proxy_set_header 를 이용해 X-Real-IP 에 $remote_addr 값을 담아 준다.

    이렇게 하면 다음과 같은 코드로 Client IP 를 가지고 올 수 있다.

    Nginx 에서 설정을 하게 되면 결국 Spring Boot3 에서 Nginx 에서 넘겨주는 Client IP 를 담고 있는 별도의 Header 값을 가지고 와야 하는 코드를 작성해야 한다.

    Spring Boot3 에 Tomcat 설정

    원래 Tomcat 에서는 Valve 를 이용해서 Real IP 를 가지고 올 수 있다. 놀랍게도 이 방법은 Nginx 의 설정과 방법과 동일하고 문법만 다르다. 신뢰할 수 있는 IP 대역을 지정하고 체크할 Header 를 지정해 주면 된다. 이것을 Spring Boot3 에서는 application.properties 에 설정만으로 간단하게 할 수 있다.

    다음과 같다.

    internal-proxies 는 신뢰할 IP 설정이라고 보면 되고, Nginx 에서 set_real_ip_from 설정이며, remote-ip-header 는 어느 헤더에서 찾을지를 지정하는 것으로 Nginx 에서 real_ip_header 와 같다.

    이렇게 하면 Tomcat 은 Servlet 객체에 Client Ip 를 담아주게 되고 req.getRemoteAddr() 호출만으로, 추가적인 코딩 없이 Client Ip 를 가지고 올 수 있게 된다.

    Running a arm64 of guest on x86_64 host via kvm

    최근들어 arm64 아키텍쳐가 인기가 많아졌다. 애플의 실리콘 반도체라고 불리는것도 arm64 기반이며 Windows 11 도 arm64 에서도 작동된다. 리눅스는 오래전부터 다양한 아키텍쳐로 포팅이되었기 때문에 arm64 를 위한 배포판도 다양하게 존재한다. 문제는 arm64 아키텍쳐를 경험하기 위해서 arm64 하드웨어가 있어야 했지만, 이제는 x86_64 기반의 가상머신을 이용하면 arm64 아키텍쳐를 게스트로 운영할 수 있다.

    이 문서는 x86_64 기반 가상머신에서 arm64 아키텍쳐기반의 게스트를 실행하는 방법에 대해서 기술한 것이다.

    x86_64 가상머신

    x86_64 아키텍쳐 기반의 가상머신으로 리눅스 운영체제를 기반으로 KVM 을 활용하고 있다. GUI 툴로서 virt-manager, CLI 로는 virsh 를 활용해서 간단하게 게스트를 생성하고 운영하고 있다. arm64 아키텍쳐 게스트를 운영하기 위한 호스트로서 x86_64 아키텍쳐 기반 가상머신 스펙은 다음과 같다.

    • OS: Ubuntu 22.04
    • Kernel: 5.15.0-207.156.6.el9uek.x86_64
    • CPU: AMD Ryzen 7 2700X Eight-Core Processor

    libvirt vs QEMU

    arm64 아키텍쳐기반 게스트를 운영하기 위해서는 QEMU 를 사용해야 한다. QEMU 는 가상머신 에뮬레이터라고 생각하면 된다. 문제는 QEMU 는 CLI 기반만 제공한다.

    반면에 libvirt 는 KVM/QEMU 등을 지원하는 일종의 핸들러 라이브러리고 생각하면 된다. libvirt 를 이용하면 xml 기반으로 게스트 관련 가상머신 스펙을 정의할 수 있으며 virt-manager 와같은 GUI 툴도 활용할 수 있다.

    QEMU 에뮬레이터라고 했기 때문에 arm64 를 위한 QEMU 에뮬레이터를 설치하면 arm64 기반 게스트 운영체제를 운영할 수 있다. 다음과 같이 arm64 를 위한 QEMU 에뮬레이터를 설치해준다.

    위 패키지를 설치가 완료되면 다음과 같이 virt-manager 에서 게스트를 생성할때에 Architecture options 이 생긴다.

    qemu-system-arm 패키지 설치후 virt-manager 에서 Architecture options 이 나타난다.

    arm64 를 위한 지원 파일 생성(Optional)

    Ubuntu22.04 에서는 이 과정이 필요하지 않다.

    arm64 게스트 실행을 위해서 파일이 필요하다. 첫번째로 NVRAM 변수들을 저장하기 위한 플래쉬 볼륨(Flash volume) 을 생성해야 한다.

    두번째로 ARM UEFI 펌웨어 파일이 필요하다.

    이제 필요한 사항은 모두 갖춰졌다.

    Amazon Linux Arm64 이미지 실행

    처음부터 Arm64 기반 OS 를 게스트로 설치하면서 생성할 수도 있다. 하지만 이미 있는 Arm64 기반 OS를 가져다 쓸 수도 있다. 여기서는 Amazon Linux 를 가져다 사용해 보도록 하겠다.

    Amazon Linux 는 Amazon 이 AWS 서비스에서 사용할 목적으로 만든 배포판이다. 현재 Amazon Linux2 와 Amazon Linux3 등 다양한 버전을 제공한다. 최근 프로젝트가 Amazon Linux2 를 많이 사용하고 있어서 Amazon Linux2 Arm64 기반 OS 이미지를 게스트로 한번 실행 보도록 하겠다.

    Amazon Linux2 는 다음과같이 다운로드 할 수 있다. 이미지 파일은  Amazon Linux 2 virtual machine images 에서 찾을 수 있다.

    seed.iso 만들기

    Amazon Linux2 는 기본적으로 ec2-user 라는 시스템 계정이 있으며, 로그인을 위해 패스워드 방식이 아닌 SSH 인증키 방식을 사용한다. 하지만 배포되는 이미지에 맞는 인증키가 따로 없기 때문에 부팅과정에서 이 부분을 변경하도록 해야하는데, 이를 위해서 seed.iso 를 만들어 게스트 OS 에 CD-ROM 에 넣어 부팅해준다. 이 부분에 대한 설명은 다음의 페이지에서 찾을 수 있다.

    간단하게, 시스템 호스트 이름(나중에 변경하면 된다) 과 ec2-user 에 로그인 패스워드를 변경하도록 seed.iso 파일을 만들어 보도록 하겠다.

    먼저 작업을 위한 디렉토리 seedconfig 만들고 meta-data, user-data 파일을 생성한다.

    로그인이 가능하도록 필요한 부분만 넣었다. 이제 다음과 같이 seed.iso 파일을 생성해 준다.

    virt-manager 로 Amazon Linux2 생성하기

    이제 준비할 것들은 모두 갖췄다. virt-manager 를 이용해서 Amazon Linux2 를 구동해 보자.

    새로운 게스트 생성하기

    OS 이미지가 있기 때문에 Import 로 하고 Arm64 는 aarch64 로 선택, Machine Type 은 virt 다.

    amazon linux2 이미지, 운영체제 종류 선택

    다운로드 받은 amazon linux2 이미지와 운영체제 종류를 선택한다. 운영체제 종류를 잘 모르겠다면 Generic Linux 로 해도 된다.

    Memory, Cpu 는 사양에 맞게 조절해주고 다음을 선택한다.

    Customize configuration before install 필수

    가상머신 이름을 지정하고 네트워크 선택을 한다음에 반드시 Customize configuration before install 를 체크해 준다. 그리고 Finish 를 하면 다음과 같은 화면이 나온다.

    Firmware 를 Custom 선택

    Firmware 를 Custom 으로 하고 no-secboot.fd 선택해 준다. 그리고 설치를 시작해 준다.

    정상적으로 부팅

    정상적으로 부팅이 된다. 로그인이 안되기 때문에 앞에서 만든 seed.iso 파일을 이용해야 한다. 이를 위해서 Amazon Linux2 게스트 OS 에 CD-ROM 하드웨어를 추가해 준다.

    CD-ROM 하드웨어 추가

    이제 VM 을 다시 시작시키면 seed.iso 에 user-data 에 설정한 ec2-user 의 패스워드로 로그인이 가능해 진다.

    정상적으로 로그인 성공

    정상적으로 로그인이 성공했다. ec2-user 는 기본적으로 sudoer 권한을 가지고 있기 때문에 sudo 를 이용해서 root 작업을 할 수 있다.

    sshd 에 설정에서 패스워드 인증을 활성화 해준다.

    설정을 변경해 줬기 때문에 sshd 를 재시작 해준다.

    이렇게 sshd 로 패스워드 인증방식을 활성화 했기 때문에 원격에서 접속도 가능해진다.

    마지막으로 Amazon Linux2 게스트 를 셧다운 해주고 seed.iso 를 위한 CD-ROM 하드웨어를 삭제해준다.

    systemctl –user 설정하기

    systemctl 은 리눅스에 서비스 데몬을 설정하는 것과 유사하다. 과거 init script 를 systemd 로 전환하면서 만들어진 것인데, 문제는 시스템의 서비스 데몬 등록이라서 대부분 root 권한으로 실행된다. 만일 일반 사용자가 systemd 유닛으로 등록하기 위해서는 어떻게 해야 할까…

    ~/.config/systemd/user

    사용자 홈디렉토리에 ~/.config/systemd/user 디렉토리를 생성한다. 이 디렉토리에 사용자 systemd 유닛 파일을 작성해야 한다.

    test.service

    systemd 유닛 파일의 확장자는 service 다. 그리고 반드시 다음과 같이 WantedBy 값을 default.target 으로 해줘야 한다. 가끔 multi-user.target 으로 하는 경우가 있는데, 이걸로 할 경우 부팅시에 자동으로 실행되지 않는다.

    서비스 활성화

    이제 다음과 같이 사용자 서비스를 활성화 해준다. 이렇게 함으로써 부팅시에 자동으로 서비스가 시작된다.

    이렇게 세팅을 하게되면 리눅스 서버가 부팅될때마다 사용자 서비스도 함께 자동으로 실행이 된다.

    루트(root) 사용자 systemctl –user 사용하기

    systemctl –user 는 일반사용자가 사용하는 명령어다. 하지만, root 사용자가 systemctl –user 명령어를 사용할 수 있을까? systemd 248 (released March 2021) 버전에서 -M username@ 옵션을 통해서 root 사용자가 명령어를 실행할 수 있다.

    참고

    Start a systemd user service at boot

    Run systemctl –user commands as root

    민트 리눅스에 KVM 가상환경 구성하기

    민트 리눅스 21.03 에서 KVM 가상환경을 구성해 본다. 구성에 핵심은 KVM 의 네트워크 설정이다. 앞서 설정한 OpenvSwitch 를 이용하도록 설정 해야 한다.

    네트워크 환경

    KVM 가상화를 위해서는 네트워크 환경을 먼저 고려해야 한다. 필자의 경우에는 공유기를 이용하고 있다. 외부 광랜으로 들어온 라인을 모뎀에서 받아서 이더넷로 변환해준다. 여기서 랜선으로 공유기에 연결하고 각 컴퓨터에 연결해서 쓴다.

    공유기는 다들 아는 IpTime 인데, IpTime 은 새로운 장비가 접속되면 자동으로 사설IP 를 할당해 준다. 이것을 외부로 내보낼때는 NAT 기능을 이용해서 하나의 인터넷 라인으로 공유기 안쪽에 많은 장비를 사용할 수 있게된다.

    KVM 가상화를 하게되면 브릿지 네트워크가 생성된다. 이 브릿지 네트워크는 NAT 모드로 작동된다. 172.x.x.x 대역으로 게스트에게 자동으로 IP 를 할당해 준다. 마치 IpTime 공유기와 같은데, 문제는 이렇게 되면 다른 컴퓨터에서 게스트에 접속할 수가 없게 된다. NAT 는 단반향으로 게시트에서 바깥으로 접속은 할 수 있지만 바깥에서 게스트로 접속은 불가능하다. 가능한 방법은 Port 포워딩인데, 포트마다 설정을 해줘야 하는 번거로움이 있다.

    KVM 네트워크 설정을 NAT 가 아닌 브릿지(Bridge) 모드로 설정하고 드라이버를 OpenvSwitch 로 설정하면 호스트 컴퓨터에 브릿지 네트워크인 OpenvSwitch 를 통해서 IpTime 에서 사설 IP를 받게 된다. IpTime 내에 네트워크 모두 접속이 가능해 진다.

    KVM 을 위한 패키지 설치

    다음과 같이 패키지를 설치해 준다.

    KVM 를 위한 브릿지 네트워크 설정

    주의해야 할 것은 OpenvSwitch 를 사용하도록 설정을 해야 한다. 민트 리눅스 21.03 은 특이하게도 KVM 설치해도 네트워크 설정이 없다. 다음과 같이 확인이 가능하다.

    네트워크를 정의하기 위해서 다음과 같이 xml 파일을 작성해 준다.

    forward 모드를 ‘bridge’ 이고 virtualport 의 타입이 openvswitch 여야 한다. 파일이 작성되었다면 이제 이것을 KVM 네트워크로 정의해 줘야 한다.

    이로써 KVM 구성이 완료 됐다. 이제 virt-manager 를 이용해서 잘 게스트 OS 를 설치해 본다.

    일반 계정으로 KVM 이용하기

    KVM 설치하게 되면 root 계정으로만 사용할 수 있도록 되었다. 일반계정으로 사용하기 위해서는 다음과 같이 libvirt 그룹에 일반 계정을 추가해주면 된다.

    이제 libvritd 의 설정 파일을 다음과 같이 변경한다.

    설정을 변경했기 때문에 다음과 같이 재시작해 준다.

    민트 리눅스에서 OpenvSwitch 설정하기

    민트 리눅스(Mint Linux) 21.03 을 쓰고 있는데, 네트워킹이 NetworkManager 으로 되어 있다. Ubuntu 22.04 LTS 에서는 networkd 였지만 민트 리눅스는 같은 Ubuntu 라고 하더라도 네트워킹 운영을 NetworkManager 가 담당하고 있다.

    이 문서는 민트 리눅스에서 OpenvSwitch 설정에 대한 것이다. Ubuntu 와 다른 네트워킹을 사용하기 때문에 nmcli 명령어를 이용한 방법을 소개 한다.

    민트 리눅스 네트워킹 설정

    이렇게 NetworkManager 일 경우에는 네트워크 인터페이스 관련 설정을 nmcli 로 하게된다. 물론 민트 리눅스 이기 때문에 GUI 를 통해서 손쉽게 할 수 있다.

    GUI 툴을 이용해서 이렇게 설정을 하게 되면, nmcli 명령어를 사용해서 하는 것과 동일하게 nmcli 관련 설정파일이 변경 된다. 다음과 같은 경로에 파일이 존재한다.

    GUI 툴을 이용해 설정한 내용이 위 파일에 적용된다.

    OpenvSwitch 설치

    OpenvSwitch 를 설치를 먼저 해야 한다. Ubuntu 를 사용하고 NetworkManager 일 경우에 OpenvSwitch 도 NetworkManager 와 연관된 패키지를 설치하는 경우가 많지만 민트 리눅스에는 다음과 같은 패키지를 설치 한다.

    systemd 에 설정이 되었고 시작도 되었다. 다음과 같이 명령어가 문제없이 출력되는 확인한다.

    위와같이 정상적으로 버전이 출력되어야 한다.

    network-manager 패키지 문제

    민트 리눅스 21.03 에서는 network-manager 패키지에 문제가 있다. OpenvSwitch 플러그인이 비활성화된 패키지라는 거다. 다음과 같다.

    ovs 를 활성화하기 위해서는 network-manager 를 재패키징 해야 한다. 이를 위해서는 소스 deb 를 다운로드 받아야 한다.

    이렇게하면 디렉토로에 network-manager 관련 패키징을 위한 파일과 디렉토리가 생성된다. 여기서 다름과 같이 deb 패키징을 위한 configure 파일이라 할 수 있는 rules 파일을 수정해 줘야 한다.

    rules 파일을 열면 configure 설정을 볼 수 있다. 여기서 –disable-ovs 를 삭제한다. 이렇게 되면 ovs 를 위한 설정파일이 생성되고 이 파일에 대한 처리가 없으면 deb 패키징이 실패한다. 설치를 위한 파일 목록은 network-manager.install 파일이다. 여기서 다음을 추가해 준다.

    lib/systemd/system/NetworkManager.service.d/NetworkManager-ovs.conf

    이제 deb 패키지를 만들어야 하는데, network-manager 의 의존성 라이브러리를 설치해 줘야 한다.

    그리고 debuild 명령어로 패키징을 제작해야 한다.

    위와같이 하면 deb 패키지가 만들어진다. deb 패키지는 network-manager-1.36.6 디렉토리 밖에 만들어 진다.

    이제 다음과 같이 재설치를 해준다.

    nmcli 명령어를 이용한 openvswitch 설정

    이제 nmcli 명령어를 이용해서 openvswitch 설정을 다음과 같이 한다. IP는 각자 자신의 네트워크 설정으로 바꾸면 된다. 먼저 명령어로 device 상태를 봐본다.

    ‘유선 연결 1’ 을 connection name 이라고 하는데, 한글이라 앞으로 설정하는데 문제가 될 수 있다. 이것을 DEVICE와 동일하게 설정해준다. 이것은 앞서 GUI 툴을 이용해서 변경해서 적용하면 간단하게 바꿀 수 있다. 변경이 되면 다음과 같다.

    이제 nmcli 명령어를 다음과 같이 입력해준다.

    IP 와 gateway 그리고 DNS 를 자신에 맞게 고쳐준다. 위와 같이하고 ovs-vsctl show 명령어로 제대로 되었는지 확인한다.

    그리고 ip a 명령어로 제대로 ip가 세팅되었는지 확인한다.

    잘되어 보인다. 이제 nmcli 명령어를 이용해 enp5s0 connection 을 삭제한다.

    최종적인 모습은 다음과 같다.

    이제 재부팅을 한 후에 네트워크가 잘된다면 끝난다.

    Kafka 설치하기

    이벤트 스트림을 제공하는 브로커 이다. 더블어 메시지 스트림도 지원한다. Scala 로 제작되었으며 JVM 위에서 동작한다. 따라서 Java 가 있어야 한다.

    환경

    서버는 3대로 준비했다. Production 에서는 적어도 3대의 Broker 를 권장대로 설치한 것이다. 서버 3대는 다음과 같다.

    • klab-master1.systemv.local(192.168.96.60)
    • klab-worker1.systemv.local(192.168.96.61)
    • klab-worker2.systemv.local(192.168.96.62)

    배포판은 Rocky Linux 9.3 최신 버전이다.

    요구사항

    Apache Kafka 를 실행하기 위해 JVM 이 필요하다. Kafka 버전에 따라서 Java 버전도 선택해야 한다. 최신의 Kafka 는 Java 11 을 필요로 한다.

    Java 11 을 설치는 패키지로 설치 했다.

    Kafka 다운로드 및 설치

    Apache Kafka 는 AWS 의 MSK 에 버전과 동일하게 선택했다.

    다음과 같이 /app 디렉토리에 압축해제로 설치를 완료 한다.

    Zookeeper 설정

    kafka 에 zookeeper 가 내장되어 있다. 설정은 /app/kafka/config 디렉토리에 zookeeper.properties 파일이다. 다음은 예시다.

    systemd Unit을 작성해서 관리한다.

    Kafka 설정

    kafka 의 설정은 /app/kafka/conf/server.properties 파일이다. 주요한 설정은 다음과 같다.

    systemd Unit을 작성해서 관리한다.

    토픽 테스트

    이제 토픽을 생성해 테스트를 해본다. 다음과 같이 Topic 을 생성한다.

    파티션 1개 복제는 3개로 토픽이 생성되었다. consumer 명령어를 이용해서 토픽에 접속한다.

    접속을 하면 아무런 일도 발생하지 않는다.

    이제 producer 를 이용해서 메시지를 생산한다.

    이렇게 메시지를 생성하면 앞서 접속한 consumer 화면에 똑같이 내용이 나와야 한다.

    topic 의 통계 정보를 다음과 같이 확인해 볼 수 있다.

    참고