> For the complete documentation index, see [llms.txt](https://container.youngwjung.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://container.youngwjung.com/intro-container.md).

# 컨테이너 기초

### Chroot

1. 새로운 루트 디렉토리로 사용할 디렉토리 생성

   ```
   mkdir ~/environment/new-root
   ```
2. 위에서 생성한 디렉토리를 루트 디렉토리로 변경 시도

   ```
   sudo chroot ~/environment/new-root
   ```
3. 새로운 루트로 사용할 디렉토리 하위에 bin 디렉토리 생성

   ```
   mkdir ~/environment/new-root/bin
   ```
4. 호스트에 있는 /bin/bash 파일을 새로운 루트 디렉토리로 사용할 디렉토리 하위의 bin 디렉토리로 복사

   ```
   cp /bin/bash ~/environment/new-root/bin
   ```
5. 루트 디렉토리 변경 재시도

   ```
   sudo chroot ~/environment/new-root
   ```
6. Bash 실행을 위해서 필요한 라이브러리 연관성 확인

   ```
   ldd /bin/bash
   ```
7. 새로운 루트로 사용할 디렉토리로 Bash 실행에 필요한 연관 라이브러리 파일 복사&#x20;

   ```
   {
       mkdir ~/environment/new-root/lib64

       cp /lib64/libtinfo.so.6 ~/environment/new-root/lib64
       cp /lib64/libc.so.6 ~/environment/new-root/lib64
       cp /lib64/ld-linux-x86-64.so.2 ~/environment/new-root/lib64
   }
   ```
8. 루트 디렉토리 변경 재시도

   ```
   sudo chroot ~/environment/new-root
   ```
9. 현재 작업중인 디렉토리 확인

   ```
   pwd
   ```
10. 아래의 명령어를 실행해서 위에서 실행한(새로운 루트 디렉토리에서 실해된) Bash 프로세스 종료

    ```
    exit
    ```
11. 새로운 루트로 사용할 디렉토리 하위에 텍스트 파일 생성

    ```
    date > ~/environment/new-root/date.txt
    ```
12. 새로운 루트로 사용할 디렉토리로 /bin/ls 파일 및 ls 실행에 필요한 연관 라이브러리 파일 복사

    ```
    {
        cp /bin/ls ~/environment/new-root/bin
        cp /lib64/libselinux.so.1 ~/environment/new-root/lib64
        cp /lib64/libcap.so.2 ~/environment/new-root/lib64
        cp /lib64/libpcre2-8.so.0 ~/environment/new-root/lib64
    }
    ```
13. 루트 디렉토리 변경

    ```
    sudo chroot ~/environment/new-root
    ```
14. 위에서 생성한 텍스트파일이 보이는지 확인

    ```
    ls
    ```
15. 현재 작업중인 디렉토리 확인

    ```
    pwd
    ```
16. 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

    ```
    exit
    ```
17. 연습 문제
    1. 새로운 루트로 사용할 디렉토리로 /bin/cat 파일 및 cat 실행에 필요한 연관 라이브러리 파일 복사
    2. 루트 디렉토리를 변경하고 cat 명령어로 텍스트 파일 내용 확인

<details>

<summary>문제 답안</summary>

1. 새로운 루트로 사용할 디렉토리로 /bin/cat 파일 및 cat 실행에 필요한 연관 라이브러리 파일 복사

   ```
   cp /bin/cat ~/environment/new-root/bin
   ```
2. 루트 디렉토리 변경

   ```
   sudo chroot ~/environment/new-root
   ```
3. 위에서 생성한 텍스트 파일 내용 확인

   ```
   cat date.txt
   ```
4. 새로운 루트 디렉토리로 실행된 Bash 프로세스 종료

   ```
   exit
   ```

</details>

### Container Image

1. Docker Hub에 있는 nginx 이미지로 컨테이너 생성&#x20;

   ```
   docker create --name nginx nginx 
   ```
2. 로컬 호스트에 생성된 컨테이너 목록  확인

   ```
   docker container ls -a
   ```
3. nginx 컨테이너에 포함된 파일들을 저장할 디렉토리 생성 &#x20;

   ```
   mkdir ~/environment/nginx
   ```
4. 위에서 생성한 디렉토리로 nginx 컨테이너의 파일들을 복사

   ```
   docker cp $(docker container ls -aq -f name=nginx):/ ~/environment/nginx
   ```
5. nginx 컨테이너가 실행될때 수행되는 명령어 확인&#x20;

   ```
   docker inspect $(docker container ls -aq -f name=nginx) --format='{{.Config.Cmd}}'
   ```
6. nginx 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 변경

   ```
   sudo chroot ~/environment/nginx
   ```
7. Nginx 웹서버 실행&#x20;

   ```
   nginx -g "daemon off;"
   ```
8. 새로운 터미널을 열고 Nginx 웹서버 구동 확인&#x20;

   ```
   curl localhost
   ```
9. 연습 문제
   1. 두번째 터미널에서 ubuntu 이미지로 컨테이너를 생성
   2. 해당 컨테이너의 파일들을 \~/environment/ubuntu 로 복사
   3. \~/environment/ubuntu 디렉토리로 루트 디렉토리로 변경. 단 /bin/bash가 아닌 /bin/sh 를 실행

<details>

<summary>문제 답안</summary>

1. ubuntu 이미지로 컨테이너 생성

   ```
   docker create --name ubuntu ubuntu
   ```
2. ubuntu 컨테이너에 포함된 파일들을 저장할 디렉토리 생성 &#x20;

   ```
   mkdir ~/environment/ubuntu
   ```
3. 위에서 생성한 디렉토리로 ubuntu 컨테이너의 파일들을 복사

   ```
   docker cp $(sudo docker container ls -aq -f name=ubuntu):/ ~/environment/ubuntu
   ```
4. ubuntu 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 변경

   ```
   sudo chroot ~/environment/ubuntu /bin/sh
   ```
5. 현재 작업중인 디렉토리 확인

   ```
   pwd
   ```
6. 파일 목록 확인&#x20;

   ```
   ls
   ```
7. 정상 동작시 새로운 루트 디렉토리로 실행된 sh 프로세스 종료

   ```
   exit
   ```

</details>

### Network Namespace

1. 첫번째 터미널에서 Ctrl+C를 입력해서 Nginx 프로세스 종료
2. 새로운 루트 디렉토리에서 실행된 Bash 프로세스 종료

   ```
   exit
   ```
3. 새로운 네트워크 네임스페이스 생성

   ```
   sudo ip netns add nginx
   ```
4. 네트워크 네임스페이스 생성 확인

   ```
   sudo ip netns list
   ```
5. 위에서 생성한 네트워크 네임스페이스의 네트워크 디바이스 목록 확인

   ```
   sudo ip netns exec nginx ip -br link
   ```
6. nginx 네트워크 네임스페이스의 루프백 인터페이스 구동

   ```
   sudo ip netns exec nginx ip link set dev lo up
   ```
7. nginx 네트워크 네임스페이스를 사용하면서 nginx 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 변경

   ```
   sudo ip netns exec nginx chroot ~/environment/nginx
   ```
8. Nginx 웹서버 실행&#x20;

   ```
   nginx -g "daemon off;"
   ```
9. 두번째 터미널로 이동해서 Nginx 웹서버 구동 확인

   ```
   curl localhost
   ```
10. nginx 네트워크 네임스페이스의 루프백 인터페이스를 통해서 접근 시도

    ```
    sudo ip netns exec nginx curl localhost
    ```
11. 첫번째 터미널로 이동
12. Ctrl+C를 입력해서 Nginx 프로세스 종료
13. 새로운 루트 디렉토리에서 실행된 Bash 프로세스 종료

    ```
    exit
    ```
14. 디폴트 네트워크 네임스페이스에 가상 네트워크 인터페이스 추가

    ```
    sudo ip link add veth0 type veth peer name veth1
    ```
15. 생성된 가상 네트워크 인터페이스 확인&#x20;

    ```
    sudo ip -br link
    ```
16. 디폴트 네트워크 네임스페이스에 생성된 네트워크 디바이스 veth1를 nginx 네트워크 네임스페이스로 이동

    ```
    sudo ip link set veth1 netns nginx
    ```
17. 디폴트 네트워크 네임스페이스의 네트워크 디바이스 목록 확인

    ```
    sudo ip -br link
    ```
18. nginx 네트워크 네임스페이스의 네트워크 디바이스 목록 확인

    ```
    sudo ip netns exec nginx ip -br link
    ```
19. 디폴트 네트워크 네임스페이스의 네트워크 디바이스 veth0에 IP 주소 할당

    ```
    sudo ip addr add 192.168.1.1/24 dev veth0
    ```
20. nginx 네트워크 네임스페이스의 네트워크 디바이스 veth1에 IP 주소 할당

    ```
    sudo ip netns exec nginx ip addr add 192.168.1.2/24 dev veth1
    ```
21. 네트워크 디바이스 veth0 구동

    ```
    sudo ip link set dev veth0 up
    ```
22. 네트워크 디바이스 veth1 구동

    ```
    sudo ip netns exec nginx ip link set dev veth1 up
    ```
23. 디폴트 네트워크 네임스페이스의 네트워크 디바이스 상태 확인

    ```
    sudo ip -br link
    ```
24. nginx 네트워크 네임스페이스의 네트워크 디바이스 상태 확인

    ```
    sudo ip netns exec nginx ip -br link
    ```
25. nginx 네트워크 네임스페이스를 사용하면서 nginx 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 변경

    ```
    sudo ip netns exec nginx chroot ~/environment/nginx
    ```
26. IP 주소 확인&#x20;

    ```
    hostname -I
    ```
27. Nginx 웹서버 실행&#x20;

    ```
    nginx -g "daemon off;"
    ```
28. 두번째 터미널로 이동해서 IP 주소 확인

    ```
    hostname -I
    ```
29. Nginx 웹서버 구동 확인

    ```
    curl 192.168.1.2
    ```
30. 첫번째 터미널로 이동
31. Ctrl+C를 입력해서 Nginx 프로세스 종료
32. Nginx 접근 로그 확인&#x20;

    ```
    cat /var/log/nginx/access.log       
    ```

### PID Namespace

1. 첫번째 터미널에서 Nginx 웹서버 구동&#x20;

   ```
   nginx -g "daemon off;"
   ```
2. 두번째 터미널에 ubuntu 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 사용하는 sh 프로세스 실행&#x20;

   ```
   sudo chroot ~/environment/ubuntu /bin/sh
   ```
3. 새로운 터미널을 열고 첫번째 터미널에서 구동한 nginx 프로세스의 PID를 확인&#x20;

   ```
   ps aux | grep nginx
   ```
4. 두번째 터미널에서 kill 명령어를 통해서 **모든** nginx 프로세스 종료를 시도

   ```
   kill -9 NGINX_PID
   ```
5. 첫번째 터미널로 이동해서 실제로 nginx 프로세스가 종료되었는지 확인
6. 첫번째 터미널에서 종료된 nginx 프로세스를 재기동&#x20;

   ```
   nginx -g "daemon off;"
   ```
7. 만약 nginx 프로세스 재기동에 실패할 경우 세번째 터미널로 이동해서 아래의 명령어로 모든 nginx 프로세스를 종료하고 위의 명령어 재실행

   ```
   sudo pkill nginx
   ```
8. 세번째 터미널로 이동해서 리눅스 네임스페이스 목록 확인

   ```
   lsns
   ```
9. nginx 프로세스의 PID를 확인&#x20;

   ```
   ps aux | grep nginx
   ```
10. nginx 프로세스가 실행되는 PID 네임스페이스 확인

    ```
    sudo ls -Li /proc/NGINX_PID/ns/pid
    ```
11. sh 프로세스의 PID를 확인

    ```
    ps aux | grep /bin/sh
    ```
12. sh 프로세스가 실행되는 PID 네임스페이스 확인

    ```
    sudo ls -Li /proc/SH_PID/ns/pid
    ```
13. 두번째 터미널로 이동해서 /proc 파일시스템 마운트

    ```
    {
        chown -R root:root .
        mount -t proc proc /proc
    }
    ```
14. nginx 프로세스의 PID를 확인&#x20;

    ```
    ps aux | grep nginx
    ```
15. ubuntu 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 실행되고 있는 sh 프로세스 종료

    ```
    exit
    ```
16. 새로운 PID 네임스페이스를 생성하고 ubuntu 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 하는 sh 프로세스 실행

    ```
    sudo unshare --pid --fork chroot ~/environment/ubuntu /bin/sh
    ```
17. /proc 파일시스템 마운트

    ```
    mount -t proc proc /proc
    ```
18. 실행중인 프로세스 목록 확인&#x20;

    ```
    ps aux
    ```
19. 세번째 터미널로 이동해서 첫번째 터미널에서 구동중인 nginx 프로세스의 PID 확인

    ```
    ps aux | grep nginx
    ```
20. 두번째 터미널으로 (PID가 격리된 ubuntu 환경) 이동해서 nginx 프로세스 종료 시도 &#x20;

    ```
    kill -9 NGINX_PID
    ```
21. 세번째 터미널로 이동해서 PID 네임스페이스 목록 확인

    ```
    sudo lsns -t pid
    ```
22. sh 프로세스의 PID를 확인

    ```
    ps aux | grep /bin/sh
    ```
23. sh 프로세스 종료 시도

    ```
    sudo kill -9 SH_PID
    ```
24. 두번째 터미널로 이동해서 sh 프로세스가 종료되었는지 확인&#x20;
25. PID 네임스페이스 목록 확인

    ```
    sudo lsns -t pid
    ```

### Cgroup

1. 두번째 터미널로 이동해서 새로운 PID 네임스페이스를 생성하고 ubuntu 컨테이너 파일들이 저장된 디렉토리를 루트 디렉토리로 하는 sh 프로세스 실행

   ```
   sudo unshare --pid --fork chroot ~/environment/ubuntu /bin/sh
   ```
2. 아래와 같은 명령어를 실행해서 CPU 부하를 발생

   ```
   {
       mknod /root/null c 1 3
       yes > /root/null
   }
   ```
3. 세번째 터미널로 이동해서 CPU 사용량 확인

   ```
   top
   ```
4. cgroup-tools 설치

   ```
   sudo dnf install libcgroup-tools -y
   ```
5. 새로운 cgroup 생성&#x20;

   ```
   sudo cgcreate -g cpu,memory:/ubuntu
   ```
6. 격리된 ubuntu 환경에서 실행중인 sh 프로세스의 PID를 확인

   ```
   ps aux | grep /bin/sh
   ```
7. sh 프로세스에  위에서 생성한 cgroup 부여&#x20;

   ```
   sudo cgclassify -g cpu,memory:ubuntu SH_PID
   ```
8. CPU 사용률을 30%로 제한&#x20;

   ```
   sudo cgset -r cpu.max="300000 1000000" ubuntu
   ```
9. CPU 사용량 확인

   ```
   top
   ```
10. 두번째 터미널로 이동해서 실행중인 프로세스를 종료 - Ctrl+Shift+Z
11. CPU 부하를 다시 발생시키고 CPU 사용률이 30%로 제한되는지 확인&#x20;

    ```
    yes > /root/null
    ```
12. 메모리 사용량을 100M로 제한하고 아래와 같은 명령어를 실행해서 확인&#x20;

    ```
    {
        mknod /root/zero c 1 5
        head -c 1000M /root/zero | tail
    }
    ```

<details>

<summary>문제 답안</summary>

1. 세번째 터미널로 이동해서 메모리 제한량 설정&#x20;

   ```
   sudo cgset -r memory.max=100M ubuntu
   ```
2. 두번째 터미널로 이동해서 메모리 부하 발생

   ```
   {
     mknod /tmp/zero c 1 5
     head -c 1000M /tmp/zero | tail
   }
   ```
3. 메모리 부하를 발생하는 프로세스가 강제 종료될 경우 아래의 명령어로 원인 파악

   ```
   sudo dmesg | tail -n 100
   ```

</details>

### 리소스 정리

1. 모든 터미널을 닫고 새로운 터미널 생성
2. 리소스 삭제

   ```
   {
       sudo umount -l ubuntu/proc
       sudo rm -rf new-root nginx ubuntu
       docker container rm $(docker ps -aq)
       sudo cgdelete -g cpu,memory:/ubuntu
       docker image prune --all --force
   }
   ```
