2019独角兽企业重金招聘Python工程师标准>>>

在本文中,我将向您展示一些基本和更高级的示例,说明如何使用Istio平台来提供部署在Kubernetes上的微服务之间的通信。按照Istio网站上的描述,它是:

一个开放的平台,用于连接,管理和保护微服务。Istio提供了一种通过负载平衡,服务到服务身份验证,监控等创建已部署服务网络的简便方法,无需更改服务代码。

Istio提供流量管理机制,如请求路由,发现,负载平衡,处理故障和故障注入。此外,您可以启用istio-auth,它提供RBAC(基于角色的访问控制)和相互TLS身份验证。在本文中,我们将仅讨论流量管理机制。

步骤1.在Minikube平台上安装Istio

在Kubernetes上测试Istio最舒适的方法是通过Minikube。我已经在本文中描述了如何在本地计算机上配置Minikube:  使用Kubernetes和Docker的微服务。在Minikube上安装Istio时,首先应该在启动时启用一些Minikube的插件。

1

minikube start --extra-config=controller-manager.ClusterSigningCertFile="/var/lib/localkube/certs/ca.crt" --extra-config=controller-manager.ClusterSigningKeyFile="/var/lib/localkube/certs/ca.key" --extra-config=apiserver.Admission.PluginNames=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota

Istio安装在名为的专用命名空间中istio-system,但能够管理来自所有其他命名空间的服务。首先,您应该去发布页面并下载与您的操作系统相对应的安装文件。对我而言,它是Windows,所有后续步骤都将在假设我们正在使用此操作系统的情况下进行描述。在运行Minikube之后,在Minikube的VM上启用Docker会很有用。多亏了你,你将能够执行docker命令。

1

@FOR /f "tokens=* delims=^L" %i IN ('minikube docker-env') DO @call %i

现在,将Istio文件提取到本地文件系统。文件istioctl.exe,这是在现有${ISTIO_HOME}/bin的目录应该被添加到您的PATH。Istio包含一些Kubernetes平台的安装文件  ${ISTIO_HOME}/install/kubernetes。要在Minikube上安装Istio的核心组件,只需应用以下YAML定义文件。

1

kubectl apply -f install/kubernetes/istio.yaml

现在,您已在Minikube实例上部署了Istio的核心组件。这些组件是:

Envoy - 它是一个开源边缘和服务代理,专为云原生应用程序而设计。Istio使用Envoy代理的扩展版本。如果您对Envoy和微服务的一些细节感兴趣,请阅读我的文章  Envoy Proxy with Microservices,它描述了如何将Envoy网关与服务发现集成。

混合器 - 它是一个独立于平台的组件,负责跨服务网格实施访问控制和使用策略。

Pilot - 它为Envoy边车提供服务发现,为智能路由和弹性提供流量管理功能。

istio.yaml定义文件中提供的配置部署了与上述组件相关的一些pod和服务。您可以使用kubectl命令验证安装,也可以在执行命令后访问Web Dashboard minikube dashboard

步骤2.基于Spring Boot构建示例应用程序

在我们开始使用Istio配置任何流量规则之前,我们需要创建将相互通信的示例应用程序。这些都是非常简单的服务。这些应用程序的源代码可以在我的GitHub帐户中的repository sample-istio-services中找到。有两种服务:caller-servicecallme-service。它们都暴露了端点ping,它打印了应用程序的名称和版本。这两个值都取自Spring Boot build-info文件,该文件是在应用程序构建期间生成的。这是端点的实现GET /callme/ping

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

@RestController

@RequestMapping("/callme")

public class CallmeController {

    private static final Logger LOGGER = LoggerFactory.getLogger(CallmeController.class);

    @Autowired

    BuildProperties buildProperties;

    @GetMapping("/ping")

    public String ping() {

        LOGGER.info("Ping: name={}, version={}", buildProperties.getName(), buildProperties.getVersion());

        return buildProperties.getName() + ":" + buildProperties.getVersion();

    }

}

