Kubernetes 持续集成 SpringCloud

写在开始之前

在开始之前我们需要了解几个概念:

1.什么是持续集成?

持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。

2.什么是 kubernetes?

Kubernetes 是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes 提供了应用部署,规划,更新,维护的一种机制。

3.什么是 SpringCloud?

Spring Cloud 是一系列框架的有序集合。它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。

通过以上几个概念我们简单了解了 kubernetes 等技术是干什么的,其他的具体我们这里不做赘述,大家可以到其官方网站进行了解。我们这里只教会大家如何利用这些工具或技术来实现我们应用的可持续集成到不同的环境中。那么就让我开始实践吧!

我们先来看看实现这个持续集成的流水线:

  • 开发人员提交代码 Gitlab 远程触发 Jenkins 构建
  • Jenkins 构建镜像推送到私有仓库
  • Jenkins 将最新的镜像部署到 k8s 中
  • 将部署反馈发送给用户

一、实验环境概览

这里推荐大家使用 VMware 安装虚拟机,Centos 7 系统,所有操作都在 root 用户下进行。

以下内容将以 IP 区分各个虚拟机。

二、 虚拟机和必要软件安装

1. 虚拟机的安装与配置

这里安装虚拟机按照 VMware 的指示一步步安装配置虚拟机的内存和硬盘即可,重点教大家如何配置虚拟机的网卡和固定 IP,如果不配置固定 IP 虚拟机重启之后自动分配 IP 会导致 kubernetes 集群崩掉无法访问以及各种各样的问题。

注意:安装虚拟机的时候网络适配器要选择 NAT 模式(N):用于共享主机的 IP 地址。

1.1 网卡配置

第一步、打开虚拟网络编辑器

第二步、获得管理员权限

第三步、这里一定要按照图中这样配置

第四步、这一步可有可无,默认配置也没问题

1.2 为各个虚拟机分配静态 IP

# 1. 修改网络配置文件
vi /etc/sysconfig/network-scripts/ifcfg-ens33

# 2. 把BOOTPROTO="dhcp"修改为BOOTPROTO="static"

# 3. 在末尾添加如下内容:
IPADDR=192.168.1.11
NETMASK=255.255.255.0
GATEWAY=192.168.1.2
DNS1=114.114.114.114
  • IPADDR 是你为此虚拟机分配的静态 IP
  • NETMASK 子网掩码,在第三步图中获取

  • GATEWAY 网关,在第三步图中点击 NAT 设置 获取

  • DNS 使用 114.114.114.114 即可

# 4. 重启网络服务
service network restart

# 5.验证设置
## 验证是否能连接外网
ping baidu.com

## 查看ip
ip addr

2. 安装一些必要的工具以及一些通用配置

在所有机器上进行如下操作:

# 1. 安装常用软件
yum -y install vim wget

# 2.安装 Docker
## v1.11.x版本推荐使用docker v17.03 更高的版本可能无法正常运行

### 2.1 安装依赖包
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

### 2.2 设置阿里云镜像源
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

### 2.3 安装指定版本的Docker
yum install -y --setopt=obsoletes=0 docker-ce-17.03.2.ce-1.el7.centos.x86_64 docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch

### 2.4 设置Docker开机自启并启动
sudo systemctl enable docker && sudo systemctl start docker

2.5 配置 Docker 加速器

配置阿里云加速器。

# 3. 通用配置
## 3.1 关闭防火墙 
systemctl stop firewalld && systemctl disable firewalld

## 3.2 配置host解析
cat >>/etc/hosts<<EOF
192.168.1.11 k8s-master
192.168.1.12 k8s-slave1
192.168.1.13 k8s-slave2
192.168.1.101 hub.k8s.com
EOF

## 3.3 设置每个机器的hostname
hostnamectl --static set-hostname {hostname}

注意:配置host解析的时候我们添加了Docker私库的ip解析。

三、搭建 Docker 私有仓库:Harbor(192.168.1.101)

1. 安装 Docker-compose

# 1.安装Docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

#赋予Docker-compose执行权限
sudo chmod +x /usr/local/bin/docker-compose

#验证Docker-compose
docker-compose --version

2. 安装 Harbor

# 1. 下载安装文件(可以在指定目录下载)
wget https://storage.googleapis.com/harbor-releases/harbor-online-installer-v1.5.2.tgz

# 2. 解压下载的文件
tar xvf harbor-online-installer-v1.5.2.tgz

3. 配置 Harbor

1. 修改Harbor的配置文件
cd harbor
vim harbor.cfg

内容如下:

# hostname设置访问地址,可以使用ip、域名,不可以设置为127.0.0.1或localhost
hostname = hub.k8s.com

# 访问协议,默认是http,也可以设置https,如果设置https,则nginx ssl需要设置on
ui_url_protocol = http

# mysql数据库root用户默认密码root123,实际使用时修改下
db_password = root@1234

max_job_workers = 3 
customize_crt = on
ssl_cert = /data/cert/server.crt
ssl_cert_key = /data/cert/server.key
secretkey_path = /data
admiral_url = NA

# 邮件设置,发送重置密码邮件时使用
email_identity = 
email_server = smtp.mydomain.com
email_server_port = 25
email_username = sample_admin@mydomain.com
email_password = abc
email_from = admin <sample_admin@mydomain.com>
email_ssl = false

# 启动Harbor后,管理员UI登录的密码,默认是Harbor12345
harbor_admin_password = root@1234

# 认证方式,这里支持多种认证方式,如LADP、本次存储、数据库认证。默认是db_auth,mysql数据库认证
auth_mode = db_auth

# LDAP认证时配置项
#ldap_url = ldaps://ldap.mydomain.com
#ldap_searchdn = uid=searchuser,ou=people,dc=mydomain,dc=com
#ldap_search_pwd = password
#ldap_basedn = ou=people,dc=mydomain,dc=com
#ldap_filter = (objectClass=person)
#ldap_uid = uid 
#ldap_scope = 3 
#ldap_timeout = 5

# 是否开启自注册
self_registration = on

# Token有效时间,默认30分钟
token_expiration = 30

# 用户创建项目权限控制,默认是everyone(所有人),也可以设置为adminonly(只能管理员)
project_creation_restriction = everyone

verify_remote_cert = on

4.启动 Harbor

# 1.在当前安装目录下
./install.sh

5.访问 Harbor 以及一些常用操作

配置宿主机对 Docker 私有仓库镜像IP的解析,编辑 C:\WINDOWS\System32\drivers\etc\hosts 文件

添加如下内容:

192.168.1.101 hub.k8s.com

这样我们在宿主机直接访问 hub.k8s.com 就可以看到Docker私库的可视化界面。

默认用户就是 admin,密码就是我们在配置文件里配置的 root@1234

