Docker 外掛的開發,本質上是建立一個與 Docker Engine 互動的外接程式。這些外掛通常使用 Go 語言編寫,並透過 Unix Socket 或 TCP 埠與 Docker 通訊。外掛的註冊需要在特定目錄下建立 .sock.spec.json 檔案,其中 .json 檔案包含更詳細的外掛資訊,例如名稱、地址和 TLS 組態。為了確保外掛在 Docker 之前啟動,可以使用 systemd 建立服務檔案。當 Docker 呼叫外掛時,它會傳送 /Plugin.Activate 請求,外掛則需要傳回 JSON 格式的回應,宣告其功能。

除了 Docker 外掛開發外,本文還介紹了使用 Puppet 自動化 Docker 基礎設施。Puppet 作為一個組態管理工具,能簡化伺服器組態和維護,特別是在多伺服器環境下,可以確保組態一致性,減少錯誤並提高效率。文章以佈署 WordPress 為例,展示瞭如何使用 Puppet 組態 Docker 映像檔、容器、連線埠和環境變數。更進一步,文章還提到了使用 Puppet Master 管理多個 Docker 節點的架構,讓讀者對 Puppet 在 Docker 自動化中的應用有更全面的理解。

建立自己的 Docker 外掛

Docker 外掛是擴充套件 Docker Engine 功能的外接程式。在本章中,我們將探討如何建立自己的 Docker 外掛。

瞭解外掛

到目前為止,我們已經瞭解到所有安裝的外掛實際上與 Docker 本身沒有直接關係。那麼,什麼是外掛呢?Docker 將外掛描述為「Docker 外掛是新增 Docker Engine 功能的外接擴充套件」。

外掛的共同點

我們觀察到的所有外掛都有一些共同的特點:

  • 它們都有指令碼和二進位制檔案,這些檔案都是在 Docker 之外執行的。
  • 大多數外掛都是用與 Docker 相同的語言編寫的,例如 Go 語言。
外掛名稱程式語言
ConvoyGo
REX-RayGo
FlockerPython
WeaveGo

建立自己的外掛

大多數 Docker 服務都是用 Go 語言編寫的,只有 Flocker 是用 Python 編寫的。Go 語言具有表達力強、簡潔、乾淨和高效的特點。其並發機制使得編寫充分利用多核和網路機器的程式變得容易。

發現(Discovery)

通常,外掛會安裝在與 Docker 二進位制檔案相同的宿主機上。我們可以透過在特定目錄下建立檔案來向 Docker 註冊我們的外掛,例如 /run/docker/plugins/etc/docker/plugins/usr/lib/docker/plugins

對於我們的 mobyfs 外掛,我們需要建立以下檔案之一:

  • mobyfs.sock(Unix 通訊端檔案)
  • mobyfs.spec
  • mobyfs.json

如果使用 .spec 檔案,則只需包含一個指向 TCP 主機和埠或本地通訊端檔案的 URL。例如:

tcp://192.168.1.1:8080
tcp://localhost:8080
unix:///other.sock

如果使用 .json 檔案,則需要包含外掛的詳細資訊,例如:

{
  "Name": "mobyfs",
  "Addr": "https://192.168.1.1:8080",
  "TLSConfig": {
    "InsecureSkipVerify": false,
    "CAFile": "/usr/shared/docker/certs/example-ca.pem",
    "CertFile": "/usr/shared/docker/certs/example-cert.pem",
    "KeyFile": "/usr/shared/docker/certs/example-key.pem"
  }
}

啟動順序(Startup Order)

理想情況下,外掛服務應該在 Docker 之前啟動。如果宿主機上安裝了 systemd,可以透過建立一個 systemd 服務檔案來實作這一點。

[Unit]
Description=mobyfs
Before=docker.service

[Service]
EnvironmentFile=/etc/mobyfs/mobyfs.env
ExecStart=/usr/bin/mobyfs start -p 8080
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

[Install]
WantedBy=docker.service

啟用(Activation)

