Jasper Ji

博观而约取,厚积而薄发。

学习GTK

2021-06-02 19:49

GTK之前有看过,还专门购买了一本关于GTK的书籍,那是主要是为了学习C的面向对象实现。现在又看GTK是因为准备购买PinePhone,主要看中了PinePhone可以运行多个Linux发行版本。现在无论是iOS还是Android都不能自由的使用喜欢的语言。作为一个曾经的iOS开发者,iOS的运行速度还真的没得说。Android因为使用Java缘故,总还是没有办法更快似乎,所以一直想着整一台Linux手机自己定制UI,不过找一台Linux手机实在是太难了,主要是目前手机都是这些公司定制的硬件,所以驱动是个大问题。

为什么选择GTK呢?我也看了下QT,之前也简单的使用了下,确实非常适合开发UI程序,不过授权方面还是有点问题,GTK没有这个问题。另外无论C/C++对我来说都不是太大的问题,所以还是用GTK了。

考虑到主要是用来开发Linux的,所以直接在Ubuntu下跑个例子看下。

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdnoreturn.h>
#include <stdatomic.h>
#include <assert.h>
#include <gtk/gtk.h>
#include <glib/gprintf.h>

#ifndef var
#define var __auto_type
#endif

static void PrintMsg(GtkWidget *widget, gpointer window) {
	if(window != NULL)
		g_printf("Button clicked\n");

	var dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, u8"按钮已被按下!");
	gtk_window_set_title(GTK_WINDOW(dialog), u8"请注意");

	gtk_dialog_run(GTK_DIALOG(dialog));
	gtk_widget_destroy(dialog);
}

int main(int argc, char *argv[]) {
	gtk_init(&argc, &argv);
	var window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(GTK_WINDOW(window), "GTK Test");
	gtk_window_set_default_size(GTK_WINDOW(window), 640, 480);
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	gtk_container_set_border_width(GTK_CONTAINER(window), 15);

	var button = gtk_button_new_with_label("Button");
	gtk_widget_set_halign(button, GTK_ALIGN_START);
	gtk_widget_set_valign(button, GTK_ALIGN_START);
	gtk_widget_set_tooltip_text(button, "This is a button widget");
	g_signal_connect(button, "clicked", G_CALLBACK(PrintMsg), window); 
	gtk_container_add(GTK_CONTAINER(window), button);
	gtk_widget_show_all(window);
	g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	static_assert(sizeof(int) == 4, "Not compatible architecture!");
	puts("Will enter GTK runloop...");
	gtk_main();
	puts("Program terminated!");
	return 0;
}

问题

/usr/include/pango-1.0/pango/pango-coverage.h:28:10: fatal error: 'hb.h' file not found

解决方案运行pkgconf --cflags harfbuzz

/usr/bin/ld: cannot find -lBlocksRuntime

解决方案运行sudo apt-get install libblocksruntime-dev

编译

clang main.c -std=gnu11 -fblocks -lBlocksRuntime -I/usr/include/glib-2.0/ -I/usr/include/atk-1.0/ -I/usr/include/gdk-pixbuf-2.0/ -I/usr/include/cairo/ -I/usr/include/pango-1.0/ -I/usr/lib/x86_64-linux-gnu/glib-2.0/include/ -I/usr/include/gtk-3.0/ -I /usr/include/harfbuzz/ -L/usr/lib/x86_64-linux-gnu/ -lgtk-3 -lgobject-2.0 -lpangocairo-1.0 -lgio-2.0 -latk-1.0 -lgdk-3 -lglib-2.0    -o gtk-test

总结

GTK开发确实比较麻烦,后续还的配置个好的IDE环境,不过想想要求的OC开发,其实对于C还是蛮亲切的。其实一旦涉及真实项目,更多都是对框架的学习了,先挖个坑。

天童如净禅师

2021-05-27 11:33

去年专门去了趟宁波天童寺,雪窦山、净慈寺,几个跟如净禅师有关的地方,这样加深了我对禅师的一些了解。

