자바스크립트를 활성화 해주세요

h050 Hugo 퍼포먼스테스트, 10만 Md파일 렌터링하기

 ·  ☕ 5 min read

100만개의 post가 있으면 렌더링하는데 얼마나 걸릴까 궁금했습니다. 원래 100만파일까지 블로그를 쓸 일은 없지만, 사전데이터를 이용하여 빌드하는 때에는 100만개 가까운 static html이 생성될 수도 있게다고 생각해서 시도했던 것입니다. 그래서 10만개의 markdown을 만들고 실험해 보았습니다.

10만번 loop

목적하는 파일의 수는 100만이지만, 시험상으로 10만개의 파일을 테스트 해 보았습니다.
10만번 loop를 하는 코드를 다음과 같이 준비했습니다.

1
2
3
4
5
[int]$Number = 100000

foreach($i in (1..$Number)){
    Write-Host ("{0,6:d6}" -f $i)
}

시간이 얼마나 걸리나요? 한 번 알아보았습니다. 함수로 정의하고 measure-command로 호출해 보았습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Function Make-DummyMD {
    Param(
        [int]$Number = 100000
    )

    foreach($i in (1..$Number)){
        Write-Host ("{0,6:d6}" -f $i)
    }
}

measure-command {Make-DummyMD}

결과

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Days              : 0
Hours             : 0
Minutes           : 1
Seconds           : 46
Milliseconds      : 117
Ticks             : 1061175564
TotalDays         : 0.00122821245833333
TotalHours        : 0.029477099
TotalMinutes      : 1.76862594
TotalSeconds      : 106.1175564
TotalMilliseconds : 106117.5564

1분 46초가 걸렸네요. powershell을 사용할 때는 runtime 퍼포먼스보다는 작성하는 사람의 퍼포먼스를 중시해서 사용합시다.

markdown의 본문을 만들려고 합니다.

git clone https://gitlab.com/pages/nfhugo

순서

git clone https://gitlab.com/pages/nfhugo

여기에 나와있는 다음의 순서대로 실행할 것입니다.

1. Install go and Hugo
2. Fork, clone or download this project
3. Preview your project: hugo server
4. Add content
5. Generate the website: hugo (optional)

1. Install go and Hugo

1번은 p047 문서를 참조하시면 실행을 빨리 하실 수 있습니다. 환경은 goormide를 이용합니다.

2. 사이트 생성

2번, site를 만들고 post를 추가합니다.

site를 만드는 방법은 공식적인 방법과 템플릿을 다운로드하는 방법이 있습니다.

  1. hugo new site [sitename]
  2. git clone https://gitlab.com/pages/nfhugo

여기에서는 첫 번째, 공식적인 방법을 이용하였습니다.
다음의 커맨드로 사이트를 만들고 테마를 설치했습니다. 아마도 블로그를 구성하고 테스트하면서 여러번 실행해본 커맨드일 것입니다.

1
2
3
4
5
6
hugo new site indigo_testsite
cd indigo_testsite
git init
git submodule add "https://github.com/AngeloStavrow/indigo.git" themes/indigo
git submodule update --recursive --init
# hugo server -D

두 번째 방법은 gitlab에서 제공하는 템플릿을 이용하여 만드는 방법입니다. nfhugo는 hugo와 netlify와 결합된 템플릿을 미리 만들어 둔 것입니다.

1
2
3
4
5
cd /workspace/test

#hugo new site blog && cd
git clone https://gitlab.com/pages/nfhugo
cd nfhugo

파일 생성

파일 생성은 hugo 명령어 hugo new $title를 이용하여 다음과 같이 합니다.
타이틀에 확장자 .md를 붙일 수도 있고 안붙일 수도 있습니다만, vscode로 편집을 한다면 있는 쪽이 좋습니다.

1
2
3
4
5
6
$Number = 100000
foreach($i in (1..$Number)){
    $title = ("t{0,6:d6} dummy title.md" -f $i)
    Write-Host $title
    hugo new $title
}

위의 코드는 매번 hugo new $title을 실행하는 것이므로 효율이 좋지 않습니다. 매우 오래 걸립니다. go언어의 설치와 hugo의 빌드시간을 합한 것보다도 훨씬 더 오래 걸립니다. 빠르지 않은 머신에서 프로그램을 실행하면 퍼포먼스를 확실하게 확인할 수 있는 장점(?)이 있습니다.

아예 지우고 다시 만들었습니다.

1
2
$files = Get-ChildItem -Recurse -Include *.md
remove-item $files.FullName

Powershell로 파일 다루기 기본편

파일을 만들고 내용을 읽고 지우는 것은 powershell에서는 어렵지 않습니다. 예를 들면 다음과 같습니다. 다음 예제는 만들고 지우기를 합니다.

1
2
3
4
5
6
7
"" > hello1.md
"" > hello2.md
"" > hello3.md
"" > hello4.md
"" > hello5.md
$files = Get-ChildItem -Recurse -Include *.md
remove-item $files.FullName

내용 구하기

무의미한 문자열 Lorem Ipsum 문자열을 https://www.lipsum.com/feed/html 에서 구했습니다. 이 문자열은 호출할 때마다 바뀝니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 <# using TLS 1.2 is vitally important #>
$req = Invoke-Webrequest -URI "www.lipsum.com/feed/html"
$loremIpsum = $req.ParsedHtml.getElementById('lipsum').textContent
$content = @"
---
subtitle: ""
tags: []
---
$loremIpsum
"@

