一、 YAML 文件

它的基本语法规则如下:

大小写敏感
使用缩进表示层级关系
缩进时不允许使用Tab键,只允许使用空格。
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
# 表示注释,从这个字符一直到行尾,都会被解析器忽略。

在我们的 kubernetes 中,你只需要两种结构类型就行了:

Lists
Maps

1 Maps

Map 是字典,就是一个key:value的键值对,Maps 可以让我们更加方便的去书写配置信息,例如:

---
apiVersion: v1
kind: Pod

第一行的—是分隔符,是可选的,在单一文件中,可用连续三个连字号—区分多个文件。这里我们可以看到,我们有两个键:kind 和 apiVersion,他们对应的值分别是:v1 和Pod。
上面的 YAML 文件转换成 JSON 格式的话:

{
    "apiVersion": "v1",
    "kind": "pod"
}

在创建一个相对复杂一点的 YAML 文件,创建一个 KEY 对应的值不是字符串而是一个 Maps:

---
apiVersion: v1
kind: Pod
metadata:
  name: kube100-site
  labels:
    app: web

上面的 YAML 文件,metadata 这个 KEY 对应的值就是一个 Maps 了,而且嵌套的 labels 这个 KEY 的值又是一个 Map,你可以根据你自己的情况进行多层嵌套。

我们可以看到 name 和 labels 是相同级别的缩进,所以 YAML 处理器就知道了他们属于同一个 MAP,而 app 是 labels 的值是因为 app 的缩进更大。

可以将上面的 YAML 文件转换成 JSON 文件:

{
  "apiVersion": "v1",
  "kind": "Pod",
  "metadata": {
    "name": "kube100-site",
    "labels": {
      "app": "web"
    }
  }
}

2Lists

Lists 就是列表,说白了就是数组,在 YAML 文件中我们可以这样定义:

args
  - Cat
  - Dog
  - Fish

你可以有任何数量的项在列表中,每个项的定义以破折号(-)开头的,与父元素直接可以缩进一个空格。对应的 JSON 格式如下:

{
    "args": ["Cat", "Dog", "Fish"]
}

当然,list 的子项也可以是 Maps,Maps 的子项也可以是list如下所示:

---
apiVersion: v1
kind: Pod
metadata:
  name: kube100-site
  labels:
    app: web
spec:
  containers:
    - name: front-end
      image: nginx
      ports:
        - containerPort: 80
    - name: flaskapp-demo
      image: jcdemo/flaskapp
      ports:
        - containerPort: 5000

这个 YAML 文件,我们定义了一个叫 containers 的 List 对象,每个子项都由 name、image、ports 组成,每个 ports 都有一个 key 为 containerPort 的 Map 组成,同样的,我们可以转成如下 JSON 格式文件:

{
    "apiVersion": "v1",
    "kind": "Pod",
    "metadata": {
        "name": "kube100-site",
        "labels": {
            "app": "web"
        }
    },
    "spec": {
        "containers": [{
            "name": "front-end",
            "image": "nginx",
            "ports": [{
                "containerPort": 80
            }]
        }, {
            "name": "flaskapp-demo",
            "image": "jcdemo/flaskapp",
            "ports": [{
                "containerPort": 5000
            }]
        }]
    }
}

3 使用 YAML 创建 Pod

创建 Pod

---
apiVersion: v1
kind: Pod
metadata:
  name: kube100-site
  labels:
    app: web
spec:
  containers:
    - name: front-end
      image: nginx
      ports:
        - containerPort: 80
    - name: flaskapp-demo
      image: jcdemo/flaskapp
      ports:
        - containerPort: 5000

我们先来简单分析下文件内容:

  • apiVersion,这里它的值是 v1,这个版本号需要根据我们安装的 kubernetes 版本和资源类型进行变化的,记住不是写死的
  • kind,这里我们创建的是一个 Pod,当然根据你的实际情况,这里资源类型可以是 Deployment、Job、Ingress、Service 等待。
  • metadata:包含了我们定义的 Pod 的一些 meta 信息,比如名称、namespace、标签等等信息。
  • spec:包括一些 containers,storage,volumes,或者其他 Kubernetes 需要知道的参数,以及诸如是否在容器失败时重新启动容器的属性。你可以在特定 Kubernetes API 找到完整的 Kubernetes Pod 的属性。

典型的容器的定义:

…spec:
  containers:
    - name: front-end
      image: nginx
      ports:
        - containerPort: 80

在这个例子中,这是一个简单的最小定义:一个名字(front-end),基于 nginx 的镜像,以及容器 将会监听的一个端口(80)。在这些当中,只有名字是非常需要的,你也可以指定一个更加复杂的属性,例如在容器启动时运行的命令,应使用的参数,工作目录,或每次实例化时是否拉取映像的新副本。

以下是一些容器可选的设置属性:

name
image
command
args
workingDir
ports
env
resources
volumeMounts
livenessProbe
readinessProbe
livecycle
terminationMessagePath
imagePullPolicy
securityContext
stdin
stdinOnce
tty

我们将上面创建 POD 的 YAML 文件保存成 pod.yaml,然后使用kubectl创建 POD

$ kubectl create -f pod.yaml
pod "kube100-site" created

kubectl 命令来查看 POD 的状态了:

$ kubectl get pods
NAME           READY     STATUS    RESTARTS   AGE
kube100-site   2/2       Running   0          1m

先删除上面创建的 POD

$ kubectl delete -f pod.yaml
pod "kube100-site" deleted

4 创建 Deployment

在上面的例子中,我们只是单纯的创建了一个 POD 实例,但是如果这个 POD 出现了故障的话,我们的服务也就挂掉了,所以 kubernetes 提供了一个Deployment的概念,可以让 kubernetes 去管理一组 POD 的副本,也就是副本集,这样就可以保证一定数量的副本一直可用的,不会因为一个 POD 挂掉导致整个服务挂掉。我们可以这样定义一个 Deployment:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube100-site
spec:
  replicas: 2

