Docker 安装 MongoDB

MongoDB 是当下最流行的 NoSQL 数据库之一,Docker 安装 apache 的方式有两种

如果你是 Docker 初学者,如果你以后长期使用 mongodb ,我们建议你两种方法都试一试,为什么呢?

原因很简单,第一种方法,简单快捷,第二种方法,就是尝试自己编译安装

1. docker pull mongodb

如果想以最简单的方式安装 MongoDB, 可以直接使用 docker pull mongo 命令

流程如下

  1. 查找 Docker Hub 上的 mongodb 镜像

    [root@localhost ~]# docker search mongodb
    NAME    ...    OFFICIAL  ...
    mongo   ...    [OK]      ...
    mongo-express  [OK]      ...
    

    列表很多,我们推荐你使用 OFFICIAL = OK 的那两条

    1. mongo 是官方数据库
    2. mongo-express 是官方使用 Node.js 开发的 MongoDB 管理后台
  2. 拉取官方最新的 MongoDB 镜像

    [root@localhost ~]# docker pull mongo
    Using default tag: latest
    ...
    
  3. 稍等片刻,下载完成后就可以在本地镜像列表里看到 mongo 的镜像了

    [root@localhost apache]# docker images mongodb
    REPOSITORY  TAG       IMAGE ID        CREATED        SIZE
    mongo       latest    f93ff881751f    5 days ago     367.6 MB
    

2. 通过 Dockerfile 文件构建

如果你是运维工程师,我们推荐你使用 Dockerfile 文件构建 mongodb 镜像

因为可以加深对安装 mongodb 的理解