这里是端点的实现GET /caller/ping。它GET /callme/ping使用Spring 调用端点RestTemplate。我们假设callme-service可以在callme-service:8091Kubernetes的地址下找到。此服务将在端口8091下的Minikube节点内公开。

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@RestController

@RequestMapping("/caller")

public class CallerController {

    private static final Logger LOGGER = LoggerFactory.getLogger(CallerController.class);

    @Autowired

    BuildProperties buildProperties;

    @Autowired

    RestTemplate restTemplate;

    @GetMapping("/ping")

    public String ping() {

        LOGGER.info("Ping: name={}, version={}", buildProperties.getName(), buildProperties.getVersion());

        String response = restTemplate.getForObject("http://callme-service:8091/callme/ping", String.class);

        LOGGER.info("Calling: response={}", response);

        return buildProperties.getName() + ":" + buildProperties.getVersion() + ". Calling... " + response;

    }

}

必须在Docker容器上启动示例应用程序。这是Dockerfile,负责使用caller-service应用程序构建映像。

1

2

3

4

6

7

8

FROM openjdk:8-jre-alpine

ENV APP_FILE caller-service-1.0.0-SNAPSHOT.jar

ENV APP_HOME /usr/app

EXPOSE 8090

COPY target/$APP_FILE $APP_HOME/

WORKDIR $APP_HOME

ENTRYPOINT ["sh", "-c"]

CMD ["exec java -jar $APP_FILE"]

类似Dockerfile的可用callme-service。现在,我们唯一需要的是构建Docker镜像。

1

2

docker build -t piomin/callme-service:1.0 .

docker build -t piomin/caller-service:1.0 .

还有一个版本2.0.0-SNAPSHOTcallme-service可用分支v2。切换到此分支,构建整个应用程序,然后使用2.0标记构建docker镜像。为什么我们需要2.0版本?我将在下一节中对其进行描述。

1

docker build -t piomin/callme-service:2.0 .

步骤3.在Minikube上部署示例应用程序

在我们开始在Minikube上部署应用程序之前,让我们看看下图中可见的示例系统架构。我们将部署callme-service两个版本:1.02.0。应用程序caller-service只是调用callme-service,所以我对目标服务的不同版本一无所知。如果我们想callme-service在20%到80%的两个版本之间路由流量,我们必须配置正确的Istio路由器。还有一件事。由于Minikube不支持Istio Ingress,我们将只提供Kubernetes服务。如果我们需要在Minikube集群之外公开它,我们应该将类型设置为  NodePort

让我们进入部署阶段。这是callme-service版本中的部署定义1.0

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

三十

31

32

apiVersion: v1

kind: Service

metadata:

  name: callme-service

  labels:

    app: callme-service

spec:

  type: NodePort

  ports:

  - port: 8091

    name: http

  selector:

    app: callme-service

---

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

  name: callme-service

spec:

  replicas: 1

  template:

    metadata:

      labels:

        app: callme-service

        version: v1

    spec:

      containers:

      - name: callme-service

        image: piomin/callme-service:1.0

        imagePullPolicy: IfNotPresent

        ports:

        - containerPort: 8091

在Minikube上部署它之前,我们必须注入一些Istio属性。下面显示的命令打印了一个富含Istio配置的新版本的部署定义。我们可以复制它并保存为deployment-with-istio.yaml文件。

1

istioctl kube-inject -f deployment.yaml

现在,让我们将配置应用于Kubernetes。

1

kubectl apply -f deployment-with-istio.yaml

同样的步骤应进行caller-service换版,并且还2.0callme-service。所有YAML配置文件都与应用程序一起提交,并且位于每个应用程序模块的根目录中。如果您已成功部署了所有必需的组件,则应在Minikube的仪表板中看到以下元素。

步骤4.应用Istio路由规则

Istio提供了一种简单的特定于域的语言(DSL),允许您配置一些有趣的规则来控制请求在服务网格中的路由方式。我将向您展示以下规则:

  • 拆分不同服务版本之间的流量
  • 在请求路径中注入延迟
  • 注入HTTP错误作为服务的响应

