一、docker

Docker 架构 | 菜鸟教程

docker由dockerd来接受客户端的命令,它可以下载、管理镜像,创建、管理容器。

由一个镜像可以创建多个容器,系统中运行的虚拟实体,就是容器。

dockerd也可以管理容器与宿主机之间的端口,网络,目录映射。

每个容器有自己的ip吗?是的

容器可以启动,也可以停:docker start/stop <container name/id>

删除正在运行或已停止的容器:docker rm [-f] <container name/id>

进入容器 docker attach 或 nsenter (没试过)

编译:https://www.cnblogs.com/newguy/p/10609765.html

docker相关概念:什么是Docker?看这一篇干货文章就够了! - 知乎 (zhihu.com)

dockerd -- 服务进程

容器 -- 运行的image称为容器

官方教程是理解docker的很好的一个过程https://docs.docker.com/get-started/

看完part1-part10,跟着操作,就能理解dockers关键技术。

启动一个ubuntu镜像,如果没有这个镜像就下载,然后映射宿主机的$(pwd)目录到容器的/src目录,容器启动后,运行bash进程,然后登录进去,如果从bash命令行退出,容器也退出了。

docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash

如何进入一个运行的container:

如何进入、退出docker的container_docker 进入container_我是干勾鱼的博客-CSDN博客

查看正在运行的容器:

docker ps

容器的存储空间,默认是临时目录,容器退出存储的内容就丢失了,要想保留容器中修改的数据,就要映射宿主机目录到容器内,有两种映射方法:volume mount和bind mount,经体验,感觉bind mount更好用一些,下面的启动容器命令就是用的bind mount:

docker run -it --mount type=bind,src="$(pwd)",target=/src ubuntu bash

sudo docker run -d --net=host --name v3ubuntu2204 -v /host-dir:/container-dir aff4994b858e tail -f /dev/null

这个命令从镜像创建容器,并在后台运行

-d 表示在后台运行

-v hostdir:containterdir  映射宿主机目录和容器目录

--net=host 容器和宿主机共网络,使用相同的ip,端口在同一命名空间,就是说对外部来说,容器和宿主机是同一台机器,端口也是不区分宿主机和容器的,在宿主机中访问容器,用户127.0.0.1或者访问容器虚拟出来的一个内部ip地址,端口直接访问容器中的端口,不需要映射。这种配置对于在自己的机器上开发测试很有用

--name 表示要创建的容器的名称

aff4994b858e 是镜像id

tail -f /dev/null 容器启动后执行的命令,执行完这个命令,容器就会退出,所以如果想让容器在后台一直运行,这个命令应该是一个不会退出的命令。

一般来说每个container都有自己的一套完整的文件系统,就是说每个container都可以用命令行登录上去,例如,下面的命令登录了mysql容器下面的文件系统:

[root@srcapp10 getting-started-app]# docker exec -it getting-started-app_mysql_1 /bin/bash
bash-4.4# ls /
bin  boot  dev  docker-entrypoint-initdb.d  entrypoint.sh  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

这个登录了我的app容器文件系统,包括了node,yarn等应用程序:

[root@srcapp10 getting-started-app]# docker exec -it getting-started-app_app_1 /bin/ash
/app # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 node /opt/yarn-v1.22.19/bin/yarn.js run dev
   49 root      0:00 /usr/local/bin/node /app/node_modules/.bin/nodemon src/index.js
   61 root      0:00 /usr/local/bin/node src/index.js
  109 root      0:00 /bin/ash
  115 root      0:00 ps -ef

登录容器的文件系统,一般要指定命令行程序:sh/bash/ash 都可以,只要容器的文件系统里有这样命令。 

docker exec和docker run是不同的,docker run要指定一个镜像,它会创建这个镜像的容器,然后执行后面的命令,而docker exec是让一个已经运行的容器执行后面的命令,docker exec要指定一个容器名。例如:

docker run perl:5.34.0 perl -Mbignum=bpi -wle "print bpi(200)"

# 以perl:5.34.0创建容器,然后在容器中执行perl -Mbignum=bpi -wle "print bpi(200)"

为了能用docker exec

docker run -d -w /home perl:5.34.0 tail -f /dev/null

# 然后

docker exec flamboyant_johnson perl -Mbignum=bpi -wle "print bpi(200)" 

docker compose问题:

自从docker发明以后,出现了其它的相同功能的软件,例如containerd、podman之类的。

对于podman,没有docker compose这个命令,而是docker-compose,这是一个python程序,用yum可以下载,与官方教程(Use Docker Compose | Docker Docs)不同的是,要注意版本问题,如果docker-compose --version是1.6.0+就要在docker-compose.yml文件里指定version '2',而默认是version '1',另外docker-compose的默认输入文件是当前目录下的docker-compose.yml,因此官方文档中的compose.yaml应该改成docker-compose.yml,而内容应该增加version '2',如下:

version: '2'
services:
  app:
    image: node:18-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 172.32.148.228:3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:8.0
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:
 

compose会自动创建docker网络,注意删除旧网络。

创建镜像

创建镜像有两种方式:用Dockerfile构建和用commit构建。

Dockerfile是描述镜像构建过程的文本文件,镜像构建过程包括下载、加入跟文件系统,基于这个跟文件系统、下载软件、将宿主机的文件数据复制到镜像跟文件系统中、设置环境变量、映射镜像与宿主机的目录、执行跟镜像文件系统中的可执行程序、映射宿主机与容器的端口。

这个构建过程中,如果构建操作,导致镜像的跟文件系统增加了数据,那么这个操作就创建了一个层,如下:

SIZE不为0B的操作,创建了一个layer,SIZE为0B的操作,没有创建layer。

这里讨论一下Dockerfile里的COPY和RUN两个操作:

COPY <src> <dest> 将宿主机目录中的内容,复制到镜像文件系统的目录中。

RUN 是在镜像的文件系统中,执行镜像文件系统中的可执行程序,例如执行yum安装软件、执行wget下载。

注意,docker run 可以借助镜像文件系统里的编译系统,编译宿主机文件系统里的源码,这需要将宿主机源码目录映射到容器文件系统的目录。可以参考这篇里用ubuntu20.04及其里面已经准备好的编译环境,编译nebula的过程:使用 Docker 编译 - NebulaGraph Database 手册

使用Dockerfile构建镜像的命令为:

docker build -t nebula-graphd-mybuild:v3.8.0 -f docker/Dockerfile.graphd .

-t 表示构建出的镜像的标签,可以在docker images里查看

-f 表示Dockerfile所在路径,最后还有个点 (.) 表示,-f的路径是相对于当前目录的,-f也可以用绝对路径

除了用Dockerfile,还可以在容器里安装、编译依赖,然后commit,以这种方式固化对容器的修改。

Docker生成镜像的两种方式 - 知乎 (zhihu.com)

我们可以在容器里下载依赖包,然后固化保存,再把容器或者镜像导出,在另一个宿主机上导入镜像,这样另一台宿主机上就直接具备了运行容器中软件的环境了。

如何固化呢?

对一个容器(可以是正在运行的,也可以是已经停止的,其实做commit时,正在运行的容器会暂停)做docker commit,就可以将这个容器转化成一个镜像,例如:

docker commit -m "test image" -a "qinh" badcf92dbc6d test:v1

固化后,新生成了一个image,可以将这个image导出、上传,导出镜像用 docker save

docker save image-id > ubuntuv3.tar.gz

在另一台机器的宿主机上导入镜像用docker load

docker load   < ubuntuv3.tar.gz

注意,还有两个命令,是对容器(而不是镜像)的导出导入,就是:

docker export containter-id > ubuntuv3.tar.gz # 正在运行和停止的容器都可以导出

docker import < ubuntuv3.tar.gz

注意,这里有个细节,导出用 > 而不是帮助里的 -o,使用 -o 会有个权限报错问题。

参考:

Docker(十三)-Docker save and load镜像保存 - 圆圆测试日记 - 博客园 (cnblogs.com)

容器可以运行和关闭,关闭的容器,其数据不受影响,启动后修改还在,但删除Docker容器,则其更改将会全部丢失。

关于容器访问外网问题,我试过用openeular的镜像创建的容器,没法访问外网,没法curl百度,或yum,但是用centos-stream-8的镜像创建的容器,是可以访问外网的,可以ping、curl百度,宿主机没有做什么特殊配置。

Docker-Bridge Network 02 容器与外部通信 - 云中往事cloud - 博客园 (cnblogs.com)

