【运维知识大神篇】运维人必学的Docker教程5(Namespace+Cgroup+OverlayFS原理+资源限制+自定义容器日志输出+Docker-compose安装+脚本全自动部署Docker)

本篇文章继续给大家分享docker的内容,在2007年前后,Linux内核支持Cgroup和NameSpace技术,这两种技术在增加对Linux的整体控制的同时,也成为了保持环境隔离的重要框架。此外Docker还有chroot和OverlayFS等核心技术,值得我们去研究其原理,为深刻理解docker打下基础。还有自定义镜像日志输出,Docker优劣势,Docker-compose安装,脚本自动部署docker+Docker-compose,为后面docker终章准备!

目录

Namespace原理详解

一、NET网络名称空间测试

二、Docker自定义网段

Cgroup原理详解

一、CPU资源限制案例

二、Docker的CPU限制案例

三、Docker的内存限制案例

OverlayFS原理详解

一、OverlayFS实现方式

二、案例演示

三、实际应用

自定义镜像的日志输出

一、案例测试

二、实际应用

三、ENNTRYPOINT脚本

Docker优势与劣势

Docker-compose

一、三种安装方式

全自动脚本部署(二进制安装docker+docker-compose)


Namespace原理详解

Linux Namespace是Linux系统提供的一种资源隔离机制,可实现系统资源隔离的列表如下

IPC        用于隔离进程间通信

MNT       用户隔离文件系统和提供硬盘挂载点

NET        用于隔离网络

PID         用于隔离进程ID

User       用于隔离用户和用户组

UTS        用于隔离HostName和DomianName

一、NET网络名称空间测试

1、创建一个名为linux的网络名称空间

[root@Docker01 ~]# ip netns add linux
[root@Docker01 ~]# ll /var/run/netns/linux 
-r--r--r-- 1 root root 0 Jun 11 20:23 /var/run/netns/linux
[root@Docker01 ~]# ip netns exec linux ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

2、启动linux的网络名称空间的网卡

[root@Docker01 ~]# ip netns exec linux ping 127.0.0.1
connect: Network is unreachable
[root@Docker01 ~]# ip netns exec linux ifconfig lo up
[root@Docker01 ~]# ip netns exec linux ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.120 ms

3、宿主机创建网络设备对

[root@Docker01 ~]# ip link add veth100 type veth peer name veth200
[root@Docker01 ~]# ip a    #发现多出了两块网卡
......
541: veth200@veth100: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether ee:f5:95:1b:5f:d9 brd ff:ff:ff:ff:ff:ff
542: veth100@veth200: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 56:42:58:18:2f:fd brd ff:ff:ff:ff:ff:ff
......

4、将veth200设备关联到咱们自定义的linux网络名称空间

[root@Docker01 ~]# ip link set veth200 netns linux
[root@Docker01 ~]# ip a    #发现宿主机的veth200不见了

5、将veth200设备配置IP地址

[root@Docker01 ~]# ip netns exec linux ip a
......
541: veth200@if542: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether ee:f5:95:1b:5f:d9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
[root@Docker01 ~]# ip netns exec linux ifconfig veth200 172.31.100.200/24 up
[root@Docker01 ~]# ip netns exec linux ip a
......
541: veth200@if542: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
    link/ether ee:f5:95:1b:5f:d9 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 172.31.100.200/24 brd 172.31.100.255 scope global veth200
       valid_lft forever preferred_lft forever

6、宿主机veth100也配置IP地址