以下是示例路由规则定义callme-service。它在服务的版本1.0和2.0之间以20:80的比例分割流量。它还在10%的请求中增加了3秒的延迟,并为10%的请求返回HTTP 500错误代码。

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

  name: callme-service

spec:

  destination:

    name: callme-service

  route:

  - labels:

      version: v1

    weight: 20

  - labels:

      version: v2

    weight: 80

  httpFault:

    delay:

      percent: 10

      fixedDelay: 3s

    abort:

      percent: 10

      httpStatus: 500

让我们为Kubernetes应用新的路线规则。

1

kubectl apply -f routerule.yaml

现在,我们可以通过执行命令轻松验证该规则  istioctl get routerule

步骤5.测试解决方案

在我们开始测试之前,让我们在Minikube上部署Zipkin。Istio zipkin.yaml在目录中提供部署定义文件${ISTIO_HOME}/install/kubernetes/addons

1

kubectl apply -f zipkin.yaml

我们来看看Minikube上部署的服务列表。应用程序调用者服务提供的API在端口30873下  可用

我们可以通过调用URL http://192.168.99.100:30873/caller/ping轻松测试Web浏览器的服务  。它打印服务的名称和版本,以及调用者服务调用的callme-service的名称和版本。由于80%的流量路由到2.0版的callme-service,您可能会看到以下响应。

但是,有时可能会调用1.0版的callme-service ...

在本文中,我将向您展示一些基本和更高级的示例,说明如何使用Istio平台来提供部署在Kubernetes上的微服务之间的通信。按照Istio网站上的描述,它是:

一个开放的平台,用于连接,管理和保护微服务。Istio提供了一种通过负载平衡,服务到服务身份验证,监控等创建已部署服务网络的简便方法,无需更改服务代码。

Istio提供流量管理机制,如请求路由,发现,负载平衡,处理故障和故障注入。此外,您可以启用istio-auth,它提供RBAC(基于角色的访问控制)和相互TLS身份验证。在本文中,我们将仅讨论流量管理机制。

步骤1.在Minikube平台上安装Istio

在Kubernetes上测试Istio最舒适的方法是通过Minikube。我已经在本文中描述了如何在本地计算机上配置Minikube:  使用Kubernetes和Docker的微服务。在Minikube上安装Istio时,首先应该在启动时启用一些Minikube的插件。

1

minikube start --extra-config=controller-manager.ClusterSigningCertFile="/var/lib/localkube/certs/ca.crt" --extra-config=controller-manager.ClusterSigningKeyFile="/var/lib/localkube/certs/ca.key" --extra-config=apiserver.Admission.PluginNames=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota

Istio安装在名为的专用命名空间中istio-system,但能够管理来自所有其他命名空间的服务。首先,您应该去发布页面并下载与您的操作系统相对应的安装文件。对我而言,它是Windows,所有后续步骤都将在假设我们正在使用此操作系统的情况下进行描述。在运行Minikube之后,在Minikube的VM上启用Docker会很有用。多亏了你,你将能够执行docker命令。

1

@FOR /f "tokens=* delims=^L" %i IN ('minikube docker-env') DO @call %i

现在,将Istio文件提取到本地文件系统。文件istioctl.exe,这是在现有${ISTIO_HOME}/bin的目录应该被添加到您的PATH。Istio包含一些Kubernetes平台的安装文件  ${ISTIO_HOME}/install/kubernetes。要在Minikube上安装Istio的核心组件,只需应用以下YAML定义文件。

1

kubectl apply -f install/kubernetes/istio.yaml

现在,您已在Minikube实例上部署了Istio的核心组件。这些组件是:

Envoy - 它是一个开源边缘和服务代理,专为云原生应用程序而设计。Istio使用Envoy代理的扩展版本。如果您对Envoy和微服务的一些细节感兴趣,请阅读我的文章  Envoy Proxy with Microservices,它描述了如何将Envoy网关与服务发现集成。

混合器 - 它是一个独立于平台的组件,负责跨服务网格实施访问控制和使用策略。

Pilot - 它为Envoy边车提供服务发现,为智能路由和弹性提供流量管理功能。

