说在前面

在开始之前有这样一个问题:用了kubernetes还要不要用springcloud?关于这个问题可以看下kubernetes社区翻译的一篇文章:Kubernetes和Spring Cloud哪个部署微服务更好?对于我(码农)来说对springcloud会比较熟悉,所以我选择了全套都用springcloud,只用kubernetes部署。

2022-05-14:

这个是1.18,建议直接看我新发的1.23版本。


部署方式

参阅kubernetes官方文档:Installing Kubernetes with kops | Kubernetes,安装方式有很多,测试环境我选择了kubeadm,简单快速并且根据官方文档的描述可以用于生产环境,缺点是当运行是出现一些BUG可能排错会有些困难。生产环境我选择的是二进制安装(还是有点麻烦),本文将记录kubeadm安装的方式,下一篇再记二进制

2022-03-18更新:

官方已经正式说明kubeadm可以用于生产环境,没必要再搞二进制了。


环境配置

CentOS7(三台,master+node01+node02),docker 18.06.0,kubelet-1.18.8,kubeadm-1.18.8,kubectl-1.18.8。

务必装1.18以上的版本,如果非要装1.18以下的那一定要重新编译源码改证书过期时间,否则就会移步


开始安装

关闭selinux和swap

关闭selinux

/usr/sbin/sestatus -v

关闭swap

echo "vm.swappiness = 0">> /etc/sysctl.conf

swapoff -a
sysctl -p

安装docker(三台都需要)

根据官方文档:Redirecting…

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

sudo yum install -y yum-utils \
  device-mapper-persistent-data \
  lvm2

sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

sudo yum install docker-ce-18.06.0.ce-3.el7
 docker-ce-cli-18.06.0.ce-3.el7
 containerd.io
//现在已经找不到docker-ce-cli-18.06.0.ce-3.el7这个版本了,不过这个是远程控制用的,不按也无所谓

修改docker镜像源:

mkdir /etc/docker

vim /etc/docker/daemon.json

{
    "registry-mirrors": ["https://y0qd3iq.mirror.aliyuncs.com"],
    "insecure-registries":["120.78.151.255:5000"],//此为私有仓库的仓库地址,后面会写
    "log-driver":"json-file",
    "log-opts": {"max-size":"500m", "max-file":"3"}  //限制日志大小
}

systemctl start docker.service

要使用k8s部署需要一个私有的镜像仓库,推荐harbor,我这时间比较赶就用了简易版,这个仓库可以安装在单独一台服务器上,我是直接装在master上了。

docker pull registry:2

docker run -d -v /usr/local/docker-registry:/var/lib/registry -e REGISTRY_STORAGE_DELETE_ENABLED=true -p 5000:5000 --name myregistry registry:2

上面的“insecure-registries”地址配置为安装了私有仓库的地址,注意REGISTRY_STORAGE_DELETE_ENABLED是开启删除镜像功能,某日掉坑里了,这个仓库大小涨的很快,轻轻松松几十个G。

至此docker安装完毕。


安装kubernetes(三台都需要)

注意kubernetes和docker有版本对应关系,具体可以在这里查看:https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.16.md#downloads-for-v1167

首先配置kubernetes的镜像源

vim /etc/yum.repos.d/kubernetes.repo

