사용자 도구

사이트 도구


사이드바

메뉴:

블로그:

기타:


연락처:


안내:

blog:start

시니어

한동안 계속해서 구인하고 있습니다. 프로젝트가 어느 시점에 다다르면 빠르게 확장해야 합니다. 맨바닥부터 개발하기 시작1)해 어느 정도 기반을 갖추면 이제 본격적으로 기반 위에 컨텐츠를 쌓아올리고 한편으로는 아직 조금 이를 수 있는 런칭 준비를 해야 합니다. 런칭은 어처구니없이 높은 완성도를 요구합니다. 테슬라처럼 새로운 장르를 개척하고 차별화된 가치를 제공하지 않는 우리들은 문짝이 잘 안 맞는 상태로는 런칭 근처에도 갈 수가 없습니다. 이를 달성하기 위해 지금까지와 비교할 수 없을 정도로 많은 작업을 해야 하고 당장 사람들을 채용해서 이 거대한 완성도에 맞설 준비를 해야 합니다.

이런 시점에는 많은 주니어님들과 적은 시니어님들을 구인합니다. 주니어님들께 게임 컨텐츠를 크게 확장하고 방대한 게임 구석구석의 완성도를 올릴 임무를 맡깁니다. 시니어님들께는 이 확장과 완성도 업무를 지휘하고 각각의 수준을 끌어올리는 역할을 맡기게 됩니다. 주니어와 시니어를 구분하는 기준이 명확하지 않고 또 이런 연차에 따른 구분이 요즘 세상에 잘 맞지는 않습니다. 편의상 오래된 직급 체계를 기준으로 이야기하면 대강 대리급까지는 주니어로 보고 과장급부터는 시니어의 초입에 들어서기 시작했다고 보곤 합니다. 확장에는 이들 양쪽 모두의 도움이 필요합니다.

이런 과정에서 시니어님들이 제출해 주신 이력서를 살펴보며 몇 가지 느낀 점이 있습니다. 저 역시 이제부터 적는 아쉬움을 피할 수 있는 입장이 아닙니다. 아직까지는 감사하게도 회사에 고용되어 프로젝트에 기여하고 있어 당장 저를 시장에 노출시키지 않아도 되지만 모두들 잘 알고 계신 대로 언제 갑작스럽게 시장에 내던져질지 모릅니다. 그래서 이 아쉬운 점들은 미래에 스스로를 시장에 노출시킬 때2)를 맞이할 저 자신을 위한 글이기도 합니다.

업계에서 긴 시간 동안 일을 계속하고 있다는 것은 어쨌든 업무능력을 어느 정도 인정받았다는 의미로 해석할 수 있습니다. 물론 어떤 사례를 보면 어떻게 이런 수준으로 살아남아 있는지 의심스러울 때가 없는 것은 아니지만 대체로 그렇다는 이야기입니다. 시장에서 업무능력을 검증받는데 실패하면 시장에 살아남아 있을 수가 없습니다. 아니면 아주 가난한 프로젝트에 고용되어 간신히 숨만 쉬며 먹고사는 수준에 머무르든지요. 그래서 위에서도 기간에 따른 주니어와 시니어의 구분이 항상 옳은 것은 아니라고 이야기했습니다. 하지만 한편으로는 어쨌든 일정 수준 이상의 프로젝트에 고용되어 긴 기간 살아남았다면 그게 업무능력이든 정치적인 면이든 뭐든지간에 살아남을만한 이유가 있다는 이야기입니다. 이 말을 뒤집어 생각해보면 어느 정도 연차를 쌓고 나면 업무능력만으로는 충분한 경쟁력을 가지기 어렵다는 의미이기도 합니다.

연차가 쌓임에 따라 업무능력이 올라 프로젝트에 개인이 기여할 수 있는 한계를 맞이하면 그 다음부터는 개인으로써 업무능력을 갈고닦아도 더이상 프로젝트에 기여하는 수준을 끌어올릴 수 없게 됩니다. 여기부터는 나를 포함한 주변의 다른 개인들에게 영향을 끼쳐야 합니다. 이제부터는 업무능력 이외에 업무 맥락을 파악하고 더 넓은 시야로부터 비롯된 인사이트가 필요합니다. 프로젝트 구성원 개인들은 게임 구석구석을 훑으며 완성도를 올릴 온갖 방법을 찾아 실행하고 있습니다. 자연스럽게 이들의 시야는 좁아지고 한 발 물러나서 보면 생각보다 간단한 일에 매몰되어 고통받는 경우가 많습니다. 개인 수준을 넘어 프로젝트에 기여하기 위해서는 구성원 개개인이 하루하루 업무 수행을 위해 가질 수밖에 없는 시야와 맥락 파악을 도와줄 수 있어야 합니다. 이 도움에는 반드시 더 넓은 시야에 의한 인사이트가 필요합니다.

