用Ansible部署服务器

常用系统部署工具

系统部署工具其实已经有很多了,从比较原始的用shell script开始用到现在,用过和了解过的就有:fabric, SaltStack和Puppet。SaltStack和Puppet我觉得麻烦了点,fabric看上去简单,但实际使用需要写不少代码,本质上比Sheel script好不了多少,只是换成Python的语法会习惯一些而已。

Ansible的大名其实早就听说过,也曾经试图去试试,但是一直没空(其实就是拖延症),这次正好要部署几台服务器,就借这个机会尝试一下,感觉不错。

Ansible的使用例子

这里只讨论Ansible的playbook(剧本)模式。

一般的教程喜欢从最简单的例子开始,但是实际上从来不会这样用的,所以我觉得意义不大,还是从一个实际的例子出发比较好。

现在要举的例子是从一台全新安装的Debian的服务器(比如VPS服务商提供的初始化主机)开始,以root用户登录,安装基本的docker环境等。具体项目包括:

  • 修改软件源为合适的镜像
  • 更新软件包
  • 安装需要的软件包
  • 配置时区
  • 配置语言
  • 修改系统参数
  • 安装Docker
  • 创建用户并分配docker权限

Ansible playbook结构

最简单的playbook只需要一个yaml文件即可,但并不适合这个例子,所以这里要用一个复杂一点的结构。

  • ansible
    • group_vars
      • all
    • inventory
      • hosts
    • roles
      • user
        • tasks
          • main.yml
      • debian
        • files
          • sources.list
        • tasks
          • main.yml
      • docker
        • tasks
          • main.yml
    • site.yml

ansible是剧本根目录,下面包含三个子目录:group_vars,inventory,roles和一个yaml文件:site.yml。

  • site.yml是项目主文件
  • group_vars里是需要用到的变量定义,这里没有用到分组变量,只有全局变量,所以下面只有一个all文件。
  • inventory里放的主机定义,在hosts文件里。
  • roles是剧本角色,类似子剧本的功能,这里定义了三个:user,debian,docker。分别用于公共配置(这里是用户创建),debian系统基础安装配置,docker是安装配置docker。

roles的结构是类似的,这里只用到了两个子目录:files和tasks。files用于存放需要用到的文件,比如软件包源配置,tasks下只简单地用了一个main.yml。如果需要在文件中使用可变内容,还会需要使用templates,后续再说。

site.yml

---
- name: Init server
  hosts: myserver
  roles:
    - debian
    - docker
    - user

这是一个典型的Ansible剧本文件,用的是YAML格式,其中的---是playbook步骤开始标志,name是步骤名称,会在运行时显示,hosts是指定主机,可以使用all表示所有主机,具体的主机定义在inventory/hosts里,后面会说。roles则指明要调用的子剧本(角色)。

group_vars

需要用到的变量,比如可以定义一个代理配置,后面可以放到环境变量里使用。

proxy_env:
    http_proxy: "http://localhost:8123"
    https_proxy: "http://localhost:8123"

hosts

[web]
web.yourserver.com
svrproxy ansible_host=127.0.0.1 ansible_port=2201

[app]
svrapp ansible_host=127.0.0.1 ansible_port=2202

[myserver:children]
web
app

[myserver:vars]
ansible_user=root

这是一个稍微复杂的结构。

首先,可以在文件的第三段看到在剧本里指定的hosts名:myserver,它是一个主机组,包含两个子组:web和app,其中web子组又包括两台主机:一个是可以外网直接通过22端口访问的主机,另一个是通过ssh tunnel映射到本地的内网主机。

最后,需要注意myserver:vars,这里指定了整个myserver组都默认使用这个变量,指定了登录的用户是root。

debian角色定义

debian角色用于安装系统基本环境。

main.yml

---
- name: copy sources.list
  copy:
    src: sources.list
    dest: /etc/apt/sources.list
    owner: root
    group: root
    mode: '0644'

- name: apt update and upgrade
  apt:
    upgrade: yes
    update_cache: yes
    install_recommends: no
  environment: "{{ proxy_env }}"

