仓库

Helm 的 Repo 仓库和 Docker Registry 比较类似,Chart 库可以用来存储和共享打包 Chart 的位置,我们在安装了 Helm 后,默认的仓库地址是 google 的一个地址,这对于我们不能科学上网的同学就比较苦恼了,没办法访问到官方提供的 Chart 仓库,可以用helm repo list来查看当前的仓库配置:

$ helm repo list
NAME    URL
stable  https://kubernetes-charts.storage.googleapis.com/
local   http://127.0.0.1:8879/charts

我们可以看到除了一个默认的 stable 的仓库配置外,还有一个 local 的本地仓库,这是我们本地测试的一个仓库地址。其实要创建一个 Chart 仓库也是非常简单的,Chart 仓库其实就是一个带有index.yaml索引文件和任意个打包的 Chart 的 HTTP 服务器而已,比如我们想要分享一个 Chart 包的时候,将我们本地的 Chart 包上传到该服务器上面,别人就可以使用了,所以其实我们自己托管一个 Chart 仓库也是非常简单的,比如阿里云的 OSS、Github Pages,甚至自己创建的一个简单服务器都可以。

为了解决科学上网的问题,我这里建了一个 Github Pages 仓库,每天会自动和官方的仓库进行同步,地址是:https://github.com/cnych/kube-charts-mirror,这样我们就可以将我们的 Helm 默认仓库地址更改成我们自己的仓库地址了:

$ helm repo remove stable
"stable" has been removed from your repositories
$ helm repo add stable https://cnych.github.io/kube-charts-mirror/
"stable" has been added to your repositories
$ helm repo list
NAME    URL
stable  https://cnych.github.io/kube-charts-mirror/
local   http://127.0.0.1:8879/charts
$ helm repo update
Hang tight while we grab the latest from your chart repositories...
...Skip local chart repository
...Successfully got an update from the "stable" chart repository
Update Complete. ⎈ Happy Helming!⎈

仓库添加完成后,可以使用 update 命令进行仓库更新。当然如果要我们自己来创建一个 web 服务器来服务 Helm Chart 的话,只需要实现下面几个功能点就可以提供服务了:

  • 将索引和Chart置于服务器目录中
  • 确保索引文件index.yaml可以在没有认证要求的情况下访问
  • 确保 yaml 文件的正确内容类型(text/yaml 或 text/x-yaml)

如果你的 web 服务提供了上面几个功能,那么也就可以当做 Helm Chart 仓库来使用了。

查找 chart

Helm 将 Charts 包安装到 Kubernetes 集群中,一个安装实例就是一个新的 Release,要找到新的 Chart,我们可以通过搜索命令完成。

记住,如果不能科学上网,将默认的 stable 的仓库地址更换成上面我们创建的地址

直接运行helm search命令可以查看有哪些 Charts 是可用的:

$ helm search
NAME                                    CHART VERSION   APP VERSION                     DESCRIPTION
...
stable/minio                            1.6.3           RELEASE.2018-08-25T01-56-38Z    Minio is a high performance distributed object storage se...
stable/mission-control                  0.4.2           3.1.2                           A Helm chart for JFrog Mission Control
stable/mongodb                          4.2.2           4.0.2                           NoSQL document-oriented database that stores JSON-like do...
stable/mongodb-replicaset               3.5.6           3.6                             NoSQL document-oriented database that stores JSON-like do...
...
stable/zetcd                            0.1.9           0.0.3                           CoreOS zetcd Helm chart for Kubernetes
...

如果没有使用过滤条件,helm search 显示所有可用的 charts。可以通过使用过滤条件进行搜索来缩小搜索的结果范围:

$ helm search mysql
NAME                                CHART VERSION   APP VERSION DESCRIPTION
...
stable/mysql                        0.10.1          5.7.14      Fast, reliable, scalable, and easy to use open-source rel...
stable/mysqldump                    0.1.0           5.7.21      A Helm chart to help backup MySQL databases using mysqldump
stable/prometheus-mysql-exporter    0.1.0           v0.10.0     A Helm chart for prometheus
stable/mariadb                      4.4.0           10.1.35     Fast, reliable, scalable, and easy to use open-source rel...
...

可以看到明显少了很多 charts 了,同样的,我们可以使用 inspect 命令来查看一个 chart 的详细信息:

