2025년 11월 11일 화요일

High Performance Web Server Appliance 만들기

이번 시간에는 Intel Xeon Appliance를 사용하여, 고성능 Web Server Appliance를 만드는 세가지 방법 🚀🚀🚀을 소개해 보고자 한다. 😎


 "
100만원 짜리 Intel appliance로 값비싼 고성능 웹서버 Appliance를 만들 수는 없을까 ?"


목차
1. 고성능 웹 서버 만들기
2. DPDK & F-Stack 기반의 Nginx 구동하기
3. DPDK & VPP 기반의 Nginx 구동하기
4. Linux Kernel 기반의 Web 서버 구동하기 - Tempesta FW
5. References


거짓말 같이 파란 가을 하늘~

1. 고성능 웹 서버 만들기
Linux(혹은 다른 상용 OS) 상에서 동작하는 일반적인 web server는 아래와 같이 OS의 사용자 영역에서 동작한다. 

[그림 1.1] 일반적인 웹서버 구성
📌 웹서버는 일반적으로 network appliance 보다는 server 형태로 구축되는게 보통이지만, 편의상 위와 같이 표현하였다.

1.1 고성능 웹서버를 만들기 위한 방안
사용자 영역에서 동작하는 웹서버가 극강의 성능을 내지 못하는 이유는 아래와 같은 여러가지 원인에 기인한다.

<기존 웹서버 방식의 성능상 한계 원인>
1) (userspace -> kernel로 향하는) 잦은 system call로 인한 빈번한 context switching 발생 처리
2) 빈도수가 높은 copy operation
3) asynchronous socket 처리 방식으로 인한 지연
4) interrupt 기반의 packet handling
5) HTTP parser의 비효율성(복잡한 http string 처리)
6) ...
이 밖에도 다양한 이유가 있을 수 있다.

물론, 기존의 방식으로 웹서비스가 불가하다는 얘기는 절대 아니다. CPU, Memory, NIC의 사양을 적절히 올리고, 상황에 맞게 제대로 tuning하여 사용한다면, 대개는 아무런 문제없이 서비스가 가능하다(보통 이렇게들 하고 있다).
하지만, 특정 event가 발생하여 트래픽이 폭주할 경우(예: concert ticket 예매, 인기투표, 대학 수강신청 등), 서비스가 일정 시간 마비되는 현상이 발생하는 경우도 종종 있다. 💣
물론, 이러한 상황을 대비하여 (많은 경우에) 웹서버 앞단에 L4 switch나 보안이 강화된 ADC(Application Delivery Controller) 장비, 아니면 HAProxy 같은 부하분산 장비를 배치하여 사용하고 있지만, 이는 추가 장비를 구매해야 하는 부담으로 다가온다. 💰

따라서, 이번 posting에서는 값싼 장비(혹은 서버)를 사용하면서도, 추가 비용 없이 극강의 웹서버를 만들 수 있는 방법을 소개해 보고자 한다. 💢 단, 절대적인 방법은 아니며, 그만큼 개발자의 많은 노력이 필요하다는 말을 하고 싶다. 😂

<이번 Posting의 주제>
1) DPDK & F-Stack(FreeBSD stack) 기반의 Nginx 소개
2) DPDK & VPP(Host-Stack) 기반의 Nginx 소개
3) Linux Kernel 기반의 웹 서버 & 가속기(Tempesta FW ) 소개

과연, 이 3개의 방법 중 어느 것이 가장 효율적일까 ? 또한 각각의 방법에는 장점만 있고, 단점은 없을까 ?

1.2 시험에 사용할 Intel Appliance
시험에 사용할 장비는 지난 시간과 마찬가지로 Intel Xeon E5-2695 appliance가 되겠다.

[그림 1.2] Intel Xeon appliance

<장비 주요 spec>
1) CPU: Intel Xeon E5-2695 v4 @ 2.10GHz (CPU core: 18개, 36 threads)
2) RAM: 16GB
3) NVME: 128GB
4) NIC: 8 x 2.5G(i226V)
5) SFP+: No
6) USB: 4 x USB3.0
7) VGA 1
________________________________
📌 아래 내용 중, 위의 사진과 다른 장비가 포함된 그림이 다수 있다. 이는 일일이 그림을 수정하기 번거로워 그렇게 한 것이니, 감안해서 읽어 주시기 바란다. 😂

1.3 HTTPS 연결을 위한 사설 인증서 준비하기
본격적인 주제에 들어가기에 앞서서, 각 장에서 공통으로 사용할 x.509 인증서 및 Private Key를 아래와 같이 준비해 보기로 한다. Certbot(Let's encrypt)을 사용하여 자동으로 인증서를 만드는 것이 편리하겠으나, 별도의 domain name이 필요한 문제가 있으니, 여기에서는 아래 site 내용을 참조하여 self-signed 인증서를 만들어 사용하기로 하겠다. 방구석 테스트라 어쩔 수가 없다. 😂

[1] RSA 기반 인증서

[2] ECDSA 기반 인증서

[3] PQC ML-DSA 기반 인증서
📌 PQC(양자내성 암호)를 사용하는 경우라면, ML-DSA 기반의 인증서를 만들어 사용하는 것도 좋은 선택이다.

1) RSA 기반 인증서 만들기

<step#1 - self-signed CA 인증서 만들기>
openssl genrsa -aes256 -out rootca.key -passout pass:pass123456
  -> rootca용 private key(rootca.key) 생성
chmod 600 rootca.key

vi rootca.conf
  -> rootca CSR 생성을 위한 configuration 파일 준비
------------------------------------------------------------------------------------------------
[ req ]
default_bits                     = 2048
default_md                      = sha1
default_keyfile                = rootca.key
distinguished_name      = req_distinguished_name
extensions                       = v3_ca
req_extensions               = v3_ca
 
[ v3_ca ]
basicConstraints             = critical, CA:TRUE, pathlen:0
subjectKeyIdentifier       = hash
##authorityKeyIdentifier = keyid:always, issuer:always
keyUsage                         = keyCertSign, cRLSign
nsCertType                      = sslCA, emailCA, objCA

