Docker 系列七(Duubo 微服務部署實踐).

來源:互聯網
上載者:User
一、前言

    之前我們公司部署服務,就是大家都懂的那一套(安裝JDK、Tomcat —> 編譯好檔案或者打war包上傳 —> 啟動Tomcat),這種部署方式一直持續了很久,帶來的問題也很多:

1、繁重的發布任務。微服務一多,就要每個服務都要重啟一遍,而且要是叢集的話,那要啟動的服務就更多了。

2、環境遷移報錯。經常發生的一件事,同樣的一套代碼,這台伺服器上就是能跑起來,換個伺服器就是報錯了。

3、士氣低落。小公司沒有正經的營運,都是讓開發兼并著做這方面的工作,然後負責這塊的同事怨言很多(因為這種發布部署實在太無趣了)。

    所以領導決定引起 Docker 作為我們的部署方式,一來可以很好的解決目前項目部署存在的問題,二來為項目注入新鮮血液。

    從上個月15號開始接觸 Docker,到現在把我們系統的微服務架構初步搭建好,折騰了好久,踩了很多坑。紀念一下小成就,寫了這篇部落格。為了避免涉嫌泄露公司機密,就小而全的做一些簡單介紹哈,以下面這張最小微服務架構圖為例,部署一套 Dubbo 微服務。

二、服務鏡像打包     1、Tomcat 基礎環境搭建

    我們系統的每個微服務都部署運行在 Tomcat 上(聽說這種方式很不好,對於一些不是web工程的,沒必要搭建成 web 服務,增加複雜性,也浪費系統資源),所以我的想法是:先搭建一套 Tomcat 環境鏡像,然後每個微服務都基於這個環境鏡像去構建。所以寫了一個 tomcat-env 的鏡像,思路如下:

    -- 基於 JDK 的 Tomcat 容器(主要參考官網 Tomcat 鏡像的 Dockerfile)。

    -- 在上下文目錄存放項目編譯檔案,並重新命名為 ROOT(不放 war 包的原因是考慮調試的時候方便,不用改一個檔案,就打個war包)。

    -- 刪除原本 Tomcat 容器 webapps 目錄下的 ROOT 檔案,並將上下文目錄中項目的 ROOT 檔案夾上傳到容器 webapps 目錄下。

    -- 啟動服務。