[root@Docker01 ~]# ifconfig veth100 172.31.100.100/24 up
[root@Docker01 ~]# ifconfig veth100
veth100: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.31.100.100  netmask 255.255.255.0  broadcast 172.31.100.255
        inet6 fe80::5442:58ff:fe18:2ffd  prefixlen 64  scopeid 0x20<link>
        ether 56:42:58:18:2f:fd  txqueuelen 1000  (Ethernet)
        RX packets 6  bytes 516 (516.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 6  bytes 516 (516.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@Docker01 ~]# ping 172.31.100.200
PING 172.31.100.200 (172.31.100.200) 56(84) bytes of data.
64 bytes from 172.31.100.200: icmp_seq=1 ttl=64 time=0.249 ms

7、linux的网络名称空间ping宿主机的IP地址

[root@Docker01 ~]# ip netns exec linux ping 172.31.100.100
PING 172.31.100.100 (172.31.100.100) 56(84) bytes of data.
64 bytes from 172.31.100.100: icmp_seq=1 ttl=64 time=0.149 ms
[root@Docker01 ~]# ip netns exec linux ping 10.0.0.201    #未添加网关,无法跨网段ping通
connect: Network is unreachable
[root@Docker01 ~]# ip netns exec linux route add default gw 172.31.100.100    #配置默认网关
[root@Docker01 ~]# ip netns exec linux ping 10.0.0.201
PING 10.0.0.201 (10.0.0.201) 56(84) bytes of data.
64 bytes from 10.0.0.201: icmp_seq=1 ttl=64 time=0.062 ms
[root@Docker01 ~]# ip netns exec linux ping baidu.com    #ping不通外网,因为域名解析需要DNS
ping: baidu.com: Name or service not known
[root@Docker01 ~]# yum -y install tcpdump
......
[root@Docker01 ~]# ip netns exec linux ping 39.156.66.10    #此时ping百度的IP
PING 39.156.66.10 (39.156.66.10) 56(84) bytes of data.
^C
--- 39.156.66.10 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 2999ms
[root@Docker01 ~]# tcpdump -i veth100 -nn icmp    #tcpdump抓包工具可以检测到
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on veth100, link-type EN10MB (Ethernet), capture size 262144 bytes
20:48:20.417651 IP 172.31.100.200 > 39.156.66.10: ICMP echo request, id 61056, seq 1, length 64
20:48:21.417464 IP 172.31.100.200 > 39.156.66.10: ICMP echo request, id 61056, seq 2, length 64
20:48:22.417496 IP 172.31.100.200 > 39.156.66.10: ICMP echo request, id 61056, seq 3, length 64
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel
[root@Docker01 ~]# ip netns exec linux iptables -vnL -t nat    #如果想连接外网需要手动配置NAT
Chain PREROUTING (policy ACCEPT 1 packets, 84 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain INPUT (policy ACCEPT 1 packets, 84 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 10 packets, 695 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 10 packets, 695 bytes)
 pkts bytes target     prot opt in     out     source               destination 

8、测试完成后,删除名称空间

[root@Docker01 ~]# ll /var/run/netns
total 0
-r--r--r-- 1 root root 0 Jun 11 20:23 linux
[root@Docker01 ~]# ip netns del linux     #删除名称空间
[root@Docker01 ~]# ll /var/run/netns
total 0
[root@Docker01 ~]# ip link del veth100    #删除宿主机的虚拟网卡,其实已经跟着名称空间的删除自动删除了
Cannot find device "veth100"

二、Docker自定义网段

由上面可以看出docker自定义网段,底层做了 虚拟网卡,DNS解析,网管路由,监听端口等等操作。

Cgroup原理详解

Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)

这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)

在2007年时,因为在Linux内核中,容器(container)这个名词太过广泛,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中去。然后,其它开始了他的发展。

在Linux系统中能够控制的资源列表如下

cpu        主要限制进程的cpu使用率

cpuacct  可以统计cgroups中的进程的cpu使用报告

cpuset    可以为cgroups中的进程分配单独的cpu节点或者内存节点

memory  可以限制进程的memory使用量

blkio        可以限制进程的块设备io

devices   可以控制进程能够访问某些设备

net_cls    可以标记cgroups中进程的网络数据包,然后可以使用tc模块(traffic control)对数据包进行控制。

net_prio   这个子系统用来设计网络流量的优先级

freezer     可以挂起或者恢复cgroups中的进程。

ns             可以使不同cgroups下面的进程使用不同的namespace

hugetlb     这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。

一、CPU资源限制案例

1、查看Cgroup类型

[root@Docker01 ~]# mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpuacct,cpu)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_prio,net_cls)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

2、进入到CPU的挂载路径,并创建自定义的资源限制组 

[root@Docker01 ~]# cd /sys/fs/cgroup/cpu && mkdir linux && ls linux
cgroup.clone_children  cpu.cfs_quota_us
cgroup.event_control   cpu.rt_period_us
cgroup.procs           cpu.rt_runtime_us
cpuacct.stat           cpu.shares
cpuacct.usage          cpu.stat
cpuacct.usage_percpu   notify_on_release
cpu.cfs_period_us      tasks

3、使用stress压力测试

[root@Docker01 cpu]# yum -y install epel-release
[root@Docker01 cpu]# yum -y install stress
[root@Docker01 cpu]# stress -c 4 -v -t 20m    #压力测试20分钟,启动4个worker进程的CPU压测
stress: info: [61098] dispatching hogs: 4 cpu, 0 io, 0 vm, 0 hdd
stress: dbug: [61098] using backoff sleep of 12000us
stress: dbug: [61098] setting timeout to 1200s
stress: dbug: [61098] --> hogcpu worker 4 [61099] forked
stress: dbug: [61098] using backoff sleep of 9000us
stress: dbug: [61098] setting timeout to 1200s
stress: dbug: [61098] --> hogcpu worker 3 [61100] forked
stress: dbug: [61098] using backoff sleep of 6000us
stress: dbug: [61098] setting timeout to 1200s
stress: dbug: [61098] --> hogcpu worker 2 [61101] forked
stress: dbug: [61098] using backoff sleep of 3000us
stress: dbug: [61098] setting timeout to 1200s
stress: dbug: [61098] --> hogcpu worker 1 [61102] forked
^C

4、限制CPU的使用率在30%

[root@Docker01 ~]# cd /sys/fs/cgroup/cpu/linux && echo 30000 > cpu.cfs_quota_us

5、将任务的ID加入自定义限制组

