常常,我會幻想著擁有一個隨時可以攜帶、隨時可以使用的開發環境,那該是多么美好的事情。
在工作中,編譯環境的復雜性常常讓我頭疼不已。稍有不慎,刪除了一些關鍵文件,整個編譯鏈就會瞬間崩潰。更糟糕的是,當我花費大量時間和精力搭建好的環境,不得不在不同的機器上重復這份工作時,心中的焦慮更是無以言表。尤其是當代碼需要交給同事處理,如果能將整個環境如同復制粘貼一般簡單傳遞,那將是多么美妙的事。
當然,虛擬機的確可以實現這種“復制粘貼”的效果,但從版本管理、鏡像托管和維護的角度來看,其成本卻頗高。這樣的方案,并不是我心目中理想的選擇。于是,我將目光轉向了Docker。
一、需求整理
我理想的C/C++編譯環境,至少需要具備以下環境或工具:
- ?
Conan
?: 一個我常用且比較流行的的C/C++包管理工具 - ?
CMake
?: 最流行的C/C++構建系統 - 基礎編譯環境:
gcc
?,make
?,gdb
?等 - ARM編譯工具鏈:
gcc-arm-none-eabi
?,gcc-arm-linux-gnueabihf
?,gcc-aarch64-linux-gnu
?等 - 基礎開發庫:比如
libssl-dev
?,libgl-dev
?等,這些主要是方便其他上層庫的編譯
二、Docker簡介
??
Docker 是一個容器引擎。對于沒有接觸過 Docker 的開發者來說可能比較陌生。可以將 Docker 大致理解為一個輕量級虛擬機框架,但實際上它比虛擬機更高效。Docker 具備以下幾點特性:
- 足夠快:Docker 容器啟動速度非常快,通常只需要幾秒鐘。這是因為容器共享主機操作系統的內核,而不像虛擬機需要啟動一個完整的操作系統。這種高效性使得開發和測試過程更加順暢。
- 管理方便:Docker 提供了豐富的命令行工具和 API,使得容器的創建、啟動、停止和銷毀變得非常簡單。同時,Docker Compose 允許開發者使用一個簡單的 YAML 文件來定義和管理多容器應用,使得部署和管理復雜應用變得更加方便。
- 生態好:Docker 擁有龐大且活躍的社區,提供了豐富的鏡像資源。通過 Docker Hub,開發者可以方便地下載和分享容器鏡像。Docker 還支持與各種 CI/CD 工具、云平臺和編排工具(如 Kubernetes)的集成,形成了一個完善的生態系統。
- 一致性:Docker 確保應用程序在開發、測試和生產環境中運行的一致性。這意味著開發者可以在本地開發和測試應用程序,然后將相同的容器部署到生產環境中,而不必擔心環境差異帶來的問題。
- 資源高效:與傳統的虛擬機相比,Docker 容器更加輕量級,消耗的資源更少。容器共享主機操作系統的內核,避免了虛擬機需要為每個實例分配完整操作系統資源的情況。
- 安全隔離:Docker 使用內核命名空間(namespace)和控制組(cgroup)技術提供進程和資源的隔離。這種隔離確保了每個容器之間的安全性,避免了相互干擾。
- 可移植性:Docker 容器可以在任何支持 Docker 的系統上運行,無論是本地開發環境、云服務還是數據中心。這種可移植性使得應用程序能夠在不同環境之間輕松遷移。
這些特性使得 Docker 成為現代軟件開發和部署過程中不可或缺的工具,極大地提高了應用的可移植性和開發效率。在Docker中包含三個基本概念,分別是:
- 鏡像(Image) :Docker 鏡像(Image),就相當于是一個 root 文件系統。比如官方鏡像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系統的 root 文件系統。
- 容器(Container) :鏡像(Image)和容器(Container)的關系,就像是面向對象程序設計中的類和實例一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等。
- 倉庫(Repository) :倉庫可看成一個代碼控制中心,用來保存鏡像。
Docker 的安裝方法不在這里贅述。目前 Docker 發布了 Docker Desktop 軟件,這款桌面軟件允許用戶通過 GUI 來管理 Docker 鏡像和容器。對新手來說會比較友好,至于是使用命令行還是 GUI 方式,可根據個人喜好進行選擇。
三、DockerFile
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
COPY ./sources.list /etc/apt/sources.list# Install packages
RUN set -eux \&& apt-get update \&& apt-get -yq upgrade \&& apt-get -yq install \aptitude apt-rdepends bash build-essential ccache clang clang-tidy cppcheck curl doxygen diffstat gawk gdb git gnupg gperf iputils-ping \linux-tools-generic nano nasm ninja-build openssh-server openssl pkg-config python3 python-is-python3 spawn-fcgi net-tools iproute2 \sudo tini unzip valgrind wget zip texinfo gcc-multilib chrpath socat cpio xz-utils debianutils \patch perl tar rsync bc xterm whois software-properties-common apt-transport-https ca-certificates\dh-autoreconf apt-transport-https g++ graphviz xdot mesa-utils \&& exit 0# Install cmake
RUN set -eux \&& wget https://github.com/Kitware/CMake/releases/download/v3.28.5/cmake-3.28.5-linux-x86_64.sh -q -O /tmp/cmake-install.sh \&& chmod u+x /tmp/cmake-install.sh \&& mkdir /opt/cmake-3.28.5 \&& /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-3.28.5 \&& rm /tmp/cmake-install.sh \&& ln -s /opt/cmake-3.28.5/bin/* /usr/local/bin \&& cmake --version \&& exit 0# Install arm compiler
RUN set -eux \&& apt-get update \&& apt-get -yq install \gcc-arm-none-eabi \g++-arm-linux-gnueabi gcc-arm-linux-gnueabi \g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf \g++-aarch64-linux-gnu gcc-aarch64-linux-gnu \&& exit 0# Install python pip
RUN set -eux \&& python3 --version \&& curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \&& python3 get-pip.py \&& rm get-pip.py \&& python3 -m pip install -U pip \&& pip3 --version \&& pip3 install --upgrade pip setuptools wheel \&& pip3 --version \&& exit 0# Install python packages
RUN set -eux \&& pip3 --version \&& pip3 install --upgrade pip setuptools wheel \&& pip3 --version \&& pip3 install --upgrade autoenv autopep8 cmake-format clang-format conan meson \&& pip3 install --upgrade cppclean flawfinder lizard pygments pybind11 GitPython pexpect subunit Jinja2 pylint CLinters \&& exit 0# Install libraries
RUN set -eux \&& apt-get install -yq \libgl-dev libgl1-mesa-dev \libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxext-dev \libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev \libxss-dev libxt-dev libxtst-dev libxv-dev libxxf86vm-dev libxcb-glx0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev \libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev \libxcb-xinerama0-dev libxcb-dri3-dev uuid-dev libxcb-cursor-dev libxcb-dri2-0-dev libxcb-dri3-dev libxcb-present-dev \libxcb-composite0-dev libxcb-ewmh-dev libxcb-res0-dev libxcb-util-dev libxcb-util0-dev\&& apt-get -yq autoremove \&& apt-get -yq autoclean \&& apt-get -yq clean \&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \&& exit 0# Setup ssh
RUN set -eux \&& mkdir -p /var/run/sshd \&& mkdir -p /root/.ssh \&& sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config \&& sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config \&& echo 'root:root' | chpasswd \&& exit 0ENTRYPOINT ["/usr/bin/tini","--"]
CMD ["/usr/sbin/sshd","-D","-e"]
如您所見,我已經寫完了一份DockerFile。其中包含了一些基礎工具,如網絡工具、CMake、Python、ARM交叉編譯工具鏈等。甚至還有一些開發用的基礎庫,同時也配置好了SSH。點擊下方圖片查看最新完整代碼👇👇👇(歡迎START!!!🤩)
3.1 基礎鏡像和環境變量設置
FROM ubuntu:22.04
ENV DEBIAN_FRONTEND=noninteractive
COPY ./sources.list /etc/apt/sources.list
- FROM ubuntu:22.04:指定基礎鏡像為Ubuntu 22.04。
- ENV DEBIAN_FRONTEND=noninteractive:設置環境變量,使apt-get在非交互模式下運行,避免提示用戶輸入。
- COPY ./sources.list /etc/apt/sources.list:將本地的sources.list文件復制到容器中的/etc/apt/sources.list,這里使用了清華源,如果不要請將此條代碼注釋掉。
3.2 安裝基礎軟件包
RUN set -eux \&& apt-get update \&& apt-get -yq upgrade \&& apt-get -yq install \aptitude apt-rdepends bash build-essential ccache clang clang-tidy cppcheck curl doxygen diffstat gawk gdb git gnupg gperf iputils-ping \linux-tools-generic nano nasm ninja-build openssh-server openssl pkg-config python3 python-is-python3 spawn-fcgi net-tools iproute2 \sudo tini unzip valgrind wget zip texinfo gcc-multilib chrpath socat cpio xz-utils debianutils \patch perl tar rsync bc xterm whois software-properties-common apt-transport-https ca-certificates\dh-autoreconf apt-transport-https g++ graphviz xdot mesa-utils \&& exit 0
- set -eux:啟用嚴格模式,打印所有命令,并在出現錯誤時停止腳本執行。
- apt-get update:更新包列表。
- apt-get -yq upgrade:升級所有已安裝的包。
- apt-get -yq install … :安裝列出的各種軟件包,涵蓋編譯工具、開發工具、網絡工具等。
3.3 安裝CMake
RUN set -eux \&& wget https://github.com/Kitware/CMake/releases/download/v3.28.5/cmake-3.28.5-linux-x86_64.sh -q -O /tmp/cmake-install.sh \&& chmod u+x /tmp/cmake-install.sh \&& mkdir /opt/cmake-3.28.5 \&& /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-3.28.5 \&& rm /tmp/cmake-install.sh \&& ln -s /opt/cmake-3.28.5/bin/* /usr/local/bin \&& cmake --version \&& exit 0
- wget … -O /tmp/cmake-install.sh:下載CMake安裝腳本到/tmp目錄。
- chmod u+x /tmp/cmake-install.sh:給予安裝腳本執行權限。
- mkdir /opt/cmake-3.28.5:創建安裝目錄。
- /tmp/cmake-install.sh --skip-license --prefix=/opt/cmake-3.28.5:執行安裝腳本,跳過許可證,并安裝到指定目錄。
- rm /tmp/cmake-install.sh:刪除安裝腳本。
- ln -s /opt/cmake-3.28.5/bin/ /usr/local/bin*:創建符號鏈接,使CMake命令可用。
- cmake --version:驗證CMake安裝。
3.4 安裝ARM編譯器
RUN set -eux \&& apt-get update \&& apt-get -yq install \gcc-arm-none-eabi \g++-arm-linux-gnueabi gcc-arm-linux-gnueabi \g++-arm-linux-gnueabihf gcc-arm-linux-gnueabihf \g++-aarch64-linux-gnu gcc-aarch64-linux-gnu \&& exit 0
- apt-get update:更新包列表。
- apt-get -yq install … :安裝ARM編譯器,包括不同架構的編譯器,如
ARM none-eabi
?、ARM Linux-gnueabi
?、ARM Linux-gnueabihf
?、AArch64 Linux-gnu
?。
3.5 安裝Python的pip工具
RUN set -eux \&& python3 --version \&& curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \&& python3 get-pip.py \&& rm get-pip.py \&& python3 -m pip install -U pip \&& pip3 --version \&& pip3 install --upgrade pip setuptools wheel \&& pip3 --version \&& exit 0
- python3 --version:檢查Python3版本。
- curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py:下載pip安裝腳本。
- python3 get-pip.py:安裝pip。
- rm get-pip.py:刪除安裝腳本。
- python3 -m pip install -U pip:升級pip。
- pip3 --version:檢查pip版本。
- pip3 install --upgrade pip setuptools wheel:安裝和升級常用的Python包。
3.6 安裝Python包
RUN set -eux \&& pip3 --version \&& pip3 install --upgrade pip setuptools wheel \&& pip3 --version \&& pip3 install --upgrade autoenv autopep8 cmake-format clang-format conan meson \&& pip3 install --upgrade cppclean flawfinder lizard pygments pybind11 GitPython pexpect subunit Jinja2 pylint CLinters \&& exit 0
- pip3 --version:檢查pip版本。
- pip3 install --upgrade pip setuptools wheel:確保pip及其依賴包是最新的。
- pip3 install --upgrade autoenv autopep8 cmake-format clang-format conan meson:安裝開發工具包。
- pip3 install --upgrade cppclean flawfinder lizard pygments pybind11 GitPython pexpect subunit Jinja2 pylint CLinters:安裝其他開發工具包。
3.7 安裝其他開發庫文件
RUN set -eux \&& apt-get install -yq \libgl-dev libgl1-mesa-dev \libx11-xcb-dev libfontenc-dev libice-dev libsm-dev libxaw7-dev libxcomposite-dev libxcursor-dev libxdamage-dev libxext-dev \libxfixes-dev libxi-dev libxinerama-dev libxkbfile-dev libxmu-dev libxmuu-dev libxpm-dev libxrandr-dev libxrender-dev libxres-dev \libxss-dev libxt-dev libxtst-dev libxv-dev libxxf86vm-dev libxcb-glx0-dev libxcb-render0-dev libxcb-render-util0-dev libxcb-xkb-dev \libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-sync-dev libxcb-xfixes0-dev \libxcb-xinerama0-dev libxcb-dri3-dev uuid-dev libxcb-cursor-dev libxcb-dri2-0-dev libxcb-dri3-dev libxcb-present-dev \libxcb-composite0-dev libxcb-ewmh-dev libxcb-res0-dev libxcb-util-dev libxcb-util0-dev\&& apt-get -yq autoremove \&& apt-get -yq autoclean \&& apt-get -yq clean \&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \&& exit 0
- 這些主要是QT的依賴庫,后續也可以自行增加。
3.8 設置SSH
RUN \set -eux && \mkdir -p /var/run/sshd && \mkdir -p /root/.ssh && \sed -ri 's/^#?PermitRootLogin\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config && \sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config && \echo 'root:root' | chpasswd && \exit 0
- mkdir -p /var/run/sshd:創建sshd運行目錄。
- mkdir -p /root/.ssh:創建root用戶的SSH目錄。
- sed -ri ‘s/^#?PermitRootLogin\s+.*/PermitRootLogin yes/’ /etc/ssh/sshd_config:允許root用戶通過SSH登錄。
- sed -ri ‘s/UsePAM yes/#UsePAM yes/g’ /etc/ssh/sshd_config:禁用PAM。
- echo ‘root’ | chpasswd:設置root用戶密碼為’root’。
3.9 入口點和默認命令
ENTRYPOINT ["/usr/bin/tini" "--"]
CMD ["/usr/sbin/sshd" "-D" "-e"]
- ENTRYPOINT ["/usr/bin/tini" "–"] :使用tini作為init進程,處理信號和子進程。
- CMD ["/usr/sbin/sshd" "-D" "-e"] :啟動sshd服務并保持前臺運行。
四、構建鏡像
首先,請將第三部分的代碼復制將其保存為Dockerfile
?文件。如果您需要切換其他APT源(如阿里源、清華源等),請手動到官方鏡像站獲取相關源地址,并將其保存為sources.list
?。這里附上Ubuntu22.04清華鏡像源:
# 默認注釋了源碼鏡像以提高 apt update 速度,如有需要可自行取消注釋
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
# deb-src http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
# deb-src http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-updates main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse
# deb-src http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-backports main restricted universe multiverse# 以下安全更新軟件源包含了官方源與鏡像站配置,如有需要可自行修改注釋切換
deb http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse
# deb-src http://security.ubuntu.com/ubuntu/ jammy-security main restricted universe multiverse# 預發布軟件源,不建議啟用
# deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
# # deb-src http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ jammy-proposed main restricted universe multiverse
注意:請使用http而不要用https.
完成后您的文件夾應該為如下布局:
?
接下來請打開您的命令行程序并進入該文件夾,如果您使用的是Windows操作系統,推薦使用Powershell. 鍵入以下命令,開始構建:
docker buildx build . -t jelin-dev/ubuntu22.04
您可以在控制臺中看到構建過程:
[+] Building 218.2s (14/14) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 3.77kB 0.0s=> [internal] load metadata for docker.io/library/ubuntu:22.04 3.5s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [internal] load build context 0.0s=> => transferring context: 1.28kB 0.0s=> [1/9] FROM docker.io/library/ubuntu:22.04@sha256:a6d2b38300ce017add71440577d5b0a90460d0e57fd7aec21dd0d1b0761b 6.9s=> => resolve docker.io/library/ubuntu:22.04@sha256:a6d2b38300ce017add71440577d5b0a90460d0e57fd7aec21dd0d1b0761b 0.0s=> => sha256:2af372c1e2645779643284c7dc38775e3dbbc417b2d784a27c5a9eb784014fb8 424B / 424B 0.0s=> => sha256:52882761a72a60649edff9a2478835325d084fb640ea32a975e29e12a012025f 2.30kB / 2.30kB 0.0s=> => sha256:a8b1c5f80c2d2a757adc963e3fe2dad0b4d229f83df3349fbb70e4d12dd48822 29.53MB / 29.53MB 5.7s=> => sha256:a6d2b38300ce017add71440577d5b0a90460d0e57fd7aec21dd0d1b0761bbfb2 1.13kB / 1.13kB 0.0s=> => extracting sha256:a8b1c5f80c2d2a757adc963e3fe2dad0b4d229f83df3349fbb70e4d12dd48822 1.0s=> [2/9] COPY ./sources.list /etc/apt/sources.list 0.1s=> [3/9] RUN set -eux && apt-get update && apt-get -yq upgrade && apt-get -yq install aptit 81.2s=> [4/9] RUN set -eux && wget https://github.com/Kitware/CMake/releases/download/v3.28.5/cmake-3.28.5-linux 13.4s=> [5/9] RUN set -eux && apt-get update && apt-get -yq install gcc-arm-none-eabi g+ 61.0s=> [6/9] RUN set -eux && python3 --version && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.p 9.8s=> [7/9] RUN set -eux && pip3 --version && pip3 install --upgrade pip setuptools wheel && pip3 29.4s=> [8/9] RUN set -eux && apt-get install -yq libgl-dev libgl1-mesa-dev libx11-xcb-dev libfon 5.8s=> [9/9] RUN set -eux && mkdir -p /var/run/sshd && mkdir -p /root/.ssh && sed -ri 's/^#?PermitRo 0.4s=> exporting to image 6.7s=> => exporting layers 6.7s=> => writing image sha256:27e12e5d293fc972028bba5e9e488d4ed655ae5d479bd92b4fe1cb0295e6311d 0.0s=> => naming to docker.io/jelin-dev/ubuntu22.04
構建完成后,您可以通過docker images
?查看已有鏡像:
REPOSITORY TAG IMAGE ID CREATED SIZE
jelin-dev/ubuntu22.04 latest 27e12e5d293f About a minute ago 5.25GB
您可以通過以下命令創建并啟動容器:
docker run -itd -p 2201:22 --name test jelin-dev/ubuntu22.04:latest
這樣容器就在后臺運行了,您可以通過SSH連接到容器,賬號密碼均為root
?
五、使用建議
眾所周知,C/C++足夠底層,以至于其跨平臺特性也僅限于代碼層面,只能通過使用不同的編譯器編譯才能實現多平臺運行。所以它不像C#、Java、Python等編程語言有官方指定的包管理器,甚至連構建系統也五花八門。jelin-dev
?這個鏡像挑選了業界主流的C/C++包管理器和構建系統,以及配備了我所涉及到需要交叉編譯的工具鏈,如果您有特殊的需要,您可以自行添加相關環境,再進行構建和使用。
Tap1. Conan包管理數據持久化
在 jelin-dev
? 鏡像中,我使用了 Conan 作為包管理器。建議您在創建容器時將 /root/.conan2
? 文件夾掛載到本地目錄中。這些數據很重要,因為 Conan 會將已經下載和編譯的庫存放在這個文件夾內。如果沒有做持久化,那么每次使用都將需要重新構建這些庫。
Tap2. 使用CLion
CLion 是一個非常好用的 IDE,功能多樣且靈活。它默認使用 CMake 進行構建,并且對 CMake 的支持比其他 IDE 都要強大,配置項目和管理變得非常輕松。除此之外,CLion 還提供了 Conan 插件,可以通過 GUI 方式配置依賴,使用體驗非常棒。另外,CLion 對 Docker 的支持也是一大亮點。啟動速度極快,幾乎感覺不到延遲,使用起來非常順暢。這些特性讓 CLion 成為開發 C++ 項目的理想選擇。
?