SSD Nodes Learn 🎉 VPS $5.50/월부터
가이드 Matt Connor작성자 Matt Connor · 업데이트됨 2026-08-09

VPS에서 AES-NI 확인 및 성능 복구 방법

VPS에서 AES-NI 지원 여부를 확인하고 CPUID 마스킹으로 인한 성능 저하를 측정합니다. OPENSSL_ia32cap 환경 변수를 사용하여 하드웨어 가속을 강제로 활성화하고 AES-GCM 처리량을 최적화하는 방법을 상세히 설명합니다.

VPS에서 AES-NI가 실제로 제공하는 이점

VPS에서 AES-NI는 하드웨어 수준에서 AES(Advanced Encryption Standard)의 한 라운드를 수행하는 6개의 x86 명령어 세트입니다. 제공업체의 CPU 모델이 이 명령어를 숨기더라도 하드웨어 자체에는 포함되어 있지만, OpenSSL이 이를 인식하지 못하면 바이트당 약 10배의 사이클이 소요되는 소프트웨어 구현으로 대체됩니다. 명령어 한 줄로 기능을 확인하고, 두 줄로 성능 차이를 측정할 수 있으며, 환경 변수 하나로 빠른 경로를 강제로 활성화할 수 있는 경우가 많습니다.

해당 명령어는 AESENC, AESENCLAST, AESDEC, AESDECLAST, AESIMC, AESKEYGENASSIST입니다. Intel은 2010년에 이를 출시했고 AMD도 뒤를 이었으므로, 임대하는 대부분의 서버 CPU는 해당 실리콘을 탑재하고 있습니다. 보조 명령어인 PCLMULQDQ은 GCM(Galois/Counter Mode)이 인증 태그를 생성하는 데 필요한 캐리리스 곱셈(carry-less multiplication)을 수행합니다. 암호화와 태그 생성은 별개의 작업이므로, 두 명령어 모두 사용 가능할 때만 AES-GCM이 빠르게 동작합니다.

VPS에서 이 기능이 모니터링에 나타나는 4가지 영역은 다음과 같습니다.

  • TLS(Transport Layer Security) 종료. AES-128-GCM 또는 AES-256-GCM을 사용하는 웹 서버는 대량 암호화 작업 시간의 대부분을 AES 연산에 소비합니다.
  • 암호화된 볼륨. LUKS(Linux Unified Key Setup)와 dm-crypt는 커널 수준에서 모든 읽기 및 쓰기 작업 시 CPU를 사용하여 aes-xts를 실행합니다.
  • AES 기반 VPN 트래픽. AES-256-GCM을 사용하는 OpenVPN과 AES-GCM을 사용하는 IPsec 모두 이 기능에 의존합니다.
  • 암호화된 백업. 데이터를 외부로 전송하기 전에 AES로 암호화하는 모든 작업은 동일한 비용을 발생시킵니다.

영향을 전혀 받지 않는 일반적인 작업 부하도 있습니다. WireGuard는 데이터 처리에 ChaCha20-Poly1305를 사용하며 AES를 전혀 사용하지 않으므로, 자체 호스팅 WireGuard VPN은 해당 플래그가 마스킹된 호스트에서도 동일한 속도로 실행됩니다. 이러한 차이는 저렴한 VPS를 위한 터널링 방식을 선택하기 전에 WireGuard와 OpenVPN을 비교해야 하는 실질적인 이유가 됩니다.

VPS에서 AES-NI 지원 여부를 확인하는 방법

커널은 CPUID 기능 비트를 /proc/cpuinfo로 복사하므로, grep 명령 한 번으로 확인할 수 있습니다.

grep -m1 -o '\baes\b' /proc/cpuinfo
lscpu | grep -i -o '\baes\b'

두 명령 중 하나라도 aes를 출력한다면, CPU가 해당 게스트에게 AES-NI를 제공하고 있다는 의미입니다. 아무것도 출력되지 않는다면 지원하지 않는 것입니다. lscpu도 동일한 플래그를 읽으므로 두 명령의 결과는 항상 일치합니다. 설치되어 있는 명령어를 사용하십시오.

이제 호스트가 어떤 CPU를 사용 중이라고 알리는지 확인합니다.

grep -m1 'model name' /proc/cpuinfo

Intel(R) Xeon(R) Gold 6338 CPU @ 2.00GHz 또는 AMD EPYC 7443P 24-Core Processor과 같은 실제 모델 문자열이 출력된다면, 호스트가 물리적 CPU 모델을 게스트에게 그대로 전달하고 있는 것입니다. QEMU Virtual CPU version 2.5+ 또는 Common KVM processor이 출력된다면 다른 상황이 발생하고 있는 것이며, 이 경우 해당 내용을 파악해 둘 필요가 있습니다.

실리콘은 지원하는데 플래그가 누락되는 이유

CPUID는 프로그램이 CPU의 지원 기능을 확인하기 위해 사용하는 명령어입니다. 가상 머신 내부에서 CPUID는 항상 하이퍼바이저로 트랩(trap)되며, 따라서 게스트에게 어떤 정보를 전달할지는 하이퍼바이저가 결정합니다. 대부분의 제어 패널은 이 결정을 게스트 CPU 모델 형태로 노출합니다. qemu64kvm64는 범용 기준 모델이며, 두 모델 모두 기능 집합에 AES-NI나 SSSE3를 포함하지 않습니다. 따라서 물리 호스트가 최신 EPYC 프로세서라 하더라도 게스트는 aes 플래그를 확인할 수 없습니다. VPS는 타인의 하드웨어 위에서 동작하는 게스트이므로, 보고되는 모든 기능은 한 단계 상위 수준에서 결정된 결과입니다. 이러한 계층 구조가 생소하다면 VPS의 정의부터 확인하십시오.

호스트가 범용 모델을 선택하는 것은 의도적인 조치입니다. 프로세서가 서로 다른 머신 간의 라이브 마이그레이션은, 목적지 머신이 지원하지 않는 기능을 게스트가 사전에 인지하지 못했을 때만 정상적으로 작동하기 때문입니다. 이로 인한 성능 저하는 사용자의 몫입니다. 커널과 OpenSSL은 시작 시점에 마스킹된 CPUID를 한 번 읽어 들인 뒤, 프로세스가 종료될 때까지 느린 코드 경로를 선택하여 실행합니다.

근본적인 해결책은 호스트 측 설정에 있습니다. QEMU 기준으로는 -cpu host를 사용하거나, AES-NI를 포함하는 명명된 모델을 선택하거나, 모델 설정에 +aes를 명시적으로 추가해야 합니다. 게스트 내부에서는 이러한 설정을 변경할 수 없습니다. 지원 티켓을 생성하거나, 하이퍼바이저가 CPU 모델을 그대로 통과(pass-through)시키는 요금제를 선택하는 것이 지속 가능한 해결 방법입니다.

openssl speed로 성능 차이 측정하기

공개된 벤치마크 결과를 그대로 믿지 마십시오. 실제 서버에서 협상되는 암호화 방식을 직접 실행해 보아야 합니다.

openssl version
openssl speed -evp aes-128-gcm

결과 행은 AES-128-GCM으로 표시되며, 6가지 블록 크기에 대한 처리량을 초당 1000바이트 단위로 보여줍니다. 대용량 전송 성능을 확인하려면 8192바이트 열을 보십시오. 16바이트 열은 호출당 오버헤드의 영향이 커서 파일 다운로드 성능을 파악하는 데 적합하지 않습니다.

이제 AES-NI와 PCLMULQDQ을 소프트웨어에서 비활성화하고 동일한 명령을 실행합니다.

OPENSSL_ia32cap="~0x200000200000000" openssl speed -evp aes-128-gcm

이 값은 OpenSSL의 자체 기능 벡터 문서에서 가져온 것입니다. 앞의 ~은 "해당 비트를 지움"을 의미합니다. 57번 비트는 AES-NI이고 33번 비트는 PCLMULQDQ이므로, 0x200000200000000은 정확히 이 두 가지 비트만을 지칭합니다. 첫 번째 결과보다 두 번째 결과가 훨씬 낮게 나온다면 서버의 AES-NI가 정상 작동 중이며 측정이 완료된 것입니다. 두 숫자가 동일하다면 OpenSSL이 이미 소프트웨어 경로를 사용 중이었던 것이며, 이는 비활성화할 플래그가 애초에 존재하지 않았음을 의미합니다.

