지난 글에서는 로컬에서 ssh 터널링을 통해 RDS에 접근하는 방법에 대해 알아보았다. 그러나 나는 도커를 사용중이기 때문에, 컨테이너 내부에서 호스트(내 PC)의 localhost에 접근하기 위한 방법이 필요했다.
도커에서는 자체적으로 호스트의 localhost를 접근할 수 있도록 host.docker.internal
도메인을 제공한다. 따라서 로컬에서 ssh 터널링을 수행한 후에 컨테이너에서는 localhost
대신 host.docker.internal을 사용하면 된다.
로컬에서 매번 ssh 터널링을 해주기가 조금 번거롭긴 하다. 게다가 다른 팀원들은 윈도우를 쓰고 있기 때문에 혹시 모를 환경 문제를 막고 싶었다. 따라서 컨테이너에서 직접 띄우는 방법을 시도해보았다. 기존에 있던 백엔드 컨테이너에서 백그라운드로 ssh 터널링을 수행하면 해당 컨테이너에서는 localhost로 접근이 가능하게 된다.
실행 중인 컨테이너에 접속해 기존 명령어에 -fN 옵션을 추가하면 ssh를 백그라운드로 연결할 수 있다.
<컨테이너 접속>
docker exec -it <container id> /bin/bash
이미지에 따라 bash를 찾을 수 없다고 오류가 날 수 있다. /bin/sh
로 바꿔주면 된다.
<ssh 터널링>
ssh -fN -4 -i "${SSH_PEM_KEY}" -L ${MYSQL_PORT}:${RDS_HOST}:${MYSQL_PORT} ${SSH_USER}@${SSH_HOST}
그러나 이 과정을 매번 수동으로 할 수 없다. Dockerfile에서 RUN 명령어를 통해 실행하려고 했으나, RDS 접속 주소, EC2 주소, username 등 민감한 정보를 github에 올라가게 둘 수 없어서 환경변수를 이용하기 위해 쉘스크립트를 작성해 CMD로 실행하도록 했다.
dockerfile 수정
CMD ["./entrypoint.sh"]
환경 변수 작성
.env파일에 환경변수를 작성하고, docker-compose.yml에서 env_file로 등록한다.
backend:
container_name: backend
build:
context: ./server
dockerfile: Dockerfile.dev
env_file: ./server/.env
...
fingerprint 등록
처음 ssh 접속을 할 때 fingerprint를 허용해야 한다. 원래라면 직접 yes를 쳐야 하는데, 직접 터미널에 입력을 할 수 없는 상황이니 미리 known_hosts에 등록해두었다.
mkdir -p ~/.ssh
ssh-keyscan -H ${SSH_HOST} > ~/.ssh/known_hosts
pem key 권한 설정
이후 pem key의 권한이 너무 높으면 문제가 되는 경우도 있기 때문에 권한을 낮춰주었다.
chmod 400 ${SSH_PEM_KEY}
전체 쉘 스크립트 (entrypoint.sh
)
#!/bin/bash
#shell script for ssh port forwarding
mkdir -p ~/.ssh
ssh-keyscan -H ${SSH_HOST} > ~/.ssh/known_hosts
chmod 400 ${SSH_PEM_KEY}
ssh -fN -4 -i "${SSH_PEM_KEY}" -L ${MYSQL_PORT}:${RDS_HOST}:${MYSQL_PORT} ${SSH_USER}@${SSH_HOST}
# 기존 백엔드 코드
npx nodemon -L main.js
이렇게 설정했을 때도 문제가 있다. 바로 SSH 연결이 일정 시간이 지나면 끊어진다는 것이다. 아마 timeout 설정과 관련이 있을 것 같은데, 일정 주기로 통신 패킷을 보내는 방식을 시도해봐야 할 것 같다.
Docker Container 내부에서 Host로 접근하는 방법
Automatically Accept SSH Fingerprint