Jan 12, 2017
Technology目标
在Wordpress的每篇文章添加TurnToJPG功能,点击该按钮以后,由博文直接生成长文字图片。
搭建测试环境
为了及时测试我们的博客,快速搭建一个基于docker的开发环境:
$ sudo docker pull wordpress
$ sudo docker pull mariadb
$ sudo docker pull corbinu/docker-phpmyadmin
创建一个docker-compose的yml文件,启动之:
$ vim docker-compose.yml
wordpress:
image: wordpress
links:
- wordpress_db:mysql
ports:
- 8080:80
wordpress_db:
image: mariadb
environment:
MYSQL_ROOT_PASSWORD: examplepass
phpmyadmin:
image: corbinu/docker-phpmyadmin
links:
- wordpress_db:mysql
ports:
- 8181:80
environment:
MYSQL_USERNAME: root
MYSQL_ROOT_PASSWORD: examplepass
现在启动服务:
$ sudo docker-compose up -d
Creating mywordpress_wordpress_db_1
Creating mywordpress_wordpress_1
Creating mywordpress_phpmyadmin_1
打开http://127.0.0.1:8080即可访问到我们的测试站点。在wordpress后台
查找并安装tdPersona主题.
分析
所用主题模板
随机拷贝某篇博文的html, 而后:
$ cat test.html | grep -i themes
<link rel='stylesheet' id='tdpersona-icons-css' href='http://www.hahahahohoho.com.cn/wp-content/themes/tdpersona/css/font-awesome.min.css?ver=4.6.2' type='text/css' media='all' />
<link rel='stylesheet' id='tdpersona-framework-css' href='http://www.hahahahohoho.com.cn/wp-content/themes/tdpersona/css/bootstrap.min.css?ver=4.6.2' type='text/css' media='all' />
<link rel='stylesheet' id='style-css' href='http://www.hahahahohoho.com.cn/wp-content/themes/tdpersona/style.css?ver=4.6.2' type='text/css' media='all' />
可见原博主选用的是tdpersona主题.
找到需要图形化的部分
在chromium里按F12, 耐心找寻你需要的那一部分。

我的:
<article id="post-10746" class="post-10746 post type-post status-publish format-standard hentry category-1">
// .........
</article>
进一步看,发现我们需要在<head></head>动文章:

<header class="entry-header">
<h2 class="entry-title"><?php the_title(); ?></h2>
<?php if ( 'post' == get_post_type() ) : ?>
<?php tdpersona_post_date(); ?>
<?php endif; ?>
<?php if ( has_post_thumbnail() ): ?>
<div class="post-thumb">
<?php the_post_thumbnail(); ?>
</div><!-- .post-thumb -->
<?php endif; ?>
</header><!-- .entry-header -->
代码修改
/var/www/html/wp-content/themes/tdpersona/template-parts/content-single.php:
首尾分别添加如下行数(+代表添加的行数):
+ <div class="capturepost">
<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
</footer><!-- .entry-meta -->
</article><!-- #post -->
+ </div>
/var/www/html/wp-content/themes/tdpersona/inc/template-tags.php:
$output .= '<div class="entry-date">';
$output .= '<a href="'.esc_url( get_permalink() ).'">'.$date_format_html.'</a>';
$output .= '</div><!-- .entry-date -->';
+ $output .= '<div class="save-to-jpg", align="right">';
+ $output .= '<a href="javascript:genPostShot()">TurnToJPG --> <i class="fa fa-camera-retro fa-2x"></i></a><a id="test"></a>';
+ $output .= '</div><!-- .save-to-jpg -->';
+ $output .= '<hr>';
这将在标题栏下面添加一个图标和链接,点击该图标将触发javascript函数,将当前页面
转变为jpg图片.
/var/www/html/wp-includes/formatting.php:
<script type="text/javascript">
window._wpemojiSettings = <?php echo wp_json_encode( $settings ); ?>;
!function(a,b,c){function d(a){var b,c,d,e,f=String.fromCharCode;if(!k||!k.fillText)return!1;switch(k.clearRect(0,0,j.width,j.height),k.textBaseline="top",k.font="600 32px Arial",a){case"flag":return k.fillText(f(55356,56826,55356,56819),0,0),!(j.toDataURL().length<3e3)&&(k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57331,65039,8205,55356,57096),0,0),b=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55356,57331,55356,57096),0,0),c=j.toDataURL(),b!==c);case"emoji4":return k.fillText(f(55357,56425,55356,57341,8205,55357,56507),0,0),d=j.toDataURL(),k.clearRect(0,0,j.width,j.height),k.fillText(f(55357,56425,55356,57341,55357,56507),0,0),e=j.toDataURL(),d!==e}return!1}function e(a){var c=b.createElement("script");c.src=a,c.defer=c.type="text/javascript",b.getElementsByTagName("head")[0].appendChild(c)}var f,g,h,i,j=b.createElement("canvas"),k=j.getContext&&j.getContext("2d");for(i=Array("flag","emoji4"),c.supports={everything:!0,everythingExceptFlag:!0},h=0;h<i.length;h++)c.supports[i[h]]=d(i[h]),c.supports.everything=c.supports.everything&&c.supports[i[h]],"flag"!==i[h]&&(c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&c.supports[i[h]]);c.supports.everythingExceptFlag=c.supports.everythingExceptFlag&&!c.supports.flag,c.DOMReady=!1,c.readyCallback=function(){c.DOMReady=!0},c.supports.everything||(g=function(){c.readyCallback()},b.addEventListener?(b.addEventListener("DOMContentLoaded",g,!1),a.addEventListener("load",g,!1)):(a.attachEvent("onload",g),b.attachEvent("onreadystatechange",function(){"complete"===b.readyState&&c.readyCallback()})),f=c.source||{},f.concatemoji?e(f.concatemoji):f.wpemoji&&f.twemoji&&(e(f.twemoji),e(f.wpemoji)))}(window,document,window._wpemojiSettings);
</script>
+ /* Actually add it into here seems not a good idea, just a hacking */
+ <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
+ <script type="text/javascript" src="/js/html2canvas.js"></script>
+ <script type='text/javascript'>//<![CDATA[
+ function genPostShot() {
+ var rightNow = new Date();
+ var imageName = rightNow.toISOString().slice(0,16).replace(/(-)|(:)|(T)/g,"");
+ imageName += '.jpg'
+ html2canvas(document.getElementsByClassName('capturepost'), {
+ background :'#FFFFFF',
+ onrendered: function(canvas) {
+ // Click them for download
+ $('#test').attr('href', canvas.toDataURL("image/jpeg"));
+ $('#test').attr('download',imageName);
+ $('#test')[0].click();
+ }
+ });
+ };
+ //]]>
+ </script>
+ /* Hacking Ended */
其实这个hacking做得不好,整洁的代码应该是新建一个函数用于添加这些代码。
最后,在/var/www/html下创建一个文件夹,并下载html2canvas.js文件:
$ sudo mkdir -p /var/www/html/js
$ cd js
$ sudo wget https://cdn.bootcss.com/html2canvas/0.5.0-alpha1/html2canvas.js
或者直接修改上面定义中的/js/html2canvas.js 为:
//cdn.bootcss.com/html2canvas/0.5.0-alpha1/html2canvas.js
验证
现在重新刷新博客页面,即可享受一键另存为图片的快捷功能。

