Yang 的笔记
      发布 0.1.0
   Yang DONG
  2020 年 06 月 08 日
                                                                                                                目录
1 Conda                                                                                                             1
  1.1  Conda 安装 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     1
  1.2  Conda 配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     4
  1.3  Conda 使用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     8
  1.4  Conda vs. pip vs. virtualenv 命令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     20
2 Docker                                                                                                           23
  2.1  Docker 安装 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     23
  2.2  Docker 命令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     26
  2.3  Dockerﬁle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
  2.4  Docker Compose 安装 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       41
  2.5  Docker Compose 命令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       41
  2.6  Docker Compose 文件 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       44
3 Git                                                                                                              55
  3.1  命令速查表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .         55
  3.2  使用场景 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      58
  3.3  工作流 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     59
  3.4  Git 服务器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     70
  3.5  Git 客户端 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     72
4 GitHub                                                                                                           79
  4.1  注册 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    79
  4.2  常用配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      80
  4.3  SSH 配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .    80
  4.4  创建仓库 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .      84
5 Markdown                                                                                                         87
                                                                                                                    i
   5.1  生成目录 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       87
   5.2  标题 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     88
   5.3  段落和换行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .          89
   5.4  代码 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     90
   5.5  引用 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     91
   5.6  列表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     92
   5.7  表格 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     94
   5.8  链接 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     95
   5.9  图片 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .     96
   5.10 文字处理 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .       98
   5.11 参考 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6 OpenSSL                                                                                                           101
   6.1  生成证书签名请求 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
   6.2  生成证书 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
   6.3  查看证书 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
   6.4  私钥生成与验证 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
   6.5  证书格式转换 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
   6.6  私有证书颁发机构 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
7 reStructuredText                                                                                                  129
   7.1  标题和分隔线 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
   7.2  段落和换行 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
   7.3  代码 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
   7.4  列表 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
   7.5  表格 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
   7.6  链接 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
   7.7  图片 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
   7.8  角色 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
   7.9  指令 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
   7.10 注释 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
   7.11 参考 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
8 Sphinx                                                                                                            157
   8.1  Docstring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
   8.2  扩展 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
9 Visual Studio Code                                                                                                163
   9.1  VS Code 安装 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
   9.2  VS Code 配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
   9.3  VS Code 扩展 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10 Linux 杂记                                                                                                         171
ii
   10.1 Vim . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
   10.2 Ubuntu 国内源 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
   10.3 ssh-server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
   10.4 Samba 服务器 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
   10.5 tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
   10.6 tmux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
   10.7 ls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
   10.8 chmod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
11 Sphinx & ReadTheDocs & GitHub 集成写作环境                                                                              183
   11.1 注册账号 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
   11.2 创建 GitHub 仓库 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
   11.3 创建 Sphinx 项目 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
   11.4 创建 Read The Docs 项目 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
   11.5 进阶配置 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
12 Apple ID 更换美国区                                                                                                    199
13 杂七杂八                                                                                                              201
   13.1 Ubuntu 网络消失 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
   13.2 Vim 变成 Visual 模式 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
                                                                                                                      iii
iv
                                                        CHAPTER 1
                                                             Conda
                                                      1
Conda 是一个开源的软件包管理系统和环境管理系统，用于安装多个版本的软件包及其依赖关系，并在它们
之间轻松切换。Conda 是为 Python 程序创建的，适用于 Linux，OS X 和 Windows，也可以打包和分发其他软
件。
你可以使用 Conda 为不同的项目隔离开发环境，可以在不同的机器上重现开发环境。
1.1 Conda 安装
Conda 分为两个大版本
    • Miniconda 只包含 Conda 和它的依赖
    • Anaconda 除了 Conda 还包含 7500 多个开源包
Anaconda 又包含命令行版本和图形版本。
  1 https://conda.io/projects/conda/en/latest/
                                                                 1
Yang 的笔记, 发布 0.1.0
小技巧: 关于版本选用：我建议 Windows 环境下使用 Anaconda 图形版本。Linux 环境下使用 Miniconda 。
1.1.1 系统需求
    • 32 或 64 位计算机
    • Miniconda—400 MB 硬盘空间
    • Anaconda—3 GB 以上硬盘空间
    • Windows, macOS, 或 Linux
1.1.2 安装步骤
Windows Anaconda 图形版
   1. 下载 Anaconda 安装程序2
   2. 双击打开安装程序
   3. 点击 Next
   4. 阅读使用许可协议，如果想继续安装点击 I Agree
   5. 选择安装类型，为“所有用户”安装需要管理员权限，如非必要，选择 Just Me 点击 Next
   6. 选择安装位置，默认在“C:\Users\<USERNAME>\Anaconda3”, 点击 Next
   7. 选择是否将 Anaconda 加入 PATH 环境变量。Anaconda 的建议是不加到 PATH 环境变量，以免影响其他
       软件。作为替代，使用 开始菜单 ‣ Anaconda Navigator 和 开始菜单 ‣ Anaconda Prompt 。
       选择是否将 Anaconda 注册为默认的 Python 解释器。勾选，点击 Install。
  2 https://www.anaconda.com/download/#windows
2                                                                 Chapter 1. Conda
                                                                       Yang 的笔记, 发布 0.1.0
   8. 如果想了解 Anaconda 正在安装的包，点击 Show Details
   9. 安装完成点击 Next
  10. 可选项，如果需要安装“PyCharm”          ，点击链接 https://www.anaconda.com/pycharm
      不需要的话，点击 Next
  11. 如果安装成功，这是你将看到“感谢安装 Anaconda3 ”，如果不想看说明，取消勾选，点击 Finish。
  12. 验证安装
       • 开始菜单 ‣ Anaconda Navigator
       • 开始菜单 ‣ Anaconda Prompt
           – 输入 conda list , 如果安装正常，会显示安装的包及它们的版本
           – 输入 python , 如果安装正常，版本信息会包含 Anaconda
           – 输入 anaconda-navigator, 如果安装正常会启动 Anaconda 图形界面
Linux Miniconda
还没装，装的时候再说。
1.1. Conda 安装                                                                           3
Yang 的笔记, 发布 0.1.0
1.2 Conda 配置
1.2.1 PowerShell
我习惯使用 PowerShell 而不是 Anaconda Prompt 。由于我们没有把 Anaconda 加入 PATH 环境变量，当使用
PowerShell 的时候，出现找不到 Conda 的问题。
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
PS C:\Users\yang> conda
conda : The term 'conda' is not recognized as the name of a cmdlet, function, script␣
 ,→ file, or operable program. Check
the spelling of the name, or if a path was included, verify that the path is correct␣
 ,→ and try again.
At line:1 char:1
+ conda
+ ~~~~~
     + CategoryInfo          : ObjectNotFound: (conda:String) [],␣
 ,→ CommandNotFoundException
     + FullyQualifiedErrorId : CommandNotFoundException
可以通过 conda init powershell 来初始化 PowerShell 环境变量。Conda 可执行文件在安装目录的 Scripts
文件夹。
PS C:\Users\yang> C:\Users\yang\Anaconda3\Scripts\conda init powershell
no change       C:\Users\yang\Anaconda3\Scripts\conda.exe
no change       C:\Users\yang\Anaconda3\Scripts\conda-env.exe
no change       C:\Users\yang\Anaconda3\Scripts\conda-script.py
no change       C:\Users\yang\Anaconda3\Scripts\conda-env-script.py
no change       C:\Users\yang\Anaconda3\condabin\conda.bat
no change       C:\Users\yang\Anaconda3\Library\bin\conda.bat
no change       C:\Users\yang\Anaconda3\condabin\_conda_activate.bat
no change       C:\Users\yang\Anaconda3\condabin\rename_tmp.bat
no change       C:\Users\yang\Anaconda3\condabin\conda_auto_activate.bat
no change       C:\Users\yang\Anaconda3\condabin\conda_hook.bat
no change       C:\Users\yang\Anaconda3\Scripts\activate.bat
no change       C:\Users\yang\Anaconda3\condabin\activate.bat
no change       C:\Users\yang\Anaconda3\condabin\deactivate.bat
modified        C:\Users\yang\Anaconda3\Scripts\activate
modified        C:\Users\yang\Anaconda3\Scripts\deactivate
modified        C:\Users\yang\Anaconda3\etc\profile.d\conda.sh
modified        C:\Users\yang\Anaconda3\etc\fish\conf.d\conda.fish
                                                                                   (下页继续)
4                                                                        Chapter 1. Conda
                                                                    Yang 的笔记, 发布 0.1.0
                                                                                (续上页)
no change     C:\Users\yang\Anaconda3\shell\condabin\Conda.psm1
modified      C:\Users\yang\Anaconda3\shell\condabin\conda-hook.ps1
modified      C:\Users\yang\Anaconda3\Lib\site-packages\xontrib\conda.xsh
modified      C:\Users\yang\Anaconda3\etc\profile.d\conda.csh
modified      D:\Documents\WindowsPowerShell\profile.ps1
==> For changes to take effect, close and re-open your current shell. <==
重新打开 PowerShell ，输入 conda 验证
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Try the new cross-platform PowerShell https://aka.ms/pscore6
Loading personal and system profiles took 1289ms.
(base) PS C:\Users\yang> conda -V
conda 4.7.12
如果不想每次一启动 PowerShell 就自动激活 Base 环境
conda config --set auto_activate_base false
如果又想启动了
conda config --set auto_activate_base true
1.2.2 下载频道
下载频道就是选择从哪里下载包，国外的比较慢，推荐使用国内的源，比如清华的:
https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
可以使用三种方式配置：
    • 图形界面
      开始菜单 ‣ Anaconda Navigator
1.2. Conda 配置                                                                        5
Yang 的笔记, 发布 0.1.0
    点击 Channels
    点击 Add，并加入新的源地址
6                   Chapter 1. Conda
                                                                     Yang 的笔记, 发布 0.1.0
    • 命令行
      使用 PowerShell
      conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/
       ,→free/
      conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/
       ,→main/
    • 修改配置文件
      配置文件一般位于 C:\\Users\\<USERNAME>\\.condarc
      Channles 部分默认为:
      channels:
         - defaults
      修改为:
      channels:
         - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
         - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
         - defaults
1.2. Conda 配置                                                                         7
Yang 的笔记, 发布 0.1.0
1.2.3 环境位置
Anaconda 创建的环境默认位置是 C:\Users\<USERNAME>\Anaconda3\envs ，如果想修改创建环境的默
认位置，可以通过修改配置文件 .condarc 来实现:
ssl_verify: true
channels:
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
  - https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
  - defaults
envs_dirs:
  - E:\conda\envs
  - C:\Users\yang\Anaconda3\envs
  注意: 文件夹位置很重要，使用 conda create 命令默认创建在 envs 的第一个文件夹，在上面例子
  里就是 E:\conda\envs。
  如果本例写成:
  envs_dirs:
    - C:\Users\yang\Anaconda3\envs
    - E:\conda\envs
  则 使 用 conda create --name <ENVNAME> python 会 把 环 境 创 建 在 C:\Users\yang\
  Anaconda3\envs, 想 在 E:\conda\envs 中 创 建 环 境 需 要 使 用 conda create --prefix
  E:\conda\envs\<ENVNAME> python , 或 者 从 命 令 行 进 入 E:\conda\envs 文 件 夹， 再 使 用
  conda create --prefix <ENVNAME> python
1.3 Conda 使用
1.3.1 环境
创建环境
   • 图形界面
     点击 Environments
8                                                                Chapter 1. Conda
                                                                   Yang 的笔记, 发布 0.1.0
      点击 Create ，输入环境名，选择 Python 版本
      点击 Create
    • 命令行
      使用 conda create --name <ENVNAME> python=3.7 , Python 的版本号根据需要更改，在命令
      行询问 ProProceed ([y]/n)? 时，输入 y
      Windows PowerShell
      Copyright (C) Microsoft Corporation. All rights reserved.
      Try the new cross-platform PowerShell https://aka.ms/pscore6
      Loading personal and system profiles took 1183ms.
                                                                              (下页继续)
1.3. Conda 使用                                                                       9
Yang 的笔记, 发布 0.1.0
                                                                                    (续上页)
    (base) PS C:\Users\yang> conda create --name hello python=3.7
    Collecting package metadata (current_repodata.json): done
    Solving environment: done
    ==> WARNING: A newer version of conda exists. <==
    current version: 4.7.12
    latest version: 4.8.2
    Please update conda by running
         $ conda update -n base -c defaults conda
    ## Package Plan ##
    environment location: E:\conda\envs\hello
    added / updated specs:
         - python=3.7
    The following NEW packages will be INSTALLED:
    ca-certificates      anaconda/pkgs/main/win-64::ca-certificates-2020.1.1-0
    certifi              anaconda/pkgs/main/win-64::certifi-2019.11.28-py37_0
    openssl              anaconda/pkgs/main/win-64::openssl-1.1.1d-he774522_4
    pip                  anaconda/pkgs/main/win-64::pip-20.0.2-py37_1
    python               anaconda/pkgs/main/win-64::python-3.7.6-h60c2a47_2
    setuptools           anaconda/pkgs/main/win-64::setuptools-45.2.0-py37_0
    sqlite               anaconda/pkgs/main/win-64::sqlite-3.31.1-he774522_0
    vc                   anaconda/pkgs/main/win-64::vc-14.1-h0510ff6_4
    vs2015_runtime       anaconda/pkgs/main/win-64::vs2015_runtime-14.16.27012-hf0eaf9b_
     ,→1
    wheel                anaconda/pkgs/main/win-64::wheel-0.34.2-py37_0
    wincertstore         anaconda/pkgs/main/win-64::wincertstore-0.2-py37_0
    Proceed ([y]/n)? y
    Preparing transaction: done
    Verifying transaction: done
    Executing transaction: done
    #
    # To activate this environment, use
    #
    #       $ conda activate hello
                                                                                  (下页继续)
10                                                                      Chapter 1. Conda
                                                       Yang 的笔记, 发布 0.1.0
                                                                    (续上页)
      #
      # To deactivate an active environment, use
      #
      #     $ conda deactivate
查看环境
    • 图形界面
      点击 Environments
      点击想查看的环境。
    • 命令行
      使用 conda env list 或 conda info --envs 命令
      (base) PS C:\Users\yang> conda env list
      # conda environments:
      #
      base                  *  C:\Users\yang\Anaconda3
      hello                    E:\conda\envs\hello
      (base) PS C:\Users\yang> conda info --envs
      # conda environments:
      #
                                                                  (下页继续)
1.3. Conda 使用                                                          11
Yang 的笔记, 发布 0.1.0
                                                                           (续上页)
     base                   *  C:\Users\yang\Anaconda3
     hello                     E:\conda\envs\hello
     其中带 * 的表示当前激活的环境。
激活环境
   • 图形界面
     在 Environments 中点击想要激活的环境，三角形 ‣ Open Terminal 或者 三角形 ‣ Open Python
   • 命令行
     使用 conda activate <env name> 激活环境，使用 conda deactivate <env name> 去激活。
     (base) PS C:\Users\yang> conda activate hello
     (hello) PS C:\Users\yang>
     括号内的是当前激活环境。
迁移环境
   • 克隆
     如果只是想在本机上创建一个相同环境，可以克隆现有环境。例如克隆 base 环境:
     conda create --name <ENVNAME> --clone base
   • 操作系统一致
     如果想在使用同一操作系统的不同计算机间迁移，可以导出 spec list 文件。
       – 导出
          conda list --explicit > spec-list.txt
       – 导入
          conda create --name <ENVNAME> --file spec-list.txt
   • 操作系统不一致
     使用不同操作系统间进行迁移，需要导出 environment.yml 文件。
       – 导出
12                                                              Chapter 1. Conda
                                                                    Yang 的笔记, 发布 0.1.0
          conda env export > environment.yml
        – 导入
          conda env create -f environment.yml
      重要: 事实上，这样导出是不行的，因为这会导出所有包及依赖，很多都是操作系统不兼容的。。                              。
      要想使用非操作系统相关的，只需要导出你通过 install 命令安装的包，不含它们的依赖，不含创建
      环境的依赖。这种情况下导出时要使用:
      conda env export --from-history > environment.yml
      然后要小修补一下，比如去掉 Preﬁx ，是否要去掉添加的国内加速频道，如果国内使用，就保留，如果
      放国外，可能默认的频道更快。以 Read the Docs 为例，使用清华的镜像频道比默认频道慢接近一个量
      级。
    • 完全打包
      适合在没网或者网不好的情况下，把所有的二进制和安装的包都存档，这个默认安装不支持，需要安
      装 conda-pack 包。
        – 安装 conda-pack 包
          conda install -c conda-forge conda-pack
          或者:
          pip install conda-pack
        – 打包环境
          # Pack environment my_env into my_env.tar.gz
          $ conda pack -n my_env
          # Pack environment my_env into out_name.tar.gz
          $ conda pack -n my_env -o out_name.tar.gz
          # Pack environment located at an explicit path into my_env.tar.gz
          $ conda pack -p /explicit/path/to/my_env
        – 安装环境
1.3. Conda 使用                                                                      13
Yang 的笔记, 发布 0.1.0
         # Unpack environment into directory `my_env`
         $ mkdir -p my_env
         $ tar -xzf my_env.tar.gz -C my_env
         # Use Python without activating or fixing the prefixes. Most Python
         # libraries will work fine, but things that require prefix cleanups
         # will fail.
         $ ./my_env/bin/python
         # Activate the environment. This adds `my_env/bin` to your path
         $ source my_env/bin/activate
         # Run Python from in the environment
         (my_env) $ python
         # Cleanup prefixes from in the active environment.
         # Note that this command can also be run without activating the environment
         # as long as some version of Python is already installed on the machine.
         (my_env) $ conda-unpack
删除环境
   • 图形界面
     在 Environments 中点击想要删除的环境，点击 Remove
   • 命令行
     conda remove --name <ENVNAME> --all
1.3.2 包
查看包
   • 图形界面
     在 Environments 中点击想要查看的环境，右侧有包列表，可以在下拉菜单中选择 Installed，Not installed，
     Updatable，Selected，及 All 进行过滤
   • 命令行
     使用 conda list 命令
14                                                                     Chapter 1. Conda
                                                                    Yang 的笔记, 发布 0.1.0
      (hello) PS C:\Users\yang> conda list
      # packages in environment at E:\conda\envs\hello:
      #
      # Name                     Version                  Build Channel
      ca-certificates            2020.1.1                     0   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      certifi                    2019.11.28              py37_0   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      openssl                    1.1.1d              he774522_4   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      pip                        20.0.2                  py37_1   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      python                     3.7.6               h60c2a47_2   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      setuptools                 45.2.0                  py37_0   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      sqlite                     3.31.1              he774522_0   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      vc                         14.1                h0510ff6_4   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      vs2015_runtime             14.16.27012         hf0eaf9b_1   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      wheel                      0.34.2                  py37_0   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
      wincertstore               0.2                     py37_0   https://mirrors.tuna.
       ,→tsinghua.edu.cn/anaconda/pkgs/main
安装包
    • 图形界面
      在 Environments 中点击想要安装包的环境，在下拉菜单中选择 Not installed，然后在搜索栏搜索想要安装
      的包，比如“numpy”
1.3. Conda 使用                                                                        15
Yang 的笔记, 发布 0.1.0
    点击 numpy，在选项菜单中选择 mark for installation
    点击 Apply
    点击 Apply
16                                          Chapter 1. Conda
                                                                     Yang 的笔记, 发布 0.1.0
      安装完毕后，在 Environments 中点击想要安装包的环境，在下拉菜单中选择 Installed 查看
    • 命令行
      搜索包，使用命名 conda search PACKAGENAME, 例如:
      (hello) PS C:\Users\yang> conda search beau
      Loading channels: done
      No match found for: beau. Search: *beau*
      # Name                       Version           Build  Channel
      beautiful-soup                 4.3.1          py26_0  anaconda/pkgs/free
      beautiful-soup                 4.3.1          py27_0  anaconda/pkgs/free
      ...
      beautifulsoup4                 4.8.2          py38_0  anaconda/pkgs/main
      beautifulsoup4                 4.8.2          py38_0  pkgs/main
      安装包，使用命令 conda install PACKAGENAME==Rev, 例如:
      (hello) PS C:\Users\yang> conda install beautifulsoup4
      Conda 不包含的包，可以用 pip install PACKAGENAME=Rev 安装, 例如:
      doc) PS C:\Users\yang> pip install doc8
删除包
    • 图形界面
      在 Environments 中点击想要安装包的环境，在下拉菜单中选择 Installed，然后在搜索栏搜索想要安装的
      包，比如“numpy”
      点击 numpy，在选项菜单中选择 mark for removal
      点击 Apply
    • 命令行
      Conda 使用命令:
      conda uninstall PACKAGENAME
      pip 使用命令:
      pip uninstall PACKAGENAME
1.3. Conda 使用                                                                       17
Yang 的笔记, 发布 0.1.0
1.3.3 其他 Conda 命令
    • 升级
       使用 conda update 命令
       升级 conda:
       conda update conda
       升级 anaconda:
       conda update anaconda
       升级所有包:
       conda update -n <ENVIRONMENT_NAME> --all
参见:
了解更多命令，参见: conda cheat sheet3
  3 https://conda.io/projects/conda/en/latest/_downloads/843d9e0198f2a193a3484886fa28163c/conda-cheatsheet.pdf
18                                                                                                             Chapter 1. Conda
              Yang 的笔记, 发布 0.1.0
1.3. Conda 使用                19
Yang 的笔记, 发布 0.1.0
1.4 Conda vs. pip vs. virtualenv 命令
                        表 1.1: Conda vs. pip vs. virtualenv 命令
 任务                Conda 包和环境管理器             Pip 包管理器命令            virtualenv 环境管理器命
                   命令                                              令
 安装包               conda install             pip install           X
                   $PACKAGE_NAME             $PACKAGE_NAME
 升级包               conda update              pip install           X
                   --name                    --upgrade
                   $ENVIRONMENT_NAME         $PACKAGE_NAME
                   $PACKAGE_NAME
 升级包管理器            conda update              Linux/macOS:      pip X
                   conda                     install -U pip
                                             Win: python -m pip
                                             install -U pip
 卸载包               conda remove              pip uninstall         X
                   --name                    $PACKAGE_NAME
                   $ENVIRONMENT_NAME
                   $PACKAGE_NAME
 创建环境              conda create              X                     cd $ENV_BASE_DIR;
                   --name                                          virtualenv
                   $ENVIRONMENT_NAME                               $ENVIRONMENT_NAME
                   python
 激活环境              conda activate            X                     source
                                         4
                   $ENVIRONMENT_NAME                               $ENV_BASE_DIR/
                                                                   $ENVIRONMENT_NAME/
                                                                   bin/activate
 去激活               conda deactivate          X                     deactivate
 搜索可用包             conda search              pip search            X
                   $SEARCH_TERM              $SEARCH_TERM
 从指定源安装包           conda install             pip install           X
                   --channel $URL            --index-url $URL
                   $PACKAGE_NAME             $PACKAGE_NAME
 已安装包列表            conda list --name         pip list              X
                   $ENVIRONMENT_NAME
 创建依赖文件            conda list                pip freeze            X
                   --export
 环境列表              conda info --envs         X                     安装 virtualenv wrapper,
                                                                   然后 lsvirtualenv
 安装其他包管理器          conda install pip         pip install conda     X
 安装 Python         conda install             X                     X
20                 python=x.x                                            Chapter 1. Conda
 升级 Python         conda update              X                     X
                   python5
                                                                            Yang 的笔记, 发布 0.1.0
   4 conda activate 适用于 conda 4.6 版本及以上。4.6 之前的版本:
     • Windows: activate
     • Linux and macOS: source activate
   5 conda update python 适用于同个大版本的 Python 更新，比如 Python 2.x 更新到 Python 2.x 最新版本，或者 Python 3.x 更新到
Python 3.x 最新版本
1.4. Conda vs. pip vs. virtualenv 命令                                                           21
Yang 的笔记, 发布 0.1.0
22                 Chapter 1. Conda
                                                                          CHAPTER  2
                                                                              Docker
                     6
2.1 Docker 安装
   1. 卸载旧版本:
       sudo apt-get remove docker docker-engine docker.io containerd runc
   2. 更新 apt 包索引:
       sudo apt-get update
   3. 安装以下包以使 apt 可以通过 HTTPS 使用源仓库:
       sudo apt-get install -y \
           apt-transport-https \
                                                                               (下页继续)
  6 https://www.docker.com/
                                                                                   23
Yang 的笔记, 发布 0.1.0
                                                                                                (续上页)
        ca-certificates \
        curl \
        gnupg-agent \
        software-properties-common
   4. 添加 Docker 官方的 GPG 密钥:
      curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
      检查你现在有包含指纹 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 的密钥:
      sudo apt-key fingerprint 0EBFCD88
      检查输出:
      pub   rsa4096 2017-02-22 [SCEA]
            9DC8 5822 9FC7 DD38 854A        E2D8 8D81 803C 0EBF CD88
      uid           [ unknown] Docker Release (CE deb) <docker@docker.com>
      sub   rsa4096 2017-02-22 [S]
   5. 设置源仓库版本:
      sudo add-apt-repository \
       "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
       $(lsb_release -cs) \
       stable"
      参数：
        • arch: amd64 (x86_64/amd64), armhf, arm64, ppc64el (IBM Power), s390x (IBM Z)
        • repository: stable, nightly, test
   6. 再更新一下 apt 包索引:
      sudo apt-get update
   7. 安装最新版本的 Docker CE:
      sudo apt-get install docker-ce docker-ce-cli containerd.io
      要安装指定版本，先列出你源仓库中的版本列表:
      apt-cache madison docker-ce
      输出:
24                                                                                  Chapter 2. Docker
                                                                      Yang 的笔记, 发布 0.1.0
      docker-ce | 5:18.09.1~3-0~ubuntu-xenial | https://download.docker.com/linux/
       ,→ubuntu   xenial/stable amd64 Packages
      docker-ce | 5:18.09.0~3-0~ubuntu-xenial | https://download.docker.com/linux/
       ,→ubuntu   xenial/stable amd64 Packages
      docker-ce | 18.06.1~ce~3-0~ubuntu        | https://download.docker.com/linux/
       ,→ubuntu   xenial/stable amd64 Packages
      docker-ce | 18.06.0~ce~3-0~ubuntu        | https://download.docker.com/linux/
       ,→ubuntu   xenial/stable amd64 Packages
      ...
      安 装 指 定 版 本, 比 如 5:18.09.1~3-0~ubuntu-xenial, 将 版 本 字 符 串 替 换 下 面 名 例 中 的
      <VERSION_STRING>
      sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING>␣
       ,→containerd.io
   8. 验证安装
          • 检查服务状态:
            sudo systemctl status docker
          • 如服务未启动，启动服务:
            sudo systemctl start docker
          • 测试安装:
            sudo docker run hello-world
   9. 建立 Docker 用户组，并将当前用户加入用户组:
      sudo groupadd docker
      sudo usermod -a -G docker $USER
      默认情况下，docker 命令会使用 Unix socket 与 Docker 引擎通讯。只有 root 用户和 docker 组的用户才可
      以访问 Docker 引擎的 Unix socket。出于安全考虑，一般 Linux 系统上不会直接使用 root 用户。因此，更
      好地做法是将需要使用 docker 的用户加入 docker 用户组。
  10. 配置 Docker 在开机时启动:
      sudo systemctl enable docker
  11. 国内加速
      修改 /etc/docker/daemon.json 文件，如果文件不存在就创建一个新的，添加如下内容:
2.1. Docker 安装                                                                        25
Yang 的笔记, 发布 0.1.0
     {
       "registry-mirrors": [
         "https://registry.docker-cn.com/",
         "http://hub-mirror.c.163.com"
       ]
     }
     然后重启 Docker 服务:
     sudo systemctl daemon-reload
     sudo systemctl restart docker
                                     表 2.1: Docker 国内源列表
                     名称            地址
                     Docker 中国     https://registry.docker-cn.com
                     网易            http://hub-mirror.c.163.com
                     中国科技大学        https://docker.mirrors.ustc.edu.cn
