# Docker 筆記 ###### tags: `Docker` * 系統需求 : Ubuntu ### 第一部分 : 下載安裝 #### 確保使用HTTPS下載軟件不被竄改 ``` # sudo apt-get update # sudo apt-get install apt-transport-https ca-certificates curl gnupg lsb-release ``` #### 下載軟件包需要的 GPG 密鑰 ``` # curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg ``` #### 在 sources.list 中添加 Docker 軟件包 ``` # echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null ``` ### 第二部分 : 啟動服務 ``` //開啟服務 # systemctl start docker //設定開機啟動 # systemctl enable docker //停止服務 # systemctl stop docker //查看服務狀態 # systemctl status docker ``` ### 第三部分 : 介紹指令 #### Docker Image * Docker 映像檔是一個模板,用來重複產生容器實體 1. 列出本地的 image ``` # docker images # docker image ls ``` 2. 刪除 image ``` # docker image rm -f [image id] # docker rmi [image id] ``` 3. 替 image 加上標籤/別名 ``` # docker tag [source] [target] ``` 4. 建立容器但沒有執行 ``` # docker create -it [image] --name [container] ``` 5. 將容器轉換為 image ``` # docker commit [container id] [new image name] ``` 6. 查看映像檔的詳細資料 ``` # docker inspect [image] ``` 7. 執行 A 專案的 docker image(B的版本) ``` # docker run A:B` ``` 8. 看看這個 image 的磁碟空間 ``` # docker run A df ``` #### Docker Container * 容器是用映像檔建立出來的執行實例,它可以被啟動、開始、停止、刪除。每個容器都是相互隔離、保證安全的平台。 1. 列出正在運行的contianer ``` docker ps ``` 2. 列出所有的 Container ``` # docker ps -a ``` 3. 用 `<CONTAINER ID>` 刪除 Container ``` # docker rm -f <CONTAINER ID> # docker pull simonwxzhao/auto-test ``` 4. 建立 Container ``` # docker create -i -t --name [container] [image name] # docker create -i -t --name foo docker-test 3756e5fc76f82e66cc10e3aca0d111bccaa61a62b41810c85254afff725c67f0 # docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3756e5fc76f8 docker-test "/docker-entrypoint.…" 7 seconds ago Created foo ``` 5. 執行容器 ``` # docker start -i [container] ``` 6. 離開容器 (`ctrl+p+q`) 後再進入 ``` # docker attach [container] ``` 7. 停止容器 ``` # docker stop [container] ``` 8. 刪除容器 ``` # docker rm [container] ``` 9. 刪除全部容器 ``` # docker rm $(docker ps -aq) # docker rm -f `docker ps -aq` ``` #### Registry (Docker Hub) * 倉庫(Repository)是集中存放映像檔檔案的場所 1. 登入 Registry ``` # docker login ``` 2. 經由8080進入倉庫 ``` # docker login localhost:8080 ``` 3. 退出 Registry ``` # dokcer logout ``` 4. 抓取A專案最新的版本 ``` # docker pull A:latest ``` 5. 把映像檔推到倉庫(docker hub) ``` # docker push [image]:[tag] ``` ##### 查看 Images 的紀錄 ``` # docker run -d -v /home/user/volume-demo:/usr/share/nginx/html --name vol-example nginx:latest 97cb5f09456c63a2d117ef035fade334869d980141e696195fdc5765d9e49e53 # docker run -d -v $PWD:/usr/share/nginx/html --name vol-example nginx:latest # docker run -d -v $(pwd):/usr/share/nginx/html --name vol-example nginx:latest ``` ### 第四部分 : DOCKER IMAGE PUSH/PULL ON DOCKER HUB #### 1. 登入 Docker Hub 帳號 ``` # docker login Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username: yichien1019 Password: WARNING! Your password will be stored unencrypted in /root/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store Login Succeeded ``` #### 2. 修改 IMAGE 名稱 : `docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]` ``` # docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker-test latest e51d91cc64cd 3 hours ago 22.3MB # docker tag docker-test:latest yichien/docker-test0.1 # docker images REPOSITORY TAG IMAGE ID CREATED SIZE docker-test latest e51d91cc64cd 3 hours ago 22.3MB yichien/docker-test0.1 latest e51d91cc64cd 3 hours ago 22.3MB ``` #### 3. 回推 IMAGE ``` # docker push yichien1019/docker-test0.1 Using default tag: latest The push refers to repository [docker.io/yichien1019/docker-test0.1] d9d86e7e46f7: Pushed 3633e038dbe3: Pushed e8f8cd3583be: Pushed 0614f8d14b89: Pushed 029c325415ee: Pushed 777b2c648970: Pushed latest: digest: sha256:33d6c64f7198c74f49702d74cbdf1012e136864f2cbebf7c9fd828fff9b0187f size: 1567 ``` ##### DOCKER HUB ![](https://i.imgur.com/GkLNgfu.png) #### 4. 刪除鏡像 * 刪除鏡像前,如果有容器正在使用,要先把容器刪除,才能再刪除鏡像 ``` # docker rmi yichien1019/docker-test0.1 Untagged: yichien1019/docker-test0.1:latest Untagged: yichien1019/docker-test0.1@sha256:33d6c64f7198c74f49702d74cbdf1012e136864f2cbebf7c9fd828fff9b0187f ``` #### 5. 測試 PULL IMAGE ``` # docker pull yichien1019/docker-test0.1 Using default tag: latest latest: Pulling from yichien1019/docker-test0.1 Digest: sha256:33d6c64f7198c74f49702d74cbdf1012e136864f2cbebf7c9fd828fff9b0187f Status: Downloaded newer image for yichien1019/docker-test0.1:latest docker.io/yichien1019/docker-test0.1:latest # docker images REPOSITORY TAG IMAGE ID CREATED SIZE webserver-test2.0 latest b2cf4dcf3367 About an hour ago 22.3MB docker-test latest e51d91cc64cd 4 hours ago 22.3MB yichien1019/docker-test0.1 latest e51d91cc64cd 4 hours ago 22.3MB myhttpd 0.1 063c01b6b66d 4 hours ago 145MB nginx latest 5d58c024174d 4 days ago 142MB httpd 2.4 d16a51d08814 2 weeks ago 145MB centos 7 eeb6ee3f44bd 13 months ago 204MB nginx 1.19.6-alpine 629df02b47c8 22 months ago 22.3MB ``` ### 第五部分 : DOCKER IMAGE PUSH/PULL ON AWS ECR #### 1. 先在 AWS ECR 上創建儲存庫 * 可以選擇 Public(公有) 或 Private(私有) #### 2. 設定 AWS Cli ``` # aws configure AWS Access Key ID [****************EMP7]: [存取金鑰 ID] AWS Secret Access Key [****************wT0D]: [私密存取金鑰] Default region name [ap-northeast-1]: [AWS 區域] Default output format [json]: [輸出格式] # aws configservice put-aggregation-authorization --authorized-account-id 285167715064 --authorized-aws-region ap-northeast-1 { "AggregationAuthorization": { "AggregationAuthorizationArn": "arn:aws:config:ap-northeast-1:285167715064:aggregation-authorization/285167715064/ap-northeast-1", "AuthorizedAccountId": "285167715064", "AuthorizedAwsRegion": "ap-northeast-1", "CreationTime": "2022-08-01T09:59:02.916000+08:00" } } ``` #### 3. 擷取驗證字符並將 Docker 用戶端驗證至您的登錄檔 ``` # aws ecr get-login-password --region ap-northeast-1 | docker login --username AWS --password-stdin 285167715064.dkr.ecr.ap-northeast-1.amazonaws.com Login Succeeded ``` #### 4. 建置 Docker 映像 ``` # docker build -t eva-test . [+] Building 0.1s (9/9) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 31B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/nginx:latest 0.0s => [1/4] FROM docker.io/library/nginx 0.0s => [internal] load build context 0.0s => => transferring context: 56B 0.0s => CACHED [2/4] WORKDIR /app 0.0s => CACHED [3/4] COPY index.html /usr/share/nginx/html 0.0s => CACHED [4/4] COPY nginx.conf /etc/nginx/conf.d/default.conf 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:2e3da95a84aa6f7a756b8d4751f2f1655df8b08c2285b159ee8eba00adab9118 0.0s => => naming to docker.io/library/eva-test ``` #### 5. 建置完成後加上標籤,以便將映像推送至此儲存庫 ``` # docker tag eva-test:latest 285167715064.dkr.ecr.ap-northeast-1.amazonaws.com/eva-test:latest ``` #### 6. 將此映像推送至 AWS 儲存庫 ``` # docker push 285167715064.dkr.ecr.ap-northeast-1.amazonaws.com/eva-test:latest The push refers to repository [285167715064.dkr.ecr.ap-northeast-1.amazonaws.com/eva-test] 2e2a82515826: Pushed 083c1a49f16a: Pushed e5862212052f: Pushed abc66ad258e9: Pushed 243243243ee2: Pushed f931b78377da: Pushed d7783033d823: Pushed 4553dc754574: Pushed 43b3c4e3001c: Pushed latest: digest: sha256:dea4b2ec40ab9d2d22e3839c94bb1c9f1de980fdd97ce9b589d57d43bde11033 size: 2191 ``` #### AWS ECR ![](pic/aws_ecr_images.png) ### 第六部分 : DOCKER COMMIT * 從容器創建一個新鏡像 | 參數 | 內容 | |------|------| | -a | --author string 作者 | | -c | --change list 對創建的鏡像應用 Dockerfile 指令 | | -m | --message 提交時的說明文字 | | -p | --pause 在commit時,將容器暫停 | #### Example. `docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]` ``` # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 5371d449897d webserver-test "/docker-entrypoint.…" 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcp, :::8080->80/tcp heuristic_diffie # docker commit 537 webserver-test2.0 sha256:b2cf4dcf3367c2c35d37aad4c822006b0ec282056ddfd2b28007d65e8697b9d3 # docker images REPOSITORY TAG IMAGE ID CREATED SIZE webserver-test2.0 latest b2cf4dcf3367 50 seconds ago 22.3MB webserver-test latest e51d91cc64cd 2 hours ago 22.3MB ``` ### 第七部分 : Docker Volume * Docker 容器的資料儲存(storage)問題 * 使用 bind mount 的 volume 其實就是透過 host machine 的檔案系統(filesystem)提供容器儲存的能力。 #### 1. 新增 volume ``` # docker volume create [volume name] # docker volume create my-volume my-volume ``` #### 2. 列出所有 volumes ``` # docker volume ls DRIVER VOLUME NAME local my-volume ``` #### 3. 列出 volume 詳細資訊 ``` # docker volume inspect [volume name] # docker volume inspect my-volume [ { "CreatedAt": "2022-10-24T23:14:27-07:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/my-volume/_data", "Name": "my-volume", "Options": {}, "Scope": "local" } ] ``` #### 4. 共享儲存空間 ``` # docker run -d -p 8080:80 --name volume1 -v my-volume:/usr/share/nginx/html nginx:latest 4e0d5b72e0cf07fb3ad6ad81366067496ea864ab3830c83e42e8ab7dec601657 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4e0d5b72e0cf nginx:latest "/docker-entrypoint.…" 14 seconds ago Up 13 seconds 0.0.0.0:8080->80/tcp, :::8080->80/tcp volume1 ac87c68a7c79 project1:1.5 "/docker-entrypoint.…" 2 hours ago Up 2 hours 80/tcp, 0.0.0.0:8855->8877/tcp, :::8855->8877/tcp stupefied_kalam # docker exec -it volume1 bash root@4e0d5b72e0cf:/# cd /usr/share/nginx/html/ root@4e0d5b72e0cf:/usr/share/nginx/html# echo "volume test example" > index.html root@4e0d5b72e0cf:/usr/share/nginx/html# cat index.html volume test example # curl localhost:8080 volume test example ``` ``` # docker run -d -p 8181:80 --name volume2 -v my-volume:/usr/share/nginx/html nginx:latest b5aa20daea070d5ba7d323ac3d32112944d70fe699b581fac5fb9a4832a930ae # docker exec -it volume2 bash root@b5aa20daea07:/# cd /usr/share/nginx/html/ root@b5aa20daea07:/usr/share/nginx/html# cat index.html volume test example ``` ### 運行於 Docker 環境中的應用程式 (DNS SERVER ADGUARDHOME) #### 第一部分 : 拉取鏡像 & 運行 * 拉取鏡像 ``` # docker pull adguard/adguardhome Using default tag: latest latest: Pulling from adguard/adguardhome 213ec9aee27d: Pull complete 31f9e8542749: Pull complete 35797e6c4bae: Pull complete 3c40b1afc955: Pull complete 4f4fb700ef54: Pull complete Digest: sha256:d6c7532726d132d44cc006a9b25f810225554264a905870fc9dbeb71e7405471 Status: Downloaded newer image for adguard/adguardhome:latest docker.io/adguard/adguardhome:latest ``` * 運行鏡像 (本機 67/68 port 會被占用,所以改成 167/168 port) ``` # docker run --name adguardhome --restart unless-stopped -v /my/own/workdir:/opt/adguardhome/work -v /my/own/confdir:/opt/adguardhome/conf -p 53:53/tcp -p 53:53/udp -p 167:67/udp -p 168:68/udp -p 80:80/tcp -p 443:443/tcp -p 443:443/udp -p 3000:3000/tcp -p 853:853/tcp -p 784:784/udp -p 853:853/udp -p 8853:8853/udp -p 5443:5443/tcp -p 5443:5443/udp -d adguard/adguardhome a5ff73392e28a0737ea940aefeaa774bbdb9c14d5407ac3927a8d8708c0ed07a //--restart unless-stopped : 如果被停止會重新啟動 ``` * 占用端口清除 (因為DNS SERVER需要再53端口開啟,但卻被佔用了,這時需要查詢是誰占用53端口,並把它刪除) ``` # netstat -tunlp | grep 53 # kill -9 XXX ``` #### 第二部分 : 連線 ![](https://i.imgur.com/GBcszB7.png) ![](https://i.imgur.com/0EVD9Cb.png) ![](https://i.imgur.com/e8A3IYm.png) ![](https://i.imgur.com/i5Na4IC.png) ![](https://i.imgur.com/LNPERRS.png) ![](https://i.imgur.com/Wq1BoNJ.png) # DOCKERFILE 撰寫方式 ## 建立 Dockerfile | 指令 | 內容 | | ---- | ---- | | FROM | 使用到的 Docker Image 名稱 | | MAINTAINER | 說明撰寫和維護的人是誰,也可以給 E-mail 的資訊 | | WORKDIR | 指定 Docker 執行起來時候的預設目錄位置 | | EXPOSE | 指定所有發布的 port | | RUN | 放 Linux 指令,用來執行安裝和設定這個 Image 需要的東西 | | ADD | 把 Local 的檔案複製到 Image 裡,如果是 tar.gz 檔複製進去 Image 時會順便自動解壓縮 | | COPY | 複製本機的資料放到容器內 | | ENV | 用來設定環境變數 | | CMD | 指定 Instance 啟動後所要執行的指令 | ### Example. ``` FROM 基底映像檔 USER 指定執行容器的使用者名稱或者ID WORKDIR 工作目錄 LABEL 指定維護者資訊 ENV 建立環境變數可供後續使用 ARG --build-arg 帶入變數,讓build可以結合外部指定建構時所需的參數 COPY 複製本機上的資料至容器 ADD 複製指定的url或是tar檔(自動解壓縮)至指定的目錄 VOLUME 建立一個可以從本幾對外掛載的目錄 EXPOSE 宣告此容器Run在哪個 Port RUN Build layer 中執行的指令,一個Dockerfile中可以有多個RUN CMD 當容器啟動之後默認會執行的命令,可以有多個CMD,不過只有最後一個會生效 ENTRYPOINT 指定容器啟動程序及參數 ``` ### 簡單 Dockerfile ``` # vim Dockerfile FROM nginx WORKDIR /app EXPOSE 8877 COPY index.html /usr/share/nginx/html COPY default.conf /etc/nginx/conf.d/default.conf # vim nginx.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } } # vim index.html <p>hello world for dockerfile test</p> # docker build -t project1:1.5 . Sending build context to Docker daemon 4.096kB Step 1/4 : FROM nginx ---> 5d58c024174d Step 2/4 : EXPOSE 8877 ---> Running in f61eaeaff094 Removing intermediate container f61eaeaff094 ---> 04101da4691a Step 3/4 : COPY index.html /usr/share/nginx/html ---> 51a92f98877a Step 4/4 : COPY default.conf /etc/nginx/conf.d/default.conf ---> 35d18220cde9 Successfully built 35d18220cde9 Successfully tagged project1:1.5 # docker images REPOSITORY TAG IMAGE ID CREATED SIZE project1 1.5 35d18220cde9 2 minutes ago 142MB # docker run -d --rm -p 8855:8877 project1:1.5 cd55ab764022f8fb108f2860af6a851a3be7c2b395c00d3e9b73fb3293556d49 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cd55ab764022 project1:1.5 "/docker-entrypoint.…" 4 seconds ago Up 2 seconds 80/tcp, 0.0.0.0:8855->8877/tcp, :::8855->8877/tcp charming_dubinsky a3cbe5317d5a project1:1.2 "/docker-entrypoint.…" 26 minutes ago Up 26 minutes 0.0.0.0:8888->80/tcp, :::8888->80/tcp thirsty_hoover fbef1936ce47 project1:1.0 "/docker-entrypoint.…" 35 minutes ago Up 35 minutes 0.0.0.0:8866->80/tcp, :::8866->80/tcp web # curl localhost:8855 hi # docker exec -it ac8 bash root@ac87c68a7c79:/app# pwd /app ``` ### 運行於 Docker 環境中的應用程式 (Python) ``` # tree . ├── app.py ├── Dockerfile └── requirements.txt 0 directories, 3 files ``` * Dockerfile ``` # vim Dockerfile FROM python:3.8-slim-buster WORKDIR /app COPY . . RUN pip3 install Flask RUN pip3 install -r requirements.txt CMD ["python3","app.py"] ``` * app.py ``` # vim app.py import os from flask import Flask app = Flask(__name__) HOST = os.getenv('HOST', '0.0.0.0') PORT = os.getenv('PORT', 5000) HOST_NAME = os.getenv('HOST_NAME', 'none') @app.route('/') def hello_world(): return 'Hello,Docker!This application is running on host: {0}'.format(HOST_NAME) if __name__ == '__main__': print('Server is running: {0}:{1} on host name {2}'.format(HOST, PORT, HOST_NAME)) app.run(host=HOST,port=PORT) ``` * 創建映像 ``` # docker build -t app . Sending build context to Docker daemon 3.584kB Step 1/6 : FROM python:3.8-slim-buster ---> d55c26ea3903 Step 2/6 : WORKDIR /app ---> Running in e94cebcba9cd Removing intermediate container e94cebcba9cd ---> 4bda9f7b8c43 Step 3/6 : COPY . . ---> 454072492443 Step 4/6 : RUN pip3 install Flask ---> Running in 6ea2fcd9701b Installing collected packages: zipp, MarkupSafe, itsdangerous, click, Werkzeug, Jinja2, importlib-metadata, Flask Successfully installed Flask-2.2.2 Jinja2-3.1.2 MarkupSafe-2.1.1 Werkzeug-2.2.2 click-8.1.3 importlib-metadata-5.0.0 itsdangerous-2.1.2 zipp-3.10.0 Removing intermediate container 6ea2fcd9701b ---> 6144b6fd9fa3 Step 5/6 : RUN pip3 install -r requirements.txt ---> Running in 744d5dcb5639 Removing intermediate container 744d5dcb5639 ---> dec226758e95 Step 6/6 : CMD ["python3","app.py"] ---> Running in 9c98ff0a17ea Removing intermediate container 9c98ff0a17ea ---> a593889953fc Successfully built a593889953fc Successfully tagged app:latest ``` * 執行容器 ``` # docker run -p 8000:5000 app Server is running: 0.0.0.0:5000 on host name none * Serving Flask app 'app' * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://172.17.0.5:5000 Press CTRL+C to quit 172.17.0.1 - - [25/Oct/2022 07:53:35] "GET / HTTP/1.1" 200 - # curl localhost:8000 Hello,Docker!This application is running on host: none ``` ### 運行於 Docker 環境中的應用程式 (Python Flask + Redis 應用) * Dockerfile ``` # vim Dockerfile FROM python:3.9.5-slim RUN pip install flask redis && \ groupadd -r flask && useradd -r -g flask flask && \ mkdir /src && \ chown -R flask:flask /src USER flask COPY app.py /src/app.py WORKDIR /src ENV FLASK_APP=app.py REDIS_HOST=redis EXPOSE 5000 CMD ["flask", "run", "-h", "0.0.0.0"] ``` * app.py ``` # vim app.py from flask import Flask from redis import Redis import os import socket app = Flask(__name__) redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379) @app.route('/') def hello(): redis.incr('hits') return f"Hello Container World! I have been seen {redis.get('hits').decode('utf-8')} times and my hostname is {socket.gethostname()}.\n" ``` * 下載 Redis 映像檔 ``` # docker image pull redis Using default tag: latest latest: Pulling from library/redis e9995326b091: Pull complete f2cd78d6f24c: Pull complete 8f3614d34c89: Pull complete 697fd51ec515: Pull complete a554cf50a327: Pull complete 66f93c02e79c: Pull complete Digest: sha256:aeed51f49a6331df0cb2c1039ae3d1d70d882be3f48bde75cd240452a2348e88 Status: Downloaded newer image for redis:latest docker.io/library/redis:latest ``` * 創建 Flask-demo 映像 ``` # docker image build -t flask-demo . Sending build context to Docker daemon 3.072kB Step 1/8 : FROM python:3.9.5-slim ---> c71955050276 Step 2/8 : RUN pip install flask redis && groupadd -r flask && useradd -r -g flask flask && mkdir /src && chown -R flask:flask /src ---> Running in c477e9c5acae Collecting flask Downloading Flask-2.2.2-py3-none-any.whl (101 kB) Collecting redis Downloading redis-4.3.4-py3-none-any.whl (246 kB) Collecting Werkzeug>=2.2.2 Downloading Werkzeug-2.2.2-py3-none-any.whl (232 kB) Collecting itsdangerous>=2.0 Downloading itsdangerous-2.1.2-py3-none-any.whl (15 kB) Collecting Jinja2>=3.0 Downloading Jinja2-3.1.2-py3-none-any.whl (133 kB) Collecting importlib-metadata>=3.6.0 Downloading importlib_metadata-5.0.0-py3-none-any.whl (21 kB) Collecting click>=8.0 Downloading click-8.1.3-py3-none-any.whl (96 kB) Collecting zipp>=0.5 Downloading zipp-3.10.0-py3-none-any.whl (6.2 kB) Collecting MarkupSafe>=2.0 Downloading MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (25 kB) Collecting deprecated>=1.2.3 Downloading Deprecated-1.2.13-py2.py3-none-any.whl (9.6 kB) Collecting packaging>=20.4 Downloading packaging-21.3-py3-none-any.whl (40 kB) Collecting async-timeout>=4.0.2 Downloading async_timeout-4.0.2-py3-none-any.whl (5.8 kB) Collecting wrapt<2,>=1.10 Downloading wrapt-1.14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl (77 kB) Collecting pyparsing!=3.0.5,>=2.0.2 Downloading pyparsing-3.0.9-py3-none-any.whl (98 kB) Installing collected packages: zipp, wrapt, pyparsing, MarkupSafe, Werkzeug, packaging, Jinja2, itsdangerous, importlib-metadata, deprecated, click, async-timeout, redis, flask Successfully installed Jinja2-3.1.2 MarkupSafe-2.1.1 Werkzeug-2.2.2 async-timeout-4.0.2 click-8.1.3 deprecated-1.2.13 flask-2.2.2 importlib-metadata-5.0.0 itsdangerous-2.1.2 packaging-21.3 pyparsing-3.0.9 redis-4.3.4 wrapt-1.14.1 zipp-3.10.0 WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv WARNING: You are using pip version 21.1.3; however, version 22.3 is available. You should consider upgrading via the '/usr/local/bin/python -m pip install --upgrade pip' command. Removing intermediate container c477e9c5acae ---> 746e7b1eeb92 Step 3/8 : USER flask ---> Running in 3195047b50fe Removing intermediate container 3195047b50fe ---> d3bc26ba3d50 Step 4/8 : COPY app.py /src/app.py ---> fdd063765656 Step 5/8 : WORKDIR /src ---> Running in 2a1b120a3818 Removing intermediate container 2a1b120a3818 ---> 3831e90d5fd7 Step 6/8 : ENV FLASK_APP=app.py REDIS_HOST=redis ---> Running in fd5c4f9594dc Removing intermediate container fd5c4f9594dc ---> f635f65302c3 Step 7/8 : EXPOSE 5000 ---> Running in 2110f2c1c15c Removing intermediate container 2110f2c1c15c ---> aaf83e0f20fd Step 8/8 : CMD ["flask", "run", "-h", "0.0.0.0"] ---> Running in 9131ba8886e0 Removing intermediate container 9131ba8886e0 ---> 82a4b8deae23 Successfully built 82a4b8deae23 Successfully tagged flask-demo:latest ``` * 創建 Docker Bridge ``` # docker network create -d bridge demo-network aa7804bc99a19a1d203b6beca727d581b6a69e74d437a0a1c1b57ecc200b1d1a # docker network ls NETWORK ID NAME DRIVER SCOPE 4bd8e05f4cb8 bridge bridge local aa7804bc99a1 demo-network bridge local 12b814a11be2 host host local 2aa33d0d503f none null local ``` * 創建 Redis 容器 ``` # docker run -d --name redis-server --network demo-network redis 78fa815fecbc6440b55eaaef6de58683eeecfb9d40259144685882ff50196756 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 78fa815fecbc redis "docker-entrypoint.s…" 4 seconds ago Up 2 seconds 6379/tcp redis-server ``` * 創建 Flask 容器 ``` # docker container run -d --network demo-network --name flask-demo --env REDIS_HOST=redis-server -p 5000:5000 flask-demo 137444e0d1f8eb10398b94c70e1b0b1e53800873eda754c779a729a520fce0d7 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 137444e0d1f8 flask-demo "flask run -h 0.0.0.0" 15 seconds ago Up 14 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp flask-demo 78fa815fecbc redis "docker-entrypoint.s…" 25 seconds ago Up 23 seconds 6379/tcp redis-server ``` * 開啟瀏覽器 `http://<IP>:5000/` 查看頁面 ![](https://i.imgur.com/N1BqF8e.png) ![](https://i.imgur.com/ybyKJ6I.png) ### 創建一個圖形化界面的 Docker #### 第一部分 : Dockerfile * Dockerfile ``` FROM ubuntu:14.04 ENV DEBIAN_FRONTEND noninteractive ENV HOME /root RUN sed -i 's/# \(.*multiverse$\)/\1/g' /etc/apt/sources.list RUN \ apt-get update && \ apt-get install -y build-essential && \ apt-get install -y software-properties-common && \ apt-get install -y byobu curl git htop man unzip vim wget && \ apt-get install -y xorg lxde-core lxterminal tightvncserver && \ rm -rf /var/lib/apt/lists/* EXPOSE 5901 WORKDIR /root CMD ["bash"] ``` #### 第二部分 : 創建鏡像 & 執行 ``` # docker build -t hello-vnc:1.0 . # docker run -it --rm -p 5901:5901 -e USER=root hello-vnc:1.0 bash -c "vncserver :1 -geometry 1280x800 -depth 24 && tail -F /root/.vnc/*.log" You will require a password to access your desktops. Password: Verify: Would you like to enter a view-only password (y/n)? n ``` #### 第三部分 : 使用 vnc viewer 連線 * <IP>:5901 ![](https://i.imgur.com/cTbYSCM.png) ## 結合機器學習 #### 第一部分 : 安裝 ``` # wget https://bootstrap.pypa.io/pip/2.7/get-pip.py # python get-pip.py # pip install sklearn ``` #### 第二部分 : `train_model.py` & `server.py` & `client.py` * train_model.py ``` # coding: utf-8 import pickle from sklearn import datasets from sklearn.model_selection import train_test_split from sklearn import tree # simple demo for traing and saving model iris=datasets.load_iris() x=iris.data y=iris.target #labels for iris dataset labels ={ 0: "setosa", 1: "versicolor", 2: "virginica" } x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=.25) classifier=tree.DecisionTreeClassifier() classifier.fit(x_train,y_train) predictions=classifier.predict(x_test) #export the model model_name = 'model.pkl' print("finished training and dump the model as {0}".format(model_name)) pickle.dump(classifier, open(model_name,'wb')) ``` ``` # python train_model.py finished training and dump the model as model.pkl # ls get-pip.py model.pkl train_model.py ``` * server.py ``` # coding: utf-8 import pickle from flask import Flask, request, jsonify app = Flask(__name__) # Load the model model = pickle.load(open('model.pkl', 'rb')) labels = { 0: "versicolor", 1: "setosa", 2: "virginica" } @app.route('/api', methods=['POST']) def predict(): # Get the data from the POST request. data = request.get_json(force = True) predict = model.predict(data['feature']) return jsonify(predict[0].tolist()) if __name__ == '__main__': app.run(debug = True, host = '0.0.0.0') ``` ``` # python server.py * Serving Flask app "server" (lazy loading) * Environment: production WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Debug mode: on * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 152-542-791 192.168.163.138 - - [27/Oct/2022 23:36:42] "POST /api HTTP/1.1" 200 - 192.168.163.138 - - [27/Oct/2022 23:36:45] "POST /api HTTP/1.1" 200 - 192.168.163.138 - - [27/Oct/2022 23:36:46] "POST /api HTTP/1.1" 200 - ``` * client.py ``` # coding: utf-8 import requests # Change the value of experience that you want to test url = 'http://192.168.163.138:5000/api' feature = [[5.8, 4.0, 1.2, 0.2]] labels ={ 0: "setosa", 1: "versicolor", 2: "virginica" } r = requests.post(url,json={'feature': feature}) print(labels[r.json()]) ``` ``` # python client.py setosa ``` #### 第三部分 : Dockerfile * Dockerfile ``` # train and run the model with RESTful api FROM nitincypher/docker-ubuntu-python-pip COPY ./requirements.txt /app/requirements.txt WORKDIR /app RUN pip install -r requirements.txt COPY train_model.py /app COPY server.py /app CMD python /app/train_model.py && python /app/server.py ``` * 創建容器 ``` # docker build -t myiris:1.0 . # docker run -itd --name myiris -p 5000:5000 myiris:1.0 b93a3f4a86b3d69d0911dcf82c85aefad7cf4f5f23ee74e11e0e0144bb4f7330 ``` * 測試 ``` # python client.py setosa ``` # DOCKER NETWORK > Docker network 因應不同需求分為幾種類別,分別是 : Bridge networks、Host networking、Overlay networks、Macvlan networks #### Bridge networks * 若無更改 network driver,docker network 預設為 Bridge networks,Bridge networks 通常運用做需要獨立通信的 Container 當中 #### Host networking * Host networking 會使 Container 的隔離性質消失,在該 Container 當中可以直接使用例如 localhost 來找尋到主機上的 port 或其他資源 #### Overlay networks * Overlay networks 能使不同 docker daemons 間互相通信,使不同群集的服務能夠交流,亦也能使不同的獨立 Containers 間互相通信 #### Maclvan networks * Maclvan 允許使用者能將實體網卡設定多個 mac address,並將這些 address 分配給Container 使用,使其在 network 上顯示為 physical address 而非 virtual address。maclvan 希望能讓某些只能連到物理設備的應用程序能夠正常運作 | 參數 | 用途 | |------|------| |connect|將容器連接到網路| |vreate|創建網路| |disconnect|斷開容器與網路的連接| |inspect|顯示一個或多個網路的詳細信息| |ls|列出網路| |prune|刪除所有未使用的網路| |rm|移除一個或多個網路| ``` # docker network ls NETWORK ID NAME DRIVER SCOPE 4bd8e05f4cb8 bridge bridge local 12b814a11be2 host host local 2aa33d0d503f none null local ``` ``` # docker network create --driver bridge mybr bf3f3f2dd57d2fe8ee4d5188beb79b66005dba2909a5f68d60450802bda3c20e # docker network ls NETWORK ID NAME DRIVER SCOPE 4bd8e05f4cb8 bridge bridge local 12b814a11be2 host host local bf3f3f2dd57d mybr bridge local 2aa33d0d503f none null local # docker network inspect mybr [ { "Name": "mybr", "Id": "bf3f3f2dd57d2fe8ee4d5188beb79b66005dba2909a5f68d60450802bda3c20e", "Created": "2022-10-25T23:41:25.713906621-07:00", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": {}, "Options": {}, "Labels": {} } ] ``` # DOCKER-COMPOSE * 同時建立多容器應用程式 ### YAML 檔格式指令 #### `version` * 指定 yml 使用哪個版本的 compose #### `build` * 指定 Dockerfile 所在文件夾的路徑 #### `command` * 覆蓋容器啟動後默認執行的命令 #### `container_name` * 指定容器名稱 (默認將會使用 `項目名稱_服務名稱_序號` 這樣的格式) * `container_name: docker-web-container` #### `devices` * 指定設備映射關係 * ``` devices: - "/dev/ttyUSB1:/dev/ttyUSB0" ``` #### `depends_on` * 解決容器的依賴、啟動先後的問題,優先權 * ``` version: '3' services: web: build: . depends_on: - db - redis redis: image: redis db: image: postgres ``` #### `dns` * 自定義 DNS 服務器 (可以是一個值,也可以是一個列表) * ``` dns: 8.8.8.8 dns: - 8.8.8.8 - 114.114.114.114 ``` #### `dns_search` * 配置 DNS 搜索域 (可以是一個值,也可以是一個列表) * ``` dns_search: example.com dns_search: - domain1.example.com - domain2.example.com ``` #### `env_file` * 從文件中獲取環境變量,可以為單獨的文件路徑或列表 * ``` env_file: .env env_file: - ./common.env - ./apps/web.env - /opt/secrets.env ``` #### `environment` * 設置環境變量。你可以使用數組或字典兩種格式 (只給定名稱的變量會自動獲取運行 Compose 主機上對應變量的值,可以用來防止洩露不必要的數據) * ``` environment: RACK_ENV: development SESSION_SECRET: environment: - RACK_ENV=development - SESSION_SECRET ``` #### `expose` * 暴露端口,但不映射到宿主機,只被連接的服務訪問 * ``` expose: - "3000" - "8000" ``` #### `image` * 指定為鏡像名稱或鏡像 ID。如果鏡像在本地不存在,Compose 將會嘗試拉取這個鏡像。 * `image: ubuntu` #### `labels` * 為容器添加 Docker 元數據(metadata)信息 (可以為容器添加輔助說明信息) * ``` labels: com.startupteam.description: "webapp for a startup team" com.startupteam.department: "devops department" com.startupteam.release: "rc3 for v1.0" ``` #### `network_mode` * 設置網絡模式 (使用和 docker run 的 --network 參數一樣的值) * ``` network_mode: "bridge" network_mode: "host" network_mode: "none" network_mode: "service:[service name]" network_mode: "container:[container name/id]" ``` #### `networks` * 配置容器連接的網路 * ``` version: "3" services: some-service: networks: - some-network - other-network networks: some-network: other-network: ``` #### `ports` * 暴露端口信息 (使用宿主端口:容器端口 (HOST:CONTAINER) 格式,或者僅僅指定容器的端口都可以) * ``` ports: - "3000" - "8000:8000" - "49100:22" - "127.0.0.1:8001:8001" ``` #### `secrets` * 儲存敏感數據 (密碼) * ``` version: "3.1" services: mysql: image: mysql environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password secrets: - db_root_password - my_other_secret secrets: my_secret: file: ./my_secret.txt my_other_secret: external: true ``` #### `volumes` * 數據卷所掛載路徑設置 * 可以設置為宿主機路徑(HOST:CONTAINER)或者數據卷名稱(VOLUME:CONTAINER),並且可以設置訪問模式(HOST:CONTAINER:ro)。 * ``` version: "3" services: my_src: image: mysql:8.0 volumes: - mysql_data:/var/lib/mysql volumes: mysql_data: ``` ### Example. ``` version: '3' services: app1: image: imageName build: context: . dockerfile: dockerfilePath/Dockerfile app2: image: imageName ``` ## 第一部分 : 安裝 ``` # curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0 100 11.2M 100 11.2M 0 0 5748k 0 0:00:01 0:00:01 --:--:-- 23.7M # chmod +x /usr/local/bin/docker-compose # docker-compose --version docker-compose version 1.23.2, build 1110ad01 ``` ## 第二部分 : 撰寫 docker-compose.yml * docker-compose.yml (注意:縮排很重要) ``` version: '2.3' services: flask-demo: image: flask-demo:latest container_name: flask-demo environment: REDIS_HOST: redis-server depends_on: - redis-server ports: - "5000:5000" networks: - demo-network redis-server: image: redis:latest container_name: redis-server networks: - demo-network networks: demo-network: ``` ## 第三部分 : 啟動 ``` # docker-compose up -d Creating redis-server ... done Creating flask-demo ... done # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 262859afcc65 flask-demo:latest "flask run -h 0.0.0.0" 6 seconds ago Up 3 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp flask-demo bd97f8c42c69 redis:latest "docker-entrypoint.s…" 8 seconds ago Up 5 seconds 6379/tcp redis-server ``` ## 第四部分 : 關閉 ``` # docker-compose down Stopping flask-demo ... done Stopping redis-server ... done Removing flask-demo ... done Removing redis-server ... done Removing network test_flask_demo-network # docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ``` ## Docker-Compose 運行於 Docker 環境中的應用程式 (APACHE + PHP + MYSQL) ### 第一部分 : 安裝 mysql + php-apache ``` # docker pull mysql # docker pull radys/php-apache:7.4 ``` ### 第二部分 : 建立 bridge 網路 ``` # docker network create --driver bridge mybridge 5cf62e8432b6d1d62a7e36d4d8264dc78f893eb6cecb92a387960a538dffa791 # docker network ls NETWORK ID NAME DRIVER SCOPE 5d6223670fd7 bridge bridge local 4800c4d375c5 demo-network bridge local 12b814a11be2 host host local 2d5fd2991d14 mybr bridge local 5cf62e8432b6 mybridge bridge local 2aa33d0d503f none null local ``` ### 第三部分 : 產生 MYSQL 容器 ``` # docker run -d --network mybridge --name hello-mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306 mysql 3a41d63af05e9b26ea0383a8fa66dcae61b78b6913728723a4045cfc7770ad45 # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3a41d63af05e mysql "docker-entrypoint.s…" 5 seconds ago Up 3 seconds 33060/tcp, 0.0.0.0:49153->3306/tcp, :::49153->3306/tcp hello-mysql ``` ### 第四部分 : 進入容器建立資料庫 ``` # docker exec -it hello-mysql bash bash-4.4# mysql -u root -p mysql> create database testdb; Query OK, 1 row affected (0.19 sec) mysql> use testdb; Database changed mysql> create table addrbook(name varchar(50) not null, phone varchar(10)); Query OK, 0 rows affected (0.15 sec) mysql> insert into addrbook(name, phone) values ("eva", "0933928937"); Query OK, 1 row affected (0.09 sec) mysql> insert into addrbook(name, phone) values ("yc", "0933928937"); Query OK, 1 row affected (0.01 sec) mysql> select * from addrbook; +------+------------+ | name | phone | +------+------------+ | eva | 0933928937 | | yc | 0933928937 | +------+------------+ 2 rows in set (0.01 sec) ``` ### 第五部分 : 創建持久化的 PHP 容器 ``` # docker run -d -p 80:80 --network mybridge -v /root/myphp:/var/www/html --name hello-php radys/php-apache:7.4 c896a5bb121826d535dcc7bc2f2f467ae358a84520ccfa4f36fdadb698dcfe2d # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES c896a5bb1218 radys/php-apache:7.4 "docker-php-entrypoi…" 7 seconds ago Up 4 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp hello-php 3a41d63af05e mysql "docker-entrypoint.s…" 6 minutes ago Up 6 minutes 33060/tcp, 0.0.0.0:49153->3306/tcp, :::49153->3306/tcp hello-mysql ``` * 測試 ``` # vim /root/myphp/test.php <?php phpinfo(); ?> ``` * 測試結果 ![](pic/test.php.png) ### 第六部分 : 顯示資料庫資料 ``` # vim index.php <?php $servername="hello-mysql"; $username="root"; $password="123456"; $dbname="testdb"; $conn = new mysqli($servername, $username, $password, $dbname); if($conn->connect_error){ die("connection failed:" . $conn->connect_error); } $sql="select name, phone from addrbook"; $result = $conn->query($sql); if($result->num_rows >0){ while($row = $result->fetch_assoc()){ echo "name:" . $row["name"]." phone:".$row["phone"]."<br>"; } } else { echo "0 result"; } ``` * 成果 ![](pic/index.php.png) ### 第七部分 : 用 docker-compose 做 * docker-compose.yml ``` version: '2.3' services: hello-php: image: radys/php-apache:7.4 container_name: hello-php depends_on: - hello-mysql ports: - "80:80" volumes: - /root/myphp:/var/www/html networks: - mybr hello-mysql: image: mysql:latest container_name: hello-mysql environment: MYSQL_ROOT_PASSWORD: 123456 volumes: - /root/mydbdata:/var/lib/mysql ports: - "3306" networks: - mybr networks: mybr: ``` * 指令 ``` # docker-compose up -d Creating hello-mysql ... done Creating hello-php ... done # docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 4309faae535e radys/php-apache:7.4 "docker-php-entrypoi…" 57 seconds ago Up 48 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp hello-php c3cc212dfa2f mysql:latest "docker-entrypoint.s…" 59 seconds ago Up 57 seconds 33060/tcp, 0.0.0.0:49153->3306/tcp, :::49153->3306/tcp hello-mysql # docker exec -it c3c bash bash-4.4# mmysql -u root -p bash: mmysql: command not found bash-4.4# mysql -u root -p mysql> create database testdb; Query OK, 1 row affected (0.10 sec) mysql> use testdb; Database changed mysql> create table addrbook(name varchar(50) not null, phone varchar(10)); Query OK, 0 rows affected (0.12 sec) mysql> insert into addrbook(name, phone) values ("eva", "0933928937"); Query OK, 1 row affected (0.08 sec) mysql> insert into addrbook(name, phone) values ("ycc", "0933928937"); Query OK, 1 row affected (0.00 sec) mysql> select * from addrbook; +------+------------+ | name | phone | +------+------------+ | eva | 0933928937 | | ycc | 0933928937 | +------+------------+ 2 rows in set (0.02 sec) ``` * 本地儲存資料的地方 ``` # cd /root/mydbdata/ # ls auto.cnf binlog.000002 ca-key.pem client-cert.pem #ib_16384_0.dblwr ib_buffer_pool ibtmp1 #innodb_temp mysql.ibd performance_schema public_key.pem server-key.pem testdb undo_002 binlog.000001 binlog.index ca.pem client-key.pem #ib_16384_1.dblwr ibdata1 #innodb_redo mysql mysql.sock private_key.pem server-cert.pem sys undo_001 ```