Docker 安装mediawiki

一、install and config

1.1 db

pre

#在运行docker容器时可以加如下参数来保证每次docker服务重启后容器也自动重启:
$docker run --restart=always
#如果已经启动了则可以使用如下命令:
$docker update --restart=always <CONTAINER ID>

mariadb

docker pull mariadb:latest
docker run -p 3307:3306 --restart 策略名称 -v 宿主机数据目录:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=mysql密码 -d 镜像ID
docker run --name mymariadb  -p 3306:3306 --restart always -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=2240881 -d 54514d54a4a5
docker run --name mymariadb  -p 3307:3306 --restart always -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=2240881 -d  mariadb #同上

echo 'mymariadb  192.168.10.105' >>/etc/hosts
 mysql -uroot -p2240881 -h 192.168.10.105 -P3307  #连接 

mysql

docker pull mysql:5.7
#config and run 
docker run -p 3307:3306 --restart 策略名称 -v 宿主机数据目录:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=mysql密码 -d 镜像ID
docker run --name mymariadb  -p 3306:3306 --restart always -v /data/mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=2240881 -d 54514d54a4a5

通过Docker安装和使用MariaDB:https://mariadb.com/kb/en/library/installing-and-using-mariadb-via-docker/

Docker使用MariaDB:https://www.jianshu.com/p/32542630c2bd

1.2 wiki

docker run --name mymediawiki  -p 88:80  --link mymariadb:mysql -d  mediawiki #is good 
docker  cp LocalSettings.php  3c0002c21a6a:/var/www/html/LocalSettings.php
--net=host 

mediawiki   这个不会自启动 


docker run --name mymediawiki --link mymariadb:mysql -d   wikimedia/mediawiki  #起来 这个感觉不对 
#is old
docker pull mediawiki

#还是指定目录比较好  但是现在 官方的好像不支持 -v了 
docker run  --restart=always --name mediawiki -p 8080:80 -v  /data/mediawiki -d 8d721d068c81 #2018  但是没办法直接数据交互 下面的用了有问题呢
#mysql 填写 192.168.0.10:3307

docker run --name mediawiki -p 8080:80  -v /data/mediawiki:/var/www/html/ -d id
#docker run --name mediawiki -p 8080:80  -v /data/mediawiki:/var/www/html/ -d mediawiki
docker cp /data/LocalSettings.php  mediawiki:/var/www/html/LocalSettings.php
默认上传目录 /var/www/html/images/


#解说 
使用docker镜像nginx:latest以后台模式启动一个容器,并将容器命名为mynginx。
docker run --name mynginx -d nginx:latest

#交互  
sudo docker exec -it facethink-mediawiki /bin/bash

#开机自启动
#在运行docker容器时可以加如下参数来保证每次docker服务重启后容器也自动重启:
$docker run --restart=always
#如果已经启动了则可以使用如下命令:
$docker update --restart=always <CONTAINER ID> #上次就是这样解决了重启后nginx问题

1.3 mysql

Docker mysql:https://wiki.linuxchina.net/index.php?title=Docker_mysql

1.4 nginx

#这个在内网其实可以不用呢
server {
    listen       80;
    server_name  wiki.linuxsa.org;

    charset utf-8;
    #access_log  /var/log/nginx/host.access.log  main;

    location / {
        proxy_pass   http://192.168.50.206:8080;
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

docker  cp default.conf  nginx:/etc/nginx/conf.d/default.conf

Docker nginx:https://wiki.linuxchina.net/index.php?title=Docker_nginx

1.5 Memcached

Docker 安装 Memcached:https://wiki.linuxchina.net/index.php?title=Docker_%E5%AE%89%E8%A3%85_Memcached

二、see also

Docker for mediawiki:https://www.mediawiki.org/wiki/Docker

https://hub.docker.com/_/mediawiki/

使用 Docker 部署 MediaWiki:https://www.jianshu.com/p/a644988684d8

docker安装mediawiki教程:https://yq.aliyun.com/ziliao/65666

使用 Docker 部署 MediaWiki:https://cloud.tencent.com/developer/article/1193750

Docker 安装 Memcached:https://wiki.linuxchina.net/index.php?title=Docker_%E5%AE%89%E8%A3%85_Memcached

Docker 实践 3:fig 搭建 mediawiki:http://wiki.jikexueyuan.com/project/docker-practice/fig-creat-mediawiki.html

Docker Hello World容器运行报错的解决办法

费了好大力气从Docker官网下载了Docker Community Editor的安装镜像,Docker.dmg, 总共将近500MB,双击进行安装:

未分类

命令行里使用docker version查看版本:

未分类

然后使用命令行docker run hello-world,遇到错误信息:Error response from daemon: unauthorized: incorrect username or password:

未分类

解决方案

使用命令docker login首先进行登录:

未分类

然后hello world的Docker 容器就能正常工作了:

未分类

使用Docker安装phabricator的配置和使用方法

我这里使用的是ubuntu16.04的系统。

使用docker进行安装

我们使用https://hub.docker.com/r/redpointgames/phabricator/ 中提供的镜像。

在则这个镜像中已经为我们提供了多种插件,不过需要我们自己提供一个mysql数据库的地址。

所以我们可以先使用docker安装一个mysql数据库。可以参考:https://www.jb51.net/article/148880.htm

按照上面的教程,我们安装了mysql,账号为root,密码为123456

通过

docker inspect <container id> |grep IPAddress

我们可以知道mysql这个容器的ip地址。加入我们查到的这个容器的私有ip为172.17.0.2。

下面我们就可以来使用docker安装phabricator了。

docker pull redpointgames/phabricator    # 拉取最新版的phabricator

启动

docker run --name phabricator -p 443:443 -p 332:22 -p 8891:80 -v /root/phabricator/backup:/repos -e PHABRICATOR_HOST=xxxxxx:8891 -e MYSQL_HOST=172.17.0.2 -e MYSQL_USER=root -e MYSQL_PASS=123456 -e PHABRICATOR_REPOSITORY_PATH=/repos -e PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin -it redpointgames/phabricator /bin/bash /app/init.sh

其中xxxx为你服务器的外网ip,这个就可以通过http://xxxxx:8891来访问你服务器上docker部署的phabricator了。不过记得开启安全组。

安装完成后,我们就可以通过外网ip
加端口号8891来访问了。

未分类

这时我们添加的用户,会默认为系统的管理员,就可以登录到后台进行其他的设置了。但是先不要推出这个页面。因为要进行一些设置才行。

如果你第一次访问不是这个界面, 我们也可以在控制台通过命令 的方式添加用户.

进入容器
cd /srv/phabricator/phabricator
./bin/accountadmin

如果你的管理员用户退出来了,提示下面的信息了

This Phabricator install is not configured with any enabled authentication providers which can be used to log in. If you have accidentally locked yourself out by disabling all providers, you can use phabricator/bin/auth recover <username> to recover access to an administrative account.

你应该运行命令

进入容器
cd /srv/phabricator/phabricator
./bin/auth recover luanpeng(在网页中创建的管理员账号)

会提示通过一个网页进入。

Use this link to recover access to the "luanpeng" account from the web interface:
  http://xxxxxx:8891/login/once/recover/1/xxxxxxxxxx/
After logging in, you can use the "Auth" application to add or restore authentication providers and allow normal logins to succeed.

根据提示进行操作

在主页左侧栏选择Auth,进入下图

允许用户登录注册

到此为止,phabricator就安装完毕了

如果你不想使用docker安装,可以自己独立部署。不过步骤比较繁琐,所以建议还是通过docker部署。

使用安装脚本安装

创建安装脚本。打开网址将网页中的脚本内容复制到新建的sh文件中,https://secure.phabricator.com/source/phabricator/browse/master/scripts/install/install_ubuntu.sh

将网址中的脚本文件下载下来,或者直接在本地创建一个sh文件,将网页上的sh代码复制下来。形成本地的安装脚本install_ubuntu.sh

创建安装目录/home/luanpeng/work/phabricator

将安装脚本拷贝到安装目录,执行脚本

sh install_ubuntu.sh

执行脚安装本按照屏幕提示进行操作。脚本会自动帮你安装git, apache, mysql, php…等一系列工具。

注意:在安装mysql时,会有几次让你输入root密码的提示。这里推荐将密码设置为空,以方便后面的使用。

下载Phabricator以及其依赖包

$ cd somewhere/ # pick some install directory 切换到安装目录
somewhere/ $ git clone git://github.com/facebook/libphutil.git
somewhere/ $ git clone git://github.com/facebook/arcanist.git
somewhere/ $ git clone git://github.com/facebook/phabricator.git

WEB服务器:配置Apache

加入sverver name
(1)修改httpd.conf,这个文件在刚装完Apache时应该是空的。(2.4.x版本在/etc/apache2路径下创建httpd.conf文件即可)
加入如下内容: ServerName localhost
(2)保存退出。

验证Apache安装
(1)重启Apache服务
/etc/init.d/apache2 restart
(2)验证页面
打开浏览器,在地址栏中输入 localhost。看是否会出现“It works”的字样,如果出现代表Apache运行正常。否则,就需要去查看一下httpd.conf是否配置正确了。

更新Phabricator

由于Phabricator一直处于发展状态,所以你应该经常更新它。如何更新:

  • 停止webserver的运行
  • 运行git拉取libphutil/,arcanist/,phabricator
  • 运行phabricator/bin/storage upgrade
  • 重新启动webserver

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。

Ubuntu Server下Docker实战 02: Docker进阶配置

在上一篇文章里《Ubuntu Server下Docker实战 01: 安装Docker,见 https://www.linuxidc.com/Linux/2018-10/154873.htm》,我们已经把docker安装起来了,并运行了一个hello-world

这一篇,我们继续讲进阶配置。

1. 配置docker在系统启动时候自动启动

sudo /lib/systemd/systemd-sysv-install enable docker
sudo systemctl enable docker
sudo systemctl restart docker.service

这样我们重启服务器后,docker服务也自动启动了

2. 转移docker数据文件路径

修改配置文件

vi /etc/default/docker

我们假设要把数据移动到 /data/docker下,那么在配置文件中添加如下内容,保存退出

OPTIONS=--graph="/data/docker" -H fd://

停止docker

service docker stop

确认不存在 /data/docker 目录

rm -rf /data/docker

转移文件,将docker数据从/var/lib/docker 移到 /data/docker,并创建连接

sudo mv /var/lib/docker /data/docker
sudo ln -s /data/docker /var/lib

启动docker

service docker start

3. 创建docker子网,docker默认使用 172.17.0.0/24 子网,但我们可能有自己的需要,那么可以通过下面方法创建子网

显示已有子网

docker network ls

创建一个172.18.0.0/24的子网

docker network create --subnet=172.18.0.0/24 dockernetwork

我们看下现在的子网列表

docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
b295fdf8279f        bridge              bridge              local
b338774b3d02        dockernetwork      bridge              local
88b7535d052a        host                host                local
79814a08c6d1        none                null                local

如何为双活Redis Enterprise搭建基于Docker的开发环境?

Redis Enterprise这种双活数据库是地域分布式应用程序的理想选择。其架构基于无冲突复制数据类型(CRDT)方面是突破性的学术研究。这种方法与其他双活数据库相比具有许多优点,包括如下:

  1. 为读写操作提供本地延迟

  2. 为简单和复杂的数据类型提供内置的冲突解决方案

  3. 跨区域故障切换

  4. 简化实施了诸多用例,比如积分榜、分布式缓存、共享会话和多用户计费等。

最近我们发布了一篇关于https://redislabs.com/docs/developing-apps-using-active-active-redis-enterprise/ 。为了模拟生产环境,开发人员或测试人员需要一种小型化的开发环境,很容易用Docker来搭建。

Redis Enterprise在Docker hub上以redislabs/redis的形式存在,我们已经在Redis Enterprise说明文档页面和docker hub本身上介绍了如何在Docker上搭建Redis Enterprise的详细逐步说明。

我们在本文中介绍创建基于Docker的Redis Enterprise集群的步骤,这一切通过命令行来完成。下面大体介绍了整个过程(更多详细信息如下):

  1. 安装数据库

    1)创建一个3个节点的Redis Enterprise集群,每个节点在单独的子网上

    2)创建基于CRDT的Redis Enterprise数据库

    3)连接到三个不同的实例

  2. 验证安装的环境

  3. 拆分网络

  4. 恢复连接

  5. 停止Redis Enterprise

在开始之前,确保你已有一个bash shell,并为docker进程分配了足够的内存。你可以进入到Docker -> Preferences -> Advanced来检查内存。

未分类

1. 安装数据库

下列脚本在3节点集群上创建基于CRDT的Redis Enterprise数据库。将其保存在文件中并为其命名,比如“create_3_node_cluster.sh”。然后将模式改成可执行(chmod + x create_3_node_cluster.sh),并运行脚本([path] /create_3_node_cluster.sh)。

#!/bin/bash  
# Delete the bridge networks if they already exist  
docker network rm network1 2>/dev/null  
docker network rm network2 2>/dev/null  
docker network rm network3 2>/dev/null  
# Create new bridge networks  
echo “Creating new subnets…”  
docker network create network1 –subnet=172.18.0.0/16 –gateway=172.18.0.1  
docker network create network2 –subnet=172.19.0.0/16 –gateway=172.19.0.1  
docker network create network3 –subnet=172.20.0.0/16 –gateway=172.20.0.1  
# Start 3 docker containers. Each container is a node in a separate network  
# These commands pull redislabs/redis from the docker hub. Because of the  
# port mapping rules, Redis Enterprise instances are available on ports  
# 12000, 12002, 12004  
echo “”  
echo “Starting Redis Enterprise as Docker containers…”  
docker run -d –cap-add sys_resource -h rp1 –name rp1 -p 8443:8443 -p 9443:9443 -p 12000:12000 –network=network1 –ip=172.18.0.2 redislabs/redis  
docker run -d –cap-add sys_resource -h rp2 –name rp2 -p 8445:8443 -p 9445:9443 -p 12002:12000 –network=network2 –ip=172.19.0.2 redislabs/redis  
docker run -d –cap-add sys_resource -h rp3 –name rp3 -p 8447:8443 -p 9447:9443 -p 12004:12000 –network=network3 –ip=172.20.0.2 redislabs/redis  
# Connect the networks  
docker network connect network2 rp1  
docker network connect network3 rp1  
docker network connect network1 rp2  
docker network connect network3 rp2  
docker network connect network1 rp3  
docker network connect network2 rp3  
# Sleep while the nodes start. Increase the sleep time if your nodes take  
# longer than 60 seconds to start  
echo “”  
echo “Waiting for the servers to start…”  
sleep 60  
# Create 3 Redis Enterprise clusters – one for each network. You can login to  
# a cluster as https://localhost:8443/ (or 8445, 8447). The user name is  
# [email protected], password is password. Change the user  
echo “”  
echo “Creating clusters”  
docker exec -it rp1 /opt/redislabs/bin/rladmin cluster create name cluster1.local username [email protected] password test  
docker exec -it rp2 /opt/redislabs/bin/rladmin cluster create name cluster2.local username [email protected] password test  
docker exec -it rp3 /opt/redislabs/bin/rladmin cluster create name cluster3.local username [email protected] password test  
# Create the CRDB  
echo “”  
echo “Creating a CRDB”  
docker exec -it rp1 /opt/redislabs/bin/crdb-cli crdb create –name mycrdb –memory-size 512mb –port 12000 –replication false –shards-count 1 –instance fqdn=cluster1.local,[email protected],password=test –instance fqdn=cluster2.local,[email protected],password=test –instance fqdn=cluster3.local,[email protected],password=test 

2. 验证安装的环境

在端口12000、12002和12004上运行redis-cli,验证你可以连接到所有三个Redis Enterprise端口。如果你将应用程序连接到Redis Enterprise,需要应用程序的三个实例连接到三个不同的端口。比如:

$ redis-cli -p 12000  
127.0.0.1:12000> incr counter  
(integer) 1  
127.0.0.1:12000> get counter  
“1” 

3. 拆分网络

拆分网络可帮助你在Redis Enterprise副本之间引入“网络分区”。你在设计应用程序时,必须设计成副本断开连接后可以顺畅运行。该脚本帮助你隔离三个副本。将该脚本保存在文件“split_networks.sh”中,并在运行之前更改模式,让它成为可执行(chmod +x split_networks.sh)。

#!/bin/bash  
docker network disconnect network2 rp1  
docker network disconnect network3 rp1  
docker network disconnect network1 rp2  
docker network disconnect network3 rp2  
docker network disconnect network1 rp3  
docker network disconnect network2 rp3 

4. 恢复连接

你运行脚本“split_netorks.sh”后,本地副本会停止与其他副本共享数据库更新。恢复连接将让它们能够交换所有更新,并获得同样的最终状态,这归功于Redis Enterprise提供了很强的最终一致性。下列脚本恢复副本之间的网络连接。将这保存在文件“restore_networks.sh”中,并更改模式让它成为可执行(chmod +x restore_networks.sh)。

#!/bin/bash  
docker network connect network2 rp1  
docker network connect network3 rp1  
docker network connect network1 rp2  
docker network connect network3 rp2  
docker network connect network1 rp3  
docker network connect network2 rp3 

5. 停止Redis Enterprise

完成开发和测试后,只要运行下列脚本,就可以终止Redis Enterprise的所有三个节点。将该文件保存在文件中,并将文件命名为“stop.sh”,更改模式,让它成为可执行(chmod +x stop.sh)。

#!/bin/bash  
docker stop rp1 rp2 rp3  
docker rm rp1 rp2 rp3  
docker network rm network1  
docker network rm network2  
docker network rm network3 

就是这样。完成了上述步骤后,现在你有了自己的基于Docker的Redis Enterprise双活数据库环境。若有任何问题,欢迎留言交流。

docker指令学习记录

前言

本文为学习整理和参考文章,不具有教程的功能。其次,后面将会陆续更新各种应用的容器化部署的实践,如MySQL容器化,Jenkins容器化,以供读者参考。

镜像获取

docker pull [options] [Docker Registry地址]<仓库名>:<标签>

-a, --all-tags: 下载该镜像的所有版本

Docker Registry地址默认为Docker Hub,一般格式为IP:端口号
仓库名为两段式 <用户名>:<软件名> 默认用户名为library
标签不填则默认为latest

列出镜像

docker images [options] [Repository[:tag]]
默认情况会展示所有最终镜像,如果加上了镜像名,则会展示该镜像的所有信息
-a, --all: 展示所有镜像,包括中间层
-f, --filter filter: 根据某种条件对镜像进行筛选
--format string: 使用go的模板语法
-q, --quiet: 只返回镜像的ID

docker images -f since=mongo:3.2  #查看mongo3.2版本之后建立的镜像,如果是要在之前,则使用before
docker images --format "{{.ID}}:{{.Repository}}" #输出结构为ID:Repository

虚悬镜像

虚悬镜像是指既没有仓库名,也没有标签的镜像。这种镜像的产生常常由于当前的仓库名和标签被更新版本占用,导致当前境像失效。

docker images -f danling=true #列出所有虚悬镜像
docker rmi $(docker images -q -f dangling=true) #利用复合指令删除虚悬镜像

commit镜像

commit会将容器的存储层保存下来成为新的镜像

docker commit [options] <容器ID或容器名> [<仓库名>[:<标签>]]
-a, --author string: 容器所有者
-c, --change list: 在容器上执行Dockerfile指令
-m, --message string: 提交信息
-p, --pause: 提交过程中停止容器的运行,默认为true

docker history IMAGE #显示镜像的历史记录
docker diff CONTAINER #查看容器的改动

尽量不要使用commit指令构建镜像

Dockerfile

构建镜像

利用Dockerfile构建镜像。

docker build [options] PATH | URL | -
-f, --file string: Dockerfile的路径
--rm: 成功构建后删除中间镜像
-t, --tag: 以name:tag的形式为镜像命名
docker build -t nginx:v3 . #执行当前目录下的Dockerfile并构建镜像,新的镜像名为nginx:v3
docker build https://......   #直接从github构建,会自动clone这个项目,切换到指定分支(默认为master),并进入指定目录进行构建

最后的路径是指镜像构建的上下文,docker在build的时候会把该上下文中的而所有内容全部打包上传给docker引擎。当在Dockerfile中需要引用相对路径时,就是以该上下文作为当前指令执行的目录。可以编写.dockerignore文件来剔除无需打包的文件。
在默认情况下,如果不指定Dockerfile的位置,就会从构建的上下文寻找Dockerfile来执行

FROM

指定基础镜像,Dockerfile的第一行必须制定基础镜像

RUN

执行命令。RUN指令会新建一层并在其上执行指令,指令完成之后再commit该镜像。所以RUN指令中的内容应当尽可能合并,并且记得清除冗余的内容如缓存等。

RUN <指令>
RUN ["可执行文件", "参数1", "参数2"]
RUN mkdir newDir 
   && touch newFile

COPY

将构建上下文中源路径中的内容复制到目标路径之下。可以使用通配符。如果目标目录不存在,容器会帮助创建。复制过程不改变文件属性。

COPY 源路径 目标路径
COPY ["源路径",...,"目标路径"]

COPY hom* /mydir/

CMD

默认的容器的主进程的启动命令,在运行时可以指定新的命令来替代镜像设置中的默认命令。比如ubuntu的默认指令是/bin/bash。如果使用第一种形式,则会以sh -c的形式执行,这样就能够得到环境变量。容器中的应用都应该前台执行。

CMD <命令>
CMD ["可执行文件", "参数一", "参数二", ...]
CMD ["参数一", "参数二"...]

CMD ["nginx", "-g", "daemon off;"]
docker run -it ubuntu #直接进入bash,因为默认指令为/bin/bash
docker run -it ubuntu /etc/os-release #默认指令变成/etc/os-release

ENTRYPOINT

指定容器启动程序及参数,当指定了ENTRYPOINT之后,CMD的含义就变成了ENTRYPOINT的参数。从而实现我们在build镜像时可以根据配置修改启动指令的参数。在docker run运行时可以用–entrypoint覆盖

ENTRYPOINT "CMD"
ENTRYPOINT ["可执行文件", "参数一", "参数二"...]

ENV

设置环境变量

ENV KEY VALUE
ENV KEY1=VALUE2 KEY2=VALUE2

ARG

同ENV,设置环境变量并为其提供默认值,不同的是在容器运行时,这些值将不存在。在运行时可以用–build-arg <参数名>:<值>覆盖

ARG <参数名>[=默认值]

VOLUMN

指定匿名卷,防止用户忘记挂载,运行时用-v HOST_DIR/CONTAINER_DIR进行覆盖

VOLUMN PATH

EXPOSE

声明运行时容器提供的服务端口,运行时应用并不会因为这个声明而打开这个端口。docker run -P时会对声明的端口随机映射

EXPOSE 端口一 端口二

WORKDIR

指定容器之后各层的工作目录。因为本层的cd并不会顺带到下一层。

WORKDIR PATH

USER

改变之后层执行RUN,ENTRYPOINT等指令的身份

RUN groupadd -r redis && useradd -r -g redis redis
USER redis
RUN ["redis-server"]

ONBUILD

ONBUILD 其它指令

用于构建基础镜像,被引用是才会真正执行。可以提取出重复的部分,方便维护

删除

docker rmi [options] <image1> [<image2>....] #删除镜像
docker rm [options] <container1> [<container2>...] #删除容器

进入容器

docker attach CONTAINER_NAME

查看数据卷信息

docker inspect CONTAINER_NAME

匿名的数据卷默认位于/var/lib/docker/volumes之下

查看容器

docker logs [-f] container

查看端口映射配置

docker port container container_port

容器链接

--link container_name:alias

Docker入门总结

原文地址: http://zhenhua-lee.github.io/container/docker.html

未分类

Docker是一个虚拟环境容器,可以将应用代码、环境配置、系统环境等一并打包在一起,生成一个镜像,然后就可以发布到任意平台上。

与VM的区别

未分类

VM在物理机的操作系统上建立了一个中间软件层 hypervisor,利用物理机资源,虚拟出多个硬件资源,这些新的虚拟硬件环境、安装的操作系统、相应软件便构成了一台虚拟机

而 Docker 对硬件资源,在不同docker container上做了隔离,使得每个docker container拥有不同的环境,同时可以共享硬件资源

日常使用的基本概念

未分类

核心功能简答介绍:

  • Docker是C/S模式
  • images:docker镜像,是Docker run的原材料
  • container: Docker运行的内容,是独立存在的
  • data volumes: 通过数据挂载的方式,实现数据共享
  • network:用户容器与外部、容器之间的通信,常用的方法有端口映射、link等

使用流程

未分类

基本操作

  • docker version: 查看基本版本信息,包括client、server

关于镜像的基本操作

  • docker search: 默认在 https://hub.docker.com 中查询镜像,当然可以修改registry
  • docker pull: 镜像拉取 docker pull imageName:version
  • docker push: 镜像提交
  • docker images: 查看本地镜像
  • docker rmi: 删除本地镜像
  • docker build:利用 Dockerfile 制作镜像,例如 docker build -t newImageName -f dockerFile [contextPath]
  • docker commit: 基于运行的 container 制作镜像

关于容器的基本操作

  • docker run
    • 镜像的运行
    • d: 在后台运行
    • v: 用户数据挂载
    • p: 端口映射,实现外部与容器之间的通信
    • rm: 容器推出时,直接删除容器
    • i: 交互式的方式
    • t: 在容器中启动一个终端
  • docker ps
    • 查询当前存在的容器
    • a: 列出所有容器
    • q: 仅出 container id
  • dock exec: 在容器中执行命名,例如可以使用 docker exec -it containerId /bin/bash 进入到容器内部

  • docker stop: 停止容器的运行
  • docker restart: 重新启动容器的运行
  • docker rm: 容器删除

DockerFile

# 指定基础镜像
FROM NODE:10.12

## 从本地 copy 文件到镜像中
COPY ./ /data/my-node/

## 切换 container 的工作目录
WORKDIR /data/my/node

## 执行命令
RUN npm install

## 容器的启动命名
ENTRYPOINT ["node", "./index.js"]

使用Docker和GitLab构建一个CI/CD Pipeline

本文主要讲述了如何在GitLab上使用Docker镜像构建一个CI/CD的Pipeline。

现如今持续集成(CI)和持续交付(CD)大家已经不陌生了,它们是为了辅助你的产品/工程项目能够更快、更容易地运行最新版本。在这篇文章中,我将讲述如何使用Docker镜像和GitLab的CI/CD工具构建一个Pipeline,在一个VPS/KVM Linux服务器上进行部署。

前提要求

  • 对Linux、Docker以及CI/CD有基本的了解。
  • GitLab帐号(免费计划即可)。
  • 一台具备SSH访问权限的Linux服务器(非root用户即可)。我使用的是带有LAMP技术栈的Ubuntu 16.04 LTS系统。
  • 装有SSH和LFTP的轻量级Docker镜像。

在开始之前,你需要确保:

  • 你已经登录GitLab
  • 你是某个project/repository的拥有者
  • 你能够在本地机器通过Git访问这个repo进行pull和push操作

我用的是GitKraken,一个Git GUI工具,能够较为方面的进行Git操作。

关于GitLab的CI/CD

GitLab提供了一种通过Docker和Shared Runners处理CI/CD Pipeline的简单方法。每次运行Pipeline时,GitLab都会创建一个独立的虚拟机并构建一个Docker镜像。Pipeline可以使用YAML配置文件进行配置,一个Pipeline可以有多个job,但如果job太多,Pipeline的运行时间就较长。我们肯定不希望这样,因为使用免费计划,每月最多可以有2000分钟的构建时间。

“GitLab.com上的Shared Runners以自动缩放模式运行,由DigitalOcean提供支持。自动缩放意味着减少启动构建的等待时间,并为每个项目建立隔离虚拟机,从而最大限度地提高安全性。”
——来自GitLab文档中的描述

为GitLab的runner创建SSH密钥

注意:即使你的服务器上已有具备SSH访问方式,还是建议你为CI/CD创建一套新的密钥,同时为部署流程创建一个新的非root用户。

我们将在Docker容器中通过SSH连接我们的服务器,这就意味着我们不能输入用户密码(即非交互式登录),因此我们需要在本地计算机中创建无密码的SSH密钥对。通常我会创建一个2048字节的RSA密钥,因为这足够安全。

$ ssh-keygen rsa -b 2048

输入以上命令,跟随创建步骤,如果对创建步骤有疑问,使用man ssh-key。记住不要为密钥对设置密码。创建完成后,我们需要把私钥导入我们的服务器:

$ ssh-copy-id -i /path/to/key user@host

现在你可以尝试通过以下命令连接:

$ ssh -i /path/to/key user@host

连接过程应该不会让你输入密码。这个私钥我们后面会使用到。

选择Dockerfile

我使用Docker Hub来存放我的定制化Dockerfile,这个Dockerfile将基于Alpine构建一个安装有OpenSSH和LFTP的轻量级镜像(大约8Mb)。在GitLab的CI/CD中我们需要使用这个镜像来运行Pipeline的job和脚本,镜像越轻量意味着下载镜像的时间就越少。你可以用你自己的镜像或者用我的Dockerfile。

Pipleline的配置

在正式构建前,你需要在你repo的根目录创建一个”.gitlab-ci.yml”文件。接下来我将解释我使用的配置文件,如果有兴趣,你可以先到GitLab官网阅读配置文件格式以及所有可以使用的配置项。

我的配置文件如下:

image: jimmyadaro/gitlab-ci-cd:latest
Deploy:
stage: deploy
only:
— ‘master’
when: manual
allow_failure: false
before_script:
#Create .ssh directory
— mkdir -p ~/.ssh
#Save the SSH private key
— echo “$SSH_PRIVATE_KEY” > ~/.ssh/id_rsa
— chmod 700 ~/.ssh
— chmod 600 ~/.ssh/id_rsa
— eval $(ssh-agent -s)
— ssh-add ~/.ssh/id_rsa
script:
#Backup everything in /var/www/html/
— ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa $USERNAME@$HOST “zip -q -r /var/backups/www/01-Deploy-$(date +%F_%H-%M-%S).zip /var/www/html/”
#Deploy new files to /var/www/html
— lftp -d -u $USERNAME, -e ‘set sftp:auto-confirm true; set sftp:connect-program “ssh -a -x -i ~/.ssh/id_rsa”; mirror -Rnev ./ /var/www/html — ignore-time — exclude-glob .git* — exclude .git/; exit’ sftp://$HOST
— rm -f ~/.ssh/id_rsa
— ‘echo Deploy done: $(date “+%F %H:%M:%S”)’

让我们逐行看看配置文件的每一步都在做什么。

image: jimmyadaro/gitlab-ci-cd:latest

这行将告诉runner从Docker Hub上拉取并运行最新版本的容器。你可以在这里设置你想要使用的镜像,但别忘了给镜像安装OpenSSH和LFTP。

Deploy:

这行设置了pipeline的job名字,创建一个job必须设置这行内容。

stage: deploy

这行设置了job的stage名字,如果你需要运行多个stage,例如“backup”、“build”、“deploy”等,stage名字将帮助你识别当前Pipeline处于什么状态。由于我不需要其他stage,所以我只用了一个job,并且这个job只有一个stage。对于job和stage的名字可以任意设置,例如你的job可以叫“ASDF”,stage可以叫“GHJK”,不过如果你有多个stage,你肯定需要鉴别不同的stage,因此我建议还是规范化这些名字。

only:
— ‘master’

这行表示Pipeline只有当你repo的master分支收到一个更新(例如git merge)时才会被触发。因此,我建议开发使用其他分支(例如development、wip等),然后使用master分支作为“产品分支”。

when: manual

这行表示你需要进入你的project的CI/CD配置中手动触发整个部署流程。当然,这一步是可以跳过的,只是我更喜欢手动触发Pipeline。如果去掉这行,你所选分支(本例中为master)的任何改动都会触发一次Pipeline。

allow_failure: false

这行表示如果你的Pipeline中有其他stage,当一个job中发生错误时,不允许继续执行剩余任务。这是一个可选配置。

before_script:
#Create .ssh directory
— mkdir -p ~/.ssh
#Save the SSH private key
— echo “$SSH_PRIVATE_KEY” > ~/.ssh/id_rsa
— chmod 700 ~/.ssh
— chmod 600 ~/.ssh/id_rsa
— eval $(ssh-agent -s)
— ssh-add ~/.ssh/id_rsa

before_script单元设置的所有命令都会在执行主单元(main script)之前执行。如你所见,每行shell命令需要用短横线(“-“)指定。上面的命令将把我们刚刚生成的SSH私钥保存到容器默认的SSH路径下,这样我们就可以免密连接我们的服务器。

刚刚生成的私钥将作为Protected变量保存在我的project的CI/CD配置中,在GitLab的web UI上,点击Settings > CI/CD > Variables将看到这个变量。同样,我将服务器地址和部署使用的用户名(非root用户)也使用Protected变量保存。

未分类

script:
#Backup everything in /var/www/html/
— ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa $USERNAME@$HOST “zip -q -r /var/backups/www/01-Deploy-$(date +%F_%H-%M-%S).zip /var/www/html/”
#Deploy new files to /var/www/html
— lftp -d -u $USERNAME, -e ‘set sftp:auto-confirm true; set sftp:connect-program “ssh -a -x -i ~/.ssh/id_rsa”; mirror -Rnev ./ /var/www/html — ignore-time — exclude-glob .git* — exclude .git/; exit’ sftp://$HOST
— rm -f ~/.ssh/id_rsa
— ‘echo Deploy done: $(date “+%F %H:%M:%S”)’

script下的内容就是GitLab的runner执行的主单元。首先,我会连接到我的服务器将所有内容备份到一个ZIP文件中,这个ZIP文件将使用当前时间(格式为yyyy-mm-dd_hh-mm-ss)进行命名:

— ssh -o StrictHostKeyChecking=no -i ~/.ssh/id_rsa $USERNAME@$HOST “zip -q -r /var/backups/www/01-Deploy-$(date +%F_%H-%M-%S).zip /var/www/html/”

注意:你需要在你的服务器上安装ZIP CLI。

在将/var/www/html备份后,使用LFTP连接到我的服务器并且上传最新的repo文件。这里我用的是SFTP,FTP配置有点不一样:

— lftp -d -u $USERNAME, -e ‘set sftp:auto-confirm true; set sftp:connect-program “ssh -a -x -i ~/.ssh/id_rsa”; mirror -Rnev ./ /var/www/html — ignore-time — exclude-glob .git* — exclude .git/; exit’ sftp://$HOST

使用mirror -Rnev ./ /var/www/html让LFTP上传./(我repo的根目录)下的所有文件到我服务器的/var/www/html路径下。上面部分参数的意思如下:

  • -u设置了我们sftp://$HOSTSSH用户名。
  • -e用于设置执行命令(使用单引号进行配置)。
  • -R用于设置reverse mirror。
  • -n表示只上传新的文件。
  • -e用于删除在我们源中不存在的文件。
  • -v用于配置verbose日志。
  • ignore-time将在决定是否下载时忽略时间。
  • exclude-glob .git*将会排除任何目录中匹配.git*的所有文件(例如.gitignore以及.gitkeep)。你可以在这里设置其他文件匹配方式。
  • exclude .git/这个配置将会保证不上传我们repo中的git文件。
  • exit将会停止LFTP和SSH执行。

注意:所有在我们服务上但是不在我们repository中的文件将被删除,记住上面所述的’源’指的就是我们GitLab的repository。

最终,脚本会在shared runner的容器中删除我们的私钥(这是一个安全措施),并且输出带有当前时间的结束语句。

— rm -f ~/.ssh/id_rsa
— ‘echo Deploy done: $(date “+%F %H:%M:%S”)’

以上部分就是我配置文件的所有内容。在GitLab中一个成功的Pipeline执行流程如下图所示:

未分类

运行Docker镜像

未分类

Pipeline的最终状态

结论

我尝试了一些其他的方式,例如使用rsync替代LFTP、使用多阶段以及缓存依赖(我能够重用SSH密钥)的Jobs、使用Docker的ENTRYPOINT和CMD等等,但我发现上面描述的方式对我来说是最快和最容易的。

Docker的网络类型及驱动器

计算机如果不能够联网,其价值就要大打折扣。类似的,一个Docker容器也需要通过网络访问其他资源,或者被其他资源访问。这就涉及到Docker容器实例的网络,也与Docker宿主机的网络息息相关。

总的来说,Docker的网络是一个通过网络驱动器(driver)实现的Docker子系统。不同的网络驱动器,能够创建具有不同网络特性的Docker容器实例。

目前(Docker v18.03),Docker内置提供如下的网络驱动器:

1. bridge

Docker容器实例默认即使用该网络驱动器,-d=bridge明确使用该网络驱动器。

该网络驱动器适用于独立的容器实例之间通信,但是这些容器实例必须位于同一个宿主机。在bridge网络中,各个独立的容器实例都是连接到一个bridge,并通过bridge通信。

在Docker环境中,默认即已经创建了一个名为bridge的网络。后续启动的容器实例,如果没有指明网络驱动器,则默认加入到该网络。也可以专门创建一个定制的bridge网络,创建的bridge网络会覆盖默认的bridge网络。定制的bridge网络,最大的好处是默认即支持服务的自动发现。

在bridge网络中,每次启动一个容器实例,都会按照顺序获取网络IP。所以重启容器实例后,可能会发现容器的IP变化了。

bridge网络默认不支持IPv6。

2. host

-d=host明确使用该网络驱动器。

该网络驱动器适用于Linux宿主机上的独立的容器实例。容器实例与容器宿主机之间没有网络隔离,容器实例直接使用宿主机的网络。
host网络默认不支持IPv6。

3. overlay

-d=overlay明确使用该网络驱动器。

该网络驱动器适用于Docker宿主机集群中的各个独立的容器实例之间通信。为集群中的Docker容器实例提供跨多个Docker引擎的网络连接。

4. macvlan

-d=macvlan明确使用该网络驱动器。
该网络驱动器适用于与需要有MAC地址的容器实例。
在某些历史遗留应用中,只能通过MAC通信,与之通信的容器实例也必须拥有MAC地址。这时容器实例就如同真实的物理设备一样。

5. none

-d=none禁用容器实例中的网络功能。通常,这表明要使用其他的第三方网络驱动器。

关于其他第三方网络驱动器,请参考https://store.docker.com/search?category=network&q=&type=plugin

参考链接:

https://docs.docker.com/network/

在Docker的工作流中常见问题及最终方案

这次我们创建一个Hello world的web服务器。

mkdir -p identidock/app   #首先创建一个新的multiidentidock来存放我们的项目,在这个目录下面,创建一个app目录来存放Python代码。
touch app/identidock.py  #在app目录下创建identidock.py
# 编辑identidock.py的内容
from flask import Flask
app = Flask(__name__)  #初始化Flask和设置应用对象
@app.route('/')     #创建一个与URL关联的路由,当这个URL被请求,它会调用hello_world函数。
def hello_world():
        return "Hello Docker!n"
