Iawen's Blog

我喜欢这样自由的随手涂鸦, 因为我喜欢风......

kubernetes, 简称K8s, 是用8代替名字中间的8个字符“ubernete”而成的缩写。是一个开源的, 用于管理云平台中多个主机上的容器化的应用, Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署, 规划, 更新, 维护的一种机制。

Kubernetes是Google开源的一个容器编排引擎, 它支持自动化部署、大规模可伸缩、应用容器化管理。在生产环境中部署一个应用程序时, 通常要部署该应用的多个实例以便对应用请求进行负载均衡。
在Kubernetes中, 我们可以创建多个容器, 每个容器里面运行一个应用实例, 然后通过内置的负载均衡策略, 实现对这一组应用实例的管理、发现、访问, 而这些细节都不需要运维人员去进行复杂的手工配置和处理。

0

1. Kubernetes 概述

1.1 基本概念

  • 节点(Node): 一个节点是一个运行 Kubernetes 中的主机
  • 容器组(Pod): 一个 Pod 对应于由若干容器组成的一个容器组, 同个组内的容器共享一个存储卷(volume)
  • 容器组生命周期(pos-states): 包含所有容器状态集合, 包括容器组状态类型、容器组生命周期、事件、重启策略以及 replication controllers
  • Replication Controllers: 主要负责指定数量的 pod 在同一时间一起运行(逐步为副本集所替代)
  • 服务(services): 一个 Kubernetes 服务是容器组逻辑的高级抽象, 同时也对外提供访问容器组的策略
  • 卷(volumes): 一个卷就是一个目录, 容器对其有访问权限
  • 标签(labels): 标签是用来连接一组对象的, 比如容器组。标签可以被用来组织和选择子对象
  • 接口权限(accessing_the_api): 端口, IP 地址和代理的防火墙规则
  • web 界面(ux): 用户可以通过 web 界面操作 Kubernetes

1.2 Kubernetes 的运行原理

1

2. Kubernetes 部署

2.1 环境设置(Minibute 略过)

2.1.1 依赖安装

yum -y install ebtables ethtool socat conntrack-tools

2.1.2 允许iptables检查桥接流量

cat <<EOF | tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF

cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

2.1.3 docker 配置

cat <<EOF | tee /etc/docker/daemon.json
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

systemctl restart docker

2.1.4 安装 CNI 插件(大多数 Pod 网络都需要)

CNI_VERSION="v0.8.6"
ARCH="amd64"
sudo mkdir -p /opt/cni/bin
curl -L "https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz" | sudo tar -C /opt/cni/bin -xz

2.1.5 安装 crictl(kubeadm/kubelet 容器运行时接口(CRI)所需)

CRICTL_VERSION="v1.22.0"
ARCH="amd64"
wget -c "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-${ARCH}.tar.gz"

2.1.6 安装 kubeadm、kubelet、kubectl 并添加 kubelet 系统服务

# https://www.downloadkubernetes.com/
DOWNLOAD_DIR=/usr/local/k8s
# RELEASE="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
RELEASE="v1.23.1"
mkdir -p /usr/local/k8s/bin
cd /usr/local/k8s/bin
wget -c https://storage.googleapis.com/kubernetes-release/release/${RELEASE}/bin/linux/${ARCH}/{kubeadm,kubelet,kubectl}

RELEASE_VERSION="v0.4.0"
wget -c "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/kubepkg/templates/latest/deb/kubelet/lib/systemd/system/kubelet.service"
sed "s:/usr/bin:${DOWNLOAD_DIR}:g" kubelet.service | tee /usr/lib/systemd/system/kubelet.service

mkdir -p /usr/lib/systemd/system/kubelet.service.d
wget -c "https://raw.githubusercontent.com/kubernetes/release/${RELEASE_VERSION}/cmd/kubepkg/templates/latest/deb/kubeadm/10-kubeadm.conf"
sed "s:/usr/bin:${DOWNLOAD_DIR}:g" 10-kubeadm.conf | tee /usr/lib/systemd/system/kubelet.service.d/10-kubeadm.conf

systemctl enable kubelet

2.1.7 拉取镜像

