잠깐 20초만 한눈을 팔면, 멈춰 서는 WSL

🧐 , 🧐 | 2023-05-31

안녕하세요, 넷마블 기술관리팀 조병승입니다.

이번 글은 아키텍처최적화팀 박정욱님과 WSL을 쓰면서 겪은 에피소드를 재구성했습니다.

이야기 시작: WSL2 버전과 도커 설정

박정욱님은 WSL2 기반 도커 환경을 주로 사용하고 있습니다. 박정욱님이 도커 환경을 세팅하신 이야기는 이미 넷마블 기술 블로그에서 한번 다뤘었습니다.

며칠 전, 박정욱님께서 제게 와서 몇 마디를 남기고 가셨습니다.

: 전에 제가 올렸던 글에서 배치 파일 실행이 안 되고 있어요. 생성되는 nohup.out 파일명을 변경만 해도 동작을 안 하더라구요 ㅜ.ㅠ

: 엇? 마침 제가 노트북을 초기화해서 기본 설정이 완전 깔끔하니까, 한번 다시 따라 하면서 확인해 볼게요.

: 그러면 동작하도록 바꾼 배치 파일은 이거니까, 이것도 같이 확인해 봐주세요.

@echo Starting dockerd in WSL ...
@echo off
if exist nohup.out del /f /q nohup.out
for /f "tokens=1" %%a in ('wsl sh -c "hostname -I"') do set wsl_ip=%%a
netsh interface portproxy add v4tov4 listenport=2375 connectport=2375 connectaddress=%wsl_ip%
wsl -d Ubuntu -u root -e nohup sh -c "dockerd -H tcp://%wsl_ip% &"

WSL에서 WSL2로 업데이트한 이후 특별히 세부 내용은 추적하지 않고 있었는데, 박정욱님의 글을 다시 보면서 하나씩 확인해 보기로 했습니다.

버전이 바뀌었다

다시 따라 하면서 발견한 가장 큰 변화는 버전이었습니다.

윈도우10과 윈도우11 모두 WSL을 WSL2로 업그레이드하더라도, 윈도우 기본 프로그램 구성에 맞춘 WSL 버전을 따라갑니다. 기본 구성에 따르면, wsl.exe 프로그램 자체는 ‘10.0.19041.2311’ 버전이고, 리눅스 커널 버전은 ‘5.10.16.3’이었습니다.

그런데, 윈도우 업데이트나 Microsoft Store 업데이트 등을 따라가다가 WSL 자체가 업데이트되는 구간이 나옵니다. 대표적인 사례가 Microsoft Store에 있는 WSL로 설치하는 시점입니다.

이 업데이트를 지나면 전체 버전이 한번 격변합니다. 지금 다시 보니, WSL 아이콘도 턱스에서 파란 펭귄으로 바뀌었네요. wsl.exe 프로그램은 ‘1.2.5.0’ 버전이고, 리눅스 커널 버전은 ‘5.15.90.1’이 됐습니다. WSL 자체는 이제 Microsoft Store의 패키지 관리 상태를 따르도록 바뀌었습니다.

똑같은 WSL2지만, 버전 업데이트 사이에 있는 제일 큰 기능 차이는 systemd 지원입니다. 여기서부터 생긴 여파인지 혹은 다른 업데이트에 엮였는지 더 찾아봐야 하겠지만, 버전 릴리스 노트를 보니 정말 쉬지 않고 계속 업데이트가 있었더군요.

그나마 리눅스 커널 버전은 MS 공식 페이지 기준으로 2022년 8월 2일 ‘5.15.57.1’ 버전 이후 멈춘 것처럼 보이지만, WSL2 리눅스 커널 리포지터리를 보면 6.1 버전을 향해 달리고 있습니다. (업데이트 프리뷰 등을 보다 보면 리눅스용 GUI 앱을 지원하는 것도 볼 수 있습니다.)

버전 변화를 보다 보니, 이야기가 너무 옆길로 샜네요. 박정욱님이 알려주신 현상은 이 버전 차이로 인해 발생한 것으로 확인 후, 기존 글에 버전 관련 내용을 업데이트했습니다.

도커 설치 및 활용을 위한 조금 다른 환경 설정 방법

업데이트된 버전으로 좀 더 기웃거리다 보니, 5.15 버전에서는 기존과는 조금 다르게 설정을 잡고 쓸 수도 있었습니다. systemd를 지원하면서 생긴 차이죠.

도커 엔진 설치나 사용자 권한 설정 등은 기존 박정욱님 글과 동일합니다.

// 도커 설치
$ curl -sSL get.docker.com | sh

// sudo 권한 설정 
$ sudo usermod -aG sudo $USER

