这次作业要求改用mysql数据库,并且用docker容器化客户端,服务端,数据库
作业要求:应用容器化
项目地址:SimpleBlog
坑很多,慢慢说
0. 安装docker
由于CentOs的虚拟机不小心玩崩了,只能用windows来装docker,而且由于不是专业版,不能安装docker for windows,所以只能安装DockerToolbox
下载地址:https://docs.docker.com/toolbox/toolbox_install_windows/
安装过程默认就行了,不过要把virtualbox和git的安装点掉,不然会把本机的删掉重装……太毒了
安装好后点击快捷方式,会出现这个
点击浏览去git的安装目录下的bin文件夹里找到bash.exe就行了
然后可能会出现“VBoxManage not found. Make sure VirtualBox is installed and VBoxManage is in the path”这种错误
STEP="Looking for vboxmanage.exe"
if [ ! -z "$VBOX_MSI_INSTALL_PATH" ]; then
VBOXMANAGE="{$VBOX_MSI_INSTALL_PATH}VBoxManage.exe"
else
VBOXMANAGE="${VBOX_INSTALL_PATH}VBoxManage.exe"
把{$VBOX_MSI_INSTALL_PATH}
直接换成了本机这个环境变量的路径解决了
接着要安装Boot2Docker,不过会很慢,建议直接下载放进C:\Users\username\.docker\machine\cache\boot2docker.iso
下载地址:https://github.com/boot2docker/boot2docker/releases/download/v17.05.0-ce/boot2docker.iso
最后如果看到小鲸鱼,就大功告成了
再换一下源
换源指南:http://guide.daocloud.io/dcs/daocloud-9153151.html#docker-toolbox
常用命令
docker run/build
build构建镜像,run运行镜像创建容器
docker images
查看所有镜像
docker ps [-a]
查看正在运行的容器,加上-a查看所有容器
docker stop/kill
安全停止/直接杀死正在运行的容器
docker rm/rmi
rm移除容器,rmi移除镜像
docker network
网络相关命令
docker bash
进入容器内部
1. 配置mysql
获取mysql官方最新docker镜像
docker pull mysql/mysql-server:latest
运行mysql
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -v /mysql:/var/lib/mysql --network=mynet --name=mysql --rm mysql/mysql-server
-p:端口映射。将容器的3306端口映射到宿主机的3306端口。[宿主机]:[容器]
-e:设置环境变量。这里是设置root账号的密码为123456
-v:目录映射。将宿主机的/mysql目录映射到容器的/var/lib/mysql目录,这样容器就能直接使用宿主机的目录,而实现持久化。值得注意的是,docker是运行在Linux上的,在Windows中运行docker,实际上还是在Windows下先安装了一个Linux环境,然后在这个系统中运行的docker,所这里的宿主机指的是Linux虚拟机,在virtualbox里可以查看。
–name:将容器命名为mysql,否则会随机取一个名字,不太方便
–network:详见下面关于network的介绍
–rm:在容器停止运行后自动删除这个容器
可以使用docker images
查看拉取的mysql/mysql-server镜像
使用docker ps
看到正在运行的mysql容器
使用docker exec -it mysql bash
进入mysql容器,并使用mysql -uroot -p
命令进入数据库操作
接下来,因为mysql的root用户只允许localhost,即容器内授权登录,外网(其它容器或者宿主机)无法授权登录root用户,所以我们可以添加一个用户,允许外网登录它,赋予它操作某个数据库的权限。
select user,host from user;
如果看到root用户的host是localhost,就说明只能本机登录
如果新建的用户的host是%,就说明可以用任意ip登录
添加用户,并授权
create database test;
create user testuser identified by '123';
grant all on test.* to 'testuser'@'%';
flush privileges;
这是项目的mysql初始创建表
CREATE DATABASE `test`;
CREATE TABLE `test`.`Article` (
`id` INT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`date` VARCHAR(45) NOT NULL,
`content` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
CREATE TABLE `test`.`Comment` (
`id` INT NOT NULL AUTO_INCREMENT,
`content` VARCHAR(100) NOT NULL,
`date` VARCHAR(100) NOT NULL,
`author` VARCHAR(100) NOT NULL,
`articleId` INT NOT NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
CREATE TABLE `test`.`User` (
`username` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
PRIMARY KEY (`username`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;
2. dockerfile
dockerfile的编写挺简单,具体操作主要是拉取源,声明工作目录,导入项目,执行项目的运行步骤,在网上也有很多详细的教程
前端
FROM node:10
# Create app directory
WORKDIR D:\vue\Client
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
RUN npm install cross-env -g
RUN npm install webpack-dev-server -g
RUN npm install webpack -g
# Bundle app source
COPY . .
EXPOSE 8080
CMD [ "npm", "run", "dev" ]
值得注意的是那几个全局安装的包,不可思议的是如果不这样做,运行时会找不到这些包,排除这个故障的时候花了很长时间
docker build -t gostbops-client .
docker run -p 8081:8080 --name=client --rm --network=mynet gostbops-client
后端
FROM golang:latest
RUN go get github.com/go-sql-driver/mysql && go get github.com/dgrijalva/jwt-go && go get github.com/gorilla/mux
WORKDIR $GOPATH/src/github.com/GostBops/Server
ADD . $GOPATH/src/github.com/GostBops/Server
RUN go build .
EXPOSE 8080
ENTRYPOINT ["./Server"]]
docker build -t gostbops-server .
docker run -p 8080:8080 --name=server--rm --network=mynet gostbops-server
3. golang和mysql
在golang中使用mysql可以导入以下两个包
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
开启数据库:
db, err := sql.Open("mysql", "testuser:123@tcp(mysql:3306)/?charset=utf8")
if err != nil {
log.Fatal(err)
}
defer db.Close()
查询操作:
query, err := db.Query("select * from test.Article where id=" + articleId)
if err != nil {
log.Fatal(err)
}
defer query.Close()
query是一个*sql.Rows
指针,需要读取数据并将其转换为json格式,这是一个轮子
func getJSON(rows *sql.Rows) ([]byte, error) {
columns, err := rows.Columns()
if err != nil {
return []byte(""), err
}
count := len(columns)
tableData := make([]map[string]interface{}, 0)
values := make([]interface{}, count)
valuePtrs := make([]interface{}, count)
for rows.Next() {
for i := 0; i < count; i++ {
valuePtrs[i] = &values[i]
}
rows.Scan(valuePtrs...)
entry := make(map[string]interface{})
for i, col := range columns {
var v interface{}
val := values[i]
b, ok := val.([]byte)
if ok {
v = string(b)
} else {
v = val
}
entry[col] = v
}
tableData = append(tableData, entry)
}
jsonData, err := json.Marshal(tableData)
if err != nil {
return []byte(""), err
}
return jsonData, nil
}
插入操作
query, err = db.Query("INSERT INTO `test`.`User` (`username`, `password`) VALUES ('" + user.Username + "', '" + user.Password + "')")
if err != nil {
log.Fatal(err)
}
defer query.Close()
4. network
docker network create mynet
创建mynet网络
docker network inspect mynet
查看mynet网络相关信息
创建网络简单的说,就是分配一个子网,然后可以把容器连接到这个子网,形成一个局域网,实现容器互联,相互访问。
具体命令可以在文档上找到描述,或者使用docker network [command] --help
关于如何在windows下访问客户端,服务端和数据库
前面说过,docker是运行在Linux上的,在Windows中运行docker,实际上还是在Windows下先安装了一个Linux环境,然后在这个系统中运行的docker。也就是说,服务中使用的localhost指的是这个Linux环境的地址,而不是我们的宿主环境Windows。我们可以通过命令:
docker-machine ip default
找到这个Linux的ip地址
我的是192.168.99.100
使用这个ip可以访问有设置端口映射的容器
比如上述容器,可以用
- 192.168.99.100:3306连接mysql
- 192.168.99.100:8080向服务端发送请求
- 192.168.99.100:8081访问客户端
关于客户端,服务端,数据库相互访问的ip设置
- 由于服务端,数据库设置在了同一个网络mynet里,所以服务端可以直接用容器名连接数据库:
mysql:3306
。不过数据库的root用户只能localhost访问,也就是容器内访问,容器外的服务端需要用刚才创建的testuser:123
连接mysql,并且只有权限使用test这个数据库 - 当在宿主机(windows)访问客户端,客户端是通过浏览器向服务端发送请求,所以要使用
192.168.99.100:8080
来访问服务端