前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >kubernetes 上手指南:前言

kubernetes 上手指南:前言

作者头像
谢伟
发布2019-12-02 21:31:17
8310
发布2019-12-02 21:31:17
举报
文章被收录于专栏:GopherCoder

今天的主题:kubernetes 学习前言,主要和容器相关。

部署方案

容器技术诞生后,成为云计算领域的绝对主角,但容器本身价值并不大,任何互联网领域都涉及到部署,容器编排才重要。创造docker 的dotCloud 的公司并没有获取到云计算领域的红利,虽然之后也推出的自家的 docker swarm 应用于容器编排,相比设计理念更为先进的 kubernetes,存在更多的问题,事实上 k8s 已经成为容器编排领域的领头羊。几乎所有的互联网公司,云计算公司都使用 k8s 用于容器编排。

简单的说容器编排的意思是:将应用容器化,按照一套规则自动在节点按照用户的需求部署。这套规则由k8s 规定,开发者应用容器化,按照规则编写编排脚本即可。

整体上 k8s设计理念先进,得益于 Google 领域内的多年实践总结,尽管如此,对开发者而言,掌握 k8s 知识,却有点复杂。

复杂体现在两方面:

  • 集群部署

集群部署有规格要求,如果你是初学者,想要亲手部署这样一套集群服务,环境可能都没有。基础的容器组件拉取也可能是问题,不过这一情况正在好转。

  • 概念繁多

开发者需掌握 k8s 诸多的概念,另外得掌握编排脚本语法约束,不过,只要肯花时间都可以掌握。

简单的说 k8s 适用于复杂的部署,多节点,多应用,系统越复杂,上 k8s 调度起来更方便。简单的系统,应用容器化,运行容器即可,如果多应用使用单节点部署方案 docker-compose 即可。

容器

容器技术给应用创造一个完全独立的环境,可以跨平台使用,我认为是每一个开发者需要掌握的技术之一。

如何使用容器技术?

  • 开者专注自身业务需求,编写代码
  • 编写镜像制作脚本,将应用打包
  • 服务器上拉取制作的镜像
  • 容器启动,运行服务

容器技术主要包四大组成部分:

  • 镜像 image
  • 容器 container
  • 网络 network
  • 数据卷 volume

何为镜像:简单的说,包含虚拟运行环境的文件包,是一堆文件的合集,服务在该系统之上能够运行起来。docker 镜像采用了分层架构。

何为容器:简单的说,镜像的运行状态,用来隔离虚拟环境的基础设施。主要包含:镜像、运行环境、指令集

何为网络:网络是应用之间通讯的媒介。

何为数据卷:应用肯定会涉及到数据持久化操作,数据卷就是用于宿主机和容器之间共享或者持久化。

容器技术的实现: 得益于以下三点技术

  • Namespace命名空间 : 作用是隔离
  • Control Groups控制组:作用是限制计算机资源的使用
  • Union File System 联合文件系统:作用是实现不同目录挂载到同一目录

问题一: Namespace 怎么就能做到隔离?

容器本质上是进程,Linux操作系统提供了 PID, Mount, Network 等 namespace, 使被隔离的进程只能看到当前的环境状态。

比方说 PID namespace, 启动容器执行命令,为什么该进程的 PID 是 1 呢?

代码语言:javascript
复制
>> ps -ef | grep 1
root         1     0  0 14:08 ?        00:00:01 /go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything
root        15     0  0 15:20 pts/0    00:00:00 bash
root        33    15  0 15:22 pts/0    00:00:00 ps -ef
root        34    15  0 15:22 pts/0    00:00:00 grep 1

其中 /go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything 是我自己的容器启动时的命令。

主要原因是:Linux 创建进程可以指定 PID

代码语言:javascript
复制
int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

类似的其他也可以依靠对应的 namespace 技术做到隔离。

问题二:control Groups 怎么就能够现在计算机资源的使用了?

