Docker-Compose 多容器部署工具

概述

Docker-Compose 项目是 Docker 官方的开源项目,是用于定义和运行多容器 Docker 应用程序的工具。负责实现对 Docker 容器集群的快速编排。 通过 Compose,可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

Docker-Compose 将所管理的容器分为三层,分别是工程(project),服务(service)以及容器(container)。Docker-Compose 运行目录下的所有文件(docker-compose.yml,extends文件或环境变量文件等)组成一个工程,若无特殊指定工程名即为当前目录名。

一个工程当中可包含多个服务,每个服务中定义了容器运行的镜像,参数,依赖。一个服务当中可包括多个容器实例,Docker-Compose 并没有解决负载均衡的问题,因此需要借助其它工具实现服务发现及负载均衡。

Docker-Compose 的工程配置文件默认为 docker-compose.yml,可通过环境变量 COMPOSE_FILE 或 -f 参数自定义配置文件,其定义了多个有依赖关系的服务及每个服务运行的容器。

使用一个 Dockerfile 模板文件,可以让用户很方便的定义一个单独的应用容器。在工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。

Compose 允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 Docker-Compose 项目由 Python 编写,调用 Docker 服务提供的 API 来对容器进行管理。因此,只要所操作的平台支持Docker API,就可以在其上利用 Compose 来进行编排管理。

Docker-Compose 安装及卸载

Docker-compose 安装

  • 安装 Docker Compose 可以通过下面命令自动下载适应版本的 Compose,并为安装脚本添加执行权限
# 要安装其他版本的 Compose,请替换 v2.2.2。
sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-composechmod +x /usr/local/bin/docker-compose
  • 查看安装是否成功
docker-compose -v
  • 创建软链:
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Docker-compose卸载

apt-get remove docker-compose

docker-compose 常用命令

  • ps:列出所有运行容器
docker-compose ps
  • logs:查看服务日志输出
docker-compose logs [options] [SERVICE...]
# 选项包括
–no-color   关闭颜色。默认情况下,docker-compose将对不同的服务输出使用不同的颜色来区分
-f          跟踪日志输出
。
# 示例
docker-compose logs
  • up:使用当前目录下的docker-compose.yaml文件构建、启动、更新容器

    • 当服务的配置发生更改时,可使用 docker-compose up 命令更新配置

      此时,Compose 会删除旧容器并创建新容器,新容器会以不同的 IP 地址加入网络,名称保持不变,任何指向旧容起的连接都会被关闭,重新找到新容器并连接上去

docker-compose up [options] [--scale SERVICE=NUM...] [SERVICE...]
# 选项包括:
-f                  # 指定yml部署模板文件
-d                  # 在后台运行服务容器
--force-recreate    # 强制重新创建容器,不能与-no-recreate同时使用
-no-recreate        # 如果容器已经存在,则不重新创建,不能与–force-recreate同时使用
-no-color       # 不用颜色来区分不同的服务的控制输出
-no-deps        # 不启动服务所链接的容器
-no-build       # 不自动构建缺失的服务镜像
-build          # 在启动容器前构建服务镜像
-V, --renew-anon-volumes        # 重新创建匿名卷,而不是从以前的容器中检索数据
-abort-on-container-exit        # 如果任何一个容器被停止,则停止所有容器。不能与-d同时使用
-t, -timeout TIMEOUT            # 停止容器时候的超时(默认为10秒)
-remove-orphans                 # 删除服务中没有在compose.yaml文件中定义的容器
--scale SERVICE=NUM            # 将Compose.yaml中的SERVICE服务扩展到NUM个实例,快速实现一个负载均衡(单节点)# 该参数会覆盖Compose.yaml文件中的“scale”设置(如果存在)。# 注意:如果Compose.yaml中设置了ports配置(端口绑定)# ,当使用scale参数拓展到多个实例时,会端口冲突,需删除Compose.yaml中的ports配置# 示例
docker-compose up --force-recreate -d
  • port:显示某个容器端口所映射的公共端口
