2018年6月

按照这些简单的步骤在你的 Linux、Mac 或 Windows 桌面上安装 Docker CE。

在上一篇文章中,我们学习了容器世界的一些基本术语。当我们运行命令并在后续文章中使用其中一些术语时,这些背景信息将会派上用场,包括这篇文章。本文将介绍在桌面 Linux、 macOS 和 Windows 上安装 Docker,它适用于想要开始使用 Docker 容器的初学者。唯一的先决条件是你对命令行界面满意。

为什么我在本地机器上需要 Docker CE?

作为一个新用户,你很可能想知道为什么你在本地系统上需要容器。难道它们不是作为微服务在云和服务器中运行吗?尽管容器长期以来一直是 Linux 世界的一部分,但 Docker 才真正使容器的工具和技术步入使用。

Docker 容器最大的优点是可以使用本地机器进行开发和测试。你在本地系统上创建的容器映像可以在“任何位置”运行。开发人员和操作人员之间不再会为应用程序在开发系统上运行良好但生产环境中出现问题而产生纷争。

而这个关键是,要创建容器化的应用程序,你必须能够在本地系统上运行和创建容器。

你可以使用以下三个平台中的任何一个 —— 桌面 Linux、 Windows 或 macOS 作为容器的开发平台。一旦 Docker 在这些系统上成功运行,你将可以在不同的平台上使用相同的命令。因此,接下来你运行的操作系统无关紧要。

这就是 Docker 之美。

让我们开始吧

现在有两个版本的 Docker:Docker 企业版(EE)和 Docker 社区版(CE)。我们将使用 Docker 社区版,这是一个免费的 Docker 版本,面向想要开始使用 Docker 的开发人员和爱好者。

Docker CE 有两个版本:stable 和 edge。顾名思义,stable(稳定)版本会为你提供经过充分测试的季度更新,而 edge 版本每个月都会提供新的更新。经过进一步的测试之后,这些边缘特征将被添加到稳定版本中。我建议新用户使用 stable 版本。

Docker CE 支持 macOS、 Windows 10、 Ubuntu 14.04/16.04/17.04/17.10、 Debian 7.7/8/9/10、 Fedora 25/26/27 和 CentOS。虽然你可以下载 Docker CE 二进制文件并安装到桌面 Linux 上,但我建议添加仓库源以便继续获得修补程序和更新。

在桌面 Linux 上安装 Docker CE

你不需要一个完整的桌面 Linux 来运行 Docker,你也可以将它安装在最小的 Linux 服务器上,即你可以在一个虚拟机中运行。在本教程中,我将在我的主系统的 Fedora 27 和 Ubuntu 17.04 上运行它。

在 Ubuntu 上安装

首先,运行系统更新,以便你的 Ubuntu 软件包完全更新:

$ sudo apt-get update

现在运行系统升级:

$ sudo apt-get dist-upgrade

然后安装 Docker PGP 密钥:

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
Update the repository info again:
$ sudo apt-get update

现在安装 Docker CE:

$ sudo apt-get install docker-ce

一旦安装完成,Docker CE 就会在基于 Ubuntu 的系统上自动运行,让我们来检查它是否在运行:

$ sudo systemctl status docker

你应该得到以下输出:

docker.service - Docker Application Container Engine
  Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
  Active: active (running) since Thu 2017-12-28 15:06:35 EST; 19min ago
    Docs: https://docs.docker.com
Main PID: 30539 (dockerd)

由于 Docker 安装在你的系统上,你现在可以使用 Docker CLI(命令行界面)运行 Docker 命令。像往常一样,我们运行 ‘Hello World’ 命令:

$ sudo docker run hello-world

恭喜!在你的 Ubuntu 系统上正在运行着 Docker。

在 Fedora 上安装 Docker CE

Fedora 27 上的情况有些不同。在 Fedora 上,你首先需要安装 def-plugins-core 包,这将允许你从 CLI 管理你的 DNF 包。

$ sudo dnf -y install dnf-plugins-core

现在在你的系统上安装 Docker 仓库:

$ sudo dnf config-manager \
    --add-repo \
    https://download.docker.com/linux/fedora/docker-ce.repo
It’s time to install Docker CE:
$ sudo dnf install docker-ce

与 Ubuntu 不同,Docker 不会在 Fedora 上自动启动。那么让我们启动它:

$ sudo systemctl start docker

你必须在每次重新启动后手动启动 Docker,因此让我们将其配置为在重新启动后自动启动。$ systemctl enable docker 就行。现在该运行 Hello World 命令了:

$ sudo docker run hello-world

恭喜,在你的 Fedora 27 系统上正在运行着 Docker。

解除 root

你可能已经注意到你必须使用 sudo 来运行 docker 命令。这是因为 Docker 守护进程与 UNIX 套接字绑定,而不是 TCP 端口,套接字由 root 用户拥有。所以,你需要 sudo 权限才能运行 docker 命令。你可以将系统用户添加到 docker 组,这样它就不需要 sudo 了:

$ sudo groupadd docker

在大多数情况下,在安装 Docker CE 时会自动创建 Docker 用户组,因此你只需将用户添加到该组中即可:

$ sudo usermod -aG docker $USER

为了测试该组是否已经成功添加,根据用户名运行 groups 命令:

$ groups swapnil

(这里,swapnil 是用户名。)

这是在我系统上的输出:

swapnil : swapnil adm cdrom sudo dip plugdev lpadmin sambashare docker

你可以看到该用户也属于 docker 组。注销系统,这样组就会生效。一旦你再次登录,在不使用 sudo 的情况下试试 Hello World 命令:

$ docker run hello-world

你可以通过运行以下命令来查看关于 Docker 的安装版本以及更多系统信息:

$ docker info

在 macOS 和 Windows 上安装 Docker CE

你可以在 macOS 和 Windows 上很轻松地安装 Docker CE(和 EE)。下载官方为 macOS 提供的 Docker 安装包,在 macOS 上安装应用程序的方式是只需将它们拖到 Applications 目录即可。一旦文件被复制,从 spotlight(LCTT 译注:mac 下的搜索功能)下打开 Docker 开始安装。一旦安装,Docker 将自动启动,你可以在 macOS 的顶部看到它。