首先限制的资源包括:CPU, 内存,磁盘,网络等。如果你经常使用 docker,一定会经常看到一些目录: /sys/fs/cgroup

代码语言:javascript
复制
>> mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)

以 CPU 为例,如何实现对资源的限制呢?

首先查看我本机运行的容器:

代码语言:javascript
复制
>>  docker ps --format "{{.ID}}: {{.Status}}"

81f4b0f829c9: Up 4 days
226749434f46: Up 4 days
3102306bf580: Up 4 days
代码语言:javascript
复制
>> ls /sys/fs/cgroup/cpu/docker

drwxr-xr-x 2 root root 0 Dec  1 07:31 226749434f46cc4197a24c09e691536b679b372735f1197608bc9085c1d95d9f
drwxr-xr-x 2 root root 0 Nov 26 08:44 3102306bf58037012bd5d2b6f595ae00450125d4152ed53d2db71d35ab297439
drwxr-xr-x 2 root root 0 Nov 26 14:17 81f4b0f829c9558228691c979af188b30cfaec01dd78b97d90e95aff00cbfb99
-rw-r--r-- 1 root root 0 Dec  1 07:30 cgroup.clone_children
-rw-r--r-- 1 root root 0 Dec  1 07:30 cgroup.procs
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.stat
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_all
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Dec  1 07:30 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec  1 07:30 cpu.shares
-r--r--r-- 1 root root 0 Dec  1 07:30 cpu.stat
-rw-r--r-- 1 root root 0 Dec  1 07:30 notify_on_release
-rw-r--r-- 1 root root 0 Dec  1 07:30 tasks

>> cd 226749434f46cc4197a24c09e691536b679b372735f1197608bc9085c1d95d9f

-rw-r--r-- 1 root root 0 Dec  1 07:31 cgroup.clone_children
-rw-r--r-- 1 root root 0 Nov 26 14:06 cgroup.procs
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.stat
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_all
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Dec  1 07:31 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Dec  1 07:31 cpu.shares
-r--r--r-- 1 root root 0 Dec  1 07:31 cpu.stat
-rw-r--r-- 1 root root 0 Dec  1 07:31 notify_on_release
-rw-r--r-- 1 root root 0 Dec  1 07:31 tasks

没错,docker 实现对资源的限制靠的是写文件的形式,即对类似这样的文件进行限制:假设:cpu.cfs_period_us(10000),cpu.cfs_quota_us(2000)表明只能使用到20 % 的CPU 资源。

代码语言:javascript
复制
cpu.cfs_period_us
cpu.cfs_quota_us
...

问题三:联合文件系统是什么意思?

将多个位置的不同目录挂载到同一目录下。

比如:

代码语言:javascript
复制
>> tree
├── A
│  ├── a
│  └── x
└── B
  ├── b
  └── x

挂载到 C 目录下:

代码语言:javascript
复制
$ tree C
├── a
├── b
└── x

当然实际的实现比我说的要复杂的多。docker 正是依靠这三种技术,使的可以为我们创造一个完全隔离的沙箱,这样做的好处是:环境的一致性,方便部署。你会越来越少的问这样一个问题:我本地可以运行啊?为什么在服务器上就不行。

Docker 架构

docker 采用典型的 C/S 架构,你安装 docker 软件。默认会在本机启动一个守护进程 docker dameon , 同时提供一个命令行客户端 docker cli . 你可以使用命令行操作包括:镜像、容器等各种资源。

根据 docker 的四大组成部分,docker cli 的命令主要围绕这四个命令展开:

  • docker image // 操作镜像
  • docker container // 操作容器
  • docker network // 操作网络
  • docker volume // 操作数据卷

你可能会觉得命令行命令太多,怎么办?创造高频使用环境即可,即:日常工作中频繁的不断的使用docker 即可。

制作镜像