[req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default       = KR
countryName_min            = 2
countryName_max            = 2

organizationName            = Organization Name (eg, company)
organizationName_default   = Slowboot Inc.
 
organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_default  = R&D.
 
commonName                      = Common Name (eg, your name or your server's hostname)
commonName_default        = Slowboot Certificate Trust Root
commonName_max             = 64
____________________________________________________________________________________

openssl req -new -key rootca.key -out rootca.csr -config rootca.conf -passin pass:pass123456
  -> CSR(certificate signing request) 파일(rootca.csr) 생성

openssl x509 -req -days 3650 -extensions v3_ca -set_serial 1 -in rootca.csr -signkey rootca.key -out rootca.crt -extfile rootca.conf -passin pass:pass123456
  -> ca 인증서(rootca.crt) 생성

$ openssl x509 -text -in rootca.crt
  -> rootca.crt 내용 확인
____________________________________________________________________________

<step#2 - server 인증서 만들기>
openssl genrsa -aes256 -out private-cert.key.enc -passout pass:pass123456
openssl rsa -in private-cert.key.enc -out private-cert.key -passin pass:pass123456
  -> privatekey(private-cert.key) 생성

vi private-cert.conf
  -> 서버용 인증서 생성 요청을 위한 configuration 파일 준비
[ req ]
default_bits            = 2048
default_md              = sha1
default_keyfile         = rootca.key
distinguished_name      = req_distinguished_name
extensions              = v3_user
## req_extensions = v3_user

[ v3_user ]
# Extensions to add to a certificate request
basicConstraints       = CA:FALSE
authorityKeyIdentifier = keyid,issuer
subjectKeyIdentifier   = hash
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
subjectAltName         = @alt_names
[ alt_names]
DNS.1   = slowboot.net 
DNS.2   = slowboot.com

[req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_default             = KR
countryName_min                 = 2
countryName_max                 = 2

organizationName              = Organization Name (eg, company)
organizationName_default      = Slowboot Inc.
 
organizationalUnitName          = Organizational Unit Name (eg, section)
organizationalUnitName_default  = R&D.
 
commonName                      = Common Name (eg, your name or your server's hostname)
commonName_default              = VPPBox 
commonName_max                  = 64
__________________________________________________________________________

openssl req -new  -key private-cert.key -out private-cert.csr -config private-cert.conf
  -> CSR(certificate signing request) 파일 생성

openssl x509 -req -days 1825 -extensions v3_user -in private-cert.csr -CA rootca.crt -CAcreateserial -CAkey  rootca.key -out slowboot.net.crt  -extfile private-cert.conf -passin pass:pass123456
  -> 서버용 인증서 파일(slowboot.net.crt) 생성(rootca.key로 서명함).

openssl x509 -text -in slowboot.net.crt 
  -> 서버용 인증서 내용 확인

이렇게 해서, 생성된 결과물은 다음과 같다(만들고 보니 이름이 직관적이지 못하다 😓). 절차를 간소화하기 위해 Intermedia CA 인증서 생성 과정은 제외하였다.

-rw-rw-r-- 1 chyi chyi 1557 10월 29 21:31 private-cert.conf
-rw-rw-r-- 1 chyi chyi  956 10월 29 21:31 private-cert.csr
-rw------- 1 chyi chyi 1704 10월 29 21:27 private-cert.key       <= server.key
-rw------- 1 chyi chyi 1874 10월 29 21:25 private-cert.key.enc
-rw-rw-r-- 1 chyi chyi 1119 10월 29 21:19 rootca.conf
-rw-rw-r-- 1 chyi chyi 1289 10월 29 21:21 rootca.crt               <= RootCA.crt
-rw-rw-r-- 1 chyi chyi 1127 10월 29 21:20 rootca.csr
-rw------- 1 chyi chyi 1874 10월 29 21:17 rootca.key                <= RootCA.key
-rw-rw-r-- 1 chyi chyi 1387 10월 29 21:33 slowboot.net.crt    <= server.crt

______________________________________________________

2) ECDSA 기반 인증서 만들기

<step#1 - self-signed CA 인증서 만들기>
$ openssl ecparam -genkey -name prime256v1 -out ca.key
  -> prime256v1 curve algorithm을 사용하여 ca private key 생성
  -> prime256v1: X9.62/SECG curve over a 256 bit prime field
$ cat ca.key
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIDanX+sS/D4bY1bv2xreUKcJ6O6s9wp7f6o5cyD76CM5oAoGCCqGSM49
AwEHoUQDQgAEYwNyRvNODk9X4xyh2vTTwk/4dQEvXLl6tKurIptFJpAR1hG3/qEz
/dcbFOFiGG+Z9g4ZOqrOwB8mBD7KaI5tsg==
-----END EC PRIVATE KEY-----

$ openssl req -x509 -new -SHA384 -nodes -key ca.key -days 3650 -out ca.crt
  -> ca.key와 SHA384 algorithm을 이용하여 self-signed 인증서 ca.crt 생성
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:KR
State or Province Name (full name) [Some-State]:Gyeonggi
Locality Name (eg, city) []:Suwon
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Slowboot.net
Organizational Unit Name (eg, section) []:R&D
Common Name (e.g. server FQDN or YOUR name) []:VPPBox
Email Address []:chunghan.yi@gmail.com

$ cat ca.crt
-----BEGIN CERTIFICATE-----
MIICbzCCAhWgAwIBAgIUN2YZQcWqqSOBsHELpJoT1fwLlCswCgYIKoZIzj0EAwMw
gYwxCzAJBgNVBAYTAktSMREwDwYDVQQIDAhHeWVvbmdnaTEOMAwGA1UEBwwFU3V3
b24xFTATBgNVBAoMDFNsb3dib290Lm5ldDEMMAoGA1UECwwDUiZEMQ8wDQYDVQQD
DAZWUFBCb3gxJDAiBgkqhkiG9w0BCQEWFWNodW5naGFuLnlpQGdtYWlsLmNvbTAe
Fw0yNTExMTAxMjA0MzVaFw0zNTExMDgxMjA0MzVaMIGMMQswCQYDVQQGEwJLUjER
MA8GA1UECAwIR3llb25nZ2kxDjAMBgNVBAcMBVN1d29uMRUwEwYDVQQKDAxTbG93
Ym9vdC5uZXQxDDAKBgNVBAsMA1ImRDEPMA0GA1UEAwwGVlBQQm94MSQwIgYJKoZI
hvcNAQkBFhVjaHVuZ2hhbi55aUBnbWFpbC5jb20wWTATBgcqhkjOPQIBBggqhkjO
PQMBBwNCAARjA3JG804OT1fjHKHa9NPCT/h1AS9cuXq0q6sim0UmkBHWEbf+oTP9
1xsU4WIYb5n2Dhk6qs7AHyYEPspojm2yo1MwUTAdBgNVHQ4EFgQURg8KsBGWqYfu
DBN+hDrc+sv0Af0wHwYDVR0jBBgwFoAURg8KsBGWqYfuDBN+hDrc+sv0Af0wDwYD
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNIADBFAiAbi9jaikaNAxPcf4J+xMB6
ums3fB0dEPlNlg9zbcfuHQIhAN8X7eWQ/g5rnQlfO0H8OqnZqVToUxJYqeW+XFaL
szHA
-----END CERTIFICATE-----


<step#2 - server 인증서 만들기>
$ openssl ecparam -genkey -name prime256v1 -out server.key
  -> server private key 생성(prime256v1 curve 사용)
$ cat server.key 
-----BEGIN EC PARAMETERS-----
BggqhkjOPQMBBw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIOjqs9UiZaKVWhy4mjVR6h9bX2UsS6947EBSbazclcbvoAoGCCqGSM49
AwEHoUQDQgAEzUhpSwmYhg9g331QGQJ+nGYEhRXb9m8bvAi2jk8DikVua/s/Fg3I
iZ9ve4HPneBPNQcOiFT0UwI1c2hIb1Lxaw==
-----END EC PRIVATE KEY-----

$ openssl req -new -SHA384 -key server.key -nodes -out server.csr
  -> server.key를 이용하여 CSR 생성
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:KR
State or Province Name (full name) [Some-State]:Gyeonggi
Locality Name (eg, city) []:Suwon
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Slowboot.net
Organizational Unit Name (eg, section) []:R&D
Common Name (e.g. server FQDN or YOUR name) []:vppnginx
Email Address []:chunghan.yi@gmail.com

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:12345678
An optional company name []:

$ openssl req -in server.csr -noout -text
  -> server.csr 내용 확인 
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = KR, ST = Gyeonggi, L = Suwon, O = Slowboot.net, OU = R&D, CN = vppnginx, emailAddress = chunghan.yi@gmail.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:cd:48:69:4b:09:98:86:0f:60:df:7d:50:19:02:
                    7e:9c:66:04:85:15:db:f6:6f:1b:bc:08:b6:8e:4f:
                    03:8a:45:6e:6b:fb:3f:16:0d:c8:89:9f:6f:7b:81:
                    cf:9d:e0:4f:35:07:0e:88:54:f4:53:02:35:73:68:
                    48:6f:52:f1:6b
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
            challengePassword        :12345678
            Requested Extensions:
    Signature Algorithm: ecdsa-with-SHA384
    Signature Value:
        30:45:02:20:0a:eb:74:b7:b9:30:be:6a:64:90:72:fb:fc:b6:
        38:71:53:38:81:03:2d:8a:b0:be:c4:07:ca:3a:25:da:ea:e2:
        02:21:00:cc:f1:ad:89:63:9a:c9:7f:6b:df:bb:92:4b:d5:0e:
        96:a6:c0:41:ee:7f:8e:22:25:2d:c3:fe:9a:c3:4b:92:9d

$ vi v3.ext
  -> 요즘나오는 web browser는 인증서에 FQDN(domain name 정보가 포함한 hostname) 정보가 들어 있을 것을 요구한다. 따라서 아래와 같은 내용을 포함된 v3.ext file을 먼저 만들도록 한다.

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = ubuntu.slowboot.net   #이 내용은 웹서버에 맞게 적절히 기입하자.
~

$ openssl x509 -req -SHA384 -extfile v3.ext -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt
  -> server.csr에 대해 ca.key, SHA384로 서명한 server.crt 파일 생성
Certificate request self-signature ok
subject=C = KR, ST = Gyeonggi, L = Suwon, O = Slowboot.net, OU = R&D, CN = vppnginx, emailAddress = chunghan.yi@gmail.com
Certificate request self-signature ok
subject=C = KR, ST = Gyeonggi, L = Suwon, O = Slowboot.net, OU = R&D, CN = vppnginx, emailAddress = chunghan.yi@gmail.com

$ cat server.crt 
-----BEGIN CERTIFICATE-----
MIICmTCCAj6gAwIBAgIUYSWSiAhvqYt2dxIpLhRftocp2yQwCgYIKoZIzj0EAwMw
gYwxCzAJBgNVBAYTAktSMREwDwYDVQQIDAhHeWVvbmdnaTEOMAwGA1UEBwwFU3V3
b24xFTATBgNVBAoMDFNsb3dib290Lm5ldDEMMAoGA1UECwwDUiZEMQ8wDQYDVQQD
DAZWUFBCb3gxJDAiBgkqhkiG9w0BCQEWFWNodW5naGFuLnlpQGdtYWlsLmNvbTAe
Fw0yNTExMTEwMzAzNDFaFw0yNjExMTEwMzAzNDFaMIGOMQswCQYDVQQGEwJLUjER
MA8GA1UECAwIR3llb25nZ2kxDjAMBgNVBAcMBVN1d29uMRUwEwYDVQQKDAxTbG93
Ym9vdC5uZXQxDDAKBgNVBAsMA1ImRDERMA8GA1UEAwwIdnBwbmdpbngxJDAiBgkq
hkiG9w0BCQEWFWNodW5naGFuLnlpQGdtYWlsLmNvbTBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABM1IaUsJmIYPYN99UBkCfpxmBIUV2/ZvG7wIto5PA4pFbmv7PxYN
yImfb3uBz53gTzUHDohU9FMCNXNoSG9S8WujejB4MB8GA1UdIwQYMBaAFEYPCrAR
lqmH7gwTfoQ63PrL9AH9MAkGA1UdEwQCMAAwCwYDVR0PBAQDAgTwMB4GA1UdEQQX
MBWCE3VidW50dS5zbG93Ym9vdC5uZXQwHQYDVR0OBBYEFASQ9YlUL8TOYxSSqOfE
3/17I7k+MAoGCCqGSM49BAMDA0kAMEYCIQD3bv6MaclHHqXaAbpp9WR04nxHnHgb
kSii0CaTw8k//wIhANhRvGW4aHO+hdIqVMrZrh2vmsT+qWutITnxycPffsf4
-----END CERTIFICATE-----

이번에는 좀 더 직관적인 이름으로 만들어 보았다.
$ ls -la
-rw-rw-r-- 1 chyi chyi  904 11월 11 14:29 ca.crt
-rw------- 1 chyi chyi  302 11월 11 14:29 ca.key
-rw-rw-r-- 1 chyi chyi  960 11월 11 14:30 server.crt
-rw-rw-r-- 1 chyi chyi  558 11월 11 14:29 server.csr
-rw------- 1 chyi chyi  302 11월 11 14:29 server.key
-rw-rw-r-- 1 chyi chyi  211 11월 11 14:30 v3.ext
____________________________________________________________________

아래 2개의 그림은 앞서 만든 x.509 인증서의 상관 관계를 표현해 본 것이다.

PC에는 web browser에서 사용하는 범용 Root CA용 인증서 파일 목록이 사전에 준비되어 있다. 따라서 범용 RootCA 인증서로 서명한 경우(예: Let's encrypt로 만든 인증서)에는 RootCA 인증서를 사전에 전달할 필요가 없다. 웹 browser로 인터넷 surfing을 하는 경우를 생각해 보라. 인터넷 저너머의 다양한 서버와 연결함에도 불구하고, 해당 서버로 부터 일일이 CA 인증서를 사전에 전달 받아 사용하는 사람은 없을 것이다. 👀

[그림 1.3] x.509 인증서 상관 관계(1) - 범용 CA 인증서 사용 예 [출처 - 참고문헌 20]

하지만, self signed CA 인증서의 경우라면 얘기가 달라진다. Web browser는 사용자가 만든 self-signed CA 인증서의 존재를 모르므로, (어떤 식으로든) 사전에 이를 등록해 주어야 하면, 정상적인 TLS 통신이 가능하다.

[그림 1.4] x.509 인증서 상관 관계(2) - self-signed CA 인증서 사용 예

<인증서 관련 몇가지 내용 정리>
[1] 이렇게 복잡한 인증서(x.509 Certificate) ! 그렇다면, 도대체 인증서는 왜 필요한 것일까 ?
  • 궁극적인 목적은 통신 상대방의 Public key를 신뢰를 기반으로 안전하게 전달하기 위함이다. 물론, 이 과정에서 상대방에 대한 인증도 덤으로 진행되지만 말이다.

[2] (다시 말하지만) TLS 통신 시, CA.crt는 Client와 사전에 공유되어 있어야 한다.
  • 만일, Ubuntu PC의 chrome browser를 사용하는 경우라면, 아래와 같은 방법으로 self-signed CA.crt 값을 사전에 등록해 줄 수 있다.
  • $ certutil -A -n "My Certificate" -t "TCu,Cu,Tu" -i ./ca.crt -d sql:$HOME/.pki/nssdb

[3] TLS 통신 시, (일반적으로) [ Server.crt | IMCA.crt | RootCA.crt ] 형태의 Chained Certificate이 전달되어야 한다.

[4] x.509 certificate의 형식은 다음과 같이 여러 형태가 될 수 있다.
  • ASN.1 표기법으로 표현 -> DER encoding하여 byte 형태로 표현 -> Base64 encoding -> PEM format

[그림 1.5] x.509 인증서의 형식 [출처 - 참고문헌 20]
__________________________________________________________________________

지금까지 이번 posting의 주제를 간략히 살펴 보고, 웹서버에서 사용할 인증서까지 준비해 보았다. 이어지는 장에서는 DPDK 기반의 사용자 영역에서 nginx 웹서버를 구동시키는 두가지 기법을 차례로 소개하고자 한다.


2. DPDK & F-Stack 기반의 Nginx 구동하기
DPDK가 고성능을 낼 수 있는 framework인 것은 맞지만, linux kernel stack을 bypass하는 관계로, 기존에 사용하던 다양한 application(server)을 재 사용하기 위해서는 linux kernel stack에 상응하는 것을 새로 구현해 주어야 하는 부담이 남는다. 

[그림 2.1] DPDK Overview
[출처 https://media.frnog.org/FRnOG_28/FRnOG_28-3.pdf etc.]

아래 project는 FreeBSD의 tcp/ip stack을 사용자 영역에서 동작하도록 porting하고, 이를 DPDK 위에 올려 사용하도록 해주는 open source project이다. 이번 장에서는 이를 사용해 보고, 느낀 바를 정리해 보고자 한다. 😃



[그림 2.2] F-Stack(FreeBSD Stack on DPDK)


[그림 2.3] F-Stack의 개념도

F-Stack(DPDK 내용 포함)에서 성능 향상을 꾀하기 위해 노력한 내용을 정리해 보면 다음과 같다.

1) Zero-copy 패킷 처리: 패킷은 NIC에서 애플리케이션으로 전송되는 DPDK 메모리 풀에 유지됨.

2) 잠금 없는 작업(Lock-free operation): 링 버퍼와 메모리 풀을 통해 잠금 경합을 방지함.

3) CPU 캐시 최적화: 스레드 고정(pinning) 및 NUMA 인식 기능을 통해 데이터를 로컬에 유지함.

4) 폴링 기반 I/O: 인터럽트 오버헤드와 컨텍스트 전환(context switching)을 제거함.

5) Cooperative multitasking: 마이크로 스레드가 자발적으로 양보하여 선점 비용(preemption cost)을 방지함.

