1.タスク定義の登録(コンテナの設定)
2.タスクの実行環境となるクラスターの作成(EC2, VPC, セキュリティグループなど)
3.サービスの作成(タスクとクラスターを紐づける)
4.EC2が生成される

コンテナの設定のところを、独自のコンテナで作りたい。
どうやらECRにimageをpushして、そのimageをそのまま使うようだ。
随机应变 ABCD: Always Be Coding and … : хороший
1.タスク定義の登録(コンテナの設定)
2.タスクの実行環境となるクラスターの作成(EC2, VPC, セキュリティグループなど)
3.サービスの作成(タスクとクラスターを紐づける)
4.EC2が生成される

コンテナの設定のところを、独自のコンテナで作りたい。
どうやらECRにimageをpushして、そのimageをそのまま使うようだ。
ECSとはElastic Container Registryの略、Dockerのコンテナイメージを保存しておくレジストリ

Edit permission

$ chmod 400 KEYPAIR.pem
$ ssh -i KEYPAIR.pem ec2-user@PublicIP
$ docker pull amazon/amazon-ecs-sample
Using default tag: latest
latest: Pulling from amazon/amazon-ecs-sample
72d97abdfae3: Pull complete
9db40311d082: Pull complete
991f1d4df942: Pull complete
9fd8189a392d: Pull complete
Digest: sha256:36c7b282abd0186e01419f2e58743e1bf635808231049bbc9d77e59e3a8e4914
Status: Downloaded newer image for amazon/amazon-ecs-sample:latest
docker.io/amazon/amazon-ecs-sample:latest
$ REPOSITORY=your-repository-uri
$ docker tag amazon/amazon-ecs-sample:latest $REPOSITORY
$ EC2_REGION=`wget -q -O – http://169.254.169.254/latest/meta-data/placement/availability-zone | sed s/.$//`
$ `aws ecr get-login –region $EC2_REGION –no-include-email`
$ docker push $REPOSITORY
ECS
-> Task definition -> EC2
{
"family": "ecs-demo",
"containerDefinitions": [
{
"volumesFrom": [],
"memory": 128,
"extraHosts": null,
"dnsServers": null,
"disableNetworking": null,
"dnsSearchDomains": null,
"portMappings": [
{
"hostPort": 80,
"containerPort": 80,
"protocol": "tcp"
}
],
"hostname": null,
"essential": true,
"entryPoint": null,
"mountPoints": [],
"name": "ecs-sample",
"ulimits": null,
"dockerSecurityOptions": null,
"environment": [],
"links": null,
"workingDirectory": null,
"readonlyRootFilesystem": null,
"image": "YOUR-REPOSITORY-URI:latest",
"command": null,
"user": null,
"dockerLabels": null,
"logConfiguration": null,
"cpu": 0,
"privileged": null
}
],
"volumes": []
}
ECSCluster

ECRからpushしてimageを作ることが出来る
なるほど、ECSとECRはなんと無くわかったので、後は実践するのみですね。
ECSとはDockerを簡単に実行、停止、管理できるサービス
management console ECS

Task Definition
create task definition -> EC2 -> json
{
"family": "myContainer",
"containerDefinitions": [
{
"volumesFrom": [],
"portMappings": [
{
"hostPort": 80,
"containerPort": 80
}
],
"command": null,
"environment": [],
"essential": true,
"entryPoint": null,
"links": [],
"mountPoints": [
{
"containerPath": "/usr/local/apache2/htdocs",
"sourceVolume": "my-vol",
"readOnly": null
}
],
"memory": 300,
"name": "simple-app",
"cpu": 10,
"image": "httpd:2.4"
},
{
"volumesFrom": [
{
"readOnly": null,
"sourceContainer": "simple-app"
}
],
"portMappings": [],
"command": [
"/bin/sh -c \"while true; do echo '<html> <head> <title>Amazon ECS Sample App</title> <style>body {margin-top: 40px; background-color: #333;} </style> </head><body> <div style=color:white;text-align:center> <h1>Amazon ECS Sample App</h1> <h2>Congratulations!</h2> <p>Your application is now running on a container in Amazon ECS.</p>' > top; /bin/date > date ; echo '</div></body></html>' > bottom; cat top date bottom > /usr/local/apache2/htdocs/index.html ; sleep 1; done\""
],
"environment": [],
"essential": false,
"entryPoint": [
"sh",
"-c"
],
"links": [],
"mountPoints": [],
"memory": 200,
"name": "busybox",
"cpu": 10,
"image": "busybox"
}
],
"volumes": [
{
"host": {
"sourcePath": null
},
"name": "my-vol"
}
]
}
create
### Cluster
ECS Cluster -> Create
Launch type: EC2