시니어의 서류에 이런 점이 드러나지 않는다면 이 문서는 지독하게 평이해지고 맙니다. 맡은 일을 훌륭하게 수행해낼 수 있음은 물론 그 자체고 훌륭하기는 하지만 이 분을 시니어로써 구인할 수 있을지 의문을 가지게 만듭니다. 또 이분을 채용한다 하더라도 이 분을 시니어로써 대우하지 않을 수 있습니다. 만약 시니어 이상의 연차로써 시장에 자신을 노출시켜야 한다면 업무능력 이외의 뭔가를 이야기할 수 있어야 합니다. 업무 각각의 맥락을 이해하고 업무 자체의 목표 뿐 아니라 업무 자체가 충분히 표현하고 있지 않은 그 바깥의 목적을 달성하는데도 기여해야 합니다. 또 목표를 방해하는 이상한 업무에 의문을 가질 수 있어야 하고 나아가 이를 바로잡아야 합니다. 이를 어필하지 않으면 시장에서 시니어로 인지되지 않을 겁니다.

흔히 경력자 구인은 자기소개서보다는 경력기술서가 더 의미있다고 알려져 있습니다. 어느 정도는 맞는 말입니다. 하지만 현대에 모국어가 한국어이면서도 한국어를 충분히 잘 구사하지 못하는 사람들이 많아 어려움을 겪다 보면 자기소개서가 자기소개 이상의 역할을 하기도 합니다. 자기소개서는 좀 딱딱하게 느껴지기는 하지만 자신이 평균 이상의 한국어 구사능력을 갖추고 생각한 바를 조리있게 글로 표현할 수 있으며 자기소개서라는 좁은 시야를 벗어난 이야기를 자연스럽게 이어갈 수 있다는 사실을 어필할 훌륭한 기회입니다. 만약 주니어님들의 자기소개서라면 '일남일녀의 장남으로 태어나…'로 시작하는 자기소개서를 웃으며 넘어갈 수 있지만 시니어의 자기소개서에 전통적인 의미의 진짜 자기소개를 하면 안됩니다. 시니어의 자기소개서는 자유 주제 에세이라고 보는 편이 더 적당합니다.

솔직히 시니어를 지인찬스 없이 구인하려는 시도는 투입한 시간과 노력에 비해 처참하게 실패하는 중입니다. 구인을 꿈꾸는 시니어들은 이미 어딘가에 단단히 고용되어 있어 시장에 나타나지 않고 지인찬스를 통과하는 시니어들은 아주 잠깐 동안만 시장에 나타날 뿐입니다. 우물쭈물하다가는 순식간에 다른 프로젝트로 사라져버립니다. 이 과정을 거치지 않은 시니어들을 구인해 보려고 하지만 위에 이야기한 여러 가지 아쉬움으로 채용을 결정하기 쉽지 않습니다. 우리는 런칭을 위해 확장을 해야만 하는 상황이고 여기서 구인의 실패는 프로젝트의 실패로 이어질 수 있는 중대한 문제입니다. 그러니 시니어로써 구직하실 때 이런 점들을 조금 고려해주시면 좋겠습니다. 물론 저 자신도 다음번 시장에 나타날 때 이 점들을 신경쓸 겁니다.

· 2020-06-28 22:38

Dokuwiki Loadbalaning

Load balancing was primarily understood by the services of a certain size to serve more requests. This wiki I have been using for a long time has become much larger than initially expected, and as I have become increasingly dependent on it, I have started adding some requirements. For example, it was necessary to reduce the chance of losing data, and service should always be maintained. The chance of data loss is reduced by using a disk that can be detached from the instance and taking daily snapshots of the entire instance, including this disk. However, the requirement that the service should always be maintained was not easy to achieve. My previous experience with failure has led me to realize that Lightsail instances can fail more often than I thought, and I used to experiment with this on wikis and web servers on my own to break services. I used to do something on my own, and after breaking the wiki, I was frustrated that I could not find a way to use my wiki.

Then I came to the conclusion that to meet the second requirement that this service should always be maintained, I had to add servers and configure them to load balance. I have written about adding servers and setting up load balancing3) after the last failure. However, I have never talked about the Dokuwiki load balancing itself. So I decided to write an article that covers this topic precisely because it was difficult to find an article that tried the same with me no matter how much Google ringing this setting. This article is about 'Dokuwiki through load balancing'.

Background

Dokuwiki works on a file system basis. This is both an advantage and a disadvantage of Dokuwiki. Although both simples to install and use, it is platform-dependent and makes it difficult to choose a reliable scaling method like a database when trying to scale through load balancing. Although there is a document on the official Dokuwiki website that addresses the scalability caused by Dokuwiki's operation on a file system basis4), this document itself does not actually explain the use of Dokuwiki by cultivating it for a larger service. This document just talks about how big Dokuwiki exists in the world, and that this wiki is capable of operating at such a large number of documents, and does not explain the situation in which users must continue to operate in the event of an increase or failure.

