deployment

我们已经知道k8s是通过各种controller来管理pod的生命周期。为了满足不同业务场景,k8s开发了Deployment、ReplicaSet、DaemonSet、StatefuleSet、Job 等多种 Controller。我们首先学习最常用的 Deployment。

运行一个deployment

  1. [root@ken ~]# kubectl run httpd-ken1--generator=run-pod/v1 --image=httpd --replicas=

下面详细分析 Kubernetes 都做了些什么工作。

  1. [root@ken ~]# kubectl get deployment
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. httpd-ken / 35m

kubectl get deplouyment命令可以查看 httpd-ken 的状态,输出显示两个副本正常运行。

接下来我们用 kubectl describe deployment 了解更详细的信息。

  1. [root@ken ~]# kubectl describe deployment httpd-ken
  2. Name: httpd-ken
  3. Namespace: default
  4. CreationTimestamp: Tue, Jan :: +
  5. Labels: run=httpd-ken
  6. Annotations: deployment.kubernetes.io/revision:
  7. Selector: run=httpd-ken
  8. Replicas: desired | updated | total | available | unavailable
  9. StrategyType: RollingUpdate
  10. MinReadySeconds:
  11. RollingUpdateStrategy: % max unavailable, % max surge
  12. Pod Template:
  13. Labels: run=httpd-ken
  14. Containers:
  15. httpd-ken:
  16. Image: httpd
  17. Port: <none>
  18. Host Port: <none>
  19. Environment: <none>
  20. Mounts: <none>
  21. Volumes: <none>
  22. Conditions:
  23. Type Status Reason
  24. ---- ------ ------
  25. Available True MinimumReplicasAvailable
  26. Progressing True NewReplicaSetAvailable
  27. OldReplicaSets: <none>
  28. NewReplicaSet: httpd-ken-5c949b96f (/ replicas created)
  29. Events:
  30. Type Reason Age From Message
  31. ---- ------ ---- ---- -------
  32. Normal ScalingReplicaSet 18m deployment-controller Scaled up replica set httpd-ken-5c949b96f to

大部分内容都是自解释的,我们重点看最下面部分。这里告诉我们创建了一个 ReplicaSet httpd-ken-5c949b96,Events 是 Deployment 的日志,记录了 ReplicaSet 的启动过程。

通过上面的分析,也验证了 Deployment 通过 ReplicaSet 来管理 Pod 的事实。接着我们将注意力切换到 httpd-ken-5c949b96,执行 kubectl describe replicaset:

  1. [root@ken ~]# kubectl get replicaset
  2. NAME DESIRED CURRENT READY AGE
  3. httpd-ken-5c949b96f 20m

两个副本已经就绪,用 kubectl describe replicaset 查看详细信息:

  1. [root@ken ~]# kubectl describe replicaset
  2. Name: httpd-ken-5c949b96f
  3. Namespace: default
  4. Selector: pod-template-hash=5c949b96f,run=httpd-ken
  5. Labels: pod-template-hash=5c949b96f
  6. run=httpd-ken
  7. Annotations: deployment.kubernetes.io/desired-replicas:
  8. deployment.kubernetes.io/max-replicas:
  9. deployment.kubernetes.io/revision:
  10. Controlled By: Deployment/httpd-ken
  11. Replicas: current / desired
  12. Pods Status: Running / Waiting / Succeeded / Failed
  13. Pod Template:
  14. Labels: pod-template-hash=5c949b96f
  15. run=httpd-ken
  16. Containers:
  17. httpd-ken:
  18. Image: httpd
  19. Port: <none>
  20. Host Port: <none>
  21. Environment: <none>
  22. Mounts: <none>
  23. Volumes: <none>
  24. Events:
  25. Type Reason Age From Message
  26. ---- ------ ---- ---- -------
  27. Normal SuccessfulCreate 20m replicaset-controller Created pod: httpd-ken-5c949b96f-twdsd
  28. Normal SuccessfulCreate 20m replicaset-controller Created pod: httpd-ken-5c949b96f-9cd52

Controlled By 指明此 ReplicaSet 是由 Deployment httpd-ken 创建。Events 记录了两个副本 Pod 的创建。接着我们来看 Pod,执行 kubectl get pod:

  1. [root@ken ~]# kubectl get pod
  2. NAME READY STATUS RESTARTS AGE
  3. httpd-ken-5c949b96f-9cd52 / Running 22m
  4. httpd-ken-5c949b96f-twdsd / Running 22m

两个副本 Pod 都处于 Running 状态,用 kubectl describe pod 查看更详细的信息:

  1. root@ken ~]# kubectl describe pod
  2. Name: httpd-ken-5c949b96f-9cd52
  3. Namespace: default
  4. Priority:
  5. PriorityClassName: <none>
  6. Node: host1/172.20.10.7
  7. Start Time: Tue, Jan :: +
  8. Labels: pod-template-hash=5c949b96f
  9. run=httpd-ken
  10. Annotations: <none>
  11. Status: Running
  12. IP: 10.244.1.3
  13. Controlled By: ReplicaSet/httpd-ken-5c949b96f
  14. Containers:
  15. httpd-ken:
  16. Container ID: docker://e59bda9941a16f20027c89a0d8fa8e17797b517f6f5461e905c0d29b57369dde
  17. Image: httpd
  18. Image ID: docker-pullable://httpd@sha256:44daa8e932a32ab6e50636d769ca9a60ad412124653707e5ed59c0209c72f9b3
  19. Port: <none>
  20. Host Port: <none>
  21. State: Running
  22. Started: Tue, Jan :: +
  23. Ready: True
  24. Restart Count:
  25. Environment: <none>
  26. Mounts:
  27. /var/run/secrets/kubernetes.io/serviceaccount from default-token-vb7lm (ro)
  28. Conditions:
  29. Type Status
  30. Initialized True
  31. Ready True
  32. ContainersReady True
  33. PodScheduled True
  34. Volumes:
  35. default-token-vb7lm:
  36. Type: Secret (a volume populated by a Secret)
  37. SecretName: default-token-vb7lm
  38. Optional: false
  39. QoS Class: BestEffort
  40. Node-Selectors: <none>
  41. Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
  42. node.kubernetes.io/unreachable:NoExecute for 300s
  43. Events:
  44. Type Reason Age From Message
  45. ---- ------ ---- ---- -------
  46. Normal Scheduled 23m default-scheduler Successfully assigned default/httpd-ken-5c949b96f-9cd52 to host1
  47. Normal Pulling 23m kubelet, host1 pulling image "httpd"
  48. Normal Pulled 22m kubelet, host1 Successfully pulled image "httpd"
  49. Normal Created 22m kubelet, host1 Created container
  50. Normal Started 22m kubelet, host1 Started container

Controlled By 指明此 Pod 是由 ReplicaSet  httpd-ken-5c949b96f创建。Events 记录了 Pod 的启动过程。如果操作失败(比如 image 不存在),也能在这里查看到原因。

总结一下这个过程:

用户通过 kubectl 创建 Deployment。

Deployment 创建 ReplicaSet。

ReplicaSet 创建 Pod

也可以看出,对象的命名方式是:子对象的名字 = 父对象名字 + 随机字符串或数字。

命令vs配置文件

k8s支持两种创建资源的方式:

1.用 kubectl 命令直接创建,比如:

kubectl run nginx-deployment --image=nginx:1.7.9 --replicas=2

在命令行中通过参数指定资源的属性。

2. 通过配置文件和 kubectl apply 创建,要完成前面同样的工作,可执行命令:

kubectl apply -f nginx.yml

nginx.yml 的内容为:

资源的属性写在配置文件中,文件格式为 YAML。

下面对这两种方式进行比较。

基于命令的方式:

简单直观快捷,上手快。

适合临时测试或实验。

基于配置文件的方式:

配置文件描述了 What,即应用最终要达到的状态。

配置文件提供了创建资源的模板,能够重复部署。

可以像管理代码一样管理部署。

适合正式的、跨环境的、规模化部署。

这种方式要求熟悉配置文件的语法,有一定难度。