ChartAES-128-GCM throughput, one core, 8192-byte blocks (representative published figures)
The data behind this chart
[
  {
    "label": "AES-NI and PCLMULQDQ",
    "mb_per_sec": "4,850",
    "cycles_per_byte": 0.7
  },
  {
    "label": "Software fallback",
    "mb_per_sec": 310,
    "cycles_per_byte": 11.0
  }
]

위 수치는 특정 호스트에서 측정한 값이 아니라 3.4 GHz 근처의 현대적인 x86 코어에서 나타나는 대표적인 공개 수치입니다. 이 수치들은 전체적인 경향을 파악하는 용도로만 사용하십시오. 하드웨어 경로는 바이트당 약 0.7 사이클, 소프트웨어 대체 경로는 바이트당 약 11.0 사이클로 동작하며, 이는 코어당 각각 약 4,850 MB/s와 310 MB/s의 성능에 해당합니다. 위에서 실행한 두 명령의 결과만이 귀하의 서버 성능을 정확히 설명합니다. 이러한 원칙은 시스템의 다른 부분에도 동일하게 적용되므로, 요금제를 결정하기 전에 VPS 벤치마크를 반복 수행하는 방법을 함께 활용하십시오.

OPENSSL_ia32cap을 사용하여 비트 강제 활성화

이 부분에서 많은 사람이 놀라곤 합니다. AES-NI 명령어는 특권 명령이 아니며 하이퍼바이저가 이를 가로채지(trap) 않습니다. 오직 CPUID만 가로챕니다. 따라서 호스트는 게스트에게 AES-NI가 없다고 알릴 수 있지만, AESENC은 여전히 네이티브 속도로 완전히 실행됩니다. 소프트웨어는 CPUID에 물어본 뒤 잘못된 답변을 받았기 때문에 빠른 경로(fast path)를 건너뜁니다. 명령어 자체는 작동을 멈춘 적이 없습니다.

OpenSSL은 사용자가 CPU를 대신하여 답변할 수 있도록 허용합니다. OPENSSL_ia32cap에 16진수 값을 입력하면 기능을 마스킹하는 대신 기능 벡터(capability vector)를 덮어씁니다.

OPENSSL_ia32cap="0x0200020207000000" openssl speed -evp aes-128-gcm

만약 이 실행 결과가 일반 실행보다 몇 배 더 빠르다면, 하드웨어에는 AES-NI가 존재하지만 호스트가 이를 숨기고 있는 것입니다. 이는 진단 방법이기도 하지만, OpenSSL의 경우에는 해결책이 되기도 합니다.

16진수 값 구성 방법

첫 번째 논리 벡터는 CPUID leaf 1 EDX를 하위 32비트에, leaf 1 ECX를 상위 32비트에 배치합니다. 하위 절반에서 24번 비트는 FXSR, 25번은 SSE, 26번은 SSE2이며, 이는 0x07000000이 됩니다. 상위 절반에서 33번 비트는 PCLMULQDQ, 41번은 SSSE3, 57번은 AES-NI이며, 이는 0x02000202가 됩니다. 이를 합치면 0x0200020207000000이 됩니다. OpenSSL의 PCLMULQDQ 기반 GHASH는 바이트 교환을 위해 pshufb을 사용하므로 SSSE3가 목록에 포함됩니다. 일반적인 게스트 CPU 모델은 AES-NI와 함께 SSSE3도 숨기기 때문입니다.

두 가지 주의 사항이 있으며, 의도적으로 둘 다 발생시킬 수 있습니다.