《续指月录》有关于如净禅师的记录

慶元天童長翁如淨禪師

生而岐嶷。不類常童。長學出世法。參足庵鑑公於雪竇。看庭前柏樹子話有省。呈頌曰。西來祖意庭前柏。鼻孔寥寥對眼睛。落地枯枝纔[跳-兆+孛]跳。松蘿亮鬲笑掀騰。鑑頷之。出世屢主名剎。勑住天童。開爐上堂。召眾打圓相曰。箇是天童火爐。近前則燒殺。退後則凍殺。忽有箇漢出來道。合作麼生[囗@力]。火爐動也。便下座○上堂。霜風號肅殺。霜葉隨蕭[颱-台+(癸-天+虫)]。舉拂曰。看。惟有玲瓏巖。崔嵬望轉高。所謂天童滯貨。今朝短販一遭。莫有酬價底麼。下座巡寮○上堂。外不放入。內不放出。痛下一槌。萬事了畢。如何太白峰前令斬新。內外紀綱俱委悉○上堂。陸修靜。陶淵明。文殊普賢。作圓相曰。咦。一款具呈。且道憑誰批判。若是孔夫子。吾無隱乎爾○師六坐道場。未言稟承。眾有是請。師曰。我待涅槃堂裏拈出。臨終果拈香曰。如淨行脚四十餘年。首到乳峰。失脚墮於陷穽。此香今不免拈出。鈍置我住雪竇足庵大和尚。并書辭世頌曰。六十六年。罪犯彌天。打箇[跳-兆+孛]跳。活陷黃泉。咦,從來死生不相干。擲筆而逝。塔全身於本山。

葬在何处?

一种说法是葬在了天童寺的附近,我上次去的时候塔院正在维修,也没有看到塔。净慈寺的如净禅师塔,这个是网上能搜到的,我也亲自去拜祭过。

语录

现存的语录有两本《如净禅师语录》和《如净禅师续语录》,《如净禅师语录》比较完整,我刚完成了注音版的编辑工作,之前去天童寺的时候有请了本线装本的语录,内容包含两本的内容。唯一的缺点是因为是繁体竖排,另外很多的字不知道发音,尝试的读过一些,感觉还是有困难,携带其实也不方便。最近又在孔网上买了一本简体注音的32开本,确实让我读进去了一些内容,不过这个版本的问题是缺少了大概两页的内容,另外有些无法打出来的字体直接用问号显示,于是我想着还是自己整理一个版本吧,虽然不到一百页的内容,实际整理的时间也差不多有半个月时间,我整理的是繁体版,这次整理工作确实也让我学习了一些东西,目前初版算是完成了。后续我再校对一些发音和内容,没有问题的话,打算印本自己看看了。

《如净禅师语录》注音版我在Github上建了个项目,有兴趣的可以拉下来读读。项目地址

其他资料

道元禅师的《宝庆记》也是了解如净禅师比较好的资料,另外道元禅师的《正法眼藏》中也有不少如净禅师的记录。

关于RSS的思考

2021-05-04 13:55

缘起

17年的时候为了练习Swift,也想验证下自己对于产品方面的想法,当时流行的应用都是通过爬虫抓取内容网站的数据,清洗数据后再通过推荐引擎展示给用户,当时我们公司也做的是这类应用,只不过后来没有成功。作为练手的业余项目,我并没有打算也做类似的项目,那会我主要做iOS客户端,对于服务端也不是很在行,而且当时这类应用有个问题是,你需要搜集大量的内容网站,可这一定是用户喜欢的吗?当时想关于用户喜欢什么样的内容让用户自己去找吧,而我只提供一个工具,最后选定了RSS阅读器。在App Store上找了一些RSS应用调研,很多应用是国外人开发的,交互上不太符合国人的习惯,另外不少应用需要注册后才能使用,当然有些还是付费的。觉得还是有改进的空间的,目标是开发一款简单的应用,首先不需要登录注册之类的,用户添加自己的RSS源,然后拉取数据,展示详情,最后这是一款免费的应用。很快这款应用就被开发出来上线了,过了段时间我看了下数据,有不少用户评价,之前的应用用户很少评价,我一直觉得用户能评价应用很难,至少我自己都很少评价了。用户多半是通过搜索下载的,这让我想到了长尾效应,即使如此小众的应用,总有它的用户群体。