Scale

This wiki is divided into public and private parts. Dokuwiki provides a feature called a farm that runs multiple wikis by installing one set of scripts and setting up different data directories based on it, but I am not using it. Based on the namespace, some parts are public and private, and they are separated by ACLs supported by Dokuwiki itself. To increase the level of security, Cloudflare Access is used to access additional private parts, requiring additional authentication and enabling a specific VPN to log in5). None of these are required in the public part. Wiki is about 20 gigabytes of text and images, except for cache files, and It using 32-gigabyte disks. The number of registered users is 10 or less, and the average number of edits per day is about 50.

Subjects

Load balancing

If you are just getting bigger wikis or more users, you can simply fix the problem using the larger Lightsail bundle. I am using the 2GB memory bundle, Until now, I don't feel particularly lacking in digesting the features I need for Dokuwiki. In particular, Cloudflare handles requests that the web server does not need to receive directly in front of the webserver6), so I do not feel the need for a bigger web server. As I said above, I wanted to reduce service outages and continue to use the wiki in the event of an outage by me, and I came to the conclusion that to meet this requirement, I had to increase the number of instances instead of the larger instances.

Among the load balancing services that can be easily found right now where services provided by Lightsail and Cloudflare. With Lightsail, you could build load balancing regardless of the number of servers, a number of requests, and traffic at a fixed cost of $18 per month7). Cloudflare can start load balancing with two servers for $5 per month but increases the cost by increasing the number of servers or increasing the number of requests8). In my case, I choose this because Cloudflare's pricing was cheaper because My scale is smaller. If it grows in size someday, there is room for migration to the flat rate load balancing offered by Lightsail.

Cloudflare Load Balancing

Cloudflare's load balancing does not use the API it requires IP addresses as many as the number of servers in order to use it without setting DNS separately on the Lightsail side. Currently, I building load balancing with two Lightsail instances, and the two instances have separate IP addresses and they are accessed through Cloudflare's DNS settings. Each server has to be set as A record and must be accessed separately through sub-domain, and load balancing is set based on this. The load balancing setup first creates a server pool and adds each of the servers to be in that pool. What you can do with $5 per month is one pool and up to two servers in it. Exactly what I am doing right now can be handled with this amount. Here, to increase the number of pools or increase the number of servers, the cost increases by about $5.

Once set up, it will be deployed within seconds, and once you enter the domain name, one of the two servers will start responding. With the session affinity9) setting, even if there are multiple servers, the server that initially responds for a certain period of time continues to respond, making it easy to maintain login and synchronize.

Synchronization

Because Dokuwiki works on a file system basis, it creates a number of concerns when expanding in this way. When I Googled, I want to expand Dokuwiki, but since it is based on a file system, there are articles10) saying that I am not sure what to do. Perhaps experts who know how to fit the situation have already left Dokuwiki or have already solved the problem via a network filesystem, but this article has not appeared on Google. I wasn't expert enough to set this up, and will not go to leave Dokuwiki for a while, so I had to find a way to fix it. To be precise, I had to decide how to synchronize the file system between the two servers and run it.

As I said in the previous article, I put unison11) into crontab and run it in a short cycle. Basically, I think synchronization should be the most secure and the most efficient way to reduce waste when the file system is changed. But for some reason, people with the requirement to sync the filesystems shown on Google were using more methods to perform synchronization in a shorter period by using tricks on the crontab12), which can only be done for up to 1 minute. Once I did it right now, it seemed a lot easier, so I followed the same thing and there haven't been any incidents so far.

However, it can potentially cause problems if one sync operation is longer than the minimum time unit in which synchronization occurs. unison runs on only one of the two servers. The other one just syncs but doesn't run it on its own. If we need to increase the number of instances in the future, I plan to respond by adding synchronization settings to instances that are expanded based on the 'Synchronizing Side' instance and running synchronization.

Certification

To maintain the security of the entire service, certificates are required in three sections. One is between client and Cloudflare. I use a free certificate provided by Cloudflare. In desktop browsers, you can click on the padlock icon next to the address to view the certificate information, but here I see the Cloudflare domain name instead of my domain name. I thought it does not matter. And it fully achieves the original purpose of encrypting traffic in this section.

The other two are between two servers and a Cloudflare server. Initially, Let's Encrypt certificates were issued and used, it was possible to automate the issuance and maintenance of certificates for each sub-domain. But I switched to using the origin certificate provided by Cloudflare. This origin certificate is signed by Cloudflare and the certificate is valid itself, but not valid for all authentication chain from the root certificate. So, if you use this certificate and without going through the Cloudflare, the authentication is marked as broken. However, since all requests are always going through the Cloudflare, the request was never marked as broken to the client, and the validity of the certificate was long, so it was less administrative and could achieve the purpose of encrypting between the Cloudflare and two origin servers.