2.2 Docker 命令
2.2.1 镜像
   • 列出本地镜像:
     docker images
   • 拉取镜像:
     docker pull <name:tag>
   • 创建镜像:
     docker build -t <name:tag> .
       – -t: 指定要创建的目标镜像名及标签
       – -f: 指定要使用的 Dockerﬁle 路径，默认为当前目录
       – . : 上下文路径，是指 docker 在构建镜像，有时候想要使用到本机的文件（比如复制）                    ，docker build
         命令得知这个路径后，会将路径下的所有内容打包。上下文路径下不要放无用的文件，因为会一
         起打包发送给 docker 引擎，如果文件过多会造成过程缓慢。
   • 删除镜像:
26                                                                  Chapter 2. Docker
                                                                     Yang 的笔记, 发布 0.1.0
      docker rmi <IMAGE ID>
      删除镜像前要先删除使用镜像的容器
2.2.2 容器
    • 列出所有容器:
      docker ps -a
    • 启动容器:
      docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
      常用选项:
        – -d: 后台运行容器，并返回容器 ID
        – -i: 以交互模式运行容器，通常与 -t 同时使用
        – -t: 为容器重新分配一个伪输入终端，通常与 -i 同时使用
        – --name="<name>": 为容器指定一个名称
        – -P: 随机端口映射，容器内部端口随机映射到主机的高端口
        – -p, --expose: 指定端口映射，格式为：主机 (宿主) 端口: 容器端口
        – --volume, -v: 绑定一个卷
        – --hostname , -h: 容器主机名
        – --network: 连接到一个网络
        – --mount: 挂载卷，主机目录和 tmpfs 到容器
      例如:
      yang@SkyLab:~$ docker run -it nginx:latest /bin/bash
      Unable to find image 'nginx:latest' locally
      latest: Pulling from library/nginx
      68ced04f60ab: Pull complete
      28252775b295: Pull complete
      a616aa3b0bf2: Pull complete
      Digest: sha256:2539d4344dd18e1df02be842ffc435f8e1f699cfc55516e2cf2cb16b7a9aea0b
      Status: Downloaded newer image for nginx:latest
      root@15730a99e735:/# exit
      exit
      yang@SkyLab:~$
2.2. Docker 命令                                                                        27
Yang 的笔记, 发布 0.1.0
   • 进入容器:
     docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
     参数
         – -d: 分离模式: 在后台运行
         – -i: 即使没有附加也保持 STDIN 打开
         – -t: 分配一个伪终端
     例如:
     yang@SkyLab:~$ docker ps -a
     CONTAINER ID          IMAGE                 COMMAND                  CREATED     ␣
      ,→        STATUS                   PORTS               NAMES
     461eaeab82a7          nginx:latest          "/bin/bash"              7 seconds␣
      ,→ago        Up 5 seconds             80/tcp              epic_driscoll
     yang@SkyLab:~$ docker exec -it 461eaeab82a7 /bin/bash
     root@461eaeab82a7:/# exit
     exit
     yang@SkyLab:~$
   • 停止容器:
     docker stop <CONTAINER ID>
   • 启动停止的容器:
     docker start <CONTAINER ID>
   • 重启容器:
     docker restart <CONTAINER ID>
   • 删除容器:
     docker rm <CONTAINER ID>
     删除所有已停止的容器:
     docker rm $(docker ps -qa)
   • 获取容器的日志:
     docker logs [OPTIONS] CONTAINER
     常用选项:
28                                                                     Chapter 2. Docker
                                                         Yang 的笔记, 发布 0.1.0
        – -f: 跟踪日志输出
        – --since: 显示某个开始时间的所有日志
        – -t: 显示时间戳
        – --tail: 仅列出最新 N 条容器日志
    • 获取容器/镜像的元数据:
      docker inspect [OPTIONS] NAME|ID [NAME|ID...]
      常用选项:
        – -f: 指定返回值的模板文件。
        – -s: 显示总的文件大小。
        – --type: 为指定类型返回 JSON。
2.2.3 网络
    • 列出所有网络:
      docker network ls
    • 创建网络:
      docker network create [OPTIONS] NETWORK
      例如:
      $ docker network create \
        --driver=bridge \
        --subnet=172.28.0.0/16 \
        --ip-range=172.28.5.0/24 \
        --gateway=172.28.5.254 \
        br0
        – --driver: 可以是 bridge 或 overlay, 默认为 bridge。
            * bridge: 依附于运行 Docker Engine 的单台主机上;
            * overlay: 网络能够覆盖运行各自 Docker Engine 的多主机环境中。
        – --subnet: 子网设置
        – --ip-range: IP 段
        – --gateway: 网关
        – --internal: 禁止外部连接
2.2. Docker 命令                                                          29
Yang 的笔记, 发布 0.1.0
       – --ipv6: 使能 ipv6
       – --attachable: 使能手动容器连接
   • 查看网络信息:
     docker network inspect [OPTIONS] NETWORK [NETWORK...]
     例如:
     docker network inspect my-bridge
   • 连接一个容器到网络:
     docker network connect [OPTIONS] NETWORK CONTAINER
     连接一个正在运行的容器:
     docker network connect multi-host-network container1
     在容器启动时连接一个网络:
     docker run -itd --network=multi-host-network busybox
     给容器指定一个 ip 地址:
     docker network connect --ip 10.10.36.122 multi-host-network container2
     使用 --link 选项，连接另一个容器
         docker network connect –link container1:c1 multi-host-network container2
   • 断开容器与网络的连接:
     docker network disconnect [OPTIONS] NETWORK CONTAINER
     例如:
     docker network disconnect multi-host-network container1
   • 删除网络:
     docker network rm NETWORK [NETWORK...]
     例如:
     docker network rm my-network
   • 删除所有未用的网络:
30                                                                                Chapter 2. Docker
                                                                     Yang 的笔记, 发布 0.1.0
      docker network prune [OPTIONS]
      例如:
      docker network prune
      WARNING! This will remove all networks not used by at least one container.
      Are you sure you want to continue? [y/N] y
      Deleted Networks:
      n1
      n2
2.2.4 数据卷
volumes 是 Docker 数据持久化机制。bind mounts 依赖主机目录结构，volumes 完全由 Docker 管理。
volumes 有以下优点：
    • Volumes 更容易备份和移植。
    • 可以通过 Docker CLI 或 API 进行管理
    • Volumes 可以无区别的工作中 Windows 和 Linux 下。
    • 多个容器共享 Volumes 更安全。
    • Volume 驱动可以允许你把数据存储到远程主机或者云端，并且加密数据内容，以及添加额外功能。
    • 一个新的数据内容可以由容器预填充。
    • volumes 不会增加容器的大小，生命周期独立与容器。
2.2. Docker 命令                                                                      31
Yang 的笔记, 发布 0.1.0
如果你的容器产生不需要持久化数据，请使用 tmpfs mount 方式，可以避免容器的写入层数据写入。
挂载卷：
   • -v,--volume: 由 3 部分参数组成，使用 : 间隔, 顺序不能颠倒
         – 第一个部分是 volumes 名字，在宿主机上具有唯一性。匿名卷名字系统给出。
         – 第二部分是挂载到容器里的文件或文件夹路径。
         – 第三部分是可选项列表分隔符
   • —mount: 由多个键值对组成，<key>=<value>。
         – type: 可以是 bind,volume 或者 tmpfs
         – source, src: volumes 的名字，匿名 volume 可以省略。
         – destination, dst, target: 挂载到容器中的文件或目录路径
         – readonly: 指定挂载在容器中为只读。
         – volume-opt: 可选属性，可以多次使用。
     例如:
     docker run --read-only --mount type=volume,target=/icanwrite busybox touch /
      ,→icanwrite/here
     docker run -t -i --mount type=bind,src=/data,dst=/data busybox sh
  注意: Docker 建议使用 --mount 来挂载
卷命令：
   • 列出所有卷:
     docker volume ls
   • 创建卷:
     docker volume create [OPTIONS] [VOLUME]
     例如:
     $ docker volume create hello
     hello
     $ docker run -d -v hello:/world busybox ls /world
32                                                                     Chapter 2. Docker
                                                                     Yang 的笔记, 发布 0.1.0
    • 显示卷信息:
      docker volume inspect [OPTIONS] VOLUME [VOLUME...]
      例如:
      docker volume inspect hello
    • 删除卷:
      docker volume rm [OPTIONS] VOLUME [VOLUME...]
      例如:
      docker volume rm hello
    • 删除所有未用卷:
      docker volume prune [OPTIONS]
      例如:
      docker volume prune
      WARNING! This will remove all local volumes not used by at least one container.
      Are you sure you want to continue? [y/N] y
      Deleted Volumes:
      07c7bdf3e34ab76d921894c2b834f073721fccfbbcba792aa7648e3a7a664c2e
      my-named-vol
      Total reclaimed space: 36 B
2.3 Dockerﬁle
Dockerﬁle 是一个用来构建镜像的文本文件，文本内容包含了一条条构建镜像所需的指令和说明。
举个定制一个 nginx 镜像的例子, Dockerﬁle:
FROM nginx
RUN echo '这是一个本地构建的 nginx 镜像' > /usr/share/nginx/html/index.html
创建过程:
2.3. Dockerﬁle                                                                        33
Yang 的笔记, 发布 0.1.0
yang@SkyLab:~/workspace$ mkdir docker_example
yang@SkyLab:~/workspace$ cd docker_example/
yang@SkyLab:~/workspace/docker_example$ ls
yang@SkyLab:~/workspace/docker_example$ touch Dockerfile
yang@SkyLab:~/workspace/docker_example$ vi Dockerfile
yang@SkyLab:~/workspace/docker_example$ cat Dockerfile
FROM nginx
RUN echo ' 这是一个本地构建的 nginx 镜像' > /usr/share/nginx/html/index.html
yang@SkyLab:~/workspace/docker_example$ docker build -t dockfile_demo .
Sending build context to Docker daemon   2.048kB
Step 1/2 : FROM nginx
---> 6678c7c2e56c
Step 2/2 : RUN echo ' 这是一个本地构建的 nginx 镜像' > /usr/share/nginx/html/index.html
---> Running in 2386a741e098
Removing intermediate container 2386a741e098
---> bf76644412f8
Successfully built bf76644412f8
Successfully tagged dockfile_demo:latest
yang@SkyLab:~/workspace/docker_example$ docker images
REPOSITORY               TAG                 IMAGE ID           CREATED            ␣
 ,→ SIZE
dockfile_demo            latest              bf76644412f8       11 seconds ago     ␣
 ,→ 127MB
nginx                    latest              6678c7c2e56c       6 days ago         ␣
 ,→ 127MB
   注意: Dockerﬁle 的指令每执行一次都会在 docker 上新建一层。所以过多无意义的层，会造成镜像膨胀
   过大。可以用 && 符号连接命令，这样执行后，只会创建 1 层镜像。
2.3.1 Dockerﬁle 格式
通用格式为:
# directive=value   解析指令（Parser directives）
# 注释
INSTRUCTION arguments
     • 指令 (INSTRUCTION): 不区分大小写，习惯上使用大写，以便更容易和参数区分开。
       Docker 按顺序运行 Dockerﬁle 里的指令，Dockerﬁle 必须以 FROM 指令开始，在 FROM 指令前只可以存在
       注释，解析指令，或者全局变量（ARG 指令）。
     • 注释: 以 # 开始的行，如果不是解析指令，就当作注释处理，注释中不支持行连接符 (\)。
34                                                                    Chapter 2. Docker
                                                                  Yang 的笔记, 发布 0.1.0
    • 解析指令：目前只支持两种
         – syntax：这个指令只有在使用 BuildKit 后端时才可以使用，用来指定本地的 Dockerﬁle builder，
           基本用不上:
           # syntax=[remote image reference]
           # syntax=docker/dockerfile
           # syntax=docker/dockerfile:1.0
           # syntax=docker.io/docker/dockerfile:1
           # syntax=docker/dockerfile:1.0.0-experimental
           # syntax=example.com/user/repo:tag@sha256:abcdef...
         – escape: 用来设置 Dockerﬁle 中的转义符，默认转义符为 \, 比如 Windows 下 \ 会被用在路径, 这
           时就需要更换转义符:
           # escape=`
           或者:
           # escape=\
    • .dockerignore 文件: 用来定义哪些文件或者目录不被加入到 docker 镜像中。
2.3.2 Dockerﬁle 指令
    • FROM: 定制的镜像都是基于 FROM 的镜像。
      格式:
      FROM [--platform=<platform>] <image> [AS <name>]
      FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
      FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]
         – 一个 Dockerﬁle 可以包含多行 FROM 指令来创建多个镜像或者一个作为另一的依赖项。
         – name 是一个可选项。这个名字可以用于后面的 FROM 或者 COPY --from=<name|index> 指令
         – ARG 是唯一一个可以放在 FROM 指令前的指令，例如:
           ARG    CODE_VERSION=latest
           FROM base:${CODE_VERSION}
           CMD    /code/run-app
           FROM extras:${CODE_VERSION}
           CMD    /code/run-extras
2.3. Dockerﬁle                                                                   35
Yang 的笔记, 发布 0.1.0
          在 FROM 指令前的 ARG 指令不包含在构建阶段，所以不能被 FROM 后的指令使用，要想继续使用，
          需要在 FROM 后定义一个不带值的, 像这样:
          ARG VERSION=latest
          FROM busybox:$VERSION
          ARG VERSION
          RUN echo $VERSION > image_version
   • RUN: 在现有镜像上的新层执行命令并提交结果，这个包含提交结果的新镜像将被用于下一步构建
     格式：
       – shell 格式:
          # < 命令行命令> 等同于在终端操作的 shell 命令, Linux 的 /bin/sh -c， Windows 的 cmd␣
           ,→/S /C
          RUN <命令行命令>
       – exec 格式:
          RUN ["可执行文件", "参数 1", "参数 2"]
          例如:
          RUN ["./test.php", "dev", "offline"]
          等价于:
          RUN ./test.php dev offline
   • CMD: 主要目的是为执行容器提供默认值。这些默认值可以包括可执行文件，也可以省略可执行文件，
     在这种情况下，还必须指定 ENTRYPOINT 指令。
     为启动的容器指定默认要运行的程序，程序运行结束，容器也就结束。CMD 指令指定的程序可被 docker
     run 命令行参数中指定要运行的程序所覆盖。如果 Dockerﬁle 中如果存在多个 CMD 指令，仅最后一个
     生效。
     格式:
     CMD <shell 命令> <param1> <param2>
     CMD ["< 可执行文件或命令>","<param1>","<param2>",...]
     CMD ["<param1>","<param2>",...]   # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数
     推荐使用第二种格式，执行过程比较明确。第一种格式实际上在运行的过程中也会自动转换成第二种
     格式运行，并且默认可执行文件是 sh。
36                                                              Chapter 2. Docker
                                                             Yang 的笔记, 发布 0.1.0
        注意: CMD 和 RUN 指令区别在于，运行时间不同
           – CMD 在容器运行时运行。
           – RUN 在镜像构建时运行。
    • ENTRYPOINT: 类似于 CMD 指令，但其不会被 docker run 的命令行参数指定的指令所覆盖，而且
      这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。如果运行 docker run 时使用了
      --entrypoint 选项，此选项的参数可当作要运行的程序覆盖 ENTRYPOINT 指令指定的程序。如果
      Dockerﬁle 中如果存在多个 ENTRYPOINT 指令，仅最后一个生效。
      格式:
      ENTRYPOINT command param1 param2
      ENTRYPOINT ["<executeable>","<param1>","<param2>",...]
      可以搭配 CMD 命令使用：一般是变参才会使用 CMD ，这里的 CMD 等于是在给 ENTRYPOINT 传参，例
      如使用如下 dockerﬁle 生成镜像 nginx:test
      FROM nginx
      ENTRYPOINT ["nginx", "-c"] # 定参
      CMD ["/etc/nginx/nginx.conf"] # 变参
      如果使用:
      docker run nginx:test
      容器内会默认运行以下命令，启动主进程:
      nginx -c /etc/nginx/nginx.conf
      如果使用:
      docker run  nginx:test -c /etc/nginx/new.conf
      容器内会默认运行以下命令，启动主进程 (/etc/nginx/new.conf: 假设容器内已有此文件):
      nginx -c /etc/nginx/new.conf
    • LABEL: 用于给镜像添加元数据。
      格式:
      LABEL <key>=<value> <key>=<value> <key>=<value> ...
      例如:
2.3. Dockerﬁle                                                              37
Yang 的笔记, 发布 0.1.0
     LABEL "com.example.vendor"="ACME Incorporated"
     LABEL com.example.label-with-value="foo"
     LABEL version="1.0"
     LABEL description="This text illustrates \
     that label-values can span multiple lines."
     要查看镜像的标签，可以使用 docker inspect 命令, 显示如下:
     "Labels": {
           "com.example.vendor": "ACME Incorporated"
           "com.example.label-with-value": "foo",
           "version": "1.0",
           "description": "This text illustrates that label-values can span multiple␣
      ,→lines.",
           "multi.label1": "value1",
           "multi.label2": "value2",
           "other": "value3"
     },
   • COPY: 复制指令，从上下文目录中复制文件或者目录到容器里指定路径。
     格式:
     COPY [--chown=<user>:<group>] <源路径 1>...      <目标路径>
     COPY [--chown=<user>:<group>] ["< 源路径 1>",...      "< 目标路径>"]
         – [--chown=<user>:<group>]: 可选参数，用户改变复制到容器内文件的拥有者和属组，只有
           生成 Linux 镜像时可用。
         – < 源 路 径>: 源 文 件 或 者 源 目 录， 这 里 可 以 是 通 配 符 表 达 式， 其 通 配 符 规 则 要 满 足 Go 的
           ﬁlepath.Match 规则。例如:
           COPY hom* /mydir/
           COPY hom?.txt /mydir/
         – < 目标路径>: 容器内的指定路径，该路径不用事先建好，路径不存在的话，会自动创建。
   • ADD: 与 COPY 指令的使用格式一致，区别为
         – < 源文件> 为 tar 压缩文件的话，压缩格式为 gzip, bzip2 以及 xz 的情况下，会自动复制并解压到
           < 目标路径>
         – < 源文件> 为远程 URL 时会自动下载到 < 目标路径> 或下载后复制到 < 目标路径>
        注意: 推荐除了解压缩的情况，任何情况都使用 COPY。
38                                                                       Chapter 2. Docker
                                                                      Yang 的笔记, 发布 0.1.0
    • ENV: 设置环境变量，定义了环境变量，那么在后续的指令中，就可以使用这个环境变量。
      格式:
      ENV <key> <value>
      ENV <key1>=<value1> <key2>=<value2>...
      环境变量在 Dockerﬁle 中可以通过 $key 或者 ${key} 来引用，${key} 格式还支持两种修饰符：
          – ${key:-word}: 如果键值未被设置则设成 word，否则使用设置值
          – ${key:+word}: 如果键值已被设置则改为 word，否则使用空字符串
      例子:
      ENV NODE_VERSION 7.2.0
      RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-
       ,→x64.tar.xz" \
         && curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc"
      使用 ENV 设置的环境变量会在保留在运行的容器中，可以用 docker inspect 查看，可以通过
      docker run --env <key>=<value> 来修改环境变量。
    • ARG: 构建参数。
      格式:
      ARG <参数名>[=<默认值>]
      ARG 设置的环境变量仅对 Dockerﬁle 内有效，也就是说只对构建的过程中有效，构建好的镜像内不存在
      此环境变量。构建命令 docker build 中可以用 --build-arg < 参数名>=< 值> 来覆盖。
         注意: 如果 ARG 和 ENV 定义了同样的变量，ENV 定义的值会覆盖 ARG 定义的值。
    • VOLUME: 定义匿名数据卷。在启动容器时忘记挂载数据卷，会自动挂载到匿名卷。挂载数据卷的好处
      是避免重要的数据因容器重启而丢失和避免容器不断变大。
      格式:
      VOLUME ["< 路径 1>", "< 路径 2>"...]
      VOLUME <路径>
      在启动容器 docker run 的时候，也可以通过 -v 参数修改挂载点。
    • EXPOSE: 端口声明, 帮助镜像使用者理解这个镜像服务的守护端口，以方便配置映射。默认对外映射的
      是 TCP``协议，你可以指定 ``UDP 。
      格式:
2.3. Dockerﬁle                                                                         39
Yang 的笔记, 发布 0.1.0
     EXPOSE <端口 1> [<端口 2>/<协议>...]
     使用 docker run -P 时，会自动随机映射 EXPOSE 的端口。
   • USER: 用于指定执行后续命令的用户和用户组，这边只是切换后续命令执行的用户（用户和用户组必
     须提前已经存在）         。
     格式:
     USER <用户名>[:<用户组>]
     USER <UID>[:<GID>]
   • WORKDIR: 指定工作目录。用 WORKDIR 指定的工作目录，会在构建镜像的每一层中都存在。docker 构
     建镜像过程中的每一个 RUN 命令都是新建的一层。只有通过 WORKDIR 创建的目录才会一直存在。如
     果 WORKDIR 不存在，docker 会去创建一个。
     格式:
     WORKDIR <工作目录路径>
     WORKDIR 在 dockerﬁle 中可以被多次使用，例如:
     WORKDIR /a
     WORKDIR b
     WORKDIR c
     RUN pwd
     输出的 pwd 为 /a/b/c
   • HEALTHCHECK: 用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
     格式:
     HEALTHCHECK [选项] CMD < 命令>：设置检查容器健康状况的命令
     HEALTHCHECK NONE：如果基础镜像有健康检查指令，使用这行可以屏蔽掉其健康检查指令
     可以用选项:
       – –interval= 时长 (默认: 30s)
       – –timeout= 时长 (默认: 30s)
       – –start-period= 时长 (默认: 0s)
       – –retries= 次数 (默认: 3)
     例如:
     HEALTHCHECK --interval=5m --timeout=3s \
       CMD curl -f http://localhost/ || exit 1
40                                                  Chapter 2. Docker
                                                                      Yang 的笔记, 发布 0.1.0
2.4 Docker Compose 安装
   1. 安装当前稳定版:
      sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-
       ,→compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
      如果 GitHub 太慢了，可以使用国内 DaoCloud
      sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.4/
       ,→docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
      如果更新版本了或者你想换个版本，把 1.25.4 换成你指定的版本。
   2. 设置运行权限:
      sudo chmod +x /usr/local/bin/docker-compose
   3. 验证安装:
      docker-compose --version
   4. 删除:
      sudo rm /usr/local/bin/docker-compose
2.5 Docker Compose 命令
    • 通用格式:
      docker-compose [-f …] [options] [COMMAND] [ARGS…]
      常用选项
          – -f, --file FILE: 指定 Compose 模板文件，默认为 docker-compose.yml 可多次指定。
          – -p, --project-name NAME: 指定项目名称，默认使用当前所在目录名称作为项目名称。
          – -verbose: 输出更多调试信息
          – -v, --version: 打印版本并退出
    • docker-compose up: 启动所有服务:
      docker-compose up [options] [–scale SERVICE=NUM…] [SERVICE…]
      命令选项
2.4. Docker Compose 安装                                                                 41
Yang 的笔记, 发布 0.1.0
       – -d: 指定在后台以守护进程方式运行服务容器
       – -no-color: 设置不使用颜色来区分不同的服务器的控制输出
       – -no-deps: 设置不启动服务所链接的容器
       – -force-recreate: 设置强制重新创建容器，不能与 –no-recreate 选项同时使用。
       – –no-create: 若容器已经存在则不再重新创建，不能与 –force-recreate 选项同时使用。
       – –no-build: 设置不自动构建缺失的服务镜像
       – –build: 设置在启动容器前构建服务镜像
       – –abort-on-container-exit: 若任何一个容器被停止则停止所有容器，不能与选项 -d 同时使
         用。
       – -t, --timeout TIMEOUT: 设置停止容器时的超时秒数，默认为 10 秒。
       – –remove-orphans 设置删除服务中没有在 compose 文件中定义的容器
       – –scale SERVICE=NUM 设置服务运行容器的个数，此选项将会负载在 compose 中通过 scale 指定
         的参数。
   • docker-compose down: 停止和删除容器、网络、卷、镜像:
     docker-compose down [options]
     命令选项
       – -rmi type: 删除镜像类型，类型可选：
           * –rmi all: 删除 compose 文件中定义的所有镜像
           * –rmi local: 删除镜像名为空的镜像
       – -v, --volumes: 删除已经在 compose 文件中定义的和匿名的附在容器上的数据卷
       – –remove-orphans: 删除服务中没有在 compose 中定义的容器
   • docker-compose ps: 列出项目中当前的所有容器
   • docker-compose logs: 查看服务容器的输出，默认情况下 docker-compose 将对不同的服务输
     出使用不同的颜色来区分。可以通过 –no-color 来关闭颜色。
   • docker-compose build: 构建或重构项目中的服务容器，服务容器一旦构建后将会带上一个标记名
     称，可以随时在项目目录下运行 docker-compose build 来重新构建服务:
     docker-compose build [options] [–build-arg key=val…] [SERVICE…]
     命令选项
       – –compress: 通过 gzip 压缩构建上下文环境
       – –force-rm: 删除构建过程中的临时容器
42                                                                   Chapter 2. Docker
                                                                   Yang 的笔记, 发布 0.1.0
          – –no-cache: 构建镜像过程中不使用缓存
          – –pull: 始终尝试通过拉取操作来获取更新版本的镜像
          – -m, --memory MEM: 为构建的容器设置内存大小
          – –build-arg key=val“: 为服务设置 build-time 变量
    • docker-compose restart: 重启项目中的服务:
      docker-compose restart [options] [SERVICE…]
      命令选项
          – -t, --timeout TIMEOUT: 指定重启前停止容器的超时时长，默认为 10 秒。
    • docker-compose rm: 删除所有停止状态的服务容器，推荐先执行 docker-compose stop 命令来
      停止容器:
      docker-compose rm [options] [SERVICE…]
      命令选项
          – -f, --force: 强制直接删除包含非停止状态的容器
          – -v: 删除容器所挂载的数据卷
    • docker-compose start/stop: 启动/停止已经存在的服务容器
    • docker-compose pause/unpause/kill: 暂停/恢复/强行终止服务容器
    • docker-compose config: 验证并查看 compose 文件配置:
      docker-compose config [options]
      命令选项
          – –resolve-image-digests: 将镜像标签标记为摘要
          – -q, --quiet: 只验证配置不输出，当配置正确时不输出任何容器，当配置错误时输出错误信息。
          – –services: 打印服务名称，一行显示一个。
          – –volumes: 打印数据卷名称，一行显示一个。
    • docker-compose run: 在指定服务上执行一条命令:
      docker-compose run [options] [-v VOLUME…] [-p PORT…] [-e KEY=VAL…] SERVICE␣
       ,→[COMMAND] [ARGS…]
      例如, 在 test 容器上运行 ping 命令 10 次:
2.5. Docker Compose 命令                                                            43
Yang 的笔记, 发布 0.1.0
     docker-compose run testping www.baidu.com -c 10
   • docker-compose exec: 进入服务:
     docker-compose exec [options] SERVICE COMMAND [ARGS…]
     命令选项
       – -d: 分离模式，以后台守护进程运行命令。
       – –privileged: 获取特权
       – -T: 禁用分配 TTY，默认 docker-compose exec 分配 TTY。
       – –index=index: 当一个服务拥有多个容器时可通过该参数登录到该服务下的任何服务
     例如:
     docker-compose exec --index=1 web /bin/bash
   • docker-compose port: 显示某个容器端口所映射的公共端口:
     docker-compose port [options] SERVICE PRIVATE_PORT
     命令选项
       – –protocol=proto: 指定端口协议，默认为 TCP，可选 UDP。
       – –index=index: 若同意服务存在多个容器，指定命令对象容器的索引序号，默认为 1。
2.6 Docker Compose 文件
2.6.1 Docker Compose 文件格式
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose，您可以使用 YML 文件来配置应
用程序需要的所有服务，网络及数据卷。然后，使用一个命令，就可以从 YML 文件配置中创建并启动所有服
务。Compose 使用的三个步骤：
   • 使用 Dockerﬁle 定义应用程序的环境。
   • 使用 docker-compose.yml 定义构成应用程序的服务，这样它们可以在隔离环境中一起运行。
   • 最后，执行 docker-compose up 命令来启动并运行整个应用程序。