Jsonでupdateできる。
とりあえず触ってみたという感じか。
Getting startedでECSを触ってみる

さくらレンタルサーバーにPython3をPip3を入れて動かそうとすると、
ModuleNotFoundError: No module named ‘_ctypes’ となるので、
あらかじめlibffiをインストールする必要がある。
### libffiインストール
mkdir -p ~/work/libffi
cd work/libffi
wget ftp://sourceware.org/pub/libffi/libffi-3.2.1.tar.gz
tar xvfz libffi-3.2.1.tar.gz
cd libffi-3.2.1
./configure –prefix=$HOME/local/libffi/3_2_1
make
% mkdir -p ~/local/include
% ln -s $HOME/local/libffi/3_2_1/lib/libffi-3.2.1/include/ffi.h $HOME/local/include/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi-3.2.1/include/ffitarget.h $HOME/local/include/
% mkdir -p ~/local/lib
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.a $HOME/local/lib/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.la $HOME/local/lib/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.so $HOME/local/lib/
% ln -s $HOME/local/libffi/3_2_1/lib/libffi.so.6 $HOME/local/lib/
% mkdir -p ~/local/lib/pkgconfig/
% ln -s $HOME/local/libffi/3_2_1/lib/pkgconfig/libffi.pc $HOME/local/lib/pkgconfig/
% cd ~/
% vi .cshrc
// パスを最終行に追加
setenv LD_LIBRARY_PATH $HOME/local/lib setenv PKG_CONFIG_PATH $HOME/local/lib/pkgconfig
% source ~/.cshrc
% rehash
### Python3インストール
% mkdir -p ~/work/python3
% cd ~/work/python3
% wget –no-check-certificate https://www.python.org/ftp/python/3.9.0/Python-3.9.0.tgz
% tar zxf Python-3.9.0.tgz
% cd ./Python-3.9.0
% ./configure –prefix=$HOME/local/python/ –with-system-ffi LDFLAGS=”-L $HOME/local/lib/” CPPFLAGS=”-I $HOME/local/include/”
% make
% make install
% cd ~/
%vi .cshrc
// 最終行に以下を追加
set path = ($path $HOME/local/python/bin)
% source ~/.cshrc
% rehash
% python3 –version
Python 3.9.0
% pip3 –version
pip 20.2.3
$ pip3 install ${package}
libffiが無くて、何度もNo module named ‘_ctypes’となり、焦りました。
まず画像を用意します。

#! /usr/bin/python3
# -*- coding: utf-8 -*-
from PIL import Image
from io import BytesIO
import os
COMPRESS_QUALITY = 30
path = "./src"
images = os.listdir(path)
for image in images:
if image.endswith('.jpg'):
with open("./src/" + image, 'rb') as inputfile:
im = Image.open(inputfile)
im_io = BytesIO()
im.save(im_io,'JPEG', quality=COMPRESS_QUALITY)
with open("./src/comp_" + image, mode='wb') as outputfile:
outputfile.write(im_io.getvalue())
if image.endswith('.jpeg'):
with open("./src/" + image, 'rb') as inputfile:
im = Image.open(inputfile)
im_io = BytesIO()
im.save(im_io,'JPEG', quality=COMPRESS_QUALITY)
with open("./src/comp_" + image, mode='wb') as outputfile:
outputfile.write(im_io.getvalue())
if image.endswith('.png'):
with open("./src/" + image, 'rb') as inputfile:
im = Image.open(inputfile)
im_p = im.convert('P')
with open("./src/comp_" + image, mode='wb') as outputfile:
im_p.save(outputfile)

