lxc(7)

NAME
lxc - Linux 컨테이너

빠른 도움말
man 페이지를 읽고 싶지는 않지만 서둘러서 해보고 싶다면, 된다고 보장할 수는 없지만, 미리정의된 설정파일로 컨테이너 내에서 쉘을 실행하는 아래 명령어를 소개하고자 한다. /usr/bin/lxc-execute -n foo -f /usr/share/doc/lxc/examples/lxc-macvlan.conf /bin/bash

개요
컨테이너 기술은 리눅스 커널의 메인스트림에서 활발하게 개발이 진행되고 있다. 컨트롤 그룹(aka. 프로세스 컨테이너)을 통한 자원 관리와 네임스페이슬 통한 자원의 고립 기능을 제공한다.

linux 컨테이너 (lxc)는 사용자영역 컨테이너 개체를 제공하는 새로운 기능을 사용하는 것을 목표로 하고 있다. 이 새로운 기능은 응용 프로그램이나 시스템에서 모든 자원의 격리와 제어를 제공한다.

이 프로젝트의 첫번째 목적은 컨테이너 프로젝트에 속해있는 커널 개발자들의 작업을 편하게 하며, 특히 새로운 기능인 Checkpoint/Restart에 대해 계속 작업을 진행해 나가는 것이다. lxc는 작지만, 컨테이너를 간단한 명령어를 통해 쉽게 관리할 수 있고, 다목적으로 사용되기에도 충분하다.

요구사항
lxc는 커널이 제공하는 몇가지 기능들에 의존적이며, 해당 기능이 활성화되어 있어야 한다. 부족한 기능에 따라, 제한된 기능만이 동작하거나, 아예 동작을 안 할 수 있다.

아래 리스트는 컨테이너의 모든 기능을 사용하기 위해 활성화되어야 하는 커널 기능들이다.

\FC
	    * General setup
	      * Control Group support
	        -> Namespace cgroup subsystem
	        -> Freezer cgroup subsystem
	        -> Cpuset support
	        -> Simple CPU accounting cgroup subsystem
	        -> Resource counters
	          -> Memory resource controllers for Control Groups
	      * Group CPU scheduler
	        -> Basis for grouping tasks (Control Groups)
	      * Namespaces support
	        -> UTS namespace
	        -> IPC namespace
	        -> User namespace
	        -> Pid namespace
	        -> Network namespace
	    * Device Drivers
	      * Character devices
	        -> Support multiple instances of devpts
	      * Network device support
	        -> MAC-VLAN support
	        -> Virtual ethernet pair device
	    * Networking
	      * Networking options
	        -> 802.1d Ethernet Bridging
	    * Security options
	      -> File POSIX Capabilities
      \F[]

배포판들에 포함된 3.10 이상의 커널에서는 lxc가 동작한다. 매우 작은 기능만 있지만 충분히 사용할 수 있다. lxc-checkconfig 스크립트를 사용하면 현재 커널 설정에 대한 정보를 얻을 수 있다.

컨트롤 그룹은 어디에든지 마운트될 수 있다. 예를 들어 mount -t cgroup cgroup /cgroup도 가능하다. 그러나 cgmanager, cgroup-lite 또는 systemd를 사용하여, /sys/fs/cgroup에 cgroup 계층구조를 마운트하는 것이 좋다.

기능 사양
컨테이너는 응용프로그램이나 시스템을 내부에서 실행시키기 위해, 호스트의 몇몇 자원들을 격리시키는 객체이다.

어플리케이션/시스템은 처음 생성될때 또는 시작 명령어의 인자로 넘겨주었던 설정을 기반으로 한 컨테이너 안에서 실행된다.

어떻게 컨테이너 내부에서 응용 프로그램을 실행하는가?