// docker 그룹 추가
$ sudo usermod -aG docker $USER

.profile 파일에 입력하는 도커 컨텍스트 설정도 똑같습니다.

$ echo '' >> ~/.profile
$ echo 'wsl_ip=$(ip addr show eth0 | grep -oP "(?<=inet\s)\d+(\.\d+){3}")' >> ~/.profile
$ echo 'export DOCKER_HOST=tcp://$wsl_ip:2375' >> ~/.profile

우분투 22.04 이상일 땐 ‘iptables’ 설정도 iptables-legacy로 변경합니다.

$ sudo update-alternatives --config iptables

…
 1            /usr/sbin/iptables-legacy   10        manual mode
…

여기까진 기존과 동일합니다.

이제 daemon.json 파일로 도커 데몬의 설정값을 넣습니다. daemon.json파일에 대한 자세한 설명은 아래 도커 공식 문서 링크를 참고해 주세요.

$ sudo vi /etc/docker/daemon.json


// 아래 설정값을 추가합니다.
{
    "hosts": [
        "fd://",
        "unix:///var/run/docker.sock",
        "tcp://0.0.0.0:2375"
    ]
}

다음으로 docker.service 설정을 변경합니다. 도커 데몬이 fd 소켓을 통해 클라이언트 요청을 처리하도록 설정하는 -H fd:// 옵션을 제거합니다.

$ sudo vi /lib/systemd/system/docker.service

#ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecStart=/usr/bin/dockerd --containerd=/run/containerd/containerd.sock

<2023년 11월 14일 추가>

WSL을 업데이트한 후, 이 옵션이 원복되는 경우가 있습니다. WSL 업데이트 이후에 도커 소켓 에러를 마주친다면, -H fd:// 옵션 제거를 꼭 다시 확인해주세요.

만약, WSL 최신버전 신규 설치가 아니라 구버전부터 업데이트하신 분이라면 /etc/wsl.conf 파일이 없어서 systemd 활성화 설정이 빠져있을 수도 있습니다. 꼭!! 확인하셔야 합니다. 파일이 없다면 수동으로 추가하시면 됩니다.

$ cat /etc/wsl.conf
[boot]
systemd=true

이제 도커 데몬 설정값을 다시 불러오도록, 데몬을 재시작합니다.

$ sudo systemctl daemon-reload
$ sudo systemctl restart docker.service

이제 모든 설정이 끝났습니다. 윈도우에서 시스템 환경 변수 설정을 추가하면, 도커 클라이언트를 윈도우에서도 바로 실행할 수 있습니다.

이야기 확장: 터미널 창을 닫으면 멈춰 서는 WSL

윈도우 업데이트 중 어느 업데이트부터인지는 정확하지 않습니다. WSL에서 NGINXAPACHE 같은 웹서버를 돌릴 때에는 WSL 접속창을 닫아도 STATERunning 상태로 유지가 됐었는데, 그 이외에는 WSL 접속창을 닫으면 20초 정도 뒤에 Stopped로 바뀌기 시작했습니다. 엄밀히는 이 현상이 있음을 이번에 인지했다고 하는 게 더 맞겠군요. (저는 늘 NGINX가 돌아가는 상황에서만 쓰고 있었습니다.)

박정욱님께 바로 쫓아가서 물어보기로 했습니다.

: 박정욱님, 혹시 이 현상 겪어보셨어요?

: 그 현상을 겪어본 건 아니지만, 백그라운드에서 계속 실행되도록 하는 옵션이 nohup &이었어요. 백그라운드에서 WSL이 계속 실행 중이니까, 아마 저도 계속 Running 상태였겠네요. 앞 이야기에서 수정한 배치 파일이 그 역할을 하고 있었어요.

: 얼마 전에 커뮤니티에서도 이 이야기가 올라왔었는데, 저는 어차피 NGINX라, 그 현상이 안 나오더라고요. 혹시나 해서 WSL 커널 버전 차이 탓인가도 살펴봤지만, 그건 아니었어요. WSL2에서는 대충 다 공통으로 등장하는 현상 같아요.

: WSL 접속창을 백그라운드에 띄워두기만 하면 되는 거니까, nohup &이 잘 동작 안 한다면 백그라운드에서 실행하는 다른 방법을 찾으면 되지 싶어요.

WSL 접속창을 간단히 떠올려 봤습니다. 우분투 실행화면, 윈도우 터미널, 파워셸, 명령 프롬프트 정도가 있었습니다. 이 중 하나에서 깨알 같은 팁을 찾기로 했습니다.

nohup & 동작 방식