macOS 是类 UNIX 系统,所以你可以简单地打开终端应用程序,并开始使用 Docker 命令。测试 hello world 应用:

$ docker run hello-world

恭喜,你已经在你的 macOS 上运行了 Docker。

在 Windows 10 上安装 Docker

你需要最新版本的 Windows 10 Pro 或 Server 才能在它上面安装或运行 Docker。如果你没有完全更新,Windows 将不会安装 Docker。我在 Windows 10 系统上遇到了错误,必须运行系统更新。我的版本还很落后,我出现了这个 bug。所以,如果你无法在 Windows 上安装 Docker,只要知道并不是只有你一个。仔细检查该 bug 以找到解决方案。

一旦你在 Windows 上安装 Docker 后,你可以通过 WSL 使用 bash shell,或者使用 PowerShell 来运行 Docker 命令。让我们在 PowerShell 中测试 “Hello World” 命令:

PS C:\Users\swapnil> docker run hello-world

恭喜,你已经在 Windows 上运行了 Docker。

在下一篇文章中,我们将讨论如何从 DockerHub 中拉取镜像并在我们的系统上运行容器。我们还会讨论推送我们自己的容器到 Docker Hub。


via: https://www.linux.com/blog/learn/intro-to-linux/how-install-docker-ce-your-desktop

作者:SWAPNIL BHARTIYA 选题:lujun9972 译者:MjSeven 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

cp 和 mv 之外,在 Linux 上有更多的复制和重命名文件的命令。试试这些命令或许会惊艳到你,并能节省一些时间。

Linux 用户数十年来一直在使用简单的 cpmv 命令来复制和重命名文件。这些命令是我们大多数人首先学到的,每天可能有数百万人在使用它们。但是还有其他技术、方便的方法和另外的命令,这些提供了一些独特的选项。

首先,我们来思考为什么你想要复制一个文件。你可能需要在另一个位置使用同一个文件,或者因为你要编辑该文件而需要一个副本,并且希望确保备有便利的备份以防万一需要恢复原始文件。这样做的显而易见的方式是使用像 cp myfile myfile-orig 这样的命令。

但是,如果你想复制大量的文件,那么这个策略可能就会变得很老。更好的选择是:

  • 在开始编辑之前,使用 tar 创建所有要备份的文件的存档。
  • 使用 for 循环来使备份副本更容易。

使用 tar 的方式很简单。对于当前目录中的所有文件,你可以使用如下命令:

$ tar cf myfiles.tar *

对于一组可以用模式标识的文件,可以使用如下命令:

$ tar cf myfiles.tar *.txt

在每种情况下,最终都会生成一个 myfiles.tar 文件,其中包含目录中的所有文件或扩展名为 .txt 的所有文件。

一个简单的循环将允许你使用修改后的名称来制作备份副本:

$ for file in *
> do
> cp $file $file-orig
> done

当你备份单个文件并且该文件恰好有一个长名称时,可以依靠使用 tab 来补全文件名(在输入足够的字母以便唯一标识该文件后点击 Tab 键)并使用像这样的语法将 -orig 附加到副本的名字后。

$ cp file-with-a-very-long-name{,-orig}

然后你有一个 file-with-a-very-long-name 和一个 file-with-a-very-long-name-orig

在 Linux 上重命名文件

重命名文件的传统方法是使用 mv 命令。该命令将文件移动到不同的目录,或原地更改其名称,或者同时执行这两个操作。

$ mv myfile /tmp
$ mv myfile notmyfile
$ mv myfile /tmp/notmyfile

但我们也有 rename 命令来做重命名。使用 rename 命令的窍门是习惯它的语法,但是如果你了解一些 Perl,你可能发现它并不棘手。

有个非常有用的例子。假设你想重新命名一个目录中的文件,将所有的大写字母替换为小写字母。一般来说,你在 Unix 或 Linux 系统上找不到大量大写字母的文件,但你可以有。这里有一个简单的方法来重命名它们,而不必为它们中的每一个使用 mv 命令。 /A-Z/a-z/ 告诉 rename 命令将范围 A-Z 中的任何字母更改为 a-z 中的相应字母。

$ ls
Agenda Group.JPG MyFile
$ rename 'y/A-Z/a-z/' *
$ ls
agenda group.jpg myfile

你也可以使用 rename 来删除文件扩展名。也许你厌倦了看到带有 .txt 扩展名的文本文件。简单删除这些扩展名 —— 用一个命令。

$ ls
agenda.txt notes.txt weekly.txt
$ rename 's/.txt//' *
$ ls
agenda notes weekly

现在让我们想象一下,你改变了心意,并希望把这些扩展名改回来。没问题。只需修改命令。窍门是理解第一个斜杠前的 s 意味着“替代”。前两个斜线之间的内容是我们想要改变的东西,第二个斜线和第三个斜线之间是改变后的东西。所以,$ 表示文件名的结尾,我们将它改为 .txt

$ ls
agenda notes weekly
$ rename 's/$/.txt/' *
$ ls
agenda.txt notes.txt weekly.txt

你也可以更改文件名的其他部分。牢记 s/旧内容/新内容/ 规则。

$ ls
draft-minutes-2018-03 draft-minutes-2018-04 draft-minutes-2018-05
$ rename 's/draft/approved/' *minutes*
$ ls
approved-minutes-2018-03 approved-minutes-2018-04 approved-minutes-2018-05

在上面的例子中注意到,当我们在 s/old/new/ 中使用 s 时,我们用另一个名称替换名称的一部分。当我们使用 y 时,我们就是直译(将字符从一个范围替换为另一个范围)。

总结

现在有很多复制和重命名文件的方法。我希望其中的一些会让你在使用命令行时更愉快。


via: https://www.networkworld.com/article/3276349/linux/copying-and-renaming-files-on-linux.html

作者:Sandra Henry-Stocker 选题:lujun9972 译者:geekpi 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

学习如何在 Linux 中使用 parted 命令来对存储设备分区。

在 Linux 中创建和删除分区是一种常见的操作,因为存储设备(如硬盘驱动器和 USB 驱动器)在使用之前必须以某种方式进行结构化。在大多数情况下,大型存储设备被分为称为 分区 partition 的独立部分。分区操作允许您将硬盘分割成独立的部分,每个部分都像是一个硬盘驱动器一样。如果您运行多个操作系统,那么分区是非常有用的。

