k8s学习整理文档(上)

949 阅读29分钟

实验环境

服务器

最低配置要求:

  • 2 核虚拟 CPU
  • 4 GB 内存
  • 20 GB 储存空间

==X 4 台==

(三台集群,一台镜像仓库服务器)

网络环境

由于搭建网络需要,同时要让各个主机互通,因此这里需要对云服务器设置专门的网络(同时也需要开放服务器对应的公网 IP)

  1. 在云服务器厂商中,创建 VPC 网络,网段为: 172.31.0.0/16
  2. 创建私有网络(交换机):172.31.0.0/24
  3. 所有主机均设置为在该私有网络(交换机)当中

安全组设置

  1. 开启组内互信

    同一内网开放所有端口

  2. 对外开放如下端口(每台服务器均开放)

    协议端口范围服务名称描述
    TCP22sshSSH远程访问服务器
    TCP53domainDNS服务器通信
    UDP53domainDNS服务器通信
    TCP80http暴露Web应用程序或服务
    TCP111rpcbindNFS文件共享服务通信
    TCP179bgpBGP路由协议通信
    TCP443https暴露Web应用程序或服务
    TCP2379-2380etcdEtcd集群通信
    TCP5000docker-registryDocker镜像本地仓库通信(离线环境)
    TCP5080apt-cacher-ng本地APT源通信(离线环境)
    TCP6443kube-apiKubernetes API服务器通信
    TCP9099-9100calico-etcdCalico网络插件通信
    TCP10250-10258kubeletKubernetes主节点组件通信
    TCP30000-32767nodeportNodePort类型的Service通信

基础整理

kubernetes集群概述

k8s以容器技术为基础,实现对容器的高效管理,安全容错,弹性伸缩等功能。docker容器技术实现,将独立应用抽象为独立虚拟主机。随着主机的增多,对主机的编排管理是一项重要工作。

组件如下:

在这里插入图片描述

1、控制平面组件(Control Plane Components)

控制平面的组件对集群做出全局决策(比如调度),以及检测和响应集群事件

控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件, 并且不会在此计算机上运行用户容器。 请参阅使用 kubeadm 构建高可用性集群 中关于多 VM 控制平面设置的示例。

kube-apiserver

API 服务器是 Kubernetes 控制面的组件, 该组件公开了 Kubernetes API。 API 服务器是 Kubernetes 控制面的前端。

Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver 设计上考虑了水平伸缩,也就是说,它可通过部署多个实例进行伸缩。 你可以运行 kube-apiserver 的多个实例,并在这些实例之间平衡流量。

etcd

**etcd 是兼具一致性和高可用性的键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库。**​etcd 文档

kube-scheduler

控制平面组件,负责监视新创建的、未指定运行节点(node)Pods,选择节点让 Pod 在上面运行。

调度决策考虑的因素包括单个 Pod 和 Pod 集合的资源需求、硬件/软件/策略约束、亲和性和反亲和性规范、数据位置、工作负载间的干扰和最后时限。

kube-controller-manager

在主节点上运行 控制器 的组件。

从逻辑上讲,每个控制器都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在一个进程中运行。

这些控制器包括:

  • 节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应
  • 任务控制器(Job controller): 监测代表一次性任务的 Job 对象,然后创建 Pods 来运行这些任务直至完成
  • 端点控制器(Endpoints Controller): 填充端点(Endpoints)对象(即加入 Service 与 Pod)
  • 服务帐户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认帐户和 API 访问令牌

cloud-controller-manager

云控制器管理器是指嵌入特定云的控制逻辑的 控制平面组件。 云控制器管理器允许您链接集群到云提供商的应用编程接口中, 并把和该云平台交互的组件与只和您的集群交互的组件分离开。

<span class="ne-text">cloud-controller-manager</span>​​ 仅运行特定于云平台的控制回路。 如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的环境中不需要云控制器管理器。

<span class="ne-text">kube-controller-manager</span>​​ 类似,<span class="ne-text">cloud-controller-manager</span>​​ 将若干逻辑上独立的 控制回路组合到同一个可执行文件中,供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。

下面的控制器都包含对云平台驱动的依赖:

  • 节点控制器(Node Controller): 用于在节点终止响应后检查云提供商以确定节点是否已被删除
  • 路由控制器(Route Controller): 用于在底层云基础架构中设置路由
  • 服务控制器(Service Controller): 用于创建、更新和删除云提供商负载均衡器

2、Node 组件

节点组件在每个节点上运行,维护运行的 Pod 并提供 Kubernetes 运行环境。

kubelet