[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0

清空缓存

yum clean all

yum makecache

安装三件套

yum install -y kubelet-1.18.8 kubeadm-1.18.8 kubectl-1.18.8 --disableexcludes=Kubernete

2021-01更新:建议换1.19版本,最低也要1.18,这个1.16版本是以前部署的了。

注意如果你的环境无法访问外网,那么你需要找一台能访问外网的机器下载离线的rpm包,如下:

yum install --downloadonly --downloaddir=/usr/local/kubernetes-rpm kubelet-1.16.4 kubeadm-1.16.4 kubectl-1.16.4 --disableexcludes=Kubernete

这条命令可以把相关的所需包全部下载到本地但是不安装,下载完以后拖到需要安装的机器上用rpm安装。安装完以后还需要导入所需的镜像,使用

kubeadm config images list

查看安装所需要的镜像

k8s集群内 springcloud配置 k8s部署springcloud项目_docker

然后根据需要的列表去找到并导入(就是找台外网机器用docker下载镜像,打包出来在导入这台离线机器)。例子如下:

#可访问外网的机器
docker save -o /usr/local/kubernetes-data/kubernetes-offline-image/kube-controller-manager fb4cca6b4e4c

打包出这个镜像后,扔到离线机器上导入:

#离线机器
docker load -i kube-controller-manager.tar
#注意此时导入的镜像name等信息都是空的,需要手动加上
docker tag fb4cca6b4e4cb2f606da76df5c3227ca1769493d1a1f89fcc7e6351908fced20 k8s.gcr.io/kube-controller-manager:v1.16.4
#fb4cca6b4e4cb2f606da76df5c3227ca1769493d1a1f89fcc7e6351908fced20  是镜像id,我把他重命名为k8s.gcr.io/kube-controller-manager,版本是v1.16.4

如此往复把所需要的镜像都导入就可以了。如果你懒的搞就下这个:

链接:百度网盘-链接不存在 提取码:c0an,这是我导出的反正也没用了干脆放上来。导入完成后上传到最开始搭建的私有仓库里,写了个凑合用的脚本:

#!/bin/bash
images=(
k8s.gcr.io/kube-apiserver:v1.16.4
k8s.gcr.io/kube-controller-manager:v1.16.4
k8s.gcr.io/kube-scheduler:v1.16.4
k8s.gcr.io/kube-proxy:v1.16.4
k8s.gcr.io/etcd:3.3.15-0
k8s.gcr.io/coredns:1.6.2
k8s.gcr.io/pause:3.1
)

for imageName in ${images[@]} ;
do

    name=`echo $imageName | grep -o '/.*'`;
    docker tag k8s.gcr.io$name 120.78.151.255:5000$name;
    docker push 120.78.151.255:5000$name;
done
echo "push success"

其中120.78.151.255是上面搭建的私有库的地址,这样其他的机器直接去这里下载即可,不用再次导入,至此离线的处理也完了。


使用kubeadm安装

kubeadm init --image-repository=registry.aliyuncs.com/google_containers --pod-network-cidr=10.244.0.0/16

其中cidr是指定 Pod 网络的范围。Kubernetes 支持多种网络方案,而且不同网络方案对 --pod-network-cidr 有自己的要求,这里设置为 10.244.0.0/16 是因为我用 flannel 网络方案,必须设置成这个。repository地址就是拉取镜像的地址,如果是离线机器的话改成自己的私有仓库地址即可。

成功后会出现:

k8s集群内 springcloud配置 k8s部署springcloud项目_spring_02

一定要保存master机器上以kubeadm join开头的这两行,这是node加入用的token。此命令生成的token24小时候会过期,所以应该创建一条永久的token来代替它:

kubeadm token create --ttl 0

将生成的token替换掉刚才保存的kubeadm join中的token就可以了。

接下来安装flannel(三台):

如果是root用户执行:

export KUBECONFIG=/etc/kubernetes/admin.conf

非root用户:

其实就是把root下的admin.conf(/etc/kubernetes/admin.conf)复制到当前用户的用户目录(/home/xxxx),然后用root在用户的bash_profile文件夹下增加权限,记得也要把复制过来的admin.conf授权给当前用户,具体如下,需要使用root用户:

cp /etc/kubernetes/admin.conf /home/testuser/

chown -R testuser/home/testuser/admin.conf

vim /home/testuser/.bash_profile

#在bash_profile文件内添加如下:
export KUBECONFIG=/home/testuser/admin.conf

:wq
#保存退出

#生效
source /home/cifiadmin/.bash_profile

注意需要退出shell重进才有反应,export命令是一次性的,最上面那个root仅执行了一次,重启后会无效,想每次重启生效的话还是要写入root下的bash_profile文件,这样每次重启都会执行。

以上二选一,然后执行

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

如果这个连接不好用了就需要去github上下载flannel的yml文件,然后在本地执行。

如果报以下错误

unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused
unable to recognize "kube-flannel.yml": Get http://localhost:8080/api?timeout=32s: dial tcp 127.0.0.1:8080: connect: connection refused

你需要检查下/etc/kubernetes/文件夹下到底有没有admin.conf文件,没有就去master复制一份过来。

最后在两个node节点上都执行刚才保存的kubeadm join语句,如果报以下错误

k8s集群内 springcloud配置 k8s部署springcloud项目_docker_03

可以通过重置网络来解决,在Node上重置:

kubeadm reset
systemctl stop kubelet
systemctl stop docker
rm -rf /var/lib/cni/
rm -rf /var/lib/kubelet/*
rm -rf /etc/cni/
rm -rf /root/.kube/
ifconfig cni0 down
ifconfig flannel.1 down
ifconfig docker0 down
ip link delete cni0
ip link delete flannel.1
systemctl start docker
mkdir -p /root/.kube
cp -i /etc/kubernetes/admin.conf /root/.kube/config
chown $(id -u):$(id -g) /root/.kube/config

注意此命令有时会导致/etc/kubernetes/admin.conf丢失,如果是node节点丢失直接把master的拷贝过来即可,master的话需要重新init生成。最后加入master即可,至此kubernetes安装完毕。


部署springcloud(都在master上操作)

首先创建命名空间 test-cloud

kubectl create namespace test-cloud

安装ingress

关于ingress网上有很多资料,我就不再废话了,选用的nginx。首先需要下载mandatory.yaml文件:

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

如果这个下载不了就去github上下载需要的版本:https://github.com/kubernetes/ingress-nginx/blob/nginx-0.20.0/docs/deploy/index.md

打开mandatory.yaml文件,找到image处:

k8s集群内 springcloud配置 k8s部署springcloud项目_kubernetes_04

替换镜像地址,图中是我已经替换过的,然后执行

kubectl apply -f mandatory.yaml

创建完以后我采用nodeport的方式暴露ingress,所以需要创建一个service

vim service-nodeport.yaml

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  type: NodePort
  ports:
    - name: http
      port: 80
      targetPort: 80
      protocol: TCP
      nodePort: 32700  #http
    - name: https
      port: 443
      targetPort: 443
      protocol: TCP
      nodePort: 31443  #https
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx

此处32700和31443端口是指外部访问的端口,当外部访问32700时就会进入此ingress,同样的创建它

kubectl apply -f service-nodeport.yaml

最后创建本项目用的ingress

vim ingress-test-cloud.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-test-cloud
  namespace: test-cloud
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/enable-cors: "true"
    #nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
   - http:
      paths:
      - path: /test-eureka(/|$)(.*)
        backend:
          serviceName: eureka
          servicePort: 32316
      - path: /eureka-static(/|$)(.*)
        backend:
          serviceName: eureka
          servicePort: 32316
      - path: /testcenter(/|$)(.*)
        backend:
          serviceName: zuul
          servicePort: 12335

由于eureka的css文件路径有差异(有的带/eureka,有的不带),我在nginx中做了路由,当请求由nginx处理后转发到k8s的ingress时就是上面的路径,nginx下面会贴。将其创建,ingress就完成了:

kubectl apply -f ingress-test-cloud.yaml

根据上面的配置,当访问master节点的32700端口时就会开始ingress的处理,根据test-cloud-ingress的配置,所有testcenter开头的请求都会被去掉/testcenter并转发到12335端口的zuul中,由zuul进行再次路由。

2022-03-18更新:

如果用springcloud,网关应该选择gateway,k8s的ingress可以不用了,直接暴露gateway的端口就行。

部署eureka

先贴下eureka的配置

eureka:
  server:
    enable-self-preservation: false
  instance:
    prefer-ip-address: true
    hostname: eureka
    lease-expiration-duration-in-seconds: 30
    lease-renewal-interval-in-seconds: 5
  client:
    # 表示是否注册自身到eureka服务器
    register-with-eureka: false
    # 是否从eureka上获取注册信息
    fetch-registry: false
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

server:
  port: 32316
spring:
  application:
    name: eureka

接下来创建eureka的镜像,编写Dockerfile

FROM openjdk:8u181
RUN mkdir /data
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo 'Asia/Shanghai' >/etc/timezone \
ARG villagecentercloud-eureka-0.0.1-SNAPSHOT.jar
COPY villagecentercloud-eureka-0.0.1-SNAPSHOT.jar eureka.jar
ENV JAVA_TOOL_OPTIONS ""
ENTRYPOINT ["java","-jar","/eureka.jar"]
EXPOSE 32316

镜像我使用的是openjdk:8u181,镜像内的时间设定为上海时区,JAVA_TOOL_OPTIONS用来在k8s的配置文件中写入环境变量,后面会贴。 

需要打包镜像并且上传到私有仓库,我干脆写了个脚本

vim build.sh

docker rmi server-eureka-0.0.1:v1
docker build -t server-eureka-0.0.1:v1 .
docker tag server-eureka-0.0.1:v1 127.0.0.1:5000/server-eureka-0.0.1:v1
docker push 127.0.0.1:5000/server-eureka-0.0.1:v1

echo "success!"

chmod u+x build.sh

打包镜像,直接执行build.sh即可

k8s集群内 springcloud配置 k8s部署springcloud项目_spring_05

上传完镜像以后开始在k8s中部署eureka,首先创建service和deployment

vim eureka-deployment.yaml

apiVersion: v1
kind: Namespace
metadata:
  name: cifi-cloud
---
apiVersion: v1
kind: Service
metadata:
  name: eureka
  namespace: cifi-cloud
  labels:
    app: eureka
spec:
  ports:
    - port: 32316
      targetPort: 32316
      name: eureka
  selector:
    app: eureka

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: eureka
  namespace: cifi-cloud
  labels:
    name: eureka
spec:
  replicas: 1
  selector:
    matchLabels:
      app: eureka
  template:
    metadata:
      labels:
        app: eureka
    spec:
      containers:
        - name: eureka
          image:  172.18.28.52:5000/server-eureka-0.0.1:v1
          imagePullPolicy: Always
          ports:
            - containerPort: 32316
          resources:
            limits:
              memory: 2048Mi
            requests:
              memory: 2048Mi
          env:
           - name: JAVA_TOOL_OPTIONS
             value: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -Djava.security.egd=file:/dev/./urandom "

resources是为了限制容器资源,因为jvm是按照宿主机的内存来设置的,如果你的宿主机是16G内存,那么一个java容器就可以占到1.3G左右,这样显然不行,因此在这里需要配置每个pod的最大资源;env这里通过JAVA_TOOL_OPTIONS来给容器中的jvm传递jvm启动参数:-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap这两个参数是让jvm感知到CGroup并且根据其大小来自动调整;replicas是指部署几份pod,其他的看看文档就知道了,没什么太难的。image要填私有仓库的地址,接下来部署

kubectl apply -f eureka-deployment.yaml

不出意外很快就部署好了,如果出错可能就要具体原因具体改,接下来部署zuul,打包镜像跟上面一样,就只贴springcloud配置文件和k8s部署文件

server:
  port: 32335

#服务的名称
spring:
  application:
    name: zuul

#指定注册中心地址
eureka:
  client:
    service-url:
      defaultZone: http://eureka:32316/eureka/
  instance:
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 30

#自定义路由映射
zuul:
  routes:
    es:
      path: /es/**
    eureka:
      path: /test-eureka/**
      serviceId: eureka
      stripPrefix: true
      custom-sensitive-headers: true
    eureka-static-32316:
      path: /eureka/**
      serviceId: eureka
      stripPrefix: false
      custom-sensitive-headers: true
filter:
  allowPaths: /es/test/razer#不想被zuul拦截的url
auth:
  accessTokenTime: 360000
logging:
  level:
    root: debug
apiVersion: v1
kind: Service
metadata:
  name: zuul
  namespace: test-cloud
  labels:
    app: zuul
spec:
  ports:
    - port: 12335
      targetPort: 12335
      name: zuul
  selector:
    app: zuul

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: zuul
  namespace: test-cloud
  labels:
    name: zuul
spec:
  replicas: 1
  selector:
    matchLabels:
      app: zuul
  template:
    metadata:
      labels:
        app: zuul
    spec:
      containers:
        - name: zuul
          image:  120.78.151.255:5000/service-zuul-0.0.1:v1
          imagePullPolicy: Always
          ports:
            - containerPort: 12335
      initContainers:
        - name: eureka
          image: busybox:1.31
          command: ['sh', '-c', 'until nslookup eureka; do echo waiting for eureka; sleep 1; done;']

最后部署一个普通的生产者即可,此处我使用一个es项目,也是只放配置文件应用即可

spring配置文件

server:
  port: 12333
#  servlet:
#    context-path: /es
eureka:
  instance:
    prefer-ip-address: true
    lease-renewal-interval-in-seconds: 5
    lease-expiration-duration-in-seconds: 30
    hostname: es
  client:
    service-url:
      defaultZone: http://eureka:32316/eureka/
spring:
  application:
    name: es

es:
  host: 10.10.2.5,10.10.2.6,10.10.2.7
  port: 9200
  schema: http          #协议
  connectTimeOut: 1000 #连接超时时间
  socketTimeOut: 30000 #连接超时时间
  connectionRequestTimeOut: 500 #获取连接的超时时间
  maxConnectNum: 100 #最大连接数
  maxConnectPerRoute: 100 #最大路由数

k8s配置文件

apiVersion: v1
kind: Service
metadata:
  name: elasticsearch
  namespace: test-cloud
  labels:
    app: elasticsearch
spec:
  ports:
    - port: 12333
      targetPort: 12333
      name: elasticsearch
  selector:
    app: elasticsearch

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: elasticsearch
  namespace: test-cloud
  labels:
    name: elasticsearch
spec:
  replicas: 1
  selector:
    matchLabels:
      app: elasticsearch
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      containers:
        - name: elasticsearch
          image:  120.78.151.255:5000/service-elasticsearch-0.0.1:v1
          imagePullPolicy: Always
          ports:
            - containerPort: 12333
      initContainers:
        - name: eureka
          image: busybox:1.31
          command: ['sh', '-c', 'until nslookup eureka; do echo waiting for eureka; sleep 2; done;']

应用即可,这些都部署完成以后通过外部/其他机器的nginx转发到我们的k8s集群,贴下我的nginx部分配置

location /test-eureka/ {
            proxy_pass http://172.18.28.51:32700/test-eureka/;
        }
        location ^~ /eureka/ {
            proxy_pass http://127.18.28.51:32700/eureka-static/eureka/;
        }

        location ^~ /testcenter/ {
            proxy_pass http://127.0.0.1:32700/testcenter/;
        }

文件挂载

文件挂载是个必须的配置,方案有很多,简单点用nfs挂载到物理机。

首先是挂载文件的服务器,我这里使用master节点:

yum -y install nfs-utils rpcbind

systemctl enable rpcbind
systemctl enable nfs-server
systemctl start rpcbind
systemctl start nfs-server

//给要共享的文件夹权限
chmod -R go+w /usr/local/data

//编辑exports文件
vim /etc/exports

/usr/local/data 172.155.155.34(rw,sync,root_squash) 172.155.155.23(rw,sync,root_squash)
//其中172.155.155.34和172.155.155.23是需要共享目录的服务器,本文中为两个从节点


//修改完后生效
systemctl restart nfs
systemctl reload

然后配置两个需要挂载的从节点:

yum -y install nfs-utils
systemctl enable rpcbind
systemctl start rpcbind

这样就搞定了,没什么事早点下班嗷铁汁。

2022-03-18更新:

建议直接用oss,不要搞这些nfs啥的了。

结语

至此kubernetes部署springcloud全部结束,还有很多东西要学,有空会把二进制部署的贴上来。中间踩了很多坑,惨。

如果没事可以装个rancher(真香),一行命令搞定:

docker run -d --restart=unless-stopped -p 9000:80 -p 3443:443 -v /usr/local/rancher:/var/lib/rancher rancher/rancher:v2.4.5

网页端通过3443端口访问,务必要挂载配置文件出来。