返回介绍

自动化环境搭建

发布于 2025-01-19 23:59:31 字数 10393 浏览 0 评论 0 收藏 0

自动化

我们来梳理一下上面这个场景里的问题:

  • 开发自己不做部署
  • 环境的安装是手工的
  • 应用的配置信息需要手工修改

除了艺术品之外,在工业社会里,手工就意味着低效,容易犯错,且不可持续。一切可以自动化的,都应该被自动化起来。

一个软件系统往往会包含很多的组件(消息队列服务,应用服务器,数据库服务器,负载均衡器,反向代理,文件服务器等),而且每一套环境(开发环境,测试环境,UAT,Staging,生产)还有自己独立的组件。

因此一个操作系统要被配置成系统的某个组件还需要做很多工作:以 Java 为例,我们需要安装特定版本的 JDK,设置 CLASSPATH 环境变量,修改操作系统的内核参数,创建特定用户(数据库用户等),修改一些目录的权限等等。这些操作如果交给人工来完成,必然会出现各种错误(想想这个过程要被在不同的环境中重复多遍,出错的机率会大大增加)。

事实上业界已经有了很多帮助开发/运维工程师进行环境安装的工具,比如

  • Chef
  • Puppet
  • Ansible

前两者我已经在 《轻量级 Web 应用开发》 有过介绍,这里我们以 Ansible 为例来描述。

Vagrant

Vagrant 提供对虚拟机的封装,使用它可以很容易的通过配置的方式来定义一个 虚拟机

使用 Vagrant ,你只需要定义一个文本文件 Vagrantfile 即可。 Vagrant 自带的命令行工具 vagrant 会尝试加载这个文件,并按照其中的配置来启动虚拟机。 Vagrantfile 按照 ruby 的语法编写,不过不用担心,你无需在其中定义函数或者类,只需要做一些配置即可。

下面是一个简单的虚拟机定义:

Vagrant.configure("2") do |config|
  config.vm.box = "precise64"
  config.vm.network "private_network", :ip => "192.168.2.100"
end

我们指定了虚拟机使用 precise64 (precise 是一个 ubuntu 的发行版,64 表示它是一个 64 位系统的镜像)这样一个镜像,并且给这个虚拟机分配一个私有的 IP 地址,这样我们就可以在宿主环境中通过这个 IP 来访问该虚拟机了。

定义了 Vagrantfile 之后,使用 vagrant 工具的子命令就可以启动虚拟机了

$ vagrant up

你可以在 VirtualBox 的界面里看到正在运行的虚拟机(Vagrant 在底层使用了 VirtualBox 的虚拟机,而不是自行开发另外一套):

virtual box

启动之后,你可以通过

$ vagrant ssh

来登录到虚拟机中。

如果你不知道使用哪个镜像,不知道如何配置 Vagrantfile ,可以使用这个命令来从头开始:

$ vagrant init hashicorp/precise64
$ vagrant up

vagrant 命令会自动下载镜像,并设置环境,然后启动虚拟机。

在工程实践里, Vagrantfile 会 checkin 到代码库中,这样团队里的其他人也可以很容易的在本地重新搭建相同的环境。另外,我推荐你将 box 的版本尽量和生产环境一致(比如都使用 ubuntu 的 precise64 位),这样可以尽早发现一些环境相关的问题。

初始化环境

Vagrant 还提供了丰富的机制来初始化环境。你可以使用简单的 Shell 脚本,或者全功能的 AnsibleChef 等来初始化环境。

设想你需要在虚拟机环境就绪后,在 vagrant 用户的 home 目录下创建一个叫 workers 的目录,安装一个叫 wget 的软件包,然后下载一个网络上的文件到 workers 目录。

要完成这样的动作,我们可以在当前目录(和 Vagrantfile 放在一起)创建一个 setup.sh 的脚本:

#!/usr/bin/env bash

mkdir -p ~/workers
sudo apt-get update
sudo apt-get install wget
wget http://host:port/resource.zip -O ~/workers/resource.zip

然后在 Vagrantfile 中加入:

Vagrant.configure("2") do |config|
  config.vm.box = "precise64"
  config.vm.provision :shell, path: "setup.sh"
end

Vagrant 在初始化虚拟机的时候,会执行 setup.sh ,这样我们就得到了一个经过设置的环境。设置环境可能会是一个非常复杂的过程,比如安装 web 服务器,定义缓存目录,安装监控服务器的客户端,为某些应用程序创建专用用户,修改权限等等,如果用 shell 来写,会比较复杂。

