首页 文章 精选 留言 我的

精选列表

搜索[桌面应用程序],共10005篇文章
优秀的个人博客,低调大师

应用程序内部任意界面退出程序

创建工具类如下: package com.example.hxd.gittest; import android.app.Activity; import java.util.ArrayList; import java.util.List; /** * 统一退出程序的操作 */ class ActivitySetting { //创建集合存储打开的Activity static List<Activity> activityList = new ArrayList<>(); //添加打开的Activity到集合 static void addActivity(Activity activity) { activityList.add(activity); } //移除集合内部的Activity static void removeActivity(Activity activity) { activityList.remove(activity); } //关闭所有的Activity static void finishAllActivity() { for (Activity activity : activityList) { if (!activity.isFinishing()) { activity.finish(); //杀死当前应用进程 android.os.Process.killProcess(android.os.Process.myPid()); } } } } 在BaseActivity内部添加如下代码: package com.example.hxd.gittest; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_base); //添加当前操作的Activity到集合内部 ActivitySetting.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); //移除无用的Activity ActivitySetting.removeActivity(this); } } 具体Activity内部代码如下: btnSecond.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //点击按钮退出程序,杀死进程 ActivitySetting.finishAllActivity(); } });

优秀的个人博客,低调大师

spark 应用程序性能优化经验

一 常规性能调优 1 .分配更多资源 --num-executors 3 \ 配置executor的数量 --driver-memory 100m \ 配置driver的内存(影响不大) --executor-memory 100m \ 配置每个executor的内存大小 --executor-cores 3 \ 配置每个executor的cpu core数量 增加每个executor的内存量。增加了内存量以后,对性能的提升,有两点: 1、如果需要对RDD进行cache,那么更多的内存,就可以缓存更多的数据,将更少的数据写入磁盘,甚至不写入磁盘。减少了磁盘IO。 2、对于shuffle操作,reduce端,会需要内存来存放拉取的数据并进行聚合。如果内存不够,也会写入磁盘。如果给executor分配更多内存以后,就有更少的数据,需要写入磁盘,甚至不需要写入磁盘。减少了磁盘IO,提升了性能。 3、对于task的执行,可能会创建很多对象。如果内存比较小,可能会频繁导致JVM堆内存满了,然后频繁GC,垃圾回收,minor GC和full GC。(速度很慢)。内存加大以后,带来更少的GC,垃圾回收,避免了速度变慢,速度变快了。 2.调节并行度 很简单的道理,只要合理设置并行度,就可以完全充分利用你的集群计算资源,并且减少每个task要处理的数据量,最终,就是提升你的整个Spark作业的性能和运行速度。 3.重构RDD架构以及RDD持久化 第一,RDD架构重构与优化 尽量去复用RDD,差不多的RDD,可以抽取称为一个共同的RDD,供后面的RDD计算时,反复使用。 第二,公共RDD一定要实现持久化北方吃饺子,现包现煮。你人来了,要点一盘饺子。馅料+饺子皮+水->包好的饺子,对包好的饺子去煮,煮开了以后,才有你需要的熟的,热腾腾的饺子。 现实生活中,饺子现包现煮,当然是最好的了;但是Spark中,RDD要去“现包现煮”,那就是一场致命的灾难。 对于要多次计算和使用的公共RDD,一定要进行持久化。 持久化,也就是说,将RDD的数据缓存到内存中/磁盘中,(BlockManager),以后无论对这个RDD做多少次计算,那么都是直接取这个RDD的持久化的数据,比如从内存中或者磁盘中,直接提取一份数据。 第三,持久化,是可以进行序列化的 如果正常将数据持久化在内存中,那么可能会导致内存的占用过大,这样的话,也许,会导致OOM内存溢出。 当纯内存无法支撑公共RDD数据完全存放的时候,就优先考虑,使用序列化的方式在纯内存中存储。将RDD的每个partition的数据,序列化成一个大的字节数组,就一个对象;序列化后,大大减少内存的空间占用。 序列化的方式,唯一的缺点就是,在获取数据的时候,需要反序列化。 如果序列化纯内存方式,还是导致OOM,内存溢出;就只能考虑磁盘的方式,内存+磁盘的普通方式(无序列化)。 内存+磁盘,序列化 第四,为了数据的高可靠性,而且内存充足,可以使用双副本机制,进行持久化 持久化的双副本机制,持久化后的一个副本,因为机器宕机了,副本丢了,就还是得重新计算一次;持久化的每个数据单元,存储一份副本,放在其他节点上面;从而进行容错;一个副本丢了,不用重新计算,还可以使用另外一份副本。 这种方式,仅仅针对你的内存资源极度充足 4.广播大变量 广播变量,初始的时候,就在Drvier上有一份副本。 task在运行的时候,想要使用广播变量中的数据,此时首先会在自己本地的Executor对应的BlockManager中,尝试获取变量副本;如果本地没有,那么就从Driver远程拉取变量副本,并保存在本地的BlockManager中;此后这个executor上的task,都会直接使用本地的BlockManager中的副本。 executor的BlockManager除了从driver上拉取,也可能从其他节点的BlockManager上拉取变量副本,举例越近越好。 每个 Executor一个副本,不一定每个节点。 5.使用Kryo序列化 内存占用,网络传输 1、算子函数中使用到的外部变量 2、持久化RDD时进行序列化,StorageLevel.MEMORY_ONLY_SER 3、shuffle 1、算子函数中使用到的外部变量,使用Kryo以后:优化网络传输的性能,可以优化集群中内存的占用和消耗 2、持久化RDD,优化内存的占用和消耗;持久化RDD占用的内存越少,task执行的时候,创建的对象,就不至于频繁的占满内存,频繁发生GC。 3、shuffle:可以优化网络传输的性能 bbg 使用时,要自定义注册类哦 6.使用 fastutil 7.数据本地化的等待时长 Spark在Driver上,对Application的每一个stage的task,进行分配之前,都会计算出每个task要计算的是哪个分片数据,RDD的某个partition;Spark的task分配算法,优先,会希望每个task正好分配到它要计算的数据所在的节点,这样的话,就不用在网络间传输数据; 但是呢,通常来说,有时,事与愿违,可能task没有机会分配到它的数据所在的节点,为什么呢,可能那个节点的计算资源和计算能力都满了;所以呢,这种时候,通常来说,Spark会等待一段时间,默认情况下是3s钟(不是绝对的,还有很多种情况,对不同的本地化级别,都会去等待),到最后,实在是等待不了了,就会选择一个比较差的本地化级别,比如说,将task分配到靠它要计算的数据所在节点,比较近的一个节点,然后进行计算。 但是对于第二种情况,通常来说,肯定是要发生数据传输,task会通过其所在节点的BlockManager来获取数据,BlockManager发现自己本地没有数据,会通过一个getRemote()方法,通过TransferService(网络数据传输组件)从数据所在节点的BlockManager中,获取数据,通过网络传输回task所在节点。 对于我们来说,当然不希望是类似于第二种情况的了。最好的,当然是task和数据在一个节点上,直接从本地executor的BlockManager中获取数据,纯内存,或者带一点磁盘IO;如果要通过网络传输数据的话,那么实在是,性能肯定会下降的,大量网络传输,以及磁盘IO,都是性能的杀手。 二.JVM调优 JVM调优的第一个点:降低cache操作的内存占比 spark中,堆内存又被划分成了两块儿,一块儿是专门用来给RDD的cache、persist操作进行RDD数据缓存用的;另外一块儿,就是我们刚才所说的,用来给spark算子函数的运行使用的,存放函数中自己创建的对象。 默认情况下,给RDD cache操作的内存占比,是0.6,60%的内存都给了cache操作了。但是问题是,如果某些情况下,cache不是那么的紧张,问题在于task算子函数中创建的对象过多,然后内存又不太大,导致了频繁的minor gc,甚至频繁full gc,导致spark频繁的停止工作。性能影响会很大。 针对上述这种情况,大家可以在之前我们讲过的那个spark ui。yarn去运行的话,那么就通过yarn的界面,去查看你的spark作业的运行统计,很简单,大家一层一层点击进去就好。可以看到每个stage的运行情况,包括每个task的运行时间、gc时间等等。如果发现gc太频繁,时间太长。此时就可以适当调价这个比例。 降低cache操作的内存占比,大不了用persist操作,选择将一部分缓存的RDD数据写入磁盘,或者序列化方式,配合Kryo序列化类,减少RDD缓存的内存占用;降低cache操作内存占比;对应的,算子函数的内存占比就提升了。这个时候,可能,就可以减少minor gc的频率,同时减少full gc的频率。对性能的提升是有一定的帮助的。 一句话,让task执行算子函数时,有更多的内存可以使用。 spark.storage.memoryFraction,0.6 -> 0.5 -> 0.4 -> 0.2 --conf spark.yarn.executor.memoryOverhead=2048 spark-submit脚本里面,去用--conf的方式,去添加配置;一定要注意!!!切记,不是在你的spark作业代码中,用new SparkConf().set()这种方式去设置,不要这样去设置,是没有用的!一定要在spark-submit脚本中去设置。 spark.yarn.executor.memoryOverhead(看名字,顾名思义,针对的是基于yarn的提交模式) 默认情况下,这个堆外内存上限大概是300多M;后来我们通常项目中,真正处理大数据的时候,这里都会出现问题,导致spark作业反复崩溃,无法运行;此时就会去调节这个参数,到至少1G(1024M),甚至说2G、4G 通常这个参数调节上去以后,就会避免掉某些JVM OOM的异常问题,同时呢,会让整体spark作业的性能,得到较大的提升。 http://blog.csdn.net/hammertank/article/details/48346285 此时呢,就会没有响应,无法建立网络连接;会卡住;ok,spark默认的网络连接的超时时长,是60s;如果卡住60s都无法建立连接的话,那么就宣告失败了。 碰到一种情况,偶尔,偶尔,偶尔!!!没有规律!!!某某file。一串file id。uuid(dsfsfd-2342vs--sdf--sdfsd)。not found。file lost。 这种情况下,很有可能是有那份数据的executor在jvm gc。所以拉取数据的时候,建立不了连接。然后超过默认60s以后,直接宣告失败。 报错几次,几次都拉取不到数据的话,可能会导致spark作业的崩溃。也可能会导致DAGScheduler,反复提交几次stage。TaskScheduler,反复提交几次task。大大延长我们的spark作业的运行时间。 实际案例脚本: /usr/local/spark/bin/spark-submit \ --class com.ibeifeng.sparkstudy.WordCount \ --num-executors 80 \ --driver-memory 6g \ --executor-memory 6g \ --executor-cores 3 \ --master yarn-cluster \ --queue root.default \ --conf spark.yarn.executor.memoryOverhead=2048 \ --conf spark.core.connection.ack.wait.timeout=300 \ /usr/local/spark/spark.jar \ 三.Shuffle调优 new SparkConf().set("spark.shuffle.consolidateFiles", "true") 开启shuffle map端输出文件合并的机制;默认情况下,是不开启的,就是会发生如上所述的大量map端输出文件的操作,严重影响性能。 增大map端溢写的内存缓冲空间,减少溢写次数。 spark.shuffle.file.buffer ,32k--》 64k 增大reduce端聚合内存,减少读写次数spark.shuffle.memoryFraction ,0.2--》0.3 尝试性的增加 new SparkConf().set("spark.shuffle.manager", "hash"):hash(默认)、sort(可以排序)、tungsten-sort钨丝(1.5版本后才有,不稳定) 四.spark操作调优(算子调优) 1.MapPartitions替代map操作,不过看具体操作,因为Maprtition容易导致OOM哦! 2.filter之后,数据容易倾斜,采用coalesce算子。主要就是用于在filter操作之后,针对每个partition的数据量各不相同的情况,来压缩partition的数量。减少partition的数量,而且让每个partition的数据量都尽量均匀紧凑。 从而便于后面的task进行计算操作,在某种程度上,能够一定程度的提升性能。 3.foreachPartition替代foreach,例如数据库连接操作的时候,是非常好的。在实际生产环境,都是用这个,但数据量特别大,会有oom的可能。 4.repartition,SparkSQL的初始stage受限于hdfs的block数量限制。repartition算子,你用Spark SQL这一步的并行度和task数量,肯定是没有办法去改变了。但是呢,可以将你用Spark SQL查询出来的RDD,使用repartition算子,去重新进行分区,此时可以分区成多个partition,比如从20个partition,分区成100个。 5.reduceBykey,map 端本地聚合。 本文转自里冲51CTO博客,原文链接:http://blog.51cto.com/coollast/1889258 ,如需转载请自行联系原作者

优秀的个人博客,低调大师

在 Docker 容器中运行应用程序

案例说明 运行 3 个容器,实现对网站的监控。 三个容器的说明: 容器web: 创建自 nginx 映像,使用 80 端口,运行于后台,实现 web 服务。 容器mailer: 该容器中运行一个 mailer 程序,运行于后台,当接收到事件后会向管理员发送邮件。 容器agent: 该容器运行一个 watcher 程序,以交互模式运行,用于不断地监测 web 服务的运行情况,一旦出现故障会立即向mailer容器发送消息。 创建容器 创建并运行 web 容器 $ docker run --detach --name web nginx:latest 命令执行后,docker 会从 Docker Hub 上下载nginx:latest映像文件,根据该映像文件开启一个容器,并在容器中运行 nginx 程序。 运行后,会输出一行字符串,该字符串为该容器的唯一标识符,类似7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5。通常我们可以将该标识符保存到一个变量里,以便于在其它命令中使用。 --detach选项使得该容器在后台运行,也可以用其缩写版本-d。 --name web将当前容器命名为web,以便之后引用。 创建并运行 mailer 容器 $ docker run -d --name mailer dockerinaction/ch2_mailer 创建并运行一个交互式的容器 agent 一个交互式的程序可以从用户获取输入或将输出显示到终端中。在 Docker 中运行交互式程序需要将你的终端绑定到容器的输入或输出上。 运行一个交互式容器如下: $ docker run --interactive --tty \ --link web:myweb \ --name web_test \ busybox:latest /bin/sh --interactive或-i选项告诉 Docker 为该容器开启标准输入 (stdin)。--tty或-t选项告诉 Docker 为该容器分配一个虚拟终端,以便于向容器发送信号。通常这两个选项是一起使用的,合记为-it。 --link web:web选项使得当前容器中能用myweb来引用 容器 web。 最后,/bin/sh是指定在该容器中运行的程序,运行后,可以在 sh 中运行wget -O - http://myweb:80/来检测容器 web 的运行情况。这里的wget命令实现向 nginx 服务器发送请求,并将获取的页面内容输出到终端上。 通用--tty开启的交互式容器,可以使用Ctrl-P Q来使其转入后台运行。 运行 agent 容器 $ docker run -it \ --name agent \ --link web:insideweb \ --link mailer:insidemailer \ dockerinaction/ch2_agent 该容器会每 1 秒对容器 web 检测一次,并输出类似System up.等信息。当看到这些信息后,可以用Ctrl-P Q将其转入后台运行。 容器命令 docker ps docker ps会列出每个正在运行的容器的下面信息: 容器 ID 使用的映像文件 在容器中运行的命令 自容器创建后的时间 容器已运行的时间 容器使用的端口号 容器的名称 重启容器 $ docker restart web $ docker restart mailer $ docker restart agent 查看容器的日志 $ docker logs web 由于容器 agent 对容器 web 进行了多次请求,故上面的命令会输出一长串的GET / HTTP/1.0" 200。 容器运行是的每条输出(或错误输出)都会保存到容器的日志文件中,因此,只要容器一直在运行,它的日志文件会不断的变大。由于没有截断的手段,因而最好用 Volume 来处理日志数据。 $ docker logs mailer mailer 的日志输出类似:CH2 Example Mailer has started. docker logs命令添加--follow或-f选项时,会一直保持运行,并持续显示最新的日志。可以用Ctrl C中断。 关闭容器 $ docker stop web 以上命令将中止容器中的 PID #1 程序的运行。 容器 web 中止后,容器 agent 将触发对容器 mailer 的请求,进而可以看到容器 mailer 中相关日志Sending email: To: admin@work Message: The service is down! 已解决的问题及 PID 命名空间 PID 命名空间是可用于标识进程的一个数集。Linux 可创建多个 PID 命名空间,每个命名空间中使用的 PID 相互独立,即每个命名空间都各自可使用 1, 2, 3 等而互不干扰。 Docker 默认为每个容器创建一个 PID 命名空间: $ docker run -d --name namespaceA \ busybox:latest /bin/sh -c "sleep 30000" $ docker run -d --name namespaceB \ busybox:latest /bin/sh -c "nc -l -p 0.0.0.0:80" 运行这两个容器后, $ docker exec namespaceA ps PID USER TIME COMMAND 1 root 0:00 /bin/sh -c sleep 30000 6 root 0:00 sleep 30000 7 root 0:00 ps $ docker exec namespaceB ps PID USER TIME COMMAND 1 root 0:00 /bin/sh -c nc -l -p 0.0.0.0:80 5 root 0:00 nc -l -p 0.0.0.0:80 6 root 0:00 ps 可以看到,每个容器中使用的 PID 都是独立的,例如都有 PID #1。 要使容器不创建自己的 PID 命名空间,在运行docker create或docker run时要加上--pid host选项: $ docker run --pid host busybox:latest ps 以上命令将列出机器上的所有运行中的进程。 Docker 解决的问题 Docker 基于 Linux namespace, file system roots, virtualized network components 实现的容器隔离性解决了如下的冲突问题: 多个程序想绑定到相同的端口 多个程序想使用相同的临时文件名 各程序想使用全局安装的代码库的不同版本 相同程序的不同进程想使用相同的 PID 文件 多个程序同时修改环境变量 消除 metaconflicts:创建一个网站集群 metaconflicts 即容器间的冲突。 继续上面的例子,这次开启多组 web + agent 容器对,然后只开启一个 mailer 容器,所有的 agent 都将事件发送给容器 mailer。 灵活的容器标识 当执行docker run -d --name webid nginx时,生成的容器的名称为 webid,容器的名称不能重复。当没有使用--name选项时,Docker 会自动为我们创建一个易读的唯一容器名。 也可以重命名容器: $ docker rename webid webid-old 每个容器还有一个 1024 位的十六进制编码的唯一 ID,如7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5。可以通过这个 ID 对该容器进行引用。如: docker stop \ 7cb5d2b9a7eab87f07182b5bf58936c9947890995b1b94f412912fa822a9ecb5 该 ID 值是完全唯一的,即永远不会冲突,若要想在同一台机器上保持唯一性,只需取其前 12 个字符长的字符串即可,因此,上面的命令也可以这样: ```bash docker stop 7cb5d2b9a7ea 容器 ID 值不适合人读,但可在脚本处理或自动化程序中使用。 如何获取容器 ID 当开启一个在后台运行的容器时,容器 ID 会自动输出到终端,因此可以获取。但如果开启的是交互式的容器,就不能获取 ID。这种情况下可以先用docker create命令创建一个容器(不立即运行),该命令和docker run的格式完成一样,同样也会输出容器的 ID。 将 ID 值保存到一个 Shell 变量中: CID=$(docker create nginx:latest) echo $CID 这种方式获取的 ID 只能在一个脚本或程序中使用,不能在多个程序间共享。如果要在多个程序间共享该容器 ID 值,可以将值保存在 container ID(CID) 文件中。docker run和docker create命令都可以用--cidfile选项指定 CID 文件的位置,如: $ docker create --cidfile /tmp/web.cid ngix 然后用cat /tmp/web.cid来获取该值。用这种方式时, CID 文件可能会冲突。幸运的是,当指定的 CID 冲突时(即该文件已经存在),Docker 会报错,不会创建该容器。 CID 文件可以在多个容器间共享,并且可以通过 Volume 功能进行重命名。 另一种获取 ID 的方式是使用docker ps: CID=$(docker ps --latest --quiet) # or CID=$(docker ps -l -q) echo $CID 这种方式获取的是截取的 12 字节长的 ID,要想获取整个 ID,要加--no-trunc选项。 容器 ID 不适合人使用,因此 Docker 还会为容器自动创建一个可读的唯一名字,名字的结构是: 一个形容词_某个名人的名字,如hungry_swartz,distracted_turing等。 容器的状态及其依赖 用脚本加载容器: MAILER_CID=$(docker run -d dockerinaction/ch2_mailer) WEB_CID=$(docker create nginx) AGENT_CID=$(docker create --link $WEB_CID:insideweb \ --link $MAILER_CID:insidemailer \ dockerinaction/ch2_agent) 以上针对 web, agent 容器的命令只是创建容器,还没有运行,因此docker ps默认不会列出 web, agent 这两个容器,要想查看所有状态的容器,使用docker ps -a。 容器的所有状态为: running, paused, restarting, exited 等。各状态相互转化如下: 容器创建后,再开启: docker start $AGENT_CID docker start $WEB_ID 但运行以上的命令会出错: Error response from daemon: Cannot start container 03e65e3c6ee34e714665a8dc4e33fb19257d11402b151380ed4c0a5e38779d0a: Cannot link to a non running container: /clever_wright AS /modest_hopper/ insideweb FATA[0000] Error: failed to start one or more containers 这是因为 agent 容器依赖于 web 容器,故要先启动 web 容器,如下: docker start $WEB_ID docker start $AGENT_CID 创建环境无关的系统 安装软件和维护的工作量主要在于对计算环境的定制。这种定制工作有: 全局依赖(如主机上的文件系统位置) 硬编码的部署架构(如在代码或配置中检测变量值) 数据的存储位置(如数据保存在一个特定的机器上) Docker 可以利用以下 3 个特性来帮助实现环境无关的系统,从而减少维护量: 只读文件系统 环境变量注入 Volume 本次实现的案例是使用 Docker 运行多个 WordPress 博客。每个博客共享 WordPress 程序,只是博客内容不同。 只读文件系统 使用--read-only选项开启一个只读的 WordPress 容器: $ docker run -d --name wp --read-only wordpress:4 --read-only确保该容器的内容不可修改。 执行后,再使用docker inspect --format "" wp来查看容器是否已经开启了,输出 true 和 false。 这里会输出 false,用docker logs wp查看日志: error: missing required WORDPRESS_DB_PASSWORD environment variable Did you forget to -e WORDPRESS_DB_PASSWORD=... ? (Also of interest might be WORDPRESS_DB_USER and WORDPRESS_DB_NAME.) 可见,WordPress 依赖 MySQL。 使用 docker 运行一个 Mysql 容器: $ docker run -d --name wpdb \ -e MYSQL_ROOT_PASSWORD=ch2demo \ mysql:5 上面命令中的-e选项向容器注入了一个环境变量值,以便容器使用。 现在再开启一个新的 WordPress 容器,并与 MySQL 数据库连接起来: $ docker run -d --name wp2 \ --link wpdb:mysql \ -p 80 --read-only \ wordpress:4 再查看该容器是否已正常运行: $ docker inspect --format "" wp2 发现还是没有启动,用docker logs wp2再次检查,可看到类似以下的日志: Fatal Error Unable to create lock file: Bad file descriptor (9) 可以看到因为 WordPress 容器是只读的,从而无法生成一个 lock 文件,而导致该容器启动失败。 因此,需要通过挂载 Volume 使该只读容器中的某些目录可写: # start the container with specific volumes for read only exceptions $ docker run -d --name wp3 --link wpdb:mysql -p 80 \ -v /run/lock/apach2/ \ -v /run/apache2/ \ --read-only wordpress:4 上面-v /datadir选项使得主机上的某个临时目录挂载到容器中的 /datadir 目录。 至此,一个可用于开启 WordPress 及监控程序的脚本如下: SQL_CID=$(docker create -e MYSQL_ROOT_PASSWORD=ch2demo mysql:5) docker start $SQL_CID MAILER_CID=$(docker create dockerinaction/ch2_mailer) docker start $MAILER_CID WP_CID=$(docker create --link $SQL_CID:mysql -p 80\ -v /run/lock/apache2/ -v /run/apache2/ \ --read-only wordpress:4) docker start $WP_CID AGENT_CID=$(docker create --link $WP_CID:insideweb \ --link $MAILER_CID:insidemailer \ dockerinaction/ch2_agent) docker start $AGENT_CID 环境变量注入 很多程序可根据环境变量进行配置。而 Docker 也会利用环境变量来共享主机名、容器等信息,同时还有可向容器注入环境变量的机制。 env命令可列出当前会话上下文里的所有环境变量值。向容器注入环境变量并显示: $ docker run --env MY_ENVIRONMENT_VAR="this is a test" \ busybox:latest env 上面的--env或-e选项可用来向容器注入环境变量值,如果映像里已经设置了该变量,那么本次设置会覆盖原来的设置值。 WordPress 用到下面这些环境变量: WORDPRESS_DB_HOST WORDPRESS_DB_USER WORDPRESS_DB_PASSWORD WORDPRESS_DB_NAME WORDPRESS_AUTH_KEY WORDPRESS_SECURE_AUTH_KEY WORDPRESS_LOGGED_IN_KEY WORDPRESS_NONCE_KEY WORDPRESS_AUTH_SALT WORDPRESS_SECURE_AUTH_SALT WORDPRESS_LOGGED_IN_SALT WORDPRESS_NONCE_SALT 创建 WordPress 容器时这样注入环境变量: $ docker create --env WORDPRESS_DB_HOST=<my_database_hostname> 、 --env WORDPRESS_DB_USER=site_admin \ --env WORDPRESS_DB_PASSWORD=MeowMix42 \ wordpress:4 要能开启多个 WordPress 容器,还需要为每个容器指定使用的数据库名: docker create --link wpdb:mysql \ -e WORDPRESS_DB_NAME=client_a_wp wordpress:4 docker create --link wpdb:mysql \ -e WORDPRESS_DB_NAME=client_b_wp wordpress:4 至此,可以更新启动脚本了: # 先启动 mysql 和 mailer 容器: DB_CLD=$(docker run -d -e MYSQL_ROOT_PASSWORD=ch2demo mysql:5) MAILER_CID=$(docker run -d dockerinaction/ch2_mailer) # 假设 $CLIENT_ID 变量会传入脚本 if [ ! -n "$CLIENT_ID" ]; then echo "Client ID not set" exit 1 fi WP_CID=$(docker create \ --link $DB_CID:mysql \ --name wp_$CLIENT_ID \ -p 80 \ -v /run/lock/apach2/ -v /run/apache2/ \ -e WORDPRESS_DB_NAME=$CLIENT_ID \ --read-only wordpress:4) docker start $WP_CID AGENT_CID=$(docker create \ --name agent_$CLIENT_ID \ --link $WP_CID:insideweb \ --link $MAILER_CID:insidemailer \ dockerinaction/ch2_agent) docker start $AGENT_CID 创建可持续运行的容器 Docker 的选项可用于监测并自动重启容器。 自动重启容器 在创建容器时,可用--restart选项指定以下的重启策略: 不重启(默认) 当检测到某种条件后才重启 不管什么情况问题重启 重启的等待时间采用 exponential backoff strategy。 采用这种方式重启会有空白时间,期间容器没有启动。 使用监管程序来保持容器运行 监管进程,或者 init 进程,可用来加载和维护其它进程状态。在 Linux 上,PID #1 是一个 init 进程,它用于开启所有其它系统进程,并且当出现异常时重启这些系统进程。 在容器中也可以采用类似的模式,主要可用的监管程序有 init, systemd, runit, upstart 和 supervisord 等。 Tutum 公司有一个 Docker 映像,包含 LAMP 和 supervisord,通过以下方式运行容器后可确保该容器一直运行: $ docker run -d -p 80:80 --name lamp-test tutum/lamp 可以用docker exec lamp-test ps来查看容器中当前运行的进程,可以看到运行有 supervisord, mysqld_safe 和 apache2 等进程。 PID TTY TIME CMD 1 ? 00:00:00 supervisord 439 ? 00:00:00 mysqld_safe 440 ? 00:00:00 apache2 827 ? 00:00:00 ps 现可以测试 supervisord 的监控重启功能,先 kill 掉 apache2 进程: $ docker exec lamp-test kill 440 # 440 是 apache2 的 PID 当 apache2 结束后,supervisord 会记录日志,并重启该进程,可用docker logs lamp-test查看: 2016-10-10 01:23:39,784 INFO exited: apache2 (exit status 0; expected) 2016-10-10 01:23:40,787 INFO spawned: 'apache2' with pid 841 2016-10-10 01:23:41,821 INFO success: apache2 entered RUNNING state, process has stayed up for > than 1 seconds (startsecs) 使用 entrypoint 脚本 相对于使用 init 或监管程序,另一种方法是使用启动脚本(通常的脚本名为 entrypoint.sh) 来至少检测容器能正常开启的一些先决条件。这个启动脚本有时也会用作容器的默认开启程序。 例如,之前开启的 WordPress 容器中就有一个启动脚本,它会在开启 WordPress 进程前先验证和配置相关的环境变量。可以通过覆盖默认命令为 cat 来查看该启动脚本: $ docker run --entrypoint="cat" wordpress:4 /entrypoint.sh 以上命令覆盖设置了默认命令为 cat, 同时最后的/entrypoint.sh为该默认命令的参数。 启动脚本 + Docker 的重启策略是确保容器持续运行的重要方式。 清理 所有的容器都会使用硬盘空间来存储日志、容器元数据和写入容器内的文件。所有容器也会消耗全局命名空间的资源(如容器名,主机端口绑定等)。因此,不再使用的容器应该要删除。 状态为exited的容器可以用docker rm container_name来删除,而其它状态(如 running, paused, restarting) 的容器必须用docker stop container_name先关闭后才能删除,或者用docker rm -f container_name来强制删除。 docker stop会向容器发送SIG_HUG信号,从而容器有时间来进行一些清理工作,而强制删除会向容器发送SIG_KILL信号,从而直接退出。docker kill命令也可用于向容器发送SIG_KILL信号 。 docker run命令加--rm选项时,该容器在运行退出后,即状态为 existed 时,会自动删除,如: $ docker run --rm --name auto-exit-test busybox:latest echo Hello World 下面的命令能删除所有的容器(没有退出的会强制删除): $ docker rm -vf $(docker ps -a -q) -v选项表示一并删除容器的 Volumes -q选项表示只列出容器的数字 ID 参考文献: 《Docker in Action》by Jeff Nickoloff: Running software in containers http://www.atjiang.com/running-software-in-docker-containers/

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册