标签归档:Kubernetes

Kubespray 部署生产级 k8s 集群

kubespray 是基于 ansible 开发的一套 K8s 生命周期管理软件,由 k8s 官方 sig 维护。

遇到问题多读文档,搞清楚命令意味着什么再操作。

部署方法

获取部署程序

# 从 kubespray 官方仓库拉取
git clone --depth=1 https://github.com/kubernetes-sigs/kubespray.git
# 切换到 v2.26.0 版本,不同版本对应支持不同 k8s 版本,根据需要切换
git checkout v2.26.0

# 进入部署程序目录
cd kubespray

# 创建 python 虚拟环境,激活,并安装依赖
python3 -m venv .venv
source .venv/bin/activate
pip3 install -r  requirements.txt

定义部署节点信息

# 从集群配置模板拷贝一份
cp -rfp inventory/sample inventory/mycluster

# 定义节点 IP 清单,将 IP 更换为自己节点的 IP
declare -a IPS=(10.10.1.3 10.10.1.4 10.10.1.5)
CONFIG_FILE=inventory/mycluster/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}

# 根据需要调整节点角色
inventory/mycluster/hosts.yaml

执行以上命令后会在生成一份节点角色清单在 inventory/mycluster/hosts.yaml 路径下,可以根据需要调整,如调整希望作为 master 的节点、运行 etcd 的节点、作为 worker 的节点等。

定义部署 集群信息

