Kubernetes

[Kans 3 Study - 1.1 ] 도커 컨테이너 격리

su''@ 2024. 9. 1. 03:33
CloudNetaStudy - Kubernets Networtk 3기 실습 스터디 게시글입니다.

 

  • 컨테이너는 독립된 리눅스 환경(pivot-root, namespace, Overlay filesystem, cgroup)을 보장받는 프로세스이다! - Pivot_root
 

Why Pivot Root is Used for Containers

In this post, you will learn about the differences between only chroot and chroot after pivot_root in detail. You will realise the reason why it is used in containerization.

tbhaxor.com

  • 컨테이너는 애플리케이션(프로세스) 동작에 필요한 파일들만 패키징된 이미지를 실행하여 동작합니다!
  • 컨테이너는 컨테이너 환경이 조성된곳 어디에서나(온프레미스/클라우드 환경)에서도 실행할 수 있다.

 

실습 환경 구성

 

사전 준비 : AWS 계정, SSH 키 페어

구성 : VPC 1개(퍼블릭 서브넷 2개), EC 인스턴스 1대(Ubuntu 22.04 LTS, t3.small)

  • CloudFormation 스택 실행 시 파라미터를 기입하면, 해당 정보가 반영되어 배포됩니다.
  • CloudFormation 에 EC2의 UserData 부분(Script 실행)으로 실습 환경에 필요한 기본 설정들이 자동으로 진행됩니다.
  • CloudFormation 스택 배포 ← 실행하는 PC에 aws cli 설치되어 있고, aws configure 자격증명 설정 상태.
# YAML 파일 다운로드
curl -O https://s3.ap-northeast-2.amazonaws.com/cloudformation.cloudneta.net/kans/kans-1w.yaml

# CloudFormation 스택 배포
# aws cloudformation deploy --template-file kans-1w.yaml --stack-name mylab --parameter-overrides KeyName=<My SSH Keyname> SgIngressSshCidr=<My Home Public IP Address>/32 --region ap-northeast-2
예시) aws cloudformation deploy --template-file kans-1w.yaml --stack-name mylab --parameter-overrides KeyName=sshkey SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

## Tip. 인스턴스 타입 변경 : MyInstanceType=t2.micro
예시) aws cloudformation deploy --template-file kans-1w.yaml --stack-name mylab --parameter-overrides MyInstanceType=t2.micro KeyName=<My SSH Keyname> SgIngressSshCidr=$(curl -s ipinfo.io/ip)/32 --region ap-northeast-2

# CloudFormation 스택 배포 완료 후 작업용 EC2 IP 출력
aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2

# [모니터링] CloudFormation 스택 상태 : 생성 완료 확인
while true; do 
  date
  AWS_PAGER="" aws cloudformation list-stacks \
    --stack-status-filter CREATE_IN_PROGRESS CREATE_COMPLETE CREATE_FAILED DELETE_IN_PROGRESS DELETE_FAILED \
    --query "StackSummaries[*].{StackName:StackName, StackStatus:StackStatus}" \
    --output table
  sleep 1
done

# Ansible Server EC2 SSH 접속
ssh -i ~/.ssh/tndk617.pem ubuntu@$(aws cloudformation describe-stacks --stack-name mylab --query 'Stacks[*].Outputs[0].OutputValue' --output text --region ap-northeast-2)

  • EC2 생성 수량 부족 실패 시 : 사용자 계정의 해당 리전에 EC2 최대 갯수 제한 일 경우, Service Quotas (EC2) 증설 요청으로 해결 - 링크 EC2 요청
    • Limit Type(EC2 Instances) ⇒ 서울 리전, All Standard (A, C, D, H, I, M, R, T, Z) Instances, New limit value(40 정도)
 

https://support.console.aws.amazon.com/support/home#/case/create?issueType=service-limit-increase&limitType=service-code-ec2-instances&serviceLimitIncreaseType=ec2-instances&type=service_limit_increase

 

support.console.aws.amazon.com

 

 

 

[  1. 도커 소개  ]

1.0 도커란? 

 

도커(Docker)가상실행 환경을 제공해주는 오픈소스 플랫폼입니다. 도커에서는 이 가상실행 환경을 '컨테이너(Container)'라고 부릅니다.

  • 좀 더 정확히 표현하는 용어는 '컨테이너화된 프로세스(Containerized Process)' 이다
  • 도커 플랫폼이 설치된 곳이라면 컨테이너로 묶인 애플리케이션을 어디서든 실행할 수 있는 장점을 가집니다.

https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/

 

Process 란? 

 

1.1 컨테이너와 가상머신

 

도커 컨테이너 vs 가상 머신

https://www.docker.com/resources/what-container

 

* 가상머신은 운영체제 위에 하드웨어를 에뮬레이션하고 그 위에 운영체제를 올리고 프로세스를 실행하는 반면에,

   도커 컨테이너는 하드웨어 에뮬레이션 없이 리눅스 커널을 공유해서 바로 프로세스를 실행한다

* [심화] 퀴즈. 보안의 측면에서 '가상 머신' 환경과 '컨테이너' 환경 중 좀 더 격리 수준이 높은쪽은 어디일까요?

 (1) 가상 머신 , (2) 컨테이너

더보기

컨테이너 환경에서는 호스트 OS(커널)을 공유한다 → 약한 격리(Weak Isolation)

 

  • 가상 머신은 기존의 서버에 하이퍼바이저를 설치하고, 그 위에 가상 OS와 패키징한 VM을 만들어 실행하는 방식인 하드웨어 레벨의 가상화를 지원합니다.
  • 반면, 컨테이너는 운영체제를 제외한 나머지 애플리케이션 실행에 필요한 모든 파일을 패키징한다는 점에서 OS레벨 가상화를 지원합니다. 컨테이너는 게스트OS 와 하이버파이저가 없기 때문에 이로 인한 오버헤드를 줄임으로써 훨씬 더 가볍게 프로세스를 실행할 수 있고 컨테이너에 대한 복제와 배포가 더 용이합니다.
    • 컨테이너는 호스트의 커널을 공유하지만, 개별적인 '사용자 공간(user space)' 를 가집니다.
    • 가상화된 공간을 생성하기 위해 리눅스 기능 pivot-root, 네임스페이스(namespace), cgroup 를 사용함으로써 프로세스 단위의 격리 환경과 리소스을 제공합니다.

 

 1.2 도커 아키텍처

https://docs.docker.com/get-started/docker-overview/#docker-architecture
https://docs.docker.com/get-started/overview/#docker-architecture

 