docker-compose port [options] SERVICE PRIVATE_PORT
# 选项包括:
–protocol=proto        指定端口协议,TCP(默认值)或者UDP
–index=index       如果同意服务存在多个容器,指定命令对象容器的序号(默认为1)
# 示例:下面命令可以输出 eureka 服务 8761 端口所绑定的公共端口
docker-compose port eureka 8761
  • build:构建或者重新构建服务容器。

    服务容器一旦构建后,将会带上一个标记名。可以随时在项目目录下运行docker-compose build来重新构建服务

docker-compose build [options] [--build-arg key=val...] [SERVICE...]
# 选项包括:
–compress   通过gzip压缩构建上下环境
–force-rm   删除构建过程中的临时容器
–no-cache   构建镜像过程中不使用缓存
–pull       始终尝试通过拉取操作来获取更新版本的镜像
-m, –memory MEM     为构建的容器设置内存大小
–build-arg key=val 为服务设置build-time变量
# 示例:
docker-compose build
  • stop:停止已运行的服务的容器
docker-compose stop [options] [SERVICE...]
# 选项包括
-t, –timeout TIMEOUT 停止容器时候的超时(默认为10秒)
# 示例
docker-compose stop eureka
  • start:启动指定服务已存在的容器
docker-compose start [SERVICE...]
# 示例
docker-compose start eureka
  • restart:重启项目中的服务
docker-compose restart [options] [SERVICE...]
# 选项包括:
-t, –timeout TIMEOUT    指定重启前停止容器的超时(默认为10秒)
# 示例:
docker-compose restart
  • rm:删除所有或指定(停止状态的)服务的容器
docker-compose rm [options] [SERVICE...]
# 选项包括:
–f, –force  强制直接删除,包括非停止状态的容器
-v          删除容器所挂载的数据卷
# 示例
docker-compose rm eureka
  • kill:通过发送 SIGKILL 信号来强制停止指定服务的容器
docker-compose kill [options] [SERVICE...]
# 选项包括:
-s  来指定发送的信号
# 示例
docker-compose kill eureka
docker-compose kill -s SIGINT
  • push:推送服务依的镜像
docker-compose push [options] [SERVICE...]
# 选项包括:
–ignore-push-failures  忽略推送镜像过程中的错误
  • pull:下载服务镜像
docker-compose pull [options] [SERVICE...]
# 选项包括:
–ignore-pull-failures   忽略拉取镜像过程中的错误
–parallel   多个镜像同时拉取
–quiet      拉取镜像过程中不打印进度信息
# 示例
docker-compose pull
  • scale:设置指定服务运行容器的个数,以 service=num 形式指定
docker-compose scale user=3 movie=3
  • run:在一个服务上执行一个命令
docker-compose run web bash
  • down:停止并删除所有容器、网络、卷、镜像。
docker-compose down [options]
# 选项包括:
–rmi type       删除镜像,类型必须是:all,删除compose文件中定义的所有镜像;local,删除镜像名为空的镜像
-v, –volumes    删除已经在compose文件中定义的和匿名的附在容器上的数据卷
–remove-orphans 删除服务中没有在compose中定义的容器
# 示例
docker-compose down
  • pause:暂停一个服务容器
docker-compose pause [SERVICE...]
  • uppause:恢复处于暂停状态中的服务
docker-compose unpause [SERVICE...]
  • config:验证并查看compose文件配置
docker-compose config [options]
# 选项包括:
–resolve-image-digests  将镜像标签标记为摘要
-q, –quiet      只验证配置,不输出。 当配置正确时,不输出任何内容,当文件配置错误,输出错误信息
–services       打印服务名,一行一个
–volumes        打印数据卷名,一行一个
  • create:为服务创建容器
docker-compose create [options] [SERVICE...]
# 选项包括:
–force-recreate     重新创建容器,即使配置和镜像没有改变,不兼容–no-recreate参数
–no-recreate        如果容器已经存在,不需要重新创建,不兼容–force-recreate参数
–no-build       不创建镜像,即使缺失
–build          创建容器前,生成镜像

Docker-compose 模板文件

简介及示例模板