Code distribution

Data and settings are synchronized in short cycles. But the Dokuwiki script didn't seem to do that. I frequently experimented with web servers and wikis, and if the experiment failed, the service would be temporarily stopped. At this time, if the Dokuwiki script was also synchronized, problems from one server could quickly spread to another server. However, I had to modify the code and I needed a way to deploy it comfortably. Perhaps the experts have another way, but I'm distributing the code by creating a repository on Github13). First, create an instance with a snapshot to modify and test the code there. If it does not seem to have any problems here push it to Github, deploy it to only one of the two servers, and let it go for a while. If it still doesn't seem like much, deploy it to the other one.

This method was used a while ago when applying14) the new RC15) of Dokuwiki. First I created a branch, applied the new code, then reviewed it, and tested it for some time, reflecting only on the newly created instance. I didn't have a big problem, and the third RC decided that all the changes were for code cleanup and deployed them on a daily basis on each server. Of course, this is because the major version update does not migrate the filesystem. If a new version of the future migrates a filesystem, it will not be possible to deploy one by one in this way.

Problems

Synchronization interval

File system synchronization occurs over time. Now it happens once every 10 seconds. In the first, 1-minute interval setting, there was a case where a page was written on one side but was not reflected on the other side, so there was a page that was not available when sharing an address. So for some time, when I shared the address, I opened the subdomains of all the servers to see if the posts appeared on both sides, and then shared the address. I have reduced the synchronization interval no. and It is working reliably to some extent, no longer checking every server before sharing the address.

If the number of users increases and the same page is modified on different servers at intervals shorter than the synchronization interval, the data of the side that was modified earlier will be lost. It was very difficult to merge when corrections occurred on both sides at the same time automatically. I'm simply overwriting the revised side later, but on my scale, there haven't been any problems yet, but it's not without problems. Potentially modified pages may lose their modifications.

External edit

Dokuwiki has a function that detects edits that Dokuwiki does not know through the time stamp of the file system. If someone or some other app opens and edits the Dokuwiki data file directly, there will be a difference between the time stamp recorded inside the file and the timestamp of the file itself. The next time you try to edit this page while in this state, It will first make a new revision of the changes that Dokuwiki doesn't know about, then open this revision to begin editing. However, in the environment of synchronizing files, the side that synchronizes the modifications will always have a wrong timestamp. On one server, the timestamp inside the file will match on the file itself, but on the other server, the timestamp of the file itself will point to a more future perspective, leaving an 'External edit' record at all times. The two servers that are synchronized always show the same revision history, but I didn't want to call the detectExternalEdit function16) because I didn't want to register the revisions that occurred. However, this setting will prevent you from recording this revision when external editing actually occurs.

Another external edit issue is that if the latest revision of a page has been synced from another server, the user who modified this page appears as 'External edit'. This is not recorded in the revision, but when the next modification occurs, it is normally recorded in the revision, but the newest page displays the edited user information normally on one server, while on the other server it is displayed as 'external edit' instead of the modified user information. This seems to be modifiable in the template output code, but I didn't fix it because I didn't understand why there was no user information because it was supposed to be external edit when no user information edited the page.

Session affinity

As I solved this external edit issue, I noticed a situation where the session affinity function was working. Session affinity is a function that continuously routes users accessing either server to the server through load balancing. Without this feature, a user who just logged in to one server may be routed to another server immediately after logging in and may be asked to log in again. When the page is edited and saved in connection with the external editing problem above, the user information is displayed in the page history, but when you access the page again after a while, it is sometimes changed to 'external edit'. This means that I was routed from one server to another while I was refreshing and using the wiki.

Conclusion

If Dokuwiki had used a database, I would have been talking about database replication for most of the load balancing. On the other hand, instead of using another tool like unison, you could just use the functionality of the database itself to build more stable load balancing without the potential problems discussed above. However, on my scale, I built Dokuwiki load balancing that works anyway, including potential issues, and it's been running without problems for a while. I can increase the number of servers to be synchronized and to reduce the possibility of data loss, I can back up the servers to synchronization. If one server is lost in the future, it can be recovered from data from the other server or from data in another Availability Zone. I believe that I have achieved some of the two goals mentioned above.

· 2020-06-28 18:18

도쿠위키 로드밸런싱