我们可以新建一个项目,把和这个项目相关的镜像都推送到这个项目里,我们也可以根据项目来区分我们不同环境的镜像。例如:我的项目叫 k8s-pig,那么在 Harbor 中我们新创建一个项目叫 k8s-pig-prod 用来存放生产环境的镜像,新创建一个项目叫 k8s-pig-dev 用来存放开发环境的镜像,以及在后面我们持续集成时可以指定使用什么环境的镜像。这个我们后面再详细的讲。

我们可以点推送镜像来复制命令

进到项目的某一个模块镜像列表页面我们可以查看这个模块打包镜像的各个版本,并且可以复制 pull 镜像的命令

以下是重点!!!

我们已经完成了 Docker 私有仓库的搭建了,是不是我们就能向私库李推送镜像了?

答案显然是不能的!!!

那么我们就来解决一下这个问题,在所有机器进行如下操作(本机也需要):

1.修改 Docker 的 daemon.json 文件

vim /etc/docker/daemon.json

# 添加如下内容:
"insecure-registries":["hub.k8s.com"]

# 重启服务
systemctl restart docker

注意:不要忘记添加内容之前加个逗号

2.docker 登录到 Harbor

docker login hub.k8s.com
#输入用户名密码即可(admin/root@1234)

3.小试牛刀

# 1. 拉取docker hub的公共镜像
docker pull gitlab/gitlab-ce
# 2. 标记本地镜像,将其归入某一仓库
docker tag gitlab/gitlab-ce hub.k8s.com/k8s-pig/gitlab
# 3. 推送镜像到私有仓库
docker push hub.k8s.com/k8s-pig/gitlab
# 4. 从私有仓库pull我们的镜像
docker pull hub.k8s.com/k8s-pig/gitlab

登录到 harbor 可视化界面就可以看到我们刚刚推送的镜像。

四、搭建代码托管服务:GitLab(192.168.1.100)

GitLab 是一个用于仓库管理系统的开源项目,使用 Git 作为代码管理工具,并在此基础上搭建起来的 web 服务。

其实,基于 Docker 安装 Gitlab 十分简单,下面就让我们搭建 Gitlab 服务,并配置邮件服务。

1. 搭建 Gitlab 服务

# 1. pull镜像,这个镜像就是我们前面小试牛刀时推送到私有仓库的镜像
docker pull hub.k8s.com/k8s-pig/gitlab

# 2. 运行Gitlab镜像
docker run -d -h gitlab.cn  -p 8443:443 -p 80:80 -p 2222:22 -p 9090:9090 --name gitlab --restart always -v /soft/gitlab/config:/etc/gitlab -v /soft/gitlab/logs:/var/log/gitlab -v /soft/gitlab/data:/var/opt/gitlab hub.k8s.com/k8s-pig/gitlab
  • -h:通过gitlab.cn域名访问容器
  • -v:将配置文件、日志、数据持久化
  • -p:容器端口映射到本机端口
  • --name:容器名称
  • --restart:容器重启

注意:这里的 --restart 在 gitlab 容器上并不会启动成功,如果我们的虚拟机关机了,那么我们只能把容器删除了,重启启动,不用担心数据丢失,因为我们已经把数据持久化了,具体什么原因,大家可重启一下 gitlab 容器去看一下容器的日志。

2. 配置邮件服务

我这里用的 QQ 的 smtp 服务,大家可以自己百度一下如何开启 QQ 的 smtp 服务。

# 1.编辑gitlab的配置文件
vim /soft/gitlab/config/gitlab.rb

# 2.修改我们的clone地址
external_url 'http://192.168.1.100' #本机IP

# 3.添加如下内容:
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.qq.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "邮箱地址"
gitlab_rails['smtp_password'] = "认证码"
gitlab_rails['smtp_domain'] = "smtp.qq.com"
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = '邮箱地址'

# 4.重启Gitlab容器
## 4.1 删除gitlab容器
docker rm -f gitlab
## 4.2 重新运行gitlab容器
docker run -d -h gitlab.cn  -p 8443:443 -p 80:80 -p 2222:22 -p 9090:9090 --name gitlab --restart always -v /soft/gitlab/config:/etc/gitlab -v /soft/gitlab/logs:/var/log/gitlab -v /soft/gitlab/data:/var/opt/gitlab hub.k8s.com/k8s-pig/gitlab

这里的 clone 地址不修改的默认值为 http://gitlab.cn。我们用命令行 clone 代码的时候会用问题。

3. 初始化登录

gitlab 的默认用户为 root,超级管理员,我们第一次登录 Gitlab 的时候会让你输入密码作为 root 用户的密码,完成后你就可以在 Gitlab 创建你的项目了。

五、搭建 Jenkins 服务 (192.168.1.11)

Jenkins 是一个开源软件项目,是基于 Java 开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,使软件的持续集成变成可能。

我们这里为了简单和少出问题将 Jenkins 服务部署在 k8s-master 节点上,以便我们可以直接在 Jenkins 里使用 kubernetes 的命令行工具,那你就会问了,单节点抗压能力是不是不够啊,这里推荐一种方案也是我实践过的,其实在生产环境中 k8s-master 都是需要高可用的,所以 k8s-master 至少要部署三个节点(奇数个),Jenkins 也可以集群部署,这样我们在每一个 k8s-master 节点上部署 Jenkins 选一个作为 Jenkins-master 作为 Jenkins 集群的入口这样就解决了单点抗压能力不足的情况。

这只是一种解决方案,你也可以将 Jenkins 部署到 kubernetes 集群中,具体怎么实现这里就不做赘述了(大家如果需要的话可以给我留言看看有多少人需要,我再重新开一个 chat 具体讲讲如何部署 kubernetes 集群高可用和 Jenkins 部署到 kubernetes 集群中),下面我们就来搭建 Jenkins 服务。

1. 环境搭建

安装 Git

yum -y install git

安装 JDK

# 1. 下载JDK文件
cd /usr/
mkdir java && cd java
wget http://download.oracle.com/otn-pub/java/jdk/8u181-b13/96a7b8442fe848ef90c96a2fad6ed6d1/jdk-8u181-linux-x64.tar.gz

# 2. 解压安装文件
tar -zxvf jdk-8u151-linux-x64.tar.gz

# 3. 添加环境变量

vim /etc/profile
# 在文本最后添加如下内容
export JAVA_HOME=/usr/java/jdk1.8.0_181
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib/dt.JAVA_HOME/lib/tools.jar:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin:${PATH}

# 4.立即生效
source /etc/profile

# 5. 验证安装
java -version

安装 Maven

# 1. 下载安装文件
cd /usr/
mkdir maven && cd maven
wget http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.5.4/binaries/apache-maven-3.5.4-bin.tar.gz

# 2. 解压安装文件
tar -zxvf apache-maven-3.5.4-bin.tar.gz

# 3. 添加环境变量
vim /etc/profile
# 修改为如下内容:
export JAVA_HOME=/usr/java/jdk1.8.0_181
export MAVEN_HOME=/usr/maven/apache-maven-3.5.4
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib/dt.JAVA_HOME/lib/tools.jar:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:${JRE_HOME}/bin:${MAVEN_HOME}/bin:${PATH}

