Docker 技術筆記

前言

這是一份未整理過的 Docker 學習筆記

環境

GCP - ubuntu 18.04

安裝

移除舊版本

sudo apt-get remove docker docker-engine docker.io containerd runc

安裝前設定

更新 apt

sudo apt-get update

使 apt 可以經由 HTTPS 使用倉庫

  • 安裝以下套件,使 apt 可以經由 HTTPS 使用倉庫
    sudo apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

增加 Docker 的正式 GPG 密鑰

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
  • 核對一下我們現在擁有含有 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88 指印的密鑰,我們可以搜尋最後八碼

    sudo apt-key fingerprint 0EBFCD88
  • 理應得到輸出如下:

    pub   rsa4096 2017-02-22 [SCEA]
    9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88
    uid [ unknown] Docker Release (CE deb) <docker@docker.com>
    sub rsa4096 2017-02-22 [S]

設定 Docker 倉庫版本

  • Docker 倉庫 設定為 stable 版,若要設定為 nightlytest 版,可在以下的指令中自由取代 stable
    sudo add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"

安裝 Docker

  • 安裝最新的 Docker CE 版本

    sudo apt-get install docker-ce docker-ce-cli containerd.io -y
  • 你也可以安裝特定版本的 Docker:

  1. 列出版本

    apt-cache madison docker-ce
  2. 應該會得到輸出如下:

    docker-ce | 5:18.09.1~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu  xenial/stable amd64 Packages
    docker-ce | 5:18.09.0~3-0~ubuntu-xenial | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    docker-ce | 18.06.1~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    docker-ce | 18.06.0~ce~3-0~ubuntu | https://download.docker.com/linux/ubuntu xenial/stable amd64 Packages
    ...
  3. 使用上面輸出的第二欄位版本號,結合下面的指令來安裝特定版本

    sudo apt-get install docker-ce=<VERSION_STRING> docker-ce-cli=<VERSION_STRING> containerd.io
  • 運行鏡像 hello-world 來驗證 Docker CE 是否已經正確安裝

    sudo docker run hello-world
  • 如果要使用非 root 身份運行 Docker 的話,可以把你的使用者加到 Docker 群組

    sudo usermod -aG docker your-user



解除安裝

  • 解除安裝 Docker CE

    sudo apt-get purge docker-ce
  • 刪除所有的 image, containers, volumes, 可以使用以下指令。慎用!此指令適合解除安裝後使用。

    sudo rm -rf /var/lib/docker/



docker

  • 列出 Docker 的指令
    docker



docker version

  • 查看 Docker 版本
    docker version



Docker info

  • 查看 Docker 完美安裝資訊
    docker info



docker network

列出 network

docker network ls

查看 network

docker network inspect networkName

建立 network

docker network create -d driverType networkName

docker volume

列出 volume 清單

docker volume ls

列出特定 volume 訊息

docker volume inspect volumeName

刪除 volume

docker volume rm volumeName

清除所有未掛載的 volume

docker volume prune



docker container exec

以 root 登入容器

docker exec -it --user root <container id> /bin/bash

連接還在運行中的 container

docker container exec -it yourContainerIDOrContainerName bash

exec 在運行中的 container 中運行一個新的程序




docker cp

傳檔案進 container

docker cp file containerName:/location



確認 Docker daemon 運行狀況

service docker status
systemctl is-active docker



Docker container run

運行 container

docker container run -it ubuntu:latest /bin/bash
  1. Docker container run 告訴 Docker daemon 開始一個新的 container
  2. -i 告訴 Docker daemancontainer 可以互動
  3. -t 使目前的 terminal 視窗連接 containershell
  4. ubuntu:latest 為開始這個 containerimage
  5. /bin/bash 指定了我們想要運行 container 裡頭的哪一個程序

docker container run -d \
--name web1 \
-p 8080:8080 \
test:latest

-d 表示讓 container 運行在背景 , 與 -it 相反,無法共存
--name 指定 container 的名稱
-p 指定 port , 左邊的是外部可以從瀏覽器存取的 port ,右邊的是 container 向外暴露的 port


架設一個私有倉庫

docker run -d -p 5000:5000 registry

我們也可以將上傳的映像檔備份一份到容器外

docker run -d -p 5000:5000 -v outsideAddress:insideAddress registry

