diff options
-rw-r--r-- | docs/source/Tutorials.rst | 1 | ||||
-rw-r--r-- | docs/tutorials/README.md | 4 | ||||
-rw-r--r-- | docs/tutorials/basic_networking.md | 291 | ||||
-rw-r--r-- | docs/tutorials/podman_bridge.png | bin | 0 -> 48144 bytes | |||
-rw-r--r-- | docs/tutorials/podman_macvlan.png | bin | 0 -> 37646 bytes | |||
-rw-r--r-- | docs/tutorials/podman_pod.png | bin | 0 -> 53274 bytes | |||
-rw-r--r-- | docs/tutorials/podman_rootless_default.png | bin | 0 -> 40293 bytes | |||
-rw-r--r-- | pkg/api/handlers/decoder.go | 15 | ||||
-rw-r--r-- | pkg/bindings/util/util.go | 11 | ||||
-rw-r--r-- | test/apiv2/20-containers.at | 2 |
10 files changed, 323 insertions, 1 deletions
diff --git a/docs/source/Tutorials.rst b/docs/source/Tutorials.rst index e3e869d5b..e48d1e853 100644 --- a/docs/source/Tutorials.rst +++ b/docs/source/Tutorials.rst @@ -11,3 +11,4 @@ Here are a number of useful tutorials to get you up and running with Podman. If * `Podman remote-client tutorial <https://github.com/containers/podman/blob/master/docs/tutorials/remote_client.md>`_: A brief how-to on using the Podman remote-client. * `How to use libpod for custom/derivative projects <https://github.com/containers/podman/blob/master/docs/tutorials/podman-derivative-api.md>`_: How the libpod API can be used within your own project. * `How to use Podman's Go bindings <https://github.com/containers/podman/blob/master/docs/tutorials/podman-go-bindings.md>`_: A brief how-to on using Podman's Go bindings in external applications. +* `Common network setups <https://github.com/containers/podman/blob/master/docs/tutorials/basic_networking.md>`_: A basic guide to common network setups for Podman. diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 246d235ee..455459062 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -31,3 +31,7 @@ Learn how to setup and use image signing with Podman. **[Go Bindings](podman-go-bindings.md)** A brief how-to on using Podman's Go bindings in external applications. + +**[Go Bindings](basic_networking.md)** + +A basic guide to common network setups with Podman diff --git a/docs/tutorials/basic_networking.md b/docs/tutorials/basic_networking.md new file mode 100644 index 000000000..7544c1cfd --- /dev/null +++ b/docs/tutorials/basic_networking.md @@ -0,0 +1,291 @@ +![PODMAN logo](../../logo/podman-logo-source.svg) + + +# Basic Networking Guide for Podman + + +It seems once people master the basics of containers, networking is one of the first +aspects they begin experimenting with. And in regards to networking, it takes very +little experimentation before ending up on the deep end of the pool. The following +guide shows the most common network setups for Podman rootfull and rootless containers. +Each setup is supported with an example. + + +## Differences between rootfull and rootless container networking + +One of the guiding factors on networking for containers with Podman is going to be +whether or not the container is run by a root user or not. This is because unprivileged +users cannot create networking interfaces on the host. Therefore, with rootfull +containers, the default networking mode is to use the Container Network Interface +(CNI) plugins and specifically the bridge plugin. For rootless, the default network +mode is slirp4netns. Because of the limited privileges, slirp4netns lacks some of +the features of CNI networking; for example, slirp4netns cannot give containers a +routable IP address. + +## Firewalls + +The role of a firewall will not impact the setup and configuration of networking, +but it will impact traffic on those networks. The most obvious is inbound network +traffic to the container host, which is being passed onto containers usually with +port mapping. Depending on the firewall implementation, we have observed firewall +ports being opened automatically due to running a container with a port mapping (for +example). If container traffic does not seem to work properly, check the firewall +and allow traffic on ports the container is using. A common problem is that +reloading the firewall deletes the cni iptables rules resulting in a loss of +network connectivity for rootful containers. Podman v3 provides the podman +network reload command to restore this without having to restart the container. + +## Basic Network Setups + +Most containers and pods being run with Podman adhere to a couple of simple scenarios. +By default, rootfull Podman will create a bridged network. This is the most straightforward +and preferred network setup for Podman. Bridge networking creates an interface for +the container on an internal bridge network, which is then connected to the internet +via Network Address Translation(NAT). We also see users wanting to use `macvlan` +for networking as well. The `macvlan` plugin forwards an entire network interface +from the host into the container, allowing it access to the network the host is connected +to. And finally, the default network configuration for rootless containers is slirp4netns. +The slirp4netns network mode has limited capabilities but can be run on users without +root privileges. It creates a tunnel from the host into the container to forward +traffic. + +### Bridge + +CNI defines a bridge network as where an internal network is created where both the +container and host are attached. Then this network is capable of allowing the containers +to communicate outside of the host. + + +![bridge_network](podman_bridge.png) + +Consider the above illustration. It depicts a laptop user running two containers: +a web and db instance. These two containers are on the virtual network with the +host. Additionally, by default, these containers can initiate communications outside +the laptop (to the Internet for example). The containers on the virtual network +typically have non-routable, also known as private IP addresses. + +When dealing with communication that is being initiated outside the host, the outside +client typically must address the laptop’s external network interface and given port +number. Assuming the host allows incoming traffic, the host will know to forward +the incoming traffic on that port to the specific container. To accomplish this, +firewall rules are added to forward traffic when a container requests a specific +port be forwarded. + +Bridge networking is the default for Podman containers created as root. Podman provides +a default bridge network, but you can create others using the `podman network create` +command. Containers can be joined to a CNI network when they are created with the +`--network` flag, or after they are created via the `podman network connect` and +`podman network disconnect` commands. + +As mentioned earlier, slirp4netns is the default network configuration for rootless +users. But as of Podman version 3.0, rootless users can also use CNI networking. +The user experience of rootless CNI is very akin to a rootfull CNI, except that +there is no default network configuration provided. You simply need to create a +network, and the one will be created as a bridge network. + +``` +$ podman network create +``` + +When rootless containers are run with a CNI networking configuration, a “side-car” +container for running CNI is also run. Do not remove this container while your rootless +containers are running. if you remove this container (e.g by accident) all attached +containers lose network connectivity. In order to restore the network connectivity +all containers with networks must be restarted. This will automatically recreate +the "side-car" container. For rootfull containers, there is no “side-car” container +as rootfull users have the permissions to create and modify network interfaces on +the host. + +#### Example + +By default, rootfull containers use the CNI bridge plugin for its default configuration. +In this case, no network name must be passed to Podman. However, you can create +additional bridged networks with the podman create command. In that case, you will +have to set the network name. + +The following example shows how to set up a web server and expose it to the network +outside the host as both rootfull and rootless. It will also show how an outside +client can connect to the container. + +``` +(rootfull) $ sudo podman run -dt --name webserver -p 8080:80 quay.io/libpod/banner +00f3440c7576aae2d5b193c40513c29c7964e96bf797cf0cc352c2b68ccbe66a +``` + +As mentioned earlier, for rootless containers using CNI, a network must first be +created. +``` +$ podman network create +/home/baude/.config/cni/net.d/cni-podman1.conflist +``` +Now run the container. +``` +$ podman run -dt --name webserver --net cni-podman1 -p 8081:80 quay.io/libpod/banner +269fd0d6b2c8ed60f2ca41d7beceec2471d72fb9a33aa8ca45b81dc9a0abbb12 +``` +Note in the above run command, the container’s port 80 (where the Nginx server is +running) was mapped to the host’s port 8080. Port 8080 was chosen to demonstrate +how the host and container ports can be mapped for external access. The port could +very well have been 80 as well (except for rootless users). + +To connect from an outside client to the webserver, simply point an HTTP client to +the host’s IP address at port 8080 for rootfull and port 8081 for rootless. +``` +(outside_host): $ curl 192.168.99.109:8080 + ___ __ + / _ \___ ___/ /_ _ ___ ____ + / ___/ _ \/ _ / ' \/ _ `/ _ \ +/_/ \___/\_,_/_/_/_/\_,_/_//_/ + +(outside_host): $ curl 192.168.99.109:8081 + ___ __ + / _ \___ ___/ /_ _ ___ ____ + / ___/ _ \/ _ / ' \/ _ `/ _ \ +/_/ \___/\_,_/_/_/_/\_,_/_//_/ +``` + +### Macvlan + +With macvlan, the container is given access to a physical network interface on the +host. This interface can configure multiple subinterfaces. And each subinterface +is capable of having its own MAC and IP address. In the case of Podman containers, +the container will present itself as if it is on the same network as the host. + +![macvlan_network](podman_bridge.png) + +In the illustration, outside clients will be able to access the web container by +its IP address directly. Usually the network information, including IP address, +is leased from a DHCP server like most other network clients on the network. If +the laptop is running a firewall, such as firewalld, then accommodations will need +to be made for proper access. + +#### Example + +The following example demonstrates how to set up a web container on a macvlan and +how to access that container from outside the host. First, create the macvlan network. + You need to know the network interface on the host that connects to the routable +network. In the example case, it is eth0. +``` +$ sudo podman network create -d macvlan -o parent=eth0 webnetwork +/etc/cni/net.d/webnetwork.conflist +``` +The next step is to ensure that the DHCP CNI plugin is running. This plugin facilitates +the DHCP lease from the network. +``` +$ sudo /usr/libexec/cni/dhcp daemon +``` +Now run the container and be certain to attach it to the network we created earlier. +``` +$ sudo podman run -dt --name webserver --network webnetwork quay.io/libpod/banner +03d82083c434d7e937fc0b87c25401f46ab5050007df403bf988e25e52c5cc40 +[baude@localhost ~]$ sudo podman exec webserver ip address show eth0 +2: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state +UP +link/ether 0a:3c:e2:eb:87:0f brd ff:ff:ff:ff:ff:ff +inet 192.168.99.186/24 brd 192.168.99.255 scope global eth0 +valid_lft forever preferred_lft forever +inet6 fe80::83c:e2ff:feeb:870f/64 scope link +valid_lft forever preferred_lft forever +``` +Because the container has a routable IP address (on this network) and is not being +managed by firewalld, no change to the firewall is needed. +``` +(outside_host): $ curl http://192.168.99.186 + ___ __ + / _ \___ ___/ /_ _ ___ ____ + / ___/ _ \/ _ / ' \/ _ `/ _ \ +/_/ \___/\_,_/_/_/_/\_,_/_//_/ +``` + + + +### Slirp4netns + +Slirp4netns is the default network setup for rootless containers and pods. It was +invented because unprivileged users are not allowed to make network interfaces on +the host. Slirp4netns creates a TAP device in the container’s network namespace +and connects to the usermode TCP/IP stack. Consider the following illustration. + +![slirp_network](podman_rootless_default.png) + +The unprivileged user on this laptop has created two containers: a DB container and +a web container. Both of these containers have the ability to access content on +networks outside the laptop. And outside clients can access the containers if the +container is bound to a host port and the laptop firewall allows it. Remember, unprivileged +users must use ports 1024 through 65535 as lower ports require root privileges. (CAP_NET_BIND_SERVICE) +Note: this can be adjusted using the `sysctl net.ipv4.ip_unprivileged_port_start` + +One of the drawbacks of slirp4netns is that the containers are completely isolated +from each other. Unlike the bridge approach, there is no virtual network. For containers +to communicate with each other, they can use the port mappings with the host system, +or they can be put into a Pod where they share the same network namespace. See [Communicating +between containers and pods](#Communicating-between-containers-and-pods) for more information. + +#### Example + +The following example will show how two rootless containers can communicate with +each other where one is a web server. Then it will show how a client on the host’s +network can communicate with the rootless web server. + +First, run the rootless web server and map port 80 from the container to a non-privileged +port like 8080. +``` +$ podman run -dt --name webserver -p 8080:80 quay.io/libpod/banner +17ea33ccd7f55ff45766b3ec596b990a5f2ba66eb9159cb89748a85dc3cebfe0 +``` +Because rootfull containers cannot communicate with each other directly with TCP/IP +via IP addresses, the host and the port mapping are used. To do so, the IP address +of the host (interface) must be known. +``` +$ ip address show eth0 +3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group +default qlen 1000 +link/ether 3c:e1:a1:c1:7a:3f brd ff:ff:ff:ff:ff:ff +altname eth0 +inet 192.168.99.109/24 brd 192.168.99.255 scope global dynamic noprefixroute eth0 +valid_lft 78808sec preferred_lft 78808sec +inet6 fe80::5632:6f10:9e76:c33/64 scope link noprefixroute +valid_lft forever preferred_lft forever +``` +From another rootless container, use the host’s IP address and port to communicate +between the two rootless containers successfully. +``` +$ podman run -it quay.io/libpod/banner curl http://192.168.99.109:8080 + ___ __ + / _ \___ ___/ /_ _ ___ ____ + / ___/ _ \/ _ / ' \/ _ `/ _ \ +/_/ \___/\_,_/_/_/_/\_,_/_//_/ +``` + +From a client outside the host, the IP address and port can also be used: +``` +(outside_host): $ curl http://192.168.99.109:8080 + ___ __ + / _ \___ ___/ /_ _ ___ ____ + / ___/ _ \/ _ / ' \/ _ `/ _ \ +/_/ \___/\_,_/_/_/_/\_,_/_//_/ +``` + +## Communicating between containers and pods + +Most users of containers have a decent understanding of how containers communicate +with each other and the rest of the world. Usually each container has its own IP +address and networking information. They communicate amongst each other using regular +TCP/IP means like IP addresses or, in many cases, using DNS names often based on +the container name. But pods are a collection of one or more containers, and with +that, some uniqueness is inherited. + +By definition, all containers in a Podman pod share the same network namespace. This +fact means that they will have the same IP address, MAC addresses, and port mappings. +You can conveniently communicate between containers in a pod by using localhost. + +![slirp_network](podman_pod.png) + +The above illustration describes a Pod on a bridged network. As depicted, the Pod +has two containers “inside” it: a DB and a Web container. Because they share the +same network namespace, the DB and Web container can communicate with each other +using localhost (127.0.0.1). Furthermore, they are also both addressable by the +IP address (and DNS name if applicable) assigned to the Pod itself. + +For more information on container to container networking, see Configuring container +networking with Podman. diff --git a/docs/tutorials/podman_bridge.png b/docs/tutorials/podman_bridge.png Binary files differnew file mode 100644 index 000000000..c0cb88951 --- /dev/null +++ b/docs/tutorials/podman_bridge.png diff --git a/docs/tutorials/podman_macvlan.png b/docs/tutorials/podman_macvlan.png Binary files differnew file mode 100644 index 000000000..04d9c9080 --- /dev/null +++ b/docs/tutorials/podman_macvlan.png diff --git a/docs/tutorials/podman_pod.png b/docs/tutorials/podman_pod.png Binary files differnew file mode 100644 index 000000000..17718e20b --- /dev/null +++ b/docs/tutorials/podman_pod.png diff --git a/docs/tutorials/podman_rootless_default.png b/docs/tutorials/podman_rootless_default.png Binary files differnew file mode 100644 index 000000000..5d4a66644 --- /dev/null +++ b/docs/tutorials/podman_rootless_default.png diff --git a/pkg/api/handlers/decoder.go b/pkg/api/handlers/decoder.go index 54087168a..123d325aa 100644 --- a/pkg/api/handlers/decoder.go +++ b/pkg/api/handlers/decoder.go @@ -6,6 +6,7 @@ import ( "syscall" "time" + "github.com/containers/podman/v2/libpod/define" "github.com/containers/podman/v2/pkg/util" "github.com/gorilla/schema" "github.com/sirupsen/logrus" @@ -19,6 +20,7 @@ func NewAPIDecoder() *schema.Decoder { d.IgnoreUnknownKeys(true) d.RegisterConverter(map[string][]string{}, convertURLValuesString) d.RegisterConverter(time.Time{}, convertTimeString) + d.RegisterConverter(define.ContainerStatus(0), convertContainerStatusString) var Signal syscall.Signal d.RegisterConverter(Signal, convertSignal) @@ -46,6 +48,19 @@ func convertURLValuesString(query string) reflect.Value { return reflect.ValueOf(f) } +func convertContainerStatusString(query string) reflect.Value { + result, err := define.StringToContainerStatus(query) + if err != nil { + logrus.Infof("convertContainerStatusString: Failed to parse %s: %s", query, err.Error()) + + // We return nil here instead of result because reflect.ValueOf().IsValid() will be true + // in github.com/gorilla/schema's decoder, which means there's no parsing error + return reflect.ValueOf(nil) + } + + return reflect.ValueOf(result) +} + // isZero() can be used to determine if parsing failed. func convertTimeString(query string) reflect.Value { var ( diff --git a/pkg/bindings/util/util.go b/pkg/bindings/util/util.go index 6296fc22f..c1961308e 100644 --- a/pkg/bindings/util/util.go +++ b/pkg/bindings/util/util.go @@ -2,6 +2,7 @@ package util import ( "errors" + "fmt" "net/url" "reflect" "strconv" @@ -11,14 +12,23 @@ import ( ) func IsSimpleType(f reflect.Value) bool { + if _, ok := f.Interface().(fmt.Stringer); ok { + return true + } + switch f.Kind() { case reflect.Bool, reflect.Int, reflect.Int64, reflect.Uint, reflect.Uint64, reflect.String: return true } + return false } func SimpleTypeToParam(f reflect.Value) string { + if s, ok := f.Interface().(fmt.Stringer); ok { + return s.String() + } + switch f.Kind() { case reflect.Bool: return strconv.FormatBool(f.Bool()) @@ -31,6 +41,7 @@ func SimpleTypeToParam(f reflect.Value) string { case reflect.String: return f.String() } + panic("the input parameter is not a simple type") } diff --git a/test/apiv2/20-containers.at b/test/apiv2/20-containers.at index 0da196e46..a99e9a184 100644 --- a/test/apiv2/20-containers.at +++ b/test/apiv2/20-containers.at @@ -63,7 +63,7 @@ cid=$(jq -r '.Id' <<<"$output") # Prior to the fix in #6835, this would fail 500 "args must not be empty" t POST libpod/containers/${cid}/start '' 204 # Container should exit almost immediately. Wait for it, confirm successful run -t POST libpod/containers/${cid}/wait '' 200 '0' +t POST "libpod/containers/${cid}/wait?condition=stopped&condition=exited" '' 200 '0' t GET libpod/containers/${cid}/json 200 \ .Id=$cid \ .State.Status~\\\(exited\\\|stopped\\\) \ |