- name: install packages
  apt:
    pkg:
      - sudo
      - vim
      - rsync
      - apt-transport-https
      - ca-certificates
      - wget
      - software-properties-common
      - gnupg2
      - curl
    update_cache: no
    install_recommends: no
    autoremove: yes
  environment: "{{ proxy_env }}"

- name: Set timezone to Asia/Shanghai
  timezone:
    name: Asia/Shanghai

- name: Ensure a locale exists
  locale_gen:
    name: en_US.UTF-8
    state: present

- name: Update sysctl vm.max_map_count
  sysctl:
    name: vm.max_map_count
    value: '262144'
    reload: yes

各步骤功能如描述所说:

  • 更新软件源配置文件(sources.list中为自已喜欢的镜像源)
  • 更新软件包
  • 安装必要的软件包
  • 设置时区
  • 设置语言
  • 更新sysctl参数(运行Elasticsearch需要这个参数)

其中proxy_env变量就是在group_vars里定义的,用于指定使用代理,如果变量内容为空或不存在,则不使用代理。

这个角色有一点局限性就是:

软件源配置文件是写死的,所以只能用于指定版本的debian,如果需要用于不同版本甚至是ubuntu等则需要使用templates来创建可变内容的软件源配置文件。

另一个可扩展的点在于:包操作指定使用了apt,如果使用ansible的通用包管理命令则可以支持不同的OS,比如CentOS。

这两点以后另外再说。

sources.list

docker角色定义

用于安装docker环境。

main.yml

---
- name: Add Apt signing key from official docker repo
  apt_key:
    url: "https://download.docker.com/linux/{{ ansible_distribution | lower }}/gpg"
    state: present
  environment: "{{ proxy_env }}"

- name: add docker official repository
  apt_repository:
    repo: "deb [arch=amd64] https://download.docker.com/linux/{{ ansible_distribution | lower }} {{ ansible_distribution_release }} stable"
    state: present
  environment: "{{ proxy_env }}"

- name: apt update and upgrade for docker
  apt:
    name: '*'
    state: latest
    update_cache: yes
    install_recommends: no
    force_apt_get: yes
  environment: "{{ proxy_env }}"

- name: actually install docker
  apt:
    pkg:
      - docker-ce
      - docker-compose
    state: latest
    update_cache: no
    install_recommends: no
  environment: "{{ proxy_env }}"

基本就是以下步骤:

  • 增加APT KEY
  • 增加软件源
  • 更新软件源
  • 安装docker

其中用到了两个ansible变量,用于兼容不同系统——Debian或Ubuntu的不同版本。

  • ansible_distribution: 系统发行版,Debian或Ubuntu。| lower是一个变量转换函数,用于转成小写,详见jinja2(Ansible用的模板引擎)的文档。
  • ansible_distribution_release: 发行版的版本名,比如:Debian 10是buster,Ubuntu 18.04是bionic。

user角色定义

用于创建用户及相应配置。

main.yml

---
- name: add group
  group:
    name: raptor

- name: add user
  user:
    name: raptor
    password: $1$SomeSalt$UqddPX3r4kH3UL5jq5/ZI.
    shell: /bin/bash
    group: raptor
    groups:
      - sudo
      - docker
    append: yes

这个就很简单直接了,没什么好说的。密码的生成方法如下:

python3 -c 'import crypt; print(crypt.crypt("This is my Password", "$1$SomeSalt$"))'

需要注意的是,crypt是系统相关的,必须在Linux系统下运行这个命令才能得到正确的结果,在macOS下生成的不对。

运行playbook

现在到了实践的时候了。先检查一下,在hosts里配置了正确的主机地址和端口,用户名是root,然后在site.yml所在的位置运行:

ansible-playbook -i inventory/hosts site.yml

即可连上服务器并按步骤执行所有操作。

如果不是使用证书登录,而是用密码的话,需要加上--ask-become-pass参数输入密码。如果不用root用户……续篇再说吧。

推送到[go4pro.org]