kubernetes 1.8 高可用安装

1、如何获取rpm包,及制作kubernetes镜像

1.1 官方yum源,有翻墙能力的 请使用

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://yum.kubernetes.io/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

1.2 自行编译生成相关rpm包

下载源码包
git clone 
https://github.com/kubernetes/release
 kubeadm
cd kubeadm/rpm
sh docker-build.sh

结果在 rpm/output/x86_64/yum/

kubectl-1.8.2-0.x86_64.rpm
kubelet-1.8.2-0.x86_64.rpm
kubeadm-1.8.2-0.x86_64.rpm
kubernetes-cni-0.5.1-0.x86_64.rpm

期间会生成一个docker image叫kubelet-rpm-builder,也需要翻墙
我已上传自己私有仓

https://hub.docker.com/r/foxchan/kubelet-rpm-builder/

注意:如果提前下载好kubelet-rpm-builder,需要修改rpm目录下的docker-build.sh 和Dockerfile

Dockerfile修改如下:

FROM foxchan/kubelet-rpm-builder
USER root
ADD entry.sh /root/
COPY ./ /root/rpmbuild/SPECS
ENTRYPOINT ["/root/entry.sh"]

docker-build.sh

#!/bin/sh
set -e
echo "Cleaning output directory..."
sudo rm -rf output/*
mkdir -p output
docker run -ti --rm -v $PWD/output/:/root/rpmbuild/RPMS/ kubelet-rpm-builder $1
sudo chown -R $USER $PWD/output
echo
echo "----------------------------------------"
echo
echo "RPMs written to: "
ls $PWD/output/*/
echo
echo "Yum repodata written to: "
ls $PWD/output/*/repodata/

2、利用github,创建kubernetes镜像

2.1 Dockerfile上传到github

github上新建一个工程,https://github.com/foxchenlei/docker-library

然后在工程中新增: ./kube-apiserver-amd64/Dockerfile,Dockerfile内容为:

FROM gcr.io/google_containers/kube-apiserver-amd64:v1.8.2 
MAINTAINER FoxChan

2.2 Docker Hub上创建Automated build

到docker hub上,Create -> Create Automated Build,新增一个Github类型的自动编译,选择docker-library项目;修改Repository的Name为kube-apiserver-amd64,简单填下描述,这样就创建了一个Automated Build。

进到Repository kube-apiserver-amd64,Build Setting中填写Dockerfile Location为kube-apiserver-amd64,修改Docker Tag Name为1.8.2,Save Change and Trigger;然后点Build Details,可以看到build的过程,状态切为Success就可以了。

2.3 剩下的,就是重复上面的操作,把你需要的镜像全部构建到hub.docker.com,以后需要的时候 就pull下来

使用kubeadm在CentOS 7上安装Kubernetes 1.8

1. 系统配置

1.1 关闭防火墙

systemctl stop firewalld
systemctl disable firewalld

1.2 禁用SELinux

setenforce 0

编辑文件/etc/selinux/config,将SELINUX修改为disabled,如下:

SELINUX=disabled

1.3 关闭系统Swap

Kubernetes 1.8开始要求关闭系统的Swap,如果不关闭,默认配置下kubelet将无法启动。方法一,通过kubelet的启动参数–fail-swap-on=false更改这个限制。方法二,关闭系统的Swap。

swapoff -a

修改/etc/fstab文件,注释掉SWAP的自动挂载,使用free -m确认swap已经关闭。

2. 安装Docker

注: 所有节点均需执行该步骤。

2.1 下载Docker安装包

  • 下载地址:https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
  • 下载安装包:
mkdir ~/k8s
cd k8s
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm
wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm

2.2 安装Docker

cd k8s
yum install ./docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm
yum install ./docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm
systemctl enable docker
systemctl start docker

2.3 配置Docker

  • 开启iptables filter表的FORWARD链
    编辑/lib/systemd/system/docker.service,在ExecStart=..上面加入如下内容:
ExecStartPost=/usr/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT

如下:

......
ExecStartPost=/usr/sbin/iptables -I FORWARD -s 0.0.0.0/0 -j ACCEPT
ExecStart=/usr/bin/dockerd
......
  • 配置Cgroup Driver
    创建文件/etc/docker/daemon.json,添加如下内容:
{
  "exec-opts": ["native.cgroupdriver=systemd"]
}
  • 重启Docker服务
systemctl daemon-reload && systemctl restart docker && systemctl status docker

3. 安装Kubernetes

3.1 安装kubeadm、kubectl、kubelet

  • 配置软件源
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/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
  • 调整swappiness参数
    修改/etc/sysctl.d/k8s.conf添加下面一行:
vm.swappiness=0

执行sysctl -p /etc/sysctl.d/k8s.conf使修改生效。

  • 安装kubeadm、kubectl、kubelet
    ① 查看可用版本
yum list --showduplicates | grep 'kubeadm|kubectl|kubelet'

② 安装指定版本

yum install kubeadm-1.8.1 kubectl-1.8.1 kubelet-1.8.1
systemctl enable kubelet
systemctl start kubelet

3.2 使用kubeadm init初始化集群

注:该小节仅在Master节点上执行

  • 初始化Master节点
kubeadm init --kubernetes-version=v1.8.1 --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=master.k8s.samwong.im
  • 配置普通用户使用kubectl访问集群
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
  • 查看集群状态
[root@master ~]# kubectl get cs
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok                   
controller-manager   Healthy   ok                   
etcd-0               Healthy   {"health": "true"} 
  • 初始化失败清理命令
kubeadm reset
ifconfig cni0 down
ip link delete cni0
ifconfig flannel.1 down
ip link delete flannel.1
rm -rf /var/lib/cni/

3.3 安装Pod Network

注:该小节仅在Master节点上执行

  • 安装Flannel
[root@master ~]# cd ~/k8s
[root@master ~]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
[root@master ~]# kubectl apply -f  kube-flannel.yml
clusterrole "flannel" created
clusterrolebinding "flannel" created
serviceaccount "flannel" created
configmap "kube-flannel-cfg" created
daemonset "kube-flannel-ds" created
  • 指定网卡
    如果有多个网卡,需要在kube-flannel.yml中使用–iface参数指定集群主机内网网卡的名称,否则可能会出现dns无法解析。需要将kube-flannel.yml下载到本地,flanneld启动参数加上–iface=。
......
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
......
containers:
      - name: kube-flannel
        image: quay.io/coreos/flannel:v0.9.0-amd64
        command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr", "--iface=eth1" ]
......
  • 查询Pod状态
kubectl get pod --all-namespaces -o wide

3.4 Master节点参与工作负载

使用kubeadm初始化的集群,出于安全考虑Pod不会被调度到Master Node上,可使用如下命令使Master节点参与工作负载。

kubectl taint nodes node1 node-role.kubernetes.io/master-

3.5 向Kubernetes集群添加Node

  • 查看master的token
kubeadm token list | grep authentication,signing | awk '{print $1}'
  • 查看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/^.* //'
  • 添加节点到Kubernetes集群
kubeadm join --token=a20844.654ef6410d60d465 --discovery-token-ca-cert-hash sha256:0c2dbe69a2721870a59171c6b5158bd1c04bc27665535ebf295c918a96de0bb1 master.k8s.samwong.im:6443
  • 查看集群中的节点
[root@master ~]# kubectl get nodes
NAME                    STATUS    ROLES     AGE       VERSION
master.k8s.samwong.im   Ready     master    1d        v1.8.1

3.6 从Kubernetes集群中移除节点

  • Master节点操作
kubectl drain master.k8s.samwong.im --delete-local-data --force --ignore-daemonsets
kubectl delete node master.k8s.samwong.im
  • Node节点操作
kubeadm reset
ifconfig cni0 down
ip link delete cni0
ifconfig flannel.1 down
ip link delete flannel.1
rm -rf /var/lib/cni/
  • 查看集群节点
kubectl get nodes

3.7 部署Dashboard插件

  • 下载Dashboard插件配置文件
cd ~/k8s
wget https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml
  • 修改Dashboard Service
    编辑kubernetes-dashboard.yaml文件,在Dashboard Service中添加type: NodePort,暴露Dashboard服务。
# ------------------- Dashboard Service ------------------- #
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  type: NodePort
  ports:
    - port: 443
      targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
  • 安装Dashboard插件
kubectl create -f kubernetes-dashboard.yaml
  • Dashboard账户集群管理权限
    创建一个kubernetes-dashboard-admin的ServiceAccount并授予集群admin的权限,创建kubernetes-dashboard-admin.rbac.yaml。
---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard-admin
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard-admin
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard-admin
  namespace: kube-system

执行命令:

[root@master ~]# kubectl create -f kubernetes-dashboard-admin.rbac.yaml
serviceaccount "kubernetes-dashboard-admin" created
clusterrolebinding "kubernetes-dashboard-admin" created
  • 查看kubernete-dashboard-admin的token
[root@master ~]# kubectl -n kube-system get secret | grep kubernetes-dashboard-admin
kubernetes-dashboard-admin-token-jxq7l   kubernetes.io/service-account-token   3         22h
[root@master ~]# kubectl describe -n kube-system secret/kubernetes-dashboard-admin-token-jxq7l
Name:         kubernetes-dashboard-admin-token-jxq7l
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name=kubernetes-dashboard-admin
              kubernetes.io/service-account.uid=686ee8e9-ce63-11e7-b3d5-080027d38be0
Type:  kubernetes.io/service-account-token
Data
====
namespace:  11 bytes
token:      eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbi10b2tlbi1qeHE3bCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJrdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbiIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjY4NmVlOGU5LWNlNjMtMTFlNy1iM2Q1LTA4MDAyN2QzOGJlMCIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDprdWJlLXN5c3RlbTprdWJlcm5ldGVzLWRhc2hib2FyZC1hZG1pbiJ9.Ua92im86o585ZPBfsOpuQgUh7zxgZ2p1EfGNhr99gAGLi2c3ss-2wOu0n9un9LFn44uVR7BCPIkRjSpTnlTHb_stRhHbrECfwNiXCoIxA-1TQmcznQ4k1l0P-sQge7YIIjvjBgNvZ5lkBNpsVanvdk97hI_kXpytkjrgIqI-d92Lw2D4xAvHGf1YQVowLJR_VnZp7E-STyTunJuQ9hy4HU0dmvbRXBRXQ1R6TcF-FTe-801qUjYqhporWtCaiO9KFEnkcYFJlIt8aZRSL30vzzpYnOvB_100_DdmW-53fLWIGYL8XFnlEWdU1tkADt3LFogPvBP4i9WwDn81AwKg_Q
ca.crt:     1025 bytes
  • 查看Dashboard服务端口
[root@master k8s]# kubectl get svc -n kube-system
NAME                   TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
kube-dns               ClusterIP   10.96.0.10       <none>        53/UDP,53/TCP   1d
kubernetes-dashboard   NodePort    10.102.209.161   <none>        443:32513/TCP   21h
  • 访问Dashboard
    打开浏览器输入https://192.168.56.2:32513,如下:
    未分类

3.8 部署heapster插件

安装Heapster为集群添加使用统计和监控功能,为Dashboard添加仪表盘。

mkdir -p ~/k8s/heapster
cd ~/k8s/heapster
wget https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/grafana.yaml
wget https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/rbac/heapster-rbac.yaml
wget https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/heapster.yaml
wget https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/influxdb.yaml
kubectl create -f ./

4. 遇到的问题

4.1 使用代理科学上网

  • 没有代理
    可申请AWS免费账户,创建EC2实例,搭建Shadowsocks服务器。

  • 配置代理客户端
    参考链接:https://www.zybuluo.com/ncepuwanghui/note/954160

  • 配置Docker代理
    ① 创建docker服务配置文件

mkdir -p /etc/systemd/system/docker.service.d

② 编辑vi /etc/systemd/system/docker.service.d/http-proxy.conf,添加如下内容:

[Service]
Environment="HTTP_PROXY=http://master.k8s.samwong.im:8118" "NO_PROXY=localhost,*.samwong.im,192.168.0.0/16,127.0.0.1,10.244.0.0/16"

③ 编辑/etc/systemd/system/docker.service.d/https-proxy.conf,添加如下内容:

[Service]
Environment="HTTPS_PROXY=https://master.k8s.samwong.im:8118" "NO_PROXY=localhost,*.samwong.im,192.168.0.0/16,127.0.0.1,10.244.0.0/16"

④ 重启Docker服务

systemctl daemon-reload && systemctl restart docker

⑤ 查看是否配置成功

[root@master k8s]# systemctl show --property=Environment docker | more
Environment=HTTP_PROXY=http://master.k8s.samwong.im:8118 NO_PROXY=localhost,*.samwong.im,192.168.0.0/16,127.0.0.1,10.244.0.0/16 HTTPS_PROXY=https://master.k8
s.samwong.im:8118
  • 配置yum代理
    ① 编辑/etc/yum.conf文件,追加如下内容:
proxy=http://master.k8s.samwong.im:8118

② 更新yum缓存

yum makecache
  • 配置wget代理
    编辑/etc/wgetrc文件,追加如下内容:
ftp_proxy=http://master.k8s.samwong.im:8118
http_proxy=http://master.k8s.samwong.im:8118
https_proxy=http://master.k8s.samwong.im:8118
  • 配置全局代理
    如需上网,可编辑/etc/profile文件,追加如下内容:
PROXY_HOST=master.k8s.samwong.im
export all_proxy=http://$PROXY_HOST:8118
export ftp_proxy=http://$PROXY_HOST:8118
export http_proxy=http://$PROXY_HOST:8118
export https_proxy=http://$PROXY_HOST:8118
export no_proxy=localhost,*.samwong.im,192.168.0.0/16.,127.0.0.1,10.10.0.0/16

注: 部署Kubernetes时需禁用全局代理,会导致访问内部服务失败。

4.2 下载软件包和镜像

  • 下载kubeadm、kubectl、kubelet
wget https://storage.googleapis.com/kubernetes-release/release/v1.8.1/bin/linux/amd64/kubeadm
wget https://storage.googleapis.com/kubernetes-release/release/v1.8.1/bin/linux/amd64/kubectl
wget https://storage.googleapis.com/kubernetes-release/release/v1.8.1/bin/linux/amd64/kubelet

参考链接:https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-binary-via-curl

4.3 推送本地镜像到镜像仓库

  • 上传镜像
docker login -u [email protected] -p xxxxxx hub.c.163.com
docker tag gcr.io/google_containers/kube-apiserver-amd64:v1.8.1 hub.c.163.com/xxxxxx/kube-apiserver-amd64:v1.8.1
docker push hub.c.163.com/xxxxxx/kube-apiserver-amd64:v1.8.1
docker rmi hub.c.163.com/xxxxxx/kube-apiserver-amd64:v1.8.1
docker logout hub.c.163.com
  • 下载镜像
docker pull hub.c.163.com/xxxxxx/kube-apiserver-amd64:v1.8.1
docker tag hub.c.163.com/xxxxxx/kube-apiserver-amd64:v1.8.1 gcr.io/google_containers/kube-apiserver-amd64:v1.8.1
docker rmi hub.c.163.com/xxxxxx/kube-apiserver-amd64:v1.8.1
docker logout hub.c.163.com
  • 更新镜像
docker update --restart=no $(docker ps -q) && docker stop $(docker ps -q) && docker rm $(docker ps -q)

4.4 kubeadm init错误

  • 错误描述
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {
  },
  "status": "Failure",
  "message": "nodes is forbidden: User "system:anonymous" cannot list nodes at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "nodes"
  },
  "code": 403
}
  • 问题原因
    该节点在/etc/profile中配置了全局代理,kubectl访问kube-apiserver也通过代理转发请求,导致证书不对,连接拒绝。

  • 解决方法
    取消全局代理,只配置Docker代理、yum代理、wget代理。
    参考4.1。

4.5 向Kubernetes集群添加Node失败

  • 问题描述
    在Node上使用kubeadm join命令向kubernetes集群添加节点时提示Failed,如下:
kubeadm join --token=a20844.654ef6410d60d465 --discovery-token-ca-cert-hash sha256:0c2dbe69a2721870a59171c6b5158bd1c04bc27665535ebf295c918a96de0bb1 master.k8s.samwong.im:6443
[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.
[preflight] Running pre-flight checks
[discovery] Trying to connect to API Server "master.k8s.samwong.im:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://master.k8s.samwong.im:6443"
[discovery] Failed to request cluster info, will try again: [Get https://master.k8s.samwong.im:6443/api/v1/namespaces/kube-public/configmaps/cluster-info: EOF]
  • 问题原因
    token失效被删除。在Master上查看token,结果为空。
kubeadm token list
  • 解决方法
    重新生成token,默认token有效期为24小时,生成token时通过指定–ttl 0可设置token永久有效。
[root@master ~]# kubeadm token create --ttl 0
3a536a.5d22075f49cc5fb8
[root@master ~]# kubeadm token list
TOKEN                     TTL         EXPIRES                     USAGES                   DESCRIPTION   EXTRA GROUPS
3a536a.5d22075f49cc5fb8   <forever>   <never>                     authentication,signing   <none>        system:bootstrappers:kubeadm:default-node-token

kubeadm快速部署kubernetes

1、环境准备

准备了三台机器作安装测试工作,机器信息如下:

未分类

2、安装docker

tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF

yum update -y && yum upgrade -y
yum install docker-engine -y
systemctl start docker
systemctl enable docker.service

3、安装k8s工具包

三种方式:官方源安装、非官方源安装和release工程编译,yum方式因为不能直接使用google提供的源,非官方源中提供的版本比较老(mritd提供的源很不错,版本很新),如果要使用新版本,可以尝试release工程编译的方式。

博主提供

一些比较懒得同学:-D,可以直接从博主提供的位置下载RPM工具包安装,下载地址: http://abyun.org/wp-content/themes/begin/inc/go.php?url=https://github.com/CloudNil/kubernetes-library/tree/master/rpm_x86_64/For_kubelet_1.5.2

yum install -y socat

rpm -ivh kubeadm-1.6.0-0.alpha.0.2074.a092d8e0f95f52.x86_64.rpm  kubectl-1.5.1-0.x86_64.rpm  kubelet-1.5.1-0.x86_64.rpm  kubernetes-cni-0.3.0.1-0.07a8a2.x86_64.rpm

systemctl enable kubelet.service

官方源安装

跨越GFW方式不细说,你懂的。

建议使用yumdownloader下载rpm包,不然那下载速度,会让各位对玩k8s失去兴趣的。

yum install -y yum-utils

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=http://yum.kubernetes.io/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

yumdownloader kubelet kubeadm kubectl kubernetes-cni
rpm -ivh *.rpm
systemctl enable kubelet.service && systemctl start kubelet

非官方源安装

#感谢mritd维护了一个yum源
tee /etc/yum.repos.d/mritd.repo << EOF
[mritdrepo]
name=Mritd Repository
baseurl=https://rpm.mritd.me/centos/7/x86_64
enabled=1
gpgcheck=1
gpgkey=https://cdn.mritd.me/keys/rpm.public.key
EOF
yum makecache
yum install -y kubelet kubectl kubernetes-cni kubeadm
systemctl enable kubelet && systemctl start kubelet

relese编译

git clone https://github.com/kubernetes/release.git
cd release/rpm
./docker-build.sh

编译完成后生成rpm包到:/output/x86_64,进入到该目录后安装rpm包,注意选择amd64的包(相信大多数同学都是64bit环境,如果是32bit或者arm架构请自行选择安装)。

4、下载docker镜像

kubeadm方式安装kubernetes集群需要的镜像在docker官方镜像中并未提供,只能去google的官方镜像库:gcr.io 中下载,GFW咋办?翻墙!也可以使用docker hub做跳板自己构建,这里针对k8s-1.5.2我已经做好镜像,各位可以直接下载,dashboard的版本并未紧跟kubelet主线版本,用哪个版本都可以,本文使用kubernetes-dashboard-amd64:v1.5.0。

kubernetes-1.5.2所需要的镜像:

  • etcd-amd64:2.2.5
  • kubedns-amd64:1.9
  • kube-dnsmasq-amd64:1.4
  • dnsmasq-metrics-amd64:1.0
  • exechealthz-amd64:1.2
  • pause-amd64:3.0
  • kube-discovery-amd64:1.0
  • kube-proxy-amd64:v1.5.2
  • kube-scheduler-amd64:v1.5.2
  • kube-controller-manager-amd64:v1.5.2
  • kube-apiserver-amd64:v1.5.2
  • kubernetes-dashboard-amd64:v1.5.0

偷下懒吧,直接执行以下脚本:

#!/bin/bash
images=(kube-proxy-amd64:v1.5.2 kube-discovery-amd64:1.0 kubedns-amd64:1.9 kube-scheduler-amd64:v1.5.2 kube-controller-manager-amd64:v1.5.2 kube-apiserver-amd64:v1.5.2 etcd-amd64:2.2.5 kube-dnsmasq-amd64:1.4 dnsmasq-metrics-amd64:1.0 exechealthz-amd64:1.2 pause-amd64:3.0 kubernetes-dashboard-amd64:v1.5.0 nginx-ingress-controller:0.8.3)
for imageName in ${images[@]} ; do
  docker pull cloudnil/$imageName
  docker tag cloudnil/$imageName gcr.io/google_containers/$imageName
  docker rmi cloudnil/$imageName
done

5、安装master节点

由于kubeadm和kubelet安装过程中会生成/etc/kubernetes目录,而kubeadm init会先检测该目录是否存在,所以我们先使用kubeadm初始化环境。

kubeadm reset && systemctl start kubelet
kubeadm init --api-advertise-addresses=172.16.1.101 --use-kubernetes-version v1.5.2
#如果使用外部etcd集群:
kubeadm init --api-advertise-addresses=172.16.1.101 --use-kubernetes-version v1.5.2 --external-etcd-endpoints http://172.16.1.107:2379,http://172.16.1.107:4001

说明:如果打算使用flannel网络,请加上:–pod-network-cidr=10.244.0.0/16。如果有多网卡的,请根据实际情况配置–api-advertise-addresses=,单网卡情况可以省略。

如果出现ebtables not found in system path的错误,要先安装ebtables包,我安装的过程中未提示,该包系统已经自带了。

yum install -y ebtables
  • 1

安装过程大概2-3分钟,输出结果如下:

[kubeadm] WARNING: kubeadm is in alpha, please do not use it for production clusters.
[preflight] Running pre-flight checks
[init] Using Kubernetes version: v1.5.2
[tokens] Generated token: "064158.548b9ddb1d3fad3e"
[certificates] Generated Certificate Authority key and certificate.
[certificates] Generated API Server key and certificate
[certificates] Generated Service Account signing keys
[certificates] Created keys and certificates in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[apiclient] Created API client, waiting for the control plane to become ready
[apiclient] All control plane components are healthy after 61.317580 seconds
[apiclient] Waiting for at least one node to register and become ready
[apiclient] First node is ready after 6.556101 seconds
[apiclient] Creating a test deployment
[apiclient] Test deployment succeeded
[token-discovery] Created the kube-discovery deployment, waiting for it to become ready
[token-discovery] kube-discovery is ready after 6.020980 seconds
[addons] Created essential addon: kube-proxy
[addons] Created essential addon: kube-dns

Your Kubernetes master has initialized successfully!

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

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

kubeadm join --token=de3d61.504a049ec342e135 172.16.1.101

6、安装minion节点

Master节点安装好了Minoin节点就简单了。

kubeadm reset && systemctl start kubelet
kubeadm join --token=de3d61.504a049ec342e135 172.16.1.101
  • 1
  • 2

输出结果如下:

[kubeadm] WARNING: kubeadm is in alpha, please do not use it for production clusters.
[preflight] Running pre-flight checks
[preflight] Starting the kubelet service
[tokens] Validating provided token
[discovery] Created cluster info discovery client, requesting info from "http://172.16.1.101:9898/cluster-info/v1/?token-id=f11877"
[discovery] Cluster info object received, verifying signature using given token
[discovery] Cluster info signature and contents are valid, will use API endpoints [https://172.16.1.101:6443]
[bootstrap] Trying to connect to endpoint https://172.16.1.101:6443
[bootstrap] Detected server version: v1.5.2
[bootstrap] Successfully established connection with endpoint "https://172.16.1.101:6443"
[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request
[csr] Received signed certificate from the API server:
Issuer: CN=kubernetes | Subject: CN=system:node:yournode | CA: false
Not before: 2016-12-15 19:44:00 +0000 UTC Not After: 2017-12-15 19:44:00 +0000 UTC
[csr] Generating kubelet configuration
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

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

安装完成后可以查看下状态:

[root@master ~]# kubectl get nodes
NAME       STATUS         AGE
master     Ready,master   6m
minion01   Ready          2m
minion02   Ready          2m

7、安装Calico网络

网络组件选择很多,可以根据自己的需要选择calico、weave、flannel,calico性能最好,weave和flannel差不多。Addons中有配置好的yaml,部署环境使用的阿里云的VPC,官方提供的flannel.yaml创建的flannel网络有问题,所以本文中尝试calico网络,。

kubectl apply -f http://docs.projectcalico.org/v2.0/getting-started/kubernetes/installation/hosted/kubeadm/calico.yaml
  • 1

如果使用了外部etcd,去掉其中以下内容,并修改etcd_endpoints: [ETCD_ENDPOINTS]:

---

# This manifest installs the Calico etcd on the kubeadm master.  This uses a DaemonSet
# to force it to run on the master even when the master isn't schedulable, and uses
# nodeSelector to ensure it only runs on the master.
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: calico-etcd
  namespace: kube-system
  labels:
    k8s-app: calico-etcd
spec:
  template:
    metadata:
      labels:
        k8s-app: calico-etcd
      annotations:
        scheduler.alpha.kubernetes.io/critical-pod: ''
        scheduler.alpha.kubernetes.io/tolerations: |
          [{"key": "dedicated", "value": "master", "effect": "NoSchedule" },
           {"key":"CriticalAddonsOnly", "operator":"Exists"}]
    spec:
      # Only run this pod on the master.
      nodeSelector:
        kubeadm.alpha.kubernetes.io/role: master
      hostNetwork: true
      containers:
        - name: calico-etcd
          image: gcr.io/google_containers/etcd:2.2.1
          env:
            - name: CALICO_ETCD_IP
              valueFrom:
                fieldRef:
                  fieldPath: status.podIP
          command: ["/bin/sh","-c"]
          args: ["/usr/local/bin/etcd --name=calico --data-dir=/var/etcd/calico-data --advertise-client-urls=http://$CALICO_ETCD_IP:6666 --listen-client-urls=http://0.0.0.0:6666 --listen-peer-urls=http://0.0.0.0:6667"]
          volumeMounts:
            - name: var-etcd
              mountPath: /var/etcd
      volumes:
        - name: var-etcd
          hostPath:
            path: /var/etcd

---

# This manfiest installs the Service which gets traffic to the Calico
# etcd.
apiVersion: v1
kind: Service
metadata:
  labels:
    k8s-app: calico-etcd
  name: calico-etcd
  namespace: kube-system
spec:
  # Select the calico-etcd pod running on the master.
  selector:
    k8s-app: calico-etcd
  # This ClusterIP needs to be known in advance, since we cannot rely
  # on DNS to get access to etcd.
  clusterIP: 10.96.232.136
  ports:
    - port: 6666

检查各节点组件运行状态:

[root@master work]# kubectl get po -n=kube-system -o wide
NAME                                       READY     STATUS    RESTARTS   AGE       IP                NODE
calico-node-0jkjn                          2/2       Running   0          25m       172.16.1.101      master
calico-node-w1kmx                          2/2       Running   2          25m       172.16.1.106      minion01
calico-node-xqch6                          2/2       Running   0          25m       172.16.1.107      minion02
calico-policy-controller-807063459-d7z47   1/1       Running   0          11m       172.16.1.107      minion02
dummy-2088944543-qw3vr                     1/1       Running   0          29m       172.16.1.101      master
kube-apiserver-master                      1/1       Running   0          28m       172.16.1.101      master
kube-controller-manager-master             1/1       Running   0          29m       172.16.1.101      master
kube-discovery-1769846148-lzlff            1/1       Running   0          29m       172.16.1.101      master
kube-dns-2924299975-jfvrd                  4/4       Running   0          29m       192.168.228.193   master
kube-proxy-6bk7n                           1/1       Running   0          28m       172.16.1.107      minion02
kube-proxy-6pgqz                           1/1       Running   1          29m       172.16.1.106      minion01
kube-proxy-7ms6m                           1/1       Running   0          29m       172.16.1.101      master
kube-scheduler-master                      1/1       Running   0          28m       172.16.1.101      master

说明:kube-dns需要等calico配置完成后才是running状态。

8、部署Dashboard

下载kubernetes-dashboard.yaml

curl -O https://rawgit.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml
  • 1

修改配置内容,#——内是修改的内容,调整目的:部署kubernetes-dashboard到default-namespaces,不暴露端口到HostNode,调整版本为1.5.0,imagePullPolicy调整为IfNotPresent。

kind: Deployment
apiVersion: extensions/v1beta1
metadata:
  labels:
    app: kubernetes-dashboard
  name: kubernetes-dashboard
#----------
#  namespace: kube-system
#----------
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kubernetes-dashboard
  template:
    metadata:
      labels:
        app: kubernetes-dashboard
      annotations:
        scheduler.alpha.kubernetes.io/tolerations: |
          [
            {
              "key": "dedicated",
              "operator": "Equal",
              "value": "master",
              "effect": "NoSchedule"
            }
          ]
    spec:
      containers:
      - name: kubernetes-dashboard
        #----------
        image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.5.0
        imagePullPolicy: IfNotPresent
        #----------
        ports:
        - containerPort: 9090
          protocol: TCP
        args:
          # Uncomment the following line to manually specify Kubernetes API server Host
          # If not specified, Dashboard will attempt to auto discover the API server and connect
          # to it. Uncomment only if the default does not work.
          # - --apiserver-host=http://my-address:port
        livenessProbe:
          httpGet:
            path: /
            port: 9090
          initialDelaySeconds: 30
          timeoutSeconds: 30
---
kind: Service
apiVersion: v1
metadata:
  labels:
    app: kubernetes-dashboard
  name: kubernetes-dashboard
#----------
#  namespace: kube-system
#----------
spec:
#----------
#  type: NodePort
#----------
  ports:
  - port: 80
    targetPort: 9090
  selector:
    app: kubernetes-dashboard

9、Dashboard服务暴露到公网

kubernetes中的Service暴露到外部有三种方式,分别是:

  • LoadBlancer Service
  • NodePort Service
  • Ingress

LoadBlancer Service是kubernetes深度结合云平台的一个组件;当使用LoadBlancer Service暴露服务时,实际上是通过向底层云平台申请创建一个负载均衡器来向外暴露服务;目前LoadBlancer Service支持的云平台已经相对完善,比如国外的GCE、DigitalOcean,国内的 阿里云,私有云 Openstack 等等,由于LoadBlancer Service深度结合了云平台,所以只能在一些云平台上来使用。

NodePort Service顾名思义,实质上就是通过在集群的每个node上暴露一个端口,然后将这个端口映射到某个具体的service来实现的,虽然每个node的端口有很多(0~65535),但是由于安全性和易用性(服务多了就乱了,还有端口冲突问题)实际使用可能并不多。

Ingress可以实现使用nginx等开源的反向代理负载均衡器实现对外暴露服务,可以理解Ingress就是用于配置域名转发的一个东西,在nginx中就类似upstream,它与ingress-controller结合使用,通过ingress-controller监控到pod及service的变化,动态地将ingress中的转发信息写到诸如nginx、apache、haproxy等组件中实现方向代理和负载均衡。

9.1 部署Nginx-ingress-controller

Nginx-ingress-controller是kubernetes官方提供的集成了Ingress-controller和Nginx的一个docker镜像。

apiVersion: v1
kind: ReplicationController
metadata:
  name: nginx-ingress-controller
  labels:
    k8s-app: nginx-ingress-lb
spec:
  replicas: 1
  selector:
    k8s-app: nginx-ingress-lb
  template:
    metadata:
      labels:
        k8s-app: nginx-ingress-lb
        name: nginx-ingress-lb
    spec:
      terminationGracePeriodSeconds: 60
      hostNetwork: true
      #本环境中的minion02节点有外网IP,并且有label定义:External-IP=true
      nodeSelector:
        External-IP: true
      containers:
      - image: gcr.io/google_containers/nginx-ingress-controller:0.8.3
        name: nginx-ingress-lb
        imagePullPolicy: IfNotPresent
        readinessProbe:
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
        livenessProbe:
          httpGet:
            path: /healthz
            port: 10254
            scheme: HTTP
          initialDelaySeconds: 10
          timeoutSeconds: 1
        env:
          - name: POD_NAME
            valueFrom:
              fieldRef:
                fieldPath: metadata.name
          - name: POD_NAMESPACE
            valueFrom:
              fieldRef:
                fieldPath: metadata.namespace
        args:
        - /nginx-ingress-controller
        - --default-backend-service=$(POD_NAMESPACE)/kubernetes-dashboard

9.2 部署Ingress

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: k8s-dashboard
spec:
  rules:
  - host: dashboard.cloudnil.com
    http:
      paths:
      - path: /
        backend:
          serviceName: kubernetes-dashboard
          servicePort: 80

部署完Ingress后,解析域名dashboard.cloudnil.com到minion02的外网IP,就可以使用dashboard.cloudnil.com访问dashboard。

10、注意事项

kubeadm目前还在开发测试阶段,不建议在生产环境中使用kubeadm部署kubernetes环境。此外,使用kubeadm是需要注意以下几点:

10.1 单点故障

当前版本的kubeadm暂且不能部署真正高可用的kubernetes环境,只具有单点的master环境,如采用内置etcd,那etcd也是单节点,若master节点故障,可能存在数据丢失的情况,所以建议采用外部的etcd集群,这样即使master节点故障,那只要重启即可,数据不会丢失,高可用的部署功能据说正在开发中,很快就可以发布使用。

10.2 暴露主机端口

POD实例配置中的HostPort和HostIP参数无法用于使用了CNI网络插件的kubernetes集群环境,如果需要暴露容器到主机端口,可以使用NodePort或者HostNetwork。

10.3 CentOS环境路由错误

RHEL/CentOS7 环境中iptables的策略关系,会导致路由通讯错误,需要手动调整iptables的桥接设置:

# cat /etc/sysctl.d/k8s.conf
 net.bridge.bridge-nf-call-ip6tables = 1
 net.bridge.bridge-nf-call-iptables = 1

10.4 Token丢失

Master节点部署完成之后,会输出一个token用于minion节点的配置链接,不过这个token没有很方便的查看方式,导致此日志输出关闭后,没有token无法join minion节点,可以通过下述方式查看token:

kubectl -n kube-system get secret clusterinfo -o yaml | grep token-map | awk '{print $2}' | base64 --decode | sed "s|{||g;s|}||g;s|:|.|g;s/"//g;" | xargs echo
  • 1

建议提前使用kubeadm token命令生成token,然后在执行kubeadm init和kubeadm join的使用通过–token指定token。

10.5 Vagrant中主机名的问题

如果使用Vagrant虚拟化环境部署kubernetes,首先得确保hostname -i能够获取正确的通讯IP,默认情况下,如果/etc/hosts中未配置主机名与IP的对应关系,kubelet会取第一个非lo网卡作为通讯入口,若这个网卡不做了NAT桥接的网卡,那安装就会出现问题。

腾讯云多Kubernetes的多维度监控实践

本次内容根据2017年11月4日 K8S Geek Gathering 沙龙深圳站腾讯云高级工程师王天夫的演讲内容整理而成。

本次分享的主要内容涉及腾讯云容器的顶层整体设计,包括产品功能,及提供的附加能力。同时会介绍我们现在Master集群化部署的整体方案。通过这些内容的提前了解,可以更好理解后面和大家分享的关于容器监控的内容,所有监控的设计都是依赖于Master集群化部署的。最后会和大家分享腾讯云容器服务监控的Future Work。

未分类

大家可以看一下这个图,这是腾讯云容器服务PaaS平台顶层的设计,最上面是云Portal,意义是用户在使用我们容器服务的时候能够从这几个维度去掌控他们的集群、创建他们的容器。第一个是MC,可以理解为是控制管理台,大家都知道Kubernetes本身的概念是比较复杂的,对一个刚刚接手Kubernetes的人来说理解成本是特别大的,我们在Kubernetes之上包装了它的概念,对于刚刚接手的人来说是非常好的理解。同时可视化出来提供页面给大家用。第二点是支持原生的K8S API,因为整个容器服务是基于K8S开发的,为了保证用户使用不同的Kubernetes,所以我们在Kubernetes主干上并不会去做很大的改动,因为一开始我们是做过这方面的东西,但是我们发现如果我们在K8S上做了特别大的改动,在第二次提供给用户或者升级的时候,是一个非常困难的事情,因为大家知道K8S的迭代是非常快的,大概是半年到一年时间就会升级一个大版本,所以我们基本上都是原生基于K8S开发。最后我们还把我们自己包装的概念通过一个云API提供给用户使用,因为现在很多的用户都是有自己的运营系统的,他们会使用云API去封装一些自己的运营平台,所以我们这里也支持了一下。

中间这块是整个容器服务三个大部分,第一块是整个CCS,这块提供了如下的功能,像集群管理、模板应用管理,应用管理这里指的是我们会把所有的服务包装成一个大的应用,方便用户去一键部署自己的应用。第三个是服务管理,我们把K8S里面提到的概念,像deployment,service,pod封装成服务的概念提供给用户,后面还有日志、券和配额,这些都是我们这边开发的。最底层,因为我们要支持原生的Kubernetes,所以不可避免的需要有些开发,比如和IaaS层、PaaS层的打通,就需要有日志、网络、券、负载均衡的驱动开发。第二块是CCR,是我刚才提到的镜像服务。镜像服务支持有多个hub源的镜像,还有自建的Cloud的镜像,还有第三方的也支持。同时我们做了一些比较重要的优化,我们在海外搭建了很多节点,帮助用户能够快速的拉取镜像,并且做成mirror,同时我们的镜像仓库是分地域部署。同时我们还会定时拉去Docker Hub上的镜像做缓存,进一步提升效率。最后一块是CI/CD,CI/CD是去定义自动部署、自动构建的策略,例如提交代码时自动触发。

下面是我现在负责的CCS-Monitor Server,包括监控上面整套容器服务的体系。最底下是对我们自己腾讯云的IaaS和PaaS层的集合,譬如说CVM、黑石,大家可以理解为公有云和私有云,同时我们集成块存储、对象存储、CFS,我们还有日志中心,后面我会讲一下。最后还有弹性伸缩和负载均衡,弹性伸缩主要可以理解为HPA和HNA,这是腾讯服务顶层的计。

未分类

接下来第二点,给大家讲一下腾讯云Master集群化部署。下图是现在腾讯容器服务Master集群化部署的概览,我给大家说一下大概的背景。当初腾讯云的容器服务刚开始上线的时候,这个地方并不是这样子的。

刚开始我们会为用户的每个集群创建一个Master,这个Master当初是一个虚拟机,为了做高可用,我们会为用户再部署一个stand by的Master,当Master出现故障的时候,我们可以方便的切换。

最后就造成很多多的问题,一是成本的问题,如果每个集群都部署一个Master,部署一台虚拟机,成本很大。二是运维成本,每台Master都是一台虚拟机,每个master的组件,是一个二进制文件部署在Master上面。这种情况下,如果对某个Master进行升级,不可避免的就会出现很多问题。

基于这个考虑,我们重新优化了整个我们的Master部署,我们采用的方案是调研了社区里面一些热门的方案,这个方案就是kubernetes in kubernetes,不知道大家有没有了解过这个东西。我们单独部署一套K8S集群,所有Master的组件,大家知道的三大组件都会以pod的形式运行在K8S集群中。我们为Pod绑定双网卡,一个网卡是弹性网卡,它会单独与用户的VPC做网络通信,另外一个网卡是原生的,这个网卡会是Master集群中使用的网卡。

每个API的组件都是一个Pod,好处是,一是Master集群的部署,主要是以K8S管理,这样HA、SLA都有很大的保证,包括后面运维的提升,可以使用K8S原生的rolling update去操作。其次是成本,有些用户可能Master不需要那么大的CPU Memory,我们可以共同调整CPU Memory的request,同时对于大型的客户,譬如说在集群中运行了上千个Pod情况下,通过动态调整扩展Master的配置,增大更多的Api server做一个负载调优。

这里大概讲了现在集群化部署的方案,后面我监控的方案都会依赖于这个,给大家稍微先讲一下这块。

未分类

未分类

第三,基础的指标监控。我们的基础指标监控主要还是以IaaS层为主,就是大家所说的Cpu,Memory、DiSK和Network方面。这里一共有四块,Node、Deployment,Deployment,Pod,container,所有IaaS层的监控指标都会依照这四个模块来做聚合。

上面的四块是我讲的IaaS层基础指标,这里我选取了几个重要的指标,如果大家以后有自建容器监控,希望对大家有所帮助。CPU Memory有些指标是非常重要的,例如CPU Usage和Memory Usage,这里的Cpu Usage和Memory Usage是占整个pod request或者 limit的整个比例,如果这两个指标比较高,就必须警觉起来,可能会造成pod不可用的问题发生,另外我提一下,大家知道,在K8S中,有一个request 和limit的概念,如果request limit不配置,在一些测试环境,不知道大家有没有试过,当一个Pod跑到很高的情况下,会出现雪崩的效应,比如跑挂一台机器,这时候挂了之后,节点异常,K8S会自动的把这台机器上所有的Pod踢走,Pod会自动创建到另外的机器上,继续拖垮另外一台机器,这种可以称之为“雪崩效应”。最后造成的结果是K8S集群不可用。其次,CPU node和Memory node。当前你的K8S集群中还有剩余的CPU和Memory可分配的比例,当一个K8S pod配置的request limit不能满足当前集群中所剩余的量,就会造成,新的Pod无法调度。Network比较基本,一个是进出带宽、进出流量,还有进出包量。Disk有几个比较重要的,traffic、iops,这些自己自建的时候也需要关注。

最后单独提一下Inode,有一次用户使用过程发现Pod创建不成功,经过多方面调查,发现Inode满了,为什么出现这种情况?我们看了K8S代码,它对镜像和日志都有回收机制,但是对Inode的回收和清理是没有的,社区也有讨论,但是一直没有解决。所以说Inode这块是必须要监控起来的,它会造成你整个集群中某个节点无法创建服务,所以说我单独把它拎出来和大家提一下,我不知道现在的1.8版本有没有解决这个问题。

最后是LB的监控,大家可以知道,servers有几种提供的访问方式,腾讯云容器服务的servers与腾讯云的LB做了绑定,用户创建servers的时候,如果指定的方式是LB,我们通过插件的方式帮他申请一个LB挂在上面,不管是内网还是外网。用户对自己服务的监控,我们可以通过LB的Httpcode和当前的连接数、回包的时间等指标去做,就能准确的判断出当前业务的健康状态。

未分类

这是基础监控指标大概的架构(见下图)。我们主要使用开源的方式,大家在网上关注监控就知道,容器的监控大概有这么几种方案,当时我们做方案评估的时候也考虑过其他几种,最后还是选择了heapster的方式,还是业务驱动,因为调研的时候发现cadvisor对K8S中的聚合没有做得特别好,它的数据都是原始的数据,如果我们在以Kubernetes的方式聚合,这是很复杂的。普罗米修斯,我们考虑它比较重,一个是集收集,告警和存储一体的,我们需要的仅仅是收集这块,所以说普罗米修斯并不适合我们这里的业务,最后我们就选择使用heapster。我们这里的heapster也是以容器的方式部署在Master集群化部署的集群里面,对用户是无感知的,也不消耗用户的资源。其次heapster我们帮它绑定两张网卡,一张是去用户的集群中拉取监控数据,因为大家知道heapster需要与Kubernetes通信,拉取一些监控数据,所以我们这里必须绑定两张网卡。其次我们会构建一个CCS Monitorserver,定时拉取一些集群的监控数据,做一些汇总或计算。最后,我们会把这些收集到的数据Push到Barad server,这可以理解为腾讯云整个监控的组件,这是平台级的组件。Barad server做了几件事,一是聚合,会把我们所有上报的指标聚合成,比如我们加一些时间粒度的汇总,1分钟、5分钟、10分钟这种,包括我们以Node的形式展示出整个Node下所有pod平均的监控指标。做这种聚合。最后我们会提供云API的方式给接入层使用,接入层主要是用户调取需要的监控指标,还有HPA和HNA,就是Pod和Node水平扩展,这个是我们直接拉取Barad API的数据做扩展工作。

未分类

第四,事件指标。其实在问题发生的时候,如果仅仅只看基础的监控指标是远远不够的,因为你只能发现你的服务出现了问题,但是你不能很好的去知道到底是哪个服务出现了问题,事件指标主要如下几个问题:1、汇聚当前K8S所有资源事件的汇总,我们会根据这些事件做自己的提炼,同时push到事件中心。2、事件中心指标弥补指标监控信息部直接的短板,我刚才说到过。3、提高问题的定位效率。4、事件指标支持告警。事件指标主要分两大块:1、异常事件;2、状态事件。异常事件大家可以理解为Pod创建失败、重启、节点异常、内存不足、调度失败的事件。状态事件是一些正常事件,比如Pod启动、销毁、更新中、HPA触发、HNA触发。它对对于定位问题也有很大的帮助。

未分类

事件指标的整体方案,我们当前是从API server中拉取K8S所有的事件,会存储在ES集群中,我们会有内部的Cluster做数据的查询和展示。ES中汇聚的都是每条基础数据,也就是大家俗称的源数据。其次CCS Monitor会把它合并成用户能够理解的汇总的数据,然后推到腾讯云事件中心去。最后用户可以在控制台上看到这个事件发生的原因、属于哪个资源,最后还会告诉它如何解决这个问题,会有一个帮助文档。

未分类

第五,整个监控中对腾讯最重要的是集群稳定性的监控。这里的图会有点问题,集群稳定性的监控分为四大块,Kube-System和ETCD、Master相关组件监控,比如API server等,最重要的是运行时问题监控。

未分类

集群稳定性监控采用三个开源的方案,一是Node Detector,主要是做运行时。Fluentd主要是采集每个Master集群上每个容器的node,后面也用了普罗米修斯的方案,没有再使用heapster,因为普罗米修斯,我们需要它做一些存储,不需要做对外展示,这是内部使用,所以我们需要采用普罗米修斯去定制一些东西去采集更多的指标。大家可以看到整个Master集群上,每个Master集群上每个node部署的各个pod,Fluentd会拉取lod。普罗米修斯我们自己定制了一些插件,在每个pod上拉取一些我们基本的指标。同时Node Detector部署在每个用户节点上,这是用户可以自己选择的。它主要采集一些Kernel和runtime的问题。Node Detector是K8S官方的项目,如果大家感兴趣可以了解一下。

未分类

第六,最后一个监控组件,业务日志监控。大家知道业务日志对于一个问题定位帮助是非常大的,如果一个监控仅仅只有事件和指标,其实很多问题都无法定位,因为容器在使用的时候,更多的会动态的增加和减少。所以我们这里会采集容器日志,并且保存在日志服务中,供用户在后期能更方便的去追溯到原来的一些业务问题。业务日志分四个板块,容器日志的收集和节点日志的收集,还有日志存储和日志处理,现在容器日志也是前面提到的stdout和stderr日志,并且附加相关的Kubernetes Metadata,主机特定目录的日志并附加指定的label去收集用户容器达到主机上固定的日志。存储方面,我们支持把日志发送到用户自己搭建的Kafka或者ES或者腾讯云的日志服务中存储起来做消费。最后一块是日志处理,日志处理主要是方便用户能够去方便定位问题,同时我们支持能够根据一定关键字配置一些关键字的告警,最后我们后续可能还会支持做一些大数据的处理工作。

未分类

整个业务日志的监控整体的方案(见PPT),我们让用户定义一个个不同的规则,不同的规则可以叫collector,所有的collector会并成一个Config Map,在启动Fluentd的时候会收集Cluster指定的收集策略,最后发送到不同的后端源,当时做这套设计的时候我们考虑其他组件的方案,主要采集的agent,我们考虑了filebeat和fluentd,最后我们采用的还是fluentd,一是基于性能的考虑,fluentd是c和ruby写的,消耗只有在40M左右,filebeat会更大一些。最重要的一点是fluentd支持数据源推的路由,也就是说它能够通过不同的规则、不同的lable去推到不同的终端上。例如我有一条规则A和规则B,它分别推到ES或者Ckafka,所以我们最后就选择了fluentd的方案做业务日志的收集。

未分类

最后讲一下我们后期调研的工作和待开发的工作:1、自定义监控。自定义希望让用户能够自己去定义一些监控指标,自己Push一些监控指标到我们的监控平台。2、调用链追踪和Real-time Monitor的部署,当一个应用起来之后,会有成千上百的容器在里面传输,调用链就是非常直观的监控方向,同时配合realtimemonitor,能够很好的发现服务的健康状态,其次我们还会提供一个应用市场,因为现在普遍上开源也有很多的监控方案,我们会把它打包成一个个应用提供给用户使用,最后两点就是告警的预测和集群网络的监控,告警预测我们希望通过我们收集的数据去分析可能发生的问题,例如说当一个时间点内某项指标与前段时间周期性比较的指标的发生很大差异的时候,我们需要能够及时的通知用户,这里可能会出现了问题。集群网络的监控方面,我们发现用户在部署的集群的时候,经常有些iptables,ACL,或者timeout等网络的问题,这块的监控后期同样会补上。

未分类

Kubernetes 1.6到1.7升级记录

最近正在制定将团队生产环境的Kubernetes集群从1.6升级1.7的计划。 Kubernetes 1.8已经发布,所以准备考虑从1.6到1.7的升级。

准备

当前1.7的最新版本是1.7.8。在做准备之前需要仔细读一遍官方的Kubernetes 1.7 (https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.7.md#action-required-before-upgrading)。

使用ansible升级k8s的核心组件

目前我们总共有两个高可用的Kubernetes集群,分别是测试环境和生产环境,版本都是1.6.10。 这两套环境的Kubernetes集群都是基于ansible自动部署,在1.6.x的每个小版本的升级也都是使用ansible完成。

这里准备的升级步骤是本地VM环境 => 测试环境 => 生产环境。 先使用ansible在本地VM中部署k8s 1.6.10的集群,然后再使用ansible将本地1.6升级到1.7并做一些验证,再升级测试环境,测试环境稳定运行一段时间后完成生产环境的升级。

本地VM环境中的ansible play的十分顺利,集群的各个核心组件已经成功的升级到1.7.8。

以容器形式运行组件的升级

接下来要对一些以容器形式运行的组件升级,对应版本如下:

  • flannel 0.9.0
  • kube-dns 1.14.5
  • dashboard 1.7.1

升级kube-dns的注意事项

kube-dns 1.14.5部署文件的地址在gitlab中发生了变化,在内容上使用Deployment替换了Replication Controller。

wget https://raw.githubusercontent.com/kubernetes/kubernetes/e1d6bcc22736a15ce662b3bd1009a16cdde5cd86/cluster/addons/dns/kube-dns.yaml.base
wget https://raw.githubusercontent.com/kubernetes/kubernetes/e1d6bcc22736a15ce662b3bd1009a16cdde5cd86/cluster/addons/dns/transforms2sed.sed

查看transforms2sed.sed:

s/__PILLAR__DNS__SERVER__/$DNS_SERVER_IP/g
s/__PILLAR__DNS__DOMAIN__/$DNS_DOMAIN/g
s/__MACHINE_GENERATED_WARNING__/Warning: This is a file generated from the base underscore template file: __SOURCE_FILENAME__/g

将$DNS_SERVER_IP替换成10.96.0.10,将DNS_DOMAIN替换成cluster.local。 注意$DNS_SERVER_IP要和kubelet设置的–cluster-dns参数一致

执行:

cd ~/k8s/kube-dns
sed -f transforms2sed.sed kube-dns.yaml.base > kube-dns.yaml
  • 上面的变量DNS_SERVER要和kubelet设置的–cluster-dns参数一致。
kubectl create -f kube-dns.yaml

查看kube-dns的Pod,确认所有Pod都处于Running状态:

kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   kube-dns-3468831164-chjw5               3/3       Running   0          3m

测试一下DNS功能是否好用:

kubectl run curl --image=radial/busyboxplus:curl -i --tty

nslookup kubernetes.default
Server:    10.96.0.10
Address 1: 10.96.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes
Address 1: 10.96.0.1 kubernetes.default.svc.cluster.local

kube-dns是Kubernetes实现服务发现的重要组件之一,默认情况下只会创建一个DNS Pod,在生产环境中我们可能需要对kube-dns进行扩容。 有两种方式:

  • 手动扩容 kubectl –namespace=kube-system scale deployment kube-dns –replicas=

  • 使用DNS Horizontal Autoscaler

升级dashboard的注意事项

因为之前部署Dashboard 1.6时创建了ServiceAccount kubernetes-dashboard,并分配了cluster-admin的权限。 所以在升级前应该先删除掉这个ServiceAccount:

kubectl delete  serviceaccount kubernetes-dashboard -n kube-system
kubectl delete  clusterrolebinding kubernetes-dashboard

1.7.x版本的dashboard对安全做了增强,默认需要以https的方式访问,增加了登录的页面,同时增加了一个gcr.io/google_containers/kubernetes-dashboard-init-amd64的init容器。

另外需要注意dashboard调整了部署文件的源码目录结构:

mkdir -p ~/k8s/
wget https://raw.githubusercontent.com/kubernetes/dashboard/b44f7cc5fde4dbe2a884b1e32a2b363d8031e4ca/src/deploy/recommended/kubernetes-dashboard.yaml
kubectl create -f kubernetes-dashboard.yaml

kubernetes-dashboard.yaml文件中的ServiceAccount kubernetes-dashboard只有相对较小的权限,因此我们创建一个kubernetes-dashboard-admin的ServiceAccount并授予集群admin的权限,创建kubernetes-dashboard-admin.rbac.yaml:

---
apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard-admin
  namespace: kube-system

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard-admin
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard-admin
  namespace: kube-system
kubectl create -f kubernetes-dashboard-admin.rbac.yaml
serviceaccount "kubernetes-dashboard-admin" created
clusterrolebinding "kubernetes-dashboard-admin" created

查看kubernete-dashboard-admin的token:

kubectl -n kube-system get secret | grep kubernetes-dashboard-admin
kubernetes-dashboard-admin-token-mrngz   kubernetes.io/service-account-token   3         18m

kubectl describe -n kube-system secret kubernetes-dashboard-admin-token-mrngz
Name:           kubernetes-dashboard-admin-token-mrngz
Namespace:      kube-system
Labels:         <none>
Annotations:    kubernetes.io/service-account.name=kubernetes-dashboard-admin
                kubernetes.io/service-account.uid=906d3aa6-b06d-11e7-afdd-080027d9d784

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1302 bytes
namespace:      11 bytes
token:          ......

在dashboard的登录窗口使用上面的token登录。

使用Docker和Kubernetes构建可伸缩的微服务

从现在开始,我们将从更高的维度讨论微服务,涵盖了组织敏捷性、设计和依赖的思考、领域驱动设计以及Promise理论。当我们深入使用之前介绍的三个流行的微服务框架:Spring Boot、Dropwizard和WildFly Swarm,我们能够使用它们开箱即用的能力去构建一个暴露或者消费REST服务的应用,能够使用外部环境对应用进行配置,可以打包成一个可执行的jar,同时提供Metrics信息,但这些都是围绕着一个微服务实例。当我们需要管理微服务之间的依赖、集群的启动和关闭、健康检查以及负载均衡的时候,我们使用微服务架构会面临什么问题呢?本章,我们将讨论这些高阶话题用来理解部署微服务时面对的挑战。

当我们开始将我们的应用和服务按照微服务的思路进行拆分后,我们将面临这样的场景:我们有了更多的服务、更多的二进制内容、更多的配置,更多的交互点等等。传统方式是将这些构建成一个二进制单元,比如:WARs或者EARs,然后将其打包后等待运维人员将它部署到我们指定的应用服务器上。如果对于高可用有要求,也会将应用服务器进行分布式部署,形成集群,依靠负载均衡、共享磁盘(数据库)等方式提升可用性。传统运维体系下也开发了一些自动化部署的工具,比如:Chef和Ansible,工具虽然简化了部署,但是开发人员还是需要面对部署时容易出现的问题,比如:配置、环境等不可预知的问题。
  • chef
    Chef是由Ruby与Erlang写成的配置管理软件,它以一种纯Ruby的领域专用语言(DSL)保存系统配置“烹饪法(recipes)”或“食谱(cookbooks)”。Chef由Opscode公司开发,并在Apache协议版本2.0下开源发布。

  • ansible
    使用python构建,中文化资料比较多,Ansible的简洁界面和可用性非常迎合系统管理员的想法。

在传统方式下尝试微服务的部署,将会是个糟糕的结果。如何解决应用服务器在开发、测试以及生产环境的不同配置?如果没有,如何能够捕获到这些配置的变更?而这些变更如何确认已经运行在应用服务器中了?运行时软件环境,比如:操作系统、JVM以及相关的组件在生产和开发环境下的不同问题如何解决?如果我们的应用已经针对特定的JVM做了调优,这些调优参数会不会影响到他人?如果部署微服务,你会选择使用进程隔离的方式将它们部署在一台机器上吗?如果其中一个微服务实例消耗了系统100%的资源,该如何是好?如果过度的占用了I/O或者共享存储怎么办?如果部署了多个微服务实例的宿主机崩溃了怎么办?我们的应用为此做过应对方案吗?将应用分拆为微服务是小,但面对的问题显然会更多。

不可变的递交

不可变的递交(Immutable delivery)原则可以帮助我们应对上述的部分问题,在这个体系下,我们将使用镜像技术来尝试减少开发到生产的步骤。例如:构建系统能够输出一个包含了操作系统、JVM、配置、相关组件的镜像,我们可以将它部署到一个环境中,测试它,如果通过测试,最终可以将它部署到生产环境中而不用担心开发流程使交付的软件缺少了什么。如果你想变更应用,那么可以回到刚才这个流程的最开始,应用你的修改,重新构建镜像,最终完成部署,如果出乎你的意料,程序有问题,你可以直接选择回滚到上一个正确的镜像而不用担心遗漏了什么。

这听起来很好,但是我们怎么做到呢?将应用打包成一个jar还不足以足够做到这些。JVM是底层实现,我们如何将它也打包进去,而JVM又使用了操作系统级别组件,这些内容我们都要打包,除此之外,我们还需要配置、环境变量、权限等等,这些都需要打包,而这些内容无法被打包到一个可执行jar中去。更加重要的是,不止java一种微服务,如果程序使用NodeJS、Golang编写,我们还要针对不同的语言环境做不同的打包。你可能想使用自动化手段完成这些软件的安装,将基础设施作为服务(IaaS),使用它们的API完成环境的搭建。事实上Netflix已经使用了自动化构建工具来完成VM的构建,并利用这项技术实现了不可变的递交,但是VM很难管理、更新和变更,而每个VM都有自己完备的虚拟化环境,对资源有些浪费。

那么有什么更加轻量化的打包和镜像化方式让我们使用吗?

Docker,Docker,Docker

Docker是近几年出现用于解决不可变递交的优雅解决方案,它允许我们将应用以及应用的所有依赖(包括了:OS,JVM以及其他组件)打包成为一个轻量的、分层的镜像格式。然后Docker使用这些镜像,运行它们,产生实例,而这些实例都运行在Linux containers中,在Linux containers中,会带来CPU、内存、网络以及磁盘的隔离。在这种模式下,这些容器实例就是一种应用虚拟化的方式,它运行一个进程去执行,你甚至可以在实例中运行ps查看你的进程,而且这个容器实例具备访问CPU、内存、磁盘和网络的能力,但是它只能使用指定好的配额。例如:能够启动一个Docker容器,只为它分配一部分的CPU、内存以及I/O的访问限制。如果在Linux containers外部去看,在主机上,这个容器就是一个进程,不需要设备驱动的虚拟化、操作系统、网络栈以及特殊的中间层,它仅仅是一个进程。这意味着,我们可以在一台机器上部署尽可能多的容器,提供了比虚拟机更高的部署密度。

在这些激动人心的特性下,其实没有革命性的技术。Docker使用到的技术有:cgroups、namespaces以及chroot,这些都已经在Linux内核中运行了相当长的时间,而这些技术被Docker用来构造应用虚拟化技术。Linux containers已经推出了十几年,而进程虚拟化技术在Solaris和FreeBSD上出现的时间更早。以往使用这些技术的lxc会比较复杂,而Docker通过简单的API以及优秀的用户体验使得Linux containers的运用变得火热起来,Docker通过一个客户端命令工具能够与Linux containers进行交互,去部署Docker镜像,而Docker镜像的出现改变了我们打包和交付软件的方式。

一旦你拥有了镜像,可以迅速的转化为Linux containers,镜像是按照层进行构建的,一般会在一个基础的层(例如:RHEL、 Debian等)上进行构建,然后包含应用所需的内容,构建应用其实也就是在基础层上进行一层一层的镜像构建。镜像的出现,是的发布到各种环境变得容易,不会在面对一堆零散的内容,如果发现基础镜像中有问题,可以进行重新构建,其他镜像进行重新选择构建即可,这使得从开发环境到测试,再到生产环境减少了人工干预发布内容的环节,如果我们要新发布一版本,只需要重新构建一个镜像即可,而改动只是去修改了镜像中对应的层。

构建了镜像,但是我们怎样启动一个应用?怎样停止它?怎样做健康检查?怎样收集应用的日志、Metrics等信息,使用标准的API可以使我们自己构建工具来完成这些工作。出色的集群机制,例如服务发现、负载均衡、失败容错以及配置使得开发人员很容易获得这些特性。

Docker相关的技术可以关注 https://www.gitbook.com/book/weipeng2k/the-docker-book/details

Kubernetes

外界都知晓Google使用Linux containers技术来支撑其扩展性,事实上Google的所有应用都运行在Linux containers上,并且被他们的管理系统Brog进行着管理。前Google工程师Joe Beda说,公司每周要启动超过20亿次的容器,Google甚至投入资源涉及到linux底层技术来支持其容器在生产环境的运用。在2006年,Google开始了一个名叫 进程容器 的项目,最终演变成为了cgroups,而它在2008被合并到了Linux核心,同年正式发布。Google在拥有极强的运维容器的背景下,其对构建容器平台的影响力就不言而喻了,事实上,一些流行的容器管理项目都受到了Google的影响。
  • Cloud Foundry
    它的创立者Derek Collison和Vadim Spivak都在Google工作过,并且使用Borg系统很多年

  • Apache Mesos
    它的创立者Ben Hindman在Google实习过,与Google的诸多工程师有过容器技术的交流(围绕容器集群、调度和管理等技术)

  • Kubernetes
    开源的容器集群管理平台和社区,创建它的工程师,同时也在Google创建了Borg

    在Docker震惊技术届的2013年,Google决定是时候开源他们下一代的技术–Borg,而它被命名为Kubernetes。今天,Kubernetes是一个巨大、开放和快速成长的社区,来自Google、Red Hat、CoreOS以及其他的个体在为它做出贡献。Kubernetes为在可伸缩的Linux containers下运行微服务提供了非常多有价值的功能,Google将近20年的运维经验都浓缩到了Kubernetes,这对我们使用微服务部署产生了巨大的影响。大部分高流量的互联网企业在这个领域耕耘了很长时间(Netflix、Amazon等)尝试构建的伸缩技术,在Kubernetes中都已经默认进行了集成,在正式深入例子之前,我们先介绍一些Kubernetes的概念,接下来在后面的章节将会用它来挂历一个微服务集群。

Pods

一个Pod是一个或者多个Docker容器的组合,一般情况下一个Pod对应一个Docker容器,应用部署在其中。

Kubernetes进行编排、调度以及管理Pod,当我们谈到一个运行在Kubernetes中的应用时,指的是运行在Pod中的Docker容器。一个Pod有自己的IP地址,所有运行在这个Pod中的容器共享这个IP(这个不同于普通的Docker容器,普通的Docker容器每个实例都有一个IP),当一个卷挂载到Pod,这个卷也能够被Pod中的容器共同访问。

关于Pod需要注意的一点是:它们是短暂的,这代表着它们会在任何时候消失(不是因为服务崩溃就是集群cluster杀死了它),它们不像VM一样引起你的额外注意。Pods能够在任意时刻被销毁,而这种意外的失败就如同介绍微服务架构中任何事情都会失败一样(design for failure),我们强烈建议在编写微服务时时刻记着这个建议。和之前介绍的其他原则相比,这个建议显得更加重要。

Kubernetes的最小部署单元是Pod而不是容器。作为First class API公民,Pods能被创建,调度和管理。简单地来说,像一个豌豆荚中的豌豆一样,一个Pod中的应用容器同享同一个上下文(比如:PID名字空间、网络等)。在实际使用时,我们一般不直接创建Pods, 我们通过replication controller来负责Pods的创建,复制,监控和销毁。一个Pod可以包括多个容器,他们直接往往相互协作完成一个应用功能。

标签(Label)

标签(Label)是一个能分配给Pods的简单键值对,比如:release=stable或者tier=backend,Pods(或者其他资源,但是我们当前只关注Pods)可以拥有多个标签并且可以以松耦合的方式进行分组,这在Kubernetes的使用过程中非常常见。因此一点也不奇怪,Google使用这种简单的方式用来区分不同的容器,并以此来构建大规模伸缩的集群。当我们用标签区分了Pods之后,我们可以使用 label selector 来按照分组来查询所有的Pods,例如:如果我们有一些Pods打上了tier=backend的标签,而其他的一些打上了tier=frontend标签,只需要使用 label selector 表达式 tier != frontend就可以完成对所有没有打上tier=frontend的Pods进行查询,而 label selector 在接下来介绍的 replication controllers 和 services 所使用。

复制控制器(Replication Controllers)

当我们讨论微服务的可伸缩性时,可能想的是将给定的一组微服务部署到多个实例(机器)上,用多个实例的部署来增加伸缩性。Kubernetes为伸缩性定义了一个叫做 Replication Controllers 的概念,它能够管理给定的一组微服务的多个复制体(replicas),例如:我们需要管理许多打上 tier=backend and release=stable 的需要Pods,可以创建一个复制控制器,该控制器拥有对应的 label selector ,此时它就能够在集群中以replicas的形式控制和管理这些Pods。如果我们设置replica的数量为10,当Kubernetes会确定当前的复制控制器是否达到了该状态,如果此刻只有5个,那么Kubernetes就会循环创建剩余的5个,当有20个运行着,Kubernetes将会选择停止10个。Kubernetes将会尽可能的保持设定的10个replica的状态,你可以认为使用复制控制器来控制集群的数量是非常容易的事情,在接下来的章节中,我们会看到使用复制控制器的例子。

服务(Services)

我们最后需要理解的Kubernetes概念是服务(Service), Replication Controllers 能控制一个服务下的多个复制体(replicas),我们也观察到Pods能够被停止(要么自己crash、或者被kill,也有可能被复制控制器停止),因此,当我们尝试与一组Pods进行通信时,不应该依赖于具体的IP(每个Pod都有自己的IP),我们需要的是能够以组的形式访问这些Pods的方式,以组的形式发现它们,可能的话能够以负载均衡的方式访问它们,这个就是 服务(Service) 需要做的。它(服务)允许我们通过一个 label selector 获取一组Pods,将它们抽象为一个虚拟IP,然后以这个虚拟IP来让我们对这些Pods进行发现和交互,我们将在接下来的章节中介绍具体的例子。

Service是定义一系列Pod以及访问这些Pod的策略的一层抽象。Service通过Label找到Pod组。因为Service是抽象的,所以在图表里通常看不到它们的存在

了解这些简单的概念,Pods、Labels、Replication Controllers和services,我们能够以可伸缩的模式,用Google的实践,来管理微服务。这些实践花费了多年,经历了多次失败总结出来的经验之谈,而这个模式能够解决复杂的问题,因此强烈建议学习这些概念以及实践,使用Kubernetes来管理你的微服务。

开始使用Kubernetes

Docker和Kubernetes都是基于Linux本地技术的产品,因此它们需要运行在一个基于Linux的环境中,我们假设大部分的Java开发人员都是工作在Windows或者Mac下,我们推荐在Linux环境下进行相关的实践。

接下来的内容,作者作为redhat的员工,开始介绍CDK(RedHat Container Development Kit),然后是CDK的安装,译者觉得CDK没有多大的参考性,因此将其替换成了对Kubernetes官方的MiniKube使用,并基于MiniKube在linux机器上搭建Kubernetes。

Kubernetes之MiniKube的安装

笔者准备了aliyun oss 下载,比googleapis快许多

该文档介绍如何运行起一个本地Kubernetes集群,需要一个支持Hyper-V虚拟化的CPU以及至少8GB CPU

笔者的环境是 ubuntu 16.04 / amd k8 4 core CPU / 16 gb mem

需要提前安装VirtualBox5.1,自行到官网上进行安装,不要图简单使用ubuntu默认的,那个平常自己使没问题,但是MiniKube不行

安装MiniKube

wget http://029145.oss-cn-hangzhou.aliyuncs.com/minikube-linux-amd64 mv minikube-linux-amd64 minikube chmod u+x minikube sudo mv minikube /usr/local/bin/ 

安装Kubectl

wget http://029145.oss-cn-hangzhou.aliyuncs.com/kubectl chmod u+x kubectl sudo mv kubectl /usr/local/bin/ 

启动MiniKube

通过以下命令启动minikube,该过程会下载一个ISO镜像,然后完成启动。

minikube start 

下载依赖的镜像

这个过程最为复杂,当启动minikube时,会自动下载一些镜像,但是这些镜像都被墙了,但是我们可以从aliyun的仓库下载对应的镜像,然后将其重命名。在启动完minikube后,使用minikube ssh可以登录到后台,然后运行下面的命令完成镜像的下载和别名设置。

docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/pause-amd64:3.0 gcr.io/google_containers/pause-amd64:3.0  docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/kube-addon-manager-amd64:v6.1 docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/kube-addon-manager-amd64:v6.1 gcr.io/google-containers/kube-addon-manager:v6.1  docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/kubedns-amd64:1.9 docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/kubedns-amd64:1.9 gcr.io/google_containers/kubedns-amd64:1.9  docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/kube-dnsmasq-amd64:1.4 docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/kube-dnsmasq-amd64:1.4 gcr.io/google_containers/kube-dnsmasq-amd64:1.4  docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/exechealthz-amd64:1.2 docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/exechealthz-amd64:1.2 gcr.io/google_containers/exechealthz-amd64:1.2  docker pull registry.cn-hangzhou.aliyuncs.com/google-containers/kubernetes-dashboard-amd64:v1.5.0 docker tag registry.cn-hangzhou.aliyuncs.com/google-containers/kubernetes-dashboard-amd64:v1.5.0 gcr.io/google_containers/kubernetes-dashboard-amd64:v1.5.1 

测试echoserver

运行命令创建一个echoserver服务,运行如下命令:

kubectl run hello-minikube --image=registry.cn-hangzhou.aliyuncs.com/google-containers/echoserver:1.4 --port=8080 kubectl expose deployment hello-minikube 

然后运行minikube service hello-minikube –url,将会返回hello-minikube的url,然后可以基于该url做一下测试。

$ minikube service hello-minikube --url http://192.168.99.100:31907 $ curl http://192.168.99.100:31907/123 CLIENT VALUES: client_address=172.17.0.1 command=GET real path=/123 query=nil request_version=1.1 request_uri=http://192.168.99.100:8080/123  SERVER VALUES: server_version=nginx: 1.10.0 - lua: 10001  HEADERS RECEIVED: accept=*/* host=192.168.99.100:31907 user-agent=curl/7.47.0 BODY: -no body in request- 