# 4. 立即生效
source /etc/profile

# 5. 验证安装 
mvn -version

2. 安装配置 Jenkins

2.1 下载安装文件并运行

# 1. 创建安装目录
cd ~
mkdir /soft/jenkins -p
cd /soft/jenkins

# 2. 下安装文件
wget http://mirrors.jenkins.io/war-stable/latest/jenkins.war

# 3. 运行安装文件
nohup java -jar jenkins.war --httpPort=80 >temp.txt &

# 获取登录密码
cat temp.txt

复制以下内容:

2.2 登录并配置

在宿主机上输入 IP:192.168.1.11 访问 Jenkins 然后初始化用户,把我们复制的密码粘贴上,点击继续。

初始化安装插件,过程比较慢,耐心等待即可。

安装完成后就会出现初始化用户页面,大家自己去初始化用户就可以了,然后保存并结束。

初始化完成后就会进入到 Jenkins 的首页,我们后面再详细讲解如何使用 Jenkins。

六、搭建 k8s 集群 (192.168.1.11,192.168.1.12,192.168.1.13)

1. k8s 集群环境初始化设置

如下操作在192.168.1.11,192.168.1.12,192.168.1.13执行

1.1 安装 kubeadm、kubelet、kubectl

# 1. 配置阿里云源
cat <<EOF > /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
EOF

# 2. 开始安装
yum -y install kubelet kubeadm kubectl kubernetes-cni

# 3. 启动kubeadm服务
systemctl enable kubelet && systemctl start kubelet

# 4. 下载所需要的镜像
# 创建脚本 images.sh
vim images.sh
# 添加如下内容:
#!/bin/bash
images=(kube-proxy-amd64:v1.11.2 kube-scheduler-amd64:v1.11.2 kube-controller-manager-amd64:v1.11.2 kube-apiserver-amd64:v1.11.2
etcd-amd64:3.2.18 coredns:1.1.3 pause-amd64:3.1 kubernetes-dashboard-amd64:v1.8.3 k8s-dns-sidecar-amd64:1.14.9 k8s-dns-kube-dns-amd64:1.14.9
k8s-dns-dnsmasq-nanny-amd64:1.14.9 )
for imageName in ${images[@]} ; do
docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
done
docker tag da86e6ba6ca1 k8s.gcr.io/pause:3.1
# 添加执行权限
chmod -R 777 images.sh
# 拉取所需镜像(耐心等待)
./images.sh

# 5. 关闭 Swap
swapoff -a
# 永久关闭
#要永久禁掉swap分区,打开如下文件注释掉swap那一行 
# vim /etc/fstab

# 6. 关闭 SELinux
sed -i 's/SELINUX=permissive/SELINUX=disabled/' /etc/sysconfig/selinux

setenforce 0

# 7. 配置转发参数
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
EOF

sysctl --system

2. 开始安装 Master节点(192.168.1.11)

2.1 初始化

注意这里--apiserver-cert-extra-sans=后面的IP:192.168.1.11,如果你的和我的不一样要修改成自己的。

kubeadm init --kubernetes-version=v1.11.2 --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=0.0.0.0 --apiserver-cert-extra-sans=192.168.1.11,127.0.0.1,k8s-master

初始化完成之后会打印一条命令,例如:

kubeadm join 192.168.1.11:6443 --token udrvwc.nn0ujov1re4ms63j --discovery-token-ca-cert-hash sha256:d6f93d4b2f9efc49e7e063707daa474057e58d3e4e09d62c3ab747b4279332cb

需要我们找个地方记录一下这个值,后面我们添加节点是会用到。

2.2 配置 kubctl

export KUBECONFIG=/etc/kubernetes/admin.conf

echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile

2.3 安装 Flannel

# 1. 创建安装文件夹
mkdir -p /etc/cni/net.d/

# 2. 创建安装文件
cat <<EOF> /etc/cni/net.d/10-flannel.conf
{
“name”: “cbr0”,
“type”: “flannel”,
“delegate”: {
“isDefaultGateway”: true
}
}
EOF

mkdir /usr/share/oci-umount/oci-umount.d -p

mkdir /run/flannel/

cat <<EOF> /run/flannel/subnet.env
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.1.0/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true
EOF

创建 flannel 的 yaml 文件

vim k8s-flannel.yaml

添加如下内容:

---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: flannel
rules:
  - apiGroups:
      - ""
    resources:
      - pods
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes/status
    verbs:
      - patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: flannel
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: flannel
subjects:
- kind: ServiceAccount
  name: flannel
  namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: flannel
  namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: kube-flannel-cfg
  namespace: kube-system
  labels:
    tier: node
    app: flannel
data:
  cni-conf.json: |
    {
      "name": "cbr0",
      "type": "flannel",
      "delegate": {
        "isDefaultGateway": true
      }
    }
  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }
---
apiVersion: extensions/v1beta1
kind: DaemonSet
metadata:
  name: kube-flannel-ds
  namespace: kube-system
  labels:
    tier: node
    app: flannel
spec:
  template:
    metadata:
      labels:
        tier: node
        app: flannel
    spec:
      hostNetwork: true
      nodeSelector:
        beta.kubernetes.io/arch: amd64
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: Exists
        effect: NoSchedule
      serviceAccountName: flannel
      initContainers:
      - name: install-cni
        image: quay.io/coreos/flannel:v0.9.1-amd64
        command:
        - cp
        args:
        - -f
        - /etc/kube-flannel/cni-conf.json
        - /etc/cni/net.d/10-flannel.conf
        volumeMounts:
        - name: cni
          mountPath: /etc/cni/net.d
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      containers:
      - name: kube-flannel
        image: quay.io/coreos/flannel:v0.9.1-amd64
        command: [ "/opt/bin/flanneld", "--ip-masq", "--kube-subnet-mgr" ]
        securityContext:
          privileged: true
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: POD_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        volumeMounts:
        - name: run
          mountPath: /run
        - name: flannel-cfg
          mountPath: /etc/kube-flannel/
      volumes:
        - name: run
          hostPath:
            path: /run
        - name: cni
          hostPath:
            path: /etc/cni/net.d
        - name: flannel-cfg
          configMap:
            name: kube-flannel-cfg

运行 k8s-flannel.yaml

kubectl create -f k8s-flannel.yaml

2.4 k8s 可视化配置

我们这里使用别人开源的 DashBoard 模板。

# 1. clone 模板
git clone https://github.com/gh-Devin/kubernetes-dashboard.git

# 2. 这个开源的模板里heapster插件的镜像有问题我们这里给他修改一下
cd kubernetes-dashboard/

vim heapster.yaml

修改镜像名称 :

registry.cn-shenzhen.aliyuncs.com/intbee/heapster-amd64:v1.4.2 为 wavefronthq/heapster-amd64

部署 DashBoard

kubectl -n kube-system create -f .

创建 dashboard-admin.yaml

vim dashboard-admin.yaml

添加如下内容:

apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: kubernetes-dashboard
  labels:
    k8s-app: kubernetes-dashboard
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kube-system

执行 dashboard-admin.yaml

kubectl create -f dashboard-admin.yaml

现在我们在宿主机访问:192.168.1.11:30090 就可以访问 k8s 管理界面了。如下(我的是已经加入 slave 的):

下面我们就来加入 slave 节点。

2.5 加入子节点

分别在 192.168.1.12 和 192.168.1.13 节点上执行我们刚刚保存的命令:

kubeadm join 192.168.1.11:6443 --token udrvwc.nn0ujov1re4ms63j --discovery-token-ca-cert-hash sha256:d6f93d4b2f9efc49e7e063707daa474057e58d3e4e09d62c3ab747b4279332cb

我们在 master 节点上运行如下命令来查看节点如否加入集群(需要等一会让子节点初始化完成才能看见状态为 Ready):

kubect get nodes

七、 搭建完整的持续的流水线

1. 准备我们的 springcloud 项目

这里推荐一下冷冷的 springcloud 项目,冷总的微服务项目:PIG,欢迎大家 start、fork。PIG 大概是我从零开始学 springcloud 里面最容易上手的微服务项目,基础设施健全,技术群内也非常活跃,这里给大家强势安利一波++请戳官网++。

这里我们使用 IDEA 代码编辑工具。

1.1 准备代码

在你的宿主机上创建代码存放目录,将代码 clone 到此目录中运行如下命令:

# 1. 后台代码
git clone https://gitee.com/log4j/pig.git

# 2. 前台代码
git clone https://gitee.com/log4j/pig-ui.git

# 3. 配置文件
git clone https://gitee.com/cqzqxq_lxh/pig-config.git

1.2 改造项目并上传到 Gitlab

用 IDEA 打开我们刚刚准备好的三个项目。我们先将微服务的配置本地化:

  • 在 pig-config 模块下创建 config 文件夹
  • 将 pig-config 项目下的所有配置文件都拷贝到 config 文件夹下
  • 修改 pig-config 模块的配置文件

如下图:

1.3 在 Gitlab 上创建项目

我这里将项目重命名了,你们也可以将项目重命名(注意自己重命名之后的 Git Url)。下面就是我在 gitlab 上创建的 pig 的前后台项目:

在 IDEA 中打开 pig 项目的终端输入如下内容:

git remote rename origin old-origin
git remote add origin http://192.168.1.100/Maozk/k8s-pig.git
git push -u origin --all
git push -u origin --tags

在 IDEA 中打开 pig-ui 项目的终端输入如下内容:

git remote rename origin old-origin
git remote add origin http://192.168.1.100/Maozk/k8s-pig-ui.git
git push -u origin --all
git push -u origin --tags

终端在这里打开:

这样我们就把从码云 clone 下来的代码推送到了我们自己的代码托管服务 Gitlab 上了。

2. 将 Gitlab 和 Jenkins 连接起来

2.1 Jenkins 配置修改

系统管理 > 全局安全配置

2.2 安装 Generic Webhook Trigger Plugin

  • 系统管理 > 管理插件 > 可选插件 Tab 页
  • 在过滤里面输入:Generic Webhook Trigger
  • 然后勾选要安装的插件:Generic Webhook Trigger Plugin
  • 点击直接安装,等待安装完成即可。

2.3 新建一个任务:Jenkins-pig

我这里已经有一个相同名称的任务了,大家忽略我这个错误即可。

  • 输入任务名称
  • 选择流水线(pipeline)
  • 点击确定

2.4 配置 Jenkins-pig 任务

这里的 Pipeline 脚本我们后面再细讲,这里主要配置了如何监听用户对 Gitlab 不同的操作时触发什么构建。

在这里我们就可以实现了提交不同的分支时触发不同的构建。

2.5 创建任务:Jenkins-pig-ui

这里创建 pig-ui 任务和创建 pig 任务差不多,主要是注意监听的仓库路径要替换成 pig-ui 的。

3. 和 Gitlab 连接

首先用 admin 用户登录 Gitlab,然后配置 Gitlab 能请求本地 IP。

然后选择 System Hooks

我们这里再看一下 Gitlab 发送的 JSon 数据
编辑我们创建的 System Hooks,在页面的最先发你会看见触发构建的历史数据,点击 View details。

你就会从 Request body 中看见本次请求的操作类型 object_kind: push 为提交代码,分支是 ref : refs/heads/master。

仓库地址:repository{"git_http_url": "http://192.168.1.100/Maozk/k8s-pig-ui.git"}

Request body:

{
  "object_kind": "push",
  "event_name": "push",
  "before": "6242b95eb45ef3ca6137e1ed97d13467a66a2e48",
  "after": "baf2b2b5f098673088aa14b3349acd35b9e12a24",
  "ref": "refs/heads/master",
  "checkout_sha": "baf2b2b5f098673088aa14b3349acd35b9e12a24",
  "message": null,
  "user_id": 1,
  "user_name": "Maozk",
  "user_username": "Maozk",
  "user_email": "549595297@qq.com",
  "user_avatar": "http://192.168.1.100/uploads/-/system/user/avatar/1/avatar.png",
  "project_id": 2,
  "project": {
    "id": 2,
    "name": "k8s-pig-ui",
    "description": "",
    "web_url": "http://192.168.1.100/Maozk/k8s-pig-ui",
    "avatar_url": null,
    "git_ssh_url": "git@192.168.1.100:Maozk/k8s-pig-ui.git",
    "git_http_url": "http://192.168.1.100/Maozk/k8s-pig-ui.git",
    "namespace": "Maozk",
    "visibility_level": 20,
    "path_with_namespace": "Maozk/k8s-pig-ui",
    "default_branch": "master",
    "ci_config_path": null,
    "homepage": "http://192.168.1.100/Maozk/k8s-pig-ui",
    "url": "git@192.168.1.100:Maozk/k8s-pig-ui.git",
    "ssh_url": "git@192.168.1.100:Maozk/k8s-pig-ui.git",
    "http_url": "http://192.168.1.100/Maozk/k8s-pig-ui.git"
  },
  "commits": [
    {
      "id": "baf2b2b5f098673088aa14b3349acd35b9e12a24",
      "message": "修改标签页\n",
      "timestamp": "2018-08-15T09:02:05Z",
      "url": "http://192.168.1.100/Maozk/k8s-pig-ui/commit/baf2b2b5f098673088aa14b3349acd35b9e12a24",
      "author": {
        "name": "549595297@qq.com",
        "email": "jia327926"
      },
      "added": [

      ],
      "modified": [

      ],
      "removed": [
        "dist/index.html",
      ]
    }
  ],
  "total_commits_count": 1,
  "repository": {
    "name": "k8s-pig-ui",
    "url": "git@192.168.1.100:Maozk/k8s-pig-ui.git",
    "description": "",
    "homepage": "http://192.168.1.100/Maozk/k8s-pig-ui",
    "git_http_url": "http://192.168.1.100/Maozk/k8s-pig-ui.git",
    "git_ssh_url": "git@192.168.1.100:Maozk/k8s-pig-ui.git",
    "visibility_level": 20
  }
}