備份容器的 volume

  • 比方說, 先建立一個容器叫做 dbstore, 並且內建一個匿名 volume, 掛在容器內的 /dbdata 位址

    docker run -v /dbdata --name dbstore ubuntu /bin/bash
  • 接下來, 備份的步驟如下:

    • 建立一個新容器, 並且掛載 dbstore 上的 volume, 下面指令中的 --volumes-from 達成這一項
    • 將本機的一個資料夾掛到容器中的 /backup, 下面指令中的 -v $(pwd):/backup 達成這一項
    • 接下來, 我們要把從 dbstore 掛載過來的, 容器內的 /dbdata 資料夾備份到容器外, 下面指令中的 tar cvf /backup/backup.tar /dbdata 達成這一項
    • 當備份完成後, 該容器內沒有其他執行的任務, 所以該容器關閉, --rm 會讓關閉後的容器自行銷毀
docker run --rm --volumes-from dbstore -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata

掛載一個 volume

docker run -d --name mysql2 -v mysql:/var/lib/mysql -p 3310:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:5.7

-v: 外部建立一個 volume 叫做 mysql, 並且掛載到容器內部的 /var/lib/mysql 位置
-p: 映射內外 port
-e: 環境變數
-d: 背景執行


連接到一個指定的容器

docker run --name test2 --link test1 -d busybox /bin/sh -c "while true; do echo hello; sleep 10; done"

指定一個 network

docker run --name test3 --network demo -d busybox /bin/bash

--network: 指定連接到 demo network

與該 container 共享網路

docker run --name test3 --network container:test2 -d busybox /bin/bash



docker system

查看 Docker 真實系統佔用空間

docker system df



離開 container 但不關掉它

CTRL + PQ



docker container ls

  • 列出運行中的 container

    docker container ls
  • 列出所有的 container , 包含已停止的

    docker container ls -a

or

docker ps -a



docker container stop

  • 停止 container
    docker container stop containerIdOrContainerName



docker container start

  • 重新開始停止中的 container
    docker container start containerName



docker rm

  • 刪除 container

    docker rm containerID
  • 刪除所有的 container

    docker rm $(docker container ls -aq) -f



docker commit

  • 將現有的 container 存成一個新的 image
    docker commit -m 'imageMessage' -a 'AuthorName' containerSHA imageName:imageTag

Multi Stage (多階段構建)

  • Multi Stage Build 範本
    # 給予該層一個名字, storefront
    FROM node:latest AS storefront

    # 指定目前工作目錄
    WORKDIR /usr/src/atsea/app/react-app

    # 複製 context 中的 react-app 到當前目錄, 即上一行指令的 react-app
    COPY react-app .

    # 執行 npm install
    RUN npm install

    # 執行 npm run build
    RUN npm run build

    # 給予該層一個名字, 即 appserver
    FROM maven:latest AS appserver

    # 指定目前的工作目錄, 即 /usr/src/atsea
    WORKDIR /usr/src/atsea

    # 複製 context 中的 pom.xml 到當前位置, 即 atsea
    COPY pom.xml .

    # 執行以下指令
    RUN mvn -B -f pom.xml -s /usr/share/maven/ref/settings-docker.xml dependency:resolve

    # 複製 context 中的所有檔案到容器內的當前目錄下, 即 atsea
    COPY . .

    # 執行以下指令
    RUN mvn -B -s /usr/share/maven/ref/settings-docker.xml package -DskipTests

    # 該是最終鏡像了, 沒給 alias
    FROM java:8-jdk-alpine

    # 執行以下命令
    RUN adduser -Dh /home/gordon gordon

    # 指令當前工作目錄
    WORKDIR /static

    # 從上面建構 storefront 中, 複製 /usr/src/atsea/app/react-app/build/ 當中的所有資料到當前工作目錄, 即 /static
    COPY --from=storefront /usr/src/atsea/app/react-app/build/ .

    # 指令當前工作目錄, 即 /app
    WORKDIR /app

    # 從上面建構的 appserver 中, 複製 /usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar 到當前工作目錄, 即 appserver
    COPY --from=appserver /usr/src/atsea/target/AtSea-0.0.1-SNAPSHOT.jar .

    # 指令 ENTRYPOINT, 該命令為容器啟動後默認一定會執行的命令, 無法被替換
    ENTRYPOINT ["java", "-jar", "/app/AtSea-0.0.1-SNAPSHOT.jar"]

    # CMD 在這個位置的角色為上面的 ENTRYPOINT 的 flag, 若在容器啟動指令輸入其他命令, 該 CMD 將會被替換掉
    CMD ["--spring.profiles.active=postgres"]

-t 用來指定 image 的名字
-f 可以用來指定名稱不是 DockfileDockerfile
From 指定基礎 image 來源
RUN 可以在 image 內執行 command 並建立新的 layer ,每一個 RUN 都會建立一層 layer
COPY 可以增加複製檔案到你的 image
EXPOSE 暴露 APP 用的 port
ENTRYPOINT 設定當 image 被啟動為一個 container 時,預設執行的指令




Docker image pull

  • 拉下 image

    docker image pull ubuntu:latest
  • 根據 digests 拉下 image

    docker image pull imageNane@sha256:specificDigests



docker image ls

  • 列出下載過的 image

    docker image ls
  • 列出下載過的 image 以及 digest

    docker image ls --digests



docker rmi

  • 刪除 image

    docker rmi imageID
  • 刪除所有的 image

    docker image rm $(docker image ls -q) -f



docker build

  • 建立 image
    docker image build -t imageName:tagName .



docker image inspect

  • 列出一個 image 的結構
    docker image inspect inamgeID



docker save

  • 將 image 存成映像檔
    docker save -o outputFileName imageName:imageTag
    -o 代表 output



docker logs

  • 查 log
    docker logs -f containerID



Docker push

  1. 首先, tag 本地的 image 一個可以用來推上 DockerHub 的名字

    docker tag localImageName:localTagName userAccount/toBeTaggedImageName:toBeTaggedImageTagName
  2. 推上 DockerHub

    docker push userAccount/taggedImageName:taggedImageTag

Docker-Compose

安裝 docker-compose

  • 安裝 Docker Compose, 儘管我們可以從官方的 ubuntu 倉庫 安裝 Docker Compose ,但因為最新的版本中有很多細小的版本差異,所以我們從 Docker GitHub 來安裝。 先到官方頁面確認版本,並且視需求更新下面指令的版本號。
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
  • 設定權限

    sudo chmod +x /usr/local/bin/docker-compose
  • 確認 Docker-compose 版本

    docker-compose --version
  • 建立 yaml 檔案

    vim docker-compose.yml
  • 輸入內容如下:

    my-test:
    image: hello-world
  • 建立一個 container

    docker-compose up
  • 輸出理應如下:




建立一個 Swarm

  • 開啟 docker swarm 模式
    docker swarm init \
    --advertise-addr 0.0.0.1:8080 \
    --listen-addr 0.0.0.1:8080
    上面的 IPPORT 自己訂的
    --advertise-addr: 指定別的節點要連接這個 manager 時該使用的 IP 以及 port。這不是個必要的選項,但是你可以對哪個 IP 被使用有完全的控制權,並且你也可以訂一個不存在於節點上的 IP ,像是負載平衡的 IP

--listen-addr: 讓你可以指定用來聽 swarm traffic 的 IP 以及 port ,通常他會跟 --advertise-addr 相同,但如果你想要限制 swarm 在特定的 IP,就特別有用。 還有一種情況,當 advertise-addr 是一個遠端的 IP ,像是平衡負載器,那這個也是必要的。

建議總是使用兩個附加選項。

  • 從一個 node 開始 swarm mode ,並且設為 leader

    docker swarm init \
    --advertise-addr yourInternalIP:yourPort \
    --listen-addr yourInternalIP:yourPort
  • 取得新增 manager 的 token

    docker swarm join-token manager

    輸出大概如下:

docker swarm join \
token SWMTKN-1-0uahebax...c87tu8dx2c \
10.0.0.1:2377
  • 取得新增 worker 的 token
    docker swarm join-token token

輸出大概如下:

docker swarm join \
token SWMTKN-1-0uahebax...c87tu8dx2c \
10.0.0.1:2377
  • 重新產生 token

    docker swarm join-token --rotate managerOrWorker
  • 開啟並且登入一台新的 instance , 輸入上面的 token 還有自己的 IP 位址

    docker swarm join \
    token SWMTKN-1-0uahebax...c87tu8dx2c \
    10.0.0.1:2377 \
    advertise-addr yourInternalIP:yourPort \
    listen-addr yourInternalIP:yourPort
  • 不管你是想加入成為 manager 或是 workder ,只要輸入相對應的 token 就可以加入。

  • 在 Leader 的 node 上可以查看 swarm 的詳細資料

    docker swarm node ls
  • 離開當前的 swarm

    docker swarm leave --force



開啟一個 SERVICE

  • 要開立 service 首先必須確定, swarm 已建立。
    docker service create --name web-fe \
    -p 8080:8080 \
    --replicas 5 \
    nigelpoulton/pluralsight-docker-ci
    docker service create: 建立一個 service
    --name: 指定 service 的名稱
    -p: 指定 container 內以及連接外部的 port
    --replicas: 在這個服務內,至少要有 5 個 container
    nigelpoulton/pluralsight-docker-ci: image 以及 tag

開立 service 之後, swarm 會一直的監看真實的狀態是否跟我的理想的狀態一致,如果一致的話那很好,如果不, swarm 會採取相對應的動作。
舉例來說,如果五個 container 裡面有一個關閉了, swarm 會自動在啟動一個

  • 查看 service 清單

    docker service ls
  • 查看 service 內的任務狀態

    docker service ps serviceName

  • 更詳盡的資訊

    docker service inspect --pretty serviceName
  • 擴展規模

    docker service scale web-fe=10

  • 刪除 service

    docker service rm web-fe
  • 建立一個 overlay network

    docker network create -d overlay uber-net

    連接到相同 overlay networkcontainer , 儘管他們的 docker host 連接的網路不同,彼此也可以互相溝通連接。

  • 列出 network 資料

    docker network ls
  • 刪除一個 network

    docker network rm networkName
  • 依據指定的 network 建立一個新的服務

    docker service create --name uber-svc \
    --network uber-net \
    -p 80:80 --replicas 12 \
    nigelpoulton/tu-demo:v1

    --network: 指定 network




滾動升級 rolling update

  • 滾動升級運行中的服務

    docker service update \
    --image nigelpoulton/tu-demo:v2 \
    --update-parallelism 2 \
    --update-delay 20s uber-svc

    docker service update: 升級 service
    --image: 升級的 image 來源
    --update-parallelism 2: 一次升級兩個 container
    --update-delay 20s uber-svc: 每批次的等待時間為 20 秒 , 需等當前批次的升級完成,時間才會開始計算。 最後指定要升級的 service 名稱

  • 查看上次升級的參數

    docker service inspect --pretty serviceName

如上圖,前一次升級的參數都會被保留下來,除非你再次升級去覆蓋它




Composer

  • 利用 container 安裝 Composer
    docker run --rm -v $(pwd):/app composer install
    --rm: 當 container 關閉後,自動刪除
    -v: 使用 volumes
    $(pwd):/app: $(pwd) 表示當前資料夾, /app 表示 container 裡頭一個叫做 app 的資料夾, 所以這個指令代表 : 前後的兩個資料夾會在 container 關閉之前,同步所有資料
    composer install: 安裝 composer

所以這個指令實際上做的事情,就是從 Docker Hub 拉下官方的 composer image ,然後開啟一個 container 並執行安裝, composer 會依照資料夾內 composer.jsoncomposer.lock 來安裝相對應的 package 。 package 會被安裝在 app 這個資料夾內,但因為 volumes 的關係,所以兩個資料夾會同步, $(pwd) 內也會有安裝的 package 。 當安裝結束後, container 關閉,因為 --rm 的作用, container 會自動刪除。




利用 Docker-compose 部署 Laravel

安裝 docker-compose

  • 環境為 Ubuntu 18.04
  • 安裝 Docker Compose, 儘管我們可以從官方的 ubuntu 倉庫 安裝 Docker Compose ,但因為最新的版本中有很多細小的版本差異,所以我們從 Docker GitHub 來安裝。 先到官方頁面確認版本,並且視需求更新下面指令的版本號。
sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
  • 設定權限
    sudo chmod +x /usr/local/bin/docker-compose



利用 Docker 來安裝環境

MySQL

  • 安裝 MySQL , 並指定連接一個外部不同的 port 號 , 像是 52000

    docker run --name serviceName -e MYSQL_ROOT_PASSWORD=yourPassword -d -p 52000:3306 mysql:5.7

    docker run: 啟動一個 container
    --name: 指定這個服務的名稱
    -e MYSQL_ROOT_PASSWORD: 指定環境參數
    -d: 使這個服務跑在 container 當中
    -p: 指定外跟內的 port 號, container 內部的 3306 連接外部的 52000
    mysql:5.7: 指定 image 以及 tag

  • 連接本地安裝的 MySQL 以及 Docker container 內的 MySQL

  1. 本地安裝的

    sudo mysql -uroot
  2. Docker Container

    mysql -h 127.0.0.1 -uroot -p2434 -P52000

製作 Docker image

  • 時區問題
    我的第一個目標,是利用 Docker commit 來做一個專屬的 image。簡單來說,就是起一個純淨的 container ,然後在這個 container 裡頭部署完之後,再使用 docker commit 將這個 container 變成一個 image , 結果發現在安裝 php7.2-imagick 中會出現 Configuring tzdata ,會彈出一個視窗並要求選擇時區,如下圖

後來找到解決方法,是在安裝 php7.2-imagick 之前就把時區設定好,所以在一開始就加入

export DEBIAN_FRONTEND=noninteractive && apt-get install -y tzdata && ln -fs /usr/share/zoneinfo/Asia/Taipei /etc/localtime && dpkg-reconfigure --frontend noninteractive tzdata
  • MySQL 問題
    接下來,又遇到一個問題,在我利用我製作好的 image 開一個 container 時, MySQL 無法成功啟動,會一直出現 Failed
    後來的解決方法非常的奇怪,我是到 /var/www/mysql/mysql 中,下 chown -R mysql:mysql . 的指令,然後它就好了…
    可問題是,我仔細地確認過,在我下這指令之前,這個資料夾下的所有 user 以及 group 早就已經是 mysql:mysql 了

  • Apache 以及 MySQL 無法設定自動重啟的問題 所以我們必須要在 container 裏頭啟動這些服務,需要啟動的服務如下:

    • Apache
    • MySQL

所以從目前的進度看來, container 啟動之後,我們必須要執行三個指令,如下:

chown -R mysql:mysql /var/www/mysql/mysql
service apache restart
service mysql restart

沒想到實際執行之後,我們遇到了一個新的問題…

  • Container 自動退出的問題
    Container 的特性,是只要 command 執行完畢後,就會自動退出。 container 一旦退出了,我們原本建構的環境當然也就不在了。
    我們怎麼可以容許這種事情發生呢?
    因此,我們需要給 container 一個會一直持續存在的指令,好比是 /bin/bash

  • 該怎麼樣讓 container 在啟動的時候又執行多個指令呢?
    在 container 啟動時, 我們需要執行多個指令來啟動 container 裡面的服務,但是 entrypoint 只能帶一個指令進去,所以我們要寫一個 shell script ,然後當 container 開啟之後去執行這個 shell script ,當然,這個 shell script 裡頭的指令就是上述提到的指令,如下:

    chown -R mysql:mysql /var/www/mysql/mysql
    service apache2 restart
    service mysql restart
    /bin/bash

假設這個 shell script 叫做 test.sh ,然後放在 /usr/sbin/ 之下,那我們的 docker 指令如下:

docker run -dt --entrypoint "/usr/sbin/test.sh" -p 8880:80 --name containerName yourAccount/imageName:tagName



ENV




man 指令

Docker 為了縮小 image 大小,預設不產生 man 說明文件。 在安裝 man 以及 man-pages 之前,需要先將此設定改掉

  • 修改設定
    Docker 為了縮小 image 大小,預設不產生 man 說明文件。 在安裝 man 以及 man-pages 之前,需要先將此設定改掉
    vi /etc/yum.conf

將以下這行 comment 掉

tsflags=nodocs
  • 安裝

    yum install man-pages -y; yum install man -y
  • 更新 mandb

    mandb
  • 安裝 vim 套件測試

    yum install vim
  • 測試

    man vim

可以用了