在 Linux 中有许多强大的工具可以创建、删除和操作磁盘分区。在本文中,我将解释如何使用 parted 命令,这对于大型磁盘设备和许多磁盘分区尤其有用。parted 与更常见的 fdiskcfdisk 命令之间的区别包括:

  • GPT 格式:parted 命令可以创建全局惟一的标识符分区表 GPT,而 fdiskcfdisk 则仅限于 DOS 分区表。
  • 更大的磁盘: DOS 分区表可以格式化最多 2TB 的磁盘空间,尽管在某些情况下最多可以达到 16TB。然而,一个 GPT 分区表可以处理最多 8ZiB 的空间。
  • 更多的分区: 使用主分区和扩展分区,DOS 分区表只允许 16 个分区。在 GPT 中,默认情况下您可以得到 128 个分区,并且可以选择更多的分区。
  • 可靠性: 在 DOS 分区表中,只保存了一份分区表备份,在 GPT 中保留了两份分区表的备份(在磁盘的起始和结束部分),同时 GPT 还使用了 CRC 校验和来检查分区表的完整性,在 DOS 分区中并没有实现。

由于现在的磁盘更大,需要更灵活地使用它们,建议使用 parted 来处理磁盘分区。大多数时候,磁盘分区表是作为操作系统安装过程的一部分创建的。在向现有系统添加存储设备时,直接使用 parted 命令非常有用。

尝试一下 parted

下面解释了使用 parted 命令对存储设备进行分区的过程。为了尝试这些步骤,我强烈建议使用一块全新的存储设备或一种您不介意将其内容删除的设备。

1、列出分区

使用 parted -l 来标识你要进行分区的设备。一般来说,第一个硬盘 (/dev/sda/dev/vda )保存着操作系统, 因此要寻找另一个磁盘,以找到你想要分区的磁盘 (例如,/dev/sdb/dev/sdc/dev/vdb/dev/vdc 等)。

$ sudo parted -l
[sudo] password for daniel: 
Model: ATA RevuAhn_850X1TU5 (scsi)
Disk /dev/vdc: 512GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags: 

Number  Start   End    Size   Type     File system  Flags
 1      1049kB  525MB  524MB  primary  ext4         boot
 2      525MB   512GB  512GB  primary               lvm

2、打开存储设备

使用 parted 选中您要分区的设备。在这里例子中,是虚拟系统上的第三个磁盘(/dev/vdc)。指明你要使用哪一个设备非常重要。 如果你仅仅输入了 parted 命令而没有指定设备名字, 它会随机选择一个设备进行操作。

$ sudo parted /dev/vdc
GNU Parted 3.2
Using /dev/vdc
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted)

3、 设定分区表

设置分区表为 GPT ,然后输入 Yes 开始执行。

(parted) mklabel gpt 
Warning: the existing disk label on /dev/vdc will be destroyed 
and all data on this disk will be lost. Do you want to continue? 
Yes/No? Yes

mklabelmktable 命令用于相同的目的(在存储设备上创建分区表)。支持的分区表有:aix、amiga、bsd、dvh、gpt、mac、ms-dos、pc98、sun 和 loop。记住 mklabel 不会创建一个分区,而是创建一个分区表。

4、 检查分区表

查看存储设备信息:

(parted) print 
Model: Virtio Block Device (virtblk) 
Disk /dev/vdc: 1396MB 
Sector size (logical/physical): 512B/512B 
Partition Table: gpt 
Disk Flags: 
Number Start End Size File system Name Flags

5、 获取帮助

为了知道如何去创建一个新分区,输入: (parted) help mkpart

(parted) help mkpart 
  mkpart PART-TYPE [FS-TYPE] START END     make a partition

        PART-TYPE is one of: primary, logical, extended
        FS-TYPE is one of: btrfs, nilfs2, ext4, ext3, ext2, fat32, fat16, hfsx, hfs+, hfs, jfs, swsusp,
        linux-swap(v1), linux-swap(v0), ntfs, reiserfs, hp-ufs, sun-ufs, xfs, apfs2, apfs1, asfs, amufs5,
        amufs4, amufs3, amufs2, amufs1, amufs0, amufs, affs7, affs6, affs5, affs4, affs3, affs2, affs1,
        affs0, linux-swap, linux-swap(new), linux-swap(old)
        START and END are disk locations, such as 4GB or 10%.  Negative values count from the end of the
        disk.  For example, -1s specifies exactly the last sector.

        'mkpart' makes a partition without creating a new file system on the partition.  FS-TYPE may be
        specified to set an appropriate partition ID.

6、 创建分区

为了创建一个新分区(在这个例子中,分区 0 有 1396MB),输入下面的命令:

(parted) mkpart primary 0 1396MB 

Warning: The resulting partition is not properly aligned for best performance 
Ignore/Cancel? I 

(parted) print 
Model: Virtio Block Device (virtblk) 
Disk /dev/vdc: 1396MB 
Sector size (logical/physical): 512B/512B 
Partition Table: gpt 
Disk Flags: 
Number Start   End     Size    File system Name Flags 
1      17.4kB  1396MB  1396MB  primary

文件系统类型(fstype)并不是在 /dev/vdc1上创建 ext4 文件系统。 DOS 分区表的分区类型是 主分区 primary 逻辑分区 logical 扩展分区 extended 。 在 GPT 分区表中,分区类型用作分区名称。 在 GPT 下必须提供分区名称;在上例中,primary 是分区名称,而不是分区类型。

7、 保存退出

当你退出 parted 时,修改会自动保存。退出请输入如下命令:

(parted) quit
Information: You may need to update /etc/fstab.
$

谨记

当您添加新的存储设备时,请确保在开始更改其分区表之前确定正确的磁盘。如果您错误地更改了包含计算机操作系统的磁盘分区,会使您的系统无法启动。


via: https://opensource.com/article/18/6/how-partition-disk-linux

作者:Daniel Oh 选题:lujun9972 译者:amwps290 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

长文预警:在最新的 Linux 内核(>=4.4)中使用 eBPF,你可以将任何内核函数调用转换为一个带有任意数据的用户空间事件。这通过 bcc 来做很容易。这个探针是用 C 语言写的,而数据是由 Python 来处理的。