注意这里的apiVersion对应的值是apps/v1,当然 kind 要指定为 Deployment,因为这就是我们需要的,然后我们可以指定一些 meta 信息,比如名字,或者标签之类的。最后,最重要的是spec配置选项,这里我们定义需要两个副本。

当然还有很多可以设置的属性,比如一个 Pod 在没有任何错误变成准备的情况下必须达到的最小秒数。 我们可以在Kubernetes v1beta1 API参考中找到一个完整的 Depolyment 可指定的参数列表。 现在我们来定义一个完整的 Deployment 的 YAML 文件:

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kube100-site
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - name: front-end
          image: nginx
          ports:
            - containerPort: 80
        - name: flaskapp-demo
          image: jcdemo/flaskapp
          ports:
            - containerPort: 5000

看起来是不是和我们上面的 pod.yaml 很类似啊,注意其中的 template,其实就是对 POD 对象的定义。将上面的 YAML 文件保存为 deployment.yaml,然后创建 Deployment:

$ kubectl create -f deployment.yaml
deployment "kube100-site" created

想要查看它的状态,我们可以检查 Deployment的列表:

$ kubectl get deployments
NAME           DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
kube100-site   2         2         2            2           2m

我们可以看到所有的 Pods 都已经正常运行了。

二、用 kubeadm 搭建集群环境

官方文档:Creating a cluster with kubeadm

k8s的架构图
在这里插入图片描述

在这里插入图片描述

核心层Nucleus Layer:

  • Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境

应用层Application Layer:

  • 部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析等)

管理层Governance Layer:

  • 系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)

接口层Interface Layer:

  • kubectl 命令行工具、客户端 SDK 以及集群联邦

生态系统Ecosystem Layer:

  • 在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
    Kubernetes 外部:日志、监控、配置管理、CI、CD、Workflow等
    Kubernetes 内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等

这里使用的是kubeadm工具来进行集群的搭建。

kubeadm是Kubernetes官方提供的用于快速安装Kubernetes集群的工具,通过将集群的各个组件进行容器化安装管理,通过kubeadm的方式安装集群比二进制的方式安装要方便不少。

1 环境

Master节点和Node节点都需要配置

我们这里准备两台Centos7的主机用于安装,后续节点可以同样需要配置:

$ cat /etc/hosts
10.151.30.57 master
10.151.30.62 node01

禁用防火墙:

$ systemctl stop firewalld
$ systemctl disable firewalld

禁用SELINUX:

$ setenforce 0
$ cat /etc/selinux/config
SELINUX=disabled

创建/etc/sysctl.d/k8s.conf文件,添加如下内容:

net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1

执行如下命令使修改生效:

$ modprobe br_netfilter
$ sysctl -p /etc/sysctl.d/k8s.conf

2 镜像(如果你的节点上面有科学上网的工具,可以忽略这一步)

将所需的gcr.io上面的镜像下载到节点上面。master节点,执行下面的命令:

docker pull cnych/kube-apiserver-amd64:v1.10.0
docker pull cnych/kube-scheduler-amd64:v1.10.0
docker pull cnych/kube-controller-manager-amd64:v1.10.0
docker pull cnych/kube-proxy-amd64:v1.10.0
docker pull cnych/k8s-dns-kube-dns-amd64:1.14.8
docker pull cnych/k8s-dns-dnsmasq-nanny-amd64:1.14.8
docker pull cnych/k8s-dns-sidecar-amd64:1.14.8
docker pull cnych/etcd-amd64:3.1.12
docker pull cnych/flannel:v0.10.0-amd64
docker pull cnych/pause-amd64:3.1

docker tag cnych/kube-apiserver-amd64:v1.10.0 k8s.gcr.io/kube-apiserver-amd64:v1.10.0
docker tag cnych/kube-scheduler-amd64:v1.10.0 k8s.gcr.io/kube-scheduler-amd64:v1.10.0
docker tag cnych/kube-controller-manager-amd64:v1.10.0 k8s.gcr.io/kube-controller-manager-amd64:v1.10.0
docker tag cnych/kube-proxy-amd64:v1.10.0 k8s.gcr.io/kube-proxy-amd64:v1.10.0
docker tag cnych/k8s-dns-kube-dns-amd64:1.14.8 k8s.gcr.io/k8s-dns-kube-dns-amd64:1.14.8
docker tag cnych/k8s-dns-dnsmasq-nanny-amd64:1.14.8 k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64:1.14.8
docker tag cnych/k8s-dns-sidecar-amd64:1.14.8 k8s.gcr.io/k8s-dns-sidecar-amd64:1.14.8
docker tag cnych/etcd-amd64:3.1.12 k8s.gcr.io/etcd-amd64:3.1.12
docker tag cnych/flannel:v0.10.0-amd64 quay.io/coreos/flannel:v0.10.0-amd64
docker tag cnych/pause-amd64:3.1 k8s.gcr.io/pause-amd64:3.1

其他Node,执行下面的命令:

docker pull cnych/kube-proxy-amd64:v1.10.0
docker pull cnych/flannel:v0.10.0-amd64
docker pull cnych/pause-amd64:3.1
docker pull cnych/kubernetes-dashboard-amd64:v1.8.3
docker pull cnych/heapster-influxdb-amd64:v1.3.3
docker pull cnych/heapster-grafana-amd64:v4.4.3
docker pull cnych/heapster-amd64:v1.4.2
docker pull cnych/k8s-dns-kube-dns-amd64:1.14.8
docker pull cnych/k8s-dns-dnsmasq-nanny-amd64:1.14.8
docker pull cnych/k8s-dns-sidecar-amd64:1.14.8