瓶颈

经过一年多的更新,应用基本上算是稳定了,18年底后就没有再更新过了,一方面工作有些变化,之前公司的新闻类应用已经不再运营了。而我也做了一年多的Unity游戏应用,iOS开发实际上很少了,之后离开了原来的公司。短暂的停留后,开始到现在的公司做服务器方面的开发,iOS的开发者账号也在到期后没有再续,一方面我想试下,如果账号不续费,应用还在商店吗?当然最主要的是,工作太忙iOS已经很久都不弄了,对于RSS阅读器应用,也没有更好的想法。最后每年花着钱,却没有一分的收入,这可能对于做一款免费应用者或多或少的有点失落吧,毕竟需要生活。苹果账号没有续费后,应用结果就被下架了。大概一年的时间,我没有再想关于苹果开发的事。

重启

21年四月份的时候,我又把苹果账号给续费了,在做企业应用的一年里,发现相比行业的复杂性,个人应用似乎更能好把握些吧。另外作为一个软件开发者,很大程度是依赖于工作的,工作上我们可以沉迷于技术的细节,但从宏观上来看,至少这么些年做的应用都是商业上的不成功吧,当然作为开发者很难去左右这个结果。而作为App Store的独立开发者可以很大程度上让自己在整个从产品的构想到设计,开发,到最后的营销及运营,都能有很大的锻炼。对于开发人员而言,App Store依旧施展自己创意和想法的最好平台。

于是我决定继续RSS阅读器的开发,iOS 13启用夜间模式后,之前的应用确实一些问题,新版本主要是适配夜间模式,之前有用户也提到是否可以开发夜间模式的显示,不过一直没有启动。因为界面比较少很快也就适配完了,内容显示那块我也学习到了如何使用CSS适配夜间模式。

另外做了一个决定新版本将只支持iOS 13以上的版本,夜间模式和Swift UI都只能在iOS 13以上的版本使用,只想专注在功能的开发上,而不是兼容上。我自己的iPhone 6s都已经升级到iOS 14了,所以这个决定也是有一定的依据的。

未来

关于RSS阅读器,我依旧没有更好的想法,实际上在新版本中我又去掉了几个已经失效的RSS源,而有些RSS源已经很久没有更新了。我一度想再找一些不错的源,可发现真的很难,很多都是好几年前的帖子,所以RSS阅读器的前景并不乐观,这是一块正在极速萎缩的市场,而很多开发者估计会放弃,更不可能成为一款公司的产品存在。难怪早在2012的时候谷歌就关闭了RSS阅读应用Google Reader。

Octopress升级

2021-05-02 00:15

升级其他软件的时候居然把Ruby版本升级到了3.0,而Octopress依赖的Jekyll的版本是2.0,对Ruby版本有限制,新版本用不了。本来想换个静态博客的框架来着,想着自己好歹也是全栈工程师,自己升级下吧。

Octopress其实是基于Jekyll的定制静态博客框架,生成静态内容主要依赖于Jekyll了。看了下Jekyll已经到4.0了,干脆直接升级到4.0了。不过发现一些问题,4.0后放弃了对RDiscount、Pygments的支持,默认的Markdown解析用的是Kramdown。Octopress分别使用了RDiscount、Pygments,所以这块只能弃用了,改用Kramdown。

升级后发现代码高亮有点问题,4.0因为弃用了Pygments,所以只能使用默认的Rouge。Octopress自己实现了一个代码高亮的样式,但是有兼容问题,最后直接弃用。使用Rouge生成Github的样式,最后修改了下,最终变成现在的样子,总之还是不太喜欢之前的暗背景的样式。