如果你对 eBPF 或者 Linux 跟踪不熟悉,那你应该好好阅读一下整篇文章。本文尝试逐步去解决我在使用 bcc/eBPF 时遇到的困难,以为您节省我在搜索和挖掘上花费的时间。

在 Linux 的世界中关于推送与轮询的一个看法

当我开始在容器上工作的时候,我想知道我们怎么基于一个真实的系统状态去动态更新一个负载均衡器的配置。一个通用的策略是这样做的,无论什么时候只要一个容器启动,容器编排器触发一个负载均衡配置更新动作,负载均衡器去轮询每个容器,直到它的健康状态检查结束。它也许只是简单进行 “SYN” 测试。

虽然这种配置方式可以有效工作,但是它的缺点是你的负载均衡器为了让一些系统变得可用需要等待,而不是 … 让负载去均衡。

可以做的更好吗?

当你希望在一个系统中让一个程序对一些变化做出反应,这里有两种可能的策略。程序可以去 轮询 系统去检测变化,或者,如果系统支持,系统可以 推送 事件并且让程序对它作出反应。你希望去使用推送还是轮询取决于上下文环境。一个好的经验法则是,基于处理时间的考虑,如果事件发生的频率较低时使用推送,而当事件发生的较快或者让系统变得不可用时切换为轮询。例如,一般情况下,网络驱动程序将等待来自网卡的事件,但是,像 dpdk 这样的框架对事件将主动轮询网卡,以达到高吞吐低延迟的目的。

理想状态下,我们将有一些内核接口告诉我们:

  • “容器管理器,你好,我刚才为容器 servestaticfiles 的 Nginx-ware 创建了一个套接字,或许你应该去更新你的状态?
  • “好的,操作系统,感谢你告诉我这个事件”

虽然 Linux 有大量的接口去处理事件,对于文件事件高达 3 个,但是没有专门的接口去得到套接字事件提示。你可以得到路由表事件、邻接表事件、连接跟踪事件、接口变化事件。唯独没有套接字事件。或者,也许它深深地隐藏在一个 Netlink 接口中。

理想情况下,我们需要一个做这件事的通用方法,怎么办呢?

内核跟踪和 eBPF,一些它们的历史

直到最近,内核跟踪的唯一方式是对内核上打补丁或者借助于 SystemTap。SytemTap 是一个 Linux 系统跟踪器。简单地说,它提供了一个 DSL,编译进内核模块,然后被内核加载运行。除了一些因安全原因禁用动态模块加载的生产系统之外,包括在那个时候我开发的那一个。另外的方式是为内核打一个补丁程序以触发一些事件,可能是基于 netlink。但是这很不方便。深入内核所带来的缺点包括 “有趣的” 新 “特性” ,并增加了维护负担。

从 Linux 3.15 开始给我们带来了希望,它支持将任何可跟踪内核函数可安全转换为用户空间事件。在一般的计算机科学中,“安全” 是指 “某些虚拟机”。在此也不例外。自从 Linux 2.1.75 在 1997 年正式发行以来,Linux 已经有这个多好年了。但是,它被称为伯克利包过滤器,或简称 BPF。正如它的名字所表达的那样,它最初是为 BSD 防火墙开发的。它仅有两个寄存器,并且它仅允许向前跳转,这意味着你不能使用它写一个循环(好吧,如果你知道最大迭代次数并且去手工实现它,你也可以实现循环)。这一点保证了程序总会终止,而不会使系统处于挂起的状态。还不知道它有什么用?你用过 iptables 的话,其作用就是 CloudFlare 的 DDos 防护的基础

好的,因此,随着 Linux 3.15,BPF 被扩展 成为了 eBPF。对于 “扩展的” BPF。它从两个 32 位寄存器升级到 10 个 64 位寄存器,并且增加了它们之间向后跳转的特性。然后它 在 Linux 3.18 中被进一步扩展,并将被移出网络子系统中,并且增加了像映射(map)这样的工具。为保证安全,它 引进了一个检查器,它验证所有的内存访问和可能的代码路径。如果检查器不能保证代码会终止在固定的边界内,它一开始就要拒绝程序的插入。

关于它的更多历史,可以看 Oracle 的关于 eBPF 的一个很棒的演讲

让我们开始吧!

来自 inet\_listen 的问候

因为写一个汇编程序并不是件十分容易的任务,甚至对于很优秀的我们来说,我将使用 bcc。bcc 是一个基于 LLVM 的工具集,并且用 Python 抽象了底层机制。探针是用 C 写的,并且返回的结果可以被 Python 利用,可以很容易地写一些不算简单的应用程序。

首先安装 bcc。对于一些示例,你可能会需要使用一个最新的内核版本(>= 4.4)。如果你想亲自去尝试一下这些示例,我强烈推荐你安装一台虚拟机, 而不是 一个 Docker 容器。你不能在一个容器中改变内核。作为一个非常新的很活跃的项目,其安装教程高度依赖于平台/版本。你可以在 https://github.com/iovisor/bcc/blob/master/INSTALL.md 上找到最新的教程。

现在,我希望不管在什么时候,只要有任何程序开始监听 TCP 套接字,我将得到一个事件。当我在一个 AF_INET + SOCK_STREAM 套接字上调用一个 listen() 系统调用时,其底层的内核函数是 inet_listen。我将从钩在一个“Hello World” kprobe 的入口上开始。

from bcc import BPF

# Hello BPF Program
bpf_text = """ 
#include <net/inet_sock.h>
#include <bcc/proto.h>

// 1. Attach kprobe to "inet_listen"
int kprobe__inet_listen(struct pt_regs *ctx, struct socket *sock, int backlog)
{
    bpf_trace_printk("Hello World!\\n");
    return 0;
};
"""

# 2. Build and Inject program
b = BPF(text=bpf_text)

# 3. Print debug output
while True:
    print b.trace_readline()

这个程序做了三件事件:

  1. 它通过命名惯例来附加到一个内核探针上。如果函数被调用,比如说 my_probe 函数,它会使用 b.attach_kprobe("inet_listen", "my_probe") 显式附加。
  2. 它使用 LLVM 新的 BPF 后端来构建程序。使用(新的) bpf() 系统调用去注入结果字节码,并且按匹配的命名惯例自动附加探针。
  3. 从内核管道读取原生输出。

