K8S应用流程安全(镜像安全 配置管理 访问安全)
应用流程安全
1 应用流程安全
1.1 镜像安全
1.1.1 构建原则
学习目标
这一节,我们从 基础知识、原则解读、小结 三个方面来学习。
基础知识
k8s平台使用业务环境
生产中使用k8s操作业务环境的基本原则
1 保证容器应用是正常运行的
2 创建专属的资源对象文件
3 基于资源对象文件创建业务环境
4 测试业务环境,如果不满足则进行后续动作,否则不做任何改变
- 调整容器的镜像文件
- 重新调整资源对象文件
- 基于新资源对象文件创建并测试新的环境
需求
镜像是容器运行的基础,容器引擎服务可使用不同的镜像启动相应的容器。在容器出现错误后,能迅速通过 删除容器、启动新的容器来恢复服务,这都需要以容器镜像作为支撑技术。
而Kubernetes作为一种容器任务编排平台,它对于应用的管理都是基于镜像文件创建出来的。
镜像的使用流程
Docker镜像加载
对于Docker镜像文件整体来说,它是一个只读的文件,但是根据我们对构建过程的理解,我们发现,对于Docker镜像还有很多更深层的东西:
1 镜像文件是基于分层机制实现的
- Docker 镜像的最底层是 bootfs(boot file system), bootfs主要包含 bootloader 和 Kernel , bootloader加载kernel信息到内存中,然后内存使用权交给内核,接着内核卸载bootfs。
- 在 bootfs之上是rootfs(root file system),它包含了不同Linux发行版(Ubuntu|Centos)文件系统中的标准目录和文件,比如/dev,/proc,/bin,/etc等。它由内核挂载为只读模式,而后通过"联合挂载"在其基础上挂载一个"可写层"。
2 下层镜像是上层镜像的父镜像,最底层的称为基础镜像
- 最上层的是可写的,其他各层都是只读的。
总的来说,镜像文件包含运行某个应用软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
UnionFS
UnionFS(联合文件系统):Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将存在不同物理位置的不同目录挂载到同一个虚拟文件系统下,从外面看起来,只能看到一个包含所有底层的文件和目录的文件系统。UnionFS可以把只读和可读写文件系统合并在一起,具有写时复制功能,允许只读文件系统的修改可以保存到可写文件系统当中。
Union文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
Docker镜像使用分层最大的好处就是 共享资源。许多镜像基于相同的base镜像构建而来,宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份base镜像,就可以为所有容器服务了,而且镜像的每一层都可以被共享。
原则解读
构建样式
我们将中间只读的 rootfs 的集合称为 Docker 镜像,Docker 镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。UnionFS 使得镜像的复用、定制变得更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
当用docker run启动这个容器时,实际上在镜像的顶部添加了一个新的可写层。这个可写层也叫容器层。容器启动后,其内的应用所有对容器的改动,文件的增删改操作都只会发生在容器层中,根据容器镜像的写时拷贝(Copy-on-Write)技术,某个容器对基础镜像的修改会被限制在单个容器内,对容器层下面的所有只读镜像层没有影响。
构建原则
1、镜像最小化原则;需要选择最精简的基础镜像、清理镜像构建的中间产物、减少镜像的层数。
2、构建速度最快化原则;充分利用镜像构建缓存,再利用构建的缓存来加快镜像构建速度。
3、注意优化网络请求。
实践原则
镜像构建实践的最佳实践原则
1 按照项目功能定制镜像构建目录结构
2 按照角色定制不同镜像
3 基于嵌套方式分层构建镜像
分层效果
功能效果
小结
1.1.2 Dockerfile实践
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
镜像构建过程
1)从基础镜像1创建一个容器A
2)遇到一条Dockerfile指令,都对容器A做一次修改操作
3)执行完一条指令,提交生成一个新镜像2
4)再基于新的镜像2运行一个容器B
5)遇到一条Dockerfile指令,都对容器B做一次修改操作
6)执行完一条指令,提交生成一个新镜像3
...
Dockerfile
在工作中,我们经常会因为业务需求,而定制各种各样的Doker镜像,由于Dockerfile的便捷性,所以我们经常会基于Dockerfile来创建我们业务场景中所需要的各种镜像。
根据我自己的工作经验,我们在使用Dockerfile的过程中,一般只需要关注三个方面即可:
1 Dockerfile在使用的过程中,构建的指令越少越好,能合并的就合并。
2 基于Docker镜像的分层特性,我们最好按照项目的架构级别来定制不同层的镜像
3 Dockerfile构建的过程中,功能越简单越好,最好只有一个
简单实践
准备工作
创建Dockerfile专用目录
mkdir /docker/images/ssh -p
cd /docker/images/ssh
创建秘钥认证
ssh-keygen -t rsa
cat ~/.ssh/id_rsa.pub > authorized_keys
最初的Dockerfile
# 构建一个基于ubuntu的ssh定制镜像
# 基础镜像
FROM ubuntu
# 镜像作者
MAINTAINER shuji@superopsmsb.com
# 安装 ssh 服务
RUN apt-get update
RUN apt-get install -y openssh-server curl vim net-tools git dnsutils jq
RUN mkdir -p /var/run/sshd
RUN mkdir -p /root/.ssh
RUN sed -i "s/.*pam_loginuid.so/#&/" /etc/pam.d/sshd
RUN apt-get autoclean
RUN apt-get clean
RUN apt-get autoremove
# 复制配置文件到相应位置,并赋予脚本可执行权限
ADD authorized_keys /root/.ssh/authorized_keys
# 对外端口
EXPOSE 22
# 启动ssh
CMD ["/usr/sbin/sshd","-D"]
优化后Dockerfile
# 构建一个基于ubuntu的ssh定制镜像
# 基础镜像
FROM ubuntu
# 镜像作者
MAINTAINER shuji@superopsmsb.com
# 安装 ssh 服务
RUN apt-get update && apt-get install -y openssh-server curl vim net-tools git dnsutils jq && mkdir -p /var/run/sshd && mkdir -p /root/.ssh && sed -i "s/.*pam_loginuid.so/#&/" /etc/pam.d/sshd && apt-get autoclean && apt-get clean && apt-get autoremove
# 复制配置文件到相应位置,并赋予脚本可执行权限
ADD authorized_keys /root/.ssh/authorized_keys
# 对外端口
EXPOSE 22
# 启动ssh
CMD ["/usr/sbin/sshd","-D"]
小结
1.1.3 构建进阶
学习目标
这一节,我们从 多阶段构建、触发器、小结 三个方面来学习。
多阶段构建
需求
对于一些大型微服务项目的研发来说,研发阶段会是多个团队来进行项目的研发,如果每个研发人员都是基于使用Docker容器来做项目的部署,这个时候后,因为几个项目组件可能包含公用文件,从而导致大量的Docker镜像使用的重复冗余资源,所以我们有必要为团队的提供一个更加丰富的Base镜像,以满足所有研发人员的需求的前提下,极大的节省资源的消耗。
多阶段构建
旧版本的Docker软件仅仅支持1个FROM,从1.17版本之后开始支持多阶段构建的能力 -- 也就是说多个FROM指令,也就是说我们可以使用多个base image和中间层image来构建镜像层。这里主要用到的指令是 FROM 和 AS。也就是说,它可以让我在前一个image层上创建多个image层,
示例:
FROM image as layer_name
同时Dockerfile在构建的时候,可以借助于COPY 指令的 --from 参数,docker镜像中直接获取相关文件:
COPY --from=0 /require/file /dest/
从前一个镜像层中获取文件到当前镜像层面指定位置
COPY --from=layer_name /require/file /dest/
从前指定镜像层中获取文件到当前镜像层面指定位置
COPY --from=docker.io/nginx /require/file /dest/
从前现有的镜像中获取文件到当前镜像层面指定位置
构建实践
# 基于基础镜像构建普通功能软件,将其作为ubuntu环境使用
FROM python:3 AS base
MAINTAINER shuji@superopsmsb.com
RUN apt-get update && apt-get install -y vim
# 创建一个专属的镜像层来安装Python依赖软件
FROM base AS requirements
RUN pip3 install django==3.1.2
# 使用Python镜像层创建专属的项目环境
FROM requirements as django
ADD blog /data/code/blog
ADD scripts/startup.sh /data/scripts/startup.sh
EXPOSE 8000
# 定制容器的启动命令
CMD ["/bin/bash", "/data/scripts/startup.sh"]
构建镜像
docker build -t simple-test .
测试效果
docker run -d --name django-test -p 666:8000 simple-test
curl localhost:666
docker rm -f django-test
结果显示:
我们使用FROM和AS指令组合帮助我们为所有项目创建唯一的层次结构。从而让我们一次完成所有任务docker文件,这样不仅简化了版本管理, 还可以更好地了解整个项目, 尤其是通过消除在不同镜像中的相同文件,并实现了最终镜像文件容量缩容。
触发器
需求
工作中我们在做大量Docker镜像的时候,因为分层的优势,我们一般先把基础镜像做出来,然后在它的基础上进行上层镜像的构建。结合我们刚才的实践,如果将多个团队需求的文件都进行初始化构建的话,导致基础镜像容量太大,而且很多功能对于上层镜像无用。
所以我们就可以借助于触发器的能力,提前将上层镜像需要的环境定制出来,但是不执行,只有当上层镜像需要的时候,我们再进行针对性环境的构建,这样可以让我们的容量更加高效。
指令
Dockerfile 用于 build 镜像文件,A镜像和B镜像分别需要不同的Dockerfile文件来进行构建,如果B镜像依赖于A镜像,我们可以借助于ONBUILD指令,在构建B镜像的时候,自动触发A镜像内部的构建步骤,当A镜像构建完毕后,再开始B镜像构建。
构建实践
定制两个目录
mkdir father child
定制father项目的Dockerfile文件
FROM python:3
MAINTAINER father-image
RUN echo "father image"
ONBUILD RUN echo "child image"
定制child项目的Dockerfile文件
FROM father-image
MAINTAINER child-image
RUN echo "this is my cmd"
构建父级镜像
# docker build -t father-image father/
Sending build context to Docker daemon 2.048kB
Step 1/4 : FROM python:3
---> f05c8762fe15
Step 2/4 : MAINTAINER father-image
---> Running in 90bb60b129c3
Removing intermediate container 90bb60b129c3
---> 39be4d0f1254
Step 3/4 : RUN echo "father image"
---> Running in 1ec695c98bfb
father image
Removing intermediate container 1ec695c98bfb
---> dcde17f6a0dc
Step 4/4 : ONBUILD RUN echo "child image"
---> Running in c41f441091a7
Removing intermediate container c41f441091a7 # 结果显示:命令没有执行
---> 5dcb262c76e6
Successfully built 5dcb262c76e6
Successfully tagged father-image:latest
构建子镜像
# docker build -t child-image child/
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM father-image
# Executing 1 build trigger
---> Running in 3e26148923b1
child image # 结果显示:子镜像构建的时候,自动执行父镜像的
Removing intermediate container 3e26148923b1
---> ccbcc3569240
Step 2/3 : MAINTAINER child-image
---> Running in a622eba55824
Removing intermediate container a622eba55824
---> bbaee5a9ea61
Step 3/3 : RUN echo "this is my cmd"
---> Running in ee716a0392aa
this is my cmd
Removing intermediate container ee716a0392aa
---> 229bcbf7c314
Successfully built 229bcbf7c314
Successfully tagged child-image:latest
小结
1.1.4 镜像检测
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
在基于kubernetes平台上的云原生应用,基本上都是基于Docker镜像的方式来运行起来的,如果镜像的来源就是有问题的,那么我们建立该环境上的应用就会存在所谓0day的现象。虽然可以通过Dockerfile的方式来控制镜像的构建过程,但是这只是我们能够控制的一部分,还有大量第三方应用的镜像我们无法进行强有力的安全控制,如果第三方的平台镜像存在安全漏洞,我们是无法处理的。
在互联网中有很多组织和社区,为了实现某些场景的镜像安全,自发或者其他因素影响下,开始从事镜像安全漏洞的工作,他们研发了一系列镜像级别的安全检测工具,可以帮助我们进行镜像安全的控制,Trivy就是其中的佼佼者。
根据2022年<<sysdig云原生和使用报告>>的统计数据显示:在生产环境中运行的 85% 的镜像至少包含一个可修补漏洞。此外,75% 的镜像包含“高危”或“严重”的可修补漏洞。使用公共仓库可能会带来风险,因为它们很少会被验证或检查漏洞。但是在某些情况下,使用公共库的便利性大于风险,所以很多企业宁可面对风险也要用公共镜像仓库,并且将其作为最佳实践。
trivy
Trivy是一个简单而全面的漏洞/错误配置扫描器,常被应用于容器级别的业务安全场景。它主要的工作对象是 容器镜像、文件系统、Git存储库等。
Trivy提供了大量用于检测操作系统和程序语言的检测工具包。它无论是部署还是使用都是非常简单的。尤其是它对于其他平台环境的集成非常轻松,常常应用在以DevSecOps为代表的CI环境中。
官方网站:https://trivy.dev/
github: https://github.com/aquasecurity/trivy
最新版本: 0.32.1
软件部署
Centos定制软件源
# vim /etc/yum.repos.d/trivy.repo
[trivy]
name=Trivy repository
baseurl=https://aquasecurity.github.io/trivy-repo/rpm/releases/$releasever/$basearch/
gpgcheck=0
enabled=1
安装软件
yum makecache fast
yum -y install trivy
Ubuntu定制软件源
apt-get install wget apt-transport-https gnupg lsb-release
wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add -
echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list
安装软件
apt-get update
apt-get -y install trivy
简单实践
命令解析
镜像扫描
trivy image [YOUR_IMAGE_NAME]
漏洞检测
trivy image --severity HIGH,CRITICAL [YOUR_IMAGE_NAME]
信息输出
trivy image -f json -o results.json [YOUR_IMAGE_NAME]
仓库检测
trivy repo https://github.com/knqyf263/trivy-ci-test
容器内检测
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
trivy fs /
简单实践
下载漏洞数据库
trivy image --download-db-only
清理检测缓存
trivy image --clear-cache
清理所有检测数据库
trivy image --reset
镜像检测
trivy image nginx:latest
注意
在title部分有该漏洞的一些信息https://avd.aquasec.com/nvd/2011/cve-2011-3374/
CVSS:Common Vulnerability Scoring System,即通用漏洞评分系统,是一个行业公开标准,其被设计用来评测漏洞的严重程度,并帮助确定所需反应的紧急度和重要度。
指定检测级别测试
trivy image --severity HIGH,CRITICAL nginx:latest
文件系统扫描
trivy fs /tmp/
仓库扫描
trivy repo https://github.com/kubernetes/kubernetes.git
信息输出
trivy image -f json -o results.json nginx:latest
harbor实践
我们可以在harbor部署的时候,指定一个参数来启用trivy能力。
./install.sh --with-trivy
这样的话,我们的镜像后面就会多出检测的能力条,只需要我们配置检测的规则,它就会自动检测
小结
1.1.5 仓库升级
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
对于docker镜像仓库来说,默认使用的级别是https的,如果是非https的话,需要将其设置到一个非安全的仓库范围中,无论是对于containerd还是docker来说,这都不是一个很好的现象。
harbor本身是支持https能力的,我们只需要按部就班的实施就可以了。
修改harbor配置
修改配置 /data/server/harbor/harbor.yml
...
hostname: kubernetes-register.superopsmsb.com
http:
port: 80
https: 注释ssl相关的部分
port: 443
certificate: /data/server/harbor/certs/kubernetes-register.superopsmsb.com/kubernetes-register.superopsmsb.com.crt
private_key: /data/server/harbor/certs/kubernetes-register.superopsmsb.com/kubernetes-register.superopsmsb.com.key
harbor_admin_password: 123456
data_volume: /data/harbor/data
准备证书信息
生成CA证书私钥
mkdir -p /data/server/harbor/ssl
cd /data/server/harbor/ssl
openssl genrsa -out ca.key 4096
生成CA证书
openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=kubernetes-register.superopsmsb.com" \
-key ca.key \
-out ca.crt
注意:如果使用IP地址,需要在执行以上命令前执行以下操作:
cd /root
openssl rand -writerand .rnd
cd -
生成服务器证书私钥
openssl genrsa -out kubernetes-register.superopsmsb.com.key 4096
生成证书签名请求(CSR)
openssl req -sha512 -new \
-subj "/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=kubernetes-register.superopsmsb.com" \
-key kubernetes-register.superopsmsb.com.key \
-out kubernetes-register.superopsmsb.com.csr
生成一个x509 v3扩展文件
无论您使用FQDN还是IP地址连接到Harbor主机,都必须创建此文件,以便可以为您的Harbor主机生成符合主题备用名称(SAN)和x509 v3的证书扩展要求。替换DNS条目以反映您的域。
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1=kubernetes-register.superopsmsb.com
DNS.2=kubernetes-register.superopsmsb
DNS.3=kubernetes-register
EOF
使用该v3.ext文件为您的Harbor主机生成证书
openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in kubernetes-register.superopsmsb.com.csr \
-out kubernetes-register.superopsmsb.com.crt
提供证书给harbor和docker
将服务器证书和密钥复制到Harbor主机上的certficates文件夹中
mkdir /data/server/harbor/certs/kubernetes-register.superopsmsb.com -p
cp kubernetes-register.superopsmsb.com.crt /data/server/harbor/certs/kubernetes-register.superopsmsb.com/
cp kubernetes-register.superopsmsb.com.key /data/server/harbor/certs/kubernetes-register.superopsmsb.com/
Docker守护程序将.crt文件解释为CA证书,并将.cert文件解释为客户端证书
openssl x509 -inform PEM -in kubernetes-register.superopsmsb.com.crt -out kubernetes-register.superopsmsb.com.cert
将服务器证书,密钥和CA文件复制到Harbor主机上的Docker证书文件夹中。您必须首先创建适当的文件夹
mkdir -p /etc/docker/certs.d/kubernetes-register.superopsmsb.com/
cp kubernetes-register.superopsmsb.com.cert /etc/docker/certs.d/kubernetes-register.superopsmsb.com/
cp kubernetes-register.superopsmsb.com.key /etc/docker/certs.d/kubernetes-register.superopsmsb.com/
cp ca.crt /etc/docker/certs.d/kubernetes-register.superopsmsb.com/
注意:
如果将默认nginx端口443映射到其他端口,请创建文件夹
/etc/docker/certs.d/yourdomain.com:port
或/etc/docker/certs.d/harbor_IP:port
这一步,对于新版的docker来说可以省略,即使不做这一步,影响不大。
重新启动Docker Engine
systemctl restart docker
harbor启动
进入工作目录
cd /data/server/harbor
重新应用harbor配置
./prepare
./install.sh
检查效果
docker-compose ps
简单实践
浏览器测试
修改windows的hosts文件 C:\Windows\System32\drivers\etc\hosts
10.0.0.20 kubernetes-register.superopsmsb.com
浏览器访问 https://kubernetes-register.superopsmsb.com
这个时候,就会使用https方式了,如果需要让浏览器不提示不安全,需要下载ca.crt到本地电脑安装证书,在Windows环境下双击,安装选择受信任的根证书即可。
docker测试
删除之前的docker认证信息
rm -f /root/.docker/config.json
修改docker的私服配置 /etc/docker/daemon.json
"insecure-registries": ["kubernetes-register.superopsmsb.com"],
重启docker服务
systemctl restart docker
测试效果
docker login https://kubernetes-register.superopsmsb.com
小结
1.1.6 高可用仓库
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
docker镜像很重要,如果harbor仓库存在单点现象的话,会导致所有镜像无法在项目更新的时候进行使用。所以,我们有必要对于harbor进行高可用环境的定制。
Harbor 提供了很好的性能和安全, 它支持安装在多个 Registry 节点的镜像资源复制,从而实现镜像数据的一致性需求,比较适合于负载均衡、高可用、混合云和多云的场景。
方案
我们选择的高可用方案是比较简单的Harbor的双主复制,搭建两个Harbor节点,节点之间能够互相复制,然后通过haproxy代理Harbor节点提供外部访问。
高可用节点
- 10.0.0.18|19
- keepalived+haproxy
harbor节点
- 10.0.0.20|21
- harbor
注意:
基于离线安装包安装的高可用方案需要保证以下文件在多个实例之间的一致性。同时,由于这些文件是在各个 Harbor 实例的安装过程中默认生成的,所以需要用户手动复制这些文件来保证一致性。
再一个Harbor主要是给公司内部的开发人员使用的,由于业务使用量不是那么大,所以我们对于数据的一致性能够保证分钟级别也就可以了。
从节点部署
我们在 10.0.0.21节点上部署harbor
参考 前一节的 仓库升级内容,关于证书相关的信息使用复制即可。
注意:
修改两个harbor节点的hostname为 各自的ip地址
然后执行 ./prepare 和 ./install.sh
如果需要启动时候,添加镜像检测功能,我们可以使用如下命令
./install.sh --with-trivy
keepalived配置
主keepalived配置
vrrp_instance VI_2 {
state MASTER
interface eth0
virtual_router_id 52
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.201 dev eth0 label eth0:2
}
}
从keepalived配置
vrrp_instance VI_2 {
state BACKUP
interface eth0
virtual_router_id 52
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
10.0.0.201 dev eth0 label eth0:2
}
}
重启keepalived
systemctl restart keepalived
haproxy配置
修改haproxy的配置文件
listen harbor-80
bind 10.0.0.201:80
mode tcp
server harbor1 10.0.0.20:80 check inter 3s fall 3 rise 5
server harbor2 10.0.0.21:80 check inter 3s fall 3 rise 5
简单实践
配置主从
我们暂时访问 http://10.0.0.20,登陆配置一个主从角色,将20相关的配置同步到21主机。点击仓库管理后,创建harbor从角色的信息:
目标名:可以随意定义
目标url: 是从harbor节点的http链接地址
访问id: admin
访问密码: 123456
注意:当我们点击确定后,就会出现图中下面的仓库列表样式
点击复制管理后,创建主从同步规则信息:
名称和描述:自定义
资源过滤器:定制同步仓库即可
目标仓库:从列表中选择即可
触发模式:选择手动模式
注意:当我们点击确定后,就会出现图中下面的规则列表样式
触发复制并验证结果
1 勾选复制规则
2 点击复制
3 确认复制
4 查看复制任务
harbor2确认复制效果
测试效果
对于master节点来说,我们只需要修改/etc/hosts 文件的主机名解析记录即可
10.0.0.201 kubernetes-register.superopsmsb.com kubernetes-register
小结
1.1.7 镜像策略
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
简介
Kubernetes 对 API 访问提供了三种安全访问控制措施:认证、授权和 Admission Control。认证解决用户合法性的问题,授权解决用户能做什么的问题,Admission Control 则是资源管理方面的作用。通过合规的用户、合理的权限管理以及合适的准入控制机制,能够保证系统的安全可靠。
注意:关于插件化的准入控制是1.9版本之后的能力,之前是需要编译才可以实现。
k8s在很多方面都具备可扩展性,比如通过cni实现多种网络模型,通过csi实现多种存储引擎,通过cri实现多种容器运行时等等。而AdmissionWebhook就是准入层面的一种可扩展的手段。 它可以已默认的Admission插件外,还有自定义的Admission插件,在api-server运行时指定资源对象的时候,通过webhook进行操作的请求控制。
Admission webhooks是基于HTTP回调机制运行的,当它接收Admission请求并对它们做一些事情的时候。Admission webhook对k8s的请求资源对象进行深层次准入控制,Admission webhook有两种控制机制,MutatingAdmissionWebhook(对请求对象的修改)和ValidatingAdmissionWebhook(对请求的合法性进行检查)。
webhook解析
Admission Webhook 本质是 api-server 的一个 webhook 调用,下面是 api-server 的处理流程:
api-server 通过读取 mutatingwebhookconfiguration 和 validatingwebhookconfiguration 的 CRD 文件的目标地址,然后回调用户自定义的服务。
参考资料:
https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/
部署注意事项
1 webhook 的本质是 http server,因此,需要用代码实现这么一个 server,供 apiserver 调用
2 mutating webhook 和 validating webhook 没有直接联系,分别实现了不同的功能,本质上说,如果修改/验证都需要,我们需要编写两个 http server
3 编写好的 webhook,建议使用 k8s deployment 部署,同时使用 service 来暴露能力
4 apiserver 通过MutatingWebhookConfiguration/ValidatingWebhookConfiguration 调用 webhook
5 apiserver 和 webhook 通信时的身份认证和权限控制必须合理控制
案例需求
ImagePolicyWebhook是专门用于镜像获取的准入控制器策略,它主要通过验证镜像字段来判断,资源在创建时候是否满足创建的条件。我们可以在k8s的资源对象创建的时候,保证镜像来源仓库的可靠。但是该实践需要配置涉及到的很多文档内容,包括认证、配置、环境等。
参考资料:
https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#configuration-file-format
操作步骤
1 准备https专用的证书文件
2 构建webhook服务器
3 kubernetes集成镜像webhook服务
4 综合测试
5 收尾动作
简单实践
准备https专用的证书文件
准备证书工具环境
mkdir /data/kubernetes/process_secure/ ; cd /data/kubernetes/process_secure/
for i in cfssl cfssljson
do
wget https://github.com/cloudflare/cfssl/releases/download/v1.6.1/$i_1.6.1_linux_amd64
chmod +x $i_1.6.1_linux_amd64
cp $i_1.6.1_linux_amd64 /usr/local/bin/$i
done
准备专用证书目录
mkdir /data/kubernetes/process_secure/cert -p
cd /data/kubernetes/process_secure/cert/
准备CA配置信息,参考 cfssl print-defaults config 命令
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"expiry": "87600h",
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
]
}
}
}
}
EOF
准备签名请求信息参考 cfssl print-defaults csr 命令
cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "Beijing",
"ST": "Beijing"
}
]
}
EOF
准备image专属的webhook请求信息
cat > webhook-csr.json <<EOF
{
"CN": "webhook",
"hosts": [
"10.0.0.12",
"10.0.0.13",
"10.0.0.14",
"127.0.0.1"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
EOF
准备webhook和apiserver的认证信息
cat > apiserver-client-csr.json <<EOF
{
"CN": "apiserver",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"L": "BeiJing",
"ST": "BeiJing"
}
]
}
EOF
生成私钥和证书
cfssl gencert -initca ca-csr.json | cfssljson -bare ca -
生成webhook证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes webhook-csr.json | cfssljson -bare webhook
生成apiserver证书
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes apiserver-client-csr.json | cfssljson -bare apiserver-client
构建webhook服务器
定制镜像专属目录结构
mkdir /data/images/web/flask/conf
cd /data/images/web/flask
定制webhook的核心代码(Flask) main.conf
from flask import Flask,request
import json
# 创建app应用
app = Flask(__name__)
# 定制测试url
@app.route("/index")
def index():
return "flask web ok\n"
@app.route('/image_check', methods=['POST'])
def image_policy():
# 获取用户提交数据并进行数据反序列化
post_data = request.get_data().decode()
print("--- POST数据: {}".format(post_data))
data = json.loads(post_data)
# 定制容器镜像格式的检测规则 -- 必须携带标签
for container in data['spec']['containers']:
if ":" not in container['image'] or ":latest" in container['image']:
msg="镜像格式检测失败! 镜像名称必须携带标签,同时禁用默认标签."
allowed, reason = False, msg
break
else:
msg="镜像格式检测成功!"
allowed, reason = True, msg
print("--- 检查结果: {}".format(reason))
# 定制返回信息
obj_api=data['apiVersion']
obj_kind=data['kind']
obj_image=data['spec']['containers'][0]['image']
result = {"apiVersion": obj_api, "kind": obj_kind, "image_name": obj_image , "status": {"allowed": allowed, "reason": reason}}
print("--- 响应数据: {}".format(result))
# 对响应数据进行序列化操作,ensure_ascii=False,表示返回信息包含中文
return json.dumps(result, ensure_ascii=False)
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8080, ssl_context=('/data/server/image_check/webhook.pem', '/data/server/image_check/webhook-key.pem'))
定制专属的Dockerfile文件
# 构建一个基于flask的定制镜像
# 基础镜像
FROM kubernetes-register.superopsmsb.com/superopsmsb/python
# 镜像作者
MAINTAINER shuji@superopsmsb.com
# 拷贝文件
ADD conf/main.py /data/server/image_check/main.py
# 创建flask环境
RUN pip install flask && useradd python && chown -R python /data
# 暴露django端口
EXPOSE 8080
USER python
# 定制容器的启动命令
CMD ["python", "/data/server/image_check/main.py"]
注意:
这里使用普通用户主要后期运行时候的缓存信息及时显示的原因
定制镜像文件
docker build -t kubernetes-register.superopsmsb.com/superopsmsb/image-webhook:v0.1 .
在生成证书的节点上运行镜像文件
export cert_dir='/data/kubernetes/process_secure/cert/'
export cont_dir='/data/server/image_check'
docker run -d --name=image-webhook -u root -e PYTHONUNBUFFERED=1 -v $cert_dir/webhook.pem:$cont_dir/webhook.pem -v $cert_dir/webhook-key.pem:$cont_dir/webhook-key.pem -p 8080:8080 kubernetes-register.superopsmsb.com/superopsmsb/image-webhook:v0.1
注意:
-u root -e PYTHONUNBUFFERED=1 的作用是将请求相关的数据即时输出
检查效果
]# docker ps | grep image
77bf356ce83e kubernetes-register.superopsmsb.com/superopsmsb/image-webhook:v0.1 "python /data/server…" 9 seconds ago Up 9 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp image-webhook
]# curl -k https://10.0.0.12:8080/index
flask web ok
结果显示:
webhook服务器已经部署完毕了
kubernetes集成镜像webhook服务
检查kubernetes集群是否启用了准入注册 API
]# kubectl api-versions |grep admission
admissionregistration.k8s.io/v1
查看kubernetes集群的准入控制现状
]# grep admission /etc/kubernetes/manifests/kube-apiserver.yaml
- --enable-admission-plugins=NodeRestriction
准备专属的目录
mkdir -p /data/kubernetes/process_secure/api_conf
cd /data/kubernetes/process_secure/api_conf
准备准入控制的策略文件 admission_configuration.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
configuration:
imagePolicy:
# 指定image策略专属连接和认证文件
kubeConfigFile: /etc/kubernetes/image-policy/connect_webhook.yaml
allowTTL: 50
denyTTL: 50
retryBackoff: 500
defaultAllow: true
定制image策略专属连接和认证文件 connect_webhook.yaml
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /etc/kubernetes/image-policy/webhook.pem
# 定制
server: https://10.0.0.12:8080/image_check
name: webhook
contexts:
- context:
cluster: webhook
user: apiserver
name: webhook
current-context: webhook
preferences: {}
users:
- name: apiserver
user:
client-certificate: /etc/kubernetes/image-policy/apiserver-client.pem
client-key: /etc/kubernetes/image-policy/apiserver-client-key.pem
同步证书相关信息
cd /data/kubernetes/process_secure/cert/
for i in 12 13 14
do
ssh root@10.0.0.$i mkdir /etc/kubernetes/image-policy -p
scp webhook.pem apiserver-client.pem apiserver-client-key.pem root@10.0.0.$i:/etc/kubernetes/image-policy/
scp ../api_conf/{admission_configuration.yaml,connect_webhook.yaml} root@10.0.0.$i:/etc/kubernetes/image-policy/
done
修改kube-apiserver的配置文件与webhook集成
]# cp /etc/kubernetes/manifests/kube-apiserver.yaml{,.bak}
]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
spec:
containers:
- command:
...
- --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
- --admission-control-config-file=/etc/kubernetes/image-policy/admission_configuration.yaml
...
volumeMounts:
...
- mountPath: /etc/kubernetes/image-policy
name: image-policy
readOnly: true
...
volumes:
- hostPath:
path: /etc/kubernetes/image-policy
type: DirectoryOrCreate
name: image-policy
...
配置解读:
因为涉及到专属证书信息,所以需要hostPath方式进行挂载操作
注意:
因为kube-apiserver是静态pod,所以文件修改完毕后,就可以重启了
综合测试
在master节点上创建两个deployment
kubectl create deployment web1 --image=kubernetes-register.superopsmsb.com/superopsmsb/nginx_web
kubectl create deployment web2 --image=kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
查看docker容器日志
docker logs -f image-webhook
环境收尾
删除docker容器
docker rm -f image-webhook
清理deployment
kubectl delete deployments.apps web1 web2
还原kube-apiserver环境
for i in 12 13 14
do
ssh root@10.0.0.$i "mv /etc/kubernetes/manifests/kube-apiserver.yaml.bak /etc/kubernetes/manifests/kube-apiserver.yaml; rm -rf /etc/kubernetes/image-policy"
done
小结
1.2 配置管理
1.2.1 配置基础
学习目标
这一节,我们从 基础知识、资源清单、小结 三个方面来学习。
基础知识
场景需求
生产中所有的应用程序中,都涉及到配置文件,而配置文件经常会有变更,比如数据库连接、代码版本号等,最典型场景就是:
项目经历开发环境、测试环境、预发布环境、线上环境才能完成发布,而每个环境都有定义其独立的各种配置,这些配置手工操作很繁杂,所以好多大公司专门开发了专用配置管理中心,如百度的disconf等。
如何为容器化应用提供配置信息?
1 启动容器时,直接向应用程序传递参数,args: []
2 将定义好的配置文件焙进镜像之中;
3 通过环境变量向容器传递配置数据:有个前提要求,应用得支持从环境变量加载配置信息;
4 制作镜像时,使用entrypoint脚本来预处理变量,常见的做法就是使用非交互式编辑工具,将环境变量的值替换到应用的配置文件中;
5 基于存储卷向容器传递配置文件;
注意:
对于运行容器中的配置改变,需要通过应用程序重载相关配置
k8s的配置管理
kubernetes作为分布式容器调度平台,肯定会遇到同样的问题,那么遇到这种问题后,我们不可能把资源删除后,重新修改一下,然后在启动生成,这种方法就太繁琐的。kubernetes提供了对pod中容器应用的集中配置管理组件:ConfigMap、Secret、downwardAPI。通过这些组件来实现向pod中的容器中注入配置信息的机制。
资源清单
对象形式
在kubernetes中,资源对象有两种形态:
文件形态 - 编写出来的资源对象文件,有大量属性组成。
对象形态 - 基于资源对象文件初始化后出来的应用对象。
对象初始化
资源对象初始化的方法主要有两大类:
命令行工具(kubectl)
- 通过纯粹的 "k8s命令及其选项" 来实现资源的创建
文件方式(API)
- 基于 "k8s命令 + 配置文件" 来实现资源的创建
- 基于 "声明式的配置文件 + kubectl" 来实现资源的创建
使用资源对象
生产中使用k8s操作业务环境
1 保证容器应用是正常运行的
2 创建专属的资源对象文件
3 基于资源对象文件创建业务环境
4 测试业务环境,如果不满足则进行后续动作,否则不做任何改变
- 调整容器的镜像文件
- 重新调整资源对象文件
- 基于新资源对象文件创建并测试新的环境
小结
1.2.2 YAML安全
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
资源清单的最大的好处是,让我们手工繁琐操作流程变成一个一个的资源对象属性,通过一个简单的kubectl apply -f的方式,基于资源清单文件,实现业务环境的高度定制化、自动化的操作。
大量的资源清单文件在大规模使用的时候,往往会因为场景的不同,需要对于同一个配置文件进行修整。如果我们对于资源清单文件的目录结构、内容样式、适用场景非常了解的前提下,我们可以随意的操作这些海量的资源清单文件。
但是我们知道,无论操作人员的能力多么的高,在面对海量数据的时候,总会有懈怠的时候,再加上yaml操作的高速度,一旦yaml文件出现安全问题,那会导致非常严重的业务环境问题。尤其是当我们的能力不足以撑起yaml的优势的时候,导致的问题会更大。
所以,我们需要一种yaml文件的安全检测机制,来保证yaml文件的安全性。这里的安全性主要体现在三个层面:
1 yaml文件自身的结构 - explain
2 yaml文件本身的安全 - kubesec
3 yaml文件项目的安全 - kustomize
安全机制
yaml文件自身的结构 - explain
所谓文件自身的结构,就是结合explain的资源对象属性体系,然后根据业务场景的功能,选择合适的对象属性组合在一起即可。
- 它是kubeadm的一个子命令
yaml文件本身的安全 - kubesec
所谓文件本身的安全,是借助于专用的资源清单的安全配置评估的工具,根据生产的最佳实践来验证资源清单文件的安全性并适时给出建议。Kubesec 可以作为 Web 服务或准入控制器的样式存在,接收 Kubernetes资源作为输入,并进程检测分析,如果不合格,则禁止执行。
- 参考地址:https://github.com/controlplaneio/kubesec
yaml文件项目的安全 - kustomize
所谓的文件项目的安全,是站在项目的角度,对所有场景中的资源清单文件,按照版本管理的方式进行统一安全质量控制。kustomize 是 kubernetes 原生的配置管理,以无模板方式来定制应用的配置。通过功能复用、补丁等方式实现项目级别的yaml结构安全。
- 参考地址;https://github.com/kubernetes-sigs/kustomize
简单实践
普通方式实践kubeasc
Kubesec 可以作为二进制包、容器镜像、准入控制器甚至 kubectl 插件安装
cd /data/softs
wget https://github.com/controlplaneio/kubesec/releases/download/v2.12.0/kubesec_linux_amd64.tar.gz
部署kubesec
tar -xvf kubesec_linux_amd64.tar.gz
mv kubesec /usr/local/bin/
注意:
命令格式 kubesec scan /path/to/file.yaml
该命令在第一次检测的时候,需要到github中获取检测文件
简单实践
]# kubesec scan /data/kubernetes/app_secure/01_kubernetes_application_secure_secret_data.yaml
[
{
"object": "Secret/superopsmsb-secret.default",
"valid": true,
"fileName": "/data/kubernetes/app_secure/01_kubernetes_application_secure_secret_data.yaml",
"message": "This resource kind is not supported by kubesec",
"score": 0,
"scoring": {}
}
]
]# kubesec scan /data/kubernetes/server_secure/01_kubernetes_server_secure_pod_test.yaml
[
{
"object": "Pod/nginx-web.default",
"valid": true,
"fileName": "/data/kubernetes/server_secure/01_kubernetes_server_secure_pod_test.yaml",
"message": "Passed with a score of 0 points",
"score": 0,
"scoring": {
"advise": [
{
"id": "ApparmorAny",
"selector": ".metadata .annotations .\"container.apparmor.security.beta.kubernetes.io/nginx\"",
"reason": "Well defined AppArmor policies may provide greater protection from unknown threats. WARNING: NOT PRODUCTION READY",
"points": 3
},
...
web服务方式
部署web服务方式1 - 依赖于gitlab的访问获取配置文件
kubesec http 8080 &
部署web服务方式2 - 容器内部的自带配置文件
docker pull kubesec/kubesec
docker run -d --name kubesec -p 8080:8080 kubesec/kubesec http 8080
使用方式
curl -sSX POST --data-binary @/path/to/file.yaml http://localhost:8080/scan
检测方式
]# curl -sSX POST --data-binary @/data/kubernetes/server_secure/01_kubernetes_server_secure_pod_test.yaml http://10.0.0.12:8080/scan
[
{
"object": "Pod/nginx-web.default",
"valid": true,
"fileName": "API",
"message": "Passed with a score of 0 points",
"score": 0,
...
其他检测方式
变种方式
docker run -i kubesec/kubesec scan /dev/stdin < /path/to/file.yaml
注意:
不推荐
webhook方式
参考资料: https://github.com/controlplaneio/kubesec-webhook
小结
1.2.3 kustomize
学习目标
这一节,我们从 基础知识、应用管理、小结 三个方面来学习。
基础知识
实际现状
根据我们之前对 k8s的学习,尤其是对各种状态应用的管理,比如statsfulset资源对象,redis集群、alertmanager部署等具体的实践。当我们需要在kubernetes环境运行一个完整的业务项目的时候,我们需要根据业务需求提前梳理功能,进而编写大量的资源清单文件的YAML文件,通过这些文件来定制项目的预期目标(副本,存储空间,内存和CPU等信息),我们就会遇到两种问题:
1 一大堆的部署配置资源清单就是工作量非常大的一件事情。
对于这些复杂的应用,在k8s之上,我们不建议使用独立的部署清单来进行配置
2 部署清单本身就有相当大的限制约束,导致部署清单的任务量成倍增加
如果我们要部署多套prometheus集群,我们可以将所有的资源配置清单部署到不同的namespace中
- 这就导致,两套不同的部署清单文件结构
根据我们之前说的,持续交付的解决方案,为了实现成功的项目交付,我们要在不同的环境中测试该业务是否满足我们的预期,所以项目交付的过程中,会涉及到多种环境。所以实际的情况中,我们需要通过复制+定制的方式,为不同的环境配置独有的一套配置。
我们可以想象得到,一旦我们针对这么来进行管理的话,那当我们的业务环境达到一定量级的时候,我们就有多套仅存在细微差别的配置,对于后期的方案维护相当的不友好。
方案规整
所以,为了便于k8s来管理这些同业务的多版本场景,尤其是云原生的时代,我们应该使用一个 git版本控制系统来对所有的资源配置清单进行统一的管控。同时为了方便后续技术支持团队的管理维护,开发人员应该做如下的三件事情:
1 编写业务项目相关的部署清单文件,并提交到代码管理仓库
- 方便后续团队,根据业务需求,准备相关的资源
2 提供项目持续维护的jenkinsfile、Dockerfile文件。
- 方便后续团队,根据业务的实际情况,以流水线的方式实现功能的迭代更新。
3 不同环境的资源文件,都应该有独有的一份。
- 以k8s环境为例,方便后续团队在多个ns中分别创建资源对象。
由于k8s上还有一种根据监控指标实现自动扩缩容的功能,所以,一旦我们将持续交付流水线做完毕后,结合提前做好的资源清单文件,与项目交付相关的所有操作,都不需要我们人工干预了。
应用管理
简介
根据我们刚才所描述的信息内容,我们可以了解到,越是复杂的业务运行环境,越需要有一套业务部署管理工具
- 将业务相关的功能解决方案,将多个云原生的软件以组件方式整合在一起,然后统一的进行管理
对于k8s来说,它就提供了这样的一种工具:kustomize。
官网介绍
Kubernetes native configuration management. Kustomize introduces a template-free way to customize application configuration that simplifies the use of off-the-shelf applications.
kustomize 是 kubernetes 原生的配置管理,以无模板方式来定制应用的配置。
也就是说,kustomize 使用 k8s 原生概念帮助创建并复用资源配置(YAML),允许用户以一个应用描述文件(YAML 文件)为基础(Base YAML),然后通过 Overlay 的方式生成最终部署应用所需的描述文件。
工具简介
kustomize允许用户将不同环境所共享的配置放在一个文件目录下,而将其他不同的配置放在另外的目录下。通过这种通用+特有的组合方式,让我们的多业务环境更轻松的来解决适配性的问题。
kustomize是sig-cli的一个子项目,它的设计目的是给kubernetes的用户提供一种可以重复使用同一套配置的声明式应用管理,从而在配置工作中用户只需要管理和维护kubernetes的API对象,而不需要学习或安装其它的配置管理工具,也不需要通过复制粘贴来得到新的环境的配置。
Kustomize 是一个用来定制 Kubernetes 配置的工具。它提供以下功能特性来管理 应用配置文件:
从其他来源生成资源
为资源设置贯穿性(Cross-Cutting)字段
组织和定制资源集合
github地址:https://github.com/kubernetes-sigs/kustomize
最新版本:v4.5.7 | 20220803
概念解析
组件 | 解析 |
---|---|
kustomization | 该文件所在的目录下可以运行 kustomize build,自动生成配套文件 |
base | kustomization.yaml文件所在目录,项目的根目录 |
resource | kubernetes API资源对象文件 |
patch | kubernetes API资源对象文件特有的patch属性信息文件,符合yaml格式。 |
variant | base下的多个不同业务场景的kustomization |
overlay | 声明了与 base 之间的差异。通过 overlay 来维护基于 base 的不同 variants(变体) |
入口配置
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases <[]string> # 基础通用配置信息所在目录
resources <[]string> # 待定制的原始资源配置文件列表,按顺序处理
namespace <string> # 资源对象归属目标名称空间
commonLabels <map[string]string> # 资源对象的通用标签
commonAnnotations <map[string]string> # 资源对象的通用注解
namePrefix <string> # 资源对象添加的名称前缀
nameSuffix <string> # 资源对象添加的名称后缀
images <[]Image> # 模板中定制的镜像文件
- name <String> # 待替换的镜像名称
nameName <String> # 要使用的新镜像名称
newTag <String> # 要使用的新镜像的标签
digest <String> # 要使用的新镜像的sha256校验码
vars <[]Var> # 可替换的变量列表
- name <String> # 变量的名称,支持以“$(name)”格式进行引用
objref <String> # 包含了要引用的目标字段的对象的名称
fieldref <String> # 引用的字段名称,默认为metadata.name
配置生成器
ConfigMap生成器:
configMapGenerator <[]ConfigMapGeneratorArgs> # ConfigMap资源生成器列表
- name <String> # 资源对象名称
namespace <String> # 命名空间
behavior <String> # 合并策略,可为create/replace/merge;
files <[]String> # ConfigMap文件相对当前项目的路径
literals <[]String> # 以“key=value”格式的生成ConfigMap
env <String> # 以环境变量文件格式生成ConfigMap
Secret生成器:
secretGenerator <[]secretGeneratorArgs> # Secret资源生成器列表
- name <String> # 资源对象名称
namespace <String> # 命名空间
behavior <String> # 合并策略,可为create/replace/merge;
files <[]String> # Secret文件相对当前项目的路径
literals <[]String> # 以“key=value”格式的生成Secret
type <String> # 选择Secret资源类型
生成器选项:
generatorOptions <GeneratorOptions> # 生成器额外属性,为上面两项的补充内容
labels <map[String]String> # 扩展的标签
annotations <map[String]String> # 扩展的注解
disableNameSuffixHash <Boolean> # 是否禁用hash名称后缀,默认为启用
资源补丁
patchesJson6902 <[]Json6902> # 待补对象列表
path <String> # 补丁文件路径,支持json或yaml格式
target <Target> # 待补资源对象
group <String> # 资源所属的群组
version <String> # API版本
kind <String> # 资源类型
name <String> # 资源对象的名称
namespace <string> # 资源对象所属的名称空间
patchesStrategicMerge <[]string> # 将补丁补到匹配的资源之上,匹配的方式是根据资源
# Group/Version/Kind + Name/Namespace判断
配置示例
资源清单文件:kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- /config/app1/base
- /config/app2/base
- ...
resources:
- service.yaml
- deployment.yaml
patches:
- patch.yaml
namePrefix: my-
Kustomize的核心目标在于为管理的应用生成资源配置,而这些资源配置中定义了资源的期望状态
小结
1.2.4 基础实践
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
环境准备
简介
我们可以通过如下两种不同方式来安装kustomize
二进制方式:将软件包解压到系统命令路径即可,我们推荐
go方式:go get github.com/kubernetes-sigs/kustomize
k8s方法:从 1.14 版本开始,kubectl 也开始支持使用 kustomization 文件来管理 Kubernetes 对象。 要查看包含 kustomization 文件的目录中的资源,执行下面的命令:
软件安装
下载软件
mkdir /data/softs && cd /data/softs
wget https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize%2Fv4.5.7/kustomize_v4.5.7_linux_amd64.tar.gz
解压文件
mkdir /data/server/kustomize/bin
tar xf kustomize_v4.5.7_linux_amd64.tar.gz -C /data/server/kustomize/bin/
环境变量定制
# vim /etc/profile.d/kustomize.sh
#!/bin/bash
# set kustomize env path
export KUSTOMIZE_HOME=/data/server/kustomize
export PATH=${KUSTOMIZE_HOME}/bin:$PATH
加载环境变量
chmod +x /etc/profile.d/kustomize.sh
source /etc/profile.d/kustomize.sh
查看命令帮助
]# kustomize --help
...
Available Commands:
build Build a kustomization target from a directory or URL.
cfg Commands for reading and writing configuration.
completion Generate shell completion script
create Create a new kustomization in the current directory
edit Edits a kustomization file
fn Commands for running functions against configuration.
help Help about any command
version Prints the kustomize version
Flags:
-h, --help help for kustomize
--stack-trace print a stack-trace on error
Additional help topics:
kustomize docs-fn [Alpha] 开发和调用配置函数的文档
kustomize docs-fn-spec [Alpha] 配置函数规范文档.
kustomize docs-io-annotations [Alpha] io使用的注释文档
kustomize docs-merge [Alpha] 合并资源的文档.
kustomize docs-merge3 [Alpha] 合并资源的文档.
kustomize tutorials-command-basics [Alpha] 使用基本配置命令的教程.
kustomize tutorials-function-basics [Alpha] 使用函数的教程.
Use "kustomize [command] --help" for more information about a command.
安装方式2 - k8s子命令
从 1.14 版本开始,kubectl 也开始支持使用 kustomization 文件来管理 Kubernetes 对象。 要查看包含 kustomization 文件的目录中的资源,执行下面的命令:
kubectl kustomize <kustomization_directory>
要应用这些资源,使用参数 --kustomize 或 -k 标志来执行 kubectl apply:
kubectl apply -k <kustomization_directory>
简单实践
简单实践
创建测试目录
mkdir -p /data/kubernetes/process_secure/kustomize/kustomize-test -p
创建核心的kustomization.yaml文件,用于核心流程处理
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- kustomiz-deployment-test.yaml
- kustomiz-service-test.yaml
commonLabels:
generated-by: kustomize
创建依赖的 kustomiz-deployment-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-nginx-web
spec:
replicas: 1
selector:
matchLabels:
app: nginx-web
template:
metadata:
labels:
app: nginx-web
spec:
containers:
- name: nginx
image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
name: http
创建依赖的 kustomiz-service-test.yaml
kind: Service
apiVersion: v1
metadata:
name: service-nginx-web
spec:
selector:
app: nginx-web
ports:
- name: http
protocol: TCP
port: 80
targetPort: 80
查看目录结构
kustomize/kustomize-test]# tree ./
./
├── kustomization.yaml
├── kustomiz-deployment-test.yaml
└── kustomiz-service-test.yaml
0 directories, 3 files
执行效果
创建资源对象
kubectl apply -k ./
查看资源对象
]# kubectl get pod,svc
NAME READY STATUS RESTARTS AGE
pod/deployment-nginx-web-c77b687c5-6tzmt 1/1 Running 0 9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 57d
service/service-nginx-web ClusterIP 10.106.243.105 <none> 80/TCP 10s
删除资源对象
kubectl delete -k ./
直接以模拟的方式查看效果
kubectl apply -k ./ --dry-run=client -o yaml
仅仅查看生成的配置文件
kustomize build ./
注意:
这种方式可以确认,创建好的资源对象是否包含我们刚才定制的相关属性信息。
小结
1.2.5 功能复用
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
简介
kustomize 可以通过目录复用的机制,实现在通用功能的基础上,对某些特殊的机制进行重写或者集成
方案解析
我们可以创建一个base目录,作为所有相关项目的根目录。
然后再另外一个目录中,通过 bases 属性,继承base目录的相关内容
简单实践
准备基础文件
将我们之前实践出来的环境文件,作为一个基础的可以被其他环境集成的项目目录
mkdir kustomize-proj/base/ -p
cp -a kustomize-test/* kustomize-proj/base/
环境实践
在base目录同级别的位置,创建一个test目录
mkdir kustomize-proj/test
创建一个专用的继承功能的文件 kustomize-proj/test/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../base/
resources:
- namespace.yaml
namespace: test
# kustomize支持添加标注和注释,这些标注和注释会加在每一个它所管理的API对象上
commonLabels:
env: test
commonAnnotations:
superopsmsb.io/app: "kustomize"
images:
- name: "kubernetes-register.superopsmsb.com/superopsmsb/nginx_web"
newTag: "v0.1"
属性解析:
1 通过base继承原来的应用
2 images仅仅是基于原来的镜像名称,更改了镜像的标签。
创建依赖出来的文件 kustomize-proj/test/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test
查看目录效果
# tree kustomize-proj/
kustomize-proj/
├── base
│ ├── kustomization.yaml
│ ├── kustomiz-deployment-test.yaml
│ └── kustomiz-service-test.yaml
└── test
├── kustomization.yaml
└── namespace.yaml
2 directories, 5 files
检查效果
kubectl apply -k kustomize-proj/base/ --dry-run=client -o yaml
kubectl apply -k kustomize-proj/test/ --dry-run=client -o yaml
或者
kustomize build kustomize-proj/base/
kustomize build kustomize-proj/test/
小结
1.2.6 配置定制
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
简介
ConfigMap 和 Secret 包含其他 Kubernetes 对象(如 Pod)所需要的配置或敏感数据。 ConfigMap 或 Secret 中数据的来源往往是集群外部. 例如某个 .properties 文件或者 SSH 密钥文件。
Kustomize 提供 secretGenerator 和 configMapGenerator,可以基于文件或字面 值来生成 Secret 和 ConfigMap。
简单实践
准备基础文件
在base目录同级别的位置,创建一个test目录
mkdir kustomize-proj/staging
环境实践
创建一个专用的继承功能的文件 kustomize-proj/staging/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../base/
resources:
- namespace.yaml
namespace: staging
commonLabels:
environment: staging
commonAnnotations:
superopsmsb.io/app: "kustomize"
configMapGenerator:
- name: nginx-conf
literals:
- HOST="0.0.0.0"
- PORT="80"
secretGenerator:
- name: nginx-ssl
files:
- secrets/tls.crt
- secrets/tls.key
type: "kubernetes.io/tls"
generatorOptions:
disableNameSuffixHash: true
属性解析:
1 通过secretGenerator 和 generatorOptions 生成相关的对象
2 disableNameSuffixHash 的目的是在基于deployment创建pod的时候,禁止生成hash后缀
创建依赖出来的文件 kustomize-proj/staging/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: staging
准备相关的证书信息
mkdir kustomize-proj/staging/secrets && cd kustomize-proj/staging/secrets
openssl genrsa -out tls.key 2048
openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=shuji.superopsmsb.com
注意:
域名必须是我们使用的域名信息
检查效果
查看目录效果
]# tree kustomize-proj/
kustomize-proj/
├── base
│ ├── kustomization.yaml
│ ├── kustomiz-deployment-test.yaml
│ └── kustomiz-service-test.yaml
├── staging
│ ├── kustomization.yaml
│ ├── namespace.yaml
│ └── secrets
│ ├── tls.crt
│ └── tls.key
└── test
├── kustomization.yaml
└── namespace.yaml
4 directories, 9 files
方法1
kubectl apply -k ./base/ --dry-run=client -o yaml
kubectl apply -k ./staging/ --dry-run=client -o yaml
方法2
kustomize build kustomize-proj/base/
kustomize build kustomize-proj/staging/
小结
1.2.7 补丁实践
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
简介
由于软件开发是一个复杂的过程,组织必须保持警惕以确保系统是最新版本的,涉及到的业务功能需要更新,以避免不必要的风险。根据2022年云原生环境下容器存活周期的调查,容器的更新时间是非常快的,在加上容器业务环境的更新迭代非常高,这就对于kubernetes场景中海量的资源清单文件的管理,提出了更高的要求。
补丁的作用
补丁管理是针对当前资源对象进行识别、获取、部署和验证软件更新的做法。我们可以借助于kustomize补丁管理策略和解决方案有助于为kuberntes资源管理对象的统一管理,提供应用验证和应用更新的便捷方法。
补丁管理的意义对于网络安全行业来说不言而喻。为了应对高级威胁,检测全球威胁情报源已知的恶意软件是成功的一半。补丁管理对于阻止已知威胁而言至关重要。
属性解析
资源补丁
patchesJson6902 <[]Json6902> # 待补对象列表
path <String> # 补丁文件路径,支持json或yaml格式
target <Target> # 待补资源对象
group <String> # 资源所属的群组
version <String> # API版本
kind <String> # 资源类型
name <String> # 资源对象的名称
namespace <string> # 资源对象所属的名称空间
patchesStrategicMerge <[]string> # 将补丁补到匹配的资源之上,匹配的方式是根据资源
# Group/Version/Kind + Name/Namespace判断
由于定制的补丁信息,主要起的是补充的作用,所以,为了能够让补丁信息和所归属的资源对象产生关联,我们需要在patch文件中补充原资源对象的基本属性信息,比如apiVersion、Kind、Namespace等。
简单实践
准备基础文件
在base目录同级别的位置,创建一个prod目录
mkdir kustomize-proj/prod/patches
环境实践
创建一个专用的继承功能的文件 kustomize-proj/prod/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../base/
resources:
- namespace.yaml
namespace: prod
commonLabels:
env: prod
commonAnnotations:
superopsmsb.io/app: "kustomize"
configMapGenerator:
- name: nginx-conf
literals:
- host="0.0.0.0"
- port="80"
secretGenerator:
- name: nginx-ssl
files:
- secrets/tls.crt
- secrets/tls.key
type: "kubernetes.io/tls"
generatorOptions:
disableNameSuffixHash: true
patchesStrategicMerge:
- patches/nginx-add-check.yaml
- patches/nginx-add-conf.yaml
patchesJson6902:
- target:
version: v1
kind: Service
name: service-nginx-web
path: patches/patch-service-nginx.yaml
属性解析:
1 通过patchesStrategicMerge 加载相关的补丁文件
2 patchesJson6902 使用这种规则进行规范限制
创建依赖出来的文件 kustomize-proj/prod/namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: prod
准备补丁文件
定制配置相关的补丁文件 kustomize-proj/prod/patches/nginx-add-conf.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-nginx-web
spec:
template:
spec:
containers:
- name: nginx
env:
- name: PORT
valueFrom:
configMapKeyRef:
name: nginx-conf
key: port
optional: false
- name: HOST
valueFrom:
configMapKeyRef:
name: nginx-conf
key: host
optional: true
volumeMounts:
- name: nginx-certs
mountPath: /etc/certs/
readOnly: true
volumes:
- name: nginx-certs
secret:
secretName: nginx-ssl
定制检测相关的补丁文件 kustomize-proj/prod/patches/nginx-add-check.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-nginx-web
spec:
template:
spec:
containers:
- name: nginx
livenessProbe:
httpGet:
path: '/'
port: 80
initialDelaySeconds: 5
readinessProbe:
httpGet:
path: '/'
port: 80
initialDelaySeconds: 15
准备纯粹的补丁文件 kustomize-proj/prod/patches/nginx-add-sidecar.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-nginx-web
spec:
template:
spec:
containers:
- name: busybox
image: kubernetes-register.superopsmsb.com/superopsmsb/busybox:v0.1
command: ['/bin/sh','-c']
args: ['sleep 3600']
定制专属的service修改补丁文件 kustomize-proj/prod/patches/patch-service-nginx.yaml
- op: replace
path: /spec/ports/0/targetPort
value: 8888
- op: add
path: /spec/ports/1
value:
name: https
protocol: TCP
port: 443
targetPort: 8443
准备相关的证书信息
mkdir kustomize-proj/prod/secrets && cd kustomize-proj/prod/secrets
openssl genrsa -out tls.key 2048
openssl req -new -x509 -key tls.key -out tls.crt -subj /O=DevOps/CN=shuji.superopsmsb.com
注意:
域名必须是我们使用的域名信息
查看效果
查看目录效果
]# tree kustomize-proj/prod/
kustomize-proj/prod/
├── kustomization.yaml
├── namespace.yaml
├── patches
│ ├── nginx-add-check.yaml
│ ├── nginx-add-conf.yaml
│ ├── nginx-add-sidecar.yaml
│ └── patch-service-nginx.yaml
└── secrets
├── tls.crt
└── tls.key
2 directories, 8 files
方法1:
kubectl apply -k kustomize-proj/base/ --dry-run=client -o yaml
kubectl apply -k kustomize-proj/prod/ --dry-run=client -o yaml
方法2:
kustomize build kustomize-proj/base/
kustomize build kustomize-proj/prod/
运行项目环境
kubectl apply -k kustomize-proj/prod/
检测效果
kubectl get all -n prod
小结
1.3 访问安全
1.3.1 安全检测
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
国外软件供应商产品历史上出现了许多恶意后门和网络安全事件,譬如服务源码泄露漏洞、 黑客攻击漏洞、木马事件等,由于这些软件运行本身的安全漏洞,导致在此平台之上的应用也运行在危险环境中,我们把这类的安全问题统统成为供应链安全问题 -- 非项目代码本身导致的安全问题。由于供应链安全事件愈演愈烈,从2021年逐渐引起大家注意。
Synopsys 2022年对市场上17个行业2400多个商用产品的代码源进行了分析。开源软件代码使用率超过78%,81%的代码包含至少一个安全漏洞,而且53%的代码存在许可证冲突问题。许可证冲突问题会影响商务软件的使用。
虽然供应链方面的安全隐患非常多,但是我们可以站在用户访问流程的角度,直接从入口进行控制,只要我们能够将常见的用户请求进行精细化的限制的话,我们可以极大程度的降低软件项目所面对的安全攻击手段。
对于基于kubernetes的容器应用来说,我们还可以在Ingress层面进行更加高层次的,专门针对用户请求数据的安全访问控制。
为什么需要使用Ingress暴露服务?
Kubernetes主要通过两种方式实现集群内外的流量访问:
1 支持通过NodePort、LoadBalancer等方式向集群外部暴露内部服务
2 支持以ingress通过单一IP为多个服务提供基于http形式的访问
- 通过主机名和路径将流量路由到特定服务,使得服务访问更加灵活,同时还可以用来提供https服务。
ingress的两大实现软件:
Kubernetes Ingress Nginx
kubernetes社区开发和维护(Nginx帮忙管理)的ingress-nginx,通过nginx的负载均衡和反向代理能力实现流量控制
- kubernetes/ingress-nginx
- https://github.com/kubernetes/ingress-nginx
Nginx Ingress Controller
NGINX公司开发和维护的一种Ingress Controller。实现了nginx更多的能力,包括VS和VSR等,这些都是k8s的ingress默认不支持的功能。
- 它主要有两个发行:基于NGINX开源(免费)和基于NGINX Plus(商业)
- nginxinc/kubernetes-ingress
- https://github.com/nginxinc/kubernetes-ingress/
- https://docs.nginx.com/nginx-ingress-controller/
nginx 控制器的优势
Nginx Ingress Controller和其他Ingress Controller之间的主要区别在于它们的开发和部署模型,尤其是这些模型可以基于不同的目标和优先级实现nginx善于负载均衡和反向代理。
开发理念
- NGINX 项目和产品的首要任务是提供一个快速、轻量级的工具,并具有长期的稳定性和一致性。
集成代码库
- 所有代码都是NGINX自己的代码和校验过的代码,保证控制器环境的高度稳定。
高级流量管理
- 有别于其他Ingress通过注释、CM、template定制高级功能。
- Nginx控制器提供原生、类型安全和缩进的配置风格,高级功能配置
- 包括 TCP/UDP、断路、A/B测试、蓝绿部署、双向TLS认证和WAF等。
持续的生产准备
- 每个版本都按照可支持的生产标准进行构建和维护。
根据2022年<<sysdig云原生和使用报告>>的统计数据显示:在生产环境中有了更多的命名空间,每个命名空间有了更多的 Deployments,每个命名空间下的 Deployments 构成了用户服务应用的部署单元。
所以说如何让流量入口适应新形势下的kubernetes应用就是非常重要的选项了。
简单实践
清理旧环境
清理旧有环境
cd /data/kubernetes/app_secure/
kubectl delete -f ./
清理旧有ingress控制器
kubectl delete -f ingress/deploy-daemonset.yaml
准备环境
获取环境文件
cd /data/kubernetes/process_secure/
git clone https://github.com/nginxinc/kubernetes-ingress.git --branch v2.4.1
cd kubernetes-ingress/deployments
部署基础环境
kubectl apply -f common/ns-and-sa.yaml
kubectl apply -f rbac/rbac.yaml
kubectl apply -f rbac/ap-rbac.yaml
kubectl apply -f rbac/apdos-rbac.yaml
部署通用资源
kubectl apply -f common/default-server-secret.yaml
kubectl apply -f common/nginx-config.yaml
kubectl apply -f common/ingress-class.yaml
创建自定义资源
kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml
kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml
kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml
kubectl apply -f common/crds/k8s.nginx.org_policies.yaml
kubectl apply -f common/crds/k8s.nginx.org_globalconfigurations.yaml
创建应用保护资源对象
kubectl apply -f common/crds/appprotect.f5.com_aplogconfs.yaml
kubectl apply -f common/crds/appprotect.f5.com_appolicies.yaml
kubectl apply -f common/crds/appprotect.f5.com_apusersigs.yaml
创建洪水攻击防护资源对象
kubectl apply -f common/crds/appprotectdos.f5.com_apdoslogconfs.yaml
kubectl apply -f common/crds/appprotectdos.f5.com_apdospolicy.yaml
kubectl apply -f common/crds/appprotectdos.f5.com_dosprotectedresources.yaml
部署环境
部署应用保护的环境
kubectl apply -f deployment/appprotect-dos-arb.yaml
kubectl apply -f service/appprotect-dos-arb-svc.yaml
修改ingress的入口 service/nodeport.yaml
...
spec:
externalIPs: ['10.0.0.12'] # 增加该条属性
...
deployment方式部署ingress控制器
kubectl apply -f deployment/nginx-ingress.yaml
kubectl create -f service/nodeport.yaml
daemonset方式部署ingress控制器
kubectl apply -f daemon-set/nginx-ingress.yaml
注意:
二选一即可,由于daemonset自动做了端口的映射,所以不需要做service
环境检查
]# kubectl get pods --namespace=nginx-ingress
NAME READY STATUS RESTARTS AGE
appprotect-dos-arb-78fb9d4cbf-82xbt 1/1 Running 0 3m31s
nginx-ingress-6644665fb7-rrtsm 1/1 Running 0 2m34s
其他
环境的收尾
kubectl delete namespace nginx-ingress
kubectl delete clusterrole nginx-ingress
kubectl delete clusterrolebinding nginx-ingress
kubectl delete -f common/crds/
小结
1.3.3 简单实践
学习目标
这一节,我们从 准备工作、简单实践、小结 三个方面来学习。
基础知识
准备服务模板文件
定制资源清单文件 01_ingress_web_resource.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: django
spec:
replicas: 2
selector:
matchLabels:
app: django
template:
metadata:
labels:
app: django
spec:
containers:
- name: django
image: kubernetes-register.superopsmsb.com/superopsmsb/django_web:v0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: django-svc
spec:
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: django
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
labels:
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: nginx
应用资源清单文件
kubectl apply -f 01_ingress_web_resource.yaml
检查效果
]# kubectl get deploy,svc
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/django 2/2 2 2 30s
deployment.apps/nginx 2/2 2 2 30s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/django-svc ClusterIP 10.105.153.107 <none> 8000/TCP 30s
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 49d
service/nginx-svc ClusterIP 10.97.65.163 <none> 80/TCP 30s
这里是使用ingressclasses的方式进行ingress方式选择的
]# kubectl get ingressclasses.networking.k8s.io
NAME CONTROLLER PARAMETERS AGE
nginx nginx.org/ingress-controller <none> 26h
功能页面访问
]# curl 10.105.153.107:8000
Hello Django, django-548fb788f5-4mckj-1.10.4
]# curl 10.97.65.163
Hello Nginx, nginx-7f7b4d6686-56jc6-1.23.1
定制单域名多url的ingress
定制资源清单文件 02_ingress_mulurl.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-test
annotations:
# nginx controller的反向代理,默认没有进行重写,我们需要定制重写机制
nginx.org/rewrites: "serviceName=nginx-svc rewrite=/"
spec:
# 对于nginx的入口控制器,它是以ingressClassName的方式来选择如何使用的
# 后面的定制格式与kubernetes的ingress格式一致
ingressClassName: nginx
rules:
- host: shuji.superopsmsb.com
http:
paths:
- path: /django
pathType: Prefix
backend:
service:
name: django-svc
port:
number: 8000
- path: /nginx
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
应用资源清单文件
kubectl apply -f 02_ingress_mulurl.yaml
检查效果
]# kubectl describe ingress ingress-test
Name: ingress-test
...
Ingress Class: nginx
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
shuji.superopsmsb.com
/django django-svc:8000 (10.244.3.11:8000,10.244.4.11:8000)
/nginx nginx-svc:80 (10.244.3.12:80,10.244.5.10:80)
Annotations: nginx.org/rewrites: serviceName=nginx-svc rewrite=/
...
定制专属的域名解析,因为我们对于ingress的入口进行限制了,所以hosts主机名解析信息如下:
echo '10.0.0.12 shuji.superopsmsb.com' >> /etc/hosts
访问效果
]# curl shuji.superopsmsb.com/django
Hello Django, django-548fb788f5-xfdjs-1.10.4
]# curl shuji.superopsmsb.com/nginx
Hello Nginx, nginx-7f7b4d6686-qfsfn-1.23.1
配置解析
]# kubectl -n nginx-ingress exec -it nginx-ingress-6644665fb7-54scz -- cat /etc/nginx/conf.d/default-ingress-test.conf
# configuration for default/ingress-test
upstream default-ingress-test-shuji.superopsmsb.com-django-svc-8000 {
zone default-ingress-test-shuji.superopsmsb.com-django-svc-8000 256k;
random two least_conn;
server 10.244.3.11:8000 max_fails=1 fail_timeout=10s max_conns=0;
server 10.244.4.11:8000 max_fails=1 fail_timeout=10s max_conns=0;
}
upstream default-ingress-test-shuji.superopsmsb.com-nginx-svc-80 {
zone default-ingress-test-shuji.superopsmsb.com-nginx-svc-80 256k;
random two least_conn;
server 10.244.3.12:80 max_fails=1 fail_timeout=10s max_conns=0;
server 10.244.5.10:80 max_fails=1 fail_timeout=10s max_conns=0;
}
server {
listen 80;
listen [::]:80;
server_tokens on;
server_name shuji.superopsmsb.com;
set $resource_type "ingress";
set $resource_name "ingress-test";
set $resource_namespace "default";
location /django {
set $service "django-svc";
...
proxy_pass http://default-ingress-test-shuji.superopsmsb.com-django-svc-8000;
}
location /nginx {
set $service "nginx-svc";
...
proxy_pass http://default-ingress-test-shuji.superopsmsb.com-nginx-svc-80/;
}
}
简单实践
定制secret文件
mkdir ingress-tls && cd ingress-tls
(umask 077; openssl genrsa -out tls.key 2048)
openssl req -new -x509 -key tls.key -out tls.crt -subj "/CN=shuji.superopsmsb.com" -days 365
kubectl create secret tls ingress-tls --cert=./tls.crt --key=./tls.key
cd ..
创建资源对象文件 03_ingress_tlshosts.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-test
annotations:
# 定制重写规则
nginx.org/rewrites: "serviceName=nginx-svc rewrite=/"
spec:
ingressClassName: nginx
# 添加tls认证信息
tls:
- hosts:
- shuji.superopsmsb.com
secretName: ingress-tls
rules:
- host: shuji.superopsmsb.com
http:
paths:
- path: /django
pathType: Prefix
backend:
service:
name: django-svc
port:
number: 8000
- path: /nginx
pathType: Prefix
backend:
service:
name: nginx-svc
port:
number: 80
应用ingress资源对象
kubectl delete -f 02_ingress_mulurl.yaml
kubectl apply -f 03_ingress_tlshosts.yaml
查看效果
]# kubectl describe ingress ingress-test
Name: ingress-test
...
TLS:
ingress-tls terminates shuji.superopsmsb.com
Rules:
Host Path Backends
---- ---- --------
shuji.superopsmsb.com
/django django-svc:8000 (10.244.3.11:8000,10.244.4.11:8000)
/nginx nginx-svc:80 (10.244.3.12:80,10.244.5.10:80)
Annotations: nginx.org/rewrites: serviceName=nginx-svc rewrite=/
...
访问效果
]# curl -k https://shuji.superopsmsb.com/django
Hello Django, django-548fb788f5-xfdjs-1.10.4
]# curl -k https://shuji.superopsmsb.com/nginx
Hello Nginx, nginx-7f7b4d6686-qfsfn-1.23.1
环境收尾
kubectl delete -f 01_ingress_web_resource.yaml -f 03_ingress_tlshosts.yaml
小结
1.3.3 虚拟主机
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
Nginx Ingress Controller提供了基于虚拟主机的路由分发策略,可以让不同的应用位于不同的namespace环境,然后基于虚拟主机的方式在k8s中存在,然后定制一个专属的总路由进行配置,从而实现流量的分发。这种方式与传统意义上的应用流量分发基本一致。
配置解析
虚拟主机属性解析
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: <vs_name>
namespace: <namespace>
spec:
host: 访问域名
tls:
secret: https依赖的tls
routes:
- path: /分发url
route: namespace/虚拟主机名称
虚拟主机路由配置
apiVersion: k8s.nginx.org/v1
kind: VirtualServerRoute
metadata:
name: <vs_name>
namespace: <namespace>
spec:
host: 访问域名
upstreams:
- name: <应用service组名>
service: <应用service组名>
port: <应用service端口>
subroutes:
# 反向代理配置
- path: /<应用url>
action:
proxy:
upstream: <应用service组名>
requestHeaders:
pass: true
set:
- name: 定制请求头
value: 定制请求值
responseHeaders:
add: # 增加响应头
- name: 定制响应头
value: 定制响应结果
always: true # 是否永远存在
rewritePath: / # 设定转发规则
# 根据条件请求路由分发
- path: /<应用url>
matches:
- conditions:
- cookie: version
value: v2
action:
pass: django-v2
案例需求
应用解析
nginx命名空间
nginx的deployment和svc,以及专属的虚拟主机路由
django命名空间
django的deployment和svc,以及专属的虚拟主机路由
django同时部署两个版本,v1和v2
shuji命名空间
定制专属虚拟主机
访问解析
https://shuji.superopsmsb.com/nginx - 默认访问nginx命名空间的nginx应用
https://shuji.superopsmsb.com/django - 默认访问django命名空间的django的v2版本
访问django的时候,携带"version=v1"请求头 - 访问django命名空间的django的v1版本
简单实践
准备基础环境
定制资源清单文件 04_ingress_vs_base.yaml
apiVersion: v1
kind: Namespace
metadata:
name: django
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: django-v1
namespace: django
spec:
replicas: 2
selector:
matchLabels:
app: django-v1
template:
metadata:
labels:
app: django-v1
spec:
containers:
- name: django
image: kubernetes-register.superopsmsb.com/superopsmsb/django_web:v0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: django-v1-svc
namespace: django
spec:
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: django-v1
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: django-v2
namespace: django
spec:
replicas: 2
selector:
matchLabels:
app: django-v1
template:
metadata:
labels:
app: django-v1
spec:
containers:
- name: django
image: kubernetes-register.superopsmsb.com/superopsmsb/django_web:v0.2
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
name: django-v1-svc
namespace: django
spec:
ports:
- port: 8000
targetPort: 8000
protocol: TCP
name: http
selector:
app: django-v1
---
apiVersion: v1
kind: Namespace
metadata:
name: nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: nginx
spec:
replicas: 2
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: kubernetes-register.superopsmsb.com/superopsmsb/nginx_web:v0.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: nginx-svc
namespace: nginx
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: nginx
---
apiVersion: v1
kind: Namespace
metadata:
name: shuji
准备子路由主机
定制nginx虚拟主机路由资源清单文件 05_ingress_vs_nginx.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServerRoute
metadata:
name: nginx
namespace: nginx
spec:
host: shuji.superopsmsb.com
upstreams:
- name: nginx
service: nginx-svc
port: 80
subroutes:
- path: /nginx
action:
proxy:
upstream: nginx
requestHeaders:
pass: true
set:
- name: My-Header
value: NGINX-Best
responseHeaders:
add:
- name: My-Header
value: ${http_user_agent}
- name: IC-Nginx-Version
value: ${nginx_version}
always: true
rewritePath: /
定制django虚拟主机路由资源清单文件 06_ingress_vs_django.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServerRoute
metadata:
name: django
namespace: django
spec:
host: shuji.superopsmsb.com
upstreams:
- name: django-v1
service: django-v1-svc
port: 8000
- name: django-v2
service: django-v2-svc
port: 8000
subroutes:
- path: /django
matches:
- conditions:
- cookie: version
value: v2
action:
pass: django-v2
action:
pass: django-v1
定制虚拟服务主机
定制虚拟主机的资源清单 07_ingress_vs_shuji.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: shuji
namespace: shuji
spec:
host: shuji.superopsmsb.com
tls:
secret: ingress-tls
routes:
- path: /nginx
route: nginx/nginx
- path: /django
route: django/django
创建secret
kubectl -n shuji create secret tls ingress-tls --cert=./ingress-tls/tls.crt --key=./ingress-tls/tls.key
测试效果
应用资源清单文件
kubectl apply -f 04_ingress_vs_base.yaml -f 05_ingress_vs_nginx.yaml -f 06_ingress_vs_django.yaml -f 07_ingress_vs_shuji.yaml
检查命名空间
kubectl get ns | egrep 'shuji|django|nginx'
检查资源对象
kubectl get vs -n shuji
kubectl get vsr,deploy,svc -n django
kubectl get vsr,deploy,svc -n nginx
普通http检查
]# curl -k https://shuji.superopsmsb.com/nginx
Hello Nginx, nginx-7f7b4d6686-g66wh-1.23.1
]# curl -k https://shuji.superopsmsb.com/django
Hello Django, django-v1-fddf5c955-h8rrz-1.10.4
指定请求版本检查
]# curl -k --cookie "version=v1" https://shuji.superopsmsb.com/django
Hello Django, django-v1-fddf5c955-h8rrz-1.10.4
环境收尾
kubectl delete -f 07_ingress_vs_shuji.yaml -f 06_ingress_vs_django.yaml -f 05_ingress_vs_nginx.yaml -f 04_ingress_vs_base.yaml
1.3.4 四层实践
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
Nginx ingress 不仅仅可以做http级别的ingress,还支持ip:port级别的ingress。这个功能需要GlobalConfiguration能力的支持。
准备工作
改造 deployment资源对象 deployment/nginx-ingress.yaml
...
spec:
...
template:
...
spec:
...
containers:
- image: nginx/nginx-ingress:2.4.1
...
ports:
...
- name: tcp-8001
containerPort: 8001
- name: tcp-8002
containerPort: 8002
...
args:
...
- -global-configuration=$(POD_NAMESPACE)/nginx-configuration
配置解析:
开启-global-configuration启动参数,开启专属容器端口映射
改造service资源对象 service/nodeport.yaml
...
...
spec:
externalIPs: ['10.0.0.12']
type: NodePort
ports:
...
- port: 8001
targetPort: 8001
protocol: TCP
name: tcp-8001
- port: 8002
targetPort: 8002
protocol: TCP
name: tcp-8002
配置解析:
开启专属容器端口映射
重启环境
kubectl apply -f deployment/nginx-ingress.yaml
kubectl apply -f service/nodeport.yaml
简单实践
创建基础应用环境
kubectl apply -f 01_ingress_web_resource.yaml
创建资源对象文件
创建 08_ingress_globalconfiguration_listener.yaml
apiVersion: k8s.nginx.org/v1alpha1
kind: GlobalConfiguration
metadata:
name: nginx-configuration
namespace: nginx-ingress
spec:
listeners:
- name: tcp-8001
port: 8001
protocol: TCP
- name: tcp-8002
port: 8002
protocol: TCP
创建专属的代理配置 09_ingress_transportserver.yaml
apiVersion: k8s.nginx.org/v1alpha1
kind: TransportServer
metadata:
name: nginx
spec:
listener:
name: tcp-8001
protocol: TCP
upstreams:
- name: nginx
service: nginx-svc
port: 80
action:
pass: nginx
---
apiVersion: k8s.nginx.org/v1alpha1
kind: TransportServer
metadata:
name: django
spec:
listener:
name: tcp-8002
protocol: TCP
upstreams:
- name: django
service: django-svc
port: 8000
action:
pass: django
应用资源清单文件
kubectl apply -f 01_ingress_web_resource.yaml -f 08_ingress_globalconfiguration_listener.yaml -f 09_ingress_transportserver.yaml
检查效果
]# curl 10.0.0.12:8001
Hello Nginx, nginx-7f7b4d6686-fkbbj-1.23.1
]# curl 10.0.0.12:8002
Hello Django, django-548fb788f5-pdgzw-1.10.4
环境收尾
对于globalconfiguration环境来说,这个资源千万不要删除,如果非要删除的话,可以清理该对象的配置即可,资源对象文件:10_ingress_globalconfiguration_reset.yaml
apiVersion: k8s.nginx.org/v1alpha1
kind: GlobalConfiguration
metadata:
name: nginx-configuration
namespace: nginx-ingress
spec:
清理环境
kubectl delete -f 01_ingress_web_resource.yaml -f 09_ingress_transportserver.yaml -f 10_ingress_globalconfiguration_reset.yaml
小结
1.3.5 策略实践
学习目标
这一节,我们从 基础知识、简单实践、小结 三个方面来学习。
基础知识
需求
Nginx ingress 提供了大量与waf等相关的内容,我们可以在ingress中,为不同的应用定制各自的访问策略信息。
属性解读
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: <policy_name>
spec:
rateLimit:
rate: 100r/s # 每秒 100 个请求的速率
burst: 50 # 超过burst数量的过多的请求会被延迟。
noDelay: true # 在请求受到限制时禁用延迟过多请求。delay如果两者都设置,则覆盖。
key: ${binary_remote_addr} # 应用速率限制的键。可以是文本、变量${}或组合。
zoneSize: 10M # 设定共享内存区域的大小
rejectCode: 444 # 设置状态码以响应被拒绝的请求而返回。必须落入范围400..599。默认为503。
参考资料:
https://docs.nginx.com/nginx-ingress-controller/configuration/policy-resource/
工具部署
git clone https://github.com/wg/wrk.git wrk
cd wrk
make
cp wrk /usr/local/bin
命令帮助
语法格式: wrk <选项> <被测HTTP服务的URL>
语法参数
-c, --connections <N> 跟服务器建立并保持的 TCP 连接数量
-d, --duration <T> 压测时间
-t, --threads <N> 使用多少个线程进行压测
-s, --script <S> 指定 Lua 脚本路径
-H, --header <H> 为每一个 HTTP 请求添加 HTTP 头
--latency 在压测结束后,打印延迟统计信息
--timeout <T> 超时时间
-v, --version 打印正在使用的 wrk 的详细版本信息
<N>代表数字参数,支持国际单位 (1k, 1M, 1G)
<T>代表时间参数,支持时间单位 (2s, 2m, 2h)
简单实践
简单对象
定制资源对象 11_ingress_vs_nginx.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: shuji
spec:
host: shuji.superopsmsb.com
# policies:
# - name: rate-limit-policy
upstreams:
- name: nginx
service: nginx-svc
port: 80
routes:
- path: /
action:
pass: nginx
运行资源对象
kubectl apply -f 01_ingress_web_resource.yaml -f 11_ingress_vs_nginx.yaml
测试效果
]# wrk -t2 -c10 -d10s http://shuji.superopsmsb.com
Running 10s test @ http://shuji.superopsmsb.com # 压测时间10s)
2 threads and 10 connections # 共1个测试线程,10个连接
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.48ms 4.97ms 109.56ms 93.11% # 延迟信息
Req/Sec 1.02k 229.62 1.48k 70.50% # 处理中请求
20466 requests in 10.05s, 5.44MB read # 10.05s处理20466个请求
# 读取5.44MB数据
Requests/sec: 2037.01 # 平均每秒处理完成2037.01个请求
Transfer/sec: 554.91KB # 平均每秒读取数据554.91KB
策略实践
定制策略文件 12_ingress_policy_limit.yaml
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: rate-limit-policy
spec:
rateLimit:
rate: 10r/s
burst: 50
noDelay: true
key: ${binary_remote_addr}
zoneSize: 5M
rejectCode: 444
修改虚拟主机文件 11_ingress_vs_nginx.yaml
...
spec:
host: shuji.superopsmsb.com
policies:
- name: rate-limit-policy
...
应用资源清单文件
kubectl apply -f 12_ingress_policy_limit.yaml -f 11_ingress_vs_nginx.yaml
测试效果:
]# wrk -t2 -c10 -d10s http://shuji.superopsmsb.com
Running 10s test @ http://shuji.superopsmsb.com
2 threads and 10 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 5.72ms 3.58ms 28.40ms 83.44%
Req/Sec 11.90 35.35 282.00 98.02%
151 requests in 10.05s, 41.14KB read
Socket errors: connect 0, read 29404, write 0, timeout 0
Requests/sec: 15.02
Transfer/sec: 4.09KB
结果显示:
Requests/sec 的值从原来的 2000左右,下降到了10左右,说明限制生效了。
查看控制器pod
]# kubectl get pod -n nginx-ingress
NAME READY STATUS RESTARTS AGE
nginx-ingress-79fbbb9dc-s4qnz 1/1 Running 0 64m
查看日志效果
]# kubectl logs nginx-ingress-79fbbb9dc-s4qnz -n nginx-ingress
...
2022/10/24 06:33:31 [error] 138#138: *81481 limiting requests, excess: 50.470 by zone "pol_rl_default_rate-limit-policy_default_shuji", client: 10.244.0.1, server: shuji.superopsmsb.com, request: "GET / HTTP/1.1", host: "shuji.superopsmsb.com"
10.244.0.1 - - [24/Oct/2022:06:33:31 +0000] "GET / HTTP/1.1" 444 0 "-" "-" "-"
结果显示:
限制策略实施成功了
环境收尾
kubectl delete -f 12_ingress_policy_limit.yaml -f 11_ingress_vs_nginx.yaml -f 01_ingress_web_resource.yaml