로드밸런싱은 주로 어느 정도 규모 있는 서비스가 더 많은 요청을 처리하기 위해 하는 것으로 알고 있었습니다. 오랫동안 사용해오던 위키의 규모가 처음 예상한 것보다 훨씬 커지고 이에 따라 의존성이 점점 높아지면서 몇 가지 요구사항이 추가되기 시작했습니다. 가령 데이터를 유실할 가능성을 줄여야 했고 서비스가 항상 유지되어야 했습니다. 데이터 유실 가능성은 인스턴스와 분리할 수 있는 디스크를 사용하고 이 디스크를 포함한 인스턴스 전체의 스냅샷을 매일 생성하는 정도로 줄이고 있습니다. 하지만 서비스가 항상 유지되어야 하는 요구사항은 달성하기 쉽지 않았습니다. 지난번의 장애 경험으로 라이트세일 인스턴스에 생각보다 더 자주 장애가 발생할 수 있다는 점을 깨닫게 되었고 저 스스로 위키와 웹서버에 이것 저것 실험해보면서 서비스를 망가뜨리곤 했습니다. 제 스스로 뭔가 시도하다가 위키를 망가뜨린 다음 이 상황을 벗어날 방법을 위키에서 검색할 수 없는 상황에 좌절하곤 했습니다.

그러다가 이 서비스가 항상 유지되어야 하는 두 번째 요구사항을 만족하려면 서버를 추가하고 로드밸런싱 하도록 구성해야 한다는 결론에 다다랐습니다. 지난번에 장애를 겪은 다음 서버를 추가하고 로드밸런싱을 설정한 이야기를 한 적이 있습니다만17) 도쿠위키 로드밸런싱을 주제로 직접 이야기한 적은 없는 것 같고 또 이 설정을 하면서 아무리 구글링을 해도 저와 같은 시도를 한 글을 찾기 어려워서 정확히 이 주제를 다룬 글을 남겨놓기로 했습니다. 이 글은 '도쿠위키를 로드밸런싱을 통해 서비스'하는 내용입니다.

배경

도쿠위키는 파일시스템 기반으로 동작합니다. 이 자체가 도쿠위키의 장점이기도 하고 단점이기도 합니다. 설치와 사용 모두 단순하지만 플랫폼에 영향을 받고 또 지금처럼 로드밸런싱을 통해 확장하려고 할 때 데이터베이스처럼 안정적인 확장 방법을 선택하기 어렵게 만듭니다. 도쿠위키 공식 웹사이트에는 도쿠위키가 파일시스템 기반으로 동작하기 때문에 생기는 확장성에 대한 걱정에 답하는 문서18)가 있기는 하지만 이 문서 자체가 실제로 도쿠위키를 더 큰 서비스에 학장해서 사용하는 사례를 설명하고 있지는 않습니다. 또 이 문서는 단지 세상에 얼마나 큰 도쿠위키가 존재하는지, 또 이 위키가 그런 큰 규모로 동작 가능함을 이야기할 뿐 사용자가 늘어나거나 장애상황에도 지속적으로 동작해야 하는 상황을 설명하지는 않고 있었습니다.

규모

이 위키는 공개된 부분과 공개되지 않은 부분으로 나뉩니다. 도쿠위키는 스크립트 한 세트를 설치해 놓고 이를 기반으로 서로 다른 데이터 디렉토리를 설정해 여러 위키를 구동하는 이라는 기능을 제공하지만 그렇게 사용하지는 않고 있습니다. 네임스페이스를 기준으로 공개된 영역과 그렇지 않은 영역이 있고 이들을 도쿠위키에서 지원하는 ACL19)을 통해 구분하고 있습니다. 이 위에 보안 수준을 올리기 위해 클라우드플레어 액세스를 사용해 공개되지 않은 부분에 접근할 때 추가로 인증을 요구하고 로그인에는 특정 VPN을 사용하도록 설정20)했습니다. 공개된 영역에는 이들 중 어느 것도 요구하지 않고 있습니다. 위키는 캐시파일을 제외하고 텍스트와 이미지를 모두 합쳐 약 20기가 정도이고 라이트세일에서 32기가짜리 디스크를 사용해 서비스하고 있습니다. 등록된 사용자는 10명 이하, 하루 평균 편집 횟수는 50회 정도입니다.

주제

로드밸런싱

단순히 위키 규모가 커지거나 사용자가 늘어나는 상황이라면 더 큰 라이트세일 번들21)을 사용해 문제를 간단히 해결할 수 있습니다. 지금은 2기가 메모리 번들을 사용해 서비스하고 있는데 어직까지는 도쿠위키에 제가 요구하는 기능을 소화하는데 특별히 부족함을 느끼고 있지는 않습니다. 특히 클라우드플레어가 웹서버 앞에서 굳이 웹서버가 직접 받을 필요 없는 요청을 처리해주고 있어22) 웹서버 사양을 더 올릴 필요를 느끼고 있지는 않습니다. 하지만 위에서 이야기한 대로 서비스 중단을 줄이고 제 실험에 의해 서비스가 중단되는 상황에도 위키를 계속해서 사용할 수 있기를 바랬고 이 요구사항을 만족하려면 더 큰 인스턴스 대신 인스턴스 개수를 늘려야 한다는 결론에 이르렀습니다. 로드밸런싱을 해야 했습니다.