docker tag cnych/flannel:v0.10.0-amd64 quay.io/coreos/flannel:v0.10.0-amd64
docker tag cnych/pause-amd64:3.1 k8s.gcr.io/pause-amd64:3.1
docker tag cnych/kube-proxy-amd64:v1.10.0 k8s.gcr.io/kube-proxy-amd64:v1.10.0

docker tag cnych/k8s-dns-kube-dns-amd64:1.14.8 k8s.gcr.io/k8s-dns-kube-dns-amd64:1.14.8
docker tag cnych/k8s-dns-dnsmasq-nanny-amd64:1.14.8 k8s.gcr.io/k8s-dns-dnsmasq-nanny-amd64:1.14.8
docker tag cnych/k8s-dns-sidecar-amd64:1.14.8 k8s.gcr.io/k8s-dns-sidecar-amd64:1.14.8

docker tag cnych/kubernetes-dashboard-amd64:v1.8.3 k8s.gcr.io/kubernetes-dashboard-amd64:v1.8.3
docker tag cnych/heapster-influxdb-amd64:v1.3.3 k8s.gcr.io/heapster-influxdb-amd64:v1.3.3
docker tag cnych/heapster-grafana-amd64:v4.4.3 k8s.gcr.io/heapster-grafana-amd64:v4.4.3
docker tag cnych/heapster-amd64:v1.4.2 k8s.gcr.io/heapster-amd64:v1.4.2

3安装 kubeadm、kubelet、kubectl

Master和Node都需要执行(master节点同样需要安装kubelet,因为Pod是跑在kubelet上的)

我们这里是通过指定yum 源的方式来进行安装的:

另外还需要注意的是当前版本的 kubeadm 支持的docker版本最大是 17.03,如果需要docker版本比较高,建议安装高版本的kubeadm

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
        https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

如果不能科学上网的话,我们可以使用阿里云的源进行安装:

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
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
        http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF

注意:由于阿里云的源将依赖进行了更改,如果你需要安装 1.10.0 版本的集群的话,需要使用下面的命令分别安装:

$ yum makecache fast 
$ yum install -y kubelet-1.10.0-0
$ yum install -y kubectl-1.10.0-0
$ yum install -y kubeadm-1.10.0-0

4配置 kubelet

master和slave节点都需要安装;

安装完成后,我们还需要对kubelet进行配置,因为用yum源的方式安装的kubelet生成的配置文件将参数–cgroup-driver改成了systemd,而 docker 的cgroup-driver是cgroupfs,这二者必须一致才行,我们可以通过docker info命令查看:

$ docker info |grep Cgroup
Cgroup Driver: cgroupfs

修改文件 kubelet 的配置文件/etc/systemd/system/kubelet.service.d/10-kubeadm.conf,将其中的KUBELET_CGROUP_ARGS参数更改成cgroupfs:

Environment="KUBELET_CGROUP_ARGS=--cgroup-driver=cgroupfs"

Kubernetes 从1.8开始要求关闭系统的 Swap ,如果不关闭,默认配置的 kubelet 将无法启动,我们可以通过 kubelet 的启动参数–fail-swap-on=false更改这个限制,所以我们需要在上面的配置文件中增加一项配置(在ExecStart之前):

Environment="KUBELET_EXTRA_ARGS=--fail-swap-on=false"

当然最好的还是将 swap 给关掉,这样能提高 kubelet 的性能。修改完成后,重新加载我们的配置文件即可:

$ systemctl daemon-reload

5集群安装初始化

准备工作就完成了;

在master节点上用kubeadm命令来初始化我们的集群了:

$ kubeadm init --kubernetes-version=v1.10.0 --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=10.151.30.57

如果无法执行,就增加sudo

命令非常简单,就是kubeadm init,后面的参数是需要安装的集群版本,因为我们这里选择flannel作为 Pod 的网络插件,所以需要指定–pod-network-cidr=10.244.0.0/16,然后是 apiserver 的通信地址,这里就是我们 master 节点的 IP 地址。

执行上面的命令,如果出现running with swap on is not supported. Please disable swap之类的错误,则我们还需要增加一个参数–ignore-preflight-errors=Swap来忽略 swap 的错误提示信息。

$ kubeadm init \
   --kubernetes-version=v1.10.0 \
   --pod-network-cidr=10.244.0.0/16 \
   --apiserver-advertise-address=10.151.30.57 \
   --ignore-preflight-errors=Swap
[init] Using Kubernetes version: v1.10.0
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks.
    [WARNING FileExisting-crictl]: crictl not found in system pathSuggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl
[preflight] Starting the kubelet service
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [ydzs-master1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 10.151.30.57]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated etcd/ca certificate and key.
[certificates] Generated etcd/server certificate and key.
[certificates] etcd/server serving cert is signed for DNS names [localhost] and IPs [127.0.0.1]
[certificates] Generated etcd/peer certificate and key.
[certificates] etcd/peer serving cert is signed for DNS names [ydzs-master1] and IPs [10.151.30.57]
[certificates] Generated etcd/healthcheck-client certificate and key.
[certificates] Generated apiserver-etcd-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[controlplane] Wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] Wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] Wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests".
[init] This might take a minute or longer if the control plane images have to be pulled.
[apiclient] All control plane components are healthy after 22.007661 seconds
[uploadconfig] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[markmaster] Will mark node ydzs-master1 as master by adding a label and a taint
[markmaster] Master ydzs-master1 tainted and labelled with key/value: node-role.kubernetes.io/master=""
[bootstraptoken] Using token: 8xomlq.0cdf2pbvjs2gjho3
[bootstraptoken] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: kube-dns
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:


  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 10.151.30.57:6443 --token 8xomlq.0cdf2pbvjs2gjho3 --discovery-token-ca-cert-hash sha256:92802317cb393682c1d1356c15e8b4ec8af2b8e5143ffd04d8be4eafb5fae368

要注意将上面的加入集群的命令保存下面,如果忘记保存上面的 token 和 sha256 值的话也不用担心,我们可以使用下面的命令来查找:

$ kubeadm token list
kubeadm token list
TOKEN                     TTL       EXPIRES                     USAGES                   DESCRIPTION                                                EXTRA GROUPS
i5gbaw.os1iow5tdo17rwdu   23h       2018-05-18T01:32:55+08:00   authentication,signing   The default bootstrap token generated by 'kubeadm init'.   system:bootstrappers:kubeadm:default-node-token

要查看 CA 证书的 sha256 的值的话,我们可以使用openssl来读取证书获取 sha256 的值:

$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
e9ca4d9550e698105f1d8fae7ecfd297dd9331ca7d50b5493fa0491b2b4df40c

另外还需要注意的是当前版本的 kubeadm 支持的docker版本最大是 17.03,所以要注意下。 上面的信息记录了 kubeadm 初始化整个集群的过程,生成相关的各种证书、kubeconfig 文件、bootstraptoken 等等,后边是使用kubeadm join往集群中添加节点时用到的命令

下面的命令是配置如何使用kubectl访问集群的方式(以普通用户的方式使用kubectl,$HOME/.kube/config是kubectl默认使用的配置文件):

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

将节点加入集群的命令:

kubeadm join 10.151.30.57:6443 --token 8xomlq.0cdf2pbvjs2gjho3 --discovery-token-ca-cert-hash sha256:92802317cb393682c1d1356c15e8b4ec8af2b8e5143ffd04d8be4eafb5fae368

我们根据上面的提示配置好 kubectl 后,就可以使用 kubectl 来查看集群的信息了:

$ kubectl get cs
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok
controller-manager   Healthy   ok
etcd-0               Healthy   {"health": "true"}
$ kubectl get csr
NAME                                                   AGE       REQUESTOR                 CONDITION
node-csr-8qygb8Hjxj-byhbRHawropk81LHNPqZCTePeWoZs3-g   1h        system:bootstrap:8xomlq   Approved,Issued
$ kubectl get nodes
NAME           STATUS    ROLES     AGE       VERSION
ydzs-master1   Ready     master    3h        v1.10.0

如果你的集群安装过程中遇到了其他问题,我们可以使用下面的命令来进行重置:

$ kubeadm reset
$ ifconfig cni0 down && ip link delete cni0
$ ifconfig flannel.1 down && ip link delete flannel.1
$ rm -rf /var/lib/cni/

6安装 Pod Network

在master节点上操作

接下来我们来安装flannel网络插件:

$ wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
$ kubectl apply -f  kube-flannel.yml
clusterrole.rbac.authorization.k8s.io "flannel" created
clusterrolebinding.rbac.authorization.k8s.io "flannel" created
serviceaccount "flannel" created
configmap "kube-flannel-cfg" created
daemonset.extensions "kube-flannel-ds" created

另外需要注意的是如果你的节点有多个网卡的话,需要在 kube-flannel.yml 中使用–iface参数指定集群主机内网网卡的名称,否则可能会出现 dns 无法解析。flanneld 启动参数加上–iface=<iface-name>

args:
- --ip-masq
- --kube-subnet-mgr
- --iface=eth0

安装完成后使用 kubectl get pods 命令可以查看到我们集群中的组件运行状态,如果都是Running 状态的话,那么恭喜你,你的 master 节点安装成功了。

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                   READY     STATUS    RESTARTS   AGE
kube-system   etcd-ydzs-master1                      1/1       Running   0          10m
kube-system   kube-apiserver-ydzs-master1            1/1       Running   0          10m
kube-system   kube-controller-manager-ydzs-master1   1/1       Running   0          10m
kube-system   kube-dns-86f4d74b45-f5595              3/3       Running   0          10m
kube-system   kube-flannel-ds-qxjs2                  1/1       Running   0          1m
kube-system   kube-proxy-vf5fg                       1/1       Running   0          10m
kube-system   kube-scheduler-ydzs-master1            1/1       Running   0          10m

查询集群中node

  • 默认情况下,普通的node不会被调度到master的node上的
➜  Updater kubectl get nodes

7添加节点

在node上操作

同样的上面的环境配置、docker 安装、kubeadm、kubelet、kubectl 这些都在Node(10.151.30.62)节点安装配置好过后;
我们就可以直接在 Node 节点上执行kubeadm join命令了(上面初始化的时候有),同样加上参数–ignore-preflight-errors=Swap:

$ kubeadm join 10.151.30.57:6443 --token 8xomlq.0cdf2pbvjs2gjho3 --discovery-token-ca-cert-hash sha256:92802317cb393682c1d1356c15e8b4ec8af2b8e5143ffd04d8be4eafb5fae368 --ignore-preflight-errors=Swap
[preflight] Running pre-flight checks.
    [WARNING Swap]: running with swap on is not supported. Please disable swap
    [WARNING FileExisting-crictl]: crictl not found in system path
Suggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl
[discovery] Trying to connect to API Server "10.151.30.57:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://10.151.30.57:6443"
[discovery] Requesting info from "https://10.151.30.57:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "10.151.30.57:6443"
[discovery] Successfully established connection with API Server "10.151.30.57:6443"

This node has joined the cluster:
* Certificate signing request was sent to master and a response
  was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

在这里插入图片描述