一个在集群中每个节点(node)上运行的代理。 它保证容器(containers)都 运行在 Pod 中。

kubelet 接收一组通过各类机制提供给它的 PodSpecs,确保这些 PodSpecs 中描述的容器处于运行状态且健康。 kubelet 不会管理不是由 Kubernetes 创建的容器。

kube-proxy

kube-proxy 是集群中每个节点上运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。

kube-proxy 维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话与 Pod 进行网络通信。

如果操作系统提供了数据包过滤层并可用的话,kube-proxy 会通过它来实现网络规则。否则, kube-proxy 仅转发流量本身。

将k8s集群比作公司集团,各个组件之间的关系如图所示:

在这里插入图片描述

控制(运行)架构

前.1.2内容主要介绍基本的对于k8s各个组件当中的认识。但是作为使用者作为汽车的驾驶人员,并不需要关心汽车的传动结构是如何设计的等等基本细节。因此作为使用人员更加关注的是,如何驾驭k8s集群。

组件分析

在开始绘制大概的架构图之前,从正常的开发经验出发来猜测如果我们需要实现类似于对docker集群的管理。我们需要注意哪些问题,并且当我们部署docker是,不同的docker容器相当于一个基础服务,不同的服务之间充当不同角色。因此我们从可用服务进行底层划分,由于按照最小(业务层面出发)可拆分服务进行划分,例如一个基础api服务为一个划分,一个基础api服务又包含多个组件,每个组件都可以由docker进行部署。

Pod

因此,我们抽象出k8s当中最基础的单元Pod

Pod:

  • Pod是最小调度单元
  • Pod里面会包含一个或多个容器(Container)
  • Pod内的容器共享存储及网络,可通过localhost通信

于是在k8s当中,最小的执行单元将不再是容器,这非常好理解,一块cpu当中具有多个核心,但是在售卖时,我们往往按照一块一块cpu进行售卖,而不是按照一个核心一个核心进行售卖。通过不同的cpu型号来划分不同的核心配置。

Deployment

此时我们的问题从对docker容器的管理转移到了对Pod的管理。

因此Deployment 是在 Pod 这个抽象上更为上层的一个抽象,可以定义一组 Pod 的副本数目、以及这个 Pod 的版本。一般大家用 Deployment 这个抽象来做应用的真正的管理,而 Pod 是组成 Deployment 最小的单元

  • 定义一组Pod的副本数量,版本等
  • 通过控制器维护Pod的数目
  • 自动恢复失败的Pod
  • 通过控制器以指定的策略控制版本
Service

此时我们解决了关于Pod的管理问题,但是Pod做为一个最小可调度服务,因此不同的Pod可能存在调用关系。以Java微服务举例,需要通过网络,通过注册中心完成api间的服务调用。Service解决的就是Pod的服务调用问题。

并且Pod是不稳定的,IP是会变化的,所以也需要一层抽象来屏蔽这种变化

  • 提供访问一个或者多个Pod实例稳定的访问地址
  • 支持多种访问方式ClusterIP(对集群内部访问)NodePort(对集群外部访问)LoadBalancer(集群外部负载均衡)

在这里插入图片描述

Ingress

一个Service可用解决多个Pod之间的通讯问题,但是对于Service来说,以及服务的复杂度,可能需要多个Service来进行处理,因此还需要一个抽象层对Service进行管理,那么这个层面就是Ingress。同时ingress提供对外暴露接口,其本质就是一个nginx服务器。

其中Ingress与Service的关系,类比微服务架构如下图所示:

在这里插入图片描述

存储

由于Pod本质上还是基于Docker进行实现的,因此对于持久化操作来说依然可用采用原先基于Docker的存储方式。但是对于k8s集群来说,对于一个统一管理平台来说同样可以由一个抽象层进行统一管理,此时我们集成了三要素:网络抽象,运行抽象,存储抽象。同时这里也向我们传达了一个基本的设计思想,代理模式,通过对底层的抽象,向上层提供更加符合业务操作的服务。在底层是现实Pod依然通过docker的卷挂载实现,但是对k8s操作来说我们可以直接通过抽象存储层进行操作。

那么这个抽象,在这里是:PV&PVC。

PV:持久卷(Persistent Volume),将应用需要持久化的数据保存到指定位置

PVC:持久卷申明(Persistent Volume Claim),申明需要使用的持久卷规格

同时在声明之后,持久层的内容依然需要存储到服务器物理硬盘当中,那么由于良好的抽象层设计,同时为了保证存储的安全,这里依然可以在物理存储层面采用分布式存储器来确保数据安全。

