Ansible 提供了便捷的模組,能有效簡化 AWS VPC 的組態和管理。透過 ec2_vpc 模組,可以輕鬆建立 VPC 和子網,並設定路由表和網際網路閘道器。同時,利用 ec2 模組佈署 NAT 例項,確保私有子網中的例項可以連外,並使用 ec2_eip 模組關聯彈性 IP,提供穩定的對外連線。此外,文章也說明瞭如何建立跳板機,以便從外部管理 VPC 內的資源,並透過 OpenVPN 建立安全連線。最後,示範了自定義 Ansible 模組 vpc_lookup 的方法,可以根據標籤查詢 VPC 和子網 ID,進一步提升自動化管理的效率。

使用Ansible進行VPC組態與NAT例項佈署

前言

在雲端運算中,虛擬私有雲(VPC)提供了隔離的網路環境,讓使用者能夠在雲端佈署自己的網路架構。Amazon Web Services(AWS)提供了VPC服務,允許使用者建立自己的虛擬網路,並控制網路的組態和安全。本篇文章將介紹如何使用Ansible自動化工具來組態VPC並佈署NAT例項,以實作私有子網中的例項能夠存取網際網路。

VPC與子網的建立

首先,我們需要建立一個VPC並在其下建立子網。Ansible提供了ec2_vpc模組來建立和管理VPC及其相關資源。

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    region: ap-southeast-2
    prefix: staging
    az0: ap-southeast-2a
    az1: ap-southeast-2b
  tasks:
    - name: create vpc with multi-az subnets
      ec2_vpc:
        region: "{{ region }}"
        cidr_block: 10.0.0.0/16
        resource_tags: '{"Name":"{{ prefix }}_vpc"}'
        subnets:
          - cidr: 10.0.0.0/24
            az: "{{ az0 }}"
            resource_tags: '{"Name":"{{ prefix }}_subnet_public_0"}'
          - cidr: 10.0.1.0/24
            az: "{{ az0 }}"
            resource_tags: '{"Name":"{{ prefix }}_subnet_private_0"}'
          - cidr: 10.0.2.0/24
            az: "{{ az1 }}"
            resource_tags: '{"Name":"{{ prefix }}_subnet_public_1"}'
          - cidr: 10.0.3.0/24
            az: "{{ az1 }}"
            resource_tags: '{"Name":"{{ prefix }}_subnet_private_1"}'
        internet_gateway: yes
        route_tables:
          - subnets:
              - 10.0.0.0/24
              - 10.0.2.0/24
            routes:
              - dest: 0.0.0.0/0
                gw: igw
      register: vpc

內容解密

  1. ec2_vpc模組:用於建立和管理VPC。
  2. region:指定AWS區域。
  3. cidr_block:定義VPC的CIDR區塊。
  4. subnets:定義子網及其相關屬性,如CIDR區塊和可用區域(AZ)。
  5. internet_gateway:啟用網際網路閘道。
  6. route_tables:組態路由表,將公有子網與網際網路閘道關聯。

NAT例項的佈署

為了使私有子網中的例項能夠存取網際網路,我們需要佈署一個NAT例項。

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - staging_vpc_info
  vars:
    region: ap-southeast-2
    key: yan-key-pair-apsydney
    instance_type: t1.micro
    image: ami-3bae3201
    prefix: staging
  tasks:
    - name: NAT instance provisioning
      ec2:
        region: "{{ region }}"
        key_name: "{{ key }}"
        instance_type: "{{ instance_type }}"
        image: "{{ image }}"
        wait: yes
        group: "{{ prefix }}_sg_nat"
        instance_tags:
          Name: "{{ prefix }}_nat"
          class: nat
          environment: staging
          id: nat_launch_01
        vpc_subnet_id: "{{ staging_subnet_public }}"
        source_dest_check: no
        wait: yes
      register: ec2
    - name: associate new EIP for the instance
      ec2_eip:
        region: "{{ region }}"
        instance_id: "{{ item.id }}"
      with_items: ec2.instances
      when: item.id is defined