如果出现下面的错误信息:[discovery] Failed to request cluster info, will try again: [Get https://10.151.30.27:6443/api/v1/namespaces/kube-public/configmaps/cluster-info: x509: certificate has expired or is not yet valid],应该是 master 和 node 之间时间不同步造成的,执行 ntp 时间同步后,重新 init、join 即可。

如果 join 的时候出现下面的错误信息:[ERROR CRI]: unable to check if the container runtime at “/var/run/dockershim.sock” is running: fork/exec /usr/bin/crictl -r /var/run/dockershim.sock info: no such file or directory,这个是因为 cri-tools 版本造成的错误,可以卸载掉即可:yum remove cri-tools。

我们可以看到该节点已经加入到集群中去了,然后我们把 master 节点的~/.kube/config文件拷贝到当前节点对应的位置即可使用 kubectl 命令行工具了。

$ kubectl get nodes
NAME           STATUS    ROLES     AGE       VERSION
evjfaxic       Ready     <none>    1h        v1.10.0
ydzs-master1   Ready     master    3h        v1.10.0

查看所有节点证书

➜  Updater kubectl get csr

查看在所有节点上运行的pod

➜  Updater kubectl get pods -A -o wide

三、静态pod

K3S不支持。

创建静态 Pod 有两种方式:配置文件和 HTTP 两种方式

静态 Pod 直接由特定节点上的kubelet进程来管理,不通过 master 节点上的apiserver。无法与我们常用的控制器Deployment或者DaemonSet进行关联,它由kubelet进程自己来监控,当pod崩溃时重启该pod,kubelete也无法对他们进行健康检查。

静态 pod 始终绑定在某一个kubelet,并且始终运行在同一个节点上。 kubelet会自动为每一个静态 pod 在 Kubernetes 的 apiserver 上创建一个镜像 Pod(Mirror Pod),因此我们可以在 apiserver 中查询到该 pod,但是不能通过 apiserver 进行控制(例如不能删除)。

1 配置文件

配置文件就是放在特定目录下的标准的 JSON 或 YAML 格式的 pod 定义文件。用kubelet --pod-manifest-path=来启动kubelet进程,kubelet 定期的去扫描这个目录,根据这个目录下出现或消失的 YAML/JSON 文件来创建或删除静态 pod。

比如我们在 node01 这个节点上用静态 pod 的方式来启动一个 nginx 的服务。我们登录到node01节点上面,可以通过下面命令找到kubelet对应的启动配置文件

$ systemctl status kubelet

配置文件路径为:

$ /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

打开这个文件我们可以看到其中有一条如下的环境变量配置:

Environment="KUBELET_SYSTEM_PODS_ARGS=--pod-manifest-path=/etc/kubernetes/manifests --allow-privileged=true"

所以如果我们通过kubeadm的方式来安装的集群环境(master节点),对应的kubelet已经配置了我们的静态 Pod 文件的路径,那就是/etc/kubernetes/manifests,所以我们只需要在该目录下面创建一个标准的 Pod 的 JSON 或者 YAML 文件即可;
如果你的 kubelet 启动参数中没有配置上面的–pod-manifest-path参数的话,那么添加上这个参数然后重启 kubelet 即可。

[root@ node01 ~] $ cat <<EOF >/etc/kubernetes/manifest/static-web.yaml
apiVersion: v1
kind: Pod
metadata:
  name: static-web
  labels:
    app: static
spec:
  containers:
    - name: web
      image: nginx
      ports:
        - name: web
          containerPort: 80
EOF

master节点:
在这里插入图片描述

node节点:
在这里插入图片描述

2 通过 HTTP 创建静态 Pods

kubelet 周期地从–manifest-url=参数指定的地址下载文件,并且把它翻译成 JSON/YAML 格式的 pod 定义。此后的操作方式与–pod-manifest-path=相同,kubelet 会不时地重新下载该文件,当文件变化时对应地终止或启动静态 pod。

3 静态pods的动作行为

kubelet 启动时,由–pod-manifest-path= or --manifest-url=参数指定的目录下定义的所有 pod 都会自动创建,例如,我们示例中的 static-web。

[root@node01 ~] $ docker ps
CONTAINER ID IMAGE         COMMAND  CREATED        STATUS         PORTS     NAMES
f6d05272b57e nginx:latest  "nginx"  8 minutes ago  Up 8 minutes             k8s_web.6f802af4_static-web-fk-node1_default_67e24ed9466ba55986d120c867395f3c_378e5f3c

现在我们通过kubectl工具可以看到这里创建了一个新的镜像 Pod:

[root@node01 ~] $ kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
static-web-my-node01        1/1       Running   0          2m

静态 pod 的标签会传递给镜像 Pod,可以用来过滤或筛选。 需要注意的是,我们不能通过 API 服务器来删除静态 pod(例如,通过kubectl命令),kebelet 不会删除它。

[root@node01 ~] $ kubectl delete pod static-web-my-node01
[root@node01 ~] $ kubectl get pods
NAME                       READY     STATUS    RESTARTS   AGE
static-web-my-node01        1/1       Running   0          12s

我们尝试手动终止容器,可以看到kubelet很快就会自动重启容器。

[root@node01 ~] $ docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED       ...
5b920cbaf8b1        nginx:latest  "nginx -g 'daemon of   2 seconds ago ...

4 静态pods的动态增加和删除

运行中的kubelet周期扫描配置的目录(我们这个例子中就是/etc/kubernetes/manifests,master节点)下文件的变化,当这个目录中有文件出现或消失时创建或删除pods。

[root@node01 ~] $ mv /etc/kubernetes/manifests/static-web.yaml /tmp
[root@node01 ~] $ sleep 20
[root@node01 ~] $ docker ps
// no nginx container is running
[root@node01 ~] $ mv /tmp/static-web.yaml  /etc/kubernetes/manifests
[root@node01 ~] $ sleep 20
[root@node01 ~] $ docker ps
CONTAINER ID        IMAGE         COMMAND                CREATED           ...
e7a62e3427f1        nginx:latest  "nginx -g 'daemon of   27 seconds ago

其实我们用 kubeadm 安装的集群,master 节点上面的几个重要组件都是用静态 Pod 的方式运行的,我们登录到 master 节点上查看/etc/kubernetes/manifests目录:
[root@master ~]# ls /etc/kubernetes/manifests/
etcd.yaml kube-apiserver.yaml kube-controller-manager.yaml kube-scheduler.yaml
这些 Pod 都不会受到 apiserver 的控制.只能通过kubelet自己来进行控制,这就是我们所说的静态 Pod。

四、Pod Hook

所有资料在:kubernetes-learning

实际上 Kubernetes 为我们的容器提供了生命周期钩子的,就是我们说的Pod Hook,Pod Hook 是由 kubelet 发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的生命周期之中。我们可以同时为 Pod 中的所有容器都配置 hook。

Kubernetes 为我们提供了两种钩子函数:

  • PostStart:这个钩子在容器创建后立即执行。但是,并不能保证钩子将在容器ENTRYPOINT之前运行,因为没有参数传递给处理程序。主要用于资源部署、环境准备等。不过需要注意的是如果钩子花费太长时间以至于不能运行或者挂起, 容器将不能达到running状态。
  • PreStop:这个钩子在容器终止之前立即被调用。它是阻塞的,意味着它是同步的, 所以它必须在删除容器的调用发出之前完成。主要用于优雅关闭应用程序、通知其他系统等。如果钩子在执行期间挂起, Pod阶段将停留在running状态并且永不会达到failed状态。
    如果PostStart或者PreStop钩子失败, 它会杀死容器。所以我们应该让钩子函数尽可能的轻量。当然有些情况下,长时间运行命令是合理的, 比如在停止容器之前预先保存状态。

另外我们有两种方式来实现上面的钩子函数:

  • Exec - 用于执行一段特定的命令,不过要注意的是该命令消耗的资源会被计入容器。
  • HTTP - 对容器上的特定的端点执行HTTP请求。

1环境准备

以下示例中,定义了一个Nginx Pod,其中设置了PostStart钩子函数,即在容器创建成功后,写入一句话到/usr/share/message文件中。

---
apiVersion: v1
kind: Pod
metadata:
  name: hook-demo1
  labels:
    app: hook
spec:
  containers:
  - name: hook-demo1
    image: nginx
    ports:
    - name: webport
      containerPort: 80
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart Handler > /usr/share/message"]

创建

kubectl apply -f pod-hook1.yaml

查看pod的详细信息

kubectl describe pod hook-demo1

验证poststart hook,需要进入到pod里面

kubectl exec hook-demo1 -i -t /bin/bash

如果pod里面有多个container,需要指定-c参数,进入到特定的某个container里面

2优雅删除资源对象

当用户请求删除含有 pod 的资源对象时(如Deployment等),K8S 为了让应用程序优雅关闭(即让应用程序完成正在处理的请求后,再关闭软件),K8S提供两种信息通知:

  • 默认:K8S 通知 node 执行docker stop命令,docker 会先向容器中PID为1的进程发送系统信号SIGTERM,然后等待容器中的应用程序终止执行,如果等待时间达到设定的超时时间,或者默认超时时间(30s),会继续发送SIGKILL的系统信号强行 kill 掉进程。
  • 使用 pod 生命周期(利用PreStop回调函数),它执行在发送终止信号之前。

默认所有的优雅退出时间都在30秒内。kubectl delete 命令支持 --grace-period=选项,这个选项允许用户用他们自己指定的值覆盖默认值。

  • 值’0’代表 强制删除 pod.
  • 在 kubectl 1.5 及以上的版本里,执行强制删除时必须同时指定 --force --grace-period=0。
kubectl delete pod hook-demo1 --grace-period=0 --force

强制删除一个 pod 是从集群状态还有 etcd 里立刻删除这个 pod。

  • 当 Pod 被强制删除时, api 服务器不会等待来自 Pod 所在节点上的 kubelet 的确认信息:pod 已经被终止。
  • 在 API 里 pod 会被立刻删除,在节点上, pods 被设置成立刻终止后,在强行杀掉前还会有一个很小的宽限期。

以下示例中,定义了一个Nginx Pod,其中设置了PreStop钩子函数,即在容器退出之前,优雅的关闭 Nginx:

---
apiVersion: v1
kind: Pod
metadata:
  name: hook-demo2
  labels:
    app: hook
spec:
  containers:
  - name: hook-demo2
    image: nginx
    ports:
    - name: webport
      containerPort: 80
    volumeMounts:
    - name: message
      mountPath: /usr/share
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the preStop Handler > /usr/share/message"]
  volumes:
  - name: message
    hostPath:
      path: /tmp

运行:

kubectl apply -f pod-hook2.yaml

删除

kubectl delete pod  hook-demo2
或者
kubectl delete -f pod-hook2.yaml

普通的node不会被调度到master上面,所以可能需要到普通的node节点上去验证,看/tmp/message是否存在
在这里插入图片描述

五、Pod健康检查

PostStart与PreStop,其中PostStart是在容器创建后立即执行的,而PreStop这个钩子函数则是在容器终止之前执行的
除了上面这两个钩子函数以外,还有一项配置会影响到容器的生命周期的,那就是健康检查的探针。

在Kubernetes集群当中,我们可以通过配置liveness probe(存活探针)和readiness probe(可读性探针)来影响容器的生存周期。

kubelet 通过使用 liveness probe 来确定你的应用程序是否正在运行,通俗点将就是是否还活着。一般来说,如果你的程序一旦崩溃了, Kubernetes 就会立刻知道这个程序已经终止了,然后就会重启这个程序。而我们的 liveness probe 的目的就是来捕获到当前应用程序还没有终止,还没有崩溃,如果出现了这些情况,那么就重启处于该状态下的容器,使应用程序在存在 bug 的情况下依然能够继续运行下去。

kubelet 使用 readiness probe 来确定容器是否已经就绪可以接收流量过来了。这个探针通俗点讲就是说是否准备好了,现在可以开始工作了。只有当 Pod 中的容器都处于就绪状态的时候 kubelet 才会认定该 Pod 处于就绪状态,因为一个 Pod 下面可能会有多个容器。当然 Pod 如果处于非就绪状态,那么我们就会将他从我们的工作队列(实际上就是我们后面需要重点学习的 Service)中移除出来,这样我们的流量就不会被路由到这个 Pod 里面来了。

和前面的钩子函数一样的,我们这两个探针的支持两种配置方式:

  • exec:执行一段命令
  • http:检测某个 http 请求
  • tcpSocket:使用此配置, kubelet 将尝试在指定端口上打开容器的套接字。如果可以建立连接,容器被认为是健康的,如果不能就认为是失败的。实际上就是检查端口

1 livenessProbe:exec

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec
  labels:
    test: liveness
spec:
  containers:
  - name: liveness
    image: busybox
    args:
    - /bin/sh
    - -c
    - touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600
    livenessProbe:
      exec:
        command:
        - cat
        - /tmp/healthy
      initialDelaySeconds: 5
      periodSeconds: 5

运行:

kubectl apply -f libeness-exec.yaml

我们这里需要用到一个新的属性:livenessProbe,下面通过exec执行一段命令,其中periodSeconds属性表示让kubelet每隔5秒执行一次存活探针,也就是每5秒执行一次上面的cat /tmp/healthy命令,如果命令执行成功了,将返回0,那么kubelet就会认为当前这个容器是存活的并且很监控,如果返回的是非0值,那么kubelet就会把该容器杀掉然后重启它。

另外一个属性initialDelaySeconds表示在第一次执行探针的时候要等待5秒,这样能够确保我们的容器能够有足够的时间启动起来。

另外我们在容器启动的时候,执行了如下命令:

  • 意思是说在容器最开始的30秒内有一个/tmp/healthy文件,在这30秒内执行cat /tmp/healthy命令都会返回一个成功的返回码。30秒后,我们删除这个文件,现在执行cat /tmp/healthy是不是就会失败了,这个时候就会重启容器了。
 ~ /bin/sh -c "touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 600"

我们来创建下该Pod,在30秒内,查看Pod的Event

  • 我们可以观察到容器是正常启动的,在隔一会儿,比如40s后,再查看下Pod的Event,在最下面有一条信息显示 liveness probe失败了,容器被删掉并重新创建。
  • 然后通过kubectl get pod liveness-exec可以看到RESTARTS值加1了。
~ kubectl describe pod liveness-exec

2 livenessProbe:httpGet

---
apiVersion: v1
kind: Pod
metadata:
  name: liveness-http
  labels:
    app: liveness
spec:
  containers:
  - name: liveness
    image: cnych/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 3
      periodSeconds: 3
      

同样的,根据periodSeconds属性我们可以知道kubelet需要每隔3秒执行一次liveness probe,该探针将向容器中的 server 的8080端口发送一个 HTTP GET 请求。

  • 如果 server 的 /healthz 路径的 handler 返回一个成功的返回码,kubelet就会认定该容器是活着的并且很健康,如果返回失败的返回码,kubelet将杀掉该容器并重启它。
  • initialDelaySeconds 指定kubelet在该执行第一次探测之前需要等待3秒钟。

通常来说,任何大于200小于400的返回码都会认定是成功的返回码。其他返回码都会被认为是失败的返回码。

http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
    duration := time.Now().Sub(started)
    if duration.Seconds() > 10 {
        w.WriteHeader(500)
        w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds())))
    } else {
        w.WriteHeader(200)
        w.Write([]byte("ok"))
    }
})