istio.yaml定义文件中提供的配置部署了与上述组件相关的一些pod和服务。您可以使用kubectl命令验证安装,也可以在执行命令后访问Web Dashboard minikube dashboard

步骤2.基于Spring Boot构建示例应用程序

在我们开始使用Istio配置任何流量规则之前,我们需要创建将相互通信的示例应用程序。这些都是非常简单的服务。这些应用程序的源代码可以在我的GitHub帐户中的repository sample-istio-services中找到。有两种服务:caller-servicecallme-service。它们都暴露了端点ping,它打印了应用程序的名称和版本。这两个值都取自Spring Boot build-info文件,该文件是在应用程序构建期间生成的。这是端点的实现GET /callme/ping

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

@RestController

@RequestMapping("/callme")

public class CallmeController {

    private static final Logger LOGGER = LoggerFactory.getLogger(CallmeController.class);

    @Autowired

    BuildProperties buildProperties;

    @GetMapping("/ping")

    public String ping() {

        LOGGER.info("Ping: name={}, version={}", buildProperties.getName(), buildProperties.getVersion());

        return buildProperties.getName() + ":" + buildProperties.getVersion();

    }

}

这里是端点的实现GET /caller/ping。它GET /callme/ping使用Spring 调用端点RestTemplate。我们假设callme-service可以在callme-service:8091Kubernetes的地址下找到。此服务将在端口8091下的Minikube节点内公开。

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@RestController

@RequestMapping("/caller")

public class CallerController {

    private static final Logger LOGGER = LoggerFactory.getLogger(CallerController.class);

    @Autowired

    BuildProperties buildProperties;

    @Autowired

    RestTemplate restTemplate;

    @GetMapping("/ping")

    public String ping() {

        LOGGER.info("Ping: name={}, version={}", buildProperties.getName(), buildProperties.getVersion());

        String response = restTemplate.getForObject("http://callme-service:8091/callme/ping", String.class);

        LOGGER.info("Calling: response={}", response);

        return buildProperties.getName() + ":" + buildProperties.getVersion() + ". Calling... " + response;

    }

}

必须在Docker容器上启动示例应用程序。这是Dockerfile,负责使用caller-service应用程序构建映像。

1

2

3

4

6

7

8

FROM openjdk:8-jre-alpine

ENV APP_FILE caller-service-1.0.0-SNAPSHOT.jar

ENV APP_HOME /usr/app

EXPOSE 8090

COPY target/$APP_FILE $APP_HOME/

WORKDIR $APP_HOME

ENTRYPOINT ["sh", "-c"]

CMD ["exec java -jar $APP_FILE"]

类似Dockerfile的可用callme-service。现在,我们唯一需要的是构建Docker镜像。

1

2

docker build -t piomin/callme-service:1.0 .

docker build -t piomin/caller-service:1.0 .

还有一个版本2.0.0-SNAPSHOTcallme-service可用分支v2。切换到此分支,构建整个应用程序,然后使用2.0标记构建docker镜像。为什么我们需要2.0版本?我将在下一节中对其进行描述。

1

docker build -t piomin/callme-service:2.0 .

步骤3.在Minikube上部署示例应用程序

在我们开始在Minikube上部署应用程序之前,让我们看看下图中可见的示例系统架构。我们将部署callme-service两个版本:1.02.0。应用程序caller-service只是调用callme-service,所以我对目标服务的不同版本一无所知。如果我们想callme-service在20%到80%的两个版本之间路由流量,我们必须配置正确的Istio路由器。还有一件事。由于Minikube不支持Istio Ingress,我们将只提供Kubernetes服务。如果我们需要在Minikube集群之外公开它,我们应该将类型设置为  NodePort

让我们进入部署阶段。这是callme-service版本中的部署定义1.0

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

三十

31

32

apiVersion: v1

kind: Service

metadata:

  name: callme-service

  labels:

    app: callme-service

spec:

  type: NodePort

  ports:

  - port: 8091

    name: http

  selector:

    app: callme-service

---

apiVersion: extensions/v1beta1

kind: Deployment

metadata:

  name: callme-service