后面我们都将采用配置文件的方式,大家需要尽快熟悉和掌握。

kubectl apply 不但能够创建 Kubernetes 资源,也能对资源进行更新,非常方便。不过 Kubernets 还提供了几个类似的命令,例如 kubectl create、kubectl replace、kubectl edit 和 kubectl patch。

为避免造成不必要的困扰,我们会尽量只使用 kubectl apply,

此命令已经能够应对超过 90% 的场景,事半功倍。

deployment yaml

既然要用 YAML 配置文件部署应用,现在就很有必要了解一下 Deployment 的配置格式,其他 Controller(比如 DaemonSet)非常类似。

① apiVersion 是当前配置格式的版本。

  先执行kubectl api-resources找到所有的资源  

  在执行命令 kubectl explain deploy即可获取到版本和类型信息

  

② kind 是要创建的资源类型,这里是 Deployment。

③ metadata 是该资源的元数据,name 是必需的元数据项。

④ spec 部分是该 Deployment 的规格说明。

⑤ replicas 指明副本数量,默认为 1。

⑥ template 定义 Pod 的模板,这是配置文件的重要部分。

⑦ metadata 定义 Pod 的元数据,至少要定义一个 label。label 的 key 和 value 可以任意指定。

⑧ spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需的。

此 nginx.yml 是一个最简单的 Deployment 配置文件,后面我们学习 Kubernetes 各项功能时会逐步丰富这个文件。

执行 kubectl apply -f nginx.yml:

  1. [root@ken ~]# kubectl apply -f nginx.yml
  2. deployment.extensions/nginx-deployment created

查看nginx-deployment各种资源

  1. [root@ken ~]# kubectl get deployment
  2. NAME READY UP-TO-DATE AVAILABLE AGE
  3. httpd-ken / 73m
  4. nginx-deployment / 107s
  5. [root@ken ~]# kubectl get replicaset
  6. NAME DESIRED CURRENT READY AGE
  7. httpd-ken-5c949b96f 54m
  8. nginx-deployment-65998d8886 111s
  9. [root@ken ~]# kubectl get pod -o wide
  10. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  11. httpd-ken-5c949b96f-9cd52 / Running 54m 10.244.1.3 host1 <none> <none>
  12. httpd-ken-5c949b96f-twdsd / Running 54m 10.244.2.3 host2 <none> <none>
  13. nginx-deployment-65998d8886-9qrrv / Running 2m4s 10.244.2.4 host2 <none> <none>
  14. nginx-deployment-65998d8886-vnbgt / Running 2m4s 10.244.1.4 host1 <none> <none>

Deployment、ReplicaSet、Pod 都已经就绪。如果要删除这些资源,执行 kubectl delete deployment nginx-deployment 或者 kubectl delete -f nginx.yml (编写的nginx.yml文件不会被删除)。

  1. [root@ken ~]# kubectl delete -f nginx.yml
  2. deployment.extensions "nginx-deployment" deleted

Scale Up/Down

伸缩(Scale Up/Down)是指在线增加或减少 Pod 的副本数。

Deployment nginx-deployment 初始是两个副本。

  1. [root@ken ~]# kubectl apply -f nginx.yml
  2. [root@ken ~]# kubectl get pod -o wide
  3. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  4. nginx-deployment-65998d8886-5b5rc / Running 84s 10.244.1.5 host1 <none> <none>
  5. nginx-deployment-65998d8886-tnpcx / Running 84s 10.244.2.5 host2 <none> <none>

k8s-node1 和 k8s-node2 上各跑了一个副本。现在修改 nginx.yml,将副本改成 5 个。

再次执行kubectl apply

  1. [root@ken ~]# kubectl apply -f nginx.yml
  2. deployment.extensions/nginx-deployment configured

查看pod

  1. [root@ken ~]# kubectl get pod -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. nginx-deployment-65998d8886-4hfgp / Running 3m 10.244.1.7 host1 <none> <none>
  4. nginx-deployment-65998d8886-5b5rc / Running 5m48s 10.244.1.5 host1 <none> <none>
  5. nginx-deployment-65998d8886-btrsq / Running 3m 10.244.2.6 host2 <none> <none>
  6. nginx-deployment-65998d8886-tnpcx / Running 5m48s 10.244.2.5 host2 <none> <none>
  7. nginx-deployment-65998d8886-x4pbd / Running 3m 10.244.1.6 host1 <none> <none>

