diff options
-rw-r--r-- | cmd/podman/pods/logs.go | 31 | ||||
-rw-r--r-- | docs/tutorials/podman_tutorial_cn.md | 173 | ||||
-rwxr-xr-x | hack/xref-helpmsgs-manpages | 7 | ||||
-rw-r--r-- | libpod/define/container_inspect.go | 10 | ||||
-rw-r--r-- | libpod/networking_linux.go | 4 | ||||
-rw-r--r-- | libpod/networking_linux_test.go | 217 |
6 files changed, 412 insertions, 30 deletions
diff --git a/cmd/podman/pods/logs.go b/cmd/podman/pods/logs.go index fe5205669..a79c0430f 100644 --- a/cmd/podman/pods/logs.go +++ b/cmd/podman/pods/logs.go @@ -27,11 +27,11 @@ type logsOptionsWrapper struct { var ( logsPodOptions logsOptionsWrapper logsPodDescription = `Displays logs for pod with one or more containers.` - logsPodCommand = &cobra.Command{ + podLogsCommand = &cobra.Command{ Use: "logs [options] POD", Short: "Fetch logs for pod with one or more containers", Long: logsPodDescription, - // We dont want users to invoke latest and pod togather + // We dont want users to invoke latest and pod together Args: func(cmd *cobra.Command, args []string) error { switch { case registry.IsRemote() && logsPodOptions.Latest: @@ -53,35 +53,16 @@ var ( podman pod logs --follow=true --since 10m podID podman pod logs mywebserver`, } - - containerLogsCommand = &cobra.Command{ - Use: logsPodCommand.Use, - Short: logsPodCommand.Short, - Long: logsPodCommand.Long, - Args: logsPodCommand.Args, - RunE: logsPodCommand.RunE, - ValidArgsFunction: logsPodCommand.ValidArgsFunction, - Example: `podman pod logs podId - podman pod logs -c ctrname podName - podman pod logs --tail 2 mywebserver - podman pod logs --follow=true --since 10m podID`, - } ) func init() { + // pod logs registry.Commands = append(registry.Commands, registry.CliCommand{ - Command: logsPodCommand, - }) - logsFlags(logsPodCommand) - validate.AddLatestFlag(logsPodCommand, &logsPodOptions.Latest) - - // container logs - registry.Commands = append(registry.Commands, registry.CliCommand{ - Command: containerLogsCommand, + Command: podLogsCommand, Parent: podCmd, }) - logsFlags(containerLogsCommand) - validate.AddLatestFlag(containerLogsCommand, &logsPodOptions.Latest) + logsFlags(podLogsCommand) + validate.AddLatestFlag(podLogsCommand, &logsPodOptions.Latest) } func logsFlags(cmd *cobra.Command) { diff --git a/docs/tutorials/podman_tutorial_cn.md b/docs/tutorials/podman_tutorial_cn.md new file mode 100644 index 000000000..5290c4076 --- /dev/null +++ b/docs/tutorials/podman_tutorial_cn.md @@ -0,0 +1,173 @@ +> - 译文出自:[掘金翻译计划](https://juejin.cn/translate) + +![PODMAN logo](../../logo/podman-logo-source.svg) + +Podman是由libpod库提供一个实用的程序,可以被用于创建和管理容器。 + +下面的教程会教你如何启动 Podman 并使用 Podman 执行一些基本的命令。 + +如果你正在使用 Mac 或者 Windows +,你应该先查看[Mac 和 Windows 使用说明](https://github.com/containers/podman/blob/master/docs/tutorials/mac_win_client.md)来设置 Podman +远程客户端。 + +**注意**:示例中所有命令皆以非 root 的用户运行,必要的时候通过 `sudo` 命令来获取 root 权限。 + +## 安装Podman + +安装或者编译 Podman ,请参照[安装说明](https://github.com/containers/podman/blob/master/install.md)。 + +## 熟悉podman + +### 运行一个示例容器 + +这个示例容器会运行一个简单的只有主页的 httpd 服务器。 + +```console +podman run -dt -p 8080:8080/tcp -e HTTPD_VAR_RUN=/run/httpd -e HTTPD_MAIN_CONF_D_PATH=/etc/httpd/conf.d \ + -e HTTPD_MAIN_CONF_PATH=/etc/httpd/conf \ + -e HTTPD_CONTAINER_SCRIPTS_PATH=/usr/share/container-scripts/httpd/ \ + registry.fedoraproject.org/f29/httpd /usr/bin/run-httpd +``` + +因为命令中的 *-d* 参数表明容器以 "detached" 模式运行,所以 Podman 会在容器运行后打印容器的 ID。 + +注意为了访问这个 HTTP 服务器,我们将使用端口转发。成功运行需要 slirp4netns 的 v0.3.0+ 版本。 + +Podman 的 *ps* 命令用于列出正在创建和运行的容器。 + +```console +podman ps +``` + +**注意**:如果为 *ps* 命令添加 *-a* 参数,Podman 将展示所有的容器。 + +### 查看正在运行的容器 + +你可以 "inspect" (查看)一个正在运行的容器的元数据以及其他详细信息。我们甚至可以使用 inspect 的子命令查看分配给容器的 IP 地址。由于容器以非 root 模式运行,没有分配 IP 地址,inspect 的输出会是 " +none" 。 + +```console +podman inspect -l | grep IPAddress\": + "SecondaryIPAddresses": null, + "IPAddress": "", +``` + +**注意**:*-l* 参数是**最近的容器**的指代,你也可以使用容器的ID 代替 *-l* + +### 测试httpd服务器 + +由于我们没有容器的 IP 地址,我们可以使用 curl 测试主机和容器之间的网络通信。下面的命令应该显示我们的容器化 httpd 服务器 的主页。 + +```console +curl http://localhost:8080 +``` + +### 查看容器的日志 + +你也可以使用 podman 查看容器的日志 + +```console +podman logs --latest +10.88.0.1 - - [07/Feb/2018:15:22:11 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.55.1" "-" +10.88.0.1 - - [07/Feb/2018:15:22:30 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.55.1" "-" +10.88.0.1 - - [07/Feb/2018:15:22:30 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.55.1" "-" +10.88.0.1 - - [07/Feb/2018:15:22:31 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.55.1" "-" +10.88.0.1 - - [07/Feb/2018:15:22:31 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.55.1" "-" +``` + +### 查看容器中的进程pid + +你可以使用 *top* 命令查看容器中 httpd 的 pid + +```console +podman top <container_id> + UID PID PPID C STIME TTY TIME CMD + 0 31873 31863 0 09:21 ? 00:00:00 nginx: master process nginx -g daemon off; + 101 31889 31873 0 09:21 ? 00:00:00 nginx: worker process +``` + +### 设置容器的检查点 + +设置检查点会在停止容器的同时把容器中所有进程的状态写入磁盘。 + +有了它,容器后续可以被恢复,并在与检查点完全相同的时间点继续运行。 这个功能需要在系统上安装 CRIU 的 3.11+ 版本。 + +这个功能不支持非 root 模式,因此,如果你想尝试使用它,你需要使用 sudo 方式重新创建容器。 + +设置容器检查点请使用: + +```console +sudo podman container checkpoint <container_id> +``` + +### 恢复容器 + +恢复容器只能在以前设置过检查点的容器上使用。恢复的容器会在与设置检查点时完全相同的时间点继续运行。 + +恢复容器请使用: + +```console +sudo podman container restore <container_id> +``` + +恢复之后。容器会像设置检查点之前一样回复请求 + +```console +curl http://<IP_address>:8080 +``` + +### 迁移容器 + +为了将容器从一个主机上热迁移到另一个主机,容器可以在在源系统上创建检查点,传输到目的系统,然后再在目的系统上恢复。 +为了便于传输容器的检查点,可以将其存储在一个指定的输出文件中。 + +在源系统上: + +```console +sudo podman container checkpoint <container_id> -e /tmp/checkpoint.tar.gz +scp /tmp/checkpoint.tar.gz <destination_system>:/tmp +``` + +在目标系统上: + +```console +sudo podman container restore -i /tmp/checkpoint.tar.gz +``` + +恢复之后,容器会像设置检查点之前一样回复请求。这时,容器会在目标系统上继续运行。 + +```console +curl http://<IP_address>:8080 +``` + +### 停止容器 + +停止 httpd 容器 + +```console +podman stop --latest +``` + +你还可以使用 *ps* 命令检查一个或多个容器的状态,在这个例子中,我们使用 *-a* 参数列出所有的容器。 + +```console +podman ps -a +``` + +### 移除容器 + +移除 httpd 容器 + +```console +podman rm --latest +``` + +你可以使用 *podman ps -a* 验证容器的删除。 + +## 集成测试 + +在环境中如何设置并运行集成测试请查看集成测试的[自述页面](../../test/README.md) + +## 更多信息 + +有关podman 和它的子命令的更多信息请查看 podman 的[自述页面](../../README.md#commands) diff --git a/hack/xref-helpmsgs-manpages b/hack/xref-helpmsgs-manpages index 6a2d627bb..a447f4da1 100755 --- a/hack/xref-helpmsgs-manpages +++ b/hack/xref-helpmsgs-manpages @@ -242,6 +242,13 @@ sub podman_help { if ($line =~ /^\s{1,4}(\S+)\s/) { my $subcommand = $1; print "> podman @_ $subcommand\n" if $debug; + + # check that the same subcommand is not listed twice (#12356) + if (exists $help{$subcommand}) { + warn "$ME: 'podman @_ help' lists '$subcommand' twice\n"; + ++$Errs; + } + $help{$subcommand} = podman_help(@_, $subcommand) unless $subcommand eq 'help'; # 'help' not in man } diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go index 9f939335c..8e07cff81 100644 --- a/libpod/define/container_inspect.go +++ b/libpod/define/container_inspect.go @@ -542,6 +542,12 @@ type InspectContainerHostConfig struct { CgroupConf map[string]string `json:"CgroupConf"` } +// Address represents an IP address. +type Address struct { + Addr string + PrefixLength int +} + // InspectBasicNetworkConfig holds basic configuration information (e.g. IP // addresses, MAC address, subnet masks, etc) that are common for all networks // (both additional and main). @@ -556,7 +562,7 @@ type InspectBasicNetworkConfig struct { IPPrefixLen int `json:"IPPrefixLen"` // SecondaryIPAddresses is a list of extra IP Addresses that the // container has been assigned in this network. - SecondaryIPAddresses []string `json:"SecondaryIPAddresses,omitempty"` + SecondaryIPAddresses []Address `json:"SecondaryIPAddresses,omitempty"` // IPv6Gateway is the IPv6 gateway this network will use. IPv6Gateway string `json:"IPv6Gateway"` // GlobalIPv6Address is the global-scope IPv6 Address for this network. @@ -565,7 +571,7 @@ type InspectBasicNetworkConfig struct { GlobalIPv6PrefixLen int `json:"GlobalIPv6PrefixLen"` // SecondaryIPv6Addresses is a list of extra IPv6 Addresses that the // container has been assigned in this network. - SecondaryIPv6Addresses []string `json:"SecondaryIPv6Addresses,omitempty"` + SecondaryIPv6Addresses []Address `json:"SecondaryIPv6Addresses,omitempty"` // MacAddress is the MAC address for the interface in this network. MacAddress string `json:"MacAddress"` // AdditionalMacAddresses is a set of additional MAC Addresses beyond diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 314a74427..7d1214183 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -1139,7 +1139,7 @@ func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNe config.IPPrefixLen = size config.Gateway = netAddress.Gateway.String() } else { - config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, netAddress.IPNet.IP.String()) + config.SecondaryIPAddresses = append(config.SecondaryIPAddresses, define.Address{Addr: netAddress.IPNet.IP.String(), PrefixLength: size}) } } else { //ipv6 @@ -1148,7 +1148,7 @@ func resultToBasicNetworkConfig(result types.StatusBlock) (define.InspectBasicNe config.GlobalIPv6PrefixLen = size config.IPv6Gateway = netAddress.Gateway.String() } else { - config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, netAddress.IPNet.IP.String()) + config.SecondaryIPv6Addresses = append(config.SecondaryIPv6Addresses, define.Address{Addr: netAddress.IPNet.IP.String(), PrefixLength: size}) } } } diff --git a/libpod/networking_linux_test.go b/libpod/networking_linux_test.go index 06bf05723..d925b69f7 100644 --- a/libpod/networking_linux_test.go +++ b/libpod/networking_linux_test.go @@ -2,10 +2,14 @@ package libpod import ( "fmt" + "net" + "reflect" "testing" - "github.com/containers/podman/v3/libpod/network/types" "github.com/stretchr/testify/assert" + + "github.com/containers/podman/v3/libpod/define" + "github.com/containers/podman/v3/libpod/network/types" ) func Test_ocicniPortsToNetTypesPorts(t *testing.T) { @@ -234,6 +238,217 @@ func Test_ocicniPortsToNetTypesPorts(t *testing.T) { } } +func Test_resultToBasicNetworkConfig(t *testing.T) { + testCases := []struct { + description string + expectError bool + inputResult types.StatusBlock + expectedNetworkConfig define.InspectBasicNetworkConfig + }{ + { + description: "single secondary IPv4 address is shown as define.Address", + inputResult: types.StatusBlock{ + Interfaces: map[string]types.NetInterface{ + "eth1": { + Subnets: []types.NetAddress{ + { + Gateway: net.ParseIP("172.26.0.1"), + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("172.26.0.2"), + Mask: net.CIDRMask(20, 32), + }, + }, + }, + { + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("172.26.0.3"), + Mask: net.CIDRMask(10, 32), + }, + }, + }, + }, + }, + }, + }, + expectedNetworkConfig: define.InspectBasicNetworkConfig{ + IPAddress: "172.26.0.2", + IPPrefixLen: 20, + Gateway: "172.26.0.1", + SecondaryIPAddresses: []define.Address{ + { + Addr: "172.26.0.3", + PrefixLength: 10, + }, + }, + }, + }, + { + description: "multiple secondary IPv4 addresses are shown as define.Address", + inputResult: types.StatusBlock{ + Interfaces: map[string]types.NetInterface{ + "eth1": { + Subnets: []types.NetAddress{ + { + Gateway: net.ParseIP("172.26.0.1"), + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("172.26.0.2"), + Mask: net.CIDRMask(20, 32), + }, + }, + }, + { + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("172.26.0.3"), + Mask: net.CIDRMask(10, 32), + }, + }, + }, + { + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("172.26.0.4"), + Mask: net.CIDRMask(24, 32), + }, + }, + }, + }, + }, + }, + }, + expectedNetworkConfig: define.InspectBasicNetworkConfig{ + IPAddress: "172.26.0.2", + IPPrefixLen: 20, + Gateway: "172.26.0.1", + SecondaryIPAddresses: []define.Address{ + { + Addr: "172.26.0.3", + PrefixLength: 10, + }, + { + Addr: "172.26.0.4", + PrefixLength: 24, + }, + }, + }, + }, + { + description: "single secondary IPv6 address is shown as define.Address", + inputResult: types.StatusBlock{ + Interfaces: map[string]types.NetInterface{ + "eth1": { + Subnets: []types.NetAddress{ + { + Gateway: net.ParseIP("ff02::fb"), + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("ff02::fc"), + Mask: net.CIDRMask(20, 128), + }, + }, + }, + { + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("ff02::fd"), + Mask: net.CIDRMask(10, 128), + }, + }, + }, + }, + }, + }, + }, + expectedNetworkConfig: define.InspectBasicNetworkConfig{ + GlobalIPv6Address: "ff02::fc", + GlobalIPv6PrefixLen: 20, + IPv6Gateway: "ff02::fb", + SecondaryIPv6Addresses: []define.Address{ + { + Addr: "ff02::fd", + PrefixLength: 10, + }, + }, + }, + }, + { + description: "multiple secondary IPv6 addresses are shown as define.Address", + inputResult: types.StatusBlock{ + Interfaces: map[string]types.NetInterface{ + "eth1": { + Subnets: []types.NetAddress{ + { + Gateway: net.ParseIP("ff02::fb"), + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("ff02::fc"), + Mask: net.CIDRMask(20, 128), + }, + }, + }, + { + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("ff02::fd"), + Mask: net.CIDRMask(10, 128), + }, + }, + }, + { + IPNet: types.IPNet{ + IPNet: net.IPNet{ + IP: net.ParseIP("ff02::fe"), + Mask: net.CIDRMask(24, 128), + }, + }, + }, + }, + }, + }, + }, + expectedNetworkConfig: define.InspectBasicNetworkConfig{ + GlobalIPv6Address: "ff02::fc", + GlobalIPv6PrefixLen: 20, + IPv6Gateway: "ff02::fb", + SecondaryIPv6Addresses: []define.Address{ + { + Addr: "ff02::fd", + PrefixLength: 10, + }, + { + Addr: "ff02::fe", + PrefixLength: 24, + }, + }, + }, + }, + } + + for _, tcl := range testCases { + tc := tcl + t.Run(tc.description, func(t *testing.T) { + t.Parallel() + actualNetworkConfig, err := resultToBasicNetworkConfig(tc.inputResult) + + if tc.expectError && err == nil { + t.Fatalf("Expected error didn't happen") + } + + if !tc.expectError && err != nil { + t.Fatalf("Unexpected error happened: %v", err) + } + + if !reflect.DeepEqual(tc.expectedNetworkConfig, actualNetworkConfig) { + t.Fatalf( + "Expected networkConfig %+v didn't match actual value %+v", tc.expectedNetworkConfig, actualNetworkConfig) + } + }) + } +} + func benchmarkOCICNIPortsToNetTypesPorts(b *testing.B, ports []types.OCICNIPortMapping) { for n := 0; n < b.N; n++ { ocicniPortsToNetTypesPorts(ports) |