根据需要配置集群:

  • inventory/mycluster/group_vars/all/*.yml
  • inventory/mycluster/group_vars/k8s-cluster/*.yml

可以定义部署的 K8s 版本、使用的容器运行时、CNI、Ingress 等内容,具体看注释。

开始部署

# 可选,清理旧集群,若怀疑有残留可以运行这个清理,也可跳过
ansible-playbook -i inventory/mycluster/hosts.yaml  --become --become-user=root reset.yml

# 开始部署
ansible-playbook -i inventory/mycluster/hosts.yaml  --become --become-user=root cluster.yml

若调整了集群配置,重新运行部署即可。

reset.yml 亦可用于销毁集群,谨慎操作.

扩容

扩容貌似再次执行一次部署命令也可,也可运行例如下面的命令,供参考。

ansible-playbook \
  -i inventory/mycluster/inventory.ini \
  --private-key=id_rsa \
  --user=root -b \
  scale.yml

缩容

使用以下命令运行 remote-node.yml 即可,将 node 变量指向希望移除的节点,命令供参考。

ansible-playbook \
  -i inventory/mycluster/inventory.ini \
  --private-key=id_rsa \
  --user=ubuntu -b \
  --extra-vars "node=node1,node2" \
  remove-node.yml

--extra-vars 里写要移出的节点名列表,如果节点已经卡死,无法通过 SSH 登录,可以在 --extra-vars 加个 reset_nodes=false 的选项,跳过第二个步骤。

References

#Kubernetes #Kubespray

镜像操作神器 skopeo 用法总结

skopeo 是一个命令行工具,可对容器镜像和容器存储进行操作。 在没有dockerd的环境下,使用 skopeo 操作镜像是非常方便的。

安装

包管理器

# RHEL / CentOS Stream ≥ 8
sudo dnf install skopeo

# RHEL/CentOS ≤ 7.x
yum install skopeo

# openSUSE:
sudo zypper install skopeo

# alpine:
sudo apk add skopeo

# macOS:
brew install skopeo

# ArchLinux
sudo pacman -S skopeo

其他系统见 安装文档

编译安装

git clone https://github.com/containers/skopeo skopeo
cd !$
git checkout v1.0.0
make binary-static DISABLE_CGO=1
cp skopeo /usr/local/bin/

使用

在使用 skopeo 之前,我们首先要知道在命令行中镜像的格式

需要注意的是,这几种镜像的名字,对应着镜像存在的方式,不同存在的方式对镜像的 layer 处理的方式也不一样,比如 docker:// 这种方式是存在 registry 上的,docker-daemon: 是存在本地 docker pull 下来的,再比如 docker-archive 是通过 docker save 出来的镜像。同一个镜像有这几种存在的方式就像水有气体、液体、固体一样。可以这样去理解,他们表述的都是同一个镜像,只不过是存在的方式不一样而已。

IMAGE NAMES example
containers-storage: containers-storage:
dir: dir:/PATH
docker:// docker://k8s.gcr.io/kube-apiserver:v1.17.5
docker-daemon: docker-daemon:alpine:latest
docker-archive: docker-archive:alpine.tar (docker save)
oci: oci:alpine:latest

命令选项

# skopeo --help
NAME:
   skopeo - Various operations with container images and container image registries

USAGE:
   skopeo [global options] command [command options] [arguments...]

VERSION:
   0.1.37

COMMANDS:
     copy               Copy an IMAGE-NAME from one location to another
     inspect            Inspect image IMAGE-NAME
     delete             Delete image IMAGE-NAME
     manifest-digest    Compute a manifest digest of a file
     standalone-sign    Create a signature using local files
     standalone-verify  Verify a signature using local files
     help, h            Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --debug                  enable debug output
   --policy value           Path to a trust policy file
   --insecure-policy        run the tool without any policy check
   --registries.d DIR       use registry configuration files in DIR (e.g. for container signature storage)
   --override-arch ARCH     use ARCH instead of the architecture of the machine for choosing images
   --override-os OS         use OS instead of the running OS for choosing images
   --command-timeout value  timeout for the command execution (default: 0s)
   --help, -h               show help
   --version, -v            print the version

可以看到 skopeo 的功能很简单:

  • copy:复制一个镜像从 A 到 B,这里的 A 和 B 可以为本地 docker 镜像或者 registry 上的镜像。
  • inspect:查看一个镜像的 manifest 火车 image config 详细信息
  • delete:删除一个镜像,可以是本地 docker 镜像或者 registry 上的镜像
  • list-tags:列出一个 registry 上某个镜像的所有 tag
  • login:登录到某个 registry,和 docker login 类似
  • logout: 退出已经登录到某个 registry 的 auth 信息,和 docker logout 类似
  • manifest-digeststandalone-signstandalone-verify 这三个用的不多
  • sync:同步一个镜像从 A 到 B,感觉和 copy 一样,但 sync 支持的参数更多,功能更强大

获取镜像的信息

skopeo 可以在不用下载镜像的情况下,获取镜像信息

# skopeo inspect docker://docker.io/centos
{
    "Name": "docker.io/library/centos",
    "Digest": "sha256:fe8d824220415eed5477b63addf40fb06c3b049404242b31982106ac204f6700",
    "RepoTags": [
        "5.11",
        "5",
        "6.10",
        "6.6",
        "6.7",
        "6.8",
        "6.9",
        "6",
        "7.0.1406",
        "7.1.1503",
        "7.2.1511",
        "7.3.1611",
        "7.4.1708",
        "7.5.1804",
        "7.6.1810",
        "7.7.1908",
        "7",
        "8.1.1911",
        "8",
        "centos5.11",
        "centos5",
        "centos6.10",
        "centos6.6",
        "centos6.7",
        "centos6.8",
        "centos6.9",
        "centos6",
        "centos7.0.1406",
        "centos7.1.1503",
        "centos7.2.1511",
        "centos7.3.1611",
        "centos7.4.1708",
        "centos7.5.1804",
        "centos7.6.1810",
        "centos7.7.1908",
        "centos7",
        "centos8.1.1911",
        "centos8",
        "latest"
    ],
    "Created": "2020-01-18T00:26:46.850750902Z",
    "DockerVersion": "18.06.1-ce",
    "Labels": {
        "org.label-schema.build-date": "20200114",
        "org.label-schema.license": "GPLv2",
        "org.label-schema.name": "CentOS Base Image",
        "org.label-schema.schema-version": "1.0",
        "org.label-schema.vendor": "CentOS",
        "org.opencontainers.image.created": "2020-01-14 00:00:00-08:00",
        "org.opencontainers.image.licenses": "GPL-2.0-only",
        "org.opencontainers.image.title": "CentOS Base Image",
        "org.opencontainers.image.vendor": "CentOS"
    },
    "Architecture": "amd64",
    "Os": "linux",
    "Layers": [
        "sha256:8a29a15cefaeccf6545f7ecf11298f9672d2f0cdaf9e357a95133ac3ad3e1f07"
    ]
}

docker://: 是使用 Docker Registry HTTP API V2 进行连接远端 docker.io: 远程仓库 centos: 镜像名称

也可以获取本地dockerd的镜像信息

# skopeo inspect docker-daemon:ubuntu:latest
{
    "Name": "docker.io/library/ubuntu",
    "Digest": "sha256:004d05bd520c5c386fb8322d6081f4de567a9417e1ba8e5071663d7811992c88",
    "RepoTags": [],
    "Created": "2020-03-20T19:20:22.835345724Z",
    "DockerVersion": "18.09.7",
    "Labels": null,
    "Architecture": "amd64",
    "Os": "linux",
    "Layers": [
        "sha256:c8be1b8f4d60d99c281fc2db75e0f56df42a83ad2f0b091621ce19357e19d853",
        "sha256:977183d4e9995d9cd5ffdfc0f29e911ec9de777bcb0f507895daa1068477f76f",
        "sha256:6597da2e2e52f4d438ad49a14ca79324f130a9ea08745505aa174a8db51cb79d",
        "sha256:16542a8fc3be1bfaff6ed1daa7922e7c3b47b6c3a8d98b7fca58b9517bb99b75"
    ]
}

docker-daemon: docker守护镜像的镜像 ubuntu:latest: 本地镜像的名称

拷贝镜像

注意一下,这里的 location 就是指的上面提到的 IMAGE NAMES ,也就是说 skopeo copy src dest 可以有 6*6=36 种组合!比如我可以将一个镜像从一个 registry 复制到另一个 registry,skopeo copy docker://IMAGE_NAME docker://IMAGE_NAME,再强调一遍,一定要注意 IMAGE_NAME 的命名的格式。

在不使用 docker 的情况下从远端下载镜像

# skopeo --insecure-policy copy docker://nginx:1.17.6 docker-archive:/tmp/nginx.tar
Getting image source signatures
Copying blob 8ec398bc0356 done
Copying blob 465560073b6f done
Copying blob f473f9fd0a8c done
Copying config f7bb5701a3 done
Writing manifest to image destination
Storing signatures
# ls -alh  /tmp/nginx.tar 
-rw-r--r-- 1 root root 125M 4月  13 15:22 /tmp/nginx.tar

--insecure-policy: 用于忽略安全策略配置文件 docker://nginx:1.17.6: 该命令将会直接通过 http 下载目标镜像 docker-archive: 存储为 /tmp/nginx.tar,此文件可以直接通过 docker load 命令导入

相应的,可以将下载的文件导入到本地

# skopeo copy docker-archive:/tmp/nginx.tar docker-daemon:nginx:latest
Getting image source signatures
Copying blob 556c5fb0d91b done
Copying blob 49434cc20e95 done
Copying blob 75248c0d5438 done
Copying config f7bb5701a3 done
Writing manifest to image destination
Storing signatures

# docker images nginx
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
nginx               latest              f7bb5701a33c        3 months ago        126MB

也可以将镜像下载到指定目录

# skopeo copy docker://busybox:latest dir:/tmp/busybox
Getting image source signatures
Copying blob 0669b0daf1fb done
Copying config 83aa35aa1c done
Writing manifest to image destination
Storing signatures

# ls -alh /tmp/busybox/
总用量 760K
drwxr-xr-x   2 root root  186 4月  13 15:26 .
drwxrwxrwt. 12 root root 4.0K 4月  13 15:25 ..
-rw-r--r--   1 root root 743K 4月  13 15:26 0669b0daf1fba90642d105f3bc2c94365c5282155a33cc65ac946347a90d90d1
-rw-r--r--   1 root root 1.5K 4月  13 15:26 83aa35aa1c79e4b6957e018da6e322bfca92bf3b4696a211b42502543c242d6f
-rw-r--r--   1 root root  527 4月  13 15:26 manifest.json
-rw-r--r--   1 root root   33 4月  13 15:25 version

或者从指定目录导入到本地

# skopeo copy dir:/tmp/busybox docker-daemon:busybox:latest
Getting image source signatures
Copying blob 0669b0daf1fb done
Copying config 83aa35aa1c done
Writing manifest to image destination
Storing signatures
# docker images busybox
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
busybox             latest              83aa35aa1c79        4 weeks ago         1.22MB

其他命令

skopeo --debug copy docker-archive:/dev/stdin containers-storage:foo < /tmp/nginx.tar

删除镜像

skopeo delete docker://localhost:5000/nginx:latest

认证文件

认证文件默认存放在 $HOME/.docker/config.json

文件内容

{
    "auths": {
        "myregistrydomain.com:5000": {
            "auth": "dGVzdHVzZXI6dGVzdHBhc3N3b3Jk",
            "email": "stuf@ex.cm"
        }
    }
}

其他参数

# 跳过源地址的 tls
skopeo copy docker://192.168.25.8:5000/library/registry:latest  oci-archive:registry-latest.tar --src-tls-verify=false

# 跳过验证 tls
skopeo list-tags --tls-verify=false docker://dockerio.vkvmsg1.skybyte.me/library/registry
# 注意这里 dockerio 的顶级镜像自行补全 library 前缀

References

#Docker #Containerd #Kubernetes

Kubernetes 使用 Kubectl 重启 Pod 的多种方法

大家可能都知道 kubectl 其实没有 restart pod 这个命令,这个主要是由于在 k8spod 的管理属于 rs 等控制器,并不需要运维手动维护,但有时候,我们修改了 configmap 的配置文件后,希望重启 pod 加载配置,此时就需要 “重启” Pod。这里说的“重启”是加了引号的,准确地来说,是重建 pod,给用户的感觉是重启。

下面介绍六种 k8s 里面重启 pod 的方式

方法一:kubectl rollout restart

这个命令是比较推荐的,通过

kubectl rollout restart deployment 
<deployment_name> -n <namespace>

便可以重建这个deployment下的 pod,和滚动升级类似,并不会一次性杀死Pod,比较平滑。

方法二:kubectl scale

这种方法相对来说,比较粗放,我们可以先将副本调成 0

kubectl scale deployment <deployment name> -n <namespace> --replicas=0

然后再改回目的副本数

kubectl scale deployment <deployment name> -n <namespace> --replicas=10

但这个会中断服务。但两条命令也能解决,下面介绍的就更直接了。

方法三: kubectl delete pod

这个就不解释了

kubectl delete pod 
<pod_name> -n <namespace>

还是多说一句,此时优雅删除的效果还是有的。再多说一句,直接删 rs 效果也挺好。

方法四:kubectl replace

这种方法是通过更新 Pod ,从触发 k8s pod 的更新

kubectl get pod 
<pod_name> -n <namespace> -o yaml | kubectl replace --force -f -

方法五:kubectl set env

通过 设置环境变量,其实也是更新 pod spec 从而触发滚动升级。

kubectl set env deployment <deployment name> -n <namespace> DEPLOY_DATE="$(date)"

只不过这里通过 kubectl 命令行,当我们通过 API 更新 pod spec 后一样会触发滚动升级

方法六: kill 1

这种方法就是在容器里面 kill 1 号进程。

kubectl exec -it 
<pod_name> -c <container_name> --/bin/sh -c "kill 1"

但是但是但是,重要的话说三遍,它有个局限,必须要求你的 1 号进程要 捕获 TERM 信号,否则在容器里面是杀不死自己的.

References

#Kubernetes

Kubernetes 使用 multus 插件增加子接口并固定 ip

apiVersion: "k8s.cni.cncf.io/v1"  
kind: NetworkAttachmentDefinition  
metadata:  
 name: macvlan8  
 namespace: multicast  
spec:  
 config: '{  
   "cniVersion": "0.3.1",  
   "plugins": [  
     {  
       "type": "macvlan",  
       "capabilities": { "ips": true },  
       "master": "eth1",  
       "mode": "bridge",  
       "ipam": {  
         "type": "static",  
         "addresses": [  
             {  
                 "address": "192.168.25.62/22",  
                 "gateway": "192.168.27.254"  
             }  
         ],  
         "routes": [  
             { "dst": "192.168.24.0/22", "gw": "192.168.27.254" },  
             { "dst": "192.168.5.0/24" }  
         ]  
       }  
     }  
   ]  
 }'

示例负载:

---  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
 labels:  
   app: demo8  
 name: demo8  
 namespace: multicast  
spec:  
 replicas: 1  
 selector:  
   matchLabels:  
     app: demo8  
 template:  
   metadata:  
     annotations:  
       k8s.v1.cni.cncf.io/networks: macvlan8  
     labels:  
       app: demo8  
   spec:  
     containers:  
       - name: demo8  
         command:    
           - /bin/sh  
         args:  
           - '-c'  
           - "while true; do echo 1;sleep 10; done"  
         image: '192.168.25.9/hello_multicast_iperf:1_aarch64'  
         imagePullPolicy: Always  
         resources:  
           limits:  
             cpu: '2'  
           requests:  
             cpu: '2'

测试效果:

[root@node1 multus]# kubectl -n multicast exec -it demo8-578986878b-6m5nr -- sh
/ # ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
3: eth0@if75: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1430 qdisc noqueue state UP 
    link/ether 02:0c:78:14:6e:f9 brd ff:ff:ff:ff:ff:ff
    inet 10.233.71.42/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::c:78ff:fe14:6ef9/64 scope link 
       valid_lft forever preferred_lft forever
4: net1@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
    link/ether ca:35:41:d3:92:3e brd ff:ff:ff:ff:ff:ff
    inet 192.168.25.62/22 brd 192.168.27.255 scope global net1
       valid_lft forever preferred_lft forever
    inet6 fe80::c835:41ff:fed3:923e/64 scope link 
       valid_lft forever preferred_lft forever
/ # ip r
default via 169.254.1.1 dev eth0 
169.254.1.1 dev eth0 scope link 
192.168.5.0/24 via 192.168.27.254 dev net1 
192.168.24.0/22 via 192.168.27.254 dev net1 
192.168.24.0/22 dev net1 scope link  src 192.168.25.62

References

kubernetes 使用 multus 为 pod 增加子接口

部署 multus-cni

git clone https://github.com/k8snetworkplumbingwg/multus-cni.git
cat ./deployments/multus-daemonset-thick.yml | kubectl apply -f -

创建 NetworkAttachmentDefinition

NetworkAttachmentDefinition 是 Kubernetes 中的一个自定义资源定义(Custom Resource Definition,简称 CRD)。这是由 Multus CNI 插件引入的,用于在 Kubernetes 中定义和管理额外的网络接口。

Multus CNI 是一个插件,允许 Kubernetes Pods 同时连接到多个网络。默认情况下,Kubernetes Pod 只有一个网络接口,即使你有多个网络,Pod 也只能连接到一个。Multus 解决了这个问题,使得 Pod 可以连接到多个网络。

NetworkAttachmentDefinition 对象定义了如何创建这些额外的网络接口。它包含了必要的配置信息,例如 CNI 插件的类型(如 flannel,macvlan,vlan等)、网络配置参数等。当你创建一个 Pod,并希望它连接到额外的网络时,你可以在 Pod 的定义中引用相应的 NetworkAttachmentDefinition

例如,以下是一个 NetworkAttachmentDefinition 的示例:

下面定义了一个名叫 macvlan1multus-cni NetworkAttachmentDefinitionmulticast 命名空间中,桥接到宿主机的 eth0 接口:

apiVersion: "k8s.cni.cncf.io/v1"  
kind: NetworkAttachmentDefinition  
metadata:  
 name: macvlan1  
 namespace: multicast  
spec:  
 config: '{  
     "cniVersion": "0.3.0",  
     "type": "macvlan",  
     "master": "eth0",  
     "mode": "bridge",  
     "ipam": {  
       "type": "host-local",  
       "ranges": [  
                   [ {  
                        "subnet": "192.168.24.0/22",  
                        "rangeStart": "192.168.25.60",  
                        "rangeEnd": "192.168.25.70",  
                        "gateway": "192.168.27.254"  
                   } ]  
       ],  
       "routes": [  
           {  
             "dst": "192.168.24.0/22",  
             "dst": "192.168.5.0/24",  
             "gw": "192.168.27.254"  
           }  

       ]  
     }  
   }'

其中的 ranges 字段定义了用于 multus 子接口分配的 ip 资源信息,routes 字段定义了桥接后增加到 pod 中的路由信息, dst 定义了通过该接口的网段,gw 字段定义这些网段的网关。

描述使用子接口的 pod 资源

apiVersion: v1
kind: Pod
metadata:
  name: pod-case-01
  annotations:
    k8s.v1.cni.cncf.io/networks: macvlan-conf-1, macvlan-conf-2
spec:
  containers:
  - name: pod-case-01
    image: docker.io/centos/tools:latest
    command:
    - /sbin/init

还可以通过添加 `@

` 来指定接口名称,例如 `macvlan-conf-1@eth1`

描述使用子接口的 deployment 资源

---  
apiVersion: apps/v1  
kind: Deployment  
metadata:  
 labels:  
   app: broadcast  
 name: broadcast  
 namespace: multicast  
spec:  
 replicas: 1  
 selector:  
   matchLabels:  
     app: broadcast  
 template:  
   metadata:  
     annotations:  
       k8s.v1.cni.cncf.io/networks: macvlan1@eth1  
     labels:  
       app: broadcast  
   spec:  
     containers:  
       - name: broadcast  
         command:    
           - /bin/sh  
         args:  
           - '-c'  
           - "while true; do echo 1;sleep 10; done"  
         image: '192.168.25.9/hello_multicast_iperf:1_aarch64'  
         imagePullPolicy: Always  
         resources:  
           limits:  
             cpu: '2'  
           requests:  
             cpu: '2'

References

#Kubernetes #MultusCNI