diff --git a/README.md b/README.md index 0d7124f..e3e825e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- + logo

diff --git a/assets/docker.xmind b/assets/docker.xmind new file mode 100644 index 0000000..293f1ff Binary files /dev/null and b/assets/docker.xmind differ diff --git a/assets/linux.xmind b/assets/linux.xmind new file mode 100644 index 0000000..291a618 Binary files /dev/null and b/assets/linux.xmind differ diff --git a/codes/docker/docker-compose-demo/Dockerfile b/codes/docker/docker-compose-demo/Dockerfile new file mode 100644 index 0000000..7c4b8c7 --- /dev/null +++ b/codes/docker/docker-compose-demo/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.6-alpine +ADD . /code +WORKDIR /code +RUN pip install redis flask +CMD ["python", "app.py"] diff --git a/codes/docker/docker-compose-demo/app.py b/codes/docker/docker-compose-demo/app.py new file mode 100644 index 0000000..f0dfc20 --- /dev/null +++ b/codes/docker/docker-compose-demo/app.py @@ -0,0 +1,13 @@ +from flask import Flask +from redis import Redis + +app = Flask(__name__) +redis = Redis(host='redis', port=6379) + +@app.route('/') +def hello(): + count = redis.incr('hits') + return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/codes/docker/docker-compose-demo/docker-compose.yml b/codes/docker/docker-compose-demo/docker-compose.yml new file mode 100644 index 0000000..80d8ced --- /dev/null +++ b/codes/docker/docker-compose-demo/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + + web: + build: . + ports: + - "5000:5000" + + redis: + image: "redis:alpine" diff --git a/codes/docker/docker-compose-demo/run.sh b/codes/docker/docker-compose-demo/run.sh new file mode 100644 index 0000000..0913e24 --- /dev/null +++ b/codes/docker/docker-compose-demo/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash + +set -x +docker-compose up diff --git a/codes/linux/lib/env.sh b/codes/linux/lib/env.sh index 4c41818..9176eec 100644 --- a/codes/linux/lib/env.sh +++ b/codes/linux/lib/env.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +# ------------------------------------------------------------------------------ +# 常用变量库 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + # ------------------------------------------------------------------------------ 颜色状态 # Regular Color @@ -51,3 +56,48 @@ YES=0 NO=1 SUCCEED=0 FAILED=1 + +# 显示打印日志的时间 +DATE=`date "+%Y-%m-%d %H:%M:%S"` +# 那个用户在操作 +USER=$(whoami) + +# ------------------------------------------------------------------------------ log + +logInfo() { + #($0脚本本身,$@将参数作为整体传输调用) + echo "[${DATE}] [${USER}] [INFO] [$0] [$@] execute succeed." >> /var/log/shell.log +} + +logWarn() { + #($0脚本本身,$@将参数作为整体传输调用) + echo "[${DATE}] [${USER}] [WARN] [$0] [$@] execute succeed." >> /var/log/shell.log +} + +logError() { + #($0脚本本身,$@将参数作为整体传输调用) + echo "[${DATE}] [${USER}] [ERROR] [$0] [$@] execute failed." >> /var/log/shell.log +} + +printInfo() { + echo -e "${C_B_GREEN}[INFO] $@${C_RESET}" +} + +printWarn() { + echo -e "${C_B_YELLOW}[WARN] $@${C_RESET}" +} + +printError() { + echo -e "${C_B_RED}[ERROR] $@${C_RESET}" +} + +callAndLog () { + $* + if [[ $? -eq ${SUCCEED} ]]; then + logInfo "$@ succeed" + echo -e "${C_B_GREEN}[INFO] [$0] [$@] execute succeed.${C_RESET}" + else + logError "$@ failed" + echo -e "${C_B_RED}[ERROR] [$0] [$@] execute failed.${C_RESET}" + fi +} diff --git a/codes/linux/lib/git.sh b/codes/linux/lib/git.sh index 6e33ca7..1a1b422 100644 --- a/codes/linux/lib/git.sh +++ b/codes/linux/lib/git.sh @@ -1,5 +1,10 @@ #!/usr/bin/env bash +# ------------------------------------------------------------------------------ +# Git 基本操作脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + # 装载其它库 ROOT=`dirname ${BASH_SOURCE[0]}` source ${ROOT}/env.sh @@ -33,7 +38,7 @@ checkGit() { return ${NO} fi - printf "${C_B_B_YELLOW}${source} is invalid dir.${C_RESET}\n" + printf "${C_B_YELLOW}${source} is invalid dir.${C_RESET}\n" return ${NO} } @@ -49,18 +54,18 @@ cloneOrPullGit() { local root=$5 if [[ ! ${repository} ]] || [[ ! ${group} ]] || [[ ! ${project} ]] || [[ ! ${branch} ]] || [[ ! ${root} ]]; then - printf "${C_B_YELLOW}>>>> Please input root, group, project, branch.${C_RESET}\n" + printf "${C_B_YELLOW}Please input root, group, project, branch.${C_RESET}\n" return ${FAILED} fi if [[ ! -d "${root}" ]]; then - printf "${C_B_YELLOW}>>>> ${root} is not directory.${C_RESET}\n" + printf "${C_B_YELLOW}${root} is not directory.${C_RESET}\n" return ${FAILED} fi local source=${root}/${group}/${project} - printf "${C_B_CYAN}>>>> project directory is ${source}.${C_RESET}\n" - printf "${C_B_CYAN}>>>> git url is ${repository}:${group}/${project}.git.${C_RESET}\n" + printf "${C_B_MAGENTA}project directory is ${source}.${C_RESET}\n" + printf "${C_B_MAGENTA}git url is ${repository}:${group}/${project}.git.${C_RESET}\n" mkdir -p ${root}/${group} checkGit ${source} @@ -73,21 +78,21 @@ cloneOrPullGit() { printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git checkout ${branch} succeed.${C_RESET}\n" + printf "${C_B_GREEN}git checkout ${branch} succeed.${C_RESET}\n" git reset --hard if [[ "${SUCCEED}" != "$?" ]]; then printf "${C_B_RED}<<<< git reset --hard failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git reset --hard succeed.${C_RESET}\n" + printf "${C_B_GREEN}git reset --hard succeed.${C_RESET}\n" git pull if [[ "${SUCCEED}" != "$?" ]]; then printf "${C_B_RED}<<<< git pull failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git pull succeed.${C_RESET}\n" + printf "${C_B_GREEN}git pull succeed.${C_RESET}\n" else # 如果 ${source} 不是 git 项目,执行 clone 操作 @@ -96,7 +101,7 @@ cloneOrPullGit() { printf "${C_B_RED}<<<< git clone ${project} failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git clone ${project} succeed.${C_RESET}\n" + printf "${C_B_GREEN}git clone ${project} succeed.${C_RESET}\n" cd ${source} || return ${FAILED} @@ -105,8 +110,9 @@ cloneOrPullGit() { printf "${C_B_RED}<<<< git checkout ${branch} failed.${C_RESET}\n" return ${FAILED} fi - printf "${C_B_GREEN}>>>> git checkout ${branch} succeed.${C_RESET}\n" + printf "${C_B_GREEN}git checkout ${branch} succeed.${C_RESET}\n" fi + printf "${C_B_GREEN}Clone or pull git project [$2/$3:$4] succeed.${C_RESET}\n" return ${SUCCEED} } diff --git a/codes/linux/lib/java.sh b/codes/linux/lib/java.sh new file mode 100644 index 0000000..0d66f22 --- /dev/null +++ b/codes/linux/lib/java.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# Java 应用运维脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# ------------------------------------------------------------------------------ env preparation +# load libs +CURRENT_PATH=`dirname ${BASH_SOURCE[0]}` +source ${CURRENT_PATH}/env.sh + +# ------------------------------------------------------------------------------ functions + +stopServer() { + if [[ ! $1 ]]; then + printError "please input java app name" + return ${FAILED} + fi + + local javaAppName=$1 + local pid=`jps | grep ${javaAppName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + kill -9 ${pid} + if [[ $? -eq ${SUCCEED} ]]; then + printInfo "stop ${javaAppName} succeed" + return ${SUCCEED} + else + printError "stop ${javaAppName} failed" + return ${FAILED} + fi + else + printWarn "${javaAppName} is not running" + return ${SUCCEED} + fi +} + +startServer() { + if [[ ! $1 ]]; then + printError "please input java app name" + return ${FAILED} + fi + + # >>>> 1. check java app is started or not + # >>>> 1.1. exit script if the app is started + local javaAppName=$1 + local pid=`jps | grep ${javaAppName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + printInfo "${javaAppName} is started, PID: ${pid}" + return ${SUCCEED} + fi + + # >>>> 2. package options + # GC OPTS + local javaOptions="-server -Xms1g -Xmx2g -Xss256k" + javaOptions="${javaOptions} -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:NewRatio=4" + + # GC LOG OPTS + javaOptions="${javaOptions} -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps" + javaOptions="${javaOptions} -verbose:gc -Xloggc:${LOG_PATH}/${javaAppName}.gc.log" + javaOptions="${javaOptions} -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M" + + # Heap Dump OPTS + javaOptions="${javaOptions} -XX:-OmitStackTraceInFastThrow -XX:+HeapDumpOnOutOfMemoryError" + javaOptions="${javaOptions} -XX:HeapDumpPath=${LOG_PATH}/${javaAppName}.heapdump.hprof" + + # APP OPTS + javaOptions="${javaOptions} -Dsun.net.inetaddr.ttl=60 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8" + if [[ ${PROFILE} ]]; then + javaOptions="${javaOptions} -Dspring.profiles.active=${PROFILE}" + fi + + # DEBUG OPTS + if [[ "${DEBUG}" == "on" ]]; then + # JMX OPTS + local ip=$(ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/') + local jmxPort=$(expr 10000 + ${PORT}) + javaOptions="${javaOptions} -Dcom.sun.management.jmxremote=true" + javaOptions="${javaOptions} -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false" + javaOptions="${javaOptions} -Djava.rmi.server.hostname=${ip} -Dcom.sun.management.jmxremote.port=${jmxPort}" + + # Remote Debug + local debugPort=$(expr 20000 + ${PORT}) + javaOptions="${javaOptions} -Xdebug -Xnoagent -Djava.compiler=NONE" + javaOptions="${javaOptions} -Xrunjdwp:transport=dt_socket,address=${debugPort},server=y,suspend=n" + fi + + # CLASSPATH + local appOptions="-classpath ${ROOT_PATH}/lib/* -Dlogging.config=file:${ROOT_PATH}/config/logback.dev.xml" + appOptions="${appOptions} --spring.config.location=classpath:/,classpath:/config/,file:${ROOT_PATH},file:${ROOT_PATH}/config/" + if [[ ${PORT} ]]; then + appOptions="${appOptions} --server.port=${PORT}" + fi + + # >>>> 3. create log dir and console log file + mkdir -p ${LOG_PATH} + if [[ ! -f ${CONSOLE_LOG} ]]; then + touch ${CONSOLE_LOG} + fi + + # >>>> 4. start java app + printInfo "starting ${javaAppName}, execute cli: " + printInfo "nohup java ${javaOptions} -jar ${ROOT_PATH}/${javaAppName}.jar ${appOptions} >> ${CONSOLE_LOG} 2>&1 &" + nohup java ${javaOptions} -jar ${ROOT_PATH}/${javaAppName}.jar ${appOptions} >> ${CONSOLE_LOG} 2>&1 & + + # >>>> 5. check java app is started or not + local pid=`jps | grep ${javaAppName} | awk '{print $1}'` + if [[ -n "${pid}" ]]; then + printInfo "start ${javaAppName} succeed, PID: ${pid}" + return ${SUCCEED} + else + printError "start ${javaAppName} failed" + return ${FAILED} + fi +} + +# ------------------------------------------------------------------------------ main +export LANG="zh_CN.UTF-8" +ROOT_PATH=$(cd ${CURRENT_PATH}/..; pwd) + +APP_NAME=java-app +LOG_PATH=/var/log/myapp +CONSOLE_LOG=${LOG_PATH}/${APP_NAME}.console.log + +PORT=8888 +PROFILE=dev +DEBUG=off + +startServer ${APP_NAME} +#stopServer ${APP_NAME} +if [[ $? -eq ${SUCCEED} ]]; then + exit ${SUCCEED} +else + exit ${FAILED} +fi diff --git a/codes/linux/lib/maven.sh b/codes/linux/lib/maven.sh new file mode 100644 index 0000000..64fc15f --- /dev/null +++ b/codes/linux/lib/maven.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +# ------------------------------------------------------------------------------ +# maven 项目操作脚本 +# @author Zhang Peng +# ------------------------------------------------------------------------------ + +# 装载其它库 +ROOT=`dirname ${BASH_SOURCE[0]}` +source ${ROOT}/env.sh + +mavenBuild() { + local source=$1 + mavenCheck $1 + if [[ "${SUCCEED}" != "$?" ]]; then + return ${FAILED} + fi + + if [[ -d "${source}" ]]; then + cd ${source} + if [[ -f "${source}/settings.xml" ]]; then + callAndLog "mvn clean install -B -U -s ${source}/settings.xml -Dmaven.test.skip=true" + else + callAndLog "mvn clean install -DskipTests=true -B -U" + fi + cd - + return ${SUCCEED} + else + printf "${C_B_RED}please input valid maven project path.${C_RESET}\n" + return ${FAILED} + fi +} + +mavenCheck() { + local source=$1 + if [[ -d "${source}" ]]; then + cd ${source} + if [[ -f "${source}/pom.xml" ]]; then + return ${YES} + else + printf "${C_B_RED}pom.xml is not exists.${C_RESET}\n" + return ${NO} + fi + cd - + return ${YES} + else + printf "${C_B_RED}please input valid maven project path.${C_RESET}\n" + return ${NO} + fi +} + +##################################### MAIN ##################################### +printf "\n${C_B_GREEN}>>>> maven build begin.${C_RESET}\n\n" + +printf "${C_B_MAGENTA}Current path is ${ROOT}.${C_RESET}\n" + +mavenBuild ${ROOT}/.. +r1=$? + +if [[ "${r1}" == "${SUCCEED}" ]]; then + printf "\n${C_B_GREEN}<<<< maven build succeed.${C_RESET}\n\n" + exit ${SUCCEED} +else + printf "\n${C_B_RED}<<<< maven build failed.${C_RESET}\n\n" + exit ${FAILED} +fi diff --git a/codes/linux/lib/net.sh b/codes/linux/lib/net.sh new file mode 100644 index 0000000..ce8dbd3 --- /dev/null +++ b/codes/linux/lib/net.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# ---------------------------------------------------------------------------------- +# 控制台颜色 +BLACK="\033[1;30m" +RED="\033[1;31m" +GREEN="\033[1;32m" +YELLOW="\033[1;33m" +BLUE="\033[1;34m" +PURPLE="\033[1;35m" +CYAN="\033[1;36m" +RESET="$(tput sgr0)" +# ---------------------------------------------------------------------------------- + +printf "${PURPLE}" +cat << EOF +# ---------------------------------------------------------------------------------- +# XXX 脚本 +# @author: Zhang Peng +# ---------------------------------------------------------------------------------- +EOF +printf "${RESET}" + +printf "${BLUE}>>>>>>>> begin.\n${RESET}" + +printf "${GREEN}[OK]\n${RESET}" +printf "${RED}[ERROR]\n${RESET}" + +printf "${BLUE}<<<<<<<< end.\n${RESET}" + +IP=`ip addr | grep 'state UP' -A2 | tail -n1 | awk '{print $2}' | cut -f1 -d '/'` diff --git a/codes/linux/libtest/git-update.sh b/codes/linux/libtest/git-update.sh index 0c457cf..2baea11 100644 --- a/codes/linux/libtest/git-update.sh +++ b/codes/linux/libtest/git-update.sh @@ -28,10 +28,9 @@ doCloneOrPullGit ${REPOSITORY} turnon nginx-tutorial master ${ROOT} r2=$? if [[ "${r1}" == "${SUCCEED}" && "${r2}" == "${SUCCEED}" ]]; then - printf "\n${C_GREEN}Succeed.${C_RESET}\n" + printf "\n${C_B_GREEN}<<<< Init workspace Succeed.${C_RESET}\n\n" exit ${SUCCEED} else - printf "\n${C_RED}Failed.${C_RESET}\n" + printf "\n${C_B_RED}<<<< Init workspace Failed.${C_RESET}\n\n" exit ${FAILED} fi - diff --git a/codes/linux/soft/README.md b/codes/linux/soft/README.md index fcd4f70..c78aa35 100644 --- a/codes/linux/soft/README.md +++ b/codes/linux/soft/README.md @@ -13,7 +13,7 @@ - [RocketMQ 安装](#rocketmq-安装) - [Nacos 安装](#nacos-安装) - [ZooKeeper 安装](#zookeeper-安装) -- [Nginx 安装](#nginx-安装) +- [Nginx 运维](#nginx-安装) - [Fastdfs 安装](#fastdfs-安装) - [Docker 安装](#docker-安装) @@ -190,7 +190,7 @@ curl -o- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zoo wget -qO- https://gitee.com/turnon/linux-tutorial/raw/master/codes/linux/soft/zookeeper-install.sh | bash ``` -## Nginx 安装 +## Nginx 运维 **安装说明** diff --git a/codes/linux/soft/docker-install.sh b/codes/linux/soft/docker-install.sh index 549406a..fd142bc 100644 --- a/codes/linux/soft/docker-install.sh +++ b/codes/linux/soft/docker-install.sh @@ -26,23 +26,22 @@ printf "${RESET}" printf "${GREEN}>>>>>>>> install docker begin.${RESET}\n" # uninstall old version docker -sudo yum remove docker \ - docker-client \ - docker-client-latest \ - docker-common \ - docker-latest \ - docker-latest-logrotate \ - docker-logrotate \ - docker-selinux \ - docker-engine-selinux \ - docker-engine +sudo yum remove docker \ + docker-client \ + docker-client-latest \ + docker-common \ + docker-latest \ + docker-latest-logrotate \ + docker-logrotate \ + docker-engine + # install required libs sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # add docker yum repo sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo sudo yum makecache fast # install docker -sudo yum -y install docker-ce +sudo yum install docker-ce docker-ce-cli containerd.io sudo systemctl start docker docker version printf "${GREEN}<<<<<<<< install docker end.${RESET}\n" diff --git a/docs/README.md b/docs/README.md index 10b2c7b..08cfab5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,4 +1,14 @@ -# linux-tutorial +