6) 배칭(batching): 폴 주기당 여러 패킷을 처리함(vector processing으로 이해하면 됨).

7) NIC offloading: TCP 체크섬, 세그먼테이션 오프로드(TSO) 기능 사용
_______________________________________________________

2.1 F-Stack Nginx Build 하기
F-Stack 기반의 Nginx는 아래 그림과 같이 f-stack library(static)를 통합한 binary로 이해하면 된다. 자, 그럼 지금부터 f-stack 기반의 nginx를 build하는 과정을 살펴 보도록 하자.

[그림 2.4] F-Stack 기반 NGINX binary

<Ubuntu 24.04 LTS PC>
$ sudo apt-get install libnuma-dev
$ sudo pip3 install pyelftools
$ sudo apt install python3

$ git clone https://github.com/F-Stack/f-stack.git
cd f-stack

<dpdk build 하기>
f-stack code 안에는 이미 dpdk(23.11.5 version) source code가 준비되어 있다.

$ cd dpdk
$ meson -Denable_kmods=true -Ddisable_libs=flow_classify -Ddisable_drivers=crypto/openssl,net/ice build
$ ninja -C build
$ ninja -C build install
  -> /usr/lib/x86_64-linux-gnu/librte* 파일로 설치된다.

<f-stack library build 하기>
$ sudo apt-get install gawk
$ sudo apt install gcc make libssl-dev
   -> 이미 설치되어 있음.
