产品能力|云原生能力知识体系构建-Docker学习笔记Part5

系列文章目录

云原生能力知识体系构建-Docker学习笔记 第一节:Docker的介绍 第二节:Docker的安装 第三节:Docker的常用命令 第四节:Docker镜像 第五节:Docker 容器连接 第六节:Docker 常见仓库 第七节:构建自己的第一Docker应用。 其他后续完善

提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 系列文章目录
  • 前言
  • 一、什么是容器
  • 二、进入Docker容器
  • 三、启动容器
    • 3.1 新建并启动
    • 3.2 守护态运行
  • 四、 终止Docker容器
  • 总结

前言

什么是容器: 容器的本质: 容器是使用 namespace 进行隔离,cgroup 进行资源限制,并且带有 rootfs 的进程。 主要目标是 了解容器的概念,以及容器的常用操作 进入容器 启动容器 和停止容器。

一、什么是容器

理解容器的本质最简单的方式就是类比。

进程是程序的运行实体; 容器是镜像的运行实体。 镜像和程序的角色是一样的,只不过镜像要比程序更加的丰富。程序只是按简单的格式存储在文件系统中,而镜像是按层,以联合文件系统的方式存储。

容器和进程的角色也是类似的,只不过容器相比于普通进程多了更多地附加属性。

既然容器也是进程,那么它一定也有进程号,那么如何将容器映射到操作系统的进程呢?我们这里还是以 Docker 容器为例。通过 docker top 命令可以看到容器的进程号。下面举个例子。

在这里插入图片描述

从上图我们可以看进程的信息包括:

参数

含义

USER:

进程的启动用户;

PID:

进程号,每个进程都会被分配一个 PID,是一种系统资源,并且每个系统中的进程号个数是有限的;

%CPU:

CPU 使用率;

%MEM:

内存使用率;

理解容器的本质最简单的方式就是类比。

进程是程序的运行实体; 容器是镜像的运行实体。

镜像和程序的角色是一样的,只不过镜像要比程序更加的丰富。程序只是按简单的格式存储在文件系统中,而镜像是按层,以联合文件系统的方式存储。

容器和进程的角色也是类似的,只不过容器相比于普通进程多了更多地附加属性。

既然容器也是进程,那么它一定也有进程号,那么如何将容器映射到操作系统的进程呢?我们这里还是以 Docker 容器为例。通过 docker top 命令可以看到容器的进程号。下面举个例子。

代码语言:javascript
复制
[root@dockerdemo ~]# docker ps
CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                    NAMES
d3973eb73bec        http-server:v1           "/http-server"           35 hours ago        Up 35 hours         0.0.0.0:8091->8091/tcp   clever_nobel
bf90054c3017        google/cadvisor:latest   "/usr/bin/cadvisor -…"   12 days ago         Up 12 days          0.0.0.0:8081->8080/tcp   cadvisor
246cf9479cdf        busybox                  "sh"                     12 days ago         Up 12 days                                   ecstatic_shirley
ff4f54614a02        busybox                  "sh"                     12 days ago         Up 12 days                                   boring_meitner
9d72cb96129c        busybox                  "sh"                     13 days ago         Up 13 days                                   priceless_shannon
[root@dockerdemo ~]# docker top d3973eb73bec
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                25533               25514               0                   Jun25               ?                   00:00:00            /http-server
[root@xxx ~]# ps aux | grep 25533
root      7008  0.0  0.0 112716   964 pts/0    S+   20:26   0:00 grep --color=auto 25533
root     25533  0.0  0.0 707104  2564 ?        Ssl  Jun25   0:00 /http-server

我们的 http-server 容器对应的操作系统进程号就为 25533 号进程。为了更加直接的感受一下容器是一种进程,我们可以看一下 /proc/ 这个目录。在 Linux 中,每个进程的信息都可以通过目录 /proc 下面查找到,进程号会作为目录的名称。

