距离上一篇k8初窥门径(二)已经过去了一段时间了,因为中间在做CI/CD的东西,这一篇专门写入门级别的实战案例,本地仓库好了就可以把镜像都集中到本地了
0x00. 镜像仓库对应 都是kubeguide库下的镜像,根据版本不同分别对应不同章节或者实践,备注会在下面说明
Dockerhub镜像名
版本/Tag
本地仓库地址
说明
tomcat-app
v1
59.68.29.47/test/tomcat-app:v1
带增查功能的tomcat(第一章)
tomcat-app
v2
59.68.29.47/test/tomcat-app:v2
滚动升级专版(页面更新)
redis-master
latest
59.68.29.47/test/redis-master:latest
guestbook-php-frontend
localredis
59.68.29.47/test/phpdemo:localredis
共处在一个pod中的版本
guestbook-redis-slave
latest
59.68.29.47/test/redis-slave:latest
tomcat
8-jre8
59.68.29.47/test/tomcat:v8
官方tomcat8镜像-Java1.8
jnlp-slave
latest
59.68.29.47/test/jslave:latest
jenkins-slave镜像
busybox
latest
59.68.29.47/test/box:latest
很小的镜像,测试日志之类
mysql
latest
59.68.29.47/test/mysql:latest
官方18年最新镜像
0x01.yaml语法简介 因为yaml在k8中的重要性很高且有些语法必须了解一下,它并不是只能表达简单的K-V对,而是可以表达层级关系、对象和数组。 这里丢两个有用的网站:Json和yaml在线转化(推荐) ,yaml语法检测和自动规范化 。通过对比也可以更好理解后面说的语法,因为有些yaml文件省去了一些标记
以一个具体的tomcat-svc.yaml 为例,对比一下标准写法和json写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 apiVersion: v1 kind: Service metadata: name: tomcat-svc spec: type: NodePort ports: - port: 8080 nodePort: 31000 name: service-port - port: 8005 nodePort: 31001 name: shutdown-port selector: tier: frontend --- apiVersion: v1 kind: Service metadata: name: tomcat-svc spec: ports: - name: service-port nodePort: 31000 port: 8080 - name: shutdown-port nodePort: 31001 port: 8005 selector: tier: frontend type: NodePort
可以重点关注一下 - ,如果我们不加它会是什么效果呢?什么时候需要加呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 { "apiVersion" : "v1" , "kind" : "Service" , "metadata" : { "name" : "tomcat-svc" } , "spec" : { "type" : "NodePort" , "ports" : [ { "port" : 8080 , "nodePort" : 31000 , "name" : "service-port" } , { "port" : 8005 , "nodePort" : 31001 , "name" : "shutdown-port" } ] , "selector" : { "tier" : "frontend" } } }
在线网站观察可知,如果不加 -, 那么首先写多个port参数不会被识别为数组,只会识别一个pod
除此之外,最常用的还是K-V对字典,组合如下:
1 2 3 4 5 6 str1: - name: a num: 1 - name: b num: 2 - name: "{var: tier}"
0x02.实战演练 第一章的tomcat-mysql组合已经在初窥门径(二)实现过,也优化改进了,这里丢一下我逆向查看的源码h和结构。首先tomcat-app 默认进来再 /usr/local/tomcat 下,tomcat文件夹几个核心文件&目录:
conf/server.xml (设置http端口默认8080开启服务,8005关闭服务)
logs (日志)
webapps(源码)
work (JSP转为java文件和class文件)
demo项目代码结构图
1 2 3 4 5 6 7 8 9 [root@localhost webapps]$ tree ├── demo │ ├── index.jsp │ ├── input.html │ ├── insert.jsp │ └── WEB-INF │ ├── lib │ │ └── mysql-connector-java-5.1.37.jar │ └── web.xml
这是完整index.jsp源码。可以清楚看到流程,我简单注释了一下核心
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > <title>HPE University Docker&Kubernetes Learning</title> </head> <body align="center" > <% java.sql.Connection conn=null ; java.lang.String strConn; java.sql.Statement stmt=null ; java.sql.ResultSet rs=null ; Class.forName("com.mysql.jdbc.Driver" ).newInstance(); try { Class.forName("com.mysql.jdbc.Driver" ); String ip=System.getenv("MYSQL_SERVICE_HOST" ); String port=System.getenv("MYSQL_SERVICE_PORT" ); ip=(ip==null )?"localhost" :ip; port=(port==null )?"3306" :port; System.out.println("Connecting to database..." ); conn = java.sql.DriverManager.getConnection("jdbc:mysql://" +ip+":" +port+"?useUnicode=true&characterEncoding=UTF-8" , "root" ,"123456" ); stmt = conn.createStatement(); String sql = "show databases like 'HPE_APP'" ; rs =stmt.executeQuery(sql); if (!rs.next()) { sql = "CREATE DATABASE HPE_APP DEFAULT CHARSET utf8 COLLATE utf8_general_ci" ; stmt.executeUpdate(sql); System.out.println("Database created successfully..." ); sql = "CREATE TABLE HPE_APP.T_USERS (ID INT NOT NULL AUTO_INCREMENT , USER_NAME VARCHAR(100), LEVEL VARCHAR(20), PRIMARY KEY ( ID ))" ; stmt.executeUpdate(sql); System.out.println("table created successfully..." ); sql="insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values('me','100')" ; stmt.executeUpdate(sql); sql="insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values('our team','100')" ; stmt.executeUpdate(sql); sql="insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values('HPE','100')" ; stmt.executeUpdate(sql); sql="insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values('teacher','100')" ; stmt.executeUpdate(sql); sql="insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values('docker','100')" ; stmt.executeUpdate(sql); sql="insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values('google','100')" ; stmt.executeUpdate(sql); System.out.println("demo records inserted successfully..." ); } %> <h2>Congratulations!!</h2> <br></br> <input type="button" value="Add..." onclick="location.href='input.html'" > <br></br> <TABLE align="center" border="1" width="600px" > <TR> <TD>Name</TD> <TD>Level(Score)</TD> </TR> <% rs = stmt.executeQuery("SELECT * FROM HPE_APP.T_USERS order by id desc" ); while (rs.next()) { System.out.println("find record" ); %> <TR> <TD><%= rs.getString("USER_NAME" ) %></TD> <TD><%= rs.getString("LEVEL" ) %></TD> </TR> <% } %> </TABLE> <% }catch (Exception se){ se.printStackTrace(); %> <h3> Error:<%= se %></h3> <% }finally { try { if (stmt!=null ) stmt.close(); }catch (Exception se2){ } try { if (conn!=null ) conn.close(); }catch (Exception se){ se.printStackTrace(); } } System.out.println("Goodbye!" ); %> </body> </html>
以及插入insert.jsp的源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd" > <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" > <title>HPE University Docker&Kubernetes Learning</title> </head> <body align="center" > <% java.sql.Connection conn=null ; java.lang.String strConn; java.sql.PreparedStatement stmt=null ; java.sql.ResultSet rs=null ; Class.forName("com.mysql.jdbc.Driver" ).newInstance(); try { request.setCharacterEncoding("UTF-8" ); Class.forName("com.mysql.jdbc.Driver" ); String ip=System.getenv("MYSQL_SERVICE_HOST" ); String port=System.getenv("MYSQL_SERVICE_PORT" ); ip=(ip==null )?"localhost" :ip; port=(port==null )?"3306" :port; System.out.println("Connecting to database..." ); conn = java.sql.DriverManager.getConnection("jdbc:mysql://" +ip+":" +port+"?useUnicode=true&characterEncoding=UTF-8" , "root" ,"123456" ); String sql = "insert into HPE_APP.T_USERS(USER_NAME,LEVEL) values(?,?)" ; stmt= conn.prepareStatement(sql); stmt.setString (1 , request.getParameter("user_name" )); stmt.setString (2 , request.getParameter("level" )); stmt.execute(); %> <h3> Success add your info</h3> <% }catch (Exception se){ se.printStackTrace(); %> <h3> Error:<%= se %></h3> <% }finally { try { if (stmt!=null ) stmt.close(); }catch (Exception se2){ } try { if (conn!=null ) conn.close(); }catch (Exception se){ se.printStackTrace(); } } System.out.println("Goodbye!" ); %> <input type="button" name="Submit" value="return" onclick="location.href='index.jsp'" /> </body> </html>
分析完了简单的demo,就直接从dp开始跳过RC和RS
1.Deplyment实践 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 apiVersion: extensions/v1beta1 kind: Deployment metadata: name: frontend spec: replicas: 2 selector: matchLabels: tier: frontend matchExpressions: - {key: tier , operator: In , values: [frontend ]} template: metadata: labels: app: app-demo tier: frontend spec: containers: - name: tomcat-demo image: 59. xx.xx.47/test/java-app:10 imagePullPolicy: IfNotPresent ports: - containerPort: 8080
可以从状态显示看出deploy的特色,有desired(期望值),current(当前值),up-to-data(升级完成pod数),available(当前存活pod),那么我们尝试升级一次版本这些参数的看看变化(为了明显把replicas从2调为6)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 2 2 2 0 1m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 2 2 2 2 3m $ vi tomcat-dp.yaml $ kb replace -f tomcat-dp.yaml deployment "frontend" replaced $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 7 2 2 52m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 7 2 2 52m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 7 3 5 52m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 7 5 5 53m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 7 5 5 53m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 6 6 5 53m $ kb get deploy NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE frontend 6 6 6 6 53m
通过上面的变化过程可以看到,首先期望值会马上改变为最终值,current也会马上增大到期望值+1 ,应该是为了保证多一个pod更新,然后更新完成的pod个数和当前可用的pod都是逐步增长的,最后一个available会很慢 ,有时间可以设定副本为几十然后绘制一下变化图可以很直观看出运行机制了。
并且这里发现一个问题,当副本数从几十突然骤降至个位数的时候,因为大量pod需要退出可能造成退出拥挤,比如副本从30降至2个,很快两个pod就能up-to-date但是最后available的时间却比较长,查看总体集群情况发现退出出现了部分error,记录一下
2.Service实践 k8支持多个endpoint,但是如果有多个ep需要每个都有单独的名字来区分,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 apiVersion: v1 kind: Service metadata: name: tomcat-svc spec: ports: - port: 8080 name: service-port - port: 8005 name: shutdown-port selector: tier: frontend
这里一上来就发现报错没有发现期望的值唔。。 然后我想是不是因为对应的name 语法不太对,原本书上name没有和port对齐,修改了一下发现果然是这问题(原书P33)