ALIYUN_REGISTRY="registry.cn-hangzhou.aliyuncs.com/google_containers"
for imageName in `kubeadm config images list`; do \ 
    imageName2=${imageName##*/}; \
    docker pull $ALIYUN_REGISTRY/${imageName2}; \
    docker tag $ALIYUN_REGISTRY/${imageName2} ${imageName}; \
    docker rmi $ALIYUN_REGISTRY/${imageName2}; \
done

2.1.8 修改主机映射(根据自己的ip设置)

vim /etc/hosts
192.168.137.3 localhost.localdomain localhost.localdomain

2.2 Minibute 安装部署

2.2.1 下载 Minibute

curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
mv minikube-linux-amd64 /usr/local/bin/minikube
chmod +x /usr/local/bin/minikube

2.2.2 启动 Minibute

# 由于 Minibute无法使用root启动, 需要使用其他帐号
adduser iawen
passwd iawen

# 在 Centos 下, 新建的帐号没有sudo 权限, 需要更改 `/etc/sudoers` 文件
chmod u+w /etc/sudoers
vim /etc/sudoers
root    ALL=(ALL)       ALL
iawen   ALL=(ALL)       ALL

chmod u-w /etc/sudoers

su iawen
sudo groupadd docker
sudo usermod -aG docker $USER #将您的用户添加到该docker组
newgrp docker #在Linux上, 运行以下命令来激活对组的更改

# 由于原生的kicbase docker 镜像(gcr.io/k8s-minikube/kicbase:v0.0.18)无法下载, 使用`anjone/kicbase` 替代
docker pull anjone/kicbase
minikube start --vm-driver=docker --base-image="anjone/kicbase" --image-mirror-country='cn' --image-repository='registry.cn-hangzhou.aliyuncs.com/google_containers'

minikube status

# 如果没有下载 kubectl,  可以使用别名, 保持命令一致
# alias kubectl="minikube kubectl --"

2.2.3 启动 Minibute dashboard

kubectl proxy --port=8001 --address='192.168.137.3' --accept-hosts='^.*' & 
minikube dashboard

2.3 使用 kubeadm 安装部署

2.3.1 初始化服务(根据自己的ip和网段和版本来设置)

kubeadm init --apiserver-advertise-address=192.168.137.3 --pod-network-cidr=192.168.0.0/16 --kubernetes-version=v1.23.1 | tee kubeadmin-init.log

# 安装flannel
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# 环境变量
vim /etc/profile
export KUBECONFIG=/etc/kubernetes/admin.conf

2.3.2 验证

kubectl get nodes
kubectl get nodes -o yaml

# 默认情况下, 出于安全原因, 你的集群不会在控制平面节点上调度 Pod。 如果你希望能够在控制平面节点上调度 Pod,  例如用于开发的单机 Kubernetes 集群, 请运行:  
kubectl taint nodes --all node-role.kubernetes.io/master-

# 禁止master部署pod
kubectl taint nodes k8s node-role.kubernetes.io/master=true:NoSchedule

2.3.3 加入节点

节点是你的工作负载(容器和 Pod 等)运行的地方。要将新节点添加到集群, 请对每台计算机执行以下操作:

  • SSH 到机器
  • 成为 root (例如 sudo su -)
  • 运行 kubeadm init 输出的命令
# <control-plane-host>:<control-plane-port>
kubectl cluster-info

# token
kubeadm token list # OR
kubeadm token create

# discovery-token-ca-cert-hash:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
   openssl dgst -sha256 -hex | sed 's/^.* //'

kubeadm join --token <token> <control-plane-host>:<control-plane-port> --discovery-token-ca-cert-hash sha256:<hash>

2.3.4 dashboard安装

kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml

kubectl get pods -n kubernetes-dashboard
kubectl get svc -n kubernetes-dashboard
kubectl patch svc kubernetes-dashboard -p '{"spec":{"type":"NodePort"}}' -n kubernetes-dashboard

# kubectl proxy --address='0.0.0.0'  --accept-hosts='^*$'
# 一般情况下,登陆的token默认都以secret对象的形式存在kube-system名称空间下,我们执行
# 这个是普通用户
kubectl get secret -n=kube-system | grep 'default-token'
kubectl describe secret -n=kube-system default-token-l6m2b

# 生成管理用户Token
# 创建一个dashboard管理用户
kubectl create serviceaccount dashboard -n kubernetes-dashboard 
# 绑定用户为集群管理用户
kubectl create rolebinding def-ns-admin --clusterrole=admin --serviceaccount=default:def-ns-admin
kubectl create clusterrolebinding dashboard-cluster-admin --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard

kubectl describe sa dashboard -n kubernetes-dashboard
kubectl describe secret <xxxxxxx> -n kubernetes-dashboard 

kubectl get pods -o wide

3. Kubernetes 常用操作

3.1 kubectl 命令

# 即刻从镜像创建部署,  很多选项跟 docker run 相同 --expose=true 自动创建一个关联的服务,需要同时指定 --port
kubectl run nginx2 --image=nginx --image-pull-policy='Never' --port=80 --expose=true

# 更新特定的键值
kubectl patch svc kubernetes-dashboard -p '{"spec":{"type":"NodePort"}}' -n kubernetes-dashboard

kubectl exec -it nginx -- bash

# 查看命名空间的角色列表
kubectl get roles

kubectl get clusterroles

kubectl describe clusterroles/cluster-admin -n=kube-system
Name:         cluster-admin
Labels:       kubernetes.io/bootstrapping=rbac-defaults
Annotations:  rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources  Non-Resource URLs  Resource Names  Verbs
  ---------  -----------------  --------------  -----
  *.*        []                 []              [*]
             [*]                []              [*]

kubectl logs

kubectl cluster-info dump

3.2 Ingress

Ingress 公开了从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:
1

为了让 Ingress 资源工作, 集群必须有一个正在运行的 Ingress 控制器。与作为 kube-controller-manager 可执行文件的一部分运行的其他类型的控制器不同, Ingress 控制器不是随集群自动启动的。 基于此页面, 你可选择最适合你的集群的 ingress 控制器实现。Kubernetes 作为一个项目, 目前支持和维护 AWS, GCE 和 nginx Ingress 控制器。参考: https://kubernetes.io/zh/docs/concepts/services-networking/ingress-controllers/

docker pull image: k8s.gcr.io/ingress-nginx/controller:v1.1.1

docker pull registry.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1
docker tag registry.aliyuncs.com/google_containers/kube-webhook-certgen:v1.1.1 c41e9fcadf5a k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml

可以将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS, 以及提供基于名称的虚拟主机等能力。 Ingress 控制器 通常负责通过负载均衡器来实现 Ingress, 尽管它也可以配置边缘路由器或其他前端来帮助处理流量。

Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时, 通常使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的服务。

3.3 Jobs

Job 会创建一个或者多个 Pods, 并将继续重试 Pods 的执行, 直到指定数量的 Pods 成功终止。 随着 Pods 成功结束, Job 跟踪记录成功完成的 Pods 个数。 当数量达到指定的成功个数阈值时, 任务(即 Job)结束。 删除 Job 的操作会清除所创建的全部 Pods。 挂起 Job 的操作会删除 Job 的所有活跃 Pod, 直到 Job 被再次恢复执行。

一种简单的使用场景下, 你会创建一个 Job 对象以便以一种可靠的方式运行某 Pod 直到完成。 当第一个 Pod 失败或者被删除(比如因为节点硬件失效或者重启)时, Job 对象会启动一个新的 Pod。

你也可以使用 Job 以并行的方式运行多个 Pod。

适合以 Job 形式来运行的任务主要有三种:

  • 非并行 Job
  • 具有 确定完成计数 的并行 Job
  • 带 工作队列 的并行 Job

3.4 Cron Job

CronJob 用于执行周期性的动作, 例如备份、报告生成等。 这些任务中的每一个都应该配置为周期性重复的(例如: 每天/每周/每月一次); 你可以定义任务开始执行的时间间隔.

CronJob 根据其计划编排, 在每次该执行任务的时候大约会创建一个 Job。 我们之所以说 “大约”, 是因为在某些情况下, 可能会创建两个 Job, 或者不会创建任何 Job。 我们试图使这些情况尽量少发生, 但不能完全杜绝。因此, Job 应该是 幂等的。

如果 startingDeadlineSeconds 设置为很大的数值或未设置(默认), 并且 concurrencyPolicy 设置为 Allow, 则作业将始终至少运行一次。

apiVersion: batch/v1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            imagePullPolicy: IfNotPresent
            command:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

4 常见问题

4.1 镜像拉取

k8s默认会从远端拉取镜像, 其配置参数imagePullPolicy为Always。所以, 如果yaml文件中没有定义那就是使用默认的, 因此我们可以通过将该参数显示设置为Never或者IfNotPresent, k8s就会从本地拉取镜像了: imagePullPolicy: Never

4.2 k8s的容器的端口暴露

4.2.1 使用主机网络

kind: Deployment
spec.template.spec:
  hostNetwork: true

4.2.2 使用hostPort参数

containerPort: 80     --容器端口
hostPort: 80     --暴露端口
protocol: TCP

4.2.3 使用node节点的IP加端口可以访问Pod服务

注: master节点IP不可以访问。端口范围30000-32767。

spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      nodePort: 31000
  selector:     --后端Pod标签
    app: web