当前最新的 Compose ﬁle 格式版本为 3.7。下面是个例子:
version: '3.7'
services:
                                                                      (下页继续)
44                                                         Chapter 2. Docker
                                                          Yang 的笔记, 发布 0.1.0
                                                                      (续上页)
  web:
     build: .
     ports:
       - "5000:5000"
  redis:
     image: "redis:alpine"
2.6.2 Docker Compose 文件指令
版本配置指令
    • version：指定本 yml 依从的 compose 哪个版本制定的。
服务配置指令
    • build: 指定为构建镜像上下文路径
      例如 webapp 服务，指定为从上下文路径./dir/Dockerﬁle 所构建的镜像:
      version: "3.7"
      services:
        webapp:
          build: ./dir
      或者，作为具有在上下文指定的路径的对象，以及可选的 Dockerﬁle 和 args
      version: "3.7"
      services:
      webapp:
        build:
          context: ./dir
          dockerfile: Dockerfile-alternate
          args:
            buildno: 1
          labels:
            - "com.example.description=Accounting webapp"
            - "com.example.department=Finance"
            - "com.example.label-with-empty-value"
          target: prod
        – context: 上下文路径。
        – dockerfile: 指定构建镜像的 Dockerﬁle 文件名。
        – args: 添加构建参数，在 Dockerﬁle 中定义的 ARG
        – labels: 设置构建镜像的标签。
2.6. Docker Compose 文件                                                   45
Yang 的笔记, 发布 0.1.0
       – target: 多层构建，可以指定构建哪一层, Dockerﬁle 中 FROM 指令中定义的 AS 名字
   • image: 指定容器运行的镜像。以下格式都可以:
     image: redis
     image: ubuntu:14.04
     image: tutum/influxdb
     image: example-registry.com:4000/postgresql
     image: a4bc65fd # 镜像 id
   • container_name: 指定自定义容器名称，而不是生成的默认名称:
     container_name: my-web-container
   • command: 覆盖容器启动的默认命令:
     command: ["bundle", "exec", "thin", "-p", "3000"]
   • depends_on: 设置依赖关系:
     version: "3.7"
     services:
       web:
         build: .
         depends_on:
            - db
            - redis
       redis:
         image: redis
       db:
         image: postgres
       – docker-compose up: 以依赖性顺序启动服务。在上面示例中，先启动 db 和 redis ，才会启动
         web。
       – docker-compose up SERVICE: 自动包含 SERVICE 的依赖项。
       – docker-compose stop: 按依赖关系顺序停止服务。在以下示例中，web 在 db 和 redis 之前停
         止。
   • configs: 对每个服务使用不同的配置，例如:
       version: "3.7"
       services:
       redis:
         image: redis:latest
         deploy:
                                                                    (下页继续)
46                                                       Chapter 2. Docker
                                                                      Yang 的笔记, 发布 0.1.0
                                                                                    (续上页)
             replicas: 1
           configs:
             - my_config
             - my_other_config
      configs:
         my_config:
           file: ./my_config.txt
         my_other_config:
           external: "true"
    • devices: 指定设备映射列表:
      devices:
         - "/dev/ttyUSB0:/dev/ttyUSB0"
    • 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
    • entrypoint: 覆盖容器默认的 entrypoint:
      entrypoint: /code/entrypoint.sh
      也可以用类似 Dockerﬁle 的格式:
      entrypoint:
           - php
           - -d
           - zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20100525/
       ,→xdebug.so
           - -d
           - memory_limit=-1
           - vendor/bin/phpunit
2.6. Docker Compose 文件                                                                 47
Yang 的笔记, 发布 0.1.0
   • env_file: 从文件添加环境变量。可以是单个值或列表的多个值:
     env_file: .env
     也可以是列表格式:
     env_file:
        - ./common.env
        - ./apps/web.env
        - /opt/secrets.env
   • environment: 添加环境变量。您可以使用数组或字典、任何布尔值，布尔值需要用引号引起来，以
     确保 YML 解析器不会将其转换为 True 或 False
     environment:
        RACK_ENV: development
        SHOW: 'true'
   • expose: 暴露端口，但不映射到宿主机，只被连接的服务访问。仅可以指定内部端口为参数:
     expose:
      - "3000"
      - "8000"
   • extra_hosts: 添加主机名映射:
     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 # 启动后，多少秒开始启动检测程序
   • logging: 服务的日志记录配置。
     driver：指定服务容器的日志记录驱动程序，默认值为 json-ﬁle。有以下三个选项
48                                                               Chapter 2. Docker
                                                             Yang 的笔记, 发布 0.1.0
         – driver: “json-ﬁle”
         – driver: “syslog”
         – driver: “none”
      仅在 json-ﬁle 驱动程序下，可以使用以下参数，限制日志得数量和大小:
      logging:
         driver: json-file
         options:
            max-size: "200k" # 单个文件大小为 200k
            max-file: "10" # 最多 10 个文件
      当达到文件限制上限，会自动删除旧得文件。
      syslog 驱动程序下，可以使用 syslog-address 指定日志接收地址:
      logging:
         driver: syslog
         options:
            syslog-address: "tcp://192.168.0.42:123"
    • restart: 重启策略:
      restart: "no"
      restart: always
      restart: on-failure
      restart: unless-stopped
         – no：是默认的重启策略，在任何情况下都不会重启容器。
         – always：容器总是重新启动。
         – on-failure：在容器非正常退出时（退出状态非 0），才会重启容器。
         – unless-stopped：在容器退出时总是重启容器，但是不考虑在 Docker 守护进程启动时就已经停止了
            的容器
    • secrets: 存储敏感数据，例如密码:
      version: "3.1"
      services:
      mysql:
         image: mysql
         environment:
            MYSQL_ROOT_PASSWORD_FILE: /run/secrets/my_secret
         secrets:
                                                                        (下页继续)
2.6. Docker Compose 文件                                                      49
Yang 的笔记, 发布 0.1.0
                                                                        (续上页)
         - my_secret
     secrets:
       my_secret:
         file: ./my_secret.txt
   • 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:
       some-service:
         networks:
           some-network:
             aliases:
              - alias1
           other-network:
             aliases:
              - alias2
     networks:
       some-network:
         # Use a custom driver
         driver: custom-driver-1
       other-network:
         # Use a custom driver which takes special options
         driver: custom-driver-2
   • port: 端口映射, 有两种格式。
     短格式:
     ports:
       - "3000"
       - "3000-3005"
       - "8000:8000"
       - "9090-9091:8080-8081"
       - "49100:22"
       - "127.0.0.1:8001:8001"
                                                                      (下页继续)
50                                                         Chapter 2. Docker
                                                                      Yang 的笔记, 发布 0.1.0
                                                                                   (续上页)
        - "127.0.0.1:5000-5010:5000-5010"
        - "6060:6060/udp"
      长格式:
      ports:
        - target: 80
          published: 8080
          protocol: tcp
          mode: host
        – target: 容器内端口
        – published: 映射端口
        – protocol: 协议
        – mode: 模式
            * host: 在每一个 node 上映射端口
            * ingress: swarm 模式时，负载均衡
    • volumes: 将主机的数据卷或着文件挂载到容器里。如果你不需要重用卷，那么这样定义就可以了。如
      果需要重用卷，需要在顶级 volumes 下引用。数据卷映射有两种格式。
      短格式:
      version: "3.7"
      services:
        db:
          image: postgres:latest
          volumes:
             - "/localhost/postgres.sock:/var/run/postgres/postgres.sock"
             - "/localhost/data:/var/lib/postgresql/data"
      长格式:
      version: "3.7"
      services:
        web:
          image: nginx:alpine
          ports:
             - "80:80"
          volumes:
             - type: volume
                source: mydata
                                                                                 (下页继续)
2.6. Docker Compose 文件                                                                51
Yang 的笔记, 发布 0.1.0
                                                          (续上页)
               target: /data
               volume:
                 nocopy: true
            - type: bind
               source: ./static
               target: /opt/app/static
     networks:
       webnet:
     volumes:
       mydata:
       – type：volume, bind, tmpfs 或 npipe
       – source：需要映射的主机目录或者顶级 volumes 里定义的名字
       – target: 容器内的目录
       – read_only: 是否只读
       – bind: bind 模式的其他选项
       – volume: volume 模式的其他选项
           * nocopy: 在创建卷的时候不进行拷贝
       – tmpfs: tmpfs 模式的其他选项
           * size: tmpfs 的大小
       – consistency: 挂载的一致性要求
           * consistent: 主机和容器内容一致
           * cached: 读取缓存，主机内容权威
           * delegated: 读写缓存，容器内容权威
卷配置指令
顶级 volumes 块是为了共享卷使用的, 例如:
version: "3.7"
services:
  db:
    image: db
    volumes:
      - data-volume:/var/lib/db
                                                        (下页继续)
52                                           Chapter 2. Docker
                                                      Yang 的笔记, 发布 0.1.0
                                                                  (续上页)
  backup:
     image: backup-service
     volumes:
       - data-volume:/var/lib/backup/data
volumes:
  data-volume:
此例中 db 服务和 backup 服务共用卷 data-volume 。
网络配置指令
顶级 networks 块是为了创建服务指定的虚拟网络，服务在这个虚拟网络中独立运行，主要看你的服务需不
需要隔离。例如:
version: "3"
services:
proxy:
    build: ./proxy
    networks:
       - frontend
app:
    build: ./app
    networks:
       - frontend
       - backend
db:
    image: postgres
    networks:
       - backend
networks:
frontend:
    # Use a custom driver
    driver: custom-driver-1
backend:
    # Use a custom driver which takes special options
    driver: custom-driver-2
    driver_opts:
       foo: "1"
       bar: "2"
这里 driver 需要你创建，例如:
2.6. Docker Compose 文件                                               53
Yang 的笔记, 发布 0.1.0
docker network create --driver weave mynet
54                                         Chapter 2. Docker
                                                  CHAPTER 3
                                                         Git
                      7
3.1 命令速查表
 帮助
 git COMMAND --help                查看帮助信息
 配置工具
 git config --global user.name     对你的提交设置关联的用户名
 "<name>"
 git config --global user.email    对你的提交设置关联的邮箱地址
 <email>
 git config --global color.ui auto 命令行彩色输出
 git config --list                 查看配置列表
 创建仓库
                                                      下页继续
  7 https://git-scm.com
                                                          55
Yang 的笔记, 发布 0.1.0
                                  表 3.1 – 续上页
 git init <directory>                     将目标目录转换为 Git 仓库
 git init                                 将当前目录转换为 Git 仓库
 git clone <URL>                          克隆现有的仓库, 包括所有的文件、分支和提交
 本地修改
 git add <directory>                      将目录内的所有更改 添加到暂存区
 git add <file>                           将文件内的所有更改 添加到暂存区
 git add -p                               将文件内的所有更改分次 添加到暂存区
 git commit -m "<message>"                提交暂存的内容到本地仓库
 git commit -a -m "<message>"             跳过暂存直接 提交更改到本地仓库
 git rm <file>                            从项目 删除文件，并记录到暂存区
 git rm --cached <file>                   停止追踪指定文件，但该文件会保留在工作区
 git mv <old-path> <new-path>             在项目 移动文件，并记录到暂存区
 git diff                                 显示未暂存的更改
 git diff --staged                        显示已暂存未提交的更改
 git status                               显示工作区状态
 git checkout -- <file>                   丢弃工作区的更改
 git reset <file>                         撤销文件的暂存区更改，工作区不变
 git reset --hard HEAD^                   撤销暂存区的更改，工作区 回退到上一版本
                                            • ^ 表示上一版本
                                            • ^^ 表示上两个版本
                                            • ~N 表示上 N 个版本
 git reset                                暂存区 回退到上次提交，工作区不变
 git reset --hard                         暂存区清空，工作区 回退到上次提交
 git revert <commit>                      创建一个新的提交 ** 撤销 ** <commit> 中的更改，
                                          然后应用在当前分支
 git commit --amend -m "<message>"        用暂存区更改和最后一次提交更改的组合
                                          替换最后一次提交
                                          如果暂存区为空, 则修改最后一次提交的信息
 git reset <commit>                       HEAD 指向 <commit>，工作区不变
 git reset --hard <commit>                清空暂存区，用指定 <commit> 覆盖工作区
 git log                                  本地仓库提交日志
                                            • –oneline 每个提交一行
                                            • –graphe 基于文本的图形化
                                                                      下页继续
56                                                               Chapter 3. Git
                                                                     Yang 的笔记, 发布 0.1.0
                                  表 3.1 – 续上页
 git reflog                               本地仓库 HEAD 更改日志
                                              • –relative-dat 显示时间
                                              • –all 显示全部
 分支
 git branch                               列出本地所有分支，当前分支前面会标一个 * 号
                                              • -r 列出远程所有分支
                                              • -a 列出所有分支
 git branch <branch-name>                 创建一个新分支
 git checkout <branch-name>               切换到指定分支并更新工作区
 git switch <branch-name>
 git checkout -b <branch-name>            创建并 切换到指定分支
 git switch -c <branch-name>
 git merge <branch-name>                  合并指定分支到当前分支
                                              • –no-ﬀ 禁用 Fast forward 模式
                                              • -m 添加描述
 git cherry-pick <commit>                 选择一个 commit，合并进当前分支
 git rebase <branch-name>                 将提交到当前分支上的所有修改
                                          都 合并至另一分支上
                                          rebase 通过合并操作来整合分叉了的历史
                                          不要对在你的仓库外有副本的分支执行变基。
 git branch -d <branch-name>              删除指定分支
 标签
 git tag                                  列出所有标签
 git tag <tag-name> <commit>              创建标签，如不指定 <commit>, 默认为 HEAD
 git tag -a <tag-name> -m "message"       创建标签，使用 -m 添加说明
 远程仓库
 git remote add <alias> <url>             关联一个远程库, alias 为别名，如“orginal”
 git fetch <alias> <branch>               从远程库 拉取指定分支，
                                          如不带分支，则拉取所有分支
 git merger <alias>/<branch>              将远程库指定分支，合并到当前分支
 git pull                                 从远程分支 拉取并 合并
 git push <alias> <branch>                从指定分支最新修改 推送到远程库
 储藏与清理
 git stash                                储藏工作区和暂存区的更改
 git stash list                           列出现有的储藏
                                                                              下页继续
3.1. 命令速查表                                                                          57
Yang 的笔记, 发布 0.1.0
                              表 3.1 – 续上页
 git stash apply <stash-name>         重新应用指定储藏
 git stash drop <stash-name>          移除应用指定储藏
 git clean -n                         展示工作区中将被清理的文件，
                                      如未被追踪的文件或空文件夹
 git clean -f                         执行 清理
3.2 使用场景
58                                                   Chapter 3. Git
                                                                       Yang 的笔记, 发布 0.1.0
3.3 工作流
工作流的选择取决于团队规模，项目大小，个人习惯等。不管哪种方式，要习惯经常创建，合并，删除分支。
以我写这个文档为例，也使用了非常简单的工作流，比如使用一个主分支 master , 写新的文档时创建一个
基于 master 的分支，文档写完，把分支合并进 master, 然后删除分支。下面列举几种广泛使用并在实践中
证明过的工作流，可以根据实际情况进行选择。
注解: 本章中部分文字和图片出自:
     • Git Flow Workﬂow8
     • 廖雪峰的 Git 教程9
     • 阮一峰的 Git 分支管理策略10
3.3.1 Git Flow
Git ﬂow 来自 Vincent Driessen 在 2010 年发表的分支管理策略 A successful Git branching model11 。
   8 https://leanpub.com/git-flow/read
   9 https://www.liaoxuefeng.com/wiki/896043488029600
  10 http://www.ruanyifeng.com/blog/2012/07/git.html
  11 https://nvie.com/posts/a-successful-git-branching-model/
3.3. 工作流                                                                              59
Yang 的笔记, 发布 0.1.0
60                 Chapter 3. Git
                                                                                                 Yang 的笔记, 发布 0.1.0
有意思的是今年他又在他的帖子上增加了一个类似于“10 年回顾”的东西：
     “If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workﬂow
      (like GitHub ﬂow) instead of trying to shoehorn git-ﬂow into your team.
      If, however, you are building software that is explicitly versioned, or if you need to support multiple versions
      of your software in the wild, then git-ﬂow may still be as good of a ﬁt to your team as it has been to people
      in the last 10 years. In that case, please read on.
      To conclude, always remember that panaceas don’t exist. Consider your own context. Don’t be hating.
      Decide for yourself.”
我觉得其中三点对我们选择工作流非常有帮助:
   1. 没有万能药，适合的才是最好的
   2. 如果你的团队使用持续部署的方式提供软件或者服务，简单的工作流 (如 GitHub ﬂow) 可能更合适
   3. 如果你的团队提供基于版本的软件，对不同的版本还要提供持续的支持，那么 Git ﬂow 应该还是合适的
我们从这张示意图开始:
Git ﬂow 使用一个中心仓库作为所有开发者的沟通中心。开发者在本地开发然后推送分支到中心仓库。
    • 主分支中心仓库保持两个永久存在的主分支
          – master : 用于存放对外发布的版本，标签永远打在 master 分支上。该分支是一个稳定分支，任何
            时刻都是可以发布的。
3.3. 工作流                                                                                                               61
Yang 的笔记, 发布 0.1.0
      – develop：作为一个集成分支，用于日常开发，存放最新的开发版。可以每天拉取此分支做 nightly
        build。
   • 支持分支
     支持分支的作用是辅助团队成员并行开发，追踪功能，准备版本发布，快速修复已有版本问题。支持分
     支是临时性的，使用完后需要删除。支持分支按作用分为三种：
      – 功能 ( feature ) 分支
      – 预发布 ( release ) 分支
      – 修复 ( hotﬁx ) 分支
      – 功能分支
        如上图所示，每一个功能分支都是为实现新的功能创建，不要把多个功能混合在同一功能分支：
          * 开始: 从 develop 分支创建
          * 结束: 合并回 develop 分支
          * 命名: 任何名称除了 master, develop, release-, hotﬁx-
        例如:
         开始                      结束
         git checkout -b         git checkout develop
         feature-x develop       git merge --no-ff feature-x
                                 git branch -d feature-x
      – 预发布分支当开发版本满足发版本要求的时候，我们需要一个创建一个预发布分支，一部分成员
        可以继续开发新的功能，另一部分，可以在预发布分支上进行准备，如测试，生成文档，bug 修改
        等待。
62                                                           Chapter 3. Git
                                                       Yang 的笔记, 发布 0.1.0
        预发布分支上不能开发任何新功能，只能进行 bug 修改：
         * 开始: 从 develop 分支创建
         * 结束: 合并回 develop 分支和 master 分支
         * 命名: release-*
        例如:
         开始              结束
         git checkout    git checkout master
         -b release-1.   git merge --no-ff release-1.2
         2 develop       git tag -a 1.2
                         git checkout develop
                         git merge --no-ff release-1.2
                         git branch -d release-1.2
      – 修复分支
3.3. 工作流                                                              63
Yang 的笔记, 发布 0.1.0
        当发布的版本出现问题时，需要创建一个修复分支来修复 bug：
         * 开始: 从 master 分支创建
         * 结束: 合并回 master 分支和 develop 分支
         * 命名: hotﬁx-*
        例如:
         开始              结束
         git checkout    git checkout master
         -b hotfix-1.    git merge --no-ff hotfix-1.2
         2 master        git tag -a 1.2.1
                         git checkout develop
                         git merge --no-ff hotfix-1.2
                         git branch -d hotfix-1.2
重要: 这里 merge 使用了 --no-ff 是为了保留分支信息。
fast-farward merge VS --no-ff
64                                                    Chapter 3. Git
                                           Yang 的笔记, 发布 0.1.0
下面这个图中总结了上面的命令：
Windows 版的 Git 集成了 git flow 命令，可以使上面的命令简单些
3.3. 工作流                                                  65
Yang 的笔记, 发布 0.1.0
这里的 start 和 finish 命令对应 git 命令中的开始和结束流程。
参见:
更多 git flow 的例子参见 Using git-ﬂow to automate your git branching workﬂow12
3.3.2 GitHub Flow
GitHub Flow13 是一个轻量级的，基于分支的工作流，用来支持持续部署的团队和项目。
   1. 从 master 创建一个新分支
   2. 添加提交。每个提交都要带有信息描述为什么进行更改。而且，每个提交都被视作一个独立的更改单
       元，这样如果发现 bug 或者决定换个方向的时候比较容易回退。
   3. 发起一个 Pull Request (PR) 。PR 会开启一个关于你的提交的讨论。
       PR 是一个对话机制，你可以在开发的任何时候发起, 比如即使你还没写代码或者刚写了一小部分，但
       想和其他人分享你的截屏或者想法，比如你卡住了需要其他人帮助，比如你准备好让其他人做代码检
       视。你还可以在 PR 中使用 GitHub 的 @mention 系统，说白了就是 @ 特别的人或者团队。
 12 https://jeffkreeftmeijer.com/git-flow/
 13 https://guides.github.com/introduction/flow/index.html
66                                                                       Chapter 3. Git
                                                        Yang 的笔记, 发布 0.1.0
    4. 讨论和代码检视。在这个过程中，你还可以继续的提交更改，比如根据他人的要求更改代码格式啊，改
        bug 啊，等等等
    5. 使用 GitHub，你也可以在合并到 master 之前，从分支部署到生产环境做最终测试。一旦你的 PR 被
        接受，分支也通过你的测试，你就可以在生产环境部署你的更改并测试。如果有问题，你可以通过部署
        master 到生产环境来回滚。
    6. 合并。你的更改经过生产检验，现在可以合并到 master 了。一旦合并，PR 会保留一份你的代码更改
        历史记录。因为 PR 可以被搜索到，任何人都可以明白当时的状况。合并后，你的分支就可以被删除了。
3.3.3 GitLab Flow
GitHub Flow 假设你每一次合并分支都可以部署生产。对于 SaaS 服务，这是有可能的，但是对于很多其他情
况，却不行。比如你无法控制确切的发布时间，像 iOS APP，需要 AppStore 检查。或者你有部署窗口 (比如
工作日早 10 点到晚 4 点，运维团队马力全开)，但这时候你也需要合并代码。
GitLab Flow14 试图解决这些问题。下面介绍三种 GitLab 工作流：
     • 生产分支
        本章最初提到的问题，可以通过创建生产分支来解决，生产分支反映已部署代码。可以通过把主分支
  14 https://docs.gitlab.com/ee/topics/gitlab_flow.html
3.3. 工作流                                                               67
Yang 的笔记, 发布 0.1.0
     合并到生产分支来部署新版本。如果需要了解生产中的代码是什么，只需查看生产分支即可。在版本
     控制系统中提交合并时，很容易看到部署的大致时间。如果生产分支是自动部署的，则此时间非常准
     确。如果需要更精确的时间，可以让部署脚本在每个部署上打标签。
   • 环境分支
     假设你有三个环境，临时环境，预生产环境和生产环境。在这种情况下，主分支部署在临时环境。当有
     人想要部署到预生产时，他们会创建从主分支到预生产分支的合并请求。同理，当向生产环境部署时
     需要创建一个从预生产分支到生产分支的合并请求。
     此工作流仅向下游提交以确保所有内容在所有环境中都经过测试。
     如果生产环境出现了 bug ，这时要在功能分支上修改，然后 cherry-pick 合并到 master，确认没有问题
     (一般来说使用持续部署不会出啥问题)，然后再按上下游顺序继续合并到其他分支。如果因为一些情
     况，不能这么做，也可以直接合并到下游分支，但不推荐。
   • 发布分支
68                                                       Chapter 3. Git
                                                            Yang 的笔记, 发布 0.1.0
     只有在需要向外界发布软件的情况下，才需要使用发布分支。在这种情况下，每个分支都包含一个小
     版本（2-3-stable, 2-4-stable 等）。发布分支从 master 创建。尽可能晚的创建发布分支，可以最大限度地
     减少对多个分支应用 Bug 修复的时间。发布分支一旦发布后，只有严重级别的 Bug 修复会合并到发布
     分支。这些 bug 首先合并到 master 分支，然后从 master 分支 cherry-picked 到发布分支，这样就不会因
     为忘记合并到 master 让后面的版本还有问题。这个策略称为“上游优先”               。每次 bug 修复并合并入发布
     分支后需要给修订版本打一个标签。
     一些项目会包含一个稳定分支指向最新的发布分支的提交。此流程通常不包含生产分支。
3.3. 工作流                                                                   69
   Yang 的笔记, 发布 0.1.0
   3.4 Git 服务器
   3.4.1 Git 原生服务器
   我只用 GitLab 搭建过，等我单独用 git 搭的时候再说。。           。
   3.4.2 GitLab CE 服务器
   我是用 Docker 搭建过 GitLab ，你可以参考我的 docker-compose.yml
 1
 2 version: "3.7"
 3 services:
 4   gitlab:
 5     image: 'gitlab/gitlab-ce:latest'
 6     container_name: GitLab_https
 7     restart: always
 8     hostname: 'gitlab.example.com'
 9     environment:
10       GITLAB_OMNIBUS_CONFIG: |
11         external_url 'https://gitlab.example.com:30443'
12         gitlab_rails['gitlab_shell_ssh_port'] = 30022
13         gitlab_rails['time_zone'] = 'Beijing'
14         gitlab_rails['backup_keep_time'] = 14515200
15         unicorn['worker_timeout'] = 60
16         unicorn['worker_processes'] = 2
17         logging['logrotate_frequency'] = "weekly"
18         logging['logrotate_rotate'] = 52
19         logging['logrotate_compress'] = "compress"
20         logging['logrotate_method'] = "copytruncate"
21         logging['logrotate_delaycompress'] = "delaycompress"
22         nginx['listen_port'] = 443
23         nginx['redirect_http_to_https'] = true
24         nginx['ssl_certificate'] = "/etc/ssl/certs/gitlab/gitlab.example.com.crt"
25         nginx['ssl_certificate_key'] = "/etc/ssl/certs/gitlab/gitlab.example.com.key"
26         nginx['ssl_protocols'] = "TLSv1.1 TLSv1.2"
27         nginx['logrotate_frequency'] = "weekly"
28         nginx['logrotate_rotate'] = 52
29         nginx['logrotate_compress'] = "compress"
30         nginx['logrotate_method'] = "copytruncate"
31         nginx['logrotate_delaycompress'] = "delaycompress"
32         # Add any other gitlab.rb configuration here, each on its own line
33     ports:
                                                                                    (下页继续)
   70                                                                         Chapter 3. Git
                                                           Yang 的笔记, 发布 0.1.0
                                                                       (续上页)
34         - '30443:443'
35         - '30022:22'
36      volumes:
37         - '/srv/gitlab_https/config:/etc/gitlab'
38         - '/srv/gitlab_https/ssl:/etc/ssl/certs/gitlab'
39         - '/srv/gitlab_https/logs:/var/log/gitlab'
40         - '/srv/gitlab_https/data:/var/opt/gitlab'
41      networks:
42         - default
43
44 networks:
45   default:
   你需要根据你的情况修改
       • hostname：你的主机名
       • external_url: 如果不使用 ssl 端口改为 30080，使用你的主机名和端口
       • gitlab_rails[‘gitlab_shell_ssh_port’]：你的 ssh 端口
       • ports：docker 对外映射的端口
       • volumes：存储映射
   如果你不需要 ssl ，去掉 22-26，38 行。
   运行:
   docker-compose up
   3.4.3 GitHub 网站
   GitHub 是最大的 Git 版本库托管商，是成千上万的开发者和项目能够合作进行的中心。大部分 Git 版本库都
   托管在 GitHub，很多开源项目使用 GitHub 实现 Git 托管、问题追踪、代码审查以及其它事情。
   参见:
   了解更多，参见GitHub
   3.4. Git 服务器                                                           71
Yang 的笔记, 发布 0.1.0
3.5 Git 客户端
3.5.1 Git 命令行客户端
    • Linux
       一般 Ubuntu 自带，没带的话用下面的命令安装:
       apt-get install git
    • Windows
       可以下载 安装版15 或者 便携版16
       安装版基本是傻瓜安装，你一步步下去就行了。我用的是便携版，解压缩到一个目录就可以用。
       如果在 PowerShell 或者 cmd 中使用, 需要加入系统路径，例如：
 15 https://github.com/git-for-windows/git/releases/download/v2.25.1.windows.1/Git-2.25.1-64-bit.exe
 16 https://github.com/git-for-windows/git/releases/download/v2.25.1.windows.1/PortableGit-2.25.1-64-bit.7z.exe