因此,在存储层面,在配合k8s集群时,可以由nfs文件系统来完成物理层面的文件存储。

这里需要注意:PVC其实在Pod和PV之前又增加了一层抽象,这样做的目的在于将Pod的存储行为于具体的存储设备解耦,试想一下,假设哪天NFS网络存储的IP地址变化了,如果没有PVC,就需要每个Pod都改一下IP的声明,那得多累,有PVC来屏蔽这些细节之后只用改PV即可!他们是动态分配的

此外,在存储时,有个特殊的存储需求,那就是对配置文件的存储需求,对于配置文件来说,我们可能希望,当配置文件修改时,配置文件将可以及时生效。因此在k8s当中有个configMap的概念。

NameSpace

之后,我们这里你会发现,这样一个关系。那就是,我们实际上在“干活”的组件是,Pod。为了管理Pod我们有了deployment,同时Pod的运行需要网络,于是又Service,对网络进一层管理于是有了Ingress。之后是对存储的需求,于是又提出了Pv与Pvc,其中Pv面向底层的物理存储进行定义操作,Pvc面向Pod。那么在这里,这些都是我们一个正常的服务必要的组件,那么假设我有另一个服务,另一个独立的系统需要管理,那么这个时候就显然需要对我们上述提到的内容进行隔离管理。那么这个就是我们的NameSpace,当没有指定NameSpace是,所有声明的资源都在默认NameSpace当中。当然这里不同的是:持久化存储卷和持久化存储卷声明通常属于集群范围的资源,而不受命名空间的限制。因此,在上述提供的配置中,并没有指定命名空间是正常的。持久化存储通常被设计为跨命名空间共享,以便多个 Pod 可以访问同一块持久化存储。换句话说,在这里存储被设计为整个集群的资源。

梳理

因此对于k8s我们可以做一个简单的梳理,在屏蔽底层细节的同时,如果我们需要将服务进行大规模推送至k8s统一管理平台,我们需要注意哪些细节。

服务层面

在服务层面,我们需要关注到Pod,我们需要定义并且声明一个Pod,同时我们需要关注到,我们要部署多少个Pod,这些Pod将有什么特殊特性。例如,你是否希望此时运行的Pod(因为我们知道Pod实际上还是个容器(同时由于Pod本身做了一层抽象,因此对于容器的实现不一定需要采用docker进行实现,实际上很早以前,k8s就有想要拜托docker的想法))因此,此时我们需要关注到Deployment。当然在一开始时我们还需要主要到NameSpace。

一个标准的服务示例可以是这样的:

(注意在k8s当中,可以直接使用指令创建资源,也可以直接使用yml文件,通过kubectl apply -f deployment.yaml​​即可,这一点与docker的docker-compose类似)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: 示例deployment
  namespace: 示例空间
spec:
  replicas: 2
  selector:
    matchLabels:
      app: 示例应用
  template:
    metadata:
      labels:
        app: 示例应用
    spec:
      containers:
      - name: 示例容器
        image: 示例镜像
        volumeMounts:
        - name: 示例pvc
          mountPath: /data
      volumes:
      - name: 示例pvc
        persistentVolumeClaim:
          claimName: 示例pvc
      restartPolicy: Always
      hostname: 示例pod
---
apiVersion: v1
kind: Service
metadata:
  name: 示例服务
  namespace: 示例空间
spec:
  selector:
    app: 示例应用
  ports:
    - name: http
      port: 80
      targetPort: 8080
  type: ClusterIP

网络

之后是我们需要关注到网络层面。也就是我们的Pod将使用哪个网络,这里值得一提的是,在内部网络当中,是支持通过生成的域名进行访问的。也就是说,一个组内网络是互通的,并且在Service当中具备负载均衡。与传统的Java微服务架构当中的Nacos来说,Service少了服务之间api级别的服务发现。当然为了弥补这个缺陷,k8s当中拥有:Istio 这一类服务发现技术。这意味着我们可以不在需要nacos的注册中心功能,同时基于configMap,我们也可以取代注册中心的功能。当然对于传统微服务来说我们依然可以将Nacos,ZooKeeper,并将其部署至集群,但是的确会增加服务成本,当然对于老项目又会增加重构成本,如何取舍是个需要考虑的问题。但毫无疑问,云原生体系下的技术可以将开发技术聚焦到服务数据层面,而不是语言实现层面,尽管Nacos,Zookeeper也面向分布式,但是对不同语言的项目存在一定的适配问题,基于nacos-python-sdk存在心跳检测的异常,导致nacos认为服务异常,需要手动重新实现heartbeat。

存储

