{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Namespaces Lab\n", "\n", "In this lab, we will explore how namespaces are presented in the Linux shell and how you can interact with namespaces using some simple commands. During the lab we will explore how you can interact with both the Network and Mount namespaces, which are commonly used by containers as an isolation mechanism to separate workloads running a single host.\n", "\n", "To start, lets explore a simple example of how namespaces work. Lets look at the network namespace.\n", "\n", "Here are the interfaces on our host:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ip link" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, Lets use unshare to create a new namespace and run the same command to see the interfaces in this namespace:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sudo unshare -n ip link" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note, we have a loopback interface, but the parameters attached to this interface are different to loopback on the host OS. This is a separate logical interface which has been facilitated by namespaces." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Docker namespace utilization demo\n", "\n", "Now lets take a look at how docker uses namespaces to separate containers, and explore how we can interact with this namespace from the host OS. \n", "\n", "To start, lets run a redis container:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "docker pull redis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "docker run --name redis -d redis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the purposes of this lab, we will need to know the PID the Redis container is running under from the Host OS. We can extract this with the following shell command:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "REDIS_PID=$(ps ax | grep redis | grep -v grep | awk '{print $1}')\n", "echo $REDIS_PID" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From the Host OS, we can see the redis process.. even though redis is in a separate namespace. This is because the PID namespace is a little bit different and is visible to the host OS to allow us to see all running processes within the kernel. \n", "\n", "Next, we can find the network namespace id for our process by exploring the /proc filesystem with the readlink command:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sudo readlink /proc/$REDIS_PID/ns/net" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets look deeper and examine the networking configuration of the Redis container we've started up. First, lets pull some information from Docker to gather the IP address recorded for the container: " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "CONTAINER_IP=$(docker inspect redis | jq .[0].NetworkSettings.IPAddress | sed -e s/\\\"//g)\n", "echo $CONTAINER_IP" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now to cross check, lets enter the namespace created for the redis-server process (aka the Docker container), and run the ifconfig command. We should see the same IP address as recorded by Docker:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sudo nsenter -t $REDIS_PID -n ifconfig" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As docker uses a bridge network by default, I can telnet to the redis-server from the host OS" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(printf \"PING\\r\\n\";) | nc $CONTAINER_IP 6379 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now lets explore how we can interact with the container namespace from the host OS.\n", "\n", "To do this, lets launch a nginx process from the Host OS, in the redis container namespace. \n", "\n", "Now, nginx does not ship in the docker container for redis, and the redis container is based on Ubuntu Linux, but we do have nginx installed on our Host OS, running Amazon Linux. \n", "\n", "In the next example, we namespace for our redis server, but execute nginx from our host OS, which is running Amazon Linux:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sudo nsenter -t $REDIS_PID -n nginx" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, to validate the nginx process is not running in the host OS network namespace, i can try to connect to localhost:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "curl http://localhost" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "However, if i connect to the IP associated with the redis container network namespace:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "curl http://$CONTAINER_IP" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can see this is the Amazon Linux distribution of Redis. \n", "\n", "I'm running a process in the same networking space as my container, but not in the container. It's sharing the networking space, but it's not running under Ubuntu which is what the container is running\n", "\n", "Interacting with namespaces on a host can be a useful tool for troubleshooting. By using commands like the above above, we can interact with the networking stack the docker container is running in. In turn, we can use this for troubleshooting to perform tests and rule out configuration problems in the docker container without needing to rebuild the container and redeploy. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Exploring the mount namespace\n", "\n", "Lets now look at this from the opposite direction: We don't have redis installed on the Amazon Linux host OS, but we do have this as part of our redis container that is running. Lets explore accessing the mount namespace from the Host OS. \n", "\n", "First, lets show there is no redis-server installed or running on localhost:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "which redis-server" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(printf \"PING\\r\\n\";) | nc localhost 6379 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Next, we can enter the mount namespace for our redis container, and run this binary on the host OS:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "sudo nsenter -t $REDIS_PID -m /usr/local/bin/redis-server &" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can test the redis server in the host OS network namespace:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "(printf \"PING\\r\\n\";) | nc localhost 6379 " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also see we have two instances of redis running:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ps ax | grep redis | grep -v grep" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Lab Cleanup\n", "\n", "After running this demo, use the following commands to clean up the lab and stop any running redis-service processes:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": true }, "outputs": [], "source": [ "docker ps" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "docker stop redis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "ps -ef | grep redis" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Use the kill command in the next section to kill the nsenter and redis-server process IDs" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#sudo kill -9 " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "docker rm redis" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Bash", "language": "bash", "name": "bash" }, "language_info": { "codemirror_mode": "shell", "file_extension": ".sh", "mimetype": "text/x-sh", "name": "bash" } }, "nbformat": 4, "nbformat_minor": 2 }