.NetCore&Linux&Docker&Portainer踩坑歷險記

来源:https://www.cnblogs.com/hohoa/archive/2019/04/21/10743552.html
-Advertisement-
Play Games

最近有一個雲伺服器和資料庫的遷移任務,踩坑爬坑無數次,覺得必須要記錄一下。大家瓜子花生準備好,聽我慢慢講故事#手動笑哭#。 故事背景 公司是做電商業務的,在天貓有幾家旗艦店數據量也很大。阿裡有一個稱為聚石塔的平臺,專門給這些ISV提供各種雲資源,強制綁定了一些業務,原本我們在聚石塔中有一臺ECS和一 ...


最近有一個雲伺服器和資料庫的遷移任務,踩坑爬坑無數次,覺得必須要記錄一下。大家瓜子花生準備好,聽我慢慢講故事#手動笑哭#。

 

故事背景

公司是做電商業務的,在天貓有幾家旗艦店數據量也很大。阿裡有一個稱為聚石塔的平臺,專門給這些ISV提供各種雲資源,強制綁定了一些業務,原本我們在聚石塔中有一臺ECS和一臺RDS部署在華東杭州節點,本月初突然收到阿裡的郵件說是要整體遷移到張北節點,華東節點將會在9月底全部停止服務,並附帶發了一份遷移文檔,要我們儘快遷移。好在我們用到的資源不多,最初覺得遷移過程並不會太複雜,實際還是太天真了。像我這樣只有一臺伺服器和一臺資料庫的用戶遷移過程都謂之艱辛,對於那些有幾十甚至上百實例的ISV,那真是欲哭無淚了。每天看著遷移群里大家的各種吐槽、抱怨、焦急、無可奈何,還有那幾位一整天都在被艾特的阿裡技術支持,說起來都是淚。

於是接下來制定好遷移計劃,發郵件購買要用到的資源,等過了兩天東西到位,就擼起袖子開幹了。

忘了說了,這些東西原先是由另外一位同事負責,然而年後他就開溜了,上級指示我扛過大旗(guo)。


開胃菜

我們的RDS是SQL Server 08 R2版本,阿裡在遷移通知中專門提到了這個產品,而且用到了重要提示字樣,大意是說微軟已經對這個版本的資料庫停止了安全更新,所以張北節點已經不再售賣這個版本的實例,要先在當前節點完成版本升級後再遷移。看了下他的遷移手冊,覺得異常複雜和危險重重,於是果斷放棄官方方案,決定在張北節點買好2016版本資料庫,直接切換數據推送,後來找阿裡的技術支持咨詢了這個方案,也表示可以執行。當然了,我能這樣做是有一個前提的,我們的這個庫是只讀庫,用來接收阿裡的數據推送然後給業務系統查詢,可以理解為只是一個過渡不存儲實際的業務數據,對安全性要求不高,就算丟失也能通過淘寶開放平臺的API去查詢。如果是業務庫,那就只能老老實實的按官方文檔摸著石頭過河了,看群里的反饋,這道開胃菜不好吃,我也算是幸運跳過了第一個坑。

 

初進坑

RDS處理完畢,那就著手開始折騰伺服器,這是一臺Linux的機器,系統是Centos7,主要跑了3個服務:上文提到的RDS數據查詢API(一個dotnetcore2.1的程式)、Rabbitmq實例和它的管理工具、Portainer,由Docker統一管理,而Docker又由Portainer來管理。按照官方文檔,先在原伺服器上創建鏡像,經過漫長的等待(大概40分鐘吧,有的人反映等了大半天最後生成失敗的,心態崩…),然後把鏡像複製到張北節點,然後通過鏡像生成實例,按理說新機器和原機器是完全一樣的,各項服務都應該運行正常,並且專門找技術支持確認了,可實際真的不是這樣。

聚石塔的伺服器只開放30001-30005這幾個埠,於是嘗試訪問一下Portainer所在的30003埠。瀏覽器輸入地址再回車,等了幾十秒後顯示超時無法訪問,一臉懵逼。Ping了一下伺服器IP,沒毛病,又登錄伺服器查看docker和container的運行狀態以及埠映射,都沒問題,又查看埠監聽和防火牆,還是正常,二臉懵逼。

查一下container的日誌,提示運行正常,三臉懵逼。