代码语言:javascript
复制
[root@dockerdemo proc]# ls
1      10733  13860  18     2227   25514  3041   33    456   542   659   7922  8440       cpuinfo      irq         modules       swaps
10     10773  14     1843   22270  25533  3042   3327  46    543   7     7923  8442       crypto       kallsyms    mounts        sys
1006   1078   14011  19     22288  26     3043   3333  47    57    7274  7941  8443       devices      kcore       mtrr          sysrq-trigger
10148  11     14180  2      22392  260    30526  34    4765  5716  7283  7994  8450       diskstats    keys        net           sysvipc
10173  11247  1502   2068   23     26513  306    3475  4767  5718  75    8     8988       dma          key-users   pagetypeinfo  timer_list
10338  11632  1503   2083   2312   27900  3074   35    49    59    7618  8043  9          driver       kmsg        partitions    timer_stats
1035   12     1505   20890  24     28     3075   36    5163  6     7624  8062  9006       execdomains  kpagecount  sched_debug   tty
10353  12461  15489  20892  24985  28390  30761  3600  517   60    7626  8122  acpi       fb           kpageflags  schedstat     uptime
1036   13     16     21     2500   289    31     37    529   61    7730  8205  buddyinfo  filesystems  loadavg     scsi          version
1038   1301   17     21249  2520   29     32     3706  531   611   7740  8252  bus        fs           locks       self          vmallocinfo
1039   13267  17030  2140   25247  294    32226  38    532   62    7774  8341  cgroups    interrupts   mdstat      slabinfo      vmstat
1046   1372   17165  22     2531   30     32676  39    5343  624   7806  8343  cmdline    iomem        meminfo     softirqs      zoneinfo
1051   1376   172    22264  25508  301    3277   4     5361  646   7814  8439  consoles   ioports      misc        stat
[root@emr-header-1 proc]# cd 25533
[root@emr-header-1 25533]# ls
attr       clear_refs       cpuset   fd       limits     mem         net        oom_score      personality  schedstat  stack   syscall  wchan
autogroup  cmdline          cwd      fdinfo   loginuid   mountinfo   ns         oom_score_adj  projid_map   sessionid  stat    task
auxv       comm             environ  gid_map  map_files  mounts      numa_maps  pagemap        root         setgroups  statm   timers
cgroup     coredump_filter  exe      io       maps       mountstats  oom_adj    patch_state    sched        smaps      status  uid_map
[root@xxx 25533]# ls -al ns
total 0
dr-x--x--x 2 root root 0 Jun 25 09:40 .
dr-xr-xr-x 9 root root 0 Jun 25 09:40 ..
lrwxrwxrwx 1 root root 0 Jun 26 20:29 ipc -> ipc:[4026532462]
lrwxrwxrwx 1 root root 0 Jun 26 20:29 mnt -> mnt:[4026532460]
lrwxrwxrwx 1 root root 0 Jun 25 09:40 net -> net:[4026532524]
lrwxrwxrwx 1 root root 0 Jun 26 20:29 pid -> pid:[4026532463]
lrwxrwxrwx 1 root root 0 Jun 26 20:29 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jun 26 20:29 uts -> uts:[4026532461]
[root@xxx 25533]# cat cgroup
11:cpuset:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
10:hugetlb:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
9:perf_event:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
8:pids:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
7:freezer:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
6:memory:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
5:net_prio,net_cls:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
4:devices:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
3:blkio:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
2:cpuacct,cpu:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d
1:name=systemd:/docker/d3973eb73bec5e62bf47710d8607a87ce27973c3dcd653b39eae41da25564d4d

上图显示的就是 http-server 这个容器作为操作系统的进程的一些基本信息,比如 ns 目录就对应 6 个不同的 namespace,而 cgroup 则对应 11 种不同的 cgroup。

二、进入Docker容器

在使用 -d 参数时,容器启动后会进入后台。 某些时候需要进入容器进行操作,有很多种方法,包括使用 docker attach 命令或 nsenter 工具等。

attach 命令 docker attach 是Docker自带的命令。下面示例如何使用该命令。

代码语言:javascript
复制
$ sudo docker run -idt ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
243c32535da7        ubuntu:latest       "/bin/bash"         18 seconds ago      Up 17 seconds                           nostalgic_hypatia
$sudo docker attach nostalgic_hypatia
root@243c32535da7:/#

但是使用 attach 命令有时候并不方便。当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示。当某个窗口因命令阻塞时,其他窗口也无法执行操作了。

nsenter 命令 安装 nsenter 工具在 util-linux 包2.23版本后包含。 如果系统中 util-linux 包没有该命令,可以按照下面的方法从源码安装。

代码语言:javascript
复制
$ cd /tmp; curl https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz | tar -zxf-; cd util-linux-2.24;
$ ./configure --without-ncurses
$ make nsenter && sudo cp nsenter /usr/local/bin

使用 nsenter 可以访问另一个进程的名字空间。nsenter 要正常工作需要有 root 权限。 很不幸,Ubuntu 14.04 仍然使用的是 util-linux 2.20。安装最新版本的 util-linux(2.24)版,请按照以下步骤:

代码语言:javascript
复制
$ wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz; tar xzvf util-linux-2.24.tar.gz
$ cd util-linux-2.24
$ ./configure --without-ncurses && make nsenter
$ sudo cp nsenter /usr/local/bin

为了连接到容器,你还需要找到容器的第一个进程的 PID,可以通过下面的命令获取。

