Dockerfile 實戰

未使用過請先看 Docker 簡介指令速查 & 基本操作

在 Docker hub 可看到幾種 Repository,official(官方)、public(公開)、automated build(自動建立)。

可以指定任何一種當底,在做 layer 堆疊出個人的版本。layer 上限 127 層(v0.7.2)

堆疊個人的 image 有兩種方式,一個是進去 container 中手動安裝,另一種就是 寫 Dockerfile。

本篇不使用 docker-compose 串連獨立 service,而是將所有 service 安裝在同一個容器中(Fat container)。

Dockerfile 結構大致上就是基於某個版本,設定環境參數、使用指定身份、下指令、串資料源、開放PORT、執行容器時對應的動作

開發流程使用 git做版控,並讓 docker hub 連結,有異動時會觸發 auto build

練習流程:
  1. ubuntu + nginx
    1. install
    2. service auto start
    3. copy config
    4. volume
    5. volume container
  2. push image
  3. push dockerfile
  4. + php7
  5. + mysql 5.7
  6. + nodejs7


基本指令
  • FROM 底為某個 repository(官方、公開皆可)
    • FROM ubuntu
    • FROM ubuntu:14.04
    • FROM someone/lemp:1.0
  • MAINTAINER 維護者
  • ENV 環境變數
    • ENV NAME=abc
  • RUN 下指令,並增加 layer 一層
    • RUN <command> 如:RUN apt-get install xxx
    • RUN ["executable", "param1", "param2"] 如:
  • EXPOSE 要開放的連接阜
    • EXPOSE 80 443 8080
  • USER 切換使用者(預設為 root)
    • USER root
  • ONBUILD 
  • ADD
  • COPY 複製檔案到容器中
    • COPY /path/src /etc/nginx
    • 要異動,必須重 build
  • WORKDIR 預設工作路徑
  • ENTRYPOINT 執行指令
  • CMD 執行指令
    • CMD 
  • ARG
  • LABEL
  • VOLUME 建立掛載點,允許外部關聯
    • VOLUME ["/etc/nginx", "/etc/php",...]
    • VOLUME /var/log /var/db
  • STOPSIGNAL

Ubuntu + Nginx

  • 建一個測試資料夾、script file
$ mkdir lemp
$ cd lemp
$ vi Dockerfile
  • 純安裝的 script 內容
FROM ubuntu:14.04
MAINTAINER wild0522 <[email protected]>
USER root
#解決 locale 指令,LANG,LANGUAGE,LC_ALL 為空的狀況
RUN locale-gen en_US.UTF-8
ENV LANG=en_US.UTF-8
ENV LANGUAGE=en_US.UTF-8
ENV LC_ALL=en_US.UTF-8
ENV LC_CTYPE=UTF-8
#解決 TERM is not set 況
ENV TERM xterm
RUN apt-get update
#解決 policy-rc.d denied execution of start 狀況
RUN sed -i "s/^exit 101$/exit 0/" /usr/sbin/policy-rc.d
#使用非互動模式,都用預設值跑過,且全部選 y
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes nginx
EXPOSE 80 
在 mongoDB 安裝時,policy-rc.d 這行會導致失敗,問題待解,可先註解掉
  • build、run,檢查 nginx 狀態
