主讲内容:docker/kubernetes 云原生技术,大数据架构,分布式微服务,自动化测试、运维。
7月1号-7月29号 8折优惠!!!
7月1号-7月29号 8折优惠!!!
7月1号-7月29号 8折优惠!!!
全栈工程师开发手册 (作者:栾鹏)
架构系列文章
首先我们需要在本地docker中调试运行一遍,再发布到k8s上去。
如果需要在本地部署k8s环境,可以使用mimnikube,参考:https://blog.csdn.net/luanpeng825485697/article/details/80862581
如果在docker拉取公用镜像,例如拉取redis和mysql
拉取mysql和redis镜像
docker pull mysql
docker pull redis
将应用制作成docker镜像,需要我们编写dockerfile文件.
可以参考https://blog.csdn.net/luanpeng825485697/article/details/80921390
编写好dockerfile以后,我们将应用制作成镜像
docker build -t images_name -f dockerfilepath .
上面这句话images_name是自己指定的镜像的名称 dockerfilepath是dockerfile文件的相对路径,最后面的.是当前目录.在dacokerfile文件中就是以最后的这个.代表的路径作为上下文路径的
还是以redis和mysql为例
docker run -p 3306:3306 --name mysql_container -v $PWD/conf:/etc/mysql/conf.d -v $PWD/logs:/logs -v $PWD/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql
docker run -p 6379:6379 --name redis_container -v $PWD/data:/data -d redis redis-server --appendonly yes
上面的语句直接运行镜像,通过命令设置了端口号,容器名称,设置了挂载和部分参数设置.
其中我们也可以看出,对于数据库镜像,我们要将容器的数据文件挂载成本地的.这样容器重启后数据也还在本地.
根据构建的镜像启动容器
docker run --name container_name -p 8881:8881 -d image_name # 容器名 镜像名 映射端口
这样就能按照镜像来启动容器了. 命令中端口映射前面是主机端口,后面是容器环境的端口. 只有绑定了才能将主机端口的信息送达到容器的端口,容器才能从自己的端口处得到信息.
当然也可以启动,停止,删除容器
docker ps -a 查看容器
docker stop container_id 停止容器,但是不删除
docker start contain_id 启动停止的容器
docker rm contain_id 删除容器
这个需要注意的是在docker中,容器启动后,再关闭容器中的内容是会保存的,所以启动一个已经关闭的容器,会在上一次运行以后的结果下再运行.但是这些结果不会保存到镜像中,如果我们将容器删了,再重新根据镜像启动容器,那会是一个原始的容器.
而在k8s中,容器一但关闭,就彻底删除,不可能保留上一次的结果.
上面我们已经学习了一种启动模型
docker run --name container_name -p 8881:8881 -d image_name # 容器名 镜像名 映射端口
启动-d表示后台运行的意思.
当然也可以以交互的方式启动容器
docker run --name container_name -p 8881:8881 -it image_name /bin/bash
这种启动方式,启动后就自动进入容器内部命令行,不过这种启动方式,在退出容器后会,容器会自动关闭.
所以建议还是以-d后台启动的形式启动容器,然后我们再进入正在运行的容器中
docker ps -a 查看所有的容器
docker exec -it container_id /bin/bash 根据容器id进入容器
这样就可以进入一个正在运行的容器,做相关修改
容器内部是一个非常简单的环境,所以不会像我们的电脑包含很多工具。
容器应用配置文件问题
比如最简单的vim都没有,所以你会发现很多命令没法用了。我们想编辑文件,可能就需要安装一下vim
apt-get update
apt-get install vim
但是只要安装了就会增大容器的大小,而且安装的内容并不经常运行,所以我们也可以经本地的文件修改,复制到容器中
docker cp xxx.txt container_id:/xxx/xxx/xxxx
不过这种方法也过于繁琐,所以最好的办法还是通过主机和容器建共享目录。在本机上和容器中使用同一个共享文件。
应用初始化问题
有些应用第一次运行是需要初始化的,比如mysql,我们要创建数据库,这些数据库和数据表创建以后会保存到共享文件中。
比如下面是一个初始化mysql的命令
docker exec进入正在运行的mysql容器
配置mysql
初始化设置mysql数据库
mysql -u root -p
密码123456
创建数据库 create database database_name
然后运行本地初始化脚本
或者直接
use database_name;
create table table_name(face_id varchar(64),age int,gender tinyint, handle_time int, vip tinyint,face_group char(64),face_info char(64),save_path char(64) not null, reg_path char(64));
下面就可以在本机模拟客户端进行调试运行,查看每个容器的运行,查看配置文件该怎么写。以便放在k8s上能正常运行。
当我们成功在docker中成功跑通了我们的所有应用,并修正了所有的问题后,build了最近的镜像。下面我们来在k8s上运行应用
我们需要先将本地docker镜像save到本地压缩文件
docker save -o image_name.tar image_name
再通过其他方式上传到服务器,比如ssh的scp方式
scp image_name.tar [email protected]:/home/xxx/
然后登录服务器,在服务器的docker中加载镜像压缩文件
docker load -i image_name.tar
这样本地的docker中的镜像就到了服务器的docker镜像中,下面需要将docker中的镜像推送到服务器私有仓库的镜像中
这一步操作是管理员做的事情。我们需要先在docker中创建私有仓库,再将k8s指定到docker的私有仓库。这样k8s才能使用私有仓库
1.master主机的docker先从公有仓库下载registry 镜像
docker pull registry
2.master主机的docker启动镜像
docker run -d -p 5000:5000 --restart=always --privileged --name registry-container -v /root/docker/my_registry:/var/lib/registry registry
添加挂载使得本地/root/docker/my_registry用来存放私有仓库的镜像,这样容器重启镜像也不会删除。
3.master主机上修改每个结点的docker配置文件,用来将docker绑定到私有仓库
网络上有些教程,修改/etc/sysconfig/docker这个配置文件。但是这些教程对应的docker版本比较老,所以不要使用这些教程。新版本的docker使用下面的命令
vi /lib/systemd/system/docker.service
添加或修改
ExecStart=/usr/bin/dockerd -H fd:// --insecure-registry=192.168.42.58:5000
如果不配置上面的情况会出现https错误
重启docker
systemctl daemon-reload
systemctl restart docker
service docker stop 关闭docker服务
service docker start 启动docker服务
上面的教程是使docker能正常拉取私有仓库镜像
我们平时基本把镜像部署在k8s中,所以k8s绑定docker私有仓库镜像也需要对k8s进行设置,以避免https错误,当然如果你的版本正确,是不会出现https错误,那样也就不需要修改配置文件。
根据k8s文件版本不同,也需要修改k8s的配置文件kube-apiserver文件,也是要添加
--insecure-registry=192.168.42.58:5000
不过每种格式的书写格式可能不同。
如果是minikube也可以设置insecure-registry。根据版本不同包含向量两种写法
minikube start --insecure-registry=192.168.42.58:5000
但是如果你已经‘minikube start’过,那必须先:
minikube delete
或者
minikube start --insecure-registry "192.168.42.58:5000" 我的版本比较新,这种成功的
如果你觉的在服务器上搭建私有仓库麻烦,你可以在docker-hub上申请私有仓库,那样就不需要修改配置文件了。
如果想要控制registry的使用权限,使其只有在登录用户名和密码之后才能使用的话,还需要做额外的设置。如果只是调试那就不需要太多的控制,可以忽略这一步。
先创建存储账号密码的地方
mkdir /data/docker-registry-auth/
创建账号密码
docker run --entrypoint htpasswd registry:latest -Bbn username password > /data/docker-registry-auth/htpasswd
例如
docker run --entrypoint htpasswd registry:latest -Bbn luanpeng 123456 > /data/docker-registry-auth/htpasswd
上面这条命令是为luanpeng用户名生成密码为123456的一条用户信息,存在/data/docker-registry-auth/htpasswd文件里面,文件中存的密码是被加密过的。
此容器启动后会自动停止。
检查下htpasswd是否已经被创建:
$ cat /data/docker-registry-auth/htpasswd
luanpeng:$2y$05$WcUjExtIbG4w1.viDLYxQeo65dUzVeN80YWHwuIpWHTwDIjXRnJIS
使用带用户权限的registry时候,容器的启动命令就跟上面不一样了,将之前的容器停掉并删除,然后执行下面的命令:
docker run -d -p 5000:5000 --restart=always --name registry-container -v /data/docker-registry-auth/:/auth/ -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -v /root/docker/my_registry:/var/lib/registry registry:latest
上面的命令添加了配置文件的映射,又添加了镜像存储地方的映射
带有用户权限的registry启动以后,如果直接想查看仓库信息、pull或push都会出现权限报错,必须先登录
sudo docker login --username=luanpeng 192.168.42.58:5000 # 登录
docker pull 192.168.42.58:5000/images_name:latest
将镜像推送到带有访问权限的私有仓库也是一样
sudo docker login --username=yourusername 192.168.42.58:5000
$ sudo docker tag [ImageId] 192.168.42.58:5000/images_name:[镜像版本号]
$ sudo docker push 192.168.42.58:5000/images_name:[镜像版本号]
根据提示,输入用户名和密码即可。如果登录成功,会在/root/.docker/config.json文件中保存账户信息,这样就可以继续使用了。
上传以后可以查看
curl http://192.168.42.58:5000/v2/_catalog 查看私有仓库里面的镜像
curl http://192.168.42.58:5000/v2/imagename/tags/list 查看私有仓库里面的镜像的版本
(下面是阿里云私有镜像)
sudo docker login --username=yourusername registry.cn-shenzhen.aliyuncs.com
$ sudo docker tag [ImageId] registry.cn-shenzhen.aliyuncs.com/haibincoder/centos:[镜像版本号]
$ sudo docker push registry.cn-shenzhen.aliyuncs.com/haibincoder/centos:[镜像版本号]
如果是minikube启动的单机效果可以不用设置账号密码。
直接使用
docker login localhost:5000 就可以登录了,账号密码就是电脑账号密码
为k8s集群创建Secret
启动私有仓库账号密码权限以后,k8s链接私有仓库就需要账号密码了。
kubectl delete secret your-registr-name 删除旧的或错的秘钥,当然也可以不删除
kubectl create secret docker-registry lpsecret --docker-server=192.168.42.58:5000 --docker-username=luanpeng --docker-password=123456 [email protected] -n kube-system
使用命令方式创建邮箱为必选参数,老版本可能不是必填项
注意: -n your-namespaces 为指定命名空间,需要自己先创建命名空间,(后面的教程包含创建命名空间)一般搭建k8s集群时,建议新建一个命名空间来隔离资源。
your-registr-name为你为保密字典定义的名称。为后面的yaml中就要填写这个秘钥的形成
spec:
imagePullSecrets:
- name: your_registr_name
containers:
- name: your-container # 容器名称
image: 192.168.42.201:5000/your_images:1.0 # 基于的镜像名, 根据镜像创建容器
创建以后你就可以在k8s的dashboard中保密字典类目看到你创建的秘钥
如果没有dashboard可以通过下面的命令查看
kubectl get secret -n your-namespaces
在k8s中拉取私有仓库的镜像有时也会报错https错误,这个时候就需要修改k8s的配置文件
要想在k8s中拉取镜像,必须按照上面的教程,为k8s绑定私有仓库服务器,并配置私有仓库提供的账号密码。
对于有权限限制的docker私有仓库,我们要先使用docker login进行登录
sudo docker login --username=yourusername 192.168.42.58:5000
docker tag image_name:latest 192.168.42.58:5000/image_name:latest #在docker镜像标记为私有仓库的镜像
docker push 192.168.42.58:5000/image_name:latest # 将本地镜像push到私有仓库
如果出现下面类似的错误,说明使用的是https,需要按上面的教程修改docker配置文件
v1 ping attempt failed with error: Get https://192.168.174.128:5000/v1/_ping: http: server gave HTTP response to HTTPS client
如果tag错了,想要删除的话需要
关闭容器后
docker rmi 192.168.42.58:5000/image_name 先根据名称删除,删除tag
docker rmi 1c8ea16890a8 再根据镜像id删除,删除镜像
查看仓库里面情况
curl http://192.168.42.58:5000/v2/_catalog
查看私有仓库里面的镜像
curl http://192.168.42.58:5000/v2/imagename/tags/list
查看私有仓库里面的镜像的版本
curl http://192.168.42.58:5000/v2/imagename/manifests/tag (镜像每一层的哈希值(digest))
推送到私有仓库以后,自定义镜像便有了镜像地址。在后面编写yaml文件时,需要使用私有仓库镜像的地址
若在将镜像推送到私有仓库时,有错误类似Get https://192.168.42.58:5000/v1/_ping: http: server gave HTTP response to HTTPS client
修改/etc/docker/daemon.json文件,写入:
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
{
"insecure-registries":["192.168.42.58:5000"]
}
或者修改
vi /lib/systemd/system/docker.service
添加或修改
ExecStart=/usr/bin/dockerd -H fd:// --insecure-registry=192.168.42.58:5000
或者修改
/etc/init/docker.conf,在其中增加--insecure-registry 192.168.112.136:5000
由于各个版本不一样,所以修改的地方就不一样
//重启docker
service docker restart
k8s的控制是通过~/.kube/config文件来实现的。
所以我们只需要将服务器上/.kube/config的文件内容拷贝到本地pc机器上的/.kube/config就可以通过kubectl来控制服务器上的k8s的资源了。
如果本地使用monikube调试,/.kube/config文件必须删除以后,monikube才能重新创建config文件,不会自动修改config。所以如果我们当前想通过minikube调试,就将/.kube/config文件删除,再启动minikube start,如果想控制远程服务器的k8s,就修改/.kube/config为远程服务器的/.kube/config内容。
yaml文件的语法格式可以参考:https://blog.csdn.net/luanpeng825485697/article/details/79478338
当然,如果你已经学过了或者你有现成的yaml文件demo可以直接跳过
创建文件create_namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: luanpeng
labels:
name: luanpeng
在当前目录下运行
kubectl create -f create_namespace.yaml
就可以在kubectl链接的k8s上创建命名空间luanpeng这个空间了。以后设置资源都可以在这个空间中设置
这里我们以创建service服务为例,分别以私有镜像和共有镜像为例。
使用公有镜像创建容器,比如我们使用官方最新版的mysql来创建服务。
如果我们创建的是共有镜像,我们就不需要前面介绍的那些build镜像,上传,加载等一系列操作。只需要编写yaml文件。系统会自动从官方拉取镜像。不过要保障官方有此版本的镜像文件。
创建mysql.yaml文件
---
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
namespace: luanpeng
labels:
app: mysql-service
spec:
type: NodePort
ports:
- port: 3306
targetPort: 3306
protocol: TCP
name: http
selector:
app: mysql-deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: mysql-deployment
namespace: luanpeng
spec:
selector:
matchLabels:
app: mysql-pod
replicas: 1
template:
metadata:
labels:
app: mysql-pod
spec:
nodeName: node1 # 使用nodeName可以指定运行节点
volumes:
- name: mysql-data
hostPath:
path: /mysql/data
- name: mysql-log
hostPath:
path: /mysql/log
- name: mysql-conf
hostPath:
path: /mysql/conf
containers:
- name: mysql-container
image: mysql:5.6 # 最新版本的不能进行划分数据卷
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: introcks
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
- name: mysql-log
mountPath: /logs
- name: mysql-conf
mountPath: /etc/mysql/conf.d
上面的yaml文件中,我们创建了一个mysql服务,服务下包含一个deployment,deployment下包含一个pod,一个pod下面启动一个container
使用kubectl创建镜像
kubectl create -f mysql.yaml
启动以后我们可以在k8s的dashboard中查看运行情况,如果报错,我们可以先关闭服务,然后根据提示解决问题
kubectl delete -f mysql.yaml
启动mysql的容器后,
我们进入容器
kubectl get pods -n luanpeng # 查看指定空间下的pod
kubectl exec mysql-deployment-db9bc9f9c-5cdpq -it -n luanpeng /bin/bash # 进入指定容器
对数据库进行初始化
初始化设置mysql数据库
mysql -u root -p
密码xxxxx
创建数据库
create database yourdatabase;
然后运行本地初始化脚本
或者直接
use yourdatabase;
create table yourtable(face_id varchar(64),age int,gender tinyint, handle_time int, vip tinyint,face_group char(64),face_info char(64),save_path char(64) not null, reg_path char(64));
desc yourtable; 查看表结构
这样就完成了创建,由于使用了共享卷,所以pod重启以后数据库结构信息仍然存在
对于公司内部的项目, 我们不可能使用公有开放的镜像仓库, 一般情况可能会花钱买 docker私仓服务, 或者说自己在服务器上搭建自己的私仓, 但不管怎样, 我们如何让k8s能够拉取私有仓库的镜像
自己的服务器创建私有仓库
如果在自己服务器上创建私有仓库。则直接在yaml中输入192.168.xx.xx:5000/imagesname:1.0这种镜像地址,然后输入拉取秘钥就可以拉取成功。
....
spec:
imagePullSecrets:
- name: registry-key
containers:
- name: your-container # 容器名称
image: 192.168.42.201:5000/your-images:1.0 # 基于的镜像名, 根据镜像创建容器
.....
使用阿里云提供的私有仓库
1、登录docker镜像仓库
这里以阿里云docker镜像仓库为例
docker login [email protected] registry.cn-hangzhou.aliyuncs.com
之后输入密码就可以了, 这个时候我们可以在配置文件中查看登录情况
cat ~/.docker/config.json
这个时候我们虽然可以通过docker pull命令拉取镜像, 但无法通过k8s创建pod方式拉取
2、生成密钥secret
kubectl create secret docker-registry regsecret --docker-server=registry.cn-hangzhou.aliyuncs.com [email protected] --docker-password=xxxxxx [email protected]
其中:
regsecret: 指定密钥的键名称, 可自行定义
--docker-server: 指定docker仓库地址
--docker-username: 指定docker仓库账号
--docker-password: 指定docker仓库密码
--docker-email: 指定邮件地址(选填)
可以看到当前除了默认的密钥, 还有我们刚才生成的. 另外要注意的是, 该密钥只能在对应namespace使用, 也就是这里的default, 如果需要用到其他namespace, 比如说test, 就需要在生成的时候指定参数 -n test
3、 yml文件加入密钥参数
containers:
- name: channel
image: registry-internal.cn-hangzhou.aliyuncs.com/yin32167/channel:dev-1.0
ports:
- containerPort: 8114
imagePullSecrets:
- name: regsecret
其中imagePullSecrets是声明拉取镜像时需要指定密钥, regsecret 必须和上面生成密钥的键名一致, 另外检查一下pod和密钥是否在同一个namespace, 之后k8s便可以拉取镜像
配置文件一般我们包含在应用中,而入股打包成镜像以后,配置文件就不能再修改了,所以我们有集中方式来实现配置文件。
1、设置挂载,我们可以将配置文件的目录挂载成主机地址或网络地址。
在第一次创建pod时,不要让pod包含任何启动命令,然后进入pod,里面的配置文件是空的,因为容器中配置文件的目录,其实是主机的目录,而这个目录可能都不存在。在容器中,进入配置文件目录,添加配置文件,或修改配置文件。当然也可以通过kubectl cp直接复制到容器指定目录下。这样容器的配置文件目录下就有了配置文件,其实这个配置文件是存储在服务器主机或者网络上的(因为这个目录是通过挂载添加到容器的)。然后删除这个服务(或者pod),重新创建一个服务(pod),这个pod是带有启动命令的。并且也添加了挂载。这个pod启动后就能正常读取配置文件了。
2、通过k8s的configmap实现。
configmap是k8s环境下的配置文件。跟secret一样。通过命令创建存储在k8s中。pod可以绑定configmap来读取配置。
通过命令行创建configmap
kubectl create configmap lykops-config --from-file=db_config_file=database.conf --from-file=ver.conf --from-literal=username=test --from-literal=hostname=localhost
–from-file表示来自文件,直接把文件内容写入configmap中,可以为目录也可以为文件,如果是文件的话,可以使用db_config_file=database.conf来修改key值
–from-literal表示使用键值对配置
通过yaml文件创建configmap
kubectl delete -f lykops-config.yaml
cat << EOF > lykops-config.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: lykops-config
namespace: default
labels:
software: apache
project: lykops
app: configmap
version: v1
data:
PWD: /
user: lykops
mysql.config : |-
username: lykops
host: localhost
port: 3306
EOF
kubectl create -f lykops-config.yaml
ata就是配置变量:
PWD和user两行就是两个环境变量属性
mysql.config : |-就是配置文件,下面的内容是配置文件mysql.config内容
使用ConfigMap
两种方式让pod使用,第一种是环境变量或参数,第二种是文件挂载。
cat << EOF > lykops-cm-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: lykops-cm-pod
labels:
project: lykops
app: lykops-cm
version: v1
spec:
containers:
- name: lykops-cm-pod
image: web:apache
command: ['sh',/etc/run.sh]
env:
- name: SPECIAL_USER
valueFrom:
configMapKeyRef:
name: lykops-config
key: username
resources:
requests:
cpu: 0.01
memory: 8Mi
limits:
cpu: 0.1
memory: 16Mi
volumeMounts:
- name: config-volume
mountPath: /data/
volumes:
- name: config-volume
configMap:
name: lykops-config
EOF
kubectl create -f lykops-cm-pod.yaml
当ConfigMap以数据卷的形式挂载进Pod时,更新ConfigMap(或删掉重建ConfigMap),Pod内挂载的配置信息会热更新,但使用环境变量方式加载到pod,则不会自动更新。