现在,我们一步一步使用 Dockerfile 文件构建 mongodb 镜像吧

  1. 创建目录 mongo 用于存放后面的相关东西

    [root@localhost ~]#  mkdir -p  ~/mongo/db
    
    目录 说明
    mongo 该目录存放 Dockerfile 文件和 db 数据库目录
    mongo/db 该目录将映射为 mongodb 容器的数据库目录
  2. 进入创建的 mongo 目录,创建 docker-entrypoint.sh 脚本用于管理 MongoDB 数据库

    [root@localhost ~]# cd mongo
    [root@localhost mongo] touch docker-entrypoint.sh
    [root@localhost mongo] chmod +x docker-entrypoint.sh
    [root@localhost mongo] vi docker-entrypoint.sh
    

    然后输入以下内容

    # !/bin/bash
    # 注意去掉上一行 # ! 之间的空格
    set -Eeuo pipefail
    
    if [ "${1:0:1}" = '-' ]; then
        set -- mongod "$@"
    fi
    
    originalArgOne="$1"
    
    # allow the container to be started with `--user`
    # all mongo* commands should be dropped to the correct user
    if [[ "$originalArgOne" == mongo* ]] && [ "$(id -u)" = '0' ]; then
        if [ "$originalArgOne" = 'mongod' ]; then
            chown -R mongodb /data/configdb /data/db
        fi
    
        # make sure we can write to stdout and stderr as "mongodb"
        # (for our "initdb" code later; see "--logpath" below)
        chown --dereference mongodb "/proc/$$/fd/1" "/proc/$$/fd/2" || :
        # ignore errors thanks to https://github.com/docker-library/mongo/issues/149
    
        exec gosu mongodb "$BASH_SOURCE" "$@"
    fi
    
    # you should use numactl to start your mongod instances, including the config servers, mongos instances, and any clients.
    # https://docs.mongodb.com/manual/administration/production-notes/#configuring-numa-on-linux
    if [[ "$originalArgOne" == mongo* ]]; then
        numa='numactl --interleave=all'
        if $numa true &> /dev/null; then
            set -- $numa "$@"
        fi
    fi
    
    # usage: file_env VAR [DEFAULT]
    #    ie: file_env 'XYZ_DB_PASSWORD' 'example'
    # (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
    #  "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
    file_env() {
        local var="$1"
        local fileVar="${var}_FILE"
        local def="${2:-}"
        if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
            echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
            exit 1
        fi
        local val="$def"
        if [ "${!var:-}" ]; then
            val="${!var}"
        elif [ "${!fileVar:-}" ]; then
            val="$(< "${!fileVar}")"
        fi
        export "$var"="$val"
        unset "$fileVar"
    }
    
    # see https://github.com/docker-library/mongo/issues/147 (mongod is picky about duplicated arguments)
    _mongod_hack_have_arg() {
        local checkArg="$1"; shift
        local arg
        for arg; do
            case "$arg" in
                "$checkArg"|"$checkArg"=*)
                    return 0
                    ;;
            esac
        done
        return 1
    }
    # _mongod_hack_get_arg_val '--some-arg' "$@"
    _mongod_hack_get_arg_val() {
        local checkArg="$1"; shift
        while [ "$#" -gt 0 ]; do
            local arg="$1"; shift
            case "$arg" in
                "$checkArg")
                    echo "$1"
                    return 0
                    ;;
                "$checkArg"=*)
                    echo "${arg#$checkArg=}"
                    return 0
                    ;;
            esac
        done
        return 1
    }
    declare -a mongodHackedArgs
    # _mongod_hack_ensure_arg '--some-arg' "$@"
    # set -- "${mongodHackedArgs[@]}"
    _mongod_hack_ensure_arg() {
        local ensureArg="$1"; shift
        mongodHackedArgs=( "$@" )
        if ! _mongod_hack_have_arg "$ensureArg" "$@"; then
            mongodHackedArgs+=( "$ensureArg" )
        fi
    }
    # _mongod_hack_ensure_no_arg '--some-unwanted-arg' "$@"
    # set -- "${mongodHackedArgs[@]}"
    _mongod_hack_ensure_no_arg() {
        local ensureNoArg="$1"; shift
        mongodHackedArgs=()
        while [ "$#" -gt 0 ]; do
            local arg="$1"; shift
            if [ "$arg" = "$ensureNoArg" ]; then
                continue
            fi
            mongodHackedArgs+=( "$arg" )
        done
    }
    # _mongod_hack_ensure_no_arg '--some-unwanted-arg' "$@"
    # set -- "${mongodHackedArgs[@]}"
    _mongod_hack_ensure_no_arg_val() {
        local ensureNoArg="$1"; shift
        mongodHackedArgs=()
        while [ "$#" -gt 0 ]; do
            local arg="$1"; shift
            case "$arg" in
                "$ensureNoArg")
                    shift # also skip the value
                    continue
                    ;;
                "$ensureNoArg"=*)
                    # value is already included
                    continue
                    ;;
            esac
            mongodHackedArgs+=( "$arg" )
        done
    }
    # _mongod_hack_ensure_arg_val '--some-arg' 'some-val' "$@"
    # set -- "${mongodHackedArgs[@]}"
    _mongod_hack_ensure_arg_val() {
        local ensureArg="$1"; shift
        local ensureVal="$1"; shift
        _mongod_hack_ensure_no_arg_val "$ensureArg" "$@"
        mongodHackedArgs+=( "$ensureArg" "$ensureVal" )
    }
    
    # _js_escape 'some "string" value'
    _js_escape() {
        jq --null-input --arg 'str' "$1" '$str'
    }
    
    jsonConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-config.json"
    tempConfigFile="${TMPDIR:-/tmp}/docker-entrypoint-temp-config.json"
    _parse_config() {
        if [ -s "$tempConfigFile" ]; then
            return 0
        fi
    
        local configPath
        if configPath="$(_mongod_hack_get_arg_val --config "$@")"; then
            # if --config is specified, parse it into a JSON file so we can remove a few problematic keys (especially SSL-related keys)
            # see https://docs.mongodb.com/manual/reference/configuration-options/
            mongo --norc --nodb --quiet --eval "load('/js-yaml.js'); printjson(jsyaml.load(cat($(_js_escape "$configPath"))))" > "$jsonConfigFile"
            jq 'del(.systemLog, .processManagement, .net, .security)' "$jsonConfigFile" > "$tempConfigFile"
            return 0
        fi
    
        return 1
    }
    dbPath=
    _dbPath() {
        if [ -n "$dbPath" ]; then
            echo "$dbPath"
            return
        fi
    
        if ! dbPath="$(_mongod_hack_get_arg_val --dbpath "$@")"; then
            if _parse_config "$@"; then
                dbPath="$(jq '.storage.dbPath' "$jsonConfigFile")"
            fi
        fi
    
        : "${dbPath:=/data/db}"
    
        echo "$dbPath"
    }
    
    if [ "$originalArgOne" = 'mongod' ]; then
        file_env 'MONGO_INITDB_ROOT_USERNAME'
        file_env 'MONGO_INITDB_ROOT_PASSWORD'
        # pre-check a few factors to see if it's even worth bothering with initdb
        shouldPerformInitdb=
        if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
            # if we have a username/password, let's set "--auth"
            _mongod_hack_ensure_arg '--auth' "$@"
            set -- "${mongodHackedArgs[@]}"
            shouldPerformInitdb='true'
        elif [ "$MONGO_INITDB_ROOT_USERNAME" ] || [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
            cat >&2 <<-'EOF'
    
                error: missing 'MONGO_INITDB_ROOT_USERNAME' or 'MONGO_INITDB_ROOT_PASSWORD'
                       both must be specified for a user to be created
    
            EOF
            exit 1
        fi
    
        if [ -z "$shouldPerformInitdb" ]; then
            # if we've got any /docker-entrypoint-initdb.d/* files to parse later, we should initdb
            for f in /docker-entrypoint-initdb.d/*; do
                case "$f" in
                    *.sh|*.js) # this should match the set of files we check for below
                        shouldPerformInitdb="$f"
                        break
                        ;;
                esac
            done
        fi
    
        # check for a few known paths (to determine whether we've already initialized and should thus skip our initdb scripts)
        if [ -n "$shouldPerformInitdb" ]; then
            dbPath="$(_dbPath "$@")"
            for path in \
                "$dbPath/WiredTiger" \
                "$dbPath/journal" \
                "$dbPath/local.0" \
                "$dbPath/storage.bson" \
            ; do
                if [ -e "$path" ]; then
                    shouldPerformInitdb=
                    break
                fi
            done
        fi
    
        if [ -n "$shouldPerformInitdb" ]; then
            mongodHackedArgs=( "$@" )
            if _parse_config "$@"; then
                _mongod_hack_ensure_arg_val --config "$tempConfigFile" "${mongodHackedArgs[@]}"
            fi
            _mongod_hack_ensure_arg_val --bind_ip 127.0.0.1 "${mongodHackedArgs[@]}"
            _mongod_hack_ensure_arg_val --port 27017 "${mongodHackedArgs[@]}"
            _mongod_hack_ensure_no_arg --bind_ip_all "${mongodHackedArgs[@]}"
    
            # remove "--auth" and "--replSet" for our initial startup (see https://docs.mongodb.com/manual/tutorial/enable-authentication/#start-mongodb-without-access-control)
            # https://github.com/docker-library/mongo/issues/211
            _mongod_hack_ensure_no_arg --auth "${mongodHackedArgs[@]}"
            if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
                _mongod_hack_ensure_no_arg_val --replSet "${mongodHackedArgs[@]}"
            fi
    
            sslMode="$(_mongod_hack_have_arg '--sslPEMKeyFile' "$@" && echo 'allowSSL' || echo 'disabled')" # "BadValue: need sslPEMKeyFile when SSL is enabled" vs "BadValue: need to enable SSL via the sslMode flag when using SSL configuration parameters"
            _mongod_hack_ensure_arg_val --sslMode "$sslMode" "${mongodHackedArgs[@]}"
    
            if stat "/proc/$$/fd/1" > /dev/null && [ -w "/proc/$$/fd/1" ]; then
                # https://github.com/mongodb/mongo/blob/38c0eb538d0fd390c6cb9ce9ae9894153f6e8ef5/src/mongo/db/initialize_server_global_state.cpp#L237-L251
                # https://github.com/docker-library/mongo/issues/164#issuecomment-293965668
                _mongod_hack_ensure_arg_val --logpath "/proc/$$/fd/1" "${mongodHackedArgs[@]}"
            else
                initdbLogPath="$(_dbPath "$@")/docker-initdb.log"
                echo >&2 "warning: initdb logs cannot write to '/proc/$$/fd/1', so they are in '$initdbLogPath' instead"
                _mongod_hack_ensure_arg_val --logpath "$initdbLogPath" "${mongodHackedArgs[@]}"
            fi
            _mongod_hack_ensure_arg --logappend "${mongodHackedArgs[@]}"
    
            pidfile="${TMPDIR:-/tmp}/docker-entrypoint-temp-mongod.pid"
            rm -f "$pidfile"
            _mongod_hack_ensure_arg_val --pidfilepath "$pidfile" "${mongodHackedArgs[@]}"
    
            "${mongodHackedArgs[@]}" --fork
    
            mongo=( mongo --host 127.0.0.1 --port 27017 --quiet )
    
            # check to see that our "mongod" actually did start up (catches "--help", "--version", MongoDB 3.2 being silly, slow prealloc, etc)
            # https://jira.mongodb.org/browse/SERVER-16292
            tries=30
            while true; do
                if ! { [ -s "$pidfile" ] && ps "$(< "$pidfile")" &> /dev/null; }; then
                    # bail ASAP if "mongod" isn't even running
                    echo >&2
                    echo >&2 "error: $originalArgOne does not appear to have stayed running -- perhaps it had an error?"
                    echo >&2
                    exit 1
                fi
                if "${mongo[@]}" 'admin' --eval 'quit(0)' &> /dev/null; then
                    # success!
                    break
                fi
                (( tries-- ))
                if [ "$tries" -le 0 ]; then
                    echo >&2
                    echo >&2 "error: $originalArgOne does not appear to have accepted connections quickly enough -- perhaps it had an error?"
                    echo >&2
                    exit 1
                fi
                sleep 1
            done
    
            if [ "$MONGO_INITDB_ROOT_USERNAME" ] && [ "$MONGO_INITDB_ROOT_PASSWORD" ]; then
                rootAuthDatabase='admin'
    
                "${mongo[@]}" "$rootAuthDatabase" <<-EOJS
                    db.createUser({
                        user: $(_js_escape "$MONGO_INITDB_ROOT_USERNAME"),
                        pwd: $(_js_escape "$MONGO_INITDB_ROOT_PASSWORD"),
                        roles: [ { role: 'root', db: $(_js_escape "$rootAuthDatabase") } ]
                    })
                EOJS
            fi
    
            export MONGO_INITDB_DATABASE="${MONGO_INITDB_DATABASE:-test}"
    
            echo
            for f in /docker-entrypoint-initdb.d/*; do
                case "$f" in
                    *.sh) echo "$0: running $f"; . "$f" ;;
                    *.js) echo "$0: running $f"; "${mongo[@]}" "$MONGO_INITDB_DATABASE" "$f"; echo ;;
                    *)    echo "$0: ignoring $f" ;;
                esac
                echo
            done
    
            "$@" --pidfilepath="$pidfile" --shutdown
            rm -f "$pidfile"
    
            echo
            echo 'MongoDB init process complete; ready for start up.'
            echo
        fi
    
        # MongoDB 3.6+ defaults to localhost-only binding
        haveBindIp=
        if _mongod_hack_have_arg --bind_ip "$@" || _mongod_hack_have_arg --bind_ip_all "$@"; then
            haveBindIp=1
        elif _parse_config "$@" && jq --exit-status '.net.bindIp // .net.bindIpAll' "$jsonConfigFile" > /dev/null; then
            haveBindIp=1
        fi
        if [ -z "$haveBindIp" ]; then
            # so if no "--bind_ip" is specified, let's add "--bind_ip_all"
            set -- "$@" --bind_ip_all
        fi
    
        unset "${!MONGO_INITDB_@}"
    fi
    
    rm -f "$jsonConfigFile" "$tempConfigFile"
    
    exec "$@"
    
  3. mongo 目录下创建 Dockerfile

    [root@localhost mongo]# vi Dockerfile
    

    然后输入以下内容

    FROM debian:jessie-slim
    
    # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
    RUN groupadd -r mongodb && useradd -r -g mongodb mongodb
    
    RUN apt-get update \
        && apt-get install -y --no-install-recommends \
            ca-certificates \
            jq \
            numactl \
        && rm -rf /var/lib/apt/lists/*
    
    # grab gosu for easy step-down from root (https://github.com/tianon/gosu/releases)
    ENV GOSU_VERSION 1.10
    # grab "js-yaml" for parsing mongod's YAML config files (https://github.com/nodeca/js-yaml/releases)
    ENV JSYAML_VERSION 3.10.0
    
    RUN set -ex; \
        \
        apt-get update; \
        apt-get install -y --no-install-recommends \
            wget \
        ; \
        rm -rf /var/lib/apt/lists/*; \
        \
        dpkgArch="$(dpkg --print-architecture | awk -F- '{ print $NF }')"; \
        wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch"; \
        wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$dpkgArch.asc"; \
        export GNUPGHOME="$(mktemp -d)"; \
        gpg --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4; \
        gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu; \
        rm -r "$GNUPGHOME" /usr/local/bin/gosu.asc; \
        chmod +x /usr/local/bin/gosu; \
        gosu nobody true; \
        \
        wget -O /js-yaml.js "https://github.com/nodeca/js-yaml/raw/${JSYAML_VERSION}/dist/js-yaml.js"; \
    # TODO some sort of download verification here
        \
        apt-get purge -y --auto-remove wget
    
    RUN mkdir /docker-entrypoint-initdb.d
    
    ENV GPG_KEYS \
    # pub   rsa4096 2017-11-15 [SC] [expires: 2019-11-15]
    #       BD8C 80D9 C729 D005 24E0  68E0 3DAB 7171 3396 F72B
    # uid           [ unknown] MongoDB 3.8 Release Signing Key <packaging@mongodb.com>
        BD8C80D9C729D00524E068E03DAB71713396F72B
    # https://docs.mongodb.com/manual/tutorial/verify-mongodb-packages/#download-then-import-the-key-file
    RUN set -ex; \
        export GNUPGHOME="$(mktemp -d)"; \
        for key in $GPG_KEYS; do \
            gpg --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
        done; \
        gpg --export $GPG_KEYS > /etc/apt/trusted.gpg.d/mongodb.gpg; \
        rm -r "$GNUPGHOME"; \
        apt-key list
    
    # Allow build-time overrides (eg. to build image with MongoDB Enterprise version)
    # Options for MONGO_PACKAGE: mongodb-org OR mongodb-enterprise
    # Options for MONGO_REPO: repo.mongodb.org OR repo.mongodb.com
    # Example: docker build --build-arg MONGO_PACKAGE=mongodb-enterprise --build-arg MONGO_REPO=repo.mongodb.com .
    ARG MONGO_PACKAGE=mongodb-org-unstable
    ARG MONGO_REPO=repo.mongodb.org
    ENV MONGO_PACKAGE=${MONGO_PACKAGE} MONGO_REPO=${MONGO_REPO}
    
    ENV MONGO_MAJOR 3.7
    ENV MONGO_VERSION 3.7.9
    
    RUN echo "deb http://$MONGO_REPO/apt/debian jessie/${MONGO_PACKAGE%-unstable}/$MONGO_MAJOR main" | tee "/etc/apt/sources.list.d/${MONGO_PACKAGE%-unstable}.list"
    
    RUN set -x \
        && apt-get update \
        && apt-get install -y \
            ${MONGO_PACKAGE}=$MONGO_VERSION \
            ${MONGO_PACKAGE}-server=$MONGO_VERSION \
            ${MONGO_PACKAGE}-shell=$MONGO_VERSION \
            ${MONGO_PACKAGE}-mongos=$MONGO_VERSION \
            ${MONGO_PACKAGE}-tools=$MONGO_VERSION \
        && rm -rf /var/lib/apt/lists/* \
        && rm -rf /var/lib/mongodb \
        && mv /etc/mongod.conf /etc/mongod.conf.orig
    
    RUN mkdir -p /data/db /data/configdb \
        && chown -R mongodb:mongodb /data/db /data/configdb
    VOLUME /data/db /data/configdb
    
    COPY docker-entrypoint.sh /usr/local/bin/
    ENTRYPOINT ["docker-entrypoint.sh"]
    
    EXPOSE 27017
    CMD ["mongod"]
    

    这些内容,基本都是官方给的 Dockerfile

  4. 通过 Dockerfile 创建一个镜像 ms-mongodb,你可以替换成你自己的名字

    [root@localhost mongo]#  docker build -t ms-mongo:3.7 .
    
  5. 稍等片刻,创建完成后,可以在本地的镜像列表里查找到刚刚创建的镜像

    [root@localhost mongo]# docker images  mongo:3.7
    REPOSITORY   TAG   IMAGE ID      CREATED     SIZE
    ms-mongo     3.7  f0f3e4beccef  11 days ago 323 MB
    

    这次自己编译的竟然比官方镜像小....

GitHub 下载加速

从 GitHub 下载文件非常慢,因为下载链接指向了 Amazon 的服务器
下载地址是 http://github-cloud.s3.amazonaws.com/,从国内访问 Amazon 非常慢,所以总是下载失败,解决方法时更改 hosts 文件,使该域名指向香港的服务器

219.76.4.4 github-cloud.s3.amazonaws.com

运行 ms-mongo:3.7 容器

可以使用下面的命令运行我们刚刚创建的 ms-mongo:3.7 镜像

[root@localhost mongo]# docker run -d -p 27017:27017 -v $PWD/db:/data/db ms-mongo:3.7
dbe5d334e0063332633fe78fcc031c26851cbb1c5d589d50414e7bd37471671d

参数说明:

  1. -p 27017:27017

    将容器的 27017 端口映射到主机的 27017 端口

  2. -v $PWD/db:/data/db

    将主机中当前目录下的 db 挂载到容器的 /data/db,作为 mongo 数据存储目录

查看容器启动情况

[root@localhost mongo]# docker ps -a 
CONTAINER ID  IMAGE          COMMAND                  ...
dbe5d334e006  ms-mongo:3.7   "docker-entrypoint.sh"   ...

查看容器的 IP

可以使用命令 docker inspect 查看容器的 IP

docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container_id>

例如可以使用下面的命令查看刚刚创建的容器的 IP

[root@localhost mongo] docker inspect --format '{{ .NetworkSettings.IPAddress }}' dbe5d334e006
172.17.0.2

连接到 mongo 镜像

可以使用 mongo 镜像执行 mongo 命令连接到刚启动的容器, 主机 IP172.17.0.2

[root@localhost mongo]# docker run -it ms-mongo:3.7 mongo --host 172.17.0.2
MongoDB shell version v3.6.5
connecting to: mongodb://172.17.0.2:27017/
MongoDB server version: 3.6.5
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
    http://docs.mongodb.org/
Questions? Try the support group
    http://groups.google.com/group/mongodb-user
2018-05-18T14:01:20.078+0000 I STORAGE  [main] In File::open(), ::open for '/home/mongodb/.mongorc.js' failed with No such file or directory
Server has startup warnings: 
2018-05-18T13:59:12.821+0000 I CONTROL  [initandlisten] 
2018-05-18T13:59:12.821+0000 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2018-05-18T13:59:12.821+0000 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2018-05-18T13:59:12.821+0000 I CONTROL  [initandlisten]
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.