我ながらよく出来ています。
heightを0にして、padding-topを%で指定する
Sass
.v-box {
width: 100%;
height: 0;
padding-top:75%;
background-color: gray;
}
html
<div class="columns"> <div class="column">Ch1<br><div class="v-box"></div><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit</p></div> <div class="column">Ch2<br><div class="v-box"></div><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit</p></div> <div class="column">Ch3<br><div class="v-box"></div><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit</p></div> </div>

なるほど
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.1/css/all.css" integrity="sha384-vp86vTRFVJgpjF9jiIGPEEqYqlDwgyBgEF109VFjmqGmIY/Y4HV4d3Gp2irVfcrp" crossorigin="anonymous">
</head>
<body>
<div id="app">
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" href="/home" style="font-size:1.5em;font-weight:bold;">
<!-- <img src="https://bulma.io/images/bulma-logo.png" width="112" height="28"> -->
Speech Recognition Demo
</a>
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="">
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="" class="navbar-menu">
<!-- <div class="navbar-start">
<a class="navbar-item">
Home
</a>
<a class="navbar-item">
Documentation
</a>
</div> -->
<div class="navbar-end">
<div class="navbar-item">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
Language
</a>
<div class="navbar-dropdown">
<a class="navbar-item">
English
</a>
<a class="navbar-item">
Chinese
</a>
<a class="navbar-item">
Japanase
</a>
</div>
</div>
<div class="buttons">
<a class="button is-light">
Logout
</a>
</div>
</div>
</div>
</div>
</nav>
<section class="main-content columns is-fullheight">
<aside class="column is-3 is-narrow-mobile is-fullheight section is-hidden-mobile">
<p class="menu-label is-hidden-touch">MENU</p>
<ul class="menu-list">
<li>
<a href="/home" class="">
<span class="icon"><i class="fa fa-home"></i></span> Home
</a>
</li>
<li>
<a href="#" class="">
<span class="icon"><i class="fa fa-table"></i></span> Channel
</a>
<ul>
<li>
<a href="#">
<span class="icon is-small"><i class="fa fa-link"></i></span> Ch1
</a>
</li>
<li>
<a href="#">
<span class="icon is-small"><i class="fa fa-link"></i></span> Ch2
</a>
</li>
<li>
<a href="#">
<span class="icon is-small"><i class="fa fa-link"></i></span> Ch3
</a>
</li>
</ul>
</li>
<li>
<a href="#" class="">
<span class="icon"><i class="fa fa-id-card"></i></span> MyPage
</a>
</li>
<li>
<a href="#" class="">
<span class="icon"><i class="fa fa-file-alt"></i></span> Document
</a>
</li>
<li>
<a href="#" class="">
<span class="icon"><i class="fa fa-comments"></i></span> Chat room
</a>
</li>
</ul>
</aside>
<div class="container column is-9">
<div class="section">
<div class="card">
<div class="card-header"><p class="card-header-title">Header</p></div>
<div class="card-content"><div class="content">Content</div></div>
</div>
<br>
<div class="card">
<div class="card-header"><p class="card-header-title">Header</p></div>
<div class="card-content"><div class="content">Content</div></div>
</div>
<br>
<div class="columns">
<div class="column">Column1</div>
<div class="column">Column2</div>
<div class="column">Column3</div>
</div>
</div>
</div>
</section>
<footer class="footer is-hidden">
<div class="container">
<div class="content has-text-centered">
<p>Hello</p>
</div>
</div>
</footer>
</div>
<script src="bundle.js"></script>
</body>
</html>