Docker Hub 是最大的镜像托管平台,作用是:1. 镜像存储 2. 镜像分发。无数的开源软件的官方镜像托管在该平台,用户可以完全免费的从该平台拉取镜像,完成自己的任务。

那么对于开发者如何制作自己的镜像呢?

答案是:编写 Dockerfile 文件。

docker 公司对于制作镜像,约束了一套语法规范,其实命令也就 10几个。

先不谈具体的命令是什么,假设你自己开发了一套系统,没有容器之前,你部署应用会执行什么操作?

  • 应用运行涉及依赖,你会提前在主机上安装相应的依赖软件
  • 指定应用目录
  • 拷贝,将应用程序拷贝至主机,不管是本机直接拷贝还是代码托管平台克隆代码,都算
  • 应用访问涉及端口开放,应用多了,你还得考虑端口会不会冲突不是?
  • 应用启动

这是不是大概的操作流程?

是的,dockerfile 将这一系列动作指令化。

举个例子:我这边自己写了个服务,对外暴露的是 API,我现在想制作镜像,我会怎么做呢?

代码语言:javascript
复制
FROM golang:1.13.4

MAINTAINER XieWei(1156143589@qq.com)

EXPOSE 8888 8081

RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime;\
    echo "Asia/Shanghai" > /etc/timezone;\
    dpkg-reconfigure -f noninteractive tzdata

WORKDIR /go/src/github.com/wuxiaoxiaoshen/go-anything
RUN echo $PWD
COPY . /go/src/github.com/wuxiaoxiaoshen/go-anything

RUN apt-get update && apt-get install -q -y vim nginx  git openssh-client cron && apt-get clean;\
    go mod vendor;\
    make remove;\
    make prod;\
    echo Succeed!
CMD ["bash","-c", "/go/src/github.com/wuxiaoxiaoshen/go-anything/go-anything"]

简单的说:

  • FORM 依赖的基础镜像,我这个服务是 Go 编写的,依赖 Go 的运行环境,所以继承官方基础镜像
  • MAINTAINER 维护者,将被抛弃的命令,使用替代指令 LABEL
  • EXPOSE 暴露端口,意思是容器会暴露8888,8081 两个端口
  • RUN 将一些命令发送到终端执行,安装一些依赖
  • WORKDIR 指定目录
  • COPY 拷贝上下文内容至指定目录
  • CMD 容器启动时执行的命令

dockerfile 文件编写并不用太复杂,就可以按照开发者的约束制作镜像。非常便利。如果记不住命令,仍然建议自身创造高频使用环境,在日常工作中,有意无意的多使用。

一般我会把制作好的镜像直接推送至 Docker Hub, 需要使用到再从 Docker Hub 上拉取,当然你需要先注册个账号。

多应用

还是我这个应用:go-anything, 实质它是个 web 服务,为创造稍微复杂的系统,这个系统,我使用到了 MySQL, Redis, Kafka 等技术集成进来。

如果这些 MySQL, Redis, Kafka 等都选择的是云服务,go-anything 中指定相应的云服务地址即可,相当于没有外部依赖。当然我没钱买云服务,这些服务都选择的是在本地启动相应的容器,这么说,这其实是个多应用的编排的问题。

如果是单节点或者测试环境,那么我推荐使用 docker-compose 来链接和启动多服务。我这个服务依赖于上面三个服务,这三个服务启动之后,才能正确的运行服务。

那么什么是 docker-compose, 简单的说,是用 Python 编写的命令行工具,用来定义和运行由多个容器组成的应用。它规定了一套语法规范,这些规范的关键字都和上文提到的容器相关。

就我这个应用,我怎么编写 docker-compose 呢?

代码语言:javascript
复制
version: "3"

