【Docker 那些事儿】带你走进Docker的未知宇宙
🌟 前言
大家好,我是Edison😎
今天是我们「云原生」系列的第一篇:「走进Docker的世界」;
Let’s get it!
序言
对于Docker,相信大家并不陌生,本文将介绍docker的前世今生,了解docker的实现与原理🐱🏍
1. 认识docker
什么是 Docker?
为什么会出现Docker?
Docker能用来做什么?
🍑 为什么出现Docker
需要一种轻量、高效的虚拟化能力
Docker 公司位于旧金山,原名 dotCloud,底层利用了Linux容器技术(LXC)(在操作系统中实现资源隔离与限制)。为了方便创建和管理这些容器,dotCloud 开发了一套内部工具,之后被命名为 “Docker”,Docker就是这样诞生的。
Hypervisor: 一种运行在基础物理服务器和操作系统之间的中间软件层,可允许多个操作系统和应用共享硬件 。常见的VMware的 Workstation 、ESXi、微软的Hyper-V 或者 思杰的XenServer。
Container Runtime: 通过Linux内核虚拟化能力管理多个容器,多个容器共享一套操作系统内核。因此摘掉了内核占用的空间及运行所需要的耗时,使得容器极其轻量与快速。
🍑 什么是Docker
基于操作系统内核,提供轻量级虚拟化功能的CS架构的软件产品。
基于轻量的特性,解决软件交付过程中的环境依赖
🍑 Docker能做什么
1、可以把应用程序代码及运行依赖环境打包成镜像,作为交付介质,在各环境部署;
2、可以将镜像(image)启动成为容器(container),并且提供多容器的生命周期进行管理(启、停、删);
3、container 容器之间相互隔离,且每个容器可以设置资源限额;
4、提供轻量级虚拟化功能,容器就是在宿主机中的一个个的虚拟的空间,彼此相互隔离,完全独立;
🍑 版本管理
Docker 引擎主要有两个版本:企业版(EE) 和 社区版(CE);
每个季度(1-3,4-6,7-9,10-12),企业版和社区版都会发布一个稳定版本(Stable)。社区版本会提供 4 个月的支持,而企业版本会提供 12 个月的支持;
每个月社区版还会通过 Edge 方式发布月度版;
从 2017 年第一季度开始,Docker 版本号遵循 YY.MM-xx 格式,类似于 Ubuntu 等项目。例如,2018 年 6 月第一次发布的社区版本为 18.06.0-ce
🍑 发展史
13年成立,15年开始,迎来了飞速发展。
Docker 1.8之前,使用LXC,Docker在上层做了封装, 把 LXC 复杂的容器创建与使用方式简化为自己的一套命令体系。
之后,为了实现跨平台等复杂的场景,Docker抽出了 libcontainer 项目,把对namespace、cgroup的操作封装在libcontainer项目里,支持不同的平台类型。
2015年6月,Docker牵头成立了 OCI(Open Container Initiative开放容器计划)组织,这个组织的目的是建立起一个围绕容器的通用标准 。 容器格式标准是一种不受上层结构绑定的协议,即不限于某种特定操作系统、硬件、CPU架构、公有云等 , 允许任何人在遵循该标准的情况下开发应用容器技术,这使得容器技术有了一个更广阔的发展空间。
OCI成立后,libcontainer 交给OCI组织来维护,但是 libcontainer 中只包含了与kernel交互的库,因此基于 libcontainer 项目,后面又加入了一个 CLI 工具,并且项目改名为 runC (runC的主页), 目前runC已经成为一个功能强大的runtime工具。
Docker也做了架构调整。将容器运行时相关的程序从docker daemon剥离出来,形成了containerd。
containerd向上为Docker Daemon提供了gRPC接口
,使得Docker Daemon屏蔽下面的结构变化,确保原有接口向下兼容。向下通过containerd-shim
结合runC
,使得引擎可以独立升级,避免之前Docker Daemon升级会导致所有容器不可用的问题。
也就是说🧐
runC(libcontainer)是符合OCI标准的一个实现,与底层系统交互。
containerd 是实现了OCI之上的容器的高级功能,比如镜像管理、容器执行的调用等。
Docker 目前是最上层与CLI交互的进程,接收cli的请求并与containerd协作。
2. 安装Docker
配置宿主机网卡转发
## 若未配置,需要执行如下
$ cat <<EOF > /etc/sysctl.d/docker.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward=1
EOF
$ sysctl -p /etc/sysctl.d/docker.conf
Yum安装配置docker
## 下载阿里源repo文件
$ curl -o /etc/yum.repos.d/Centos-7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
$ curl -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
$ yum clean all && yum makecache
## yum安装
$ yum install docker-ce-20.10.12 -y
## 查看源中可用版本
$ yum list docker-ce --showduplicates | sort -r
## 安装旧版本
##yum install -y docker-ce-18.09.9
## 配置源加速
## https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
mkdir -p /etc/docker
vi /etc/docker/daemon.json
{
"registry-mirrors" : [
"https://8xpk5wnt.mirror.aliyuncs.com"
]
}
## 设置开机自启
systemctl enable docker
systemctl daemon-reload
## 启动docker
systemctl start docker
## 查看docker信息
docker info
## docker-client
which docker
## docker daemon
ps aux |grep docker
## containerd
ps aux|grep containerd
systemctl status containerd
到这里就安装好我们的Docker啦!
3. Docker核心操作详解
Docker的三大核心要素:镜像(Image)、容器(Container)、仓库(Registry)
🍑 镜像(Image)
打包了业务代码及运行环境的包,是静态的文件,不能直接对外提供服务。
🍑 容器(Container)
镜像的运行时,可以对外提供服务。
🍑 仓库(Registry)
存放镜像的地方
仓库分类
公有仓库:Docker Hub,阿里,网易…
私有仓库:企业内部搭建;
Docker Registry:Docker官方提供的镜像仓库存储服务;
Harbor:是Docker Registry的更高级封装,它除了提供友好的Web UI界面,角色和用户权限管理,用户操作审计等功能;
公有的仓库中,一般存在这么几类镜像
操作系统基础镜像(centos,ubuntu,suse,alpine)
中间件(nginx,redis,mysql,tomcat)
语言编译环境(python,java,golang)
业务镜像(django-demo…)
容器和仓库不会直接交互,都是以镜像为载体来操作。
🍑 Docker常用命令
1、查看镜像列表
$ docker images
2、如何获取镜像
①从远程仓库拉取
$ docker pull nginx:alpine
$ docker images
②使用tag命令
$ docker tag nginx:alpine 172.21.51.143:5000/nginx:alpine
$ docker images
③本地构建
$ docker build . -t my-nginx:ubuntu -f Dockerfile
3、如何通过镜像启动容器
$ docker run --name my-nginx-alpine -d nginx:alpine
4、如何知道容器内部运行了什么程序?
# 进入容器内部,分配一个tty终端
$ docker exec -ti my-nginx-alpine /bin/sh
# ps aux
5、docker怎么知道容器启动后该执行什么命令?
通过docker build来模拟构建一个nginx的镜像👇
①创建Dockerfile
# 告诉docker使用哪个基础镜像作为模板,后续命令都以这个镜像为基础
FROM ubuntu
# RUN命令会在上面指定的镜像里执行命令
RUN apt-get update && apt install -y nginx
#告诉docker,启动容器时执行如下命令
CMD ["/usr/sbin/nginx", "-g","daemon off;"]
②构建本地镜像
$ docker build . -t my-nginx:ubuntu -f Dockerfile
③使用新镜像启动容器
$ docker run --name my-nginx-ubuntu -d my-nginx:ubuntu
④进入容器查看进程
$ docker exec -ti my-nginx-ubuntu /bin/sh
# ps aux
6、如何访问容器内服务
# 进入容器内部
$ docker exec -ti my-nginx-alpine /bin/sh
# ps aux|grep nginx
# curl localhost:80
7、宿主机中如何访问容器服务
# 删掉旧服务,重新启动
$ docker rm -f my-nginx-alpine
$ docker run --name my-nginx-alpine -d -p 8080:80 nginx:alpine
$ curl 172.21.51.143:8080
8、docker client如何与daemon通信
# /var/run/docker.sock
$ docker run --name portainer -d -p 9001:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer
如图
🍑 操作流程图
1、查看所有镜像
$ docker images
2、拉取镜像
$ docker pull nginx:alpine
3、如何唯一确定镜像
①image_id
②repository:tag
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx alpine 377c0837328f 2 weeks ago 19.7MB
4、导出镜像到文件中
$ docker save -o nginx-alpine.tar nginx:alpine
5、从文件中加载镜像
$ docker load -i nginx-alpine.tar
6、部署镜像仓库
## 使用docker镜像启动镜像仓库服务
$ docker run -d -p 5000:5000 --restart always --name registry registry:2
## 默认仓库不带认证,若需要认证,参考https://docs.docker.com/registry/deploying/#restricting-access
7、推送本地镜像到镜像仓库中
$ docker tag nginx:alpine localhost:5000/nginx:alpine
$ docker push localhost:5000/nginx:alpine
## 查看仓库内元数据
$ curl -X GET http://172.21.51.143:5000/v2/_catalog
$ curl -X GET http://172.21.51.143:5000/v2/nginx/tags/list
## 镜像仓库给外部访问,不能通过localhost,尝试使用内网地址172.21.51.143:5000/nginx:alpine
$ docker tag nginx:alpine 172.21.51.143:5000/nginx:alpine
$ docker push 172.21.51.143:5000/nginx:alpine
The push refers to repository [172.21.51.143:5000/nginx]
Get https://172.21.51.143:5000/v2/: http: server gave HTTP response to HTTPS client
## docker默认不允许向http的仓库地址推送,如何做成https的,参考:https://docs.docker.com/registry/deploying/#run-an-externally-accessible-registry
## 我们没有可信证书机构颁发的证书和域名,自签名证书需要在每个节点中拷贝证书文件,比较麻烦,因此我们通过配置daemon的方式,来跳过证书的验证:
$ cat /etc/docker/daemon.json
{
"registry-mirrors": [
"https://8xpk5wnt.mirror.aliyuncs.com"
],
"insecure-registries": [
"172.21.51.143:5000"
]
}
$ systemctl restart docker
$ docker push 172.21.51.143:5000/nginx:alpine
$ docker images # IMAGE ID相同,等于起别名或者加快捷方式
REPOSITORY TAG IMAGE ID CREATED SIZE
172.21.51.143:5000/nginx alpine 377c0837328f 4 weeks ago
nginx alpine 377c0837328f 4 weeks ago
localhost:5000/nginx alpine 377c0837328f 4 weeks ago
registry 2 708bc6af7e5e 2 months ago
8、删除镜像
docker rmi nginx:alpine
9、查看容器列表
## 查看运行状态的容器列表
$ docker ps
## 查看全部状态的容器列表
$ docker ps -a
10、启动容器
## 后台启动
$ docker run --name nginx -d nginx:alpine
## 映射端口,把容器的端口映射到宿主机中,-p <host_port>:<container_port>
$ docker run --name nginx -d -p 8080:80 nginx:alpine
## 资源限制,最大可用内存500M
$ docker run --memory=500m nginx:alpine
11、容器数据持久化
## 挂载主机目录
$ docker run --name nginx -d -v /opt:/opt nginx:alpine
$ docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d -v /opt/mysql/:/var/lib/mysql mysql:5.7
12、进入容器或者执行容器内的命令
$ docker exec -ti <container_id_or_name> /bin/sh
$ docker exec <container_id_or_name> hostname
13、主机与容器之间拷贝数据
## 主机拷贝到容器
$ echo '123'>/tmp/test.txt
$ docker cp /tmp/test.txt nginx:/tmp
$ docker exec -ti nginx cat /tmp/test.txt
123
## 容器拷贝到主机
$ docker cp nginx:/tmp/test.txt ./
14、查看容器日志
## 查看全部日志
$ docker logs nginx
## 实时查看最新日志
$ docker logs -f nginx
## 从最新的100条开始查看
$ docker logs --tail=100 -f nginx
15、停止或者删除容器
## 停止运行中的容器
$ docker stop nginx
## 启动退出容器
$ docker start nginx
## 删除非运行中状态的容器
$ docker rm nginx
## 删除运行中的容器
$ docker rm -f nginx
16、查看容器或者镜像的明细
## 查看容器详细信息,包括容器IP地址等
$ docker inspect nginx
## 查看镜像的明细信息
$ docker inspect nginx:alpine
4. Dockerfile使用
Dockerfile 是一堆指令,在 docker build
的时候,按照该指令进行操作,最终生成我们期望的镜像
$ docker build . -t ImageName:ImageTag -f Dockerfile
FROM 指定基础镜像,必须为第一个命令
格式:
FROM <image>
FROM <image>:<tag>
示例:
FROM mysql:5.7
注意:
tag是可选的,如果不使用tag时,会使用latest版本的基础镜像
MAINTAINER 镜像维护者的信息
格式:
MAINTAINER <name>
示例:
MAINTAINER Edison
MAINTAINER Edison@gmail.com
MAINTAINER Edison <Edison@gmail.com>
COPY|ADD 添加本地文件到镜像中
格式:
COPY <src>... <dest>
示例:
ADD hom* /mydir/ # 添加所有以"hom"开头的文件
ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/
ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/
WORKDIR 工作目录
格式:
WORKDIR /path/to/workdir
示例:
WORKDIR /a (这时工作目录为/a)
注意:
通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY等命令都会在该目录下执行
RUN 构建镜像过程中执行命令
格式:
RUN <command>
示例:
RUN yum install nginx
RUN pip install django
RUN mkdir test && rm -rf /var/lib/unusedfiles
注意:
RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,可以在构建时指定--no-cache参数,如:docker build --no-cache
CMD 构建容器后调用,也就是在容器启动时才进行调用
格式:
CMD ["executable","param1","param2"] (执行可执行文件,优先)
CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
CMD command param1 param2 (执行shell内部命令)
示例:
CMD ["/usr/bin/wc","--help"]
CMD ping www.baidu.com
注意:
CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。
ENTRYPOINT 设置容器初始化命令,使其可执行化
格式:
ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先)
ENTRYPOINT command param1 param2 (shell内部命令)
示例:
ENTRYPOINT ["/usr/bin/wc","--help"]
注意:
ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT。Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,而只执行最后的ENTRYPOINT指令
ENV
格式:
ENV <key> <value>
ENV <key>=<value>
示例:
ENV myName John
ENV myCat=fluffy
EXPOSE
格式:
EXPOSE <port> [<port>...]
示例:
EXPOSE 80 443
EXPOSE 8080
EXPOSE 11211/tcp 11211/udp
注意:
EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
以上很多名词相信大家一头雾水,来看这张图👇
基础环境镜像
FROM java:8-alpine
RUN apk add --update ca-certificates && rm -rf /var/cache/apk/* && \
find /usr/share/ca-certificates/mozilla/ -name "*.crt" -exec keytool -import -trustcacerts \
-keystore /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/cacerts -storepass changeit -noprompt \
-file {} -alias {} \; && \
keytool -list -keystore /usr/lib/jvm/java-1.8-openjdk/jre/lib/security/cacerts --storepass changeit
ENV MAVEN_VERSION 3.5.4
ENV MAVEN_HOME /usr/lib/mvn
ENV PATH $MAVEN_HOME/bin:$PATH
RUN wget http://archive.apache.org/dist/maven/maven-3/$MAVEN_VERSION/binaries/apache-maven-$MAVEN_VERSION-bin.tar.gz && \
tar -zxvf apache-maven-$MAVEN_VERSION-bin.tar.gz && \
rm apache-maven-$MAVEN_VERSION-bin.tar.gz && \
mv apache-maven-$MAVEN_VERSION /usr/lib/mvn
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
前端镜像
FROM nginx:1.19.0-alpine
LABEL maintainer="mritd <mritd@linux.com>"
ARG TZ='Asia/Shanghai'
ENV TZ ${TZ}
RUN apk upgrade --update \
&& apk add bash tzdata curl wget ca-certificates \
&& ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime \
&& echo ${TZ} > /etc/timezone \
&& rm -rf /usr/share/nginx/html /var/cache/apk/*
COPY dist /usr/share/nginx/html
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
java镜像
FROM java:8u111
ENV JAVA_OPTS "\
-Xmx4096m \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m"
ENV JAVA_HOME /usr/java/jdk
ENV PATH ${PATH}:${JAVA_HOME}/bin
COPY target/myapp.jar myapp.jar
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
EXPOSE 9000
CMD java ${JAVA_OPTS} -jar myapp.jar
🍑 通过1号进程理解容器的本质
$ docker exec -ti my-nginx-alpine /bin/sh
#/ ps aux
容器启动的时候可以通过命令去覆盖默认的CMD
$ docker run -d --name xxx nginx:alpine <自定义命令>
# <自定义命令>会覆盖镜像中指定的CMD指令,作为容器的1号进程启动。
$ docker run -d --name test-3 nginx:alpine echo 123
$ docker run -d --name test-4 nginx:alpine ping www.baidu.com
本质上讲容器是利用 namespace 和 cgroup 等技术在宿主机中创建的独立的虚拟空间,这个空间内的网络、进程、挂载等资源都是隔离的。
$ docker exec -ti my-nginx /bin/sh
#/ ip addr
#/ ls -l /
#/ apt install xxx
#/ #安装的软件对宿主机和其他容器没有任何影响,和虚拟机不同的是,容器间共享一个内核,所以容器内没法升级内核
5. Docker网络
docker容器是一块具有隔离性的虚拟系统,容器内可以有自己独立的网络空间;
多个容器之间是如何实现通信的呢?
容器和宿主机之间又是如何实现的通信呢?
使用-p参数是怎么实现的端口映射?
带着这些问题,我们来学习一下docker的网络模型!
🍑 网络模式
我们在使用docker run创建Docker容器时,可以用–net选项指定容器的网络模式,Docker有以下4种网络模式:
1、bridge模式,使用--net=bridge
指定,默认设置;
2、host模式,使用--net=host
指定,容器内部网络空间共享宿主机的空间,效果类似直接在宿主机上启动一个进程,端口信息和宿主机共用;
3、container模式,使用--net=container:NAME_or_ID
指定;
4、none模式,使用--net=none
指定;
🍑 bridge模式
之前在演示创建docker容器的时候其实是没有指定的网络模式的,如果不指定的话默认就会使用bridge模式,bridge本意是桥的意思,其实就是网桥模式。
怎么理解网桥,如果需要做类比的话,我们可以把网桥看成一个二层的交换机设备,我们来看下面的图片
交换机通信简图👇
交换机网络通信流程👇
网桥模式示意图👇
🍑 Host模式
容器内部不会创建网络空间,共享宿主机的网络空间。比如直接通过host模式创建 mysql 容器👇
$ docker run --net host -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
容器启动后,会默认监听3306端口;
由于网络模式是host,因为可以直接通过宿主机的 3306 端口进行访问服务,
效果等同于在宿主机中直接启动 mysqld 的进程。
🍑 Conatiner模式
这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace,而不是和宿主机共享。
新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
## 启动测试容器,共享mysql的网络空间
$ docker run -ti --rm --net=container:mysql busybox sh
/ # ip a
/ # netstat -tlp|grep 3306
/ # telnet localhost 3306
在一些特殊的场景中非常有用,例如,kubernetes 的
pod
;
kubernetes 为pod
创建一个基础设施容器,同一pod
下的其他容器都以 container模式 共享这个基础设施容器的网络命名空间,相互之间以 localhost 访问,构成一个统一的整体。
🍑 None模式
只会创建对应的网络空间,不会配置网络堆栈(网卡、路由等)。
# 创建none的容器
$ docker run -it --name=network-none --net=none nginx:alpine sh
# ifconfig
在宿主机中操作:
# 创建虚拟网卡对
$ ip link add A type veth peer name B
# A端插入到docker0网桥
$ brctl addif docker0 A
$ ip link set A up
# B端插入到network-none容器中,需要借助ip netns,因此需要显示的创建命名network namespace
$ PID=$(docker inspect -f '{{.State.Pid}}' network-none)
$ mkdir -p /var/run/netns
$ ln -s /proc/$PID/ns/net /var/run/netns/$PID
# B端放到容器的命名空间
$ ip link set B netns $PID
$ ip netns exec $PID ip link set dev B name eth0 # 修改设备名称为eth0,和docker默认行为一致
$ ip netns exec $PID ip link set eth0 up
# 设置ip
$ ip netns exec $PID ip addr add 172.17.0.100/16 dev eth0
# 添加默认路由,指定给docker0网桥
$ ip netns exec $PID ip route add default via 172.17.0.1
# 测试容器间通信
6. 总结
1、为了解决软件交付过程中的环境依赖,同时提供一种更加轻量的虚拟化技术,Docker出现了。
2、2013年诞生,15年开始迅速发展,从17.03月开始,使用时间日期管理版本,稳定版以每季度为准。
3、Docker是一种CS架构的软件产品,可以把代码及依赖打包成镜像,作为交付介质,并且把镜像启动成为容器,提供容器生命周期的管理。
4、使用 yum 部署 docker,启动后通过操作 docker 这个命令行,自动调用 docker daemon 完成容器相关操作。
5、常用操作,围绕镜像|容器|仓库
三大核心要素;
6、通过 dockerfile 构建业务镜像,先使用基础镜像,然后通过一系列的指令把我们的业务应用所需要的运行环境和依赖都打包到镜像中,然后通过 CMD 或者 ENTRYPOINT 指令把镜像启动时的入口制定好,完成封装即可。
有点类似于,先找来一个集装箱模板(基础镜像),然后把项目依赖的服务都扔到集装箱中,然后设置好服务的启动入口,关闭箱门,即完成了业务镜像的制作。
7、docker的网络模式分为4种,最常用的为bridge和host模式。bridge模式通过docker0网桥
,启动容器的时候通过创建一对虚拟网卡,将容器连接在桥上,同时维护了虚拟网卡与网桥端口的关系,实现容器间的通信。
容器与宿主机之间的通信通过iptables端口映射的方式,docker利用 iptables 的 PREROUTING 和 POSTROUTING 的 nat 功能,实现了SNAT与DNAT,使得容器内部的服务被完美的保护起来。
以上就是Docker的全部内容,但个别繁琐的点我都给去掉了,比如Docker网络中的bridge模式,本来是有一个抓包演示的,但是会涉及到iptables链表、wireshark合并包进行分析等…我觉得这东西过于深入了,就给去掉了。
其实这篇文章主要是为后续的 Kubernetes 作铺垫(毕竟我的CKA证书不能白拿🤣)
- 点赞
- 收藏
- 关注作者
评论(0)