うん、いい感じです
<div class="hero is-fullheight is-primary"> <div class="hero-body"> <div class="container has-text-centered"> <div class="column is-8 is-offset-2"> <h3 class="title has-text-white">Speech Recognition Demo</h3> <hr class="login-hr"> <p class="subtitle has-text-white">Login</p> <div class="box"> <!-- <div class="box"> <img src="img/login.png"> </div> --> <div class="title has-text-grey is-5">Please enter your email and password.</div> <form> <div class="field"> <div class="control"> <input class="input is-large" type="email" placeholder="Email" autofocus=""> </div> </div> <div class="field"> <div class="control"> <input class="input is-large" type="password" placeholder="Password"> </div> </div> <button class="button is-block is-danger is-large is-fullwidth">Login</button> </form> <br> <p class="has-text-grey"> <a href="">Sign Up</a> <a href="">Forgot Password</a> <a href="">Need Help?</a> </p> </div> </div> </div> </div> </div>

bootstrapにはない体験ができますね。
package.json
{
"name": "front",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack-dev-server --mode development --env development",
"dev": "webpack-dev-server --mode development --env development",
"build": "webpack --mode production"
},
"keywords": [],
"author": "hpscript",
"license": "ISC",
"devDependencies": {
"@babel/core": "^7.12.13",
"@babel/preset-env": "^7.12.13",
"@babel/preset-react": "^7.12.13",
"babel-loader": "^8.2.2",
"css-loader": "^5.0.2",
"mini-css-extract-plugin": "^1.3.6",
"node-sass": "^5.0.0",
"optimize-css-assets-webpack-plugin": "^5.0.4",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"sass-loader": "^11.0.1",
"style-loader": "^2.0.0",
"ts-loader": "^9.2.6",
"typescript": "^4.4.3",
"webpack": "^5.53.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.2.1"
},
"description": ""
}
webpack.config.js
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
module.exports = {
entry: {
anyname: `./src/scss/style.scss`,
bundle: './src/app.ts'
},
output: {
path: path.join(__dirname,'dist'),
filename: '[name].js'
},
resolve: {
extensions:['.ts','.js']
},
devServer: {
host: '192.168.34.10',
port: '8000',
static: "./dist",
open: true
},
module: {
rules: [
{
test:/\.ts$/,use:'ts-loader'
},
{
test: /\.scss$/,
use: [
{ loader: MiniCssExtractPlugin.loader },
{ loader: 'css-loader' },
{ loader: 'sass-loader' },
],
}
]
},
plugins: [
new MiniCssExtractPlugin({ filename: 'css/style.css'}),
],
optimization: {
minimizer: [new OptimizeCSSAssetsPlugin({})],
},
}
tsconfig.json