services:
  redis:
    image: redis:latest
    ports:
      - 6379:6379
    expose:
      - 6379
    container_name: redis_for_go_anything
    command: redis-server --appendonly yes --requirepass "adminRedis"
    networks:
      - go-anything-network
    volumes:
      - data:/data
  mysql:
    image: mysql:latest
    container_name: mysql_for_go_anything
    networks:
      - go-anything-network
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: adminMysql
      MYSQL_DATABASE: go-anything
      MYSQL_USER: root
    ports:
      - 3306:3306
  kafka:
    image: index.docker.io/wurstmeister/kafka:latest
    container_name: kafka_for_go_anything
    ports:
      - 9092:9092
    environment:
      KAFKA_OFFSETS_TOPIC_REPLIATION_FACTOR: 1
      KAFKA_ADVERTISED_LISTENERS: "PLAINTEXT://127.0.0.1:9092"
      KAFKA_LISTENERS: "PLAINTEXT://:9092"
      KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
      KAFKA_CREATE_TOPICS: "go-anything:20:1:compact"
      KAFKA_LOG_DIRS: /kafka/kafka-logs
    depends_on:
      - zookeeper
    networks:
      - go-anything-network
    volumes:
      - data:/kafka/kafka-logs
  zookeeper:
    image: index.docker.io/wurstmeister/zookeeper:latest
    container_name: zookeeper_for_go_anything
    ports:
      - 2181:2181
    networks:
      - go-anything-network
networks:
  go-anything-network:
    driver: bridge
volumes:
  data: {}

看上去稍微复杂点,其实可以划分为四个部分:

代码语言:javascript
复制
version: 版本
services: 服务
volume: 数据卷
network: 网络

其中最重要的是 services,像 network, volume 这些,不写都可以使用默认的,但我一般都会写,显式化得定义 network 和 volume。

单独抽出 redis 服务来看,其实是关于容器的操作:

代码语言:javascript
复制
  redis:
    image: redis:latest
    ports:
      - 6379:6379
    expose:
      - 6379
    container_name: redis_for_go_anything
    command: redis-server --appendonly yes --requirepass "adminRedis"
    networks:
      - go-anything-network
    volumes:
      - data:/data

redis: 服务的名称,自定义 image: 镜像地址和版本 ports: 主机和容器端口映射 expose: 暴露容器端口 container_name: 容器自定义名称 command: 容器启动时命令 networks: 指定网络 volume: 数据卷定义

这些比如端口,执行命令等,开发者有时候记不住,怎么办? 我也记不住,都是查看 docker hub 上相应的文档说明,毕竟这是别人制作的镜像,不看文档,怎么知道怎么做。

编排好上文三个服务:MySQL, Redis, Kafka,怎么一键让服务运行?

代码语言:javascript
复制
>> docker-compose -f docker-compose.yml up -d

可以命名为 docker-compose.yml,也可以不这样命名。不指定文件,自动会在当前目录下寻找 docker-compose.yml。docker-compose 还提供了其他命令,绝大多数也是用来操作镜像和容器的。

假设我把我的应用已经推送至 docker hub 上:wuxiaoshen/go-anything:v0.19

我现在应该怎么启动我的服务呢?

代码语言:javascript
复制
>> docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
wuxiaoshen/go-anything               v0.19               1788461daca4        30 hours ago        1.43GB

>> docker run --name go-anything-2  --link mysql_for_go_anything --link redis_for_go_anything --link kafka_for_go_anything --net go-anything_go-anything-network -p 8081:8888 -d 1788461daca4

其中:

代码语言:javascript
复制
--name 指定容器名称
--link 链接服务
-p 端口映射
-d 后台运行

为什么要 --link 服务呢?因为我项目的配置文件是这样的:

代码语言:javascript
复制
  mysql:
    port: "3306"
    db: go-anything
    user: root
    passwd: adminMysql
    host: mysql
  redis:
    port: "6379"
    auth: adminRedis
    host: redis
  kafka:
    broker: kafka:9092
    topic: go-anything
    consumerGroup: go-anything-consumer-group