어플리케이션을 실행하기에 앞서, 고립시키고 싶은 자원을 먼저 알아야 한다. 기본 설정은 pid와 sysv ipc 그리고 마운트 포인트들을 고립시킨다. 만약에 간단한 쉘을 컨테이너 내부에서 실행시키기 원한다면, 특히 rootfs를 공유하고 싶다면 매우 기초적인 설정이 요구된다. sshd 같은 응용 프로그램을 실행시키고 싶다면, 새로운 네트워크 스택과 호스트네임을 제공해 주어야 한다. 만약 몇몇 파일들, 예를 들어, \FC/var/run/httpd.pid\F[]이 충돌나는것을 막고 싶다면, \FC/var/run\F[]를 빈 디렉토리로 다시 마운트하는 것이 필요하다. 모든 경우의 파일 충돌을 피하고 싶다면, 컨테이너를 위한 루트 파일시스템를 따로 지정해 줄 수도 있다. 루트 파일시스템은 미리 원래의 루트 파일시스템을 바인드 마운트한 디렉토리가 될 수도 있다. 이렇게 되면 자신만의 \FC/etc\F[], \FC/home\F[]을 사용하면서도 배포판을 그대로 사용할 수 있다.

아래는 sshd를 사용하기 위한 디렉토리 트리 예제이다.
\FC
[root@lxc sshd]$ tree -d rootfs