당장 쉽게 찾을 수 있는 로드밸런싱 서비스에는 라이트세일과 클라우드플레어에서 제공하는 서비스가 있었습니다. 라이트세일에서는 월 $18을 고정비용23)을 내면 서버 수, 요청 횟수, 트래픽에 관계 없이 로드밸런싱을 구축할 수 있었습니다. 클라우드플레어는 월 $524)를 내면 서버 두 대로 로드밸런싱을 시작할 수 있지만 서버 수를 늘리거나 요청 횟수가 늘어나면 비용이 늘어나는 구조입니다. 제 경우에는 규모가 작기 때문에 오히려 클라우드플레어의 종량제 요금이 더 저렴해서 이쪽을 선택했습니다. 만약 언젠가 규모가 커진다면 라이트세일에서 제공하는 고정 요금 로드밸런싱으로 이전할 여지는 있습니다.

클라우드플레어 로드밸런싱

클라우드플레어의 로드밸런싱은 API를 사용하지 않고, 또 라이트세일 쪽에서 별도로 DNS를 설정하지 않은 채 사용하려면 서버 개수만큼 아이피 주소가 필요합니다. 지금은 라이트세일 인스턴스 두 개로 로드밸런싱을 구축하고 있는데 인스턴스 두 개가 별도의 아이피 주소를 가지고 있고 클라우드플레어의 DNS 설정을 통해 접근하게 됩니다. 각 서버는 기존과 동일하게 A레코드설정을 해 서브도메인을 통해 접근할 수 있는 상태여야 하고 이를 기반으로 로드밸런싱 설정을 하게 됩니다. 로드밸런싱 설정은 먼저 서버 풀을 생성하고 그 풀 안에 속할 서버 각각을 추가하게 되는데 월 $5로 할 수 있는 것은 풀 한 개와 그 안에 들어갈 서버 두 대 까지입니다. 정확히 지금 제가 하고 있는 서비스까지만 이 금액으로 처리할 수 있습니다. 여기서 풀 개수를 늘리거나 서버 수를 늘리려면 비용이 약 $5 단위로 증가합니다.

일단 설정을 마치면 몇 초 안에 배포되어 도메인 네임을 입력하면 서버 두 대 중 한 대가 응답하기 시작합니다. 세션 어피니티25) 설정이 있어 서버가 여러대이더라도 정해진 시간 동안은 처음에 응답한 서버가 계속해서 응답해 로그인 유지, 동기화를 편하게 해줍니다.

동기화

도쿠위키가 파일시스템 기반으로 동작하기 때문에 이런 식으로 확장할 때 여러 가지 고민거리를 만듭니다. 구글링해보면 도쿠위키를 확장하고 싶은데 파일시스템 기반이라 어떻게 해야 할지 잘 모르겠다는 글26)들이 나타납니다. 아마도 상황에 맞는 방법을 알고 있는 전문가들은 이미 도쿠위키를 떠났거나 네트워크 파일시스템을 통해 이미 문제를 해결했을 것 같지만 이런 글은 구글에 나타나지 않았습니다. 저는 이런 설정을 할만큼 전문가는 아니었고 또 한동안은 도쿠위키를 떠날 생각도 없으므로 문제를 해결할 방법을 찾아야 했습니다. 정확히는 서버 두 대 사이에 파일시스템을 어떻게 동기화할지 정하고 실행해야 했습니다.

이전에 동기화에서 이야기한 대로 unison27)을 크론탭에 넣고 짧은 주기로 실행하고 있습니다. 근본적으로는 파일시스템이 변경될 시점마다 동기화를 수행해야 가장 안전하고 또 낭비를 줄일 수 있는 방법일 거라고 생각합니다. 하지만 어째서인지 구글에 나타난 파일시스템을 동기화하려는 요구사항을 가진 사람들은 최대 1분 단위로만 수행할 수 있는 크론탭에 꼼수를 써서 더 짧은 주기로 동기화를 수행하는 방법28)을 더 많이 사용하고 있었습니다. 일단 제가 당장 실행 하기에도 이쪽이 훨씬 쉬워 보였기 때문에 저도 똑같이 따라했고 아직까지는 별다른 사고는 없었습니다. 하지만 잠재적으로 동기화 작업 한 번이 동기화가 일어나는 최소 시간단위보다 더 길 경우 문제가 생길 수 있습니다.

unison은 서버 두 대 중 한 대에서만 실행합니다. 나머지 한 대는 동기화를 받기만 할 뿐 스스로 동기화를 실행하지 않습니다. 만약 미래에 인스턴스 수를 늘려야 하면 '동기화를 받는 쪽' 인스턴스를 기반으로 증설하고 동기화를 실행하는 인스턴스에는 동기화 설정을 추가해 대응할 계획입니다.

인증서