Q & A

  • 當執行 Docker build 時, 如果不想要 context 中的某些資料夾或檔案被上傳到 docker daemon 去, 可以怎麼做? 建立一個 dockerignore 檔案, 並將不想上傳的檔案或資料夾加進去

  • 如果不想使用預設的 dockerfile, 可以怎麼做? docker -t -f dockerFileNameYouWant .

  • 在 Docker CLI, 哪一個 flag 可以指定環境變數? -e

  • 在 Docker CLI, 哪一個 MYSQL 的 flag 可以允許空的密碼? -e MYSQL_ALLOW_EMPTY_PASSWORD=yes

  • 在 Docker CLI, 哪一個 flag 可以指定 volume? -v

  • 如何列出 volume 清單? docker volume ls

  • 如何列出指定 volume 訊息? docker volume inspect volumeName

  • Docker 中, 預設的網路模式是? Bridge

  • 可透過何種方式證明 Docker 預設容器可互聯互通? 在容器內取得 ip, 並使用 ping

  • 當使用 –link 來連接到一個容器時, 實際上容器做了什麼事, 讓我們可以直接使用名字連接? Docker 在 /etc/hosts 裡頭將 IP 以及該容器名稱做連結

  • Docker 當中, --link 是單向還是雙向的? 單向

  • Docker 當中, --network 是單向還是雙向的? 雙向

  • docker –network host 只在哪一個 OS 上有用 Linux

  • docker –network container 的效果? 與指定的 container 共用網路

  • docker –network host 的效果? 與當前宿主機同網路, 換言之, 不需 port binding

  • docker –network none 的效果? 不為容器配置任何網路

  • 以下的 Docker command 會輸出什麼? Hello, Cloud Man 。 環境變量 name 已經被值 Cloud Man 替換掉了

    ENV name Cloud Man
    ENTRYPOINT echo "Hello, $name"
  • 以下的 Docker command 會輸出什麼? Hello, $name 。 環境變量沒有被替換

    ENV name Cloud Man  
    ENTRYPOINT ["/bin/echo", "Hello, $name"]
  • 以下的 Docker command 中, 如果要使輸出使用環境變量, 該怎麼修改?/bin/echoHello, $name 之間新增 command -c

    ENV name Cloud Man  
    ENTRYPOINT ["/bin/echo", "Hello, $name"]
  • Docker 中, apt-get update 和 apt-get install 建議放在同一個 RUN 指令中執行, 為什麼? Docker 會使用 Cache 如果指令沒有改變的話。 要是將這兩個指令分開了, 當執行到 apt-get update 時, 由於指令沒有變更, 所以 docker 使用緩存, 但是 apt-get install 這層的指令變了, 可能會變成使用很久之前的緩存來安裝新的套件的情況, 這樣就找不到套件了。

  • Docker 中, CMD 允許被使用者替換嗎? 允許

  • Docker 中, 如果 CMD 已有設定, 但 docker run 之後沒有指令命令, 那 container 會執行哪一個命令? CMD

  • 如果 Dockerfile 中有多個 CMD, 哪一個為準? 只有最後一個 CMD 有效

  • CMD 有幾種格式? 三種

  • CMD 有哪三種格式? exec, shell, CMD [“param1”, “param2”]

  • 在 Docker image 中, CMD 設定為 [“/bin/echo”, “Hello World”], 執行 docker run -it [image] /bin/bash 時, 會怎麼樣? 會只執行 /bin/bash

  • Docker 中, ENTRYPOINT 有幾種格式? 兩種?

  • Docker 中, ENTRYPOINT 有哪兩種格式? exec, shell

  • Docker 中, ENTRYPOINT 的參數可以被動態替換掉嗎? 不行

  • 在 Docker image 中, ENTRYPOINT 的設定為 [“/bin/echo”, “Hello”], CMD 為[“world”], 當我執行容器 docker run -it [image], 輸出為? Hello world

  • 在 Docker image 中, ENTRYPOINT 的設定為 [“/bin/echo”, “Hello”], CMD 為[“world”], 當我執行容器 docker run -it [image] Ray, 輸出為? Hello Ray

  • 在 Docker image 中, ENTRYPOINT 為 shell 格式時, CMD 跟 docker run 的參數會? 被無視

  • 在 Dockerfile 中, 哪三個指令會增加新的層數? RUN, ADD, COPY

  • 在多階段構建中, 如何從指定層數 COPY 東西過來當層 藉由 AS 給該層一個名字, 然後使用 COPY --from NAME

  • 在 Docker 中, 如何使用 ENV 來更新 PATH 環境變量? 如下

    ENV PATH /new/directory:$PATH
  • 在 Docker 中, 當 COPY 的內容變了, 緩存是否會失效? 會哦




使用 docker-compose 部署 Laravel-LEMP

下載專案並安裝依賴

  • 下載專案

    git clone https://github.com/laravel/laravel.git laravel-app
  • 進到資料夾

    cd laravel-app
  • 安裝依賴套件

    • -v 同步當層目錄與容器內的 /app
    • 執行命令 composer install
    • --rm 當容器關閉時, 自動刪除容器
    • 容器建立時, 外頭的 laravel-app 會與容器內的 /app 同步, 這時候在容器內部的 /app 執行 composer install, 安裝在容器內部 /app 資料夾內的 composer 套件會即時的同步到外頭的 laravel-app 資料夾內。 執行完畢後, 容器沒有其餘的任務, 因此容器會關閉, --rm 會自動刪除容器
    • 更多 docker composer 的應用可參考官方文件
