返回文章列表

Docker 與 Ansible 開發高用性 Flask 與 MySQL 基礎設施

本文講述如何結合 Docker 與 Ansible 建立高用性基礎設施,佈署 Flask 應用程式並整合 MySQL 資料函式庫。文章涵蓋目錄結構規劃、輕量級基礎映像選擇、資料儲存容器設計、Flask 應用程式容器化、Ansible playbook 撰寫以及高用性考量,提供完整實作步驟與程式碼範例,並探討

Web 開發 系統設計

輕量級容器化技術 Docker 結合 Ansible 的自動化佈署能力,能有效提升基礎設施的效率和可靠性。本文將示範如何利用這兩個工具,建構一個包含 Flask 應用程式和 MySQL 資料函式庫的高用性基礎設施。首先,合理的目錄結構有助於專案的管理和維護,例如將 Docker 相關檔案放置於 docker 目錄,Ansible 組態檔案置於 provisioning 目錄,資料函式庫檔案置於 db 目錄,網頁程式碼放置於 www 目錄等。選擇輕量級的基礎映像能提升容器啟動速度和執行效率,例如 CoreOS 或 Alpine Linux。資料儲存容器的設計重點在於資料持久化,確保容器重啟後資料不會遺失,可以使用 BusyBox 作為基礎映像,並透過 VOLUME 指令將資料目錄掛載到主機或其他容器。Flask 應用程式容器中,需要安裝 Flask、Flask-SQLAlchemy 和 MySQLclient 等必要的 Python 套件,並組態好與 MySQL 資料函式庫的連線。Ansible playbook 則負責自動化佈署流程,例如取得主機 IP 地址、替換範本變數、複製應用程式碼和範本檔案等。高用性方面,需要考慮資料持久化、負載平衡和自動擴充套件等機制,例如使用雲端資料函式庫服務、Nginx 或 HAProxy 進行負載平衡,以及 Kubernetes 或 AWS ECS 進行自動擴充套件。此外,文章也探討了在 Dockerfile 中使用 Ansible 的優缺點,以及如何使用 ansible-bender 進行更最佳化的容器構建流程,避免在容器內執行 Ansible 產生過多垃圾檔案,並以 Hubot Slack Bot 容器構建為例,說明如何使用 Dockerfile 建立和管理容器映像。

透過 Docker 與 Ansible 建立高用性基礎設施

在現代軟體開發中,Docker 和 Ansible 這兩種工具已成為不可或缺的組成部分。Docker 提供了輕量級的容器化解決方案,而 Ansible 則以其簡單易用的自動化能力著稱。透過這兩者的結合,我們可以建立高效且可靠的基礎設施。以下是如何使用 Docker 與 Ansible 建立一個包含 Flask 應用程式和 MySQL 資料函式庫的高用性基礎設施。

目錄結構

首先,我們需要一個清晰的目錄結構來組織我們的專案檔案。這樣可以確保專案結構簡潔且易於維護。以下是推薦的目錄結構:

docker/
provisioning/
data/
db/
www/
docker.yml
main.yml
setup.yml
Vagrantfile

輕量級基礎映象

在選擇基礎映像時,應該盡量使用輕量級的映像,避免使用過於龐大且包含不必要功能的映像。這樣可以提高容器的啟動速度和執行效率。例如,CoreOS 這樣的輕量級作業系統非常適合用來執行 Docker 容器。

資料儲存容器

資料儲存容器負責持久化儲存資料,以便在容器重啟時不會丟失資料。以下是資料儲存容器的 Dockerfile 範例:

# 建立一個簡單的 MySQL 資料卷 Docker 容器。
FROM busybox
MAINTAINER Jeff Geerling <[email protected]>

# 建立 MySQL 資料卷。
RUN mkdir -p /var/lib/mysql
VOLUME /var/lib/mysql

內容解密:

  1. FROM busybox: 使用 BusyBox 作為基礎映像,BusyBox 是一個非常輕量級的 Linux 發行版,只包含基本命令。
  2. MAINTAINER Jeff Geerling [email protected]: 指定維護者資訊。
  3. RUN mkdir -p /var/lib/mysql: 建立一個目錄來儲存 MySQL 的資料。
  4. VOLUME /var/lib/mysql: 把這個目錄暴露為一個卷,以便可以被主機或其他容器掛載。

Flask 應用程式容器