存储主要聚焦到物理层的Pvc定义和Pv定义。同时需要注意到configMap如何定义编写即可。

至此,关于K8s的大致组件,在使用过程当中,作为使用者需要关注的部分为上述部分。

Docker基本使用

由于当中Pod当中需要使用到docker,因此,我们需要对Docker的基本使用重新做一个梳理。

概述

Docker 是一种轻量级的容器化技术,它可以将应用程序及其依赖项打包在一个可移植的容器中,并在任何地方运行。Docker 的实现原理主要涉及以下几个方面:

  1. 命名空间和控制组:Docker 利用 Linux 操作系统提供的命名空间和控制组技术来实现容器隔离。命名空间允许容器拥有自己的进程树、网络接口、用户 ID 等系统资源,而控制组则可以限制容器的 CPU、内存、磁盘等使用量。
  2. 镜像和容器:在 Docker 中,镜像是应用程序及其依赖项的打包格式,类似于虚拟机中的镜像文件,但比虚拟机更轻量级。容器则是基于镜像创建的运行实例,每个容器都是相互独立的,具有自己的文件系统、进程、网络等环境。
  3. Docker 守护进程:Docker 守护进程负责管理容器的生命周期,包括创建、启动、停止、删除等操作。当用户运行一个容器时,Docker 守护进程会根据该容器所需的镜像创建一个新的容器,并为其分配唯一的命名空间和控制组。
  4. Dockerfile:Dockerfile 是用于定义镜像构建过程的文本文件,其中包含了一系列指令,如基础镜像选择、软件包安装、环境变量设置等。当用户执行 Docker build 命令时,Docker 会根据 Dockerfile 构建一个新的镜像。

通过上述机制,Docker 实现了应用程序和其依赖项的隔离和可移植性,并为用户提供了简单易用的管理工具。同时,Docker 还支持容器编排和集群管理等高级功能,可以帮助用户更好地构建分布式系统。

(Ps: 在使用Docker启动容器时,请勿在root用户权限下运行容器,实验环境可以采用。因为docker本质上只是一个程序,底层还是基于Linux操作系统提供的命名空间进行,控制主,文件系统上实现的(是的在操作系统成本上的抽象)。因此当docker以root身份运行时,如果攻击者成功入侵了容器,并获取了 root 权限,那么他们可以对容器内的文件系统进行修改、进行系统级别的操作,甚至尝试逃逸到宿主机系统。这对整个平台来说是灾难性的)

docker的基本架构如下:

在这里插入图片描述

  • Docker_Host:

    • 安装Docker的主机
  • Docker Daemon:

    • 运行在Docker主机上的Docker后台进程
  • Client:

    • 操作Docker主机的客户端(命令行、UI等)
  • Registry:

    • 镜像仓库
    • Docker Hub
  • Images:

    • 镜像,带环境打包好的程序,可以直接启动运行
  • Containers:

    • 容器,由镜像启动起来正在运行中的程序

安装

  1. 移除以前的安装包:

    sudo yum remove docker \
                      docker-client \
                      docker-client-latest \
                      docker-common \
                      docker-latest \
                      docker-latest-logrotate \
                      docker-logrotate \
                      docker-engine
    
  2. 配置yml源

    sudo yum install -y yum-utils
    sudo yum-config-manager \
    --add-repo \
    http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
  3. 安装社区版本docker

    sudo yum install -y docker-ce docker-ce-cli containerd.io
    

    #以下是在安装k8s的时候使用 yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io-1.4.6

  4. 开启开机自启动并启动容器

    systemctl enable docker --now
    

此外还可以配置镜像加速地址(阿里云有相关服务,只需要将地址进行替换即可)

"registry-mirrors": ["82m9ar63.mirror.aliyuncs.com"] 是示例地址。

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://82m9ar63.mirror.aliyuncs.com"],
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

此外还有docker-compose

sudo curl -L https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose

常用命令

docker的运行指令关系,可以使用一张图进行概括:

在这里插入图片描述

对镜像进行操作

docker pull nginx  #下载最新版

镜像名:版本名(标签)
docker pull nginx:1.20.1
docker pull redis  #下载最新
docker pull redis:6.2.4
## 下载来的镜像都在本地
docker images  #查看所有镜像
redis = redis:latest
docker rmi 镜像名:版本号/镜像id

对容器进行操作

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

【docker run  设置项   镜像名   镜像启动运行的命令(镜像里面默认有的,一般不会写)

# -d:后台运行
# --restart=always: 开机自启
docker run --name=mynginx   -d  --restart=always -p  88:80   nginx