+ + logo + +

+ +

+ license +

+ +

linux-tutorial

> 📚 **linux-tutorial** 是一个 Linux 教程。 > diff --git a/docs/book.json b/docs/book.json index 17705e6..59e70a6 100644 --- a/docs/book.json +++ b/docs/book.json @@ -58,9 +58,7 @@ }, "sharing": { "weibo": true, - "all": [ - "weibo" - ] + "all": ["weibo"] }, "tbfed-pagefooter": { "copyright": "Copyright © Zhang Peng 2017", diff --git a/docs/docker/README.md b/docs/docker/README.md index 7715d4e..bc54df6 100644 --- a/docs/docker/README.md +++ b/docs/docker/README.md @@ -8,8 +8,9 @@ - **官方** - [Docker 官网](http://www.docker.com) - - [Docker Github](https://github.com/moby/moby) - [Docker 官方文档](https://docs.docker.com/) + - [Docker Github](https://github.com/moby/moby) + - [Docker Compose Github](https://github.com/docker/compose) - [Docker Hub](https://hub.docker.com/) - [Docker 开源](https://www.docker.com/community/open-source) - **资源整理** diff --git a/docs/docker/docker-cheat-sheet.md b/docs/docker/docker-cheat-sheet.md index 49cdab3..f3ad99d 100644 --- a/docs/docker/docker-cheat-sheet.md +++ b/docs/docker/docker-cheat-sheet.md @@ -308,6 +308,48 @@ $ docker run --rm -it --net iptastic --ip 203.0.113.2 nginx $ curl 203.0.113.2 ``` +## 暴露端口(Exposing ports) + +通过宿主容器暴露输入端口相当 [繁琐但有效的](https://docs.docker.com/engine/reference/run/#expose-incoming-ports)。 + +例如使用 `-p` 将容器端口映射到宿主端口上(只使用本地主机 (localhost) 接口): + +``` +docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage +``` + +你可以使用 [EXPOSE](https://docs.docker.com/engine/reference/builder/#expose) 告知 Docker,该容器在运行时监听指定的端口: + +``` +EXPOSE +``` + +但是注意 EXPOSE 并不会直接暴露端口,你需要用参数 `-p` 。比如说你要在 localhost 上暴露容器的端口: + +``` +iptables -t nat -A DOCKER -p tcp --dport -j DNAT --to-destination : +``` + +如果你是在 Virtualbox 中运行 Docker,那么你需要配置端口转发 (forward the port)。使用 [forwarded_port](https://docs.vagrantup.com/v2/networking/forwarded_ports.html) 在 Vagrantfile 上配置暴露的端口范围,这样你就可以动态地映射了: + +``` +Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| + ... + + (49000..49900).each do |port| + config.vm.network :forwarded_port, :host => port, :guest => port + end + + ... +end +``` + +如果你忘记了将什么端口映射到宿主机上的话,可使用 `docker port` 查看: + +``` +docker port CONTAINER $CONTAINERPORT +``` + ## 仓管中心和仓库(Registry & Repository) 仓库 (repository) 是 _被托管(hosted)_ 的已命名镜像 (tagged images) 的集合,这组镜像用于构建容器文件系统。 @@ -411,19 +453,27 @@ $ALIAS_PORT_1337_TCP_ADDR 通常,Docker 容器(亦可理解为「服务」)之间的链接,是「服务发现」的一个子集。如果你打算在生产中大规模使用 Docker,这将是一个很大的问题。请参阅[The Docker Ecosystem: Service Discovery and Distributed Configuration Stores](https://www.digitalocean.com/community/tutorials/the-docker-ecosystem-service-discovery-and-distributed-configuration-stores) 获取更多信息。 -## 卷标(Volumes) +## 卷标(Volumes)和挂载 + +### 卷标 Docker 的卷标 (volumes) 是 [独立的文件系统](https://docs.docker.com/engine/tutorials/dockervolumes/)。它们并非必须连接到特定的容器上。 -### 生命周期 +`数据卷` 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性: -- [`docker volume create`](https://docs.docker.com/engine/reference/commandline/volume_create/) -- [`docker volume rm`](https://docs.docker.com/engine/reference/commandline/volume_rm/) +- `数据卷` 可以在容器之间共享和重用 +- 对 `数据卷` 的修改会立马生效 +- 对 `数据卷` 的更新,不会影响镜像 +- `数据卷` 默认会一直存在,即使容器被删除 -### 信息 +卷标相关命令: -- [`docker volume ls`](https://docs.docker.com/engine/reference/commandline/volume_ls/) -- [`docker volume inspect`](https://docs.docker.com/engine/reference/commandline/volume_inspect/) +- [`docker volume create`](https://docs.docker.com/engine/reference/commandline/volume_create/) - 创建卷标 +- [`docker volume rm`](https://docs.docker.com/engine/reference/commandline/volume_rm/) - 删除卷标 + +- [`docker volume ls`](https://docs.docker.com/engine/reference/commandline/volume_ls/) - 查看卷标 +- [`docker volume inspect`](https://docs.docker.com/engine/reference/commandline/volume_inspect/) - 查看数据卷的具体信息 +- [`docker volume prune`](https://docs.docker.com/engine/reference/commandline/volume_prune/) - 清理无主的数据卷 卷标在不能使用链接(只有 TCP/IP)的情况下非常有用。例如,如果你有两个 Docker 实例需要通讯并在文件系统上留下记录。 @@ -445,47 +495,11 @@ docker run -v /Users/wsargent/myapp/src:/src 记得,[文件也可以被挂载为卷标](https://github.com/wsargent/docker-cheat-sheet/tree/master/zh-cn#将文件挂载为卷标)。 -## 暴露端口(Exposing ports) +### 挂载 -通过宿主容器暴露输入端口相当 [繁琐但有效的](https://docs.docker.com/engine/reference/run/#expose-incoming-ports)。 +使用 `--mount` 标记可以指定挂载一个本地主机的目录到容器中去。 -例如使用 `-p` 将容器端口映射到宿主端口上(只使用本地主机 (localhost) 接口): - -``` -docker run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage -``` - -你可以使用 [EXPOSE](https://docs.docker.com/engine/reference/builder/#expose) 告知 Docker,该容器在运行时监听指定的端口: - -``` -EXPOSE -``` - -但是注意 EXPOSE 并不会直接暴露端口,你需要用参数 `-p` 。比如说你要在 localhost 上暴露容器的端口: - -``` -iptables -t nat -A DOCKER -p tcp --dport -j DNAT --to-destination : -``` - -如果你是在 Virtualbox 中运行 Docker,那么你需要配置端口转发 (forward the port)。使用 [forwarded_port](https://docs.vagrantup.com/v2/networking/forwarded_ports.html) 在 Vagrantfile 上配置暴露的端口范围,这样你就可以动态地映射了: - -``` -Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| - ... - - (49000..49900).each do |port| - config.vm.network :forwarded_port, :host => port, :guest => port - end - - ... -end -``` - -如果你忘记了将什么端口映射到宿主机上的话,可使用 `docker port` 查看: - -``` -docker port CONTAINER $CONTAINERPORT -``` +在用 `docker run` 命令的时候,使用 `--mount` 标记来将 `数据卷` 挂载到容器里。在一次 `docker run` 中可以挂载多个 `数据卷`。 ## 最佳实践 diff --git a/docs/docker/docker-compose.md b/docs/docker/docker-compose.md new file mode 100644 index 0000000..3fec871 --- /dev/null +++ b/docs/docker/docker-compose.md @@ -0,0 +1,110 @@ +# Docker Compose + +> [compose](https://github.com/docker/compose) 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 `OpenStack` 中的 `Heat` 十分类似。 + +## 一、Compose 简介 + +**`Compose` 的定位是:定义和运行多个 Docker 容器的应用**。 使用一个 `Dockerfile` 模板文件,可以让用户很方便的定义一个单独的应用容器。然而,在日常工作中,经常会碰到需要多个容器相互配合来完成某项任务的情况。例如要实现一个 Web 项目,除了 Web 服务容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等。 + +`Compose` 恰好满足了这样的需求。它允许用户通过一个单独的 `docker-compose.yml` 模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。 + +`Compose` 中有两个重要的概念: + +- **服务 (`service`)**:一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。 +- **项目 (`project`)**:由一组关联的应用容器组成的一个完整业务单元,在 `docker-compose.yml` 文件中定义。 + + `Compose` 的默认管理对象是项目,通过子命令对项目中的一组容器进行便捷地生命周期管理。 + +## 二、安装卸载 + +`Compose` 支持 Linux、macOS、Windows10 三大平台。 + +Linux 安装方式: + +```bash +sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose +sudo chmod +x /usr/local/bin/docker-compose +``` + +> :bell: 详情请参考:[Install Docker Compose](https://docs.docker.com/compose/install/) + +## 三、快速入门 + +### web 应用 + +新建文件夹,在该目录中编写 `app.py` 文件 + +```python +from flask import Flask +from redis import Redis + +app = Flask(__name__) +redis = Redis(host='redis', port=6379) + +@app.route('/') +def hello(): + count = redis.incr('hits') + return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) +``` + +### Dockerfile + +编写 `Dockerfile` 文件,内容为 + +```docker +FROM python:3.6-alpine +ADD . /code +WORKDIR /code +RUN pip install redis flask +CMD ["python", "app.py"] +``` + +### docker-compose.yml + +编写 `docker-compose.yml` 文件,这个是 Compose 使用的主模板文件。 + +```yaml +version: '3' +services: + + web: + build: . + ports: + - "5000:5000" + + redis: + image: "redis:alpine" +``` + +### 运行 compose 项目 + +```bash +$ docker-compose up +``` + +此时访问本地 `5000` 端口,每次刷新页面,计数就会加 1。 + +## 四、命令 + +> :bell: 请参考: +> +> - [Compose 官方命令说明文档](https://docs.docker.com/compose/reference/) +> - [Compose 命令说明中文文档](https://yeasy.gitbooks.io/docker_practice/content/compose/commands.html) + +## 五、模板文件 + +> `docker-compose.yml` 文件是 Docker Compose 的模板文件,其作用类似于 Dockerfile 和 Docker。 + +[docker-compose.yml 支持的默认环境变量官方文档](https://docs.docker.com/compose/env-file/) + +## 参考资料 + +- **官方** + - [Docker Compose Github](https://github.com/docker/compose) + - [Docker Compose 官方文档](https://docs.docker.com/compose/) +- **教程** + - [Docker — 从入门到实践 - Docker Compose 项目]( https://yeasy.gitbooks.io/docker_practice/content/compose/ ) + diff --git a/docs/docker/docker-dockerfile.md b/docs/docker/docker-dockerfile.md index 28bc6dc..492e092 100644 --- a/docs/docker/docker-dockerfile.md +++ b/docs/docker/docker-dockerfile.md @@ -21,7 +21,15 @@ -## 一、Dockerfile 指令 +## 一、Dockerfile 简介 + +Docker 镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。 + +Dockerfile 是一个文本文件,其内包含了一条条的 **指令(Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。 + +### 使用 Dockerfile 构建镜像 + +## 二、Dockerfile 指令详解 ### FROM(指定基础镜像) @@ -658,6 +666,8 @@ FROM my-node 是的,只有这么一行。当在各个项目目录中,用这个只有一行的 `Dockerfile` 构建镜像时,之前基础镜像的那三行 `ONBUILD` 就会开始执行,成功的将当前项目的代码复制进镜像、并且针对本项目执行 `npm install`,生成应用镜像。 +## 二、最佳实践 + 有任何的问题或建议,欢迎给我留言 :laughing: @@ -665,6 +675,6 @@ FROM my-node ## 参考资料 - [Dockerfie 官方文档](https://docs.docker.com/engine/reference/builder/) -- [Dockerfile 最佳实践文档](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) +- [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) - [Docker 官方镜像 Dockerfile](https://github.com/docker-library/docs) - [Dockerfile 指令详解](https://yeasy.gitbooks.io/docker_practice/content/image/dockerfile/) diff --git a/docs/docker/docker-quickstart.md b/docs/docker/docker-quickstart.md index ff00c0e..6fe6834 100644 --- a/docs/docker/docker-quickstart.md +++ b/docs/docker/docker-quickstart.md @@ -117,7 +117,7 @@ $ sudo service docker start $ sudo systemctl start docker ``` -## 三、hello world 实例 +## 三、Hello World 实例 下面,我们通过最简单的 image 文件"[hello world"](https://hub.docker.com/r/library/hello-world/),感受一下 Docker。 @@ -354,3 +354,4 @@ $ sudo systemctl start docker ## 参考资料 - [Docker 入门教程](https://www.ruanyifeng.com/blog/2018/02/docker-tutorial.html) +- [Docker — 从入门到实践](https://github.com/yeasy/docker_practice) \ No newline at end of file