Laravel Eloquent 模型关联速查表
Laravel Eloquent 模型关联速查表

一张 Laravel 的 Eloquent ORM 5.5 速查表
一对一关联
细节展示
在这个展示中,我们有 2 个模型(所有者和汽车)及两张表 (所有者和汽车)。
商业逻辑:
一个所有者可以拥有一台车。
一台车可以属于一个所有者。
关联图:

关联细节:
汽车表必须储存所有者 ID。
Eloquent模型:
1 | class Owner |
数据库迁移:
1 | Schema::create('owners', function (Blueprint $table) { |
储存记录
1 | // 在 Owner 及 Car 之间建立关联 |
获取记录
1 | // 获取 Owner Car |
一对多关联
示例细节:
在此示例中,我们有两个模型:Thief(小偷)和 Car(车),和两张表:
thieves 和 cars。
业务规则:
小偷可以投走多辆车。
车只能被一个小偷偷走。
关系图:

关系细节:
车辆表应该储存小偷的 ID。
Eloquent 模型:
1 | class Thief |
数据迁移:
1 | Schema::create('thieves', function (Blueprint $table) { |
储存记录:
1 | // 创建介于小偷与车之间的关联。 |
检索记录:
1 | // 获取小偷所偷的车 |
多态一对多关系
演示细节:
在这个演示中,我们有三个模型(男,女和车),和三张表(男,女和车)。
业务规则:
一个男人(买家)可以买很多汽车。
一个女人(买家)可以买很多汽车。
这个汽车可以被一个买家购买(男人或女人)。
关系图:

Eloquent模型:
1 | class Man |
数据库迁移:
1 | Schema::create('men', function (Blueprint $table) { |
储存记录:
1 | // 在买家 (男人 / 女人) 和汽车之间建立联系。 |
检索记录:
1 | // 获取买家 (男人 / 女人)的汽车 |
多对多关联
示例展示:
在这个示例中,我们有两个模型(司机和汽车),和三张表
(司机,汽车和名为car_driver的中间关联表)。
业务规则:
一个司机可以驾驶很多辆车。
一辆车可以被很多个司机驾驶。
关系图:

关联详情:
关联表 “car_driver” 应该保存驾驶员ID和汽车ID。
关联模型:
1 | class Driver |
数据库迁移:
1 | Schema::create('drivers', function (Blueprint $table) { |
储存记录:
1 | //创建Driver和Car之间的关联。 |
检索记录:
1 | // Get Driver Car |
多对多多态关联
展示细节:
在这个展示中我们有三个模型(代客,所有者及汽车)和4张表(代客,车主,汽车及司机)。
商业逻辑:
一个代驾(司机)可以驾驶很多车辆
一个车主(司机)可以驾驶很多
车车一台车可以被很多个司机驾驶(代驾和/或车主)
关联图:

关联细节:
中间表「司机」应该储存驾驶人ID,驾驶人类型及车辆ID。
驾驶是一个模型集合的代称(代客及所有者),而且不限定两个模型。驾驶人类型是模型的真正名称。
Eloquent模型:
1 | class Valet |
数据库迁移:
1 | Schema::create('valets', function (Blueprint $table) { |
储存记录:
1 | // 在 driver(Valet / Owner)和 Car 間建立關聯 |
检索记录:
1 | // 取得 driver(Valet / Owner)的 Cars |

原文作者的 Twitter Mahmoud Zalt
原文地址:https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209
赞赏
Ubuntu-Server系统swappiness参数在内存与交换分区间优化
Ubuntu-Server系统swappiness参数在内存与交换分区间优化
swappiness 值的大小对如何使用swap分区是有着很大的联系。swappiness=0 的时候表示最大限度使用物理内存,然后才是 swap空间,swappiness=100 的时候表示积极的使用swap分区,并且把内存上的数据及时的搬运到swap空间里面。linux的基本默认设置为60,具体如下:

也就是说,你的内存在使用到100-60=40%的时候,就开始出现有交换分区的使用。大家知道,内存的速度会比磁盘快很多,这样子会加大系统 IO,同时造的成大量页的换进换出,严重影响系统的性能,所以我们在操作系统层面,要尽可能使用内存,对该参数进行调整。
临时调整的方法如下,我们调成10:
1 | $ sysctl vm.swappiness=10 |
这只是临时调整的方法,重启后会回到默认设置的
要想永久调整的话,需要在 /etc/sysctl.conf 修改,追加内容:
1 | vm.swappiness=10 |
使其生效:
1 | sudo sysctl -p |
这样便设置完成了。
赞赏
MySQL慢查询优化、索引优化、以及表等优化总结
MySQL慢查询优化、索引优化、以及表等优化总结
MySQL 优化概述
MySQL数据库常见的两个瓶颈是:CPU和I/O的瓶颈。
CPU在饱和的时候一般发生在数据装入内存或从磁盘上读取数据时候。
磁盘I/O瓶颈发生在装入数据远大于内存容量的时候,如果应用分布在网络上,那么查询量相当大的时候那么平瓶颈就会出现在网络上。
我们可以用mpstat, iostat, sar和vmstat来查看系统的性能状态。除了服务器硬件的性能瓶颈,对于MySQL系统本身,我们可以使用工具来优化数据库的性能。
MySQL 优化方案
Mysql的优化,大体可以分为三部分:索引的优化,sql语句的优化,表的优化

索引优化
1. 索引
一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,在生产环境中,我们遇到最多的也是最容易出现问题的,还是一些复杂的查询操作,因此对查询语句的优化是重中之重,加速查询最好的方法就是索引。
索引:简单的说,相当于图书的目录,可以帮助用户快速的找到需要的内容。
在MySQL中也叫做“键”,是存储引擎用于快速找到记录的一种数据结构。能够大大提高查询效率。特别是当数据量非常大,查询涉及多个表时,使用索引往往能使查询速度加快成千上万倍。
总结:索引的目的在于提高查询效率,与我们查询图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小结,然后找到页数。相似的例子还有:查字典,查地图等。
2. 索引类型
- 普通索引
是最基本的索引,它没有任何限制。
- 唯一索引
与前面的普通索引类似,不同的就是:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
- 主键索引
指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。
- 全文索引
主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。fulltext索引配合match against操作使用,而不是一般的where语句加like。它可以在create table,alter table ,create index使用,不过目前只有char、varchar,text 列上可以创建全文索引。值得一提的是,在数据量较大时候,现将数据放入一个没有全局索引的表中,然后再用CREATE index创建fulltext索引,要比先为一张表建立fulltext然后再将数据写入的速度快很多。
3. 索引优化
- 只要列中含有NULL值,就最好不要在此例设置索引,复合索引如果有NULL值,此列在使用时也不会使用索引
- 尽量使用短索引,如果可以,应该制定一个前缀长度
- 对于经常在where子句使用的列,最好设置索引,这样会加快查找速度
- 对于有多个列where或者order by子句的,应该建立复合索引
- 对于like语句,以%或者‘-’开头的不会使用索引,以%结尾会使用索引
- 尽量不要在列上进行运算(函数操作和表达式操作)
- 尽量不要使用not in和<>操作
SQL 慢查询的优化

1. 如何捕获低效sql
1)slow_query_log
这个参数设置为ON,可以捕获执行时间超过一定数值的SQL语句。
2)ong_query_time
当SQL语句执行时间超过此数值时,就会被记录到日志中,建议设置为1或者更短。
3)slow_query_log_file
记录日志的文件名。
4)log_queries_not_using_indexes
这个参数设置为ON,可以捕获到所有未使用索引的SQL语句,尽管这个SQL语句有可能执行得挺快。
2. 慢查询优化的基本步骤
- 先运行看看是否真的很慢,注意设置SQL_NO_CACHE
- where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
- explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
- order by limit 形式的sql语句让排序的表优先查
- 了解业务方使用场景
- 加索引时参照建索引的几大原则
- 观察结果,不符合预期继续从1开始分析
3.优化原则
- 查询时,能不要就不用,尽量写全字段名
- 大部分情况连接效率远大于子查询
- 多使用explain和profile分析查询语句
- 查看慢查询日志,找出执行时间长的sql语句优化
- 多表连接时,尽量小表驱动大表,即小表 join 大表
- 在千万级分页时使用limit
- 对于经常使用的查询,可以开启缓存
数据库表优化
- 表的字段尽可能用NOT NULL
- 字段长度固定的表查询会更快
- 把数据库的大表按时间或一些标志分成小表
- 将表拆分
数据表拆分:主要就是垂直拆分和水平拆分。
水平切分:将记录散列到不同的表中,各表的结构完全相同,每次从分表中查询, 提高效率。
垂直切分:将表中大字段单独拆分到另外一张表, 形成一对一的关系。
总结:
Mysql的优化主要就在于:索引的优化,sql语句的优化,表的优化,在高并发网络环境下,除了优化数据库外,还会涉及到分布式缓存,CDN,数据库读写分离等高并发优化技术。
以上就是MySQL的优化技术方案,以下是最新阿里P8架构师谈架构设计系列。
赞赏
实用 Docker 速查大全:快速认识 Docker 的概念和使用方法
实用 Docker 速查大全:快速认识 Docker 的概念和使用方法
为何使用 Docker
「通过 Docker,开发者可以使用任何语言任何工具创建任何应用。“Dockerized” 的应用是完全可移植的,能在任何地方运行 - 不管是同事的 OS X 和 Windows 笔记本,或是在云端运行的 Ubuntu QA 服务,还是在虚拟机运行的 Red Hat 产品数据中心。
Docker Hub 上有 13000+ 的应用,开发者可以从中选取一个进行快速扩展开发。Docker 跟踪管理变更和依赖关系,让系统管理员能更容易理解开发人员是如何让应用运转起来的。而开发者可以通过 Docker Hub 的共有 / 私有仓库,构建他们的自动化编译,与其他合作者共享成果。
Docker 帮助开发者更快地构建和发布高质量的应用。」—— 什么是 Docker

系统环境
我用的是 Oh My Zsh 和 Docker 插件,它可以自动补全 Docker 命令。你的环境可能有所不同。
Linux
Docker 对于 Linux 内核版本的 最低要求 为 3.10.x。
MacOS
10.8「Mountain Lion」或更新版本。
检查版本
时刻关注你当前正在使用的 Docker 版本是十分重要的,这能够帮助你了解可用的特性。同时,可以让你在查找镜像时选择使用的版本。接下来让我们看看如何操作。
docker version查看你正在运行的 Docker 版本。
获取 Docker 服务版本:
1 | docker version --format '{{.Server.Version}}' |
你也可以输出原始的 JSON 数据:
1 | docker version --format '{{json .}}' |
安装
Linux
Docker 官方提供了快速、易用的安装脚本:
1 | curl -sSL https://get.docker.com/ | sh |
如果你不想执行一个不明不白的 Shell 脚本,那么请看 安装说明,选择你在用的发行版本。
如果你是一个 Docker 超新手,那么你应当先去看看 系列教程。
macOS
下载并安装 Docker Community Edition。如果你在使用 Homebrew-Cask,只需在命令行输入 brew cask install docker 即可。下载安装 Docker Toolbox 亦可。Docker For Mac 很赞,但是它的安装过程与 VirtualBox 不太一样。详情请查阅 比较。
注意:Docker Toolbox 已经过时。你应当使用 Docker Community Edition,详见 Docker Toolbox
安装好 Docker Community Edition 后,点击 Launchpad 内的 Docker 图标。接着即可启动容器了:
1 | docker run hello-world |
好了,现在你有了一个运行中的 Docker 容器了。
容器 (Container)
关于 Docker 进程隔离的基础。容器 (Container) 之于虚拟机 (Virtual Machine) 就好比线程之于进程。或者你可以把他们想成是「吃了类固醇的 chroots」。
生命周期
docker create创建容器但不启动它。docker rename用于重命名容器。docker run一键创建并同时启动该容器。docker rm删除容器。docker update调整容器的资源限制。
通常情况下,不使用任何命令行选项启动一个容器,该容器将会立即启动并停止。若需保持其运行,你可以使用 docker run -td container_id 命令。选项 -t 表示分配一个 pseudo-TTY 会话,-d 表示自动将容器与终端分离(也就是说在后台运行容器,并输出容器 ID)。
如果你需要一个临时容器,可使用 docker run --rm 会在容器停止之后删除它。
如果你需要映射宿主机 (host) 的目录到 Docker 容器内,可使用 docker run -v $HOSTDIR:$DOCKERDIR。详见 卷标 (Volumes) 一节。
如果你想同时删除与容器相关联的卷标,那么在删除容器的时候必须包含 -v 选项,像这样 docker rm -v。
从 Docker 1.10 起,其内置一套各容器独立的 日志引擎,每个容器可以独立使用。你可以使用 docker run --log-driver=syslog 来自定义日志引擎(例如以上的 syslog)。
启动和停止
docker start启动已存在的容器。docker stop停止运行中的容器。docker restart重启容器。docker pause暂停运行中的容器,将其「冻结」在当前状态。docker unpause结束容器暂停状态。docker wait阻塞地等待某个运行中的容器直到停止。docker kill向运行中的容器发送 SIGKILL 指令。docker attach连接到运行中的容器。
如果你想将容器的端口 (ports) 暴露至宿主机,请见 暴露端口 一节。
关于 Docker 实例崩溃后的重启策略,详见 本文。
CPU 限制
你可以限制 CPU 资源占用,无论是指定百分比,或是特定核心数。
例如,你可以设置 cpu-shares。该配置看起来有点奇怪 – 1024 表示 100% CPU,因此如果你希望容器使用所有 CPU 内核的 50%,应将其设置为 512:
1 | docker run -ti --c 512 agileek/cpuset-test |
更多信息请参阅 https://goldmann.pl/blog/2014/09/11/resour...。
通过 cpuset-cpus 可使用特定 CPU 内核。
1 | docker run -ti --cpuset-cpus=0,4,6 agileek/cpuset-test |
请参阅 https://agileek.github.io/docker/2014/08/0... 获取更多细节以及一些不错的视频。
注意,Docker 在容器内仍然能够 看到 全部 CPU – 它仅仅是不使用全部而已。请参阅 https://github.com/docker/docker/issues/20... 获取更多细节。
内存限制
同样,亦可给 Docker 设置 内存限制:
1 | docker run -it -m 300M ubuntu:14.04 /bin/bash |
能力 (Capabilities)
Linux 的 Capability 可以通过使用 cap-add 和 cap-drop 设置。请参阅 https://docs.docker.com/engine/reference/r... 获取更多细节。这有助于提高安全性。
如需要挂载基于 FUSE 的文件系统,你需要结合 --cap-add 和 --device 使用:
1 | docker run --rm -it --cap-add SYS_ADMIN --device /dev/fuse sshfs |
授予对某个设备的访问权限:
1 | docker run -it --device=/dev/ttyUSB0 debian bash |
授予对所有设备的访问权限:
1 | docker run -it --privileged -v /dev/bus/usb:/dev/bus/usb debian bash |
有关容器特权的更多信息请参阅 本文。
信息
docker ps查看运行中的所有容器。docker logs 从容器中读取日志。(你也可以使用自定义日志驱动,不过在 1.10 中,它只支持 json-file 和 journald)。docker inspect查看某个容器的所有信息(包括 IP 地址)。docker events从容器中获取事件 (events)。docker port查看容器的公开端口。docker top查看容器中活动进程。docker stats查看容器的资源使用量统计信息。docker diff查看容器文件系统中存在改动的文件。
docker ps -a 将显示所有容器,包括运行中和已停止的。
docker stats --all 同样将显示所有容器,默认仅显示运行中的容器。
导入 / 导出
docker cp在容器和本地文件系统之间复制文件或目录。docker export将容器的文件系统打包为归档文件流 (tarball archive stream) 并输出至标准输出 (STDOUT)。
执行命令
docker exec在容器内执行命令。
例如,进入正在运行的 foo 容器,并连接 (attach) 到一个新的 Shell 进程:docker exec -it foo /bin/bash。
镜像 (Images)
镜像是 Docker 容器的模板。
生命周期
docker images查看所有镜像。docker import从归档文件创建镜像。docker build从 Dockerfile 创建镜像。docker commit为容器创建镜像,如果容器正在运行则会临时暂停。docker rmi删除镜像。docker load从标准输入 (STDIN) 加载归档包 (tar archive) 作为镜像,包括镜像本身和标签 (tags, 0.7 起)。docker save将镜像打包为归档包,并输出至标准输出 (STDOUT),包括所有的父层、标签和版本 (parent layers, tags, versions, 0.7 起)。
其它信息
docker history查看镜像的历史记录。docker tag给镜像打标签命名(本地或者仓库均可)。
清理
虽然你可以用 docker rmi 命令来删除指定的镜像,不过有个名为 docker-gc 的工具,它可以以一种安全的方式,清理掉那些不再被任何容器使用的镜像。Docker 1.13 起,使用 docker image prune 亦可删除未使用的镜像。参见 清理。
加载 / 保存镜像
从文件中加载镜像:
1 | docker load < my_image.tar.gz |
保存既有镜像:
1 | docker save my_image:my_tag | gzip > my_image.tar.gz |
导入 / 导出容器
从文件中导入容器镜像:
1 | cat my_container.tar.gz | docker import - my_image:my_tag |
导出既有容器:
1 | docker export my_container | gzip > my_container.tar.gz |
加载已保存的镜像 与 导入已导出为镜像的容器 的不同
通过 load 命令来加载镜像,会创建一个新的镜像,并继承原镜像的所有历史。
通过 import 将容器作为镜像导入,也会创建一个新的镜像,但并不包含原镜像的历史,因此会比使用 load 方式生成的镜像更小。
网络 (Networks)
Docker 具备 网络 功能。我并不是很了解它,所以这是一个扩展本文的好地方。文档 使用网络 指出,这是一种无需暴露端口即可实现 Docker 容器间通信的好方法。
生命周期
其它信息
建立连接
你可以 为容器指定 IP 地址:
1 | # 使用你自己的子网和网关创建一个桥接网络 |
仓管中心和仓库 (Registry & Repository)
仓库 (repository) 是 被托管 (hosted) 的已命名镜像 (tagged images) 的集合,这组镜像用于构建容器文件系统。
仓管中心 (registry) 则是 托管服务 (host) – 用于存储仓库并提供 HTTP API,以便 管理仓库的上传和下载。
Docker 官方托管着自己的 仓管中心,包含着数量众多的仓库。不过话虽如此,这个仓管中心 并没有很好地验证镜像,所以如果你担心安全问题的话,请尽量避免使用它。
docker login登入仓管中心。docker logout登出仓管中心。docker search从仓管中心检索镜像。docker pull从仓管中心拉取镜像到本地。docker push从本地推送镜像到仓管中心。
本地仓管中心
你可以使用 docker distribution 项目搭建本地的仓管中心,详情参阅 本地发布 (local deploy) 的介绍。
科学上网后,也可以看看 Google+ Group。
Dockerfile
当你执行 docker build 时,Docker 将会根据 配置文件 启动 Docker 容器。远优于使用 docker commit。
以下是一些编写 Dockerfile 的常用编辑器,并链接到适配的语法高亮模块︰
- 如果你在使用 jEdit,你可以使用我开发的 Dockerfile 语法高亮模块。
- Sublime Text 2
- Atom
- Vim
- Emacs
- TextMate
- 更多信息请参阅 Docker 遇上 IDE
指令
- .dockerignore
- FROM 为其他指令设置基础镜像 (Base Image)。
- MAINTAINER (deprecated - use LABEL instead) 为生成的镜像设置作者字段。
- RUN 在当前镜像的基础上生成一个新层并执行命令。
- CMD 设置容器默认执行命令。
- EXPOSE 告知 Docker 容器在运行时所要监听的网络端口。注意:并没有实际上将端口设置为可访问。
- ENV 设置环境变量。
- ADD 将文件、目录或远程文件复制到容器中。缓存无效。请尽量用
COPY 代替 ADD。 - COPY 将文件或文件夹复制到容器中。注意:将使用 ROOT 用户复制文件,故无论 USER / WORKDIR 指令如何配置,你都需要手动修改其所有者(
chown),ADD也是一样。 - ENTRYPOINT 将容器设为可执行的。
- VOLUME 在容器内部创建挂载点 (mount point) 指向外部挂载的卷标或其他容器。
- USER 设置随后执行 RUN / CMD / ENTRYPOINT 命令的用户名。
- WORKDIR 设置工作目录 (working directory)。
- ARG 定义编译时 (build-time) 变量。
- ONBUILD 添加触发指令,当该镜像被作为其他镜像的基础镜像时该指令会被触发。
- STOPSIGNAL 设置停止容器时,向容器内发送的系统调用信号 (system call signal)。
- LABEL 将键值对元数据 (key/value metadata) 应用到镜像、容器或是守护进程。
教程
例子
- Examples
- Best practices for writing Dockerfiles
- Michael Crosby 还有更多的 Dockerfiles best practices / take 2
- Building Good Docker Images / Building Better Docker Images
- Managing Container Configuration with Metadata
层 (Layers)
Docker 的版本化文件系统是基于层的。就像 Git 的提交或文件变更系统 一样。
链接 (Links)
链接 (links) 通过 TCP/IP 端口 实现 Docker 容器之间的通讯。Atlassian 展示了可用的例子。你还可以 通过主机名 (hostname) 链接。
在某种意义上来说,该特性已经被 自定义网络 所替代。
注意:如果你希望容器之间只通过链接进行通讯,在启动 Docker 守护进程时,请使用 -icc=false 来禁用内部进程通讯。
假设你有一个名为 CONTAINER 的容器(通过 docker run --name CONTAINER 指定)并且在 Dockerfile 中,暴露了一个端口:
1 | EXPOSE 1337 |
然后,我们创建另外一个名为 LINKED 的容器:
1 | docker run -d --link CONTAINER:ALIAS --name LINKED user/wordpress |
然后 CONTAINER 暴露的端口和别名将会以如下的环境变量出现在 LINKED 中:
1 | $ALIAS_PORT_1337_TCP_PORT |
那么你便可以通过这种方式来连接它了。
使用 docker rm --link 即可删除链接。
通常,Docker 容器(亦可理解为「服务」)之间的链接,是「服务发现」的一个子集。如果你打算在生产中大规模使用 Docker,这将是一个很大的问题。请参阅 The Docker Ecosystem: Service Discovery and Distributed Configuration Stores 获取更多信息。
卷标 (Volumes)
Docker 的卷标 (volumes) 是 独立的文件系统。它们并非必须连接到特定的容器上。
生命周期
信息
卷标在不能使用链接(只有 TCP/IP)的情况下非常有用。例如,如果你有两个 Docker 实例需要通讯并在文件系统上留下记录。
你可以一次性将其挂载到多个 docker 容器上,通过 docker run --volumes-from。
因为卷标是独立的文件系统,它们通常被用于存储各容器之间的瞬时状态。也就是说,你可以配置一个无状态临时容器,关掉之后,当你有第二个这种临时容器实例的时候,你可以从上一次保存的状态继续执行。
查看 卷标进阶 来获取更多细节。Container42 非常有用。
你可以 将宿主 MacOS 的文件夹映射为 Docker 卷标:
1 | docker run -v /Users/wsargent/myapp/src:/src |
你也可以用远程 NFS 卷标,如果你觉得你 有足够勇气。
还可以考虑运行一个纯数据容器,像 这里 所说的那样,提供可移植数据。
记得,文件也可以被挂载为卷标。
暴露端口 (Exposing ports)
通过宿主容器暴露输入端口相当 繁琐但有效的。
例如使用 -p 将容器端口映射到宿主端口上(只使用本地主机 (localhost) 接口):
1 | docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage |
你可以使用 EXPOSE 告知 Docker,该容器在运行时监听指定的端口:
1 | EXPOSE <CONTAINERPORT> |
但是注意 EXPOSE 并不会直接暴露端口,你需要用参数 -p 。比如说你要在 localhost 上暴露容器的端口:
1 | iptables -t nat -A DOCKER -p tcp --dport <LOCALHOSTPORT> -j DNAT --to-destination <CONTAINERIP>:<PORT> |
如果你是在 Virtualbox 中运行 Docker,那么你需要配置端口转发 (forward the port)。使用 forwarded_port 在 Vagrantfile 上配置暴露的端口范围,这样你就可以动态地映射了:
1 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| |
如果你忘记了将什么端口映射到宿主机上的话,可使用 docker port 查看:
1 | docker port CONTAINER $CONTAINERPORT |
最佳实践
这里有一些最佳实践,以及争论焦点:
- The Rabbit Hole of Using Docker in Automated Tests
- Bridget Kromhout has a useful blog post on running Docker in production at Dramafever.
- There’s also a best practices blog post from Lyst.
- A Docker Dev Environment in 24 Hours!
- Building a Development Environment With Docker
- Discourse in a Docker Container
安全 (Security)
这节准备讨论一些关于 Docker 安全性的问题。Docker 官方文档 安全 页面讲述了更多细节。
首先第一件事:Docker 是有 root 权限的。如果你在 docker 组,那么你就有 root 权限。如果你将 Docker 的 Unix Socket 暴露给容器,意味着你赋予了容器 宿主机 root 权限。
Docker 不应当作为唯一的防御措施。你应当使其更加安全可靠。
为了更好地理解容器暴露了什么,可参阅由 Aaron Grattafiori 编写的 Understanding and Hardening Linux Containers。这是一个完整全面且包含大量链接和脚注的容器问题指南,介绍了许多有用的内容。即使你已经加固过容器,以下的安全提示依然十分有帮助,但并不能代替理解的过程。
安全提示
为了最大的安全性,你应当考虑在虚拟机上运行 Docker。这是直接从 Docker 安全团队拿来的资料 – slides / notes。之后,可使用 AppArmor、seccomp、SELinux、grsec 等来 限制容器的权限。更多细节,请查阅 Docker 1.10 security features。
Docker 镜像 ID 属于 敏感信息 所以它不应该向外界公开。请将它们当作密码来对待。
阅读由 Thomas Sjögren 编写的 Docker Security Cheat Sheet:关于加固容器的不错的建议。
查看 Docker 安全测试脚本,下载 最佳实践白皮书。
你应当远离使用非稳定版本 grsecurity /pax 的内核,比如 Alpine Linux。如果在产品中用了 grsecurity,那么你应该考虑使用有 商业支持 的 稳定版本,就像你对待 RedHat 那样。虽然要 $200 每月,但对于你的运维预算来说不值一提。
从 Docker 1.11 开始,你可以轻松的限制在容器中可用的进程数,以防止 fork 炸弹。 这要求 Linux 内核 >= 4.3,并且要在内核配置中打开 CGROUP_PIDS=y。
1 | docker run --pids-limit=64 |
同时,你也可以限制进程再获取新权限。该功能是 Linux 内核从 3.5 版本开始就拥有的。你可以从 这篇博客 中阅读到更多关于这方面的内容。
1 | docker run --security-opt=no-new-privileges |
以下内容摘选自 Container Solutions 的 Docker Security Cheat Sheet(PDF 版本,难以使用,故复制至此):
关闭内部进程通讯:
1 | docker -d --icc=false --iptables |
设置容器为只读:
1 | docker run --read-only |
通过 hashsum 来验证卷标:
1 | docker pull debian@sha256:a25306f3850e1bd44541976aa7b5fd0a29be |
设置卷标为只读:
1 | docker run -v $(pwd)/secrets:/secrets:ro debian |
在 Dockerfile 中定义用户并以该用户运行,避免在容器中以 ROOT 身份操作:
1 | RUN groupadd -r user && useradd -r -g user user |
用户命名空间 (User Namespaces)
还可以通过使用 用户命名空间 – 自 1.10 版本起已内置,但默认并未启用。
要在 Ubuntu 15.10 中启用用户命名空间 (remap the userns),请 跟着这篇博客的例子 来做。
安全相关视频
- Using Docker Safely
- Securing your applications using Docker
- Container security: Do containers actually contain?
- Linux Containers: Future or Fantasy?
安全路线图
Docker 的路线图提到关于 seccomp 的支持。
一个名为 bane 的 AppArmor 策略生成器正在实现 安全配置文件。
小贴士
链接:
清理
最新的 数据管理命令 已在 Docker 1.13 实现:
docker system prunedocker volume prunedocker network prunedocker container prunedocker image prune
df 命令
docker system df 将显示当前 Docker 各部分占用的磁盘空间。
Heredoc 声明 Docker 容器
1 | docker build -t htop - << EOF |
最近一次的容器 ID
1 | alias dl='docker ps -l -q' |
带命令的提交(需要 Dockerfile)
1 | docker commit -run='{"Cmd":["postgres", "-too -many -opts"]}' $(dl) postgres |
获取 IP 地址
1 | docker inspect $(dl) | grep -wm1 IPAddress | cut -d '"' -f 4 |
或使用 jq:
1 | docker inspect $(dl) | jq -r '.[0].NetworkSettings.IPAddress' |
或使用 go 模板:
1 | docker inspect -f '{{ .NetworkSettings.IPAddress }}' <container_name> |
或在通过 Dockerfile 构建镜像时,通过构建参数 (build argument) 传入:
1 | DOCKER_HOST_IP=`ifconfig | grep -E "([0-9]{1,3}\.){3}[0-9]{1,3}" | grep -v 127.0.0.1 | awk '{ print $2 }' | cut -f2 -d: | head -n1` |
获取端口映射
1 | docker inspect -f '{{range $p, $conf := .NetworkSettings.Ports}} {{$p}} -> {{(index $conf 0).HostPort}} {{end}}' <containername> |
通过正则匹配容器
1 | for i in $(docker ps -a | grep "REGEXP_PATTERN" | cut -f1 -d" "); do echo $i; done` |
获取环境变量配置
1 | docker run --rm ubuntu env |
强行终止运行中的容器
1 | docker kill $(docker ps -q) |
删除所有容器(强行删除!无论容器运行或停止)
1 | docker rm -f $(docker ps -qa) |
删除旧容器
1 | docker ps -a | grep 'weeks ago' | awk '{print $1}' | xargs docker rm |
删除已停止的容器
1 | docker rm -v `docker ps -a -q -f status=exited` |
停止并删除容器
1 | docker stop $(docker ps -aq) && docker rm -v $(docker ps -aq) |
删除无用 (dangling) 的镜像
1 | docker rmi $(docker images -q -f dangling=true) |
删除所有镜像
1 | docker rmi $(docker images -q) |
删除无用 (dangling) 的卷标
Docker 1.9 版本起:
1 | docker volume rm $(docker volume ls -q -f dangling=true) |
1.9.0 中,参数 dangling=false 居然 没 用 - 它会被忽略然后列出所有的卷标。
查看镜像依赖
1 | docker images -viz | dot -Tpng -o docker.png |
Docker 容器瘦身
- 在某层 (RUN layer) 清理 APT
这应当和其他 apt 命令在同一层中完成。
否则,前面的层将会保持原有信息,而你的镜像则依旧臃肿。
1 | RUN {apt commands} \ |
压缩镜像
1
2ID=$(docker run -d image-name /bin/bash)
docker export $ID | docker import – flat-image-name备份
1
2
3ID=$(docker run -d image-name /bin/bash)
(docker export $ID | gzip -c > image.tgz)
gzip -dc image.tgz | docker import - flat-image-name
监视运行中容器的系统资源利用率
检查某个容器的 CPU、内存以及网络 I/O 使用情况,你可以:
1 | docker stats <container> |
按 ID 列出所有容器:
1 | docker stats $(docker ps -q) |
按名称列出所有容器:
1 | docker stats $(docker ps --format '{{.Names}}') |
按指定镜像名称列出所有容器:
1 | docker ps -a -f ancestor=ubuntu |
删除所有未标签命名 (untagged) 的容器:
1 | docker rmi $(docker images | grep “^” | awk '{split($0,a," "); print a[3]}') |
通过正则匹配删除指定容器:
1 | docker ps -a | grep wildfly | awk '{print $1}' | xargs docker rm -f |
删除所有已退出 (exited) 的容器:
1 | docker rm -f $(docker ps -a | grep Exit | awk '{ print $1 }') |
将文件挂载为卷标
文件也可以被挂载为卷标。例如你可以仅仅注入单个配置文件:
1 | # 从容器复制文件 |
参考链接
赞赏
使用 privoxy 转发 socks 到 http
使用 privoxy 转发 socks 到 http
安装及配置
安装
1 | $ sudo apt-get update |
配置,在文件/etc/privoxy/config 末追加内容
1 | # 把本地 HTTP 流量转发到本地 1080 SOCKS5 代理 |
重新加载配置
1 | $ sudo /etc/init.d/privoxy start |
privoxy 默认开机自启动
参考:
赞赏
Linux 技巧:让进程在后台运行
Linux 技巧:让进程在后台运行
使用 screen
新建窗口
1 | $ screen -S test // 新建一个名字为 test 的窗口,并进入到该窗口 |
跳出窗口
快捷键 Ctrl a +d 实现会话分离,重新进入 ssh 主界面
恢复会话窗口
1 | $ screen -list // 列出窗口列表 |
杀死当前会话窗口
快捷键 Ctrl a +k
或者在当前会话窗口输入 exit 按回车退出
1 | exit |
清除死去的窗口
窗口被杀死后,再用 screen -ls 或者 screen -list 可以看到该窗口后面的(???dead)字样,说明窗口死了,但是仍在占用空间。这时需要清除窗口
1 | $ screen -wipe // 自动清除死去的窗口 |
参考:https://linuxize.com/post/how-to-use-linux-screen/
赞赏
使用 RDM 连接 Homestead 中的 Redis
使用 RDM 连接 Homestead 中的 Redis
安装RDM
https://redisdesktop.com/download
修改 redis.conf
1 | $ sudo vim /etc/redis/redis.conf |
- 将bind 127.0.0.1 ::1 改成 bind 0.0.0.0 ::1
- 将 requirepass 前面的 # 号去掉,后面加上一个密码,例如:
1 | requirepass yourpassword |
修改 Homestead.yaml
添加端口转发
1 | ports: |
重启虚拟机
1 | $ vagrant reload --provision |
赞赏
Linux 设置关机重启提示
Linux 设置关机重启提示
由于运行 reboot 命令系统默认不需要输入密码,为了防止意外发生,必须设置关机提示。
编辑 .zshrc
1 | $ vi ~/.zshrc |
注意,如果使用 bash 的话,请编辑 .bashrc 文件。
追加内容
1 | # Shutdown and restart confirmation |
更新文件
1 | $ source .zshrc |
到此,每当运行 reboot 或者 shutdown 命令的时候,都会出现提示。
这只是其中一种笨方法,但是很简单。
赞赏
如何在Linux中禁用网络摄像头和麦克风
如何在Linux中禁用网络摄像头和麦克风
禁用摄像头
首先,使用如下命令找到网络摄像头驱动:
1 | $ sudo lsmod | grep uvcvideo |
示例输出:
1 | uvcvideo 114688 1 |
这里,uvcvideo 是我的网络摄像头驱动。
现在,让我们禁用网络摄像头。
为此,请编辑以下文件(如果文件不存在,只需创建它):
1 | $ sudo vi /etc/modprobe.d/blacklist.conf |
添加以下行:
1 | ##Disable webcam. |
##Disable webcam 这行不是必需的。为了便于理解,我添加了它。
保存并退出文件。重启系统以使更改生效。
要验证网络摄像头是否真的被禁用,可打开 vlc player 的视频捕捉功能,看设备当中有没有发现摄像头硬件信息。
相反,如果要启用它,只需将 blacklist.confi 中添加的内容注释或删除掉即可。
禁用麦克风
使用如下命令查看麦克风驱动信息:
1 | cat /proc/asound/modules |
示例输出:
1 | 0 snd_hda_intel |
其中 snd_hda_intel 为麦克风驱动名称。
编辑以下文件:
1 | $ sudo vi /etc/modprobe.d/blacklist.conf |
添加以下行:
1 | # Disable mic |
保存并推出文件。重启系统以便更改生效。
要验证是否真的被禁用,只需打开声音设置,查看声音输入源是否存在麦克风信息。
相反,如果要启用它,只需将 blacklist.confi 中添加的内容注释或删除掉即可。