一、docker

Docker 架构 | 菜鸟教程

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

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

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

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

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

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

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

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

查看正在运行的容器:

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: todosvolumes:
   todo-mysql-data:


 

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

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

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

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

如何固化呢?

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

mac docker desktop k8s 使用 docker k8s教程_docker

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容器,则其更改将会全部丢失。

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

许多容器登入后会发现,容器内的根文件系统没有什么可用的命令,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控制资源对象。

相对于虚机,容器的动态性会更强
一方面,单进程的故障、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学习环境,但是在国内因为许多依赖无法下载,所以并不好用,不建议使用。

参考:

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)

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 文档