大概意思就是最开始前10s返回状态码200,10s过后就返回500的status_code了。所以当容器启动3秒后,kubelet 开始执行健康检查。第一次健康监测会成功,因为是在10s之内,但是10秒后,健康检查将失败,因为现在返回的是一个错误的状态码了,所以kubelet将会杀掉和重启容器。

同样的,我们来创建下该Pod测试下效果,10秒后,查看 Pod 的 event,确认liveness probe失败并重启了容器。

~ kubectl describe pod liveness-http

在这里插入图片描述

3 livenessProbe/readinessProbe:tcpSocket

过端口的方式来配置存活探针,使用此配置,kubelet将尝试在指定端口上打开容器的套接字。 如果可以建立连接,容器被认为是健康的,如果不能就认为是失败的。

---
apiVersion: v1
kind: Pod
metadata:
  name: liveness-readiness
  labels:
    app: liveness-readiness
spec:
  containers:
  - name: liveness-readiness
    image: cnych/liveness
    args:
    - /server
    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 3
      perioidSeconds: 3

或者:

apiVersion: v1
kind: Pod
metadata:
  name: goproxy
  labels:
    app: goproxy
spec:
  containers:
  - name: goproxy
    image: cnych/goproxy
    ports:
    - containerPort: 8080
    readinessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 10
    livenessProbe:
      tcpSocket:
        port: 8080
      initialDelaySeconds: 15
      periodSeconds: 20

