构建微服务实验环境(一):容器与应用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pmlpml/article/details/71083907

构建微服务实验环境(一):容器与应用

【摘要】微服务架构目前最流行的方案(最佳实践)是容器集群。AWS 的 ECS、docker 的 Swarm、google 的 Kubernetes(K8s),apache 的 mesos,包括 Rancher Labs 的 Rancher 。 建议使用 AWS 或 阿里云 等共有云部署,会有更多技术支持,让你体验不同方案的优势。这里仅为了让你更好理解容器集群工作原理,介绍在 Centos 集群上部署 Swarm、K8s、 Rancher 的方案。

特别提示 无论 swarm,k8s 甚至 AWS 的 ECS 都是快速进化的软件,每个月都会发生一些变化。

(1)容器与应用: 讲述 centos docker的安装,仓库、镜像、容器、服务的概念, dockerfile,compose 文件,容器管理图形界面,以单机操作为主线。
(2)Docker Swarm 集群:讲述集群(cluster)manager,worker,node 的概念与应用在集群部署。重点讲述容器网络、存储管理、集群管理、服务发现等知识。
(3)Rancher 管理平台:讲述多租户多主机容器运行管理平台 Rancher 的租户、环境、主机、环境模板概念与应用。包括 Rancher 自带集群管理 Cattle 的栈、服务的管理,需要读者对比与Docker Swarm 集群在容器基础服务、管理方式方面的差异

1、环境与网络

准备三台 Centos 的 物理机或虚拟机,虚拟机可使用 VMware 或 Vbox,本文案例主机采用 Window10,虚拟化软件 Vbox。

虚拟机应采用双网卡配置:

  • 第一块网卡配 NAT,
  • 第二块网卡按(Host-Only)配内部私有网络。

配置参考: docker 集群网络规划与 VM 网络配置

2、安装 CentOS 与 Docker 引擎

2.1 安装 CentOS 基础环境

1.安装CentOS服务器的模板

双网卡网络配置: NAT,192.168.56.250/24

具体过程:安装基础-centos-7-服务器

2.安装docker-master虚拟机

使用 VBox 复制上述服务器。使用 nmtui 配置:

  • 第二网卡,例如:192.168.56.110/24
  • 主机名:docker-master

2.2 安装 Docker 引擎

1. 在阿里云注册账号

进入阿里云,dev.aliyun.com

登陆后,进入【管理中心】

2. 安装 docker 引擎

如果是安装最新版本 docker:

  • 【管理中心】 –> Docker镜像仓库 –> 加速器 –> centos
  • 按提示操作

如果需要安装 docker 历史版本:

3. 启动 docker 引擎

systemctl start docker
systemctl enable docker
systemctl status docker

docker version
docker run --rm hello-world

4. 配置容器加速服务

  • 【管理中心】 –> Docker镜像仓库 –> 加速器 –> centos

5. 建立你的镜像仓库

  • 【管理中心】 –> Docker镜像仓库 –> 镜像列表 –> 华南1 –> 【创建镜像创库】

创建公有仓库,(一个仓库一般就存一个镜像的不同版本)。完成后,看到【管理】按钮,进入

仓库使用操作: 如上传 hello-world 镜像

  • 列表 docker images
  • 登陆 docker login --username=<your account> registry.cn-shenzhen.aliyuncs.com
  • tag docker tag hello-world registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:hello-world
  • 上传 docker push registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:hello-world
  • 下载 docker push registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:hello-world
  • tag docker tag registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:hello-world hello-world
  • 删除 docker rmi registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:hello-world
  • 运行 docker run --rm hello-world
  • 退出 docker logout registry.cn-shenzhen.aliyuncs.com

3、Docker 入门

docker 官网文档 Get Stated 给出了 6 个基础教程,Part1-6。 Part I 安装,请参考前面

入门要求 docker 1.13 版本以上,当前版本 17.04.00 ce

3.1 核心概念

Docker引擎 (Docker Engine)

Docker Engine is a client-server application with these major components(一个客户-服务应用,含三个部件)

1)驻守(daemon)进程 docker

2) REST API 接口提供外部系统与 docker 进程交互

3) Cli 命令行接口的应用 docker 命令

基础架构
图:Docker引擎基础架构

镜像(image)

An image is a read-only template with instructions for creating a Docker container.

镜像是创建docker容器指令的只读模板。包含一个只读的文件系统镜像,应用程序在这个文件系统上运行,并拥有属于自己的网络、内存、CPU 等资源的定义。通常,一个镜像申明了自己主进程的应用。

镜像一般在其他镜像基础上建立,如在 ubuntu 上添加自己的 apache 文件,然后在 apache 上建立 web 应用文件