아쉽게도 윈도우즈의 powershell 5.1 에서는 ParseHtml 이 존재합니다만, powershell core 6 부터는 존재하지 않습니다. linux에서도 사용해야 했기 때문에 iexplorer의 COM 객체를 넘기는 것이 불가능해졌기 때문입니다.

Pwsh Core에서는 PowerHTML

linux에서는 PowerHTML과 같은 thrid party를 이용해야 합니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

PS /root> Install-Module -Name PowerHTML

Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the
Set-PSRepository cmdlet. Are you sure you want to install the modules from 'PSGallery'?
[Y] Yes  [A] Yes to All  [N] No  [L] No to All  [S] Suspend  [?] Help (default is "N"): Y
PS /root> Import-Module PowerHTML
PS /root> Get-Module PowerHTML

ModuleType Version    PreRelease Name                                ExportedCommands
---------- -------    ---------- ----                                ----------------
Script     0.1.7                 PowerHTML                           ConvertFrom-Html

명령어는 ConvertFrom-Html 하나밖에 없군요.
ConvertFrom-Html -URI “https://www.lipsum.com/feed/html"

$req.ParsedHtml.getElementById(‘lipsum’).textContent
이제 준비된 문자열을 파일에 전부 입력합니다.

예제 파일 만들기 코드

1
2
3
4
5
$Number = 100000  # 10만
foreach($i in (1..$Number)){
    $title = ("t{0,7:d7} dummy title.md" -f $i)
    $content >> $title
}

결과1 100만파일 만들기 - 40분

파일을 만들기까지는 40분 걸렸습니다. 이 디렉토리는 가능하면 탐색기 같은 GUI 프로그램으로는 살펴보지 않는 쪽이 좋습니다. GUI Object를 너무 다량으로 만들어 시스템에 무리를 만들 수 있습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
PS C:\temp\nowhereblog1\content\posts>     Get-Date
>>     $Number = 1000000  # 100만
>>     foreach($i in (1..$Number)) {
>>         $filename = ("t{0,6:d6} dummy title.md" -f $i)
>>         $content >> $filename
>>     }
>>     Get-Date
>>

2020年8月13日 12:05:40
2020年8月13日 12:45:46

결과2 100만파일 빌드 - 중도포기

hugo커맨드로 build해 보았습니다.

1
2
3
4
PS C:\temp\nowhereblog1> Get-Date; hugo; Get-Date

2020年8月13日 12:52:51
Building sites 

빌드하는 것은 Single 코어만 사용하고 있는 듯 했는데, 멀티 코어를 사용하고 있는 시스템이어서 15% 정도의 CPU 사용률을 보였습니다.

그런데 메모리가 문제네요.

h050_memory.png

결국 빌드 시작한 후로 35분쯤 경과하여 중단할 수 밖에 없었습니다.

50만파일로 build 재시도

절반정도의 50만 파일을 지웠습니다. 지우는 것도 꽤 시간이 걸립니다. 혹시나 해서 pwsh이 아닌 cmd에서 삭제를 해봤는데 역시 오래걸립니다. 지울 때 시간을 재보지 않았는데 10분은 더 걸렸던 것 같습니다.

1
2
3
PS C:\temp\nowhereblog1\content\posts> 5..9 | % {del "t$($_)*.*"}
PS C:\temp\nowhereblog1\content\posts> (dir).count
500000

h050_resource_monitor.png

일단 안정적으로 보여서 잠시 다른 작업을 하고 나중에 와봤습니다.

그랬더니 로그인도 잘 되지 않아 어렵게 로그인을 한 뒤의 상태를 보니, 메모리가 이런 상태가 되어 있었습니다.

h050_resource_monitor2.png

빌드는 완성되지 않았습니다. 이미 2시간도 지난 뒤였기 때문에, 일단 빌드를 중단했습니다.

h050_resource_monitor3.png

10만파일로 build 재시도

10만 파일로 다시 시도를 해보니, 2시간이상 지나도 끝나지 않고, 메모리 사용률이 올라가는 속도는 더디었지만 9기가 이상으로 사용중이어서, 아슬아슬했습니다. 하지만 결과는 성공.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
C:\temp\nowhereblog1>hugo
Building sites …
                   |   EN
-------------------+---------
  Pages            | 100008
  Paginator pages  |  19998
  Non-page files   |      0
  Static files     |     64
  Processed images |      0
  Aliases          |      2
  Sitemaps         |      1
  Cleaned          |      0

Total in 8610330 ms

143분, 즉 2시간 40분만에 성공하였습니다.

단순히 선형계산이면, 1만파일이면 14분 걸리릴까 하고 예상합니다.

간단히 build된 /public/ 디렉토리를 serve해 브라우저에서 확인해 보았습니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@'
package main

import (
        "net/http"
)

func main() {
        http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("./public"))))
        http.ListenAndServe(":80", nil)
}
'@ > test.go
go run ./test.go

결과화면

결과화면입니다.

h050_server_demo.png

포스트 10개씩 1만개의 Page를 가진 CMS가 생성되었습니다.

레퍼런스

공유하기

tkim
글쓴이
tkim
Software Engineer