spec:

  replicas: 1

  template:

    metadata:

      labels:

        app: callme-service

        version: v1

    spec:

      containers:

      - name: callme-service

        image: piomin/callme-service:1.0

        imagePullPolicy: IfNotPresent

        ports:

        - containerPort: 8091

在Minikube上部署它之前,我们必须注入一些Istio属性。下面显示的命令打印了一个富含Istio配置的新版本的部署定义。我们可以复制它并保存为deployment-with-istio.yaml文件。

1

istioctl kube-inject -f deployment.yaml

现在,让我们将配置应用于Kubernetes。

1

kubectl apply -f deployment-with-istio.yaml

同样的步骤应进行caller-service换版,并且还2.0callme-service。所有YAML配置文件都与应用程序一起提交,并且位于每个应用程序模块的根目录中。如果您已成功部署了所有必需的组件,则应在Minikube的仪表板中看到以下元素。

步骤4.应用Istio路由规则

Istio提供了一种简单的特定于域的语言(DSL),允许您配置一些有趣的规则来控制请求在服务网格中的路由方式。我将向您展示以下规则:

  • 拆分不同服务版本之间的流量
  • 在请求路径中注入延迟
  • 注入HTTP错误作为服务的响应

以下是示例路由规则定义callme-service。它在服务的版本1.0和2.0之间以20:80的比例分割流量。它还在10%的请求中增加了3秒的延迟,并为10%的请求返回HTTP 500错误代码。

1

2

3

4

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

apiVersion: config.istio.io/v1alpha2

kind: RouteRule

metadata:

  name: callme-service

spec:

  destination:

    name: callme-service

  route:

  - labels:

      version: v1

    weight: 20

  - labels:

      version: v2

    weight: 80

  httpFault:

    delay:

      percent: 10

      fixedDelay: 3s

    abort:

      percent: 10

      httpStatus: 500

让我们为Kubernetes应用新的路线规则。

1

kubectl apply -f routerule.yaml

现在,我们可以通过执行命令轻松验证该规则  istioctl get routerule

步骤5.测试解决方案

在我们开始测试之前,让我们在Minikube上部署Zipkin。Istio zipkin.yaml在目录中提供部署定义文件${ISTIO_HOME}/install/kubernetes/addons

1

kubectl apply -f zipkin.yaml

我们来看看Minikube上部署的服务列表。应用程序调用者服务提供的API在端口30873下  可用

我们可以通过调用URL http://192.168.99.100:30873/caller/ping轻松测试Web浏览器的服务  。它打印服务的名称和版本,以及调用者服务调用的callme-service的名称和版本。由于80%的流量路由到2.0版的callme-service,您可能会看到以下响应。

但是,有时可能会调用1.0版的callme-service ...

...或Istio可以模拟HTTP 500代码。

您可以使用Zipkin控制台轻松分析流量统计信息。

或者只是看看pod生成的日志。

...或Istio可以模拟HTTP 500代码。

您可以使用Zipkin控制台轻松分析流量统计信息。

或者只是看看pod生成的日志。

https://github.com/xiaomin0322/sample-istio-services

转载于:https://my.oschina.net/xiaominmin/blog/1851984