서비스 전체의 보안을 유지하려면 모두 세 구간에 인증서가 필요합니다. 하나는 클라이언트와 클라우드플레어 구간입니다. 여기에는 클라우드플레어가 제공하는 무료 인증서를 사용합니다. 데스크탑 브라우저에서는 주소 옆에 있는 자물쇠 아이콘을 클릭해 인증서 정보를 볼 수 있는데 여기에 제 도메인네임 대신 클라우드플레어 도메인네임이 나타나기는 합니다만 모바일 기계에서는 대부분 인증서를 직접 볼 일이 없어 인증서에 내 도메인네임이 나타나지 않는다는 점이 문제는 아니라고 생각했습니다. 그리고 이 구간의 트래픽을 암호화하려는 원래 목적을 충분히 달성하고요.

다른 둘은 서버 두 대와 클라우드플레어 서버 구간입니다. 처음에는 Let's Encrypt인증서를 발급받아 사용했는데 서브도메인 각각마다 인증서를 발급받고 유지하는 일은 자동화할 수는 있지만 근본적으로 관리부담을 늘린다고 생각했습니다. 그래서 클라우드플레어에서 제공하는 오리진 인증서29)를 사용하도록 바꿨습니다. 이 오리진 인증서는 클라우드플레어가 사인해 인증서 자체는 유효합니다만 루트인증서로부터 모든 인증 경로에는 유효하지 않습니다. 그래서 이 인증서를 사용한 채로 클라우드플레어를 거치지 않고 서비스하면 인증이 깨진 상태로 표시됩니다. 하지만 모든 요청은 항상 클라우드플레어를 통하므로 클라이언트에 요청이 깨진 상태로 표시될 일은 없었고 인증서의 유효기간이 길어 관리부담이 적을 뿐 아니라 클라우드플레어와 오리진 서버 두 대 사이를 암호화하는 목적을 달성할 수도 있었습니다.

코드 배포

데이터와 설정은 짧은 주기로 동기화됩니다. 하지만 도쿠위키 스크립트는 그렇게 하면 안될 것 같았습니다. 웹서버와 위키에 자주 실험을 했고 만약 실험이 실패하면 서비스가 일시적으로 중단되곤 했습니다. 이 때 도쿠위키 스크립트도 동기화된다면 한 서버에서 생긴 문제가 다른 서버로 빠르게 퍼질 수 있었습니다. 하지만 코드를 수정할 일은 있었고 이를 편안하게 배포할 방법이 필요했습니다. 아마도 전문가들은 다른 방법이 있을 것 같습니다만 저는 깃헙에 리파지토리30)를 생성해 코드를 배포하고 있습니다. 먼저 스냅샷으로 인스턴스를 생성해 거기서 코드를 수정하고 테스트합니다. 여기서 별 문제가 없는 것 같으면 깃헙에 푸시하고 서버 두 대 중 어느 한 대에만 배포한 다음 한동안 놔둬봅니다. 그래도 별 일 없는 것 같으면 나머지 한 대에도 배포합니다.

이런 방법은 얼마 전에 도쿠위키의 새 RC31)를 적용32)할 때 사용했습니다. 먼저 브랜치를 만들어 새 코드를 적용한 다음 검토해보고 새로 만든 인스턴스에만 반영해 얼마 동안 테스트했습니다. 큰 문제를 겪지 않았고 또 세 번째 RC에서는 모든 변경사항이 코드 정리에 해당한다고 판단해 이를 각 서버에 하루 간격을 두고 배포했습니다. 물론 이렇게 배포할 수 있는 이유는 이번 메이저 버전 업데이트는 파일시스템을 마이그레이션 하지 않기 때문입니다. 만약 미래의 새로운 버전이 파일시스템을 마이그레이션 할 경우에는 이런 식으로 한대씩 배포할 수는 없을 겁니다.

문제

동기화 간격

파일시스템 동기화는 시간 간격에 따라 일어납니다. 지금은 10초에 한번 일어납니다. 처음 1분 간격으로 설정할 때는 한 쪽에는 글을 썼지만 다른 쪽에는 아직 반영되지 않아 주소를 공유할 때 없는 페이지가 나타나는 경우가 있었습니다. 그래서 얼마 동안은 주소를 공유할 때 모든 서버의 서브도메인을 열어 양쪽 모두에 글이 똑같이 표시되는지 확인한 다음 주소를 공유했습니다. 지금은 동기화 간격을 줄였고 어느 정도 신뢰할 수 있게 동작해 더이상 주소를 공유하기 전에 모든 서버를 확인하지는 않습니다.

또 사용자가 늘어나 같은 페이지를 동기화 간격보다 짧은 간격에 서로 다른 서버에서 수정할 경우 일찍 수정한 쪽의 데이터가 유실됩니다. 양쪽에서 동시에 수정이 일어날 때 이를 자동으로 머지하는 건 아주 어려운 일이었습니다. 간단히 더 나중에 수정된 쪽으로 덮어쓰고 있는데 제 규모에서는 아직까지 문제가 일어나지는 않았습니다만 문제가 없는 것은 아닙니다. 잠재적으로 수정이 잦은 페이지에서는 수정사항을 유실할 수 있습니다.