docker run --rm -v $(pwd):/app composer install
  • 更改權限
    sudo chown -R $USER:$USER ~/laravel-app



建立 Docker Compose 檔案

  • 建立並打開 docker-compose 設定檔

    vim ~/laravel-app/docker-compose.yml
  • docker-compose 檔案中, 你將會自定義三個服務: app, webserver, 以及 db 。 在檔案中輸入以下的代碼, 別忘了將 MY_SQL_ROOT_PASSWORD 替換成你自己的密碼

# 版本
version: '3'
services:

#PHP Service
app:
# 這裡建構客製鏡像, Dockerfile 之後會提到
build:
# 上下文目錄為當層
context: .
# Dockerfile 為當層的 dockerfile
dockerfile: Dockerfile
# 客製化鏡像的名稱
image: digitalocean.com/php
# 容器名稱, 若不指定也可, 但 docker 會用歷史名人命名
container_name: app
# 若停止自動重啟
restart: unless-stopped
# 為避免容器開啟後即關閉, 設定 tty: true
tty: true
# 環境變數
environment:
SERVICE_NAME: app
SERVICE_TAGS: dev
# 當前工作目錄
working_dir: /var/www
# 持久化數據
volumes:
# 將當層目錄與容器內的 /var/www 同步
- ./:/var/www
# 將當層目錄下的 php/local.ini 與容器內的 /usr/local/etc/php/conf.d/local.ini 同步
- ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
# 使用的網路為 app-network
networks:
- app-network

#Nginx Service
webserver:
# 使用官方 nginx:alpine 鏡像
image: nginx:alpine
# 容器名稱, 若不指定也可, 但 docker 會用歷史名人命名
container_name: webserver
# 若停止自動重啟
restart: unless-stopped
# 為避免容器開啟後即關閉, 設定 tty: true
tty: true
# 將容器內的 80, 443 port 跟宿主機的 80, 443 port 對接
ports:
- "80:80"
- "443:443"
# 持久化數據
volumes:
# 將當層目錄與容器內的 /var/www 同步
- ./:/var/www
# 將當層目錄下的 nginx/conf.d/ 與容器內的 /etc/nginx/conf.d/ 目錄同步
- ./nginx/conf.d/:/etc/nginx/conf.d/
# 使用的網路為 app-network
networks:
- app-network

#MySQL Service
db:
# 使用官方 mysql:5.7.22 鏡像
image: mysql:5.7.22
# 容器名稱, 若不指定也可, 但 docker 會用歷史名人命名
container_name: db
# 若停止自動重啟
restart: unless-stopped
# 為避免容器開啟後即關閉, 設定 tty: true
tty: true
# 將容器內的 3306 port 跟宿主機的 3306 port 對接
ports:
- "3306:3306"
# 設定環境變數
environment:
# 資料庫名稱, 可自定義
MYSQL_DATABASE: laravel
# 資料庫密碼, 可自定義
MYSQL_ROOT_PASSWORD: your_mysql_root_password
SERVICE_TAGS: dev
SERVICE_NAME: mysql
# 持久化數據
volumes:
# 將自定義的數據卷 dbdata 跟容器內的 /var/lib/mysql/ 目錄做同步
- dbdata:/var/lib/mysql/
# 將當層目錄下的 mysql/my.cnf 跟容器內的 /etc/mysql/my.cnf 做同步
- ./mysql/my.cnf:/etc/mysql/my.cnf
# 使用的網路為 app-network
networks:
- app-network

#Docker Networks
networks:
# 定義 network 名稱
app-network:
# 定義 network driver 為 bridge, 還有其他類型如 host, none, container 等等...
driver: bridge
#Volumes
volumes:
# 定義數據卷名稱
dbdata:
# 定義數據卷類型
driver: local
  • app:
    • 運行 Laravel
    • build 客制鏡像 digitalocean.com/php, 下面會有建立的 dockerfile
    • 使用客製的鏡像 digitalocean.com/php,
    • 將當前工作目錄設在了 /var/www
  • webserver:
    • 使用官方 nginx:aline 鏡像
    • 將容器內的 80 以及 443 port 接到宿主機的 80 以及 443 port
  • db:
    • 使用官方 mysql:5.7.22 鏡像
    • 定義一些環境變數, 包含資料庫名稱, 資料庫密碼, 這部分可以依個人需求更改
    • 將容器內的 3306 接到宿主機的 3306 port
  • 我們把容器名稱跟服務名稱定義成一樣, 如果我們不定義容器名稱, Docker 會自動使用歷史上有名的人物的名稱來命名
  • 我們為不同容器群組定義了專屬的 network, 位於相同 network 下的容器可以互相溝通, 反之則不行, 這確保了高等級的安全性, 不同類型的容器群不可以互相溝通, 像是前端只能跟前端溝通, 後端跟後端



