## 一、歡迎來到 Docker 世界
### 1\. Docker 與虛擬化
在沒有 Docker 的時代,我們會使用硬件虛擬化(虛擬機)以提供隔離。這里,虛擬機通過在操作系統上建立了一個中間虛擬軟件層 Hypervisor ,并利用物理機器的資源虛擬出多個虛擬硬件環境來共享宿主機的資源,其中的應用運行在虛擬機內核上。但是,虛擬機對硬件的利用率存在瓶頸,因為虛擬機很難根據當前業務量動態調整其占用的硬件資源,因此容器化技術得以流行。其中,Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然后發布到任何流行的 Linux 機器上。

Docker 容器不使用硬件虛擬化,它的守護進程是宿主機上的一個進程,換句話說,應用直接運行在宿主機內核上。因為容器中運行的程序和計算機的操作系統之間沒有額外的中間層,沒有資源被冗余軟件的運行或虛擬硬件的模擬而浪費掉。
Docker 的優勢不僅如此,我們來比較一番。
| 特性 | Docker | 虛擬機 |
| --- | --- | --- |
| 啟動速度 | 秒級 | 分鐘級 |
| 交付/部署 | 開發、測試、生產環境一致 | 無成熟體系 |
| 性能 | 近似物理機 | 性能損耗大 |
| 體量 | 極小(MB) | 較大(GB) |
| 遷移/擴展 | 跨平臺,可復制 | 較為復雜 |
### 2\. 鏡像、容器和倉庫
Docker 由鏡像(Image)、容器(Container)、倉庫(Repository) 三部分組成。
Docker 的鏡像可以簡單的類比為電腦裝系統用的系統盤,包括操作系統,以及必要的軟件。例如,一個鏡像可以包含一個完整的 centos 操作系統環境,并安裝了 Nginx 和 Tomcat 服務器。注意的是,鏡像是只讀的。這一點也很好理解,就像我們刻錄的系統盤其實也是可讀的。我們可以使用`docker images`?來查看本地鏡像列表。
Docker 的容器可以簡單理解為提供了系統硬件環境,它是真正跑項目程序、消耗機器資源、提供服務的東西。例如,我們可以暫時把容器看作一個 Linux 的電腦,它可以直接運行。那么,容器是基于鏡像啟動的,并且每個容器都是相互隔離的。注意的是,容器在啟動的時候基于鏡像創建一層可寫層作為最上層。我們可以使用`docker ps -a`?查看本地運行過的容器。
Docker 的倉庫用于存放鏡像。這一點,和 Git 非常類似。我們可以從中心倉庫下載鏡像,也可以從自建倉庫下載。同時,我們可以把制作好的鏡像 commit 到本地,然后 push 到遠程倉庫。倉庫分為公開倉庫和私有倉庫,最大的公開倉庫是官方倉庫 Dock Hub,國內的公開倉庫也有很多選擇,例如阿里云等。

### 3\. Docker 促使開發流程變更
筆者認為,Docker 對開發流程的影響在于使環境標準化。例如,原來我們存在三個環境:開發(日常)環境、測試環境、生產環境。這里,我們對于每個環境都需要部署相同的軟件、腳本和運行程序,如圖所示。事實上,對于啟動腳本內容都是一致的,但是沒有統一維護,經常會出問題。此外,對于運行程序而言,如果所依賴的底層運行環境不一致,也會造成困擾和異常。

現在,我們通過引入 Docker 之后,我們只需要維護一個 Docker?鏡像。換句話說,多套環境,一個鏡像,實現系統級別的一次構建到處運行。此時,我們把運行腳本標準化了,把底層軟件鏡像化了,然后對于相同的將要部署的程序實行標準化部署。因此,Docker 為我們提供了一個標準化的運維模式,并固化運維步驟和流程。