[root@Docker01 linux]# ps -ef|grep stress|grep -v grep
root      61145  60914  0 21:01 pts/2    00:00:00 stress -c 4 -v -t 20m    #父进程
root      61146  61145 49 21:01 pts/2    00:00:49 stress -c 4 -v -t 20m
root      61147  61145 49 21:01 pts/2    00:00:49 stress -c 4 -v -t 20m
root      61148  61145 50 21:01 pts/2    00:00:49 stress -c 4 -v -t 20m
root      61149  61145 49 21:01 pts/2    00:00:49 stress -c 4 -v -t 20m
[root@Docker01 linux]# pwd    #在我们自定义的限制组里
/sys/fs/cgroup/cpu/linux
[root@Docker01 linux]# echo 61145 >> tasks    #将进程id依次加入tasks,查看top的信息,发现加入的id的cpu会被限制在30%内
[root@Docker01 linux]# echo 61146 >> tasks
[root@Docker01 linux]# echo 61147 >> tasks
[root@Docker01 linux]# echo 61148 >> tasks
[root@Docker01 linux]# echo 61149 >> tasks

#最终所有的进程id的总数被限制在了30%内
 61148 root      20   0    7312    100      0 R   8.0  0.0   2:45.67 stress                            
 61146 root      20   0    7312    100      0 R   7.6  0.0   2:40.83 stress                            
 61147 root      20   0    7312    100      0 R   7.6  0.0   2:43.36 stress                            
 61149 root      20   0    7312    100      0 R   7.3  0.0   2:47.92 stress

二、Docker的CPU限制案例

我准备了一个有压测工具的镜像

1、启动容器

[root@Docker01 ~]# docker run --name linux -d bosskoten/stress-tools:v1.0 stress -c 4 -v -t 20m

 2、查看容器状态

[root@Docker01 ~]# docker stats linux

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O   PIDS
a354115bb2be   linux     200.67%   124KiB / 3.84GiB    0.00%     656B / 0B   0B / 0B     5

......

3、更改CPU限制案例,修改CPU的内存限制为30%

[root@Docker01 cpu]# docker container update  --cpu-quota=30000 linux
linux

[root@Docker01 ~]# docker stats linux    #状态的CPU立马就被限制了
......
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O   PIDS
a354115bb2be   linux     29.93%    124KiB / 3.84GiB    0.00%     656B / 0B   0B / 0B     5

三、Docker的内存限制案例

1、启动容器

[root@Docker01 ~]# docker run --name linux -d bosskoten/stress-tools:v1.0  tail -f /etc/hosts

2、查看容器的状态

[root@Docker01 ~]# docker stats linux 

CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O   PIDS
e200246ea26b   linux     0.01%     56KiB / 3.84GiB     0.00%     656B / 0B   0B / 0B     1
......

3、更新内存限制,限制内存使用量为200M,注意,若此时内存使用量超过200M,则没有办法限制了

[root@Docker01 ~]# docker container update -m 209715200 --memory-swap 209715200  linux
linux
[root@Docker01 cpu]# docker stats linux
......
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O   PIDS
e200246ea26b   linux     0.01%     56KiB / 200MiB      0.03%     656B / 0B   0B / 0B     1
......

4、压测是否能超过200M内存

测试发现内存使用量可以无限解决200M,但是只要超过了任务就崩溃掉

[root@Docker01 ~]# docker exec linux stress --vm-bytes 60M -m 3 --vm-keep
stress: info: [18] dispatching hogs: 0 cpu, 0 io, 3 vm, 0 hdd
[root@Docker01 cpu]# docker stats linux
......
CONTAINER ID   NAME      CPU %     MEM USAGE / LIMIT   MEM %     NET I/O     BLOCK I/O   PIDS
e200246ea26b   linux     197.98%   180.2MiB / 200MiB   90.08%    656B / 0B   0B / 0B     5
......
[root@Docker01 ~]# docker exec linux stress --vm-bytes 70M -m 3 --vm-keep
stress: info: [8] dispatching hogs: 0 cpu, 0 io, 3 vm, 0 hdd
stress: FAIL: [8] (415) <-- worker 16 got signal 9
stress: WARN: [8] (417) now reaping child worker processes
stress: FAIL: [8] (451) failed run completed in 1s

OverlayFS原理详解

OverlayFS是一种堆叠文件系统,它依赖并建立在其它的文件系统上(例如,适合装小文件的ext4fs和适合装大文件的xfs等),并不直接参与磁盘空间结构的划分,仅仅将原来系统文件中的文件或者目录进行合并一起,最后向用户展示"合并"的文件是挂载在同一级目录,这就是联合挂载技术,在这个虚拟文件系统中,所有的文件和目录都可以看作是来自于不同的物理文件系统。因此,联合挂载技术可以用于将多个存储设备上的文件系统合并成一个单一的逻辑存储设备。相对于AUFS(<1.2早期使用的存储技术),OverlayFS速度更快,实现更简单。