$ pkg-config --version
1.8.1
    -> upgrade 불 필요
$ export FF_PATH=/mnt/hdd/workspace/f-stack
$ export PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/local/lib64/pkgconfig:/usr/lib/pkgconfig

$ cd f-stack/lib
$ make
$ ls -l *.a
-rw-rw-r-- 1 chyi chyi 4911188 11월  1 17:46 libfstack.a

$ sudo make install
- /usr/local/lib/libfstack.a
- /usr/local/include/ff_*.h
- /usr/local/bin/ff_start
- /etc/f-stack.conf

<nginx build 하기>
f-stack에는 (f-stack에 맞게 포팅된) nginx도 포함되어 있다. 이는 nginx code를 수정 없이 곧바로 사용하는 것은 아니라는 얘기다. 😂

$ sudo apt install libsystemd-dev
$ cd f-stack/app/nginx-1.25.2
$ bash ./configure --prefix=/usr/local/nginx_fstack --with-ff_module --with-http_ssl_module
$ make
$ sudo make install
$ ls -la /usr/local/nginx_fstack
  -> 이 디렉토리를 묶어서 target device로 복사하여 사용하도록 하자.
합계 24
drwxr-xr-x  6 root root 4096 11월  1 18:00 .
drwxr-xr-x 12 root root 4096 11월  1 18:00 ..
drwxr-xr-x  2 root root 4096 11월  1 18:00 conf
drwxr-xr-x  2 root root 4096 11월  1 18:00 html
drwxr-xr-x  2 root root 4096 11월  1 18:00 logs
drwxr-xr-x  2 root root 4096 11월  1 18:00 sbin

2.2 F-Stack Nginx 구동하기
F-Stack 관련 상세한 설명은 아래 wiki에 자세히 기술되어 있다. 👍


아래 내용은 target 장비에서 실행하도록 한다.

<dpdk 설정>
$ sudo sh -c 'echo 1024 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages'
$ sudo sh -c 'echo 1024 > /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages'
$ sudo sh -c 'echo 1024 > /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages'

$ mkdir /mnt/huge
$ mount -t hugetlbfs nodev /mnt/huge

$ sudo sh -c 'echo 0 > /proc/sys/kernel/randomize_va_space'

$ sudo modprobe vhost
$ sudo modprobe vhost-net
   -> 아래 rte_kni.ko는 dpdk에서 이미 제거된(deprecated) 상태이므로, 대신 이 방법(virtio_user)을 이용하기로 한다. 아래 (A), (B) 설정과 연관되어 있음.
insmod build/kernel/linux/kni/rte_kni.ko

[그림 2.5] DPDK에서 virtio-user를 사용하여 excpetional path 만들기 [출처 - 참고문헌 4]

$ sudo modprobe vfio-pci
$ sudo ifconfig enp10s0 down
$ sudo ifconfig enp17s0 down
$ sudo dpdk-devbind.py -b vfio-pci 0000:0a:00.0
$ sudo dpdk-devbind.py -b vfio-pci 0000:11:00.0

<f-stack 설정 변경하기>
$ sudo vi /usr/local/nginx_fstack/conf/f-stack.conf
  -> 필자가 변경한 내용만 기술하기로 한다.

port_list=0,1

[port0]
addr=192.168.5.254
netmask=255.255.255.0
broadcast=192.168.5.255
gateway=0.0.0.0

[port1]
addr=192.168.1.254
netmask=255.255.255.0
broadcast=192.168.1.255
gateway=192.168.1.1

[kni]
type=1  #rte_kni.ko 대신 virtio_user 방식을 사용 ................... (A)
enable=1
method=reject
tcp_port=80,443
#udp_port=53

~

<nginx 설정 변경하기>
sudo vi /usr/local/nginx_fstack/conf/nginx.conf
    -> 필자가 변경한 내용만 기술하기로 한다.
 server {
       listen       443 ssl;
       server_name slowboot.net vbox1.slowboot.net;

       #ssl_certificate /usr/local/nginx/etc/nginx/certs/slowboot.net.crt;
       #ssl_certificate_key /usr/local/nginx/etc/nginx/certs/private-cert.key;
       ssl_certificate /usr/local/nginx/etc/nginx/certs/ecdsa/server.crt;
       ssl_certificate_key /usr/local/nginx/etc/nginx/certs/ecdsa/server.key;


       # Optional: SSL optimizations and security headers
       ssl_protocols TLSv1.2 TLSv1.3;
       ssl_prefer_server_ciphers on;
       #ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
       ssl_ciphers HIGH:!aNULL:!MD5;
       ssl_session_cache shared:SSL:10m;
       ssl_session_timeout 10m;
       ssl_stapling_verify on;
       resolver 8.8.8.8 8.8.4.4 valid=300s; # Google DNS or your preferred DNS server
       resolver_timeout 5s;

       proxy_kernel_network_stack on;

       location / {
               proxy_pass http://10.10.1.1:5000;
               #proxy_pass http://192.168.1.1;
               proxy_http_version 1.1;
               proxy_pass_header Server;
               proxy_set_header X-Scheme $scheme;
               proxy_set_header Host $host;
               proxy_set_header X-Real-IP $remote_addr;
               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
               proxy_set_header X-Forwarded-Proto "https";
       }
~

$ sudo apt-get install libxdp-dev
  -> nginx 구동시에 에러가 발생하여 설치해 주었다.

$ sudo /usr/local/nginx_fstack/sbin/nginx
$ ps aux|grep nginx
root        1608  0.0  0.0 153568  4608 ?        Ss   05:32   0:00 nginx: master process /usr/local/nginx_fstack/
sbin/nginx
root        1609 84.1  0.0 67300112 12384 ?      RLl  05:32   0:02 nginx: master process /usr/local/nginx_fstack/
sbin/nginx

[그림 2.6] F-Stack 기반 NGINX 실행 모습 - CPU 점유율 100% 육박

f-stack 기반의 nginx가 구동되면, vethX interface(= tap interface)가 port 갯수만큼 자동으로 생성된다. 이와 관련해서는 아래 파일에 관련 내용이 상세히 설명되어 있다.

f-stack/dpdk/doc/guides/howto/virtio_user_as_exception_path.rst

$ sudo ip addr add 10.10.1.1/24 dev veth0    ................................... (B)
sudo ip link set dev veth0 up

sudo ip addr add 172.16.1.1/24 dev veth1
sudo ip link set dev veth1 up

2.3 F-Stack Nginx Reverse Proxy 시험하기
앞서와 같이 설정 후, web browser로 https 연결을 시도해 본다. 어라, 어디에 문제가 있는지, 접속은 되는데 매우 느리다. 😓

[그림 2.7] F-Stack 기반 Nginx의 Local Machine Reverse Proxy 구성

하지만, (설정을 바꾸어) 아래와 같이 외부 서버로의 reverse proxy 구성은 제대로 동작한다. 😎

[그림 2.8] F-Stack 기반 Nginx의 외부 서버 Reverse Proxy 구성

2.4 F-Stack Nginx에 대한 고찰

1) F-Stack의 가장 큰 문제는 동시에 여러 application(예: nginx, redis, ...)을 구동할 수 있는가의 여부이다(가령, Nginx, Redis를 동시에 실행).
2) Nginx, Redis 정도가 porting되어 있는데, 그 밖의 다른 application을 사용하려면, 적절한 porting 절차가 필요하다. 이 과정이 쉽지는 않은 듯 보인다.
3) Local Machine Reverse Proxy 구성에 문제가 있는지, 처리 속도가 느리다.


