25/10/21 컨테이너 기술과 애플리케이션 가상화 2

344th·2025년 12월 11일

AWS AI

목록 보기
27/48
docker create -it
docker start -ai
docker run -it

둘 다 결국 표준입출력 스트림 가상터미널이 열리는 건 똑같음

그러나 왜 start 에는 -ait 가 아니라 -ai 를 쓸까?

→ start 는 컨테이너 실행만 가능하고, 가상터미널은 가지고 있지 않음

→ create 에서 입출력스트림 가상터미널을 이미 만들어놨기 때문에?

→ start 에서는 입력스트림에 붙기만 해도 가상터미널이 열린다?

도커 실행

도커 foreground 실행

docker run

: 포어그라운드 실행

  • Dockerfile 안의 CMDENTRYPOINT 에 정의된 명령이 컨테이너 시작 시 자동 실행됨
  • 그 명령이 종료되면 컨테이너도 즉시 종료(exited) 상태로 바뀜

→ 따라서 무엇을 실행하도록 이미지가 만들어졌느냐가 곧 “컨테이너가 살아있는지”를 결정

Docker 컨테이너는 “리눅스 프로세스를 실행하는 일회용 환경”

  • docker run -it
  • docker create -it && docker start -ai
항목httpdnginxubuntu
기본 CMDhttpd-foregroundnginx -g "daemon off;"bash
목적웹 서버웹 서버리눅스 기본 환경
실행 방식포그라운드 모드 (백그라운드 안 감)포그라운드 모드 + 로그 출력쉘 실행 (stdin 없으면 종료)
로그 출력내부 로그 파일 중심STDOUT 로그 출력없음
TTY 필요 여부필요 없음필요 없음필요함 (-it 필요)
컨테이너 종료 조건httpd 프로세스 종료 시nginx 프로세스 종료 시bash 종료 시 (입력 끝남)

PID 1 예시

이미지기본 CMD / ENTRYPOINT컨테이너 내부의 PID 1
ubuntubashbash
nginxnginx -g "daemon off;"nginx (마스터 프로세스)
httpdhttpd-foregroundhttpd
mysqldocker-entrypoint.sh mysqldbash (entrypoint 스크립트) 또는 mysqld
redisredis-serverredis-server
python (기본)python3python3

도커 background 실행

**docker run -it
Ctrl+P+Q**
**docker run -d

# 실행 후 종료되는 경우
# docker run -it, Ctrl+P+Q 의 과정을 생략
docker run -itd**

도커 command

**$ docker run --name os3-temp ubuntu:latest hostname

$ docker ps -a**
CONTAINER ID   IMAGE           COMMAND              CREATED          STATUS                       PORTS     NAMES
0873a8a6a0f1   ubuntu:latest   **"hostname"**           55 seconds ago   Exited (0) 54 seconds ago              os3-temp

hostname 을 보고 싶은데 컨테이너가 남으면 일일이 지워야 하는게 귀찮으니까

hostname 만 보고 바로 지워지도록 하려면

$ docker run --rm ubuntu:latest hostname
4f2314f0b6c6

실행시 컨테이너 내부에 환경변수 설정

**$ docker run -it --name os3 -e "LAB=docker" ubuntu:latest**

root@7fef3580f96e:/# **echo $LAB**
docker

실행 컨테이너 상태 확인

docker stats

: 실시간 상태 확인 가능

: Ctrl+C 로 종료

docker stats --no-stream

→ 실시간으로 보지 않고 그 순간의 스냅샷을 확인하고 싶을 때

컨테이너에 cpu 제한

# Ubuntu 이미지를 기반으로
# CPU 사용률이 0.3(=30%)로 제한된 컨테이너 os4 를 
# 백그라운드에서 실행
**$ docker run -itd --name os4 --cpus 0.3 ubuntu:latest**

# 실행 중인 컨테이너(os4) 안에서
# dd 명령어를 이용해 CPU를 계속 사용하는 작업을 백그라운드로 실행
**$ docker exec os4 dd if=/dev/zero of=/dev/null &**