许多容器登入后会发现,容器内的根文件系统没有什么可用的命令,ping,ifconfig都没有,用yum或apt下载也说没有这个软件,我试过ubuntu的镜像,登入后许多命令都没有,办法是apt update,然后许多软件就可以下载了。

查看容器的操作系统发行版和版本:cat /etc/issue 不要用uname -a这样看的是宿主机的操作系统。

关于docker的性能,可以参考下面链接,总的结论是docker与本地程序速度几乎一样,比kvm快一些,只是有一点,在网络速度上docker NAT(容器和宿主机端口映射)方式有较高延迟,但是如果使用 --net=host选项,即不使用端口映射,那就和本地网络速度一样了。

linux - What is the runtime performance cost of a Docker container? - Stack Overflow

二、k8s 

关于kubebuilder 构建operator

利用 kubebuilder 优化 Kubernetes Operator 开发体验 (dongyueweb.com)

The Operator itself is a piece of software running in a Pod on the cluster, interacting with the Kubernetes API server. That’s how it gets notified about the presence or modification of FooBarApp objects. That’s also when it will start running its loop to ensure that the application service is actually available and configured in the way the user expressed in the specification of FooBarApp objects. This is called a reconciliation loop  

operator会引入一个自定义资源(CRD),就像deployment资源(deployment资源是k8s内置资源,用户只要配置yaml文件就能创建deployment资源),operator引入的CRD也一样。

operator = 自定义controller + CRD

CRD可以独立于controller创建,这样就创建了一种资源,如deployment、pod、replicaSet就是一种资源,还可以创建这种资源的对象。但是样只是数据,没有对资源对象的控制,要想有对资源对象的控制,就要创建controller,controller也是一个pod应用,通过kubernetes API控制资源对象。

见这篇文章,写的很清晰:

了解Operator与CRD的区别与实践 - Layzer - 博客园 (cnblogs.com)

相对于虚机,容器的动态性会更强
一方面,单进程的故障、OOM等都可能会导致容器重启,另一方面,K8s的调度器会根据节点的资源状况进行一定的动态调度。从应对的角度,一方面需要配置数据库应用的调度优先级和资源配给,确保K8s尽可能保证数据库pod的资源;另一方面,对于特定的数据库,可以考虑某些节点只预留给数据库Pod使用。 

一个pod里可能会有多个container,如何查看pod里的container名称,有许多办法:

首先创建pod或deployment时可以指定container名。

其次可以用 kubectl describe 查看。

operator其实就是自定义controller+自定义资源,自定义controller就是一个可执行程序,调用client-go库,操作k8s api,controller既可以在k8s集群外执行,也可以作为pod在k8s内执行,通过网络和k8s api,controller可以控制k8s集群和pod里的应用。

在operator里选择deployment里的pod用什么办法呢?用selector,pod的selector也是在定义资源(pod、deployment)时指定的,可以任意指定。

在单机上创建k8s实验环境可以使用kind

kind – Quick Start (k8s.io)

还需单独下载kubectl,这个程序相当于k8s客户端。

在kubernete的源码中有sample-controller例子,这个例子是自定义operator的很好的例子,涉及了代码生成,CRD的编写,controller的编写,这例子生成代码的脚本需要在kubernete源码环境中才能成功,因此还要单独下载sample-controller的代码库进行编译。

关于operator的概念理解和编写:

Operator pattern | Kubernetes

Kubernetes - Operator Pattern 介紹 | hwchiu learning note

写一个简单的 Kubernetes Operator - aneasystone's blog

关于minikube

这是一个单机k8s学习环境,但是在国内因为许多依赖无法下载,所以并不好用,不建议使用。建议使用kind!!!

这篇关于Kubernetes Operator的发展历史的文章,写的太好了,一定要看,帮你破除operator的神秘感:

亲历者说:Kubernetes API 与 Operator,不为人知的开发者战争-阿里云开发者社区

三、k8s使用技巧

进入pod:

kubectl exec -ti <your-pod-name>  -n <your-namespace> -c <your-container-name> -- /bin/sh

如果不指定-c,就进入默认容器。

另外一个pod中运行的多个容器,叫做 sidecar-container,sidecar-container之间共享pod的volume,网络等资源,但是又自己的进程空间和文件系统,参考:

https://kodekloud.com/blog/kubernetes-sidecar-container/

Sidecar Containers | Kubernetes

Init Containers | Kubernetes

一些概念的理解:

PV,PVC,storageClass

