小网站的容器化(上)
戳藍字“CSDN云計算”關注我們哦!
作者 |?王洪鵬
出品 | CSDN云計算(ID:CSDNcloud)
作為一枚程序員,大家幾乎基本都有自己的個人網站,這些網站有的可能是自己開發的有的可能是用一些工具自動生成的,不管我們是用什么樣的方式搭建的網站,如果想對自己的網站進行容器化,可以參考下當前這篇文章。
前兩天剛將自己的網站做了下容器化,所以本文也算是筆者自己的一個個人筆記。接下來的內容里我會給大家講下網站容器化的過程中需要用到的大部分的技術棧,所涉及的每一部分都會詳細寫明具體的實施過程,絕不會出現類似“詳細過程可參考:?https://xxx.xxx”這種動輒甩鏈接的情況。
?
容器化之前的痛點
?
以我自己為例,我自己目前維護的網站主要有我自己的個人簡歷網站、個人博客網站、個人常用工具集網站以及自己的開源項目網站。這些網站不是同時搭建起來的,有的是上學的時候搭建的,有的是工作之后搭建的,由于歷史和網站用途的原因,這些網站在搭建時所采用的方式和技術都不太一樣,差別挺大的。比如我的個人簡歷網站是直接使用vue.js構建的SPA單頁應用,純靜態的網站,相對比較簡單;我的博客網站和個人工具集網站使用的是當時比較流行的WordPress工具進行配置和搭建的,架構比較簡單:PHP + Apache Web Server + MySQL數據庫;我的個人開源項目網站采用的是Node.js + Express SSR進行搭建的。
?
從周圍的同事來看,像筆者一樣因為個人需要維護著幾個自己網站的并不在少數。 每次修改完網站的代碼接下來都需要進行一系列繁瑣的操作,在此我們以個人簡歷網站這種算是最簡單的純靜態的個人網站為例,看下修改一次網站我們需要進行的操作事項:
?
1.?下載網站當前的源代碼
?
對網站進行修改,首先我們需要先將網站當前的代碼clone下來,不論是從公共的Github、Gitlab還是自己搭建的代碼托管平臺。代碼下載完成后本地部署下我們的網站程序,方便接下來的代碼修改、測試。
?
2.?開發新的功能
?
這一步就是對已經下載下來的代碼進行功能的修改,修改過程中需要對功能進行不斷的測試,修改完成后一般還要對新增的功能進行一個大概的整體的測試,以確保新加的功能是正常的,比較有心的同學一般還會對先前已經有的功能在大概測試下,確保加入的新功能沒有影響到已有的功能。
?
3.?代碼編譯
?
這一步的目的也很簡單,就是對前端的項目進行編譯,得到我們需要的靜態資源文件。
?
4.?網站新代碼提交
?
本地開發、測試好后,在部署新的代碼前我們需要將最新的代碼push到我們的代碼托管平臺,方便后續的代碼維護。
?
當然如果代碼的修改量較大的話我們一般還會在代碼的書寫過程中對代碼進行提交,而不是等到代碼全部更新完成后再進行最終的代碼提交。
?
5.?代碼更新
?
接下來我們需要將我們的代碼文件打包下,然后借助一些工具將我們首先打包好的包含新加入功能的包傳到我們的網站服務器。
?
考慮到網站的可用性,在我們上傳新的代碼包到網站的服務器后,我們一般不會直接對網站的文件進行替換,更常見的做法是先將已經在跑的代碼備份下,然后再用新的代碼進行替換,這樣當新的代碼出現問題時我們可以及時的進行代碼的回滾,將代碼再替換回原先的老代碼。
?
6.?網站測試
?
上一步我們完成了新代碼的部署操作,雖然在本地我們已經做過了對代碼的測試,保險起見,我們還要對網站的功能進行一個大概的功能層面的測試,來確保新功能的添加沒有引入太明顯的bug。
?
以上過程放在工作中可能并不復雜,但是工作中一個人一般不會去負責這么多的事情:開發、測試、部署、驗證。一般只是做這里面的某一塊,各個模塊的工作做的也相對比較細致,再加上工作中的項目迭代頻率一般不會很高,所以還是可以接受的。但是如果是放在自己維護的個人網站的場景,里就會變的很麻煩,所有的操作都需要我們自己一個人來進行。
?
網站更新完成后,新增的內容沒有問題可能還好,一旦出現問題前面的這些步驟就得再來一遍,且這類的情況幾乎是不可避免的,比如更新的內容中有錯別字。每次更新基本都等花掉我們少則半個小時,多則數小時的時間。
?
再加上由于用途多樣,我們一般還會維護多個網站,這樣的花在網站發布的時間又得加倍,由于改動一次流程比較多,所以每次不管我們進行多小的改動都很麻煩,幾個錯別字下來都能讓我們感覺身體被掏空,花在這上面的精力不亞于維護一個大的網站,徒勞、無聊且得不償失。
?
以上僅僅是針對諸如個人簡歷這種最簡單的靜態網站進行的說明,個人網站從功能層面上來講可能并不復雜,但其更新過程中需要做的操作還是蠻費心力的。簡單靜態網站的維護尚且如此,一個常規的個人博客網站呢?
?
針對個人博客網站這種情況,一般需要有個web server、數據庫,以最常見的lnmp這種架構為例,除了前面靜態網站的更新中我們已經描述過的步驟之外,我們可能還要對mysql數據庫的結構進行變更,甚至還要mysql的配置進行修改,對數據庫服務進行維護。這些更新都完成之后,過幾天你可能會發現網站出現問題,訪問不了了,上去看下可能是mysql異常了或者web server 本身掛了。這種情況下,如果對linux、mysql、web server比較了解的話還好,如果不了解的話那接下來就需要一次次焦頭爛額的排查。
?
對于大部分程序員來說,最好是只專注于代碼,但現在面臨的問題是,我們既要維護代碼還是維護代碼的運行環境。代碼寫的沒問題,環境卻老是出問題,這對于不太了解運維的同學來說的確是一件很難受的事情。更加難受的是當前使用的服務器,隨著時間的推移,功能已經變的越來越復雜,這個趨勢是必然的,作為小小程序員的我們根本無力阻攔。今天MySQL數據庫掛了,明天web server 不能正常運行了,接下來呢?真的是個無底洞。對于很多程序員來說,既要維護代碼又要負責網站的日常運維工作,簡直不要太難受!
?
Docker 基礎概念
?
我們總結一下容器化前我們面對的問題:
?
1.?手動部署成本代價大,成本高,上文中我們提到過僅僅是一個錯別字的問題,我們就得將整個一系列的操作從頭再操作一遍,很麻煩。
?
2.?隨著時間的拉長,由于發布次數比較多,因此服務器上的環境會變的越來越臟、亂、差:這個路徑放了這個組件的日志、那個路徑備份了數據庫的數據等等,每次一想到升級時需要面對這些蜘蛛網一般的文件分布,能硬生生的將自己的開發沖動給憋回去。
?
3.?如果服務器操作系統版本太低,或者因為其他的原因需要更換其他的操作,這個時候簡直就是一個噩夢。新的服務器上,我們需要將我們的web server、mysql、網站本身,等逐一遷移過去,如果老服務器上的文件分布比較合理那還好,如果分布的比較雜亂的話,這個時候你會發現將將前面的這些文件遷移到新的服務器上后網站并不能正常運行起來,或者運行起來后網站不能正常的進行訪問,這個時候可能是因為你從老的服務器上拷貝錯了配置文件,或者少遷移了一些文件等,此時我們就需要逐步的進行嘗試,如果不想進行嘗試,直接將老的服務器上的所有文件都拷貝過去,網站可能會正常的運行起來,但是之前老服務器上的混亂狀態也會被你帶到新的服務器中。
?
再者即使我們將老服務器上的文件進行整體的拷貝,我們可能會發現網站還是沒辦法正常的運行起來,這個情況的原因很多,比如使用的mysql或者web server或者語言的解釋器本身和當前的操作系統版本不兼容,巴拉、巴拉……總之就是一大堆的環境問題。
?
好在我們有容器,容器基本可以說是我們解決上面一系列環境問題的靈丹妙藥,即使不能輕松的解決所有的問題,也可以解決掉大部分的問題(剩下的一小部分可以通過一些其他的折中方式進行解決)。了解容器云的同學應該知道,容器話的方案其實是有很多的,比如DockerOne 的Docker產品,CoreOS公司推出的Rocket,已經傳統的LXC等等,在此就不一一列舉了,但這些容器化的產品中在企業中使用最廣泛的是Docker,所以接下來的內容中我們也是以Docker為例進行描述。
?
既然說容器可以幫我們解決前面說過的環境不一致所帶來的的問題,那么問題來了,容器是如何幫我們解決這些問題的呢?接下來我們簡單的介紹一下。
?
簡單說來,Docker 由鏡像和容器兩部分所組成。
?
1.?鏡像(Image)
?
鏡像簡單說來就是一個文件,并且是一個用來創建容器的文件,如果大家裝過windows 系列的操作系統的話,那么Docker就特別像windows 里面的”windowsx 純凈版.rar”文件。
?
對于我們來說,最重要的是一個鏡像中不但可以包含我們的應用進程同時也包含了我們的應用進程所需要的運行環境,用Docker 官方的說法就是build once,run anywhere。
?
2.?容器(Container)
?
容器就是一個加了殼的進程,容器在使用方面特別像我們常用的操作系統,有點類似我們常用的虛擬機,但是和虛擬機的一大區別是,容器并沒有自己完整獨立的內核,同一個物理機或者虛擬機上運行容器共用同一個內核。
?
一個容器里面可以支持什么樣的操作,主要取決于我們在容器里面配置的環境是怎樣的。
?
??一句話來概括鏡像和容器的關系就是:容器是鏡像的運行時,鏡像是容器的靜態時。
容器化準備
?
在進行網站的容器化之前,我們需要先部署下容器的運行環境,好在這個過程非常的簡單,Docker 為我們提提供了常用操作系統上的安裝方式,在此我們以Mac、Windows、Linux為例:
?
Mac:https://download.docker.com/mac/stable/Docker.dmg
Windows:https://download.docker.com/win/stable/Docker%20for%20Windows%20Installer.exe
Linux:https://get.docker.com/
?
需要注意的是如果我們使用的是windows系統,需要看下當前操作系統是否支持虛擬化,比如win10 家庭版是不支持的,win10專業版是支持的,這個需要注意下。
?
鑒于我們使用的服務器多數是linux,在此我們就以linux的CentOS7.x為例看下Docker 的具體安裝部署過程。
?
Docker 的安裝方式有多種,比如源碼編譯安裝、下載二進制包安裝以及直接在線安裝等。源碼編譯適合對Docker的把控比較細的場景,比如需要修改Docker本身的源碼,或者更改Docker自身的一些比較精細的配置等;二進制安裝適合對Docker不需要什么精細化的更改且,且服務器不通外網的場景,下載二進制的安裝包后直接進行使用即可;yum 在線安裝適合服務器通外網且對Docker自身沒有精細化控制需要的場景。鑒于我們搭建個人網站的服務器一般都是通外網的,在此我們直接以yum在線安裝為例,看下Docker的具體安裝過程:
# 安裝下redhat的擴展源 [root@localhost ~]# yum install -y epel-release# 清下版本緩存 [root@localhost ~]# yum clean all Loaded plugins: fastestmirror, langpacks Cleaning repos: Ceph Ceph-noarch ceph-source dockerrepo epel kubernetes Cleaning up everything Cleaning up list of fastest mirrors# 看下可以安裝的Docker資源 [root@localhost ~]# yum list | grep docker docker.x86_64 1.10.3-46.el7.centos.14 @extras docker-common.x86_64 1.10.3-46.el7.centos.14 @extras docker-selinux.x86_64 1.10.3-46.el7.centos.14 @extras docker-compose.noarch 1.18.0-4.el7 epel docker-engine.x86_64 17.05.0.ce-1.el7.centos dockerrepo docker-engine-debuginfo.x86_64 17.05.0.ce-1.el7.centos dockerrepo docker-engine-selinux.noarch 17.05.0.ce-1.el7.centos dockerrepo golang-github-fsouza-go-dockerclient-devel.x86_64 kdocker.x86_64 4.9-1.el7 epel python-docker-scripts.noarch 0.4.4-1.el7 epel python-dockerfile-parse.noarch 0.0.5-1.el7 epel python2-avocado-plugins-runner-docker.noarch python2-docker-squash.noarch 1.0.7-3.el7 epel python2-dockerpty.noarch 0.4.1-10.el7 epel python36-docker.noarch 2.6.1-3.el7 epel python36-docker-pycreds.noarch 0.2.1-2.el7 epel python36-dockerpty.noarch 0.4.1-10.el7 epel# 直接yum 在線安裝 [root@localhost ~]# yum install -y docker-io# 啟動Docker 服務 [root@localhost ~]# systemctl start docker# 查看Docker服務狀態 [root@localhost ~]# systemctl status docker ● docker.service - Docker Application Container EngineLoaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)Active: active (running) since Sun 2020-01-12 18:39:31 PST; 9h agoDocs: http://docs.docker.comMain PID: 1544 (docker-current)CGroup: /system.slice/docker.service└─1544 /usr/bin/docker-current daemon --exec-opt native.cgroupdriver=systemd --selinux-enabled --log-driver=journald --insecure-registry=harbor-inner.v190129.163skiff.com --insecure-registry=re...Jan 12 18:39:30 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:30.629872714-08:00" level=info msg="Graph migration to content-addressability took 0.00 seconds" Jan 12 18:39:30 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:30.697717901-08:00" level=info msg="Firewalld running: true" Jan 12 18:39:31 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:31.001124722-08:00" level=info msg="Default bridge (docker0) is assigned with an IP address 172.17.0.0/16. Da... IP address" Jan 12 18:39:31 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:31.250233807-08:00" level=info msg="Loading containers: start." Jan 12 18:39:31 localhost.localdomain docker-current[1544]: ..... Jan 12 18:39:31 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:31.359107953-08:00" level=info msg="Loading containers: done." Jan 12 18:39:31 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:31.359128191-08:00" level=info msg="Daemon has completed initialization" Jan 12 18:39:31 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:31.359142247-08:00" level=info msg="Docker daemon" commit=cb079f6-unsupported execdriver=native-0.2 graphdriv...rsion=1.10.3 Jan 12 18:39:31 localhost.localdomain systemd[1]: Started Docker Application Container Engine. Jan 12 18:39:31 localhost.localdomain docker-current[1544]: time="2020-01-12T18:39:31.364346067-08:00" level=info msg="API listen on /var/run/docker.sock" Hint: Some lines were ellipsized, use -l to show in full.# 配置Docker服務的開機自啟 [root@localhost ~]# systemctl enable docker# 查看Docker 是否安裝成功 [root@localhost ~]# docker info Containers: 5Running: 0Paused: 0Stopped: 5 Images: 26 Server Version: 1.10.3 Storage Driver: devicemapperPool Name: docker-8:3-134990499-poolPool Blocksize: 65.54 kBBase Device Size: 10.74 GBBacking Filesystem: xfsData file: /dev/loop0Metadata file: /dev/loop1Data Space Used: 2.438 GBData Space Total: 107.4 GBData Space Available: 74.84 GBMetadata Space Used: 3.445 MBMetadata Space Total: 2.147 GBMetadata Space Available: 2.144 GBUdev Sync Supported: true可以查看到這些info信息,則Docker就正常安裝上了。
?
除了命令行界面,Docker也有自己的圖形化界面,但一般很少使用,在此我們不再進行描述。
?
以上就是一個簡單的Docker環境的部署過程,接下來我們就會用這個搭建好的Docker環境來部署我們的個人網站。
靜態網站的容器化
?
由難到易,我們先以前面提到的個人簡歷網站為例看下容器化到底可以為我們帶來什么。
?
個人簡歷網站只是一個簡單的純靜態的網站,不會涉及到動態內容的解析,因此在此我們直接用一個nginx?web 服務器來托管我們的網頁文件。
?
那么問題來了,接下來我們是要制作nginx鏡像嗎?答案是:非必須。對于nginx這種很常見的組件,多數的鏡像中心中都可以查到,所以我們直接從鏡像中心中下載一個即可。如果不進行配置的話默認Docker 會從官網的鏡像中心進行鏡像的下載,國內環境直接下載的話可能會比較慢,好在國內也有很多的免費的可以下載容器鏡像的站點,比如阿里云、騰訊云、網易云等。下面我們看下鏡像的具體獲取過程。
# 下載鏡像前我們的服務器中是不包含任何鏡像的[root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE# 直接從網易云的鏡像中心下載nginx鏡像[root@localhost ~]# docker pull hub.c.163.com/public/nginx:1.2.1 Trying to pull repository hub.c.163.com/public/nginx ... 1.2.1: Pulling from hub.c.163.com/public/nginx f46924f139ed: Pull complete a3ed95caeb02: Pull complete 4849cac99801: Pull complete ff472075fa83: Pull complete Digest: sha256:ef9bd0fa2c97429562ecc209772ef07ebb9dfd66dc310ea02739d1f026dbf7b8 Status:?Downloaded?newer?image?for?hub.c.163.com/public/nginx:1.2.1由于我們下載的是公開的鏡像,因此不需要進行docker login 的登錄操作。
# 這個時候我們再查看下,就會發現本地已經多出了一個nginx鏡像[root@localhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE hub.c.163.com/public/nginx 1.2.1 2dc68ff797db 3 years ago 171.5 MB雖然我們沒有進行nginx組件下載和部署操作,但通過這簡單的docker pull 操作我們就獲取到了一個nginx的運行環境。
???
接下來我們需要將我們的簡歷對應的網頁文件放到我們下載的nginx鏡像中,那么怎么進行操作呢?這個時候我們需要用到Docker為我們提供的工具Dockerfile。
?
Dockerfile 是一個純文本文件,里面存放的是我們對鏡像的操作,在使用方式上非常簡單。Dockerfile有自己的一組語法、命令,本文中我們也會用到一些,感興趣的同學可以自己查閱學習下。
?
在創建Dockerfile前我們需要先新建一個網頁文件,作為我們的個人簡歷頁面。
?
1.?準備網頁文件
# 新建一個目錄存在網頁文件和Dockerfile文件[root@localhost ~]# mkdir web1 [root@localhost ~]# cd web1# 新建網頁文件 [root@localhost ~]# vim index.html加下來需要在剛剛新建的index.html文件中加入一點示例代碼,代表我們的簡歷內容:
<!DOCTYPE html> <html><head><meta charset="utf-8"><title>個人簡歷</title></head><body>個人簡歷網站。</body> </html>接下來我們先建Dockerfile文件,將網頁文件加入到已經下載好的nginx鏡像中。
??
2.?書寫Dockerfile文件
# 創建Dockerfile文件[root@localhost web1]# vim Dockerfile在新建好的Dockerfile 中加入如下內容,將寫好的網頁文件添加到我們nginx鏡像當中:# 聲明我們的基鏡像,接下來的操作都是在這個鏡像的基礎上進行的 FROM hub.c.163.com/public/nginx:1.2.1 # 將我們在服務器上寫好的網頁文件拷貝到nginx中 COPY ./index.html /usr/share/nginx/html/index.html # 開放80端口,這樣后面可以通過這個端口來訪問我們的網站 EXPOSE 803.?構建新鏡像
?
前面兩步我們已經準備好了我們需要的文件,這一步我們需要用寫好的Dockerfile 文件來構建新的鏡像,構建鏡像的過程非常簡單,直接docker build即可:?
# 構建鏡像[root@localhost web1]# docker build -t web1:v1 . Sending build context to Docker daemon 3.072 kB Step 1 : FROM hub.c.163.com/public/nginx:1.2.1---> 2dc68ff797db Step 2 : COPY ./index.html /usr/share/nginx/html/index.html---> 5bae75fcb366 Removing intermediate container dd981fe066a7 Step 3 : EXPOSE 80---> Running in 55a50dd13a52---> 81ede6cab5c5 Removing intermediate container 55a50dd13a52 Successfully built 81ede6cab5c5這樣我們的新鏡像就構建好了,新鏡像里面已經包含了我們的nginx?web組件和網站的代碼。如果Dockerfile中存在錯誤,則在構建過程中打出的日志里面會給出提示,提示非常詳細,會為我們指明具體是在執行哪一步時觸發的錯誤,以及錯誤本身具體的信息,方便我們排查和修改Dockerfile文件。
# 查看新構建的鏡像[root@localhost web1]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE web1 v1 81ede6cab5c5 16 minutes ago 171.5 MB hub.c.163.com/public/nginx 1.2.1 2dc68ff797db 3 years ago 171.5 MB?可以看到我們新構建的鏡像已經存在了,
?
4.?運行容器
?
既然鏡像已經有了,接下來我們就可以運行我們的容器了。
# 后臺運行容器[root@localhost web1]# docker run --name web1 -p 80:80 -d web1:v1 7f8203be0991cb34059958f7bd14ee63cc8dedc033f29a1fdcf6c8d4cc016b53--name 是為我們的容器進行命名,此處我們命名為了web1。 -p 是指將容器中我們nginx的80端口映射為我們服務器上的80端口。 -d 是指后臺運行我們的容器,后臺運行運行容器有些類似我們的后臺運行的守護進程。 # 查看下容器是否已經在后臺正常的運行了 [root@localhost web1]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7f8203be0991 web1:v1 "/bin/sh -c '/etc/ini" About a minute ago Up About a minute 22/tcp, 443/tcp, 0.0.0.0:80->80/tcp web1從查詢結果可以看出,我們的容器已經在運行中了,且容器除了映射了我們設置的80端口,基鏡像nginx還默認對外映射了22端口和443端口,且我們nginx的80端口已經被映射為服務器上的80端口。
?
到此為止我們的個人簡歷網站就部署好了,接下來我們測試下網站是否可以正常訪問。
?
直接訪問我們的服務器地址即可:
如果需要進入容器內查看,也很簡單,直接docker exec 進入即可:
# 進入容器 [root@localhost web1]# docker exec -ti web1 bash root@7f8203be0991:/# hostname -I 172.17.0.2進入容器后需要進行的操作就就和我們日常在虛擬機上操作一樣了。
?
可以看到網站容器化后已經可以正常訪問了,整個過程很簡單,且后續的再對網站進行修改時也非常的便捷,代碼修改后只需要修改下Dockerfile文件,打出新鏡像,直接運行容器即可,基本不再需要去糾結哪些惱人的環境不一致帶來的問題。
容器化小結
?
總結一下,整個過程其實非常的簡單,簡單到只需要三個步驟:
?
(1)?書寫Dockerfile文件。
(2)?使用寫好的Dockerfile文件構建鏡像。
(3)?用上面構建好的新鏡像運行容器。
福利掃描添加小編微信,備注“姓名+公司職位”,加入【云計算學習交流群】,和志同道合的朋友們共同打卡學習! 推薦閱讀:消息稱蘋果斥資2億美元收購Xnor.ai;華為2000萬英鎊投資,加速發展HMS手機生態系統;國產SSD將迎來爆發……
如何打造“智能助理”?阿里對話開發平臺這樣做
浪潮十年:“云計算”中崛起,“智慧計算”中爆發
掌握 8 種語言、被阿里點贊,這名德國程序員簡直開掛了!
為什么 k8s 在阿里能成功?| 問底中國 IT 技術演進
170個新項目,579個活躍代碼倉庫,Facebook開源年度回顧
總結
以上是生活随笔為你收集整理的小网站的容器化(上)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何使用 Mybatis 实现数据库 C
- 下一篇: 神作!3万程序员在学,这本深度学习宝典刷