Linux内核为Docker提供的OverlayFS驱动有两种,Overlay和Overlay2。而Overlay2是相对于Overlay的一种改进,在Inode利用率方面比Overlay更有效。

但是Overlay有环境要求,1、docker版本17.06.02+;2、宿主机文件系统需要是EXT4或XFS格式

一、OverlayFS实现方式

OverlayFS通过三个目录,lower目录,upper目录,以及work目录实现,lower,一般对应的是只读数据,upper可以进行读写操作的目录,work目录为工作基础目录,挂载后会自动创建一个work子目录(实际测试,手动卸载后该目录并不会被删除)该目录主要是存储一些临时存放的结果或中间数据的工作目录

值得注意的是,在使用的过程中其内容用户不可见,最后联合挂载完成给用户呈现的统一视图称为merged目录

OverlayFS结构分为三个层,LowerDir、Upperdir、MergedDir

LowerDir(只读)的image layer(镜像层),其实就是rootfs(临时的跟文件系统),在使用dockerfile构建镜像的时候,image layer可以分很多层,所以对应的lowerdir会很多

Lower包括两个层,一是系统的init,容器在启动的时候,默认的情况下,lower层是不能够修改内容的,但是用户有需求需要修改主机名与域名地址的时候,那么就需要添加init层的文件(hostname,resolv.conf,hosts,mtab等文件),用于解决此类问题;修改的内容只对当前的容器生效,而在docker commit提交为镜像的时候,并不会将init层提交;init文件存放的目录是/var/lib/docker/overlay2/<init id>/diff

二是容器的镜像层,也是不可修改的数据

Upperdir是读写层,是在lowerdir之上的一层,为读写层,容器在启动的时候会创建,所有对容器的修改,都是在这一层,比如容器启动写入的日志文件,或者是应用程序写入的临时文件

MergedDir,merged目录是容器的挂载点,在用户视角能看到的所有文件,都是在这一层展示的。

二、案例演示

1、创建工作目录

[root@Docker01 ~]# mkdir -pv /koten2023/lower{0..2} /koten2023/{upper,work,merged}
[root@Docker01 ~]# ll -R /koten2023/
/koten2023/:
total 0
drwxr-xr-x 2 root root 6 Jun 11 22:07 lower0
drwxr-xr-x 2 root root 6 Jun 11 22:07 lower1
drwxr-xr-x 2 root root 6 Jun 11 22:07 lower2
drwxr-xr-x 2 root root 6 Jun 11 22:07 merged
drwxr-xr-x 2 root root 6 Jun 11 22:07 upper
drwxr-xr-x 2 root root 6 Jun 11 22:07 work

/koten2023/lower0:
total 0

/koten2023/lower1:
total 0

/koten2023/lower2:
total 0

/koten2023/merged:
total 0

/koten2023/upper:
total 0

/koten2023/work:
total 0

2、挂载文件系统

不管是读层还是写层都挂载到合并层的目录

[root@Docker01 ~]# mount -t overlay overlay -o lowerdir=/koten2023/lower0:/koten2023/lower1:/koten2023/lower2,upperdir=/koten2023/upper,workdir=/koten2023/work  /koten2023/merged/
[root@Docker01 ~]# ll -R /koten2023/
/koten2023/:
total 0
drwxr-xr-x 2 root root  6 Jun 11 22:07 lower0
drwxr-xr-x 2 root root  6 Jun 11 22:07 lower1
drwxr-xr-x 2 root root  6 Jun 11 22:07 lower2
drwxr-xr-x 1 root root  6 Jun 11 22:07 merged
drwxr-xr-x 2 root root  6 Jun 11 22:07 upper
drwxr-xr-x 3 root root 18 Jun 11 22:13 work

/koten2023/lower0:
total 0

/koten2023/lower1:
total 0

/koten2023/lower2:
total 0

/koten2023/merged:
total 0

/koten2023/upper:
total 0

/koten2023/work:
total 0
d--------- 2 root root 6 Jun 11 22:13 work    #挂载之后显示了临时目录,但是并没有产生临时数据

/koten2023/work/work:
total 0

3、查看挂载信息

[root@Docker01 ~]# df -h
Filesystem      Size  Used Avail Use% Mounted on
......
overlay          49G   13G   37G  25% /koten2023/merged
[root@Docker01 ~]# mount | grep merged
overlay on /koten2023/merged type overlay (rw,relatime,lowerdir=/koten2023/lower0:/koten2023/lower1:/koten2023/lower2,upperdir=/koten2023/upper,workdir=/koten2023/work)

4、尝试在lower层写入准备初始数据

可以发现只读层加入了数据后,merged也有了数据

[root@Docker01 ~]# cp /etc/hosts /koten2023/lower0/
[root@Docker01 ~]# cp /etc/issue /koten2023/lower1/
[root@Docker01 ~]# cp /etc/resolv.conf /koten2023/lower2/
[root@Docker01 ~]# ll -R /koten2023/
/koten2023/:
total 0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower1
drwxr-xr-x 2 root root 25 Jun 11 22:16 lower2
drwxr-xr-x 1 root root  6 Jun 11 22:07 merged
drwxr-xr-x 2 root root  6 Jun 11 22:07 upper
drwxr-xr-x 3 root root 18 Jun 11 22:13 work