此时你只要是提交的操作就会触发 Jenkins 的构建。
至此我们就将代码、Gitlab、Jenkins 连接起来了。

4.部署我们的微服务到 k8s 集群 (192.168.1.11)

# 1.创建脚本存放文件夹
mkdir /soft/project/pig/script -p

cd /soft/project/pig/script

在 k8s 集群里创建我们项目的命名空间

kubectl create namespace kube-pig

4.1 在 k8s 中部署 mysql、rabbitmq、redis

  • 部署 mysql

编写 k8s-mysql-pvc.yaml 使 mysql 数据持久化

vim k8s-mysql-pvc.yaml

apiVersion: v1
kind: PersistentVolume
metadata:
  name: k8s-mysql-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: pig-mysql
  hostPath:
    path: /soft/data/pig/mysql
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: k8s-mysql-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: pig-mysql

编写 mysql 运行的 k8s-mysql-pig.yaml

vim k8s-mysql-pig.yaml

apiVersion: v1
kind: Service
metadata:
  name: k8s-mysql-pig
spec:
  type: NodePort
  ports:
  - port: 3306
    nodePort: 32307
    targetPort: 3306
  selector:
    app: k8s-mysql-pig
---

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: k8s-mysql-pig-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: k8s-mysql-pig
    spec:
      containers:
      - image: mysql:5.7
        name: k8s-mysql-pig
        ports:
        - containerPort: 3306
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "root"
        volumeMounts:
        - name: k8s-mysql-pig-data
          mountPath: /var/lib/mysql
      volumes:
      - name: k8s-mysql-pig-data
        persistentVolumeClaim:
          claimName: k8s-mysql-pvc

注意这里的数据持久化是通过:

volumes.persistentVolumeClaim.claimName:k8s-mysql-pvc将mysql 和 pvc 绑定的。

kubectl create -f k8s-mysql-pvc.yaml --namespace kube-pig

kubectl create -f k8s-mysql-pig.yaml --namespace kube-pig

然后在宿主机用 mysql 客户端创建数据库 pig,运行项目下的 pig.sql 脚本来初始化数据库。

连接地址:192.168.1.11:32307 用户名:root 密码:root。

  • 部署 rabbitmq

vim k8s-rabbitmq-pig.yaml

apiVersion: v1
kind: Service
metadata:
  name: k8s-rabbitmq-pig
spec:
  type: NodePort
  ports:
  - name: rabbit-cli
    port: 5671
    targetPort: 5671
  - name: rabbit-page
    port: 15671
    targetPort: 15671
  - name: rabbit-cli1
    port: 5672
    targetPort: 5672
  - name: rabbit-page1
    port: 15672
    targetPort: 15672
    nodePort: 30072
  - name: rabbit-page2
    port: 25672
    targetPort: 25672
  selector:
    app: k8s-rabbitmq-pig
---

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: k8s-rabbitmq-pig-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: k8s-rabbitmq-pig
    spec:
      containers:
      - image: rabbitmq:3.7.7-management
        name: k8s-rabbitmq-pig
        ports:
        - containerPort: 5671
        - containerPort: 15671
        - containerPort: 5672
        - containerPort: 15672
        - containerPort: 25672
        env:
        - name: RABBITMQ_DEFAULT_USER
          value: "redis"
        - name: RABBITMQ_DEFAULT_PASS
          value: "redis"

这里我们指定了 rabbitmq 默认的用户和密码为 redis,并配置了其外部的访问端口为 30072。

kubectl create -f k8s-rabbitmq-pig.yaml --namespace kube-pig

  • 部署 redis

vim k8s-redis-pig.yaml

apiVersion: v1
kind: Service
metadata:
  name: k8s-redis-pig
spec:
  type: ClusterIP
  ports:
  - port: 6379
    targetPort: 6379
  selector:
    app: k8s-redis-pig
---

apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: k8s-redis-pig-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: k8s-redis-pig
    spec:
      containers:
      - image: redis:3.2
        name: k8s-redis-pig
        ports:
        - containerPort: 6379
        volumeMounts:
        - name: k8s-redis-pig-conf
          mountPath: /etc/redis
      volumes:
      - name: k8s-redis-pig-conf
        hostPath:
          path: /soft/data/redis

kubectl create -f k8s-redis-pig.yaml --namespace kube-pig

至此我们创建完了这些外部服务,分别是 k8s-redis-pig、k8s-rabbitmq-pig、k8s-mysql-pig,在集群内我们都是根据服务名来访问具体的服务的,所以下面我们需要改造我们代码的配置文件,改成我们在 k8s 中的服务名。

4.2 修改代码配置文件

修改 Dockerfile,我们这里不用 pig 原来的 Dockerfile,因为我们这里只部署主要的服务,而且不使用 docker-compose 所以需要修改一下,并且重名。

pig-auth

FROM hub.k8s.com/k8s-pig/java:8-jre
MAINTAINER Pig lengleng <wangiegie@gmail.com>

ADD pig-auth.jar /app/

CMD ["java", "-Xmx500m", "-jar", "/app/pig-auth.jar"]

EXPOSE 3000

pig-config

FROM hub.k8s.com/k8s-pig/java:8-jre
MAINTAINER Pig lengleng <wangiegie@gmail.com>

ADD pig-config.jar /app/
CMD ["java", "-Xmx200m", "-jar", "/app/pig-config.jar"]

EXPOSE 4001

pig-eureka

FROM hub.k8s.com/k8s-pig/java:8-jre
MAINTAINER Pig lengleng <wangiegie@gmail.com>

ADD pig-eureka.jar /app/
CMD ["java", "-Xmx200m", "-jar", "/app/pig-eureka.jar"]

EXPOSE 1025

pig-gateway

FROM hub.k8s.com/k8s-pig/java:8-jre
MAINTAINER Pig lengleng <wangiegie@gmail.com>

ADD pig-gateway.jar /app/

CMD ["java", "-Xmx500m", "-jar", "/app/pig-gateway.jar"]

EXPOSE 9999

pig-upms-service

FROM hub.k8s.com/k8s-pig/java:8-jre
MAINTAINER Pig lengleng <wangiegie@gmail.com>

ADD pig-upms-service.jar /app/

CMD ["java", "-Xmx1000m", "-jar", "/app/pig-upms-service.jar"]

EXPOSE 4000

所有模块下的 bootstrap.yml 文件里的服务注册地址都要改为 http://pig:gip6666@pig-eureka:1025/eureka。

包括 pig-config 下的 application.yml 文件。

修改 pig-gateway 下的 bootstrap.yml 的认证服务器地址:

修改 pig-config 模块下的 config 文件夹下的配置文件:

application-dev.yml

pig-auth-dev.yml

pig-upms-service-dev.yml

总之就是把所有和其他服务相关联的都要改成服务名。这里的服务名是我们服务在 k8s 集群中的服务名。