https://blog.naver.com/yu3papa/223562337709

 

[ 2. 도커 기본 사용 ]

보안을 위해서 root 계정을 사용을 최소화해야합니다. 다만, 스터디 실습 환경에서만 좀 더 편하게 사용하기 위해서 일부 실습에서 root 계정을 사용합니다.
2.1 사전 준비 사항

 

[기초] Linux Process 이해 : /proc - Blog

  • 프로세스실행 중인 프로그램의 인스턴스를 의미. OS에서 프로세스를 관리하며, 각 프로세스는 고유한 ID(PID)를 가짐.
  • 프로세스는 CPU와 메모리를 사용하는 기본 단위로, OS 커널(Cgroup)에서 각 프로세스의 자원을 관리함.
# 프로세스 정보 확인
ps

# /sbin/init 1번 프로세스 확인
# 프로세스별 CPU 차지율, Memory 점유율, 실제 메모리 사용량 등 확인 >> 비율로 표현되는 이유는?
ps aux
ps -ef

# 프로세스 트리 확인
pstree --help
pstree
pstree -a
pstree -p
pstree -apn
pstree -apnT
pstree -apnTZ
pstree -apnTZ | grep -v unconfined

# 실시간 프로세스 정보 출력
top -d 1
htop

# 특정 프로세스 정보 찾기
pgrep -h

# [터미널1]
sleep 10000

# [터미널2]
pgrep sleep
pgrep sleep -u root
pgrep sleep -u ubuntu

 

  • 리눅스의 /proc 디렉터리는 커널이 동적으로 생성하는 정보를 실시간 제공 : 시스템 상태, 프로세스(/proc/[PID]), HW 정보
    • /proc/cpuinfo: CPU에 대한 정보가 포함되어 있습니다. CPU 모델, 코어 수, 클럭 속도 등의 정보를 확인할 수 있습니다.
    • /proc/meminfo: 메모리 사용 현황을 보여줍니다. 전체 메모리, 사용 중인 메모리, 가용 메모리, 캐시 메모리 등 다양한 메모리 관련 정보를 제공합니다.
    • /proc/uptime: 시스템이 부팅된 후 경과된 시간을 초 단위로 보여줍니다. 첫 번째 숫자는 총 가동 시간, 두 번째 숫자는 시스템의 유휴 시간입니다.
    • /proc/loadavg: 시스템의 현재 부하 상태를 나타냅니다. 첫 번째 세 개의 숫자는 1, 5, 15분간의 시스템 부하 평균을 의미하며, 네 번째 숫자는 현재 실행 중인 프로세스와 총 프로세스 수, 마지막 숫자는 마지막으로 실행된 프로세스의 PID를 나타냅니다.
    • /proc/version: 커널 버전, GCC 버전 및 컴파일된 날짜와 같은 커널의 빌드 정보를 포함합니다.
    • /proc/filesystems: 커널이 인식하고 있는 파일 시스템의 목록을 보여줍니다.
    • /proc/partitions: 시스템에서 인식된 파티션 정보를 제공합니다. 디스크 장치와 해당 파티션 크기 등을 확인할 수 있습니다.
  • 프로세스(/proc/[PID]) 별 정보
    • /proc/[PID]/cmdline: 해당 프로세스를 실행할 때 사용된 명령어와 인자를 포함합니다.
    • /proc/[PID]/cwd: 프로세스의 현재 작업 디렉터리에 대한 심볼릭 링크입니다. ls -l로 확인하면 해당 프로세스가 현재 작업 중인 디렉터리를 알 수 있습니다.
    • /proc/[PID]/environ: 프로세스의 환경 변수를 나타냅니다. 각 변수는 NULL 문자로 구분됩니다.
    • /proc/[PID]/exe: 프로세스가 실행 중인 실행 파일에 대한 심볼릭 링크입니다.
    • /proc/[PID]/fd: 프로세스가 열어놓은 모든 파일 디스크립터에 대한 심볼릭 링크를 포함하는 디렉터리입니다. 이 파일들은 해당 파일 디스크립터가 가리키는 실제 파일이나 소켓 등을 참조합니다.
    • /proc/[PID]/maps: 프로세스의 메모리 맵을 나타냅니다. 메모리 영역의 시작과 끝 주소, 접근 권한, 매핑된 파일 등을 확인할 수 있습니다.
    • /proc/[PID]/stat: 프로세스의 상태 정보를 포함한 파일입니다. 이 파일에는 프로세스의 상태, CPU 사용량, 메모리 사용량, 부모 프로세스 ID, 우선순위 등의 다양한 정보가 담겨 있습니다.
    • /proc/[PID]/status: 프로세스의 상태 정보를 사람이 읽기 쉽게 정리한 파일입니다. PID, PPID(부모 PID), 메모리 사용량, CPU 사용률, 스레드 수 등을 확인할 수 있습니다.
      #
      mount -t proc
      findmnt /proc
      TARGET SOURCE FSTYPE OPTIONS
      /proc  proc   proc   rw,nosuid,nodev,noexec,relatime
      
      #
      ls /proc
      tree /proc -L 1
      tree /proc -L 1 | more
      
      # 커널이 동적으로 생성하는 정보
      cat /proc/cpuinfo
      cat /proc/meminfo
      cat /proc/uptime
      cat /proc/loadavg
      cat /proc/version
      cat /proc/filesystems
      cat /proc/partitions
      
      # 실시간(갱신) 정보
      cat /proc/uptime
      cat /proc/uptime
      cat /proc/uptime
      
      # 프로세스별 정보
      ls /proc > 1.txt
      
      # [터미널1]
      sleep 10000
      
      # [터미널2]
      ## 프로세스별 정보
      ls /proc > 2.txt
      ls /proc
      diff 1.txt 2.txt
      pstree -p
      ps -C sleep
      pgrep sleep
      
      ## sleep 프로세스 디렉터리 확인
      tree /proc/$(pgrep sleep) -L 1
      tree /proc/$(pgrep sleep) -L 2 | more
      
      ## 해당 프로세스가 실행한 명령 확인
      cat /proc/$(pgrep sleep)/cmdline ; echo
      
      ## 해당 프로세스의 Current Working Directory 확인
      ls -l /proc/$(pgrep sleep)/cwd
      
      ## 해당 프로세스가 오픈한 file descriptor 목록 확인
      ls -l /proc/$(pgrep sleep)/fd
      
      ## 해당 프로세스의 환경 변수 확인
      cat /proc/$(pgrep sleep)/environ ; echo
      cat /proc/$(pgrep sleep)/environ | tr '\000' '\n'
      
      ## 해당 프로세스의 메모리 정보 확인
      cat /proc/$(pgrep sleep)/maps
      
      ## 해당 프로세스의 자원 사용량 확인
      cat /proc/$(pgrep sleep)/status
      
      ## 기타 정보
      ls -l /proc/$(pgrep sleep)/exe
      cat /proc/$(pgrep sleep)/stat

 

 