바깥 편집

도쿠위키는 파일시스템의 타임스탬프를 통해 도쿠위키가 모르는 편집을 감지하는 기능이 있습니다. 만약 누군가, 또는 어떤 다른 앱이 도쿠위키 데이터파일을 직접 열어 수정하면 파일 내부에 기록된 타임스탬프와 파일 자체의 타임스탬프에 차이가 생깁니다. 이 상태에서 다음번에 이 페이지를 수정하려고 하면 먼저 이 도쿠위키가 모르는 변경사항을 새로운 리비전으로 만든 다음 이 리비전을 열어 수정을 시작하게 합니다. 그런데 파일을 동기화하는 환경에서는 수정사항을 동기화 받는 쪽이 항상 타임스탬프가 틀리게 됩니다. 한쪽 서버에서는 파일 내부의 타임스탬프와 파일 자체의 타임스탬프가 일치하겠지만 다른쪽 서버에서는 파일 자체의 타임스탬프가 더 미래의 시각을 가리키게 되어 항상 '바깥 편집' 기록이 남게 됩니다. 동기화되는 서버 두 대는 항상 같은 리비전 기록을 보여주되 동기화가 일어난 기록들마저 리비전으로 등록되기를 원하지는 않았기 때문에 detectExternalEdit함수를 부르지 않도록 설정33)했습니다. 다만 이 설정으로 인해 실제로 바깥 편집이 발생할 때 이 리비전을 기록하지 못하게 됩니다.

다른 한 가지 바깥편집 문제는 페이지의 최신 리비전을 다른 서버로부터 동기화 받은 경우 이 페이지를 수정한 사용자가 '바깥 편집'으로 나타나는 점입니다. 이건 리비전에 기록되지는 않고 다음 수정이 일어나면 리비전 상에는 정상적으로 기록되지만 최신 페이지가 한쪽 서버에서는 수정한 사용자 정보를 정상적으로 표시하는 반면 다른쪽 서버에서는 수정한 사용자 정보 대신 '바깥 편집'으로 표시됩니다. 이건 템플릿 출력 코드에서 수정할 수 있을 것 같아 보이지만 페이지를 편집한 사용자 정보가 없을 때 바깥 편집으로 표시하게 되어 있어 사용자 정보가 없는 이유를 이해하지 못했기 때문에 수정하지 않았습니다.

세션 어피니티

이 바깥 편집 문제를 해결하면서 세션 어피니티 기능이 동작하는 상황을 알게 되었습니다. 세션 어피니티는 로드밸런싱을 통해 어느 한 쪽 서버에 접근한 사용자를 계속해서 그 서버로만 라우팅하는 기능입니다. 이 기능이 없다면 방금 한쪽 서버에 로그인한 사용자가 로그인 직후 다른 서버로 라우팅 되어 다시 로그인을 요구받게 될 수도 있습니다. 위에 바깥 편집 문제와 연결되어 페이지를 수정하고 저장한 시점에는 페이지 히스토리에 사용자 정보가 표시되지만 잠시 후 다시 그 페이지에 접근해보면 '바깥 편집'으로 바뀌어 있을 때가 있습니다. 이는 제가 새로고침을 하며 위키를 사용하는 동안에 한 서버에서 다른 서버로 라우팅되었다는 것을 의미합니다.

결론

만약 도쿠위키가 데이터베이스를 사용했다면 로드밸런싱 대부분에 데이터베이스 리플리케이션 이야기만 했을 겁니다. 또 한편으로는 unison같은 또 다른 도구를 사용하는 대신 그냥 데이터베이스 자체의 기능을 사용해 위에 이야기한 잠재적인 문제 없이 더 안정적으로 로드밸런싱을 구축했을 수도 있습니다. 하지만 제 규모에서는 잠재적인 문제를 포함해 어쨌든 동작하는 도쿠위키 로드밸런싱을 구축했고 한동안 문제 없이 동작하고 있습니다. 서버를 증설하려면 동기화 받는 쪽 서버를 증설하면 되고 데이터 유실 가능성을 줄이려면 동기화 하는 쪽 서버를 백업하면 됩니다. 만약 미래에 어느쪽 서버가 유실되더라도 다른쪽 서버의 데이터나 다른 가용 영역에 있는 데이터를 통해 복구할 수 있습니다. 맨 위에 이야기한 두 가지 목적을 어느 정도 달성했다고 판단합니다.

· 2020-06-21 17:57

<< 새 항목 | 오래된 항목 >>

blog/start.txt · 마지막으로 수정됨: 2020-06-07 14:29 (바깥 편집)