k8s里面运行程序的都是一个个pod,pod可以理解为容器,但是运行应用的pod和真实的主机不同,pod退出后是不会保存数据的,在k8s中数据是通过pv保存的,k8s实现了计算和存储分开管理的理念,集群中的存储可以使用各种技术,如nfs、ceph等待,只要按照k8s指定的接口和k8s集成,就能被k8s抽象成PV,而每个pod需要使用磁盘(持久化卷)时,就绑定一个PV,被pod绑定的PV称为PVC,pod中的目录可以挂在到PVC上,这样向这个目录中的文件写数据,就是持久化存储的了。

为了省事,PVC绑定PV时可以不直接操作PV,而是指定storageClass。

这篇文章对PV,PVC,storageClass的解释很好:

k8s之PV、PVC、StorageClass详解-腾讯云开发者社区-腾讯云

查看k8s里有哪些node:

kubectl get nodes -A

service是个啥?

k8s中的service有点像浮动IP,创建pod时,pod都有一个集群内的IP,但是这个IP一般不是集群内其它pod访问它的IP,一般会为这个pod创建一个service,其它pod通过这个service可以访问它背后的pod,不管这个pod怎么变,都能访问到。这个service也有一个IP(可能也有port信息)。可以用命令查看集群中的service

kubectl get svc

注意service是在k8s集群内作为访问pod的服务的入口点,并不能在k8s集群外访问。

将service暴露到集群之外:

kubectl port-forward --address 0.0.0.0 -n qinhao service/<service name> :19530

这个命令会自动寻找一个端口,在集群之外可以访问这个端口,集群之外访问的IP就是执行这个命令的机器。

参考:Kubernetes网络篇——ExternalIP和NodePort - 晴耕小筑

查看k8s中资源的描述:

kubectl get pod <podname> -o yaml

kubectl describe pod/<podname>

kubectl logs <podname>

k8s中编辑资源属性:

kubectl edit pod milvus-release-minio-bd65fb99d-s7f2g -n milvus-product

直接下载镜像起一个pod:

kubectl run qinh-etcd-test --image=172.32.150.15/qinhao/milvus/etcd:v3.5.14

删除资源时卡住,强制删除:

例如删除milvus资源对象milvus-qinh时卡住,强制删除:

kubectl patch milvus/milvus-qinh --type json --patch='[ { "op": "remove", "path": "/metadata/finalizers" } ]'

使用helm部署:

helm install my-nebula-operator --namespace qinh ./charts/nebula-operator/ -f ./charts/nebula-operator/values.yaml

my-nebula-operator 是这次部署的名称,可以卸载时使用。

--namespace qinh 是将资源部署在qinh命名空间中,命名空间要事前创建。

./charts/nebula-operator/ 是chart包解压后的目录,里面有个values.yaml,是关于这次部署的各项配置,对于本地安装(chart配置文件在本地,镜像还是要远程拉取),需要指定chart解压后的目录。

-f ./charts/nebula-operator/values.yaml 是chart包里的配置文件,非常重要,决定了部署时的各种参数,如镜像地址,这个是可选的,只要指定了chart包目录,helm会到目录下自己寻找values.yaml。

k8s中特殊的container

Init Containers | Kubernetes

Sidecar Containers | Kubernetes

https://kodekloud.com/blog/kubernetes-sidecar-container/

k8s中有两中特殊container:Init Container和Sidecar container。 

参考:

community/contributors/devel/controllers.md at 8cafef897a22026d42f5e5bb3f104febe7e29830 · kubernetes/community · GitHub

client-go/examples/workqueue/main.go at master · kubernetes/client-go · GitHub

GitHub - kubernetes/sample-controller: Repository for sample controller. Complements sample-apiserver

Client-go Informer 使用 - 知乎 (zhihu.com)

k8s中label和label selector的基本概念及使用方法 - Zhai_David - 博客园 (cnblogs.com)

k8s之StatefulSet详解-CSDN博客

go - 如何使用 go-client 在 kubernetes 中获取 pod 的状态 - IT工具网 (coder.work)

一文图解Kubernetes的持久化存储解决方案 - 知乎 (zhihu.com)

remotecommand package - k8s.io/client-go/tools/remotecommand - Go Packages

探索client-go中的exec和cp的原理 - 刘达的博客 (imliuda.com)

client-go使用总结一,获取pod状态_51CTO博客_client-go 文档

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