一旦外掛服務啟動,我們需要讓 Docker 知道應該將請求傳送到哪裡。對於我們的 mobyfs 卷外掛,可以執行以下命令:

docker run -ti -v volumename:/data --volume-driver=mobyfs russmckendrick/base bash

這將掛載 volumename 捲到容器內的 /data 目錄。

API 呼叫(API Calls)

一旦外掛被呼叫,Docker 守護程式將使用 RPC 風格的 JSON over HTTP 向外掛服務發出 POST 請求。外掛服務必須實作一個 HTTP 伺服器並繫結到指定的通訊端或埠。

第一個請求是 /Plugin.Activate,外掛服務必須回應其中一個有效的回應。例如,對於 mobyfs 卷外掛,回應如下:

{
  "Implements": ["VolumeDriver"]
}

程式碼範例與解析

以下是一個簡單的 Go 語言程式碼範例,用於建立一個 HTTP 伺服器並回應 Docker 的 /Plugin.Activate 請求:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
		response := map[string]interface{}{
			"Implements": []string{"VolumeDriver"},
		}
		json.NewEncoder(w).Encode(response)
	})
	fmt.Println("Server is listening on port 8080")
	http.ListenAndServe(":8080", nil)
}

內容解密:

此程式碼建立了一個簡單的 HTTP 伺服器,監聽在 8080 埠。當收到 /Plugin.Activate 請求時,它會傳回一個 JSON 物件,表明該外掛實作了 VolumeDriver 介面。這是 Docker 外掛啟用流程中的關鍵一步。

使用第三方工具擴充基礎架構

在第2章「介紹第一方工具」中,我們探討了Docker提供的工具,用於擴充核心Docker引擎的功能。在本章中,我們將研究擴充Docker組態管理和建置及啟動容器方式的第三方工具。我們將要討論的工具有:

  • Puppet:http://puppetlabs.com/
  • Ansible:http://www.ansible.com/docker/
  • Vagrant:https://docs.vagrantup.com/v2/docker/
  • Packer:https://www.packer.io/docs/builders/docker.html
  • Jenkins:https://jenkins-ci.org/content/jenkins-and-docker/

對於每個工具,我們將探討如何安裝、組態和使用它們與Docker一起使用。在我們研究如何使用這些工具之前,讓我們先討論為什麼要使用它們。

為什麼使用這些工具?

到目前為止,我們一直在研究使用主要Docker客戶端或由Docker和其他第三方提供的工具來支援主要Docker客戶端的工具。有一段時間,這些工具現在具有的一些功能在Docker支援產品中並不存在。例如,如果你想啟動一個Docker主機,你不能直接使用Docker Machine,而是必須使用像Vagrant這樣的工具來啟動虛擬機器(本地或雲端),然後使用bash指令碼、Puppet或Ansible安裝Docker。

Puppet化一切

在「容器化一切」的梗圖開始頻繁出現在人們的演講中之前:

人們對Puppet也有同樣的說法。那麼,什麼是Puppet?為什麼要在所有事情上使用它?

Puppet Labs,Puppet的製造商,將Puppet描述為:

「使用Puppet,您可以定義IT基礎架構的狀態,Puppet會自動強制執行所需的狀態。Puppet自動化軟體交付過程的每個步驟,從實體和虛擬機器的組態到協調和報告;從早期程式碼開發到測試、生產發布和更新。」

在像Puppet這樣的工具出現之前,作為系統管理員的工作有時會是一個非常繁瑣的過程:如果你不是在排查問題,就是在編寫自己的指令碼來引導伺服器,或者更糟糕的是,從內部wiki複製和貼上命令來安裝軟體堆積疊並進行組態。

使用Puppet進行自動化

Puppet是一種組態管理工具,可以幫助您自動化IT基礎架構的管理。它允許您定義基礎架構的所需狀態,並確保實際狀態與所需狀態一致。

Puppet的工作原理

Puppet使用一種稱為「宣告式組態」的概念,您可以在其中定義基礎架構的所需狀態。然後,Puppet會自動強制執行所需的狀態。