# 查看正在运行的容器
docker ps
# 查看所有
docker ps -a
# 删除停止的容器
docker rm  容器id/名字
docker rm -f mynginx   #强制删除正在运行中的

#停止容器
docker stop 容器id/名字
#再次启动
docker start 容器id/名字

#应用开机自启
docker update 容器id/名字 --restart=always

进入容器内部

docker exec -it 容器id  /bin/bash

挂载数据卷

docker run --name=mynginx   \
-d  --restart=always \
-p  88:80 -v /data/html:/usr/share/nginx/html:ro  \
nginx
# 修改页面只需要去 主机的 /data/html

其余指令可参考运行指令关系图

搭建私有仓库/上传本地镜像至仓库

在实际任务需求当中,我们存在搭建私有仓库,或者使用阿里云仓库,再或者在dockerhub使用仓库的需求。尤其是在DevOps过程中,我们构建本地镜像时需要推送至仓库,之后拉取到测试/生产环境当中的最新镜像进行部署。并且在整个项目周期当中,会产生较多的镜像,需要进行统一托管。

搭建私有仓库

这里使用官方的register进行搭建,并带有基本界面

通过docker-compose进行搭建

version: '3.0'
services:
  registry:
    image: registry
    volumes:
      - ./registry-data:/var/lib/registry
  ui:
    image: joxit/docker-registry-ui:static
    ports:
      - 8080:80
    environment:
      - REGISTRY_TITLE=测试仓库
      - REGISTRY_URL=http://registry:5000
    depends_on:
      - registry


docker-compose up -d

这将启动私有仓库服务,并且 -d​ 参数表示在后台运行服务。

验证私有仓库是否成功运行:

docker-compose ps

停止并移除私有仓库服务:

docker-compose down

之后需要配置信任地址:

vi /etc/docker/daemon.json

添加地址

例如:

{
    "registry-mirror":[
        "http://hub-mirror.c.163.com"
    ],
    "insecure-registries":[
        "192.168.1.1:5000"
    ]
}

之后重启docker

systemctl daemon-reload
systemctl restart doc

推送

推送镜像到私有镜像服务必须先tag,步骤如下:

① 重新tag本地镜像,名称前缀为私有仓库的地址:192.168.150.101:8080/

docker tag nginx:latest 192.168.150.101:8080/nginx:1.0 

② 推送镜像

docker push 192.168.150.101:8080/nginx:1.0 

③ 拉取镜像

docker pull 192.168.150.101:8080/nginx:1.0 

(注意在dockerhub创建仓库类似(并且可以搭建私有仓库,此时需要docker login 登录,如果是私有仓库))

docker login <registry-url>

echo "<your-password>" | docker login --username <your-username> --password-stdin <registry-url>

docker-compose结构

Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。格式如下:

version: "3.8"
 services:
  mysql:
    image: mysql:5.7.25
    environment:
     MYSQL_ROOT_PASSWORD: 123 
    volumes:
     - "/tmp/mysql/data:/var/lib/mysql"
     - "/tmp/mysql/conf/hmy.cnf:/etc/mysql/conf.d/hmy.cnf"
	restart: always
  web:
    build: .
    ports:
     - "8090:8090"
  • mysql:一个基于mysql:5.7.25​镜像构建的容器,并且挂载了两个目录
  • web:一个基于docker build​临时构建的镜像容器,映射端口时8090

可以看做是将多个docker run命令写到一个文件,只是语法稍有差异。描述清楚需求,可以由gpt生成。or 使用shell脚本

#!/bin/bash

# 交互式输入服务个数
read -p "请输入要创建的服务个数: " num_services

output_file="docker-compose.yml"

# 创建空的 Docker Compose 文件
echo "version: '3.8'" > $output_file
echo "services:" >> $output_file

# 循环读取每个服务的信息并写入文件
for ((i=1; i<=$num_services; i++))
do
    read -p "请输入第 $i 个服务的名称: " service_name
    read -p "请输入基础镜像名称: " image_name
    read -p "请输入挂载卷信息(格式:/path/to/host/dir:/container/dir): " volume_info
    read -p "是否自动启动该服务?(true/false): " auto_start

    # 写入服务信息到 Docker Compose 文件
    echo "  $service_name:" >> $output_file
    echo "    image: $image_name" >> $output_file
    echo "    container_name: $service_name" >> $output_file
    echo "    volumes:" >> $output_file
    echo "      - $volume_info" >> $output_file
    echo "    networks:" >> $output_file
    echo "      - bridge" >> $output_file
    echo "    restart: $auto_start" >> $output_file
done

