update
parent
eff3b92990
commit
6ab987a746
|
@ -10,7 +10,7 @@ root = true
|
|||
[*]
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
max_line_length = 120
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
|
@ -19,7 +19,7 @@ insert_final_newline = true
|
|||
[*.{bat, cmd}]
|
||||
end_of_line = crlf
|
||||
|
||||
[*.{java, groovy, kt, sh}]
|
||||
[*.{java, gradle, groovy, kt, sh}]
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
|
|
|
@ -29,6 +29,7 @@ hs_err_pid*
|
|||
|
||||
# maven plugin temp files
|
||||
.flattened-pom.xml
|
||||
package-lock.json
|
||||
|
||||
|
||||
# ------------------------------- javascript -------------------------------
|
||||
|
@ -47,6 +48,7 @@ npm-debug.log*
|
|||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
bundle*.js
|
||||
book.pdf
|
||||
|
||||
|
||||
# ------------------------------- intellij -------------------------------
|
||||
|
|
10
README.md
10
README.md
|
@ -8,7 +8,8 @@
|
|||
<img src="https://badgen.net/github/license/dunwu/linux-tutorial" alt="license">
|
||||
</p>
|
||||
|
||||
<h1 align="center">Linux Tutorial</h1>
|
||||
<h1 align="center">linux-tutorial</h1>
|
||||
|
||||
> 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial)
|
||||
>
|
||||
> 📖 [电子书](https://dunwu.github.io/linux-tutorial/) | [电子书(国内)](http://turnon.gitee.io/linux-tutorial/)
|
||||
|
@ -68,15 +69,16 @@
|
|||
- [Nacos 运维](docs/linux/soft/nacos-install.md)
|
||||
- 服务器
|
||||
- [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial)
|
||||
- [Tomcat 运维](linux/soft/tomcat-install.md)
|
||||
- [Tomcat 运维](docs/linux/soft/tomcat-install.md)
|
||||
- [数据库 📚](https://github.com/dunwu/db-tutorial)
|
||||
- [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md)
|
||||
- [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md)
|
||||
|
||||
### 扩展
|
||||
|
||||
- [Docker 教程](docs/docker)
|
||||
- [Docker 应用指南](docs/docker/docker.md)
|
||||
- [Docker 教程](docs/docker/README.md)
|
||||
- [Docker 快速入门](docs/docker/docker-quickstart.md)
|
||||
- [Dockerfile 最佳实践](docs/docker/docker-dockerfile.md)
|
||||
- [Docker Cheat Sheet](docs/docker/docker-cheat-sheet.md)
|
||||
- [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md)
|
||||
- [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md)
|
||||
|
|
|
@ -40,7 +40,7 @@ if [[ $# -lt 1 ]] || [[ $# -lt 2 ]]; then
|
|||
printf "${RESET}\n"
|
||||
fi
|
||||
|
||||
version=3.6.2
|
||||
version=3.5.4
|
||||
if [[ -n $1 ]]; then
|
||||
version=$1
|
||||
fi
|
||||
|
@ -58,7 +58,7 @@ printf "${RESET}\n"
|
|||
|
||||
# download and decompression
|
||||
mkdir -p ${path}
|
||||
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -O ${path}/apache-maven-${version}-bin.tar.gz http://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/${version}/binaries/apache-maven-${version}-bin.tar.gz
|
||||
wget --no-check-certificate --no-cookies --header "Cookie: oraclelicense=accept-securebackup-cookie" -O ${path}/apache-maven-${version}-bin.tar.gz http://apache.01link.hk/maven/maven-3/${version}/binaries/apache-maven-${version}-bin.tar.gz
|
||||
tar -zxvf ${path}/apache-maven-${version}-bin.tar.gz -C ${path}
|
||||
|
||||
# setting env
|
||||
|
|
|
@ -6,3 +6,7 @@
|
|||
>
|
||||
> - 你想要执行某个操作,却不知 Shell 命令如何写
|
||||
> - 你想要快速掌握 Shell 基本用法
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [pure-bash-bible](https://github.com/dylanaraps/pure-bash-bible#change-a-string-to-lowercase)
|
||||
|
|
|
@ -1,14 +1,4 @@
|
|||
<p align="center">
|
||||
<a href="https://spring.io/projects/spring-boot" target="_blank" rel="noopener noreferrer">
|
||||
<img src="http://dunwu.test.upcdn.net/common/logo/linux.svg" alt="logo" width="100px">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://badgen.net/github/license/dunwu/linux-tutorial" alt="license">
|
||||
</p>
|
||||
|
||||
<h1 align="center">Linux Tutorial</h1>
|
||||
# linux-tutorial
|
||||
|
||||
> 🔁 项目同步维护在 [github](https://github.com/dunwu/linux-tutorial) | [gitee](https://gitee.com/turnon/linux-tutorial)
|
||||
>
|
||||
|
@ -38,11 +28,12 @@
|
|||
|
||||
> Linux 系统的常见运维工作。
|
||||
|
||||
- [linux 典型运维应用](linux/ops/linux典型运维应用.md)
|
||||
- [samba 使用详解](linux/ops/samba.md)
|
||||
- [Systemd 教程](linux/ops/systemd.md)
|
||||
- [Vim 应用指南](linux/ops/vim.md)
|
||||
- [Zsh 应用指南](linux/ops/zsh.md)
|
||||
- [linux 典型运维应用](linux/ops/linux典型运维应用.md) - 关键词:域名解析、防火墙、网卡、NTP、crontab
|
||||
- [Samba 应用](linux/ops/samba.md)
|
||||
- [Systemd 应用](linux/ops/systemd.md)
|
||||
- [Vim 应用](linux/ops/vim.md)
|
||||
- [Iptables 应用](linux/ops/iptables.md)
|
||||
- [oh-my-zsh 应用](linux/ops/zsh.md)
|
||||
|
||||
### 软件运维
|
||||
|
||||
|
@ -75,8 +66,9 @@
|
|||
|
||||
### 扩展
|
||||
|
||||
- [Docker 教程](docker)
|
||||
- [Docker 应用指南](docker/docker.md)
|
||||
- [Docker 教程](docker/README.md)
|
||||
- [Docker 快速入门](docker/docker-quickstart.md)
|
||||
- [Dockerfile 最佳实践](docker/docker-dockerfile.md)
|
||||
- [Docker Cheat Sheet](docker/docker-cheat-sheet.md)
|
||||
- [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md)
|
||||
- [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md)
|
||||
|
@ -100,7 +92,7 @@
|
|||
|
||||
- **Linux 命令**
|
||||
- [命令行的艺术](https://github.com/jlevy/the-art-of-command-line/blob/master/README-zh.md)
|
||||
- [Linux命令大全](https://man.linuxde.net/)
|
||||
- [Linux 命令大全](https://man.linuxde.net/)
|
||||
- [linux-command](https://github.com/jaywcjlove/linux-command)
|
||||
- **社区网站**
|
||||
- [Linux 中国](https://linux.cn/) - 各种资讯、文章、技术
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
{
|
||||
"gitbook": "3.2.2",
|
||||
"title": "linux-tutorial",
|
||||
"language": "zh-hans",
|
||||
"root": "./",
|
||||
"structure": {
|
||||
"summary": "sidebar.md"
|
||||
},
|
||||
"links": {
|
||||
"sidebar": {
|
||||
"linux-tutorial": "https://github.com/dunwu/linux-tutorial"
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"-lunr",
|
||||
"-search",
|
||||
"advanced-emoji@^0.2.2",
|
||||
"anchor-navigation-ex@1.0.10",
|
||||
"anchors@^0.7.1",
|
||||
"edit-link@^2.0.2",
|
||||
"expandable-chapters-small@^0.1.7",
|
||||
"github@^2.0.0",
|
||||
"search-plus@^0.0.11",
|
||||
"simple-page-toc@^0.1.1",
|
||||
"splitter@^0.0.8",
|
||||
"tbfed-pagefooter@^0.0.1"
|
||||
],
|
||||
"pluginsConfig": {
|
||||
"anchor-navigation-ex": {
|
||||
"showLevel": false,
|
||||
"associatedWithSummary": true,
|
||||
"multipleH1": true,
|
||||
"mode": "float",
|
||||
"isRewritePageTitle": false,
|
||||
"float": {
|
||||
"showLevelIcon": false,
|
||||
"level1Icon": "fa fa-hand-o-right",
|
||||
"level2Icon": "fa fa-hand-o-right",
|
||||
"level3Icon": "fa fa-hand-o-right"
|
||||
},
|
||||
"pageTop": {
|
||||
"showLevelIcon": false,
|
||||
"level1Icon": "fa fa-hand-o-right",
|
||||
"level2Icon": "fa fa-hand-o-right",
|
||||
"level3Icon": "fa fa-hand-o-right"
|
||||
}
|
||||
},
|
||||
"edit-link": {
|
||||
"base": "https://github.com/dunwu/linux-tutorial/blob/master/docs",
|
||||
"label": "编辑此页面"
|
||||
},
|
||||
"github": {
|
||||
"url": "https://github.com/dunwu"
|
||||
},
|
||||
"simple-page-toc": {
|
||||
"maxDepth": 4,
|
||||
"skipFirstH1": true
|
||||
},
|
||||
"sharing": {
|
||||
"weibo": true,
|
||||
"all": [
|
||||
"weibo"
|
||||
]
|
||||
},
|
||||
"tbfed-pagefooter": {
|
||||
"copyright": "Copyright © Zhang Peng 2017",
|
||||
"modify_label": "该文件上次修订时间:",
|
||||
"modify_format": "YYYY-MM-DD HH:mm:ss"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1,28 @@
|
|||
# Docker 教程
|
||||
|
||||
- [Docker 快速入门](docker-quickstart.md)
|
||||
- [Dockerfile 最佳实践](docker-dockerfile.md)
|
||||
- [Docker Cheat Sheet](docker-cheat-sheet.md)
|
||||
|
||||
## 资源
|
||||
|
||||
- **官方**
|
||||
- [Docker 官网](http://www.docker.com)
|
||||
- [Docker Github](https://github.com/moby/moby)
|
||||
- [Docker 官方文档](https://docs.docker.com/)
|
||||
- [Docker Hub](https://hub.docker.com/)
|
||||
- [Docker 开源](https://www.docker.com/community/open-source)
|
||||
- **资源整理**
|
||||
- [Awesome Docker](https://github.com/veggiemonk/awesome-docker)
|
||||
- **教程**
|
||||
- [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) - 非常详尽的 Docker 中文教程
|
||||
- [Docker 中文网站](https://www.docker-cn.com/)
|
||||
- [Docker 安装手册](https://docs.docker-cn.com/engine/installation/)
|
||||
- **镜像**
|
||||
- [时速云镜像仓库](https://hub.tenxcloud.com/)
|
||||
- [网易云镜像服务](https://c.163.com/hub#/m/library/)
|
||||
- [DaoCloud 镜像市场](https://hub.daocloud.io/)
|
||||
- [阿里云镜像库](https://cr.console.aliyun.com/)
|
||||
- **文章**
|
||||
- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html)
|
||||
- [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn)
|
||||
|
|
|
@ -1,97 +0,0 @@
|
|||
<!-- TOC -->
|
||||
|
||||
- [Docker 的设计](#docker-%E7%9A%84%E8%AE%BE%E8%AE%A1)
|
||||
- [Docker 架构](#docker-%E6%9E%B6%E6%9E%84)
|
||||
- [Docker 守护进程(docker daemon)](#docker-%E5%AE%88%E6%8A%A4%E8%BF%9B%E7%A8%8B%EF%BC%88docker-daemon%EF%BC%89)
|
||||
- [Docker 客户端](#docker-%E5%AE%A2%E6%88%B7%E7%AB%AF)
|
||||
- [Docker 注册中心](#docker-%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83)
|
||||
- [Docker 对象](#docker-%E5%AF%B9%E8%B1%A1)
|
||||
- [镜像](#%E9%95%9C%E5%83%8F)
|
||||
- [容器](#%E5%AE%B9%E5%99%A8)
|
||||
- [服务](#%E6%9C%8D%E5%8A%A1)
|
||||
- [底层技术](#%E5%BA%95%E5%B1%82%E6%8A%80%E6%9C%AF)
|
||||
- [命名空间](#%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4)
|
||||
- [控制组](#%E6%8E%A7%E5%88%B6%E7%BB%84)
|
||||
- [联合文件系统](#%E8%81%94%E5%90%88%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F)
|
||||
- [容器格式](#%E5%AE%B9%E5%99%A8%E6%A0%BC%E5%BC%8F)
|
||||
- [资料](#%E8%B5%84%E6%96%99)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
# Docker 的设计
|
||||
|
||||
## Docker 架构
|
||||
|
||||
Docker 使用 C/S 体系结构。Docker 守护进程,负责构建、运行和分发 Docker 容器;Docker 客户端与 Docker 守护进程通信。Docker 客户端和守护进程可以在同一个系统上运行,也可以将 Docker 客户端连接到远程 Docker 守护进程。Docker 客户端和守护进程使用 REST API,并通过 UNIX 套接字或网络接口进行通信。
|
||||
|
||||
<br><div align="center"><img src="https://docs.docker.com/engine/images/architecture.svg"/></div><br>
|
||||
|
||||
### Docker 守护进程(docker daemon)
|
||||
|
||||
Docker 守护进程(`dockerd`)监听 Docker API 请求并管理 Docker 对象(如镜像,容器,网络和卷)。守护进程还可以与其他守护进程通信来管理 Docker 服务。
|
||||
|
||||
### Docker 客户端
|
||||
|
||||
Docker 客户端(`docker`)是许多 Docker 用户与 Docker 进行交互的主要方式。当你使用诸如 `docker run` 之类的命令时,客户端将这些命令发送到 `dockerd`,`dockerd` 执行这些命令。 `docker` 命令使用 Docker API。 Docker 客户端可以与多个守护进程进行通信。
|
||||
|
||||
### Docker 注册中心
|
||||
|
||||
Docker 注册中心存储 Docker 镜像。Docker Hub 和 Docker Cloud 是任何人都可以使用的公共注册中心,并且 Docker 默认配置为在 Docker Hub 上查找镜像。你甚至可以运行你自己的私人注册中心。如果您使用 Docker Datacenter(DDC),它包括 Docker Trusted Registry(DTR)。
|
||||
|
||||
当您使用 `docker pull` 或 `docker run` 命令时,所需的镜像将从配置的注册中心中提取。当您使用 `docker push` 命令时,您的镜像将被推送到您配置的注册中心。
|
||||
|
||||
[Docker 商店](http://store.docker.com/) 允许您购买和销售 Docker 镜像或免费发布。例如,您可以购买包含来自软件供应商的应用程序或服务的 Docker 镜像,并使用该镜像将应用程序部署到您的测试,临时和生产环境中。您可以通过拉取新版本的镜像并重新部署容器来升级应用程序。
|
||||
|
||||
### Docker 对象
|
||||
|
||||
#### 镜像
|
||||
|
||||
镜像是一个只读模板,带有创建 Docker 容器的说明。通常,镜像基于另一个镜像,并具有一些额外的自定义功能。例如,您可以构建基于 ubuntu 镜像的镜像,但会安装 Apache Web 服务器和应用程序,以及使应用程序运行所需的配置细节。
|
||||
|
||||
您可能会创建自己的镜像,或者您可能只能使用其他人创建并在注册中心中发布的镜像。为了构建您自己的镜像,您可以使用简单的语法创建 `Dockerfile`,以定义创建镜像并运行所需的步骤。 `Dockerfile` 中的每条指令都会在镜像中创建一个图层。当您更改 `Dockerfile` 并重建镜像时,只重建那些已更改的图层。与其他虚拟化技术相比,这是使镜像轻量,小巧,快速的一部分。
|
||||
|
||||
#### 容器
|
||||
|
||||
容器是镜像的可运行实例。您可以使用 Docker API 或 CLI 创建、启动、停止、移动或删除容器。您可以将容器连接到一个或多个网络,将存储器连接到它,甚至可以根据其当前状态创建新镜像。
|
||||
|
||||
默认情况下,容器与其他容器及其主机相对隔离。您可以控制容器的网络、存储或其他底层子系统与其他容器或主机的隔离程度。
|
||||
|
||||
容器由其镜像以及您在创建或启动时提供给它的任何配置选项来定义。当一个容器被移除时,其未被存储在永久存储器中的状态将消失。
|
||||
|
||||
#### 服务
|
||||
|
||||
通过服务,您可以跨多个 Docker 守护进程扩展容器,这些守护进程可以作为一个群组与多个管理人员、工作人员一起工作。集群中的每个成员都是 Docker 守护进程,守护进程都使用 Docker API 进行通信。服务允许您定义所需的状态,例如在任何给定时间必须可用的服务的副本数量。默认情况下,该服务在所有工作节点之间进行负载平衡。对于消费者来说,Docker 服务似乎是一个单一的应用程序。Docker 引擎在 Docker 1.12 及更高版本中支持集群模式。
|
||||
|
||||
## 底层技术
|
||||
|
||||
Docker 使用 Go 编写,利用 Linux 内核的几个特性来提供其功能。
|
||||
|
||||
### 命名空间
|
||||
|
||||
Docker 使用名为 `namespaces` 的技术来提供独立工作空间(即容器)。当你运行一个容器时,Docker 会为该容器创建一组命名空间。
|
||||
|
||||
这些命名空间提供了一个隔离层。容器的每个方面都在单独的命名空间中运行,并且其访问权限限于该命名空间。
|
||||
|
||||
Docker 引擎在 Linux 上使用如下的命名空间:
|
||||
|
||||
* `pid` 命名空间:进程隔离(PID:进程ID)。
|
||||
* `net` 命名空间:管理网络接口(NET:网络)。
|
||||
* `ipc` 命名空间:管理对IPC资源的访问(IPC:InterProcess Communication)。
|
||||
* `mnt` 命名空间:管理文件系统挂载点(MNT:挂载)。
|
||||
* `uts` 命名空间:隔离内核和版本标识符。 (UTS:Unix分时系统)。
|
||||
|
||||
### 控制组
|
||||
|
||||
Linux 上的 Docker Engine 也依赖于另一种称为控制组(`cgroups`)的技术。 cgroup 将应用程序限制为一组特定的资源。控制组允许 Docker 引擎将可用硬件资源共享给容器,并可选地强制实施限制和约束。例如,您可以限制可用于特定容器的内存。
|
||||
|
||||
### 联合文件系统
|
||||
|
||||
联合文件系统(UnionFS)是通过创建图层进行操作的文件系统,这使它们非常轻巧和快速。 Docker 引擎使用 UnionFS 为容器提供构建块。Docker 引擎可以使用多种 UnionFS 变体,包括 AUFS,btrfs,vfs 和 DeviceMapper。
|
||||
|
||||
### 容器格式
|
||||
|
||||
Docker 引擎将命名空间,控制组和 UnionFS 组合成一个名为容器格式的包装器。默认的容器格式是`libcontainer`。将来,Docker 可以通过与诸如 BSD Jails 或 Solaris Zones 等技术集成来支持其他容器格式。
|
||||
|
||||
## 资料
|
||||
|
||||
* https://docs.docker.com/engine/docker-overview/
|
|
@ -1,261 +0,0 @@
|
|||
# Docker 容器
|
||||
|
||||
容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。
|
||||
|
||||
<!-- TOC depthFrom:2 depthTo:3 -->
|
||||
|
||||
- [启动容器](#启动容器)
|
||||
- [新建并启动](#新建并启动)
|
||||
- [启动已终止容器](#启动已终止容器)
|
||||
- [后台运行](#后台运行)
|
||||
- [终止容器](#终止容器)
|
||||
- [进入容器](#进入容器)
|
||||
- [`attach` 命令](#attach-命令)
|
||||
- [`exec` 命令](#exec-命令)
|
||||
- [导出和导入容器](#导出和导入容器)
|
||||
- [导出容器](#导出容器)
|
||||
- [导入容器快照](#导入容器快照)
|
||||
- [删除容器](#删除容器)
|
||||
- [清理所有处于终止状态的容器](#清理所有处于终止状态的容器)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
## 启动容器
|
||||
|
||||
启动容器有两种方式,一种是基于镜像新建一个容器并启动,另外一个是将在终止状态(`stopped`)的容器重新启动。
|
||||
|
||||
因为 Docker 的容器实在太轻量级了,很多时候用户都是随时删除和新创建容器。
|
||||
|
||||
### 新建并启动
|
||||
|
||||
所需要的命令主要为 `docker run`。
|
||||
|
||||
例如,下面的命令输出一个 “Hello World”,之后终止容器。
|
||||
|
||||
```bash
|
||||
$ docker run ubuntu:14.04 /bin/echo 'Hello world'
|
||||
Hello world
|
||||
```
|
||||
|
||||
这跟在本地直接执行 `/bin/echo 'hello world'` 几乎感觉不出任何区别。
|
||||
|
||||
下面的命令则启动一个 bash 终端,允许用户进行交互。
|
||||
|
||||
```bash
|
||||
$ docker run -t -i ubuntu:14.04 /bin/bash
|
||||
root@af8bae53bdd3:/#
|
||||
```
|
||||
|
||||
其中,`-t` 选项让 Docker 分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, `-i` 则让容器的标准输入保持打开。
|
||||
|
||||
在交互模式下,用户可以通过所创建的终端来输入命令,例如
|
||||
|
||||
```bash
|
||||
root@af8bae53bdd3:/# pwd
|
||||
/
|
||||
root@af8bae53bdd3:/# ls
|
||||
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
|
||||
```
|
||||
|
||||
当利用 `docker run` 来创建容器时,Docker 在后台运行的标准操作包括:
|
||||
|
||||
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
|
||||
- 利用镜像创建并启动一个容器
|
||||
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
|
||||
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
|
||||
- 从地址池配置一个 ip 地址给容器
|
||||
- 执行用户指定的应用程序
|
||||
- 执行完毕后容器被终止
|
||||
|
||||
### 启动已终止容器
|
||||
|
||||
可以利用 `docker container start` 命令,直接将一个已经终止的容器启动运行。
|
||||
|
||||
容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 `ps` 或 `top` 来查看进程信息。
|
||||
|
||||
```bash
|
||||
root@ba267838cc1b:/# ps
|
||||
PID TTY TIME CMD
|
||||
1 ? 00:00:00 bash
|
||||
11 ? 00:00:00 ps
|
||||
```
|
||||
|
||||
可见,容器中仅运行了指定的 bash 应用。这种特点使得 Docker 对资源的利用率极高,是货真价实的轻量级虚拟化。
|
||||
|
||||
## 后台运行
|
||||
|
||||
更多的时候,需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 `-d` 参数来实现。
|
||||
|
||||
下面举两个例子来说明一下。
|
||||
|
||||
如果不使用 `-d` 参数运行容器。
|
||||
|
||||
```bash
|
||||
$ docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
|
||||
hello world
|
||||
hello world
|
||||
hello world
|
||||
hello world
|
||||
```
|
||||
|
||||
容器会把输出的结果 (STDOUT) 打印到宿主机上面
|
||||
|
||||
如果使用了 `-d` 参数运行容器。
|
||||
|
||||
```bash
|
||||
$ docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
|
||||
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
|
||||
```
|
||||
|
||||
此时容器会在后台运行并不会把输出的结果 (STDOUT) 打印到宿主机上面(输出结果可以用 `docker logs` 查看)。
|
||||
|
||||
**注:** 容器是否会长久运行,是和 `docker run` 指定的命令有关,和 `-d` 参数无关。
|
||||
|
||||
使用 `-d` 参数启动后会返回一个唯一的 id,也可以通过 `docker container ls` 命令来查看容器信息。
|
||||
|
||||
```
|
||||
$ docker container ls
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
77b2dc01fe0f ubuntu:18.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
|
||||
```
|
||||
|
||||
要获取容器的输出信息,可以通过 `docker container logs` 命令。
|
||||
|
||||
```bash
|
||||
$ docker container logs [container ID or NAMES]
|
||||
hello world
|
||||
hello world
|
||||
hello world
|
||||
. . .
|
||||
```
|
||||
|
||||
## 终止容器
|
||||
|
||||
可以使用 `docker container stop` 来终止一个运行中的容器。
|
||||
|
||||
此外,当 Docker 容器中指定的应用终结时,容器也自动终止。
|
||||
|
||||
例如对于上一章节中只启动了一个终端的容器,用户通过 `exit` 命令或 `Ctrl+d` 来退出终端时,所创建的容器立刻终止。
|
||||
|
||||
终止状态的容器可以用 `docker container ls -a` 命令看到。例如
|
||||
|
||||
```bash
|
||||
docker container ls -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ba267838cc1b ubuntu:14.04 "/bin/bash" 30 minutes ago Exited (0) About a minute ago trusting_newton
|
||||
98e5efa7d997 training/webapp:latest "python app.py" About an hour ago Exited (0) 34 minutes ago backstabbing_pike
|
||||
```
|
||||
|
||||
处于终止状态的容器,可以通过 `docker container start` 命令来重新启动。
|
||||
|
||||
此外,`docker container restart` 命令会将一个运行态的容器终止,然后再重新启动它。
|
||||
|
||||
## 进入容器
|
||||
|
||||
在使用 `-d` 参数时,容器启动后会进入后台。
|
||||
|
||||
某些时候需要进入容器进行操作,包括使用 `docker attach` 命令或 `docker exec` 命令,推荐大家使用 `docker exec` 命令,原因会在下面说明。
|
||||
|
||||
### `attach` 命令
|
||||
|
||||
`docker attach` 是 Docker 自带的命令。下面示例如何使用该命令。
|
||||
|
||||
```bash
|
||||
$ docker run -dit ubuntu
|
||||
243c32535da7d142fb0e6df616a3c3ada0b8ab417937c853a9e1c251f499f550
|
||||
|
||||
$ docker container ls
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
243c32535da7 ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds nostalgic_hypatia
|
||||
|
||||
$ docker attach 243c
|
||||
root@243c32535da7:/#
|
||||
```
|
||||
|
||||
_注意:_ 如果从这个 stdin 中 exit,会导致容器的停止。
|
||||
|
||||
### `exec` 命令
|
||||
|
||||
#### -i -t 参数
|
||||
|
||||
`docker exec` 后边可以跟多个参数,这里主要说明 `-i` `-t` 参数。
|
||||
|
||||
只用 `-i` 参数时,由于没有分配伪终端,界面没有我们熟悉的 Linux 命令提示符,但命令执行结果仍然可以返回。
|
||||
|
||||
当 `-i` `-t` 参数一起使用时,则可以看到我们熟悉的 Linux 命令提示符。
|
||||
|
||||
```bash
|
||||
$ docker run -dit ubuntu
|
||||
69d137adef7a8a689cbcb059e94da5489d3cddd240ff675c640c8d96e84fe1f6
|
||||
|
||||
$ docker container ls
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
69d137adef7a ubuntu:latest "/bin/bash" 18 seconds ago Up 17 seconds zealous_swirles
|
||||
|
||||
$ docker exec -i 69d1 bash
|
||||
ls
|
||||
bin
|
||||
boot
|
||||
dev
|
||||
...
|
||||
|
||||
$ docker exec -it 69d1 bash
|
||||
root@69d137adef7a:/#
|
||||
```
|
||||
|
||||
如果从这个 stdin 中 exit,不会导致容器的停止。这就是为什么推荐大家使用 `docker exec` 的原因。
|
||||
|
||||
更多参数说明请使用 `docker exec --help` 查看。
|
||||
|
||||
## 导出和导入容器
|
||||
|
||||
### 导出容器
|
||||
|
||||
如果要导出本地某个容器,可以使用 `docker export` 命令。
|
||||
|
||||
```bash
|
||||
$ docker container ls -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
|
||||
$ docker export 7691a814370e > ubuntu.tar
|
||||
```
|
||||
|
||||
这样将导出容器快照到本地文件。
|
||||
|
||||
### 导入容器快照
|
||||
|
||||
可以使用 `docker import` 从容器快照文件中再导入为镜像,例如
|
||||
|
||||
```bash
|
||||
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
|
||||
$ docker image ls
|
||||
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
|
||||
test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
|
||||
```
|
||||
|
||||
此外,也可以通过指定 URL 或者某个目录来导入,例如
|
||||
|
||||
```bash
|
||||
$ docker import http://example.com/exampleimage.tgz example/imagerepo
|
||||
```
|
||||
|
||||
_注:用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。_
|
||||
|
||||
## 删除容器
|
||||
|
||||
可以使用 `docker container rm` 来删除一个处于终止状态的容器。例如
|
||||
|
||||
```bash
|
||||
$ docker container rm trusting_newton
|
||||
trusting_newton
|
||||
```
|
||||
|
||||
如果要删除一个运行中的容器,可以添加 `-f` 参数。Docker 会发送 `SIGKILL` 信号给容器。
|
||||
|
||||
## 清理所有处于终止状态的容器
|
||||
|
||||
用 `docker container ls -a` 命令可以查看所有已经创建的包括终止状态的容器,如果数量太多要一个个删除可能会很麻烦,用下面的命令可以清理掉所有处于终止状态的容器。
|
||||
|
||||
```bash
|
||||
$ docker container prune
|
||||
```
|
|
@ -1,71 +0,0 @@
|
|||
# Docker 之 Hello World
|
||||
|
||||
## 前提
|
||||
|
||||
确保你的环境上已经成功安装 Docker。
|
||||
|
||||
## Hello World 实例
|
||||
|
||||
1. 使用 `docker version` 命令确保你的环境已成功安装 Docker。
|
||||
|
||||
```
|
||||
# docker version
|
||||
Client:
|
||||
Version: 1.13.1
|
||||
API version: 1.26
|
||||
Package version: <unknown>
|
||||
Go version: go1.8.3
|
||||
Git commit: 774336d/1.13.1
|
||||
Built: Wed Mar 7 17:06:16 2018
|
||||
OS/Arch: linux/amd64
|
||||
|
||||
Server:
|
||||
Version: 1.13.1
|
||||
API version: 1.26 (minimum version 1.12)
|
||||
Package version: <unknown>
|
||||
Go version: go1.8.3
|
||||
Git commit: 774336d/1.13.1
|
||||
Built: Wed Mar 7 17:06:16 2018
|
||||
OS/Arch: linux/amd64
|
||||
Experimental: false
|
||||
```
|
||||
|
||||
2. 使用 `docker run` 命令运行 Hello World 镜像。
|
||||
|
||||
```
|
||||
docker run hello-world
|
||||
|
||||
Unable to find image 'hello-world:latest' locally
|
||||
latest: Pulling from library/hello-world
|
||||
ca4f61b1923c: Pull complete
|
||||
Digest: sha256:ca0eeb6fb05351dfc8759c20733c91def84cb8007aa89a5bf606bc8b315b9fc7
|
||||
Status: Downloaded newer image for hello-world:latest
|
||||
|
||||
Hello from Docker!
|
||||
This message shows that your installation appears to be working correctly.
|
||||
...
|
||||
```
|
||||
|
||||
3. 使用 `docker image ls`命令查看镜像
|
||||
|
||||
```
|
||||
docker image ls
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
docker.io/maven latest 76c9ab5df55b 7 days ago 737 MB
|
||||
docker.io/python 2.7-slim 5541369755c4 13 days ago 139 MB
|
||||
docker.io/hello-world latest f2a91732366c 4 months ago 1.85 kB
|
||||
docker.io/java 8-jre e44d62cf8862 14 months ago 311 MB
|
||||
docker.io/training/webapp latest 6fae60ef3446 2 years ago 349 MB
|
||||
```
|
||||
|
||||
4. 使用 `docker container ls --all` 命令查看容器
|
||||
|
||||
如果查看正在运行的容器,不需要添加 `--all` 参数。
|
||||
|
||||
```
|
||||
docker container ls --all
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
a661d957c6fa hello-world "/hello" 2 minutes ago Exited (0) 2 minutes ago mystifying_swartz
|
||||
3098f24a1064 docker.io/hello-world "/hello" 6 minutes ago Exited (0) 6 minutes ago sad_yonath
|
||||
4c98c4f18a39 hello-world "/hello" 8 minutes ago Exited (0) 8 minutes ago admiring_banach
|
||||
```
|
|
@ -1,491 +0,0 @@
|
|||
# Docker 镜像
|
||||
|
||||
<!-- TOC depthFrom:2 depthTo:3 -->
|
||||
|
||||
- [获取镜像](#获取镜像)
|
||||
- [运行](#运行)
|
||||
- [列出镜像](#列出镜像)
|
||||
- [镜像体积](#镜像体积)
|
||||
- [虚悬镜像](#虚悬镜像)
|
||||
- [中间层镜像](#中间层镜像)
|
||||
- [列出部分镜像](#列出部分镜像)
|
||||
- [以特定格式显示](#以特定格式显示)
|
||||
- [删除本地镜像](#删除本地镜像)
|
||||
- [用 ID、镜像名、摘要删除镜像](#用-id镜像名摘要删除镜像)
|
||||
- [Untagged 和 Deleted](#untagged-和-deleted)
|
||||
- [用 docker image ls 命令来配合](#用-docker-image-ls-命令来配合)
|
||||
- [CentOS/RHEL 的用户需要注意的事项](#centosrhel-的用户需要注意的事项)
|
||||
- [使用 Dockerfile 定制镜像](#使用-dockerfile-定制镜像)
|
||||
- [构建镜像](#构建镜像)
|
||||
- [镜像构建上下文(Context)](#镜像构建上下文context)
|
||||
- [其它 `docker build` 的用法](#其它-docker-build-的用法)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
## 获取镜像
|
||||
|
||||
之前提到过,[Docker Hub](https://hub.docker.com/explore/) 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像。
|
||||
|
||||
从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为:
|
||||
|
||||
```bash
|
||||
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
|
||||
```
|
||||
|
||||
具体的选项可以通过 `docker pull --help` 命令看到,这里我们说一下镜像名称的格式。
|
||||
|
||||
- Docker 镜像仓库地址:地址的格式一般是 `<域名/IP>[:端口号]`。默认地址是 Docker Hub。
|
||||
- 仓库名:如之前所说,这里的仓库名是两段式名称,即 `<用户名>/<软件名>`。对于 Docker Hub,如果不给出用户名,则默认为 `library`,也就是官方镜像。
|
||||
|
||||
比如:
|
||||
|
||||
```bash
|
||||
$ docker pull ubuntu:18.04
|
||||
18.04: Pulling from library/ubuntu
|
||||
bf5d46315322: Pull complete
|
||||
9f13e0ac480c: Pull complete
|
||||
e8988b5b3097: Pull complete
|
||||
40af181810e7: Pull complete
|
||||
e6f7c7e5c03e: Pull complete
|
||||
Digest: sha256:147913621d9cdea08853f6ba9116c2e27a3ceffecf3b492983ae97c3d643fbbe
|
||||
Status: Downloaded newer image for ubuntu:18.04
|
||||
```
|
||||
|
||||
上面的命令中没有给出 Docker 镜像仓库地址,因此将会从 Docker Hub 获取镜像。而镜像名称是 `ubuntu:18.04`,因此将会获取官方镜像 `library/ubuntu` 仓库中标签为 `18.04` 的镜像。
|
||||
|
||||
从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 `sha256` 的摘要,以确保下载一致性。
|
||||
|
||||
在使用上面命令的时候,你可能会发现,你所看到的层 ID 以及 `sha256` 的摘要和这里的不一样。这是因为官方镜像是一直在维护的,有任何新的 bug,或者版本更新,都会进行修复再以原来的标签发布,这样可以确保任何使用这个标签的用户可以获得更安全、更稳定的镜像。
|
||||
|
||||
*如果从 Docker Hub 下载镜像非常缓慢,可以参照 镜像加速器 一节配置加速器。*
|
||||
|
||||
### 运行
|
||||
|
||||
有了镜像后,我们就能够以这个镜像为基础启动并运行一个容器。以上面的 `ubuntu:18.04` 为例,如果我们打算启动里面的 `bash` 并且进行交互式操作的话,可以执行下面的命令。
|
||||
|
||||
```bash
|
||||
$ docker run -it --rm \
|
||||
ubuntu:18.04 \
|
||||
bash
|
||||
|
||||
root@e7009c6ce357:/# cat /etc/os-release
|
||||
NAME="Ubuntu"
|
||||
VERSION="18.04.1 LTS (Bionic Beaver)"
|
||||
ID=ubuntu
|
||||
ID_LIKE=debian
|
||||
PRETTY_NAME="Ubuntu 18.04.1 LTS"
|
||||
VERSION_ID="18.04"
|
||||
HOME_URL="https://www.ubuntu.com/"
|
||||
SUPPORT_URL="https://help.ubuntu.com/"
|
||||
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
|
||||
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
|
||||
VERSION_CODENAME=bionic
|
||||
UBUNTU_CODENAME=bionic
|
||||
```
|
||||
|
||||
`docker run` 就是运行容器的命令,具体格式我们会在 [容器](https://yeasy.gitbooks.io/docker_practice/content/container) 一节进行详细讲解,我们这里简要的说明一下上面用到的参数。
|
||||
|
||||
- `-it`:这是两个参数,一个是 `-i`:交互式操作,一个是 `-t` 终端。我们这里打算进入 `bash` 执行一些命令并查看返回结果,因此我们需要交互式终端。
|
||||
- `--rm`:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 `docker rm`。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 `--rm` 可以避免浪费空间。
|
||||
- `ubuntu:18.04`:这是指用 `ubuntu:18.04` 镜像为基础来启动容器。
|
||||
- `bash`:放在镜像名后的是**命令**,这里我们希望有个交互式 Shell,因此用的是 `bash`。
|
||||
|
||||
进入容器后,我们可以在 Shell 下操作,执行任何所需的命令。这里,我们执行了 `cat /etc/os-release`,这是 Linux 常用的查看当前系统版本的命令,从返回的结果可以看到容器内是 `Ubuntu 18.04.1 LTS` 系统。
|
||||
|
||||
最后我们通过 `exit` 退出了这个容器。
|
||||
|
||||
## 列出镜像
|
||||
|
||||
要想列出已经下载下来的镜像,可以使用 `docker image ls` 命令。
|
||||
|
||||
```bash
|
||||
$ docker image ls
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
redis latest 5f515359c7f8 5 days ago 183 MB
|
||||
nginx latest 05a60462f8ba 5 days ago 181 MB
|
||||
mongo 3.2 fe9198c04d62 5 days ago 342 MB
|
||||
<none> <none> 00285df0df87 5 days ago 342 MB
|
||||
ubuntu 18.04 f753707788c5 4 weeks ago 127 MB
|
||||
ubuntu latest f753707788c5 4 weeks ago 127 MB
|
||||
ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB
|
||||
```
|
||||
|
||||
列表包含了 `仓库名`、`标签`、`镜像 ID`、`创建时间` 以及 `所占用的空间`。
|
||||
|
||||
其中仓库名、标签在之前的基础概念章节已经介绍过了。**镜像 ID** 则是镜像的唯一标识,一个镜像可以对应多个**标签**。因此,在上面的例子中,我们可以看到 `ubuntu:18.04` 和 `ubuntu:latest` 拥有相同的 ID,因为它们对应的是同一个镜像。
|
||||
|
||||
### 镜像体积
|
||||
|
||||
如果仔细观察,会注意到,这里标识的所占用空间和在 Docker Hub 上看到的镜像大小不同。比如,`ubuntu:18.04` 镜像大小,在这里是 `127 MB`,但是在 [Docker Hub](https://hub.docker.com/r/library/ubuntu/tags/) 显示的却是 `50 MB`。这是因为 Docker Hub 中显示的体积是压缩后的体积。在镜像下载和上传过程中镜像是保持着压缩状态的,因此 Docker Hub 所显示的大小是网络传输中更关心的流量大小。而 `docker image ls` 显示的是镜像下载到本地后,展开的大小,准确说,是展开后的各层所占空间的总和,因为镜像到本地后,查看空间的时候,更关心的是本地磁盘空间占用的大小。
|
||||
|
||||
另外一个需要注意的问题是,`docker image ls` 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
|
||||
|
||||
你可以通过以下命令来便捷的查看镜像、容器、数据卷所占用的空间。
|
||||
|
||||
```bash
|
||||
$ docker system df
|
||||
|
||||
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
|
||||
Images 24 0 1.992GB 1.992GB (100%)
|
||||
Containers 1 0 62.82MB 62.82MB (100%)
|
||||
Local Volumes 9 0 652.2MB 652.2MB (100%)
|
||||
Build Cache 0B 0B
|
||||
```
|
||||
|
||||
### 虚悬镜像
|
||||
|
||||
上面的镜像列表中,还可以看到一个特殊的镜像,这个镜像既没有仓库名,也没有标签,均为 `<none>`。:
|
||||
|
||||
```bash
|
||||
<none> <none> 00285df0df87 5 days ago 342 MB
|
||||
```
|
||||
|
||||
这个镜像原本是有镜像名和标签的,原来为 `mongo:3.2`,随着官方镜像维护,发布了新版本后,重新 `docker pull mongo:3.2` 时,`mongo:3.2` 这个镜像名被转移到了新下载的镜像身上,而旧的镜像上的这个名称则被取消,从而成为了 `<none>`。除了 `docker pull` 可能导致这种情况,`docker build` 也同样可以导致这种现象。由于新旧镜像同名,旧镜像名称被取消,从而出现仓库名、标签均为 `<none>` 的镜像。这类无标签镜像也被称为 **虚悬镜像(dangling image)** ,可以用下面的命令专门显示这类镜像:
|
||||
|
||||
```bash
|
||||
$ docker image ls -f dangling=true
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
<none> <none> 00285df0df87 5 days ago 342 MB
|
||||
```
|
||||
|
||||
一般来说,虚悬镜像已经失去了存在的价值,是可以随意删除的,可以用下面的命令删除。
|
||||
|
||||
```bash
|
||||
$ docker image prune
|
||||
```
|
||||
|
||||
### 中间层镜像
|
||||
|
||||
为了加速镜像构建、重复利用资源,Docker 会利用 **中间层镜像**。所以在使用一段时间后,可能会看到一些依赖的中间层镜像。默认的 `docker image ls` 列表中只会显示顶层镜像,如果希望显示包括中间层镜像在内的所有镜像的话,需要加 `-a` 参数。
|
||||
|
||||
```bash
|
||||
$ docker image ls -a
|
||||
```
|
||||
|
||||
这样会看到很多无标签的镜像,与之前的虚悬镜像不同,这些无标签的镜像很多都是中间层镜像,是其它镜像所依赖的镜像。这些无标签镜像不应该删除,否则会导致上层镜像因为依赖丢失而出错。实际上,这些镜像也没必要删除,因为之前说过,相同的层只会存一遍,而这些镜像是别的镜像的依赖,因此并不会因为它们被列出来而多存了一份,无论如何你也会需要它们。只要删除那些依赖它们的镜像后,这些依赖的中间层镜像也会被连带删除。
|
||||
|
||||
### 列出部分镜像
|
||||
|
||||
不加任何参数的情况下,`docker image ls` 会列出所有顶级镜像,但是有时候我们只希望列出部分镜像。`docker image ls` 有好几个参数可以帮助做到这个事情。
|
||||
|
||||
根据仓库名列出镜像
|
||||
|
||||
```bash
|
||||
$ docker image ls ubuntu
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ubuntu 18.04 f753707788c5 4 weeks ago 127 MB
|
||||
ubuntu latest f753707788c5 4 weeks ago 127 MB
|
||||
ubuntu 14.04 1e0c3dd64ccd 4 weeks ago 188 MB
|
||||
```
|
||||
|
||||
列出特定的某个镜像,也就是说指定仓库名和标签
|
||||
|
||||
```bash
|
||||
$ docker image ls ubuntu:18.04
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ubuntu 18.04 f753707788c5 4 weeks ago 127 MB
|
||||
```
|
||||
|
||||
除此以外,`docker image ls` 还支持强大的过滤器参数 `--filter`,或者简写 `-f`。之前我们已经看到了使用过滤器来列出虚悬镜像的用法,它还有更多的用法。比如,我们希望看到在 `mongo:3.2` 之后建立的镜像,可以用下面的命令:
|
||||
|
||||
```bash
|
||||
$ docker image ls -f since=mongo:3.2
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
redis latest 5f515359c7f8 5 days ago 183 MB
|
||||
nginx latest 05a60462f8ba 5 days ago 181 MB
|
||||
```
|
||||
|
||||
想查看某个位置之前的镜像也可以,只需要把 `since` 换成 `before` 即可。
|
||||
|
||||
此外,如果镜像构建时,定义了 `LABEL`,还可以通过 `LABEL` 来过滤。
|
||||
|
||||
```bash
|
||||
$ docker image ls -f label=com.example.version=0.1
|
||||
...
|
||||
```
|
||||
|
||||
### 以特定格式显示
|
||||
|
||||
默认情况下,`docker image ls` 会输出一个完整的表格,但是我们并非所有时候都会需要这些内容。比如,刚才删除虚悬镜像的时候,我们需要利用 `docker image ls` 把所有的虚悬镜像的 ID 列出来,然后才可以交给 `docker image rm` 命令作为参数来删除指定的这些镜像,这个时候就用到了 `-q` 参数。
|
||||
|
||||
```bash
|
||||
$ docker image ls -q
|
||||
5f515359c7f8
|
||||
05a60462f8ba
|
||||
fe9198c04d62
|
||||
00285df0df87
|
||||
f753707788c5
|
||||
f753707788c5
|
||||
1e0c3dd64ccd
|
||||
```
|
||||
|
||||
`--filter` 配合 `-q` 产生出指定范围的 ID 列表,然后送给另一个 `docker` 命令作为参数,从而针对这组实体成批的进行某种操作的做法在 Docker 命令行使用过程中非常常见,不仅仅是镜像,将来我们会在各个命令中看到这类搭配以完成很强大的功能。因此每次在文档看到过滤器后,可以多注意一下它们的用法。
|
||||
|
||||
另外一些时候,我们可能只是对表格的结构不满意,希望自己组织列;或者不希望有标题,这样方便其它程序解析结果等,这就用到了 [Go 的模板语法](https://gohugo.io/templates/go-templates/)。
|
||||
|
||||
比如,下面的命令会直接列出镜像结果,并且只包含镜像ID和仓库名:
|
||||
|
||||
```bash
|
||||
$ docker image ls --format "{{.ID}}: {{.Repository}}"
|
||||
5f515359c7f8: redis
|
||||
05a60462f8ba: nginx
|
||||
fe9198c04d62: mongo
|
||||
00285df0df87: <none>
|
||||
f753707788c5: ubuntu
|
||||
f753707788c5: ubuntu
|
||||
1e0c3dd64ccd: ubuntu
|
||||
```
|
||||
|
||||
或者打算以表格等距显示,并且有标题行,和默认一样,不过自己定义列:
|
||||
|
||||
```bash
|
||||
$ docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
|
||||
IMAGE ID REPOSITORY TAG
|
||||
5f515359c7f8 redis latest
|
||||
05a60462f8ba nginx latest
|
||||
fe9198c04d62 mongo 3.2
|
||||
00285df0df87 <none> <none>
|
||||
f753707788c5 ubuntu 18.04
|
||||
f753707788c5 ubuntu latest
|
||||
1e0c3dd64ccd ubuntu 14.04
|
||||
```
|
||||
|
||||
## 删除本地镜像
|
||||
|
||||
如果要删除本地的镜像,可以使用 `docker image rm` 命令,其格式为:
|
||||
|
||||
```
|
||||
$ docker image rm [选项] <镜像1> [<镜像2> ...]
|
||||
```
|
||||
|
||||
### 用 ID、镜像名、摘要删除镜像
|
||||
|
||||
其中,`<镜像>` 可以是 `镜像短 ID`、`镜像长 ID`、`镜像名` 或者 `镜像摘要`。
|
||||
|
||||
比如我们有这么一些镜像:
|
||||
|
||||
```bash
|
||||
$ docker image ls
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
centos latest 0584b3d2cf6d 3 weeks ago 196.5 MB
|
||||
redis alpine 501ad78535f0 3 weeks ago 21.03 MB
|
||||
docker latest cf693ec9b5c7 3 weeks ago 105.1 MB
|
||||
nginx latest e43d811ce2f4 5 weeks ago 181.5 MB
|
||||
```
|
||||
|
||||
我们可以用镜像的完整 ID,也称为 `长 ID`,来删除镜像。使用脚本的时候可能会用长 ID,但是人工输入就太累了,所以更多的时候是用 `短 ID` 来删除镜像。`docker image ls` 默认列出的就已经是短 ID 了,一般取前3个字符以上,只要足够区分于别的镜像就可以了。
|
||||
|
||||
比如这里,如果我们要删除 `redis:alpine` 镜像,可以执行:
|
||||
|
||||
```bash
|
||||
$ docker image rm 501
|
||||
Untagged: redis:alpine
|
||||
Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d
|
||||
Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7
|
||||
Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
|
||||
Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
|
||||
Deleted: sha256:127227698ad74a5846ff5153475e03439d96d4b1c7f2a449c7a826ef74a2d2fa
|
||||
Deleted: sha256:1333ecc582459bac54e1437335c0816bc17634e131ea0cc48daa27d32c75eab3
|
||||
Deleted: sha256:4fc455b921edf9c4aea207c51ab39b10b06540c8b4825ba57b3feed1668fa7c7
|
||||
```
|
||||
|
||||
我们也可以用`镜像名`,也就是 `<仓库名>:<标签>`,来删除镜像。
|
||||
|
||||
```bash
|
||||
$ docker image rm centos
|
||||
Untagged: centos:latest
|
||||
Untagged: centos@sha256:b2f9d1c0ff5f87a4743104d099a3d561002ac500db1b9bfa02a783a46e0d366c
|
||||
Deleted: sha256:0584b3d2cf6d235ee310cf14b54667d889887b838d3f3d3033acd70fc3c48b8a
|
||||
Deleted: sha256:97ca462ad9eeae25941546209454496e1d66749d53dfa2ee32bf1faabd239d38
|
||||
```
|
||||
|
||||
当然,更精确的是使用 `镜像摘要` 删除镜像。
|
||||
|
||||
```bash
|
||||
$ docker image ls --digests
|
||||
REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE
|
||||
node slim sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228 6e0c4c8e3913 3 weeks ago 214 MB
|
||||
|
||||
$ docker image rm node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
|
||||
Untagged: node@sha256:b4f0e0bdeb578043c1ea6862f0d40cc4afe32a4a582f3be235a3b164422be228
|
||||
```
|
||||
|
||||
### Untagged 和 Deleted
|
||||
|
||||
如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是 `Untagged`,另一类是 `Deleted`。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。
|
||||
|
||||
因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 `Untagged` 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 `Delete` 行为就不会发生。所以并非所有的 `docker image rm`都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。
|
||||
|
||||
当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。这就是为什么,有时候会奇怪,为什么明明没有别的标签指向这个镜像,但是它还是存在的原因,也是为什么有时候会发现所删除的层数和自己 `docker pull` 看到的层数不一样的源。
|
||||
|
||||
除了镜像依赖以外,还需要注意的是容器对镜像的依赖。如果有用这个镜像启动的容器存在(即使容器没有运行),那么同样不可以删除这个镜像。之前讲过,容器是以镜像为基础,再加一层容器存储层,组成这样的多层存储结构去运行的。因此该镜像如果被这个容器所依赖的,那么删除必然会导致故障。如果这些容器是不需要的,应该先将它们删除,然后再来删除镜像。
|
||||
|
||||
### 用 docker image ls 命令来配合
|
||||
|
||||
像其它可以承接多个实体的命令一样,可以使用 `docker image ls -q` 来配合使用 `docker image rm`,这样可以成批的删除希望删除的镜像。我们在“镜像列表”章节介绍过很多过滤镜像列表的方式都可以拿过来使用。
|
||||
|
||||
比如,我们需要删除所有仓库名为 `redis` 的镜像:
|
||||
|
||||
```bash
|
||||
$ docker image rm $(docker image ls -q redis)
|
||||
```
|
||||
|
||||
或者删除所有在 `mongo:3.2` 之前的镜像:
|
||||
|
||||
```bash
|
||||
$ docker image rm $(docker image ls -q -f before=mongo:3.2)
|
||||
```
|
||||
|
||||
充分利用你的想象力和 Linux 命令行的强大,你可以完成很多非常赞的功能。
|
||||
|
||||
### CentOS/RHEL 的用户需要注意的事项
|
||||
|
||||
在 Ubuntu/Debian 上有 `UnionFS` 可以使用,如 `aufs` 或者 `overlay2`,而 CentOS 和 RHEL 的内核中没有相关驱动。因此对于这类系统,一般使用 `devicemapper` 驱动利用 LVM 的一些机制来模拟分层存储。这样的做法除了性能比较差外,稳定性一般也不好,而且配置相对复杂。Docker 安装在 CentOS/RHEL 上后,会默认选择 `devicemapper`,但是为了简化配置,其 `devicemapper`是跑在一个稀疏文件模拟的块设备上,也被称为 `loop-lvm`。这样的选择是因为不需要额外配置就可以运行 Docker,这是自动配置唯一能做到的事情。但是 `loop-lvm` 的做法非常不好,其稳定性、性能更差,无论是日志还是 `docker info` 中都会看到警告信息。官方文档有明确的文章讲解了如何配置块设备给 `devicemapper` 驱动做存储层的做法,这类做法也被称为配置 `direct-lvm`。
|
||||
|
||||
除了前面说到的问题外,`devicemapper` + `loop-lvm` 还有一个缺陷,因为它是稀疏文件,所以它会不断增长。用户在使用过程中会注意到 `/var/lib/docker/devicemapper/devicemapper/data` 不断增长,而且无法控制。很多人会希望删除镜像或者可以解决这个问题,结果发现效果并不明显。原因就是这个稀疏文件的空间释放后基本不进行垃圾回收的问题。因此往往会出现即使删除了文件内容,空间却无法回收,随着使用这个稀疏文件一直在不断增长。
|
||||
|
||||
所以对于 CentOS/RHEL 的用户来说,在没有办法使用 `UnionFS` 的情况下,一定要配置 `direct-lvm` 给 `devicemapper`,无论是为了性能、稳定性还是空间利用率。
|
||||
|
||||
*或许有人注意到了 CentOS 7 中存在被 backports 回来的 overlay 驱动,不过 CentOS 里的这个驱动达不到生产环境使用的稳定程度,所以不推荐使用。*
|
||||
|
||||
## 使用 Dockerfile 定制镜像
|
||||
|
||||
从刚才的 `docker commit` 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
|
||||
|
||||
Dockerfile 是一个文本文件,其内包含了一条条的**指令(Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
|
||||
|
||||
还以之前定制 `nginx` 镜像为例,这次我们使用 Dockerfile 来定制。
|
||||
|
||||
在一个空白目录中,建立一个文本文件,并命名为 `Dockerfile`:
|
||||
|
||||
```bash
|
||||
$ mkdir mynginx
|
||||
$ cd mynginx
|
||||
$ touch Dockerfile
|
||||
```
|
||||
|
||||
其内容为:
|
||||
|
||||
```dockerfile
|
||||
FROM nginx
|
||||
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
|
||||
```
|
||||
|
||||
这个 Dockerfile 很简单,一共就两行。涉及到了两条指令,`FROM` 和 `RUN`。
|
||||
|
||||
### 构建镜像
|
||||
|
||||
好了,让我们再回到之前定制的 nginx 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。
|
||||
|
||||
在 `Dockerfile` 文件所在目录执行:
|
||||
|
||||
```bash
|
||||
$ docker build -t nginx:v3 .
|
||||
Sending build context to Docker daemon 2.048 kB
|
||||
Step 1 : FROM nginx
|
||||
---> e43d811ce2f4
|
||||
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
|
||||
---> Running in 9cdc27646c7b
|
||||
---> 44aa4490ce2c
|
||||
Removing intermediate container 9cdc27646c7b
|
||||
Successfully built 44aa4490ce2c
|
||||
```
|
||||
|
||||
从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 `Step 2` 中,如同我们之前所说的那样,`RUN` 指令启动了一个容器 `9cdc27646c7b`,执行了所要求的命令,并最后提交了这一层 `44aa4490ce2c`,随后删除了所用到的这个容器 `9cdc27646c7b`。
|
||||
|
||||
这里我们使用了 `docker build` 命令进行镜像构建。其格式为:
|
||||
|
||||
```bash
|
||||
docker build [选项] <上下文路径/URL/->
|
||||
```
|
||||
|
||||
在这里我们指定了最终镜像的名称 `-t nginx:v3`,构建成功后,我们可以像之前运行 `nginx:v2` 那样来运行这个镜像,其结果会和 `nginx:v2` 一样。
|
||||
|
||||
### 镜像构建上下文(Context)
|
||||
|
||||
如果注意,会看到 `docker build` 命令最后有一个 `.`。`.` 表示当前目录,而 `Dockerfile` 就在当前目录,因此不少初学者以为这个路径是在指定 `Dockerfile` 所在路径,这么理解其实是不准确的。如果对应上面的命令格式,你可能会发现,这是在指定**上下文路径**。那么什么是上下文呢?
|
||||
|
||||
首先我们要理解 `docker build` 的工作原理。Docker 在运行时分为 Docker 引擎(也就是服务端守护进程)和客户端工具。Docker 的引擎提供了一组 REST API,被称为 [Docker Remote API](https://docs.docker.com/engine/reference/api/docker_remote_api/),而如 `docker` 命令这样的客户端工具,则是通过这组 API 与 Docker 引擎交互,从而完成各种功能。因此,虽然表面上我们好像是在本机执行各种 `docker` 功能,但实际上,一切都是使用的远程调用形式在服务端(Docker 引擎)完成。也因为这种 C/S 设计,让我们操作远程服务器的 Docker 引擎变得轻而易举。
|
||||
|
||||
当我们进行镜像构建的时候,并非所有定制都会通过 `RUN` 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 `COPY` 指令、`ADD` 指令等。而 `docker build` 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
|
||||
|
||||
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,`docker build` 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
|
||||
|
||||
如果在 `Dockerfile` 中这么写:
|
||||
|
||||
```Dockerfile
|
||||
COPY ./package.json /app/
|
||||
```
|
||||
|
||||
这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`,也不是复制 `Dockerfile` 所在目录下的 `package.json`,而是复制 **上下文(context)** 目录下的 `package.json`。
|
||||
|
||||
因此,`COPY` 这类指令中的源文件的路径都是*相对路径*。这也是初学者经常会问的为什么 `COPY ../package.json /app` 或者 `COPY /opt/xxxx /app` 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
|
||||
|
||||
现在就可以理解刚才的命令 `docker build -t nginx:v3 .` 中的这个 `.`,实际上是在指定上下文的目录,`docker build` 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
|
||||
|
||||
如果观察 `docker build` 输出,我们其实已经看到了这个发送上下文的过程:
|
||||
|
||||
```bash
|
||||
$ docker build -t nginx:v3 .
|
||||
Sending build context to Docker daemon 2.048 kB
|
||||
...
|
||||
```
|
||||
|
||||
理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 `COPY /opt/xxxx /app` 不工作后,于是干脆将 `Dockerfile` 放到了硬盘根目录去构建,结果发现 `docker build` 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 `docker build`打包整个硬盘,这显然是使用错误。
|
||||
|
||||
一般来说,应该会将 `Dockerfile` 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 `.gitignore` 一样的语法写一个 `.dockerignore`,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
|
||||
|
||||
那么为什么会有人误以为 `.` 是指定 `Dockerfile` 所在目录呢?这是因为在默认情况下,如果不额外指定 `Dockerfile` 的话,会将上下文目录下的名为 `Dockerfile` 的文件作为 Dockerfile。
|
||||
|
||||
这只是默认行为,实际上 `Dockerfile` 的文件名并不要求必须为 `Dockerfile`,而且并不要求必须位于上下文目录中,比如可以用 `-f ../Dockerfile.php` 参数指定某个文件作为 `Dockerfile`。
|
||||
|
||||
当然,一般大家习惯性的会使用默认的文件名 `Dockerfile`,以及会将其置于镜像构建上下文目录中。
|
||||
|
||||
### 其它 `docker build` 的用法
|
||||
|
||||
#### 直接用 Git repo 进行构建
|
||||
|
||||
或许你已经注意到了,`docker build` 还支持从 URL 构建,比如可以直接从 Git repo 中构建:
|
||||
|
||||
```bash
|
||||
$ docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14
|
||||
docker build https://github.com/twang2218/gitlab-ce-zh.git\#:8.14
|
||||
Sending build context to Docker daemon 2.048 kB
|
||||
Step 1 : FROM gitlab/gitlab-ce:8.14.0-ce.0
|
||||
8.14.0-ce.0: Pulling from gitlab/gitlab-ce
|
||||
aed15891ba52: Already exists
|
||||
773ae8583d14: Already exists
|
||||
...
|
||||
```
|
||||
|
||||
这行命令指定了构建所需的 Git repo,并且指定默认的 `master` 分支,构建目录为 `/8.14/`,然后 Docker 就会自己去 `git clone` 这个项目、切换到指定分支、并进入到指定目录后开始构建。
|
||||
|
||||
#### 用给定的 tar 压缩包构建
|
||||
|
||||
```bash
|
||||
$ docker build http://server/context.tar.gz
|
||||
```
|
||||
|
||||
如果所给出的 URL 不是个 Git repo,而是个 `tar` 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。
|
||||
|
||||
#### 从标准输入中读取 Dockerfile 进行构建
|
||||
|
||||
```bash
|
||||
docker build - < Dockerfile
|
||||
```
|
||||
|
||||
或
|
||||
|
||||
```bash
|
||||
cat Dockerfile | docker build -
|
||||
```
|
||||
|
||||
如果标准输入传入的是文本文件,则将其视为 `Dockerfile`,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 `COPY` 进镜像之类的事情。
|
||||
|
||||
#### 从标准输入中读取上下文压缩包进行构建
|
||||
|
||||
```bash
|
||||
$ docker build - < context.tar.gz
|
||||
```
|
||||
|
||||
如果发现标准输入的文件格式是 `gzip`、`bzip2` 以及 `xz` 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。
|
|
@ -1,100 +0,0 @@
|
|||
# Docker 仓库
|
||||
|
||||
仓库(Repository)是集中存放镜像的地方。
|
||||
|
||||
一个容易混淆的概念是注册服务器(Registry)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从这方面来说,仓库可以被认为是一个具体的项目或目录。例如对于仓库地址 dl.dockerpool.com/ubuntu 来说,dl.dockerpool.com 是注册服务器地址,ubuntu 是仓库名。
|
||||
|
||||
## Docker Hub
|
||||
|
||||
目前 Docker 官方维护了一个公共仓库 [Docker Hub](https://hub.docker.com/),其中已经包括了数量超过 15,000 的镜像。大部分需求都可以通过在 Docker Hub 中直接下载镜像来实现。
|
||||
|
||||
### 注册
|
||||
|
||||
你可以在 [https://cloud.docker.com](https://cloud.docker.com/) 免费注册一个 Docker 账号。
|
||||
|
||||
### 登录
|
||||
|
||||
可以通过执行 `docker login` 命令交互式的输入用户名及密码来完成在命令行界面登录 Docker Hub。
|
||||
|
||||
你可以通过 `docker logout` 退出登录。
|
||||
|
||||
### 拉取镜像
|
||||
|
||||
你可以通过 `docker search` 命令来查找官方仓库中的镜像,并利用 `docker pull` 命令来将它下载到本地。
|
||||
|
||||
例如以 `centos` 为关键词进行搜索:
|
||||
|
||||
```bash
|
||||
$ docker search centos
|
||||
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
|
||||
centos The official build of CentOS. 465 [OK]
|
||||
tianon/centos CentOS 5 and 6, created using rinse instea... 28
|
||||
blalor/centos Bare-bones base CentOS 6.5 image 6 [OK]
|
||||
saltstack/centos-6-minimal 6 [OK]
|
||||
tutum/centos-6.4 DEPRECATED. Use tutum/centos:6.4 instead. ... 5 [OK]
|
||||
```
|
||||
|
||||
可以看到返回了很多包含关键字的镜像,其中包括镜像名字、描述、收藏数(表示该镜像的受关注程度)、是否官方创建、是否自动创建。
|
||||
|
||||
官方的镜像说明是官方项目组创建和维护的,automated 资源允许用户验证镜像的来源和内容。
|
||||
|
||||
根据是否是官方提供,可将镜像资源分为两类。
|
||||
|
||||
一种是类似 `centos` 这样的镜像,被称为基础镜像或根镜像。这些基础镜像由 Docker 公司创建、验证、支持、提供。这样的镜像往往使用单个单词作为名字。
|
||||
|
||||
还有一种类型,比如 `tianon/centos` 镜像,它是由 Docker 的用户创建并维护的,往往带有用户名称前缀。可以通过前缀 `username/` 来指定使用某个用户提供的镜像,比如 tianon 用户。
|
||||
|
||||
另外,在查找的时候通过 `--filter=stars=N` 参数可以指定仅显示收藏数量为 `N` 以上的镜像。
|
||||
|
||||
下载官方 `centos` 镜像到本地。
|
||||
|
||||
```bash
|
||||
$ docker pull centos
|
||||
Pulling repository centos
|
||||
0b443ba03958: Download complete
|
||||
539c0211cd76: Download complete
|
||||
511136ea3c5a: Download complete
|
||||
7064731afe90: Download complete
|
||||
```
|
||||
|
||||
### 推送镜像
|
||||
|
||||
用户也可以在登录后通过 `docker push` 命令来将自己的镜像推送到 Docker Hub。
|
||||
|
||||
以下命令中的 `username` 请替换为你的 Docker 账号用户名。
|
||||
|
||||
```bash
|
||||
$ docker tag ubuntu:18.04 username/ubuntu:18.04
|
||||
|
||||
$ docker image ls
|
||||
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
ubuntu 18.04 275d79972a86 6 days ago 94.6MB
|
||||
username/ubuntu 18.04 275d79972a86 6 days ago 94.6MB
|
||||
|
||||
$ docker push username/ubuntu:18.04
|
||||
|
||||
$ docker search username
|
||||
|
||||
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
|
||||
username/ubuntu
|
||||
```
|
||||
|
||||
### 自动创建
|
||||
|
||||
自动创建(Automated Builds)功能对于需要经常升级镜像内程序来说,十分方便。
|
||||
|
||||
有时候,用户创建了镜像,安装了某个软件,如果软件发布新版本则需要手动更新镜像。
|
||||
|
||||
而自动创建允许用户通过 Docker Hub 指定跟踪一个目标网站(目前支持 [GitHub](https://github.com/) 或 [BitBucket](https://bitbucket.org/))上的项目,一旦项目发生新的提交或者创建新的标签(tag),Docker Hub 会自动构建镜像并推送到 Docker Hub 中。
|
||||
|
||||
要配置自动创建,包括如下的步骤:
|
||||
|
||||
- 创建并登录 Docker Hub,以及目标网站;
|
||||
- 在目标网站中连接帐户到 Docker Hub;
|
||||
- 在 Docker Hub 中 [配置一个自动创建](https://registry.hub.docker.com/builds/add/);
|
||||
- 选取一个目标网站中的项目(需要含 `Dockerfile`)和分支;
|
||||
- 指定 `Dockerfile` 的位置,并提交创建。
|
||||
|
||||
之后,可以在 Docker Hub 的 [自动创建页面](https://registry.hub.docker.com/builds/) 中跟踪每次创建的状态。
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
# Docker 服务
|
||||
|
||||
## 关于服务
|
||||
|
||||
在分布式应用程序中,应用程序的不同部分被称为“服务”。例如,如果您想象一个视频共享网站,它可能包含用于将应用程序数据存储在数据库中的服务,用户上传文件后在后台传输的服务,前端应用服务等等。
|
||||
|
||||
服务实际上只是“生产环境中的容器”。一个服务只运行一个镜像,但它需要制定镜像的运行方式 - 应该使用哪个端口,容器应该运行多少副本,以便服务具有所需的容量,以及等等。缩放服务会更改运行该软件的容器实例的数量,从而为流程中的服务分配更多计算资源。
|
||||
|
||||
幸运的是,使用 Docker 平台定义,运行和扩展服务非常简单 - 只需编写一个 docker-compose.yml 文件即可。
|
||||
|
||||
## docker-compose.yml 文件
|
||||
|
||||
它是一个YAML文件,它定义了Docker容器在生产中的行为方式。
|
||||
|
||||
|
||||
```
|
||||
version: "3"
|
||||
services:
|
||||
web:
|
||||
# replace username/repo:tag with your name and image details
|
||||
image: username/repo:tag
|
||||
deploy:
|
||||
replicas: 5
|
||||
resources:
|
||||
limits:
|
||||
cpus: "0.1"
|
||||
memory: 50M
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
ports:
|
||||
- "80:80"
|
||||
networks:
|
||||
- webnet
|
||||
networks:
|
||||
webnet:
|
||||
```
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
<!-- TOC depthFrom:2 depthTo:2 -->
|
||||
|
||||
- [为何使用 Docker](#为何使用-docker)
|
||||
- [运维](#运维)
|
||||
- [容器(Container)](#容器container)
|
||||
- [镜像(Images)](#镜像images)
|
||||
- [网络(Networks)](#网络networks)
|
||||
|
@ -20,9 +22,89 @@
|
|||
|
||||
<!-- /TOC -->
|
||||
|
||||
## 为何使用 Docker
|
||||
|
||||
「通过 Docker,开发者可以使用任何语言任何工具创建任何应用。“Dockerized” 的应用是完全可移植的,能在任何地方运行 - 不管是同事的 OS X 和 Windows 笔记本,或是在云端运行的 Ubuntu QA 服务,还是在虚拟机运行的 Red Hat 产品数据中心。
|
||||
|
||||
Docker Hub 上有 13000+ 的应用,开发者可以从中选取一个进行快速扩展开发。Docker 跟踪管理变更和依赖关系,让系统管理员能更容易理解开发人员是如何让应用运转起来的。而开发者可以通过 Docker Hub 的共有/私有仓库,构建他们的自动化编译,与其他合作者共享成果。
|
||||
|
||||
Docker 帮助开发者更快地构建和发布高质量的应用。」—— [什么是 Docker](https://www.docker.com/what-docker/#copy1)
|
||||
|
||||
## 运维
|
||||
|
||||
### 安装
|
||||
|
||||
Docker 是一个开源的商业产品,有两个版本:社区版(Community Edition,缩写为 CE)和企业版(Enterprise Edition,缩写为 EE)。企业版包含了一些收费服务,个人开发者一般用不到。
|
||||
|
||||
Docker CE 的安装请参考官方文档。
|
||||
|
||||
- [Mac](https://docs.docker.com/docker-for-mac/install/)
|
||||
- [Windows](https://docs.docker.com/docker-for-windows/install/)
|
||||
- [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/)
|
||||
- [Debian](https://docs.docker.com/install/linux/docker-ce/debian/)
|
||||
- [CentOS](https://docs.docker.com/install/linux/docker-ce/centos/)
|
||||
- [Fedora](https://docs.docker.com/install/linux/docker-ce/fedora/)
|
||||
- [其他 Linux 发行版](https://docs.docker.com/install/linux/docker-ce/binaries/)
|
||||
|
||||
### 检查版本
|
||||
|
||||
[`docker version`](https://docs.docker.com/engine/reference/commandline/version/) 查看你正在运行的 Docker 版本。
|
||||
|
||||
获取 Docker 服务版本:
|
||||
|
||||
```
|
||||
docker version --format '{{.Server.Version}}'
|
||||
```
|
||||
|
||||
你也可以输出原始的 JSON 数据:
|
||||
|
||||
```
|
||||
docker version --format '{{json .}}'
|
||||
```
|
||||
|
||||
### Docker 加速
|
||||
|
||||
国内访问 Docker Hub 很慢,所以,推荐配置 Docker 镜像仓库来提速。
|
||||
|
||||
镜像仓库清单:
|
||||
|
||||
| 镜像仓库 | 镜像仓库地址 | 说明 |
|
||||
| --------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [DaoCloud 镜像站](https://daocloud.io/mirror) | `http://f1361db2.m.daocloud.io` | 开发者需要开通 DaoCloud 账户,然后可以得到专属加速器。 |
|
||||
| [阿里云](https://cr.console.aliyun.com) | `https://yourcode.mirror.aliyuncs.com` | 开发者需要开通阿里开发者帐户,再使用阿里的加速服务。登录后阿里开发者帐户后,`https://cr.console.aliyun.com/undefined/instances/mirrors` 中查看你的您的专属加速器地址。 |
|
||||
| [网易云](https://c.163yun.com/hub) | `https://hub-mirror.c.163.com` | 直接配置即可,亲测较为稳定。 |
|
||||
|
||||
配置镜像仓库方法(以 CentOS 为例):
|
||||
|
||||
> 下面的示例为在 CentOS 环境中,指定镜像仓库为 `https://hub-mirror.c.163.com`
|
||||
|
||||
(1)修改配置文件
|
||||
|
||||
修改 `/etc/docker/daemon.json` ,如果不存在则新建。执行以下 Shell:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /etc/docker
|
||||
cat >> /etc/docker/daemon.json << EOF
|
||||
{
|
||||
"registry-mirrors": [
|
||||
"https://hub-mirror.c.163.com"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
重启 docker 以生效:
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
执行 `docker info` 命令,查看 `Registry Mirrors` 是否已被改为 `https://hub-mirror.c.163.com` ,如果是,则表示配置成功。
|
||||
|
||||
## 容器(Container)
|
||||
|
||||
[关于 Docker 进程隔离的基础](http://etherealmind.com/basics-docker-containers-hypervisors-coreos/)。容器 (Container) 之于虚拟机 (Virtual Machine) 就好比线程之于进程。
|
||||
[关于 Docker 进程隔离的基础](http://etherealmind.com/basics-docker-containers-hypervisors-coreos/)。容器 (Container) 之于虚拟机 (Virtual Machine) 就好比线程之于进程。或者你可以把他们想成是「吃了类固醇的 chroots」。
|
||||
|
||||
### 生命周期
|
||||
|
||||
|
@ -38,7 +120,7 @@
|
|||
|
||||
如果你需要一个临时容器,可使用 `docker run --rm` 会在容器停止之后删除它。
|
||||
|
||||
如果你需要映射宿主机 (host) 的目录到 Docker 容器内,可使用 `docker run -v $HOSTDIR:$DOCKERDIR`。详见 [卷标(Volumes)](#卷标volumes) 一节。
|
||||
如果你需要映射宿主机 (host) 的目录到 Docker 容器内,可使用 `docker run -v $HOSTDIR:$DOCKERDIR`。详见 [卷标(Volumes)](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#卷标volumes) 一节。
|
||||
|
||||
如果你想同时删除与容器相关联的卷标,那么在删除容器的时候必须包含 `-v` 选项,像这样 `docker rm -v`。
|
||||
|
||||
|
@ -55,7 +137,7 @@
|
|||
- [`docker kill`](https://docs.docker.com/engine/reference/commandline/kill) 向运行中的容器发送 SIGKILL 指令。
|
||||
- [`docker attach`](https://docs.docker.com/engine/reference/commandline/attach) 连接到运行中的容器。
|
||||
|
||||
如果你想将容器的端口 (ports) 暴露至宿主机,请见 [暴露端口](#暴露端口exposing-ports) 一节。
|
||||
如果你想将容器的端口 (ports) 暴露至宿主机,请见 [暴露端口](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#暴露端口exposing-ports) 一节。
|
||||
|
||||
关于 Docker 实例崩溃后的重启策略,详见 [本文](http://container42.com/2014/09/30/docker-restart-policies/)。
|
||||
|
||||
|
@ -69,7 +151,7 @@
|
|||
docker run -ti --c 512 agileek/cpuset-test
|
||||
```
|
||||
|
||||
更多信息请参阅 <https://goldmann.pl/blog/2014/09/11/resource-management-in-docker/#_cpu>。
|
||||
更多信息请参阅 https://goldmann.pl/blog/2014/09/11/resource-management-in-docker/#_cpu。
|
||||
|
||||
通过 [`cpuset-cpus`](https://docs.docker.com/engine/reference/run/#/cpuset-constraint) 可使用特定 CPU 内核。
|
||||
|
||||
|
@ -77,9 +159,9 @@ docker run -ti --c 512 agileek/cpuset-test
|
|||
docker run -ti --cpuset-cpus=0,4,6 agileek/cpuset-test
|
||||
```
|
||||
|
||||
请参阅 <https://agileek.github.io/docker/2014/08/06/docker-cpuset/> 获取更多细节以及一些不错的视频。
|
||||
请参阅 https://agileek.github.io/docker/2014/08/06/docker-cpuset/ 获取更多细节以及一些不错的视频。
|
||||
|
||||
注意,Docker 在容器内仍然能够 **看到** 全部 CPU -- 它仅仅是不使用全部而已。请参阅 <https://github.com/docker/docker/issues/20770> 获取更多细节。
|
||||
注意,Docker 在容器内仍然能够 **看到** 全部 CPU -- 它仅仅是不使用全部而已。请参阅 https://github.com/docker/docker/issues/20770 获取更多细节。
|
||||
|
||||
#### 内存限制
|
||||
|
||||
|
@ -91,7 +173,7 @@ docker run -it -m 300M ubuntu:14.04 /bin/bash
|
|||
|
||||
#### 能力(Capabilities)
|
||||
|
||||
Linux 的 Capability 可以通过使用 `cap-add` 和 `cap-drop` 设置。请参阅 <https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities> 获取更多细节。这有助于提高安全性。
|
||||
Linux 的 Capability 可以通过使用 `cap-add` 和 `cap-drop` 设置。请参阅 https://docs.docker.com/engine/reference/run/#/runtime-privilege-and-linux-capabilities 获取更多细节。这有助于提高安全性。
|
||||
|
||||
如需要挂载基于 FUSE 的文件系统,你需要结合 `--cap-add` 和 `--device` 使用:
|
||||
|
||||
|
@ -136,7 +218,8 @@ docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash
|
|||
### 执行命令
|
||||
|
||||
- [`docker exec`](https://docs.docker.com/engine/reference/commandline/exec) 在容器内执行命令。
|
||||
- 例如,进入正在运行的 `foo` 容器,并连接 (attach) 到一个新的 Shell 进程:`docker exec -it foo /bin/bash`。
|
||||
|
||||
例如,进入正在运行的 `foo` 容器,并连接 (attach) 到一个新的 Shell 进程:`docker exec -it foo /bin/bash`。
|
||||
|
||||
## 镜像(Images)
|
||||
|
||||
|
@ -159,7 +242,7 @@ docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash
|
|||
|
||||
### 清理
|
||||
|
||||
虽然你可以用 `docker rmi` 命令来删除指定的镜像,不过有个名为 [docker-gc](https://github.com/spotify/docker-gc) 的工具,它可以以一种安全的方式,清理掉那些不再被任何容器使用的镜像。Docker 1.13 起,使用 `docker image prune` 亦可删除未使用的镜像。参见 [清理](#清理)。
|
||||
虽然你可以用 `docker rmi` 命令来删除指定的镜像,不过有个名为 [docker-gc](https://github.com/spotify/docker-gc) 的工具,它可以以一种安全的方式,清理掉那些不再被任何容器使用的镜像。Docker 1.13 起,使用 `docker image prune` 亦可删除未使用的镜像。参见 [清理](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#清理)。
|
||||
|
||||
### 加载 / 保存镜像
|
||||
|
||||
|
@ -191,8 +274,7 @@ docker export my_container | gzip > my_container.tar.gz
|
|||
|
||||
### 加载已保存的镜像 与 导入已导出为镜像的容器 的不同
|
||||
|
||||
通过 `load` 命令来加载镜像,会创建一个新的镜像,并继承原镜像的所有历史。
|
||||
通过 `import` 将容器作为镜像导入,也会创建一个新的镜像,但并不包含原镜像的历史,因此会比使用 `load` 方式生成的镜像更小。
|
||||
通过 `load` 命令来加载镜像,会创建一个新的镜像,并继承原镜像的所有历史。 通过 `import` 将容器作为镜像导入,也会创建一个新的镜像,但并不包含原镜像的历史,因此会比使用 `load` 方式生成的镜像更小。
|
||||
|
||||
## 网络(Networks)
|
||||
|
||||
|
@ -252,8 +334,8 @@ Docker 官方托管着自己的 [仓管中心](https://hub.docker.com/),包含
|
|||
|
||||
以下是一些编写 Dockerfile 的常用编辑器,并链接到适配的语法高亮模块︰
|
||||
|
||||
- 如果你在使用 [jEdit](http://jedit.org),你可以使用我开发的 Dockerfile [语法高亮模块](https://github.com/wsargent/jedit-docker-mode)。
|
||||
- [Sublime Text 2](https://packagecontrol.io/packages/Dockerfile%20Syntax%20Highlighting)
|
||||
- 如果你在使用 [jEdit](http://jedit.org/),你可以使用我开发的 Dockerfile [语法高亮模块](https://github.com/wsargent/jedit-docker-mode)。
|
||||
- [Sublime Text 2](https://packagecontrol.io/packages/Dockerfile Syntax Highlighting)
|
||||
- [Atom](https://atom.io/packages/language-docker)
|
||||
- [Vim](https://github.com/ekalinin/Dockerfile.vim)
|
||||
- [Emacs](https://github.com/spotify/dockerfile-mode)
|
||||
|
@ -361,7 +443,7 @@ docker run -v /Users/wsargent/myapp/src:/src
|
|||
|
||||
还可以考虑运行一个纯数据容器,像 [这里](http://container42.com/2013/12/16/persistent-volumes-with-docker-container-as-volume-pattern/) 所说的那样,提供可移植数据。
|
||||
|
||||
记得,[文件也可以被挂载为卷标](#将文件挂载为卷标)。
|
||||
记得,[文件也可以被挂载为卷标](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#将文件挂载为卷标)。
|
||||
|
||||
## 暴露端口(Exposing ports)
|
||||
|
||||
|
@ -498,8 +580,7 @@ USER user
|
|||
|
||||
### 安全路线图
|
||||
|
||||
Docker 的路线图提到关于 [seccomp 的支持](https://github.com/docker/docker/blob/master/ROADMAP.md#11-security)。
|
||||
一个名为 [bane](https://github.com/jfrazelle/bane) 的 AppArmor 策略生成器正在实现 [安全配置文件](https://github.com/docker/docker/issues/17142)。
|
||||
Docker 的路线图提到关于 [seccomp 的支持](https://github.com/docker/docker/blob/master/ROADMAP.md#11-security)。 一个名为 [bane](https://github.com/jfrazelle/bane) 的 AppArmor 策略生成器正在实现 [安全配置文件](https://github.com/docker/docker/issues/17142)。
|
||||
|
||||
## 小贴士
|
||||
|
||||
|
@ -654,8 +735,7 @@ docker images -viz | dot -Tpng -o docker.png
|
|||
|
||||
- 在某层 (RUN layer) 清理 APT
|
||||
|
||||
这应当和其他 apt 命令在同一层中完成。
|
||||
否则,前面的层将会保持原有信息,而你的镜像则依旧臃肿。
|
||||
这应当和其他 apt 命令在同一层中完成。 否则,前面的层将会保持原有信息,而你的镜像则依旧臃肿。
|
||||
|
||||
```
|
||||
RUN {apt commands} \
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Dockerfile
|
||||
# Dockerfile 最佳实践
|
||||
|
||||
<!-- TOC depthFrom:2 depthTo:3 -->
|
||||
|
||||
|
@ -25,9 +25,8 @@
|
|||
|
||||
### FROM(指定基础镜像)
|
||||
|
||||
> 作用:
|
||||
> 作用:**`FROM` 指令用于指定基础镜像**。
|
||||
>
|
||||
> `FROM` 指令用于指定基础镜像。
|
||||
|
||||
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 `nginx` 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 `FROM` 就是指定**基础镜像**,因此一个 `Dockerfile` 中 `FROM` 是必备的指令,并且必须是第一条指令。
|
||||
|
||||
|
@ -48,15 +47,16 @@ FROM scratch
|
|||
|
||||
### RUN(执行命令)
|
||||
|
||||
`RUN` 指令是用来执行命令行命令的。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种:
|
||||
|
||||
- _shell_ 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。
|
||||
|
||||
```dockerfile
|
||||
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
|
||||
```
|
||||
|
||||
- _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。
|
||||
> **`RUN` 指令是用来执行命令行命令的**。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种:
|
||||
>
|
||||
> - _shell_ 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。
|
||||
>
|
||||
> ```dockerfile
|
||||
> RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
|
||||
> ```
|
||||
>
|
||||
> - _exec_ 格式:`RUN ["可执行文件", "参数1", "参数2"]`,这更像是函数调用中的格式。
|
||||
>
|
||||
|
||||
既然 `RUN` 就像 Shell 脚本一样可以执行命令,那么我们是否就可以像 Shell 脚本一样把每个命令对应一个 RUN 呢?比如这样:
|
||||
|
||||
|
@ -641,7 +641,7 @@ FROM my-node
|
|||
|
||||
是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。
|
||||
|
||||
## 引用和引申
|
||||
## 参考资料
|
||||
|
||||
- [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/)
|
||||
- [Dockerfile 最佳实践文档](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/)
|
|
@ -0,0 +1,356 @@
|
|||
# Docker 快速入门
|
||||
|
||||
<!-- TOC depthFrom:2 depthTo:2 -->
|
||||
|
||||
- [一、Docker 的简介](#一docker-的简介)
|
||||
- [二、Docker 的运维](#二docker-的运维)
|
||||
- [三、hello world 实例](#三hello-world-实例)
|
||||
- [四、制作 Docker 容器](#四制作-docker-容器)
|
||||
- [参考资料](#参考资料)
|
||||
|
||||
<!-- /TOC -->
|
||||
|
||||
## 一、Docker 的简介
|
||||
|
||||
### 什么是 Docker
|
||||
|
||||
> **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。**
|
||||
|
||||
它是目前最流行的 Linux 容器解决方案。
|
||||
|
||||
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
|
||||
|
||||
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
|
||||
|
||||
### 为什么需要 Docker
|
||||
|
||||
- **更高效的利用系统资源** - 由于容器不需要进行硬件虚拟以及运行完整操作系统等额外开销,`Docker` 对系统资源的利用率更高。无论是应用执行速度、内存损耗或者文件存储速度,都要比传统虚拟机技术更高效。因此,相比虚拟机技术,一个相同配置的主机,往往可以运行更多数量的应用。
|
||||
- **更快速的启动时间** - 传统的虚拟机技术启动应用服务往往需要数分钟,而 `Docker` 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。
|
||||
- **一致的运行环境** - 开发过程中一个常见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些 bug 并未在开发过程中被发现。而 `Docker` 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 _「这段代码在我机器上没问题啊」_ 这类问题。
|
||||
- **持续交付和部署** - 对开发和运维([DevOps](https://zh.wikipedia.org/wiki/DevOps))人员来说,最希望的就是一次创建或配置,可以在任意地方正常运行。使用 `Docker` 可以通过定制应用镜像来实现持续集成、持续交付、部署。开发人员可以通过 [Dockerfile](https://yeasy.gitbooks.io/docker_practice/image/dockerfile) 来进行镜像构建,并结合 [持续集成(Continuous Integration)](https://en.wikipedia.org/wiki/Continuous_integration) 系统进行集成测试,而运维人员则可以直接在生产环境中快速部署该镜像,甚至结合 [持续部署(Continuous Delivery/Deployment)](https://en.wikipedia.org/wiki/Continuous_delivery) 系统进行自动部署。而且使用 [`Dockerfile`](https://yeasy.gitbooks.io/docker_practice/image/build.html) 使镜像构建透明化,不仅仅开发团队可以理解应用运行环境,也方便运维团队理解应用运行所需条件,帮助更好的生产环境中部署该镜像。
|
||||
- **更轻松的迁移** - 由于 `Docker` 确保了执行环境的一致性,使得应用的迁移更加容易。`Docker` 可以在很多平台上运行,无论是物理机、虚拟机、公有云、私有云,甚至是笔记本,其运行结果是一致的。因此用户可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。
|
||||
- **更轻松的维护和扩展** - `Docker` 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,`Docker` 团队同各个开源项目团队一起维护了一大批高质量的 [官方镜像](https://hub.docker.com/search/?type=image&image_filter=official),既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
|
||||
|
||||
![img](http://dunwu.test.upcdn.net/cs/os/docker/containers-and-vm.png)
|
||||
|
||||
### Docker 的主要用途
|
||||
|
||||
Docker 提供了被称为容器的松散隔离环境,在环境中可以打包和运行应用程序。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机的内核中运行。这意味着您可以在给定的硬件组合上运行更多容器,而不是使用虚拟机。你甚至可以在实际上是虚拟机的主机中运行 Docker 容器!
|
||||
|
||||
Docker 的主要用途,目前有三大类。
|
||||
|
||||
- **提供一次性的环境。**比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
|
||||
- **提供弹性的云服务。**因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
|
||||
- **组建微服务架构。**通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。
|
||||
|
||||
### Docker 的核心概念
|
||||
|
||||
#### 镜像
|
||||
|
||||
Docker 把应用程序及其依赖,打包在镜像(Image)文件里面。
|
||||
|
||||
我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
|
||||
|
||||
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
|
||||
|
||||
**分层存储**
|
||||
|
||||
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
|
||||
|
||||
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
|
||||
|
||||
分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
|
||||
|
||||
#### 容器
|
||||
|
||||
镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
|
||||
|
||||
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
|
||||
|
||||
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为**容器存储层**。
|
||||
|
||||
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
|
||||
|
||||
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
|
||||
|
||||
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
|
||||
|
||||
#### 仓库
|
||||
|
||||
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,[Docker Registry](https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html) 就是这样的服务。
|
||||
|
||||
一个 **Docker Registry** 中可以包含多个**仓库**(`Repository`);每个仓库可以包含多个**标签**(`Tag`);每个标签对应一个镜像。
|
||||
|
||||
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 `latest` 作为默认标签。
|
||||
|
||||
以 [Ubuntu 镜像](https://store.docker.com/images/ubuntu) 为例,`ubuntu` 是仓库的名字,其内包含有不同的版本标签,如,`16.04`, `18.04`。我们可以通过 `ubuntu:14.04`,或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 `ubuntu`,那将视为 `ubuntu:latest`。
|
||||
|
||||
仓库名经常以 _两段式路径_ 形式出现,比如 `jwilder/nginx-proxy`,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
|
||||
|
||||
## 二、Docker 的运维
|
||||
|
||||
不同操作系统环境下安装 Docker 的方式有所不同,详情可以参:
|
||||
|
||||
- [Docker 官方安装指南](https://docs.docker.com/install/)
|
||||
- [安装 Docker(中文)](https://docker_practice.gitee.io/install/)
|
||||
|
||||
国内访问 Docker 比较慢,如果需要提速,可以参考 [镜像加速器](https://docker_practice.gitee.io/install/mirror.html)
|
||||
|
||||
安装完成后,运行下面的命令,验证是否安装成功。
|
||||
|
||||
- `docker version`
|
||||
- `docker info`
|
||||
|
||||
Docker 需要用户具有 sudo 权限,为了避免每次命令都输入`sudo`,可以把用户加入 Docker 用户组([官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user))。
|
||||
|
||||
```bash
|
||||
$ sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
Docker 是服务器----客户端架构。命令行运行`docker`命令的时候,需要本机有 Docker 服务。如果这项服务没有启动,可以用下面的命令启动([官方文档](https://docs.docker.com/config/daemon/systemd/))。
|
||||
|
||||
```bash
|
||||
# service 命令的用法
|
||||
$ sudo service docker start
|
||||
|
||||
# systemctl 命令的用法
|
||||
$ sudo systemctl start docker
|
||||
```
|
||||
|
||||
## 三、hello world 实例
|
||||
|
||||
下面,我们通过最简单的 image 文件"[hello world"](https://hub.docker.com/r/library/hello-world/),感受一下 Docker。
|
||||
|
||||
需要说明的是,国内连接 Docker 的官方仓库很慢,还会断线,需要将默认仓库改成国内的镜像网站,具体的修改方法在[下一篇文章](http://www.ruanyifeng.com/blog/2018/02/docker-wordpress-tutorial.html)的第一节。有需要的朋友,可以先看一下。
|
||||
|
||||
首先,运行下面的命令,将 image 文件从仓库抓取到本地。
|
||||
|
||||
> ```bash
|
||||
> $ docker image pull library/hello-world
|
||||
> ```
|
||||
|
||||
上面代码中,`docker image pull`是抓取 image 文件的命令。`library/hello-world`是 image 文件在仓库里面的位置,其中`library`是 image 文件所在的组,`hello-world`是 image 文件的名字。
|
||||
|
||||
由于 Docker 官方提供的 image 文件,都放在[`library`](https://hub.docker.com/r/library/)组里面,所以它的是默认组,可以省略。因此,上面的命令可以写成下面这样。
|
||||
|
||||
> ```bash
|
||||
> $ docker image pull hello-world
|
||||
> ```
|
||||
|
||||
抓取成功以后,就可以在本机看到这个 image 文件了。
|
||||
|
||||
> ```bash
|
||||
> $ docker image ls
|
||||
> ```
|
||||
|
||||
现在,运行这个 image 文件。
|
||||
|
||||
> ```bash
|
||||
> $ docker container run hello-world
|
||||
> ```
|
||||
|
||||
`docker container run`命令会从 image 文件,生成一个正在运行的容器实例。
|
||||
|
||||
注意,`docker container run`命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的`docker image pull`命令并不是必需的步骤。
|
||||
|
||||
如果运行成功,你会在屏幕上读到下面的输出。
|
||||
|
||||
> ```bash
|
||||
> $ docker container run hello-world
|
||||
>
|
||||
> Hello from Docker!
|
||||
> This message shows that your installation appears to be working correctly.
|
||||
>
|
||||
> ... ...
|
||||
> ```
|
||||
|
||||
输出这段提示以后,`hello world`就会停止运行,容器自动终止。
|
||||
|
||||
有些容器不会自动终止,因为提供的是服务。比如,安装运行 Ubuntu 的 image,就可以在命令行体验 Ubuntu 系统。
|
||||
|
||||
> ```bash
|
||||
> $ docker container run -it ubuntu bash
|
||||
> ```
|
||||
|
||||
对于那些不会自动终止的容器,必须使用[`docker container kill`](https://docs.docker.com/engine/reference/commandline/container_kill/) 命令手动终止。
|
||||
|
||||
> ```bash
|
||||
> $ docker container kill [containID]
|
||||
> ```
|
||||
|
||||
## 四、制作 Docker 容器
|
||||
|
||||
下面我以 [koa-demos](http://www.ruanyifeng.com/blog/2017/08/koa.html) 项目为例,介绍怎么写 Dockerfile 文件,实现让用户在 Docker 容器里面运行 Koa 框架。
|
||||
|
||||
作为准备工作,请先[下载源码](https://github.com/ruanyf/koa-demos/archive/master.zip)。
|
||||
|
||||
> ```bash
|
||||
> $ git clone https://github.com/ruanyf/koa-demos.git
|
||||
> $ cd koa-demos
|
||||
> ```
|
||||
|
||||
### 编写 Dockerfile 文件
|
||||
|
||||
首先,在项目的根目录下,新建一个文本文件`.dockerignore`,写入下面的[内容](https://github.com/ruanyf/koa-demos/blob/master/.dockerignore)。
|
||||
|
||||
> ```bash
|
||||
> .git
|
||||
> node_modules
|
||||
> npm-debug.log
|
||||
> ```
|
||||
|
||||
上面代码表示,这三个路径要排除,不要打包进入 image 文件。如果你没有路径要排除,这个文件可以不新建。
|
||||
|
||||
然后,在项目的根目录下,新建一个文本文件 Dockerfile,写入下面的[内容](https://github.com/ruanyf/koa-demos/blob/master/Dockerfile)。
|
||||
|
||||
> ```bash
|
||||
> FROM node:8.4
|
||||
> COPY . /app
|
||||
> WORKDIR /app
|
||||
> RUN npm install --registry=https://registry.npm.taobao.org
|
||||
> EXPOSE 3000
|
||||
> ```
|
||||
|
||||
上面代码一共五行,含义如下。
|
||||
|
||||
> - `FROM node:8.4`:该 image 文件继承官方的 node image,冒号表示标签,这里标签是`8.4`,即 8.4 版本的 node。
|
||||
> - `COPY . /app`:将当前目录下的所有文件(除了`.dockerignore`排除的路径),都拷贝进入 image 文件的`/app`目录。
|
||||
> - `WORKDIR /app`:指定接下来的工作路径为`/app`。
|
||||
> - `RUN npm install`:在`/app`目录下,运行`npm install`命令安装依赖。注意,安装后所有的依赖,都将打包进入 image 文件。
|
||||
> - `EXPOSE 3000`:将容器 3000 端口暴露出来, 允许外部连接这个端口。
|
||||
|
||||
### 创建 image 文件
|
||||
|
||||
有了 Dockerfile 文件以后,就可以使用`docker image build`命令创建 image 文件了。
|
||||
|
||||
> ```bash
|
||||
> $ docker image build -t koa-demo .
|
||||
> # 或者
|
||||
> $ docker image build -t koa-demo:0.0.1 .
|
||||
> ```
|
||||
|
||||
上面代码中,`-t`参数用来指定 image 文件的名字,后面还可以用冒号指定标签。如果不指定,默认的标签就是`latest`。最后的那个点表示 Dockerfile 文件所在的路径,上例是当前路径,所以是一个点。
|
||||
|
||||
如果运行成功,就可以看到新生成的 image 文件`koa-demo`了。
|
||||
|
||||
> ```bash
|
||||
> $ docker image ls
|
||||
> ```
|
||||
|
||||
### 生成容器
|
||||
|
||||
`docker container run`命令会从 image 文件生成容器。
|
||||
|
||||
> ```bash
|
||||
> $ docker container run -p 8000:3000 -it koa-demo /bin/bash
|
||||
> # 或者
|
||||
> $ docker container run -p 8000:3000 -it koa-demo:0.0.1 /bin/bash
|
||||
> ```
|
||||
|
||||
上面命令的各个参数含义如下:
|
||||
|
||||
> - `-p`参数:容器的 3000 端口映射到本机的 8000 端口。
|
||||
> - `-it`参数:容器的 Shell 映射到当前的 Shell,然后你在本机窗口输入的命令,就会传入容器。
|
||||
> - `koa-demo:0.0.1`:image 文件的名字(如果有标签,还需要提供标签,默认是 latest 标签)。
|
||||
> - `/bin/bash`:容器启动以后,内部第一个执行的命令。这里是启动 Bash,保证用户可以使用 Shell。
|
||||
|
||||
如果一切正常,运行上面的命令以后,就会返回一个命令行提示符。
|
||||
|
||||
> ```bash
|
||||
> root@66d80f4aaf1e:/app#
|
||||
> ```
|
||||
|
||||
这表示你已经在容器里面了,返回的提示符就是容器内部的 Shell 提示符。执行下面的命令。
|
||||
|
||||
> ```bash
|
||||
> root@66d80f4aaf1e:/app# node demos/01.js
|
||||
> ```
|
||||
|
||||
这时,Koa 框架已经运行起来了。打开本机的浏览器,访问 http://127.0.0.1:8000,网页显示"Not Found",这是因为这个 [demo](https://github.com/ruanyf/koa-demos/blob/master/demos/01.js) 没有写路由。
|
||||
|
||||
这个例子中,Node 进程运行在 Docker 容器的虚拟环境里面,进程接触到的文件系统和网络接口都是虚拟的,与本机的文件系统和网络接口是隔离的,因此需要定义容器与物理机的端口映射(map)。
|
||||
|
||||
现在,在容器的命令行,按下 Ctrl + c 停止 Node 进程,然后按下 Ctrl + d (或者输入 exit)退出容器。此外,也可以用`docker container kill`终止容器运行。
|
||||
|
||||
> ```bash
|
||||
> # 在本机的另一个终端窗口,查出容器的 ID
|
||||
> $ docker container ls
|
||||
>
|
||||
> # 停止指定的容器运行
|
||||
> $ docker container kill [containerID]
|
||||
> ```
|
||||
|
||||
容器停止运行之后,并不会消失,用下面的命令删除容器文件。
|
||||
|
||||
> ```bash
|
||||
> # 查出容器的 ID
|
||||
> $ docker container ls --all
|
||||
>
|
||||
> # 删除指定的容器文件
|
||||
> $ docker container rm [containerID]
|
||||
> ```
|
||||
|
||||
也可以使用`docker container run`命令的`--rm`参数,在容器终止运行后自动删除容器文件。
|
||||
|
||||
> ```bash
|
||||
> $ docker container run --rm -p 8000:3000 -it koa-demo /bin/bash
|
||||
> ```
|
||||
|
||||
### CMD 命令
|
||||
|
||||
上一节的例子里面,容器启动以后,需要手动输入命令`node demos/01.js`。我们可以把这个命令写在 Dockerfile 里面,这样容器启动以后,这个命令就已经执行了,不用再手动输入了。
|
||||
|
||||
> ```bash
|
||||
> FROM node:8.4
|
||||
> COPY . /app
|
||||
> WORKDIR /app
|
||||
> RUN npm install --registry=https://registry.npm.taobao.org
|
||||
> EXPOSE 3000
|
||||
> CMD node demos/01.js
|
||||
> ```
|
||||
|
||||
上面的 Dockerfile 里面,多了最后一行`CMD node demos/01.js`,它表示容器启动后自动执行`node demos/01.js`。
|
||||
|
||||
你可能会问,`RUN`命令与`CMD`命令的区别在哪里?简单说,`RUN`命令在 image 文件的构建阶段执行,执行结果都会打包进入 image 文件;`CMD`命令则是在容器启动后执行。另外,一个 Dockerfile 可以包含多个`RUN`命令,但是只能有一个`CMD`命令。
|
||||
|
||||
注意,指定了`CMD`命令以后,`docker container run`命令就不能附加命令了(比如前面的`/bin/bash`),否则它会覆盖`CMD`命令。现在,启动容器可以使用下面的命令。
|
||||
|
||||
> ```bash
|
||||
> $ docker container run --rm -p 8000:3000 -it koa-demo:0.0.1
|
||||
> ```
|
||||
|
||||
### 发布 image 文件
|
||||
|
||||
容器运行成功后,就确认了 image 文件的有效性。这时,我们就可以考虑把 image 文件分享到网上,让其他人使用。
|
||||
|
||||
首先,去 [hub.docker.com](https://hub.docker.com/) 或 [cloud.docker.com](https://cloud.docker.com/) 注册一个账户。然后,用下面的命令登录。
|
||||
|
||||
> ```bash
|
||||
> $ docker login
|
||||
> ```
|
||||
|
||||
接着,为本地的 image 标注用户名和版本。
|
||||
|
||||
> ```bash
|
||||
> $ docker image tag [imageName] [username]/[repository]:[tag]
|
||||
> # 实例
|
||||
> $ docker image tag koa-demos:0.0.1 ruanyf/koa-demos:0.0.1
|
||||
> ```
|
||||
|
||||
也可以不标注用户名,重新构建一下 image 文件。
|
||||
|
||||
> ```bash
|
||||
> $ docker image build -t [username]/[repository]:[tag] .
|
||||
> ```
|
||||
|
||||
最后,发布 image 文件。
|
||||
|
||||
> ```bash
|
||||
> $ docker image push [username]/[repository]:[tag]
|
||||
> ```
|
||||
|
||||
发布成功以后,登录 hub.docker.com,就可以看到已经发布的 image 文件。
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Docker 入门教程](https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html)
|
|
@ -1,154 +0,0 @@
|
|||
# Docker 应用指南
|
||||
|
||||
## 简介
|
||||
|
||||
### Docker 是什么
|
||||
|
||||
> **Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。**
|
||||
|
||||
Docker 是目前最流行的 Linux 容器解决方案。
|
||||
|
||||
Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。
|
||||
|
||||
总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
|
||||
|
||||
### Docker 的用途
|
||||
|
||||
Docker 的主要用途,目前有三大类。
|
||||
|
||||
- **提供一次性的环境** - 比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
|
||||
- **提供弹性的云服务** - 因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
|
||||
- **组建微服务架构** - 通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。
|
||||
|
||||
### 虚拟机和 Docker
|
||||
|
||||
虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。
|
||||
|
||||
**资源占用多** - 虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。
|
||||
|
||||
**冗余步骤多** - 虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。
|
||||
|
||||
**启动慢** - 启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/docker/containers-and-vm.png!zp"/></div><br>
|
||||
|
||||
### Docker 平台
|
||||
|
||||
Docker 提供了被称为容器的松散隔离环境,在环境中可以打包和运行应用程序。隔离和安全性允许您在给定主机上同时运行多个容器。容器是轻量级的,因为它们不需要管理程序的额外负载,而是直接在主机的内核中运行。这意味着您可以在给定的硬件组合上运行更多容器,而不是使用虚拟机。你甚至可以在实际上是虚拟机的主机中运行 Docker 容器!
|
||||
|
||||
Docker 提供工具和平台来管理容器的生命周期:
|
||||
|
||||
- 使用容器开发您的应用程序及其支持组件。
|
||||
- 容器成为分发和测试你的应用程序的单元。
|
||||
- 准备好后,将您的应用程序部署到生产环境中,作为容器或协调服务。无论您的生产环境是本地数据中心,云提供商还是两者的混合,这都是一样的。
|
||||
|
||||
## 核心概念
|
||||
|
||||
### 引擎
|
||||
|
||||
Docker 引擎是一个 C/S 架构的应用,它有这些主要的组件:
|
||||
|
||||
服务器是一个长期运行的程序,被称为守护进程。
|
||||
|
||||
REST API 指定程序可用于与守护进程进行通信并指示其执行操作的接口。
|
||||
|
||||
命令行客户端。
|
||||
|
||||
<br><div align="center"><img src="https://docs.docker.com/engine/images/engine-components-flow.png"/></div><br>
|
||||
|
||||
CLI 使用 Docker REST API 通过脚本或直接 CLI 命令来控制 Docker 守护进程或与其进行交互。许多其他 Docker 应用程序使用底层的 API 和 CLI。
|
||||
|
||||
守护进程创建并管理 Docker 对象,如镜像,容器,网络和卷。
|
||||
|
||||
### 镜像
|
||||
|
||||
我们都知道,操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系统的 root 文件系统。
|
||||
|
||||
Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
|
||||
|
||||
**分层存储**
|
||||
|
||||
因为镜像包含操作系统完整的 root 文件系统,其体积往往是庞大的,因此在 Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。所以严格来说,镜像并非是像一个 ISO 那样的打包文件,镜像只是一个虚拟的概念,其实际体现并非由一个文件组成,而是由一组文件系统组成,或者说,由多层文件系统联合组成。
|
||||
|
||||
镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
|
||||
|
||||
分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
|
||||
|
||||
### 容器
|
||||
|
||||
镜像(`Image`)和容器(`Container`)的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
|
||||
|
||||
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 [命名空间](https://en.wikipedia.org/wiki/Linux_namespaces)。因此容器可以拥有自己的 `root` 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。也因为这种隔离的特性,很多人初学 Docker 时常常会混淆容器和虚拟机。
|
||||
|
||||
前面讲过镜像使用的是分层存储,容器也是如此。每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层,我们可以称这个为容器运行时读写而准备的存储层为**容器存储层**。
|
||||
|
||||
容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。
|
||||
|
||||
按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用 [数据卷(Volume)](https://yeasy.gitbooks.io/docker_practice/content/data_management/volume.html)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
|
||||
|
||||
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
|
||||
|
||||
### 仓库
|
||||
|
||||
镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,[Docker Registry](https://yeasy.gitbooks.io/docker_practice/content/repository/registry.html) 就是这样的服务。
|
||||
|
||||
一个 **Docker Registry** 中可以包含多个**仓库**(`Repository`);每个仓库可以包含多个**标签**(`Tag`);每个标签对应一个镜像。
|
||||
|
||||
通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 `<仓库名>:<标签>` 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 `latest` 作为默认标签。
|
||||
|
||||
以 [Ubuntu 镜像](https://store.docker.com/images/ubuntu) 为例,`ubuntu` 是仓库的名字,其内包含有不同的版本标签,如,`16.04`, `18.04`。我们可以通过 `ubuntu:14.04`,或者 `ubuntu:18.04` 来具体指定所需哪个版本的镜像。如果忽略了标签,比如 `ubuntu`,那将视为 `ubuntu:latest`。
|
||||
|
||||
仓库名经常以 _两段式路径_ 形式出现,比如 `jwilder/nginx-proxy`,前者往往意味着 Docker Registry 多用户环境下的用户名,后者则往往是对应的软件名。但这并非绝对,取决于所使用的具体 Docker Registry 的软件或服务。
|
||||
|
||||
## 安装配置
|
||||
|
||||
不同操作系统环境下安装 Docker 的方式有所不同,详情可以参:
|
||||
|
||||
- [Docker 官方安装指南](https://docs.docker.com/install/)
|
||||
- [安装 Docker(中文)](https://docker_practice.gitee.io/install/)
|
||||
|
||||
国内访问 Docker 比较慢,如果需要提速,可以参考 [镜像加速器](https://docker_practice.gitee.io/install/mirror.html)
|
||||
|
||||
安装完成后,运行下面的命令,验证是否安装成功。
|
||||
|
||||
- `docker version`
|
||||
- `docker info`
|
||||
|
||||
Docker 需要用户具有 sudo 权限,为了避免每次命令都输入`sudo`,可以把用户加入 Docker 用户组([官方文档](https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user))。
|
||||
|
||||
```bash
|
||||
$ sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
Docker 是服务器----客户端架构。命令行运行`docker`命令的时候,需要本机有 Docker 服务。如果这项服务没有启动,可以用下面的命令启动([官方文档](https://docs.docker.com/config/daemon/systemd/))。
|
||||
|
||||
```bash
|
||||
# service 命令的用法
|
||||
$ sudo service docker start
|
||||
|
||||
# systemctl 命令的用法
|
||||
$ sudo systemctl start docker
|
||||
```
|
||||
|
||||
## 参考资料
|
||||
|
||||
- Docker 官方资源
|
||||
- [Docker 官网](http://www.docker.com)
|
||||
- [Docker Github](https://github.com/moby/moby)
|
||||
- [Docker 官方文档](https://docs.docker.com/)
|
||||
- [Docker Hub](https://hub.docker.com/)
|
||||
- [Docker 开源](https://www.docker.com/community/open-source)
|
||||
- 资源整理
|
||||
- [Awesome Docker](https://github.com/veggiemonk/awesome-docker)
|
||||
- Docker 中文资源
|
||||
- [Docker 中文网站](https://www.docker-cn.com/)
|
||||
- [Docker 安装手册](https://docs.docker-cn.com/engine/installation/)
|
||||
- [Docker — 从入门到实践](https://docker_practice.gitee.io/)
|
||||
- Docker 国内镜像
|
||||
- [时速云镜像仓库](https://hub.tenxcloud.com/)
|
||||
- [网易云镜像服务](https://c.163.com/hub#/m/library/)
|
||||
- [DaoCloud 镜像市场](https://hub.daocloud.io/)
|
||||
- [阿里云镜像库](https://cr.console.aliyun.com/)
|
||||
- 文章
|
||||
- [Docker 入门教程](http://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html)
|
||||
- [Docker Cheat Sheet](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn)
|
|
@ -44,7 +44,7 @@ Kubernetes 包含若干抽象用来表示系统状态,包括:已部署的容
|
|||
- Volume
|
||||
- Namespace
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/kubernetes/pod.svg!zp)<br>
|
||||
|
||||
高级对象
|
||||
|
515
docs/index.html
515
docs/index.html
|
@ -1,290 +1,291 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Linux Tutorial</title>
|
||||
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" />
|
||||
<meta content="Linux Tutorial" name="description" />
|
||||
<meta
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
name="viewport"
|
||||
/>
|
||||
<link href="http://dunwu.test.upcdn.net/common/logo/zp_50_50.png" rel="icon" type="image/x-icon" />
|
||||
<link href="//unpkg.com/docsify/lib/themes/vue.css" rel="stylesheet" title="vue" />
|
||||
<style>
|
||||
h1 + ul {
|
||||
display: block !important;
|
||||
}
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>linux-tutorial</title>
|
||||
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" />
|
||||
<meta content="linux-tutorial" name="description" />
|
||||
<meta
|
||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
||||
name="viewport"
|
||||
/>
|
||||
<link href="http://dunwu.test.upcdn.net/common/logo/zp_50_50.png" rel="icon" type="image/x-icon" />
|
||||
<link href="//unpkg.com/docsify/lib/themes/vue.css" rel="stylesheet" title="vue" />
|
||||
<style>
|
||||
h1 + ul {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.content img,
|
||||
.sidebar img {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
box-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
.content img,
|
||||
.sidebar img {
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
box-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
|
||||
body,
|
||||
.content,
|
||||
.sidebar,
|
||||
.sidebar-toggle,
|
||||
.search input {
|
||||
color: #6b615f !important;
|
||||
background-color: #fff4e6 !important;
|
||||
}
|
||||
body,
|
||||
.content,
|
||||
.sidebar,
|
||||
.sidebar-toggle,
|
||||
.search input {
|
||||
color: #6b615f !important;
|
||||
background-color: #fff4e6 !important;
|
||||
}
|
||||
|
||||
body strong,
|
||||
.content strong,
|
||||
.sidebar strong {
|
||||
color: #5c5869 !important;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.cover-main .anchor span {
|
||||
text-align: center;
|
||||
background-image: -webkit-linear-gradient(left, #ffdcb4, #b96972 25%, #e88a57 50%, #804170 75%, #a596cd);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation 1.5s infinite linear;
|
||||
font-family: 'Brush Script MT', 隶书, serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
body strong,
|
||||
.content strong,
|
||||
.sidebar strong {
|
||||
color: #5c5869 !important;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.cover-main .anchor span {
|
||||
text-align: center;
|
||||
background-image: -webkit-linear-gradient(left, #ffdcb4, #b96972 25%, #e88a57 50%, #804170 75%, #a596cd);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation 1.5s infinite linear;
|
||||
font-family: 'Brush Script MT', 隶书, serif;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cover-main blockquote p {
|
||||
color: #5c5869;
|
||||
font-family: 'Arial', 隶书, serif;
|
||||
}
|
||||
.cover-main blockquote p {
|
||||
color: #5c5869;
|
||||
font-family: 'Arial', 隶书, serif;
|
||||
}
|
||||
|
||||
.cover-main ul a:hover {
|
||||
color: #fe4165 !important;
|
||||
}
|
||||
.cover-main ul a:hover {
|
||||
color: #fe4165 !important;
|
||||
}
|
||||
|
||||
.cover-main a:hover {
|
||||
text-align: center;
|
||||
background-image: -webkit-linear-gradient(left, #ffdcb4, #b96972 25%, #e88a57 50%, #804170 75%, #a596cd);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation 1.5s infinite linear;
|
||||
}
|
||||
.cover-main a:hover {
|
||||
text-align: center;
|
||||
background-image: -webkit-linear-gradient(left, #ffdcb4, #b96972 25%, #e88a57 50%, #804170 75%, #a596cd);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation 1.5s infinite linear;
|
||||
}
|
||||
|
||||
/* content 样式内容 */
|
||||
.content a,
|
||||
.sidebar a,
|
||||
.sidebar ul li a,
|
||||
.sidebar ul li a strong {
|
||||
color: #399ab2 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
/* content 样式内容 */
|
||||
.content a,
|
||||
.sidebar a,
|
||||
.sidebar ul li a,
|
||||
.sidebar ul li a strong {
|
||||
color: #399ab2 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.content a:hover,
|
||||
.sidebar a:hover,
|
||||
.sidebar ul li a:hover,
|
||||
.sidebar ul li a strong:hover {
|
||||
color: #fe4165 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
.content a:hover,
|
||||
.sidebar a:hover,
|
||||
.sidebar ul li a:hover,
|
||||
.sidebar ul li a strong:hover {
|
||||
color: #fe4165 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
.sidebar h2 span {
|
||||
font-size: 18px;
|
||||
color: #399ab2 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.sidebar h2 span {
|
||||
font-size: 18px;
|
||||
color: #399ab2 !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.sidebar h2 span:hover {
|
||||
color: #fe4165 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
.sidebar h2 span:hover {
|
||||
color: #fe4165 !important;
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
|
||||
.sidebar .sidebar-nav {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.sidebar .sidebar-nav {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.content h1 :hover,
|
||||
.content h2 :hover,
|
||||
.content h3 :hover,
|
||||
.content h4 :hover {
|
||||
text-align: center;
|
||||
background-image: -webkit-linear-gradient(left, #ffdcb4, #b96972 25%, #e88a57 50%, #804170 75%, #a596cd);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation 1.5s infinite linear;
|
||||
font-family: '微软雅黑', serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
.content h1 :hover,
|
||||
.content h2 :hover,
|
||||
.content h3 :hover,
|
||||
.content h4 :hover {
|
||||
text-align: center;
|
||||
background-image: -webkit-linear-gradient(left, #ffdcb4, #b96972 25%, #e88a57 50%, #804170 75%, #a596cd);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-background-size: 200% 100%;
|
||||
-webkit-animation: masked-animation 1.5s infinite linear;
|
||||
font-family: '微软雅黑', serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@-webkit-keyframes masked-animation {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -100% 0;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes masked-animation {
|
||||
0% {
|
||||
background-position: 0 0;
|
||||
}
|
||||
100% {
|
||||
background-position: -100% 0;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-section h1,
|
||||
.content h1 a,
|
||||
.content h1 span {
|
||||
color: #399ab2 !important;
|
||||
font-size: 30px;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
.markdown-section h1,
|
||||
.content h1 a,
|
||||
.content h1 span {
|
||||
color: #399ab2 !important;
|
||||
font-size: 30px;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
|
||||
.content h2 a,
|
||||
.content h2 span {
|
||||
color: #60497c !important;
|
||||
font-size: 26px;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
.content h2 a,
|
||||
.content h2 span {
|
||||
color: #60497c !important;
|
||||
font-size: 26px;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
|
||||
.content h3 a,
|
||||
.content h3 span {
|
||||
color: #346093 !important;
|
||||
font-size: 22px;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
.content h3 a,
|
||||
.content h3 span {
|
||||
color: #346093 !important;
|
||||
font-size: 22px;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
|
||||
.content h4 a,
|
||||
.content h4 span {
|
||||
font-size: 18px;
|
||||
color: #78943a;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.markdown-section h1 {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
.content h4 a,
|
||||
.content h4 span {
|
||||
font-size: 18px;
|
||||
color: #78943a;
|
||||
text-shadow: 2px 2px 5px grey;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.markdown-section h1 {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown-section h2 {
|
||||
font-size: 1rem;
|
||||
margin: 1rem 0px;
|
||||
}
|
||||
.markdown-section h2 {
|
||||
font-size: 1rem;
|
||||
margin: 1rem 0px;
|
||||
}
|
||||
|
||||
.markdown-section h3 {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
.markdown-section h3 {
|
||||
font-size: 1rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown-section p {
|
||||
word-spacing: 0.05rem;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.content > p {
|
||||
font-size: 16px !important;
|
||||
line-height: 24px;
|
||||
}
|
||||
.markdown-section p {
|
||||
word-spacing: 0.05rem;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
.content > p {
|
||||
font-size: 16px !important;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.content blockquote {
|
||||
display: block;
|
||||
padding: 0 16px;
|
||||
border-left: 8px solid #dddfe4;
|
||||
background: #fff2c9;
|
||||
overflow: auto;
|
||||
}
|
||||
.content blockquote {
|
||||
display: block;
|
||||
padding: 0 16px;
|
||||
border-left: 8px solid #dddfe4;
|
||||
background: #fff2c9;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.content pre {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
border-radius: 8px;
|
||||
box-shadow: 1px 1px 20px 3px #dddddd !important;
|
||||
}
|
||||
.content pre {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
border-radius: 8px;
|
||||
box-shadow: 1px 1px 20px 3px #dddddd !important;
|
||||
}
|
||||
|
||||
.content code {
|
||||
background-color: white;
|
||||
box-shadow: 1px 1px 1px lightgrey;
|
||||
}
|
||||
.content code {
|
||||
background-color: white;
|
||||
box-shadow: 1px 1px 1px lightgrey;
|
||||
}
|
||||
|
||||
.content table {
|
||||
display: table;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
box-shadow: 2px 2px 20px 6px lightgrey !important;
|
||||
}
|
||||
.content table {
|
||||
display: table;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
box-shadow: 2px 2px 20px 6px lightgrey !important;
|
||||
}
|
||||
|
||||
.content th {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
background-color: #cce6b6;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
@media (min-width: 600px) {
|
||||
.markdown-section pre > code {
|
||||
font-size: 0.9rem !important;
|
||||
}
|
||||
}
|
||||
.content th {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
background-color: #cce6b6;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
@media (min-width: 600px) {
|
||||
.markdown-section pre > code {
|
||||
font-size: 0.9rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.markdown-section pre > code {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.markdown-section pre > code {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
pre:after {
|
||||
content: '' !important;
|
||||
}
|
||||
}
|
||||
pre:after {
|
||||
content: '' !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
pre code {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
}
|
||||
@media (min-width: 600px) {
|
||||
pre code {
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
pre {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">正在加载...</div>
|
||||
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//unpkg.com/docsify-edit-on-github/index.js"></script>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'linux-tutorial',
|
||||
repo: 'https://github.com/dunwu/linux-tutorial',
|
||||
logo: 'http://dunwu.test.upcdn.net/common/logo/zp_100_100.png',
|
||||
auto2top: true,
|
||||
coverpage: 'coverpage.md',
|
||||
loadSidebar: 'sidebar.md',
|
||||
maxLevel: 4,
|
||||
subMaxLevel: 2,
|
||||
formatUpdated: '{MM}/{DD} {HH}:{mm}',
|
||||
search: {
|
||||
maxAge: 86400000,
|
||||
paths: ['/'],
|
||||
placeholder: '🔍 搜索',
|
||||
noData: '😭 没有结果!',
|
||||
depth: 4
|
||||
},
|
||||
disqus: 'linux-tutorial',
|
||||
plugins: [
|
||||
EditOnGithubPlugin.create('https://github.com/dunwu/linux-tutorial/tree/master/docs/', null, function(file) {
|
||||
if (file.indexOf('en') === -1) {
|
||||
return '📝 编辑文档'
|
||||
} else {
|
||||
return '📝 Edit Document'
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/disqus.min.js"></script>
|
||||
<script src="//unpkg.com/docsify-edit-on-github/index.js"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||
@media (max-width: 600px) {
|
||||
pre {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">正在加载...</div>
|
||||
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
|
||||
<script src="//unpkg.com/docsify-edit-on-github/index.js"></script>
|
||||
<script>
|
||||
window.$docsify = {
|
||||
name: 'linux-tutorial',
|
||||
repo: 'https://github.com/dunwu/linux-tutorial',
|
||||
logo: 'http://dunwu.test.upcdn.net/common/logo/zp_100_100.png',
|
||||
auto2top: true,
|
||||
coverpage: 'coverpage.md',
|
||||
loadSidebar: 'sidebar.md',
|
||||
autoHeader: true,
|
||||
maxLevel: 4,
|
||||
subMaxLevel: 2,
|
||||
formatUpdated: '{MM}/{DD} {HH}:{mm}',
|
||||
search: {
|
||||
maxAge: 86400000,
|
||||
paths: ['/'],
|
||||
placeholder: '🔍 搜索',
|
||||
noData: '😭 没有结果!',
|
||||
depth: 4
|
||||
},
|
||||
disqus: 'linux-tutorial',
|
||||
plugins: [
|
||||
EditOnGithubPlugin.create('https://github.com/dunwu/linux-tutorial/tree/master/docs/', null, function(file) {
|
||||
if (file.indexOf('en') === -1) {
|
||||
return '📝 编辑文档'
|
||||
} else {
|
||||
return '📝 Edit Document'
|
||||
}
|
||||
})
|
||||
]
|
||||
}
|
||||
</script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/disqus.min.js"></script>
|
||||
<script src="//unpkg.com/docsify-edit-on-github/index.js"></script>
|
||||
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
|
||||
|
||||
<!--代码高亮-->
|
||||
<!--@see https://github.com/PrismJS/prism -->
|
||||
<script src="//unpkg.com/prismjs/components/prism-basic.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-markdown.min.js"></script>
|
||||
</body>
|
||||
<!--代码高亮-->
|
||||
<!--@see https://github.com/PrismJS/prism -->
|
||||
<script src="//unpkg.com/prismjs/components/prism-basic.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-bash.min.js"></script>
|
||||
<script src="//unpkg.com/prismjs/components/prism-markdown.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
linux 目录结构是树形结构,其根目录是 `/` 。一张思维导图说明各个目录的作用:
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/linux-folders.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/linux-folders.png!zp)<br>
|
||||
|
||||
### 1.2. Linux 文件属性
|
||||
|
||||
|
@ -35,7 +35,7 @@ dr-xr-xr-x 4 root root 4096 Apr 19 2012 boot
|
|||
|
||||
每个文件的属性由左边第一部分的 10 个字符来确定(如下图)。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20180920180927171909.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20180920180927171909.png!zp)<br>
|
||||
|
||||
从左至右用 0-9 这些数字来表示。
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](RE
|
|||
|
||||
# 命令行的艺术
|
||||
|
||||
<br><div align="center"><img src="https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"/></div><br>
|
||||
<br>![img](https://gitter.im/jlevy/the-art-of-command-line?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)<br>
|
||||
|
||||
- [前言](#前言)
|
||||
- [基础](#基础)
|
||||
|
@ -18,7 +18,7 @@ _[Čeština](README-cs.md) ∙ [Deutsch](README-de.md) ∙ [Ελληνικά](RE
|
|||
- [更多资源](#更多资源)
|
||||
- [免责声明](#免责声明)
|
||||
|
||||
<br><div align="center"><img src="https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/cowsay.png"/></div><br>
|
||||
<br>![img](https://raw.githubusercontent.com/jlevy/the-art-of-command-line/master/cowsay.png)<br>
|
||||
|
||||
熟练使用命令行是一种常常被忽视,或被认为难以掌握的技能,但实际上,它会提高你作为工程师的灵活性以及生产力。本文是一份我在 Linux 上工作时,发现的一些命令行使用技巧的摘要。有些技巧非常基础,而另一些则相当复杂,甚至晦涩难懂。这篇文章并不长,但当你能够熟练掌握这里列出的所有技巧时,你就学会了很多关于命令行的东西了。
|
||||
|
||||
|
@ -616,6 +616,6 @@ mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir
|
|||
|
||||
## 授权条款
|
||||
|
||||
<br><div align="center"><img src="http://creativecommons.org/licenses/by-sa/4.0/"/></div><br>
|
||||
<br>![img](http://creativecommons.org/licenses/by-sa/4.0/)<br>
|
||||
|
||||
本文使用授权协议 [Creative Commons Attribution-ShareAlike 4.0 International License](http://creativecommons.org/licenses/by-sa/4.0/)。
|
||||
|
|
|
@ -149,7 +149,7 @@ Windows:
|
|||
|
||||
访问:`\\<你的ip>\<你的共享路径>` :
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20180920180928161334.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20180920180928161334.png!zp)<br>
|
||||
|
||||
Mac:
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
>
|
||||
> \> 如果你认为单词是由 blank 字符分隔符,那么你需要使用大写的 E 和 W。(注:程序语句)
|
||||
>
|
||||
> <br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-46f752c581d79057.jpg"/></div><br>
|
||||
> <br>![img](http://upload-images.jianshu.io/upload_images/3101171-46f752c581d79057.jpg)<br>
|
||||
|
||||
下面,让我来说说最强的光标移动:
|
||||
|
||||
|
@ -196,7 +196,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
> - `t,` → 到逗号前的第一个字符。逗号可以变成其它字符。
|
||||
> - `3fa` → 在当前行查找第三个出现的 a。
|
||||
> - `F` 和 `T` → 和 `f` 和 `t` 一样,只不过是相反方向。
|
||||
> <br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-00835b8316330c58.jpg"/></div><br>
|
||||
> <br>![img](http://upload-images.jianshu.io/upload_images/3101171-00835b8316330c58.jpg)<br>
|
||||
|
||||
还有一个很有用的命令是 `dt"` → 删除所有的内容,直到遇到双引号—— `"。`
|
||||
|
||||
|
@ -218,7 +218,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
> - `v2i)` → 会选择 `map (+) ("foo")`
|
||||
> - `v2a)` → 会选择 `(map (+) ("foo"))`
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-0b109d66a6111c83.png"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-0b109d66a6111c83.png)<br>
|
||||
|
||||
#### 2.4.3. 块操作: `<C-v>`
|
||||
|
||||
|
@ -229,7 +229,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
- `<C-d>` → 向下移动 (你也可以使用 hjkl 来移动光标,或是使用%,或是别的)
|
||||
- `I-- [ESC]` → I 是插入,插入“`--`”,按 ESC 键来为每一行生效。
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-8b093a0f65707949.gif?imageMogr2/auto-orient/strip"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-8b093a0f65707949.gif?imageMogr2/auto-orient/strip)<br>
|
||||
|
||||
在 Windows 下的 vim,你需要使用 `<C-q>` 而不是 `<C-v>` ,`<C-v>` 是拷贝剪贴板。
|
||||
|
||||
|
@ -237,7 +237,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
|
||||
在 Insert 模式下,你可以输入一个词的开头,然后按 `<C-p>或是<C-n>,自动补齐功能就出现了……`
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-e2ae877e67880ff7.gif?imageMogr2/auto-orient/strip"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-e2ae877e67880ff7.gif?imageMogr2/auto-orient/strip)<br>
|
||||
|
||||
#### 2.4.5. 宏录制: `qa` 操作序列 `q`, `@a`, `@@`
|
||||
|
||||
|
@ -266,7 +266,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
>
|
||||
> - 现在做 `100@@` 会创建新的 100 行,并把数据增加到 103.
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-f1889f8bca723964.gif?imageMogr2/auto-orient/strip"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-f1889f8bca723964.gif?imageMogr2/auto-orient/strip)<br>
|
||||
|
||||
#### 2.4.6. 可视化选择: `v`,`V`,`<C-v>`
|
||||
|
||||
|
@ -276,7 +276,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
- `<` 或 `>` → 左右缩进
|
||||
- `=` → 自动给缩进 (注:这个功能相当强大,我太喜欢了)
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-fe1e19983fca213f.gif?imageMogr2/auto-orient/strip"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-fe1e19983fca213f.gif?imageMogr2/auto-orient/strip)<br>
|
||||
|
||||
在所有被选择的行后加上点东西:
|
||||
|
||||
|
@ -285,7 +285,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
- `$` 到行最后
|
||||
- `A`, 输入字符串,按 `ESC。`
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-b192601247334c4e.gif?imageMogr2/auto-orient/strip"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-b192601247334c4e.gif?imageMogr2/auto-orient/strip)<br>
|
||||
|
||||
#### 2.4.7. 分屏: `:split` 和 `vsplit`.
|
||||
|
||||
|
@ -296,7 +296,7 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
> - `<C-w>_` (或 `<C-w>|`) : 最大化尺寸 (<C-w>| 垂直分屏)
|
||||
> - `<C-w>+` (或 `<C-w>-`) : 增加尺寸
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-f329d01e299cb366.gif?imageMogr2/auto-orient/strip"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-f329d01e299cb366.gif?imageMogr2/auto-orient/strip)<br>
|
||||
|
||||
## 3. Vim Cheat Sheet
|
||||
|
||||
|
@ -308,33 +308,33 @@ Vim 是从 vi 发展出来的一个文本编辑器。代码补完、编译及错
|
|||
|
||||
此外,[这里](http://blog.ngedit.com/vi-vim-cheat-sheet-sch.gif)还有简体中文版。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet.png!zp)<br>
|
||||
|
||||
### 3.2. 入门版
|
||||
|
||||
基本操作的入门版。[原版出处](https://github.com/ahrencode/Miscellaneous)还有 keynote 版本可供 DIY 以及其他相关有用的 cheatsheet。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/vim/basic-vim-cheat-sheet.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/basic-vim-cheat-sheet.png!zp)<br>
|
||||
|
||||
### 3.3. 进阶版
|
||||
|
||||
下图是 300DPI 的超清大图,另外[查看原文](http://michael.peopleofhonoronly.com/vim/)还有更多版本:黑白,低分辨率,色盲等
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-for-programmers.png!zp)<br>
|
||||
|
||||
### 3.4. 增强版
|
||||
|
||||
下图是一个更新时间较新的现代版,含有的信息也更丰富。[原文链接](http://vimcheatsheet.com/)
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-02.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-02.png!zp)<br>
|
||||
|
||||
### 3.5. 文字版
|
||||
|
||||
[原文链接](http://tnerual.eriogerg.free.fr/vimqrc.pdf)
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-01.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-01.png!zp)<br>
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-02.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/cs/os/linux/vim/vim-cheat-sheet-text-02.png!zp)<br>
|
||||
|
||||
## 4. 资料
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ wget https://github.com/robbyrussell/oh-my-zsh/raw/master/tools/install.sh -O -
|
|||
|
||||
zsh 效果如下:
|
||||
|
||||
<div align="center"><img src="https://cloud.githubusercontent.com/assets/2618447/6316862/70f58fb6-ba03-11e4-82c9-c083bf9a6574.png"/></div>
|
||||
![img](https://cloud.githubusercontent.com/assets/2618447/6316862/70f58fb6-ba03-11e4-82c9-c083bf9a6574.png)
|
||||
## 3. 快捷键
|
||||
|
||||
- 呃,这个其实可以不用讲的,你自己用的时候你自己会发现的,各种便捷,特别是用 Tab 多的人一定会有各种惊喜的。
|
||||
|
|
|
@ -18,7 +18,7 @@ Beats 是安装在服务器上的数据中转代理。
|
|||
|
||||
Beats 可以将数据直接传输到 Elasticsearch 或传输到 Logstash 。
|
||||
|
||||
<br><div align="center"><img src="https://www.elastic.co/guide/en/beats/libbeat/current/images/beats-platform.png"/></div><br>
|
||||
<br>![img](https://www.elastic.co/guide/en/beats/libbeat/current/images/beats-platform.png)<br>
|
||||
|
||||
Beats 有多种类型,可以根据实际应用需要选择合适的类型。
|
||||
|
||||
|
@ -47,7 +47,7 @@ Filebeat带有内部模块(auditd,Apache,Nginx,System和MySQL),可
|
|||
|
||||
FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。
|
||||
|
||||
<br><div align="center"><img src="https://www.elastic.co/guide/en/beats/filebeat/current/images/filebeat.png"/></div><br>
|
||||
<br>![img](https://www.elastic.co/guide/en/beats/filebeat/current/images/filebeat.png)<br>
|
||||
|
||||
## 安装
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
单击侧面导航栏中的 `Discover` ,可以显示 `Kibana` 的数据查询功能功能。
|
||||
|
||||
<br><div align="center"><img src="https://www.elastic.co/guide/en/kibana/current/images/tutorial-discover.png"/></div><br>
|
||||
<br>![img](https://www.elastic.co/guide/en/kibana/current/images/tutorial-discover.png)<br>
|
||||
|
||||
在搜索栏中,您可以输入Elasticsearch查询条件来搜索您的数据。您可以在 `Discover` 页面中浏览结果并在 `Visualize` 页面中创建已保存搜索条件的可视化。
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
|||
|
||||
默认情况下,每个匹配文档都显示所有字段。要选择要显示的文档字段,请将鼠标悬停在“可用字段”列表上,然后单击要包含的每个字段旁边的添加按钮。例如,如果只添加account_number,则显示将更改为包含五个帐号的简单列表:
|
||||
|
||||
<br><div align="center"><img src="https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-discover-3.png"/></div><br>
|
||||
<br>![img](https://www.elastic.co/guide/en/kibana/6.1/images/tutorial-discover-3.png)<br>
|
||||
|
||||
### 查询语义
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ Logstash 有两个必要元素:`input` 和 `output` ,一个可选元素:`f
|
|||
|
||||
这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出。
|
||||
|
||||
<br><div align="center"><img src="https://www.elastic.co/guide/en/logstash/current/static/images/basic_logstash_pipeline.png"/></div><br>
|
||||
<br>![img](https://www.elastic.co/guide/en/logstash/current/static/images/basic_logstash_pipeline.png)<br>
|
||||
|
||||
- input 负责从数据源采集数据。
|
||||
- filter 将数据修改为你指定的格式或内容。
|
||||
|
|
|
@ -32,7 +32,7 @@ ELK 是 elastic 公司旗下三款产品 [ElasticSearch](https://www.elastic.co/
|
|||
|
||||
### Elastic 架构
|
||||
|
||||
<br><div align="center"><img src="https://www.elastic.co/guide/en/logstash/current/static/images/deploy3.png"/></div><br>
|
||||
<br>![img](https://www.elastic.co/guide/en/logstash/current/static/images/deploy3.png)<br>
|
||||
|
||||
> **说明**
|
||||
>
|
||||
|
@ -274,7 +274,7 @@ output {
|
|||
|
||||
大功告成,此后,`io.github.dunwu.spring` 包中的 TRACE 及以上级别的日志信息都会被定向输出到 logstash 服务。
|
||||
|
||||
<br><div align="center"><img src="http://upload-images.jianshu.io/upload_images/3101171-cd876d79a14955b0.png"/></div><br>
|
||||
<br>![img](http://upload-images.jianshu.io/upload_images/3101171-cd876d79a14955b0.png)<br>
|
||||
|
||||
## 资料
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ storage 接受到写文件请求时,会根据配置好的规则(后面会介
|
|||
|
||||
**meta data** :文件相关属性,键值对( Key Value Pair) 方式,如:width=1024,heigth=768 。
|
||||
|
||||
<div align="center"><img src="http://www.ityouknow.com/assets/images/2018/fastdfs/fastdfs_arch.png"/></div>
|
||||
![img](http://www.ityouknow.com/assets/images/2018/fastdfs/fastdfs_arch.png)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
进入官方下载地址:https://about.gitlab.com/install/ ,如下图,选择合适的版本。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20190129155838.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20190129155838.png!zp)<br>
|
||||
|
||||
以 CentOS7 为例:
|
||||
|
||||
|
@ -87,7 +87,7 @@ docker run -d \
|
|||
gitlab/gitlab-ce
|
||||
```
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20190131150515.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20190131150515.png!zp)<br>
|
||||
|
||||
## 安装 gitlab-ci-multi-runner
|
||||
|
||||
|
@ -138,7 +138,7 @@ sudo gitlab-runner register
|
|||
|
||||
URL 和令牌信息在 Gitlab 的 Runner 管理页面获取:
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20190129163100.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20190129163100.png!zp)<br>
|
||||
|
||||
```
|
||||
Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com )
|
||||
|
@ -302,31 +302,31 @@ sudo gitlab-ctl restart
|
|||
|
||||
3. 打开 **Profile settings**.
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings.png)<br>
|
||||
|
||||
4. 跳转到 **SSH keys** tab 页
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys.png)<br>
|
||||
|
||||
5. 黏贴你的 SSH 公钥内容到 Key 文本框
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png)<br>
|
||||
|
||||
6. 为了便于识别,你可以为其命名
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_title.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_title.png)<br>
|
||||
|
||||
7. 点击 **Add key** 将 SSH 公钥添加到 GitLab
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_single_key.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/gitlab-basics/img/profile_settings_ssh_keys_single_key.png)<br>
|
||||
|
||||
### 创建项目
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20190131150658.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20190131150658.png!zp)<br>
|
||||
|
||||
输入项目信息,点击 Create project 按钮,在 Gitlab 创建项目。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20190131150759.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20190131150759.png!zp)<br>
|
||||
|
||||
### 克隆项目到本地
|
||||
|
||||
|
@ -338,23 +338,23 @@ sudo gitlab-ctl restart
|
|||
|
||||
依次点击 **Project’s Dashboard** > **Issues** > **New Issue** 可以新建 Issue
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_tracker_list.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_tracker_list.png)<br>
|
||||
|
||||
在项目中直接添加 issue
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/user/project/issues/img/new_issue.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue.png)<br>
|
||||
|
||||
在未关闭 issue 中,点击 **New Issue** 添加 issue
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_open_issue.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_open_issue.png)<br>
|
||||
|
||||
通过项目面板添加 issue
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_projects_dashboard.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_projects_dashboard.png)<br>
|
||||
|
||||
通过 issue 面板添加 issue
|
||||
|
||||
<br><div align="center"><img src="https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png"/></div><br>
|
||||
<br>![img](https://docs.gitlab.com/ce/user/project/issues/img/new_issue_from_issue_board.png)<br>
|
||||
|
||||
## 更多内容
|
||||
|
||||
|
|
|
@ -22,13 +22,13 @@ a. 进入 [Java 官网下载页面](https://www.oracle.com/technetwork/java/java
|
|||
|
||||
b. 选择需要的版本:
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20180920181010164121.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20180920181010164121.png!zp)<br>
|
||||
|
||||
c. 选择对应操作系统的安装包:
|
||||
|
||||
Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系统选择 tar.gz 压缩包(RedHat 发行版可以安装 rpm 包)。
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20180920181010164308.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20180920181010164308.png!zp)<br>
|
||||
|
||||
(2)运行安装包,按提示逐步安装
|
||||
|
||||
|
@ -51,11 +51,11 @@ Windows 系统选择 exe 安装包;Mac 系统选择 dmp 安装包;Linux 系
|
|||
|
||||
a. 安装完成后,右击"我的电脑",点击"属性",选择"高级系统设置";
|
||||
|
||||
<br><div align="center"><img src="https://www.runoob.com/wp-content/uploads/2013/12/win-java1.png"/></div><br>
|
||||
<br>![img](https://www.runoob.com/wp-content/uploads/2013/12/win-java1.png)<br>
|
||||
|
||||
b. 选择"高级"选项卡,点击"环境变量";
|
||||
|
||||
<br><div align="center"><img src="https://www.runoob.com/wp-content/uploads/2013/12/java-win2.png"/></div><br>
|
||||
<br>![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win2.png)<br>
|
||||
|
||||
然后就会出现如下图所示的画面:
|
||||
|
||||
|
@ -78,7 +78,7 @@ a. "开始"->"运行",键入"cmd";
|
|||
|
||||
b. 键入命令: **java -version**、**java**、**javac** 几个命令,出现以下信息,说明环境变量配置成功;
|
||||
|
||||
<br><div align="center"><img src="https://www.runoob.com/wp-content/uploads/2013/12/java-win9.png"/></div><br>
|
||||
<br>![img](https://www.runoob.com/wp-content/uploads/2013/12/java-win9.png)<br>
|
||||
|
||||
## Linux 系统安装方法
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
进入[官方下载地址](https://www.sonatype.com/download-oss-sonatype),选择合适版本下载。
|
||||
|
||||
<div align="center"><img src="http://dunwu.test.upcdn.net/snap/20181127203029.png!zp"/></div>
|
||||
![img](http://dunwu.test.upcdn.net/snap/20181127203029.png!zp)
|
||||
|
||||
本人希望将 Nexus 部署在 Linux 机器,所以选用的是 Unix 版本。
|
||||
|
||||
|
@ -66,7 +66,7 @@ Usage: ./nexus {start|stop|run|run-redirect|status|restart|force-reload}
|
|||
|
||||
启动成功后,在浏览器中访问 `http://<ip>:8081`,欢迎页面如下图所示:
|
||||
|
||||
<div align="center"><img src="http://dunwu.test.upcdn.net/snap/20181127203131.png!zp"/></div>
|
||||
![img](http://dunwu.test.upcdn.net/snap/20181127203131.png!zp)
|
||||
|
||||
点击右上角 Sign in 登录,默认用户名/密码为:admin/admin123。
|
||||
|
||||
|
@ -81,7 +81,7 @@ Nexus 中的仓库有以下类型:
|
|||
- `virtual` - 虚拟仓库。用于适配 Maven 1;
|
||||
- `group` - 仓库组。Nexus 通过仓库组的概念统一管理多个仓库,这样我们在项目中直接请求仓库组即可请求到仓库组管理的多个仓库。
|
||||
|
||||
<div align="center"><img src="http://dunwu.test.upcdn.net/cs/java/javalib/maven/nexus.png!zp"/></div>
|
||||
![img](http://dunwu.test.upcdn.net/cs/java/javalib/maven/nexus.png!zp)
|
||||
|
||||
> **最佳实践**
|
||||
>
|
||||
|
@ -96,7 +96,7 @@ Nexus 中的仓库有以下类型:
|
|||
> - group 仓库
|
||||
> - maven-public - 私有仓库的公共空间,把上面三个仓库组合在一起对外提供服务,在本地 maven 基础配置 settings.xml 中使用。
|
||||
|
||||
<div align="center"><img src="http://dunwu.test.upcdn.net/snap/20181127203156.png!zp"/></div>
|
||||
![img](http://dunwu.test.upcdn.net/snap/20181127203156.png!zp)
|
||||
|
||||
### 3.2. 配置 settings.xml
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ $ vi /etc/sysconfig/svnserve
|
|||
|
||||
在新的窗口,输入地址 `svn://<你的 IP>` 即可,不出意外输入用户名和密码就能连接成功了(这里的用户、密码必须在 passwd 配置文件的清单中)。默认端口 3690,如果你修改了端口,那么要记得加上端口号。如下图所示:
|
||||
|
||||
<br><div align="center"><img src="http://dunwu.test.upcdn.net/snap/20190129175443.png!zp"/></div><br>
|
||||
<br>![img](http://dunwu.test.upcdn.net/snap/20190129175443.png!zp)<br>
|
||||
|
||||
## 2. 参考资料
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
>
|
||||
> 本文目的在于记录 svn 的安装、配置、使用。
|
||||
|
||||
<div align="center"><img src="http://dunwu.test.upcdn.net/snap/1562814562978.png!zp"/></div>
|
||||
![img](http://dunwu.test.upcdn.net/snap/1562814562978.png!zp)
|
||||
|
||||
<!-- TOC depthFrom:2 depthTo:3 -->
|
||||
|
||||
|
|
|
@ -1,7 +1,33 @@
|
|||
{
|
||||
"name": "linux-tutorial",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"start": "docsify serve ./ --port 4000"
|
||||
}
|
||||
"name": "linux-tutorial",
|
||||
"author": "Zhang Peng",
|
||||
"homepage": "http://dunwu.github.io/linux-tutorial",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:dunwu/linux-tutorial.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "docsify serve ./ --port 4000",
|
||||
"clean": "rimraf _book",
|
||||
"install": "gitbook install",
|
||||
"serve": "gitbook serve",
|
||||
"build": "npm run clean & gitbook build",
|
||||
"pdf": "gitbook pdf ."
|
||||
},
|
||||
"dependencies": {
|
||||
"gitbook-plugin-advanced-emoji": "^0.2.2",
|
||||
"gitbook-plugin-anchor-navigation-ex": "^1.0.10",
|
||||
"gitbook-plugin-anchors": "^0.7.1",
|
||||
"gitbook-plugin-edit-link": "^2.0.2",
|
||||
"gitbook-plugin-expandable-chapters-small": "^0.1.7",
|
||||
"gitbook-plugin-github": "^2.0.0",
|
||||
"gitbook-plugin-search-plus": "0.0.11",
|
||||
"gitbook-plugin-simple-page-toc": "^0.1.2",
|
||||
"gitbook-plugin-splitter": "0.0.8",
|
||||
"gitbook-plugin-tbfed-pagefooter": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gh-pages": "^2.1.1",
|
||||
"rimraf": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,49 @@
|
|||
- **文章**
|
||||
- [**Linux 命令**](linux/cli/README.md)
|
||||
- [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md)
|
||||
- [Linux 文件目录管理](linux/cli/Linux文件目录管理.md)
|
||||
- [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md)
|
||||
- [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md)
|
||||
- [Linux 用户管理](linux/cli/Linux用户管理.md)
|
||||
- [Linux 系统管理](linux/cli/Linux系统管理.md)
|
||||
- [Linux 网络管理](linux/cli/Linux网络管理.md)
|
||||
- [Linux 硬件管理](linux/cli/Linux硬件管理.md)
|
||||
- [Linux 软件管理](linux/cli/Linux硬件管理.md)
|
||||
- [**Linux 系统运维**](linux/ops/README.md)
|
||||
- [linux 典型运维应用](linux/ops/linux典型运维应用.md)
|
||||
- [samba 使用详解](linux/ops/samba.md)
|
||||
- [Systemd 教程](linux/ops/systemd.md)
|
||||
- [Vim 应用指南](linux/ops/vim.md)
|
||||
- [Zsh 应用指南](linux/ops/zsh.md)
|
||||
- [**软件运维**](linux/soft/README.md)
|
||||
- 开发环境
|
||||
- [JDK 安装](linux/soft/jdk-install.md)
|
||||
- [Maven 安装](linux/soft/maven-install.md)
|
||||
- [Nodejs 安装](linux/soft/nodejs-install.md)
|
||||
- 开发工具
|
||||
- [Nexus 运维](linux/soft/nexus-ops.md)
|
||||
- [Gitlab 运维](linux/soft/kafka-install.md)
|
||||
- [Jenkins 运维](linux/soft/jenkins.md)
|
||||
- [Svn 运维](linux/soft/svn-ops.md)
|
||||
- [YApi 运维](linux/soft/yapi-ops.md)
|
||||
- 中间件服务
|
||||
- [Elastic 运维](linux/soft/elastic)
|
||||
- [Kafka 运维](linux/soft/kafka-install.md)
|
||||
- [RocketMQ 运维](linux/soft/rocketmq-install.md)
|
||||
- [Nacos 运维](linux/soft/nacos-install.md)
|
||||
- [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md)
|
||||
- 服务器
|
||||
- [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial)
|
||||
- [Tomcat 运维](linux/soft/tomcat-install.md)
|
||||
- [数据库 📚](https://github.com/dunwu/db-tutorial)
|
||||
- [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md)
|
||||
- [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md)
|
||||
- **扩展**
|
||||
- [Docker 教程](docker)
|
||||
- [Docker 应用指南](docker/docker.md)
|
||||
- [Docker Cheat Sheet](docker/docker-cheat-sheet.md)
|
||||
- [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md)
|
||||
- [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md)
|
||||
- [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md)
|
||||
- **脚本**
|
||||
- [**Shell 脚本大全**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys)
|
||||
- [**CentOS 常规操作运维脚本集合**](https://github.com/dunwu/linux-tutorial/tree/master/codes/linux/sys)
|
||||
# 【目录】
|
||||
|
||||
- [**Linux 命令**](linux/cli/README.md)
|
||||
- [查看 Linux 命令帮助信息](linux/cli/查看Linux命令帮助信息.md)
|
||||
- [Linux 文件目录管理](linux/cli/Linux文件目录管理.md)
|
||||
- [Linux 文件内容查看命令](linux/cli/Linux文件内容查看编辑.md)
|
||||
- [Linux 文件压缩和解压](linux/cli/Linux文件压缩和解压.md)
|
||||
- [Linux 用户管理](linux/cli/Linux用户管理.md)
|
||||
- [Linux 系统管理](linux/cli/Linux系统管理.md)
|
||||
- [Linux 网络管理](linux/cli/Linux网络管理.md)
|
||||
- [Linux 硬件管理](linux/cli/Linux硬件管理.md)
|
||||
- [Linux 软件管理](linux/cli/Linux硬件管理.md)
|
||||
- [**Linux 系统运维**](linux/ops/README.md)
|
||||
- [linux 典型运维应用](linux/ops/linux典型运维应用.md)
|
||||
- [samba 使用详解](linux/ops/samba.md)
|
||||
- [Systemd 教程](linux/ops/systemd.md)
|
||||
- [Vim 应用指南](linux/ops/vim.md)
|
||||
- [Zsh 应用指南](linux/ops/zsh.md)
|
||||
- [**软件运维**](linux/soft/README.md)
|
||||
- 开发环境
|
||||
- [JDK 安装](linux/soft/jdk-install.md)
|
||||
- [Maven 安装](linux/soft/maven-install.md)
|
||||
- [Nodejs 安装](linux/soft/nodejs-install.md)
|
||||
- 开发工具
|
||||
- [Nexus 运维](linux/soft/nexus-ops.md)
|
||||
- [Gitlab 运维](linux/soft/kafka-install.md)
|
||||
- [Jenkins 运维](linux/soft/jenkins.md)
|
||||
- [Svn 运维](linux/soft/svn-ops.md)
|
||||
- [YApi 运维](linux/soft/yapi-ops.md)
|
||||
- 中间件服务
|
||||
- [Elastic 运维](linux/soft/elastic/README.md)
|
||||
- [Kafka 运维](linux/soft/kafka-install.md)
|
||||
- [RocketMQ 运维](linux/soft/rocketmq-install.md)
|
||||
- [Nacos 运维](linux/soft/nacos-install.md)
|
||||
- [Zookeeper 运维](https://github.com/dunwu/javaweb/blob/master/docs/technology/monitor/zookeeper-ops.md)
|
||||
- 服务器
|
||||
- [Nginx 教程 📚](https://github.com/dunwu/nginx-tutorial)
|
||||
- [Tomcat 运维](linux/soft/tomcat-install.md)
|
||||
- [数据库 📚](https://github.com/dunwu/db-tutorial)
|
||||
- [Mysql 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/sql/mysql/mysql-ops.md)
|
||||
- [Redis 运维](https://github.com/dunwu/db-tutorial/blob/master/docs/nosql/redis/redis-ops.md)
|
||||
- **扩展**
|
||||
- [Docker 教程](docker/README.md)
|
||||
- [Docker 快速入门](docker/docker-quickstart.md)
|
||||
- [Dockerfile 最佳实践](docker/docker-dockerfile.md)
|
||||
- [Docker Cheat Sheet](docker/docker-cheat-sheet.md)
|
||||
- [一篇文章让你彻底掌握 Python](https://github.com/dunwu/blog/blob/master/source/_posts/coding/python.md)
|
||||
- [一篇文章让你彻底掌握 Shell](https://github.com/dunwu/blog/blob/master/source/_posts/coding/shell.md)
|
||||
- [Git 从入门到精通](https://github.com/dunwu/blog/blob/master/source/_posts/tools/git.md)
|
||||
|
|
Loading…
Reference in New Issue