FROM openjdk:8-jreENV CATALINA_HOME /usr/local/tomcatENV PATH $CATALINA_HOME/bin:$PATHRUN mkdir -p "$CATALINA_HOME"WORKDIR $CATALINA_HOME# let "Tomcat Native" live somewhere isolatedENV TOMCAT_NATIVE_LIBDIR $CATALINA_HOME/native-jni-libENV LD_LIBRARY_PATH ${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOMCAT_NATIVE_LIBDIR# runtime dependencies for Tomcat Native Libraries# Tomcat Native 1.2+ requires a newer version of OpenSSL than debian:jessie has available# > checking OpenSSL library version >= 1.0.2...# > configure: error: Your version of OpenSSL is not compatible with this version of tcnative# see http://tomcat.10.x6.nabble.com/VOTE-Release-Apache-Tomcat-8-0-32-tp5046007p5046024.html (and following discussion)# and github.com/docker-library/tomcat/pull/31ENV OPENSSL_VERSION 1.1.0f-3+deb9u2RUN set -ex; \    currentVersion="$(dpkg-query --show --showformat '${Version}\n' openssl)"; \    if dpkg --compare-versions "$currentVersion" '<<' "$OPENSSL_VERSION"; then \        if ! grep -q stretch /etc/apt/sources.list; then \# only add stretch if we're not already building from within stretch            { \                echo 'deb http://deb.debian.org/debian stretch main'; \                echo 'deb http://security.debian.org stretch/updates main'; \                echo 'deb http://deb.debian.org/debian stretch-updates main'; \            } > /etc/apt/sources.list.d/stretch.list; \            { \# add a negative "Pin-Priority" so that we never ever get packages from stretch unless we explicitly request them                echo 'Package: *'; \                echo 'Pin: release n=stretch*'; \                echo 'Pin-Priority: -10'; \                echo; \# ... except OpenSSL, which is the reason we're here                echo 'Package: openssl libssl*'; \                echo "Pin: version $OPENSSL_VERSION"; \                echo 'Pin-Priority: 990'; \            } > /etc/apt/preferences.d/stretch-openssl; \        fi; \        apt-get update; \        apt-get install -y --no-install-recommends openssl="$OPENSSL_VERSION"; \        rm -rf /var/lib/apt/lists/*; \    fiRUN apt-get update && apt-get install -y --no-install-recommends \        libapr1 \    && rm -rf /var/lib/apt/lists/*# see www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/KEYS# see also "update.sh" (github.com/docker-library/tomcat/blob/master/update.sh)ENV GPG_KEYS 05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 713DA88BE50911535FE716F5208B0AB1D63011C7 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23ENV TOMCAT_MAJOR 8ENV TOMCAT_VERSION 8.0.53ENV TOMCAT_SHA512 cd8a4e48a629a2f2bb4ce6b101ebcce41da52b506064396ec1b2915c0b0d8d82123091242f2929a649bcd8b65ecf6cd1ab9c7d90ac0e261821097ab6fbe22df9ENV TOMCAT_TGZ_URLS \# issues.apache.org/jira/browse/INFRA-8753?focusedCommentId=14735394#comment-14735394    www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz \# if the version is outdated, we might have to pull from the dist/archive :/    www-us.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz \    www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz \    archive.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gzENV TOMCAT_ASC_URLS \    www.apache.org/dyn/closer.cgi?action=download&filename=tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc \# not all the mirrors actually carry the .asc files :'(    www-us.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc \    www.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.asc \    archive.apache.org/dist/tomcat/tomcat-$TOMCAT_MAJOR/v$TOMCAT_VERSION/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz.ascRUN set -eux; \    \    savedAptMark="$(apt-mark showmanual)"; \    apt-get update; \    \    apt-get install -y --no-install-recommends gnupg dirmngr; \    \    export GNUPGHOME="$(mktemp -d)"; \    for key in $GPG_KEYS; do \        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \    done; \    \    apt-get install -y --no-install-recommends wget ca-certificates; \    \    success=; \    for url in $TOMCAT_TGZ_URLS; do \        if wget -O tomcat.tar.gz "$url"; then \            success=1; \            break; \        fi; \    done; \    [ -n "$success" ]; \    \    echo "$TOMCAT_SHA512 *tomcat.tar.gz" | sha512sum -c -; \    \    success=; \    for url in $TOMCAT_ASC_URLS; do \        if wget -O tomcat.tar.gz.asc "$url"; then \            success=1; \            break; \        fi; \    done; \    [ -n "$success" ]; \    \    gpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz; \    tar -xvf tomcat.tar.gz --strip-components=1; \    rm bin/*.bat; \    rm tomcat.tar.gz*; \    command -v gpgconf && gpgconf --kill all || :; \    rm -rf "$GNUPGHOME"; \    \    nativeBuildDir="$(mktemp -d)"; \    tar -xvf bin/tomcat-native.tar.gz -C "$nativeBuildDir" --strip-components=1; \    apt-get install -y --no-install-recommends \        dpkg-dev \        gcc \        libapr1-dev \        libssl-dev \        make \        "openjdk-${JAVA_VERSION%%[.~bu-]*}-jdk=$JAVA_DEBIAN_VERSION" \    ; \    ( \        export CATALINA_HOME="$PWD"; \        cd "$nativeBuildDir/native"; \        gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)"; \        ./configure \            --build="$gnuArch" \            --libdir="$TOMCAT_NATIVE_LIBDIR" \            --prefix="$CATALINA_HOME" \            --with-apr="$(which apr-1-config)" \            --with-java-home="$(docker-java-home)" \            --with-ssl=yes; \        make -j "$(nproc)"; \        make install; \    ); \    rm -rf "$nativeBuildDir"; \    rm bin/tomcat-native.tar.gz; \    \# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies    apt-mark auto '.*' > /dev/null; \    [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; \    apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \    rm -rf /var/lib/apt/lists/*; \    \# sh removes env vars it doesn't support (ones with periods)# github.com/docker-library/tomcat/issues/77    find ./bin/ -name '*.sh' -exec sed -ri 's|^#!/bin/sh$|#!/usr/bin/env bash|' '{}' +# verify Tomcat Native is working properlyRUN set -e \    && nativeLines="$(catalina.sh configtest 2>&1)" \    && nativeLines="$(echo "$nativeLines" | grep 'Apache Tomcat Native')" \    && nativeLines="$(echo "$nativeLines" | sort -u)" \    && if ! echo "$nativeLines" | grep 'INFO: Loaded APR based Apache Tomcat Native library' >&2; then \        echo >&2 "$nativeLines"; \        exit 1; \    fiEXPOSE 8080RUN rm -rf /usr/local/tomcat/webapps/ROOT/ONBUILD COPY ROOT /usr/local/tomcat/webapps/ROOT/ONBUILD ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]
tomcat-env

看起來很複雜,不要被嚇到,其實都是抄的官網 Tomcat 鏡像的Dockerfile,然後改動了一點,主要是後面三句:刪除容器 ROOT 檔案夾,拷貝上下文目錄的 ROOT 檔案夾到 wenapps 目錄下,重啟服務。

RUN rm -rf /usr/local/tomcat/webapps/ROOT/ONBUILD COPY ROOT /usr/local/tomcat/webapps/ROOT/ONBUILD ENTRYPOINT ["/usr/local/tomcat/bin/catalina.sh","run"]

tips:1、ONBUILD 命令本次鏡像不會被執行,只有以這個鏡像為基礎鏡像的時候才會被執行。

          2、上下文目錄指的是 Dockerfile 檔案所在的目錄。

          3、該鏡像已上傳到 DockerHub 上:hub.docker.com/r/jmcui/tomcat-env/

     2、微服務鏡像打包

    有了基礎環境鏡像 tomcat-env,那麼打包一個服務鏡像就是一件再簡單不過的事情了:

FROM tomcat-env:1.0

    沒錯,就是這麼簡單,因為我們把所有的工作都放在 tomcat-env 中了,其實就是那個 ONBUILD 命令的效果啦~~ 

三、編排檔案 docker-compose.yml

    微服務項目要部署起來,主要是靠 docker-compose.yml 檔案進行編排,規定服務之間的關聯以及先後啟動順序,然後把幾十個零散的微服務當成一個整體來統一管理。

    首先,困擾我的是網路問題。做過開發的都知道,要在項目中指定(Spring 在 applicationContext.xml)資料庫地址和 Zookeeper 地址,那麼我怎麼知道容器的 ip 地址是多少呢?先來瞭解下 Docker 的網路模式?

    Docker 的預設網路配置是 "bridge",當 Docker 啟動時,會自動在主機上建立一個 docker0 虛擬橋接器,實際上是 Linux 的一個 bridge,可以理解為一個軟體交換器。Docker 會隨機分配一個本地未佔用的私人網段(在 RFC1918 中定義)中的一個地址給 docker0 介面,它會在掛載到它的網口之間進行轉寄。當建立一個 Docker 容器的時候,同時會建立了一對 veth pair 介面。這對介面一端在容器內,即 eth0;另一端在本地並被掛載到 docker0 橋接器,名稱以 veth 開頭(例如 vethAQI2QT)。通過這種方式,主機可以跟容器通訊,容器之間也可以相互連信。

     也就是說,每次容器啟動以後的 ip 地址是不固定的,這該怎麼辦呢?當然可以寫死 IP 位址,規定區域網路網段,給每個服務編排 IP 位址;當然也可以把network_mode="host",統一用宿主機的網路地址。當然!這些都不是最好的辦法:

version: '3.7'#服務列表services:  #基礎組件 zookeeper    zookeeper:    image: zookeeper    restart: always    ports:      - 4181:2181  #基礎組件 MySQL  db:    image: mysql:5.7.17    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --init-connect='SET NAMES utf8mb4;'    ports:     - "3636:3306"    volumes:     - /var/mysqldb:/var/lib/mysql     - /docker/mysql/my.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf    restart: always    environment:      MYSQL_ROOT_PASSWORD: password  #消費者服務1 admin  admin:    image: "admin:2.3.1"    ports:     - "7575:8080"    depends_on:     - zookeeper    restart: always    environment:      zookeeper.host: zookeeper://zookeeper:2181  #提供者服務1 system  system:    image: "system:2.3.1"    depends_on:     - db     - zookeeper    restart: always    environment:      zookeeper.host: zookeeper://zookeeper:2181      mysql.address: db:3306

    看到了嗎?IP 位址直接由 服務名 指定就可以了。另外, Docker 中設定的環境變數,竟然能被 applicationContext.xml 中讀取,我也是蠻詫異的!(在代碼和 Docker 中都配置了mysql.address 的話,以 Docker 中設定的生效)。

    然後 docker-compose up -d 啟動微服務項目就可以了~~

    容器部署的一個原則:盡量不要在容器內部做檔案的修改,要修改的內容用資料卷的方式映射到宿主機上,比如上面的MySQL設定檔和資料倉儲。

    在 Docker 上部署 MySQL 遇到了幾個問題,簡單羅列下:

1、Navicat 串連的時候: Client does not support authentication protocol requested by server ?

解決:進入 MySQL 容器,運行

ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';

2、Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggre 的問題?

原因:MySQL 5.7.5及以上功能依賴檢測功能。如果啟用了ONLY_FULL_GROUP_BY SQL模式(預設情況下),MySQL將拒絕挑選清單,HAVING條件或ORDER BY列表的查詢引用在GROUP BY子句中既未命名的非集合列,也不在功能上依賴於它們。

解決:在MySQL的設定檔中加上:

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION

3、MySQL 串連參數useSSL=true 和 useSSL=false 的區別?

    建議不要在沒有伺服器身分識別驗證的情況下建立SSL串連(同一個 Docker-compose 中是內網環境)。根據 MySQL 5.5.45 +,5.6.26 +和5.7.6+ 要求如果未設定顯式選項,則必須預設建立SSL串連。為了符合不使用SSL的現有應用程式。您需要通過設定useSSL = false顯式禁用SSL,或者設定useSSL = true並為伺服器憑證驗證提供信任庫。

四、結語

    總算是把一個微服務項目部署運行起來了,幾乎是用了最少的 Docker-compose 模板檔案,所以還是有很多地方可以完善的,比如說 MySQL 密碼沒有加密處理、服務沒有做健全狀態檢查、叢集方面還沒怎麼考慮(用 Docker Swarm 實現)等等......路漫漫其修遠兮,吾將上下而求索。共勉!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.