Jan 10, 2017
Technology从图书馆借回来不少书,其中有一本腾讯工程师写的《后台开发核心技术与应用实践》,这本书的内容很浅显易懂,
基本上涵盖了Linux下C++开发在一般公司能用到的范畴。作者也说了,她写书的初衷在于用最短的篇幅讲解实际后台
用到的核心知识点以便读者能快速进入到实际开发中。扫了扫,前两张用来复习准备面试中有关C++的内容不错。
想提升的就算了,这本书的代码和调试手段都比较初级,实际工作中,需要更多的借助Google和开源社区来完成。
这里记录的主要是本人对该书里提到的一些概念的理解.
第一章
函数模板/函数重载
1.2函数章节里,关于函数重载和函数模板的理解可以用下面的代码来解释,左边是用函数重载的情形,可以看到一个
同名函数可以有多个参数版本,而右边的函数模板则引用了模板的概念,大大节约了代码行。
#include<iostream> #include<iostream>
using namespace std; using namespace std;
int min(int a, int b, int c){ | template<typename T>
> T min(T a,T b,T c){
if(a>b)a=b; if(a>b)a=b;
if(a>c)a=c; if(a>c)a=c;
return a; return a;
} }
long long min(long long a,long long b, long long c){ <
if(a>b)a=b; <
if(a>c)a=c; <
return a; <
} <
double min(double a, double b){ //�������������ϵIJ�����ֻ�� <
if(a-b>(1e-5))a=b; <
return a; <
} <
int main(){ int main(){
int a=1,b=2,c=3; | int a=1,b=2,c=3;
cout<<min(a,b,c)<<endl; | cout<<min(a,b,c)<<endl;
long long a1=100,b1=200,c1=300; | long long a1=1000000000,b1=2000000000,c1=3000000000;
cout<<min(a1,b1,c1)<<endl; | cout<<min(a1,b1,c1)<<endl;
double a2=1.1,b2=2.2; | return 0;
cout<<min(a2,b2)<<endl; <
return 0; <
} }
字符数组
字符数组中,关于strlen()和sizeof()可以作为面试中的题目来问面试者。
函数与指针
函数指针的情形, 注意这里对函数的引用是直接将函数的地址赋给f.
#include<iostream>
using namespace std;
int Mmin(int x,int y){
if(x<y)return x;
return y;
}
int Mmax(int x,int y){
if(x>y)return x;
return y;
}
int main(){
int (*f)(int x,int y);
int a=10,b=20;
f=Mmin; //��Mmin���������ڵ�ַ����f
cout << (*f)(a,b)<<endl;
f=Mmax; //��Mmax���������ڵ�ַ����f
cout << (*f)(a,b)<<endl;
return 0;
}
结构体/共用体/枚举
共用体的定义可以回忆一下:用关键字union来定义,是一种特殊的类。在一个共用体里可以定义多种
不同的数据类型,这些数据类型共享一段内存,在不同的时间里保存不同的数据类型和长度的变量。但是,
同一时间内只能存储一种类型的数据。其存在的目的是为了节省空间。
判断大小端的程序有点意思:
#include<iostream>
using namespace std;
union TEST{
short a;
char b[sizeof(short)];
};
int main(){
TEST test;
test.a=0x0102;// �������ù�����������ֻ�����ù����������еij�Ա��
if(test.b[0]==0x01&&test.b[1]==0x02){
cout<<"big endian."<<endl;
}
else if(test.b[0]==0x02&&test.b[1]==0x01){
cout<<"small endian."<<endl;
}
else{
cout<<"unknown"<<endl;
}
return 0;
}
枚举要注意类似于下面的题目:
enum fruits{apple=3,orange,banana=7,bear};
结果为: 3, 4, 7, 8
占用字节数的计算
鉴于这个题材经常在面试中被问到,单独拎出来写一段:
Union:
#include<iostream>
using namespace std;
union A{
int a[5];
char b;
double c;
};
int main(){
cout<<sizeof(A)<<endl;
return 0;
}
最长的double(8Byte)对齐,因而占用的大小应该是int(4Byte)*5=20, 而8字节对齐应该是3x8=24。因而运行结果为24
Struct:
#include<iostream>
using namespace std;
struct B{
char a;
double b;
int c;
}test_struct_b;
int main(){
cout<<sizeof(test_struct_b)<<endl;
return 0;
}
计算方法是:
char a , 1
补充7
double b, 8
int c, 4
补充4
所以结果为1+7+8+4+4=24.
混合结构体:
#include<iostream>
using namespace std;
typedef union{
long i;
int k[5];
char c;
} UDATE;
struct data{
int cat;
UDATE cow;
double dog;
}too;
UDATE temp;
int main(){
cout<<sizeof(struct data)+sizeof(temp)<<endl;
return 0;
}
原书有错:
sizeof(temp)大小为24, 因为temp是UDATA类型结构。
sizeof(struct data)的计算则是结构体大小变化,每个变量需要占据各自独立的空间,依次为:
int cat: 4
4字节填充(参照double 8字节对齐)
UPDATE cow: 24
double dog: 8
一共为: 4+4+24+8=40
综上所述,结果为40+24=64.
do…while(0)的使用
这个问题可以在面试的时候问面试者。
例程:
# define Foo(x) do{\
statement one;\
statement two;\
}while(0)
在宏替换时这样的写法不会破坏以下的情形:
if(condition)
statement one;
statement two;
else
//.....
如果去掉do…while(0)则会导致else语句孤立而出现编译错误,加了以后,则使得宏展开后,仍然保留了
原始的语义,从而保证程序的正确性。
extern “C”
加这个是为了让编译器将其当成C语言处理。
#ifdef __cpluscplus
extern "C" {
#endif
第二章
类与结构体
定义的异同:
class CStudent{ | struct SStudent{
int num; | public:
char name[20]; <
int age; //��Щ�����ݳ�Ա��Ҳ��Ϊ��Ա���� <
void display(){ //���dz�Ա���� void display(){ //���dz�Ա����
cout<<"num:"<<num<<endl; cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl; cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl; cout<<"age:"<<age<<endl;
} | } //����û�зֺ�
}; | private:
CStudent cstu1,cstu2;//������2������ | int num;
> char name[20];
> int age;
> };
类的封装性
源代码如下:
➜ 0205 cat student.h
class CStudent{
public:
void display();
private:
int num;
char name[20];
int age;
};
➜ 0205 cat main.cpp
#include<iostream>
#include "student.h" //ע��������˫����
int main(){
CStudent stu1;//����stu1����
stu1.display();//ָ��stu1�����ij�Ա����
return 0;
}
➜ 0205 cat student.cpp
#include<iostream>
#include "student.h" //������Ҫinclude����ͷ�ļ����������ҵ�Student��
using namespace std;
void CStudent::display(){ //����Ҫע����Student����
cout<<"num:"<<num<<endl;
cout<<"name:"<<name<<endl;
cout<<"age:"<<age<<endl;
}
运行结果:
num:4196688
name:
age:0
这样的结果是因为没有对数据成员进行初始化而导致的。
构造函数
更改上面的代码:
➜ 0205_1 cat student.h
class CStudent{
public:
CStudent() {
num = 0;
age = 0;
}
void display();
private:
int num;
char name[20];
int age;
};
➜ 0205_1 ./test
num:0
name:
age:0
静态数据成员
例子:
➜ chapter02 cat ./0212.cpp
#include<iostream>
using namespace std;
class Base{
public:
static int var;
};
int Base::var=10;
class Derived:public Base{
};
int main(){
Base base1;
base1.var++;//ͨ������������
cout<<base1.var<<endl;//����11
Base base2;
base2.var++;
cout<<base2.var<<endl;//����12
Derived derived1;
derived1.var++;
cout<<derived1.var<<endl;//����13
Base::var++;//ͨ����������
cout<<derived1.var<<endl;//����14
return 0;
}
➜ chapter02 ./0212
11
12
13
14
对象存储空间
这个和第一章的存储空间可以结合起来看。
空类的存储空间为1:
➜ chapter02 cat 0214.cpp
#include<iostream>
using namespace std;
class CBox{
};
int main(){
CBox boxobj;
cout<<sizeof(boxobj)<<endl;//����1
return 0;
}
➜ chapter02 ./0214
1
有成员变量的类的存储空间:
➜ chapter02 cat 0215.cpp
#include<iostream>
using namespace std;
class CBox{
int length,width,height;
};
int main(){
CBox boxobj;
cout<<sizeof(boxobj)<<endl;
return 0;
}
➜ chapter02 ./0215
12
有静态成员变量时,静态成员变量不占据对象的内存空间:
➜ chapter02 cat 0216.cpp
#include<iostream>
using namespace std;
class CBox{
int length,width,height;
static int count;
};
int main(){
CBox boxobj;
cout<<sizeof(boxobj)<<endl;
return 0;
}
➜ chapter02 ./0216
12
成员函数不占据空间:
➜ chapter02 cat 0217.cpp
#include<iostream>
using namespace std;
class CBox{
int foo();
};
int main(){
CBox boxobj;
cout<<sizeof(boxobj)<<endl;
return 0;
}
➜ chapter02 ./0217
1
构造函数与析构函数也不占据空间:
class CBox{
public:
CBox(){};
~CBox(){};
};
大小为1
虚析构函数,占用大小为8:
class CBox{
public:
CBox(){};
virtual ~CBox(){};
};
大小为8
类模板
操作整数的类与操作浮点数的类:
class Operation_int{ | class Operation_double{
public: public:
Operation_int(int a,int b):x(a),y(b){} | Operation_double(double a, double b):x(a),y(b){}
int add(){ | double add(){
return x+y; return x+y;
} }
int subtract(){ | double subtract(){
return x-y; return x-y;
} }
private: private:
int x,y; | double x,y;
}; };
用类模板来抽象:
#include <iostream>
using namespace std;
template<class T>//����һ��ģ�壬����������ΪT
class Operation {
public:
Operation (T a, T b):x(a),y(b){}
T add(){
return x+y;
}
T subtract(){
return x-y;
}
private:
T x,y;
};
int main(){
Operation <int> op_int(1,2);
cout<<op_int.add()<<" "<<op_int.subtract()<<endl;//����3��-1
Operation <double> op_double(1.2,2.3);
cout<<op_double.add()<<" "<<op_double.subtract()<<endl;//����3.5��-1.1
return 0;
}
➜ chapter02 ./0224
3 -1
3.5 -1.1
虚函数/纯虚函数
虚函数可以使得基类指针访问派生类中的同名函数:
而纯虚函数则是因为:用基类本身生成对象不合情理,例如用动物作为基类来抽象具体的动物,而动物这个基类
本身是不能被实例化的。
析构函数不是虚函数时,容易引发内存泄漏:
➜ chapter02 ./0231
Base::Base()
Derive::Derive()
Base::~Base()
➜ chapter02 cat 0231.cpp
#include<iostream>
using namespace std;
class Base{
public:
Base(){ std::cout<<"Base::Base()"<<std::endl; }
~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
};
class Derive:public Base{
public:
Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
};
int main(){
Base* pBase = new Derive();
/*����base classed������Ŀ����Ϊ������"ͨ��base class�ӿڴ���derived class����"*/
delete pBase;
return 0;
}
这是由C++的定义指出的:如果一个派生类对象经由一个基类指针被删除,而该基类带有一个非虚析构函数,则会导致
派生类中的成分没被销毁。
所以,需要把析构函数定义为虚函数。
➜ chapter02 ./0232
Base::Base()
Derive::Derive()
Derive::~Derive()
Base::~Base()
➜ chapter02 cat 0232.cpp
#include<iostream>
using namespace std;
class Base{
public:
Base(){ std::cout<<"Base::Base()"<<std::endl; }
virtual ~Base(){ std::cout<<"Base::~Base()"<<std::endl; }
};
class Derive:public Base{
public:
Derive(){ std::cout<<"Derive::Derive()"<<std::endl; }
~Derive(){ std::cout<<"Derive::~Derive()"<<std::endl; }
};
int main(){
Base* pBase = new Derive();
delete pBase;
return 0;
}
单例模式
理解:一台计算机上可以连好几台打印机,但是打印程序只能有一个,这里就可以通过单例模式来避免两个打印作业
被同时输出到打印机中。
➜ chapter02 ./0233
s1=s2
➜ chapter02 cat 0233.cpp
#include<iostream>
using namespace std;
class CSingleton{
private:
CSingleton(){ //构造函数是私有的
}
static CSingleton *m_pInstance;
public:
static CSingleton * GetInstance(){
if(m_pInstance == NULL) //判断是否是第一次调用
m_pInstance = new CSingleton();
return m_pInstance;
}
};
CSingleton * CSingleton::m_pInstance=NULL;//初始化静态成员变量
int main(){
CSingleton *s1= CSingleton::GetInstance();
CSingleton *s2= CSingleton::GetInstance();
if(s1==s2){
cout<<"s1=s2"<<endl;
}
return 0;
}
单例类的特点:
1. 有指向唯一实例的静态指针m_pInstance,且为私有。
2. 有一个公有函数用于获取唯一的实例。
3. 构造函数是私有的,不能在别处创建该类的实例.
Jan 7, 2017
TechnologyProblem
In this morning when I get up and try to write something in my blog, I found
the blog won’t upate. In travisCI website I got something very strange like
following picture shows:

Error info:
Failed to normalize URL string. Returning in = "/"
Reason
As discussed in this post:
https://discuss.gohugo.io/t/started-getting-failed-to-normalize-url-string-returning-in/5034
This is because hugo now holds its own dependencies using govendor, you could
view from its repository:
$ cd $GOPATH
$ cd src/github.com/spf13/hugo
$ ls -l vendor
total 16
-rw-r--r-- 1 dash root 14793 Jan 3 11:23 vendor.json
Solution
Using govendor for syncing the dependencies, the modified .travis.yml is
listed as following:
install:
- go get -u -v github.com/kardianos/govendor
- go get -u -v github.com/spf13/hugo
- cd $GOPATH/src/github.com/spf13/hugo && govendor sync && go install
script:
- cd $HOME/gopath/src/github.com/purplepalmdash/purplepalmdash.github.io && hugo
Save changes and commit it into github, the travis building will start again,
this time you won’t failed.
Jan 6, 2017
Technology第一章
Docker镜像准备:
$ sudo docker pull redis
$ sudo docker pull django
$ sudo docker pull haproxy
$ sudo docker pull ubuntu
应用栈节点架构:
启动redis-master容器节点, 两个redis-slave容器节点启动时连接到redis-master上面,
两个app容器节点启动时连接到redis-master上面, haproxy容器结点启动时连接到两个app结点上面。
容器的启动顺序为:redis-master -> redis-slave -> APP -> HAProxy.
Redis Master:
$ sudo docker run -it --name redis-master redis /bin/bash
root@4e4e597ffcb6:/data#
Redis Slave1/Slave2:
$ sudo docker run -it --name redis-slave1 --link redis-master:master redis /bin/bash
$ sudo docker run -it --name redis-slave2 --link redis-master:master redis /bin/bash
App1, App2:
$ mkdir -p ~/Projects/Django/App1
$ mkdir -p ~/Projects/Django/App2
$ sudo docker run -it --name APP1 --link redis-master:db -v ~/Projects/Django/App1:/user/src/app django /bin/bash
$ sudo docker run -it --name APP2 --link redis-master:db -v ~/Projects/Django/App2:/user/src/app django /bin/bash
HAProxy:
$ mkdir -p ~/Projects/HAProxy
$ sudo docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash
检查source目录挂载:
$ sudo docker ps | grep master
4e4e597ffcb6 redis "docker-entrypoint.sh"
43 minutes ago Up 43 minutes 6379/tcp redis-master
$ sudo docker inspect 4e4e | grep Source
"Source":
"/var1/DockerRepo/docker/volumes/1dc31736abd411569cfbc51c6624125867883b44733f40113e2f918770843438/_data",
下载redis.conf文件:
$ wget http://download.redis.io/redis-stable/redis.conf
$ cp redis.conf /var1/DockerRepo/docker/volumes/1dc31736abd411569cfbc51c6624125867883b44733f40113e2f918770843438/_data
修改redis.conf内容:
bind 0.0.0.0
进入到容器后的操作:
# mkdir /var/lib/redis
# cd /data
# cp redis.conf /usr/local/bin
# cd /usr/local/bin
# redis-server redis.conf
Slave节点的配置:
# docker inspect f88a69ca2b1e | grep Source
"Source": "/var1/DockerRepo/docker/volumes/6b211a6e469f4864a723dd7531f49674eeb9af9509ddcb75de8f18f4a677b85f/_data",
# cd /var1/DockerRepo/docker/volumes/6b211a6e469f4864a723dd7531f49674eeb9af9509ddcb75de8f18f4a677b85f/_data
# cp /home/dash/redis.conf ./
# vim redis.conf
slaveof master 6379
# docker inspect 14bbec8a900b | grep Source
"Source": "/var1/DockerRepo/docker/volumes/a51e5a23a9b2e36350325aac6c533a48beb34d566b08773d48a2a3273c786d42/_data",
# cp redis.conf /var1/DockerRepo/docker/volumes/a51e5a23a9b2e36350325aac6c533a48beb34d566b08773d48a2a3273c786d42/_data
启动流程和Master一样。
Django配置, 注意要在我们映射的目录下:
root@7243f0d5a231:/user/src/app# mkdir dockerweb
root@7243f0d5a231:/user/src/app# cd dockerweb/
root@7243f0d5a231:/user/src/app/dockerweb# cd redisweb/
root@7243f0d5a231:/user/src/app/dockerweb/redisweb# python manage.py startapp helloworld
修改django文件:
$ pwd
/user/src/app
$ vim dockerweb/redisweb/helloworld/views.py
from django.shortcuts import render
from django.http import HttpResponse
#Create your views here
import redis
def hello(request):
str = redis.__file__
str += "<br>"
r = redis.Redis(host='db', port=6379, db=0)
info = r.info()
str += ("Set Hi <br>")
r.set('Hi', 'HelloWorld-APP1')
str += ("Get Hi: %s <br>" % r.get('Hi'))
str += ("Redis Info: <br>")
str += ("Key: Info Value")
for key in info:
str += ("%s: %s <br>" % (key, info[key]))
return HttpResponse(str)
添加helloworld程序到INSTALLED_APPS选项下:
$ vim dockerweb/redisweb/redisweb/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'helloworld',
]
修改urls.py:
$ vim dockerweb/redisweb/redisweb/urls.py
from django.conf.urls import url
from django.contrib import admin
from helloworld.views import hello
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^helloworld$', hello),
]
App1和App2的差别在于views.py里改掉这一行:
- r.set('Hi', 'HelloWorld-APP1')
+ r.set('Hi', 'HelloWorld-APP2')
之后运行:
# python manage.py makemigrations
# python manage.py migrate
# python manage.py createsuperuser
分别运行:
#### APP1
# python manage.py runserver 0.0.0.0:8001
#### APP2
# python manage.py runserver 0.0.0.0:8002
HAProxy配置:
# cd ~/Projects/HAProxy
# vim haproxy.cfg
global
log 127.0.0.1 local0
maxconn 4096
chroot /usr/local/sbin
daemon
nbproc 4
pidfile /usr/local/sbin/haproxy.pid
defaults
log 127.0.0.1 local3
mode http
option dontlognull
option redispatch
retries 2
maxconn 2000
balance roundrobin
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms
listen redis_proxy
bind 0.0.0.0:6301
stats enable
stats uri /haproxy-stats
server APP1 APP1:8001 check inter 2000 rise 2 fall 5
server APP2 APP2:8002 check inter 2000 rise 2 fall 5
在haproxy容器里:
# cd /tmp
# cp haproxy.cfg /usr/local/sbin/
# cd /usr/local/sbin
# haproxy -f haproxy.cfg
现在验证:
http://127.0.0.1:6301/helloworld