Vagrant 支持很多的 provision 的工具,比如 Ansible 来完成这种复杂的操作。

Ansible

Ansible 是一个自动化配置工具,相对于 ChefPuppet ,它的安装和配置更加简单(无需在被配置的服务器安装额外的 Agent 程序)。它通过 ssh 将一些 Ansible 模块部署到远程机器上,然后执行。

使用 Ansible 可以同时配置,更新多个机器。目前很多企业都会使用各种各样的云产品,比如 AWS 的 EC2,阿里云等,通过 Ansible 可以很容易的将这些环境配置变成自动化。在企业内部的私有云(从一台服务器划分出来的众多虚拟机)中,也可以使用 Ansible 来减少配置环境的时间,提高效率。

在一个冬日的下午,我和一个新手程序员结对在服务器上修改 tomcat 服务器的一些日志的配置,折腾了很久之后,我放弃了。我心想,要不删了 webapps 这个目录重新部署一下看看吧,可能是缓存问题也说不定。不过,头昏脑涨的我并没有发现敲入的命令是 rm -rf /usr/share/tomcat7 。新手程序员问我, rm -rf 是什么意思?我一边用力的敲下回车键,一遍警告这个新手:“rm -rf 是一个非常危险的操作,它表示要强力删除整个……”。等我发现我删除的是 tomcat7 的时候已经太晚了,我们的 QA 环境彻底挂了,所有人都被 block 住了(还好不是在其他人给客户 showcase 的时候)。

另一个工程师,我们姑且称之为 运维工程师 吧,花费了好几个小时来重新安装 tomcat ,以及其中的各种 jvm 参数。

我们在随后的几周里,引入了 Ansible ,这样即使头昏脑涨的程序员无意识的敲入了愚蠢而致命的命令也无所谓,我们只需要 2 分钟就可以配置好一个 tomcat 服务器,崭新的。

惯例

Ansible 中的一些关键概念:

  1. role 定义一个角色,比如 nginx 就可以是一个角色,要完成 nginx 的安装需要很多小的步骤,这些步骤都包含在 nginx 这个 role 中
  2. inventory 定义一组环境,比如 Web 服务器需要三台做负载均衡,数据库由两台服务器组成等,这些都可以通过 inventory 文件来描述,inventory 文件被称为清单文件
  3. playbook 定义在哪些 inventory 应用哪些 role

Ansible 中有一些惯例,遵循这些惯例有助于你快速读懂其他人写的 playbook / role

production                # 生产环境的清单文件
staging                   # staging 环境的清单文件
qa                        # 测试环境的清单文件

site.yml                  # 主 playbook
webservers.yml            # web 服务器的 playbook
dbservers.yml             # 数据库服务器的 playbook

roles/
    common/               # this hierarchy represents a "role"
        tasks/            #
            main.yml      # 具体任务定义
        handlers/         #
            main.yml      # 回调任务
        templates/        #  
            nginx.conf.j2 # 模板文件
        files/            #
            app.conf      # 需要拷贝到被配置环境中的文件 
        vars/             #
            main.yml      # 变量定义
        defaults/         #
            main.yml      # 低优先级变量定义
        meta/             #
            main.yml      # 元数据,用以表述作者信息,定义依赖等

    webtier/              # 另外一个 role,结构和`common`一致
    monitoring/           # 用于监控的 role,结构同上

比如一个简单的 inventory 文件看起来是这样的:

[webservers]
10.29.2.1
10.29.2.2
10.29.2.3

[dbservers]
10.29.2.4
10.29.2.5

没错,它就是一个简单的 ini 文件。如果你需要添加新的机器,只需要将域名/IP 地址添加到对应的小节即可。

Ansible 使用 yml 作为配置,我们来看一个 playbook 的例子:

-   name: webservers
    hosts: webservers
    roles:
        - roles/nginx
    user: robot
    sudo: true

-   name: dbservers
    hosts: dbservers
    roles:
        - roles/mongodb 
    user: robot
    sudo: true
    environment:
        http_proxy: http://user:pass@proxy.host:8080
        https_proxy: http://user:pass@proxy.host:8080

