Published on

从 Docker 到 OpenShift 的容器化笔记

Authors
  • avatar
    Name
    Terry
    Twitter

缺乏系统的实践,每次都会遗忘不少关键内容,于是我决定梳理容器化的发展脉络与配置方法。由于参考资料已相当完备,本文更偏向实践记录与个人补充。

容器化

容器是一种操作系统虚拟化形式。可以使用一个容器来运行从小型微服务或软件进程到大型应用程序的所有内容。容器包含所有必要的可执行文件、二进制代码、库和配置文件。但是,与服务器或计算机虚拟化方法不同,容器不包含操作系统映像。因此,它们更轻便且可移植,其开销很小。在大型应用程序部署中,可以将多个容器部署为一个或多个容器集群。此类集群可由 Kubernetes 等容器编排程序管理。

alt text

了解更多:什么是容器?Containers vs. Virtual Machines (VMs): What's the Difference?

Docker

入门资料:

Docker是一种容器的运行时实现,可以说是事实上的行业标准。

Docker Engine is the industry’s de facto container runtime that runs on various Linux (CentOS, Debian, Fedora, RHEL, and Ubuntu) and Windows Server operating systems.

Docker Desktop

根据Getting Started with Docker Desktop:

Docker Desktop is an easy-to-install application and includes Docker Engine, Docker CLI client, Docker Compose, Docker Content Trust, Kubernetes, and Credential Helper.

所以Docker Desktop 集成好了所有关键的要素,比如Engine, CLI, GUI等等,对于个人用户来说,直接安装Desktop是最省时省力的。

安装

对于Mac和Windows用户来说,不太好单独安装Engine(Docker Engine on Linux, also known as Docker CE)和CLI,直接安装Desktop即可。Linux暂时还没有Desktop,可以通过docker-ce docker-ce-cli使用。

各平台的安装可以参考 Docker — 从入门到实践 · 安装 Docker,讲解细致。

Docker Engine 支持平台

查看官方的Install Docker Engine文档。

底层实现

Docker 底层的核心技术包括 Linux 上的命名空间(Namespaces)、控制组(Control groups)、Union 文件系统(Union file systems)和容器格式(Container format)。

Docker architecture on macOS

Docker 在 macOS 上的实现有所不同,底层运行于一个 Linux 虚拟机。

根据 Under the Hood: Demystifying Docker Desktop For Mac

There is no docker0 bridge on macOS. Because of the way networking is implemented in Docker for Mac, you cannot see a docker0 interface on the host. This interface is actually within the virtual machine.

Kubernetes(k8s)

入门资料:

minikube

minikube is local Kubernetes, focusing on making it easy to learn and develop for Kubernetes.

所以minikube非常适合用户用来学习入门Kubernetes。

minikube dashboard

minikube dashboard 命令可以提供一个 Web 控制台,与 OpenShift 的控制台体验类似。

kubectl

By default, kubectl gets configured to access the kubernetes cluster control plane inside minikube when the minikube start command is executed.

安装 minikube 后默认会安装并配置好 kubectl。

Docker Compose

Docker Compose is a tool for defining and running multi-container applications. It is the key to unlocking a streamlined and efficient development and deployment experience.

简而言之,Docker Compose 也是用于容器编排的工具,Docker 原生支持。参考:Docker Compose 和 Kubernetes 的对比

Docker Compose is the declarative version of the docker cli

  • It can start one or more containers
  • It can create one or more networks and attach containers to them
  • It can create one or more volumes and configure containers to mount them
  • All of this is for use on a single host

Docker Classic Swarm

  • Docker swarm has been abandoned by Docker Inc. and is not being actively maintained or supported.
  • Docker Swarm is for running and connecting containers on multiple hosts.
  • Docker Swarm is a container cluster management and orchestration tool.
  • It manages containers running on multiple hosts and does things like scaling, starting a new container when one crashes, networking containers ...
  • The Docker Swarm file named stack file is very similar to a Docker Compose file
  • The only comparison between Kubernetes and Compose is at the most trivial and unimportant level: they both run containers, but this says nothing to help one understand what the two tools are and where they are useful. They are both useful for different things