$ helm inspect stable/mysql
appVersion: 5.7.14
description: Fast, reliable, scalable, and easy to use open-source relational databasesystem.
engine: gotpl
home: https://www.mysql.com/
icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png
keywords:
- mysql
- database
- sql
maintainers:
- email: o.with@sportradar.comname: olemarkus
- email: viglesias@google.comname: viglesiasce
name: mysql
sources:
- https://github.com/kubernetes/charts
- https://github.com/docker-library/mysql
version: 0.10.1---
## mysql image version
## ref: https://hub.docker.com/r/library/mysql/tags/
##
image: "mysql"
imageTag: "5.7.14"
...

使用 inspect 命令可以查看到该 chart 里面所有描述信息,包括运行方式、配置信息等等。

通过 helm search 命令可以找到我们想要的 chart 包,找到后就可以通过 helm install 命令来进行安装了。

安装 chart

要安装新的软件包,直接使用 helm install 命令即可。最简单的情况下,它只需要一个 chart 的名称参数:

$ helm install stable/mysql
NAME:   mewing-squid
LAST DEPLOYED: Tue Sep  4 23:31:23 2018
NAMESPACE: default
STATUS: DEPLOYEDRESOURCES:
==> v1/PersistentVolumeClaim
NAME                STATUS   VOLUME  CAPACITY  ACCESS MODES  STORAGECLASS  AGE
mewing-squid-mysql  Pending  1s==> v1/Service
NAME                TYPE       CLUSTER-IP     EXTERNAL-IP  PORT(S)   AGE
mewing-squid-mysql  ClusterIP  10.108.197.48  <none>       3306/TCP  1s==> v1beta1/Deployment
NAME                DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
mewing-squid-mysql  1        0        0           0          1s==> v1/Pod(related)
NAME                                 READY  STATUS   RESTARTS  AGE
mewing-squid-mysql-69f587bdf9-z7glv  0/1    Pending  0         0s==> v1/Secret
NAME                TYPE    DATA  AGE
mewing-squid-mysql  Opaque  2     1s==> v1/ConfigMap
NAME                     DATA  AGE
mewing-squid-mysql-test  1     1sNOTES:
MySQL can be accessed via port 3306 on the following DNS name from within your cluster:
mewing-squid-mysql.default.svc.cluster.localTo get your root password run:MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default mewing-squid-mysql -o jsonpath="{.data.mysql-root-password}" | base64 --decode; echo)To connect to your database:1. Run an Ubuntu pod that you can use as a client:kubectl run -i --tty ubuntu --image=ubuntu:16.04 --restart=Never -- bash -il2. Install the mysql client:$ apt-get update && apt-get install mysql-client -y3. Connect using the mysql cli, then provide your password:$ mysql -h mewing-squid-mysql -pTo connect to your database directly from outside the K8s cluster:MYSQL_HOST=127.0.0.1MYSQL_PORT=3306# Execute the following command to route the connection:kubectl port-forward svc/mewing-squid-mysql 3306mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}

现在 mysql chart 已经安装上了,安装 chart 会创建一个新 release 对象。上面的 release 被命名为 hmewing-squid。如果你想使用你自己的 release 名称,只需使用--name参数指定即可,比如:

$ helm install stable/mysql --name mydb

在安装过程中,helm 客户端将打印有关创建哪些资源的有用信息,release 的状态以及其他有用的配置信息,比如这里的有访问 mysql 服务的方法、获取 root 用户的密码以及连接 mysql 的方法等信息。

值得注意的是 Helm 并不会一直等到所有资源都运行才退出。因为很多 charts 需要的镜像资源非常大,所以可能需要很长时间才能安装到集群中去。

要跟踪 release 状态或重新读取配置信息,可以使用 helm status 查看:

$  helm status mewing-squid
LAST DEPLOYED: Tue Sep  4 23:31:23 2018
NAMESPACE: default
STATUS: DEPLOYEDRESOURCES:
...

可以看到当前 release 的状态是DEPLOYED,下面还有一些安装的时候出现的信息。

自定义 chart

上面的安装方式是使用 chart 的默认配置选项。但是在很多时候,我们都需要自定义 chart 以满足自身的需求,要自定义 chart,我们就需要知道我们使用的 chart 支持的可配置选项才行。

要查看 chart 上可配置的选项,使用helm inspect values命令即可,比如我们这里查看上面的 mysql 的配置选项:

$ helm inspect values stable/mysql
## mysql image version
## ref: https://hub.docker.com/r/library/mysql/tags/
##
image: "mysql"
imageTag: "5.7.14"## Specify password for root user
##
## Default: random 10 character string
# mysqlRootPassword: testing## Create a database user
##
# mysqlUser:
## Default: random 10 character string
# mysqlPassword:## Allow unauthenticated access, uncomment to enable
##
# mysqlAllowEmptyPassword: true## Create a database
##
# mysqlDatabase:## Specify an imagePullPolicy (Required)
## It's recommended to change this to 'Always' if the image tag is 'latest'
## ref: http://kubernetes.io/docs/user-guide/images/#updating-images
##
imagePullPolicy: IfNotPresentextraVolumes: |# - name: extras#   emptyDir: {}extraVolumeMounts: |# - name: extras#   mountPath: /usr/share/extras#   readOnly: trueextraInitContainers: |# - name: do-something#   image: busybox#   command: ['do', 'something']# Optionally specify an array of imagePullSecrets.
# Secrets must be manually created in the namespace.
# ref: https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod
# imagePullSecrets:# - name: myRegistryKeySecretName## Node selector
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector
nodeSelector: {}livenessProbe:initialDelaySeconds: 30periodSeconds: 10timeoutSeconds: 5successThreshold: 1failureThreshold: 3readinessProbe:initialDelaySeconds: 5periodSeconds: 10timeoutSeconds: 1successThreshold: 1failureThreshold: 3## Persist data to a persistent volume
persistence:enabled: true## database data Persistent Volume Storage Class## If defined, storageClassName: <storageClass>## If set to "-", storageClassName: "", which disables dynamic provisioning## If undefined (the default) or set to null, no storageClassName spec is##   set, choosing the default provisioner.  (gp2 on AWS, standard on##   GKE, AWS & OpenStack)### storageClass: "-"accessMode: ReadWriteOncesize: 8Giannotations: {}## Configure resource requests and limits
## ref: http://kubernetes.io/docs/user-guide/compute-resources/
##
resources:requests:memory: 256Micpu: 100m# Custom mysql configuration files used to override default mysql settings
configurationFiles: {}
#  mysql.cnf: |-
#    [mysqld]
#    skip-name-resolve
#    ssl-ca=/ssl/ca.pem
#    ssl-cert=/ssl/server-cert.pem
#    ssl-key=/ssl/server-key.pem# Custom mysql init SQL files used to initialize the database
initializationFiles: {}
#  first-db.sql: |-
#    CREATE DATABASE IF NOT EXISTS first DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;
#  second-db.sql: |-
#    CREATE DATABASE IF NOT EXISTS second DEFAULT CHARACTER SET utf8 DEFAULT COLLATE utf8_general_ci;metrics:enabled: falseimage: prom/mysqld-exporterimageTag: v0.10.0imagePullPolicy: IfNotPresentresources: {}annotations: {}# prometheus.io/scrape: "true"# prometheus.io/port: "9104"livenessProbe:initialDelaySeconds: 15timeoutSeconds: 5readinessProbe:initialDelaySeconds: 5timeoutSeconds: 1## Configure the service
## ref: http://kubernetes.io/docs/user-guide/services/
service:## Specify a service type## ref: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services---service-typestype: ClusterIPport: 3306# nodePort: 32000ssl:enabled: falsesecret: mysql-ssl-certscertificates:
#  - name: mysql-ssl-certs
#    ca: |-
#      -----BEGIN CERTIFICATE-----
#      ...
#      -----END CERTIFICATE-----
#    cert: |-
#      -----BEGIN CERTIFICATE-----
#      ...
#      -----END CERTIFICATE-----
#    key: |-
#      -----BEGIN RSA PRIVATE KEY-----
#      ...
#      -----END RSA PRIVATE KEY-----## Populates the 'TZ' system timezone environment variable
## ref: https://dev.mysql.com/doc/refman/5.7/en/time-zone-support.html
##
## Default: nil (mysql will use image's default timezone, normally UTC)
## Example: 'Australia/Sydney'
# timezone:# To be added to the database server pod(s)
podAnnotations: {}

然后,我们可以直接在 YAML 格式的文件中来覆盖上面的任何配置,在安装的时候直接使用该配置文件即可:(config.yaml)

mysqlUser: haimaxyUser
mysqlDatabase: haimaxyDB
service:type: NodePort

我们这里通过 config.yaml 文件定义了 mysqlUser 和 mysqlDatabase,并且把 service 的类型更改为了 NodePort,然后现在我们来安装的时候直接指定该 yaml 文件:

$ helm install -f config.yaml stable/mysql --name mydb
NAME:   mydb
LAST DEPLOYED: Wed Sep  5 00:09:44 2018
NAMESPACE: default
STATUS: DEPLOYEDRESOURCES:
==> v1/Secret
NAME        TYPE    DATA  AGE
mydb-mysql  Opaque  2     1s==> v1/ConfigMap
NAME             DATA  AGE
mydb-mysql-test  1     1s==> v1/PersistentVolumeClaim
NAME        STATUS   VOLUME  CAPACITY  ACCESS MODES  STORAGECLASS  AGE
mydb-mysql  Pending  1s==> v1/Service
NAME        TYPE      CLUSTER-IP     EXTERNAL-IP  PORT(S)         AGE
mydb-mysql  NodePort  10.96.150.198  <none>       3306:32604/TCP  0s==> v1beta1/Deployment
NAME        DESIRED  CURRENT  UP-TO-DATE  AVAILABLE  AGE
mydb-mysql  1        1        1           0          0s==> v1/Pod(related)
NAME                        READY  STATUS   RESTARTS  AGE
mydb-mysql-dfc999888-hbw5d  0/1    Pending  0         0s
...

我们可以看到当前 release 的名字已经变成 mydb 了。然后可以查看下 mydb 关联的 Service 是否变成 NodePort 类型的了:

$ kubectl get svc
NAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes           ClusterIP   10.96.0.1        <none>        443/TCP          110d
mewing-squid-mysql   ClusterIP   10.108.197.48    <none>        3306/TCP         46m
mydb-mysql           NodePort    10.96.150.198    <none>        3306:32604/TCP   8m

看到服务 mydb-mysql 变成了 NodePort 类型的,二之前默认创建的 mewing-squid-mysql 是 ClusterIP 类型的,证明上面我们通过 YAML 文件来覆盖 values 是成功的。

接下来我们查看下 Pod 的状况:

$ kubectl get pods
NAME                                      READY     STATUS    RESTARTS   AGE
mewing-squid-mysql-69f587bdf9-z7glv       0/1       Pending   0          49m
mydb-mysql-dfc999888-hbw5d                0/1       Pending   0          11m

比较奇怪的是之前默认创建的和现在的 mydb 的 release 创建的 Pod 都是 Pending 状态,直接使用 describe 命令查看下:

$ kubectl describe pod mydb-mysql-dfc999888-hbw5d
Name:           mydb-mysql-dfc999888-hbw5d
Namespace:      default
Node:           <none>
Labels:         app=mydb-mysqlpod-template-hash=897555444
...
Events:Type     Reason            Age                From               Message----     ------            ----               ----               -------Warning  FailedScheduling  2m (x37 over 12m)  default-scheduler  pod has unbound PersistentVolumeClaims (repeated 2 times)

我们可以发现两个 Pod 处于 Pending 状态的原因都是 PVC 没有被绑定上,所以这里我们可以通过 storageclass 或者手动创建一个合适的 PV 对象来解决这个问题。

另外为了说明 helm 更新的用法,我们这里来直接禁用掉数据持久化,可以在上面的 config.yaml 文件中设置:

persistence:enabled: false

另外一种方法就是在安装过程中使用--set来覆盖对应的 value 值,比如禁用数据持久化,我们这里可以这样来覆盖:

$ helm install stable/mysql --set persistence.enabled=false --name mydb

升级

我们这里将数据持久化禁用掉来对上面的 mydb 进行升级:

$ echo config.yaml
mysqlUser: haimaxyUser
mysqlDatabase: haimaxyDB
service:type: NodePort
persistence:enabled: false
$ helm upgrade -f config.yaml mydb stable/mysql
helm upgrade -f config.yaml mydb stable/mysql
Release "mydb" has been upgraded. Happy Helming!
LAST DEPLOYED: Wed Sep  5 00:38:33 2018
NAMESPACE: default
STATUS: DEPLOYEDRESOURCES:
...

可以看到已经变成 DEPLOYED 状态了,现在我们再去看看 Pod 的状态呢:

$ kubectl get pods
NAME                                      READY     STATUS            RESTARTS   AGE
mewing-squid-mysql-69f587bdf9-z7glv       0/1       Pending           0          1h
mydb-mysql-6ffc84bbf6-lcn4d               0/1       PodInitializing   0          49s
...

我们看到 mydb 关联的 Pod 已经变成了 PodInitializing 的状态,已经不是 Pending 状态了,同样的,使用 describe 命令查看:

$ kubectl describe pod mydb-mysql-6ffc84bbf6-lcn4d
Name:           mydb-mysql-6ffc84bbf6-lcn4d
Namespace:      default
Node:           node02/10.151.30.63
Start Time:     Wed, 05 Sep 2018 00:38:33 +0800
Labels:         app=mydb-mysqlpod-template-hash=2997406692
Annotations:    <none>
Status:         Pending
...
Events:Type    Reason                 Age   From               Message----    ------                 ----  ----               -------Normal  SuccessfulMountVolume  58s   kubelet, node02    MountVolume.SetUp succeeded for volume "data"Normal  SuccessfulMountVolume  58s   kubelet, node02    MountVolume.SetUp succeeded for volume "default-token-n9w2d"Normal  Scheduled              57s   default-scheduler  Successfully assigned mydb-mysql-6ffc84bbf6-lcn4d to node02Normal  Pulling                57s   kubelet, node02    pulling image "busybox:1.25.0"Normal  Pulled                 45s   kubelet, node02    Successfully pulled image "busybox:1.25.0"Normal  Created                44s   kubelet, node02    Created containerNormal  Started                44s   kubelet, node02    Started containerNormal  Pulling                41s   kubelet, node02    pulling image "mysql:5.7.14"

我们可以看到现在没有任何关于 PVC 的错误信息了,这是因为我们刚刚更新的版本中就是禁用掉了的数据持久化的,证明 helm upgrade 和 –values 是生效了的。现在我们使用 helm ls 命令查看先当前的 release:

$ helm ls
NAME            REVISION    UPDATED                     STATUS      CHART           APP VERSION NAMESPACE
mewing-squid    1           Tue Sep  4 23:31:23 2018    DEPLOYED    mysql-0.10.1    5.7.14      default
mydb            2           Wed Sep  5 00:38:33 2018    DEPLOYED    mysql-0.10.1    5.7.14      default

可以看到 mydb 这个 release 的REVISION已经变成2了,这是因为 release 的版本是递增的,每次安装、升级或者回滚,版本号都会加1,第一个版本号始终为1,同样我们可以使用 helm history 命令查看 release 的历史版本:

$ helm history mydb
REVISION    UPDATED                     STATUS      CHART           DESCRIPTION
1           Wed Sep  5 00:09:44 2018    SUPERSEDED  mysql-0.10.1    Install complete
2           Wed Sep  5 00:38:33 2018    DEPLOYED    mysql-0.10.1    Upgrade complete

当然如果我们要回滚到某一个版本的话,使用 helm rollback 命令即可,比如我们将 mydb 回滚到上一个版本:

$ $ helm rollback mydb 1

删除

上节课我们就学习了要删除一个 release 直接使用 helm delete 命令就 OK:

$ helm delete mewing-squid
release "mewing-squid" deleted

这将从集群中删除该 release,但是这并不代表就完全删除了,我们还可以通过--deleted参数来显示被删除掉 release:

$ helm list --deleted
NAME            REVISION    UPDATED                     STATUS  CHART           APP VERSION NAMESPACE
mewing-squid    1           Tue Sep  4 23:31:23 2018    DELETED mysql-0.10.1    5.7.14      default
$  helm list --all
NAME            REVISION    UPDATED                     STATUS      CHART           APP VERSION NAMESPACE
mewing-squid    1           Tue Sep  4 23:31:23 2018    DELETED     mysql-0.10.1    5.7.14      default
mydb            2           Wed Sep  5 00:38:33 2018    DEPLOYED    mysql-0.10.1    5.7.14      default

helm list --all则会显示所有的 release,包括已经被删除的

由于 Helm 保留已删除 release 的记录,因此不能重新使用 release 名称。(如果 确实 需要重新使用此 release 名称,则可以使用此 –replace 参数,但它只会重用现有 release 并替换其资源。)这点是不是和 docker container 的管理比较类似

请注意,因为 release 以这种方式保存,所以可以回滚已删除的资源并重新激活它。

如果要彻底删除 release,则需要加上--purge参数:

$ helm delete mewing-squid --purge
release "mewing-squid" deleted