Compose允许用户通过一个docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 Compose模板文件是一个定义服务、网络和卷的YAML文件。Compose模板文件默认路径是当前目录下的docker-compose.yml,可以使用.yml或.yaml作为文件扩展名。

Docker-Compose标准模板文件应该包含version、services、networks 三大部分,最关键的是services和networks两个部分。

示例模板

version: '3'networks:front-tier:driver: bridgeback-tier:driver: bridgeservices:web:image: dockercloud/hello-worldports:- 8080networks:- front-tier- back-tierredis:image: redislinks:- webnetworks:- back-tierlb:image: dockercloud/haproxyports:- 80:80links:- webnetworks:- front-tier- back-tiervolumes:- /var/run/docker.sock:/var/run/docker.sock

Compose目前有三个版本分别为Version 1,Version 2,Version 3,Compose区分Version 1和Version 2(Compose 1.6.0+,Docker Engine 1.10.0+)。Version 2支持更多的指令。Version 1将来会被弃用。

启动应用

创建一个webapp目录,将docker-compose.yaml文件拷贝到webapp目录下,使用docker-compose启动应用。

docker-compose up -d

Docker-compose.yml 配置说明

  • version:指定 docker-compose.yml 文件的写法格式

  • services:多个容器集合

  • build:指定为构建镜像上下文路径。相对路径、绝对路径均可

    服务除了可以基于指定的镜像,还可以基于一份Dockerfile,在使用up启动时执行构建任务,构建标签是build,可以指定Dockerfile所在文件夹的路径。Compose将会利用Dockerfile自动构建镜像,然后使用镜像启动服务容器。

    • context:上下文路径。
    • dockerfile:指定构建镜像的 Dockerfile 文件名。使用dockerfile文件来构建时,必须指定构建路径
    • args:添加构建参数,这是只能在构建过程中访问的环境变量。可选
    • labels:设置构建镜像的标签。
    • target:多层构建,可以指定构建哪一层。
build: ./dir
---------------
build:context: /path/to/build/dirdockerfile: Dockerfileargs:buildno: 1labels:- "com.example.description=Accounting webapp"- "com.example.department=Finance"- "com.example.label-with-empty-value"target: prod# build都是一个目录,如果要指定Dockerfile文件需要在build标签的子级标签中使用dockerfile标签指定。
# 如果同时指定image和build两个标签,那么Compose会构建镜像并且把镜像命名为image值指定的名字。
  • container_name:指定自定义容器名称,而不是生成的默认名称(<项目名称><服务名称><序号>)。
container_name: my-web-container
  • command:覆盖容器启动后默认执行的命令

    在用命令行运行容器时,有的容器需要在命令中加入附加的命令行参数,这就需要加在compose文件的服务中的command中。这一个参数没有什么固定的模式,建议按照对应容器的镜像的使用说明来决定是否需要加,加什么。

command: bundle exec thin -p 3000
----------------------------------
command: ["bundle","exec","thin","-p","3000"]
  • cap_add,cap_drop:添加或删除容器拥有的宿主机的内核功能。
  • depends_on:设置依赖关系。
    • docker-compose up :以依赖性顺序启动服务。在以下示例中,先启动 db 和 redis ,才会启动 web。
    • docker-compose up SERVICE :自动包含 SERVICE 的依赖项。在以下示例中,docker-compose up web 还将创建并启动 db 和 redis。
    • docker-compose stop :按依赖关系顺序停止服务。在以下示例中,web 在 db 和 redis 之前停止。
    • 注意:web 服务不会等待 redis db 完全启动 之后才启动。
cap_add:- ALL # 开启全部权限cap_drop:- SYS_PTRACE # 关闭 ptrace权限
version: "3.7"
services:web:build: .depends_on:- db- redisredis:image: redisdb:image: postgres
  • dns:配置 dns 服务器,可以是一个值或列表
dns: 8.8.8.8
------------
dns:- 8.8.8.8- 9.9.9.9
  • dns_search:配置 DNS 搜索域,可以是一个值或列表
dns_search: example.com
------------------------
dns_search:- dc1.example.com- dc2.example.com
  • env_file:从文件中获取环境变量,可以指定一个文件路径或路径列表,其优先级低于 environment 指定的环境变量
