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

容器实现的是操作系统层面的虚拟化,而虚拟机是实现硬件层面的虚拟化。所以对于应用来说,选择容器会更加轻量级,这意味着它将占用更少的资源,能更快的启动和停止,更灵活地分配资源等等。
PS: 这里并不是说容器一定总比虚拟机好,一般来说,不同的技术方案都有它适应的场景。比如我们需要模拟完整的硬件环境,虚拟机方案显然就更合适了。例如,在办公电脑上,我常常在windows笔记本上使用hyper-v安装一个ubuntu做开发,容器化方案在这里就相形见绌了。
很多时候,容器和虚拟机被一起使用。比如,你买了一台性能很好的高可用服务器,1TB RAM, 64 core CPU,那么为了更合理的分配和管理资源,你可能首先在这台服务器上建立数个virtualbox虚拟机,然后再在虚拟机操作系统里安装docker。
容器编排
容器化是我们能更方便地打包和运行应用,但当需要运行成百上千或者更多容器的时候,容器的自动化管理就十分重要了。对于习惯于实现应用自动化的程序员来说,甚至只管理两三个容器就巴不得有一个工具来完成自动化。容器编排就是来解决这个问题的,比较流行的有Google开发并开源的kubernetes, 及docker官方推的docker swarm。前者几乎已经成为业界规范,不过docker swarm对docker集成的更好,如果熟悉docker的话,docker swarm也会更加容易上手。
容器编排常见的功能 (不想翻译)
- Expose containers by DNS name or IP address.
- Handle load balancing and traffic distribution for containers.
- Automatically mount local and cloud-based storage.
- Allocate specific CPU and RAM resources to containers and then fit them onto nodes.
- Replace or kill problematic containers without jeopardizing application performance and uptime.
- Manage sensitive information like password and tokens without rebuilding containers.
- Change the state of containers and roll back old containers to replace them with new ones.
参考
安装
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永远只运行在一个节点上。
kubernetes定义了一系列的Controllers (控制器)来的定义和管理Pod.
- ReplicaSet
- ReplicationController
- Deployments
- StatefulSets
- DaemonSet
- Garbage Collection
- TTL Controller for Finished Resources
- Jobs - Run to Completion
- CronJob
我们比较(直接)常用到的是Deployment。根据其名字可以看到它的使用场景和应用部署密切相关,比如创建部署、回滚、更新状态、暂停部署等。
以下是一个nginx应用示例 (kubernetes的服务编排一般可以通过yaml声明定义)
- nginx-deployment.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
参考
- https://kubernetes.io/docs/concepts/workloads/pods/pod/
- https://kubernetes.io/docs/tutorials/kubernetes-basics/explore/explore-intro/
- https://kubernetes.io/docs/concepts/workloads/controllers/deployment/
Service
Service将运行在一组Pods上的某个应用抽象为一个服务。kubernetes还集成了服务发现和服务代理来保证服务的高可用性。
- nginx-service.yaml
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/