可以看到请求对应的url,有数据返回,当然也可以启动dashboard,比如运行minikube dashboard将会打开管理页面。

Kubernetes之“暂停”容器

【编者的话】希望这篇文章可以帮助大家更好的了解Kubernetes的相关核心内容。

当检查你的Kubernetes集群的节点时,在节点上执行命令docker ps,你可能会注意到一些被称为“暂停(/pause)”的容器。

$ docker ps
CONTAINER ID IMAGE COMMAND ...
...
3b45e983c859 gcr.io/google_containers/pause-amd64:3.0  “/pause” ...
...
dbfc35b00062 gcr.io/google_containers/pause-amd64:3.0  “/pause” ...
...
c4e998ec4d5d gcr.io/google_containers/pause-amd64:3.0  “/pause” ...
...
508102acf1e7 gcr.io/google_containers/pause-amd64:3.0  “/pause” ...

这些“暂停”容器是啥,而且还这么多暂停的?这到底是什么情况?

未分类

为了回答这些问题,我们需要退一步看看Kubernetes中的pods如何实现,特别是在Docker/containerd运行时。如果你还不知道怎么做,可以先阅读我以前发表的关于Kubernetes pods 的文章(http://dockone.io/article/2682)。

Docker支持容器,这非常适合部署单个软件单元。但是,当你想要一起运行多个软件时,这种模式会变得有点麻烦。当开发人员创建使用supervisord作为入口点的Docker镜像来启动和管理多个进程时,你经常会看到这一点。对于生产系统,许多人发现,将这些应用程序部署在部分隔离并部分共享环境的容器组中更为有用。

Kubernetes为这种使用场景提供了一个称为Pod的干净的抽象。它在隐藏Docker标志的复杂性的同时会保留容器,共享卷等。它也隐藏了容器运行时的差异。例如,rkt原生支持Pod,所以Kubernetes的工作要少一些,但是不管怎样,作为Kubernetes用户的你都不用担心(Pod会帮你搞定这些)。

原则上,任何人都可以配置Docker来控制容器组之间的共享级别——你只需创建一个父容器,在知道正确的标志配置的情况下来设置并创建共享相同环境的新容器,然后管理这些容器的生命周期。而管理所有这些片段的生命周期可能会变得相当复杂。

在Kubernetes中,“暂停”容器用作你的Pod中所有容器的“父容器”。“暂停”容器有两个核心职责。首先,在Pod中它作为Linux命名空间共享的基础。其次,启用PID(进程ID)命名空间共享,它为每个Pod提供PID 1,并收集僵尸进程。

共享命名空间

在Linux中,当你运行新进程时,该进程从父进程继承其命名空间。在新命名空间中运行进程的方法是通过“取消共享”命名空间(与父进程),从而创建一个新的命名空间。以下是使用该unshare工具在新的PID,UTS,IPC和装载命名空间中运行shell的示例。

sudo unshare --pid --uts --ipc --mount -f chroot rootfs / bin / sh

一旦进程运行,你可以将其他进程添加到进程的命名空间中以形成一个Pod。可以使用setns系统调用将新进程添加到现有命名空间。

Pod中的容器在其中共享命名空间。Docker可让你自动执行此过程,因此,让我们来看一下如何使用“暂停”容器和共享命名空间从头开始创建Pod的示例。首先,我们将需要使用Docker启动“暂停”容器,以便我们可以将容器添加到Pod中。

docker run -d --name pause gcr.io/google_containers/pause-amd64:3.0

然后我们可以运行我们的Pod的容器。首先我们将运行Nginx。这将在端口2368上设置Nginx到其localhost的代理请求。

$ cat <<EOF >> nginx.conf
> error_log stderr;
> events { worker_connections  1024; }
> http {
>     access_log /dev/stdout combined;
>     server {
>         listen 80 default_server;
>         server_name example.com www.example.com;
>         location / {
>             proxy_pass http://127.0.0.1:2368;
>         }
>     }
> }
> EOF
$ docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 --net=container:pause --ipc=container:pause --pid=container:pause nginx 

然后,我们将为作为我们的应用服务器的ghost博客应用程序创建另一个容器。

$ docker run -d --name ghost --net = container:pause --ipc = container:pause --pid = container:pause ghost

在这两种情况下,我们将“暂停”容器指定为我们要加入其命名空间的容器。这将有效地创建我们的Pod。如果你访问 http://localhost:8080/ 你应该能够看到ghost通过Nginx代理运行,因为网络命名空间在pause,nginx和ghost容器之间共享。

未分类

如果你觉得这一切好复杂,恭喜你,大家都这么就觉得;它确实很复杂(感觉像句废话)。而且我们甚至还没有了解如何监控和管理这些容器的生命周期。不过,值得庆幸的事,通过Pod,Kubernetes会为你很好地管理所有这些。

收割僵尸

在Linux中,PID命名空间中的所有进程会形成一个树结构,每个进程都会有一个父进程。只有在树的根部的进程没有父进程。这个进程就是“init”进程,即PID 1。

进程可以使用fork和exec syscalls启动其他进程。当启动了其他进程,新进程的父进程就是调用fork syscall的进程。fork用于启动正在运行的进程的另一个副本,而exec则用于启动不同的进程。每个进程在OS进程表中都有一个条目。这将记录有关进程的状态和退出代码。当子进程运行完成,它的进程表条目仍然将保留直到父进程使用wait syscall检索其退出代码将其退出。这被称为“收割”僵尸进程。

未分类

僵尸进程是已停止运行但进程表条目仍然存在的进程,因为父进程尚未通过wait syscall进行检索。从技术层面来说,终止的每个进程都算是一个僵尸进程,尽管只是在很短的时间内发生的,但只要不终止他们就可以存活更久。

当父进程wait在子进程完成后不调用syscall时,会发生较长的生存僵尸进程。这样的情况有很多,比如:当父进程写得不好并且简单地省略wait call时,或者当父进程在子进程之前死机,并且新的父进程没有调用wait去检索子进程时。当进程的父进程在子进程之前死机时,OS将子进程分配给“init”进程即PID 1。init进程“收养”子进程并成为其父进程。这意味着现在当子进程退出新的父进程(init)时,必须调用wait 来获取其退出代码否则其进程表项将保持永远,并且它也将成为一个僵尸进程。

在容器中,一个进程必须是每个PID命名空间的init进程。使用Docker,每个容器通常都有自己的PID命名空间,ENTRYPOINT进程是init进程。然而,正如我在上一篇关于Kubernetes Pods的文章 (http://dockone.io/article/2682) 中所指出的,某个容器可以在另一个容器的命名空间中运行。在这种情况下,这个容器必须承担init进程的角色,而其他容器则作为init进程的子进程添加到命名空间中。

在Kubernetes Pods的文章中,我在一个容器中运行Nginx,并将ghost添加到了Nginx容器的PID命名空间。

$ docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf -p 8080:80 nginx
$ docker run -d --name ghost --net=container:nginx --ipc=container:nginx --pid=container:nginx ghost

在这种情况下,Nginx将承担PID 1的作用,并将ghost添加为Nginx的子进程。虽然这样貌似不错,但从技术上来看,Nginx现在需要负责任何ghost进程的子进程。例如,如果ghost分身或者使用子进程运行exec,并在子进程完成之前崩溃,那么这些子进程将被Nginx收养。但是,Nginx并不是设计用来作为一个init进程运行并收割僵尸进程的。这意味着将会有很多的这种僵尸进程,并且在整个容器的生命周期,他们都将持续存活。

在Kubernetes Pods中,容器的运行方式与上述方式大致相同,但是每个Pod都有一个特殊的“暂停”容器。这个“暂停”容器运行一个非常简单的进程,它不执行任何功能,基本上是永远睡觉的(见pause()下面的调用)。因为它比较简单,在这里写下完整的源代码,如下:

/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
include <signal.h>

include <stdio.h>

include <stdlib.h>

include <sys/types.h>

include <sys/wait.h>

include <unistd.h>

static void sigdown(int signo) {
psignal(signo, "Shutting down, got signal");
exit(0);
}

static void sigreap(int signo) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}

int main() {
if (getpid() != 1)
/* Not an error because pause sees use outside of infra containers. */
fprintf(stderr, "Warning: pause should be the first processn");

if (sigaction(SIGINT, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 1;
if (sigaction(SIGTERM, &(struct sigaction){.sa_handler = sigdown}, NULL) < 0)
return 2;
if (sigaction(SIGCHLD, &(struct sigaction){.sa_handler = sigreap,
                                         .sa_flags = SA_NOCLDSTOP},
            NULL) < 0)
return 3;

for (;;)
pause();
fprintf(stderr, "Error: infinite loop terminatedn");
return 42;
} 

正如你所看到的,它当然不会只知道睡觉。它执行另一个重要的功能——即它扮演PID 1的角色,并在子进程被父进程孤立的时候通过调用wait 来收割这些僵尸子进程(参见sigreap)。这样我们就不用担心我们的Kubernetes Pods的PID命名空间里会堆满僵尸了。

PID命名空间共享的一些上下文

值得注意的是,PID命名空间共享已经有了很多的前后关系。如果你启用了PID命名空间共享,那么只能通过暂停容器来收割僵尸,并且目前这一功能仅在Kubernetes 1.7+以上的版本中可用。如果使用Docker 1.13.1+运行Kubernetes 1.7,这一功能默认是开启的,除非使用kubelet标志(–docker-disable-shared-pid=true)禁用。这在Kubernetes 1.8 中正好相反的,现在默认情况下是禁用的,除非由kubelet标志(–docker-disable-shared-pid=false)启用。感兴趣的话,可以看看在GitHub issue中对增加支持PID命名空间共享的有关讨论。

如果没有启用PID命名空间共享,则Kubernetes Pod中的每个容器都将具有自己的PID 1,并且每个容器将需要收集僵尸进程本身。很多时候,这不是一个问题,因为应用程序不会产生其他进程,但僵尸进程使用内存是一个经常被忽视的问题。因此,由于PID命名空间共享使你能够在同一个Pod中的容器之间发送信号,我衷心的希望PID命名空间共享可以成为Kubernetes中的默认选项。

使用Jenkins进行持续构建与发布应用到Kubernetes集群中

我们基于Jenkins的CI/CD流程如下所示。

未分类

流程说明

应用构建和发布流程说明。

  1. 用户向Gitlab提交代码,代码中必须包含Dockerfile;

  2. 将代码提交到远程仓库;

  3. 用户在发布应用时需要填写git仓库地址和分支、服务类型、服务名称、资源数量、实例个数等,确定后触发Jenkins自动构建;

  4. Jenkins的CI流水线自动编译代码并打包成docker镜像推送到Harbor镜像仓库;

  5. Jenkins的CI流水线中包括了自定义脚本,根据我们已准备好的kubernetes的YAML模板,将其中的变量替换成用户输入的选项;

  6. 生成应用的kubernetes YAML配置文件;

  7. 更新Ingress的配置,根据新部署的应用的名称,在ingress的配置文件中增加一条路由信息

  8. 更新PowerDNS,向其中插入一条DNS记录,IP地址是边缘节点的IP地址。关于边缘节点,请查看kubernetes-handbook中的【最佳实践——边缘节点配置】章节;

  9. Jenkins调用kubernetes的API,部署应用到kubernetes集群中。

使用 Docker 和 Kubernetes 将 MongoDB 作

介绍

想在笔记本电脑上尝试 MongoDB?只需执行一个命令,你就会有一个轻量级的、独立的沙箱。完成后可以删除你所做的所有痕迹。

想在多个环境中使用相同的程序栈application stack副本?构建你自己的容器镜像,让你的开发、测试、运维和支持团队使用相同的环境克隆。

容器正在彻底改变整个软件生命周期:从最早的技术性实验和概念证明,贯穿了开发、测试、部署和支持。

编排工具用来管理如何创建、升级多个容器,并使之高可用。编排还控制容器如何连接,以从多个微服务容器构建复杂的应用程序。

丰富的功能、简单的工具和强大的 API 使容器和编排功能成为 DevOps 团队的首选,将其集成到连续集成(CI) 和连续交付 (CD) 的工作流程中。

这篇文章探讨了在容器中运行和编排 MongoDB 时遇到的额外挑战,并说明了如何克服这些挑战。

MongoDB 的注意事项

使用容器和编排运行 MongoDB 有一些额外的注意事项:

  • MongoDB 数据库节点是有状态的。如果容器发生故障并被重新编排,数据则会丢失(能够从副本集的其他节点恢复,但这需要时间),这是不合需要的。为了解决这个问题,可以使用诸如 Kubernetes 中的 数据卷volume 抽象等功能来将容器中临时的 MongoDB 数据目录映射到持久位置,以便数据在容器故障和重新编排过程中存留。

  • 一个副本集中的 MongoDB 数据库节点必须能够相互通信 – 包括重新编排后。副本集中的所有节点必须知道其所有对等节点的地址,但是当重新编排容器时,可能会使用不同的 IP 地址重新启动。例如,Kubernetes Pod 中的所有容器共享一个 IP 地址,当重新编排 pod 时,IP 地址会发生变化。使用 Kubernetes,可以通过将 Kubernetes 服务与每个 MongoDB 节点相关联来处理,该节点使用 Kubernetes DNS 服务提供“主机名”,以保持服务在重新编排中保持不变。

  • 一旦每个单独的 MongoDB 节点运行起来(每个都在自己的容器中),则必须初始化副本集,并添加每个节点到其中。这可能需要在编排工具之外提供一些额外的处理。具体来说,必须使用目标副本集中的一个 MongoDB 节点来执行 rs.initiate 和 rs.add 命令。

  • 如果编排框架提供了容器的自动化重新编排(如 Kubernetes),那么这将增加 MongoDB 的弹性,因为这可以自动重新创建失败的副本集成员,从而在没有人为干预的情况下恢复完全的冗余级别。

  • 应该注意的是,虽然编排框架可能监控容器的状态,但是不太可能监视容器内运行的应用程序或备份其数据。这意味着使用 MongoDB Enterprise Advanced [1] 和 MongoDB Professional [2] 中包含的 MongoDB Cloud Manager [3] 等强大的监控和备份解决方案非常重要。可以考虑创建自己的镜像,其中包含你首选的 MongoDB 版本和 MongoDB Automation Agent [4]。

使用 Docker 和 Kubernetes 实现 MongoDB 副本集

如上节所述,分布式数据库(如 MongoDB)在使用编排框架(如 Kubernetes)进行部署时,需要稍加注意。本节将介绍详细介绍如何实现。

我们首先在单个 Kubernetes 集群中创建整个 MongoDB 副本集(通常在一个数据中心内,这显然不能提供地理冗余)。实际上,很少有必要改变成跨多个集群运行,这些步骤将在后面描述。

副本集的每个成员将作为自己的 pod 运行,并提供一个公开 IP 地址和端口的服务。这个“固定”的 IP 地址非常重要,因为外部应用程序和其他副本集成员都可以依赖于它在重新编排 pod 的情况下保持不变。

下图说明了其中一个 pod 以及相关的复制控制器和服务。

未分类

逐步介绍该配置中描述的资源:

  • 从核心开始,有一个名为 mongo-node1 的容器。 mongo-node1 包含一个名为 mongo 的镜像,这是一个在 Docker Hub [5] 上托管的一个公开可用的 MongoDB 容器镜像。容器在集群中暴露端口 27107。

  • Kubernetes 的数据卷功能用于将连接器中的 /data/db 目录映射到名为 mongo-persistent-storage1 的永久存储上,这又被映射到在 Google Cloud 中创建的名为 mongodb-disk1 的磁盘中。这是 MongoDB 存储其数据的地方,这样它可以在容器重新编排后保留。

  • 容器保存在一个 pod 中,该 pod 中有标签命名为 mongo-node,并提供一个名为 rod 的(任意)示例。

  • 配置 mongo-node1 复制控制器以确保 mongo-node1 pod 的单个实例始终运行。

  • 名为 mongo-svc-a 的 负载均衡 服务给外部开放了一个 IP 地址以及 27017 端口,它被映射到容器相同的端口号上。该服务使用选择器来匹配 pod 标签来确定正确的 pod。外部 IP 地址和端口将用于应用程序以及副本集成员之间的通信。每个容器也有本地 IP 地址,但是当容器移动或重新启动时,这些 IP 地址会变化,因此不会用于副本集。

下一个图显示了副本集的第二个成员的配置。

未分类

90% 的配置是一样的,只有这些变化:

  • 磁盘和卷名必须是唯一的,因此使用的是 mongodb-disk2 和 mongo-persistent-storage2

  • Pod 被分配了一个 instance: jane 和 name: mongo-node2 的标签,以便新的服务可以使用选择器与图 1 所示的 rod Pod 相区分。

  • 复制控制器命名为 mongo-rc2

  • 该服务名为 mongo-svc-b,并获得了一个唯一的外部 IP 地址(在这种情况下,Kubernetes 分配了 104.1.4.5)

第三个副本成员的配置遵循相同的模式,下图展示了完整的副本集:

未分类

请注意,即使在三个或更多节点的 Kubernetes 群集上运行图 3 所示的配置,Kubernetes 可能(并且经常会)在同一主机上编排两个或多个 MongoDB 副本集成员。这是因为 Kubernetes 将三个 pod 视为属于三个独立的服务。

为了在区域内增加冗余,可以创建一个附加的 headless 服务。新服务不向外界提供任何功能(甚至不会有 IP 地址),但是它可以让 Kubernetes 通知三个 MongoDB pod 形成一个服务,所以 Kubernetes 会尝试在不同的节点上编排它们。

未分类

配置和启动 MongoDB 副本集所需的实际配置文件和命令可以在白皮书《启用微服务:阐述容器和编排[6]》中找到。特别的是,需要一些本文中描述的特殊步骤来将三个 MongoDB 实例组合成具备功能的、健壮的副本集。

多个可用区 MongoDB 副本集

上面创建的副本集存在风险,因为所有内容都在相同的 GCE 集群中运行,因此都在相同的可用区availability zone中。如果有一个重大事件使可用区离线,那么 MongoDB 副本集将不可用。如果需要地理冗余,则三个 pod 应该在三个不同的可用区或地区中运行。

令人惊奇的是,为了创建在三个区域之间分割的类似的副本集(需要三个集群),几乎不需要改变。每个集群都需要自己的 Kubernetes YAML 文件,该文件仅为该副本集中的一个成员定义了 pod、复制控制器和服务。那么为每个区域创建一个集群,永久存储和 MongoDB 节点是一件很简单的事情。

未分类

下一步

要了解有关容器和编排的更多信息 – 所涉及的技术和所提供的业务优势 – 请阅读白皮书《启用微服务:阐述容器和编排[7]》。该文件提供了获取本文中描述的副本集,并在 Google Container Engine 中的 Docker 和 Kubernetes 上运行的完整的说明。

Kubernetes连接外部数据源

Kubernetes架构下比较核心的问题是数据如何persistance,虽然提供了Persistent volumn的方式,但是对于像数据库之类的产品在kubernetes集群环境中运行和管理还是很有难度的,Kubernetes提供了endpoints这种模式让外部的服务映射成内部的服务,这样比较好的解决了集群对外的连接问题,

如果我们去连接外部的一个oracle数据库,具体的步骤如下:

  • 建立endpoints和service.
[root@k8s-master jdbcservice]# cat jdbc-endpoint.yaml 
apiVersion: v1
kind: Endpoints
metadata:
  name: jdbc
subsets:
  - addresses:
    - ip: 10.182.168.244
    ports:
    - port: 1521
      protocol: TCP

为了方便,我们固定了service的集群地址

[root@k8s-master jdbcservice]# cat jdbcservice.yaml 
apiVersion: v1
kind: Service
metadata:
  name: jdbc
spec:
  clusterIP: 10.254.150.201
  ports:
  - port: 1521
    targetPort: 1521
    protocol: TCP

需要注意的是,service和endpoints的名字要相同,另外如果delete了service.再重新建立的时候要再把endpoints建立一遍。

在这个service的表里,我们看到jdbc服务绑在了201这个地址上。

[root@k8s-master jdbcservice]# kubectl get services
NAME            CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
helloworldsvc   10.254.43.122    <nodes>       7001:30001/TCP   12m
jdbc            10.254.150.201   <none>        1521/TCP         30m
kubernetes      10.254.0.1       <none>        443/TCP          121d
registry        10.254.174.54    <nodes>       5000:30002/TCP   20h
  • images的配置

然后对我们的weblogic images进行jdbc的配置。

未分类

点击Test Configuration,如果测试不过,weblogic不允许建立连接池成功.

未分类

未分类

在运行pod的节点上运行docker ps找到启动image的容器id.

[root@k8s-node-1 ~]# docker ps
CONTAINER ID        IMAGE                                                        COMMAND                  CREATED             STATUS              PORTS               NAMES
d2c1dc2a2cef        1213-helloworld:v1                                           "startWebLogic.sh"       7 minutes ago       Up 7 minutes                            k8s_weblogichelloworld.9efb3a79_helloworld-service-vfd10_default_6162d68a-9da9-11e7-b746-08002797edef_026d2cc4
85e04042041a        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/pod"                   7 minutes ago       Up 7 minutes                            k8s_POD.15c40ba1_helloworld-service-vfd10_default_6162d68a-9da9-11e7-b746-08002797edef_b59984a7
96acfd65eb3a        registry                                                     "/entrypoint.sh /etc/"   23 minutes ago      Up 23 minutes                           k8s_registry.71ab5625_registry-7nj8q_default_19ab0b7f-9cff-11e7-bf9d-08002797edef_fb5ae620
63652932256c        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/pod"                   23 minutes ago      Up 23 minutes                           k8s_POD.100f0b9e_registry-7nj8q_default_19ab0b7f-9cff-11e7-bf9d-08002797edef_98dd7f3f
1ed61c53625f        gcr.io/google_containers/etcd-amd64:2.2.1                    "/usr/local/bin/etcd "   47 minutes ago      Up 47 minutes                           k8s_etcd.bb974d90_kube-dns-v11-x0vr3_kube-system_5dd26461-3ef1-11e7-acf2-08002797edef_7316989c
fb8545a4aba4        gcr.io/google_containers/exechealthz:1.0                     "/exechealthz '-cmd=n"   47 minutes ago      Up 47 minutes                           k8s_healthz.525e4aad_kube-dns-v11-x0vr3_kube-system_5dd26461-3ef1-11e7-acf2-08002797edef_cec4d740
aa6c4caf3fa7        gcr.io/google_containers/skydns:2015-10-13-8c72f8c           "/skydns -machines=ht"   47 minutes ago      Up 47 minutes                           k8s_skydns.96837166_kube-dns-v11-x0vr3_kube-system_5dd26461-3ef1-11e7-acf2-08002797edef_5da89b81
5930dae5b843        registry.access.redhat.com/rhel7/pod-infrastructure:latest   "/pod"                   47 minutes ago      Up 47 minutes                           k8s_POD.4efc54ff_kube-dns-v11-x0vr3_kube-system_5dd26461-3ef1-11e7-acf2-08002797edef_3e3af088

生成新的images

[root@k8s-node-1 ~]# docker commit -m "jdbc" -a "ericnie" d2c1dc2a2cef 1213-helloworld-jdbc:v1
sha256:953e124483d2bcc03b3f46c8e6d935e3746634d78942cc477e31888c8d569171
  • 验证

修改weblogic replication control指向1213-helloworld-jdbc:v1 images,启动后看到连接建立.

weblogic端

未分类

数据库端

数据库端原来的jdbc链接

未分类

Pod完全启动后的连接

未分类