This commit is contained in:
Baohua Yang
2026-01-30 16:28:20 -08:00
parent 8a0230e493
commit c58f61dbed
21 changed files with 4 additions and 669 deletions

View File

@@ -1,15 +0,0 @@
kind: pipeline
type: docker
name: build
steps:
- name: build
image: yeasy/docker_practice:latest
pull: if-not-exists # always never
environment:
TZ: Asia/Shanghai
commands:
- docker-entrypoint.sh build
trigger:
branch:
- master

View File

@@ -1,26 +0,0 @@
FROM node:14.4.0-alpine
ENV TZ=Asia/Shanghai
WORKDIR /srv/gitbook
COPY book.json book.json
COPY docker-entrypoint.sh /usr/local/bin/
RUN set -x && apk add --no-cache \
tzdata bash \
&& npm install -g gitbook-cli \
&& gitbook install \
&& ln -s /usr/local/bin/docker-entrypoint.sh / \
&& rm -rf /root/.npm /tmp/*
EXPOSE 4000
VOLUME /srv/gitbook-src
WORKDIR /srv/gitbook-src
ENTRYPOINT ["docker-entrypoint.sh"]
CMD server

View File

@@ -1,37 +0,0 @@
{
"title": "Docker -- 从入门到实践",
"author": "yeasy",
"language": "zh-hans",
"links": {
"sidebar": {
"GitHub": "https://github.com/yeasy/docker_practice"
}
},
"plugins": [
"-livereload",
"image-captions",
"github",
"page-treeview@2.9.8",
"editlink"
],
"pluginsConfig": {
"image-captions": {
"attributes": {
"width": "600"
},
"caption": "图 _PAGE_LEVEL_._PAGE_IMAGE_NUMBER_ - _CAPTION_"
},
"github": {
"url": "https://github.com/yeasy/docker_practice"
},
"editlink": {
"base": "https://github.com/yeasy/docker_practice/blob/master/",
"label": "编辑本页"
},
"page-treeview": {
"copyright": "Copyright © yeasy",
"minHeaderCount": "2",
"minHeaderDeep": "2"
}
}
}

View File

@@ -1,43 +0,0 @@
user root;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
index index.html index.php;
server {
server_name localhost;
listen 4000;
root /srv/www/;
index index.html;
}
}

View File

@@ -1,5 +0,0 @@
sut:
build: .
volumes:
- ../:/srv/gitbook-src
command: build

View File

@@ -1,23 +0,0 @@
#!/bin/sh
START=`date "+%F %T"`
if [ $1 = "sh" ];then sh ; exit 0; fi
rm -rf node_modules _book
srcDir=$PWD
cp -a . /srv/gitbook
cd /srv/gitbook
main(){
if [ "$1" = build ];then
gitbook build && cp -a _book $srcDir && echo $START && date "+%F %T" && exit 0
else
exec gitbook serve
fi
}
main $1 $2 $3

View File

@@ -1,8 +0,0 @@
#!/usr/bin/env bash
# cd .travis
# ./update.sh
if [ ! -f Dockerfile ];then exit 1; fi
cp -a ../book.json book.json

View File

@@ -79,14 +79,7 @@
* [实战 Rails](compose/rails.md)
* [实战 WordPress](compose/wordpress.md)
* [实战 LNMP](compose/lnmp.md)
* [Swarm mode](swarm_mode/README.md)
* [基本概念](swarm_mode/overview.md)
* [创建 Swarm 集群](swarm_mode/create.md)
* [部署服务](swarm_mode/deploy.md)
* [使用 compose 文件](swarm_mode/stack.md)
* [管理密钥](swarm_mode/secret.md)
* [管理配置信息](swarm_mode/config.md)
* [滚动升级](swarm_mode/rolling_update.md)
* [安全](security/README.md)
* [内核命名空间](security/kernel_ns.md)
* [控制组](security/control_group.md)

View File

@@ -1,7 +0,0 @@
# 归档项目
以下项目不被官方支持或内容陈旧将在下一版本中删除或已经删除
* [Docker Machine](https://github.com/yeasy/docker_practice/tree/ca29ab51b121f43563f5d6659dedbda5cb6f048d/machine)
* [Docker Swarm](https://github.com/yeasy/docker_practice/tree/ca29ab51b121f43563f5d6659dedbda5cb6f048d/swarm)
* Mesos

View File

@@ -90,7 +90,7 @@ command: echo "hello world"
## `configs`
仅用于 `Swarm mode`详细内容请查看 [`Swarm mode`](../swarm_mode/) 一节
仅用于 `Swarm mode`已弃用推荐使用 Kubernetes
## `cgroup_parent`
@@ -114,7 +114,7 @@ container_name: docker-web-container
## `deploy`
仅用于 `Swarm mode`详细内容请查看 [`Swarm mode`](../swarm_mode/) 一节
仅用于 `Swarm mode`已弃用推荐使用 Kubernetes
## `devices`

View File

@@ -165,7 +165,7 @@ $ docker run -dit --rm --network=laravel -p 8080:80 my/nginx
## 生产环境优化
本小节内容为了方便测试将配置文件直接放到了镜像中实际在使用时 **建议** 将配置文件作为 `config` `secret` 挂载到容器中请读者自行学习 `Swarm mode` `Kubernetes` 的相关内容
本小节内容为了方便测试将配置文件直接放到了镜像中实际在使用时 **建议** 将配置文件作为 `config` `secret` 挂载到容器中请读者自行学习 `Kubernetes` 的相关内容
由于篇幅所限本小节只是简单列出更多内容可以参考 https://github.com/khs1994-docker/laravel-demo 项目。

View File

@@ -1,7 +0,0 @@
# Swarm mode
Docker 1.12 [Swarm mode](https://docs.docker.com/engine/swarm/) 已经内嵌入 Docker 引擎,成为了 docker 子命令 `docker swarm`。请注意与旧的 `Docker Swarm` 区分开来。
`Swarm mode` 内置 kv 存储功能提供了众多的新特性比如具有容错能力的去中心化设计内置服务发现负载均衡路由网格动态伸缩滚动更新安全传输等
> **定位说明**Swarm mode 适合小型团队和简单的容器编排场景具有学习成本低配置简单的优势对于大规模生产部署复杂的微服务架构建议使用 [Kubernetes](../kubernetes/README.md)它拥有更丰富的生态系统和更强大的扩展能力

View File

@@ -1,54 +0,0 @@
# Swarm 集群中管理配置数据
在动态的大规模的分布式集群上管理和分发配置文件也是很重要的工作传统的配置文件分发方式如配置文件放入镜像中设置环境变量volume 动态挂载等都降低了镜像的通用性
Docker 17.06 以上版本中Docker 新增了 `docker config` 子命令来管理集群中的配置信息以后你无需将配置文件放入镜像或挂载到容器中就可实现对服务的配置
>注意`config` 仅能在 Swarm 集群中使用
这里我们以在 Swarm 集群中部署 `redis` 服务为例
## 创建 config
新建 `redis.conf` 文件
```bash
port 6380
```
此项配置 Redis 监听 `6380` 端口
我们使用 `docker config create` 命令创建 `config`
```bash
$ docker config create redis.conf redis.conf
```
## 查看 config
使用 `docker config ls` 命令来查看 `config`
```bash
$ docker config ls
ID NAME CREATED UPDATED
yod8fx8iiqtoo84jgwadp86yk redis.conf 4 seconds ago 4 seconds ago
```
## 创建 redis 服务
```bash
$ docker service create \
--name redis \
# --config source=redis.conf,target=/etc/redis.conf \
--config redis.conf \
-p 6379:6380 \
redis:latest \
redis-server /redis.conf
```
如果你没有在 `target` 中显式的指定路径时默认的 `redis.conf` `tmpfs` 文件系统挂载到容器的 `/config.conf`
经过测试redis 可以正常使用
以前我们通过监听主机目录来配置 Redis就需要在集群的每个节点放置该文件如果采用 `docker config` 来管理服务的配置信息我们只需在集群中的管理节点创建 `config`当部署服务时集群会自动的将配置文件分发到运行服务的各个节点中大大降低了配置信息的管理和分发难度

View File

@@ -1,50 +0,0 @@
# 创建 Swarm 集群
阅读 [基本概念](overview.md) 一节我们知道 `Swarm` 集群由 **管理节点** **工作节点** 组成本节我们来创建一个包含一个管理节点和两个工作节点的最小 `Swarm` 集群
## 初始化集群
在已经安装好 Docker 的主机上执行如下命令
```bash
$ docker swarm init --advertise-addr 192.168.99.100
Swarm initialized: current node (dxn1zf6l61qsb1josjja83ngz) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join \
--token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
192.168.99.100:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
```
如果你的 Docker 主机有多个网卡拥有多个 IP必须使用 `--advertise-addr` 指定 IP
> 执行 `docker swarm init` 命令的节点自动成为管理节点
## 增加工作节点
上一步我们初始化了一个 `Swarm` 集群拥有了一个管理节点下面我们继续在两个 Docker 主机中分别执行如下命令创建工作节点并加入到集群中
```bash
$ docker swarm join \
--token SWMTKN-1-49nj1cmql0jkz5s954yi3oex3nedyz0fb0xx14ie39trti4wxv-8vxv8rssmk743ojnwacrr2e7c \
192.168.99.100:2377
This node joined a swarm as a worker.
```
## 查看集群
经过上边的两步我们已经拥有了一个最小的 `Swarm` 集群包含一个管理节点和两个工作节点
在管理节点使用 `docker node ls` 查看集群
```bash
$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
03g1y59jwfg7cf99w4lt0f662 worker2 Ready Active
9j68exjopxe7wfl6yuxml7a7j worker1 Ready Active
dxn1zf6l61qsb1josjja83ngz * manager Ready Active Leader
```

View File

@@ -1,47 +0,0 @@
services:
wordpress:
image: wordpress
ports:
- 80:80
networks:
- overlay
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
deploy:
mode: replicated
replicas: 3
db:
image: mysql
networks:
- overlay
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
deploy:
placement:
constraints: [node.role == manager]
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
volumes:
db-data:
networks:
overlay:

View File

@@ -1,69 +0,0 @@
# 部署服务
我们使用 `docker service` 命令来管理 `Swarm` 集群中的服务该命令只能在管理节点运行
## 新建服务
现在我们在上一节创建的 `Swarm` 集群中运行一个名为 `nginx` 服务
```bash
$ docker service create --replicas 3 -p 80:80 --name nginx nginx:1.13.7-alpine
```
现在我们使用浏览器输入任意节点 IP 即可看到 nginx 默认页面
## 查看服务
使用 `docker service ls` 来查看当前 `Swarm` 集群运行的服务
```bash
$ docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
kc57xffvhul5 nginx replicated 3/3 nginx:1.13.7-alpine *:80->80/tcp
```
使用 `docker service ps` 来查看某个服务的详情
```bash
$ docker service ps nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
pjfzd39buzlt nginx.1 nginx:1.13.7-alpine swarm2 Running Running about a minute ago
hy9eeivdxlaa nginx.2 nginx:1.13.7-alpine swarm1 Running Running about a minute ago
36wmpiv7gmfo nginx.3 nginx:1.13.7-alpine swarm3 Running Running about a minute ago
```
使用 `docker service logs` 来查看某个服务的日志
```bash
$ docker service logs nginx
nginx.3.36wmpiv7gmfo@swarm3 | 10.255.0.4 - - [25/Nov/2017:02:10:30 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.3.36wmpiv7gmfo@swarm3 | 10.255.0.4 - - [25/Nov/2017:02:10:30 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.3.36wmpiv7gmfo@swarm3 | 2017/11/25 02:10:30 [error] 5#5: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.255.0.4, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.99.102"
nginx.1.pjfzd39buzlt@swarm2 | 10.255.0.2 - - [25/Nov/2017:02:10:26 +0000] "GET / HTTP/1.1" 200 612 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.1.pjfzd39buzlt@swarm2 | 10.255.0.2 - - [25/Nov/2017:02:10:27 +0000] "GET /favicon.ico HTTP/1.1" 404 169 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0" "-"
nginx.1.pjfzd39buzlt@swarm2 | 2017/11/25 02:10:27 [error] 5#5: *1 open() "/usr/share/nginx/html/favicon.ico" failed (2: No such file or directory), client: 10.255.0.2, server: localhost, request: "GET /favicon.ico HTTP/1.1", host: "192.168.99.101"
```
## 服务伸缩
我们可以使用 `docker service scale` 对一个服务运行的容器数量进行伸缩
当业务处于高峰期时我们需要扩展服务运行的容器数量
```bash
$ docker service scale nginx=5
```
当业务平稳时我们需要减少服务运行的容器数量
```bash
$ docker service scale nginx=2
```
## 删除服务
使用 `docker service rm` 来从 `Swarm` 集群移除某个服务
```bash
$ docker service rm nginx
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

View File

@@ -1,38 +0,0 @@
# 基本概念
`Swarm` 是使用 [`SwarmKit`](https://github.com/docker/swarmkit/) 构建的 Docker 引擎内置(原生)的集群管理和编排工具。
使用 `Swarm` 集群之前需要了解以下几个概念
> [!NOTE]
> 虽然 Swarm Docker 引擎内置的集群管理工具但在大规模生产环境中Kubernetes 已经成为容器编排的事实标准如果您主要是为了学习容器编排建议重点关注 Kubernetes 章节
## 节点
运行 Docker 的主机可以主动初始化一个 `Swarm` 集群或者加入一个已存在的 `Swarm` 集群这样这个运行 Docker 的主机就成为一个 `Swarm` 集群的节点 (`node`)
节点分为管理 (`manager`) 节点和工作 (`worker`) 节点
管理节点用于 `Swarm` 集群的管理`docker swarm` 命令基本只能在管理节点执行节点退出集群命令 `docker swarm leave` 可以在工作节点执行一个 `Swarm` 集群可以有多个管理节点但只有一个管理节点可以成为 `leader``leader` 通过 `raft` 协议实现
工作节点是任务执行节点管理节点将服务 (`service`) 下发至工作节点执行管理节点默认也作为工作节点你也可以通过配置让服务只运行在管理节点
来自 Docker 官网的这张图片形象的展示了集群中管理节点与工作节点的关系
![](https://docs.docker.com/engine/swarm/images/swarm-diagram.webp)
## 服务和任务
任务 `Task` `Swarm` 中的最小的调度单位目前来说就是一个单一的容器
服务 `Services` 是指一组任务的集合服务定义了任务的属性服务有两种模式
* `replicated services` 按照一定规则在各个工作节点上运行指定个数的任务
* `global services` 每个工作节点上运行一个任务
两种模式通过 `docker service create` `--mode` 参数指定
来自 Docker 官网的这张图片形象的展示了容器任务服务的关系
![](https://docs.docker.com/engine/swarm/images/services-diagram.webp)

View File

@@ -1,46 +0,0 @@
# SWarm mode 与滚动升级
[部署服务](deploy.md) 一节中我们使用 `nginx:1.13.7-alpine` 镜像部署了一个名为 `nginx` 的服务
现在我们想要将 `NGINX` 版本升级到 `1.13.12`那么在 Swarm mode 中如何升级服务呢
你可能会想到先停止原来的服务再使用新镜像部署一个服务不就完成服务的 升级 了吗
这样做的弊端很明显如果新部署的服务出现问题原来的服务删除之后很难恢复那么在 Swarm mode 中到底该如何对服务进行滚动升级呢
答案就是使用 `docker service update` 命令
```bash
$ docker service update \
--image nginx:1.13.12-alpine \
nginx
```
以上命令使用 `--image` 选项更新了服务的镜像当然我们也可以使用 `docker service update` 更新任意的配置
`--secret-add` 选项可以增加一个密钥
`--secret-rm` 选项可以删除一个密钥
更多选项可以通过 `docker service update -h` 命令查看
## 服务回退
现在假设我们发现 `nginx` 服务的镜像升级到 `nginx:1.13.12-alpine` 出现了一些问题我们可以使用命令一键回退
```bash
$ docker service rollback nginx
```
现在使用 `docker service ps` 命令查看 `nginx` 服务详情
```bash
$ docker service ps nginx
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
rt677gop9d4x nginx.1 nginx:1.13.7-alpine VM-20-83-debian Running Running about a minute ago
d9pw13v59d00 \_ nginx.1 nginx:1.13.12-alpine VM-20-83-debian Shutdown Shutdown 2 minutes ago
i7ynkbg6ybq5 \_ nginx.1 nginx:1.13.7-alpine VM-20-83-debian Shutdown Shutdown 2 minutes ago
```
结果的输出详细记录了服务的部署滚动升级回退的过程

View File

@@ -1,85 +0,0 @@
# Swarm 集群中管理敏感数据
在动态的大规模的分布式集群上管理和分发 `密码``证书` 等敏感信息是极其重要的工作传统的密钥分发方式如密钥放入镜像中设置环境变量volume 动态挂载等都存在着潜在的巨大的安全风险
Docker 目前已经提供了 `secrets` 管理功能用户可以在 Swarm 集群中安全地管理密码密钥证书等敏感数据并允许在多个 Docker 容器实例之间共享访问指定的敏感数据
>注意 `secret` 也可以在 `Docker Compose` 中使用
我们可以用 `docker secret` 命令来管理敏感信息接下来我们在上面章节中创建好的 Swarm 集群中介绍该命令的使用
这里我们以在 Swarm 集群中部署 `mysql` `wordpress` 服务为例
## 创建 secret
我们使用 `docker secret create` 命令以管道符的形式创建 `secret`
```bash
$ openssl rand -base64 20 | docker secret create mysql_password -
$ openssl rand -base64 20 | docker secret create mysql_root_password -
```
## 查看 secret
使用 `docker secret ls` 命令来查看 `secret`
```bash
$ docker secret ls
ID NAME CREATED UPDATED
l1vinzevzhj4goakjap5ya409 mysql_password 41 seconds ago 41 seconds ago
yvsczlx9votfw3l0nz5rlidig mysql_root_password 12 seconds ago 12 seconds ago
```
## 创建 MySQL 服务
创建服务相关命令已经在前边章节进行了介绍这里直接列出命令
```bash
$ docker network create -d overlay mysql_private
$ docker service create \
--name mysql \
--replicas 1 \
--network mysql_private \
--mount type=volume,source=mydata,destination=/var/lib/mysql \
--secret source=mysql_root_password,target=mysql_root_password \
--secret source=mysql_password,target=mysql_password \
-e MYSQL_ROOT_PASSWORD_FILE="/run/secrets/mysql_root_password" \
-e MYSQL_PASSWORD_FILE="/run/secrets/mysql_password" \
-e MYSQL_USER="wordpress" \
-e MYSQL_DATABASE="wordpress" \
mysql:latest
```
如果你没有在 `target` 中显式的指定路径时`secret` 默认通过 `tmpfs` 文件系统挂载到容器的 `/run/secrets` 目录中
```bash
$ docker service create \
--name wordpress \
--replicas 1 \
--network mysql_private \
--publish target=30000,port=80 \
--mount type=volume,source=wpdata,destination=/var/www/html \
--secret source=mysql_password,target=wp_db_password,mode=0444 \
-e WORDPRESS_DB_USER="wordpress" \
-e WORDPRESS_DB_PASSWORD_FILE="/run/secrets/wp_db_password" \
-e WORDPRESS_DB_HOST="mysql:3306" \
-e WORDPRESS_DB_NAME="wordpress" \
wordpress:latest
```
查看服务
```bash
$ docker service ls
ID NAME MODE REPLICAS IMAGE
wvnh0siktqr3 mysql replicated 1/1 mysql:latest
nzt5xzae4n62 wordpress replicated 1/1 wordpress:latest
```
现在浏览器访问 `IP:30000`即可开始 `WordPress` 的安装与使用
通过以上方法我们没有像以前通过设置环境变量来设置 MySQL 密码 而是采用 `docker secret` 来设置密码防范了密码泄露的风险

View File

@@ -1,98 +0,0 @@
# 使用 Compose 文件
正如之前使用 `compose.yml` 来一次配置启动多个容器 `Swarm` 集群中也可以使用 `compose` 文件 `compose.yml` 来配置启动多个服务
上一节中我们使用 `docker service create` 一次只能部署一个服务使用 `compose.yml` 我们可以一次启动多个关联的服务
我们以在 `Swarm` 集群中部署 `WordPress` 为例进行说明
```yaml
version: "3"
services:
wordpress:
image: wordpress
ports:
- 80:80
networks:
- overlay
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
deploy:
mode: replicated
replicas: 3
db:
image: mysql
networks:
- overlay
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
deploy:
placement:
constraints: [node.role == manager]
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
volumes:
db-data:
networks:
overlay:
```
`Swarm` 集群管理节点新建该文件其中的 `visualizer` 服务提供一个可视化页面我们可以从浏览器中很直观的查看集群中各个服务的运行节点
`Swarm` 集群中使用 `compose.yml` 我们用 `docker stack` 命令下面我们对该命令进行详细讲解
## 部署服务
部署服务使用 `docker stack deploy`其中 `-c` 参数指定 compose 文件名
```bash
$ docker stack deploy -c compose.yml wordpress
```
现在我们打开浏览器输入 `任一节点IP:8080` 即可看到各节点运行状态如下图所示
![](../.gitbook/assets/wordpress.png)
在浏览器新的标签页输入 `任一节点IP` 即可看到 `WordPress` 安装界面安装完成之后输入 `任一节点IP` 即可看到 `WordPress` 页面
## 查看服务
```bash
$ docker stack ls
NAME SERVICES
wordpress 3
```
## 移除服务
要移除服务使用 `docker stack down`
```bash
$ docker stack down wordpress
Removing service wordpress_db
Removing service wordpress_visualizer
Removing service wordpress_wordpress
Removing network wordpress_overlay
Removing network wordpress_default
```
该命令不会移除服务所使用的 `数据卷`如果你想移除数据卷请使用 `docker volume rm`