三个新副本被创建并调度到 k8s-node1 和 k8s-node2 上。

接下来修改配置文件,将副本数减少为 3 个,重新执行 kubectl apply:

  1. [root@ken ~]# kubectl apply -f nginx.yml
  2. deployment.extensions/nginx-deployment configured
  3. [root@ken ~]# kubectl get pod -o wide
  4. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  5. nginx-deployment-65998d8886-5b5rc / Running 7m6s 10.244.1.5 host1 <none> <none>
  6. nginx-deployment-65998d8886-btrsq / Running 4m18s 10.244.2.6 host2 <none> <none>
  7. nginx-deployment-65998d8886-tnpcx / Running 7m6s 10.244.2.5 host2 <none> <none>

可以看到两个副本被删除,最终保留了 3 个副本。

模拟故障

上面我们有 3 个 nginx 副本分别运行在 k8s-node1 和 k8s-node2 上。现在模拟 k8s-node2 故障,关闭该节点(poweroff)。

首先查看节点

  1. [root@ken ~]# kubectl get node
  2. NAME STATUS ROLES AGE VERSION
  3. host1 Ready <none> 5h25m v1.13.2
  4. host2 NotReady <none> 5h43m v1.13.2
  5. ken Ready master 6h18m v1.13.2

发现host2状态为NotReady

等待一段时间,Kubernetes 会检查到 k8s-node2 不可用,将 k8s-node2 上的 Pod 标记为  Terminating状态,并在 k8s-node1 上新创建两个 Pod,维持总副本数为 3。

  1. [root@ken ~]# kubectl get pod -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. nginx-deployment-65998d8886-5b5rc / Running 16m 10.244.1.5 host1 <none> <none>
  4. nginx-deployment-65998d8886-8647d / Running 79s 10.244.1.8 host1 <none> <none>
  5. nginx-deployment-65998d8886-btrsq / Terminating 13m 10.244.2.6 host2 <none> <none>
  6. nginx-deployment-65998d8886-qp6jj / Running 79s 10.244.1.9 host1 <none> <none>
  7. nginx-deployment-65998d8886-tnpcx / Terminating 16m 10.244.2.5 host2 <none> <none>

当 k8s-node2 恢复后, Terminating的 Pod 会被删除,不过已经运行的 Pod 不会重新调度回 k8s-node2。

  1. [root@ken ~]# kubectl get node
  2. NAME STATUS ROLES AGE VERSION
  3. host1 Ready <none> 5h33m v1.13.2
  4. host2 Ready <none> 5h51m v1.13.2
  5. ken Ready master 6h26m v1.13.2
  6. [root@ken ~]# kubectl get pod -o wide
  7. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  8. nginx-deployment-65998d8886-5b5rc / Running 19m 10.244.1.5 host1 <none> <none>
  9. nginx-deployment-65998d8886-8647d / Running 4m33s 10.244.1.8 host1 <none> <none>
  10. nginx-deployment-65998d8886-qp6jj / Running 4m33s 10.244.1.9 host1 <none> <none>

删除 nginx-deployment:

  1. [root@ken ~]# kubectl delete -f nginx.yml
  2. deployment.extensions "nginx-deployment" deleted

label 控制 Pod 的位置

默认配置下,Scheduler 会将 Pod 调度到所有可用的 Node。不过有些情况我们希望将 Pod 部署到指定的 Node,比如将有大量磁盘 I/O 的 Pod 部署到配置了 SSD 的 Node;或者 Pod 需要 GPU,需要运行在配置了 GPU 的节点上。

Kubernetes 是通过 label 来实现这个功能的。

label 是 key-value 对,各种资源都可以设置 label,灵活添加各种自定义属性。比如执行如下命令标注 k8s-node1 是配置了 SSD 的节点。

第一步:定义标签

