基本概念

我们都知道一个pod里面可以放若干个容器,容器之间可以共享网络命名空间、文件夹。这时候一个pod里面有两个容器a和b,同时对外提供服务。如果这个两个服务之间需要彼此交互,比如a想要触发b容器的容器,或者b想要获取a容器里的一个变量之类的。我们这时候就可以使用容器共享命名空间的方法。

具体操作

先准备两个程序app1和app2

app1.go

1
2
3
4
5
6
7
8
9
10
11
package main

import "net/http"

func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("this is app1\n"))
})
http.ListenAndServe(":8080", nil)
}

app2.go

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"net/http"
)

func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("this is app2\n"))
})
http.ListenAndServe(":8081", nil)
}

然后分别编译丢到服务器上

编写对应的yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8splay
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: k8splay
template:
metadata:
labels:
app: k8splay
spec:
nodeName: k8s-node1
containers:
- name: k8splayapp1
image: alpine:3.12
imagePullPolicy: IfNotPresent
command: [ "./app1" ]
workingDir: /app
volumeMounts:
- name: app
mountPath: /app
ports:
- containerPort: 8080
- name: k8splayapp2
image: alpine:3.12
imagePullPolicy: IfNotPresent
command: [ "./app2" ]
workingDir: /app
volumeMounts:
- name: app
mountPath: /app
ports:
- containerPort: 8081
volumes:
- name: app
hostPath:
path: /root/k8splay

然后直接apply

进入这个容器查看

可以看到 pid为1的进程为 ./app1,这也没啥好说的

这时候在yaml中加入 shareProcessNamespace: true,打开共享进程命名空间开关。

此时进入pod查看进程

现在任意一个容器查看 pid为1的进程就不是自己了,而且可以查看到其他容器的进程了。这就是进程命名空间共享。

现在在app1这个容器写入一个文件,在app2容器利用共享进程命名空间可以查看到了,而不需要设置emptydir

总结一下:

  • 当启用进程命名空间共享时,容器中的进程对该pod中所有其他容器都可见
  • 容器进程PID不再是1
  • 容器文件系统 通过 /proc/$pid/root 链接对pod中的其他容器可见

使用信号通知进程

使用命令行操作

使用脚本方式发送信号通知容器1,不通知容器2

重新修改app1.go代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)

func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("this is app1\n"))
})
go func() {
for {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR1) // 监听 kill -15 和 kill -10 (用户自定义信号)
c := <-ch
fmt.Println("接收到信号:", c)
time.Sleep(time.Millisecond * 50)
}

}()
http.ListenAndServe(":8080", nil)
}

重新apply 一下

使用代码方式

app1.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package main

import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)

var counter = 1

func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("this is app1\n"))
})
http.HandleFunc("/counter", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte(fmt.Sprintf("counter: %d\n", counter)))
counter++
})
go func() {
for {
ch := make(chan os.Signal)
signal.Notify(ch, syscall.SIGTERM, syscall.SIGUSR1) // 监听 kill -15 和 kill -10 (用户自定义信号)
c := <-ch
switch c {
case syscall.SIGUSR1:
fmt.Println("counter被重置")
counter = 0
}
time.Sleep(time.Millisecond * 50)
}

}()
http.ListenAndServe(":8080", nil)
}

每次访问/counter ,counter就会加1

app2.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"net/http"
"syscall"
)

func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("this is app2\n"))
})
http.HandleFunc("/rest", func(writer http.ResponseWriter, request *http.Request) {
syscall.Kill(-1, syscall.SIGUSR1)
})
http.ListenAndServe(":8081", nil)
}

在app2中访问 /rest,就会发生信号给app1,从而触发counter=0的逻辑

Deploy.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8splay
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: k8splay
template:
metadata:
labels:
app: k8splay
spec:
nodeName: k8s-node1
shareProcessNamespace: true
containers:
- name: k8splayapp1
image: byrnedo/alpine-curl
imagePullPolicy: IfNotPresent
command: [ "./app1" ]
workingDir: /app
volumeMounts:
- name: app
mountPath: /app
ports:
- containerPort: 8080
- name: k8splayapp2
image: byrnedo/alpine-curl
imagePullPolicy: IfNotPresent
command: [ "./app2" ]
workingDir: /app
volumeMounts:
- name: app
mountPath: /app
ports:
- containerPort: 8081
volumes:
- name: app
hostPath:
path: /root/k8splay