內容解密:

  1. ec2模組:用於啟動EC2例項。
  2. key_name:指定用於連線例項的金鑰對。
  3. instance_typeimage:定義例項型別和AMI映像。
  4. vpc_subnet_id:指定例項啟動的子網。
  5. source_dest_check: no:停用源/目標檢查,允許NAT例項轉發流量。
  6. ec2_eip模組:為NAT例項關聯彈性IP(EIP)。

路由表的更新

最後,我們需要更新路由表,以使私有子網中的流量能夠透過NAT例項存取網際網路。

  1. 在AWS VPC控制檯中,選擇路由表。
  2. 編輯主路由表,新增一條路由,將目標設為NAT例項。

在 VPC 中使用 Ansible 進行組態管理

在前面的章節中,我們已經使用 Ansible 成功建立了一個 VPC 以及相關的子網和安全組。本章節將探討如何在 VPC 環境中使用 Ansible 進行例項的管理和組態。

VPC 中的例項管理挑戰

由於 VPC 中的私有子網無法直接接收來自網際網路的流量,因此無法直接使用 Ansible 從網際網路上管理私有子網中的伺服器組態。為瞭解決這個問題,我們有兩種可行的方案:

  1. 在公有子網中安裝 Ansible:在公有子網中啟動一個例項並安裝 Ansible,然後允許該 Ansible 機器透過 SSH 連線到私有子網中的主機進行管理。這台 Ansible 機器也可以作為跳板機,允許從網際網路透過 SSH 連線到私有子網中的主機(先 SSH 到 Ansible 機器,然後再從 Ansible 機器 SSH 到私有子網中的主機)。

  2. 建立 VPN 連線:在公有子網中啟動一個 OpenVPN 伺服器例項(可從 AWS Marketplace 獲得),然後在 Ansible 機器上使用 OpenVPN 使用者端連線到 VPN,從而透過 SSH 連線到私有子網中的主機。

建立跳板機例項

為了示範第一種方法,我們將在公有子網中建立一個跳板機例項並在其上安裝 Ansible。首先,需要為這個例項建立一個新的安全組。

建立跳板機安全組

以下是建立跳板機安全組的 playbook(sg_jumpbox.yml):

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - staging_vpc_info
  vars:
    region: ap-southeast-2
    allowed_ip: 123.xxx.xxx.xxx/32
    prefix: staging
    vpc_id: "{{ staging_vpc }}"
  tasks:
    - name: create security group for jump box instance
      ec2_group:
        region: "{{ region }}"
        vpc_id: "{{ vpc_id }}"
        name: "{{ prefix }}_sg_jumpbox"
        description: security group for jump box
        rules:
          - proto: tcp
            from_port: 22
            to_port: 22
            cidr_ip: "{{ allowed_ip }}"
        rules_egress:
          - proto: all
            cidr_ip: 0.0.0.0/0

執行以下命令來建立安全組:

$ ansible-playbook -i hosts sg_jumpbox.yml

建立跳板機例項

接下來,使用以下 playbook(ec2_vpc_jumpbox.yml)在公有子網 A 中啟動跳板機例項:

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - staging_vpc_info
  vars:
    region: ap-southeast-2
    key: yan-key-pair-apsydney
    instance_type: t2.micro
    image: ami-d9fe9be3
    prefix: staging
    vpc_subnet_id: "{{ staging_subnet_public_0 }}"
  tasks:
    - name: jump box instance provisioning
      ec2:
        region: "{{ region }}"
        key_name: "{{ key }}"
        instance_type: "{{ instance_type }}"
        image: "{{ image }}"
        wait: yes
        group: "{{ prefix }}_sg_jumpbox"
        instance_tags:
          Name: "{{ prefix }}_jumpbox"
          class: jumpbox
          environment: "{{ prefix }}"
          id: jumpbox_launch_01
        vpc_subnet_id: "{{ vpc_subnet_id }}"
      register: ec2
    - name: associate new EIP for the instance
      ec2_eip:
        region: "{{ region }}"
        instance_id: "{{ item.id }}"
      with_items: ec2.instances