TCP 检查的配置与 HTTP 检查非常相似,只是将httpGet替换成了tcpSocket。 而且我们同时使用了readiness probe和liveness probe两种探针。 容器启动后5秒后,kubelet将发送第一个readiness probe(可读性探针)。 该探针会去连接容器的8080端,如果连接成功,则该 Pod 将被标记为就绪状态。然后Kubelet将每隔10秒钟执行一次该检查。

liveness probe: 容器启动15秒后,kubelet将运行第一个 liveness probe。 就像readiness probe一样,这将尝试去连接到容器的8080端口。如果liveness probe失败,容器将重新启动。

有的时候,应用程序可能暂时无法对外提供服务,例如,应用程序可能需要在启动期间加载大量数据或配置文件。 在这种情况下,您不想杀死应用程序,也不想对外提供服务。 那么这个时候我们就可以使用readiness probe来检测和减轻这些情况。 Pod中的容器可以报告自己还没有准备,不能处理Kubernetes服务发送过来的流量。

两者如果同时使用的话就可以确保流量不会到达还未准备好的容器,准备好过后,如果应用程序出现了错误,则会重新启动容器。

另外除了上面的initialDelaySeconds和periodSeconds属性外,探针还可以配置如下几个参数:

  • timeoutSeconds:探测超时时间,默认1秒,最小1秒。
  • successThreshold:探测失败后,最少连续探测成功多少次才被认定为成功。默认是 1,但是如果是liveness则必须是 1。最小值是 1。
  • failureThreshold:探测成功后,最少连续探测失败多少次才被认定为失败。默认是 3,最小值是 1。