注意:eBPF 的后端 LLVM 还很新。如果你认为你遇到了一个 bug,你也许应该去升级。

注意到 bpf_trace_printk 调用了吗?这是一个内核的 printk() 精简版的调试函数。使用时,它产生跟踪信息到一个专门的内核管道 /sys/kernel/debug/tracing/trace_pipe 。就像名字所暗示的那样,这是一个管道。如果多个读取者在读取它,仅有一个将得到一个给定的行。对生产系统来说,这样是不合适的。

幸运的是,Linux 3.19 引入了对消息传递的映射,以及 Linux 4.4 带来了对任意 perf 事件的支持。在这篇文章的后面部分,我将演示基于 perf 事件的方式。

# From a first console
ubuntu@bcc:~/dev/listen-evts$ sudo /python tcv4listen.py 
              nc-4940  [000] d... 22666.991714: : Hello World!

# From a second console
ubuntu@bcc:~$ nc -l 0 4242
^C

搞定!

抓取 backlog

现在,让我们输出一些很容易访问到的数据,比如说 “backlog”。backlog 是正在建立 TCP 连接的、即将被 accept() 的连接的数量。

只要稍微调整一下 bpf_trace_printk

bpf_trace_printk("Listening with with up to %d pending connections!\\n", backlog);

如果你用这个 “革命性” 的改善重新运行这个示例,你将看到如下的内容:

(bcc)ubuntu@bcc:~/dev/listen-evts$ sudo python tcv4listen.py 
              nc-5020  [000] d... 25497.154070: : Listening with with up to 1 pending connections!

nc 是个单连接程序,因此,其 backlog 是 1。而 Nginx 或者 Redis 上的 backlog 将在这里输出 128 。但是,那是另外一件事。

简单吧?现在让我们获取它的端口。

抓取端口和 IP

正在研究的 inet_listen 来源于内核,我们知道它需要从 socket 对象中取得 inet_sock。只需要从源头拷贝,然后插入到跟踪器的开始处:

// cast types. Intermediate cast not needed, kept for readability
struct sock *sk = sock->sk;
struct inet_sock *inet = inet_sk(sk);

端口现在可以按网络字节顺序(就是“从小到大、大端”的顺序)从 inet->inet_sport 访问到。很容易吧!因此,我们只需要把 bpf_trace_printk 替换为:

bpf_trace_printk("Listening on port %d!\\n", inet->inet_sport);

然后运行:

ubuntu@bcc:~/dev/listen-evts$ sudo /python tcv4listen.py 
...
R1 invalid mem access 'inv'
...
Exception: Failed to load BPF program kprobe__inet_listen

抛出的异常并没有那么简单,Bcc 现在提升了 许多。直到写这篇文章的时候,有几个问题已经被处理了,但是并没有全部处理完。这个错误意味着内核检查器可以证实程序中的内存访问是正确的。看这个显式的类型转换。我们需要一点帮助,以使访问更加明确。我们将使用 bpf_probe_read 可信任的函数去读取一个任意内存位置,同时确保所有必要的检查都是用类似这样方法完成的:

// Explicit initialization. The "=0" part is needed to "give life" to the variable on the stack
u16 lport = 0;

// Explicit arbitrary memory access. Read it:
//    Read into 'lport', 'sizeof(lport)' bytes from 'inet->inet_sport' memory location
bpf_probe_read(&lport, sizeof(lport), &(inet->inet_sport));

读取 IPv4 边界地址和它基本上是相同的,使用 inet->inet_rcv_saddr 。如果我把这些一起放上去,我们将得到 backlog、端口和边界 IP:

from bcc import BPF  

# BPF Program  
bpf_text = """   
#include <net/sock.h>  
#include <net/inet_sock.h>  
#include <bcc/proto.h>  

// Send an event for each IPv4 listen with PID, bound address and port  
int kprobe__inet_listen(struct pt_regs *ctx, struct socket *sock, int backlog)  
{  
    // Cast types. Intermediate cast not needed, kept for readability  
    struct sock *sk = sock->sk;  
    struct inet_sock *inet = inet_sk(sk);  

    // Working values. You *need* to initialize them to give them "life" on the stack and use them afterward  
    u32 laddr = 0;  
    u16 lport = 0;  

    // Pull in details. As 'inet_sk' is internally a type cast, we need to use 'bpf_probe_read'  
    // read: load into 'laddr' 'sizeof(laddr)' bytes from address 'inet->inet_rcv_saddr'  
    bpf_probe_read(&laddr, sizeof(laddr), &(inet->inet_rcv_saddr));  
    bpf_probe_read(&lport, sizeof(lport), &(inet->inet_sport));  

    // Push event
    bpf_trace_printk("Listening on %x %d with %d pending connections\\n", ntohl(laddr), ntohs(lport), backlog);  
    return 0;
};  
"""  

# Build and Inject BPF  
b = BPF(text=bpf_text)  

# Print debug output  
while True:  
  print b.trace_readline()

测试运行输出的内容像下面这样:

(bcc)ubuntu@bcc:~/dev/listen-evts$ sudo python tcv4listen.py 
              nc-5024  [000] d... 25821.166286: : Listening on 7f000001 4242 with 1 pending connections

这证明你的监听是在本地主机上的。因为没有处理为友好的输出,这里的地址以 16 进制的方式显示,但是这是没错的,并且它很酷。

注意:你可能想知道为什么 ntohsntohl 可以从 BPF 中被调用,即便它们并不可信。这是因为它们是宏,并且是从 “.h” 文件中来的内联函数,并且,在写这篇文章的时候一个小的 bug 已经 修复了

全部达成了,还剩下一些:我们希望获取相关的容器。在一个网络环境中,那意味着我们希望取得网络的命名空间。网络命名空间是一个容器的构建块,它允许它们拥有独立的网络。

抓取网络命名空间:被迫引入的 perf 事件

在用户空间中,网络命名空间可以通过检查 /proc/PID/ns/net 的目标来确定,它将看起来像 net:[4026531957] 这样。方括号中的数字是网络空间的 inode 编号。这就是说,我们可以通过 /proc 来取得,但是这并不是好的方式,我们或许可以临时处理时用一下。我们可以从内核中直接抓取 inode 编号。幸运的是,那样做很容易:

// Create an populate the variable
u32 netns = 0;

// Read the netns inode number, like /proc does
netns = sk->__sk_common.skc_net.net->ns.inum;

很容易!而且它做到了。

但是,如果你看到这里,你可能猜到那里有一些错误。它在:

bpf_trace_printk("Listening on %x %d with %d pending connections in container %d\\n", ntohl(laddr), ntohs(lport), backlog, netns);

如果你尝试去运行它,你将看到一些令人难解的错误信息:

(bcc)ubuntu@bcc:~/dev/listen-evts$ sudo python tcv4listen.py
error: in function kprobe__inet_listen i32 (%struct.pt_regs*, %struct.socket*, i32)
too many args to 0x1ba9108: i64 = Constant<6>

clang 想尝试去告诉你的是 “嗨,哥们,bpf_trace_printk 只能带四个参数,你刚才给它传递了 5 个”。在这里我不打算继续追究细节了,但是,那是 BPF 的一个限制。如果你想继续去深入研究,这里是一个很好的起点

去修复它的唯一方式是 … 停止调试并且准备投入使用。因此,让我们开始吧(确保运行在内核版本为 4.4 的 Linux 系统上)。我将使用 perf 事件,它支持传递任意大小的结构体到用户空间。另外,只有我们的读者可以获得它,因此,多个没有关系的 eBPF 程序可以并发产生数据而不会出现问题。

去使用它吧,我们需要:

  1. 定义一个结构体
  2. 声明事件
  3. 推送事件
  4. 在 Python 端重新声明事件(这一步以后将不再需要)
  5. 处理和格式化事件

这看起来似乎很多,其它并不多,看下面示例:

// At the begining of the C program, declare our event
struct listen_evt_t {
    u64 laddr;
    u64 lport;
    u64 netns;
    u64 backlog;
};
BPF_PERF_OUTPUT(listen_evt);

// In kprobe__inet_listen, replace the printk with
struct listen_evt_t evt = {
    .laddr = ntohl(laddr),
    .lport = ntohs(lport),
    .netns = netns,
    .backlog = backlog,
};
listen_evt.perf_submit(ctx, &evt, sizeof(evt));

Python 端将需要一点更多的工作:

# We need ctypes to parse the event structure
import ctypes

# Declare data format
class ListenEvt(ctypes.Structure):
    _fields_ = [
        ("laddr",   ctypes.c_ulonglong),
        ("lport",   ctypes.c_ulonglong),
        ("netns",   ctypes.c_ulonglong),
        ("backlog", ctypes.c_ulonglong),
    ]

# Declare event printer
def print_event(cpu, data, size):
    event = ctypes.cast(data, ctypes.POINTER(ListenEvt)).contents
    print("Listening on %x %d with %d pending connections in container %d" % (
        event.laddr,
        event.lport,
        event.backlog,
        event.netns,
    ))

# Replace the event loop
b["listen_evt"].open_perf_buffer(print_event)
while True:
    b.kprobe_poll()

来试一下吧。在这个示例中,我有一个 redis 运行在一个 Docker 容器中,并且 nc 运行在主机上:

(bcc)ubuntu@bcc:~/dev/listen-evts$ sudo python tcv4listen.py
Listening on 0 6379 with 128 pending connections in container 4026532165
Listening on 0 6379 with 128 pending connections in container 4026532165
Listening on 7f000001 6588 with 1 pending connections in container 4026531957

结束语

现在,所有事情都可以在内核中使用 eBPF 将任何函数的调用设置为触发事件,并且你看到了我在学习 eBPF 时所遇到的大多数的问题。如果你希望去看这个工具的完整版本,像 IPv6 支持这样的一些技巧,看一看 https://github.com/iovisor/bcc/blob/master/tools/solisten.py。它现在是一个官方的工具,感谢 bcc 团队的支持。

更进一步地去学习,你可能需要去关注 Brendan Gregg 的博客,尤其是 关于 eBPF 映射和统计的文章。他是这个项目的主要贡献人之一。


via: https://blog.yadutaf.fr/2016/03/30/turn-any-syscall-into-event-introducing-ebpf-kernel-probes/

作者:Jean-Tiare Le Bigot 译者:qhwdw 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

我们已经学会了如何为多个平台构建包,以及如何从源代码构建包。 今天,我们将学习如何将 DEB 包转换为 Arch Linux 包。 您可能会问,AUR 是这个星球上的大型软件存储库,几乎所有的软件都可以在其中使用。 为什么我需要将 DEB 软件包转换为 Arch Linux 软件包? 这的确没错! 但是,由于某些软件包无法编译(封闭源代码软件包),或者由于各种原因(如编译时出错或文件不可用)而无法从 AUR 生成。 或者,开发人员懒得在 AUR 中构建一个包,或者他/她不想创建 AUR 包。 在这种情况下,我们可以使用这种快速但有点粗糙的方法将 DEB 包转换成 Arch Linux 包。

Debtap - 将 DEB 包转换成 Arch Linux 包

为此,我们将使用名为 “Debtap” 的实用程序。 它代表了 DEB T o A rch (Linux) P ackage。 Debtap 在 AUR 中可以使用,因此您可以使用 AUR 辅助工具(如 PacaurPackerYaourt )来安装它。

使用 pacaur 安装 debtap 运行:

pacaur -S debtap

使用 Packer 安装:

packer -S debtap

使用 Yaourt 安装:

yaourt -S debtap

同时,你的 Arch 系统也应该已经安装好了 bashbinutilspkgfilefakeroot 包。

在安装 Debtap 和所有上述依赖关系之后,运行以下命令来创建/更新 pkgfile 和 debtap 数据库。

sudo debtap -u

示例输出是:

==> Synchronizing pkgfile database...
:: Updating 6 repos...
 download complete: archlinuxfr [ 151.7 KiB 67.5K/s 5 remaining]
 download complete: multilib [ 319.5 KiB 36.2K/s 4 remaining]
 download complete: core [ 707.7 KiB 49.5K/s 3 remaining]
 download complete: testing [ 1716.3 KiB 58.2K/s 2 remaining]
 download complete: extra [ 7.4 MiB 109K/s 1 remaining]
 download complete: community [ 16.9 MiB 131K/s 0 remaining]