3. DPDK & VPP 기반의 Nginx 구동하기
이번에는 FD.io VPP 위에 NGINX 웹서버를 올리는 과정을 소개해 보도록 하겠다. 이와 관련해서는 사실 이전 posting을 통해서 한 차례 소개한 바가 있긴 하다(아래 link 3장).


따라서, 여기에서는 (부가적인 설명은 빼고) 간단히 설정 위주로 내용 진행을 하도록 하겠다.

3.1 VPP Host-Stack 기반의 Nginx 개요
그림만 봐도 무슨 얘기인지 금방 알 수가 있다. 😎

[그림 3.1] VPP 기반의 웹서버(1)

[그림 3.2] VPP 기반의 웹서버(2)


[그림 3.3] VPP Host Stack과 Nginx 웹 서버

3.2 VPP 기반의 Nginx 설정하기

<vpp 설정하기>
vpp# set int state TwoDotFiveGigabitEtherneta/0/0 up
vpp# set int ip address TwoDotFiveGigabitEtherneta/0/0 192.168.5.254/24
  -> lan port 설정
vpp# ip route add 0.0.0.0/0 via 192.168.5.1 TwoDotFiveGigabitEtherneta/0/0
  -> default gateway 설정

vpp# lcp create TwoDotFiveGigabitEtherneta/0/0 host-if eth0
vpp# lcp lcp-sync on

vpp# create host-interface name vpp1out
vpp# set int state host-vpp1out up
vpp# set int ip address host-vpp1out 10.10.1.2/24
  -> local machine backend 연결을 위해 필요한 설정을 한다.

<Linux Host 설정하기>
ip link add name vpp1out type veth peer name vpp1host
ip link set dev vpp1out up
ip link set dev vpp1host up
$ ip addr add 10.10.1.1/24 dev vpp1host
  -> host 쪽에서도 local machine backend 연결을 위해 필요한 설정을 해준다.

$ sudo vi /etc/vpp/vcl.conf
vcl {
  heapsize 64M
  segment-size 4000000000
  add-segment-size 4000000000
  rx-fifo-size 4000000
  tx-fifo-size 4000000
  app-socket-api /var/run/vpp/app_ns_sockets/default
}

~

<nginx 설정하기>
$ sudo vi /usr/local/nginx/conf/nginx.conf
  -> 아래와 같이 수정해 주도록 한다.

[그림 3.4] /usr/local/nginx/conf/nginx.conf 파일 수정

nginx를 구동하도록 하자.

$ sudo taskset -c 1-4 sh -c "LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvcl_ldpreload.so VCL_CONFIG=/etc/vpp/vcl.conf /usr/local/nginx/sbin/nginx -p /usr/local/nginx -c /usr/local/nginx/conf/nginx.conf"
  -> 실행 방법이 좀 복잡해 보이지만, 내용 자체는 간단한다.
  -> 즉, nginx로 하여금, 기존에 사용하던 POSIX API를 libvcl_ldpreload.so에서 새로 정의한 것을 이용하도록 강제하는 것으로 이해하면 된다.
  -> 또한 taskset -c 1-4 설정은 없어도 동작하며, cpu core를 지정하고자 할 때만 사용하면 된다.

[그림 3.5] nginx 실행 모습 - worker_processes 10 으로 설정한 예
📌 10개의 worker process를 실행하도록 한 경우, 각각의 process의 CPU 점유율이 100%에 육박하고 있다(여러 차례 확인해 보니, 항상 그런 것은 아님).

3.3 VPP 기반의 Nginx 동작 시험하기
먼저, local machine에 backend server를 구동시킨 상태에서 nginx reverse proxy 동작을 확인해 보기로 한다.

$ sudo vi  /usr/local/nginx/conf/nginx.conf
                location / {
                       proxy_pass http://10.10.1.1:5000;       #host interface
                       #proxy_pass http://192.168.5.10:5000;       #external server
                       ...
~

$ sudo killall nginx

$ sudo taskset -c 1-4 sh -c "LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvcl_ldpreload.so VCL_CONFIG=/etc/vpp/vcl.conf /usr/local/nginx/sbin/nginx -p /usr/local/nginx -c /usr/local/nginx/conf/nginx.conf"

이 상태에서 web browser에서 https://vppbox1.slowboot.net 연결을 시도해 보니, 정상 연결된다.

[그림 3.6] VPP 기반의 Nginx Local Machine Reverse Proxy 구성(1)


[그림 3.7] VPP 기반의 Nginx Local Machine Reverse Proxy 구성(2)

다음으로, 외부에 http server를 배치한 상태에서 역시 reverse proxy 기능이 정상 동작하는지 확인해 본다.

$ sudo vi  /usr/local/nginx/conf/nginx.conf
                location / {
                       proxy_pass http://192.168.5.10:5000;       #external server
                       #proxy_pass http://10.10.1.1:5000;       #host interface
                       ...
~

$ sudo killall nginx
$ sudo taskset -c 1-4 sh -c "LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libvcl_ldpreload.so VCL_CONFIG=/etc/vpp/vcl.conf /usr/local/nginx/sbin/nginx -p /usr/local/nginx -c /usr/local/nginx/conf/nginx.conf"

이 경우도 역시 정상적으로 연결된다.
[그림 3.8] VPP 기반의 Nginx 외부 서버 Reverse Proxy 구성

3.4 VPP 기반의 Nginx 동작 방식에 대한 고찰

1) [장점] 일단, 모든 경우의 시험에서 안정적으로 동작한다(원하는 설정에 이상이 없다).
2) [장점] F-Stack과 비교해서는 여러개의 application을 동시에 올릴 수가 있다. 물론, 각각의 application을 돌리려면 적절한 porting 작업이 선행되어야 한다(이는 f-stack과 비슷함).
3) [장점] VPP에서 제공하는 다양한 기능(NAT, ACL, QoS ... 등)을 함께 사용할 수 있다. 
4) [단점] F-Stack과 더불어 CPU 점유율이 늘 100%를 차지한다. 이는 앞으로 설명할 Kernel web server 방식과 비교하면 단점이 될 수 있다.


4. Linux Kernel 기반의 웹 서버 구동하기 - Tempesta FW
사용자 영역에서 동작하는 기존 웹서버 방식의 문제점을 해결하기 위한 마지막 방법으로, 이번 장에서는 온전히 kernel에서만 동작하는 web server project인 Tempesta FW(FrameWork or FireWall)를 소개해 보고자 한다. 앞선 장에서 소개한 DPDK 기반의 방식과 비교하여 어떤 차별 점이 있을지 귀추가 주목된다. 💢

[그림 4.1] Linux kernel에서 동작하는 웹 서버

4.1 Tempesta FW Overview
Tempesta FW는 tempesta tech사에서 개발하여 open한 프로젝트로, 시작된지는 10여년 정도 된 듯 보이며, 아직까지는 0.9 version을 개발 중인 상태이다.


사실, tempesta fw는 단순 웹서버(혹은 웹 가속기)가 아니다. FW라는 이름에서도 느낄 수 있듯이, 보안 기능이 가미된 ADC(Application Delivery Controller) 제품을 지향하고 있다.

[그림 4.2] Application Delivery Controller 개요


[그림 4.3] Linux kernel에서 동작하는 Tempesta FW ADC

Tempesta FW에서 웹서버 성능을 극대화시키기 위해 노력한 내용을 정리해 보면 다음과 같다.

1) Linux 커널과의 통합: Tempesta FW는 Linux TCP/IP 스택 내에서 작동하므로 Berkeley 소켓을 사용하는 표준 사용자 공간 애플리케이션과 관련된 많은 context switching, 잠금(lock) 및 대기열(queue)을 방지하고 있음.

2) Zero-copy 인터페이스: HTTP 메시지를 사용자 공간에 직접 매핑하여 커널과 사용자 공간 간에 데이터가 복사되지 않는 zero-copy 처리를 가능하게 하며, 이는 고성능 애플리케이션에 필수적임.

