[WEB] 도커 101
10 Oct 2022#1. 개념을 짚고 갑시다.
컨테이너 이미지
- OS의 컨테이너 내부에서 애플리케이션을 실행하기 위해 필요한 모든 파일을 캡슐화한 바이너리 패키지
- 간단히 압축 파일과 같다.
- 바이너리 파일
- 고유 id 를 가지며, 컨테이너를 실행하는데 필요한 모든 것을 담고 있다.
- 컨테이너 실행에 필요한 모든 것이 담긴 단일 파일
도커
- 가장 famous한 컨테이너 이미지 포맷 === 도커 이미지 포맷
- 도커 파일을 사용해 도커 컨테이너 이미지를 자동으로 생성할 수 있음
- 도커 파일 : 컨테이너를 빌드하는 명령어가 들어 있는 텍스트 파일
- 파일 형식
Dockerfile
컨테이너 생성 및 실행 명령어
- CMD 방식
- 이미지를 바탕으로 생성된 컨테이너 실행 시 매번 명령을 실행
- 해당 명령으로 컨테이너가 실행됨
- 하나의 CMD 만 작성 가능하며, 중복일 경우 맨 마지막 하나만 유효
- ENTRYPOINT 방식
- 도커파일에서 빌드한 이미지를 컨테이너로 생성할 때 단 한번만 실행
- m1 환경에서 이미지 빌드시, 멀티os를 환경에 맞춰진 이미지를 위해서는 해당 명령어를 사용해야함)
도커 허브 유의점
- 도커 허브에서 이미지 불러올 시, 오피셜 태그 붙은 것만 사용하기!
- 이미지는 가벼울수록 좋으니 경량 리눅스를 사용하기
- 데비안 (devian)
- 알파인
- 이미지는 가벼울수록 좋으니 경량 리눅스를 사용하기
#2. 도커 파일 작성하기
뭅스터를 이미지로 만드는 실습을 진행해보자
- 클라이언트 소스코드를 클론해서 의존성 파일 설치 후, 실행시키는 이미지
// Dockerfile
FROM node:14.20.0-bullseye-slim
RUN apt-get update -y
RUN apt-get install -y git
RUN git clone https://github.com/movester/movester_client_product
ADD .env /movester_client_product
WORKDIR /movester_client_product
RUN npm install
RUN npm install @material-ui/core
EXPOSE 7001
CMD ["npm","run","start"]
- 노드 이미지를 가져옴 (도커 허브로부터, 경량화된 리눅스인 bullseye)
- apt 업데이트 진행
- 깃 설치
- 깃 클론해옴 (public 레파지토리여서 인증 필요없지만, private 레파지토리라면 사전에 깃 계정 인증하는 코드 추가해야함)
- .env 파일 copy함 (해당 이미지를 작성중인 파일과 동일한 위치의 .env 파일을 /movester_client_product 내에 복사함
- .env 파일은 깃 클론시 받아올 수 없기 때문에, 도커 파일 작성하는 환경에서 주입해주어야함
- 단! 이렇게 작성하면 이미지 안에 .env 를 가지고 있게 됨
- 컨테이너를 띄운 뒤, 컨테이너로 직접 들어가며 .env 가 노출됨 → 지양해야함!
- k8s에서는 secret 으로 관리할 수 있음
- 디렉토리를 이동 (cd 명령어대신 해당 명령어를 사용해야 함)
- 의존성을 위해 npm 설치
- 7001 포트를 노출시킴
- 해당 명령어와 함께 컨테이너가 실행됨
도커 이미지를 빌드하자
docker build -t [image이름] .
- 해당 작업 파일에는 .env 가 존재해야 한다!
이미지가 만들어졌는지 확인하자
docker images
컨테이너를 실행시켜보자
docker run —name [컨테이너이름] -itd -p [외부포트:내부포트] [이미지이름]
docker ps -a
로 실행중인 컨테이너를 확인하자- 포트도 7001 로 연결된 걸 확인할 수 있다!
컨테이너의 내부로 직접 들어가보기
docker exec -it [컨테이너이름] sh
- .env 가 잘 복사됐는지 확인해보자
- 나갈때는 exit
컨테이너 로그 확인하기
docker logs -f [컨테이너이름]
- 컨테이너 상세 정보 확인하기
docker inspect [컨테이너이름]
- 컨테이너 내리기
docker stop [컨테이너이름]
- 컨테이너를 내려도 볼륨은 남는다! 볼륨까지 깔끔하기 지우자
docker rm $(docker ps -qa)
- 사용하지 않는 이미지 지우기
docker rmi $(docker images -qa)
#3. 여러 컨테이너를 하나의 네트워크로 묶기 (심화과정)
자! 이제 뭅스터에 필요한 FE, BE, redis 까지 3가지의 설정을 모두 해보자!
// BE Dockerfile
FROM node:lts
RUN apt-get update -y
RUN apt-get install -y git
RUN git clone https://github.com/movester/movester_server_product.git
ADD .env /movester_server_product
WORKDIR /movester_server_product
RUN npm install
EXPOSE 8000
CMD ["npm","run","start"]
여기서는 경량리눅스가 아닌 일반 노드를 가져왔다.
- 경량 리눅스에는 파이썬이 설치되어 있지 않기때문! (서버 코드에는 bcrypt 가 사용되는데 해당 라이브러리는 파이썬이 있어야한다.)
해당 이미지를 빌드하고, 컨테이너에 올려보자 (참고로 redis 컨테이너는 이미 띄어져있다.)
앗…! 실행이 안된다?
백앤드 컨테이너의 로그를 조회하면, 다음과 같이 레디스 연결이 안된 것을 확인할 수 있다.
흐음…좀 더 디버깅을 해보자
be 컨테이너 내부로 접속하여 telnet 을 설치한 후, ping을 해보았다.
apt-get update -y && apt-get install telnet -y
telnet redis 6379
- 역시 서로 네트워크 연결이 안되어있다. 왜그럴까?
컨테이너를 동일 네트워크로 묶기
이유는 소제목으로 짐작 가능하다시피, 각각 컨테이너의 네트워크가 동일하지 않기 때문이다.
네트워크를 생성하여 해당 컨테이너를 동일 네트워크로 묶어주자
- 뭅스터 네트워크 생성
docker network create [네트워크 이름]
- 네트워크 리스트 확인
docker network ls
- 컨테이너를 해당 네트워크로 띄우기 (fe, be, redis 세개 모두 동일 네트워크에 작업해야한다)
docker run —name [컨테이너이름] -itd —networkt [네트워크이름] -p [포트:포트] [이미지이름]
네트워크에 잘 묶였는지 확인해보자
docker network inspect [네트워크이름]
BE-REDIS 접속
자. 이제 be와 redis 의 접속을 확인해보자!
be 컨테이너 접속 후 telnet redis를 확인해보자
연결이 잘 됐다! 이때 172.29.0.2
ip를 기억하자!
- telnet 세션 접속 나가기
control + ]
telnet 나가기
q 누른 뒤 엔터
- 이때 redis는 엄밀히 말하면 localhost 가 아닌 private ip인
172.29.0.2
를 가지고 있기 때문에 be 코드에서 redis에 접근했던 host 를 변경해주어야 한다. - 해당 ip는 컨테이너마다 유동적으로 변한다! k8s 에서는 service를 사용하여 도메인네임을 지정할 수 있다.
// .env
// as-is
REDIS_HOST = "127.0.0.1";
// to-be
REDIS_HOST = "172.29.0.2";
be 컨테이너 내부로 직접 들어가서 .env 를 변경한 후 재실행해보자!
- 이때 우리의 컨테이너에는 vim이 없기때문에 vim을 한번 설치해주자
apt-get install vim -y
vi .env
잘 연결이 되었다!🤭
- api 응답도 잘 온다
FE-BE 접속
이제 fe-be 통신을 연결해주자!
마찬가지로 기존의 로컬호스트를 바라보던 api 호출 host를 연결된 네트워크 내의 private ip 로 변경해주어야 한다.
// fe
// src/services/defaultClient.js
baseURL: '/api',
// .env
// as-is
REACT_APP_SERVER_HOST = "http://127.0.0.1:8000";
// to-be
REACT_APP_SERVER_HOST = "http://[서버 컨테이너 ip]:[서버포트]";
fe 컨테이너를 restart 해주자!
접속이 잘 된다!
- 디버깅까지 해보자
- fe 컨테이너 내부 접속 후 curl 요청을 해보자
api-get install curl -y
curl [http://172.29.0.3:8000/api/weeks/expose](http://172.29.0.3:8000/api/weeks/expose)
- 역시 잘 연결되어진 것을 확인할 수 있다!👍
이로써 3개의 컨테이너를 띄우고 컨테이너들을 하나의 네트워크로 연결하였다🎉
한 가지 의문점 🤔
// fe
// src/services/defaultClient.js
baseURL: `http://${process.env.REACT_APP_SERVER_HOST}/api`,
REACT_APP_SERVER_HOST = "[서버 컨테이너 ip]:[서버포트]";
이렇게 작업을 하면 api 호출을 컨테이너 네트워크 내부의 private ip로 던지지 않고, 외부 public ip ( = 브라우저 검색창 GET 요청)로 요청이 되어 제대로 api 요청이 이루어지지 않는다. 🤔
#4. ECR 에 업로드하기
k8s 와 연동하기 위해 ECR에 이미지를 업로드하자!
유의점은 이미지 빌드 환경이 현재 M1 이기 때문에 multi os 를 지원하는 이미지를 생성해야 한다.
- entrypoint 방식을 사용해야 한다.
- 빌드 명령어
docker buildx build --platform linux/amd64 -t [image이름] .
// Dockerfile
FROM node:14.20.0-bullseye-slim
RUN apt-get update -y
RUN apt-get install -y git
RUN git clone https://github.com/movester/movester_client_product
WORKDIR /movester_client_product
ADD entrypoint.sh ./
RUN npm install
RUN npm install @material-ui/core
RUN chmod 755 entrypoint.sh
EXPOSE 7001
ENTRYPOINT ["./entrypoint.sh"]
// entrypoint.sh
#!/bin/bash
npm run start
ECR push 인증하기
aws ecr get-login-password --region ap-northeast-2 --profile dev | docker login --username AWS --password-stdin [ecr 이미지 url]