4.3 编写我们的构建脚本

打开我们在 Jenkins 创建的 Jenkins-pig 任务,在最后的 Pipeline script 项中添加如下内容:

#!groovy
pipeline {
    agent any

    environment{
       
        SCRIPT_PATH="/soft/project/pig/script"

    }
    stages {
        stage('获取代码') {
            steps {
                echo "start fetch code from gitlab:${REPOSITORY}"
                deleteDir()
                git "${REPOSITORY}"
            }
        }

        stage('编译') {
            steps {
                echo "start compile"
                sh "mvn -U -am clean package -Dmaven.test.skip=true"
            }
        }

        stage('构建镜像') {
            steps {
                echo "start build images"
                sh "${SCRIPT_PATH}/k8s-pig-build.sh  k8s-pig ${SCRIPT_PATH}"
            }
        }

    }
}

SCRIPT_PATH 就是我们脚本存放的路径,REPOSITORY 代码仓库地址。

在我们脚本存放的路径创建构建 docker 镜像的脚本。

cd /soft/project/pig/script

vim k8s-pig-build.sh
#!/bin/bash

#时间格式化
TIME_TAG=`date "+%Y%m%d%H%M"`
#Docker 私有仓库地址
IMAGE_REPOSITORY=hub.k8s.com/$1/
# git提交的版本
GIT_VERSION=`git log -1 --pretty=format:"%h"`

MOUDLE=(pig-eureka pig-config pig-auth pig-gateway)

cd doc/docker/
echo "DockerFile所在当前路径:" &&  pwd

for moudleName in ${MOUDLE[@]};do
echo "构建$moudleName is begining.."

# 创建模块的Dockerfile文件夹
mkdir -p Docker_$moudleName
mv $moudleName Docker_$moudleName/Dockerfile

cd ../../
echo "目录:" && pwd
#将打好的jar包移动到模块的Dockerfile文件夹下
mv $moudleName/target/$moudleName.jar doc/docker/Docker_$moudleName/$moudleName.jar
cd doc/docker/Docker_$moudleName

#组装docker镜像的名称
IMAGE_NAME=${IMAGE_REPOSITORY}$moudleName:${TIME_TAG}_${GIT_VERSION}

echo "开始构建镜像:${IMAGE_NAME}"
docker build -t ${IMAGE_NAME} .

echo "开始推送镜像:${IMAGE_NAME}至私有仓库:${IMAGE_REPOSITORY}"
docker push ${IMAGE_NAME}

echo "删除当前镜像:${IMAGE_NAME}"
docker rmi ${IMAGE_NAME}

cd ../

#将构建好的镜像名称存到本地,后面发布的时候会用到
echo "${IMAGE_NAME}" > $2/image_$moudleName

echo "构建$moudleName is ending..."
done

echo "目录DOCKER:" && pwd

mkdir Docker_pig-upms-service

cd ../../
echo "目录:" && pwd

cd pig-modules/pig-upms-service/target/

echo "目录JAR->:" && pwd && ls
mv pig-upms-service.jar /root/.jenkins/workspace/Jenkins-pig/doc/docker/Docker_pig-upms-service/pig-upms-service.jar

cd ../../../

mv doc/docker/pig-upms-service doc/docker/Docker_pig-upms-service/Dockerfile

echo "目录:" && pwd && ls
cd doc/docker/Docker_pig-upms-service

echo "开始构建:${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION}"
docker build -t ${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION} .

echo "开始推送:${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION}"
docker push ${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION}

echo "删除当前镜像:${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION}"
docker rmi ${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION}

echo "${IMAGE_REPOSITORY}pig-upms-service:${TIME_TAG}_${GIT_VERSION}" > $2/image_pig-upms-service

echo "构建pig-upms-service is ending..."

echo "所有镜像构建、推送完毕..."

为脚本添加可执行权限

chmod -R 777 k8s-pig-build.sh

脚本都有注释,这里说一下脚本中传入了两个参数,k8s-pig 为 docker 私有仓库中创建的项目,
S
C
R
I
P
T
P
A
T
H
为脚本存放地址。这样动态传值就会让我们的脚本针对 pig 项目变得通用起来,可以用私有仓库的项目来区分不同的环境。

以及 pig-upms-service 模块并没有放到 for 循环中,因为 pig-upms-service 模块目录层级更深一级。

4.4 在 k8s 集群中初始化我们的项目

这里说一下,项目第一次部署的时候需要我们手动的在集群中创建,只有已存在的项目才能升级。我们需要登录到私有仓库获取每个模块的镜像名称。

k8s-pig-eureka.yaml

vim k8s-pig-eureka.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pig-eureka
  name: pig-eureka
spec:
  ports:
  - port: 1025
    protocol: TCP
    targetPort: 1025
    nodePort: 30001
  selector:
    app: pig-eureka
  type: NodePort
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: pig-eureka-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: pig-eureka
    spec:
      containers:
      - name: pig-eureka
        image: hub.k8s.com/k8s-pig/pig-eureka:201808142201_67bd117
        ports:
        - containerPort: 1025

这里我们指定了外部访问的端口:30001

kubectl create -f k8s-pig-eureka.yaml --namespace kube-pig

k8s-pig-config.yaml

vim k8s-pig-config.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pig-config
  name: pig-config
spec:
  ports:
  - port: 4001
    protocol: TCP
    targetPort: 4001
  selector:
    app: pig-config
  type: ClusterIP
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: pig-config-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: pig-config
    spec:
      containers:
      - name: pig-config
        image: hub.k8s.com/k8s-pig/pig-config:201808142231_35f7388
        ports:
        - containerPort: 4001

kubectl create -f k8s-pig-config.yaml --namespace kube-pig

k8s-pig-auth.yaml

vim k8s-pig-auth.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pig-auth
  name: pig-auth
spec:
  ports:
  - port: 3000
    protocol: TCP
    targetPort: 3000
  selector:
    app: pig-auth
  type: ClusterIP
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: pig-auth-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: pig-auth
    spec:
      containers:
      - name: pig-auth
        image: hub.k8s.com/k8s-pig/pig-auth:201808142231_35f7388
        ports:
        - containerPort: 3000

kubectl create -f k8s-pig-auth.yaml --namespace kube-pig

k8s-pig-gateway.yaml

vim k8s-pig-gateway.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pig-gateway
  name: pig-gateway
spec:
  ports:
  - port: 9999
    protocol: TCP
    targetPort: 9999
    nodePort: 30080
  selector:
    app: pig-gateway
  type: NodePort
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: pig-gateway-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: pig-gateway
    spec:
      containers:
      - name: pig-gateway
        image: hub.k8s.com/k8s-pig/pig-gateway:201808142231_35f7388
        ports:
        - containerPort: 9999

这里我们指定了外部访问的端口:30080

kubectl create -f k8s-pig-gateway.yaml --namespace kube-pig

k8s-pig-upms-service.yaml