echo "networks:" >> $output_file
echo "  bridge:" >> $output_file
echo "    driver: bridge" >> $output_file

echo "Docker Compose 文件已生成: $output_file"

镜像构建

镜像构建是docker当中非常重要的部分

在根目录下使用Dockerfile

语法与格式

格式如下: 在这里插入图片描述

FROM(指定基础 image)

构建指令,必须指定且需要在 Dockerfile 其他指令的前面。后续的指令都依赖于该指令指定的 image。FROM 指令指定的基础 image 可以是官方远程仓库中的,也可以位于本地仓库

FROM centos:7.2
FROM centos
MAINTAINER(用来指定镜像创建者信息)

构建指令,用于将 image 的制作者相关的信息写入到 image 中。当我们对该 image 执行 docker inspect 命令时,输出中有相应的字段记录该信息。

MAINTAINER  wangyang "wangyang@itxdl.cn"

LABEL(将元数据添加到镜像)
标签的定义
# 指令将元数据添加到镜像。`LABEL` 是键值对。要在 `LABEL` 值中包含空格,请像在命令行中一样使用引号和反斜杠
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

# 多行标签定义方式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"
标签的继承

**基础或父镜像(**​FROM​ 行中的镜像)中包含的标签由您的镜像继承。如果标签已经存在但具有不同的值,则最近应用的值将覆盖任何先前设置的值

查看镜像标签
$ docker image inspect --format='' myimage
案例
# dockerfile
FROM busybox
LABEL author=wangyanglinux
CMD echo wangyanglinux

# 构建镜像
[root@k8s-master01 testd]# docker build -t wangyanglinux:0.0.1 . --no-cache

# 安装 jq
[root@k8s-master01 testd]# yum install epel-release
[root@k8s-master01 testd]# yum install jq

# 查看标签
[root@k8s-master01 testd]# docker image inspect wangyanglinux:0.0.1 --format "{{json .ContainerConfig.Labels}}" | jq
{
  "author": "wangyanglinux"
}
RUN(安装软件用)

构建指令,RUN 可以运行任何被基础 image 支持的命令。如基础 image 选择了 Centos,那么软件管理部分只能使用 Centos 的包管理命令

RUN cd /tmp && curl -L 'http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.8/bin/apache-tomcat-7.0.8.tar.gz' | tar -xz 
RUN ["/bin/bash", "-c", "echo hello"]
USER(设置container容器的用户)

设置指令,设置启动容器的用户,默认是 root 用户

USER daemon  =  ENTRYPOINT ["memcached", "-u", "daemon"]  
EXPOSE(指定容器需要映射到宿主机器的端口)

设置指令,该指令会将容器中的端口映射成宿主机器中的某个端口。当你需要访问容器的时候,可以不是用容器的 IP 地址而是使用宿主机器的 IP 地址和映射后的端口。要完成整个操作需要两个步骤,首先在 Dockerfile 使用 EXPOSE 设置需要映射的容器端口,然后在运行容器的时候指定 -P 选项加上 EXPOSE 设置的端口,这样 EXPOSE 设置的端口号会被随机映射成宿主机器中的一个端口号。也可以指定需要映射到宿主机器的那个端口,这时要确保宿主机器上的端口号没有被使用。EXPOSE指令可以一次设置多个端口号,相应的运行容器的时候,可以配套的多次使用 -p 选项

# 映射多个端口  
EXPOSE port1 port2 port3  

# 随机暴露需要运行的端口
docker run -P image

# 相应的运行容器使用的命令  
docker run -p port1 -p port2 -p port3 image  

# 还可以指定需要映射到宿主机器上的某个端口号  
docker run -p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image  
ENV(用于设置环境变量)

构建指令,在 image 中设置一个环境变量。设置了后,后续的 RUN 命令都可以使用,container 启动后,可以通过 docker inspect 查看这个环境变量,也可以通过在 docker run --env key=value 时设置或修改环境变量。假如你安装了 JAVA 程序,需要设置 JAVA_HOME,那么可以在 Dockerfile 中这样写:

ENV JAVA_HOME /path/to/java/dirent
ARG(设置变量)

起作用时机

  • arg 是在 build 的时候存在的, 可以在 Dockerfile 中当做变量来使用
  • env 是容器构建好之后的环境变量, 不能在 Dockerfile 中当参数使用
案例
# Dockerfile
FROM redis:3.2-alpine

LABEL maintainer="wangyanglinux@163.com"

ARG REDIS_SET_PASSWORD=developer
ENV REDIS_PASSWORD ${REDIS_SET_PASSWORD}

VOLUME /data

EXPOSE 6379