nohup은 “no hang up”의 약자로, 리눅스와 유닉스 시스템에서 프로세스를 백그라운드로 실행하고, 터미널 세션이 종료돼도 해당 프로세스가 계속 실행되도록 하는 명령어입니다. 뒤에 &를 붙이면 백그라운드로 실행하라는 지시를 하므로, 둘을 같이 쓰는 경우가 많습니다.

nohup 명령어와 &를 함께 사용하면 다음과 같은 동작 방식을 가집니다.

  1. 사용자가 nohup 명령어를 사용해 실행할 프로세스를 지정합니다.
  2. nohup은 해당 프로세스를 터미널 세션과 분리하고, 터미널 세션이 종료되더라도 해당 프로세스가 종료되지 않도록 합니다.
  3. &는 프로세스를 백그라운드로 실행하도록 지시합니다. 이로 인해 해당 프로세스는 터미널의 제어를 반환하고, 다른 작업을 수행할 수 있게 됩니다.
  4. 실행한 프로세스는 백그라운드에서 계속 실행되며, 터미널 세션이 종료되더라도 영향을 받지 않습니다. 프로세스는 계속 실행될 것입니다.

이렇게 nohup&를 함께 사용하면 백그라운드에서 프로세스를 실행하고, 터미널 세션의 종료와는 독립적으로 프로세스를 유지하면서 작업을 수행할 수 있습니다.

찾아보니, 윈도우 빌드 17046 버전부터 WSL의 백그라운드 태스크 실행을 위해 지원하기 시작한 명령어로 나와 있었습니다.

이런 업데이트 노트에도 불구하고, WSL은 계속 꺼졌습니다. 약속의 20초… 스택오버플로 등에서는 disown을 붙여서 유지하는 방법도 나와 있지만, 역시 약속의 20초는 무너지지 않았습니다.

이쯤 되면 nohup을 포기해야 하지 않을까 싶었습니다.

파워셸 백그라운드 실행

가만히 그간의 테스트를 살펴보니, 도커 데몬 등을 실행하기 위해서 WSL에 이런저런 명령어를 바꿔가며 백그라운드 실행을 유지하려고 했던 시도였었습니다. 하지만, 박정욱님이 해준 말이 다시 떠올랐습니다. 뭐든 WSL 실행창만 백그라운드에서 유지되고 있으면 된다는 걸 말이죠.

우분투 실행화면, 윈도우 터미널, 파워셸, 명령 프롬프트 중 백그라운드로 실행하는 옵션이 있는지 찾아보기 시작했고, 얼마 지나지 않아 파워셸 실행 옵션을 찾았습니다.

$ powershell.exe -Command "start-process wsl.exe -WindowStyle Hidden"

파워셸의 Start-Process를 사용해 WSL을 실행하면서, -WindowStyle Hidden 옵션을 넣으면 실행한 창을 숨길 수 있습니다. 윈도우 터미널 등에서 wsl.exe만 실행하고 가만히 놔두는 창을 백그라운드에 올려둔 것이죠.

백그라운드에 파워셸로 실행한 WSL을 찾거나 종료할 땐 Get-Process 명령어와 Stop-Process 명령어를 활용하면 됩니다.

$ Get-Process | Where-Object { $_.Name -like '*wsl*' }

$ Stop-Process -ID 16848

박정욱님과 처음 이야기를 나눴던 주제인 도커 데몬 자동 실행 배치 파일에 이걸 응용한다면, 맨 뒤에 백그라운드 실행 한 줄만 더 추가하면 되는 셈이 됐습니다. nohup 대신 말이죠. 마침, 도커 데몬에 호스트 설정하는 옵션은 WSL 커널 버전 5.15 이후에는 daemon.json에서 이미 잡고 있으니 조금 더 간편해진 느낌도 듭니다.

@echo off
for /f "tokens=1" %%a in ('wsl -d Ubuntu sh -c "hostname -I"') do set wsl_ip=%%a
netsh interface portproxy add v4tov4 listenport=2375 connectport=2375 connectaddress=%wsl_ip%
powershell.exe -Command "start-process wsl.exe -WindowStyle Hidden"

특이점은 또 오겠지

WSL이 WSL2가 됐고, WSL2에서는 다시 커널 버전과 클라이언트 버전이 업데이트됐습니다. 그리고 앞으로도 또 새로운 업데이트가 계속 줄 서서 기다리고 있는 것도 알게 됐고요.

잠깐 20초만 한눈을 팔면 WSL이 멈춰 섰던 것처럼, 분명 어느 날 또 뭔가 잘 실행되지 않는 특이점이 올 것입니다. 그리고 또 우리는 해결책을 다시 찾아 나서겠죠.

그날 다시 박정욱님과 함께 돌아오겠습니다.