執行以下命令來啟動跳板機例項:

$ ansible-playbook -i ec2.py ec2_vpc_jumpbox.yml

在跳板機上安裝 Ansible

為了在跳板機上安裝 Ansible,首先需要建立一個角色(role)來執行安裝任務。

建立 Ansible 安裝角色

建立 roles/ansible/tasks/main.yml 檔案,內容如下:

---
- name: upgrade all packages
  yum: name=* state=latest

- name: install the 'Development tools' package group
  yum: name="@Development tools" state=present

- name: install required packages
  yum: name={{ item }} state=present
  with_items:
    - epel-release.noarch
    - python-pip
    - python-devel

- name: install setuptools
  pip: name=setuptools extra_args='--upgrade'

- name: install ansible
  pip: name=ansible

然後,建立一個 playbook(install_ansible.yml)來應用這個角色:

---
- hosts: tag_class_jumpbox
  become: yes
  roles:
    - ansible

執行以下命令來安裝 Ansible:

$ ansible-playbook -i ec2.py install_ansible.yml

組態安全組規則以允許 SSH 存取

為了允許從跳板機到其他例項的 SSH 連線,需要修改相關的安全組規則。例如,如果要在私有子網中安裝 MySQL 資料函式庫伺服器並使用 Ansible 管理其組態,可以修改 sg_modify.yml 中的規則:

- name: modify sg_database rules
  ec2_group:
    region: "{{ region }}"
    vpc_id: "{{ vpc_id }}"
    name: "{{ prefix }}_sg_database"
    description: security group for databases
    rules:
      # allow ssh from the jump box
      - proto: tcp
        from_port: 22
        to_port: 22
        group_name: "{{ prefix }}_sg_jumpbox"
      # allow mysql access from web servers
      - proto: tcp
        from_port: 3306
        to_port: 3306
        group_name: "{{ prefix }}_sg_web"
    rules_egress:
      - proto: tcp
        from_port: 80
        to_port: 80
        cidr_ip: 0.0.0.0/0
      - proto: tcp
        from_port: 443
        to_port: 443
        cidr_ip: 0.0.0.0/0

執行以下命令來修改安全組規則:

$ ansible-playbook -i hosts sg_modify.yml

在Ansible中組態VPC:OpenVPN伺服器佈署

OpenVPN伺服器簡介

OpenVPN Access Server是一種功能全面的SSL VPN軟體解決方案,它整合了OpenVPN伺服器功能、企業管理功能、簡化的OpenVPN Connect UI,以及支援Windows、MAC和Linux作業系統環境的OpenVPN客戶端軟體套件。

啟動OpenVPN例項

首先,我們需要知道OpenVPN Access Server在我們所選區域的AMI ID。

取得AMI ID步驟:

  1. 前往EC2儀錶板,選擇您的區域,然後點選「啟動例項」按鈕。
  2. 在左側導航欄中,選擇「社群AMI」。
  3. 當AMI選擇對話框出現時,在搜尋框中輸入「OpenVPN」。
  4. 找到由openvpn.net提供的最新版本的OpenVPN Access Server AMI,並記錄其AMI ID。
  5. 選擇「取消並離開」。

建立OpenVPN伺服器的安全組

$ vi sg_openvpn.yml
---
- hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - staging_vpc_info
  vars:
    region: ap-southeast-2
    allowed_ip: 123.xxx.xxx.xxx/32
    prefix: staging
    vpc_id: "{{ staging_vpc }}"
  tasks:
    - name: create security group for openvpn instance
      ec2_group:
        region: "{{ region }}"
        vpc_id: "{{ vpc_id }}"
        name: "{{ prefix }}_sg_openvpn"
        description: security group for openvpn
        rules:
          - proto: tcp
            from_port: 22
            to_port: 22
            cidr_ip: "{{ allowed_ip }}"
          - proto: tcp
            from_port: 443
            to_port: 443
            cidr_ip: 0.0.0.0/0
          - proto: tcp
            from_port: 943
            to_port: 943
            cidr_ip: 0.0.0.0/0
          - proto: udp
            from_port: 1194
            to_port: 1194
            cidr_ip: 0.0.0.0/0
        rules_egress:
          - proto: all
            cidr_ip: 0.0.0.0/0

