在 Kubernetes 最新版本安装过程和注意事项 中,我已经配置好了 Kubernetes 的基础环境,接下来要按照 Kubernetes 权威指南 书中的内容简单做个练习。
Kubernetes 使用的 v1.13.3 版本,在实际操作时发现和书上 v1.6.3 版本的命令没太大区别,但是由于例子中使用的 mysql 没有指定版本,因此,不管 Kubernetes 版本如何,跟着书上第一章的入门操作时,会遇到各种各样的问题,错误的根源在 mysql 版本,但是根据错误信息解决问题的过程中,往往让人跑偏。
我自己为了方便管理,直接在 /
创建了 /k8s/chapter01
目录。由于我也是正在入门,对概念性的东西以及配置参数理解不深,所以本文只是能让你完成某些功能,并不能让你知道为什么这么做,如果想要了解,可以找一些资料学习。
如果你正好也买了《Kubernetes 权威指南》这本书,先按照 Kubernetes 最新版本安装过程和注意事项 配置好环境,然后再配合本文看第 1 章 Kubernetes 入门,你可以避免很多坑,会让你学的更顺利。
这些坑没必要自己踩一遍,我认为这是写书的人没有考虑周全导致的,是书的问题。
创建 mysql-rc.yaml
配置文件,写入下面的内容:
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
spec:
replicas: 1
selector:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.6.43
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
如果你从来没接触过 yaml 格式,要切记,所有属性名的冒号后面必须有一个空格,必须形如:
name:_mysql
(下划线代表空格,不要真的输入一个下划线),而不能是name:mysql
。
本文中的配置和书上的区别在 image: mysql:5.6.43
,这里指定了一个 5.6
的最新版本,不用 5.7 和最新的 8 是因为驱动或者密码机制有很多不同的地方,基本上指定这个版本后,就没有坑了。
既然用到了 mysql:5.6.43
,建议先通过 docker 下载下来,执行 docker pull mysql:5.6.43
。
只需要执行命令 kubectl create -f mysql-rc.yaml
即可。
如果出错想要删除,可以执行:
kubectl delete -f mysql-rc.yaml
启动后,通过 kubectl get pods
查看状态。
创建 mysql-svc.yaml
配置文件,写入以下内容:
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
type: NodePort
ports:
- port: 3306
nodePort: 30002
selector:
app: mysql
为了方便外网连接数据库查看数据库问题,这里参考书上对 tomcat 的配置,增加了 NodePort
,这样配置后,外网能通过 30002 端口访问 MySQL 数据库。
只需要执行命令 kubectl create -f mysql-svc.yaml
(删除和前面方式一样,改成 delete
即可)。
然后通过 kubectl get svc
查看服务状态。
此时你也可以通过 Navicat 等数据库管理工具连接到 MySQL 数据库查看。
和前面一样的套路,这里不在细分。
创建 myweb-rc.yaml
,内容如下:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 2
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
这里用到了 kubeguide/tomcat-app:v1
镜像,通过 docker 下载即可。
特别注意,上面配置中有个 replicas: 2
,因此通过该配置创建 Pod 时,会出现两个不同的 Pod。
执行命令 kubectl create -f myweb-rc.yaml
创建 Pod,只需要执行一次,但是会出现两个 Pod,例如我这里的输出如下:
[root@k8s-master chapter01]# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-wjmlk 1/1 Running 0 41m
myweb-j94ws 1/1 Running 0 26m
myweb-vvlkg 1/1 Running 0 26m
接下来创建对应的服务,文件为 myweb-svc.yaml
,内容如下:
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
selector:
app: myweb
这里对外公开了 30001 端口。执行命令启动服务 kubectl create -f myweb-svc.yaml
,此时所有的服务如下:
[root@k8s-master chapter01]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 156m
mysql NodePort 10.100.172.159 <none> 3306:30002/TCP 42m
myweb NodePort 10.105.77.133 <none> 8080:30001/TCP 28m
此时访问 http://K8s主机IP:30001/demo/
即可查看效果。
如何连上 docker 容器?
执行 docker ps
查看当前运行的容器(部分)列表如下:
[root@k8s-master chapter01]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
db1fafff0e93 a29e200a18e9 "catalina.sh run" 31 minutes ago Up 31 minutes k8s_myweb_myweb-vvlkg_default_00f1ad16-2ae7-11e9-8861-000c293fb758_0
01a31ce5551e a29e200a18e9 "catalina.sh run" 31 minutes ago Up 31 minutes k8s_myweb_myweb-j94ws_default_00f2305e-2ae7-11e9-8861-000c293fb758_0
51a678ae4a2e k8s.gcr.io/pause:3.1 "/pause" 31 minutes ago Up 31 minutes k8s_POD_myweb-j94ws_default_00f2305e-2ae7-11e9-8861-000c293fb758_0
74397325fb22 k8s.gcr.io/pause:3.1 "/pause" 31 minutes ago Up 31 minutes k8s_POD_myweb-vvlkg_default_00f1ad16-2ae7-11e9-8861-000c293fb758_0
15042ef46260 96e41ac53eac "docker-entrypoint.s…" 45 minutes ago Up 45 minutes k8s_mysql_mysql-wjmlk_default_04017175-2ae5-11e9-8861-000c293fb758_0
31fda404c387 k8s.gcr.io/pause:3.1 "/pause" 45 minutes ago Up 45 minutes k8s_POD_mysql-wjmlk_default_04017175-2ae5-11e9-8861-000c293fb758_0
复制想要连接的容器 ID,例如第一个的 Tomcat 容器 db1fafff0e93
,执行下面命令:
docker exec -it db1fafff0e93 /bin/bash
然后就进入了容器中,此时命令行显示如下:
root@myweb-vvlkg:/usr/local/tomcat#
通过 cd webapps/demo/
进入 web 项目的目录,可以看看 index.jsp
的内容,执行 cat index.jsp
输出如下:
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
HPE University Docker&Kubernetes Learning
<%
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...");
}
%>
Congratulations!!
Name
Level(Score)
<%
rs = stmt.executeQuery("SELECT * FROM HPE_APP.T_USERS order by id desc");
while(rs.next()) {
System.out.println("find record");
%>
<%= rs.getString("USER_NAME") %>
<%= rs.getString("LEVEL") %>
<%
}
%>
<%
}catch(Exception se){
se.printStackTrace();
%>
Error:<%= se %>
<%
}finally{
//finally block used to close resources
try{
if(stmt!=null)
stmt.close();
}catch(Exception se2){
}// nothing we can do
try{
if(conn!=null)
conn.close();
}catch(Exception se){
se.printStackTrace();
}//end finally try
}//end try
System.out.println("Goodbye!");
%>
可以看到两个关键的环境变量:MYSQL_SERVICE_HOST
和 MYSQL_SERVICE_PORT
,控制台执行命令查看:
root@myweb-vvlkg:/usr/local/tomcat/webapps/demo# echo $MYSQL_SERVICE_HOST
10.100.172.159
这里的 IP 和上面执行 kubectl get svc
时 mysql 对应的虚拟 IP 相同。
执行 export
看看所有的环境变量,输出如下:
root@myweb-vvlkg:/usr/local/tomcat/webapps/demo# export
declare -x CATALINA_HOME="/usr/local/tomcat"
declare -x HOME="/root"
declare -x HOSTNAME="myweb-vvlkg"
declare -x JAVA_DEBIAN_VERSION="7u101-2.6.6-1~deb8u1"
declare -x JAVA_HOME="/usr/lib/jvm/java-7-openjdk-amd64/jre"
declare -x JAVA_VERSION="7u101"
declare -x KUBERNETES_PORT="tcp://10.96.0.1:443"
declare -x KUBERNETES_PORT_443_TCP="tcp://10.96.0.1:443"
declare -x KUBERNETES_PORT_443_TCP_ADDR="10.96.0.1"
declare -x KUBERNETES_PORT_443_TCP_PORT="443"
declare -x KUBERNETES_PORT_443_TCP_PROTO="tcp"
declare -x KUBERNETES_SERVICE_HOST="10.96.0.1"
declare -x KUBERNETES_SERVICE_PORT="443"
declare -x KUBERNETES_SERVICE_PORT_HTTPS="443"
declare -x LANG="C.UTF-8"
declare -x MYSQL_PORT="tcp://10.100.172.159:3306"
declare -x MYSQL_PORT_3306_TCP="tcp://10.100.172.159:3306"
declare -x MYSQL_PORT_3306_TCP_ADDR="10.100.172.159"
declare -x MYSQL_PORT_3306_TCP_PORT="3306"
declare -x MYSQL_PORT_3306_TCP_PROTO="tcp"
declare -x MYSQL_SERVICE_HOST="10.100.172.159"
declare -x MYSQL_SERVICE_PORT="3306"
declare -x OLDPWD="/usr/local/tomcat"
declare -x OPENSSL_VERSION="1.0.2h-1"
declare -x PATH="/usr/local/tomcat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/usr/local/tomcat/webapps/demo"
declare -x SHLVL="1"
declare -x TERM="xterm"
declare -x TOMCAT_MAJOR="8"
declare -x TOMCAT_TGZ_URL="https://www.apache.org/dist/tomcat/tomcat-8/v8.0.35/bin/apache-tomcat-8.0.35.tar.gz"
declare -x TOMCAT_VERSION="8.0.35"
入门只是一个很简单的例子,但是因为书中没有指定 mysql 的版本,你会遇到各种各样的问题,细节很重要,有一点点的不认真,都可能要付出百倍的时间来解决,所以希望本文能节省大家的时间,让大家能避免类似的错误。