利用 Dockfile、Docker Compose 建立 LAMP 環境 (PHP、Apache、MySQL) === 範例原始碼放在 GitHub Repo:[titangene/docker-lamp](https://github.com/titangene/docker-lamp) ## 常用 Docker 指令 ![](https://i.imgur.com/5xciVnC.png) 圖片來源:[Gitbook - 《Docker —— 從入門到實踐­》正體中文版 by Philipzheng](https://philipzheng.gitbooks.io/docker_practice/content/appendix_command/) ## 使用基本 Docker 指令建立 PHP + Apache 環境 ### 建立並執行 container 接著使用 `docker run` 指令來建立並執行 container,下面是參數說明: - `--name my-php-apache`:設定 container 名稱為 `my-php-apache` - `-d`:container 在背景執行 - `-p 8000:80`:指定一個 port,host 對外開 8000 port,container 對內開 80 port - `php:7.1-apache`:使用 PHP 官方在 Docker Hub 上提供的 [7.1-apache](https://hub.docker.com/_/php/) tag 的 image ```shell $ docker run --name my-php-apache -d -p 8000:80 php:7.1-apache 08e5975c8a279087db637782181f458cad2a10d5086454b418301de387c1ceb8 ``` ### 確定 container 是否成功執行 使用 `docker ps` 指令來確定 container 是否成功執行 (查看正在執行的 container 狀態) ```shell $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 08e5975c8a27 php:7.1-apache "docker-php-entryp..." 11 seconds ago Up 10 seconds 0.0.0.0:8000->80/tcp my-php-apache ``` ### 查詢主機的 IP 位址 接著是查詢主機的 IP 位址,如果是 Linux 或 masOS 可使用 `ifconfig` 指令,如果是 Windows 則是使用 `ipconfig` 指令 ```shell $ ifconfig ... ens33 Link encap:Ethernet HWaddr 00:0c:29:89:a0:57 inet addr:192.168.191.130 Bcast:192.168.191.255 Mask:255.255.255.0 inet6 addr: fe38::ec00:21cf:ce7f:1e8d/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:131354 errors:0 dropped:0 overruns:0 frame:0 TX packets:19549 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:190319852 (190.3 MB) TX bytes:1769853 (1.7 MB) ... ``` ### 查看服務頁面顯示 403 錯誤 開啟瀏覽器並輸入網址 `[ip-address]:8000` (Port 是剛剛建立 container 時設定的),會發現出現 403 錯誤,原因是因為我們還沒在 Apache web server 的根目錄 `/var/www/html` 下放首頁 `index.php` 或 `index.html`: ![](https://i.imgur.com/SsdyPXI.png) ### 進入 container 新增 `index.html` 所以接著要使用 `docker exec` 指令進入 container 來新增 `index.php` (在外部向執行中的 container 內部下指令,此時會呼叫 Container 內部的 shell 程式來執行你下的指令) 成功進入 container 後可以看到預設工作目錄就是 `/var/www/html`,這是因為官方在 image 上已經設定好了 (在 `Dockerfile` 中設定的 `WORKDIR /var/www/html`,詳情請參考官方提供的 [`Dockerfile`](https://github.com/docker-library/php/blob/master/7.1/jessie/apache/Dockerfile) ) ```shell $ docker exec -it my-php-apache bash root@08e5975c8a27:/var/www/html# ``` 接著就在 Apache web server 的根目錄 `/var/www/html` 下新增 `index.php` ```shell $ echo "<?php phpinfo(); ?>" > index.php $ cat index.php <?php phpinfo(); ?> ``` 接著輸入 `exit` 指令離開 container ```shell $ exit ``` ### 刪除 container 如果要刪除 container 可以使用以下指令: ```shell $ docker stop my-php-apache $ docker rm my-php-apache ``` 或是直接使用 `docker rm -f` 強制刪除正在執行的 container ```shell $ docker rm -f my-php-apache ``` ### 重新查看服務頁面是否正確顯示 然後開啟瀏覽器並輸入網址 `[ip-address]:8000` 就可以看到 `phpinfo()` 的畫面 ![](https://i.imgur.com/FKvfgib.png) ## 建立自己的 Docker Images,並 push 至 Docker Hub ### Commit images | 簡寫,名稱 | 預設 | 說明 | | ----------------- | ----- | ---------------- | | `-a`,`--author` | false | 作者 | | `-m`,`--message` | false | 提交訊息 | | `-p`,`--pause` | true | 提交期間暫停執行容器 | ```shell $ docker commit -a "titangene <titangene.tw@gmail.com>" -m "Add index.php" my-php-apache titangene/php-apache:v1.0 ``` ### 登入 Docker Hub ```shell $ 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 (titangene): Password: Login Succeeded ``` ### push image 至 Docker Hub ```shell $ docker push [image] $ docker push titangene/php-apache:v1.0 The push refers to a repository [docker.io/titangene/php-apache] b0a5a0170d8f: Pushed d7ca93da4280: Pushed 3be05f838c62: Pushed ... v1.0: digest: sha256:ea3454... size: 3242 ``` ### 使用之前儲存的 image ```shell $ docker run --name my-php-apache -d -p 8000:80 titangene/php-apache:v1.0 ``` ## 使用 Dockerfile 建立 PHP + Apache 環境 > 詳情請參考我另外整理的 [Dockerfile 學習筆記 by Titangene](/PnuGE_LtQhSArxhx8Ga3FA) 筆記。 ### 什麼是 Dockerfile? `Dockerfile` 是一個文字檔,其中包含了使用者可以在指令列 (command line) 上使用的所有指令,Docker 透過 `docker build` 指令執行 `Dockerfile` 中的所有指令來自動建置新的 image。 ### Dockerfile 能做什麼? - 可將環境進行版本控制 - 自動建置環境,減少重複步驟 - 自訂建立 image 時要做什麼 - 自訂建立 container 時要做什麼 ### 建立目錄 ```shell project ├── Dockerfile └── src └── index.php ``` ```php // src/index.php <?php echo '<h1>Hello World</h1>'; phpinfo(); ?> ``` ### Dockerfile - `FROM php:7.1-apache`:第一條命令必須為 `FROM`,說明使用哪個 image 作為基底 (Base Image) - `COPY src /var/www/html/`:將 Dockerfile 所在目錄的 `src` 資料夾內的資料複製到 container 內的 `/var/www/html/` 下 - `EXPOSE 80`:開放 80 port ```dockerfile FROM php:7.1-apache COPY src /var/www/html/ EXPOSE 80 ``` ### 利用 Dockerfile 建立 Docker Image - `-t`:添加標記 (tag) - 指定此 image 的名稱 (name) 和標記 (tag) - 格式:`docker build -t name:tag .` - tag 可用來區分同一個 repository 的不同 image (通常作為版本號) - EX:Ubuntu repo 中有多個 image,通過 Tag 來區分發行版本,例如 12.04、13.04、14.04 ... 等 - `.` (一點):指定 Dockerfile 所在的路徑 - 這邊的 `.` 點指的是當前目錄,也可以換成 Dockerfile 的絕對路徑 ```shell $ cd project $ ls Dockerfile src/ $ docker build -t my-php . Sending build context to Docker daemon 3.584kB Step 1/3 : FROM php:7.1-apache ---> b7ce92f2bd78 Step 2/3 : COPY src /var/www/html/ ---> 3b6ac8f06072 Step 3/3 : EXPOSE 80 ---> Running in 0f36c2370aff Removing intermediate container 0f36c2370aff ---> 365eace4a8d4 Successfully built 365eace4a8d4 Successfully tagged my-php:latest ``` ### 查看目前 image 查看 local 有哪些 image,確定是否有名為 `my-php` 的 Docker Image ```shell $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE my-php latest 365eace4a8d4 18 seconds ago 392MB php 7.1-apache b7ce92f2bd78 2 days ago 392MB ``` ### 使用自建的 image 來新建並啟動 container ```shell $ docker run --name my-php-apache -d -p 8000:80 my-php ``` ### 確定 container 是否成功執行 使用 `docker ps` 指令來確定 container 是否成功執行 (查看正在執行的 container 狀態) ```shell $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES dc194e799ca7 my-php "docker-php-entrypoi…" 49 seconds ago Up 46 seconds 0.0.0.0:8000->80/tcp quirky_booth ``` ## Docker Volume 在上一個範例,為了要把 `index.php` 首頁檔放入 Apache web server 的根目錄 `/var/www/html` 下,要先利用 `docker exec` 指令進去 container 裡面操作,但是這樣非常麻煩,有沒有方便的方法? 那就要提到 Docker Volume 的概念:掛載主機上的指定目錄到 container 的指定目錄上 (將主機上的目前目錄 bind 到 container 的指定目錄)。 - ``-v `pwd`:/var/www/html``:將 Host 端的目前目錄 ( `pwd` ) 掛載到 container 的 `/var/www/html` 目錄 ```shell $ cd project/src $ docker run -d -p 8000:80 -v `pwd`:/var/www/html php:7.1-apache ``` ### 修改 `index.php` ```shell $ vi index.html ``` ```php <?php echo '<h1>Hello World</h1>'; phpinfo(); ?> ``` 修改後可以看到 `phpinfo` 前面已出現剛剛加入的 `Hello World` 字串: ![](https://i.imgur.com/R8PRSZR.png) ## 利用 Docker Compose 建立 LAMP ![](https://i.imgur.com/Li3tbU7.png) > 詳情請參考我另外整理的 [Docker Compose 學習筆記 by Titangene](https://hackmd.io/c/H1WITKNUz/%2FBkPoXLJ4M) 筆記。 ### 什麼是 Docker Compose? - 快速建立多個 container 的工具 - 利用 `docker-compose.yml` YAML 檔來管理多個 Docker container - 並使用 `docker-compose` 指令來啟動、停止和重啟應用,以及應用中的服務和所有依賴服務的 container ### 使用的 Image - [PHP 7.1 on Apache (httpd)](https://hub.docker.com/r/_/php/) - [MySQL 5.7](https://hub.docker.com/_/mysql/) - [phpMyAdmin](https://hub.docker.com/r/phpmyadmin/phpmyadmin/) ### 建立目錄 ```shell project ├── docker-compose.yml ├── mysql │ ├── Dockerfile │ └── sql │ └── testdb.sql ├── php │ └── Dockerfile └── www ├── db.php └── index.php ``` ### docker-compose.yml - `build`:指定要使用的 Dockerfile 所在目錄,Docker Compose 會自動幫你執行 `docker build` 指令的動作 - `image`:直接使用某 Docker image 來建立該 container - `ports: [hostPort]:[ContainerPort]`:設定 port mapping,指定一個 port,host 對外開 `[hostPort]` port,container 對內開 `[ContainerPort]` port - `depends_on`:依據依賴順序啟動服務 - `volumes: [hostPath]:[containerPath]`:掛載主機上的指定 `[hostPath]` 目錄到 container 的指定目錄 `[containerPath]` 上 - `environment`:設定環境變數 ```yaml version: '3.3' services: phpapache: #image: titangene/php-apache-mysql:v1.0 build: ./php ports: - "80:80" - "443:443" depends_on: - mysql volumes: - ./www:/var/www/html mysql: build: ./mysql ports: - "3306:3306" volumes: - ./mysql/data:/var/lib/mysql environment: MYSQL_ROOT_PASSWORD: admin #MYSQL_DATABASE: testdb phpmyadmin: image: phpmyadmin/phpmyadmin ports: - "8080:80" depends_on: - mysql environment: PMA_HOST: mysql PMA_PORT: 3306 ``` ### 利用 Docker Compose 啟動服務 - `-d`:背景執行 ```shell $ docker-compose up -d Creating dockerlamp_mysql_1 ... done Creating dockerlamp_phpmyadmin_1 ... done Creating dockerlamp_phpapache_1 ... done ``` 用瀏覽器開啟 `localhost` 就會看到 `index.php` 的頁面: ![](https://i.imgur.com/52OTLsp.png) 接著開啟 `localhost:8080` 登入 phpMyAdmin: ![](https://i.imgur.com/ga2rj5p.png) 接著開啟 `localhost/db.php` 可看到 PHP 能正確讀取到 MySQL 的資料: ![](https://i.imgur.com/gMqZz3f.png =300x) ### 停止執行、刪除服務 ```shell $ docker-compose stop Stopping dockerlamp_phpmyadmin_1 ... done Stopping dockerlamp_phpapache_1 ... done Stopping dockerlamp_mysql_1 ... done $ docker-compose rm Going to remove dockerlamp_phpmyadmin_1, dockerlamp_phpapache_1, dockerlamp_mysql_1 Are you sure? [yN] y Removing dockerlamp_phpmyadmin_1 ... done Removing dockerlamp_phpapache_1 ... done Removing dockerlamp_mysql_1 ... done ``` - `-s`:會先讓 container 停止執行,然後再刪除 container - `-f`:會自動確認刪除 container (原本會問 Y or N) ```shell $ docker-compose rm -sf Stopping dockerlamp_phpmyadmin_1 ... done Stopping dockerlamp_phpapache_1 ... done Stopping dockerlamp_mysql_1 ... done Going to remove dockerlamp_phpmyadmin_1, dockerlamp_phpapache_1, dockerlamp_mysql_1 Removing dockerlamp_phpmyadmin_1 ... done Removing dockerlamp_phpapache_1 ... done Removing dockerlamp_mysql_1 ... done ``` ### 停止容器並移除由 `up` 建立的容器、網絡、volume、image `docker-compose down` 指令預設只刪除以下內容: - 在 Compose 檔案中,定義的服務容器 - 在 Compose 檔案中,定義在 `networks` 欄位的網絡 - 預設網絡 (如果有使用的話) 在 Compose 檔案中,定義在 `external` 欄位的網絡和 volume 不會被刪除 - `--rmi type`:刪除 image,type 必須從下面選一個: - `all`:刪除任何服務使用的所有 image - `local`:只刪除沒有在 `image` 欄位設定的自定標籤 image - `-v`,`--volumes`:刪除 Compose 檔案 `volumes` 欄位上有命名的 volume 和 attach 到 container 的匿名 volume - `--remove-orphans`:刪除未在 Compose 檔案中定義的服務容器 - `-t`,`--timeout TIMEOUT`:指定 shutdown timeout,以秒為單位,預設為 10 秒 ```shell $ docker-compose down --rmi local Stopping dockerlamp_phpmyadmin_phpapache_1 ... done Stopping dockerlamp_phpmyadmin_phpmyadmin_1 ... done Stopping dockerlamp_phpmyadmin_mysql_1 ... done Removing dockerlamp_phpmyadmin_phpapache_1 ... done Removing dockerlamp_phpmyadmin_phpmyadmin_1 ... done Removing dockerlamp_phpmyadmin_mysql_1 ... done Removing network dockerlamp_phpmyadmin_default Removing image dockerlamp_phpmyadmin_mysql ``` ### 透過 Docker Compose 設定 network > 詳情請參考我另外整理的 [透過 Docker Compose 設定 network by Titangene](https://titangene.github.io/article/networking-in-docker-compose.html) 筆記。