Flask 是一個輕量級的 Python 網頁框架,適合用來開發小型且快速的網頁應用程式。以下是 Flask 應用程式的程式碼範例:

# 基礎測試頁面。
from flask import Flask, Markup, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import text

app = Flask(__name__)

# 組態 MySQL 連線。
db_uri = 'mysql://flask:flask@{{ host_ip_address }}/flask'
app.config['SQLALCHEMY_DATABASE_URI'] = db_uri
db = SQLAlchemy(app)

@app.route("/")
def test():
    mysql_result = False
    try:
        query = text('SELECT 1')
        result = db.engine.execute(query)
        if [row[0] for row in result][0] == 1:
            mysql_result = True
    except:
        pass

    if mysql_result:
        result = Markup('<span style="color: green;">PASS</span>')
    else:
        result = Markup('<span style="color: red;">FAIL</span>')

    # 傳回包含結果的頁面。
    return render_template('index.html', result=result)

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=80)

內容解密:

  1. from flask import Flask, Markup, render_template: 載入 Flask 的基本模組和範本渲染功能。
  2. from flask_sqlalchemy import SQLAlchemy: 載入 SQLAlchemy 的 Flask 外掛,用於與 MySQL 資料函式庫進行互動。
  3. from sqlalchemy import text: 載入 SQLAlchemy 的文字查詢功能。
  4. app = Flask(name): 建立一個 Flask 應用程式例項。
  5. db_uri = ‘mysql://flask:flask@{{ host_ip_address }}/flask’: 組態 MySQL 的連線字串,其中 {{ host_ip_address }} 是由 Ansible 在佈署時替換的變數。
  6. app.config[‘SQLALCHEMY_DATABASE_URI’] = db_uri: 設定 Flask 應用程式的資料函式庫 URI 組態。
  7. db = SQLAlchemy(app): 建立一個 SQLAlchemy 擴充套件例項並與 Flask 應用程式關聯。
  8. @app.route("/"): 定義一個路由來處理根 URL 的請求。
  9. def test(): 路由處理函式,用於測試與 MySQL 的連線狀態。
  10. mysql_result = False: 初始化 MySQL 查詢結果為 False。
  11. try … except … pass: 執行查詢並捕捉可能的異常。
  12. **result = Markup(’PASS’) if mysql_result else Markup(’FAIL’)`: 執行測試並設定相應的 HTML 結果顯示樣式。
  13. return render_template(‘index.html’, result=result): 渲染範本並傳回結果頁面。

範本檔案

Flask 應用程式需要一個 HTML 範本檔案來顯示結果。以下是範本檔案 index.html 的範例:

<!DOCTYPE html>
<html>
<head>
    <title>Flask + MySQL Docker Example</title>
    <style>* { font-family: Helvetica, Arial, sans-serif }</style>
</head>
<body>
    <h1>Flask + MySQL Docker Example</h1>
    <p>MySQL Connection: {{ result }}</p>
</body>
</html>

內容解密:

  1. Flask + MySQL Docker Example: 標題標籤設定頁面標題。
  2. *: CSS 樣式設定,確保文字使用統一字型。
  3. Flask + MySQL Docker Example

    : 頁面主標題。
  4. MySQL Connection: {{ result }}

    : 動態顯示 MySQL 連線狀態結果。

Flask 應用程式 Dockerfile

接下來,我們需要建立一個 Dockerfile 來構建 Flask 應用程式容器:

# 一個簡單的 Flask 應用程式容器。
FROM geerlingguy/docker-ubuntu2004-ansible
MAINTAINER Jeff Geerling <[email protected]>

# 安裝 Flask 應用程式依賴項。
RUN apt-get update && apt-get install -y libmysqlclient-dev build-essential \
python3-dev python3-pip && pip3 install flask flask-sqlalchemy mysqlclient

# 安裝 playbook 和執行它。
COPY playbook.yml /etc/ansible/playbook.yml
COPY index.py.j2 /etc/ansible/index.py.j2
COPY templates /etc/ansible/templates
RUN mkdir -m 755 /opt/www && ansible-playbook /etc/ansible/playbook.yml --connection=local

EXPOSE 80

內容解密:

  1. FROM geerlingguy/docker-ubuntu2004-ansible: 根據預先組態好的 Ubuntu Ansible 基礎映像構建我的映像。
  2. MAINTAINER Jeff Geerling [email protected]: 指定維護者資訊。
  3. RUN apt-get update && apt-get install -y libmysqlclient-dev build-essential python3-dev python3-pip && pip3 install flask flask-sqlalchemy mysqlclient: 安裝所需的系統包和 Python 包依賴項。這些命令會被 Docker 快取,從而提高後續構建速度。
  4. COPY playbook.yml /etc/ansible/playbook.yml COPY index.py.j2 /etc/ansible/index.py.j2 COPY templates /etc/ansible/templates: 複製 Ansible Playbook 和應用程式範本到映像內部指定位置 。
  5. RUN mkdir -m 755 /opt/www && ansible-playbook /etc/ansible/playbook.yml –connection=local: 建立應用程式目錄並執行 Ansible Playbook進行更複雜組態(如替換範本變數)。
  6. EXPOSE 80:暴露埠80,允許外部存取。

高用性考量

在設計高用性基礎設施時,我們需要考慮多種因素。例如,資料持久化、負載平衡和自動擴充套件等。以下是一些具體建議:

  • 資料持久化:使用外部資料函式庫服務或雲端儲存解決方案來確保資料安全和永續性。例如,可以使用 AWS RDS 或 Google Cloud SQL 作為 MySQL 的託管服務提供商。

  • 負載平衡:使用負載平衡器來分散流量,避免單點故障。Nginx 或 HAProxy 是常見的負載平衡器選擇。

  • 自動擴充套件:使用 Kubernetes 或 AWS ECS 等工具來實作自動擴充套件功能,根據流量自動調整應用程式例項數量。

透過以上步驟和考量,我們可以構建出一個高效且可靠的基礎設施來支援我們的應用程式需求。希望這些資訊對你有所幫助!

Flask 容器化佈署與 MySQL 整合

在 Dockerfile 的最後,我們執行一個 playbook(應該位於與 Dockerfile 相同的目錄中),並暴露埠 80,以便應用程式可以透過 HTTP 被外部世界存取。接下來,我們將建立應用程式佈署的 playbook。純粹主義者可能會對在 Dockerfile 中看到 Ansible playbook 而皺眉,這也是有道理的!像 ansible-playbook 這樣的命令掩蓋了可能在 Docker 中進行並進行快取的組態。此外,使用 geerlingguy/docker-ubuntu1804-ansible 基礎映像(包含 Ansible)需要一個初始下載,這比一個相似的沒有 Ansible 的 Debian 或 Ubuntu 檔案大約大 50+ MB。然而,為了簡潔和維護的便利性,我們使用 Ansible 來管理容器內的所有應用程式組態(否則我們需要執行一堆積冗長且難以理解的 shell 命令來替換 Ansible 的範本功能)。使用像 ansible-bender 這樣的工具,或直接使用 Ansible 構建容器(使用後面章節中提到的 docker 連線外掛),都是你可以自動化容器映像構建而不需要在映像內執行 Ansible 的方式。

Flask 應用程式佈署 Playbook

為了讓 Flask 應用程式正常執行,我們需要取得主機 IP 地址,然後替換 index.py.j2 範本中的變數。在 docker/provisioning/www/playbook.yml 中建立 Flask 佈署 playbook:

---
- hosts: localhost
  become: true

  tasks:
    - name: Get host IP address.
      shell: "/sbin/ip route | awk '/default/ { print $3 }'"
      register: host_ip
      changed_when: false

    - name: Set host_ip_address variable.
      set_fact:
        host_ip_address: "{{ host_ip.stdout }}"

    - name: Copy Flask app into place.
      template:
        src: /etc/ansible/index.py.j2
        dest: /opt/www/index.py
        mode: 0755

    - name: Copy Flask templates into place.
      copy:
        src: /etc/ansible/templates
        dest: /opt/www
        mode: 0755

內容解密:

  • 取得主機 IP 地址:這個任務使用 shell 命令來取得主機的 IP 地址,並將其註冊到 host_ip 變數中。
  • 設定主機 IP 地址變數:這個任務將從上一個任務中取得的 IP 地址設定為 host_ip_address
  • 複製 Flask 應用程式:這個任務將 index.py.j2 範本複製到 /opt/www/ 目錄中,並設定適當的許可權。
  • 複製 Flask 範本:這個任務將範本目錄從 /etc/ansible/templates 複製到 /opt/www/

此圖示展示了 Ansible Playbook 的流程:

此圖示說明瞭每個步驟如何依次執行。

MySQL 應用程式佈署

我們已經多次組態 MySQL,因此不會花太多時間討論 MySQL 的設定。相反,我們將探討 MySQL 在 Docker 容器中的工作原理,並使用之前組態的資料容器中的持久資料卷。

MySQL 已經有一個非常健全且靈活的社群 Docker 映像可供依賴。為此,我們將其包裝在自己的 Dockerfile 中(以便未來進行進一步自定義)。以下是 Dockerfile 的內容:

# A simple MySQL container.
FROM mysql:5.7
MAINTAINER Jeff Geerling <[email protected]>

EXPOSE 3306

內容解密:

  • 基礎映像:這行指令告訴 Docker 從 Docker Hub 上提取 MySQL 的映像。
  • 暴露埠:這行指令告訴 Docker 暴露埠 3306,以便外部世界可以存取 MySQL。

執行與檢查

現在所有東西都到位了,你應該能夠進入主 docker 目錄並執行 vagrant up。大約10分鐘後,Vagrant 應該顯示 Ansible 組態成功,如果你在瀏覽器中存取 http://192.168.33.39/ ,你應該會看到以下內容:

Docker orchestration success!

如果你看到「MySQL Connection: PASS」,恭喜你!如果顯示「FAIL」,你可能需要給 MySQL 一點時間來完成初始化,因為它必須在首次啟動時構建其環境。如果頁面根本沒有顯示出來,你可能需要將你的程式碼與 GitHub 上的 Docker Flask 情況比較。

使用 Ansible 在外部構建容器

在前面的例子中,Ansible playbook 在 Docker 裡面執行來構建 Flask 應用程式映像。雖然這種方法可行且可維護,但它也帶來了很多垃圾。根據容器的應用程式佈署的一大優勢是每個服務都有一個幹凈整潔的容器映像。

在容器內使用 Ansible 需要很多依賴項——Python、Ansible 的依賴項和 Ansible 本身。

ansible-bender

解決這一問題的一種方法是使用 ansible-bender。ansible-bender 的關鍵功能之一是一個適用於 Docker 的映像構建系統。它使用Ansible任務來構建Docker映象而不是直接在構建映象時執行Ansible。

早期的 Ansible 歷史中有一個名為 [Ansible Container] 的專案,它使用一個側邊車容器來執行Ansible,將每個角色儲存為一個單獨的映象層,但該專案從未達到臨界品質,並在大約2018年後進入了一種破敗狀態。

構建 Hubot Slack Bot Container

Hubot 和 Slack

Hubot 是一款來自 GitHub 的開源聊天機器人, 根據 CoffeeScript 編寫, 能夠連線到多種不同的聊天系統。Slack 是一個流行的聊天平台, 被許多企業用來進行內部溝通。

許多團隊從 Hubot 中受益匪淺, 因為它可以儲存資料供快速檢索, 或甚至連線 Hubot 與其他服務 (例如 CI 工具), 啟動佈署、檢查基礎設施健康狀況等。

# A simple Hubot Slack bot container.
FROM hubotio/hubot:latest
MAINTAINER Jeff Geerling <[email protected]>

RUN npm install --save hubot-slack hubot-help hubot-diagnostics hubot-redis-brain

COPY scripts/* ./scripts/

CMD ["hubot", "--adapter", "slack"]

內容解密:

  • 基礎映像:這行指令告訴 Docker 去從 hubotio/hubot:latest 中提取最新版本。
  • 安裝必要外掛:這些外掛是構成 Hubot Slack Bot 的基本需求。
  • 複製指令碼:把本地指令碼複製到容器中。
  • 執行命令:執行 hubot 指令來啟動 bot。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Docker 與 Ansible 開發高用性 Flask 與 MySQL 基礎設施

package "Docker 架構" {
    actor "開發者" as dev

    package "Docker Engine" {
        component [Docker Daemon] as daemon
        component [Docker CLI] as cli
        component [REST API] as api
    }

    package "容器運行時" {
        component [containerd] as containerd
        component [runc] as runc
    }

    package "儲存" {
        database [Images] as images
        database [Volumes] as volumes
        database [Networks] as networks
    }

    cloud "Registry" as registry
}

dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置

@enduml

此圖示說明瞭整個流程如何依次執行。


以上就是玄貓對於如何將 Flask 應用與 MySQL 整合進行容器化佈署的一些心得與實作經驗分享。希望對你有所幫助!