/koten2023/lower0:
total 4
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts

/koten2023/lower1:
total 4
-rw-r--r-- 1 root root 23 Jun 11 22:16 issue

/koten2023/lower2:
total 4
-rw-r--r-- 1 root root 51 Jun 11 22:16 resolv.conf

/koten2023/merged:
total 12
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts
-rw-r--r-- 1 root root  23 Jun 11 22:16 issue
-rw-r--r-- 1 root root  51 Jun 11 22:16 resolv.conf

/koten2023/upper:
total 0

/koten2023/work:
total 0
d--------- 2 root root 6 Jun 11 22:13 work

/koten2023/work/work:
total 0

5、尝试在upper层写入准备初始数据

发现写入层有了数据,merged层也有了数据

[root@Docker01 ~]# cp /etc/hostname /koten2023/upper/
[root@Docker01 ~]# ll /koten2023/ -R
/koten2023/:
total 0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower1
drwxr-xr-x 2 root root 25 Jun 11 22:16 lower2
drwxr-xr-x 1 root root 22 Jun 11 22:17 merged
drwxr-xr-x 2 root root 22 Jun 11 22:17 upper
drwxr-xr-x 3 root root 18 Jun 11 22:13 work

/koten2023/lower0:
total 4
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts

/koten2023/lower1:
total 4
-rw-r--r-- 1 root root 23 Jun 11 22:16 issue

/koten2023/lower2:
total 4
-rw-r--r-- 1 root root 51 Jun 11 22:16 resolv.conf

/koten2023/merged:
total 16
-rw-r--r-- 1 root root   9 Jun 11 22:17 hostname
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts
-rw-r--r-- 1 root root  23 Jun 11 22:16 issue
-rw-r--r-- 1 root root  51 Jun 11 22:16 resolv.conf

/koten2023/upper:
total 4
-rw-r--r-- 1 root root 9 Jun 11 22:17 hostname

/koten2023/work:
total 0
d--------- 2 root root 6 Jun 11 22:13 work

/koten2023/work/work:
total 0

6、尝试在merged目录写完数据,观察数据实际写入的应该是upper层

只读层和merged层都有

[root@Docker01 ~]# cp /etc/fstab /koten2023/merged/ 
[root@Docker01 ~]# ll /koten2023/ -R
/koten2023/:
total 0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower1
drwxr-xr-x 2 root root 25 Jun 11 22:16 lower2
drwxr-xr-x 1 root root 35 Jun 11 22:19 merged
drwxr-xr-x 2 root root 35 Jun 11 22:19 upper
drwxr-xr-x 3 root root 18 Jun 11 22:13 work

/koten2023/lower0:
total 4
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts

/koten2023/lower1:
total 4
-rw-r--r-- 1 root root 23 Jun 11 22:16 issue

/koten2023/lower2:
total 4
-rw-r--r-- 1 root root 51 Jun 11 22:16 resolv.conf

/koten2023/merged:
total 20
-rw-r--r-- 1 root root 501 Jun 11 22:19 fstab
-rw-r--r-- 1 root root   9 Jun 11 22:17 hostname
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts
-rw-r--r-- 1 root root  23 Jun 11 22:16 issue
-rw-r--r-- 1 root root  51 Jun 11 22:16 resolv.conf

/koten2023/upper:
total 8
-rw-r--r-- 1 root root 501 Jun 11 22:19 fstab
-rw-r--r-- 1 root root   9 Jun 11 22:17 hostname

/koten2023/work:
total 0
d--------- 2 root root 6 Jun 11 22:13 work

/koten2023/work/work:
total 0

7、重新挂载,但是不挂载upperdir层

[root@Docker01 ~]# umount /koten2023/merged
[root@Docker01 ~]# mount -t overlay overlay -o lowerdir=/koten2023/lower0:/koten2023/lower1:/koten2023/lower2,workdir=/koten2023/work  /koten2023/merged/
[root@Docker01 ~]# ll -R /koten2023/
/koten2023/:
total 0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower0
drwxr-xr-x 2 root root 19 Jun 11 22:16 lower1
drwxr-xr-x 2 root root 25 Jun 11 22:16 lower2
drwxr-xr-x 1 root root 19 Jun 11 22:16 merged
drwxr-xr-x 2 root root 35 Jun 11 22:19 upper
drwxr-xr-x 3 root root 18 Jun 11 22:13 work

/koten2023/lower0:
total 4
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts

/koten2023/lower1:
total 4
-rw-r--r-- 1 root root 23 Jun 11 22:16 issue

/koten2023/lower2:
total 4
-rw-r--r-- 1 root root 51 Jun 11 22:16 resolv.conf