我的招已經用完了,沒辦法轉向群里咨詢技術支持,回覆說這幾個埠要走工單申請開通,WTF……老實寫工單提交再到群里艾特幫忙快點處理,又陷入漫長的等待中,當時大概2點鐘的樣子。下午5點多工單狀態更新了說正在轉給技術處理請耐心等待,然後,就沒有然後了接著等,到7點還是沒消息決定先下班。

第二天上班發現還是沒有消息,又去群里艾特技術支持,幾分鐘後回覆叫我去給ECS綁定一個安全組,照做後再次訪問30003埠依然不行,長嘆一口氣。又嘗試訪問了一下webapi所在的30001埠,神奇般的成功了#手動黑人問號臉#。咨詢了公司運維,教我幾個命令簡單排查了下,後來因為太忙沒回覆我了,後來又一頓百度谷歌無果,陷入僵局。心理暗自把這個鍋丟給了阿裡,覺得是他們哪裡配置有問題。

事情不能就這樣僵著啊,Portainer起不來程式不能更新,於是打算直接在宿主機上跑一下修改後的dotnetcore程式看資料庫訪問是否正常。按照微軟文檔安裝對應版本的SDK:

安裝好後把發佈文件上傳到伺服器,然後用dotnet命令啟動了程式,一切正常。訪問我的測試入口:

Curl http://locahost:5000/api/values/testdb/123

看到返回了資料庫的測試數據,信心重拾。回過頭重新折騰docker,發現docker死活起不來了,囧:

  

拿著錯誤信息又是一頓百度谷歌,不斷的照網上改配置重啟系統,幾個小時過去依然不行,決定卸載docker重裝。於是依次執行:

yum remove docker*

reboot

yum install docker

docker version

systemctl start docker

然而啟動的時候問題依舊,又是長嘆一口氣。仔細回想了一下,只有yum update對系統做了大的改動,難道是這個問題麽?不知不覺又到了晚上7點多,腦子懵的很決定先下班第二天接著搞。

真所謂一波未平一波又起。


再進坑

早上到公司和微信群的小伙伴吐糟著遭遇,大家勸我重裝系統,我一邊發著捂臉笑哭的表情,一邊默默地上聚石塔後臺點了磁碟初始化,docker啟動不了的問題就算翻篇了,一切從頭再來。

依然還是埠的問題,實在沒轍了只有給阿裡提工單問為什麼埠不通,阿裡工程師先後叫我排查了iptables、埠監聽情況、清除iptables等等還是不行,最後要了我的伺服器賬號上去排查,在工單中看到阿裡的工程師晚上11點多還在幫我排查問題,也真不容易。

終於,在阿裡後面的回覆中事情迎來了轉機,給了我非常大的提示:

從中我捕捉到了2個重要信息,一個是容器的IP,一個是路由解析問題。我馬上百度如何查容器的IP地址,然後試著去ping容器的IP,發現30001埠綁定的容器(172.22.0網段)正常,30003埠綁定的容器(192.168.0網段)無法訪問,那麼這就說明是宿主機和容器網路不通導致的問題。又查看了系統的路由表:

這個路由表有個奇怪的現象,就是192.168.0這個網段指向了2個不同的網卡,分別是eth0和docker0。我知道,eth0是宿主機預設的網關,docker0是docker啟動時自動創建的虛擬網關,但是還不清楚這樣的配置會有什麼影響,於是百度了一下Linux路由的詳細介紹,得知相同的配置會有優先順序的問題,又嘗試著刪除eth0的配置:

route del -net 192.168.0.0 netmask 255.255.255.0

再次用公網訪問30003埠,成功了!!!終於看到了熟悉的頁面:

 

沒那麼簡單

以為事情就此告一段落後面都是平坦大道,想不到問題又來了。通過docker run我新鏡像後發現容器總是自動退出,於是尋找各種讓容器持續運行的辦法,一陣折騰沒有效果,去微信群問小伙伴,問我是不是程式拋異常了,我頓時一種柳暗花明的感覺,立馬查看容器日誌:

docker logs topapi

果然是報錯了:

很顯然,是說我的framework版本不對,但是我的dockerfile中確實引入的2.1版本運行時:

FROM microsoft/dotnet:2.1-runtime

COPY . /app

WORKDIR /app

EXPOSE 5000 80

ENTRYPOINT ["dotnet", "DRP.API.dll"]

