KDT - 컨테이너를 CLI에서 디테일하게 관리하기
1. 개요
도커의 컨테이너 가상화 기술은 도커엔진이라는 자체 스펙으로 바뀌었지만 LXC로부터 시작되었다.
LXC와 컨테이너 격리에 대해 알아보자.
2. 컨테이너 내부 측정
가상머신 생성시 도커와 OS 드라이버를 분리해주었다.
이제 /var 폴더에 '이미지'가 패키징된 스냅샷을 찍고 스냅샷을 활용해 프로세스를 띄우는 방식으로 진행해보자.
이미지 레이어에는 Read Only 레이어와 Read, Write가 가능한 컨테이너 레이어 두 종류가 있다.
이미지를 사용하여 띄운 컨테이너는 OS가 실행되며 Process id 1번을 부여받는다.
이어서 네트워크 설정, cpu, 메모리, 디스크 등도 할당 받는다.
즉, 도커는 리눅스의 불편한 절차를 자동으로 처리해주므로 편의성이 증대된다.
이제 내부 구조를 가져와보도록 하자.
$ docker run -it --rm --name=newcontainer ubuntu:14.04 bash
ubuntu 14.04버전의 bash를 띄우기
$ ls
시스템 영역에서 사용할법한 디렉터리 확인 (chroot or pivot_root라고 불림)
$ df -h
디바이스의 사용량이나 가용 자원 확인
+ host docker가 설치된 영역인 /devsdb1이 공유되는 것 확인(=mount namespace 기술)
UTS namespace
# hostname
컨테이너 아이디와 호스트네임을 동기화시키는 작업
PID or IPC namespace
# ps -ef
해당 프로세스가 작업을 수행하는데 필요한 것들을 격리시키는 기술
network namespace
# ifconfig
eth0 에 내부 ip가 할당된 것 확인
3. 컨테이너 격리 기술 정리
chroot → 프로세스의 루트 디렉토리를 변경, 격리해 가상의 루트 디렉토리 배정
pivot root → 루트 파일시스템을 바꿔, 컨테이너가 전용 루트 파일시스템을 가지도록 함
mount namespace → namespace 내에 파일 시스템 트리 구성
uts namespace → host와 다른 별개의 hostname을 가지도록 함
pid namespace → pid와 프로세스를 분리(systemd와 분리, 우분투의 1번 프로세스인데 안잡힘)
network namespace → 네트워크 리소스 할당(ip, port, route table, ethernet 등)
ipc namespace → 전용 process table 보유
$ lsns
격리된 namespace 목록 조회
4. 컨테이너의 라이프사이클
- create → image의 스냅샷으로 /var/lib/docker 영역 내에 컨테이너가 생성됨
- start → process영역에 컨테이너를 생성해 실행 상태로 함
- stop → process 영역에서 컨테이너를 제거해 종료 상태로 함
- rm → create로 생성된 스냅샷을 삭제
+) run : create + start
실습
$ docker pull ubuntu:16.04
먼저 ubuntu 받아오기
$ docker create -it --name=ubuntu16-1 ubuntu:16.04
새로운 컨테이너 생성 (스냅샷을 생성만 하고 프로세스로 가동하지 않은 상태. created 상태)
$ docker start ubuntu16-1
start로 프로세스로 올려서 실행 (상태가 Up으로 변경)
$ docker stop ubuntu16-1
stop으로 종료 (상태가 Exited로 변경. 언제든 다시 start 가능)
$ docker rm ubuntu16-1
마지막 단계인 삭제
run 수행 실습
$ docker run -it --name=ubuntu16-2 ubuntu:16.04 bash
bash 창 접속
+) 기동 중인 컨테이너 명령창은 attach, exec 등의 명령어로 접속 가능
격리 환경이므로 업데이트와 의존성 설정 다시 해주기 (ex. apt update, apt -y install net-tools)
5. 컨테이너 내부 구조 상세히 파악하기
$ docker run -it --name=ubuntu16-2 ubuntu:16.04 bash
exit 후 다시 사용자 계정으로 돌아와 컨테이너 띄우기
bash에서 hostname, echo, cat 등 명령 여러가지 수행 ->
$ ps -ef | grep ubuntu
우분투 bash 창 외에 새 터미널에서 프로세스 조회 ->
도커 컨테이너가 프로세스로 잡힌 모습 확인 가능
스냅샷(이미지)는 Read Only지만 컨테이너(프로세스)는 Read,Write가 가능하다.
확인을 위해 ctrl + p + q 로 host에서 나와서 root 상태에서
# find /var/lib/docker -name container.txt
find 명령으로 container.txt 찾기
merged 경로로 이동한 다음 ls를 사용하면 컨테이너 내부 파일 구성 확인 가능
즉, 컨테이너 내부는 overlay2라는 스토리지 영역에 포함된 컨테이너 아이디 폴더에 종속되어 있는 파일들의 집합
다시 다른 터미널에서
$ docker exec -it ubuntu14-1 bash
조회시 같은 내용 확인 가능.
+) 루트 계정에서 touch 등으로 파일 생성 후 컨테이너 내부 조회시 반영됨을 확인 가능
정리하면, 컨테이너 내부의 정보는 container layer + snapshot 영역에 저장되며
컨테이너는 하나의 폴더를 생성하고 그 지점을 독립된 기기처럼 활용하는 것이다.
6. 컨테이너 운영에 필요한 명령어들
https://docs.docker.com/engine/reference/commandline/container/
docker container
docs.docker.com
컨테이너 운영을 살펴보기 위한 노드 웹서버 구축
const http = require('http');
const server = http.createServer().listen(5678);
server.on('request', (req, res) => {
console.log('request');
res.write("HostName: " + process.env.HOSTNAME + "\n");
res.end();
});
server.on('connection', (socket) => {
console.log("connected.");
});
app.js
FROM node:20-alpine3.17
RUN apk add --no-cache tini curl
WORKDIR /app
COPY app.js .
EXPOSE 5678
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["node", "app.js"]
Dockerfile
//
FROM : 어떤 os와 어떤 프레임워크 위에서 돌릴지
RUN : 알파인 리눅스이므로 apk로 구동
WORKDIR : /app 이라는 경로를 생성한 다음 cd /app 을 실행하라는 의미
COPY : Dockerfile과 같은 경로의 app.js를 현재경로(. → /app) 에 복사해주라는 의미
EXPOSE : 포트바인딩시 컨테이너측의 노출포트가 5678이라는 의미
ENTRYPOINT : CMD는 내부적으로 그 app.js를 실행해주는 명령어
$ docker build -t nodeapp:1.0 .
이미지 생성
$ docker run -itd -p 5678:5678 --name=nodeapp -h nodeapp nodeapp:1.0
구동
// --env → 컨테이너의 환경변수 지정
-d, --detach=true → 백그라운드 실행 모드 활성화 + 컨테이너 아이디 등록
-t → TTY(단말 디바이스) 할당(bash창같은거 열어주기)
-i, --interactive → 대화식 모드 열기(컨테이너 내부에 명령어 주고받기)
--name → 실행되는 컨테이너에 이름 부여(미지정시 랜덤한 2단어 조합명으로 부여)
--rm → 컨테이너 종료시 자동으로 컨테이너 제거(stop시 삭제)
--restart → 컨테이너 종료시 적용할 재시작 정책 지정(no, on-failure, on-failure:n(횟수), always)
-v, --volume=호스트경로:컨테이너의경로 → 볼륨설정(볼륨마운트)
-h → 컨테이너의 호스트명 지정(미 지정시 컨테이너 아이디를 호스트명으로 등록)
-p 호스트포트:컨테이너포트, --publish → 호스트포트와 컨테이너포트를 바인딩
-P(대문자), --publish-all=true|false → 컨테이너 내부의 EXPOSE 포트를 랜덤포트와 바인딩
--workdir, -w → 컨테이너 내부의 작업 경로(디렉터리)
$ docker top 컨테이너명
프로세스 상태 확인
$ docker port 컨테이너명
포트 정보 확인
$ sudo netstat -nlp | grep 조회포트번호
대리포트 조회
$ ps -ef | grep 프록시번호
위의 프록시 값을 토대로 해당 포트바인딩 명령어의 정보가 저장된 위치 확인
$ docker stats 컨테이너명
실시간 자원 소비 확인
7. 모니터링용 이미지 및 컨테이너로 상태 감시하기
cadvisor라는 이미지와 컨테이너로 docker stats보다 전문적인 감시가 가능하다.
도커허브가 아닌 gcr에 올라와있는 이 이미지는 아래와 같이 복잡한 명령어로 볼륨마운트를 해야만 볼 수 있다.
$ docker run \
--restart=always \
--volume=/:/rootfs:ro \
--volume=/var/run:/var/run:rw \
--volume=/sys/fs/cgroup:/sys/fs/cgroup:ro \
--volume=/var/lib/docker/:/var/lib/docker:ro \
--volume=/dev/disk/:/dev/disk:ro \
--publish=8765:8080 \
--detach=true \
--name=cadvisor \
--privileged \
--device=/dev/kmsg \
gcr.io/cadvisor/cadvisor:latest
cadvisor 컨테이너 조회 -> 브라우저에서 접속 -> 메트릭 형태로 실시간 트래픽 확인 가능
헬스체크 : 서버의 가동 여부를 지속적으로 확인하는 행위
$ while true; do curl node서버주소; sleep 초단위; done
sleep으로 딜레이를 주기 때문에 몇 초 마다 한번씩 node서버에 접속
$ docker logs -f 컨테이너명
nodeapp에 지속적으로 요청을 넣어 찍히는 log를 확인
$ docker info | grep -i log
json파일로 log관련 내용 저장
그러나 파일이 커지면 디스크가 모자랄 수 있다.
$ sudo ls -l /var/lib/docker/containers
특정 컨테이너에서 발생한 로그 조회
$ sudo truncate -s 0 /var/lib/docker/contaners/컨테이너아이디-json.log
로그 비우기 // 초기화
$ sudo vi /etc/docker/daemon.json
로그 파일 사이즈 제한
접속 후
{
"insecure-registries": ["아이피주소"],
"log-driver": "json-file",
"log-opts": {
"max-size": "30m",
"max-file": "10"
}
}
$ sudo systemctl restart docker.service
$ sudo systemctl status docker.service
차례로 수행
or
docker run 수행 당시 해당 옵션 포함
$ docker run \
--log-driver json-file \
--log-opt max-size=30m \
--log-opt max-file=10
상단 방식은 stdout(표준출력)만을 반영하는 로그임으로 에러상황의 로그를 보려면
$ docker run -itd --name=mysql-5 mysql:5.7-debian
환경변수를 포함하지 않은 컨테이너로 에러를 띄우고 stop 시킨다.
$ docker ps -a | grep mysql
조회시 상태가 exited인것을 확인하고
$ docker logs mysql-5
에러메세지가 뜨는 것을 확인하면 된다.