sonic中大量的組件運行在docker容器中,用於隔離彼此的運行環境,從而解決相互之間的互斥問題。下面咱們分析一下sonic中各個容器的構建過程。python
sonic中的容器Dockerfile文件是經過jinjia2模板文件生成的,使用j2命令。linux
sonic在編譯過程當中首先會構建一個叫sonic-slave的容易,該容器用來作編譯容器,後續全部的編譯過程都是在該容器中進行的。redis
編譯容器使用slave.mk做爲makefile,其中有這樣一段代碼將Dockerfile.j2轉換成Dockerfiledocker
# Targets for building docker images $(addprefix $(TARGET_PATH)/, $(SONIC_DOCKER_IMAGES)) : $(TARGET_PATH)/%.gz : .platform docker-start $$(addprefix $(DEBS_PATH)/,$$($$*.gz_DEPENDS)) $$(addprefix $(FILES_PATH)/,$$($$*.gz_FILES)) $$(addprefix $(PYTHON_WHEELS_PATH)/,$$($$*.gz_PYTHON_WHEELS)) $$(addsuffix -load,$$(addprefix $(TARGET_PATH)/,$$($$*.gz_LOAD_DOCKERS))) $$($$*.gz_PATH)/Dockerfile.j2 $(HEADER) # Apply series of patches if exist if [ -f $($*.gz_PATH).patch/series ]; then pushd $($*.gz_PATH) && QUILT_PATCHES=../$(notdir $($*.gz_PATH)).patch quilt push -a; popd; fi mkdir -p $($*.gz_PATH)/debs $(LOG) mkdir -p $($*.gz_PATH)/files $(LOG) mkdir -p $($*.gz_PATH)/python-wheels $(LOG) sudo mount --bind $(DEBS_PATH) $($*.gz_PATH)/debs $(LOG) sudo mount --bind $(FILES_PATH) $($*.gz_PATH)/files $(LOG) sudo mount --bind $(PYTHON_WHEELS_PATH) $($*.gz_PATH)/python-wheels $(LOG) # Export variables for j2. Use path for unique variable names, e.g. docker_orchagent_debs $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_debs=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_DEPENDS),RDEPENDS))\n" | awk '!a[$$0]++')) $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_whls=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_PYTHON_WHEELS)))\n" | awk '!a[$$0]++')) $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_dbgs=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_DBG_PACKAGES)))\n" | awk '!a[$$0]++')) j2 $($*.gz_PATH)/Dockerfile.j2 > $($*.gz_PATH)/Dockerfile #使用環境變量編譯Dockfile.j2 docker build --squash --no-cache \ --build-arg http_proxy=$(HTTP_PROXY) \ --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg user=$(USER) \ --build-arg uid=$(UID) \ --build-arg guid=$(GUID) \ --build-arg docker_container_name=$($*.gz_CONTAINER_NAME) \ -t $* $($*.gz_PATH) $(LOG) docker save $* | gzip -c > $@ # Clean up if [ -f $($*.gz_PATH).patch/series ]; then pushd $($*.gz_PATH) && quilt pop -a -f; popd; fi $(FOOTER)
編譯的過程首先構建一個docker-base容器。shell
docker-base.j2vim
# 根容器是debian:jessie FROM debian:jessie #set up proxy 構建apt代理,會進行deb包緩存,加速編譯速度 RUN echo 'Acquire::http { Proxy "http://172.17.0.2:3142"; };' >>/etc/apt/apt.conf.d/01proxy RUN echo 'Acquire::HTTP::Proxy::download.docker.com "Direct";' >>/etc/apt/apt.conf.d/01proxy RUN echo 'Acquire::HTTP::Proxy::get.docker.io "Direct";' >>/etc/apt/apt.conf.d/01proxy # Clean documentation in FROM image RUN find /usr/share/doc -depth \( -type f -o -type l \) ! -name copyright | xargs rm || true # Clean doc directories that are empty or only contain empty directories RUN while [ -n "$(find /usr/share/doc -depth -type d -empty -print -exec rmdir {} +)" ]; do :; done RUN rm -rf \ /usr/share/man/* \ /usr/share/groff/* \ /usr/share/info/* \ /usr/share/lintian/* \ /usr/share/linda/* \ /var/cache/man/* \ /usr/share/locale/* # Make apt-get non-interactive ENV DEBIAN_FRONTEND=noninteractive # Configure data sources for apt/dpkg COPY ["dpkg_01_drop", "/etc/dpkg/dpkg.cfg.d/01_drop"] COPY ["sources.list", "/etc/apt/sources.list"] COPY ["no_install_recommend_suggest", "/etc/apt/apt.conf.d"] RUN apt-get update # Pre-install fundamental packages RUN apt-get -y install \ rsyslog \ vim-tiny \ perl \ python \ less \ gdb \ lsof \ strace \ tcpdump \ pstack \ net-tools \ sysstat \ dstat \ iotop \ htop \ lrzsz \ linux-base \ linux.perf \ linux-tools-3.16 # Pre-install troubleshooting packages RUN apt-get -y install socat COPY ["etc/rsyslog.conf", "/etc/rsyslog.conf"] COPY ["etc/rsyslog.d/*", "/etc/rsyslog.d/"] COPY ["root/.vimrc", "/root/.vimrc"] # Install dependencies of supervisor RUN apt-get -y install python-pkg-resources python-meld3 RUN mkdir -p /etc/supervisor RUN mkdir -p /var/log/supervisor COPY ["etc/supervisor/supervisord.conf", "/etc/supervisor/"] RUN apt-get -y purge \ exim4 \ exim4-base \ exim4-config \ exim4-daemon-light #根據環境變量進行渲染,決定拷貝哪些deb到容器中 {% if docker_base_debs.strip() -%} # Copy built Debian packages {%- for deb in docker_base_debs.split(' ') %} COPY debs/{{ deb }} debs/ {%- endfor %} # Install built Debian packages and implicitly install their dependencies {%- for deb in docker_base_debs.split(' ') %} RUN dpkg_apt() { [ -f $1 ] && { dpkg -i $1 || apt-get -y install -f; } || return 1; }; dpkg_apt debs/{{ deb }} {%- endfor %} {%- endif %} {% if docker_base_dbgs.strip() -%} # Install common debug-packages {%- for dbg_pkg in docker_base_dbgs.split(' ') %} RUN apt-get -y install {{ dbg_pkg }} {%- endfor %} {% else %} RUN ln /usr/bin/vim.tiny /usr/bin/vim {%- endif %} # Clean up apt # Remove /var/lib/apt/lists/*, could be obsoleted for derived images # 刪除全部下載的安裝包,刪除全部之前下載的安裝包,卸載全部沒有使用的包 RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y RUN rm -rf /var/lib/apt/lists/* RUN rm -rf /tmp/*
渲染後會生成docker-base/Dockerfile文件,因此要修改容器構建Dockerfile時,不能直接修改,而是修改Dockerfile緩存
在docker-base容器的基礎上安裝一些python工具以及安裝一些編譯生成的python庫,該容器沒有實際的內容,只是基礎容器的功能加強。less
docker-config-engine/Dockerfile.j2tcp
# 在docker-base容器上構建 FROM docker-base ## Make apt-get non-interactive ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update # Dependencies for sonic-cfggen RUN apt-get install -y python-lxml python-yaml python-bitarray python-pip python-dev python-natsort RUN pip install --upgrade pip RUN pip install netaddr ipaddr jinja2 pyangbind==0.5.10 COPY \ python-wheels/swsssdk-2.0.1-py2-none-any.whl python-wheels/sonic_config_engine-1.0-py2-none-any.whl python-wheels/ RUN pip install \ python-wheels/swsssdk-2.0.1-py2-none-any.whl python-wheels/sonic_config_engine-1.0-py2-none-any.whl ## Clean up RUN apt-get remove -y python-pip python-dev; apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y RUN rm -rf /debs /python-wheels
構建好了基礎鏡像後,後面的鏡像都在docker-config-engine或者docker-base的基礎上構建,這樣的例如docker-orchagent-bfn,docker-fpm-frr,docker-teamd,docker-database等。工具
咱們以docker-orchagent-bfn爲例看一下功能鏡像的Dockerfile文件:docker-orchagent-bfn/Dockerfile.j2
FROM docker-config-engine ARG docker_container_name RUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name\/%syslogtag%/;" /etc/rsyslog.conf ## Make apt-get non-interactive ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update RUN apt-get install -f -y ifupdown arping libdbus-1-3 libdaemon0 libjansson4 ## Install redis-tools dependencies ## TODO: implicitly install dependencies RUN apt-get -y install libjemalloc1 COPY \ {% for deb in docker_orchagent_debs.split(' ') -%} debs/{{ deb }}{{' '}} {%- endfor -%} debs/ RUN dpkg -i \ {% for deb in docker_orchagent_debs.split(' ') -%} debs/{{ deb }}{{' '}} {%- endfor %} ## Clean up RUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -y ## 鏡像構建完畢後,刪除/debs目錄 RUN rm -rf /debs COPY ["files/arp_update", "/usr/bin"] COPY ["enable_counters.py", "/usr/bin"] COPY ["start.sh", "orchagent.sh", "swssconfig.sh", "/usr/bin/"] COPY ["supervisord.conf", "/etc/supervisor/conf.d/"] ## Copy all Jinja2 template files into the templates folder COPY ["*.j2", "/usr/share/sonic/templates/"] ENTRYPOINT ["/usr/bin/supervisord"]