服务网与Kubernetes上的Istio分5步相关推荐

  1. Kubernetes 上的服务网格技术大比较: Istio, Linkerd 和 Consul

    云原生应用通常是由一组运行在容器中的分布式微服务架构起来的.目前越来越多的容器应用都是基于 Kubernetes 的,Kubernetes 已经成为了容器编排的事实标准. 大多数采用了微服务架构的公司 ...

  2. SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可灰度)

    作者 | 白寂  阿里云开发工程师 导读:前三篇文章我们介绍了应用的开发和部署,那么在应用成功上云后,我就要面对应用的管理话题了,这一篇我们来看看如何做线上发布,并且是可灰度的. 相关文章推荐: &l ...

  3. Istio最佳实践:在K8s上通过Istio服务网格进行灰度发布

    Istio是什么? Istio是Google继Kubernetes之后的又一开源力作,主要参与的公司包括Google,IBM,Lyft等公司.它提供了完整的非侵入式的微服务治理解决方案,包含微服务的管 ...

  4. knative_使用knative在kubernetes上实现无服务器

    knative If you're already using Kubernetes, you've probably heard about serverless. While both platf ...

  5. Kubernetes上领先的开源Serverless解决方案有哪些

    在去年年底的一次YC Startup School采访中,YC软件工程师Kyle Corbitt,询问了亚马逊的首席技术官Werner Vogels,关于容器和Kubernetes的问题.Werner ...

  6. Spring Cloud 应用在 Kubernetes 上的最佳实践 — 高可用(混沌工程)

    作者 | 穹谷 导读:从上篇开始,我们进入到了高可用的章节,上篇提到的熔断能力,是历年保障大促当天晚上整个系统不被洪峰流量打垮的法宝.本文将重点介绍为什么我们要做混沌工程以及如何使用 ChaoBlad ...

  7. SpringCloud 应用在 Kubernetes 上的最佳实践 — 高可用(熔断)

    作者 | 宿何 导读:前几篇我们主要站在应用发布的场景,描述在发布过程中会遇到的灰度.监控.回滚.优雅上下线等保障发布能顺利进行的注意事项.作为一个程序员 GG,可灰度的发布顺利上线往往意味着准点下班 ...

  8. 在阿里云Kubernetes上运行SpringCloud示例PiggyMetrics

    阿里云Kubernetes服务运行SpringCloud osswangxining大侠在 阿里云Kubernetes SpringCloud 实践进行时 系列文章中系统地介绍了如何在阿里云Kuber ...

  9. 如何在 Kubernetes 上配置 Jenkins?

    作者 | Sudip Sengupta 译者 | 火火酱,责编 | Carol 封图 | CSDN 下载自视觉中国 在本文中,我们将一起完成在Kubernetes上配置Jenkins的工作.作为一款被 ...

  10. 从头开始搭建kubernetes集群+istio服务网格(3)—— 搭建istio

    (win10 + virtualbox6.0 + centos7.6.1810 + docker18.09.8 + kubernetes1.15.1 + istio1.2.3) 本文参考网址: htt ...

最新文章

  1. html多重边框,中间空白,【基础】CSS实现多重边框的5种方式
  2. 代码注释规范-google版本
  3. RaySync 传输协议的有效带宽利用率分析介绍
  4. 转载 - 整数划分问题
  5. 数据结构-栈2-链式存储
  6. 菜鸟学习笔记:Java提升篇12(Java动态性2——动态编译、javassist字节码操作)
  7. .NET中的异步编程——常见的错误和最佳实践
  8. Mysql多表关联删除操作
  9. Looksery Cup 2015 B. Looksery Party 暴力
  10. 七月算法--12月机器学习在线班-第九次课笔记—推荐系统
  11. 高项_第三章项目立项管理
  12. 人工智能主要应用的七大领域
  13. AutoRun类型分析
  14. Cartesian coordinate system
  15. 与其他带货直播平台相比,视频号更适合哪些人?
  16. PTA 选择结构 7-1 能买手机吗?
  17. 【信息系统项目管理师】第十五章 知识产权与法律法规(考点汇总篇)
  18. 基于管道过滤器实现的kwic实现
  19. Flutter 官方做了一款游戏,开源的
  20. Python自动抢购脚本,学废了双十一双十二帮女票抢购心爱的礼物,隔壁女孩都馋哭了。

热门文章

  1. 判断一个字符串(str)是否以指定的字符串(target)结尾。 如果是,返回true;如果不是,返回false。...
  2. 关于gcc的一点小人性化提示
  3. iOS开发日记19-7.0之后的截屏方法
  4. MasterPage简介
  5. 验证视图MAC失败 解决办法
  6. 贝叶斯定理决策规则及Bayes思想总结
  7. Java Android客户端开发
  8. 延时摄影制作软件——GlueMotion for Mac支持m1
  9. CentOS 7.6 安装 nginx,配置端口访问网站,切换root目录
  10. Mac 连不上华为 p9 处理历程(一)