rootfs
|-- bin
|-- dev
|   |-- pts
|   `-- shm
|       `-- network
|-- etc
|   `-- ssh
|-- lib
|-- proc
|-- root
|-- sbin
|-- sys
|-- usr
`-- var
    |-- empty
    |   `-- sshd
    |-- lib
    |   `-- empty
    |       `-- sshd
    `-- run
        `-- sshd
      \F[]
그리고, 해당 마운트 포인트 파일의 내용은 아래와 같다.
\FC
	[root@lxc sshd]$ cat fstab
	/lib /home/root/sshd/rootfs/lib none ro,bind 0 0
	/bin /home/root/sshd/rootfs/bin none ro,bind 0 0
	/usr /home/root/sshd/rootfs/usr none ro,bind 0 0
	/sbin /home/root/sshd/rootfs/sbin none ro,bind 0 0
      \F[]

어떻게 컨테이너 내에서 시스템을 실행하는가?

컨테이너 내에서 시스템을 실행하는 것은 역설적으로 어플리케이션을 실행하는 것보다 쉽다. 왜 그럴까? 왜냐하면, 어떤 자원이 고립되어야 하는지 고려할 필요가 없다. 모든 자원이 고립되면 된다. 자원들은 별다른 설정없이 고립된다고 지정만 해도 된다. 왜냐하면 컨테이너가 그 자원들을 세팅할 것이기 때문이다. 예를 들어 ipv4 주소는 시스템 컨테이너의 init 스크립트들을 통해 세팅된다. 아래는 마운트 포인트 파일의 예제이다.
\FC
	[root@lxc debian]$ cat fstab
	/dev	/home/root/debian/rootfs/dev none bind 0 0
	/dev/pts /home/root/debian/rootfs/dev/pts  none bind 0 0
      \F[]
설정을 돕기 위해서 컨테이너에 부가 정보를 추가할 수 있다. 아래와 같이 호스트에 있는 resolv.conf를 컨테이너 안에서 접근할 수 있다.
\FC
	/etc/resolv.conf /home/root/debian/rootfs/etc/resolv.conf none bind 0 0
      \F[]

  컨테이너의 생명주기
컨테이너가 생성될때, 컨테이너는 설정정보를 포함하게 된다. 프로세스가 실행될때, 컨테이너는 시작되고 실행된다. 컨테이너 내에서 실행되던 마지막 프로세스가 종료되면, 컨테이너는 종료된다.

컨테이너의 초기화가 실패했을 경우, (아래 그림처럼)중단 상태로 바뀌게 된다.

\FC
   ---------
  | STOPPED |<---------------
   ---------                 |
       |                     |
     start                   |
       |                     |
       V                     |
   ----------                |
  | STARTING |--error-       |
   ----------         |      |
       |              |      |
       V              V      |
   ---------    ----------   |
  | RUNNING |  | ABORTING |  |
   ---------    ----------   |
       |              |      |
  no process          |      |
       |              |      |
       V              |      |
   ----------         |      |
  | STOPPING |<-------       |
   ----------                |
       |                     |
        ---------------------
      \F[]

  설정
컨테이너는 설정파일에 의해서 설정된다. 설정파일의 형식은 다음을 참조하면 된다. \FClxc.conf\F[](5)

  컨테이너의 생성/제거 (지속 컨테이너)
지속성 컨테이너 객체는 lxc-create 명령어로 생성된다. 컨테이너이름을 인수로 받으며, 부가적인 설정파일과 템플릿을 지정한다. 여기서 지정하는 이름은 다른 명령어들을 사용할 때 해당 컨테이너를 참조하기 위해 사용된다. lxc-destroy 명령어는 컨테이너 객체를 제거한다.
\FC
	  lxc-create -n foo
	  lxc-destroy -n foo
	\F[]

  휘발성 컨테이너
컨테이너 시작전에 컨테이너 오브젝트를 생성하는 것이 의무는 아니다. 컨테이너는 설정파일을 파라미터로 넣어서 바로 시작할 수도 있다.

  컨테이너의 시작과 종료
컨테이너가 생성하면 응용 프로그램/시스템이 실행될 준비를 마친 것이다. 실행하는 것이 바로 lxc-execute와 lxc-start 명령어의 목적이다. 응용프로그램 시작전에 컨테이너가 생성되어 있지 않다면, 컨테이너는 명령어의 인수로 넘겼던 설정파일을 사용한다. 그런 인수마저 없다면, 기본 고립 환경을 사용한다. 만약 응용프로그램이 종료되면, 컨테이너도 역시 종료된다. 실행중인 응용프로그램을 종료시키고 싶다면 lxc-stop를 사용하면 된다.

컨테이너 내부에서 응용프로그램을 실행하는 것은 시스템을 실행하는 것과는 차이가 있다. 이런 이유로 아래의 두가지 명령어가 사용된다.
\FC
	  lxc-execute -n foo [-f config] /bin/bash
	  lxc-start -n foo [-f config] [/bin/bash]
	\F[]

lxc-execute 명령어는 컨테이너 내부에서 lxc-init 프로세스를 통해 실행할 명령어를 지정할 수 있다. lxc-init는 지정한 명령어를 실행한 후, 그 명령어로 실행된 모든 프로세스들이 종료되기를 기다린다. (컨테이너 내부에서 데몬을 지원하기 위해서이다) 다시 말해서, 컨테이너 내부에서 lxc-init는 1번 pid를 갖고, 응용프로그램의 첫번째 프로세스는 2번 pid를 가진다.

lxc-start 명령어는 지정한 명령어를 컨테이너 내에서 직접 실행한다. 첫 프로세스의 pid는 1번이다. 만약 어떤 명령어도 지정되지 않으면, lxc.init.cmd에 지정된 명령어를 실행한다. 이마저도 지정되있지 않으면, \FC/sbin/init\F[]를 실행한다.

요약하자면, lxc-execute는 응용 프로그램 실행을 위해서, lxc-start는 시스템 실행을 위해 적합하다.

만약 어플리케이션이 더이상 응답하지 않거나, 접근이 불가능하거나, 스스로 종료되지 못할 경우, lxc-stop 명령어는 컨테이너 내의 모든 프로세스들을 가차없이 종료시킬 것이다.
\FC
          lxc-stop -n foo
        \F[]

  사용가능한 TTY 접속
컨테이너에 tty가 설정되어 있다면, tty를 통해 컨테이너에 접근할 수 있다. 아래 명령어를 통해 사용될 가능한 tty를 제공하는 것은 컨테이너에 달려있다. tty가 종료되었을 때는 다시 로그인하지 않고도 재접속할 수 있다.
\FC
	  lxc-console -n foo -t 3
	\F[]

  컨테이너 동결/동결 해제
스케줄링 등을 위해 컨테이너에 속해있는 모든 프로세스를 정지 시키는 것은 때로 유용할 수 있다. 아래 명령어들을 사용하면 된다.
\FC
	  lxc-freeze -n foo
	\F[]
는 모든 프로세스들을 인터럽트 불가능한 상태로 만든다.
\FC
	  lxc-unfreeze -n foo
	\F[]
는 모든 프로세스를 정지 해제 시킨다.

이 기능은 커널에서 cgroup freezer 기능이 활성화 되어 있어야 사용 가능하다.

  컨테이너 관련 정보 얻어오기
컨테이너가 많이 존재하는 경우, 어떤 것이 생성되고 제거됬는지, 어떤 것이 실행 중인지 또는 어떤 프로세스들이 특정 컨테이너 내에서 실행되는지를 따라가기 힘들다. 이를 위해 다음과 같은 명령어들이 유용하게 사용될 수 있다.
\FC
	  lxc-ls
	  lxc-info -n foo
	\F[]

lxc-ls는 시스템의 컨테이너들의 리스트를 표시한다.

lxc-info는 지정한 컨테이너의 정보를 얻어온다.

아래는 명령어들을 조합하여 컨테이너들의 리스트를 얻어오고 상태를 출력하는 예제이다.
\FC
	  for i in $(lxc-ls -1); do
	    lxc-info -n $i
	  done
	\F[]

  컨테이너 모니터링
컨테이너의 상태를 추적하는 것은 때때로 매우 유용하다. 예를 들어, 상태를 모니터링하거나, 스크립트에서 특정상태를 기다리는 경우이다.

lxc-monitor 명령어는 하나 또는 여러개의 컨테이너들을 모니터링한다. 이 명령어의 인수로 정규표현식을 넘길 수도 있다. 예를 들면,
\FC
	  lxc-monitor -n "foo|bar"
	\F[]
는 'foo'와 'bar'라는 이름의 컨테이너의 상태 변화를 모니터링한다. 그리고,
\FC
	  lxc-monitor -n ".*"
	\F[]
는 모든 컨테이너를 모니터링한다.

'foo' 컨테이너가 시작되고 몇 가지 작업을 수행하고 종료된 경우, 출력은 다음과 같다.
\FC
	  'foo' changed state to [STARTING]
	  'foo' changed state to [RUNNING]
	  'foo' changed state to [STOPPING]
	  'foo' changed state to [STOPPED]
	\F[]

lxc-wait 명령어는 지정한 상태로 변화되는 것을 기다린다. 이 명령어는 컨테이너의 시작이나 종료와 동기화되는 스크립트를 작성할 때 유용하다. 인수는 다른 상태들을 OR로 묶어서 지정해 줄 수 있다. 아래 예제는 백그라운드에서 어떻게 컨테이너의 상태 변화를 기다리는지 보여준다.
\FC
	  # launch lxc-wait in background
	  lxc-wait -n foo -s STOPPED &
	  LXC_WAIT_PID=$!
	  # this command goes in background
	  lxc-execute -n foo mydaemon &
	  # block until the lxc-wait exits
	  # and lxc-wait exits when the container
	  # is STOPPED
	  wait $LXC_WAIT_PID
	  echo "'foo' is finished"
	\F[]

  컨테이너 컨트롤 그룹 설정
컨테이너는 컨트롤 그룹과 결합되어 있다. 컨테이너가 시작되면 컨트롤그룹이 만들어지고 해당 컨트롤 그룹과 연결된다. 컨테이너가 실행중일 때, lxc-cgroup 명령어를 이용해 컨트롤 그룹 속성은 읽거나 수정될 수 있다.

lxc-cgroup 명령어는 컨테이너와 연결된 컨트롤 그룹 서브시스템의 값을 얻어오거나 설정한다. 서브시스템의 이름은 사용자가 결정하며, 이 명령어는 이름이 적합한지 여부를 검사하지 않는다. 만약 서브시스템의 이름이 없다면 명령어는 실패할 것이다.

\FC
	  lxc-cgroup -n foo cpuset.cpus
	\F[]
는 해당 서브시스템의 내용을 표시한다.
\FC
	  lxc-cgroup -n foo cpu.shares 512
	\F[]
는 해당 서브시스템의 값을 설정한다.

버그
lxc는 아직 개발중이다. 그래서 명령어 사용법이나, API가 변경될 수 있다. 버전 1.0.0은 변경되지 않는 고정된 버전이다.

참조
lxc(7), lxc-create(1), lxc-copy(1), lxc-destroy(1), lxc-start(1), lxc-stop(1), lxc-execute(1), lxc-console(1), lxc-monitor(1), lxc-wait(1), lxc-cgroup(1), lxc-ls(1), lxc-info(1), lxc-freeze(1), lxc-unfreeze(1), lxc-attach(1), lxc.conf(5)