Helm系列(3)- Helm 的基本使用相关推荐

  1. Kubernetes系列之Helm介绍篇

    本次系列使用的所需部署包版本都使用的目前最新的或最新稳定版,安装包地址请到公众号内回复[K8s实战]获取 介绍 Helm 是 Deis 开发的一个用于 Kubernetes 应用的包管理工具,主要用来 ...

  2. Helm系列-helm chart实战(一)

    目录 1. 什么是Chart? 2. Chart 基础使用 2.1 查找 Chart 2.1.1  搜索所有公开可用的 charts 2.1.2 搜索本地 stable 源中可用的 charts 2. ...

  3. Helm系列(5)-helm 常用命令

    安装前自定义 chart 上述安装方式只会使用 chart 的默认配置选项.很多时候,我们需要自定义 chart 来指定我们想要的配置. 使用 helm show values 可以查看 chart ...

  4. 【Helm三部曲】 Helm 包管理器 chartmuseum 简介及安装

    一.Chartmuseum ChartMuseum is an open-source Helm Chart Repository server written in Go (Golang), wit ...

  5. k8s包管理器helm_是时候使用Helm了:Helm, Kubernetes的包管理工具

    目前我们的一个产品共有4套环境:dev环境.test环境.staging环境.production环境. 其中dev, test, staging环境在一个Kubernetes集群上以不同namesp ...

  6. K8s Helm 可视化工具 - Helm Dashboard

    Helm-Dashboard 是 ValidKube 之后 Komodor 的第二个开源项目,Komodor 的愿景是通过构建有助于理解分布式云原生系统引入的复杂性的工具,使Kubernetes的操作 ...

  7. Helm3入门教程全系列,26小时轻松掌握Helm

    很多人都使用过Ubuntu下的ap-get或者CentOS下的yum, 这两者都是Linux系统下的包管理工具.采用apt-get/yum,应用开发者可以管理应用包之间的依赖关系,发布应用:用户则可以 ...

  8. 初探云原生应用管理(二): 为什么你必须尽快转向 Helm v3

    系列介绍:这个系列是介绍如何用云原生技术来构建.测试.部署.和管理应用的内容专辑.做这个系列的初衷是为了推广云原生应用管理的最佳实践,以及传播开源标准和知识.在这个系列文章的开篇初探云原生应用管理(一 ...

  9. 初探云原生应用管理(一): Helm 与 App Hub

    系列介绍:初探云原生应用管理系列是介绍如何用云原生技术来构建.测试.部署.和管理应用的内容专辑.做这个系列的初衷是为了推广云原生应用管理的最佳实践,以及传播开源标准和知识.通过这个系列,希望帮助大家学 ...

最新文章

  1. Mysql:AVG()函数如何去除0值做平均值
  2. c语言垂直制表的作用,c语言中“\”的用途
  3. 正则表达式的20个小应用
  4. Python 2.x 与Python 3.x的差别总结
  5. 微型计算机技术及应用 习题答案,微型计算机技术及应用习题答案.doc
  6. Zabbix中文模式:图片下面的字符乱码
  7. 组建实验室仅3年,团队人均26岁,这位85后女博导成果登上Nature!
  8. 2021-2025年中国制药行业MR报告软件行业市场供需与战略研究报告
  9. windows ios android的架构_什么便签备忘录软件横跨Windows、iOS和Android - 学显
  10. hadoop漏洞_【漏洞公告】CVE-2017-7669:Apache Hadoop远程权限提升漏洞
  11. 镜像下载cudnn+tensorflow
  12. Android移动应用开发大作业——日程管理+邮件反馈
  13. 基于JAVA疫情防控期间网上教学管理计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  14. Matlab 可见光波段植被指数
  15. 国家开放大学《国际经济法》第二章 国际货物买卖法 边学边练
  16. swift 打开第三方应用_Swift常用第三方
  17. Oracle 12c 的安装步骤教程
  18. 技术人员的公众号如何打造高质量内容和精美排版?
  19. 基于ZigBee和STM32的智能家居控制系统的设计与实现
  20. Smarty - 官方网站

热门文章

  1. MacBook typora快捷键
  2. chromedriver、firefox-geckodriver、iedriver下载链接
  3. javascript语法_了解JavaScript中的解构,剩余参数和传播语法
  4. 高效沟通与面试技巧--《牛津式高效沟通》
  5. 题目:下面关于Java接口的说法错误的是()
  6. python 奥特曼打小怪兽的简单版
  7. Unity插件 EasyTouch
  8. 云服务器搭建ThinkPHP框架图文教程
  9. QT之Qml使用QSystemTrayIcon实现系统托盘
  10. 下单后半小时未付款订单自动取消的实现,延迟队列