72                                                                                                              Chapter 3. Git
                                                                 Yang 的笔记, 发布 0.1.0
        你可以根据自己的喜好使用 git bash 或者在 PowerShell 中使用 git。
3.5.2 Sourcetree
Sourcetree17 是一个 Git 用户界面, 你可以在官网点击链接下载安装。
安装好之后，先换成中文，点击 Tools ‣ Options ‣General, 在 Repo Settings 里找到 Language , 在下拉菜单中选 汉
语，然后重启你的 Sourcetree ，语言就变成中文了。
  17 https://www.sourcetreeapp.com/
3.5. Git 客户端                                                                    73
Yang 的笔记, 发布 0.1.0
你可以顺手在 Project folder 里设置一下本地仓库的默认存储文件夹。
接下来，需要关联你的 GitHub 账号，点击 工具 ‣ 选项 ‣ 验证。点击 添加, 然后按下图选择
点击 刷新 OAuth 令牌, 会弹出一个网页，请求 GitHub 授权，这个授权我没截图，后面 Read the Docs 我截图了，
你可以参考后面的图。点击绿色的 Authorize xxx, 这个 xxx 是应用的名字。这样 GitHub 就授权你的 Sourcetree
访问它了。认证成功，会显示。中间可能会要求输入用户名密码，这个我忘了。。。
74                                                        Chapter 3. Git
                                                         Yang 的笔记, 发布 0.1.0
这里首选协议，我建议选 HTTPS ，如果使用 SSH, Sourcetree 支持两种，openssl 和 putty agent，两种都有问题，
openssl 要不停的输入密码，putty agent 的密钥和 GitHub 的密钥不兼容。
点击 + ，新建一个 Tab，点击 Remote
3.5. Git 客户端                                                            75
Yang 的笔记, 发布 0.1.0
选中你刚刚创建的仓库，点击 Clone ，在新的 Tab 中看看你有没有要修改的地方，比如存储位置什么的，没
有问题的话，点击 克隆。
这样你就有一个本地仓库了，点击 Local，你就能看到它了，双击它，Sourcetree 会为你创建一个新的 Tab 来
操作这个仓库。
76                                                Chapter 3. Git
                                    Yang 的笔记, 发布 0.1.0
3.5.3 VS Code
在 VS Code 中使用 Git，请参见VS Code Git 扩展
3.5. Git 客户端                                       77
Yang 的笔记, 发布 0.1.0
78                 Chapter 3. Git
                                                  CHAPTER     4
                                                       GitHub
                       18
GitHub 是最大的 Git 版本库托管商，是成千上万的开发者和项目能够合作进行的中心。大部分 Git 版本库都
托管在 GitHub，很多开源项目使用 GitHub 实现 Git 托管、问题追踪、代码审查以及其它事情。
4.1 注册
直接访问 https://github.com ，选择一个未被占用的用户名，提供一个电子邮件地址和密码，点击 Sign up for
GitHub 即可。
 18 https://github.com
                                                               79
Yang 的笔记, 发布 0.1.0
4.2 常用配置
    • 简介
      点击 头像 ‣ Settings ‣ Proﬁle
      这里可以修改姓名，头像，公开邮件，自我介绍啊啥的。
    • 安全
      点击 头像 ‣ Settings ‣ Security
      可以修改密码，如果要设置两步验证不要使用 SMS , 国内不支持。
    • 邮箱
      点击 头像 ‣ Settings ‣ Emails
      可以添加，修改，删除 GitHub 使用的邮箱。
4.3 SSH 配置
   1. 生成 SSH 密钥对
      在 Windows 下打开 git-bash，在其他环境下打开终端。输入如下命令:
      ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
      这里使用你在 GitHub 中设置的邮箱地址。
      下面是我生成的例子。
80                                                          Chapter 4. GitHub
                                                                     Yang 的笔记, 发布 0.1.0
  1   yang@SkyLab-X1 MINGW64 /
  2   $ ssh-keygen -t rsa -b 4096 -C d12y12@hotmail.com
  3   Generating public/private rsa key pair.
  4   Enter file in which to save the key (/c/Users/yang/.ssh/id_rsa):
  5   Enter passphrase (empty for no passphrase):
  6   Enter same passphrase again:
  7   Your identification has been saved in /c/Users/yang/.ssh/id_rsa
  8   Your public key has been saved in /c/Users/yang/.ssh/id_rsa.pub
  9   ...
        • 第 4 行：询问密钥要存在哪，直接回车就行了。默认存在用户的 ~/.ssh/ 目录
        • 第 5 行：输入一个密码
        • 第 6 行：再输一边，当然要和刚刚的一样。
      这样你的密钥对就创建好了，其中
        • id_rsa：你的私钥
        • id_rsa.pub：公钥
   2. 添加密钥到 ssh-agent
      如果你不想每次使用密钥都输入密码，你可以把密钥添加到 ssh-agent, 它会管理你的密钥和密码。
      启动 ssh-agent:
      $ eval $(ssh-agent -s)
      添加 SSH 私钥:
      $ ssh-add ~/.ssh/id_rsa
      这样只有第一次使用密钥的时候需要输入密码，后面就不用了。这里也还是有一个问题，就是你每次
      使用 git-bash 都要检查一下 ssh-agent 是不是已经打开，没打开就要手动打开一次。
      你可以通过修改 ~/.bashrc 文件来实现打开 git-bash 就自动启动 ssh-agent。
        • 检查一下你的 ~/.bashrc，如果没有就创建一个在 git-bash 中输入 cp /etc/bash.bashrc
          .bashrc
        • 将下面内容复制粘贴到 ~/.bashrc
          env=~/.ssh/agent.env
          agent_load_env () { test -f "$env" && . "$env" >| /dev/null ; }
          agent_start () {
               (umask 077; ssh-agent >| "$env")
                                                                                (下页继续)
4.3. SSH 配置                                                                         81
Yang 的笔记, 发布 0.1.0
                                                                                    (续上页)
               . "$env" >| /dev/null ; }
          agent_load_env
          # agent_run_state: 0=agent running w/ key; 1=agent w/o key; 2= agent not␣
           ,→running
          agent_run_state=$(ssh-add -l >| /dev/null 2>&1; echo $?)
          if [ ! "$SSH_AUTH_SOCK" ] || [ $agent_run_state = 2 ]; then
               agent_start
               ssh-add
          elif [ "$SSH_AUTH_SOCK" ] && [ $agent_run_state = 1 ]; then
               ssh-add
          fi
          unset env
      这样当你启动系统后第一次使用 git-bash 时，你需要输入一次密码:
      > Initializing new SSH agent...
      > succeeded
      > Enter passphrase for /c/Users/you/.ssh/id_rsa:
      > Identity added: /c/Users/you/.ssh/id_rsa (/c/Users/you/.ssh/id_rsa)
      > Welcome to Git (version 1.6.0.2-preview20080923)
      >
      > Run 'git help git' to display the help index.
      > Run 'git help ' to display help for specific commands.
      之后 ssh-agent 进程会一直运行直到你登出，关机或者强制关闭这个进程。
   3. 添加公钥到你的 GitHub 账号
        • 复制你的公钥，随便找个文字编辑工具打开 id_rsa.pub，然后复制
        • 在 GitHub ，点击 头像 ‣ Settings ‣ SSH and GPG keys
        • 点击 New SSH key
        • 给这个新密钥一个 Title, 然后把你复制的密钥拷贝进 Key
82                                                                      Chapter 4. GitHub
                                              Yang 的笔记, 发布 0.1.0
        • 点击 Add SSH key , 然后需要输入你的 GitHub 密码
        • 成功如下显示
   4. 测试
      使用如下命令:
      ssh -T git@github.com
      下面是我测试的例子，此处需要输入你创建密钥时的密码。
4.3. SSH 配置                                                  83
Yang 的笔记, 发布 0.1.0
  1 yang@SkyLab-X1 MINGW64 /
  2 $ ssh -T git@github.com
  3 The authenticity of host 'github.com (13.229.188.59)' can't be established.
  4 RSA key fingerprint is SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8.
  5 Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
  6 Warning: Permanently added 'github.com,13.229.188.59' (RSA) to the list of known␣
     ,→hosts.
  7 Enter passphrase for key '/c/Users/yang/.ssh/id_rsa':
  8 Hi d12y12! You've successfully authenticated, but GitHub does not provide shell␣
     ,→access.
    到此为止，你就可以使用 ssh 来访问 GitHub 了。
4.4 创建仓库
一般情况下，你登陆 GitHub 后，主页的左侧会显示你的仓库。
可以直接点击 new 来创建新的仓库。如果你找不到也可以点击 头像 ‣ Your repositories , 进入仓库页面点击 new
来创建新的仓库也可以。
接下来，给你的仓库取个名字填进去
84                                                                    Chapter 4. GitHub
                                                         Yang 的笔记, 发布 0.1.0
如果你勤快的话，可以在 Description 里填一些关于项目的描述。
这里你可以让 GitHub 替你创建 README , .gitignore 和 LICENSE 文件，也可以稍后添加。
没问题就点击 Create repository 。
4.4. 创建仓库                                                               85
Yang 的笔记, 发布 0.1.0
86                 Chapter 4. GitHub
                                                                  CHAPTER    5
                                                                     Markdown
Markdown 是一种全文本格式，用来写作结构化文档。Markdown 的语法全由一些符号所组成，这些符号经过
精挑细选，其作用一目了然。Markdown 的缺点在于没有标准，只有一个最初的格式描述，CommonMark 在做
标准化的事，但目前为止还没有如 reStructuredText 一样的标准。
    • GitHub Flavored Markdown (GFM)
       由于 Markdown 没有明确的标准，所以存在各种味道 ( Flavored ) 的 Markdown ，简单地说就是各自增加
       的自己的一些语法或者渲染的方式不一样。
       GitHub 味的 Markdown 就是 GitHub 在 CommonMark19 规格基础上，对语法格式和语义的定义，文档请
       见 GitHub Flavored Markdown Spec20 。
       注：Sphinx 插件 recommonmark 支持使用 CommonMark 的 Markdown 语法，不支持 GFM ，所以本
       文中很多示例在 Sphinx 显示不正常。