CMD ["sh", "-c", "exec redis-server --requirepass \"$REDIS_PASSWORD\""]
FROM nginx:1.13.1-alpine

LABEL maintainer="wangyanglinux@163.com"

RUN mkdir -p /etc/nginx/cert \
    && mkdir -p /etc/nginx/conf.d \
    && mkdir -p /etc/nginx/sites

COPY ./nginx.conf /etc/ngixn/nginx.conf
COPY ./conf.d/ /etc/nginx/conf.d/
COPY ./cert/ /etc/nginx/cert/

COPY ./sites /etc/nginx/sites/


ARG PHP_UPSTREAM_CONTAINER=php-fpm
ARG PHP_UPSTREAM_PORT=9000
RUN echo "upstream php-upstream { server ${PHP_UPSTREAM_CONTAINER}:${PHP_UPSTREAM_PORT}; }" > /etc/nginx/conf.d/upstream.conf

VOLUME ["/var/log/nginx", "/var/www"]

WORKDIR /usr/share/nginx/html

<!--这里的变量用的就是 ARG 而不是 ENV了,因为这条命令运行在 Dockerfile 当中的, 像这种临时使用一下的变量没必要存环境变量的值就很适合使用 ARG-->

ADD(从 src 复制文件到 container 的 dest 路径)
ADD <src> <dest>  
        <src> 是相对被构建的源目录的相对路径,可以是文件或目录的路径,也可以是一个远程的文件 url;
        <dest> 是 container 中的绝对路径
COPY (从 src 复制文件到 container 的 dest 路径)
COPY <src> <dest> 
VOLUME(指定挂载点)

设置指令,使容器中的一个目录具有持久化存储数据的功能,该目录可以被容器本身使用,也可以共享给其他容器使用。我们知道容器使用的是 AUFS,这种文件系统不能持久化数据,当容器关闭后,所有的更改都会丢失。当容器中的应用有持久化数据的需求时可以在 Dockerfile中 使用该指令

FROM base  
VOLUME ["/tmp/data"]  
WORKDIR(切换目录)

设置指令,可以多次切换(相当于cd命令),对RUN,CMD,ENTRYPOINT生效

WORKDIR /p1   
RUN touch a.txt
WORKDIR p2 
RUN vim a.txt
CMD(设置 container 启动时执行的操作)

设置指令,用于 container 启动时指定的操作。该操作可以是执行自定义脚本,也可以是执行系统命令。该指令只能在文件中存在一次,如果有多个,则只执行最后一条

CMD echo “Hello, World!” 

CMD /usr/local/nginx/sbin/nginx  tail -f /usr/local/nginx/logs/access.log
ENTRYPOINT(设置container启动时执行的操作)

设置指令,指定容器启动时执行的命令,可以多次设置,但是只有最后一个有效。

ENTRYPOINT ls -l 

该指令的使用分为两种情况,一种是独自使用,另一种和 CMD 指令配合使用。当独自使用时,如果你还使用了 CMD 命令且 CMD 是一个完整的可执行的命令,那么 CMD 指令和 ENTRYPOINT 会互相覆盖只有最后一个 CMD 或者 ENTRYPOINT 有效

CMD echo “Hello, World!” 
ENTRYPOINT ls -l
# CMD 指令将不会被执行,只有 ENTRYPOINT 指令被执行 
ENTRYPOINT ls -l
CMD echo “Hello, World!” 
# 反之

另一种用法和 CMD 指令配合使用来指定 ENTRYPOINT 的默认参数,这时 CMD 指令不是一个完整的可执行命令,仅仅是参数部分;ENTRYPOINT 指令只能使用 JSON 方式指定执行命令,而不能指定参数

FROM ubuntu  
CMD ["-l"]  
ENTRYPOINT ["/usr/bin/ls"]
CMD 与 ENTRYPOINT 的较量

**官方释义:**​docs.docker.com/engine/refe…

cmd 给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行的命令

FROM centos
CMD echo "hello cmd!"

docker run xx 
==> hello cmd!

# 如果我们在 run 时指定了命令或者有entrypoint,那么 cmd 就会被覆盖。仍然是上面的 image。run 命令变了:
docker run xx echo glgl
==> glgl

cmd 是默认体系,entrypoint 是正统地用于定义容器启动以后的执行体

FROM centos 
CMD ["p in cmd"]
ENTRYPOINT ["echo"]


[root@k8s-master01 testd]# docker run --name test1  ent:v1
p in cmd
[root@k8s-master01 testd]# docker run --name test2 ent:v1  p in run
p in run