env_file: .env
---------------
env_file:- ./common.env
  • environment添加环境变量。可以使用数组或字典、任何布尔值,布尔值需要用引号引起来,以确保 YML 解析器不会将其转换为 True 或 False
environment:RACK_ENV: developmentSHOW: 'ture'
-------------------------
environment:- RACK_ENV=development- SHOW=ture
  • expose:暴露端口,但不映射到宿主机,只被连接的服务访问。
expose:- "3000"- "8000"
  • ports:对外暴露的端口定义,和 expose 对应

    注意:ports属性是物理机和集群网络中服务(而不是ingress)之间的映射,容器访问ingress网络中的某个地址时,是无法根据端口做转发的。

    若使用了ports,则该服务会生成两个虚拟ip,一个是服务间通信的overlay网络虚拟ip,一个是用于映射服务端口到物理机端口的ingress网络虚拟ip,这时服务注册到注册中心(比如nacos)的ip很可能会是ingress网络虚拟ip,最终会导致基于注册中心服务发现的gateway路由和feign调用不通(超时)。

    解决方案为:

    1. 若不用暴露端口给物理机,则只使用expose暴露端口到集群网络,这种可以注册正确的虚拟ip到注册中心

    2. 若必须暴露端口给物理机,则配置文件中设定容器虚拟网段

      spring:application:name: @artifactId@cloud:inetutils:ignored-interfaces: eth.*preferred-networks: 192.168.0
      

      详见:https://my.oschina.net/woniuyi/blog/4984748

# 暴露端口信息。格式:- "宿主机端口:容器暴露端口",或者只是指定容器的端口,宿主机会随机映射端口。
ports:
- "8763:8763"
- "8763:8763"# 当使用HOST:CONTAINER格式来映射端口时,如果使用的容器端口小于60可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。
  • extra_hosts:添加容器内主机名映射。类似 docker client --add-host。
extra_hosts:- "somehost:162.242.195.82"- "otherhost:50.31.209.229"# 以上会在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:
162.242.195.82  somehost
50.31.209.229   otherhost
  • healthcheck:用于检测 docker 服务是否健康运行。
healthcheck:test: ["CMD", "curl", "-f", "http://localhost"] # 设置检测程序interval: 1m30s # 设置检测间隔timeout: 10s # 设置检测超时时间retries: 3 # 设置重试次数start_period: 40s # 启动后,多少秒开始启动检测程序
  • image:指定服务所使用的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取镜像。
# 以下格式都可以:
image: redis
image: ubuntu:14.04
image: tutum/influxdb
image: example-registry.com:4000/postgresql
image: a4bc65fd # 镜像id
  • links:将指定容器连接到当前连接,可以设置别名,避免ip方式导致的容器重启动态改变的无法连接情况。

    在用命令行部署容器的时候,经常会需要在一个容器里面访问另外一个容器,这个时候就需要用到 --link参数,一般的使用形式为 --link [引用的其他容器名]:[容器内代表引用容器的字段]。被 --link A:B形式引用的A容器会以[A 的 ip:port] : 'B’的形式出现在容器内的/etc/hosts文件里。也就是说,经过 --link 引入的其他容器A将自己的地址和端口赋给了字段B。在容器内需要获得A的时候,可以直接使用字段B。

    注意:在v3版本的compose file中,links属性是会自动被 docker stack deploy命令忽略的,因为在v3中,links的功能已经移除。作为替代的是networks属性。

links:# 指定服务名称:别名 - docker-compose-eureka-server:compose-eureka# 仅指定服务名称(同时作为别名)- db
  • logs:日志输出信息
--no-color          单色输出,不显示其他颜.
-f, --follow        跟踪日志输出,就是可以实时查看日志
-t, --timestamps    显示时间戳
--tail              从日志的结尾显示,--tail=200
  • logging:日志输出控制

    linux上,容器日志一般存放在 /var/lib/docker/containers/CONTAINER_ID/ 下面,以 json.log 结尾的文件