生成文章方面,因为Jekyll的一些变更,遇到三个问题,首先是文章列表是生序排序的,修改了下也好了。其次发现首页没有文章,主要是没有安装jekyll-paginate的缘故。最后就是生成的文章没有时间,Octopress自定义了一个时间格式的钩子,但是没有起作用,最后直接使用原生的方式解决了。

博客预览使用rake preview会报错,最后直接在Public目录下起了个Python的Web服务器,这个并不影响使用。

总结

现在还在用Octopress人确实不多了,多半都是不更新的僵尸博客了。其实这些静态博客解决方案原理大同小异的,之前有人吐槽Octopress生成博客文章慢,实际上主要是Jekyll慢了,不过现在都4.0了,目前我觉得速度还是能接受的。

既然Octopress作者都不维护了,想着后续问题修复的差不多了,把修改的版本放出了,也算是尽一份力吧。

参考:
Jekyll 4.0.0 Released

谈谈冥想

2021-04-26 23:06

坚持冥想已经有六七年了,略有点经验与大家分享下,一家之言,只供参考。

时间

冥想多长时间比较好?我目前是30分钟,之前一段时间都是1个小时,改成30分钟是因为后来找的工作太远,个人建议不低于30分钟为好。

什么时间冥想比较好?我是早上冥想了,下午很少冥想。首先早上精神比较好,不容易发困,下午冥想的时候往往劳累一天状态不是很好。早上冥想是因为时间比较好安排,只要你起的早,时间都是可以挪出来的。

姿势

首推双盘,其次单盘,最不好散盘。能单盘不要散盘,能双盘不要单盘。单盘基本上大家都可以,双盘如果身体不够瘦或者腿实在太硬,还是有点难度的。不过双盘只是时间问题了,我单盘五年一直双盘不上,基本上都已经放弃对双盘的想法,不过五年后的某天我居然可以双盘了,所以现在觉得双盘这个只是时间的问题,可能每个人不太一样,不过就是不要太主观的设置一个时间点,顺其自然就好,功夫到了自然盘的上。

双盘和单盘冥想有什么差别,进入状态后其实差别不大,不过双盘确实比较稳。双盘比较难,很大程度是因为在一开始不熟练的情况下,疼痛会干扰到无法安心坐下去。双盘我目前采用先盘左腿,再盘右腿的吉祥坐,另外手印也采用右手放在左手掌的方式,目前自我感觉这样的姿势可以很好的安心双盘。

调身,总的来说身体瘦一点,下午少吃点,对于盘腿还是有帮助的。我断食21天时,双盘完全不是问题,所以还是不要自己太胖。

观呼吸

干坐着,心可能会更乱,一般人都会觉得坐不了多久,自己就想不坐了。我早期接触的时候,也是不得其法,一开始就那样坐着,时间比较短。后来去了禅寺的禅堂坐了几次香(打坐),下午香时间最长一个半小时,当时还是散盘,到最后腿巨痛,虽然坚持了下来,后来问一个坐的比较好的小姑娘,原来她在用数呼吸,我也学着使用这个方式。数呼吸,一呼一吸算一下,从1到10,重复,实际上比较难,你会发现你的心会莫名的飞出个念头,打乱数呼吸的节奏。不过你只要持续的数,专注了,我当时觉得时间一下子变短了,腿也不痛了,所以数呼吸对于摄心还是很有效果了。结果一数就是到现在了,关于数呼吸详细的内容可以参阅南传佛教的典籍《清静道论》。

心态

不管什么原因让你开始冥想,但不要对冥想有一个心理期许,放长远一点,用余生去练习,每个阶段你能收获什么取决于你走的有多远,而多远不是你主观可以预判的。虎头蛇尾的冥想我也见过几个,其实冥想对我而言更多获得了一种内心的平静,而我觉得这就够了。但很多人,不是这样想的,期许太多,最终没有能坚持下来。坚持冥想,全身心的投入到工作,让冥想成为一种习惯,不要太热衷,也不要太懈怠。