vim k8s-pig-upms-service.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pig-upms-service
  name: pig-upms-service
spec:
  ports:
  - port: 4000
    protocol: TCP
    targetPort: 4000
  selector:
    app: pig-upms-service
  type: ClusterIP
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: pig-upms-service-deployment
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: pig-upms-service
    spec:
      containers:
      - name: pig-upms-service
        image: hub.k8s.com/k8s-pig/pig-upms-service:201808142231_35f7388
        ports:
        - containerPort: 4000
kubectl create -f k8s-pig-upms-service.yaml --namespace kube-pig

部署完我们的服务可以访问 192.168.1.11:30001 看一下我们的服务的状态,是否注册到 eureka。

到这里我们初始化完我们的微服务。

4.5 让我们的微服务可以在 k8s 里平滑的升级

在 Jenkins 中修改我们的任务 Jenkins-pig,把我们的构建脚本改为如下内容:

#!groovy
pipeline {
    agent any

    environment{
       
        SCRIPT_PATH="/soft/project/pig/script"

    }
    stages {
        stage('获取代码') {
            steps {
                echo "start fetch code from gitlab:${REPOSITORY}"
                deleteDir()
                git "${REPOSITORY}"
            }
        }

        stage('编译') {
            steps {
                echo "start compile"
                sh "mvn -U -am clean package -Dmaven.test.skip=true"
            }
        }

        stage('构建镜像') {
            steps {
                echo "start build images"
                sh "${SCRIPT_PATH}/k8s-pig-build.sh  k8s-pig ${SCRIPT_PATH}"
            }
        }

        stage('发布系统') {
            steps {
                echo "start deploy pods"
                sh "${SCRIPT_PATH}/k8s-pig-deploy.sh ${SCRIPT_PATH} kube-pig"
            }
        }

    }
}

增加了一步:发布系统,传入了两个参数,和构建镜像的思路一样都是希望针对 pig 项目脚本能通用。

编写我们的部署脚本:

vim k8s-pig-deploy.sh

#获取最新的镜像名称
PIG_CONFIG_IMAGE=`cat $1/image_pig-config`
#替换运行中服务的镜像
echo "update image to deployments/pig-config-deployment pig-config:${PIG_CONFIG_IMAGE}"
kubectl set image deployments/pig-config-deployment pig-config=${PIG_CONFIG_IMAGE} --namespace=$2
echo "update pig-config is ending"
# 延迟的作用是因为服务的发布是有先后顺序的
sleep 30s
PIG_AUTH_IMAGE=`cat $1/image_pig-auth`
echo "update image to deployments/pig-auth-deployment pig-auth:${PIG_AUTH_IMAGE}"
kubectl set image deployments/pig-auth-deployment pig-auth=${PIG_AUTH_IMAGE} --namespace=$2
echo "update pig-auth is ending"
sleep 30s
PIG_GATEWAY_IMAGE=`cat $1/image_pig-gateway`
echo "update image to deployments/pig-gateway-deployment pig-gateway:${PIG_GATEWAY_IMAGE}"
kubectl set image deployments/pig-gateway-deployment pig-gateway=${PIG_GATEWAY_IMAGE} --namespace=$2
echo "update pig-gateway is ending"
sleep 30s
PIG_UPMS_SERVICE_IMAGE=`cat $1/image_pig-upms-service`
echo "update image to deployments/pig-upms-service-deployment pig-upms-service:${PIG_UPMS_SERVICE_IMAGE}"
kubectl set image deployments/pig-upms-service-deployment pig-upms-service=${PIG_UPMS_SERVICE_IMAGE} --namespace=$2
echo "update pig-upms-service is ending"

为脚本添加可执行权限

chmod -R 777 k8s-pig-deploy.sh

到这里我们就已经将代码、gitlab、Jenkins、k8s、harbor 连起来了。

4.6 部署前端项目

4.6.1 初始化前端项目

修改我们前端的配置文件

在我们的前端工程下创建一个文件夹:docker

新建两个文件 Dockerfile 和 default.conf

Dockerfile

FROM hub.c.163.com/library/nginx
MAINTAINER Maozk <549595297@qq.com>
RUN rm /etc/nginx/conf.d/default.conf
ADD default.conf /etc/nginx/conf.d/
COPY dist/ /usr/share/nginx/html/
COPY favicon.ico /usr/share/nginx/html/

default.conf

server {
    listen       9000;
    server_name  localhost;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location ~ ^/favicon\.ico$ {
            root    /usr/share/nginx/html/favicon.ico;
    }

    location ^~ /auth/ {
        proxy_pass http://pig-gateway:9999;
    }

    location ^~ /admin/ {
        proxy_pass http://pig-gateway:9999;
    }

}

上面两个文件主要是在构建 Docker 镜像时替换掉 nginx 原来的 default.conf,要注意我们配置文件中的代理地址。

为 Jenkins 任务 Jenkins-pig-ui 添加构建脚本

#!groovy
pipeline {
    agent any

    environment{
       
        SCRIPT_PATH="/soft/project/pig/script/ui"

    }
    stages {
        stage('获取代码') {
            steps {
                echo "start fetch code from gitlab:${REPOSITORY}"
                deleteDir()
                git "${REPOSITORY}"
            }
        }

        stage('编译代码') {
            steps {
                echo "start npm run build"
                sh "npm install"
                sh "npm run build"
            }
        }

        stage('构建镜像') {
            steps {
                echo "start build nginx images"
                sh "${SCRIPT_PATH}/k8s-pig-ui-build.sh k8s-pig ${SCRIPT_PATH}"
            }
        }
    }
}

在我们脚本存放路径创建脚本(前端的脚本路径和后端的不一样)

vim k8s-pig-ui-build.sh

TIME_TAG=`date "+%Y%m%d%H%M"`
IMAGE_REPOSITORY=hub.k8s.com/$1/
GIT_VERSION=`git log -1 --pretty=format:"%h"`
IMAGE_NAME="${IMAGE_REPOSITORY}pig-ui:${TIME_TAG}_${GIT_VERSION}"

echo "移动静态文件到docker目录"
mv dist docker/dist
cd docker/

echo "开始构建镜像:${IMAGE_NAME}"
docker build -t ${IMAGE_NAME} .

echo "开始推送镜像:${IMAGE_NAME}至私有仓库:${IMAGE_REPOSITORY}"
docker push ${IMAGE_NAME}

echo "删除当前镜像:${IMAGE_NAME}"
docker rmi ${IMAGE_NAME}

echo "${IMAGE_NAME}" > $2/image_pig-ui

echo "构建pig-ui is ending..."

在 k8s 集群中初始化运行我们的前端项目

vim k8s-pig-ui.yaml

apiVersion: v1
kind: Service
metadata:
  labels:
    app: pig-ui
  name: pig-ui
