Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: resolve special ip host-gateway #1978

Merged
merged 1 commit into from
Feb 28, 2023

Conversation

ningziwen
Copy link
Contributor

@ningziwen ningziwen commented Feb 7, 2023

#1979

Tests and docs will be added when the issue got concensus.

Signed-off-by: Ziwen Ning ningziwe@amazon.com

@@ -171,6 +171,7 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet,
rootCmd.PersistentFlags().StringSlice("hosts-dir", cfg.HostsDir, "A directory that contains <HOST:PORT>/hosts.toml (containerd style) or <HOST:PORT>/{ca.cert, cert.pem, key.pem} (docker style)")
// Experimental enable experimental feature, see in https://github.com/containerd/nerdctl/blob/main/docs/experimental.md
AddPersistentBoolFlag(rootCmd, "experimental", nil, nil, cfg.Experimental, "NERDCTL_EXPERIMENTAL", "Control experimental: https://github.com/containerd/nerdctl/blob/main/docs/experimental.md")
rootCmd.PersistentFlags().String("host-gateway-ip", cfg.HostGatewayIP, "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason not to just use the host IP?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default bridge IP isn't useful for rootless because the slirp is launched with --disable-host-loopback for the security reason

--disable-host-loopback --port-driver=$CONTAINERD_ROOTLESS_ROOTLESSKIT_PORT_DRIVER \

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I just tried to use host ip as default, as the commit I pushed.

I tested the built nerdctl in Lima. The returned host ip can't be used to access host (curl failed).

The static Lima guest IP (192.168.5.15) can be used to access host (curl succeeded). However it seems like a loopback IP which was skipped by the loopback check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried in ubuntu linux host and it works. I think the host IP is VM has special setup. I will follow the route of making host IP the default as it should work for normal cases. VM users will need to use the config to set the their special IP.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I finally used env variable the pass the host IP to nsenter. Let me know if you have different thoughts.

@@ -25,6 +25,7 @@ func mirrorOf(s string) string {

var (
AlpineImage = mirrorOf("alpine:3.13")
AmazonLinux2Image = "public.ecr.aws/amazonlinux/amazonlinux:2"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is a new image needed here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to use curl in tests but alpine doesn't have it and AL2 has it. Let me know if you have suggestions for better image choice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I'm okay with that. The nginx image should have curl, can you try that?

Copy link
Contributor Author

@ningziwen ningziwen Feb 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That works. Changed to it.

@@ -130,3 +131,18 @@ func HostsDirs() []string {
filepath.Join(xch, "docker/certs.d"),
}
}

func HostGatewayIP() string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add a comment here? since it's a new export non-test func.

Comment on lines 719 to 722
gateway, err := cmd.Flags().GetString("host-gateway-ip")
if err != nil {
return nil, nil, err
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

host-gateway-ip has been parsed and passed via globalOptions right? If so you can use globalOptions.HostGatewayIP

Comment on lines +73 to +76
hostGatewayIP, err := cmd.Flags().GetString("host-gateway-ip")
if err != nil {
return types.GlobalCommandOptions{}, err
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just to confirm, HostGatewayIP will never be empty right? Since the doc mentions it defauts to the IP address of the host.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From nerdctl external it can't be empty.

But it can be empty string "" when passing to here. When there was any issue getting host ip, it would be empty here. In container_run.go, it will return error when it is still "".

			if globalOptions.HostGatewayIP == "" {
				return nil, nil, fmt.Errorf("unable to derive the IP value for host-gateway")
			}

@ningziwen ningziwen requested review from djdongjin and AkihiroSuda and removed request for AkihiroSuda and djdongjin February 20, 2023 06:24
func TestRunAddHostWithCustomHostGatewayIP(t *testing.T) {
// Not parallelizable (https://github.com/containerd/nerdctl/issues/1127)
base := testutil.NewBase(t)
testutil.DockerIncompatible(t)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is it docker incompatible?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker run --host-gateway-ip "192.168.5.2" --add-host=name:host-gateway -it run amazonlinux bash
unknown flag: --host-gateway-ip
See 'docker run --help'.

Docker has host-gateway-ip in config but not in global flag. From my understanding, there is a convention in nerdctl that transferring all the config to global flag first before using them. So the global flag is docker incompatible.

Comment on lines +172 to +185
var found bool
sc := bufio.NewScanner(bytes.NewBufferString(stdout))
for sc.Scan() {
//removing spaces and tabs separating items
line := strings.ReplaceAll(sc.Text(), " ", "")
line = strings.ReplaceAll(line, "\t", "")
if strings.Contains(line, "192.168.5.2test") {
found = true
}
}
if !found {
return errors.New("host was not added")
}
return nil
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does a .AssertOutContains("192.168.5.2test") work?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should work but not that accurate as this, becuase 192.168.5.2test can also appear as a host alias instead of IP if the tested code is wrong. (Given tests can't assume tested code is correct)

Copy link
Member

@djdongjin djdongjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks

@@ -171,6 +171,7 @@ func initRootCmdFlags(rootCmd *cobra.Command, tomlPath string) (*pflag.FlagSet,
rootCmd.PersistentFlags().StringSlice("hosts-dir", cfg.HostsDir, "A directory that contains <HOST:PORT>/hosts.toml (containerd style) or <HOST:PORT>/{ca.cert, cert.pem, key.pem} (docker style)")
// Experimental enable experimental feature, see in https://github.com/containerd/nerdctl/blob/main/docs/experimental.md
AddPersistentBoolFlag(rootCmd, "experimental", nil, nil, cfg.Experimental, "NERDCTL_EXPERIMENTAL", "Control experimental: https://github.com/containerd/nerdctl/blob/main/docs/experimental.md")
AddPersistentStringFlag(rootCmd, "host-gateway-ip", nil, nil, nil, aliasToBeInherited, cfg.HostGatewayIP, "HOST_GATEWAY_IP", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Docker use HOST_GATEWAY_IP?

Otherwise:

Suggested change
AddPersistentStringFlag(rootCmd, "host-gateway-ip", nil, nil, nil, aliasToBeInherited, cfg.HostGatewayIP, "HOST_GATEWAY_IP", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge")
AddPersistentStringFlag(rootCmd, "host-gateway-ip", nil, nil, nil, aliasToBeInherited, cfg.HostGatewayIP, "NERDCTL_HOST_GATEWAY_IP", "IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the default bridge")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Defaults to the IP address of the default bridge

Doesn't seem consistent with the actual implementation

@@ -57,7 +57,8 @@ import (
"github.com/containerd/nerdctl/pkg/referenceutil"
"github.com/containerd/nerdctl/pkg/strutil"
"github.com/containerd/nerdctl/pkg/taskutil"
dopts "github.com/docker/cli/opts"
dockercliopts "github.com/docker/cli/opts"
dockerops "github.com/docker/docker/opts"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo dockeropts

}
for _, addr := range addrs {
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do not support ipv6 yet. You should return empty string for ipv6

@@ -130,3 +131,19 @@ func HostsDirs() []string {
filepath.Join(xch, "docker/certs.d"),
}
}

// HostGatewayIP returns the non-loop-back host ip if available and returns empty string if running into error.
func HostGatewayIP() string {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the HostGatewayIP should be the container network interface IP and not from other network interfaces

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think by defaulit we are returning the host's real ip now instead of any interface IP. The host ip is accessible from containers.

Copy link
Member

@fahedouch fahedouch Feb 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docker is setting the host bridge net interface IP. not sure if the host real IP will fit here, because the host net interface IP that's attached to the container need to be the gateway to route traffic from the container to host.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two blockers of using bridge IP.
One is it is not deterministic in nerdctl as I mentioned here For networking, Nerdctl and Docker are using different technology. Nerdctl uses CNI but Docker uses libnetwork, which caused the gap.

The other is it is not useful for rootless based on @AkihiroSuda 's comment.

I guess Docker compatibility is more about consistent user experience instead of implementation details. The default host gateway ip is just something that allows accessing host from containers. Only if the default ip can satisfy this use case, it should be fine.

Let me know if you have better suggestions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SGTM the real host IP

@@ -1599,6 +1600,7 @@ Flags:
- :nerd_face: `--cgroup-manager=(cgroupfs|systemd|none)`: cgroup manager
- Default: "systemd" on cgroup v2 (rootful & rootless), "cgroupfs" on v1 rootful, "none" on v1 rootless
- :nerd_face: `--insecure-registry`: skips verifying HTTPS certs, and allows falling back to plain HTTP
- :nerd_face: `--host-gateway-ip`: IP address that the special 'host-gateway' string in --add-host resolves to. Defaults to the IP address of the host
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to mention that --host-gateway-ip have no effect without setting --add-host
cc @ningziwen

Signed-off-by: Ziwen Ning <ningziwe@amazon.com>
Copy link
Member

@fahedouch fahedouch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

Copy link
Member

@AkihiroSuda AkihiroSuda left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants