德聚全的部落格

随便写一些开发的经验和生活的感悟


  • 首页

  • 归档

懒人实现一键上传

Posted on 2020-10-10 | In Mac,Linux,SSH

SSHPASS

在命令行中想上传文件到远端主机都要先 ssh 登录,知识贫瘠的我手动了很多年今天终于知道了可以用 sshpass 命令先保存密码再做对应操作

1
2
# 将dist文件发送到远程
sshpass -p ${serverPass} scp -o stricthostkeychecking=no -r dist/ root@${serverIP}:/home/web/movie-trailer

然而 MacOS 上默认是不带这个工具的,Homebrew 也无法直接安装,stackoverflow 上找到下面 2 种方法

1
2
3
4
5
6
7
8
9
10
# 安装的版本要看对应维护的rb脚本指定的是啥
brew install hudochenkov/sshpass/sshpass
brew install http://git.io/sshpass.rb
brew install esolitos/ipa/sshpass

# 源码编译(未验证)
curl -O -L https://sourceforge.net/projects/sshpass/files/sshpass/1.06/sshpass-1.06.tar.gz && tar xvzf sshpass-1.06.tar.gz
cd sshpass-1.06/
./configure
sudo make install

现在就可以愉快的上传了

ReactNative 经验总结

Posted on 2020-09-09

什么是 ReactNative

ReactNative 提供组件,可以使用 javascript 和 css 编写原生移动应用

ReactNative 有平台差异性,相同组件样式不同、交互不同、api 不同

ReactNative 仅仅是绘制界面,绝大部分功能(地图、音乐、视频、文件处理、社会化分享、支付)都需要依赖原生程序的支持

ReactNative 支持 CodePush,可以向已经发布的程序推送 js 包实现热更新

推荐使用的库

  • react-navigation
    路由导航组件,更新较快,4.x 不支持动态组件。安装这个库随之必装的是 react-native-gesture-handler,react-native-reanimated,react-navigation-stack,react-navigation-tabs
  • react-native-vector-icons
    图标控件

  • react-native-swiper
    Swiper 组件

  • react-native-webview
    WebView 组件

  • @react-native-community/async-storage
    本地缓存组件

  • react-native-svg
    配合react-native-svg-transformer可以编写 svg 组件

  • react-native-syan-image-picker
    图片选择、拍照组件, 因为 react-native-file-selector 中引用的 FileBrowser 有问题,暂时弃用

  • axios
    通信组件,RN 官方推荐的 Fetch 不支持拦截器

  • rn-fetch-blob
    网络传输文件

  • jpush jshare jcore
    极光推送,极光分享, 有相对详细的 react-native 集成文档

  • teaset
    国人写的 UI 组件

  • react-native-wechat
    微信登录、支付、分享

  • react-native-audio
    播放音频文件

  • react-native-video
    播放视频文件

  • react-native-sound
    控制音量

  • react-native-orientaion
    控制屏幕方向

  • react-native-image-zoom-viewer
    点击图片预览、放大、手势关闭

  • lottie-react-native
    播放 AE 动效

  • react-native-swipe-list-view
    侧滑列表

  • rn-placeholder
    骨架屏

环境配置

  • MAC OS 10.15
  • Xcode11
  • AndroidStudio 3.5
    按照指南完成环境配置

难点

  • ios
    CocoaPods 的仓库国内很难访问到,可以用先下载到本地的方式完成配置

  • android
    在 android/gradle.properties 文件中配置如下内容

1
2
3
4
5
# 使用代理
systemProp.http.proxyHost=127.0.0.1
systemProp.https.proxyHost=127.0.0.1
systemProp.http.proxyPort=1087
systemProp.https.proxyPort=1087

总之做 React Native 开发需要十分稳定的\$\$代理

编译问题

ios

使用 CocoaPods 的项目需要在 ios 目录下执行 pod install 安装全部依赖
然后打开.xcworkspace 文件编译

android

android 打包需要下载对应版本的 gradle 和 jar 包,强烈依赖翻墙

依赖库

react-native-fast-image

AppGlideModule 问题
在 build.gradle 中加入下面代码

1
2
3
project.ext {
excludeAppGlideModule = true
}

react-native-vector-icons

字体安装

ios
  • 在 Xcode 中新建 Group(新建的 Group 并不是一个真实的文件夹),然后将字体文件拉进去
  • 在 Info.plist 文件中添加 key,Fonts provided by application
  • 上面的 key 内添加项,每项的值都是字体名字,如 Ionicons.ttf,AntDesign.ttf
android

将字体放到 app/src/main/assets/fonts 目录下

react-native-amap3d

安卓模拟器蓝屏问题
解决方法

权限

ios

编辑 Info.plist 加入下列内容

1
2
3
4
5
6
<key>NSLocationWhenInUseUsageDescription</key>
<string>启用定位更加准确</string>
<key>NSCameraUsageDescription</key>
<string>需要打开您的相机</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>需要打开您的相册</string>

android

编辑 app/src/main/AndroidManifest.xml

1
2
3
4
5
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

需要深入理解

react-native-gesture-handler

TouchableOpacity 在安卓下没有 onPress 事件,因为要引用 react-native 中的同名类,自动引用总是引错

启动图片设置

点我观看

app 保持 portrait 模式

ios

Xcode 选择 TARGETS 项目,General 下的 Deployment Info 下的 DeviceOrientation 只勾选 Portrait

android

AndroidManifest.xml 中为 MainActivity 添加 android:screenOrientation=”portrait”

开源项目 example 的问题

很多开源项目都带有 example 目录方便查看效果,但因为 react-native 版本更新迭代相当快,如果不满足当前 react-native 版本的基本无法运行

react-native 版本更迭问题

react-native 更新非常快,应保持项目依赖的 react-native 大版本稳定在半年至一年更新,包括 android 和 ios 项目格式的更新

shuttle_bus_app_passenger 端

班车 APP 乘客

运行方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 安装依赖包
yarn

# 启动jsbundle服务器
yarn start

# 启动ios端
# 等与 react-native run-ios
yarn ios

# 启动android端
yarn android

# 清除安卓编译缓存
yarn clean:android

# 构建安卓
# 构建结果在android/app/build/outputs/apk/release/中
yarn build:android

# 构建ios
# 构建完成后将ios/bundle拖入xcode ${project_name}中
yarn build:ios

# 调试
yarn devtool
# 先运行devtool后运行模拟器可以进行界面调试
# 也可以自行安装后运行
# npm i -g react-devtools@3.6.3
# 安装也许会遇到electron无法下载的问题,先用npx方式完成缓存加载之后也许可安装或者挂代理

# 执行测试用例
yarn test

# 执行语法lint
yarn lint

# 切换开发环境到STAGE环境
STAGE=xxx yarn env:dev

# 切换打包环境到STAGE环境
STAGE=xxx yarn env:stage

# 给bin/下所有sh执行权限
yarn chmod

# 用工具给项目打版本号,git不提交但会自动打tag
yarn version

终极清缓存秘籍

1
2
# 可以清除修改了js构建方案后之前的顽固缓存
watchman watch-del-all && rm -rf $TMPDIR/react-native-packager-cache-* && rm -rf $TMPDIR/metro-bundler-cache-* && npm cache clean --force && npm start -- -- reset-cache

react-native-splash-screen

集成后安卓闪退问题,需要配置 res 下的 xml 文件

安卓 keystore 生成

1
2
3
4
5
6
7
8
signingConfigs {
release {
storeFile file('./android/app/keystore')
storePassword '12345678'
keyAlias 'passenger'
keyPassword '12345678'
}
}
1
2
3
4
# 生成keystore
keytool -genkey -alias shuttle_bus_app_passenger -keyalg RSA -keysize 2048 -validity 36500 -keystore keystore
# 查看keystore
keytool -list -v -keystore release.keystore

react-native-wechat 集成问题

版本 1.9.12

ios

因为 ReactNative0.59 已经支持自动 link 不需要进行手动链结动态库进来
其他步骤照旧

  • 手动链结库(无需配置)
  • 配置 URL Types
  • 配置 Info.plist
  • 配置 AppDelegate.m

android

因为自动 link,不需要编辑 1、2 步

  • 配置 android/settings.gradle(无需配置)
  • 配置 android/app/build.gradle(无需配置)
  • 配置 proguard-rules.pro
  • 配置 MainApplication.java
  • 在自己的子 package 下建立 wxapi 包
  • 配置微信分享,新建 WXEntryActivity,配置 AndroidManifest.xml
  • 配置微信支付,新建 WXPayEntryActivity,配置 AndroidManifest.xml

The modules [‘node_modules-react-native-wechat-lib-android-RCTWeChat’, ‘node_modules-react-native-wechat-lib-android-react-native-wechat-lib~1’] point to the same directory in the file system.
Each module must have a unique path.
不要配置上面的 1、2 步
安卓打包提示 WeChatModule.java:7: 错误: 程序包 android.support.annotation 不存在 进入 WeChatModule 包修改文件

1
2
//import android.support.annotation.Nullable;
import androidx.annotation.Nullable;

jpush-react-native

jcore-react-native 1.7.5
jpush-react-native 2.7.5

按文档完全可配置,如何调用需测试

自定义图标字体

教程

xcode 要将 icommon.ttf 拖拽到项目根路径, 在 info.plist 中添加 Fonts provided by application > icomoon.ttf

弹窗制作

方法 1: 高阶组件套壳
通过高阶组件在外部定义弹窗组件通过 redux state 触发渲染
优点:稳定
缺点:state 变更后会触发大面积的高阶组件重绘

方法 2: 使用 react-native-root-siblings
优点:快,直接提供了插入到根路径的功能,方便编写
缺点:hack 了 AppRegistry.registerComponent 方法,不知道新版本绘有何问题。所有组件需做好退回 hoc 编写的方案上

我选择快[Doge]

aliyun-oss-react-native

当前版本:1.0.0-alpha.7

ios 安装

ios 目录下 pod install

android 安装

在 android/settings.gradle 添加代码

1
2
include ':aliyun-oss-react-native'
project(':aliyun-oss-react-native').projectDir = new File(rootProject.projectDir, '../node_modules/aliyun-oss-react-native/android')

在 android/app/build.gradle 里增加代码

1
2
3
4
5
6
7
8
9
dependencies {
implementation project(':aliyun-oss-react-native')
}

遇到这种报错的
```bash
Manifest merger failed : Attribute application@allowBackup value=(false) from AndroidManifest.xml:16:7-34
is also present at [com.aliyun.dpa:oss-android-sdk:2.9.3] AndroidManifest.xml:18:9-35 value=(true).
Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:7:5-117 to override.

在 AndroidManifest.xml 中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.ccbuluo.shuttle_bus_app_passenger">

<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme"
+ tools:replace="android:allowBackup">

./gradlew assembleRelease 失败 #49
Execution failed for task ‘:aliyun-oss-react-native:verifyReleaseResources’.

修改

1
2
3
xmlns:tools="http://schemas.android.com/tools">
tools:replace="android:allowBackup"
android:allowBackup="true"

修改 node_modules/aliyun-oss-react-native/android/build.gradle

1
2
3
4
5
android {
<!-- 改成和自己的版本一致 -->
compileSdkVersion 28
buildToolsVersion '28.0.3'
}

Lottie

android

lottie 中带图片的需要在安卓对应目录下拷贝资源文件到[PROJECT FOLDER]/android/app/src/main/assets,在 react prop 中对应其子目录 imageAssetsFolder={‘lottie/animation_1’}

ios

在 Xcode 中左侧文件夹右键 “Add file to [Project]” 将文件添加进去重新编译即可

文件名需和 json 中的文件一致

编译打包

android

安卓虚拟机堆栈爆满问题
Expiring Daemon because JVM heap space is exhausted

在 android/app/build.gradle 中添加

1
2
3
4
5
6
android {
dexOptions {
javaMaxHeapSize "3g"
}
...
}

在 android/gradle.properties 中添加

1
2
org.gradle.daemon=true
org.gradle.jvmargs=-Xmx2560m

adb logcat AndroidRuntime:E *:S

手机插上线,打开 Android Studio logcat 面板 过滤 I/ReactNativeJS 级别 debug

启动图标

安卓启动图(内容尽量靠中间)
2560*1440
安卓图标
圆形,圆角
192x192
144x144
96x96
72x72
48x48

ios

ios xcode 升级带来的问题

Module compiled with Swift 5.1.3 cannot be imported by the Swift 5.2 compiler

这个问题是因为升级了新的编译器,之前已经用旧编译器生成过结果需要进入/Users/\${username}/Library/Developer/Xcode/DerivedData 将数据清除

也可以清除 Pods/目录,重新 pod install 一下

2020多半年总结

Posted on 2020-09-09

我的多半年总结

2019 年底我从工作了 6 年的公司离职了。原因嘛,内耗严重,没有升职空间。最关键的是技术负责人为了“降低成本”选了 LayUI 作为后端框架,完全背离整个技术行业的发展方向,这个没法忍。很多人可能没法接受我在新公司成立会上的举动,然而这就是我,我无法选择沉默,抛开利益谈理想都是耍流氓。强烈的个性促成了我选择前端工作,6 年里为这家公司构建了 3 套完整的后端框架,一套移动端组件库,高度可配置营销方案,完整的前端开发部署流程,甚至到广州拿项目的高并发架构,开发项目无数。我早已做到问心无愧。

后面去了一家以前老大负责的公司,中恒天汽车集团下属的越野部落。在西二旗。路挺远的,每天单程 2 小时。真不知那阵是怎么熬过来的,也许是一股热情维持的吧。去了那家公司薪资很好,职位前端架构师,带一个不错的小伙子做个 RN 项目。因为一直以来都为了上家公司利益服务,手机客户端这块算是个空白。心里一股劲就想搞一个东西出来,这回是真的用心做了。然而。。。

转过年遇上了新冠疫情,这段时间到复工一直在家开发。RN 新手的我克服了各种困难搭起了项目架构顺利开发,个中的心酸只有打碎牙往肚里咽了。一晃时间就来到了 4 月,App 再做一期就可以上线了,然而整个集团却开始不稳定起来,不发工资了。

3 月份的工资拖到了五一发,4 月的拖到了我离职才发。起初以为只是简单的疫情原因,后来才知道江湖上水有多深、有多混,我真是太单纯了。就这样我的 App 梦想以一种闹剧收场,黯然辞职。不过在这里我还是得感谢这位给我两次机会的领导,你是我人生路上的贵人,你在我心中的形象一直十分高大。

6 月收到了 FESCO 的 offer,自降 10k 工资,也是架构师。然而这也不踏实,一进来就大量的人员离职,直接背了前任们留下的各种锅,填了 3 个多月的坑。每天都在背锅填坑,忙忙碌碌的过了 3 个月。好在项目已经到了中后期,已经没有太多新需求。

如今的我已经到了 IT 民工的末期,不知道未来如何,只有过好每一天了。先定个小目标,搬砖到 40 岁。

ffmpeg

Posted on 2019-07-03 | Edited on 2020-09-09

mp4 生成 m3u8

在线视频直接播放mp4太大,网络跟不上

m3u8介君愁,切成一堆小文件,读取速度会很快

页面需使用 videojs 一类的播放框架才能支持m3u8

1
ffmpeg -i input.mp4 -codec: copy -bsf:v h264_mp4toannexb -start_number 0 -hls_time 10 -hls_list_size 0 -f hls output.m3u8

页面中如下

1
<source src="/output.m3u8" type="application/x-mpegURL">

docker stack

Posted on 2019-06-20 | Edited on 2020-09-09 | In Docker

docker stack

docker stack 需要在编写完 docker-compose(会读取Compose配置文件) 和创建好 docker swarm(使用Node Worker) 后使用

docker stack deploy

开启stack,更新stack
别名

docker stack up

部署docker-compose编排服务

1
2
3
4
docker stack deploy $stack_name -c docker-compose.yml

# $stack_name stack名称
# -c docker-compose配置文件

docker stack rm

停止stack,删除stack
别名

docker stack down

1
2
3
docker stack rm $stack_name

# $stack_name stack名称

docker stack ls

显示stack列表

别名

docker stack list

docker stack ps

显示正在运行的stack列表

1
2
3
4
5
6
7
docker stack ps
# 常用参数
# -a 显示全部容器
# -f 根据条件过滤(匹配关系),例name=worker
# -l 显示刚创建的容器
# -q 只返回容器id值
# -s 显示数量

docker stack services

显示stack中的服务

1
2
3
4
5
6
7
docker stack services $stack_name

# $stack_name stack名称

# 常用参数
# -f 过滤
# -q 返回服务id

实用技巧

读取日志

1
docker-compose ps | tail -n +3 | awk '{print $1}' | xargs -n1 docker logs

至此,我们已经完成了从编写Dockerfile跑webapp,再到编写docker-compose跑整个服务,再到集群化部署服务的过程。

写的比较简单,还需要遇到真实场景去做更复杂的处理

k8s

Posted on 2019-06-18 | Edited on 2020-09-09 | In Docker

Kubernetes

直接来到了k8s,为什么直接写k8s呢?因为简单啊!
我一个前端看tm什么k8s呢。本着探索的精神我只想在自己机器上跑起来而已。在这里只探索跑起来,因为跑起来已经很tm难啦。你还想玩的溜,多努力吧少年!

跑起来都很难

因为google的原因?所有k8s的资源都是被墙的,即便是挂着小飞机也不行。网上搜到的所有方案全部无效。直到我找到了github上一个仓库k8s-for-docker-desktop,解决了问题。

注:上面的方法只是解决Docker Desktop的Kubernetes,linux下的应该大致相同但又有不同

好吧,照着做就行了😊

docker-swarm

Posted on 2019-06-18 | Edited on 2020-09-09 | In Docker

docker swarm

docker swarm 是 docker亲生的管理docker集群的工具。kubernetes是google私生的,不过现在已经转正。

swarm

swarm是一个运行docker engine节点(node)的集合,每台跑着docker的机器都可以被视为一个节点。这个节点集合可以用来发布和编排服务。

节点可以主动初始化一个swarm或者加入一个已经存在的swarm。这样该节点就成为了swarm中的一个节点。

角色

swarm中的节点分为2种角色

manager

manager节点用于cluster的管理,swarm命令基本只能在manager节点执行。一个cluster可以有多个manager节点,但只有一个节点可以成为leader manager node,leader选主通过raft协议实现,参数可配置。

worker

worker节点是任务执行节点,manager将service下发至worker节点执行。

STATUS为空的是worker节点,Reachable的是非leader manager node。Manager节点默认也作为worker节点。

检查一个节点是否处于swarm

1
2
3
4
# 处于
Swarm: active
# 不处于
Swarm: inactive

创建集群

1
2
3
4
5
6
7
8
9
10
11
12
docker swarm init --listen-addr ip:port  --advertise-addr ip
# --listen-addr 监听ip:端口
# --advertise-addr 有多块网卡时制定端口

Swarm initialized: current node (kb7aqhna2cz9e3i3v4ewkc7sp) is now a manager.

To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-5jm4q3ern4f4u76wve3dofol2rav253of42ygxf6nk78t18re9-c35uk3qc4l2itli9rkt87qqem 19
2.168.65.3:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

创建集群的节点,自己就是manager了。会返回加入worker所需的token

获取加入角色 token

1
2
3
4
5
6
7
8
docker swarm join-token manager
To add a manager to this swarm, run the following command:

docker swarm join --token SWMTKN-1-5jm4q3ern4f4u76wve3dofol2rav253of42ygxf6nk78t18re9-cd8lmiift26osjk1cqqhpy1hy 192.168.65.3:2377
docker swarm join-token worker
To add a worker to this swarm, run the following command:

docker swarm join --token SWMTKN-1-5jm4q3ern4f4u76wve3dofol2rav253of42ygxf6nk78t18re9-c35uk3qc4l2itli9rkt87qqem 192.168.65.3:2377

加入集群

在其他节点上执行上面获取的命令即可

退出集群

1
2
docker swarm leave --force
# --force 强制退出

已经跑起服务的需要强制退出

Service与Task

Task是swarm中的最小原子调度单位,就是一个单一的容器
Service 是一组Task的集合,Service 定义 Task的属性

Service 有两种模式:

  • replicated service: 按一定规则在各节点上运行指定个数的Task

  • global service:每个节点上只运行一个task

因为有docker stack 绝大部分指令了解即可

创建服务
1
docker service create --replicas 2 --name helloworld --network=swarm_test nginx:alpine
查看服务列表
1
2
3
docker service ls
# ID NAME REPLICAS IMAGE COMMAND
# 5gz0h2s5agh2 helloworld 0/2 nginx:alpine
查看运行中服务
1
2
3
4
docker service ps helloworld
# ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR
# ay081uome3 helloworld.1 nginx:alpine manager1 Running Preparing 2 seconds ago
# 16cvore0c96 helloworld.2 nginx:alpine worker2 Running Preparing 2 seconds ago
服务扩容
1
2
docker service scale helloworld=3
# helloworld scaled to 3

可扩大,也可缩小

有点像一种神秘的柱状物(金箍棒)可大可小

回滚服务配置
1
docker service rollback $service_id
更新服务配置
1
docker service update $service_id
检查服务配置
1
docker service inspect $service_id
查看服务日志

使用方法

1
2
3
4
5
6
7
8
9
10
11
docker service logs $service_id

# --details Show extra details provided to logs
# -f, --follow Follow log output
# --no-resolve Do not map IDs to Names in output
# --no-task-ids Do not include task IDs in output
# --no-trunc Do not truncate output
# --raw Do not neatly format logs
# --since string Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
# --tail string Number of lines to show from the end of the logs (default "all")
# -t, --timestamps Show timestamps

等价于

1
docker logs $container_id

节点查询

1
2
3
docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
kb7aqhna2cz9e3i3v4ewkc7sp * linuxkit-025000000001 Ready Active Leader 18.09.2

网络

网络查询
1
2
3
4
5
6
docker network ls
# NETWORK ID NAME DRIVER SCOPE
# 764ff31881e5 bridge bridge local
# fbd9a977aa03 host host local
# 6p6xlousvsy2 ingress overlay swarm
# e81af24d643d none null local

docker swarm得网络配置是十分丰富的,而且自带服务发现功能

基于swarm搭配负载均衡工具这些实践还没有做

To Be Continue

docker-compose

Posted on 2019-06-18 | Edited on 2020-09-09 | In Docker

docker-compose

我曾经跨过山河大海、也穿过人山人海!!看过了前面那么多知识概念,自己的docker镜像终于跑起来了。但总感觉少了点什么,那就是老敲命令太tm烦了。尤其是container每次还的先删掉才能重新启动。docker run那么多参数哥真的记不住啊。一个项目要依赖很多服务,又要部署超多实例,没有带给我们丝毫的便利。于是有人来拯救我们了!

使用docker-compose可以轻松、高效的管理容器,它是一个定义和运行多容器Docker应用的工具。使用YAML文件来配置应用服务,只需要一行命令就可以创建运行配置的服务。

Compose文件

docker-compose主要围绕compose文件定义工作,从发布到目前一共有下面这些版本


































































Compose文件版本 Docker Engine版本需求
3.7 18.06.0+
3.6 18.02.0+
3.5 17.12.0+
3.4 17.09.0+
3.3 17.06.0+
3.2 17.04.0+
3.1 1.13.1+
3.0 1.13.0+
2.4 17.12.0+
2.3 17.06.0+
2.2 1.13.0+
2.1 1.12.0+
2.0 1.10.0+
1.0 1.9.1.+

不同版本的Compose文件格式要求不同的Docker Engine和docker-compose版本。之前就发生过Compose文件版本过高,docker-compose版本过低无法运行的窘境

Compose文件版本选择

可以使用最新的。也可以按阿里云一类的要求用对应版本的格式

Compose文件的编写

Compose文件使用YAML格式编写,这个格式在python,ruby等语言中比较流行

让我们来看看大搜车easy-mock服务的Compose文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# 声明版本
version: '3'

# service配置 - 固定格式
services:
# 服务名称-叫什么都行,自己起名字
mongodb:
# 依赖镜像
image: mongo:3.4
# 数据卷配置
# 将容器/data/db目录映射到宿主机上下文的./data/db目录,这个目录在宿主机可能会有权限问题,一般需提前创建好并chown 555 -R ./data/db解决.也可以配置service的privileged: true解决,但使用了这个文件夹权限会是root级别
volumes:
- './data/db:/data/db'
# 配置网络
networks:
# 容器可以发现easy-mock网络
easy-mock:
# 配置容器网络别名
aliases:
# webapp可以通过hostname mongodb来找到这台服务器,数据库链接字符串变成了mongodb://mongodb/easy-mock
- mongodb
# swarm 模式失效
# 一直重启
restart: always
# swarm 模式生效
deploy:
# 实例个数 1,webserver可以配多个
replicas: 1
# 资源配置
resources:
# 固定格式
limits:
# cpu单核的20%处理能力
cpus: ".2"
# 不能超过50兆内存
memory: 50M
redis:
image: redis:4.0.6
# 替换掉Dockerfile中的CMD指令
command: redis-server --appendonly yes
volumes:
- './data/redis:/data'
networks:
easy-mock:
# 其他同一网络下的可用 host redis来访问
aliases:
- redis
restart: always
deploy:
replicas: 1
resources:
limits:
cpus: "0.3"
memory: 50M

web:
image: easymock/easymock:1.6.0
command: /bin/bash -c "npm start"
# 将容器的7300端口映射到宿主机7300端口
ports:
- 7300:7300
volumes:
# 日志地址
- './logs:/home/easy-mock/easy-mock/logs'
networks:
- easy-mock
restart: always
deploy:
replicas: 1
resources:
limits:
cpus: "0.5"
memory: 200M
# network配置
networks:
# 创建名叫easy-mock的network
easy-mock:

编译自己的Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
version: '3'

services:

webapp:
# 赋予volume权限
privileged: true
# 编译本地的Dockerfile,文件名默认是Dockerfile
build: .
ports:
- "8888:3000"
volumes:
# 下面这个有个大坑就是会让自己Dockerfile的COPY到WORKDIR的内容全部消失,😂
# - ./:/code 加入这个会让dockerfile npm i 产生的 node_modules消失
- ./logs:/code/logs

详细的配置

使用环境变量

假设有文件 dev.sh

1
2
3
export NODE_RUN=dev
export NGINX_PORT=8089
export COMPOSE_PROJECT_NAME=oil_mall_admhtml_dev

在配置文件中${variable_name}引用

1
2
3
4
5
6
7
8
9
10
11
version: '3'

services:
webapp:
build: .
volumes:
- ./dist:/code/dist
environment:
- NODE_RUN
ports:
- ${NGINX_PORT}:80

使用方法

1
2
source dev.sh
docker-compose config

常用命令

启动

创建启动container

1
2
3
4
docker-compose -f docker-compose.yml up -d --build
# 重要参数
# -d, --detach 后台运行
# --build 开启容器前始终编译镜像

停止

停止并删除container,networks,images,volumes

1
2
3
4
5
docker-compose -f "docker-compose.yml" down

# 重要参数
# -v, --volumes 删除volumes
# --rmi type 删除镜像,all|local 全部或本地未加标签的镜像

检查单个容器环境

1
docker-compose run web env

单独的docker-compose只用于开发测试
配合 docker swarm 和 docker stack就是用于部署

疑难配置详解

command 和 entrypoint

command 和 entrypoint 最后必须能产生一个不退出进程的PID

command 和 entrypoint 指令会替换镜像 Dockerfile 中最后的 CMD 或 ENTRYPOINT 如果不太熟悉官方镜像尽量只传参数不要自己写命令

多行书写
1
2
3
4
5
6
7
8
9
10
  command:
- /bin/sh
- -c
- |
bundle config mirror.https://rubygems.org https://gems.ruby-china.org
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
bundle exec rake tmp:cache:clear tmp:sessions:clear RAILS_ENV=production
/docker-entrypoint.sh passenger start
# 或
command: ["sh", "-c", "cp -r /usr/src/redmine/public/. /www/public/ && /docker-entrypoint.sh"]

实战

设置Mysql时区,默认字符集等参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
services:
db:
image: mysql
restart: always
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci',
'--default-time-zone=+8:00',
'--default-authentication-plugin=mysql_native_password'
]
# 或
services:
db:
image: mysql
restart: always
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --default-time-zone=+8:00 --default-authentication-plugin=mysql_native_password

To Be Continue

Dockerfile

Posted on 2019-06-18 | Edited on 2020-09-09 | In Docker

Dockerfile

Dockerfile是用于构建Docker镜像的配置文件。在Dockerfile中包含了一些列构建镜像需要执行的命令操作。利用Docker镜像体积小的特点能够快速实现迁移部署。

Dockerfile就是一个名为Dockerfile的文件。

Dockerfile有自己的语法,但十分简单,只有2种:指令和注释

1
2
INSTRUCTION arguments
# 注释

Dockerfile 指令

FROM

使用FROM指定一个基础镜像

这个指令一般都出现在Dockerfile的第一行,接下来的所有工作都是基于FROM指令指定的镜像开展的。一个合法的Dockerfile必须以FROM指令开头。

  • ARG 指令允许在FROM之前出现
  • FROM 可以在单个Dockerfile中出现多次用来构建多个镜像或作为其他镜像的依赖。提交(commit)之前指令构建的镜像(image)获取ID,之后的FROM会清除前面指令的状态
  • FROM指令可以选择性的添加AS name参数来给予当前构建状态命名。命名可以被之后的FROM和COPY --from=<name|index>指令引用当前阶段的镜像(image)
  • tag和digest是可选的,如果忽略它们,构造器会默认添加latest标签。若没有找到tag会报错
1
2
3
FROM <image> [AS <name>]
FROM <image>[:<tag>] [AS <name>]
FROM <image>[@<digest>] [AS <name>]

WORKDIR

1
WORKDIR /path/to/workdir

WORKDIR指令设置工作目录。如果目录不存在则会创建。RUN, CMD, ENTRYPOINT, COPY, ADD都会受到影响

RUN

RUN指令用来执行shell命令,并且在当前镜像的最高层级上新创建一层并记录下来

RUN有2中格式,但我个人只使用第一种

第二种方式会被解析成JSON数组,所以里面的命令必须用双引号包裹

1
2
RUN <command>
RUN ["executable", "param1", "param2"]
默认使用shell命令

RUN默认使用/bin/sh shell执行命令

使用bash,当然有的极简镜像不包含bash…

1
RUN /bin/bash -c 'echo hello'
使用换行符

如果你的命令过长不易读可以使用换行符\来多行编写

1
2
RUN /bin/bash -c \
'source $HOME/.bashrc;
一次执行多条语句

通常不会一次只执行一条命令而是多条可以用;来分割

1
2
3
4
RUN npm config set \
registry http://r.cnpmjs.org/; \
npm i -g pm2; \
npm i -g gulp;

ENV

ENV指指令用来设置环境变量供之后的构建使用

1
2
ENV <key> <value>
ENV <key>=<value> ...

ENV <key> <value>设置一个变量,空格前的字符串设置为变量名,空格后的整个字符串设置为值,即便后面字符串中间有空格也被包含在内

ENV <key>=<value> ...允许一次设置多个变量,可以在值中间使用空格符

使用ENV设置环境变量会对镜像造成持久化的影响

替换ENV设置的方法

docker run --env key=value

不造成持久化影响

RUN <key>=<value> <command>

ARG

ARG命令用来定义变量,用户可以在构建镜像时传递参数

默认值

设置一个默认值,构建时没有传递值就使用默认的

1
2
3
FROM mysql
ARG user=myuser
ARG buildno=1
作用域

ARG 定义从当前行生效,并不从Dockerfile启动时或命令行调用时就生效

1
2
3
4
FROM busybox
USER ${user:-some_user}
ARG user
USER $user
1
docker build --build-arg user=what_user .

USER在第二行为some_user
USER在第四行为what_user

ENV与ARG区别

ENV 是对容器设置的环境变量,而ARG是编译变量.ENV值可覆盖ARG值,ENV值始终保持在镜像中

ADD

Docker团队推荐使用COPY,😊

COPY

1
2
COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]

COPY把<src>的文件和目录拷贝到<dest>中

支持通配符选择器

1
2
COPY hom* /mydir/        # 添加"hom"开头的所有文件
COPY hom?.txt /mydir/ # ?匹配任意字符
  • <src>路径必须在上下文(context)之内,不能超出范围
  • <src>使用相对路径
  • <dest>可以相对路径(相对WORKDIR)和绝对路径

VOLUME

1
VOLUME ["/data"]

VOLUME指令用来声明容器中的某个目录需要映射到某个数据卷volume

通过 VOLUME 指令创建的挂载点,无法指定主机上对应的目录,是自动生成的。

要指定宿主机目录需要配合docker run -v等命令

EXPOSE

1
EXPOSE <port> [<port>/<protocol>...]

EXPOSE指令通知容器在运行时监听指定的端口,可以明确指定是监听TCP还是UDP默认是TCP

EXPOSE指令并不能真正的暴露端口,只是一种指示作用。真正暴露端口还要和-p,-P,--link指令对应起来才可以使用

  • -p
1
docker run -p 8080:80/tcp -p 8080:80/udp ...

使用-p将容器的80端口绑定到宿主机的8080端口上,还可以分别指定方式

  • -P
1
docker run -P nginx:latest

并将容器的80端口映射到主机随机端口

  • --link
1
docker run -P nginx:latest

添加其他容器链接到这个容器,这时被链接的容器可访问这个端口

排列组合

A 既没有在Dockerfile里Expose,也没有run -p

启动在这个container里的服务既不能被host主机和外网访问,也不能被link的container访问,只能在此容器内部使用

B 只在Dockerfile里Expose了这个端口

启动在这个container里的服务不能被docker外部世界(host和其他主机)访问,但是可以通过container link,被其他link的container访问到

C 同时在Dockerfile里Expose,又run -p

启动的这个cotnainer既可以被docker外部世界访问,也可以被link的container访问

D 只有run -p

docker做了特殊的隐式转换,等价于情况C,既可以被外部世界访问,也可以被link的container访问到(真对这种情况,原因是docker认为,既然你都要把port open到外部世界了,等价于其他的container肯定也能访问,所以docker做了自动的Expose

ENTRYPOINT

下面是mysql Dockerfile的例子

1
2
ENTRYPOINT ["docker-entrypoint.sh"]
# sh文件内略过200+行shell脚本

ENTRYPOINT更适合用在mysql,nginx这种特定软件发行包里使用。即维护者不希望用户自己执行命令而是调用自己的入口文件

所以不再讨论这一块了

CMD

CMD指令是设置容器启动后默认执行的命令及其参数

CMD指令在Dockerfile中只能写一条,如果写了多条只有最后一条会生效

1
2
3
4
5
6
# (exec form, 推荐使用)
CMD ["executable","param1","param2"]
# (ENTRYPOINT参数)
CMD ["param1","param2"]
# (shell form)
CMD command param1 param2

作为普通用户CMD是每个Dockerfile的最后一条命令,必须保证这条命令不会退出,一旦退出容器也将停止

数据卷

Posted on 2019-06-18 | Edited on 2020-09-09 | In Docker

数据卷 ( Volume )

在 Docker 中,通过这几种方式进行数据共享或持久化的文件或目录,我们都称为数据卷 ( Volume )

123
德巍

德巍

时间是把杀猪刀

27 posts
10 categories
32 tags
Github V2ex SegmentFault Zhihu bilibili
© 2021 德巍
Powered by Hexo v3.7.1
|
Theme — NexT.Mist v6.3.0