5.1 生成目录
GFM 支持的目录方式如下:
1. [Example](#example)
2. [Example2](#example2)
3. [Third Example](#third-example)
                                                                         (下页继续)
 19 https://spec.commonmark.org/
 20 https://github.github.com/gfm/
                                                                             87
Yang 的笔记, 发布 0.1.0
                                                                          (续上页)
4. [Fourth Example](#fourth-examplehttpwwwfourthexamplecom)
5. [第五个中文例子](# 第五个中文例子)
这里是你的章节标题
# Example
# Example2
# Third Example
# [Fourth Example](http://www.fourthexample.com)
# 第五个中文例子
还有很多生成目录的工具，比如在 VSCode 里用 markdown-all-in-one 扩展。
5.2 标题
5.2.1 章节标题
类 Atx 形式，在行首插入 1 到 6 个 #，对应到标题 1 到 6 阶。例如：
# 这是一级标题
## 这是二级标题
###### 这是六级标题
     注:
      1. 还有一种 Setext 形式，使用少于 3 个空格缩进的连续 = (一级标题) 和 - (二级标题)，任何
         数量的 = 和 - 都可以有效果，- 数量必须大于 1 个，单一的 - 会被处理成列表。例如：
            这是一级标题
            ===========
            这是二级标题
            -----------
         我个人建议不使用 Setext 形式，尽量使用 ATX 形式.
      2. 此处我使用代码块来展示标题，为的是不让自动生成目录出错。。。
88                                                          Chapter 5. Markdown
                                                  Yang 的笔记, 发布 0.1.0
5.2.2 分隔线
你可以在一行中用 3 个以上的星号、减号、底线来建立一个分隔线 ( Thematic breaks )，行内不能有其他东西。
你也可以在星号或是减号中间插入空格。例如：
* * *
***
*****
- - -
---------------------------------------
下面就是一个分割线
5.3 段落和换行
5.3.1 段落
一个 Markdown 段落是由一个或多个连续的文本行组成，它的前后要有一个以上的空行 (空行的定义是显示
上看起来像是空的，便会被视为空行。比方说，若某一行只包含空格和制表符，则该行也会被视为空行)。普
通段落不该用空格或制表符来缩进。
5.3.2 换行
如果要换行，可以在行尾加 2 个以上空格 ( space ) 或者显示使用 \。例如：
朝辞白帝彩云间，
千里江陵一日还。\
两岸猿声啼不住，
轻舟已过万重山。
朝辞白帝彩云间，千里江陵一日还。两岸猿声啼不住，轻舟已过万重山。
     注：此处 HTML builder 可以正常输出换行，Latex PDF 只输出一行。
只使用回车是不会形成换行的。例如：
5.3. 段落和换行                                                       89
Yang 的笔记, 发布 0.1.0
朝辞白帝彩云间，
千里江陵一日还。
两岸猿声啼不住，
轻舟已过万重山。
朝辞白帝彩云间，千里江陵一日还。两岸猿声啼不住，轻舟已过万重山。
5.4 代码
5.4.1 缩进代码块
要在 Markdown 中建立代码区块很简单，只要简单地缩进 4 个空格或是 1 个制表符就可以，一个代码区块会
一直持续到没有缩进的那一行 (或是文件结尾。例如：
这是一段代码：
      def hello(name):
             print("Hello", name.title())
这是一段代码：
def hello(name):
      print("Hello", name.title())
5.4.2 栅栏代码块
使用大于 3 个 ` 或 ~ ，将代码包围起来。例如：
```python
def hello(name):
      print("Hello", name.title())
```
def hello(name):
      print("Hello", name.title())
GFM 支持的所有语言请参考 GFM 支持语言列表21 。
 21 https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
90                                                                            Chapter 5. Markdown
                                          Yang 的笔记, 发布 0.1.0
5.4.3 内联代码段
使用 1 个或多个 ` 。例如：
`foo`
``foo ` bar``
` `` `
foo
foo ` bar
``
5.5 引用
Markdown 标记区块引用是使用类似电子邮件中用 > 的引用方式。如果你还熟悉在邮件中的引言部分，你就
知道怎么在 Markdown 文件中建立一个区块引用，那会看起来像是你自己先断好行，然后在每行的最前面加
上 >：
> 这个引用包含两个段落。
>
> 《早发白帝城》：
> 朝辞白帝彩云间，千里江陵一日还。两岸猿声啼不住，轻舟已过万重山。
>
> 《静夜思》   ：
> 床前明月光，疑是地上霜。举头望明月，低头思故乡。
      这个引用包含两个段落。
     《早发白帝城》  ：朝辞白帝彩云间，千里江陵一日还。两岸猿声啼不住，轻舟已过万重山。
     《静夜思》  ：床前明月光，疑是地上霜。举头望明月，低头思故乡。
区块引用可以嵌套 (例如：引用内的引用)，只要根据层次加上不同数量的 >：
> 这是第一层引用。
>
> > 这是一个嵌套引用。
>
> 回到第一层引用
      这是第一层引用。
         这是一个嵌套引用。
5.5. 引用                                                  91
Yang 的笔记, 发布 0.1.0
     回到第一层引用
引用的区块内也可以使用其他的 Markdown 语法，包括标题、列表、代码区块等。例如：
>
> 1. 这是第一行列表项。
> 2. 这是第二行列表项。
>
> 给出一些例子代码：
>
>     return shell_exec("echo $input | markdown_script");
       1. 这是第一行列表项。
       2. 这是第二行列表项。
     给出一些例子代码：
     return shell_exec("echo $input | markdown_script");
5.6 列表
Markdown 支持有序列表和无序列表。
5.6.1 无序列表
无序列表使用 *、+ 或是-作为列表标记。例如：
- 李白
- 杜甫
- 白居易
   • 李白
   • 杜甫
   • 白居易
92                                                        Chapter 5. Markdown
                                        Yang 的笔记, 发布 0.1.0
5.6.2 有序列表
有序列表则使用数字接着一个 . ，在列表标记上使用的数字并不会影响输出的结果。例如：
1. 李白
2. 杜甫
3. 白居易
   1. 李白
   2. 杜甫
   3. 白居易
列表项目标记通常是放在最左边，但是其实也可以缩进，最多 3 个空格，项目标记后面则一定要接着至少一
个空格或制表符。
列表项目可以包含多个段落，每个项目下的段落都必须缩进 4 个空格或是 1 个制表符。例如：
1. 这个列表包含两个段落。
     《早发白帝城》：
     朝辞白帝彩云间，千里江陵一日还。两岸猿声啼不住，轻舟已过万重山。
2. 《静夜思》：
     床前明月光，疑是地上霜。举头望明月，低头思故乡。
   1. 这个列表包含两个段落。
     《早发白帝城》 ：
      朝辞白帝彩云间，千里江陵一日还。两岸猿声啼不住，轻舟已过万重山。
   2.《静夜思》 ：
      床前明月光，疑是地上霜。举头望明月，低头思故乡。
如果要在列表项目内放进引用，那 > 就需要缩进。例如：
- 列表项包含一个引用区块:
     > 这个引用区块在列表内部。
    • 列表项包含一个引用区块:
         这个引用区块在列表内部。
如果要放代码区块的话，该区块就需要缩进两次，也就是 8 个空格或是 2 个制表符。例如：
5.6. 列表                                                93
Yang 的笔记, 发布 0.1.0
- 列表项包含一个代码区块：
        printf("hello")
   • 列表项包含一个代码区块：
       printf("hello")
5.6.3 任务列表
GFM 扩展项，在列表项上增加了复选框。在列表标识 (-，*，+) 后加空格再加 [ ] 及 1 个空格，[ ] 中如果是
空格则复选框未被选中，如为 x 则复选框被选中。例如：
- [ ] 吃饭
- [x] 睡觉
- [ ] 打豆豆
     注: 这里不展示了，因为 recommonmark 不支持 GFM 扩展项。
5.7 表格
GFM 扩展项，并非所有 Markdown 都支持，我们以 GFM 为例。一个列表包括表头，分隔符行，表数据。例
如：
| 序号 | 日期          | 内容       |
| ---- | --------- | ---------- |
| 1    | 2020-2-17 | 文档初始化 |
| 2    | 2020-2-18 | 文档完成      |
在分隔符行，可以使用：来定义对齐方式：
| 对齐方式 | 语法       |
| -------- | ----- |
| 居中       | :---: |
| 左对齐     | :---   |
| 右对齐     | ---： |
     注: 这里不展示了，因为 recommonmark 不支持 GFM 扩展项。
94                                            Chapter 5. Markdown
                                                                     Yang 的笔记, 发布 0.1.0
5.8 链接
5.8.1 普通链接
Markdown 支持两种形式的链接语法：内联式和引用式两种形式。
不管是哪一种，链接文字都是用 [方括号] 来标记。
    • 内联式
      要建立一个行内式的链接，只要在方块括号后面紧接着圆括号并插入网址链接即可，如果你还想要加
      上链接的 title 文字，只要在网址后面，用双引号把 title 文字包起来即可。例如：
      This is [an example](http://example.com/ "Title") inline link.
      [This link](http://example.net/) has no title attribute.
          注：recommonmark 不支持 title 。
      如果你是要链接到同样主机的资源，你可以使用相对路径。例如：
      这只 [熊猫](../_static/markdown/panda.png) 来自中国。
          注：我不推荐这种方式，因为生成 PDF 可能无法使用。
    • 引用式
      引用式的链接是在链接文字的括号后面再接上另一个方括号，而在第二个方括号里面要填入用以辨识
      链接的标记。例如：
      This is [an example][id] reference-style link.
      接着，在文件的任意处，你可以把这个标记的链接内容定义出来：
      [id]: http://example.com/  "Optional Title Here"
      链接的定义可以放在文件中的任何一个地方，我比较偏好把它放在文件最后面.
          注：recommonmark 不支持 title 。
    • 独立链接
      独立链接是指把链接直接放进 <> 尖括号内，如果支持自动链接扩展，可以不加 <>。例如：
      visit <https://cn.bing.com/>
      <d12y12@hotmail.com>
      如支持扩展， 可直接链接， 如 www.google.com
5.8. 链接                                                                             95
Yang 的笔记, 发布 0.1.0
          注：此处 HTML 中 www.google.com 不是链接，Latex PDF 中 www.google.com 是链接
5.8.2 注脚
GFM 并不支持注脚 ( footnotes )，但鉴于其作用，还是把它写出来了，注脚的定义与引用链接一样，只是在 []
方括号及引用定义内加上 ^ 。例如:
这是一个链接到谷歌的 [^ 注脚]。
[^ 注脚]: http://www.google.com
     注：这里不展示了，因为 CommonMark 和 GFM 都不支持注脚。
5.9 图片
Markdown 使用一种和链接很相似的语法来标记图片，同样也允许两种样式：内联式和引用式。到目前为止，
Markdown 还没有办法指定图片的宽高。
   • 内联式
     行内式的图片语法是一个惊叹号 !，接着一个方括号，里面放上图片的替代文字，接着一个普通括号，
     里面放上图片的网址，最后还可以用引号包住并加上选择性的‘title’文字。例如：
     ![本地图片](/_static/markdown/apple_logo.png)
     ![网上图片](https://github.com/d12y12/notes/blob/master/_static/markdown/tesla_
      ,→logo.jpg?raw=true "Optional title")
96                                                               Chapter 5. Markdown
                                   Yang 的笔记, 发布 0.1.0
        注：recommonmark 不支持 title 。
5.9. 图片                                           97
Yang 的笔记, 发布 0.1.0
    • 引用式
      引用式的图片语法则长得像这样：
      ![Alt text][img_id]
      [img_id] 是图片参考的名称，图片参考的定义方式则和连结参考一样：
        [img_id]: /_static/markdown/tesla_logo.jpg "Optional title attribute"
5.10 文字处理
5.10.1 强调
Markdown 使用星号 * 和下划线 _ 作为标记强调字词的符号
    • 斜体
      被 1 个 * 或 _ 包围的字词会被转成斜体，例如：
      - *single asterisks*
      - _single underscores_
         – single asterisks
         – single underscores
    • 加粗
      用 2 个 * 或 _ 包起来的话，则会被转成加粗，例如：
      - **double asterisks**
      - __double underscores__
         – double asterisks
         – double underscores
你可以随便用你喜欢的样式，唯一的限制是，你用什么符号开启标签，就要用什么符号结束。
强调也可以直接插在文字中间。例如：
un*frigging*believable
unfriggingbelievable
但是如果你的 * 和 _ 两边都有空白的话，它们就只会被当成普通的符号。例如：
98                                                                   Chapter 5. Markdown
                                                 Yang 的笔记, 发布 0.1.0
a * foo bar*
a * foo bar*
如果要在文字前后直接插入普通的星号或底线，你可以用反斜线。例如：
\*this text is surrounded by literal asterisks\*
*this text is surrounded by literal asterisks*
5.10.2 删除线
GFM 扩展项，使用 2 个 ~。例如：
~~Hi~~ Hello, world!
       注: 这里不展示了，因为 recommonmark 不支持 GFM 扩展项。
5.10.3 反斜杠转义
Markdown 可以利用反斜杠来插入一些在语法中有其它意义的符号，Markdown 支持以下这些符号前面加上反
斜杠来帮助插入普通的符号：
\     反斜线
`     反引号
*     星号
_     底线
{}    花括号
[]    方括号
()    括弧
#     井字号
+     加号
-     减号
.     英文句点
!     惊叹号
5.10. 文字处理                                                      99
Yang 的笔记, 发布 0.1.0
5.11 参考
    • GitHub Flavored Markdown Spec22
    • GFM 支持语言列表23
    • CommonMark24
 22 https://github.github.com/gfm/
 23 https://github.com/github/linguist/blob/master/lib/linguist/languages.yml
 24 https://spec.commonmark.org/
100                                                                           Chapter 5. Markdown
                                                       CHAPTER        6
                                                             OpenSSL
                                       25
OpenSSL 是一个功能极其强大的命令行工具，可以用来完成公钥体系 (Public Key Infrastructure) 及 HTTPS 相
关的很多任务。
6.1 生成证书签名请求
如果你要从证书颁发机构 (CA) 获取一个 SSL 证书，那首先需要先生成一个证书签名请求 (CSR)。证书签名
请求的主要内容是“密钥对”中的公钥，以及一些额外的信息——这些内容都将在签名时插入到证书里。
当使用 OpenSSL 生成证书签名请求时，需要输入证书的唯一标识信息 (Distinguished Name)，其中重要的一项
是常见名 (Common Name)，它应当是你要部署证书的主机的域名全称 (FQDN)。标识信息中的其他条目用来
提供关于你的机构的额外信息。如果你从证书颁发机构购买 SSL 证书，那么通常也需要这些额外的字段，例
如组织机构 (Organization)，以便能够真实地展示你的机构详情。
下面是 CSR 的模样:
 25 https://www.openssl.org/
                                                                     101
Yang 的笔记, 发布 0.1.0
Country Name (2 letter code) [AU]:US
State or Province Name (full name) [Some-State]:New York
Locality Name (eg, city) []:Brooklyn
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example Brooklyn Company
Organizational Unit Name (eg, section) []:Technology Division
Common Name (e.g. server FQDN or YOUR name) []:examplebrooklyn.com
Email Address []:
也可以使用非交互方式提供生成 CSR 时要求的信息，任何需要 CSR 信息的 OpenSSL 命令都可以添加 -subj
选项。例如:
-subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=examplebrooklyn.com"
   • 生成私钥和 CSR
     如果你需要使用 HTTPS 来加固你的 web 服务器，那么你会向证书颁发机构申请一个证书。这里生成的
     CSR 可以发送给 CA 来发行其签名的 SSL 证书。
     下面的命令创建一个 2048 位的私钥 (domain.key) 以及一个 CSR (domain.csr):
     openssl req \
            -newkey rsa:2048 -nodes -keyout domain.key \
            -out domain.csr
     这里需要交互地输入 CSR 信息，以便完成整个过程。
       – -newkey rsa:2048: 声明了使用 RAS 算法生成 2048 位的私钥
       – -nodes: 表明我们不使用密码加密私钥。
       – 上面隐含了 -new 选项，表示要生成一个 CSR
   • 使用已有私钥生成 CSR
     如果你已经有了一个私钥，那么可以直接用它来向 CA 申请证书。
     下面的命令使用一个已有的私钥 (domain.key) 创建一个新的 CSR (domain.csr):
     openssl req \
            -key domain.key \
            -new -out domain.csr
       – -key: 用来指定已有的私钥文件
       – -new: 表明我们要生成一个 CSR
   • 使用已有的证书和私钥生成 CSR
     如果你需要续订已有的证书，但你和 CA 都没有原始的 CSR，那可以再次生成 CSR。
     下面的命令使用已有的证书 (domain.crt) 和私钥 (domain.key) 创建一个新的 CSR:
102                                                                 Chapter 6. OpenSSL
                                                             Yang 的笔记, 发布 0.1.0
      openssl x509 \
             -in domain.crt \
             -signkey domain.key \
             -x509toreq -out domain.csr
        – -x509toreq: 表明我们要使用 X509 证书来制作 CSR
6.2 生成证书
6.2.1 自签名证书
如果你只是想用 SSL 证书加固你的 web 服务器，但是并不需要 CA 签名的证书，那么一个简单的方法是自己
签发证书。
一种常见的你可以签发的类型是自签名证书——使用自己的私钥签发的证书。自签名证书可以像 CA 签发的
证书一样用于加密数据，但是你的用户将收到提示说明该证书不被其计算机或浏览器信息。因此，自签名证
书只能在不需要向用户证明你的身份时使用，例如非生产环境或者非公开服务。
    • 生成自签名证书
      下面的命令创建一个 2048 位的私钥 (domain.key) 以及一个自签名证书 (domain.crt):
      openssl req \
             -newkey rsa:2048 -nodes -keyout domain.key \
             -x509 -days 365 -out domain.crt
        – -x509: 表示我们要创建自签名证书
        – -days 365: 声明该证书的有效期为 365 天
      在上面的命令执行过程中将创建一个临时 CSR 来收集与证书相关的 CSR 信息。
    • 使用已有私钥生成自签名证书
      下面的命令使用已有的私钥 (domain.key) 生成一个自签名证书 (domain.crt):
      openssl req \
             -key domain.key \
             -new \
             -x509 -days 365 -out domain.crt
        – -new: 用来启动 CSR 信息采集提示。
    • 使用已有的私钥和 CSR 生成自签名证书
      下面的命令使用私钥 (domain.key) 和 CSR (domain.csr) 创建一个自签名证书 (domain:crt):
6.2. 生成证书                                                                  103
Yang 的笔记, 发布 0.1.0
     openssl x509 \
            -signkey domain.key \
            -in domain.csr \
            -req -days 365 -out domain.crt
6.2.2 私有 CA 签名证书
自签名的证书无法被吊销，CA 签名的证书可以被吊销。能不能吊销证书的区别在于，如果你的私钥被黑客
获取，如果证书不能被吊销，则黑客可以伪装成你与用户进行通信。
如果你的规划需要创建多个证书，那么使用私有 CA 的方法比较合适，因为只要给所有的客户端都安装了 CA
的证书，那么以该证书签名过的证书，客户端都是信任的，也就是安装一次就够了。
如果你直接用自签名证书，你需要给所有的客户端安装该证书才会被信任，如果你需要第二个证书，则还的
挨个给所有的客户端安装证书 2 才会被信任。
简单的 CA 签名可用如下方法
   • 生成 CA 私钥:
     openssl genrsa -des3 -out ca.key 4096
   • 生成 CA 的自签名证书:
     openssl req \
            -key ca.key \
            -new \
            -x509 -days 365 -out ca.crt
     其实 CA 证书就是一个自签名证书
   • 生成服务端私钥:
     openssl genrsa -des3 -out server.key 2048
   • 需要签名的对象（服务端）生成证书签名请求:
     openssl req \
            -key server.key \
            -new \
            -out server.csr
     这里注意证书签名请求当中的 Common Name 必须区别与 CA 的证书里面的 Common Name。
   • 用 CA 证书给生成的签名请求进行签名:
104                                              Chapter 6. OpenSSL
                                                                     Yang 的笔记, 发布 0.1.0
      openssl x509 \
             -CA ca.crt -CAkey ca.key \
             -req -days 365 -in server.csr -set_serial 01 -out server.crt
关于在主机上创建一个证书颁发机构的方法，请参见私有证书颁发机构 。
6.3 查看证书
证书和 CSR 文件都采用 PEM 编码格式，并不适合人类阅读。这一部分主要介绍 OpenSSL 中查看 PEM 编码
文件的命令。
    • 查看 CSR 文件的明文文本并进行验证:
      openssl req -text -noout -verify -in domain.csr
    • 查看证书证书文件的明文文本:
      openssl x509 -text -noout -in domain.crt
    • 验证证书 doman.crt 是否由证书颁发机构 (ca.crt) 签发:
      openssl verify -verbose -CAFile ca.crt domain.crt
6.4 私钥生成与验证
    • 创建私钥
      创建一个密码保护的 2048 位私钥 (domain.key)
      openssl genrsa -des3 -out domain.key 2048
      此命令会提示输入密码。
    • 验证私钥
      验证私钥 (domain.key) 是否有效:
      openssl rsa -check -in domain.key
      如果私钥是加密的，命令会提示输入密码，验证密码成功则会显示不加密的私钥。
    • 验证私钥与证书和 CSR 匹配
      验证私钥 (domain.key) 是否与证书 (domain.crt) 以及 CSR 匹配:
6.3. 查看证书                                                                          105
Yang 的笔记, 发布 0.1.0
     openssl rsa -noout -modulus -in domain.key | openssl md5
     openssl x509 -noout -modulus -in domain.crt | openssl md5
     openssl req -noout -modulus -in domain.csr | openssl md5
     如果上面三个命令的输出一致，那么有极高的概率可以认为私钥、证书和 CSR 是相关的。
   • 加密私钥
     将未加密私钥 (unencrypted.key) 加密，输出加密后的私钥 (encrypted.key)
     openssl rsa -des3 \
            -in unencrypted.key \
            -out encrypted.key
     此命令执行时会提示设置密码。
   • 解密私钥
     将加密私钥 (encrypted.key) 解密，并输出明文 (decrypted.key)
     openssl rsa \
            -in encrypted.key \
            -out decrypted.key
     此命令执行时会提示输入解密密码。
6.5 证书格式转换
我们之前接触的证书都是 X.509 格式，采用 ASCII 的 PEM 编码。还有其他一些证书编码格式与容器类型。
OpenSSL 可以用来在众多不同类型之间转换证书。
   • PEM 转 DER
     将 PEM 编码的证书 (domain.crt) 转换为二进制 DER 编码的证书 (domain.der)
     openssl x509 \
            -in domain.crt \
            -outform der -out domain.der
     DER 格式通常用于 Java。
   • DER 转 PEM
     将 DER 编码的证书 (domain.der) 转换为 PEM 编码 (domain.crt)
106                                                            Chapter 6. OpenSSL
                                                                       Yang 的笔记, 发布 0.1.0
      openssl x509 \
             -inform der -in domain.der \
             -out domain.crt
    • PEM 转 PKCS7
      将 PEM 证书 (domain.crt 和 ca-chain.crt) 添加到一个 PKCS7 (domain.p7b) 文件中:
      openssl crl2pkcs7 -nocrl \
             -certfile domain.crt \
             -certfile ca-chain.crt \
             -out domain.p7b
        – -certfile: 指定要添加到 PKCS7 中的证书
      PKCS7 文件也被称为 P7B，通常用于 Java 的 Keystore 和微软的 IIS 中保存证书的 ASCII 文件。
    • PKCS7 转换为 PEM
      将 PKCS7 文件 (domain.p7b) 转换为 PEM 文件 (domain.crt)
      openssl pkcs7 \
             -in domain.p7b \
             -print_certs -out domain.crt
      如果 PKCS7 文件中包含多个证书，例如一个普通证书和一个中间 CA 证书，那么输出的 PEM 文件中将
      包含所有的证书。
    • PEM 转换为 PKCS12
      将私钥文件 (domain.key) 和证书文件 (domain.crt) 组合起来生成 PKCS12 文件 (domain.pfx)
      openssl pkcs12 \
             -inkey domain.key \
             -in domain.crt \
             -export -out domain.pfx
      此命令将提示你输入导出密码，可以留空不填。
      PKCS12 文件也被称为 PFX 文件，通常用于导入/导出微软 IIS 中的证书链。
    • PKCS12 转换为 PEM
      将 PKCS12 文件 (domain.pfx) 转换为 PEM 格式 (omain.combined.crt)
      openssl pkcs12 \
             -in domain.pfx \
             -nodes -out domain.combined.crt
      如果 PKCS12 文件中包含多个条目，例如证书及其私钥，那么生成的 PEM 文件中将包含所有条目。
6.5. 证书格式转换                                                                          107
 Yang 的笔记, 发布 0.1.0
 6.6 私有证书颁发机构
 OpenSSL 中一些工具可用作证书颁发机构。
 证书颁发机构（CA）是签署数字证书的实体。许多网站需要让他们的客户知道连接是安全的，因此他们向国
 际信任的 CA（例如，VeriSign，DigiCert）支付费用为其域名签署证书。
 在某些情况下，与其给像 DigiCert 那样的 CA 支付费用来获得证书，不如充当您自己的 CA 可能更有意义。常
 见情况包括保护内部网站，或者向客户端颁发证书以允许它们向服务器（例如，Apache，OpenVPN）进行身
 份验证。
 参见:
 OpenSSL Certiﬁcate Authority26
 6.6.1 创建根对
 充当证书颁发机构 (CA) 意味着处理加密的私钥对和公共证书。我们将创建的第一个加密对是“根对”。
“根对”由根密钥 (ca.key.pem) 和根证书 (ca.cert.pem) 组成，是您的 CA 的身份识别。
 通常，根 CA 不直接签署服务器或客户端证书。根 CA 仅用于创建一个或多个中间 CA，中间 CA 由根 CA 信
 任以代表其签署证书。这种方式允许根密钥尽可能保持脱机，并不被使用，因为根密钥的任何损害都是灾难
 性的。
   注意: 最佳的做法是在安全环境中创建根对。理想情况下，这应该是一个完全加密，永久隔离互联网的
   计算机。卸下无线网卡并用胶水填充以太网端口。
    1. 准备目录
        选择一个目录 (/root/ca) 来存储所有密钥和证书:
        mkdir /root/ca
        创建目录结构:
        cd /root/ca
        mkdir certs crl newcerts private
        chmod 700 private
        touch index.txt
        touch index.txt.attr
        echo 1000 > serial
        index.txt 和 serial 文件充当扁平数据库文件，以跟踪签名证书。
  26 https://jamielinux.com/docs/openssl-certificate-authority/index.html
 108                                                                      Chapter 6. OpenSSL
                                                                              Yang 的笔记, 发布 0.1.0
    2. 准备配置文件
        您必须为 OpenSSL 创建配置文件才能使用。下载 根 CA 配置文件27 并复制到 /root/ca/openssl.
        cnf。
        下面分部分介绍一下配置文件。
             • [ca]
                     这部分是强制性的。在这里，我们告诉 OpenSSL 使用 [CA_default] 部分中的选项:
                     [ ca ]
                     # `man ca`
                     default_ca = CA_default
             • [CA_default]
                     这部分包含一系列默认值。确保声明之前选择的目录 (/root/ca)
                     [ CA_default ]
                     # Directory and file locations.
                     dir                         = /root/ca
                     certs                       = $dir/certs
                     crl_dir                     = $dir/crl
                     new_certs_dir               = $dir/newcerts
                     database                    = $dir/index.txt
                     serial                      = $dir/serial
                     RANDFILE                    = $dir/private/.rand
                     # The root key and root certificate.
                     private_key                 = $dir/private/ca.key.pem
                     certificate                 = $dir/certs/ca.cert.pem
                     # For certificate revocation lists.
                     crlnumber                   = $dir/crlnumber
                     crl                         = $dir/crl/ca.crl.pem
                     crl_extensions              = crl_ext
                     default_crl_days            = 30
                     # SHA-1 is deprecated, so use SHA-2 instead.
                     default_md                  = sha256
                     name_opt                    = ca_default
                     cert_opt                    = ca_default
                     default_days                = 375
                                                                                     (下页继续)
  27 https://github.com/d12y12/notes/blob/master/_static/OpenSSL/root_ca.conf
6.6. 私有证书颁发机构                                                                               109
Yang 的笔记, 发布 0.1.0
                                                                              (续上页)
           preserve          = no
           policy            = policy_strict
      • [ policy_strict ]
           我们将对所有根 CA 签名应用 policy_strict ，因为根 CA 仅用于创建中间 CA:
           [ policy_strict ]
           # The root CA should only sign intermediate certificates that match.
           # See the POLICY FORMAT section of `man ca`.
           countryName             = match
           stateOrProvinceName     = match
           organizationName        = match
           organizationalUnitName  = optional
           commonName              = supplied
           emailAddress            = optional
      • [ policy_loose ]
           我们将对所有中间 CA 签名应用 policy_loose ，因为中间 CA 用于签署来自各种第三
           方的服务器和客户端证书:
           [ policy_loose ]
           # Allow the intermediate CA to sign a more diverse range of␣
            ,→certificates.
           # See the POLICY FORMAT section of the `ca` man page.
           countryName             = optional
           stateOrProvinceName     = optional
           localityName            = optional
           organizationName        = optional
           organizationalUnitName  = optional
           commonName              = supplied
           emailAddress            = optional
      • [ req ]
           创建证书或证书签名请求时，将应用 [req] 部分中的选项:
           [ req ]
           # Options for the `req` tool (`man req`).
           default_bits        = 2048
           distinguished_name  = req_distinguished_name
           string_mask         = utf8only
           # SHA-1 is deprecated, so use SHA-2 instead.
                                                                            (下页继续)
110                                                                Chapter 6. OpenSSL
                                                                   Yang 的笔记, 发布 0.1.0
                                                                              (续上页)
            default_md           = sha256
            # Extension to add when the -x509 option is used.
            x509_extensions      = v3_ca
       • [ req_distinguished_name ]
            这部分声明证书签名请求中通常需要的信息。您可以选择指定一些默认值:
            [ req_distinguished_name ]
            # See <https://en.wikipedia.org/wiki/Certificate_signing_request>.
            countryName                     = Country Name (2 letter code)
            stateOrProvinceName             = State or Province Name
            localityName                    = Locality Name
            0.organizationName              = Organization Name
            organizationalUnitName          = Organizational Unit Name
            commonName                      = Common Name
            emailAddress                    = Email Address
            # Optionally, specify some defaults.
            countryName_default             = GN
            stateOrProvinceName_default     = Guangdong
            localityName_default            = Shenzhen
            0.organizationName_default      = Yang Ltd
            #organizationalUnitName_default =
            #emailAddress_default           =
            接下来的几部分是签名证书时可以应用的扩展。例如，传递 -extensions v3_ca 命令
            行参数将应用 [v3_ca] 中设置的选项。
       • [ v3_ca ]
            在创建根证书时应用 v3_ca 扩展:
            [ v3_ca ]
            # Extensions for a typical CA (`man x509v3_config`).
            subjectKeyIdentifier = hash
            authorityKeyIdentifier = keyid:always,issuer
            basicConstraints = critical, CA:true
            keyUsage = critical, digitalSignature, cRLSign, keyCertSign
       • [ v3_intermediate_ca ]
            在创建中间证书时应用 v3_ca_intermediate 扩展:
6.6. 私有证书颁发机构                                                                       111
Yang 的笔记, 发布 0.1.0
           [ v3_intermediate_ca ]
           # Extensions for a typical intermediate CA (`man x509v3_config`).
           subjectKeyIdentifier = hash
           authorityKeyIdentifier = keyid:always,issuer
           basicConstraints = critical, CA:true, pathlen:0
           keyUsage = critical, digitalSignature, cRLSign, keyCertSign
            – pathlen：0: 确保中间 CA 下方不再有其他证书颁发机构
      • [ usr_cert ]
           在签署客户端证书 (例如用于远程用户身份验证的证书) 时应用 usr_cert 扩展:
           [ usr_cert ]
           # Extensions for client certificates (`man x509v3_config`).
           basicConstraints = CA:FALSE
           nsCertType = client, email
           nsComment = "OpenSSL Generated Client Certificate"
           subjectKeyIdentifier = hash
           authorityKeyIdentifier = keyid,issuer
           keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
           extendedKeyUsage = clientAuth, emailProtection
      • [ server_cert ]
           在签署服务器证书 (例如用于 Web 服务器的证书) 时应用 server_cert 扩展:
           [ server_cert ]
           # Extensions for server certificates (`man x509v3_config`).
           basicConstraints = CA:FALSE
           nsCertType = server
           nsComment = "OpenSSL Generated Server Certificate"
           subjectKeyIdentifier = hash
           authorityKeyIdentifier = keyid,issuer:always
           keyUsage = critical, digitalSignature, keyEncipherment
           extendedKeyUsage = serverAuth
      • [ crl_ext ]
           在创建证书吊销列表时会自动应用 crl_ext 扩展:
           [ crl_ext ]
           # Extension for CRLs (`man x509v3_config`).
           authorityKeyIdentifier=keyid:always
      • [ ocsp ]
           在签署在线证书状态协议 (OCSP) 证书时应用 ocsp 扩展:
112                                                                Chapter 6. OpenSSL
                                                                       Yang 的笔记, 发布 0.1.0
               [ ocsp ]
               # Extension for OCSP signing certificates (`man ocsp`).
               basicConstraints = CA:FALSE
               subjectKeyIdentifier = hash
               authorityKeyIdentifier = keyid,issuer
               keyUsage = critical, digitalSignature
               extendedKeyUsage = critical, OCSPSigning
   3. 创建根密钥
      创建根密钥 (ca.key.pem) 并保证绝对安全, 因为拥有根密钥的任何人都可以颁发受信任的证书。使用
      AES 256 加密和强密码加密根密钥:
      cd /root/ca
      openssl genrsa -aes256 -out private/ca.key.pem 4096
      Enter pass phrase for ca.key.pem: secretpassword
      Verifying - Enter pass phrase for ca.key.pem: secretpassword
      chmod 400 private/ca.key.pem
        注意: 对所有根证书和中间证书颁发机构密钥使用 4096 位。您仍然可以签署更短的服务器和客户
        端证书。
   4. 创建根证书
      使用根密钥 (ca.key.pem) 创建根证书 (ca.cert.pem):
      cd /root/ca
      openssl req -config openssl.cnf \
             -key private/ca.key.pem \
             -new -x509 -days 7300 -sha256 -extensions v3_ca \
             -out certs/ca.cert.pem
      Enter pass phrase for ca.key.pem: secretpassword
      You are about to be asked to enter information that will be incorporated
      into your certificate request.
      \-----
      Country Name (2 letter code) [CN]:CN
      State or Province Name [Guangdong]:Guangdong
      Locality Name [Shenzhen]:Shenzhen
      Organization Name [Yang Ltd]:Yang Ltd
      Organizational Unit Name []:Yang Ltd Certificate Authority
                                                                                  (下页继续)
6.6. 私有证书颁发机构                                                                        113
Yang 的笔记, 发布 0.1.0
                                                                                  (续上页)
     Common Name []:Yang Ltd Root CA
     Email Address []:
     chmod 444 certs/ca.cert.pem
     给根证书一个很长的失效日期，例如二十年。根证书过期后，CA 签名的所有证书都将失效。
        警告: 无论何时使用 req 工具，都必须指定要与 -config 选项一起使用的配置文件，否则 OpenSSL
        将默认为 /etc/pki/tls/openssl.cnf。
  5. 验证根证书
     使用命令:
     openssl x509 -noout -text -in certs/ca.cert.pem
     输出显示:
         • 使用的签名算法
         • 证书的有效期
         • 公钥长度
         • 发行人，即签署证书的实体
         • 主题，指证书本身
         • 扩展
     颁发者和主题相同，因为证书是自签名的。请注意，所有根证书都是自签名的:
     Signature Algorithm: sha256WithRSAEncryption
     Issuer: C = CN, ST = Guangdong, L = Shenzhen, O = Yang Ltd, OU = Yang Ltd␣
      ,→Certificate Authority, CN = Yang Ltd Root CA
     Validity
          Not Before: Jul  4 08:44:04 2019 GMT
          Not After : Jun 29 08:44:04 2039 GMT
     Subject: C = CN, ST = Guangdong, L = Shenzhen, O = Yang Ltd, OU = Yang Ltd␣
      ,→Certificate Authority, CN = Yang Ltd Root CA
     Subject Public Key Info:
          Public Key Algorithm: rsaEncryption
             RSA Public-Key: (4096 bit)
     输出还显示 X509v3 扩展。我们应用了 v3_ca 扩展，因此 [v3_ca] 中的选项应该反映在输出中:
114                                                                  Chapter 6. OpenSSL
                                                                                      Yang 的笔记, 发布 0.1.0
        X509v3 extensions:
              X509v3 Subject Key Identifier:
                   6A:F6:94:92:65:96:F9:01:92:79:F7:0D:7C:A6:91:DC:EB:1B:38:37
              X509v3 Authority Key Identifier:
                   keyid:6A:F6:94:92:65:96:F9:01:92:79:F7:0D:7C:A6:91:DC:EB:1B:38:37
              X509v3 Basic Constraints: critical
                   CA:TRUE
              X509v3 Key Usage: critical
                   Digital Signature, Certificate Sign, CRL Sign
6.6.2 创建中间对
中间证书颁发机构 (CA) 是可以代表根 CA 签署证书的实体。根 CA 签署中间证书，形成信任链。使用中间
CA 的目的主要是为了安全。根密钥可以保持脱机状态并尽可能不频繁地使用。如果中间密钥被泄露，则根
CA 可以撤销中间证书并创建新的中间加密对。
    1. 准备目录
        根 CA 文件保存在 /root/ca 中。选择其他目录 (/root/ca/intermediate) 来存储中间 CA 文件:
        mkdir /root/ca/intermediate
        创建与根 CA 文件相同的目录结构。创建一个 csr 目录来保存证书签名请求:
        cd /root/ca/intermediate
        mkdir certs crl csr newcerts private
        chmod 700 private
        touch index.txt
        touch index.txt.attr
        echo 1000 > serial
        echo 1000 > /root/ca/intermediate/crlnumber
             • crlnumber: 用于跟踪证书吊销列表
        下载 中间 CA 配置文件28 并复制到 /root/ca/intermediate/openssl.cnf。
        与根 CA 配置文件相比，有五个选项已更改:
        [ CA_default ]
        dir                      = /root/ca/intermediate
        private_key              = $dir/private/intermediate.key.pem
        certificate              = $dir/certs/intermediate.cert.pem
                                                                                                 (下页继续)
  28 https://github.com/d12y12/notes/blob/master/_static/OpenSSL/intermediate_ca.conf
6.6. 私有证书颁发机构                                                                                       115
Yang 的笔记, 发布 0.1.0
                                                                                  (续上页)
     crl              = $dir/crl/intermediate.crl.pem
     policy           = policy_loose
  2. 创建中间密钥
     创建中间密钥 (intermediate.key.pem)。使用 AES 256 加密和强密码加密中间密钥:
     cd /root/ca
     openssl genrsa -aes256 \
            -out intermediate/private/intermediate.key.pem 4096
     Enter pass phrase for intermediate.key.pem: secretpassword
     Verifying - Enter pass phrase for intermediate.key.pem: secretpassword
     chmod 400 intermediate/private/intermediate.key.pem
  3. 创建中间证书
     使用中间密钥创建证书签名请求 (CSR)
     cd /root/ca
     openssl req -config intermediate/openssl.cnf -new -sha256 \
            -key intermediate/private/intermediate.key.pem \
            -out intermediate/csr/intermediate.csr.pem
     Enter pass phrase for intermediate.key.pem: secretpassword
     You are about to be asked to enter information that will be incorporated
     into your certificate request.
     \-----
     Country Name (2 letter code) [CN]:CN
     State or Province Name [Guangdong]:Guangdong
     Locality Name [Shenzhen]:Shenzhen
     Organization Name [Yang Ltd]:Yang Ltd
     Organizational Unit Name []:Yang Ltd Certificate Authority
     Common Name []:Yang Ltd Intermediate CA
     Email Address []:
     标识信息通常应与根 CA 匹配, 但是 通用名称 (Common Name) 必须不同。
       注意: 确保指定中间 CA 配置文件 intermediate/openssl.cnf。
     使用带有 v3_intermediate_ca 扩展名的根 CA 来签署中间 CSR:
116                                                                  Chapter 6. OpenSSL
                                                                      Yang 的笔记, 发布 0.1.0
      cd /root/ca
      openssl ca -config openssl.cnf -extensions v3_intermediate_ca \
             -days 3650 -notext -md sha256 \
             -in intermediate/csr/intermediate.csr.pem \
             -out intermediate/certs/intermediate.cert.pem
      Enter pass phrase for ca.key.pem: secretpassword
      Sign the certificate? [y/n]: y
      chmod 444 intermediate/certs/intermediate.cert.pem
      中间证书的有效期应短于根证书, 例如十年。
         注意: 确保指定根 CA 配置文件 /root/ca/openssl.cnf。
      index.txt 文件是 OpenSSL CA 工具存储证书数据库的位置。请勿手动删除或编辑此文件。它现在应
      该包含一个引用中间证书的行:
      V        290701141133Z           1000    unknown /C=CN/ST=Guangdong/O=Yang Ltd/
       ,→OU=Yang Ltd Certificate Authority/CN=Yang Ltd Intermediate CA
   4. 验证中间证书
      和验证根证书一样，检查中间证书的详细信息是否正确:
      openssl x509 -noout -text \
              -in intermediate/certs/intermediate.cert.pem
      根据根证书验证中间证书:
      openssl verify -CAfile certs/ca.cert.pem \
               intermediate/certs/intermediate.cert.pem
      如果输出 OK 表示信任链完好无损:
      intermediate/certs/intermediate.cert.pem: OK
   5. 创建证书链文件
      当应用程序 (例如，Web 浏览器) 尝试验证由中间 CA 签名的证书时，它还必须根据根证书验证中间证
      书。
      要完成信任链，需要创建 CA 证书链以呈现给应用程序:
6.6. 私有证书颁发机构                                                                         117
Yang 的笔记, 发布 0.1.0
     cat intermediate/certs/intermediate.cert.pem \
         certs/ca.cert.pem > intermediate/certs/ca-chain.cert.pem
     chmod 444 intermediate/certs/ca-chain.cert.pem
       注意: 我们的证书链文件必须包含根证书，因为还没有客户端应用程序知道它。更好的选择，特别
       是在管理内网时，是在每个需要连接的客户端上安装根证书, 在这种情况下，链文件只需要包含您
       的中间证书。
6.6.3 签署服务器和客户端证书
我们将使用我们的中间 CA 签署证书。可以在各种情况下使用这些签名证书，例如保护与 Web 服务器的连接
或验证连接到服务的客户端。
提示: 以下步骤是从证书颁发机构的角度出发的。但是，第三方可以创建自己的私钥和证书签名请求（CSR）                                   ，
而无需向您透露其私钥。他们为您提供 CSR，而您将为他们提供已签署的证书，在这情况下，跳过创建密钥
和证书的过程。
  1. 创建密钥
     我们的根和中间对是 4096 位。服务器和客户端证书通常在一年后到期，因此我们可以安全地使用 2048
     位。
     提示: 尽管 4096 位比 2048 位稍微安全一些，但它会降低 TLS 握手速度并显着增加握手期间的处理器
     负载。因此，大多数网站使用 2048 位对。
     如果要创建用于 Web 服务器（例如，Apache）的加密对，则每次重新启动 Web 服务器时都需要输入此
     密码。您可能希望省略 “-aes256“选项以创建没有密码的密钥:
     cd /root/ca
     openssl genrsa -aes256 \
            -out intermediate/private/www.example.com.key.pem 2048
     chmod 400 intermediate/private/www.example.com.key.pem
  2. 创建证书
     使用私钥创建证书签名请求 (CSR)。CSR 详细信息不需要与中间 CA 匹配。
       • 对于服务器证书，公共名称必须是完全限定的域名 (例如，www.example.com)。
       • 对于客户端证书，它可以是任何唯一标识符（例如，电子邮件地址）。
     请注意，通用名称不能与根证书或中间证书相同。
118                                                                Chapter 6. OpenSSL
                                                                       Yang 的笔记, 发布 0.1.0
      cd /root/ca
      openssl req -config intermediate/openssl.cnf \
              -key intermediate/private/www.example.com.key.pem \
              -new -sha256 -out intermediate/csr/www.example.com.csr.pem
      Enter pass phrase for www.example.com.key.pem: secretpassword
      You are about to be asked to enter information that will be incorporated
      into your certificate request.
      \-----
      Country Name (2 letter code) [CN]:CN
      State or Province Name [Guangdong]:Beijing
      Locality Name [Shenzhen]:Beijing
      Organization Name [Yang Ltd]:Yang Ltd
      Organizational Unit Name []:Yang Ltd Web Services
      Common Name []:www.example.com
      Email Address []:
      要创建证书，请使用中间 CA 对 CSR 进行签名。
          • 如果要在服务器上使用证书，请使用 server_cert 扩展名。
          • 如果证书将用于用户身份验证，请使用 usr_cert 扩展名。
      证书通常有效期为一年，但 CA 通常会为方便起见提供额外的几天。
      intermediate/index.txt 应包含引用此新证书的行:
      V         200713143355Z           1000    unknown /C=CN/ST=Beijing/L=Beijing/O=Yang␣
       ,→Ltd/OU=Yang Ltd Web Services/CN=www.example.com
   3. 验证证书
      openssl x509 -noout -text \
               -in intermediate/certs/www.example.com.cert.pem
      发行人是中间 CA. 主题是指证书本身:
      Signature Algorithm: sha256WithRSAEncryption
      Issuer: C = CN, ST = Guangdong, O = Yang Ltd, OU = Yang Ltd Certificate Authority,
       ,→  CN = Yang Ltd Intermediate CA
      Validity
           Not Before: Jul  4 14:33:55 2019 GMT
           Not After : Jul 13 14:33:55 2020 GMT
      Subject: C = CN, ST = Beijing, L = Beijing, O = Yang Ltd, OU = Yang Ltd Web␣
       ,→Services, CN = www.example.com
      Subject Public Key Info:
                                                                                   (下页继续)
6.6. 私有证书颁发机构                                                                          119
Yang 的笔记, 发布 0.1.0
                                                                                    (续上页)
          Public Key Algorithm: rsaEncryption
             RSA Public-Key: (2048 bit)
     输出还将显示 X509v3 扩展。创建证书时，您使用了 server_cert 或 usr_cert 扩展。相应配置部
     分中的选项将反映在输出中:
     X509v3 extensions:
          X509v3 Basic Constraints:
             CA:FALSE
          Netscape Cert Type:
             SSL Server
          Netscape Comment:
             OpenSSL Generated Server Certificate
          X509v3 Subject Key Identifier:
             1C:65:5D:28:DF:FE:40:36:C1:1A:B4:02:FD:CD:AE:B5:B5:72:BE:F3
          X509v3 Authority Key Identifier:
             keyid:A8:10:FC:02:D7:41:51:F7:56:E0:35:94:8B:8F:7D:EB:81:1C:5D:89
             DirName:/C=CN/ST=Guangdong/L=Shenzhen/O=Yang Ltd/OU=Yang Ltd Certificate␣
      ,→Authority/CN=Yang Ltd Root CA
             serial:10:00
          X509v3 Key Usage: critical
             Digital Signature, Key Encipherment
          X509v3 Extended Key Usage:
             TLS Web Server Authentication
     使用我们之前创建的 CA 证书链文件 (ca-chain.cert.pem) 来验证新证书是否具有有效的信任链:
     openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \
               intermediate/certs/www.example.com.cert.pem
     输出:
     intermediate/certs/www.example.com.cert.pem: OK
  4. 部署证书
     现在可以将新证书部署到服务器，也可以将证书分发给客户端。
     部署到服务器应用程序 (例如，Apache) 时，需要提供以下文件:
         • ca-chain.cert.pem
         • www.example.com.key.pem
         • www.example.com.cert.pem
     如果您从第三方签署 CSR，则无法访问其私钥，因此您只需要给他们提供以下文件:
120                                                                    Chapter 6. OpenSSL
                                                                     Yang 的笔记, 发布 0.1.0
         • 链文件 (ca-chain.cert.pem)
         • 证书 (www.example.com.cert.pem)
6.6.4 证书撤销列表
证书吊销列表 (CRL) 提供已吊销的证书列表。
    • 客户端应用程序 (如 Web 浏览器) 可以使用 CRL 检查服务器的真实性。
    • 服务器应用程序 (如 Apache 或 OpenVPN), 可以使用 CRL 拒绝访问不再受信任的客户端。
在可公共访问的位置发布 CRL(例如，http：//example.com/intermediate.crl.pem)。第三方可以
从此位置获取 CRL，以检查它们所依赖的任何证书是否已被撤销。
  注意: 一些应用程序供应商已弃用证书吊销列表 (CRL)，而是使用在线证书状态协议 (OCSP)。
   1. 准备配置文件
      当证书颁发机构签署证书时，它通常会将 CRL 位置编码到证书中。将 crlDistributionPoints 添
      加到适当的部分。在我们的示例中，将其添加到 [server_cert] 部分:
      [ server_cert ]
      # ... snipped ...
      crlDistributionPoints = URI:http://example.com/intermediate.crl.pem
   2. 创建 CRL
      cd /root/ca
      openssl ca -config intermediate/openssl.cnf \
            -gencrl -out intermediate/crl/intermediate.crl.pem
      参见:
      ca 手册页的 CRL OPTIONS 部分包含有关如何创建 CRL 的更多信息。
      您可以使用 crl 工具检查 CRL 的内容:
      openssl crl -in intermediate/crl/intermediate.crl.pem -noout -text
      尚未撤销任何证书，因此输出将声明 No Revoked Certificates。
      您 应 该 定 期 重 新 创 建 CRL。 默 认 情 况 下，CRL 在 30 天 后 到 期, 由 [CA_default] 部 分 中 的
      default_crl_days 控制。
   3. 撤销证书
      让我们来看一个完整的例子。
6.6. 私有证书颁发机构                                                                      121
Yang 的笔记, 发布 0.1.0
    阳正在运行 Apache Web 服务器，并拥有一个私有文件夹。阳想要授权他的朋友川访问此系列。
    川创建私钥和证书签名请求 (CSR):
    cd /home/chuan
    openssl genrsa -out chuan@example.com.key.pem 2048
    openssl req -new -key chuan@example.com.key.pem \
           -out chuan@example.com.csr.pem
    You are about to be asked to enter information that will be incorporated
    into your certificate request.
    \-----
    Country Name (2 letter code) [AU]:CN
    State or Province Name (full name) [Some-State]:Guangxi
    Locality Name (eg, city) []:
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Chuan Ltd
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:chuan@example.com
    Email Address []:
    川将她的 CSR 发送给阳，阳随后签名并验证证书是否有效:
    cd /root/ca
    openssl ca -config intermediate/openssl.cnf \
           -extensions usr_cert -notext -md sha256 \
           -in intermediate/csr/chuan@example.com.csr.pem \
           -out intermediate/certs/chuan@example.com.cert.pem
    Sign the certificate? [y/n]: y
    1 out of 1 certificate requests certified, commit? [y/n]: y
    openssl verify -CAfile intermediate/certs/ca-chain.cert.pem \
           intermediate/certs/chuan@example.com.cert.pem
    intermediate/certs/chuan@example.com.cert.pem: OK
    此时，index.txt 文件应包含一个新条目:
    V        200713143355Z           1000    unknown /C=CN/ST=Beijing/L=Beijing/O=Yang␣
     ,→Ltd/OU=Yang Ltd Web Services/CN=www.example.com
    V        200713151536Z           1001    unknown /C=CN/ST=Guangxi/O=Chuan Ltd/
     ,→CN=chuan@example.com
    阳向川发送签名证书。
    川在她的网络浏览器中安装证书，现在可以访问阳的私人文件夹了，手动滑稽！
122                                                                  Chapter 6. OpenSSL
                                                                      Yang 的笔记, 发布 0.1.0
      倒霉的是川被黑了，阳发现并需要立即撤销她的访问权限:
      cd /root/ca
      openssl ca -config intermediate/openssl.cnf \
             -revoke intermediate/certs/chuan@example.com.cert.pem
      Enter pass phrase for intermediate.key.pem: secretpassword
      Revoking Certificate 1001.
      Data Base Updated
      此时，index.txt 中与川的证书对应的行现在以字符 R 开头。这意味着证书已被撤销:
      V        200713143355Z           1000    unknown /C=CN/ST=Beijing/L=Beijing/O=Yang␣
       ,→Ltd/OU=Yang Ltd Web Services/CN=www.example.com
      R        200713151536Z   190704152120Z   1001    unknown /C=CN/ST=Guangxi/O=Chuan␣
       ,→Ltd/CN=chuan@example.com
      在撤销川的证书后，阳必须重新创建 CRL。
   4. 服务器端使用 CRL
      对于客户端证书，它通常是正在进行验证的服务器端应用程序 (例如，Apache)。此应用程序需要具有对
      CRL 的本地访问权限。
      在刚刚的例子中，阳可以将 SSLCARevocationPath 指令添加到他的 Apache 配置中，并将 CRL 复制
      到他的 Web 服务器。下次川连接到 Web 服务器时，Apache 将根据 CRL 检查其客户端证书并拒绝访问。
      类似地，OpenVPN 有一个 crl-verify 指令，因此它可以阻止已撤销证书的客户端。
   5. 客户端使用 CRL
      对于服务器证书，它通常是执行验证的客户端应用程序 (例如，Web 浏览器)。此应用程序必须具有对
      CRL 的远程访问权限。
      如果证书是使用包含 crlDistributionPoints 的扩展来签名的，则客户端应用程序可以读取此信
      息并从指定位置获取 CRL。
      CRL 分发点在证书 X509v3 详细信息中可见:
      X509v3 CRL Distribution Points:
             Full Name:
                URI:http://example.com/intermediate.crl.pem
6.6. 私有证书颁发机构                                                                         123
Yang 的笔记, 发布 0.1.0
6.6.5 在线证书状态协议
创建在线证书状态协议 (OCSP) 作为证书撤销列表 (CRL) 的替代方案。与 CRL 类似，OCSP 使请求方 (例如，
web 浏览器) 能够确定证书的撤销状态。
当 CA 签署证书时，它们通常会在证书中包含 OCSP 服务器地址 (例如，http：//ocsp.example.com)。这与用于
CRL 的 crlDistributionPoints 的功能类似。
例如，当 Web 浏览器显示服务器证书时，它将向证书中指定的 OCSP 服务器地址发送查询。在此地址，OCSP
响应程序侦听查询并以证书的撤销状态作出响应。
提示: 建议尽可能使用 OCSP，实际上，您只需要 OCSP 来获取网站证书。某些 Web 浏览器已弃用或删除了
对 CRL 的支持。
  1. 准备配置文件
     要使用 OCSP，CA 必须将 OCSP 服务器位置编码到为其签名的证书中。在我们的示例中，使用
     [server_cert] 部分的 authorityInfoAccess 选项:
     [ server_cert ]
     # ... snipped ...
     authorityInfoAccess = OCSP;URI:http://ocsp.example.com
  2. 创建 OCSP 对
     OCSP 响应者需要用 OCSP 加密对来签署它发送给请求方的响应。OCSP 加密对与正在检查的证书必须
     由同一 CA 签署签名。
     创建私钥并使用 AES-256 加密对其进行加密:
     cd /root/ca
     openssl genrsa -aes256 \
           -out intermediate/private/ocsp.example.com.key.pem 4096
     创建证书签名请求 (CSR)。细节通常应与签名 CA 的细节匹配。但是，通用名 (Common Name) 必须是
     完全限定的域名:
     cd /root/ca
     openssl req -config intermediate/openssl.cnf -new -sha256 \
           -key intermediate/private/ocsp.example.com.key.pem \
           -out intermediate/csr/ocsp.example.com.csr.pem
     Enter pass phrase for intermediate.key.pem: secretpassword
     You are about to be asked to enter information that will be incorporated
     into your certificate request.
                                                                                 (下页继续)
124                                                                  Chapter 6. OpenSSL
                                                                      Yang 的笔记, 发布 0.1.0
                                                                                  (续上页)
      \-----
      Country Name (2 letter code) [CN]:CN
      State or Province Name [Guangdong]:Guangdong
      Locality Name [Shenzhen]:
      Organization Name [Yang Ltd]:Yang Ltd
      Organizational Unit Name []:Yang Ltd Certificate Authority
      Common Name []:ocsp.example.com
      Email Address []:
      使用中间 CA 对 CSR 进行签名:
      openssl ca -config intermediate/openssl.cnf \
             -extensions ocsp -days 375 -notext -md sha256 \
             -in intermediate/csr/ocsp.example.com.csr.pem \
             -out intermediate/certs/ocsp.example.com.cert.pem
      验证证书是否具有正确的 X509v3 扩展:
      openssl x509 -noout -text \
             -in intermediate/certs/ocsp.example.com.cert.pem
      X509v3 Key Usage: critical
         Digital Signature
      X509v3 Extended Key Usage: critical
         OCSP Signing
   3. 撤销证书
      OpenSSL ocsp 工具可以充当 OCSP 响应器，但它仅用于测试。有可在生产中使用的 OCSP 响应器，但
      这些超出了本文的范围。
      创建要测试的服务器证书:
      cd /root/ca
      openssl genrsa -out intermediate/private/test.example.com.key.pem 2048
      openssl req -config intermediate/openssl.cnf \
             -key intermediate/private/test.example.com.key.pem \
             -new -sha256 -out intermediate/csr/test.example.com.csr.pem
      openssl ca -config intermediate/openssl.cnf \
             -extensions server_cert -days 375 -notext -md sha256 \
             -in intermediate/csr/test.example.com.csr.pem \
             -out intermediate/certs/test.example.com.cert.pem
      在 localhost 上运行 OCSP 响应程序。
      OCSP 响应程序不直接在单独的 CRL 文件中存储撤销状态，而是直接读取 index.txt 。响应使用
6.6. 私有证书颁发机构                                                                       125
Yang 的笔记, 发布 0.1.0
    OCSP 加密对进行签名 (使用 -rkey 和 -rsigner 选项):
    openssl ocsp -port 2560 \
            -index intermediate/index.txt \
            -CA intermediate/certs/ca-chain.cert.pem \
            -rkey intermediate/private/ocsp.example.com.key.pem \
            -rsigner intermediate/certs/ocsp.example.com.cert.pem \
            -nrequest 1 \
            -text
    Enter pass phrase for ocsp.example.com.key.pem: secretpassword
    在另一个终端中，向 OCSP 响应者发送查询。-cert 选项指定要查询的证书:
    openssl ocsp -CAfile intermediate/certs/ca-chain.cert.pem \
            -url http://127.0.0.1:2560 -resp_text \
            -issuer intermediate/certs/intermediate.cert.pem \
            -cert intermediate/certs/test.example.com.cert.pem
    输出:
    OCSP Response Data:
         OCSP Response Status: successful (0x0)
         Response Type: Basic OCSP Response
         Version: 1 (0x0)
         Responder Id: C = CN, ST = Guangdong, L = Shenzhen, O = Yang Ltd, OU = Yang␣
     ,→Ltd Certificate Authority, CN = ocsp.example.com
         Produced At: Jul  4 16:10:49 2019 GMT
         Responses:
         Certificate ID:
            Hash Algorithm: sha1
            Issuer Name Hash: 3C550CCB561AC011EBD5CA8638D2983A9DEBBAEF
            Issuer Key Hash: A810FC02D74151F756E035948B8F7DEB811C5D89
            Serial Number: 1003
         Cert Status: good
         This Update: Jul  4 16:10:49 2019 GMT
    输出的开头显示：
        • 是否收到成功回复（OCSP Response Status）
        • 响应者的身份（Responder Id）
        • 证书的撤销状态（Cert Status）
    撤销证书:
126                                                                   Chapter 6. OpenSSL
                                                                     Yang 的笔记, 发布 0.1.0
     openssl ca -config intermediate/openssl.cnf \
            -revoke intermediate/certs/test.example.com.cert.pem
     Enter pass phrase for intermediate.key.pem: secretpassword
     Revoking Certificate 1003.
     Data Base Updated
     和刚刚一样，运行 OCSP 响应器并在另一个终端上发送查询。
     这次，输出显示 Cert Status：revoked 和 Revocation Time:
     OCSP Response Data:
         OCSP Response Status: successful (0x0)
         Response Type: Basic OCSP Response
         Version: 1 (0x0)
         Responder Id: C = CN, ST = Guangdong, L = Shenzhen, O = Yang Ltd, OU = Yang␣
      ,→Ltd Certificate Authority, CN = ocsp.example.com
         Produced At: Jul  4 16:17:29 2019 GMT
         Responses:
         Certificate ID:
            Hash Algorithm: sha1
            Issuer Name Hash: 3C550CCB561AC011EBD5CA8638D2983A9DEBBAEF
            Issuer Key Hash: A810FC02D74151F756E035948B8F7DEB811C5D89
            Serial Number: 1003
         Cert Status: revoked
         Revocation Time: Jul  4 16:16:18 2019 GMT
         This Update: Jul  4 16:17:29 2019 GMT
6.6. 私有证书颁发机构                                                                         127
Yang 的笔记, 发布 0.1.0
128                Chapter 6. OpenSSL
                                                                               CHAPTER    7
                                                                            reStructuredText
reStructuredText29 是 Docutils30 和 Sphinx31 使用的默认纯文本标记语言。
Docutils 提供基本的 reStructuredText 语法，而 Sphinx 则扩展此语法以支持其他功能。
提示: 本文所有展示效果只在 HTML 输出
7.1 标题和分隔线
7.1.1 标题
定义标题的方式是在行的下面添加特殊符号，例如“================”，但必须遵守一些原则：
    1. 符号长度至少和文字行一样长, 更长也行
    2. 相同级别必须使用统一的符号, 否则会被识别为更小的级别
通常没有专门的符号表示标题的等级，但是对于 Python 文档，可以这样认为:
     • #，及上划线表示部分
     • *，及上划线表示章节
  29 http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
  30 http://docutils.sourceforge.net/
  31 http://www.sphinx-doc.org/
                                                                                         129
Yang 的笔记, 发布 0.1.0
   • =，小章节
   • -，子章节
   • ^，子章节的子章节
   •“，段落
对于写其他文档，可以这样：
   • =，标题
   • -，章
   • ~，节
   • #，小节
注解: 这里为了避免目录生成出错，我就不举例了。
7.1.2 分隔线
连续 4 个以上的 - , ~ , = 等字符放在首尾空行之间。
例 7.1 :
                               例 7.1: 分隔线
---------
7.2 段落和换行
7.2.1 段落
段落是 reStructuredText 文档中最基础的部分，段落通过一个或者多个空行分隔开。左侧必须对齐 (没有空格，
或者有相同多的空格)。
普通的文本段落之间，还有块级元素之间，必须使用一个空行加以区分，否则会被 reStructuredText 折叠到上
一行。
段落引述，使用缩进即可。
例 7.2 :
130                                         Chapter 7. reStructuredText
                                       Yang 的笔记, 发布 0.1.0
                       例 7.2: 段落引述
这是个正常段落。
    这是个引述。
        这是个内嵌引述。
7.2.2 换行
如果要换行，在行首显示使用 | 加一个空格, 只使用回车是不会形成换行的。
例 7.3 :
                        例 7.3: 换行
| 《早发白帝城》
| 朝辞白帝彩云间,
| 千里江陵一日还。
| 两岸猿声啼不住，
| 轻舟已过万重山。
《黄鹤楼送孟浩然之广陵》
故人西辞黄鹤楼，
烟花三月下扬州。
孤帆远影碧空尽，
唯见长江天际流。
7.3 代码
7.3.1 缩进代码块
在段落末尾添加 :: ，并且代码块需要与周围文本以空行分隔，代码的左侧必须缩进, 代码引用到没有缩进的
行为止。
例 7.4 :
                      例 7.4: 缩进代码块
这是一段代码::
                                                  (下页继续)
7.3. 代码                                              131
Yang 的笔记, 发布 0.1.0
                                                                                   (续上页)
   def hello(name):
        print("Hello", name.title())
这个 :: 标记很优雅:
   • 如果作为独立段落存在, 则整段都不会出现在文档里.
   • 如果前面有空白，则标记被移除.
   • 如果前面是非空白，则标记被一个冒号取代.
7.3.2 栅栏代码块
使用 code 或 Sphinx 扩展的 code-block，highlight 指令。
例 7.5 :
                                       例 7.5: code 指令
.. code:: python
   :number-lines: 1
   def hello(name):
        print("Hello", name.title())
例 7.6 :
                                  例 7.6: code + highlight 指令
.. highlight:: python
   :linenothreshold: 1
.. code::
   def hello(name):
        print("Hello", name.title())
.. highlight:: default
例 7.7 :
                                    例 7.7: code-block 指令
.. code-block:: python
   :linenos:
                                                                                 (下页继续)
132                                                          Chapter 7. reStructuredText
                                                               Yang 的笔记, 发布 0.1.0
                                                                           (续上页)
     def hello(name):
             print("Hello", name.title())
简单介绍一下这三个指令
     • code:
        .. code:: 高亮语言
              :number-lines: 第一行行号
     • highlight:
        .. highlight:: 高亮语言
              :linenothreshold: 此处给一个门限值，
                                       当代码行数大于这个门限显示行数，
                                       否则不显示
        这个指令是个全局命令。如果这个命令选择的高亮语言失败了，code 和 code-block 指令也不会高
        亮代码。如果一个文档只使用一种语言，可以在文档头部定义高亮语言，在文档的尾部设会默认。
     • code-block:
        .. code-block:: 高亮语言
              :linenos: 不带参数，有则显示行数，没有不显示
              :lineno-start: 第一行行号，这个会自动使能 linenos
              :emphasize-lines: 强调某些行，如“3,5-8”，则第 3 行，第 5 到第 8 行斜体
              :caption: 代码块的标题，有这个才能编号
              :name: 代码块的名字，可以用来引用
所有支持的高亮语言，请参见 Available lexers32 。
7.3.3 内联代码段
使用两个反引号 `` 来包含，反引号前后要有空格。
例 7.8 :
  32 https://pygments.org/docs/lexers/
7.3. 代码                                                                      133
Yang 的笔记, 发布 0.1.0
                                     例 7.8: 内联代码
这是一个内联 ``code``
7.3.4 包含源文件
使用 literalinclude 指令:
.. literalinclude:: 文件，通常使用相对路径，
                      如果使用绝对路径 (以 ``/`` 开头)，则相对源文件顶级目录
   :language: 高亮语言
   :linenos: 是否显示行号
   :emphasize-lines: 强调某些行，如“3,5-8”，则第 3 行，第 5 到第 8 行斜体
   :encoding: 编码格式
   :lines: 指定哪些行被包含进文件，如“3,5-8”，则第 3 行，第 5 到第 8 行被包含
   :diff: 文件，展示两个文件的 diff
例 7.9 :
                                    例 7.9: 包含源文件
   .. literalinclude:: ../_static/reStructuredText/example.py
        :language: python
        :linenos:
        :lines: 1-3
7.4 列表
7.4.1 无序列表
无序列表使用 * 、+ 或是 - 作为列表标记。
例 7.10 :
                                    例 7.10: 无序列表
 * 李白
 * 杜甫
 * 白居易
列表可以嵌套，但是需跟父列表使用空行分隔。
134                                                           Chapter 7. reStructuredText
                                          Yang 的笔记, 发布 0.1.0
例 7.11 :
                     例 7.11: 列表嵌套
* 这是一个列表
  * 嵌套列表
  * 子项
* 父列表继续
7.4.2 有序列表
有序列表则使用数字, 大小写字母，大小写罗马字母，或者 # (自动排序) 接着一个 . ，在列表标记上使用的
数字必须递增，数字不必从一开始。
例 7.12 :
                     例 7.12: 有序列表
这个无法形成有序列表：
1. 李白
1. 杜甫
1. 白居易
下面的可以形成有序列表：
#. 李白
#. 杜甫
#. 白居易
1. 李白
2. 杜甫
3. 白居易
a. 李白
b. 杜甫
c. 白居易
3. 李白
4. 杜甫
5. 白居易
7.4. 列表                                                 135
Yang 的笔记, 发布 0.1.0
小技巧: 推荐使用 # 自动排序
7.4.3 任务列表
reStructuredText 不支持生成带复选框的任务列表，但如下方式也能凑合看：
    • [ ] 吃饭
    • [x] 睡觉
    • [ ] 打豆豆
7.4.4 定义列表
用来定义术语。
例 7.13 :
                              例 7.13: 定义列表
术语
    定义术语，必须缩进
    可以有多段组成
* 爬虫 *
    一段自动抓取互联网信息的程序，从互联网上抓取对于我们有价值的信息。
7.4.5 横向列表
使用 hlist 指令:
.. hlist::
    :columns: 每行几项
例 7.14 :
                              例 7.14: 横向列表
.. hlist::
    :columns: 3
    * 这是
                                                                (下页继续)
136                                         Chapter 7. reStructuredText
                                                                    Yang 的笔记, 发布 0.1.0
                                                                                (续上页)
    * 一个
    * 横向
    * 列表
    * 每行三个
  注意: Latex PDF 不支持 hlist
7.4.6 选项列表
选项列表用来描述命令行或者程序的选项和描述。
例 7.15 :
                                     例 7.15: 选项列表
-a         Output all.
-b         Output both (this description is
           quite long).
-c arg     Output just arg.
--long     Output all day long.
-p         This option has two paragraphs in the description.
           This is the first.
           This is the second.   Blank lines may be omitted between
           options (as above) or left in (as here and below).
--very-long-option  A VMS-style option.   Note the adjustment for
                  the required two spaces.
--an-even-longer-option
           The description can also start on the next line.
-2, --two  This option has two variants.
-f FILE, --file=FILE   These two options are synonyms; both have
                     arguments.
/V         A VMS/DOS-style option.
7.4. 列表                                                                           137
Yang 的笔记, 发布 0.1.0
7.5 表格
7.5.1 简单表格
书写简单, 但有一些限制: 需要有多行，且第一列元素不能分行显示。
例 7.16 :
                                    例 7.16: 简单表格
.. table:: Simple Table
   :name: simple_table
   =====  =====  ======
   Inputs        Output
   ------------  ------
   A      B      A or B
   =====  =====  ======
   False  False  False
   True   False  True
   False  True   True
   True   True   True
   =====  =====  ======
7.5.2 网格表格
可以自定义表格的边框。
例 7.17
                                    例 7.17: 网格表格
.. table:: Grid Table
   :name: grid_table
   +------------------------+------------+----------+----------+
   | Header row, column 1   | Header 2   | Header 3 | Header 4 |
   | (header rows optional) |            |          |          |
   +========================+============+==========+==========+
   | body row 1, column 1   | column 2   | column 3 | column 4 |
   +------------------------+------------+----------+----------+
   | body row 2             | Cells may span columns.          |
   +------------------------+------------+---------------------+
   | body row 3             | Cells may  | - Table cells       |
                                                                                 (下页继续)
138                                                          Chapter 7. reStructuredText
                                                                    Yang 的笔记, 发布 0.1.0
                                                                                 (续上页)
    +------------------------+ span rows. | - contain           |
    | body row 4             |             | - body elements.   |
    +------------------------+------------+---------------------+
    | body row 5             | Use the command ``ls | more``.   |
    +------------------------+------------+---------------------+
7.5.3 CSV 表格
使用 csv-table 指令:
.. csv-table:: 表名
    :header: 表头，如"Treat", "Quantity", "Description"
    :widths: 默认每列是同样宽度，这个选项可以指定每列相对宽度, 例如“15, 10, 30”
             也可以使用 auto 选项
    :width： 指定行宽度
    :header-rows: 指定表头行数，默认为 0
    :stub-columns: 行标题列数, 默认为 0
    :file：本地 CSV 文件, 使用相对路径
    :url: 网上 CSV 文件
    :delim: 分隔符，如“字符 | 制表符 | 空格”，默认为“，             ”
例 7.18 :
                                   例 7.18: csv 表格 (数据)
.. csv-table:: Frozen Delights with data!
    :header: "Treat", "Quantity", "Description"
    :widths: 15, 10, 30
    :stub-columns: 1
    "Albatross", 2.99, "On a stick!"
    "Crunchy Frog", 1.49, "If we took the bones out, it wouldn't be
    crunchy, now would it?"
    "Gannet Ripple", 1.99, "On a stick!"
加载文件:
例 7.19 :
                                   例 7.19: csv 表格 (文件)
.. csv-table:: Frozen Delights with csv file!
    :file: ../_static/reStructuredText/example.csv
                                                                               (下页继续)
7.5. 表格                                                                            139
Yang 的笔记, 发布 0.1.0
                                                                       (续上页)
   :widths: 15, 10, 30
   :header-rows: 1
7.5.4 列表表格
使用 list-table 指令:
.. list-table:: 表名
   :widths: 默认每列是同样宽度，这个选项可以指定每列相对宽度, 例如“15, 10, 30”
              也可以使用 auto 选项
   :width： 指定行宽度
   :header-rows: 指定表头行数，默认为 0
   :stub-columns: 行标题列数, 默认为 0
例 7.20 :
                                    例 7.20: 列表表格
.. _my_table:
.. list-table:: Frozen Delights with list!
   :widths: 15 10 30
   :header-rows: 1
   * - Treat
     - Quantity
     - Description
   * - Albatross
     - 2.99
     - On a stick!
   * - Crunchy Frog
     - 1.49
     - If we took the bones out, it wouldn't be
         crunchy, now would it?
   * - Gannet Ripple
     - 1.99
     - On a stick!
140                                              Chapter 7. reStructuredText
                                                        Yang 的笔记, 发布 0.1.0
7.6 链接
7.6.1 超链接
    • 独立链接
      两种格式的独立链接会被生成自动链接，一个是邮件，另一个是以协议名比如“http”,“ftp”,“mailto”
      , “telnet”等开头的合法 URL。
      例 7.21 :
                                         例 7.21: 独立链接
      | 访问 https://cn.bing.com/
      | 邮件 d12y12@hotmail.com
      | 这个 HTML 不会被转换，但 Latex PDF 会转换： www.google.com
    • 外部链接
         – 使用内联的方式，‘链接文本 < 链接 >‘_
           例 7.22 :
                                           例 7.22: 内联链接
           使用 `必应 <https://cn.bing.com/>`_ 进行搜索。
         – 使用引用的方式，`链接文本 `_, 在后面定义链接文本，当链接文本为单个词的时候，也可以不
           加反引号。
           例 7.23 :
                                         例 7.23: 引用外部链接
           搜索只能使用 `微软 必应 `_ 。
           .. _ 微软 必应: https://cn.bing.com/
           外部链接, 比如 Python_
           .. _Python: http://www.python.org/
    • 内部链接
      内部链接可以被用来跳到文档指定位置。
         – 使用引用。
           例 7.24 :
7.6. 链接                                                               141
Yang 的笔记, 发布 0.1.0
                                     例 7.24: 引用内部链接
           内部链接, 比如 点这里_
           .. _ 点这里:
             点这里就到这里了。
         – 使用 ref 角色来跳到文档任意位置，Sphinx 推荐。
           例 7.25 :
                                    例 7.25: ref 角色内部链接
           .. _my-reference-label:
           超链接
           ~~~~~~~
           | 这里指向章节引用, 参见 :ref:`my-reference-label` 。
           | 这里指向图片引用, 参加 :ref:`my-figure` 。
           | 这里指向表格引用, 参加 :ref:`my_table`.
           注解: 为了避免目录混乱，这里并没有定义标题，my-reference-label 定义在标题超链接
         – 使用自动产生链接，如章节标题, 注脚, 引文
           例 7.26 :
                                     例 7.26: 自动产生链接
           | `超链接 `_
           | `注脚 4`_      Latex 不支持
           | `CIT2002`_   Latex 不支持
7.6.2 注脚
Sphinx 建议的使用方式是使用 [#name]_ 来标记注脚，前后要有空格，然后在文尾使用 rubric 加入注脚
段，这个段不会进入文档结构。
例 7.27 :
142                                                    Chapter 7. reStructuredText
                                                 Yang 的笔记, 发布 0.1.0
                             例 7.27: Sphinx 注脚
到底是注脚 [#f1]_ 还是脚注 [#f2]_
.. rubric:: 注脚
.. [#f1] 这是注脚 1
.. [#f2] 这是注脚 2
当然也可以指定注脚号，或者不带名字自动编号注脚。
例 7.28 :
                              例 7.28: 更多注脚
| [3]_ 会是"3" (指定注脚号)
| [#]_ 会是"5" (自动编号)
| [# 注脚 4]_ 会是"4" (自动编号).
| 我们可以再次引用它 [# 注脚 4]_
| 也可以这样引用它 注脚 4_ (内部链接).     Latex 不支持
.. [3] 指定注脚号 3
.. [# 注脚 4] 第一这个在#f1 和#f2 之后，3 被指定了，所以是 4。这个序号是按定义注脚
    的顺序，不是按引用的顺序。这里注脚后面括号里的是只以注脚的方式被引用的地方。
.. [#] 自动编号，所以是 5
最后还有一种符号注脚, 但 Latex 不支持
例 7.29 :
                              例 7.29: 符号注脚
这是一个符号注脚引用: [*]_.
.. [*] 符号注脚在这
7.6.3 引文
引文与注脚类似，只是不用编号，使用 [name]_ 来标记引文。
例 7.30 :
7.6. 链接                                                        143
Yang 的笔记, 发布 0.1.0
                                     例 7.30: 引用文献
这是以一个引用文献: [CIT2002]_.
.. [CIT2002] 这是引用文献，和注脚类似，只是不需要编号
7.6.4 置换
置换能够替换文本、图片、链接、或者其它任何东西的组合。
   • 文本置换
     例 7.31 :
                                       例 7.31: 文本置换
     |RST|_ is a little annoying to type over and over, especially
     when writing about |RST| itself, and spelling out the
     bicapitalized word |RST| every time isn't really necessary for
     |RST| source readability.
     .. |RST| replace:: reStructuredText
     .. _RST: http://docutils.sourceforge.net/rst.html
   • 图片置换
     例 7.32 :
                                       例 7.32: 图片置换
     这只 | 熊猫 | 来自中国。
     .. | 熊猫 | image:: ../_static/reStructuredText/panda.png
        :height: 20
        :width: 20
   • 默认置换
     Sphinx 提供了三个默认置换。
     例 7.33 :
                                       例 7.33: 默认置换
     | release |release|
     | version |version|
                                                                                  (下页继续)
144                                                           Chapter 7. reStructuredText
                                                                   Yang 的笔记, 发布 0.1.0
                                                                                (续上页)
      | today   |today|
    • 其他置换
      还可以置换对象，格式，模板，暂时用不上，以后再说。
7.6.5 文件链接
    • 引用文档
      使用 doc 角色，链接到文档引用，可以是绝对路径或者相对路径，注意这里是引用，不是文件。举个
      例子，如果引用Conda 出现在文档 tools/contents，那么引用路径为 tools/Conda 。例如我们想引用 Conda，
      它和我们在 tools/contents 里是同一级别，所以可以直接引用。
      例 7.34 :
                                       例 7.34: 引用文档
      | 参考 :doc:`Conda <Conda>`   相对路径
      | 参考 :doc:`/tools/Conda`    绝对路径
    • 下载文档
      使用 download 角色，用法同引用文档, 文件需要带扩展名。一般只有生成 html 页面支持下载，可以
      用 only 指令。
      例 7.35 :
                                       例 7.35: 下载文档
       .. only:: html or readthedocs
          下载 :download:`hello <../_static/reStructuredText/hello.txt>`
7.7 图片
7.7.1 内联式
    • 使用 image 指令:
      .. image:: 文件，通常使用相对路径，
                 如果使用绝对路径 (以 ``/`` 开头)，则相对源文件顶级目录
                 可以是 URI
         :alt: 替代文字
                                                                              (下页继续)
7.7. 图片                                                                           145
Yang 的笔记, 发布 0.1.0
                                                                                     (续上页)
         :height: 高度，如"100px", 当 scale 存在的时候要乘以 scale，如 scale 为 50%，则高度为
      ,→"50px"
         :width: 宽度，如"200px", 当 scale 存在的时候要乘以 scale，如 scale 为 50%，则高度为
      ,→"100px"
         :scale: 比例， 如"50 %", 这个"%" 号可有可无，默认为 100%
         :align: 对齐，"top", "middle", "bottom", "left", "center", "right"
         :target: 将图片指向一个链接，可以是 URI，也可以是引用名。
     例 7.36 :
                                         例 7.36: image 指令
       这是一个本地图片
       .. image:: ../_static/reStructuredText/apple_logo.png
          :alt: 本地图片
          :height: 200px
          :width: 200px
          :scale: 50
          :align: center
          :target: `图片 `_
       这是一个网络图片
       .. image:: https://github.com/d12y12/notes/blob/master/_static/reStructuredText/
      ,→tesla_logo.jpg?raw=true
          :alt: 网络图片
          :height: 200px
          :width: 200px
          :scale: 50
          :align: center
   • 使用 figure 指令，可以包含一个图片，图片标题，解释文字:
     .. figure:: 图片地址
         包含所有 image 选项
         :align: 对齐，"left", "center", "right"
         :figwidth: 可以是"image"," 长度"，" 现有行长度的% 百分比"，
                     "image" 选项需要 Python Imaging Library，如果图片不存在或者库不存在，则此选
     项被忽略
                      +---------------------------+
                      |        figure               |
                      |                             |
                                                                                   (下页继续)
146                                                            Chapter 7. reStructuredText
                                                                    Yang 的笔记, 发布 0.1.0
                                                                                  (续上页)
                     |<------ figwidth --------->|
                     |                             |
                     |  +---------------------+    |
                     |  |     image            |   |
                     |  |                      |   |
                     |  |<--- width --------->|    |
                     |  +---------------------+    |
                     |                             |
                     |The figure's caption should|
                     |wrap at this width.          |
                     +---------------------------+
     例 7.37 :
                                        例 7.37: ﬁgure 指令
      .. _my-figure:
      .. figure:: ../_static/reStructuredText/tesla_logo.jpg
         :scale: 50 %
         :alt: map to buried treasure
         :figwidth: 50 %
         :align: center
         This is the caption of the figure (a simple paragraph).
         The legend consists of all elements after the caption.  In this
         case, the legend consists of this paragraph and the following
         table:
         +---------------------------------------------------+-----------------------+
         | Symbol                                            | Meaning               |
         +===================================================+=======================+
         | .. image:: ../_static/reStructuredText/panda.png  | Panda                 |
         |    :scale: 25 %                                   |                       |
         +---------------------------------------------------+-----------------------+
         | .. image:: ../_static/reStructuredText/small.png  | Small                 |
         |    :scale: 25 %                                   |                       |
         +---------------------------------------------------+-----------------------+
         | .. image:: ../_static/reStructuredText/sad.png    | Sad                   |
         |    :scale: 25 %                                   |                       |
         +---------------------------------------------------+-----------------------+
7.7. 图片                                                                             147
Yang 的笔记, 发布 0.1.0
7.7.2 引用式
参见 例 7.32
7.8 角色
7.8.1 文字处理
   • 标记
     例 7.38 :
                                例 7.38: 文字处理-标记
     | 斜体 *italics*
     | 加粗 **bold**
     | 代码 ``code``
       注意: 标记不能叠加
   • 文本解释
     文本解释的语法为 :role:`text` 或 `text`:role:, 功能是把文本 (text) 根据角色 (role) 进行解释。
     例 7.39 :
                                例 7.39: 文字处理-解释
     常用的文本处理角色：
     | 斜体: `text`:emphasis:
     | 粗体：`text`:strong:
     | 代码：`text`:code:
     | 下标：`text`:sub:
     | 上标：`text`:sup:
   • 自定义角色
     例 7.40 :
                             例 7.40: 文字处理-自定义
     .. role:: raw-html(raw)
        :format: html
                                                                (下页继续)
148                                              Chapter 7. reStructuredText
                                                     Yang 的笔记, 发布 0.1.0
                                                             (续上页)
      .. default-role:: raw-html
      `<U>` 下划线 `</U>` 、 `<S>` 删除线 `</S>`
      .. default-role:: title-reference
7.8.2 用户界面交互
    • 用户界面互动，使用 :guilabel: 。任何界面的标签都应该使用这个角色，包含按钮，窗口标题，菜
      单，可选列表等等。
      例 7.41 :
                                      例 7.41: 图形界面标签
      :guilabel:`Cancel`
    • 菜单选择，使用 :menuselection: 。用来表示一连串的菜单选择项。
      例 7.42 :
                                    例 7.42: 图形界面菜单选择
      :menuselection:`Start --> Programs`
7.8.3 其他
    • 缩写，使用 :abbr:。
      例 7.43 :
                                          例 7.43: 缩写
      :abbr:`LIFO (last-in, first-out)`.
7.9 指令
7.9.1 提示
    • 参见，使用 seealso 指令:
7.9. 指令                                                            149
Yang 的笔记, 发布 0.1.0
     .. seealso::
        这是一个参见事项。
   • 注意，使用 attention 指令:
     .. attention::
        这是一个注意事项。
   • 警告，使用 caution 指令:
     .. caution::
        这是一个警告事项。
   • 危险，使用 danger 指令:
     .. danger::
        这是一个危险事项。
   • 错误，使用 error 指令:
     .. error::
        这是一个错误事项。
   • 提示，使用 hint 指令:
     .. hint::
        这是一个提示事项。
   • 重要，使用 important 指令:
     ..  important::
        这是一个重要事项。
   • 注释，使用 note 指令:
     .. note::
        这是一个注释事项。
150                      Chapter 7. reStructuredText
                                             Yang 的笔记, 发布 0.1.0
    • 贴士，使用 tip 指令:
      .. tip::
         这是一个贴士事项。
    • 警告，使用 warning 指令:
      .. warning::
         这是一个警告事项。
    • 定制，使用 admonition 指令:
      .. admonition:: 忠告名
         这是一个自定事项。
    • 版本添加，使用 versionadded 指令:
      .. versionadded:: 2.5
         The *spam* parameter.
    • 版本修改，使用 versionchanged 指令:
      .. versionchanged:: 2.5
         The *spam* parameter.
    • 版本删除，使用 deprecated 指令:
      .. deprecated:: 2.5
         Use :func:`spam` instead.
7.9.2 目录及目录树
    • 目录，使用 contents 指令:
      .. contents:: 目录名
         :depth: 目录深度，如“2”，只包含一级和二级标题
         :local: 如果存在，则只生成从 contents 指令开始的目录
                 不存在，则生成整个文档的目录
         :backlinks: "entry" 生成返回目录条目的链接
                     "top" 生成返回目录本身的链接
                     "none" 不生成返回链接
7.9. 指令                                                    151
Yang 的笔记, 发布 0.1.0
   • 目录树，使用 toctree 指令:
     .. toctree::
         :maxdepth: 最大深度，包含子文档
         :numbered: 是否给顶级目录章节编号
         :caption: 目录树名
         :titlesonly: 只给出子文档 title
         :glob: 如果使用，则匹配文档名，
                如文档名使用"intro*", 匹配所有以"intro" 开头的文档，
                如"recipe/*", 则所有 recip 文件夹内的文档
         :reversed: 与 glob 同时使用，则反向选择 glob 匹配
         :hidden: 文档被包含在文档结构中，但是不会显示在目录树中
7.9.3 术语表
使用 glossary 指令包含一个定义列表。这些定义其后可被 term 引用。
例 7.44 :
                                      例 7.44: 术语表
.. glossary::
   environment
       A structure where information about all documents under the root is
       saved, and used for cross-referencing.  The environment is pickled
       after the parsing stage, so that successive runs only need to read
       and parse new and changed documents.
   source directory
       The directory which, including its subdirectories, contains all
       source files for one Sphinx project.
:term:`environment`
7.9.4 数学
使用 math 指令
例 7.45 :
152                                                           Chapter 7. reStructuredText
                                                   Yang 的笔记, 发布 0.1.0
                                      例 7.45: 数学 1
.. math::
    :nowrap:
    \begin{eqnarray}
       y     & = & ax^2 + bx + c \\
       f(x) & = & x^2 + 2xy + y^2
    \end{eqnarray}
例 7.46 :
                                      例 7.46: 数学 2
.. math::
    (a + b)^2 = a^2 + 2ab + b^2
    (a - b)^2 = a^2 - 2ab + b^2
例 7.47 :
                                      例 7.47: 数学 3
.. math::
    (a + b)^2   &=  (a + b)(a + b) \\
             &=  a^2 + 2ab + b^2
.. math:: (a + b)^2 = a^2 + 2ab + b^2
例 7.48 :
7.9. 指令                                                          153
Yang 的笔记, 发布 0.1.0
                                                          例 7.48: 数学 4
.. math::
    α_t(i) = P(O_1, O_2, … O_t, q_t = S_i λ)
关于如何写数学表达式，参见 AMS-LaTeX33
7.9.5 排版
    • 主题，独立于文档大纲的章节，只可以包含一个章节，在无缩进的情况下使用 topic 指令。
       例 7.49 :
                                                           例 7.49: 主题指令
       .. topic:: Topic Title
            Subsequent indented lines comprise
            the body of the topic, and are
            interpreted as body elements.
    • 侧边栏，在无缩进的情况下使用 sidebar 指令。
       例 7.50 :
 33 https://www.ams.org/publications/authors/tex/amslatex
154                                                                     Chapter 7. reStructuredText
                                                                                 Yang 的笔记, 发布 0.1.0
                                                                   例 7.50: 侧边栏指令
        .. sidebar:: Sidebar Title
              :subtitle: Optional Sidebar Subtitle
              Subsequent indented lines comprise
              the body of the sidebar, and are
              interpreted as body elements.
7.10 注释
任何以 .. 标记开始，但不使用任何指令结构的文本，都视为注释:
.. 这是一个注释。你看不到。
可以通过缩进产生多行注释:
..
     你还是看不到。
     看不到。。       。
     看不到。。       。
现在你能看到了，但这不是注释了。
7.11 参考
    1. reStructuredText Primer34
    2. reStructuredText Directives35
    3. docutils Directives36
    4. docutils rst reference37
    5. The Docutils Document Tree38
  34 http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
  35 http://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html
  36 https://docutils.sourceforge.io/docs/ref/rst/directives.html
  37 https://docutils.sourceforge.io/docs/ref/rst/restructuredtext
  38 https://docutils.sourceforge.io/docs/ref/doctree.html
7.10. 注释                                                                                       155
Yang 的笔记, 发布 0.1.0
156                Chapter 7. reStructuredText
                                                                                             CHAPTER  8
                                                                                                 Sphinx
8.1 Docstring
三种风格：
    • reStructuredText 风格39
       类:
       """
       类描述
       :param 属性: 属性的描述
       :type 属性: 属性的类型
       """
       函数:
       """
       函数描述
       :arg 属性: 参数的描述
       :type 属性: 参数的类型
                                                                                                  (下页继续)
 39 http://www.sphinx-doc.org/en/master/usage/restructuredtext/domains.html#info-field-lists
                                                                                                     157
Yang 的笔记, 发布 0.1.0
                                                                                           (续上页)
       :return: 返回值的描述
       :rtype: 返回值的类型
       :raises 返回值: 生成器返回值描述
       Returns/Yields:
              类型: 返回值的描述/生成器返回值描述
       Raises:
              异常名称: 异常描述
       """
    • Google 风格40
       类:
       """
       类描述
       Attributes:
              属性 (类型): 属性的描述
       """
       函数:
       """
       函数描述
       Args:
              参数 (类型): 参数的描述
       Returns/Yields:
              类型: 返回值的描述/生成器返回值描述
       Raises:
              异常名称: 异常描述
       """
    • Numpy 风格41
       类:
       """
       类描述
                                                                                         (下页继续)
 40 http://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings
 41 https://numpydoc.readthedocs.io/en/latest/format.html#
158                                                                            Chapter 8. Sphinx
                                Yang 的笔记, 发布 0.1.0
                                             (续上页)
     Parameters
     ----------
     参数 : 类型
         参数的描述
     Attributes
     ----------
     属性 : 类型
         属性的描述
     Methods
     -------
     函数 (参数列表)
         函数描述，注意不要把 self 放入参数列表
     """
     函数:
     """
     函数描述
     Parameters
     ----------
     参数 : 类型
         参数的描述
     Returns/Yields
     ---------------
     返回值 : 类型
         返回值的描述
     Raises
     ------
     异常名称
         异常描述
     Examples
     --------
     范例描述
     >>> 范例
     Note
                                           (下页继续)
8.1. Docstring                                 159
Yang 的笔记, 发布 0.1.0
                                                                          (续上页)
     ----
     注释内容
     See Also
     --------
     参考内容
     Warnings
     --------
     警告内容
     """
我推荐使用Napoleon 扩展 来支持 Google/Numpy 风格，毕竟 reStructuredText 风格太难看。
简单比较一下 Google/Numpy 的区别，至于如何选择看个人喜好:
                                     Google 风格 Numpy 风格
                           分段方式      缩进分段      连续下划线分段
                           空间需求      横向空间      纵向空间
                           方便阅读      短小精干型     长而精细型
8.2 扩展
8.2.1 Napoleon 扩展
Napoleon 扩展可以让 Sphinx 支持 Google/Numpy 风格的 docstring 。
   • 使能
     将 sphinx.ext.napoleon 添加到扩展列表:
     extensions = [
          'sphinx.ext.autodoc',
          'sphinx.ext.napoleon',
     ]
   • 配置
     默认值:
     # Napoleon settings
     napoleon_google_docstring = True # 是否支持解析 google 风格
     napoleon_numpy_docstring = True  # 是否支持解析 numpy 风格
                                                                        (下页继续)
160                                                           Chapter 8. Sphinx
                                                                       Yang 的笔记, 发布 0.1.0
                                                                                     (续上页)
      napoleon_include_init_with_doc = False       # 构造函数 ( __init___ ) 是否放入文档
      napoleon_include_private_with_doc = False # 私有成员 (如        _membername) 是否放入文档
      napoleon_include_special_with_doc = True     # 内置成员 (如     __membername__) 是否放入文档
      napoleon_use_admonition_for_examples = False # True     使用 .. admonition:: 指令展示示
      例
                                                      # False 使用 .. rubric:: 指令展示示例
      napoleon_use_admonition_for_notes = False # True     使用 .. admonition:: 指令展示注释
                                                   # False 使用 .. rubric:: 指令展示注释
      napoleon_use_admonition_for_references = False # True     使用 .. admonition:: 指令展示
      引用
                                                        # False 使用 .. rubric:: 指令展示引用
      napoleon_use_ivar = False # True     使用 :ivar: 角色展示变量
                                  # False 使用 .. attribute:: 指令展示变量
      napoleon_use_param = True # True     对每一个函数变量使用 :param: 角色展示
                                  # False 函数的所有变量使用一个 :parameters: 角色展示
      napoleon_use_rtype = True # True     使用 :rtype: 角色展示返回变量类型
                                  # False 将返回值类型内联入返回值描述，
                                  #       如 :returns: *bool* -- True if successful,␣
       ,→False otherwise
      一般来说，我们只需要使用一种，比如 numpy:
      napoleon_google_docstring = False
      napoleon_numpy_docstring = True
8.2.2 autosectionlabel 扩展
autosectionlabel 扩展允许使用章节标题进行引用。
    • 使能
      将 sphinx.ext.autosectionlabel 添加到扩展列表:
      extensions = [
            'sphinx.ext.autosectionlabel',
      ]
    • 配置
      默认值:
      autosectionlabel_prefix_document = False # True     使用文档名称作为章节标题标签的前缀，
                                                 #        例如 index:Introduction，意思是␣
       ,→index.rst 中的 Introduction 章节
                                                                                   (下页继续)
8.2. 扩展                                                                                161
Yang 的笔记, 发布 0.1.0
                                                                         (续上页)
                                             # False 不使用前缀
                                             # 这个配置的主要作用是避免不同文章使用相同的标题
    导致引用混乱
    autosectionlabel_maxdepth = None # 选择允许到几级标题生成自动标签，可以被引用
                                     # 例如设成 1，那么只有文章大标题 (最高级) 可以被引用
                                     # None 不使能此功能，所有级别标题都可引用
162                                                          Chapter 8. Sphinx
                                                            CHAPTER     9
                                                       Visual Studio Code
                       42
9.1 VS Code 安装
首先根据自己的系统 下载软件43 。我使用 Windows, 这里有安装版和便携版可以选，懒得可以直接下安装版
安装，我用的便携版，下载后解压就能用。
如果你想在 PowerShell 或者 Cmd 里使用 code 命令，需要把 VS Code 加入到 PATH。
 42 https://code.visualstudio.com/
 43 https://code.visualstudio.com/download
                                                                       163
  Yang 的笔记, 发布 0.1.0
  9.2 VS Code 配置
  9.2.1 中文包
  默认 VS Code 只有英文，需要给它下一个中文包扩展。
  点击       ，在搜索框中输入 chinese，点击 install
  忘了需不需要重新启动 VS Code，如果没切换成中文你就重启一下 VS Code。
  9.2.2 扩展位置
  如果你跟我一样是便携软件爱好者，肯定不喜欢 VS Code 默认把扩展安装在你的用户目录 ~/.vscode/
  extensions/，这样应用程序虽然便携了，但扩展带不走了。我写了个批处理解决这个问题, 你只需要把这
  个存成 .bat 文件，放到你想存放 extensions 的文件夹，运行这个批处理就行了。
1 @echo off
2 set destDir=%CD%\extensions
3 if not exist "%destDir%" (md "%destDir%")
4 if exist "%UserProfile%\.vscode\extensions" (xcopy "%UserProfile%\.vscode\extensions"
   ,→ "%destDir%" /v /s /e /k /y)
5 if exist "%UserProfile%\.vscode\extensions" (rd /S /Q "%UserProfile%\.vscode\
   ,→ extensions")
6 mklink /D "%UserProfile%\.vscode\extensions" "%destDir%"
  简单解释一下:
  164                                                         Chapter 9. Visual Studio Code
                                                                                 Yang 的笔记, 发布 0.1.0
    • 行 2: 设置目标目录为当前目录下的 extensions 目录
    • 行 3: 检查目录是否存在，不存在则创建
    • 行 4: 检查之前 VS Code 默认目录是否存在，存在就把里面的扩展考到这边
    • 行 5: 删除默认目录
    • 行 6: 创建一个目录符号链接
从 VS Code 角度看，默认目录还在那，只不过它已经链接到你指定的目录了。
以后凡是遇到默认目录不能改的，你都可以用符号链接来解决。
9.2.3 快捷键
在 文件 ‣ 首选项 ‣ 键盘快捷方式，可以查看快捷键列表。
                                快捷键                     用途
                                Ctrl + Shift + P        打开命令面板
                                Ctrl + Shift + X        打开扩展面板
                                Ctrl + Shift + G        打开 Git 面板
                                Ctrl + ,                打开设置面板
                                Ctrl + `                跳到终端
9.2.4 字体设置
在 VS Code 中使用中文的时候，有个对齐的问题，很烦躁，比如画表格。通过改变字体设置，可以让对齐整
齐好看一些。目前看来只有 更纱字体能完美的完成这个任务。
   1. 下载
      在     ‘字 体 下 载 链 接             <https://github.com/be5invis/Sarasa-Gothic/releases>‘_ 中 下 载
      sarasa-gothic-ttf-*.7z, 并解压缩
   2. 安装
      在解压缩文件夹中，找到 sarasa-mono-sc-*.ttf 的所有文件，我用的 0.11.0 版本中，有 10 个文件
      选中字体 ‣ 右键 ‣ 安装
   3. 配置
      命令面板 ‣ 输入: settings.json, 打开用户配置文件，添加如下内容:
      "[markdown]": {
          "editor.fontFamily": "'Sarasa Mono SC'"
      },
                                                                                             (下页继续)
9.2. VS Code 配置                                                                                 165
Yang 的笔记, 发布 0.1.0
                                                                                (续上页)
        "[restructuredtext]": {
                "editor.fontFamily": "'Sarasa Mono SC'"
        },
        我们只针对写作工具更改了字体，不会影响终端，及其他代码编辑。
        有兴趣使用 Fira Code 的，下载 最新版44 Fira Coda, 命令面板 ‣ 输入: settings.json, 打开用户配置文
        件，添加如下内容:
        "editor.fontFamily": "'Fira Code'",
        "editor.fontLigatures": true,
9.2.5 缩进设置
命令面板 ‣ 输入: settings.json, 打开用户配置文件，添加如下内容:
"[restructuredtext]": {
       "editor.tabSize": 3
}
9.3 VS Code 扩展
VS Code 的扩展包含内置和安装两种。内置扩展在发行包里就自带了，其他的扩展要自己手动安装。可以在
这里45 搜索你感兴趣的扩展。
9.3.1 Git 扩展
要使用 VSCode 中的内置 Git 扩展，首先要在系统中安装并配置好 Git , 参见Git 命令行客户端 。
下面介绍一下如何配置，点击 文件 ‣ 首选项 ‣ 设置
  44 https://github.com/tonsky/FiraCode/releases
  45 https://marketplace.visualstudio.com/vscode
166                                                     Chapter 9. Visual Studio Code
                                                         Yang 的笔记, 发布 0.1.0
在 搜索配置中输入 git path , 可以看到 在 setting.json 中编辑，点击打开 setting.json, 并添加:
"git.path": "your_git_path"
你可以在终端中输入 git status 试试。
配置好后，点击          ，你可以看到文件的变化，U 带表没有追踪，M 代表已修改。点击右边绿框里的三个小
点点，会弹出所有命令。
9.3. VS Code 扩展                                                        167
Yang 的笔记, 发布 0.1.0
你可以按照自己的喜好，使用终端或者 Git 扩展。
168                       Chapter 9. Visual Studio Code
                                       Yang 的笔记, 发布 0.1.0
9.3.2 Python 扩展
    • Python
    • Anaconda Extension Pack
9.3.3 写作相关扩展
    • Markdown 扩展
         – Markdown All in one
         – Markdown Preview
         – markdownlint
    • reStructuredText 扩展
         – reStructuredText
    • 其他通用扩展
         – Badges
         – Table Formatter
9.3.4 有趣的扩展
    • vscode-icons: 可以让你的 VS Code 活泼点。
9.3. VS Code 扩展                                      169
Yang 的笔记, 发布 0.1.0
170                Chapter 9. Visual Studio Code
                                                  CHAPTER  10
                                                     Linux 杂记
10.1 Vim
vi 命令上下左右方向键出现字母 backspace 键不能删除字符的问题，可以用两个方式解决, 可以任意选择一
个:
   • 修改 vim 设置文件 /etc/vim/vimrc.tiny , 修改/添加下面内容:
     set nocompatible
     set backspace=2
     默认是兼容模式，上下左右是 hjkl，删除时 x X
                                                            171
Yang 的笔记, 发布 0.1.0
   • 用 Vim full 替换默认的 Vim tiny:
     sudo apt remove vim-common
     sudo apt install vim -y
10.2 Ubuntu 国内源
需要修改 /etc/apt/sources.list 文件，在文件最上面添加国内源:
sudo cp /etc/apt/sources.list{,.backup}
sudo vi /etc/apt/sources.list
修改好后，更新一下:
sudo apt updates
   • 18.04
        – 阿里:
          deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe␣
           ,→multiverse
          deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted␣
           ,→universe multiverse
          deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe␣
           ,→multiverse
          deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted␣
           ,→universe multiverse
          deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted␣
           ,→universe multiverse
          deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe␣
           ,→multiverse
          deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted␣
           ,→universe multiverse
          deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted␣
           ,→universe multiverse
          deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted␣
           ,→universe multiverse
          deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted␣
           ,→universe multiverse
172                                                                 Chapter 10. Linux 杂记
                                                               Yang 的笔记, 发布 0.1.0
10.3 ssh-server
   1. 安装
      sudo apt update
      sudo apt install openssh-server -y
   2. 服务
                          命令                         用途
                          sudo systemctl stop ssh    停止 ssh 服务
                          sudo systemctl start ssh   开始 ssh 服务
                          sudo systemctl restart ssh 重启 ssh 服务
                          sudo systemctl disable ssh 禁用 ssh 服务
                          sudo systemctl enable ssh  启用 ssh 服务
                          sudo systemctl status ssh  ssh 服务状态
10.4 Samba 服务器
   1. 安装
      sudo apt update
      sudo apt install samba -y
      此命令将安装并启动 Samba 服务器 smbd 和 Samba NetBIOS 服务器 nmbd。如果不需要使用 nmbd，为了
      安全起见，您可以使用 systemctl 停止和禁用
      sudo systemctl stop nmbd.service
      sudo systemctl disable nmbd.service
   2. 配置
      配置文件 /etc/samba/smb.conf 中分为两部分：
        • [global]: 配置 Samba 服务器的行为
        • [shares]: 配置文件共享
      修改配置文件前先停止 sambd 服务:
      sudo systemctl stop smbd.service
      配置文件示例：
10.3. ssh-server                                                             173
Yang 的笔记, 发布 0.1.0
  1   [global]
  2        workgroup = WORKGROUP
  3        server string = %h server (Samba, Ubuntu)
  4        dns proxy = No
  5        log file = /var/log/samba/log.%m
  6        max log size = 1000
  7        syslog = 0
  8        panic action = /usr/share/samba/panic-action %d
  9        server role = standalone server
 10        pam password change = Yes
 11        obey pam restrictions = Yes
 12        unix password sync = Yes
 13        passwd program = /usr/bin/passwd %u
 14        passwd chat = *Enter\snew\s*\spassword:* %n\n *Retype\snew\s*\spassword:* %n\
       ,→n *password\supdated\ssuccessfully* .
 15        pam password change = yes
 16        map to guest = Bad User
 17        usershare allow guests = Yes
 18
 19   [Workspace]
 20        comment = yang workspace
 21        guest ok = Yes
 22        path = /home/yang/workspace
 23        read only = No
 24        valid users = yang
      这里 [shares] 名为 [Workspace]。
      编译完配置文件可以用下面的命令测试:
      testparm /etc/samba/smb.conf
      如果出现:
      Loaded services file OK.
      说明配置文件可以使用。
   3. 添加用户
      将 yang 用户添加到 samba 用户，并设置密码:
      sudo smbpasswd -a yang
   4. 服务
174                                                                  Chapter 10. Linux 杂记
                                                                           Yang 的笔记, 发布 0.1.0
                                     命令                          用途
                                     sudo systemctl stop smbd    停止 smbd 服务
                                     sudo systemctl start smbd   开始 smbd 服务
                                     sudo systemctl restart smbd 重启 smbd 服务
                                     sudo systemctl disable smbd 禁用 smbd 服务
                                     sudo systemctl enable smbd  启用 smbd 服务
                                     sudo systemctl status smbd  smbd 服务状态
10.5 tree
安装:
sudo apt install tree -y
使用:
tree -d                                # 只显示目录
tree -L 1                              # 只显示第一层目录
tree > structure.txt                   # 输出到文件 structure.txt
10.6 tmux
                                                                  46
Tmux 是一个优秀的终端复用软件，类似 GNU Screen，但来自于 OpenBSD，采用 BSD 授权, 是 BSD 实现的
Screen 替代品，相对于 Screen，它更加先进：支持屏幕切分，而且具备丰富的命令行参数，使其可以灵活、动
态的进行各种布局和操作。
使用它最直观的好处就是，通过一个终端登录远程主机并运行 tmux 后，在其中可以开启多个控制台而无需
再“浪费”多余的终端来连接这台远程主机。
Tmux 可用于在一个终端窗口中运行多个终端会话。不仅如此，还可以通过 Tmux 使终端会话运行于后台或
是按需接入、断开会话，这个功能非常实用。
  46 https://github.com/tmux/tmux/wiki
10.5. tree                                                                               175
Yang 的笔记, 发布 0.1.0
安装:
sudo apt install tmux -y
命令:
   • 列出所有 sessions:
     tmux ls
   • 创建 session:
     tmux new -s <session-name>
   • 连接到 session:
     tmux a -t <session-name>
   • 关闭一个 session:
     tmux kill-session -t <session-name>
快捷键:
176                                      Chapter 10. Linux 杂记
                                   Yang 的笔记, 发布 0.1.0
 Ctrl+b    激活控制台；此时以下按键生效
 系统操作      ?              列出所有快捷键；按 q 返回
           d              脱离当前会话, 这样可以暂时返
                          回 Shell 界面;
                          输入 tmux attach 能够重新进入之
                          前的会话
           D              选择要脱离的会话；在同时开启
                          了多个会话时使用
           Ctrl+z         挂起当前会话
           r              强制重绘未脱离的会话
           s              选择并切换会话；在同时开启了
                          多个会话时使用
           :              进入命令行模式；此时可以输入
                          支持的命令，
                          例如 kill-server 可以关闭服务器
           [              进入复制模式；
                          此时的操作与 vi/emacs 相同，按
                          q/Esc 退出
           ~              列出提示信息缓存；
                          其中包含了之前 tmux 返回的各种
                          提示信息
 窗口操作      c              创建新窗口
           &              关闭当前窗口
           数字键            切换至指定窗口
           p              切换至上一窗口
           n              切换至下一窗口
           l              在前后两个窗口间互相切换
           w              通过窗口列表切换窗口
           ,              重命名当前窗口；这样便于识别
           .              修改当前窗口编号；相当于窗口
                          重新排序
           f              在所有窗口中查找指定文本
 面板操作      ”              将当前面板平分为上下两块
           %              将当前面板平分为左右两块
           x              关闭当前面板
           !              将当前面板置于新窗口；
                          即新建一个窗口，其中仅包含当
                          前面板
                                             下页继续
10.6. tmux                                       177
Yang 的笔记, 发布 0.1.0
                                      表 10.1 – 续上页
                              Ctrl+ 方向键                      以 1 个单元格为单位移动边缘以
                                                             调整当前面板大小
                              Alt+ 方向键                       以 5 个单元格为单位移动边缘以
                                                             调整当前面板大小
                              Space                          在预置的面板布局中循环切换；
                                                             依 次 包 括 even-horizontal、even-
                                                             vertical、
                                                             main-horizontal、main-vertical、tiled
                              q                              显示面板编号
                              o                              在当前窗口中选择下一面板
                              方向键                            移动光标以选择面板
                              {                              向前置换当前面板
                              }                              向后置换当前面板
                              Alt+o                          逆时针旋转当前窗口的面板
                              Ctrl+o                         顺时针旋转当前窗口的面板
10.7 ls
使用 ls -l 或者 ll 命令，可以使用长格式显示文件内容，察看更详细的文件资料:
yang@SkyLab:~/workspace/tmp$ ls -l
total 36
-rw-rw-r-- 1 yang yang  445 4 月     4 20:56 cgit.conf
-rw-rw-r-- 1 yang yang   52 4 月     4 21:01 cgitrc
-rwxrw-r-- 1 yang yang  833 4 月     6 18:40 Dockerfile
-rw-rw-r-- 1 yang yang 4891 4 月     3 22:57 Dockerfile_bak
-rw-rw-r-- 1 yang yang 4737 4 月     3 21:22 Dockerfile_nginx
-rw-rw-r-- 1 yang yang   46 4 月     4 20:58 spawn-fcgi.cgit
drwxr-xr-x 6 yang yang 4096 4 月    11 17:32 spider.git
| 文件属性 | 文件数 | 拥有者 | 所属组 | 文件大小 | 创建时间 | 文件名 |
   • 文件属性
178                                                                    Chapter 10. Linux 杂记
                                                            Yang 的笔记, 发布 0.1.0
       分类               文件属性           属主权限            属组权限     其他用户权限
       字符               0              123             456      789
       说明               d 目录           rwxr-xr-x
                           – 文件        r 可读 (read)
                        l 链接文件         w 可写 (write)
                        b 可供储存的接       x 可执行 (execute)
                        口设备               – 无权限
                             随机存
                             取装置
                        c 串行端口设备
                             一次性
                             读取装
                             置
      可使用 chmod 修改权限。
10.8 chmod
使用方式 1:
chmod [-cfvR] [--help] [--version] mode file...
参数:
   • mode: 权限设定字串，格式如下:
      [ugoa...][[+-=][rwxX]...][,...]
        – [ugoa….] u 表示拥有者，g 表示与拥有者属于同组者，o 表示其他人，a 表示这三者皆是。
        – [+-=] + 表示增加权限、- 表示取消权限、= 表示唯一设定权限。
        – [rwxX] r 表示可读，w 表示可写，x 表示可执行，X 表示只有当该文件是个子目录或者该文件已经
          被设定过为可执行。
   • -c: 若该文件权限确实已经更改，才显示其更改动作
   • -f : 若该文件权限无法被更改也不要显示错误讯息
   • -v : 显示权限变更的详细资料
   • -R: 对目前目录下的所有文件与子目录进行相同的权限变更 (即以递回的方式逐个变更)
   • --help : 显示帮助
10.8. chmod                                                               179
Yang 的笔记, 发布 0.1.0
   • --version: 显示版本
例子:
   • 将 ﬁle1.txt 设为所有人皆可读:
     chmod ugo+r file1.txt
   • 将 ﬁle1.txt 设为所有人皆可读:
     chmod a+r file1.txt
   • 将 ﬁle1.txt 与 ﬁle2.txt 设为拥有者，与其所属同组可写，但其他人不可写入:
     chmod ug+w,o-w file1.txt file2.txt
   • 将 ex1.py 设为拥有者可以执行:
     chmod u+x ex1.py
   • 将目前目录下的所有档案与子目录皆设为任何人可读取:
     chmod -R a+r *
使用方式 2:
chmod abc file
参数:
   • abc: 各为一个数字，分别表示拥有者、同组者、及其他人的权限:
     r=4，w=2，x=1
       – 若要 rwx 属性则 4+2+1=7
       – 若要 rw-属性则 4+2=6
       – 若要 r-x 属性则 4+1=5
例子:
   • 将 ﬁle1.py 设为所有人有所有权限:
     chmod a=rwx file1.py
     chmod 777 file
     两种方式效果一致。
   • 将 ﬁle1.py 设为拥有者及同组者有所有权限，其他人有执行权限:
180                                                 Chapter 10. Linux 杂记
                                Yang 的笔记, 发布 0.1.0
      chmod ug=rwx,o=x file1.py
      chmod 771 file
      两种方式效果一致。
10.8. chmod                                   181
Yang 的笔记, 发布 0.1.0
182                Chapter 10. Linux 杂记
                                                                               CHAPTER 11
                                                   Sphinx & ReadTheDocs & GitHub 集成写作环境
R.I.P. “Konami Code”creator Kazuhisa Hashimoto, Feb 25, 2020.
Sphinx47 是一款工具可以生成文档项目，把 reStructuredText48 文件转换为其他文件格式 (如 HTML, PDF, MAN,
EPUB)，并自动生成交叉引用，索引等待。
Read the Docs49 为开源社区提供文档托管的平台，支持 Sphinx50 工具，它可以从你的 Subversion51 , Bazaar52 ,
Git53 , 和 Mercurial54 仓库拉取源文件，构建文档，并更新在线文档。
GitHub55 是一个面向开源及私有开发项目的托管平台。
本文主要介绍如何使用 Sphinx 生成文档项目，将项目托管在 GitHub ，并使用 Read the Docs 构建并托管生成
的 HTML ，进而实现文档的持续生成及在线浏览。
  47 http://www.sphinx-doc.org/
  48 http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html
  49 https://readthedocs.org/
  50 http://www.sphinx-doc.org/
  51 http://subversion.tigris.org/
  52 http://bazaar.canonical.com/
  53 http://git-scm.com/
  54 https://www.mercurial-scm.org/
  55 https://github.com/
                                                                                        183
Yang 的笔记, 发布 0.1.0
11.1 注册账号
直接去 Read the docs56 和 GitHub57 上注册就行。
11.2 创建 GitHub 仓库
    • 创建远程仓库
       在 GitHub 上创建一个远程仓库，如果不会，参考这里
    • 创建本地仓库
       把你的远程仓库拉取到本地，你可以使用命令行，或者其他软件例如 Sourcetree，参考这里
11.3 创建 Sphinx 项目
11.3.1 创建 Python 环境
首先使用 Conda 创建环境。
conda create --name doc python
conda activate doc
conda install sphinx sphinx_rtd_theme
参见:
关于如何使用 Conda 的详细介绍，请参见Conda
11.3.2 创建项目
接下来我们使用 Sphinx 来创建项目, 先看例子。
(base) PS C:\Users\yang> conda activate doc
(doc) PS C:\Users\yang> cd E:\Git\notes\
(doc) PS E:\Git\notes> sphinx-quickstart
Welcome to the Sphinx 2.4.0 quickstart utility.
Please enter values for the following settings (just press Enter to
accept a default value, if one is given in brackets).
                                                                               (下页继续)
 56 https://readthedocs.org/
 57 https://github.com/
184                                Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                                                   Yang 的笔记, 发布 0.1.0
                                                                                  (续上页)
Selected root path: .
You have two options for placing the build directory for Sphinx output.
Either, you use a directory "_build" within the root path, or you separate
"source" and "build" directories within the root path.
> Separate source and build directories (y/n) [n]:
The project name will occur in several places in the built documentation.
> Project name: Yang 的笔记
> Author name(s): yang
> Project release []: 0.1.0
If the documents are to be written in a language other than English,
you can select a language here by its language code. Sphinx will then
translate text that it generates into that language.
For a list of supported codes, see
https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-language.
> Project language [en]: zh_CN
Creating file .\conf.py.
Creating file .\index.rst.
Creating file .\Makefile.
Creating file .\make.bat.
Finished: An initial directory structure has been created.
You should now populate your master file .\index.rst and create other documentation
source files. Use the Makefile to build the docs, like so:
make builder
where "builder" is one of the supported builders, e.g. html, latex or linkcheck.
简单解释一下：
   1. conda activate doc 激活 doc 环境
   2. cd E:\Git\notes\ 路由到你的本地仓库目录
   3. sphinx-quickstart 命令创建项目，命令中需要的输入
        • Separate source and build directories：是否分开代码和构建目录，不需要选 n
        • Project name: 项目名称，你选个喜欢的名字
        • Author name(s): 作者，你的名字
        • Project release []: 项目版本，这个你随意，后面会被替换掉的
11.3. 创建 Sphinx 项目                                                                  185
Yang 的笔记, 发布 0.1.0
             • Project language [en]: 项目使用语言，你要写英文文档，就不用动，要写简体中文文档就输入 zh_CN,
                这个很重要，因为会影响输出文档的字体。
项目创建好的目录结构如下:
.
├── _build               (构建目录)
├── _static              (静态文件目录)
├── _templates (放置模板目录)
├── conf.py              (配置文件)
├── index.rst            (文档首页文件)
├── Makefile
├── Make.bat             (Windows 下生成命令)
11.3.3 必要修改
如果你懒得看，这里有一个完整的例子58 。
    1. conf.py 是 Sphinx 的配置文件，下面是一个示例
   1    # -*- coding: utf-8 -*-
   2
   3    # -- Project information -----------------------------------------------------
   4    project = 'Yang 的笔记'
   5    copyright = '2020, yang'
   6    author = 'yang'
   7    version = '0.1.0'
   8    release = version
   9
  10    extensions = [
  11    ]
  12
  13    master_doc = 'index'
  14
  15    templates_path = ['_templates']
  16
  17    language = 'zh_CN'
  18
  19    exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', 'README.rst']
  20
  21    html_theme = 'sphinx_rtd_theme'
  22
  23    html_static_path = ['_static']
  58 https://github.com/d12y12/notes/archive/0.1.0.zip
186                                                    Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                                                     Yang 的笔记, 发布 0.1.0
      这里我们只修改了几个小的地方:
         • 添加 master-doc , 指的是文档的入口，一般是目录页
         • 修改 exclude_patterns , 将 README.rst 添加到去除列表，这样不会编译它
         • 修改 html_theme, 使用 Read the Docs 的模板
   2. 添加.gitignore 文件, 用于 Git 忽略某些文件或文件夹。
  1   Thumbs.db
  2   .DS_Store
  3   _build
  4   .vscode
      这里去除的是头两行是 Windows 或 MacOs 生成的一些系统文件。还需要忽略构建文件夹 (_build)，及
      其他不需要提交的文件，比如我这里使用的 VScode 的配置文件。
   3. 添加 README.rst 或 README.md ，用作项目描述
      随便写点什么都可以。
      如果使用 README.rst, 需要加入到 exclude_patterns ，这个文件不需要编译。
   4. 添加 LICENSE 文件，我选择的是 CC-BY-SA-4.0
11.3.4 生成输出
使用 make <builder> 命令来生成输出，常用的 <builder> 有 html 和 latexpdf。
    • html 输出
      要创建 HTML 输出，只需要运行 make html, 下面是个例子 (这个例子不是刚生成项目的时候)：
      (doc) PS E:\Git\notes> make html
      Running Sphinx v2.4.0
      loading translations [zh_CN]... done
      loading pickled environment... done
      building [mo]: targets for 0 po files that are out of date
      building [html]: targets for 0 source files that are out of date
      updating environment: 0 added, 1 changed, 0 removed
      ...
      build succeeded.
      The HTML pages are in _build\html.
      你可以在浏览器打开 _build\html\index.html 来预览生成效果。
    • pdf 输出
      要创建 PDF 输出，只需要运行 make latexpdf。但在运行这个命令之前，先要装一些必要的库。
11.3. 创建 Sphinx 项目                                                                 187
Yang 的笔记, 发布 0.1.0
            1. 下载 TexLiv59
            2. 安装 TeX Live，安装过程中要下很多库 3G 多，选一个国内的源，下载快点
            3. 将 你的安装目录\texlive\2019\bin 加入到系统路径
            4. TeX Live 自带一个小型 Perl 。如果运行中，发现这 Perl 不够用，可以 下载 activeperl60 , 然后一路
               安装就行了。
11.4 创建 Read The Docs 项目
创建 Read The Docs 项目，同时也是 Read The Docs 和 GitHub 集成的过程。
11.4.1 导入项目
假设你已有 Read The docs 账户，登录之后，点击用户名旁边倒三角下拉菜单中的 我的项目, 点击 Import a
Project
第一次导入的时候，需要 GitHub 授权
  59 http://mirror.ctan.org/systems/texlive/tlnet/install-tl-windows.exe
  60 http://www.activestate.com/activeperl/downloads
188                                                          Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                       Yang 的笔记, 发布 0.1.0
同意授权即可。
你也可以在用户名旁边倒三角下拉菜单中的 设置, 点击 已连接的服务中查看授权状态，或者使用 Connect to
GitHub 来进行授权。
11.4. 创建 Read The Docs 项目                            189
Yang 的笔记, 发布 0.1.0
在 GitHub 的 Personal Setting ‣ Applications ‣ Authorized OAuth Apps 中可以查看你已授权的应用。
授权成功后，你可以看到自己的项目列表
190                                          Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                            Yang 的笔记, 发布 0.1.0
点击你想导入项目旁边的 +
给项目取个名字，然后点击 下一页, 就完成项目导入了。
此时再点击 我的项目, 你就能看到导入的项目了。
11.4. 创建 Read The Docs 项目                 191
Yang 的笔记, 发布 0.1.0
点击 项目，会进入 项目概况页面。
192                Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                                     Yang 的笔记, 发布 0.1.0
关于 项目概况页面，这里有几点要说一下:
   • 版本，展示 Read The Docs 自动构建的版本，默认只有 latest , 也就是最新版本。如果你打了标签 ( tag
      )，就会出现 statble , 表示稳定版。这个主要是开发说明文档使用，对应不同软件版本。我这种纯文
      档项目，打标签的意义并不大，我这里打标签只是为了展示用。
11.4. 创建 Read The Docs 项目                                          193
Yang 的笔记, 发布 0.1.0
   • Build version 按钮，点击可以触发一次立即构建
   • 上次构建，显示上次构建状态，如我的这个上次就失败了，你可以点击上面概况旁边的 构建按钮查看
     构建日志。
   • 短地址，访问在线文档的地址
再说一下 概况旁边的几个按钮:
   • 下载, 进入下载页面，下载输出文档，如 PDF，HTML 打包，EPUB
   • 搜索, 进入搜索页面
   • 构建, 进入构建页面，可以查看构建历史，构建日志，或触发立即构建
   • 版本, 进入版本页面，可以激活或者去激活一个版本，设置版本的隐私级别如公开或私有
   • 管理, 进入管理页面，可管理的东西很多，这里就不列举了，我们现在需要用的就是 集成
到此为止，Read The Docs 项目就创建成功了，你可以试试手动触发编译一下，然后点击 阅读文档看看效果。
11.4.2 Webhook
虽然上面的项目可以使用，但每次都要手动触发，我们希望的是当 GitHub 的远程仓库发生变化的时候就触
发 Read The Docs 进行文档编译，像持续集成代码一样持续集成文档，这就需要用到 Webhook 。
Webhook 是一种 Web 回调 API ，简单说就是可以在 GitHub 上注册一个回调 URL，当 GitHub 发生变化的时
候，会产生事件，驱动调用这个 URL，使用 POST 把变化传回去，注册方收到回调，进行处理。
   • Read The Docs 端
     在 管理页面中点击 集成 ‣ 添加集成
194                           Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                                            Yang 的笔记, 发布 0.1.0
      在下拉列表中选择 GitHub 进向 webhook, Read The Docs 会生成一个回调 URL，如:
      https://readthedocs.org/api/v2/webhook/yang/113113/
      记下这个 URL，Read The Docs 端就算完成了。
   • GitHub 端
      在 项目页面点击 Setting ‣ Webhooks,
11.4. 创建 Read The Docs 项目                                                 195
Yang 的笔记, 发布 0.1.0
     点击 Add webhook, 然后填空：
        – Payload URL , 填 Read The Docs 生成的回调 URL
        – Content type , application/json 或 application/x-www-form-urlencoded 都可以
        – Secrets , 空着就行
        – Which events would you like to trigger this webhook? , 选择 Let me select individual events 来自定义触发事
           件，比如可以选择 Branch or tag creation , Branch or tag deletion 和 Pushes
        – 确保 Active 是勾选的，然后点击 Add webhook
     在添加 Webhook 的过程中需要输入 GitHub 的密码来确认。
到此为止，集成就结束了，你可以随便 Push 点东西到 GitHub，来确认 Read The Docs 是否触发了自动构建。
11.5 进阶配置
11.5.1 Read the Docs 配置文件
使用配置文件，可以让 Read the Docs 的生成环境和你本地的一样。
配置文件的名字必须是下面几个之一：
   • readthedocs.yml
   • readthedocs.yaml
   • .readthedocs.yml
196                                           Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                                                      Yang 的笔记, 发布 0.1.0
      • .readthedocs.yaml
   下面是个例子。
 1 # .readthedocs.yml
 2 # Read the Docs configuration file
 3 # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
 4
 5 # Required
 6 version: 2
 7
 8 # Build documentation in the docs/ directory with Sphinx
 9 sphinx:
10   configuration: conf.py
11
12 # Optionally build your docs in additional formats such as PDF and ePub
13 formats: all
14
15 conda:
16   environment: rtd_env.yml
   这里 Conda 的环境文件要用 conda env export --from-history 导出。
   11.5. 进阶配置                                                                       197
Yang 的笔记, 发布 0.1.0
198                Chapter 11. Sphinx & ReadTheDocs & GitHub 集成写作环境
                                                         CHAPTER 12
                                                    Apple ID 更换美国区
最后测试时间 3/16/2020
必要条件：一台可以自如上网的电脑，查看一下你的公网 IP 地址61 , 如果地址是在美国的话，那么按照下面
步骤，应该可以换区。
   1. 打开 Apple ID 登录页面62 ，登录你的 Apple ID，需要双重认证
   2. 点击 付款和送货右侧的 编辑
   3. 在 国家和地区中选择 美国
   4. 在 付款方式中选择 无
   5. 在 帐单寄送地址中填写一个美国地址，比如在必应地图中搜索 los angeles us，随便找个地方的
       地址填进去，实在不知道填哪的搜 STAPLES Center 的地址
   6. 在 帐单寄送地址中填写一个美国电话，可以在 SMS Receive Free63 这个网站中，找一个，它提供的电话
       号码格式为 +13109286504 (1427), 你只需要在电话栏中填 3109286504 。
   7. 勾选 送货地址右边的 拷贝帐单寄送地址
   8. 点击 付款和送货右侧的 存储
   9. 如果电话号码无效就换一个，有可能会报错说存储遇到错误之类的，你可以再刷新一下页面，看一下
       地区是否还是 美国, 如果还是就成功了
 10. 下载完你需要的 App 后，切换回中国即可。
 61 https://whatismyipaddress.com/
 62 https://appleid.apple.com/#!&page=signin
 63 https://smsreceivefree.com/
                                                                  199
Yang 的笔记, 发布 0.1.0
200                Chapter 12. Apple ID 更换美国区
                                                     CHAPTER 13
                                                         杂七杂八
13.1 Ubuntu 网络消失
Ubuntu 网络莫名消失，右上角的小标不见了。
检查状态:
cat /var/lib/NetworkManager/NetworkManager.state
[main]
NetworkingEnabled=false
WirelessEnabled=true
WWANEnabled=true
不知为何 NetworkingEnabled 变成 false 了。
修改:
sudo service network-manager stop
# 将 NetworkingEnabled 改为 true
sudo vi /var/lib/NetworkManager/NetworkManager.state
cat /var/lib/NetworkManager/NetworkManager.state
[main]
NetworkingEnabled=true
WirelessEnabled=true
WWANEnabled=true
sudo service network-manager start
                                                              201
Yang 的笔记, 发布 0.1.0
小标又回来了。     。。
13.2 Vim 变成 Visual 模式
解决方法：
  1. 可以创建 ~/.vimrc (或 ~/.vim/vimrc)，空文件就行。
  2. 在 /etc/vim/vimrc 文件中去掉注释 let g:skip_defaults_vim = 1
202                                                       Chapter 13. 杂七杂八