spec:
  ports:
  - port: 9000
    protocol: TCP
    targetPort: 9000
    nodePort: 30000
  selector:
    app: pig-ui
  type: NodePort
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: pig-ui-deployment
spec:
  replicas: 2
  template:
    metadata:
      labels:
        app: pig-ui
    spec:
      containers:
      - name: pig-ui
        image: hub.k8s.com/k8s-pig/pig-ui:201808150355_70a58dd
        ports:
        - containerPort: 9000

指定暴露集群端口:30000

kubectl create -f k8s-pig-ui.yaml --namespace kube-pig

现在我们在宿主机浏览器中访问 192.168.1.11:30000 就可以访问我们的部署好的微服务了,默认用户名密码 admin/123456。

4.7 让我们的前端项目也变成可平滑升级的服务

修改我们的构建脚本,修改为如下内容:

#!groovy
pipeline {
    agent any

    environment{
       
        SCRIPT_PATH="/soft/project/pig/script/ui"

    }
    stages {
        stage('获取代码') {
            steps {
                echo "start fetch code from gitlab:${REPOSITORY}"
                deleteDir()
                git "${REPOSITORY}"
            }
        }

        stage('编译代码') {
            steps {
                echo "start npm run build"
                sh "npm install"
                sh "npm run build"
            }
        }

        stage('构建镜像') {
            steps {
                echo "start build nginx images"
                sh "${SCRIPT_PATH}/k8s-pig-ui-build.sh k8s-pig ${SCRIPT_PATH}"
            }
        }

        stage('部署镜像') {
            steps {
                echo "start deploy nginx images"
                sh "${SCRIPT_PATH}/k8s-pig-ui-deploy.sh ${SCRIPT_PATH} kube-pig"
            }
        }
    }
}

我们增加了一步:部署镜像,添加我们的部署脚本。

vim k8s-pig-ui-deploy.sh

echo "部署镜像"
IMAGE_NAME=`cat $1/image_pig-ui`
echo "update image to deployments/pig-ui-deployment pig-ui:${IMAGE_NAME}"
kubectl set image deployments/pig-ui-deployment pig-ui=${IMAGE_NAME} --namespace=$2
echo "update pig-ui is ending"

添加脚本执行权限

chmod -R 777 k8s-pig-ui-deploy.sh

4.8 配置 Jenkins 邮件服务,将部署信息反馈给我们

4.8.1 安装邮件服务的插件 Email Extension Plugin

参考我们之前安装插件的方法之间安装就好。

4.8.2 配置全局邮箱
系统管理 > 系统设置 > 完成邮箱配置

4.8.3 配置邮件服务

Default Recipients这一项配置多个收件人用逗号隔开。

这里已经返回我们测试邮件的结果。

到这里我们已经将 pig 和 pig-ui 部署到了 k8s 集群环境中,并且配置了 Jenkins 监听指定分支的任务,和构建脚本中平滑的升级 k8s 中的微服务。我们如何实现分环境部呢?我们可以新建一个构建任务例如:Jenkins-pig-dev,配置任务中监听提交的代码分支为refs/heads/master,在构建脚本里的构建镜像传入不同的仓库项目,部署镜像里传入不同命名空间。

需要注意的几件事

  • 部署之前要自己手动的在集群中部署项目。
  • 所有的脚本都需要可执行权限。

  • 部署的项目都需要指定命名空间,不指定默认为 default,这样不利于我们区分。

  • 第一次部署时都要去私有镜像仓库可视化界面获取镜像名称,然后把yaml中的镜像替换掉。

八、 常见问题和常用命令

1. Git 常用命令

# Git global setup
git config --global user.name "Maozk"
git config --global user.email "549595297@qq.com"

# Create a new repository
git clone http://192.168.1.100/Maozk/test.git
cd test
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master

# Existing folder
cd existing_folder
git init
git remote add origin http://192.168.1.100/Maozk/test.git
git add .
git commit -m "Initial commit"
git push -u origin master

# Existing Git repository
cd existing_repo
git remote rename origin old-origin
git remote add origin http://192.168.1.100/Maozk/test.git
git push -u origin --all
git push -u origin --tags

2. Docker 常用命令

菜鸟教程 Docker 命令大全:http://www.runoob.com/docker/docker-command-manual.html

3. kubernetes 常用命令

kubectl get cs //查看集群信息

kubectl get componentstatuses //查看node节点组件状态

kubectl get svc -n kube-system //查看应用

kubectl cluster-info //查看集群信息

kubectl cluster-info dump //更详细的集群信息

kubectl describe --namespace kube-system service kubernetes-dashboard //详细服务信息

kubectl apply -f kube-apiserver.yaml //更新kube-apiserver容器

kubectl delete -f /root/k8s/k8s_images/kubernetes-dashboard.yaml //删除应用

kubectl delete service example-server //删除服务

systemctl start kube-apiserver.service //启动服务。

kubectl get deployment --all-namespaces //启动的应用

kubectl get pod -o wide --all-namespaces //查看pod上跑哪些服务

kubectl get pod -o wide -n kube-system //查看应用在哪个node上

kubectl describe pod --namespace=kube-system //查看pod上活动信息

kubectl describe depoly kubernetes-dashboard -n kube-system

kubectl get depoly kubernetes-dashboard -n kube-system -o yaml

kubectl get service kubernetes-dashboard -n kube-system //查看应用

kubectl delete -f kubernetes-dashboard.yaml //删除应用

kubectl get events //查看事件

kubectl get rc/kubectl get svc

kubectl get namespace //获取namespace信息

kubectl delete node 节点名 //删除节点

kubectl create namespace {名称} //创建namespace

kubectl config set-context $(kubectl config current-context) --namespace={名称} //设置当前namespace

kubectl get service,deployment,pod

4. 常见问题

1.重新初始化 Master 节点或者各个子节点

kubeadm reset

重新初始化配置 k8s 集群,子节点重新 join。

**2.子节点加入 Master 时 Token 过期。

默认 token 的有效期为 24 小时,当过期之后,该 token 就不可用了。

解决方法如下:

#1. 重新生成Token
kubeadm token create

#2.获取ca证书sha256编码hash值
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

#3. 子节点加入集群
kubeadm join 10.0.0.147:6443 --token <New Token> --discovery-token-ca-cert-hash sha256:<New SSH>

4.The connection to the server localhost:8080 was refused – did you specify the right host or port?

在该节点机器上执行如下命令:

sudo cp /etc/kubernetes/kubelet.conf $HOME/
sudo chown $(id -u):$(id -g) $HOME/kubelet.conf
export KUBECONFIG=$HOME/kubelet.conf

5.子节点加入不到 Master,或者子节点访问不了 Api Server

初始化 Master 的时候需要指定 Api Server 能够被访问的 IP 0.0.0.0 是任何 IP 等能访问。

6.Get https://docker-registry:5000/v1/_ping: http: server gave HTTP response to HTTPS client

vim /etc/docker/daemon.json
# 加入如下内容:
{ "insecure-registries":["hub.k8s.com"] }
# 重启服务
systemctl restart docker

7.Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen

# 重启服务
systemctl restart docker

九、效果预览

标签:Kubernetes 发布于:2019-10-19 06:47:32