容器化 Containerization

容器是标准化的软件单元。容器化将软件打包成标准化的单元,从而能够以此进行开发,装载和部署。简单地说,通过底层虚拟化,容器化实现应用Write once, run anywhere, 而且是快速地run anywhere,就像docker图标里被鲸鱼背着的集装箱一样。

容器与虚拟机的对比

container vm

容器实现的是操作系统层面的虚拟化,而虚拟机是实现硬件层面的虚拟化。所以对于应用来说,选择容器会更加轻量级,这意味着它将占用更少的资源,能更快的启动和停止,更灵活地分配资源等等。

PS: 这里并不是说容器一定总比虚拟机好,一般来说,不同的技术方案都有它适应的场景。比如我们需要模拟完整的硬件环境,虚拟机方案显然就更合适了。例如,在办公电脑上,我常常在windows笔记本上使用hyper-v安装一个ubuntu做开发,容器化方案在这里就相形见绌了。

很多时候,容器和虚拟机被一起使用。比如,你买了一台性能很好的高可用服务器,1TB RAM, 64 core CPU,那么为了更合理的分配和管理资源,你可能首先在这台服务器上建立数个virtualbox虚拟机,然后再在虚拟机操作系统里安装docker。

容器编排

容器化是我们能更方便地打包和运行应用,但当需要运行成百上千或者更多容器的时候,容器的自动化管理就十分重要了。对于习惯于实现应用自动化的程序员来说,甚至只管理两三个容器就巴不得有一个工具来完成自动化。容器编排就是来解决这个问题的,比较流行的有Google开发并开源的kubernetes, 及docker官方推的docker swarm。前者几乎已经成为业界规范,不过docker swarm对docker集成的更好,如果熟悉docker的话,docker swarm也会更加容易上手。

容器编排常见的功能 (不想翻译)

参考

安装

docker 安装

不推荐使用系统源或者snap包方式安装docker, 使用的时候会遇到环境或者兼容性的问题。

推荐使用docker官方源进行安装 https://docs.docker.com/install/

Kubernetes 安装

这里我们在本地学习使用kubernetes, 可以选择microk8s

如果没有安装snapd,可以参考以下内容安装

https://snapcraft.io/docs/installing-snapd

sudo snap install microk8s --classic

配置

sudo ufw allow in on cni0 && sudo ufw allow out on cni0
sudo ufw default allow routed

PS: 这里如果漏了先执行这一步,可能会导致容器之间网络访问不通 常见错误为dial tcp 10.152.183.1:443: i/o timeout 其实执行ufw default allow routed的时候已经提示了已生成的iptables规则需要手动更新

Default routed policy changed to 'allow'
(be sure to update your rules accordingly)

这里我直接采用简单暴力的方式解决(全部刷新重建)

sudo microk8s.stop
sudo ufw disable

sudo iptables -F
sudo iptables -X
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT

sudo ufw enable
sudo microk8s.start
sudo usermod -a -G microk8s $USER
sudo chown -f -R $USER ~/.kube
sudo snap alias microk8s.kubectl kubectl
sudo microk8s.start
sudo microk8s status --wait-ready

# 检查是否成功
sudo microk8s.inspect

配置kubeconfig,默认情况下helm等工具使用$HOME/.kube/config作为KUBECONFIG文件访问k8s api, microk8s在把它存放在$SNAP_DATA目录下

kubectl config view --raw >~/.kube/config

代理设置

Docker服务代理设置

我们可以通过给docker daemon设定http代理环境变量来使用代理。

例如如果使用的是http代理(https类似),可以创建/etc/systemd/system/docker.service.d/http-proxy.conf 文件,并添加以下内容:

[Service]
Environment="HTTP_PROXY=http://127.0.0.1:8118/" "NO_PROXY=localhost,127.0.0.1,docker-registry.somecorporation.com"

参考https://docs.docker.com/config/daemon/systemd/

microk8s 代理

编辑/var/snap/microk8s/current/args/containerd-env文件,按照文件里的向导,设置好HTTPS_PROXY环境变量,使用命令sudo systemctl restart snap.microk8s.daemon-containerd.service重启生效。