$ :wq! #存檔並離開
$ docker build -t="你的暱稱/專案名稱:1.0" .
$ docker images #查看是否有建立成功
$ docker run -tid -p 80:80 --name="lemp_t" 你的暱稱/專案名稱:1.0 /bin/bash
$ docker ps -a #查看 lemp_t 容器是否在執行中
$ docker attach lemp_t #進入 container
root$ service nginx status #顯示 nginx 狀態,此時應該為 not running
root$ exit
$ docker ps -a #lemp_t 容器應該已經停止
$ docker rm lemp_t #刪除容器
  • 讓 nginx 能在 run container 後啟動
    • (x) RUN update-rc.d nginx defaults
    • (x) RUN service nginx restart
    • (x) CMD service nginx restart
    • (o) ENTRYPOINT service nginx start && /bin/bash
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes nginx
ENTRYPOINT service nginx start && /bin/bash
EXPOSE 80 
$ :wq! #存檔並離開
$ docker build -t="你的暱稱/專案名稱:1.1"
$ docker run -tid -p 80:80 --name="lemp_t" 你的暱稱/專案名稱:1.1 /bin/bash
$ docker attach lemp_t #進入 container
root$ service nginx status #顯示 nginx 狀態,此時應該為 running
root$ ls /etc/nginx/sites-enabled/ #應該只有 default 一個檔案
root$ ctrl + p, 放開後接著 ctrl + q #detach容器
$ docker ps -a #此時 lemp_t 容器仍舊執行中
$ docker stop lemp_t; docker rm lemp_t 
  • 用 COPY 把外部檔案送進去
    • 這種方式,只有在 run 的當下才會複製檔案,若外部檔案異動,就須 stop/rm 並重新 run 一次