disk为自定义字符串

  1. [root@ken ~]# kubectl label node host1 disk=ssd

第二步:查看标签

  1. [root@ken ~]# kubectl get node --show-labels
  2. NAME STATUS ROLES AGE VERSION LABELS
  3. host1 Ready <none> 8h v1.13.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disk=ssd,kubernetes.io/hostname=host1
  4. host2 Ready <none> 8h v1.13.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=host2
  5. ken Ready master 8h v1.13.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=ken,node-role.kubernetes.io/master=

disk=ssd 已经成功添加到 host1,除了 disk,Node 还有几个 Kubernetes 自己维护的 label。

第三步:配置nginx.yml

有了 disk 这个自定义 label,接下来就可以指定将 Pod 部署到 host1。编辑 nginx.yml:

在 Pod 模板的 spec 里通过 nodeSelector 指定将此 Pod 部署到具有 label disktype=ssd 的 Node 上。

注意:1. nodeSelector需要与containers位置保持一致

2. S必须大写

第四步:部署

部署 Deployment

  1. [root@ken ~]# kubectl apply -f nginx.yml
  2. deployment.extensions/nginx-deployment created

第五步:查看 Pod 的运行节点

  1. [root@ken ~]# kubectl get pod -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. nginx-deployment-5d8db4598d-2gdmz / ContainerCreating 102s <none> host1 <none> <none>
  4. nginx-deployment-5d8db4598d-cq55q / ContainerCreating 102s <none> host1 <none> <none>
  5. nginx-deployment-5d8db4598d-qjh4x / ContainerCreating 102s <none> host1 <none> <none>

全部 3个副本都运行在 host1 上,符合我们的预期。

要删除 label disktype,执行如下命令:

kubectl label node k8s-node1 disktype-

- 即删除。

  1. [root@ken ~]# kubectl label node host1 disk-
  2. node/host1 labeled
  3. [root@ken ~]# kubectl get node --show-labels
  4. NAME STATUS ROLES AGE VERSION LABELS
  5. host1 NotReady <none> 8h v1.13.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=host1
  6. host2 Ready <none> 8h v1.13.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=host2
  7. ken Ready master 9h v1.13.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=ken,node-role.kubernetes.io/master=

不过此时 Pod 并不会重新部署,依然在 host1 上运行。

  1. [root@ken ~]# kubectl get pod -o wide
  2. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  3. nginx-deployment-5d8db4598d-2dbw9 / Running 39s 10.244.1.12 host1 <none> <none>
  4. nginx-deployment-5d8db4598d-4brh5 / Running 39s 10.244.1.11 host1 <none> <none>
  5. nginx-deployment-5d8db4598d-p87mj / Running 39s 10.244.1.13 host1 <none> <none>

除非在 nginx.yml 中删除 nodeSelector 设置,然后通过 kubectl apply 重新部署。

不需要删除之前的deployment,直接部署即可

Kubernetes 自己会删除之前的 Pod 并调度和运行新的 Pod。

  1. [root@ken ~]# kubectl apply -f nginx.yml
  2. [root@ken ~]# kubectl get pod -o wide
  3. NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
  4. nginx-deployment-5d8db4598d-p87mj / Terminating 2m 10.244.1.13 host1 <none> <none>
  5. nginx-deployment-65998d8886-t5nmv / Running 7s 10.244.2.9 host2 <none> <none>
  6. nginx-deployment-65998d8886-wz7c2 / Running 4s 10.244.2.10 host2 <none> <none>
  7. nginx-deployment-65998d8886-xdlz4 / Running 6s 10.244.1.14 host1 <none> <none>