书籍

最好的依旧是坚持练习,少看书,偶尔看一点就可以了,多了反而是障碍。偏方法的推荐《清静道论》或者南传佛教相关的书籍,不过《清静道论》是根本。心态类的推荐《禅者的初心》,心态要对,这很关键。

如何开始?

去禅寺的禅堂学习冥想是我走过的路,正规的禅寺有一套很完备的机制,让你短时间内可以上路。不过很多人在禅寺的时候可以冥想,但出了禅寺就不行了,总是找各种理由。禅寺只是开始的地方,冥想之路才刚开始。

Spring Boot使用Elasticsearch

2021-04-26 05:23

主要是想把Elasticsearch整合到当前的系统中,用来搜索文章。

环境

Elasticsearch 7.12.0
Logstash 7.12.0
Mysql 8.0

数据同步

把Elasticsearch整合到现有系统意味需要把当前要搜索的数据添加到Elasticsearch里面,这就有个数据同步问题。因为是测试工程使用的是Logstash的全量数据同步。Logstash的新版本已经包含了Jdbc的插件,所以不需要安装。

input {
  jdbc {
      # myslq驱动,可以官网下载,我用的是Maven依赖已经下载的Jar文件
      jdbc_driver_library => ""
      # Mysql驱动类全类名,注意mysql8.x以上需要加cj
      jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
      # mysql连接url
      jdbc_connection_string => ""
      # mysql 用户名
      jdbc_user => ""
      # mysql 密码
      jdbc_password => ""
      # 设置定时任务,多久执行一次查询,默认一分钟,需要无延迟可使用schedule => "* * * * * *"
      schedule => "* * * * *"
      # 清空上次的sql_last_value记录
      clean_run => true
      # 要执行的sql(同步)语句,替换成你自己的sql
      statement => "SELECT * FROM t_article"
  }
}

output {
   elasticsearch {
      # es主机和端口
      hosts => ["127.0.0.1:9200"]
      # 同步数据在ES的索引名称,替换为你自己的。
      index => "my-cms"
      # es文档的id,表示使用mysql表的id
      document_id => "%{id}"
  }
}

生产环境的数据同步可以参考阿里云Elasticsearch

参考:
Jdbc input plugin

Spring Boot配置

添加Maven依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

添加Elasticsearch客户端配置类

@Configuration
@EnableElasticsearchRepositories(basePackages = "io.pratik.elasticsearch.repositories")
@ComponentScan(basePackages = { "io.pratik.elasticsearch" })
public class ElasticsearchClientConfig extends AbstractElasticsearchConfiguration {
	@Override
	@Bean
	public RestHighLevelClient elasticsearchClient() {
		// 配置连接信息
		final ClientConfiguration clientConfiguration = ClientConfiguration.builder().connectedTo("localhost:9200")
				.build();

		return RestClients.create(clientConfiguration).rest();
	}
    
    /**
     * 这个很重要,不添加的话无法使用@Filed注解
     */
    @Bean
    @Override
    public EntityMapper entityMapper() {

        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
                elasticsearchMappingContext(), new DefaultConversionService()
        );
        entityMapper.setConversions(elasticsearchCustomConversions());

        return entityMapper;
    }
}

测试文章类,必须要加type='_doc',不然无法使用。另外遇到的问题是我数据库的日期用的是Date话,一直提示解析错误,最后使用LocalDateTime后就好了。

@Data
@Document(indexName = "my-cms", type = "_doc")
public class EsArticle {
	@Id // org.springframework.data.annotation.Id
	private Integer id; // 文章ID

	@Field(type = FieldType.Text, analyzer = "ik_max_word")
	private String title; // 文章标题

	@Field(name = "content", type = FieldType.Text, analyzer = "ik_max_word")
	private String articleContent; // 文章正文内容

	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	@Field(name = "create_time")
	private LocalDateTime createTime; // 文章创建时间
}