通過這個流程的改進,我們更容易實現 DevOps 的目標,因為我們的鏡像生成后可以跑在任何系統,并快速部署。此外,使用 Docker 的很大動力是基于 Docker 實現彈性調度,以更充分地利用機器資源,節省成本。
哈哈,筆者在使用 Docker 過程中,還發現了一些很棒的收益點,例如我們發布回滾的時候只需要切換 TAG 并重啟即可。還比如,我們對環境升級,也只需要升級基礎鏡像,那么新構建的應用鏡像,自動會引用新的版本。(歡迎補充~~~)
## 二、從搭建 Web 服務器開始說起
### 1\. 環境先行,安裝 Docker
現在,我們需要安裝以下步驟安裝 Docker。
* 注冊帳號:在[https://hub.docker.com/](https://link.segmentfault.com/?enc=3fEqfDbhnMyUeVkm36SPrg%3D%3D.uXjJrY0%2BMvh8QvTRFQnOdJ89%2Fxr7%2B2opfffmQFRAsRc%3D)注冊賬號。
* 下載安裝
> 官方下載地址:(Mac):[https://download.docker.com/mac/stable/Docker.dmg](https://link.segmentfault.com/?enc=bnhl4MQZqJrkm%2B0b9Um0SQ%3D%3D.xlledHBMTUkBsEQKtHjGkmdpv%2FYjsoe%2F839Sn%2B7rI%2FAYk6XjLzOgr5M2A1u0SHujNZSdX632TG8FIDrzlaodpw%3D%3D)
> 阿里云下載地址(Mac):[http://mirrors.aliyun.com/docker-toolbox/mac/docker-for-mac/](https://link.segmentfault.com/?enc=1%2BajUSvB5%2BH87EwgRMK7Xw%3D%3D.%2BHuTJitVL%2BI9O9YD9llOOlEYXLwIBr8AovkF7qVCMpeEnce7d9CdckBQUaSzQriUXb7YcjgqT0UxVl7M4CQ15V%2BupVUPJx%2Fv68Ph%2BDILPgAkry6ekK7BDBkEGK8Y6JvTN3LOmZzEFH4FsKx7y88aIQ%3D%3D)
> 阿里云下載地址(Windows):[http://mirrors.aliyun.com/docker-toolbox/windows/docker-for-windows/](https://link.segmentfault.com/?enc=TG%2BloDqMAd9o%2BV6H5dQ42Q%3D%3D.8rwSadWUVeYK9gr44D6G8JqzS%2FIq1D7bmWuIDQ602psIrCn58%2FUw1y0j%2BWfenvBsH3IVU6jX8N6lNjELdBynSZlUZPgwbQZIxpywwX7Q19k%3D)
* 安裝指南
這里,雙擊剛剛下載的 Doker.dmg 安裝包進行安裝。

安裝完成后啟動, Mac 頂部導航欄出現了一個圖標,通過菜單可以進行 docker 配置和退出等操作。
> 官方指南:[https://docs.docker.com/install/](https://link.segmentfault.com/?enc=5lxUhqldVNoPSo%2FZxZw3ig%3D%3D.mDqLkQAHTGH%2BqMatUfHQPnQvE7slGieqcsFk4i%2BR5lhMxs5iPaBmbI%2FsPaYh%2BeDz)
> 阿里云指南(Linux):[https://yq.aliyun.com/articles/110806?spm=5176.8351553.0.0.468b1991jdT95t](https://link.segmentfault.com/?enc=oDkJ2YpaOIgRowA%2Fd20vIA%3D%3D.d94qt1xshFm%2F0ACUlr0cskq3DB3y8aQ%2BVNMD58zdYcoskBKh8wmhZp%2FleHlWuSNtw6lErcQjpV9kVQ6iErgqqyR4vkSpuG8RdD69bbQV%2Fd4%3D)
* 設置加速服務
市面上有很多加速服務的提供商,如:DaoCloud,阿里云等。這里,筆者使用的是阿里云。(注意的是,筆者操作系統是?Mac,其他操作系列參見阿里云操作文檔)?

右鍵點擊桌面頂欄的 docker 圖標,選擇 Preferences ,在 Daemon 標簽(Docker 17.03 之前版本為 Advanced 標簽)下的 Registry mirrors 列表中將
`https://xxx.mirror.aliyuncs.com`?加到"registry-mirrors"的數組里,點擊 Apply & Restart 按鈕,等待 Docker 重啟并應用配置的鏡像加速器。

> 阿里云操作文檔:[https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors](https://link.segmentfault.com/?enc=OtIYF2HOo8dYFakbguLSLA%3D%3D.Drl0a7eEpFdIDzAfvhxGV8W9iWvaLMi36Pldhg%2BP8v5CmKytMGchmp6ojmZMlKVwXZQFLELI9r%2BzjEOAPfZxlg%3D%3D)
* 查看版本
至此,我們已經安裝完成了。這里,我們來查看版本。
~~~applescript
docker version
~~~
查看結果,如下所示。

### 2\. 實干派,從搭建 Web 服務器開始
我們作為實干派,那么先來搭建一個 Web 服務器吧。然后,筆者帶你慢慢理解這個過程中,做了什么事情。首先,我們需要拉取 centos 鏡像。
~~~css
docker run -p 80 --name web -i -t centos /bin/bash
~~~
緊接著,我們安裝 nginx 服務器,執行以下命令:
~~~awk
rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
~~~
安裝完 Nginx 源后,就可以正式安裝 Nginx 了。
~~~cmake
yum install -y nginx
~~~
至此,我們再輸入`whereis nginx`?命令就可以看到安裝的路徑了。最后,我們還需要將 Nginx 跑起來。
~~~ebnf
nginx
~~~
現在,我們執行?`ctrl + P + ?Q`?切換到后臺。然后,通過?`docker ps -a`?來查看隨機分配的端口。

這里,筆者分配的端口是`32769`?,那么通過瀏覽器訪問`http://127.0.0.1:32769`?即可。

大功告成,哈哈哈~
### 3\. 復盤理解全過程
現在,我們來理解下這個流程。首先,我們輸入`docker run -p 80 --name web -i -t centos /bin/bash`?命令會運行交互式容器,其中`-i`?選項告訴 Docker 容器保持標準輸入流對容器開放,即使容器沒有終端連接,另一個`-t`?選項告訴 Docker 為容器分配一個虛擬終端,以便于我們接下來安裝 Nginx 服務器。(筆者備注:Docker 還支持輸入`-d`?選項告訴 Docker 在后臺運行容器的守護進程)
Docker 會為我們創建的每一個容器自動生成一個隨機的名稱。事實上,這種方式雖然便捷,但是可讀性很差,并且對我們后期維護的理解成本會比較大。因此,我們通過`--name web`?選項告訴 Docker 創建一個名稱是`web`?的容器。此外,我們通過`-p 80`?告訴 Docker 開放 80 端口,那么, Nginx 才可以對外通過訪問和服務。但是,我們的宿主機器會自動做端口映射,比如上面分配的端口是?`32769`?,注意的是,如果關閉或者重啟,這個端口就變了,那么怎么解決固定端口的問題,筆者會在后面詳細剖析和帶你實戰。
這里,還有一個非常重要的知識點`docker run`?。Docker 通過 run 命令來啟動一個新容器。Docker 首先在本機中尋找該鏡像,如果沒有安裝,Docker 在 Docker Hub 上查找該鏡像并下載安裝到本機,最后 Docker 創建一個新的容器并啟動該程序。

但是,當第二次執行??`docker run`?時,因為?Docker 在本機中已經安裝該鏡像,所以 Docker 會直接創建一個新的容器并啟動該程序。

注意的是,`docker run`?每次使用都會創建一個新的容器,因此,我們以后再次啟動這個容器時,只需要使用命令`docker start`? 即可。這里,`docker start`?的作用在用重新啟動已存在的鏡像,而`docker run`?包含將鏡像放入容器中`docker create`?,然后將容器啟動?`docker start`?,如圖所示。

現在,我們可以在上面的案例的基礎上,通過`exit`?命令關閉 Docker 容器。當然,如果我們運行的是后臺的守護進程,我們也可以通過`docker stop web`?來停止。注意的是,`docker stop`?和?`docker kill`?略有不同,`docker stop`?發送 SIGTERM 信號,而?`docker kill`?發送SIGKILL 信號。然后,我們使用?`docker start`?重啟它。
~~~crmsh
docker start web
~~~
Docker 容器重啟后會沿用?`docker run`?命令指定的參數來運行,但是,此時它還是后臺運行的。我們必須通過`docker attach`?命令切換到運行交互式容器。
~~~arduino
docker attach web
~~~
### 4\. 不止如此,還有更多命令
Docker 提供了非常豐富的命令。所謂一圖勝千言,我們可以從下面的圖片了解到很多信息和它們之前的用途。(可以直接跳過閱讀,建議收藏,便于擴展閱讀)

如果希望獲取更多信息,可以閱讀官方使用文檔。
| Command | Description |
| --- | --- |
| [docker attach](https://link.segmentfault.com/?enc=51nzk%2FmYh7Vag4K9a%2BD09A%3D%3D.nkCKD6a6VjAFWjJhoJidH4r71L8lcjY%2Ff6XUW1jmOwTDhlrPykE0yokDfxz8p4slHV5VrnmjQ%2BSCYZsV5wiDwg%3D%3D) | Attach local standard input, output, and error streams to a running container |
| [docker build](https://link.segmentfault.com/?enc=3BYam3VH0BKx0Fd3gNyrEA%3D%3D.EXVBtPbrq96uqMMJ1houSs%2B7u6z5Z5kkUjU%2Frh2FdSae9iT8AnFkpPifNCgZ%2BX8XaiOGL6D0tNeWBBiNH8z7hw%3D%3D) | Build an image from a Dockerfile |
| [docker builder](https://link.segmentfault.com/?enc=YQ0YYLgcYCe2uXZ%2BVzkSCg%3D%3D.X7QOuM64T3WucLhxoO4BeYYR6ehwp7ivyPNEAzPgflwbdgkzHwGyBHoRvDNB5%2F9jpxbpFnnbNYmgMTDF%2B6PjNA%3D%3D) | Manage builds |
| [docker checkpoint](https://link.segmentfault.com/?enc=XXOvu0bllsqwkhjtDYpiMA%3D%3D.4eDGhpSw1Ji10PPL%2F5NokLnoPN9Cu1eJpMvmTV0T09nhqo2ZjbPa2sMFI%2Bu8krIFes%2Ft8H02SjRTHT5X2z8fftagGobuj1OTZx%2FQMe9lYGE%3D) | Manage checkpoints |
| [docker commit](https://link.segmentfault.com/?enc=qrfWP51ghwkPkP%2F6sqN3Dg%3D%3D.veAuG%2B4zofT%2FMNGCGwSXoOHvlf2S1AqxI60pok4zDDlFuCzie9inOw5ke%2BGfw8aLpVVx82guQCRgxV6yeo9Qkw%3D%3D) | Create a new image from a container’s changes |
| [docker config](https://link.segmentfault.com/?enc=jPLOQt25RhZqDnoylXDZzg%3D%3D.cIFim9OLPTRk34XBViNGhCYjZDrsIxDLyYWgDS3wV0kV41TZjLA%2BHbZCsOpAhV4dbfllQxYDIz6YD5GG3%2BiwKA%3D%3D) | Manage Docker configs |
| [docker container](https://link.segmentfault.com/?enc=L%2BBRU6YR9KvoiJHihIiGsQ%3D%3D.ZNP1N3Cb7spvAmw5N9S8JtfLItV1lVvDE%2BM26kRM3sAaBTTPUIdcKFNryK7CI%2FegKDNQszq0gx%2F0krIRj6vIiQ%3D%3D) | Manage containers |
| [docker cp](https://link.segmentfault.com/?enc=sFmdnmgqFqcJW6n1%2FzRlwA%3D%3D.5PeuMi%2FcKuQdqWzqPAsj5BjGmFIvtVZ9Qbg7cS3DZeuGqTqFMSobL5ttqy%2BvNvpyYvlS86mpSKx%2Fvq4wfkgj8g%3D%3D) | Copy files/folders between a container and the local filesystem |
| [docker create](https://link.segmentfault.com/?enc=LwmPIT8hOq0tGRZIGJacZA%3D%3D.Io53d5sHLjeFoBd9OamR274wia5gsaKej8eZfS0KrDwbnliX3Lc869ScrbF6dniCd0N0jk50gbc3ifQ13NbUTw%3D%3D) | Create a new container |
| [docker deploy](https://link.segmentfault.com/?enc=JmoMIEdLy5mEpvsWG8SZdA%3D%3D.Vd3SqG5z34luyBxKMOjcl3QreD0ANN7tUJO2KDbgf0EkK8itr5MMrk2V%2B4ZEmK7ultlV3DGX68RkQN40wRHZIw%3D%3D) | Deploy a new stack or update an existing stack |
| [docker diff](https://link.segmentfault.com/?enc=SmtBFPzkpYFt5NtuOMj%2Fgw%3D%3D.kOl02rOsin5lgCxFI7bi%2FKeltFXadlM%2BlLNc0UW0f85JVHjtIphTln%2FsGTGXkZLmnVoSq9Os5xXuoO%2BifL1o4A%3D%3D) | Inspect changes to files or directories on a container’s filesystem |
| [docker engine](https://link.segmentfault.com/?enc=E1D8woQ%2Bp0BGGyzpmYldsA%3D%3D.iZnNIhc7nzAXADLTg2%2Bbd%2Fqu6wUy7MnPBMXjcKBz8%2F4nOVhxPaJsTjrIj%2BogPOEJ8JHeykg9sENVum73FqfLvA%3D%3D) | Manage the docker engine |
| [docker events](https://link.segmentfault.com/?enc=d4%2FGiguEgTZQknJBFBjdjg%3D%3D.1mF5k%2FJgb7xIa2dCHW5e1H1PJBUYqmKZvlikAOCvtDk6Yr6QZ218FKgqOo6NtVEx7jjX0nVxFS4nSqKJXlF6Fg%3D%3D) | Get real time events from the server |
| [docker exec](https://link.segmentfault.com/?enc=vSytHkN0UjCeNuMcjr577g%3D%3D.ArCnPVjF2zabfQylIY63oW4ypTNTN1jH7Rzp37mjC1%2BEXmA2VORrwchxtObFsAW2QfL4jkiLoCtHOZUfD8UVkg%3D%3D) | Run a command in a running container |
| [docker export](https://link.segmentfault.com/?enc=MbapZ2bztrQw6IE3QzR45A%3D%3D.L3UxIGQtBKDTBUQgJEaO3Ko4pZiovE1nqv5bngMs6pUb7ssrAB%2BWjWvmxUFvQv9zma%2BOw%2F%2B4xlHNC5Ez3M%2FLdw%3D%3D) | Export a container’s filesystem as a tar archive |
| [docker history](https://link.segmentfault.com/?enc=iEm0PS2UQUHi6pXieD790Q%3D%3D.DE%2FscQYtksxm70NAnW%2FBg%2FhovWMEjRs9%2Fy%2FscVaPliaueb%2FeX%2BdasrxxDSqpstEzHQAJcTvl1VqTsoLKMkpEVA%3D%3D) | Show the history of an image |
| [docker image](https://link.segmentfault.com/?enc=modvyNQtJB9HAthXPxB%2FEA%3D%3D.io%2FRQWwBxR0iCZKBBHROHkSHB%2FviIhEeUvCzFKLELRoWy6poZ27whdnrCSURTujkVViNsKI5a0xNEf2vW6xLXw%3D%3D) | Manage images |
| [docker images](https://link.segmentfault.com/?enc=nEGYkN9cTQH0t%2Fu1BrdtHg%3D%3D.GgK9H4aeSuHFKKQ1j4FMSvndGw2of5G0aCTSZUQvKApVzs%2BtkNTrVuR%2BSKGcrH45jXbAukdGWrW6f5EThZPTsA%3D%3D) | List images |
| [docker import](https://link.segmentfault.com/?enc=nSDclK26nSeDzesHDESF3w%3D%3D.gLc4WiWCzmmzPRb07kH2jqk6yMMQtMJQzvY2OkA4tbqv32iGimrC0jYzURFdZGEroh526KPUX0Kw3Q4H40uGVw%3D%3D) | Import the contents from a tarball to create a filesystem image |
| [docker info](https://link.segmentfault.com/?enc=X3gHvFHcotfDinGAqDimtg%3D%3D.pHsYmVn9kFznJUQ1OoN9%2Bynyo%2FQALl4CIaBEO5ubjv4koZ2Dz5iouDFH4LVA2yKcn0q%2BA5OXaeJQJB6xCYhdHA%3D%3D) | Display system-wide information |
| [docker inspect](https://link.segmentfault.com/?enc=ZVKZu5BE1ECURq48Fs9EOg%3D%3D.UyPryKWTcOa9LCD%2FVs%2BJTyH3zppCz%2BDcuQ2BeewCUYjECiImGT4vVNcDH5LvjxgvKNdqIyT9n5QMwF8gC6bdnA%3D%3D) | Return low-level information on Docker objects |
| [docker kill](https://link.segmentfault.com/?enc=CXYeKunk%2FQ%2F9vScMzDsc1Q%3D%3D.za%2BPuQqieYpWmK%2BPc7shc7vwhJlulTPyuOBdimNUAIOshIBU47DeMRcTxI6c%2B9HumMB4z1L2TCyF9kkkk3opeg%3D%3D) | Kill one or more running containers |
| [docker load](https://link.segmentfault.com/?enc=ZIrBzU5V%2BhgXh9s%2BlF%2Bxjw%3D%3D.eIJeSSzJlnkodVIbS9if0SZPmCdu6xDXpJHRFznweX%2BSCgCCVsW6%2BTkv6Lqej8Y4Uer2iy1251VTX8LTU2yP1A%3D%3D) | Load an image from a tar archive or STDIN |
| [docker login](https://link.segmentfault.com/?enc=hWx4rzbQnXNZdhmScngMbw%3D%3D.m4%2BZR1Bt5sYBWH0FlnF0EmquOfTU2PyXOqUwaw8dCkoR%2BvnZPOGdOT2sLvibIRL9WRUMNMw06nRsnqQG1sfYYg%3D%3D) | Log in to a Docker registry |
| [docker logout](https://link.segmentfault.com/?enc=cAbVFwoIWI2p2IjvpJB43w%3D%3D.UE431MHWh4U2w436%2F52wgvKmObRKPJNgr4CD0oNbZQzgHRKuuLPbcMW3bMw2Ys4Q1J1ujIhaVhNap11rtbJ1%2Fw%3D%3D) | Log out from a Docker registry |
| [docker logs](https://link.segmentfault.com/?enc=T2tVc217zkCvk9lRRwwv6A%3D%3D.jMH%2BC7boH1sQ9hv%2BAlM9xCUyouyhwTybK9ZabSqisoo%2BXDIsdl7w3FpEDae4Oq3NxYjBsXMJtwWhrv1q1Fve1w%3D%3D) | Fetch the logs of a container |
| [docker manifest](https://link.segmentfault.com/?enc=uvdNpkI5alDwXYqZ00oqaA%3D%3D.3QMWIRCJT1YCgZtJooSHNLdQqeyo6pv9LC6Oz5XqIsLfPYqowT5mslLcSm2gNNPyec5kY30xFEm17xjeCFImdw%3D%3D) | Manage Docker image manifests and manifest lists |
| [docker network](https://link.segmentfault.com/?enc=QVc60iWjbU60%2FIS0kjwqlA%3D%3D.sRPLW86MARsdI5nb2QOXXbnZvzFtXUYBVzCymTcsaQ1KmwlHe4OvP%2Fyit7XVZFpGH3HYfWLt0Y58sRjgVta1iQ%3D%3D) | Manage networks |
| [docker node](https://link.segmentfault.com/?enc=kWufUa6d4aiEHjvovEg36w%3D%3D.n5xERDJALwuzwhsVS%2Bpd4qU%2BnJlfzUa22ytHaUxmXxNEvpVr8t5RoD%2F9i8ifxFe9BucgKifb1%2Bg%2BEWEzMcyDJw%3D%3D) | Manage Swarm nodes |
| [docker pause](https://link.segmentfault.com/?enc=NaRIrO8EJvZCX9SiZBGgAQ%3D%3D.FDLBCtNQ2wBwJYg0%2Ftv0Xl58535QbWtEfO4d9sfkx3aAedJek%2BHNRKAeMqn55pg5YE1bIW%2B%2FyrELZgIUSqVinQ%3D%3D) | Pause all processes within one or more containers |
| [docker plugin](https://link.segmentfault.com/?enc=1EwNOSXc9jXiWbmPO06mxQ%3D%3D.DW%2BnDBjAsQUQL01gcJqcFu4QTET6gi3r3vkkxzQU5CaeotmVHbJ6%2B1wTpZ0T1mzkOTIfJV3HiE8ZMnI0fWSAxA%3D%3D) | Manage plugins |
| [docker port](https://link.segmentfault.com/?enc=2wcNoVhue508Y%2BvoXnvJjA%3D%3D.zAP708hhFU3RnXiPVzlnmcmsd7mAnNXIEMVBmtWxDpR%2Bc7m8M9zojaJvl7YNMGPK9%2Fg83rwG4QpmQCmd6hWatQ%3D%3D) | List port mappings or a specific mapping for the container |
| [docker ps](https://link.segmentfault.com/?enc=5HcUHYVgTkSeFWMA2ubAQA%3D%3D.Zt3s69kBPi7t0BlBLUZtTXqSPPVXLK5ho0fU8zwMTZYtmkI8h7lOMFjJ3%2BUhVsH4YmVVdx8lw6ETl9i2QP27SQ%3D%3D) | List containers |
| [docker pull](https://link.segmentfault.com/?enc=gMq%2Fpi35tOm%2Fz4IBLjDgRQ%3D%3D.LmQycLyZsbYai9y70yuPCiS55ysSCfi3PSwKXBgsWHjWZ%2BXOTEnqiu2BiITzH1QirbwShtjp1WAGUjLmnd11fg%3D%3D) | Pull an image or a repository from a registry |
| [docker push](https://link.segmentfault.com/?enc=n7mw0HTiflx7miJ%2F2kjkWg%3D%3D.qfGyO%2F3Cm3608HsQC8FtYDmLTsJ7VHjtXFxzEAHhynbhebNml87mWXdpVq3BIWh9v%2BolrRhhoQoBUfXcV5nBbQ%3D%3D) | Push an image or a repository to a registry |
| [docker rename](https://link.segmentfault.com/?enc=c3c0rnJSO5NIwR0vClGy0A%3D%3D.KfElnfykQ8FMlDyRIKkNWlv5deOTr0Dn3DBsbaPguafkP9oHtq29J%2BfUzpXtrIB95IQjjGG%2FRSMQK258N%2B4H7Q%3D%3D) | Rename a container |
| [docker restart](https://link.segmentfault.com/?enc=XDo8v1D5vizWAZt%2F4s7jww%3D%3D.BXZf%2Fauec%2BOMGeeYy9CjFfDsFu2kRQEwrSCFhFhBZPbqsxi8SP8tChImTUW1zS56npXRBi6gJsnMcfxhWBiMnQ%3D%3D) | Restart one or more containers |
| [docker rm](https://link.segmentfault.com/?enc=s8qGvgOGQys35t4C2do57w%3D%3D.eXc6C3EkP0VLKrJjZYDRadoXsLv3z7Wgc70iKrnXsKSLVk5FgvgjPo6VyUlmr9xtgDpsYh8Ht7G3os7LGaU21A%3D%3D) | Remove one or more containers |
| [docker rmi](https://link.segmentfault.com/?enc=s8VRuS%2B9YV6aokKs4KC6CA%3D%3D.4j%2BBZsBGa2UkKxUmEE7BJLWFkJt0cLj%2FOR08Z%2BGG1UORlPbSq0wKKW%2BxxFLulNwWKvyudYWh5SIEzfwejthMWA%3D%3D) | Remove one or more images |
| [docker run](https://link.segmentfault.com/?enc=KscT86Lpt8yjfqJ5uWEJ9w%3D%3D.NI33A%2BP8Pk%2F%2F%2Bfz2vLPYA6Q%2FPwD79htmSk0VreEgTGU4fJEnTLVo%2B45V%2BUocSqVtYe8VowHkar1HGMOXdSb%2BXg%3D%3D) | Run a command in a new container |
| [docker save](https://link.segmentfault.com/?enc=YZJ3%2BMQZy6CkWVeQpazvjw%3D%3D.N6OEgaYv6CgSn5oARib%2Bptb7lsKy%2FkcARIHbLF%2BgkhM%2BTcQz1l424V7R9QST0SBAccOEwG%2FCuhDACOTiFvFVpw%3D%3D) | Save one or more images to a tar archive (streamed to STDOUT by default) |
| [docker search](https://link.segmentfault.com/?enc=pIOn4OyKRdDOzl8ISyqGRg%3D%3D.onoGDK66CdTYMdw%2BzF0mReOIeVyOb9N1bKkTnNcQvYZgIVL4Eo4o4iKX%2BzuNcYoNLHp6NC0YNLBNlefJhtJPdQ%3D%3D) | Search the Docker Hub for images |
| [docker secret](https://link.segmentfault.com/?enc=3ldCnXaSBU4ue%2BJalZfaTg%3D%3D.XhbQRNQ72umQM%2BH5yc33tVkpRt4Bcnrw%2BQzs37iKle7iFOOI64ZglYE7tTnPJbSKLibiadfcwPAdDNLuPNx4mA%3D%3D) | Manage Docker secrets |
| [docker service](https://link.segmentfault.com/?enc=YuncbzmIgqOir0q00l1Taw%3D%3D.YiEL0JXEF3WHAQop%2Bvo7mGdeedzAyIq9Zo1lWc%2B0dWkLT5rpnov2bHGpM8Yedqxgg7SJaw3HA4okPUdJnbCWLQ%3D%3D) | Manage services |
| [docker stack](https://link.segmentfault.com/?enc=6GKntnIHP4lPLQ20OMPO1w%3D%3D.AMSb%2BVfJlu6bqLRxYTQ2mZhc3gwllUxP%2FzTcdAPLUveN4IrqIXUXBIqc23VootiEskETDEOke%2BzB0mu4lQd8pg%3D%3D) | Manage Docker stacks |
| [docker start](https://link.segmentfault.com/?enc=w4Jok7qqqb0YFbzX1ng1Vg%3D%3D.w80DypaGn61v99PrFKw7jLhcBuZjxjz%2Bx1f66%2Bruj3SScuWx6fr9NghtcPeC1EIrbOMJbfLGdIdWzl0wuYzJLg%3D%3D) | Start one or more stopped containers |
| [docker stats](https://link.segmentfault.com/?enc=Kuyt0bQjvI%2FMpiy1wPpvrw%3D%3D.HDgBF3%2BFxnFs4ZhLnEw2%2BSdIA3ovctZ8XumMD4a3KZNkL%2FIN%2FlMS1JUfPQH7ftLiuDAZ%2FONmfr0eAISEUj59Sw%3D%3D) | Display a live stream of container(s) resource usage statistics |
| [docker stop](https://link.segmentfault.com/?enc=C2umDkIV33iiHWVENJ4ymQ%3D%3D.T8VeMJ5qW7uDS7DdteqEuIe0WAruUvwI0b%2Fgy90j%2FqPLNOtKfqh%2FWtWiMkKy1V9RXCyg4lJZcVRkXQuzyoOIRQ%3D%3D) | Stop one or more running containers |
| [docker swarm](https://link.segmentfault.com/?enc=XCcYJm%2F2U7BsJhOEX%2B8FrA%3D%3D.qrTfdjcM3bTMn6NWM6ReOFjlvGFfvTZtTmzmvKYh%2B0MXIHyabU7cGCMFvw1nYORJTbfP62oJ6yQhc1GVeVDbTA%3D%3D) | Manage Swarm |
| [docker system](https://link.segmentfault.com/?enc=ClT4vJJ%2F8lpHgHqFqVcNFQ%3D%3D.6OoeKWTwVGz4DaFhpviAO0f%2Bn4pygkUjb50Qzz6eXBpyZVObbKp%2FsG6FGWq9AyAZ6LIIYSXq3PcTHZ5BDlZrDA%3D%3D) | Manage Docker |
| [docker tag](https://link.segmentfault.com/?enc=2xGYq%2B5oHJN8m333aTqDSw%3D%3D.lfu25PCvLK7QH%2FEvqHTbK9y8Q5A6%2FyzRWZgpBH1aULgg57ey5ACVUNX9JTwXHafB11Vmx577QDSY3NfQDCB0kg%3D%3D) | Create a tag TARGET\_IMAGE that refers to SOURCE\_IMAGE |
| [docker top](https://link.segmentfault.com/?enc=RNGGj8c0vCkV%2FVcHiKaacQ%3D%3D.SC5VM0GBwQ%2FV%2Fs7fiUaRXZ%2BLuHso%2Bko3wYlKbDM7CA0krwr2xdWo7rs8d7Hee3sjDCJn8DslmFTOTGCygAeR%2Fw%3D%3D) | Display the running processes of a container |
| [docker trust](https://link.segmentfault.com/?enc=5LyGdCzot5hJnvoOVUprdA%3D%3D.etFirTwQv1dJxZzzOTRaVCcvoRv3ryLgxwGhL1IBf4w6W%2B98skOnr8djhXbDRQPomJasv39iLpF67H%2BaqCWk6Q%3D%3D) | Manage trust on Docker images |
| [docker unpause](https://link.segmentfault.com/?enc=diW0Cxg6RvF84r574MXdWA%3D%3D.CrDa7YDM4udOkCIIPkFGKeENd2dRBx%2BMUJyRtOpYHh86opoNDXnOUMcRdJENomKit%2BxbqerhoW4vQTVhzrajWA%3D%3D) | Unpause all processes within one or more containers |
| [docker update](https://link.segmentfault.com/?enc=DjrjocdrStoi99NRYbtl2A%3D%3D.MQAA10KVl5zZw2qdIIdeQswLyhZEhvCaaFiuInSyw%2F9OLMwm7%2FP1o6B8lKRaSGq8Y59rPnYkk8wWlJGsX%2FtmuQ%3D%3D) | Update configuration of one or more containers |
| [docker version](https://link.segmentfault.com/?enc=d0eoB%2FAts79rClPVMukvOA%3D%3D.NG6b5aE1M9XeDi3sQpBNwNznAbeGe9rIjv3%2Flh5WUrbQscvZRxwnMTNHglGuBbOhffchydvG7smClZ8qGlYp9Q%3D%3D) | Show the Docker version information |
| [docker volume](https://link.segmentfault.com/?enc=sgENp387ZD%2Bs78h9LzQSkg%3D%3D.AMnvROwi82h97I6RoIJ3bEfNOpQH3hkTRdITjD2lw5ArhZ7v2ZR%2BAfa7nzyzZpekKCP6rcllv3DzfSygBRg02A%3D%3D) | Manage volumes |
| [docker wait](https://link.segmentfault.com/?enc=HJm2XMHsKm9Kn7EFESSg4Q%3D%3D.I4UOFWm0hXa9YzFfDktThLP2c58HZbmCYdmYarf8cOGijzaWMEIFu6QK1H9ICbALfKT5GTgcjaMWGzyseZlW1w%3D%3D) | Block until one or more containers stop, then print their exit codes |
> 官方閱讀鏈接:[https://docs.docker.com/engine/reference/commandline/docker/](https://link.segmentfault.com/?enc=S2ceUllWkY%2F6slJ6fxR3PQ%3D%3D.vZIKWfT7ufeoyFpQ9N0gtEzZkwGbx3zH%2FJjgiZT%2FqvV8uEFqJJzSzdEcgCTa9oqCWq5fn5Fh2u%2BmQ4yRmkayJw%3D%3D)
### 5\. 進階:倉庫與軟件安裝的簡化
還記得筆者在文章開頭介紹的「鏡像、容器和倉庫」嗎?Docker 的倉庫用于存放鏡像。我們可以從中心倉庫下載鏡像,也可以從自建倉庫下載。同時,我們可以把制作好的鏡像從本地推送到遠程倉庫。
首先,筆者先引入一個知識點:Docker 的鏡像就是它的文件系統,一個鏡像可以放在另外一個鏡像的上層,那么位于下層的就是它的父鏡像。所以,Docker 會存在很多鏡像層,每個鏡像層都是只讀的,并且不會改變。當我們創建一個新的容器時,Docker 會構建出一個鏡像棧,并在棧的最頂層添加一個讀寫層,如圖所示。

現在,我們可以通過`docker images`?命令查看本地的鏡像。
~~~ebnf
docker images
~~~
查詢結果,如圖所示。

這里,對幾個名詞解釋一下含義。
* REPOSITORY:倉庫名稱。
* TAG: 鏡像標簽,其中 lastest 表示最新版本。注意的是,一個鏡像可以有多個標簽,那么我們就可以通過標簽來管理有用的版本和功能標簽。
* IMAGE ID :鏡像唯一ID。
* CREATED :創建時間。
* SIZE :鏡像大小。
那么,如果第一次我們通過`docker pull centos:latest`?拉取鏡像,那么當我們執行?`docker run -p 80 --name web -i -t centos /bin/bash`?時,它就不會再去遠程獲取了,因為本機中已經安裝該鏡像,所以 Docker 會直接創建一個新的容器并啟動該程序。
事實上,官方已經提供了安裝好 Nginx 的鏡像,我們可以直接使用。現在,我們通過拉取鏡像的方式重新構建一個 Web 服務器。首先,我們通過`docker search`?來查找鏡像。我們獲取到 Nginx 的鏡像清單。
~~~ebnf
docker search nginx
~~~
補充一下,我們也可以通過訪問 Docker Hub ([https://hub.docker.com/](https://link.segmentfault.com/?enc=o3c0AWSVx1g9vB%2BN4ldXig%3D%3D.i5mxxIK5v5b06PsLc4gzWJeDSL3MF%2F4dz5lK5trZzmo%3D))搜索倉庫,那么 star 數越多,說明它越靠譜,可以放心使用。

現在,我們通過`docker pull nginx`?拉取最新的?Nginx 的鏡像。當然,我們也可以通過?`docker pull nginx:latest`? 來操作。
~~~ebnf
docker pull nginx
~~~
然后,我們創建并運行一個容器。與前面不同的是,我們通過`-d`?選項告訴 Docker 在后臺運行容器的守護進程。并且,通過`8080:80`?告訴 Docker 8080 端口是對外開放的端口,80 端口對外開放的端口映射到容器里的端口號。
~~~apache
docker run -p 8080:80 -d --name nginx nginx
~~~
我們再通過?`docker ps -a`?來查看,發現容器已經后臺運行了,并且后臺執行了 nginx 命令,并對外開放 8080 端口。

因此,通過瀏覽器訪問?`http://127.0.0.1:8080`?即可。

### 6\. 其他選擇,使用替代注冊服務器
Docker Hub 不是軟件的唯一來源,我們也可以切換到國內的其他替代注冊服務器,例如阿里云。我們可以登錄?[https://cr.console.aliyun.com](https://link.segmentfault.com/?enc=OVOPPohEfpdJQ4IMe2tnnw%3D%3D.ilhUyBFJ89PxX2UkaH3bfgNhX1Oyrn3KkNXTkzDXKYA%3D)?搜索,并拉取公開的鏡像。


現在,我們輸入`docker pull`?命令進行拉取。
~~~vim
docker pull registry.cn-hangzhou.aliyuncs.com/qp_oraclejava/orackejava:8u172_DCEVM_HOTSWAPAGEN_JCE
~~~
這里,筆者繼續補充一個知識點:注冊服務器的地址。事實上,注冊服務器的地址是有一套規范的。完整格式是:倉庫主機/容器短名\[:標簽\]。這里,倉庫主機是?registry.cn-hangzhou.aliyuncs.com,用戶名是?qp\_oraclejava,容器短名是?orackejava,標簽名是 8u172\_DCEVM\_HOTSWAPAGEN\_JCE。事實上,我們上面通過?`docker pull centos:latest`?拉取鏡像,相當于?`docker pull registry.hub.docker.com/centos:latest`?。
## 三、構建我的鏡像
通過上面的學習,筆者相信你已經對 Docker 使用有了一個大致的了解,就好比我們通過 VMware 安裝了一個系統,并讓它跑了起來,那么我們就可以在這個 Linux 系統(CentOS 或者 Ubuntu ) 上面工作我們想要的任何事情。事實上,我們還會經常把我們安裝好的 VMware 系統進行快照備份并實現克隆來滿足我們下次快速的復制。這里,Docker 也可以構建定制內容的 Docker 鏡像,例如上面我們使用官方提供的安裝好 Nginx 的 Docker 鏡像。注意的是,我們通過基于已有的基礎鏡像,在上面添加鏡像層的方式構建新鏡像而已。
總結一下,Docker 提供自定義鏡像的能力,它可以讓我們保存對基礎鏡像的修改,并再次使用。那么,我們就可以把操作系統、運行環境、腳本和程序打包在一起,并在宿主機上對外提供服務。
Docker 構建鏡像有兩種方式,一種方式是使用?`docker commit`?命令,另外一種方式使用`docker build`?命令和`Dockerfile`?文件。其中,不推薦使用?`docker commit`?命令進行構建,因為它沒有使得整個流程標準化,因此,在企業的中更加推薦使用?`docker build`?命令和?`Dockerfile`?文件來構建我們的鏡像。我們使用`Dockerfile`?文件可以讓構建鏡像更具備可重復性,同時保證啟動腳本和運行程序的標準化。
### 1\. 構建第一個 Dockerfile 文件
現在,我們繼續實戰。這里,我們把一開始搭建的 Web 服務器構建一個鏡像。首先,我們需要創建一個空的 Dokcerfile 文件。
~~~bash
mkdir dockerfile_test
cd dockerfile_test/
touch Dockerfile
nano Dockerfile
~~~
緊接著,我們需要編寫一個 Dockerfile 文件,代碼清單如下
~~~awk
FROM centos:7
MAINTAINER LiangGzone "lianggzone@163.com"
RUN rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
RUN yum install -y nginx
EXPOSE 80
~~~
最后,我們通過`docker build`?命令進行構建。
~~~ebnf
docker build -t="lianggzone/nginx_demo:v1" .
~~~
現在, 我們來通過?`docker images`?看下我們的新鏡像吧。

### 2\. 理解 Dockerfile 全過程
哇,我們通過編寫一個 Dockerfile 文件順利構建了一個新的鏡像。這個過程簡單得讓人無法相信。現在,讓我們來理解一下這個全過程吧。首先,?`FROM centos:7`?是 Dockerfile 必須要的第一步,它會從一個已經存在的鏡像運行一個容器,換句話說,Docker 需要依賴于一個基礎鏡像進行構建。這里,我們指定 centos 作為基礎鏡像,它的版本是 7 (CentOS 7)。然后,我們通過?`MAINTAINER LiangGzone "lianggzone@163.com"`?指定該鏡像的作者是?LiangGzone,郵箱是?lianggzone@163.com。這有助于告訴使用者它的作者和聯系方式。接著,我們執行兩個 RUN 指令進行 Nginx 的下載安裝,最后通過??`EXPOSE 80`?暴露 Dokcer 容器的 80 端口。注意的是,Docker 的執行順序是從上而下執行的,所以我們要明確整個流程的執行順序。除此之外,Docker 在執行每個指令之后都會創建一個新的鏡像層并且進行提交。
我們使用??`docker build`?命令進行構建,指定`- t`?告訴 Docker 鏡像的名稱和版本。注意的是,如果沒有指定任何標簽,Docker 將會自動為鏡像設置一個 lastest 標簽。還有一點,我們最后還有一個`.`?是為了讓 Docker 到當前本地目錄去尋找 Dockerfile 文件。注意的是,Docker 會在每一步構建都會將結果提交為鏡像,然后將之前的鏡像層看作緩存,因此我們重新構建類似的鏡像層時會直接復用之前的鏡像。如果我們需要跳過,可以使用?`--no-cache`?選項告訴 Docker 不進行緩存。
### 3\. Dockerfile 指令詳解
Dockerfile 提供了非常多的指令。筆者這里特別整理了一份清單,建議收藏查看。

> 官方地址:[https://docs.docker.com/engine/reference/builder/#usage](https://link.segmentfault.com/?enc=LYtyl0eignVs6%2FngiHh%2Bhw%3D%3D.k5GtoYaBg3mLeGVnkNdOmjMUqIgt2GHC5G2LirFqzA15G%2B%2Be%2BmVX%2B03cyFUJXVjgHu8f9C%2BgPZpGhQhhVa62Zg%3D%3D)
#### 指令辨別一:RUN、CMD、ENTRYPOINT
`RUN`?、`CMD`?、`ENTRYPOINT`? 三個指令的用途非常相識,不同在于,`RUN`?指令是在容器被構建時運行的命令,而`CMD`?、?`ENTRYPOINT`?是啟動容器時執行 shell 命令,而?`RUN`?會被`docker run`?命令覆蓋,但是??`ENTRYPOINT`?不會被覆蓋。事實上,`docker run`?命令指定的任何參數都會被當作參數再次傳遞給?`ENTRYPOINT`? 指令。`CMD`?、?`ENTRYPOINT`?兩個指令之間也可以一起使用。例如,我們 可以使用?`ENTRYPOINT`?的 exec 形式設置固定的默認命令和參數,然后使用任一形式的`CMD`?來設置可能更改的其他默認值。
~~~css
FROM ubuntu
ENTRYPOINT ["top", "-b"]
CMD ["-c"]
~~~
#### 指令辨別二:ADD、COPY
`ADD`?、`COPY`?指令用法一樣,唯一不同的是?`ADD`? 支持將歸檔文件(tar, gzip, bzip2, etc)做提取和解壓操作。注意的是,`COPY`?指令需要復制的目錄一定要放在 Dockerfile 文件的同級目錄下。
### 4\. 將鏡像推送到遠程倉庫
#### 遠程倉庫:Docker Hub?
鏡像構建完畢之后,我們可以將它上傳到 Docker Hub 上面。首先,我們需要通過`docker login`?保證我們已經登錄了。緊接著,我們使用`docker push`?命令進行推送。
~~~armasm
docker push lianggzone/nginx_demo:v1
~~~
這里,我們了解下它的使用,格式是`docker push [OPTIONS] NAME[:TAG]`?,其中,筆者設置 NAME 是?lianggzone/nginx\_demo,TAG 是 v1。 (筆者注:推送 Docker Hub 速度很慢,耐心等待) 最后,上傳完成后訪問:[https://hub.docker.com/u/lianggzone](https://link.segmentfault.com/?enc=NS4l7eYONYnbiWtMMF890g%3D%3D.abA3jJtAjm90GI1A6NXokARTajq3pZ%2FnU6Og4rZKiYi05mtRy04wdVDn07AGGZV8)/,如圖所示。

#### 遠程倉庫:阿里云
同時,我們也可以使用國內的倉庫,比如阿里云。首先,在終端中輸入訪問憑證,登錄 Registry 實例。如果你不知道是哪個,可以訪問?[https://cr.console.aliyun.com/cn-hangzhou/instances/credentials](https://link.segmentfault.com/?enc=QpGTBoXndE2nTrec1vx9RA%3D%3D.6nX5fz6NiChWTgPhwiWDKNKgdZgI%2FiPzJoB1ahJjGALNnFHXenb9hDj3eIkbT0ZY%2B3b2GgUtwPuByycoYZm%2FAQ%3D%3D)。
~~~stylus
docker login --username=帳號 registry.cn-hangzhou.aliyuncs.com
~~~
現在,將鏡像推送到阿里云鏡像倉庫。其中,`docker tag [IMAGE_ID] registry.cn-hangzhou.aliyuncs.com/[命名空間]/[鏡像名稱]:[版本]`?和`docker push registry.cn-hangzhou.aliyuncs.com/[命名空間]/[鏡像名稱]:[版本]`?命令的使用如下所示。
~~~gradle
docker tag 794c07361565 registry.cn-hangzhou.aliyuncs.com/lianggzone/nginx_demo:v1
docker push registry.cn-hangzhou.aliyuncs.com/lianggzone/nginx_demo:v1
~~~
最后,上傳完成后訪問:[https://cr.console.aliyun.com/cn-hangzhou/instances/repositories](https://link.segmentfault.com/?enc=wdTcZfABjsTg5PoDp5J36g%3D%3D.vd1NCKejTWSc%2BSLeEJl5PMPisksxadFOco9yny0x%2BAY52OgmvHThNGEXtMDmmT1JnPw3Q7tyRGQs5NDaUMWuF%2FK%2B35L%2FUVAM6OQ%2FoOmuNJM%3D),如圖所示。

### 5.?Dockerfile 的 Github 源碼地址
這里,附上我整理的 Dockerfile 的倉庫。后面,筆者會陸續更新用到的一些常用文件,歡迎 star 關注。
> [https://github.com/lianggzone/dockerfile-images](https://link.segmentfault.com/?enc=rYXa4qBUf0Wx8igwqd%2By0Q%3D%3D.B5MTnRML4mwJgmfopPcmYK3DiELBA5e9kgAi%2BW3WIGEgJ%2BlDNsnACl6O5Q0Ba9HW)
## 附:參考資料
* 《Docker實戰》
* 《第一本Docker書》
* [Docker 命令參考文檔](https://link.segmentfault.com/?enc=gyqEUC%2FD%2B2DtN8xpua2Zmg%3D%3D.kBPHn%2BXWnoC%2Ba%2Fv3uFt6uMhR2JVchRHHJ9jk5%2BBOoLRw9tUjNj3YUw2M31iaIp9kGjTRwcYU99ffn%2BTuXOPi2my%2BugoTxgLX3o8dvH%2BbmzoFRQEHHInf4JfWDOct%2FHKX)
* [Dockerfile 鏡像構建參考文檔](https://link.segmentfault.com/?enc=53AZ0gq%2FgQZ7IndYEPq5fA%3D%3D.HZJ60WLawCItRJGnE0%2BNHAnEd0619ykVZuoCc%2FuxaC650Cdk78dp9xcsyJqLXAkSpw3BSzg1Rib38XTtkcugyufoVcvspBfv%2BFYaBKcXrz3gH1vzx8Cr8PpOoheD7Fvc)
(完,轉載請注明作者及出處。)
## 寫在末尾
【服務端思維】:我們一起聊聊服務端核心技術,探討一線互聯網的項目架構與實戰經驗。同時,擁有眾多技術大牛的「后端圈」大家庭,期待你的加入,一群同頻者,一起成長,一起精進,打破認知的局限性。
- php開發
- 常用技巧
- 字符數組對象
- php換行替換,PHP替換回車換行符的三種方法
- PHP 數組轉字符串,與字符串轉數組
- php將img中的寬高刪除,PHP刪除HTML中寬高樣式的詳解
- php去除換行(回車換行)的三種方法
- php 過濾word 樣式
- php如何設置隨機數
- 2個比較經典的PHP加密解密函數分享
- php怎么去除小數點后多余的0
- php中判斷是一維數組還是二維數組的解決方案
- php 獲取數組中出現次數最多的值(重復最多的值)與出現的次數
- PHP過濾掉換行符、特殊空格、制表符等
- PHP中json_endoce轉義反斜杠的問題
- PHP過濾Emoji表情和特殊符號的方法
- PHP完美的提取鏈接正則
- php很牛的圖片采集
- 日期處理
- php 獲取今日、昨日、上周、本月的起始時間戳和結束時間戳的方法非常簡單
- PHP指定時間戳/日期加一天,一年,一周,一月
- 使用php 獲取時間今天明天昨天時間戳的詳解
- php獲得當月的節假日函數(包含周末,年度節假日)
- PHP獲取本月起始和截止時間戳
- php 獲取每月開始結束時間,php 獲取指定月份的開始結束時間戳
- PHP獲取今天,昨天,本月,上個月,本年 起始時間戳
- php、mysql查詢當天,本周,本月的用法
- php獲取兩個時間戳之間相隔多少天多少小時多少分多少秒
- 毫秒級時間戳和日期格式轉換
- php-倒計時
- 請求提交上傳
- php+put+post,Curl和PHP-如何通過PUT,POST,GET通過curl傳遞json
- PHP put提交和獲取數據
- PHP curl put方式上傳文件
- 數據導入導出
- PHP快速導入大量數據到數據庫的方法
- PHP快速導出百萬級數據到CSV或者EXCEL文件
- PHP解析大型Excel表格的庫:box/spout
- PHP導入(百萬級)Excel表格數據
- PHP如何切割excel大文件
- 使用 PHP_XLSXWriter 代替 PHPExcel 10W+ 數據秒級導出
- 安裝php擴展XLSXWriter
- 解決php導入excel表格時獲取日期變成浮點數的方法
- xml處理
- PHP XML和數組互相轉換
- php解析xml字符串
- php 生成vcf通訊錄
- 文件操作相關
- php獲取文件后綴的9種方法
- PHP判斷遠程文件是否存在
- PHP獲取文件修改時間,訪問時間,inode修改時間
- php獲取遠程文件大小教程
- php 讀取文件并以文件方式下載
- php 把數字轉化為大寫中文
- 請求響應
- PHP 獲取當前訪問的URL
- 壓縮
- php生成zip壓縮包
- PHPMailer
- 整理PHPMailer 發送郵件 郵件內容為html 可以添加多個附件等
- 通達oa
- OA管理員密碼忘了怎么辦,這里教你分分鐘搞定…
- 跨域
- php解決多站點跨域
- php設置samesite cookie,有效防止CSRF
- Chrome 配置samesite=none方式
- Cookie 的 SameSite 屬性
- 圖片
- php pdf首頁截圖,PHP_PHP中使用Imagick讀取pdf并生成png縮略圖實例,pdf生成png首頁縮略圖
- PHP -- 七牛云 在線視頻 獲取某一幀作為封面圖
- PHP圖片壓縮方法
- 如何解決PHP curl或file_get_contents下載圖片損壞或無法打開的問題
- php遠程下載文章中圖片并保存源文件名不變
- 詳解PHP如何下載采集圖片到本地(附代碼實例)
- php如何將webp格式圖片轉為jpeg
- PHP獲取遠程圖片的寬高和體積大小
- php 軟件版本號比較
- 使用PHP通過SMTP發送電郵
- 常用正則表達式
- php如何用正則表達式匹配中文
- 用于分割字符串的 PHP preg_match_all 正則表達式
- 性能優化
- php.ini配置調優
- PHP 幾種常見超時的設置方法
- PHP函數in_array、array_key_exists和isset效率分析
- php array push 和array_merge 效率誰高,php 通過array_merge()和array+array合并數組的區別和效率比較...
- php 兩個數組取交集、并集、差集
- 設置PHP最大連接數及php-fpm 高并發 參數調整
- 小工具
- php 獲取代碼執行時間和消耗的內存
- PHP如何判斷某項擴展是否開啟
- centos7.x下php 導出擴展 XLSXWriter 安裝
- php生成mysql數據庫字典
- PHP 實現 word/excel/ppt 轉換為 PDF
- composer的使用
- showdoc sqlite3 找回管理員密碼
- php怎么將數組轉為xml
- PHP抖音最新視頻提取代碼
- yii
- Yii2 如何獲取Header參數?
- swoole
- Linux下搭建swoole服務的基本步驟
- 相關學習資料
- 帶你學習swoole_process詳解
- 按照官方文檔 在win10下安裝 docker for windows easyswoole鏡像 掛載目錄
- php常用框架
- Hyperf
- 常用算法PHP版
- thinkphp6
- TP6 事件綁定、監聽、訂閱
- Thinkphp 模板中輸出HTML的變量
- Thinkphp6(操作SQL數據庫)
- thinkphp6 mysql查詢語句對于為null和為空字符串給出特定值處理
- Thinkphp 6 - 連接配置多個數據庫并實現自由切換(詳細過程及實例demo)
- TP框架中的Db::name 和 dB::table 以及 db('') 的區別
- thinkphp6.0模型篇之模型的軟刪除
- thinkphp6自定義日志驅動,增加顯示全部請求信息
- 其他系統
- 微擎數據庫字段字典
- Flutter實現微信支付和iOS IAP支付
- Flutter上線項目實戰——蘋果內購
- PHP接入蘋果支付
- 調試
- php如何獲取當前腳本所有加載的文件
- php跟蹤所有調用方法,日志方法
- 解析phpstorm + xdebug 遠程斷點調試
- PHP XDEBUG調試 PHPSTORM配置
- 異常處理
- PHP 出現 502 解決方案
- php 語法解析錯誤 syntax error unexpected namespace T_NAMESPACE
- Composer 安裝與使用
- 數據庫相關
- php pdo怎么設置utf8
- php 如何根據最新聊天對用戶進行排序
- php lic&fpm
- 讓php程序在linux后臺執行
- PHPcli模式和fpm模式優缺點在哪里?
- 運行模式
- php運行模式之cli模式
- 自己庫
- php批量獲取所有公眾號粉絲openid
- 地圖
- php 判斷點在多邊形內,php百度地圖api判斷地址是否在多邊形區域內
- PHP,Mysql-根據一個給定經緯度的點,進行附近地點查詢
- MySQL 根據經緯度查找排序
- PHP+MySQL獲取坐標范圍內的數據
- 【百度地圖】刪除指定覆蓋物
- 百度地圖多點+畫連接線+數字標注
- laravel5.8
- laravel5.8(四)引入自定義常量文件及公共函數文件
- Lumen 查詢執行SQL
- 使你的 Laravel 項目模塊化
- Laravel 多條件 AND , OR條件組合查詢
- Laravel 查詢 多個or或者and條件
- laravel redis操作大全
- laravel中外部定義whereIn的用法和where中使用in
- lumen5.8
- 創建laravel5.8 lumen前后臺api項目--記錄請求和響應日志
- Laravel和Lumen開啟SQL日志記錄
- Laravel 5.8 常用操作(路徑+日志+分頁+其他操作)
- 升級php7.4 laravel lumen報錯Trying to access array offset on value of type null
- Laravel 任務調度(計劃任務,定時任務)
- laravel的command定時任務時間的設置
- Laravel任務調度的簡單使用
- laravel單數據庫執行事務和多數據庫執行事務
- laravel中鎖以及事務的簡單使用
- 申請其他相關
- 小程序地理位置接口申請
- PHP高并發
- php 高并發下 秒殺處理思路
- 記錄 PHP高并發 商品秒殺 問題的 Redis解決方案
- thinkphp3.2
- thinkphp3.2 數據庫 AND OR連綴使用
- laravel
- laravel的聯表查詢with方法的使用
- laravel獲取請求路由對應的控制器和方法
- Laravel 模型關聯建立與查詢
- Laravel多表(3張表以上)with[]關聯查詢,對關聯的模型做條件查詢(has,跟join一樣結果 )
- Laravel模型屬性的隱藏屬性、顯示屬性和臨時暴露隱藏屬性用法介紹
- aravel獲取當前的url以及當前的基礎域名方法匯總
- Laravel 模型實現多庫查詢或者多表映射
- 關于 Laravel 的 with 多表查詢問題
- Laravel 模型過濾(Filter)設計
- 懶加載、預加載、with()、load() 傻傻分不清楚?
- laravel模型$castsl屬性
- Laravel Query Builder 復雜查詢案例:子查詢實現分區查詢 partition by
- Laravel 模型關聯、關聯查詢、預加載使用實例
- laravel 中with關聯查詢限定查詢字段
- laravel 原生字段查詢 whereRaw 和 where(DB::raw(''))
- lavarel - where條件分組查詢(orWhere)
- 通過 Laravel 查詢構建器實現復雜的查詢語句
- 兩個結果集合并成一個
- Laravel 對某一列進行篩選然后求和 sum()
- laravel怎么優雅的拼接where,處理whereIn與where數組查詢的問題
- laravel查詢時判斷是否存在數據
- laravel中的whereNull和whereNotNull
- laravel框架中的子查詢
- Laravel框架中 orwhere 多條件查詢的使用
- Laravel中where的高級使用方法
- laravel復雜的數據庫查詢(事例)
- laravel多條件查詢方法(and,or嵌套查詢)
- Laravel 的 where or 查詢
- Laravel 進行where 多個or和and的條件查詢可用
- laravel Middleware 中間件 $next($request) 報錯不執行問題
- 基于Laravel框架--自定義CORS跨域中間件
- laravel9新增路由文件及解決跨域問題方法
- 解決在laravel中leftjoin帶條件查詢沒有返回右表為NULL的問題
- 【Laravel 】faker數據填充詳解
- 數據庫
- mysql
- mysql聯合索引(復合索引)詳解
- MYSQL 清空表和截斷表
- MySQL快速生成大量測試數據(100萬、1000萬、1億)
- 提高mysql千萬級大數據SQL查詢優化30條經驗(Mysql索引優化注意)
- MySQL常用命令
- MySQL(三)|《千萬級大數據查詢優化》第一篇:創建高性能的索引
- MySQL(一)|性能分析方法、SQL性能優化和MySQL內部配置優化
- MySQL(二)|深入理解MySQL的四種隔離級別及加鎖實現原理
- MySQL(四)|《千萬級大數據查詢優化》第一篇:創建高性能的索引(補充)
- MySQL(五)|《千萬級大數據查詢優化》第二篇:查詢性能優化(1)
- MySQL(六)|《千萬級大數據查詢優化》第二篇:查詢性能優化(2)
- MySQL(七)|MySQL分庫分表的那點事
- Mysql索引優化 Mysql通過索引提升查詢效率(第二棒)
- MySQL查詢的性能優化(查詢緩存、排序跟索引)
- 【總結】MySQL數據庫
- MySQL存儲引擎、事務日志并發訪問以及隔離級別
- 技巧
- 數據庫 SQL查詢重復記錄 方法
- 替換數據庫中某個字段中的部分字符
- mysql開啟bin log 并查看bin log日志(linux)
- 分表分區
- 千萬級別數據的mysql數據表優化
- MYSQL百萬級數據,如何優化
- MySQL備份和恢復
- MySQL間隙鎖死鎖問題
- 小技巧
- 基礎
- MySQL中sql_mode參數
- mysql數據庫異常
- this is incompatible with sql_mode=only_full_group_by
- mysql安全
- MySQL數據庫被比特幣勒索及安全調整
- mysql忘記密碼后重置(以windows系統mysql 8.4為例)
- MongoDB
- sql查詢
- MYSQL按時間段分組查詢當天,每小時,15分鐘數據分組
- 高級
- 基于 MySQL + Tablestore 分層存儲架構的大規模訂單系統實踐-架構篇
- 數據庫安全
- 服務器被黑,MySQL 數據庫遭比特幣勒索!該如何恢復?
- 數千臺MySQL數據庫遭黑客比特幣勒索,該怎么破?
- MySQL 數據庫規范
- MySQL數據庫開發的36條鐵律
- Elasticsearch
- 安裝與配置
- ElasticSearch關閉重啟命令
- 設置ES默認分詞器IK analyzer
- 查詢
- elasticsearch 模糊查詢不分詞,實現 mysql like
- elasticSearch多條件高級檢索語句,包含多個must、must_not、should嵌套示例,并考慮nested對象的特殊檢索
- elasticSearch按字段普通檢索,結果高亮
- Elasticsearch 如何實現查詢/聚合不區分大小寫?
- 索引更新&刷新
- refresh與批量操作的效率
- Elasticsearch 刪除type
- 分詞器
- ElasticSearch最全分詞器比較及使用方法
- 異常錯誤
- 解決ES因內存不足而無法查詢的錯誤,Data too large, data for [<http_request>]
- linux
- 基本知識
- CentOS7.5 通過wget下載文件到指定目錄
- 【CentOS】vi命令
- centos7查看硬盤使用情況
- CentOS7 查看目錄大小
- Centos 7下查看當前目錄大小及文件個數
- 普通用戶sudo\su 到root免密碼
- 普通用戶切換到root用戶下的免密配置方法
- linux 獲取進程啟動參數,linux查看進程啟動及運行時間
- Linux 查看進程
- linux刪除文件后不釋放磁盤的問題
- Linux查找大文件命令
- linux 如何關閉正在執行的php腳本
- linux三劍客(grep、sed、awk)基本使用
- centos 卸載軟件
- centos查看內存、cpu占用、占用前10,前X
- Centos 查看系統狀態
- 異常
- 問題解決:Failed to download metadata for repo ‘appstream‘: Cannot prepare internal mirrorlist:...
- php相關
- centos 安裝phpize
- Centos7.2下phpize安裝php擴展
- 切換版本
- 運營工具
- 資深Linux運維工程師常用的10款軟件/工具介紹
- 一款良心的終端連接工具
- 六款Linux常用遠程連接工具介紹,看看哪一款最適合你
- Finalshell
- Linux Finalshell連接centos7和文件無顯示問題
- WSL2:我在原生的Win10玩轉Linux系統
- MobaXterm
- 運維
- linux服務器上定時自動備份數據庫,并保留最新5天的數據
- Centos系統開啟及關閉端口
- CentOS7開放和關閉端口命令
- Linux中查看所有正在運行的進程
- 防火墻firewall-cmd命令詳解
- centos 7.8阿里云服務器掛載 數據盤
- Linux Finalshell連接centos7和文件無顯示問題
- Centos7系統端口被占用問題的解決方法
- vi
- 如何在Vim/Vi中復制,剪切和粘貼
- 命令
- [Linux kill進程] kill 進程pid的使用詳解
- 備份還原
- Linux的幾種備份、恢復系統方式
- Linux系統全盤備份方法
- 相關軟件安裝
- linux下 lua安裝
- python
- 升級pip之后出現sys.stderr.write(f“ERROR: {exc}“)
- lua
- centos源碼部署lua-5.3
- deepin
- deepin20.6設置默認的root密碼
- 任務相關
- 寶塔定時任務按秒執行
- CentOS 7 定時任務 crontab 入門
- centos7定時任務crontab
- Linux下定時任務的查看及取消
- Linux(CentOS7)定時執行任務Crond詳細說明
- Linux 查看所有定時任務
- linux查看所有用戶定時任務
- Linux 定時任務(超詳細)
- 防火墻
- Centos7開啟防火墻及特定端口
- CentOS防火墻操作:開啟端口、開啟、關閉、配置
- 生成 SSH 密鑰(windows+liunx)
- 阿里云,掛載云盤
- 前端
- layui
- layui多文件上傳
- layer.msg()彈框,彈框后繼續運行
- radio取值
- layui-數據表格排序
- Layui select選擇框添加搜索選項功能
- 保持原來樣式
- layui表格單元如何自動換行
- layui-laydate時間日歷控件使用方法詳解
- layui定時刷新數據表格
- layer 延時設置
- layer.open 回調函數
- 【Layui內置方法】layer.msg延時關閉msg對話框(代碼案例)
- layui多圖上傳圖片順序錯亂及重復上傳解決
- layer.confirm關閉彈窗
- vue
- Vue跨域解決方法
- vue 4.xx.xx版本降級至2.9.6
- vue-cli 2.x升級到3.x版本, 和3.x降級到2.x版本命令
- 最新版Vue或者指定版本
- Vue2.6.11按需模塊安裝配置
- jQuery
- jQuery在頁面加載時動態修改圖片尺寸的方法
- jquery操作select(取值,設置選中)
- 日歷
- FullCalendar中文文檔:Event日程事件
- js
- JS 之 重定向
- javascript截取video視頻第一幀作為封面方案
- HTML <video> preload 屬性
- jQuery使用ajax提交post數據
- JS截取視頻靚麗的幀作為封面
- H5案例分享:移動端touch事件判斷滑屏手勢的方向
- JS快速獲取圖片寬高的方法
- win
- Windows環境下curl的使用
- Cygwin
- Windows下安裝Cygwin及apt-cyg
- Cygwin 安裝、CMake 安裝
- mklink命令 詳細使用
- Nginx
- Nginx高級篇-性能優化
- Nginx常用命令(Linux)
- linux+docker+nginx如何配置環境并配置域名訪問
- Nginx的啟動(start),停止(stop)命令
- linux 查看nginx 安裝路徑
- 安裝配置
- Linux 查看 nginx 安裝目錄和配置文件路徑
- 【NGINX入門】3.Nginx的緩存服務器proxy_cache配置
- thinkphp6.0 偽靜態失效404(win下)
- 深入
- nginx rewrite及多upstream
- Nginx負載均衡(upstream)
- 專業術語
- 耦合?依賴?耦合和依賴的關系?耦合就是依賴
- PHP常用六大設計模式
- 高可用
- 分布式與集群
- Nginx 實踐案例:反向代理單臺web;反向代理多組web并實現負載均衡
- 容器
- Docker
- 30 分鐘快速入門 Docker 教程
- linux查看正在運行的容器,說說Docker 容器常用命令
- Windows 安裝Docker至D盤
- 配置
- win10 快速搭建 lnmp+swoole 環境 ,部署laravel6 與 swoole框架laravel-s項目1
- win10 快速搭建 lnmp+swoole 環境 ,部署laravel6 與 swoole框架laravel-s項目2
- docker 容器重命名
- Linux docker常用命令
- 使用
- docker 搭建php 開發環境 添加擴展redis、swoole、xdebug
- docker 單機部署redis集群
- Docker 退出容器不停止容器運行 并重新進入正在運行的容器
- 進入退出docker容器
- Docker的容器設置隨Docker的啟動而啟動
- 使用異常處理
- docker容器中bash: vi: command not found
- OCI runtime exec failed: exec failed:解決方法
- docker啟動容器慢,很慢,特別慢的坑
- 解決windows docker開發thinkphp6啟動慢的問題
- 【Windows Docker】docker掛載解決IO速度慢的問題
- Docker的網絡配置,導致Docker使用網路很慢的問題及解決辦法
- golang工程部署到docker容器
- Docker 容器設置自啟動
- 如何優雅地刪除Docker鏡像和容器(超詳細)
- 5 個好用的 Docker 圖形化管理工具
- Docker 可能會用到的命令
- Kubernetes
- 消息隊列
- RabbitMQ
- php7.3安裝使用rabbitMq
- Windows環境PHP如何使用RabbitMQ
- RabbitMQ學習筆記:4369、5672、15672、25672默認端口號修改
- Window10 系統 RabbitMQ的安裝和簡單使用
- RabbitMQ默認端口
- RabbitMQ可視化界面登錄不了解決方案
- RocketMQ
- Kafka
- ActiveMQ
- mqtt
- phpMQTT詳解以及處理使用過程中內存耗死問題
- MQTT--物聯網(IoT)消息推送協議
- php實現mqtt發布/發送 消息到主題
- Mqtt.js 的WSS鏈接
- emqx
- 如何在 PHP 項目中使用 MQTT
- emqx 修改dashboard 密碼
- 其他
- Windows 系統中單機最大TCP的連接數詳解
- EMQX
- Linux系統EMQX設置開機自啟
- Centos7 EMQX部署
- docker安裝 EMQX 免費版 docker安裝并配置持久化到服務器
- 實時數倉
- 網易云音樂基于 Flink + Kafka 的實時數倉建設實踐
- 實時數倉-基于Flink1.11的SQL構建實時數倉探索實踐
- 安全
- 網站如何保護用戶的密碼
- 關于web項目sessionID欺騙的問題
- php的sessionid可以偽造,不要用來做防刷新處理了
- DVWA-Weak Session IDs (弱會話)漏洞利用方式
- 保證接口數據安全的10種方案
- cookie和session的竊取
- 萬能密碼漏洞
- 黑客如何快速查找網站后臺地址方法整理
- 網站后臺萬能密碼/10大常用弱口令
- 萬能密碼漏洞02
- 大多數網站后臺管理的幾個常見的安全問題注意防范
- token可以被竊取嗎_盜取token
- token被劫持[token被劫持如何保證接口安全性]
- PHP給后臺管理系統加安全防護機制的一些方案
- php - 重新生成 session ID
- 隱藏響應中的server和X-Powered-By
- PHP會話控制之如何正確設置session_name
- Session攻擊001
- PHP防SQL注入代碼,PHP 預防CSRF、XSS、SQL注入攻擊
- php25個安全實踐
- php架構師 系統管理員必須知道的PHP安全實踐
- 版本控制
- Linux服務器關聯Git,通過執行更新腳本實現代碼同步
- PHP通過exec執行git pull
- git 在linux部署并從windows上提交代碼到linux
- git上傳到linux服務器,git一鍵部署代碼到遠程服務器(linux)
- linux更新git命令,使用Linux定時腳本更新服務器的git代碼
- git異常
- 如何解決remote: The project you were looking for could not be found
- git status顯示大量文件修改的原因是什么
- PHPstorm批量修改文件換行符CRLF為LF
- git使用
- git常用命令大全
- centos git保存賬戶密碼
- GIT 常用命令
- git怎樣還原修改
- Git 如何放棄所有本地修改的方法
- Git忽略文件模式改變
- git: 放棄所有本地修改
- Git三種方法從遠程倉庫拉取指定的某一個分支
- 雜七雜八
- h5視頻
- H5瀏覽器支持播放格式:H264 AVCA的MP4格式,不能轉換為mpeg-4格式,
- iOS無法播放MP4視頻文件的解決方案 mp4視頻iphone播放不了怎么辦
- h5點播播放mp4視頻遇到的坑,ios的h5不能播放視頻等
- 【Linux 并發請求數】支持多少并發請求數
- Linux下Apache服務器并發優化
- 緩存
- redis
- Linux啟動PHP的多進程任務與守護redis隊列
- 重啟redis命令
- golang