Kubernetes

  • Kubernetes (K8S) is a distributed container orchestration tool initially created by Google
  • It was open-sourced in 2014 and handed over to the Cloud Native Computing Foundation (CNCF) the following year
  • The CNCF is an industry body with hundreds of members drawn from the majority of large cloud, software and hardware companies
  • At the time of writing (late 2021) there are nearly a thousand K8S related projects split into around twenty classes with a total of over $21 billion dollars in funding
  • Kubernetes (2021) is the most popular distributed system orchestrator in the world with 88% adoption
  • Because of its near ubiquity, K8S has become the most popular contemporary platform for innovative system development in 2021
  • Kubernetes is a competitor (more or less) to Docker swarm but does more stuff than docker swarm i.e a popular choice.

Docker 提供的 Docker Compose 案例:awesome-compose

Multi-platform images

Multi-platform images 是指一个image有适配多个平台的镜像,拉取镜像会自动选择合适架构的镜像。build时可以添加--platform来指定build镜像支持的架构。

OpenShift

入门资料:

OpenShift 与 Kubernetes

根据Red Hat OpenShift vs. Kubernetes

Red Hat® OpenShift® is an enterprise grade open source application platform for accelerating the development and delivery of cloud-native applications in a consistent way across the hybrid and multi cloud, all the way to the edge. It is powered by Kubernetes as the container orchestration engine, and many more features from the CNCF open source ecosystem, all tested, packaged, and supported together as a comprehensive application platform by Red Hat.

简而言之,OpenShift 是以 Kubernetes 为内核的企业级发行版。我目前所在的公司使用 OpenShift 作为基础设施,因此有必要单独整理。

OpenShift 与 Kubernetes:作用相同但实现不同的概念

阅读 OpenShift 的博客可发现,诸如 Route 与 Ingress、Template 与 Helm 等概念,常源于 OpenShift 的先行实现,随后影响了上游 Kubernetes 的设计与实现。

Where OpenShift differs, it is often where no equivalent feature existed in the upstream core version at the time Red Hat needed to deliver it. OpenShift Routes, for example, predate the related Ingress resource that has since emerged in upstream Kubernetes. In fact, Routes and the OpenShift experience supporting them in production environments helped influence the later Ingress design, and that’s exactly what participation in a community like Kubernetes is all about. Another concept unique to Openshift is the Template object. An OpenShift Template bundles some number of objects that are related from a functional point of view. Kubernetes is a vibrant laboratory, and so there are similar efforts in the community to group an “application”’s objects and resources in a way convenient for deployment, management, and organization. One of the other popular systems in this space is Microsoft/Deis’s Helm. In this post, I’ll show how an OpenShift Template can be converted into a Helm Chart with just a few changes.

对此,社区有不少讨论与实践比较。

Kubernetes Helm charts or OpenShift templates?

还有将 Templates 转化为 Helm Charts 的工具:

OpenShift Template

您可以定义新模板,以便轻松重新创建应用程序的所有对象。该模板将定义由其创建的对象以及一些元数据,以指导创建这些对象。

通过 OpenShift 提供的模板,可在一个文件中定义并模板化多种资源。

OpenShift 的编写模板与 Kubernetes 的组织资源配置类似但功能更强大。另一个常用方案是 Helm 与 Helm Charts

实践篇

Docker 和 Container

作者使用的是Go项目作为Demo,那我将以我熟悉的Angular项目作为案例(不熟悉Angular的读者直接参考我们的旅程从一段代码开始)。

首先ng new k8s_angular_demo, VS Code打开,在根目录新建Dockerfile,内容如下:

v1(本地开发:ng serve)

# Multi-platform images
FROM --platform=$BUILDPLATFORM node:20-alpine as builder

RUN mkdir /project
WORKDIR /project

