mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2026-05-10 19:54:28 +08:00
mod
This commit is contained in:
296
极客时间专栏/容器实战高手课/结束语/加餐福利 | 课后思考题答案合集.md
Normal file
296
极客时间专栏/容器实战高手课/结束语/加餐福利 | 课后思考题答案合集.md
Normal file
@@ -0,0 +1,296 @@
|
||||
<audio id="audio" title="加餐福利 | 课后思考题答案合集" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/61/f1/61f34e746b673b086b17bdd71a76e5f1.mp3"></audio>
|
||||
|
||||
你好,我是程远,好久不见。
|
||||
|
||||
距离我们的专栏更新结束,已经过去了不少时间。我仍然会在工作之余,到这门课的留言区转一转,回答同学的问题。大部分的疑问,我都通过留言做了回复。
|
||||
|
||||
除了紧跟更新的第一批同学,也很开心有更多新朋友加入到这个专栏的学习中。那课程的思考题呢,为了给你留足思考和研究的时间,我选择用加餐的方式,给你提供参考答案。
|
||||
|
||||
这里我想和你说明的是,我这里给你提供的参考答案,都是我能够直接给你特定答案的问题。至于操作类的题目,有的我引用了同学回复的答案。
|
||||
|
||||
另外一类操作题,是为了帮你巩固课程内容知识的,相信你可以从课程正文里找到答案。我还是建议你自己动手实战,这样你的收获会更大。
|
||||
|
||||
## 必学部分思考题
|
||||
|
||||
[第2讲](https://time.geekbang.org/column/article/309423)
|
||||
|
||||
Q:对于这一讲的最开始,有这样一个C语言的init进程,它没有注册任何信号的handler。如果我们从Host Namespace向它发送SIGTERM,会发生什么情况呢?
|
||||
|
||||
A:即使在宿主机上向容器1号进程发送SIGTERM,在1号进程没有注册handler的情况下,这个进程也不能被杀死。
|
||||
|
||||
这个问题的原因是这样的:开始要看内核里的那段代码,“ !(force && sig_kernel_only(sig))”,
|
||||
|
||||
虽然由不同的namespace发送信号, 虽然force是1了,但是sig_kernel_only(sig)对于SIGTERM来说还是0,这里是个&&, 那么 !(1 && 0) = 1。
|
||||
|
||||
```
|
||||
#define sig_kernel_only(sig) siginmask(sig, SIG_KERNEL_ONLY_MASK)
|
||||
#define SIG_KERNEL_ONLY_MASK (\
|
||||
rt_sigmask(SIGKILL) | rt_sigmask(SIGSTOP))
|
||||
|
||||
```
|
||||
|
||||
[第3讲](https://time.geekbang.org/column/article/310060)
|
||||
|
||||
Q:如果容器的init进程创建了子进程B,B又创建了自己的子进程C。如果C运行完之后,退出成了僵尸进程,B进程还在运行,而容器的init进程还在不断地调用waitpid(),那C这个僵尸进程可以被回收吗?
|
||||
|
||||
A:这道题可以参考下面两位同学的回答。
|
||||
|
||||
Geek2014用户的回答:
|
||||
|
||||
>
|
||||
这时C是不会被回收的,只有等到B也被杀死,C这个僵尸进程也会变成孤儿进程,被init进程收养,进而被init的wait机制清理掉。
|
||||
|
||||
|
||||
莫名同学的回答:
|
||||
|
||||
>
|
||||
C应该不会被回收,waitpid仅等待直接children的状态变化。
|
||||
|
||||
|
||||
>
|
||||
为什么先进入僵尸状态而不是直接消失?觉得是留给父进程一次机会,查看子进程的PID、终止状态(退出码、终止原因,比如是信号终止还是正常退出等)、资源使用信息。如果子进程直接消失,那么父进程没有机会掌握子进程的具体终止情况。
|
||||
|
||||
|
||||
>
|
||||
一般情况下,程序逻辑可能会依据子进程的终止情况做出进一步处理:比如 Nginx Master 进程获知 Worker 进程异常退出,则重新拉起来一个Worker进程。
|
||||
|
||||
|
||||
[第4讲](https://time.geekbang.org/column/article/310804)
|
||||
|
||||
Q:请你回顾一下基本概念中最后的这段代码,你可以想一想,在不做编译运行的情况下,它的输出是什么?
|
||||
|
||||
```
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
|
||||
typedef void (*sighandler_t)(int);
|
||||
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
if (signo == SIGTERM) {
|
||||
printf("received SIGTERM\n\n");
|
||||
// Set SIGTERM handler to default
|
||||
signal(SIGTERM, SIG_DFL);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
//Ignore SIGTERM, and send SIGTERM
|
||||
// to process itself.
|
||||
|
||||
signal(SIGTERM, SIG_IGN);
|
||||
printf("Ignore SIGTERM\n\n");
|
||||
kill(0, SIGTERM);
|
||||
|
||||
//Catch SIGERM, and send SIGTERM
|
||||
// to process itself.
|
||||
signal(SIGTERM, sig_handler);
|
||||
printf("Catch SIGTERM\n");
|
||||
kill(0, SIGTERM);
|
||||
|
||||
|
||||
//Default SIGTERM. In sig_handler, it sets
|
||||
//SIGTERM handler back to default one.
|
||||
printf("Default SIGTERM\n");
|
||||
kill(0, SIGTERM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
A:可以参考用户geek 2014同学的答案。输出结果如下:
|
||||
|
||||
Ignore SIGTERM<br>
|
||||
Catch SIGTERM<br>
|
||||
received SIGTERM<br>
|
||||
Default SIGTERM
|
||||
|
||||
[第5讲](https://time.geekbang.org/column/article/311054)
|
||||
|
||||
Q:我们还是按照文档中定义的控制组目录层次结构图,然后按序执行这几个脚本:
|
||||
|
||||
- [create_groups.sh](https://github.com/chengyli/training/blob/main/cpu/cgroup_cpu/create_groups.sh)
|
||||
- [update_group1.sh](https://github.com/chengyli/training/blob/main/cpu/cgroup_cpu/update_group1.sh)
|
||||
- [update_group4.sh](https://github.com/chengyli/training/blob/main/cpu/cgroup_cpu/update_group4.sh)
|
||||
- [update_group3.sh](https://github.com/chengyli/training/blob/main/cpu/cgroup_cpu/update_group3.sh)
|
||||
|
||||
那么,在一个4个CPU的节点上,group1/group3/group4里的进程,分别会被分配到多少CPU呢?
|
||||
|
||||
A:分配比例是: 2 : 0.5 : 1.5
|
||||
|
||||
**可以参考geek 2014的答案:**
|
||||
|
||||
>
|
||||
group1 的shares为1024,quota 3.5,尝试使用4,
|
||||
|
||||
|
||||
>
|
||||
group2的shares默认为1024,quota设置为-1,不受限制,也即是,如果CPU上只有group2的话,那么group2可以使用完所有的CPU(实际上根据group3和group4,group2最多也就能用到1.5+3.5 core)
|
||||
|
||||
|
||||
>
|
||||
故而,group1和group2各分配到2。把group2分到的2CPU,看作总量,再次分析group3和group4。group3和group3尝试使用的总量超过2,所以按照shares比例分配,group3使用1/(1+3) * 2 = 0.5,group4使用3/(1+3) * 2 = 1.5
|
||||
|
||||
|
||||
[第6讲](https://time.geekbang.org/column/article/313255)
|
||||
|
||||
Q:写一个小程序,在容器中执行,它可以显示当前容器中所有进程总的CPU使用率。
|
||||
|
||||
A:上邪忘川的回答可以作为一个参考。
|
||||
|
||||
```
|
||||
#!/bin/bash
|
||||
cpuinfo1=$(cat /sys/fs/cgroup/cpu,cpuacct/cpuacct.stat)
|
||||
utime1=$(echo $cpuinfo1|awk '{print $2}')
|
||||
stime1=$(echo $cpuinfo1|awk '{print $4}')
|
||||
sleep 1
|
||||
cpuinfo2=$(cat /sys/fs/cgroup/cpu,cpuacct/cpuacct.stat)
|
||||
utime2=$(echo $cpuinfo2|awk '{print $2}')
|
||||
stime2=$(echo $cpuinfo2|awk '{print $4}')
|
||||
cpus=$((utime2+stime2-utime1-stime1))
|
||||
echo "${cpus}%"
|
||||
|
||||
```
|
||||
|
||||
[第8讲](https://time.geekbang.org/column/article/315468)
|
||||
|
||||
Q:在我们的例子[脚本](https://github.com/chengyli/training/blob/main/memory/oom/start_container.sh)基础上,你可以修改一下,在容器刚一启动,就在容器对应的Memory Cgroup中禁止OOM,看看接下来会发生什么?
|
||||
|
||||
A:通过“**memory.oom_control**”禁止OOM后,在容器中的进程不会发生OOM,但是也无法申请出超过“memory.limit_in_bytes”内存。
|
||||
|
||||
```
|
||||
# cat start_container.sh
|
||||
#!/bin/bash
|
||||
docker stop mem_alloc;docker rm mem_alloc
|
||||
|
||||
docker run -d --name mem_alloc registry/mem_alloc:v1
|
||||
|
||||
sleep 2
|
||||
CONTAINER_ID=$(sudo docker ps --format "{{.ID}}\t{{.Names}}" | grep -i mem_alloc | awk '{print $1}')
|
||||
echo $CONTAINER_ID
|
||||
|
||||
CGROUP_CONTAINER_PATH=$(find /sys/fs/cgroup/memory/ -name "*$CONTAINER_ID*")
|
||||
echo $CGROUP_CONTAINER_PATH
|
||||
|
||||
echo 536870912 > $CGROUP_CONTAINER_PATH/memory.limit_in_bytes
|
||||
echo 1 > $CGROUP_CONTAINER_PATH/memory.oom_control
|
||||
cat $CGROUP_CONTAINER_PATH/memory.limit_in_bytes
|
||||
|
||||
```
|
||||
|
||||
[第10讲](https://time.geekbang.org/column/article/317216)
|
||||
|
||||
Q:在一个有Swap分区的节点上用Docker启动一个容器,对它的Memory Cgroup控制组设置一个内存上限N,并且将memory.swappiness设置为0。这时,如果在容器中启动一个不断读写文件的程序,同时这个程序再申请1/2N的内存,请你判断一下,Swap分区中会有数据写入吗?
|
||||
|
||||
A:Memory Cgroup参数memory.swappiness起到局部控制的作用,因为已经设置了memory.swappiness参数,全局参数swappiness参数失效,那么容器里就不能使用swap了。
|
||||
|
||||
[第11讲](https://time.geekbang.org/column/article/318173)
|
||||
|
||||
Q:在这一讲OverlayFS的[例子](https://github.com/chengyli/training/blob/main/filesystem/overlayfs/test_overlayfs.sh)的基础上,建立2个lowerdir的目录,并且在目录中建立相同文件名的文件,然后一起做一个overlay mount,看看会发生什么?
|
||||
|
||||
A:这里引用上邪忘川同学的实验结果。
|
||||
|
||||
实验过程如下,结果是lower1目录中的文件覆盖了lower2中同名的文件, 第一个挂载的目录优先级比较高
|
||||
|
||||
```
|
||||
[[root@localhost ~]# cat overlay.sh
|
||||
#!/bin/bash
|
||||
|
||||
umount ./merged
|
||||
rm upper lower1 lower2 merged work -r
|
||||
|
||||
mkdir upper lower1 lower2 merged work
|
||||
echo "I'm from lower1!" > lower1/in_lower.txt
|
||||
echo "I'm from lower2!" > lower2/in_lower.txt
|
||||
echo "I'm from upper!" > upper/in_upper.txt
|
||||
# `in_both` is in both directories
|
||||
echo "I'm from lower1!" > lower1/in_both.txt
|
||||
echo "I'm from lower2!" > lower2/in_both.txt
|
||||
echo "I'm from upper!" > upper/in_both.txt
|
||||
|
||||
sudo mount -t overlay overlay \
|
||||
-o lowerdir=./lower1:./lower2,upperdir=./upper,workdir=./work \
|
||||
./merged
|
||||
[root@localhost ~]# sh overlay.sh
|
||||
[root@localhost ~]# cat merged/in_lower.txt
|
||||
I'm from lower1!
|
||||
|
||||
```
|
||||
|
||||
[第12讲](https://time.geekbang.org/column/article/318978)
|
||||
|
||||
Q:在正文知识详解的部分,我们使用"xfs_quota"给目录打了project ID并且限制了文件写入的数据量。那么在做完限制之后,我们是否能用xfs_quota命令,查询到被限制目录的project ID和限制的数据量呢?
|
||||
|
||||
A:xfs_quota不能直接得到一个目录的quota大小的限制,只可以看到project ID上的quota限制,不过我们可以用[这段程序](https://github.com/chengyli/training/blob/main/filesystem/quota/get_projectid.c)来获得目录对应的project ID。
|
||||
|
||||
```
|
||||
# xfs_quota -x -c 'report -h /'
|
||||
...
|
||||
Project ID Used Soft Hard Warn/Grace
|
||||
---------- ---------------------------------
|
||||
#0 105.6G 0 0 00 [------]
|
||||
#101 0 0 10M 00 [------]
|
||||
|
||||
# ./get_proj /tmp/xfs_prjquota
|
||||
Dir: /tmp/xfs_prjquota projectid is 101
|
||||
|
||||
```
|
||||
|
||||
[第13讲](https://time.geekbang.org/column/article/320123)
|
||||
|
||||
Q:这是一道操作题,通过这个操作你可以再理解一下 blkio Cgroup与 Buffered I/O的关系。
|
||||
|
||||
在Cgroup V1的环境里,我们在blkio Cgroup V1的例子基础上,把fio中“-direct=1”参数去除之后,再运行fio,同时运行iostat查看实际写入磁盘的速率,确认Cgroup V1 blkio无法对Buffered I/O限速。
|
||||
|
||||
A: 这是通过iostat看到磁盘的写入速率,是可以突破cgroup V1 blkio中的限制值的。
|
||||
|
||||
[第17讲](https://time.geekbang.org/column/article/324122)
|
||||
|
||||
Q:在这节课的最后,我提到“由于ipvlan/macvlan网络接口直接挂载在物理网络接口上,对于需要使用iptables规则的容器,比如Kubernetes里使用service的容器,就不能工作了”,请你思考一下这个判断背后的具体原因。
|
||||
|
||||
A:ipvlan/macvlan工作在网络2层,而iptables工作在网络3层。所以用ipvlan/macvlan为容器提供网络接口,那么基于iptables的service服务就不工作了。
|
||||
|
||||
[第18讲](https://time.geekbang.org/column/article/324357)
|
||||
|
||||
Q:在这一讲中,我们提到了Linux内核中的tcp_force_fast_retransmit()函数,那么你可以想想看,这个函数中的tp->recording和内核参数 /proc/sys/net/ipv4/tcp_reordering是什么关系?它们对数据包的重传会带来什么影响?
|
||||
|
||||
```
|
||||
static bool tcp_force_fast_retransmit(struct sock *sk)
|
||||
{
|
||||
struct tcp_sock *tp = tcp_sk(sk);
|
||||
|
||||
return after(tcp_highest_sack_seq(tp),
|
||||
tp->snd_una + tp->reordering * tp->mss_cache);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
A: 在TCP链接建立的时候,tp->reordering默认值是从/proc/sys/net/ipv4/tcp_reordering(默认值为3)获取的。之后根据网络的乱序情况,进行动态调整,最大可以增长到/proc/sys/net/ipv4/tcp_max_reordering (默认值为300)的大小。
|
||||
|
||||
[第20讲](https://time.geekbang.org/column/article/327107)
|
||||
|
||||
Q:我在这一讲里提到了rootless container,不过对于rootless container的支持,还存在着不少的难点,比如容器网络的配置、Cgroup的配置,你可以去查阅一些资料,看看podman是怎么解决这些问题的。
|
||||
|
||||
A:可以阅读一下[这篇文档](https://github.com/containers/podman/blob/master/rootless.md)。
|
||||
|
||||
## 专题加餐
|
||||
|
||||
[专题03](https://time.geekbang.org/column/article/340142)
|
||||
|
||||
Q:我们讲ftrace实现机制时,说过内核中的“inline函数”不能被ftrace到,你知道这是为什么吗?那么内核中的“static函数”可以被ftrace追踪到吗?
|
||||
|
||||
A:inline函数在编译的时候被展开了,所以不能被ftrace到。而static函数需要看情况,如果加了编译优化参数“-finline-functions-called-once”,对于只被调用到一次的static函数也会当成inline函数处理,那么也不能被ftrace追踪到了。
|
||||
|
||||
[专题04](https://time.geekbang.org/column/article/340934)
|
||||
|
||||
Q:想想看,当我们用kprobe为一个内核函数注册了probe之后,怎样能看到对应内核函数的第一条指令被替换了呢?
|
||||
|
||||
A:**首先可以参考莫名同学的答案:**
|
||||
|
||||
>
|
||||
关于思考题,想到一个比较笨拙的方法:gdb+qemu调试内核。先进入虚拟机在某个内核函数上注册一个kprobe,然后gdb远程调试内核,查看该内核函数的汇编指令(disass)是否被替换。应该有更简单的方法,这方面了解不深。
|
||||
|
||||
|
||||
另外,我们用gdb远程调试内核看也可以。还可以通过 /proc/kallsyms找到函数的地址,然后写个kernel module把从这个地址开始后面的几个字节dump出来,比较一下probe函数注册前后的值。
|
||||
119
极客时间专栏/容器实战高手课/结束语/用户故事 | 莫名:相信坚持的力量,终会厚积薄发.md
Normal file
119
极客时间专栏/容器实战高手课/结束语/用户故事 | 莫名:相信坚持的力量,终会厚积薄发.md
Normal file
@@ -0,0 +1,119 @@
|
||||
<audio id="audio" title="用户故事 | 莫名:相信坚持的力量,终会厚积薄发" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/90/85/90b5359yy1f17b63f9bed65f0a733f85.mp3"></audio>
|
||||
|
||||
你好,我是莫名同学,坐标杭州。
|
||||
|
||||
我已经工作六年多了,曾经参与过对象存储、大数据等产品的开发。最近两年,我开始接触容器云平台系统开发与性能优化方向的工作。
|
||||
|
||||
最初是偶然的一次机会,我关注了eBay技术荟公众号,认真拜读过作者的eBay云计算网事系列文章,受益匪浅,从中汲取了不少排查网络疑难杂症的新思路。但意犹未尽,一直期待有机会看到作者更多的分享。
|
||||
|
||||
后来,又看到极客时间**《容器实战高手课》**的课程预告,作者正是程远老师,而且课程内容也和我现在做的工作不谋而合。我满怀期待地翻了一遍开篇词,激起了不少共鸣,于是毫不犹豫地购买了该专栏。
|
||||
|
||||
我比较赞同作者“一个态度,两个步骤”的方法论。可以说,近年来我也是这个方法论的忠实践行者。正如作者所言,**容器的大多数问题,最终都可以映射到Linux操作系统相应模块上。**因此,想要在容器方面技高一筹,意味着需要苦练内功,深入了解隐藏在容器背后的Linux操作系统知识原理。
|
||||
|
||||
记得我刚参加工作的前几年,我没有直接接触过容器技术,对Linux了解也不多,仅仅停留在使用层面,遇到问题经常一头雾水。
|
||||
|
||||
庆幸的是,从2017年末起,我开始沉下心来学习Linux相关技术,从基础知识到内核源码,一步一步地向前进。这个过程中,也逐渐养成了遇到问题刨根问底的思维习惯,而这种态度,也正是这个专栏里一直倡导的。
|
||||
|
||||
2019年初,我开始真正接触容器技术,凭借平时积累了比较扎实的Linux知识功底,遇到的多数容器问题都能够迎刃而解,“两个步骤”在平时的系统性能分析与优化工作中得以体现。
|
||||
|
||||
## 我为什么要学习专栏?
|
||||
|
||||
选择学习专栏的主要原因有两个,一方面是完善知识体系,另一方面是拓宽自己的技术思路。
|
||||
|
||||
经过前期了解,我认为作者技术经验丰富。他经历了Kubernetes容器云平台在eBay的具体落地过程,解决过大大小小的各类问题,是货真价实的容器实战高手。在学习专栏之前,我自己简单整理过一套容器知识结构体系。不过相比之下,作者列出的容器知识结构体系更加完备。
|
||||
|
||||
所以,我期待借鉴作者的思路重新审视自己的理解,查漏补缺、温故知新,并在专栏更新过程中通过留言和作者进一步交流心得体会。相信经过系统的学习,我也能更好地建立自己的知识架构。
|
||||
|
||||
另外,既然这门课是实战课,自然少不了动手实战,我期待可以在案例中,借鉴老师解决问题的思路。
|
||||
|
||||
作者在eBay云计算网事系列文章中曾提过,Kubernetes容器云平台在大规模应用场景下的偶发性问题十分棘手,如果手忙脚乱地进行各种尝试,可能会事倍功半、越理越乱。事实的确如此,在复杂的Linux环境下,传统的性能分析工具应对这些偶发性问题,已经略显捉襟见肘了。
|
||||
|
||||
过去一年里,我也曾使用perf、ftrace、BCC/bpftrace等Linux性能分析工具解决过网络抖动、存储写入延迟等偶发性问题。但是仍然担心自己的思路存在局限性,所以很期待作者专题加餐的分享,看看这些工具在容器网络不稳定的真实案例中要怎么综合运用,拓宽自己定位、解决类似问题的思路。
|
||||
|
||||
## 我是怎么学习专栏的
|
||||
|
||||
我应该是订阅比较早的同学了,现在想想,能够坚持学完整个专栏,而且在更新期间跟老师做了积极互动,主要有这样三个方法。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d1/29/d16e5761504a9cc4dff7ed5382db9329.png" alt="">
|
||||
|
||||
### 1.坚持学习,用好碎片时间
|
||||
|
||||
专栏的首刷,我基本上是利用碎片化的时间完成的。每周一、三、五专栏更新时,我会在早上通勤路上先把内容过一遍,从整体上把握文章思路。
|
||||
|
||||
由于碎片时间很难保持较高专注度,我倾向于选择轻松一些的阅读方式,不刻意做过多的深度思考,但是会把自己比较赞同或疑惑的知识点先标记下来,以便后续重点学习。
|
||||
|
||||
专栏内容结构简练、清晰,一共二十多篇,其实我觉得坚持下来并不难。专栏更新过程中,我利用工作闲暇时,针对最有收获的几篇文章反复刷了若干次,结合文章示例动手实践,并翻阅相关的Linux、Docker源码,将首刷时留下的疑点逐一击破。
|
||||
|
||||
两个月下来,这样的学习让我受益匪浅,进一步拓宽了自己的容器知识结构体系,也学到了分析常见容器性能问题的新思路。
|
||||
|
||||
**我相信,当一个人开始把坚持学习当作一种习惯,并且善于合理利用碎片时间提升自我的时候,他已经走在超越大多数人的道路上。**
|
||||
|
||||
### 2.知行合一,善于思考总结
|
||||
|
||||
每个专栏作者几乎都会反复强调:学习专栏时,请动手实践文章中提到的示例以及课后思考题。所以,学习中思考与实践的重要性不言而喻。
|
||||
|
||||
数学家华罗庚说过,他的学习方法很简单,就是“读书要从薄到厚,再从厚到薄”。其实专栏的学习也是同样的道理。在我看来,专栏每篇文章都浓缩着作者历经磨砺后的思考与总结,有些很难一次性消化,所以需要我们在不断实践与思考中领悟。
|
||||
|
||||
我觉得,专栏起到的是一个**领航**的作用,想要相关的知识点我们可以根据自己的需要继续做功课,这样才能不断精进。遇到有疑问的地方,我会通过官方文档、源码等方式深挖这些知识点背后原理,希望可以由点及面,直达问题本质。
|
||||
|
||||
有了这样的问题导向,我才会进行更多深度思考,在研究、解决问题的过程中,也加深了自己对知识的理解。下面举几个我曾经思考并想办法弄清楚的问题:
|
||||
|
||||
- 什么是容器绑定挂载?容器创建过程中涉及哪些挂载行为和传播方式?
|
||||
- 为什么说虚拟网络设备veth的行为像一根网线?
|
||||
- 如何使用strace追踪容器创建的大致过程?
|
||||
- 拥有独立Network Namespace的容器如何反过来操控宿主机的防火墙、路由表等?
|
||||
- 为什么Pid Namespace可以使得容器的init进程的PID总是为1?
|
||||
- ...
|
||||
|
||||
### 3.敢于怀疑,拒绝教条主义
|
||||
|
||||
延续刚才说的知行合一。其实我们学习的时候,对有疑问的内容拒绝教条主义,这也很重要,我举个例子吧。
|
||||
|
||||
第12讲学习容器Quota技术的时候,文章里用到的例子是xfs quota。但我认为,目前容器quota技术存在以下局限性:容器通常采用overlay存储驱动,仅支持在宿主文件系统xfs上开启quota功能,这意味着overlay over ext4不支持project quota功能。
|
||||
|
||||
虽然ext4自身支持project quota功能(Linux4.5版本之后)。但是,overlay over ext4却不支持project quota功能。
|
||||
|
||||
经过我的尝试,采用overlay over ext4启动容器,会报以下错误:
|
||||
|
||||
Docker:Error response from daemon: --storage-opt is supported only for overlay over xfs with 'pquota' mount option.
|
||||
|
||||
[官方文档](https://docs.docker.com/engine/reference/commandline/run/#set-storage-driver-options-per-container)里也有提到:For the overlay2 storage driver, the size option is only available if the backing fs is xfs and mounted with the pquota mount option(大意是overlay存储驱动仅支持基于xfs开启project quota功能)。
|
||||
|
||||
## 专栏中最有收获的文章是哪几篇?
|
||||
|
||||
首先是[第2讲](https://time.geekbang.org/column/article/309423),我印象最深的是这篇文章最后的总结了,提到容器里1号进程对信号处理的两个要点:
|
||||
|
||||
1.在容器中,1号进程永远不会响应SIGKILL和SIGSTOP这两个特权信号;<br>
|
||||
2.对于其他的信号,如果用户自己注册了handler,1号进程可以响应。
|
||||
|
||||
其中,要点2打破了我的固有认知。我原先一直认为在容器中1号进程无法被杀死,而这一讲结合相关内核源码与若干示例充分证明了这一要点,思路清晰,令人受益良多。
|
||||
|
||||
在[第9讲](https://time.geekbang.org/column/article/316436)学习Memory Cgroup的时候,课程里提到,每个容器的Memory Cgroup在统计每个控制组的内存使用时包含了两部分,RSS和Page Cache。
|
||||
|
||||
不过我认为,Momory Cgroup除了包括RSS 和Page Cache,还包括了对内核内存的限制,作者给出的例子情况比较简单,基本没有使用slab,倘若在容器中打开海量小文件,内核内存inode、dentry等会被计算在内。
|
||||
|
||||
内存使用量计算公式(memory.kmem.usage_in_bytes表示该memcg内核内存使用量):
|
||||
|
||||
memory.usage_in_bytes = memory.stat[rss] + memory.stat[cache] + memory.kmem.usage_in_bytes
|
||||
|
||||
另外,Memory Cgroup OOM不是真正依据内存使用量memory.usage_in_bytes,而是依据working set(使用量减去非活跃 file-backed 内存),working set计算公式是:
|
||||
|
||||
working_set=memory.usage_in_bytes-total_inactive_file。
|
||||
|
||||
接下来要说说[第14讲](https://time.geekbang.org/column/article/321330),文章首先抛出一个引人关注的现象:应用程序从虚拟机迁移到容器并施加Memory Cgroup 限制时,文件写入出现较大的延时波动。
|
||||
|
||||
我印象最深的是,这一讲通过程序复现、理论分析、工具追踪等一系列手段,最终得出一个令我深有同感的结论:在对容器做Memory Cgroup限制内存大小的时候,不仅要考虑容器中进程实际使用的内存量,还要考虑容器中程序I/O的量,合理预留足够的内存作为Buffered I/O的Page Cache。
|
||||
|
||||
其实学习容器技术没不久,我就已经了解到,容器网络延迟相比宿主机高出10%左右,但并未深入思考过网络延迟变高的具体原因。
|
||||
|
||||
而专栏[第17讲](https://time.geekbang.org/column/article/324122)内容讲网络延时的时候,从veth driver的内核源码入手,合理解释了容器网络性能损失的来龙去脉,感觉收获很大,可以说是透过现象挖到了本质。受到这一讲的启发,对排查网络延迟问题,我又多了新的思路。
|
||||
|
||||
## 总结
|
||||
|
||||
在这个专栏学习结束后,我回过头来看,很高兴已经达到当初选择学习专栏的目的。
|
||||
|
||||
阅读过程整体比较顺畅,尤其觉得作者列出的Linux源码恰到好处,产生不少共鸣,因为在探究容器问题过程中,我也曾经阅读过这些代码。
|
||||
|
||||
感谢极客时间为我们提供了如此优秀的学习平台,也感谢作者为我们带来了非常精彩的容器实战课程。希望订阅这门课程的同学,也能有所收获,把学到的知识用到工作里。
|
||||
|
||||
**我觉得,无论是学习容器,还是学习其他技术知识,就好像升级打怪一样,苦练基本功,手感和意识才会逐渐提升。最后,希望我们都能成为容器高手,让我们相信坚持的力量,终会厚积薄发!**
|
||||
85
极客时间专栏/容器实战高手课/结束语/结束语 | 跳出舒适区,突破思考的惰性.md
Normal file
85
极客时间专栏/容器实战高手课/结束语/结束语 | 跳出舒适区,突破思考的惰性.md
Normal file
@@ -0,0 +1,85 @@
|
||||
<audio id="audio" title="结束语 | 跳出舒适区,突破思考的惰性" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/be/a1/beddeab3d00c48bab6f4699cb1e08ba1.mp3"></audio>
|
||||
|
||||
你好,我是程远。
|
||||
|
||||
今天是我们专栏必学内容的最后一讲。当你读到这一讲内容的时候,刚好是元旦。首先我要祝你元旦快乐,2021年一切顺利!
|
||||
|
||||
在过去的二十多讲内容里,我们从基础开始,一起学习了容器进程、内存、存储、网络以及安全这几部分的内容。在每一讲里,我们都会从一个实际问题或者现象出发,然后一步步去分析和解决问题。一路走来,真是百感交集,我有好多心里话,但又不知该从何说起。
|
||||
|
||||
所以最后一讲,我想和你聊聊我个人的一些成长感悟,在辞旧迎新的元旦,正适合回顾过去和展望未来。所以这既是专栏的一次总结交流,也是我们开启新征程的“号角”。
|
||||
|
||||
在多年以前,我在书里读到一句话,说的是“每个人都有潜在的能量,只是很容易被习惯所掩盖,被时间所迷离,被惰性所消磨。”
|
||||
|
||||
今天再次回看这段话,还真是一语中的,感触良多,回想起专栏写作的整个过程,这件事带给我的最大感悟就是:**跳出自己的舒适区,才能有所突破。**
|
||||
|
||||
## 突破舒适区是很难的事儿
|
||||
|
||||
我们都知道,突破舒适区是一件很难的事儿。这里我给你分享一个我自己的故事,也许你也会从这个故事里找到自己的影子。
|
||||
|
||||
记得在2年前,我参加过eBay的一个内部培训,培训的目标就是要让自己有所“突破”。我必须承认,这个培训是我经历过的所有培训中最接地气的一个培训,在培训过程里我也是情绪激昂的,准备带着学到的东西回到工作里去大展身手,好好突破一番的。
|
||||
|
||||
不过等培训结束,再回到日常工作的时候,之前的雄心壮志、激情澎湃又被日常的琐事所淹没,积蓄的那股劲儿又慢慢被消磨了。周围的同事会开玩笑地对我说:“程远啊,我觉得你没有突破啊。”
|
||||
|
||||
其实,我心里也知道,所谓的“突破”就要跳出自己的舒适区。不过我始终不知道怎么跳出来,哪怕自己手上的工作再多,工作到再晚,但这仍然是处于自己舒适区。这是因为这一切的工作节奏还有思考的问题,都是我自己熟悉的。
|
||||
|
||||
这种熟悉很可能让我们沉湎其中,裹足不前。那问题来了,意识到自己处于舒适区,产生想要“跳出去”的念头的确是良好开局,难的是怎么有效突破。这就要聊到突破方法路径的问题了,我想结合自己的感悟给你说一说。
|
||||
|
||||
## 主动迎接挑战,在实战中进步
|
||||
|
||||
不知道你有没有听过热力学里熵增的定律,大概说的是:封闭系统的熵(能量)会不可逆地增加,最终导致整个系统崩溃。那怎么才能保持这个系统的活力呢?就是能量交换,不断去引入外部的能量,也就是负熵。
|
||||
|
||||
我们可以引申一下,自然会想到走出舒适区这件事,也是同样的道理。我们要有一种冒险家的勇气,主动去迎接挑战,在实战里迫使自己不断进步。
|
||||
|
||||
其实选择做这样一个专栏,对我来说就是走出舒适区的一项“挑战”。在今年7月份,那还是我们这个专栏筹备的前期,我当时就一个想法,就是把我这些年来在容器方面的积累给记录下来。
|
||||
|
||||
从7月份决定写容器这个专栏开始,到现在差不多也有半年的时间了,我真的觉得,在工作的同时把写专栏的这件事给坚持下来,真的是一件不容易的事情。**这里不仅仅是一个简单的时间投入问题,更多的是迫使自己再去思考的问题。**
|
||||
|
||||
估计你也发现了,我每一讲都涉及不少知识点。我在专栏写作的过程中,花时间最多的就是怎么把问题说清楚,这里要解释哪些关键知识点,适合用什么样的例子做解释,每个知识点要讲到什么程度,需要查阅哪些代码和资料来保证自己所讲内容的正确性。
|
||||
|
||||
这样的思考模式和我日常思考工作问题的模式是完全不同的。但也正是借着这样的机会,我才从自己原先的舒适区里跳了出来,工作之余同时也在思考写专栏的问题,每天都有大量的context switch,也就是上下文切换。
|
||||
|
||||
我很高兴自己可以坚持下来,完成了专栏的主体部分。可以说,这门课既是容器的实战课,也是我自己走出舒适区的实战训练。
|
||||
|
||||
## 突破舒适区,本质是突破思考的惰性
|
||||
|
||||
这次的专栏写作,还让我意识到,**突破舒适区的本质就是突破思考的惰性。只有不断思考,才能推着自己不断往前走,才能让我们更从容地解决工作上的问题。**
|
||||
|
||||
在2020年的12月初,Kubernetes宣布不再支持dockershim,也就是说Kubernetes节点上不能再直接用Docker来启动容器了。当时我看到这条新闻,觉得这是理所当然的,因为我们的容器云平台上在2019年初就从Docker迁移到了Containerd。
|
||||
|
||||
不过,后来我在专栏留言回复的过程中,连续有三位同学留言,问我怎么看Kubernetes的这个决定,这让我又回忆起了当初我们团队是怎么做的迁移决定。
|
||||
|
||||
这件事还要追溯到2018年的时候,我们发现kubelet通过CRI接口就可以集成Containerd了,于是我们就开始思考,是不是应该用Containerd来替换Docker呢?
|
||||
|
||||
当时我们看到的好处有两点。第一点是这样替换之后**架构上的优势**,CRI可以说是kubelet连接Runtime的标准了,而用Dockershim接Docker再转Containerd,这样很累赘。第二点好处就是**降低了维护成本**。Containerd只是Docker中的一部分,维护Containerd明显要比维护庞大的Docker容易。
|
||||
|
||||
当然,这么做的挑战也是很大的。当时,我们在生产环境中已经有2万台物理机节点以及几十万个容器,而且那时候业界还几乎没有人在生产环境中用kubelet直接调用Containerd。没有前人的尝试可以借鉴,只能咬牙打一场硬仗。
|
||||
|
||||
后来我们通过一个多月的测试,发现直接使用Containerd,无论是稳定性还是性能都没有问题。有了实际测试做保障,我们在2019年初又花了3个月时间,才把生产环境上的Docker全部替换成Containerd。
|
||||
|
||||
这样的结果看似轻描淡写,一两句话就带过了。但实际过程里,已经不是过五关斩六将了,而是一直在发现问题、解决问题,大大小小的战役才汇聚成了最后的战果。其实,我在这个专栏里和你分享的一些容器问题,也来源于我们当时的迁移实践。
|
||||
|
||||
现在回想起来,当初的这个决定无疑是非常正确的了。不过再想想,如果当时看到Kubernetes的变化,我们没有主动思考,等到现在Kubernetes宣布不再支持Dockershim才去做应对,结果又会怎样呢?
|
||||
|
||||
这个问题,我觉得用数字来说话更直观。刚才提到当时迁移的时候,有2万台物理机节点以及几十万个容器。但如果等到现在才迁移,我们需要面对的就是6万台物理机和上百万的容器了。
|
||||
|
||||
你看,无论是写专栏也好,还是我们实际工作也好,呆在舒适区里,短期成本看着挺小,不需要你大动干戈,消耗脑细胞和精力。但是,当你习惯了这种思考的惰性,就会变成温水煮青蛙而不自知,等到外部条件发生变化时会很被动。
|
||||
|
||||
## 最后的彩蛋
|
||||
|
||||
前面我们聊了很多突破舒适区的事儿,不知道你有没有被触动呢?
|
||||
|
||||
其实学习也好,工作也罢,就是要有一种突破意识,走出舒适区,才能“开疆拓土”。那为了让你我都知行合一,我还要给你聊聊后面的专题加餐安排。
|
||||
|
||||
在开篇词我也提到了这个安排。虽然这一讲是我们课程的结束语,但我们课程的内容并没有结束。在这个专题里,我选择了一个真实案例。那这个案例我是怎么选的呢?
|
||||
|
||||
其实这是2020年初,我们在生产环境里遇到的一个真实的容器网络问题。我觉得这是一个很好的调试案例,它的好就在于可以用到Linux内核的最主要的几个调试工具,包括perf,ftrace和ebpf。我们逐个使用这些工具,就可以层层递进地揭开问题的本质。
|
||||
|
||||
通过这个案例的学习,我会带你掌握每种工具的特性。这样你在理解了容器基本原理的基础上,就能利用这些好的工具系统化地分析生产环境中碰到的容器问题了,就像我们开篇中说的那样——变黑盒为白盒。
|
||||
|
||||
写完结束语之后,我会认真为你准备这个专题加餐。而这一个月的时间,你还可以继续消化理解课程主体部分的内容,打牢基础,这样对你学习后面的专题加餐也有很大帮助。
|
||||
|
||||
最后的最后,我想和你说的是,希望你我都能主动思考,不断突破自己,走出舒适区,一起共勉吧!
|
||||
|
||||
这里我为你准备了一份[毕业问卷](https://jinshuju.net/f/socZck),题目不多,希望你可以花两分钟填一下。也十分期待能听到你的声音,说说你对这门课程的想法和建议。
|
||||
|
||||
[<img src="https://static001.geekbang.org/resource/image/a4/b6/a477edbc0cb9a0902715e7e3c5a666b6.jpg" alt="">](https://jinshuju.net/f/socZck)
|
||||
16
极客时间专栏/容器实战高手课/结束语/结课测试|这些容器技术的问题,你都掌握了么?.md
Normal file
16
极客时间专栏/容器实战高手课/结束语/结课测试|这些容器技术的问题,你都掌握了么?.md
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
你好,我是程远。
|
||||
|
||||
《容器实战高手课》这门课程的必学内容已经全部结束了,感谢你一直以来的认真学习和支持!
|
||||
|
||||
我给你准备了一个结课小测试,希望可以帮助你检测一下这段时间的学习成果。
|
||||
|
||||
这套测试题共有 20 道题目,10道单选题,10道多选题,满分 100 分,系统自动评分。
|
||||
|
||||
还等什么,快点击下面按钮开始测试吧!
|
||||
|
||||
[<img src="https://static001.geekbang.org/resource/image/28/a4/28d1be62669b4f3cc01c36466bf811a4.png" alt="">](http://time.geekbang.org/quiz/intro?act_id=357&exam_id=966)
|
||||
|
||||
最后,这里有一份[毕业问卷](https://jinshuju.net/f/socZck),题目不多,希望你可以花两分钟填一下。也十分期待能听到你的声音,说说你对这门课程的想法和建议。
|
||||
|
||||
[<img src="https://static001.geekbang.org/resource/image/a4/b6/a477edbc0cb9a0902715e7e3c5a666b6.jpg" alt="">](https://jinshuju.net/f/socZck)
|
||||
Reference in New Issue
Block a user