2.2 도커 설치 및 확인
  • 도커 설치
    # [터미널1] 관리자 전환
    sudo su -
    whoami
    id
    
    # 도커 설치
    curl -fsSL https://get.docker.com | sh
    
    # 도커 정보 확인 : Client 와 Server , Storage Driver(overlay2), Cgroup Version(2), Default Runtime(runc)
    docker info
    docker version
    
    # 도커 서비스 상태 확인
    systemctl status docker -l --no-pager
    
    # 모든 서비스의 상태 표시 - 링크
    systemctl list-units --type=service
    
    # 도커 루트 디렉터리 확인 : Docker Root Dir(/var/lib/docker)
    tree -L 3 /var/lib/docker

  • Manage Docker as non -root user & Socket 소켓 - Link
    • [Warning] The docker group grants root-level privileges to the user. For details on how this impacts security in your system, see Docker Daemon Attack Surface.
    • The Docker daemon binds to a Unix socket, not a TCP port. By default it's the root user that owns the Unix socket, and other users can only access it using sudo.
    • The Docker daemon always runs as the root user
    • 널널한 개발자 TV - 소켓 01 00 소켓의 본질에 대한 이해 - https://www.youtube.com/watch?v=3jQ2dBpiqPo
    • 소켓(Socket)은 OS 커널에 구현되어 있는 프로토콜 요소에 대한 추상화된 인터페이스, 장치 파일의 일종
      # [터미널2] 일반 유저 ubuntu 로 실습 진행
      whoami
      
      # 도커 서버 정보 획득 실패
      docker info
      ...
      Server:
      ERROR: permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "http://%2Fvar%2Frun%2Fdocker.sock/v1.46/info": dial unix /var/run/docker.sock: connect: permission denied
      errors pretty printing info
      
      #
      ls -l /run/docker.sock /var/run/docker.sock
      file /var/run/docker.sock
      
      # [터미널1] 관리자
      sudo su -
      
      #
      sudo systemctl status docker -l --no-pager
      docker info
      
      # 소켓 정보 확인 : tcp, udp, sctp, Unix Domain
      ss -h | grep sockets
      ss -tl # 혹은 ss --tcp --listening
      ss -xl # 혹은 ss --unix --listening
      ss -xl | grep -i docker
      u_str LISTEN 0      4096                               /run/docker.sock 69239            * 0          
      u_str LISTEN 0      4096                   /var/run/docker/metrics.sock 69882            * 0          
      u_str LISTEN 0      4096   /var/run/docker/libnetwork/914c2d2f1446.sock 69422            * 0 
      
      # 특정 소켓 파일을 사용하는 프로세스 확인
      lsof /run/docker.sock
      COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF  NODE NAME
      systemd    1 root   40u  unix 0xffff96bd96236a80      0t0 69239 /run/docker.sock type=STREAM
      dockerd 5178 root    4u  unix 0xffff96bd96236a80      0t0 69239 /run/docker.sock type=STREAM
      
      # unix domain socket 중 docker 필터링
      lsof -U | grep -i docker

* root와 달리 일반유저로 접속할 경우 unix socket을 사용할 수 없어서 error 발생

 

 

  • Manage Docker as a non-root user
    # [터미널2] 일반 유저 ubuntu 로 실습 진행
    whoami
    
    # Create the docker group : 도커 스크립트 생성 시 자동 생성되어 그룹 확인만 진행
    sudo groupadd docker
    getent group | tail -n 3
    
    # Add your user to the docker group.
    echo $USER
    sudo usermod -aG docker $USER
    
    # ssh logout
    exit
    
    # ssh 재접속 후 확인
    
    #
    docker info
    
    
    # 컨테이너 실행
    docker run hello-world
    
    #
    docker ps
    docker ps -a
    docker images
    
    # 중지된 컨테이너 삭제
    docker ps -aq
    docker rm -f $(docker ps -aq)
    docker ps -a
  • [심화] 컨테이너가 host의 docker socket file 공유로 도커 실행 - 
    • 호스트의 도커 데몬을 Jenkins 컨테이너에게 Bind Mount로 전달하여, Jenkins 가 도커 데몬 사용 → 해당 방식을 DooD (Docker Out of Docker)
      https://blog.naver.com/yu3papa/223562337709
      #
      docker run --rm -it -v /run/docker.sock:/run/docker.sock -v /usr/bin/docker:/usr/bin/docker ubuntu:latest bash
      --------------------
      docker info
      docker run -d --rm --name webserver nginx:alpine
      docker ps
      docker rm -f webserver
      docker ps -a
      exit
      --------------------
    • Jenkins 컨테이너 : 도커 빌드 과정에서 활용 - DockerHub , Github , Docs
      # Jenkins 컨테이너 실행
      docker run -d -p 8080:8080 -p 50000:50000 --name jenkins-server --restart=on-failure -v jenkins_home:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock -v /usr/bin/docker:/usr/bin/docker jenkins/jenkins
      
      # 확인
      docker ps
      docker volume ls
      local     jenkins_home
      
      # 초기 암호 확인
      docker exec -it jenkins-server cat /var/jenkins_home/secrets/initialAdminPassword
      
      # Jenkins 컨테이너 웹 접속 주소 확인 : 초기 암호 입력
      echo "http://$(curl -s ipinfo.io/ip):8080"
      
      # jdk 확인
      docker exec -it jenkins-server java --version
      # JAVA_HOME 확인
      docker exec -it jenkins-server sh -c 'echo $JAVA_HOME'
      # Git 확인
      docker exec -it jenkins-server git -v
      
      # 기본 사용자 확인
      docker exec -it jenkins-server whoami
      
      # Jenkins 컨테이너에서 도커 명령 실행
      docker exec -it --user 0 jenkins-server whoami
      docker exec -it --user 0 jenkins-server docker info
      docker exec -it --user 0 jenkins-server docker run --rm hello-world
      docker exec -it --user 0 jenkins-server docker ps
      
      # [Tip] Jenkins 컨테이너 내부에서 root가 아닌 jenkins 유저도 docker를 실행할 수 있도록 권한을 부여
      # 정보 참고 출처 : https://www.handongbee.com/posts/Docker-%EB%93%A4%EC%97%AC%EB%8B%A4%EB%B3%B4%EA%B8%B0/
      docker exec -it --user 0 jenkins-server bash
      --------------------------------------------
      groupadd -for -g $(stat -c '%g' /var/run/docker.sock) docker
      usermod -aG docker jenkins
      exit
      --------------------------------------------
      
      # Jenkins 컨테이너 삭제
      docker rm -f jenkins-server
      docker volume rm jenkins_home
       
    • [도전과제1] Jenkins 컨테이너로 해당 호스트의 Docker 데몬을 사용하는 파이프라인 구성 및 해당 동작이 가능한 원리에 대해서 정리
  • 도커 설치 후 기본 정보 확인
    # [터미널1] 관리자 권한
    
    # 프로세스 확인 - 셸변수
    ps -ef
    pstree -p
    
    # 시스템에 (마운트 된) disk free 디스크 여유 공간 확인
    df -hT
    
    # 네트워크 정보 확인 >> docker0 네트워크 인터페이스가 추가됨, 현재는 DOWN 상태
    ip -br -c addr
    ip -c addr
    ip -c link
    ip -br -c link
    ip -c route
    
    # 이더넷 브리지 정보 확인
    brctl show
    
    # iptables 정책 확인
    iptables -t filter -S
    iptables -t nat -S
    
    ## filter 에 FORWARD 가 기존 ACCEPT 에서 DROP 로 변경됨
    ## filter 에 FORWARD 에 docker0 에서 docker0 혹은 외부로 전달 허용 정책이 추가됨
    iptables -t filter -S
    -P INPUT ACCEPT
    -P FORWARD DROP
    -P OUTPUT ACCEPT
    -N DOCKER
    -N DOCKER-ISOLATION-STAGE-1
    -N DOCKER-ISOLATION-STAGE-2
    -N DOCKER-USER
    -A FORWARD -j DOCKER-USER
    -A FORWARD -j DOCKER-ISOLATION-STAGE-1
    -A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    -A FORWARD -o docker0 -j DOCKER
    -A FORWARD -i docker0 ! -o docker0 -j ACCEPT
    -A FORWARD -i docker0 -o docker0 -j ACCEPT
    -A DOCKER-ISOLATION-STAGE-1 -i docker0 ! -o docker0 -j DOCKER-ISOLATION-STAGE-2
    -A DOCKER-ISOLATION-STAGE-1 -j RETURN
    -A DOCKER-ISOLATION-STAGE-2 -o docker0 -j DROP
    -A DOCKER-ISOLATION-STAGE-2 -j RETURN
    -A DOCKER-USER -j RETURN
    
    ## nat POSTROUTING 에 172.17.0.0/16 에서 외부로 전달 시 매스커레이딩(SNAT) 정책이 추가됨
    iptables -t nat -S
    -P PREROUTING ACCEPT
    -P INPUT ACCEPT
    -P OUTPUT ACCEPT
    -P POSTROUTING ACCEPT
    -N DOCKER
    -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
    -A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
    -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
    -A DOCKER -i docker0 -j RETURN

 

 

