diff --git a/404.md b/404.md index 1cea9647e8e25..ce124b556134e 100644 --- a/404.md +++ b/404.md @@ -1,8 +1,5 @@ --- layout: page -title: 404 - Page not found +title: 404 - 我猜你迷路了!没找到你想要的。 --- - -Sorry, we can't find that page that you're looking for. You can try take for a look by going [back to the homepage]({{ site.baseurl }}/). - -[Constructocat by https://github.com/jasoncostello]({{ site.baseurl }}/) \ No newline at end of file + diff --git a/CNAME b/CNAME index 8b137891791fe..833868e549d06 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ - +jeffkit.com diff --git a/_config.yml b/_config.yml index 494da8c24deec..1672102f41af8 100644 --- a/_config.yml +++ b/_config.yml @@ -3,13 +3,13 @@ # # Name of your site (displayed in the header) -name: Your Name +name: Jeff的妙想奇境 # Short bio or description (displayed in the header) -description: Web Developer from Somewhere +description: 告诉你,她来自我的心 # URL of your avatar or profile pic (you could use your GitHub profile pic) -avatar: https://raw.githubusercontent.com/barryclark/jekyll-now/master/images/jekyll-logo.png +avatar: https://avatars3.githubusercontent.com/u/252377?v=3&s=460 # # Flags below are optional @@ -18,15 +18,15 @@ avatar: https://raw.githubusercontent.com/barryclark/jekyll-now/master/images/je # Includes an icon in the footer for each username you enter footer-links: dribbble: - email: - facebook: + email: bbmyth+pages@gmail.com + facebook: i.jeff.kit flickr: - github: barryclark/jekyll-now + github: jeffkit instagram: linkedin: pinterest: rss: # just type anything here for a working RSS icon, make sure you set the "url" above! - twitter: jekyllrb + twitter: jeff_kit stackoverflow: # your stackoverflow profile, e.g. "users/50476/bart-kiers" youtube: # channel/ or user/ @@ -39,7 +39,7 @@ google_analytics: # Your website URL (e.g. http://barryclark.github.io or http://www.barryclark.co) # Used for Sitemap.xml and your RSS feed -url: +url: http://jeffkit.com # If you're hosting your site at a Project repository on GitHub pages # (http://yourusername.github.io/repository-name) diff --git a/_posts/2014-3-3-Hello-World.md b/_posts/2014-3-3-Hello-World.md deleted file mode 100644 index d4665b6d18e9e..0000000000000 --- a/_posts/2014-3-3-Hello-World.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: post -title: You're up and running! ---- - -Next you can update your site name, avatar and other options using the _config.yml file in the root of your repository (shown below). - -![_config.yml]({{ site.baseurl }}/images/config.png) - -The easiest way to make your first post is to edit this one. Go into /_posts/ and update the Hello World markdown file. For more instructions head over to the [Jekyll Now repository](https://github.com/barryclark/jekyll-now) on GitHub. \ No newline at end of file diff --git a/_posts/2015-3-11-Hello-World.md b/_posts/2015-3-11-Hello-World.md new file mode 100644 index 0000000000000..5eff802ad5a12 --- /dev/null +++ b/_posts/2015-3-11-Hello-World.md @@ -0,0 +1,12 @@ +--- +layout: post +title: 我回来了 +--- + +![i am back](../images/posts/hello-im-back-again.png "I am back") + +Hi,我是Jeff,我回来了。 + +创业三年多,一直在所谓的碎片化里生存,刷微博、刷微信、刷知乎等等,整个人都碎了,有深深有无力感。写文章也许是把破碎的我重新拼装回来的唯一有效途径吧。 + +三年多的时间里面,发生过很多事情,将来,还有更有趣的事情发生,容我好好坐下来,慢慢讲。谢谢! \ No newline at end of file diff --git a/_posts/2015-3-23-os-vs-app.md b/_posts/2015-3-23-os-vs-app.md new file mode 100644 index 0000000000000..04f2c6209d003 --- /dev/null +++ b/_posts/2015-3-23-os-vs-app.md @@ -0,0 +1,29 @@ +--- +layout: post +title: 写操作系统就比写App牛B吗 +--- + +![写操作系统就比写App牛B吗](../images/posts/morden.jpg "写操作系统就比写App牛B吗") + +今天中午在办公室和同事喝茶聊(chui)人(niu)生(bi)的时候,提到已离职的某前同事,同时想起当时离职的其中一个原因,有感,遂记录之,同时献给一直在认为自己做的事情无足轻重或无存在感的同学。 + +这位旧同事,他曾经跟我抱怨过自己整天在写业务代码,没有更大的发展空间,是他想要离职的原因之一。我问,什么叫写业务代码,他说,就是写应用业务逻辑啊,不像你,可以设计公司的技术架构,多牛B。我笑着说,那我也是在写业务代码啊,只不过我面向的用户是公司的开发同学,业务是让大家写代码和运维起来更爽更快而已。 + +应该有不少同学都会认为,写操作系统的人一定比写app的人要牛B。并由此推理,架构师一定比研发牛B,研发一定比开发牛B。如果你也这么认为,那你就错了,掉进了一个看上去是正确的坑里。 + +事实上,不管是写操作系统还是写一个应用,本质上都是在解决具体的业务问题,要求工程师们首先要对业务知识了如指掌,再通过工程的方法来解决它,过程是统一无异的。 + +例如同样是写操作系统,有些人写的水平就是跟着《自制操作系统》做出来的差不多,而有人则写出了世界上最流行的操作系统之一,指的是linus。 +例如同样是写app,你可能写的乱七八糟crash不断,但高手写出来的作品就行云流水,体验满分呢。 + +有同学也许会反驳:胡说!写操作系统用的技术哪里是你做应用能用得上的呢!呵呵!那我只能说,如果你的操作系统只是一个简单的Demo,我打赌,它一定没有你正在写的app用到的算法、技术点多。 + +当然,我的意思并不是说你现在是个初级开发者,一样可以做好与架构师同样的事情。架构师的业务知识的积累是来自具体的应用开发生涯,也就是说没有一定的应用开发经验的(业务知识)积累,是干不好(不是干不了)架构师干的事的。 + +所以,写操作系统的人未必比写App的人牛B!你如果认为自己做的事情无足轻重,没有存在感,问题的根本不在于你正在做的事情,而在于你没有把它做好。做不好,你只会让自己长期处于这种被动的局面,就算你换份工作亦然;做好了,后续的好事情才会继续来,改变才会真正发生。 + +所以,看见脏代码,很不爽,你要干的事情是捋干净它,而不是坐着叹前任不行又沿他步履前行;感觉天天在做重复的事情,那就让它自动化,而不是机械般地复制粘贴;把你讨厌的事情都干掉,很快你就无事可干了,即有更重要的事情可以干了! + +人的竟争力不在于你在做什么,而在于那件事情你做得比其他人好。 + +就酱! \ No newline at end of file diff --git a/_posts/2015-4-16-dokku.md b/_posts/2015-4-16-dokku.md new file mode 100644 index 0000000000000..7c8d00b5430be --- /dev/null +++ b/_posts/2015-4-16-dokku.md @@ -0,0 +1,351 @@ +--- +layout: post +title: Dokku自动部署源码解读 +--- + +Dokku是一个号称世界上最小的PAAS平台,百行级别的代码实现单机版的Heroku。读一下它的源码看看他到底做了些什么事情。 + +关于Dokku怎么安装和使用,并不是本文关注的范围,如果你还没用过Dokku,建议先补补课。 + +另,本文只关心Dokku的自动部署部份的代码,其他命令行的代码不在本文解读。 + +##魔术发生的地方 + +当你在本地向Dokku的服务器推送最新的代码时,Dokku会为你接管以下工作: + +- 检查你的代码类型,编译之。 +- 使用Docker运行你的应用,并将其暴露在外网。 + +那么,我们读源码就从这里开始吧,假如我们的项目名是helloworld,当你向dokku的git推送代码时,会触发Git的hooks脚本,Dokku仅提供了一个脚本:pre-receive: + +``` +$ cat /home/dokku/helloworld/hooks/pre-receive +#!/usr/bin/env bash +set -e; set -o pipefail; + +cat | DOKKU_ROOT="/home/dokku" dokku git-hook helloworld +``` + +关键的命令是```dokku git-hook helloworld```,我们需要去阅读dokku脚本来了解更多了! + +### Dokku git-hook +Dokku所有代码都是shell脚本,Dokku主代码的结构大致如下: + +- 声明变量 +- 加载通用的函数,如log,parse_args等。 +- 执行用户定义脚本 $DOKKU_ROOT/dokkurc或 $DOKKU_ROOT/.dokkurc目录下的脚本。 +- 执行用户指定的命令,DOKKU主脚本仅声明了几个命令:receive, deploy, cleanup, plugins, plugins-install, plugins-install-depencdencies, plugin-update, deploy:all, help。如果主脚本找不到命令,则会去插件目录查找是否存在,在则执行插件的命令。 + + +### git-hook + +git-hook这个命令,是在plugins/git/commands里面定义的: + +``` +case "$1" in + git-hook) + APP=$2 + + while read oldrev newrev refname + do + # Only run this script for the master branch. You can remove this + # if block if you wish to run it for others as well. + if [[ $refname = "refs/heads/master" ]] ; then + # broken out into pluginhook so we might support other methods to receive an app + pluginhook receive-app $APP $newrev + else + echo $'\e[1G\e[K'"-----> WARNING: deploy did not complete, you must push to master." + echo $'\e[1G\e[K'"-----> for example, try 'git push ${refname/refs\/heads\/}:master'" + fi + done + ;; +``` +程序从命令行中读取参数,判断所更新的分支是否master分支,不接受非master分支的推送。 +正常的推送,会继续调用```pluginhook receive-app $APP $newrev```这个命令。 + +### receive-app + +pluginhook是Dokku作者的另一个开源项目,是用来方便执行分散在某个目录下的命令的。 +我们在plugins/git的目录下找下receive-app这个脚本: + +``` +#!/usr/bin/env bash +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$(dirname $0)/../common/functions" + +APP="$1"; REV="$2" + +dokku git-build $APP $REV +``` + +好吧,又跳转到git-build去了。 + +### git-build + +在plugins/git/commands脚本下面找到git-build命令: + +``` +git-build) + APP="$2"; APP_BUILD_LOCK="$DOKKU_ROOT/$APP/.build.lock" + APP_BUILD_LOCK_MSG="$APP is currently being deployed or locked. Waiting..." + [[ $(flock -n "$APP_BUILD_LOCK" true &>/dev/null ; echo $?) -ne 0 ]] && echo "$APP_BUILD_LOCK_MSG" + + shift 1 + flock -o "$APP_BUILD_LOCK" dokku git-build-locked "$@" + ;; + +``` +检测当前是否正在build,如果正在build,则提示等待下一次。否则创建一个锁,开始build。调用 ```dokku git-build-locked``` + +### git-build-locked + +``` +git_build_app_repo() { + verify_app_name "$1" + APP="$1"; REV="$2" + + # clean up after ourselves + TMP_WORK_DIR=$(mktemp -d) + trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' RETURN + + # git clone + chmod 755 $TMP_WORK_DIR + unset GIT_DIR GIT_WORK_TREE + pushd $TMP_WORK_DIR > /dev/null + git clone -q "$DOKKU_ROOT/$APP" "$TMP_WORK_DIR" &> /dev/null + git config advice.detachedHead false + git checkout "$REV" &> /dev/null + git submodule update --init --recursive &> /dev/null + find -name .git -prune -exec rm -rf {} \; > /dev/null + + if [[ -f Dockerfile ]] && [[ "$([[ -f .env ]] && grep -q BUILDPACK_URL .env; echo $?)" != "0" ]] && [[ ! -f ".buildpacks" ]]; then + dokku receive "$APP" "dockerfile" "$TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/" + else + dokku receive "$APP" "buildstep" "$TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/" + fi +} + + git-build-locked) + APP="$2" + if [[ $# -ge 3 ]];then + REF="$3" + else + REF=$(< "$DOKKU_ROOT/$APP/refs/heads/master") + fi + git_build_app_repo $APP $REF + ;; +``` +这里做的事情就是创建一个临时目录,把应用的源码clone出来,再检查看看是使用DockerFile构建还是buildstep构建,然后分别使用不同的方式来构建之。完事后把临时目录删除。 + +下面是关键的步骤 ```dokku receive "$APP" "buildstep" "$TMP_WORK_DIR" | sed -u "s/^/"$'\e[1G'"/"``` + +### receive +receive命令定义在dokku主文件: + +``` +receive) + APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4" + dokku_log_info1 "Cleaning up..." + dokku cleanup + dokku_log_info1 "Building $APP from $IMAGE_SOURCE_TYPE..." + dokku build "$APP" "$IMAGE_SOURCE_TYPE" "$TMP_WORK_DIR" + dokku_log_info1 "Releasing $APP..." + dokku release "$APP" "$IMAGE_SOURCE_TYPE" + dokku_log_info1 "Deploying $APP..." + dokku deploy "$APP" + dokku_log_info2 "Application deployed:" + dokku urls "$APP" | sed "s/^/ /" + echo + ;; +``` +可以看到,收到推送后的构建过程有以下几步: + +- cleanup, 清除掉所有未运行的容器及未使用的镜像。 +- build, 构建 +- release, 发布新版本 +- deploy, 部署 + +#### build +在plugins/00_dokku-standard/commands中找到build命令的定义: + +``` +case "$1" in + build) + APP="$2"; IMAGE="dokku/$APP"; IMAGE_SOURCE_TYPE="$3"; TMP_WORK_DIR="$4" + CACHE_DIR="$DOKKU_ROOT/$APP/cache" + + pushd "$TMP_WORK_DIR" &> /dev/null + + case "$IMAGE_SOURCE_TYPE" in + buildstep) + id=$(tar -c . | docker run -i -a stdin $DOKKU_IMAGE /bin/bash -c "mkdir -p /app && tar -xC /app") + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null + [[ -d $CACHE_DIR ]] || mkdir $CACHE_DIR + # *DEPRECATED* in v0.4.0: `pluginhook pre-build` will be removed in future releases + pluginhook pre-build "$APP" + pluginhook pre-build-buildstep "$APP" + + # *DEPRECATED* in v0.3.14: `pluginhook docker-args` will be removed in future releases + # https://github.com/progrium/dokku/issues/896 & https://github.com/progrium/dokku/issues/906 + DOCKER_ARGS=$(: | pluginhook docker-args $APP build) + DOCKER_ARGS+=$(: | pluginhook docker-args-build $APP) + id=$(docker run -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build/builder) + docker attach $id + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null + + # *DEPRECATED* in v0.4.0: `pluginhook post-build` will be removed in future releases + pluginhook post-build "$APP" + pluginhook post-build-buildstep "$APP" + ;; + + dockerfile) + # extract first port from Dockerfile + DOCKERFILE_PORT=$(grep EXPOSE Dockerfile | head -1 | awk '{ print $2 }' || true) + [[ -n "$DOCKERFILE_PORT" ]] && dokku config:set-norestart $APP DOKKU_DOCKERFILE_PORT=$DOCKERFILE_PORT + + # buildstep pluginhooks don't necessarily make sense for dockerfiles. call the new breed!!! + pluginhook pre-build-dockerfile "$APP" + + docker build -t "$IMAGE" . + + pluginhook post-build-dockerfile "$APP" + ;; + + *) + dokku_log_fail "Building image source type $IMAGE_SOURCE_TYPE not supported!" + ;; + esac + ;; +``` +在BuildStep类型的应用当中,关键的一行代码在这里: + +``` +id=$(docker run -d -v $CACHE_DIR:/cache -e CACHE_PATH=/cache $DOCKER_ARGS $IMAGE /build/builder) +``` + +关键的Build脚本在buildstep镜像的/build/builder里面。这个脚本将在被执行完之后删除,所以在app的镜像中是找不到它的存在的。 + +build目录的结构如下: + +``` +builder +complie.sh +install-buildpack +config + - buildpacks + - paths.sh +``` + +##### builder + +下面先来看看build脚本做了什么事。这是整个Dokku当中最关键的一步!builder脚本主要做了以下事情: + +- 创建一系列的目录备用。 +- 创建一个随机用户,并让该用户有权限访问上面的目录。 +- 调用同目录下的compile.sh脚本,这才是真正构建应用的地方。 +- 生成一个start脚到到根目录下。用于启动应用。 + +##### complile.sh + +compile.sh脚本,也就是整个Build过程最核心的环节,使用buildpack来检测应用的类型并使用对应的buildpack来compile, release个应用。 + +整个Build过程到此完毕。 + +#### Release + +应用创建好以后,就应该release.再次回到Dokku脚本的receive部份,跟踪dokku release。在plugins/00_dokku-standard的commands脚本上找到release部份: + +``` +if [[ -f "$DOKKU_ROOT/ENV" ]]; then + id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/00-global-env.sh" < "$DOKKU_ROOT/ENV") + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null + fi + if [[ -f "$DOKKU_ROOT/$APP/ENV" ]]; then + id=$(docker run -i -a stdin $IMAGE /bin/bash -c "mkdir -p /app/.profile.d && cat > /app/.profile.d/01-app-env.sh" < "$DOKKU_ROOT/$APP/ENV") + test "$(docker wait $id)" -eq 0 + docker commit $id $IMAGE > /dev/null +``` +关键的操作是把两个文件复制到镜像的/app/.profile.d/目录下面: + +- $DOKKU_ROOT/ENV +- $DOKKU_ROOT/$APP/ENV + +这两个文件其实都是用户定义的环境变量来的。 + +#### Deploy + +deploy部分的代码位于dokku主脚本内: + +``` +deploy) + APP="$2"; IMAGE="dokku/$APP" + pluginhook pre-deploy $APP + + if [[ -f "$DOKKU_ROOT/$APP/CONTAINER" ]]; then + oldid=$(< "$DOKKU_ROOT/$APP/CONTAINER") + fi + + # start the app + DOCKER_ARGS=$(: | pluginhook docker-args $APP deploy) + DOCKER_ARGS+=$(: | pluginhook docker-args-deploy $APP) + BIND_EXTERNAL=$(pluginhook bind-external-ip $APP) + + is_image_buildstep_based "$IMAGE" && DOKKU_BUILDSTEP=true + [[ -n "$DOKKU_BUILDSTEP" ]] && START_CMD="/start web" + [[ -z "$DOKKU_BUILDSTEP" ]] && DOKKU_DOCKERFILE_PORT=$(dokku config:get $APP DOKKU_DOCKERFILE_PORT || true) + + if [[ "$BIND_EXTERNAL" = "false" ]];then + port=${DOKKU_DOCKERFILE_PORT:=5000} + id=$(docker run -d -e PORT=$port $DOCKER_ARGS $IMAGE $START_CMD) + ipaddr=$(docker inspect --format '{{ .NetworkSettings.IPAddress }}' $id) + else + id=$(docker run -d -p 5000 -e PORT=5000 $DOCKER_ARGS $IMAGE $START_CMD) + port=$(docker port $id 5000 | sed 's/[0-9.]*://') + ipaddr=127.0.0.1 + fi + + # if we can't post-deploy successfully, kill new container + kill_new() { + docker inspect $id &> /dev/null && docker kill $id > /dev/null + trap - INT TERM EXIT + kill -9 $$ + } + + # run checks first, then post-deploy hooks, which switches Nginx traffic + trap kill_new INT TERM EXIT + dokku_log_info1 "Running pre-flight checks" + pluginhook check-deploy $APP $port $ipaddr $id + trap - INT TERM EXIT + + # now using the new container + echo $id > "$DOKKU_ROOT/$APP/CONTAINER" + echo $ipaddr > "$DOKKU_ROOT/$APP/IP" + echo $port > "$DOKKU_ROOT/$APP/PORT" + echo "http://$(< "$DOKKU_ROOT/HOSTNAME"):$port" > "$DOKKU_ROOT/$APP/URL" + + dokku_log_info1 "Running post-deploy" + pluginhook post-deploy $APP $port $ipaddr + + # kill the old container + if [[ -n "$oldid" ]]; then + # Let the old container finish processing requests, before terminating it + WAIT="${DOKKU_WAIT_TO_RETIRE:-60}" + dokku_log_info1 "Shutting down old container in $WAIT seconds" + ( + exec >/dev/null 2>/dev/null {{ post.title }} Read More {% endfor %} - \ No newline at end of file +