:: download complete in 131.47s < 27.1 MiB 211K/s 6 files >
:: waiting for 1 process to finish repacking repos...
==> Synchronizing debtap database...
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 34.1M 100 34.1M 0 0 206k 0 0:02:49 0:02:49 --:--:-- 180k
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 814k 100 814k 0 0 101k 0 0:00:08 0:00:08 --:--:-- 113k
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 120k 100 120k 0 0 61575 0 0:00:02 0:00:02 --:--:-- 52381
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 35.4M 100 35.4M 0 0 175k 0 0:03:27 0:03:27 --:--:-- 257k
==> Downloading latest virtual packages list...
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 149 0 149 0 0 49 0 --:--:-- 0:00:03 --:--:-- 44
100 11890 0 11890 0 0 2378 0 --:--:-- 0:00:05 --:--:-- 8456
==> Downloading latest AUR packages list...
 % Total % Received % Xferd Average Speed Time Time Time Current
 Dload Upload Total Spent Left Speed
100 264k 0 264k 0 0 30128 0 --:--:-- 0:00:09 --:--:-- 74410
==> Generating base group packages list...
==> All steps successfully completed!

你至少需要运行上述命令一次。

现在是时候开始转换包了。

比如说要使用 debtap 转换包 Quadrapassel,你可以这样做:

debtap quadrapassel_3.22.0-1.1_arm64.deb

上述的命令会将 DEB 包文件转换为 Arch Linux 包。你需要输入包的维护者和许可证,输入他们,然后按下回车键就可以开始转换了。

包转换的过程可能依赖于你的 CPU 的速度从几秒到几分钟不等。喝一杯咖啡等一等。

示例输出:

==> Extracting package data...
==> Fixing possible directories structure differencies...
==> Generating .PKGINFO file...

:: Enter Packager name:
quadrapassel

:: Enter package license (you can enter multiple licenses comma separated):
GPL

*** Creation of .PKGINFO file in progress. It may take a few minutes, please wait...

Warning: These dependencies (depend = fields) could not be translated into Arch Linux packages names:
gsettings-backend

==> Checking and generating .INSTALL file (if necessary)...

:: If you want to edit .PKGINFO and .INSTALL files (in this order), press (1) For vi (2) For nano (3) For default editor (4) For a custom editor or any other key to continue:

==> Generating .MTREE file...

==> Creating final package...
==> Package successfully created!
==> Removing leftover files...

:Quadrapassel 在 Arch Linux 官方的软件库中早已可用,我只是用它来说明一下。

如果在包转化的过程中,你不想回答任何问题,使用 -q 略过除了编辑元数据之外的所有问题。

debtap -q quadrapassel_3.22.0-1.1_arm64.deb

为了略过所有的问题(不推荐),使用 -Q

debtap -Q quadrapassel_3.22.0-1.1_arm64.deb

转换完成后,您可以使用 pacman 在 Arch 系统中安装新转换的软件包,如下所示。

sudo pacman -U <package-name>

显示帮助文档,使用 -h

$ debtap -h
Syntax: debtap [options] package_filename

Options:

 -h --h -help --help Prints this help message
 -u --u -update --update Update debtap database
 -q --q -quiet --quiet Bypass all questions, except for editing metadata file(s)
 -Q --Q -Quiet --Quiet Bypass all questions (not recommended)
 -s --s -pseudo --pseudo Create a pseudo-64-bit package from a 32-bit .deb package
 -w --w -wipeout --wipeout Wipeout versions from all dependencies, conflicts etc.
 -p --p -pkgbuild --pkgbuild Additionally generate a PKGBUILD file
 -P --P -Pkgbuild --Pkgbuild Generate a PKGBUILD file only

这就是现在要讲的。希望这个工具有所帮助。如果你发现我们的指南有用,请花一点时间在你的社交、专业网络分享并支持我们!

更多的好东西来了。请继续关注!

干杯!


via: https://www.ostechnix.com/convert-deb-packages-arch-linux-packages/

作者:SK 译者:amwps290 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出

利用这 8 个命令可以学习 Docker 容器的基本管理方式。这是一个为 Docker 初学者准备的,带有示范命令输出的指南。

Docker 容器管理命令

在这篇文章中,我们将带你学习 8 个基本的 Docker 容器命令,它们操控着 Docker 容器的基本活动,例如 运行 run 列举 list 停止 stop 、 查看 历史纪录 logs 删除 delete 等等。如果你对 Docker 的概念很陌生,推荐你看看我们的 介绍指南,来了解 Docker 的基本内容以及 如何 在 Linux 上安装 Docker。 现在让我们赶快进入要了解的命令:

如何运行 Docker 容器?

众所周知,Docker 容器只是一个运行于 宿主操作系统 host OS 上的应用进程,所以你需要一个镜像来运行它。Docker 镜像以进程的方式运行时就叫做 Docker 容器。你可以加载本地 Docker 镜像,也可以从 Docker Hub 上下载。Docker Hub 是一个提供公有和私有镜像来进行 拉取 pull 操作的集中仓库。官方的 Docker Hub 位于 hub.docker.com。 当你指示 Docker 引擎运行容器时,它会首先搜索本地镜像,如果没有找到,它会从 Docker Hub 上拉取相应的镜像。

让我们运行一个 Apache web 服务器的 Docker 镜像,比如 httpd 进程。你需要运行 docker container run 命令。旧的命令为 docker run, 但后来 Docker 添加了子命令部分,所以新版本支持下列命令:

root@kerneltalks # docker container run -d -p 80:80 httpd
Unable to find image 'httpd:latest' locally
latest: Pulling from library/httpd
3d77ce4481b1: Pull complete
73674f4d9403: Pull complete
d266646f40bd: Pull complete
ce7b0dda0c9f: Pull complete
01729050d692: Pull complete
014246127c67: Pull complete
7cd2e04cf570: Pull complete
Digest: sha256:f4610c3a1a7da35072870625733fd0384515f7e912c6223d4a48c6eb749a8617
Status: Downloaded newer image for httpd:latest
c46f2e9e4690f5c28ee7ad508559ceee0160ac3e2b1688a61561ce9f7d99d682