/koten2023/merged:    #此时merged只能显示只读层
total 12
-rw-r--r-- 1 root root 158 Jun 11 22:16 hosts
-rw-r--r-- 1 root root  23 Jun 11 22:16 issue
-rw-r--r-- 1 root root  51 Jun 11 22:16 resolv.conf

/koten2023/upper:
total 8
-rw-r--r-- 1 root root 501 Jun 11 22:19 fstab
-rw-r--r-- 1 root root   9 Jun 11 22:17 hostname

/koten2023/work:
total 0
d--------- 2 root root 6 Jun 11 22:13 work

/koten2023/work/work:
total 0

8、再次尝试写入数据失败,因为没有读写层,只有只读层

[root@Docker01 ~]# cp /etc/os-release /koten2023/merged/
cp: cannot create regular file ‘/koten2023/merged/os-release’: Read-only file system

三、实际应用

在安装软件后清除缓存时,多条RUN就没有效果,但是一条就可以清除,原因就是因为多个镜像层,后面的RUN虽然在自己的视角已经把它删掉了,但是上面的镜像层还有数据,合并在一起展示给用户后镜像体积并没有变小

[root@Docker01 ~]# cat dockerfile 
# 指定基础镜像
FROM centos:7
 
# 在容器中运行命令
RUN curl -s -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
RUN curl -s -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
RUN yum -y install nginx
RUN rm -rf /var/cache/yum/
 
# 容器启动时的命令
CMD ["nginx","-g","daemon off"]
[root@Docker01 ~]# docker build -t koten-nginx:v1.0 .
[root@Docker01 ~]# docker images
REPOSITORY           TAG       IMAGE ID       CREATED          SIZE
koten-nginx          v1.0      8aec38ab7701   30 minutes ago   486MB

使用单个RUN,镜像就变小了

[root@Docker01 ~]# cat dockerfile
# 指定基础镜像
FROM centos:7
 
# 在容器中运行命令
RUN curl -s -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \
    curl -s -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo && \
    yum -y install nginx && \
    rm -rf /var/cache/yum/
 
# 容器启动时的命令
CMD ["nginx","-g","daemon off"]
[root@Docker01 ~]# docker build -t koten-nginx:v2.0 .
[root@Docker01 ~]# docker images
REPOSITORY           TAG       IMAGE ID       CREATED          SIZE
koten-nginx          v2.0      5ffea6117d49   6 minutes ago    262MB
koten-nginx          v1.0      8aec38ab7701   30 minutes ago   486MB

自定义镜像的日志输出

当我们使用nginx镜像启动容器的时候,我们curl下URL,用docker logs -f <容器名>,查看日志,发现可以看到我们的日志实时输出;但是当我们自己构建了一个镜像,用该命令查看日志的时候发现并没有输出,原因是在镜像中并没有将日志做为软连接,链接到/dev/stdout或者/dev/stderr,只有输入进/dev/stdout或者/dev/stderr的数据,才会在容器日志中输出来,我们写日志写到nginx的access.log的文件,做了链接/dev/stdout的操作后,也输入到/dev/stdout中,我们也就能看到日志了。

只是提供这样看日志的方式,以后可以根据容器目录去做日志收集

一、案例测试

启动alpine容器

[root@Docker01 ~]# docker run -it alpine sh
/ # cat 1.sh 
for i in `seq 300` 
do 
   echo $i > /dev/stdout
   sleep 1 
   echo "$i + error info ..." > /dev/stderr
done

/ # sh 1.sh 
1
1 + error info ...
2
2 + error info ...
3
3 + error info ...
4
4 + error info ...
5
5 + error info ...
6
^C
/ # 

与此同时查看输出日志

[root@Docker01 cpu]# docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED          STATUS          PORTS     NAMES
312be4f66d19   alpine    "sh"      33 seconds ago   Up 31 seconds             focused_burnell
[root@Docker01 cpu]# docker logs -f focused_burnell 
/ # vi 1.sh
/ # cat 1.sh 
for i in `seq 300` 
do 
   echo $i > /dev/stdout
   sleep 1 
   echo "$i + error info ..." > /dev/stderr
done