3) 고속 웹 캐시: Tempesta FW는 웹 캐시에 고도로 최적화된 in-memory 데이터베이스인 Tempesta DB를 사용함. 이 데이터베이스는 잠금 없는(lock-free) 데이터 구조 및 대용량 페이지(hugepage)와 같은 고급 기술을 활용하여 빠른 조회(lookup)를 구현하고 있음.

4) 더욱 빠른 TLS 가속: 맞춤형 TLS 구현은 OpenSSL과 같은 기존 사용자 공간 라이브러리보다 훨씬 빠르며, 최대 80% 더 빠른 처리 속도와 더 낮은 지연 시간을 제공함.

5) 효율적인 HTTP 파싱: 다른 웹 서버보다 성능이 뛰어난 더 빠른 HTTP 파서(Intel AVX2 등 활용)를 탑재하고 있으며, 향상된 보안 검사 기능을 포함하고 있음.

6) 멀티 코어 시스템에 최적화: Tempesta FW는 저수준 기술과 사용자 정의 데이터 구조를 사용하여 멀티 코어 머신의 다른 사용자 공간 서버에 영향을 미치는 확장 제한을 극복하고 있음.
📌 캬~ 이쯤되면 최강의 웹서버/가속기라는 얘긴데...
_____________________________________________________________________

Tempesta FW에 대한 자세한 사항은 위의 web page를 참조하기로 하고, 여기에서는 몇가지 그림을 제시하는 것으로, 대략적인 내부 구조를 유추해 보고자 한다(내용 중 일부는 사실과 다를 수 있다).
[그림 4.4] Tempesta FW 웹서버 & 가속기 내부 구조(1)


[그림 4.5] Tempesta FW 웹서버 & 가속기 내부 구조(2)

[그림 4.6] Tempesta FW 웹서버 & 가속기 내부 구조(3)

4.2 Tempesta FW kernel build 하기
그럼, 이제부터 아래 site 내용을 기초로 하여, tempesta fw용으로 patch된 linux kernel을 build 및 설치하는 과정을 알아 보기로 하자.


<Ubuntu 24.04 LTS>
  -> 이 버젼에서만 build가 가능하다.
sudo apt-get install make flex bison gcc g++ libboost-all-dev libssl-dev bc fakeroot dwarves libelf-dev lz4

sudo vi /etc/apt/sources.list
deb-src http://archive.ubuntu.com/ubuntu noble main restricted
~

sudo apt-get update
sudo apt-get build-dep linux

<tempesta patch된 kernel build하기>
git clone https://github.com/tempesta-tech/linux-6.12.12-tfw.git

<kernel config 조정하기>
cp /boot/config-$(uname -r)  .config
 -> Ubuntu 24.04에서 사용중인 .config 파일을 복사하여 사용하기로 한다.
vi .config
  -> (일반적으로 이렇게 .config file을 직접 편집하지는 않지만) 아래와 같이 몇가지 설정을 막도록 한다.
#CONFIG_SYSTEM_TRUSTED_KEYRING=y
#CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"

#CONFIG_DEFAULT_SECURITY_APPARMOR=y
#CONFIG_LSM="landlock,lockdown,yama,integrity,apparmor"

make olddefconfig
  -> 이 명령을 시행하면 아래와 같은 내용이 바뀌게 된다.
ONFIG_SYSTEM_TRUSTED_KEYRING=y
CONFIG_SYSTEM_TRUSTED_KEYS=""

CONFIG_DEFAULT_SECURITY_TEMPESTA=y
CONFIG_LSM="tempesta,landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"

CONFIG_SECURITY_TEMPESTA=y

  -> 또한, 아래 내용도 자동으로 설정된다.
CONFIG_SLUB
CONFIG_HUGETLB_PAGE
CONFIG_SECURITY
CONFIG_SECURITY_NETWORK
CONFIG_SECURITY_TEMPESTA
CONFIG_DEFAULT_SECURITY_TEMPESTA
CONFIG_LSM="tempesta,lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"

<kernel build 하기>
make -j$(nproc)
  -> kernel compile을 시도한다.

  CC      fs/notify/inotify/inotify_user.o
  CC      arch/x86/coco/tdx/tdx-shared.o
  CC      mm/kfence/report.o
  CC      ipc/msgutil.o
make[6]: *** 'certs/x509_revocation_list'에서 필요한 'debian/canonical-revoked-certs.pem' 타겟을 만들 규칙이 없습니다.  멈춤.
make[6]: *** 끝나지 않은 작업을 기다리고 있습니다....
  GEN     certs/blacklist_hash_list
make[5]: *** [scripts/Makefile.build:478: certs] 오류 2
make[5]: *** 끝나지 않은 작업을 기다리고 있습니다....
  CC      arch/x86/coco/sev/core.o
  LDS     arch/x86/entry/vdso/vdso.lds
  AS      arch/x86/entry/vdso/vdso-note.o
  AS      arch/x86/coco/tdx/tdcall.o
  CC      arch/x86/entry/vdso/vclock_gettime.o
  AR      arch/x86/coco/tdx/built-in.a
  CC      fs/crypto/crypto.o
-----------------------------------------------------------------------

위의 문제가 발생할 경우, 아래의 명령을 실행해 주도록 한다.

scripts/config --disable SYSTEM_REVOCATION_KEYS

make -j$(nproc)
...
*
* Restart config...
*
*
* Certificates for signature checking
*
File name or PKCS#11 URI of module signing key (MODULE_SIG_KEY) [certs/signing_key.pem] certs/signing_key.pem
Type of module signing key to be generated
> 1. RSA (MODULE_SIG_KEY_TYPE_RSA)
  2. ECDSA (MODULE_SIG_KEY_TYPE_ECDSA)
choice[1-2?]: 1
Provide system-wide ring of trusted keys (SYSTEM_TRUSTED_KEYRING) [Y/?] y
  Additional X.509 keys for default system keyring (SYSTEM_TRUSTED_KEYS) [] 
  Reserve area for inserting a certificate without recompiling (SYSTEM_EXTRA_CERTIFICATE) [Y/n/?] y
    Number of bytes to reserve for the extra certificate (SYSTEM_EXTRA_CERTIFICATE_SIZE) [4096] 4096
  Provide a keyring to which extra trustable keys may be added (SECONDARY_TRUSTED_KEYRING) [Y/n/?] y
    Only allow additional certs signed by keys on the builtin trusted keyring (SECONDARY_TRUSTED_KEYRING_SIGNED_BY_BUILTIN) [N/y/?] n
Provide system-wide ring of blacklisted keys (SYSTEM_BLACKLIST_KEYRING) [Y/n/?] y
  Hashes to be preloaded into the system blacklist keyring (SYSTEM_BLACKLIST_HASH_LIST) [] 
  Provide system-wide ring of revocation certificates (SYSTEM_REVOCATION_LIST) [Y/n/?] y
    X.509 certificates to be preloaded into the system blacklist keyring (SYSTEM_REVOCATION_KEYS) [] (NEW) <Enter 입력>
___________________________________________________________________________________________

<debian package 만들기>
make deb-pkg -j$(nproc)
  -> (설치 시 편리할 수 있으니) debian package를 만들자.

