Postgres On Docker-窺探容器服務

lanzhiheng發表於2019-01-06

這篇文章會以容器的方式執行Postgres服務作為例子簡單介紹一些容器管理相關的命令,我們會看到容器的表現像是一個作業系統的程式,而映象則像是一個“原始碼庫”,一切容器的啟動都依賴於它。此外還會介紹容器內的服務與外界打交道的方式,我們或許可以利用容器的這種特性在日常開發中採用容器服務。

容器服務

拋開底層技術還有一些較為高階的叢集服務不談,或許可以把Docker服務簡單理解為**一個託管著映象並能夠利用映象來排程容器的地方。**這些映象根據作業系統的不同或者說Docker版本的不同被託管在不同的目錄下。

每次Docker要啟動容器的時候都會在這個目錄下以它的內部程式尋找相關的映象,如果有相關的映象存在則直接使用,否則會從相關的託管網站下載映象,比如DockerHub

啟動Postgres容器的方式相當簡單,直接執行指令碼即可

docker run postgres複製程式碼

容器啟動之後會顯示一堆日誌資訊

2019-01-02 10:40:46.987 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 54322019-01-02 10:40:46.989 UTC [1] LOG:  listening on IPv6 address "::", port 54322019-01-02 10:40:46.997 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"2019-01-02 10:40:47.020 UTC [52] LOG:  database system was shut down at 2019-01-02 10:40:46 UTC2019-01-02 10:40:47.029 UTC [1] LOG:  database system is ready to accept connections複製程式碼

通常以非容器的方式啟動Postgres服務也會有類似的日誌資訊,只不過由於配置的關係我們無法從命令列直接讀取這些日誌資訊,它們都記錄在相關的日誌檔案中。這種容器的預設啟動方式除了會在命令列直接顯示相關之外,還會佔用一個視窗,這對於開發者而言並不是那麼友好。停止原有的服務並新增-d引數重新啟動,即可讓相關的容器服務以守護程式的形式在後臺執行。

>
docker run -d postgres3ac6e6a43546aed69349d1d523f3c4279a6c595dc848be3d512b73dfc2993879>
docker container ps # 映象已經正常啟動CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES3ac6e6a43546 postgres "docker-entrypoint.s…" 16 seconds ago Up 15 seconds 5432/tcp ecstatic_pascal複製程式碼

這裡似乎有個有用的資訊,以容器方式啟動的Postgres服務是以5432這個埠啟動的,它是Postgres服務的預設埠。我們在系統層面檢查一下這個埠是否被佔用

>
sudo lsof -i :5432>
複製程式碼

可見埠沒有被佔用,可以理解為容器內服務的相關埠完全與外界隔離了,這倒是給我們省了不少事情。要使用這個服務可以進入到容器內部

docker exec -it 3ac6e6a43546 bash # 通過指定容器id向容器傳送相關的命令,並以互動的方式執行root@3ac6e6a43546:/#複製程式碼

登入後通過容器內的Postgres客戶端psql即可連線相應的Postgres服務

root@3ac6e6a43546:/# psql -U postgrespsql (11.1 (Debian 11.1-1.pgdg90+1))Type "help" for help.postgres=#複製程式碼

接著就可以用Postgres的相關命令運算元據庫了,這裡不詳細說。

容器與外界互動

正如前面所說到的,基於容器的服務,它的埠都與外界隔離著,因此不需要擔心埠衝突的問題。但是如果每次使用服務都要藉助Docker命令進入容器內部是很無聊的一件事情,而這種限制下容器帶給人們的好處也很有限。我們需要尋求把容器服務暴露給外界的方法。

簡單來說所需要做的事情只有把容器的內部服務埠跟外界的埠做個對映。之前的容器程式我們先放著不管。重新啟動一個容器,並通過-p引數做埠對映,這裡我把容器內部的5432埠對映到外界的6700

docker run  -d -p 6700:5432 postgres複製程式碼

再次檢視容器列表