if __name__ == '__main__':
        app.run(debug=True,host='0.0.0.0')  #初始化Python web服务器,使用0.0.0.0作为主机参数绑定了所有的网络接口
#现在我们需要一个存放Python代码的容器然后运行它。在identidock目录下面创建一个Dockerfile文件,并且编辑如下内容
FROM python:3.4
RUN pip install Flask==0.10.1
WORKDIR /app
COPY app /app
CMD ["python" "identidock.py"]

现在我们可以构建我们的简单镜像了

docker build -t identidock .
……
docker run -d -p 5000:5000 identidock
  • -d 代表在后台运行容器
  • -p 代表我们想转发主机的5000端口到容器的5000端口
curl localhost:5000    #进行测试
Hello World!

但是有一些问题存在:每次代码的改变,我们都需要重新构建镜像然后重启这个容器。

对此,有一个简单的解决方案,我们可以绑定主机的源代码文件夹到内部容器文件夹中。

docker stop $(docker ps -ql)  #停止最近创建的容器
docker rm $(docker ps -lq)    #删除最近创建的容器
docker run -d -p 5000:5000 -v $(pwd)/app:/app identidock
-v $(pwd)/app:/app 参数绑定app目录到容器的/app。 它会覆盖容器内/app的内容,同时对于容器内部也是可写的(你也可以挂载为只读模式)-v 参数必须是绝对路径。
curl localhost:5000  #测试
Hello world!

现在我们可以在主机上编辑文件看看

sed -i 's/World/Docker/' app/identidock.py  #使用sed快速替换World为Docker,你也可以使用正常的文本编辑器。
curl localhost:5000
Hello Docker!

现在除了容器内容封装的一些依赖关系,我们就拥有了一个相对正常的开发环境了。然而这里还有一个问题,那就是我们不能在生产环境使用这个容器,因为它正在运行的是默认的Flask webserver,它只适用于开发者,在生产环境中则效率低下并且不安全。一个好的解决方法就是采纳Docker减少开发环境和生产环境的区别,现在让我们在看一下怎么处理吧。

uWSGI是一个为生产环境准备的应用服务器,它也可以位于类似于Nginx的web server后面。使用uWSGI代替Flask webserver会提供我们一个灵活的容器,方便我们进行设置。我们可以转换这个容器到使用uwSGI容器只需要修改Dockerfile中的两行。

FROM python:3.4
RUN pip install Flask==0.10.1 uWSGI==2.0.8 
WORKDIR /app
COPY app /app
CMD ["uwsgi", "--http", "0.0.0.0:9090", "--wsgi-file", "/app/identidock.py", 
     "--callable", "app", "--stats", "0.0.0.0:9191"]
docker build -t identidock  #构建这个镜像,然后运行它我们可以看到其中的不同
……
docker run -d -p 9090:9090 -p 9191:9191 identidock
curl localhost:9090
Hello Docker!

你可以使用docker logs来看一下日志uWSGI的日志信息。当然我们也可以在http://localhost:9191中看到一些uWSGI暴露的状态信息。

但是实际上,上面会提示一个安全问题,我们使用root来运行服务了。我们可以在Dockerfile中很容易的修复这个问题,同时我们在声明一下容器监听的端口。

FROM python:3.4
RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask==0.10.1 uWSGI==2.0.8
WORKDIR /app
COPY app /app
COPY cmd.sh /
EXPOSE 9090 9191
USER uwsgi
CMD ["uwsgi","--http","0.0.0.0:9090","--wsgi-file","/app/identidock.py", 
"--callable","app","--stats","0.0.0.0:9191"]
docker build -t identidock . #重建这个镜像
...
docker run identidock whoami 
uwsgi

#你最好在你的所有的Dockerfile中设置用户,或者在ENTRYPOINT或CMD脚本中改变用户。

现在在容器呢把的命令不是以root来运行了,让我们运行一下这个容器试试。。

docker run -d -P --name port-test identidock
#-P 让Docker自动的映射一个大数字的本地主机端口到容器内部。

我们可以看下那个端口被映射了

docker port port-test 
9090/tcp -> 0.0.0.0:32769 
9191/tcp -> 0.0.0.0:32768
curl localhost:32769 #现在做个测试
Hello Docker!

现在还存在一个问题,那就是我们缺失了开发工具,例如调试输出和实时的代码重载。理想情况下,我们想要使用这个镜像既可以作为开发环境又可以作为生产环境。我们可以使用环境变量和一个简单的脚本来实现这个需求。

在和Dockerfile相同的目录下创建cmd.sh,然后编辑如下内容

#!/bin/bash set -e
if [ "$ENV" = 'DEV' ]; then
  echo "Running Development Server"
  exec python "identidock.py" 
else
  echo "Running Production Server"
  exec uwsgi --http 0.0.0.0:9090 --wsgi-file /app/identidock.py 
             --callable app --stats 0.0.0.0:9191 
fi
chmod +x cmd.sh
#编辑Dockerfile
FROM python:3.4
RUN groupadd -r uwsgi && useradd -r -g uwsgi uwsgi
RUN pip install Flask==0.10.1 uWSGI==2.0.8
WORKDIR /app
COPY app /app
COPY cmd.sh /
EXPOSE 9090 9191
USER uwsgi
CMD ["/cmd.sh"]
docker stop $(docker ps -q)
docker rmr $(docker ps -aq)
docker build -t identidock .  #重建镜像
docker run -e "ENV=DEV" -p 5000:5000 identidock
Running in development environment.
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger pin code: 290-812-275
docker run -p 5000:5000 -v $(pwd)/app:/app -e "ENV=DEV" identidock #这时我们可以实时修改代码了

然后再不加ENV=DEV的情况下运行就是成产环境了。

小结:
上面依次展示了一些Docker生产中可能会存在的问题。并且提供了最后的解决方案。