<debian package 생성 결과물>
cd ..
$ ls -la
합계 1742236
drwxrwxr-x  4 chyi chyi       4096 11월  5 14:54 .
drwxrwxr-x 27 chyi chyi       4096 11월  3 21:02 ..
drwxrwxr-x 28 chyi chyi       4096 11월  5 14:28 linux-6.12.12-tfw
-rw-r--r--  1 chyi chyi    9540910 11월  5 14:29 linux-headers-6.12.12+_6.12.12-g0a0690436855-3_amd64.deb
-rw-r--r--  1 chyi chyi 1408809824 11월  5 14:53 linux-image-6.12.12+-dbg_6.12.12-g0a0690436855-3_amd64.deb
-rw-r--r--  1 chyi chyi  119136996 11월  5 14:34 linux-image-6.12.12+_6.12.12-g0a0690436855-3_amd64.deb
-rw-r--r--  1 chyi chyi    1440522 11월  5 14:29 linux-libc-dev_6.12.12-g0a0690436855-3_amd64.deb
-rw-rw-r--  1 chyi chyi      74894 11월  5 11:59 linux-upstream_6.12.12-g0a0690436855-1.debian.tar.gz
-rw-rw-r--  1 chyi chyi       1488 11월  5 11:59 linux-upstream_6.12.12-g0a0690436855-1.dsc
-rw-rw-r--  1 chyi chyi      74956 11월  5 14:12 linux-upstream_6.12.12-g0a0690436855-3.debian.tar.gz
-rw-rw-r--  1 chyi chyi       1488 11월  5 14:12 linux-upstream_6.12.12-g0a0690436855-3.dsc
-rw-rw-r--  1 chyi chyi       9043 11월  5 14:54 linux-upstream_6.12.12-g0a0690436855-3_amd64.buildinfo
-rw-rw-r--  1 chyi chyi       3485 11월  5 14:54 linux-upstream_6.12.12-g0a0690436855-3_amd64.changes
-rw-rw-r--  2 chyi chyi  244910203 11월  5 11:58 linux-upstream_6.12.12-g0a0690436855.orig.tar.gz

<debian package 설치하기>
sudo dpkg -i ./linux-headers-6.12.12+_6.12.12-g0a0690436855-3_amd64.deb ./linux-image-6.12.12+*
  -> linux header와 linux image package(recovery image 포함)를 설치하자.
______________________________________________________________________________________________

교체된 kernel이 정상 동작하는지 확인하기 위해서는 PC를 reboot해 주어야 한다. 또한 부팅 시, <ESC> key를 눌러, grub 메뉴로 진입한 후, (Advanced 메뉴에서) 6.12.12+ kernel을 선택해 주도록 한다.

<부팅 후, kernel version 확인>
uname -a
Linux earth-new 6.12.12+ #3 SMP PREEMPT_DYNAMIC Wed Nov  5 14:19:04 KST 2025 x86_64 x86_64 x86_64 GNU/Linux

4.3 Tempesta FW build 및 설치하기
kernel이 준비되었으니, 이제 부터는 나머지 코드 즉, tempesta module & logger 등을 build할 차례이다.

<tempesta fw module 및 기타 program build 하기>
sudo apt-get install build-essential libboost-dev libboost-program-options-dev cmake ninja-build libfmt-dev libspdlog-dev pkgconf

git clone https://github.com/tempesta-tech/tempesta
  -> (주의) 0.8.2 version이 latest stable version이긴 하지만, 5.x kernel을 기준으로 되어 있는 만큼, (안정적이지는 않겠지만) 일단 master branch code를 내려 받아 테스트해 보도록 하자.

make clean
make
  -> build 절차는 상대적으로 간단하다.

정상적으로, build가 진행된 후에는 결과물을 적절히 설치해 주도록 하자.

<설치 디렉토리 위치 정리>
  -> 디렉토리는 위치는 임의의 위치로 변경 가능하다. 아래 내용은 release 버젼을 참조하여, 나름의 위치를 정해 본 것이다.

a) kernel module 4개
   => /usr/src/tempesta/db/core/tempesta_db.ko
   => /usr/src/tempesta/tls/tempesta_tls.ko
   => /usr/src/tempesta/fw/tempesta_fw.ko
   => /usr/src/tempesta/lib/tempesta_lib.ko

b) logger daemon
   => /usr/src/tempesta/logger/tfw_logger --config /etc/tempesta/tfw_logger.json
   => /usr/lib/x86_64-linux-gnu/libtdb.so
   => /usr/lib/x86_64-linux-gnu/libtus.so

c) configuration files
   => /etc/tempesta
-rw-r--r-- 1 root root 1227 11월  5 16:20 js_challenge.js.tpl
-rw-r--r-- 1 root root  485 11월  5 16:20 js_challenge.tpl
-rw-r--r-- 1 root root 3412 11월  5 17:54 tempesta_fw.conf
-rw-r--r-- 1 root root  305 11월  5 16:20 tfw_logger.json

d) 구동 script
   => /usr/lib/systemd/system/tempesta-fw.service

e) 기타 script
   => /lib/tempesta/scripts

-rwxr-xr-x 1 root root   2295 11월  6 14:31 check_conf.pl
-rwxr-xr-x 1 root root 240243 11월  6 14:31 checkpatch.pl
-rwxr-xr-x 1 root root   1362 11월  6 14:31 gcov_gather.sh
-rwxr-xr-x 1 root root   1002 11월  6 14:31 run-clang-tidy.sh
-rwxr-xr-x 1 root root   1881 11월  6 14:31 skmem.pl
-rw-r--r-- 1 root root  34997 11월  6 14:31 spelling.txt
-rwxr-xr-x 1 root root  14775 11월  6 14:31 tempesta.sh
-rw-r--r-- 1 root root   8030 11월  6 14:31 tfw_lib.sh
drwxr-xr-x 2 root root   4096 11월  6 14:31 troubleshooting
-rwxr-xr-x 1 root root   2330 11월  6 14:31 update_template.pl
___________________________________________________________________________

<tempesta fw 구동하기>
tempesta fw module & binary 등이 설치가 되었으니, 아래 명령으로 tempesta fw를 구동시켜 보도록 한다.
sudo systemctl start tempesta-fw
$ sudo systemctl status tempesta-fw
● tempesta-fw.service - High performance HTTP proxy server
     Loaded: loaded (/usr/lib/systemd/system/tempesta-fw.service; disabled; preset: enabled)
     Active: active (exited) since Wed 2025-11-05 16:26:02 KST; 9s ago
    Process: 7549 ExecStart=/lib/tempesta/scripts/tempesta.sh --start (code=exited, status=0/SUCCESS)
   Main PID: 7549 (code=exited, status=0/SUCCESS)
        CPU: 588ms

11월 05 16:26:02 earth-new tempesta.sh[7549]: Loading Tempesta kernel modules...
11월 05 16:26:02 earth-new tempesta.sh[7549]: Loading module tempesta_lib
11월 05 16:26:02 earth-new tempesta.sh[7549]: Loading module tempesta_tls
11월 05 16:26:02 earth-new tempesta.sh[7549]: Loading module tempesta_db
11월 05 16:26:02 earth-new tempesta.sh[7549]: Loading module tempesta_fw tfw_cfg_path=/lib/tempesta/etc/tempe>
11월 05 16:26:02 earth-new tempesta.sh[7549]: /lib/tempesta/scripts/tempesta.sh: line 110: /lib/tempesta/etc/>
11월 05 16:26:02 earth-new tempesta.sh[7549]: ...compile html templates for JS challenge
11월 05 16:26:02 earth-new tempesta.sh[7549]: ...start Tempesta FW
11월 05 16:26:02 earth-new tempesta.sh[7549]: done
11월 05 16:26:02 earth-new systemd[1]: Finished tempesta-fw.service - High performance HTTP proxy server.

lsmod |more
  -> tempesta-fw service를 구동시키면, 아래와 같이 4개의 kernel module이 올라온다.
Module                  Size  Used by
tempesta_fw          1232896  0
tempesta_db            98304  1 tempesta_fw
tempesta_tls          421888  1 tempesta_fw
tempesta_lib           12288  3 tempesta_fw,tempesta_tls,tempesta_db
...

sudo dmesg
  -> kernel log를 확인해 보면 다음과 같다.