程式碼解密:

此YAML檔案定義了一個Ansible playbook,用於建立OpenVPN例項的安全組。主要步驟包括:

  • 指定目標主機為localhost,並設定連線方式為local。
  • 載入變數檔案staging_vpc_info,並定義了一些變數,如區域、允許的IP位址、字首和VPC ID。
  • 使用ec2_group模組建立安全組,名稱和描述根據變數動態生成。
  • 設定安全組的入站規則,允許特定的TCP和UDP流量。
  • 設定安全組的出站規則,允許所有流量。

執行playbook:

$ ansible-playbook -i hosts sg_openvpn.yml

啟動OpenVPN伺服器例項

$ vi ec2_vpc_openvpn.yml
---
- hosts: localhost
  connection: local
  gather_facts: no
  vars_files:
    - staging_vpc_info
  vars:
    region: ap-southeast-2
    key: yan-key-pair-apsydney
    instance_type: t2.micro
    image: ami-a17f199b
    prefix: staging
    vpc_subnet_id: "{{ staging_subnet_public_0 }}"
  tasks:
    - name: openvpn server instance provisioning
      ec2:
        region: "{{ region }}"
        key_name: "{{ key }}"
        instance_type: "{{ instance_type }}"
        image: "{{ image }}"
        source_dest_check: no
        wait: yes
        group: "{{ prefix }}_sg_openvpn"
        instance_tags:
          Name: "{{ prefix }}_openvpn"
          class: openvpn
          environment: "{{ prefix }}"
          id: openvpn_launch_01
        vpc_subnet_id: "{{ vpc_subnet_id }}"
        register: ec2
    - name: associate new EIP for the instance
      ec2_eip:
        region: "{{ region }}"
        instance_id: "{{ item.id }}"
        with_items: ec2.instances

程式碼解密:

此YAML檔案定義了另一個Ansible playbook,用於啟動OpenVPN伺服器例項。主要步驟包括:

  • 指定目標主機為localhost,並設定連線方式為local。
  • 載入變數檔案staging_vpc_info,並定義了一些變數,如區域、金鑰名稱、例項型別、映像ID、字首和VPC子網ID。
  • 使用ec2模組啟動一個EC2例項,根據變數設定例項的屬性,如區域、金鑰名稱、例項型別、映像ID、安全組和標籤等。
  • 使用ec2_eip模組為新啟動的例項分配一個新的彈性IP位址。

組態OpenVPN伺服器

SSH到OpenVPN Access Server,使用openvpnas使用者:

# ssh -i ~/.ssh/yan-key-pair-apsydney.pem openvpnas@openvpn-ipaddress

按照提示完成OpenVPN Access Server設定精靈。

連線客戶端

使用者可以透過存取https://openvpn-ipaddress來使用Connect Client。使用者可以選擇直接連線到VPN或登入到Connect Client以下載使用者組態檔案(client.ovpn)並使用其他OpenVPN客戶端連線到VPN。

使用Ansible進行VPC資源管理

自定義Ansible模組取得VPC與子網ID

在自動化AWS資源管理過程中,經常需要根據特定的標籤(tags)來取得VPC或子網的ID。雖然Ansible提供了豐富的AWS相關模組,但某些特定功能仍需要自定義模組來實作。本章節將介紹如何建立一個自定義的Ansible模組vpc_lookup,用於根據資源標籤取得VPC和子網ID。

建立vpc_lookup模組

首先,在您的playbook目錄下建立一個名為library的子目錄,並在其中建立vpc_lookup檔案:

$ cd /home/yan/ansible4aws
$ mkdir library
$ vi library/vpc_lookup

將以下Python指令碼內容複製到vpc_lookup檔案中:

#!/usr/bin/python

# author: John Jarvis

import sys

AWS_REGIONS = ['ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2', 'eu-west-1', 'sa-east-1', 'us-east-1', 'us-west-1', 'us-west-2']

try:
    from boto.vpc import VPCConnection
    from boto.vpc import connect_to_region
except ImportError:
    print("failed=True msg='boto required for this module'")
    sys.exit(1)

def main():
    module = AnsibleModule(
        argument_spec=dict(
            region=dict(choices=AWS_REGIONS),
            aws_secret_key=dict(aliases=['ec2_secret_key', 'secret_key'], no_log=True),
            aws_access_key=dict(aliases=['ec2_access_key', 'access_key']),
            tags=dict(default=None, type='dict'),
        )
    )

    tags = module.params.get('tags')
    aws_secret_key = module.params.get('aws_secret_key')
    aws_access_key = module.params.get('aws_access_key')
    region = module.params.get('region')

    if region:
        try:
            vpc = connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
        except boto.exception.NoAuthHandlerFound as e:
            module.fail_json(msg=str(e))
    else:
        module.fail_json(msg="region must be specified")

    subnet_ids = []
    for tag, value in tags.items():
        for subnet in vpc.get_all_subnets(filters={"tag:" + tag: value}):
            subnet_ids.append(subnet.id)

    vpc_ids = []
    for tag, value in tags.items():
        for vpc_item in vpc.get_all_vpcs(filters={"tag:" + tag: value}):
            vpc_ids.append(vpc_item.id)

    module.exit_json(changed=False, vpc_ids=vpc_ids, subnet_ids=subnet_ids)

# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()

儲存並關閉檔案後,賦予該檔案執行許可權:

# chmod 755 library/vpc_lookup

vpc_lookup模組解密

此Python指令碼定義了一個名為vpc_lookup的Ansible模組,主要功能是根據提供的標籤(tags)查詢對應的VPC和子網ID,並以JSON格式傳回結果。

  • 匯入必要的模組:指令碼首先匯入了必要的Python模組,包括sysboto.vpc相關模組。boto是一個Python介面,用於存取AWS服務。
  • 定義支援的AWS區域:變數AWS_REGIONS列出了該模組支援的AWS區域。
  • AnsibleModule定義:透過AnsibleModule類別定義了模組的介面,包括接受的引數如regionaws_secret_keyaws_access_keytags
  • 連線至指定AWS區域:根據提供的regionaws_access_keyaws_secret_key,指令碼嘗試連線到指定的AWS區域。
  • 查詢VPC和子網ID:遍歷提供的tags,使用boto.vpc連線查詢符合標籤過濾條件的VPC和子網,並收集其ID。
  • 傳回結果:最終,指令碼以JSON格式傳回查詢結果,包括vpc_idssubnet_ids列表。

使用vpc_lookup模組

建立一個playbook檔案,例如vpc_delete.yml,來演示如何使用剛才建立的vpc_lookup模組:

---
- hosts: localhost
  connection: local
  gather_facts: no
  vars:
    region: ap-southeast-2
  tasks:
    - name: 取得VPC ID
      vpc_lookup:
        region: "{{ region }}"
        tags:
          Name: test-vpc
      register: vpc

    - name: 刪除VPC
      ec2_vpc:
        region: "{{ region }}"
        state: absent
        vpc_id: "{{ item }}"
        wait: yes
      with_items: "{{ vpc.vpc_ids }}"

Playbook解密

此playbook執行兩個主要任務:

  1. 使用vpc_lookup模組根據標籤"Name": "test-vpc"查詢VPC ID,並將結果註冊到變數vpc中。
  2. 利用查詢到的VPC ID,透過ec2_vpc模組刪除對應的VPC資源。