http://127.0.0.1:6301/haproxy-stats:

Jan 3, 2017
TechnologyGuestBook
注意修改imagePullPolicy为IfNotPresent, 创建服务的步骤分别为:
$ kubectl create -f redis-master-deployment.yaml
$ kubectl create -f redis-master-service.yaml
$ kubectl create -f frontend-deployment.yaml
$ kubectl create -f frontend-service.yaml
现在得到其运行状态:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
frontend-88237173-02dvl 1/1 Running 0 2h
frontend-88237173-r7g3v 1/1 Running 0 2h
frontend-88237173-vjbv5 1/1 Running 0 2h
redis-master-4154998525-f186t 1/1 Running 0 2h
redis-slave-132015689-3qh7b 1/1 Running 0 2h
redis-slave-132015689-hpw88 1/1 Running 0 2h
可以用proxy-forward直接访问某个pod中暴露出来的frontend服务:
$ kubectl port-forward frontend-88237173-02dvl 9081:80
上述命令的意思是,将pod frontend-88237173-02dvl80端口的流量转发到
本地的9081端口,则可以通过访问http://127.0.0.1:9081来访问frontend.
或者,我们可以在service文件中指定服务类型为NodePort, 定义文件修改如下:
spec:
type: NodePort
ports:
- port: 80
nodePort: 31080
服务创建以后,访问http://CoreOS1IP:31080则可访问到guestbook前端页面,三个CoreOS
节点的31080端口均可提供前端页面访问。