Docker 的 run 命令将镜像名作为强制参数,另外还有很多可选参数。常用的参数有:

  • -d:从当前 shell 脱离容器
  • -p X:Y:绑定容器的端口 Y 到宿主机的端口 X
  • --name:命名你的容器。如果未指定,它将被赋予随机生成的名字
  • -e:当启动容器时传递环境编辑及其值

通过以上输出你可以看到,我们将 httpd 作为镜像名来运行容器。接着,本地镜像没有找到,Docker 引擎从 Docker Hub 拉取了它。注意,它下载了镜像 httpd:latest, 其中 : 后面跟着版本号。如果你需要运行特定版本的容器,你可以在镜像名后面注明版本名。如果不提供版本名,Docker 引擎会自动拉取最新的版本。

输出的最后一行显示了你新运行的 httpd 容器的唯一 ID。

如何列出所有运行中的 Docker 容器?

现在,你的容器已经运行起来了,你可能想要确认这一点,或者你想要列出你的机器上运行的所有容器。你可以使用 docker container ls 命令。在旧的 Docker 版本中,对应的命令为 docker ps

root@kerneltalks # docker container ls
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                NAMES
c46f2e9e4690        httpd               "httpd-foreground"   11 minutes ago      Up 11 minutes       0.0.0.0:80->80/tcp   cranky_cori

列出的结果是按列显示的。每一列的值分别为:

  1. Container ID :一开始的几个字符对应你的容器的唯一 ID
  2. Image :你运行容器的镜像名
  3. Command :容器启动后运行的命令
  4. Created :创建时间
  5. Status :容器当前状态
  6. Ports :与宿主端口相连接的端口信息
  7. Names :容器名(如果你没有命名你的容器,那么会随机创建)

如何查看 Docker 容器的历史纪录?

在第一步我们使用了 -d 参数来将容器,在它一开始运行的时候,就从当前的 shell 中脱离出来。在这种情况下,我们不知道容器里面发生了什么。所以为了查看容器的历史纪录,Docker 提供了 logs 命令。它采用容器名称或 ID 作为参数。

root@kerneltalks # docker container logs cranky_cori
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.2. Set the 'ServerName' directive globally to suppress this message
[Thu May 31 18:35:07.301158 2018] [mpm_event:notice] [pid 1:tid 139734285989760] AH00489: Apache/2.4.33 (Unix) configured -- resuming normal operations
[Thu May 31 18:35:07.305153 2018] [core:notice] [pid 1:tid 139734285989760] AH00094: Command line: 'httpd -D FOREGROUND'

这里我使用了容器名称作为参数。你可以看到在我们的 httpd 容器中与 Apache 相关的历史纪录。

如何确定 Docker 容器的进程?

容器是一个使用宿主资源来运行的进程。这样,你可以在宿主系统的进程表中定位容器的进程。让我们在宿主系统上确定容器进程。

Docker 使用著名的 top 命令作为子命令的名称,来查看容器产生的进程。它采用容器的名称或 ID 作为参数。在旧版本的 Docker 中,只可运行 docker top 命令。在新版本中,docker topdocker container top 命令都可以生效。

root@kerneltalks # docker container top  cranky_cori
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                15702               15690               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND
bin                 15729               15702               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND
bin                 15730               15702               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND
bin                 15731               15702               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND

root@kerneltalks # ps -ef |grep -i 15702
root     15702 15690  0 18:35 ?        00:00:00 httpd -DFOREGROUND
bin      15729 15702  0 18:35 ?        00:00:00 httpd -DFOREGROUND
bin      15730 15702  0 18:35 ?        00:00:00 httpd -DFOREGROUND
bin      15731 15702  0 18:35 ?        00:00:00 httpd -DFOREGROUND
root     15993 15957  0 18:59 pts/0    00:00:00 grep --color=auto -i 15702

在第一个输出中,列出了容器产生的进程的列表。它包含了所有细节,包括 用户号 uid 进程号 pid 父进程号 ppid 、开始时间、命令,等等。这里所有的进程号你都可以在宿主的进程表里搜索到。这就是我们在第二个命令里做得。这证明了容器确实是宿主系统中的进程。

如何停止 Docker 容器?

只需要 stop 命令!同样,它采用容器名称或 ID 作为参数。

root@kerneltalks # docker container stop cranky_cori
cranky_cori

如何列出停止的或不活动的 Docker 容器?

现在我们停止了我们的容器,这时如果我们使用 ls 命令,它将不会出现在列表中。

root@kerneltalks # docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

所以,在这种情况下,如果想要查看停止的或不活动的容器,你需要在 ls 命令里同时使用 -a 参数。

root@kerneltalks # docker container ls -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS                     PORTS               NAMES
c46f2e9e4690        httpd               "httpd-foreground"   33 minutes ago      Exited (0) 2 minutes ago                       cranky_cori

有了 -a 参数,现在我们可以查看已停止的容器。注意这些容器的状态被标注为 已退出 exited 。既然容器只是一个进程,那么用“退出”比“停止”更合适!

如何(重新)启动 Docker 容器?

现在,我们来启动这个已停止的容器。这和运行一个容器有所区别。当你运行一个容器时,你将启动一个全新的容器。当你启动一个容器时,你将开始一个已经停止并保存了当时运行状态的容器。它将以停止时的状态重新开始运行。

root@kerneltalks #  docker container start c46f2e9e4690
c46f2e9e4690

root@kerneltalks # docker container ls -a
CONTAINER ID        IMAGE               COMMAND              CREATED             STATUS              PORTS                NAMES
c46f2e9e4690        httpd               "httpd-foreground"   35 minutes ago      Up 8 seconds        0.0.0.0:80->80/tcp   cranky_cori

如何移除 Docker 容器?

我们使用 rm 命令来移除容器。你不可以移除运行中的容器。移除之前需要先停止容器。你可以使用 -f 参数搭配 rm 命令来强制移除容器,但并不推荐这么做。

root@kerneltalks # docker container rm cranky_cori
cranky_cori
root@kerneltalks # docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

你看,一旦移除了容器,即使再使用 ls -a 命令也查看不到容器了。


via: https://kerneltalks.com/virtualization/8-basic-docker-container-management-commands/

作者:Shrikant Lavhate 选题:lujun9972 译者:lonaparte 校对:wxy

本文由 LCTT 原创编译,Linux中国 荣誉推出