RUN npm install -g @angular/cli@17

# 构建缓存
COPY package.json package-lock.json ./
RUN npm ci

COPY . .
CMD ["ng", "serve", "--host", "0.0.0.0"]

然后

docker build . -t terry/k8s_angular_demo:v1

docker run --rm --name terry_k8s_angular_demo -p 4200:4200 terry/k8s_angular_demo:v1

然后访问localhost:4200就可以访问到了。

v2(生产:Nginx 静态托管)

改为使用 Nginx 作为静态资源服务器。

# Build Stage
# Multi-platform images
FROM --platform=$BUILDPLATFORM node:20-alpine as builder

RUN mkdir /project
WORKDIR /project

# 构建缓存
COPY package.json package-lock.json ./
RUN npm ci

COPY . .
RUN npm run build

# Stage 2
FROM nginx:stable-alpine

COPY --from=builder /project/dist/k8s_angular_demo/browser /usr/share/nginx/html

EXPOSE 80
docker build . -t terry/k8s_angular_demo:v2

docker run --rm --name terry_k8s_angular_demo -p 4200:80 terry/k8s_angular_demo:v2

然后再次访问localhost:4200,可以得到一样的界面。

可以看到两次构建镜像的大小差距:

➜  ~ docker images | grep terry/k8s_angular_demo
terry/k8s_angular_demo                          v2        9a7bbdcd00a2   26 minutes ago   89.3MB
terry/k8s_angular_demo                          v1        6fde77d66add   48 minutes ago   770MB

pod

# deploy/nginx_pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
spec:
  containers:
    - name: nginx-container
      image: terry/k8s_angular_demo:v2

找不到镜像问题

遇到一个Failed to pull image pull access denied , repository does not exist or may require 'docker login'的问题,最后发现是Minikube有独立的daemon, 为了让Minikube能够找到镜像,需要在执行eval $(minikube -p minikube docker-env)的shell中执行构建命令,然后执行kubectl apply -f deploy/nginx_pod.yaml

eval $(minikube -p minikube docker-env)
docker build . -t terry/k8s_angular_demo:v2
kubectl apply -f deploy/nginx_pod.yaml

Pod 和 Container 区别

container (容器) 的本质是进程,而 pod 是管理这一组进程的资源。pod是k8s资源中的最小单位。

Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: angular-k8s-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
        # 和下面的 template 中的标签一致,使用选中的pod
      app: angular-k8s
  template:
    metadata:
      labels:
        app: angular-k8s
    spec:
      containers:
        - image: terry/k8s_angular_demo:v2
          name: angular-container-name

然后kubectl apply -f deploy/nginx_deployment.yaml部署,可以看到结果:

你还可以查看 minikube 提供的 Web Console 进行可视化管理:

![alt text](/static/images/post/CleanShot 2024-06-22 at 10.02.51.png)

探针

探针分为存活探针,就绪探针和启动探针。

详细教程查看博客的对应章节:

init container 和 sidecar container

讲到探针,就不得不涉及一下这部分了。docker-compose里有一个叫做depends_on的功能,可以设置服务启动的顺序,非常好用。但是Kubernetes里不提供这一功能,我查看讨论,大意是这不属于Kubernetes的scope,而是使用者的scope。而我练习自己部署ELK的服务时,最好先等待有状态的Elasticdearch启动完成后再启动Kibana。这一功能可以通过init container实现。

对应官网文档地址:

Service

Service 为 pod 提供了一个稳定的 Endpoint(可以理解为Service维护了pod列表,知道如何找到对应的pod) 和 负载均衡 功能。

DNS

DNS 服务是Service中非常重要的一个功能,可以帮助工程师写出更灵活的部署配置。

例如,如果你在 Kubernetes 命名空间 my-ns 中有一个名为 my-service 的 Service, 则控制平面和 DNS 服务共同为 my-service.my-ns 生成 DNS 记录。 名字空间 my-ns 中的 Pod 应该能够通过按名检索 my-service 来找到服务 (my-service.my-ns 也可以)。

