2017年1月

HTTP/2 时代的开启为前端开发带来了最佳体验。

如果你对 HTTP/2 有所了解,那你可能用过它,或者至少想过怎样能把它融入你的项目中。尽管有很多关于它如何改变工作流程,提高 Web 速度和效率等方面的猜想,但最佳使用方式还没有定下来。这里我想讲的就是我在之前的项目中所发现的 HTTP/2 的最佳实践。

如果你还不确定什么是 HTTP/2,或者为什么它能改进你的工作,可以先看看我介绍背景方面的第一篇文章

记住:开始之前,我要告诉你,尽管你的浏览器可能支持 HTTP/2,但你的服务器可能不支持。检查你的主机托管服务,看看他们是否提供 HTTP/2 的支持。否则你可能要建立你自己的服务器。这篇文章并不会涉及这方面该如何做,但你可以查看 http2 github 页面,找一找这方面的工具。

热身工作

首先组织好你的文件。看一看下面的文件树结构,作为组织你的样式表的起点:

/styles
|── /setup
|     /* 变量、混入(minin)和函数 */
|── /global
|     /* 能放在任何组件和部分中的可重用组件 */
|── /components
|     /* 特殊组件和部分 */
|── setup.scss // setup 样式索引
|── global.scss // 全局样式索引

这会把你的样式分到三个目录下面:setupglobalcomponenets。接下来我会说明这些目录对你的项目有什么用。

setup 目录

setup 目录保存所有的变量、函数、 混入 minin 以及一些正常编译需要的其它文件的定义。要想让这个目录物尽其用,把这个目录下所有内容导入到 setup.scss 文件中是个很不错的主意,这样这个文件就会像下面所展示的一样:

/* setup.scss */

/* 变量 */
@import "setup/variables/colors";

/* 混入 */
@import "setup/mixins/color";

/* 函数 */
@import "setup/functions/color";

... 等等

现在我们能快速引用这个站点中的所有定义,应该确保在所有的样式文件顶部包含我们这里创建的这个文件。

global 目录

接下来的目录,global 目录,应该包含可在当前站点的多个部分或者每一个页面中重复使用的组件。像按钮、文本、主要样式,以及你的浏览器默认设置应该放在这里。我不建议把页面的头部或底部样式放在这儿,因为某些项目中没有头部,或者不同页面头部不同。而且,底部永远是页面的最后一个元素,所以在用户加载完当前站点的其它东西前,不必过分优先考虑加载底部样式。

记住,如果没有那些定义在 setup 目录下的东西,你的 global 样式就可能没有作用,你的 global 文件看起来应该像这样:

/* global.scss */

/* 应用定义 */
@import "setup";

/* 全局样式 */
@import "global/reset";
@import "global/buttons";
@import "global/typography";
@import "global/grid";

... 等等

注意,首先要做的就是导入 setup 样式。这样的话,之后的文件都可以引用这个样式里的定义。

由于站点内的每个页面都需要 global 样式,我们可以用典型的方式,在 <head> 标签内用一个 <link> 标签来加载它们。你所看到的将是一个十分小巧的 css 文件,或者说理论上小巧的,这取决于你需要多少全局样式。

最后,你的组件

注意,我没有在上述目录树中的 components 目录里包含索引文件。这是 HTTP/2 所带来的效用。直到现在,我们已经按照标准步骤构建了一个典型的站点,保持相当简单的结构,仅选择全局化那些最重要的样式。组件充当它们自己的索引文件。

大多数开发者有独特的组织组件的方式,因此我并不想影响你的策略。但是,你所有的组件看起来应该像这样:

/* header.scss */

/* 应用定义 */
@import "../setup";

header {
  // 样式
}

... 等等

同样的,你要把 setup 样式包含进来,确保所有东西在编译时都定义过。除了编译这些文件,以及可能要把他们放到 /assets 目录,以便很容易找到模版,对这些文件你不必 拼接 concatenate 压缩 minify 它们或者改变什么。

现在样式表已经差不多了,构建站点应该很简单。

构建组件

或许对于模板语言你有自己的选择,这取决于你的项目,有可能是 Twig、Rails、Jade 或者 Handlebars。我认为考虑组件最好的方式是它是否有自己的模版文件,它该有个与名字相应的样式。这样你的项目中,模版和样式的比例就会是个不错的 1:1 的比例,而且你知道哪个文件有哪些东西,哪里有哪个文件,因为它们的命名是有规律的。

现在它正步入正轨,用好 HTTP/2 的多种功能十分简单,让我们做一个模版:

{# header.html #}

{# compiled header styles #}
<link href="assets/components/header.css" rel="stylesheet" media="all">

<header>
  <h1>This Awesome HTTP/2 Site</h1>
  ... 等等

非常好!在模版里你可能有更简单的方式链接到资源,但这里显示你所要做的仅是在开始构建时,在模版文件中链接一个小小的头部样式。这将允许你的站点仅仅加载特定资源到任意给定页面的组件中,而且,能够设定页面从头到脚的组件的优先级。

结合在一起

现在所有的组件都有结构,浏览器将会类似以下方式来渲染它们:

<!DOCTYPE html>
<html>
  <head>
    <link rel="stylesheet" media="all" href="/assets/global.css">
  </head>
  <body>

    <link rel="stylesheet" media="all" href="/assets/components/header.css">
    <header>
      ... etc
    </header>

    <link rel="stylesheet" media="all" href="/assets/components/title.css">
    <section class="title">
      ... etc
    </section>

    <link rel="stylesheet" media="all" href="/assets/components/image-component.css">
    <section class="image-component">
      ... etc
    </section>

    <link rel="stylesheet" media="all" href="/assets/components/text-component.css">
    <section class="text-component">
      ... etc
    </section>

    <link rel="stylesheet" media="all" href="/assets/components/footer.css">
    <footer>
      ... etc
    </footer>

  </body>
</html>

这是一个高级别方法,但你的项目中可能有调整的更细致的组件。例如,在头部的 <nav> 组件可能要加载自己的样式表。尽你所能地自由发挥,让组件更有作用 - HTTP/2 不会因这些需求而阻碍你!

结论

这只是一个关于如何在前端用 HTTP/2 构建项目的基本介绍,仅是皮毛而已。你可能注意到我上面所用的方法有的还有改进的空间。请不吝赐教!正如我在第一篇文章中所说的,HTTP/2 可能颠覆自 HTTP/1 以来我们所熟知的某些标准,所以要慎重思考和实践,以便高效使用 HTTP/2 的开发环境。


via: https://www.viget.com/articles/getting-started-with-http-2-part-2

作者:Ben 译者:GitFuture 校对:jasminepeng

本文由 LCTT 组织编译,Linux中国 荣誉推出

在数千个 Linux 系统上的命令/程序中,知道给定命令的类型和目的以及其在系统上的位置(绝对路径)对于新手来说可能是一个挑战。

知道命令/程序的一些细节不仅有助于 Linux 用户掌握大量命令,还能使用户理解命令行或脚本在系统上的操作。

因此,在本文中我们将向你解释五个有用的命令,用于显示给定命令的简短描述和位置。

要在系统上发现新命令,请查看 PATH 环境变量中的所有目录。这些目录存储系统上安装的所有命令/程序。

一旦你找到一个有趣的命令,在继续阅读更多关于它的手册页之前,请尝试如下收集一些简要的信息。

假设你输出了 PATH 的值,然后进到其中的一个目录 /usr/local/bin,注意到一个名为 fswatch(监视文件修改更改)的新命令:

$ echo $PATH
$ cd /usr/local/bin

Find New Commands in Linux

在 Linux 中找出新命令

现在让我们在 Linux 中用不同的方法找出 fswatch 命令的描述和位置。

1、 whatis 命令

whatis 用于显示你作为参数输入的命令名的单行描述(例如下面命令中的 fswatch)。

如果描述太长,一些部分在默认情况下会被省略,使用 -l 标志来显示完整的描述。

$ whatis fswatch
$ whatis -l fswatch

Linux whatis Command Example

Linux whatis 命令示例

2、 apropos 命令

apropos 会搜索手册页名称和关键字描述(以命令名作为正则表达式搜索)。

使用 -l 标志来显示完整的描述。

$ apropos fswatch 
$ apropos -l fswatch

Linux apropos Command Example

Linux apropos 命令示例

默认上,apropos 会如示例那样输出所有匹配的行。你可以使用 -e 选项来精确匹配:

$ apropos fmt
$ apropos -e fmt

Linux apropos Command Show by Keyword

Linux apropos 命令根据关键词显示

3、 type 命令

type 命令会输出给定命令的完整路径名,此外,如果输入的命令名不是做为独立存储在磁盘的文件的程序,type 还会告诉你命令分类:

  • shell 内置命令
  • shell 关键字或保留字
  • 别名
$ type fswatch 

Linux type Command Example

Linux type 命令示例

当命令是另外一个命令的别名时,type 会显示运行别名时所执行的命令。使用 alias 命令可以查看你系统上创建的所有别名:

$ alias
$ type l
$ type ll

Show All Aliases in Linux

显示 Linux 中所有别名

4、 which 命令

which 可以帮助命令定位命令,它会打印出命令的绝对路径:

$ which fswatch 

Find Linux Command Location

找出 Linux 命令位置

一些二进制文件存在于 PATH 环境变量中的多个目录,使用 -a 标志来找出所有匹配的路径名。

5、 whereis 命令

whereis 定位指定命令名的二进制、源和帮助页文件,如下所示:

$ whereis fswatch
$ whereis mkdir 
$ whereis rm

Linux whereis Command Example

Linux whereis 命令示例

虽然上面的命令对于查找关于命令/程序的一些快速信息很重要,但是该命令的手册总是可以提供完整的文档,它还包括其他相关程序的列表:

$ man fswatch

在本文中,我们回顾了五个简单的命令,用于显示命令的简短的手册描述和位置。 你可以在反馈栏中对此文章做出贡献或提出问题。


作者简介:

Aaron Kili 是 Linux 和 F.O.S.S 爱好者,将来的 Linux SysAdmin、web 开发人员,目前是 TecMint 的内容创作者,他喜欢用电脑工作,并坚信分享知识。


via: http://www.tecmint.com/find-linux-command-description-and-location/

作者:Aaron Kili 译者:geekpi 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

嗨,在本教程中,我们将学习如何使用 docker 部署 golang web 应用程序。 你可能已经知道,由于 golang 的高性能和可靠性,docker 是完全是用 golang 写的。在我们详细介绍之前,请确保你已经安装了 docker 以及 golang 并对它们有基本了解。

关于 docker

Docker 是一个开源程序,它可以将应用及其完整的依赖包捆绑到一起,并打包为容器,与宿主机共享相同的 Linux 内核。另一方面,像 VMware 这样的基于 hypervisor 的虚拟化操作系统容器提供了高级别的隔离和安全性,这是由于客户机和主机之间的通信是通过 hypervisor 来实现的,它们不共享内核空间。但是硬件仿真也导致了性能的开销,所以容器虚拟化诞生了,以提供一个轻量级的虚拟环境,它将一组进程和资源与主机以及其它容器分组及隔离,因此,容器内部的进程无法看到容器外部的进程或资源。

用 Go 语言创建一个 “Hello World” web 应用

首先我们为 Go 应用创建一个目录,它会在浏览器中显示 “Hello World”。创建一个 web-app 目录并使它成为当前目录。进入 web-app 应用目录并编辑一个名为 main.go 的文件。

root@demohost:~# mkdir web-app
root@demohost:~# cd web-app/
root@demohost:~/web-app# vim.tiny main.go

package main
import (
    "fmt"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello %s", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/World", handler)
    http.ListenAndServe(":8080", nil)
}

使用下面的命令运行上面的 “Hello World” Go 程序。在浏览器中输入 http://127.0.0.1:8080/World 测试,你会在浏览器中看到 “Hello World”。

root@demohost:~/web-app# PORT=8080 go run main.go

下一步是将上面的应用在 docker 中容器化。因此我们会创建一个 dockerfile 文件,它会告诉 docker 如何容器化我们的 web 应用。

root@demohost:~/web-app# vim.tiny Dockerfile

# 得到最新的 golang docker 镜像
FROM golang:latest

# 在容器内部创建一个目录来存储我们的 web 应用,接着使它成为工作目录。
RUN mkdir -p /go/src/web-app
WORKDIR /go/src/web-app

# 复制 web-app 目录到容器中
COPY . /go/src/web-app

# 下载并安装第三方依赖到容器中
RUN go-wrapper download
RUN go-wrapper install

# 设置 PORT 环境变量
ENV PORT 8080

# 给主机暴露 8080 端口,这样外部网络可以访问你的应用
EXPOSE 8080

# 告诉 Docker 启动容器运行的命令
CMD ["go-wrapper", "run"]

构建/运行容器

使用下面的命令构建你的 Go web-app,你会在成功构建后获得确认。

root@demohost:~/web-app# docker build --rm -t web-app .
Sending build context to Docker daemon 3.584 kB
Step 1 : FROM golang:latest
latest: Pulling from library/golang
386a066cd84a: Already exists
75ea84187083: Pull complete
88b459c9f665: Pull complete
a31e17eb9485: Pull complete
1b272d7ab8a4: Pull complete
eca636a985c1: Pull complete
08158782d330: Pull complete
Digest: sha256:02718aef869a8b00d4a36883c82782b47fc01e774d0ac1afd434934d8ccfee8c
Status: Downloaded newer image for golang:latest
---> 9752d71739d2
Step 2 : RUN mkdir -p /go/src/web-app
---> Running in 9aef92fff9e8
---> 49936ff4f50c
Removing intermediate container 9aef92fff9e8
Step 3 : WORKDIR /go/src/web-app
---> Running in 58440a93534c
---> 0703574296dd
Removing intermediate container 58440a93534c
Step 4 : COPY . /go/src/web-app
---> 82be55bc8e9f
Removing intermediate container cae309ac7757
Step 5 : RUN go-wrapper download
---> Running in 6168e4e96ab1
+ exec go get -v -d
---> 59664b190fee
Removing intermediate container 6168e4e96ab1
Step 6 : RUN go-wrapper install
---> Running in e56f093b6f03
+ exec go install -v
web-app
---> 584cd410fdcd
Removing intermediate container e56f093b6f03
Step 7 : ENV PORT 8080
---> Running in 298e2a415819
---> c87fd2b43977
Removing intermediate container 298e2a415819
Step 8 : EXPOSE 8080
---> Running in 4f639a3790a7
---> 291167229d6f
Removing intermediate container 4f639a3790a7
Step 9 : CMD go-wrapper run
---> Running in 6cb6bc28e406
---> b32ca91bdfe0
Removing intermediate container 6cb6bc28e406
Successfully built b32ca91bdfe0

现在可以运行我们的 web-app 了,可以执行下面的命令。

root@demohost:~/web-app# docker run -p 8080:8080 --name="test" -d web-app
7644606b9af28a3ef1befd926f216f3058f500ffad44522c1d4756c576cfa85b

进入 http://localhost:8080/World 浏览你的 web 应用。你已经成功容器化了一个可重复的/确定性的 Go web 应用。使用下面的命令来启动、停止并检查容器的状态。

### 列出所有容器
root@demohost:~/ docker ps -a

### 使用 id 启动容器
root@demohost:~/ docker start CONTAINER_ID_OF_WEB_APP

### 使用 id 停止容器
root@demohost:~/ docker stop CONTAINER_ID_OF_WEB_APP

重新构建镜像

假设你正在开发 web 应用程序并在更改代码。现在要在更新代码后查看结果,你需要重新生成 docker 镜像、停止旧镜像并运行新镜像,并且每次更改代码时都要这样做。为了使这个过程自动化,我们将使用 docker 卷在主机和容器之间共享一个目录。这意味着你不必为在容器内进行更改而重新构建镜像。容器如何检测你是否对 web 程序的源码进行了更改?答案是有一个名为 “Gin” 的好工具 https://github.com/codegangsta/gin,它能检测是否对源码进行了任何更改,然后重建镜像/二进制文件并在容器内运行更新过代码的进程。

要使这个过程自动化,我们将编辑 Dockerfile 并安装 Gin 将其作为入口命令来执行。我们将开放 3030 端口(Gin 代理),而不是 8080。 Gin 代理将转发流量到 web 程序的 8080 端口。

root@demohost:~/web-app# vim.tiny Dockerfile

# 得到最新的 golang docker 镜像
FROM golang:latest

# 在容器内部创建一个目录来存储我们的 web 应用,接着使它称为工作目录。
RUN mkdir -p /go/src/web-app
WORKDIR /go/src/web-app

# 复制 web 程序到容器中
COPY . /go/src/web-app

# 下载并安装第三方依赖到容器中
RUN go get github.com/codegangsta/gin
RUN go-wrapper download
RUN go-wrapper install

# 设置 PORT 环境变量
ENV PORT 8080

# 给主机暴露 8080 端口,这样外部网络可以访问你的应用
EXPOSE 3030

# 启动容器时运行 Gin
CMD gin run

# 告诉 Docker 启动容器运行的命令
CMD ["go-wrapper", "run"]

现在构建镜像并启动容器:

root@demohost:~/web-app# docker build --rm -t web-app .

我们会在当前 web 程序的根目录下运行 docker,并通过暴露的 3030 端口链接 CWD (当前工作目录)到容器中的应用目录下。

root@demohost:~/web-app# docker run -p 3030:3030 -v `pwd`:/go/src/web-app --name="test" -d web-app

打开 http://localhost:3030/World, 你就能看到你的 web 程序了。现在如果你改变了任何代码,会在浏览器刷新后反映在你的浏览器中。

总结

就是这样,我们的 Go web 应用已经运行在 Ubuntu 16.04 Docker 容器中运行了!你可以通过使用 Go 框架来快速开发 API、网络应用和后端服务,从而扩展当前的网络应用。


via: http://linoxide.com/containers/setup-go-docker-deploy-application/

作者:Dwijadas Dey 译者:geekpi 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出

这是一篇关于 Ansible 的速成课程,你可以用作小项目的模板,或者帮你深入了解这个神奇的工具。阅读了本指南之后,你将对自动化服务器配置、部署等有足够的了解。

Ansible 是什么,为什么你该了解?

Ansible 简单的说是一个 配置管理系统 configuration management system 。你只需要可以使用 ssh 访问你的服务器或设备就行。它也不同于其他工具,因为它使用推送的方式,而不是像 puppet 或 chef 那样使用拉取的方式。你可以将代码部署到任意数量的服务器上,配置网络设备或在基础架构中自动执行任何操作。

前置要求

假设你使用 Mac 或 Linux 作为你的工作站,Ubuntu Trusty 作为你的服务器,并有一些安装软件包的经验。此外,你的计算机上将需要以下软件。所以,如果你还没有它们,请先安装:

情景

我们将模拟 2 个连接到 MySQL 数据库的 Web 应用程序服务器。Web 应用程序使用 Rails 5 和 Puma。

准备

Vagrantfile

为这个项目创建一个文件夹,并将下面的内容保存到名为 Vagrantfile 的文件。

VMs = [
    [ "web1", "10.1.1.11"],
    [ "web2", "10.1.1.12"],
    [ "dbserver", "10.1.1.21"],
  ]

Vagrant.configure(2) do |config|
  VMs.each { |vm|
    config.vm.define vm[0] do |box|
      box.vm.box = "ubuntu/trusty64"
      box.vm.network "private_network", ip: vm[1]
      box.vm.hostname = vm[0]
      box.vm.provider "virtualbox" do |vb|
         vb.memory = "512"
      end
    end
  }
end

配置你的虚拟网络

我们希望我们的虚拟机能互相交互,但不要让流量流出到真实的网络,所以我们将在 Virtualbox 中创建一个仅主机(HOST-Only)的网络适配器。

  1. 打开 Virtualbox
  2. 转到 Preferences
  3. 转到 Network
  4. 单击 Host-Only
  5. 单击添加网络
  6. 单击 Adapter
  7. 将 IPv4 设置为 10.1.1.1,IPv4 网络掩码:255.255.255.0
  8. 单击 “OK”

测试虚拟机及虚拟网络

在终端中,在存放 Vagrantfile 的项目目录中,输入下面的命令:

vagrant up

它会创建你的虚拟机,因此会花费一会时间。输入下面的命令并验证输出内容以检查是否已经工作:

$ vagrant status
Current machine states:

web1                      running (virtualbox)
web2                      running (virtualbox)
master                    running (virtualbox)

This environment represents multiple VMs. The VMs are all listed
above with their current state. For more information about a specific
VM, run `vagrant status NAME`.

现在使用 vagrant 的用户名和密码 ,按 Vagrantfile 中的 IP 登录其中一台虚拟机,这将验证虚拟机并将它们的密钥添加到你的已知主机(known_hosts)文件中。

ssh [email protected] # password is `vagrant`
ssh [email protected]
ssh [email protected]

恭喜你!现在你已经有可以实验的服务器了。下面的剩下的部分!

安装 Ansible

对于 Mac 用户:

$ brew install ansible

对于 Ubuntu 用户:

$ sudo apt install ansible

确保你使用了ansible 最近的版本 2.1 或者更高的版本:

$ ansible --version
ansible 2.1.1.0

清单

Ansible 使用清单文件来了解要使用的服务器,以及如何将它们分组以并行执行任务。让我们为这个项目创建我们的清单文件 inventory,并将它放在与 Vagrantfile 相同的文件夹中:

[all:children]
webs
db

[all:vars]
ansible_user=vagrant
ansible_ssh_pass=vagrant

[webs]
web1 ansible_host=10.1.1.11
web2 ansible_host=10.1.1.12

[db]
dbserver ansible_host=10.1.1.21
  • [all:children] 定义一个组的组(all
  • [all:vars] 定义属于组 all 的变量
  • [webs] 定义一个组,就像 [db] 一样
  • 文件的其余部分只是主机的声明,带有它们的名称和 IP
  • 空行表示声明结束

现在我们有了一个清单,我们可以从命令行开始使用 ansible,指定一个主机或一个组来执行命令。以下是检查与服务器的连接的命令示例:

$ ansible -i inventory all -m ping
  • -i 指定清单文件
  • all 指定要操作的服务器或服务器组
  • -m' 指定一个 ansible 模块,在这种情况下为ping`

下面是命令输出:

dbserver | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web1 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}
web2 | SUCCESS => {
    "changed": false,
    "ping": "pong"
}

服务器以不同的顺序响应,这只取决于谁先响应,但是这个没有关系,因为 ansible 独立保持每台服务器的状态。

你也可以使用另外一个选项来运行任何命令:

  • -a <command>
$ ansible -i inventory all -a uptime
web1 | SUCCESS | rc=0 >>
 21:43:27 up 25 min,  1 user,  load average: 0.00, 0.01, 0.05

dbserver | SUCCESS | rc=0 >>
 21:43:27 up 24 min,  1 user,  load average: 0.00, 0.01, 0.05

web2 | SUCCESS | rc=0 >>
 21:43:27 up 25 min,  1 user,  load average: 0.00, 0.01, 0.05

这是只有一台服务器的另外一个例子:

$ ansible -i inventory dbserver -a "df -h /"
dbserver | SUCCESS | rc=0 >>
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        40G  1.4G   37G   4% /

剧本

剧本(playbook)只是个 YAML 文件,它将清单文件中的服务器组与命令关联。在 ansible 中的对于关键字是 tasks,它可以是一个预期的状态、shell 命令或许多其它的选项。有关 ansible 可做的所有事情列表,可以查看所有模块的列表

下面是一个运行 shell 命令的剧本示例,将其保存为 playbook1.yml

---
- hosts: all
  tasks:
    - shell: uptime
  • --- 是 YAML 文件的开始
  • - hosts:指定要使用的组
  • tasks:标记任务列表的开始
  • - shell:指定第一个任务使用 shell 模块
  • 记住:YAML 需要缩进结构,确保你始终遵循剧本中的正确结构

用下面的命令运行它:

$ ansible-playbook -i inventory playbook1.yml

PLAY [all] *********************************************************************

TASK [setup] *******************************************************************
ok: [web1]
ok: [web2]
ok: [dbmaster]

TASK [command] *****************************************************************
changed: [web1]
changed: [web2]
changed: [dbmaster]

PLAY RECAP *********************************************************************
dbmaster                   : ok=2    changed=1    unreachable=0    failed=0
web1                       : ok=2    changed=1    unreachable=0    failed=0
web2                       : ok=2    changed=1    unreachable=0    failed=0

正如你所见,ansible 运行了 2 个任务,而不是只有剧本中的一个。TASK [setup] 是一个隐式任务,它会首先运行以捕获服务器的信息,如主机名、IP、发行版和更多详细信息,然后可以使用这些信息运行条件任务。

还有最后的 PLAY RECAP,其中 ansible 显示了运行了多少个任务以及每个对应的状态。在我们的例子中,因为我们运行了一个 shell 命令,ansible 不知道结果的状态,它被认为是 changed

安装软件

我们将使用 apt 在我们的服务器上安装软件,因为我们需要 root 权限,所以我们必须使用 become 语句,将这个内容保存在 playbook2.yml 中并运行它(ansible-playbook playbook2.yml):

---
- hosts: webs
  become_user: root
  become: true
  tasks:
    - apt: name=git state=present

有一些语句可以应用于 ansible 中所有模块;一个是 name 语句,可以让我们输出关于正在执行的任务的更具描述性的文本。要使用它,保持任务内容一样,但是添加 name :描述性文本 作为第一行,所以我们以前的文本将改成:

---
- hosts: webs
  become_user: root
  become: true
  tasks:
    - name: This task will make sure git is present on the system
      apt: name=git state=present

使用 with_items

当你要处理一个列表时,比如要安装的项目和软件包、要创建的文件,可以用 ansible 提供的 with_items。下面是我们如何在 playbook3.yml 中使用它,同时添加一些我们已经知道的其他语句:

---
- hosts: all
  become_user: root
  become: true
  tasks:
    - name: Installing dependencies
      apt: name={{item}} state=present
      with_items:
        - git
        - mysql-client
        - libmysqlclient-dev
        - build-essential
        - python-software-properties

使用 templatevars

vars 是一个定义变量语句,可以在 task 语句或 template 文件中使用。 Jinja2 是 Ansible 中使用的模板引擎,但是关于它你不需要学习很多。在你的剧本中定义变量,如下所示:

---
- hosts: all
  vars:
    - secret_key: VqnzCLdCV9a3jK
    - path_to_vault: /opt/very/deep/path
  tasks:
    - name: Setting a configuration file using template
      template: src=myconfig.j2 dest={{path_to_vault}}/app.conf

正如你看到的,我可以使用 {{path_to_vault}} 作为剧本的一部分,但也因为我使用了 template语句,我可以使用 myconfig.j2 中的任何变量,该文件必须存在一个名为 templates 的子文件夹中。你项目树应该如下所示:

├── Vagrantfile
├── inventory
├── playbook1.yml
├── playbook2.yml
└── templates
    └── myconfig.j2

当 ansible 找到一个 template 语句后它会在 templates 文件夹内查找,并将把被 {{}} 括起来的变量展开来。

示例模板:

this is just an example vault_dir: {{path_to_vault}} secret_password: {{secret_key}}

即使你不扩展变量你也可以使用 template。考虑到将来会添加所以我先做了。比如创建一个 hosts.j2 模板并加入主机名和 IP。

10.1.1.11 web1
10.1.1.12 web2
10.1.1.21 dbserver

这里要用像这样的语句:

  -  name: Installing the hosts file in all servers
     template: src=hosts.j2 dest=/etc/hosts mode=644

shell 命令

你应该尽量使用模块,因为 Ansible 可以跟踪任务的状态,并避免不必要的重复,但有时 shell 命令是不可避免的。 对于这些情况,Ansible 提供两个选项:

  • command:直接运行一个命令,没有环境变量或重定向(|<> 等)
  • shell:运行 /bin/sh 并展开变量和支持重定向

其他有用的模块

  • apt\_repository - 在 Debian 系的发行版中添加/删除包仓库
  • yum\_repository - 在 RedHat 系的发行版中添加/删除包仓库
  • service - 启动/停止/重新启动/启用/禁用服务
  • git - 从 git 服务器部署代码
  • unarchive - 从 Web 或本地源解开软件包

只在一台服务器中运行任务

Rails 使用 migrations 来逐步更改数据库,但由于你有多个应用程序服务器,因此这些迁移任务不能被分配为组任务,而我们只需要一个服务器来运行迁移。在这种情况下,当使用 run_once 时,run_once 将分派任务到一个服务器,并直到这个任务完成继续下一个任务。你只需要在你的任务中设置 run_once:true

    - name: 'Run db:migrate'
      shell: cd {{appdir}};rails db:migrate
      run_once: true

会失败的任务

通过指定 ignore_errors:true,你可以运行可能会失败的任务,但不会影响剧本中剩余的任务完成。这是非常有用的,例如,当删除最初并不存在的日志文件时。

    - name: 'Delete logs'
      shell: rm -f /var/log/nginx/errors.log
      ignore_errors: true

放到一起

现在用我们先前学到的,这里是每个文件的最终版:

Vagrantfile

VMs = [
    [ "web1", "10.1.1.11"],
    [ "web2", "10.1.1.12"],
    [ "dbserver", "10.1.1.21"],
  ]

Vagrant.configure(2) do |config|
  VMs.each { |vm|
    config.vm.define vm[0] do |box|
      box.vm.box = "ubuntu/trusty64"
      box.vm.network "private_network", ip: vm[1]
      box.vm.hostname = vm[0]
      box.vm.provider "virtualbox" do |vb|
         vb.memory = "512"
      end
    end
  }
end

inventory

[all:children]
webs
db

[all:vars]
ansible_user=vagrant
ansible_ssh_pass=vagrant

[webs]
web1 ansible_host=10.1.1.11
web2 ansible_host=10.1.1.12

[db]
dbserver ansible_host=10.1.1.21

templates/hosts.j2:

10.1.1.11 web1
10.1.1.12 web2
10.1.1.21 dbserver

templates/my.cnf.j2

[client]
port        = 3306
socket      = /var/run/mysqld/mysqld.sock

[mysqld_safe]
socket      = /var/run/mysqld/mysqld.sock
nice        = 0

[mysqld]
server-id   = 1
user        = mysql
pid-file    = /var/run/mysqld/mysqld.pid
socket      = /var/run/mysqld/mysqld.sock
port        = 3306
basedir     = /usr
datadir     = /var/lib/mysql
tmpdir      = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
bind-address        = 0.0.0.0
key_buffer      = 16M
max_allowed_packet  = 16M
thread_stack        = 192K
thread_cache_size       = 8
myisam-recover         = BACKUP
query_cache_limit   = 1M
query_cache_size        = 16M
log_error = /var/log/mysql/error.log
expire_logs_days    = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet  = 16M

[mysql]

[isamchk]
key_buffer      = 16M

!includedir /etc/mysql/conf.d/

final-playbook.yml

- hosts: all
  become_user: root
  become: true
  tasks:
    - name: 'Install common software on all servers'
      apt: name={{item}} state=present
      with_items:
        - git
        - mysql-client
        - libmysqlclient-dev
        - build-essential
        - python-software-properties
    - name: 'Install hosts file'
      template: src=hosts.j2 dest=/etc/hosts mode=644

- hosts: db
  become_user: root
  become: true
  tasks:
    - name: 'Software for DB server'
      apt: name={{item}} state=present
      with_items:
        - mysql-server
        - percona-xtrabackup
        - mytop
        - mysql-utilities
    - name: 'MySQL config file'
      template: src=my.cnf.j2 dest=/etc/mysql/my.cnf
    - name: 'Restart MySQL'
      service: name=mysql state=restarted
    - name: 'Grant access to web app servers'
      shell: echo 'GRANT ALL PRIVILEGES ON *.* TO "root"@"%" WITH GRANT OPTION;FLUSH PRIVILEGES;'|mysql -u root mysql

- hosts: webs
  vars:
    - appdir: /opt/dummyapp
  become_user: root
  become: true
  tasks:
    - name: 'Add ruby-ng repo'
      apt_repository: repo='ppa:brightbox/ruby-ng'
    - name: 'Install rails software'
      apt: name={{item}} state=present
      with_items:
        - ruby-dev
        - ruby-all-dev
        - ruby2.2
        - ruby2.2-dev
        - ruby-switch
        - libcurl4-openssl-dev
        - libssl-dev
        - zlib1g-dev
        - nodejs
    - name: 'Set ruby to 2.2'
      shell: ruby-switch --set ruby2.2
    - name: 'Install gems'
      shell: gem install bundler rails
    - name: 'Kill puma if running'
      shell: file /run/puma.pid >/dev/null && kill `cat /run/puma.pid` 2>/dev/null
      ignore_errors: True
    - name: 'Clone app repo'
      git:
           repo=https://github.com/c0d5x/rails_dummyapp.git
           dest={{appdir}}
           version=staging
           force=yes
    - name: 'Run bundler'
      shell: cd {{appdir}};bundler
    - name: 'Run db:setup'
      shell: cd {{appdir}};rails db:setup
      run_once: true
    - name: 'Run db:migrate'
      shell: cd {{appdir}};rails db:migrate
      run_once: true
    - name: 'Run rails server'
      shell: cd {{appdir}};rails server -b 0.0.0.0 -p 80 --pid /run/puma.pid -d

放在你的环境中

将这些文件放在相同的目录,运行下面的命令打开你的开发环境:

vagrant up
ansible-playbook -i inventory final-playbook.yml

部署新的代码

确保修改了代码并推送到了仓库中。接下来,确保你 git 语句中使用了正确的分支:

    - name: 'Clone app repo'
      git:
           repo=https://github.com/c0d5x/rails_dummyapp.git
           dest={{appdir}}
           version=staging
           force=yes

作为一个例子,你可以修改 version 字段为 master,再次运行剧本:

ansible-playbook -i inventory final-playbook.yml

检查所有的 web 服务器上的页面是否已更改:http://10.1.1.11http://10.1.1.12。将其更改为 version = staging 并重新运行剧本并再次检查页面。

你还可以创建只包含与部署相关的任务的替代剧本,以便其运行更快。

接下来是什么 ?!

这只是可以做的很小一部分。我们没有接触 角色 role 过滤器 filter 、调试等许多其他很棒的功能,但我希望它给了你一个良好的开始!所以,请继续学习并使用它。如果你有任何问题,你可以在 twitter 或评论栏联系我,让我知道你还想知道哪些关于 ansible 的东西!


via: https://gorillalogic.com/blog/getting-started-with-ansible/?utm_source=webopsweekly&utm_medium=email

作者:JOSE HIDALGO 译者:geekpi 校对:wxy

本文由 LCTT 组织编译,Linux中国 荣誉推出

用披萨来说明当你订单数很大的时候 HTTP/2 是怎么打败 HTTP/1.1 的。

在建立网站和应用的方式上 HTTP/2 有些令人惊叹的改变,在 HTTP/2 发布后的一年半,几乎 10% 的网站使用了 HTTP/2。它绝对值得采用,但是这篇文章应该首先推给使用 HTTP/2 的前端开发者。这个连载的文章是指导前端开发者怎么转换到 HTTP/2。

本文涵盖了 HTTP/2 对 HTTP/1.1 来说有什么提高的内容,并且向前端开发者介绍了 HTTP/2。

再次让我想起什么是 HTTP ...

超文本传输协议,也就是 HTTP,这个协议决定了 web 内容怎么传输。HTTP/1.1 在 1999 年被标准化,那时候的 web 和现在有很大的不同,表格霸占了整个网络。样式通常被内联在元素中,如果网站管理员更加的细致,他们会在头部写个 <style>标签。 JavaScript 也被丢在文档里面,那时候完整的网站通常也不会超过几页。

HTTP/1.1 预计这种情况会持续一段时间,所以它并没有太过关注在让一个站点可以加载大量的资源方面,因为那时候的开发者并不需要这个。因此它使用了一个非常简单的方式来处理资源,你访问一个资源然后服务器去寻找它,并且返回你访问的资源,或者告诉你这个资源不存在。这种被叫作"线头堵塞"方式非常高效,但是当你需要多个资源的时候,这个进程会依次寻找每个资源。这意味着在你访问第二个资源之前,服务器必须找到你访问的第一个资源并且载入它,或者告诉你没找到。

大型站点的发展

在 1999 年之后的几年里,随着 php 和其他像 Rails 这样的动态语言的崛起,站点变得越来越复杂。css 文件也随着向响应式开发的转变而变的越来越大,因此像 Sass 这样的可以创造一个简单的工作环境的 CSS 编译器就应运而生。 JavaScript 也在 web 上有了更大的作用,它允许开发者编写复杂的应用,这曾经只是 C++ 开发人员的工作。随着 Retina 和高清显示屏的兴起,也让图片变得更高清。随着这些改变,文件大小呈现指数式的增长,使得本来是等待几个字节的资源变成了加载几千字节,甚至在某些情况下有几兆。当你开始载入页面的其它东西前,必须先载入数百 K 的东西,你只能乐观的假设你的用户有很快的网络接入。

想象 HTTP/1.1 是个过去的那种柜台购买式的街旁披萨店。你能自己过去并且预定一个雪碧和 2 片 Angry Hawaiian ,然后等待 3 分钟。他们可以很容易地处理这些,实际上这是一个蓬勃发展的商业模式-定单简单、处理迅速。

然而,一旦你决定在同样的披萨店主办一场小区域的季度颁奖典礼,事情就变的更复杂了。每个人都预定不同的东西,快速而杂乱无章让等待时间直线上升。

哪里是 HTTP/2 的舞台

HTTP/2 对前端开发者主要的承诺就是复用。意思就是资源请求能发生在同一时间,并且服务器能马上响应这些资源。在请求之间没有等待,因为它们发生在同一时间。

使用同样的比喻,HTTP/2 允许披萨店在餐馆他们自己区域举办派对。派一个服务员接受订单,并把所有已经准备好的订单交付。当其他人的比萨在制作的时候,你也不需要花 30 分钟去等待你的雪碧,它们在第一批交付的东西之中。这方式使得管理大量订单更加简单,并且防止人们等他们的订单时间太长。

复用带给我们的 web 开发的大变化是改变了文件的加载方式。帮助绕过资源加载的 HTTP/1.1 瓶颈的方式是通过连接和压缩需要被加载的文件。所有任务运行器都默认采取这样的操作方式,或者需要作一点小设置就行。和过去一样,开发人员可以将图像放在 精灵拼图 sprite sheets 中,这会减少了对服务器的请求数。

改进 HTTP/1.1

将文件连接起来是个处理 HTTP1.1 的请求限制问题的非常聪明的方式,但是连接文件的主要问题是它要求用户第一次访问整个网站时下载所有的资源。一旦它们载入后,浏览器就会缓存所有的资源。这能提高用户每次访问网页时的速度,但是前期负载很重,对跳出率不利。此外,他们可能为所不访问的页面加载资源。期望用户访问每个页面以查看每个样式,并与每个脚本进行交互是不现实的。此外,在加拿大和欧洲以及几乎每个美国移动提供商的地方,有每月的带宽上限。不是加载额外的 54 千字节的内容就会超过每月的流量限制,而是让我们假设用户想保留这些额外的流量看 Taylor Swift 的 gif。

使用 HTTP/2 和多路复用,您可以开发一些最高效的网站,但它需要一些重新思考、甚至​​撤销之前的最佳做法。重复一次,我的目的是加快 HTTP/2 的会话,使用我们新的工具,我们可以发现这些新的最佳的做法。

在我的下一篇文章,我将探索建设基于 HTTP/2 的网站的一些最好方式


via: https://www.viget.com/articles/getting-started-with-http-2-part-1?imm_mid=0eb24a&cmp=em-web-na-na-newsltr_20161130

作者:Ben 译者:hkurj 校对:wxy

本文由 LCTT 组织编译,Linux中国 荣誉推出

众所周知,如果你去电脑城购买一个新的笔记本电脑,你所见到的尽是预安装了 Windows 或是 Mac 系统的笔记本电脑。无论怎样,你都会被迫支付一笔额外的费用—— 微软系统的许可费用或是苹果电脑背后的商标使用权费用。

当然,你也可以选择购买一款笔记本电脑,然后安装自己喜欢的操作系统。然而,最困难的可能是需要找到一款硬件跟你想安装的操作系统兼容性良好的笔记本电脑。

在此之上,我们还需要考虑硬件驱动程序的可用性。那么,你应该怎么办呢?答案很简单:购买一款预安装了 Linux 系统的笔记本电脑

幸运的是,正好有几家值得依赖的公司提供质量好、有名气,并且预安装了 Linux 系统的笔记本电脑,这样你就不用再担心驱动程序的可用性了。

也就是说,在这篇文章中,我们将根据用户对笔记本电脑的用途列出 3 款可供用户选择的高性价比机器。

普通用户使用的 Linux 笔记本电脑

如果你正在寻找一款能够满足日常工作及娱乐需求的 Linux 笔记本电脑,它能够正常运行办公软件,有诸如 Firefox或是 Chrome 这样的 Web 浏览器,有局域网 / Wifi 连接功能,那么你可以考虑选择 System76 公司生产的 Linux 笔记本电脑,它可以根据用户的定制化需求选择处理器类型,内存及磁盘大小,以及其它配件。

除此之外, System76 公司为他们所有的 Ubuntu 系统的笔记本电脑提供终身技术支持。如果你觉得这听起来不错,并且也比较感兴趣,你可以考虑下 Lemur 或者 Gazelle 这两款笔记本电脑。

Lemur Laptop for Linux

Lemur Linux 笔记本电脑

Gazelle Laptop for Linux

Gazelle Linux 笔记本电脑

开发者使用的 Linux 笔记本电脑

如果你想找一款坚固可靠,外观精美,并且性能强悍的笔记本电脑用于开发工作,你可以考虑一下 Dell 的 XPS 13 笔记本电脑

这款 13 英寸的精美笔记本电脑,配置全高清 HD 显示器,触摸板,售价范围视其配置情况而定,CPU 代号/型号:Intel 的第 7 代处理器 i5 和 i7,固态硬盘大小:128 至 512 GB,内存大小:8 至 16 GB。

Dells XPS Laptop for Linux

Dell XPS Linux 笔记本电脑

这些都是你应该考虑在内的重要因素,Dell 已经做得很到位了。不幸的是,Dell ProSupport 为该型号的笔记本电脑仅提供 Ubuntu 16.04 LTS 系统的技术支持(在写本篇文章的时候 - 2016 年 12 月)。

系统管理员使用的 Linux 笔记本电脑

虽然系统管理员可以顺利搞定在裸机上安装 Linux 系统的工作,但是使用 System76 的产品,你可以避免寻找各种驱动并解决兼容性问题上的麻烦。

之后,你可以根据自己的需求来配置电脑特性,你可以提高笔记本电脑的性能,增加内存到 32 GB 以确保你可以运行虚拟化环境并进行各种系统管理相关的任务。

如果你对此比较感兴趣,你可以考虑购买 Kudu 或者是 Oryx Pro 笔记本电脑。

Kudu Linux Laptop

Kudu Linux 笔记本电脑

Oryx Pro Linux Laptop

Oryx Pro 笔记本电脑

总结

在这篇文章中,我们探讨了对于普通用户、开发者及系统管理员来说,为什么购买一款预安装了 Linux 系统的笔记本是一个不错的选择。一旦你决定好,你就可以轻松自如的考虑下应该如何消费这笔省下来的钱了。

你觉得在购买一款 Linux 系统的笔记本电脑时还应该注意些什么?请在下面的评论区与大家分享。

像往常一样,如果你对这篇文章有什么意见和看法,请随时提出来。我们很期待看到你的回复。


作者简介:

Gabriel Cánepa 来自 Argentina,San Luis,Villa Mercedes ,他是一名 GNU/Linux 系统管理员和网站开发工程师。目前在一家世界领先的消费品公司工作,在日常工作中,他非常善于使用 FOSS 工具来提高公司在各个领域的生产率。


via: http://www.tecmint.com/buy-linux-laptops/

作者:Gabriel Cánepa 译者:rusking 校对:jasminepeng

本文由 LCTT 原创编译,Linux中国 荣誉推出