退一萬步說,宿主機我也已經安裝過SDK,而且直接在宿主機上運行都是可以的,為什麼通過docker來運行就掛了,百思不得解。只能按照提示中的信息排查是不是少裝了什麼組件,一陣yum install下來還是失敗:

去廣州微軟.net俱樂部的微信群請教別人,兩位大佬給我分析解答了一下,一位說是我的dockerfile在copy文件時漏了一些引用文件,要我重新修改dockerfile,不過經過多次調整測試依然無效,不得不採用第二位的辦法,就是把運行時改為2.2版本:

修改dockerfile為如下內容:

# 添加基礎鏡像
FROM microsoft/dotnet:2.2-aspnetcore-runtime

#容器中系統的工作空間
WORKDIR /app

#拷貝當前文件夾下的文件到容器中系統的工作空間
COPY . /app

#設置Docker容器對外暴露的埠
EXPOSE 5000 80

#運行應用程式
ENTRYPOINT ["dotnet", "DRP.API.dll"]

重新打包鏡像,然後run起來,這次一切都是那麼的自然,docker ps查看容器已經狀態是up了。欣喜若狂,以為即將看到勝利的曙光,接著用瀏覽器打開我的測試入口:

http://xxx.xxx.xxx.xxx:30001/api/values/testdb/123

尷尬的報了500,心中萬馬奔騰….

這次學機靈了,第一時間docker logs,發現是資料庫報錯了:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      => ConnectionId:0HLM4DDINAGJC => RequestId:0HLM4DDINAGJC:00000001 RequestPath:/api/values/testdb/123
      Connection id "0HLM4DDINAGJC", Request id "0HLM4DDINAGJC:00000001": An unhandled exception was thrown by the application.