代码语言:javascript
复制
PID=$(docker inspect --format "{{ .State.Pid }}" <container>)

通过这个 PID,就可以连接到这个容器:

代码语言:javascript
复制
$ nsenter --target $PID --mount --uts --ipc --net --pid

下面给出一个完整的例子。

代码语言:javascript
复制
$ sudo docker run -idt ubuntu
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
243c32535da7        ubuntu:latest       "/bin/bash"         18 seconds ago      Up 17 seconds                           nostalgic_hypatia
$ PID=$(docker-pid 243c32535da7)
10981
$ sudo nsenter --target 10981 --mount --uts --ipc --net --pid
root@243c32535da7:/#

更简单的,建议大家下载 .bashrc_docker,并将内容放到 .bashrc 中。

代码语言:javascript
复制
$ wget -P ~ https://github.com/yeasy/docker_practice/raw/master/_local/.bashrc_docker;
$ echo "[ -f ~/.bashrc_docker ] && . ~/.bashrc_docker" >> ~/.bashrc; source ~/.bashrc

这个文件中定义了很多方便使用 Docker 的命令,例如 docker-pid 可以获取某个容器的 PID;而 docker-enter 可以进入容器或直接在容器内执行命令。

代码语言:javascript
复制
$ echo $(docker-pid <container>)
$ docker-enter <container> ls

三、启动容器

启动Docker容器 启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(stopped)的容器重新启动。

因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。

3.1 新建并启动

所需要的命令主要为 docker run。

例如,下面的命令输出一个 “Hello World”,之后终止容器。

代码语言:javascript
复制
$ sudo docker run ubuntu:14.04 /bin/echo 'Hello world'
Hello world

这跟在本地直接执行 /bin/echo ‘hello world’ 几乎感觉不出任何区别。

下面的命令则启动一个 bash 终端,允许用户进行交互。

代码语言:javascript
复制
$ sudo docker run -t -i ubuntu:14.04 /bin/bash
root@af8bae53bdd3:/#

其中,-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。

在交互模式下,用户可以通过所创建的终端来输入命令,例如

代码语言:javascript
复制
root@af8bae53bdd3:/# pwd
/
root@af8bae53bdd3:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:

检查本地是否存在指定的镜像,不存在就从公有仓库下载 利用镜像创建并启动一个容器 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去 从地址池配置一个 ip 地址给容器 执行用户指定的应用程序 执行完毕后容器被终止 启动已终止容器 可以利用 docker start 命令,直接将一个已经终止的容器启动运行。

容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 ps 或 top 来查看进程信息。

代码语言:javascript
复制
root@ba267838cc1b:/# ps
  PID TTY          TIME CMD
    1 ?        00:00:00 bash
   11 ?        00:00:00 ps

可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。

3.2 守护态运行

更多的时候,需要让 Docker 容器在后台以守护态(Daemonized)形式运行。此时,可以通过添加 -d 参数来实现。

例如下面的命令会在后台运行容器。

代码语言:javascript
复制
$ sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

1e5535038e285177d5214659a068137486f96ee5c2e85a4ac52dc83f2ebe4147 容器启动后会返回一个唯一的 id,也可以通过 docker ps 命令来查看容器信息。

代码语言:javascript
复制
$ sudo docker ps
CONTAINER ID  IMAGE         COMMAND               CREATED        STATUS       PORTS NAMES
1e5535038e28  ubuntu:14.04  /bin/sh -c 'while tr  2 minutes ago  Up 1 minute        insane_babbage

要获取容器的输出信息,可以通过 docker logs 命令。

代码语言:javascript
复制
$ sudo docker logs insane_babbage
hello world
hello world
hello world

四、 终止Docker容器

可以使用 docker stop 来终止一个运行中的容器。

此外,当Docker容器中指定的应用终结时,容器也自动终止。 例如对于上一章节中只启动了一个终端的容器,用户通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。

终止状态的容器可以用 docker ps -a 命令看到。例如

代码语言:javascript
复制
sudo docker ps -a
CONTAINER ID        IMAGE                    COMMAND                CREATED             STATUS                          PORTS               NAMES
ba267838cc1b        ubuntu:14.04             "/bin/bash"            30 minutes ago      Exited (0) About a minute ago                       trusting_newton
98e5efa7d997        training/webapp:latest   "python app.py"        About an hour ago   Exited (0) 34 minutes ago                           backstabbing_pike

处于终止状态的容器,可以通过 docker start 命令来重新启动。

此外,docker restart 命令会将一个运行态的容器终止,然后再重新启动它。

总结

`提示:容器是一个特殊进程

例如:以上就是今天要讲的内容,让我们了解了容器以及一些常用的容器的操作。

下一篇:Docker 常见仓库