옵션의미
-iinteractive — 표준 입력을 열어둠 (입력 가능 상태 유지)
-ttty — 가상 터미널 할당 (터미널 모드)
-ddetach — 백그라운드 모드로 실행 (터미널에 붙지 않음)
--name os4컨테이너 이름을 os4로 지정
--cpus 0.3이 컨테이너가 사용할 수 있는 CPU 시간 비율을 30%로 제한
ubuntu:latest사용할 베이스 이미지 지정
부분의미
docker exec os4이미 실행 중인 컨테이너 os4 내부에서 명령 실행
dd파일 복사 유틸리티 (Data Duplicator)
if=/dev/zero입력 파일(input file): 무한히 0을 출력하는 장치
of=/dev/null출력 파일(output file): 데이터를 모두 버리는 장치
&명령을 백그라운드에서 실행
  • dd if=/dev/zero of=/dev/null무한 루프 형태의 CPU 연산을 계속 수행 (0을 읽어서 버리는 일을 계속 반복 → 매우 CPU 집약적)
  • 하지만 컨테이너는 -cpus 0.3 으로 제한되어 있으므로, 실제 호스트 CPU 점유율은 약 30% 정도로 제한됨

컨테이너에 메모리 할당 제한

**$ docker run -itd --name os4 --memory 1024m ubuntu:latest**

# 할당 메모리 크기 업데이트
**$ docker update --memory 1400m os4**

컨테이너 내부 접속 방법

**$ docker run -itd --name os-final ubuntu:latest**

백그라운드에서 실행되는 컨테이너 생성

  • attach
    **$ docker attach os-final
    
    root@57f434c69026:/# exit**
    exit
    
    **$ docker ps -a**
    ... STATUS                           PORTS     NAMES
    ... Exited (0) 4 seconds ago                   os-final
    exit 로 빠져나가면 ubuntu 의 PID 1 인 "/bin/bash” (=표준입출력)이 종료되므로 컨테이너도 종료됨
  • exec
    **$ docker start os-final**
    os-final
    
    **$ docker exec -it os-final /bin/bash
    root@57f434c69026:/# ps -ef**
    UID          PID    PPID  C STIME TTY          TIME CMD
    root           1       0  0 10:30 pts/0    00:00:00 /bin/bash
    root           9       0  0 10:31 pts/1    00:00:00 /bin/bash
    root          18       0  0 10:33 pts/2    00:00:00 /bin/bash
    root          26      18  0 10:33 pts/2    00:00:00 ps -ef
    
    **root@57f434c69026:/# exit**
    exit
    
    **$ docker ps**
    .. STATUS             PORTS     NAMES
    ... Up 5 minutes                 os-final
    • pty

      : 가상터미널

      : pts 의 일부

    • pts/0 , pts/1 , pts/2

      : 새로운 터미널 세션이 만들어질 때마다 뒤 숫자가 증가

      이렇게 열면 exit 로 터미널을 빠져나와도 메인프로세스(PID 1, run -itd 로 백그라운드 실행 중인 터미널)는 종료되지 않으므로 컨테이너는 계속 실행됨. 종료되지 않음.

컨테이너 내부 프로세스 확인

docker top

**$ docker top os-final**
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                41643               41619               0                   10:30               pts/0               00:00:00            /bin/bash
root                41692               41619               0                   10:31               pts/1               00:00:00            /bin/bash

컨테이너 내부 로그 확인

docker logs

: 대표적으로 표준입출력, 에러 등의 메세지가 기록됨

# mysql 컨테이너 생성 및 백그라운드 실행
**$ docker run -d --name db-fail public.ecr.aws/docker/library/mysql:5.7

# 비정상 종료
$ docker ps -a**
... STATUS                           PORTS     NAMES
... Exited (1) 44 seconds ago                  db-fail