첫 번째 벡터만 설정하면 이후 벡터는 0으로 남게 되어 AVX2 및 AVX-512 코드 경로가 비활성화됩니다. 이는 여기에서 의도된 동작입니다. 마스킹된 게스트에서 AVX 비트를 강제로 켜려고 하지 마십시오. AVX 레지스터는 운영 체제가 XCR0에서 확장 상태를 활성화해야 하는데, 커널이 동일한 마스킹된 CPUID를 근거로 이를 거부했기 때문입니다. VEX 인코딩 명령어가 실행되면 정의되지 않은 연산 코드 오류(undefined-opcode fault)가 발생하며 프로세스가 종료됩니다.

실제로 AES-NI가 없는 코어에서 AES-NI를 강제로 활성화하면 프로세스가 즉시 종료됩니다.

Illegal instruction (core dumped)

이는 AESENC이 정의되지 않은 연산 코드 오류를 발생시키는 상황입니다. 해당 코어에는 실행할 명령어가 없기 때문입니다. 일부 서버 펌웨어는 다음 재설정 전까지 하드웨어 수준에서 AES-NI를 비활성화할 수도 있으며, 증상은 동일합니다. 어느 경우든 해결책은 환경 변수를 바꾸는 것이 아니라 다른 호스트를 사용하는 것입니다.

장기 실행 서비스에 이 설정을 유지하려면 systemd 드롭인(drop-in) 파일을 사용하십시오.

sudo systemctl edit nginx
[Service]
Environment="OPENSSL_ia32cap=0x0200020207000000"
sudo systemctl daemon-reload
sudo systemctl restart nginx
sudo systemctl show nginx -p Environment

마지막 명령어를 실행하면 변수 값이 출력되어야 합니다. 이 설정이 의미하는 바를 이해해야 합니다. 만약 해당 서버가 AES-NI가 실제로 없는 CPU를 가진 호스트로 마이그레이션되면, nginx는 첫 번째 TLS 연결 시도 시 잘못된 명령어(illegal instruction) 오류로 종료됩니다. 이 내용을 런북(runbook)에 기록하거나, 운영 환경에는 이 설정을 적용하지 말고 티켓을 발행하여 문제를 증명할 때만 사용하십시오.

override가 해결하지 못하는 문제

OPENSSL_ia32cap은 OpenSSL에만 영향을 미칩니다. 다른 모든 소프트웨어는 자체적으로 기능을 탐색하며 해당 변수를 참조하지 않습니다.

커널이 중요한 사례입니다. dm-crypt와 LUKS는 커널 암호화 API를 사용하며, CPU 기능 비트가 없으면 aesni_intel 모듈이 로드되지 않습니다:

modprobe: ERROR: could not insert 'aesni_intel': No such device

이를 위한 사용자 공간 변수는 존재하지 않습니다. 커널은 부팅 시 CPUID를 한 번 읽으며, 다른 호스트에서 재부팅하기 전까지 그 결정은 유지됩니다. 따라서 OpenSSL의 설정과 관계없이 암호화된 볼륨은 소프트웨어 암호화 방식을 그대로 사용합니다. 실제로 어떤 성능이 나오는지 측정하십시오:

sudo cryptsetup benchmark -c aes-xts -s 256

aes-xts 256b 행의 수치는 하드웨어 AES를 사용하면 수천 MiB/s에 달하지만, 그렇지 않으면 수백 MiB/s 초반대에 머뭅니다. Go나 Java처럼 자체적인 탐색 기능을 갖춘 언어 런타임도 마찬가지로 이 변수의 영향을 받지 않습니다. Go의 crypto/aes는 CPUID를 직접 확인하며, 해당 비트가 비활성화되어 있으면 조용히 상수 시간(constant-time) 소프트웨어 구현을 사용합니다. TLS를 종료하는 서비스가 Go 바이너리라면, OpenSSL 변수를 변경해도 아무런 효과가 없습니다.

AES-NI를 사용할 수 없다면 ChaCha20을 우선하십시오

ChaCha20-Poly1305는 소프트웨어 환경에서 빠르게 동작하도록 설계되었습니다. AES-NI를 사용할 수 없는 코어에서는 일반적으로 AES-GCM보다 훨씬 뛰어난 성능을 보이므로, 이러한 호스트에서는 AES를 우선순위에서 제외하는 것이 합리적입니다.