>
docker container psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES8f8c80f8177b postgres "docker-entrypoint.s…" 44 seconds ago Up 44 seconds 0.0.0.0:6700->
5432/tcp nifty_mayer3ac6e6a43546 postgres "docker-entrypoint.s…" 17 hours ago Up 6 minutes 5432/tcp ecstatic_pascal複製程式碼

新啟動的容器ID為8f8c80f8177b,注意力集中在PORTS那一列,該容器有一條埠對映的資訊0.0.0.0:6700->
5432/tcp
,檢查一下相關的埠,可以看到有相應的程式在執行

>
lsof -i :6700COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAMEcom.docke 2511 lan 21u IPv4 0xa28bb36c099582e3 0t0 TCP *:6700 (LISTEN)com.docke 2511 lan 23u IPv6 0xa28bb36becd214fb 0t0 TCP localhost:6700 (LISTEN)複製程式碼

這個時候我們可以通過6700埠來訪問容器內的Postgres服務了。不過這裡需要走TCP協議,稍稍做個驗證

>
psql -U postgres -h 127.0.0.1 -p 5432 # Case1psql: could not connect to server: Connection refused Is the server running on host "127.0.0.1" and accepting TCP/IP connections on port 5432?>
psql -U postgres -p 6700 # Case2psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/tmp/.s.PGSQL.6700"?>
psql -U postgres -h 127.0.0.1 -p 6700 # Case3psql (9.6.10, server 11.1 (Debian 11.1-1.pgdg90+1))WARNING: psql major version 9.6, server major version 11. Some psql features might not work.Type "help" for help.postgres=#複製程式碼

幾點需要注意的

  • Case1: 因為容器3ac6e6a43546中服務的埠沒有暴露出來,所以我們無法在本機的客戶端對其進行訪問。
  • Case2: 容器8f8c80f8177b內的Postgres服務所有檔案都託管在容器中,它並沒有在容器外建立SOCKET協議的相關檔案,因此我們無法以預設的SOCKET協議對其進行訪問。
  • Case3: 由於容器8f8c80f8177b把Postgres服務的埠暴露給了外界,我們可以通過明確指定相關的IP和埠讓客戶端以TCP協議訪問相關的Postgres服務。

開發環境下的用途

容器服務雖說管理起來沒有“正常服務”那麼直觀,但這種服務的好處就是隔離性比較好。用運維小夥伴的話來說就是

我不用再擔心當一個Web應用被攻擊它的資料庫服務掛了的時候其他的Web應用也跟著遭殃。

開發環境下是自然是不用考慮當機的事情了,不過假設有些時候你不想通過編譯原始碼,包管理等方式來安裝相關的服務。或者你同時管理著幾個專案而不同專案會有連線不同版本資料庫的需求,這種情況下就可以考慮在開發環境下采用容器服務了。

通過下載不同版本的資料庫服務的相關映象比如postgres:9.x, postgres:11.x。然後基於這些映象啟動相關的容器服務,並暴露特定埠。然後簡單修改應用程式的配置檔案讓應用可以分別連線到對應的資料庫。另一方面,基於容器的服務刪除起來比較容易,不像本地編譯安裝的軟體,基本都要手動去刪除好幾個目錄中的相關內容,很多時候你都會懷疑自己到底刪除乾淨沒有。

總結

本文通過容器來啟動了Postgres服務,並對它進行了一系列的操作,稍微窺探了容器服務到底是個什麼樣的東西,當然比起窺探原始碼這種程度的探索還是顯得比較表面,不過理解這些基本點還是有助於日後的發展。

PS:本文對於容器的管理只是限於“程式”層面的管理,如果需要一個可靠的容器服務單單是這樣是不足夠的,我們還需要涉及到“資料”層面的管理。如果我們把所有東西都放在容器中一旦容器不小心被銷燬,一切努力都將會白費。要知道銷燬一個容器就跟殺死一個程式那麼容易。這時我們可能會需要在容器外維護一份配置檔案,或者為容器記憶體儲資料的目錄做一個資料卷的對映,這樣即便容器被銷燬了,我們依然可以保留工作成果。

來源:https://juejin.im/post/5c31b09251882525c86a3337#comment

相關文章