avatar

青青子衿的拾枝杂谈

A text-focused Halo theme

  • 首页
  • linux基础
  • Linux系统
  • Linux高级
  • nginx
  • k8s
  • 网络
Home Docker与Kubernetes
文章

Docker与Kubernetes

Posted recently Updated recently
By 青青子衿
129~167 min read

Docker容器与Kubernetes集群 -- 从零开始的实战指南


目录

第一部分:Docker容器技术

  1. 容器概念:为什么我们需要容器?
  2. Docker介绍与安装
  3. Docker客户端操作
  4. 镜像管理:打造你自己的"安装包"
  5. Docker仓库:镜像的"应用商店"
  6. 数据卷:让数据不再"随容器消亡"
  7. 网络配置:容器如何"上网冲浪"
  8. 网络架构:深入理解docker0网桥
  9. 跨主机容器通讯
  10. Namespace与Cgroup:隔离与限制的魔法
  11. Overlay文件系统:写时复制的秘密

第二部分:Kubernetes集群编排

  1. K8s介绍:容器世界的"交响乐指挥家"
  2. YAML基础:写给K8s的"说明书"
  3. Master与Node部署
  4. kubectl使用:与集群对话
  5. Dashboard:集群的Web管理界面
  6. Pod:K8s的最小调度单元
  7. ReplicaSet与Deployment:副本管理与滚动更新
  8. Service:让外部用户找到你的服务
  9. Volume:K8s中的数据持久化
  10. Job与CronJob:批处理与定时任务
  11. DaemonSet:每个节点的"管家"
  12. HPA:自动弹性伸缩

第一部分:Docker容器技术


1. 容器概念:为什么我们需要容器?

生活中的问题

想象你是一位机房管理员,你需要在机房的三台服务器上分别安装 Python、Jupyter Notebook、各种数据库……每台电脑的系统版本还不一样。每次都要重复折腾一遍,太痛苦了!

问题: 如何让"我的程序在你的电脑上也能跑起来"?

类比理解:

  • 虚拟机 = 盖一栋独立房子: 每栋房子有自己的水电、地基,虽然隔离好,但造价高、启动慢、占地方大。
  • 容器 = 标准化的集装箱: 不管里面装的是电视还是冰箱,集装箱外面的尺寸是统一的,轮船(服务器)只管搬运就行。

虚拟机 vs 容器

对比维度虚拟机 (VM)容器 (Container)
类比独栋别墅公寓单元房
启动速度分钟级秒级甚至毫秒级
占用内存GB级(需要完整OS)MB级(共享宿主机内核)
隔离程度强(独立内核)中(共享内核)
单台服务器运行数量几十个数百到上千个
性能损耗较大(Hypervisor层)极小(接近裸机)

容器的核心技术

容器并不是什么全新的黑科技,它依赖Linux内核的三个核心能力:

  1. Namespace(命名空间) —— 实现"隔离":让每个容器以为自己独享整台机器
  2. Cgroup(控制组) —— 实现"限制":限制每个容器能用多少CPU和内存
  3. UnionFS(联合文件系统) —— 实现"分层":多个只读层叠加,写操作在最上层

容器的使用场景

  • 开发、测试、部署一体化——"在我机器上能跑"不再是借口
  • 创建隔离的运行环境,互不干扰
  • 快速搭建教学环境(一键启动Python/Jupyter/数据库)
  • 大规模服务器部署,一台机器跑几百个服务

思考题

如果你要在学校机房为30位同学搭建独立的Python编程环境,用虚拟机还是容器更合适?为什么?


2. Docker介绍与安装

Docker是什么?

Docker 是一个开源的容器引擎,可以把应用程序和它所需的所有依赖打包成一个标准化的单元(容器),然后"一次打包,到处运行"。

核心组件:

  • Docker Client(客户端) —— 你用来发命令的工具
  • Docker Daemon(守护进程) —— 在后台真正干活的"管家"
  • Docker Image(镜像) —— 容器的"蓝图"或"菜谱"
  • Docker Container(容器) —— 镜像运行起来的"实例"
  • Docker Registry(仓库) —— 存放镜像的"应用商店"

在CentOS 7上安装Docker

# 第一步:安装系统工具
yum install -y yum-utils device-mapper-persistent-data lvm2

# 第二步:添加阿里云Docker源(国内更快)
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

# 第三步:安装Docker CE(社区版,免费)
yum makecache fast
yum -y install docker-ce

# 第四步:启动并设置开机自启
systemctl start docker
systemctl enable docker

验证安装成功

[root@docker ~]# docker info
Client:
 Version:           20.10.x
 ...
Server:
 Containers: 0
 Images: 0
 Storage Driver: overlay2
 ...
[root@docker ~]# docker version
Client: Docker Engine - Community
 Version:           20.10.x
Server: Docker Engine - Community
 Version:           20.10.x

Docker服务管理常用命令

systemctl start docker     # 启动Docker
systemctl stop docker      # 停止Docker
systemctl restart docker   # 重启Docker
systemctl status docker    # 查看Docker状态
systemctl enable docker    # 开机自启

配置国内镜像加速(解决下载慢的问题)

# 创建配置文件
cat > /etc/docker/daemon.json <<EOF
{
  "registry-mirrors": [
    "https://mirror.ccs.tencentyun.com",
    "https://docker.mirrors.ustc.edu.cn"
  ]
}
EOF

# 重启Docker使配置生效
systemctl daemon-reload
systemctl restart docker

常见问题排查

问题原因解决方案
Cannot connect to Docker daemonDocker服务未启动systemctl start docker
permission denied当前用户无权限用sudo或加入docker组
拉取镜像超时网络问题配置镜像加速器

3. Docker客户端操作

核心命令速查表

命令功能类比
docker pull下载镜像从应用商店下载App
docker images查看本地镜像查看手机已安装的App
docker run从镜像创建并运行容器打开App
docker ps查看运行中的容器查看正在运行的App
docker exec进入容器内部执行命令进入App的设置界面
docker logs查看容器日志查看App的崩溃日志
docker stop/start/rm停止/启动/删除容器关闭/重启/卸载App
docker rmi删除镜像卸载App安装包

拉取镜像

# 从Docker Hub拉取nginx镜像
[root@docker ~]# docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
...
Digest: sha256:xxxxx
Status: Downloaded newer image for nginx:latest

# 拉取指定版本的镜像
[root@docker ~]# docker pull mysql:5.7