SqlSugar.UtilExceptions: English Message : Connection open error . A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server)
Chinese Message :  連接資料庫過程中發生錯誤,檢查伺服器是否正常連接字元串是否正確,實在找不到原因請先Google錯誤信息:A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server).
   at SqlSugar.AdoProvider.GetDataReader(String sql, SugarParameter[] parameters)
   at SqlSugar.QueryableProvider`1.GetData[TResult](KeyValuePair`2 sqlObj)
   at SqlSugar.QueryableProvider`1._ToList[TResult]()

很明顯是資料庫連接不上,檢查連接字元串,沒毛病,再次進入僵局。

正在苦惱時,突然想起前面刪掉的那條路由,嘗試重啟網路恢復路由:

service network restart

再次訪問測試地址,確實成功了。可問題又進入了死迴圈,容器內的應用無法訪問。

 

終見天日

經過以上的種種分析後,最終把問題定在了路由這兒。既然是因為同一網段有2個網關,那麼我修改一下docker的預設網段不就可以了嗎?再次面向百度編程,得到兩種方案:

第一種方案,創建新的的網關和路由,然後分配給docker:

service docker stop

ip addr add 192.168.1.1/24 dev bridge0

ip link set dev bridge0 up

vim /etc/docker/daemon.json

加上"bridge": "bridge0"節點並保存退出,再重啟docker:

service docker start

第二種方案,直接修改docker0的預設網段:

service docker stop

vim /etc/docker/daemon.json

加上"bip": "192.168.1.1/24"節點並保存退出,再重啟docker即可。

我這裡採用第二種方式,修改後的路由表為:

重新訪問各種服務,全部都正常運行,到此總算是撥開雲霧見青天。

有個小細節不知大家是否發現,也是我當時存在的一個疑惑,就是前面有提過兩個容器的網段不一樣,按理說通過docker run來的容器應該都是相同的網段,為什麼會這樣呢?後來在折騰Portainer的時候找到了這個問題。

Portainer是一款docker管理工具,簡而言之的說就是把用命令操作的東西可視化,當然功能遠不止這些。Portainer中有一個Stack功能,我並不清楚這是乾什麼用的,只是看到舊的Portainer中的容器綁定了一個stack所以想依葫蘆畫瓢也搞一個:

於是拿stack的配置文件新創建一個,沒想到居然報錯,提示已存在相同名稱的容器。我馬上意識到這個特殊的容器應該是通過stack創建,我刪掉已存在的容器再次創建stack,這次成功了。出於好奇,仔細分析了stack的配置文件:

發現裡面主要是定義了鏡像名、容器名、網路模式、埠映射這些,而其中vhnet這個網路配置讓我很感興趣,轉而查看docker已經配置好的網關:

看到這裡,一種恍然大悟的感覺,你懂的。

除此之外,從前任留下的文檔里可以知道,stack有一種類似熱更新的功能,修改配置文件中的鏡像名後update stack就能實現對應的容器更新,不用起新的容器,這點確實很不錯。更多強大的功能日後也會慢慢學習。

 

我的收穫

經過前面幾天的折騰,我更加熟悉了docker的各種基本操作和配置,也學會了使用新的命令,像docker inspect查看容器信息、docker attach進入容器內部,也加深了在Linux上排查問題的思路理解,學到了新的操作命令。也實際使用docker在Linux上部署了一次dotnetcore的生產環境,收穫頗豐。


遺留的問題

1、     yum update後到底經歷了什麼讓docker跪地不起,報錯原因至今沒搞明白。

2、    為什麼2.1的dotnetcore程式在2.1運行時跑不起來,換成2.2版本就可以。

3、stack是怎麼實現修改鏡像後容器就能生效的呢?

有知道的大佬還請多多指導。

 

總結

錶面上全篇都在講才踩坑的事,但追根究底還是因為自己在Linux方面的知識欠缺和經驗不足。還是那句話,多踩坑,會讓你記憶深刻,會讓你學到意想不到的東西,會讓你的身體變得足夠大,下次碰到坑能一腳踏過去。

故事講完了,大家周末愉快~



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、流的概念 流:數據在數據源(文件)和程式(記憶體)之間經歷的路徑。 輸入流:數據從數據源(文件)到程式(記憶體)的路徑。 輸出流:數據從程式(記憶體)到數據源(文件)的路徑。 以記憶體為參照,如果數據向記憶體流動,則是輸入流,反之則是輸出流 位元組流:FileInputStream用來讀取文件 FileOu ...
  • 選擇排序:是每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,直到全部待排序的數據元素排完。 原理:首先用第一個元素和後面的每一個元素進行比較,如果後面有比第一個元素小的就交換這兩個元素 比較下來會得到第最小的一個元素,放在第一個位置,然後依次拿著後面每一個元素依次這樣比 ...
  • 概述 由於Go語言不允許隱式類型轉換,不同的類型之間的轉換必須做顯示的類型轉換。而類型轉換和類型斷言的本質,就是把一個類型轉換到另一個類型。 不過Go語言必須做顯示的類型轉換的要求也有例外的情況: 當普通 T 類型變數向 I 介面類型轉換時,是隱式轉換的(編譯時轉換);(T->I) 當 IX 介面變 ...
  • 冒泡排序演算法的原理如下:1.比較相鄰的元素。如果第一個比第二個大,就交換他們兩個,否則不交換2.對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最後一對。做完這一步,最後的元素應該會是最大的數。3.針對所有的元素重覆以上的步驟,除了最後一個。4.持續每次對越來越少的元素重覆上面的步驟,直到沒有任何 ...
  • 一、前言 在上一篇C++基礎博文中討論了C++最基本的代碼重用特性——類繼承,派生類可以在繼承基類元素的同時,添加新的成員和方法。但是沒有考慮一種情況:派生類繼承下來的方法的實現細節並不一定適合派生類的需求,此時派生類需要重載集成方法。 二、重載方法及虛函數 我們討論《C++ Primer Plus ...
  • 1.列表是什麼? 列表【list】:列表是由一系列特定順序排列的元素組成。 列表由[]表示 eg: a = ['jackal','jax';'jack','jeef','jacky'] print(a) ['jackal', 'jax', 'jack', 'jeef', 'jacky'] 2.列表的 ...
  • 異常:是在運行時期發生的不正常情況。 異常類:在java中用類的形式對不正常情況進行了描述和封裝對象,描述不正常的情況的類。 異常就是java通過面向對象的思想將問題封裝成了對象.用異常類對其進行描述。 異常體系:不同的問題用不同的類進行具體的描述。 比如角標越界。空指針等等。問題很多,意味著描述的 ...
  • 1.基礎準備 1. ubuntu 18.04 2. docker version 18.09 3. netcore 2.1 2.簡介 自從netcore支持跨平臺之後,以及現在很多公司都是採用容器化部署,今天在比較空閑的時候學習了一下。 3.使用 3.1 創建netcore 項目 ,選擇api程式完 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...