[](https://hackmd.io/6YoTr489SimJ0pNIn8JxUw) ###### tags: `教學` `Docker` `container` `學習筆記` # Docker and Container 004 - Container 容器 [TOC] --- ## 1. container > 容器是一種**以應用程式為中心 (application-centric) 的架構**,能夠在選定的基礎環境中 (Infrastructure) 部署高效率 (high-performance)、可擴充 (scalable) 與個別獨立 (isolate) 的應用程式。 1. **選定的基礎環境**:可自行決定需要使用的環境、版本。(Dockerfile) 2. **高效率**:不似龐大的系統,啟動一個容器只需短短幾秒(docker run) 3. **可擴充**:可動態新增或刪除容器 4. **個別獨立**:容器可互相溝通但容器間的運作並不會互相干擾 - 其中**高效率、可擴充與個別獨立為容器的主要特色**。再來由於容器可以部署在多種不同環境 (Desktop, VMs, Cloud 等等),不但增加了便利與彈性,更能確保跨平台的一致性。 - container 是一個獨立的、隔離的環境,當你啟動了兩個容器時,即便是**從同一個 image 啟動起來,Docker 是會幫你做出兩個不同的容器的**。 - **一致性** > 開發程式最常見的的問題是 _"我的電腦可以跑!沒問題"_ ,但事實可能是 _"就只有我的電腦可以跑"_。透過**容器一致性的特性能確保使用的環境都是一致的**而不再因為環境的不同造成程式無法運作。 - 容器中最為人熟知的莫過於 Docker,~~而 Docker 恰恰是 k8s 預設使用的容器~~。因此,如果對 Docker 的概念已經了解的話,對學習 k8s 是有加分的效果。 - [參考資料](https://ithelp.ithome.com.tw/articles/10192193) --- ## 2. container運行練習 ```linux= $ docker container ls,確認現況 $ docker pull alpine,拉一個專案下來 $ docker images,檢查現況 $ docker container run --name c001 alpine ls /,使用一個container來執行alpine這個image - --name c001,container取名為c001 - ls /,列出根目錄所有的檔案以及目錄 - container可省略,指令變為docker run ``` - container預設:只有下達指令時會運行,跑完就清掉。 - dockerfile在建構映像檔時可看到 - `$ docker run -it --name c003 alpine /bin/sh` - -it,interactive mode持續運行 - /bin/sh,要執行的程序 - 進入後,使用exit可離開 - `$ docker run -d --name c004 alpine tail -f /dev/null` - -d,背景運行 - tail,追蹤某個file的log,並印到console上 - `$ docker exec -it 7d54f35304ca /bin/sh` - exec會在container裡啟動/bin/sh這一個程序 * `$ docker pull nginx:latest` * `$ docker images` * `$ docker run -d -p 8081:80 --name c006 nginx` * -p,port mapping,將VM OS(linux)的8081port對應到container的port 80 -  * 確定主機ip,接著在瀏覽器查看結果 --- > #### Container 是一次性的(disposable) > 現在 container 都是交由 swarm/Kubernetes 等 container orchestration 系統負責這些維運任務,但開發者也有必要了解這類問題的處理方法,才有辦法寫出符合 container 架構的程式。 > 一次性的特色可以參考 [Create ephemeral containers](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#create-ephemeral-containers)。 ### 另一個練習 - `$ docker run -d -p 80:80 docker/getting-started`  #### Container與CI/CD的關係 - CI:持續將程式碼整合在一起 - CD:持續將(整合好的)程式碼部署到伺服器上 - container的位置 ### 以腳本運行 範例檔案為小i的ai-engine的執行檔,`$ ./init.sh`,執行之後會照內容開始運行一個container。 ``` #!/bin/bash HOST_DIR=/opt/xiaoi_app/ai-engine # 1. 配置 AI-Engine 安裝路徑 HOST_PORT=9000 # 2. 配置容器內服務映射到宿主機埠 CONTAINER_NAME=AI-Engine_Container # 3. 配置引擎服務 docker 容器名稱 IMAGE_REPOSITORY=xiaoi/ai-engine # 4. 配置引擎鏡像的 REPOSITORY/ TAG IMAGE_TAG=v20201208 # 【說明】:docker images 可以查看鏡像的 REPOSITORY、 TAG. CONTAINER_PORT=9000 # 5. 配置引擎在容器內的埠和安裝路徑 CONTAINER_DIR=/opt/ai-engine8 # 【說明】:一般情況下,CONTAINER_PORT 和 CONTAINER_DIR 預設無需配置. # 若需要配置 CONTAINER_PORT,則需要/opt/xiaoi_app/ai-engine/app.properties 文件中的 knity.port 值保持一致. # 若需要配置 CONTAINER_DIR,則需要登錄容器內查看引擎的安裝路徑. docker run -d --name $CONTAINER_NAME -v $HOST_DIR/app:$CONTAINER_DIR/app -v $HOST_DIR/conf:$CONTAINER_DIR/conf -v $HOST_DIR/lib:$CONTAINER_DIR/ext-lib -v $HOST_DIR/logs:$CONTAINER_DIR/logs -p $HOST_PORT:$CONTAINER_PORT $IMAGE_REPOSITORY:$IMAGE_TAG ``` - 腳本內先寫好各個變數定義(設定路徑) - 參數可重複疊加(上面的範例使用了-v好幾次) --- ## 3. 其他 ### 指令:查看log - `$ docker container logs [OPTIONS] CONTAINER` - CONTAINER:大寫的為容器名稱 - [OPTIONS]:可放入各種參數,常用-f, --tail - [參考連結1:docker container logs](https://docs.docker.com/engine/reference/commandline/container_logs/) - [參考連結2](https://www.papertrail.com/solution/tips/how-to-live-tail-docker-logs/) ### 其他container指令 - [Docker CLI_docker container](https://docs.docker.com/engine/reference/commandline/container/) --- ## 回顧 * [ ] 什麼是container * [ ] 與image之間的關係 * [ ] CI/CD ## 課後複習/測驗 - [關於容器 (Container) 技術的問答](https://forms.office.com/Pages/ResponsePage.aspx?id=tkrhf12POUGEv82K7Q7muXVL-ort5lNEs9e7z5vz03FUOUVJMzZQNFk2NTcwM1dYT1JONk9NMlBYTS4u&fbclid=IwAR17WCj0I6Rshb_8wAK_b7_pCZcep4qlzJ3S4zpu6lHRc3vnxI_D8EvuyII) --- ### Q10:如何建立Docker容器? 可以使用下列命令,利用Docker映像檔來新建Docker容器:`$ docker run -t -i command` 此指令將建立並啟動容器,如果要確認目前主機上所有仍在運行中的容器清單,則使用下面指令:`$ docker ps -a` ### Q11:每台主機可執行的最大容器數量是多少? 實際上,取決於本身的系統環境、應用程式的大小,以及可用資源的數量(像是CPU),都會影響到環境中可運行的容器數量。 很遺憾的是,容器並非神奇魔法,它無法憑空創造出新的CPU,但這確實提供了一種更具效率的資源使用方式,容器本身是非常輕量的(千萬記住,作業系統式容器和應用系統式容器的差異),且唯有在它們正確使用並執行,才是如此輕盈(一個容器執行一種服務)。 --- 1. **Docker image 與 container 之間的關係**,下列哪種描述「錯誤」? - (A) Container 必須基於 image 產生;但相對地,image 無法藉由 container 產生。 - (B) 同個 image ID 內容是不可變的(immutable),而 container 內容是可變的(mutable)。 - (C) 若使用某個 image 運行一個 container,則想移除 image 前,必須先移除 container 才能移除 image。 - (D) 上述 ABC 都正確。 - (E) 上述 ABC 都「錯誤」。 :::spoiler - [題目來源](https://ithelp.ithome.com.tw/articles/10253334) - 答案是 (A) * A 描述錯誤:`docker run` 可以基於 image 產生 container,所以第一句話是正確的;相對地,`docker commit` 可以藉由 container 產生 image,所以第二句話是錯誤的。 * B 描述正確:`docker run <IMAGE ID>` 指令中, 為 SHA256 digest,同個 digest 的內容無法改變。而 container 內容則是可變的,比方說進入 Nginx container 新增 HTML 檔,即可立刻使用瀏覽器看到內容。 * C 描述正確:Image 上 run 一個 container 時,可以使用 `docker rmi -f <IMAGE ID>` 強制把 image 移除。雖然無法再使用該 Digest 再次啟動 container,但實際上 image 內容依然是存在的,因為重新 pull image 會發現 `Already exists` 的關鍵字。實際測試指令如下: ```dockerfile= $ docker run php:7.4 php -v Unable to find image 'php:7.4' locally 7.4: Pulling from library/php ... $ docker rmi -f php:7.4 Untagged: php:7.4 Untagged: php@sha256:... Deleted: sha256:... $ docker pull php:7.4 7.4: Pulling from library/php xxxxxxxxxxxx: Already exists xxxxxxxxxxxx: Already exists ... ``` 了解 image 與 container 的關係後,才能進一步了解 Docker 是如何建置、如何加速建置、啟動 container 等。 ::: --- 2. 下列都是兩個 container 組合的架構,皆可正常運行。以單一 container 作為一個 SaaS 來看,哪一種「比較不」符合 Docker 官方的最佳實踐(Docker best practices)? - (A) Nginx container + Apache with PHP-FPM(FastCGI)container。 - (B) Apache container + Tomcat container。 - (C) Nginx container + Node container。 - (D) 以上 ABC 皆符合。 - (E) 以上 ABC 皆「不」符合。 :::spoiler 答案是:(A) **Container 盡可能只做一件事** 參考 [Decouple applications](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#decouple-applications),Docker 官方是這麼說的: > Limiting each container to one process is a good rule of thumb, but it is not a hard and fast rule. 上面五種 container,唯獨 **Apache with PHP-FPM(FastCGI)container** 是比較不符合的,因為它必須啟動做兩件不同任務的 process。可以注意到在 DockerHub 找到類似的 image 的 CMD,都必須要使用 Supervisor 或類似的工具來管理 process。 順帶一提,官方也沒有提供 Apache + PHP-FPM 或 Nginx + PHP-FPM 的 image,而是提供只有 PHP-FPM 的 image,或是 Apache + PHP-CGI 的 image。 這概念與 SOLID 裡 SRP 的優點一樣:只做一件事,架構不會複雜,除錯會更容易。 ::: --- 3. 考慮部署「線上環境」,下列建置 Docker image 與運行 container 的描述,何種做法「不」符合 Docker 官方的最佳實踐(Docker best practices)? - (A) 持續整合(Continuous Integration)有提到要經常做驗證,因此部署上線後的第一件事是在 container 上先跑單元測試,再啟動應用程式。 - (B) 利用 Docker VOLUME 參數設定,可以直接進上線用的 container,到共用 volume 目錄裡更新程式。 - (C) 使用 ARG 參數,在建置 image 階段過程才能機敏資訊加入 image 裡,如此一來,機敏資訊就可以不用放原始碼裡了。 - (D) 上述 ABC 做法都符合。 - (E) 上述 ABC 做法都「不」符合。 :::spoiler 答案是:(E) **Container 應該怎麼應用?** A 的問題:部署上線後居然可以跑單元測試?這代表測試程式碼和單元測試套件也一併被部署上線,這不符合 [Don’t install unnecessary packages](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#dont-install-unnecessary-packages)。 正確的做法:在建置 image 前或是 [Use multi-stage builds](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#use-multi-stage-builds) 在一開始的階段做單元測試,最終僅部署做完單元測試後的程式碼。 > 相對地,部署上線後,可以做冒煙測試([smoke testing](https://en.wikipedia.org/wiki/Smoke_testing_(software)))。 B 的問題:[VOLUME](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#volume) 官方不建議用來放程式,而是放額外要保存的資料,如 MySQL 的 Dockerfile 就有設定 [`VOLUME /var/lib/mysql`](https://github.com/docker-library/mysql/blob/3dfa7a3c038f342b9dec09fa85247bef69ae2349/8.0/Dockerfile#L67) 是存放資料庫檔案。 正確的做法:程式上線應該透過建置新的 image,跑新的 container 來取代舊的。 > 可以試著反過來思考,為何我們會覺得 Docker 上跑 MySQL / Redis / Apache 很快速方便?因為它們把程式都包在 image 裡了。 C 的問題:「把機敏資訊加入 image 裡」這句話的意思就是,只要有辦法下載 image 就能得到機敏資訊。當然可以限制下載 image 的權限,但還是有另一個問題是難以做到環境同步,因為線上環境跟測試環境會是不同的 image--因為機敏資訊設定通常是不一樣的。 正確的做法:使用 `ENV` 環境參數(參考 [12 Factor Config](https://12factor.net/config)),把機敏資訊存在環境。 ::: --- --- --- - [回到目錄](https://hackmd.io/@Hualiteq/r1lye3M3d)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up