以下是Puppet工作原理的高階概述:

  1. 您定義基礎架構的所需狀態,使用Puppet的DSL(領域特定語言)或JSON/YAML組態檔案。
  2. Puppet將您的組態推播到您的節點(伺服器、虛擬機器等)。
  3. Puppet的代理程式在您的節點上執行,並強制執行所需的狀態。

使用Puppet的好處

使用Puppet可以帶來多種好處,包括:

  • 提高一致性:Puppet可確保您的基礎架構的一致性,無論是單個伺服器還是數千個伺服器。
  • 減少錯誤:透過自動化任務,Puppet可以減少人為錯誤的可能性。
  • 提高效率:Puppet可以節省您在管理和維護基礎架構上的時間和精力。
# Puppet程式碼範例
node 'example.com' {
  package { 'httpd':
    ensure => installed,
  }
  service { 'httpd':
    ensure => running,
    enable => true,
  }
}

內容解密:

這段Puppet程式碼定義了一個名為example.com的節點,並確保httpd套件已安裝且httpd服務正在執行。這是Puppet宣告式組態的一個簡單範例。

圖表翻譯: 此圖示展示了Puppet的工作流程。Puppet Master將組態推播到Puppet Agent,然後Agent在節點上執行組態。節點將其狀態報告給Agent,Agent再將狀態報告給Master。

使用Puppet管理Docker容器

在伺服器管理中,保持組態的一致性至關重要。隨著伺服器數量的增加,管理和維護這些伺服器變得越來越複雜。Puppet是一種組態管理工具,可以幫助管理員定義伺服器的組態,並確保這些組態被正確地應用和維護。

為何需要Puppet

在沒有Puppet的情況下,管理多台伺服器需要手動執行相同的組態變更,或編寫指令碼來同步這些變更。這不僅耗時,而且容易出錯。Puppet可以幫助管理員定義伺服器的組態,並自動將這些組態應用到多台伺服器上。

例如,如果你有多台伺服器執行PHP網站,你需要確保這些伺服器的NGINX或Apache組態、PHP版本和模組都相同。使用Puppet,你可以定義一個組態檔案,確保所有伺服器都符合這些要求。

package { 'php' :
  ensure => '5.6',
}

這段程式碼確保了PHP套件被安裝,並且版本是5.6。

Docker與Puppet的結合

在Docker的早期版本中,Puppet被用來管理和啟動Docker容器。雖然現在有Docker Machine、Docker Compose和Docker Swarm等工具,但是Puppet仍然可以用來管理Docker主機和容器。

安裝Vagrant和啟動虛擬機器

首先,你需要安裝Vagrant。Vagrant是一個虛擬機器管理工具,可以幫助你快速啟動和管理虛擬機器。

mkdir ubuntu && cd ubuntu/
vagrant init ubuntu/trusty64; vagrant up --provider VirtualBox

安裝Puppet代理

接下來,你需要在虛擬機器中安裝Puppet代理。

vagrant ssh
sudo su -
curl -fsS https://raw.githubusercontent.com/russmckendrick/puppet-install/master/ubuntu | bash

安裝Docker模組

安裝完Puppet代理後,你需要安裝Docker模組。

puppet module install garethr-docker

編寫Puppet組態檔案

在本地機器上,建立一個名為docker.pp的檔案,內容如下:

include 'docker'
docker::image { 'russmckendrick/base': }
docker::run { 'helloworld':
  image => 'russmckendrick/base',
  command => '/bin/sh -c "while true; do echo hello world; sleep 1; done"',
}

套用Puppet組態

回到虛擬機器中,執行以下命令來套用Puppet組態:

puppet apply /vagrant/docker.pp

驗證結果

執行完上述命令後,你可以驗證Docker是否已經安裝,以及容器是否已經啟動。

docker --version
docker images
docker ps
docker attach helloworld

再次套用Puppet組態

你可以再次執行puppet apply /vagrant/docker.pp來檢視Puppet如何處理已經套用的組態。