環境構築まではOK^^
manager.go
package sessions
import (
"crypto/rand"
"encoding/base64"
"errors"
"io"
"net/http"
)
type Manager struct {
database map[string]interface{}
}
var mg Manager
func NewManager() *Manager {
return &mg
}
func (m *Manager) NewSessionID() string {
b := make([]byte, 64)
if _, err := io.ReadFull(rand.Reader, b); err != nil {
return ""
}
return base64.URLEncoding.EncodeToString(b)
}
fun (m *Manager) New(r *http.Request, cookieName string)(*Session, error){
cookie, err := r.Cookie(cookie.Name)
if err == nil && m.Exists(cookie.Value){
return nil, errors.New("sessionIDは既に発行されています")
}
session := NewSession(m, cookieName)
session.ID = m.NewSessionID()
session.request = r
return session, nil
}
func(m *Manager) Save(r *http.Request, w http.ResponseWriter, session *Session) error {
m.database[session.ID] = session
c := &http.Cookie {
Name: session.Name(),
Value: session.ID,
Path: "/",
}
http.SetCookie(session.writer, c)
return nil
}
func(m *Manager) Exists(sessionID string) bool {
_, r = m.database[sessionID]
return r
}
func(m *Manager) Get(r *http.Request, cookieName string)(*Session, error){
cookie, err := r.Cookie(cookieName)
if err != nil {
return nil, err
}
sessionID := cookie.Value
buffer, exists := m.database[sessionID]
if !exists {
return nil, errors.New("無効なセッションIDです")
}
session := buffer.(*Session)
session.request = r
return session, nil
}
func(m *Manager) Destroy(sessionID string){
delete(m.database, sessionID)
}
session.go
package sessions
import(
"net/http"
"github.com/gin-gonic/gin"
"github.com/gorilla/context"
)
const (
DefaultSessionName = "default-session"
DefaultCookieName = "default-cookie"
)
type Session struct {
cookieName string
ID string
manager *Manager
request *http.request
writer http.ResponseWriter
Values map[string]interface{}
}
func NewSession(manager *Manager, cookieName string) *Session {
return &Session {
cookieName: cookieName,
manager: manager,
Values: map[string]interface{}{},
}
}
func StartSession(sessionName, cookieName string, manager *Manager) gin.HandlerFunc {
return func(ctx *gin.Context){
var session *Session
var err error
session, err = manager.Get(ctx.Request, cookieName)
if err != nil {
session, err = manager.New(ctx.Request, cookieName)
if err != nil {
println(err.Error())
ctx.Abort()
}
}
session.writer = ctx.Writer
ctx.Set(sessionName, session)
defer context.Clear(ctx.Request)
ctx.Next()
}
}
func StartDefaultSession(manager *Manager) gin.HandlerFunc {
return StartSession(DefaultSessionName, DefaultCookieName, manager)
}
func GetSession(c *gin.Context, sessionName string) *Session {
return c.MustGet(sessionName).(*Session)
}
func GetDefaultSession(c *gin.Context) *Session {
return GetSession(c, DefaultSessionName)
}
func (s *Session) Save() error {
return s.manager.Save(s.request, s.writer, s)
}
func (s *Session) Name() string {
return s.cookieName
}
func (s *Session) Get(key string)(interface{}, bool){
ret, exists := s.Value[key]
return ret, exists
}
func(s *Session) Set(key string, val interface{}){
s.Values[key] = val
}
func(s *Session) Delete(key string){
delete(s.Values, key)
}
func(s *Session) Terminate(){
s.manager.Destroy(s.ID)
}
h_Login.go
package req_handler
import (
"fmt"
"html/template"
"net/http"
)
func HandlerLogin(w http.ResponseWrite, req *http.Request){
tpl := template.Must(template.ParseFiles("templates/login.gtpl"))
values := map[string]string{}
if err := tpl.ExecuteTemplate(w, "login.gtpl", values); err != nil {
fmt.Println(err)
}
}
fun HandlerExecLogin(w http.ResponseWriter, req *http.Request){
manager := sessions.NewManager()
sessions.StartDefaultSession(manager)
session := manager.Get(req, sessions.DefaultCookieName)
session.Set("account", req.FormValue("account"))
session.Save()
http.Redirect(w, req, "http://192.168.34.10:10443/login-top", 301)
}
h_LoginTop.go
package req_handler
import (
"fmt"
"html/template"
"net/http"
)
func HandlerLoginTop(w http.ResponseWriter, r *http.Request){
tpl := template.Must(template.ParseFiles("templates/login-top.gtpl"))
isLoggedIn := ""
account := ""
session := manager.Get(req, sessions.DefaultCookieName)
if session.Values["account"]{
isLoggedIn = "isLoggedIn"
account = session.Values["account"]
}
values := map[string] string {
"isLoggedIn": isLoggedIn,
"account": account,
}
if err := tpl.ExecuteTemplate(w, "login-top.gtpl", values); err != nil {
fmt.Println(err)
}
}
func HandlerExecLogout(w http.ResponseWriter, req *http.Request){
session := manager.Get(req, sessions.DefaultCookieName)
session.Terminate()
http.Redirect(w, req, "https://192.168.34.10:10443/login", 301)
}
なるほど、ginね
1. usersテーブルでユーザ名とパスワードが一致するかチェックして、一致した場合進めるようにする
2. sessionで管理する
セッションの使い方は覚えていけば良さそう