29、Kubernetes持续集成实践
Kubernetes持续集成实践
本文演示的CI工具主要是以Jenkins为例,并且在此基础之上结合Python完成整个SpringBoot项目的完整性的发布,其发布逻辑主要是结合了docker和kubernetes的python sdk来实现整个项目的发布、编译、打包、上线,一看pipline那low逼的语法就没兴趣,因此此次直接使用Python来完成相应的操作;
其中,因为此次主要是以现今最主流的JAVA的SpringCloud项目为主,在下文中有提供一个简单的SpringCloud项目来做测试,会有四个项目,部署完成之后他们都将以分布式的方式运行,整个大项目用到的组件非常之多,所以在做此项目之前都需要一定的知识储备,那么接下来就分段介绍;
安装环境
此步骤主要是安装各个环境,为后面的配置提供基石,因为用到的组件有点多所以就分段进行,此段主要是为了配置整个项目的所需用到的一些基础环境,至于Kubernetes的安装就不在此描述了,我这边有自己准备一个使用kubeadm初始化出来的一个三节点的Kubernetes集群,此段主要是介绍安装环境;
JDK
配置Java环境,为我们的jenkins提供基础支撑;
# 二进制方式安装JDK,不用编译直接可用;
[root@node4 ~]# tar xf jdk-8u212-linux-x64.tar.gz
[root@node4 ~]# mv jdk1.8.0_212/ /usr/local/jdk
# 配置JDK的环境变量;
[root@node4 ~]# cat >> /etc/profile << EOF
export JAVA_HOME=/usr/local/jdk
export CLASSPATH=.:$JAVA_HOME/jre/lib/*:$JAVA_HOME/lib/*
export PATH=$PATH:$JAVA_HOME/bin
EOF
# 将环境变量加载到当前的shell中;
[root@node4 ~]# source /etc/profile
[root@node4 ~]# java -version
java version "1.8.0_212"
Java(TM) SE Runtime Environment (build 1.8.0_212-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)
Maven
Maven是apache的一个顶级项目,它的出现越来越影响着现在的众多的开源项目,不仅如此,很多公司的很多新项目都采用Maven提倡的方式进行管理。Maven正逐渐侵入我们原先的管理项目的习惯,对于团队的管理,项目的构建,都是一种质的飞跃。当然是我个人的一些项目经验而说的这话。如果原先的团队老大本身的管理非常科学,也有一套其他软件辅助项目的构建、打包、发布等等一系列机制保证;
# 二进制方式安装Maven,不用编译直接可用;
[root@node4 ~]# tar xf apache-maven-3.5.0-bin.tar.gz
[root@node4 ~]# mv apache-maven-3.5.0 /usr/local/maven
# 配置Maven的环境变量,使其在当前shell环境中生效;
[root@node4 ~]# cat >> /etc/profile << EOF
export MAVEN_HOME=/usr/local/maven
export PATH=\$MAVEN_HOME/bin:\$PATH
EOF
[root@node4 ~]# source /etc/profile
# 修改默认的maven仓库地址问国内的阿里云源,后面在编译项目的时候有大用;
[root@node4 ~]# vim /usr/local/maven/conf/settings.xml
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
# 安装成功;
[root@node4 ~]# mvn -v
Apache Maven 3.5.0 (ff8f5e7444045639af65f6095c62210b5713f426; 2017-04-04T03:39:06+08:00)
Maven home: /usr/local/maven
Java version: 1.8.0_212, vendor: Oracle Corporation
Java home: /usr/local/jdk/jre
Default locale: en_US, platform encoding: UTF-8
OS name: "linux", version: "3.10.0-957.el7.x86_64", arch: "amd64", family: "unix"
Tomcat
Apache是web服务器,Tomcat是应用(java)服务器,它只是一个servlet容器,是Apache的扩展,Apache和Tomcat都可以做为独立的web服务器来运行,但是Apache不能解释java程序(jsp,servlet),两者都是一种容器,只不过发布的东西不同:Apache是html容器,功能像IIS一样;Tomcat是jsp/servlet容器,用于发布jsp及java的,类似的有IBM的websphere、BEA的Weblogic,sun的JRun等等,此处配置Tomcat服务器发布我们的Jenkins;
# 二进制方式安装Tomcat,不用编译直接可用;
[root@node4 ~]# tar xf apache-tomcat-8.5.53.tar.gz
[root@node4 ~]# mv apache-tomcat-8.5.53 /usr/local/tomcat
# 将Tomcat的webroot配置为Jenkins的源码地址
[root@node4 ~]# cat > /usr/local/tomcat/conf/server.xml << EOF
<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.startup.VersionLoggerListener" />
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8081" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.LockOutRealm">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
<Context path="" docBase="/data/jenkins/wwwroot" reloadable="true" />
</Host>
</Engine>
</Service>
</Server>
EOF
Jenkins
Jenkins是持续集成工具,在敏捷模式中,通常都是先开发一个小的模块,再逐渐和其他开发好的模块组合在一起来提高产品的完成度。这个过程称之为集成,通常由上传代码,构建应用,部署环境和测试等一系列步骤所组成,但是如果这些步骤都由人手工来做的话,每次都会耗费大量的时间精力在重复的流程上面,所以就有人考虑把集成步骤交给工具来完成,然后不断的循环相同的流程,这个动作我们称之为持续集成,配置将Jenkins的war包放在Tomcat配置文件所定义的docBase里面;
# 配置Jenkins的默认data地址,后面这个目录会作为Jenkins的工作目录,存储Jenkins运行时数据;
[root@node4 ~]# cat >> /etc/profile << EOF
export JENKINS_HOME=/data/jenkins/data
EOF
[root@node4 ~]# source /etc/profile
# 创建Jenkins的webroot和data目录;
[root@node4 ~]# mkdir -p /data/jenkins/{wwwroot,data}
# 将Jenkins程序解压到webroot下面;
[root@node4 ~]# unzip jenkins.war -d /data/jenkins/wwwroot
# 启动Tomcat,并查看Jenkins是否能正常访问;
[root@node4 ~]# /usr/local/tomcat/bin/startup.sh
安装常用插件:
Locale:Jenkins本地语言化插件;
Git Parameter:此插件后面可能会用到,主要是配合Git插件使用的一些参数设置;
Maven Integration:此插件为JAVA项目提供maven编译的环境;
AnsiColor:此插件是让我们是conlsoe里面输出一些信息,可以打印出带颜色的一些信息;
Gitlab
Gitlab是一个用于仓库管理系统的开源项目,使用Git作为代码管理工具,并在此基础上搭建起来的Web服务,可通过Web界面进行访问公开的或者私人项目。它拥有与Github类似的功能,能够浏览源代码,管理缺陷和注释,提供一个代码仓库;
# 安装GitLab,目前以最新的12.x为实验示例,因为官网下载较慢,所以此处直接在清华大学的仓库下载;
[root@node4 ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el7/gitlab-ce-12.9.3-ce.0.el7.x86_64.rpm
# 使用YUM的方式安装,这样会自动解决gitlab的依赖;
[root@node4 ~]# yum install -y gitlab-ce-12.9.2-ce.0.el7.x86_64.rpm
# 修改Gitlab配置,主要修改访问IP和Gitlab的发信者邮箱信息;
[root@node4 ~]# grep "^[^#]" /etc/gitlab/gitlab.rb
external_url 'http://192.168.1.64'
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.163.com"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_user_name'] = "mail0426@163.com"
gitlab_rails['smtp_password'] = "caichangen"
gitlab_rails['smtp_domain'] = "163.com"
gitlab_rails['smtp_authentication'] = :login
gitlab_rails['smtp_enable_starttls_auto'] = true
gitlab_rails['smtp_tls'] = true
gitlab_rails['gitlab_email_from'] = "mail0426@163.com"
user["git_user_email"] = "mail0426@163.com"
# 重载配置,每次修改配置都得重载配置,必须重载,重载完成之后会自动启动所有应用;
[root@node4 ~]# gitlab-ctl reconfigure
Docker
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口;
# 安装Docker,此处直接使用阿里云官方Docker仓库提供的rpm包进行安装;
[root@node4 ~]# wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
[root@node4 ~]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker.repo
# 记得安装一下bash-completion、docker-compose,前者主要用途就是让我们更好更方面的使用Linux系统(命令参数tab补齐),后者是一个Docker编排工具,主要是为了Harbor而装,因为Harbor是依赖于docker-compose的;
[root@node4 ~]# yum install -y bash-completion docker-ce docker-compose
[root@node4 ~]# source /usr/share/bash-completion/bash_completion
[root@node4 ~]# source /usr/share/bash-completion/completions/docker
[root@node4 ~]# systemctl start docker
# 配置内核参数,默认网桥功能为打开,所以为了更好的使用Docker,需要做一些内核的初始化配置;
[root@node4 ~]# cat >> /etc/sysctl.conf << EOF
# iptables透明网桥的实现;
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-arptables = 1
EOF
[root@node4 ~]# sysctl -p
# 设定镜像加速器,让我们更快的pull官方镜像仓库;
[root@node4 ~]# cat >> /etc/docker/daemon.json << EOF
{
"registry-mirrors": ["https://owcyje1z.mirror.aliyuncs.com"]
}
EOF
# 设定iptables,防止Docker将iptables的FORWARD修改为DROP
[root@node4 ~]# sed -i '/^ExecStart.*/aExecStartPost=/usr/sbin/iptables -P FORWARD ACCEPT' /usr/lib/systemd/system/docker.service
# 将Docker的网络端口开启,因为这台主要是来给我们build镜像的,因为测试需要,所以开启TCP地址,正常情况下应该做好安全防护;
[root@node4 ~]# sed -ri 's@(^ExecStart\>.*)@\1 -H tcp://0.0.0.0:2375@g' /usr/lib/systemd/system/docker.service
# 每次修改System风格程序的管理脚本都得重载配置;
[root@node4 ~]# systemctl daemon-reload
# 重启下Docker,让我们的配置生效;
[root@node4 ~]# systemctl restart docker
Harbor
容器仓库,实现快速部署,为实现"Build,Ship and Run Any App,Anywhere"提供基石;
# 离线安装Harbor,需要事先安装好docker-compose;
[root@node4 ~]# tar xf harbor-offline-installer-v1.8.3.tgz
[root@node4 ~]# mv harbor /usr/local/harbor
[root@node4 ~]# cd /usr/local/harbor
# 修改一些基础的配置,比如Host、DataDir等配置信息;
[root@node4 harbor]# grep '^[^#]' harbor.yml
hostname: 192.168.1.64
http:
port: 8181
harbor_admin_password: Harbor12345
database:
password: caichangen
data_volume: /data/harbor
clair:
updaters_interval: 12
http_proxy:
https_proxy:
no_proxy: 127.0.0.1,localhost,core,registry
jobservice:
max_job_workers: 10
chart:
absolute_url: disabled
log:
level: info
rotate_count: 50
rotate_size: 200M
location: /var/log/harbor
_version: 1.8.0
# 第一次安装直接运行install脚本,后面再管理就直接使用docker-compose管理即可;
[root@node4 harbor]# ./install.sh
初始化配置
上面只是一些基础的环境安装操作,那么为了更加贴合实验环境的真切性,这里将我没在笔记里面写出来的一些步骤写出来,让阅读者在拿到这篇文章的时候能更容易的上手,此段主要是为了测试一些基础配置,提供必要的代码、Dockerfile编写,Jenkins基础配置,Docker镜像的编译打包等信息;
测试打包
测试打包,此步骤主要是做一个简单的测试,测试我们的应用程序是否能够正常打包,并且能够正常运行为一个基于http协议的应用程序;
# 因为这阶段的实验主要是以JAVA项目为主,那么我这边也提供了一个JAVA项目作为测试,项目地址为https://github.com/caichangen/SpringCloud.git,目前只是为了测试Dockerfile,所以我们就以里面的eureka项目来做测试;
[root@node4 ~]# git clone https://github.com/caichangen/SpringCloud.git
[root@node4 ~]# cd SpringCloud/eureka/
# 使用一下命令将项目编译为jar包,依赖于maven哦;
[root@node4 ~]# mvn clean install -Dmaven.test.skip=true compile package spring-boot:repackage
# 默认编译成功之后会在与pom.xml文件同级的一个target文件夹下生成一个eureka.jar的包,这就是编译后的包;
[root@node4 eureka]# ls target/eureka.jar
target/eureka.jar
# 先测试下包有没有问题,默认监听6220端口;
[root@node4 eureka]# java -jar target/eureka.jar
# 返回200的response code说明这个包启动成功;
[root@node4 ~]# curl -sIL -w "%{http_code}\n" -o /dev/null localhost:6220
200
编译镜像
此处只是做一个简单的测试,创建Dockerfile并且打包成Docker镜像,然后再进行测试,应用程序是否能正常运行;
# 包没问题了,那么此时我们就准备Dockerfile;
[root@node4 eureka]# cat Dockerfile
FROM gmaslowski/jdk
RUN mkdir -p /data/source
ADD eureka.jar /data/source
CMD ["/opt/jdk/bin/java","-jar","/data/source/eureka.jar"]
EXPOSE 6220/TCP
# 测试打包;
[root@node4 eureka]# mkdir context
[root@node4 eureka]# mv Dockerfile target/eureka.jar context/
[root@node4 eureka]# cd context/
[root@node4 context]# docker build -t eureka:v1 .
[root@node4 context]# docker images eureka:v1
REPOSITORY TAG IMAGE ID CREATED SIZE
eureka v1 1976d9f3339c 58 seconds ago 218MB
# 测试启动eureka容器;
[root@node4 ~]# docker run -it --rm -p 8888:6220 eureka:v1
# 测试访问eureka容器里面的页面,查看是否启动成功,返回200说明Dockerfile也没问题了;
[root@node4 ~]# curl -sIL -w "%{http_code}\n" -o /dev/null localhost:8888
200
提供代码
此步主要是将我们测试的SpringCloud项目上传到我们的私有Gitab,然后还需要在每个项目的内部加入一个Dockerfile,为后面做镜像打包提供基础支撑,具体流程如下;
# 在Gitlab上创建四个项目eureka、zuul、feign、ribbon,分别存储我们的SpringCloud中的四个项目组件;
# 一切准备就绪,现在测试将我们的测试项目传到我们是私有Gitlab,在Gitlab上新建四个项目,eureka、zuul、feign、ribbon;
[root@node4 ~]# git clone https://github.com/caichangen/SpringCloud.git
[root@node4 ~]# cd SpringCloud/eureka/
# 提供Dockerfile和README.md;
[root@node4 eureka]# cat >> Dockerfile << EOF
FROM gmaslowski/jdk
RUN mkdir -p /data/source
ADD eureka.jar /data/source
CMD ["/opt/jdk/bin/java","-jar","/data/source/eureka.jar"]
EXPOSE 6210/TCP
EOF
[root@node4 eureka]# cp ../README.md .
# 初始化git环境,并将我们的远程仓库设置为我们的gitlab仓库,并且push到主干;
[root@node4 eureka]# git init
[root@node4 eureka]# git remote add origin http://192.168.1.64/SpringCloud/eureka.git
[root@node4 eureka]# git add *
[root@node4 eureka]# git commit -m 'add code'
[root@node4 eureka]# git push -u origin master
# 配置zuul;
[root@node4 ~]# cd SpringCloud/zuul/
# 提供Dockerfile和README.md;
[root@node4 zuul]# cat >> Dockerfile << EOF
FROM gmaslowski/jdk
RUN mkdir -p /data/source
ADD zuul.jar /data/source
CMD ["/opt/jdk/bin/java","-jar","/data/source/zuul.jar"]
EXPOSE 6220/TCP
EOF
[root@node4 zuul]# cp ../README.md .
# 初始化git环境,并将我们的远程仓库设置为我们的gitlab仓库,并且push到主干;
[root@node4 zuul]# git init
[root@node4 zuul]# git remote add origin http://192.168.1.64/SpringCloud/zuul.git
[root@node4 zuul]# git add *
[root@node4 zuul]# git commit -m 'add code'
[root@node4 zuul]# git push -u origin master
# 配置feign;
[root@node4 ~]# cd SpringCloud/feign/
# 提供Dockerfile和README.md;
[root@node4 feign]# cat >> Dockerfile << EOF
FROM gmaslowski/jdk
RUN mkdir -p /data/source
ADD feign.jar /data/source
CMD ["/opt/jdk/bin/java","-jar","/data/source/feign.jar"]
EXPOSE 6230/TCP
EOF
[root@node4 feign]# cp ../README.md .
# 初始化git环境,并将我们的远程仓库设置为我们的gitlab仓库,并且push到主干;
[root@node4 feign]# git init
[root@node4 feign]# git remote add origin http://192.168.1.64/SpringCloud/feign.git
[root@node4 feign]# git add *
[root@node4 feign]# git commit -m 'add code'
[root@node4 feign]# git push -u origin master
# 配置ribbon;
[root@node4 ~]# cd SpringCloud/ribbon/
# 提供Dockerfile和README.md;
[root@node4 ribbon]# cat >> Dockerfile << EOF
FROM gmaslowski/jdk
RUN mkdir -p /data/source
ADD ribbon.jar /data/source
CMD ["/opt/jdk/bin/java","-jar","/data/source/ribbon.jar"]
EXPOSE 6240/TCP
EOF
[root@node4 ribbon]# cp ../README.md .
# 初始化git环境,并将我们的远程仓库设置为我们的gitlab仓库,并且push到主干;
[root@node4 ribbon]# git init
[root@node4 ribbon]# git remote add origin http://192.168.1.64/SpringCloud/ribbon.git
[root@node4 ribbon]# git add *
[root@node4 ribbon]# git commit -m 'add code'
[root@node4 ribbon]# git push -u origin master
创建Docker仓库
配置仓库,直接创建一个项目就行了,用户也不需要,因为此实验直接使用admin用户来进行镜像管理,默认Harbor的账号为admin,密码为Harbor12345;
配置Jenkins
Jenkins配置就是一些图形化的配置了,接下来主要是演示Jenkins从拉去代码,到项目打包,然后镜像打包,最后传到Harbor仓库的流程,此处不涉及到Kubernetes,Kubernetes将在下一章节中演示,整个过程采取Python来使用,此实验主要以python-3.5.2为基础,从而实现整个流水化操作;
# 创建一个maven项目;
# 配置eureka的项目地址,以及其他信息;
# 配置Python程序,实现从编译完成到打包镜像并push到Harbor仓库的整个流程,代码的说明在文中都有,主要是调用具体代码见附件eureka-ci.py;
# 安装所需的模块
[root@node4 ~]# pip3 uninstall docker # 先卸载老版本的docker模块
[root@node4 ~]# pip3 install docker -i https://pypi.tuna.tsinghua.edu.cn/simple
# 配置docker支持非安全仓库认证
[root@node4 ~]# cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": ["https://owcyje1z.mirror.aliyuncs.com"],
"insecure-registries":["http://192.168.1.64:8181"]
}
EOF
[root@node4 ~]# systemctl daemon-reload
[root@node4 ~]# systemctl restart docker.service
# 测试发布,可以看到整个发布直至将镜像上传到Harbor是成功的;
# 验证打包好的镜像是否真的已经传输到我们的自建Harbor仓库了;
至此,整个基础组件的测试环节已经结束,下一章节将介绍如何结合Kubernetes完成整个SpringCloud项目的发布;
附件列表