...
[ 6288.336753] [tempesta fw] exiting...
[ 6288.405078] [tdb] Shutdown Tempesta DB
[ 6288.724896] [tempesta sc] OS configuration or hardware is not suitable/ (Total 2 errors)
[ 6288.724927] [tempesta sc]    1. Current loaded kernel is not suitable with TFW: 6.12.12+
[ 6288.724949] [tempesta sc]    2. Current loaded kernel has not suitable version: 6.12.12+, supported versions 6.12.12+
[ 6288.731683] [tempesta sc] Kernel config params are missing or have different values. Please, check configuration of `/boot/config-6.12.12+`/ (Total 4 errors)
[ 6288.731708] [tempesta sc]    1. `CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC={'y'}` is not defined.
[ 6288.731727] [tempesta sc]    2. `CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE={'1'}` is not defined.
[ 6288.731743] [tempesta sc]    3. `CONFIG_BOOTPARAM_HARDLOCKUP_PANIC={'y'}` is not defined.
[ 6288.731760] [tempesta sc]    4. `CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE={'1'}` is not defined.
[ 6288.731873] [tempesta sc] Sysctl config params are missing or have different values. Please, check configuration of `/etc/sysctl.conf`/ (Total 4 errors)
[ 6288.731909] [tempesta sc]    1. `kernel.panic={'1'}` is not defined.
[ 6288.731925] [tempesta sc]    2. `kernel.panic_on_oops={'1'}` is not defined.
[ 6288.731942] [tempesta sc]    3. `kernel.panic_on_rcu_stall={'1'}` is not defined.
[ 6288.731958] [tempesta sc]    4. `kernel.softlockup_panic={'1'}` is not defined.
[ 6288.731977] [tempesta sc] Verification passed with errors!
[ 6288.731996] [tempesta sc] Skipped Netconsole configuration and Support Server connection
[ 6288.816611] [tdb] Start Tempesta DB
[ 6288.845533] [tempesta fw] Initializing Tempesta FW kernel module...
[ 6289.069103] [tempesta fw] Configuration processing is completed.
[ 6289.090081] [tdb] Opened table /opt/tempesta/db/filter0.tdb: size=16777216 rec_size=20 base=00000000507685dd
[ 6289.200927] [tdb] Opened table /opt/tempesta/db/cache0.tdb: size=268435456 rec_size=0 base=00000000f9f97107
[ 6289.206915] [tdb] Opened table /opt/tempesta/db/sessions0.tdb: size=16777216 rec_size=312 base=0000000019909c00
[ 6289.213073] [tdb] Opened table /opt/tempesta/db/client0.tdb: size=16777216 rec_size=624 base=00000000057136d1
[ 6289.213131] [tempesta fw] Open listen socket on: 0.0.0.0:8080
[ 6289.222967] [tempesta fw] Tempesta FW is ready

OK, 여기까지 tempesta fw가 정상적으로 설치가 되었다.

4.4 Tempesta FW 동작 시험하기
tempesta fw는 nginx 수준의 configuration(/etc/tempesta/tempesta_fw.conf) 파일을 제공하고 있다. 이제 부터는 이 설정 값을 수정하여 실제로 tempesta fw가 정상 동작하는지를 확인할 차례이다.

a) Local Machine Reverse Proxy 연결 시험
먼저, tempesta_fw.conf 파일을 아래와 같이 수정하여, local server로 연결할 수 있도록 준비한다.

$ sudo vi /etc/tempesta/tempesta_fw.conf
listen 443 proto=h2,https;
tls_certificate /mnt/hdd/workspace/Tempesta_FW/ecdsa_cert/server.crt;
tls_certificate_key /mnt/hdd/workspace/Tempesta_FW/ecdsa_cert/server.key;

block_action error reply;
block_action attack reply;

frang_limits {
  request_rate 200;
  request_burst 25;
  tcp_connection_rate 100;
  tcp_connection_burst 10;
  concurrent_tcp_connections 500;
  http_methods head post put get delete;
}
keepalive_timeout 30;

srv_group srv_grp_1 { server 127.0.0.1:5000 conns_n=64; }

vhost main {
     proxy_pass srv_grp_1;
}
http_chain {
  -> main;
}

[그림 4.7] Tempesta FW configuration 파일 예

$ sudo systemctl restart tempesta-fw
환경 설정 값이 변경되었으니, tempesta module을 재구동하도록 한다.


[그림 4.8] Tempesta FW 웹서버 & 가속기 - Reverse Proxy#1

이 상태에서 웹 브라우져로 HTTPS 연결(https://ubuntu.slowboot.net)을 시도해 보니, 정상 동작한다.

b) 외부 서버 Reverse Proxy 연결 시험
이번에는, tempesta_fw.conf 파일을 아래와 같이 수정하여, 외부 server와 연결될 수 있도록 준비한다.

sudo vi /etc/tempesta/tempesta_fw.conf
listen 443 proto=h2,https;
tls_certificate /mnt/hdd/workspace/Tempesta_FW/ecdsa_cert/server.crt;
tls_certificate_key /mnt/hdd/workspace/Tempesta_FW/ecdsa_cert/server.key;

block_action error reply;
block_action attack reply;

frang_limits {
  request_rate 200;
  request_burst 25;
  tcp_connection_rate 100;
  tcp_connection_burst 10;
  concurrent_tcp_connections 500;
  http_methods head post put get delete;
}
keepalive_timeout 30;

srv_group srv_grp_1 { server 192.168.8.1 conns_n=64; }

vhost main {
     proxy_pass srv_grp_1;
}
http_chain {
  -> main;
}

sudo systemctl restart tempesta-fw

[그림 4.9] Tempesta FW 웹서버 & 가속기 - Reverse Proxy#2

OK, 역시 정상 동작한다.

4.5 Tempesta FW에 대한 고찰
그런데, 일단 stable version을 사용한게 아니라서 그런지, 가끔씩 system이 자동 reboot되는 심각한 문제가 보인다. Lastest stable version은 0.8.2인데, 관련 kernel이 5.x 버젼으로 되어 있어, 아직 테스트를 해보지 못했다. 😂

1) 문제점: master branch 버젼의 경우 아직 안정적이지 못하다.
2) 단점: TLS 1.3을 아직 지원하지 못하고 있다.
3) 장점#1: CPU 사용률이 거의 올라가지 않는다.
4) 장점#2: 실제로 많은 테스트를 해 보지는 않았지만, WAF & Filtering, DDoS attack 차단, web cache, load balancing 등 다양한 기능을 보유하고 있다.
5) 장점#3: tfw_logger + ClickHouse(DBMS)를 사용한 log 기능을 지원한다.
6) 총평: 안정성만 보장된다면, 꽤나 유의미한 solution이 될 수 있을 것으로 보인다. 😍
___________________________________________________________________________________


이상으로, 값싼 Intel appliance를 사용하여 고가(high price)의 고성능 web server를 만드는 방법을 대략적으로 알아 보았다. 

<아쉬운 점>
1) 실제 일정한 기준으로 3가지 방법의 성능을 측정하고 이를 비교하지 못한 점.
2) Tempesta FW의 경우 안정적인 버젼(kernel 5.x 버젼) 사용 및 추가 기능 시험 필요함.
3) 양자 내성 알고리즘 적용 시험(F-Stack, VPP nginx의 경우) 필요함.

늘 그렇지만, 초반에 계획했던 것 보다 아쉬운 점이 많이 보인다. 부족한 부분은 후일을 기약하기로 하며, 이것으로 이번 posting을 마치고자 한다. 끝까지 읽어주셔서 감사드린다. 😎

To be continued...


5. References
<f-stack>
[1] https://deepwiki.com/F-Stack/f-stack
[2] https://photonlibos.github.io/cn/blog/photon-dpdk?os=CentOS+7
[3] https://github.com/F-Stack/f-stack/wiki/%5BPhoton---F%E2%80%90Stack%5D-Coroutine-made-DPDK-dev-easy
[4] https://dpdk.readthedocs.io/en/v17.02/howto/virtio_user_as_exceptional_path.html
[5] https://seastar.io/

<vpp host-stack>
[6] https://wiki.fd.io/view/VPP/HostStack
[7] https://wiki.fd.io/images/0/08/Using_vpp_as_envoys_network_stack.pdf

<tempesta fw>
[8] quick start
[9] backend server 설정 
[10] tempesta TLS
[11] HTTP security
[12] Caching
[13] HTTP tables 
[14] logger + ClickHouse DBMS
[15] HTTPS 성능 및 인증서 설정 등

<certificate 관련>
[16] https://tack0829.tistory.com/12
[17] https://www.erianna.com/ecdsa-certificate-authorities-and-certificates-with-openssl/
[18] https://github.com/open-quantum-safe/oqs-provider/blob/main/USAGE.md#creating-keys-and-certificates
[19] https://docs.strongswan.org/docs/latest/pki/pkiQuickstart.html
[20] Real-World Cryptography, David Wong, Manning

[21] And, Google~

Slowboot

댓글 없음:

댓글 쓰기