2.3 컨테이너 실행 및 확인
  • nginx 컨테이너 사용 : 기초 내용으로 스터디 시간은 Skip
    • Ubuntu 호스트 VM 에서 동작하는 컨테이너의 네트워크 정보 샘플

https://youtu.be/SJFO2w5Q2HI

    • nginx 컨테이너를 백그라운드로 실행 - 링크
      • nginx (웹 서버/프록시 소프트웨어 - 링크 생코) 이미지로 컨테이너 실행 시 백그라운드로 동작 - Web Server Survey & Official Image
        # nginx 이미지를 컨테이너 백그라운드로 실행
        # -d 는 Detached 모드로 컨테이너를 실행. 컨테이너를 백그라운드에서 동작하는 애플리케이션으로써 실행하도록 설정.
        # Detached 모드인 컨테이너는 반드시 컨테이너에서 프로그램이 실행돼야 하며 프로그램이 실행되지 않으면 컨테이너는 종료됩니다.
        docker run -d nginx
        docker ps
        
        # 실행중인 컨테이너의 ID만 확인
        docker ps -q
        
        # 컨테이너 상세 정보 확인
        # docker inspect '<NAME> 혹은 <ID>'
        docker inspect $(docker ps -q)
        
        # 컨테이너(=Instance)의 IP 정보 확인(JSON) - 링크
        # docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $INSTANCE_ID
        docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
        ping 172.17.0.2
        
        # 호스트 네트워크 인터페이스 정보 확인
        ip -c a
        brctl show
        
        # curl 로 http 접속 테스트 - 링크 링크2
        curl `docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)` | grep -o '<title>.*</title>'
        curl -s 172.17.0.2 | grep -o '<title>.*</title>'
         
    • 컨테이너 상세 정보 확인 - 링크
      docker inspect <CONTAINER_ID>
      • 네트워크, 볼륨 등 컨테이너에 대한 모든 정보를 출력
        docker inspect $(docker ps -q)
        docker inspect $(docker ps -q) | more
        
        # 컨테이너(=Instance)의 IP 정보 확인(JSON) - 링크
        docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)
    • 컨테이너 로깅
      docker logs <CONTAINER_ID>

    • 컨테이너에서 출력되는 로그 기록을 확인 - 링크
      # 터미널1 : -f 는 follow output 옵션입니다(tail -f) ⇒ <CTRL> + <C> 로 로깅을 종료
      docker logs -f $(docker ps -q)
      172.17.0.1 - - [01/Jun/2021:09:00:07 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.68.0" "-"
      172.17.0.1 - - [01/Jun/2021:09:00:09 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.68.0" "-"
      ...
      
      # 터미널2
      curl `docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $(docker ps -q)`
      curl 172.17.0.2


    • 백그라운드로 실행된 컨테이너의 로그를 직접 확인할 수 있습니다
    • 컨테이너 명령 전달
      • 실행된 컨테이너에 새로운 패키지를 설치하거나 수정해야 하는 경우가 있습니다. 이런 경우에 exec 명령을 이용하여 컨테이너에 명령을 전달할 수 있습니다. - 링크
        # nginx 컨테이너의 index.html 파일의 위치는 /usr/share/nginx/html/index.html 입니다
        docker exec $(docker ps -q) ls /usr/share/nginx/html
        docker exec $(docker ps -q) cat /usr/share/nginx/html/index.html

      • nginx 컨테이너의 index.html 내용을 변경하는 실행(exec)를 실습해보자
        # nginx 컨테이너의 index.html 파일의 위치는 /usr/share/nginx/html/index.html 입니다
        docker exec $(docker ps -q) ls /usr/share/nginx/html
        docker exec $(docker ps -q) cat /usr/share/nginx/html/index.html

    • 컨테이너 삭제
# 실행 및 종료된 컨테이너까지 전부삭제
docker rm -f $(docker ps -a -q)

 


[ 3. 컨테이너 격리 ]

리눅스 프로세스 격리 기술 발전

https://speakerdeck.com/kakao/ige-dwaeyo-dokeo-eobsi-keonteineo-mandeulgi?slide=200

이게 돼요? 도커 없이 컨테이너 만들기 1 : Chroot + 탈옥 - Youtube , Github , Docs
  • chroot root directory : user 디렉터리를 user 프로세스에게 root 디렉터리를 속임
    # [터미널1] 관리자 전환
    sudo su -
    whoami
    
    #
    cd /tmp
    mkdir myroot
    
    # chroot 사용법 : [옵션] NEWROOT [커맨드]
    chroot myroot /bin/sh
    chroot: failed to run command ‘/bin/sh’: No such file or directory
    
    #
    tree myroot
    which sh
    ldd /bin/sh
    
    # 바이러리 파일과 라이브러리 파일 복사
    mkdir -p myroot/bin
    cp /usr/bin/sh myroot/bin/
    mkdir -p myroot/{lib64,lib/x86_64-linux-gnu}
    tree myroot
    
    cp /lib/x86_64-linux-gnu/libc.so.6 myroot/lib/x86_64-linux-gnu/
    cp /lib64/ld-linux-x86-64.so.2 myroot/lib64
    tree myroot/
    
    #
    w
    --------------------
    ls
    exit
    --------------------
    
    #
    which ls
    ldd /usr/bin/ls
    	
    #
    cp /usr/bin/ls myroot/bin/
    mkdir -p myroot/bin
    cp /lib/x86_64-linux-gnu/{libselinux.so.1,libc.so.6,libpcre2-8.so.0} myroot/lib/x86_64-linux-gnu/
    cp /lib64/ld-linux-x86-64.so.2 myroot/lib64
    tree myroot
    
    #
    chroot myroot /bin/sh
    --------------------
    ls /
    
    ## 탈출 가능한지 시도
    cd ../../../
    ls /
    
    # 아래 터미널2와 비교 후 빠져나오기
    exit
    --------------------
    # chroot 요약 : 경로를 모으고(패키징), 경로에 가둬서 실행(격리)
    
    
    # [터미널2]
    # chroot 실행한 터미널1과 호스트 디렉터리 비교
    ls /

  • chroot 에서 ps 실행해보기 - Link 
    # copy ps
    ldd /usr/bin/ps;
    cp /usr/bin/ps /tmp/myroot/bin/;
    cp /lib/x86_64-linux-gnu/{libprocps.so.8,libc.so.6,libsystemd.so.0,liblzma.so.5,libgcrypt.so.20,libgpg-error.so.0,libzstd.so.1,libcap.so.2} /tmp/myroot/lib/x86_64-linux-gnu/;
    mkdir -p /tmp/myroot/usr/lib/x86_64-linux-gnu;
    cp /usr/lib/x86_64-linux-gnu/liblz4.so.1 /tmp/myroot/usr/lib/x86_64-linux-gnu/;
    cp /lib64/ld-linux-x86-64.so.2 /tmp/myroot/lib64/;
    
    # copy mount
    ldd /usr/bin/mount;
    cp /usr/bin/mount /tmp/myroot/bin/;
    cp /lib/x86_64-linux-gnu/{libmount.so.1,libc.so.6,libblkid.so.1,libselinux.so.1,libpcre2-8.so.0} /tmp/myroot/lib/x86_64-linux-gnu/;
    cp /lib64/ld-linux-x86-64.so.2 /tmp/myroot/lib64/;
    
    # copy mkdir
    ldd /usr/bin/mkdir;
    cp /usr/bin/mkdir /tmp/myroot/bin/;
    cp /lib/x86_64-linux-gnu/{libselinux.so.1,libc.so.6,libpcre2-8.so.0} /tmp/myroot/lib/x86_64-linux-gnu/;
    cp /lib64/ld-linux-x86-64.so.2 /tmp/myroot/lib64/;
    
    # tree 확인
    tree myroot
    
    #
    chroot myroot /bin/sh
    ---------------------
    # 왜 ps가 안될까요?
    ps
    Error, do this: mount -t proc proc /proc
    
    #
    mount -t proc proc /proc
    mount: /proc: mount point does not exist.
    
    #
    mkdir /proc
    mount -t proc proc /proc
    mount -t proc
    
    # ps는 /proc 의 실시간 정보를 활용
    ps
    ps auf
    ps aux
    ls -l /proc
    
    exit
    ---------------------
    
    # 실습 시 사용한 proc 마운트 제거
    mount -t proc
    sudo umount /tmp/myroot/proc
    mount -t proc

  • 남이 만든 이미지 chroot 해보기 : 컨테이너 이미지는 실행되는 프로세스의 동작에 필요한 모든 관련 파일을 묶어서 패키징
    #
    mkdir nginx-root
    tree nginx-root
    
    # nginx 컨테이너 압축 이미지를 받아서 압축 풀기
    docker export $(docker create nginx) | tar -C nginx-root -xvf -;
    docker images
    
    #
    tree -L 1 nginx-root
    tree -L 2 nginx-root | more
    
    #
    chroot nginx-root /bin/sh
    ---------------------
    ls /
    
    #
    nginx -g "daemon off;"
    
    # 터미널1에서 아래 확인 후 종료
    CTRL +C # nginx 실행 종료
    exit
    ---------------------
    
    # [터미널2]
    ## 루트 디렉터리 비교 및 확인
    ls /
    ps -ef |grep nginx
    curl localhost:80
    sudo ss -tnlp


  • 탈옥 코드 : vi escape_chroot.c
    #include <sys/stat.h>
    #include <unistd.h>
    
    int main(void)
    {
      mkdir(".out", 0755);
      chroot(".out");
      chdir("../../../../../");
      chroot(".");
    
      return execl("/bin/sh", "-i", NULL);
    }

  • 탈옥 코드를 컴파일하고 new-root 에 복사
    # 컴파일
    gcc -o myroot/escape_chroot escape_chroot.c
    tree -L 1 myroot
    file myroot/escape_chroot
    
    # chroot 실행
    chroot myroot /bin/sh
    -----------------------
    ls /
    cd ../../
    cd ../../
    ls /
    
    # 탈출!
    ./escape_chroot
    ls /
    
    # 종료
    exit
    exit
    -----------------------
    
    # [터미널2]
    ## 루트 디렉터리 비교 및 확인
    ls /

이게 돼요? 도커 없이 컨테이너 만들기 2 : 마운트 네임스페이스 + Pivot_root
    • chroot 차단을 위해서, pivot_root + mount ns(호스트 영향 격리) 를 사용 : 루트 파일 시스템을 변경(부착 mount) + 프로세스 환경 격리
      https://speakerdeck.com/kakao/ige-dwaeyo-dokeo-eobsi-keonteineo-mandeulgi?slide=80
    • 마운트 네임스페이스 : 마운트 포인트를 격리(unshare)
    • pivot root 와 마운트 네임스페이스를 결합해 host에 영향이 없이 탈옥불가능하게 함..
    • 마운트 네임스페이스
      주요 명령어
      pivot_root
      # pivot_root [new-root] [old-root]
      ## 사용법은 심플합니다 ~ new-root와 old-root 경로를 주면 됩니다
      
      mount
      # mount -t [filesystem type] [device_name] [directory - mount point]
      ## root filesystem tree에 다른 파일시스템을 붙이는 명령
      ## -t : filesystem type  ex) -t tmpfs  (temporary filesystem : 임시로 메모리에 생성됨)               
      ## -o  : 옵션  ex) -o size=1m  (용량 지정 등 …)
      ## 참고) * /proc/filesystems 에서 지원하는 filesystem type 조회 가능
      
      unshare
      # unshare [options] [program] [arguments]]
      ## "새로운 네임스페이스를 만들고 나서 프로그램을 실행" 하는 명령어입니다
      
      
      # [터미널1]
      unshare --mount /bin/sh
      -----------------------
      # 아래 터미널2 호스트 df -h 비교 : mount unshare 시 부모 프로세스의 마운트 정보를 복사해서 자식 네임스페이스를 생성하여 처음은 동일
      df -h
      -----------------------
      
      # [터미널2]
      df -h
      
      
      # [터미널1]
      -----------------------
      #
      mkdir new_root
      mount -t tmpfs none new_root
      ls -l
      tree new_root
      
      ## 마운트 정보 비교 : 마운트 네임스페이스를 unshare
      df -h
      mount | grep new_root
      findmnt -A
      
      ## 파일 복사 후 터미널2 호스트와 비교
      cp -r myroot/* new_root/
      tree new_root/
      
      -----------------------
      
      # [터미널2]
      cd /tmp
      ls -l
      tree new_root
      df -h
      mount | grep new_root
      findmnt -A
      
      ## 안보이는 이유 : 마운트 네임스페이스를 unshare 된 상태
      tree new_root/
    • pivot_root
      # 터미널1
      -----------------------
      mkdir new_root/put_old
      
      ## pivot_root 실행
      cd new_root # pivot_root 는 실행 시, 변경될 root 파일시스템 경로로 진입
      pivot_root . put_old # [신규 루트] [기존 루트]
      
      ##
      cd /
      ls / # 터미널2와 비교
      ls put_old
      
      -----------------------
      
      # 터미널2
      ls /
      
      
      탈옥 시도
      # 터미널1
      -----------------------
      ./escape_chroot
      cd ../../../
      ls /
      exit
      exit
      -----------------------
    • [도전과제2] 이게 돼요? 도커 없이 컨테이너 만들기 3 : Overlay 파일시스템(이미지 ‘중복 문제’ 해결) 실습 따라해보고 정리 - Docs , Youtube
 

이게 돼요? 도커 없이 컨테이너 만들기

#Container #Cloud #Backend #Linux #Docker 카카오엔터프라이즈에서는 뉴크루와 클라우드에 관심이 있는 개발자 크루를 대상으로 컨테이너 교육을 진행하고 있습니다. 본 핸즈온은 컨테이너 내부를 이해

speakerdeck.com

  • 네임스페이스와 관련된 프로세스의 특징
    1. 모든 프로세스들은 네임스페이스 타입별로 특정 네임스페이스에 속합니다
    2. Child 는 Parent 의 네임스페이스를 상속받습니다
    3. 프로세스는 네임스페이스 타입별로 일부는 호스트(root) 네임스페이스를 사용하고 일부는 컨테이너의 네임스페이스를 사용할 수 있습니다.
      • mount 네임스페이스는 컨테이너의 것으로 격리하고, network 네임스페이스는 호스트 것을 사용
  • Mount(파일시스템), Network(네트워크), PID(프로세스 id), User(계정), ipc(프로세스간 통신), Uts(Unix time sharing, 호스트네임), cgroup
    O'REILLY - Networking and Kubernetes 책
    https://wizardzines.com/comics/https://wizardzines.com/comics/namespaces/
  • 실습 : 터미널 2개 모두 관리자 권한으로 진행
  • 프로세스 별 네임스페이스 확인
    # [터미널 1,2] 관리자
    sudo su -
    cd /tmp
    
    # 네임스페이스 확인 방법 1 : 프로세스 별 네임스페이스 확인
    ls -al /proc/$$/ns
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 cgroup -> 'cgroup:[4026531835]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 ipc -> 'ipc:[4026531839]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 mnt -> 'mnt:[4026531841]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 net -> 'net:[4026531840]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 pid -> 'pid:[4026531836]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 pid_for_children -> 'pid:[4026531836]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 time -> 'time:[4026531834]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 time_for_children -> 'time:[4026531834]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 user -> 'user:[4026531837]'
    lrwxrwxrwx 1 root root 0 Aug 25 13:45 uts -> 'uts:[4026531838]'
    
    ## 특정 네임스페이스의 inode 값만 확인
    readlink /proc/$$/ns/mnt
    readlink /proc/$$/ns/net
    
    
    # 네임스페이스 확인 방법 2 : lsns - List system namespaces
    lsns -h
    lsns -p 1
    lsns -p $$
    
    ## -t 네임스페이스 타입 , -p 조회할 PID
    ## NPROCS : 해당 네임스페이스에 속해있는 프로세스 갯수
    ## PID : 해당 네임스페이스의 (최초) 주인 프로세스
    lsns -t mnt -p 1
    lsns -t mnt -p $$
  • 마운트 네임스페이스 MOUNT (mnt) Namespace : 2002년, 마운트 포인트 격리, 최초의 네임스페이스
  • echo $$ - 링크
    # PID 1과 현재 Shell 속한 프로세스의 MNT NS 정보 확인
    lsns -t mnt -p 1
    lsns -t mnt -p $$
    
    # [터미널1] /tmp 디렉터리
    # unshare -m [명령어] : -m 옵션을 주면 [명령어]를 mount namespace 를 isolation 하여 실행합니다
    unshare -m  # *[명령어]를 지정하지 않으면 환경변수 $SHELL 실행
    -----------------------------------
    # NPROCS 값과 PID 값의 의미 확인
    lsns -p $$
            NS TYPE   NPROCS   PID USER COMMAND
    4026531834 time      112     1 root /sbin/init
    4026531835 cgroup    112     1 root /sbin/init
    4026531836 pid       112     1 root /sbin/init
    4026531837 user      112     1 root /sbin/init
    4026531838 uts       108     1 root /sbin/init
    4026531839 ipc       112     1 root /sbin/init
    4026531840 net       112     1 root /sbin/init
    4026532206 mnt         2  5834 root -bash
    
    # PID 1과 비교
    lsns -p 1
    
    # 빠져나오기
    exit
    -----------------------------------
     호스트 경로를 직접 마운트 시 주의 사항 : 예를 들어 호스트 최상위 루트 디렉터리 (/) 마운트 시 보안상 어떤 문제가 있을까요?

 

  • UTS 네임스페이스 Namespace : 2006년, Unix Time Sharing (여러 사용자 작업 환경 제공하고자 서버 시분할 나눠쓰기), 호스트명, 도메인명 격리
    # unshare -u [명령어]
    # -u 옵션을 주면 [명령어]를 UTS namespace 를 isolation 하여 실행
    
    # [터미널1] /tmp 디렉터리
    unshare -u
    -----------------------------------
    lsns -p $$
    lsns -p 1
    
    ## 기본은 부모 네임스페스의 호스트 네임을 상속
    hostname
    
    ## 호스트 네임 변경
    hostname KANS
    
    ## 아래 터미널2에서 hostname 비교
    hostname
    
    exit
    -----------------------------------
    
    # [터미널2] /tmp 디렉터리
    hostname

 

  • IPC 네임스페이스 : 2006년, Inter-Process Communication 격리, 프로세스 간 통신 자원 분리 관리 - Shared Memory, Pipe, Message Queue 등
    # [터미널1] /tmp 디렉터리
    unshare -i
    -----------------------------------
    lsns -p $$
    lsns -p 1
    
    exit
    -----------------------------------

    • [추가 실습] 2개의 컨테이너 간 IPC 네임스페이스 공유
      # [터미널1]
      ipcs -m
      docker run --rm --name test1 --ipc=shareable -it ubuntu bash
      ---------------------
      ipcs -m
      ipcmk -M 2000
      Shared memory id: 0
      
      ipcs -m
      ------ Shared Memory Segments --------
      key        shmid      owner      perms      bytes      nattch     status
      0x6427dde0 0          root       644        2000       0
      
      lsns -p $$
      
      # 아래 실습 확인 후 종료
      exit
      ---------------------
      
      # [터미널2]
      # 호스트에서 확인
      ipcs -m
      
      # 컨테이너 생성 시 IPC 공유 : 해당 컨테이너는 test1 컨테이너와 IPC 네임스페이스를 공유
      docker run --rm --name test2 --ipc=container:test1 -it ubuntu bash
      ---------------------
      ipcs -m
      ------ Shared Memory Segments --------
      key        shmid      owner      perms      bytes      nattch     status
      0x6427dde0 0          root       644        2000       0
      
      lsns -p $$
      
      # 실습 확인 후 종료
      exit
      ---------------------
  • PID 네임스페이스 : 2008년, Process ID 격리
    • 부모-자식 네임스페이스 중첩 구조, 부모 네임스페이스 에서는 → 자식 네임스페이스를 볼 수 있음
    • 자식 네임스페이스는 parent tree 의 id 와 subtree 의 id 두 개를 가짐
    • https://speakerdeck.com/kakao/ige-dwaeyo-dokeo-eobsi-keonteineo-mandeulgi?slide=140

      ps -ef | head -n 3
      UID          PID    PPID  C STIME TTY          TIME CMD
      root           1       0  0 09:57 ?        00:00:04 /sbin/init
      root           2       0  0 09:57 ?        00:00:00 [kthreadd]


    • unshare 할 때 fork 하여, 자식 PID 네임스페이스의 pid 1로 실행
    • pid 1 (init) 이 종료되면 pid namespace 도 종료
      # unshare -p [명령어]
      ## -p 옵션을 주면 [명령어]를 PID namespace 를 isolation 하여 실행합니다
      ## -f(fork) : PID namespace 는 child 를 fork 하여 새로운 네임스페이스로 격리함
      ## --mount-proc : namespace 안에서 ps 명령어를 사용하려면 /proc 를 mount 하기위함 
      
      # [터미널1] /proc 파일시스템 마운트
      echo $$
      
      unshare -fp --mount-proc /bin/sh
      --------------------------------
      # 터미널2 호스트와 비교
      echo $$
      ps -ef
      ps aux
      
      
      # 내부에서 PID NS 확인 : 아래 터미널2에서 lsns -t pid -p <위 출력된 PID>와 비교
      lsns -t pid -p 1
      
      --------------------------------
      
      # [터미널2]
      ps -ef
      ps aux
      ps aux | grep '/bin/sh'
      root        6186  0.0  0.0   6192  1792 pts/2    S    15:08   0:00 unshare -fp --mount-proc /bin/sh
      root        6187  0.0  0.0   2892  1664 pts/2    S+   15:08   0:00 /bin/sh
      
      # 터미널1 PID NS와 비교
      lsns -t pid -p <위 출력된 PID>
      lsns -t pid -p 6187


    • 호스트에서 컨테이너 프로세스 종료 해보기
      # [터미널1]
      --------------------------------
      # fork
      sleep 10000
      
      # 아래 종료로 자동으로 sleep 가 exit 됨
      echo $$
      
      # 아래 종료로 자동으로 exit됨 : 컨테이너의 PID 1 프로세스 종료 시
      --------------------------------
      echo $$
      
      
      # [터미널2]
      ps aux | grep sleep
      
      ## 호스트에서 sleep 종료 시켜보기 : 어떻게 되는가?
      kill -l
      kill -SIGKILL $(pgrep sleep)
      
      ## 호스트에서 /bin/sh 종료 시켜보기 : 어떻게 되는가?
      ps aux | grep '/bin/sh'
      kill -SIGKILL <위 출력된 PID>
      kill -9 6187
    • 도커 종료 Exit status - Docs KrBlog
      # Exit code 125 indicates that the error is with Docker daemon itself.
      docker run --foo busybox; echo $?
      flag provided but not defined: --foo
      See 'docker run --help'.
      125
      
      # Exit code 126 indicates that the specified contained command can't be invoked.
      # The container command in the following example is: /etc; echo $?.
      docker run busybox /etc; echo $?
      docker: Error response from daemon: Container command '/etc' could not be invoked.
      126
      
      # Exit code 127 indicates that the contained command can't be found.
      docker run busybox foo; echo $?
      docker: Error response from daemon: Container command 'foo' not found or does not exist.
      127
      
      # Other exit codes : Any exit code other than 125, 126, and 127 represent the exit code of the provided container command.
      docker run busybox /bin/sh -c 'exit 3'
      echo $?
      3
      
      docker ps -a
      CONTAINER ID   IMAGE     COMMAND                 CREATED          STATUS                     PORTS     NAMES
      f02d387b3c00   busybox   "/bin/sh -c 'exit 3'"   8 seconds ago    Exited (3) 6 seconds ago             youthful_hypatia
      ...
      
      # 컨테이너 삭제
      docker rm -f $(docker ps -aq)

    • (심화) 도커 128 이상의 종료 코드 - DockerExitCodes 죽임당한컨테이너
    • USER 네임스페이스 : 2012년, UID/GID 넘버스페이스 격리(Remap_, 컨테이너의 루트권한 문제를 해결함, 부모-자식 네임스페이스의 중첩 구조
    • https://speakerdeck.com/kakao/ige-dwaeyo-dokeo-eobsi-keonteineo-mandeulgi?slide=171
    • 네임스페이스 안과 밖의 UID/GID 를 다르게 설정할 수 있음
    • 사전 준비 : 터미널1(ubuntu 일반 유저, docker 실행 가능 상태) , 터미널2(ubuntu 일반 유저)
      # 터미널1,2
      exit
      whoami
      
      
      # 터미널1
      docker run -it ubuntu /bin/sh
      -----------------------------
      # 아래 터미널2와 비교
      whoami
      id
      
      # 아래 터미널2와 비교
      ps -ef 
      
      # User 네임스페이스는 도커 컨테이너 실행 시, 호스트 User 를 그대로 사용
      readlink /proc/$$/ns/user
      lsns -p $$
              NS TYPE   NPROCS PID USER COMMAND
      4026531834 time        2   1 root /bin/sh
      4026531837 user        2   1 root /bin/sh
      4026532208 mnt         2   1 root /bin/sh
      4026532209 uts         2   1 root /bin/sh
      4026532210 ipc         2   1 root /bin/sh
      4026532211 pid         2   1 root /bin/sh
      4026532212 net         2   1 root /bin/sh
      4026532273 cgroup      2   1 root /bin/sh
      
      # 아래 동작 확인 후 종료
      exit
      -----------------------------
      
      
      # 터미널2
      whoami
      id
      
      ## root 로 실행됨
      ps -ef |grep "/bin/sh"
      ubuntu      6733    5348  0 15:34 pts/0    00:00:00 docker run -it ubuntu /bin/sh
      root        6790    6768  0 15:34 pts/0    00:00:00 /bin/sh
      
      ##
      readlink /proc/$$/ns/user
      lsns -p $$
      lsns -p $$ -t user
              NS TYPE  NPROCS   PID USER   COMMAND
      4026531837 user       5  2391 ubuntu /lib/systemd/systemd --user
    • 컨테이너를 탈취 후, 해당 프로세스를 기반으로 호스트에 Action 이 가능할 경우, root 계정 권한 실행이 가능 ⇒ 보안상 취약
    • https://speakerdeck.com/kakao/ige-dwaeyo-dokeo-eobsi-keonteineo-mandeulgi?slide=184
      # 터미널1
      unshare -U --map-root-user /bin/sh
      -----------------------------
      # 내부에서는 여전히 root로 보임
      whoami
      id
      
      # User 네임스페이스를 호스터(터미널2)와 비교
      readlink /proc/$$/ns/user
      lsns -p $$
      
      # 아래 동작 확인 후 종료
      exit
      -----------------------------
      
      # 터미널2
      readlink /proc/$$/ns/user
      lsns -p $$
      
      ## ubuntu 로 실행됨
      ps -ef |grep "/bin/sh"
      ubuntu      6874    5348  0 15:42 pts/0    00:00:00 /bin/sh

    • time : 프로세스가 볼 수 있는 시스템 시간을 격리하여, 특정 프로세스에 대해 다른 시간대를 설정할 수 있습니다.
    • cgroup
      • cgroup namespace 내에 속한 프로세스는 자신이 속한 cgroup 및 하위의 cgroup에 대해서만 볼 수 있습니다.
      • 실제 CPU, MEM 등의 자원 제어는 cgroup namespace가 아닌 cgroup을 통해 이루어집니다.
      • cgroup이란? : Cpu, Disk I/O, Memory, Network 등 자원 사용을 제한/격리 시키는 커널 기능
      • cgroups인가? cgroup 인가?
        • 리눅스 커널 공식문서를 보면 허태준님께서 이미 정의를 하고 시작을 하셨다.
        • 여러 개의 control group을 명시적으로 언급할 때는 복수형인 "cgroups"을 사용한다고 한다.
        • 그리고 앞에 c는 대문자로 쓰이지 않는다고 한다.
[참고] 자원 관리, cgroups - Docs , Youtube
    • 쿠버네티스 환경에서 파드에 USER 네임스페이스 격리 실습 후 정리 - Docs , Task
  • 프로세스실행 중인 프로그램의 인스턴스를 의미. OS에서 프로세스를 관리하며, 각 프로세스는 고유한 ID(PID)를 가짐.
  • 프로세스는 CPU와 메모리를 사용하는 기본 단위로, OS 커널(cgroup)에서 각 프로세스의 자원을 관리함.
  • cgroups : Blog1 , Blog2
  • cgroup v1, v2 : Blog1 , Blog2 , check
    • cgroup v1, v2가 존재하며 v2는 v1에 비해 자원계층구조의 가시성을 향상
    • cgroup v1에서는 request, limit 두개의 자원설정만이 가능했는데, cgroup v2에서는 memoryQoS라는 기능을 추가하여 컨테이너 등에서 쉽사리 OOM이 나지않게 하는 기능 지원 → Memory High : 메모리 사용량 조절 제한. cgroup의 사용량이 높은 경계를 초과하면 cgroup의 프로세스가 제한되고 회수 압력이 커짐