容器(Container)

A container is a runnable instance of an image.

镜像中的程序(主进程)运行在只读文件镜像上,对现有文件变更将写在一个临时的卷上(新的一层),该主进程、相关进程、文件系统成为一个运行的实例。

  • 每个容器创建后,有运行、暂停、中止状态,直到移除。
  • 程序运行的产生的数据,都在容器中的文件中,Restart 不会丢失数据。

当主进程结束时,该实例同时结束运行。使用 –rm 表示运行后自动删除容器。

服务(Services)

Services allow you to scale containers across multiple Docker daemons, which all work together as a swarm with multiple nodes.

服务是一组运行于集群不同结点中可复制的容器的集合。通过服务发现机制,使用该服务的客户端可以透明的访问这些容器。这些容器的分布、升级、故障管理有集群统一管理。

一般地,集群内部服务容器地选择由 动态DNS 实现。

栈(stack)

A stack is a group of interrelated services that share dependencies, and can be orchestrated and scaled together.

能被编排伸缩的一组相互依赖的服务

栈(stack)用 docker-compose.yml 文件描述服务之间的依赖、数据共享、网络等。使用 docker stack 管理

3.2 简单教程

3.2.1 Part 2:镜像与容器(官方)

https://docs.docker.com/get-started/part2/

1)创建镜像

(1) 创建一个目录,如 webservice1, 然后,进入该目录

mkdir webservice1 && cd webservice1 

(2) 创建一个简单 web 应用程序

使用 vi app.py 创建 web 应用代码:

from flask import Flask
from redis import Redis, RedisError
import os
import socket

# Connect to Redis
redis = Redis(host="redis", db=0)

app = Flask(__name__)

@app.route("/")
def hello():
    try:
        visits = redis.incr('counter')
    except RedisError:
        visits = "<i>cannot connect to Redis, counter disabled</i>"

    html = "<h3>Hello {name}!</h3>" \
           "<b>Hostname:</b> {hostname}<br/>" \
           "<b>Visits:</b> {visits}"
    return html.format(name=os.getenv('NAME', "world"), hostname=socket.gethostname(), visits=visits)

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=80)

写简单 web 应用, python 最方便。 Node.js 也很好! J2EE 就是蹒跚的大象啊。

(3) 创建镜像指令文件 dockerfile

使用 vi dockerfile 创建文件

# Use an official Python runtime as a base image
FROM python:2.7-slim

# Set the working directory to /app
WORKDIR /app

# Copy the current directory contents into the container at /app
ADD . /app

# Install any needed packages specified in requirements.txt
RUN pip install -r requirements.txt

# Make port 80 available to the world outside this container
EXPOSE 80

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "app.py"]

指令非常直观,

  • FROM 指示从 python:2.7-slim 镜像基础上开始构建。这个镜像在哪里,docker hub 的仓库。 镜像命名规则 [镜像:版本]
  • WORKDIR 设置主进程的工作目录 /app
  • ADD 将当前目录内容复制到基础镜像文件系统的 /app
  • RUN 在基础镜像文件系统上运行 pip install -r requirements.txt 这时 pip 下载依赖包到基础镜像之上
  • EXPOSE 暴露容器对外的 TCP 端口 80
  • ENV 定义进程的环境变量 NAME World
  • CMD 设置主进程启动的命令 ["python", "app.py"] 。因此,容器一般是单进程应用

最后编辑 python 的依赖包 vi requirements.txt

Flask
Redis

(4)构建 app 的镜像

检查文件:

# ls
app.py  dockerfile  requirements.txt

创建 tag 为 friendlyhello 的镜像:

# docker build -t friendlyhello .

完成之后,检查镜像文件:

docker images

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398

2)在容器中运行 app

docker run -d -p 4000:80 friendlyhello
  • docker run 运行镜像的命令
  • -d 后台运行
  • -p 端口映射,将容器的 80 端口映射到主机(host)的 4000 端口

检查结果:

# curl localhost:4000

<h3>Hello World!</h3><b>Hostname:</b> 82523c7dcf52<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled

检查容器的进程:

# docker ps

CONTAINER ID        IMAGE                 COMMAND             CREATED             STATUS              PORTS                    NAMES
82523c7dcf52        friendlyhello         "python app.py"     2 minutes ago       Up 2 minutes        0.0.0.0:4000->80/tcp     sad_wescoff

结束并清理进程:

# docker rm -f 82523c7dcf52

3.2.2 上传镜像到你的仓库

docker push registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello ?

3.2.3 使用图形化管理工具