六、 初始化容器initContainers

Init Container就是用来做初始化工作的容器,可以是一个或者多个,如果有多个的话,这些容器会按定义的顺序依次执行,只有所有的Init Container执行完后,主容器才会被启动。我们知道一个Pod里面的所有容器是共享数据卷和网络命名空间的,所以Init Container里面产生的数据可以被主容器使用到的。

从直观的角度看上去的话,初始化容器的确有点像PreStart,但是钩子函数和我们的Init Container是处在不同的阶段的,我们可以通过下面的图来了解下:

在这里插入图片描述

Init Container和钩子函数之类的区别:

  • 从上面这张图我们可以直观的看到PostStart和PreStop包括liveness和readiness是属于主容器的生命周期范围内的,
  • 而Init Container是独立于主容器之外的,当然他们都属于Pod的生命周期范畴之内的

另外我们可以看到上面我们的Pod右边还有一个infra的容器,这是一个什么容器呢?

  • 我们可以在集群环境中去查看下任意一个Pod对应的运行的Docker容器,我们可以发现每一个Pod下面都包含了一个pause-amd64的镜像,这个就是我们的infra镜像,我们知道Pod下面的所有容器是共享同一个网络命名空间的,这个镜像就是来做这个事情的,所以每一个Pod当中都会包含一个这个镜像。
  • 在这里插入图片描述
kubectl get pods <pod-name> -o yaml

很多同学最开始 Pod 启动不起来就是因为这个 infra 镜像没有被拉下来,因为默认该镜像是需要到谷歌服务器上拉取的,所以需要提前拉取到节点上面

我们说Init Container主要是来做初始化容器工作的,那么他有哪些应用场景呢?

  • 等待其他模块Ready:这个可以用来解决服务之间的依赖问题,比如我们有一个 Web 服务,该服务又依赖于另外一个数据库服务,但是在我们启动这个 Web 服务的时候我们并不能保证依赖的这个数据库服务就已经启动起来了,所以可能会出现一段时间内 Web 服务连接数据库异常。要解决这个问题的话我们就可以在 Web 服务的 Pod 中使用一个 InitContainer,在这个初始化容器中去检查数据库是否已经准备好了,准备好了过后初始化容器就结束退出,然后我们的主容器 Web 服务被启动起来,这个时候去连接数据库就不会有问题了。
  • 做初始化配置:比如集群里检测所有已经存在的成员节点,为主容器准备好集群的配置信息,这样主容器起来后就能用这个配置信息加入集群。
  • 其它场景:如将 pod 注册到一个中央数据库、配置中心等。

演示下服务依赖的场景下初始化容器的使用方法,如下Pod的定义方法

apiVersion: v1
kind: Pod
metadata:
  name: init-pod1
  labels:
    app: init
spec:
  containers:
  - name: init-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
---
apiVersion: v1
kind: Service
metadata:
  name: myservice
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 6379

---
apiVersion: v1
kind: Service
metadata:
  name: mydb
spec:
  ports:
  - protocol: TCP
    port: 80
    targetPort: 6378

Service的对应YAML内容:

kubectl create -f initservice.yaml

或者更新

kubectl apply -f initservice.yaml

查看service

kubectl get svc

等价于
kubectl get svc -n default

我们可以先创建上面的Pod,然后查看下Pod的状态,然后再创建下面的Service,对比下前后状态。

我们在Pod启动过程中,初始化容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个容器启动之前成功退出。如果由于运行时或失败退出,导致容器启动失败,它会根据Pod的restartPolicy指定的策略进行重试。 然而,如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时会使用 RestartPolicy 策略。

在所有的初始化容器没有成功之前,Pod将不会变成 Ready状态。正在初始化中的Pod处于Pending状态,但应该会将条件Initializing设置为 true。

接下来我们再来尝试创建一个做初始化配置工作的Pod:

---
apiVersion: v1
kind: Pod
metadata:
  name: init-demo
  labels:
    app: init
spec:
  initContainers:
  - name: install
    image: busybox
    command:
    - wget
    - "-O"
    - "/work-dir/index.html"
    - http://www.baidu.com
    volumeMounts:
    - name: workdir
      mountPath: /work-dir
  containers:
  - name: nginx
    image: nginx
    ports:
    - containerPort: 80
    volumeMounts:
    - name: workdir
      mountPath: /usr/share/nginx/html
  volumes:
  - name: workdir
    emptyDir: {}

我们可以看到这里又出现了volumes,

  • spec.volumes指的是Pod中的卷(在host上),
  • spec.containers.volumeMounts,是将指定的卷 mount 到容器指定的位置(容器内),相当于docker里面的-v 宿主机目录:容器目录,
  • 我们前面用到过hostPath,我们这里使用的是emptyDir{},这个就相当于一个共享卷,是一个临时的目录,生命周期等同于Pod的生命周期。

初始化容器执行完,会下载一个 html 文件映射到emptyDir{},而主容器也是和 spec.volumes 里的emptyDir{} 进行映射,所以nginx容器的/usr/share/nginx/html`目录下会映射 index.html 文件。

我们来创建下该Pod,然后验证nginx容器是否运行:

$ kubectl get pod init-demo

输出显示了nginx容器正在运行:

NAME      READY     STATUS    RESTARTS   AGE
nginx     1/1       Running   0          43m

在 init-demo 容器里的 nginx 容器打开一个 shell:

$ kubectl exec -it init-demo -- /bin/bash

在Shell里,直接查看下 index.html 的内容:

root@nginx:~# cat /usr/share/nginx/html/index.html
Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