创建Repository类。

public interface ElasticArticleRepository extends ElasticsearchRepository<EsArticle, Integer> {
	
}

生成Controller,使用Postman测试接口是否正常工作。

@RestController
@RequestMapping("/article")
public class SearchController { 
	@Autowired
    private ElasticArticleRepository elasticRepository;

    @PostMapping(value = "/search")
    public List<EsArticle> handleSearchRequest(
            @RequestParam Map<String, Object> requestParam) {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        // 构造ES查询条件
        String keyword = (String) requestParam.get("keyword");
        queryBuilder.should(QueryBuilders.matchPhraseQuery("title", keyword))
                .should(QueryBuilders.matchPhraseQuery("content", keyword));
 
        StopWatch watch = new StopWatch();
        watch.start(); // 开始计时
 
        Page<EsArticle> articles = (Page<EsArticle>)
                elasticRepository.search(queryBuilder);  // 检索数据
 
        watch.stop();  // 结束计时
        System.out.println(String.format("数据检索耗时:%s ms",
                watch.getTotalTimeMillis()));
 
        return articles.getContent();
    }
}

Spring Boot使用ELK

2021-04-23 08:31

是在Mac环境下跑的例子。

安装ELK相关软件

ELK主要指Elasticsearch、Logstash、Kibana,使用elastic.co提供的安装指南,使用Brew安装。

切换Tab

brew tap elastic/tap

安装软件

brew install elastic/tap/elasticsearch-full  # 安装Elasticsearch
brew install elastic/tap/logstash-full # Logstash
brew install elastic/tap/kibana-full # Kibana

可以在前台或者后台启动软件

brew services start elastic/tap/elasticsearch-full # 后台启动
elasticsearch # 前台启动

brew services start elastic/tap/logstash-full
logstash

brew services start elastic/tap/kibana-full
kibana

Spring Boot配置

Maven添加Logstash依赖

<dependency>
	<groupId>net.logstash.logback</groupId>
	<artifactId>logstash-logback-encoder</artifactId>
	<version>5.3</version>
</dependency>