# 查看本地所有镜像
[root@docker ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
nginx        latest    605c77e29e40   2 weeks ago    142MB
mysql        5.7       1b12f2e9257b   3 weeks ago    462MB

运行容器

# 最基本的运行方式
[root@docker ~]# docker run --name web1 -d nginx
bd054d7986f809cba9bbfecb7a14dc5dbf16e3643aa265dd3a5b136770260333

# 查看运行中的容器
[root@docker ~]# docker ps
CONTAINER ID   IMAGE   COMMAND                  STATUS          PORTS               NAMES
bd054d7986f8   nginx   "nginx -g 'daemon of…"   Up 3 seconds    80/tcp, 443/tcp     web1

docker run常用参数:

-d          # 后台运行(detached模式)
--name      # 给容器起名字
-it         # 交互式运行(分配终端)
-p 8080:80  # 端口映射(宿主机8080 → 容器80)
-P          # 随机端口映射
-v          # 挂载数据卷
-e          # 设置环境变量
--restart=always  # Docker重启后自动启动容器

端口映射

# -P 随机端口映射
[root@docker ~]# docker run -d -P --name web2 nginx
[root@docker ~]# docker ps
CONTAINER ID   IMAGE   STATUS    PORTS                                          NAMES
006d1043652b   nginx   Up 15s    0.0.0.0:32769->80/tcp, 0.0.0.0:32768->443/tcp  web2

# -p 指定端口映射(宿主机端口:容器端口)
[root@docker ~]# docker run -d -p 80:80 --name web3 nginx

# 绑定到指定IP
[root@docker ~]# docker run -d -p 192.168.100.100:80:80 --name web4 nginx

# 多端口映射
[root@docker ~]# docker run -d -p 80:80 -p 443:443 --name web5 nginx

进入容器内部

# 在容器中执行单条命令
[root@docker ~]# docker exec web1 ls /usr/share/nginx/html
50x.html
index.html

# 交互式登录容器(像SSH登录一样)
[root@docker ~]# docker exec -it web1 /bin/bash
root@bd054d7986f8:/# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
root@bd054d7986f8:/# exit

查看容器日志

# 查看容器日志
[root@docker ~]# docker logs web1
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, ...
172.17.0.1 - - [10/Jun/2024:08:30:00 +0000] "GET / HTTP/1.1" 200 615

# 实时跟踪日志(类似tail -f)
[root@docker ~]# docker logs -f web1

容器的生命周期管理

# 停止容器
[root@docker ~]# docker stop web1

# 启动已停止的容器
[root@docker ~]# docker start web1

# 重启容器
[root@docker ~]# docker restart web1

# 暂停/恢复容器
[root@docker ~]# docker pause web1
[root@docker ~]# docker unpause web1

# 强制删除运行中的容器
[root@docker ~]# docker rm -f web1

# 批量停止并删除所有容器
[root@docker ~]# docker stop $(docker ps -q)
[root@docker ~]# docker rm $(docker ps -aq)

查看容器详细信息

# 查看容器的元数据(IP地址、挂载点、环境变量等)
[root@docker ~]# docker inspect web1
[
    {
        "Id": "bd054d7986f8...",
        "State": {
            "Status": "running",
            "Pid": 12345,
            ...
        },
        "NetworkSettings": {
            "IPAddress": "172.17.0.2",
            ...
        }
    }
]

实战:快速启动一个MySQL数据库

[root@docker ~]# docker run --name mysql \
  -p 3306:3306 \
  -e MYSQL_ROOT_PASSWORD=robin123 \
  -d mysql:5.7

# 等待几秒后测试连接
[root@docker ~]# mysql -h 127.0.0.1 -uroot -probin123
mysql> show databases;

思考题

为什么docker run -d nginx之后容器会立即退出?提示:容器需要什么才能保持运行?


4. 镜像管理:打造你自己的"安装包"

镜像是什么?

类比: 镜像就像一份详细的菜谱,记录了操作系统、软件、配置的所有信息。容器就是用这份菜谱"做出来的菜"。同一份菜谱可以做出很多份一样的菜(容器)。

方法一:docker commit(基于容器创建镜像)

适合快速记录你对容器做的修改。

# 第一步:运行一个基础容器并进入
[root@docker ~]# docker run -it --name web centos:7 /bin/bash

# 在容器内安装nginx
[root@web /]# yum install -y epel-release && yum install -y nginx
[root@web /]# echo 'Welcome to my server!' > /usr/share/nginx/html/index.html
[root@web /]# exit

# 第二步:将修改提交为新镜像
[root@docker ~]# docker commit web my-nginx:v1
sha256:4ba57203004b...

# 第三步:用新镜像运行容器
[root@docker ~]# docker run -d -p 80:80 --name web_server my-nginx:v1 \
  nginx -g "daemon off;"

方法二:Dockerfile(推荐,可复现)

Dockerfile是一个文本文件,记录了构建镜像的每一步操作。

# 创建构建目录
[root@docker ~]# mkdir -p /root/my-nginx && cd /root/my-nginx

# 编写Dockerfile
[root@docker my-nginx]# cat Dockerfile
# 基础镜像(必须放在第一行)
FROM centos:7

# 作者信息
MAINTAINER teacher "teacher@school.edu.cn"

# 安装软件(合并RUN减少层数)
RUN yum install -y epel-release && \
    yum install -y nginx && \
    yum clean all && \
    rm -rf /var/cache/yum/*

# 复制自定义首页
COPY index.html /usr/share/nginx/html/index.html

# 声明端口
EXPOSE 80

# 容器启动时运行的命令
CMD ["nginx", "-g", "daemon off;"]
# 构建镜像(注意末尾的"."表示当前目录)
[root@docker my-nginx]# docker build -t my-nginx:v2 .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM centos:7
 ---> eeb6ee3f44bd
Step 2/6 : MAINTAINER teacher "teacher@school.edu.cn"
 ---> Running in abc123
Step 3/6 : RUN yum install -y epel-release && ...
 ---> Running in def456
 ---> ghi789
...
Successfully built xxxxx
Successfully tagged my-nginx:v2

# 运行新镜像
[root@docker my-nginx]# docker run -d -p 80:80 --name web_test my-nginx:v2

Dockerfile常用指令速查

指令作用示例
FROM指定基础镜像FROM centos:7
RUN构建时执行命令RUN yum install -y nginx
COPY复制本地文件到镜像COPY index.html /usr/share/nginx/html/
ADD类似COPY,还能解压tarADD app.tar.gz /opt/
CMD容器启动时执行的命令CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT容器启动时执行的入口命令ENTRYPOINT ["/usr/sbin/nginx"]
EXPOSE声明容器端口EXPOSE 80
ENV设置环境变量ENV APP_HOME /opt/app
WORKDIR设置工作目录WORKDIR /app
VOLUME声明数据卷挂载点VOLUME ["/data"]
USER指定运行用户USER nginx

CMD vs ENTRYPOINT的区别

# CMD:可以被docker run后面的命令覆盖
CMD ["nginx", "-g", "daemon off;"]
# docker run my-image /bin/bash  ← 会覆盖CMD

# ENTRYPOINT:不会被覆盖,docker run后面的参数会追加
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
# docker run my-image -t  ← 变成 nginx -t

镜像的导入与导出

# 导出镜像为文件(方便离线传输)
[root@docker ~]# docker save my-nginx:v2 > /tmp/my-nginx.tar
[root@docker ~]# du -sh /tmp/my-nginx.tar
385M  /tmp/my-nginx.tar

# 在其他机器上导入
[root@docker2 ~]# docker load < /tmp/my-nginx.tar

# 导出容器(注意:会丢失历史记录)
[root@docker ~]# docker export web1 > /tmp/web1-export.tar
[root@docker2 ~]# docker import /tmp/web1-export.tar my-image:v3

镜像优化技巧

  1. 减少镜像层数: 把多个RUN合并成一行
  2. 清理缓存: yum clean all && rm -rf /var/cache/yum/*
  3. 使用.dockerignore: 排除不需要的文件
  4. 多阶段构建: 编译阶段和运行阶段分离
# 多阶段构建示例
# 第一阶段:编译
FROM golang:1.16 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp

# 第二阶段:运行(只复制编译结果)
FROM alpine:latest
COPY --from=builder /app/myapp /myapp
CMD ["/myapp"]

5. Docker仓库:镜像的"应用商店"

Docker Hub(公有仓库)

类比: Docker Hub 就像手机上的"应用商店",你可以搜索、下载别人做好的镜像,也可以上传自己的。

# 登录Docker Hub
[root@docker ~]# docker login
Username: your_username
Password: ********
Login Succeeded

# 给镜像打标签(格式:用户名/镜像名)
[root@docker ~]# docker tag my-nginx:v2 robinround/my-nginx:v2

# 推送到Docker Hub
[root@docker ~]# docker push robinround/my-nginx:v2

# 在其他机器上拉取
[root@docker2 ~]# docker pull robinround/my-nginx:v2

搭建私有Registry

企业内部使用,不依赖外网,更安全更快。

# 服务端(192.168.100.103)
[root@registry ~]# docker pull registry
[root@registry ~]# docker run --name registry_server -d \
  -p 5000:5000 \
  -v /registry:/var/lib/registry \
  registry

# 客户端配置(允许连接非HTTPS仓库)
[root@client ~]# vim /etc/docker/daemon.json
{
  "insecure-registries": ["192.168.100.103:5000"]
}
[root@client ~]# systemctl restart docker

# 打标签并推送
[root@client ~]# docker tag my-nginx:v2 192.168.100.103:5000/my-nginx:v2
[root@client ~]# docker push 192.168.100.103:5000/my-nginx:v2

# 查询私有仓库中的镜像
[root@client ~]# curl -XGET http://192.168.100.103:5000/v2/_catalog
{"repositories":["my-nginx"]}

# 查询标签
[root@client ~]# curl -XGET http://192.168.100.103:5000/v2/my-nginx/tags/list
{"name":"my-nginx","tags":["v2"]}

Harbor(企业级私有仓库)

Harbor 是 VMware 开源的企业级 Docker 镜像仓库,相比 Registry 有以下优势:

  • Web管理界面: 可视化搜索、管理镜像
  • 权限控制: 基于角色的访问控制(RBAC)
  • 镜像复制: 支持跨数据中心同步
  • 安全扫描: 自动检测镜像漏洞
# 安装docker-compose(Harbor依赖)
[root@master ~]# yum install -y docker-compose

# 下载Harbor安装包
[root@master ~]# tar -xvf harbor-online-installer-v2.0.0.tgz
[root@master ~]# cd harbor/

# 配置Harbor
[root@master harbor]# cp harbor.yml.tmpl harbor.yml
[root@master harbor]# vim harbor.yml
# 修改hostname、密码等

# 执行安装
[root@master harbor]# ./install.sh
...
----Harbor has been installed and started successfully.----

# 访问:https://<你的IP>
# 默认账号:admin  默认密码:Harbor12345

6. 数据卷:让数据不再"随容器消亡"

问题的产生

问题: 容器删除后,里面的数据全没了!数据库的数据、用户上传的文件全丢了,怎么办?

类比: 容器就像一个临时工位,你一走东西就清了。数据卷就像你的个人储物柜,不管你换到哪个工位,柜子里的东西都在。

数据卷的特点

  1. 独立于容器生命周期 —— 容器删了,数据卷还在
  2. 存在于宿主机文件系统 —— 可以直接在宿主机上查看和修改
  3. 多个容器可共享 —— 实现容器间的数据共享
  4. 不影响镜像更新 —— 数据和镜像分离

绑定挂载(Bind Mount)

将宿主机的目录映射到容器内部:

# 准备宿主机目录
[root@docker ~]# mkdir -p /web/html
[root@docker ~]# echo "Hello from Host!" > /web/html/index.html

# 运行容器并挂载目录
[root@docker ~]# docker run --name web1 -d -p 80:80 \
  -v /web/html:/usr/share/nginx/html nginx

# 验证:在容器内修改,宿主机同步
[root@docker ~]# docker exec web1 cat /usr/share/nginx/html/index.html
Hello from Host!

# 在宿主机修改,容器内同步
[root@docker ~]# echo "Updated!" > /web/html/index.html
[root@docker ~]# docker exec web1 cat /usr/share/nginx/html/index.html
Updated!

挂载配置文件

# 自定义nginx配置文件
[root@docker ~]# cat /mnt/nginx.conf
server {
    listen 80;
    server_name example.com;
    location / {
        root /usr/share/nginx/html;
        index index.html;
    }
}

# 挂载单个文件
[root@docker ~]# docker run --name web2 -d -p 80:80 \
  -v /mnt/nginx.conf:/etc/nginx/conf.d/default.conf \
  -v /web/html:/usr/share/nginx/html nginx

# 修改配置文件后需要重启容器
[root@docker ~]# docker restart web2

Docker管理的卷(Named Volume)

# 创建命名卷
[root@docker ~]# docker volume create mydata
mydata

# 查看所有卷
[root@docker ~]# docker volume ls
DRIVER    VOLUME NAME
local     mydata

# 使用命名卷
[root@docker ~]# docker run -d --name db \
  -v mydata:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

# 查看卷的实际位置
[root@docker ~]# docker volume inspect mydata
[{
    "Mountpoint": "/var/lib/docker/volumes/mydata/_data",
    ...
}]

数据卷容器(容器间共享数据)

# 创建数据卷容器
[root@docker ~]# docker run -it -d -v /dbdata --name dbdata centos:7

# 其他容器使用这个数据卷
[root@docker ~]# docker run -it -d --volumes-from dbdata --name app1 centos:7
[root@docker ~]# docker run -it -d --volumes-from dbdata --name app2 centos:7

# 在app1中写入数据
[root@docker ~]# docker exec app1 bash -c "echo 'shared data' > /dbdata/test.txt"

# 在app2中可以读到
[root@docker ~]# docker exec app2 cat /dbdata/test.txt
shared data

7. 网络配置:容器如何"上网冲浪"

问题

问题: 容器内部运行了一个Web服务监听80端口,但外面的人怎么访问到它?

四种网络模式

模式参数说明类比
bridge--net=bridge(默认)独立网络,通过docker0网桥通信公寓有自己的门牌号
host--net=host直接使用宿主机网络合租,共用一个门牌号
none--net=none无网络,需手动配置没有网线,完全断网
container--net=container:NAME共享指定容器的网络和室友共用网络

bridge模式(默认)

# 默认就是bridge模式
[root@docker ~]# docker run -d --name web1 -p 80:80 nginx

# 查看容器IP
[root@docker ~]# docker inspect web1 | grep IPAddress
            "IPAddress": "172.17.0.2",

host模式

# host模式直接使用宿主机IP和端口
[root@docker ~]# docker run -d --net=host --name web2 nginx

# 不需要端口映射,直接通过宿主机IP:80访问
[root@docker ~]# curl http://localhost:80
<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>

自定义网络

# 创建自定义网络(支持指定IP和容器间DNS解析)
[root@docker ~]# docker network create --subnet=172.18.0.0/16 mynetwork

# 查看网络列表
[root@docker ~]# docker network ls
NETWORK ID     NAME       DRIVER    SCOPE
abc123         bridge     bridge    local
def456         mynetwork  bridge    local

# 在自定义网络中运行容器并指定IP
[root@docker ~]# docker run -d --name web3 \
  --net mynetwork --ip 172.18.0.10 nginx

# 自定义网络内的容器可以通过容器名互相访问(内置DNS)
[root@docker ~]# docker exec web3 ping web4

容器互联(--link)

# 启动MySQL容器
[root@docker ~]# docker run -d --name mysqldb \
  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

# 启动Nginx容器并链接到MySQL
[root@docker ~]# docker run -d -p 80:80 --name web \
  --link mysqldb:mysql nginx

# 在web容器中可以直接通过"mysql"主机名访问
[root@docker ~]# docker exec -it web /bin/bash
root@web:/# cat /etc/hosts
172.17.0.2  mysql 5b59dcbdd017 mysqldb

root@web:/# env | grep MYSQL
MYSQL_PORT_3306_TCP_ADDR=172.17.0.2
MYSQL_PORT_3306_TCP_PORT=3306

注意: --link是旧方式,推荐使用自定义网络(支持DNS解析,更灵活)。


8. 网络架构:深入理解docker0网桥

docker0是什么?

当你安装Docker后,系统会自动创建一个名为docker0的虚拟网桥,相当于一台"虚拟交换机"。所有容器都连接到这台交换机上。

[root@docker ~]# ip addr show docker0
8: docker0: <BROADCAST,MULTICAST,UP> mtu 1500
    link/ether 02:42:ac:11:00:00 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0

默认地址分配:

  • docker0 IP:172.17.0.1
  • 子网掩码:255.255.0.0(即 /16)
  • 可用地址:65534个(172.17.0.2 ~ 172.17.255.254)

容器网络的创建过程

  1. Docker创建一对虚拟网卡(veth pair),就像一根网线的两端
  2. 一端放入容器内,命名为eth0
  3. 另一端接入docker0网桥,命名为vethXXX
  4. 从docker0子网中分配IP给容器
  5. 设置docker0的IP(172.17.0.1)为容器的默认网关

端口映射的原理

当使用-p 80:80时,Docker会自动添加iptables规则:

[root@docker ~]# iptables -t nat -L -n | grep 80
DNAT  tcp  --  0.0.0.0/0  0.0.0.0/0  tcp dpt:80  to:172.17.0.2:80

这条规则的意思是:所有访问宿主机80端口的流量,都会被转发到容器172.17.0.2的80端口。

CNM(容器网络模型)

Docker的网络架构基于CNM(Container Network Model),核心概念:

  • Sandbox(沙箱) —— 容器的网络栈(IP、路由、防火墙规则等)
  • Endpoint(端点) —— 连接沙箱和网络的虚拟网卡
  • Network(网络) —— 一组可以互相通信的端点

9. 跨主机容器通讯

问题

问题: 两台物理机上的容器,默认不能互相通信(因为各自的docker0分配了相同的IP段),怎么办?

方案一:直接路由

原理: 给两台主机的docker0分配不同子网,然后添加路由规则。

# 主机1:配置docker0使用172.17.0.0/24
[root@host1 ~]# cat /etc/docker/daemon.json
{"bip":"172.17.0.254/24"}

# 主机2:配置docker0使用172.17.1.0/24
[root@host2 ~]# cat /etc/docker/daemon.json
{"bip":"172.17.1.254/24"}

# 两台主机都重启Docker
systemctl restart docker

# 主机1添加路由(到对方网段,走对方物理IP)
[root@host1 ~]# route add -net 172.17.1.0 netmask 255.255.255.0 gw 192.168.0.205

# 主机2添加路由
[root@host2 ~]# route add -net 172.17.0.0 netmask 255.255.255.0 gw 192.168.0.204

# 清理iptables NAT规则(避免SNAT干扰)
[root@host1 ~]# iptables -t nat -F POSTROUTING
[root@host1 ~]# iptables -t nat -A POSTROUTING \
  -s 172.17.0.0/24 ! -d 172.17.0.0/16 -j MASQUERADE

# 测试:在主机1的容器中ping主机2的容器
[root@host1 ~]# docker run -it --name test1 centos:7 /bin/bash
[root@test1 /]# ping 172.17.1.2

方案二:Flannel覆盖网络

Flannel 会自动为每台主机分配不重叠的子网,并通过VXLAN等技术实现跨节点通信。Kubernetes默认使用类似方案。

# 安装etcd(Flannel的配置存储)
yum install -y etcd

# 安装Flannel
yum install -y flannel

# 配置Flannel
[root@node1 ~]# cat /etc/sysconfig/flanneld
FLANNEL_ETCD_ENDPOINTS="http://192.168.0.204:2379,http://192.168.0.205:2379"
FLANNEL_ETCD_PREFIX="/atomic.io/network"

# 向etcd写入网络配置
etcdctl mk /atomic.io/network/config \
  '{"Network":"10.10.0.0/16", "SubnetMin":"10.10.1.0", "SubnetMax":"10.10.254.0"}'

# 启动Flannel
systemctl start flanneld && systemctl enable flanneld

# 重启Docker(Docker会自动使用Flannel分配的网络)
systemctl daemon-reload
systemctl restart docker

# 验证:两台主机上的容器可以互相ping通
[root@test1 /]# ping 10.10.49.2

10. Namespace与Cgroup:隔离与限制的魔法

Namespace:实现"隔离"

类比: 就像考试时每位同学有独立的隔间,看不到别人的试卷,以为自己独享整个教室。

Docker利用Linux的6种Namespace实现容器的隔离:

Namespace隔离什么类比
PID进程编号每个人给自己的试卷编号都是"第1题"
NET网络(IP、端口、路由)每人有独立的网络接口
MNT文件系统挂载点每人有自己的文件柜
UTS主机名和域名每人可以给自己的隔间起名
IPC进程间通信每人有独立的信箱
USER用户和组ID在自己的隔间里是"班长",在外面是普通同学
# 查看当前进程的Namespace
[root@docker ~]# ls -l /proc/$$/ns
lrwxrwxrwx 1 root root 0 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 uts -> uts:[4026531838]

# 在容器内看到的主机名是独立的(UTS Namespace)
[root@docker ~]# docker exec web1 hostname
bd054d7986f8

# 容器内的进程PID从1开始(PID Namespace)
[root@docker ~]# docker exec web1 ps aux
USER    PID  %CPU  COMMAND
root      1  0.0   nginx: master process
nginx     7  0.0   nginx: worker process

Cgroup:实现"限制"

类比: 就像食堂给每个班级分配的饭菜配额——不管你多饿,最多只能拿这么多。

# 查看系统支持的cgroup子系统
[root@docker ~]# lssubsys
cpuset
cpu,cpuacct
memory
devices
freezer
net_cls
blkio
perf_event
hugetlb

主要子系统功能:

子系统作用
cpu控制CPU时间分配
memory限制内存使用量
blkio限制磁盘IO
cpuset绑定到特定CPU核心
devices控制设备访问权限

实战:限制容器的CPU和内存

# 限制容器最多使用512MB内存和50% CPU
[root@docker ~]# docker run -d --name limited-app \
  --memory=512m \
  --cpus=0.5 \
  nginx

# 验证资源限制
[root@docker ~]# docker inspect limited-app | grep -A5 "Memory"
            "Memory": 536870912,
            "NanoCpus": 500000000,

手动体验cgroup的威力

# 创建一个CPU控制组
[root@docker ~]# cd /sys/fs/cgroup/cpu/
[root@docker cpu]# mkdir test-group

# 设置CPU配额:每100000微秒中只用30000微秒(30%)
[root@docker cpu]# echo 30000 > test-group/cpu.cfs_quota_us

# 写一个死循环脚本
[root@docker ~]# cat > /tmp/cpu_test.sh << 'EOF'
#!/bin/bash
while true; do : ; done
EOF
chmod +x /tmp/cpu_test.sh
/tmp/cpu_test.sh &

# 此时top可以看到CPU占用100%
# 将进程加入控制组
[root@docker ~]# echo $! > /sys/fs/cgroup/cpu/test-group/tasks

# 再看top,CPU占用降到约30%

11. Overlay文件系统:写时复制的秘密

什么是OverlayFS?

类比: 想象你在用一张透明的薄膜覆盖在一张打印好的纸上。你可以在薄膜上写字、涂改,但原来的纸不会受影响。如果薄膜上某个位置和纸上同一位置都有内容,你看到的是薄膜上的(上层覆盖下层)。

核心特性

  1. 分层叠加: 下层(lowerdir)只读,上层(upperdir)可写
  2. 同名覆盖: 上下层同名文件,只显示上层的
  3. 写时复制(Copy-on-Write): 修改下层文件时,先复制到上层再修改

Docker镜像的分层结构

可写层(容器层)   ← 容器运行时修改的文件写在这里
─────────────────
只读层 5 (nginx)
只读层 4 (epel-release)
只读层 3 (yum update)
只读层 2 (系统配置)
只读层 1 (CentOS base)
# 查看镜像的分层历史
[root@docker ~]# docker history nginx:latest
IMAGE          CREATED       SIZE      COMMENT
abc123         2 days ago    0B        nginx: daemon off
def456         2 days ago    101MB     yum install nginx
ghi789         2 days ago    19MB      yum install epel-release
jkl012         3 days ago    106MB     yum update

OverlayFS的工作原理

Lowerdir(只读层)          Upperdir(可写层)
├── index.html (原始)       ├── newfile.txt (新建的文件)
├── config.conf             └── config.conf (修改后的版本)
└── style.css

Merged(合并视图 = 容器看到的)
├── index.html    ← 来自Lower(未修改)
├── config.conf   ← 来自Upper(覆盖了下层)
├── style.css     ← 来自Lower(未修改)
└── newfile.txt   ← 来自Upper(新建)

删除操作的策略:

  • 删除只在下层的文件 → 在上层创建一个"白名单"标记文件
  • 删除只在上层的文件 → 直接从上层删除
  • 修改只在下层的文件 → 先复制到上层,再修改(写时复制)

思考题

为什么10个基于同一个CentOS镜像的容器,只占用一份CentOS的磁盘空间?如果每个容器都修改了不同的文件呢?


第二部分:Kubernetes集群编排


1. K8s介绍:容器世界的"交响乐指挥家"

为什么需要Kubernetes?

问题: 你有了Docker,可以在一台机器上跑几十个容器。但如果有10台机器、100个服务呢?手动管理太累了!

类比: Docker就像每位乐手自己练习,Kubernetes就是指挥家——它告诉每位乐手什么时候开始、什么时候停止,确保整个交响乐团协调一致。

K8s能做什么?

  • 自动部署和回滚: 一键部署100个容器,一键升级/回退
  • 自动修复: 容器挂了?自动重启。节点挂了?自动迁移到其他节点
  • 自动伸缩: 访问量大了?自动增加副本。访问量小了?自动缩减
  • 负载均衡: 自动将流量分发到健康的容器
  • 服务发现: 容器之间通过名字就能找到对方

K8s的架构

┌─────────────────────────────────────────────────────┐
│                    Master节点(大脑)                  │
│  ┌──────────┐  ┌──────────┐  ┌───────────────────┐  │
│  │API Server│  │Scheduler │  │Controller Manager │  │
│  │(对外接口) │  │(调度决策) │  │(状态维护)          │  │
│  └──────────┘  └──────────┘  └───────────────────┘  │
│  ┌──────────┐                                        │
│  │  etcd    │ ← 集群的"记事本",存储所有状态           │
│  └──────────┘                                        │
└─────────────────────────────────────────────────────┘
        │
        ├──→ ┌─────────────────────────┐
        │    │     Node节点(工人)       │
        │    │  ┌──────┐  ┌──────────┐ │
        │    │  │kubelet│  │kube-proxy│ │
        │    │  │(管家) │  │(网络代理)│ │
        │    │  └──────┘  └──────────┘ │
        │    │  ┌──────────────────┐   │
        │    │  │  Pod Pod Pod Pod │   │
        │    │  └──────────────────┘   │
        │    └─────────────────────────┘
        │
        └──→ ┌─────────────────────────┐
             │     Node节点(工人)       │
             │    ...                   │
             └─────────────────────────┘

核心组件说明:

组件位置功能类比
API ServerMaster集群的统一入口,接收所有请求前台接待
etcdMaster存储集群所有状态数据记事本/数据库
SchedulerMaster决定Pod运行在哪个Node上HR分配工位
Controller ManagerMaster确保实际状态=期望状态质检员
kubeletNode管理本节点上的Pod和容器车间主任
kube-proxyNode维护网络规则,实现负载均衡邮递员

2. YAML基础:写给K8s的"说明书"

为什么是YAML?

K8s用YAML文件来描述"我想要什么"——创建什么资源、多少个副本、用什么镜像。

类比: YAML就像你写给指挥家的乐谱——你只需要写清楚"要3个小提琴、2个大提琴",指挥家会帮你安排好一切。

YAML语法要点

# 键值对(注意冒号后面有空格)
name: my-app
version: 1.0

# 列表(用 - 表示)
fruits:
  - apple
  - banana
  - orange

# 嵌套对象(用缩进表示层级,只能用空格,不能用Tab!)
server:
  host: 192.168.1.1
  port: 8080
  config:
    debug: true
    log_level: info

# 多行字符串
description: |
  这是一个多行
  文本内容,保留换行符

# 单行字符串(折叠换行)
summary: >
  这是单行文本
  换行会被折叠

一个完整的K8s YAML示例

apiVersion: apps/v1          # API版本
kind: Deployment             # 资源类型
metadata:                    # 元数据
  name: nginx-deployment     # 名称
  labels:                    # 标签
    app: nginx
spec:                        # 规格定义
  replicas: 3                # 副本数
  selector:                  # 选择器
    matchLabels:
      app: nginx
  template:                  # Pod模板
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx          # 容器名
        image: nginx:1.21    # 镜像
        ports:
        - containerPort: 80  # 容器端口

常见YAML错误

错误示例正确
使用了Tab缩进\tname: app name: app(空格)
冒号后缺少空格name:appname: app
缩进层级不一致混用2空格和4空格统一使用2空格

3. Master与Node部署

环境准备

主机IP角色
master192.168.0.200Master节点
node1192.168.0.201Worker节点
node2192.168.0.202Worker节点

所有节点都要执行:

# 1. 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld

# 2. 关闭SELinux
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config

# 3. 关闭Swap分区(K8s要求)
swapoff -a
sed -i '/swap/s/^/#/' /etc/fstab

# 4. 配置内核参数
cat > /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
modprobe br_netfilter
sysctl -p /etc/sysctl.d/k8s.conf

# 5. 安装Docker(参照第一部分)
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum -y install docker-ce
systemctl start docker && systemctl enable docker

# 6. 安装kubeadm、kubelet、kubectl
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
EOF
yum install -y kubelet kubeadm kubectl
systemctl enable kubelet

Master节点初始化

# 初始化集群
[root@master ~]# kubeadm init \
  --kubernetes-version=v1.22.0 \
  --pod-network-cidr=10.244.0.0/16 \
  --image-repository=registry.aliyuncs.com/google_containers

# 成功后会输出如下信息:
Your Kubernetes master has initialized successfully!

# 配置kubectl访问(root用户)
export KUBECONFIG=/etc/kubernetes/admin.conf
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
source ~/.bash_profile

# 或者(普通用户)
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 安装网络插件(Flannel)
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

Node节点加入集群

# 使用kubeadm init输出的join命令
[root@node1 ~]# kubeadm join 192.168.0.200:6443 \
  --token h4lewk.4vex9tcwtdwfkn05 \
  --discovery-token-ca-cert-hash sha256:7118c498...

# 如果token过期(默认24小时),在Master上重新生成
[root@master ~]# kubeadm token create --print-join-command

验证集群状态

# 查看所有节点
[root@master ~]# kubectl get nodes
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   50m   v1.22.0
node1    Ready    <none>   16m   v1.22.0
node2    Ready    <none>   10m   v1.22.0

# 查看集群信息
[root@master ~]# kubectl cluster-info
Kubernetes master is running at https://192.168.0.200:6443
CoreDNS is running at https://192.168.0.200:6443/api/v1/...

# 查看各组件健康状态
[root@master ~]# kubectl get cs
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok
controller-manager   Healthy   ok
etcd-0               Healthy   {"health": "true"}

4. kubectl使用:与集群对话

kubectl命令分类

# 查看帮助
kubectl --help

# 查看子命令帮助
kubectl get --help

最常用的命令

# ===== 查看资源 =====
kubectl get pods                    # 查看Pod
kubectl get pods -o wide            # 更多信息(含IP和Node)
kubectl get pods --all-namespaces   # 所有命名空间
kubectl get deployments             # 查看Deployment
kubectl get services                # 查看Service
kubectl get nodes                   # 查看节点
kubectl get all                     # 查看所有资源

# ===== 详细描述 =====
kubectl describe pod <pod-name>     # Pod详细信息
kubectl describe service <svc-name> # Service详细信息
kubectl describe node <node-name>   # 节点详细信息

# ===== 创建/更新资源 =====
kubectl apply -f nginx.yaml         # 创建或更新资源
kubectl create -f nginx.yaml        # 仅创建

# ===== 删除资源 =====
kubectl delete pod <pod-name>       # 删除Pod
kubectl delete -f nginx.yaml        # 按文件删除
kubectl delete deployment nginx     # 删除Deployment

# ===== 调试和排错 =====
kubectl logs <pod-name>             # 查看Pod日志
kubectl logs -f <pod-name>          # 实时跟踪日志
kubectl exec -it <pod-name> -- /bin/bash  # 进入Pod
kubectl top pods                    # 查看Pod资源使用
kubectl top nodes                   # 查看节点资源使用

# ===== 扩缩容 =====
kubectl scale deployment nginx --replicas=5  # 扩到5个副本

# ===== 标签和注解 =====
kubectl label pod my-pod env=prod   # 添加标签
kubectl get pods -l env=prod        # 按标签筛选

实战:创建一个简单的应用

# 方法一:命令行快速创建
[root@master ~]# kubectl run httpd-app --image=httpd --replicas=2

# 方法二:YAML文件(推荐)
[root@master ~]# cat nginx-deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80
[root@master ~]# kubectl apply -f nginx-deploy.yaml
deployment.apps/nginx-deploy created

[root@master ~]# kubectl get pods -o wide
NAME                            READY   STATUS    IP           NODE
nginx-deploy-5fbccd7c6c-2fq46   1/1     Running   10.244.1.3   node1
nginx-deploy-5fbccd7c6c-szzn7   1/1     Running   10.244.1.2   node2

[root@master ~]# curl 10.244.1.3
<html><body><h1>Welcome to nginx!</h1></body></html>

5. Dashboard:集群的Web管理界面

部署Dashboard

# 下载Dashboard配置文件
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

# 修改Service类型为NodePort(使外部可以访问)
# 编辑recommended.yaml中的Service部分:
kind: Service
apiVersion: v1
metadata:
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: NodePort          # 改为NodePort
  ports:
  - port: 443
    targetPort: 8443
    nodePort: 32666       # 指定外部访问端口
  selector:
    k8s-app: kubernetes-dashboard
# 部署
[root@master ~]# kubectl apply -f recommended.yaml

# 查看Pod状态
[root@master ~]# kubectl get pods --all-namespaces | grep dashboard
kubernetes-dashboard   kubernetes-dashboard-cfc74c45b-tfsx9   1/1  Running  0  6m

创建管理员账号

# 创建管理员ServiceAccount
cat > dashboard-admin.yaml << 'EOF'
apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard-admin
  namespace: kubernetes-dashboard
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: dashboard-admin
subjects:
- kind: ServiceAccount
  name: dashboard-admin
  namespace: kubernetes-dashboard
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io
EOF

[root@master ~]# kubectl apply -f dashboard-admin.yaml

# 获取登录Token
[root@master ~]# kubectl -n kubernetes-dashboard describe secret \
  $(kubectl -n kubernetes-dashboard get secret | grep dashboard-admin | awk '{print $1}')
...
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9...(复制这串Token)

访问Dashboard

浏览器打开:https://<NodeIP>:32666

输入上面获取的Token登录即可看到集群管理界面。


6. Pod:K8s的最小调度单元

Pod是什么?

类比: Pod就像一间公寓单元房——里面可以住一到几个人(容器),他们共享水电(网络和存储),但各有各的房间。

Pod是K8s中最小的调度单位,一个Pod可以包含一个或多个容器。

单容器Pod

apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80

多容器Pod(Sidecar模式)

apiVersion: v1
kind: Pod
metadata:
  name: web-with-logger
spec:
  containers:
  - name: web
    image: nginx:1.21
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  - name: logger        # Sidecar容器:负责收集日志
    image: busybox
    command: ['sh', '-c', 'tail -f /var/log/nginx/access.log']
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  volumes:
  - name: shared-logs
    emptyDir: {}

Pod的生命周期

Pending → Running → Succeeded/Failed

健康检查(Probes)

apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-probes
spec:
  containers:
  - name: nginx
    image: nginx:1.21
    # 存活探针:检查容器是否存活
    livenessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 10   # 启动后10秒开始检查
      periodSeconds: 5          # 每5秒检查一次
    # 就绪探针:检查容器是否准备好接收流量
    readinessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 3

重启策略

spec:
  restartPolicy: Always     # 总是重启(默认)
  # restartPolicy: OnFailure  # 失败时重启
  # restartPolicy: Never      # 从不重启

7. ReplicaSet与Deployment:副本管理与滚动更新

ReplicaSet:确保副本数量

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: nginx-rs
spec:
  replicas: 3          # 始终保持3个副本
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21

注意: 实际使用中很少直接创建ReplicaSet,而是通过Deployment来管理。

Deployment:推荐的生产方式

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deploy
spec:
  replicas: 3
  strategy:
    type: RollingUpdate          # 滚动更新策略
    rollingUpdate:
      maxSurge: 1                # 最多多出1个Pod
      maxUnavailable: 0          # 不允许有Pod不可用
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21
        ports:
        - containerPort: 80

常用操作

# 创建Deployment
[root@master ~]# kubectl apply -f nginx-deploy.yaml

# 查看Deployment
[root@master ~]# kubectl get deployments
NAME           READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deploy   3/3     3            3           2m

# 扩容
[root@master ~]# kubectl scale deployment nginx-deploy --replicas=5
deployment.apps/nginx-deploy scaled

# 更新镜像(触发滚动更新)
[root@master ~]# kubectl set image deployment/nginx-deploy nginx=nginx:1.22
deployment.apps/nginx-deploy image updated

# 查看滚动更新状态
[root@master ~]# kubectl rollout status deployment/nginx-deploy
deployment "nginx-deploy" successfully rolled out

# 回滚到上一个版本
[root@master ~]# kubectl rollout undo deployment/nginx-deploy
deployment.apps/nginx-deploy rolled back

# 查看历史版本
[root@master ~]# kubectl rollout history deployment/nginx-deploy
REVISION  CHANGE-CAUSE
1         <none>
2         <none>

滚动更新的原理

旧版本:Pod1(v1)  Pod2(v1)  Pod3(v1)
           ↓
更新中:  Pod1(v1)  Pod2(v1)  Pod3(v2)  ← 新建1个v2,删1个v1
           ↓
更新中:  Pod1(v1)  Pod2(v2)  Pod3(v2)  ← 继续
           ↓
完成:    Pod1(v2)  Pod2(v2)  Pod3(v2)  ← 全部更新为v2

8. Service:让外部用户找到你的服务

问题

问题: Pod的IP是动态的(重启就变),而且外部无法直接访问Pod IP。怎么让用户稳定地访问服务?

类比: Service就像一个"总机号码"——不管员工(Pod)怎么换工位,打总机号码总能转接到对的人。

Service的三种端口

端口类型说明
targetPort容器内的端口(Pod的端口)
portService在集群内的端口(:port)
nodePortService在节点上的端口(外部访问用)

ClusterIP(默认,仅集群内访问)

apiVersion: v1
kind: Service
metadata:
  name: nginx-clusterip
spec:
  type: ClusterIP
  selector:
    app: nginx              # 选择标签为app=nginx的Pod
  ports:
  - port: 80               # Service端口
    targetPort: 80          # Pod端口

NodePort(外部可访问)

apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port: 80               # 集群内Service端口
    targetPort: 80          # Pod端口
    nodePort: 30080         # 节点端口(范围30000-32767)
[root@master ~]# kubectl apply -f nginx-svc.yaml
[root@master ~]# kubectl get svc
NAME             TYPE       CLUSTER-IP     PORT(S)        AGE
nginx-nodeport   NodePort   10.96.100.50   80:30080/TCP   10s

# 外部访问:http://<任意NodeIP>:30080

LoadBalancer(云环境自动分配外部IP)

apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  type: LoadBalancer
  selector:
    app: nginx
  ports:
  - port: 80
    targetPort: 80

ExternalName(别名映射)

apiVersion: v1
kind: Service
metadata:
  name: my-db
spec:
  type: ExternalName
  externalName: db.example.com  # 将my-db映射到外部域名

9. Volume:K8s中的数据持久化

emptyDir:临时共享空间

Pod创建时创建,Pod删除时数据丢失。适合容器间临时共享数据。

volumes:
- name: shared-data
  emptyDir: {}

hostPath:挂载宿主机目录

volumes:
- name: host-volume
  hostPath:
    path: /opt/data          # 宿主机目录
    type: DirectoryOrCreate

PersistentVolume(PV)与PersistentVolumeClaim(PVC)

类比:

  • PV = 学校图书馆的书架(由管理员预先创建)
  • PVC = 你的借书申请(你告诉系统你需要多大的空间)
  • K8s自动把你的PVC和合适的PV绑定
# PV定义(管理员创建)
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv001
spec:
  capacity:
    storage: 5Gi             # 容量
  accessModes:
  - ReadWriteOnce            # 读写权限,只能被单个Node挂载
  nfs:
    server: 192.168.0.100    # NFS服务器地址
    path: /opt/nfsshare      # 共享路径
---
# PVC定义(用户创建)
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi           # 申请5GB空间
---
# Pod引用PVC
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: app
    image: nginx
    volumeMounts:
    - mountPath: /data
      name: my-volume
  volumes:
  - name: my-volume
    persistentVolumeClaim:
      claimName: my-pvc
[root@master ~]# kubectl get pv
NAME    CAPACITY   ACCESS MODES   STATUS    CLAIM            AGE
pv001   5Gi        RWO            Bound     default/my-pvc   2m

[root@master ~]# kubectl get pvc
NAME      STATUS   VOLUME   CAPACITY   ACCESS MODES   AGE
my-pvc    Bound    pv001    5Gi        RWO            2m

ConfigMap:配置文件管理

# 从文件创建ConfigMap
[root@master ~]# kubectl create configmap nginx-config \
  --from-file=nginx.conf=/etc/nginx/nginx.conf
# 在Pod中使用ConfigMap
apiVersion: v1
kind: Pod
metadata:
  name: nginx-with-config
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: config-volume
      mountPath: /etc/nginx/conf.d
  volumes:
  - name: config-volume
    configMap:
      name: nginx-config

Secret:敏感信息管理

# 创建Secret
[root@master ~]# kubectl create secret generic db-secret \
  --from-literal=username=admin \
  --from-literal=password=S3cur3P@ss
# 在Pod中使用Secret作为环境变量
env:
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: password

10. Job与CronJob:批处理与定时任务

Job:一次性任务

类比: 就像期末考试的阅卷任务——完成后就结束了,不需要一直运行。

apiVersion: batch/v1
kind: Job
metadata:
  name: math-calculation
spec:
  completions: 3              # 总共需要完成3次
  parallelism: 2              # 最多同时运行2个
  backoffLimit: 3             # 失败重试最多3次
  template:
    spec:
      containers:
      - name: calculator
        image: python:3.9
        command: ["python", "-c", "print('计算完成!结果: 42')"]
      restartPolicy: Never
[root@master ~]# kubectl apply -f job.yaml
[root@master ~]# kubectl get jobs
NAME               COMPLETIONS   DURATION   AGE
math-calculation   3/3           30s        2m

[root@master ~]# kubectl logs math-calculation-xxx
计算完成!结果: 42

CronJob:定时任务

类比: 就像每周一早上自动收作业一样。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: daily-backup
spec:
  schedule: "0 2 * * *"        # 每天凌晨2点执行
  # cron表达式:分 时 日 月 星期
  # "*/5 * * * *" = 每5分钟
  # "0 9 * * 1" = 每周一9点
  successfulJobsHistoryLimit: 3   # 保留最近3个成功的Job
  failedJobsHistoryLimit: 1       # 保留最近1个失败的Job
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: backup
            image: busybox
            command: ["sh", "-c", "echo '备份完成: $(date)'"]
          restartPolicy: Never
[root@master ~]# kubectl apply -f cronjob.yaml
[root@master ~]# kubectl get cronjobs
NAME           SCHEDULE    LAST SCHEDULE   AGE
daily-backup   0 2 * * *   2h ago          1d

11. DaemonSet:每个节点的"管家"

什么是DaemonSet?

类比: 就像每栋教学楼都有一个保洁员——确保每个Node上都运行且只运行一个Pod副本。

典型应用场景:

  • 日志收集(Fluentd、Filebeat)
  • 系统监控(Prometheus Node Exporter)
  • 网络插件(Flannel、Calico)
  • 存储守护进程
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      containers:
      - name: node-exporter
        image: prom/node-exporter:v1.3.1
        ports:
        - containerPort: 9100
          hostPort: 9100       # 使用宿主机端口
[root@master ~]# kubectl apply -f daemonset.yaml
[root@master ~]# kubectl get daemonset -n monitoring
NAME            DESIRED   CURRENT   READY   NODES   AGE
node-exporter   3         3         3       3       2m
# DESIRED=3表示3个节点各运行1个Pod

12. HPA:自动弹性伸缩

什么是HPA?

类比: 就像学校食堂——中午人多的时候多开几个窗口,人少的时候关掉多余窗口,节省人力。

HPA(Horizontal Pod Autoscaler)根据CPU或内存使用率,自动增减Pod副本数。

前提条件

需要安装metrics-server来采集资源使用数据:

# 安装metrics-server
kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml

# 验证
[root@master ~]# kubectl top nodes
NAME     CPU(cores)   CPU%   MEMORY(bytes)   MEMORY%
master   150m         7%     1024Mi          52%
node1    80m          4%     512Mi           26%
node2    90m          4%     480Mi           24%

创建HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: nginx-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: nginx-deploy
  minReplicas: 2              # 最少2个副本
  maxReplicas: 10             # 最多10个副本
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 50    # CPU使用率超过50%则扩容
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80    # 内存使用率超过80%则扩容

或者用命令行快速创建:

[root@master ~]# kubectl autoscale deployment nginx-deploy \
  --min=2 --max=10 --cpu-percent=50

[root@master ~]# kubectl get hpa
NAME        REFERENCE                 TARGETS   MINPODS   MAXPODS   REPLICAS
nginx-hpa   Deployment/nginx-deploy   20%/50%   2         10        3

弹性伸缩的过程

正常:CPU 20% → 3个Pod(正常)
        ↓ 流量高峰
CPU 70% → HPA检测到超阈值 → 扩到5个Pod
        ↓ 流量恢复
CPU 30% → HPA检测到低于阈值 → 缩回3个Pod(等待冷却期后)

总结:Docker与K8s知识图谱

Docker(单机容器管理)
├── 核心概念
│   ├── 镜像(Image)—— 菜谱
│   ├── 容器(Container)—— 做出来的菜
│   └── 仓库(Registry)—— 菜谱大全
├── 底层技术
│   ├── Namespace → 隔离(PID/NET/MNT/UTS/IPC/USER)
│   ├── Cgroup → 限制(CPU/内存/IO)
│   └── OverlayFS → 分层文件系统
└── 网络
    ├── bridge/host/none/container模式
    ├── 端口映射(-p/-P)
    └── 跨主机通讯(Flannel/直接路由)

Kubernetes(集群容器编排)
├── 架构
│   ├── Master: API Server + Scheduler + Controller + etcd
│   └── Node: kubelet + kube-proxy + Pod
├── 核心资源
│   ├── Pod → 最小调度单元
│   ├── Deployment → 副本管理 + 滚动更新
│   ├── Service → 服务发现 + 负载均衡
│   └── Volume → 数据持久化(PV/PVC/ConfigMap/Secret)
├── 工作负载
│   ├── Job/CronJob → 批处理/定时任务
│   └── DaemonSet → 每节点一个
└── 高级功能
    └── HPA → 自动弹性伸缩

常见问题FAQ

Docker相关

Q1:容器和虚拟机到底该选哪个?

  • 需要强隔离、运行不同操作系统 → 虚拟机
  • 需要快速部署、高密度、微服务 → 容器

Q2:docker run之后容器立即退出?

  • 原因:容器的前台进程执行完毕了
  • 解决:确保有持续运行的前台进程,如 nginx -g "daemon off;"

Q3:如何查看容器的IP地址?

  • docker inspect <容器名> | grep IPAddress

Kubernetes相关

Q4:Pod一直处于Pending状态?

  • kubectl describe pod <pod-name> 查看Events
  • 常见原因:资源不足、镜像拉取失败、PVC未绑定

Q5:Service访问不通?

  • 检查Pod是否Running:kubectl get pods -l app=<label>
  • 检查Service选择器:kubectl describe svc <svc-name>
  • 检查kube-proxy是否正常:kubectl get pods -n kube-system | grep proxy

Q6:如何查看某个Pod在哪个Node上运行?

  • kubectl get pods -o wide

linux服务
容器 虚拟机
License:  CC BY 4.0
Share

Further Reading

Jul 4, 2026

监控虚拟化与集群

监控系统、虚拟化与集群存储 -- 运维实战指南 第一部分:监控系统 -- 给服务器做"体检" 1.1 为什么需要监控系统? 生活类比:想象你是一个学校的校医,负责1000名学生的健康。如果每天都要一个个量体温、测心率,你肯定累趴了。最好的办法是:每个学生戴一个智能手环,实时把健康数据发到你办公室的大

Jul 4, 2026

Docker与Kubernetes

Docker容器与Kubernetes集群 -- 从零开始的实战指南 目录 第一部分:Docker容器技术 容器概念:为什么我们需要容器? Docker介绍与安装 Docker客户端操作

Jul 4, 2026

Shell脚本与版本控制

Shell 脚本编程与版本控制实战指南 环境假设:CentOS 7/8 或 RHEL 系列 Linux 目录 第一部分:Shell 脚本编程 1. Shell 脚本介绍 2. 脚本基础知识及变量

OLDER

监控虚拟化与集群

NEWER

Nginx与Tomcat

Recently Updated

  • 监控虚拟化与集群
  • Docker与Kubernetes
  • Nginx与Tomcat
  • 网络协议与安全
  • 数据库与自动化运维

Trending Tags

安全 Linux系统 nginx 日志管理 Linux服务 网络 Linux高级 pxe 中间件 python

Contents

©2026 青青子衿的拾枝杂谈 . Some rights reserved.

Using the Halo theme Chirpy