
개요
Windows 환경에서 작성한 설정 파일이나 쉘 스크립트를 Linux 기반 Docker 이미지에 포함시키다 보면 예상치 못한 오류가 발생하는 경우가 있다.
특히 Nginx 설정 파일, Logrotate 설정 파일, Shell Script 등을 Windows에서 수정한 뒤 그대로 이미지에 포함하면 컨테이너 실행 시 오류가 발생하거나 설정 파일을 정상적으로 읽지 못하는 현상이 발생할 수 있다.
이번 글에서는 Docker 빌드 과정에서 CRLF(Line Ending)를 자동으로 LF 형식으로 변환하도록 수정한 사례를 정리해본다.
증상
가장 흔하게 발생하는 증상은 다음과 같다.
/bin/sh^M: bad interpreter: No such file or directory
또는
/usr/bin/env: 'bash\r': No such file or directory
쉘 스크립트가 정상적으로 존재함에도 실행되지 않는다.
경우에 따라서는 Nginx 설정 파일이나 Cron 설정 파일이 정상적으로 읽히지 않는 현상도 발생할 수 있다.
원인
원인은 Windows와 Linux의 줄바꿈(Line Ending) 방식 차이에 있다.
운영체제별 기본 줄바꿈 문자는 다음과 같다.
운영체제줄바꿈
| Windows | CRLF (\r\n) |
| Linux / Unix | LF (\n) |
Windows에서 저장된 파일에는 줄 끝마다 \r 문자가 포함된다.
Linux는 이 문자를 줄바꿈 문자로 인식하지 않기 때문에 파일 내부에 불필요한 제어 문자가 남게 된다.
확인 방법
CRLF 형식으로 저장된 파일은 vi 또는 cat 명령으로 확인할 수 있다.
예를 들어 다음과 같이 표시된다.
server {
listen 80;^M
server_name localhost;^M
}^M
또는
#!/bin/sh^M
echo "start"^M
여기서 보이는 ^M 이 바로 CR(Carriage Return, \r) 문자다.
정상적인 Linux 파일이라면 다음과 같이 보여야 한다.
#!/bin/sh
echo "start"
또는 다음 명령으로도 쉽게 확인할 수 있다.
cat -A run.sh
결과:
#!/bin/sh^M$
echo "start"^M$
^M 이 보인다면 해당 파일은 CRLF 형식으로 저장된 상태다.
해결 방법 1. 수동으로 변환하기
가장 간단한 방법은 sed를 이용해 CR 문자를 제거하는 것이다.
단일 파일이라면 다음과 같이 처리할 수 있다.
sed -i 's/\r$//' run.sh
변환 전:
#!/bin/sh^M
echo "start"^M
변환 후:
#!/bin/sh
echo "start"
해결 방법 2. 디렉터리 전체 변환하기
설정 파일이 여러 개라면 find와 함께 사용할 수 있다.
find . -type f -exec sed -i 's/\r$//' {} +
현재 디렉터리 이하의 모든 파일에 대해 CR 문자를 제거한다.
다만 바이너리 파일까지 포함될 수 있으므로 실제 환경에서는 대상 디렉터리를 제한하는 것이 좋다.
예시:
find /etc/nginx -type f -exec sed -i 's/\r$//' {} +
해결 방법 3. Docker 빌드 과정에서 자동화하기 (추천)
개발자가 Windows를 사용하든 Linux를 사용하든 동일한 결과를 보장하려면 Docker 빌드 단계에서 변환하는 것이 가장 확실하다.
예를 들어 실행 스크립트만 처리하려면:
RUN sed -i 's/\r$//' /app/run.sh
Nginx 설정 파일 전체를 처리하려면:
RUN find /etc/nginx -type f -exec sed -i 's/\r$//' {} +
Cron 설정 파일도 포함하려면:
RUN find /etc/crontabs -type f -exec sed -i 's/\r$//' {} +
왜 빌드 단계에서 처리하는가
개발자 PC 설정에 의존하면 다음과 같은 문제가 발생할 수 있다.
- Windows에서 수정
- CRLF로 저장
- Git Push
- Docker Build
- 운영 환경 장애 발생
반면 빌드 단계에서 강제로 정규화하면
개발 환경
↓
Docker Build
↓
CRLF 제거
↓
이미지 생성
↓
운영 환경 배포
어떤 환경에서 파일을 수정했는지와 관계없이 동일한 결과를 보장할 수 있다.
적용 효과
해당 구문 적용 후 다음과 같은 효과를 얻을 수 있었다.
- Shell Script 실행 오류 방지
- Nginx 설정 파일 파싱 문제 예방
- Cron 설정 파일 오류 예방
- Logrotate 설정 파일 오류 예방
- 개발자 OS에 의존하지 않는 빌드 결과 보장
특히 운영 환경에서만 간헐적으로 발생하는 "왜 이 서버만 안되지?" 유형의 문제를 사전에 차단할 수 있다.
결론
Line Ending 문제는 매우 단순하지만 생각보다 찾기 어려운 장애 원인이다.
파일을 열어보면 정상으로 보이고, Git에서도 차이가 보이지 않으며, Windows에서는 문제없이 동작하는 경우가 많다.
하지만 Linux 입장에서는 줄 끝에 숨어 있는 \r 문자 하나 때문에 설정 파일 파싱 실패, 쉘 스크립트 실행 실패, Cron 작업 미등록 같은 문제로 이어질 수 있다.
특히 Docker 환경에서는 이미지 빌드 단계에서 CRLF를 제거하는 방어 코드를 추가해 두는 것이 가장 확실한 해결 방법 중 하나라고 볼 수 있다.