在Resources目录下添加logback-spring.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/base.xml" />
    <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    	<!-- 配置输出地址 -->
        <destination>127.0.0.1:4560</destination>
        <!-- 日志输出编码 -->
        <encoder charset="UTF-8"
                class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>UTC</timeZone>
                </timestamp>
                <pattern>
                    <pattern>
                        {
                        "logLevel": "%level",
                        "serviceName": "${springAppName:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "rest": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="LOGSTASH" />
        <appender-ref ref="CONSOLE" />
    </root>
</configuration>

配置Logstash,创建一个logstash-sample.conf文件

input {
  tcp {
    mode => "server"
    host => "0.0.0.0"
    port => 4560
    codec => json
  }
}
output{
  elasticsearch {
     hosts => ["http://localhost:9200"] # Elasticsearch 地址
     index => "spring_cms-%{+YYYY.MM.dd}" # 这里我用spring_cms,可以替换为自己的。需要注意ES关于索引的命名规范
  }
}

使用配置文件重启Logstash服务

logstash  -f logstash-sample.conf

最后在项目中输出一段测试日志

logger.info("Hello,ELK");

最后在Kibana创建索引就应该能看到日志了。

额外工具

一开始发现Kibana中没有数据,猜测Elasticsearch没有收到数据,最后用到了ElasticSearch head这个可以查看Elasticsearch存储的工具,具体安装可以参考Github的说明。

运行这个工具遇到问题,发现ElasticSearch连接不上,原来是跨域访问的问题,使用brew安装ElasticSearch后会有如下目录

Data:    /usr/local/var/lib/elasticsearch/elasticsearch_xxxx/ # xxxx此处是你自己的电脑名
Logs:    /usr/local/var/log/elasticsearch/elasticsearch_xxxx.log # xxxx此处是你自己的电脑名
Plugins: /usr/local/var/elasticsearch/plugins/
Config:  /usr/local/etc/elasticsearch/

编辑配置文件nano /usr/local/etc/elasticsearch/elasticsearch.yml,添加如下配置。

http.cors.enabled: true
http.cors.allow-origin: "*"

最后就可以正常访问了。

总结

使用ELK访问日志确实很方便,实际开发中可能会使用云服务商提供ELK服务。

学习Docker使用二

2021-04-19 06:01

打包现有Java工程的Jar到Docker镜像,也是遇到了问题。

不熟悉Dockerfile

COPY 源 目标

一开始把源和目标的顺序给弄反了,但是还在镜像中有那个文件,结果执行java -jar xxx.jar老提示Error: Unable to access jarfile xxx.jar。最后登录到容器内部发现jar的文件大小不对,最后才反应过来把顺序弄反了。

登入容器内部

一开始为了诊断Java运行Jar文件不成功的问题,我就想看看容器内的文件是否已经在。最后用如下命令,登入容器,这个还是比较有用,不过应该去掉镜像的CMD命令。

docker run -t -i xxx_container /bin/bash

镜像大小

一开始用的FROM openjdk:8,打出的镜像有800多M,后来使用FROM openjdk:8-jdk-alpine,大小150多M,我的Jar包实际只有不到50M的样子,所以还是比较大了。

学习Kubernetes

2021-04-18 11:19

学习Docker后,发现还是得学习下k8s,目前使用Spring Cloud,引入k8s后原来现有的架构确实会有些变化,比如k8s的服务与Spring Eureka实际上有些功能相似的地方。最早我打算使用微服务的时候也是看中可以使用其他语言,但实际上使用了Eureka后,发现用其他语言还是有困难的,Java依旧是服务的首选。引入k8s后这似乎成为了一些可能,不过目前还没有使用来着。

书籍

关于k8s的书籍目前只买了《Kubernetes实战》,之前也是选了几本,但这本当时一看就觉得干货很多,不过在解决细节问题时还是依赖网路了,不过这次可能错了,关于k8s的资料网上的还是太少,解决细节的问题最后还是依靠这本书。这本书在理论和细节方面结合的很好。

使用本地镜像

我用的minikube,本地构建的doker镜像,我并没有推送到docker的镜像服务器,打出的docker镜像,其实还是蛮大的,Python和Flask,只是个简单的Hello World的例子,有800多M。再将这个推送到docker镜像服务器太慢了,实际我根本就没有成功,但拉取镜像速度还不错。

一开始我使用kubectrl run创建Pods。

kubectrl run testflask --image=testflask --port=5000 --generator=run/v1

上面这种方式还是有局限,网上说要把imagePullPolicy设置成IfNotPresent或者Never。但是怎么设置?网上的博文根本不说,最后还是从《Kubernetes实战》这书找到答案,使用配置文件来创建,创建一个flasktest.yaml的配置文件。这里需要注意的是配置文件中一定要使用空格缩进而不是Tab,貌似我用的vs code和textmate都是使用Tab缩进。

apiVersion: v1
kind: Pod
metadata:
  name: flasktest
spec:
  containers:
  - image: testflask
    name: flasktest-container
    imagePullPolicy: IfNotPresent # 拉取的策略
    ports:
    - containerPort: 5000 # 容器的端口
      protocol: TCP

通过配置文件来创建pods

kubectl create -f flasktest.yaml # 通过配置文件创建pods

最后发现还是有问题,这个镜像始终还是无法拉下来,网上搜索半天也没有找到个解决方案,最后打算直接使用docker的公共镜像docker/getting-started,估计是网络的问题,始终没有拉取下来。我一直把minikube的当成本地的了,docker也是共用一个,实际上犯了个严重的认知错误,minikube在VirtualBox创建了一个minikube虚拟机在运行,docker肯定与我本地的不是一个,《Kubernetes实战》书中提供了一种把本地的docker复制到minikube虚拟机中docker镜像库的方法。

docker save testflask | (eval $(minikube docker-env) && docker load)

可以使用minikube ssh登录到minikube虚拟机中,查看docker镜像是否已经复制过去了。最后顺利启动了,不过怎么访问服务呢?

kubectl expose rc flasktest --type=LoadBalancer --name flasktest-http

不过这里也个问题,提示replicationcontrollers "flasktest" not found。其实命令kubectrl run testflask --image=testflask --port=5000 --generator=run/v1中,--generator=run/v1是创建replicationcontroller的,但我使用的是配置文件创建,问题不大,修改下配置文件。如下:

apiVersion: v1
kind: ReplicationController
metadata:
  name: flasktest
spec:
  replicas: 3
  selector:
    app: flasktest
  template:
    metadata:
      labels:
        app: flasktest
    spec:
        containers:
        - image: testflask
          name: flasktest-container
          imagePullPolicy: IfNotPresent
          ports:
          - containerPort: 5000
            protocol: TCP

重新创建,再启动服务,服务时启动了,不过还是有问题,使用minikube没有外部的ip的访问,需要使用如下命令。不过这个端口是随机的,跟原来用docker的那个不太一样。

minikube service flasktest-http

总结

网络的问题使本来简单的入门变得复杂,另外k8s的资料目前确实不是很多,一些细节网上的很少有人总结出来。不过感觉方案虽好,但是镜像大,以及可能要自己部署私有的镜像,再到其他的,其实投入还是蛮多的。

学习Docker使用

2021-04-16 21:01

去年年底的时候,当时有简单的研究下Docker,不过只是下载了一些类似MongoDB的镜像运行了下,感觉Docker非常适合安装这些软件。最近开始看Kubernetes,Docker的研究难免,那么如何把现有项目打包成一个Docker镜像以及运行,去年好像有简单的试下,不过有点遗忘,再次运行发现一些问题。

准备

这里运行Flask的一个简单例子,只为演示。创建一个文件flask_test的文件夹,

目录文件

app.py
requirements.txt
Dockerfile

app.py,默认host是127.0.0.1,这里有个问题就是打包成镜像,无法访问端口。在mac和linux都一样,具体可以参考这篇文章《Connection refused? Docker networking and how it impacts your image》

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello, World!'

if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5000)