# 왜 비정상 종료됐는지 에러 로그 확인
**$ docker logs db-fail**
2025-10-20 10:43:04+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.44-1.el7 started.
2025-10-20 10:43:04+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2025-10-20 10:43:04+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.44-1.el7 started.
2025-10-20 10:43:05+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
    You need to specify one of the following as an environment variable:
    - MYSQL_ROOT_PASSWORD
    - MYSQL_ALLOW_EMPTY_PASSWORD
    - MYSQL_RANDOM_ROOT_PASSWORD

# 이미지 태그 추가(너무 길어서)
$ docker tag public.ecr.aws/docker/library/mysql:5.7 mysql:5.7

# log 에 뜬 패스워드 에러를 해결하고 실행
**$ docker run -d --name db-good -e "MYSQL_ROOT_PASSWORD=qwer" mysql:5.7

$ docker ps**
... STATUS             PORTS                 NAMES
... Up 6 seconds       3306/tcp, 33060/tcp   db-good

# 에러 로그 없는 거 확인 가능 
**$ docker logs db-good
...**
2025-10-20T10:47:56.717750Z 0 [Warning] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
2025-10-20T10:47:56.743144Z 0 [Note] Event Scheduler: Loaded 0 events
2025-10-20T10:47:56.744334Z 0 [Note] mysqld: ready for connections.
Version: '5.7.44'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)

도커 컨테이너 네트워크

호스트에서 컨테이너로 파일 전송

docker cp [파일] [컨테이너 이름:디렉토리 경로]

**$ touch host-file.txt**