参考https://microk8s.io/docs/install-proxy

containerd

microk8s 使用containerd替换了原来使用的docker,参考https://github.com/ubuntu/microk8s/commit/7c3311a6d24a0eae482dc09e86471ead41aaa19e PS: docker engine现在也是使用containerd。

不过microk8s使用的containerd为microk8s.containerd 服务,不与apt方式安装的共用sock文件,因此docker pull下来的images microk8s不能直接使用。

我们可以使用containerd手动导入镜像解决该问题(microk8s下载docker镜像比较慢,我们也可以先使用docker下载,然后用microk8s.ctr倒入)

# save image as tar file
# docker save k8s.gcr.io/pause:3.1 > pause.tar

https_proxy=socks5://192.168.50.1:1080 microk8s.ctr image pull docker.io/calico/cni:v3.23.5

# the cri plugin is using the k8s.io namespace
microk8s.ctr --namespace k8s.io image import pause.tar

kubernetes相关概念

试一试

安装好kubernetes后,类似于docker run命令,我们可以使用简单的命令立即启动一个pod。

docker run --name whoami containous/whoami:latest
kubectl run --image=containous/whoami:latest whoami

简单地介绍k8s

这里我们仅以应用开发者的角度简单地介绍kubernetes。

Pod

Pod为描述应用程序的"逻辑主机"进行建模,一个pod可以包含一个或多个相对耦合的容器。在同一个Pod里的容器,他们的许多资源是共享的,甚至可以使用标准IPC通信。

一个Pod永远只运行在一个节点上。

包含一个文件拉取应用和为web服务应用的Pod,文件存储volume是共享的

kubernetes定义了一系列的Controllers (控制器)来的定义和管理Pod.

我们比较(直接)常用到的是Deployment。根据其名字可以看到它的使用场景和应用部署密切相关,比如创建部署、回滚、更新状态、暂停部署等。

以下是一个nginx应用示例 (kubernetes的服务编排一般可以通过yaml声明定义)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:stable
        ports:
        - containerPort: 80

部署

kubectl apply -f nginx-deployment.yaml

# 查看
# kubectl get deployments

参考

Service

Service将运行在一组Pods上的某个应用抽象为一个服务。kubernetes还集成了服务发现和服务代理来保证服务的高可用性。

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx
  ports:
    - protocol: http
      port: 8080
      targetPort: 80

使用kubectl apply后,可以得到以下服务

artificerpi@ky-laptop:~/build/nginx$ kubectl get services
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes      ClusterIP   10.152.183.1    <none>        443/TCP    104m
nginx-service   ClusterIP   10.152.183.39   <none>        8080/TCP   2m37s

# curl http://10.152.183.39:8080

Ingress

Ingress是一个管理外部如何对服务进行访问的API对象,将外部请求路由到kubernetes集群里的服务。也可以理解为内部服务向外暴露。 internet |

[ Ingress ]
[ Services ]

一般由Ingress Controller来实现各种Ingress需求,通常Ingress controller也是一个负载均衡器(Load banlancer),比如Kong,Traefik,ingress-nginx等。

Kubernetes组建库本身没有集成安装Ingress Controller,但一般的云供应商会提供该产品,当然也可以选择上面提到的几个开源方案。 一般云供应商提供的Ingress Controller会集成ISP网络服务给service分配外部IP,这里本地开发测试的话,我们可以使用traefik 或者metalb https://metallb.universe.tf/

Namespace 命名空间

通过namespace定义,Kubernetes支持多租户模式使用。

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: traefik
  name: traefik-ingress-controller

参考

ServiceAccount 服务帐号

服务账户为 Pod 中运行的进程提供了一个标识。通过服务帐号和权限配置(如RBAC), 我们可以保证应用在集群的安全性。 结合namespace的作用, 就算不同的用户(或应用)在同一个kubernetes集群中,也能做好应用隔离和保证服务安全性。

如果要进一步学习请参考官方文档 https://kubernetes.io/docs/concepts/

Kubernetes组件

参考