^[[12;5R^[[13;5R

/ # sh 1.sh 
^[[16;5R1
1 + error info ...
2
2 + error info ...
3
3 + error info ...
4
4 + error info ...
5
5 + error info ...
6
^C
^C

二、实际应用

构建一个nginx服务的镜像

[root@Docker01 ~]# cat dockerfile_nginx
FROM centos:7
 
# 在容器中运行命令
RUN curl -s -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \
    curl -s -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo && \
    yum -y install nginx && \
    rm -rf /var/cache/yum/
 
# 容器启动时的命令
CMD ["nginx","-g","daemon off;"]

[root@Docker01 ~]# docker build -t nginx:v0.1 -f dockerfile_nginx .

测试curl,发现容器中nginx日志显示记录,但是容器日志不显示。

修改dockerfile,做软连接,再次测试,日志成功输出,网络慢,中间查看日志流程不再展示。

[root@Docker01 ~]# cat dockerfile_nginx
FROM centos:7
 
# 在容器中运行命令
RUN curl -s -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \
    curl -s -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo && \
    yum -y install nginx && \
    rm -rf /var/cache/yum/
 
RUN rm -rf /var/log/nginx/* && \
    ln -svf /dev/stdout /var/log/nginx/access.log && \
    ln -svf /dev/stderr /var/log/nginx/error.log

# 容器启动时的命令
CMD ["nginx","-g","daemon off;"]

[root@Docker01 ~]# docker build -t nginx:v0.1 -f dockerfile_nginx .

三、ENNTRYPOINT脚本

有时候可以看到有些镜像在看日志的时候会看到一些容器启动的信息,实际原理一致,是通过运行ENNTRYPOINT脚本去判断输出哪些信息,我们自己也可以去写这样的脚本,在容器启动的时候指定启动ENNTRYPOINT脚本。

Docker优势与劣势

优势

1、Docker让软件开发者与维护人员可以非常方便的启动应用程序以及将程序的依赖, 包到一个容器中,然后启动Docker应用到支持Docker的系统平台中,就可以实现应用虚拟化;
2、Docker镜像中包含了应用运行环境和配置文件,所以Docker可以简化部署多种应用的工作。比如说Web应用、后台应用(Java/C++)、数据库应用、Hadoop集群、消息队列等等都可以打包成一个个独立的Docker应用镜像来部署;
3、提升宿主机(物理服务器/云虚拟机), 系统资源的利用率;
4、docker在迁移过程中是非常方便的。

劣势

1、基本的Docker网络管理模式比较简单,主要是基于系统使用Namespace隔离;
2、与其他系统的网络连通性,使用自定义的地址网段,需要借助其他插件实现与其他网段的互通, 提高了网络的整体复杂度;
3、容器中应用程序日志不方便查看与收集;
4、容器中无法运行Windows。

综上所述,我们说容器技术不能完全取代虚拟机,而是虚拟机的一种互补技术。

Docker-compose

Docker-compose指的是在单机节点上批量管理一组容器的方法,目前比较主流的单机编排工具

参考连接:https://docs.docker.com/compose/compose-file/
https://docs.docker.com/compose/compose-file/compose-versioning/

一、三种安装方式

1、使用epel源,直接安装(缺点是版本低,docker-compose version 1.18.0)

yum -y install epel-release
yum -y install docker-compose

2、给docker打补丁安装(先安装docker,再docker compose打补丁,通常是最新版)

curl -s -o /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo
yum -y install docker-compose-plugin
vim /root/.bashrc 
...
alias docker-compose='docker compose'

source /root/.bashrc 

yum -y remove docker-compose-plugin  # 卸载软件

3、基于二进制安装(需要在github下载软件包,速度比较慢,源不稳定,但是可以和docker的安装部署写进脚本,实现在任何linux系统的安装部署)

当github不稳定,无法访问的时候,也可以用国内镜像去下载,但是注意这种国内镜像下载公开项目可以,不要去登录,危险!

curl -SL https://github.com/docker/compose/releases/download/v2.18.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose

全自动脚本部署(二进制安装docker+docker-compose)

网络情况良好并且安装源可用,可以直接运行脚本,也可以提前下载放到download目录下

还有一些需要准备的配置文件,镜像加速,代码补全,开机自启动(文末有完整的download目录下载链接)

[root@Docker01 download]# ls
daemon.json          docker-24.0.2.tgz
docker               docker-compose-v2.17.3
docker-19.03.15.tgz  docker-compose-v2.18.1
docker-20.10.24.tgz  docker.service

编写脚本,需要安装或卸载脚本的话指定版本即可

[root@Docker01 ~]# cat deploy_docker.sh
#!/bin/bash

# DOCKER_VERSION=24.0.2
DOCKER_VERSION=20.10.24
# DOCKER_COMPOSE_VERSION=2.17.3
DOCKER_COMPOSE_VERSION=2.18.1
FILENAME=docker-${DOCKER_VERSION}.tgz
DOCKER_COMPOSE_FILE=docker-compose-v${DOCKER_COMPOSE_VERSION}
URL=https://download.docker.com/linux/static/stable/x86_64
DOCKER_COMPOSE_URL=https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64
# DOCKER_COMPOSE_URL=https://hub.nuaa.cf/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-linux-x86_64
DOWNLOAD=./download
BASE_DIR=/koten/softwares

# 判断是否下载了docker-compose
function prepare(){
   # 判断下载目录是否创建了,没有创建创建
   if [ ! -d ${DOWNLOAD} ]; then
      mkdir ${DOWNLOAD}
   fi

   # 判断软件安装目录是否创建了,没有创建创建
   if [ ! -d ${BASE_DIR} ]; then
      mkdir -p ${BASE_DIR}
   fi

   # 判断是否下载docker-compose文件
   if [ ! -f ${DOWNLOAD}/${DOCKER_COMPOSE_FILE} ]; then
      wget -T 3  -t 2 ${DOCKER_COMPOSE_URL} -O ${DOWNLOAD}/${DOCKER_COMPOSE_FILE}
   fi
   
   if [ $? != 0 ];then
    # rm -f ${DOWNLOAD}/${DOCKER_COMPOSE_FILE}
     echo "不好意思,由于网络波动原因,无法下载${DOCKER_COMPOSE_URL}软件包,程序已退出!请稍后再试......"
     exit 100
   fi

   # 给脚本添加执行权限
   chmod +x ${DOWNLOAD}/${DOCKER_COMPOSE_FILE}

   # 判断docker文件是否存在,若不存在则下载软件包
   if [ ! -f ${DOWNLOAD}/${FILENAME} ]; then
      wget ${URL}/${FILENAME} -O ${DOWNLOAD}/${FILENAME}
   fi

   if [ $? != 0 ];then
    # rm -f ${DOWNLOAD}/${FILENAME}
     echo "不好意思,由于网络波动原因,无法下载${URL}/${FILENAME}软件包,程序已退出!请稍后再试......"
     exit 100
   fi
}


# 定义安装函数
function InstallDocker(){
    prepare

    # 判断解压路径是否存在
    if [ ! -d ${BASE_DIR} ]; then
      install -d ${BASE_DIR}
    fi
    
    # 解压软件包到指定目录
    tar xf ${DOWNLOAD}/${FILENAME} -C ${BASE_DIR}
    
    # 安装docker-compose
    cp $DOWNLOAD/${DOCKER_COMPOSE_FILE} ${BASE_DIR}/docker/docker-compose

    # 创建软连接
    ln -svf ${BASE_DIR}/docker/* /usr/bin/
    
    # 自动补全功能
    cp $DOWNLOAD/docker /usr/share/bash-completion/completions/docker
    source /usr/share/bash-completion/completions/docker
    
    # 配置镜像加速
    install -d /etc/docker
    cp $DOWNLOAD/daemon.json /etc/docker/daemon.json
    

    # 开机自启动脚本
    cp download/docker.service /usr/lib/systemd/system/docker.service
    systemctl daemon-reload
    systemctl enable --now docker
    docker version
    docker-compose version
    tput setaf 3
    echo "安装成功,欢迎使用二进制docker安装脚本,欢迎下次使用!"
    tput setaf 2
}


# 卸载docker
function UninstallDocker(){
  # 停止docker服务
  systemctl disable --now docker

  # 卸载启动脚本
  rm -f /usr/lib/systemd/system/docker.service

  # 清空程序目录
  rm -rf ${BASE_DIR}/docker

  # 清空数据目录
  rm -rf /var/lib/{docker,containerd} 

  # 清除符号链接
  rm -f /usr/bin/{containerd,containerd-shim,containerd-shim-runc-v2,ctr,docker,dockerd,docker-init,docker-proxy,runc}

  # 使得终端变粉色
  tput setaf 5
  echo "卸载成功,欢迎再次使用二进制docker安装脚本!"
  tput setaf 7
}


# 程序的入口函数
function main(){
   # 判断传递的参数
   case $1 in
     "install")
      InstallDocker
      ;;
     "remove")
      UninstallDocker
      ;;
     *)
       echo "Invalid parameter, Usage: $0 install|remove"
       ;;
   esac
}

# 向入口函数传参
main $1 

目录结构如下

[root@Docker01 ~]# tree
.
├── deploy_docker.sh
└── download
    ├── daemon.json
    ├── docker
    ├── docker-19.03.15.tgz
    ├── docker-20.10.24.tgz
    ├── docker-24.0.2.tgz
    ├── docker-compose-v2.17.3
    ├── docker-compose-v2.18.1
    └── docker.service

1 directory, 9 files

运行脚本并查看效果,如果自动补全不能用,退出xshell会话重新进,脚本成功部署!

[root@Docker01 ~]# sh deploy_docker.sh remove
Failed to execute operation: No such file or directory
卸载成功,欢迎再次使用二进制docker安装脚本!
[root@Docker01 ~]# sh deploy_docker.sh install
......
Server: Docker Engine - Community
 Engine:
  Version:          20.10.24
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.19.7
  Git commit:       5d6db84
  Built:            Tue Apr  4 18:23:02 2023
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.21
  GitCommit:        3dce8eb055cbb6872793272b4f20ed16117344f8
 runc:
  Version:          1.1.5
  GitCommit:        v1.1.5-0-gf19387a6
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
Docker Compose version v2.18.1
安装成功,欢迎使用二进制docker安装脚本,欢迎下次使用!
[root@Docker01 ~]# source /usr/share/bash-completion/completions/docker    #激活自动补全

Docker一键部署download目录下载链接:链接:https://pan.baidu.com/s/1XRE2yZokVrd01DjBWLWbhg?pwd=91n0

我是koten,10年运维经验,持续分享运维干货,感谢大家的阅读和关注!

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是koten

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值