Docker 컨테이너에서 터미널을 입력할 때 줄바꿈 처리의 문제
현재 코딩테스트 input이 될 값을 띄어쓰기 정도만 포함된 문제로밖에 못하는 것입니다. "3" 또는 "3 7 4" 이런 값은 가능한데, "1 2 3\n5 6 7" 이런 값이 안됩니다. 쉘 명령어를 echo -e "1 2 3\n5 6 7" 이렇게 작성을 해보았으나, 클라이언트 측에서 출력할 때 -e 1 이렇게만 출력됩니다. 컨테이너 안에서 shell 명령어가 실행되거나, shell 파일을 읽어오도록 수정해 보았으나 해결되지 못했고, bash 에러가 떠서 이 부분도 수정을 해보았으나, 아직까지 해결하지 못했습니다. 이 부분은 공부가 좀 더 필요할 것 같습니다
위에는 제가 이전에 프로젝트를 하다가 해결하지 못한 부분이었습니다
해당 프로젝트의 실행 순서는 이렇습니다
- 클라이언트는 매개변수를 출력할 수 있다
- 클라이언트는 매개변수를 이용해서 문제에 맞게 코드를 작성한다
- 클라이언트는 결괏값을 출력한다
- 서버는 클라이언트에게 받은 code를 docker 이미지로 빌드한다
- docker 이미지를 실행시켜 컨테이너를 띄운다
- 이 과정에서 테스트케이스는 여러개이며 매개변수가 동적으로 할당되고 테스트케이스마다 컨테이너가 실행되어야 한다
기존에 해당 로직을 타는 코드는 아래와 같이 작성되었습니다
// Dcokerfile
FROM python:3.9-alpine
COPY compile/code.py /usr/src/
WORKDIR /usr/src/
CMD ["python3", "code.py"]
// 컨테이너 실행 명령어
const command = `echo ${test.input} | docker run --rm -i python:3`;
// test.input에 들어갈 동적 매개변수 형태
test.input = "1 2 3\n4 5 6"
이렇게 작성해보았을 때 한 줄로 입력된 매개변수 값을 echo 명령어로 실행시킬 땐 문제가 없었으나
줄바꿈 개행이 들어갔을 때 문제가 생겼습니다
여러 가지를 시도를 해봤으나 뜨는 이슈는 아래와 같습니다
- "1 2 3 까지만 출력됨
- 1 2 3 까지만 출력됨
- 1 2 3\n 까지만 출력됨
- -e 1 출력됨
- python 실행 시 EOFError: EOF when reading a line 에러
- Command failed: docker run -e GREETING="1 2 3 4 5 6" -i python:3 "docker run" requires at least 1 argument. See 'docker run --help'. Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] Create and run a new container from an image 에러 반환
해결방법
그 이후로 docker 강의를 듣고 -i, -t 옵션을 알게 되면서 코드를 수정해 보게 되었습니다
일단 결론부터 말하자면 제 쪽 자바스크립트 코드 문제, 클라이언트 쪽에서 작성한 코드의 문제였습니다
수정한 코드는 아래와 같습니다
// Dcokerfile
FROM python:3.9-alpine
COPY src/docker/input.sh /app
COPY compile/code.py /app
WORKDIR /app
CMD ["sh", "-c", "./input.sh | python3 code.py"]
// input.sh
echo -e "$ARGS"
// 컨테이너 실행 명령어
const command = `docker run -e ARGS="${test.input}" -i python:3`;
// test.input에 들어갈 동적 매개변수 형태
test.input = "1 2 3\\n4 5 6"
사실상 기존 코드에서 많이 바꿀 필요도 없었고... 제가 삽질한 거였습니다
일단 이처럼 코드가 바뀐 이유는 여러 가지 테스트해보다가 나오게 된 거고 굳이 저렇게 작성 안 해줘도 되더군요...
그냥 제 기존 코드에서 몇 가지만 살짝 수정하면 되었던 문제였습니다
여기서 눈여겨봐야 할 포인트는 아래와 같습니다
- echo -e 옵션 ( -e 옵션은 echo 명령어가 \n이 개행 문자로 해석되어 출력됩니다)
- test.input을 따옴표로 감싸줘야 하는 것 (큰따옴표, 작은따옴표 크게 상관없는 것 같습니다)
- 매개변수 string의 개행문자 \n에 \를 한번 더 써줘야 할 것
그리고 정답을 출력해 내는 클라이언트 쪽 코드도 수정해줘야 합니다
제가 python을 잘 몰라서 str = input() print(str) 이런 식으로 받아서.... 삽질한 케이스인데요
이러니까 계속 첫 번째 줄만 출력되는 거였습니다..... (그때 다른 식의 코드도 써본 것 같은데 잘 안 됐던... 뭐지...)
import sys
for line in sys.stdin:
print(line, end="")
위와 같이 여러 줄의 문자열을 출력해 주는 코드로 작성해 주니까 해결되었습니다
.
Docker 내부에 코드 복사 및 실행
Docker 실행될 때 굉장히 느립니다. 이 부분은 제 개인 노트북 문제일 확률이 높다고는 생각이 듭니다.
유튜브에 해당 프로젝트 공부 브이로그를 올렸더니
댓글로 도커 이미지를 매번 빌드할 필요는 없을 것 같다는 댓글이 많이 달렸습니다
저 또한 로컬에서 너무 느려서 개발하면서 불편한 상태였고요
아직 도커에 대해 잘 모르는 저로선, 유저가 보낸 코드가 달라질 때마다 매번 빌드하는 건 당연한 건 아닌가?라고 생각을 했습니다만
도커 강의를 듣고 나니 제가 많이 몰랐구나라는 생각이 들었습니다
// 각 언어마다 도커 파일
FROM node:16-alpine
COPY src/docker/input.sh /usr/src/
COPY compile/code.js /usr/src/
WORKDIR /usr/src/
CMD ["sh", "-c", "./input.sh | node code.js"]
// 이미지 빌드
execSync(`docker build -t ${build[lang].name} -f ${build[lang].path} .`);
// 컨테이너 실행
exec(`docker run --rm -e ARGS="${test.input}" -i node:16`);
기존에는 유저가 코드를 제출할 때마다 위 코드가 실행이 되었습니다
그래서 매번 <none>이라는 이름의 이미지가 생성되기도 했고,
각 언어마다 도커 이미지가 별도로 생성되는 형태,
컨테이너 또한 실행할 테스트케이스마다 생성되고 실행되었기에 거기에서 오래 걸리는 형태였습니다
그래서 이걸 아래와 같이 리팩토링 했습니다
개선사항
FROM python:3.9-alpine
RUN apk add --update nodejs npm
RUN apk add openjdk11
RUN apk add g++
COPY src/docker/input.sh /usr/src
WORKDIR /usr/src
VOLUME ["/app"]
일단 가장 먼저 모든 언어를 실행시킬 수 있는 환경을 만들었습니다
해당 도커 이미지는 Docker hub에 올려놓은 상태입니다
router.get("/:problemId", (req: Request, res: Response) => {
try {
const imageExists = execSync(`docker images -q kyunga/all-lang:latest`)
.toString()
.trim();
if (!imageExists) {
dockerBuild();
execSync(`docker run --rm -d -it --name test-app kyunga/all-lang:latest`);
} else {
exec(`docker run --rm -d -it --name test-app kyunga/all-lang:latest`);
}
// ..
});
그리고 해당 문제 페이지에 접근하자마자 이미지 빌드 및 컨테이너 실행을 합니다
여기서 문제는 해당 이미지가 이미 있으면 에러가 발생하기 때문에
이미지가 있으면 컨테이너만 실행시키고, 이미지가 없으면 둘 다 진행합니다
// 유저의 코드를 파일화
fs.writeFileSync(`${filePath}/${fileName[lang]}`, code);
// 파일화 끝나면 컨테이너 내부에 복사
execSync(`docker cp compile/. test-app:/usr/src`);
유저가 코드를 제출하면 먼저 파일화를 동기로 진행합니다
(동기로 진행하지 않으면 에러 반환)
그다음 컨테이너 내부에 복사합니다 여기서 중요한 게 해당 컨테이너는 실행 중이어야 합니다
(-it 옵션이 들어간 이유!)
execSync(`docker exec test-app sh -c "g++ -o main main.cpp"`);
exec(`docker exec test-app sh -c "echo -e '${test.input}' | ./main"`);
그리고 각 언어마다 위와 같이 실행해 줍니다
execSync는 모든 언어마다 필수는 아니고 java, cpp처럼 코드를 컴파일해야 하는 경우에만 필수입니다
이렇게 작성해 주니 각 언어마다 Dockerfile이 있을 필요도 없어졌고,
훠어어어어얼씬 속도가 빨라졌습니다!!!!!!!!!!!!!!!!!!!!!
사실 도커 강의에서 cp 명령어는 일반적인 경우는 아니다, 웬만해선 안 쓰는 거 추천한다라는 강사의 말이 있었으나,
저는 현재까지 배운 내용으로 토대로 1차 수정을 한 것이고 분명 이 것보다 더 좋은 방법이 있을 거라고 생각합니다
'Project > WEB IDE' 카테고리의 다른 글
Socket.io 오버헤드 최적화하기 | Web IDE 구축하기 - 6 (0) | 2024.08.27 |
---|---|
React + express(node.js) + Docker 프로젝트 AWS EC2 배포 후기 | Web IDE 구축하기 - 5 (2) | 2024.05.15 |
코딩테스트용 Web IDE 구축 회고 | Web IDE 구축하기 - 3 (0) | 2024.04.10 |
Docker로 각 컨테이너마다 Java, C++, Python, Node.js 언어 실행시키기 | Web IDE 구축하기 - 2 (0) | 2024.04.06 |
Javascript 코드를 서버에서 안전하게 실행시키는 방법 (feat. isolated-vm) | Web IDE 구축하기 - 1 (0) | 2024.03.12 |
댓글