OpenSSL 1.1.1 이상으로 빌드된 nginx 1.19.4 버전부터는 다음 설정을 사용합니다.

ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_conf_command Ciphersuites TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384;

ssl_ciphers는 TLS 1.2에 적용됩니다. ssl_conf_command Ciphersuites은 TLS 1.3에 적용되는데, nginx는 TLS 1.3에 대한 별도의 지시어를 제공하지 않으며 문자열을 검증 없이 OpenSSL로 직접 전달하므로 오타가 발생해도 경고 없이 수락됩니다. 설정을 다시 불러온 뒤 클라이언트가 어떤 암호화 방식을 제공받는지 확인하십시오.

sudo nginx -t && sudo systemctl reload nginx
openssl s_client -connect example.com:443 -tls1_3 </dev/null 2>/dev/null | grep -i cipher

정상적인 결과라면 TLS_CHACHA20_POLY1305_SHA256이 포함되어야 합니다. 변경 사항을 적용하기 전에 동일한 서버에서 openssl speed -evp chacha20-poly1305을 실행하여 AES와 성능을 비교한 뒤, 두 수치를 바탕으로 결정하십시오.

ARM 호스트는 다른 확장 기능을 사용합니다

AES-NI는 x86 전용입니다. ARM VPS는 동일한 작업을 수행하는 별도의 명령어 세트인 ARMv8 암호화 확장 기능을 사용합니다. aarch64 환경에서 해당 플래그는 flags이 아닌 Features 아래에 위치합니다:

grep -m1 Features /proc/cpuinfo

aespmull를 찾으십시오. pmullPCLMULQDQ에 대응하는 ARM 명령어이며, GCM은 동일한 이유로 이 명령어가 필요합니다. ARM에서 OpenSSL의 오버라이드 변수는 OPENSSL_armcap이며, OpenSSL 소스 코드의 crypto/arm_arch.h에 정의된 고유한 비트 레이아웃을 따릅니다. 따라서 이 가이드에 기재된 x86 16진수 값은 ARM 환경에서 아무런 의미가 없습니다. 실제 VPS 호스트로 판매되는 ARM 서버 코어는 이러한 확장 기능을 노출하므로, 마스킹된 기능 문제는 대부분 x86 환경에서 발생하는 현상입니다.

실패 유형 및 표시되는 문자열

aes이(가) /proc/cpuinfo에 없으며, 강제 실행이 훨씬 빠릅니다. 호스트가 CPUID를 마스킹하고 있습니다. 모델 이름이 일반적인지 확인한 다음, 하이퍼바이저가 어떤 게스트 CPU 모델을 제공하는지 공급자에게 문의하십시오.

aes이(가) /proc/cpuinfo에 없으며, 강제 실행 시 Illegal instruction이(가) 출력됩니다. 해당 명령어 세트가 실제로 없거나 펌웨어에서 비활성화된 상태입니다. 워크로드를 다른 호스트로 이전하십시오.

aes은(는) 존재하지만 처리량이 여전히 낮습니다. 8192-byte 열을 확인하고 있는지, 다른 프로세스가 해당 코어를 사용하고 있지 않은지 확인하십시오. 공유 플랜에서는 다른 사용자의 부하(noisy neighbour)가 CPU 기능 누락과 동일하게 나타날 수 있으므로, 하루 중 다른 시간대에 테스트를 두 번 실행해 보아야 합니다.

마스킹된 실행과 일반 실행의 결과값이 동일합니다. OpenSSL이 이미 소프트웨어 경로를 사용 중입니다. 해당 결과는 테스트 오류가 아니라 실제 측정값입니다.

중첩 가상화(Nested virtualisation)는 한 단계 아래의 결과를 변경합니다. 게스트 내부의 게스트는 중간 계층이 전달하기로 선택한 CPUID를 그대로 사용하게 되며, 이 과정에서 AES-NI가 인지하지 못한 채 누락되기 쉽습니다. VPS에서의 중첩 가상 머신을 실행하는 경우, 임대한 머신뿐만 아니라 내부 게스트 안에서도 플래그를 확인하십시오.

FAQ

VPS의 /proc/cpuinfo에 왜 aes 플래그가 없습니까?

