雲端容器化技術與資源調度

認識 Docker Container 與 Image

Container 和 VM 差在哪邊?

container and vm

目標相同

VM 和 Container 都是為了將一套應用程式所需的執行環境打包起來,建立一個獨立環境(沙箱),方便在不同的硬體中移動。

實現方式不同

  • VM 基於硬體層的虛擬化
  • Container 是基於作業系統層的虛擬化

啟動速度不同

Container 採取了共用 OS 的策略,因此不用在 Container 內啟動 Guest OS。因此大幅提升了啟動的速度,在不到幾秒的時間,整個沙箱環境就能啟動完成!

容器是一個很新的技術嗎?

  • 早在 1982 年的 Unix 系統當中,就內建了 chroot 的指令,可以讓該 Unix 環境當中,虛擬化另一個完全隔離的沙箱環境出來,這個環境就稱為容器。
  • Google 的 Gmail / YouTube 早在 12 年前就已經全面以容器化技術來架設,服務全球各地的用戶。
  • 2013 年,dotCloud (一家雲端PaaS服務公司),開源了一套標準化的容器平台 Docker,受到非常大的迴響,這才讓更多的開發者/企業投入了使用 Docker 作為部署應用程式的平台。

容器裡面到底有沒有自己的作業系統?

雖然容器共用了外面的 Host OS,但是實際上,從容器內部其實是可以模擬各家作業系統的 File System根目錄結構,來達到讓容器感覺上好像有自己的獨立作業系統。但事實上,Kernel 層還是使用外面的OS的。因此定義上,不能算是擁有自己的作業系統。

讓我們實際在 Cloud Shell 來分別起動 Ubuntu 和 CentOS 兩種作業系統的容器看看,

$ docker run -it --name my-ubuntu ubuntu:18.04 /bin/bash

# 在 ubuntu 容器內查詢 linux 發行版本:
$ cat /etc/*-release

# 在 ubuntu 容器內查詢 kernal 版本
$ uname -r

$ exit
$ docker rm my-ubuntu
docker run -it --name my-centos centos:7 /bin/bash
# 在 centos 容器內查詢 linux 發行版本:
$ cat /etc/*-release

# 在 centos 容器內查詢 kernal 版本
$ uname -r

$ exit
$ docker rm my-centos

什麼是 Image (映像)?

先回顧我們第一次用來啟動 Nginx 容器:

docker run --name my-nginx -p 8080:80 -d nginx:1.17.4

其中,nginx:1.17.4 就是一個他人製作好的 Image。

我們可以利用這個 Image 來下 docker run 指令,把這個 Image 實際跑起來,跑起來之後的隔離環境,我們才稱之為容器

Image 就像一個印章一樣,而跑起來的容器就像被這個印章蓋出來的圖案。

我們甚至可以多下幾次 docker run 的指令,來一次獲得多個隔離(且幾乎一模一樣)的 Nginx 容器。

image container

如果你曾機接觸過虛擬化技術,應該會知道VM 也可以在某個時間點來進行快照並做成 Image。這邊的 Image 跟 Docker Image 概念上是很類似的。

Image 的內容是什麼?

image layer

Image 是如何製作出來的?

我們可以透過撰寫文本格式的 Dockerfile 接著透過 docker build 指令來讓 Docker Engine 替我們產生 Image。 例如,以下就是的 nginx:1.17.5 當初製作時,作者撰寫的Dockerfile

FROM debian:buster-slim

LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"

ENV NGINX_VERSION   1.17.5
ENV NJS_VERSION     0.3.6
ENV PKG_RELEASE     1~buster

RUN set -x \
# create nginx user/group first, to be consistent throughout docker variants
    && addgroup --system --gid 101 nginx \
    && adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \
    && apt-get update \
    && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \
    && \
    NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
    found=''; \
    for server in \
        ha.pool.sks-keyservers.net \
        hkp://keyserver.ubuntu.com:80 \
        hkp://p80.pool.sks-keyservers.net:80 \
        pgp.mit.edu \
    ; do \
        echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
        apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
    done; \
    test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
    apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
    && dpkgArch="$(dpkg --print-architecture)" \
    && nginxPackages=" \
        nginx=${NGINX_VERSION}-${PKG_RELEASE} \
        nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \
        nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \
        nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \
        nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${PKG_RELEASE} \
    " \
    && case "$dpkgArch" in \
        amd64|i386) \
# arches officialy built by upstream
            echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \
            && apt-get update \
            ;; \
        *) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
            echo "deb-src https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \
            \
# new directory for storing sources and .deb files
            && tempDir="$(mktemp -d)" \
            && chmod 777 "$tempDir" \
# (777 to ensure APT's "_apt" user can access it too)
            \
# save list of currently-installed packages so build dependencies can be cleanly removed later
            && savedAptMark="$(apt-mark showmanual)" \
            \
# build .deb files from upstream's source packages (which are verified by apt-get)
            && apt-get update \
            && apt-get build-dep -y $nginxPackages \
            && ( \
                cd "$tempDir" \
                && DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
                    apt-get source --compile $nginxPackages \
            ) \
# we don't remove APT lists here because they get re-downloaded and removed later
            \
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
            && apt-mark showmanual | xargs apt-mark auto > /dev/null \
            && { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
            \
# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
            && ls -lAFh "$tempDir" \
            && ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
            && grep '^Package: ' "$tempDir/Packages" \
            && echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
#   Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
#   ...
#   E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages  Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
            && apt-get -o Acquire::GzipIndexes=false update \
            ;; \
    esac \
    \
    && apt-get install --no-install-recommends --no-install-suggests -y \
                        $nginxPackages \
                        gettext-base \
    && apt-get remove --purge --auto-remove -y ca-certificates && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \
    \
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
    && if [ -n "$tempDir" ]; then \
        apt-get purge -y --auto-remove \
        && rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
    fi

# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
    && ln -sf /dev/stderr /var/log/nginx/error.log

EXPOSE 80

STOPSIGNAL SIGTERM

CMD ["nginx", "-g", "daemon off;"]