这个 playbook 定义了两个环境的配置信息: webserversdbserverswebservers 中的所有主机会被应用 nginx 角色(安装和配置 nginx,并启动 nginx 服务),而 dbservers 中的主机会被应用 mongodb 的角色。

dbservers 中,我们还加入了 environment 节,其中定义了可以用在安装过程中的一些环境变量设置。

定义好之后,你可以通过下列命令来执行这个 playbook

ansible-playbook -i qa playbook.yml

命令

Ansible 内置了很多常用的命令来简化配置的工作,比如安装软件包,拷贝文件,使用模板,创建用户,创建目录等。

安装软件包

- name: install package
  apt: name=nginx state=present

apt 命令可以用于安装一个软件包,它相当于在主机上执行 apt-get install nginx -y 。如果你需要安装多个软件包,可以采用 with_items 子命令:

- name: install packages
  apt: name={{ item }} state=present
  with_items: 
      -  nginx
      -  python
      -  git

创建目录

- name: create directory
  file: path=/home/vagrant/workers state=directory

拷贝文件

- name: copy file to workers folder
  copy: src=resource.zip dest=/home/vagrant/resource.zip

如果你要使用的命令,正好 Ansible 没有内置,你还可以使用 command 命令来执行:

- name: universal command
  command: ls -alt /home/vagrant

如果要完成 Vagrant 小节中的例子,我们的配置看起来就是这样的:

- name: create directory
  file: path=/home/vagrant/workers state=directory

- name: install package
  apt: name=wget state=present

- name: download file
  command: wget http://host:port/resource.zip -O ~/workers/resource.zip

当然,根据预定,我们会把变量放在 vars 目录的 main.yml 中,比如上面 apt 例子中的多个包的安装,我们会在 vars/main.yml 中定义变量

packages:
      -  nginx
      -  python
      -  git

然后在 tasks/main.yml 中引用:

- name: install packages
  apt: name={{ item }} state=present
  with_items: '{{ packages }}'

独立使用

通常我们会在一个 Linux 环境安装 Ansible ,这个环境专门做环境初始化的工作。如果你使用 ubuntu 环境,可以通过预编译的二进制包来安装:

$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

当然,你也可以通过源码来安装:

$ git clone git://github.com/ansible/ansible.git --recursive
$ cd ./ansible
$ source ./hacking/env-setup
$ sudo make install #安装到系统路径,其他用户也可以使用

安装完成之后,你会得到 ansible 命令和 ansible-playbook 命令。

在执行 ansible 命令之前,我们需要定义 ansible 对应的远程机器列表配置。你需要在 /etc/ansible/hosts 文件中添加所有需要配置的机器 IP 地址或者域名(如果没有这个目录和文件,可以直接创建)。

文件内容就是每个地址一行的形式:

10.29.2.1
10.29.2.2
10.29.2.3

ansible 命令可以用来向 inventory 执行 shell 命令,比如:

$ ansible all -m ping -u robot -b --become-user root

上面的命令会向主机 all/etc/ansible/hosts 文件中指定的所有地址),执行一个 Ansible 模块 ping (-m ping),使用用户 robot (-u robot),并且以另一个用户身份 root (-b --become-user root)。

如果没有设置过 ssh 的私钥,还需要指定 --ask-pass 选项,否则 ansible 会尝试用 ssh 登陆,然后失败。

$ ansible all -m ping -u robot -b --become-user root --ask-pass

除了使用内置模块之外,你开可以使用其他任何 shell 命令:

$ ansible all -a "/bin/echo hello" -u robot --ask-pass

Ansible 会直接在远程机器上执行对应的 shell 命令。

在 Vagrant 中使用

Vagrant 可以很容易的和 Ansible 集成在一起,只需要指定 config.vm.provisionansible 即可:

Vagrant.configure("2") do |config|
  config.vm.provision :ansible do |ansible|
    ansible.playbook = "playbook.yml"
  end
end

当然,你可以定义多个虚拟机,然后并发的来自动化配置,比如像这样:

(1..5).each do |machine_id|
  config.vm.define "machine#{machine_id}" do |machine|
    machine.vm.hostname = "machine#{machine_id}"
    machine.vm.network "private_network", ip: "192.168.2.#{20+machine_id}"

    if machine_id == N
      machine.vm.provision :ansible do |ansible|
        ansible.limit = "all"
        ansible.playbook = "playbook.yml"
      end
    end
  end
end

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文