Cloud in Action: Practice Docker and its Networking on Ubuntu
VM, LXC, Docker and Libcontainer
VMs and LXC (Linux Containers) differ on quite a few dimensions, but primarily LXC is a OS-level virtualization technology that allows creation and running of multiple isolated Linux virtual environments on a single control host, whereas with VMs, the hardware is being virtualized to run multiple OS instances. LXC sits on top of a physical server and its host OS, and each container shares the host OS kernel. The shared components are read-only and each container is able to be written to through a unique mount, which makes containers exceptionally “light”. Containers’ speed, agility and portability make them an important tool for DevOps.
Docker is a significant improvement and extension of LXC’s capabilities, and acts as a portable container engine, packaging the application and all its dependencies in a virtual container that can run on any Linux server, and making containers more protable and flexible to use. Using Docker containers, you can deploy, replicate, move, and back up a workload even more quickly and easily than you can do so using virtual machines. Basically, Docker brings cloudlike flexibility to any infrastructure capable of running containers.
Docker/LXC takes advantage of several features of the Linux kernel to deliver its functionality:
Namespace is to wrap a particular global system resource in an abstraction that makes it appear to the process within the namespace that they have their own isolated instance of the global resource. Currently there are 6 namespaces implemented in the Linux Kernel: Mount, UTS, IPC, Network, PID and User.
Cgroups allow allocating resources to user-defined groups of processes running on the system. A cgroup limits an application to a specific set of resources.
UnionFS, Union file systems operate by creating layers, making them very lightweight and fast to privide the building blocks for containers.
LXC was used before Docker 1.10 as one execution driver, and offered a userspace interface for the Linux kernel containment features, which is very specific to Linux. Libcontainer is an abstraction layer being an attempt to standarize the way apps are packed up, delivered, and run in isolation, and support a wider range of isolation technologies, and could transform Docker into a cross-platform technology including Windows. Now Libcontainer is the default Docker execution environment.
It deserves mentioning that Windows server can’t run Linux containers in Docker format but only Windows containers, because Linux containers require the Linux APIs from the host kernel and Windows Server Containers require the Windows APIs of a host Windows kernel; however, the process of managing Linux and Windows containers are strictly identical.
Here is the quick summary: Container - Virtual Machine; Docker - Hypervisor (Xen,KVM); Kubernetes - OpenStack.
Practice Namespace
// Create a child process with the different namespace on UTS
gset@ubuntu:~$ gedit hello.c
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#define STACK_SIZE (1024 * 1024)
static char child_stack[STACK_SIZE];
char * const child_args[] = { "/bin/bash", NULL};
int child_main1(void* args)
{
printf("Running in the child process!\n");
sethostname("XGF", 12);
execv(child_args[0], child_args);
return 1;
}
int main() {
printf("Get started in the main process: \n");
int child_pid1 = clone(child_main1, child_stack+STACK_SIZE,CLONE_NEWUTS | SIGCHLD, NULL);
waitpid(child_pid1, NULL, 0); // CLONE_NEWUTS,create a new UTS Namespace
printf("The main process exits\n");
return 0;
}
gset@ubuntu:~$ gcc hello.c -o hello.out
gset@ubuntu:~$
gset@ubuntu:~$ hostname
ubuntu
gset@ubuntu:~$ sudo ./hello.out
Get started in the main process:
Running in the child process!
root@XGF:~# hostname
XGF
root@XGF:~# exit
exit
The main process exits
gset@ubuntu:~$ hostname
ubuntu
// Create a network namespace - xgf, and connect it to Host with a pair of veth interfaces
gset@ubuntu:~$ sudo ip netns add xgf // create a network namespace - xgf
gset@ubuntu:~$ sudo ip link add veth-a type veth peer name veth-b // create a pair of veth interfaces: veth-a, veth-b
gset@ubuntu:~$ sudo ip link set veth-b netns xgf // move veth-b to the network namespace - xgf
gset@ubuntu:~$ sudo ip link set dev veth-a up
gset@ubuntu:~$ sudo ip addr add 10.110.0.1/24 dev veth-a
gset@ubuntu:~$ sudo ip netns exec xgf ip link set dev veth-b up // execute commands in the network namespace - xgf
gset@ubuntu:~$ sudo ip netns exec xgf ip addr add 10.110.0.2/24 dev veth-b
gset@ubuntu:~$ sudo ip netns exec xgf ip link set dev lo up
gset@ubuntu:~$ ip link/addr/route
gset@ubuntu:~$ sudo ip netns exec xgf ip link/addr/route
gset@ubuntu:~$ sudo ip netns list
gset@ubuntu:~$ ping 10.110.0.1
PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.
64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.032 ms
gset@ubuntu:~$ ping 10.110.0.2
PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.
64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.038 ms
gset@ubuntu:~$ sudo ip netns exec xgf ping 10.110.0.1
PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.
64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.093 ms
gset@ubuntu:~$ sudo ip netns exec xgf ping 10.110.0.2
PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.
64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.075 ms
gset@ubuntu:~$ ifconfig
veth-a Link encap:Ethernet HWaddr d6:8e:09:5a:ed:4a
inet addr:10.110.0.1 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::d48e:9ff:fe5a:ed4a/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:25 errors:0 dropped:0 overruns:0 frame:0
TX packets:48 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:1818 (1.8 KB) TX bytes:5455 (5.4 KB)
gset@ubuntu:~$ sudo ip netns exec xgf bash // enter into the network namespace - xgf
root@ubuntu:~# ifconfig
veth-b Link encap:Ethernet HWaddr 1a:ed:45:0f:1b:36
inet addr:10.110.0.2 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::18ed:45ff:fe0f:1b36/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:47 errors:0 dropped:0 overruns:0 frame:0
TX packets:24 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:5368 (5.3 KB) TX bytes:1748 (1.7 KB)
root@ubuntu:~# ip route
10.110.0.0/24 dev veth-b proto kernel scope link src 10.110.0.2
root@ubuntu:~# ping 10.110.0.1
PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.
64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.039 ms
root@ubuntu:~# ping 10.110.0.2
PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.
64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.024 ms
gset@ubuntu:~$ sudo ip link del veth-a ( or veth-b ) // delete veth intefaces
gset@ubuntu:~$ sudo ip netns del xgf // delete the network namespace – xgf
Docker Installation
https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-16-04
The Docker installation package available in the official Ubuntu 16.04 repository may not be the latest version. To get the latest and greatest version, install Docker from the official Docker repository:
gset@ubuntu:~$ uname –rso
gset@ubuntu:~$ cat /etc/lsb-release
gset@ubuntu:~$ sudo apt-get install curl
gset@ubuntu:~$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add –
// add the GPG key for the official Docker repository to the system
gset@ubuntu:~$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" // add the Docker repository to APT sources
gset@ubuntu:~$ sudo apt-get update
gset@ubuntu:~$ apt-cache policy docker-ce
gset@ubuntu:~$ sudo apt-get install -y docker-ce
gset@ubuntu:~$ sudo systemctl status docker
gset@ubuntu:~$ sudo docker run hello-world
Running the docker command requires root privileges and we have to prefix the command with sudo. It can also be run by a user in the docker group, which is automatically created during the installation of Docker. If you attempt to run the docker command without prefixing it with sudo or without being in the docker group, you need finish the following confiurations:
sudo usermod -aG docker ${USER} // add the current username to the docker group:
su - ${USER} // apply the new group membership
id -nG // confirm the membership of docker group
gset@ubuntu:~$ docker version
gset@ubuntu:~$ docker info
Use Case 1: Create a web server with port mapping
// Download the Ubuntu 14.04 image from Docker Hub
gset@ubuntu:~$ docker pull ubuntu:14.04
gset@ubuntu:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 14.04 d6ed29ffda6b 8 days ago 221MB
// Create the xgfwebserver container with the docker image ubuntu 14.04
// Map the host port - 8000 to the xgfwebserver port - 80
gset@ubuntu:~$ docker run -it -p 8000:80 --name xgfwebserver ubuntu:14.04
// Install python-pip and Flask, and run the web service on the 80 port
root@62b4975b2a8d:/#
root@62b4975b2a8d:/# ip addr
1: lo:
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
7: eth0@if8:
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.2/16 scope global eth0
valid_lft forever preferred_lft forever
root@62b4975b2a8d:/# sudo apt-get update
root@62b4975b2a8d:/# sudo apt-get install python-pip
root@62b4975b2a8d:/# sudo pip install Flask
root@62b4975b2a8d:/# vi hello.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World, this is Richard Xue \r\n'
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)
root@62b4975b2a8d:/# python hello.py
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
10.0.0.129 - - [26/Nov/2017 20:29:26] "GET / HTTP/1.1" 200 -
10.0.0.1 - - [26/Nov/2017 20:29:27] "GET / HTTP/1.1" 200 -
10.0.0.137 - - [26/Nov/2017 20:29:32] "GET / HTTP/1.1" 200 –
root@62b4975b2a8d:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 23:29 pts/0 00:00:00 /bin/bash
root 15 1 0 23:29 pts/0 00:00:00 ps -ef
// Check the web service in the host
gset@ubuntu:~$ curl 10.0.0.137:8000
Hello World, this is Richard Xue
gset@ubuntu:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
62b4975b2a8d ubuntu:14.04 "/bin/bash" 6 minutes ago Up 6 minutes 0.0.0.0:8000->80/tcp xgfwebserver
gset@ubuntu:~$ docker stop xgfwebserver
gset@ubuntu:~$ docker restart xgfwebserver
gset@ubuntu:~$ docker start xgfwebserver
gset@ubuntu:~$ docker attach xgfwebserver
gset@ubuntu:~$ docker exec -it 62b4975b2a8d /bin/bash
gset@ubuntu:~$ docker exec -it 62b4975b2a8d python hello.py
Use Case 2: Share the data between Host and containers
// Create the xgf directory in the host and share with its containers
gset@ubuntu:~$ mkdir xgf
gset@ubuntu:~$ cd xgf
gset@ubuntu:~/xgf$ gedit hello1.py
from flask import Flask
app = Flask(__name__)
@app.route('/',methods=['GET'])
def root_get():
return 'GET - This is Richard Xue\r\n'
@app.route('/hello/
def hello_world(id):
return 'Hello World: {}\r\n'.format(id)
@app.route('/add/
def add(id):
return 'Sum: %d\r\n' %id
if __name__ == '__main__':
app.run(host='0.0.0.0',port=80)
// Create ‘xgf’ in the container and map ‘/home/gset/xgf’ in the host to the ‘/xgf’ of container
gset@ubuntu:~$ docker run -it -v /home/gset/xgf:/xgf -p 8080:80 --name xgfwebserver2 flaskweb
// If ready-only
gset@ubuntu:~$ docker run -it -v /home/gset/xgf:/xgf:ro -p 8080:80 --name xgfwebserver2 flaskweb
root@770058c45a94:/# python ./xgf/hello1.py
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
10.0.0.129 - - [26/Nov/2017 21:23:59] "GET / HTTP/1.1" 200 –
10.0.0.137 - - [26/Nov/2017 21:49:32] "GET / HTTP/1.1" 200 -
10.0.0.137 - - [26/Nov/2017 21:49:38] "GET /hello/10 HTTP/1.1" 200 -
10.0.0.137 - - [26/Nov/2017 21:49:55] "GET /add/100 HTTP/1.1" 200 -
// Check the web service in the host
gset@ubuntu:~$ curl 10.0.0.137:8080
GET - This is Richard Xue
gset@ubuntu:~$ curl 10.0.0.137:8080/hello/10
Hello World: 10
gset@ubuntu:~$ curl 10.0.0.137:8080/add/100
Sum: 100
// Create ‘/xgf’ in ‘data-provider’, and share data between two the containers of ‘data-provider’ and ‘data consumer’
gset@ubuntu:~$ docker run -ti -v /xgf --name "data-provider" ubuntu:14.04
gset@ubuntu:~$ docker run -ti --volumes-from "data-provider" --name "data-consumer" ubuntu:14.04
gset@ubuntu:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b3275e7357f4 ubuntu:14.04 "/bin/bash" 2 minutes ago Up 2 minutes data-consumer
ed8643c1f926 ubuntu:14.04 "/bin/bash" 3 minutes ago Up 3 minutes data-provider
Use Case 3: Create images
gset@ubuntu:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
62b4975b2a8d ubuntu:14.04 "/bin/bash" 6 minutes ago Up 6 minutes 0.0.0.0:8000->80/tcp xgfwebserver
// Create a new image with the running or stoped container
gset@ubuntu:~$ docker commit -m "flask web server" -a "Richard Xue" 62b4975b2a8d flaskweb
sha256:5589ca92a091801dd8e35bcc1238a92a10b977dd32eb99b98422680fc0806203
gset@ubuntu:~$ docker inspect flaskweb
gset@ubuntu:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flaskweb latest 5589ca92a091 6 seconds ago 394MB
ubuntu 14.04 d6ed29ffda6b 8 days ago 221MB
// Create a new image with the docker file
gset@ubuntu:~$ cd xgf
gset@ubuntu:~/xgf$ gedit dockerfile
FROM flaskweb
CMD python hello.py
gset@ubuntu:~/xgf$ docker build -t flaskweb:0.0.1 .
gset@ubuntu:~$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
flaskweb 0.0.1 d3855d7f874b 50 seconds ago 394MB
flaskweb latest 4532c3c18668 2 minutes ago 394MB
ubuntu 14.04 d6ed29ffda6b 9 days ago 221MB
// Create a container with the new image: flaskweb:0.0.1
gset@ubuntu:~$ docker run -it -p 8001:80 --name xgfwebserver3 flaskweb:0.0.1
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
10.0.0.137 - - [26/Nov/2017 22:31:48] "GET / HTTP/1.1" 200 -
10.0.0.137 - - [26/Nov/2017 22:31:57] "GET / HTTP/1.1" 200 –
// Check the web service in the host
gset@ubuntu:~$ curl 10.0.0.137:8001
Hello World, this is Richard Xue
gset@ubuntu:~$ docker rmi dc63ece8d22e // delete the image - dc63ece8d22e
gset@ubuntu:~$ docker rm -f 0d020af8693a // delete the running container - 0d020af8693a
Use Case 4: Connect containers in different hosts with VxLAN
// Configuration in Host 1
gset@ubuntu:~$ docker start xgfwebserver
gset@ubuntu:~$ sudo ls -la /var/run/netns
gset@ubuntu:~$ sudo ip link add vxlan0 type vxlan id 42 dstport 4789 remote 10.0.0.139 local 10.0.0.137
gset@ubuntu:~$ ifconfig –a
gset@ubuntu:~$ sudo docker inspect --format '{{.State.Pid}}' xgfwebserver
2748
gset@ubuntu: ~$ sudo ln -s /proc/2748/ns/net /var/run/netns/xgfwebserver
gset@ubuntu: ~$ sudo ip netns list
gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link
gset@ubuntu:~$ sudo ip link set vxlan0 netns xgfwebserver
gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link set dev vxlan0 up
gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip addr add 10.110.0.1/24 dev vxlan0
gset@ubuntu:~$ docker attach xgfwebserver
root@62b4975b2a8d:/# ifconfig
vxlan0 Link encap:Ethernet HWaddr 06:a5:2d:28:03:b8
inet addr:10.110.0.1 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
root@62b4975b2a8d:/#
root@62b4975b2a8d:/# ping 10.110.0.1
PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.
root@62b4975b2a8d:/# ping 10.110.0.2
PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.
64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.840 ms
root@62b4975b2a8d:/#
root@62b4975b2a8d:/# python hello.py
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
10.110.0.2 - - [27/Nov/2017 02:17:30] "GET / HTTP/1.1" 200 -
10.110.0.2 - - [27/Nov/2017 02:17:41] "GET / HTTP/1.1" 200 -
root@62b4975b2a8d:/# route
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
default 172.17.0.1 0.0.0.0 UG 0 0 0 eth0
10.110.0.0 * 255.255.255.0 U 0 0 0 vxlan0
172.17.0.0 * 255.255.0.0 U 0 0 0 eth0
root@62b4975b2a8d:/# ip route
default via 172.17.0.1 dev eth0
10.110.0.0/24 dev vxlan0 proto kernel scope link src 10.110.0.1
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2
// Configuration in Host 2
gset@ubuntu:~$ docker start xgfwebserver
gset@ubuntu:~$ sudo ls -la /var/run/netns
gset@ubuntu:~$ sudo ip link add vxlan0 type vxlan id 42 dstport 4789 remote 10.0.0.137 local 10.0.0.139
gset@ubuntu:~$ ifconfig -a
gset@ubuntu:~$ sudo docker inspect --format '{{.State.Pid}}' xgfwebserver
2751
gset@ubuntu: ~$ sudo ln -s /proc/2751/ns/net /var/run/netns/xgfwebserver
gset@ubuntu: ~$ sudo ip netns list
gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link
gset@ubuntu:~$ sudo ip link set vxlan0 netns xgfwebserver
gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip link set dev vxlan0 up
gset@ubuntu:~$ sudo ip netns exec xgfwebserver ip addr add 10.110.0.2/24 dev vxlan0
gset@ubuntu:~$ docker attach xgfwebserver
root@62b4975b2a8d:/# ifconfig
vxlan0 Link encap:Ethernet HWaddr 96:66:72:e7:1e:42
inet addr:10.110.0.2 Bcast:0.0.0.0 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
root@62b4975b2a8d:/#
root@62b4975b2a8d:/# ping 10.110.0.1
PING 10.110.0.1 (10.110.0.1) 56(84) bytes of data.
64 bytes from 10.110.0.1: icmp_seq=1 ttl=64 time=0.301 ms
root@62b4975b2a8d:/# ping 10.110.0.2
PING 10.110.0.2 (10.110.0.2) 56(84) bytes of data.
64 bytes from 10.110.0.2: icmp_seq=1 ttl=64 time=0.029 ms
root@62b4975b2a8d:/#
root@62b4975b2a8d:/# apt-get install curl
root@62b4975b2a8d:/# curl 10.110.0.1:80
root@62b4975b2a8d:/#
root@62b4975b2a8d:/#
root@62b4975b2a8d:/# ip route
default via 172.17.0.1 dev eth0
10.110.0.0/24 dev vxlan0 proto kernel scope link src 10.110.0.2
172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2