內容解密:
  1. 安裝Vagrant和啟動虛擬機器:使用Vagrant快速啟動和管理虛擬機器。
  2. 安裝Puppet代理:在虛擬機器中安裝Puppet代理,以便能夠執行Puppet組態。
  3. 安裝Docker模組:安裝Docker模組,以便能夠使用Puppet管理Docker容器。
  4. 編寫Puppet組態檔案:編寫Puppet組態檔案,以定義Docker容器的佈署和管理。
  5. 套用Puppet組態:執行Puppet組態,以自動化Docker容器的佈署和管理。
  6. 驗證結果:驗證Docker是否已經安裝,以及容器是否已經啟動。
  7. 再次套用Puppet組態:再次執行Puppet組態,以檢視Puppet如何處理已經套用的組態。

圖表翻譯:

此圖示展示了使用Puppet管理Docker容器的流程。 圖表翻譯: 此圖表展示了使用Puppet管理Docker容器的步驟,包括安裝Vagrant、啟動虛擬機器、安裝Puppet代理、安裝Docker模組、編寫Puppet組態檔案、套用Puppet組態、驗證結果和再次套用Puppet組態。

使用 Puppet 擴充套件 Docker 基礎設施

佈署 WordPress 與 Puppet

首先,我們需要移除現有的虛擬機器並建立一個更複雜的組態。輸入以下命令以銷毀虛擬機器:

vagrant destroy

然後,在 ubuntu 資料夾中的 Vagrantfile 檔案內容替換為:

# -*- mode: ruby -*-
# vi: set ft=ruby :
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.network "private_network", ip: "192.168.33.10"
  HOSTNAME = 'docker'
  DOMAIN = 'media-glass.es'
  Vagrant.require_version '>= 1.7.0'

  config.ssh.insert_key = false
  config.vm.host_name = HOSTNAME + '.' + DOMAIN
  config.vm.provider "VirtualBox" do |v|
    v.memory = 2024
    v.cpus = 2
  end
  config.vm.provider "vmware_fusion" do |v|
    v.vmx["memsize"] = "2024"
    v.vmx["numvcpus"] = "2"
  end
  $script = <<SCRIPT
    sudo sh -c 'curl -fsS https://raw.githubusercontent.com/russmckendrick/puppet-install/master/ubuntu | bash'
    sudo puppet module install garethr-docker
  SCRIPT
  config.vm.provision "shell",
    inline: $script
end

內容解密:

  • 這段 Vagrantfile 組態了虛擬機器的基本設定,包括使用 ubuntu/trusty64 映象、設定私有網路 IP 為 192.168.33.10
  • 組態了虛擬機器的主機名稱、記憶體和 CPU 資源。
  • 使用 shell provision 安裝 Puppet 和 Docker Puppet 模組。

接下來,在 ubuntu 資料夾中建立一個名為 wordpress.pp 的 Puppet 清單檔案,內容如下:

include 'docker'
docker::image { 'wordpress': }
docker::image { 'mysql': }
docker::run { 'wordpress':
  image => 'wordpress',
  ports => ['80:80'],
  links => ['mysql:mysql'],
}
docker::run { 'mysql':
  image => 'mysql',
  env => ['MYSQL_ROOT_PASSWORD=password', 'FOO2=BAR2'],
}

內容解密:

  • 這段 Puppet 清單定義了 Docker 的映像檔和容器。
  • 使用 docker::image 資源下載 WordPress 和 MySQL 的映像檔。
  • 使用 docker::run 資源啟動 WordPress 和 MySQL 容器,並組態連線埠和環境變數。

完成後,執行 vagrant upvagrant ssh,然後應用 Puppet 清單:

sudo puppet apply /vagrant/wordpress.pp

更進階的 Puppet 範例

Puppet 的真正強大之處在於使用 Puppet Master 伺服器來管理多個節點。以下是一個範例架構圖:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Docker外掛開發與Puppet自動化佈署

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

圖表翻譯: 此圖示展示了一個 Puppet Master 控制四個 Docker 節點的架構。

在這個範例中,可以使用 Puppet 管理每個節點上的 Docker 和 Weave。Puppet Agent 每隔一段時間會向 Puppet Master 回報並套用組態。