• RedHat OpenShift QuickStart 2.1 容器介绍


    原文:https://learn.openshift.com/subsystems/container-internals-lab-2-0-part-1

    1. 容器介绍

    四个容器的基本元素:

    • 容器镜像
    • 容器注册仓库
    • 容器主机
    • 容器编排
    • Container Images: made up of underlying operating system components like libraries and programming languages
    • Container Registries: Fancy file servers that help users share container images
    • Contianer Hosts: Includes Podman (or Docker) runtime, Systemd, runc, and Libcontainer
    • Container Orchestration: Includes Kubernetes/OpenShift

    需要了解的还包括一些后面需要用到的高级特性,包括:

    • 容器标准:OCI,ORI,CNI等
    • 容器工具生态系统:Podman,buildah,Skopeo, cloud registries,等。
    • 产品镜像构建:技术专家(性能、网络、安全、数据库等)之间的共享和协作
    • 中间架构:生产环境
    • 高级架构:弹性构建
    • 容器历史:目前所处的背景

    现在介绍四种基本类型:

     

    2. 容器镜像

    容器镜像实际上是包含JSON文件的tar文件。我们可以把这些合在一起称之为镜像包,此包的磁盘格式由OCI Image Specification定义。所有主要的容器引擎包括:Podman,Docker,RKT, CRI-O和containerd构建和使用这些包。

    下面来更深入的讨论三个概念:

    (1)可移植性:由于OCI标准管理着镜像规范,所以可以用Podman来创建容器镜像,并推送到容器注册仓库,与外界共享。可以使用其他容器引擎来操作:Docker、RKT、crio、containerd和其他Podman实例。这种镜像标准化可以让我们构建注册服务器等基础设施,用来存放任何容器镜像。可以是RHEL 6/7/8、Fedora、或者是windows容器镜像。无论镜像中的操作系统或者二进制文件是什么,镜像格式都是相同的。请注意,Podman可以下载一个Fedora镜像,解压它,并将其存储在本地/var/lib/containers映像存储中,即使它不是一个Fedora容器主机:

    podman pull fedora

    (2)兼容性:这解决了容器镜像中的内容,无论怎么弄,容器镜像中的ARM二进制文件都不会在POWER容器主机上运行。容器不提供兼容性保证;只有虚拟化可以做到这一点。这个兼容性问题扩展到处理器体系结构,以及操作系统的版本。尝试在RHEL 4容器主机上运行RHEL 8容器镜像-这是行不通的。但是,只要操作系统相当相似,容器镜像中的二进制文件通常就会运行。注意,使用Fedora镜像执行基本命令可以工作,即使这不是一个Fedora容器主机:

    podman run -t fedora cat /etc/redhat-release

    (3)可支持性。这是供应商可以支持的。这涉及到在测试、安全性、性能和体系结构方面的投资,以及确保镜像和二进制文件以在给定的一组容器主机上正确运行的方式构建。例如,Red Hat在RHEL 7和RHEL 8容器主机上都支持RHEL 6、UBI 7和UBI 8容器镜像(CoreOS是由RHEL位构建的)。Red Hat不能保证这个星球上的每个容器镜像排列和主机组合都能正常工作。它将以非线性的速度扩展测试和分析矩阵资源。为了演示,在这个容器主机上运行一个Red Hat通用基础镜像(UBI)容器。如果这是一个RHEL容器主机,这将是完全支持的(抱歉,只有CentOS主机可用于这个实验室环境:-):

    podman run -t registry.access.redhat.com/ubi7/ubi cat /etc/redhat-release

    通过对可移植性、兼容性、可支持性分析,我们可以推断RHEL 7的镜像可以完美运行在RHEL7主机上。两者中的代码都是一起设计、编译和测试的。

    然而,也有限制。Red Hat不能保证RHEL 5、Fedora和Alpine镜像能够在RHEL 7主机上正常工作。容器镜像标准保证容器引擎能够摄取镜像,将它们拉下并在本地缓存它们。但是,没有人能够保证容器镜像中的二进制文件能够正确工作。没有人能保证不会因为版本组合而出现奇怪的cve(是的,这是“一件事”),当然,也没有人能保证在没有编译它的内核上运行的二进制文件的性能。也就是说,很多时候,这些二进制代码看起来只是工作。

    这导致我们将可支持性作为一个与可移植性和兼容性分离的概念。这是在某种程度上保证某些图像将在某些主机上工作的能力。人们不应该期望RHEL 9、10或11的容器映像在RHEL 8主机上运行。

    3. 容器注册

    注册中心实际上只是帮助用户分享文件的服务器,容器的神奇之处在于,它能够使用一种新的打包格式查找、运行、构建、共享和协作,并将应用程序及其所有依赖项分组在一起。

    容器映像使软件构建人员能够轻松地打包软件,并提供有关如何运行软件的信息。使用元数据,软件构建者可以告诉用户如何运行他们的软件,并且可以使用现有软件灵活的构建新的软件。

    注册服务器只是让与其他用户共享这项工作变得更容易。构建器可以将图像推送到注册表,允许用户甚至像CI/CD系统这样的自动化系统将其拉下并使用数千次或数百万次。一些注册中心,如Red Hat Container Catalog,提供了经过精心策划、良好测试和企业级的映像。其它 像Quay.io是基于云的注册中心,为个人用户提供公共和私人空间,让他们推送自己的图片并与他人分享。对于希望一起交付解决方案的合作伙伴(例如Red Hat and CrunchyDB),,而基于云的注册中心则适合终端用户协作工作。

    一个例子展示quay.io的强大功能。为这个实验先拉下一个镜像:

    podman pull quay.io/fatherlinux/linux-container-internals-2-0-introduction

    现在运行下面命令来模拟启动一个数据库:

    podman run -d -p 3306:3306 quay.io/fatherlinux/linux-container-internals-2-0-introduction

    使用CURL来访问:

    curl localhost:3306

    这个看上去很简单,我们不需要知道它是如何去运行的,所有的复杂的逻辑都嵌入到了镜像中。下面是构建文件,你可以检查它的启动逻辑。你可能不理解这里的bash代码,但是没关系,这就是容器的好处。

    #

    # Version 1

    # Pull from Red Hat Universal Base Image FROM registry.access.redhat.com/ubi7/ubi-minimal

    MAINTAINER Scott McCarty smccarty@redhat.com

    # Update the image

    RUN microdnf -y install nmap-ncat && echo "Hi! I'm a database. Get in ma bellie!!!" > /srv/hello.txt

    # Output ENTRYPOINT bash -c 'while true; do /usr/bin/nc -l -p 3306 < /srv/hello.txt; done'

    通过本节,你可以使用构建文件将运行时逻辑嵌入到容器镜像中,这样不仅可以传达要做什么,还有怎么去运行。你可以和其他人分享镜像文件或者将构建文件放到GitHub上,方便其他人构建。

    4. 容器主机

    为了理解容器主机,下面来分析协同工作来创建容器的层。包含:

    • 容器引擎
    • 容器运行时
    • Linux内核

    容器引擎:

    容器引擎可以粗略的被描述为容器提供API或者CLI的工具。这是从Docker开始的,但也包括Podman,Buildah,rkt和CRI-O。容器引擎接受用户输入,获取容器镜像,创建一些如果运行容器的元数据,然后将这些信息传送给容器运行时。

    容器运行时:

    容器运行时是一个小工具,通常用来处理两种事情:一是通常被称为跟文件系统的目录(或者rootfs),二是一些被称为config.json的元数据(或者spec文件)。对于上门提到的容器引擎,runc是默认的运行时,但是有很多创新的runtime包括:katacontainers、gvisor、crun和railcar。

    Linux内核:

    内核负责容器创建的最后一步,以及在运行时的生命周期的管理。容器运行时通过特定的叫做clone()的方法来告诉内核创建一个新的容器。运行时同样还会告诉内核去配置cgroups、SELinux和SECCOMP等。所调用的内核技术组合由容器运行时定义,但是最近在内核中进行了标准化。

    容器只是作为容器运行时的子进程启动的常规Linux进程,而不是由在shell中运行命令的用户启动的。所有的Linux进程都是并行的,不管它们是守护进程、批处理作业还是用户命令——容器引擎、容器运行时和容器(容器运行时的子进程)都是一样的。所有这些进程都向Linux内核请求受保护的资源,如内存、RAM、TCP套接字等。

    使用podman执行下面的一些命令,观察进程ID和命令空间ID,容器就是常规的进程。

    podman ps -ls
    
    podman top -l huser user hpid pid %C etime tty time args
    
    ps -ef | grep 3306

    5. 容器编排

    在熟悉了单一主机上使用容器后,容器编排是下一步要学习的。使用单一主机,可以与传统应用程序非常相似的方式管理容器化应用程序,同时提高了效率。对于容器编排,有一个重要转变:开发者和管理员都需要以不同的方式去思考,即通过一个API对应用程序进行所有的更改。有人质疑容器编排的“复杂性”,但是它的好处远超于学习编排的工作量。目前Kubernetes是目前容器编排方面是明显的赢家,你可以从中获得:

    • 应用定义:YAML和JSON可以在开发人员和开发人员和操作人员中传递来运行功能齐全的多容器应用程序。
    • 更容易的应用实例:在不同命名空间运行多个版本的应用程序。
    • 多节点调度:使用kubernetes内置的控制工具可以管理10-10000台主机,没有额外的复杂性。
    • 强大的API——开发人员、集群管理员和自动化人员都可以使用openshift4定义应用程序状态等,甚至集群节点状态
    • 操作自动化——Kubernetes操作框架可以被认为是一个机器人系统管理员,与应用程序一起部署,管理应用程序的日常和复杂任务(备份、恢复等)
    • 更高层次的框架——一旦你采用了Kubernetes编制,你就可以获得一个创新的工具生态系统,如Istio、Knative和前面提到的Operator框架

    为了进行演示,需要的只是bash、curl和netcat,它们允许我们通过TCP端口传输文本。这次我们将演示通过kubernetes YAML如何去运行逻辑:

    User -> Web App (port 80) -> Database (port 3306)

    通过下面命令可以浏览一下YAML:

    curl https://raw.githubusercontent.com/fatherlinux/two-pizza-team/master/two-pizza-team-ubi.yaml
    # Written By: Scott McCarty
    # Date: 02/15/2018
    # Description: Simple two replication controllers which use standard RHEL images to deliver front end/back end
    # application. The connection looks like this client -> pepperoni-pizza:80 -> cheese-pizza:3306 which is similar to a
    # standard web application using apache and mysql. I chose to use only netcat so that very simple images 
    # could be used. Also, you talk to pepperoni, but cheese gets the work don :-)
     
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: cheese-pizza 
        app: two-pizza-team
      name: cheese-pizza
    spec:
      type: ClusterIP
      ports:
        - port: 3306
      selector:
        app: cheese-pizza
    ---
    apiVersion: v1
    kind: Service
    metadata: 
      labels: 
        app: pepperoni-pizza
        app: two-pizza-team
      name: pepperoni-pizza
    spec: 
      type: LoadBalancer
      ports:
        - port: 80
          targetPort: pepperoni-pizza
      selector: 
        app: pepperoni-pizza
    ---
    apiVersion: v1
    kind: ReplicationController
    metadata:
      name: cheese-pizza
      labels:
        app: cheese-pizza
        app: two-pizza-team
    spec:
      replicas: 1
      template:
        metadata:
          labels:
            app: cheese-pizza
        spec:
          containers:
          - name: cheese-pizza
            image: quay.io/fatherlinux/linux-container-internals-2-0-introduction
            # Simple netcat command to pull redhat-release from disk and serve it over port 3306
            command: ['/bin/bash', '-c', 'while true; do /usr/bin/nc -l -p 3306 < /srv/hello.txt; done']
            ports:
              - containerPort: 3306
                name: cheese-pizza
    ---
    apiVersion: v1
    kind: ReplicationController
    metadata:
      name: pepperoni-pizza
      labels:
        app: pepperoni-pizza
        app: two-pizza-team
    spec:
      replicas: 1
      template:
        metadata:
          labels:
            app: pepperoni-pizza
        spec:
          containers:
          - name: pepperoni-pizza
            image: quay.io/fatherlinux/linux-container-internals-2-0-introduction
            # Simple netcat command to pull redhat-release from disk and serve it over port 3306
            # Runs on non-privileged port 8080 to improve security. The Kubernetes service will
            # NAT it to port 80 for regular connection from curl
            command: ['/bin/bash', '-c', 'while true; do /usr/bin/nc -l -p 8080 -c "curl $DATABASE_HOST:3306"; done']
            env:
              - name: DATABASE_HOST
                value: cheese-pizza 
            ports:
              - containerPort: 8080
                name: pepperoni-pizza
    ---
    apiVersion: v1
    kind: Route
    metadata:
      labels:
        app: pepperoni-pizza
        app: two-pizza-team
      name: pepperoni-pizza
    spec:
      host: pepperoni-pizza.example.com
      to:
        kind: Service
        name: pepperoni-pizza
        weight: 10    
    status: {}
    ---
    YAML

    在“数据库”中,我们打开一个文件并使用netcat将其发送到3306端口。在“web应用程序”中,我们从端口3306获取数据,然后像普通应用程序那样通过端口80将数据发送出去。我们的想法是通过一个简单的例子来展示它的强大,而不需要学习其他技术。我们能够启动这个应用程序实例用单一的oc命令:

    oc create -f https://raw.githubusercontent.com/fatherlinux/two-pizza-team/master/two-pizza-team-ubi.yaml

    使用下面命令来监控pod运行情况,直至都为running:

    for i in {1..5}; do oc get pods;sleep 3; done

    使用下面命令在数据库中获取数据,而不是web应用:

    curl $(kubectl get svc pepperoni-pizza -o yaml | grep ip | awk '{print $3}')

    注意:括号中的命令只是获取IP

    然后换种方式在数据库中返回,通过3306端口的方式:

    curl $(kubectl get svc cheese-pizza -o yaml | grep clusterIP | awk '{print $2}'):3306

    6. 结尾

    通过下图可以理解一下为什么需要容器,或者为什么需要容器编排;

  • 相关阅读:
    Flask 静态文件缓存问题
    Flask中current_app和g对象
    Flask-Login详解
    Flask-分开Models解决循环引用
    python包中__init__.py文件的作用
    Flask中路由参数
    Flask之加载静态资源
    Flask request 属性详解
    Django REST framework+Vue 打造生鲜超市(一)
    8.Vue基础
  • 原文地址:https://www.cnblogs.com/tiandz/p/12397833.html
Copyright © 2020-2023  润新知