logging:driver: "json-file"options:max-size: "5g"        # 日志文件的大小限制在5GB
  • network_mode:设置网络模式
network_mode: "bridge"
network_mode: "host"
network_mode: "none"
network_mode: "service:[service name]"
network_mode: "container:[container name/id]"
  • networks:配置容器连接的网络,引用顶级 networks 下的条目 。

    • 建立一个和services参数并级的networks参数,在networks参数下,可以设立一个或者多个的网络,形成一个网络的networks列表。

      • external: true :可以使用该属性引用已创建好了外部网络,实现跨堆栈的网络共享
    • 在每个服务里面可以添加networks属性,该属性下可以包括一个或者多个在先前建立的网络列表中的网络名称,表示该服务在这些网络中可以用这些服务的名称来在其他服务中访问。有时因为各种原因,服务的名称和其他容器内访问的字段会不一致,这个时候还可以在网络名称后面再加一级,aliases别名,别名可以发挥和服务本身的名字一样的作用,在其所属的network中被其他服务访问。
      • aliases :同一网络上的其他容器可以使用服务名称或此别名来连接到对应容器的服务。
networks:some-network:# 使用自定义驱动程序driver: custom-driver-1other-network:# 引用外部网络external: true
services:some-service:networks:some-network:aliases:- alias1other-network:aliases:- alias2
  • restart

    • no:是默认的重启策略,在任何情况下都不会重启容器。
    • always:容器总是重新启动。
    • on-failure:在容器非正常退出时(退出状态非0),才会重启容器。
    • unless-stopped:在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器

    注:swarm 集群模式,请改用 restart_policy。

restart: "no"
------------------------------
restart: always
------------------------------
restart: on-failure
------------------------------
restart: unless-stopped
  • security_opt:修改容器默认的 schema 标签。
security-opt:- label:user:USER   # 设置容器的用户标签- label:role:ROLE   # 设置容器的角色标签- label:type:TYPE   # 设置容器的安全策略标签- label:level:LEVEL  # 设置容器的安全等级标签
  • tmpfs:在容器内安装一个临时文件系统。可以是单个值或列表的多个值。
tmpfs: /run
------------------------------
tmpfs:- /run- /tmp
  • volumes:将主机的数据卷或着文件挂载到容器里

    可以直接使用 [HOST:CONTAINER]格式,或者使用[HOST:CONTAINER:ro]格式,后者对于容器来说,数据卷是只读的,可以有效保护宿主机的文件系统。 Compose的数据卷指定路径可以是相对路径,使用 . 或者 … 来指定相对目录。

    volumes对应命令行中的 -v 选项,其本身的意义并没有发生什么变化。但是,由于volume在yml文件中是以字符串的形式存在的,所以像 $PWD 这样的写法是不能被接受的,要改成相对路径或绝对路径。

# 数据卷的格式可以是下面多种形式
volumes:# 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。- /var/lib/mysql# 使用绝对路径挂载数据卷- /opt/data:/var/lib/mysql# 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。- ./cache:/tmp/cache# 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。- ~/configs:/etc/configs/:ro# 已经存在的命名的数据卷。- datavolume:/var/lib/mysql# 如果不使用宿主机的路径,可以指定一个volume_driver。
  • volumes_from:从另一个服务或容器挂载其数据卷
volumes_from:- service_name    - container_name
  • ulimits:覆盖容器默认的 ulimit。
ulimits:nproc: 65535nofile:soft: 20000hard: 40000