requirements.txt,这个主要是放置python依赖库,目前只有flask一项。

flask

Dockerfile,这个是Build Docker的配置文件,EXPOSE 5000这个是指定容器内部的端口的,在Mac使用Docker客户端启动镜像时会有出现在设置里面。在Linux上构建镜像时出现下载python依赖https的错误SSL: CERTIFICATE_VERIFY_FAILED,需要去掉验证,RUN pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org -r requirements.txt,下面这个是在Mac上可以运行的配置。

FROM python:3.9
WORKDIR /code
EXPOSE 5000

COPY requirements.txt ./
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple

COPY . .

CMD ["python", "app.py"]

构建

构建Docker镜像,在当前目录运行该命令,记得不要忽略这个.,另外镜像的名字不能出现大写,我一直开始写成testFlask,结果构建失败。

sudo docker build -t 'testflask' .

####运行 下面这个是以后台形式运行容器,8000是外部的访问端口,5000是容器内的端口。

docker run -p 8000:5000 -d testflask

常用命令

Mac上我会用Docker Desktop,不过Linux上还是命令行是王道。主要记几个常用的命令。

docker ps # 列出运行的容器
docker images # 列出当前的所有镜像
docker rm 容器ID # 删除指定的容器
docker rmi 镜像ID # 删除指定的镜像

后记

目前打包出的镜像居然有900多M,只有一个python和flask的依赖,觉得可能还是研究的不够。通过这个例子,发现还是得找本Docker的书籍系统的研究下,问题解决了,但是缺乏理论依据。

更新

因为初次接触docker,后来发现可以使用基础版本,修改Dockerfile文件FROM python:3.9-alpine,最后打包后50M。