其他名字空间中的 Pod 必须将名称限定为 my-service.my-ns。 这些名称将解析为分配给 Service 的集群 IP。

apiVersion: v1
kind: Service
metadata:
  name: service-angular-k8s-clusterip
spec:
  type: ClusterIP
  selector:
    # 这里声明哪些是被 selector 选中的 Pod
    app: angular-k8s
  ports:
  - port: 80
    targetPort: 80

这样的话其它 Pod 就能通过访问 http://service-angular-k8s-clusterip:80 访问到这个服务。由于 type: ClusterIP,仅集群内部可访问。

如果想外部也可以访问到,就需要把type设置为NodePort。比如:

apiVersion: v1
kind: Service
metadata:
  name: service-angular-k8s-nodeport
spec:
  type: NodePort
  selector:
    app: angular-k8s
  ports:
  - port: 80
    targetPort: 80
    nodePort: 30000

这样就可以通过k8s cluster node IP + 30000端口访问到该服务啦。不过使用的情况不是很多,一般情况下我会再写一个Ingress或者Route对象来暴露给外界。

调试 DNS 问题

如果发现找不到其它Service或者类似问题,就需要调试 DNS 问题。官方文档给出了具体的方法。

题外话,DNS好像也是一个pod,有点一切皆pod的意思了。

Ingress

Ingress 公开从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。Ingress 可为 Service 提供外部可访问的 URL、负载均衡流量、 SSL/TLS,以及基于名称的虚拟托管。

Ingress 可以“简单理解”为服务网关(Gateway),它是外部流量进入集群的入口,通过配置路由规则将流量转发到后端服务。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: angular-k8s-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
        # 和下面的 template 中的标签一致,使用选中的pod
      app: angular-k8s
  template:
    metadata:
      labels:
        app: angular-k8s
    spec:
      containers:
        - image: terry/k8s_angular_demo:v2
          name: angular-container-name

---

apiVersion: v1
kind: Service
metadata:
  name: service-angular-k8s-clusterip
spec:
  type: ClusterIP
  selector:
    # 这里声明哪些是被 selector 选中的 Pod
    app: angular-k8s
  ports:
  - port: 80
    targetPort: 80

---

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ingress
  annotations:
    # We are defining this annotation to prevent nginx
    # from redirecting requests to `https` for now
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
    - http:
        paths:
          - path: /angular
            pathType: Prefix
            backend:
              service:
                name: service-angular-k8s-clusterip
                port:
                  number: 80

其它

除此之外,还有NamespaceConfigmapSecretJobCronJob等等其它的资源,这部分比较符合直觉,我也想对比较熟悉,将在接下里的实践篇-从0开始搭建EAK集群中进行实践和记录。

Job

公司内的Jenkins打包,除了build image还有deploy image。deploy image 可以用来部署 build image,我觉得就是通过Job 以及 Source-To-Image (S2I) 实现的部署完就停止运行,oc apply等等命令都在deployer image里,不用每次部署都需要我手动输入命令。

Helm

经过前面的教程,想必你已经对 kubernetes 的使用有了一定的理解。但是不知道你是否想过这样一个问题,就是我们前面教程中提到的所有资源,包括用 pod, deployment, service, ingress, configmap,secret 所有资源来部署一套完整的 hellok8s 服务的话,难道需要一个一个的 kubectl apply -f 来创建吗?如果换一个 namespace,或者说换一套 kubernetes 集群部署的话,又要重复性的操作创建的过程吗?

我们平常使用操作系统时,需要安装一个应用的话,可以直接使用 apt 或者 brew 来直接安装,而不需要关心这个应用需要哪些依赖,哪些配置。在使用 kubernetes 安装应用服务 hellok8s 时,我们自然也希望能够一个命令就安装完成,而提供这个能力的,就是 CNCF 的毕业项目 Helm。

教程作者的这段话基本可以概括Helm的优点,轻度使用下来和Openshift的template类似。