建立 Dockerfile

# 使用官方 php:7.3-fpm
FROM php:7.3-fpm

# 複製 composer.lock 以及 composer.json 從上下文資料夾複製到鏡像內的 /var/www/
COPY composer.lock composer.json /var/www/

# 設定工作目錄為 /var/www
WORKDIR /var/www

# 安裝依賴
# mariadb-client

RUN apt-get update && apt-get install -y \
build-essential \
mariadb-client \
mysql-client \
libpng-dev \
libjpeg62-turbo-dev \
libfreetype6-dev \
locales \
zip \
jpegoptim optipng pngquant gifsicle \
vim \
unzip \
git \
curl

# 清除 /var/cache/apt/archives 下的所有 deb 檔, 以及清除 /var/lib/apt/lists/ 下的所有安裝檔
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# 安裝 extensions
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Install composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# 建立 laravel 應用 user
# 建立 group www 並指定 GID 為 1001
RUN groupadd -g 1001 www
# 建立 user www 並指定 UID 為 1001, 將之加入群組 www
RUN useradd -u 1001 -ms /bin/bash -g www www

# 複製檔案以及重新定義權限
COPY --chown=www:www . /var/www

# 將使用者切換到 www
USER www

# 暴露 9000 port
EXPOSE 9000

# 執行 php-fpm
CMD ["php-fpm"]



PHP 設定檔

現在我們需要建一個 PHP 設定檔來同步宿主機以及容器內

  • 建立資料夾

    mkdir ~/laravel-app/php
  • 設定檔

    vim ~/laravel-app/php/local.ini
  • 設定檔內容範例, 你可以添加其他設定

    # 單檔的最大上傳限制
    upload_max_filesize=40M

    # 整個 post 的最大限制
    post_max_size=40M



Nginx 設定檔

  • 建立資料夾

    mkdir -p ~/laravel-app/nginx/conf.d
  • 建立設定檔

    vim ~/laravel-app/nginx/conf.d/app.conf
  • 範例設定檔內容, 可視需求添加

    server {
    # 監聽 80 port
    listen 80;

    # 尋找 index.php index.html
    index index.php index.html;

    # error log 位置
    error_log /var/log/nginx/error.log;

    # access log 位置
    access_log /var/log/nginx/access.log;

    # 資源的默認位置
    root /var/www/public;

    # 如果請求是 php 結尾的話, 進到 location
    location ~ \.php$ {
    # 尋找 uri, 若沒找到則 404
    try_files $uri =404;
    #
    fastcgi_split_path_info ^(.+\.php)(/.+)$;
    fastcgi_pass app:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    }
    location / {
    try_files $uri $uri/ /index.php?$query_string;
    gzip_static on;
    }
    }



設定 MySQL

  • 建立資料夾

    mkdir ~/laravel-app/mysql
  • 建立設定檔

    vim ~/laravel-app/mysql/my.cnf
  • 輸入設定檔內容, 可自行添加

    [mysqld]
    # 產生 log
    general_log = 1

    # log 位址
    general_log_file = /var/lib/mysql/general.log



運行容器以及修改 env 設定

  • 建立 .env

    cp .env.example .env
  • 運行容器

    docker-compose up -d
  • 查看容器

    docker ps
  • 修改 .env 檔

    docker-compose exec app vim .env
DB_CONNECTION=mysql
# DB 容器服務
DB_HOST=db
DB_PORT=3306
# 上面設定的 databaseName
DB_DATABASE=laravel
# 上面設定的 userName
DB_USERNAME=laraveluser
# 上面設定的 password
DB_PASSWORD=your_laravel_db_password
  • 產生 key

    docker-compose exec app php artisan key:generate
  • 開啟 cache

Node.js 學習筆記 Kubernetes Engine - 入門

留言

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×