几乎所有容器开发商都有自己的 UI 管理界面。这些 UI 共同的特点就是就是本身以一个容器的方式存在,例如:PortainerShipyard

以为例,启动 Portainer 非常简单:

docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer

这里最关键的就是 -v /var/run/docker.sock:/var/run/docker.sock 容器与主机共享了文件 /var/run/docker.sock 从而,容器获得了 docker 驻守(daemon)进程 REST API SDK 入口。

现在,在浏览器输入 http://<your machine IP>:9000 就进入了图形管理界面:

portainer
图来源:portainer on GitHub

一些问题必须指出:

还需要学习 docker 命令吗?

需要!!!且必要。

一些图形界面仅是 docker REST API 的简单包装,熟悉 docker Cli 有利于帮助你理解 docker 技术

一些常用的 Docker 命令:一张图读懂 docker 命令

选择哪个平台作为 docker 管理集成的起点

看你喜欢哪种语言:

  • portainer 采用 node.js 语言
  • shipyard 采用 go 语言

更多 dockerfile 知识: https://docs.docker.com/engine/reference/builder/

跟多 docker CLI 知识: https://docs.docker.com/engine/reference/commandline/cli/

3.2.4 Part 3:服务(官方)

A service really just means, “containers in production.” A service only runs one image, but it codifies the way that image runs – what ports it should use, how many replicas of the container should run so the service has the capacity it needs, and so on.

运行一个服务,需要使用 docker-compose.yml 文件。顾名思义,这是服务组合的定义。

(1)创建一个目录,例如 service_test:

cd && mkdir service_test && cd service_test 

(2)编写服务定义, 例如 vi docker-compose.yml

version: "3"
services:
  web:
    image: <username/repository:tag>
    deploy:
      replicas: 5
      resources:
        limits:
          cpus: "0.1"
          memory: 50M
      restart_policy:
        condition: on-failure
    ports:
      - "80:80"
    networks:
      - webnet
networks:
  webnet:

上述 yaml 描述的对象也非常清晰:

  • version = “3”;不能修改
  • services
    • web 服务名称
      • 镜像: 请使用你仓库中的镜像
      • 部署:申明使用 5 个容器实例,每个资源,重新启动条件
      • 端口映射:80:80
      • 网络: webnet
  • networks
    • webnet: 定义这个应用使用的 overlay 网络

(3) 启动服务

在启动服务之前,务必初始化集群模式 。注意:多网卡必须说明在那块网卡上组建集群。

docker swarm init --advertise-addr 192.168.56.110

检查是否在集群模式的命令:

# docker info

Containers: 1
 Running: 1
...
Swarm: active
 NodeID: 23iftsrtbxm4kyyh2w5n3fmat
...

启动应用程序服务

docker stack deploy -c docker-compose.yml myservice

检查部署的结果:

# docker stack ps myservice

ID                  NAME                IMAGE                                                         NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
gzg8n8oqpic8        myservice_web.1     registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello   docker-master       Running             Running 4 seconds ago
xqyqekt9dubc        myservice_web.2     registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello   docker-master       Running             Running 4 seconds ago
owvdk5hypg7h        myservice_web.3     registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello   docker-master       Running             Running 4 seconds ago
0vi2cwingnyw        myservice_web.4     registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello   docker-master       Running             Running 3 seconds ago
7n2l036fv851        myservice_web.5     registry.cn-shenzhen.aliyuncs.com/pmlpml/repo:friendlyhello   docker-master       Running             Running 4 seconds ago

使用浏览器或curl 访问 http://192.168.56.110:80/ 每次返回的主机名都是不一样的,负载均衡实现了。

使用 portainer 观察主机(host)中的变化:

  • Services: 添加了一个服务 myservice_web
  • Containers: 添加了 5 个服务 myservice_web.?.<long_id>
  • Networks:添加了 2 个 swarm overlay 的网络
  • Swarm:有一个管理结点

使用 portainer 的 Containers 杀死 myservice_web 服务的实例,会发现它会自动启动。

(4) 结束服务

docker stack rm myservice

4、小结

本文给出了在 centos 环境下,完成 docker 官方 part 2,part 3 的教程的要点。由于网络等因素,建议大家使用阿里云或其他 docker 容器服务商提供的安装、容器仓库与加速设施。尽管技术不断发展,无论命令还是定义格式都会发生一些变化,docker 容器的核心内容与操作基本没有大变化。适当使用图形界面,可以更加快速观察系统内部的变化。

本文围绕镜像、容器、服务三个核心知识以及相关的操作,读者应可以从容安装 docker 引擎,并在单机上发布与部署简单的 web 应用。

没有更多推荐了,返回首页