**$ docker cp host-file.txt os-final:/tmp/**
Successfully copied 1.54kB to os-final:/tmp/

**$ docker exec os-final cat /tmp/host-file.txt**

컨테이너에서 호스트로 파일 전송

docker cp [컨테이너 이름:디렉토리 경로] [파일]

**$ docker run -d --name web3 --rm httpd:latest

$ docker cp web3:/usr/local/apache2/htdocs/index.html /tmp/index.html**
Successfully copied 2.05kB to /tmp/index.html

**$ cat /tmp/index.html**
<html><body><h1>It works!</h1></body></html>
옵션설명
-d백그라운드(detached) 모드로 실행
--name web3컨테이너 이름을 web3로 지정
--rm컨테이너가 종료될 때 자동으로 삭제
httpd:latest실행할 이미지

컨테이너 파일시스템의 변경 내역

: 원본 이미지 파일시스템과 상태 비교

docker diff

**$ docker diff os-final**
C /tmp
A /tmp/host-file.txt
C /root
A /root/.bash_history

# os-final 파일 시스템에서 파일 하나 삭제
**$ docker exec os-final rm /tmp/host-file.txt**

# 파일시스템 변경 내역 리스트에서 사라짐
# 삭제됐다고 표시되지 않음(원래 파일시스템에 있는 파일이 아니었기 때문에)
**$ docker diff os-final**
C /root
A /root/.bash_history

# 원본 이미지 파일시스템에 있던 파일 삭제
**$ docker exec os-final rm /bin/ls**

# 삭제 내역 확인 가능
**$ docker diff os-final**
C /root
A /root/.bash_history
C /usr
C /usr/bin
D /usr/bin/l
  • A : append : 추가
  • C : change : 변경
  • D : delete : 삭제

도커 이미지 레이어

: 파일시스템

도커 컨테이너 네트워크

컨테이너 네트워크는 격리되어 있음

도커 컨테이너 네트워크 구조

  • 네트워크 네임스페이스
  • 가상 랜선(veth) : virtual ethernet
  • 가상 공유기(bridge) : docker0 의 0. 숫자 부분?
**$ docker run -itd --name net-test-1 public.ecr.aws/docker/library/rockylinux:9**

**$ docker exec -it net-test-1 bash**
**[root@7ee000012391 /]# dnf install -y net-tools iproute

[root@7ee000012391 /]# ifconfig**
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.2  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 2a:bd:c7:cd:7b:4a  txqueuelen 0  (Ethernet)
        RX packets 3318  bytes 20294619 (19.3 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2318  bytes 153142 (149.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  • eth0 : 가상 네트워크 인터페이스
  • lo : 루프백(localhost)

host 의 네트워크

$ ifconfig
**docker0**: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::b093:7cff:fe56:5210  prefixlen 64  scopeid 0x20<link>
        ether b2:93:7c:56:52:10  txqueuelen 0  (Ethernet)
        RX packets 5974  bytes 270750 (264.4 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8337  bytes 54824973 (52.2 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

**enp0s3**: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 10.0.2.15  netmask 255.255.255.0  broadcast 10.0.2.255
        inet6 fd17:625c:f037:2:a00:27ff:feed:ab98  prefixlen 64  scopeid 0x0<global>
        inet6 fe80::a00:27ff:feed:ab98  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:ed:ab:98  txqueuelen 1000  (Ethernet)
        RX packets 773141  bytes 1058874843 (1009.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 89966  bytes 6763529 (6.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

enp0s8: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.56.12  netmask 255.255.255.0  broadcast 192.168.56.255
        inet6 fe80::a00:27ff:fe4f:ae73  prefixlen 64  scopeid 0x20<link>
        ether 08:00:27:4f:ae:73  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 13  bytes 970 (970.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 2  bytes 144 (144.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 2  bytes 144 (144.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

**veth94a5f7f**: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::c0be:ff:fe86:135b  prefixlen 64  scopeid 0x20<link>
        ether c2:be:00:86:13:5b  txqueuelen 0  (Ethernet)
        RX packets 2318  bytes 153142 (149.5 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 3319  bytes 20294689 (19.3 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  • docker0 : 가상공유기(bridge) : 게이트웨이(ip주소 끝이 1로 끝나면 웬만하면 게이트웨이) : 도커 호스트와 컨테이너를 연결하는 다리 역할 : 컨테이너들의 veth 인터페이스를 연결하는 가상 브리지
    • 트래픽을 호스트 네트워크로 전달하는 역할을 합니다.
    • 이후 enp0s3로 내보내는 과정(NAT + 라우팅) 은 호스트 커널이 담당
    • 각 컨테이너의 eth0는 veth 쌍을 통해 docker0에 연결
    • docker0은 내부 네트워크 허브 역할
    • 컨테이너 간 통신은 docker0 안에서 직접 전달됨
    • 외부 통신은 docker0 → iptables NAT → enp0s3를 거쳐 나감
  • enp0s3 : 가상머신이 외부와 통신하기 위한(인터넷) NIC : 도커 호스트 자체적으로 보유한 네트워크 인터페이스(NIC)
  • veth94a5f7f : 가상랜선 : 컨테이너가 생성될 때마다 만들어짐 : 컨테이너 내부의 eth0과 도커 호스트의 docker0을 연결시켜주는 가상 인터페이스 : 컨테이너 내부의 eth0과 한 쌍.
   [컨테이너 내부]
      eth0 (veth의 한쪽 끝)
          │
          │
      ┌───┴─────────────┐
      │   Host          │
      │                 │
      │  vethXXXX (반대쪽) ─────┐
      │                          │
      │  vethYYYY (다른 컨테이너)│
      │                          │
      │        ┌─────────────────┴─────┐
      │        │ docker0 (가상 브리지) │  ← 컨테이너들 내부 통신 허브
      │        └─────────────────┬─────┘
      │                          │
      │               (NAT via iptables)
      │                          │
      │                    enp0s3 (물리 NIC)
      │                          │
      │                     [외부 네트워크]
      └──────────────────────────┘
컨테이너 eth0 ─┐
                │
컨테이너 eth0 ─┤  (여러 개의 veth들이 연결)
                │
             [ docker0 ]
                │
                ▼
      (호스트 커널의 라우팅 테이블)
                │
       NAT (iptables MASQUERADE)
                │
                ▼
          [ enp0s3 NIC ]
                │
             [ 외부 네트워크 ]

사용자 정의 브릿지 사용

**$ docker network create prod-net**
ffde2dfe78ffa8e4a58537af8a592c3b511b53cceab557f5780329c0dbe57fd9

**$ docker network ls**
NETWORK ID     NAME       DRIVER    SCOPE
f927f2c1e57a   bridge     bridge    local
81a81a4e3cb8   host       host      local
013a5c209a59   none       null      local
ffde2dfe78ff   prod-net   bridge    local
**$ docker run -d --name prod-web --network prod-net httpd

$ docker run -d --name prod-db --network prod-net -e MYSQL_ROOT_PASSWORD=1234 mysql:5.7

$ docker ps**
CONTAINER ID   IMAGE                                        COMMAND                  CREATED              STATUS              PORTS                 NAMES
156589d72125   mysql:5.7                                    "docker-entrypoint.s…"   55 seconds ago       Up 55 seconds       3306/tcp, 33060/tcp   prod-db
49060989a818   httpd                                        "httpd-foreground"       About a minute ago   Up About a minute   80/tcp                prod-web
7ee000012391   public.ecr.aws/docker/library/rockylinux:9   "/bin/bash"              25 minutes ago       Up 25 minutes                             net-test-1
  • 웹, 디비 컨테이너 생성 및 백그라운드 실행
  • db는 환경변수 설정 안하면 오류 나므로 지정
# exec 으로 bash 가상터미널 접속
**$ docker exec -it prod-web bash**
root@49060989a818:/usr/local/apache2#

# 핑 설치
root@49060989a818:/usr/local/apache2# apt-get update && apt-get install -y iputils-ping
# db ip 주소가 아닌 컨테이너 이름으로 요청 보내도 정상적으로 보내짐
root@49060989a818:/usr/local/apache2# ping -c 4 prod-db
PING prod-db (172.18.0.3) 56(84) bytes of data.
64 bytes from prod-db.prod-net (172.18.0.3): icmp_seq=1 ttl=64 time=0.295 ms
64 bytes from prod-db.prod-net (172.18.0.3): icmp_seq=2 ttl=64 time=0.343 ms
64 bytes from prod-db.prod-net (172.18.0.3): icmp_seq=3 ttl=64 time=0.198 ms
64 bytes from prod-db.prod-net (172.18.0.3): icmp_seq=4 ttl=64 time=0.262 ms

--- prod-db ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 0.198/0.274/0.343/0.052 ms

사용자 브리지 장점

  • 같은 사용자 브리지를 사용하는 컨테이너라면 그냥 컨테이너 이름으로 통신 가능 → ip 주소 따로 알아낼 필요 없음

포트포워딩

$ docker run -d --name prod-web --network prod-net -p 8080:80 httpd
  • 8080:80 : 을 기준으로 좌우를 나눔
    • 호스트 포트:컨테이너 포트

      해당 포트로 외부에 개방?

네트워크 none

**$ docker run -it --rm --name none-box --network none public.ecr.aws/docker/library/busybox sh**
Unable to find image 'public.ecr.aws/docker/library/busybox:latest' locally
latest: Pulling from docker/library/busybox
80bfbb8a41a2: Pull complete
Digest: sha256:2f590fc602ce325cbff2ccfc39499014d039546dc400ef8bbf5c6ffb860632e7
Status: Downloaded newer image for public.ecr.aws/docker/library/busybox:latest

**/ # ifconfig**
lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

루프백 주소만 존재

호스트의 네트워크를 그대로 사용

--network host

$ docker run -d --name host-web --network host public.ecr.aws/docker/library/nginx
  • 컨테이너가 호스트의 네트워크 스택을 그대로 공유 (즉, 호스트의 NIC(enp0s3) 를 직접 사용)
  • docker0, veth, 포트포워딩 같은 가상 네트워크 계층이 존재 X
[브라우저]
   ↓
[호스트 enp0s3:80]
   ↓ (직접)
[컨테이너 nginx:80]

이미 점유된 포트를 할당하려고 하면 에러남

profile
새싹 개발자

0개의 댓글