其中对应的 host 和 docker-compose.yml 对应的services 中命名的服务名称一致。这样可以直接使用服务名称代替 host,而不用显式的指定主机地址(127.0.0.1)

这样我们就启动了多应用:

代码语言:javascript
复制
>> docker ps --format "{{.ID}}: {{.Command}}: {{.Ports}}"
c7d820406af2: "bash -c /go/src/git…": 8081/tcp, 0.0.0.0:8081->8888/tcp
d197ec955421: "start-kafka.sh": 0.0.0.0:9092->9092/tcp
50cdda796143: "/bin/sh -c '/usr/sb…": 22/tcp, 2888/tcp, 3888/tcp, 0.0.0.0:2181->2181/tcp
3cb75161e860: "docker-entrypoint.s…": 0.0.0.0:3306->3306/tcp, 33060/tcp
46d16bd9837e: "docker-entrypoint.s…": 0.0.0.0:6379->6379/tcp

启动服务中指定了映射端口:将宿主机的 8081 和 容器内的 8888 的端口相互映射,而应用指定的端口是:8888,这样本机访问 8081 端口可以访问到容器 8888 端口。

代码语言:javascript
复制
# 项目内指定端口
router.Run("8888")

测试下:

  • 先查看应用启动日志
代码语言:javascript
复制
>> docker logs -f c7d820406af2

2019/12/01 14:08:46 Env:  service
2019/12/01 14:08:46 Web Start...
2019/12/01 14:08:46 Step 0: Configs...
2019/12/01 14:08:46 Step 1: Mysql...
2019/12/01 14:08:46 configs: LoadConfigs: key: service.mysql
2019/12/01 14:08:46 Keys: MySQL: map[string]interface {}{"db":"go-anything", "host":"mysql", "passwd":"adminMysql", "port":"3306", "user":"root"}
2019/12/01 14:08:46 root:adminMysql@tcp(mysql:3306)/go-anything?charset=utf8&parseTime=True&loc=Local
2019/12/01 14:08:47 Step 2: Redis...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.redis
2019/12/01 14:08:47 Keys: Redis: map[string]interface {}{"auth":"adminRedis", "host":"redis", "port":"6379"}
2019/12/01 14:08:47 Step 3: Kafka...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.kafka
2019/12/01 14:08:47 Keys: Kafka: map[string]interface {}{"broker":"kafka:9092", "consumergroup":"go-anything-consumer-group", "topic":"go-anything"}
2019/12/01 14:08:47 Step 4: Email...
2019/12/01 14:08:47 configs: LoadConfigs: key: service.email
// 省略
[DBUG] 2019/12/01 14:08 GET: /v1/api/status/health -> github.com/wuxiaoxiaoshen/go-anything/src/Healthz.healthZHandler() and 2 more
// 省略
[DBUG] 2019/12/01 14:08 Application: running using 1 host(s)
[DBUG] 2019/12/01 14:08 Host: addr is :8888
[DBUG] 2019/12/01 14:08 Host: virtual host is 0.0.0.0:8888
[DBUG] 2019/12/01 14:08 Host: register startup notifier
[DBUG] 2019/12/01 14:08 Host: register server shutdown on interrupt(CTRL+C/CMD+C)
Now listening on: http://0.0.0.0:8888
Application started. Press CTRL+C to shut down.
  • 调用下接口:/v1/api/status/health
代码语言:javascript
复制
>> curl http://127.0.0.1:8081/v1/api/status/health | jq .

{
  "code": 200,
  "data": "pong",
  "status": "ok"
}

可以看到完成了多应用的启动部署。

可以看到单节点上部署多应用,其实 docker-compose 是个很好的工具,那假如多节点呢?docker-compose 完全无用武之地啊。

之后我会仍然使用这个示例在 k8s 集群上进行启动部署。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 部署方案
  • 容器
  • Docker 架构
  • 制作镜像
  • 多应用
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档