# ENTRYPOINT shell 模式任何 runcmd 的参数都无法被传入到 entrypoint 里。官网推荐第一种用法
FROM centos
CMD ["p in cmd"]
ENTRYPOINT echo
ONBUILD(在子镜像中执行)

ONBUILD 指定的命令在构建镜像时并不执行,而是在它的子镜像中执行

ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
STOPSIGNAL signal

STOPSIGNAL 指令设置将发送到容器以退出的系统调用信号。这个信号可以是一个有效的无符号数字,与内核的syscall​表中的位置相匹配,例如9​,或者是SIGNAME​格式的信号名,例如:SIGKILL

SIGHUP 1 A 终端挂起或者控制进程终止

SIGINT 2 A 键盘中断(如break键被按下)

SIGQUIT 3 C 键盘的退出键被按下

SIGILL 4 C 非法指令

SIGABRT 6 Cabort(3)发出的退出指令

SIGFPE 8 C 浮点异常

SIGKILL 9 AEF Kill信号

SIGSEGV 11 C 无效的内存引用

SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道

SIGALRM 14 Aalarm(2)发出的信号

SIGTERM 15 A 终止信号

SIGUSR1 30,10,16 A 用户自定义信号1

SIGUSR2 31,12,17 A 用户自定义信号2

SIGCHLD 20,17,18 B 子进程结束信号

SIGCONT 19,18,25 进程继续(曾被停止的进程)

SIGSTOP 17,19,23 DEF 终止进程

SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键

SIGTTIN 21,21,26 D 后台进程企图从控制终端读

SIGTTOU 22,22,27 D 后台进程企图从控制终端写

SHELL (覆盖命令的shell模式所使用的默认 shell)

Linux 的默认shell是 [“/bin/sh”, “-c”],Windows 的是 [“cmd”, “/S”, “/C”]。SHELL 指令必须以 JSON 格式编写。SHELL 指令在有两个常用的且不太相同的本地 shell:cmd 和 powershell,以及可选的 sh 的 windows 上特别有用

HEALTHCHECK (容器健康状况检查命令)
HEALTHCHECK [OPTIONS] CMD command
    [OPTIONS] 的选项支持以下三中选项
        --interval=DURATION 两次检查默认的时间间隔为 30--timeout=DURATION 健康检查命令运行超时时长,默认 30--retries=N 当连续失败指定次数后,则容器被认为是不健康的,状态为 unhealthy,默认次数是3
    
    CMD后边的命令的返回值决定了本次健康检查是否成功,具体的返回值如下:
        0: success - 表示容器是健康的
        1: unhealthy - 表示容器已经不能工作了
        2: reserved - 保留值
    
HEALTHCHECK NONE

# 第一个的功能是在容器内部运行一个命令来检查容器的健康状况
# 第二个的功能是在基础镜像中取消健康检查命令
# 为了帮助调试失败的探测,command 写在 stdout 或 stderr 上的任何输出文本(UTF-8编码)都将存储在健康状态中,并且可以通过 docker inspect 进行查询。 这样的输出应该保持简短(目前只存储前4096个字节)
注意

HEALTHCHECK 命令只能出现一次,如果出现了多次,只有最后一个生效

模板
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
查看容器的健康状态
$ docker inspect –format ‘{{json .State.Health.Status}}’ cID

常用与示例

相对来说,完整的构建指令较为复杂,我们只需要知道常用的即可,并且知道如何修改模板,或者使用GPT修改即可。尽可能降低这方面的繁琐细节,提高工作效率。

在这里插入图片描述

构建Java镜像

# 指定基础镜像
FROM centos:7.9.2009

# 更换 yum 源为阿里云
RUN mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup && \
    curl -o /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo && \
    yum clean all && \
    yum makecache

# 更新系统软件包
RUN yum update -y

# 安装必要的软件包
RUN yum install -y java-1.8.0-openjdk-devel

# 设置Java环境变量
ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk
ENV PATH $PATH:$JAVA_HOME/bin

# 设置工作目录
WORKDIR /app

# 拷贝整个项目到工作目录
COPY . /app

# 安装maven
RUN yum install -y maven

# 构建Java Maven项目
RUN mvn clean package

# 拷贝生成的jar包到当前目录
RUN cp /app/target/*.jar /app/docker-demo.jar

# 暴露端口
EXPOSE 8090

# 入口,java项目的启动命令
CMD ["java", "-jar", "docker-demo.jar"]



当然基础镜像我们依然使用其他的镜像,例如Java镜像,这样不需要在构建时安装Java,配置系统源等等操作,或者你可以构建自己的模板镜像。