하이퍼바이저가 일반적인 게스트 CPU 모델을 제공하기 때문입니다. qemu64kvm64는 기능 집합에 AES-NI를 포함하지 않으므로, 물리적 프로세서의 종류와 관계없이 CPUID는 해당 기능이 없는 것으로 보고합니다. 호스트가 이렇게 설정하는 이유는 실행 중인 게스트를 서로 다른 CPU를 가진 장비 간에 마이그레이션할 수 있도록 하기 위함입니다. grep -m1 'model name' /proc/cpuinfo을 실행해 보십시오. QEMU Virtual CPU version 2.5+이나 Common KVM processor과 같은 문자열이 나타난다면 가상화된 환경임을 의미하며, 실제 Xeon이나 EPYC 모델명이 나타난다면 CPU 모델이 그대로 전달되고 있으며 하드웨어적으로도 해당 플래그가 없는 것입니다.

OPENSSL_ia32cap은 실제로 AES-NI를 활성화합니까, 아니면 흉내만 냅니까?

실제 명령어를 활성화합니다. AES-NI 명령어는 비특권 명령어이며 하이퍼바이저는 이를 가로채지(trap) 않으므로, CPUID가 무엇을 보고하든 AESENC는 네이티브로 실행됩니다. 오직 CPUID 명령어만 가로채집니다. OPENSSL_ia32cap을 일반 16진수 값으로 설정하면 OpenSSL이 CPUID로부터 받은 응답을 대체하므로, OpenSSL은 하드웨어 코드 경로를 선택하고 하드웨어는 이를 최고 속도로 실행합니다. 만약 하드웨어에 해당 명령어가 실제로 없다면, 첫 번째 AES 연산 시 프로세스는 Illegal instruction (core dumped) 오류와 함께 종료됩니다.

이 오버라이드로 LUKS 암호화 볼륨의 속도가 빨라집니까?

아니요. OPENSSL_ia32cap는 OpenSSL만 읽으며 다른 곳에서는 사용하지 않습니다. LUKS와 dm-crypt는 커널 암호화 API를 사용하는데, 해당 기능 비트가 비활성화되어 있으면 aesni_intel 모듈이 modprobe: ERROR: could not insert 'aesni_intel': No such device 오류와 함께 로드되지 않습니다. 커널은 부팅 시 CPUID를 읽으며, 사용자 공간의 변수로는 이를 변경할 수 없습니다. sudo cryptsetup benchmark -c aes-xts -s 256로 실제 수치를 측정하고, 해당 플래그가 보고되는 호스트와 aes-xts 256b 행을 비교해 보십시오.

AES-NI 플래그가 없으면 WireGuard 속도가 느려집니까?

아니요. WireGuard는 모든 데이터에 ChaCha20-Poly1305를 사용하며 AES 명령어를 전혀 사용하지 않으므로, 마스킹된 호스트와 그렇지 않은 호스트에서의 처리량은 동일합니다. 반면 AES-GCM으로 설정된 OpenVPN이나 IPsec은 AES-NI가 없는 호스트에서 처리량이 저하됩니다. 따라서 동일한 VPS 내의 두 터널이라도 동작 방식이 크게 다를 수 있으므로, 네트워크 탓을 하기 전에 이 점을 인지해야 합니다.

ARM VPS에서 AES-NI를 확인하려면 어떻게 해야 합니까?

ARM 코어에는 AES-NI가 없습니다. 대신 ARMv8 암호화 확장(cryptographic extensions)이 있으며, 이는 다른 명령어를 사용하여 동일한 작업을 수행합니다. grep -m1 Features /proc/cpuinfo을 실행하여 aespmull를 확인하십시오. aarch64 환경에서는 flags이 아닌 Features 항목 아래에 나열되기 때문입니다. x86의 OPENSSL_ia32cap 값은 ARM에서 아무런 의미가 없습니다. ARM 환경에서 OpenSSL의 대응 변수는 OPENSSL_armcap이며, 비트 레이아웃은 OpenSSL 소스 코드의 crypto/arm_arch.h에 정의되어 있습니다.