k8s运行容器之deployment(三)--技术流ken的更多相关文章

  1. k8s运行容器之deployment(三)

    deployment 我们已经知道k8s是通过各种controller来管理pod的生命周期.为了满足不同业务场景,k8s开发了Deployment.ReplicaSet.DaemonSet.Stat ...

  2. k8s重要概念及部署k8s集群(一)--技术流ken

    重要概念 1. cluster cluster是 计算.存储和网络资源的集合,k8s利用这些资源运行各种基于容器的应用. 2.master master是cluster的大脑,他的主要职责是调度,即决 ...

  3. k8s架构分析(二)--技术流ken

    master节点 k8s的集群由master和node组成,节点上运行着若干k8s服务. master节点之上运行着的后台服务有kube-apiserver .kube-scheduler.kube- ...

  4. k8s运行容器之Job(四)--技术流ken

    Job 容器按照持续运行的时间可分为两类:服务类容器和工作类容器. 服务类容器通常持续提供服务,需要一直运行,比如 http server,daemon 等.工作类容器则是一次性任务,比如批处理程序, ...

  5. k8s运行容器之Job(四)

    Job 容器按照持续运行的时间可分为两类:服务类容器和工作类容器. 服务类容器通常持续提供服务,需要一直运行,比如 http server,daemon 等.工作类容器则是一次性任务,比如批处理程序, ...

  6. k8s运行容器之Job应用(6)

    容器按照持续运行的时间可分为两类:服务类容器和工作类容器. 服务类容器通常持续提供服务,需要一直运行,比如 http server,daemon 等.工作类容器则是一次性任务,比如批处理程序,完成后容 ...

  7. k8s滚动更新(六)--技术流ken

    实践 滚动更新是一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新.滚动更新的最大的好处是零停机,整个更新过程始终有副本在运行,从而保证了业务的连续性. 下面我们部署三副本应用, ...

  8. Redis主从复制、多实例、高可用(三)--技术流ken

    Redis主从复制 在开始实现redis的高可用之前,首先来学习一下如何实现redis的主从复制,毕竟高可用也会依赖主从复制的技术. Redis的主从复制,可以实现一个主节点master可以有多个从节 ...

  9. k8s健康检查(七)--技术流ken

    默认的健康检查 强大的自愈能力是 Kubernetes 这类容器编排引擎的一个重要特性.自愈的默认实现方式是自动重启发生故障的容器.除此之外,用户还可以利用 Liveness 和 Readiness ...

随机推荐

  1. [AtCoder3856]Ice Rink Game - 模拟

    Problem Statement An adult game master and N children are playing a game on an ice rink. The game co ...

  2. yarn的工作原理

    1.YARN 是什么? 从业界使用分布式系统的变化趋势和 hadoop 框架的长远发展来看,MapReduce的 JobTracker/TaskTracker 机制需要大规模的调整来修复它在可扩展性, ...

  3. 将DataRow拷贝到另一个DataRow

    DataRow dr = dtPadFluid.Rows[gvPadFluid.FocusedRowHandle]; foreach (DataColumn dc in _dr.Table.Colum ...

  4. react-native 常用的一些插件

    react-native 常用的一些插件 最近在做react-native的app,用到的一些好用的插件,在这儿记录一下 由于返回的后台内容是富文本编辑器Quill,返回的的是Delta对象,使用了q ...

  5. 实战深度学习OpenCV(三):视频实时canny边缘检测

    #include <stdio.h> #include"opencv2/opencv.hpp" using namespace cv; int main() { Vid ...

  6. JQuery实现 图片上传

    用到的文件,我都已经打包好了,自行下载: https://files.cnblogs.com/files/lguow/lib.rar 核心代码如下: <input type="hidd ...

  7. 使用Nginx做图片服务器时候,配置之后图片访问一直是 404问题解决

    我的错误配置是: 服务器文件根地址: 想通过浏览器输入这个地址访问到图片: 但是会发现文件找不到会一直404,原因是根路径配置错误,来看下root路径原理: root 配置的意思是,会在root配置的 ...

  8. Node.js(day6)

    初始化准备工作 初始化目录 nmp init -y 安装基本的第三方插件 express npm install express --save art-template npm install art ...

  9. [Swift]LeetCode167. 两数之和 II - 输入有序数组 | Two Sum II - Input array is sorted

    Given an array of integers that is already sorted in ascending order, find two numbers such that the ...

  10. [Swift]LeetCode424. 替换后的最长重复字符 | Longest Repeating Character Replacement

    Given a string that consists of only uppercase English letters, you can replace any letter in the st ...