docker 单节点服务编排部署指南(docker-compose)相关推荐

  1. docker swarm 集群服务编排部署指南(docker stack)

    Docker Swarm 集群管理 概述 Docker Swarm 是 Docker 的集群管理工具.它将 Docker 主机池转变为单个虚拟 Docker 主机,使得容器可以组成跨主机的子网网络.D ...

  2. Docker单节点部署OceanBase集群创建租户、用户、业务表

    作者:马顺华 从事运维管理工作多年,目前就职于六棱镜(杭州)科技有限公司,熟悉运维自动化.OceanBase部署运维.MySQL 运维以及各种云平台技术和产品.并已获得OceanBase认证OBCA. ...

  3. 【项目自动发布】基于Docker/单节点Rancher/GitLab搭建简易的CI/CD流水线(备忘+补充完善)

    前言 最近面试好像也经常问到一个问题: 你们项目是怎么发布的 传统的Java项目都是本地打包成 jar包 或者 war包, 上传到服务器, 然后通过shell脚本的方式启动的 要求我们具备一定的she ...

  4. 微服务项目部署在docker容器运行

    昨天的一篇微服务项目中涉及到docker部署,今天写一篇关于微服务项目部署在docker容器中运行,使用github上另外一个比较经典的微服务项目piggyMetric,项目的github地址:htt ...

  5. docker 运行 web 服务和部署 Go web app

    docker 运行 web 服务 和 部署 Go web app 文章目录 docker 运行 web 服务 和 部署 Go web app 一.docker 简介 二.docker 运行 web 服 ...

  6. 多节点OpenStack Charms 部署指南0.0.1.dev303--21--控制器备份和还原

    目录: 第一节 多节点OpenStack Charms 部署指南0.0.1.dev223–1--OpenStack Charms 部署指南 第二节 多节点OpenStack Charms 部署指南0. ...

  7. Docker容器 - DockerFile发布Java微服务并部署到Docker容器

    目录 通过idea新建一个普通微服务项目 一.新建项目 二.POM 三.YML 四.启动类 五.Controller类 启动 通过Dockerfile发布微服务部署到docker容器 一.通过idea ...

  8. @Docker三剑客(三大编排工具):Compose、Machine和Swarm

    文章目录 Docker 一.Docker三大编排工具: 二.Docker Compose [compose的概述] [compose的核心概念] [安装Docker compose] 三.Docker ...

  9. QuikNode高性能以太坊节点服务【详细指南】

    2019独角兽企业重金招聘Python工程师标准>>> 随着Web3的演化,开发者已经开始在以太坊区块链上快速开发去中心化应用.虽然在以太坊上开发dApp很酷,但是要搭建自己的以太坊 ...

最新文章

  1. 《Python数据可视化编程实战》——5.5 用OpenGL制作动画
  2. kvm虚拟机--存储池配置梳理(转)
  3. 中兴手机数据通道打不开_中兴手机有流量,但是数据开不了怎么办?
  4. 腾讯物联网开发者社区平台Tencent Things Network发布 让IoT应用开发快速安全
  5. svn错误提示: svn is scheduled for addition, but is miss
  6. spring aop 中@annotation()和自定义注解的使用
  7. db2 联合数据源 mysql_详解DB2中联合SQL Server数据
  8. Linux下MySQL表名区分大小写
  9. VMware+Ubuntu安装过程,含秘钥
  10. aes简单文本加密工具
  11. Vue使用JsBarcode生成条形码
  12. java排序之选择排序
  13. 制作可被svchost调用的服务(下)
  14. python关键词排名批量查排名_Python批量查询百度排名
  15. 一文带你轻松掌握多种编程范式
  16. 《软件随想录-Joel on Software》书摘
  17. 推荐引擎:从搜索到发现
  18. 【金字塔原理2】剖析金字塔的内部结构
  19. 清晰明了,什么是贝叶斯定理?朴素贝叶斯又是什么?
  20. 法律文件撰写:错别字检查工具的重要性及使用技巧。

热门文章

  1. 电路板焊接技巧实践(没有电烤箱条件下)
  2. 俄罗斯套娃素数c语言解法,求俄罗斯套娃素数(BFS)
  3. 克鲁斯卡尔算法c语言,最小生成树-克鲁斯卡尔(Kruskal)算法
  4. VMware虚拟机扩展磁盘空间Ubuntu(超简单)
  5. Swift5 1.基础介绍
  6. SpixelFCN_Superpixel Segmentation with Fully Convolutional Networks
  7. arctan查表法_查表法
  8. 破解Foxit PDF SDK(DLL) 3.1, PDF转换到图片, 去除水印
  9. 如何正确阅读科研论文——吴恩达
  10. 【电子学会】2022年06月图形化四级 -- 判断闰年