将应用制作成镜像发布到服务器k8s上作为容器微服务运行。

网易公开课,开课啦!

主讲内容: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镜像上传到服务器docker镜像中

我们需要先将本地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中创建私有仓库

这一步操作是管理员做的事情。我们需要先在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的配置文件

将本地docker镜像推送到私有仓库中

要想在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

将应用制作成镜像发布到服务器k8s上作为容器微服务运行。_第1张图片

由于各个版本不一样,所以修改的地方就不一样

//重启docker

service docker restart

本地控制服务器k8s

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文件语法

yaml文件的语法格式可以参考:https://blog.csdn.net/luanpeng825485697/article/details/79478338

当然,如果你已经学过了或者你有现成的yaml文件demo可以直接跳过

在k8s上创建空间

创建文件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,则不会自动更新。

你可能感兴趣的:(架构,微服务架构)