$ mkdir sites
$ cd sites
$ vi my_web.conf
$ wq!
$ cd ..
$ vi Dockerfile
ENV TERM xterm
COPY sites/*.conf /etc/nginx/sites-enabled/
RUN apt-get update

$ wq!
$ #build 1.2版,並執行
$ docker attach lemp_t
root$ ls /etc/nginx/sites-enabled/ #變成 2 個檔案
root$ exit
  • 用 volume 關聯內外資料夾
    • 容器內的資料夾 mount 外面的 folder,可動態變更資料
$ vi Dockerfile

#COPY sites/*.conf /etc/nginx/sites-enabled/  #刪除 COPY
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes nginx
VOLUME ["/etc/nginx/sites-enabled"]
ENTRYPOINT service nginx start && /bin/bash
$ wq!
$ #build 1.3版
$ docker run -tid -p 80:80 --name="lemp_t" -v /本地端完整路徑/sites:
/etc/nginx/sites-enabled 你的暱稱/專案名稱:1.3 /bin/bash
$ docker attach lemp_t
root$ ls /etc/nginx/sites-enabled/ #變成只有 my_web.conf
root$ ctrl p, q
$ vi sites/my_sec_web.conf
$ wq!
$ docker attach lemp_t
root$ ls /etc/nginx/sites-enabled/ #此時變成 2 個檔案
root$ exit
  • 改用 volume container 

push image

$ docker login #username不是email
$ docker push 暱稱/專案名稱:1.3
  • push 結果
    • docker hub -> dashboard 應該會多出一個 public Repository

push Dockerfile

  • 此方式在 docker Hub 上才 build image,傳輸超快
  • 讓 Automated build 服務,取得 github 授權
    • docker hub 網站 -> create Automated Build -> Public and Private -> login github -> authorize
  • 建立 git repo
    • 註冊/登入 github
    • New repository 專案命名為 hello -> create_repository
    • 複製 repository 網址,ex. https://github.com/帳號/hello.git
    • 回到 Dockerfile 資料夾
$ git init
$ git remote add origin https://github.com/帳號/hello.git
$ git config --global user.email "信箱網址" #第一次用 git 才需設定
$ git config --global user.name "暱稱" #第一次用 git 才需設定
$ git add Dockerfile
$ git commit -m "first commit"
$ git push -u origin master
  • 建立 docker repo
    • docker hub 網站 -> create Automated Build -> github -> 選擇專案名稱 -> create
  • 觸發 build
    • 未 build 之前,Dockerfile 頁面內容會是空的 or 上個版本
    • 當 git push remote 之後,Build Detail 可看 build 過程,點進去可看 Build 過程
    • 手動觸發:Dashboard -> hello -> Build Settings -> Branch Master 後方 Trigger 按鈕
  • 修改一版 Dockerfile
    • 推上去後,就會自動 build
$ git add Dockerfile
$ git commit -m "sec commit"
$ git push

+PHP7

$ vi Dockerfile

RUN DEBIAN_FRONTEND=noninteractive apt-get ... nginx
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes software-properties-common && LC_ALL=C.UTF-8 DEBIAN_FRONTEND=
noninteractive add-apt-repository ppa:ondrej/php &&  
DEBIAN_FRONTEND=noninteractive apt-get update &&  
DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes php7.0 php7.0-fpm php7.0-mysql php7.0-curl php7.0-gd php7.0-json php7.0-mcrypt php7.0-opcache php7.0-xml php7.0-mbstring
VOLUME ["/etc/nginx/sites-enabled","/var/www","/var/log"]
ENTRYPOINT service nginx start && service php7.0-fpm start && /bin/bash
     
$ wq!
$ #build 1.4版
$ docker run -tid -p 80:80 --name="lemp_t" -v /本地端完整路徑/sites:/etc/nginx/sites-enabled -v /本地端完整路徑/www:/var/www -v /本地端完整路徑/log:/var/log  你的暱稱/專案名稱:1.4 /bin/bash
$ ls #應該可以看到多了 www、log 兩個資料夾
$ docker attach lamp_t
$ service nginx status #nginx 掛了?
$ nginx -t #原因是 log 建立失敗
  • log

+ mysql 5.7

$ vi Dockerfile

RUN ... install php
RUN LC_ALL=C.UTF-8 DEBIAN_FRONTEND=noninteractive add-apt-repository -y ppa:ondrej/mysql-5.7 && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes mysql-server;
ENTRYPOINT service nginx start && service php7.0-fpm start && service mysql restart && /bin/bash
     
$ wq!
$ #build 1.4 版,run
$ docker attach lemp_t
root$ mysql -V

+ Nodejs 7.x


RUN DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes curl && \
curl -sL https://deb.nodesource.com/setup_7.x | sudo -E bash - && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --force-yes nodejs
     

狀況

  • locale: Cannot set LC_XXXX to default locale: No such file or directory
    • 指定 ENV 語系之前,加上 RUN locale-gen en_US.UTF-8
  • debconf: (TERM is not set, so the dialog frontend is not usable.)
    • 加上 ENV TERM xterm
  • dpkg-preconfigure: unable to re-open stdin: No such file or directory
    • (o) 安裝前才賦予
      • RUN DEBIAN_FRONTEND=noninteractive apt-get install -y xxxxx
      • 必須接在 install 前
    • (x) 直接加環境變數
      • ENV DEBIAN_FRONTEND=noninteractive
      • 此方式將會在 container 中持續生效
    • noninteractive 非互動式(非交互式)
      • 就是不要求用戶輸入,直接用 default 值跑完全程
  • invoke-rc.d: policy-rc.d denied execution of start.
    • 加上 RUN sed -i "s/^exit 101$/exit 0/" /usr/sbin/policy-rc.d
    • 似乎會導致 mongoDB 安裝失敗
  • connect() to unix:/var/run/php/php7.0-fpm.sock failed (111: Connection refused)
    • 連不到 php7.0-fpm,用 service php7.0-fpm status 檢查狀態並處置
  • mysql 5.7 外部無法連入
    • 將 bind-address = 127.0.0.1 改成 0.0.0.0
    • 舊版可能在 /etc/mysql/my.cnf,新版在 /etc/mysql/mysql.conf.d/mysqld.cnf 內
    • 當然,mysql user 權限要開放 %
  • apt does not have a stable CLI interface. Use with caution in scripts.
  • debconf: delaying package configuration, since apt-utils is not installed
  • W: --force-yes is deprecated, use one of the options starting with --allow instead.
  • /bin/sh: 1: locale-gen: not found
    • 前面加上 RUN apt-get clean && apt-get update && apt-get install -y locales
  • WARNING: apt does not have a stable CLI interface. Use with caution in scripts.
  • Docker and PID 1 zombie 相關文獻
    • 狀況:一個 proccess 被 kill 之後,雖然 memory 已經釋放,但仍佔用 kernel 資源,直到 proccess 全滿,無法在 build 新的 proccess
    • ubuntu 可改用 phusion/baseimage


Dockerfile 實戰 Dockerfile 實戰 Reviewed by Wild on 12/29/2016 06:26:00 下午 Rating: 5

沒有留言:

沒有Google帳號也可發表意見唷!

技術提供:Blogger.